EIP1965 - Method to check if a chainID is valid at a specific block Number
# Abstract
This EIP adds a precompile that returns whether a specific chainID (EIP-155 unique identifier) is valid at a specific blockNumber. ChainID are assumed to be valid up to the blockNumber at which they get replaced by a new chainID.
# Motivation
EIP-155 proposes to use the chain ID to prevent the replay of transactions between different chains. It would be a great benefit to have the same possibility inside smart contracts when handling off-chain message signatures, especially for Layer 2 signature schemes using EIP-712.
EIP-1344 is attempting to solve this by giving smart contract access to the tip of the chainID history. This is insuficient as such value is changing. Hence why EIP-1344 describes a contract based solution to work around the problem. It would be better to solve it in a simpler, cheaper and safer manner, removing the potential risk of misuse present in EIP-1344. Furthermore EIP-1344 can't protect replay properly for minority-led hardfork as the caching system cannot guarantee accuracy of the blockNumber at which the new chainID has been introduced.
EIP-1959 solves the issue of EIP-1344 but do not attempt to protect from minority-led hardfork as mentioned in the rationale. We consider this a mistake, since it remove some freedom to fork. We consider that all fork should be given equal oportunities. And while there will always be issues we can't solve for the majority that ignore a particular fork, users that decide to use both the minority-fork and the majority-chain should be protected from replay without having to wait for the majority chain to update its chainID.
# Specification
Adds a new precompile which uses 2 argument : a 32 bytes value that represent the chainID to test and a 32 bytes value representing the blockNumber at which the chainID is tested. It return 0x1 if the chainID is valid at the specific blockNumber, 0x0 otherwise. Note that chainID are considered valid up to the blockNumber at which they are replaced. So they are valid for every blockNumber past their replacement.
The operation will costs no more than G_blockhash
+ G_verylow
to execute. This could be lower as chainID are only introduced during hardfork.
The cost of the operation might need to be adjusted later as the number of chainID in the history of the chain grows.
Note though that the alternative to keep track of old chainID is to implement a smart contract based caching solution as EIP-1344 proposes comes with an overall higher gas cost and exhibit issues for minority-led hardfork (see Rationale section below). As such the gas cost is simply a necessary cost for the feature.
# Rationale
The rationale at EIP-1959 applies here as well too :
- An opcode is better than a caching system for past chainID, It is cheaper, safer and do not include gaps.
- Direct access to the latest chainID is dangerous since it make it easy for contract to use it as a replay protection mechanism while preventing otherwise valid old messages to be valid after a fork that change the chainID. This can have disastrous consequences on users.
- all off-chain messaged signed before a fork should be valid across all side of the fork.
The only difference is that this current proposal propose a solution to protect hardfork led by a minority.
To summarize there is 2 possible fork scenario :
The majority decide to make an hardfork but a minority disagree with it (ETC is such example). The fork is planned for block X. If the majority is not taking any action to automate the process of assigning a different chainID for both, the minority has plenty of time to plan for a chainID upgrade to happen at that same block X. Now if they do not do it, their users will face the problem that their messages will be replayable on the majority chain (Note that this is not true the other way around as we assume the majority decided to change the chainID). As such there is no reason that they’ll leave it that way.
A minority decide to create an hardfork that the majority disagree with (or simply ignore). Now, the same as above can happen but since we are talking about a minority there is a chance that the majority do not care about the minority. In that case, there would be no incentive for the majority to upgrade the chainID. This means that user of both side of the fork will have the messages meant for the majority chain replayable on the minority-chain (even if this one changed its chainID) unless extra precaution is taken.
The solution is to add the blockNumber representing the time at which the message was signed and use it as an argument to the opcode proposed here. This way, when the minority forks with a new chainID, the previous chainID become invalid from that time onward. So new messages destinated to the majority chain can't be replayed on the minority fork.
# Backwards Compatibility
EIP-712 is still in draft but would need to be updated to include the blockNumber as part of the values that wallets need to verify for the protection of their users.
Since chainID and blockNumber will vary, they should not be part of the domain separator (meant to be generated once) but another part of the message.
While the pair could be optional for contract that do not care about replays or have other ways to prevent them, if chainID is present, the blockNumber must be present too. And if any of them is present, wallet need to ensure that the chainID is indeed the latest one of the chain being used, while the blockNumber is the latest one at the point of signing. During fork transition, the wallet can use the blockNumber to know which chainID to use.
# References
This was previously suggested as part of EIP1959 discussion (opens new window).
# Copyright
Copyright and related rights waived via CC0 (opens new window).