Update to spec v0.9.1 (#597)

* Update to spec v0.9.0

* Update to v0.9.1

* Bump spec tags for v0.9.1

* Formatting, fix CI failures

* Resolve accidental KeyPair merge conflict

* Document new BeaconState functions

* Fix incorrect cache drops in `advance_caches`

* Update fork choice for v0.9.1

* Clean up some FIXMEs

* Fix a few docs/logs
This commit is contained in:
Michael Sproul
2019-11-21 11:47:30 +11:00
committed by GitHub
parent b7a0feb725
commit 24e941d175
105 changed files with 1211 additions and 2940 deletions

View File

@@ -3,19 +3,13 @@ use types::*;
/// Returns validator indices which participated in the attestation, sorted by increasing index.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn get_attesting_indices<T: EthSpec>(
state: &BeaconState<T>,
attestation_data: &AttestationData,
bitlist: &BitList<T::MaxValidatorsPerCommittee>,
) -> Result<BTreeSet<usize>, BeaconStateError> {
let target_relative_epoch =
RelativeEpoch::from_epoch(state.current_epoch(), attestation_data.target.epoch)?;
let committee = state.get_crosslink_committee_for_shard(
attestation_data.crosslink.shard,
target_relative_epoch,
)?;
let committee = state.get_beacon_committee(attestation_data.slot, attestation_data.index)?;
if bitlist.len() != committee.committee.len() {
return Err(BeaconStateError::InvalidBitfield);

View File

@@ -1,41 +0,0 @@
use tree_hash::TreeHash;
use types::*;
/// Return the compact committee root at `relative_epoch`.
///
/// Spec v0.8.3
pub fn get_compact_committees_root<T: EthSpec>(
state: &BeaconState<T>,
relative_epoch: RelativeEpoch,
spec: &ChainSpec,
) -> Result<Hash256, BeaconStateError> {
let mut committees =
FixedVector::<_, T::ShardCount>::from_elem(CompactCommittee::<T>::default());
let start_shard = state.get_epoch_start_shard(relative_epoch)?;
for committee_number in 0..state.get_committee_count(relative_epoch)? {
let shard = (start_shard + committee_number) % T::ShardCount::to_u64();
for &index in state
.get_crosslink_committee_for_shard(shard, relative_epoch)?
.committee
{
let validator = state
.validators
.get(index)
.ok_or(BeaconStateError::UnknownValidator)?;
committees[shard as usize]
.pubkeys
.push(validator.pubkey.clone())?;
let compact_balance = validator.effective_balance / spec.effective_balance_increment;
// `index` (top 6 bytes) + `slashed` (16th bit) + `compact_balance` (bottom 15 bits)
let compact_validator: u64 =
((index as u64) << 16) + (u64::from(validator.slashed) << 15) + compact_balance;
committees[shard as usize]
.compact_validators
.push(compact_validator)?;
}
}
Ok(Hash256::from_slice(&committees.tree_hash_root()))
}

View File

@@ -6,139 +6,19 @@ type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
/// Convert `attestation` to (almost) indexed-verifiable form.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn get_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation<T>,
) -> Result<IndexedAttestation<T>> {
// Note: we rely on both calls to `get_attesting_indices` to check the bitfield lengths
// against the committee length
let attesting_indices =
get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;
let custody_bit_1_indices =
get_attesting_indices(state, &attestation.data, &attestation.custody_bits)?;
verify!(
custody_bit_1_indices.is_subset(&attesting_indices),
Invalid::CustodyBitfieldNotSubset
);
let custody_bit_0_indices = &attesting_indices - &custody_bit_1_indices;
Ok(IndexedAttestation {
custody_bit_0_indices: VariableList::new(
custody_bit_0_indices
.into_iter()
.map(|x| x as u64)
.collect(),
)?,
custody_bit_1_indices: VariableList::new(
custody_bit_1_indices
.into_iter()
.map(|x| x as u64)
.collect(),
attesting_indices: VariableList::new(
attesting_indices.into_iter().map(|x| x as u64).collect(),
)?,
data: attestation.data.clone(),
signature: attestation.signature.clone(),
})
}
#[cfg(test)]
mod test {
use super::*;
use itertools::{Either, Itertools};
use types::test_utils::*;
#[test]
fn custody_bitfield_indexing() {
let validator_count = 128;
let spec = MinimalEthSpec::default_spec();
let state_builder =
TestingBeaconStateBuilder::<MinimalEthSpec>::from_default_keypairs_file_if_exists(
validator_count,
&spec,
);
let (mut state, keypairs) = state_builder.build();
state.build_all_caches(&spec).unwrap();
state.slot += 1;
let shard = 0;
let cc = state
.get_crosslink_committee_for_shard(shard, RelativeEpoch::Current)
.unwrap();
// Make a third of the validators sign with custody bit 0, a third with custody bit 1
// and a third not sign at all.
assert!(
cc.committee.len() >= 4,
"need at least 4 validators per committee for this test to work"
);
let (mut bit_0_indices, mut bit_1_indices): (Vec<_>, Vec<_>) = cc
.committee
.iter()
.enumerate()
.filter(|(i, _)| i % 3 != 0)
.partition_map(|(i, index)| {
if i % 3 == 1 {
Either::Left(*index)
} else {
Either::Right(*index)
}
});
assert!(!bit_0_indices.is_empty());
assert!(!bit_1_indices.is_empty());
let bit_0_keys = bit_0_indices
.iter()
.map(|validator_index| &keypairs[*validator_index].sk)
.collect::<Vec<_>>();
let bit_1_keys = bit_1_indices
.iter()
.map(|validator_index| &keypairs[*validator_index].sk)
.collect::<Vec<_>>();
let mut attestation_builder = TestingAttestationBuilder::new(
&AttestationTestTask::Valid,
&state,
&cc.committee,
cc.slot,
shard,
&spec,
);
attestation_builder
.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();
bit_0_indices.sort();
bit_1_indices.sort();
assert!(indexed_attestation
.custody_bit_0_indices
.iter()
.copied()
.eq(bit_0_indices.iter().map(|idx| *idx as u64)));
assert!(indexed_attestation
.custody_bit_1_indices
.iter()
.copied()
.eq(bit_1_indices.iter().map(|idx| *idx as u64)));
}
}

View File

@@ -3,7 +3,7 @@ use types::{BeaconStateError as Error, *};
/// Initiate the exit of the validator of the given `index`.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn initiate_validator_exit<T: EthSpec>(
state: &mut BeaconState<T>,
index: usize,

View File

@@ -1,11 +1,9 @@
mod get_attesting_indices;
mod get_compact_committees_root;
mod get_indexed_attestation;
mod initiate_validator_exit;
mod slash_validator;
pub use get_attesting_indices::get_attesting_indices;
pub use get_compact_committees_root::get_compact_committees_root;
pub use get_indexed_attestation::get_indexed_attestation;
pub use initiate_validator_exit::initiate_validator_exit;
pub use slash_validator::slash_validator;

View File

@@ -4,7 +4,7 @@ use types::{BeaconStateError as Error, *};
/// Slash the validator with index ``index``.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn slash_validator<T: EthSpec>(
state: &mut BeaconState<T>,
slashed_index: usize,
@@ -35,8 +35,7 @@ pub fn slash_validator<T: EthSpec>(
);
// Apply proposer and whistleblower rewards
let proposer_index =
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)?;
let proposer_index = state.get_beacon_proposer_index(state.slot, spec)?;
let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index);
let whistleblower_reward = validator_effective_balance / spec.whistleblower_reward_quotient;
let proposer_reward = whistleblower_reward / spec.proposer_reward_quotient;

View File

@@ -1,12 +1,11 @@
use super::per_block_processing::{errors::BlockProcessingError, process_deposit};
use crate::common::get_compact_committees_root;
use tree_hash::TreeHash;
use types::typenum::U4294967296;
use types::*;
/// Initialize a `BeaconState` from genesis data.
///
/// Spec v0.8.0
/// Spec v0.9.1
// TODO: this is quite inefficient and we probably want to rethink how we do this
pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
eth1_block_hash: Hash256,
@@ -24,6 +23,9 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
};
let mut state = BeaconState::new(genesis_time, eth1_data, spec);
// Seed RANDAO with Eth1 entropy
state.fill_randao_mixes_with(eth1_block_hash);
// Process deposits
let leaves: Vec<_> = deposits
.iter()
@@ -40,21 +42,12 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
// Now that we have our validators, initialize the caches (including the committees)
state.build_all_caches(spec)?;
// Populate active_index_roots and compact_committees_roots
let indices_list = VariableList::<usize, T::ValidatorRegistryLimit>::from(
state.get_active_validator_indices(T::genesis_epoch()),
);
let active_index_root = Hash256::from_slice(&indices_list.tree_hash_root());
let committee_root = get_compact_committees_root(&state, RelativeEpoch::Current, spec)?;
state.fill_active_index_roots_with(active_index_root);
state.fill_compact_committees_roots_with(committee_root);
Ok(state)
}
/// Determine whether a candidate genesis state is suitable for starting the chain.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn is_valid_genesis_state<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> bool {
state.genesis_time >= spec.min_genesis_time
&& state.get_active_validator_indices(T::genesis_epoch()).len() as u64

