mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-15 19:02:42 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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<_>>();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
Reference in New Issue
Block a user