feat(core): implement validatorapi attestation + aggregation handlers#492
Open
varex83agent wants to merge 2 commits into
Open
Conversation
Port the attestation/aggregation domain of the validator API from Charon v1.7.1 (core/validatorapi). Fills the four previously-unimplemented Component methods and wires their axum router handlers: - POST /eth/v2/beacon/pool/attestations -> submit_attestations - GET /eth/v2/validator/aggregate_attestation -> aggregate_attestation - POST /eth/v2/validator/aggregate_and_proofs -> submit_aggregate_attestations - POST /eth/v1/validator/beacon_committee_selections -> beacon_committee_selections The v1 attestation/aggregate routes stay 404 (Go parity). Component: - submit_attestations: resolves the validator index (explicit for Electra/Fulu, matched from the attester-duty set + single aggregation bit for pre-Electra), looks up the DV root pubkey via pub_key_by_att, verifies the partial attester signature (DOMAIN_BEACON_ATTESTER, epoch from the attestation target), and broadcasts under an attester duty. - aggregate_attestation: blocking await via the agg-attestation hook. - submit_aggregate_attestations: looks up the aggregator pubkey from the validator cache, verifies the inner selection proof (skipped under insecure_test) and the outer partial sig (DOMAIN_AGGREGATE_AND_PROOF), broadcasts under an aggregator duty. - beacon_committee_selections: verifies each slot signature (DOMAIN_SELECTION_PROOF), broadcasts under a prepare-aggregator duty, then awaits the aggregated selections from the AggSigDB. Router decodes versioned JSON/SSZ arrays per Eth-Consensus-Version, lifts Electra/Fulu SingleAttestation into the versioned wrapper, and returns the versioned aggregate / selection response shapes. Shared surface: adds electra::SingleAttestation, derives Serialize/ Deserialize on signeddata::AttesterDuty, and replaces the three attestation placeholder types in validatorapi::types with signeddata aliases. Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
- submit_attestations: match Go's no-duty-match behaviour — leave the validator index at 0 and let the pubkey lookup fail, instead of returning an early error; move the single-aggregation-bit check inside the committee-matching loop, mirroring validatorapi.go. - beacon_committee_selections: resolve the AggSigDB hook up front so a misconfigured component fails before broadcasting partial selections. - router: rename the non-snake-case `AttestationPayload_phase0` helper to `phase0_attestation_payload`. - tests: add Electra/SSZ aggregate-and-proof decode, out-of-range committee-index rejection, and a multi-slot beacon-committee-selection broadcast test. 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 (4 parallel review agents: functional-parity vs Charon v1.7.1, security, rust-style, code-quality). Terminated by: completion (clean after fixes). Quality gates (final)
Resolved during the loopBugs (0) Major (2)
Minor (2)
Nits (0 changed) Outstanding (accepted — parity-equivalent with Charon v1.7.1)
VerdictPR is ideal — all bug/major findings resolved, gates green. |
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
Ports the attestation & aggregation domain of the validator API from Charon v1.7.1 (
core/validatorapi/{router.go,validatorapi.go,eth2types.go}). Fills the four previouslyunimplemented!()Componentmethods and wires their axum router handlers (previouslytodo!()).Stacks on #488 (PR 1 — proxy + proposal/validators wiring), which laid the producer-hook scaffolding (
pub_key_by_att_fn,await_agg_attestation_fn,await_agg_sig_db_fn,duty_def_fn) and theverify_partial_sighelper this PR consumes.Scope
Endpoints wired (v1 attestation/aggregate routes intentionally stay
404, matching Go):POST /eth/v2/beacon/pool/attestationssubmit_attestationsGET /eth/v2/validator/aggregate_attestationaggregate_attestationPOST /eth/v2/validator/aggregate_and_proofssubmit_aggregate_attestationsPOST /eth/v1/validator/beacon_committee_selectionsbeacon_committee_selectionsGo reference (v1.7.1,
core/validatorapi/):SubmitAttestations,AggregateAttestation,SubmitAggregateAttestations,BeaconCommitteeSelections, and the matching router handlers +createAggregateAttestation.Behaviour (Go parity)
pub_key_by_att, verifies the partial attester signature (DOMAIN_BEACON_ATTESTER, epoch from the attestation's target checkpoint), and broadcasts under an attester duty.Eth-Consensus-Versionheader and{ "version", "data" }body.insecure_test, as in Go) and the outer partial signature (DOMAIN_AGGREGATE_AND_PROOF), then broadcasts under an aggregator duty.DOMAIN_SELECTION_PROOF), broadcasts under a prepare-aggregator duty, then awaits the aggregated selections from the AggSigDB; returns{ "data": [...] }.Router decodes versioned JSON and SSZ arrays keyed off
Eth-Consensus-Version, lifts the Electra/FuluSingleAttestationwire form into the versioned wrapper (committee index → committee bitfield, attester index → validator index), and reproduces Go's response shapes.Shared surface (relevant to PR 3 / PR 4)
crates/eth2api/src/spec/electra.rs: addsSingleAttestation(SSZ + JSON, string-encoded indices).crates/core/src/signeddata.rs: derivesSerialize/DeserializeonAttesterDuty(so it can key aDutyDefinitionSet).crates/core/src/validatorapi/types.rs: replaces theVersionedAttestation/VersionedSignedAggregateAndProof/BeaconCommitteeSelectionplaceholder structs with aliases to the signeddata / eth2api wrappers.crates/core/src/validatorapi/testutils.rs: append-onlyTestHandlerrecording fields + setters for the four endpoints.Tests
router.rs): happy path + JSON/SSZ negotiation + missing-version-header + unsupported-content-type + malformed-body + v1-route-404 for each endpoint; versioned aggregate response headers/body; selections round-trip.component.rs): pre-Electra index resolution + broadcast, Electra explicit-index path, multi-aggregation-bit rejection, aggregate await + missing-hook 503, aggregate submit resolve+broadcast + unknown-validator rejection, selections broadcast + AggSigDB round-trip + unknown-validator rejection.Quality gates (all green)
cargo +nightly fmt --all --checkcargo clippy --workspace --all-targetsand--all-featurescargo test --workspace(pluto-core 426, eth2api 91, eth2util 144 — all pass)cargo build --workspace --all-featurescargo deny checkcargo test --all-featuresis Docker-gated in the sandbox (eth2apiintegrationfeature spins a Lighthouse testcontainer); the functional gate iscargo test --workspace(default features), and--all-featuresis confirmed to compile and be clippy-clean.🤖 Generated with Claude Code