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:
parent
177df12149
commit
a236003a7b
@ -5,14 +5,11 @@ members = [
|
||||
"eth2/state_processing",
|
||||
"eth2/types",
|
||||
"eth2/utils/bls",
|
||||
"eth2/utils/boolean-bitfield",
|
||||
"eth2/utils/cached_tree_hash",
|
||||
"eth2/utils/compare_fields",
|
||||
"eth2/utils/compare_fields_derive",
|
||||
"eth2/utils/eth2_config",
|
||||
"eth2/utils/fixed_len_vec",
|
||||
"eth2/utils/hashing",
|
||||
"eth2/utils/honey-badger-split",
|
||||
"eth2/utils/merkle_proof",
|
||||
"eth2/utils/int_to_bytes",
|
||||
"eth2/utils/serde_hex",
|
||||
|
11
README.md
11
README.md
@ -34,16 +34,15 @@ user-facing functionality.
|
||||
|
||||
Current development overview:
|
||||
|
||||
- Specification `v0.6.3` implemented, optimized and passing test vectors.
|
||||
- Rust-native libp2p integrated, with Gossipsub.
|
||||
- Discv5 (P2P discovery mechanism) integration started.
|
||||
- Specification `v0.8.1` implemented, optimized and passing test vectors.
|
||||
- Rust-native libp2p with Gossipsub and Discv5.
|
||||
- Metrics via Prometheus.
|
||||
- Basic gRPC API, soon to be replaced with RESTful HTTP/JSON.
|
||||
|
||||
### Roadmap
|
||||
|
||||
- **July 2019**: `lighthouse-0.0.1` release: A stable testnet for developers with a useful
|
||||
HTTP API.
|
||||
- **Early-September 2019**: `lighthouse-0.0.1` release: A stable testnet for
|
||||
developers with a useful HTTP API.
|
||||
- **September 2019**: Inter-operability with other Ethereum 2.0 clients.
|
||||
- **October 2019**: Public, multi-client testnet with user-facing functionality.
|
||||
- **January 2020**: Production Beacon Chain testnet.
|
||||
@ -153,6 +152,8 @@ If you'd like some background on Sigma Prime, please see the [Lighthouse Update
|
||||
- [`protos/`](protos/): protobuf/gRPC definitions that are common across the Lighthouse project.
|
||||
- [`validator_client/`](validator_client/): the "Validator Client" binary and crates exclusively
|
||||
associated with it.
|
||||
- [`tests/`](tests/): code specific to testing, most notably contains the
|
||||
Ethereum Foundation test vectors.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -83,7 +83,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
default_dir.push(DEFAULT_DATA_DIR);
|
||||
PathBuf::from(default_dir)
|
||||
default_dir
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -94,7 +94,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
store: Arc<T::Store>,
|
||||
slot_clock: T::SlotClock,
|
||||
mut genesis_state: BeaconState<T::EthSpec>,
|
||||
genesis_block: BeaconBlock,
|
||||
genesis_block: BeaconBlock<T::EthSpec>,
|
||||
spec: ChainSpec,
|
||||
log: Logger,
|
||||
) -> Result<Self, Error> {
|
||||
@ -108,7 +108,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// Also store the genesis block under the `ZERO_HASH` key.
|
||||
let genesis_block_root = genesis_block.block_header().canonical_root();
|
||||
store.put(&spec.zero_hash, &genesis_block)?;
|
||||
store.put(&Hash256::zero(), &genesis_block)?;
|
||||
|
||||
let canonical_head = RwLock::new(CheckPoint::new(
|
||||
genesis_block.clone(),
|
||||
@ -150,7 +150,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
spec.seconds_per_slot,
|
||||
);
|
||||
|
||||
let last_finalized_root = p.canonical_head.beacon_state.finalized_root;
|
||||
let last_finalized_root = p.canonical_head.beacon_state.finalized_checkpoint.root;
|
||||
let last_finalized_block = &p.canonical_head.beacon_block;
|
||||
|
||||
let op_pool = p.op_pool.into_operation_pool(&p.state, &spec);
|
||||
@ -187,8 +187,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Returns the beacon block body for each beacon block root in `roots`.
|
||||
///
|
||||
/// Fails if any root in `roots` does not have a corresponding block.
|
||||
pub fn get_block_bodies(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockBody>, Error> {
|
||||
let bodies: Result<Vec<BeaconBlockBody>, _> = roots
|
||||
pub fn get_block_bodies(
|
||||
&self,
|
||||
roots: &[Hash256],
|
||||
) -> Result<Vec<BeaconBlockBody<T::EthSpec>>, Error> {
|
||||
let bodies: Result<Vec<_>, _> = roots
|
||||
.iter()
|
||||
.map(|root| match self.get_block(root)? {
|
||||
Some(block) => Ok(block.body),
|
||||
@ -259,7 +262,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// ## Errors
|
||||
///
|
||||
/// May return a database error.
|
||||
pub fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock>, Error> {
|
||||
pub fn get_block(
|
||||
&self,
|
||||
block_root: &Hash256,
|
||||
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||
Ok(self.store.get(block_root)?)
|
||||
}
|
||||
|
||||
@ -321,15 +327,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Returns the validator index (if any) for the given public key.
|
||||
///
|
||||
/// Information is retrieved from the present `beacon_state.validator_registry`.
|
||||
/// Information is retrieved from the present `beacon_state.validators`.
|
||||
pub fn validator_index(&self, pubkey: &PublicKey) -> Option<usize> {
|
||||
for (i, validator) in self
|
||||
.head()
|
||||
.beacon_state
|
||||
.validator_registry
|
||||
.iter()
|
||||
.enumerate()
|
||||
{
|
||||
for (i, validator) in self.head().beacon_state.validators.iter().enumerate() {
|
||||
if validator.pubkey == *pubkey {
|
||||
return Some(i);
|
||||
}
|
||||
@ -469,9 +469,22 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
} else {
|
||||
*state.get_block_root(current_epoch_start_slot)?
|
||||
};
|
||||
let target = Checkpoint {
|
||||
epoch: state.current_epoch(),
|
||||
root: target_root,
|
||||
};
|
||||
|
||||
let previous_crosslink_root =
|
||||
Hash256::from_slice(&state.get_current_crosslink(shard)?.tree_hash_root());
|
||||
let parent_crosslink = state.get_current_crosslink(shard)?;
|
||||
let crosslink = Crosslink {
|
||||
shard,
|
||||
parent_root: Hash256::from_slice(&parent_crosslink.tree_hash_root()),
|
||||
start_epoch: parent_crosslink.end_epoch,
|
||||
end_epoch: std::cmp::min(
|
||||
target.epoch,
|
||||
parent_crosslink.end_epoch + self.spec.max_epochs_per_crosslink,
|
||||
),
|
||||
data_root: Hash256::zero(),
|
||||
};
|
||||
|
||||
// Collect some metrics.
|
||||
self.metrics.attestation_production_successes.inc();
|
||||
@ -479,13 +492,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
Ok(AttestationData {
|
||||
beacon_block_root: head_block_root,
|
||||
source_epoch: state.current_justified_epoch,
|
||||
source_root: state.current_justified_root,
|
||||
target_epoch: state.current_epoch(),
|
||||
target_root,
|
||||
shard,
|
||||
previous_crosslink_root,
|
||||
crosslink_data_root: Hash256::zero(),
|
||||
source: state.current_justified_checkpoint.clone(),
|
||||
target,
|
||||
crosslink,
|
||||
})
|
||||
}
|
||||
|
||||
@ -495,7 +504,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// if possible.
|
||||
pub fn process_attestation(
|
||||
&self,
|
||||
attestation: Attestation,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
) -> Result<(), AttestationValidationError> {
|
||||
self.metrics.attestation_processing_requests.inc();
|
||||
let timer = self.metrics.attestation_processing_times.start_timer();
|
||||
@ -527,9 +536,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Accept some deposit and queue it for inclusion in an appropriate block.
|
||||
pub fn process_deposit(
|
||||
&self,
|
||||
index: u64,
|
||||
deposit: Deposit,
|
||||
) -> Result<DepositInsertStatus, DepositValidationError> {
|
||||
self.op_pool.insert_deposit(deposit)
|
||||
self.op_pool.insert_deposit(index, deposit)
|
||||
}
|
||||
|
||||
/// Accept some exit and queue it for inclusion in an appropriate block.
|
||||
@ -556,7 +566,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Accept some attester slashing and queue it for inclusion in an appropriate block.
|
||||
pub fn process_attester_slashing(
|
||||
&self,
|
||||
attester_slashing: AttesterSlashing,
|
||||
attester_slashing: AttesterSlashing<T::EthSpec>,
|
||||
) -> Result<(), AttesterSlashingValidationError> {
|
||||
self.op_pool
|
||||
.insert_attester_slashing(attester_slashing, &*self.state.read(), &self.spec)
|
||||
@ -565,14 +575,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Accept some block and attempt to add it to block DAG.
|
||||
///
|
||||
/// Will accept blocks from prior slots, however it will reject any block from a future slot.
|
||||
pub fn process_block(&self, block: BeaconBlock) -> Result<BlockProcessingOutcome, Error> {
|
||||
pub fn process_block(
|
||||
&self,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
) -> Result<BlockProcessingOutcome, Error> {
|
||||
self.metrics.block_processing_requests.inc();
|
||||
let timer = self.metrics.block_processing_times.start_timer();
|
||||
|
||||
let finalized_slot = self
|
||||
.state
|
||||
.read()
|
||||
.finalized_epoch
|
||||
.finalized_checkpoint
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if block.slot <= finalized_slot {
|
||||
@ -600,18 +614,17 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
});
|
||||
}
|
||||
|
||||
if self.store.exists::<BeaconBlock>(&block_root)? {
|
||||
if self.store.exists::<BeaconBlock<T::EthSpec>>(&block_root)? {
|
||||
return Ok(BlockProcessingOutcome::BlockIsAlreadyKnown);
|
||||
}
|
||||
|
||||
// Load the blocks parent block from the database, returning invalid if that block is not
|
||||
// found.
|
||||
let parent_block_root = block.previous_block_root;
|
||||
let parent_block: BeaconBlock = match self.store.get(&parent_block_root)? {
|
||||
Some(previous_block_root) => previous_block_root,
|
||||
let parent_block: BeaconBlock<T::EthSpec> = match self.store.get(&block.parent_root)? {
|
||||
Some(block) => block,
|
||||
None => {
|
||||
return Ok(BlockProcessingOutcome::ParentUnknown {
|
||||
parent: parent_block_root,
|
||||
parent: block.parent_root,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -691,7 +704,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn produce_block(
|
||||
&self,
|
||||
randao_reveal: Signature,
|
||||
) -> Result<(BeaconBlock, BeaconState<T::EthSpec>), BlockProductionError> {
|
||||
) -> Result<(BeaconBlock<T::EthSpec>, BeaconState<T::EthSpec>), BlockProductionError> {
|
||||
let state = self.state.read().clone();
|
||||
let slot = self
|
||||
.read_slot_clock()
|
||||
@ -713,7 +726,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
mut state: BeaconState<T::EthSpec>,
|
||||
produce_at_slot: Slot,
|
||||
randao_reveal: Signature,
|
||||
) -> Result<(BeaconBlock, BeaconState<T::EthSpec>), BlockProductionError> {
|
||||
) -> Result<(BeaconBlock<T::EthSpec>, BeaconState<T::EthSpec>), BlockProductionError> {
|
||||
self.metrics.block_production_requests.inc();
|
||||
let timer = self.metrics.block_production_times.start_timer();
|
||||
|
||||
@ -724,7 +737,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
|
||||
let previous_block_root = if state.slot > 0 {
|
||||
let parent_root = if state.slot > 0 {
|
||||
*state
|
||||
.get_block_root(state.slot - 1)
|
||||
.map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?
|
||||
@ -740,7 +753,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let mut block = BeaconBlock {
|
||||
slot: state.slot,
|
||||
previous_block_root,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(), // Updated after the state is calculated.
|
||||
signature: Signature::empty_signature(), // To be completed by a validator.
|
||||
body: BeaconBlockBody {
|
||||
@ -752,12 +765,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
block_hash: Hash256::zero(),
|
||||
},
|
||||
graffiti,
|
||||
proposer_slashings,
|
||||
attester_slashings,
|
||||
attestations: self.op_pool.get_attestations(&state, &self.spec),
|
||||
deposits: self.op_pool.get_deposits(&state, &self.spec),
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec),
|
||||
transfers: self.op_pool.get_transfers(&state, &self.spec),
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings.into(),
|
||||
attestations: self.op_pool.get_attestations(&state, &self.spec).into(),
|
||||
deposits: self.op_pool.get_deposits(&state).into(),
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(),
|
||||
transfers: self.op_pool.get_transfers(&state, &self.spec).into(),
|
||||
},
|
||||
};
|
||||
|
||||
@ -790,7 +803,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
if beacon_block_root != self.head().beacon_block_root {
|
||||
self.metrics.fork_choice_changed_head.inc();
|
||||
|
||||
let beacon_block: BeaconBlock = self
|
||||
let beacon_block: BeaconBlock<T::EthSpec> = self
|
||||
.store
|
||||
.get(&beacon_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?;
|
||||
@ -805,7 +818,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let new_slot = beacon_block.slot;
|
||||
|
||||
// If we switched to a new chain (instead of building atop the present chain).
|
||||
if self.head().beacon_block_root != beacon_block.previous_block_root {
|
||||
if self.head().beacon_block_root != beacon_block.parent_root {
|
||||
self.metrics.fork_choice_reorg_count.inc();
|
||||
warn!(
|
||||
self.log,
|
||||
@ -817,16 +830,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
info!(
|
||||
self.log,
|
||||
"new head block";
|
||||
"justified_root" => format!("{}", beacon_state.current_justified_root),
|
||||
"finalized_root" => format!("{}", beacon_state.finalized_root),
|
||||
"justified_root" => format!("{}", beacon_state.current_justified_checkpoint.root),
|
||||
"finalized_root" => format!("{}", beacon_state.finalized_checkpoint.root),
|
||||
"root" => format!("{}", beacon_block_root),
|
||||
"slot" => new_slot,
|
||||
);
|
||||
};
|
||||
|
||||
let old_finalized_epoch = self.head().beacon_state.finalized_epoch;
|
||||
let new_finalized_epoch = beacon_state.finalized_epoch;
|
||||
let finalized_root = beacon_state.finalized_root;
|
||||
let old_finalized_epoch = self.head().beacon_state.finalized_checkpoint.epoch;
|
||||
let new_finalized_epoch = beacon_state.finalized_checkpoint.epoch;
|
||||
let finalized_root = beacon_state.finalized_checkpoint.root;
|
||||
|
||||
// Never revert back past a finalized epoch.
|
||||
if new_finalized_epoch < old_finalized_epoch {
|
||||
@ -836,7 +849,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
})
|
||||
} else {
|
||||
self.update_canonical_head(CheckPoint {
|
||||
beacon_block: beacon_block,
|
||||
beacon_block,
|
||||
beacon_block_root,
|
||||
beacon_state,
|
||||
beacon_state_root,
|
||||
@ -894,7 +907,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
) -> Result<(), Error> {
|
||||
let finalized_block = self
|
||||
.store
|
||||
.get::<BeaconBlock>(&finalized_block_root)?
|
||||
.get::<BeaconBlock<T::EthSpec>>(&finalized_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?;
|
||||
|
||||
let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
@ -914,7 +927,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Returns `true` if the given block root has not been processed.
|
||||
pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result<bool, Error> {
|
||||
Ok(!self.store.exists::<BeaconBlock>(beacon_block_root)?)
|
||||
Ok(!self
|
||||
.store
|
||||
.exists::<BeaconBlock<T::EthSpec>>(beacon_block_root)?)
|
||||
}
|
||||
|
||||
/// Dumps the entire canonical chain, from the head to genesis to a vector for analysis.
|
||||
@ -934,13 +949,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
dump.push(last_slot.clone());
|
||||
|
||||
loop {
|
||||
let beacon_block_root = last_slot.beacon_block.previous_block_root;
|
||||
let beacon_block_root = last_slot.beacon_block.parent_root;
|
||||
|
||||
if beacon_block_root == self.spec.zero_hash {
|
||||
if beacon_block_root == Hash256::zero() {
|
||||
break; // Genesis has been reached.
|
||||
}
|
||||
|
||||
let beacon_block: BeaconBlock =
|
||||
let beacon_block: BeaconBlock<T::EthSpec> =
|
||||
self.store.get(&beacon_block_root)?.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing block {}", beacon_block_root))
|
||||
})?;
|
||||
|
@ -6,7 +6,7 @@ use types::{BeaconBlock, BeaconState, EthSpec, Hash256};
|
||||
/// head, justified head and finalized head.
|
||||
#[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct CheckPoint<E: EthSpec> {
|
||||
pub beacon_block: BeaconBlock,
|
||||
pub beacon_block: BeaconBlock<E>,
|
||||
pub beacon_block_root: Hash256,
|
||||
pub beacon_state: BeaconState<E>,
|
||||
pub beacon_state_root: Hash256,
|
||||
@ -15,7 +15,7 @@ pub struct CheckPoint<E: EthSpec> {
|
||||
impl<E: EthSpec> CheckPoint<E> {
|
||||
/// Create a new checkpoint.
|
||||
pub fn new(
|
||||
beacon_block: BeaconBlock,
|
||||
beacon_block: BeaconBlock<E>,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState<E>,
|
||||
beacon_state_root: Hash256,
|
||||
@ -31,7 +31,7 @@ impl<E: EthSpec> CheckPoint<E> {
|
||||
/// Update all fields of the checkpoint.
|
||||
pub fn update(
|
||||
&mut self,
|
||||
beacon_block: BeaconBlock,
|
||||
beacon_block: BeaconBlock<E>,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState<E>,
|
||||
beacon_state_root: Hash256,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{BeaconChain, BeaconChainTypes};
|
||||
use lmd_ghost::LmdGhost;
|
||||
use state_processing::common::get_attesting_indices_unsorted;
|
||||
use state_processing::common::get_attesting_indices;
|
||||
use std::sync::Arc;
|
||||
use store::{Error as StoreError, Store};
|
||||
use types::{Attestation, BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256};
|
||||
@ -33,7 +33,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
/// block.
|
||||
pub fn new(
|
||||
store: Arc<T::Store>,
|
||||
genesis_block: &BeaconBlock,
|
||||
genesis_block: &BeaconBlock<T::EthSpec>,
|
||||
genesis_block_root: Hash256,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -55,18 +55,21 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
let state = chain.current_state();
|
||||
|
||||
let (block_root, block_slot) =
|
||||
if state.current_epoch() + 1 > state.current_justified_epoch {
|
||||
if state.current_epoch() + 1 > state.current_justified_checkpoint.epoch {
|
||||
(
|
||||
state.current_justified_root,
|
||||
start_slot(state.current_justified_epoch),
|
||||
state.current_justified_checkpoint.root,
|
||||
start_slot(state.current_justified_checkpoint.epoch),
|
||||
)
|
||||
} else {
|
||||
(state.finalized_root, start_slot(state.finalized_epoch))
|
||||
(
|
||||
state.finalized_checkpoint.root,
|
||||
start_slot(state.finalized_checkpoint.epoch),
|
||||
)
|
||||
};
|
||||
|
||||
let block = chain
|
||||
.store
|
||||
.get::<BeaconBlock>(&block_root)?
|
||||
.get::<BeaconBlock<T::EthSpec>>(&block_root)?
|
||||
.ok_or_else(|| Error::MissingBlock(block_root))?;
|
||||
|
||||
// Resolve the `0x00.. 00` alias back to genesis
|
||||
@ -87,7 +90,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
// A function that returns the weight for some validator index.
|
||||
let weight = |validator_index: usize| -> Option<u64> {
|
||||
start_state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(validator_index)
|
||||
.map(|v| v.effective_balance)
|
||||
};
|
||||
@ -104,7 +107,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
pub fn process_block(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
block: &BeaconBlock,
|
||||
block: &BeaconBlock<T::EthSpec>,
|
||||
block_root: Hash256,
|
||||
) -> Result<()> {
|
||||
// Note: we never count the block as a latest message, only attestations.
|
||||
@ -125,7 +128,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
fn process_attestation_from_block(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
attestation: &Attestation,
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
) -> Result<()> {
|
||||
let block_hash = attestation.data.beacon_block_root;
|
||||
|
||||
@ -147,16 +150,13 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
if block_hash != Hash256::zero()
|
||||
&& self
|
||||
.store
|
||||
.exists::<BeaconBlock>(&block_hash)
|
||||
.exists::<BeaconBlock<T::EthSpec>>(&block_hash)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let validator_indices = get_attesting_indices_unsorted(
|
||||
state,
|
||||
&attestation.data,
|
||||
&attestation.aggregation_bitfield,
|
||||
)?;
|
||||
let validator_indices =
|
||||
get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;
|
||||
|
||||
let block_slot = state.get_attestation_slot(&attestation.data)?;
|
||||
let block_slot = state.get_attestation_data_slot(&attestation.data)?;
|
||||
|
||||
for validator_index in validator_indices {
|
||||
self.backend
|
||||
@ -173,7 +173,7 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
/// `finalized_block_root` must be the root of `finalized_block`.
|
||||
pub fn process_finalization(
|
||||
&self,
|
||||
finalized_block: &BeaconBlock,
|
||||
finalized_block: &BeaconBlock<T::EthSpec>,
|
||||
finalized_block_root: Hash256,
|
||||
) -> Result<()> {
|
||||
self.backend
|
||||
|
@ -11,7 +11,7 @@ pub const BEACON_CHAIN_DB_KEY: &str = "PERSISTEDBEACONCHAINPERSISTEDBEA";
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct PersistedBeaconChain<T: BeaconChainTypes> {
|
||||
pub canonical_head: CheckPoint<T::EthSpec>,
|
||||
pub op_pool: PersistedOperationPool,
|
||||
pub op_pool: PersistedOperationPool<T::EthSpec>,
|
||||
pub genesis_block_root: Hash256,
|
||||
pub state: BeaconState<T::EthSpec>,
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use store::Store;
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation,
|
||||
AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Bitfield, ChainSpec, Domain, EthSpec,
|
||||
AttestationDataAndCustodyBit, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec,
|
||||
Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot,
|
||||
};
|
||||
|
||||
@ -216,7 +216,7 @@ where
|
||||
mut state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
block_strategy: BlockStrategy,
|
||||
) -> (BeaconBlock, BeaconState<E>) {
|
||||
) -> (BeaconBlock<E>, BeaconState<E>) {
|
||||
if slot < state.slot {
|
||||
panic!("produce slot cannot be prior to the state slot");
|
||||
}
|
||||
@ -302,12 +302,9 @@ where
|
||||
)
|
||||
.expect("should produce attestation data");
|
||||
|
||||
let mut aggregation_bitfield = Bitfield::new();
|
||||
aggregation_bitfield.set(i, true);
|
||||
aggregation_bitfield.set(committee_size, false);
|
||||
|
||||
let mut custody_bitfield = Bitfield::new();
|
||||
custody_bitfield.set(committee_size, false);
|
||||
let mut aggregation_bits = BitList::with_capacity(committee_size).unwrap();
|
||||
aggregation_bits.set(i, true).unwrap();
|
||||
let custody_bits = BitList::with_capacity(committee_size).unwrap();
|
||||
|
||||
let signature = {
|
||||
let message = AttestationDataAndCustodyBit {
|
||||
@ -317,7 +314,7 @@ where
|
||||
.tree_hash_root();
|
||||
|
||||
let domain =
|
||||
spec.get_domain(data.target_epoch, Domain::Attestation, fork);
|
||||
spec.get_domain(data.target.epoch, Domain::Attestation, fork);
|
||||
|
||||
let mut agg_sig = AggregateSignature::new();
|
||||
agg_sig.add(&Signature::new(
|
||||
@ -330,9 +327,9 @@ where
|
||||
};
|
||||
|
||||
let attestation = Attestation {
|
||||
aggregation_bitfield,
|
||||
aggregation_bits,
|
||||
data,
|
||||
custody_bitfield,
|
||||
custody_bits,
|
||||
signature,
|
||||
};
|
||||
|
||||
@ -376,9 +373,9 @@ where
|
||||
let faulty_head = self.extend_chain(
|
||||
faulty_fork_blocks,
|
||||
BlockStrategy::ForkCanonicalChainAt {
|
||||
previous_slot: Slot::from(initial_head_slot),
|
||||
previous_slot: initial_head_slot,
|
||||
// `initial_head_slot + 2` means one slot is skipped.
|
||||
first_slot: Slot::from(initial_head_slot + 2),
|
||||
first_slot: initial_head_slot + 2,
|
||||
},
|
||||
AttestationStrategy::SomeValidators(faulty_validators.to_vec()),
|
||||
);
|
||||
|
@ -93,12 +93,12 @@ fn finalizes_with_full_participation() {
|
||||
"head should be at the expected epoch"
|
||||
);
|
||||
assert_eq!(
|
||||
state.current_justified_epoch,
|
||||
state.current_justified_checkpoint.epoch,
|
||||
state.current_epoch() - 1,
|
||||
"the head should be justified one behind the current epoch"
|
||||
);
|
||||
assert_eq!(
|
||||
state.finalized_epoch,
|
||||
state.finalized_checkpoint.epoch,
|
||||
state.current_epoch() - 2,
|
||||
"the head should be finalized two behind the current epoch"
|
||||
);
|
||||
@ -136,12 +136,12 @@ fn finalizes_with_two_thirds_participation() {
|
||||
// included in blocks during that epoch.
|
||||
|
||||
assert_eq!(
|
||||
state.current_justified_epoch,
|
||||
state.current_justified_checkpoint.epoch,
|
||||
state.current_epoch() - 2,
|
||||
"the head should be justified two behind the current epoch"
|
||||
);
|
||||
assert_eq!(
|
||||
state.finalized_epoch,
|
||||
state.finalized_checkpoint.epoch,
|
||||
state.current_epoch() - 4,
|
||||
"the head should be finalized three behind the current epoch"
|
||||
);
|
||||
@ -175,11 +175,11 @@ fn does_not_finalize_with_less_than_two_thirds_participation() {
|
||||
"head should be at the expected epoch"
|
||||
);
|
||||
assert_eq!(
|
||||
state.current_justified_epoch, 0,
|
||||
state.current_justified_checkpoint.epoch, 0,
|
||||
"no epoch should have been justified"
|
||||
);
|
||||
assert_eq!(
|
||||
state.finalized_epoch, 0,
|
||||
state.finalized_checkpoint.epoch, 0,
|
||||
"no epoch should have been finalized"
|
||||
);
|
||||
}
|
||||
@ -208,11 +208,11 @@ fn does_not_finalize_without_attestation() {
|
||||
"head should be at the expected epoch"
|
||||
);
|
||||
assert_eq!(
|
||||
state.current_justified_epoch, 0,
|
||||
state.current_justified_checkpoint.epoch, 0,
|
||||
"no epoch should have been justified"
|
||||
);
|
||||
assert_eq!(
|
||||
state.finalized_epoch, 0,
|
||||
state.finalized_checkpoint.epoch, 0,
|
||||
"no epoch should have been finalized"
|
||||
);
|
||||
}
|
||||
@ -233,10 +233,10 @@ fn roundtrip_operation_pool() {
|
||||
|
||||
// Add some deposits
|
||||
let rng = &mut XorShiftRng::from_seed([66; 16]);
|
||||
for _ in 0..rng.gen_range(1, VALIDATOR_COUNT) {
|
||||
for i in 0..rng.gen_range(1, VALIDATOR_COUNT) {
|
||||
harness
|
||||
.chain
|
||||
.process_deposit(Deposit::random_for_test(rng))
|
||||
.process_deposit(i as u64, Deposit::random_for_test(rng))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
@ -18,31 +18,31 @@ use slog::{o, trace, warn};
|
||||
use ssz::{ssz_encode, Decode, DecodeError, Encode};
|
||||
use std::num::NonZeroU32;
|
||||
use std::time::Duration;
|
||||
use types::{Attestation, BeaconBlock};
|
||||
use types::{Attestation, BeaconBlock, EthSpec};
|
||||
|
||||
/// Builds the network behaviour that manages the core protocols of eth2.
|
||||
/// This core behaviour is managed by `Behaviour` which adds peer management to all core
|
||||
/// behaviours.
|
||||
#[derive(NetworkBehaviour)]
|
||||
#[behaviour(out_event = "BehaviourEvent", poll_method = "poll")]
|
||||
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite> {
|
||||
#[behaviour(out_event = "BehaviourEvent<E>", poll_method = "poll")]
|
||||
pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> {
|
||||
/// The routing pub-sub mechanism for eth2.
|
||||
gossipsub: Gossipsub<TSubstream>,
|
||||
/// The serenity RPC specified in the wire-0 protocol.
|
||||
serenity_rpc: RPC<TSubstream>,
|
||||
serenity_rpc: RPC<TSubstream, E>,
|
||||
/// Keep regular connection to peers and disconnect if absent.
|
||||
ping: Ping<TSubstream>,
|
||||
/// Kademlia for peer discovery.
|
||||
discovery: Discovery<TSubstream>,
|
||||
#[behaviour(ignore)]
|
||||
/// The events generated by this behaviour to be consumed in the swarm poll.
|
||||
events: Vec<BehaviourEvent>,
|
||||
events: Vec<BehaviourEvent<E>>,
|
||||
/// Logger for behaviour actions.
|
||||
#[behaviour(ignore)]
|
||||
log: slog::Logger,
|
||||
}
|
||||
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> Behaviour<TSubstream, E> {
|
||||
pub fn new(
|
||||
local_key: &Keypair,
|
||||
net_conf: &NetworkConfig,
|
||||
@ -68,8 +68,8 @@ impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
}
|
||||
|
||||
// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubEvent>
|
||||
for Behaviour<TSubstream>
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> NetworkBehaviourEventProcess<GossipsubEvent>
|
||||
for Behaviour<TSubstream, E>
|
||||
{
|
||||
fn inject_event(&mut self, event: GossipsubEvent) {
|
||||
match event {
|
||||
@ -101,8 +101,8 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<GossipsubE
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage>
|
||||
for Behaviour<TSubstream>
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> NetworkBehaviourEventProcess<RPCMessage>
|
||||
for Behaviour<TSubstream, E>
|
||||
{
|
||||
fn inject_event(&mut self, event: RPCMessage) {
|
||||
match event {
|
||||
@ -119,19 +119,19 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<RPCMessage
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<PingEvent>
|
||||
for Behaviour<TSubstream>
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> NetworkBehaviourEventProcess<PingEvent>
|
||||
for Behaviour<TSubstream, E>
|
||||
{
|
||||
fn inject_event(&mut self, _event: PingEvent) {
|
||||
// not interested in ping responses at the moment.
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> Behaviour<TSubstream, E> {
|
||||
/// Consumes the events list when polled.
|
||||
fn poll<TBehaviourIn>(
|
||||
&mut self,
|
||||
) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent>> {
|
||||
) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent<E>>> {
|
||||
if !self.events.is_empty() {
|
||||
return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
|
||||
}
|
||||
@ -140,8 +140,8 @@ impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<Discv5Event>
|
||||
for Behaviour<TSubstream>
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> NetworkBehaviourEventProcess<Discv5Event>
|
||||
for Behaviour<TSubstream, E>
|
||||
{
|
||||
fn inject_event(&mut self, _event: Discv5Event) {
|
||||
// discv5 has no events to inject
|
||||
@ -149,7 +149,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> NetworkBehaviourEventProcess<Discv5Even
|
||||
}
|
||||
|
||||
/// Implements the combined behaviour for the libp2p service.
|
||||
impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
impl<TSubstream: AsyncRead + AsyncWrite, E: EthSpec> Behaviour<TSubstream, E> {
|
||||
/* Pubsub behaviour functions */
|
||||
|
||||
/// Subscribes to a gossipsub topic.
|
||||
@ -158,7 +158,7 @@ impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
}
|
||||
|
||||
/// Publishes a message on the pubsub (gossipsub) behaviour.
|
||||
pub fn publish(&mut self, topics: Vec<Topic>, message: PubsubMessage) {
|
||||
pub fn publish(&mut self, topics: Vec<Topic>, message: PubsubMessage<E>) {
|
||||
let message_bytes = ssz_encode(&message);
|
||||
for topic in topics {
|
||||
self.gossipsub.publish(topic, message_bytes.clone());
|
||||
@ -179,28 +179,28 @@ impl<TSubstream: AsyncRead + AsyncWrite> Behaviour<TSubstream> {
|
||||
}
|
||||
|
||||
/// The types of events than can be obtained from polling the behaviour.
|
||||
pub enum BehaviourEvent {
|
||||
pub enum BehaviourEvent<E: EthSpec> {
|
||||
RPC(PeerId, RPCEvent),
|
||||
PeerDialed(PeerId),
|
||||
PeerDisconnected(PeerId),
|
||||
GossipMessage {
|
||||
source: PeerId,
|
||||
topics: Vec<TopicHash>,
|
||||
message: Box<PubsubMessage>,
|
||||
message: Box<PubsubMessage<E>>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Messages that are passed to and from the pubsub (Gossipsub) behaviour.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PubsubMessage {
|
||||
pub enum PubsubMessage<E: EthSpec> {
|
||||
/// Gossipsub message providing notification of a new block.
|
||||
Block(BeaconBlock),
|
||||
Block(BeaconBlock<E>),
|
||||
/// Gossipsub message providing notification of a new attestation.
|
||||
Attestation(Attestation),
|
||||
Attestation(Attestation<E>),
|
||||
}
|
||||
|
||||
//TODO: Correctly encode/decode enums. Prefixing with integer for now.
|
||||
impl Encode for PubsubMessage {
|
||||
impl<E: EthSpec> Encode for PubsubMessage<E> {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
false
|
||||
}
|
||||
@ -229,7 +229,7 @@ impl Encode for PubsubMessage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for PubsubMessage {
|
||||
impl<E: EthSpec> Decode for PubsubMessage<E> {
|
||||
fn is_ssz_fixed_len() -> bool {
|
||||
false
|
||||
}
|
||||
@ -264,7 +264,9 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn ssz_encoding() {
|
||||
let original = PubsubMessage::Block(BeaconBlock::empty(&MainnetEthSpec::default_spec()));
|
||||
let original = PubsubMessage::Block(BeaconBlock::<MainnetEthSpec>::empty(
|
||||
&MainnetEthSpec::default_spec(),
|
||||
));
|
||||
|
||||
let encoded = ssz_encode(&original);
|
||||
|
||||
|
@ -271,7 +271,7 @@ fn load_enr(
|
||||
// Note: Discovery should update the ENR record's IP to the external IP as seen by the
|
||||
// majority of our peers.
|
||||
let mut local_enr = EnrBuilder::new()
|
||||
.ip(config.discovery_address.into())
|
||||
.ip(config.discovery_address)
|
||||
.tcp(config.libp2p_port)
|
||||
.udp(config.discovery_port)
|
||||
.build(&local_key)
|
||||
@ -318,7 +318,7 @@ fn load_enr(
|
||||
Ok(local_enr)
|
||||
}
|
||||
|
||||
fn save_enr_to_disc(dir: &Path, enr: &Enr, log: &slog::Logger) -> () {
|
||||
fn save_enr_to_disc(dir: &Path, enr: &Enr, log: &slog::Logger) {
|
||||
let _ = std::fs::create_dir_all(dir);
|
||||
match File::create(dir.join(Path::new(ENR_FILENAME)))
|
||||
.and_then(|mut f| f.write_all(&enr.to_base64().as_bytes()))
|
||||
|
@ -65,7 +65,7 @@ where
|
||||
dst.clear();
|
||||
dst.reserve(1);
|
||||
dst.put_u8(item.as_u8());
|
||||
return self.inner.encode(item, dst);
|
||||
self.inner.encode(item, dst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,16 +120,14 @@ where
|
||||
|
||||
if RPCErrorResponse::is_response(response_code) {
|
||||
// decode an actual response
|
||||
return self
|
||||
.inner
|
||||
self.inner
|
||||
.decode(src)
|
||||
.map(|r| r.map(|resp| RPCErrorResponse::Success(resp)));
|
||||
.map(|r| r.map(RPCErrorResponse::Success))
|
||||
} else {
|
||||
// decode an error
|
||||
return self
|
||||
.inner
|
||||
self.inner
|
||||
.decode_error(src)
|
||||
.map(|r| r.map(|resp| RPCErrorResponse::from_error(response_code, resp)));
|
||||
.map(|r| r.map(|resp| RPCErrorResponse::from_error(response_code, resp)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ use super::methods::{RPCErrorResponse, RPCResponse, RequestId};
|
||||
use super::protocol::{RPCError, RPCProtocol, RPCRequest};
|
||||
use super::RPCEvent;
|
||||
use crate::rpc::protocol::{InboundFramed, OutboundFramed};
|
||||
use core::marker::PhantomData;
|
||||
use fnv::FnvHashMap;
|
||||
use futures::prelude::*;
|
||||
use libp2p::core::protocols_handler::{
|
||||
@ -11,14 +12,16 @@ use libp2p::core::upgrade::{InboundUpgrade, OutboundUpgrade};
|
||||
use smallvec::SmallVec;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use types::EthSpec;
|
||||
|
||||
/// The time (in seconds) before a substream that is awaiting a response times out.
|
||||
pub const RESPONSE_TIMEOUT: u64 = 9;
|
||||
|
||||
/// Implementation of `ProtocolsHandler` for the RPC protocol.
|
||||
pub struct RPCHandler<TSubstream>
|
||||
pub struct RPCHandler<TSubstream, E>
|
||||
where
|
||||
TSubstream: AsyncRead + AsyncWrite,
|
||||
E: EthSpec,
|
||||
{
|
||||
/// The upgrade for inbound substreams.
|
||||
listen_protocol: SubstreamProtocol<RPCProtocol>,
|
||||
@ -52,6 +55,9 @@ where
|
||||
|
||||
/// After the given duration has elapsed, an inactive connection will shutdown.
|
||||
inactive_timeout: Duration,
|
||||
|
||||
/// Phantom EthSpec.
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
/// An outbound substream is waiting a response from the user.
|
||||
@ -84,9 +90,10 @@ where
|
||||
},
|
||||
}
|
||||
|
||||
impl<TSubstream> RPCHandler<TSubstream>
|
||||
impl<TSubstream, E> RPCHandler<TSubstream, E>
|
||||
where
|
||||
TSubstream: AsyncRead + AsyncWrite,
|
||||
E: EthSpec,
|
||||
{
|
||||
pub fn new(
|
||||
listen_protocol: SubstreamProtocol<RPCProtocol>,
|
||||
@ -104,6 +111,7 @@ where
|
||||
max_dial_negotiated: 8,
|
||||
keep_alive: KeepAlive::Yes,
|
||||
inactive_timeout,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,18 +145,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream> Default for RPCHandler<TSubstream>
|
||||
impl<TSubstream, E> Default for RPCHandler<TSubstream, E>
|
||||
where
|
||||
TSubstream: AsyncRead + AsyncWrite,
|
||||
E: EthSpec,
|
||||
{
|
||||
fn default() -> Self {
|
||||
RPCHandler::new(SubstreamProtocol::new(RPCProtocol), Duration::from_secs(30))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream> ProtocolsHandler for RPCHandler<TSubstream>
|
||||
impl<TSubstream, E> ProtocolsHandler for RPCHandler<TSubstream, E>
|
||||
where
|
||||
TSubstream: AsyncRead + AsyncWrite,
|
||||
E: EthSpec,
|
||||
{
|
||||
type InEvent = RPCEvent;
|
||||
type OutEvent = RPCEvent;
|
||||
@ -276,13 +286,8 @@ where
|
||||
}
|
||||
|
||||
// remove any streams that have expired
|
||||
self.waiting_substreams.retain(|_k, waiting_stream| {
|
||||
if Instant::now() > waiting_stream.timeout {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
self.waiting_substreams
|
||||
.retain(|_k, waiting_stream| Instant::now() <= waiting_stream.timeout);
|
||||
|
||||
// drive streams that need to be processed
|
||||
for n in (0..self.substreams.len()).rev() {
|
||||
@ -334,7 +339,7 @@ where
|
||||
}
|
||||
Err(e) => {
|
||||
return Ok(Async::Ready(ProtocolsHandlerEvent::Custom(
|
||||
RPCEvent::Error(rpc_event.id(), e.into()),
|
||||
RPCEvent::Error(rpc_event.id(), e),
|
||||
)))
|
||||
}
|
||||
},
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use ssz::{impl_decode_via_from, impl_encode_via_from};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use types::{BeaconBlockBody, Epoch, Hash256, Slot};
|
||||
use types::{BeaconBlockBody, Epoch, EthSpec, Hash256, Slot};
|
||||
|
||||
/* Request/Response data structures for RPC methods */
|
||||
|
||||
@ -154,11 +154,11 @@ pub struct BeaconBlockBodiesResponse {
|
||||
}
|
||||
|
||||
/// The decoded version of `BeaconBlockBodiesResponse` which is expected in `SimpleSync`.
|
||||
pub struct DecodedBeaconBlockBodiesResponse {
|
||||
pub struct DecodedBeaconBlockBodiesResponse<E: EthSpec> {
|
||||
/// The list of hashes sent in the request to get this response.
|
||||
pub block_roots: Vec<Hash256>,
|
||||
/// The valid decoded block bodies.
|
||||
pub block_bodies: Vec<BeaconBlockBody>,
|
||||
pub block_bodies: Vec<BeaconBlockBody<E>>,
|
||||
}
|
||||
|
||||
/// Request values for tree hashes which yield a blocks `state_root`.
|
||||
|
@ -16,6 +16,7 @@ pub use protocol::{RPCError, RPCProtocol, RPCRequest};
|
||||
use slog::o;
|
||||
use std::marker::PhantomData;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use types::EthSpec;
|
||||
|
||||
pub(crate) mod codec;
|
||||
mod handler;
|
||||
@ -49,16 +50,16 @@ impl RPCEvent {
|
||||
|
||||
/// Implements the libp2p `NetworkBehaviour` trait and therefore manages network-level
|
||||
/// logic.
|
||||
pub struct RPC<TSubstream> {
|
||||
pub struct RPC<TSubstream, E: EthSpec> {
|
||||
/// Queue of events to processed.
|
||||
events: Vec<NetworkBehaviourAction<RPCEvent, RPCMessage>>,
|
||||
/// Pins the generic substream.
|
||||
marker: PhantomData<TSubstream>,
|
||||
marker: PhantomData<(TSubstream, E)>,
|
||||
/// Slog logger for RPC behaviour.
|
||||
_log: slog::Logger,
|
||||
}
|
||||
|
||||
impl<TSubstream> RPC<TSubstream> {
|
||||
impl<TSubstream, E: EthSpec> RPC<TSubstream, E> {
|
||||
pub fn new(log: &slog::Logger) -> Self {
|
||||
let log = log.new(o!("Service" => "Libp2p-RPC"));
|
||||
RPC {
|
||||
@ -79,11 +80,12 @@ impl<TSubstream> RPC<TSubstream> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<TSubstream> NetworkBehaviour for RPC<TSubstream>
|
||||
impl<TSubstream, E> NetworkBehaviour for RPC<TSubstream, E>
|
||||
where
|
||||
TSubstream: AsyncRead + AsyncWrite,
|
||||
E: EthSpec,
|
||||
{
|
||||
type ProtocolsHandler = RPCHandler<TSubstream>;
|
||||
type ProtocolsHandler = RPCHandler<TSubstream, E>;
|
||||
type OutEvent = RPCMessage;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ProtocolsHandler {
|
||||
|
@ -21,24 +21,25 @@ use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::time::Duration;
|
||||
use types::EthSpec;
|
||||
|
||||
type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
|
||||
type Libp2pBehaviour = Behaviour<Substream<StreamMuxerBox>>;
|
||||
type Libp2pBehaviour<E> = Behaviour<Substream<StreamMuxerBox>, E>;
|
||||
|
||||
const NETWORK_KEY_FILENAME: &str = "key";
|
||||
|
||||
/// The configuration and state of the libp2p components for the beacon node.
|
||||
pub struct Service {
|
||||
pub struct Service<E: EthSpec> {
|
||||
/// The libp2p Swarm handler.
|
||||
//TODO: Make this private
|
||||
pub swarm: Swarm<Libp2pStream, Libp2pBehaviour>,
|
||||
pub swarm: Swarm<Libp2pStream, Libp2pBehaviour<E>>,
|
||||
/// This node's PeerId.
|
||||
_local_peer_id: PeerId,
|
||||
/// The libp2p logger handle.
|
||||
pub log: slog::Logger,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
impl<E: EthSpec> Service<E> {
|
||||
pub fn new(config: NetworkConfig, log: slog::Logger) -> error::Result<Self> {
|
||||
debug!(log, "Network-libp2p Service starting");
|
||||
|
||||
@ -103,8 +104,8 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for Service {
|
||||
type Item = Libp2pEvent;
|
||||
impl<E: EthSpec> Stream for Service<E> {
|
||||
type Item = Libp2pEvent<E>;
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
||||
@ -178,7 +179,7 @@ fn build_transport(local_private_key: Keypair) -> Boxed<(PeerId, StreamMuxerBox)
|
||||
}
|
||||
|
||||
/// Events that can be obtained from polling the Libp2p Service.
|
||||
pub enum Libp2pEvent {
|
||||
pub enum Libp2pEvent<E: EthSpec> {
|
||||
/// An RPC response request has been received on the swarm.
|
||||
RPC(PeerId, RPCEvent),
|
||||
/// Initiated the connection to a new peer.
|
||||
@ -189,7 +190,7 @@ pub enum Libp2pEvent {
|
||||
PubsubMessage {
|
||||
source: PeerId,
|
||||
topics: Vec<TopicHash>,
|
||||
message: Box<PubsubMessage>,
|
||||
message: Box<PubsubMessage<E>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ pub fn create_iron_http_server<T: BeaconChainTypes + 'static>(
|
||||
pub fn start_service<T: BeaconChainTypes + 'static>(
|
||||
config: &HttpServerConfig,
|
||||
executor: &TaskExecutor,
|
||||
_network_chan: mpsc::UnboundedSender<NetworkMessage>,
|
||||
_network_chan: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
db_path: PathBuf,
|
||||
metrics_registry: Registry,
|
||||
|
@ -117,22 +117,23 @@ impl LocalMetrics {
|
||||
beacon_chain
|
||||
.head()
|
||||
.beacon_state
|
||||
.current_justified_root
|
||||
.current_justified_checkpoint
|
||||
.root
|
||||
.to_low_u64_le() as i64,
|
||||
);
|
||||
self.finalized_beacon_block_root.set(
|
||||
beacon_chain
|
||||
.head()
|
||||
.beacon_state
|
||||
.finalized_root
|
||||
.finalized_checkpoint
|
||||
.root
|
||||
.to_low_u64_le() as i64,
|
||||
);
|
||||
self.validator_count
|
||||
.set(state.validator_registry.len() as i64);
|
||||
self.validator_count.set(state.validators.len() as i64);
|
||||
self.justified_epoch
|
||||
.set(state.current_justified_epoch.as_u64() as i64);
|
||||
.set(state.current_justified_checkpoint.epoch.as_u64() as i64);
|
||||
self.finalized_epoch
|
||||
.set(state.finalized_epoch.as_u64() as i64);
|
||||
.set(state.finalized_checkpoint.epoch.as_u64() as i64);
|
||||
if SHOULD_SUM_VALIDATOR_BALANCES {
|
||||
self.validator_balances_sum
|
||||
.set(state.balances.iter().sum::<u64>() as i64);
|
||||
|
@ -14,7 +14,7 @@ use slog::{debug, warn};
|
||||
use ssz::{Decode, DecodeError};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use types::BeaconBlockHeader;
|
||||
use types::{BeaconBlockHeader, EthSpec};
|
||||
|
||||
/// Handles messages received from the network and client and organises syncing.
|
||||
pub struct MessageHandler<T: BeaconChainTypes> {
|
||||
@ -23,14 +23,14 @@ pub struct MessageHandler<T: BeaconChainTypes> {
|
||||
/// The syncing framework.
|
||||
sync: SimpleSync<T>,
|
||||
/// The context required to send messages to, and process messages from peers.
|
||||
network_context: NetworkContext,
|
||||
network_context: NetworkContext<T::EthSpec>,
|
||||
/// The `MessageHandler` logger.
|
||||
log: slog::Logger,
|
||||
}
|
||||
|
||||
/// Types of messages the handler can receive.
|
||||
#[derive(Debug)]
|
||||
pub enum HandlerMessage {
|
||||
pub enum HandlerMessage<E: EthSpec> {
|
||||
/// We have initiated a connection to a new peer.
|
||||
PeerDialed(PeerId),
|
||||
/// Peer has disconnected,
|
||||
@ -38,17 +38,17 @@ pub enum HandlerMessage {
|
||||
/// An RPC response/request has been received.
|
||||
RPC(PeerId, RPCEvent),
|
||||
/// A gossip message has been received.
|
||||
PubsubMessage(PeerId, Box<PubsubMessage>),
|
||||
PubsubMessage(PeerId, Box<PubsubMessage<E>>),
|
||||
}
|
||||
|
||||
impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
/// Initializes and runs the MessageHandler.
|
||||
pub fn spawn(
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
network_send: mpsc::UnboundedSender<NetworkMessage>,
|
||||
network_send: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
executor: &tokio::runtime::TaskExecutor,
|
||||
log: slog::Logger,
|
||||
) -> error::Result<mpsc::UnboundedSender<HandlerMessage>> {
|
||||
) -> error::Result<mpsc::UnboundedSender<HandlerMessage<T::EthSpec>>> {
|
||||
debug!(log, "Service starting");
|
||||
|
||||
let (handler_send, handler_recv) = mpsc::unbounded_channel();
|
||||
@ -78,7 +78,7 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
}
|
||||
|
||||
/// Handle all messages incoming from the network service.
|
||||
fn handle_message(&mut self, message: HandlerMessage) {
|
||||
fn handle_message(&mut self, message: HandlerMessage<T::EthSpec>) {
|
||||
match message {
|
||||
// we have initiated a connection to a peer
|
||||
HandlerMessage::PeerDialed(peer_id) => {
|
||||
@ -222,7 +222,7 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
fn decode_block_bodies(
|
||||
&self,
|
||||
bodies_response: BeaconBlockBodiesResponse,
|
||||
) -> Result<DecodedBeaconBlockBodiesResponse, DecodeError> {
|
||||
) -> Result<DecodedBeaconBlockBodiesResponse<T::EthSpec>, DecodeError> {
|
||||
//TODO: Implement faster block verification before decoding entirely
|
||||
let block_bodies = Vec::from_ssz_bytes(&bodies_response.block_bodies)?;
|
||||
Ok(DecodedBeaconBlockBodiesResponse {
|
||||
@ -249,7 +249,7 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
}
|
||||
|
||||
/// Handle RPC messages
|
||||
fn handle_gossip(&mut self, peer_id: PeerId, gossip_message: PubsubMessage) {
|
||||
fn handle_gossip(&mut self, peer_id: PeerId, gossip_message: PubsubMessage<T::EthSpec>) {
|
||||
match gossip_message {
|
||||
PubsubMessage::Block(message) => {
|
||||
let _should_forward_on =
|
||||
@ -265,15 +265,15 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
}
|
||||
|
||||
// TODO: RPC Rewrite makes this struct fairly pointless
|
||||
pub struct NetworkContext {
|
||||
pub struct NetworkContext<E: EthSpec> {
|
||||
/// The network channel to relay messages to the Network service.
|
||||
network_send: mpsc::UnboundedSender<NetworkMessage>,
|
||||
network_send: mpsc::UnboundedSender<NetworkMessage<E>>,
|
||||
/// The `MessageHandler` logger.
|
||||
log: slog::Logger,
|
||||
}
|
||||
|
||||
impl NetworkContext {
|
||||
pub fn new(network_send: mpsc::UnboundedSender<NetworkMessage>, log: slog::Logger) -> Self {
|
||||
impl<E: EthSpec> NetworkContext<E> {
|
||||
pub fn new(network_send: mpsc::UnboundedSender<NetworkMessage<E>>, log: slog::Logger) -> Self {
|
||||
Self { network_send, log }
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ use crate::error;
|
||||
use crate::message_handler::{HandlerMessage, MessageHandler};
|
||||
use crate::NetworkConfig;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use core::marker::PhantomData;
|
||||
use eth2_libp2p::Service as LibP2PService;
|
||||
use eth2_libp2p::Topic;
|
||||
use eth2_libp2p::{Libp2pEvent, PeerId};
|
||||
@ -10,16 +11,16 @@ use futures::prelude::*;
|
||||
use futures::Stream;
|
||||
use parking_lot::Mutex;
|
||||
use slog::{debug, info, o, trace};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::TaskExecutor;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use types::EthSpec;
|
||||
|
||||
/// Service that handles communication between internal services and the eth2_libp2p network service.
|
||||
pub struct Service<T: BeaconChainTypes> {
|
||||
libp2p_service: Arc<Mutex<LibP2PService>>,
|
||||
libp2p_service: Arc<Mutex<LibP2PService<T::EthSpec>>>,
|
||||
_libp2p_exit: oneshot::Sender<()>,
|
||||
_network_send: mpsc::UnboundedSender<NetworkMessage>,
|
||||
_network_send: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
_phantom: PhantomData<T>, //message_handler: MessageHandler,
|
||||
//message_handler_send: Sender<HandlerMessage>
|
||||
}
|
||||
@ -30,9 +31,9 @@ impl<T: BeaconChainTypes + 'static> Service<T> {
|
||||
config: &NetworkConfig,
|
||||
executor: &TaskExecutor,
|
||||
log: slog::Logger,
|
||||
) -> error::Result<(Arc<Self>, mpsc::UnboundedSender<NetworkMessage>)> {
|
||||
) -> error::Result<(Arc<Self>, mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>)> {
|
||||
// build the network channel
|
||||
let (network_send, network_recv) = mpsc::unbounded_channel::<NetworkMessage>();
|
||||
let (network_send, network_recv) = mpsc::unbounded_channel::<NetworkMessage<_>>();
|
||||
// launch message handler thread
|
||||
let message_handler_log = log.new(o!("Service" => "MessageHandler"));
|
||||
let message_handler_send = MessageHandler::spawn(
|
||||
@ -64,15 +65,15 @@ impl<T: BeaconChainTypes + 'static> Service<T> {
|
||||
Ok((Arc::new(network_service), network_send))
|
||||
}
|
||||
|
||||
pub fn libp2p_service(&self) -> Arc<Mutex<LibP2PService>> {
|
||||
pub fn libp2p_service(&self) -> Arc<Mutex<LibP2PService<T::EthSpec>>> {
|
||||
self.libp2p_service.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn spawn_service(
|
||||
libp2p_service: Arc<Mutex<LibP2PService>>,
|
||||
network_recv: mpsc::UnboundedReceiver<NetworkMessage>,
|
||||
message_handler_send: mpsc::UnboundedSender<HandlerMessage>,
|
||||
fn spawn_service<E: EthSpec>(
|
||||
libp2p_service: Arc<Mutex<LibP2PService<E>>>,
|
||||
network_recv: mpsc::UnboundedReceiver<NetworkMessage<E>>,
|
||||
message_handler_send: mpsc::UnboundedSender<HandlerMessage<E>>,
|
||||
executor: &TaskExecutor,
|
||||
log: slog::Logger,
|
||||
) -> error::Result<tokio::sync::oneshot::Sender<()>> {
|
||||
@ -98,10 +99,10 @@ fn spawn_service(
|
||||
}
|
||||
|
||||
//TODO: Potentially handle channel errors
|
||||
fn network_service(
|
||||
libp2p_service: Arc<Mutex<LibP2PService>>,
|
||||
mut network_recv: mpsc::UnboundedReceiver<NetworkMessage>,
|
||||
mut message_handler_send: mpsc::UnboundedSender<HandlerMessage>,
|
||||
fn network_service<E: EthSpec>(
|
||||
libp2p_service: Arc<Mutex<LibP2PService<E>>>,
|
||||
mut network_recv: mpsc::UnboundedReceiver<NetworkMessage<E>>,
|
||||
mut message_handler_send: mpsc::UnboundedSender<HandlerMessage<E>>,
|
||||
log: slog::Logger,
|
||||
) -> impl futures::Future<Item = (), Error = eth2_libp2p::error::Error> {
|
||||
futures::future::poll_fn(move || -> Result<_, eth2_libp2p::error::Error> {
|
||||
@ -175,14 +176,14 @@ fn network_service(
|
||||
|
||||
/// Types of messages that the network service can receive.
|
||||
#[derive(Debug)]
|
||||
pub enum NetworkMessage {
|
||||
pub enum NetworkMessage<E: EthSpec> {
|
||||
/// Send a message to libp2p service.
|
||||
//TODO: Define typing for messages across the wire
|
||||
Send(PeerId, OutgoingMessage),
|
||||
/// Publish a message to pubsub mechanism.
|
||||
Publish {
|
||||
topics: Vec<Topic>,
|
||||
message: Box<PubsubMessage>,
|
||||
message: Box<PubsubMessage<E>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use tree_hash::TreeHash;
|
||||
use types::{BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Hash256, Slot};
|
||||
use types::{BeaconBlock, BeaconBlockBody, BeaconBlockHeader, EthSpec, Hash256, Slot};
|
||||
|
||||
/// Provides a queue for fully and partially built `BeaconBlock`s.
|
||||
///
|
||||
@ -23,7 +23,7 @@ use types::{BeaconBlock, BeaconBlockBody, BeaconBlockHeader, Hash256, Slot};
|
||||
pub struct ImportQueue<T: BeaconChainTypes> {
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
/// Partially imported blocks, keyed by the root of `BeaconBlockBody`.
|
||||
partials: HashMap<Hash256, PartialBeaconBlock>,
|
||||
partials: HashMap<Hash256, PartialBeaconBlock<T::EthSpec>>,
|
||||
/// Time before a queue entry is considered state.
|
||||
pub stale_time: Duration,
|
||||
/// Logging
|
||||
@ -50,7 +50,10 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
///
|
||||
/// Returns an Enum with a `PartialBeaconBlockCompletion`.
|
||||
/// Does not remove the `block_root` from the `import_queue`.
|
||||
pub fn attempt_complete_block(&self, block_root: Hash256) -> PartialBeaconBlockCompletion {
|
||||
pub fn attempt_complete_block(
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
) -> PartialBeaconBlockCompletion<T::EthSpec> {
|
||||
if let Some(partial) = self.partials.get(&block_root) {
|
||||
partial.attempt_complete()
|
||||
} else {
|
||||
@ -60,7 +63,7 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
|
||||
/// Removes the first `PartialBeaconBlock` with a matching `block_root`, returning the partial
|
||||
/// if it exists.
|
||||
pub fn remove(&mut self, block_root: Hash256) -> Option<PartialBeaconBlock> {
|
||||
pub fn remove(&mut self, block_root: Hash256) -> Option<PartialBeaconBlock<T::EthSpec>> {
|
||||
self.partials.remove(&block_root)
|
||||
}
|
||||
|
||||
@ -141,11 +144,11 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
for header in headers {
|
||||
let block_root = Hash256::from_slice(&header.canonical_root()[..]);
|
||||
|
||||
if self.chain_has_not_seen_block(&block_root) {
|
||||
if !self.insert_header(block_root, header, sender.clone()) {
|
||||
// If a body is empty
|
||||
required_bodies.push(block_root);
|
||||
}
|
||||
if self.chain_has_not_seen_block(&block_root)
|
||||
&& !self.insert_header(block_root, header, sender.clone())
|
||||
{
|
||||
// If a body is empty
|
||||
required_bodies.push(block_root);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +160,7 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
/// If there is no `header` for the `body`, the body is simply discarded.
|
||||
pub fn enqueue_bodies(
|
||||
&mut self,
|
||||
bodies: Vec<BeaconBlockBody>,
|
||||
bodies: Vec<BeaconBlockBody<T::EthSpec>>,
|
||||
sender: PeerId,
|
||||
) -> Option<Hash256> {
|
||||
let mut last_block_hash = None;
|
||||
@ -168,7 +171,7 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
last_block_hash
|
||||
}
|
||||
|
||||
pub fn enqueue_full_blocks(&mut self, blocks: Vec<BeaconBlock>, sender: PeerId) {
|
||||
pub fn enqueue_full_blocks(&mut self, blocks: Vec<BeaconBlock<T::EthSpec>>, sender: PeerId) {
|
||||
for block in blocks {
|
||||
self.insert_full_block(block, sender.clone());
|
||||
}
|
||||
@ -211,13 +214,17 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
/// If the body already existed, the `inserted` time is set to `now`.
|
||||
///
|
||||
/// Returns the block hash of the inserted body
|
||||
fn insert_body(&mut self, body: BeaconBlockBody, sender: PeerId) -> Option<Hash256> {
|
||||
fn insert_body(
|
||||
&mut self,
|
||||
body: BeaconBlockBody<T::EthSpec>,
|
||||
sender: PeerId,
|
||||
) -> Option<Hash256> {
|
||||
let body_root = Hash256::from_slice(&body.tree_hash_root()[..]);
|
||||
let mut last_root = None;
|
||||
|
||||
self.partials.iter_mut().for_each(|(root, mut p)| {
|
||||
if let Some(header) = &mut p.header {
|
||||
if body_root == header.block_body_root {
|
||||
if body_root == header.body_root {
|
||||
p.inserted = Instant::now();
|
||||
p.body = Some(body.clone());
|
||||
p.sender = sender.clone();
|
||||
@ -232,7 +239,7 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
/// Updates an existing `partial` with the completed block, or adds a new (complete) partial.
|
||||
///
|
||||
/// If the partial already existed, the `inserted` time is set to `now`.
|
||||
fn insert_full_block(&mut self, block: BeaconBlock, sender: PeerId) {
|
||||
fn insert_full_block(&mut self, block: BeaconBlock<T::EthSpec>, sender: PeerId) {
|
||||
let block_root = Hash256::from_slice(&block.canonical_root()[..]);
|
||||
|
||||
let partial = PartialBeaconBlock {
|
||||
@ -254,12 +261,12 @@ impl<T: BeaconChainTypes> ImportQueue<T> {
|
||||
/// Individual components of a `BeaconBlock`, potentially all that are required to form a full
|
||||
/// `BeaconBlock`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PartialBeaconBlock {
|
||||
pub struct PartialBeaconBlock<E: EthSpec> {
|
||||
pub slot: Slot,
|
||||
/// `BeaconBlock` root.
|
||||
pub block_root: Hash256,
|
||||
pub header: Option<BeaconBlockHeader>,
|
||||
pub body: Option<BeaconBlockBody>,
|
||||
pub body: Option<BeaconBlockBody<E>>,
|
||||
/// The instant at which this record was created or last meaningfully modified. Used to
|
||||
/// determine if an entry is stale and should be removed.
|
||||
pub inserted: Instant,
|
||||
@ -267,11 +274,11 @@ pub struct PartialBeaconBlock {
|
||||
pub sender: PeerId,
|
||||
}
|
||||
|
||||
impl PartialBeaconBlock {
|
||||
impl<E: EthSpec> PartialBeaconBlock<E> {
|
||||
/// Attempts to build a block.
|
||||
///
|
||||
/// Does not consume the `PartialBeaconBlock`.
|
||||
pub fn attempt_complete(&self) -> PartialBeaconBlockCompletion {
|
||||
/// Does not comsume the `PartialBeaconBlock`.
|
||||
pub fn attempt_complete(&self) -> PartialBeaconBlockCompletion<E> {
|
||||
if self.header.is_none() {
|
||||
PartialBeaconBlockCompletion::MissingHeader(self.slot)
|
||||
} else if self.body.is_none() {
|
||||
@ -288,9 +295,9 @@ impl PartialBeaconBlock {
|
||||
}
|
||||
|
||||
/// The result of trying to convert a `BeaconBlock` into a `PartialBeaconBlock`.
|
||||
pub enum PartialBeaconBlockCompletion {
|
||||
pub enum PartialBeaconBlockCompletion<E: EthSpec> {
|
||||
/// The partial contains a valid BeaconBlock.
|
||||
Complete(BeaconBlock),
|
||||
Complete(BeaconBlock<E>),
|
||||
/// The partial does not exist.
|
||||
MissingRoot,
|
||||
/// The partial contains a `BeaconBlockRoot` but no `BeaconBlockHeader`.
|
||||
|
@ -123,7 +123,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
/// Handle the connection of a new peer.
|
||||
///
|
||||
/// Sends a `Hello` message to the peer.
|
||||
pub fn on_connect(&self, peer_id: PeerId, network: &mut NetworkContext) {
|
||||
pub fn on_connect(&self, peer_id: PeerId, network: &mut NetworkContext<T::EthSpec>) {
|
||||
info!(self.log, "PeerConnected"; "peer" => format!("{:?}", peer_id));
|
||||
|
||||
network.send_rpc_request(peer_id, RPCRequest::Hello(hello_message(&self.chain)));
|
||||
@ -137,7 +137,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
hello: HelloMessage,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(self.log, "HelloRequest"; "peer" => format!("{:?}", peer_id));
|
||||
|
||||
@ -156,7 +156,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
hello: HelloMessage,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(self.log, "HelloResponse"; "peer" => format!("{:?}", peer_id));
|
||||
|
||||
@ -171,7 +171,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
hello: HelloMessage,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
let remote = PeerSyncInfo::from(hello);
|
||||
let local = PeerSyncInfo::from(&self.chain);
|
||||
@ -188,8 +188,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
|
||||
network.disconnect(peer_id.clone(), GoodbyeReason::IrrelevantNetwork);
|
||||
} else if remote.latest_finalized_epoch <= local.latest_finalized_epoch
|
||||
&& remote.latest_finalized_root != self.chain.spec.zero_hash
|
||||
&& local.latest_finalized_root != self.chain.spec.zero_hash
|
||||
&& remote.latest_finalized_root != Hash256::zero()
|
||||
&& local.latest_finalized_root != Hash256::zero()
|
||||
&& (self.root_at_slot(start_slot(remote.latest_finalized_epoch))
|
||||
!= Some(remote.latest_finalized_root))
|
||||
{
|
||||
@ -226,7 +226,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
} else if self
|
||||
.chain
|
||||
.store
|
||||
.exists::<BeaconBlock>(&remote.best_root)
|
||||
.exists::<BeaconBlock<T::EthSpec>>(&remote.best_root)
|
||||
.unwrap_or_else(|_| false)
|
||||
{
|
||||
// If the node's best-block is already known to us, we have nothing to request.
|
||||
@ -278,7 +278,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
req: BeaconBlockRootsRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -323,7 +323,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
res: BeaconBlockRootsResponse,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -387,7 +387,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
req: BeaconBlockHeadersRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -416,7 +416,11 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
.into_iter()
|
||||
.step_by(req.skip_slots as usize + 1)
|
||||
.filter_map(|root| {
|
||||
let block = self.chain.store.get::<BeaconBlock>(&root).ok()?;
|
||||
let block = self
|
||||
.chain
|
||||
.store
|
||||
.get::<BeaconBlock<T::EthSpec>>(&root)
|
||||
.ok()?;
|
||||
Some(block?.block_header())
|
||||
})
|
||||
.collect();
|
||||
@ -436,7 +440,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
headers: Vec<BeaconBlockHeader>,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -468,13 +472,13 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
req: BeaconBlockBodiesRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
let block_bodies: Vec<BeaconBlockBody> = req
|
||||
let block_bodies: Vec<BeaconBlockBody<_>> = req
|
||||
.block_roots
|
||||
.iter()
|
||||
.filter_map(|root| {
|
||||
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock>(root) {
|
||||
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock<T::EthSpec>>(root) {
|
||||
Some(block.body)
|
||||
} else {
|
||||
debug!(
|
||||
@ -513,8 +517,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
pub fn on_beacon_block_bodies_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
res: DecodedBeaconBlockBodiesResponse,
|
||||
network: &mut NetworkContext,
|
||||
res: DecodedBeaconBlockBodiesResponse<T::EthSpec>,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -531,12 +535,11 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
|
||||
// Attempt to process all received bodies by recursively processing the latest block
|
||||
if let Some(root) = last_root {
|
||||
match self.attempt_process_partial_block(peer_id, root, network, &"rpc") {
|
||||
Some(BlockProcessingOutcome::Processed { block_root: _ }) => {
|
||||
// If processing is successful remove from `import_queue`
|
||||
self.import_queue.remove(root);
|
||||
}
|
||||
_ => {}
|
||||
if let Some(BlockProcessingOutcome::Processed { .. }) =
|
||||
self.attempt_process_partial_block(peer_id, root, network, &"rpc")
|
||||
{
|
||||
// If processing is successful remove from `import_queue`
|
||||
self.import_queue.remove(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -553,8 +556,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
pub fn on_block_gossip(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block: BeaconBlock,
|
||||
network: &mut NetworkContext,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) -> bool {
|
||||
if let Some(outcome) =
|
||||
self.process_block(peer_id.clone(), block.clone(), network, &"gossip")
|
||||
@ -577,7 +580,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
.chain
|
||||
.head()
|
||||
.beacon_state
|
||||
.finalized_epoch
|
||||
.finalized_checkpoint
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
self.request_block_roots(
|
||||
peer_id,
|
||||
@ -622,8 +626,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
pub fn on_attestation_gossip(
|
||||
&mut self,
|
||||
_peer_id: PeerId,
|
||||
msg: Attestation,
|
||||
_network: &mut NetworkContext,
|
||||
msg: Attestation<T::EthSpec>,
|
||||
_network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
match self.chain.process_attestation(msg) {
|
||||
Ok(()) => info!(self.log, "ImportedAttestation"; "source" => "gossip"),
|
||||
@ -638,7 +642,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
req: BeaconBlockRootsRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
// Potentially set state to sync.
|
||||
if self.state == SyncState::Idle && req.count > SLOT_IMPORT_TOLERANCE {
|
||||
@ -662,7 +666,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
req: BeaconBlockHeadersRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -679,7 +683,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
req: BeaconBlockBodiesRequest,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
) {
|
||||
debug!(
|
||||
self.log,
|
||||
@ -715,7 +719,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block_root: Hash256,
|
||||
network: &mut NetworkContext,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
source: &str,
|
||||
) -> Option<BlockProcessingOutcome> {
|
||||
match self.import_queue.attempt_complete_block(block_root) {
|
||||
@ -807,8 +811,8 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
fn process_block(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block: BeaconBlock,
|
||||
network: &mut NetworkContext,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
network: &mut NetworkContext<T::EthSpec>,
|
||||
source: &str,
|
||||
) -> Option<BlockProcessingOutcome> {
|
||||
let processing_result = self.chain.process_block(block.clone());
|
||||
@ -836,19 +840,18 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
);
|
||||
|
||||
// If the parent is in the `import_queue` attempt to complete it then process it.
|
||||
match self.attempt_process_partial_block(peer_id, parent, network, source) {
|
||||
// All other cases leave `parent` in `import_queue` and return original outcome.
|
||||
if let Some(BlockProcessingOutcome::Processed { .. }) =
|
||||
self.attempt_process_partial_block(peer_id, parent, network, source)
|
||||
{
|
||||
// If processing parent is successful, re-process block and remove parent from queue
|
||||
Some(BlockProcessingOutcome::Processed { block_root: _ }) => {
|
||||
self.import_queue.remove(parent);
|
||||
self.import_queue.remove(parent);
|
||||
|
||||
// Attempt to process `block` again
|
||||
match self.chain.process_block(block) {
|
||||
Ok(outcome) => return Some(outcome),
|
||||
Err(_) => return None,
|
||||
}
|
||||
// Attempt to process `block` again
|
||||
match self.chain.process_block(block) {
|
||||
Ok(outcome) => return Some(outcome),
|
||||
Err(_) => return None,
|
||||
}
|
||||
// All other cases leave `parent` in `import_queue` and return original outcome.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
@ -913,9 +916,9 @@ fn hello_message<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) -> HelloMes
|
||||
HelloMessage {
|
||||
//TODO: Correctly define the chain/network id
|
||||
network_id: spec.chain_id,
|
||||
chain_id: spec.chain_id as u64,
|
||||
latest_finalized_root: state.finalized_root,
|
||||
latest_finalized_epoch: state.finalized_epoch,
|
||||
chain_id: u64::from(spec.chain_id),
|
||||
latest_finalized_root: state.finalized_checkpoint.root,
|
||||
latest_finalized_epoch: state.finalized_checkpoint.epoch,
|
||||
best_root: beacon_chain.head().beacon_block_root,
|
||||
best_slot: state.slot,
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use types::Attestation;
|
||||
#[derive(Clone)]
|
||||
pub struct AttestationServiceInstance<T: BeaconChainTypes> {
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
pub network_chan: mpsc::UnboundedSender<NetworkMessage>,
|
||||
pub network_chan: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
pub log: slog::Logger,
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ use types::{BeaconBlock, Signature, Slot};
|
||||
#[derive(Clone)]
|
||||
pub struct BeaconBlockServiceInstance<T: BeaconChainTypes> {
|
||||
pub chain: Arc<BeaconChain<T>>,
|
||||
pub network_chan: mpsc::UnboundedSender<NetworkMessage>,
|
||||
pub network_chan: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
pub log: Logger,
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ use tokio::sync::mpsc;
|
||||
pub fn start_server<T: BeaconChainTypes + Clone + 'static>(
|
||||
config: &RPCConfig,
|
||||
executor: &TaskExecutor,
|
||||
network_chan: mpsc::UnboundedSender<NetworkMessage>,
|
||||
network_chan: mpsc::UnboundedSender<NetworkMessage<T::EthSpec>>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
log: &slog::Logger,
|
||||
) -> exit_future::Signal {
|
||||
|
@ -210,7 +210,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
default_dir.push(DEFAULT_DATA_DIR);
|
||||
PathBuf::from(default_dir)
|
||||
default_dir
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,7 +57,7 @@ pub fn run_beacon_node(
|
||||
"db_type" => &other_client_config.db_type,
|
||||
);
|
||||
|
||||
let result = match (db_type.as_str(), spec_constants.as_str()) {
|
||||
match (db_type.as_str(), spec_constants.as_str()) {
|
||||
("disk", "minimal") => run::<ClientType<DiskStore, MinimalEthSpec>>(
|
||||
&db_path,
|
||||
client_config,
|
||||
@ -94,9 +94,7 @@ pub fn run_beacon_node(
|
||||
error!(log, "Unknown runtime configuration"; "spec_constants" => spec, "db_type" => db_type);
|
||||
Err("Unknown specification and/or db_type.".into())
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs the type-generic parts of launching a `BeaconChain`.
|
||||
|
@ -1,8 +1,11 @@
|
||||
use super::*;
|
||||
use ssz::{Decode, DecodeError};
|
||||
|
||||
fn get_block_bytes<T: Store>(store: &T, root: Hash256) -> Result<Option<Vec<u8>>, Error> {
|
||||
store.get_bytes(BeaconBlock::db_column().into(), &root[..])
|
||||
fn get_block_bytes<T: Store, E: EthSpec>(
|
||||
store: &T,
|
||||
root: Hash256,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
store.get_bytes(BeaconBlock::<E>::db_column().into(), &root[..])
|
||||
}
|
||||
|
||||
fn read_slot_from_block_bytes(bytes: &[u8]) -> Result<Slot, DecodeError> {
|
||||
@ -11,7 +14,7 @@ fn read_slot_from_block_bytes(bytes: &[u8]) -> Result<Slot, DecodeError> {
|
||||
Slot::from_ssz_bytes(&bytes[0..end])
|
||||
}
|
||||
|
||||
fn read_previous_block_root_from_block_bytes(bytes: &[u8]) -> Result<Hash256, DecodeError> {
|
||||
fn read_parent_root_from_block_bytes(bytes: &[u8]) -> Result<Hash256, DecodeError> {
|
||||
let previous_bytes = Slot::ssz_fixed_len();
|
||||
let slice = bytes
|
||||
.get(previous_bytes..previous_bytes + Hash256::ssz_fixed_len())
|
||||
@ -20,24 +23,26 @@ fn read_previous_block_root_from_block_bytes(bytes: &[u8]) -> Result<Hash256, De
|
||||
Hash256::from_ssz_bytes(slice)
|
||||
}
|
||||
|
||||
pub fn get_block_at_preceding_slot<T: Store>(
|
||||
pub fn get_block_at_preceeding_slot<T: Store, E: EthSpec>(
|
||||
store: &T,
|
||||
slot: Slot,
|
||||
start_root: Hash256,
|
||||
) -> Result<Option<(Hash256, BeaconBlock)>, Error> {
|
||||
Ok(match get_at_preceding_slot(store, slot, start_root)? {
|
||||
Some((hash, bytes)) => Some((hash, BeaconBlock::from_ssz_bytes(&bytes)?)),
|
||||
None => None,
|
||||
})
|
||||
) -> Result<Option<(Hash256, BeaconBlock<E>)>, Error> {
|
||||
Ok(
|
||||
match get_at_preceeding_slot::<_, E>(store, slot, start_root)? {
|
||||
Some((hash, bytes)) => Some((hash, BeaconBlock::<E>::from_ssz_bytes(&bytes)?)),
|
||||
None => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn get_at_preceding_slot<T: Store>(
|
||||
fn get_at_preceeding_slot<T: Store, E: EthSpec>(
|
||||
store: &T,
|
||||
slot: Slot,
|
||||
mut root: Hash256,
|
||||
) -> Result<Option<(Hash256, Vec<u8>)>, Error> {
|
||||
loop {
|
||||
if let Some(bytes) = get_block_bytes(store, root)? {
|
||||
if let Some(bytes) = get_block_bytes::<_, E>(store, root)? {
|
||||
let this_slot = read_slot_from_block_bytes(&bytes)?;
|
||||
|
||||
if this_slot == slot {
|
||||
@ -45,7 +50,7 @@ fn get_at_preceding_slot<T: Store>(
|
||||
} else if this_slot < slot {
|
||||
break Ok(None);
|
||||
} else {
|
||||
root = read_previous_block_root_from_block_bytes(&bytes)?;
|
||||
root = read_parent_root_from_block_bytes(&bytes)?;
|
||||
}
|
||||
} else {
|
||||
break Ok(None);
|
||||
@ -59,6 +64,8 @@ mod tests {
|
||||
use ssz::Encode;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
type BeaconBlock = types::BeaconBlock<MinimalEthSpec>;
|
||||
|
||||
#[test]
|
||||
fn read_slot() {
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
@ -84,17 +91,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_previous_block_root() {
|
||||
fn read_parent_root() {
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let test_root = |root: Hash256| {
|
||||
let mut block = BeaconBlock::empty(&spec);
|
||||
block.previous_block_root = root;
|
||||
block.parent_root = root;
|
||||
let bytes = block.as_ssz_bytes();
|
||||
assert_eq!(
|
||||
read_previous_block_root_from_block_bytes(&bytes).unwrap(),
|
||||
root
|
||||
);
|
||||
assert_eq!(read_parent_root_from_block_bytes(&bytes).unwrap(), root);
|
||||
};
|
||||
|
||||
test_root(Hash256::random());
|
||||
@ -114,7 +118,7 @@ mod tests {
|
||||
block.slot = Slot::from(*slot);
|
||||
|
||||
if i > 0 {
|
||||
block.previous_block_root = blocks_and_roots[i - 1].0;
|
||||
block.parent_root = blocks_and_roots[i - 1].0;
|
||||
}
|
||||
|
||||
let root = Hash256::from_slice(&block.tree_hash_root());
|
||||
@ -141,7 +145,7 @@ mod tests {
|
||||
let (target_root, target_block) = &blocks_and_roots[target];
|
||||
|
||||
let (found_root, found_block) = store
|
||||
.get_block_at_preceding_slot(*source_root, target_block.slot)
|
||||
.get_block_at_preceeding_slot(*source_root, target_block.slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
@ -166,7 +170,7 @@ mod tests {
|
||||
let (target_root, target_block) = &blocks_and_roots[target];
|
||||
|
||||
let (found_root, found_block) = store
|
||||
.get_block_at_preceding_slot(*source_root, target_block.slot)
|
||||
.get_block_at_preceeding_slot(*source_root, target_block.slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
@ -177,14 +181,14 @@ mod tests {
|
||||
// Slot that doesn't exist
|
||||
let (source_root, _source_block) = &blocks_and_roots[3];
|
||||
assert!(store
|
||||
.get_block_at_preceding_slot(*source_root, Slot::new(3))
|
||||
.get_block_at_preceeding_slot::<MinimalEthSpec>(*source_root, Slot::new(3))
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
// Slot too high
|
||||
let (source_root, _source_block) = &blocks_and_roots[3];
|
||||
assert!(store
|
||||
.get_block_at_preceding_slot(*source_root, Slot::new(3))
|
||||
.get_block_at_preceeding_slot::<MinimalEthSpec>(*source_root, Slot::new(3))
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use ssz::{Decode, Encode};
|
||||
|
||||
mod beacon_state;
|
||||
|
||||
impl StoreItem for BeaconBlock {
|
||||
impl<T: EthSpec> StoreItem for BeaconBlock<T> {
|
||||
fn db_column() -> DBColumn {
|
||||
DBColumn::BeaconBlock
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ pub trait AncestorIter<U: Store, I: Iterator> {
|
||||
fn try_iter_ancestor_roots(&self, store: Arc<U>) -> Option<I>;
|
||||
}
|
||||
|
||||
impl<'a, U: Store, E: EthSpec> AncestorIter<U, BestBlockRootsIterator<'a, E, U>> for BeaconBlock {
|
||||
impl<'a, U: Store, E: EthSpec> AncestorIter<U, BestBlockRootsIterator<'a, E, U>>
|
||||
for BeaconBlock<E>
|
||||
{
|
||||
/// Iterates across all the prior block roots of `self`, starting at the most recent and ending
|
||||
/// at genesis.
|
||||
fn try_iter_ancestor_roots(&self, store: Arc<U>) -> Option<BestBlockRootsIterator<'a, E, U>> {
|
||||
@ -98,7 +100,7 @@ impl<'a, T: EthSpec, U: Store> BlockIterator<'a, T, U> {
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> {
|
||||
type Item = BeaconBlock;
|
||||
type Item = BeaconBlock<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (root, _slot) = self.roots.next()?;
|
||||
@ -109,8 +111,8 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockIterator<'a, T, U> {
|
||||
/// Iterates backwards through block roots. If any specified slot is unable to be retrieved, the
|
||||
/// iterator returns `None` indefinitely.
|
||||
///
|
||||
/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will
|
||||
/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been
|
||||
/// Uses the `block_roots` field of `BeaconState` to as the source of block roots and will
|
||||
/// perform a lookup on the `Store` for a prior `BeaconState` if `block_roots` has been
|
||||
/// exhausted.
|
||||
///
|
||||
/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`.
|
||||
@ -191,8 +193,8 @@ impl<'a, T: EthSpec, U: Store> Iterator for BlockRootsIterator<'a, T, U> {
|
||||
///
|
||||
/// This is distinct from `BestBlockRootsIterator`.
|
||||
///
|
||||
/// Uses the `latest_block_roots` field of `BeaconState` to as the source of block roots and will
|
||||
/// perform a lookup on the `Store` for a prior `BeaconState` if `latest_block_roots` has been
|
||||
/// Uses the `block_roots` field of `BeaconState` to as the source of block roots and will
|
||||
/// perform a lookup on the `Store` for a prior `BeaconState` if `block_roots` has been
|
||||
/// exhausted.
|
||||
///
|
||||
/// Returns `None` for roots prior to genesis or when there is an error reading from `Store`.
|
||||
@ -305,15 +307,15 @@ mod test {
|
||||
|
||||
let mut hashes = (0..).into_iter().map(|i| Hash256::from(i));
|
||||
|
||||
for root in &mut state_a.latest_block_roots[..] {
|
||||
for root in &mut state_a.block_roots[..] {
|
||||
*root = hashes.next().unwrap()
|
||||
}
|
||||
for root in &mut state_b.latest_block_roots[..] {
|
||||
for root in &mut state_b.block_roots[..] {
|
||||
*root = hashes.next().unwrap()
|
||||
}
|
||||
|
||||
let state_a_root = hashes.next().unwrap();
|
||||
state_b.latest_state_roots[0] = state_a_root;
|
||||
state_b.state_roots[0] = state_a_root;
|
||||
store.put(&state_a_root, &state_a).unwrap();
|
||||
|
||||
let iter = BlockRootsIterator::new(store.clone(), &state_b, state_b.slot - 1);
|
||||
@ -348,15 +350,15 @@ mod test {
|
||||
|
||||
let mut hashes = (0..).into_iter().map(|i| Hash256::from(i));
|
||||
|
||||
for root in &mut state_a.latest_block_roots[..] {
|
||||
for root in &mut state_a.block_roots[..] {
|
||||
*root = hashes.next().unwrap()
|
||||
}
|
||||
for root in &mut state_b.latest_block_roots[..] {
|
||||
for root in &mut state_b.block_roots[..] {
|
||||
*root = hashes.next().unwrap()
|
||||
}
|
||||
|
||||
let state_a_root = hashes.next().unwrap();
|
||||
state_b.latest_state_roots[0] = state_a_root;
|
||||
state_b.state_roots[0] = state_a_root;
|
||||
store.put(&state_a_root, &state_a).unwrap();
|
||||
|
||||
let iter = BestBlockRootsIterator::new(store.clone(), &state_b, state_b.slot);
|
||||
|
@ -52,12 +52,12 @@ pub trait Store: Sync + Send + Sized {
|
||||
///
|
||||
/// Returns `None` if no parent block exists at that slot, or if `slot` is greater than the
|
||||
/// slot of `start_block_root`.
|
||||
fn get_block_at_preceding_slot(
|
||||
fn get_block_at_preceeding_slot<E: EthSpec>(
|
||||
&self,
|
||||
start_block_root: Hash256,
|
||||
slot: Slot,
|
||||
) -> Result<Option<(Hash256, BeaconBlock)>, Error> {
|
||||
block_at_slot::get_block_at_preceding_slot(self, slot, start_block_root)
|
||||
) -> Result<Option<(Hash256, BeaconBlock<E>)>, Error> {
|
||||
block_at_slot::get_block_at_preceeding_slot::<_, E>(self, slot, start_block_root)
|
||||
}
|
||||
|
||||
/// Retrieve some bytes in `column` with `key`.
|
||||
|
@ -14,8 +14,6 @@ Rust crates containing logic common across the Lighthouse project.
|
||||
`BeaconState`, etc).
|
||||
- [`utils/`](utils/):
|
||||
- [`bls`](utils/bls/): A wrapper for an external BLS encryption library.
|
||||
- [`boolean-bitfield`](utils/boolean-bitfield/): Provides an expandable vector
|
||||
of bools, specifically for use in Eth2.
|
||||
- [`fisher-yates-shuffle`](utils/fisher-yates-shuffle/): shuffles a list
|
||||
pseudo-randomly.
|
||||
- [`hashing`](utils/hashing/): A wrapper for external hashing libraries.
|
||||
|
@ -10,7 +10,7 @@ pub type Result<T> = std::result::Result<T, String>;
|
||||
|
||||
pub trait LmdGhost<S: Store, E: EthSpec>: Send + Sync {
|
||||
/// Create a new instance, with the given `store` and `finalized_root`.
|
||||
fn new(store: Arc<S>, finalized_block: &BeaconBlock, finalized_root: Hash256) -> Self;
|
||||
fn new(store: Arc<S>, finalized_block: &BeaconBlock<E>, finalized_root: Hash256) -> Self;
|
||||
|
||||
/// Process an attestation message from some validator that attests to some `block_hash`
|
||||
/// representing a block at some `block_slot`.
|
||||
@ -22,7 +22,7 @@ pub trait LmdGhost<S: Store, E: EthSpec>: Send + Sync {
|
||||
) -> Result<()>;
|
||||
|
||||
/// Process a block that was seen on the network.
|
||||
fn process_block(&self, block: &BeaconBlock, block_hash: Hash256) -> Result<()>;
|
||||
fn process_block(&self, block: &BeaconBlock<E>, block_hash: Hash256) -> Result<()>;
|
||||
|
||||
/// Returns the head of the chain, starting the search at `start_block_root` and moving upwards
|
||||
/// (in block height).
|
||||
@ -40,7 +40,7 @@ pub trait LmdGhost<S: Store, E: EthSpec>: Send + Sync {
|
||||
/// `finalized_block_root` must be the root of `finalized_block`.
|
||||
fn update_finalized_root(
|
||||
&self,
|
||||
finalized_block: &BeaconBlock,
|
||||
finalized_block: &BeaconBlock<E>,
|
||||
finalized_block_root: Hash256,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ where
|
||||
T: Store,
|
||||
E: EthSpec,
|
||||
{
|
||||
fn new(store: Arc<T>, genesis_block: &BeaconBlock, genesis_root: Hash256) -> Self {
|
||||
fn new(store: Arc<T>, genesis_block: &BeaconBlock<E>, genesis_root: Hash256) -> Self {
|
||||
ThreadSafeReducedTree {
|
||||
core: RwLock::new(ReducedTree::new(store, genesis_block, genesis_root)),
|
||||
}
|
||||
@ -77,7 +77,7 @@ where
|
||||
}
|
||||
|
||||
/// Process a block that was seen on the network.
|
||||
fn process_block(&self, block: &BeaconBlock, block_hash: Hash256) -> SuperResult<()> {
|
||||
fn process_block(&self, block: &BeaconBlock<E>, block_hash: Hash256) -> SuperResult<()> {
|
||||
self.core
|
||||
.write()
|
||||
.add_weightless_node(block.slot, block_hash)
|
||||
@ -99,7 +99,11 @@ where
|
||||
.map_err(|e| format!("find_head failed: {:?}", e))
|
||||
}
|
||||
|
||||
fn update_finalized_root(&self, new_block: &BeaconBlock, new_root: Hash256) -> SuperResult<()> {
|
||||
fn update_finalized_root(
|
||||
&self,
|
||||
new_block: &BeaconBlock<E>,
|
||||
new_root: Hash256,
|
||||
) -> SuperResult<()> {
|
||||
self.core
|
||||
.write()
|
||||
.update_root(new_block.slot, new_root)
|
||||
@ -129,7 +133,7 @@ where
|
||||
T: Store,
|
||||
E: EthSpec,
|
||||
{
|
||||
pub fn new(store: Arc<T>, genesis_block: &BeaconBlock, genesis_root: Hash256) -> Self {
|
||||
pub fn new(store: Arc<T>, genesis_block: &BeaconBlock<E>, genesis_root: Hash256) -> Self {
|
||||
let mut nodes = HashMap::new();
|
||||
|
||||
// Insert the genesis node.
|
||||
@ -309,7 +313,7 @@ where
|
||||
/// If the validator had a vote in the tree, the removal of that vote may cause a node to
|
||||
/// become redundant and removed from the reduced tree.
|
||||
fn remove_latest_message(&mut self, validator_index: usize) -> Result<()> {
|
||||
if let Some(vote) = self.latest_votes.get(validator_index).clone() {
|
||||
if let Some(vote) = *self.latest_votes.get(validator_index) {
|
||||
self.get_mut_node(vote.hash)?.remove_voter(validator_index);
|
||||
let node = self.get_node(vote.hash)?.clone();
|
||||
|
||||
@ -669,9 +673,9 @@ where
|
||||
.ok_or_else(|| Error::MissingNode(hash))
|
||||
}
|
||||
|
||||
fn get_block(&self, block_root: Hash256) -> Result<BeaconBlock> {
|
||||
fn get_block(&self, block_root: Hash256) -> Result<BeaconBlock<E>> {
|
||||
self.store
|
||||
.get::<BeaconBlock>(&block_root)?
|
||||
.get::<BeaconBlock<E>>(&block_root)?
|
||||
.ok_or_else(|| Error::MissingBlock(block_root))
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ struct ForkedHarness {
|
||||
/// don't expose it to avoid contamination between tests.
|
||||
harness: BeaconChainHarness,
|
||||
pub genesis_block_root: Hash256,
|
||||
pub genesis_block: BeaconBlock,
|
||||
pub genesis_block: BeaconBlock<TestEthSpec>,
|
||||
pub honest_head: RootAndSlot,
|
||||
pub faulty_head: RootAndSlot,
|
||||
pub honest_roots: Vec<RootAndSlot>,
|
||||
@ -101,7 +101,7 @@ impl ForkedHarness {
|
||||
let genesis_block = harness
|
||||
.chain
|
||||
.store
|
||||
.get::<BeaconBlock>(&genesis_block_root)
|
||||
.get::<BeaconBlock<TestEthSpec>>(&genesis_block_root)
|
||||
.expect("Genesis block should exist")
|
||||
.expect("DB should not error");
|
||||
|
||||
@ -155,11 +155,11 @@ fn get_ancestor_roots<E: EthSpec, U: Store>(
|
||||
block_root: Hash256,
|
||||
) -> Vec<(Hash256, Slot)> {
|
||||
let block = store
|
||||
.get::<BeaconBlock>(&block_root)
|
||||
.get::<BeaconBlock<TestEthSpec>>(&block_root)
|
||||
.expect("block should exist")
|
||||
.expect("store should not error");
|
||||
|
||||
<BeaconBlock as AncestorIter<_, BestBlockRootsIterator<E, _>>>::try_iter_ancestor_roots(
|
||||
<BeaconBlock<TestEthSpec> as AncestorIter<_, BestBlockRootsIterator<TestEthSpec, _>>>::try_iter_ancestor_roots(
|
||||
&block, store,
|
||||
)
|
||||
.expect("should be able to create ancestor iter")
|
||||
@ -171,7 +171,7 @@ fn get_slot_for_block_root(harness: &BeaconChainHarness, block_root: Hash256) ->
|
||||
harness
|
||||
.chain
|
||||
.store
|
||||
.get::<BeaconBlock>(&block_root)
|
||||
.get::<BeaconBlock<TestEthSpec>>(&block_root)
|
||||
.expect("head block should exist")
|
||||
.expect("DB should not error")
|
||||
.slot
|
||||
@ -328,7 +328,7 @@ fn test_update_finalized_root(roots: &[(Hash256, Slot)]) {
|
||||
for (root, _slot) in roots.iter().rev() {
|
||||
let block = harness
|
||||
.store_clone()
|
||||
.get::<BeaconBlock>(root)
|
||||
.get::<BeaconBlock<TestEthSpec>>(root)
|
||||
.expect("block should exist")
|
||||
.expect("db should not error");
|
||||
lmd.update_finalized_root(&block, *root)
|
||||
|
@ -5,7 +5,6 @@ authors = ["Michael Sproul <michael@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
boolean-bitfield = { path = "../utils/boolean-bitfield" }
|
||||
int_to_bytes = { path = "../utils/int_to_bytes" }
|
||||
itertools = "0.8"
|
||||
parking_lot = "0.7"
|
||||
@ -13,3 +12,6 @@ types = { path = "../types" }
|
||||
state_processing = { path = "../state_processing" }
|
||||
eth2_ssz = { path = "../utils/ssz" }
|
||||
eth2_ssz_derive = { path = "../utils/ssz_derive" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.5.5"
|
||||
|
@ -1,16 +1,18 @@
|
||||
use crate::max_cover::MaxCover;
|
||||
use boolean_bitfield::BooleanBitfield;
|
||||
use types::{Attestation, BeaconState, EthSpec};
|
||||
use types::{Attestation, BeaconState, BitList, EthSpec};
|
||||
|
||||
pub struct AttMaxCover<'a> {
|
||||
pub struct AttMaxCover<'a, T: EthSpec> {
|
||||
/// Underlying attestation.
|
||||
att: &'a Attestation,
|
||||
att: &'a Attestation<T>,
|
||||
/// Bitfield of validators that are covered by this attestation.
|
||||
fresh_validators: BooleanBitfield,
|
||||
fresh_validators: BitList<T::MaxValidatorsPerCommittee>,
|
||||
}
|
||||
|
||||
impl<'a> AttMaxCover<'a> {
|
||||
pub fn new(att: &'a Attestation, fresh_validators: BooleanBitfield) -> Self {
|
||||
impl<'a, T: EthSpec> AttMaxCover<'a, T> {
|
||||
pub fn new(
|
||||
att: &'a Attestation<T>,
|
||||
fresh_validators: BitList<T::MaxValidatorsPerCommittee>,
|
||||
) -> Self {
|
||||
Self {
|
||||
att,
|
||||
fresh_validators,
|
||||
@ -18,15 +20,15 @@ impl<'a> AttMaxCover<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MaxCover for AttMaxCover<'a> {
|
||||
type Object = Attestation;
|
||||
type Set = BooleanBitfield;
|
||||
impl<'a, T: EthSpec> MaxCover for AttMaxCover<'a, T> {
|
||||
type Object = Attestation<T>;
|
||||
type Set = BitList<T::MaxValidatorsPerCommittee>;
|
||||
|
||||
fn object(&self) -> Attestation {
|
||||
fn object(&self) -> Attestation<T> {
|
||||
self.att.clone()
|
||||
}
|
||||
|
||||
fn covering_set(&self) -> &BooleanBitfield {
|
||||
fn covering_set(&self) -> &BitList<T::MaxValidatorsPerCommittee> {
|
||||
&self.fresh_validators
|
||||
}
|
||||
|
||||
@ -37,11 +39,11 @@ impl<'a> MaxCover for AttMaxCover<'a> {
|
||||
/// that a shard and epoch uniquely identify a committee.
|
||||
fn update_covering_set(
|
||||
&mut self,
|
||||
best_att: &Attestation,
|
||||
covered_validators: &BooleanBitfield,
|
||||
best_att: &Attestation<T>,
|
||||
covered_validators: &BitList<T::MaxValidatorsPerCommittee>,
|
||||
) {
|
||||
if self.att.data.shard == best_att.data.shard
|
||||
&& self.att.data.target_epoch == best_att.data.target_epoch
|
||||
if self.att.data.crosslink.shard == best_att.data.crosslink.shard
|
||||
&& self.att.data.target.epoch == best_att.data.target.epoch
|
||||
{
|
||||
self.fresh_validators.difference_inplace(covered_validators);
|
||||
}
|
||||
@ -58,22 +60,22 @@ impl<'a> MaxCover for AttMaxCover<'a> {
|
||||
/// of validators for which the included attestation is their first in the epoch. The attestation
|
||||
/// is judged against the state's `current_epoch_attestations` or `previous_epoch_attestations`
|
||||
/// depending on when it was created, and all those validators who have already attested are
|
||||
/// removed from the `aggregation_bitfield` before returning it.
|
||||
/// removed from the `aggregation_bits` before returning it.
|
||||
// TODO: This could be optimised with a map from validator index to whether that validator has
|
||||
// attested in each of the current and previous epochs. Currently quadratic in number of validators.
|
||||
pub fn earliest_attestation_validators<T: EthSpec>(
|
||||
attestation: &Attestation,
|
||||
attestation: &Attestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
) -> BooleanBitfield {
|
||||
) -> BitList<T::MaxValidatorsPerCommittee> {
|
||||
// Bitfield of validators whose attestations are new/fresh.
|
||||
let mut new_validators = attestation.aggregation_bitfield.clone();
|
||||
let mut new_validators = attestation.aggregation_bits.clone();
|
||||
|
||||
let state_attestations = if attestation.data.target_epoch == state.current_epoch() {
|
||||
let state_attestations = if attestation.data.target.epoch == state.current_epoch() {
|
||||
&state.current_epoch_attestations
|
||||
} else if attestation.data.target_epoch == state.previous_epoch() {
|
||||
} else if attestation.data.target.epoch == state.previous_epoch() {
|
||||
&state.previous_epoch_attestations
|
||||
} else {
|
||||
return BooleanBitfield::from_elem(attestation.aggregation_bitfield.len(), false);
|
||||
return BitList::with_capacity(0).unwrap();
|
||||
};
|
||||
|
||||
state_attestations
|
||||
@ -81,10 +83,12 @@ pub fn earliest_attestation_validators<T: EthSpec>(
|
||||
// In a single epoch, an attester should only be attesting for one shard.
|
||||
// TODO: we avoid including slashable attestations in the state here,
|
||||
// but maybe we should do something else with them (like construct slashings).
|
||||
.filter(|existing_attestation| existing_attestation.data.shard == attestation.data.shard)
|
||||
.filter(|existing_attestation| {
|
||||
existing_attestation.data.crosslink.shard == attestation.data.crosslink.shard
|
||||
})
|
||||
.for_each(|existing_attestation| {
|
||||
// Remove the validators who have signed the existing attestation (they are not new)
|
||||
new_validators.difference_inplace(&existing_attestation.aggregation_bitfield);
|
||||
new_validators.difference_inplace(&existing_attestation.aggregation_bits);
|
||||
});
|
||||
|
||||
new_validators
|
||||
|
@ -19,7 +19,7 @@ impl AttestationId {
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let mut bytes = ssz_encode(attestation);
|
||||
let epoch = attestation.target_epoch;
|
||||
let epoch = attestation.target.epoch;
|
||||
bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, state, spec));
|
||||
AttestationId { v: bytes }
|
||||
}
|
||||
|
@ -15,22 +15,21 @@ use state_processing::per_block_processing::errors::{
|
||||
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
||||
};
|
||||
use state_processing::per_block_processing::{
|
||||
get_slashable_indices_modular, validate_attestation,
|
||||
validate_attestation_time_independent_only, verify_attester_slashing, verify_exit,
|
||||
verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer,
|
||||
verify_transfer_time_independent_only,
|
||||
get_slashable_indices_modular, verify_attestation, verify_attestation_time_independent_only,
|
||||
verify_attester_slashing, verify_exit, verify_exit_time_independent_only,
|
||||
verify_proposer_slashing, verify_transfer, verify_transfer_time_independent_only,
|
||||
};
|
||||
use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet};
|
||||
use std::marker::PhantomData;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, BeaconState, ChainSpec, Deposit, EthSpec, ProposerSlashing,
|
||||
Transfer, Validator, VoluntaryExit,
|
||||
typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, ChainSpec, Deposit, EthSpec,
|
||||
ProposerSlashing, Transfer, Validator, VoluntaryExit,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct OperationPool<T: EthSpec + Default> {
|
||||
/// Map from attestation ID (see below) to vectors of attestations.
|
||||
attestations: RwLock<HashMap<AttestationId, Vec<Attestation>>>,
|
||||
attestations: RwLock<HashMap<AttestationId, Vec<Attestation<T>>>>,
|
||||
/// Map from deposit index to deposit data.
|
||||
// NOTE: We assume that there is only one deposit per index
|
||||
// because the Eth1 data is updated (at most) once per epoch,
|
||||
@ -38,7 +37,7 @@ pub struct OperationPool<T: EthSpec + Default> {
|
||||
// longer than an epoch
|
||||
deposits: RwLock<BTreeMap<u64, Deposit>>,
|
||||
/// Map from two attestation IDs to a slashing for those IDs.
|
||||
attester_slashings: RwLock<HashMap<(AttestationId, AttestationId), AttesterSlashing>>,
|
||||
attester_slashings: RwLock<HashMap<(AttestationId, AttestationId), AttesterSlashing<T>>>,
|
||||
/// Map from proposer index to slashing.
|
||||
proposer_slashings: RwLock<HashMap<u64, ProposerSlashing>>,
|
||||
/// Map from exiting validator to their exit data.
|
||||
@ -67,12 +66,12 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
/// Insert an attestation into the pool, aggregating it with existing attestations if possible.
|
||||
pub fn insert_attestation(
|
||||
&self,
|
||||
attestation: Attestation,
|
||||
attestation: Attestation<T>,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), AttestationValidationError> {
|
||||
// Check that attestation signatures are valid.
|
||||
validate_attestation_time_independent_only(state, &attestation, spec)?;
|
||||
verify_attestation_time_independent_only(state, &attestation, spec)?;
|
||||
|
||||
let id = AttestationId::from_data(&attestation.data, state, spec);
|
||||
|
||||
@ -110,7 +109,11 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
}
|
||||
|
||||
/// Get a list of attestations for inclusion in a block.
|
||||
pub fn get_attestations(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Vec<Attestation> {
|
||||
pub fn get_attestations(
|
||||
&self,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Vec<Attestation<T>> {
|
||||
// Attestations for the current fork, which may be from the current or previous epoch.
|
||||
let prev_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
@ -125,10 +128,10 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
})
|
||||
.flat_map(|(_, attestations)| attestations)
|
||||
// That are valid...
|
||||
.filter(|attestation| validate_attestation(state, attestation, spec).is_ok())
|
||||
.filter(|attestation| verify_attestation(state, attestation, spec).is_ok())
|
||||
.map(|att| AttMaxCover::new(att, earliest_attestation_validators(att, state)));
|
||||
|
||||
maximum_cover(valid_attestations, spec.max_attestations as usize)
|
||||
maximum_cover(valid_attestations, T::MaxAttestations::to_usize())
|
||||
}
|
||||
|
||||
/// Remove attestations which are too old to be included in a block.
|
||||
@ -141,7 +144,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
// All the attestations in this bucket have the same data, so we only need to
|
||||
// check the first one.
|
||||
attestations.first().map_or(false, |att| {
|
||||
finalized_state.current_epoch() <= att.data.target_epoch + 1
|
||||
finalized_state.current_epoch() <= att.data.target.epoch + 1
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -149,13 +152,15 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
/// Add a deposit to the pool.
|
||||
///
|
||||
/// No two distinct deposits should be added with the same index.
|
||||
// TODO: we need to rethink this entirely
|
||||
pub fn insert_deposit(
|
||||
&self,
|
||||
index: u64,
|
||||
deposit: Deposit,
|
||||
) -> Result<DepositInsertStatus, DepositValidationError> {
|
||||
use DepositInsertStatus::*;
|
||||
|
||||
match self.deposits.write().entry(deposit.index) {
|
||||
match self.deposits.write().entry(index) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(deposit);
|
||||
Ok(Fresh)
|
||||
@ -173,12 +178,12 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
/// Get an ordered list of deposits for inclusion in a block.
|
||||
///
|
||||
/// Take at most the maximum number of deposits, beginning from the current deposit index.
|
||||
pub fn get_deposits(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Vec<Deposit> {
|
||||
pub fn get_deposits(&self, state: &BeaconState<T>) -> Vec<Deposit> {
|
||||
// TODO: We need to update the Merkle proofs for existing deposits as more deposits
|
||||
// are added. It probably makes sense to construct the proofs from scratch when forming
|
||||
// a block, using fresh info from the ETH1 chain for the current deposit root.
|
||||
let start_idx = state.deposit_index;
|
||||
(start_idx..start_idx + spec.max_deposits)
|
||||
let start_idx = state.eth1_deposit_index;
|
||||
(start_idx..start_idx + T::MaxDeposits::to_u64())
|
||||
.map(|idx| self.deposits.read().get(&idx).cloned())
|
||||
.take_while(Option::is_some)
|
||||
.flatten()
|
||||
@ -187,7 +192,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
|
||||
/// Remove all deposits with index less than the deposit index of the latest finalised block.
|
||||
pub fn prune_deposits(&self, state: &BeaconState<T>) -> BTreeMap<u64, Deposit> {
|
||||
let deposits_keep = self.deposits.write().split_off(&state.deposit_index);
|
||||
let deposits_keep = self.deposits.write().split_off(&state.eth1_deposit_index);
|
||||
std::mem::replace(&mut self.deposits.write(), deposits_keep)
|
||||
}
|
||||
|
||||
@ -216,7 +221,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
///
|
||||
/// Depends on the fork field of the state, but not on the state's epoch.
|
||||
fn attester_slashing_id(
|
||||
slashing: &AttesterSlashing,
|
||||
slashing: &AttesterSlashing<T>,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> (AttestationId, AttestationId) {
|
||||
@ -229,7 +234,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
/// Insert an attester slashing into the pool.
|
||||
pub fn insert_attester_slashing(
|
||||
&self,
|
||||
slashing: AttesterSlashing,
|
||||
slashing: AttesterSlashing<T>,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), AttesterSlashingValidationError> {
|
||||
@ -248,16 +253,16 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
&self,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> (Vec<ProposerSlashing>, Vec<AttesterSlashing>) {
|
||||
) -> (Vec<ProposerSlashing>, Vec<AttesterSlashing<T>>) {
|
||||
let proposer_slashings = filter_limit_operations(
|
||||
self.proposer_slashings.read().values(),
|
||||
|slashing| {
|
||||
state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(slashing.proposer_index as usize)
|
||||
.map_or(false, |validator| !validator.slashed)
|
||||
},
|
||||
spec.max_proposer_slashings,
|
||||
T::MaxProposerSlashings::to_usize(),
|
||||
);
|
||||
|
||||
// Set of validators to be slashed, so we don't attempt to construct invalid attester
|
||||
@ -291,7 +296,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
false
|
||||
}
|
||||
})
|
||||
.take(spec.max_attester_slashings as usize)
|
||||
.take(T::MaxAttesterSlashings::to_usize())
|
||||
.map(|(_, slashing)| slashing.clone())
|
||||
.collect();
|
||||
|
||||
@ -347,7 +352,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
filter_limit_operations(
|
||||
self.voluntary_exits.read().values(),
|
||||
|exit| verify_exit(state, exit, spec).is_ok(),
|
||||
spec.max_voluntary_exits,
|
||||
T::MaxVoluntaryExits::to_usize(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -384,7 +389,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
.iter()
|
||||
.filter(|transfer| verify_transfer(state, transfer, spec).is_ok())
|
||||
.sorted_by_key(|transfer| std::cmp::Reverse(transfer.fee))
|
||||
.take(spec.max_transfers as usize)
|
||||
.take(T::MaxTransfers::to_usize())
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
@ -408,7 +413,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
}
|
||||
|
||||
/// Filter up to a maximum number of operations out of an iterator.
|
||||
fn filter_limit_operations<'a, T: 'a, I, F>(operations: I, filter: F, limit: u64) -> Vec<T>
|
||||
fn filter_limit_operations<'a, T: 'a, I, F>(operations: I, filter: F, limit: usize) -> Vec<T>
|
||||
where
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
F: Fn(&T) -> bool,
|
||||
@ -417,7 +422,7 @@ where
|
||||
operations
|
||||
.into_iter()
|
||||
.filter(|x| filter(*x))
|
||||
.take(limit as usize)
|
||||
.take(limit)
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
@ -436,7 +441,7 @@ fn prune_validator_hash_map<T, F, E: EthSpec>(
|
||||
{
|
||||
map.retain(|&validator_index, _| {
|
||||
finalized_state
|
||||
.validator_registry
|
||||
.validators
|
||||
.get(validator_index as usize)
|
||||
.map_or(true, |validator| !prune_if(validator))
|
||||
});
|
||||
@ -458,6 +463,7 @@ impl<T: EthSpec + Default> PartialEq for OperationPool<T> {
|
||||
mod tests {
|
||||
use super::DepositInsertStatus::*;
|
||||
use super::*;
|
||||
use rand::Rng;
|
||||
use types::test_utils::*;
|
||||
use types::*;
|
||||
|
||||
@ -466,13 +472,16 @@ mod tests {
|
||||
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
||||
let op_pool = OperationPool::<MinimalEthSpec>::new();
|
||||
let deposit1 = make_deposit(rng);
|
||||
let mut deposit2 = make_deposit(rng);
|
||||
deposit2.index = deposit1.index;
|
||||
let deposit2 = make_deposit(rng);
|
||||
let index = rng.gen();
|
||||
|
||||
assert_eq!(op_pool.insert_deposit(deposit1.clone()), Ok(Fresh));
|
||||
assert_eq!(op_pool.insert_deposit(deposit1.clone()), Ok(Duplicate));
|
||||
assert_eq!(op_pool.insert_deposit(index, deposit1.clone()), Ok(Fresh));
|
||||
assert_eq!(
|
||||
op_pool.insert_deposit(deposit2),
|
||||
op_pool.insert_deposit(index, deposit1.clone()),
|
||||
Ok(Duplicate)
|
||||
);
|
||||
assert_eq!(
|
||||
op_pool.insert_deposit(index, deposit2),
|
||||
Ok(Replaced(Box::new(deposit1)))
|
||||
);
|
||||
}
|
||||
@ -480,28 +489,29 @@ mod tests {
|
||||
#[test]
|
||||
fn get_deposits_max() {
|
||||
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
||||
let (spec, mut state) = test_state(rng);
|
||||
let (_, mut state) = test_state(rng);
|
||||
let op_pool = OperationPool::new();
|
||||
let start = 10000;
|
||||
let max_deposits = spec.max_deposits;
|
||||
let max_deposits = <MainnetEthSpec as EthSpec>::MaxDeposits::to_u64();
|
||||
let extra = 5;
|
||||
let offset = 1;
|
||||
assert!(offset <= extra);
|
||||
|
||||
let deposits = dummy_deposits(rng, start, max_deposits + extra);
|
||||
|
||||
for deposit in &deposits {
|
||||
assert_eq!(op_pool.insert_deposit(deposit.clone()), Ok(Fresh));
|
||||
for (i, deposit) in &deposits {
|
||||
assert_eq!(op_pool.insert_deposit(*i, deposit.clone()), Ok(Fresh));
|
||||
}
|
||||
|
||||
state.deposit_index = start + offset;
|
||||
let deposits_for_block = op_pool.get_deposits(&state, &spec);
|
||||
state.eth1_deposit_index = start + offset;
|
||||
let deposits_for_block = op_pool.get_deposits(&state);
|
||||
|
||||
assert_eq!(deposits_for_block.len() as u64, max_deposits);
|
||||
assert_eq!(
|
||||
deposits_for_block[..],
|
||||
deposits[offset as usize..(offset + max_deposits) as usize]
|
||||
);
|
||||
let expected = deposits[offset as usize..(offset + max_deposits) as usize]
|
||||
.iter()
|
||||
.map(|(_, d)| d.clone())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(deposits_for_block[..], expected[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -518,20 +528,20 @@ mod tests {
|
||||
let deposits1 = dummy_deposits(rng, start1, count);
|
||||
let deposits2 = dummy_deposits(rng, start2, count);
|
||||
|
||||
for d in deposits1.into_iter().chain(deposits2) {
|
||||
assert!(op_pool.insert_deposit(d).is_ok());
|
||||
for (i, d) in deposits1.into_iter().chain(deposits2) {
|
||||
assert!(op_pool.insert_deposit(i, d).is_ok());
|
||||
}
|
||||
|
||||
assert_eq!(op_pool.num_deposits(), 2 * count as usize);
|
||||
|
||||
let mut state = BeaconState::random_for_test(rng);
|
||||
state.deposit_index = start1;
|
||||
state.eth1_deposit_index = start1;
|
||||
|
||||
// Pruning the first bunch of deposits in batches of 5 should work.
|
||||
let step = 5;
|
||||
let mut pool_size = step + 2 * count as usize;
|
||||
for i in (start1..=(start1 + count)).step_by(step) {
|
||||
state.deposit_index = i;
|
||||
state.eth1_deposit_index = i;
|
||||
op_pool.prune_deposits(&state);
|
||||
pool_size -= step;
|
||||
assert_eq!(op_pool.num_deposits(), pool_size);
|
||||
@ -539,14 +549,14 @@ mod tests {
|
||||
assert_eq!(pool_size, count as usize);
|
||||
// Pruning in the gap should do nothing.
|
||||
for i in (start1 + count..start2).step_by(step) {
|
||||
state.deposit_index = i;
|
||||
state.eth1_deposit_index = i;
|
||||
op_pool.prune_deposits(&state);
|
||||
assert_eq!(op_pool.num_deposits(), count as usize);
|
||||
}
|
||||
// Same again for the later deposits.
|
||||
pool_size += step;
|
||||
for i in (start2..=(start2 + count)).step_by(step) {
|
||||
state.deposit_index = i;
|
||||
state.eth1_deposit_index = i;
|
||||
op_pool.prune_deposits(&state);
|
||||
pool_size -= step;
|
||||
assert_eq!(op_pool.num_deposits(), pool_size);
|
||||
@ -560,13 +570,13 @@ mod tests {
|
||||
}
|
||||
|
||||
// Create `count` dummy deposits with sequential deposit IDs beginning from `start`.
|
||||
fn dummy_deposits(rng: &mut XorShiftRng, start: u64, count: u64) -> Vec<Deposit> {
|
||||
fn dummy_deposits(rng: &mut XorShiftRng, start: u64, count: u64) -> Vec<(u64, Deposit)> {
|
||||
let proto_deposit = make_deposit(rng);
|
||||
(start..start + count)
|
||||
.map(|index| {
|
||||
let mut deposit = proto_deposit.clone();
|
||||
deposit.index = index;
|
||||
deposit
|
||||
deposit.data.amount = index * 1000;
|
||||
(index, deposit)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -596,11 +606,11 @@ mod tests {
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
extra_signer: Option<usize>,
|
||||
) -> Attestation {
|
||||
) -> Attestation<E> {
|
||||
let mut builder = TestingAttestationBuilder::new(state, committee, slot, shard, spec);
|
||||
let signers = &committee[signing_range];
|
||||
let committee_keys = signers.iter().map(|&i| &keypairs[i].sk).collect::<Vec<_>>();
|
||||
builder.sign(signers, &committee_keys, &state.fork, spec);
|
||||
builder.sign(signers, &committee_keys, &state.fork, spec, false);
|
||||
extra_signer.map(|c_idx| {
|
||||
let validator_index = committee[c_idx];
|
||||
builder.sign(
|
||||
@ -608,6 +618,7 @@ mod tests {
|
||||
&[&keypairs[validator_index].sk],
|
||||
&state.fork,
|
||||
spec,
|
||||
false,
|
||||
)
|
||||
});
|
||||
builder.build()
|
||||
@ -668,15 +679,18 @@ mod tests {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
att1.aggregation_bitfield.num_set_bits(),
|
||||
att1.aggregation_bits.num_set_bits(),
|
||||
earliest_attestation_validators(&att1, state).num_set_bits()
|
||||
);
|
||||
state.current_epoch_attestations.push(PendingAttestation {
|
||||
aggregation_bitfield: att1.aggregation_bitfield.clone(),
|
||||
data: att1.data.clone(),
|
||||
inclusion_delay: 0,
|
||||
proposer_index: 0,
|
||||
});
|
||||
state
|
||||
.current_epoch_attestations
|
||||
.push(PendingAttestation {
|
||||
aggregation_bits: att1.aggregation_bits.clone(),
|
||||
data: att1.data.clone(),
|
||||
inclusion_delay: 0,
|
||||
proposer_index: 0,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
cc.committee.len() - 2,
|
||||
@ -728,6 +742,7 @@ mod tests {
|
||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||
|
||||
// Before the min attestation inclusion delay, get_attestations shouldn't return anything.
|
||||
state.slot -= 1;
|
||||
assert_eq!(op_pool.get_attestations(state, spec).len(), 0);
|
||||
|
||||
// Then once the delay has elapsed, we should get a single aggregated attestation.
|
||||
@ -738,7 +753,7 @@ mod tests {
|
||||
|
||||
let agg_att = &block_attestations[0];
|
||||
assert_eq!(
|
||||
agg_att.aggregation_bitfield.num_set_bits(),
|
||||
agg_att.aggregation_bits.num_set_bits(),
|
||||
spec.target_committee_size as usize
|
||||
);
|
||||
|
||||
@ -854,7 +869,7 @@ mod tests {
|
||||
.map(CrosslinkCommittee::into_owned)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let max_attestations = spec.max_attestations as usize;
|
||||
let max_attestations = <MainnetEthSpec as EthSpec>::MaxAttestations::to_usize();
|
||||
let target_committee_size = spec.target_committee_size as usize;
|
||||
|
||||
let insert_attestations = |cc: &OwnedCrosslinkCommittee, step_size| {
|
||||
@ -897,7 +912,7 @@ mod tests {
|
||||
|
||||
// All the best attestations should be signed by at least `big_step_size` (4) validators.
|
||||
for att in &best_attestations {
|
||||
assert!(att.aggregation_bitfield.num_set_bits() >= big_step_size);
|
||||
assert!(att.aggregation_bits.num_set_bits() >= big_step_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ impl<T> MaxCoverItem<T> {
|
||||
///
|
||||
/// * Time complexity: `O(limit * items_iter.len())`
|
||||
/// * Space complexity: `O(item_iter.len())`
|
||||
pub fn maximum_cover<'a, I, T>(items_iter: I, limit: usize) -> Vec<T::Object>
|
||||
pub fn maximum_cover<I, T>(items_iter: I, limit: usize) -> Vec<T::Object>
|
||||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: MaxCover,
|
||||
|
@ -9,14 +9,14 @@ use types::*;
|
||||
/// Operations are stored in arbitrary order, so it's not a good idea to compare instances
|
||||
/// of this type (or its encoded form) for equality. Convert back to an `OperationPool` first.
|
||||
#[derive(Encode, Decode)]
|
||||
pub struct PersistedOperationPool {
|
||||
pub struct PersistedOperationPool<T: EthSpec> {
|
||||
/// Mapping from attestation ID to attestation mappings.
|
||||
// We could save space by not storing the attestation ID, but it might
|
||||
// be difficult to make that roundtrip due to eager aggregation.
|
||||
attestations: Vec<(AttestationId, Vec<Attestation>)>,
|
||||
deposits: Vec<Deposit>,
|
||||
attestations: Vec<(AttestationId, Vec<Attestation<T>>)>,
|
||||
deposits: Vec<(u64, Deposit)>,
|
||||
/// Attester slashings.
|
||||
attester_slashings: Vec<AttesterSlashing>,
|
||||
attester_slashings: Vec<AttesterSlashing<T>>,
|
||||
/// Proposer slashings.
|
||||
proposer_slashings: Vec<ProposerSlashing>,
|
||||
/// Voluntary exits.
|
||||
@ -25,9 +25,9 @@ pub struct PersistedOperationPool {
|
||||
transfers: Vec<Transfer>,
|
||||
}
|
||||
|
||||
impl PersistedOperationPool {
|
||||
impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
/// Convert an `OperationPool` into serializable form.
|
||||
pub fn from_operation_pool<T: EthSpec>(operation_pool: &OperationPool<T>) -> Self {
|
||||
pub fn from_operation_pool(operation_pool: &OperationPool<T>) -> Self {
|
||||
let attestations = operation_pool
|
||||
.attestations
|
||||
.read()
|
||||
@ -39,7 +39,7 @@ impl PersistedOperationPool {
|
||||
.deposits
|
||||
.read()
|
||||
.iter()
|
||||
.map(|(_, d)| d.clone())
|
||||
.map(|(index, d)| (*index, d.clone()))
|
||||
.collect();
|
||||
|
||||
let attester_slashings = operation_pool
|
||||
@ -76,13 +76,9 @@ impl PersistedOperationPool {
|
||||
}
|
||||
|
||||
/// Reconstruct an `OperationPool`.
|
||||
pub fn into_operation_pool<T: EthSpec>(
|
||||
self,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> OperationPool<T> {
|
||||
pub fn into_operation_pool(self, state: &BeaconState<T>, spec: &ChainSpec) -> OperationPool<T> {
|
||||
let attestations = RwLock::new(self.attestations.into_iter().collect());
|
||||
let deposits = RwLock::new(self.deposits.into_iter().map(|d| (d.index, d)).collect());
|
||||
let deposits = RwLock::new(self.deposits.into_iter().collect());
|
||||
let attester_slashings = RwLock::new(
|
||||
self.attester_slashings
|
||||
.into_iter()
|
||||
|
@ -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)?;
|
||||
|
||||
|
@ -6,14 +6,12 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
bls = { path = "../utils/bls" }
|
||||
boolean-bitfield = { path = "../utils/boolean-bitfield" }
|
||||
cached_tree_hash = { path = "../utils/cached_tree_hash" }
|
||||
compare_fields = { path = "../utils/compare_fields" }
|
||||
compare_fields_derive = { path = "../utils/compare_fields_derive" }
|
||||
dirs = "1.0"
|
||||
derivative = "1.0"
|
||||
ethereum-types = "0.5"
|
||||
fixed_len_vec = { path = "../utils/fixed_len_vec" }
|
||||
hashing = { path = "../utils/hashing" }
|
||||
hex = "0.3"
|
||||
int_to_bytes = { path = "../utils/int_to_bytes" }
|
||||
@ -25,6 +23,7 @@ serde_derive = "1.0"
|
||||
slog = "^2.2.3"
|
||||
eth2_ssz = { path = "../utils/ssz" }
|
||||
eth2_ssz_derive = { path = "../utils/ssz_derive" }
|
||||
eth2_ssz_types = { path = "../utils/ssz_types" }
|
||||
swap_or_not_shuffle = { path = "../utils/swap_or_not_shuffle" }
|
||||
test_random_derive = { path = "../utils/test_random_derive" }
|
||||
tree_hash = { path = "../utils/tree_hash" }
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{AggregateSignature, AttestationData, Bitfield};
|
||||
use super::{AggregateSignature, AttestationData, BitList, EthSpec};
|
||||
use crate::test_utils::TestRandom;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// Details an attestation that can be slashable.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -23,32 +23,32 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct Attestation {
|
||||
pub aggregation_bitfield: Bitfield,
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct Attestation<T: EthSpec> {
|
||||
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
pub data: AttestationData,
|
||||
pub custody_bitfield: Bitfield,
|
||||
pub custody_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl Attestation {
|
||||
impl<T: EthSpec> Attestation<T> {
|
||||
/// Are the aggregation bitfields of these attestations disjoint?
|
||||
pub fn signers_disjoint_from(&self, other: &Attestation) -> bool {
|
||||
self.aggregation_bitfield
|
||||
.intersection(&other.aggregation_bitfield)
|
||||
pub fn signers_disjoint_from(&self, other: &Self) -> bool {
|
||||
self.aggregation_bits
|
||||
.intersection(&other.aggregation_bits)
|
||||
.is_zero()
|
||||
}
|
||||
|
||||
/// Aggregate another Attestation into this one.
|
||||
///
|
||||
/// The aggregation bitfields must be disjoint, and the data must be the same.
|
||||
pub fn aggregate(&mut self, other: &Attestation) {
|
||||
pub fn aggregate(&mut self, other: &Self) {
|
||||
debug_assert_eq!(self.data, other.data);
|
||||
debug_assert!(self.signers_disjoint_from(other));
|
||||
|
||||
self.aggregation_bitfield
|
||||
.union_inplace(&other.aggregation_bitfield);
|
||||
self.custody_bitfield.union_inplace(&other.custody_bitfield);
|
||||
self.aggregation_bits = self.aggregation_bits.union(&other.aggregation_bits);
|
||||
self.custody_bits = self.custody_bits.union(&other.custody_bits);
|
||||
self.signature.add_aggregate(&other.signature);
|
||||
}
|
||||
}
|
||||
@ -56,7 +56,8 @@ impl Attestation {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
ssz_tests!(Attestation);
|
||||
cached_tree_hash_tests!(Attestation);
|
||||
ssz_tests!(Attestation<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(Attestation<MainnetEthSpec>);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Epoch, Hash256};
|
||||
use crate::{Checkpoint, Crosslink, Hash256};
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@ -9,13 +9,12 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// The data upon which an attestation is based.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Hash,
|
||||
@ -31,15 +30,11 @@ pub struct AttestationData {
|
||||
pub beacon_block_root: Hash256,
|
||||
|
||||
// FFG Vote
|
||||
pub source_epoch: Epoch,
|
||||
pub source_root: Hash256,
|
||||
pub target_epoch: Epoch,
|
||||
pub target_root: Hash256,
|
||||
pub source: Checkpoint,
|
||||
pub target: Checkpoint,
|
||||
|
||||
// Crosslink Vote
|
||||
pub shard: u64,
|
||||
pub previous_crosslink_root: Hash256,
|
||||
pub crosslink_data_root: Hash256,
|
||||
pub crosslink: Crosslink,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -7,12 +7,11 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Used for pairing an attestation with a proof-of-custody.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{test_utils::TestRandom, IndexedAttestation};
|
||||
use crate::{test_utils::TestRandom, EthSpec, 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.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -20,15 +20,17 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct AttesterSlashing {
|
||||
pub attestation_1: IndexedAttestation,
|
||||
pub attestation_2: IndexedAttestation,
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct AttesterSlashing<T: EthSpec> {
|
||||
pub attestation_1: IndexedAttestation<T>,
|
||||
pub attestation_2: IndexedAttestation<T>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
ssz_tests!(AttesterSlashing);
|
||||
cached_tree_hash_tests!(AttesterSlashing);
|
||||
ssz_tests!(AttesterSlashing<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(AttesterSlashing<MainnetEthSpec>);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// A block of the `BeaconChain`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -24,38 +24,39 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct BeaconBlock {
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlock<T: EthSpec> {
|
||||
pub slot: Slot,
|
||||
pub previous_block_root: Hash256,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
pub body: BeaconBlockBody,
|
||||
pub body: BeaconBlockBody<T>,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: Signature,
|
||||
}
|
||||
|
||||
impl BeaconBlock {
|
||||
impl<T: EthSpec> BeaconBlock<T> {
|
||||
/// Returns an empty block to be used during genesis.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn empty(spec: &ChainSpec) -> BeaconBlock {
|
||||
/// Spec v0.8.1
|
||||
pub fn empty(spec: &ChainSpec) -> Self {
|
||||
BeaconBlock {
|
||||
slot: spec.genesis_slot,
|
||||
previous_block_root: spec.zero_hash,
|
||||
state_root: spec.zero_hash,
|
||||
parent_root: Hash256::zero(),
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal: Signature::empty_signature(),
|
||||
eth1_data: Eth1Data {
|
||||
deposit_root: spec.zero_hash,
|
||||
block_hash: spec.zero_hash,
|
||||
deposit_root: Hash256::zero(),
|
||||
block_hash: Hash256::zero(),
|
||||
deposit_count: 0,
|
||||
},
|
||||
graffiti: [0; 32],
|
||||
proposer_slashings: vec![],
|
||||
attester_slashings: vec![],
|
||||
attestations: vec![],
|
||||
deposits: vec![],
|
||||
voluntary_exits: vec![],
|
||||
transfers: vec![],
|
||||
proposer_slashings: VariableList::empty(),
|
||||
attester_slashings: VariableList::empty(),
|
||||
attestations: VariableList::empty(),
|
||||
deposits: VariableList::empty(),
|
||||
voluntary_exits: VariableList::empty(),
|
||||
transfers: VariableList::empty(),
|
||||
},
|
||||
signature: Signature::empty_signature(),
|
||||
}
|
||||
@ -63,7 +64,7 @@ impl BeaconBlock {
|
||||
|
||||
/// Returns the `signed_root` of the block.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.signed_root()[..])
|
||||
}
|
||||
@ -75,23 +76,23 @@ impl BeaconBlock {
|
||||
///
|
||||
/// Note: performs a full tree-hash of `self.body`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
slot: self.slot,
|
||||
previous_block_root: self.previous_block_root,
|
||||
parent_root: self.parent_root,
|
||||
state_root: self.state_root,
|
||||
block_body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]),
|
||||
body_root: Hash256::from_slice(&self.body.tree_hash_root()[..]),
|
||||
signature: self.signature.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a "temporary" header, where the `state_root` is `spec.zero_hash`.
|
||||
/// Returns a "temporary" header, where the `state_root` is `Hash256::zero()`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn temporary_block_header(&self, spec: &ChainSpec) -> BeaconBlockHeader {
|
||||
/// Spec v0.8.0
|
||||
pub fn temporary_block_header(&self) -> BeaconBlockHeader {
|
||||
BeaconBlockHeader {
|
||||
state_root: spec.zero_hash,
|
||||
state_root: Hash256::zero(),
|
||||
signature: Signature::empty_signature(),
|
||||
..self.block_header()
|
||||
}
|
||||
@ -102,6 +103,6 @@ impl BeaconBlock {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_tests!(BeaconBlock);
|
||||
cached_tree_hash_tests!(BeaconBlock);
|
||||
ssz_tests!(BeaconBlock<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(BeaconBlock<MainnetEthSpec>);
|
||||
}
|
||||
|
@ -3,12 +3,13 @@ use crate::*;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::VariableList;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// The body of a `BeaconChain` block, containing operations.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -21,23 +22,24 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct BeaconBlockBody {
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconBlockBody<T: EthSpec> {
|
||||
pub randao_reveal: Signature,
|
||||
pub eth1_data: Eth1Data,
|
||||
#[serde(deserialize_with = "graffiti_from_hex_str")]
|
||||
pub graffiti: [u8; 32],
|
||||
pub proposer_slashings: Vec<ProposerSlashing>,
|
||||
pub attester_slashings: Vec<AttesterSlashing>,
|
||||
pub attestations: Vec<Attestation>,
|
||||
pub deposits: Vec<Deposit>,
|
||||
pub voluntary_exits: Vec<VoluntaryExit>,
|
||||
pub transfers: Vec<Transfer>,
|
||||
pub proposer_slashings: VariableList<ProposerSlashing, T::MaxProposerSlashings>,
|
||||
pub attester_slashings: VariableList<AttesterSlashing<T>, T::MaxAttesterSlashings>,
|
||||
pub attestations: VariableList<Attestation<T>, T::MaxAttestations>,
|
||||
pub deposits: VariableList<Deposit, T::MaxDeposits>,
|
||||
pub voluntary_exits: VariableList<VoluntaryExit, T::MaxVoluntaryExits>,
|
||||
pub transfers: VariableList<Transfer, T::MaxTransfers>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_tests!(BeaconBlockBody);
|
||||
cached_tree_hash_tests!(BeaconBlockBody);
|
||||
ssz_tests!(BeaconBlockBody<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(BeaconBlockBody<MainnetEthSpec>);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// A header of a `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -26,9 +26,9 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
)]
|
||||
pub struct BeaconBlockHeader {
|
||||
pub slot: Slot,
|
||||
pub previous_block_root: Hash256,
|
||||
pub parent_root: Hash256,
|
||||
pub state_root: Hash256,
|
||||
pub block_body_root: Hash256,
|
||||
pub body_root: Hash256,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: Signature,
|
||||
}
|
||||
@ -36,18 +36,18 @@ pub struct BeaconBlockHeader {
|
||||
impl BeaconBlockHeader {
|
||||
/// Returns the `tree_hash_root` of the header.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.signed_root()[..])
|
||||
}
|
||||
|
||||
/// Given a `body`, consumes `self` and returns a complete `BeaconBlock`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn into_block(self, body: BeaconBlockBody) -> BeaconBlock {
|
||||
/// Spec v0.8.0
|
||||
pub fn into_block<T: EthSpec>(self, body: BeaconBlockBody<T>) -> BeaconBlock<T> {
|
||||
BeaconBlock {
|
||||
slot: self.slot,
|
||||
previous_block_root: self.previous_block_root,
|
||||
parent_root: self.parent_root,
|
||||
state_root: self.state_root,
|
||||
body,
|
||||
signature: self.signature,
|
||||
|
@ -4,13 +4,13 @@ use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
use cached_tree_hash::{Error as TreeHashCacheError, TreeHashCache};
|
||||
use compare_fields_derive::CompareFields;
|
||||
use fixed_len_vec::{typenum::Unsigned, FixedLenVec};
|
||||
use hashing::hash;
|
||||
use int_to_bytes::{int_to_bytes32, int_to_bytes8};
|
||||
use pubkey_cache::PubkeyCache;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz::ssz_encode;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::{typenum::Unsigned, BitVector, FixedVector};
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
@ -18,6 +18,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
pub use self::committee_cache::CommitteeCache;
|
||||
pub use beacon_state_types::*;
|
||||
|
||||
#[macro_use]
|
||||
mod beacon_state_types;
|
||||
mod committee_cache;
|
||||
mod exit_cache;
|
||||
@ -44,7 +45,6 @@ pub enum Error {
|
||||
InsufficientIndexRoots,
|
||||
InsufficientAttestations,
|
||||
InsufficientCommittees,
|
||||
InsufficientSlashedBalances,
|
||||
InsufficientStateRoots,
|
||||
NoCommitteeForShard,
|
||||
NoCommitteeForSlot,
|
||||
@ -59,11 +59,12 @@ pub enum Error {
|
||||
RelativeEpochError(RelativeEpochError),
|
||||
CommitteeCacheUninitialized(RelativeEpoch),
|
||||
TreeHashCacheError(TreeHashCacheError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
}
|
||||
|
||||
/// The state of the `BeaconChain` at some slot.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -74,55 +75,63 @@ pub enum Error {
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
CompareFields,
|
||||
CachedTreeHash,
|
||||
)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BeaconState<T>
|
||||
where
|
||||
T: EthSpec,
|
||||
{
|
||||
// Misc
|
||||
pub slot: Slot,
|
||||
// Versioning
|
||||
pub genesis_time: u64,
|
||||
pub slot: Slot,
|
||||
pub fork: Fork,
|
||||
|
||||
// Validator registry
|
||||
#[compare_fields(as_slice)]
|
||||
pub validator_registry: Vec<Validator>,
|
||||
#[compare_fields(as_slice)]
|
||||
pub balances: Vec<u64>,
|
||||
|
||||
// Randomness and committees
|
||||
pub latest_randao_mixes: FixedLenVec<Hash256, T::LatestRandaoMixesLength>,
|
||||
pub latest_start_shard: u64,
|
||||
|
||||
// Finality
|
||||
pub previous_epoch_attestations: Vec<PendingAttestation>,
|
||||
pub current_epoch_attestations: Vec<PendingAttestation>,
|
||||
pub previous_justified_epoch: Epoch,
|
||||
pub current_justified_epoch: Epoch,
|
||||
pub previous_justified_root: Hash256,
|
||||
pub current_justified_root: Hash256,
|
||||
pub justification_bitfield: u64,
|
||||
pub finalized_epoch: Epoch,
|
||||
pub finalized_root: Hash256,
|
||||
|
||||
// Recent state
|
||||
pub current_crosslinks: FixedLenVec<Crosslink, T::ShardCount>,
|
||||
pub previous_crosslinks: FixedLenVec<Crosslink, T::ShardCount>,
|
||||
pub latest_block_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
#[compare_fields(as_slice)]
|
||||
pub latest_state_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
#[compare_fields(as_slice)]
|
||||
latest_active_index_roots: FixedLenVec<Hash256, T::LatestActiveIndexRootsLength>,
|
||||
latest_slashed_balances: FixedLenVec<u64, T::LatestSlashedExitLength>,
|
||||
// History
|
||||
pub latest_block_header: BeaconBlockHeader,
|
||||
pub historical_roots: Vec<Hash256>,
|
||||
#[compare_fields(as_slice)]
|
||||
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
#[compare_fields(as_slice)]
|
||||
pub state_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
pub historical_roots: VariableList<Hash256, T::HistoricalRootsLimit>,
|
||||
|
||||
// Ethereum 1.0 chain data
|
||||
pub latest_eth1_data: Eth1Data,
|
||||
pub eth1_data_votes: Vec<Eth1Data>,
|
||||
pub deposit_index: u64,
|
||||
pub eth1_data: Eth1Data,
|
||||
pub eth1_data_votes: VariableList<Eth1Data, T::SlotsPerEth1VotingPeriod>,
|
||||
pub eth1_deposit_index: u64,
|
||||
|
||||
// Registry
|
||||
#[compare_fields(as_slice)]
|
||||
pub validators: VariableList<Validator, T::ValidatorRegistryLimit>,
|
||||
#[compare_fields(as_slice)]
|
||||
pub balances: VariableList<u64, T::ValidatorRegistryLimit>,
|
||||
|
||||
// Shuffling
|
||||
pub start_shard: u64,
|
||||
pub randao_mixes: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
#[compare_fields(as_slice)]
|
||||
active_index_roots: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
#[compare_fields(as_slice)]
|
||||
compact_committees_roots: FixedVector<Hash256, T::EpochsPerHistoricalVector>,
|
||||
|
||||
// Slashings
|
||||
slashings: FixedVector<u64, T::EpochsPerSlashingsVector>,
|
||||
|
||||
// Attestations
|
||||
pub previous_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
|
||||
pub current_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
|
||||
|
||||
// Crosslinks
|
||||
pub previous_crosslinks: FixedVector<Crosslink, T::ShardCount>,
|
||||
pub current_crosslinks: FixedVector<Crosslink, T::ShardCount>,
|
||||
|
||||
// Finality
|
||||
#[test_random(default)]
|
||||
pub justification_bits: BitVector<T::JustificationBitsLength>,
|
||||
pub previous_justified_checkpoint: Checkpoint,
|
||||
pub current_justified_checkpoint: Checkpoint,
|
||||
pub finalized_checkpoint: Checkpoint,
|
||||
|
||||
// Caching (not in the spec)
|
||||
#[serde(default)]
|
||||
@ -152,75 +161,57 @@ where
|
||||
}
|
||||
|
||||
impl<T: EthSpec> BeaconState<T> {
|
||||
/// Produce the first state of the Beacon Chain.
|
||||
/// Create a new BeaconState suitable for genesis.
|
||||
///
|
||||
/// This does not fully build a genesis beacon state, it omits processing of initial validator
|
||||
/// deposits. To obtain a full genesis beacon state, use the `BeaconStateBuilder`.
|
||||
/// Not a complete genesis state, see `initialize_beacon_state_from_eth1`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn genesis(
|
||||
genesis_time: u64,
|
||||
latest_eth1_data: Eth1Data,
|
||||
spec: &ChainSpec,
|
||||
) -> BeaconState<T> {
|
||||
let initial_crosslink = Crosslink {
|
||||
epoch: T::genesis_epoch(),
|
||||
previous_crosslink_root: spec.zero_hash,
|
||||
crosslink_data_root: spec.zero_hash,
|
||||
};
|
||||
|
||||
/// Spec v0.8.0
|
||||
pub fn new(genesis_time: u64, eth1_data: Eth1Data, spec: &ChainSpec) -> Self {
|
||||
BeaconState {
|
||||
// Misc
|
||||
slot: spec.genesis_slot,
|
||||
// Versioning
|
||||
genesis_time,
|
||||
slot: spec.genesis_slot,
|
||||
fork: Fork::genesis(T::genesis_epoch()),
|
||||
|
||||
// Validator registry
|
||||
validator_registry: vec![], // Set later in the function.
|
||||
balances: vec![], // Set later in the function.
|
||||
// History
|
||||
latest_block_header: BeaconBlock::<T>::empty(spec).temporary_block_header(),
|
||||
block_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
state_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
historical_roots: VariableList::empty(),
|
||||
|
||||
// Randomness and committees
|
||||
latest_randao_mixes: FixedLenVec::from(vec![
|
||||
spec.zero_hash;
|
||||
T::LatestRandaoMixesLength::to_usize()
|
||||
]),
|
||||
latest_start_shard: 0,
|
||||
// Eth1
|
||||
eth1_data,
|
||||
eth1_data_votes: VariableList::empty(),
|
||||
eth1_deposit_index: 0,
|
||||
|
||||
// Validator registry
|
||||
validators: VariableList::empty(), // Set later.
|
||||
balances: VariableList::empty(), // Set later.
|
||||
|
||||
// Shuffling
|
||||
start_shard: 0,
|
||||
randao_mixes: FixedVector::from_elem(Hash256::zero()),
|
||||
active_index_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
compact_committees_roots: FixedVector::from_elem(Hash256::zero()),
|
||||
|
||||
// Slashings
|
||||
slashings: FixedVector::from_elem(0),
|
||||
|
||||
// Attestations
|
||||
previous_epoch_attestations: VariableList::empty(),
|
||||
current_epoch_attestations: VariableList::empty(),
|
||||
|
||||
// Crosslinks
|
||||
previous_crosslinks: FixedVector::from_elem(Crosslink::default()),
|
||||
current_crosslinks: FixedVector::from_elem(Crosslink::default()),
|
||||
|
||||
// Finality
|
||||
previous_epoch_attestations: vec![],
|
||||
current_epoch_attestations: vec![],
|
||||
previous_justified_epoch: T::genesis_epoch(),
|
||||
current_justified_epoch: T::genesis_epoch(),
|
||||
previous_justified_root: spec.zero_hash,
|
||||
current_justified_root: spec.zero_hash,
|
||||
justification_bitfield: 0,
|
||||
finalized_epoch: T::genesis_epoch(),
|
||||
finalized_root: spec.zero_hash,
|
||||
justification_bits: BitVector::new(),
|
||||
previous_justified_checkpoint: Checkpoint::default(),
|
||||
current_justified_checkpoint: Checkpoint::default(),
|
||||
finalized_checkpoint: Checkpoint::default(),
|
||||
|
||||
// Recent state
|
||||
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::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![],
|
||||
|
||||
/*
|
||||
* PoW receipt root
|
||||
*/
|
||||
latest_eth1_data,
|
||||
eth1_data_votes: vec![],
|
||||
deposit_index: 0,
|
||||
|
||||
/*
|
||||
* Caching (not in spec)
|
||||
*/
|
||||
// Caching (not in spec)
|
||||
committee_caches: [
|
||||
CommitteeCache::default(),
|
||||
CommitteeCache::default(),
|
||||
@ -234,15 +225,15 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the `tree_hash_root` of the state.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn canonical_root(&self) -> Hash256 {
|
||||
Hash256::from_slice(&self.tree_hash_root()[..])
|
||||
}
|
||||
|
||||
pub fn historical_batch(&self) -> HistoricalBatch<T> {
|
||||
HistoricalBatch {
|
||||
block_roots: self.latest_block_roots.clone(),
|
||||
state_roots: self.latest_state_roots.clone(),
|
||||
block_roots: self.block_roots.clone(),
|
||||
state_roots: self.state_roots.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,19 +242,19 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Requires a fully up-to-date `pubkey_cache`, returns an error if this is not the case.
|
||||
pub fn get_validator_index(&self, pubkey: &PublicKey) -> Result<Option<usize>, Error> {
|
||||
if self.pubkey_cache.len() == self.validator_registry.len() {
|
||||
if self.pubkey_cache.len() == self.validators.len() {
|
||||
Ok(self.pubkey_cache.get(pubkey))
|
||||
} else {
|
||||
Err(Error::PubkeyCacheIncomplete {
|
||||
cache_len: self.pubkey_cache.len(),
|
||||
registry_len: self.validator_registry.len(),
|
||||
registry_len: self.validators.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The epoch corresponding to `self.slot`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn current_epoch(&self) -> Epoch {
|
||||
self.slot.epoch(T::slots_per_epoch())
|
||||
}
|
||||
@ -272,7 +263,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// If the current epoch is the genesis epoch, the genesis_epoch is returned.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn previous_epoch(&self) -> Epoch {
|
||||
let current_epoch = self.current_epoch();
|
||||
if current_epoch > T::genesis_epoch() {
|
||||
@ -284,12 +275,12 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// The epoch following `self.current_epoch()`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn next_epoch(&self) -> Epoch {
|
||||
self.current_epoch() + 1
|
||||
}
|
||||
|
||||
pub fn get_epoch_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
pub fn get_committee_count(&self, relative_epoch: RelativeEpoch) -> Result<u64, Error> {
|
||||
let cache = self.cache(relative_epoch)?;
|
||||
|
||||
Ok(cache.epoch_committee_count() as u64)
|
||||
@ -306,20 +297,25 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
let active_validator_count = cache.active_validator_count();
|
||||
let shard_delta = T::get_shard_delta(active_validator_count, spec.target_committee_size);
|
||||
|
||||
Ok((self.latest_start_shard + shard_delta) % T::ShardCount::to_u64())
|
||||
Ok((self.start_shard + shard_delta) % T::ShardCount::to_u64())
|
||||
}
|
||||
|
||||
/// Get the slot of an attestation.
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn get_attestation_slot(&self, attestation_data: &AttestationData) -> Result<Slot, Error> {
|
||||
/// Spec v0.8.0
|
||||
pub fn get_attestation_data_slot(
|
||||
&self,
|
||||
attestation_data: &AttestationData,
|
||||
) -> Result<Slot, Error> {
|
||||
let target_relative_epoch =
|
||||
RelativeEpoch::from_epoch(self.current_epoch(), attestation_data.target_epoch)?;
|
||||
RelativeEpoch::from_epoch(self.current_epoch(), attestation_data.target.epoch)?;
|
||||
|
||||
let cc =
|
||||
self.get_crosslink_committee_for_shard(attestation_data.shard, target_relative_epoch)?;
|
||||
let cc = self.get_crosslink_committee_for_shard(
|
||||
attestation_data.crosslink.shard,
|
||||
target_relative_epoch,
|
||||
)?;
|
||||
|
||||
Ok(cc.slot)
|
||||
}
|
||||
@ -342,9 +338,9 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Does not utilize the cache, performs a full iteration over the validator registry.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_active_validator_indices(&self, epoch: Epoch) -> Vec<usize> {
|
||||
get_active_validator_indices(&self.validator_registry, epoch)
|
||||
get_active_validator_indices(&self.validators, epoch)
|
||||
}
|
||||
|
||||
/// Return the cached active validator indices at some epoch.
|
||||
@ -362,7 +358,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committees_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
@ -379,7 +375,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committee_for_shard(
|
||||
&self,
|
||||
shard: u64,
|
||||
@ -396,7 +392,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Returns the beacon proposer index for the `slot` in the given `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
// NOTE: be sure to test this bad boy.
|
||||
pub fn get_beacon_proposer_index(
|
||||
&self,
|
||||
@ -410,7 +406,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
let first_committee = cache
|
||||
.first_committee_at_slot(slot)
|
||||
.ok_or_else(|| Error::SlotOutOfBounds)?;
|
||||
let seed = self.generate_seed(epoch, spec)?;
|
||||
let seed = self.get_seed(epoch, spec)?;
|
||||
|
||||
let mut i = 0;
|
||||
Ok(loop {
|
||||
@ -421,7 +417,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
let hash = hash(&preimage);
|
||||
hash[i % 32]
|
||||
};
|
||||
let effective_balance = self.validator_registry[candidate_index].effective_balance;
|
||||
let effective_balance = self.validators[candidate_index].effective_balance;
|
||||
if (effective_balance * MAX_RANDOM_BYTE)
|
||||
>= (spec.max_effective_balance * u64::from(random_byte))
|
||||
{
|
||||
@ -433,10 +429,10 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Safely obtains the index for latest block roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn get_latest_block_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + self.latest_block_roots.len() as u64) {
|
||||
Ok(slot.as_usize() % self.latest_block_roots.len())
|
||||
if (slot < self.slot) && (self.slot <= slot + self.block_roots.len() as u64) {
|
||||
Ok(slot.as_usize() % self.block_roots.len())
|
||||
} else {
|
||||
Err(BeaconStateError::SlotOutOfBounds)
|
||||
}
|
||||
@ -444,15 +440,15 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the block root at a recent `slot`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_block_root(&self, slot: Slot) -> Result<&Hash256, BeaconStateError> {
|
||||
let i = self.get_latest_block_roots_index(slot)?;
|
||||
Ok(&self.latest_block_roots[i])
|
||||
Ok(&self.block_roots[i])
|
||||
}
|
||||
|
||||
/// Return the block root at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
// NOTE: the spec calls this get_block_root
|
||||
pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<&Hash256, BeaconStateError> {
|
||||
self.get_block_root(epoch.start_slot(T::slots_per_epoch()))
|
||||
@ -460,25 +456,25 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Sets the block root for some given slot.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn set_block_root(
|
||||
&mut self,
|
||||
slot: Slot,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), BeaconStateError> {
|
||||
let i = self.get_latest_block_roots_index(slot)?;
|
||||
self.latest_block_roots[i] = block_root;
|
||||
self.block_roots[i] = block_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `latest_randao_mixes`
|
||||
/// Safely obtains the index for `randao_mixes`
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
fn get_randao_mix_index(&self, epoch: Epoch) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
let len = T::LatestRandaoMixesLength::to_u64();
|
||||
let len = T::EpochsPerHistoricalVector::to_u64();
|
||||
|
||||
if (epoch + len > current_epoch) & (epoch <= current_epoch) {
|
||||
if epoch + len > current_epoch && epoch <= current_epoch {
|
||||
Ok(epoch.as_usize() % len as usize)
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
@ -491,45 +487,45 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// See `Self::get_randao_mix`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn update_randao_mix(&mut self, epoch: Epoch, signature: &Signature) -> Result<(), Error> {
|
||||
let i = epoch.as_usize() % T::LatestRandaoMixesLength::to_usize();
|
||||
let i = epoch.as_usize() % T::EpochsPerHistoricalVector::to_usize();
|
||||
|
||||
let signature_hash = Hash256::from_slice(&hash(&ssz_encode(signature)));
|
||||
|
||||
self.latest_randao_mixes[i] = *self.get_randao_mix(epoch)? ^ signature_hash;
|
||||
self.randao_mixes[i] = *self.get_randao_mix(epoch)? ^ signature_hash;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_randao_mix(&self, epoch: Epoch) -> Result<&Hash256, Error> {
|
||||
let i = self.get_randao_mix_index(epoch)?;
|
||||
Ok(&self.latest_randao_mixes[i])
|
||||
Ok(&self.randao_mixes[i])
|
||||
}
|
||||
|
||||
/// Set the randao mix at a recent ``epoch``.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn set_randao_mix(&mut self, epoch: Epoch, mix: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_randao_mix_index(epoch)?;
|
||||
self.latest_randao_mixes[i] = mix;
|
||||
self.randao_mixes[i] = mix;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `latest_active_index_roots`, given some `epoch`.
|
||||
/// Safely obtains the index for `active_index_roots`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn get_active_index_root_index(&self, epoch: Epoch, spec: &ChainSpec) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
|
||||
let lookahead = spec.activation_exit_delay;
|
||||
let lookback = self.latest_active_index_roots.len() as u64 - lookahead;
|
||||
let lookback = self.active_index_roots.len() as u64 - lookahead;
|
||||
|
||||
if (epoch + lookback > current_epoch) && (current_epoch + lookahead >= epoch) {
|
||||
Ok(epoch.as_usize() % self.latest_active_index_roots.len())
|
||||
if epoch + lookback > current_epoch && current_epoch + lookahead >= epoch {
|
||||
Ok(epoch.as_usize() % self.active_index_roots.len())
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
@ -537,15 +533,15 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.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])
|
||||
Ok(self.active_index_roots[i])
|
||||
}
|
||||
|
||||
/// Set the `active_index_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn set_active_index_root(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
@ -553,24 +549,76 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let i = self.get_active_index_root_index(epoch, spec)?;
|
||||
self.latest_active_index_roots[i] = index_root;
|
||||
self.active_index_roots[i] = index_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace `active_index_roots` with clones of `index_root`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
pub fn fill_active_index_roots_with(&mut self, index_root: Hash256) {
|
||||
self.latest_active_index_roots =
|
||||
vec![index_root; self.latest_active_index_roots.len()].into()
|
||||
self.active_index_roots = FixedVector::from_elem(index_root);
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `compact_committees_roots`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
fn get_compact_committee_root_index(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<usize, Error> {
|
||||
let current_epoch = self.current_epoch();
|
||||
|
||||
let lookahead = spec.activation_exit_delay;
|
||||
let lookback = self.compact_committees_roots.len() as u64 - lookahead;
|
||||
|
||||
if epoch + lookback > current_epoch && current_epoch + lookahead >= epoch {
|
||||
Ok(epoch.as_usize() % self.compact_committees_roots.len())
|
||||
} else {
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the `compact_committee_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn get_compact_committee_root(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Hash256, Error> {
|
||||
let i = self.get_compact_committee_root_index(epoch, spec)?;
|
||||
Ok(self.compact_committees_roots[i])
|
||||
}
|
||||
|
||||
/// Set the `compact_committee_root` at a recent `epoch`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn set_compact_committee_root(
|
||||
&mut self,
|
||||
epoch: Epoch,
|
||||
index_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let i = self.get_compact_committee_root_index(epoch, spec)?;
|
||||
self.compact_committees_roots[i] = index_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Replace `compact_committees_roots` with clones of `committee_root`.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn fill_compact_committees_roots_with(&mut self, committee_root: Hash256) {
|
||||
self.compact_committees_roots = FixedVector::from_elem(committee_root);
|
||||
}
|
||||
|
||||
/// Safely obtains the index for latest state roots, given some `slot`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn get_latest_state_roots_index(&self, slot: Slot) -> Result<usize, Error> {
|
||||
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.latest_state_roots.len())) {
|
||||
Ok(slot.as_usize() % self.latest_state_roots.len())
|
||||
if (slot < self.slot) && (self.slot <= slot + Slot::from(self.state_roots.len())) {
|
||||
Ok(slot.as_usize() % self.state_roots.len())
|
||||
} else {
|
||||
Err(BeaconStateError::SlotOutOfBounds)
|
||||
}
|
||||
@ -578,69 +626,76 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Gets the state root for some slot.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_state_root(&self, slot: Slot) -> Result<&Hash256, Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
Ok(&self.latest_state_roots[i])
|
||||
Ok(&self.state_roots[i])
|
||||
}
|
||||
|
||||
/// Gets the oldest (earliest slot) state root.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_oldest_state_root(&self) -> Result<&Hash256, Error> {
|
||||
let i = self
|
||||
.get_latest_state_roots_index(self.slot - Slot::from(self.latest_state_roots.len()))?;
|
||||
Ok(&self.latest_state_roots[i])
|
||||
let i =
|
||||
self.get_latest_state_roots_index(self.slot - Slot::from(self.state_roots.len()))?;
|
||||
Ok(&self.state_roots[i])
|
||||
}
|
||||
|
||||
/// Sets the latest state root for slot.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn set_state_root(&mut self, slot: Slot, state_root: Hash256) -> Result<(), Error> {
|
||||
let i = self.get_latest_state_roots_index(slot)?;
|
||||
self.latest_state_roots[i] = state_root;
|
||||
self.state_roots[i] = state_root;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Safely obtains the index for `latest_slashed_balances`, given some `epoch`.
|
||||
/// Safely obtain the index for `slashings`, given some `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn get_slashed_balance_index(&self, epoch: Epoch) -> Result<usize, Error> {
|
||||
let i = epoch.as_usize() % self.latest_slashed_balances.len();
|
||||
|
||||
// NOTE: the validity of the epoch is not checked. It is not in the spec but it's probably
|
||||
// useful to have.
|
||||
if i < self.latest_slashed_balances.len() {
|
||||
Ok(i)
|
||||
/// Spec v0.8.0
|
||||
fn get_slashings_index(&self, epoch: Epoch) -> Result<usize, Error> {
|
||||
// We allow the slashings vector to be accessed at any cached epoch at or before
|
||||
// the current epoch.
|
||||
if epoch <= self.current_epoch()
|
||||
&& epoch + T::EpochsPerSlashingsVector::to_u64() >= self.current_epoch() + 1
|
||||
{
|
||||
Ok((epoch.as_u64() % T::EpochsPerSlashingsVector::to_u64()) as usize)
|
||||
} else {
|
||||
Err(Error::InsufficientSlashedBalances)
|
||||
Err(Error::EpochOutOfBounds)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the total slashed balances for some epoch.
|
||||
/// Get a reference to the entire `slashings` vector.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
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])
|
||||
/// Spec v0.8.0
|
||||
pub fn get_all_slashings(&self) -> &[u64] {
|
||||
&self.slashings
|
||||
}
|
||||
|
||||
/// Sets the total slashed balances for some epoch.
|
||||
/// Get the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
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;
|
||||
/// Spec v0.8.0
|
||||
pub fn get_slashings(&self, epoch: Epoch) -> Result<u64, Error> {
|
||||
let i = self.get_slashings_index(epoch)?;
|
||||
Ok(self.slashings[i])
|
||||
}
|
||||
|
||||
/// Set the total slashed balances for some epoch.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
pub fn set_slashings(&mut self, epoch: Epoch, value: u64) -> Result<(), Error> {
|
||||
let i = self.get_slashings_index(epoch)?;
|
||||
self.slashings[i] = value;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the attestations from the current or previous epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_matching_source_attestations(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
) -> Result<&[PendingAttestation], Error> {
|
||||
) -> Result<&[PendingAttestation<T>], Error> {
|
||||
if epoch == self.current_epoch() {
|
||||
Ok(&self.current_epoch_attestations)
|
||||
} else if epoch == self.previous_epoch() {
|
||||
@ -652,7 +707,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get the current crosslink for a shard.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_current_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> {
|
||||
self.current_crosslinks
|
||||
.get(shard as usize)
|
||||
@ -661,41 +716,22 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Get the previous crosslink for a shard.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_previous_crosslink(&self, shard: u64) -> Result<&Crosslink, Error> {
|
||||
self.previous_crosslinks
|
||||
.get(shard as usize)
|
||||
.ok_or(Error::ShardOutOfBounds)
|
||||
}
|
||||
|
||||
/// Transform an attestation into the crosslink that it reinforces.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn get_crosslink_from_attestation_data(
|
||||
&self,
|
||||
data: &AttestationData,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Crosslink, Error> {
|
||||
let current_crosslink_epoch = self.get_current_crosslink(data.shard)?.epoch;
|
||||
Ok(Crosslink {
|
||||
epoch: std::cmp::min(
|
||||
data.target_epoch,
|
||||
current_crosslink_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.6.3
|
||||
pub fn generate_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
/// Spec v0.8.0
|
||||
pub fn get_seed(&self, epoch: Epoch, spec: &ChainSpec) -> Result<Hash256, Error> {
|
||||
// Bypass the safe getter for RANDAO so we can gracefully handle the scenario where `epoch
|
||||
// == 0`.
|
||||
let randao = {
|
||||
let i = epoch + T::latest_randao_mixes_length() as u64 - spec.min_seed_lookahead;
|
||||
self.latest_randao_mixes[i.as_usize() % self.latest_randao_mixes.len()]
|
||||
let i = epoch + T::EpochsPerHistoricalVector::to_u64() - spec.min_seed_lookahead - 1;
|
||||
self.randao_mixes[i.as_usize() % self.randao_mixes.len()]
|
||||
};
|
||||
let active_index_root = self.get_active_index_root(epoch, spec)?;
|
||||
let epoch_bytes = int_to_bytes32(epoch.as_u64());
|
||||
@ -710,13 +746,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the effective balance (also known as "balance at stake") for a validator with the given ``index``.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_effective_balance(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
_spec: &ChainSpec,
|
||||
) -> Result<u64, Error> {
|
||||
self.validator_registry
|
||||
self.validators
|
||||
.get(validator_index)
|
||||
.map(|v| v.effective_balance)
|
||||
.ok_or_else(|| Error::UnknownValidator)
|
||||
@ -724,8 +760,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the epoch at which an activation or exit triggered in ``epoch`` takes effect.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn get_delayed_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch {
|
||||
/// Spec v0.8.1
|
||||
pub fn compute_activation_exit_epoch(&self, epoch: Epoch, spec: &ChainSpec) -> Epoch {
|
||||
epoch + 1 + spec.activation_exit_delay
|
||||
}
|
||||
|
||||
@ -733,7 +769,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Uses the epoch cache, and will error if it isn't initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_churn_limit(&self, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
Ok(std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit,
|
||||
@ -747,7 +783,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
///
|
||||
/// Note: Utilizes the cache and will fail if the appropriate cache is not initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_attestation_duties(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
@ -760,7 +796,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Return the combined effective balance of an array of validators.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_total_balance(
|
||||
&self,
|
||||
validator_indices: &[usize],
|
||||
@ -779,8 +815,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
self.build_committee_cache(RelativeEpoch::Next, spec)?;
|
||||
self.update_pubkey_cache()?;
|
||||
self.update_tree_hash_cache()?;
|
||||
self.exit_cache
|
||||
.build_from_registry(&self.validator_registry, spec);
|
||||
self.exit_cache.build_from_registry(&self.validators, spec);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -867,11 +902,11 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
|
||||
/// Updates the pubkey cache, if required.
|
||||
///
|
||||
/// Adds all `pubkeys` from the `validator_registry` which are not already in the cache. Will
|
||||
/// Adds all `pubkeys` from the `validators` which are not already in the cache. Will
|
||||
/// never re-add a pubkey.
|
||||
pub fn update_pubkey_cache(&mut self) -> Result<(), Error> {
|
||||
for (i, validator) in self
|
||||
.validator_registry
|
||||
.validators
|
||||
.iter()
|
||||
.enumerate()
|
||||
.skip(self.pubkey_cache.len())
|
||||
@ -895,6 +930,7 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// Returns the `tree_hash_root` resulting from the update. This root can be considered the
|
||||
/// canonical root of `self`.
|
||||
pub fn update_tree_hash_cache(&mut self) -> Result<Hash256, Error> {
|
||||
/* TODO(#440): re-enable cached tree hash
|
||||
if self.tree_hash_cache.is_empty() {
|
||||
self.tree_hash_cache = TreeHashCache::new(self)?;
|
||||
} else {
|
||||
@ -908,6 +944,8 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
}
|
||||
|
||||
self.cached_tree_hash_root()
|
||||
*/
|
||||
Ok(Hash256::from_slice(&self.tree_hash_root()))
|
||||
}
|
||||
|
||||
/// Returns the tree hash root determined by the last execution of `self.update_tree_hash_cache(..)`.
|
||||
@ -917,10 +955,13 @@ impl<T: EthSpec> BeaconState<T> {
|
||||
/// Returns an error if the cache is not initialized or if an error is encountered during the
|
||||
/// cache update.
|
||||
pub fn cached_tree_hash_root(&self) -> Result<Hash256, Error> {
|
||||
/* TODO(#440): re-enable cached tree hash
|
||||
self.tree_hash_cache
|
||||
.tree_hash_root()
|
||||
.and_then(|b| Ok(Hash256::from_slice(b)))
|
||||
.map_err(Into::into)
|
||||
*/
|
||||
Ok(Hash256::from_slice(&self.tree_hash_root()))
|
||||
}
|
||||
|
||||
/// Completely drops the tree hash cache, replacing it with a new, empty cache.
|
||||
@ -940,3 +981,9 @@ impl From<TreeHashCacheError> for Error {
|
||||
Error::TreeHashCacheError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz_types::Error> for Error {
|
||||
fn from(e: ssz_types::Error) -> Error {
|
||||
Error::SszTypesError(e)
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,56 @@
|
||||
use crate::*;
|
||||
use fixed_len_vec::typenum::{Unsigned, U0, U1024, U64, U8, U8192};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_types::typenum::{
|
||||
Unsigned, U0, U1, U1024, U1099511627776, U128, U16, U16777216, U4, U4096, U64, U65536, U8,
|
||||
U8192,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
type JustificationBitsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq + Default;
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
type ShardCount: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type LatestRandaoMixesLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type LatestActiveIndexRootsLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type LatestSlashedExitLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/// Note: `SlotsPerEpoch` is not necessarily required to be a compile-time constant. We include
|
||||
/// it here just for the convenience of not passing `slots_per_epoch` around all the time.
|
||||
type SlotsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxValidatorsPerCommittee: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Initial values
|
||||
*/
|
||||
type GenesisEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Time parameters
|
||||
*/
|
||||
type SlotsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type SlotsPerEth1VotingPeriod: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type SlotsPerHistoricalRoot: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* State list lengths
|
||||
*/
|
||||
type EpochsPerHistoricalVector: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type EpochsPerSlashingsVector: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type HistoricalRootsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type ValidatorRegistryLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Max operations per block
|
||||
*/
|
||||
type MaxProposerSlashings: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxAttesterSlashings: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxDeposits: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxVoluntaryExits: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
type MaxTransfers: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
/*
|
||||
* Derived values (set these CAREFULLY)
|
||||
*/
|
||||
/// The length of the `{previous,current}_epoch_attestations` lists.
|
||||
///
|
||||
/// Must be set to `MaxAttestations * SlotsPerEpoch`
|
||||
// NOTE: we could safely instantiate this by using type-level arithmetic, but doing
|
||||
// so adds ~25s to the time required to type-check this crate
|
||||
type MaxPendingAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
|
||||
|
||||
fn default_spec() -> ChainSpec;
|
||||
|
||||
@ -22,11 +60,8 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
|
||||
/// Return the number of committees in one epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn get_epoch_committee_count(
|
||||
active_validator_count: usize,
|
||||
target_committee_size: usize,
|
||||
) -> usize {
|
||||
/// Spec v0.8.1
|
||||
fn get_committee_count(active_validator_count: usize, target_committee_size: usize) -> usize {
|
||||
let shard_count = Self::shard_count();
|
||||
let slots_per_epoch = Self::slots_per_epoch() as usize;
|
||||
|
||||
@ -39,12 +74,12 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
) * slots_per_epoch
|
||||
}
|
||||
|
||||
/// Return the number of shards to increment `state.latest_start_shard` by in a given epoch.
|
||||
/// Return the number of shards to increment `state.start_shard` by in a given epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn get_shard_delta(active_validator_count: usize, target_committee_size: usize) -> u64 {
|
||||
std::cmp::min(
|
||||
Self::get_epoch_committee_count(active_validator_count, target_committee_size) as u64,
|
||||
Self::get_committee_count(active_validator_count, target_committee_size) as u64,
|
||||
Self::ShardCount::to_u64() - Self::ShardCount::to_u64() / Self::slots_per_epoch(),
|
||||
)
|
||||
}
|
||||
@ -60,61 +95,66 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq {
|
||||
|
||||
/// Returns the `SLOTS_PER_EPOCH` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn slots_per_epoch() -> u64 {
|
||||
Self::SlotsPerEpoch::to_u64()
|
||||
}
|
||||
|
||||
/// Returns the `SHARD_COUNT` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn shard_count() -> usize {
|
||||
Self::ShardCount::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `SLOTS_PER_HISTORICAL_ROOT` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn slots_per_historical_root() -> usize {
|
||||
Self::SlotsPerHistoricalRoot::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `LATEST_RANDAO_MIXES_LENGTH` constant for this specification.
|
||||
/// Returns the `EPOCHS_PER_HISTORICAL_VECTOR` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn latest_randao_mixes_length() -> usize {
|
||||
Self::LatestRandaoMixesLength::to_usize()
|
||||
/// Spec v0.8.1
|
||||
fn epochs_per_historical_vector() -> usize {
|
||||
Self::EpochsPerHistoricalVector::to_usize()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `LATEST_ACTIVE_INDEX_ROOTS` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn latest_active_index_roots() -> usize {
|
||||
Self::LatestActiveIndexRootsLength::to_usize()
|
||||
}
|
||||
|
||||
/// Returns the `LATEST_SLASHED_EXIT_LENGTH` constant for this specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
fn latest_slashed_exit_length() -> usize {
|
||||
Self::LatestSlashedExitLength::to_usize()
|
||||
/// Macro to inherit some type values from another EthSpec.
|
||||
#[macro_export]
|
||||
macro_rules! params_from_eth_spec {
|
||||
($spec_ty:ty { $($ty_name:ident),+ }) => {
|
||||
$(type $ty_name = <$spec_ty as EthSpec>::$ty_name;)+
|
||||
}
|
||||
}
|
||||
|
||||
/// Ethereum Foundation specifications.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MainnetEthSpec;
|
||||
|
||||
impl EthSpec for MainnetEthSpec {
|
||||
type JustificationBitsLength = U4;
|
||||
type ShardCount = U1024;
|
||||
type SlotsPerHistoricalRoot = U8192;
|
||||
type LatestRandaoMixesLength = U8192;
|
||||
type LatestActiveIndexRootsLength = U8192;
|
||||
type LatestSlashedExitLength = U8192;
|
||||
type SlotsPerEpoch = U64;
|
||||
type MaxValidatorsPerCommittee = U4096;
|
||||
type GenesisEpoch = U0;
|
||||
type SlotsPerEpoch = U64;
|
||||
type SlotsPerEth1VotingPeriod = U1024;
|
||||
type SlotsPerHistoricalRoot = U8192;
|
||||
type EpochsPerHistoricalVector = U65536;
|
||||
type EpochsPerSlashingsVector = U8192;
|
||||
type HistoricalRootsLimit = U16777216;
|
||||
type ValidatorRegistryLimit = U1099511627776;
|
||||
type MaxProposerSlashings = U16;
|
||||
type MaxAttesterSlashings = U1;
|
||||
type MaxAttestations = U128;
|
||||
type MaxDeposits = U16;
|
||||
type MaxVoluntaryExits = U16;
|
||||
type MaxTransfers = U0;
|
||||
type MaxPendingAttestations = U8192; // 128 max attestations * 64 slots per epoch
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::mainnet()
|
||||
@ -125,20 +165,34 @@ pub type FoundationBeaconState = BeaconState<MainnetEthSpec>;
|
||||
|
||||
/// Ethereum Foundation minimal spec, as defined here:
|
||||
///
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/configs/constant_presets/minimal.yaml
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.8.0/configs/constant_presets/minimal.yaml
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(Clone, PartialEq, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct MinimalEthSpec;
|
||||
|
||||
impl EthSpec for MinimalEthSpec {
|
||||
type ShardCount = U8;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type LatestRandaoMixesLength = U64;
|
||||
type LatestActiveIndexRootsLength = U64;
|
||||
type LatestSlashedExitLength = U64;
|
||||
type SlotsPerEpoch = U8;
|
||||
type GenesisEpoch = U0;
|
||||
type SlotsPerEth1VotingPeriod = U16;
|
||||
type SlotsPerHistoricalRoot = U64;
|
||||
type EpochsPerHistoricalVector = U64;
|
||||
type EpochsPerSlashingsVector = U64;
|
||||
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
|
||||
|
||||
params_from_eth_spec!(MainnetEthSpec {
|
||||
JustificationBitsLength,
|
||||
MaxValidatorsPerCommittee,
|
||||
GenesisEpoch,
|
||||
HistoricalRootsLimit,
|
||||
ValidatorRegistryLimit,
|
||||
MaxProposerSlashings,
|
||||
MaxAttesterSlashings,
|
||||
MaxAttestations,
|
||||
MaxDeposits,
|
||||
MaxVoluntaryExits,
|
||||
MaxTransfers
|
||||
});
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::minimal()
|
||||
|
@ -24,7 +24,7 @@ pub struct CommitteeCache {
|
||||
impl CommitteeCache {
|
||||
/// Return a new, fully initialized cache.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn initialized<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
epoch: Epoch,
|
||||
@ -38,22 +38,20 @@ impl CommitteeCache {
|
||||
return Err(Error::ZeroSlotsPerEpoch);
|
||||
}
|
||||
|
||||
let active_validator_indices =
|
||||
get_active_validator_indices(&state.validator_registry, epoch);
|
||||
let active_validator_indices = get_active_validator_indices(&state.validators, epoch);
|
||||
|
||||
if active_validator_indices.is_empty() {
|
||||
return Err(Error::InsufficientValidators);
|
||||
}
|
||||
|
||||
let committee_count = T::get_epoch_committee_count(
|
||||
active_validator_indices.len(),
|
||||
spec.target_committee_size,
|
||||
) as usize;
|
||||
let committee_count =
|
||||
T::get_committee_count(active_validator_indices.len(), spec.target_committee_size)
|
||||
as usize;
|
||||
|
||||
let shuffling_start_shard =
|
||||
Self::compute_start_shard(state, relative_epoch, active_validator_indices.len(), spec);
|
||||
|
||||
let seed = state.generate_seed(epoch, spec)?;
|
||||
let seed = state.get_seed(epoch, spec)?;
|
||||
|
||||
let shuffling = shuffle_list(
|
||||
active_validator_indices,
|
||||
@ -64,11 +62,11 @@ impl CommitteeCache {
|
||||
.ok_or_else(|| Error::UnableToShuffle)?;
|
||||
|
||||
// The use of `NonZeroUsize` reduces the maximum number of possible validators by one.
|
||||
if state.validator_registry.len() > usize::max_value() - 1 {
|
||||
if state.validators.len() > usize::max_value() - 1 {
|
||||
return Err(Error::TooManyValidators);
|
||||
}
|
||||
|
||||
let mut shuffling_positions = vec![None; state.validator_registry.len()];
|
||||
let mut shuffling_positions = vec![None; state.validators.len()];
|
||||
for (i, v) in shuffling.iter().enumerate() {
|
||||
shuffling_positions[*v] = NonZeroUsize::new(i + 1);
|
||||
}
|
||||
@ -88,7 +86,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// The `active_validator_count` must be the number of validators active at `relative_epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn compute_start_shard<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
relative_epoch: RelativeEpoch,
|
||||
@ -96,21 +94,21 @@ impl CommitteeCache {
|
||||
spec: &ChainSpec,
|
||||
) -> u64 {
|
||||
match relative_epoch {
|
||||
RelativeEpoch::Current => state.latest_start_shard,
|
||||
RelativeEpoch::Current => state.start_shard,
|
||||
RelativeEpoch::Previous => {
|
||||
let shard_delta =
|
||||
T::get_shard_delta(active_validator_count, spec.target_committee_size);
|
||||
|
||||
(state.latest_start_shard + T::ShardCount::to_u64() - shard_delta)
|
||||
(state.start_shard + T::ShardCount::to_u64() - shard_delta)
|
||||
% T::ShardCount::to_u64()
|
||||
}
|
||||
RelativeEpoch::Next => {
|
||||
let current_active_validators =
|
||||
get_active_validator_count(&state.validator_registry, state.current_epoch());
|
||||
get_active_validator_count(&state.validators, state.current_epoch());
|
||||
let shard_delta =
|
||||
T::get_shard_delta(current_active_validators, spec.target_committee_size);
|
||||
|
||||
(state.latest_start_shard + shard_delta) % T::ShardCount::to_u64()
|
||||
(state.start_shard + shard_delta) % T::ShardCount::to_u64()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -128,7 +126,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn active_validator_indices(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
@ -137,7 +135,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `&[]` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn shuffling(&self) -> &[usize] {
|
||||
&self.shuffling
|
||||
}
|
||||
@ -147,7 +145,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committee_for_shard(&self, shard: Shard) -> Option<CrosslinkCommittee> {
|
||||
if shard >= self.shard_count || self.initialized_epoch.is_none() {
|
||||
return None;
|
||||
@ -201,7 +199,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn active_validator_count(&self) -> usize {
|
||||
self.shuffling.len()
|
||||
}
|
||||
@ -210,7 +208,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `usize::default()` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn epoch_committee_count(&self) -> usize {
|
||||
self.committee_count
|
||||
}
|
||||
@ -226,7 +224,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Returns `None` if `slot` is not in the initialized epoch, or if `Self` is not initialized.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_crosslink_committees_for_slot(&self, slot: Slot) -> Option<Vec<CrosslinkCommittee>> {
|
||||
let position = self
|
||||
.initialized_epoch?
|
||||
@ -258,7 +256,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn first_committee_at_slot(&self, slot: Slot) -> Option<&[usize]> {
|
||||
self.get_crosslink_committees_for_slot(slot)?
|
||||
.first()
|
||||
@ -267,7 +265,7 @@ impl CommitteeCache {
|
||||
|
||||
/// Returns a slice of `self.shuffling` that represents the `index`'th committee in the epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn compute_committee(&self, index: usize) -> Option<&[usize]> {
|
||||
Some(&self.shuffling[self.compute_committee_range(index)?])
|
||||
}
|
||||
@ -276,9 +274,11 @@ impl CommitteeCache {
|
||||
///
|
||||
/// To avoid a divide-by-zero, returns `None` if `self.committee_count` is zero.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Will also return `None` if the index is out of bounds.
|
||||
///
|
||||
/// Spec v0.8.1
|
||||
fn compute_committee_range(&self, index: usize) -> Option<Range<usize>> {
|
||||
if self.committee_count == 0 {
|
||||
if self.committee_count == 0 || index >= self.committee_count {
|
||||
return None;
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ impl CommitteeCache {
|
||||
///
|
||||
/// Always returns `None` for a non-initialized epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn crosslink_slot_for_shard(&self, shard: u64) -> Option<Slot> {
|
||||
let offset = (shard + self.shard_count - self.shuffling_start_shard) % self.shard_count;
|
||||
Some(
|
||||
@ -314,10 +314,10 @@ impl CommitteeCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all `validator_registry` indices where the validator is active at the given
|
||||
/// Returns a list of all `validators` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> Vec<usize> {
|
||||
let mut active = Vec::with_capacity(validators.len());
|
||||
|
||||
@ -332,10 +332,10 @@ pub fn get_active_validator_indices(validators: &[Validator], epoch: Epoch) -> V
|
||||
active
|
||||
}
|
||||
|
||||
/// Returns the count of all `validator_registry` indices where the validator is active at the given
|
||||
/// Returns the count of all `validators` indices where the validator is active at the given
|
||||
/// `epoch`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
fn get_active_validator_count(validators: &[Validator], epoch: Epoch) -> usize {
|
||||
validators.iter().filter(|v| v.is_active_at(epoch)).count()
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![cfg(test)]
|
||||
use super::*;
|
||||
use crate::{test_utils::*, *};
|
||||
use fixed_len_vec::typenum::*;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_types::typenum::*;
|
||||
|
||||
#[test]
|
||||
fn default_values() {
|
||||
@ -63,6 +63,8 @@ fn initializes_with_the_right_epoch() {
|
||||
|
||||
#[test]
|
||||
fn shuffles_for_the_right_epoch() {
|
||||
use crate::EthSpec;
|
||||
|
||||
let num_validators = MinimalEthSpec::minimum_validator_count() * 2;
|
||||
let epoch = Epoch::new(100_000_000);
|
||||
let slot = epoch.start_slot(MinimalEthSpec::slots_per_epoch());
|
||||
@ -70,16 +72,16 @@ fn shuffles_for_the_right_epoch() {
|
||||
let mut state = new_state::<MinimalEthSpec>(num_validators, slot);
|
||||
let spec = &MinimalEthSpec::default_spec();
|
||||
|
||||
let distinct_hashes: Vec<Hash256> = (0..MinimalEthSpec::latest_randao_mixes_length())
|
||||
let distinct_hashes: Vec<Hash256> = (0..MinimalEthSpec::epochs_per_historical_vector())
|
||||
.into_iter()
|
||||
.map(|i| Hash256::from(i as u64))
|
||||
.collect();
|
||||
|
||||
state.latest_randao_mixes = FixedLenVec::from(distinct_hashes);
|
||||
state.randao_mixes = FixedVector::from(distinct_hashes);
|
||||
|
||||
let previous_seed = state.generate_seed(state.previous_epoch(), spec).unwrap();
|
||||
let current_seed = state.generate_seed(state.current_epoch(), spec).unwrap();
|
||||
let next_seed = state.generate_seed(state.next_epoch(), spec).unwrap();
|
||||
let previous_seed = state.get_seed(state.previous_epoch(), spec).unwrap();
|
||||
let current_seed = state.get_seed(state.current_epoch(), spec).unwrap();
|
||||
let next_seed = state.get_seed(state.next_epoch(), spec).unwrap();
|
||||
|
||||
assert!((previous_seed != current_seed) && (current_seed != next_seed));
|
||||
|
||||
@ -131,7 +133,7 @@ fn can_start_on_any_shard() {
|
||||
let shard_count = MinimalEthSpec::shard_count() as u64;
|
||||
|
||||
for i in 0..MinimalEthSpec::shard_count() as u64 {
|
||||
state.latest_start_shard = i;
|
||||
state.start_shard = i;
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.current_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling_start_shard, i);
|
||||
@ -154,12 +156,26 @@ pub struct ExcessShardsEthSpec;
|
||||
|
||||
impl EthSpec for ExcessShardsEthSpec {
|
||||
type ShardCount = U128;
|
||||
type SlotsPerHistoricalRoot = U8192;
|
||||
type LatestRandaoMixesLength = U8192;
|
||||
type LatestActiveIndexRootsLength = U8192;
|
||||
type LatestSlashedExitLength = U8192;
|
||||
type SlotsPerEpoch = U8;
|
||||
type GenesisEpoch = U0;
|
||||
type MaxPendingAttestations = U1024;
|
||||
|
||||
params_from_eth_spec!(MinimalEthSpec {
|
||||
JustificationBitsLength,
|
||||
MaxValidatorsPerCommittee,
|
||||
GenesisEpoch,
|
||||
SlotsPerEth1VotingPeriod,
|
||||
SlotsPerHistoricalRoot,
|
||||
EpochsPerHistoricalVector,
|
||||
EpochsPerSlashingsVector,
|
||||
HistoricalRootsLimit,
|
||||
ValidatorRegistryLimit,
|
||||
MaxProposerSlashings,
|
||||
MaxAttesterSlashings,
|
||||
MaxAttestations,
|
||||
MaxDeposits,
|
||||
MaxVoluntaryExits,
|
||||
MaxTransfers
|
||||
});
|
||||
|
||||
fn default_spec() -> ChainSpec {
|
||||
ChainSpec::minimal()
|
||||
@ -177,13 +193,13 @@ fn starts_on_the_correct_shard() {
|
||||
|
||||
let mut state = new_state::<ExcessShardsEthSpec>(num_validators, slot);
|
||||
|
||||
let validator_count = state.validator_registry.len();
|
||||
let validator_count = state.validators.len();
|
||||
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
let next_epoch = state.next_epoch();
|
||||
|
||||
for (i, mut v) in state.validator_registry.iter_mut().enumerate() {
|
||||
for (i, mut v) in state.validators.iter_mut().enumerate() {
|
||||
let epoch = if i < validator_count / 4 {
|
||||
previous_epoch
|
||||
} else if i < validator_count / 2 {
|
||||
@ -196,28 +212,28 @@ fn starts_on_the_correct_shard() {
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validator_registry, previous_epoch),
|
||||
get_active_validator_count(&state.validators, previous_epoch),
|
||||
validator_count / 4
|
||||
);
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validator_registry, current_epoch),
|
||||
get_active_validator_count(&state.validators, current_epoch),
|
||||
validator_count / 2
|
||||
);
|
||||
assert_eq!(
|
||||
get_active_validator_count(&state.validator_registry, next_epoch),
|
||||
get_active_validator_count(&state.validators, next_epoch),
|
||||
validator_count
|
||||
);
|
||||
|
||||
let previous_shards = ExcessShardsEthSpec::get_epoch_committee_count(
|
||||
get_active_validator_count(&state.validator_registry, previous_epoch),
|
||||
let previous_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, previous_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
let current_shards = ExcessShardsEthSpec::get_epoch_committee_count(
|
||||
get_active_validator_count(&state.validator_registry, current_epoch),
|
||||
let current_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, current_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
let next_shards = ExcessShardsEthSpec::get_epoch_committee_count(
|
||||
get_active_validator_count(&state.validator_registry, next_epoch),
|
||||
let next_shards = ExcessShardsEthSpec::get_committee_count(
|
||||
get_active_validator_count(&state.validators, next_epoch),
|
||||
spec.target_committee_size,
|
||||
);
|
||||
|
||||
@ -233,7 +249,7 @@ fn starts_on_the_correct_shard() {
|
||||
|
||||
let shard_count = ExcessShardsEthSpec::shard_count();
|
||||
for i in 0..ExcessShardsEthSpec::shard_count() {
|
||||
state.latest_start_shard = i as u64;
|
||||
state.start_shard = i as u64;
|
||||
|
||||
let cache = CommitteeCache::initialized(&state, state.current_epoch(), spec).unwrap();
|
||||
assert_eq!(cache.shuffling_start_shard as usize, i);
|
||||
|
@ -8,8 +8,8 @@ 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
|
||||
pub fn build_from_registry(&mut self, validators: &[Validator], spec: &ChainSpec) {
|
||||
validators
|
||||
.iter()
|
||||
.filter(|validator| validator.exit_epoch != spec.far_future_epoch)
|
||||
.for_each(|validator| self.record_validator_exit(validator.exit_epoch));
|
||||
|
@ -44,7 +44,7 @@ fn test_beacon_proposer_index<T: EthSpec>() {
|
||||
// Test with two validators per slot, first validator has zero balance.
|
||||
let mut state = build_state(T::slots_per_epoch() as usize * 2);
|
||||
let shuffling = state.get_shuffling(relative_epoch).unwrap().to_vec();
|
||||
state.validator_registry[shuffling[0]].effective_balance = 0;
|
||||
state.validators[shuffling[0]].effective_balance = 0;
|
||||
test(&state, Slot::new(0), 1);
|
||||
for i in 1..T::slots_per_epoch() {
|
||||
test(&state, Slot::from(i), i as usize * 2);
|
||||
@ -64,7 +64,7 @@ fn active_index_range<T: EthSpec>(current_epoch: Epoch) -> RangeInclusive<Epoch>
|
||||
let delay = T::default_spec().activation_exit_delay;
|
||||
|
||||
let start: i32 =
|
||||
current_epoch.as_u64() as i32 - T::latest_active_index_roots() as i32 + delay as i32;
|
||||
current_epoch.as_u64() as i32 - T::epochs_per_historical_vector() as i32 + delay as i32;
|
||||
let end = current_epoch + delay;
|
||||
|
||||
let start: Epoch = if start < 0 {
|
||||
@ -87,7 +87,7 @@ fn test_active_index<T: EthSpec>(state_slot: Slot) {
|
||||
|
||||
let range = active_index_range::<T>(state.current_epoch());
|
||||
|
||||
let modulo = |epoch: Epoch| epoch.as_usize() % T::latest_active_index_roots();
|
||||
let modulo = |epoch: Epoch| epoch.as_usize() % T::epochs_per_historical_vector();
|
||||
|
||||
// Test the start and end of the range.
|
||||
assert_eq!(
|
||||
@ -117,7 +117,7 @@ fn test_active_index<T: EthSpec>(state_slot: Slot) {
|
||||
fn get_active_index_root_index() {
|
||||
test_active_index::<MainnetEthSpec>(Slot::new(0));
|
||||
|
||||
let epoch = Epoch::from(MainnetEthSpec::latest_active_index_roots() * 4);
|
||||
let epoch = Epoch::from(MainnetEthSpec::epochs_per_historical_vector() * 4);
|
||||
let slot = epoch.start_slot(MainnetEthSpec::slots_per_epoch());
|
||||
test_active_index::<MainnetEthSpec>(slot);
|
||||
}
|
||||
@ -213,7 +213,7 @@ mod committees {
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
let active_indices: Vec<usize> = (0..validator_count).collect();
|
||||
let seed = state.generate_seed(epoch, spec).unwrap();
|
||||
let seed = state.get_seed(epoch, spec).unwrap();
|
||||
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch).unwrap();
|
||||
let start_shard =
|
||||
CommitteeCache::compute_start_shard(&state, relative_epoch, active_indices.len(), spec);
|
||||
@ -244,7 +244,7 @@ mod committees {
|
||||
// of committees in an epoch.
|
||||
assert_eq!(
|
||||
crosslink_committees.len() as u64,
|
||||
state.get_epoch_committee_count(relative_epoch).unwrap() / T::slots_per_epoch()
|
||||
state.get_committee_count(relative_epoch).unwrap() / T::slots_per_epoch()
|
||||
);
|
||||
|
||||
for cc in crosslink_committees {
|
||||
@ -306,11 +306,11 @@ mod committees {
|
||||
|
||||
let (mut state, _keypairs): (BeaconState<T>, _) = builder.build();
|
||||
|
||||
let distinct_hashes: Vec<Hash256> = (0..T::latest_randao_mixes_length())
|
||||
let distinct_hashes: Vec<Hash256> = (0..T::epochs_per_historical_vector())
|
||||
.into_iter()
|
||||
.map(|i| Hash256::from(i as u64))
|
||||
.collect();
|
||||
state.latest_randao_mixes = FixedLenVec::from(distinct_hashes);
|
||||
state.randao_mixes = FixedVector::from(distinct_hashes);
|
||||
|
||||
state
|
||||
.build_committee_cache(RelativeEpoch::Previous, spec)
|
||||
|
@ -5,7 +5,7 @@ use test_utils::{u8_from_hex_str, u8_to_hex_str};
|
||||
|
||||
/// Each of the BLS signature domains.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub enum Domain {
|
||||
BeaconProposer,
|
||||
Randao,
|
||||
@ -17,24 +17,28 @@ pub enum Domain {
|
||||
|
||||
/// Holds all the "constants" for a BeaconChain.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ChainSpec {
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
#[serde(skip_serializing)] // skipped because Serde TOML has trouble with u64::max
|
||||
pub far_future_epoch: Epoch,
|
||||
pub base_rewards_per_epoch: u64,
|
||||
pub deposit_contract_tree_depth: u64,
|
||||
pub seconds_per_day: u64,
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
pub target_committee_size: usize,
|
||||
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_tree_depth: u64,
|
||||
pub min_genesis_active_validator_count: u64,
|
||||
pub min_genesis_time: u64,
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
@ -48,47 +52,30 @@ pub struct ChainSpec {
|
||||
* Initial Values
|
||||
*/
|
||||
pub genesis_slot: Slot,
|
||||
// Skipped because serde TOML can't handle u64::max_value, the typical value for this field.
|
||||
#[serde(skip_serializing)]
|
||||
pub far_future_epoch: Epoch,
|
||||
pub zero_hash: Hash256,
|
||||
#[serde(deserialize_with = "u8_from_hex_str", serialize_with = "u8_to_hex_str")]
|
||||
pub bls_withdrawal_prefix_byte: u8,
|
||||
|
||||
/*
|
||||
* Time parameters
|
||||
*/
|
||||
pub genesis_time: u64,
|
||||
pub seconds_per_slot: u64,
|
||||
pub min_attestation_inclusion_delay: u64,
|
||||
pub min_seed_lookahead: Epoch,
|
||||
pub activation_exit_delay: 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 max_epochs_per_crosslink: u64,
|
||||
pub min_epochs_to_inactivity_penalty: u64,
|
||||
|
||||
/*
|
||||
* Reward and penalty quotients
|
||||
*/
|
||||
pub base_reward_quotient: u64,
|
||||
pub whistleblowing_reward_quotient: u64,
|
||||
pub base_reward_factor: u64,
|
||||
pub whistleblower_reward_quotient: u64,
|
||||
pub proposer_reward_quotient: u64,
|
||||
pub inactivity_penalty_quotient: u64,
|
||||
pub min_slashing_penalty_quotient: u64,
|
||||
|
||||
/*
|
||||
* Max operations per block
|
||||
*/
|
||||
pub max_proposer_slashings: u64,
|
||||
pub max_attester_slashings: u64,
|
||||
pub max_attestations: u64,
|
||||
pub max_deposits: u64,
|
||||
pub max_voluntary_exits: u64,
|
||||
pub max_transfers: u64,
|
||||
|
||||
/*
|
||||
* Signature domains
|
||||
*
|
||||
@ -111,7 +98,7 @@ pub struct ChainSpec {
|
||||
impl ChainSpec {
|
||||
/// Get the domain number that represents the fork meta and signature domain.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_domain(&self, epoch: Epoch, domain: Domain, fork: &Fork) -> u64 {
|
||||
let domain_constant = match domain {
|
||||
Domain::BeaconProposer => self.domain_beacon_proposer,
|
||||
@ -122,8 +109,8 @@ impl ChainSpec {
|
||||
Domain::Transfer => self.domain_transfer,
|
||||
};
|
||||
|
||||
let mut bytes: Vec<u8> = fork.get_fork_version(epoch).to_vec();
|
||||
bytes.append(&mut int_to_bytes4(domain_constant));
|
||||
let mut bytes: Vec<u8> = int_to_bytes4(domain_constant);
|
||||
bytes.append(&mut fork.get_fork_version(epoch).to_vec());
|
||||
|
||||
let mut fork_and_domain = [0; 8];
|
||||
fork_and_domain.copy_from_slice(&bytes);
|
||||
@ -133,23 +120,26 @@ impl ChainSpec {
|
||||
|
||||
/// Returns a `ChainSpec` compatible with the Ethereum Foundation specification.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn mainnet() -> Self {
|
||||
Self {
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
far_future_epoch: Epoch::new(u64::max_value()),
|
||||
base_rewards_per_epoch: 5,
|
||||
deposit_contract_tree_depth: 32,
|
||||
seconds_per_day: 86400,
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
target_committee_size: 128,
|
||||
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_tree_depth: 32,
|
||||
min_genesis_active_validator_count: 65_536,
|
||||
min_genesis_time: 1_578_009_600, // Jan 3, 2020
|
||||
|
||||
/*
|
||||
* Gwei values
|
||||
@ -163,44 +153,29 @@ impl ChainSpec {
|
||||
* Initial Values
|
||||
*/
|
||||
genesis_slot: Slot::new(0),
|
||||
far_future_epoch: Epoch::new(u64::max_value()),
|
||||
zero_hash: Hash256::zero(),
|
||||
bls_withdrawal_prefix_byte: 0,
|
||||
|
||||
/*
|
||||
* Time parameters
|
||||
*/
|
||||
genesis_time: u64::from(u32::max_value()),
|
||||
seconds_per_slot: 6,
|
||||
min_attestation_inclusion_delay: 4,
|
||||
min_attestation_inclusion_delay: 1,
|
||||
min_seed_lookahead: Epoch::new(1),
|
||||
activation_exit_delay: 4,
|
||||
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,
|
||||
max_epochs_per_crosslink: 64,
|
||||
min_epochs_to_inactivity_penalty: 4,
|
||||
|
||||
/*
|
||||
* Reward and penalty quotients
|
||||
*/
|
||||
base_reward_quotient: 32,
|
||||
whistleblowing_reward_quotient: 512,
|
||||
base_reward_factor: 64,
|
||||
whistleblower_reward_quotient: 512,
|
||||
proposer_reward_quotient: 8,
|
||||
inactivity_penalty_quotient: 33_554_432,
|
||||
min_slashing_penalty_quotient: 32,
|
||||
|
||||
/*
|
||||
* Max operations per block
|
||||
*/
|
||||
max_proposer_slashings: 16,
|
||||
max_attester_slashings: 1,
|
||||
max_attestations: 128,
|
||||
max_deposits: 16,
|
||||
max_voluntary_exits: 16,
|
||||
max_transfers: 0,
|
||||
|
||||
/*
|
||||
* Signature domains
|
||||
*/
|
||||
@ -221,21 +196,18 @@ impl ChainSpec {
|
||||
|
||||
/// Ethereum Foundation minimal spec, as defined here:
|
||||
///
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.6.3/configs/constant_presets/minimal.yaml
|
||||
/// https://github.com/ethereum/eth2.0-specs/blob/v0.8.1/configs/constant_presets/minimal.yaml
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn minimal() -> Self {
|
||||
let genesis_slot = Slot::new(0);
|
||||
|
||||
// Note: bootnodes to be updated when static nodes exist.
|
||||
let boot_nodes = vec![];
|
||||
|
||||
Self {
|
||||
target_committee_size: 4,
|
||||
shuffle_round_count: 10,
|
||||
min_attestation_inclusion_delay: 2,
|
||||
slots_per_eth1_voting_period: 16,
|
||||
genesis_slot,
|
||||
min_genesis_active_validator_count: 64,
|
||||
max_epochs_per_crosslink: 4,
|
||||
chain_id: 2, // lighthouse testnet chain id
|
||||
boot_nodes,
|
||||
..ChainSpec::mainnet()
|
||||
@ -265,8 +237,8 @@ mod tests {
|
||||
|
||||
let domain = spec.get_domain(epoch, domain_type, &fork);
|
||||
|
||||
let mut expected = fork.get_fork_version(epoch).to_vec();
|
||||
expected.append(&mut int_to_bytes4(raw_domain));
|
||||
let mut expected = int_to_bytes4(raw_domain);
|
||||
expected.append(&mut fork.get_fork_version(epoch).to_vec());
|
||||
|
||||
assert_eq!(int_to_bytes8(domain), expected);
|
||||
}
|
||||
|
39
eth2/types/src/checkpoint.rs
Normal file
39
eth2/types/src/checkpoint.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Epoch, Hash256};
|
||||
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};
|
||||
|
||||
/// Casper FFG checkpoint, used in attestations.
|
||||
///
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Default,
|
||||
Hash,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Encode,
|
||||
Decode,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct Checkpoint {
|
||||
pub epoch: Epoch,
|
||||
pub root: Hash256,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
ssz_tests!(Checkpoint);
|
||||
cached_tree_hash_tests!(Checkpoint);
|
||||
}
|
35
eth2/types/src/compact_committee.rs
Normal file
35
eth2/types/src/compact_committee.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{EthSpec, PublicKey};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::VariableList;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
PartialEq,
|
||||
TreeHash,
|
||||
CachedTreeHash,
|
||||
Encode,
|
||||
Decode,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
TestRandom,
|
||||
)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct CompactCommittee<T: EthSpec> {
|
||||
pub pubkeys: VariableList<PublicKey, T::MaxValidatorsPerCommittee>,
|
||||
pub compact_validators: VariableList<u64, T::MaxValidatorsPerCommittee>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Default for CompactCommittee<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pubkeys: VariableList::empty(),
|
||||
compact_validators: VariableList::empty(),
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Specifies the block hash for a shard at an epoch.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -25,9 +25,12 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct Crosslink {
|
||||
pub epoch: Epoch,
|
||||
pub previous_crosslink_root: Hash256,
|
||||
pub crosslink_data_root: Hash256,
|
||||
pub shard: u64,
|
||||
pub parent_root: Hash256,
|
||||
// Crosslinking data
|
||||
pub start_epoch: Epoch,
|
||||
pub end_epoch: Epoch,
|
||||
pub data_root: Hash256,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
use fixed_len_vec::typenum::U32;
|
||||
use ssz_types::typenum::U33;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// A deposit to potentially become a beacon chain validator.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -23,8 +23,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct Deposit {
|
||||
pub proof: FixedLenVec<Hash256, U32>,
|
||||
pub index: u64,
|
||||
pub proof: FixedVector<Hash256, U33>,
|
||||
pub data: DepositData,
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
|
||||
/// The data supplied by the user to the deposit contract.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -35,7 +35,7 @@ pub struct DepositData {
|
||||
impl DepositData {
|
||||
/// Generate the signature for a given DepositData details.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn create_signature(
|
||||
&self,
|
||||
secret_key: &SecretKey,
|
||||
|
@ -8,7 +8,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Contains data obtained from the Eth1 chain.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
|
@ -10,7 +10,7 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Specifies a fork of the `BeaconChain`, to prevent replay attacks.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -35,7 +35,7 @@ pub struct Fork {
|
||||
impl Fork {
|
||||
/// Initialize the `Fork` from the genesis parameters in the `spec`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn genesis(genesis_epoch: Epoch) -> Self {
|
||||
Self {
|
||||
previous_version: [0; 4],
|
||||
@ -46,7 +46,7 @@ impl Fork {
|
||||
|
||||
/// Return the fork version of the given ``epoch``.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
pub fn get_fork_version(&self, epoch: Epoch) -> [u8; 4] {
|
||||
if epoch < self.epoch {
|
||||
return self.previous_version;
|
||||
|
@ -1,15 +1,15 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::*;
|
||||
|
||||
use fixed_len_vec::FixedLenVec;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use ssz_types::FixedVector;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
|
||||
/// Historical block and state roots.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.1
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -23,8 +23,8 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct HistoricalBatch<T: EthSpec> {
|
||||
pub block_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
pub state_roots: FixedLenVec<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
pub block_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
pub state_roots: FixedVector<Hash256, T::SlotsPerHistoricalRoot>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData};
|
||||
use crate::{test_utils::TestRandom, AggregateSignature, AttestationData, EthSpec, VariableList};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use test_random_derive::TestRandom;
|
||||
@ -9,7 +9,7 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
///
|
||||
/// To be included in an `AttesterSlashing`.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
PartialEq,
|
||||
@ -23,29 +23,30 @@ use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash};
|
||||
TestRandom,
|
||||
SignedRoot,
|
||||
)]
|
||||
pub struct IndexedAttestation {
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct IndexedAttestation<T: EthSpec> {
|
||||
/// Lists validator registry indices, not committee indices.
|
||||
pub custody_bit_0_indices: Vec<u64>,
|
||||
pub custody_bit_1_indices: Vec<u64>,
|
||||
pub custody_bit_0_indices: VariableList<u64, T::MaxValidatorsPerCommittee>,
|
||||
pub custody_bit_1_indices: VariableList<u64, T::MaxValidatorsPerCommittee>,
|
||||
pub data: AttestationData,
|
||||
#[signed_root(skip_hashing)]
|
||||
pub signature: AggregateSignature,
|
||||
}
|
||||
|
||||
impl IndexedAttestation {
|
||||
impl<T: EthSpec> IndexedAttestation<T> {
|
||||
/// Check if ``attestation_data_1`` and ``attestation_data_2`` have the same target.
|
||||
///
|
||||
/// Spec v0.6.3
|
||||
pub fn is_double_vote(&self, other: &IndexedAttestation) -> bool {
|
||||
self.data.target_epoch == other.data.target_epoch && self.data != other.data
|
||||
/// Spec v0.8.0
|
||||
pub fn is_double_vote(&self, other: &Self) -> 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.3
|
||||
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
|
||||
/// Spec v0.8.0
|
||||
pub fn is_surround_vote(&self, other: &Self) -> bool {
|
||||
self.data.source.epoch < other.data.source.epoch
|
||||
&& other.data.target.epoch < self.data.target.epoch
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +55,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::slot_epoch::Epoch;
|
||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||
use crate::MainnetEthSpec;
|
||||
|
||||
#[test]
|
||||
pub fn test_is_double_vote_true() {
|
||||
@ -121,15 +123,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
ssz_tests!(IndexedAttestation);
|
||||
cached_tree_hash_tests!(IndexedAttestation);
|
||||
ssz_tests!(IndexedAttestation<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(IndexedAttestation<MainnetEthSpec>);
|
||||
|
||||
fn create_indexed_attestation(target_epoch: u64, source_epoch: u64) -> IndexedAttestation {
|
||||
fn create_indexed_attestation(
|
||||
target_epoch: u64,
|
||||
source_epoch: u64,
|
||||
) -> IndexedAttestation<MainnetEthSpec> {
|
||||
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.data.source.epoch = Epoch::new(source_epoch);
|
||||
indexed_vote.data.target.epoch = Epoch::new(target_epoch);
|
||||
indexed_vote
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
//! Ethereum 2.0 types
|
||||
|
||||
// Required for big type-level numbers
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
#[macro_use]
|
||||
pub mod test_utils;
|
||||
|
||||
@ -13,6 +16,8 @@ pub mod beacon_block_body;
|
||||
pub mod beacon_block_header;
|
||||
pub mod beacon_state;
|
||||
pub mod chain_spec;
|
||||
pub mod checkpoint;
|
||||
pub mod compact_committee;
|
||||
pub mod crosslink;
|
||||
pub mod crosslink_committee;
|
||||
pub mod deposit;
|
||||
@ -46,6 +51,8 @@ pub use crate::beacon_block_body::BeaconBlockBody;
|
||||
pub use crate::beacon_block_header::BeaconBlockHeader;
|
||||
pub use crate::beacon_state::{Error as BeaconStateError, *};
|
||||
pub use crate::chain_spec::{ChainSpec, Domain};
|
||||
pub use crate::checkpoint::Checkpoint;
|
||||
pub use crate::compact_committee::CompactCommittee;
|
||||
pub use crate::crosslink::Crosslink;
|
||||
pub use crate::crosslink_committee::{CrosslinkCommittee, OwnedCrosslinkCommittee};
|
||||
pub use crate::deposit::Deposit;
|
||||
@ -71,8 +78,6 @@ pub type CrosslinkCommittees = Vec<(Committee, u64)>;
|
||||
pub type Hash256 = H256;
|
||||
pub type Address = H160;
|
||||
pub type EthBalance = U256;
|
||||
pub type Bitfield = boolean_bitfield::BooleanBitfield;
|
||||
pub type BitfieldError = boolean_bitfield::Error;
|
||||
|
||||
/// Maps a (slot, shard_id) to attestation_indices.
|
||||
pub type AttesterMap = HashMap<(u64, u64), Vec<usize>>;
|
||||
@ -81,4 +86,4 @@ pub type AttesterMap = HashMap<(u64, u64), Vec<usize>>;
|
||||
pub type ProposerMap = HashMap<u64, usize>;
|
||||
|
||||
pub use bls::{AggregatePublicKey, AggregateSignature, Keypair, PublicKey, SecretKey, Signature};
|
||||
pub use fixed_len_vec::{typenum, typenum::Unsigned, FixedLenVec};
|
||||
pub use ssz_types::{typenum, typenum::Unsigned, BitList, BitVector, FixedVector, VariableList};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{AttestationData, Bitfield};
|
||||
use crate::{AttestationData, BitList, EthSpec};
|
||||
|
||||
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.6.3
|
||||
/// Spec v0.8.0
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
@ -21,8 +21,8 @@ use tree_hash_derive::{CachedTreeHash, TreeHash};
|
||||
CachedTreeHash,
|
||||
TestRandom,
|
||||
)]
|
||||
pub struct PendingAttestation {
|
||||
pub aggregation_bitfield: Bitfield,
|
||||
pub struct PendingAttestation<T: EthSpec> {
|
||||
pub aggregation_bits: BitList<T::MaxValidatorsPerCommittee>,
|
||||
pub data: AttestationData,
|
||||
pub inclusion_delay: u64,
|
||||
pub proposer_index: u64,
|
||||
@ -31,7 +31,8 @@ pub struct PendingAttestation {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::*;
|
||||
|
||||
ssz_tests!(PendingAttestation);
|
||||
cached_tree_hash_tests!(PendingAttestation);
|
||||
ssz_tests!(PendingAttestation<MainnetEthSpec>);
|
||||
cached_tree_hash_tests!(PendingAttestation<MainnetEthSpec>);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user