Repository navigation
perf(p2p): verify one KZG batch per block for fetched data columns - #754
MegaRedHand wants to merge 2 commits into
Conversation
A range or by-root answer, or a batch of parked columns replayed after their parent imports, carries many columns of the same block. Each column was checked on its own blocking task with its own verify_cell_kzg_proof_batch call, and the chain actor heard nothing until every sidecar of the whole call was done, so data availability waited on the slowest block of the batch. check_and_forward now groups the sidecars by block (the signed header's root for fulu, beacon_block_root for gloas). A group takes one column_check_permits permit, runs every sidecar's rules that do not read the cells, then verifies the proofs of all sidecars still standing in one batch. A batch that fails is re-run sidecar by sidecar, so exactly the sidecars rejected before are rejected. The group's Keep and AwaitParent sidecars go to the chain actor as soon as the group finishes. Verdicts do not change: the proofs are the last rule on the chain path (the lookahead rule after them is the gossip path's alone), so chain_checks is now chain_precheck followed by a KZG step, with gossip's stateful_checks and stateful_checks_gloas keeping their order through the same split halves. On this machine a batch over 9-blob columns costs 3.3x less than separate calls at 8 columns and 4.8x less at 32.
Every fulu sidecar of a block carries the same signed header, so the group added in the previous commit repeated one BLS verification per sidecar, now one after another under a single permit. SignatureMemo remembers header_signature_is_valid's answer for the group, keyed on the header root, the signature bytes and (for the head-state check made before parking) the head it read, so a forged signature over a shared header is judged on its own. Only that answer is remembered; every other rule runs per sidecar in its order, so verdicts are unchanged. The memo lives in one group's spawn_blocking closure; gossip and the single-sidecar wrappers use a fresh one per call.
🤖 Claude Code ReviewReview: PR 754, one KZG batch per block for fetched data columnsI read the diff only. I did not build it or run the tests. Overall this looks good, and I found nothing blocking. Correctness
Things to consider (non-blocking)
Tests and docs
Verdict: approve. Item 1 is the only point I would act on, and it is just a comment tweak. Automated review by Claude (Anthropic) · sonnet · custom prompt |
🤖 Codex Code ReviewI found one substantive issue; otherwise the batching approach looks sound and preserves per-sidecar verdicts.
Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
🤖 Kimi Code ReviewI'll review this PR which introduces KZG batch verification for data column sidecars grouped by block. Let me analyze the changes systematically. Overall AssessmentThis is a well-structured optimization that batches KZG proof verification across multiple data column sidecars of the same block, reducing cryptographic overhead. The design is sound but I have several concerns ranging from correctness edge cases to API safety. Critical Issues1.
|
| Priority | Count | Categories |
|---|---|---|
| Critical | 3 | Signature hashing correctness, usize overflow, unwind safety |
| Security | 1 | Unbounded memo memory |
| Correctness | 3 | Lifetime fragility, HashMap ordering, length validation |
| Performance | 2 | Unnecessary allocations, eager conversion |
| Style | 5 | Various minor improvements |
The PR is well-designed and the batching optimization is valuable. The critical issues should be addressed before merge, particularly the BlsSignature hashing correctness and AssertUnwindSafe soundness claims.
Automated review by Kimi (Moonshot AI) · kimi-k2.6 · custom prompt
) A range or by-root answer, or a batch of parked columns replayed after their parent imports, carries many columns of the same block. Each column was checked on its own blocking task with its own verify_cell_kzg_proof_batch call, and the chain actor heard nothing until every sidecar of the whole call was done, so data availability waited on the slowest block of the batch. check_and_forward now groups the sidecars by block (the signed header's root for fulu, beacon_block_root for gloas). A group takes one column_check_permits permit, runs every sidecar's rules that do not read the cells, then verifies the proofs of all sidecars still standing in one batch. A batch that fails is re-run sidecar by sidecar, so exactly the sidecars rejected before are rejected. The group's Keep and AwaitParent sidecars go to the chain actor as soon as the group finishes. Verdicts do not change: the proofs are the last rule on the chain path (the lookahead rule after them is the gossip path's alone), so chain_checks is now chain_precheck followed by a KZG step, with gossip's stateful_checks and stateful_checks_gloas keeping their order through the same split halves. On this machine a batch over 9-blob columns costs 3.3x less than separate calls at 8 columns and 4.8x less at 32. The branch's second commit verifies a block group's shared header signature once: every fulu sidecar of a block carries the same signed header, so the group repeated one BLS verification per sidecar, one after another under a single permit. SignatureMemo remembers header_signature_is_valid's answer for the group, keyed on the header root, the signature bytes and (for the head-state check made before parking) the head it read, so a forged signature over a shared header is judged on its own. Only that answer is remembered; every other rule runs per sidecar in its order, so verdicts are unchanged. The memo lives in one group's spawn_blocking closure; gossip and the single-sidecar wrappers use a fresh one per call. Adapted onto nightly's #752: its column_check in-use gauge and blocking-task timer now wrap one block group's closure instead of one sidecar's, and docs/metrics.md says a column_check permit and task cover a block's group. Cherry-picked from perf/column-kzg-batch-per-block at abe310a.
🗒️ Description / Motivation
A range or by-root answer, or a batch of parked columns replayed after their parent imports, carries many columns of the same block. Each column was checked on its own blocking task with its own
verify_cell_kzg_proof_batchcall, and the chain actor heard nothing until every sidecar of the whole call was done, so data availability waited on the slowest block of the batch. And every fulu sidecar of a block carries the same signed header, so its BLS signature was verified once per sidecar.What Changed
Two commits:
p2p/src/beacon/column_checks.rs,state_transition/src/beacon/gossip/column.rs, andfork_choice::verify_column_cells_batch, oneverify_cell_kzg_proof_batchcall over every cell of several columns):check_and_forwardgroups the sidecars by block (the signed header's root for fulu,beacon_block_rootfor gloas). A group takes onecolumn_check_permitspermit, runs every sidecar's rules that do not read the cells, then verifies the proofs of all sidecars still standing in one batch. The group'sKeepandAwaitParentsidecars go to the chain actor as soon as the group finishes.chain_checksis nowchain_precheckfollowed by a KZG step, and gossip'sstateful_checks/stateful_checks_gloaskeep their order through the same split halves.SignatureMemoremembersheader_signature_is_valid's answer for the group, keyed on the header root, the signature bytes and (for the head-state check made before parking) the head it read, so a forged signature over a shared header is judged on its own. The memo lives in one group'sspawn_blockingclosure; gossip and the single-sidecar wrappers use a fresh one per call.Correctness / Behavior Guarantees
Tests Added / Run
cargo fmt --all -- --check: cleanmake lint: cleancargo test --profile release-fast -p ethlambda-state-transition -p ethlambda-p2p --lib --bins: state-transition 634 passed (16 ignored), p2p 364 passed (1 ignored)make 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, also editscolumn_checks.rsanddocs/metrics.md), #753 (merkle layer hashing). The engine/STF overlap PR also editsfork_choice.rs, in a different region.✅ 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