diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index a8db6ce9530..7b457fed370 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -337,6 +337,7 @@ struct PartialBeaconBlock { sync_aggregate: Option>, prepare_payload_handle: Option>, bls_to_execution_changes: Vec, + consolidations: Vec, } pub type BeaconForkChoice = ForkChoice< @@ -4885,6 +4886,8 @@ impl BeaconChain { .op_pool .get_bls_to_execution_changes(&state, &self.spec); + let consolidations = self.op_pool.get_consolidations(&state, &self.spec); + // Iterate through the naive aggregation pool and ensure all the attestations from there // are included in the operation pool. let unagg_import_timer = @@ -5044,6 +5047,7 @@ impl BeaconChain { sync_aggregate, prepare_payload_handle, bls_to_execution_changes, + consolidations, }) } @@ -5072,6 +5076,7 @@ impl BeaconChain { // produce said `execution_payload`. prepare_payload_handle: _, bls_to_execution_changes, + consolidations, } = partial_beacon_block; let (inner_block, maybe_blobs_and_proofs, execution_payload_value) = match &state { @@ -5215,7 +5220,7 @@ impl BeaconChain { "Kzg commitments missing from block contents".to_string(), ), )?, - consolidations: unimplemented!(), + consolidations: consolidations.into(), }, }), maybe_blobs_and_proofs, diff --git a/beacon_node/operation_pool/src/lib.rs b/beacon_node/operation_pool/src/lib.rs index 7e1ddb1fd2f..94d22e713b6 100644 --- a/beacon_node/operation_pool/src/lib.rs +++ b/beacon_node/operation_pool/src/lib.rs @@ -18,6 +18,7 @@ pub use persistence::{ PersistedOperationPoolV15, PersistedOperationPoolV5, }; pub use reward_cache::RewardCache; +use types::SignedConsolidation; use crate::attestation_storage::{AttestationMap, CheckpointKey}; use crate::bls_to_execution_changes::BlsToExecutionChanges; @@ -29,7 +30,7 @@ use rand::seq::SliceRandom; use rand::thread_rng; use state_processing::per_block_processing::errors::AttestationValidationError; use state_processing::per_block_processing::{ - get_slashable_indices_modular, verify_exit, VerifySignatures, + get_slashable_indices_modular, verify_consolidation, verify_exit, VerifySignatures, }; use state_processing::{SigVerifiedOp, VerifyOperation}; use std::collections::{hash_map::Entry, HashMap, HashSet}; @@ -58,6 +59,8 @@ pub struct OperationPool { voluntary_exits: RwLock>>, /// Map from credential changing validator to their position in the queue. bls_to_execution_changes: RwLock>, + /// Map from source validator index to consolidation. + consolidations: RwLock>>, /// Reward cache for accelerating attestation packing. reward_cache: RwLock, _phantom: PhantomData, @@ -615,6 +618,44 @@ impl OperationPool { .prune(head_block, head_state, spec) } + /// Insert a consolidation into the pool. + pub fn insert_consolidation( + &self, + verified_consolidation: SigVerifiedOp, + ) { + self.consolidations.write().insert( + verified_consolidation.as_inner().message.source_index, + verified_consolidation, + ); + } + + /// Get a list of consolidations for inclusion in a block. + pub fn get_consolidations( + &self, + state: &BeaconState, + spec: &ChainSpec, + ) -> Vec { + filter_limit_operations( + self.consolidations.read().values(), + |consolidation| { + consolidation.signature_is_still_valid(&state.fork()) + // TODO(maxeb): do a more succint check of validity + && verify_consolidation(state, consolidation.as_inner(), VerifySignatures::False, spec).is_ok() + }, + |consolidation| consolidation.as_inner().clone(), + T::MaxConsolidations::to_usize(), + ) + } + + /// Prune consolidations for validators which are exited in the finalized epoch. + pub fn prune_consolidations(&self, head_state: &BeaconState) { + prune_validator_hash_map( + &mut self.consolidations.write(), + |_, validator| validator.exit_epoch <= head_state.finalized_checkpoint().epoch, + head_state, + ); + } + /// Prune all types of transactions given the latest head state and head fork. pub fn prune_all>( &self, @@ -629,6 +670,7 @@ impl OperationPool { self.prune_attester_slashings(head_state); self.prune_voluntary_exits(head_state); self.prune_bls_to_execution_changes(head_block, head_state, spec); + self.prune_consolidations(head_state); } /// Total number of voluntary exits in the pool. @@ -705,6 +747,17 @@ impl OperationPool { .map(|address_change| address_change.as_inner().clone()) .collect() } + + /// Returns all known `SignedConsolidation` objects. + /// + /// This method may return objects that are invalid for block inclusion. + pub fn get_all_consolidations(&self) -> Vec { + self.consolidations + .read() + .iter() + .map(|(_, consolidation)| consolidation.as_inner().clone()) + .collect() + } } /// Filter up to a maximum number of operations out of an iterator. diff --git a/beacon_node/operation_pool/src/persistence.rs b/beacon_node/operation_pool/src/persistence.rs index 35d2b4ce7ee..2902de30183 100644 --- a/beacon_node/operation_pool/src/persistence.rs +++ b/beacon_node/operation_pool/src/persistence.rs @@ -193,6 +193,8 @@ impl PersistedOperationPool { proposer_slashings, voluntary_exits, bls_to_execution_changes: RwLock::new(bls_to_execution_changes), + // TODO(maxeb): persist consolidations + consolidations: <_>::default(), reward_cache: Default::default(), _phantom: Default::default(), }; diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index 768a9c34d29..a5b2948c117 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -19,6 +19,7 @@ pub use verify_attestation::{ verify_attestation_for_block_inclusion, verify_attestation_for_state, }; pub use verify_bls_to_execution_change::verify_bls_to_execution_change; +pub use verify_consolidation::verify_consolidation; pub use verify_deposit::{ get_existing_validator_index, verify_deposit_merkle_proof, verify_deposit_signature, }; diff --git a/consensus/state_processing/src/per_block_processing/errors.rs b/consensus/state_processing/src/per_block_processing/errors.rs index 4da6c82c451..a472f45a95d 100644 --- a/consensus/state_processing/src/per_block_processing/errors.rs +++ b/consensus/state_processing/src/per_block_processing/errors.rs @@ -207,6 +207,7 @@ pub type SyncCommitteeMessageValidationError = BlockOperationError; pub type ExitValidationError = BlockOperationError; pub type BlsExecutionChangeValidationError = BlockOperationError; +pub type ConsolidationValidationError = BlockOperationError; #[derive(Debug, PartialEq, Clone)] pub enum BlockOperationError { diff --git a/consensus/state_processing/src/per_block_processing/verify_consolidation.rs b/consensus/state_processing/src/per_block_processing/verify_consolidation.rs index 76bce02655b..87807c7b6ed 100644 --- a/consensus/state_processing/src/per_block_processing/verify_consolidation.rs +++ b/consensus/state_processing/src/per_block_processing/verify_consolidation.rs @@ -6,7 +6,7 @@ use crate::{ use types::*; pub fn verify_consolidation( - state: &mut BeaconState, + state: &BeaconState, signed_consolidation: &SignedConsolidation, verify_signatures: VerifySignatures, spec: &ChainSpec, diff --git a/consensus/state_processing/src/verify_operation.rs b/consensus/state_processing/src/verify_operation.rs index b3924cd9732..eb3ebbc8c59 100644 --- a/consensus/state_processing/src/verify_operation.rs +++ b/consensus/state_processing/src/verify_operation.rs @@ -1,9 +1,9 @@ use crate::per_block_processing::{ errors::{ - AttesterSlashingValidationError, BlsExecutionChangeValidationError, ExitValidationError, - ProposerSlashingValidationError, + AttesterSlashingValidationError, BlsExecutionChangeValidationError, + ConsolidationValidationError, ExitValidationError, ProposerSlashingValidationError, }, - verify_attester_slashing, verify_bls_to_execution_change, verify_exit, + verify_attester_slashing, verify_bls_to_execution_change, verify_consolidation, verify_exit, verify_proposer_slashing, }; use crate::VerifySignatures; @@ -14,7 +14,7 @@ use ssz_derive::{Decode, Encode}; use std::marker::PhantomData; use types::{ AttesterSlashing, BeaconState, ChainSpec, Epoch, EthSpec, Fork, ForkVersion, ProposerSlashing, - SignedBlsToExecutionChange, SignedVoluntaryExit, + SignedBlsToExecutionChange, SignedConsolidation, SignedVoluntaryExit, }; const MAX_FORKS_VERIFIED_AGAINST: usize = 2; @@ -206,6 +206,24 @@ impl VerifyOperation for SignedBlsToExecutionChange { } } +impl VerifyOperation for SignedConsolidation { + type Error = ConsolidationValidationError; + + fn validate( + self, + state: &BeaconState, + spec: &ChainSpec, + ) -> Result, Self::Error> { + verify_consolidation(state, &self, VerifySignatures::True, spec)?; + Ok(SigVerifiedOp::new(self, state)) + } + + #[allow(clippy::arithmetic_side_effects)] + fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> { + smallvec![self.message.epoch] + } +} + /// Trait for operations that can be verified and transformed into a /// `SigVerifiedOp`. ///