Low bit widths for Transaction
fields
Description
The Transaction
struct is defined in sdk/circuit_input.go as follows:
type Transaction struct {
ChainId Uint248
BlockNum Uint32
Nonce Uint248
// GasTipCapOrGasPrice is GasPrice for legacy tx (type 0) and GasTipCapOap for
// dynamic-fee tx (type 2)
GasTipCapOrGasPrice Uint248
// GasFeeCap is always 0 for legacy tx
GasFeeCap Uint248
GasLimit Uint248
From Uint248
To Uint248
Value Bytes32
}
It is intended to reflect the data associated to an Ethereum transaction. The Ethereum yellow paper↗ (version 9fde3f4 from September 2nd, 2024) specifies the fields associated to a transaction in section 4.2. The types of most of the fields of the above struct are specified in display (18) of the yellow paper, as follows:
Nonce
— 256-bit unsigned integerGasTipCapOrGasPrice
— 256-bit unsigned integerGasFeeCap
— 256-bit unsigned integerGasLimit
— 256-bit unsigned integerTo
— 20-byte address or emptyValue
— 256-bit unsigned integer
Apart from To
and Value
, where the type in the struct above is sufficient to represent the entire range specified in the yellow paper, the other fields listed above are specified as 256-bit unsigned integers, but the struct only stores them as 248-bit unsigned integers. The gas-related values should in practice never become large enough to not fit into 248, though. The nonce is the number of transactions sent by the sender so far. It is not feasible to reach transactions on an account, so this value also fits in practice into 248 bits.
This leaves From
, ChainId
, and BlockNum
. The block number is not a part of the transaction in the Ethereum specification, but transactions are part of a block with a block number, specified in section 4.4, and in display (44) as a natural number of arbitrary size. In practice, it will take more than years until the Ethereum block number exhausts 248 bits, so this field is not a problem either. The From
field is not part of the transaction in the Ethereum specification but derived from the signature, but as addresses on Ethereum are 160 bits large, 248 bits suffice for From
as well.
It remains to discuss ChainId
. For the transaction, the Ethereum specification only specifies that the chain ID should be to the network's chain ID, which is 1 in the case of Ethereum Mainnnet. EIP-155↗, which introduced the chain ID, did not specify a maximum size. EIP-1344↗ suggests that the chain ID is a 256-bit value. There was some discussion (see for example on GitHub here↗ or on Ethereum Magicians here↗) to restrict the range, but nothing passed by the time this report was written, as far as we are aware.
There are some suggestions that chain IDs may be generated as hashes, in which case chain IDs not fitting in 248 bits would be realistic. However, the actual range for the chain IDs in Brevis is restricted further. The pack
function restricts ChainId
to only 32 bits. There are already chains with chain IDs that are larger than 32 bits.
Impact
The current circuits will not be usable for transaction on chains with a chain ID equal to or bigger than .
Recommendations
Should Brevis wish to be usable for such chains, the bit width allowed for ChainID
would need to be increased. Alternatively, we recommend to document as a comment in the code that it is an explicit choice.
Remediation
Brevis is planning to remove ChainId
in a future iteration of Brevis SDK.