Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time
LIP: 0009
Title: Mitigate transaction replay on different chains
Author: Manu Nelamane Siddalingegowda <>
        Iker Alustiza <>
Status: Draft
Type: Standards Track
Created: 2018-10-16
Updated: 2019-10-08


This LIP proposes to mitigate transaction replay on different chains by introducing a network identifier as a constitutive component of the signature of a transaction.


This LIP is licensed under the Creative Commons Zero 1.0 Universal.


Currently, transactions included in a blockchain on the Lisk platform can be replayed on other chains. For example, transactions sent on Testnet can be replayed on Mainnet, and the problem is likely to be exacerbated by the introduction of sidechains into this system. Current mitigation methods include (1) discouraging passphrase reuse across blockchains, and (2) encouraging registration of distinct second passphrases on distinct blockchains in situations where (1) is not feasible. However, one of the touted advantages of the Lisk platform is the ability to reuse passphrases on multiple blockchains, including the mainchain and sidechains, which conflicts with this strategy.


This LIP proposes to include a network identifier as a constitutive component of the signature of a transaction. This network identifier will be a hash of the concatenation of the blockchain’s nethash and a community identifier. The combination of these two components is intended to be unique and honest actors should conform to that requirement.

This proposal avoids transaction replay attacks from any existing blockchain to Lisk Mainnet or Testnet (and between each other). Adding the network identifier in the signature function will make the signature only valid for the specific network.

Moreover, in an ecosystem with sidechains implemented, each sidechain will have a unique network identifier to mitigate transaction replays across different chains. However, the situation will be more complex in this case since a malicious sidechain developer could copy the unique identifier from Mainnet or another sidechain and try to replay a transaction from its own sidechain on that original chain. The sidechain user, who calls the signature function, should check what will be exactly signed in the transaction object, including the network identifier. Note that the question of which properties are present on the serialized object that is used as the input for this function is different to the question of what information is transmitted over a network or stored in a database, where this identifier may be omitted for reasons of efficiency/scalability.

With a general and well defined signature process for the whole Lisk ecosystem, we could (strongly) recommend that sidechains use the functions implemented in Lisk Elements (or at least functions that meet this specification), removing the opportunity for a sidechain developer to provide a malicious signature function. Users (or third party clients) would be free to use Lisk Elements (or equivalent third-party tools) directly when signing transaction objects, and their suspicions should be raised if the sidechain protocol does not allow this.


As introduced in the previous section, the network identifier will be constructed as follows:

network identifier = H(nethash + community identifier)


  • H() - The cryptographic hash function SHA-256.

  • nethash - The UTF-8 encoded string containing the hexadecimal representation of the SHA-256 hash of the genesis block of the relevant blockchain. For Lisk Mainnet and Testnet, the nethash values are stored in the source code. In the case of sidechains’ nethash, we assume that Lisk Elements will provide a standard nethash calculating function. This way, sidechain users can generate this value by themselves and verify that the sidechain network identifier is the right one.

  • community identifier - The name of the relevant community as a string encoded in UTF-8 format. This identifier uniquely specifies the community supporting the relevant network. The community identifier associated with the Lisk Foundation and its supporters will be Lisk. If a subset of the community decided to split the network, they could, for example, choose LiskCash as their community identifier. In general, the community identifier field should be well documented and defined in the Lisk ecosystem (for example as in SLIP-0044) for every chain within it.

Note that the sum symbol “+” in the formula above stands for the string concatenation operator. In Appendix B below, one can find a simple example of the construction of a network identifier.

In order to implement the proposed change, the network identifier has to be included in first position of the byte array used as the input of the transaction signing process (note that the byte array used to generate the transaction ID remains unchanged). This will impact the following functions according to the current implementation:

Below, there are two illustrative examples of Testnet and Mainnet depicting how the network identifier will be generated at the point of various hard forks in the network according to what was proposed before.



TG0 = Testnet genesis nethash

NI1 = H(TG0 + ‘Lisk’)
NI2 = H(TG0 + ‘LiskCash’)

TG0-------|----T1--------------------Lisk------------> NI1                  
          |--------------T2---------Liskcash---------> NI2

The above illustration shows a possible scenario of hard forks and how the proposal will mitigate transaction replay attacks. As one can observe, we start with a single network which eventually splits into two networks with two different network identifiers (NI1 and NI2). Assume we have a hard fork since a minority of the community decided to fork the network identified by NI1 resulting in NI2. The network identifiers of the example were generated using the same nethash (TG0). When a minority of the community decided to split the network with a hard fork (i.e., NI2), they defined a new community identifier (e.g. LiskCash) for their fork. Since the new network shares history with the old network, the network identifier for the new network should be derived from the original nethash.

Assume now we have a transaction T1 from NI1. An attacker tries to replay the transaction T1 from network NI1 over network NI2. This transaction will be rejected as the signature of the transaction is unique to network NI1. More precisely, the distinct community identifier for NI2 yield a distinct network identifier, resulting in distinct data being signed as part of the transaction-signing process. The same would happen if the attacker tried to replay a transaction T2 from network NI2 over network NI1).

Across Networks

MG0 = Mainnet genesis nethash
TG0 = Testnet genesis nethash

MNI = H(MG0 + 'Lisk')
TNI = H(TG0 + 'Lisk')

TG0-------------T1------------------------------> TNI            

MG0------------------------------T2-------------> MNI

The above illustration demonstrates how the proposal will avoid transaction replay attacks across networks with different genesis blocks (Testnet, Mainnet). As it can be observed, we have two networks, Testnet and Mainnet, with different nethashes (TG0 and MG0) resulting in two different network identifiers (TNI and MNI).

Assume we have a transaction T1 from network TNI and a transaction T2 from MNI. When an attacker tries to replay transaction T1 from network TNI over network MNI, the transaction will be rejected as the signature of T1 is unique to the network TNI. The same would happen if the user tried to replay transaction T2 from network MNI over network TNI.

Backwards Compatibility

This change will introduce a hard fork. After the change is implemented, transaction signatures will be fundamentally different. This means that transactions will be verified in a different way, and thus, nodes on the old protocol version will reject transactions that have been signed using the new protocol, and vice versa.

Reference Implementation


Appendix: Example

Example of the construction of a new network identifier in the original Lisk Mainnet supported by the Lisk Foundation:

  • Nethash: NH= 'ed14889723f24ecc54871d058d98ce91ff2f973192075c0155ba2b7b70ad2511'
  • Community identifier = 'Lisk'
  • Hash function H() is SHA-256

With these parameters, the network identifier will be calculated as: network_ID = SHA-256(NH + ‘Lisk’)= '9ee11e9df416b18bf69dbd1a920442e08c6ca319e69926bc843a561782ca17ee'.