feat(core): implement validatorapi proposal + submit_proposal + submit_blinded_proposal handlers#461
Open
varex83agent wants to merge 1 commit into
Open
Conversation
…t_blinded_proposal handlers
Ports the three proposal endpoints from Charon's
core/validatorapi/validatorapi.go: Proposal (lines 388-450),
SubmitProposal (lines 551-605) and SubmitBlindedProposal (lines
606-673), plus the propDataMatchesDuty helper (lines 451-549) and
getProposerPubkey (line 1334).
- crates/core/src/validatorapi/types.rs: replaces the three proposal
placeholders with re-exports of the concrete signeddata /
eth2api::versioned wrappers so the Handler trait now carries real
payloads (no Handler signature change — the trait method types just
point at populated structs instead of empty placeholders).
- crates/core/src/validatorapi/component.rs:
- Adds ProposerPubkeyFn / register_proposer_pubkey. Pluto's
duty_def_fn is intentionally type-erased (Box<dyn Any>) so we add
a thin typed hook for the proposer-pubkey lookup, mirroring the
existing pub_key_by_att_fn shape. This is the Rust equivalent of
Go's getProposerPubkey at validatorapi.go:1334.
- proposal: resolves proposer pubkey, derives epoch from slot via
pluto_eth2util::helpers::epoch_from_slot (Go's
eth2util.EpochFromSlot), builds a SignedRandao::new_partial wrapper,
verifies the partial randao signature, fans the parsig set out to
subscribers, and finally blocks (PROPOSAL_TIMEOUT = 24s, ~2 slots,
same sizing as ATTESTATION_DATA_TIMEOUT) on the await_proposal_fn
hook (with a dutydb.await_proposal fallback for tests). Always
returns finalized=false, execution_optimistic=false,
dependent_root=None — Go writes wrapResponse(proposal) which has
no metadata.
- submit_proposal / submit_blinded_proposal: pull the consensus-side
unsigned proposal for the slot, cross-check version, blinded flag,
proposer index, and SSZ tree-hash root against the VC submission
(proposal_matches_duty mirrors propDataMatchesDuty's per-fork
branches), then build a partial VersionedSignedProposal via
signeddata::VersionedSignedProposal::new_partial (or
new_partial_from_blinded_proposal for the blinded path), verify
the partial signature against this node's public share, and fan
out a single-entry ParSignedDataSet to subscribers.
- Adds a small fork-aware helper bundle on the eth2api
SignedProposalBlock / SignedBlindedProposalBlock enums so the
matches-duty check can reach proposer_index, slot, version, and the
per-variant SSZ root for both signed and blinded payloads. These
helpers are private to component.rs and follow Go's structure 1:1.
Tests: 12 new tests cover proposal (happy path with subscriber fanout,
builder-mode blinded branch, missing-hook 503, never-arrives 408),
submit_proposal (happy path, version mismatch, proposer-index
mismatch, blinded mismatch, unknown pubshare rejection, dutydb
fallback), and submit_blinded_proposal (happy path, proposer-index
mismatch).
Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
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 three proposal endpoints from
core/validatorapi/validatorapi.goin Charon v1.7.1:validatorapi.go:388-450. Resolves the proposerpubkey via a new
proposer_pubkey_fnhook, derives the epoch fromthe slot through the upstream beacon node, builds a partial
SignedRandaowrapper, runsverify_partial_sigagainst thisnode's pubshare, fans out the randao parsig to subscribers, then
blocks (24s ~ 2 slots) on
await_proposal_fnfor the consensusproposal landing in the dutydb. The return envelope matches Go's
wrapResponse(proposal)— no metadata.validatorapi.go:551-605. Pulls theconsensus-side unsigned proposal, validates it against the VC
submission via a new
proposal_matches_dutyhelper (the Rust portof
propDataMatchesDuty), constructs a partialsigneddata::VersionedSignedProposal::new_partial, verifies thepartial proposer signature, and fans the parsig set out to
subscribers.
validatorapi.go:606-673. Same fan-outshape; uses the existing
from_blinded_proposal/new_partial_from_blinded_proposalhelpers insigneddata::VersionedSignedProposalto bridge the blinded payloadthrough the same
proposal_matches_dutycheck.validatorapi.go:1334. Pluto's existingduty_def_fnis intentionally type-erased (Box<dyn Any>); this PRadds a typed
register_proposer_pubkeyhook that mirrors theexisting
register_pub_key_by_attestationshape and lets theproposal handlers fetch the proposer pubkey without downcasting.
The
VersionedProposal/VersionedSignedProposal/VersionedSignedBlindedProposalplaceholder types invalidatorapi/types.rsare nowpub usere-exports of the populatedsigneddata::*/eth2api::versioned::*wrappers, so theHandlertrait carries real payloads. The trait method signatures are
unchanged.
Go reference
core/validatorapi/validatorapi.go)validatorapi/component.rs)ProposalproposalpropDataMatchesDutyproposal_matches_duty+ per-fork helpersSubmitProposalsubmit_proposalSubmitBlindedProposalsubmit_blinded_proposalgetProposerPubkeyComponent::lookup_proposer_pubkey(hook)verifyPartialSigComponent::verify_partial_sig(PR-1)Test plan
cargo +nightly fmt --all --checkcargo clippy -p pluto-core --all-targets --all-features -- -D warningscargo test -p pluto-core --all-features— 386/386 passing (12 new tests)New tests:
proposal_returns_proposal_from_hook_and_fans_out_randao— happy pathproposal_returns_blinded_proposal_in_builder_mode— builder-mode branchproposal_rejects_when_proposer_pubkey_hook_missing— 503 on missing hookproposal_times_out_when_consensus_proposal_never_arrives— 408 onPROPOSAL_TIMEOUTsubmit_proposal_fans_out_partial_signed_to_subscribers— happy pathsubmit_proposal_rejects_version_mismatch— 400 on version diffsubmit_proposal_rejects_proposer_index_mismatch— 400 on index diffsubmit_proposal_rejects_blinded_mismatch— 400 on blinded flag diffsubmit_proposal_rejects_when_verification_fails— 500 on unknown pubsharesubmit_proposal_uses_dutydb_fallback_when_hook_missing— dutydb pathsubmit_blinded_proposal_fans_out_partial_signed_to_subscribers— happysubmit_blinded_proposal_rejects_proposer_index_mismatch— 400 on index