feat(core): implement validatorapi sync committee handlers#490
Open
varex83agent wants to merge 2 commits into
Open
feat(core): implement validatorapi sync committee handlers#490varex83agent wants to merge 2 commits into
varex83agent wants to merge 2 commits into
Conversation
Wire the four sync-committee validator-API endpoints end to end
(component logic + axum router handlers), porting Charon v1.7.1
core/validatorapi.
- submit_sync_committee_messages: POST /eth/v1/beacon/pool/sync_committees
- sync_committee_contribution: GET /eth/v1/validator/sync_committee_contribution
- submit_sync_committee_contributions: POST /eth/v1/validator/contribution_and_proofs
- sync_committee_selections: POST /eth/v1/validator/sync_committee_selections
Submit handlers resolve the validator index against the active-validators
cache ("validator not found" on a miss), verify partial signatures against
this node's share (sync-committee message / contribution-and-proof / sync
selection domains), and broadcast SyncMessage / SyncContribution /
PrepareSyncContribution duties to subscribers grouped by slot. The
contribution submitter additionally verifies the inner selection proof
against the aggregator's full validator pubkey, mirroring Go's
VerifyEth2SignedData. Selections then await the aggregated proof per
(duty, pubkey) from the aggsigdb. All four endpoints are JSON-only, matching
Charon's declared encodings.
Replaces the placeholder validatorapi sync types with the real altair / v1
spec types and extends the router TestHandler to record sync submissions.
Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
Address self-review findings on the sync-committee port: - parse_json_array now rejects a non-UTF8 Content-Type with 415 (surfacing the raw header), matching the sibling request_is_ssz helper instead of silently defaulting to JSON. - Drop the now-stale #[allow(dead_code)] on verify_partial_sig (it is consumed by the sync submit handlers in this PR) and refresh its doc. - Add component tests: two aggregators grouped into one slot for contributions; subscriber failure surfaces 500 and aborts the fan-out; empty input arrays broadcast nothing and return an empty selections set; selections collects multiple (duty, pubkey) entries from the aggsigdb. Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
Collaborator
Author
/loop-review-pr summaryRan 1 review-and-fix iteration against this PR (four parallel reviewers: functional-equivalence, security, rust-style, code-quality). Terminated by: completion_promise — no Quality gates (final)
Resolved during the loopBugs (0) — none. Major (0) — none. Minor (3)
Nits (3)
OutstandingNone blocking. Deferred items above are documented design/parity choices, not defects. VerdictPR is ideal — zero bug/major findings, gates green. Confirmed functional equivalence with Charon v1.7.1 across all four sync-committee endpoints (BLS domains, message roots, epoch derivation, duty grouping, inner-vs-outer signature verification, error strings, JSON-only encoding, response envelopes). 🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wires the four sync-committee validator-API endpoints end to end — component logic + axum router handlers — porting Charon v1.7.1
core/validatorapi. Nothing lands as dead code: each endpoint is reachable from the router and exercised by tests.This is PR 3 of the validatorapi port (sync committee domain). It stacks on PR 1 (#488, proxy + proposal/validators wiring) and builds on the shared surface PR 1 introduced (
new_router(handler, builder_enabled, upstream_base_url),AppState,TestHandlersetters).Scope
POST /eth/v1/beacon/pool/sync_committeessubmit_sync_committee_messagesGET /eth/v1/validator/sync_committee_contributionsync_committee_contributionPOST /eth/v1/validator/contribution_and_proofssubmit_sync_committee_contributionsPOST /eth/v1/validator/sync_committee_selectionssync_committee_selectionsThe previously
unimplemented!()Componentmethods andtodo!()router handlers are now implemented. The placeholder validatorapi sync types are replaced with the realaltair/v1spec types.Behavior (Go parity, v1.7.1)
component.go:958): resolve eachvalidator_indexagainst the active-validators cache ("validator not found"on miss), build a partialSignedSyncMessage, verify against this node's share (domainDOMAIN_SYNC_COMMITTEE, message root =beacon_block_root), group by slot, broadcastSyncMessageduties.component.go:1009): verify the inner selection proof against the aggregator's full validator pubkey (domainDOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, root =SyncAggregatorSelectionData), mirroring Go'sVerifyEth2SignedData; then verify the outer partial signature against this node's share (domainDOMAIN_CONTRIBUTION_AND_PROOF, root =Message.HashTreeRoot); group by slot, broadcastSyncContributionduties.component.go:1072): verify each partialSyncCommitteeSelectionagainst the share (domainDOMAIN_SYNC_COMMITTEE_SELECTION_PROOF), broadcastPrepareSyncContributionduties, then await the aggregated selection per(duty, pubkey)from the aggsigdb ("invalid sync committee selection"if the stored type is wrong) and return them.component.go:948): forward(slot, subcommittee_index, beacon_block_root)to the registeredawait_sync_contributionhook and wrap the result.insecure_teston both the share and full-pubkey paths, matching Go'sc.insecureTestguard.Encodings: [JSON]. Responses use the{ "data": ... }envelope (wrapResponse).Go references (v1.7.1)
core/validatorapi/validatorapi.go:SubmitSyncCommitteeMessages,SyncCommitteeContribution,SubmitSyncCommitteeContributions,SyncCommitteeSelections,verifyPartialSig.core/eth2signeddata.go:VerifyEth2SignedData+ theSignedSyncMessage/SignedSyncContributionAndProof/SyncCommitteeSelectiondomain & epoch impls.core/signeddata.go: theNewPartialSignedSync*constructors.core/validatorapi/router.go:submitSyncCommitteeMessages,syncCommitteeContribution,submitContributionAndProofs,syncCommitteeSelections.Test coverage
validator not foundrejection; selection broadcast + aggsigdb collection; wrong-aggregated-type rejection; unregistered-hook503; contribution hook forwarding.dataenvelopes for contribution (GET query parse) and selections;415on non-JSON content type;400on empty body;400on missing query parameter.Gates (run from worktree)
cargo +nightly fmt --all --check— cleancargo clippy --workspace --all-targets --all-features -- -D warnings— cleancargo test --workspace— pass (--all-featurestest is Docker-gated in this sandbox via the eth2apiintegrationfeature; confirmed it compiles and is clippy-clean)cargo deny check— ok🤖 Generated with Claude Code