diff --git a/beacon_node/beacon_chain/test_harness/examples/chain.yaml b/beacon_node/beacon_chain/test_harness/examples/chain.yaml index 919753afa..036871aeb 100644 --- a/beacon_node/beacon_chain/test_harness/examples/chain.yaml +++ b/beacon_node/beacon_chain/test_harness/examples/chain.yaml @@ -19,6 +19,17 @@ test_cases: - slot: 5 amount: 32 merkle_index: 2 + proposer_slashings: + - slot: 8 # At slot 8, we trigger a proposal slashing occurring + proposer_index: 42 # We penalize the validator at position 42 + proposal_1_shard: 0 + proposal_1_slot: 15 + proposal_1_root: !!binary | + LkmqmqoodLKAslkjdkajsdljasdkajlksjdasldjasdd + proposal_2_shard: 0 + proposal_2_slot: 15 + proposal_2_root: !!binary | + DIFFERENTKAslkjdkajsdljasdkajlksjdasldjasdd results: num_validators: 1000 - config: diff --git a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs index b60454b57..43b60d506 100644 --- a/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs +++ b/beacon_node/beacon_chain/test_harness/src/beacon_chain_harness.rs @@ -10,6 +10,7 @@ use fork_choice::BitwiseLMDGhost; use log::debug; use rayon::prelude::*; use slot_clock::TestingSlotClock; +use ssz::TreeHash; use std::collections::HashSet; use std::fs::File; use std::io::prelude::*; @@ -242,7 +243,7 @@ impl BeaconChainHarness { debug!("Free attestations processed."); } - pub fn process_deposit(&mut self, deposit: Deposit, keypair: Option) { + pub fn add_deposit(&mut self, deposit: Deposit, keypair: Option) { self.beacon_chain.receive_deposit_for_inclusion(deposit); // If a keypair is present, add a new `ValidatorHarness` to the rig. @@ -253,6 +254,36 @@ impl BeaconChainHarness { } } + pub fn add_proposer_slashing(&mut self, mut proposer_slashing: ProposerSlashing) { + let validator = &self.validators[proposer_slashing.proposer_index as usize]; + + // This following code is a little awkward, but managing the data_1 and data_1 was getting + // rather confusing. I think this is better + let proposals = vec![ + &proposer_slashing.proposal_data_1, + &proposer_slashing.proposal_data_2, + ]; + let signatures: Vec = proposals + .iter() + .map(|proposal_data| { + let message = proposal_data.hash_tree_root(); + let epoch = proposal_data.slot.epoch(self.spec.epoch_length); + let domain = self + .beacon_chain + .state + .read() + .fork + .get_domain(epoch, self.spec.domain_proposal); + Signature::new(&message[..], domain, &validator.keypair.sk) + }) + .collect(); + proposer_slashing.proposal_signature_1 = signatures[0].clone(); + proposer_slashing.proposal_signature_2 = signatures[1].clone(); + + self.beacon_chain + .receive_proposer_slashing_for_inclusion(proposer_slashing); + } + pub fn run_fork_choice(&mut self) { self.beacon_chain.fork_choice().unwrap() } diff --git a/beacon_node/beacon_chain/test_harness/src/bin.rs b/beacon_node/beacon_chain/test_harness/src/bin.rs index f747be60a..e905e9342 100644 --- a/beacon_node/beacon_chain/test_harness/src/bin.rs +++ b/beacon_node/beacon_chain/test_harness/src/bin.rs @@ -85,12 +85,25 @@ impl Manifest { info!("Starting simulation across {} slots...", slots); for slot_height in 0..slots { - // Include deposits + // Feed deposits to the BeaconChain. if let Some(ref deposits) = self.config.deposits { for (slot, deposit, keypair) in deposits { if *slot == slot_height { info!("Including deposit at slot height {}.", slot_height); - harness.process_deposit(deposit.clone(), Some(keypair.clone())); + harness.add_deposit(deposit.clone(), Some(keypair.clone())); + } + } + } + + // Feed proposer slashings to the BeaconChain. + if let Some(ref slashings) = self.config.proposer_slashings { + for (slot, slashing) in slashings { + if *slot == slot_height { + info!( + "Including proposer slashing at slot height {}.", + slot_height + ); + harness.add_proposer_slashing(slashing.clone()); } } } @@ -163,6 +176,7 @@ impl Manifest { } pub type DepositTuple = (u64, Deposit, Keypair); +pub type ProposerSlashingTuple = (u64, ProposerSlashing); struct ExecutionResult { pub chain: Vec, @@ -190,6 +204,7 @@ struct Config { pub num_slots: u64, pub skip_slots: Option>, pub deposits: Option>, + pub proposer_slashings: Option>, } impl Config { @@ -200,12 +215,52 @@ impl Config { epoch_length: as_u64(&yaml, "epoch_length"), num_slots: as_u64(&yaml, "num_slots").expect("Must specify `config.num_slots`"), skip_slots: as_vec_u64(yaml, "skip_slots"), - deposits: process_deposits(&yaml), + deposits: parse_deposits(&yaml), + proposer_slashings: parse_proposer_slashings(&yaml), } } } -fn process_deposits(yaml: &Yaml) -> Option> { +fn parse_proposer_slashings(yaml: &Yaml) -> Option> { + let mut slashings = vec![]; + + for slashing in yaml["proposer_slashings"].as_vec()? { + let slot = as_u64(slashing, "slot").expect("Incomplete slashing"); + + let slashing = ProposerSlashing { + proposer_index: as_u64(slashing, "proposer_index") + .expect("Incomplete slashing (proposer_index)"), + proposal_data_1: ProposalSignedData { + slot: Slot::from( + as_u64(slashing, "proposal_1_slot") + .expect("Incomplete slashing (proposal_1_slot)."), + ), + shard: as_u64(slashing, "proposal_1_shard") + .expect("Incomplete slashing (proposal_1_shard)."), + block_root: as_hash256(slashing, "proposal_1_root") + .expect("Incomplete slashing (proposal_1_root)."), + }, + proposal_signature_1: Signature::empty_signature(), // Will be replaced with real signature at runtime. + proposal_data_2: ProposalSignedData { + slot: Slot::from( + as_u64(slashing, "proposal_2_slot") + .expect("Incomplete slashing (proposal_2_slot)."), + ), + shard: as_u64(slashing, "proposal_2_shard") + .expect("Incomplete slashing (proposal_2_shard)."), + block_root: as_hash256(slashing, "proposal_2_root") + .expect("Incomplete slashing (proposal_2_root)."), + }, + proposal_signature_2: Signature::empty_signature(), // Will be replaced with real signature at runtime. + }; + + slashings.push((slot, slashing)); + } + + Some(slashings) +} + +fn parse_deposits(yaml: &Yaml) -> Option> { let mut deposits = vec![]; for deposit in yaml["deposits"].as_vec()? { @@ -241,6 +296,12 @@ fn as_u64(yaml: &Yaml, key: &str) -> Option { yaml[key].as_i64().and_then(|n| Some(n as u64)) } +fn as_hash256(yaml: &Yaml, key: &str) -> Option { + yaml[key] + .as_str() + .and_then(|s| Some(Hash256::from(s.as_bytes()))) +} + fn as_vec_u64(yaml: &Yaml, key: &str) -> Option> { yaml[key].clone().into_vec().and_then(|vec| { Some(