Op pool and gossip for BLS to execution changes (#3726)
This commit is contained in:
parent
58b54f0a53
commit
788b337951
@ -14,7 +14,12 @@ node_test_rig = { path = "../testing/node_test_rig" }
|
|||||||
[features]
|
[features]
|
||||||
write_ssz_files = ["beacon_chain/write_ssz_files"] # Writes debugging .ssz files to /tmp during block processing.
|
write_ssz_files = ["beacon_chain/write_ssz_files"] # Writes debugging .ssz files to /tmp during block processing.
|
||||||
withdrawals = ["beacon_chain/withdrawals", "types/withdrawals", "store/withdrawals", "execution_layer/withdrawals"]
|
withdrawals = ["beacon_chain/withdrawals", "types/withdrawals", "store/withdrawals", "execution_layer/withdrawals"]
|
||||||
withdrawals-processing = ["beacon_chain/withdrawals-processing", "store/withdrawals-processing", "execution_layer/withdrawals-processing"]
|
withdrawals-processing = [
|
||||||
|
"beacon_chain/withdrawals-processing",
|
||||||
|
"store/withdrawals-processing",
|
||||||
|
"execution_layer/withdrawals-processing",
|
||||||
|
"http_api/withdrawals-processing",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eth2_config = { path = "../common/eth2_config" }
|
eth2_config = { path = "../common/eth2_config" }
|
||||||
|
@ -11,7 +11,12 @@ write_ssz_files = [] # Writes debugging .ssz files to /tmp during block process
|
|||||||
participation_metrics = [] # Exposes validator participation metrics to Prometheus.
|
participation_metrics = [] # Exposes validator participation metrics to Prometheus.
|
||||||
fork_from_env = [] # Initialise the harness chain spec from the FORK_NAME env variable
|
fork_from_env = [] # Initialise the harness chain spec from the FORK_NAME env variable
|
||||||
withdrawals = ["state_processing/withdrawals", "types/withdrawals", "store/withdrawals", "execution_layer/withdrawals"]
|
withdrawals = ["state_processing/withdrawals", "types/withdrawals", "store/withdrawals", "execution_layer/withdrawals"]
|
||||||
withdrawals-processing = ["state_processing/withdrawals-processing", "store/withdrawals-processing", "execution_layer/withdrawals-processing"]
|
withdrawals-processing = [
|
||||||
|
"state_processing/withdrawals-processing",
|
||||||
|
"store/withdrawals-processing",
|
||||||
|
"execution_layer/withdrawals-processing",
|
||||||
|
"operation_pool/withdrawals-processing"
|
||||||
|
]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
maplit = "1.0.2"
|
maplit = "1.0.2"
|
||||||
|
@ -341,6 +341,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
|||||||
/// Maintains a record of which validators we've seen attester slashings for.
|
/// Maintains a record of which validators we've seen attester slashings for.
|
||||||
pub(crate) observed_attester_slashings:
|
pub(crate) observed_attester_slashings:
|
||||||
Mutex<ObservedOperations<AttesterSlashing<T::EthSpec>, T::EthSpec>>,
|
Mutex<ObservedOperations<AttesterSlashing<T::EthSpec>, T::EthSpec>>,
|
||||||
|
/// Maintains a record of which validators we've seen BLS to execution changes for.
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
pub(crate) observed_bls_to_execution_changes:
|
||||||
|
Mutex<ObservedOperations<SignedBlsToExecutionChange, T::EthSpec>>,
|
||||||
/// Provides information from the Ethereum 1 (PoW) chain.
|
/// Provides information from the Ethereum 1 (PoW) chain.
|
||||||
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
|
pub eth1_chain: Option<Eth1Chain<T::Eth1Chain, T::EthSpec>>,
|
||||||
/// Interfaces with the execution client.
|
/// Interfaces with the execution client.
|
||||||
@ -2181,6 +2185,42 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify a signed BLS to exection change before allowing it to propagate on the gossip network.
|
||||||
|
pub fn verify_bls_to_execution_change_for_gossip(
|
||||||
|
&self,
|
||||||
|
bls_to_execution_change: SignedBlsToExecutionChange,
|
||||||
|
) -> Result<ObservationOutcome<SignedBlsToExecutionChange, T::EthSpec>, Error> {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
{
|
||||||
|
let wall_clock_state = self.wall_clock_state()?;
|
||||||
|
Ok(self
|
||||||
|
.observed_bls_to_execution_changes
|
||||||
|
.lock()
|
||||||
|
.verify_and_observe(bls_to_execution_change, &wall_clock_state, &self.spec)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
{
|
||||||
|
drop(bls_to_execution_change);
|
||||||
|
Ok(ObservationOutcome::AlreadyKnown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import a BLS to execution change to the op pool.
|
||||||
|
pub fn import_bls_to_execution_change(
|
||||||
|
&self,
|
||||||
|
bls_to_execution_change: SigVerifiedOp<SignedBlsToExecutionChange, T::EthSpec>,
|
||||||
|
) {
|
||||||
|
if self.eth1_chain.is_some() {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
self.op_pool
|
||||||
|
.insert_bls_to_execution_change(bls_to_execution_change);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
drop(bls_to_execution_change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempt to obtain sync committee duties from the head.
|
/// Attempt to obtain sync committee duties from the head.
|
||||||
pub fn sync_committee_duties_from_head(
|
pub fn sync_committee_duties_from_head(
|
||||||
&self,
|
&self,
|
||||||
@ -3491,6 +3531,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
|
|
||||||
let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?;
|
let eth1_data = eth1_chain.eth1_data_for_block_production(&state, &self.spec)?;
|
||||||
let deposits = eth1_chain.deposits_for_block_inclusion(&state, ð1_data, &self.spec)?;
|
let deposits = eth1_chain.deposits_for_block_inclusion(&state, ð1_data, &self.spec)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "withdrawals")]
|
||||||
let bls_to_execution_changes = self
|
let bls_to_execution_changes = self
|
||||||
.op_pool
|
.op_pool
|
||||||
.get_bls_to_execution_changes(&state, &self.spec);
|
.get_bls_to_execution_changes(&state, &self.spec);
|
||||||
|
@ -780,6 +780,8 @@ where
|
|||||||
observed_voluntary_exits: <_>::default(),
|
observed_voluntary_exits: <_>::default(),
|
||||||
observed_proposer_slashings: <_>::default(),
|
observed_proposer_slashings: <_>::default(),
|
||||||
observed_attester_slashings: <_>::default(),
|
observed_attester_slashings: <_>::default(),
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
observed_bls_to_execution_changes: <_>::default(),
|
||||||
eth1_chain: self.eth1_chain,
|
eth1_chain: self.eth1_chain,
|
||||||
execution_layer: self.execution_layer,
|
execution_layer: self.execution_layer,
|
||||||
genesis_validators_root,
|
genesis_validators_root,
|
||||||
|
@ -907,8 +907,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
.execution_status
|
.execution_status
|
||||||
.is_optimistic_or_invalid();
|
.is_optimistic_or_invalid();
|
||||||
|
|
||||||
self.op_pool
|
self.op_pool.prune_all(
|
||||||
.prune_all(&new_snapshot.beacon_state, self.epoch()?);
|
&new_snapshot.beacon_block,
|
||||||
|
&new_snapshot.beacon_state,
|
||||||
|
self.epoch()?,
|
||||||
|
&self.spec,
|
||||||
|
);
|
||||||
|
|
||||||
self.observed_block_producers.write().prune(
|
self.observed_block_producers.write().prune(
|
||||||
new_view
|
new_view
|
||||||
|
@ -17,8 +17,9 @@ use ssz_types::Error as SszTypesError;
|
|||||||
use state_processing::{
|
use state_processing::{
|
||||||
block_signature_verifier::Error as BlockSignatureVerifierError,
|
block_signature_verifier::Error as BlockSignatureVerifierError,
|
||||||
per_block_processing::errors::{
|
per_block_processing::errors::{
|
||||||
AttestationValidationError, AttesterSlashingValidationError, ExitValidationError,
|
AttestationValidationError, AttesterSlashingValidationError,
|
||||||
ProposerSlashingValidationError, SyncCommitteeMessageValidationError,
|
BlsExecutionChangeValidationError, ExitValidationError, ProposerSlashingValidationError,
|
||||||
|
SyncCommitteeMessageValidationError,
|
||||||
},
|
},
|
||||||
signature_sets::Error as SignatureSetError,
|
signature_sets::Error as SignatureSetError,
|
||||||
state_advance::Error as StateAdvanceError,
|
state_advance::Error as StateAdvanceError,
|
||||||
@ -70,6 +71,7 @@ pub enum BeaconChainError {
|
|||||||
ExitValidationError(ExitValidationError),
|
ExitValidationError(ExitValidationError),
|
||||||
ProposerSlashingValidationError(ProposerSlashingValidationError),
|
ProposerSlashingValidationError(ProposerSlashingValidationError),
|
||||||
AttesterSlashingValidationError(AttesterSlashingValidationError),
|
AttesterSlashingValidationError(AttesterSlashingValidationError),
|
||||||
|
BlsExecutionChangeValidationError(BlsExecutionChangeValidationError),
|
||||||
StateSkipTooLarge {
|
StateSkipTooLarge {
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
requested_slot: Slot,
|
requested_slot: Slot,
|
||||||
@ -212,6 +214,7 @@ easy_from_to!(SyncCommitteeMessageValidationError, BeaconChainError);
|
|||||||
easy_from_to!(ExitValidationError, BeaconChainError);
|
easy_from_to!(ExitValidationError, BeaconChainError);
|
||||||
easy_from_to!(ProposerSlashingValidationError, BeaconChainError);
|
easy_from_to!(ProposerSlashingValidationError, BeaconChainError);
|
||||||
easy_from_to!(AttesterSlashingValidationError, BeaconChainError);
|
easy_from_to!(AttesterSlashingValidationError, BeaconChainError);
|
||||||
|
easy_from_to!(BlsExecutionChangeValidationError, BeaconChainError);
|
||||||
easy_from_to!(SszTypesError, BeaconChainError);
|
easy_from_to!(SszTypesError, BeaconChainError);
|
||||||
easy_from_to!(OpPoolError, BeaconChainError);
|
easy_from_to!(OpPoolError, BeaconChainError);
|
||||||
easy_from_to!(NaiveAggregationError, BeaconChainError);
|
easy_from_to!(NaiveAggregationError, BeaconChainError);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use state_processing::{SigVerifiedOp, VerifyOperation};
|
use state_processing::{SigVerifiedOp, VerifyOperation};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@ -9,6 +9,9 @@ use types::{
|
|||||||
SignedVoluntaryExit, Slot,
|
SignedVoluntaryExit, Slot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
use types::SignedBlsToExecutionChange;
|
||||||
|
|
||||||
/// Number of validator indices to store on the stack in `observed_validators`.
|
/// Number of validator indices to store on the stack in `observed_validators`.
|
||||||
pub const SMALL_VEC_SIZE: usize = 8;
|
pub const SMALL_VEC_SIZE: usize = 8;
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ pub enum ObservationOutcome<T: Encode + Decode, E: EthSpec> {
|
|||||||
AlreadyKnown,
|
AlreadyKnown,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for exits and slashings which can be observed using `ObservedOperations`.
|
/// Trait for operations which can be observed using `ObservedOperations`.
|
||||||
pub trait ObservableOperation<E: EthSpec>: VerifyOperation<E> + Sized {
|
pub trait ObservableOperation<E: EthSpec>: VerifyOperation<E> + Sized {
|
||||||
/// The set of validator indices involved in this operation.
|
/// The set of validator indices involved in this operation.
|
||||||
///
|
///
|
||||||
@ -49,13 +52,13 @@ pub trait ObservableOperation<E: EthSpec>: VerifyOperation<E> + Sized {
|
|||||||
|
|
||||||
impl<E: EthSpec> ObservableOperation<E> for SignedVoluntaryExit {
|
impl<E: EthSpec> ObservableOperation<E> for SignedVoluntaryExit {
|
||||||
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
|
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
|
||||||
std::iter::once(self.message.validator_index).collect()
|
smallvec![self.message.validator_index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> ObservableOperation<E> for ProposerSlashing {
|
impl<E: EthSpec> ObservableOperation<E> for ProposerSlashing {
|
||||||
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
|
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
|
||||||
std::iter::once(self.signed_header_1.message.proposer_index).collect()
|
smallvec![self.signed_header_1.message.proposer_index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +83,13 @@ impl<E: EthSpec> ObservableOperation<E> for AttesterSlashing<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
impl<E: EthSpec> ObservableOperation<E> for SignedBlsToExecutionChange {
|
||||||
|
fn observed_validators(&self) -> SmallVec<[u64; SMALL_VEC_SIZE]> {
|
||||||
|
smallvec![self.message.validator_index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
impl<T: ObservableOperation<E>, E: EthSpec> ObservedOperations<T, E> {
|
||||||
pub fn verify_and_observe(
|
pub fn verify_and_observe(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -5,6 +5,9 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
autotests = false # using a single test binary compiles faster
|
autotests = false # using a single test binary compiles faster
|
||||||
|
|
||||||
|
[features]
|
||||||
|
withdrawals-processing = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
warp = { version = "0.3.2", features = ["tls"] }
|
warp = { version = "0.3.2", features = ["tls"] }
|
||||||
serde = { version = "1.0.116", features = ["derive"] }
|
serde = { version = "1.0.116", features = ["derive"] }
|
||||||
|
@ -49,9 +49,9 @@ use types::{
|
|||||||
Attestation, AttestationData, AttesterSlashing, BeaconStateError, BlindedPayload,
|
Attestation, AttestationData, AttesterSlashing, BeaconStateError, BlindedPayload,
|
||||||
CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload,
|
CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload,
|
||||||
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
|
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
|
||||||
SignedBeaconBlock, SignedBlindedBeaconBlock, SignedContributionAndProof,
|
SignedBeaconBlock, SignedBlindedBeaconBlock, SignedBlsToExecutionChange,
|
||||||
SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage,
|
SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot,
|
||||||
SyncContributionData,
|
SyncCommitteeMessage, SyncContributionData,
|
||||||
};
|
};
|
||||||
use version::{
|
use version::{
|
||||||
add_consensus_version_header, execution_optimistic_fork_versioned_response,
|
add_consensus_version_header, execution_optimistic_fork_versioned_response,
|
||||||
@ -1536,6 +1536,59 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// GET beacon/pool/bls_to_execution_changes
|
||||||
|
let get_beacon_pool_bls_to_execution_changes = beacon_pool_path
|
||||||
|
.clone()
|
||||||
|
.and(warp::path("bls_to_execution_changes"))
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and_then(|chain: Arc<BeaconChain<T>>| {
|
||||||
|
blocking_json_task(move || {
|
||||||
|
let address_changes = chain.op_pool.get_all_bls_to_execution_changes();
|
||||||
|
Ok(api_types::GenericResponse::from(address_changes))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
// POST beacon/pool/bls_to_execution_changes
|
||||||
|
let post_beacon_pool_bls_to_execution_changes = beacon_pool_path
|
||||||
|
.clone()
|
||||||
|
.and(warp::path("bls_to_execution_changes"))
|
||||||
|
.and(warp::path::end())
|
||||||
|
.and(warp::body::json())
|
||||||
|
.and(network_tx_filter.clone())
|
||||||
|
.and_then(
|
||||||
|
|chain: Arc<BeaconChain<T>>,
|
||||||
|
address_change: SignedBlsToExecutionChange,
|
||||||
|
network_tx: UnboundedSender<NetworkMessage<T::EthSpec>>| {
|
||||||
|
blocking_json_task(move || {
|
||||||
|
let outcome = chain
|
||||||
|
.verify_bls_to_execution_change_for_gossip(address_change)
|
||||||
|
.map_err(|e| {
|
||||||
|
warp_utils::reject::object_invalid(format!(
|
||||||
|
"gossip verification failed: {:?}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let ObservationOutcome::New(address_change) = outcome {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
{
|
||||||
|
publish_pubsub_message(
|
||||||
|
&network_tx,
|
||||||
|
PubsubMessage::BlsToExecutionChange(Box::new(
|
||||||
|
address_change.as_inner().clone(),
|
||||||
|
)),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
drop(network_tx);
|
||||||
|
|
||||||
|
chain.import_bls_to_execution_change(address_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// GET beacon/deposit_snapshot
|
// GET beacon/deposit_snapshot
|
||||||
let get_beacon_deposit_snapshot = eth_v1
|
let get_beacon_deposit_snapshot = eth_v1
|
||||||
.and(warp::path("beacon"))
|
.and(warp::path("beacon"))
|
||||||
@ -3170,6 +3223,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.or(get_beacon_pool_attester_slashings.boxed())
|
.or(get_beacon_pool_attester_slashings.boxed())
|
||||||
.or(get_beacon_pool_proposer_slashings.boxed())
|
.or(get_beacon_pool_proposer_slashings.boxed())
|
||||||
.or(get_beacon_pool_voluntary_exits.boxed())
|
.or(get_beacon_pool_voluntary_exits.boxed())
|
||||||
|
.or(get_beacon_pool_bls_to_execution_changes.boxed())
|
||||||
.or(get_beacon_deposit_snapshot.boxed())
|
.or(get_beacon_deposit_snapshot.boxed())
|
||||||
.or(get_config_fork_schedule.boxed())
|
.or(get_config_fork_schedule.boxed())
|
||||||
.or(get_config_spec.boxed())
|
.or(get_config_spec.boxed())
|
||||||
@ -3218,6 +3272,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.or(post_beacon_pool_proposer_slashings.boxed())
|
.or(post_beacon_pool_proposer_slashings.boxed())
|
||||||
.or(post_beacon_pool_voluntary_exits.boxed())
|
.or(post_beacon_pool_voluntary_exits.boxed())
|
||||||
.or(post_beacon_pool_sync_committees.boxed())
|
.or(post_beacon_pool_sync_committees.boxed())
|
||||||
|
.or(post_beacon_pool_bls_to_execution_changes.boxed())
|
||||||
.or(post_validator_duties_attester.boxed())
|
.or(post_validator_duties_attester.boxed())
|
||||||
.or(post_validator_duties_sync.boxed())
|
.or(post_validator_duties_sync.boxed())
|
||||||
.or(post_validator_aggregate_and_proofs.boxed())
|
.or(post_validator_aggregate_and_proofs.boxed())
|
||||||
|
@ -36,6 +36,8 @@ pub struct GossipCache {
|
|||||||
signed_contribution_and_proof: Option<Duration>,
|
signed_contribution_and_proof: Option<Duration>,
|
||||||
/// Timeout for sync committee messages.
|
/// Timeout for sync committee messages.
|
||||||
sync_committee_message: Option<Duration>,
|
sync_committee_message: Option<Duration>,
|
||||||
|
/// Timeout for signed BLS to execution changes.
|
||||||
|
bls_to_execution_change: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -59,6 +61,8 @@ pub struct GossipCacheBuilder {
|
|||||||
signed_contribution_and_proof: Option<Duration>,
|
signed_contribution_and_proof: Option<Duration>,
|
||||||
/// Timeout for sync committee messages.
|
/// Timeout for sync committee messages.
|
||||||
sync_committee_message: Option<Duration>,
|
sync_committee_message: Option<Duration>,
|
||||||
|
/// Timeout for signed BLS to execution changes.
|
||||||
|
bls_to_execution_change: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -117,6 +121,12 @@ impl GossipCacheBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timeout for BLS to execution change messages.
|
||||||
|
pub fn bls_to_execution_change_timeout(mut self, timeout: Duration) -> Self {
|
||||||
|
self.bls_to_execution_change = Some(timeout);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> GossipCache {
|
pub fn build(self) -> GossipCache {
|
||||||
let GossipCacheBuilder {
|
let GossipCacheBuilder {
|
||||||
default_timeout,
|
default_timeout,
|
||||||
@ -129,6 +139,7 @@ impl GossipCacheBuilder {
|
|||||||
attester_slashing,
|
attester_slashing,
|
||||||
signed_contribution_and_proof,
|
signed_contribution_and_proof,
|
||||||
sync_committee_message,
|
sync_committee_message,
|
||||||
|
bls_to_execution_change,
|
||||||
} = self;
|
} = self;
|
||||||
GossipCache {
|
GossipCache {
|
||||||
expirations: DelayQueue::default(),
|
expirations: DelayQueue::default(),
|
||||||
@ -142,6 +153,7 @@ impl GossipCacheBuilder {
|
|||||||
attester_slashing: attester_slashing.or(default_timeout),
|
attester_slashing: attester_slashing.or(default_timeout),
|
||||||
signed_contribution_and_proof: signed_contribution_and_proof.or(default_timeout),
|
signed_contribution_and_proof: signed_contribution_and_proof.or(default_timeout),
|
||||||
sync_committee_message: sync_committee_message.or(default_timeout),
|
sync_committee_message: sync_committee_message.or(default_timeout),
|
||||||
|
bls_to_execution_change: bls_to_execution_change.or(default_timeout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,6 +177,7 @@ impl GossipCache {
|
|||||||
GossipKind::AttesterSlashing => self.attester_slashing,
|
GossipKind::AttesterSlashing => self.attester_slashing,
|
||||||
GossipKind::SignedContributionAndProof => self.signed_contribution_and_proof,
|
GossipKind::SignedContributionAndProof => self.signed_contribution_and_proof,
|
||||||
GossipKind::SyncCommitteeMessage(_) => self.sync_committee_message,
|
GossipKind::SyncCommitteeMessage(_) => self.sync_committee_message,
|
||||||
|
GossipKind::BlsToExecutionChange => self.bls_to_execution_change,
|
||||||
};
|
};
|
||||||
let expire_timeout = match expire_timeout {
|
let expire_timeout = match expire_timeout {
|
||||||
Some(expire_timeout) => expire_timeout,
|
Some(expire_timeout) => expire_timeout,
|
||||||
|
@ -199,6 +199,7 @@ impl<AppReqId: ReqId, TSpec: EthSpec> Network<AppReqId, TSpec> {
|
|||||||
.attester_slashing_timeout(half_epoch * 2)
|
.attester_slashing_timeout(half_epoch * 2)
|
||||||
// .signed_contribution_and_proof_timeout(timeout) // Do not retry
|
// .signed_contribution_and_proof_timeout(timeout) // Do not retry
|
||||||
// .sync_committee_message_timeout(timeout) // Do not retry
|
// .sync_committee_message_timeout(timeout) // Do not retry
|
||||||
|
.bls_to_execution_change_timeout(half_epoch * 2)
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -253,6 +253,7 @@ pub(crate) fn create_whitelist_filter(
|
|||||||
add(ProposerSlashing);
|
add(ProposerSlashing);
|
||||||
add(AttesterSlashing);
|
add(AttesterSlashing);
|
||||||
add(SignedContributionAndProof);
|
add(SignedContributionAndProof);
|
||||||
|
add(BlsToExecutionChange);
|
||||||
for id in 0..attestation_subnet_count {
|
for id in 0..attestation_subnet_count {
|
||||||
add(Attestation(SubnetId::new(id)));
|
add(Attestation(SubnetId::new(id)));
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,8 @@ use types::{
|
|||||||
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, ForkContext, ForkName, ProposerSlashing,
|
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, ForkContext, ForkName, ProposerSlashing,
|
||||||
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase,
|
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase,
|
||||||
SignedBeaconBlockCapella, SignedBeaconBlockEip4844, SignedBeaconBlockMerge,
|
SignedBeaconBlockCapella, SignedBeaconBlockEip4844, SignedBeaconBlockMerge,
|
||||||
SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
SignedBlsToExecutionChange, SignedContributionAndProof, SignedVoluntaryExit, SubnetId,
|
||||||
|
SyncCommitteeMessage, SyncSubnetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// TODO(pawan): move this to consensus/types? strictly not a consensus type
|
/// TODO(pawan): move this to consensus/types? strictly not a consensus type
|
||||||
@ -48,6 +49,8 @@ pub enum PubsubMessage<T: EthSpec> {
|
|||||||
SignedContributionAndProof(Box<SignedContributionAndProof<T>>),
|
SignedContributionAndProof(Box<SignedContributionAndProof<T>>),
|
||||||
/// Gossipsub message providing notification of unaggregated sync committee signatures with its subnet id.
|
/// Gossipsub message providing notification of unaggregated sync committee signatures with its subnet id.
|
||||||
SyncCommitteeMessage(Box<(SyncSubnetId, SyncCommitteeMessage)>),
|
SyncCommitteeMessage(Box<(SyncSubnetId, SyncCommitteeMessage)>),
|
||||||
|
/// Gossipsub message for BLS to execution change messages.
|
||||||
|
BlsToExecutionChange(Box<SignedBlsToExecutionChange>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `DataTransform` trait of gossipsub to employ snappy compression
|
// Implements the `DataTransform` trait of gossipsub to employ snappy compression
|
||||||
@ -133,6 +136,7 @@ impl<T: EthSpec> PubsubMessage<T> {
|
|||||||
PubsubMessage::AttesterSlashing(_) => GossipKind::AttesterSlashing,
|
PubsubMessage::AttesterSlashing(_) => GossipKind::AttesterSlashing,
|
||||||
PubsubMessage::SignedContributionAndProof(_) => GossipKind::SignedContributionAndProof,
|
PubsubMessage::SignedContributionAndProof(_) => GossipKind::SignedContributionAndProof,
|
||||||
PubsubMessage::SyncCommitteeMessage(data) => GossipKind::SyncCommitteeMessage(data.0),
|
PubsubMessage::SyncCommitteeMessage(data) => GossipKind::SyncCommitteeMessage(data.0),
|
||||||
|
PubsubMessage::BlsToExecutionChange(_) => GossipKind::BlsToExecutionChange,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,6 +262,14 @@ impl<T: EthSpec> PubsubMessage<T> {
|
|||||||
sync_committee,
|
sync_committee,
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
GossipKind::BlsToExecutionChange => {
|
||||||
|
let bls_to_execution_change =
|
||||||
|
SignedBlsToExecutionChange::from_ssz_bytes(data)
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
Ok(PubsubMessage::BlsToExecutionChange(Box::new(
|
||||||
|
bls_to_execution_change,
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,6 +292,7 @@ impl<T: EthSpec> PubsubMessage<T> {
|
|||||||
PubsubMessage::Attestation(data) => data.1.as_ssz_bytes(),
|
PubsubMessage::Attestation(data) => data.1.as_ssz_bytes(),
|
||||||
PubsubMessage::SignedContributionAndProof(data) => data.as_ssz_bytes(),
|
PubsubMessage::SignedContributionAndProof(data) => data.as_ssz_bytes(),
|
||||||
PubsubMessage::SyncCommitteeMessage(data) => data.1.as_ssz_bytes(),
|
PubsubMessage::SyncCommitteeMessage(data) => data.1.as_ssz_bytes(),
|
||||||
|
PubsubMessage::BlsToExecutionChange(data) => data.as_ssz_bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,6 +333,13 @@ impl<T: EthSpec> std::fmt::Display for PubsubMessage<T> {
|
|||||||
PubsubMessage::SyncCommitteeMessage(data) => {
|
PubsubMessage::SyncCommitteeMessage(data) => {
|
||||||
write!(f, "Sync committee message: subnet_id: {}", *data.0)
|
write!(f, "Sync committee message: subnet_id: {}", *data.0)
|
||||||
}
|
}
|
||||||
|
PubsubMessage::BlsToExecutionChange(data) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Signed BLS to execution change: validator_index: {}, address: {:?}",
|
||||||
|
data.message.validator_index, data.message.to_execution_address
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,9 @@ pub const PROPOSER_SLASHING_TOPIC: &str = "proposer_slashing";
|
|||||||
pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing";
|
pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing";
|
||||||
pub const SIGNED_CONTRIBUTION_AND_PROOF_TOPIC: &str = "sync_committee_contribution_and_proof";
|
pub const SIGNED_CONTRIBUTION_AND_PROOF_TOPIC: &str = "sync_committee_contribution_and_proof";
|
||||||
pub const SYNC_COMMITTEE_PREFIX_TOPIC: &str = "sync_committee_";
|
pub const SYNC_COMMITTEE_PREFIX_TOPIC: &str = "sync_committee_";
|
||||||
|
pub const BLS_TO_EXECUTION_CHANGE_TOPIC: &str = "bls_to_execution_change";
|
||||||
|
|
||||||
pub const CORE_TOPICS: [GossipKind; 7] = [
|
pub const CORE_TOPICS: [GossipKind; 8] = [
|
||||||
GossipKind::BeaconBlock,
|
GossipKind::BeaconBlock,
|
||||||
GossipKind::BeaconBlocksAndBlobsSidecar,
|
GossipKind::BeaconBlocksAndBlobsSidecar,
|
||||||
GossipKind::BeaconAggregateAndProof,
|
GossipKind::BeaconAggregateAndProof,
|
||||||
@ -28,6 +29,7 @@ pub const CORE_TOPICS: [GossipKind; 7] = [
|
|||||||
GossipKind::ProposerSlashing,
|
GossipKind::ProposerSlashing,
|
||||||
GossipKind::AttesterSlashing,
|
GossipKind::AttesterSlashing,
|
||||||
GossipKind::SignedContributionAndProof,
|
GossipKind::SignedContributionAndProof,
|
||||||
|
GossipKind::BlsToExecutionChange,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// A gossipsub topic which encapsulates the type of messages that should be sent and received over
|
/// A gossipsub topic which encapsulates the type of messages that should be sent and received over
|
||||||
@ -67,6 +69,8 @@ pub enum GossipKind {
|
|||||||
/// Topic for publishing unaggregated sync committee signatures on a particular subnet.
|
/// Topic for publishing unaggregated sync committee signatures on a particular subnet.
|
||||||
#[strum(serialize = "sync_committee")]
|
#[strum(serialize = "sync_committee")]
|
||||||
SyncCommitteeMessage(SyncSubnetId),
|
SyncCommitteeMessage(SyncSubnetId),
|
||||||
|
/// Topic for validator messages which change their withdrawal address.
|
||||||
|
BlsToExecutionChange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for GossipKind {
|
impl std::fmt::Display for GossipKind {
|
||||||
@ -141,6 +145,7 @@ impl GossipTopic {
|
|||||||
VOLUNTARY_EXIT_TOPIC => GossipKind::VoluntaryExit,
|
VOLUNTARY_EXIT_TOPIC => GossipKind::VoluntaryExit,
|
||||||
PROPOSER_SLASHING_TOPIC => GossipKind::ProposerSlashing,
|
PROPOSER_SLASHING_TOPIC => GossipKind::ProposerSlashing,
|
||||||
ATTESTER_SLASHING_TOPIC => GossipKind::AttesterSlashing,
|
ATTESTER_SLASHING_TOPIC => GossipKind::AttesterSlashing,
|
||||||
|
BLS_TO_EXECUTION_CHANGE_TOPIC => GossipKind::BlsToExecutionChange,
|
||||||
topic => match committee_topic_index(topic) {
|
topic => match committee_topic_index(topic) {
|
||||||
Some(subnet) => match subnet {
|
Some(subnet) => match subnet {
|
||||||
Subnet::Attestation(s) => GossipKind::Attestation(s),
|
Subnet::Attestation(s) => GossipKind::Attestation(s),
|
||||||
@ -177,30 +182,8 @@ impl From<GossipTopic> for Topic {
|
|||||||
|
|
||||||
impl From<GossipTopic> for String {
|
impl From<GossipTopic> for String {
|
||||||
fn from(topic: GossipTopic) -> String {
|
fn from(topic: GossipTopic) -> String {
|
||||||
let encoding = match topic.encoding {
|
// Use the `Display` implementation below.
|
||||||
GossipEncoding::SSZSnappy => SSZ_SNAPPY_ENCODING_POSTFIX,
|
topic.to_string()
|
||||||
};
|
|
||||||
|
|
||||||
let kind = match topic.kind {
|
|
||||||
GossipKind::BeaconBlock => BEACON_BLOCK_TOPIC.into(),
|
|
||||||
GossipKind::BeaconBlocksAndBlobsSidecar => BEACON_BLOCK_AND_BLOBS_SIDECAR_TOPIC.into(),
|
|
||||||
GossipKind::BeaconAggregateAndProof => BEACON_AGGREGATE_AND_PROOF_TOPIC.into(),
|
|
||||||
GossipKind::VoluntaryExit => VOLUNTARY_EXIT_TOPIC.into(),
|
|
||||||
GossipKind::ProposerSlashing => PROPOSER_SLASHING_TOPIC.into(),
|
|
||||||
GossipKind::AttesterSlashing => ATTESTER_SLASHING_TOPIC.into(),
|
|
||||||
GossipKind::Attestation(index) => format!("{}{}", BEACON_ATTESTATION_PREFIX, *index,),
|
|
||||||
GossipKind::SignedContributionAndProof => SIGNED_CONTRIBUTION_AND_PROOF_TOPIC.into(),
|
|
||||||
GossipKind::SyncCommitteeMessage(index) => {
|
|
||||||
format!("{}{}", SYNC_COMMITTEE_PREFIX_TOPIC, *index)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
format!(
|
|
||||||
"/{}/{}/{}/{}",
|
|
||||||
TOPIC_PREFIX,
|
|
||||||
hex::encode(topic.fork_digest),
|
|
||||||
kind,
|
|
||||||
encoding
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +205,7 @@ impl std::fmt::Display for GossipTopic {
|
|||||||
GossipKind::SyncCommitteeMessage(index) => {
|
GossipKind::SyncCommitteeMessage(index) => {
|
||||||
format!("{}{}", SYNC_COMMITTEE_PREFIX_TOPIC, *index)
|
format!("{}{}", SYNC_COMMITTEE_PREFIX_TOPIC, *index)
|
||||||
}
|
}
|
||||||
|
GossipKind::BlsToExecutionChange => BLS_TO_EXECUTION_CHANGE_TOPIC.into(),
|
||||||
};
|
};
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
|
@ -64,8 +64,8 @@ use task_executor::TaskExecutor;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, AttesterSlashing, Hash256, ProposerSlashing, SignedAggregateAndProof,
|
Attestation, AttesterSlashing, Hash256, ProposerSlashing, SignedAggregateAndProof,
|
||||||
SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit, SubnetId,
|
SignedBeaconBlock, SignedBlsToExecutionChange, SignedContributionAndProof, SignedVoluntaryExit,
|
||||||
SyncCommitteeMessage, SyncSubnetId,
|
SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
||||||
};
|
};
|
||||||
use work_reprocessing_queue::{
|
use work_reprocessing_queue::{
|
||||||
spawn_reprocess_scheduler, QueuedAggregate, QueuedRpcBlock, QueuedUnaggregate, ReadyWork,
|
spawn_reprocess_scheduler, QueuedAggregate, QueuedRpcBlock, QueuedUnaggregate, ReadyWork,
|
||||||
@ -163,6 +163,12 @@ const MAX_BLOBS_BY_RANGE_QUEUE_LEN: usize = 1_024;
|
|||||||
/// will be stored before we start dropping them.
|
/// will be stored before we start dropping them.
|
||||||
const MAX_BLOCKS_BY_ROOTS_QUEUE_LEN: usize = 1_024;
|
const MAX_BLOCKS_BY_ROOTS_QUEUE_LEN: usize = 1_024;
|
||||||
|
|
||||||
|
/// Maximum number of `SignedBlsToExecutionChange` messages to queue before dropping them.
|
||||||
|
///
|
||||||
|
/// This value is set high to accommodate the large spike that is expected immediately after Capella
|
||||||
|
/// is activated.
|
||||||
|
const MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN: usize = 16_384;
|
||||||
|
|
||||||
/// The name of the manager tokio task.
|
/// The name of the manager tokio task.
|
||||||
const MANAGER_TASK_NAME: &str = "beacon_processor_manager";
|
const MANAGER_TASK_NAME: &str = "beacon_processor_manager";
|
||||||
|
|
||||||
@ -206,6 +212,7 @@ pub const BLOCKS_BY_ROOTS_REQUEST: &str = "blocks_by_roots_request";
|
|||||||
pub const BLOBS_BY_RANGE_REQUEST: &str = "blobs_by_range_request";
|
pub const BLOBS_BY_RANGE_REQUEST: &str = "blobs_by_range_request";
|
||||||
pub const UNKNOWN_BLOCK_ATTESTATION: &str = "unknown_block_attestation";
|
pub const UNKNOWN_BLOCK_ATTESTATION: &str = "unknown_block_attestation";
|
||||||
pub const UNKNOWN_BLOCK_AGGREGATE: &str = "unknown_block_aggregate";
|
pub const UNKNOWN_BLOCK_AGGREGATE: &str = "unknown_block_aggregate";
|
||||||
|
pub const GOSSIP_BLS_TO_EXECUTION_CHANGE: &str = "gossip_bls_to_execution_change";
|
||||||
|
|
||||||
/// A simple first-in-first-out queue with a maximum length.
|
/// A simple first-in-first-out queue with a maximum length.
|
||||||
struct FifoQueue<T> {
|
struct FifoQueue<T> {
|
||||||
@ -515,6 +522,22 @@ impl<T: BeaconChainTypes> WorkEvent<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Work` event for some BLS to execution change.
|
||||||
|
pub fn gossip_bls_to_execution_change(
|
||||||
|
message_id: MessageId,
|
||||||
|
peer_id: PeerId,
|
||||||
|
bls_to_execution_change: Box<SignedBlsToExecutionChange>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
drop_during_sync: false,
|
||||||
|
work: Work::GossipBlsToExecutionChange {
|
||||||
|
message_id,
|
||||||
|
peer_id,
|
||||||
|
bls_to_execution_change,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new `Work` event for some block, where the result from computation (if any) is
|
/// Create a new `Work` event for some block, where the result from computation (if any) is
|
||||||
/// sent to the other side of `result_tx`.
|
/// sent to the other side of `result_tx`.
|
||||||
pub fn rpc_beacon_block(
|
pub fn rpc_beacon_block(
|
||||||
@ -789,6 +812,11 @@ pub enum Work<T: BeaconChainTypes> {
|
|||||||
request_id: PeerRequestId,
|
request_id: PeerRequestId,
|
||||||
request: BlobsByRangeRequest,
|
request: BlobsByRangeRequest,
|
||||||
},
|
},
|
||||||
|
GossipBlsToExecutionChange {
|
||||||
|
message_id: MessageId,
|
||||||
|
peer_id: PeerId,
|
||||||
|
bls_to_execution_change: Box<SignedBlsToExecutionChange>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BeaconChainTypes> Work<T> {
|
impl<T: BeaconChainTypes> Work<T> {
|
||||||
@ -815,6 +843,7 @@ impl<T: BeaconChainTypes> Work<T> {
|
|||||||
Work::BlobsByRangeRequest { .. } => BLOBS_BY_RANGE_REQUEST,
|
Work::BlobsByRangeRequest { .. } => BLOBS_BY_RANGE_REQUEST,
|
||||||
Work::UnknownBlockAttestation { .. } => UNKNOWN_BLOCK_ATTESTATION,
|
Work::UnknownBlockAttestation { .. } => UNKNOWN_BLOCK_ATTESTATION,
|
||||||
Work::UnknownBlockAggregate { .. } => UNKNOWN_BLOCK_AGGREGATE,
|
Work::UnknownBlockAggregate { .. } => UNKNOWN_BLOCK_AGGREGATE,
|
||||||
|
Work::GossipBlsToExecutionChange { .. } => GOSSIP_BLS_TO_EXECUTION_CHANGE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -960,6 +989,9 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
|||||||
let mut bbroots_queue = FifoQueue::new(MAX_BLOCKS_BY_ROOTS_QUEUE_LEN);
|
let mut bbroots_queue = FifoQueue::new(MAX_BLOCKS_BY_ROOTS_QUEUE_LEN);
|
||||||
let mut blbrange_queue = FifoQueue::new(MAX_BLOBS_BY_RANGE_QUEUE_LEN);
|
let mut blbrange_queue = FifoQueue::new(MAX_BLOBS_BY_RANGE_QUEUE_LEN);
|
||||||
|
|
||||||
|
let mut gossip_bls_to_execution_change_queue =
|
||||||
|
FifoQueue::new(MAX_BLS_TO_EXECUTION_CHANGE_QUEUE_LEN);
|
||||||
|
|
||||||
// Channels for sending work to the re-process scheduler (`work_reprocessing_tx`) and to
|
// Channels for sending work to the re-process scheduler (`work_reprocessing_tx`) and to
|
||||||
// receive them back once they are ready (`ready_work_rx`).
|
// receive them back once they are ready (`ready_work_rx`).
|
||||||
let (ready_work_tx, ready_work_rx) = mpsc::channel(MAX_SCHEDULED_WORK_QUEUE_LEN);
|
let (ready_work_tx, ready_work_rx) = mpsc::channel(MAX_SCHEDULED_WORK_QUEUE_LEN);
|
||||||
@ -1194,9 +1226,12 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
|||||||
self.spawn_worker(item, toolbox);
|
self.spawn_worker(item, toolbox);
|
||||||
} else if let Some(item) = gossip_proposer_slashing_queue.pop() {
|
} else if let Some(item) = gossip_proposer_slashing_queue.pop() {
|
||||||
self.spawn_worker(item, toolbox);
|
self.spawn_worker(item, toolbox);
|
||||||
// Check exits last since our validators don't get rewards from them.
|
// Check exits and address changes late since our validators don't get
|
||||||
|
// rewards from them.
|
||||||
} else if let Some(item) = gossip_voluntary_exit_queue.pop() {
|
} else if let Some(item) = gossip_voluntary_exit_queue.pop() {
|
||||||
self.spawn_worker(item, toolbox);
|
self.spawn_worker(item, toolbox);
|
||||||
|
} else if let Some(item) = gossip_bls_to_execution_change_queue.pop() {
|
||||||
|
self.spawn_worker(item, toolbox);
|
||||||
// Handle backfill sync chain segments.
|
// Handle backfill sync chain segments.
|
||||||
} else if let Some(item) = backfill_chain_segment.pop() {
|
} else if let Some(item) = backfill_chain_segment.pop() {
|
||||||
self.spawn_worker(item, toolbox);
|
self.spawn_worker(item, toolbox);
|
||||||
@ -1313,6 +1348,9 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
|||||||
Work::UnknownBlockAggregate { .. } => {
|
Work::UnknownBlockAggregate { .. } => {
|
||||||
unknown_block_aggregate_queue.push(work)
|
unknown_block_aggregate_queue.push(work)
|
||||||
}
|
}
|
||||||
|
Work::GossipBlsToExecutionChange { .. } => {
|
||||||
|
gossip_bls_to_execution_change_queue.push(work, work_id, &self.log)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1365,6 +1403,10 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
|||||||
&metrics::BEACON_PROCESSOR_ATTESTER_SLASHING_QUEUE_TOTAL,
|
&metrics::BEACON_PROCESSOR_ATTESTER_SLASHING_QUEUE_TOTAL,
|
||||||
gossip_attester_slashing_queue.len() as i64,
|
gossip_attester_slashing_queue.len() as i64,
|
||||||
);
|
);
|
||||||
|
metrics::set_gauge(
|
||||||
|
&metrics::BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_QUEUE_TOTAL,
|
||||||
|
gossip_bls_to_execution_change_queue.len() as i64,
|
||||||
|
);
|
||||||
|
|
||||||
if aggregate_queue.is_full() && aggregate_debounce.elapsed() {
|
if aggregate_queue.is_full() && aggregate_debounce.elapsed() {
|
||||||
error!(
|
error!(
|
||||||
@ -1623,6 +1665,20 @@ impl<T: BeaconChainTypes> BeaconProcessor<T> {
|
|||||||
seen_timestamp,
|
seen_timestamp,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
/*
|
||||||
|
* BLS to execution change verification.
|
||||||
|
*/
|
||||||
|
Work::GossipBlsToExecutionChange {
|
||||||
|
message_id,
|
||||||
|
peer_id,
|
||||||
|
bls_to_execution_change,
|
||||||
|
} => task_spawner.spawn_blocking(move || {
|
||||||
|
worker.process_gossip_bls_to_execution_change(
|
||||||
|
message_id,
|
||||||
|
peer_id,
|
||||||
|
*bls_to_execution_change,
|
||||||
|
)
|
||||||
|
}),
|
||||||
/*
|
/*
|
||||||
* Verification for beacon blocks received during syncing via RPC.
|
* Verification for beacon blocks received during syncing via RPC.
|
||||||
*/
|
*/
|
||||||
|
@ -23,8 +23,9 @@ use store::hot_cold_store::HotColdDBError;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, Hash256, IndexedAttestation,
|
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, Hash256, IndexedAttestation,
|
||||||
ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof,
|
ProposerSlashing, SignedAggregateAndProof, SignedBeaconBlock, SignedBlsToExecutionChange,
|
||||||
SignedVoluntaryExit, Slot, SubnetId, SyncCommitteeMessage, SyncSubnetId,
|
SignedContributionAndProof, SignedVoluntaryExit, Slot, SubnetId, SyncCommitteeMessage,
|
||||||
|
SyncSubnetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -1192,6 +1193,65 @@ impl<T: BeaconChainTypes> Worker<T> {
|
|||||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_ATTESTER_SLASHING_IMPORTED_TOTAL);
|
metrics::inc_counter(&metrics::BEACON_PROCESSOR_ATTESTER_SLASHING_IMPORTED_TOTAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process_gossip_bls_to_execution_change(
|
||||||
|
self,
|
||||||
|
message_id: MessageId,
|
||||||
|
peer_id: PeerId,
|
||||||
|
bls_to_execution_change: SignedBlsToExecutionChange,
|
||||||
|
) {
|
||||||
|
let validator_index = bls_to_execution_change.message.validator_index;
|
||||||
|
let address = bls_to_execution_change.message.to_execution_address;
|
||||||
|
|
||||||
|
let change = match self
|
||||||
|
.chain
|
||||||
|
.verify_bls_to_execution_change_for_gossip(bls_to_execution_change)
|
||||||
|
{
|
||||||
|
Ok(ObservationOutcome::New(change)) => change,
|
||||||
|
Ok(ObservationOutcome::AlreadyKnown) => {
|
||||||
|
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
|
||||||
|
debug!(
|
||||||
|
self.log,
|
||||||
|
"Dropping BLS to execution change";
|
||||||
|
"validator_index" => validator_index,
|
||||||
|
"peer" => %peer_id
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
debug!(
|
||||||
|
self.log,
|
||||||
|
"Dropping invalid BLS to execution change";
|
||||||
|
"validator_index" => validator_index,
|
||||||
|
"peer" => %peer_id,
|
||||||
|
"error" => ?e
|
||||||
|
);
|
||||||
|
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject);
|
||||||
|
// We penalize the peer slightly to prevent overuse of invalids.
|
||||||
|
self.gossip_penalize_peer(
|
||||||
|
peer_id,
|
||||||
|
PeerAction::HighToleranceError,
|
||||||
|
"invalid_bls_to_execution_change",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics::inc_counter(&metrics::BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_VERIFIED_TOTAL);
|
||||||
|
|
||||||
|
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
|
||||||
|
|
||||||
|
self.chain.import_bls_to_execution_change(change);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
self.log,
|
||||||
|
"Successfully imported BLS to execution change";
|
||||||
|
"validator_index" => validator_index,
|
||||||
|
"address" => ?address,
|
||||||
|
);
|
||||||
|
|
||||||
|
metrics::inc_counter(&metrics::BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_IMPORTED_TOTAL);
|
||||||
|
}
|
||||||
|
|
||||||
/// Process the sync committee signature received from the gossip network and:
|
/// Process the sync committee signature received from the gossip network and:
|
||||||
///
|
///
|
||||||
/// - If it passes gossip propagation criteria, tell the network thread to forward it.
|
/// - If it passes gossip propagation criteria, tell the network thread to forward it.
|
||||||
|
@ -143,6 +143,19 @@ lazy_static! {
|
|||||||
"beacon_processor_attester_slashing_imported_total",
|
"beacon_processor_attester_slashing_imported_total",
|
||||||
"Total number of attester slashings imported to the op pool."
|
"Total number of attester slashings imported to the op pool."
|
||||||
);
|
);
|
||||||
|
// Gossip BLS to execution changes.
|
||||||
|
pub static ref BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_QUEUE_TOTAL: Result<IntGauge> = try_create_int_gauge(
|
||||||
|
"beacon_processor_bls_to_execution_change_queue_total",
|
||||||
|
"Count of address changes from gossip waiting to be verified."
|
||||||
|
);
|
||||||
|
pub static ref BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_VERIFIED_TOTAL: Result<IntCounter> = try_create_int_counter(
|
||||||
|
"beacon_processor_bls_to_execution_change_verified_total",
|
||||||
|
"Total number of address changes verified for propagation."
|
||||||
|
);
|
||||||
|
pub static ref BEACON_PROCESSOR_BLS_TO_EXECUTION_CHANGE_IMPORTED_TOTAL: Result<IntCounter> = try_create_int_counter(
|
||||||
|
"beacon_processor_bls_to_execution_change_imported_total",
|
||||||
|
"Total number of address changes imported to the op pool."
|
||||||
|
);
|
||||||
// Rpc blocks.
|
// Rpc blocks.
|
||||||
pub static ref BEACON_PROCESSOR_RPC_BLOCK_QUEUE_TOTAL: Result<IntGauge> = try_create_int_gauge(
|
pub static ref BEACON_PROCESSOR_RPC_BLOCK_QUEUE_TOTAL: Result<IntGauge> = try_create_int_gauge(
|
||||||
"beacon_processor_rpc_block_queue_total",
|
"beacon_processor_rpc_block_queue_total",
|
||||||
|
@ -291,6 +291,18 @@ impl<T: BeaconChainTypes> Router<T> {
|
|||||||
sync_committtee_msg.0,
|
sync_committtee_msg.0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
PubsubMessage::BlsToExecutionChange(bls_to_execution_change) => {
|
||||||
|
trace!(
|
||||||
|
self.log,
|
||||||
|
"Received BLS to execution change";
|
||||||
|
"peer_id" => %peer_id
|
||||||
|
);
|
||||||
|
self.processor.on_bls_to_execution_change_gossip(
|
||||||
|
id,
|
||||||
|
peer_id,
|
||||||
|
bls_to_execution_change,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,8 @@ use store::SyncCommitteeMessage;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, ProposerSlashing,
|
Attestation, AttesterSlashing, BlobsSidecar, EthSpec, ProposerSlashing,
|
||||||
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, SignedVoluntaryExit,
|
SignedAggregateAndProof, SignedBeaconBlock, SignedBlsToExecutionChange,
|
||||||
SubnetId, SyncSubnetId,
|
SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncSubnetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Processes validated messages from the network. It relays necessary data to the syncing thread
|
/// Processes validated messages from the network. It relays necessary data to the syncing thread
|
||||||
@ -411,6 +411,19 @@ impl<T: BeaconChainTypes> Processor<T> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn on_bls_to_execution_change_gossip(
|
||||||
|
&mut self,
|
||||||
|
message_id: MessageId,
|
||||||
|
peer_id: PeerId,
|
||||||
|
bls_to_execution_change: Box<SignedBlsToExecutionChange>,
|
||||||
|
) {
|
||||||
|
self.send_beacon_processor_work(BeaconWorkEvent::gossip_bls_to_execution_change(
|
||||||
|
message_id,
|
||||||
|
peer_id,
|
||||||
|
bls_to_execution_change,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn send_beacon_processor_work(&mut self, work: BeaconWorkEvent<T>) {
|
fn send_beacon_processor_work(&mut self, work: BeaconWorkEvent<T>) {
|
||||||
self.beacon_processor_send
|
self.beacon_processor_send
|
||||||
.try_send(work)
|
.try_send(work)
|
||||||
|
@ -4,6 +4,9 @@ version = "0.2.0"
|
|||||||
authors = ["Michael Sproul <michael@sigmaprime.io>"]
|
authors = ["Michael Sproul <michael@sigmaprime.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
withdrawals-processing = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
derivative = "2.1.1"
|
derivative = "2.1.1"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
|
@ -30,10 +30,10 @@ use std::collections::{hash_map::Entry, HashMap, HashSet};
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use types::{
|
use types::{
|
||||||
sync_aggregate::Error as SyncAggregateError, typenum::Unsigned, Attestation, AttestationData,
|
sync_aggregate::Error as SyncAggregateError, typenum::Unsigned, AbstractExecPayload,
|
||||||
AttesterSlashing, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, ProposerSlashing,
|
Attestation, AttestationData, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec,
|
||||||
SignedBlsToExecutionChange, SignedVoluntaryExit, Slot, SyncAggregate,
|
Epoch, EthSpec, ProposerSlashing, SignedBeaconBlock, SignedBlsToExecutionChange,
|
||||||
SyncCommitteeContribution, Validator,
|
SignedVoluntaryExit, Slot, SyncAggregate, SyncCommitteeContribution, Validator,
|
||||||
};
|
};
|
||||||
|
|
||||||
type SyncContributions<T> = RwLock<HashMap<SyncAggregateId, Vec<SyncCommitteeContribution<T>>>>;
|
type SyncContributions<T> = RwLock<HashMap<SyncAggregateId, Vec<SyncCommitteeContribution<T>>>>;
|
||||||
@ -51,6 +51,7 @@ pub struct OperationPool<T: EthSpec + Default> {
|
|||||||
/// Map from exiting validator to their exit data.
|
/// Map from exiting validator to their exit data.
|
||||||
voluntary_exits: RwLock<HashMap<u64, SigVerifiedOp<SignedVoluntaryExit, T>>>,
|
voluntary_exits: RwLock<HashMap<u64, SigVerifiedOp<SignedVoluntaryExit, T>>>,
|
||||||
/// Map from credential changing validator to their execution change data.
|
/// Map from credential changing validator to their execution change data.
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
bls_to_execution_changes: RwLock<HashMap<u64, SigVerifiedOp<SignedBlsToExecutionChange, T>>>,
|
bls_to_execution_changes: RwLock<HashMap<u64, SigVerifiedOp<SignedBlsToExecutionChange, T>>>,
|
||||||
/// Reward cache for accelerating attestation packing.
|
/// Reward cache for accelerating attestation packing.
|
||||||
reward_cache: RwLock<RewardCache>,
|
reward_cache: RwLock<RewardCache>,
|
||||||
@ -432,7 +433,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
pub fn prune_proposer_slashings(&self, head_state: &BeaconState<T>) {
|
pub fn prune_proposer_slashings(&self, head_state: &BeaconState<T>) {
|
||||||
prune_validator_hash_map(
|
prune_validator_hash_map(
|
||||||
&mut self.proposer_slashings.write(),
|
&mut self.proposer_slashings.write(),
|
||||||
|validator| validator.exit_epoch <= head_state.finalized_checkpoint().epoch,
|
|_, validator| validator.exit_epoch <= head_state.finalized_checkpoint().epoch,
|
||||||
head_state,
|
head_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -507,28 +508,115 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
//
|
//
|
||||||
// We choose simplicity over the gain of pruning more exits since they are small and
|
// We choose simplicity over the gain of pruning more exits since they are small and
|
||||||
// should not be seen frequently.
|
// should not be seen frequently.
|
||||||
|validator| validator.exit_epoch <= head_state.finalized_checkpoint().epoch,
|
|_, validator| validator.exit_epoch <= head_state.finalized_checkpoint().epoch,
|
||||||
head_state,
|
head_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Insert a BLS to execution change into the pool.
|
||||||
|
pub fn insert_bls_to_execution_change(
|
||||||
|
&self,
|
||||||
|
verified_change: SigVerifiedOp<SignedBlsToExecutionChange, T>,
|
||||||
|
) {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
{
|
||||||
|
self.bls_to_execution_changes.write().insert(
|
||||||
|
verified_change.as_inner().message.validator_index,
|
||||||
|
verified_change,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
{
|
||||||
|
drop(verified_change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a list of execution changes for inclusion in a block.
|
/// Get a list of execution changes for inclusion in a block.
|
||||||
|
///
|
||||||
|
/// They're in random `HashMap` order, which isn't exactly fair, but isn't unfair either.
|
||||||
pub fn get_bls_to_execution_changes(
|
pub fn get_bls_to_execution_changes(
|
||||||
&self,
|
&self,
|
||||||
state: &BeaconState<T>,
|
state: &BeaconState<T>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Vec<SignedBlsToExecutionChange> {
|
) -> Vec<SignedBlsToExecutionChange> {
|
||||||
// FIXME: actually implement this
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
return vec![];
|
{
|
||||||
|
filter_limit_operations(
|
||||||
|
self.bls_to_execution_changes.read().values(),
|
||||||
|
|address_change| {
|
||||||
|
address_change.signature_is_still_valid(&state.fork())
|
||||||
|
&& state
|
||||||
|
.get_validator(
|
||||||
|
address_change.as_inner().message.validator_index as usize,
|
||||||
|
)
|
||||||
|
.map_or(false, |validator| {
|
||||||
|
!validator.has_eth1_withdrawal_credential(spec)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|address_change| address_change.as_inner().clone(),
|
||||||
|
T::MaxBlsToExecutionChanges::to_usize(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
{
|
||||||
|
drop((state, spec));
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
) {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
{
|
||||||
|
drop((head_block, head_state, spec));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prune all types of transactions given the latest head state and head fork.
|
/// Prune all types of transactions given the latest head state and head fork.
|
||||||
pub fn prune_all(&self, head_state: &BeaconState<T>, current_epoch: Epoch) {
|
pub fn prune_all<Payload: AbstractExecPayload<T>>(
|
||||||
|
&self,
|
||||||
|
head_block: &SignedBeaconBlock<T, Payload>,
|
||||||
|
head_state: &BeaconState<T>,
|
||||||
|
current_epoch: Epoch,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) {
|
||||||
self.prune_attestations(current_epoch);
|
self.prune_attestations(current_epoch);
|
||||||
self.prune_sync_contributions(head_state.slot());
|
self.prune_sync_contributions(head_state.slot());
|
||||||
self.prune_proposer_slashings(head_state);
|
self.prune_proposer_slashings(head_state);
|
||||||
self.prune_attester_slashings(head_state);
|
self.prune_attester_slashings(head_state);
|
||||||
self.prune_voluntary_exits(head_state);
|
self.prune_voluntary_exits(head_state);
|
||||||
|
self.prune_bls_to_execution_changes(head_block, head_state, spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Total number of voluntary exits in the pool.
|
/// Total number of voluntary exits in the pool.
|
||||||
@ -594,6 +682,23 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
.map(|(_, exit)| exit.as_inner().clone())
|
.map(|(_, exit)| exit.as_inner().clone())
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns all known `SignedBlsToExecutionChange` objects.
|
||||||
|
///
|
||||||
|
/// This method may return objects that are invalid for block inclusion.
|
||||||
|
pub fn get_all_bls_to_execution_changes(&self) -> Vec<SignedBlsToExecutionChange> {
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
{
|
||||||
|
self.bls_to_execution_changes
|
||||||
|
.read()
|
||||||
|
.iter()
|
||||||
|
.map(|(_, address_change)| address_change.as_inner().clone())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "withdrawals-processing"))]
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Filter up to a maximum number of operations out of an iterator.
|
/// Filter up to a maximum number of operations out of an iterator.
|
||||||
@ -627,7 +732,7 @@ fn prune_validator_hash_map<T, F, E: EthSpec>(
|
|||||||
prune_if: F,
|
prune_if: F,
|
||||||
head_state: &BeaconState<E>,
|
head_state: &BeaconState<E>,
|
||||||
) where
|
) where
|
||||||
F: Fn(&Validator) -> bool,
|
F: Fn(u64, &Validator) -> bool,
|
||||||
T: VerifyOperation<E>,
|
T: VerifyOperation<E>,
|
||||||
{
|
{
|
||||||
map.retain(|&validator_index, op| {
|
map.retain(|&validator_index, op| {
|
||||||
@ -635,7 +740,7 @@ fn prune_validator_hash_map<T, F, E: EthSpec>(
|
|||||||
&& head_state
|
&& head_state
|
||||||
.validators()
|
.validators()
|
||||||
.get(validator_index as usize)
|
.get(validator_index as usize)
|
||||||
.map_or(true, |validator| !prune_if(validator))
|
.map_or(true, |validator| !prune_if(validator_index, validator))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,8 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
|||||||
attester_slashings,
|
attester_slashings,
|
||||||
proposer_slashings,
|
proposer_slashings,
|
||||||
voluntary_exits,
|
voluntary_exits,
|
||||||
// FIXME: IMPLEMENT THIS
|
// FIXME(capella): implement schema migration for address changes in op pool
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
bls_to_execution_changes: Default::default(),
|
bls_to_execution_changes: Default::default(),
|
||||||
reward_cache: Default::default(),
|
reward_cache: Default::default(),
|
||||||
_phantom: Default::default(),
|
_phantom: Default::default(),
|
||||||
|
@ -158,6 +158,8 @@ where
|
|||||||
// Deposits are not included because they can legally have invalid signatures.
|
// Deposits are not included because they can legally have invalid signatures.
|
||||||
self.include_exits(block)?;
|
self.include_exits(block)?;
|
||||||
self.include_sync_aggregate(block)?;
|
self.include_sync_aggregate(block)?;
|
||||||
|
#[cfg(feature = "withdrawals")]
|
||||||
|
self.include_bls_to_execution_changes(block)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -339,6 +341,26 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Include the signature of the block's BLS to execution changes for verification.
|
||||||
|
#[cfg(feature = "withdrawals")]
|
||||||
|
pub fn include_bls_to_execution_changes<Payload: AbstractExecPayload<T>>(
|
||||||
|
&mut self,
|
||||||
|
block: &'a SignedBeaconBlock<T, Payload>,
|
||||||
|
) -> Result<()> {
|
||||||
|
// FIXME(capella): to improve performance we might want to decompress the withdrawal pubkeys
|
||||||
|
// in parallel.
|
||||||
|
if let Ok(bls_to_execution_changes) = block.message().body().bls_to_execution_changes() {
|
||||||
|
for bls_to_execution_change in bls_to_execution_changes {
|
||||||
|
self.sets.push(bls_execution_change_signature_set(
|
||||||
|
self.state,
|
||||||
|
bls_to_execution_change,
|
||||||
|
self.spec,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Verify all the signatures that have been included in `self`, returning `true` if and only if
|
/// Verify all the signatures that have been included in `self`, returning `true` if and only if
|
||||||
/// all the signatures are valid.
|
/// all the signatures are valid.
|
||||||
///
|
///
|
||||||
|
@ -15,6 +15,14 @@ use types::{
|
|||||||
SignedVoluntaryExit,
|
SignedVoluntaryExit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
use {
|
||||||
|
crate::per_block_processing::{
|
||||||
|
errors::BlsExecutionChangeValidationError, verify_bls_to_execution_change,
|
||||||
|
},
|
||||||
|
types::SignedBlsToExecutionChange,
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_FORKS_VERIFIED_AGAINST: usize = 2;
|
const MAX_FORKS_VERIFIED_AGAINST: usize = 2;
|
||||||
|
|
||||||
/// Wrapper around an operation type that acts as proof that its signature has been checked.
|
/// Wrapper around an operation type that acts as proof that its signature has been checked.
|
||||||
@ -65,7 +73,7 @@ where
|
|||||||
fn new(op: T, state: &BeaconState<E>) -> Self {
|
fn new(op: T, state: &BeaconState<E>) -> Self {
|
||||||
let verified_against = VerifiedAgainst {
|
let verified_against = VerifiedAgainst {
|
||||||
fork_versions: op
|
fork_versions: op
|
||||||
.verification_epochs()
|
.verification_epochs(state.current_epoch())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|epoch| state.fork().get_fork_version(epoch))
|
.map(|epoch| state.fork().get_fork_version(epoch))
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -87,8 +95,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn signature_is_still_valid(&self, current_fork: &Fork) -> bool {
|
pub fn signature_is_still_valid(&self, current_fork: &Fork) -> bool {
|
||||||
|
// Pass the fork's epoch as the effective current epoch. If the message is a current-epoch
|
||||||
|
// style message like `SignedBlsToExecutionChange` then `get_fork_version` will return the
|
||||||
|
// current fork version and we'll check it matches the fork version the message was checked
|
||||||
|
// against.
|
||||||
|
let effective_current_epoch = current_fork.epoch;
|
||||||
self.as_inner()
|
self.as_inner()
|
||||||
.verification_epochs()
|
.verification_epochs(effective_current_epoch)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.verified_against.fork_versions.iter())
|
.zip(self.verified_against.fork_versions.iter())
|
||||||
.all(|(epoch, verified_fork_version)| {
|
.all(|(epoch, verified_fork_version)| {
|
||||||
@ -118,7 +131,13 @@ pub trait VerifyOperation<E: EthSpec>: Encode + Decode + Sized {
|
|||||||
/// Return the epochs at which parts of this message were verified.
|
/// Return the epochs at which parts of this message were verified.
|
||||||
///
|
///
|
||||||
/// These need to map 1-to-1 to the `SigVerifiedOp::verified_against` for this type.
|
/// These need to map 1-to-1 to the `SigVerifiedOp::verified_against` for this type.
|
||||||
fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]>;
|
///
|
||||||
|
/// If the message contains no inherent epoch it should return the `current_epoch` that is
|
||||||
|
/// passed in, as that's the epoch at which it was verified.
|
||||||
|
fn verification_epochs(
|
||||||
|
&self,
|
||||||
|
current_epoch: Epoch,
|
||||||
|
) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: EthSpec> VerifyOperation<E> for SignedVoluntaryExit {
|
impl<E: EthSpec> VerifyOperation<E> for SignedVoluntaryExit {
|
||||||
@ -134,7 +153,7 @@ impl<E: EthSpec> VerifyOperation<E> for SignedVoluntaryExit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::integer_arithmetic)]
|
#[allow(clippy::integer_arithmetic)]
|
||||||
fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
fn verification_epochs(&self, _: Epoch) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
||||||
smallvec![self.message.epoch]
|
smallvec![self.message.epoch]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +171,7 @@ impl<E: EthSpec> VerifyOperation<E> for AttesterSlashing<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::integer_arithmetic)]
|
#[allow(clippy::integer_arithmetic)]
|
||||||
fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
fn verification_epochs(&self, _: Epoch) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
||||||
smallvec![
|
smallvec![
|
||||||
self.attestation_1.data.target.epoch,
|
self.attestation_1.data.target.epoch,
|
||||||
self.attestation_2.data.target.epoch
|
self.attestation_2.data.target.epoch
|
||||||
@ -173,7 +192,7 @@ impl<E: EthSpec> VerifyOperation<E> for ProposerSlashing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::integer_arithmetic)]
|
#[allow(clippy::integer_arithmetic)]
|
||||||
fn verification_epochs(&self) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
fn verification_epochs(&self, _: Epoch) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
||||||
// Only need a single epoch because the slots of the two headers must be equal.
|
// Only need a single epoch because the slots of the two headers must be equal.
|
||||||
smallvec![self
|
smallvec![self
|
||||||
.signed_header_1
|
.signed_header_1
|
||||||
@ -182,3 +201,25 @@ impl<E: EthSpec> VerifyOperation<E> for ProposerSlashing {
|
|||||||
.epoch(E::slots_per_epoch())]
|
.epoch(E::slots_per_epoch())]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "withdrawals-processing")]
|
||||||
|
impl<E: EthSpec> VerifyOperation<E> for SignedBlsToExecutionChange {
|
||||||
|
type Error = BlsExecutionChangeValidationError;
|
||||||
|
|
||||||
|
fn validate(
|
||||||
|
self,
|
||||||
|
state: &BeaconState<E>,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<SigVerifiedOp<Self, E>, Self::Error> {
|
||||||
|
verify_bls_to_execution_change(state, &self, VerifySignatures::True, spec)?;
|
||||||
|
Ok(SigVerifiedOp::new(self, state))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::integer_arithmetic)]
|
||||||
|
fn verification_epochs(
|
||||||
|
&self,
|
||||||
|
current_epoch: Epoch,
|
||||||
|
) -> SmallVec<[Epoch; MAX_FORKS_VERIFIED_AGAINST]> {
|
||||||
|
smallvec![current_epoch]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,8 +7,6 @@ use test_random_derive::TestRandom;
|
|||||||
use tree_hash_derive::TreeHash;
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
/// A deposit to potentially become a beacon chain validator.
|
/// A deposit to potentially become a beacon chain validator.
|
||||||
///
|
|
||||||
/// Spec v0.12.1
|
|
||||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||||
|
@ -7,8 +7,6 @@ use test_random_derive::TestRandom;
|
|||||||
use tree_hash_derive::TreeHash;
|
use tree_hash_derive::TreeHash;
|
||||||
|
|
||||||
/// A deposit to potentially become a beacon chain validator.
|
/// A deposit to potentially become a beacon chain validator.
|
||||||
///
|
|
||||||
/// Spec v0.12.1
|
|
||||||
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
TESTS_TAG := f5c7cf78
|
TESTS_TAG := v1.3.0-alpha.1
|
||||||
TESTS = general minimal mainnet
|
TESTS = general minimal mainnet
|
||||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user