mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-03 00:31:50 +00:00
Import BLS to execution changes before Capella (#3892)
* Import BLS to execution changes before Capella * Test for BLS to execution change HTTP API * Pack BLS to execution changes in LIFO order * Remove unused var * Clippy
This commit is contained in:
105
beacon_node/operation_pool/src/bls_to_execution_changes.rs
Normal file
105
beacon_node/operation_pool/src/bls_to_execution_changes.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use state_processing::SigVerifiedOp;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconState, ChainSpec, EthSpec, SignedBeaconBlock,
|
||||
SignedBlsToExecutionChange,
|
||||
};
|
||||
|
||||
/// Pool of BLS to execution changes that maintains a LIFO queue and an index by validator.
|
||||
///
|
||||
/// Using the LIFO queue for block production disincentivises spam on P2P at the Capella fork,
|
||||
/// and is less-relevant after that.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct BlsToExecutionChanges<T: EthSpec> {
|
||||
/// Map from validator index to BLS to execution change.
|
||||
by_validator_index: HashMap<u64, Arc<SigVerifiedOp<SignedBlsToExecutionChange, T>>>,
|
||||
/// Last-in-first-out (LIFO) queue of verified messages.
|
||||
queue: Vec<Arc<SigVerifiedOp<SignedBlsToExecutionChange, T>>>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> BlsToExecutionChanges<T> {
|
||||
pub fn existing_change_equals(
|
||||
&self,
|
||||
address_change: &SignedBlsToExecutionChange,
|
||||
) -> Option<bool> {
|
||||
self.by_validator_index
|
||||
.get(&address_change.message.validator_index)
|
||||
.map(|existing| existing.as_inner() == address_change)
|
||||
}
|
||||
|
||||
pub fn insert(
|
||||
&mut self,
|
||||
verified_change: SigVerifiedOp<SignedBlsToExecutionChange, T>,
|
||||
) -> bool {
|
||||
// Wrap in an `Arc` once on insert.
|
||||
let verified_change = Arc::new(verified_change);
|
||||
match self
|
||||
.by_validator_index
|
||||
.entry(verified_change.as_inner().message.validator_index)
|
||||
{
|
||||
Entry::Vacant(entry) => {
|
||||
self.queue.push(verified_change.clone());
|
||||
entry.insert(verified_change);
|
||||
true
|
||||
}
|
||||
Entry::Occupied(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// FIFO ordering, used for persistence to disk.
|
||||
pub fn iter_fifo(
|
||||
&self,
|
||||
) -> impl Iterator<Item = &Arc<SigVerifiedOp<SignedBlsToExecutionChange, T>>> {
|
||||
self.queue.iter()
|
||||
}
|
||||
|
||||
/// LIFO ordering, used for block packing.
|
||||
pub fn iter_lifo(
|
||||
&self,
|
||||
) -> impl Iterator<Item = &Arc<SigVerifiedOp<SignedBlsToExecutionChange, T>>> {
|
||||
self.queue.iter().rev()
|
||||
}
|
||||
|
||||
/// Prune BLS to execution changes that have been applied to the state more than 1 block ago.
|
||||
///
|
||||
/// The block check is necessary to avoid pruning too eagerly and losing the ability to include
|
||||
/// address changes during re-orgs. This is isn't *perfect* so some address changes could
|
||||
/// still get stuck if there are gnarly re-orgs and the changes can't be widely republished
|
||||
/// due to the gossip duplicate rules.
|
||||
pub fn prune<Payload: AbstractExecPayload<T>>(
|
||||
&mut self,
|
||||
head_block: &SignedBeaconBlock<T, Payload>,
|
||||
head_state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let mut validator_indices_pruned = vec![];
|
||||
|
||||
self.queue.retain(|address_change| {
|
||||
let validator_index = address_change.as_inner().message.validator_index;
|
||||
head_state
|
||||
.validators()
|
||||
.get(validator_index as usize)
|
||||
.map_or(true, |validator| {
|
||||
let prune = validator.has_eth1_withdrawal_credential(spec)
|
||||
&& head_block
|
||||
.message()
|
||||
.body()
|
||||
.bls_to_execution_changes()
|
||||
.map_or(true, |recent_changes| {
|
||||
!recent_changes
|
||||
.iter()
|
||||
.any(|c| c.message.validator_index == validator_index)
|
||||
});
|
||||
if prune {
|
||||
validator_indices_pruned.push(validator_index);
|
||||
}
|
||||
!prune
|
||||
})
|
||||
});
|
||||
|
||||
for validator_index in validator_indices_pruned {
|
||||
self.by_validator_index.remove(&validator_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ mod attestation;
|
||||
mod attestation_id;
|
||||
mod attestation_storage;
|
||||
mod attester_slashing;
|
||||
mod bls_to_execution_changes;
|
||||
mod max_cover;
|
||||
mod metrics;
|
||||
mod persistence;
|
||||
@@ -18,6 +19,7 @@ pub use persistence::{
|
||||
pub use reward_cache::RewardCache;
|
||||
|
||||
use crate::attestation_storage::{AttestationMap, CheckpointKey};
|
||||
use crate::bls_to_execution_changes::BlsToExecutionChanges;
|
||||
use crate::sync_aggregate_id::SyncAggregateId;
|
||||
use attester_slashing::AttesterSlashingMaxCover;
|
||||
use max_cover::maximum_cover;
|
||||
@@ -51,8 +53,8 @@ pub struct OperationPool<T: EthSpec + Default> {
|
||||
proposer_slashings: RwLock<HashMap<u64, SigVerifiedOp<ProposerSlashing, T>>>,
|
||||
/// Map from exiting validator to their exit data.
|
||||
voluntary_exits: RwLock<HashMap<u64, SigVerifiedOp<SignedVoluntaryExit, T>>>,
|
||||
/// Map from credential changing validator to their execution change data.
|
||||
bls_to_execution_changes: RwLock<HashMap<u64, SigVerifiedOp<SignedBlsToExecutionChange, T>>>,
|
||||
/// Map from credential changing validator to their position in the queue.
|
||||
bls_to_execution_changes: RwLock<BlsToExecutionChanges<T>>,
|
||||
/// Reward cache for accelerating attestation packing.
|
||||
reward_cache: RwLock<RewardCache>,
|
||||
_phantom: PhantomData<T>,
|
||||
@@ -513,15 +515,28 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert a BLS to execution change into the pool.
|
||||
/// Check if an address change equal to `address_change` is already in the pool.
|
||||
///
|
||||
/// Return `None` if no address change for the validator index exists in the pool.
|
||||
pub fn bls_to_execution_change_in_pool_equals(
|
||||
&self,
|
||||
address_change: &SignedBlsToExecutionChange,
|
||||
) -> Option<bool> {
|
||||
self.bls_to_execution_changes
|
||||
.read()
|
||||
.existing_change_equals(address_change)
|
||||
}
|
||||
|
||||
/// Insert a BLS to execution change into the pool, *only if* no prior change is known.
|
||||
///
|
||||
/// Return `true` if the change was inserted.
|
||||
pub fn insert_bls_to_execution_change(
|
||||
&self,
|
||||
verified_change: SigVerifiedOp<SignedBlsToExecutionChange, T>,
|
||||
) {
|
||||
self.bls_to_execution_changes.write().insert(
|
||||
verified_change.as_inner().message.validator_index,
|
||||
verified_change,
|
||||
);
|
||||
) -> bool {
|
||||
self.bls_to_execution_changes
|
||||
.write()
|
||||
.insert(verified_change)
|
||||
}
|
||||
|
||||
/// Get a list of execution changes for inclusion in a block.
|
||||
@@ -533,7 +548,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
spec: &ChainSpec,
|
||||
) -> Vec<SignedBlsToExecutionChange> {
|
||||
filter_limit_operations(
|
||||
self.bls_to_execution_changes.read().values(),
|
||||
self.bls_to_execution_changes.read().iter_lifo(),
|
||||
|address_change| {
|
||||
address_change.signature_is_still_valid(&state.fork())
|
||||
&& state
|
||||
@@ -548,33 +563,15 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
}
|
||||
|
||||
/// Prune BLS to execution changes that have been applied to the state more than 1 block ago.
|
||||
///
|
||||
/// The block check is necessary to avoid pruning too eagerly and losing the ability to include
|
||||
/// address changes during re-orgs. This is isn't *perfect* so some address changes could
|
||||
/// still get stuck if there are gnarly re-orgs and the changes can't be widely republished
|
||||
/// due to the gossip duplicate rules.
|
||||
pub fn prune_bls_to_execution_changes<Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
head_block: &SignedBeaconBlock<T, Payload>,
|
||||
head_state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
prune_validator_hash_map(
|
||||
&mut self.bls_to_execution_changes.write(),
|
||||
|validator_index, validator| {
|
||||
validator.has_eth1_withdrawal_credential(spec)
|
||||
&& head_block
|
||||
.message()
|
||||
.body()
|
||||
.bls_to_execution_changes()
|
||||
.map_or(true, |recent_changes| {
|
||||
!recent_changes
|
||||
.iter()
|
||||
.any(|c| c.message.validator_index == validator_index)
|
||||
})
|
||||
},
|
||||
head_state,
|
||||
);
|
||||
self.bls_to_execution_changes
|
||||
.write()
|
||||
.prune(head_block, head_state, spec)
|
||||
}
|
||||
|
||||
/// Prune all types of transactions given the latest head state and head fork.
|
||||
@@ -663,8 +660,8 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
pub fn get_all_bls_to_execution_changes(&self) -> Vec<SignedBlsToExecutionChange> {
|
||||
self.bls_to_execution_changes
|
||||
.read()
|
||||
.iter()
|
||||
.map(|(_, address_change)| address_change.as_inner().clone())
|
||||
.iter_fifo()
|
||||
.map(|address_change| address_change.as_inner().clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::attestation_id::AttestationId;
|
||||
use crate::attestation_storage::AttestationMap;
|
||||
use crate::bls_to_execution_changes::BlsToExecutionChanges;
|
||||
use crate::sync_aggregate_id::SyncAggregateId;
|
||||
use crate::OpPoolError;
|
||||
use crate::OperationPool;
|
||||
@@ -105,8 +106,8 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
let bls_to_execution_changes = operation_pool
|
||||
.bls_to_execution_changes
|
||||
.read()
|
||||
.iter()
|
||||
.map(|(_, bls_to_execution_change)| bls_to_execution_change.clone())
|
||||
.iter_fifo()
|
||||
.map(|bls_to_execution_change| (**bls_to_execution_change).clone())
|
||||
.collect();
|
||||
|
||||
PersistedOperationPool::V14(PersistedOperationPoolV14 {
|
||||
@@ -153,18 +154,13 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
PersistedOperationPool::V5(_) | PersistedOperationPool::V12(_) => {
|
||||
return Err(OpPoolError::IncorrectOpPoolVariant)
|
||||
}
|
||||
PersistedOperationPool::V14(pool) => RwLock::new(
|
||||
pool.bls_to_execution_changes
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|bls_to_execution_change| {
|
||||
(
|
||||
bls_to_execution_change.as_inner().message.validator_index,
|
||||
bls_to_execution_change,
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
PersistedOperationPool::V14(pool) => {
|
||||
let mut bls_to_execution_changes = BlsToExecutionChanges::default();
|
||||
for bls_to_execution_change in pool.bls_to_execution_changes {
|
||||
bls_to_execution_changes.insert(bls_to_execution_change);
|
||||
}
|
||||
RwLock::new(bls_to_execution_changes)
|
||||
}
|
||||
};
|
||||
let op_pool = OperationPool {
|
||||
attestations,
|
||||
|
||||
Reference in New Issue
Block a user