fix: drop HashTreeRoot from XMSS-containing SSZ types#302
fix: drop HashTreeRoot from XMSS-containing SSZ types#302MegaRedHand wants to merge 1 commit intomainfrom
Conversation
🤖 Kimi Code ReviewThe PR correctly addresses an SSZ spec divergence where Key observations:
Verdict: Approved. The changes are precise, well-documented, and improve safety by making the intentional spec divergence explicit and compiler-enforced. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Claude Code ReviewNow I have a complete picture. Here's my review: PR 302 —
|
🤖 Codex Code ReviewNo findings. This PR looks like the right hardening move: it removes potentially incorrect The SSZ test change in ssz_spectests.rs#L58 is also consistent with that design: serialization compatibility is still enforced for these wire types, while the incorrect tree-hash expectation is no longer treated as passing consensus behavior. The only caveat is future-facing: if the project ever needs I couldn’t run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryDrops Confidence Score: 5/5Safe to merge — the only finding is a P2 style suggestion about a All production-code changes are compile-time only (removed derive macros); any caller of crates/common/types/tests/ssz_spectests.rs — minor double-deserialization and unreachable debug assertion in run_typed_test.
|
| Filename | Overview |
|---|---|
| crates/common/types/src/attestation.rs | Removes HashTreeRoot derive from SignedAttestation with a clear doc comment explaining the serialization vs. Merkleization mismatch for XmssSignature; all remaining types that do need hashing retain the derive. |
| crates/common/types/src/block.rs | Removes HashTreeRoot derive from SignedBlock and BlockSignatures with well-documented rationale; Block, BlockHeader, BlockBody, and AggregatedSignatureProof correctly retain the derive. |
| crates/common/types/tests/ssz_spectests.rs | Extracts shared check_ssz_roundtrip helper and adds run_serialization_only_test for XMSS-containing types; run_typed_test deserializes the fixture twice (once inside the helper, once for the hash-root check) and the debug_assert_eq! guarding that duplication is unreachable in release-mode runs. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[run_ssz_test] --> B{type_name?}
B -- "Block, State, Attestation,\nBlockHeader, etc." --> C[run_typed_test]
B -- "SignedAttestation\nSignedBlock\nBlockSignatures" --> D[run_serialization_only_test]
C --> E[check_ssz_roundtrip\nencode + decode round-trip]
D --> E
E --> F{Encoding\nmatch?}
F -- No --> G[Error: encoding mismatch]
F -- Yes --> H{Round-trip\nmatch?}
H -- No --> I[Error: round-trip mismatch]
H -- Yes --> J[Return expected_bytes]
C --> K[Deserialize F→D again\ncompute hash_tree_root]
K --> L{Root\nmatch?}
L -- No --> M[Error: root mismatch]
L -- Yes --> N[✅ Pass]
D --> O[✅ Pass — root skipped\nXmssSignature diverges]
Prompt To Fix All With AI
This is a comment left during a code review.
Path: crates/common/types/tests/ssz_spectests.rs
Line: 111
Comment:
**`debug_assert_eq!` is unreachable in release-mode test runs**
Per the repo's test workflow (`cargo test --release`), `debug_assert_eq!` compiles away in every run, so the consistency guard it intends to provide — that the second `F → D` deserialization produces the same encoding as the one already validated inside `check_ssz_roundtrip` — is never actually exercised.
If the check is meaningful, use `assert_eq!` instead. Alternatively, return the `domain_value` from `check_ssz_roundtrip` alongside `expected_bytes` to eliminate the duplicate deserialization pass entirely and make the guard unnecessary.
```suggestion
assert_eq!(
<D as libssz::SszEncode>::to_ssz(&domain_value),
expected_bytes,
"re-encoded domain value must match expected bytes"
);
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix: drop HashTreeRoot from XMSS-contain..." | Re-trigger Greptile
|
|
||
| // Step 2: SSZ encode and compare with expected serialized bytes | ||
| // Re-encode for the hash computation; cheap relative to the fixture I/O. | ||
| debug_assert_eq!(<D as libssz::SszEncode>::to_ssz(&domain_value), expected_bytes); |
There was a problem hiding this comment.
debug_assert_eq! is unreachable in release-mode test runs
Per the repo's test workflow (cargo test --release), debug_assert_eq! compiles away in every run, so the consistency guard it intends to provide — that the second F → D deserialization produces the same encoding as the one already validated inside check_ssz_roundtrip — is never actually exercised.
If the check is meaningful, use assert_eq! instead. Alternatively, return the domain_value from check_ssz_roundtrip alongside expected_bytes to eliminate the duplicate deserialization pass entirely and make the guard unnecessary.
| debug_assert_eq!(<D as libssz::SszEncode>::to_ssz(&domain_value), expected_bytes); | |
| assert_eq!( | |
| <D as libssz::SszEncode>::to_ssz(&domain_value), | |
| expected_bytes, | |
| "re-encoded domain value must match expected bytes" | |
| ); |
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/common/types/tests/ssz_spectests.rs
Line: 111
Comment:
**`debug_assert_eq!` is unreachable in release-mode test runs**
Per the repo's test workflow (`cargo test --release`), `debug_assert_eq!` compiles away in every run, so the consistency guard it intends to provide — that the second `F → D` deserialization produces the same encoding as the one already validated inside `check_ssz_roundtrip` — is never actually exercised.
If the check is meaningful, use `assert_eq!` instead. Alternatively, return the `domain_value` from `check_ssz_roundtrip` alongside `expected_bytes` to eliminate the duplicate deserialization pass entirely and make the guard unnecessary.
```suggestion
assert_eq!(
<D as libssz::SszEncode>::to_ssz(&domain_value),
expected_bytes,
"re-encoded domain value must match expected bytes"
);
```
How can I resolve this? If you propose a fix, please make it concise.The leanSpec `Signature` container is SSZ-serialized as fixed bytes (via `is_fixed_size=True`) for cross-client wire compatibility, but its hash tree root is computed as a Container (merkleized fields). Our Rust `XmssSignature` is `SszVector<u8, SIGNATURE_SIZE>`, whose hash tree root uses `merkleize(pack_bytes(...))` — identical serialization, different root. Since `SignedAttestation`, `SignedBlock`, and `BlockSignatures` are never hashed directly in production (only their inner `Block` / `AttestationData` are), drop the `HashTreeRoot` derive instead of mirroring the container layout. In the SSZ test harness, route these three types through a new `run_serialization_only_test` helper that still enforces encoding and round-trip conformance but skips the hash tree root check. Fixes 4 failing `test_consensus_containers` SSZ tests after the leanSpec bump to `e9ddede`.
960b14d to
71c4a73
Compare
Summary
HashTreeRootderive fromSignedAttestation,SignedBlock, andBlockSignatures— the three types that embedXmssSignature.run_serialization_only_testhelper in the SSZ spec-test harness. Encoding and round-trip are still enforced; the hash-tree-root check is skipped for them.Why
leanSpec's
Signaturecontainer is SSZ-serialized as fixed bytes (viais_fixed_size=True, for cross-client wire compatibility with Ream) but its hash tree root is computed as a Container (merkleized fields). Our RustXmssSignatureisSszVector<u8, SIGNATURE_SIZE>, whose hash tree root usesmerkleize(pack_bytes(...))— identical serialization, different root.Since
SignedAttestation,SignedBlock, andBlockSignaturesare never hashed directly in production code (only their innerBlock/AttestationDataare), dropping theHashTreeRootderive is cheaper than mirroring leanSpec's container layout. If any of them gets hashed later, we'll re-derive and match.Fixes 4 failing
test_consensus_containersSSZ tests after the leanSpec bump toe9ddede:test_block_signatures_emptytest_block_signatures_with_attestationtest_signed_attestation_minimaltest_signed_block_minimalTest plan
cargo test -p ethlambda-types --release --test ssz_spectestspasses (112/112)Follow-ups