Update to spec v0.9.0

This commit is contained in:
Michael Sproul
2019-11-11 15:00:10 +11:00
parent 613fdbeda6
commit aaa5f2042f
93 changed files with 651 additions and 2203 deletions

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ type Result<T> = std::result::Result<T, BlockOperationError<Invalid>>;
/// Convert `attestation` to (almost) indexed-verifiable form.
///
/// Spec v0.8.0
/// Spec v0.9.0
pub fn get_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation<T>,
@@ -63,18 +63,16 @@ mod test {
state.build_all_caches(&spec).unwrap();
state.slot += 1;
let shard = 0;
let cc = state
.get_crosslink_committee_for_shard(shard, RelativeEpoch::Current)
.unwrap();
let index = 0;
let bc = state.get_beacon_committee(Slot::new(0), index).unwrap();
// Make a third of the validators sign with custody bit 0, a third with custody bit 1
// and a third not sign at all.
assert!(
cc.committee.len() >= 4,
bc.committee.len() >= 4,
"need at least 4 validators per committee for this test to work"
);
let (mut bit_0_indices, mut bit_1_indices): (Vec<_>, Vec<_>) = cc
let (mut bit_0_indices, mut bit_1_indices): (Vec<_>, Vec<_>) = bc
.committee
.iter()
.enumerate()
@@ -99,7 +97,7 @@ mod test {
.collect::<Vec<_>>();
let mut attestation_builder =
TestingAttestationBuilder::new(&state, &cc.committee, cc.slot, shard, &spec);
TestingAttestationBuilder::new(&state, &bc.committee, bc.slot, index);
attestation_builder
.sign(&bit_0_indices, &bit_0_keys, &state.fork, &spec, false)
.sign(&bit_1_indices, &bit_1_keys, &state.fork, &spec, true);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -48,9 +48,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.
@@ -209,8 +200,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,
@@ -231,13 +222,6 @@ 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
@@ -255,14 +239,10 @@ pub enum AttestationInvalid {
},
/// 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),
}
@@ -345,56 +325,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

@@ -13,7 +13,7 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
/// Verify an `IndexedAttestation`.
///
/// Spec v0.8.0
/// Spec v0.9.0
pub fn is_valid_indexed_attestation<T: EthSpec>(
state: &BeaconState<T>,
indexed_attestation: &IndexedAttestation<T>,

View File

@@ -8,8 +8,7 @@ use tree_hash::{SignedRoot, TreeHash};
use types::{
AggregateSignature, AttestationDataAndCustodyBit, AttesterSlashing, BeaconBlock,
BeaconBlockHeader, BeaconState, BeaconStateError, ChainSpec, Deposit, Domain, EthSpec, Fork,
Hash256, IndexedAttestation, ProposerSlashing, PublicKey, RelativeEpoch, Signature, Transfer,
VoluntaryExit,
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()),
@@ -154,7 +151,7 @@ pub fn indexed_attestation_signature_set<'a, 'b, T: EthSpec>(
let domain = spec.get_domain(
indexed_attestation.data.target.epoch,
Domain::Attestation,
Domain::BeaconAttester,
&state.fork,
);
@@ -242,28 +239,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

@@ -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.0
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.0
pub fn verify_attestation_for_state<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation<T>,
@@ -62,35 +58,12 @@ pub fn verify_attestation_for_state<T: EthSpec>(
) -> Result<()> {
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)?;
@@ -101,13 +74,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.0
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!(
@@ -118,7 +89,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,
@@ -128,7 +99,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.0
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.0
pub fn get_slashable_indices<T: EthSpec>(
state: &BeaconState<T>,
attester_slashing: &AttesterSlashing<T>,

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.0
pub fn verify_deposit_signature<T: EthSpec>(
state: &BeaconState<T>,
deposit: &Deposit,
@@ -50,7 +50,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.0
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.0
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.0
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.0
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.0
pub fn verify_proposer_slashing<T: EthSpec>(
proposer_slashing: &ProposerSlashing,
state: &BeaconState<T>,
@@ -26,11 +26,10 @@ pub fn verify_proposer_slashing<T: EthSpec>(
.get(proposer_slashing.proposer_index as usize)
.ok_or_else(|| error(Invalid::ProposerUnknown(proposer_slashing.proposer_index)))?;
// Verify that the epoch is the same
// Verify slots match
verify!(
proposer_slashing.header_1.slot.epoch(T::slots_per_epoch())
== proposer_slashing.header_2.slot.epoch(T::slots_per_epoch()),
Invalid::ProposalEpochMismatch(
proposer_slashing.header_1.slot == proposer_slashing.header_2.slot,
Invalid::ProposalSlotMismatch(
proposer_slashing.header_1.slot,
proposer_slashing.header_2.slot
)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,6 @@ pub struct BlockBuilder<T: EthSpec> {
pub num_attestations: usize,
pub num_deposits: usize,
pub num_exits: usize,
pub num_transfers: usize,
}
impl<T: EthSpec> BlockBuilder<T> {
@@ -30,7 +29,6 @@ impl<T: EthSpec> BlockBuilder<T> {
num_attestations: 0,
num_deposits: 0,
num_exits: 0,
num_transfers: 0,
}
}
@@ -40,7 +38,6 @@ impl<T: EthSpec> BlockBuilder<T> {
self.num_attestations = T::MaxAttestations::to_usize();
self.num_deposits = T::MaxDeposits::to_usize();
self.num_exits = T::MaxVoluntaryExits::to_usize();
self.num_transfers = T::MaxTransfers::to_usize();
}
pub fn set_slot(&mut self, slot: Slot) {
@@ -58,9 +55,7 @@ impl<T: EthSpec> BlockBuilder<T> {
builder.set_slot(state.slot);
let proposer_index = state
.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)
.unwrap();
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
let proposer_keypair = &keypairs[proposer_index];
@@ -152,24 +147,6 @@ impl<T: EthSpec> BlockBuilder<T> {
builder.block.body.voluntary_exits.len()
);
// Insert the maximum possible number of `Transfer` objects.
for _ in 0..self.num_transfers {
let validator_index = validators_iter.next().expect("Insufficient validators.");
// Manually set the validator to be withdrawn.
state.validators[validator_index as usize].withdrawable_epoch = state.previous_epoch();
builder.insert_transfer(
&state,
validator_index,
validator_index,
1,
keypairs[validator_index as usize].clone(),
spec,
);
}
info!("Inserted {} transfers.", builder.block.body.transfers.len());
// Set the eth1 data to be different from the state.
self.block_builder.block.body.eth1_data.block_hash = Hash256::from_slice(&[42; 32]);