LIP: 0009 Title: Mitigate transaction replay on different chains Author: Manu Nelamane Siddalingegowda <firstname.lastname@example.org> Iker Alustiza <email@example.com> Discussions-To: https://lists.lisk.io/pipermail/lips_lists.lisk.io/2018-November/000002.html Status: Draft Type: Standards Track Module: Block, Transactions Created: 2018-10-16 Updated: 2019-01-17
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 GNU General Public License, version 3.
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, a community identifier, as well as a version relating to that community identifier. The combination of these three 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 fields are present on the 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 + version)
H()- The cryptographic hash function SHA3-256. Refer to Appendix A for details about this hashing function and its implementation.
nethash- The output of a cryptographic hash function of the genesis block of the relevant blockchain. For Lisk Mainnet and Testnet, the
nethashvalues 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. 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
LiskCashas their community identifier.
version- The unique version number per community identifier. This version identifier is increased when a software release introduces protocol-level changes that cause a hard fork in the network. Here, we are exclusively referring to main releases for the protocol which increase the protocol version from
(x+1).0(an example would be
'2.0'). It is worth noting that, in the case of a critical bug fix (patch release), which potentially causes a hard fork, this identifier will not change (the software release version remains unchanged).
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.
It is important to point out that the version and community identifier fields should be well documented and defined in the Lisk ecosystem (for example as in SLIP-0044).
In order to implement the proposed change in the Lisk ecosystem (Lisk Core, Lisk Elements), 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’ + ‘1.0’) NI2 = H(TG0 + ‘Lisk’ + ‘2.0’) NI3 = H(TG0 + ‘LiskCash’ + ‘1.0’) TG0-------|----T1--------------------Lisk------------> NI1 | |---------T2---------------Lisk------------> NI2 | |--------------T3---------Liskcash---------> NI3
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 three networks with three different network identifiers (
NI3). Assume we have a hard fork due to the introduction of a new fee system to the network resulting in a new network identifier
NI2 and, at the same time, a second hard fork because a minority of the community decided to fork the network identified by
NI1 resulting in
NI3. All the network identifiers of the example were generated using the same nethash (
TG0). In the case of
NI2, the version was bumped for a hard fork agreed upon by the original community (the community identifier remains the same across
NI2). Furthermore, it is assumed here that the original network from before the fork is maintained as well (with network identifier
NI1). Finally, when a minority of the community decided to split the network with a hard fork (i.e.,
NI3), they defined a new community identifier (e.g.
LiskCash) for their fork, along with their desired version number. 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
NI1. An attacker tries to replay the transaction
T1 from network
NI1 over network
NI3. This transaction will be rejected as the signature of the transaction is unique to network
NI1. More precisely, the increased version for
NI2 and the distinct community identifier for
NI3 yield distinct network identifiers, 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) and a transaction
T3 from network
NI3 over network
NI2) as depicted in the example.
MG0 = Mainnet genesis nethash TG0 = Testnet genesis nethash MNI = H(MG0 + 'Lisk' + '1.0') TNI = H(TG0 + 'Lisk' + '1.0') 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 (
MG0) resulting in two different network identifiers (
Assume we have a transaction
T1 from network
TNI and a transaction
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
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.
Moreover, the history of versions must be stored in order for nodes to verify old transactions during node synchronization. This history of versions must specify the block height range for every version within the same community identifier. This way, when a node is synchronizing with the network, the corresponding network identifiers for the corresponding heights will be in place for the transaction verification.
Appendix A: SHA3-256
The hash function SHA3-256 is an instance of the KECCAK function. Its form is:
SHA3-256(M) = KECCAK(M||01, 256)
For a message M according to NIST FIPS 202.
Implementation of SHA-3
The open source library js-sha3 can be forked and integrated into the Lisk source code in order to compute SHA-3 hashes. If this library is not desired, for any reason, jsSHA represents an alternative. Note that this is only a recommendation. Any correct implementation of SHA3-256 may be used.
Appendix B: Example
Example of the construction of a new network identifier in the original Lisk Mainnet, when protocol version 2.0 is released and supported by the Lisk Foundation:
- Community identifier =
- Version =
- Hash function
With these parameters, the network identifier will be calculated as:
network_ID = SHA3-256(NH + ‘Lisk’ + ‘2.0’)= 'cd9a34062feec2b368656efb99d9f710534c4a6c5d8dd0e36e66fad0e5ea10d0'