Skip to content

feat(core): port core/eth2signeddata#501

Open
varex83agent wants to merge 1 commit into
mainfrom
bohdan/port-eth2signeddata
Open

feat(core): port core/eth2signeddata#501
varex83agent wants to merge 1 commit into
mainfrom
bohdan/port-eth2signeddata

Conversation

@varex83agent

Copy link
Copy Markdown
Collaborator

Summary

Ports core/eth2signeddata from Charon (charon/core/eth2signeddata.go, v1.7.1), closing #438.

The signed-data types already existed in crates/core/src/signeddata.rs (the SignedData trait: signature/set_signature/message_root). What was missing is the eth2 verification layer — the per-type signing domain and signing epoch, and the function that ties them to upstream domain resolution + BLS verification. The core::types TODO and the sigagg::new_verifier no-op both pointed at this gap.

What's added

New module crates/core/src/eth2signeddata.rs:

  • trait Eth2SignedData: SignedData — adds domain_name() and async epoch(client).
  • verify_eth2_signed_data(client, data, pubkey) — mirrors Go's VerifyEth2SignedData: resolve epoch → message_root()signing::verify(domain, epoch, root, sig, pubkey).
  • Eth2SignedData impls for all 12 payloads, matching the Go domain/epoch mapping exactly (proposer, attester, voluntary-exit, application-builder@epoch-0, randao, selection-proof, aggregate-and-proof, sync-committee, contribution-and-proof, sync-committee-selection-proof — versioned and non-versioned variants).
  • as_eth2_signed_data(&dyn SignedData) -> Option<&dyn Eth2SignedData> — the Rust equivalent of Go's data.(core.Eth2SignedData) type assertion.

Supporting changes:

  • sigagg::new_verifier now takes Arc<EthBeaconNodeApiClient> and verifies via verify_eth2_signed_data (was a no-op placeholder; no production callers yet). New SigAggError variants mirror Go's NewVerifier error paths (invalid eth2 signed data, pubkey from core, aggregate signature verification failed).
  • versioned::SignedProposalBlock::slot() helper (mirrors the existing graffiti()/signature()), needed for the proposal's signing epoch.

Tests

TestVerifyEth2SignedData is translated directly: for each of the 10 payload types the Go table exercises, load the existing golden JSON fixture, resolve epoch + message root against a BeaconMock, BLS-sign the signing-domain data root, inject the signature, and assert verification succeeds. Plus wrong-pubkey / zero-signature rejection, the as_eth2_signed_data view, and a sigagg test that new_verifier rejects non-eth2 data.

Gates

  • cargo +nightly fmt --all --check
  • cargo clippy -p pluto-core -p pluto-eth2api --all-targets --all-features -- -D warnings
  • cargo test -p pluto-core (559) / cargo test -p pluto-eth2api (114) ✅

Closes #438

Add the Eth2SignedData layer over the existing signed-data types: the
signing DomainName and signing Epoch for each beacon-chain signed payload,
plus verify_eth2_signed_data which resolves the upstream domain and runs BLS
verification. Wires sigagg's new_verifier to it, replacing the no-op
placeholder.

Ports charon/core/eth2signeddata.go (v1.7.1).

Co-Authored-By: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.com>
Comment on lines +466 to +477
fn as_eth2_signed_data_views_typed_payloads() {
let randao: SignedRandao = load("TestJSONSerialisation_SignedRandao.json.golden");

// A typed payload is viewable as Eth2SignedData...
let boxed: Box<dyn SignedData> = Box::new(randao);
assert!(as_eth2_signed_data(boxed.as_ref()).is_some());

// ...while a raw signature is not.
let sig: Box<dyn SignedData> = Box::new([0u8; SIGNATURE_LENGTH] as Signature);
assert!(as_eth2_signed_data(sig.as_ref()).is_none());
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better have test for all types

Comment thread crates/core/src/sigagg.rs
let owned: Box<dyn SignedData> = dyn_clone::clone_box(data);

Box::pin(async move {
let tbls_pubkey = tbls_pubkey.map_err(|_| SigAggError::PubkeyFromCore)?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't discard the inner error

Comment thread crates/core/src/sigagg.rs
#[error("aggregate signature verification failed: {source}")]
VerificationFailed {
/// The underlying error.
#[source]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[from] is better?

data: &dyn Eth2SignedData,
pubkey: &PublicKey,
) -> Result<(), Eth2SignedDataError> {
let epoch = data.epoch(client).await?;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this check is more expensive than

let sig_root = data.message_root()?;
let signature = data.signature()?;

Do it later

Comment thread crates/core/src/lib.rs

/// Eth2 signed-data verification.
pub mod eth2signeddata;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale // todo: add Eth2SignedData type comment in crates/core/src/types.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement core/eth2signeddata

2 participants