EIP-1344: Add chain id opcode

For some use cases this is still perfectly safe behavior, even if it changes values e.g. meta-transactions.

The problem is not that it is safe in some behaviour, the problem is that it can be easily used wrongly. If an application use it wrongly by assuming that simply checking chainID for equality is enough and a hardfork that change the chainID happen, the application get broken.

On that note contrary to what you say, meta transactions themselves would benefit from accepting past chainID. I would actually argue that is indeed unsafe to not do it as there are scenarios where the user will be unavailable to resubmit the meta tx in time. The fact that you do not see it prove that it is easy to used that opcode wrongly.
When users send metatx, they assume the tx will be executed. They don’t expect to have to sign it again (not just resubmitting, as a relayer could do it) later. If the meta-tx smart contract do not allow for past chainID, this assumption break easily at fork transition. A fork should ideally not bother the users. A no downtime solution should always be the goal. So if we can achieve this, we should do it.

It could be argued that around the fork time, wallet could already force the use of future chainID (and relayer would then wait that time before publishing the metatx) but this assume they know what the message is destined to be use for.

A minority-led fork is a special case of the scenario where one side changes chain ID and the other side doesn’t. This scenario is already mentioned.

As mentioned, that special case (which you agreed was an important concern in EIP-1959 discussion. You even pointed that as the weak point of EIP-1959 since it simply return a boolean) can’t be handled properly via the opcode proposed here (see below). As such it should be mentioned in the EIP.

  1. If the minority fork doesn’t implement replay protection, then yes replays are possible. That should be obvious.
  2. A properly-implemented caching mechanism can be purely trustless in this scenario, as anyone can operate it (it may even be included within the application’s user flow)

These 2 points show a misunderstanding of the issue.
The smart Contract in question would have replay protection using the caching solution mentioned in the rationale. It thus receive the chainId from the message (and let say it also receive the block number representing the time at which the message was signed) and check it against the cache.
Now let say a minority-led hardfork happen at block X so the minority fork has a new chainID from that time onward while the majority chain keep the same chainID (since it did not fork)
Now let say a user of both majority and minority chain signs a message for that smart contract at block X+1 and want it to be applied to the majority chain only (the purpose of replay protection). Unfortunately the user has no choice but to use the majority chainID which turn out to also be a past chainID of the minority chain (It would already be cached there).
As you can see, a replay is possible. Now the way to prevent the problem is to use the blockNumber provided as part of the message to check if the message block number happen after the fork (if it does then it should not be replayable). Unfortunately since the cache has a delay inherent due to being implemented as a normal smart contract the replay will still be possible for message signed at blockNumber smaller than the blockNumber at which the cache was updated with the latest chain ID.

EIP1965 does not have this issue and allow thus minority-led hard fork to happen without being exposed to replays, This preserves an important decentralisation component of blockchain where we should give equal opportunities to every communities that want to fork away. Of course, in the case of EIP-1344 the replay window is small but that could be critical in some situation.

Also, there may be application-specific scenarios where forcing the use of fork block height to differentiate changes in chain ID would be unnecessary, and potentially even harmful. The example given in the other thread was that of Plasma’s message structure (a fairly large use case for EIP-712 and this proposal).

You simply showed there that plasma did not need to use the precise block number at which the chainID changed.
As for forcing the use of a block number, this is not true. You can always provide your own. In other words both can be used, the application specific block number and the block number assigned by EIP-712. The point is that in order to guarantee replay protection for those application that require the strict version of it, EIP-712 will need to be modified so wallet ensure correct values to protect their users.

messages don’t fall into corner cases, which would not be possible with a strictly fork block height-based mechanism.

As mentioned above, nothing prevent you from using a different mechanism for blockNumber. EIP1965 will still allow you to verify that a chainID was valid at a specific block no matter how you came up with that number.

To conclude, while I agree that not all points mentioned need to be added/ expanded in the rationale. the point about minority-led hard fork need to be addressed, (like EIP-1959 does for example)
I would also still strongly advise you to clarify better why a naive chainID equality check is dangerous.

That’s a good point. Contrary to @fubuloubu 's opinion, I think this is an important feature to have. Using a similar system than EIP1965 would allow transaction at fork boundary to be accepted as normal without downtime, an obviously desirable property form the user point of view. The users might also want to be sure their transactions do not get replayed on a particular side of the fork, even before the fork happen. Arround the time of the fork, the wallet could set the transaction chainId to be of the future chainID. Miner would leave it in the pool until the chainID change. For dealing with minority-led fork, the blockNumber at which the transaction was signed should also be included (like EIP-1965)

