-
Notifications
You must be signed in to change notification settings - Fork 391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Synchronously check all transactions
to have non-zero length
#573
Conversation
As part of `newPayload` block hash verification, the `transactionsRoot` is computed by the EL. Because Merkle-Patricia Tries cannot contain `[]` entries, MPT implementations typically treat setting a key to `[]` as deleting the entry for the key. This means that if a CL receives a block with `transactions` containing one or more zero-length transactions, that such transactions will effectively be skipped when computing the `transactionsRoot`. Note that `transactions` are opaque to the CL and zero-length transactions are not filtered out before `newPayload`. ```python # https://eips.ethereum.org/EIPS/eip-2718 def compute_trie_root_from_indexed_data(data): """ Computes the root hash of `patriciaTrie(rlp(Index) => Data)` for a data array. """ t = HexaryTrie(db={}) for i, obj in enumerate(data): k = encode(i, big_endian_int) t.set(k, obj) # Implicitly skipped if `obj == b''` (invalid RLP) return t.root_hash ``` In any case, the `blockHash` validation may still succeed, resulting in a potential `SYNCING/ACCEPTED` result to `newPayload` by spec. Note, however, that there is an effective hash collision if a payload is modified by appending one or more zero-length transactions to the end of `transactions` list: In the trivial case, a block with zero transactions has the same `transactionsRoot` (and `blockHash`) as one of a block with one `[]` transaction (as that one is skipped). This means that the same `blockHash` can refer to a valid block (without extra `[]` transactions added), but also can refer to an invalid block. Because `forkchoiceUpdated` refers to blocks by `blockHash`, outcome may be nondeterministic and implementation dependent. If `forkchoiceUpdated` deems the `blockHash` to refer to a `VALID` object (obtained from a src that does not have the extra `[]` transactions, e.g., devp2p), then this could result in honest attestations to a CL beacon block with invalid `[]` transactions in its `ExecutionPayload`, risking finalizing it. The problem can be avoided by returning `INVALID` in `newPayload` if there are any zero-length `transactions` entries, preventing optimistic import of such blocks by the CL.
src/engine/paris.md
Outdated
@@ -161,26 +161,30 @@ The payload build process is specified as follows: | |||
|
|||
#### Specification | |||
|
|||
1. Client software **MUST** validate `blockHash` value as being equivalent to `Keccak256(RLP(ExecutionBlockHeader))`, where `ExecutionBlockHeader` is the execution layer block header (the former PoW block header structure). Fields of this object are set to the corresponding payload values and constant values according to the Block structure section of [EIP-3675](https://eips.ethereum.org/EIPS/eip-3675#block-structure), extended with the corresponding section of [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399#block-structure). Client software **MUST** run this validation in all cases even if this branch or any other branches of the block tree are in an active sync process. | |||
1. Client software **MUST** validate that all `transactions` have non-zero length (at least 1 byte). Client software **MUST** run this validation in all cases even if this branch or any other branches of the block tree are in an active sync process. Client software **MAY** employ stricter checks and validate that all `transactions` are fully valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why MAY in the second case? Can we change it to MUST as likely ELs run this validation either before or during the block hash computations, or not?
1. Client software **MUST** validate that all `transactions` have non-zero length (at least 1 byte). Client software **MUST** run this validation in all cases even if this branch or any other branches of the block tree are in an active sync process. Client software **MAY** employ stricter checks and validate that all `transactions` are fully valid. | |
1. Client software **MUST** validate that all `transactions` are encoded correctly and have non-zero length (at least 1 byte). Client software **MUST** run this validation in all cases even if this branch or any other branches of the block tree are in an active sync process. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the purpose of computing a unique block hash that's not affected by hash collisions, it's fine if a transaction is malformed as long as it is not empty.
MAY wording enables CL implementations to also perform the checks in situation where the EL is unavailable (maintenance etc); making this a MUST requires knowledge of individual transaction types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MAY introduces ambiguity as transactions encoding may be run synchronously or may be not depending on the state of the node and EL client implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's the same ambuity that's already present with steps (4) and (5). But arguably, the stricter transaction validation check is already covered by those. As in, if a transaction doesn't parse, it will fail in step (4). And step (5) already allows deferring validation until fcU. Will remove it from here. Only the empty-check has to be done always and must be synchronous, as in, there has to be an INVALID on newPayload if a tx is empty, so that there is no chance that allows the CL to optimistically import it and request an ambiguous fcu lateron.
Client software MUST validate the payload if it extends the canonical chain and requisite data for the validation is locally available. The validation process is specified in the Payload validation section.
Client software MAY NOT validate the payload if the payload doesn't belong to the canonical chain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This “ambiguity" is necessary to handle different EL client designs
It might make sense to have a Hive test covering the new check introduced by this PR, cc @marioevz |
As part of
newPayload
block hash verification, thetransactionsRoot
is computed by the EL. Because Merkle-Patricia Tries cannot contain[]
entries, MPT implementations typically treat setting a key to[]
as deleting the entry for the key. This means that if a CL receives a block withtransactions
containing one or more zero-length transactions, that such transactions will effectively be skipped when computing thetransactionsRoot
. Note thattransactions
are opaque to the CL and zero-length transactions are not filtered out beforenewPayload
.In any case, the
blockHash
validation may still succeed, resulting in a potentialSYNCING/ACCEPTED
result tonewPayload
by spec.Note, however, that there is an effective hash collision if a payload is modified by appending one or more zero-length transactions to the end of
transactions
list: In the trivial case, a block with zero transactions has the sametransactionsRoot
(andblockHash
) as one of a block with one[]
transaction (as that one is skipped).This means that the same
blockHash
can refer to a valid block (without extra[]
transactions added), but also can refer to an invalid block. BecauseforkchoiceUpdated
refers to blocks byblockHash
, outcome may be nondeterministic and implementation dependent. IfforkchoiceUpdated
deems theblockHash
to refer to aVALID
object (obtained from a src that does not have the extra[]
transactions, e.g., devp2p), then this could result in honest attestations to a CL beacon block with invalid[]
transactions in itsExecutionPayload
, risking finalizing it.The problem can be avoided by returning
INVALID
innewPayload
if there are any zero-lengthtransactions
entries, preventing optimistic import of such blocks by the CL.