Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use super::helpers::electra::{
use super::helpers::misc::compute_signing_root;
use super::helpers::predicates::is_slashable_validator;
use super::lean_boundary::lean_state_unreachable;
use super::stf::signatures::SignatureBatch;
use super::stf::{self, ExecutionEngine};
use ethlambda_storage::pools::single_committee;

Expand Down Expand Up @@ -442,15 +443,19 @@ fn pack_each<T>(
candidates: Vec<T>,
max: usize,
config: &Config,
apply: impl Fn(&mut BeaconState, &T, &Config) -> Result<()>,
apply: impl Fn(&mut BeaconState, &T, &Config, &mut SignatureBatch) -> Result<()>,
) -> Vec<T> {
let mut packed = Vec::new();
// Bounds the work if the pool is full of stale entries.
for candidate in candidates.into_iter().take(4 * max) {
if packed.len() == max {
break;
}
if apply(scratch, &candidate, config).is_ok() {
// An immediate batch, so a bad signature rejects the candidate before
// it writes, which is what lets a rejected candidate leave `scratch`
// untouched (see above); a deferred batch would only fail at the end.
let mut signatures = SignatureBatch::immediate();
if apply(scratch, &candidate, config, &mut signatures).is_ok() {
packed.push(candidate);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ pub fn assemble_gloas_block_on_bid(
body,
};
let mut post = state.clone();
stf::gloas::process_block(
stf::gloas::process_block_and_verify(
&mut post,
&block,
config,
Expand Down Expand Up @@ -529,7 +529,7 @@ pub fn assemble_gloas_block(
let BeaconState::Gloas(_) = &post else {
unreachable!("checked above")
};
stf::gloas::process_block(
stf::gloas::process_block_and_verify(
&mut post,
&block,
config,
Expand Down Expand Up @@ -745,7 +745,7 @@ pub mod test_support {
/// The state after `block` is applied to `state`, as the chain stores it.
pub fn post_state(state: &BeaconState, block: &BeaconBlock) -> BeaconState {
let mut post = state.clone();
stf::gloas::process_block(
stf::gloas::process_block_and_verify(
&mut post,
block,
&config(),
Expand Down Expand Up @@ -1042,7 +1042,7 @@ mod gloas_block_production_tests {

// Importing it with the same option lands on the state root it names.
let mut post = state.clone();
stf::gloas::process_block(
stf::gloas::process_block_and_verify(
&mut post,
&produced.block,
&config(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
//! the aggregation bits from one committee to a whole slot's worth and adding a
//! committee bitfield, so it gets its own versions rather than reusing these.

use crate::beacon::bls::SignatureSet;
use crate::beacon::constants;
use crate::beacon::containers::BeaconState;
use crate::beacon::containers::phase0::{Attestation, AttestingIndices, IndexedAttestation};
use crate::beacon::error::Result;
use crate::beacon::helpers::accessors::{CommitteeCache, CommitteeCacheExt, get_domain};
use crate::beacon::helpers::misc::{compute_epoch_at_slot, compute_signing_root};
use crate::beacon::helpers::predicates::are_indices_sorted_and_unique;
use crate::beacon::primitives::{Domain, HashTreeRoot as _, ValidatorIndex};
use crate::beacon::{bls, constants};

/// The committee members whose bit is set in `attestation`, in ascending order.
///
Expand Down Expand Up @@ -79,12 +80,27 @@ pub fn is_valid_indexed_attestation(
state: &BeaconState,
indexed_attestation: &IndexedAttestation,
) -> bool {
indexed_attestation_signature_set(state, indexed_attestation).is_some_and(|set| set.verify())
}

/// The signature check [`is_valid_indexed_attestation`] makes, resolved but
/// not yet run, so a block can verify every attestation's signature in one
/// batch.
///
/// Every structural check [`is_valid_indexed_attestation`] makes (the
/// attester set's shape and range) happens here, at once: `None` means the
/// attestation is invalid without needing a pairing. Only the pairing is left
/// to the caller.
pub fn indexed_attestation_signature_set(
state: &BeaconState,
indexed_attestation: &IndexedAttestation,
) -> Option<SignatureSet> {
let domain = get_domain(
state,
constants::DOMAIN_BEACON_ATTESTER,
Some(indexed_attestation.data.target.epoch),
);
is_valid_indexed_attestation_with_domain(state, indexed_attestation, domain)
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
}

/// [`is_valid_indexed_attestation`] under a signing domain the caller chose.
Expand All @@ -97,19 +113,30 @@ pub fn is_valid_indexed_attestation_with_domain(
indexed_attestation: &IndexedAttestation,
domain: Domain,
) -> bool {
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
.is_some_and(|set| set.verify())
}

/// [`indexed_attestation_signature_set`] under a signing domain the caller
/// chose.
pub fn indexed_attestation_signature_set_with_domain(
state: &BeaconState,
indexed_attestation: &IndexedAttestation,
domain: Domain,
) -> Option<SignatureSet> {
let indices: &[ValidatorIndex] = &indexed_attestation.attesting_indices;
if indices.is_empty() || !are_indices_sorted_and_unique(indices) {
return false;
return None;
}

let mut pubkeys = Vec::with_capacity(indices.len());
for index in indices {
match state.validator(*index) {
Ok(validator) => pubkeys.push(validator.pubkey),
Err(_) => return false,
Err(_) => return None,
}
}

let signing_root = compute_signing_root(indexed_attestation.data.hash_tree_root(), domain);
bls::fast_aggregate_verify(&pubkeys, signing_root, &indexed_attestation.signature)
SignatureSet::fast_aggregate(&pubkeys, signing_root, &indexed_attestation.signature)
}
36 changes: 31 additions & 5 deletions crates/blockchain/state_transition/src/beacon/helpers/electra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
//! single easiest mistake to make copying that shape without also copying
//! the reasoning behind it.

use crate::beacon::bls;
use crate::beacon::bls::SignatureSet;
use crate::beacon::config::Config;
use crate::beacon::constants::{self, FAR_FUTURE_EPOCH};
use crate::beacon::containers::shared::Validator;
Expand Down Expand Up @@ -280,12 +280,27 @@ pub fn is_valid_indexed_attestation(
state: &BeaconState,
indexed_attestation: &electra::IndexedAttestation,
) -> bool {
indexed_attestation_signature_set(state, indexed_attestation).is_some_and(|set| set.verify())
}

/// The signature check [`is_valid_indexed_attestation`] makes, resolved but
/// not yet run, so a block can verify every attestation's signature in one
/// batch.
///
/// Every structural check [`is_valid_indexed_attestation`] makes (the
/// attester set's shape and range) happens here, at once: `None` means the
/// attestation is invalid without needing a pairing. Only the pairing is left
/// to the caller.
pub fn indexed_attestation_signature_set(
state: &BeaconState,
indexed_attestation: &electra::IndexedAttestation,
) -> Option<SignatureSet> {
let domain = get_domain(
state,
constants::DOMAIN_BEACON_ATTESTER,
Some(indexed_attestation.data.target.epoch),
);
is_valid_indexed_attestation_with_domain(state, indexed_attestation, domain)
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
}

/// [`is_valid_indexed_attestation`] under a signing domain the caller chose.
Expand All @@ -297,21 +312,32 @@ pub fn is_valid_indexed_attestation_with_domain(
indexed_attestation: &electra::IndexedAttestation,
domain: Domain,
) -> bool {
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
.is_some_and(|set| set.verify())
}

/// [`indexed_attestation_signature_set`] under a signing domain the caller
/// chose.
pub fn indexed_attestation_signature_set_with_domain(
state: &BeaconState,
indexed_attestation: &electra::IndexedAttestation,
domain: Domain,
) -> Option<SignatureSet> {
let indices: &[ValidatorIndex] = &indexed_attestation.attesting_indices;
if indices.is_empty() || !are_indices_sorted_and_unique(indices) {
return false;
return None;
}

let mut pubkeys = Vec::with_capacity(indices.len());
for index in indices {
match state.validator(*index) {
Ok(validator) => pubkeys.push(validator.pubkey),
Err(_) => return false,
Err(_) => return None,
}
}

let signing_root = compute_signing_root(indexed_attestation.data.hash_tree_root(), domain);
bls::fast_aggregate_verify(&pubkeys, signing_root, &indexed_attestation.signature)
SignatureSet::fast_aggregate(&pubkeys, signing_root, &indexed_attestation.signature)
}

// ---------------------------------------------------------------------------
Expand Down
53 changes: 45 additions & 8 deletions crates/blockchain/state_transition/src/beacon/helpers/gloas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
//! doc), read through [`super::fulu::get_beacon_proposer_index`] rather
//! than a copy of it here.

use crate::beacon::bls;
use crate::beacon::bls::SignatureSet;
use crate::beacon::config::Config;
use crate::beacon::constants;
use crate::beacon::containers::shared::AttestationData;
Expand Down Expand Up @@ -124,12 +124,27 @@ pub fn is_valid_indexed_attestation(
state: &BeaconState,
indexed_attestation: &gloas::IndexedAttestation,
) -> bool {
indexed_attestation_signature_set(state, indexed_attestation).is_some_and(|set| set.verify())
}

/// The signature check [`is_valid_indexed_attestation`] makes, resolved but
/// not yet run, so a block can verify every attestation's signature in one
/// batch.
///
/// Every structural check [`is_valid_indexed_attestation`] makes (the
/// attester set's shape and range) happens here, at once: `None` means the
/// attestation is invalid without needing a pairing. Only the pairing is left
/// to the caller.
pub fn indexed_attestation_signature_set(
state: &BeaconState,
indexed_attestation: &gloas::IndexedAttestation,
) -> Option<SignatureSet> {
let domain = get_domain(
state,
constants::DOMAIN_BEACON_ATTESTER,
Some(indexed_attestation.data.target.epoch),
);
is_valid_indexed_attestation_with_domain(state, indexed_attestation, domain)
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
}

/// [`is_valid_indexed_attestation`] under a signing domain the caller chose.
Expand All @@ -141,24 +156,35 @@ pub fn is_valid_indexed_attestation_with_domain(
indexed_attestation: &gloas::IndexedAttestation,
domain: Domain,
) -> bool {
indexed_attestation_signature_set_with_domain(state, indexed_attestation, domain)
.is_some_and(|set| set.verify())
}

/// [`indexed_attestation_signature_set`] under a signing domain the caller
/// chose.
pub fn indexed_attestation_signature_set_with_domain(
state: &BeaconState,
indexed_attestation: &gloas::IndexedAttestation,
domain: Domain,
) -> Option<SignatureSet> {
let indices: &[ValidatorIndex] = &indexed_attestation.attesting_indices;
if indices.is_empty()
|| indices.len() > preset::MAX_VALIDATORS_PER_SLOT
|| !are_indices_sorted_and_unique(indices)
{
return false;
return None;
}

let mut pubkeys = Vec::with_capacity(indices.len());
for index in indices {
match state.validator(*index) {
Ok(validator) => pubkeys.push(validator.pubkey),
Err(_) => return false,
Err(_) => return None,
}
}

let signing_root = compute_signing_root(indexed_attestation.data.hash_tree_root(), domain);
bls::fast_aggregate_verify(&pubkeys, signing_root, &indexed_attestation.signature)
SignatureSet::fast_aggregate(&pubkeys, signing_root, &indexed_attestation.signature)
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -284,16 +310,27 @@ pub fn is_valid_indexed_payload_attestation(
state: &BeaconState,
attestation: &gloas::IndexedPayloadAttestation,
) -> bool {
indexed_payload_attestation_signature_set(state, attestation).is_some_and(|set| set.verify())
}

/// The signature check [`is_valid_indexed_payload_attestation`] makes,
/// resolved but not yet run. The structural checks (non-empty, sorted, every
/// index in range) happen here, at once; `None` means the attestation is
/// invalid without needing a pairing.
pub fn indexed_payload_attestation_signature_set(
state: &BeaconState,
attestation: &gloas::IndexedPayloadAttestation,
) -> Option<SignatureSet> {
let indices: &[ValidatorIndex] = &attestation.attesting_indices;
if indices.is_empty() || !indices.windows(2).all(|pair| pair[0] <= pair[1]) {
return false;
return None;
}

let mut pubkeys = Vec::with_capacity(indices.len());
for index in indices {
match state.validator(*index) {
Ok(validator) => pubkeys.push(validator.pubkey),
Err(_) => return false,
Err(_) => return None,
}
}

Expand All @@ -303,7 +340,7 @@ pub fn is_valid_indexed_payload_attestation(
Some(compute_epoch_at_slot(attestation.data.slot)),
);
let signing_root = compute_signing_root(attestation.data.hash_tree_root(), domain);
bls::fast_aggregate_verify(&pubkeys, signing_root, &attestation.signature)
SignatureSet::fast_aggregate(&pubkeys, signing_root, &attestation.signature)
}

// ---------------------------------------------------------------------------
Expand Down
Loading
Loading