mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-04 21:34:36 +00:00
Gloas payload attestation consensus (#8827)
- Implement `process_payload_attestation` - Implement EF tests for payload attestations (allows simplification of handler now that we support all `operations` tests). - Update the `BlockSignatureVerifier` to signature-verify payload attestations Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
use crate::per_block_processing::errors::{
|
||||||
|
BlockOperationError, PayloadAttestationInvalid as Invalid,
|
||||||
|
};
|
||||||
|
use ssz_types::VariableList;
|
||||||
|
use types::{
|
||||||
|
BeaconState, BeaconStateError, ChainSpec, EthSpec, IndexedPayloadAttestation,
|
||||||
|
PayloadAttestation,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get_indexed_payload_attestation<E: EthSpec>(
|
||||||
|
state: &BeaconState<E>,
|
||||||
|
payload_attestation: &PayloadAttestation<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<IndexedPayloadAttestation<E>, BlockOperationError<Invalid>> {
|
||||||
|
let attesting_indices = get_payload_attesting_indices(state, payload_attestation, spec)?;
|
||||||
|
|
||||||
|
Ok(IndexedPayloadAttestation {
|
||||||
|
attesting_indices: VariableList::new(attesting_indices)?,
|
||||||
|
data: payload_attestation.data.clone(),
|
||||||
|
signature: payload_attestation.signature.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_payload_attesting_indices<E: EthSpec>(
|
||||||
|
state: &BeaconState<E>,
|
||||||
|
payload_attestation: &PayloadAttestation<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<Vec<u64>, BeaconStateError> {
|
||||||
|
let slot = payload_attestation.data.slot;
|
||||||
|
let ptc = state.get_ptc(slot, spec)?;
|
||||||
|
let bits = &payload_attestation.aggregation_bits;
|
||||||
|
|
||||||
|
let mut attesting_indices = vec![];
|
||||||
|
for (i, index) in ptc.into_iter().enumerate() {
|
||||||
|
if let Ok(true) = bits.get(i) {
|
||||||
|
attesting_indices.push(index as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attesting_indices.sort_unstable();
|
||||||
|
|
||||||
|
Ok(attesting_indices)
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
mod deposit_data_tree;
|
mod deposit_data_tree;
|
||||||
mod get_attestation_participation;
|
mod get_attestation_participation;
|
||||||
mod get_attesting_indices;
|
mod get_attesting_indices;
|
||||||
|
mod get_payload_attesting_indices;
|
||||||
mod initiate_validator_exit;
|
mod initiate_validator_exit;
|
||||||
mod slash_validator;
|
mod slash_validator;
|
||||||
|
|
||||||
@@ -13,6 +14,9 @@ pub use get_attestation_participation::get_attestation_participation_flag_indice
|
|||||||
pub use get_attesting_indices::{
|
pub use get_attesting_indices::{
|
||||||
attesting_indices_base, attesting_indices_electra, get_attesting_indices_from_state,
|
attesting_indices_base, attesting_indices_electra, get_attesting_indices_from_state,
|
||||||
};
|
};
|
||||||
|
pub use get_payload_attesting_indices::{
|
||||||
|
get_indexed_payload_attestation, get_payload_attesting_indices,
|
||||||
|
};
|
||||||
pub use initiate_validator_exit::initiate_validator_exit;
|
pub use initiate_validator_exit::initiate_validator_exit;
|
||||||
pub use slash_validator::slash_validator;
|
pub use slash_validator::slash_validator;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
use crate::EpochCacheError;
|
use crate::EpochCacheError;
|
||||||
use crate::common::{attesting_indices_base, attesting_indices_electra};
|
use crate::common::{
|
||||||
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
attesting_indices_base, attesting_indices_electra, get_indexed_payload_attestation,
|
||||||
|
};
|
||||||
|
use crate::per_block_processing::errors::{
|
||||||
|
AttestationInvalid, BlockOperationError, PayloadAttestationInvalid,
|
||||||
|
};
|
||||||
use std::collections::{HashMap, hash_map::Entry};
|
use std::collections::{HashMap, hash_map::Entry};
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::{
|
use types::{
|
||||||
AbstractExecPayload, AttestationRef, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec,
|
AbstractExecPayload, AttestationRef, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec,
|
||||||
Hash256, IndexedAttestation, IndexedAttestationRef, SignedBeaconBlock, Slot,
|
Hash256, IndexedAttestation, IndexedAttestationRef, IndexedPayloadAttestation,
|
||||||
|
PayloadAttestation, SignedBeaconBlock, Slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -22,6 +27,8 @@ pub struct ConsensusContext<E: EthSpec> {
|
|||||||
pub current_block_root: Option<Hash256>,
|
pub current_block_root: Option<Hash256>,
|
||||||
/// Cache of indexed attestations constructed during block processing.
|
/// Cache of indexed attestations constructed during block processing.
|
||||||
pub indexed_attestations: HashMap<Hash256, IndexedAttestation<E>>,
|
pub indexed_attestations: HashMap<Hash256, IndexedAttestation<E>>,
|
||||||
|
/// Cache of indexed payload attestations constructed during block processing.
|
||||||
|
pub indexed_payload_attestations: HashMap<Hash256, IndexedPayloadAttestation<E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
@@ -55,6 +62,7 @@ impl<E: EthSpec> ConsensusContext<E> {
|
|||||||
proposer_index: None,
|
proposer_index: None,
|
||||||
current_block_root: None,
|
current_block_root: None,
|
||||||
indexed_attestations: HashMap::new(),
|
indexed_attestations: HashMap::new(),
|
||||||
|
indexed_payload_attestations: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +185,24 @@ impl<E: EthSpec> ConsensusContext<E> {
|
|||||||
.map(|indexed_attestation| (*indexed_attestation).to_ref())
|
.map(|indexed_attestation| (*indexed_attestation).to_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_indexed_payload_attestation<'a>(
|
||||||
|
&'a mut self,
|
||||||
|
state: &BeaconState<E>,
|
||||||
|
payload_attestation: &'a PayloadAttestation<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<&'a IndexedPayloadAttestation<E>, BlockOperationError<PayloadAttestationInvalid>>
|
||||||
|
{
|
||||||
|
let key = payload_attestation.tree_hash_root();
|
||||||
|
match self.indexed_payload_attestations.entry(key) {
|
||||||
|
Entry::Occupied(occupied) => Ok(occupied.into_mut()),
|
||||||
|
Entry::Vacant(vacant) => {
|
||||||
|
let indexed_payload_attestation =
|
||||||
|
get_indexed_payload_attestation(state, payload_attestation, spec)?;
|
||||||
|
Ok(vacant.insert(indexed_payload_attestation))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn num_cached_indexed_attestations(&self) -> usize {
|
pub fn num_cached_indexed_attestations(&self) -> usize {
|
||||||
self.indexed_attestations.len()
|
self.indexed_attestations.len()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
|||||||
pub use altair::sync_committee::process_sync_aggregate;
|
pub use altair::sync_committee::process_sync_aggregate;
|
||||||
pub use block_signature_verifier::{BlockSignatureVerifier, ParallelSignatureSets};
|
pub use block_signature_verifier::{BlockSignatureVerifier, ParallelSignatureSets};
|
||||||
pub use is_valid_indexed_attestation::is_valid_indexed_attestation;
|
pub use is_valid_indexed_attestation::is_valid_indexed_attestation;
|
||||||
|
pub use is_valid_indexed_payload_attestation::is_valid_indexed_payload_attestation;
|
||||||
pub use process_operations::process_operations;
|
pub use process_operations::process_operations;
|
||||||
pub use verify_attestation::{
|
pub use verify_attestation::{
|
||||||
verify_attestation_for_block_inclusion, verify_attestation_for_state,
|
verify_attestation_for_block_inclusion, verify_attestation_for_state,
|
||||||
@@ -37,6 +38,7 @@ pub mod builder;
|
|||||||
pub mod deneb;
|
pub mod deneb;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
mod is_valid_indexed_attestation;
|
mod is_valid_indexed_attestation;
|
||||||
|
mod is_valid_indexed_payload_attestation;
|
||||||
pub mod process_operations;
|
pub mod process_operations;
|
||||||
pub mod signature_sets;
|
pub mod signature_sets;
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
@@ -45,6 +47,7 @@ mod verify_attester_slashing;
|
|||||||
mod verify_bls_to_execution_change;
|
mod verify_bls_to_execution_change;
|
||||||
mod verify_deposit;
|
mod verify_deposit;
|
||||||
mod verify_exit;
|
mod verify_exit;
|
||||||
|
mod verify_payload_attestation;
|
||||||
mod verify_proposer_slashing;
|
mod verify_proposer_slashing;
|
||||||
pub mod withdrawals;
|
pub mod withdrawals;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#![allow(clippy::arithmetic_side_effects)]
|
#![allow(clippy::arithmetic_side_effects)]
|
||||||
|
|
||||||
use super::signature_sets::{Error as SignatureSetError, *};
|
use super::signature_sets::{Error as SignatureSetError, *};
|
||||||
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
use crate::per_block_processing::errors::{
|
||||||
|
AttestationInvalid, BlockOperationError, PayloadAttestationInvalid,
|
||||||
|
};
|
||||||
use crate::{ConsensusContext, ContextError};
|
use crate::{ConsensusContext, ContextError};
|
||||||
use bls::{PublicKey, PublicKeyBytes, SignatureSet, verify_signature_sets};
|
use bls::{PublicKey, PublicKeyBytes, SignatureSet, verify_signature_sets};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@@ -18,6 +20,8 @@ pub enum Error {
|
|||||||
SignatureInvalid,
|
SignatureInvalid,
|
||||||
/// An attestation in the block was invalid. The block is invalid.
|
/// An attestation in the block was invalid. The block is invalid.
|
||||||
AttestationValidationError(BlockOperationError<AttestationInvalid>),
|
AttestationValidationError(BlockOperationError<AttestationInvalid>),
|
||||||
|
/// A payload attestation in the block was invalid. The block is invalid.
|
||||||
|
PayloadAttestationValidationError(BlockOperationError<PayloadAttestationInvalid>),
|
||||||
/// There was an error attempting to read from a `BeaconState`. Block
|
/// There was an error attempting to read from a `BeaconState`. Block
|
||||||
/// validity was not determined.
|
/// validity was not determined.
|
||||||
BeaconStateError(BeaconStateError),
|
BeaconStateError(BeaconStateError),
|
||||||
@@ -66,6 +70,12 @@ impl From<BlockOperationError<AttestationInvalid>> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<BlockOperationError<PayloadAttestationInvalid>> for Error {
|
||||||
|
fn from(e: BlockOperationError<PayloadAttestationInvalid>) -> Error {
|
||||||
|
Error::PayloadAttestationValidationError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads the BLS signatures and keys from a `SignedBeaconBlock`, storing them as a `Vec<SignatureSet>`.
|
/// Reads the BLS signatures and keys from a `SignedBeaconBlock`, storing them as a `Vec<SignatureSet>`.
|
||||||
///
|
///
|
||||||
/// This allows for optimizations related to batch BLS operations (see the
|
/// This allows for optimizations related to batch BLS operations (see the
|
||||||
@@ -171,6 +181,7 @@ where
|
|||||||
self.include_sync_aggregate(block)?;
|
self.include_sync_aggregate(block)?;
|
||||||
self.include_bls_to_execution_changes(block)?;
|
self.include_bls_to_execution_changes(block)?;
|
||||||
self.include_execution_payload_bid(block)?;
|
self.include_execution_payload_bid(block)?;
|
||||||
|
self.include_payload_attestations(block, ctxt)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -296,6 +307,39 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Includes all signatures in `self.block.body.payload_attestations` for verification.
|
||||||
|
pub fn include_payload_attestations<Payload: AbstractExecPayload<E>>(
|
||||||
|
&mut self,
|
||||||
|
block: &'a SignedBeaconBlock<E, Payload>,
|
||||||
|
ctxt: &mut ConsensusContext<E>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Ok(payload_attestations) = block.message().body().payload_attestations() else {
|
||||||
|
// Nothing to do pre-Gloas.
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
self.sets.sets.reserve(payload_attestations.len());
|
||||||
|
|
||||||
|
payload_attestations
|
||||||
|
.iter()
|
||||||
|
.try_for_each(|payload_attestation| {
|
||||||
|
let indexed_payload_attestation = ctxt.get_indexed_payload_attestation(
|
||||||
|
self.state,
|
||||||
|
payload_attestation,
|
||||||
|
self.spec,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.sets.push(indexed_payload_attestation_signature_set(
|
||||||
|
self.state,
|
||||||
|
self.get_pubkey.clone(),
|
||||||
|
&payload_attestation.signature,
|
||||||
|
indexed_payload_attestation,
|
||||||
|
self.spec,
|
||||||
|
)?);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Includes all signatures in `self.block.body.voluntary_exits` for verification.
|
/// Includes all signatures in `self.block.body.voluntary_exits` for verification.
|
||||||
pub fn include_exits<Payload: AbstractExecPayload<E>>(
|
pub fn include_exits<Payload: AbstractExecPayload<E>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ pub enum BlockProcessingError {
|
|||||||
index: usize,
|
index: usize,
|
||||||
reason: AttestationInvalid,
|
reason: AttestationInvalid,
|
||||||
},
|
},
|
||||||
|
PayloadAttestationInvalid {
|
||||||
|
index: usize,
|
||||||
|
reason: PayloadAttestationInvalid,
|
||||||
|
},
|
||||||
DepositInvalid {
|
DepositInvalid {
|
||||||
index: usize,
|
index: usize,
|
||||||
reason: DepositInvalid,
|
reason: DepositInvalid,
|
||||||
@@ -217,6 +221,7 @@ impl_into_block_processing_error_with_index!(
|
|||||||
AttesterSlashingInvalid,
|
AttesterSlashingInvalid,
|
||||||
IndexedAttestationInvalid,
|
IndexedAttestationInvalid,
|
||||||
AttestationInvalid,
|
AttestationInvalid,
|
||||||
|
PayloadAttestationInvalid,
|
||||||
DepositInvalid,
|
DepositInvalid,
|
||||||
ExitInvalid,
|
ExitInvalid,
|
||||||
BlsExecutionChangeInvalid
|
BlsExecutionChangeInvalid
|
||||||
@@ -422,6 +427,52 @@ pub enum IndexedAttestationInvalid {
|
|||||||
SignatureSetError(SignatureSetError),
|
SignatureSetError(SignatureSetError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum PayloadAttestationInvalid {
|
||||||
|
/// Block root does not match the parent beacon block root.
|
||||||
|
BlockRootMismatch {
|
||||||
|
expected: Hash256,
|
||||||
|
found: Hash256,
|
||||||
|
},
|
||||||
|
/// The attestation slot is not the previous slot.
|
||||||
|
SlotMismatch {
|
||||||
|
expected: Slot,
|
||||||
|
found: Slot,
|
||||||
|
},
|
||||||
|
BadIndexedPayloadAttestation(IndexedPayloadAttestationInvalid),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BlockOperationError<IndexedPayloadAttestationInvalid>>
|
||||||
|
for BlockOperationError<PayloadAttestationInvalid>
|
||||||
|
{
|
||||||
|
fn from(e: BlockOperationError<IndexedPayloadAttestationInvalid>) -> Self {
|
||||||
|
match e {
|
||||||
|
BlockOperationError::Invalid(e) => BlockOperationError::invalid(
|
||||||
|
PayloadAttestationInvalid::BadIndexedPayloadAttestation(e),
|
||||||
|
),
|
||||||
|
BlockOperationError::BeaconStateError(e) => BlockOperationError::BeaconStateError(e),
|
||||||
|
BlockOperationError::SignatureSetError(e) => BlockOperationError::SignatureSetError(e),
|
||||||
|
BlockOperationError::SszTypesError(e) => BlockOperationError::SszTypesError(e),
|
||||||
|
BlockOperationError::BitfieldError(e) => BlockOperationError::BitfieldError(e),
|
||||||
|
BlockOperationError::ConsensusContext(e) => BlockOperationError::ConsensusContext(e),
|
||||||
|
BlockOperationError::ArithError(e) => BlockOperationError::ArithError(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum IndexedPayloadAttestationInvalid {
|
||||||
|
/// The number of indices is 0.
|
||||||
|
IndicesEmpty,
|
||||||
|
/// The validator indices were not in increasing order.
|
||||||
|
BadValidatorIndicesOrdering,
|
||||||
|
/// The indexed attestation aggregate signature was not valid.
|
||||||
|
BadSignature,
|
||||||
|
/// There was an error whilst attempting to get a set of signatures. The signatures may have
|
||||||
|
/// been invalid or an internal error occurred.
|
||||||
|
SignatureSetError(SignatureSetError),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum DepositInvalid {
|
pub enum DepositInvalid {
|
||||||
/// The signature (proof-of-possession) does not match the given pubkey.
|
/// The signature (proof-of-possession) does not match the given pubkey.
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
use super::errors::{BlockOperationError, IndexedPayloadAttestationInvalid as Invalid};
|
||||||
|
use super::signature_sets::{get_pubkey_from_state, indexed_payload_attestation_signature_set};
|
||||||
|
use crate::VerifySignatures;
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
pub fn is_valid_indexed_payload_attestation<E: EthSpec>(
|
||||||
|
state: &BeaconState<E>,
|
||||||
|
indexed_payload_attestation: &IndexedPayloadAttestation<E>,
|
||||||
|
verify_signatures: VerifySignatures,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), BlockOperationError<Invalid>> {
|
||||||
|
// Verify indices are non-empty and sorted (duplicates allowed)
|
||||||
|
let indices = &indexed_payload_attestation.attesting_indices;
|
||||||
|
verify!(!indices.is_empty(), Invalid::IndicesEmpty);
|
||||||
|
verify!(indices.is_sorted(), Invalid::BadValidatorIndicesOrdering);
|
||||||
|
|
||||||
|
if verify_signatures.is_true() {
|
||||||
|
verify!(
|
||||||
|
indexed_payload_attestation_signature_set(
|
||||||
|
state,
|
||||||
|
|i| get_pubkey_from_state(state, i),
|
||||||
|
&indexed_payload_attestation.signature,
|
||||||
|
indexed_payload_attestation,
|
||||||
|
spec
|
||||||
|
)?
|
||||||
|
.verify(),
|
||||||
|
Invalid::BadSignature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use crate::common::{
|
|||||||
slash_validator,
|
slash_validator,
|
||||||
};
|
};
|
||||||
use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
|
use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
|
||||||
|
use crate::per_block_processing::verify_payload_attestation::verify_payload_attestation;
|
||||||
use bls::{PublicKeyBytes, SignatureBytes};
|
use bls::{PublicKeyBytes, SignatureBytes};
|
||||||
use ssz_types::FixedVector;
|
use ssz_types::FixedVector;
|
||||||
use typenum::U33;
|
use typenum::U33;
|
||||||
@@ -39,8 +40,15 @@ pub fn process_operations<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
|||||||
process_bls_to_execution_changes(state, bls_to_execution_changes, verify_signatures, spec)?;
|
process_bls_to_execution_changes(state, bls_to_execution_changes, verify_signatures, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.fork_name_unchecked().electra_enabled() && !state.fork_name_unchecked().gloas_enabled()
|
if state.fork_name_unchecked().gloas_enabled() {
|
||||||
{
|
process_payload_attestations(
|
||||||
|
state,
|
||||||
|
block_body.payload_attestations()?.iter(),
|
||||||
|
verify_signatures,
|
||||||
|
ctxt,
|
||||||
|
spec,
|
||||||
|
)?;
|
||||||
|
} else if state.fork_name_unchecked().electra_enabled() {
|
||||||
state.update_pubkey_cache()?;
|
state.update_pubkey_cache()?;
|
||||||
process_deposit_requests_pre_gloas(
|
process_deposit_requests_pre_gloas(
|
||||||
state,
|
state,
|
||||||
@@ -1074,3 +1082,45 @@ pub fn process_consolidation_request<E: EthSpec>(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_payload_attestation<E: EthSpec>(
|
||||||
|
state: &mut BeaconState<E>,
|
||||||
|
payload_attestation: &PayloadAttestation<E>,
|
||||||
|
att_index: usize,
|
||||||
|
verify_signatures: VerifySignatures,
|
||||||
|
ctxt: &mut ConsensusContext<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), BlockProcessingError> {
|
||||||
|
verify_payload_attestation(state, payload_attestation, ctxt, verify_signatures, spec)
|
||||||
|
.map_err(|e| e.into_with_index(att_index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_payload_attestations<'a, E: EthSpec, I>(
|
||||||
|
state: &mut BeaconState<E>,
|
||||||
|
payload_attestations: I,
|
||||||
|
verify_signatures: VerifySignatures,
|
||||||
|
ctxt: &mut ConsensusContext<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), BlockProcessingError>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a PayloadAttestation<E>>,
|
||||||
|
{
|
||||||
|
// Presently the PTC cache requires the committee cache for `state.slot() - 1` which is either
|
||||||
|
// in the current or previous epoch.
|
||||||
|
// TODO(gloas): These requirements may change if we introduce a PTC cache.
|
||||||
|
state.build_committee_cache(RelativeEpoch::Current, spec)?;
|
||||||
|
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
|
||||||
|
|
||||||
|
payload_attestations
|
||||||
|
.enumerate()
|
||||||
|
.try_for_each(|(i, payload_attestation)| {
|
||||||
|
process_payload_attestation(
|
||||||
|
state,
|
||||||
|
payload_attestation,
|
||||||
|
i,
|
||||||
|
verify_signatures,
|
||||||
|
ctxt,
|
||||||
|
spec,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ use typenum::Unsigned;
|
|||||||
use types::{
|
use types::{
|
||||||
AbstractExecPayload, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError,
|
AbstractExecPayload, AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError,
|
||||||
BuilderIndex, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork,
|
BuilderIndex, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork,
|
||||||
IndexedAttestation, IndexedAttestationRef, ProposerSlashing, SignedAggregateAndProof,
|
IndexedAttestation, IndexedAttestationRef, IndexedPayloadAttestation, ProposerSlashing,
|
||||||
SignedBeaconBlock, SignedBeaconBlockHeader, SignedBlsToExecutionChange,
|
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader,
|
||||||
SignedContributionAndProof, SignedExecutionPayloadBid, SignedRoot, SignedVoluntaryExit,
|
SignedBlsToExecutionChange, SignedContributionAndProof, SignedExecutionPayloadBid, SignedRoot,
|
||||||
SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData,
|
SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData,
|
||||||
consts::gloas::BUILDER_INDEX_SELF_BUILD,
|
consts::gloas::BUILDER_INDEX_SELF_BUILD,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -355,6 +355,40 @@ where
|
|||||||
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
|
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn indexed_payload_attestation_signature_set<'a, 'b, E, F>(
|
||||||
|
state: &'a BeaconState<E>,
|
||||||
|
get_pubkey: F,
|
||||||
|
signature: &'a AggregateSignature,
|
||||||
|
indexed_payload_attestation: &'b IndexedPayloadAttestation<E>,
|
||||||
|
spec: &'a ChainSpec,
|
||||||
|
) -> Result<SignatureSet<'a>>
|
||||||
|
where
|
||||||
|
E: EthSpec,
|
||||||
|
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
|
||||||
|
{
|
||||||
|
let mut pubkeys = Vec::with_capacity(indexed_payload_attestation.attesting_indices.len());
|
||||||
|
for &validator_idx in indexed_payload_attestation.attesting_indices.iter() {
|
||||||
|
pubkeys.push(
|
||||||
|
get_pubkey(validator_idx as usize).ok_or(Error::ValidatorUnknown(validator_idx))?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let epoch = indexed_payload_attestation
|
||||||
|
.data
|
||||||
|
.slot
|
||||||
|
.epoch(E::slots_per_epoch());
|
||||||
|
let domain = spec.get_domain(
|
||||||
|
epoch,
|
||||||
|
Domain::PTCAttester,
|
||||||
|
&state.fork(),
|
||||||
|
state.genesis_validators_root(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let message = indexed_payload_attestation.data.signing_root(domain);
|
||||||
|
|
||||||
|
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execution_payload_bid_signature_set<'a, E, F>(
|
pub fn execution_payload_bid_signature_set<'a, E, F>(
|
||||||
state: &'a BeaconState<E>,
|
state: &'a BeaconState<E>,
|
||||||
get_builder_pubkey: F,
|
get_builder_pubkey: F,
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
use super::VerifySignatures;
|
||||||
|
use super::errors::{BlockOperationError, PayloadAttestationInvalid as Invalid};
|
||||||
|
use crate::ConsensusContext;
|
||||||
|
use crate::per_block_processing::is_valid_indexed_payload_attestation;
|
||||||
|
use safe_arith::SafeArith;
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
pub fn verify_payload_attestation<'ctxt, E: EthSpec>(
|
||||||
|
state: &mut BeaconState<E>,
|
||||||
|
payload_attestation: &'ctxt PayloadAttestation<E>,
|
||||||
|
ctxt: &'ctxt mut ConsensusContext<E>,
|
||||||
|
verify_signatures: VerifySignatures,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<(), BlockOperationError<Invalid>> {
|
||||||
|
let data = &payload_attestation.data;
|
||||||
|
|
||||||
|
// Check that the attestation is for the parent beacon block
|
||||||
|
verify!(
|
||||||
|
data.beacon_block_root == state.latest_block_header().parent_root,
|
||||||
|
Invalid::BlockRootMismatch {
|
||||||
|
expected: state.latest_block_header().parent_root,
|
||||||
|
found: data.beacon_block_root,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check that the attestation is for the previous slot
|
||||||
|
verify!(
|
||||||
|
data.slot.safe_add(1)? == state.slot(),
|
||||||
|
Invalid::SlotMismatch {
|
||||||
|
expected: state.slot().saturating_sub(Slot::new(1)),
|
||||||
|
found: data.slot,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let indexed_payload_attestation =
|
||||||
|
ctxt.get_indexed_payload_attestation(state, payload_attestation, spec)?;
|
||||||
|
|
||||||
|
is_valid_indexed_payload_attestation(
|
||||||
|
state,
|
||||||
|
indexed_payload_attestation,
|
||||||
|
verify_signatures,
|
||||||
|
spec,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -47,8 +47,6 @@ excluded_paths = [
|
|||||||
"bls12-381-tests/hash_to_G2",
|
"bls12-381-tests/hash_to_G2",
|
||||||
"tests/.*/eip7732",
|
"tests/.*/eip7732",
|
||||||
"tests/.*/eip7805",
|
"tests/.*/eip7805",
|
||||||
# TODO(gloas): remove these ignores as more Gloas operations are implemented
|
|
||||||
"tests/.*/gloas/operations/payload_attestation/.*",
|
|
||||||
# TODO(gloas): remove these ignores as Gloas consensus is implemented
|
# TODO(gloas): remove these ignores as Gloas consensus is implemented
|
||||||
"tests/.*/gloas/fork/.*",
|
"tests/.*/gloas/fork/.*",
|
||||||
"tests/.*/gloas/fork_choice/.*",
|
"tests/.*/gloas/fork_choice/.*",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use state_processing::{
|
|||||||
process_operations::{
|
process_operations::{
|
||||||
altair_deneb, base, gloas, process_attester_slashings,
|
altair_deneb, base, gloas, process_attester_slashings,
|
||||||
process_bls_to_execution_changes, process_deposits, process_exits,
|
process_bls_to_execution_changes, process_deposits, process_exits,
|
||||||
process_proposer_slashings,
|
process_payload_attestation, process_proposer_slashings,
|
||||||
},
|
},
|
||||||
process_sync_aggregate, withdrawals,
|
process_sync_aggregate, withdrawals,
|
||||||
},
|
},
|
||||||
@@ -31,8 +31,9 @@ use types::{
|
|||||||
Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyBellatrix,
|
Attestation, AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockBodyBellatrix,
|
||||||
BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu,
|
BeaconBlockBodyCapella, BeaconBlockBodyDeneb, BeaconBlockBodyElectra, BeaconBlockBodyFulu,
|
||||||
BeaconState, BlindedPayload, ConsolidationRequest, Deposit, DepositRequest, ExecutionPayload,
|
BeaconState, BlindedPayload, ConsolidationRequest, Deposit, DepositRequest, ExecutionPayload,
|
||||||
ForkVersionDecode, FullPayload, ProposerSlashing, SignedBlsToExecutionChange,
|
ForkVersionDecode, FullPayload, PayloadAttestation, ProposerSlashing,
|
||||||
SignedExecutionPayloadEnvelope, SignedVoluntaryExit, SyncAggregate, WithdrawalRequest,
|
SignedBlsToExecutionChange, SignedExecutionPayloadEnvelope, SignedVoluntaryExit, SyncAggregate,
|
||||||
|
WithdrawalRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize)]
|
||||||
@@ -667,6 +668,32 @@ impl<E: EthSpec> Operation<E> for ConsolidationRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> Operation<E> for PayloadAttestation<E> {
|
||||||
|
type Error = BlockProcessingError;
|
||||||
|
|
||||||
|
fn handler_name() -> String {
|
||||||
|
"payload_attestation".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||||
|
fork_name.gloas_enabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode(path: &Path, _fork_name: ForkName, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||||
|
ssz_decode_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_to(
|
||||||
|
&self,
|
||||||
|
state: &mut BeaconState<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
_extra: &Operations<E, Self>,
|
||||||
|
) -> Result<(), BlockProcessingError> {
|
||||||
|
let mut ctxt = ConsensusContext::new(state.slot());
|
||||||
|
process_payload_attestation(state, self, 0, VerifySignatures::True, &mut ctxt, spec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
||||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||||
let spec = &testing_spec::<E>(fork_name);
|
let spec = &testing_spec::<E>(fork_name);
|
||||||
|
|||||||
@@ -1177,25 +1177,6 @@ impl<E: EthSpec + TypeName, O: Operation<E>> Handler for OperationsHandler<E, O>
|
|||||||
fn handler_name(&self) -> String {
|
fn handler_name(&self) -> String {
|
||||||
O::handler_name()
|
O::handler_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
|
||||||
Self::Case::is_enabled_for_fork(fork_name)
|
|
||||||
&& (!fork_name.gloas_enabled()
|
|
||||||
|| self.handler_name() == "attestation"
|
|
||||||
|| self.handler_name() == "attester_slashing"
|
|
||||||
|| self.handler_name() == "block_header"
|
|
||||||
|| self.handler_name() == "bls_to_execution_change"
|
|
||||||
|| self.handler_name() == "consolidation_request"
|
|
||||||
|| self.handler_name() == "deposit_request"
|
|
||||||
|| self.handler_name() == "deposit"
|
|
||||||
|| self.handler_name() == "execution_payload"
|
|
||||||
|| self.handler_name() == "execution_payload_bid"
|
|
||||||
|| self.handler_name() == "proposer_slashing"
|
|
||||||
|| self.handler_name() == "sync_aggregate"
|
|
||||||
|| self.handler_name() == "withdrawal_request"
|
|
||||||
|| self.handler_name() == "withdrawals"
|
|
||||||
|| self.handler_name() == "voluntary_exit")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Educe)]
|
#[derive(Educe)]
|
||||||
|
|||||||
@@ -99,6 +99,12 @@ fn operations_execution_payload_bid() {
|
|||||||
OperationsHandler::<MainnetEthSpec, ExecutionPayloadBidBlock<_>>::default().run();
|
OperationsHandler::<MainnetEthSpec, ExecutionPayloadBidBlock<_>>::default().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operations_payload_attestation() {
|
||||||
|
OperationsHandler::<MinimalEthSpec, PayloadAttestation<_>>::default().run();
|
||||||
|
OperationsHandler::<MainnetEthSpec, PayloadAttestation<_>>::default().run();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn operations_withdrawals() {
|
fn operations_withdrawals() {
|
||||||
OperationsHandler::<MinimalEthSpec, WithdrawalsPayload<_>>::default().run();
|
OperationsHandler::<MinimalEthSpec, WithdrawalsPayload<_>>::default().run();
|
||||||
|
|||||||
Reference in New Issue
Block a user