feat(core): wire validatorapi proxy + proposal/validators router handlers#488
Open
varex83agent wants to merge 2 commits into
Open
feat(core): wire validatorapi proxy + proposal/validators router handlers#488varex83agent wants to merge 2 commits into
varex83agent wants to merge 2 commits into
Conversation
…lers Implement the axum router handlers that were todo!() for the already-merged component logic, plus the two infra pieces, so the validator API surface is reachable end to end: - proxy_handler fallback: reverse-proxy to the beacon node with basic-auth, Host rewrite, hop-by-hop header stripping, and a proxy-latency metric. - submit_proposal_preparations: no-op swallow (200). - propose_block_v3: versioned block response with the consensus version / payload-value headers; builder_enabled maxes the boost. - submit_proposal / submit_blinded_block: per-fork (de)serialization keyed by the Eth-Consensus-Version header (JSON or SSZ body). - get_validators / get_validator: id batch dispatched on the first element's 0x prefix, matching Charon's getValidatorsByID. new_router gains an upstream_base_url argument and AppState carries a reqwest client for the proxy. Adds public per-fork SSZ block-body decoders in ssz_codec and extends the TestHandler stubs. Go reference: charon core/validatorapi/router.go (v1.7.1). Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
- proxy: stream the upstream response body instead of buffering, so the long-lived SSE /eth/v1/events stream proxies incrementally (reqwest bytes_stream + axum Body::from_stream; enable reqwest "stream"). - proxy: strip the client Authorization header when the upstream URL carries credentials, avoiding a duplicate/conflicting Authorization. - propose_block_v3: always send builder_boost_factor (0 when builder mode is off, u64::MAX when on), matching Charon. - request_is_ssz: reject a non-ASCII Content-Type with 415 instead of silently treating it as JSON. - ssz_codec: drop the dead `blinded` parameter from decode_signed_proposal_block_body (full-block decoder only). - move the use blocks above the const declarations in router.rs. - add an SSZ-body submit test; drop Go line-number anchors from doc comments. 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 review agents: functional-parity, security, rust-style, code-quality). Terminated by: completion_promise (no remaining Quality gates (final)
Resolved during the loopMajor (3)
Minor (4)
Nits (1)
Outstanding (deferred, all minor/nit with reasons)
VerdictPR is ideal — all |
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
PR 1 of the validatorapi port — "make the current surface usable". The proposal,
blinded-proposal, submit, and validators component logic already landed on the base
branch (
bohdan/validatorapi-validators); this PR wires the axum router handlers that weretodo!()and adds the two missing infra pieces, so the already-merged logic is reachable endto end.
Go reference: Charon v1.7.1,
core/validatorapi/router.go+eth2types.go.Scope
Handlers implemented in
crates/core/src/validatorapi/router.rs:proxy_handler(.fallback) — reverse-proxy to the beacon node, basic-auth + Host rewrite + proxy-latency metricproxyHandlerPOST /eth/v1/validator/prepare_beacon_proposer— no-op swallow (200)submitProposalPreparationsGET /eth/v3/validator/blocks/{slot}— versioned block +Eth-Consensus-Version/payload-value headers;builder_enabledmaxes the boost factorproposeBlockV3+createProposeBlockResponsePOST /eth/v{1,2}/beacon/blockssubmitProposalPOST /eth/v{1,2}/beacon/blinded_blockssubmitBlindedBlockGET,POST /eth/v1/beacon/states/{state_id}/validators[/{validator_id}]getValidators/getValidatorNo
todo!()remains for PR-1-scoped handlers. The PR-2/3/4 endpoints keep their stubs.Parity notes
Content-Type) keyed by theEth-Consensus-Versionheader, mirroring Charon'ssubmitProposal/submitBlindedBlockversion switch. Header match is case-insensitive(go-eth2-client
DataVersion.UnmarshalJSON). Unknown content type →415; unknown/missingversion →
400.datais the bare block for pre-Deneb / blinded forks and theBlockContentsobject ({block, kzg_proofs, blobs}) for Deneb/Electra/Fulu full blocks,matching go-eth2-client's
apiv1<fork>.BlockContents. The fourEth-*headers are set as inGo.
ids[0]'s0xprefix exactly as GogetValidatorsByID(router.go:1930) — all-pubkeys or all-indices, no per-id bucketing.Empty result serializes to
[]notnull;get_validatorreturns404on none and500on more-than-one.
copy(graffiti[:], graffitiBytes);randao_revealis strict 96 bytes.Host, and dropshop-by-hop +
Content-Lengthheaders in both directions. Charon cancels in-flight proxiedrequests via the lifecycle context; here the proxied request inherits the axum request's
lifetime (no cluster-wide
CancellationTokenis plumbed intonew_routeryet — out of PR1scope).
Surface changes for downstream PRs (2/3/4)
new_router(handler, builder_enabled, upstream_base_url: reqwest::Url)— added the upstreamURL;
AppStatenow carriesupstream_base_url+ areqwest::Client. Addedreqwest+urlto
crates/core/Cargo.toml.crates/core/src/ssz_codec.rsgained publicdecode_signed_proposal_block_body/decode_signed_blinded_proposal_block_body(bare beacon-API SSZ block bodies — distinct fromthe Charon-header
decode_versioned_signed_proposal).testutils.rsTestHandlergainedwith_proposal/with_validatorssetters andArc<Mutex<…>>recording fields for submit endpoints. Router tests gainedtest_state/test_routerhelpers.Tests
Router-level
tower::ServiceExt::oneshottests covering happy path, content-type / versionnegotiation, and error shapes for each new endpoint: prepare-proposer swallow, propose_block_v3
(+ headers, builder boost, graffiti padding, missing randao), submit_proposal (JSON, capitalised
version, missing/invalid version, unsupported content type, bad body), submit_blinded_block
(happy + phase0-rejected), get_validators (query id / pubkey dispatch / JSON body ids / empty →
[]), get_validator (single / 404 / 500), and the reverse proxy (forward + basic-auth via awiremock upstream, error-status propagation).
Quality gates
cargo +nightly fmt --all --check,cargo clippy --workspace --all-targets --all-features -D warnings, andcargo deny checkpass. Workspace tests pass; theeth2apiintegrationfeature tests (Docker/Lighthouse via testcontainers) are not exercised here and are untouched by
this change.
🤖 Generated with Claude Code