Update to Spec v0.10 (#817)
* Start updating types * WIP * Signature hacking * Existing EF tests passing with fake_crypto * Updates * Delete outdated API spec * The refactor continues * It compiles * WIP test fixes * All release tests passing bar genesis state parsing * Update and test YamlConfig * Update to spec v0.10 compatible BLS * Updates to BLS EF tests * Add EF test for AggregateVerify And delete unused hash2curve tests for uncompressed points * Update EF tests to v0.10.1 * Use optional block root correctly in block proc * Use genesis fork in deposit domain. All tests pass * Cargo fmt * Fast aggregate verify test * Update REST API docs * Cargo fmt * Fix unused import * Bump spec tags to v0.10.1 * Add `seconds_per_eth1_block` to chainspec * Update to timestamp based eth1 voting scheme * Return None from `get_votes_to_consider` if block cache is empty * Handle overflows in `is_candidate_block` * Revert to failing tests * Fix eth1 data sets test * Choose default vote according to spec * Fix collect_valid_votes tests * Fix `get_votes_to_consider` to choose all eligible blocks * Uncomment winning_vote tests * Add comments; remove unused code * Reduce seconds_per_eth1_block for simulation * Addressed review comments * Add test for default vote case * Fix logs * Remove unused functions * Meter default eth1 votes * Fix comments * Address review comments; remove unused dependency * Disable/delete two outdated tests * Bump eth1 default vote warn to error * Delete outdated eth1 test Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com>
This commit is contained in:
co-authored by
Pawan Dhananjay
parent
03e77390a3
commit
371e5adcf8
@@ -166,9 +166,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.store
|
||||
.get_block(&beacon_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?;
|
||||
let beacon_state_root = beacon_block.state_root;
|
||||
let beacon_state_root = beacon_block.state_root();
|
||||
let beacon_state = self
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot))?
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
|
||||
.ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?;
|
||||
|
||||
CheckPoint {
|
||||
@@ -216,39 +216,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.map(|slot| slot.epoch(T::EthSpec::slots_per_epoch()))
|
||||
}
|
||||
|
||||
/// 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<T::EthSpec>>, Error> {
|
||||
let bodies: Result<Vec<_>, _> = roots
|
||||
.iter()
|
||||
.map(|root| match self.block_at_root(*root)? {
|
||||
Some(block) => Ok(block.body),
|
||||
None => Err(Error::DBInconsistent(format!("Missing block: {}", root))),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(bodies?)
|
||||
}
|
||||
|
||||
/// Returns the beacon block header for each beacon block root in `roots`.
|
||||
///
|
||||
/// Fails if any root in `roots` does not have a corresponding block.
|
||||
pub fn get_block_headers(&self, roots: &[Hash256]) -> Result<Vec<BeaconBlockHeader>, Error> {
|
||||
let headers: Result<Vec<BeaconBlockHeader>, _> = roots
|
||||
.iter()
|
||||
.map(|root| match self.block_at_root(*root)? {
|
||||
Some(block) => Ok(block.block_header()),
|
||||
None => Err(Error::DBInconsistent("Missing block".into())),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(headers?)
|
||||
}
|
||||
|
||||
/// Iterates across all `(block_root, slot)` pairs from the head of the chain (inclusive) to
|
||||
/// the earliest reachable ancestor (may or may not be genesis).
|
||||
///
|
||||
@@ -268,7 +235,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let iter = BlockRootsIterator::owned(self.store.clone(), head.beacon_state);
|
||||
|
||||
Ok(ReverseBlockRootIterator::new(
|
||||
(head.beacon_block_root, head.beacon_block.slot),
|
||||
(head.beacon_block_root, head.beacon_block.slot()),
|
||||
iter,
|
||||
))
|
||||
}
|
||||
@@ -305,11 +272,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.get_block(&block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(block_root))?;
|
||||
let state = self
|
||||
.get_state(&block.state_root, Some(block.slot))?
|
||||
.ok_or_else(|| Error::MissingBeaconState(block.state_root))?;
|
||||
.get_state(&block.state_root(), Some(block.slot()))?
|
||||
.ok_or_else(|| Error::MissingBeaconState(block.state_root()))?;
|
||||
let iter = BlockRootsIterator::owned(self.store.clone(), state);
|
||||
Ok(ReverseBlockRootIterator::new(
|
||||
(block_root, block.slot),
|
||||
(block_root, block.slot()),
|
||||
iter,
|
||||
))
|
||||
}
|
||||
@@ -349,24 +316,15 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the block at the given root, if any.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// May return a database error.
|
||||
pub fn block_at_root(
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||
Ok(self.store.get(&block_root)?)
|
||||
}
|
||||
|
||||
/// Returns the block at the given slot, if any. Only returns blocks in the canonical chain.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// May return a database error.
|
||||
pub fn block_at_slot(&self, slot: Slot) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||
pub fn block_at_slot(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> Result<Option<SignedBeaconBlock<T::EthSpec>>, Error> {
|
||||
let root = self
|
||||
.rev_iter_block_roots()?
|
||||
.find(|(_, this_slot)| *this_slot == slot)
|
||||
@@ -387,7 +345,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn get_block(
|
||||
&self,
|
||||
block_root: &Hash256,
|
||||
) -> Result<Option<BeaconBlock<T::EthSpec>>, Error> {
|
||||
) -> Result<Option<SignedBeaconBlock<T::EthSpec>>, Error> {
|
||||
Ok(self.store.get_block(block_root)?)
|
||||
}
|
||||
|
||||
@@ -448,7 +406,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.ok_or_else(|| Error::CanonicalHeadLockTimeout)?;
|
||||
|
||||
Ok(HeadInfo {
|
||||
slot: head.beacon_block.slot,
|
||||
slot: head.beacon_block.slot(),
|
||||
block_root: head.beacon_block_root,
|
||||
state_root: head.beacon_state_root,
|
||||
finalized_checkpoint: head.beacon_state.finalized_checkpoint.clone(),
|
||||
@@ -550,7 +508,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn best_slot(&self) -> Result<Slot, Error> {
|
||||
self.canonical_head
|
||||
.try_read_for(HEAD_LOCK_TIMEOUT)
|
||||
.map(|head| head.beacon_block.slot)
|
||||
.map(|head| head.beacon_block.slot())
|
||||
.ok_or_else(|| Error::CanonicalHeadLockTimeout)
|
||||
}
|
||||
|
||||
@@ -576,19 +534,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.map(|(root, _slot)| root))
|
||||
}
|
||||
|
||||
/// Reads the slot clock (see `self.read_slot_clock()` and returns the number of slots since
|
||||
/// genesis.
|
||||
pub fn slots_since_genesis(&self) -> Option<SlotHeight> {
|
||||
let now = self.slot().ok()?;
|
||||
let genesis_slot = self.spec.genesis_slot;
|
||||
|
||||
if now < genesis_slot {
|
||||
None
|
||||
} else {
|
||||
Some(SlotHeight::from(now.as_u64() - genesis_slot.as_u64()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the block proposer for a given slot.
|
||||
///
|
||||
/// Information is read from the present `beacon_state` shuffling, only information from the
|
||||
@@ -669,7 +614,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let data = self.produce_attestation_data_for_block(
|
||||
index,
|
||||
head.beacon_block_root,
|
||||
head.beacon_block.slot,
|
||||
head.beacon_block.slot(),
|
||||
&state,
|
||||
)?;
|
||||
|
||||
@@ -696,7 +641,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.produce_attestation_data_for_block(
|
||||
index,
|
||||
head.beacon_block_root,
|
||||
head.beacon_block.slot,
|
||||
head.beacon_block.slot(),
|
||||
&state,
|
||||
)
|
||||
}
|
||||
@@ -859,22 +804,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// the attestation points to a block in a prior epoch, then it is necessary to
|
||||
// load the full state corresponding to its block, and transition it to the
|
||||
// attestation's epoch.
|
||||
let attestation_block_root = attestation_head_block.state_root();
|
||||
let attestation_epoch = attestation.data.target.epoch;
|
||||
let slots_per_epoch = T::EthSpec::slots_per_epoch();
|
||||
let mut state = if attestation_epoch
|
||||
== attestation_head_block.slot.epoch(slots_per_epoch)
|
||||
== attestation_head_block.slot().epoch(slots_per_epoch)
|
||||
{
|
||||
self.store
|
||||
.load_epoch_boundary_state(&attestation_head_block.state_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))?
|
||||
.load_epoch_boundary_state(&attestation_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconState(attestation_block_root))?
|
||||
} else {
|
||||
let mut state = self
|
||||
.store
|
||||
.get_state(
|
||||
&attestation_head_block.state_root,
|
||||
Some(attestation_head_block.slot),
|
||||
)?
|
||||
.ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))?;
|
||||
.get_state(&attestation_block_root, Some(attestation_head_block.slot()))?
|
||||
.ok_or_else(|| Error::MissingBeaconState(attestation_block_root))?;
|
||||
|
||||
// Fastforward the state to the epoch in which the attestation was made.
|
||||
// NOTE: this looks like a potential DoS vector, we should probably limit
|
||||
@@ -902,7 +845,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.process_attestation_for_state_and_block(
|
||||
attestation,
|
||||
&state,
|
||||
&attestation_head_block,
|
||||
&attestation_head_block.message,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -1045,7 +988,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
|
||||
/// Accept some exit and queue it for inclusion in an appropriate block.
|
||||
pub fn process_voluntary_exit(&self, exit: VoluntaryExit) -> Result<(), ExitValidationError> {
|
||||
pub fn process_voluntary_exit(
|
||||
&self,
|
||||
exit: SignedVoluntaryExit,
|
||||
) -> Result<(), ExitValidationError> {
|
||||
match self.wall_clock_state() {
|
||||
Ok(state) => self.op_pool.insert_voluntary_exit(exit, &state, &self.spec),
|
||||
Err(e) => {
|
||||
@@ -1109,7 +1055,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Will accept blocks from prior slots, however it will reject any block from a future slot.
|
||||
pub fn process_block(
|
||||
&self,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
block: SignedBeaconBlock<T::EthSpec>,
|
||||
) -> Result<BlockProcessingOutcome, Error> {
|
||||
let outcome = self.process_block_internal(block.clone());
|
||||
|
||||
@@ -1120,7 +1066,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.log,
|
||||
"Beacon block imported";
|
||||
"block_root" => format!("{:?}", block_root),
|
||||
"block_slot" => format!("{:?}", block.slot.as_u64()),
|
||||
"block_slot" => format!("{:?}", block.slot().as_u64()),
|
||||
);
|
||||
let _ = self.event_handler.register(EventKind::BeaconBlockImported {
|
||||
block_root: *block_root,
|
||||
@@ -1160,11 +1106,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Will accept blocks from prior slots, however it will reject any block from a future slot.
|
||||
fn process_block_internal(
|
||||
&self,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
signed_block: SignedBeaconBlock<T::EthSpec>,
|
||||
) -> Result<BlockProcessingOutcome, Error> {
|
||||
metrics::inc_counter(&metrics::BLOCK_PROCESSING_REQUESTS);
|
||||
let full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES);
|
||||
|
||||
let block = &signed_block.message;
|
||||
|
||||
let finalized_slot = self
|
||||
.head_info()?
|
||||
.finalized_checkpoint
|
||||
@@ -1246,9 +1194,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// Load the parent blocks state from the database, returning an error if it is not found.
|
||||
// It is an error because if we know the parent block we should also know the parent state.
|
||||
let parent_state_root = parent_block.state_root;
|
||||
let parent_state_root = parent_block.state_root();
|
||||
let parent_state = self
|
||||
.get_state(&parent_state_root, Some(parent_block.slot))?
|
||||
.get_state(&parent_state_root, Some(parent_block.slot()))?
|
||||
.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing state {:?}", parent_state_root))
|
||||
})?;
|
||||
@@ -1268,7 +1216,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let distance = block.slot.as_u64().saturating_sub(state.slot.as_u64());
|
||||
for i in 0..distance {
|
||||
let state_root = if i == 0 {
|
||||
parent_block.state_root
|
||||
parent_block.state_root()
|
||||
} else {
|
||||
// This is a new state we've reached, so stage it for storage in the DB.
|
||||
// Computing the state root here is time-equivalent to computing it during slot
|
||||
@@ -1302,7 +1250,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// slot).
|
||||
match per_block_processing(
|
||||
&mut state,
|
||||
&block,
|
||||
&signed_block,
|
||||
Some(block_root),
|
||||
BlockSignatureStrategy::VerifyBulk,
|
||||
&self.spec,
|
||||
@@ -1372,7 +1320,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// settles down).
|
||||
// See: https://github.com/sigp/lighthouse/issues/692
|
||||
self.store.put_state(&state_root, state)?;
|
||||
self.store.put_block(&block_root, block)?;
|
||||
self.store.put_block(&block_root, signed_block)?;
|
||||
|
||||
metrics::stop_timer(db_write_timer);
|
||||
|
||||
@@ -1450,26 +1398,28 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.deposits_for_block_inclusion(&state, ð1_data, &self.spec)?
|
||||
.into();
|
||||
|
||||
let mut block = BeaconBlock {
|
||||
slot: state.slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(),
|
||||
let mut block = SignedBeaconBlock {
|
||||
message: BeaconBlock {
|
||||
slot: state.slot,
|
||||
parent_root,
|
||||
state_root: Hash256::zero(),
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal,
|
||||
eth1_data,
|
||||
graffiti,
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings.into(),
|
||||
attestations: self
|
||||
.op_pool
|
||||
.get_attestations(&state, &self.spec)
|
||||
.map_err(BlockProductionError::OpPoolError)?
|
||||
.into(),
|
||||
deposits,
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(),
|
||||
},
|
||||
},
|
||||
// The block is not signed here, that is the task of a validator client.
|
||||
signature: Signature::empty_signature(),
|
||||
body: BeaconBlockBody {
|
||||
randao_reveal,
|
||||
eth1_data,
|
||||
graffiti,
|
||||
proposer_slashings: proposer_slashings.into(),
|
||||
attester_slashings: attester_slashings.into(),
|
||||
attestations: self
|
||||
.op_pool
|
||||
.get_attestations(&state, &self.spec)
|
||||
.map_err(BlockProductionError::OpPoolError)?
|
||||
.into(),
|
||||
deposits,
|
||||
voluntary_exits: self.op_pool.get_voluntary_exits(&state, &self.spec).into(),
|
||||
},
|
||||
};
|
||||
|
||||
per_block_processing(
|
||||
@@ -1482,7 +1432,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let state_root = state.update_tree_hash_cache()?;
|
||||
|
||||
block.state_root = state_root;
|
||||
block.message.state_root = state_root;
|
||||
|
||||
metrics::inc_counter(&metrics::BLOCK_PRODUCTION_SUCCESSES);
|
||||
metrics::stop_timer(timer);
|
||||
@@ -1490,12 +1440,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
trace!(
|
||||
self.log,
|
||||
"Produced beacon block";
|
||||
"parent" => format!("{}", block.parent_root),
|
||||
"attestations" => block.body.attestations.len(),
|
||||
"slot" => block.slot
|
||||
"parent" => format!("{}", block.message.parent_root),
|
||||
"attestations" => block.message.body.attestations.len(),
|
||||
"slot" => block.message.slot
|
||||
);
|
||||
|
||||
Ok((block, state))
|
||||
Ok((block.message, state))
|
||||
}
|
||||
|
||||
/// Execute the fork choice algorithm and enthrone the result as the canonical head.
|
||||
@@ -1516,13 +1466,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.get_block(&beacon_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(beacon_block_root))?;
|
||||
|
||||
let beacon_state_root = beacon_block.state_root;
|
||||
let beacon_state_root = beacon_block.state_root();
|
||||
let beacon_state: BeaconState<T::EthSpec> = self
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot))?
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
|
||||
.ok_or_else(|| Error::MissingBeaconState(beacon_state_root))?;
|
||||
|
||||
let previous_slot = self.head_info()?.slot;
|
||||
let new_slot = beacon_block.slot;
|
||||
let new_slot = beacon_block.slot();
|
||||
|
||||
// Note: this will declare a re-org if we skip `SLOTS_PER_HISTORICAL_ROOT` blocks
|
||||
// between calls to fork choice without swapping between chains. This seems like an
|
||||
@@ -1541,7 +1491,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
"Beacon chain re-org";
|
||||
"previous_head" => format!("{}", self.head_info()?.block_root),
|
||||
"previous_slot" => previous_slot,
|
||||
"new_head_parent" => format!("{}", beacon_block.parent_root),
|
||||
"new_head_parent" => format!("{}", beacon_block.parent_root()),
|
||||
"new_head" => format!("{}", beacon_block_root),
|
||||
"new_slot" => new_slot
|
||||
);
|
||||
@@ -1636,7 +1586,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let finalized_block = self
|
||||
.store
|
||||
.get_block(&finalized_block_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?;
|
||||
.ok_or_else(|| Error::MissingBeaconBlock(finalized_block_root))?
|
||||
.message;
|
||||
|
||||
let new_finalized_epoch = finalized_block.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
@@ -1678,7 +1629,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub fn is_new_block_root(&self, beacon_block_root: &Hash256) -> Result<bool, Error> {
|
||||
Ok(!self
|
||||
.store
|
||||
.exists::<BeaconBlock<T::EthSpec>>(beacon_block_root)?)
|
||||
.exists::<SignedBeaconBlock<T::EthSpec>>(beacon_block_root)?)
|
||||
}
|
||||
|
||||
/// Dumps the entire canonical chain, from the head to genesis to a vector for analysis.
|
||||
@@ -1698,20 +1649,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
dump.push(last_slot.clone());
|
||||
|
||||
loop {
|
||||
let beacon_block_root = last_slot.beacon_block.parent_root;
|
||||
let beacon_block_root = last_slot.beacon_block.parent_root();
|
||||
|
||||
if beacon_block_root == Hash256::zero() {
|
||||
break; // Genesis has been reached.
|
||||
}
|
||||
|
||||
let beacon_block: BeaconBlock<T::EthSpec> =
|
||||
self.store.get(&beacon_block_root)?.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing block {}", beacon_block_root))
|
||||
})?;
|
||||
let beacon_state_root = beacon_block.state_root;
|
||||
let beacon_block = self.store.get_block(&beacon_block_root)?.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing block {}", beacon_block_root))
|
||||
})?;
|
||||
let beacon_state_root = beacon_block.state_root();
|
||||
let beacon_state = self
|
||||
.store
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot))?
|
||||
.get_state(&beacon_state_root, Some(beacon_block.slot()))?
|
||||
.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing state {:?}", beacon_state_root))
|
||||
})?;
|
||||
|
||||
@@ -16,7 +16,9 @@ use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use store::Store;
|
||||
use types::{BeaconBlock, BeaconState, ChainSpec, EthSpec, Hash256, Slot};
|
||||
use types::{
|
||||
BeaconBlock, BeaconState, ChainSpec, EthSpec, Hash256, Signature, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
/// An empty struct used to "witness" all the `BeaconChainTypes` traits. It has no user-facing
|
||||
/// functionality and only exists to satisfy the type system.
|
||||
@@ -205,14 +207,13 @@ where
|
||||
.clone()
|
||||
.ok_or_else(|| "genesis_state requires a store")?;
|
||||
|
||||
let mut beacon_block = genesis_block(&beacon_state, &self.spec);
|
||||
let beacon_block = genesis_block(&mut beacon_state, &self.spec)?;
|
||||
|
||||
beacon_state
|
||||
.build_all_caches(&self.spec)
|
||||
.map_err(|e| format!("Failed to build genesis state caches: {:?}", e))?;
|
||||
|
||||
let beacon_state_root = beacon_state.canonical_root();
|
||||
beacon_block.state_root = beacon_state_root;
|
||||
let beacon_state_root = beacon_block.message.state_root;
|
||||
let beacon_block_root = beacon_block.canonical_root();
|
||||
|
||||
self.genesis_block_root = Some(beacon_block_root);
|
||||
@@ -303,7 +304,7 @@ where
|
||||
.build_all_caches(&self.spec)
|
||||
.map_err(|e| format!("Failed to build state caches: {:?}", e))?;
|
||||
|
||||
if canonical_head.beacon_block.state_root != canonical_head.beacon_state_root {
|
||||
if canonical_head.beacon_block.state_root() != canonical_head.beacon_state_root {
|
||||
return Err("beacon_block.state_root != beacon_state".to_string());
|
||||
}
|
||||
|
||||
@@ -345,7 +346,7 @@ where
|
||||
"Beacon chain initialized";
|
||||
"head_state" => format!("{}", head.beacon_state_root),
|
||||
"head_block" => format!("{}", head.beacon_block_root),
|
||||
"head_slot" => format!("{}", head.beacon_block.slot),
|
||||
"head_slot" => format!("{}", head.beacon_block.slot()),
|
||||
);
|
||||
|
||||
Ok(beacon_chain)
|
||||
@@ -382,7 +383,7 @@ where
|
||||
.ok_or_else(|| "fork_choice_backend requires a genesis_block_root")?;
|
||||
|
||||
let backend = ProtoArrayForkChoice::new(
|
||||
finalized_checkpoint.beacon_block.slot,
|
||||
finalized_checkpoint.beacon_block.message.slot,
|
||||
// Note: here we set the `justified_epoch` to be the same as the epoch of the
|
||||
// finalized checkpoint. Whilst this finalized checkpoint may actually point to
|
||||
// a _later_ justified checkpoint, that checkpoint won't yet exist in the fork
|
||||
@@ -512,12 +513,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
fn genesis_block<T: EthSpec>(genesis_state: &BeaconState<T>, spec: &ChainSpec) -> BeaconBlock<T> {
|
||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||
|
||||
genesis_block.state_root = genesis_state.canonical_root();
|
||||
|
||||
genesis_block
|
||||
fn genesis_block<T: EthSpec>(
|
||||
genesis_state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<SignedBeaconBlock<T>, String> {
|
||||
let mut genesis_block = SignedBeaconBlock {
|
||||
message: BeaconBlock::empty(&spec),
|
||||
// Empty signature, which should NEVER be read. This isn't to-spec, but makes the genesis
|
||||
// block consistent with every other block.
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
genesis_block.message.state_root = genesis_state
|
||||
.update_tree_hash_cache()
|
||||
.map_err(|e| format!("Error hashing genesis state: {:?}", e))?;
|
||||
Ok(genesis_block)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -581,14 +590,14 @@ mod test {
|
||||
"should have the correct genesis time"
|
||||
);
|
||||
assert_eq!(
|
||||
block.state_root,
|
||||
block.state_root(),
|
||||
state.canonical_root(),
|
||||
"block should have correct state root"
|
||||
);
|
||||
assert_eq!(
|
||||
chain
|
||||
.store
|
||||
.get::<BeaconBlock<_>>(&Hash256::zero())
|
||||
.get_block(&Hash256::zero())
|
||||
.expect("should read db")
|
||||
.expect("should find genesis block"),
|
||||
block,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use serde_derive::Serialize;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use types::{BeaconBlock, BeaconState, EthSpec, Hash256};
|
||||
use types::{BeaconState, EthSpec, Hash256, SignedBeaconBlock};
|
||||
|
||||
/// Represents some block and it's associated state. Generally, this will be used for tracking the
|
||||
/// Represents some block and its associated state. Generally, this will be used for tracking the
|
||||
/// head, justified head and finalized head.
|
||||
#[derive(Clone, Serialize, PartialEq, Debug, Encode, Decode)]
|
||||
pub struct CheckPoint<E: EthSpec> {
|
||||
pub beacon_block: BeaconBlock<E>,
|
||||
pub beacon_block: SignedBeaconBlock<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<E>,
|
||||
beacon_block: SignedBeaconBlock<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<E>,
|
||||
beacon_block: SignedBeaconBlock<E>,
|
||||
beacon_block_root: Hash256,
|
||||
beacon_state: BeaconState<E>,
|
||||
beacon_state_root: Hash256,
|
||||
|
||||
@@ -3,15 +3,12 @@ use eth1::{Config as Eth1Config, Eth1Block, Service as HttpService};
|
||||
use eth2_hashing::hash;
|
||||
use exit_future::Exit;
|
||||
use futures::Future;
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use rand::prelude::*;
|
||||
use slog::{crit, debug, error, trace, Logger};
|
||||
use slog::{debug, error, trace, Logger};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use state_processing::per_block_processing::get_new_eth1_data;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::iter::DoubleEndedIterator;
|
||||
use std::iter::FromIterator;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use store::{Error as StoreError, Store};
|
||||
@@ -21,7 +18,6 @@ use types::{
|
||||
};
|
||||
|
||||
type BlockNumber = u64;
|
||||
type Eth1DataBlockNumber = HashMap<Eth1Data, BlockNumber>;
|
||||
type Eth1DataVoteCount = HashMap<(Eth1Data, BlockNumber), u64>;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -280,12 +276,7 @@ impl<T: EthSpec, S: Store<T>> CachingEth1Backend<T, S> {
|
||||
|
||||
impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S> {
|
||||
fn eth1_data(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Result<Eth1Data, Error> {
|
||||
// Note: we do not return random junk if this function call fails as it would be caused by
|
||||
// an internal error.
|
||||
let prev_eth1_hash = eth1_block_hash_at_start_of_voting_period(self.store.clone(), state)?;
|
||||
|
||||
let period = T::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let eth1_follow_distance = spec.eth1_follow_distance;
|
||||
let voting_period_start_slot = (state.slot / period) * period;
|
||||
let voting_period_start_seconds = slot_start_seconds::<T>(
|
||||
state.genesis_time,
|
||||
@@ -295,82 +286,54 @@ impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S
|
||||
|
||||
let blocks = self.core.blocks().read();
|
||||
|
||||
let (new_eth1_data, all_eth1_data) = if let Some(sets) = eth1_data_sets(
|
||||
blocks.iter(),
|
||||
prev_eth1_hash,
|
||||
voting_period_start_seconds,
|
||||
spec,
|
||||
&self.log,
|
||||
) {
|
||||
sets
|
||||
} else {
|
||||
// The algorithm was unable to find the `new_eth1_data` and `all_eth1_data` sets.
|
||||
//
|
||||
// This is likely because the caches are empty or the previous eth1 block hash is not
|
||||
// in the cache.
|
||||
//
|
||||
// This situation can also be caused when a testnet does not have an adequate delay
|
||||
// between the eth1 genesis block and the eth2 genesis block. This delay needs to be at
|
||||
// least `2 * ETH1_FOLLOW_DISTANCE`.
|
||||
crit!(
|
||||
self.log,
|
||||
"Unable to find eth1 data sets";
|
||||
"lowest_block_number" => self.core.lowest_block_number(),
|
||||
"earliest_block_timestamp" => self.core.earliest_block_timestamp(),
|
||||
"genesis_time" => state.genesis_time,
|
||||
"outcome" => "casting random eth1 vote"
|
||||
);
|
||||
|
||||
return Ok(random_eth1_data());
|
||||
};
|
||||
let votes_to_consider =
|
||||
get_votes_to_consider(blocks.iter(), voting_period_start_seconds, spec);
|
||||
|
||||
trace!(
|
||||
self.log,
|
||||
"Found eth1 data sets";
|
||||
"all_eth1_data" => all_eth1_data.len(),
|
||||
"new_eth1_data" => new_eth1_data.len(),
|
||||
"Found eth1 data votes_to_consider";
|
||||
"votes_to_consider" => votes_to_consider.len(),
|
||||
);
|
||||
|
||||
let valid_votes = collect_valid_votes(state, new_eth1_data, all_eth1_data);
|
||||
let valid_votes = collect_valid_votes(state, &votes_to_consider);
|
||||
|
||||
let eth1_data = if let Some(eth1_data) = find_winning_vote(valid_votes) {
|
||||
eth1_data
|
||||
} else {
|
||||
// In this case, there are no other viable votes (perhaps there are no votes yet or all
|
||||
// the existing votes are junk).
|
||||
// In this case, there are no valid votes available.
|
||||
//
|
||||
// Here we choose the latest block in our voting window.
|
||||
blocks
|
||||
// Here we choose the eth1_data corresponding to the latest block in our voting window.
|
||||
// If no votes exist, choose `state.eth1_data` as default vote.
|
||||
let default_vote = votes_to_consider
|
||||
.iter()
|
||||
.rev()
|
||||
.skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds)
|
||||
.nth(eth1_follow_distance as usize)
|
||||
.map(|block| {
|
||||
trace!(
|
||||
.max_by(|(_, x), (_, y)| x.cmp(y))
|
||||
.map(|vote| {
|
||||
let vote = vote.0.clone();
|
||||
debug!(
|
||||
self.log,
|
||||
"Choosing default eth1_data";
|
||||
"eth1_block_number" => block.number,
|
||||
"eth1_block_hash" => format!("{:?}", block.hash),
|
||||
"No valid eth1_data votes";
|
||||
"outcome" => "Casting vote corresponding to last candidate eth1 block",
|
||||
);
|
||||
|
||||
block
|
||||
vote
|
||||
})
|
||||
.and_then(|block| block.clone().eth1_data())
|
||||
.unwrap_or_else(|| {
|
||||
crit!(
|
||||
let vote = state.eth1_data.clone();
|
||||
error!(
|
||||
self.log,
|
||||
"Unable to find a winning eth1 vote";
|
||||
"outcome" => "casting random eth1 vote"
|
||||
"No valid eth1_data votes, `votes_to_consider` empty";
|
||||
"lowest_block_number" => self.core.lowest_block_number(),
|
||||
"earliest_block_timestamp" => self.core.earliest_block_timestamp(),
|
||||
"genesis_time" => state.genesis_time,
|
||||
"outcome" => "casting `state.eth1_data` as eth1 vote"
|
||||
);
|
||||
|
||||
random_eth1_data()
|
||||
})
|
||||
metrics::inc_counter(&metrics::DEFAULT_ETH1_VOTES);
|
||||
vote
|
||||
});
|
||||
default_vote
|
||||
};
|
||||
|
||||
debug!(
|
||||
self.log,
|
||||
"Produced vote for eth1 chain";
|
||||
"is_period_tail" => is_period_tail(state),
|
||||
"deposit_root" => format!("{:?}", eth1_data.deposit_root),
|
||||
"deposit_count" => eth1_data.deposit_count,
|
||||
"block_hash" => format!("{:?}", eth1_data.block_hash),
|
||||
@@ -432,139 +395,47 @@ impl<T: EthSpec, S: Store<T>> Eth1ChainBackend<T, S> for CachingEth1Backend<T, S
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces an `Eth1Data` with all fields sourced from `rand::thread_rng()`.
|
||||
fn random_eth1_data() -> Eth1Data {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
metrics::inc_counter(&metrics::JUNK_ETH1_VOTES);
|
||||
|
||||
macro_rules! rand_bytes {
|
||||
($num_bytes: expr) => {{
|
||||
let mut arr = [0_u8; $num_bytes];
|
||||
rng.fill(&mut arr[..]);
|
||||
arr
|
||||
}};
|
||||
}
|
||||
|
||||
// Note: it seems easier to just use `Hash256::random(..)` to get the hash values, however I
|
||||
// prefer to be explicit about the source of entropy instead of relying upon the maintainers of
|
||||
// `Hash256` to ensure their entropy is suitable for our purposes.
|
||||
|
||||
Eth1Data {
|
||||
block_hash: Hash256::from_slice(&rand_bytes!(32)),
|
||||
deposit_root: Hash256::from_slice(&rand_bytes!(32)),
|
||||
deposit_count: u64::from_le_bytes(rand_bytes!(8)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `state.eth1_data.block_hash` at the start of eth1 voting period defined by
|
||||
/// `state.slot`.
|
||||
fn eth1_block_hash_at_start_of_voting_period<T: EthSpec, S: Store<T>>(
|
||||
store: Arc<S>,
|
||||
state: &BeaconState<T>,
|
||||
) -> Result<Hash256, Error> {
|
||||
let period = T::SlotsPerEth1VotingPeriod::to_u64();
|
||||
|
||||
if !eth1_data_change_is_possible(state) {
|
||||
// If there are less than 50% of the votes in the current state, it's impossible that the
|
||||
// `eth1_data.block_hash` has changed from the value at `state.eth1_data.block_hash`.
|
||||
Ok(state.eth1_data.block_hash)
|
||||
} else {
|
||||
// If there have been more than 50% of votes in this period it's possible (but not
|
||||
// necessary) that the `state.eth1_data.block_hash` has been changed since the start of the
|
||||
// voting period.
|
||||
let slot = (state.slot / period) * period;
|
||||
let prev_state_root = state
|
||||
.get_state_root(slot)
|
||||
.map_err(Error::UnableToGetPreviousStateRoot)?;
|
||||
|
||||
store
|
||||
.get_state(&prev_state_root, Some(slot))
|
||||
.map_err(Error::StoreError)?
|
||||
.map(|state| state.eth1_data.block_hash)
|
||||
.ok_or_else(|| Error::PreviousStateNotInDB(*prev_state_root))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if there are enough eth1 votes in the given `state` to have updated
|
||||
/// `state.eth1_data`.
|
||||
fn eth1_data_change_is_possible<E: EthSpec>(state: &BeaconState<E>) -> bool {
|
||||
2 * state.eth1_data_votes.len() > E::SlotsPerEth1VotingPeriod::to_usize()
|
||||
}
|
||||
|
||||
/// Calculates and returns `(new_eth1_data, all_eth1_data)` for the given `state`, based upon the
|
||||
/// blocks in the `block` iterator.
|
||||
/// Get all votes from eth1 blocks which are in the list of candidate blocks for the
|
||||
/// current eth1 voting period.
|
||||
///
|
||||
/// `prev_eth1_hash` is the `eth1_data.block_hash` at the start of the voting period defined by
|
||||
/// `state.slot`.
|
||||
fn eth1_data_sets<'a, I>(
|
||||
/// Returns a hashmap of `Eth1Data` to its associated eth1 `block_number`.
|
||||
fn get_votes_to_consider<'a, I>(
|
||||
blocks: I,
|
||||
prev_eth1_hash: Hash256,
|
||||
voting_period_start_seconds: u64,
|
||||
spec: &ChainSpec,
|
||||
log: &Logger,
|
||||
) -> Option<(Eth1DataBlockNumber, Eth1DataBlockNumber)>
|
||||
) -> HashMap<Eth1Data, u64>
|
||||
where
|
||||
I: DoubleEndedIterator<Item = &'a Eth1Block> + Clone,
|
||||
{
|
||||
let eth1_follow_distance = spec.eth1_follow_distance;
|
||||
|
||||
let in_scope_eth1_data = blocks
|
||||
blocks
|
||||
.rev()
|
||||
.skip_while(|eth1_block| eth1_block.timestamp > voting_period_start_seconds)
|
||||
.skip(eth1_follow_distance as usize)
|
||||
.filter_map(|block| Some((block.clone().eth1_data()?, block.number)));
|
||||
|
||||
if in_scope_eth1_data
|
||||
.clone()
|
||||
.any(|(eth1_data, _)| eth1_data.block_hash == prev_eth1_hash)
|
||||
{
|
||||
let new_eth1_data = in_scope_eth1_data
|
||||
.clone()
|
||||
.take(eth1_follow_distance as usize);
|
||||
let all_eth1_data =
|
||||
in_scope_eth1_data.take_while(|(eth1_data, _)| eth1_data.block_hash != prev_eth1_hash);
|
||||
|
||||
Some((
|
||||
HashMap::from_iter(new_eth1_data),
|
||||
HashMap::from_iter(all_eth1_data),
|
||||
))
|
||||
} else {
|
||||
error!(
|
||||
log,
|
||||
"The previous eth1 hash is not in cache";
|
||||
"previous_hash" => format!("{:?}", prev_eth1_hash)
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
.skip_while(|eth1_block| !is_candidate_block(eth1_block, voting_period_start_seconds, spec))
|
||||
.take_while(|eth1_block| is_candidate_block(eth1_block, voting_period_start_seconds, spec))
|
||||
.filter_map(|eth1_block| {
|
||||
eth1_block
|
||||
.clone()
|
||||
.eth1_data()
|
||||
.map(|eth1_data| (eth1_data, eth1_block.number))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Selects and counts the votes in `state.eth1_data_votes`, if they appear in `new_eth1_data` or
|
||||
/// `all_eth1_data` when it is the voting period tail.
|
||||
/// Collect all valid votes that are cast during the current voting period.
|
||||
/// Return hashmap with count of each vote cast.
|
||||
fn collect_valid_votes<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
new_eth1_data: Eth1DataBlockNumber,
|
||||
all_eth1_data: Eth1DataBlockNumber,
|
||||
votes_to_consider: &HashMap<Eth1Data, BlockNumber>,
|
||||
) -> Eth1DataVoteCount {
|
||||
let mut valid_votes = HashMap::new();
|
||||
|
||||
state
|
||||
.eth1_data_votes
|
||||
.iter()
|
||||
.filter_map(|vote| {
|
||||
new_eth1_data
|
||||
.get(vote)
|
||||
.map(|block_number| (vote.clone(), *block_number))
|
||||
.or_else(|| {
|
||||
if is_period_tail(state) {
|
||||
all_eth1_data
|
||||
.get(vote)
|
||||
.map(|block_number| (vote.clone(), *block_number))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
if let Some(block_num) = votes_to_consider.get(vote) {
|
||||
Some((vote.clone(), *block_num))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.for_each(|(eth1_data, block_number)| {
|
||||
valid_votes
|
||||
@@ -572,19 +443,9 @@ fn collect_valid_votes<T: EthSpec>(
|
||||
.and_modify(|count| *count += 1)
|
||||
.or_insert(1_u64);
|
||||
});
|
||||
|
||||
valid_votes
|
||||
}
|
||||
|
||||
/// Indicates if the given `state` is in the tail of it's eth1 voting period (i.e., in the later
|
||||
/// slots).
|
||||
fn is_period_tail<E: EthSpec>(state: &BeaconState<E>) -> bool {
|
||||
let slots_per_eth1_voting_period = E::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let slot = state.slot % slots_per_eth1_voting_period;
|
||||
|
||||
slot >= slots_per_eth1_voting_period.integer_sqrt()
|
||||
}
|
||||
|
||||
/// Selects the winning vote from `valid_votes`.
|
||||
fn find_winning_vote(valid_votes: Eth1DataVoteCount) -> Option<Eth1Data> {
|
||||
valid_votes
|
||||
@@ -609,10 +470,24 @@ fn slot_start_seconds<T: EthSpec>(
|
||||
genesis_unix_seconds + slot.as_u64() * milliseconds_per_slot / 1_000
|
||||
}
|
||||
|
||||
/// Returns a boolean denoting if a given `Eth1Block` is a candidate for `Eth1Data` calculation
|
||||
/// at the timestamp `period_start`.
|
||||
///
|
||||
/// Note: `period_start` needs to be atleast (`spec.seconds_per_eth1_block * spec.eth1_follow_distance * 2`)
|
||||
/// for this function to return meaningful values.
|
||||
fn is_candidate_block(block: &Eth1Block, period_start: u64, spec: &ChainSpec) -> bool {
|
||||
block.timestamp
|
||||
<= period_start.saturating_sub(spec.seconds_per_eth1_block * spec.eth1_follow_distance)
|
||||
&& block.timestamp
|
||||
>= period_start
|
||||
.saturating_sub(spec.seconds_per_eth1_block * spec.eth1_follow_distance * 2)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use environment::null_logger;
|
||||
use std::iter::FromIterator;
|
||||
use types::{test_utils::DepositTestTask, MinimalEthSpec};
|
||||
|
||||
type E = MinimalEthSpec;
|
||||
@@ -625,9 +500,14 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_eth1_data_doesnt_panic() {
|
||||
random_eth1_data();
|
||||
fn get_voting_period_start_seconds(state: &BeaconState<E>, spec: &ChainSpec) -> u64 {
|
||||
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let voting_period_start_slot = (state.slot / period) * period;
|
||||
slot_start_seconds::<E>(
|
||||
state.genesis_time,
|
||||
spec.milliseconds_per_slot,
|
||||
voting_period_start_slot,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -709,7 +589,7 @@ mod test {
|
||||
|
||||
assert!(
|
||||
eth1_chain
|
||||
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec)
|
||||
.deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
|
||||
.is_ok(),
|
||||
"should succeed if cache is empty but no deposits are required"
|
||||
);
|
||||
@@ -718,7 +598,7 @@ mod test {
|
||||
|
||||
assert!(
|
||||
eth1_chain
|
||||
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec)
|
||||
.deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
|
||||
.is_err(),
|
||||
"should fail to get deposits if required, but cache is empty"
|
||||
);
|
||||
@@ -762,7 +642,7 @@ mod test {
|
||||
|
||||
assert!(
|
||||
eth1_chain
|
||||
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec)
|
||||
.deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
|
||||
.is_ok(),
|
||||
"should succeed if no deposits are required"
|
||||
);
|
||||
@@ -774,7 +654,7 @@ mod test {
|
||||
state.eth1_data.deposit_count = i as u64;
|
||||
|
||||
let deposits_for_inclusion = eth1_chain
|
||||
.deposits_for_block_inclusion(&state, &random_eth1_data(), spec)
|
||||
.deposits_for_block_inclusion(&state, &Eth1Data::default(), spec)
|
||||
.unwrap_or_else(|_| panic!("should find deposit for {}", i));
|
||||
|
||||
let expected_len =
|
||||
@@ -822,24 +702,20 @@ mod test {
|
||||
|
||||
let a = eth1_chain
|
||||
.eth1_data_for_block_production(&state, &spec)
|
||||
.expect("should produce first random eth1 data");
|
||||
let b = eth1_chain
|
||||
.eth1_data_for_block_production(&state, &spec)
|
||||
.expect("should produce second random eth1 data");
|
||||
|
||||
assert!(
|
||||
a != b,
|
||||
"random votes should be returned with an empty cache"
|
||||
.expect("should produce default eth1 data vote");
|
||||
assert_eq!(
|
||||
a, state.eth1_data,
|
||||
"default vote should be same as state.eth1_data"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eth1_data_unknown_previous_state() {
|
||||
fn default_vote() {
|
||||
let spec = &E::default_spec();
|
||||
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let eth1_follow_distance = spec.eth1_follow_distance;
|
||||
|
||||
let eth1_chain = get_eth1_chain();
|
||||
let store = eth1_chain.backend.store.clone();
|
||||
|
||||
assert_eq!(
|
||||
eth1_chain.use_dummy_backend, false,
|
||||
@@ -847,120 +723,41 @@ mod test {
|
||||
);
|
||||
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec);
|
||||
let mut prev_state = state.clone();
|
||||
|
||||
prev_state.slot = Slot::new(period * 1_000);
|
||||
state.slot = Slot::new(period * 1_000 + period / 2);
|
||||
state.slot = Slot::from(slots_per_eth1_voting_period * 10);
|
||||
let follow_distance_seconds = eth1_follow_distance * spec.seconds_per_eth1_block;
|
||||
let voting_period_start = get_voting_period_start_seconds(&state, &spec);
|
||||
let start_eth1_block = voting_period_start - follow_distance_seconds * 2;
|
||||
let end_eth1_block = voting_period_start - follow_distance_seconds;
|
||||
|
||||
// Add 50% of the votes so a lookup is required.
|
||||
for _ in 0..=period / 2 {
|
||||
state
|
||||
.eth1_data_votes
|
||||
.push(random_eth1_data())
|
||||
.expect("should push eth1 vote");
|
||||
}
|
||||
// Populate blocks cache with candidate eth1 blocks
|
||||
let blocks = (start_eth1_block..end_eth1_block)
|
||||
.map(|i| get_eth1_block(i, i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(0..2048).for_each(|i| {
|
||||
blocks.iter().for_each(|block| {
|
||||
eth1_chain
|
||||
.backend
|
||||
.core
|
||||
.blocks()
|
||||
.write()
|
||||
.insert_root_or_child(get_eth1_block(i, i))
|
||||
.insert_root_or_child(block.clone())
|
||||
.expect("should add blocks to cache");
|
||||
});
|
||||
|
||||
let expected_root = Hash256::from_low_u64_be(u64::max_value());
|
||||
prev_state.eth1_data.block_hash = expected_root;
|
||||
|
||||
assert!(
|
||||
prev_state.eth1_data != state.eth1_data,
|
||||
"test requires state eth1_data are different"
|
||||
);
|
||||
|
||||
store
|
||||
.put_state(
|
||||
&state
|
||||
.get_state_root(prev_state.slot)
|
||||
.expect("should find state root"),
|
||||
prev_state,
|
||||
)
|
||||
.expect("should store state");
|
||||
|
||||
let a = eth1_chain
|
||||
let vote = eth1_chain
|
||||
.eth1_data_for_block_production(&state, &spec)
|
||||
.expect("should produce first random eth1 data");
|
||||
let b = eth1_chain
|
||||
.eth1_data_for_block_production(&state, &spec)
|
||||
.expect("should produce second random eth1 data");
|
||||
|
||||
assert!(
|
||||
a != b,
|
||||
"random votes should be returned if the previous eth1 data block hash is unknown"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod prev_block_hash {
|
||||
use super::*;
|
||||
use store::MemoryStore;
|
||||
|
||||
#[test]
|
||||
fn without_store_lookup() {
|
||||
let spec = &E::default_spec();
|
||||
let store = Arc::new(MemoryStore::open());
|
||||
|
||||
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
.expect("should produce default eth1 data vote");
|
||||
|
||||
assert_eq!(
|
||||
eth1_block_hash_at_start_of_voting_period(store, &state),
|
||||
Ok(state.eth1_data.block_hash),
|
||||
"should return the states eth1 data in the first half of the period"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_store_lookup() {
|
||||
let spec = &E::default_spec();
|
||||
let store = Arc::new(MemoryStore::open());
|
||||
|
||||
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
let mut prev_state = state.clone();
|
||||
|
||||
state.slot = Slot::new(period / 2);
|
||||
|
||||
// Add 50% of the votes so a lookup is required.
|
||||
for _ in 0..=period / 2 {
|
||||
state
|
||||
.eth1_data_votes
|
||||
.push(random_eth1_data())
|
||||
.expect("should push eth1 vote");
|
||||
}
|
||||
|
||||
let expected_root = Hash256::from_low_u64_be(42);
|
||||
|
||||
prev_state.eth1_data.block_hash = expected_root;
|
||||
|
||||
assert!(
|
||||
prev_state.eth1_data != state.eth1_data,
|
||||
"test requires state eth1_data are different"
|
||||
);
|
||||
|
||||
store
|
||||
.put_state(
|
||||
&state
|
||||
.get_state_root(Slot::new(0))
|
||||
.expect("should find state root"),
|
||||
prev_state,
|
||||
)
|
||||
.expect("should store state");
|
||||
|
||||
assert_eq!(
|
||||
eth1_block_hash_at_start_of_voting_period(store, &state),
|
||||
Ok(expected_root),
|
||||
"should return the eth1_data from the previous state"
|
||||
vote,
|
||||
blocks
|
||||
.last()
|
||||
.expect("should have blocks")
|
||||
.clone()
|
||||
.eth1_data()
|
||||
.expect("should have valid eth1 data"),
|
||||
"default vote must correspond to last block in candidate blocks"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -968,132 +765,58 @@ mod test {
|
||||
mod eth1_data_sets {
|
||||
use super::*;
|
||||
|
||||
fn get_voting_period_start_seconds(state: &BeaconState<E>, spec: &ChainSpec) -> u64 {
|
||||
let period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let voting_period_start_slot = (state.slot / period) * period;
|
||||
slot_start_seconds::<E>(
|
||||
state.genesis_time,
|
||||
spec.milliseconds_per_slot,
|
||||
voting_period_start_slot,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_cache() {
|
||||
let log = null_logger().unwrap();
|
||||
|
||||
let spec = &E::default_spec();
|
||||
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
let prev_eth1_hash = Hash256::zero();
|
||||
|
||||
let blocks = vec![];
|
||||
|
||||
assert_eq!(
|
||||
eth1_data_sets(
|
||||
get_votes_to_consider(
|
||||
blocks.iter(),
|
||||
prev_eth1_hash,
|
||||
get_voting_period_start_seconds(&state, spec),
|
||||
&spec,
|
||||
&log
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_known_block_hash() {
|
||||
let log = null_logger().unwrap();
|
||||
|
||||
let mut spec = E::default_spec();
|
||||
spec.milliseconds_per_slot = 1_000;
|
||||
|
||||
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec);
|
||||
let prev_eth1_hash = Hash256::from_low_u64_be(42);
|
||||
|
||||
let blocks = vec![get_eth1_block(0, 0)];
|
||||
|
||||
assert_eq!(
|
||||
eth1_data_sets(
|
||||
blocks.iter(),
|
||||
prev_eth1_hash,
|
||||
get_voting_period_start_seconds(&state, &spec),
|
||||
&spec,
|
||||
&log
|
||||
),
|
||||
None
|
||||
HashMap::new()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ideal_scenario() {
|
||||
let log = null_logger().unwrap();
|
||||
|
||||
let mut spec = E::default_spec();
|
||||
spec.milliseconds_per_slot = 1_000;
|
||||
let spec = E::default_spec();
|
||||
|
||||
let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let eth1_follow_distance = spec.eth1_follow_distance;
|
||||
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), &spec);
|
||||
state.genesis_time = 0;
|
||||
state.slot = Slot::from(slots_per_eth1_voting_period * 3);
|
||||
state.slot = Slot::from(slots_per_eth1_voting_period * 10);
|
||||
|
||||
let prev_eth1_hash = Hash256::zero();
|
||||
|
||||
let blocks = (0..eth1_follow_distance * 4)
|
||||
let follow_distance_seconds = eth1_follow_distance * spec.seconds_per_eth1_block;
|
||||
let voting_period_start = get_voting_period_start_seconds(&state, &spec);
|
||||
let start_eth1_block = voting_period_start - follow_distance_seconds * 2;
|
||||
let end_eth1_block = voting_period_start - follow_distance_seconds;
|
||||
let blocks = (start_eth1_block..end_eth1_block)
|
||||
.map(|i| get_eth1_block(i, i))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (new_eth1_data, all_eth1_data) = eth1_data_sets(
|
||||
blocks.iter(),
|
||||
prev_eth1_hash,
|
||||
get_voting_period_start_seconds(&state, &spec),
|
||||
&spec,
|
||||
&log,
|
||||
)
|
||||
.expect("should find data");
|
||||
|
||||
let votes_to_consider =
|
||||
get_votes_to_consider(blocks.iter(), voting_period_start, &spec);
|
||||
assert_eq!(
|
||||
all_eth1_data.len(),
|
||||
eth1_follow_distance as usize * 2,
|
||||
"all_eth1_data should have appropriate length"
|
||||
);
|
||||
assert_eq!(
|
||||
new_eth1_data.len(),
|
||||
eth1_follow_distance as usize,
|
||||
"new_eth1_data should have appropriate length"
|
||||
votes_to_consider.len() as u64,
|
||||
end_eth1_block - start_eth1_block,
|
||||
"all produced eth1 blocks should be in votes to consider"
|
||||
);
|
||||
|
||||
for (eth1_data, block_number) in &new_eth1_data {
|
||||
assert_eq!(
|
||||
all_eth1_data.get(eth1_data),
|
||||
Some(block_number),
|
||||
"all_eth1_data should contain all items in new_eth1_data"
|
||||
);
|
||||
}
|
||||
|
||||
(1..=eth1_follow_distance * 2)
|
||||
(start_eth1_block..end_eth1_block)
|
||||
.map(|i| get_eth1_block(i, i))
|
||||
.for_each(|eth1_block| {
|
||||
assert_eq!(
|
||||
eth1_block.number,
|
||||
*all_eth1_data
|
||||
*votes_to_consider
|
||||
.get(ð1_block.clone().eth1_data().unwrap())
|
||||
.expect("all_eth1_data should have expected block")
|
||||
)
|
||||
});
|
||||
|
||||
(eth1_follow_distance + 1..=eth1_follow_distance * 2)
|
||||
.map(|i| get_eth1_block(i, i))
|
||||
.for_each(|eth1_block| {
|
||||
assert_eq!(
|
||||
eth1_block.number,
|
||||
*new_eth1_data
|
||||
.get(ð1_block.clone().eth1_data().unwrap())
|
||||
.unwrap_or_else(|| panic!(
|
||||
"new_eth1_data should have expected block #{}",
|
||||
eth1_block.number
|
||||
))
|
||||
.expect("votes_to_consider should have expected block numbers")
|
||||
)
|
||||
});
|
||||
}
|
||||
@@ -1130,13 +853,11 @@ mod test {
|
||||
let spec = &E::default_spec();
|
||||
let state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
|
||||
let all_eth1_data = get_eth1_data_vec(slots, 0);
|
||||
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
|
||||
let votes_to_consider = get_eth1_data_vec(slots, 0);
|
||||
|
||||
let votes = collect_valid_votes(
|
||||
&state,
|
||||
HashMap::from_iter(new_eth1_data.into_iter()),
|
||||
HashMap::from_iter(all_eth1_data.into_iter()),
|
||||
&HashMap::from_iter(votes_to_consider.clone().into_iter()),
|
||||
);
|
||||
assert_eq!(
|
||||
votes.len(),
|
||||
@@ -1151,10 +872,9 @@ mod test {
|
||||
let spec = &E::default_spec();
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
|
||||
let all_eth1_data = get_eth1_data_vec(slots, 0);
|
||||
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
|
||||
let votes_to_consider = get_eth1_data_vec(slots, 0);
|
||||
|
||||
state.eth1_data_votes = new_eth1_data[0..slots as usize / 4]
|
||||
state.eth1_data_votes = votes_to_consider[0..slots as usize / 4]
|
||||
.iter()
|
||||
.map(|(eth1_data, _)| eth1_data)
|
||||
.cloned()
|
||||
@@ -1163,12 +883,11 @@ mod test {
|
||||
|
||||
let votes = collect_valid_votes(
|
||||
&state,
|
||||
HashMap::from_iter(new_eth1_data.clone().into_iter()),
|
||||
HashMap::from_iter(all_eth1_data.into_iter()),
|
||||
&HashMap::from_iter(votes_to_consider.clone().into_iter()),
|
||||
);
|
||||
assert_votes!(
|
||||
votes,
|
||||
new_eth1_data[0..slots as usize / 4].to_vec(),
|
||||
votes_to_consider[0..slots as usize / 4].to_vec(),
|
||||
"should find as many votes as were in the state"
|
||||
);
|
||||
}
|
||||
@@ -1179,10 +898,9 @@ mod test {
|
||||
let spec = &E::default_spec();
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
|
||||
let all_eth1_data = get_eth1_data_vec(slots, 0);
|
||||
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
|
||||
let votes_to_consider = get_eth1_data_vec(slots, 0);
|
||||
|
||||
let duplicate_eth1_data = new_eth1_data
|
||||
let duplicate_eth1_data = votes_to_consider
|
||||
.last()
|
||||
.expect("should have some eth1 data")
|
||||
.clone();
|
||||
@@ -1196,8 +914,7 @@ mod test {
|
||||
|
||||
let votes = collect_valid_votes(
|
||||
&state,
|
||||
HashMap::from_iter(new_eth1_data.into_iter()),
|
||||
HashMap::from_iter(all_eth1_data.into_iter()),
|
||||
&HashMap::from_iter(votes_to_consider.clone().into_iter()),
|
||||
);
|
||||
assert_votes!(
|
||||
votes,
|
||||
@@ -1214,70 +931,6 @@ mod test {
|
||||
"should have four votes"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_period_tail() {
|
||||
let slots = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let spec = &E::default_spec();
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
state.slot = Slot::from(<E as EthSpec>::SlotsPerEpoch::to_u64()) * 10;
|
||||
|
||||
let all_eth1_data = get_eth1_data_vec(slots, 0);
|
||||
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
|
||||
|
||||
let non_new_eth1_data = all_eth1_data
|
||||
.first()
|
||||
.expect("should have some eth1 data")
|
||||
.clone();
|
||||
|
||||
state.eth1_data_votes = vec![non_new_eth1_data.0].into();
|
||||
|
||||
let votes = collect_valid_votes(
|
||||
&state,
|
||||
HashMap::from_iter(new_eth1_data.into_iter()),
|
||||
HashMap::from_iter(all_eth1_data.into_iter()),
|
||||
);
|
||||
|
||||
assert_votes!(
|
||||
votes,
|
||||
vec![],
|
||||
"should not find votes from all_eth1_data when it is not the period tail"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn period_tail() {
|
||||
let slots_per_eth1_voting_period = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
|
||||
let slots = <E as EthSpec>::SlotsPerEth1VotingPeriod::to_u64();
|
||||
let spec = &E::default_spec();
|
||||
let mut state: BeaconState<E> = BeaconState::new(0, get_eth1_data(0), spec);
|
||||
|
||||
state.slot = Slot::from(<E as EthSpec>::SlotsPerEpoch::to_u64()) * 10
|
||||
+ slots_per_eth1_voting_period.integer_sqrt();
|
||||
|
||||
let all_eth1_data = get_eth1_data_vec(slots, 0);
|
||||
let new_eth1_data = all_eth1_data[slots as usize / 2..].to_vec();
|
||||
|
||||
let non_new_eth1_data = all_eth1_data
|
||||
.first()
|
||||
.expect("should have some eth1 data")
|
||||
.clone();
|
||||
|
||||
state.eth1_data_votes = vec![non_new_eth1_data.0.clone()].into();
|
||||
|
||||
let votes = collect_valid_votes(
|
||||
&state,
|
||||
HashMap::from_iter(new_eth1_data.into_iter()),
|
||||
HashMap::from_iter(all_eth1_data.into_iter()),
|
||||
);
|
||||
|
||||
assert_votes!(
|
||||
votes,
|
||||
vec![non_new_eth1_data],
|
||||
"should find all_eth1_data votes when it is the period tail"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod winning_vote {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::marker::PhantomData;
|
||||
use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256};
|
||||
use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock};
|
||||
pub use websocket_server::WebSocketSender;
|
||||
|
||||
pub trait EventHandler<T: EthSpec>: Sized + Send + Sync {
|
||||
@@ -49,11 +49,11 @@ pub enum EventKind<T: EthSpec> {
|
||||
},
|
||||
BeaconBlockImported {
|
||||
block_root: Hash256,
|
||||
block: Box<BeaconBlock<T>>,
|
||||
block: Box<SignedBeaconBlock<T>>,
|
||||
},
|
||||
BeaconBlockRejected {
|
||||
reason: String,
|
||||
block: Box<BeaconBlock<T>>,
|
||||
block: Box<SignedBeaconBlock<T>>,
|
||||
},
|
||||
BeaconAttestationImported {
|
||||
attestation: Box<Attestation<T>>,
|
||||
|
||||
@@ -306,8 +306,11 @@ impl CheckpointManager {
|
||||
.ok_or_else(|| Error::UnknownJustifiedBlock(block_root))?;
|
||||
|
||||
let state = chain
|
||||
.get_state_caching_only_with_committee_caches(&block.state_root, Some(block.slot))?
|
||||
.ok_or_else(|| Error::UnknownJustifiedState(block.state_root))?;
|
||||
.get_state_caching_only_with_committee_caches(
|
||||
&block.state_root(),
|
||||
Some(block.slot()),
|
||||
)?
|
||||
.ok_or_else(|| Error::UnknownJustifiedState(block.state_root()))?;
|
||||
|
||||
Ok(get_effective_balances(&state))
|
||||
}
|
||||
|
||||
@@ -152,8 +152,8 @@ lazy_static! {
|
||||
/*
|
||||
* Eth1
|
||||
*/
|
||||
pub static ref JUNK_ETH1_VOTES: Result<IntCounter> =
|
||||
try_create_int_counter("beacon_eth1_junk_votes", "Count of times we have voted junk for eth1 dat");
|
||||
pub static ref DEFAULT_ETH1_VOTES: Result<IntCounter> =
|
||||
try_create_int_counter("beacon_eth1_default_votes", "Count of times we have voted default value for eth1 data");
|
||||
|
||||
/*
|
||||
* Chain Head
|
||||
|
||||
@@ -16,10 +16,9 @@ use store::{
|
||||
migrate::{BlockingMigrator, NullMigrator},
|
||||
DiskStore, MemoryStore, Migrate, Store,
|
||||
};
|
||||
use tree_hash::{SignedRoot, TreeHash};
|
||||
use types::{
|
||||
AggregateSignature, Attestation, BeaconBlock, BeaconState, BitList, ChainSpec, Domain, EthSpec,
|
||||
Hash256, Keypair, SecretKey, Signature, Slot,
|
||||
AggregateSignature, Attestation, BeaconState, BitList, ChainSpec, Domain, EthSpec, Hash256,
|
||||
Keypair, SecretKey, Signature, SignedBeaconBlock, SignedRoot, Slot,
|
||||
};
|
||||
|
||||
pub use crate::persisted_beacon_chain::{PersistedBeaconChain, BEACON_CHAIN_DB_KEY};
|
||||
@@ -279,7 +278,7 @@ where
|
||||
mut state: BeaconState<E>,
|
||||
slot: Slot,
|
||||
block_strategy: BlockStrategy,
|
||||
) -> (BeaconBlock<E>, BeaconState<E>) {
|
||||
) -> (SignedBeaconBlock<E>, BeaconState<E>) {
|
||||
if slot < state.slot {
|
||||
panic!("produce slot cannot be prior to the state slot");
|
||||
}
|
||||
@@ -308,24 +307,19 @@ where
|
||||
|
||||
let randao_reveal = {
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let message = epoch.tree_hash_root();
|
||||
let domain = self.spec.get_domain(epoch, Domain::Randao, fork);
|
||||
Signature::new(&message, domain, sk)
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), sk)
|
||||
};
|
||||
|
||||
let (mut block, state) = self
|
||||
let (block, state) = self
|
||||
.chain
|
||||
.produce_block_on_state(state, slot, randao_reveal)
|
||||
.expect("should produce block");
|
||||
|
||||
block.signature = {
|
||||
let message = block.signed_root();
|
||||
let epoch = block.slot.epoch(E::slots_per_epoch());
|
||||
let domain = self.spec.get_domain(epoch, Domain::BeaconProposer, fork);
|
||||
Signature::new(&message, domain, sk)
|
||||
};
|
||||
let signed_block = block.sign(sk, &state.fork, &self.spec);
|
||||
|
||||
(block, state)
|
||||
(signed_block, state)
|
||||
}
|
||||
|
||||
/// Adds attestations to the `BeaconChain` operations pool and fork choice.
|
||||
@@ -407,18 +401,17 @@ where
|
||||
.expect("should be able to set aggregation bits");
|
||||
|
||||
let signature = {
|
||||
let message = data.tree_hash_root();
|
||||
|
||||
let domain = spec.get_domain(
|
||||
data.target.epoch,
|
||||
Domain::BeaconAttester,
|
||||
fork,
|
||||
);
|
||||
|
||||
let message = data.signing_root(domain);
|
||||
|
||||
let mut agg_sig = AggregateSignature::new();
|
||||
agg_sig.add(&Signature::new(
|
||||
&message,
|
||||
domain,
|
||||
message.as_bytes(),
|
||||
self.get_sk(*validator_index),
|
||||
));
|
||||
|
||||
@@ -464,7 +457,7 @@ where
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_block
|
||||
.slot;
|
||||
.slot();
|
||||
|
||||
// Move to the next slot so we may produce some more blocks on the head.
|
||||
self.advance_slot();
|
||||
|
||||
@@ -267,7 +267,7 @@ fn epoch_boundary_state_attestation_processing() {
|
||||
&AttestationStrategy::SomeValidators(late_validators.clone()),
|
||||
&head.beacon_state,
|
||||
head.beacon_block_root,
|
||||
head.beacon_block.slot,
|
||||
head.beacon_block.slot(),
|
||||
));
|
||||
|
||||
harness.advance_slot();
|
||||
@@ -283,9 +283,9 @@ fn epoch_boundary_state_attestation_processing() {
|
||||
for attestation in late_attestations {
|
||||
// load_epoch_boundary_state is idempotent!
|
||||
let block_root = attestation.data.beacon_block_root;
|
||||
let block: BeaconBlock<E> = store.get(&block_root).unwrap().expect("block exists");
|
||||
let block = store.get_block(&block_root).unwrap().expect("block exists");
|
||||
let epoch_boundary_state = store
|
||||
.load_epoch_boundary_state(&block.state_root)
|
||||
.load_epoch_boundary_state(&block.state_root())
|
||||
.expect("no error")
|
||||
.expect("epoch boundary state exists");
|
||||
let ebs_of_ebs = store
|
||||
@@ -396,7 +396,7 @@ fn check_chain_dump(harness: &TestHarness, expected_len: u64) {
|
||||
// Check the forwards block roots iterator against the chain dump
|
||||
let chain_dump_block_roots = chain_dump
|
||||
.iter()
|
||||
.map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot))
|
||||
.map(|checkpoint| (checkpoint.beacon_block_root, checkpoint.beacon_block.slot()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let head = harness.chain.head().expect("should get head");
|
||||
|
||||
@@ -121,7 +121,7 @@ fn iterators() {
|
||||
|
||||
assert_eq!(
|
||||
*block_roots.first().expect("should have some block roots"),
|
||||
(head.beacon_block_root, head.beacon_block.slot),
|
||||
(head.beacon_block_root, head.beacon_block.slot()),
|
||||
"first block root and slot should be for the head block"
|
||||
);
|
||||
|
||||
@@ -435,7 +435,7 @@ fn attestations_with_increasing_slots() {
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_block
|
||||
.slot,
|
||||
.slot(),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -524,7 +524,7 @@ fn run_skip_slot_test(skip_slots: u64) {
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_block
|
||||
.slot,
|
||||
.slot(),
|
||||
Slot::new(skip_slots + 1)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -533,7 +533,7 @@ fn run_skip_slot_test(skip_slots: u64) {
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_block
|
||||
.slot,
|
||||
.slot(),
|
||||
Slot::new(0)
|
||||
);
|
||||
|
||||
@@ -566,7 +566,7 @@ fn run_skip_slot_test(skip_slots: u64) {
|
||||
.head()
|
||||
.expect("should get head")
|
||||
.beacon_block
|
||||
.slot,
|
||||
.slot(),
|
||||
Slot::new(skip_slots + 1)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use eth1::http::{get_deposit_count, get_deposit_logs_in_range, get_deposit_root,
|
||||
use eth1::{Config, Service};
|
||||
use eth1::{DepositCache, DepositLog};
|
||||
use eth1_test_rig::GanacheEth1Instance;
|
||||
use exit_future;
|
||||
use futures::Future;
|
||||
use merkle_proof::verify_merkle_proof;
|
||||
use std::ops::Range;
|
||||
@@ -101,90 +100,6 @@ fn get_block_number(runtime: &mut Runtime, web3: &Web3<Http>) -> u64 {
|
||||
.expect("should get block number")
|
||||
}
|
||||
|
||||
mod auto_update {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn can_auto_update() {
|
||||
let mut env = new_env();
|
||||
let log = env.core_context().log;
|
||||
let runtime = env.runtime();
|
||||
|
||||
let eth1 = runtime
|
||||
.block_on(GanacheEth1Instance::new())
|
||||
.expect("should start eth1 environment");
|
||||
let deposit_contract = ð1.deposit_contract;
|
||||
let web3 = eth1.web3();
|
||||
|
||||
let now = get_block_number(runtime, &web3);
|
||||
|
||||
let service = Service::new(
|
||||
Config {
|
||||
endpoint: eth1.endpoint(),
|
||||
deposit_contract_address: deposit_contract.address(),
|
||||
deposit_contract_deploy_block: now,
|
||||
lowest_cached_block_number: now,
|
||||
follow_distance: 0,
|
||||
block_cache_truncation: None,
|
||||
..Config::default()
|
||||
},
|
||||
log,
|
||||
);
|
||||
|
||||
// NOTE: this test is sensitive to the response speed of the external web3 server. If
|
||||
// you're experiencing failures, try increasing the update_interval.
|
||||
let update_interval = Duration::from_millis(3000);
|
||||
|
||||
assert_eq!(
|
||||
service.block_cache_len(),
|
||||
0,
|
||||
"should have imported no blocks"
|
||||
);
|
||||
assert_eq!(
|
||||
service.deposit_cache_len(),
|
||||
0,
|
||||
"should have imported no deposits"
|
||||
);
|
||||
|
||||
let (_exit, signal) = exit_future::signal();
|
||||
|
||||
runtime.executor().spawn(service.auto_update(signal));
|
||||
|
||||
let n = 4;
|
||||
|
||||
for _ in 0..n {
|
||||
deposit_contract
|
||||
.deposit(runtime, random_deposit_data())
|
||||
.expect("should do first deposits");
|
||||
}
|
||||
|
||||
std::thread::sleep(update_interval * 5);
|
||||
|
||||
assert!(
|
||||
service.deposit_cache_len() >= n,
|
||||
"should have imported n deposits"
|
||||
);
|
||||
|
||||
for _ in 0..n {
|
||||
deposit_contract
|
||||
.deposit(runtime, random_deposit_data())
|
||||
.expect("should do second deposits");
|
||||
}
|
||||
|
||||
std::thread::sleep(update_interval * 4);
|
||||
|
||||
assert!(
|
||||
service.block_cache_len() >= n * 2,
|
||||
"should have imported all blocks"
|
||||
);
|
||||
assert!(
|
||||
service.deposit_cache_len() >= n * 2,
|
||||
"should have imported all deposits, not {}",
|
||||
service.deposit_cache_len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod eth1_cache {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -4,11 +4,7 @@ use rayon::prelude::*;
|
||||
use ssz::Encode;
|
||||
use state_processing::initialize_beacon_state_from_eth1;
|
||||
use std::time::SystemTime;
|
||||
use tree_hash::SignedRoot;
|
||||
use types::{
|
||||
BeaconState, ChainSpec, DepositData, Domain, EthSpec, Fork, Hash256, Keypair, PublicKey,
|
||||
Signature,
|
||||
};
|
||||
use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature};
|
||||
|
||||
/// Builds a genesis state as defined by the Eth2 interop procedure (see below).
|
||||
///
|
||||
@@ -39,12 +35,7 @@ pub fn interop_genesis_state<T: EthSpec>(
|
||||
signature: Signature::empty_signature().into(),
|
||||
};
|
||||
|
||||
let domain = spec.get_domain(
|
||||
spec.genesis_slot.epoch(T::slots_per_epoch()),
|
||||
Domain::Deposit,
|
||||
&Fork::default(),
|
||||
);
|
||||
data.signature = Signature::new(&data.signed_root()[..], domain, &keypair.sk).into();
|
||||
data.signature = data.create_signature(&keypair.sk, spec);
|
||||
|
||||
data
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ use slog::{debug, o, trace, warn};
|
||||
use ssz::{Decode, DecodeError};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{Attestation, AttesterSlashing, BeaconBlock, ProposerSlashing, VoluntaryExit};
|
||||
use types::{Attestation, AttesterSlashing, ProposerSlashing, SignedBeaconBlock, VoluntaryExit};
|
||||
|
||||
/// Handles messages received from the network and client and organises syncing. This
|
||||
/// functionality of this struct is to validate an decode messages from the network before
|
||||
@@ -319,9 +319,9 @@ impl<T: BeaconChainTypes> MessageHandler<T> {
|
||||
fn decode_gossip_block(
|
||||
&self,
|
||||
beacon_block: Vec<u8>,
|
||||
) -> Result<BeaconBlock<T::EthSpec>, DecodeError> {
|
||||
) -> Result<SignedBeaconBlock<T::EthSpec>, DecodeError> {
|
||||
//TODO: Apply verification before decoding.
|
||||
BeaconBlock::from_ssz_bytes(&beacon_block)
|
||||
SignedBeaconBlock::from_ssz_bytes(&beacon_block)
|
||||
}
|
||||
|
||||
fn decode_gossip_attestation(
|
||||
@@ -355,13 +355,13 @@ impl<T: BeaconChainTypes> MessageHandler<T> {
|
||||
|
||||
/* Req/Resp Domain Decoding */
|
||||
|
||||
/// Verifies and decodes an ssz-encoded `BeaconBlock`. If `None` is passed, this represents a
|
||||
/// Verifies and decodes an ssz-encoded `SignedBeaconBlock`. If `None` is passed, this represents a
|
||||
/// stream termination.
|
||||
fn decode_beacon_block(
|
||||
&self,
|
||||
beacon_block: Vec<u8>,
|
||||
) -> Result<BeaconBlock<T::EthSpec>, DecodeError> {
|
||||
) -> Result<SignedBeaconBlock<T::EthSpec>, DecodeError> {
|
||||
//TODO: Implement faster block verification before decoding entirely
|
||||
BeaconBlock::from_ssz_bytes(&beacon_block)
|
||||
SignedBeaconBlock::from_ssz_bytes(&beacon_block)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ use ssz::Encode;
|
||||
use std::sync::Arc;
|
||||
use store::Store;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tree_hash::SignedRoot;
|
||||
use types::{Attestation, BeaconBlock, Epoch, EthSpec, Hash256, Slot};
|
||||
use types::{Attestation, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot};
|
||||
|
||||
//TODO: Rate limit requests
|
||||
|
||||
@@ -263,7 +262,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
} else if self
|
||||
.chain
|
||||
.store
|
||||
.exists::<BeaconBlock<T::EthSpec>>(&remote.head_root)
|
||||
.exists::<SignedBeaconBlock<T::EthSpec>>(&remote.head_root)
|
||||
.unwrap_or_else(|_| false)
|
||||
{
|
||||
trace!(
|
||||
@@ -300,7 +299,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
) {
|
||||
let mut send_block_count = 0;
|
||||
for root in request.block_roots.iter() {
|
||||
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock<T::EthSpec>>(root) {
|
||||
if let Ok(Some(block)) = self.chain.store.get_block(root) {
|
||||
self.network.send_rpc_response(
|
||||
peer_id.clone(),
|
||||
request_id,
|
||||
@@ -380,11 +379,11 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
|
||||
let mut blocks_sent = 0;
|
||||
for root in block_roots {
|
||||
if let Ok(Some(block)) = self.chain.store.get::<BeaconBlock<T::EthSpec>>(&root) {
|
||||
if let Ok(Some(block)) = self.chain.store.get_block(&root) {
|
||||
// Due to skip slots, blocks could be out of the range, we ensure they are in the
|
||||
// range before sending
|
||||
if block.slot >= req.start_slot
|
||||
&& block.slot < req.start_slot + req.count * req.step
|
||||
if block.slot() >= req.start_slot
|
||||
&& block.slot() < req.start_slot + req.count * req.step
|
||||
{
|
||||
blocks_sent += 1;
|
||||
self.network.send_rpc_response(
|
||||
@@ -437,7 +436,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
beacon_block: Option<BeaconBlock<T::EthSpec>>,
|
||||
beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
|
||||
) {
|
||||
let beacon_block = beacon_block.map(Box::new);
|
||||
trace!(
|
||||
@@ -458,7 +457,7 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
beacon_block: Option<BeaconBlock<T::EthSpec>>,
|
||||
beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
|
||||
) {
|
||||
let beacon_block = beacon_block.map(Box::new);
|
||||
trace!(
|
||||
@@ -479,7 +478,11 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
/// Attempts to apply to block to the beacon chain. May queue the block for later processing.
|
||||
///
|
||||
/// Returns a `bool` which, if `true`, indicates we should forward the block to our peers.
|
||||
pub fn on_block_gossip(&mut self, peer_id: PeerId, block: BeaconBlock<T::EthSpec>) -> bool {
|
||||
pub fn on_block_gossip(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block: SignedBeaconBlock<T::EthSpec>,
|
||||
) -> bool {
|
||||
match self.chain.process_block(block.clone()) {
|
||||
Ok(outcome) => match outcome {
|
||||
BlockProcessingOutcome::Processed { .. } => {
|
||||
@@ -528,8 +531,8 @@ impl<T: BeaconChainTypes> MessageProcessor<T> {
|
||||
self.log,
|
||||
"Invalid gossip beacon block";
|
||||
"outcome" => format!("{:?}", other),
|
||||
"block root" => format!("{}", Hash256::from_slice(&block.signed_root()[..])),
|
||||
"block slot" => block.slot
|
||||
"block root" => format!("{}", block.canonical_root()),
|
||||
"block slot" => block.slot()
|
||||
);
|
||||
trace!(
|
||||
self.log,
|
||||
|
||||
@@ -50,7 +50,7 @@ use std::collections::HashSet;
|
||||
use std::ops::Sub;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use types::{BeaconBlock, EthSpec, Hash256};
|
||||
use types::{EthSpec, Hash256, SignedBeaconBlock};
|
||||
|
||||
/// The number of slots ahead of us that is allowed before requesting a long-range (batch) Sync
|
||||
/// from a peer. If a peer is within this tolerance (forwards or backwards), it is treated as a
|
||||
@@ -73,18 +73,18 @@ pub enum SyncMessage<T: EthSpec> {
|
||||
BlocksByRangeResponse {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
beacon_block: Option<Box<BeaconBlock<T>>>,
|
||||
beacon_block: Option<Box<SignedBeaconBlock<T>>>,
|
||||
},
|
||||
|
||||
/// A `BlocksByRoot` response has been received.
|
||||
BlocksByRootResponse {
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
beacon_block: Option<Box<BeaconBlock<T>>>,
|
||||
beacon_block: Option<Box<SignedBeaconBlock<T>>>,
|
||||
},
|
||||
|
||||
/// A block with an unknown parent has been received.
|
||||
UnknownBlock(PeerId, Box<BeaconBlock<T>>),
|
||||
UnknownBlock(PeerId, Box<SignedBeaconBlock<T>>),
|
||||
|
||||
/// A peer has sent an object that references a block that is unknown. This triggers the
|
||||
/// manager to attempt to find the block matching the unknown hash.
|
||||
@@ -107,7 +107,7 @@ pub enum SyncMessage<T: EthSpec> {
|
||||
/// Maintains a sequential list of parents to lookup and the lookup's current state.
|
||||
struct ParentRequests<T: EthSpec> {
|
||||
/// The blocks that have currently been downloaded.
|
||||
downloaded_blocks: Vec<BeaconBlock<T>>,
|
||||
downloaded_blocks: Vec<SignedBeaconBlock<T>>,
|
||||
|
||||
/// The number of failed attempts to retrieve a parent block. If too many attempts occur, this
|
||||
/// lookup is failed and rejected.
|
||||
@@ -290,7 +290,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
block: Option<BeaconBlock<T::EthSpec>>,
|
||||
block: Option<SignedBeaconBlock<T::EthSpec>>,
|
||||
) {
|
||||
// check if this is a single block lookup - i.e we were searching for a specific hash
|
||||
if block.is_some() {
|
||||
@@ -344,7 +344,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
fn single_block_lookup_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
block: SignedBeaconBlock<T::EthSpec>,
|
||||
expected_block_hash: Hash256,
|
||||
) {
|
||||
// verify the hash is correct and try and process the block
|
||||
@@ -398,7 +398,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
|
||||
/// A block has been sent to us that has an unknown parent. This begins a parent lookup search
|
||||
/// to find the parent or chain of parents that match our current chain.
|
||||
fn add_unknown_block(&mut self, peer_id: PeerId, block: BeaconBlock<T::EthSpec>) {
|
||||
fn add_unknown_block(&mut self, peer_id: PeerId, block: SignedBeaconBlock<T::EthSpec>) {
|
||||
// If we are not in regular sync mode, ignore this block
|
||||
if self.state != ManagerState::Regular {
|
||||
return;
|
||||
@@ -516,7 +516,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
// fail loudly
|
||||
}
|
||||
let previous_index = parent_request.downloaded_blocks.len() - 2;
|
||||
let expected_hash = parent_request.downloaded_blocks[previous_index].parent_root;
|
||||
let expected_hash = parent_request.downloaded_blocks[previous_index].parent_root();
|
||||
|
||||
// Note: the length must be greater than 2 so this cannot panic.
|
||||
let block_hash = parent_request
|
||||
@@ -626,7 +626,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
.downloaded_blocks
|
||||
.last()
|
||||
.expect("The parent queue should never be empty")
|
||||
.parent_root;
|
||||
.parent_root();
|
||||
let request = BlocksByRootRequest {
|
||||
block_roots: vec![parent_hash],
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Sub;
|
||||
use types::{BeaconBlock, EthSpec, Hash256, Slot};
|
||||
use types::{EthSpec, Hash256, SignedBeaconBlock, Slot};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct BatchId(pub u64);
|
||||
@@ -55,7 +55,7 @@ pub struct Batch<T: EthSpec> {
|
||||
/// Marks the batch as undergoing a re-process, with a hash of the original blocks it received.
|
||||
pub original_hash: Option<u64>,
|
||||
/// The blocks that have been downloaded.
|
||||
pub downloaded_blocks: Vec<BeaconBlock<T>>,
|
||||
pub downloaded_blocks: Vec<SignedBeaconBlock<T>>,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Eq for Batch<T> {}
|
||||
@@ -164,7 +164,7 @@ impl<T: EthSpec> PendingBatches<T> {
|
||||
|
||||
/// Adds a block to the batches if the request id exists. Returns None if there is no batch
|
||||
/// matching the request id.
|
||||
pub fn add_block(&mut self, request_id: RequestId, block: BeaconBlock<T>) -> Option<()> {
|
||||
pub fn add_block(&mut self, request_id: RequestId, block: SignedBeaconBlock<T>) -> Option<()> {
|
||||
let batch = self.batches.get_mut(&request_id)?;
|
||||
batch.downloaded_blocks.push(block);
|
||||
Some(())
|
||||
|
||||
@@ -65,7 +65,7 @@ fn process_batch<T: BeaconChainTypes>(
|
||||
// The block was valid and we processed it successfully.
|
||||
trace!(
|
||||
log, "Imported block from network";
|
||||
"slot" => block.slot,
|
||||
"slot" => block.slot(),
|
||||
"block_root" => format!("{}", block_root),
|
||||
);
|
||||
successful_block_import = true;
|
||||
@@ -75,21 +75,21 @@ fn process_batch<T: BeaconChainTypes>(
|
||||
warn!(
|
||||
log, "Parent block is unknown";
|
||||
"parent_root" => format!("{}", parent),
|
||||
"baby_block_slot" => block.slot,
|
||||
"baby_block_slot" => block.slot(),
|
||||
);
|
||||
if successful_block_import {
|
||||
run_fork_choice(chain, log);
|
||||
}
|
||||
return Err(format!(
|
||||
"Block at slot {} has an unknown parent.",
|
||||
block.slot
|
||||
block.slot()
|
||||
));
|
||||
}
|
||||
BlockProcessingOutcome::BlockIsAlreadyKnown => {
|
||||
// this block is already known to us, move to the next
|
||||
debug!(
|
||||
log, "Imported a block that is already known";
|
||||
"block_slot" => block.slot,
|
||||
"block_slot" => block.slot(),
|
||||
);
|
||||
}
|
||||
BlockProcessingOutcome::FutureSlot {
|
||||
@@ -110,7 +110,7 @@ fn process_batch<T: BeaconChainTypes>(
|
||||
}
|
||||
return Err(format!(
|
||||
"Block at slot {} is too far in the future",
|
||||
block.slot
|
||||
block.slot()
|
||||
));
|
||||
} else {
|
||||
// The block is in the future, but not too far.
|
||||
@@ -144,7 +144,7 @@ fn process_batch<T: BeaconChainTypes>(
|
||||
if successful_block_import {
|
||||
run_fork_choice(chain, log);
|
||||
}
|
||||
return Err(format!("Invalid block at slot {}", block.slot));
|
||||
return Err(format!("Invalid block at slot {}", block.slot()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -10,7 +10,7 @@ use slog::{crit, debug, warn};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{BeaconBlock, Hash256, Slot};
|
||||
use types::{Hash256, SignedBeaconBlock, Slot};
|
||||
|
||||
/// Blocks are downloaded in batches from peers. This constant specifies how many blocks per batch
|
||||
/// is requested. There is a timeout for each batch request. If this value is too high, we will
|
||||
@@ -143,7 +143,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
&mut self,
|
||||
network: &mut SyncNetworkContext,
|
||||
request_id: RequestId,
|
||||
beacon_block: &Option<BeaconBlock<T::EthSpec>>,
|
||||
beacon_block: &Option<SignedBeaconBlock<T::EthSpec>>,
|
||||
) -> Option<()> {
|
||||
if let Some(block) = beacon_block {
|
||||
// This is not a stream termination, simply add the block to the request
|
||||
@@ -171,11 +171,11 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
|
||||
// verify the range of received blocks
|
||||
// Note that the order of blocks is verified in block processing
|
||||
if let Some(last_slot) = batch.downloaded_blocks.last().map(|b| b.slot) {
|
||||
if let Some(last_slot) = batch.downloaded_blocks.last().map(|b| b.slot()) {
|
||||
// the batch is non-empty
|
||||
if batch.start_slot > batch.downloaded_blocks[0].slot || batch.end_slot < last_slot {
|
||||
warn!(self.log, "BlocksByRange response returned out of range blocks";
|
||||
"response_initial_slot" => batch.downloaded_blocks[0].slot,
|
||||
if batch.start_slot > batch.downloaded_blocks[0].slot() || batch.end_slot < last_slot {
|
||||
warn!(self.log, "BlocksByRange response returned out of range blocks";
|
||||
"response_initial_slot" => batch.downloaded_blocks[0].slot(),
|
||||
"requested_initial_slot" => batch.start_slot);
|
||||
network.downvote_peer(batch.current_peer);
|
||||
self.to_be_processed_id = batch.id; // reset the id back to here, when incrementing, it will check against completed batches
|
||||
@@ -274,7 +274,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
// double check batches are processed in order TODO: Remove for prod
|
||||
if batch.id != self.to_be_processed_id {
|
||||
crit!(self.log, "Batch processed out of order";
|
||||
"processed_batch_id" => *batch.id,
|
||||
"processed_batch_id" => *batch.id,
|
||||
"expected_id" => *self.to_be_processed_id);
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
let processed_batch = self.processed_batches.remove(0);
|
||||
if *processed_batch.id >= *batch.id {
|
||||
crit!(self.log, "A processed batch had a greater id than the current process id";
|
||||
"processed_id" => *processed_batch.id,
|
||||
"processed_id" => *processed_batch.id,
|
||||
"current_id" => *batch.id);
|
||||
}
|
||||
|
||||
@@ -313,8 +313,8 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
// If the same peer corrected it's mistake, we allow it.... for
|
||||
// now.
|
||||
debug!(self.log, "Re-processed batch validated. Downvoting original peer";
|
||||
"batch_id" => *processed_batch.id,
|
||||
"original_peer" => format!("{}",processed_batch.original_peer),
|
||||
"batch_id" => *processed_batch.id,
|
||||
"original_peer" => format!("{}",processed_batch.original_peer),
|
||||
"new_peer" => format!("{}", processed_batch.current_peer));
|
||||
network.downvote_peer(processed_batch.original_peer);
|
||||
}
|
||||
@@ -325,7 +325,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
|
||||
// Add the current batch to processed batches to be verified in the future. We are
|
||||
// only uncertain about this batch, if it has not returned all blocks.
|
||||
if batch.downloaded_blocks.last().map(|block| block.slot)
|
||||
if batch.downloaded_blocks.last().map(|block| block.slot())
|
||||
!= Some(batch.end_slot.saturating_sub(1u64))
|
||||
{
|
||||
self.processed_batches.push(batch);
|
||||
@@ -436,12 +436,12 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
|
||||
batch.current_peer = new_peer.clone();
|
||||
|
||||
debug!(self.log, "Re-requesting batch";
|
||||
"start_slot" => batch.start_slot,
|
||||
debug!(self.log, "Re-requesting batch";
|
||||
"start_slot" => batch.start_slot,
|
||||
"end_slot" => batch.end_slot,
|
||||
"id" => *batch.id,
|
||||
"peer" => format!("{}", batch.current_peer),
|
||||
"head_root"=> format!("{}", batch.head_root),
|
||||
"head_root"=> format!("{}", batch.head_root),
|
||||
"retries" => batch.retries,
|
||||
"re-processes" => batch.reprocess_retries);
|
||||
self.send_batch(network, batch);
|
||||
@@ -522,9 +522,9 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
request_id: RequestId,
|
||||
) -> Option<ProcessingResult> {
|
||||
if let Some(batch) = self.pending_batches.remove(request_id) {
|
||||
warn!(self.log, "Batch failed. RPC Error";
|
||||
"id" => *batch.id,
|
||||
"retries" => batch.retries,
|
||||
warn!(self.log, "Batch failed. RPC Error";
|
||||
"id" => *batch.id,
|
||||
"retries" => batch.retries,
|
||||
"peer" => format!("{:?}", peer_id));
|
||||
|
||||
Some(self.failed_batch(network, batch))
|
||||
@@ -564,7 +564,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
batch.current_peer = new_peer.clone();
|
||||
debug!(self.log, "Re-Requesting batch";
|
||||
"start_slot" => batch.start_slot,
|
||||
"end_slot" => batch.end_slot,
|
||||
"end_slot" => batch.end_slot,
|
||||
"id" => *batch.id,
|
||||
"peer" => format!("{:?}", batch.current_peer),
|
||||
"head_root"=> format!("{}", batch.head_root));
|
||||
@@ -587,11 +587,11 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
// find the next pending batch and request it from the peer
|
||||
if let Some(peer_id) = self.get_next_peer() {
|
||||
if let Some(batch) = self.get_next_batch(peer_id) {
|
||||
debug!(self.log, "Requesting batch";
|
||||
"start_slot" => batch.start_slot,
|
||||
debug!(self.log, "Requesting batch";
|
||||
"start_slot" => batch.start_slot,
|
||||
"end_slot" => batch.end_slot,
|
||||
"id" => *batch.id,
|
||||
"peer" => format!("{}", batch.current_peer),
|
||||
"peer" => format!("{}", batch.current_peer),
|
||||
"head_root"=> format!("{}", batch.head_root));
|
||||
// send the batch
|
||||
self.send_batch(network, batch);
|
||||
|
||||
@@ -52,7 +52,7 @@ use slog::{debug, error, trace, warn};
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Weak;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{BeaconBlock, EthSpec};
|
||||
use types::{EthSpec, SignedBeaconBlock};
|
||||
|
||||
/// The primary object dealing with long range/batch syncing. This contains all the active and
|
||||
/// non-active chains that need to be processed before the syncing is considered complete. This
|
||||
@@ -231,7 +231,7 @@ impl<T: BeaconChainTypes> RangeSync<T> {
|
||||
network: &mut SyncNetworkContext,
|
||||
peer_id: PeerId,
|
||||
request_id: RequestId,
|
||||
beacon_block: Option<BeaconBlock<T::EthSpec>>,
|
||||
beacon_block: Option<SignedBeaconBlock<T::EthSpec>>,
|
||||
) {
|
||||
// Find the request. Most likely the first finalized chain (the syncing chain). If there
|
||||
// are no finalized chains, then it will be a head chain. At most, there should only be
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@ use ssz_derive::{Decode, Encode};
|
||||
use std::sync::Arc;
|
||||
use store::Store;
|
||||
use types::{
|
||||
BeaconBlock, BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch,
|
||||
Slot, Validator,
|
||||
BeaconState, CommitteeIndex, EthSpec, Hash256, PublicKeyBytes, RelativeEpoch,
|
||||
SignedBeaconBlock, Slot, Validator,
|
||||
};
|
||||
|
||||
/// Information about the block and state that are at head of the beacon chain.
|
||||
@@ -70,7 +70,7 @@ pub struct HeadBeaconBlock {
|
||||
pub beacon_block_slot: Slot,
|
||||
}
|
||||
|
||||
/// HTTP handler to return a list of head BeaconBlocks.
|
||||
/// HTTP handler to return a list of head block roots.
|
||||
pub fn get_heads<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
@@ -91,10 +91,10 @@ pub fn get_heads<T: BeaconChainTypes>(
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct BlockResponse<T: EthSpec> {
|
||||
pub root: Hash256,
|
||||
pub beacon_block: BeaconBlock<T>,
|
||||
pub beacon_block: SignedBeaconBlock<T>,
|
||||
}
|
||||
|
||||
/// HTTP handler to return a `BeaconBlock` at a given `root` or `slot`.
|
||||
/// HTTP handler to return a `SignedBeaconBlock` at a given `root` or `slot`.
|
||||
pub fn get_block<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
@@ -107,22 +107,22 @@ pub fn get_block<T: BeaconChainTypes>(
|
||||
let target = parse_slot(&value)?;
|
||||
|
||||
block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| {
|
||||
ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target))
|
||||
ApiError::NotFound(format!(
|
||||
"Unable to find SignedBeaconBlock for slot {:?}",
|
||||
target
|
||||
))
|
||||
})?
|
||||
}
|
||||
("root", value) => parse_root(&value)?,
|
||||
_ => return Err(ApiError::ServerError("Unexpected query parameter".into())),
|
||||
};
|
||||
|
||||
let block = beacon_chain
|
||||
.store
|
||||
.get::<BeaconBlock<T::EthSpec>>(&block_root)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::NotFound(format!(
|
||||
"Unable to find BeaconBlock for root {:?}",
|
||||
block_root
|
||||
))
|
||||
})?;
|
||||
let block = beacon_chain.store.get_block(&block_root)?.ok_or_else(|| {
|
||||
ApiError::NotFound(format!(
|
||||
"Unable to find SignedBeaconBlock for root {:?}",
|
||||
block_root
|
||||
))
|
||||
})?;
|
||||
|
||||
let response = BlockResponse {
|
||||
root: block_root,
|
||||
@@ -132,7 +132,7 @@ pub fn get_block<T: BeaconChainTypes>(
|
||||
ResponseBuilder::new(&req)?.body(&response)
|
||||
}
|
||||
|
||||
/// HTTP handler to return a `BeaconBlock` root at a given `slot`.
|
||||
/// HTTP handler to return a `SignedBeaconBlock` root at a given `slot`.
|
||||
pub fn get_block_root<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
@@ -141,7 +141,10 @@ pub fn get_block_root<T: BeaconChainTypes>(
|
||||
let target = parse_slot(&slot_string)?;
|
||||
|
||||
let root = block_root_at_slot(&beacon_chain, target)?.ok_or_else(|| {
|
||||
ApiError::NotFound(format!("Unable to find BeaconBlock for slot {:?}", target))
|
||||
ApiError::NotFound(format!(
|
||||
"Unable to find SignedBeaconBlock for slot {:?}",
|
||||
target
|
||||
))
|
||||
})?;
|
||||
|
||||
ResponseBuilder::new(&req)?.body(&root)
|
||||
|
||||
@@ -13,8 +13,8 @@ use std::sync::Arc;
|
||||
use store::{iter::AncestorIter, Store};
|
||||
use tokio::sync::mpsc;
|
||||
use types::{
|
||||
Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch,
|
||||
Signature, Slot,
|
||||
Attestation, BeaconState, CommitteeIndex, Epoch, EthSpec, Hash256, RelativeEpoch, Signature,
|
||||
SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
/// Parse a slot.
|
||||
@@ -113,7 +113,7 @@ pub fn parse_pubkey_bytes(string: &str) -> Result<PublicKeyBytes, ApiError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the root of the `BeaconBlock` in the canonical chain of `beacon_chain` at the given
|
||||
/// Returns the root of the `SignedBeaconBlock` in the canonical chain of `beacon_chain` at the given
|
||||
/// `slot`, if possible.
|
||||
///
|
||||
/// May return a root for a previous slot, in the case of skip slots.
|
||||
@@ -227,7 +227,7 @@ pub fn implementation_pending_response(_req: Request<Body>) -> ApiResult {
|
||||
|
||||
pub fn publish_beacon_block_to_network<T: BeaconChainTypes + 'static>(
|
||||
chan: Arc<RwLock<mpsc::UnboundedSender<NetworkMessage>>>,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
block: SignedBeaconBlock<T::EthSpec>,
|
||||
) -> Result<(), ApiError> {
|
||||
// create the network topic to send on
|
||||
let topic = GossipTopic::BeaconBlock;
|
||||
|
||||
@@ -14,7 +14,9 @@ use slog::{error, info, warn, Logger};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::sync::Arc;
|
||||
use types::beacon_state::EthSpec;
|
||||
use types::{Attestation, BeaconBlock, BeaconState, CommitteeIndex, Epoch, RelativeEpoch, Slot};
|
||||
use types::{
|
||||
Attestation, BeaconState, CommitteeIndex, Epoch, RelativeEpoch, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ValidatorDuty {
|
||||
@@ -256,7 +258,7 @@ pub fn get_new_beacon_block<T: BeaconChainTypes>(
|
||||
ResponseBuilder::new(&req)?.body(&new_block)
|
||||
}
|
||||
|
||||
/// HTTP Handler to publish a BeaconBlock, which has been signed by a validator.
|
||||
/// HTTP Handler to publish a SignedBeaconBlock, which has been signed by a validator.
|
||||
pub fn publish_beacon_block<T: BeaconChainTypes>(
|
||||
req: Request<Body>,
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
@@ -272,11 +274,11 @@ pub fn publish_beacon_block<T: BeaconChainTypes>(
|
||||
.map_err(|e| ApiError::ServerError(format!("Unable to get request body: {:?}", e)))
|
||||
.and_then(|chunks| {
|
||||
serde_json::from_slice(&chunks).map_err(|e| {
|
||||
ApiError::BadRequest(format!("Unable to parse JSON into BeaconBlock: {:?}", e))
|
||||
ApiError::BadRequest(format!("Unable to parse JSON into SignedBeaconBlock: {:?}", e))
|
||||
})
|
||||
})
|
||||
.and_then(move |block: BeaconBlock<T::EthSpec>| {
|
||||
let slot = block.slot;
|
||||
.and_then(move |block: SignedBeaconBlock<T::EthSpec>| {
|
||||
let slot = block.slot();
|
||||
match beacon_chain.process_block(block.clone()) {
|
||||
Ok(BlockProcessingOutcome::Processed { block_root }) => {
|
||||
// Block was processed, publish via gossipsub
|
||||
@@ -329,7 +331,7 @@ pub fn publish_beacon_block<T: BeaconChainTypes>(
|
||||
);
|
||||
|
||||
Err(ApiError::ProcessingError(format!(
|
||||
"The BeaconBlock could not be processed and has not been published: {:?}",
|
||||
"The SignedBeaconBlock could not be processed and has not been published: {:?}",
|
||||
outcome
|
||||
)))
|
||||
}
|
||||
@@ -386,7 +388,7 @@ pub fn publish_attestation<T: BeaconChainTypes>(
|
||||
.and_then(|chunks| {
|
||||
serde_json::from_slice(&chunks.as_slice()).map_err(|e| {
|
||||
ApiError::BadRequest(format!(
|
||||
"Unable to deserialize JSON into a BeaconBlock: {:?}",
|
||||
"Unable to deserialize JSON into a SignedBeaconBlock: {:?}",
|
||||
e
|
||||
))
|
||||
})
|
||||
|
||||
@@ -11,10 +11,10 @@ use remote_beacon_node::{
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypair, BeaconBlock, BeaconState, ChainSpec, Domain, Epoch,
|
||||
EthSpec, MinimalEthSpec, PublicKey, RelativeEpoch, Signature, Slot, Validator,
|
||||
EthSpec, MinimalEthSpec, PublicKey, RelativeEpoch, Signature, SignedBeaconBlock, SignedRoot,
|
||||
Slot, Validator,
|
||||
};
|
||||
use version;
|
||||
|
||||
@@ -54,17 +54,17 @@ fn get_randao_reveal<T: BeaconChainTypes>(
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let message = epoch.tree_hash_root();
|
||||
let domain = spec.get_domain(epoch, Domain::Randao, &fork);
|
||||
Signature::new(&message, domain, &keypair.sk)
|
||||
let message = epoch.signing_root(domain);
|
||||
Signature::new(message.as_bytes(), &keypair.sk)
|
||||
}
|
||||
|
||||
/// Signs the given block (assuming the given `beacon_chain` uses deterministic keypairs).
|
||||
fn sign_block<T: BeaconChainTypes>(
|
||||
beacon_chain: Arc<BeaconChain<T>>,
|
||||
block: &mut BeaconBlock<T::EthSpec>,
|
||||
block: BeaconBlock<T::EthSpec>,
|
||||
spec: &ChainSpec,
|
||||
) {
|
||||
) -> SignedBeaconBlock<T::EthSpec> {
|
||||
let fork = beacon_chain
|
||||
.head()
|
||||
.expect("should get head")
|
||||
@@ -74,7 +74,7 @@ fn sign_block<T: BeaconChainTypes>(
|
||||
.block_proposer(block.slot)
|
||||
.expect("should get proposer index");
|
||||
let keypair = generate_deterministic_keypair(proposer_index);
|
||||
block.sign(&keypair.sk, &fork, spec);
|
||||
block.sign(&keypair.sk, &fork, spec)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -335,7 +335,7 @@ fn validator_block_post() {
|
||||
let slot = Slot::new(1);
|
||||
let randao_reveal = get_randao_reveal(beacon_chain.clone(), slot, spec);
|
||||
|
||||
let mut block = env
|
||||
let block = env
|
||||
.runtime()
|
||||
.block_on(
|
||||
remote_node
|
||||
@@ -346,9 +346,13 @@ fn validator_block_post() {
|
||||
.expect("should fetch block from http api");
|
||||
|
||||
// Try publishing the block without a signature, ensure it is flagged as invalid.
|
||||
let empty_sig_block = SignedBeaconBlock {
|
||||
message: block.clone(),
|
||||
signature: Signature::empty_signature(),
|
||||
};
|
||||
let publish_status = env
|
||||
.runtime()
|
||||
.block_on(remote_node.http.validator().publish_block(block.clone()))
|
||||
.block_on(remote_node.http.validator().publish_block(empty_sig_block))
|
||||
.expect("should publish block");
|
||||
if cfg!(not(feature = "fake_crypto")) {
|
||||
assert!(
|
||||
@@ -357,12 +361,12 @@ fn validator_block_post() {
|
||||
);
|
||||
}
|
||||
|
||||
sign_block(beacon_chain, &mut block, spec);
|
||||
let block_root = block.canonical_root();
|
||||
let signed_block = sign_block(beacon_chain.clone(), block, spec);
|
||||
let block_root = signed_block.canonical_root();
|
||||
|
||||
let publish_status = env
|
||||
.runtime()
|
||||
.block_on(remote_node.http.validator().publish_block(block))
|
||||
.block_on(remote_node.http.validator().publish_block(signed_block))
|
||||
.expect("should publish block");
|
||||
|
||||
if cfg!(not(feature = "fake_crypto")) {
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::fs;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::net::{TcpListener, UdpSocket};
|
||||
use std::path::PathBuf;
|
||||
use types::{Epoch, EthSpec, Fork};
|
||||
use types::EthSpec;
|
||||
|
||||
pub const CLIENT_CONFIG_FILENAME: &str = "beacon-node.toml";
|
||||
pub const ETH2_CONFIG_FILENAME: &str = "eth2-spec.toml";
|
||||
@@ -377,11 +377,7 @@ fn init_new_client<E: EthSpec>(
|
||||
let spec = &mut eth2_config.spec;
|
||||
|
||||
// For now, assume that all networks will use the lighthouse genesis fork.
|
||||
spec.genesis_fork = Fork {
|
||||
previous_version: [0, 0, 0, 0],
|
||||
current_version: [1, 3, 3, 7],
|
||||
epoch: Epoch::new(0),
|
||||
};
|
||||
spec.genesis_fork_version = [1, 3, 3, 7];
|
||||
|
||||
client_config.eth1.deposit_contract_address =
|
||||
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
|
||||
@@ -559,11 +555,7 @@ fn process_testnet_subcommand(
|
||||
spec.ejection_balance = 1_600_000_000;
|
||||
spec.effective_balance_increment = 100_000_000;
|
||||
spec.min_genesis_time = 0;
|
||||
spec.genesis_fork = Fork {
|
||||
previous_version: [0; 4],
|
||||
current_version: [0, 0, 0, 2],
|
||||
epoch: Epoch::new(0),
|
||||
};
|
||||
spec.genesis_fork_version = [0, 0, 0, 2];
|
||||
|
||||
client_config.eth1.deposit_contract_address =
|
||||
"0x802dF6aAaCe28B2EEb1656bb18dF430dDC42cc2e".to_string();
|
||||
|
||||
@@ -1,194 +0,0 @@
|
||||
use super::*;
|
||||
use ssz::{Decode, DecodeError};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
fn get_block_bytes<T: Store<E>, 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> {
|
||||
let end = std::cmp::min(Slot::ssz_fixed_len(), bytes.len());
|
||||
|
||||
Slot::from_ssz_bytes(&bytes[0..end])
|
||||
}
|
||||
|
||||
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())
|
||||
.ok_or_else(|| DecodeError::BytesInvalid("Not enough bytes.".to_string()))?;
|
||||
|
||||
Hash256::from_ssz_bytes(slice)
|
||||
}
|
||||
|
||||
pub fn get_block_at_preceeding_slot<T: Store<E>, E: EthSpec>(
|
||||
store: &T,
|
||||
slot: Slot,
|
||||
start_root: Hash256,
|
||||
) -> 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_preceeding_slot<T: Store<E>, E: EthSpec>(
|
||||
store: &T,
|
||||
slot: Slot,
|
||||
mut root: Hash256,
|
||||
) -> Result<Option<(Hash256, Vec<u8>)>, Error> {
|
||||
loop {
|
||||
if let Some(bytes) = get_block_bytes::<_, E>(store, root)? {
|
||||
let this_slot = read_slot_from_block_bytes(&bytes)?;
|
||||
|
||||
match this_slot.cmp(&slot) {
|
||||
Ordering::Equal => break Ok(Some((root, bytes))),
|
||||
Ordering::Less => break Ok(None),
|
||||
Ordering::Greater => root = read_parent_root_from_block_bytes(&bytes)?,
|
||||
}
|
||||
} else {
|
||||
break Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ssz::Encode;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
type BeaconBlock = types::BeaconBlock<MinimalEthSpec>;
|
||||
|
||||
#[test]
|
||||
fn read_slot() {
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let test_slot = |slot: Slot| {
|
||||
let mut block = BeaconBlock::empty(&spec);
|
||||
block.slot = slot;
|
||||
let bytes = block.as_ssz_bytes();
|
||||
assert_eq!(read_slot_from_block_bytes(&bytes).unwrap(), slot);
|
||||
};
|
||||
|
||||
test_slot(Slot::new(0));
|
||||
test_slot(Slot::new(1));
|
||||
test_slot(Slot::new(42));
|
||||
test_slot(Slot::new(u64::max_value()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_slot() {
|
||||
for i in 0..8 {
|
||||
assert!(read_slot_from_block_bytes(&vec![0; i]).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_parent_root() {
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let test_root = |root: Hash256| {
|
||||
let mut block = BeaconBlock::empty(&spec);
|
||||
block.parent_root = root;
|
||||
let bytes = block.as_ssz_bytes();
|
||||
assert_eq!(read_parent_root_from_block_bytes(&bytes).unwrap(), root);
|
||||
};
|
||||
|
||||
test_root(Hash256::random());
|
||||
test_root(Hash256::random());
|
||||
test_root(Hash256::random());
|
||||
}
|
||||
|
||||
fn build_chain(
|
||||
store: &impl Store<MinimalEthSpec>,
|
||||
slots: &[usize],
|
||||
spec: &ChainSpec,
|
||||
) -> Vec<(Hash256, BeaconBlock)> {
|
||||
let mut blocks_and_roots: Vec<(Hash256, BeaconBlock)> = vec![];
|
||||
|
||||
for (i, slot) in slots.iter().enumerate() {
|
||||
let mut block = BeaconBlock::empty(spec);
|
||||
block.slot = Slot::from(*slot);
|
||||
|
||||
if i > 0 {
|
||||
block.parent_root = blocks_and_roots[i - 1].0;
|
||||
}
|
||||
|
||||
let root = Hash256::from_slice(&block.tree_hash_root());
|
||||
|
||||
store.put(&root, &block).unwrap();
|
||||
blocks_and_roots.push((root, block));
|
||||
}
|
||||
|
||||
blocks_and_roots
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_without_skips() {
|
||||
let n: usize = 10;
|
||||
let store = MemoryStore::open();
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let slots: Vec<usize> = (0..n).collect();
|
||||
let blocks_and_roots = build_chain(&store, &slots, &spec);
|
||||
|
||||
for source in 1..n {
|
||||
for target in 0..=source {
|
||||
let (source_root, _source_block) = &blocks_and_roots[source];
|
||||
let (target_root, target_block) = &blocks_and_roots[target];
|
||||
|
||||
let (found_root, found_block) = store
|
||||
.get_block_at_preceeding_slot(*source_root, target_block.slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(found_root, *target_root);
|
||||
assert_eq!(found_block, *target_block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chain_with_skips() {
|
||||
let store = MemoryStore::<MinimalEthSpec>::open();
|
||||
let spec = MinimalEthSpec::default_spec();
|
||||
|
||||
let slots = vec![0, 1, 2, 5];
|
||||
|
||||
let blocks_and_roots = build_chain(&store, &slots, &spec);
|
||||
|
||||
// Valid slots
|
||||
for target in 0..3 {
|
||||
let (source_root, _source_block) = &blocks_and_roots[3];
|
||||
let (target_root, target_block) = &blocks_and_roots[target];
|
||||
|
||||
let (found_root, found_block) = store
|
||||
.get_block_at_preceeding_slot(*source_root, target_block.slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(found_root, *target_root);
|
||||
assert_eq!(found_block, *target_block);
|
||||
}
|
||||
|
||||
// Slot that doesn't exist
|
||||
let (source_root, _source_block) = &blocks_and_roots[3];
|
||||
assert!(store
|
||||
.get_block_at_preceeding_slot(*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_preceeding_slot(*source_root, Slot::new(3))
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ pub struct HotColdDB<E: EthSpec> {
|
||||
/// The hot database also contains all blocks.
|
||||
pub(crate) hot_db: LevelDB<E>,
|
||||
/// LRU cache of deserialized blocks. Updated whenever a block is loaded.
|
||||
block_cache: Mutex<LruCache<Hash256, BeaconBlock<E>>>,
|
||||
block_cache: Mutex<LruCache<Hash256, SignedBeaconBlock<E>>>,
|
||||
/// LRU cache of deserialized states. Updated whenever a state is loaded.
|
||||
state_cache: Mutex<LruCache<Hash256, BeaconState<E>>>,
|
||||
/// Chain spec.
|
||||
@@ -107,7 +107,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
}
|
||||
|
||||
/// Store a block and update the LRU cache.
|
||||
fn put_block(&self, block_root: &Hash256, block: BeaconBlock<E>) -> Result<(), Error> {
|
||||
fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock<E>) -> Result<(), Error> {
|
||||
// Store on disk.
|
||||
self.put(block_root, &block)?;
|
||||
|
||||
@@ -118,7 +118,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
}
|
||||
|
||||
/// Fetch a block from the store.
|
||||
fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock<E>>, Error> {
|
||||
fn get_block(&self, block_root: &Hash256) -> Result<Option<SignedBeaconBlock<E>>, Error> {
|
||||
metrics::inc_counter(&metrics::BEACON_BLOCK_GET_COUNT);
|
||||
|
||||
// Check the cache.
|
||||
@@ -128,7 +128,7 @@ impl<E: EthSpec> Store<E> for HotColdDB<E> {
|
||||
}
|
||||
|
||||
// Fetch from database.
|
||||
match self.get::<BeaconBlock<E>>(block_root)? {
|
||||
match self.get::<SignedBeaconBlock<E>>(block_root)? {
|
||||
Some(block) => {
|
||||
// Add to cache.
|
||||
self.block_cache.lock().put(*block_root, block.clone());
|
||||
@@ -569,15 +569,15 @@ impl<E: EthSpec> HotColdDB<E> {
|
||||
start_slot: Slot,
|
||||
end_slot: Slot,
|
||||
end_block_hash: Hash256,
|
||||
) -> Result<Vec<BeaconBlock<E>>, Error> {
|
||||
) -> Result<Vec<SignedBeaconBlock<E>>, Error> {
|
||||
let mut blocks = ParentRootBlockIterator::new(self, end_block_hash)
|
||||
.map(|(_, block)| block)
|
||||
// Include the block at the end slot (if any), it needs to be
|
||||
// replayed in order to construct the canonical state at `end_slot`.
|
||||
.filter(|block| block.slot <= end_slot)
|
||||
.filter(|block| block.message.slot <= end_slot)
|
||||
// Exclude the block at the start slot (if any), because it has already
|
||||
// been applied to the starting state.
|
||||
.take_while(|block| block.slot > start_slot)
|
||||
.take_while(|block| block.message.slot > start_slot)
|
||||
.collect::<Vec<_>>();
|
||||
blocks.reverse();
|
||||
Ok(blocks)
|
||||
@@ -590,12 +590,12 @@ impl<E: EthSpec> HotColdDB<E> {
|
||||
fn replay_blocks(
|
||||
&self,
|
||||
mut state: BeaconState<E>,
|
||||
blocks: Vec<BeaconBlock<E>>,
|
||||
blocks: Vec<SignedBeaconBlock<E>>,
|
||||
target_slot: Slot,
|
||||
) -> Result<BeaconState<E>, Error> {
|
||||
let state_root_from_prev_block = |i: usize, state: &BeaconState<E>| {
|
||||
if i > 0 {
|
||||
let prev_block = &blocks[i - 1];
|
||||
let prev_block = &blocks[i - 1].message;
|
||||
if prev_block.slot == state.slot {
|
||||
Some(prev_block.state_root)
|
||||
} else {
|
||||
@@ -607,7 +607,7 @@ impl<E: EthSpec> HotColdDB<E> {
|
||||
};
|
||||
|
||||
for (i, block) in blocks.iter().enumerate() {
|
||||
while state.slot < block.slot {
|
||||
while state.slot < block.message.slot {
|
||||
let state_root = state_root_from_prev_block(i, &state);
|
||||
per_slot_processing(&mut state, state_root, &self.spec)
|
||||
.map_err(HotColdDBError::BlockReplaySlotError)?;
|
||||
|
||||
@@ -4,7 +4,7 @@ use ssz::{Decode, Encode};
|
||||
pub mod beacon_state;
|
||||
pub mod partial_beacon_state;
|
||||
|
||||
impl<T: EthSpec> SimpleStoreItem for BeaconBlock<T> {
|
||||
impl<T: EthSpec> SimpleStoreItem for SignedBeaconBlock<T> {
|
||||
fn db_column() -> DBColumn {
|
||||
DBColumn::BeaconBlock
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
typenum::Unsigned, BeaconBlock, BeaconState, BeaconStateError, EthSpec, Hash256, Slot,
|
||||
typenum::Unsigned, BeaconState, BeaconStateError, EthSpec, Hash256, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
/// Implemented for types that have ancestors (e.g., blocks, states) that may be iterated over.
|
||||
@@ -18,12 +18,14 @@ pub trait AncestorIter<U: Store<E>, E: EthSpec, I: Iterator> {
|
||||
}
|
||||
|
||||
impl<'a, U: Store<E>, E: EthSpec> AncestorIter<U, E, BlockRootsIterator<'a, E, U>>
|
||||
for BeaconBlock<E>
|
||||
for SignedBeaconBlock<E>
|
||||
{
|
||||
/// Iterates across all available prior block roots of `self`, starting at the most recent and ending
|
||||
/// at genesis.
|
||||
fn try_iter_ancestor_roots(&self, store: Arc<U>) -> Option<BlockRootsIterator<'a, E, U>> {
|
||||
let state = store.get_state(&self.state_root, Some(self.slot)).ok()??;
|
||||
let state = store
|
||||
.get_state(&self.message.state_root, Some(self.message.slot))
|
||||
.ok()??;
|
||||
|
||||
Some(BlockRootsIterator::owned(store, state))
|
||||
}
|
||||
@@ -120,7 +122,7 @@ impl<'a, E: EthSpec, S: Store<E>> ParentRootBlockIterator<'a, E, S> {
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec, S: Store<E>> Iterator for ParentRootBlockIterator<'a, E, S> {
|
||||
type Item = (Hash256, BeaconBlock<E>);
|
||||
type Item = (Hash256, SignedBeaconBlock<E>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Stop once we reach the zero parent, otherwise we'll keep returning the genesis
|
||||
@@ -129,15 +131,15 @@ impl<'a, E: EthSpec, S: Store<E>> Iterator for ParentRootBlockIterator<'a, E, S>
|
||||
None
|
||||
} else {
|
||||
let block_root = self.next_block_root;
|
||||
let block: BeaconBlock<E> = self.store.get(&block_root).ok()??;
|
||||
self.next_block_root = block.parent_root;
|
||||
let block = self.store.get_block(&block_root).ok()??;
|
||||
self.next_block_root = block.message.parent_root;
|
||||
Some((block_root, block))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Extends `BlockRootsIterator`, returning `BeaconBlock` instances, instead of their roots.
|
||||
/// Extends `BlockRootsIterator`, returning `SignedBeaconBlock` instances, instead of their roots.
|
||||
pub struct BlockIterator<'a, T: EthSpec, U> {
|
||||
roots: BlockRootsIterator<'a, T, U>,
|
||||
}
|
||||
@@ -159,11 +161,11 @@ impl<'a, T: EthSpec, U: Store<T>> BlockIterator<'a, T, U> {
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec, U: Store<T>> Iterator for BlockIterator<'a, T, U> {
|
||||
type Item = BeaconBlock<T>;
|
||||
type Item = SignedBeaconBlock<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (root, _slot) = self.roots.next()?;
|
||||
self.roots.store.get(&root).ok()?
|
||||
self.roots.store.get_block(&root).ok()?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
mod block_at_slot;
|
||||
pub mod chunked_iter;
|
||||
pub mod chunked_vector;
|
||||
pub mod config;
|
||||
@@ -83,12 +82,12 @@ pub trait Store<E: EthSpec>: Sync + Send + Sized + 'static {
|
||||
}
|
||||
|
||||
/// Store a block in the store.
|
||||
fn put_block(&self, block_root: &Hash256, block: BeaconBlock<E>) -> Result<(), Error> {
|
||||
fn put_block(&self, block_root: &Hash256, block: SignedBeaconBlock<E>) -> Result<(), Error> {
|
||||
self.put(block_root, &block)
|
||||
}
|
||||
|
||||
/// Fetch a block from the store.
|
||||
fn get_block(&self, block_root: &Hash256) -> Result<Option<BeaconBlock<E>>, Error> {
|
||||
fn get_block(&self, block_root: &Hash256) -> Result<Option<SignedBeaconBlock<E>>, Error> {
|
||||
self.get(block_root)
|
||||
}
|
||||
|
||||
@@ -124,19 +123,6 @@ pub trait Store<E: EthSpec>: Sync + Send + Sized + 'static {
|
||||
self.get_state(state_root, slot)
|
||||
}
|
||||
|
||||
/// Given the root of an existing block in the store (`start_block_root`), return a parent
|
||||
/// block with the specified `slot`.
|
||||
///
|
||||
/// 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_preceeding_slot(
|
||||
&self,
|
||||
start_block_root: Hash256,
|
||||
slot: Slot,
|
||||
) -> Result<Option<(Hash256, BeaconBlock<E>)>, Error> {
|
||||
block_at_slot::get_block_at_preceeding_slot::<_, E>(self, slot, start_block_root)
|
||||
}
|
||||
|
||||
/// (Optionally) Move all data before the frozen slot to the freezer database.
|
||||
fn freeze_to_state(
|
||||
_store: Arc<Self>,
|
||||
|
||||
@@ -11,7 +11,7 @@ use types::*;
|
||||
///
|
||||
/// Utilises lazy-loading from separate storage for its vector fields.
|
||||
///
|
||||
/// Spec v0.9.1
|
||||
/// Spec v0.10.1
|
||||
#[derive(Debug, PartialEq, Clone, Encode, Decode)]
|
||||
pub struct PartialBeaconState<T>
|
||||
where
|
||||
|
||||
Reference in New Issue
Block a user