I’m very sorry if this has been discussed before: From a conceptual standpoint, I would prefer if this could be a precompiled contract, since it is external to actual computations in the EVM. Until we reduce the gas costs for calls to precompiled contracts (I would very much like to include this in the next hard fork, if possible), using an opcode is probably the only practical possibility.

If we “waste” a full opcode for this, the let’s at least make it extensible in some way: Make it take a parameter and return the Chain ID if the parameter is zero, the Hardfork number if it is one, and so on and also define reserved and non-reserved parameters.

Would that be a good suggestion?

Would like to make the point that tx.origin is also external to the actual computations in the EVM, and is obtained through the signed transaction in a very similar way that chain ID would be obtained. Chain ID was added to the transaction structure in EIP-155 as a protocol update, so in many ways this is just completing that work.

1 Like

There is alternative suggestions more along this path (EIP-1959, EIP-1965) if you are interested. I believe them not to be as flexible for the end-user as this proposal, but feel free to check those out. It has been suggested to make those pre-compiles due to their added complexity.

I decided to draft an example of a “Trustless Oracle Contract” for historical chain IDs, which is something that could very easily be implemented, audited, and leveraged as a standard for any potential use case requiring access to historical chain IDs, without the complexity of alternative proposals.

Check it out here:


Again would like to note that assuming what the end user might want to use current chain ID for may not be known in advance, and adding a more complicated API may otherwise prevent the implementation of specific use cases that a simpler API would allow through some smart contract magic.

1 Like

Wow we were thinking yesterday along the same lines then. I’ve draft this EIP which may be relevant: https://github.com/ethereum/EIPs/pull/2014

2 Likes

EIP is now implemented in aleth https://github.com/ethereum/aleth/pull/5696

1 Like

Was accepted for Istanbul as of the call today, however @karalabe made the suggestion that implementations should reference a hard-coded value of chain ID present in the client instead of the value provided by EIP-155 compatible transactions (which the reference implementation uses). This would reduce the corner case that EIP-155 technical allows not providing chain ID in a transaction, and also makes it more compatible with EIP-695 (adds Chain ID JSON-RPC call).

1 Like

@fubuloubu can you specify the value range of the chainid returned? Is it 64-bit, is it 256-bit, …?

It would be nice to know it as we’re trying to have explicit ranges, see EIP-1985.

256-bit. I didn’t realize it wasn’t in the proposal

1 Like

It’s perfect, thank you.

This EIP is well past last call date. Will the process to move from Last Call to Accepted be activated?

2 Likes

I have https://github.com/ethereum/EIPs/pull/1994 open for that.

With recommended inclusion into Istanbul, I am hoping we can move this to Accepted soon so that implementers may be able to reference this proposal in a Solidified state.

I’m still working on the reference implementation into Trinity, but the Aleth implementation is here: https://github.com/ethereum/aleth/pull/5696

To clear this part of the confusion: that was added after the adoption of EIP-155 in a hard fork as well as the update of YP.

I clearly remember the ACD calls (around 2016 or 2017) where a 1 byte value was agreed on.

While I think this is a clever idea, in the worst case it means the calculation for the transaction exceeds 256 bits.

Thank you. Maybe more clever (read: risky) is an alternate implementation that does not depend on concatenation:

sha3(sha3(tar_gz(reference_implementation)) ^ sha3(genesis_block)) ^ sha3(genesis_block)

Maybe the 64-bit unsigned integer (uint64) would be enough? I don’t like planning for use cases that may be never needed. Even if you want to use a hash function, truncating the output to 64 bits seems good enough (@axic’s tip).

I’m strongly in favour not making it 256-bit wide. Allowing it to be 256-bit wide means that the RLP encoding of a transaction must use >256-bit arithmetic to calculate the v field.

1 Like

64 bits is a decent amount of entropy to avoid hash collisions with normal use, and there would likely be other mitigating factors if chain ID were hash-based and someone wished to exploit it to fake out other network node’s network discovery phases (or however it might be used). Given @axic’s observation of the need to change RLP encoding of transactions to support a larger field, I think this is a reasonable request.

What’s the worst case scenario for the v field?


If there’s consensus for this, I can update the EIP