Repository navigation
perf(ssz-tree): hash merkle layers through sha2's block-level compression - #753
MegaRedHand wants to merge 1 commit into
Conversation
…sion Every inner node is SHA-256 of exactly 64 bytes: one data block plus one constant padding block. Going through `Digest` per pair buffers, pads and finalizes generically for each of millions of pairs per state root. A new `batch` module hashes a whole layer with `sha2::compress256` directly (initial state, the pair, the fixed padding block, read the state out), so the hardware backend sha2 selects at run time is still the one used, and the output is byte-identical to `hash_nodes(&Sha2Hasher, ..)`. `fold_roots` folds each level in place through it, and leaf roots (packed chunks and composite element roots) use a local `merkleize` built on the same primitive. The rayon fan-out above PARALLEL_HASH_HEIGHT is unchanged. Element roots of composite values still come from libssz. Adds the `compress` feature of the `sha2` already in the lockfile to ssz-tree (compress256 is gated behind it); no new crate.
🤖 Claude Code ReviewReview: PR 753, batched SHA-256 merkle layersVerdict: Looks correct and well-contained. I found nothing blocking. I read the diff only and did not build it or run the tests. Correctness (checked by hand)
Minor, non-blocking
No security or consensus concerns. Hash outputs are unchanged, so state roots remain compatible. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewLooks good overall. I did not find a correctness or security flaw in the new SSZ Merkle batching path, and the added property tests are a strong fit for consensus-critical hashing code. Notes
Potential follow-up
Validation
If you want, I can also turn this into a ready-to-paste GitHub review comment. Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which introduces batched SHA-256 hashing for SSZ Merkle tree operations. This is a performance optimization that bypasses the standard Overall AssessmentThe optimization is sound in principle—using the block-level compression function for 64-byte messages (one data block + one constant padding block) is a well-known optimization. However, there are several correctness, safety, and code quality issues to address. Critical Issues1. Endianness Bug in
|
…sion (#753) Every inner node is SHA-256 of exactly 64 bytes: one data block plus one constant padding block. Going through `Digest` per pair buffers, pads and finalizes generically for each of millions of pairs per state root. A new `batch` module hashes a whole layer with `sha2::compress256` directly (initial state, the pair, the fixed padding block, read the state out), so the hardware backend sha2 selects at run time is still the one used, and the output is byte-identical to `hash_nodes(&Sha2Hasher, ..)`. `fold_roots` folds each level in place through it, and leaf roots (packed chunks and composite element roots) use a local `merkleize` built on the same primitive. The rayon fan-out above PARALLEL_HASH_HEIGHT is unchanged. Element roots of composite values still come from libssz. Adds the `compress` feature of the `sha2` already in the lockfile to ssz-tree (compress256 is gated behind it); no new crate. Cherry-picked from perf/batched-merkle-layer-hashing at 70bf71b.
Block import benchmark (offline replay of mainnet blocks, steady state)
No measurable change:
Method
|
🗒️ Description / Motivation
Every inner node of the beacon state's merkle tree is SHA-256 of exactly 64 bytes: one data block plus one constant padding block. Going through
Digestper pair buffers, pads and finalizes generically for each of the millions of pairs per state root. This hashes whole layers throughsha2::compress256instead.What Changed
crates/common/ssz-tree/src/batch.rs(new): hashes a whole layer withsha2::compress256directly (initial state, the pair, the fixed padding block, read the state out). sha2 still selects its hardware backend at run time, so the same SHA extensions are used.crates/common/ssz-tree/src/tree.rs:fold_rootsfolds each level in place through it; leaf roots (packed chunks and composite element roots) use a localmerkleizebuilt on the same primitive. The rayon fan-out abovePARALLEL_HASH_HEIGHTis unchanged. Element roots of composite values still come from libssz.crates/common/ssz-tree/Cargo.toml: enables thecompressfeature of thesha2already in the lockfile (compress256is gated behind it). No new crate.Correctness / Behavior Guarantees
hash_nodes(&Sha2Hasher, ..); no root changes, so no consensus or storage change.Tests Added / Run
batch.rs: batched pairs and odd/even layers againsthash_nodes, and a proptest ofmerkleizeagainst libssz's.batch_bench(ignored, run withcargo test -p ethlambda-ssz-tree --profile release-fast -- --ignored --nocapture batch_bench) times old against new on a 2^20 layer fold, a freshList<u64>root and a freshList<Validator>root.cargo fmt --all -- --check: cleanmake lint: cleancargo test --profile release-fast -p ethlambda-ssz-tree -p ethlambda-types -p ethlambda-state-transition --lib --bins: ssz-tree 75 passed (1 ignored:batch_bench), state-transition 630 passed (15 ignored: BLS/KZG fixture vectors), types 232 passedmake test(spec suites); CI covers them.Related Issues / PRs
Part of a beacon import perf batch, each PR independent and based on
main: #752 (validation contention metrics). No file overlap with the other batch PRs.✅ Verification Checklist
make fmt— cleanmake lint(clippy with-D warnings) — cleanmake test(test-consensusplustest-node, atrelease-fast) — not run locally (spec suites); unit tests of the touched crates pass