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

@@ -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(())
}