View File

@@ -2,9 +2,7 @@ use crate::common::{initiate_validator_exit, slash_validator};
use errors::{BlockOperationError, BlockProcessingError, HeaderInvalid, IntoWithIndex};
use rayon::prelude::*;
use signature_sets::{block_proposal_signature_set, randao_signature_set};
use std::collections::HashSet;
use std::convert::TryInto;
use std::iter::FromIterator;
use tree_hash::SignedRoot;
use types::*;
@@ -21,9 +19,6 @@ pub use verify_deposit::{
get_existing_validator_index, verify_deposit_merkle_proof, verify_deposit_signature,
};
pub use verify_exit::{verify_exit, verify_exit_time_independent_only};
pub use verify_transfer::{
execute_transfer, verify_transfer, verify_transfer_time_independent_only,
};
pub mod block_processing_builder;
mod block_signature_verifier;
@@ -36,7 +31,6 @@ mod verify_attester_slashing;
mod verify_deposit;
mod verify_exit;
mod verify_proposer_slashing;
mod verify_transfer;
/// The strategy to be used when validating the block's signatures.
#[derive(PartialEq, Clone, Copy)]
@@ -74,7 +68,7 @@ impl VerifySignatures {
/// signature. If it is `None` the signed root is calculated here. This parameter only exists to
/// avoid re-calculating the root when it is already known.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn per_block_processing<T: EthSpec>(
mut state: &mut BeaconState<T>,
block: &BeaconBlock<T>,
@@ -128,14 +122,13 @@ pub fn per_block_processing<T: EthSpec>(
verify_signatures,
spec,
)?;
process_transfers(&mut state, &block.body.transfers, verify_signatures, spec)?;
Ok(())
}
/// Processes the block header.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_block_header<T: EthSpec>(
state: &mut BeaconState<T>,
block: &BeaconBlock<T>,
@@ -158,7 +151,7 @@ pub fn process_block_header<T: EthSpec>(
state.latest_block_header = block.temporary_block_header();
// Verify proposer is not slashed
let proposer_idx = state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?;
let proposer_idx = state.get_beacon_proposer_index(block.slot, spec)?;
let proposer = &state.validators[proposer_idx];
verify!(
!proposer.slashed,
@@ -174,7 +167,7 @@ pub fn process_block_header<T: EthSpec>(
/// Verifies the signature of a block.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_block_signature<T: EthSpec>(
state: &BeaconState<T>,
block: &BeaconBlock<T>,
@@ -192,7 +185,7 @@ pub fn verify_block_signature<T: EthSpec>(
/// Verifies the `randao_reveal` against the block's proposer pubkey and updates
/// `state.latest_randao_mixes`.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_randao<T: EthSpec>(
state: &mut BeaconState<T>,
block: &BeaconBlock<T>,
@@ -215,7 +208,7 @@ pub fn process_randao<T: EthSpec>(
/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_eth1_data<T: EthSpec>(
state: &mut BeaconState<T>,
eth1_data: &Eth1Data,
@@ -240,7 +233,7 @@ pub fn process_eth1_data<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_proposer_slashings<T: EthSpec>(
state: &mut BeaconState<T>,
proposer_slashings: &[ProposerSlashing],
@@ -269,7 +262,7 @@ pub fn process_proposer_slashings<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_attester_slashings<T: EthSpec>(
state: &mut BeaconState<T>,
attester_slashings: &[AttesterSlashing<T>],
@@ -323,7 +316,7 @@ pub fn process_attester_slashings<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_attestations<T: EthSpec>(
state: &mut BeaconState<T>,
attestations: &[Attestation<T>],
@@ -343,14 +336,12 @@ pub fn process_attestations<T: EthSpec>(
})?;
// Update the state in series.
let proposer_index =
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)? as u64;
let proposer_index = state.get_beacon_proposer_index(state.slot, spec)? as u64;
for attestation in attestations {
let attestation_slot = state.get_attestation_data_slot(&attestation.data)?;
let pending_attestation = PendingAttestation {
aggregation_bits: attestation.aggregation_bits.clone(),
data: attestation.data.clone(),
inclusion_delay: (state.slot - attestation_slot).as_u64(),
inclusion_delay: (state.slot - attestation.data.slot).as_u64(),
proposer_index,
};
@@ -371,7 +362,7 @@ pub fn process_attestations<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_deposits<T: EthSpec>(
state: &mut BeaconState<T>,
deposits: &[Deposit],
@@ -408,7 +399,7 @@ pub fn process_deposits<T: EthSpec>(
/// Process a single deposit, optionally verifying its merkle proof.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn process_deposit<T: EthSpec>(
state: &mut BeaconState<T>,
deposit: &Deposit,
@@ -474,7 +465,7 @@ pub fn process_deposit<T: EthSpec>(
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_exits<T: EthSpec>(
state: &mut BeaconState<T>,
voluntary_exits: &[VoluntaryExit],
@@ -496,39 +487,3 @@ pub fn process_exits<T: EthSpec>(
Ok(())
}
/// Validates each `Transfer` and updates the state, short-circuiting on an invalid object.
///
/// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns
/// an `Err` describing the invalid object or cause of failure.
///
/// Spec v0.8.0
pub fn process_transfers<T: EthSpec>(
state: &mut BeaconState<T>,
transfers: &[Transfer],
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
let expected_transfers = HashSet::<_>::from_iter(transfers).len();
// Verify that there are no duplicate transfers
block_verify!(
transfers.len() == expected_transfers,
BlockProcessingError::DuplicateTransfers {
duplicates: transfers.len().saturating_sub(expected_transfers)
}
);
transfers
.par_iter()
.enumerate()
.try_for_each(|(i, transfer)| {
verify_transfer(&state, transfer, verify_signatures, spec)
.map_err(|e| e.into_with_index(i))
})?;
for (i, transfer) in transfers.iter().enumerate() {
execute_transfer(state, transfer, spec).map_err(|e| e.into_with_index(i))?;
}
Ok(())
}

View File

@@ -55,9 +55,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {
@@ -99,9 +97,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {
@@ -112,7 +108,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
ExitTestTask::AlreadyInitiated => {
for _ in 0..2 {
self.block_builder.insert_exit(
&test_task,
test_task,
&mut state,
(0 as usize).try_into().unwrap(),
&keypairs[0].sk,
@@ -123,7 +119,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
_ => {
for (i, keypair) in keypairs.iter().take(num_exits).enumerate() {
self.block_builder.insert_exit(
&test_task,
test_task,
&mut state,
(i as usize).try_into().unwrap(),
&keypair.sk,
@@ -140,7 +136,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
pub fn build_with_n_attestations(
mut self,
test_task: &AttestationTestTask,
test_task: AttestationTestTask,
num_attestations: u64,
randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>,
@@ -158,9 +154,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {
@@ -185,7 +179,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
pub fn build_with_attester_slashing(
mut self,
test_task: &AttesterSlashingTestTask,
test_task: AttesterSlashingTestTask,
num_attester_slashings: u64,
randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>,
@@ -203,9 +197,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {
@@ -236,7 +228,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
pub fn build_with_proposer_slashing(
mut self,
test_task: &ProposerSlashingTestTask,
test_task: ProposerSlashingTestTask,
num_proposer_slashings: u64,
randao_sk: Option<SecretKey>,
previous_block_root: Option<Hash256>,
@@ -254,9 +246,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {
@@ -298,9 +288,7 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
)),
}
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let keypair = &keypairs[proposer_index];
match randao_sk {

View File

@@ -86,7 +86,6 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
* Deposits are not included because they can legally have invalid signatures.
*/
verifier.include_exits()?;
verifier.include_transfers()?;
verifier.verify()
}
@@ -209,19 +208,4 @@ impl<'a, T: EthSpec> BlockSignatureVerifier<'a, T> {
Ok(())
}
/// Includes all signatures in `self.block.body.transfers` for verification.
fn include_transfers(&mut self) -> Result<()> {
let mut sets = self
.block
.body
.transfers
.iter()
.map(|transfer| transfer_signature_set(&self.state, transfer, &self.spec))
.collect::<SignatureSetResult<_>>()?;
self.sets.append(&mut sets);
Ok(())
}
}

View File

@@ -16,9 +16,6 @@ pub enum BlockProcessingError {
expected: usize,
found: usize,
},
DuplicateTransfers {
duplicates: usize,
},
HeaderInvalid {
reason: HeaderInvalid,
},
@@ -46,10 +43,6 @@ pub enum BlockProcessingError {
index: usize,
reason: ExitInvalid,
},
TransferInvalid {
index: usize,
reason: TransferInvalid,
},
BeaconStateError(BeaconStateError),
SignatureSetError(SignatureSetError),
SszTypesError(ssz_types::Error),
@@ -119,8 +112,7 @@ impl_into_block_processing_error_with_index!(
IndexedAttestationInvalid,
AttestationInvalid,
DepositInvalid,
ExitInvalid,
TransferInvalid
ExitInvalid
);
pub type HeaderValidationError = BlockOperationError<HeaderInvalid>;
@@ -129,7 +121,6 @@ pub type ProposerSlashingValidationError = BlockOperationError<ProposerSlashingI
pub type AttestationValidationError = BlockOperationError<AttestationInvalid>;
pub type DepositValidationError = BlockOperationError<DepositInvalid>;
pub type ExitValidationError = BlockOperationError<ExitInvalid>;
pub type TransferValidationError = BlockOperationError<TransferInvalid>;
#[derive(Debug, PartialEq)]
pub enum BlockOperationError<T> {
@@ -174,10 +165,10 @@ pub enum HeaderInvalid {
pub enum ProposerSlashingInvalid {
/// The proposer index is not a known validator.
ProposerUnknown(u64),
/// The two proposal have different epochs.
/// The two proposal have different slots.
///
/// (proposal_1_slot, proposal_2_slot)
ProposalEpochMismatch(Slot, Slot),
ProposalSlotMismatch(Slot, Slot),
/// The proposals are identical and therefore not slashable.
ProposalsIdentical,
/// The specified proposer cannot be slashed because they are already slashed, or not active.
@@ -207,8 +198,8 @@ pub enum AttesterSlashingInvalid {
/// Describes why an object is invalid.
#[derive(Debug, PartialEq)]
pub enum AttestationInvalid {
/// Shard exceeds SHARD_COUNT.
BadShard,
/// Commmittee index exceeds number of committees in that slot.
BadCommitteeIndex,
/// Attestation included before the inclusion delay.
IncludedTooEarly {
state: Slot,
@@ -229,36 +220,18 @@ pub enum AttestationInvalid {
attestation: Checkpoint,
is_current: bool,
},
/// Attestation crosslink root does not match the state crosslink root for the attestations
/// slot.
BadParentCrosslinkHash,
/// Attestation crosslink start epoch does not match the end epoch of the state crosslink.
BadParentCrosslinkStartEpoch,
/// Attestation crosslink end epoch does not match the expected value.
BadParentCrosslinkEndEpoch,
/// The custody bitfield has some bits set `true`. This is not allowed in phase 0.
CustodyBitfieldHasSetBits,
/// There are no set bits on the attestation -- an attestation must be signed by at least one
/// validator.
AggregationBitfieldIsEmpty,
/// The custody bitfield length is not the smallest possible size to represent the committee.
BadCustodyBitfieldLength {
committee_len: usize,
bitfield_len: usize,
},
/// The aggregation bitfield length is not the smallest possible size to represent the committee.
BadAggregationBitfieldLength {
committee_len: usize,
bitfield_len: usize,
},
/// The bits set in the custody bitfield are not a subset of those set in the aggregation bits.
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.
ShardBlockRootNotZero,
/// The indexed attestation created from this attestation was found to be invalid.
BadIndexedAttestation(IndexedAttestationInvalid),
}
@@ -280,14 +253,6 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
#[derive(Debug, PartialEq)]
pub enum IndexedAttestationInvalid {
/// The custody bit 0 validators intersect with the bit 1 validators.
CustodyBitValidatorsIntersect,
/// The custody bitfield has some bits set `true`. This is not allowed in phase 0.
CustodyBitfieldHasSetBits,
/// The custody bitfield violated a type-level bound.
CustodyBitfieldBoundsError(ssz_types::Error),
/// No validator indices were specified.
NoValidatorIndices,
/// The number of indices exceeds the global maximum.
///
/// (max_indices, indices_given)
@@ -339,56 +304,3 @@ pub enum ExitInvalid {
/// been invalid or an internal error occurred.
SignatureSetError(SignatureSetError),
}
#[derive(Debug, PartialEq)]
pub enum TransferInvalid {
/// The validator indicated by `transfer.from` is unknown.
FromValidatorUnknown(u64),
/// The validator indicated by `transfer.to` is unknown.
ToValidatorUnknown(u64),
/// The balance of `transfer.from` is insufficient.
///
/// (required, available)
FromBalanceInsufficient(u64, u64),
/// Adding `transfer.fee` to `transfer.amount` causes an overflow.
///
/// (transfer_fee, transfer_amount)
FeeOverflow(u64, u64),
/// This transfer would result in the `transfer.from` account to have `0 < balance <
/// min_deposit_amount`
///
/// (resulting_amount, min_deposit_amount)
SenderDust(u64, u64),
/// This transfer would result in the `transfer.to` account to have `0 < balance <
/// min_deposit_amount`
///
/// (resulting_amount, min_deposit_amount)
RecipientDust(u64, u64),
/// The state slot does not match `transfer.slot`.
///
/// (state_slot, transfer_slot)
StateSlotMismatch(Slot, Slot),
/// The `transfer.slot` is in the past relative to the state slot.
///
///
/// (state_slot, transfer_slot)
TransferSlotInPast(Slot, Slot),
/// The `transfer.from` validator has been activated and is not withdrawable.
///
/// (from_validator)
FromValidatorIneligibleForTransfer(u64),
/// The validators withdrawal credentials do not match `transfer.pubkey`.
///
/// (state_credentials, transfer_pubkey_credentials)
WithdrawalCredentialsMismatch(Hash256, Hash256),
/// The deposit was not signed by `deposit.pubkey`.
BadSignature,
/// Overflow when adding to `transfer.to` balance.
///
/// (to_balance, transfer_amount)
ToBalanceOverflow(u64, u64),
/// Overflow when adding to beacon proposer balance.
///
/// (proposer_balance, transfer_fee)
ProposerBalanceOverflow(u64, u64),
}

View File

@@ -1,8 +1,6 @@
use super::errors::{BlockOperationError, IndexedAttestationInvalid as Invalid};
use super::signature_sets::indexed_attestation_signature_set;
use crate::VerifySignatures;
use std::collections::HashSet;
use std::iter::FromIterator;
use types::*;
type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
@@ -13,38 +11,26 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// Verify an `IndexedAttestation`.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn is_valid_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>,
indexed_attestation: &IndexedAttestation<T>,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<()> {
let bit_0_indices = &indexed_attestation.custody_bit_0_indices;
let bit_1_indices = &indexed_attestation.custody_bit_1_indices;
// Verify no index has custody bit equal to 1 [to be removed in phase 1]
verify!(bit_1_indices.is_empty(), Invalid::CustodyBitfieldHasSetBits);
let indices = &indexed_attestation.attesting_indices;
// Verify max number of indices
let total_indices = bit_0_indices.len() + bit_1_indices.len();
verify!(
total_indices <= T::MaxValidatorsPerCommittee::to_usize(),
Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), total_indices)
indices.len() <= T::MaxValidatorsPerCommittee::to_usize(),
Invalid::MaxIndicesExceed(T::MaxValidatorsPerCommittee::to_usize(), indices.len())
);
// Verify index sets are disjoint
let custody_bit_intersection: HashSet<&u64> =
&HashSet::from_iter(bit_0_indices.iter()) & &HashSet::from_iter(bit_1_indices.iter());
verify!(
custody_bit_intersection.is_empty(),
Invalid::CustodyBitValidatorsIntersect
);
// Check that both vectors of indices are sorted
// Check that indices are sorted
let check_sorted = |list: &[u64]| -> Result<()> {
list.windows(2).enumerate().try_for_each(|(i, pair)| {
if pair[0] >= pair[1] {
// The spec allows duplicates, so use strict comparison (>).
if pair[0] > pair[1] {
Err(error(Invalid::BadValidatorIndicesOrdering(i)))
} else {
Ok(())
@@ -52,8 +38,7 @@ pub fn is_valid_indexed_attestation<T: EthSpec>(
})?;
Ok(())
};
check_sorted(&bit_0_indices)?;
check_sorted(&bit_1_indices)?;
check_sorted(indices)?;
if verify_signatures.is_true() {
verify!(

View File

@@ -2,14 +2,13 @@
//! validated individually, or alongside in others in a potentially cheaper bulk operation.
//!
//! This module exposes one function to extract each type of `SignatureSet` from a `BeaconBlock`.
use bls::SignatureSet;
use bls::{SignatureSet, SignedMessage};
use std::convert::TryInto;
use tree_hash::{SignedRoot, TreeHash};
use types::{
AggregateSignature, AttestationDataAndCustodyBit, AttesterSlashing, BeaconBlock,
BeaconBlockHeader, BeaconState, BeaconStateError, ChainSpec, DepositData, Domain, EthSpec,
Hash256, IndexedAttestation, ProposerSlashing, PublicKey, RelativeEpoch, Signature, Transfer,
VoluntaryExit,
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconBlockHeader, BeaconState,
BeaconStateError, ChainSpec, DepositData, Domain, EthSpec, Hash256, IndexedAttestation,
ProposerSlashing, PublicKey, Signature, VoluntaryExit,
};
pub type Result<T> = std::result::Result<T, Error>;
@@ -42,8 +41,7 @@ pub fn block_proposal_signature_set<'a, T: EthSpec>(
block_signed_root: Option<Hash256>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> {
let proposer_index =
state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?;
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
let block_proposer = &state
.validators
.get(proposer_index)
@@ -75,8 +73,7 @@ pub fn randao_signature_set<'a, T: EthSpec>(
block: &'a BeaconBlock<T>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> {
let block_proposer = &state.validators
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
let block_proposer = &state.validators[state.get_beacon_proposer_index(block.slot, spec)?];
let domain = spec.get_domain(
block.slot.epoch(T::slots_per_epoch()),
@@ -141,31 +138,20 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>(
indexed_attestation: &'b IndexedAttestation<T>,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> {
let message_0 = AttestationDataAndCustodyBit {
data: indexed_attestation.data.clone(),
custody_bit: false,
}
.tree_hash_root();
let message_1 = AttestationDataAndCustodyBit {
data: indexed_attestation.data.clone(),
custody_bit: true,
}
.tree_hash_root();
let message = indexed_attestation.data.tree_hash_root();
let signed_message = SignedMessage::new(
get_pubkeys(state, &indexed_attestation.attesting_indices)?,
message,
);
let domain = spec.get_domain(
indexed_attestation.data.target.epoch,
Domain::Attestation,
Domain::BeaconAttester,
&state.fork,
);
Ok(SignatureSet::dual(
signature,
message_0,
get_pubkeys(state, &indexed_attestation.custody_bit_0_indices)?,
message_1,
get_pubkeys(state, &indexed_attestation.custody_bit_1_indices)?,
domain,
))
Ok(SignatureSet::new(signature, vec![signed_message], domain))
}
/// Returns the signature set for the given `attester_slashing` and corresponding `pubkeys`.
@@ -244,28 +230,6 @@ pub fn exit_signature_set<'a, T: EthSpec>(
))
}
/// Returns a signature set that is valid if the `Transfer` was signed by `transfer.pubkey`.
pub fn transfer_signature_set<'a, T: EthSpec>(
state: &'a BeaconState<T>,
transfer: &'a Transfer,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>> {
let domain = spec.get_domain(
transfer.slot.epoch(T::slots_per_epoch()),
Domain::Transfer,
&state.fork,
);
let message = transfer.signed_root();
Ok(SignatureSet::single(
&transfer.signature,
&transfer.pubkey,
message,
domain,
))
}
/// Maps validator indices to public keys.
fn get_pubkeys<'a, 'b, T, I>(
state: &'a BeaconState<T>,

View File

@@ -393,6 +393,7 @@ fn invalid_exit_already_exited() {
);
}
/* FIXME: needs updating for v0.9
#[test]
fn invalid_exit_not_active() {
use std::cmp::max;
@@ -421,6 +422,7 @@ fn invalid_exit_not_active() {
})
);
}
*/
#[test]
fn invalid_exit_already_initiated() {
@@ -546,7 +548,7 @@ fn valid_attestations() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -560,91 +562,14 @@ fn 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
})
);
}
/* FIXME: needs updating for v0.9
#[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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -658,10 +583,14 @@ fn invalid_attestation_no_committee_for_shard() {
assert_eq!(
result,
Err(BlockProcessingError::BeaconStateError(
BeaconStateError::NoCommitteeForShard
BeaconStateError::NoCommittee {
slot: Slot::new(0),
index: 0
}
))
);
}
*/
#[test]
fn invalid_attestation_wrong_justified_checkpoint() {
@@ -669,7 +598,7 @@ fn invalid_attestation_wrong_justified_checkpoint() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -706,7 +635,7 @@ fn invalid_attestation_bad_target_too_low() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -716,16 +645,14 @@ fn invalid_attestation_bad_target_too_low() {
&spec,
);
// Expecting EpochTooLow because we manually set the
// Expecting BadTargetEpoch 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),
})
))
Err(BlockProcessingError::AttestationInvalid {
index: 0,
reason: AttestationInvalid::BadTargetEpoch
})
);
}
@@ -735,7 +662,7 @@ fn invalid_attestation_bad_target_too_high() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -745,43 +672,13 @@ fn invalid_attestation_bad_target_too_high() {
&spec,
);
// Expecting EpochTooHigh because we manually set the
// Expecting BadTargetEpoch 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,
reason: AttestationInvalid::BadTargetEpoch
})
);
}
@@ -792,7 +689,7 @@ fn invalid_attestation_bad_indexed_attestation_bad_signature() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -814,90 +711,13 @@ fn invalid_attestation_bad_indexed_attestation_bad_signature() {
);
}
#[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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -922,7 +742,7 @@ fn invalid_attestation_bad_signature() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
&block,
@@ -949,7 +769,7 @@ fn invalid_attestation_included_too_early() {
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -965,9 +785,9 @@ fn invalid_attestation_included_too_early() {
Err(BlockProcessingError::AttestationInvalid {
index: 0,
reason: AttestationInvalid::IncludedTooEarly {
state: Slot::from(319 as u64),
state: state.slot,
delay: spec.min_attestation_inclusion_delay,
attestation: Slot::from(319 as u64)
attestation: block.body.attestations[0].data.slot,
}
})
);
@@ -976,11 +796,11 @@ fn invalid_attestation_included_too_early() {
#[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
// note to maintainer: might need to increase validator count if we get NoCommittee
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -990,31 +810,26 @@ fn invalid_attestation_included_too_late() {
&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),
}
})
assert_eq!(
result,
Err(BlockProcessingError::AttestationInvalid {
index: 0,
reason: AttestationInvalid::IncludedTooLate {
state: state.slot,
attestation: block.body.attestations[0].data.slot,
}
})
);
}
#[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
// note to maintainer: might need to increase validator count if we get NoCommittee
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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1028,7 +843,10 @@ fn invalid_attestation_bad_target_epoch() {
assert!(
result
== Err(BlockProcessingError::BeaconStateError(
BeaconStateError::NoCommitteeForShard
BeaconStateError::NoCommittee {
slot: Slot::new(0),
index: 0
}
))
|| result
== Err(BlockProcessingError::AttestationInvalid {
@@ -1038,13 +856,14 @@ fn invalid_attestation_bad_target_epoch() {
);
}
/* FIXME: needs updating for v0.9
#[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);
builder.build_with_n_attestations(test_task, NUM_ATTESTATIONS, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1054,7 +873,7 @@ fn invalid_attestation_bad_shard() {
&spec,
);
// Expecting BadShard or NoCommitteeForShard because the shard number is higher than ShardCount
// Expecting BadShard or NoCommittee because the shard number is higher than ShardCount
assert!(
result
== Err(BlockProcessingError::AttestationInvalid {
@@ -1063,10 +882,14 @@ fn invalid_attestation_bad_shard() {
})
|| result
== Err(BlockProcessingError::BeaconStateError(
BeaconStateError::NoCommitteeForShard
BeaconStateError::NoCommittee {
slot: Slot::new(0),
index: 0
}
))
);
}
*/
#[test]
fn valid_insert_attester_slashing() {
@@ -1075,7 +898,7 @@ fn valid_insert_attester_slashing() {
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);
builder.build_with_attester_slashing(test_task, num_attester_slashings, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1096,7 +919,7 @@ fn invalid_attester_slashing_not_slashable() {
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);
builder.build_with_attester_slashing(test_task, num_attester_slashings, None, None, &spec);
let result = per_block_processing(
&mut state,
&block,
@@ -1122,7 +945,7 @@ fn invalid_attester_slashing_1_invalid() {
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);
builder.build_with_attester_slashing(test_task, num_attester_slashings, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1132,22 +955,12 @@ fn invalid_attester_slashing_1_invalid() {
&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
)
)
})
assert_eq!(
result,
Err(BlockProcessingError::IndexedAttestationInvalid {
index: 0,
reason: IndexedAttestationInvalid::BadValidatorIndicesOrdering(0)
})
);
}
@@ -1158,7 +971,7 @@ fn invalid_attester_slashing_2_invalid() {
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);
builder.build_with_attester_slashing(test_task, num_attester_slashings, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1168,22 +981,12 @@ fn invalid_attester_slashing_2_invalid() {
&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
)
)
})
assert_eq!(
result,
Err(BlockProcessingError::IndexedAttestationInvalid {
index: 1,
reason: IndexedAttestationInvalid::BadValidatorIndicesOrdering(0)
})
);
}
@@ -1192,7 +995,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1211,7 +1014,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1235,7 +1038,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1260,7 +1063,7 @@ 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);
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(
@@ -1286,7 +1089,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1311,7 +1114,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1336,7 +1139,7 @@ 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 (block, mut state) = builder.build_with_proposer_slashing(test_task, 1, None, None, &spec);
let result = per_block_processing(
&mut state,
@@ -1351,7 +1154,7 @@ fn invalid_proposer_slashing_proposal_epoch_mismatch() {
result,
Err(BlockProcessingError::ProposerSlashingInvalid {
index: 0,
reason: ProposerSlashingInvalid::ProposalEpochMismatch(
reason: ProposerSlashingInvalid::ProposalSlotMismatch(
Slot::from(0 as u64),
Slot::from(128 as u64)
)
@@ -1363,7 +1166,7 @@ fn get_builder(
spec: &ChainSpec,
slot_offset: u64,
num_validators: usize,
) -> (BlockProcessingBuilder<MainnetEthSpec>) {
) -> BlockProcessingBuilder<MainnetEthSpec> {
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.
@@ -1371,6 +1174,5 @@ fn get_builder(
(MainnetEthSpec::genesis_epoch() + slot_offset).end_slot(MainnetEthSpec::slots_per_epoch());
builder.set_slot(last_slot_of_epoch);
builder.build_caches(&spec);
(builder)
builder
}

View File

@@ -2,7 +2,6 @@ use super::errors::{AttestationInvalid as Invalid, BlockOperationError};
use super::VerifySignatures;
use crate::common::get_indexed_attestation;
use crate::per_block_processing::is_valid_indexed_attestation;
use tree_hash::TreeHash;
use types::*;
type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
@@ -16,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
///
/// Optionally verifies the aggregate signature, depending on `verify_signatures`.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation<T>,
@@ -25,22 +24,19 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
) -> Result<()> {
let data = &attestation.data;
// Check attestation slot.
let attestation_slot = state.get_attestation_data_slot(&data)?;
verify!(
attestation_slot + spec.min_attestation_inclusion_delay <= state.slot,
data.slot + spec.min_attestation_inclusion_delay <= state.slot,
Invalid::IncludedTooEarly {
state: state.slot,
delay: spec.min_attestation_inclusion_delay,
attestation: attestation_slot
attestation: data.slot,
}
);
verify!(
state.slot <= attestation_slot + T::slots_per_epoch(),
state.slot <= data.slot + T::slots_per_epoch(),
Invalid::IncludedTooLate {
state: state.slot,
attestation: attestation_slot
attestation: data.slot,
}
);
@@ -53,7 +49,7 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
/// prior blocks in `state`.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_attestation_for_state<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation<T>,
@@ -63,35 +59,12 @@ pub fn verify_attestation_for_state<T: EthSpec>(
let data = &attestation.data;
verify!(
data.crosslink.shard < T::ShardCount::to_u64(),
Invalid::BadShard
data.index < state.get_committee_count_at_slot(data.slot)?,
Invalid::BadCommitteeIndex
);
// Verify the Casper FFG vote and crosslink data.
let parent_crosslink = verify_casper_ffg_vote(attestation, state)?;
verify!(
data.crosslink.parent_root == Hash256::from_slice(&parent_crosslink.tree_hash_root()),
Invalid::BadParentCrosslinkHash
);
verify!(
data.crosslink.start_epoch == parent_crosslink.end_epoch,
Invalid::BadParentCrosslinkStartEpoch
);
verify!(
data.crosslink.end_epoch
== std::cmp::min(
data.target.epoch,
parent_crosslink.end_epoch + spec.max_epochs_per_crosslink
),
Invalid::BadParentCrosslinkEndEpoch
);
// Crosslink data root is zero (to be removed in phase 1).
verify!(
attestation.data.crosslink.data_root == Hash256::zero(),
Invalid::ShardBlockRootNotZero
);
// Verify the Casper FFG vote.
verify_casper_ffg_vote(attestation, state)?;
// Check signature and bitfields
let indexed_attestation = get_indexed_attestation(state, attestation)?;
@@ -102,13 +75,11 @@ pub fn verify_attestation_for_state<T: EthSpec>(
/// Check target epoch and source checkpoint.
///
/// Return the parent crosslink for further checks.
///
/// Spec v0.8.0
fn verify_casper_ffg_vote<'a, T: EthSpec>(
/// Spec v0.9.1
fn verify_casper_ffg_vote<T: EthSpec>(
attestation: &Attestation<T>,
state: &'a BeaconState<T>,
) -> Result<&'a Crosslink> {
state: &BeaconState<T>,
) -> Result<()> {
let data = &attestation.data;
if data.target.epoch == state.current_epoch() {
verify!(
@@ -119,7 +90,7 @@ fn verify_casper_ffg_vote<'a, T: EthSpec>(
is_current: true,
}
);
Ok(state.get_current_crosslink(data.crosslink.shard)?)
Ok(())
} else if data.target.epoch == state.previous_epoch() {
verify!(
data.source == state.previous_justified_checkpoint,
@@ -129,7 +100,7 @@ fn verify_casper_ffg_vote<'a, T: EthSpec>(
is_current: false,
}
);
Ok(state.get_previous_crosslink(data.crosslink.shard)?)
Ok(())
} else {
Err(error(Invalid::BadTargetEpoch))
}

View File

@@ -15,7 +15,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
///
/// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn verify_attester_slashing<T: EthSpec>(
state: &BeaconState<T>,
attester_slashing: &AttesterSlashing<T>,
@@ -47,7 +47,7 @@ pub fn verify_attester_slashing<T: EthSpec>(
///
/// Returns Ok(indices) if `indices.len() > 0`.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn get_slashable_indices<T: EthSpec>(
state: &BeaconState<T>,
attester_slashing: &AttesterSlashing<T>,
@@ -71,15 +71,13 @@ where
let attestation_2 = &attester_slashing.attestation_2;
let attesting_indices_1 = attestation_1
.custody_bit_0_indices
.attesting_indices
.iter()
.chain(&attestation_1.custody_bit_1_indices)
.cloned()
.collect::<BTreeSet<_>>();
let attesting_indices_2 = attestation_2
.custody_bit_0_indices
.attesting_indices
.iter()
.chain(&attestation_2.custody_bit_1_indices)
.cloned()
.collect::<BTreeSet<_>>();

View File

@@ -14,7 +14,7 @@ fn error(reason: DepositInvalid) -> BlockOperationError<DepositInvalid> {
/// Verify `Deposit.pubkey` signed `Deposit.signature`.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_deposit_signature(deposit_data: &DepositData, spec: &ChainSpec) -> Result<()> {
let deposit_signature_message = deposit_pubkey_signature_message(&deposit_data)
.ok_or_else(|| error(DepositInvalid::BadBlsBytes))?;
@@ -46,7 +46,7 @@ pub fn get_existing_validator_index<T: EthSpec>(
/// The deposit index is provided as a parameter so we can check proofs
/// before they're due to be processed, and in parallel.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_deposit_merkle_proof<T: EthSpec>(
state: &BeaconState<T>,
deposit: &Deposit,

View File

@@ -13,7 +13,7 @@ fn error(reason: ExitInvalid) -> BlockOperationError<ExitInvalid> {
///
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_exit<T: EthSpec>(
state: &BeaconState<T>,
exit: &VoluntaryExit,
@@ -25,7 +25,7 @@ pub fn verify_exit<T: EthSpec>(
/// Like `verify_exit` but doesn't run checks which may become true in future states.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_exit_time_independent_only<T: EthSpec>(
state: &BeaconState<T>,
exit: &VoluntaryExit,
@@ -37,7 +37,7 @@ pub fn verify_exit_time_independent_only<T: EthSpec>(
/// Parametric version of `verify_exit` that skips some checks if `time_independent_only` is true.
///
/// Spec v0.8.0
/// Spec v0.9.1
fn verify_exit_parametric<T: EthSpec>(
state: &BeaconState<T>,
exit: &VoluntaryExit,

View File

@@ -14,7 +14,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
///
/// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn verify_proposer_slashing<T: EthSpec>(
proposer_slashing: &ProposerSlashing,
state: &BeaconState<T>,
@@ -26,11 +26,10 @@ pub fn verify_proposer_slashing<T: EthSpec>(
.get(proposer_slashing.proposer_index as usize)
.ok_or_else(|| error(Invalid::ProposerUnknown(proposer_slashing.proposer_index)))?;
// Verify that the epoch is the same
// Verify slots match
verify!(
proposer_slashing.header_1.slot.epoch(T::slots_per_epoch())
== proposer_slashing.header_2.slot.epoch(T::slots_per_epoch()),
Invalid::ProposalEpochMismatch(
proposer_slashing.header_1.slot == proposer_slashing.header_2.slot,
Invalid::ProposalSlotMismatch(
proposer_slashing.header_1.slot,
proposer_slashing.header_2.slot
)

View File

@@ -1,208 +0,0 @@
use super::errors::{BlockOperationError, TransferInvalid as Invalid};
use crate::per_block_processing::signature_sets::transfer_signature_set;
use crate::per_block_processing::VerifySignatures;
use bls::get_withdrawal_credentials;
use types::*;
type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
fn error(reason: Invalid) -> BlockOperationError<Invalid> {
BlockOperationError::invalid(reason)
}
/// Indicates if a `Transfer` is valid to be included in a block in the current epoch of the given
/// state.
///
/// Returns `Ok(())` if the `Transfer` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.8.0
pub fn verify_transfer<T: EthSpec>(
state: &BeaconState<T>,
transfer: &Transfer,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<()> {
verify_transfer_parametric(state, transfer, verify_signatures, spec, false)
}
/// Like `verify_transfer` but doesn't run checks which may become true in future states.
///
/// Spec v0.8.0
pub fn verify_transfer_time_independent_only<T: EthSpec>(
state: &BeaconState<T>,
transfer: &Transfer,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<()> {
verify_transfer_parametric(state, transfer, verify_signatures, spec, true)
}
/// Parametric version of `verify_transfer` that allows some checks to be skipped.
///
/// When `time_independent_only == true`, time-specific parameters are ignored, including:
///
/// - Balance considerations (e.g., adequate balance, not dust, etc).
/// - `transfer.slot` does not have to exactly match `state.slot`, it just needs to be in the
/// present or future.
/// - Validator transfer eligibility (e.g., is withdrawable)
///
/// Spec v0.8.0
fn verify_transfer_parametric<T: EthSpec>(
state: &BeaconState<T>,
transfer: &Transfer,
verify_signatures: VerifySignatures,
spec: &ChainSpec,
time_independent_only: bool,
) -> Result<()> {
let sender_balance = *state
.balances
.get(transfer.sender as usize)
.ok_or_else(|| error(Invalid::FromValidatorUnknown(transfer.sender)))?;
let recipient_balance = *state
.balances
.get(transfer.recipient as usize)
.ok_or_else(|| error(Invalid::FromValidatorUnknown(transfer.recipient)))?;
// Safely determine `amount + fee`.
let total_amount = transfer
.amount
.checked_add(transfer.fee)
.ok_or_else(|| error(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
// Verify the sender has adequate balance.
verify!(
time_independent_only || sender_balance >= total_amount,
Invalid::FromBalanceInsufficient(total_amount, sender_balance)
);
// Verify sender balance will not be "dust" (i.e., greater than zero but less than the minimum deposit
// amount).
verify!(
time_independent_only
|| (sender_balance == total_amount)
|| (sender_balance >= (total_amount + spec.min_deposit_amount)),
Invalid::SenderDust(sender_balance - total_amount, spec.min_deposit_amount)
);
// Verify the recipient balance will not be dust.
verify!(
time_independent_only || ((recipient_balance + transfer.amount) >= spec.min_deposit_amount),
Invalid::RecipientDust(sender_balance - total_amount, spec.min_deposit_amount)
);
// If loosely enforcing `transfer.slot`, ensure the slot is not in the past. Otherwise, ensure
// the transfer slot equals the state slot.
if time_independent_only {
verify!(
state.slot <= transfer.slot,
Invalid::TransferSlotInPast(state.slot, transfer.slot)
);
} else {
verify!(
state.slot == transfer.slot,
Invalid::StateSlotMismatch(state.slot, transfer.slot)
);
}
// Load the sender `Validator` record from the state.
let sender_validator = state
.validators
.get(transfer.sender as usize)
.ok_or_else(|| error(Invalid::FromValidatorUnknown(transfer.sender)))?;
// Ensure one of the following is met:
//
// - Time dependent checks are being ignored.
// - The sender has never been eligible for activation.
// - The sender is withdrawable at the state's epoch.
// - The transfer will not reduce the sender below the max effective balance.
verify!(
time_independent_only
|| sender_validator.activation_eligibility_epoch == spec.far_future_epoch
|| sender_validator.is_withdrawable_at(state.current_epoch())
|| total_amount + spec.max_effective_balance <= sender_balance,
Invalid::FromValidatorIneligibleForTransfer(transfer.sender)
);
// Ensure the withdrawal credentials generated from the sender's pubkey match those stored in
// the validator registry.
//
// This ensures the validator can only perform a transfer when they are in control of the
// withdrawal address.
let transfer_withdrawal_credentials = Hash256::from_slice(
&get_withdrawal_credentials(&transfer.pubkey, spec.bls_withdrawal_prefix_byte)[..],
);
verify!(
sender_validator.withdrawal_credentials == transfer_withdrawal_credentials,
Invalid::WithdrawalCredentialsMismatch(
sender_validator.withdrawal_credentials,
transfer_withdrawal_credentials
)
);
if verify_signatures.is_true() {
verify!(
transfer_signature_set(state, transfer, spec)?.is_valid(),
Invalid::BadSignature
);
}
Ok(())
}
/// Executes a transfer on the state.
///
/// Does not check that the transfer is valid, however checks for overflow in all actions.
///
/// Spec v0.8.0
pub fn execute_transfer<T: EthSpec>(
state: &mut BeaconState<T>,
transfer: &Transfer,
spec: &ChainSpec,
) -> Result<()> {
let sender_balance = *state
.balances
.get(transfer.sender as usize)
.ok_or_else(|| error(Invalid::FromValidatorUnknown(transfer.sender)))?;
let recipient_balance = *state
.balances
.get(transfer.recipient as usize)
.ok_or_else(|| error(Invalid::ToValidatorUnknown(transfer.recipient)))?;
let proposer_index =
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)?;
let proposer_balance = state.balances[proposer_index];
let total_amount = transfer
.amount
.checked_add(transfer.fee)
.ok_or_else(|| error(Invalid::FeeOverflow(transfer.amount, transfer.fee)))?;
state.balances[transfer.sender as usize] =
sender_balance.checked_sub(total_amount).ok_or_else(|| {
error(Invalid::FromBalanceInsufficient(
total_amount,
sender_balance,
))
})?;
state.balances[transfer.recipient as usize] = recipient_balance
.checked_add(transfer.amount)
.ok_or_else(|| {
error(Invalid::ToBalanceOverflow(
recipient_balance,
transfer.amount,
))
})?;
state.balances[proposer_index] =
proposer_balance.checked_add(transfer.fee).ok_or_else(|| {
error(Invalid::ProposerBalanceOverflow(
proposer_balance,
transfer.fee,
))
})?;
Ok(())
}

View File

@@ -1,10 +1,7 @@
use crate::common::get_compact_committees_root;
use errors::EpochProcessingError as Error;
use std::collections::HashMap;
use tree_hash::TreeHash;
use types::*;
use validator_statuses::{TotalBalances, ValidatorStatuses};
use winning_root::{winning_root, WinningRoot};
pub mod apply_rewards;
pub mod errors;
@@ -12,23 +9,17 @@ pub mod process_slashings;
pub mod registry_updates;
pub mod tests;
pub mod validator_statuses;
pub mod winning_root;
pub use apply_rewards::process_rewards_and_penalties;
pub use process_slashings::process_slashings;
pub use registry_updates::process_registry_updates;
/// Maps a shard to a winning root.
///
/// It is generated during crosslink processing and later used to reward/penalize validators.
pub type WinningRootHashSet = HashMap<u64, WinningRoot>;
/// Performs per-epoch processing on some BeaconState.
///
/// Mutates the given `BeaconState`, returning early if an error is encountered. If an error is
/// returned, a state might be "half-processed" and therefore in an invalid state.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn per_epoch_processing<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,
@@ -47,9 +38,6 @@ pub fn per_epoch_processing<T: EthSpec>(
// Justification and finalization.
process_justification_and_finalization(state, &validator_statuses.total_balances)?;
// Crosslinks.
process_crosslinks(state, spec)?;
// Rewards and Penalties.
process_rewards_and_penalties(state, &mut validator_statuses, spec)?;
@@ -78,7 +66,7 @@ pub fn per_epoch_processing<T: EthSpec>(
/// - `finalized_epoch`
/// - `finalized_root`
///
/// Spec v0.8.0
/// Spec v0.9.1
#[allow(clippy::if_same_then_else)] // For readability and consistency with spec.
pub fn process_justification_and_finalization<T: EthSpec>(
state: &mut BeaconState<T>,
@@ -144,47 +132,9 @@ pub fn process_justification_and_finalization<T: EthSpec>(
Ok(())
}
/// Updates the following fields on the `BeaconState`:
///
/// - `previous_crosslinks`
/// - `current_crosslinks`
///
/// Also returns a `WinningRootHashSet` for later use during epoch processing.
///
/// Spec v0.8.0
pub fn process_crosslinks<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), Error> {
state.previous_crosslinks = state.current_crosslinks.clone();
for &relative_epoch in &[RelativeEpoch::Previous, RelativeEpoch::Current] {
let epoch = relative_epoch.into_epoch(state.current_epoch());
for offset in 0..state.get_committee_count(relative_epoch)? {
let shard =
(state.get_epoch_start_shard(relative_epoch)? + offset) % T::ShardCount::to_u64();
let crosslink_committee =
state.get_crosslink_committee_for_shard(shard, relative_epoch)?;
let winning_root = winning_root(state, shard, epoch, spec)?;
if let Some(winning_root) = winning_root {
let total_committee_balance =
state.get_total_balance(&crosslink_committee.committee, spec)?;
if 3 * winning_root.total_attesting_balance >= 2 * total_committee_balance {
state.current_crosslinks[shard as usize] = winning_root.crosslink.clone();
}
}
}
}
Ok(())
}
/// Finish up an epoch update.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_final_updates<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,
@@ -211,23 +161,6 @@ pub fn process_final_updates<T: EthSpec>(
}
}
// Set active index root
let index_epoch = next_epoch + spec.activation_exit_delay;
let indices_list = VariableList::<usize, T::ValidatorRegistryLimit>::from(
state.get_active_validator_indices(index_epoch),
);
state.set_active_index_root(
index_epoch,
Hash256::from_slice(&indices_list.tree_hash_root()),
spec,
)?;
// Set committees root
state.set_compact_committee_root(
next_epoch,
get_compact_committees_root(state, RelativeEpoch::Next, spec)?,
)?;
// Reset slashings
state.set_slashings(next_epoch, 0)?;
@@ -242,9 +175,6 @@ pub fn process_final_updates<T: EthSpec>(
.push(Hash256::from_slice(&historical_batch.tree_hash_root()))?;
}
// Update start shard.
state.start_shard = state.get_epoch_start_shard(RelativeEpoch::Next)?;
// Rotate current/previous epoch attestations
state.previous_epoch_attestations =
std::mem::replace(&mut state.current_epoch_attestations, VariableList::empty());

View File

@@ -32,7 +32,7 @@ impl std::ops::AddAssign for Delta {
/// Apply attester and proposer rewards.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_rewards_and_penalties<T: EthSpec>(
state: &mut BeaconState<T>,
validator_statuses: &mut ValidatorStatuses,
@@ -53,11 +53,6 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
get_attestation_deltas(&mut deltas, state, &validator_statuses, spec)?;
// Update statuses with the information from winning roots.
validator_statuses.process_winning_roots(state, spec)?;
get_crosslink_deltas(&mut deltas, state, &validator_statuses, spec)?;
get_proposer_deltas(&mut deltas, state, validator_statuses, spec)?;
// Apply the deltas, over-flowing but not under-flowing (saturating at 0 instead).
@@ -71,7 +66,7 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
/// For each attesting validator, reward the proposer who was first to include their attestation.
///
/// Spec v0.8.0
/// Spec v0.9.1
fn get_proposer_deltas<T: EthSpec>(
deltas: &mut Vec<Delta>,
state: &BeaconState<T>,
@@ -79,10 +74,10 @@ fn get_proposer_deltas<T: EthSpec>(
spec: &ChainSpec,
) -> Result<(), Error> {
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
if validator.is_previous_epoch_attester {
if validator.is_previous_epoch_attester && !validator.is_slashed {
let inclusion = validator
.inclusion_info
.expect("It is a logic error for an attester not to have an inclusion distance.");
.expect("It is a logic error for an attester not to have an inclusion delay.");
let base_reward = get_base_reward(
state,
@@ -104,7 +99,7 @@ fn get_proposer_deltas<T: EthSpec>(
/// Apply rewards for participation in attestations during the previous epoch.
///
/// Spec v0.8.0
/// Spec v0.9.1
fn get_attestation_deltas<T: EthSpec>(
deltas: &mut Vec<Delta>,
state: &BeaconState<T>,
@@ -137,7 +132,7 @@ fn get_attestation_deltas<T: EthSpec>(
/// Determine the delta for a single validator, sans proposer rewards.
///
/// Spec v0.8.0
/// Spec v0.9.1
fn get_attestation_delta<T: EthSpec>(
validator: &ValidatorStatus,
total_balances: &TotalBalances,
@@ -171,13 +166,8 @@ fn get_attestation_delta<T: EthSpec>(
let max_attester_reward = base_reward - proposer_reward;
let inclusion = validator
.inclusion_info
.expect("It is a logic error for an attester not to have an inclusion distance.");
delta.reward(
max_attester_reward
* (T::SlotsPerEpoch::to_u64() + spec.min_attestation_inclusion_delay
- inclusion.distance)
/ T::SlotsPerEpoch::to_u64(),
);
.expect("It is a logic error for an attester not to have an inclusion delay.");
delta.reward(max_attester_reward / inclusion.delay);
} else {
delta.penalize(base_reward);
}
@@ -222,43 +212,9 @@ fn get_attestation_delta<T: EthSpec>(
delta
}
/// Calculate the deltas based upon the winning roots for attestations during the previous epoch.
///
/// Spec v0.8.0
fn get_crosslink_deltas<T: EthSpec>(
deltas: &mut Vec<Delta>,
state: &BeaconState<T>,
validator_statuses: &ValidatorStatuses,
spec: &ChainSpec,
) -> Result<(), Error> {
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
let mut delta = Delta::default();
let base_reward = get_base_reward(
state,
index,
validator_statuses.total_balances.current_epoch,
spec,
)?;
if let Some(ref winning_root) = validator.winning_root_info {
delta.reward(
base_reward * winning_root.total_attesting_balance
/ winning_root.total_committee_balance,
);
} else {
delta.penalize(base_reward);
}
deltas[index] += delta;
}
Ok(())
}
/// Returns the base reward for some validator.
///
/// Spec v0.8.0
/// Spec v0.9.1
fn get_base_reward<T: EthSpec>(
state: &BeaconState<T>,
index: usize,

View File

@@ -2,7 +2,7 @@ use types::{BeaconStateError as Error, *};
/// Process slashings.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_slashings<T: EthSpec>(
state: &mut BeaconState<T>,
total_balance: u64,

View File

@@ -5,7 +5,7 @@ use types::*;
/// Performs a validator registry update, if required.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn process_registry_updates<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,

View File

@@ -1,4 +1,3 @@
use super::{winning_root::winning_root, WinningRootHashSet};
use crate::common::get_attesting_indices;
use types::*;
@@ -12,34 +11,21 @@ macro_rules! set_self_if_other_is_true {
};
}
/// The information required to reward some validator for their participation in a "winning"
/// crosslink root.
#[derive(Default, Clone)]
pub struct WinningRootInfo {
/// The total balance of the crosslink committee.
pub total_committee_balance: u64,
/// The total balance of the crosslink committee that attested for the "winning" root.
pub total_attesting_balance: u64,
}
/// The information required to reward a block producer for including an attestation in a block.
#[derive(Clone, Copy)]
pub struct InclusionInfo {
/// The earliest slot a validator had an attestation included in the previous epoch.
pub slot: Slot,
/// The distance between the attestation slot and the slot that attestation was included in a
/// block.
pub distance: u64,
pub delay: u64,
/// The index of the proposer at the slot where the attestation was included.
pub proposer_index: usize,
}
impl Default for InclusionInfo {
/// Defaults to `slot` and `distance` at their maximum values and `proposer_index` at zero.
/// Defaults to `delay` at its maximum value and `proposer_index` at zero.
fn default() -> Self {
Self {
slot: Slot::max_value(),
distance: u64::max_value(),
delay: u64::max_value(),
proposer_index: 0,
}
}
@@ -49,9 +35,8 @@ impl InclusionInfo {
/// Tests if some `other` `InclusionInfo` has a lower inclusion slot than `self`. If so,
/// replaces `self` with `other`.
pub fn update(&mut self, other: &Self) {
if other.slot < self.slot {
self.slot = other.slot;
self.distance = other.distance;
if other.delay < self.delay {
self.delay = other.delay;
self.proposer_index = other.proposer_index;
}
}
@@ -88,9 +73,6 @@ pub struct ValidatorStatus {
/// Information used to reward the block producer of this validators earliest-included
/// attestation.
pub inclusion_info: Option<InclusionInfo>,
/// Information used to reward/penalize the validator if they voted in the super-majority for
/// some shard block.
pub winning_root_info: Option<WinningRootInfo>,
}
impl ValidatorStatus {
@@ -162,7 +144,7 @@ impl ValidatorStatuses {
/// - Active validators
/// - Total balances for the current and previous epochs.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn new<T: EthSpec>(
state: &BeaconState<T>,
spec: &ChainSpec,
@@ -202,7 +184,7 @@ impl ValidatorStatuses {
/// Process some attestations from the given `state` updating the `statuses` and
/// `total_balances` fields.
///
/// Spec v0.8.1
/// Spec v0.9.1
pub fn process_attestations<T: EthSpec>(
&mut self,
state: &BeaconState<T>,
@@ -228,19 +210,11 @@ impl ValidatorStatuses {
} else if a.data.target.epoch == state.previous_epoch() {
status.is_previous_epoch_attester = true;
// The inclusion slot and distance are only required for previous epoch attesters.
let attestation_slot = state.get_attestation_data_slot(&a.data)?;
let inclusion_slot = attestation_slot + a.inclusion_delay;
let relative_epoch =
RelativeEpoch::from_slot(state.slot, inclusion_slot, T::slots_per_epoch())?;
// The inclusion delay and proposer index are only required for previous epoch
// attesters.
status.inclusion_info = Some(InclusionInfo {
slot: inclusion_slot,
distance: a.inclusion_delay,
proposer_index: state.get_beacon_proposer_index(
inclusion_slot,
relative_epoch,
spec,
)?,
delay: a.inclusion_delay,
proposer_index: a.proposer_index as usize,
});
if target_matches_epoch_start_block(a, state, state.previous_epoch())? {
@@ -284,66 +258,12 @@ impl ValidatorStatuses {
Ok(())
}
/// Update the `statuses` for each validator based upon whether or not they attested to the
/// "winning" shard block root for the previous epoch.
///
/// Spec v0.8.1
pub fn process_winning_roots<T: EthSpec>(
&mut self,
state: &BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), BeaconStateError> {
// We must re-calculate the winning roots here because it is possible that they have
// changed since the first time they were calculated.
//
// This is because we altered the state during the first time we calculated the winning
// roots.
let winning_root_for_shards = {
let mut winning_root_for_shards = WinningRootHashSet::new();
let relative_epoch = RelativeEpoch::Previous;
let epoch = relative_epoch.into_epoch(state.current_epoch());
for offset in 0..state.get_committee_count(relative_epoch)? {
let shard = (state.get_epoch_start_shard(relative_epoch)? + offset)
% T::ShardCount::to_u64();
if let Some(winning_root) = winning_root(state, shard, epoch, spec)? {
winning_root_for_shards.insert(shard, winning_root);
}
}
winning_root_for_shards
};
// Loop through each slot in the previous epoch.
for slot in state.previous_epoch().slot_iter(T::slots_per_epoch()) {
let crosslink_committees_at_slot = state.get_crosslink_committees_at_slot(slot)?;
// Loop through each committee in the slot.
for c in crosslink_committees_at_slot {
// If there was some winning crosslink root for the committee's shard.
if let Some(winning_root) = winning_root_for_shards.get(&c.shard) {
let total_committee_balance = state.get_total_balance(&c.committee, spec)?;
for &validator_index in &winning_root.attesting_validator_indices {
// Take note of the balance information for the winning root, it will be
// used later to calculate rewards for that validator.
self.statuses[validator_index].winning_root_info = Some(WinningRootInfo {
total_committee_balance,
total_attesting_balance: winning_root.total_attesting_balance,
})
}
}
}
}
Ok(())
}
}
/// Returns `true` if the attestation's FFG target is equal to the hash of the `state`'s first
/// beacon block in the given `epoch`.
///
/// Spec v0.8.1
/// Spec v0.9.1
fn target_matches_epoch_start_block<T: EthSpec>(
a: &PendingAttestation<T>,
state: &BeaconState<T>,
@@ -358,13 +278,12 @@ fn target_matches_epoch_start_block<T: EthSpec>(
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
/// the current slot of the `PendingAttestation`.
///
/// Spec v0.8.1
/// Spec v0.9.1
fn has_common_beacon_block_root<T: EthSpec>(
a: &PendingAttestation<T>,
state: &BeaconState<T>,
) -> Result<bool, BeaconStateError> {
let attestation_slot = state.get_attestation_data_slot(&a.data)?;
let state_block_root = *state.get_block_root(attestation_slot)?;
let state_block_root = *state.get_block_root(a.data.slot)?;
Ok(a.data.beacon_block_root == state_block_root)
}

View File

@@ -1,130 +0,0 @@
use crate::common::get_attesting_indices;
use std::collections::{HashMap, HashSet};
use tree_hash::TreeHash;
use types::*;
#[derive(Clone, Debug)]
pub struct WinningRoot {
pub crosslink: Crosslink,
pub attesting_validator_indices: Vec<usize>,
pub total_attesting_balance: u64,
}
impl WinningRoot {
/// Returns `true` if `self` is a "better" candidate than `other`.
///
/// A winning root is "better" than another if it has a higher `total_attesting_balance`. Ties
/// are broken by favouring the higher `crosslink_data_root` value.
///
/// Spec v0.8.0
pub fn is_better_than(&self, other: &Self) -> bool {
(self.total_attesting_balance, self.crosslink.data_root)
> (other.total_attesting_balance, other.crosslink.data_root)
}
}
/// Returns the crosslink `data_root` with the highest total attesting balance for the given shard.
/// Breaks ties by favouring the smaller crosslink `data_root` hash.
///
/// The `WinningRoot` object also contains additional fields that are useful in later stages of
/// per-epoch processing.
///
/// Spec v0.8.0
pub fn winning_root<T: EthSpec>(
state: &BeaconState<T>,
shard: u64,
epoch: Epoch,
spec: &ChainSpec,
) -> Result<Option<WinningRoot>, BeaconStateError> {
let attestations: Vec<&_> = state
.get_matching_source_attestations(epoch)?
.iter()
.filter(|a| a.data.crosslink.shard == shard)
.collect();
// Build a map from crosslinks to attestations that support that crosslink.
let mut candidate_crosslink_map = HashMap::new();
let current_shard_crosslink_root = state.get_current_crosslink(shard)?.tree_hash_root();
for a in attestations {
if a.data.crosslink.parent_root.as_bytes() == &current_shard_crosslink_root[..]
|| a.data.crosslink.tree_hash_root() == current_shard_crosslink_root
{
let supporting_attestations = candidate_crosslink_map
.entry(&a.data.crosslink)
.or_insert_with(Vec::new);
supporting_attestations.push(a);
}
}
// Find the maximum crosslink.
let mut winning_root = None;
for (crosslink, attestations) in candidate_crosslink_map {
let attesting_validator_indices =
get_unslashed_attesting_indices_unsorted(state, &attestations)?;
let total_attesting_balance =
state.get_total_balance(&attesting_validator_indices, spec)?;
let candidate = WinningRoot {
crosslink: crosslink.clone(),
attesting_validator_indices,
total_attesting_balance,
};
if let Some(ref winner) = winning_root {
if candidate.is_better_than(&winner) {
winning_root = Some(candidate);
}
} else {
winning_root = Some(candidate);
}
}
Ok(winning_root)
}
pub fn get_unslashed_attesting_indices_unsorted<T: EthSpec>(
state: &BeaconState<T>,
attestations: &[&PendingAttestation<T>],
) -> Result<Vec<usize>, BeaconStateError> {
let mut output = HashSet::new();
for a in attestations {
output.extend(get_attesting_indices(state, &a.data, &a.aggregation_bits)?);
}
Ok(output
.into_iter()
.filter(|index| state.validators.get(*index).map_or(false, |v| !v.slashed))
.collect())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_better_than() {
let worse = WinningRoot {
crosslink: Crosslink {
shard: 0,
start_epoch: Epoch::new(0),
end_epoch: Epoch::new(1),
parent_root: Hash256::from_slice(&[0; 32]),
data_root: Hash256::from_slice(&[1; 32]),
},
attesting_validator_indices: vec![],
total_attesting_balance: 42,
};
let mut better = worse.clone();
better.crosslink.data_root = Hash256::from_slice(&[2; 32]);
assert!(better.is_better_than(&worse));
let better = WinningRoot {
total_attesting_balance: worse.total_attesting_balance + 1,
..worse.clone()
};
assert!(better.is_better_than(&worse));
}
}

View File

@@ -9,7 +9,7 @@ pub enum Error {
/// Advances a state forward by one slot, performing per-epoch processing if required.
///
/// Spec v0.8.0
/// Spec v0.9.1
pub fn per_slot_processing<T: EthSpec>(
state: &mut BeaconState<T>,
spec: &ChainSpec,

View File

@@ -15,7 +15,6 @@ pub struct BlockBuilder<T: EthSpec> {
pub num_attestations: usize,
pub num_deposits: usize,
pub num_exits: usize,
pub num_transfers: usize,
}
impl<T: EthSpec> BlockBuilder<T> {
@@ -33,7 +32,6 @@ impl<T: EthSpec> BlockBuilder<T> {
num_attestations: 0,
num_deposits: 0,
num_exits: 0,
num_transfers: 0,
}
}
@@ -43,7 +41,6 @@ impl<T: EthSpec> BlockBuilder<T> {
self.num_attestations = T::MaxAttestations::to_usize();
self.num_deposits = T::MaxDeposits::to_usize();
self.num_exits = T::MaxVoluntaryExits::to_usize();
self.num_transfers = T::MaxTransfers::to_usize();
}
pub fn set_slot(&mut self, slot: Slot) {
@@ -61,9 +58,7 @@ impl<T: EthSpec> BlockBuilder<T> {
builder.set_slot(state.slot);
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let proposer_keypair = &keypairs[proposer_index];
@@ -80,7 +75,7 @@ impl<T: EthSpec> BlockBuilder<T> {
let validator_index = validators_iter.next().expect("Insufficient validators.");
builder.insert_proposer_slashing(
&ProposerSlashingTestTask::Valid,
ProposerSlashingTestTask::Valid,
validator_index,
&keypairs[validator_index as usize].sk,
&state.fork,
@@ -107,7 +102,7 @@ impl<T: EthSpec> BlockBuilder<T> {
}
builder.insert_attester_slashing(
&AttesterSlashingTestTask::Valid,
AttesterSlashingTestTask::Valid,
&attesters,
&secret_keys,
&state.fork,
@@ -123,7 +118,7 @@ impl<T: EthSpec> BlockBuilder<T> {
let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect();
builder
.insert_attestations(
&AttestationTestTask::Valid,
AttestationTestTask::Valid,
&state,
&all_secret_keys,
self.num_attestations as usize,
@@ -151,7 +146,7 @@ impl<T: EthSpec> BlockBuilder<T> {
let validator_index = validators_iter.next().expect("Insufficient validators.");
builder.insert_exit(
&ExitTestTask::Valid,
ExitTestTask::Valid,
&mut state,
validator_index,
&keypairs[validator_index as usize].sk,
@@ -163,24 +158,6 @@ impl<T: EthSpec> BlockBuilder<T> {
builder.block.body.voluntary_exits.len()
);
// Insert the maximum possible number of `Transfer` objects.
for _ in 0..self.num_transfers {
let validator_index = validators_iter.next().expect("Insufficient validators.");
// Manually set the validator to be withdrawn.
state.validators[validator_index as usize].withdrawable_epoch = state.previous_epoch();
builder.insert_transfer(
&state,
validator_index,
validator_index,
1,
keypairs[validator_index as usize].clone(),
spec,
);
}
info!("Inserted {} transfers.", builder.block.body.transfers.len());
// Set the eth1 data to be different from the state.
self.block_builder.block.body.eth1_data.block_hash = Hash256::from_slice(&[42; 32]);

View File

@@ -205,6 +205,4 @@ mod signatures_minimal {
spec,
);
}
// Cannot test transfers because their length is zero.
}