mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 04:37:13 +00:00
Update to frozen spec ❄️ (v0.8.1) (#444)
* types: first updates for v0.8 * state_processing: epoch processing v0.8.0 * state_processing: block processing v0.8.0 * tree_hash_derive: support generics in SignedRoot * types v0.8: update to use ssz_types * state_processing v0.8: use ssz_types * ssz_types: add bitwise methods and from_elem * types: fix v0.8 FIXMEs * ssz_types: add bitfield shift_up * ssz_types: iterators and DerefMut for VariableList * types,state_processing: use VariableList * ssz_types: fix BitVector Decode impl Fixed a typo in the implementation of ssz::Decode for BitVector, which caused it to be considered variable length! * types: fix test modules for v0.8 update * types: remove slow type-level arithmetic * state_processing: fix tests for v0.8 * op_pool: update for v0.8 * ssz_types: Bitfield difference length-independent Allow computing the difference of two bitfields of different lengths. * Implement compact committee support * epoch_processing: committee & active index roots * state_processing: genesis state builder v0.8 * state_processing: implement v0.8.1 * Further improve tree_hash * Strip examples, tests from cached_tree_hash * Update TreeHash, un-impl CachedTreeHash * Update bitfield TreeHash, un-impl CachedTreeHash * Update FixedLenVec TreeHash, unimpl CachedTreeHash * Update update tree_hash_derive for new TreeHash * Fix TreeHash, un-impl CachedTreeHash for ssz_types * Remove fixed_len_vec, ssz benches SSZ benches relied upon fixed_len_vec -- it is easier to just delete them and rebuild them later (when necessary) * Remove boolean_bitfield crate * Fix fake_crypto BLS compile errors * Update ef_tests for new v.8 type params * Update ef_tests submodule to v0.8.1 tag * Make fixes to support parsing ssz ef_tests * `compact_committee...` to `compact_committees...` * Derive more traits for `CompactCommittee` * Flip bitfield byte-endianness * Fix tree_hash for bitfields * Modify CLI output for ef_tests * Bump ssz crate version * Update ssz_types doc comment * Del cached tree hash tests from ssz_static tests * Tidy SSZ dependencies * Rename ssz_types crate to eth2_ssz_types * validator_client: update for v0.8 * ssz_types: update union/difference for bit order swap * beacon_node: update for v0.8, EthSpec * types: disable cached tree hash, update min spec * state_processing: fix slot bug in committee update * tests: temporarily disable fork choice harness test See #447 * committee cache: prevent out-of-bounds access In the case where we tried to access the committee of a shard that didn't have a committee in the current epoch, we were accessing elements beyond the end of the shuffling vector and panicking! This commit adds a check to make the failure safe and explicit. * fix bug in get_indexed_attestation and simplify There was a bug in our implementation of get_indexed_attestation whereby incorrect "committee indices" were used to index into the custody bitfield. The bug was only observable in the case where some bits of the custody bitfield were set to 1. The implementation has been simplified to remove the bug, and a test added. * state_proc: workaround for compact committees bug https://github.com/ethereum/eth2.0-specs/issues/1315 * v0.8: updates to make the EF tests pass * Remove redundant max operation checks. * Always supply both messages when checking attestation signatures -- allowing verification of an attestation with no signatures. * Swap the order of the fork and domain constant in `get_domain`, to match the spec. * rustfmt * ef_tests: add new epoch processing tests * Integrate v0.8 into master (compiles) * Remove unused crates, fix clippy lints * Replace v0.6.3 tags w/ v0.8.1 * Remove old comment * Ensure lmd ghost tests only run in release * Update readme
This commit is contained in:
committed by
Paul Hauner
parent
177df12149
commit
a236003a7b
@@ -19,6 +19,7 @@ serde_yaml = "0.8"
|
||||
bls = { path = "../utils/bls" }
|
||||
integer-sqrt = "0.1"
|
||||
itertools = "0.8"
|
||||
eth2_ssz_types = { path = "../utils/ssz_types" }
|
||||
merkle_proof = { path = "../utils/merkle_proof" }
|
||||
tree_hash = { path = "../utils/tree_hash" }
|
||||
tree_hash_derive = { path = "../utils/tree_hash_derive" }
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn bench_epoch_processing_n_validators(c: &mut Criterion, validator_count: u
|
||||
|
||||
// Assert that the state has an attestations for each committee that is able to include an
|
||||
// attestation in the state.
|
||||
let committees_per_epoch = spec.get_epoch_committee_count(validator_count);
|
||||
let committees_per_epoch = spec.get_committee_count(validator_count);
|
||||
let committees_per_slot = committees_per_epoch / T::slots_per_epoch();
|
||||
let previous_epoch_attestations = committees_per_epoch;
|
||||
let current_epoch_attestations =
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
use super::{get_attesting_indices, get_attesting_indices_unsorted};
|
||||
use itertools::{Either, Itertools};
|
||||
use types::*;
|
||||
|
||||
/// Convert `attestation` to (almost) indexed-verifiable form.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn convert_to_indexed<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation,
|
||||
) -> Result<IndexedAttestation, BeaconStateError> {
|
||||
let attesting_indices =
|
||||
get_attesting_indices(state, &attestation.data, &attestation.aggregation_bitfield)?;
|
||||
|
||||
// We verify the custody bitfield by calling `get_attesting_indices_unsorted` and throwing
|
||||
// away the result. This avoids double-sorting - the partition below takes care of the ordering.
|
||||
get_attesting_indices_unsorted(state, &attestation.data, &attestation.custody_bitfield)?;
|
||||
|
||||
let (custody_bit_0_indices, custody_bit_1_indices) =
|
||||
attesting_indices.into_iter().enumerate().partition_map(
|
||||
|(committee_idx, validator_idx)| match attestation.custody_bitfield.get(committee_idx) {
|
||||
Ok(true) => Either::Right(validator_idx as u64),
|
||||
_ => Either::Left(validator_idx as u64),
|
||||
},
|
||||
);
|
||||
|
||||
Ok(IndexedAttestation {
|
||||
custody_bit_0_indices,
|
||||
custody_bit_1_indices,
|
||||
data: attestation.data.clone(),
|
||||
signature: attestation.signature.clone(),
|
||||
})
|
||||
}
|
||||
@@ -1,44 +1,33 @@
|
||||
use crate::common::verify_bitfield_length;
|
||||
use std::collections::BTreeSet;
|
||||
use types::*;
|
||||
|
||||
/// Returns validator indices which participated in the attestation, sorted by increasing index.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_attesting_indices<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation_data: &AttestationData,
|
||||
bitfield: &Bitfield,
|
||||
) -> Result<Vec<usize>, BeaconStateError> {
|
||||
get_attesting_indices_unsorted(state, attestation_data, bitfield).map(|mut indices| {
|
||||
// Fast unstable sort is safe because validator indices are unique
|
||||
indices.sort_unstable();
|
||||
indices
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns validator indices which participated in the attestation, unsorted.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn get_attesting_indices_unsorted<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation_data: &AttestationData,
|
||||
bitfield: &Bitfield,
|
||||
) -> Result<Vec<usize>, BeaconStateError> {
|
||||
bitlist: &BitList<T::MaxValidatorsPerCommittee>,
|
||||
) -> Result<BTreeSet<usize>, BeaconStateError> {
|
||||
let target_relative_epoch =
|
||||
RelativeEpoch::from_epoch(state.current_epoch(), attestation_data.target_epoch)?;
|
||||
RelativeEpoch::from_epoch(state.current_epoch(), attestation_data.target.epoch)?;
|
||||
|
||||
let committee =
|
||||
state.get_crosslink_committee_for_shard(attestation_data.shard, target_relative_epoch)?;
|
||||
let committee = state.get_crosslink_committee_for_shard(
|
||||
attestation_data.crosslink.shard,
|
||||
target_relative_epoch,
|
||||
)?;
|
||||
|
||||
if !verify_bitfield_length(&bitfield, committee.committee.len()) {
|
||||
/* TODO(freeze): re-enable this?
|
||||
if bitlist.len() > committee.committee.len() {
|
||||
return Err(BeaconStateError::InvalidBitfield);
|
||||
}
|
||||
*/
|
||||
|
||||
Ok(committee
|
||||
.committee
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, validator_index)| match bitfield.get(i) {
|
||||
.filter_map(|(i, validator_index)| match bitlist.get(i) {
|
||||
Ok(true) => Some(*validator_index),
|
||||
_ => None,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
/// Return the compact committee root at `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
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());
|
||||
// FIXME: this is a spec bug, whereby the start shard for the epoch after the next epoch
|
||||
// is mistakenly used. The start shard from the cache SHOULD work.
|
||||
// Waiting on a release to fix https://github.com/ethereum/eth2.0-specs/issues/1315
|
||||
// let start_shard = state.get_epoch_start_shard(relative_epoch)?;
|
||||
let start_shard = state.next_epoch_start_shard(spec)?;
|
||||
|
||||
for committee_number in 0..state.get_committee_count(relative_epoch)? {
|
||||
let shard = (start_shard + committee_number) % T::ShardCount::to_u64();
|
||||
// FIXME: this is a partial workaround for the above, but it only works in the case
|
||||
// where there's a committee for every shard in every epoch. It works for the minimal
|
||||
// tests but not the mainnet ones.
|
||||
let fake_shard = (shard + 1) % T::ShardCount::to_u64();
|
||||
|
||||
for &index in state
|
||||
.get_crosslink_committee_for_shard(fake_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()))
|
||||
}
|
||||
122
eth2/state_processing/src/common/get_indexed_attestation.rs
Normal file
122
eth2/state_processing/src/common/get_indexed_attestation.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use super::get_attesting_indices;
|
||||
use crate::per_block_processing::errors::{
|
||||
AttestationInvalid as Invalid, AttestationValidationError as Error,
|
||||
};
|
||||
use types::*;
|
||||
|
||||
/// Convert `attestation` to (almost) indexed-verifiable form.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn get_indexed_attestation<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
) -> Result<IndexedAttestation<T>, Error> {
|
||||
let attesting_indices =
|
||||
get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;
|
||||
|
||||
let custody_bit_1_indices =
|
||||
get_attesting_indices(state, &attestation.data, &attestation.custody_bits)?;
|
||||
|
||||
verify!(
|
||||
custody_bit_1_indices.is_subset(&attesting_indices),
|
||||
Invalid::CustodyBitfieldNotSubset
|
||||
);
|
||||
|
||||
let custody_bit_0_indices = &attesting_indices - &custody_bit_1_indices;
|
||||
|
||||
Ok(IndexedAttestation {
|
||||
custody_bit_0_indices: VariableList::new(
|
||||
custody_bit_0_indices
|
||||
.into_iter()
|
||||
.map(|x| x as u64)
|
||||
.collect(),
|
||||
)?,
|
||||
custody_bit_1_indices: VariableList::new(
|
||||
custody_bit_1_indices
|
||||
.into_iter()
|
||||
.map(|x| x as u64)
|
||||
.collect(),
|
||||
)?,
|
||||
data: attestation.data.clone(),
|
||||
signature: attestation.signature.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use itertools::{Either, Itertools};
|
||||
use types::test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn custody_bitfield_indexing() {
|
||||
let validator_count = 128;
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
let state_builder =
|
||||
TestingBeaconStateBuilder::<MinimalEthSpec>::from_default_keypairs_file_if_exists(
|
||||
validator_count,
|
||||
&spec,
|
||||
);
|
||||
let (mut state, keypairs) = state_builder.build();
|
||||
state.build_all_caches(&spec).unwrap();
|
||||
state.slot += 1;
|
||||
|
||||
let shard = 0;
|
||||
let cc = state
|
||||
.get_crosslink_committee_for_shard(shard, RelativeEpoch::Current)
|
||||
.unwrap();
|
||||
|
||||
// Make a third of the validators sign with custody bit 0, a third with custody bit 1
|
||||
// and a third not sign at all.
|
||||
assert!(
|
||||
cc.committee.len() >= 4,
|
||||
"need at least 4 validators per committee for this test to work"
|
||||
);
|
||||
let (mut bit_0_indices, mut bit_1_indices): (Vec<_>, Vec<_>) = cc
|
||||
.committee
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| i % 3 != 0)
|
||||
.partition_map(|(i, index)| {
|
||||
if i % 3 == 1 {
|
||||
Either::Left(*index)
|
||||
} else {
|
||||
Either::Right(*index)
|
||||
}
|
||||
});
|
||||
assert!(!bit_0_indices.is_empty());
|
||||
assert!(!bit_1_indices.is_empty());
|
||||
|
||||
let bit_0_keys = bit_0_indices
|
||||
.iter()
|
||||
.map(|validator_index| &keypairs[*validator_index].sk)
|
||||
.collect::<Vec<_>>();
|
||||
let bit_1_keys = bit_1_indices
|
||||
.iter()
|
||||
.map(|validator_index| &keypairs[*validator_index].sk)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut attestation_builder =
|
||||
TestingAttestationBuilder::new(&state, &cc.committee, cc.slot, shard, &spec);
|
||||
attestation_builder
|
||||
.sign(&bit_0_indices, &bit_0_keys, &state.fork, &spec, false)
|
||||
.sign(&bit_1_indices, &bit_1_keys, &state.fork, &spec, true);
|
||||
let attestation = attestation_builder.build();
|
||||
|
||||
let indexed_attestation = get_indexed_attestation(&state, &attestation).unwrap();
|
||||
|
||||
bit_0_indices.sort();
|
||||
bit_1_indices.sort();
|
||||
|
||||
assert!(indexed_attestation
|
||||
.custody_bit_0_indices
|
||||
.iter()
|
||||
.copied()
|
||||
.eq(bit_0_indices.iter().map(|idx| *idx as u64)));
|
||||
assert!(indexed_attestation
|
||||
.custody_bit_1_indices
|
||||
.iter()
|
||||
.copied()
|
||||
.eq(bit_1_indices.iter().map(|idx| *idx as u64)));
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,23 @@ use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Initiate the exit of the validator of the given `index`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn initiate_validator_exit<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
if index >= state.validator_registry.len() {
|
||||
if index >= state.validators.len() {
|
||||
return Err(Error::UnknownValidator);
|
||||
}
|
||||
|
||||
// Return if the validator already initiated exit
|
||||
if state.validator_registry[index].exit_epoch != spec.far_future_epoch {
|
||||
if state.validators[index].exit_epoch != spec.far_future_epoch {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Compute exit queue epoch
|
||||
let delayed_epoch = state.get_delayed_activation_exit_epoch(state.current_epoch(), spec);
|
||||
let delayed_epoch = state.compute_activation_exit_epoch(state.current_epoch(), spec);
|
||||
let mut exit_queue_epoch = state
|
||||
.exit_cache
|
||||
.max_epoch()
|
||||
@@ -31,8 +31,8 @@ pub fn initiate_validator_exit<T: EthSpec>(
|
||||
}
|
||||
|
||||
state.exit_cache.record_validator_exit(exit_queue_epoch);
|
||||
state.validator_registry[index].exit_epoch = exit_queue_epoch;
|
||||
state.validator_registry[index].withdrawable_epoch =
|
||||
state.validators[index].exit_epoch = exit_queue_epoch;
|
||||
state.validators[index].withdrawable_epoch =
|
||||
exit_queue_epoch + spec.min_validator_withdrawability_delay;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
mod convert_to_indexed;
|
||||
mod get_attesting_indices;
|
||||
mod get_compact_committees_root;
|
||||
mod get_indexed_attestation;
|
||||
mod initiate_validator_exit;
|
||||
mod slash_validator;
|
||||
mod verify_bitfield;
|
||||
|
||||
pub use convert_to_indexed::convert_to_indexed;
|
||||
pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_unsorted};
|
||||
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;
|
||||
pub use verify_bitfield::verify_bitfield_length;
|
||||
|
||||
@@ -1,45 +1,51 @@
|
||||
use crate::common::initiate_validator_exit;
|
||||
use std::cmp;
|
||||
use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Slash the validator with index ``index``.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn slash_validator<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
slashed_index: usize,
|
||||
opt_whistleblower_index: Option<usize>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
if slashed_index >= state.validator_registry.len() || slashed_index >= state.balances.len() {
|
||||
if slashed_index >= state.validators.len() || slashed_index >= state.balances.len() {
|
||||
return Err(BeaconStateError::UnknownValidator);
|
||||
}
|
||||
|
||||
let current_epoch = state.current_epoch();
|
||||
let epoch = state.current_epoch();
|
||||
|
||||
initiate_validator_exit(state, slashed_index, spec)?;
|
||||
|
||||
state.validator_registry[slashed_index].slashed = true;
|
||||
state.validator_registry[slashed_index].withdrawable_epoch =
|
||||
current_epoch + Epoch::from(T::latest_slashed_exit_length());
|
||||
let slashed_balance = state.get_effective_balance(slashed_index, spec)?;
|
||||
|
||||
state.set_slashed_balance(
|
||||
current_epoch,
|
||||
state.get_slashed_balance(current_epoch)? + slashed_balance,
|
||||
state.validators[slashed_index].slashed = true;
|
||||
state.validators[slashed_index].withdrawable_epoch = cmp::max(
|
||||
state.validators[slashed_index].withdrawable_epoch,
|
||||
epoch + Epoch::from(T::EpochsPerSlashingsVector::to_u64()),
|
||||
);
|
||||
let validator_effective_balance = state.get_effective_balance(slashed_index, spec)?;
|
||||
state.set_slashings(
|
||||
epoch,
|
||||
state.get_slashings(epoch)? + validator_effective_balance,
|
||||
)?;
|
||||
safe_sub_assign!(
|
||||
state.balances[slashed_index],
|
||||
validator_effective_balance / spec.min_slashing_penalty_quotient
|
||||
);
|
||||
|
||||
// Apply proposer and whistleblower rewards
|
||||
let proposer_index =
|
||||
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)?;
|
||||
let whistleblower_index = opt_whistleblower_index.unwrap_or(proposer_index);
|
||||
let whistleblowing_reward = slashed_balance / spec.whistleblowing_reward_quotient;
|
||||
let proposer_reward = whistleblowing_reward / spec.proposer_reward_quotient;
|
||||
let whistleblower_reward = validator_effective_balance / spec.whistleblower_reward_quotient;
|
||||
let proposer_reward = whistleblower_reward / spec.proposer_reward_quotient;
|
||||
|
||||
safe_add_assign!(state.balances[proposer_index], proposer_reward);
|
||||
safe_add_assign!(
|
||||
state.balances[whistleblower_index],
|
||||
whistleblowing_reward.saturating_sub(proposer_reward)
|
||||
whistleblower_reward.saturating_sub(proposer_reward)
|
||||
);
|
||||
safe_sub_assign!(state.balances[slashed_index], whistleblowing_reward);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
use types::*;
|
||||
|
||||
/// Verify ``bitfield`` against the ``committee_size``.
|
||||
///
|
||||
/// Is title `verify_bitfield` in spec.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn verify_bitfield_length(bitfield: &Bitfield, committee_size: usize) -> bool {
|
||||
if bitfield.num_bytes() != ((committee_size + 7) / 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in committee_size..(bitfield.num_bytes() * 8) {
|
||||
if bitfield.get(i).unwrap_or(false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bitfield_length() {
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b0000_0001]), 4),
|
||||
true
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b0001_0001]), 4),
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b0000_0000]), 4),
|
||||
true
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b1000_0000]), 8),
|
||||
true
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b1000_0000, 0b0000_0000]), 16),
|
||||
true
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b1000_0000, 0b0000_0000]), 15),
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(&Bitfield::from_bytes(&[0b0000_0000, 0b0000_0000]), 8),
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(
|
||||
&Bitfield::from_bytes(&[0b0000_0000, 0b0000_0000, 0b0000_0000]),
|
||||
8
|
||||
),
|
||||
false
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
verify_bitfield_length(
|
||||
&Bitfield::from_bytes(&[0b0000_0000, 0b0000_0000, 0b0000_0000]),
|
||||
24
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
61
eth2/state_processing/src/genesis.rs
Normal file
61
eth2/state_processing/src/genesis.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use super::per_block_processing::{errors::BlockProcessingError, process_deposits};
|
||||
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
|
||||
// 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,
|
||||
eth1_timestamp: u64,
|
||||
deposits: Vec<Deposit>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<T>, BlockProcessingError> {
|
||||
let genesis_time =
|
||||
eth1_timestamp - eth1_timestamp % spec.seconds_per_day + 2 * spec.seconds_per_day;
|
||||
let eth1_data = Eth1Data {
|
||||
// Temporary deposit root
|
||||
deposit_root: Hash256::zero(),
|
||||
deposit_count: deposits.len() as u64,
|
||||
block_hash: eth1_block_hash,
|
||||
};
|
||||
let mut state = BeaconState::new(genesis_time, eth1_data, spec);
|
||||
|
||||
// Process deposits
|
||||
let leaves: Vec<_> = deposits
|
||||
.iter()
|
||||
.map(|deposit| deposit.data.clone())
|
||||
.collect();
|
||||
for (index, deposit) in deposits.into_iter().enumerate() {
|
||||
let deposit_data_list = VariableList::<_, U4294967296>::from(leaves[..=index].to_vec());
|
||||
state.eth1_data.deposit_root = Hash256::from_slice(&deposit_data_list.tree_hash_root());
|
||||
process_deposits(&mut state, &[deposit], spec)?;
|
||||
}
|
||||
|
||||
// Process activations
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
validator.effective_balance = std::cmp::min(
|
||||
balance - balance % spec.effective_balance_increment,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
if validator.effective_balance == spec.max_effective_balance {
|
||||
validator.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
use super::per_block_processing::{errors::BlockProcessingError, process_deposits};
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
pub enum GenesisError {
|
||||
BlockProcessingError(BlockProcessingError),
|
||||
BeaconStateError(BeaconStateError),
|
||||
}
|
||||
|
||||
/// Returns the genesis `BeaconState`
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn get_genesis_beacon_state<T: EthSpec>(
|
||||
genesis_validator_deposits: &[Deposit],
|
||||
genesis_time: u64,
|
||||
genesis_eth1_data: Eth1Data,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<T>, BlockProcessingError> {
|
||||
// Get the genesis `BeaconState`
|
||||
let mut state = BeaconState::genesis(genesis_time, genesis_eth1_data, spec);
|
||||
|
||||
// Process genesis deposits.
|
||||
process_deposits(&mut state, genesis_validator_deposits, spec)?;
|
||||
|
||||
// Process genesis activations.
|
||||
for validator in &mut state.validator_registry {
|
||||
if validator.effective_balance >= spec.max_effective_balance {
|
||||
validator.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the current epoch cache is built.
|
||||
state.build_committee_cache(RelativeEpoch::Current, spec)?;
|
||||
|
||||
// Set all the active index roots to be the genesis active index root.
|
||||
let active_validator_indices = state
|
||||
.get_cached_active_validator_indices(RelativeEpoch::Current)?
|
||||
.to_vec();
|
||||
let genesis_active_index_root = Hash256::from_slice(&active_validator_indices.tree_hash_root());
|
||||
state.fill_active_index_roots_with(genesis_active_index_root);
|
||||
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
impl From<BlockProcessingError> for GenesisError {
|
||||
fn from(e: BlockProcessingError) -> GenesisError {
|
||||
GenesisError::BlockProcessingError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for GenesisError {
|
||||
fn from(e: BeaconStateError) -> GenesisError {
|
||||
GenesisError::BeaconStateError(e)
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
mod macros;
|
||||
|
||||
pub mod common;
|
||||
pub mod get_genesis_state;
|
||||
pub mod genesis;
|
||||
pub mod per_block_processing;
|
||||
pub mod per_epoch_processing;
|
||||
pub mod per_slot_processing;
|
||||
|
||||
pub use get_genesis_state::get_genesis_beacon_state;
|
||||
pub use genesis::initialize_beacon_state_from_eth1;
|
||||
pub use per_block_processing::{
|
||||
errors::{BlockInvalid, BlockProcessingError},
|
||||
per_block_processing, per_block_processing_without_verifying_block_signature,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use crate::common::{initiate_validator_exit, slash_validator};
|
||||
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::FromIterator;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::*;
|
||||
|
||||
@@ -8,30 +10,29 @@ pub use self::verify_attester_slashing::{
|
||||
get_slashable_indices, get_slashable_indices_modular, verify_attester_slashing,
|
||||
};
|
||||
pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
||||
pub use validate_attestation::{
|
||||
validate_attestation, validate_attestation_time_independent_only,
|
||||
validate_attestation_without_signature,
|
||||
pub use is_valid_indexed_attestation::{
|
||||
is_valid_indexed_attestation, is_valid_indexed_attestation_without_signature,
|
||||
};
|
||||
pub use verify_attestation::{
|
||||
verify_attestation, verify_attestation_time_independent_only,
|
||||
verify_attestation_without_signature,
|
||||
};
|
||||
pub use verify_deposit::{
|
||||
get_existing_validator_index, verify_deposit_index, verify_deposit_merkle_proof,
|
||||
verify_deposit_signature,
|
||||
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_indexed_attestation::{
|
||||
verify_indexed_attestation, verify_indexed_attestation_without_signature,
|
||||
};
|
||||
pub use verify_transfer::{
|
||||
execute_transfer, verify_transfer, verify_transfer_time_independent_only,
|
||||
};
|
||||
|
||||
pub mod block_processing_builder;
|
||||
pub mod errors;
|
||||
mod is_valid_indexed_attestation;
|
||||
pub mod tests;
|
||||
mod validate_attestation;
|
||||
mod verify_attestation;
|
||||
mod verify_attester_slashing;
|
||||
mod verify_deposit;
|
||||
mod verify_exit;
|
||||
mod verify_indexed_attestation;
|
||||
mod verify_proposer_slashing;
|
||||
mod verify_transfer;
|
||||
|
||||
@@ -40,10 +41,10 @@ mod verify_transfer;
|
||||
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
|
||||
/// returns an error describing why the block was invalid or how the function failed to execute.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn per_block_processing<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
per_block_processing_signature_optional(state, block, true, spec)
|
||||
@@ -55,10 +56,10 @@ pub fn per_block_processing<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
|
||||
/// returns an error describing why the block was invalid or how the function failed to execute.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn per_block_processing_without_verifying_block_signature<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
per_block_processing_signature_optional(state, block, false, spec)
|
||||
@@ -70,10 +71,10 @@ pub fn per_block_processing_without_verifying_block_signature<T: EthSpec>(
|
||||
/// Returns `Ok(())` if the block is valid and the state was successfully updated. Otherwise
|
||||
/// returns an error describing why the block was invalid or how the function failed to execute.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn per_block_processing_signature_optional<T: EthSpec>(
|
||||
mut state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
should_verify_block_signature: bool,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
@@ -84,7 +85,7 @@ fn per_block_processing_signature_optional<T: EthSpec>(
|
||||
state.build_committee_cache(RelativeEpoch::Current, spec)?;
|
||||
|
||||
process_randao(&mut state, &block, &spec)?;
|
||||
process_eth1_data(&mut state, &block.body.eth1_data, spec)?;
|
||||
process_eth1_data(&mut state, &block.body.eth1_data)?;
|
||||
process_proposer_slashings(&mut state, &block.body.proposer_slashings, spec)?;
|
||||
process_attester_slashings(&mut state, &block.body.attester_slashings, spec)?;
|
||||
process_attestations(&mut state, &block.body.attestations, spec)?;
|
||||
@@ -97,10 +98,10 @@ fn per_block_processing_signature_optional<T: EthSpec>(
|
||||
|
||||
/// Processes the block header.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_block_header<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
should_verify_block_signature: bool,
|
||||
) -> Result<(), Error> {
|
||||
@@ -109,18 +110,18 @@ pub fn process_block_header<T: EthSpec>(
|
||||
let expected_previous_block_root =
|
||||
Hash256::from_slice(&state.latest_block_header.signed_root());
|
||||
verify!(
|
||||
block.previous_block_root == expected_previous_block_root,
|
||||
block.parent_root == expected_previous_block_root,
|
||||
Invalid::ParentBlockRootMismatch {
|
||||
state: expected_previous_block_root,
|
||||
block: block.previous_block_root,
|
||||
block: block.parent_root,
|
||||
}
|
||||
);
|
||||
|
||||
state.latest_block_header = block.temporary_block_header(spec);
|
||||
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 = &state.validator_registry[proposer_idx];
|
||||
let proposer = &state.validators[proposer_idx];
|
||||
verify!(!proposer.slashed, Invalid::ProposerSlashed(proposer_idx));
|
||||
|
||||
if should_verify_block_signature {
|
||||
@@ -132,13 +133,13 @@ pub fn process_block_header<T: EthSpec>(
|
||||
|
||||
/// Verifies the signature of a block.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_block_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let block_proposer = &state.validator_registry
|
||||
let block_proposer = &state.validators
|
||||
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
|
||||
|
||||
let domain = spec.get_domain(
|
||||
@@ -160,16 +161,16 @@ 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_randao<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let block_proposer = &state.validator_registry
|
||||
let block_proposer = &state.validators
|
||||
[state.get_beacon_proposer_index(block.slot, RelativeEpoch::Current, spec)?];
|
||||
|
||||
// Verify the RANDAO is a valid signature of the proposer.
|
||||
// Verify RANDAO reveal.
|
||||
verify!(
|
||||
block.body.randao_reveal.verify(
|
||||
&state.current_epoch().tree_hash_root()[..],
|
||||
@@ -191,22 +192,21 @@ pub fn process_randao<T: EthSpec>(
|
||||
|
||||
/// Update the `state.eth1_data_votes` based upon the `eth1_data` provided.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_eth1_data<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
eth1_data: &Eth1Data,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
state.eth1_data_votes.push(eth1_data.clone());
|
||||
state.eth1_data_votes.push(eth1_data.clone())?;
|
||||
|
||||
let num_votes = state
|
||||
.eth1_data_votes
|
||||
.iter()
|
||||
.filter(|vote| *vote == eth1_data)
|
||||
.count() as u64;
|
||||
.count();
|
||||
|
||||
if num_votes * 2 > spec.slots_per_eth1_voting_period {
|
||||
state.latest_eth1_data = eth1_data.clone();
|
||||
if num_votes * 2 > T::SlotsPerEth1VotingPeriod::to_usize() {
|
||||
state.eth1_data = eth1_data.clone();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -217,17 +217,12 @@ 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_proposer_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
proposer_slashings: &[ProposerSlashing],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify!(
|
||||
proposer_slashings.len() as u64 <= spec.max_proposer_slashings,
|
||||
Invalid::MaxProposerSlashingsExceeded
|
||||
);
|
||||
|
||||
// Verify proposer slashings in parallel.
|
||||
proposer_slashings
|
||||
.par_iter()
|
||||
@@ -250,21 +245,15 @@ 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_attester_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
attester_slashings: &[AttesterSlashing],
|
||||
attester_slashings: &[AttesterSlashing<T>],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify!(
|
||||
attester_slashings.len() as u64 <= spec.max_attester_slashings,
|
||||
Invalid::MaxAttesterSlashingsExceed
|
||||
);
|
||||
|
||||
// Verify the `IndexedAttestation`s in parallel (these are the resource-consuming objects, not
|
||||
// the `AttesterSlashing`s themselves).
|
||||
let mut indexed_attestations: Vec<&IndexedAttestation> =
|
||||
Vec::with_capacity(attester_slashings.len() * 2);
|
||||
let mut indexed_attestations: Vec<&_> = Vec::with_capacity(attester_slashings.len() * 2);
|
||||
for attester_slashing in attester_slashings {
|
||||
indexed_attestations.push(&attester_slashing.attestation_1);
|
||||
indexed_attestations.push(&attester_slashing.attestation_2);
|
||||
@@ -275,7 +264,7 @@ pub fn process_attester_slashings<T: EthSpec>(
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, indexed_attestation)| {
|
||||
verify_indexed_attestation(&state, indexed_attestation, spec)
|
||||
is_valid_indexed_attestation(&state, indexed_attestation, spec)
|
||||
.map_err(|e| e.into_with_index(i))
|
||||
})?;
|
||||
let all_indexed_attestations_have_been_checked = true;
|
||||
@@ -308,17 +297,12 @@ 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_attestations<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
attestations: &[Attestation],
|
||||
attestations: &[Attestation<T>],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify!(
|
||||
attestations.len() as u64 <= spec.max_attestations,
|
||||
Invalid::MaxAttestationsExceeded
|
||||
);
|
||||
|
||||
// Ensure the previous epoch cache exists.
|
||||
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
|
||||
|
||||
@@ -327,25 +311,27 @@ pub fn process_attestations<T: EthSpec>(
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, attestation)| {
|
||||
validate_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i))
|
||||
verify_attestation(state, attestation, spec).map_err(|e| e.into_with_index(i))
|
||||
})?;
|
||||
|
||||
// Update the state in series.
|
||||
let proposer_index =
|
||||
state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)? as u64;
|
||||
for attestation in attestations {
|
||||
let attestation_slot = state.get_attestation_slot(&attestation.data)?;
|
||||
let attestation_slot = state.get_attestation_data_slot(&attestation.data)?;
|
||||
let pending_attestation = PendingAttestation {
|
||||
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
|
||||
aggregation_bits: attestation.aggregation_bits.clone(),
|
||||
data: attestation.data.clone(),
|
||||
inclusion_delay: (state.slot - attestation_slot).as_u64(),
|
||||
proposer_index,
|
||||
};
|
||||
|
||||
if attestation.data.target_epoch == state.current_epoch() {
|
||||
state.current_epoch_attestations.push(pending_attestation)
|
||||
if attestation.data.target.epoch == state.current_epoch() {
|
||||
state.current_epoch_attestations.push(pending_attestation)?;
|
||||
} else {
|
||||
state.previous_epoch_attestations.push(pending_attestation)
|
||||
state
|
||||
.previous_epoch_attestations
|
||||
.push(pending_attestation)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,7 +343,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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_deposits<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
deposits: &[Deposit],
|
||||
@@ -366,8 +352,8 @@ pub fn process_deposits<T: EthSpec>(
|
||||
verify!(
|
||||
deposits.len() as u64
|
||||
== std::cmp::min(
|
||||
spec.max_deposits,
|
||||
state.latest_eth1_data.deposit_count - state.deposit_index
|
||||
T::MaxDeposits::to_u64(),
|
||||
state.eth1_data.deposit_count - state.eth1_deposit_index
|
||||
),
|
||||
Invalid::DepositCountInvalid
|
||||
);
|
||||
@@ -377,14 +363,13 @@ pub fn process_deposits<T: EthSpec>(
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, deposit)| {
|
||||
verify_deposit_merkle_proof(state, deposit, spec).map_err(|e| e.into_with_index(i))
|
||||
verify_deposit_merkle_proof(state, deposit, state.eth1_deposit_index + i as u64, spec)
|
||||
.map_err(|e| e.into_with_index(i))
|
||||
})?;
|
||||
|
||||
// Check `state.deposit_index` and update the state in series.
|
||||
// Update the state in series.
|
||||
for (i, deposit) in deposits.iter().enumerate() {
|
||||
verify_deposit_index(state, deposit).map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
state.deposit_index += 1;
|
||||
state.eth1_deposit_index += 1;
|
||||
|
||||
// Ensure the state's pubkey cache is fully up-to-date, it will be used to check to see if the
|
||||
// depositing validator already exists in the registry.
|
||||
@@ -421,8 +406,8 @@ pub fn process_deposits<T: EthSpec>(
|
||||
),
|
||||
slashed: false,
|
||||
};
|
||||
state.validator_registry.push(validator);
|
||||
state.balances.push(deposit.data.amount);
|
||||
state.validators.push(validator)?;
|
||||
state.balances.push(deposit.data.amount)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,17 +419,12 @@ pub fn process_deposits<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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_exits<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
voluntary_exits: &[VoluntaryExit],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify!(
|
||||
voluntary_exits.len() as u64 <= spec.max_voluntary_exits,
|
||||
Invalid::MaxExitsExceeded
|
||||
);
|
||||
|
||||
// Verify exits in parallel.
|
||||
voluntary_exits
|
||||
.par_iter()
|
||||
@@ -466,15 +446,16 @@ pub fn process_exits<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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_transfers<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
transfers: &[Transfer],
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Verify that there are no duplicate transfers
|
||||
verify!(
|
||||
transfers.len() as u64 <= spec.max_transfers,
|
||||
Invalid::MaxTransfersExceed
|
||||
transfers.len() == HashSet::<_>::from_iter(transfers).len(),
|
||||
Invalid::DuplicateTransfers
|
||||
);
|
||||
|
||||
transfers
|
||||
|
||||
@@ -4,8 +4,7 @@ use types::*;
|
||||
|
||||
pub struct BlockProcessingBuilder<T: EthSpec> {
|
||||
pub state_builder: TestingBeaconStateBuilder<T>,
|
||||
pub block_builder: TestingBeaconBlockBuilder,
|
||||
|
||||
pub block_builder: TestingBeaconBlockBuilder<T>,
|
||||
pub num_validators: usize,
|
||||
}
|
||||
|
||||
@@ -36,15 +35,15 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
randao_sk: Option<SecretKey>,
|
||||
previous_block_root: Option<Hash256>,
|
||||
spec: &ChainSpec,
|
||||
) -> (BeaconBlock, BeaconState<T>) {
|
||||
) -> (BeaconBlock<T>, BeaconState<T>) {
|
||||
let (state, keypairs) = self.state_builder.build();
|
||||
let builder = &mut self.block_builder;
|
||||
|
||||
builder.set_slot(state.slot);
|
||||
|
||||
match previous_block_root {
|
||||
Some(root) => builder.set_previous_block_root(root),
|
||||
None => builder.set_previous_block_root(Hash256::from_slice(
|
||||
Some(root) => builder.set_parent_root(root),
|
||||
None => builder.set_parent_root(Hash256::from_slice(
|
||||
&state.latest_block_header.signed_root(),
|
||||
)),
|
||||
}
|
||||
@@ -55,13 +54,11 @@ impl<T: EthSpec> BlockProcessingBuilder<T> {
|
||||
let keypair = &keypairs[proposer_index];
|
||||
|
||||
match randao_sk {
|
||||
Some(sk) => builder.set_randao_reveal::<T>(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal::<T>(&keypair.sk, &state.fork, spec),
|
||||
Some(sk) => builder.set_randao_reveal(&sk, &state.fork, spec),
|
||||
None => builder.set_randao_reveal(&keypair.sk, &state.fork, spec),
|
||||
}
|
||||
|
||||
let block = self
|
||||
.block_builder
|
||||
.build::<T>(&keypair.sk, &state.fork, spec);
|
||||
let block = self.block_builder.build(&keypair.sk, &state.fork, spec);
|
||||
|
||||
(block, state)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ pub enum BlockProcessingError {
|
||||
Invalid(BlockInvalid),
|
||||
/// Encountered a `BeaconStateError` whilst attempting to determine validity.
|
||||
BeaconStateError(BeaconStateError),
|
||||
/// Encountered an `ssz_types::Error` whilst attempting to determine validity.
|
||||
SszTypesError(ssz_types::Error),
|
||||
}
|
||||
|
||||
impl_from_beacon_state_error!(BlockProcessingError);
|
||||
@@ -78,6 +80,7 @@ pub enum BlockInvalid {
|
||||
MaxAttesterSlashingsExceed,
|
||||
MaxProposerSlashingsExceeded,
|
||||
DepositCountInvalid,
|
||||
DuplicateTransfers,
|
||||
MaxExitsExceeded,
|
||||
MaxTransfersExceed,
|
||||
AttestationInvalid(usize, AttestationInvalid),
|
||||
@@ -92,6 +95,15 @@ pub enum BlockInvalid {
|
||||
DepositProcessingFailed(usize),
|
||||
ExitInvalid(usize, ExitInvalid),
|
||||
TransferInvalid(usize, TransferInvalid),
|
||||
// NOTE: this is only used in tests, normally a state root mismatch is handled
|
||||
// in the beacon_chain rather than in state_processing
|
||||
StateRootMismatch,
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for BlockProcessingError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
BlockProcessingError::SszTypesError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<BlockProcessingError> for BlockInvalid {
|
||||
@@ -116,8 +128,8 @@ pub enum AttestationValidationError {
|
||||
/// Describes why an object is invalid.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationInvalid {
|
||||
/// Attestation references a pre-genesis slot.
|
||||
PreGenesis { genesis: Slot, attestation: Slot },
|
||||
/// Shard exceeds SHARD_COUNT.
|
||||
BadShard,
|
||||
/// Attestation included before the inclusion delay.
|
||||
IncludedTooEarly {
|
||||
state: Slot,
|
||||
@@ -128,27 +140,23 @@ pub enum AttestationInvalid {
|
||||
IncludedTooLate { state: Slot, attestation: Slot },
|
||||
/// Attestation target epoch does not match the current or previous epoch.
|
||||
BadTargetEpoch,
|
||||
/// Attestation justified epoch does not match the states current or previous justified epoch.
|
||||
/// Attestation justified checkpoint doesn't match the state's current or previous justified
|
||||
/// checkpoint.
|
||||
///
|
||||
/// `is_current` is `true` if the attestation was compared to the
|
||||
/// `state.current_justified_epoch`, `false` if compared to `state.previous_justified_epoch`.
|
||||
WrongJustifiedEpoch {
|
||||
state: Epoch,
|
||||
attestation: Epoch,
|
||||
is_current: bool,
|
||||
},
|
||||
/// Attestation justified epoch root does not match root known to the state.
|
||||
///
|
||||
/// `is_current` is `true` if the attestation was compared to the
|
||||
/// `state.current_justified_epoch`, `false` if compared to `state.previous_justified_epoch`.
|
||||
WrongJustifiedRoot {
|
||||
state: Hash256,
|
||||
attestation: Hash256,
|
||||
/// `state.current_justified_checkpoint`, `false` if compared to `state.previous_justified_checkpoint`.
|
||||
WrongJustifiedCheckpoint {
|
||||
state: Checkpoint,
|
||||
attestation: Checkpoint,
|
||||
is_current: bool,
|
||||
},
|
||||
/// Attestation crosslink root does not match the state crosslink root for the attestations
|
||||
/// slot.
|
||||
BadPreviousCrosslink,
|
||||
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
|
||||
@@ -164,6 +172,8 @@ pub enum AttestationInvalid {
|
||||
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.
|
||||
@@ -186,6 +196,12 @@ impl From<IndexedAttestationValidationError> for AttestationValidationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for AttestationValidationError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
Self::from(IndexedAttestationValidationError::from(error))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* `AttesterSlashing` Validation
|
||||
*/
|
||||
@@ -239,12 +255,14 @@ pub enum IndexedAttestationInvalid {
|
||||
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)
|
||||
MaxIndicesExceed(u64, usize),
|
||||
MaxIndicesExceed(usize, usize),
|
||||
/// The validator indices were not in increasing order.
|
||||
///
|
||||
/// The error occurred between the given `index` and `index + 1`
|
||||
@@ -263,6 +281,14 @@ impl Into<IndexedAttestationInvalid> for IndexedAttestationValidationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for IndexedAttestationValidationError {
|
||||
fn from(error: ssz_types::Error) -> Self {
|
||||
IndexedAttestationValidationError::Invalid(
|
||||
IndexedAttestationInvalid::CustodyBitfieldBoundsError(error),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl_into_with_index_without_beacon_error!(
|
||||
IndexedAttestationValidationError,
|
||||
IndexedAttestationInvalid
|
||||
@@ -356,7 +382,10 @@ pub enum ExitInvalid {
|
||||
/// The exit is for a future epoch.
|
||||
FutureEpoch { state: Epoch, exit: Epoch },
|
||||
/// The validator has not been active for long enough.
|
||||
TooYoungToLeave { lifespan: Epoch, expected: u64 },
|
||||
TooYoungToExit {
|
||||
current_epoch: Epoch,
|
||||
earliest_exit_epoch: Epoch,
|
||||
},
|
||||
/// The exit signature was not signed by the validator.
|
||||
BadSignature,
|
||||
}
|
||||
|
||||
@@ -8,60 +8,58 @@ use types::*;
|
||||
|
||||
/// Verify an `IndexedAttestation`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn verify_indexed_attestation<T: EthSpec>(
|
||||
/// Spec v0.8.0
|
||||
pub fn is_valid_indexed_attestation<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
indexed_attestation: &IndexedAttestation,
|
||||
indexed_attestation: &IndexedAttestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify_indexed_attestation_parametric(state, indexed_attestation, spec, true)
|
||||
is_valid_indexed_attestation_parametric(state, indexed_attestation, spec, true)
|
||||
}
|
||||
|
||||
/// Verify but don't check the signature.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn verify_indexed_attestation_without_signature<T: EthSpec>(
|
||||
/// Spec v0.8.0
|
||||
pub fn is_valid_indexed_attestation_without_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
indexed_attestation: &IndexedAttestation,
|
||||
indexed_attestation: &IndexedAttestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify_indexed_attestation_parametric(state, indexed_attestation, spec, false)
|
||||
is_valid_indexed_attestation_parametric(state, indexed_attestation, spec, false)
|
||||
}
|
||||
|
||||
/// Optionally check the signature.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn verify_indexed_attestation_parametric<T: EthSpec>(
|
||||
/// Spec v0.8.0
|
||||
fn is_valid_indexed_attestation_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
indexed_attestation: &IndexedAttestation,
|
||||
indexed_attestation: &IndexedAttestation<T>,
|
||||
spec: &ChainSpec,
|
||||
verify_signature: bool,
|
||||
) -> Result<(), Error> {
|
||||
let custody_bit_0_indices = &indexed_attestation.custody_bit_0_indices;
|
||||
let custody_bit_1_indices = &indexed_attestation.custody_bit_1_indices;
|
||||
let bit_0_indices = &indexed_attestation.custody_bit_0_indices;
|
||||
let bit_1_indices = &indexed_attestation.custody_bit_1_indices;
|
||||
|
||||
// Ensure no duplicate indices across custody bits
|
||||
// Verify no index has custody bit equal to 1 [to be removed in phase 1]
|
||||
verify!(bit_1_indices.is_empty(), Invalid::CustodyBitfieldHasSetBits);
|
||||
|
||||
// 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)
|
||||
);
|
||||
|
||||
// Verify index sets are disjoint
|
||||
let custody_bit_intersection: HashSet<&u64> =
|
||||
&HashSet::from_iter(custody_bit_0_indices) & &HashSet::from_iter(custody_bit_1_indices);
|
||||
&HashSet::from_iter(bit_0_indices.iter()) & &HashSet::from_iter(bit_1_indices.iter());
|
||||
verify!(
|
||||
custody_bit_intersection.is_empty(),
|
||||
Invalid::CustodyBitValidatorsIntersect
|
||||
);
|
||||
|
||||
// Check that nobody signed with custody bit 1 (to be removed in phase 1)
|
||||
if !custody_bit_1_indices.is_empty() {
|
||||
invalid!(Invalid::CustodyBitfieldHasSetBits);
|
||||
}
|
||||
|
||||
let total_indices = custody_bit_0_indices.len() + custody_bit_1_indices.len();
|
||||
verify!(1 <= total_indices, Invalid::NoValidatorIndices);
|
||||
verify!(
|
||||
total_indices as u64 <= spec.max_indices_per_attestation,
|
||||
Invalid::MaxIndicesExceed(spec.max_indices_per_attestation, total_indices)
|
||||
);
|
||||
|
||||
// Check that both vectors of indices are sorted
|
||||
let check_sorted = |list: &Vec<u64>| {
|
||||
let check_sorted = |list: &[u64]| -> Result<(), Error> {
|
||||
list.windows(2).enumerate().try_for_each(|(i, pair)| {
|
||||
if pair[0] >= pair[1] {
|
||||
invalid!(Invalid::BadValidatorIndicesOrdering(i));
|
||||
@@ -71,11 +69,11 @@ fn verify_indexed_attestation_parametric<T: EthSpec>(
|
||||
})?;
|
||||
Ok(())
|
||||
};
|
||||
check_sorted(custody_bit_0_indices)?;
|
||||
check_sorted(custody_bit_1_indices)?;
|
||||
check_sorted(&bit_0_indices)?;
|
||||
check_sorted(&bit_1_indices)?;
|
||||
|
||||
if verify_signature {
|
||||
verify_indexed_attestation_signature(state, indexed_attestation, spec)?;
|
||||
is_valid_indexed_attestation_signature(state, indexed_attestation, spec)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -94,7 +92,7 @@ where
|
||||
AggregatePublicKey::new(),
|
||||
|mut aggregate_pubkey, &validator_idx| {
|
||||
state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(validator_idx as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::UnknownValidator(validator_idx)))
|
||||
.map(|validator| {
|
||||
@@ -107,10 +105,10 @@ where
|
||||
|
||||
/// Verify the signature of an IndexedAttestation.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn verify_indexed_attestation_signature<T: EthSpec>(
|
||||
/// Spec v0.8.0
|
||||
fn is_valid_indexed_attestation_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
indexed_attestation: &IndexedAttestation,
|
||||
indexed_attestation: &IndexedAttestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let bit_0_pubkey = create_aggregate_pubkey(state, &indexed_attestation.custody_bit_0_indices)?;
|
||||
@@ -127,20 +125,11 @@ fn verify_indexed_attestation_signature<T: EthSpec>(
|
||||
}
|
||||
.tree_hash_root();
|
||||
|
||||
let mut messages = vec![];
|
||||
let mut keys = vec![];
|
||||
|
||||
if !indexed_attestation.custody_bit_0_indices.is_empty() {
|
||||
messages.push(&message_0[..]);
|
||||
keys.push(&bit_0_pubkey);
|
||||
}
|
||||
if !indexed_attestation.custody_bit_1_indices.is_empty() {
|
||||
messages.push(&message_1[..]);
|
||||
keys.push(&bit_1_pubkey);
|
||||
}
|
||||
let messages = vec![&message_0[..], &message_1[..]];
|
||||
let keys = vec![&bit_0_pubkey, &bit_1_pubkey];
|
||||
|
||||
let domain = spec.get_domain(
|
||||
indexed_attestation.data.target_epoch,
|
||||
indexed_attestation.data.target.epoch,
|
||||
Domain::Attestation,
|
||||
&state.fork,
|
||||
);
|
||||
@@ -51,7 +51,7 @@ fn invalid_parent_block_root() {
|
||||
Err(BlockProcessingError::Invalid(
|
||||
BlockInvalid::ParentBlockRootMismatch {
|
||||
state: Hash256::from_slice(&state.latest_block_header.signed_root()),
|
||||
block: block.previous_block_root
|
||||
block: block.parent_root
|
||||
}
|
||||
))
|
||||
);
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
use super::errors::{AttestationInvalid as Invalid, AttestationValidationError as Error};
|
||||
use crate::common::convert_to_indexed;
|
||||
use crate::per_block_processing::{
|
||||
verify_indexed_attestation, verify_indexed_attestation_without_signature,
|
||||
};
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state.
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn validate_attestation<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
validate_attestation_parametric(state, attestation, spec, true, false)
|
||||
}
|
||||
|
||||
/// Like `validate_attestation` but doesn't run checks which may become true in future states.
|
||||
pub fn validate_attestation_time_independent_only<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
validate_attestation_parametric(state, attestation, spec, true, true)
|
||||
}
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state, without validating the aggregate signature.
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn validate_attestation_without_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
validate_attestation_parametric(state, attestation, spec, false, false)
|
||||
}
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state, optionally validating the aggregate signature.
|
||||
///
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn validate_attestation_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation,
|
||||
spec: &ChainSpec,
|
||||
verify_signature: bool,
|
||||
time_independent_only: bool,
|
||||
) -> Result<(), Error> {
|
||||
let attestation_slot = state.get_attestation_slot(&attestation.data)?;
|
||||
|
||||
// Check attestation slot.
|
||||
verify!(
|
||||
time_independent_only
|
||||
|| attestation_slot + spec.min_attestation_inclusion_delay <= state.slot,
|
||||
Invalid::IncludedTooEarly {
|
||||
state: state.slot,
|
||||
delay: spec.min_attestation_inclusion_delay,
|
||||
attestation: attestation_slot
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
state.slot <= attestation_slot + T::slots_per_epoch(),
|
||||
Invalid::IncludedTooLate {
|
||||
state: state.slot,
|
||||
attestation: attestation_slot
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the Casper FFG vote.
|
||||
if !time_independent_only {
|
||||
verify_casper_ffg_vote(attestation, state)?;
|
||||
}
|
||||
|
||||
// Crosslink data root is zero (to be removed in phase 1).
|
||||
verify!(
|
||||
attestation.data.crosslink_data_root == spec.zero_hash,
|
||||
Invalid::ShardBlockRootNotZero
|
||||
);
|
||||
|
||||
// Check signature and bitfields
|
||||
let indexed_attestation = convert_to_indexed(state, attestation)?;
|
||||
if verify_signature {
|
||||
verify_indexed_attestation(state, &indexed_attestation, spec)?;
|
||||
} else {
|
||||
verify_indexed_attestation_without_signature(state, &indexed_attestation, spec)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check target epoch, source epoch, source root, and source crosslink.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn verify_casper_ffg_vote<T: EthSpec>(
|
||||
attestation: &Attestation,
|
||||
state: &BeaconState<T>,
|
||||
) -> Result<(), Error> {
|
||||
let data = &attestation.data;
|
||||
if data.target_epoch == state.current_epoch() {
|
||||
verify!(
|
||||
data.source_epoch == state.current_justified_epoch,
|
||||
Invalid::WrongJustifiedEpoch {
|
||||
state: state.current_justified_epoch,
|
||||
attestation: data.source_epoch,
|
||||
is_current: true,
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
data.source_root == state.current_justified_root,
|
||||
Invalid::WrongJustifiedRoot {
|
||||
state: state.current_justified_root,
|
||||
attestation: data.source_root,
|
||||
is_current: true,
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
data.previous_crosslink_root
|
||||
== Hash256::from_slice(&state.get_current_crosslink(data.shard)?.tree_hash_root()),
|
||||
Invalid::BadPreviousCrosslink
|
||||
);
|
||||
} else if data.target_epoch == state.previous_epoch() {
|
||||
verify!(
|
||||
data.source_epoch == state.previous_justified_epoch,
|
||||
Invalid::WrongJustifiedEpoch {
|
||||
state: state.previous_justified_epoch,
|
||||
attestation: data.source_epoch,
|
||||
is_current: false,
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
data.source_root == state.previous_justified_root,
|
||||
Invalid::WrongJustifiedRoot {
|
||||
state: state.previous_justified_root,
|
||||
attestation: data.source_root,
|
||||
is_current: false,
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
data.previous_crosslink_root
|
||||
== Hash256::from_slice(&state.get_previous_crosslink(data.shard)?.tree_hash_root()),
|
||||
Invalid::BadPreviousCrosslink
|
||||
);
|
||||
} else {
|
||||
invalid!(Invalid::BadTargetEpoch)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
use super::errors::{AttestationInvalid as Invalid, AttestationValidationError as Error};
|
||||
use crate::common::get_indexed_attestation;
|
||||
use crate::per_block_processing::{
|
||||
is_valid_indexed_attestation, is_valid_indexed_attestation_without_signature,
|
||||
};
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state.
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_attestation<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify_attestation_parametric(state, attestation, spec, true, false)
|
||||
}
|
||||
|
||||
/// Like `verify_attestation` but doesn't run checks which may become true in future states.
|
||||
pub fn verify_attestation_time_independent_only<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify_attestation_parametric(state, attestation, spec, true, true)
|
||||
}
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state, without validating the aggregate signature.
|
||||
///
|
||||
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_attestation_without_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
verify_attestation_parametric(state, attestation, spec, false, false)
|
||||
}
|
||||
|
||||
/// Indicates if an `Attestation` is valid to be included in a block in the current epoch of the
|
||||
/// given state, optionally validating the aggregate signature.
|
||||
///
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
fn verify_attestation_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
spec: &ChainSpec,
|
||||
verify_signature: bool,
|
||||
time_independent_only: bool,
|
||||
) -> Result<(), Error> {
|
||||
let data = &attestation.data;
|
||||
verify!(
|
||||
data.crosslink.shard < T::ShardCount::to_u64(),
|
||||
Invalid::BadShard
|
||||
);
|
||||
|
||||
// Check attestation slot.
|
||||
let attestation_slot = state.get_attestation_data_slot(&data)?;
|
||||
|
||||
verify!(
|
||||
time_independent_only
|
||||
|| attestation_slot + spec.min_attestation_inclusion_delay <= state.slot,
|
||||
Invalid::IncludedTooEarly {
|
||||
state: state.slot,
|
||||
delay: spec.min_attestation_inclusion_delay,
|
||||
attestation: attestation_slot
|
||||
}
|
||||
);
|
||||
verify!(
|
||||
state.slot <= attestation_slot + T::slots_per_epoch(),
|
||||
Invalid::IncludedTooLate {
|
||||
state: state.slot,
|
||||
attestation: attestation_slot
|
||||
}
|
||||
);
|
||||
|
||||
// Verify the Casper FFG vote and crosslink data.
|
||||
if !time_independent_only {
|
||||
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
|
||||
);
|
||||
|
||||
// Check signature and bitfields
|
||||
let indexed_attestation = get_indexed_attestation(state, attestation)?;
|
||||
if verify_signature {
|
||||
is_valid_indexed_attestation(state, &indexed_attestation, spec)?;
|
||||
} else {
|
||||
is_valid_indexed_attestation_without_signature(state, &indexed_attestation, spec)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
attestation: &Attestation<T>,
|
||||
state: &'a BeaconState<T>,
|
||||
) -> Result<&'a Crosslink, Error> {
|
||||
let data = &attestation.data;
|
||||
if data.target.epoch == state.current_epoch() {
|
||||
verify!(
|
||||
data.source == state.current_justified_checkpoint,
|
||||
Invalid::WrongJustifiedCheckpoint {
|
||||
state: state.current_justified_checkpoint.clone(),
|
||||
attestation: data.source.clone(),
|
||||
is_current: true,
|
||||
}
|
||||
);
|
||||
Ok(state.get_current_crosslink(data.crosslink.shard)?)
|
||||
} else if data.target.epoch == state.previous_epoch() {
|
||||
verify!(
|
||||
data.source == state.previous_justified_checkpoint,
|
||||
Invalid::WrongJustifiedCheckpoint {
|
||||
state: state.previous_justified_checkpoint.clone(),
|
||||
attestation: data.source.clone(),
|
||||
is_current: false,
|
||||
}
|
||||
);
|
||||
Ok(state.get_previous_crosslink(data.crosslink.shard)?)
|
||||
} else {
|
||||
invalid!(Invalid::BadTargetEpoch)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::errors::{AttesterSlashingInvalid as Invalid, AttesterSlashingValidationError as Error};
|
||||
use super::verify_indexed_attestation::verify_indexed_attestation;
|
||||
use super::is_valid_indexed_attestation::is_valid_indexed_attestation;
|
||||
use std::collections::BTreeSet;
|
||||
use types::*;
|
||||
|
||||
@@ -8,10 +8,10 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `AttesterSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn verify_attester_slashing<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attester_slashing: &AttesterSlashing,
|
||||
attester_slashing: &AttesterSlashing<T>,
|
||||
should_verify_indexed_attestations: bool,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
@@ -26,9 +26,9 @@ pub fn verify_attester_slashing<T: EthSpec>(
|
||||
);
|
||||
|
||||
if should_verify_indexed_attestations {
|
||||
verify_indexed_attestation(state, &attestation_1, spec)
|
||||
is_valid_indexed_attestation(state, &attestation_1, spec)
|
||||
.map_err(|e| Error::Invalid(Invalid::IndexedAttestation1Invalid(e.into())))?;
|
||||
verify_indexed_attestation(state, &attestation_2, spec)
|
||||
is_valid_indexed_attestation(state, &attestation_2, spec)
|
||||
.map_err(|e| Error::Invalid(Invalid::IndexedAttestation2Invalid(e.into())))?;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ pub fn verify_attester_slashing<T: EthSpec>(
|
||||
///
|
||||
/// Returns Ok(indices) if `indices.len() > 0`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_slashable_indices<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attester_slashing: &AttesterSlashing,
|
||||
attester_slashing: &AttesterSlashing<T>,
|
||||
) -> Result<Vec<u64>, Error> {
|
||||
get_slashable_indices_modular(state, attester_slashing, |_, validator| {
|
||||
validator.is_slashable_at(state.current_epoch())
|
||||
@@ -53,7 +53,7 @@ pub fn get_slashable_indices<T: EthSpec>(
|
||||
/// for determining whether a given validator should be considered slashable.
|
||||
pub fn get_slashable_indices_modular<F, T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attester_slashing: &AttesterSlashing,
|
||||
attester_slashing: &AttesterSlashing<T>,
|
||||
is_slashable: F,
|
||||
) -> Result<Vec<u64>, Error>
|
||||
where
|
||||
@@ -79,7 +79,7 @@ where
|
||||
|
||||
for index in &attesting_indices_1 & &attesting_indices_2 {
|
||||
let validator = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(index as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::UnknownValidator(index)))?;
|
||||
|
||||
|
||||
@@ -5,43 +5,27 @@ use types::*;
|
||||
|
||||
/// Verify `Deposit.pubkey` signed `Deposit.signature`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_deposit_signature<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Note: Deposits are valid across forks, thus the deposit domain is computed
|
||||
// with the fork zeroed.
|
||||
let domain = spec.get_domain(state.current_epoch(), Domain::Deposit, &Fork::default());
|
||||
verify!(
|
||||
deposit.data.signature.verify(
|
||||
&deposit.data.signed_root(),
|
||||
spec.get_domain(state.current_epoch(), Domain::Deposit, &state.fork),
|
||||
&deposit.data.pubkey,
|
||||
),
|
||||
deposit
|
||||
.data
|
||||
.signature
|
||||
.verify(&deposit.data.signed_root(), domain, &deposit.data.pubkey,),
|
||||
Invalid::BadSignature
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify that the `Deposit` index is correct.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn verify_deposit_index<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
) -> Result<(), Error> {
|
||||
verify!(
|
||||
deposit.index == state.deposit_index,
|
||||
Invalid::BadIndex {
|
||||
state: state.deposit_index,
|
||||
deposit: deposit.index
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a `Some(validator index)` if a pubkey already exists in the `validator_registry`,
|
||||
/// Returns a `Some(validator index)` if a pubkey already exists in the `validators`,
|
||||
/// otherwise returns `None`.
|
||||
///
|
||||
/// ## Errors
|
||||
@@ -57,10 +41,14 @@ pub fn get_existing_validator_index<T: EthSpec>(
|
||||
|
||||
/// Verify that a deposit is included in the state's eth1 deposit root.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// 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
|
||||
pub fn verify_deposit_merkle_proof<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
deposit: &Deposit,
|
||||
deposit_index: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let leaf = deposit.data.tree_hash_root();
|
||||
@@ -69,9 +57,9 @@ pub fn verify_deposit_merkle_proof<T: EthSpec>(
|
||||
verify_merkle_proof(
|
||||
Hash256::from_slice(&leaf),
|
||||
&deposit.proof[..],
|
||||
spec.deposit_contract_tree_depth as usize,
|
||||
deposit.index as usize,
|
||||
state.latest_eth1_data.deposit_root,
|
||||
spec.deposit_contract_tree_depth as usize + 1,
|
||||
deposit_index as usize,
|
||||
state.eth1_data.deposit_root,
|
||||
),
|
||||
Invalid::BadMerkleProof
|
||||
);
|
||||
|
||||
@@ -7,7 +7,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `Exit` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_exit<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
exit: &VoluntaryExit,
|
||||
@@ -18,7 +18,7 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
/// Like `verify_exit` but doesn't run checks which may become true in future states.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_exit_time_independent_only<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
exit: &VoluntaryExit,
|
||||
@@ -29,7 +29,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.6.3
|
||||
/// Spec v0.8.0
|
||||
fn verify_exit_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
exit: &VoluntaryExit,
|
||||
@@ -37,7 +37,7 @@ fn verify_exit_parametric<T: EthSpec>(
|
||||
time_independent_only: bool,
|
||||
) -> Result<(), Error> {
|
||||
let validator = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(exit.validator_index as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::ValidatorUnknown(exit.validator_index)))?;
|
||||
|
||||
@@ -63,12 +63,11 @@ fn verify_exit_parametric<T: EthSpec>(
|
||||
);
|
||||
|
||||
// Verify the validator has been active long enough.
|
||||
let lifespan = state.current_epoch() - validator.activation_epoch;
|
||||
verify!(
|
||||
lifespan >= spec.persistent_committee_period,
|
||||
Invalid::TooYoungToLeave {
|
||||
lifespan,
|
||||
expected: spec.persistent_committee_period,
|
||||
state.current_epoch() >= validator.activation_epoch + spec.persistent_committee_period,
|
||||
Invalid::TooYoungToExit {
|
||||
current_epoch: state.current_epoch(),
|
||||
earliest_exit_epoch: validator.activation_epoch + spec.persistent_committee_period,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -7,19 +7,20 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `ProposerSlashing` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_proposer_slashing<T: EthSpec>(
|
||||
proposer_slashing: &ProposerSlashing,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let proposer = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(proposer_slashing.proposer_index as usize)
|
||||
.ok_or_else(|| {
|
||||
Error::Invalid(Invalid::ProposerUnknown(proposer_slashing.proposer_index))
|
||||
})?;
|
||||
|
||||
// Verify that the epoch is the same
|
||||
verify!(
|
||||
proposer_slashing.header_1.slot.epoch(T::slots_per_epoch())
|
||||
== proposer_slashing.header_2.slot.epoch(T::slots_per_epoch()),
|
||||
@@ -29,11 +30,13 @@ pub fn verify_proposer_slashing<T: EthSpec>(
|
||||
)
|
||||
);
|
||||
|
||||
// But the headers are different
|
||||
verify!(
|
||||
proposer_slashing.header_1 != proposer_slashing.header_2,
|
||||
Invalid::ProposalsIdentical
|
||||
);
|
||||
|
||||
// Check proposer is slashable
|
||||
verify!(
|
||||
proposer.is_slashable_at(state.current_epoch()),
|
||||
Invalid::ProposerNotSlashable(proposer_slashing.proposer_index)
|
||||
@@ -65,7 +68,7 @@ pub fn verify_proposer_slashing<T: EthSpec>(
|
||||
///
|
||||
/// Returns `true` if the signature is valid.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn verify_header_signature<T: EthSpec>(
|
||||
header: &BeaconBlockHeader,
|
||||
pubkey: &PublicKey,
|
||||
|
||||
@@ -8,7 +8,7 @@ use types::*;
|
||||
///
|
||||
/// Returns `Ok(())` if the `Transfer` is valid, otherwise indicates the reason for invalidity.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_transfer<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
transfer: &Transfer,
|
||||
@@ -19,7 +19,7 @@ pub fn verify_transfer<T: EthSpec>(
|
||||
|
||||
/// Like `verify_transfer` but doesn't run checks which may become true in future states.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn verify_transfer_time_independent_only<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
transfer: &Transfer,
|
||||
@@ -37,7 +37,7 @@ pub fn verify_transfer_time_independent_only<T: EthSpec>(
|
||||
/// present or future.
|
||||
/// - Validator transfer eligibility (e.g., is withdrawable)
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn verify_transfer_parametric<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
transfer: &Transfer,
|
||||
@@ -97,22 +97,20 @@ fn verify_transfer_parametric<T: EthSpec>(
|
||||
|
||||
// Load the sender `Validator` record from the state.
|
||||
let sender_validator = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(transfer.sender as usize)
|
||||
.ok_or_else(|| Error::Invalid(Invalid::FromValidatorUnknown(transfer.sender)))?;
|
||||
|
||||
let epoch = state.slot.epoch(T::slots_per_epoch());
|
||||
|
||||
// Ensure one of the following is met:
|
||||
//
|
||||
// - Time dependent checks are being ignored.
|
||||
// - The sender has not been activated.
|
||||
// - 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(epoch)
|
||||
|| sender_validator.is_withdrawable_at(state.current_epoch())
|
||||
|| total_amount + spec.max_effective_balance <= sender_balance,
|
||||
Invalid::FromValidatorIneligibleForTransfer(transfer.sender)
|
||||
);
|
||||
@@ -154,7 +152,7 @@ fn verify_transfer_parametric<T: EthSpec>(
|
||||
///
|
||||
/// Does not check that the transfer is valid, however checks for overflow in all actions.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn execute_transfer<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
transfer: &Transfer,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::common::get_compact_committees_root;
|
||||
use apply_rewards::process_rewards_and_penalties;
|
||||
use errors::EpochProcessingError as Error;
|
||||
use process_slashings::process_slashings;
|
||||
@@ -26,14 +27,15 @@ pub type WinningRootHashSet = HashMap<u64, WinningRoot>;
|
||||
/// 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn per_epoch_processing<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Ensure the previous and next epoch caches are built.
|
||||
// Ensure the committee caches are built.
|
||||
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
|
||||
state.build_committee_cache(RelativeEpoch::Current, spec)?;
|
||||
state.build_committee_cache(RelativeEpoch::Next, spec)?;
|
||||
|
||||
// Load the struct we use to assign validators into sets based on their participation.
|
||||
//
|
||||
@@ -80,61 +82,67 @@ pub fn per_epoch_processing<T: EthSpec>(
|
||||
/// - `finalized_epoch`
|
||||
/// - `finalized_root`
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.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>,
|
||||
total_balances: &TotalBalances,
|
||||
) -> Result<(), Error> {
|
||||
if state.current_epoch() == T::genesis_epoch() {
|
||||
if state.current_epoch() <= T::genesis_epoch() + 1 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
|
||||
let old_previous_justified_epoch = state.previous_justified_epoch;
|
||||
let old_current_justified_epoch = state.current_justified_epoch;
|
||||
let old_previous_justified_checkpoint = state.previous_justified_checkpoint.clone();
|
||||
let old_current_justified_checkpoint = state.current_justified_checkpoint.clone();
|
||||
|
||||
// Process justifications
|
||||
state.previous_justified_epoch = state.current_justified_epoch;
|
||||
state.previous_justified_root = state.current_justified_root;
|
||||
state.justification_bitfield <<= 1;
|
||||
state.previous_justified_checkpoint = state.current_justified_checkpoint.clone();
|
||||
state.justification_bits.shift_up(1)?;
|
||||
|
||||
if total_balances.previous_epoch_target_attesters * 3 >= total_balances.previous_epoch * 2 {
|
||||
state.current_justified_epoch = previous_epoch;
|
||||
state.current_justified_root =
|
||||
*state.get_block_root_at_epoch(state.current_justified_epoch)?;
|
||||
state.justification_bitfield |= 2;
|
||||
if total_balances.previous_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 {
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: previous_epoch,
|
||||
root: *state.get_block_root_at_epoch(previous_epoch)?,
|
||||
};
|
||||
state.justification_bits.set(1, true)?;
|
||||
}
|
||||
// If the current epoch gets justified, fill the last bit.
|
||||
if total_balances.current_epoch_target_attesters * 3 >= total_balances.current_epoch * 2 {
|
||||
state.current_justified_epoch = current_epoch;
|
||||
state.current_justified_root =
|
||||
*state.get_block_root_at_epoch(state.current_justified_epoch)?;
|
||||
state.justification_bitfield |= 1;
|
||||
state.current_justified_checkpoint = Checkpoint {
|
||||
epoch: current_epoch,
|
||||
root: *state.get_block_root_at_epoch(current_epoch)?,
|
||||
};
|
||||
state.justification_bits.set(0, true)?;
|
||||
}
|
||||
|
||||
let bitfield = state.justification_bitfield;
|
||||
let bits = &state.justification_bits;
|
||||
|
||||
// The 2nd/3rd/4th most recent epochs are all justified, the 2nd using the 4th as source.
|
||||
if (bitfield >> 1) % 8 == 0b111 && old_previous_justified_epoch == current_epoch - 3 {
|
||||
state.finalized_epoch = old_previous_justified_epoch;
|
||||
state.finalized_root = *state.get_block_root_at_epoch(state.finalized_epoch)?;
|
||||
if (1..4).all(|i| bits.get(i).unwrap_or(false))
|
||||
&& old_previous_justified_checkpoint.epoch + 3 == current_epoch
|
||||
{
|
||||
state.finalized_checkpoint = old_previous_justified_checkpoint;
|
||||
}
|
||||
// The 2nd/3rd most recent epochs are both justified, the 2nd using the 3rd as source.
|
||||
if (bitfield >> 1) % 4 == 0b11 && old_previous_justified_epoch == current_epoch - 2 {
|
||||
state.finalized_epoch = old_previous_justified_epoch;
|
||||
state.finalized_root = *state.get_block_root_at_epoch(state.finalized_epoch)?;
|
||||
else if (1..3).all(|i| bits.get(i).unwrap_or(false))
|
||||
&& old_previous_justified_checkpoint.epoch + 2 == current_epoch
|
||||
{
|
||||
state.finalized_checkpoint = old_previous_justified_checkpoint;
|
||||
}
|
||||
// The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 2nd as source.
|
||||
if bitfield % 8 == 0b111 && old_current_justified_epoch == current_epoch - 2 {
|
||||
state.finalized_epoch = old_current_justified_epoch;
|
||||
state.finalized_root = *state.get_block_root_at_epoch(state.finalized_epoch)?;
|
||||
// The 1st/2nd/3rd most recent epochs are all justified, the 1st using the 3nd as source.
|
||||
if (0..3).all(|i| bits.get(i).unwrap_or(false))
|
||||
&& old_current_justified_checkpoint.epoch + 2 == current_epoch
|
||||
{
|
||||
state.finalized_checkpoint = old_current_justified_checkpoint;
|
||||
}
|
||||
// The 1st/2nd most recent epochs are both justified, the 1st using the 2nd as source.
|
||||
if bitfield % 4 == 0b11 && old_current_justified_epoch == current_epoch - 1 {
|
||||
state.finalized_epoch = old_current_justified_epoch;
|
||||
state.finalized_root = *state.get_block_root_at_epoch(state.finalized_epoch)?;
|
||||
else if (0..2).all(|i| bits.get(i).unwrap_or(false))
|
||||
&& old_current_justified_checkpoint.epoch + 1 == current_epoch
|
||||
{
|
||||
state.finalized_checkpoint = old_current_justified_checkpoint;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -147,7 +155,7 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
///
|
||||
/// Also returns a `WinningRootHashSet` for later use during epoch processing.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_crosslinks<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@@ -158,7 +166,7 @@ pub fn process_crosslinks<T: EthSpec>(
|
||||
|
||||
for &relative_epoch in &[RelativeEpoch::Previous, RelativeEpoch::Current] {
|
||||
let epoch = relative_epoch.into_epoch(state.current_epoch());
|
||||
for offset in 0..state.get_epoch_committee_count(relative_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 =
|
||||
@@ -183,7 +191,7 @@ pub fn process_crosslinks<T: EthSpec>(
|
||||
|
||||
/// Finish up an epoch update.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_final_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@@ -192,12 +200,12 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
let next_epoch = state.next_epoch();
|
||||
|
||||
// Reset eth1 data votes.
|
||||
if (state.slot + 1) % spec.slots_per_eth1_voting_period == 0 {
|
||||
state.eth1_data_votes = vec![];
|
||||
if (state.slot + 1) % T::SlotsPerEth1VotingPeriod::to_u64() == 0 {
|
||||
state.eth1_data_votes = VariableList::empty();
|
||||
}
|
||||
|
||||
// Update effective balances with hysteresis (lag).
|
||||
for (index, validator) in state.validator_registry.iter_mut().enumerate() {
|
||||
for (index, validator) in state.validators.iter_mut().enumerate() {
|
||||
let balance = state.balances[index];
|
||||
let half_increment = spec.effective_balance_increment / 2;
|
||||
if balance < validator.effective_balance
|
||||
@@ -211,7 +219,7 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
}
|
||||
|
||||
// Update start shard.
|
||||
state.latest_start_shard = state.next_epoch_start_shard(spec)?;
|
||||
state.start_shard = state.next_epoch_start_shard(spec)?;
|
||||
|
||||
// This is a hack to allow us to update index roots and slashed balances for the next epoch.
|
||||
//
|
||||
@@ -220,19 +228,18 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
state.slot += 1;
|
||||
|
||||
// Set active index root
|
||||
let active_index_root = Hash256::from_slice(
|
||||
&state
|
||||
.get_active_validator_indices(next_epoch + spec.activation_exit_delay)
|
||||
.tree_hash_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(
|
||||
next_epoch + spec.activation_exit_delay,
|
||||
active_index_root,
|
||||
index_epoch,
|
||||
Hash256::from_slice(&indices_list.tree_hash_root()),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
// Set total slashed balances
|
||||
state.set_slashed_balance(next_epoch, state.get_slashed_balance(current_epoch)?)?;
|
||||
// Reset slashings
|
||||
state.set_slashings(next_epoch, 0)?;
|
||||
|
||||
// Set randao mix
|
||||
state.set_randao_mix(next_epoch, *state.get_randao_mix(current_epoch)?)?;
|
||||
@@ -240,16 +247,27 @@ pub fn process_final_updates<T: EthSpec>(
|
||||
state.slot -= 1;
|
||||
}
|
||||
|
||||
// Set committees root
|
||||
// Note: we do this out-of-order w.r.t. to the spec, because we don't want the slot to be
|
||||
// incremented. It's safe because the updates to slashings and the RANDAO mix (above) don't
|
||||
// affect this.
|
||||
state.set_compact_committee_root(
|
||||
next_epoch,
|
||||
get_compact_committees_root(state, RelativeEpoch::Next, spec)?,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
// Set historical root accumulator
|
||||
if next_epoch.as_u64() % (T::SlotsPerHistoricalRoot::to_u64() / T::slots_per_epoch()) == 0 {
|
||||
let historical_batch = state.historical_batch();
|
||||
state
|
||||
.historical_roots
|
||||
.push(Hash256::from_slice(&historical_batch.tree_hash_root()[..]));
|
||||
.push(Hash256::from_slice(&historical_batch.tree_hash_root()))?;
|
||||
}
|
||||
|
||||
// Rotate current/previous epoch attestations
|
||||
state.previous_epoch_attestations =
|
||||
std::mem::replace(&mut state.current_epoch_attestations, vec![]);
|
||||
std::mem::replace(&mut state.current_epoch_attestations, VariableList::empty());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl std::ops::AddAssign for Delta {
|
||||
|
||||
/// Apply attester and proposer rewards.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
validator_statuses: &mut ValidatorStatuses,
|
||||
@@ -45,7 +45,7 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
|
||||
// Guard against an out-of-bounds during the validator balance update.
|
||||
if validator_statuses.statuses.len() != state.balances.len()
|
||||
|| validator_statuses.statuses.len() != state.validator_registry.len()
|
||||
|| validator_statuses.statuses.len() != state.validators.len()
|
||||
{
|
||||
return Err(Error::ValidatorStatusesInconsistent);
|
||||
}
|
||||
@@ -74,7 +74,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.6.3
|
||||
/// Spec v0.8.0
|
||||
fn get_proposer_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
@@ -85,7 +85,7 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
// Update statuses with the information from winning roots.
|
||||
validator_statuses.process_winning_roots(state, winning_root_for_shards, spec)?;
|
||||
|
||||
for validator in &validator_statuses.statuses {
|
||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
||||
if validator.is_previous_epoch_attester {
|
||||
let inclusion = validator
|
||||
.inclusion_info
|
||||
@@ -93,7 +93,7 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
|
||||
let base_reward = get_base_reward(
|
||||
state,
|
||||
inclusion.proposer_index,
|
||||
index,
|
||||
validator_statuses.total_balances.current_epoch,
|
||||
spec,
|
||||
)?;
|
||||
@@ -111,14 +111,14 @@ fn get_proposer_deltas<T: EthSpec>(
|
||||
|
||||
/// Apply rewards for participation in attestations during the previous epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn get_attestation_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
validator_statuses: &ValidatorStatuses,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let finality_delay = (state.previous_epoch() - state.finalized_epoch).as_u64();
|
||||
let finality_delay = (state.previous_epoch() - state.finalized_checkpoint.epoch).as_u64();
|
||||
|
||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
||||
let base_reward = get_base_reward(
|
||||
@@ -128,7 +128,7 @@ fn get_attestation_deltas<T: EthSpec>(
|
||||
spec,
|
||||
)?;
|
||||
|
||||
let delta = get_attestation_delta(
|
||||
let delta = get_attestation_delta::<T>(
|
||||
&validator,
|
||||
&validator_statuses.total_balances,
|
||||
base_reward,
|
||||
@@ -144,8 +144,8 @@ fn get_attestation_deltas<T: EthSpec>(
|
||||
|
||||
/// Determine the delta for a single validator, sans proposer rewards.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn get_attestation_delta(
|
||||
/// Spec v0.8.0
|
||||
fn get_attestation_delta<T: EthSpec>(
|
||||
validator: &ValidatorStatus,
|
||||
total_balances: &TotalBalances,
|
||||
base_reward: u64,
|
||||
@@ -174,10 +174,17 @@ fn get_attestation_delta(
|
||||
if validator.is_previous_epoch_attester && !validator.is_slashed {
|
||||
delta.reward(base_reward * total_attesting_balance / total_balance);
|
||||
// Inclusion speed bonus
|
||||
let proposer_reward = base_reward / spec.proposer_reward_quotient;
|
||||
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(base_reward * spec.min_attestation_inclusion_delay / inclusion.distance);
|
||||
delta.reward(
|
||||
max_attester_reward
|
||||
* (T::SlotsPerEpoch::to_u64() + spec.min_attestation_inclusion_delay
|
||||
- inclusion.distance)
|
||||
/ T::SlotsPerEpoch::to_u64(),
|
||||
);
|
||||
} else {
|
||||
delta.penalize(base_reward);
|
||||
}
|
||||
@@ -224,7 +231,7 @@ fn get_attestation_delta(
|
||||
|
||||
/// Calculate the deltas based upon the winning roots for attestations during the previous epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn get_crosslink_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
@@ -258,7 +265,7 @@ fn get_crosslink_deltas<T: EthSpec>(
|
||||
|
||||
/// Returns the base reward for some validator.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn get_base_reward<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
index: usize,
|
||||
@@ -269,9 +276,10 @@ fn get_base_reward<T: EthSpec>(
|
||||
if total_active_balance == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let adjusted_quotient = total_active_balance.integer_sqrt() / spec.base_reward_quotient;
|
||||
Ok(state.get_effective_balance(index, spec)?
|
||||
/ adjusted_quotient
|
||||
/ spec.base_rewards_per_epoch)
|
||||
Ok(
|
||||
state.get_effective_balance(index, spec)? * spec.base_reward_factor
|
||||
/ total_active_balance.integer_sqrt()
|
||||
/ spec.base_rewards_per_epoch,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub enum EpochProcessingError {
|
||||
InclusionSlotsInconsistent(usize),
|
||||
BeaconStateError(BeaconStateError),
|
||||
InclusionError(InclusionError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
}
|
||||
|
||||
impl From<InclusionError> for EpochProcessingError {
|
||||
@@ -31,6 +32,12 @@ impl From<BeaconStateError> for EpochProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for EpochProcessingError {
|
||||
fn from(e: ssz_types::Error) -> EpochProcessingError {
|
||||
EpochProcessingError::SszTypesError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InclusionError {
|
||||
/// The validator did not participate in an attestation in this period.
|
||||
|
||||
@@ -2,30 +2,23 @@ use types::{BeaconStateError as Error, *};
|
||||
|
||||
/// Process slashings.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
current_total_balance: u64,
|
||||
total_balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let current_epoch = state.current_epoch();
|
||||
let epoch = state.current_epoch();
|
||||
let sum_slashings = state.get_all_slashings().iter().sum::<u64>();
|
||||
|
||||
let total_at_start = state.get_slashed_balance(current_epoch + 1)?;
|
||||
let total_at_end = state.get_slashed_balance(current_epoch)?;
|
||||
let total_penalties = total_at_end - total_at_start;
|
||||
|
||||
for (index, validator) in state.validator_registry.iter().enumerate() {
|
||||
let should_penalize = current_epoch.as_usize() + T::LatestSlashedExitLength::to_usize() / 2
|
||||
== validator.withdrawable_epoch.as_usize();
|
||||
|
||||
if validator.slashed && should_penalize {
|
||||
let effective_balance = state.get_effective_balance(index, spec)?;
|
||||
|
||||
let penalty = std::cmp::max(
|
||||
effective_balance * std::cmp::min(total_penalties * 3, current_total_balance)
|
||||
/ current_total_balance,
|
||||
effective_balance / spec.min_slashing_penalty_quotient,
|
||||
);
|
||||
for (index, validator) in state.validators.iter().enumerate() {
|
||||
if validator.slashed
|
||||
&& epoch + T::EpochsPerSlashingsVector::to_u64() / 2 == validator.withdrawable_epoch
|
||||
{
|
||||
let increment = spec.effective_balance_increment;
|
||||
let penalty_numerator = validator.effective_balance / increment
|
||||
* std::cmp::min(sum_slashings * 3, total_balance);
|
||||
let penalty = penalty_numerator / total_balance * increment;
|
||||
|
||||
safe_sub_assign!(state.balances[index], penalty);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use types::*;
|
||||
|
||||
/// Performs a validator registry update, if required.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn process_registry_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
@@ -17,14 +17,14 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
let current_epoch = state.current_epoch();
|
||||
let is_eligible = |validator: &Validator| {
|
||||
validator.activation_eligibility_epoch == spec.far_future_epoch
|
||||
&& validator.effective_balance >= spec.max_effective_balance
|
||||
&& validator.effective_balance == spec.max_effective_balance
|
||||
};
|
||||
let is_exiting_validator = |validator: &Validator| {
|
||||
validator.is_active_at(current_epoch)
|
||||
&& validator.effective_balance <= spec.ejection_balance
|
||||
};
|
||||
let (eligible_validators, exiting_validators): (Vec<_>, Vec<_>) = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, validator)| is_eligible(validator) || is_exiting_validator(validator))
|
||||
@@ -36,7 +36,7 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
}
|
||||
});
|
||||
for index in eligible_validators {
|
||||
state.validator_registry[index].activation_eligibility_epoch = current_epoch;
|
||||
state.validators[index].activation_eligibility_epoch = current_epoch;
|
||||
}
|
||||
for index in exiting_validators {
|
||||
initiate_validator_exit(state, index, spec)?;
|
||||
@@ -44,22 +44,22 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
|
||||
// Queue validators eligible for activation and not dequeued for activation prior to finalized epoch
|
||||
let activation_queue = state
|
||||
.validator_registry
|
||||
.validators
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, validator)| {
|
||||
validator.activation_eligibility_epoch != spec.far_future_epoch
|
||||
&& validator.activation_epoch
|
||||
>= state.get_delayed_activation_exit_epoch(state.finalized_epoch, spec)
|
||||
>= state.compute_activation_exit_epoch(state.finalized_checkpoint.epoch, spec)
|
||||
})
|
||||
.sorted_by_key(|(_, validator)| validator.activation_eligibility_epoch)
|
||||
.map(|(index, _)| index)
|
||||
.collect_vec();
|
||||
|
||||
let churn_limit = state.get_churn_limit(spec)? as usize;
|
||||
let delayed_activation_epoch = state.get_delayed_activation_exit_epoch(current_epoch, spec);
|
||||
let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec);
|
||||
for index in activation_queue.into_iter().take(churn_limit) {
|
||||
let validator = &mut state.validator_registry[index];
|
||||
let validator = &mut state.validators[index];
|
||||
if validator.activation_epoch == spec.far_future_epoch {
|
||||
validator.activation_epoch = delayed_activation_epoch;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::WinningRootHashSet;
|
||||
use crate::common::get_attesting_indices_unsorted;
|
||||
use crate::common::get_attesting_indices;
|
||||
use types::*;
|
||||
|
||||
/// Sets the boolean `var` on `self` to be true if it is true on `other`. Otherwise leaves `self`
|
||||
@@ -162,15 +162,15 @@ impl ValidatorStatuses {
|
||||
/// - Active validators
|
||||
/// - Total balances for the current and previous epochs.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn new<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, BeaconStateError> {
|
||||
let mut statuses = Vec::with_capacity(state.validator_registry.len());
|
||||
let mut statuses = Vec::with_capacity(state.validators.len());
|
||||
let mut total_balances = TotalBalances::default();
|
||||
|
||||
for (i, validator) in state.validator_registry.iter().enumerate() {
|
||||
for (i, validator) in state.validators.iter().enumerate() {
|
||||
let effective_balance = state.get_effective_balance(i, spec)?;
|
||||
let mut status = ValidatorStatus {
|
||||
is_slashed: validator.slashed,
|
||||
@@ -202,7 +202,7 @@ impl ValidatorStatuses {
|
||||
/// Process some attestations from the given `state` updating the `statuses` and
|
||||
/// `total_balances` fields.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn process_attestations<T: EthSpec>(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
@@ -213,24 +213,23 @@ impl ValidatorStatuses {
|
||||
.iter()
|
||||
.chain(state.current_epoch_attestations.iter())
|
||||
{
|
||||
let attesting_indices =
|
||||
get_attesting_indices_unsorted(state, &a.data, &a.aggregation_bitfield)?;
|
||||
let attesting_indices = get_attesting_indices(state, &a.data, &a.aggregation_bits)?;
|
||||
|
||||
let mut status = ValidatorStatus::default();
|
||||
|
||||
// Profile this attestation, updating the total balances and generating an
|
||||
// `ValidatorStatus` object that applies to all participants in the attestation.
|
||||
if is_from_epoch(a, state.current_epoch()) {
|
||||
if a.data.target.epoch == state.current_epoch() {
|
||||
status.is_current_epoch_attester = true;
|
||||
|
||||
if target_matches_epoch_start_block(a, state, state.current_epoch())? {
|
||||
status.is_current_epoch_target_attester = true;
|
||||
}
|
||||
} else if is_from_epoch(a, state.previous_epoch()) {
|
||||
} 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_slot(&a.data)?;
|
||||
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())?;
|
||||
@@ -289,7 +288,7 @@ impl ValidatorStatuses {
|
||||
/// 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.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn process_winning_roots<T: EthSpec>(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
@@ -321,37 +320,30 @@ impl ValidatorStatuses {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if some `PendingAttestation` is from the supplied `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn is_from_epoch(a: &PendingAttestation, epoch: Epoch) -> bool {
|
||||
a.data.target_epoch == epoch
|
||||
}
|
||||
|
||||
/// 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.6.3
|
||||
/// Spec v0.8.1
|
||||
fn target_matches_epoch_start_block<T: EthSpec>(
|
||||
a: &PendingAttestation,
|
||||
a: &PendingAttestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
epoch: Epoch,
|
||||
) -> Result<bool, BeaconStateError> {
|
||||
let slot = epoch.start_slot(T::slots_per_epoch());
|
||||
let state_boundary_root = *state.get_block_root(slot)?;
|
||||
|
||||
Ok(a.data.target_root == state_boundary_root)
|
||||
Ok(a.data.target.root == state_boundary_root)
|
||||
}
|
||||
|
||||
/// Returns `true` if a `PendingAttestation` and `BeaconState` share the same beacon block hash for
|
||||
/// the current slot of the `PendingAttestation`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn has_common_beacon_block_root<T: EthSpec>(
|
||||
a: &PendingAttestation,
|
||||
a: &PendingAttestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
) -> Result<bool, BeaconStateError> {
|
||||
let attestation_slot = state.get_attestation_slot(&a.data)?;
|
||||
let attestation_slot = state.get_attestation_data_slot(&a.data)?;
|
||||
let state_block_root = *state.get_block_root(attestation_slot)?;
|
||||
|
||||
Ok(a.data.beacon_block_root == state_block_root)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::common::get_attesting_indices_unsorted;
|
||||
use crate::common::get_attesting_indices;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use tree_hash::TreeHash;
|
||||
use types::*;
|
||||
@@ -16,65 +16,48 @@ impl WinningRoot {
|
||||
/// 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn is_better_than(&self, other: &Self) -> bool {
|
||||
(
|
||||
self.total_attesting_balance,
|
||||
self.crosslink.crosslink_data_root,
|
||||
) > (
|
||||
other.total_attesting_balance,
|
||||
other.crosslink.crosslink_data_root,
|
||||
)
|
||||
(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.
|
||||
/// 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.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn winning_root<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
shard: u64,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Option<WinningRoot>, BeaconStateError> {
|
||||
let shard_attestations: Vec<&PendingAttestation> = state
|
||||
let attestations: Vec<&_> = state
|
||||
.get_matching_source_attestations(epoch)?
|
||||
.iter()
|
||||
.filter(|a| a.data.shard == shard)
|
||||
.filter(|a| a.data.crosslink.shard == shard)
|
||||
.collect();
|
||||
|
||||
let mut shard_crosslinks = Vec::with_capacity(shard_attestations.len());
|
||||
for att in shard_attestations {
|
||||
shard_crosslinks.push((
|
||||
att,
|
||||
state.get_crosslink_from_attestation_data(&att.data, spec)?,
|
||||
));
|
||||
}
|
||||
|
||||
// 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();
|
||||
let candidate_crosslinks = shard_crosslinks.into_iter().filter(|(_, c)| {
|
||||
c.previous_crosslink_root.as_bytes() == ¤t_shard_crosslink_root[..]
|
||||
|| c.tree_hash_root() == current_shard_crosslink_root
|
||||
});
|
||||
|
||||
// Build a map from candidate crosslink to attestations that support that crosslink.
|
||||
let mut candidate_crosslink_map: HashMap<Crosslink, Vec<&PendingAttestation>> = HashMap::new();
|
||||
|
||||
for (attestation, crosslink) in candidate_crosslinks {
|
||||
let supporting_attestations = candidate_crosslink_map
|
||||
.entry(crosslink)
|
||||
.or_insert_with(Vec::new);
|
||||
supporting_attestations.push(attestation);
|
||||
}
|
||||
|
||||
if candidate_crosslink_map.is_empty() {
|
||||
return Ok(None);
|
||||
for a in attestations {
|
||||
if a.data.crosslink.parent_root.as_bytes() == ¤t_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 =
|
||||
@@ -83,7 +66,7 @@ pub fn winning_root<T: EthSpec>(
|
||||
state.get_total_balance(&attesting_validator_indices, spec)?;
|
||||
|
||||
let candidate = WinningRoot {
|
||||
crosslink,
|
||||
crosslink: crosslink.clone(),
|
||||
attesting_validator_indices,
|
||||
total_attesting_balance,
|
||||
};
|
||||
@@ -102,24 +85,15 @@ pub fn winning_root<T: EthSpec>(
|
||||
|
||||
pub fn get_unslashed_attesting_indices_unsorted<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestations: &[&PendingAttestation],
|
||||
attestations: &[&PendingAttestation<T>],
|
||||
) -> Result<Vec<usize>, BeaconStateError> {
|
||||
let mut output = HashSet::new();
|
||||
for a in attestations {
|
||||
output.extend(get_attesting_indices_unsorted(
|
||||
state,
|
||||
&a.data,
|
||||
&a.aggregation_bitfield,
|
||||
)?);
|
||||
output.extend(get_attesting_indices(state, &a.data, &a.aggregation_bits)?);
|
||||
}
|
||||
Ok(output
|
||||
.into_iter()
|
||||
.filter(|index| {
|
||||
state
|
||||
.validator_registry
|
||||
.get(*index)
|
||||
.map_or(false, |v| !v.slashed)
|
||||
})
|
||||
.filter(|index| state.validators.get(*index).map_or(false, |v| !v.slashed))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -131,16 +105,18 @@ mod tests {
|
||||
fn is_better_than() {
|
||||
let worse = WinningRoot {
|
||||
crosslink: Crosslink {
|
||||
epoch: Epoch::new(0),
|
||||
previous_crosslink_root: Hash256::from_slice(&[0; 32]),
|
||||
crosslink_data_root: Hash256::from_slice(&[1; 32]),
|
||||
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.crosslink_data_root = Hash256::from_slice(&[2; 32]);
|
||||
better.crosslink.data_root = Hash256::from_slice(&[2; 32]);
|
||||
|
||||
assert!(better.is_better_than(&worse));
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ pub enum Error {
|
||||
|
||||
/// Advances a state forward by one slot, performing per-epoch processing if required.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn per_slot_processing<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
cache_state(state, spec)?;
|
||||
cache_state(state)?;
|
||||
|
||||
if (state.slot > spec.genesis_slot) && ((state.slot + 1) % T::slots_per_epoch() == 0) {
|
||||
if state.slot > spec.genesis_slot && (state.slot + 1) % T::slots_per_epoch() == 0 {
|
||||
per_epoch_processing(state, spec)?;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ pub fn per_slot_processing<T: EthSpec>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cache_state<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) -> Result<(), Error> {
|
||||
let previous_slot_state_root = state.update_tree_hash_cache()?;
|
||||
fn cache_state<T: EthSpec>(state: &mut BeaconState<T>) -> Result<(), Error> {
|
||||
let previous_state_root = state.update_tree_hash_cache()?;
|
||||
|
||||
// Note: increment the state slot here to allow use of our `state_root` and `block_root`
|
||||
// getter/setter functions.
|
||||
@@ -35,14 +35,15 @@ fn cache_state<T: EthSpec>(state: &mut BeaconState<T>, spec: &ChainSpec) -> Resu
|
||||
let previous_slot = state.slot;
|
||||
state.slot += 1;
|
||||
|
||||
// Store the previous slot's post-state transition root.
|
||||
if state.latest_block_header.state_root == spec.zero_hash {
|
||||
state.latest_block_header.state_root = previous_slot_state_root
|
||||
// Store the previous slot's post state transition root.
|
||||
state.set_state_root(previous_slot, previous_state_root)?;
|
||||
|
||||
// Cache latest block header state root
|
||||
if state.latest_block_header.state_root == Hash256::zero() {
|
||||
state.latest_block_header.state_root = previous_state_root;
|
||||
}
|
||||
|
||||
// Store the previous slot's post state transition root.
|
||||
state.set_state_root(previous_slot, previous_slot_state_root)?;
|
||||
|
||||
// Cache block root
|
||||
let latest_block_root = state.latest_block_header.canonical_root();
|
||||
state.set_block_root(previous_slot, latest_block_root)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user