From c10ae6b46194b6e33fbe27673c235e84bdf9f223 Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Mon, 27 Apr 2026 18:12:46 -0300 Subject: [PATCH 1/6] Emit execution-witness ancestor headers in ascending block-number order so the resulting list satisfies `headers[i].parent_hash == keccak(headers[i-1])`, matching the EELS contract for `validate_headers`. The generator walked the chain backward (newest -> oldest), so reverse the byte list before returning. This is a no-op for current consumers because the headers are stored in a `BTreeMap` keyed by number, but it makes the witness valid for any spec-conformant stateless verifier and is a precondition for adding the EIP-8025 contiguity check on the consumer side. Reference: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 --- crates/blockchain/blockchain.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 078e12a87a..0752b611d0 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -1436,6 +1436,11 @@ impl Blockchain { block_headers_bytes.push(current_header.encode_to_vec()); } + // EELS witness format requires headers in ascending block-number order so + // each header's `parent_hash` matches `keccak(prev header bytes)`. We walked + // the chain backward (newest → oldest), so reverse to ascend. + // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 + block_headers_bytes.reverse(); // Get initial state trie root and embed the rest of the trie into it let nodes: BTreeMap = used_trie_nodes @@ -1670,6 +1675,11 @@ impl Blockchain { block_headers_bytes.push(current_header.encode_to_vec()); } + // EELS witness format requires headers in ascending block-number order so + // each header's `parent_hash` matches `keccak(prev header bytes)`. We walked + // the chain backward (newest → oldest), so reverse to ascend. + // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 + block_headers_bytes.reverse(); // Get initial state trie root and embed the rest of the trie into it let nodes: BTreeMap = used_trie_nodes From 4a4a69690b4d71033fe428d367ff10e45b84b69c Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Mon, 27 Apr 2026 18:13:06 -0300 Subject: [PATCH 2/6] =?UTF-8?q?Align=20execution-witness=20consumer=20with?= =?UTF-8?q?=20EIP-8025=20=C2=A7Tolerance=20and=20=C2=A7Completeness:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Tolerance: when decoding state nodes and ancestor headers in `into_execution_witness` and `from_witness`, drop entries that fail to RLP-decode instead of failing the whole conversion. A bad/extra entry cannot be looked up by hash anyway; if the trie walk or BLOCKHASH path actually requires the dropped entry, the lookup fails explicitly there. Mirrors EELS `witness_state.build_node_db` and geth `MakeHashDB`, which both store entries keyed by hash without pre-validation. - Header chain contiguity: in `from_witness`, walk the header byte list in order and reject when `headers[i].parent_hash != keccak(headers[i-1])`. A reordered or fragmented header chain is not a valid witness even if the by-number lookup would otherwise resolve to the right header. Mirrors EELS `stateless.validate_headers`. Malformed entries are treated as a chain break (subsequent headers won't satisfy the parent_hash check), preserving the tolerance behavior for blocks that do not actually request the bad header. - Codes completeness: `get_account_code` and `get_code_metadata` now error on missing bytecode instead of silently defaulting to empty code. EIP-8025 mandates that a stateless executor reaching a code lookup whose hash is not in the witness treat the witness as incomplete and reject. Matches EELS `witness_state.get_code` (raises KeyError on miss) and geth's documented "bytecode lookup will error on junk" model. References: - https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L37-L42 - https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L204-L212 - https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 - https://github.com/ethereum/go-ethereum/blob/master/core/stateless/database.go#L26-L67 --- .../common/types/block_execution_witness.rs | 140 ++++++++++-------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/crates/common/types/block_execution_witness.rs b/crates/common/types/block_execution_witness.rs index 71071764fa..8d0480fc5a 100644 --- a/crates/common/types/block_execution_witness.rs +++ b/crates/common/types/block_execution_witness.rs @@ -151,36 +151,37 @@ impl RpcExecutionWitness { )); } - let mut initial_state_root = None; - - for h in &self.headers { - let header = BlockHeader::decode(h)?; - if header.number == first_block_number - 1 { - initial_state_root = Some(header.state_root); - break; - } - } - - let initial_state_root = initial_state_root.ok_or_else(|| { - GuestProgramStateError::Custom(format!( - "header for block {} not found", - first_block_number - 1 - )) - })?; - + // Skip headers that fail to decode (tolerance) and pick the parent by number. + let initial_state_root = self + .headers + .iter() + .filter_map(|h| BlockHeader::decode(h).ok()) + .find(|header| header.number == first_block_number - 1) + .map(|header| header.state_root) + .ok_or_else(|| { + GuestProgramStateError::Custom(format!( + "header for block {} not found", + first_block_number - 1 + )) + })?; + + // Tolerate witness entries that fail to decode (malformed RLP, or the `Null` + // node `[0x80]` other clients emit): drop them silently. They cannot be looked + // up by hash, so tolerating them is safe. If execution actually requires one, + // the trie walk will fail with a missing-node error (completeness check). + // + // Matches EELS `witness_state.build_node_db` and geth `MakeHashDB`, both of + // which store entries keyed by hash without pre-validation: + // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L37-L42 + // https://github.com/ethereum/go-ethereum/blob/master/core/stateless/database.go#L26-L67 let nodes: BTreeMap = self .state .into_iter() .filter_map(|b| { - if b == Bytes::from_static(&[0x80]) { - // other implementations of debug_executionWitness allow for a `Null` node, - // which would fail to decode in ours - return None; - } - let hash = keccak(&b); - Some(Node::decode(&b).map(|node| (hash, node))) + let node = Node::decode(&b).ok()?; + Some((keccak(&b), node)) }) - .collect::>()?; + .collect(); // get state trie root and embed the rest of the trie into it let state_trie_root = if let NodeRef::Node(state_trie_root, _) = @@ -325,17 +326,31 @@ impl GuestProgramState { value: ExecutionWitness, crypto: &dyn Crypto, ) -> Result { - let block_headers: BTreeMap = value - .block_headers_bytes - .into_iter() - .map(|bytes| BlockHeader::decode(bytes.as_ref())) - .collect::, _>>() - .map_err(|e| { - GuestProgramStateError::Custom(format!("Failed to decode block headers: {}", e)) - })? - .into_iter() - .map(|header| (header.number, header)) - .collect(); + // Decode headers and verify they form a contiguous chain in list order: + // every header's `parent_hash` must equal `keccak256(previous header bytes)`. + // Mirrors EELS `stateless.validate_headers`: + // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 + // A non-contiguous chain (e.g. reordered headers) makes the witness invalid + // even if a by-number lookup would otherwise resolve to the right header. + let mut block_headers: BTreeMap = BTreeMap::new(); + let mut prev_hash: Option = None; + for bytes in &value.block_headers_bytes { + let Ok(header) = BlockHeader::decode(bytes.as_ref()) else { + // Tolerate malformed entries by treating them as a chain break: + // subsequent headers will not satisfy the parent_hash check. + prev_hash = None; + continue; + }; + if let Some(expected_parent) = prev_hash { + if header.parent_hash != expected_parent { + return Err(GuestProgramStateError::Custom( + "witness headers are not contiguous".to_string(), + )); + } + } + prev_hash = Some(H256(crypto.keccak256(bytes))); + block_headers.insert(header.number, header); + } let parent_number = value @@ -588,29 +603,28 @@ impl GuestProgramState { Ok(self.chain_config) } - /// Retrieves the account code for a specific account. - /// Returns an Err if the code is not found. + /// Retrieves the account code for a specific code hash. + /// + /// Errors if the code is not present in the witness. Per the EELS reference + /// implementation, a stateless executor that touches code missing from its + /// witness MUST treat the witness as incomplete and reject: + /// https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L204-L212 + /// Geth implements the same hash-keyed-store + error-on-miss model: + /// https://github.com/ethereum/go-ethereum/blob/master/core/stateless/database.go#L26-L46 pub fn get_account_code(&self, code_hash: H256) -> Result { if code_hash == *EMPTY_KECCACK_HASH { return Ok(Code::default()); } - match self.codes_hashed.get(&code_hash) { - Some(code) => Ok(code.clone()), - None => { - // We do this because what usually happens is that the Witness doesn't have the code we asked for but it is because it isn't relevant for that particular case. - // In client implementations there are differences and it's natural for some clients to access more/less information in some edge cases. - // Sidenote: logger doesn't work inside SP1, that's why we use println! - println!( - "Missing bytecode for hash {} in witness. Defaulting to empty code.", // If there's a state root mismatch and this prints we have to see if it's the cause or not. - hex::encode(code_hash) - ); - Ok(Code::default()) - } - } + self.codes_hashed.get(&code_hash).cloned().ok_or_else(|| { + GuestProgramStateError::Database(format!( + "missing bytecode for hash {} in witness", + hex::encode(code_hash) + )) + }) } - /// Retrieves code metadata (length) for a specific code hash. - /// This is an optimized path for EXTCODESIZE opcode. + /// Retrieves code metadata (length) for a specific code hash. Errors on miss for + /// the same completeness reason as `get_account_code`. pub fn get_code_metadata( &self, code_hash: H256, @@ -620,19 +634,17 @@ impl GuestProgramState { if code_hash == *EMPTY_KECCACK_HASH { return Ok(CodeMetadata { length: 0 }); } - match self.codes_hashed.get(&code_hash) { - Some(code) => Ok(CodeMetadata { + self.codes_hashed + .get(&code_hash) + .map(|code| CodeMetadata { length: code.bytecode.len() as u64, - }), - None => { - // Same as get_account_code - default to empty for missing bytecode - println!( - "Missing bytecode for hash {} in witness. Defaulting to empty code metadata.", + }) + .ok_or_else(|| { + GuestProgramStateError::Database(format!( + "missing bytecode for hash {} in witness", hex::encode(code_hash) - ); - Ok(CodeMetadata { length: 0 }) - } - } + )) + }) } /// When executing multiple blocks in the L2 it happens that the headers in block_headers correspond to the same block headers that we have in the blocks array. The main goal is to hash these only once and set them in both places. From 3ecf85c62405fce4a85beb052c5eb97e04f27fc9 Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Mon, 27 Apr 2026 18:13:17 -0300 Subject: [PATCH 3/6] Re-enable the 9 EIP-8025 stateless validation_* tests that were skipped under `feature = "stateless"`. They now pass with the witness-consumer alignment (tolerance + contiguity + codes-completeness) and the generator-side ascending header order: - validation_state_extra_unused_trie_node - validation_headers_malformed_rlp_header - validation_headers_missing_oldest_blockhash_ancestor - validation_headers_missing_parent_header - validation_headers_non_contiguous_chain - validation_codes_missing_delegated_code_on_insufficient_balance_call - validation_codes_missing_external_code_read_target - validation_codes_missing_redelegation_old_marker - validation_codes_missing_sender_delegation_marker `make test-stateless`: 8720 passed / 0 failed / 0 ignored (~155 s). `make test-levm`: unchanged. --- tooling/ef_tests/blockchain/tests/all.rs | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/tooling/ef_tests/blockchain/tests/all.rs b/tooling/ef_tests/blockchain/tests/all.rs index c9513b2bf9..f2443a3c91 100644 --- a/tooling/ef_tests/blockchain/tests/all.rs +++ b/tooling/ef_tests/blockchain/tests/all.rs @@ -28,28 +28,7 @@ const EXTRA_SKIPS: &[&str] = &[ "Return50000", "static_Call1MB1024Calldepth", ]; -#[cfg(feature = "stateless")] -const EXTRA_SKIPS: &[&str] = &[ - // zkevm@v0.3.3 tolerance tests: the fixture's `statelessOutputBytes` declares `valid = 1` - // because the executed path does not actually consume the malformed/extra/missing witness - // entry, but our RpcExecutionWitness conversion eagerly validates the full witness and - // rejects it. Re-enable once the witness conversion is lazy per EIP-8025 §Tolerance. - "validation_headers_malformed_rlp_header", - "validation_headers_missing_oldest_blockhash_ancestor", - "validation_headers_missing_parent_header", - "validation_state_extra_unused_trie_node", - // zkevm@v0.3.3 rejection tests: `statelessOutputBytes` declares `valid = 0` so the guest - // program must reject the deliberately-incomplete witness, but our stateless path runs - // to completion instead of detecting the missing entry. Re-enable once the witness - // completeness checks land (missing delegation/external-code bytecodes, non-contiguous - // header chain detection). - "validation_codes_missing_delegated_code_on_insufficient_balance_call", - "validation_codes_missing_external_code_read_target", - "validation_codes_missing_redelegation_old_marker", - "validation_codes_missing_sender_delegation_marker", - "validation_headers_non_contiguous_chain", -]; -#[cfg(not(any(feature = "sp1", feature = "stateless")))] +#[cfg(not(feature = "sp1"))] const EXTRA_SKIPS: &[&str] = &[]; // Select backend From e72f5ea2ff5cf25fa487b282967d4b48526cdd97 Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Mon, 27 Apr 2026 18:16:48 -0300 Subject: [PATCH 4/6] Trim verbose comments in the witness conversion and generator changes; keep the spec link but drop the prose. No behavior change. --- crates/blockchain/blockchain.rs | 12 ++---- .../common/types/block_execution_witness.rs | 39 ++++++------------- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 0752b611d0..87b34cc29e 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -1436,10 +1436,8 @@ impl Blockchain { block_headers_bytes.push(current_header.encode_to_vec()); } - // EELS witness format requires headers in ascending block-number order so - // each header's `parent_hash` matches `keccak(prev header bytes)`. We walked - // the chain backward (newest → oldest), so reverse to ascend. - // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 + // EELS expects ascending order; we walked the chain backward, so reverse. + // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 block_headers_bytes.reverse(); // Get initial state trie root and embed the rest of the trie into it @@ -1675,10 +1673,8 @@ impl Blockchain { block_headers_bytes.push(current_header.encode_to_vec()); } - // EELS witness format requires headers in ascending block-number order so - // each header's `parent_hash` matches `keccak(prev header bytes)`. We walked - // the chain backward (newest → oldest), so reverse to ascend. - // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 + // EELS expects ascending order; we walked the chain backward, so reverse. + // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 block_headers_bytes.reverse(); // Get initial state trie root and embed the rest of the trie into it diff --git a/crates/common/types/block_execution_witness.rs b/crates/common/types/block_execution_witness.rs index 8d0480fc5a..d417e9b638 100644 --- a/crates/common/types/block_execution_witness.rs +++ b/crates/common/types/block_execution_witness.rs @@ -151,7 +151,7 @@ impl RpcExecutionWitness { )); } - // Skip headers that fail to decode (tolerance) and pick the parent by number. + // §Tolerance: skip headers that fail to decode, then pick parent by number. let initial_state_root = self .headers .iter() @@ -165,15 +165,9 @@ impl RpcExecutionWitness { )) })?; - // Tolerate witness entries that fail to decode (malformed RLP, or the `Null` - // node `[0x80]` other clients emit): drop them silently. They cannot be looked - // up by hash, so tolerating them is safe. If execution actually requires one, - // the trie walk will fail with a missing-node error (completeness check). - // - // Matches EELS `witness_state.build_node_db` and geth `MakeHashDB`, both of - // which store entries keyed by hash without pre-validation: - // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L37-L42 - // https://github.com/ethereum/go-ethereum/blob/master/core/stateless/database.go#L26-L67 + // EIP-8025 §Tolerance: drop entries that don't decode. They can't be looked + // up by hash anyway; if execution needs them, the trie walk fails there. + // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L37-L42 let nodes: BTreeMap = self .state .into_iter() @@ -326,18 +320,15 @@ impl GuestProgramState { value: ExecutionWitness, crypto: &dyn Crypto, ) -> Result { - // Decode headers and verify they form a contiguous chain in list order: - // every header's `parent_hash` must equal `keccak256(previous header bytes)`. - // Mirrors EELS `stateless.validate_headers`: - // https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 - // A non-contiguous chain (e.g. reordered headers) makes the witness invalid - // even if a by-number lookup would otherwise resolve to the right header. + // EIP-8025 §Completeness: headers must form a contiguous chain in list order + // (each `parent_hash` matches keccak of the previous header bytes). Reordered + // or fragmented chains are invalid even if by-number lookup would resolve. + // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 let mut block_headers: BTreeMap = BTreeMap::new(); let mut prev_hash: Option = None; for bytes in &value.block_headers_bytes { let Ok(header) = BlockHeader::decode(bytes.as_ref()) else { - // Tolerate malformed entries by treating them as a chain break: - // subsequent headers will not satisfy the parent_hash check. + // Malformed entry is a chain break; the next parent_hash check fails. prev_hash = None; continue; }; @@ -603,14 +594,9 @@ impl GuestProgramState { Ok(self.chain_config) } - /// Retrieves the account code for a specific code hash. + /// Retrieves bytecode by code hash. Errors if missing — EIP-8025 §Completeness. /// - /// Errors if the code is not present in the witness. Per the EELS reference - /// implementation, a stateless executor that touches code missing from its - /// witness MUST treat the witness as incomplete and reject: - /// https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L204-L212 - /// Geth implements the same hash-keyed-store + error-on-miss model: - /// https://github.com/ethereum/go-ethereum/blob/master/core/stateless/database.go#L26-L46 + /// Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L204-L212 pub fn get_account_code(&self, code_hash: H256) -> Result { if code_hash == *EMPTY_KECCACK_HASH { return Ok(Code::default()); @@ -623,8 +609,7 @@ impl GuestProgramState { }) } - /// Retrieves code metadata (length) for a specific code hash. Errors on miss for - /// the same completeness reason as `get_account_code`. + /// Code length by hash. Errors on miss, like `get_account_code`. pub fn get_code_metadata( &self, code_hash: H256, From 22d4e39e70d39e59cc90a5784aea995291fc3f53 Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Tue, 28 Apr 2026 12:01:39 -0300 Subject: [PATCH 5/6] =?UTF-8?q?Drop=20the=20=C2=A7Tolerance/=C2=A7Complete?= =?UTF-8?q?ness=20section=20markers=20from=20the=20witness-conversion=20co?= =?UTF-8?q?mments=20=E2=80=94=20keep=20the=20prose=20and=20the=20spec=20li?= =?UTF-8?q?nks,=20just=20remove=20the=20symbol-plus-word=20labels.=20Resto?= =?UTF-8?q?re=20the=20`This=20is=20an=20optimized=20path=20for=20EXTCODESI?= =?UTF-8?q?ZE=20opcode.`=20doc=20line=20on=20`get=5Fcode=5Fmetadata`=20tha?= =?UTF-8?q?t=20was=20dropped=20while=20trimming.=20No=20behavior=20change.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/common/types/block_execution_witness.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/common/types/block_execution_witness.rs b/crates/common/types/block_execution_witness.rs index d417e9b638..fbac11c5e3 100644 --- a/crates/common/types/block_execution_witness.rs +++ b/crates/common/types/block_execution_witness.rs @@ -151,7 +151,7 @@ impl RpcExecutionWitness { )); } - // §Tolerance: skip headers that fail to decode, then pick parent by number. + // Skip headers that fail to decode, then pick parent by number. let initial_state_root = self .headers .iter() @@ -165,8 +165,8 @@ impl RpcExecutionWitness { )) })?; - // EIP-8025 §Tolerance: drop entries that don't decode. They can't be looked - // up by hash anyway; if execution needs them, the trie walk fails there. + // EIP-8025: drop entries that don't decode. They can't be looked up by + // hash anyway; if execution needs them, the trie walk fails there. // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L37-L42 let nodes: BTreeMap = self .state @@ -320,8 +320,8 @@ impl GuestProgramState { value: ExecutionWitness, crypto: &dyn Crypto, ) -> Result { - // EIP-8025 §Completeness: headers must form a contiguous chain in list order - // (each `parent_hash` matches keccak of the previous header bytes). Reordered + // EIP-8025: headers must form a contiguous chain in list order (each + // `parent_hash` matches keccak of the previous header bytes). Reordered // or fragmented chains are invalid even if by-number lookup would resolve. // Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/stateless.py#L171-L191 let mut block_headers: BTreeMap = BTreeMap::new(); @@ -594,7 +594,7 @@ impl GuestProgramState { Ok(self.chain_config) } - /// Retrieves bytecode by code hash. Errors if missing — EIP-8025 §Completeness. + /// Retrieves bytecode by code hash. Errors if missing — EIP-8025. /// /// Ref: https://github.com/ethereum/execution-specs/blob/projects/zkevm/src/ethereum/forks/amsterdam/witness_state.py#L204-L212 pub fn get_account_code(&self, code_hash: H256) -> Result { @@ -610,6 +610,7 @@ impl GuestProgramState { } /// Code length by hash. Errors on miss, like `get_account_code`. + /// This is an optimized path for EXTCODESIZE opcode. pub fn get_code_metadata( &self, code_hash: H256, From 3de476f2e730ae5ea28a07ab8b00a98f88bdaf4a Mon Sep 17 00:00:00 2001 From: avilagaston9 Date: Tue, 28 Apr 2026 13:51:23 -0300 Subject: [PATCH 6/6] =?UTF-8?q?Collapse=20the=20nested=20`if=20let=20Some(?= =?UTF-8?q?expected=5Fparent)=20=3D=20prev=5Fhash=20{=20if=20header.parent?= =?UTF-8?q?=5Fhash=20!=3D=20expected=5Fparent=20{=20...=20}=20}`=20in=20`f?= =?UTF-8?q?rom=5Fwitness`=20into=20a=20single=20let-chain=20`if`.=20Requir?= =?UTF-8?q?ed=20to=20satisfy=20`clippy::collapsible=5Fif`=20under=20`-D=20?= =?UTF-8?q?warnings`=20on=20rust=201.91=20=E2=80=94=20the=20CI=20Lint=20an?= =?UTF-8?q?d=20Lint=20L2=20jobs=20were=20failing=20on=20this.=20Behavior?= =?UTF-8?q?=20unchanged.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/common/types/block_execution_witness.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/common/types/block_execution_witness.rs b/crates/common/types/block_execution_witness.rs index fbac11c5e3..4363b3b32e 100644 --- a/crates/common/types/block_execution_witness.rs +++ b/crates/common/types/block_execution_witness.rs @@ -332,12 +332,12 @@ impl GuestProgramState { prev_hash = None; continue; }; - if let Some(expected_parent) = prev_hash { - if header.parent_hash != expected_parent { - return Err(GuestProgramStateError::Custom( - "witness headers are not contiguous".to_string(), - )); - } + if let Some(expected_parent) = prev_hash + && header.parent_hash != expected_parent + { + return Err(GuestProgramStateError::Custom( + "witness headers are not contiguous".to_string(), + )); } prev_hash = Some(H256(crypto.keccak256(bytes))); block_headers.insert(header.number, header);