mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-19 21:04:41 +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:
@@ -2227,32 +2227,74 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
|
||||
/// Verify a signed BLS to execution change before allowing it to propagate on the gossip network.
|
||||
pub fn verify_bls_to_execution_change_for_gossip(
|
||||
pub fn verify_bls_to_execution_change_for_http_api(
|
||||
&self,
|
||||
bls_to_execution_change: SignedBlsToExecutionChange,
|
||||
) -> Result<ObservationOutcome<SignedBlsToExecutionChange, T::EthSpec>, Error> {
|
||||
let current_fork = self.spec.fork_name_at_slot::<T::EthSpec>(self.slot()?);
|
||||
if let ForkName::Base | ForkName::Altair | ForkName::Merge = current_fork {
|
||||
// Disallow BLS to execution changes prior to the Capella fork.
|
||||
return Err(Error::BlsToExecutionChangeBadFork(current_fork));
|
||||
// Before checking the gossip duplicate filter, check that no prior change is already
|
||||
// in our op pool. Ignore these messages: do not gossip, do not try to override the pool.
|
||||
match self
|
||||
.op_pool
|
||||
.bls_to_execution_change_in_pool_equals(&bls_to_execution_change)
|
||||
{
|
||||
Some(true) => return Ok(ObservationOutcome::AlreadyKnown),
|
||||
Some(false) => return Err(Error::BlsToExecutionConflictsWithPool),
|
||||
None => (),
|
||||
}
|
||||
|
||||
let wall_clock_state = self.wall_clock_state()?;
|
||||
// Use the head state to save advancing to the wall-clock slot unnecessarily. The message is
|
||||
// signed with respect to the genesis fork version, and the slot check for gossip is applied
|
||||
// separately. This `Arc` clone of the head is nice and cheap.
|
||||
let head_snapshot = self.head().snapshot;
|
||||
let head_state = &head_snapshot.beacon_state;
|
||||
|
||||
Ok(self
|
||||
.observed_bls_to_execution_changes
|
||||
.lock()
|
||||
.verify_and_observe(bls_to_execution_change, &wall_clock_state, &self.spec)?)
|
||||
.verify_and_observe(bls_to_execution_change, head_state, &self.spec)?)
|
||||
}
|
||||
|
||||
/// Verify a signed BLS to execution 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> {
|
||||
// Ignore BLS to execution changes on gossip prior to Capella.
|
||||
if !self.current_slot_is_post_capella()? {
|
||||
return Err(Error::BlsToExecutionPriorToCapella);
|
||||
}
|
||||
self.verify_bls_to_execution_change_for_http_api(bls_to_execution_change)
|
||||
.or_else(|e| {
|
||||
// On gossip treat conflicts the same as duplicates [IGNORE].
|
||||
match e {
|
||||
Error::BlsToExecutionConflictsWithPool => Ok(ObservationOutcome::AlreadyKnown),
|
||||
e => Err(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the current slot is greater than or equal to the Capella fork epoch.
|
||||
pub fn current_slot_is_post_capella(&self) -> Result<bool, Error> {
|
||||
let current_fork = self.spec.fork_name_at_slot::<T::EthSpec>(self.slot()?);
|
||||
if let ForkName::Base | ForkName::Altair | ForkName::Merge = current_fork {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Import a BLS to execution change to the op pool.
|
||||
///
|
||||
/// Return `true` if the change was added to the pool.
|
||||
pub fn import_bls_to_execution_change(
|
||||
&self,
|
||||
bls_to_execution_change: SigVerifiedOp<SignedBlsToExecutionChange, T::EthSpec>,
|
||||
) {
|
||||
) -> bool {
|
||||
if self.eth1_chain.is_some() {
|
||||
self.op_pool
|
||||
.insert_bls_to_execution_change(bls_to_execution_change);
|
||||
.insert_bls_to_execution_change(bls_to_execution_change)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -206,7 +206,8 @@ pub enum BeaconChainError {
|
||||
MissingPersistedForkChoice,
|
||||
CommitteePromiseFailed(oneshot_broadcast::Error),
|
||||
MaxCommitteePromises(usize),
|
||||
BlsToExecutionChangeBadFork(ForkName),
|
||||
BlsToExecutionPriorToCapella,
|
||||
BlsToExecutionConflictsWithPool,
|
||||
InconsistentFork(InconsistentFork),
|
||||
ProposerHeadForkChoiceError(fork_choice::Error<proto_array::Error>),
|
||||
}
|
||||
|
||||
@@ -148,6 +148,7 @@ pub struct Builder<T: BeaconChainTypes> {
|
||||
eth_spec_instance: T::EthSpec,
|
||||
spec: Option<ChainSpec>,
|
||||
validator_keypairs: Option<Vec<Keypair>>,
|
||||
withdrawal_keypairs: Vec<Option<Keypair>>,
|
||||
chain_config: Option<ChainConfig>,
|
||||
store_config: Option<StoreConfig>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
@@ -170,6 +171,17 @@ impl<E: EthSpec> Builder<EphemeralHarnessType<E>> {
|
||||
.clone()
|
||||
.expect("cannot build without validator keypairs");
|
||||
|
||||
// For the interop genesis state we know that the withdrawal credentials are set equal
|
||||
// to the validator keypairs. Check for any manually initialised credentials.
|
||||
assert!(
|
||||
self.withdrawal_keypairs.is_empty(),
|
||||
"withdrawal credentials are ignored by fresh_ephemeral_store"
|
||||
);
|
||||
self.withdrawal_keypairs = validator_keypairs
|
||||
.iter()
|
||||
.map(|kp| Some(kp.clone()))
|
||||
.collect();
|
||||
|
||||
let store = Arc::new(
|
||||
HotColdDB::open_ephemeral(
|
||||
self.store_config.clone().unwrap_or_default(),
|
||||
@@ -282,6 +294,7 @@ where
|
||||
eth_spec_instance,
|
||||
spec: None,
|
||||
validator_keypairs: None,
|
||||
withdrawal_keypairs: vec![],
|
||||
chain_config: None,
|
||||
store_config: None,
|
||||
store: None,
|
||||
@@ -539,6 +552,7 @@ where
|
||||
spec: chain.spec.clone(),
|
||||
chain: Arc::new(chain),
|
||||
validator_keypairs,
|
||||
withdrawal_keypairs: self.withdrawal_keypairs,
|
||||
shutdown_receiver: Arc::new(Mutex::new(shutdown_receiver)),
|
||||
runtime: self.runtime,
|
||||
mock_execution_layer: self.mock_execution_layer,
|
||||
@@ -554,6 +568,12 @@ where
|
||||
/// Used for testing.
|
||||
pub struct BeaconChainHarness<T: BeaconChainTypes> {
|
||||
pub validator_keypairs: Vec<Keypair>,
|
||||
/// Optional BLS withdrawal keys for each validator.
|
||||
///
|
||||
/// If a validator index is missing from this vec or their entry is `None` then either
|
||||
/// no BLS withdrawal key was set for them (they had an address from genesis) or the test
|
||||
/// initializer neglected to set this field.
|
||||
pub withdrawal_keypairs: Vec<Option<Keypair>>,
|
||||
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
pub spec: ChainSpec,
|
||||
@@ -1465,6 +1485,44 @@ where
|
||||
.sign(sk, &fork, genesis_validators_root, &self.chain.spec)
|
||||
}
|
||||
|
||||
pub fn make_bls_to_execution_change(
|
||||
&self,
|
||||
validator_index: u64,
|
||||
address: Address,
|
||||
) -> SignedBlsToExecutionChange {
|
||||
let keypair = self.get_withdrawal_keypair(validator_index);
|
||||
self.make_bls_to_execution_change_with_keys(
|
||||
validator_index,
|
||||
address,
|
||||
&keypair.pk,
|
||||
&keypair.sk,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn make_bls_to_execution_change_with_keys(
|
||||
&self,
|
||||
validator_index: u64,
|
||||
address: Address,
|
||||
pubkey: &PublicKey,
|
||||
secret_key: &SecretKey,
|
||||
) -> SignedBlsToExecutionChange {
|
||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||
BlsToExecutionChange {
|
||||
validator_index,
|
||||
from_bls_pubkey: pubkey.compress(),
|
||||
to_execution_address: address,
|
||||
}
|
||||
.sign(secret_key, genesis_validators_root, &self.chain.spec)
|
||||
}
|
||||
|
||||
pub fn get_withdrawal_keypair(&self, validator_index: u64) -> &Keypair {
|
||||
self.withdrawal_keypairs
|
||||
.get(validator_index as usize)
|
||||
.expect("BLS withdrawal key missing from harness")
|
||||
.as_ref()
|
||||
.expect("no withdrawal key for validator")
|
||||
}
|
||||
|
||||
pub fn add_voluntary_exit(
|
||||
&self,
|
||||
block: &mut BeaconBlock<E>,
|
||||
|
||||
Reference in New Issue
Block a user