mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 05:14:35 +00:00
Merge branch '368' into ef-tests
This commit is contained in:
@@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -28,7 +28,7 @@ pub struct Attestation {
|
||||
pub data: AttestationData,
|
||||
pub custody_bitfield: Bitfield,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub aggregate_signature: AggregateSignature,
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl Attestation {
|
||||
@@ -49,8 +49,7 @@ impl Attestation {
|
||||
self.aggregation_bitfield
|
||||
.union_inplace(&other.aggregation_bitfield);
|
||||
self.custody_bitfield.union_inplace(&other.custody_bitfield);
|
||||
self.aggregate_signature
|
||||
.add_aggregate(&other.aggregate_signature);
|
||||
self.signature.add_aggregate(&other.signature);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Crosslink, Epoch, Hash256, Slot};
|
||||
use crate::{Epoch, Hash256};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// The data upon which an attestation is based.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -27,17 +27,17 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
)]
|
||||
pub struct AttestationData {
|
||||
// LMD GHOST vote
|
||||
pub slot: Slot,
|
||||
pub beacon_block_root: Hash256,
|
||||
|
||||
// FFG Vote
|
||||
pub source_epoch: Epoch,
|
||||
pub source_root: Hash256,
|
||||
pub target_epoch: Epoch,
|
||||
pub target_root: Hash256,
|
||||
|
||||
// Crosslink Vote
|
||||
pub shard: u64,
|
||||
pub previous_crosslink: Crosslink,
|
||||
pub previous_crosslink_root: Hash256,
|
||||
pub crosslink_data_root: Hash256,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{test_utils::TestRandom, SlashableAttestation};
|
||||
use crate::{test_utils::TestRandom, IndexedAttestation};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Two conflicting attestations.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -21,8 +21,8 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct AttesterSlashing {
|
||||
pub slashable_attestation_1: SlashableAttestation,
|
||||
pub slashable_attestation_2: SlashableAttestation,
|
||||
pub attestation_1: IndexedAttestation,
|
||||
pub attestation_2: IndexedAttestation,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -43,10 +43,11 @@ impl BeaconBlock {
|
||||
previous_block_root: spec.zero_hash,
|
||||
state_root: spec.zero_hash,
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal: spec.empty_signature.clone(),
|
||||
randao_reveal: Signature::empty_signature(),
|
||||
eth1_data: Eth1Data {
|
||||
deposit_root: spec.zero_hash,
|
||||
block_hash: spec.zero_hash,
|
||||
deposit_count: 0,
|
||||
},
|
||||
proposer_slashings: vec![],
|
||||
attester_slashings: vec![],
|
||||
@@ -55,7 +56,7 @@ impl BeaconBlock {
|
||||
voluntary_exits: vec![],
|
||||
transfers: vec![],
|
||||
},
|
||||
signature: spec.empty_signature.clone(),
|
||||
signature: Signature::empty_signature(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ impl BeaconBlock {
|
||||
pub fn temporary_block_header(&self, spec: &ChainSpec) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
state_root: spec.zero_hash,
|
||||
signature: spec.empty_signature.clone(),
|
||||
signature: Signature::empty_signature(),
|
||||
..self.block_header()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use self::epoch_cache::{get_active_validator_indices, EpochCache, Error as EpochCacheError};
|
||||
use self::exit_cache::ExitCache;
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache};
|
||||
@@ -18,6 +19,7 @@ pub use beacon_state_types::*;
|
||||
|
||||
mod beacon_state_types;
|
||||
mod epoch_cache;
|
||||
mod exit_cache;
|
||||
mod pubkey_cache;
|
||||
mod tests;
|
||||
|
||||
@@ -77,17 +79,11 @@ where
|
||||
|
||||
// Validator registry
|
||||
pub validator_registry: Vec<Validator>,
|
||||
pub validator_balances: Vec<u64>,
|
||||
pub validator_registry_update_epoch: Epoch,
|
||||
pub balances: Vec<u64>,
|
||||
|
||||
// Randomness and committees
|
||||
pub latest_randao_mixes: FixedLenVec<Hash256, T::LatestRandaoMixesLength>,
|
||||
pub previous_shuffling_start_shard: u64,
|
||||
pub current_shuffling_start_shard: u64,
|
||||
pub previous_shuffling_epoch: Epoch,
|
||||
pub current_shuffling_epoch: Epoch,
|
||||
pub previous_shuffling_seed: Hash256,
|
||||
pub current_shuffling_seed: Hash256,
|
||||
pub latest_start_shard: u64,
|
||||
|
||||
// Finality
|
||||
pub previous_epoch_attestations: Vec<PendingAttestation>,
|
||||
@@ -101,7 +97,8 @@ where
|
||||
pub finalized_root: Hash256,
|
||||
|
||||
// Recent state
|
||||
pub latest_crosslinks: FixedLenVec<Crosslink, T::ShardCount>,
|
||||
pub current_crosslinks: FixedLenVec<Crosslink, T::ShardCount>,
|
||||
pub previous_crosslinks: FixedLenVec<Crosslink, T::ShardCount>,
|
||||
pub latest_block_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
latest_state_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
latest_active_index_roots: FixedLenVec<Hash256, T::LatestActiveIndexRootsLength>,
|
||||
@@ -111,7 +108,7 @@ where
|
||||
|
||||
// Ethereum 1.0 chain data
|
||||
pub latest_eth1_data: Eth1Data,
|
||||
pub eth1_data_votes: Vec<Eth1DataVote>,
|
||||
pub eth1_data_votes: Vec<Eth1Data>,
|
||||
pub deposit_index: u64,
|
||||
|
||||
// Caching (not in the spec)
|
||||
@@ -139,6 +136,12 @@ where
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub tree_hash_cache: TreeHashCache,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
#[ssz(skip_serializing)]
|
||||
#[ssz(skip_deserializing)]
|
||||
#[tree_hash(skip_hashing)]
|
||||
#[test_random(default)]
|
||||
pub exit_cache: ExitCache,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> BeaconState<T> {
|
||||
@@ -155,6 +158,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
) -> BeaconState<T> {
|
||||
let initial_crosslink = Crosslink {
|
||||
epoch: spec.genesis_epoch,
|
||||
previous_crosslink_root: spec.zero_hash,
|
||||
crosslink_data_root: spec.zero_hash,
|
||||
};
|
||||
|
||||
@@ -166,20 +170,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
// Validator registry
|
||||
validator_registry: vec![], // Set later in the function.
|
||||
validator_balances: vec![], // Set later in the function.
|
||||
validator_registry_update_epoch: spec.genesis_epoch,
|
||||
balances: vec![], // Set later in the function.
|
||||
|
||||
// Randomness and committees
|
||||
latest_randao_mixes: FixedLenVec::from(vec![
|
||||
spec.zero_hash;
|
||||
T::LatestRandaoMixesLength::to_usize()
|
||||
]),
|
||||
previous_shuffling_start_shard: spec.genesis_start_shard,
|
||||
current_shuffling_start_shard: spec.genesis_start_shard,
|
||||
previous_shuffling_epoch: spec.genesis_epoch,
|
||||
current_shuffling_epoch: spec.genesis_epoch,
|
||||
previous_shuffling_seed: spec.zero_hash,
|
||||
current_shuffling_seed: spec.zero_hash,
|
||||
latest_start_shard: 0,
|
||||
|
||||
// Finality
|
||||
previous_epoch_attestations: vec![],
|
||||
@@ -193,22 +191,16 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
finalized_root: spec.zero_hash,
|
||||
|
||||
// Recent state
|
||||
latest_crosslinks: vec![initial_crosslink; spec.shard_count as usize].into(),
|
||||
latest_block_roots: FixedLenVec::from(vec![
|
||||
current_crosslinks: vec![initial_crosslink.clone(); T::ShardCount::to_usize()].into(),
|
||||
previous_crosslinks: vec![initial_crosslink; T::ShardCount::to_usize()].into(),
|
||||
latest_block_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(),
|
||||
latest_state_roots: vec![spec.zero_hash; T::SlotsPerHistoricalRoot::to_usize()].into(),
|
||||
latest_active_index_roots: vec![
|
||||
spec.zero_hash;
|
||||
T::SlotsPerHistoricalRoot::to_usize()
|
||||
]),
|
||||
latest_state_roots: FixedLenVec::from(vec![
|
||||
spec.zero_hash;
|
||||
T::SlotsPerHistoricalRoot::to_usize()
|
||||
]),
|
||||
latest_active_index_roots: FixedLenVec::from(
|
||||
vec![spec.zero_hash; T::LatestActiveIndexRootsLength::to_usize()],
|
||||
),
|
||||
latest_slashed_balances: FixedLenVec::from(vec![
|
||||
0;
|
||||
T::LatestSlashedExitLength::to_usize()
|
||||
]),
|
||||
T::LatestActiveIndexRootsLength::to_usize()
|
||||
]
|
||||
.into(),
|
||||
latest_slashed_balances: vec![0; T::LatestSlashedExitLength::to_usize()].into(),
|
||||
latest_block_header: BeaconBlock::empty(spec).temporary_block_header(spec),
|
||||
historical_roots: vec![],
|
||||
|
||||
@@ -231,6 +223,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
],
|
||||
pubkey_cache: PubkeyCache::default(),
|
||||
tree_hash_cache: TreeHashCache::default(),
|
||||
exit_cache: ExitCache::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,9 +267,14 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// If the current epoch is the genesis epoch, the genesis_epoch is returned.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn previous_epoch(&self, spec: &ChainSpec) -> Epoch {
|
||||
self.current_epoch(&spec) - 1
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
if current_epoch > spec.genesis_epoch {
|
||||
current_epoch - 1
|
||||
} else {
|
||||
current_epoch
|
||||
}
|
||||
}
|
||||
|
||||
/// The epoch following `self.current_epoch()`.
|
||||
@@ -286,6 +284,60 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
self.current_epoch(spec) + 1
|
||||
}
|
||||
|
||||
/// Return the number of committees at ``epoch``.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_epoch_committee_count(&self, epoch: Epoch, spec: &ChainSpec) -> u64 {
|
||||
let active_validator_indices = self.get_active_validator_indices(epoch);
|
||||
spec.get_epoch_committee_count(active_validator_indices.len())
|
||||
}
|
||||
|
||||
/// Return the number of shards to increment `state.latest_start_shard` during `epoch`.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_shard_delta(&self, epoch: Epoch, spec: &ChainSpec) -> u64 {
|
||||
std::cmp::min(
|
||||
self.get_epoch_committee_count(epoch, spec),
|
||||
T::ShardCount::to_u64() - T::ShardCount::to_u64() / spec.slots_per_epoch,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return the start shard for an epoch less than or equal to the next epoch.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_epoch_start_shard(&self, epoch: Epoch, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
if epoch > self.current_epoch(spec) + 1 {
|
||||
return Err(Error::EpochOutOfBounds);
|
||||
}
|
||||
let shard_count = T::ShardCount::to_u64();
|
||||
let mut check_epoch = self.current_epoch(spec) + 1;
|
||||
let mut shard = (self.latest_start_shard
|
||||
+ self.get_shard_delta(self.current_epoch(spec), spec))
|
||||
% shard_count;
|
||||
while check_epoch > epoch {
|
||||
check_epoch -= 1;
|
||||
shard = (shard + shard_count - self.get_shard_delta(check_epoch, spec)) % shard_count;
|
||||
}
|
||||
Ok(shard)
|
||||
}
|
||||
|
||||
/// Get the slot of an attestation.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_attestation_slot(
|
||||
&self,
|
||||
attestation_data: &AttestationData,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Slot, Error> {
|
||||
let epoch = attestation_data.target_epoch;
|
||||
let committee_count = self.get_epoch_committee_count(epoch, spec);
|
||||
let offset = (attestation_data.shard + spec.shard_count
|
||||
- self.get_epoch_start_shard(epoch, spec)?)
|
||||
% spec.shard_count;
|
||||
Ok(epoch.start_slot(spec.slots_per_epoch)
|
||||
+ offset / (committee_count / spec.slots_per_epoch))
|
||||
}
|
||||
|
||||
/// Returns the active validator indices for the given epoch, assuming there is no validator
|
||||
/// registry update in the next epoch.
|
||||
///
|
||||
@@ -339,6 +391,17 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
.ok_or_else(|| Error::SlotOutOfBounds)?)
|
||||
}
|
||||
|
||||
// FIXME(sproul): implement this
|
||||
pub fn get_crosslink_committee(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
shard: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&CrosslinkCommittee, Error> {
|
||||
drop((epoch, shard, spec));
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Returns the crosslink committees for some shard in an epoch.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
@@ -415,6 +478,18 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
Ok(&self.latest_block_roots[i])
|
||||
}
|
||||
|
||||
/// Return the block root at a recent `slot`.
|
||||
///
|
||||
/// Spec v0.6.0
|
||||
// FIXME(sproul): name swap with get_block_root
|
||||
pub fn get_block_root_at_epoch(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&Hash256, BeaconStateError> {
|
||||
self.get_block_root(epoch.start_slot(spec.slots_per_epoch))
|
||||
}
|
||||
|
||||
/// Sets the block root for some given slot.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
@@ -488,14 +563,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for `latest_active_index_roots`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch(spec);
|
||||
|
||||
if (current_epoch - self.latest_active_index_roots.len() as u64
|
||||
+ spec.activation_exit_delay
|
||||
< epoch)
|
||||
& (epoch <= current_epoch + spec.activation_exit_delay)
|
||||
if current_epoch - self.latest_active_index_roots.len() as u64 + spec.activation_exit_delay
|
||||
< epoch
|
||||
&& epoch <= current_epoch + spec.activation_exit_delay
|
||||
{
|
||||
Ok(epoch.as_usize() % self.latest_active_index_roots.len())
|
||||
} else {
|
||||
@@ -505,7 +579,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_active_index_root(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
let i = self.get_active_index_root_index(epoch, spec)?;
|
||||
Ok(self.latest_active_index_roots[i])
|
||||
@@ -513,7 +587,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Set the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn set_active_index_root(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
@@ -563,7 +637,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for `latest_slashed_balances`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
fn get_slashed_balance_index(&self, epoch: Epoch) -> Result<usize, Error> {
|
||||
let i = epoch.as_usize() % self.latest_slashed_balances.len();
|
||||
|
||||
@@ -578,7 +652,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_slashed_balance(&self, epoch: Epoch) -> Result<u64, Error> {
|
||||
let i = self.get_slashed_balance_index(epoch)?;
|
||||
Ok(self.latest_slashed_balances[i])
|
||||
@@ -586,13 +660,48 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn set_slashed_balance(&mut self, epoch: Epoch, balance: u64) -> Result<(), Error> {
|
||||
let i = self.get_slashed_balance_index(epoch)?;
|
||||
self.latest_slashed_balances[i] = balance;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the attestations from the current or previous epoch.
|
||||
///
|
||||
/// Spec v0.6.0
|
||||
pub fn get_matching_source_attestations(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<&[PendingAttestation], Error> {
|
||||
if epoch == self.current_epoch(spec) {
|
||||
Ok(&self.current_epoch_attestations)
|
||||
} else if epoch == self.previous_epoch(spec) {
|
||||
Ok(&self.previous_epoch_attestations)
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform an attestation into the crosslink that it reinforces.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_crosslink_from_attestation_data(
|
||||
&self,
|
||||
data: &AttestationData,
|
||||
spec: &ChainSpec,
|
||||
) -> Crosslink {
|
||||
Crosslink {
|
||||
epoch: std::cmp::min(
|
||||
data.target_epoch,
|
||||
self.current_crosslinks[data.shard as usize].epoch + spec.max_crosslink_epochs,
|
||||
),
|
||||
previous_crosslink_root: data.previous_crosslink_root,
|
||||
crosslink_data_root: data.crosslink_data_root,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a seed for the given `epoch`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
@@ -611,17 +720,16 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
pub fn get_effective_balance(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
_spec: &ChainSpec,
|
||||
) -> Result<u64, Error> {
|
||||
let balance = self
|
||||
.validator_balances
|
||||
self.validator_registry
|
||||
.get(validator_index)
|
||||
.ok_or_else(|| Error::UnknownValidator)?;
|
||||
Ok(std::cmp::min(*balance, spec.max_deposit_amount))
|
||||
.map(|v| v.effective_balance)
|
||||
.ok_or_else(|| Error::UnknownValidator)
|
||||
}
|
||||
|
||||
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
||||
@@ -631,11 +739,19 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
epoch + 1 + spec.activation_exit_delay
|
||||
}
|
||||
|
||||
/// Initiate an exit for the validator of the given `index`.
|
||||
/// Return the churn limit for the current epoch (number of validators who can leave per epoch).
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn initiate_validator_exit(&mut self, validator_index: usize) {
|
||||
self.validator_registry[validator_index].initiated_exit = true;
|
||||
/// Uses the epoch cache, and will error if it isn't initialized.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit,
|
||||
self.cache(RelativeEpoch::Current, spec)?
|
||||
.active_validator_indices
|
||||
.len() as u64
|
||||
/ spec.churn_limit_quotient,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the `slot`, `shard` and `committee_index` for which a validator must produce an
|
||||
@@ -661,7 +777,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the combined effective balance of an array of validators.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
pub fn get_total_balance(
|
||||
&self,
|
||||
validator_indices: &[usize],
|
||||
@@ -681,6 +797,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
self.build_epoch_cache(RelativeEpoch::NextWithRegistryChange, spec)?;
|
||||
self.update_pubkey_cache()?;
|
||||
self.update_tree_hash_cache()?;
|
||||
self.exit_cache
|
||||
.build_from_registry(&self.validator_registry, spec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -207,8 +207,9 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
) -> Self {
|
||||
Self {
|
||||
epoch: state.previous_epoch(spec),
|
||||
shuffling_start_shard: state.previous_shuffling_start_shard,
|
||||
shuffling_seed: state.previous_shuffling_seed,
|
||||
// FIXME(sproul)
|
||||
shuffling_start_shard: 0,
|
||||
shuffling_seed: spec.zero_hash,
|
||||
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||
active_validator_indices,
|
||||
}
|
||||
@@ -222,8 +223,9 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
) -> Self {
|
||||
Self {
|
||||
epoch: state.current_epoch(spec),
|
||||
shuffling_start_shard: state.current_shuffling_start_shard,
|
||||
shuffling_seed: state.current_shuffling_seed,
|
||||
// FIXME(sproul)
|
||||
shuffling_start_shard: 0,
|
||||
shuffling_seed: spec.zero_hash,
|
||||
committees_per_epoch: spec.get_epoch_committee_count(active_validator_indices.len()),
|
||||
active_validator_indices,
|
||||
}
|
||||
@@ -243,8 +245,9 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
let next_epoch = state.next_epoch(spec);
|
||||
let committees_per_epoch = spec.get_epoch_committee_count(active_validator_indices.len());
|
||||
|
||||
let epochs_since_last_registry_update =
|
||||
current_epoch - state.validator_registry_update_epoch;
|
||||
// FIXME(sproul)
|
||||
// current_epoch - state.validator_registry_update_epoch;
|
||||
let epochs_since_last_registry_update = 0u64;
|
||||
|
||||
let (seed, shuffling_start_shard) = if registry_change {
|
||||
let next_seed = state
|
||||
@@ -252,7 +255,9 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
.map_err(|_| Error::UnableToGenerateSeed)?;
|
||||
(
|
||||
next_seed,
|
||||
(state.current_shuffling_start_shard + committees_per_epoch) % spec.shard_count,
|
||||
0,
|
||||
// FIXME(sproul)
|
||||
// (state.current_shuffling_start_shard + committees_per_epoch) % spec.shard_count,
|
||||
)
|
||||
} else if (epochs_since_last_registry_update > 1)
|
||||
& epochs_since_last_registry_update.is_power_of_two()
|
||||
@@ -260,11 +265,13 @@ impl EpochCrosslinkCommitteesBuilder {
|
||||
let next_seed = state
|
||||
.generate_seed(next_epoch, spec)
|
||||
.map_err(|_| Error::UnableToGenerateSeed)?;
|
||||
(next_seed, state.current_shuffling_start_shard)
|
||||
(
|
||||
next_seed, 0, /* FIXME(sproul) state.current_shuffling_start_shard*/
|
||||
)
|
||||
} else {
|
||||
(
|
||||
state.current_shuffling_seed,
|
||||
state.current_shuffling_start_shard,
|
||||
spec.zero_hash, // state.current_shuffling_seed,
|
||||
0 // state.current_shuffling_start_shard,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![cfg(test)]
|
||||
#![cfg(all(not(test), test))]
|
||||
|
||||
use super::*;
|
||||
use crate::beacon_state::FewValidatorsEthSpec;
|
||||
|
||||
35
eth2/types/src/beacon_state/exit_cache.rs
Normal file
35
eth2/types/src/beacon_state/exit_cache.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use super::{ChainSpec, Epoch, Validator};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Map from exit epoch to the number of validators known to be exiting/exited at that epoch.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ExitCache(HashMap<Epoch, u64>);
|
||||
|
||||
impl ExitCache {
|
||||
/// Add all validators with a non-trivial exit epoch to the cache.
|
||||
pub fn build_from_registry(&mut self, validator_registry: &[Validator], spec: &ChainSpec) {
|
||||
validator_registry
|
||||
.iter()
|
||||
.filter(|validator| validator.exit_epoch != spec.far_future_epoch)
|
||||
.for_each(|validator| self.record_validator_exit(validator.exit_epoch));
|
||||
}
|
||||
|
||||
/// Record the exit of a single validator in the cache.
|
||||
///
|
||||
/// Must only be called once per exiting validator.
|
||||
pub fn record_validator_exit(&mut self, exit_epoch: Epoch) {
|
||||
*self.0.entry(exit_epoch).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
/// Get the greatest epoch for which validator exits are known.
|
||||
pub fn max_epoch(&self) -> Option<Epoch> {
|
||||
// This could probably be made even faster by caching the maximum.
|
||||
self.0.keys().max().cloned()
|
||||
}
|
||||
|
||||
/// Get the number of validators exiting/exited at a given epoch, or zero if not known.
|
||||
pub fn get_churn_at(&self, epoch: Epoch) -> u64 {
|
||||
self.0.get(&epoch).cloned().unwrap_or(0)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,23 @@
|
||||
use crate::*;
|
||||
use bls::Signature;
|
||||
use int_to_bytes::int_to_bytes4;
|
||||
use serde_derive::Deserialize;
|
||||
use test_utils::u8_from_hex_str;
|
||||
|
||||
const GWEI: u64 = 1_000_000_000;
|
||||
|
||||
/// Each of the BLS signature domains.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub enum Domain {
|
||||
BeaconBlock,
|
||||
BeaconProposer,
|
||||
Randao,
|
||||
Attestation,
|
||||
Deposit,
|
||||
Exit,
|
||||
VoluntaryExit,
|
||||
Transfer,
|
||||
}
|
||||
|
||||
/// Holds all the "constants" for a BeaconChain.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
#[derive(PartialEq, Debug, Clone, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ChainSpec {
|
||||
@@ -29,35 +26,32 @@ pub struct ChainSpec {
|
||||
*/
|
||||
pub shard_count: u64,
|
||||
pub target_committee_size: u64,
|
||||
pub max_balance_churn_quotient: u64,
|
||||
pub max_indices_per_slashable_vote: usize,
|
||||
pub max_exit_dequeues_per_epoch: u64,
|
||||
pub max_indices_per_attestation: u64,
|
||||
pub min_per_epoch_churn_limit: u64,
|
||||
pub churn_limit_quotient: u64,
|
||||
pub base_rewards_per_epoch: u64,
|
||||
pub shuffle_round_count: u8,
|
||||
|
||||
/*
|
||||
* Deposit contract
|
||||
*/
|
||||
pub deposit_contract_address: Address,
|
||||
pub deposit_contract_tree_depth: u64,
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
*/
|
||||
pub min_deposit_amount: u64,
|
||||
pub max_deposit_amount: u64,
|
||||
pub fork_choice_balance_increment: u64,
|
||||
pub max_effective_balance: u64,
|
||||
pub ejection_balance: u64,
|
||||
pub effective_balance_increment: u64,
|
||||
|
||||
/*
|
||||
* Initial Values
|
||||
*/
|
||||
pub genesis_fork_version: u32,
|
||||
pub genesis_slot: Slot,
|
||||
pub genesis_epoch: Epoch,
|
||||
pub genesis_start_shard: u64,
|
||||
pub far_future_epoch: Epoch,
|
||||
pub zero_hash: Hash256,
|
||||
pub empty_signature: Signature,
|
||||
#[serde(deserialize_with = "u8_from_hex_str")]
|
||||
pub bls_withdrawal_prefix_byte: u8,
|
||||
|
||||
@@ -69,18 +63,21 @@ pub struct ChainSpec {
|
||||
pub slots_per_epoch: u64,
|
||||
pub min_seed_lookahead: Epoch,
|
||||
pub activation_exit_delay: u64,
|
||||
pub epochs_per_eth1_voting_period: u64,
|
||||
pub slots_per_eth1_voting_period: u64,
|
||||
pub slots_per_historical_root: usize,
|
||||
pub min_validator_withdrawability_delay: Epoch,
|
||||
pub persistent_committee_period: u64,
|
||||
pub max_crosslink_epochs: u64,
|
||||
pub min_epochs_to_inactivity_penalty: u64,
|
||||
|
||||
/*
|
||||
* Reward and penalty quotients
|
||||
*/
|
||||
pub base_reward_quotient: u64,
|
||||
pub whistleblower_reward_quotient: u64,
|
||||
pub attestation_inclusion_reward_quotient: u64,
|
||||
pub whistleblowing_reward_quotient: u64,
|
||||
pub proposer_reward_quotient: u64,
|
||||
pub inactivity_penalty_quotient: u64,
|
||||
pub min_penalty_quotient: u64,
|
||||
pub min_slashing_penalty_quotient: u64,
|
||||
|
||||
/*
|
||||
* Max operations per block
|
||||
@@ -100,11 +97,11 @@ pub struct ChainSpec {
|
||||
*
|
||||
* Use `ChainSpec::get_domain(..)` to access these values.
|
||||
*/
|
||||
domain_beacon_block: u32,
|
||||
domain_beacon_proposer: u32,
|
||||
domain_randao: u32,
|
||||
domain_attestation: u32,
|
||||
domain_deposit: u32,
|
||||
domain_exit: u32,
|
||||
domain_voluntary_exit: u32,
|
||||
domain_transfer: u32,
|
||||
|
||||
/*
|
||||
@@ -118,7 +115,7 @@ pub struct ChainSpec {
|
||||
impl ChainSpec {
|
||||
/// Return the number of committees in one epoch.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_epoch_committee_count(&self, active_validator_count: usize) -> u64 {
|
||||
std::cmp::max(
|
||||
1,
|
||||
@@ -131,14 +128,14 @@ impl ChainSpec {
|
||||
|
||||
/// Get the domain number that represents the fork meta and signature domain.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
|
||||
let domain_constant = match domain {
|
||||
Domain::BeaconBlock => self.domain_beacon_block,
|
||||
Domain::BeaconProposer => self.domain_beacon_proposer,
|
||||
Domain::Randao => self.domain_randao,
|
||||
Domain::Attestation => self.domain_attestation,
|
||||
Domain::Deposit => self.domain_deposit,
|
||||
Domain::Exit => self.domain_exit,
|
||||
Domain::VoluntaryExit => self.domain_voluntary_exit,
|
||||
Domain::Transfer => self.domain_transfer,
|
||||
};
|
||||
|
||||
@@ -153,47 +150,40 @@ impl ChainSpec {
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub(crate) fn foundation() -> Self {
|
||||
let genesis_slot = Slot::new(2_u64.pow(32));
|
||||
let slots_per_epoch = 64;
|
||||
let genesis_epoch = genesis_slot.epoch(slots_per_epoch);
|
||||
|
||||
Self {
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
shard_count: 1_024,
|
||||
target_committee_size: 128,
|
||||
max_balance_churn_quotient: 32,
|
||||
max_indices_per_slashable_vote: 4_096,
|
||||
max_exit_dequeues_per_epoch: 4,
|
||||
max_indices_per_attestation: 4096,
|
||||
min_per_epoch_churn_limit: 4,
|
||||
churn_limit_quotient: 65_536,
|
||||
base_rewards_per_epoch: 5,
|
||||
shuffle_round_count: 90,
|
||||
|
||||
/*
|
||||
* Deposit contract
|
||||
*/
|
||||
deposit_contract_address: Address::zero(),
|
||||
deposit_contract_tree_depth: 32,
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
*/
|
||||
min_deposit_amount: u64::pow(2, 0) * GWEI,
|
||||
max_deposit_amount: u64::pow(2, 5) * GWEI,
|
||||
fork_choice_balance_increment: u64::pow(2, 0) * GWEI,
|
||||
ejection_balance: u64::pow(2, 4) * GWEI,
|
||||
min_deposit_amount: u64::pow(2, 0) * u64::pow(10, 9),
|
||||
max_effective_balance: u64::pow(2, 5) * u64::pow(10, 9),
|
||||
ejection_balance: u64::pow(2, 4) * u64::pow(10, 9),
|
||||
effective_balance_increment: u64::pow(2, 0) * u64::pow(10, 9),
|
||||
|
||||
/*
|
||||
* Initial Values
|
||||
*/
|
||||
genesis_fork_version: 0,
|
||||
genesis_slot,
|
||||
genesis_epoch,
|
||||
genesis_start_shard: 0,
|
||||
genesis_slot: Slot::new(0),
|
||||
genesis_epoch: Epoch::new(0),
|
||||
far_future_epoch: Epoch::new(u64::max_value()),
|
||||
zero_hash: Hash256::zero(),
|
||||
empty_signature: Signature::empty_signature(),
|
||||
bls_withdrawal_prefix_byte: 0,
|
||||
|
||||
/*
|
||||
@@ -201,21 +191,24 @@ impl ChainSpec {
|
||||
*/
|
||||
seconds_per_slot: 6,
|
||||
min_attestation_inclusion_delay: 4,
|
||||
slots_per_epoch,
|
||||
slots_per_epoch: 64,
|
||||
min_seed_lookahead: Epoch::new(1),
|
||||
activation_exit_delay: 4,
|
||||
epochs_per_eth1_voting_period: 16,
|
||||
slots_per_eth1_voting_period: 1_024,
|
||||
slots_per_historical_root: 8_192,
|
||||
min_validator_withdrawability_delay: Epoch::new(256),
|
||||
persistent_committee_period: 2_048,
|
||||
max_crosslink_epochs: 64,
|
||||
min_epochs_to_inactivity_penalty: 4,
|
||||
|
||||
/*
|
||||
* Reward and penalty quotients
|
||||
*/
|
||||
base_reward_quotient: 32,
|
||||
whistleblower_reward_quotient: 512,
|
||||
attestation_inclusion_reward_quotient: 8,
|
||||
inactivity_penalty_quotient: 16_777_216,
|
||||
min_penalty_quotient: 32,
|
||||
whistleblowing_reward_quotient: 512,
|
||||
proposer_reward_quotient: 8,
|
||||
inactivity_penalty_quotient: 33_554_432,
|
||||
min_slashing_penalty_quotient: 32,
|
||||
|
||||
/*
|
||||
* Max operations per block
|
||||
@@ -225,16 +218,16 @@ impl ChainSpec {
|
||||
max_attestations: 128,
|
||||
max_deposits: 16,
|
||||
max_voluntary_exits: 16,
|
||||
max_transfers: 16,
|
||||
max_transfers: 0,
|
||||
|
||||
/*
|
||||
* Signature domains
|
||||
*/
|
||||
domain_beacon_block: 0,
|
||||
domain_beacon_proposer: 0,
|
||||
domain_randao: 1,
|
||||
domain_attestation: 2,
|
||||
domain_deposit: 3,
|
||||
domain_exit: 4,
|
||||
domain_voluntary_exit: 4,
|
||||
domain_transfer: 5,
|
||||
|
||||
/*
|
||||
@@ -312,11 +305,11 @@ mod tests {
|
||||
fn test_get_domain() {
|
||||
let spec = ChainSpec::foundation();
|
||||
|
||||
test_domain(Domain::BeaconBlock, spec.domain_beacon_block, &spec);
|
||||
test_domain(Domain::BeaconProposer, spec.domain_beacon_proposer, &spec);
|
||||
test_domain(Domain::Randao, spec.domain_randao, &spec);
|
||||
test_domain(Domain::Attestation, spec.domain_attestation, &spec);
|
||||
test_domain(Domain::Deposit, spec.domain_deposit, &spec);
|
||||
test_domain(Domain::Exit, spec.domain_exit, &spec);
|
||||
test_domain(Domain::VoluntaryExit, spec.domain_voluntary_exit, &spec);
|
||||
test_domain(Domain::Transfer, spec.domain_transfer, &spec);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Specifies the block hash for a shard at an epoch.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
@@ -25,6 +26,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
)]
|
||||
pub struct Crosslink {
|
||||
pub epoch: Epoch,
|
||||
pub previous_crosslink_root: Hash256,
|
||||
pub crosslink_data_root: Hash256,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// A deposit to potentially become a beacon chain validator.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -25,7 +25,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
pub struct Deposit {
|
||||
pub proof: FixedLenVec<Hash256, U32>,
|
||||
pub index: u64,
|
||||
pub deposit_data: DepositData,
|
||||
pub data: DepositData,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
use super::DepositInput;
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
use bls::{PublicKey, Signature};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// Data generated by the deposit contract.
|
||||
/// The data supplied by the user to the deposit contract.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -17,14 +19,45 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
SignedRoot,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct DepositData {
|
||||
pub pubkey: PublicKey,
|
||||
pub withdrawal_credentials: Hash256,
|
||||
pub amount: u64,
|
||||
pub timestamp: u64,
|
||||
pub deposit_input: DepositInput,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl DepositData {
|
||||
/// Generate the signature for a given DepositData details.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn create_signature(
|
||||
&self,
|
||||
secret_key: &SecretKey,
|
||||
epoch: Epoch,
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) -> Signature {
|
||||
let msg = self.signed_root();
|
||||
let domain = spec.get_domain(epoch, Domain::Deposit, fork);
|
||||
|
||||
Signature::new(msg.as_slice(), domain, secret_key)
|
||||
}
|
||||
|
||||
/// Verify that proof-of-possession is valid.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn validate_signature(&self, epoch: Epoch, fork: &Fork, spec: &ChainSpec) -> bool {
|
||||
let msg = self.signed_root();
|
||||
let domain = spec.get_domain(epoch, Domain::Deposit, fork);
|
||||
|
||||
self.signature.verify(&msg, domain, &self.pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -33,4 +66,23 @@ mod tests {
|
||||
|
||||
ssz_tests!(DepositData);
|
||||
cached_tree_hash_tests!(DepositData);
|
||||
|
||||
#[test]
|
||||
fn can_create_and_validate() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let fork = Fork::genesis(&spec);
|
||||
let keypair = Keypair::random();
|
||||
let epoch = Epoch::new(0);
|
||||
|
||||
let mut deposit_input = DepositData {
|
||||
pubkey: keypair.pk.clone(),
|
||||
amount: 0,
|
||||
withdrawal_credentials: Hash256::zero(),
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
|
||||
deposit_input.signature = deposit_input.create_signature(&keypair.sk, epoch, &fork, &spec);
|
||||
|
||||
assert!(deposit_input.validate_signature(epoch, &fork, &spec));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Contains data obtained from the Eth1 chain.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@@ -24,6 +24,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
)]
|
||||
pub struct Eth1Data {
|
||||
pub deposit_root: Hash256,
|
||||
pub deposit_count: u64,
|
||||
pub block_hash: Hash256,
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::{
|
||||
test_utils::{fork_from_hex_str, TestRandom},
|
||||
ChainSpec, Epoch,
|
||||
};
|
||||
use int_to_bytes::int_to_bytes4;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -11,7 +10,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -36,21 +35,18 @@ pub struct Fork {
|
||||
impl Fork {
|
||||
/// Initialize the `Fork` from the genesis parameters in the `spec`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn genesis(spec: &ChainSpec) -> Self {
|
||||
let mut current_version: [u8; 4] = [0; 4];
|
||||
current_version.copy_from_slice(&int_to_bytes4(spec.genesis_fork_version));
|
||||
|
||||
Self {
|
||||
previous_version: current_version,
|
||||
current_version,
|
||||
previous_version: [0; 4],
|
||||
current_version: [0; 4],
|
||||
epoch: spec.genesis_epoch,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the fork version of the given ``epoch``.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] {
|
||||
if epoch < self.epoch {
|
||||
return self.previous_version;
|
||||
@@ -66,10 +62,9 @@ mod tests {
|
||||
ssz_tests!(Fork);
|
||||
cached_tree_hash_tests!(Fork);
|
||||
|
||||
fn test_genesis(version: u32, epoch: Epoch) {
|
||||
fn test_genesis(epoch: Epoch) {
|
||||
let mut spec = ChainSpec::foundation();
|
||||
|
||||
spec.genesis_fork_version = version;
|
||||
spec.genesis_epoch = epoch;
|
||||
|
||||
let fork = Fork::genesis(&spec);
|
||||
@@ -79,19 +74,14 @@ mod tests {
|
||||
fork.previous_version, fork.current_version,
|
||||
"previous and current are not identical"
|
||||
);
|
||||
assert_eq!(
|
||||
fork.current_version,
|
||||
version.to_le_bytes(),
|
||||
"current version incorrect"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis() {
|
||||
test_genesis(0, Epoch::new(0));
|
||||
test_genesis(9, Epoch::new(11));
|
||||
test_genesis(2_u32.pow(31), Epoch::new(2_u64.pow(63)));
|
||||
test_genesis(u32::max_value(), Epoch::max_value());
|
||||
test_genesis(Epoch::new(0));
|
||||
test_genesis(Epoch::new(11));
|
||||
test_genesis(Epoch::new(2_u64.pow(63)));
|
||||
test_genesis(Epoch::max_value());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
136
eth2/types/src/indexed_attestation.rs
Normal file
136
eth2/types/src/indexed_attestation.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, Bitfield};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// To be included in an `AttesterSlashing`.
|
||||
///
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct IndexedAttestation {
|
||||
/// Lists validator registry indices, not committee indices.
|
||||
pub custody_bit_0_indices: Vec<u64>,
|
||||
pub custody_bit_1_indices: Vec<u64>,
|
||||
pub data: AttestationData,
|
||||
pub custody_bitfield: Bitfield,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl IndexedAttestation {
|
||||
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn is_double_vote(&self, other: &IndexedAttestation) -> bool {
|
||||
self.data.target_epoch == other.data.target_epoch && self.data != other.data
|
||||
}
|
||||
|
||||
/// Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
|
||||
///
|
||||
/// Spec v0.6.1
|
||||
pub fn is_surround_vote(&self, other: &IndexedAttestation) -> bool {
|
||||
self.data.source_epoch < other.data.source_epoch
|
||||
&& other.data.target_epoch < self.data.target_epoch
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::slot_epoch::Epoch;
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_is_double_vote_true() {
|
||||
let indexed_vote_first = create_indexed_attestation(3, 1);
|
||||
let indexed_vote_second = create_indexed_attestation(3, 2);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_double_vote(&indexed_vote_second),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_double_vote_false() {
|
||||
let indexed_vote_first = create_indexed_attestation(1, 1);
|
||||
let indexed_vote_second = create_indexed_attestation(2, 1);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_double_vote(&indexed_vote_second),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_true() {
|
||||
let indexed_vote_first = create_indexed_attestation(2, 1);
|
||||
let indexed_vote_second = create_indexed_attestation(1, 2);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_surround_vote(&indexed_vote_second),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_true_realistic() {
|
||||
let indexed_vote_first = create_indexed_attestation(4, 1);
|
||||
let indexed_vote_second = create_indexed_attestation(3, 2);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_surround_vote(&indexed_vote_second),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_false_source_epoch_fails() {
|
||||
let indexed_vote_first = create_indexed_attestation(2, 2);
|
||||
let indexed_vote_second = create_indexed_attestation(1, 1);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_surround_vote(&indexed_vote_second),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_false_target_epoch_fails() {
|
||||
let indexed_vote_first = create_indexed_attestation(1, 1);
|
||||
let indexed_vote_second = create_indexed_attestation(2, 2);
|
||||
|
||||
assert_eq!(
|
||||
indexed_vote_first.is_surround_vote(&indexed_vote_second),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
ssz_tests!(IndexedAttestation);
|
||||
cached_tree_hash_tests!(IndexedAttestation);
|
||||
|
||||
fn create_indexed_attestation(target_epoch: u64, source_epoch: u64) -> IndexedAttestation {
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let mut indexed_vote = IndexedAttestation::random_for_test(&mut rng);
|
||||
|
||||
indexed_vote.data.source_epoch = Epoch::new(source_epoch);
|
||||
indexed_vote.data.target_epoch = Epoch::new(target_epoch);
|
||||
indexed_vote
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,13 @@ pub mod crosslink;
|
||||
pub mod crosslink_committee;
|
||||
pub mod deposit;
|
||||
pub mod deposit_data;
|
||||
pub mod deposit_input;
|
||||
pub mod eth1_data;
|
||||
pub mod eth1_data_vote;
|
||||
pub mod fork;
|
||||
pub mod free_attestation;
|
||||
pub mod historical_batch;
|
||||
pub mod indexed_attestation;
|
||||
pub mod pending_attestation;
|
||||
pub mod proposer_slashing;
|
||||
pub mod slashable_attestation;
|
||||
pub mod transfer;
|
||||
pub mod voluntary_exit;
|
||||
#[macro_use]
|
||||
@@ -52,16 +50,14 @@ pub use crate::crosslink::Crosslink;
|
||||
pub use crate::crosslink_committee::CrosslinkCommittee;
|
||||
pub use crate::deposit::Deposit;
|
||||
pub use crate::deposit_data::DepositData;
|
||||
pub use crate::deposit_input::DepositInput;
|
||||
pub use crate::eth1_data::Eth1Data;
|
||||
pub use crate::eth1_data_vote::Eth1DataVote;
|
||||
pub use crate::fork::Fork;
|
||||
pub use crate::free_attestation::FreeAttestation;
|
||||
pub use crate::historical_batch::HistoricalBatch;
|
||||
pub use crate::indexed_attestation::IndexedAttestation;
|
||||
pub use crate::pending_attestation::PendingAttestation;
|
||||
pub use crate::proposer_slashing::ProposerSlashing;
|
||||
pub use crate::relative_epoch::{Error as RelativeEpochError, RelativeEpoch};
|
||||
pub use crate::slashable_attestation::SlashableAttestation;
|
||||
pub use crate::slot_epoch::{Epoch, Slot};
|
||||
pub use crate::slot_height::SlotHeight;
|
||||
pub use crate::transfer::Transfer;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Attestation, AttestationData, Bitfield, Slot};
|
||||
use crate::{Attestation, AttestationData, Bitfield};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -8,7 +8,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// An attestation that has been included in the state but not yet fully processed.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -24,18 +24,22 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
pub struct PendingAttestation {
|
||||
pub aggregation_bitfield: Bitfield,
|
||||
pub data: AttestationData,
|
||||
pub custody_bitfield: Bitfield,
|
||||
pub inclusion_slot: Slot,
|
||||
pub inclusion_delay: u64,
|
||||
pub proposer_index: u64,
|
||||
}
|
||||
|
||||
impl PendingAttestation {
|
||||
/// Create a `PendingAttestation` from an `Attestation`, at the given `inclusion_slot`.
|
||||
pub fn from_attestation(attestation: &Attestation, inclusion_slot: Slot) -> Self {
|
||||
/// Create a `PendingAttestation` from an `Attestation`.
|
||||
pub fn from_attestation(
|
||||
attestation: &Attestation,
|
||||
inclusion_delay: u64,
|
||||
proposer_index: u64,
|
||||
) -> Self {
|
||||
PendingAttestation {
|
||||
data: attestation.data.clone(),
|
||||
aggregation_bitfield: attestation.aggregation_bitfield.clone(),
|
||||
custody_bitfield: attestation.custody_bitfield.clone(),
|
||||
inclusion_slot,
|
||||
inclusion_delay,
|
||||
proposer_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +0,0 @@
|
||||
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, Bitfield, ChainSpec};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// To be included in an `AttesterSlashing`.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
Clone,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct SlashableAttestation {
|
||||
/// Lists validator registry indices, not committee indices.
|
||||
pub validator_indices: Vec<u64>,
|
||||
pub data: AttestationData,
|
||||
pub custody_bitfield: Bitfield,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub aggregate_signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl SlashableAttestation {
|
||||
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn is_double_vote(&self, other: &SlashableAttestation, spec: &ChainSpec) -> bool {
|
||||
self.data.slot.epoch(spec.slots_per_epoch) == other.data.slot.epoch(spec.slots_per_epoch)
|
||||
}
|
||||
|
||||
/// Check if ``attestation_data_1`` surrounds ``attestation_data_2``.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
pub fn is_surround_vote(&self, other: &SlashableAttestation, spec: &ChainSpec) -> bool {
|
||||
let source_epoch_1 = self.data.source_epoch;
|
||||
let source_epoch_2 = other.data.source_epoch;
|
||||
let target_epoch_1 = self.data.slot.epoch(spec.slots_per_epoch);
|
||||
let target_epoch_2 = other.data.slot.epoch(spec.slots_per_epoch);
|
||||
|
||||
(source_epoch_1 < source_epoch_2) & (target_epoch_2 < target_epoch_1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::chain_spec::ChainSpec;
|
||||
use crate::slot_epoch::{Epoch, Slot};
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
|
||||
#[test]
|
||||
pub fn test_is_double_vote_true() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(1, 1, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(1, 1, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_double_vote(&slashable_vote_second, &spec),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_double_vote_false() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(1, 1, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(2, 1, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_double_vote(&slashable_vote_second, &spec),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_true() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(2, 1, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(1, 2, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_surround_vote(&slashable_vote_second, &spec),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_true_realistic() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(4, 1, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(3, 2, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_surround_vote(&slashable_vote_second, &spec),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_false_source_epoch_fails() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(2, 2, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(1, 1, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_surround_vote(&slashable_vote_second, &spec),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_is_surround_vote_false_target_epoch_fails() {
|
||||
let spec = ChainSpec::foundation();
|
||||
let slashable_vote_first = create_slashable_attestation(1, 1, &spec);
|
||||
let slashable_vote_second = create_slashable_attestation(2, 2, &spec);
|
||||
|
||||
assert_eq!(
|
||||
slashable_vote_first.is_surround_vote(&slashable_vote_second, &spec),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
ssz_tests!(SlashableAttestation);
|
||||
cached_tree_hash_tests!(SlashableAttestation);
|
||||
|
||||
fn create_slashable_attestation(
|
||||
slot_factor: u64,
|
||||
source_epoch: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> SlashableAttestation {
|
||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||
let mut slashable_vote = SlashableAttestation::random_for_test(&mut rng);
|
||||
|
||||
slashable_vote.data.slot = Slot::new(slot_factor * spec.slots_per_epoch);
|
||||
slashable_vote.data.source_epoch = Epoch::new(source_epoch);
|
||||
slashable_vote
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ impl TestingAttestationBuilder {
|
||||
aggregation_bitfield,
|
||||
data: data_builder.build(),
|
||||
custody_bitfield,
|
||||
aggregate_signature: AggregateSignature::new(),
|
||||
signature: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
Self {
|
||||
@@ -77,13 +77,13 @@ impl TestingAttestationBuilder {
|
||||
.tree_hash_root();
|
||||
|
||||
let domain = spec.get_domain(
|
||||
self.attestation.data.slot.epoch(spec.slots_per_epoch),
|
||||
self.attestation.data.target_epoch,
|
||||
Domain::Attestation,
|
||||
fork,
|
||||
);
|
||||
|
||||
let signature = Signature::new(&message, domain, secret_keys[key_index]);
|
||||
self.attestation.aggregate_signature.add(&signature)
|
||||
self.attestation.signature.add(&signature)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,12 @@ impl TestingAttestationDataBuilder {
|
||||
state.current_justified_epoch
|
||||
};
|
||||
|
||||
let target_epoch = if is_previous_epoch {
|
||||
state.previous_epoch(spec)
|
||||
} else {
|
||||
state.current_epoch(spec)
|
||||
};
|
||||
|
||||
let target_root = if is_previous_epoch {
|
||||
*state
|
||||
.get_block_root(previous_epoch.start_slot(spec.slots_per_epoch))
|
||||
@@ -44,20 +50,17 @@ impl TestingAttestationDataBuilder {
|
||||
|
||||
let data = AttestationData {
|
||||
// LMD GHOST vote
|
||||
slot,
|
||||
beacon_block_root: *state.get_block_root(slot).unwrap(),
|
||||
|
||||
// FFG Vote
|
||||
source_epoch,
|
||||
source_root,
|
||||
target_epoch,
|
||||
target_root,
|
||||
|
||||
// Crosslink vote
|
||||
shard,
|
||||
previous_crosslink: Crosslink {
|
||||
epoch: slot.epoch(spec.slots_per_epoch),
|
||||
crosslink_data_root: spec.zero_hash,
|
||||
},
|
||||
previous_crosslink_root: spec.zero_hash,
|
||||
crosslink_data_root: spec.zero_hash,
|
||||
};
|
||||
|
||||
|
||||
@@ -21,23 +21,20 @@ impl TestingAttesterSlashingBuilder {
|
||||
where
|
||||
F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
|
||||
{
|
||||
let double_voted_slot = Slot::new(0);
|
||||
let shard = 0;
|
||||
let epoch = Epoch::new(0);
|
||||
let epoch_1 = Epoch::new(1);
|
||||
let epoch_2 = Epoch::new(2);
|
||||
let hash_1 = Hash256::from_low_u64_le(1);
|
||||
let hash_2 = Hash256::from_low_u64_le(2);
|
||||
|
||||
let data_1 = AttestationData {
|
||||
slot: double_voted_slot,
|
||||
beacon_block_root: hash_1,
|
||||
source_epoch: epoch,
|
||||
source_epoch: epoch_1,
|
||||
source_root: hash_1,
|
||||
target_epoch: epoch_2,
|
||||
target_root: hash_1,
|
||||
shard,
|
||||
previous_crosslink: Crosslink {
|
||||
epoch,
|
||||
crosslink_data_root: hash_1,
|
||||
},
|
||||
previous_crosslink_root: hash_1,
|
||||
crosslink_data_root: hash_1,
|
||||
};
|
||||
|
||||
@@ -46,21 +43,23 @@ impl TestingAttesterSlashingBuilder {
|
||||
..data_1.clone()
|
||||
};
|
||||
|
||||
let mut slashable_attestation_1 = SlashableAttestation {
|
||||
validator_indices: validator_indices.to_vec(),
|
||||
let mut attestation_1 = IndexedAttestation {
|
||||
custody_bit_0_indices: validator_indices.to_vec(),
|
||||
custody_bit_1_indices: vec![],
|
||||
data: data_1,
|
||||
custody_bitfield: Bitfield::new(),
|
||||
aggregate_signature: AggregateSignature::new(),
|
||||
signature: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
let mut slashable_attestation_2 = SlashableAttestation {
|
||||
validator_indices: validator_indices.to_vec(),
|
||||
let mut attestation_2 = IndexedAttestation {
|
||||
custody_bit_0_indices: validator_indices.to_vec(),
|
||||
custody_bit_1_indices: vec![],
|
||||
data: data_2,
|
||||
custody_bitfield: Bitfield::new(),
|
||||
aggregate_signature: AggregateSignature::new(),
|
||||
signature: AggregateSignature::new(),
|
||||
};
|
||||
|
||||
let add_signatures = |attestation: &mut SlashableAttestation| {
|
||||
let add_signatures = |attestation: &mut IndexedAttestation| {
|
||||
// All validators sign with a `false` custody bit.
|
||||
let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
|
||||
data: attestation.data.clone(),
|
||||
@@ -70,17 +69,18 @@ impl TestingAttesterSlashingBuilder {
|
||||
|
||||
for (i, validator_index) in validator_indices.iter().enumerate() {
|
||||
attestation.custody_bitfield.set(i, false);
|
||||
let signature = signer(*validator_index, &message[..], epoch, Domain::Attestation);
|
||||
attestation.aggregate_signature.add(&signature);
|
||||
let signature =
|
||||
signer(*validator_index, &message[..], epoch_2, Domain::Attestation);
|
||||
attestation.signature.add(&signature);
|
||||
}
|
||||
};
|
||||
|
||||
add_signatures(&mut slashable_attestation_1);
|
||||
add_signatures(&mut slashable_attestation_2);
|
||||
add_signatures(&mut attestation_1);
|
||||
add_signatures(&mut attestation_2);
|
||||
|
||||
AttesterSlashing {
|
||||
slashable_attestation_1,
|
||||
slashable_attestation_2,
|
||||
attestation_1,
|
||||
attestation_2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ impl TestingBeaconBlockBuilder {
|
||||
pub fn sign(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) {
|
||||
let message = self.block.signed_root();
|
||||
let epoch = self.block.slot.epoch(spec.slots_per_epoch);
|
||||
let domain = spec.get_domain(epoch, Domain::BeaconBlock, fork);
|
||||
let domain = spec.get_domain(epoch, Domain::BeaconProposer, fork);
|
||||
self.block.signature = Signature::new(&message, domain, sk);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,6 +95,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
/// Creates the builder from an existing set of keypairs.
|
||||
pub fn from_keypairs(keypairs: Vec<Keypair>, spec: &ChainSpec) -> Self {
|
||||
let validator_count = keypairs.len();
|
||||
let starting_balance = 32_000_000_000;
|
||||
|
||||
debug!(
|
||||
"Building {} Validator objects from keypairs...",
|
||||
@@ -112,11 +113,12 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
pubkey: keypair.pk.clone(),
|
||||
withdrawal_credentials,
|
||||
// All validators start active.
|
||||
activation_eligibility_epoch: spec.genesis_epoch,
|
||||
activation_epoch: spec.genesis_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
initiated_exit: false,
|
||||
slashed: false,
|
||||
effective_balance: starting_balance,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@@ -137,16 +139,17 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
genesis_time,
|
||||
Eth1Data {
|
||||
deposit_root: Hash256::zero(),
|
||||
deposit_count: 0,
|
||||
block_hash: Hash256::zero(),
|
||||
},
|
||||
spec,
|
||||
);
|
||||
|
||||
let balances = vec![32_000_000_000; validator_count];
|
||||
let balances = vec![starting_balance; validator_count];
|
||||
|
||||
debug!("Importing {} existing validators...", validator_count);
|
||||
state.validator_registry = validators;
|
||||
state.validator_balances = balances;
|
||||
state.balances = balances;
|
||||
|
||||
debug!("BeaconState initialized.");
|
||||
|
||||
@@ -192,18 +195,13 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
|
||||
state.slot = slot;
|
||||
|
||||
state.previous_shuffling_epoch = epoch - 1;
|
||||
state.current_shuffling_epoch = epoch;
|
||||
|
||||
state.previous_shuffling_seed = Hash256::from_low_u64_le(0);
|
||||
state.current_shuffling_seed = Hash256::from_low_u64_le(1);
|
||||
// FIXME(sproul): update latest_start_shard?
|
||||
|
||||
state.previous_justified_epoch = epoch - 3;
|
||||
state.current_justified_epoch = epoch - 2;
|
||||
state.justification_bitfield = u64::max_value();
|
||||
|
||||
state.finalized_epoch = epoch - 3;
|
||||
state.validator_registry_update_epoch = epoch - 3;
|
||||
}
|
||||
|
||||
/// Creates a full set of attestations for the `BeaconState`. Each attestation has full
|
||||
@@ -248,7 +246,7 @@ impl<T: EthSpec> TestingBeaconStateBuilder<T> {
|
||||
builder.add_committee_participation(signers);
|
||||
let attestation = builder.build();
|
||||
|
||||
if attestation.data.slot.epoch(spec.slots_per_epoch) < state.current_epoch(spec) {
|
||||
if attestation.data.target_epoch < state.current_epoch(spec) {
|
||||
state.previous_epoch_attestations.push(attestation)
|
||||
} else {
|
||||
state.current_epoch_attestations.push(attestation)
|
||||
|
||||
@@ -14,14 +14,11 @@ impl TestingDepositBuilder {
|
||||
let deposit = Deposit {
|
||||
proof: vec![].into(),
|
||||
index: 0,
|
||||
deposit_data: DepositData {
|
||||
data: DepositData {
|
||||
pubkey,
|
||||
withdrawal_credentials: Hash256::zero(),
|
||||
amount,
|
||||
timestamp: 1,
|
||||
deposit_input: DepositInput {
|
||||
pubkey,
|
||||
withdrawal_credentials: Hash256::zero(),
|
||||
proof_of_possession: Signature::empty_signature(),
|
||||
},
|
||||
signature: Signature::empty_signature(),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,17 +40,13 @@ impl TestingDepositBuilder {
|
||||
&get_withdrawal_credentials(&keypair.pk, spec.bls_withdrawal_prefix_byte)[..],
|
||||
);
|
||||
|
||||
self.deposit.deposit_data.deposit_input.pubkey = keypair.pk.clone();
|
||||
self.deposit
|
||||
.deposit_data
|
||||
.deposit_input
|
||||
.withdrawal_credentials = withdrawal_credentials;
|
||||
self.deposit.data.pubkey = keypair.pk.clone();
|
||||
self.deposit.data.withdrawal_credentials = withdrawal_credentials;
|
||||
|
||||
self.deposit.deposit_data.deposit_input.proof_of_possession = self
|
||||
.deposit
|
||||
.deposit_data
|
||||
.deposit_input
|
||||
.create_proof_of_possession(&keypair.sk, epoch, fork, spec);
|
||||
self.deposit.data.signature =
|
||||
self.deposit
|
||||
.data
|
||||
.create_signature(&keypair.sk, epoch, fork, spec);
|
||||
}
|
||||
|
||||
/// Builds the deposit, consuming the builder.
|
||||
|
||||
@@ -11,8 +11,7 @@ pub struct TestingPendingAttestationBuilder {
|
||||
impl TestingPendingAttestationBuilder {
|
||||
/// Create a new valid* `PendingAttestation` for the given parameters.
|
||||
///
|
||||
/// The `inclusion_slot` will be set to be the earliest possible slot the `Attestation` could
|
||||
/// have been included (`slot + MIN_ATTESTATION_INCLUSION_DELAY`).
|
||||
/// The `inclusion_delay` will be set to `MIN_ATTESTATION_INCLUSION_DELAY`.
|
||||
///
|
||||
/// * The aggregation and custody bitfields will all be empty, they need to be set with
|
||||
/// `Self::add_committee_participation`.
|
||||
@@ -27,8 +26,9 @@ impl TestingPendingAttestationBuilder {
|
||||
let pending_attestation = PendingAttestation {
|
||||
aggregation_bitfield: Bitfield::new(),
|
||||
data: data_builder.build(),
|
||||
custody_bitfield: Bitfield::new(),
|
||||
inclusion_slot: slot + spec.min_attestation_inclusion_delay,
|
||||
inclusion_delay: spec.min_attestation_inclusion_delay,
|
||||
// FIXME(sproul)
|
||||
proposer_index: 0,
|
||||
};
|
||||
|
||||
Self {
|
||||
@@ -42,15 +42,12 @@ impl TestingPendingAttestationBuilder {
|
||||
/// `signers` is true.
|
||||
pub fn add_committee_participation(&mut self, signers: Vec<bool>) {
|
||||
let mut aggregation_bitfield = Bitfield::new();
|
||||
let mut custody_bitfield = Bitfield::new();
|
||||
|
||||
for (i, signed) in signers.iter().enumerate() {
|
||||
aggregation_bitfield.set(i, *signed);
|
||||
custody_bitfield.set(i, false); // Fixed to `false` for phase 0.
|
||||
}
|
||||
|
||||
self.pending_attestation.aggregation_bitfield = aggregation_bitfield;
|
||||
self.pending_attestation.custody_bitfield = custody_bitfield;
|
||||
}
|
||||
|
||||
/// Returns the `PendingAttestation`, consuming the builder.
|
||||
|
||||
@@ -41,13 +41,13 @@ impl TestingProposerSlashingBuilder {
|
||||
header_1.signature = {
|
||||
let message = header_1.signed_root();
|
||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconBlock)
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
|
||||
header_2.signature = {
|
||||
let message = header_2.signed_root();
|
||||
let epoch = slot.epoch(spec.slots_per_epoch);
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconBlock)
|
||||
signer(proposer_index, &message[..], epoch, Domain::BeaconProposer)
|
||||
};
|
||||
|
||||
ProposerSlashing {
|
||||
|
||||
@@ -25,7 +25,7 @@ impl TestingVoluntaryExitBuilder {
|
||||
/// The signing secret key must match that of the exiting validator.
|
||||
pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) {
|
||||
let message = self.exit.signed_root();
|
||||
let domain = spec.get_domain(self.exit.epoch, Domain::Exit, fork);
|
||||
let domain = spec.get_domain(self.exit.epoch, Domain::VoluntaryExit, fork);
|
||||
|
||||
self.exit.signature = Signature::new(&message, domain, secret_key);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Information about a `BeaconChain` validator.
|
||||
///
|
||||
/// Spec v0.5.1
|
||||
/// Spec v0.6.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@@ -23,11 +23,12 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
pub struct Validator {
|
||||
pub pubkey: PublicKey,
|
||||
pub withdrawal_credentials: Hash256,
|
||||
pub activation_eligibility_epoch: Epoch,
|
||||
pub activation_epoch: Epoch,
|
||||
pub exit_epoch: Epoch,
|
||||
pub withdrawable_epoch: Epoch,
|
||||
pub initiated_exit: bool,
|
||||
pub slashed: bool,
|
||||
pub effective_balance: u64,
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
@@ -36,6 +37,11 @@ impl Validator {
|
||||
self.activation_epoch <= epoch && epoch < self.exit_epoch
|
||||
}
|
||||
|
||||
/// Returns `true` if the validator is slashable at some epoch.
|
||||
pub fn is_slashable_at(&self, epoch: Epoch) -> bool {
|
||||
!self.slashed && self.activation_epoch <= epoch && epoch < self.withdrawable_epoch
|
||||
}
|
||||
|
||||
/// Returns `true` if the validator is considered exited at some epoch.
|
||||
pub fn is_exited_at(&self, epoch: Epoch) -> bool {
|
||||
self.exit_epoch <= epoch
|
||||
@@ -53,11 +59,12 @@ impl Default for Validator {
|
||||
Self {
|
||||
pubkey: PublicKey::default(),
|
||||
withdrawal_credentials: Hash256::default(),
|
||||
activation_eligibility_epoch: Epoch::from(std::u64::MAX),
|
||||
activation_epoch: Epoch::from(std::u64::MAX),
|
||||
exit_epoch: Epoch::from(std::u64::MAX),
|
||||
withdrawable_epoch: Epoch::from(std::u64::MAX),
|
||||
initiated_exit: false,
|
||||
slashed: false,
|
||||
effective_balance: std::u64::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +82,6 @@ mod tests {
|
||||
assert_eq!(v.is_active_at(epoch), false);
|
||||
assert_eq!(v.is_exited_at(epoch), false);
|
||||
assert_eq!(v.is_withdrawable_at(epoch), false);
|
||||
assert_eq!(v.initiated_exit, false);
|
||||
assert_eq!(v.slashed, false);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user