BlockProcessing testing (#559)

* Add valid_deposit test and build_with_deposit method

* Insert_deposit takes a num_deposit param

* Deposit with spec.max_effective_balance

* Copy int_to_bytes32 implem from beacon_chain_builder

* Add debug information to insert_deposit

* Remove length-proof assertion

* Insert_deposit displays error

* Batch deposits now pass tests

* Optimize insert_deposit

* Rename insert_deposits and set num_deposits to 2 in valid_deposit test

* update test_utils to pass tests

* fix typo in test_utils

* update cast in test_utils

* Add DepositCountInvalid tests

* Add tests for bad deposit signature

* Add tests and test utils in test_builder

* Return error instead of ok on bad signature

* Update DepositTestTask enum

* Add comment about manually setting deposit_count and deposit_index

* add badblsbytes test

* add bad_index var for clarity ; remove underflow test

* cargo fmt

* Add insert 3 exits tests

* Add validator_unknwon test

* Add far_future_epoch test and already exited

* Add MaxVoluntaryExits + 1 test

* Add exit_already_initiated test

* Add exit_not_active test

* Add too_young_to_leave test

* Cargo fmt

* Confirm already_anitiated test

* Fix typo in enum variant

* Adjust some tests to return ok(()) and revert changes for early return in per_block_processing.rs

* cargo fmt

* Adjust AlreadyIniated test to expect Ok(()) and revert changes in per_block_processing.rs

* Remove extraneous newline

* Add insert_valid_attester_slashing

* Initial cargo fmt

* Add NotSlashable test

* Cargo fmt

* Remove AttestationDataIdentical

* Make test_task pass through reference ; fix max_attester_slashing_plus_one test

* Initial cargo fmt

* Add InvalidIndexedAttestation1 and 2

* Add comments

* Add ProposalsIdenticalTest

* Add ProposalsIdentical test

* Cargo fmt

* Add ProposerUnknown test

* Add ProposalEpochMismatch test

* Add BadProposal1Signature and Badproposal2Signature tests

* Add ProposerNotSlashable test

* Derive PartialEq and use if instead of match

* Merge attestation tests

* Remove useless AlreadyInitiated variant in beacon_state

* Remove MaxOperations plus one tests for each operation

* Clean comments

* add IncludedTooLate and BadTargetEpoch tests

* Update AttestationDataBuilder call in operation_pool testing

* Cargo fmt

* Remove BadIndex enum variant, unused in the code

* Cargo fmt

* Cargo fmt updated

* simply increment deposit_count instead of hardsetting deposit_index in insert_deposits

* Fix bad_merkle_proof when calling insert_deposits
This commit is contained in:
pscott 2019-11-12 06:09:33 +01:00 committed by Paul Hauner
parent 613fdbeda6
commit c7b3a7abd8
16 changed files with 1936 additions and 118 deletions

View File

@ -619,13 +619,28 @@ mod tests {
spec: &ChainSpec,
extra_signer: Option<usize>,
) -> Attestation<E> {
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::<Vec<_>>();
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,

View File

@ -98,11 +98,31 @@ mod test {
.map(|validator_index| &keypairs[*validator_index].sk)
.collect::<Vec<_>>();
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();

View File

@ -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<T: EthSpec> {
@ -30,6 +34,252 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
self.state_builder.build_caches(&spec).unwrap();
}
pub fn build_with_n_deposits(
mut self,
num_deposits: u64,
test_task: DepositTestTask,
randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>,
spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) {
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<SecretKey>,
previous_block_root: Option<Hash256>,
spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) {
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<SecretKey>,
previous_block_root: Option<Hash256>,
spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) {
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<SecretKey>,
previous_block_root: Option<Hash256>,
spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) {
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<SecretKey>,
previous_block_root: Option<Hash256>,
spec: &ChainSpec,
) -> (BeaconBlock<T>, BeaconState<T>) {
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<SecretKey>,

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@ pub fn verify_attestation_for_state<T: EthSpec>(
spec: &ChainSpec,
) -> Result<()> {
let data = &attestation.data;
verify!(
data.crosslink.shard < T::ShardCount::to_u64(),
Invalid::BadShard

View File

@ -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<T: EthSpec> {
@ -77,6 +80,7 @@ impl<T: EthSpec> BlockBuilder<T> {
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<T: EthSpec> BlockBuilder<T> {
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<T: EthSpec> BlockBuilder<T> {
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<T: EthSpec> BlockBuilder<T> {
);
// Insert `Deposit` objects.
for i in 0..self.num_deposits {
builder.insert_deposit(
builder.insert_deposits(
32_000_000_000,
state.eth1_data.deposit_count + (i as u64),
&state,
DepositTestTask::NoReset,
state.eth1_data.deposit_count,
self.num_deposits as u64,
&mut state,
spec,
);
}
state.eth1_data.deposit_count += self.num_deposits as u64;
info!("Inserted {} deposits.", builder.block.body.deposits.len());
// Insert the maximum possible number of `Exit` objects.
@ -141,7 +151,8 @@ impl<T: EthSpec> BlockBuilder<T> {
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,

View File

@ -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"

View File

@ -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<T: EthSpec> {
impl<T: EthSpec> TestingAttestationBuilder<T> {
/// Create a new attestation builder.
pub fn new(
test_task: &AttestationTestTask,
state: &BeaconState<T>,
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<T: EthSpec> TestingAttestationBuilder<T> {
/// 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,17 +77,25 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
.position(|v| *v == *validator_index)
.expect("Signing validator not in attestation committee");
match test_task {
AttestationTestTask::BadIndexedAttestationBadSignature => (),
AttestationTestTask::CustodyBitfieldNotSubset => custody_bit = true,
_ => {
self.attestation
.aggregation_bits
.set(committee_index, true)
.unwrap();
if custody_bit {
}
}
match (custody_bit, test_task) {
(true, _) | (_, AttestationTestTask::CustodyBitfieldHasSetBits) => {
self.attestation
.custody_bits
.set(committee_index, true)
.unwrap();
}
(false, _) => (),
}
let message = AttestationDataAndCustodyBit {
data: self.attestation.data.clone(),
@ -91,7 +109,12 @@ impl<T: EthSpec> TestingAttestationBuilder<T> {
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)
}

View File

@ -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<T: EthSpec>(
test_task: &AttestationTestTask,
state: &BeaconState<T>,
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 crosslink = Crosslink {
shard,
parent_root: Hash256::from_slice(&parent_crosslink.tree_hash_root()),
start_epoch: parent_crosslink.end_epoch,
end_epoch: std::cmp::min(
let mut start = parent_crosslink.end_epoch;
let mut end = std::cmp::min(
target.epoch,
parent_crosslink.end_epoch + spec.max_epochs_per_crosslink,
),
data_root: Hash256::zero(),
);
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,
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,

View File

@ -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<F, T: EthSpec>(validator_indices: &[u64], signer: F) -> AttesterSlashing<T>
pub fn double_vote<F, T: EthSpec>(
test_task: &AttesterSlashingTestTask,
validator_indices: &[u64],
signer: F,
) -> AttesterSlashing<T>
where
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
{
@ -49,21 +54,37 @@ impl TestingAttesterSlashingBuilder {
crosslink,
};
let data_2 = AttestationData {
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(),
};

View File

@ -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<T: EthSpec> {
pub block: BeaconBlock<T>,
}
/// 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<T: EthSpec> TestingBeaconBlockBuilder<T> {
/// Create a new builder from genesis.
pub fn new(spec: &ChainSpec) -> Self {
@ -61,13 +131,14 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
/// 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::<T>(validator_index, secret_key, fork, spec);
build_proposer_slashing::<T>(test_task, validator_index, secret_key, fork, spec);
self.block
.body
.proposer_slashings
@ -78,18 +149,20 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
/// 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<T: EthSpec> TestingBeaconBlockBuilder<T> {
/// to aggregate these split attestations.
pub fn insert_attestations(
&mut self,
test_task: &AttestationTestTask,
state: &BeaconState<T>,
secret_keys: &[&SecretKey],
num_attestations: usize,
@ -175,14 +249,16 @@ impl<T: EthSpec> TestingBeaconBlockBuilder<T> {
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<T: EthSpec> TestingBeaconBlockBuilder<T> {
}
/// 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<T>,
num_deposits: u64,
state: &mut BeaconState<T>,
spec: &ChainSpec,
) {
// 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(
&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::<Vec<_>>();
// 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::<Vec<_>>();
// 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<T>,
validator_index: u64,
test_task: &ExitTestTask,
state: &mut BeaconState<T>,
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<T: EthSpec> TestingBeaconBlockBuilder<T> {
///
/// Signs the message using a `BeaconChainHarness`.
fn build_proposer_slashing<T: EthSpec>(
test_task: &ProposerSlashingTestTask,
validator_index: u64,
secret_key: &SecretKey,
fork: &Fork,
@ -290,13 +433,14 @@ fn build_proposer_slashing<T: EthSpec>(
Signature::new(message, domain, secret_key)
};
TestingProposerSlashingBuilder::double_vote::<T, _>(validator_index, signer)
TestingProposerSlashingBuilder::double_vote::<T, _>(test_task, validator_index, signer)
}
/// Builds an `AttesterSlashing` for some `validator_indices`.
///
/// Signs the message using a `BeaconChainHarness`.
fn build_double_vote_attester_slashing<T: EthSpec>(
test_task: &AttesterSlashingTestTask,
validator_indices: &[u64],
secret_keys: &[&SecretKey],
fork: &Fork,
@ -311,5 +455,5 @@ fn build_double_vote_attester_slashing<T: EthSpec>(
Signature::new(message, domain, secret_keys[key_index])
};
TestingAttesterSlashingBuilder::double_vote(validator_indices, signer)
TestingAttesterSlashingBuilder::double_vote(test_task, validator_indices, signer)
}

View File

@ -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<T: EthSpec> TestingBeaconStateBuilder<T> {
for crosslink_committee in committees {
let mut builder = TestingPendingAttestationBuilder::new(
&AttestationTestTask::Valid,
state,
crosslink_committee.shard,
slot,

View File

@ -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<u8> = 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.

View File

@ -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<T: EthSpec> TestingPendingAttestationBuilder<T> {
///
/// * The aggregation and custody bitfields will all be empty, they need to be set with
/// `Self::add_committee_participation`.
pub fn new(state: &BeaconState<T>, shard: u64, slot: Slot, spec: &ChainSpec) -> Self {
let data_builder = TestingAttestationDataBuilder::new(state, shard, slot, spec);
pub fn new(
test_task: &AttestationTestTask,
state: &BeaconState<T>,
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()))

View File

@ -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<T, F>(proposer_index: u64, signer: F) -> ProposerSlashing
pub fn double_vote<T, F>(
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());
if *test_task != ProposerSlashingTestTask::BadProposal1Signature {
header_1.signature = {
let message = header_1.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,