Both sides of the fork will upgrade because both communities will (rationally) see the value in doing so… People, especially crypto communities, aren’t rational!
By the same argument, any chainID solution would not work, since you could always have a community that decide to use the same chainID as another one.
Realistically though, there are 2 possible situations :
-
The majority decide to make an hardfork but a minority disagree with it. 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. 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 such there is no reason that they’ll leave it that way,
-
A minority decide to create an hardfork that the majority disagree with. 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. If after all, the minority chain gets traction, the majority might change their mind and upgrade the chainID. At that point, users of the majority might be disapointed that a subset of their messages were replayed on the minority fork while they were not paying attention. This could be at the detriment of the minority as it would reduce its traction.
In that second case, you are right, the use of block height would indeed be useful, and as you said, we could return the block height at which a chainID become invalid. This way the minority chain can prevent replays from the majority chain, but this requires 2 things (not present in EIP-712 for example):
- messages need include both a chainID and the block height representing the time at which it was signed
- contract need to check that validChainID(
message's chainID
) >message's Block Height
where validChainID return 0 when invalid (not part of the history), 2^256-1 when the latest, and the blockHeight at which it was replaced for past chainIds.
It might be better to use time rather than block height though. This would provide a nicer user experience and do not tie the off-chain world to the on-chain one. At the same time, fork transition handling might be ticker to deal with since it is unknown what time it will be until the fork activate.
Application-level faults won’t occur due to the ability of a malicious attacker to sign a newer message with an older chain ID (this can happen outside of wallet software)
There is not malicious attacker scenario here. User can sign a new message with an old chainID if they desire to do so. Maybe they want their message to be replayed on all the fork sharing that chainID.
The one caveat is that if you have access to the current chain ID, then you can implement a block number-based solution easily (the opcode returns the block height after which the chain ID is no longer valid, or 0 if it is invalid, delegating the assert to user code)
There would be no need to access current chain ID. The issue mentioned here about a minority-led hardfork can be solved by VALID_CHAINID
return the time/blockHeight at which a chainID become invalid.
I ll update the spec in that regard and that should be all we need. Do you agree?