Compare commits

...

4 Commits

Author SHA1 Message Date
Michael Sproul
1030d2e203 Trying more stuff 2025-03-29 12:52:56 +11:00
Michael Sproul
5c3e7a44cd More progress 2025-03-28 09:06:22 +11:00
Michael Sproul
eb032bc5c6 Progress 2025-03-28 08:30:36 +11:00
Michael Sproul
296e50b0d1 WIP 2025-03-27 23:23:25 +11:00
3 changed files with 316 additions and 13 deletions

View File

@@ -2228,6 +2228,23 @@ where
((Arc::new(signed_block), blobs), state) ((Arc::new(signed_block), blobs), state)
} }
pub fn make_deposit_data(
&self,
keypair: &Keypair,
withdrawal_credentials: Hash256,
amount: u64,
) -> DepositData {
let pubkey = PublicKeyBytes::from(keypair.pk.clone());
let mut data = DepositData {
pubkey,
withdrawal_credentials,
amount,
signature: SignatureBytes::empty(),
};
data.signature = data.create_signature(&keypair.sk, &self.spec);
data
}
pub fn make_deposits<'a>( pub fn make_deposits<'a>(
&self, &self,
state: &'a mut BeaconState<E>, state: &'a mut BeaconState<E>,
@@ -2239,19 +2256,14 @@ where
for _ in 0..num_deposits { for _ in 0..num_deposits {
let keypair = Keypair::random(); let keypair = Keypair::random();
let pubkeybytes = PublicKeyBytes::from(keypair.pk.clone()); let withdrawal_credentials = Hash256::from_slice(
&get_withdrawal_credentials(&keypair.pk, self.spec.bls_withdrawal_prefix_byte)[..],
let mut data = DepositData { );
pubkey: pubkeybytes, let mut data = self.make_deposit_data(
withdrawal_credentials: Hash256::from_slice( &keypair,
&get_withdrawal_credentials(&keypair.pk, self.spec.bls_withdrawal_prefix_byte) withdrawal_credentials,
[..], self.spec.min_deposit_amount,
), );
amount: self.spec.min_deposit_amount,
signature: SignatureBytes::empty(),
};
data.signature = data.create_signature(&keypair.sk, &self.spec);
if let Some(invalid_pubkey) = invalid_pubkey { if let Some(invalid_pubkey) = invalid_pubkey {
data.pubkey = invalid_pubkey; data.pubkey = invalid_pubkey;

View File

@@ -0,0 +1,290 @@
#![cfg(not(debug_assertions))] // Tests run too slow in debug.
use beacon_chain::{
builder::BeaconChainBuilder,
test_utils::{get_kzg, mock_execution_layer_from_parts, BeaconChainHarness, DiskHarnessType},
ChainConfig, MigratorConfig, NotifyExecutionLayer, StateSkipConfig,
};
use logging::test_logger;
use slog::debug;
use slot_clock::{SlotClock, TestingSlotClock};
use state_processing::{
per_block_processing, BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot,
};
use std::sync::Arc;
use std::time::Duration;
use store::{database::interface::BeaconNodeBackend, HotColdDB, StoreConfig};
use tempfile::{tempdir, TempDir};
use types::*;
type E = MainnetEthSpec;
fn get_store(
db_path: &TempDir,
config: StoreConfig,
spec: Arc<ChainSpec>,
) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
let hot_path = db_path.path().join("chain_db");
let cold_path = db_path.path().join("freezer_db");
let blobs_path = db_path.path().join("blobs_db");
let log = test_logger();
HotColdDB::open(
&hot_path,
&cold_path,
&blobs_path,
|_, _, _| Ok(()),
config,
spec.into(),
log,
)
.expect("disk store should initialize")
}
#[tokio::test]
async fn signature_verify_chain_segment_pubkey_cache() {
let initial_validator_count = 32;
let deposit_slot = Slot::new(4 * E::slots_per_epoch() - 1);
let pre_deposit_slot = deposit_slot - 1;
let spec = Arc::new(ForkName::Electra.make_genesis_spec(E::default_spec()));
// Keep historic states on main harness.
let chain_config = ChainConfig {
reconstruct_historic_states: true,
..ChainConfig::default()
};
let harness = BeaconChainHarness::builder(E::default())
.chain_config(chain_config)
.spec(spec.clone())
.logger(logging::test_logger())
.deterministic_keypairs(initial_validator_count)
.fresh_ephemeral_store()
.mock_execution_layer()
.build();
harness
.execution_block_generator()
.move_to_terminal_block()
.unwrap();
Box::pin(harness.extend_to_slot(pre_deposit_slot)).await;
// Create a block with a deposit for a new validator.
let pre_deposit_state = harness.get_current_state();
assert_eq!(pre_deposit_state.slot(), pre_deposit_slot);
assert_eq!(pre_deposit_state.fork_name_unchecked(), ForkName::Electra);
// FIXME: Probably need to make this deterministic?
let new_keypair = Keypair::random();
let new_validator_pk_bytes = PublicKeyBytes::from(&new_keypair.pk);
let withdrawal_credentials = Hash256::ZERO;
let amount = spec.min_per_epoch_churn_limit_electra;
let deposit_data = harness.make_deposit_data(&new_keypair, withdrawal_credentials, amount);
let deposit_request = DepositRequest {
pubkey: deposit_data.pubkey,
withdrawal_credentials: deposit_data.withdrawal_credentials,
amount: deposit_data.amount,
signature: deposit_data.signature,
index: 0,
};
let ((jank_block, blobs), mut state) = harness
.make_block_with_modifier(pre_deposit_state, deposit_slot, |block| {
block
.body_mut()
.execution_requests_mut()
.unwrap()
.deposits
.push(deposit_request)
.unwrap();
})
.await;
// Compute correct state root.
// FIXME: this is kinda nasty
let mut ctxt = ConsensusContext::new(jank_block.slot());
per_block_processing(
&mut state,
&jank_block,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&mut ctxt,
&spec,
)
.unwrap();
let (mut block, _) = (*jank_block).clone().deconstruct();
*block.state_root_mut() = state.update_tree_hash_cache().unwrap();
let proposer_index = block.proposer_index() as usize;
let signed_block = Arc::new(block.sign(
&harness.validator_keypairs[proposer_index].sk,
&state.fork(),
state.genesis_validators_root(),
&spec,
));
let block_root = signed_block.canonical_root();
let block_contents = (signed_block, blobs);
harness
.process_block(deposit_slot, block_root, block_contents)
.await
.unwrap();
let post_block_state = harness.get_current_state();
assert_eq!(post_block_state.pending_deposits().unwrap().len(), 1);
assert_eq!(post_block_state.validators().len(), initial_validator_count);
// Advance to one slot before the finalization of the deposit.
Box::pin(harness.extend_to_slot(deposit_slot + 2 * E::slots_per_epoch())).await;
let pre_finalized_deposit_state = harness.get_current_state();
assert_eq!(
pre_finalized_deposit_state.validators().len(),
initial_validator_count
);
let new_epoch_start_slot = pre_finalized_deposit_state.slot() + 3 * E::slots_per_epoch() + 1;
// New validator should not be in the pubkey cache yet.
assert_eq!(
harness
.chain
.validator_index(&new_validator_pk_bytes)
.unwrap(),
None
);
let new_validator_index = initial_validator_count;
// Produce blocks in the next 3 epochs. Statistically one of these should be signed by our new
// validator (99% probability).
harness.extend_to_slot(new_epoch_start_slot).await;
// New validator should be in the pubkey cache now.
assert_eq!(
harness
.chain
.validator_index(&new_validator_pk_bytes)
.unwrap(),
Some(new_validator_index)
);
// Initialise a new harness using checkpoint sync, prior to the new deposit being finalized.
let datadir = tempdir().unwrap();
let store = get_store(&datadir, Default::default(), spec.clone());
let kzg = get_kzg(&spec);
let mock = mock_execution_layer_from_parts(
harness.spec.clone(),
harness.runtime.task_executor.clone(),
);
// Initialise a new beacon chain from the finalized checkpoint.
// The slot clock must be set to a time ahead of the checkpoint state.
let slot_clock = TestingSlotClock::new(
Slot::new(0),
Duration::from_secs(harness.chain.genesis_time),
Duration::from_secs(spec.seconds_per_slot),
);
slot_clock.set_slot(harness.get_current_slot().as_u64());
let checkpoint_slot = deposit_slot
.epoch(E::slots_per_epoch())
.start_slot(E::slots_per_epoch());
let mut checkpoint_state = harness
.chain
.state_at_slot(checkpoint_slot, StateSkipConfig::WithStateRoots)
.unwrap();
let checkpoint_state_root = checkpoint_state.update_tree_hash_cache().unwrap();
let checkpoint_block_root = checkpoint_state.get_latest_block_root(checkpoint_state_root);
let checkpoint_block = harness
.chain
.get_block(&checkpoint_block_root)
.await
.unwrap()
.unwrap();
let checkpoint_blobs_opt = harness
.chain
.get_or_reconstruct_blobs(&checkpoint_block_root)
.unwrap();
let genesis_state = harness
.chain
.state_at_slot(Slot::new(0), StateSkipConfig::WithStateRoots)
.unwrap();
let (shutdown_tx, _shutdown_rx) = futures::channel::mpsc::channel(1);
let log = harness.runtime.log.clone();
let beacon_chain = Arc::new(
BeaconChainBuilder::<DiskHarnessType<E>>::new(MainnetEthSpec, kzg)
.store(store.clone())
.custom_spec(spec.clone())
.task_executor(harness.chain.task_executor.clone())
.logger(harness.runtime.log.clone())
.weak_subjectivity_state(
checkpoint_state,
checkpoint_block.clone(),
checkpoint_blobs_opt.clone(),
genesis_state,
)
.unwrap()
.shutdown_sender(shutdown_tx)
.store_migrator_config(MigratorConfig::default().blocking())
.dummy_eth1_backend()
.expect("should build dummy backend")
.slot_clock(slot_clock)
.chain_config(ChainConfig::default())
.execution_layer(Some(mock.el))
.build()
.expect("should build"),
);
let chain_dump = harness.chain.chain_dump().unwrap();
let new_blocks = chain_dump
.iter()
.filter(|snapshot| snapshot.beacon_block.slot() > checkpoint_slot);
let mut chain_segment = vec![];
let mut new_proposer_present = false;
for snapshot in new_blocks {
let block_root = snapshot.beacon_block_root;
let full_block = harness.chain.get_block(&block_root).await.unwrap().unwrap();
new_proposer_present |= full_block.message().proposer_index() == new_validator_index as u64;
println!(
"Proposal from validator {} at slot {}",
full_block.message().proposer_index(),
full_block.slot()
);
chain_segment
.push(harness.build_rpc_block_from_store_blobs(Some(block_root), Arc::new(full_block)));
}
assert_ne!(chain_segment.len(), 0);
// This should succeed despite the new validator index being unknown to the checkpoint synced
// chain.
/*
assert!(
new_proposer_present,
"new proposer should be part of chain segment"
);
*/
assert_eq!(
beacon_chain
.validator_index(&new_validator_pk_bytes)
.unwrap(),
None,
);
beacon_chain
.process_chain_segment(chain_segment, NotifyExecutionLayer::Yes)
.await
.into_block_error()
.unwrap();
// Processing the chain segment should add the new validator to the cache.
assert_eq!(
beacon_chain
.validator_index(&new_validator_pk_bytes)
.unwrap(),
Some(new_validator_index),
);
}

View File

@@ -3,6 +3,7 @@ mod attestation_verification;
mod bellatrix; mod bellatrix;
mod block_verification; mod block_verification;
mod capella; mod capella;
mod electra;
mod events; mod events;
mod op_verification; mod op_verification;
mod payload_invalidation; mod payload_invalidation;