EIP-155 Simple replay attack prevention (Simple replay attack protection)
Final · Security & privacy · EIP status Final · created 2016-10-14
Includes a chain-specific number (chain ID) in the transaction signature so that a transaction signed for one chain cannot be resubmitted as-is on another chain.
Why it is needed
Before this EIP, a transaction signature was formed by hashing only six fields: nonce, gas price, gas limit, recipient address, amount, and data. Nowhere in the signature was there any indication of which chain the transaction was intended for. As a result, a signed transaction sent on Ethereum mainnet could be copied and resubmitted as-is on another chain, such as Ethereum Classic (ETC) or the Morden testnet, and could be processed there as a valid transaction too. This is a replay attack: a transfer a user made on mainnet could be executed identically on another chain without the user's intent. EIP-155 binds a signature to a specific chain by including a chain ID, a number unique to each chain, in the data being signed. The original text explains that this provides a way to send a transaction that works on Ethereum but not on ETC or the Morden testnet, and recommends that ETC and future testnets and consortium chains adopt this approach with their own unique chain IDs.
What changes
Today
- The signed data covers only six fields (nonce, gasprice, startgas, to, value, data), with no distinction of which chain it is for
- A replay attack is possible where a signed transaction intended for mainnet is resubmitted as-is on ETC or a testnet
- The signature's v value only ever takes 27 or 28
After EIP-155
- Nine fields are hashed, the original six plus chainid, 0, and 0, binding the signature to a specific chain
- The same transaction is not valid on a chain with a different chain ID
- The v value changes to {0,1} + CHAIN_ID * 2 + 35, so it carries the chain ID information as well
- Signatures using the old v = 27, 28 scheme remain valid, preserving backwards compatibility
Who is affected
- Users: The risk of a transaction sent on mainnet being secretly replayed on another chain is eliminated.
- Wallet & app developers: Before signing, nine fields including the chain ID must be hashed, and the new v value rule must be followed.
- Client teams: Both the new and old signature schemes must be distinguished by the v value, and both recovery rules must be handled.
- Testnets and other chains: They are recommended to adopt the same approach with their own unique chain IDs.
In depth
The signed data changes from six fields to nine fields
If block.number >= FORK_BLKNUM and a CHAIN_ID exists, when computing the transaction hash to be signed, the node SHOULD hash nine RLP fields (nonce, gasprice, startgas, to, value, data, chainid, 0, 0) instead of the previous six RLP fields (nonce, gasprice, startgas, to, value, data). The appended chainid, 0, 0 only go into the signed data and do not add fields to the actual transmitted transaction. Because the chain ID is part of the hash, the same transaction content produces a different signing hash on a different chain ID.
Encoding the chain ID into the v value
If nine fields were hashed for signing, v MUST be set to {0,1} + CHAIN_ID * 2 + 35. Here {0,1} is the parity of the y value of the curve point whose x value is r, from the secp256k1 signing process. On mainnet (CHAIN_ID = 1), v becomes 37 or 38. If the old scheme of hashing only six fields was used, v remains {0,1} + 27 as before. In other words, the v value alone reveals which scheme was used and for which chain the signature was made.
Recovery rule and backwards compatibility
If block.number >= FORK_BLKNUM and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then recovering the signer address hashes 9 RLP elements instead of 6. The old signing scheme using v = 27, 28 remains valid and continues to work under the previous rules. The new scheme is specified as SHOULD (recommended), so it is designed to allow gradual migration rather than a forced switch.
Activation point and parameters
This EIP is included in the Spurious Dragon hard fork (EIP-607) and takes effect starting at block FORK_BLKNUM = 2,675,000. Mainnet's CHAIN_ID is 1. The original text's chain ID list includes Morden (2, deprecated) and Expanse mainnet (2), Ropsten (3), Rinkeby (4), Goerli (5), Kovan (42), and the Geth private chain default (1337), and additional chain IDs can be checked and registered at chainid.network and the ethereum-lists/chains repository.
Actual values from the original example
For a transaction with nonce = 9, gasprice = 20 * 10**9, startgas = 21000, to = 0x3535...3535, value = 10**18, and empty data, the signing data is 0xec098504a817c800825208943535...018080 and the signing hash is 0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53. Signing with the private key 0x4646...4646 produces v = 37 instead of 27. The r, s values and the final signed transaction bytes are also given as is in the original Example section.
Recommends adoption by other chains
The original Rationale explains that this scheme provides a way to send transactions that work on Ethereum but not on ETC or the Morden testnet. It recommends that ETC adopt this EIP with a different CHAIN_ID value, and recommends that all future testnets, consortium chains, and alternative Ethereum chains likewise adopt it with their own unique values. This EIP's status is Final, meaning the specification is settled and will not change further.