diff --git a/eth2/operation_pool/src/lib.rs b/eth2/operation_pool/src/lib.rs index bb64c3ca2..618c9d870 100644 --- a/eth2/operation_pool/src/lib.rs +++ b/eth2/operation_pool/src/lib.rs @@ -619,13 +619,28 @@ mod tests { spec: &ChainSpec, extra_signer: Option, ) -> Attestation { - let mut builder = TestingAttestationBuilder::new(state, committee, slot, shard, spec); + let mut builder = TestingAttestationBuilder::new( + &AttestationTestTask::Valid, + state, + committee, + slot, + shard, + spec, + ); let signers = &committee[signing_range]; let committee_keys = signers.iter().map(|&i| &keypairs[i].sk).collect::>(); - builder.sign(signers, &committee_keys, &state.fork, spec, false); + builder.sign( + &AttestationTestTask::Valid, + signers, + &committee_keys, + &state.fork, + spec, + false, + ); extra_signer.map(|c_idx| { let validator_index = committee[c_idx]; builder.sign( + &AttestationTestTask::Valid, &[validator_index], &[&keypairs[validator_index].sk], &state.fork, diff --git a/eth2/state_processing/src/common/get_indexed_attestation.rs b/eth2/state_processing/src/common/get_indexed_attestation.rs index 5e9362331..6cae2e47f 100644 --- a/eth2/state_processing/src/common/get_indexed_attestation.rs +++ b/eth2/state_processing/src/common/get_indexed_attestation.rs @@ -98,11 +98,31 @@ mod test { .map(|validator_index| &keypairs[*validator_index].sk) .collect::>(); - let mut attestation_builder = - TestingAttestationBuilder::new(&state, &cc.committee, cc.slot, shard, &spec); + let mut attestation_builder = TestingAttestationBuilder::new( + &AttestationTestTask::Valid, + &state, + &cc.committee, + cc.slot, + shard, + &spec, + ); attestation_builder - .sign(&bit_0_indices, &bit_0_keys, &state.fork, &spec, false) - .sign(&bit_1_indices, &bit_1_keys, &state.fork, &spec, true); + .sign( + &AttestationTestTask::Valid, + &bit_0_indices, + &bit_0_keys, + &state.fork, + &spec, + false, + ) + .sign( + &AttestationTestTask::Valid, + &bit_1_indices, + &bit_1_keys, + &state.fork, + &spec, + true, + ); let attestation = attestation_builder.build(); let indexed_attestation = get_indexed_attestation(&state, &attestation).unwrap(); diff --git a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs index 329583759..2e7e54b6b 100644 --- a/eth2/state_processing/src/per_block_processing/block_processing_builder.rs +++ b/eth2/state_processing/src/per_block_processing/block_processing_builder.rs @@ -1,5 +1,9 @@ +use std::convert::TryInto; use tree_hash::SignedRoot; -use types::test_utils::{TestingBeaconBlockBuilder, TestingBeaconStateBuilder}; +use types::test_utils::{ + AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, + ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder, +}; use types::*; pub struct BlockProcessingBuilder { @@ -30,6 +34,252 @@ impl BlockProcessingBuilder { self.state_builder.build_caches(&spec).unwrap(); } + pub fn build_with_n_deposits( + mut self, + num_deposits: u64, + test_task: DepositTestTask, + randao_sk: Option, + previous_block_root: Option, + spec: &ChainSpec, + ) -> (BeaconBlock, BeaconState) { + let (mut state, keypairs) = self.state_builder.build(); + + let builder = &mut self.block_builder; + + builder.set_slot(state.slot); + + match previous_block_root { + Some(root) => builder.set_parent_root(root), + None => builder.set_parent_root(Hash256::from_slice( + &state.latest_block_header.signed_root(), + )), + } + + let proposer_index = state + .get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec) + .unwrap(); + let keypair = &keypairs[proposer_index]; + + match randao_sk { + Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), + None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + } + + self.block_builder.insert_deposits( + spec.max_effective_balance, + test_task, + 1, + num_deposits, + &mut state, + spec, + ); + + let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + + (block, state) + } + + pub fn build_with_n_exits( + mut self, + num_exits: usize, + test_task: ExitTestTask, + randao_sk: Option, + previous_block_root: Option, + spec: &ChainSpec, + ) -> (BeaconBlock, BeaconState) { + let (mut state, keypairs) = self.state_builder.build(); + let builder = &mut self.block_builder; + + builder.set_slot(state.slot); + + match previous_block_root { + Some(root) => builder.set_parent_root(root), + None => builder.set_parent_root(Hash256::from_slice( + &state.latest_block_header.signed_root(), + )), + } + + let proposer_index = state + .get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec) + .unwrap(); + let keypair = &keypairs[proposer_index]; + + match randao_sk { + Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), + None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + } + match test_task { + ExitTestTask::AlreadyInitiated => { + for _ in 0..2 { + self.block_builder.insert_exit( + &test_task, + &mut state, + (0 as usize).try_into().unwrap(), + &keypairs[0].sk, + spec, + ) + } + } + _ => { + for (i, keypair) in keypairs.iter().take(num_exits).enumerate() { + self.block_builder.insert_exit( + &test_task, + &mut state, + (i as usize).try_into().unwrap(), + &keypair.sk, + spec, + ); + } + } + } + + let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + + (block, state) + } + + pub fn build_with_n_attestations( + mut self, + test_task: &AttestationTestTask, + num_attestations: u64, + randao_sk: Option, + previous_block_root: Option, + spec: &ChainSpec, + ) -> (BeaconBlock, BeaconState) { + let (state, keypairs) = self.state_builder.build(); + let builder = &mut self.block_builder; + + builder.set_slot(state.slot); + + match previous_block_root { + Some(root) => builder.set_parent_root(root), + None => builder.set_parent_root(Hash256::from_slice( + &state.latest_block_header.signed_root(), + )), + } + + let proposer_index = state + .get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec) + .unwrap(); + let keypair = &keypairs[proposer_index]; + + match randao_sk { + Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), + None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + } + + let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect(); + self.block_builder + .insert_attestations( + test_task, + &state, + &all_secret_keys, + num_attestations as usize, + spec, + ) + .unwrap(); + let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + + (block, state) + } + + pub fn build_with_attester_slashing( + mut self, + test_task: &AttesterSlashingTestTask, + num_attester_slashings: u64, + randao_sk: Option, + previous_block_root: Option, + spec: &ChainSpec, + ) -> (BeaconBlock, BeaconState) { + let (state, keypairs) = self.state_builder.build(); + let builder = &mut self.block_builder; + + builder.set_slot(state.slot); + + match previous_block_root { + Some(root) => builder.set_parent_root(root), + None => builder.set_parent_root(Hash256::from_slice( + &state.latest_block_header.signed_root(), + )), + } + + let proposer_index = state + .get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec) + .unwrap(); + let keypair = &keypairs[proposer_index]; + + match randao_sk { + Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), + None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + } + + let mut validator_indices = vec![]; + let mut secret_keys = vec![]; + for i in 0..num_attester_slashings { + validator_indices.push(i); + secret_keys.push(&keypairs[i as usize].sk); + } + + for _ in 0..num_attester_slashings { + self.block_builder.insert_attester_slashing( + test_task, + &validator_indices, + &secret_keys, + &state.fork, + spec, + ); + } + let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + + (block, state) + } + + pub fn build_with_proposer_slashing( + mut self, + test_task: &ProposerSlashingTestTask, + num_proposer_slashings: u64, + randao_sk: Option, + previous_block_root: Option, + spec: &ChainSpec, + ) -> (BeaconBlock, BeaconState) { + let (state, keypairs) = self.state_builder.build(); + let builder = &mut self.block_builder; + + builder.set_slot(state.slot); + + match previous_block_root { + Some(root) => builder.set_parent_root(root), + None => builder.set_parent_root(Hash256::from_slice( + &state.latest_block_header.signed_root(), + )), + } + + let proposer_index = state + .get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec) + .unwrap(); + let keypair = &keypairs[proposer_index]; + + match randao_sk { + Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec), + None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec), + } + + for i in 0..num_proposer_slashings { + let validator_indices = i; + let secret_keys = &keypairs[i as usize].sk; + self.block_builder.insert_proposer_slashing( + test_task, + validator_indices, + &secret_keys, + &state.fork, + spec, + ); + } + let block = self.block_builder.build(&keypair.sk, &state.fork, spec); + + (block, state) + } + pub fn build( mut self, randao_sk: Option, diff --git a/eth2/state_processing/src/per_block_processing/errors.rs b/eth2/state_processing/src/per_block_processing/errors.rs index b5f440ab5..1d3094a89 100644 --- a/eth2/state_processing/src/per_block_processing/errors.rs +++ b/eth2/state_processing/src/per_block_processing/errors.rs @@ -190,8 +190,6 @@ pub enum ProposerSlashingInvalid { #[derive(Debug, PartialEq)] pub enum AttesterSlashingInvalid { - /// The attestation data is identical, an attestation cannot conflict with itself. - AttestationDataIdentical, /// The attestations were not in conflict. NotSlashable, /// The first `IndexedAttestation` was invalid. @@ -257,8 +255,6 @@ pub enum AttestationInvalid { CustodyBitfieldNotSubset, /// There was no known committee in this `epoch` for the given shard and slot. NoCommitteeForShard { shard: u64, slot: Slot }, - /// The validator index was unknown. - UnknownValidator(u64), /// The attestation signature verification failed. BadSignature, /// The shard block root was not set to zero. This is a phase 0 requirement. @@ -311,8 +307,6 @@ pub enum IndexedAttestationInvalid { #[derive(Debug, PartialEq)] pub enum DepositInvalid { - /// The deposit index does not match the state index. - BadIndex { state: u64, deposit: u64 }, /// The signature (proof-of-possession) does not match the given pubkey. BadSignature, /// The signature or pubkey does not represent a valid BLS point. @@ -331,7 +325,7 @@ pub enum ExitInvalid { /// The specified validator has a non-maximum exit epoch. AlreadyExited(u64), /// The specified validator has already initiated exit. - AlreadyInitiatedExited(u64), + AlreadyInitiatedExit(u64), /// The exit is for a future epoch. FutureEpoch { state: Epoch, exit: Epoch }, /// The validator has not been active for long enough. diff --git a/eth2/state_processing/src/per_block_processing/tests.rs b/eth2/state_processing/src/per_block_processing/tests.rs index 31704de44..fe94bf72f 100644 --- a/eth2/state_processing/src/per_block_processing/tests.rs +++ b/eth2/state_processing/src/per_block_processing/tests.rs @@ -4,14 +4,22 @@ use super::block_processing_builder::BlockProcessingBuilder; use super::errors::*; use crate::{per_block_processing, BlockSignatureStrategy}; use tree_hash::SignedRoot; +use types::test_utils::{ + AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, + ProposerSlashingTestTask, +}; use types::*; +pub const NUM_DEPOSITS: u64 = 1; pub const VALIDATOR_COUNT: usize = 10; +pub const SLOT_OFFSET: u64 = 4; +pub const EXIT_SLOT_OFFSET: u64 = 2048; +pub const NUM_ATTESTATIONS: u64 = 1; #[test] fn valid_block_ok() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let (block, mut state) = builder.build(None, None, &spec); let result = per_block_processing( @@ -28,7 +36,7 @@ fn valid_block_ok() { #[test] fn invalid_block_header_state_slot() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let (mut block, mut state) = builder.build(None, None, &spec); state.slot = Slot::new(133_713); @@ -53,7 +61,7 @@ fn invalid_block_header_state_slot() { #[test] fn invalid_parent_block_root() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let invalid_parent_root = Hash256::from([0xAA; 32]); let (block, mut state) = builder.build(None, Some(invalid_parent_root), &spec); @@ -79,7 +87,7 @@ fn invalid_parent_block_root() { #[test] fn invalid_block_signature() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); let (mut block, mut state) = builder.build(None, None, &spec); // sign the block with a keypair that is not the expected proposer @@ -110,7 +118,7 @@ fn invalid_block_signature() { #[test] fn invalid_randao_reveal_signature() { let spec = MainnetEthSpec::default_spec(); - let builder = get_builder(&spec); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); // sign randao reveal with random keypair let keypair = Keypair::random(); @@ -128,12 +136,1239 @@ fn invalid_randao_reveal_signature() { assert_eq!(result, Err(BlockProcessingError::RandaoSignatureInvalid)); } -fn get_builder(spec: &ChainSpec) -> (BlockProcessingBuilder) { - let mut builder = BlockProcessingBuilder::new(VALIDATOR_COUNT, &spec); +#[test] +fn valid_4_deposits() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::Valid; - // Set the state and block to be in the last slot of the 4th epoch. + let (block, mut state) = builder.build_with_n_deposits(4, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok because these are valid deposits. + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_deposit_deposit_count_too_big() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::Valid; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let big_deposit_count = NUM_DEPOSITS + 1; + state.eth1_data.deposit_count = big_deposit_count; + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting DepositCountInvalid because we incremented the deposit_count + assert_eq!( + result, + Err(BlockProcessingError::DepositCountInvalid { + expected: big_deposit_count as usize, + found: 1 + }) + ); +} + +#[test] +fn invalid_deposit_count_too_small() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::Valid; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let small_deposit_count = NUM_DEPOSITS - 1; + state.eth1_data.deposit_count = small_deposit_count; + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting DepositCountInvalid because we decremented the deposit_count + assert_eq!( + result, + Err(BlockProcessingError::DepositCountInvalid { + expected: small_deposit_count as usize, + found: 1 + }) + ); +} + +#[test] +fn invalid_deposit_bad_merkle_proof() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::Valid; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let bad_index = state.eth1_deposit_index as usize; + + // Manually offsetting deposit count and index to trigger bad merkle proof + state.eth1_data.deposit_count += 1; + state.eth1_deposit_index += 1; + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadMerkleProof because the proofs were created with different indices + assert_eq!( + result, + Err(BlockProcessingError::DepositInvalid { + index: bad_index, + reason: DepositInvalid::BadMerkleProof + }) + ); +} + +#[test] +fn invalid_deposit_wrong_pubkey() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::BadPubKey; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) even though the public key provided does not correspond to the correct public key + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_deposit_wrong_sig() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::BadSig; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) even though the block signature does not correspond to the correct public key + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_deposit_invalid_pub_key() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = DepositTestTask::InvalidPubKey; + + let (block, mut state) = + builder.build_with_n_deposits(NUM_DEPOSITS, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) even though we passed in invalid publickeybytes in the public key field of the deposit data. + assert_eq!(result, Ok(())); +} + +#[test] +fn valid_insert_3_exits() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 3; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let test_task = ExitTestTask::Valid; + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok because these are valid exits. + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_exit_validator_unknown() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::ValidatorUnknown; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Validator Unknwon because the exit index is incorrect + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::ValidatorUnknown(4242), + }) + ); +} + +#[test] +fn invalid_exit_already_exited() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::AlreadyExited; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting AlreadyExited because we manually set the exit_epoch to be different than far_future_epoch. + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::AlreadyExited(0), + }) + ); +} + +#[test] +fn invalid_exit_not_active() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::NotActive; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting NotActive because we manually set the activation_epoch to be in the future + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::NotActive(0), + }) + ); +} + +#[test] +fn invalid_exit_already_initiated() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::AlreadyInitiated; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) even though we inserted the same exit twice + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_exit_future_epoch() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::FutureEpoch; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting FutureEpoch because we set the exit_epoch to be far_future_epoch + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::FutureEpoch { + state: Epoch::from(2048 as u64), + exit: spec.far_future_epoch + } + }) + ); +} + +#[test] +fn invalid_exit_too_young() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::Valid; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting TooYoung because validator has not been active for long enough when trying to exit + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::TooYoungToExit { + current_epoch: Epoch::from(SLOT_OFFSET), + earliest_exit_epoch: Epoch::from(2048 as u64) + }, + }) + ); +} + +#[test] +fn invalid_exit_bad_signature() { + use std::cmp::max; + + let spec = MainnetEthSpec::default_spec(); + let num_exits = 1; + let test_task = ExitTestTask::BadSignature; + let num_validators = max(VALIDATOR_COUNT, num_exits); + let builder = get_builder(&spec, EXIT_SLOT_OFFSET, num_validators); + + let (block, mut state) = builder.build_with_n_exits(num_exits, test_task, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Bad Signature because we signed with a different secret key than the correct one. + assert_eq!( + result, + Err(BlockProcessingError::ExitInvalid { + index: 0, + reason: ExitInvalid::BadSignature, + }) + ); +} + +#[test] +fn valid_attestations() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::Valid; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) because these are valid attestations + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_attestation_parent_crosslink_start_epoch() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadParentCrosslinkStartEpoch; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadParentCrosslinkEndEpoch because we manually set an invalid crosslink start epoch + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadParentCrosslinkStartEpoch + }) + ); +} + +#[test] +fn invalid_attestation_parent_crosslink_end_epoch() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadParentCrosslinkEndEpoch; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadParentCrosslinkEndEpoch because we manually set an invalid crosslink end epoch + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadParentCrosslinkEndEpoch + }) + ); +} + +#[test] +fn invalid_attestation_parent_crosslink_hash() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadParentCrosslinkHash; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadParentCrosslinkHash because we manually set an invalid crosslink parent_root + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadParentCrosslinkHash + }) + ); +} + +#[test] +fn invalid_attestation_no_committee_for_shard() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::NoCommiteeForShard; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting NoCommiteeForShard because we manually set the crosslink's shard to be invalid + assert_eq!( + result, + Err(BlockProcessingError::BeaconStateError( + BeaconStateError::NoCommitteeForShard + )) + ); +} + +#[test] +fn invalid_attestation_wrong_justified_checkpoint() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::WrongJustifiedCheckpoint; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting WrongJustifiedCheckpoint because we manually set the + // source field of the AttestationData object to be invalid + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::WrongJustifiedCheckpoint { + state: Checkpoint { + epoch: Epoch::from(2 as u64), + root: Hash256::zero(), + }, + attestation: Checkpoint { + epoch: Epoch::from(0 as u64), + root: Hash256::zero(), + }, + is_current: true, + } + }) + ); +} + +#[test] +fn invalid_attestation_bad_target_too_low() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadTargetTooLow; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting EpochTooLow because we manually set the + // target field of the AttestationData object to be invalid + assert_eq!( + result, + Err(BlockProcessingError::BeaconStateError( + BeaconStateError::RelativeEpochError(RelativeEpochError::EpochTooLow { + base: state.current_epoch(), + other: Epoch::from(0 as u64), + }) + )) + ); +} + +#[test] +fn invalid_attestation_bad_target_too_high() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadTargetTooHigh; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting EpochTooHigh because we manually set the + // target field of the AttestationData object to be invalid + assert_eq!( + result, + Err(BlockProcessingError::BeaconStateError( + BeaconStateError::RelativeEpochError(RelativeEpochError::EpochTooHigh { + base: state.current_epoch(), + other: Epoch::from(10 as u64), + }) + )) + ); +} + +#[test] +fn invalid_attestation_bad_crosslink_data_root() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadParentCrosslinkDataRoot; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting ShardBlockRootNotZero because we manually set the + // data_root of the cross link to be non zero + + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::ShardBlockRootNotZero, + }) + ); +} + +#[test] +fn invalid_attestation_bad_indexed_attestation_bad_signature() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, 33); // minmium number of validators required for this test + let test_task = AttestationTestTask::BadIndexedAttestationBadSignature; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadIndexedAttestation(BadSignature) because we ommitted the aggregation bits in the attestation + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadIndexedAttestation( + IndexedAttestationInvalid::BadSignature + ) + }) + ); +} + +#[test] +fn invalid_attestation_custody_bitfield_not_subset() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, 33); // minmium number of validators required for this test + let test_task = AttestationTestTask::CustodyBitfieldNotSubset; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting CustodyBitfieldNotSubset because we set custody_bit to true without setting the aggregation bits. + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::CustodyBitfieldNotSubset + }) + ); +} + +#[test] +fn invalid_attestation_custody_bitfield_has_set_bits() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, 33); // minmium number of validators required for this test + let test_task = AttestationTestTask::CustodyBitfieldHasSetBits; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting CustodyBitfieldHasSetBits because we set custody bits even though the custody_bit boolean is set to false + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadIndexedAttestation( + IndexedAttestationInvalid::CustodyBitfieldHasSetBits + ) + }) + ); +} + +#[test] +fn invalid_attestation_bad_custody_bitfield_len() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadCustodyBitfieldLen; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting InvalidBitfield because the size of the custody_bitfield is bigger than the commitee size. + assert_eq!( + result, + Err(BlockProcessingError::BeaconStateError( + BeaconStateError::InvalidBitfield + )) + ); +} + +#[test] +fn invalid_attestation_bad_aggregation_bitfield_len() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadAggregationBitfieldLen; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting InvalidBitfield because the size of the aggregation_bitfield is bigger than the commitee size. + assert_eq!( + result, + Err(BlockProcessingError::BeaconStateError( + BeaconStateError::InvalidBitfield + )) + ); +} + +#[test] +fn invalid_attestation_bad_signature() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, 97); // minimal number of required validators for this test + let test_task = AttestationTestTask::BadSignature; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadSignature because we're signing with invalid secret_keys + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadIndexedAttestation( + IndexedAttestationInvalid::BadSignature + ) + }) + ); +} + +#[test] +fn invalid_attestation_included_too_early() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::IncludedTooEarly; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting IncludedTooEarly because the shard included in the crosslink is bigger than expected + assert_eq!( + result, + Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::IncludedTooEarly { + state: Slot::from(319 as u64), + delay: spec.min_attestation_inclusion_delay, + attestation: Slot::from(319 as u64) + } + }) + ); +} + +#[test] +fn invalid_attestation_included_too_late() { + let spec = MainnetEthSpec::default_spec(); + // note to maintainer: might need to increase validator count if we get NoCommitteeForShard + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::IncludedTooLate; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting IncludedTooLate because the shard included in the crosslink is bigger than expected + assert!( + result + == Err(BlockProcessingError::BeaconStateError( + BeaconStateError::NoCommitteeForShard + )) + || result + == Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::IncludedTooLate { + state: state.slot, + attestation: Slot::from(254 as u64), + } + }) + ); +} + +#[test] +fn invalid_attestation_bad_target_epoch() { + let spec = MainnetEthSpec::default_spec(); + // note to maintainer: might need to increase validator count if we get NoCommitteeForShard + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadTargetEpoch; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadTargetEpoch because the target epoch is bigger by one than the epoch expected + assert!( + result + == Err(BlockProcessingError::BeaconStateError( + BeaconStateError::NoCommitteeForShard + )) + || result + == Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadTargetEpoch + }) + ); +} + +#[test] +fn invalid_attestation_bad_shard() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttestationTestTask::BadShard; + let (block, mut state) = + builder.build_with_n_attestations(&test_task, NUM_ATTESTATIONS, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadShard or NoCommitteeForShard because the shard number is higher than ShardCount + assert!( + result + == Err(BlockProcessingError::AttestationInvalid { + index: 0, + reason: AttestationInvalid::BadShard + }) + || result + == Err(BlockProcessingError::BeaconStateError( + BeaconStateError::NoCommitteeForShard + )) + ); +} + +#[test] +fn valid_insert_attester_slashing() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttesterSlashingTestTask::Valid; + let num_attester_slashings = 1; + let (block, mut state) = + builder.build_with_attester_slashing(&test_task, num_attester_slashings, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) because attester slashing is valid + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_attester_slashing_not_slashable() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttesterSlashingTestTask::NotSlashable; + let num_attester_slashings = 1; + let (block, mut state) = + builder.build_with_attester_slashing(&test_task, num_attester_slashings, None, None, &spec); + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting NotSlashable because the two attestations are the same + assert_eq!( + result, + Err(BlockProcessingError::AttesterSlashingInvalid { + index: 0, + reason: AttesterSlashingInvalid::NotSlashable + }) + ); +} + +#[test] +fn invalid_attester_slashing_1_invalid() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttesterSlashingTestTask::IndexedAttestation1Invalid; + let num_attester_slashings = 1; + let (block, mut state) = + builder.build_with_attester_slashing(&test_task, num_attester_slashings, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting IndexedAttestation1Invalid or IndexedAttestationInvalid because Attestation1 has CustodyBitfield bits set. + assert!( + result + == Err(BlockProcessingError::IndexedAttestationInvalid { + index: 0, + reason: IndexedAttestationInvalid::CustodyBitfieldHasSetBits + }) + || result + == Err(BlockProcessingError::AttesterSlashingInvalid { + index: 0, + reason: AttesterSlashingInvalid::IndexedAttestation1Invalid( + BlockOperationError::Invalid( + IndexedAttestationInvalid::CustodyBitfieldHasSetBits + ) + ) + }) + ); +} + +#[test] +fn invalid_attester_slashing_2_invalid() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = AttesterSlashingTestTask::IndexedAttestation2Invalid; + let num_attester_slashings = 1; + let (block, mut state) = + builder.build_with_attester_slashing(&test_task, num_attester_slashings, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting IndexedAttestation2Invalid or IndexedAttestationInvalid because Attestation2 has CustodyBitfield bits set. + assert!( + result + == Err(BlockProcessingError::IndexedAttestationInvalid { + index: 1, + reason: IndexedAttestationInvalid::CustodyBitfieldHasSetBits + }) + || result + == Err(BlockProcessingError::AttesterSlashingInvalid { + index: 1, + reason: AttesterSlashingInvalid::IndexedAttestation2Invalid( + BlockOperationError::Invalid( + IndexedAttestationInvalid::CustodyBitfieldHasSetBits + ) + ) + }) + ); +} + +#[test] +fn valid_insert_proposer_slashing() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::Valid; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting Ok(()) because we inserted a valid proposer slashing + assert_eq!(result, Ok(())); +} + +#[test] +fn invalid_proposer_slashing_proposals_identical() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::ProposalsIdentical; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + // Expecting ProposalsIdentical because we the two headers are identical + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::ProposalsIdentical + }) + ); +} + +#[test] +fn invalid_proposer_slashing_proposer_unknown() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::ProposerUnknown; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting ProposerUnknown because validator_index is unknown + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::ProposerUnknown(3_141_592) + }) + ); +} + +#[test] +fn invalid_proposer_slashing_not_slashable() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::ProposerNotSlashable; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + state.validators[0].slashed = true; + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting ProposerNotSlashable because we've already slashed the validator + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::ProposerNotSlashable(0) + }) + ); +} + +#[test] +fn invalid_bad_proposal_1_signature() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::BadProposal1Signature; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadProposal1Signature because signature of proposal 1 is invalid + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::BadProposal1Signature + }) + ); +} + +#[test] +fn invalid_bad_proposal_2_signature() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::BadProposal2Signature; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting BadProposal2Signature because signature of proposal 2 is invalid + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::BadProposal2Signature + }) + ); +} + +#[test] +fn invalid_proposer_slashing_proposal_epoch_mismatch() { + let spec = MainnetEthSpec::default_spec(); + let builder = get_builder(&spec, SLOT_OFFSET, VALIDATOR_COUNT); + let test_task = ProposerSlashingTestTask::ProposalEpochMismatch; + let (block, mut state) = builder.build_with_proposer_slashing(&test_task, 1, None, None, &spec); + + let result = per_block_processing( + &mut state, + &block, + None, + BlockSignatureStrategy::VerifyIndividual, + &spec, + ); + + // Expecting ProposalEpochMismatch because the two epochs are different + assert_eq!( + result, + Err(BlockProcessingError::ProposerSlashingInvalid { + index: 0, + reason: ProposerSlashingInvalid::ProposalEpochMismatch( + Slot::from(0 as u64), + Slot::from(128 as u64) + ) + }) + ); +} + +fn get_builder( + spec: &ChainSpec, + slot_offset: u64, + num_validators: usize, +) -> (BlockProcessingBuilder) { + let mut builder = BlockProcessingBuilder::new(num_validators, &spec); + + // Set the state and block to be in the last slot of the `slot_offset`th epoch. let last_slot_of_epoch = - (MainnetEthSpec::genesis_epoch() + 4).end_slot(MainnetEthSpec::slots_per_epoch()); + (MainnetEthSpec::genesis_epoch() + slot_offset).end_slot(MainnetEthSpec::slots_per_epoch()); builder.set_slot(last_slot_of_epoch); builder.build_caches(&spec); diff --git a/eth2/state_processing/src/per_block_processing/verify_attestation.rs b/eth2/state_processing/src/per_block_processing/verify_attestation.rs index 438440901..2f5b1252e 100644 --- a/eth2/state_processing/src/per_block_processing/verify_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/verify_attestation.rs @@ -61,6 +61,7 @@ pub fn verify_attestation_for_state( spec: &ChainSpec, ) -> Result<()> { let data = &attestation.data; + verify!( data.crosslink.shard < T::ShardCount::to_u64(), Invalid::BadShard diff --git a/eth2/state_processing/src/test_utils.rs b/eth2/state_processing/src/test_utils.rs index ce06a4fbb..1651cf794 100644 --- a/eth2/state_processing/src/test_utils.rs +++ b/eth2/state_processing/src/test_utils.rs @@ -1,5 +1,8 @@ use log::info; -use types::test_utils::{TestingBeaconBlockBuilder, TestingBeaconStateBuilder}; +use types::test_utils::{ + AttestationTestTask, AttesterSlashingTestTask, DepositTestTask, ExitTestTask, + ProposerSlashingTestTask, TestingBeaconBlockBuilder, TestingBeaconStateBuilder, +}; use types::{EthSpec, *}; pub struct BlockBuilder { @@ -77,6 +80,7 @@ impl BlockBuilder { let validator_index = validators_iter.next().expect("Insufficient validators."); builder.insert_proposer_slashing( + &ProposerSlashingTestTask::Valid, validator_index, &keypairs[validator_index as usize].sk, &state.fork, @@ -102,7 +106,13 @@ impl BlockBuilder { secret_keys.push(&keypairs[validator_index as usize].sk); } - builder.insert_attester_slashing(&attesters, &secret_keys, &state.fork, spec); + builder.insert_attester_slashing( + &AttesterSlashingTestTask::Valid, + &attesters, + &secret_keys, + &state.fork, + spec, + ); } info!( "Inserted {} attester slashings.", @@ -113,6 +123,7 @@ impl BlockBuilder { let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect(); builder .insert_attestations( + &AttestationTestTask::Valid, &state, &all_secret_keys, self.num_attestations as usize, @@ -125,15 +136,14 @@ impl BlockBuilder { ); // Insert `Deposit` objects. - for i in 0..self.num_deposits { - builder.insert_deposit( - 32_000_000_000, - state.eth1_data.deposit_count + (i as u64), - &state, - spec, - ); - } - state.eth1_data.deposit_count += self.num_deposits as u64; + builder.insert_deposits( + 32_000_000_000, + DepositTestTask::NoReset, + state.eth1_data.deposit_count, + self.num_deposits as u64, + &mut state, + spec, + ); info!("Inserted {} deposits.", builder.block.body.deposits.len()); // Insert the maximum possible number of `Exit` objects. @@ -141,7 +151,8 @@ impl BlockBuilder { let validator_index = validators_iter.next().expect("Insufficient validators."); builder.insert_exit( - &state, + &ExitTestTask::Valid, + &mut state, validator_index, &keypairs[validator_index as usize].sk, spec, diff --git a/eth2/types/Cargo.toml b/eth2/types/Cargo.toml index e3138b26c..ca78da340 100644 --- a/eth2/types/Cargo.toml +++ b/eth2/types/Cargo.toml @@ -16,6 +16,7 @@ eth2_hashing = "0.1.0" hex = "0.3" int_to_bytes = { path = "../utils/int_to_bytes" } log = "0.4.8" +merkle_proof = { path = "../utils/merkle_proof" } rayon = "1.2.0" rand = "0.7.2" serde = "1.0.102" diff --git a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs index f794919f3..1742ce4d8 100644 --- a/eth2/types/src/test_utils/builders/testing_attestation_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attestation_builder.rs @@ -1,4 +1,4 @@ -use crate::test_utils::TestingAttestationDataBuilder; +use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder}; use crate::*; use tree_hash::TreeHash; @@ -13,18 +13,27 @@ pub struct TestingAttestationBuilder { impl TestingAttestationBuilder { /// Create a new attestation builder. pub fn new( + test_task: &AttestationTestTask, state: &BeaconState, committee: &[usize], slot: Slot, shard: u64, spec: &ChainSpec, ) -> Self { - let data_builder = TestingAttestationDataBuilder::new(state, shard, slot, spec); + let data_builder = TestingAttestationDataBuilder::new(test_task, state, shard, slot, spec); - let mut aggregation_bits = BitList::with_capacity(committee.len()).unwrap(); - let mut custody_bits = BitList::with_capacity(committee.len()).unwrap(); + let mut aggregation_bits_len = committee.len(); + let mut custody_bits_len = committee.len(); - for (i, _) in committee.iter().enumerate() { + match test_task { + AttestationTestTask::BadAggregationBitfieldLen => aggregation_bits_len += 1, + AttestationTestTask::BadCustodyBitfieldLen => custody_bits_len += 1, + _ => (), + } + let mut aggregation_bits = BitList::with_capacity(aggregation_bits_len).unwrap(); + let mut custody_bits = BitList::with_capacity(custody_bits_len).unwrap(); + + for i in 0..committee.len() { custody_bits.set(i, false).unwrap(); aggregation_bits.set(i, false).unwrap(); } @@ -48,11 +57,12 @@ impl TestingAttestationBuilder { /// keypair must be that of the first signing validator. pub fn sign( &mut self, + test_task: &AttestationTestTask, signing_validators: &[usize], secret_keys: &[&SecretKey], fork: &Fork, spec: &ChainSpec, - custody_bit: bool, + mut custody_bit: bool, ) -> &mut Self { assert_eq!( signing_validators.len(), @@ -67,16 +77,24 @@ impl TestingAttestationBuilder { .position(|v| *v == *validator_index) .expect("Signing validator not in attestation committee"); - self.attestation - .aggregation_bits - .set(committee_index, true) - .unwrap(); - - if custody_bit { - self.attestation - .custody_bits - .set(committee_index, true) - .unwrap(); + match test_task { + AttestationTestTask::BadIndexedAttestationBadSignature => (), + AttestationTestTask::CustodyBitfieldNotSubset => custody_bit = true, + _ => { + self.attestation + .aggregation_bits + .set(committee_index, true) + .unwrap(); + } + } + match (custody_bit, test_task) { + (true, _) | (_, AttestationTestTask::CustodyBitfieldHasSetBits) => { + self.attestation + .custody_bits + .set(committee_index, true) + .unwrap(); + } + (false, _) => (), } let message = AttestationDataAndCustodyBit { @@ -91,7 +109,12 @@ impl TestingAttestationBuilder { fork, ); - let signature = Signature::new(&message, domain, secret_keys[key_index]); + let index = if *test_task == AttestationTestTask::BadSignature { + 0 + } else { + key_index + }; + let signature = Signature::new(&message, domain, secret_keys[index]); self.attestation.signature.add(&signature) } diff --git a/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs b/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs index ac45abe0f..d439490e9 100644 --- a/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attestation_data_builder.rs @@ -1,3 +1,4 @@ +use crate::test_utils::AttestationTestTask; use crate::*; use tree_hash::TreeHash; @@ -12,8 +13,9 @@ impl TestingAttestationDataBuilder { /// Configures a new `AttestationData` which attests to all of the same parameters as the /// state. pub fn new( + test_task: &AttestationTestTask, state: &BeaconState, - shard: u64, + mut shard: u64, slot: Slot, spec: &ChainSpec, ) -> Self { @@ -22,13 +24,13 @@ impl TestingAttestationDataBuilder { let is_previous_epoch = slot.epoch(T::slots_per_epoch()) != current_epoch; - let source = if is_previous_epoch { + let mut source = if is_previous_epoch { state.previous_justified_checkpoint.clone() } else { state.current_justified_checkpoint.clone() }; - let target = if is_previous_epoch { + let mut target = if is_previous_epoch { Checkpoint { epoch: previous_epoch, root: *state @@ -50,20 +52,66 @@ impl TestingAttestationDataBuilder { state.get_current_crosslink(shard).unwrap() }; + let mut start = parent_crosslink.end_epoch; + let mut end = std::cmp::min( + target.epoch, + parent_crosslink.end_epoch + spec.max_epochs_per_crosslink, + ); + let mut parent_root = Hash256::from_slice(&parent_crosslink.tree_hash_root()); + let mut data_root = Hash256::zero(); + let beacon_block_root = *state.get_block_root(slot).unwrap(); + + match test_task { + AttestationTestTask::BadParentCrosslinkStartEpoch => start = Epoch::from(10 as u64), + AttestationTestTask::BadParentCrosslinkEndEpoch => end = Epoch::from(0 as u64), + AttestationTestTask::BadParentCrosslinkHash => parent_root = Hash256::zero(), + AttestationTestTask::NoCommiteeForShard => shard += 2, + AttestationTestTask::BadShard => shard = T::ShardCount::to_u64(), + AttestationTestTask::IncludedTooEarly => shard += 1, + AttestationTestTask::IncludedTooLate => { + target = Checkpoint { + epoch: Epoch::from(3 as u64), + root: Hash256::zero(), + } + } + AttestationTestTask::BadTargetEpoch => { + target = Checkpoint { + epoch: Epoch::from(5 as u64), + root: Hash256::zero(), + } + } + AttestationTestTask::WrongJustifiedCheckpoint => { + source = Checkpoint { + epoch: Epoch::from(0 as u64), + root: Hash256::zero(), + } + } + AttestationTestTask::BadTargetTooLow => { + target = Checkpoint { + epoch: Epoch::from(0 as u64), + root: Hash256::zero(), + } + } + AttestationTestTask::BadTargetTooHigh => { + target = Checkpoint { + epoch: Epoch::from(10 as u64), + root: Hash256::zero(), + } + } + AttestationTestTask::BadParentCrosslinkDataRoot => data_root = parent_root, + _ => (), + } let crosslink = Crosslink { shard, - parent_root: Hash256::from_slice(&parent_crosslink.tree_hash_root()), - start_epoch: parent_crosslink.end_epoch, - end_epoch: std::cmp::min( - target.epoch, - parent_crosslink.end_epoch + spec.max_epochs_per_crosslink, - ), - data_root: Hash256::zero(), + parent_root, + start_epoch: start, + end_epoch: end, + data_root, }; let data = AttestationData { // LMD GHOST vote - beacon_block_root: *state.get_block_root(slot).unwrap(), + beacon_block_root, // FFG Vote source, diff --git a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs index 39673ef38..353c4e38b 100644 --- a/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_attester_slashing_builder.rs @@ -1,3 +1,4 @@ +use crate::test_utils::AttesterSlashingTestTask; use crate::*; use tree_hash::TreeHash; @@ -17,7 +18,11 @@ impl TestingAttesterSlashingBuilder { /// - `domain: Domain` /// /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`). - pub fn double_vote(validator_indices: &[u64], signer: F) -> AttesterSlashing + pub fn double_vote( + test_task: &AttesterSlashingTestTask, + validator_indices: &[u64], + signer: F, + ) -> AttesterSlashing where F: Fn(u64, &[u8], Epoch, Domain) -> Signature, { @@ -49,21 +54,37 @@ impl TestingAttesterSlashingBuilder { crosslink, }; - let data_2 = AttestationData { - target: checkpoint_2, - ..data_1.clone() + let data_2 = if *test_task == AttesterSlashingTestTask::NotSlashable { + AttestationData { ..data_1.clone() } + } else { + AttestationData { + target: checkpoint_2, + ..data_1.clone() + } }; let mut attestation_1 = IndexedAttestation { custody_bit_0_indices: validator_indices.to_vec().into(), - custody_bit_1_indices: VariableList::empty(), + custody_bit_1_indices: if *test_task + == AttesterSlashingTestTask::IndexedAttestation1Invalid + { + validator_indices.to_vec().into() + } else { + VariableList::empty() + }, data: data_1, signature: AggregateSignature::new(), }; let mut attestation_2 = IndexedAttestation { custody_bit_0_indices: validator_indices.to_vec().into(), - custody_bit_1_indices: VariableList::empty(), + custody_bit_1_indices: if *test_task + == AttesterSlashingTestTask::IndexedAttestation2Invalid + { + validator_indices.to_vec().into() + } else { + VariableList::empty() + }, data: data_2, signature: AggregateSignature::new(), }; diff --git a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs index ebb9a64f8..fa77254d9 100644 --- a/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_beacon_block_builder.rs @@ -3,8 +3,11 @@ use crate::{ TestingAttestationBuilder, TestingAttesterSlashingBuilder, TestingDepositBuilder, TestingProposerSlashingBuilder, TestingTransferBuilder, TestingVoluntaryExitBuilder, }, + typenum::U4294967296, *, }; +use int_to_bytes::int_to_bytes32; +use merkle_proof::MerkleTree; use rayon::prelude::*; use tree_hash::{SignedRoot, TreeHash}; @@ -15,6 +18,73 @@ pub struct TestingBeaconBlockBuilder { pub block: BeaconBlock, } +/// Enum used for passing test options to builder +#[derive(PartialEq)] +pub enum DepositTestTask { + Valid, + BadPubKey, + BadSig, + InvalidPubKey, + NoReset, +} + +/// Enum used for passing test options to builder +pub enum ExitTestTask { + AlreadyInitiated, + AlreadyExited, + BadSignature, + FutureEpoch, + NotActive, + Valid, + ValidatorUnknown, +} + +#[derive(PartialEq)] +/// Enum used for passing test options to builder +pub enum AttestationTestTask { + Valid, + BadParentCrosslinkStartEpoch, + BadParentCrosslinkEndEpoch, + BadParentCrosslinkHash, + NoCommiteeForShard, + WrongJustifiedCheckpoint, + BadTargetTooLow, + BadTargetTooHigh, + BadShard, + BadParentCrosslinkDataRoot, + BadIndexedAttestationBadSignature, + CustodyBitfieldNotSubset, + CustodyBitfieldHasSetBits, + BadCustodyBitfieldLen, + BadAggregationBitfieldLen, + BadSignature, + ValidatorUnknown, + IncludedTooEarly, + IncludedTooLate, + BadTargetEpoch, +} + +#[derive(PartialEq)] +/// Enum used for passing test options to builder +pub enum AttesterSlashingTestTask { + Valid, + NotSlashable, + IndexedAttestation1Invalid, + IndexedAttestation2Invalid, +} + +/// Enum used for passing test options to builder +#[derive(PartialEq)] +pub enum ProposerSlashingTestTask { + Valid, + ProposerUnknown, + ProposalEpochMismatch, + ProposalsIdentical, + ProposerNotSlashable, + BadProposal1Signature, + BadProposal2Signature, +} + impl TestingBeaconBlockBuilder { /// Create a new builder from genesis. pub fn new(spec: &ChainSpec) -> Self { @@ -61,13 +131,14 @@ impl TestingBeaconBlockBuilder { /// Inserts a signed, valid `ProposerSlashing` for the validator. pub fn insert_proposer_slashing( &mut self, + test_task: &ProposerSlashingTestTask, validator_index: u64, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec, ) { let proposer_slashing = - build_proposer_slashing::(validator_index, secret_key, fork, spec); + build_proposer_slashing::(test_task, validator_index, secret_key, fork, spec); self.block .body .proposer_slashings @@ -78,18 +149,20 @@ impl TestingBeaconBlockBuilder { /// Inserts a signed, valid `AttesterSlashing` for each validator index in `validator_indices`. pub fn insert_attester_slashing( &mut self, + test_task: &AttesterSlashingTestTask, validator_indices: &[u64], secret_keys: &[&SecretKey], fork: &Fork, spec: &ChainSpec, ) { - let attester_slashing = - build_double_vote_attester_slashing(validator_indices, secret_keys, fork, spec); - self.block - .body - .attester_slashings - .push(attester_slashing) - .unwrap(); + let attester_slashing = build_double_vote_attester_slashing( + test_task, + validator_indices, + secret_keys, + fork, + spec, + ); + let _ = self.block.body.attester_slashings.push(attester_slashing); } /// Fills the block with `num_attestations` attestations. @@ -103,6 +176,7 @@ impl TestingBeaconBlockBuilder { /// to aggregate these split attestations. pub fn insert_attestations( &mut self, + test_task: &AttestationTestTask, state: &BeaconState, secret_keys: &[&SecretKey], num_attestations: usize, @@ -175,14 +249,16 @@ impl TestingBeaconBlockBuilder { let attestations: Vec<_> = committees .par_iter() .map(|(slot, committee, signing_validators, shard)| { - let mut builder = - TestingAttestationBuilder::new(state, committee, *slot, *shard, spec); + let mut builder = TestingAttestationBuilder::new( + test_task, state, committee, *slot, *shard, spec, + ); let signing_secret_keys: Vec<&SecretKey> = signing_validators .iter() .map(|validator_index| secret_keys[*validator_index]) .collect(); builder.sign( + test_task, signing_validators, &signing_secret_keys, &state.fork, @@ -202,47 +278,113 @@ impl TestingBeaconBlockBuilder { } /// Insert a `Valid` deposit into the state. - pub fn insert_deposit( + pub fn insert_deposits( &mut self, amount: u64, + test_task: DepositTestTask, // TODO: deal with the fact deposits no longer have explicit indices _index: u64, - state: &BeaconState, + num_deposits: u64, + state: &mut BeaconState, spec: &ChainSpec, ) { - let keypair = Keypair::random(); + // Vector containing deposits' data + let mut datas = vec![]; + for _ in 0..num_deposits { + let keypair = Keypair::random(); - let mut builder = TestingDepositBuilder::new(keypair.pk.clone(), amount); - builder.sign( - &keypair, - state.slot.epoch(T::slots_per_epoch()), - &state.fork, - spec, - ); + let mut builder = TestingDepositBuilder::new(keypair.pk.clone(), amount); + builder.sign( + &test_task, + &keypair, + state.slot.epoch(T::slots_per_epoch()), + &state.fork, + spec, + ); + datas.push(builder.build().data); + } - self.block.body.deposits.push(builder.build()).unwrap() + // Vector containing all leaves + let leaves = datas + .iter() + .map(|data| Hash256::from_slice(&data.tree_hash_root())) + .collect::>(); + + // Building a VarList from leaves + let deposit_data_list = VariableList::<_, U4294967296>::from(leaves.clone()); + + // Setting the deposit_root to be the tree_hash_root of the VarList + state.eth1_data.deposit_root = Hash256::from_slice(&deposit_data_list.tree_hash_root()); + + // Building the merkle tree used for generating proofs + let tree = MerkleTree::create(&leaves[..], spec.deposit_contract_tree_depth as usize); + + // Building proofs + let mut proofs = vec![]; + for i in 0..leaves.len() { + let (_, mut proof) = tree.generate_proof(i, spec.deposit_contract_tree_depth as usize); + proof.push(Hash256::from_slice(&int_to_bytes32(leaves.len() as u64))); + proofs.push(proof); + } + + // Building deposits + let deposits = datas + .into_par_iter() + .zip(proofs.into_par_iter()) + .map(|(data, proof)| (data, proof.into())) + .map(|(data, proof)| Deposit { proof, data }) + .collect::>(); + + // Pushing deposits to block body + for deposit in deposits { + let _ = self.block.body.deposits.push(deposit); + } + + // Manually setting the deposit_count to process deposits + // This is for test purposes only + if test_task == DepositTestTask::NoReset { + state.eth1_data.deposit_count += num_deposits; + } else { + state.eth1_deposit_index = 0; + state.eth1_data.deposit_count = num_deposits; + } } /// Insert a `Valid` exit into the state. pub fn insert_exit( &mut self, - state: &BeaconState, - validator_index: u64, + test_task: &ExitTestTask, + state: &mut BeaconState, + mut validator_index: u64, secret_key: &SecretKey, spec: &ChainSpec, ) { - let mut builder = TestingVoluntaryExitBuilder::new( - state.slot.epoch(T::slots_per_epoch()), - validator_index, - ); + let sk = &mut secret_key.clone(); + let mut exit_epoch = state.slot.epoch(T::slots_per_epoch()); - builder.sign(secret_key, &state.fork, spec); + match test_task { + ExitTestTask::BadSignature => *sk = SecretKey::random(), + ExitTestTask::ValidatorUnknown => validator_index = 4242, + ExitTestTask::AlreadyExited => { + state.validators[validator_index as usize].exit_epoch = Epoch::from(314_159 as u64) + } + ExitTestTask::NotActive => { + state.validators[validator_index as usize].activation_epoch = + Epoch::from(314_159 as u64) + } + ExitTestTask::FutureEpoch => exit_epoch = spec.far_future_epoch, + _ => (), + } + + let mut builder = TestingVoluntaryExitBuilder::new(exit_epoch, validator_index); + + builder.sign(sk, &state.fork, spec); self.block .body .voluntary_exits .push(builder.build()) - .unwrap() + .unwrap(); } /// Insert a `Valid` transfer into the state. @@ -280,6 +422,7 @@ impl TestingBeaconBlockBuilder { /// /// Signs the message using a `BeaconChainHarness`. fn build_proposer_slashing( + test_task: &ProposerSlashingTestTask, validator_index: u64, secret_key: &SecretKey, fork: &Fork, @@ -290,13 +433,14 @@ fn build_proposer_slashing( Signature::new(message, domain, secret_key) }; - TestingProposerSlashingBuilder::double_vote::(validator_index, signer) + TestingProposerSlashingBuilder::double_vote::(test_task, validator_index, signer) } /// Builds an `AttesterSlashing` for some `validator_indices`. /// /// Signs the message using a `BeaconChainHarness`. fn build_double_vote_attester_slashing( + test_task: &AttesterSlashingTestTask, validator_indices: &[u64], secret_keys: &[&SecretKey], fork: &Fork, @@ -311,5 +455,5 @@ fn build_double_vote_attester_slashing( Signature::new(message, domain, secret_keys[key_index]) }; - TestingAttesterSlashingBuilder::double_vote(validator_indices, signer) + TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer) } diff --git a/eth2/types/src/test_utils/builders/testing_beacon_state_builder.rs b/eth2/types/src/test_utils/builders/testing_beacon_state_builder.rs index 2f4dde7e6..d6aa488f9 100644 --- a/eth2/types/src/test_utils/builders/testing_beacon_state_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_beacon_state_builder.rs @@ -1,5 +1,5 @@ use super::super::{generate_deterministic_keypairs, KeypairsFile}; -use crate::test_utils::TestingPendingAttestationBuilder; +use crate::test_utils::{AttestationTestTask, TestingPendingAttestationBuilder}; use crate::*; use bls::get_withdrawal_credentials; use dirs; @@ -224,6 +224,7 @@ impl TestingBeaconStateBuilder { for crosslink_committee in committees { let mut builder = TestingPendingAttestationBuilder::new( + &AttestationTestTask::Valid, state, crosslink_committee.shard, slot, diff --git a/eth2/types/src/test_utils/builders/testing_deposit_builder.rs b/eth2/types/src/test_utils/builders/testing_deposit_builder.rs index ed08571a7..dcde1a74f 100644 --- a/eth2/types/src/test_utils/builders/testing_deposit_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_deposit_builder.rs @@ -1,3 +1,4 @@ +use crate::test_utils::DepositTestTask; use crate::*; use bls::{get_withdrawal_credentials, PublicKeyBytes, SignatureBytes}; @@ -29,18 +30,41 @@ impl TestingDepositBuilder { /// - `pubkey` to the signing pubkey. /// - `withdrawal_credentials` to the signing pubkey. /// - `proof_of_possession` - pub fn sign(&mut self, keypair: &Keypair, epoch: Epoch, fork: &Fork, spec: &ChainSpec) { + pub fn sign( + &mut self, + test_task: &DepositTestTask, + keypair: &Keypair, + epoch: Epoch, + fork: &Fork, + spec: &ChainSpec, + ) { + let new_key = Keypair::random(); + let mut pubkeybytes = PublicKeyBytes::from(keypair.pk.clone()); + let mut secret_key = keypair.sk.clone(); + + match test_task { + DepositTestTask::BadPubKey => pubkeybytes = PublicKeyBytes::from(new_key.pk.clone()), + DepositTestTask::InvalidPubKey => { + // Creating invalid public key bytes + let mut public_key_bytes: Vec = vec![0; 48]; + public_key_bytes[0] = 255; + pubkeybytes = PublicKeyBytes::from_bytes(&public_key_bytes).unwrap(); + } + DepositTestTask::BadSig => secret_key = new_key.sk, + _ => (), + } + let withdrawal_credentials = Hash256::from_slice( &get_withdrawal_credentials(&keypair.pk, spec.bls_withdrawal_prefix_byte)[..], ); - self.deposit.data.pubkey = PublicKeyBytes::from(keypair.pk.clone()); + // Building the data and signing it + self.deposit.data.pubkey = pubkeybytes; self.deposit.data.withdrawal_credentials = withdrawal_credentials; - self.deposit.data.signature = self.deposit .data - .create_signature(&keypair.sk, epoch, fork, spec); + .create_signature(&secret_key, epoch, fork, spec); } /// Builds the deposit, consuming the builder. diff --git a/eth2/types/src/test_utils/builders/testing_pending_attestation_builder.rs b/eth2/types/src/test_utils/builders/testing_pending_attestation_builder.rs index 14fe9a5f9..77cb6a302 100644 --- a/eth2/types/src/test_utils/builders/testing_pending_attestation_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_pending_attestation_builder.rs @@ -1,4 +1,4 @@ -use crate::test_utils::TestingAttestationDataBuilder; +use crate::test_utils::{AttestationTestTask, TestingAttestationDataBuilder}; use crate::*; /// Builds an `AttesterSlashing` to be used for testing purposes. @@ -15,8 +15,14 @@ impl TestingPendingAttestationBuilder { /// /// * The aggregation and custody bitfields will all be empty, they need to be set with /// `Self::add_committee_participation`. - pub fn new(state: &BeaconState, shard: u64, slot: Slot, spec: &ChainSpec) -> Self { - let data_builder = TestingAttestationDataBuilder::new(state, shard, slot, spec); + pub fn new( + test_task: &AttestationTestTask, + state: &BeaconState, + shard: u64, + slot: Slot, + spec: &ChainSpec, + ) -> Self { + let data_builder = TestingAttestationDataBuilder::new(test_task, state, shard, slot, spec); let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), slot.epoch(T::slots_per_epoch())) diff --git a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs index b97293427..0c14f0a75 100644 --- a/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs +++ b/eth2/types/src/test_utils/builders/testing_proposer_slashing_builder.rs @@ -1,3 +1,4 @@ +use crate::test_utils::ProposerSlashingTestTask; use crate::*; use tree_hash::SignedRoot; @@ -17,14 +18,22 @@ impl TestingProposerSlashingBuilder { /// - `domain: Domain` /// /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`). - pub fn double_vote(proposer_index: u64, signer: F) -> ProposerSlashing + pub fn double_vote( + test_task: &ProposerSlashingTestTask, + mut proposer_index: u64, + signer: F, + ) -> ProposerSlashing where T: EthSpec, F: Fn(u64, &[u8], Epoch, Domain) -> Signature, { let slot = Slot::new(0); let hash_1 = Hash256::from([1; 32]); - let hash_2 = Hash256::from([2; 32]); + let hash_2 = if *test_task == ProposerSlashingTestTask::ProposalsIdentical { + hash_1.clone() + } else { + Hash256::from([2; 32]) + }; let mut header_1 = BeaconBlockHeader { slot, @@ -34,22 +43,37 @@ impl TestingProposerSlashingBuilder { signature: Signature::empty_signature(), }; + let slot_2 = if *test_task == ProposerSlashingTestTask::ProposalEpochMismatch { + Slot::new(128) + } else { + Slot::new(0) + }; + let mut header_2 = BeaconBlockHeader { parent_root: hash_2, + slot: slot_2, ..header_1.clone() }; let epoch = slot.epoch(T::slots_per_epoch()); - header_1.signature = { - let message = header_1.signed_root(); - signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) - }; + if *test_task != ProposerSlashingTestTask::BadProposal1Signature { + header_1.signature = { + let message = header_1.signed_root(); + signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) + }; + } - header_2.signature = { - let message = header_2.signed_root(); - signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) - }; + if *test_task != ProposerSlashingTestTask::BadProposal2Signature { + header_2.signature = { + let message = header_2.signed_root(); + signer(proposer_index, &message[..], epoch, Domain::BeaconProposer) + }; + } + + if *test_task == ProposerSlashingTestTask::ProposerUnknown { + proposer_index = 3_141_592; + } ProposerSlashing { proposer_index,