From 55818e285a96fc086ab162c57c01ec28d6798c26 Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Wed, 19 Jun 2019 03:01:58 +1000 Subject: [PATCH] Refactor block prod. to produce for forks --- beacon_node/beacon_chain/src/beacon_chain.rs | 47 +++++++++++-- beacon_node/beacon_chain/src/errors.rs | 3 + beacon_node/beacon_chain/src/test_utils.rs | 70 +++++++++++++++----- beacon_node/network/src/sync/simple_sync.rs | 16 ++--- 4 files changed, 100 insertions(+), 36 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 920d8c320..c680498dc 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -23,6 +23,12 @@ use store::{Error as DBError, Store}; use tree_hash::TreeHash; use types::*; +// Text included in blocks. +// Must be 32-bytes or panic. +// +// |-------must be this long------| +pub const GRAFFITI: &str = "sigp/lighthouse-0.0.0-prerelease"; + #[derive(Debug, PartialEq)] pub enum BlockProcessingOutcome { /// Block was valid and imported into the block graph. @@ -657,16 +663,41 @@ impl BeaconChain { &self, randao_reveal: Signature, ) -> Result<(BeaconBlock, BeaconState), BlockProductionError> { - debug!("Producing block at slot {}...", self.state.read().slot); + let state = self.state.read().clone(); + let slot = self + .read_slot_clock() + .ok_or_else(|| BlockProductionError::UnableToReadSlot)?; + + self.produce_block_on_state(state, slot, randao_reveal) + } + + /// Produce a block for some `slot` upon the given `state`. + /// + /// Typically the `self.produce_block()` function should be used, instead of calling this + /// function directly. This function is useful for purposefully creating forks or blocks at + /// non-current slots. + /// + /// The given state will be advanced to the given `produce_at_slot`, then a block will be + /// produced at that slot height. + pub fn produce_block_on_state( + &self, + mut state: BeaconState, + produce_at_slot: Slot, + randao_reveal: Signature, + ) -> Result<(BeaconBlock, BeaconState), BlockProductionError> { self.metrics.block_production_requests.inc(); let timer = self.metrics.block_production_times.start_timer(); - let mut state = self.state.read().clone(); + // If required, transition the new state to the present slot. + for _ in state.slot.as_u64()..produce_at_slot.as_u64() { + // Ensure the next epoch state caches are built in case of an epoch transition. + state.build_committee_cache(RelativeEpoch::Next, &self.spec)?; + + per_slot_processing(&mut state, &self.spec)?; + } state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; - trace!("Finding attestations for new block..."); - let previous_block_root = if state.slot > 0 { *state .get_block_root(state.slot - 1) @@ -675,8 +706,11 @@ impl BeaconChain { state.latest_block_header.canonical_root() }; + let mut graffiti: [u8; 32] = [0; 32]; + graffiti.copy_from_slice(GRAFFITI.as_bytes()); + let (proposer_slashings, attester_slashings) = - self.op_pool.get_slashings(&*self.state.read(), &self.spec); + self.op_pool.get_slashings(&state, &self.spec); let mut block = BeaconBlock { slot: state.slot, @@ -691,8 +725,7 @@ impl BeaconChain { deposit_root: Hash256::zero(), block_hash: Hash256::zero(), }, - // TODO: badass Lighthouse graffiti - graffiti: [0; 32], + graffiti, proposer_slashings, attester_slashings, attestations: self diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 8e04948df..157d774c6 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -40,9 +40,12 @@ impl From for BeaconChainError { #[derive(Debug, PartialEq)] pub enum BlockProductionError { UnableToGetBlockRootFromState, + UnableToReadSlot, + SlotProcessingError(SlotProcessingError), BlockProcessingError(BlockProcessingError), BeaconStateError(BeaconStateError), } easy_from_to!(BlockProcessingError, BlockProductionError); easy_from_to!(BeaconStateError, BlockProductionError); +easy_from_to!(SlotProcessingError, BlockProductionError); diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index e7a54580d..fe731ac3e 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -5,13 +5,20 @@ use slot_clock::TestingSlotClock; use std::marker::PhantomData; use std::sync::Arc; use store::MemoryStore; +use store::Store; use tree_hash::{SignedRoot, TreeHash}; use types::{ test_utils::TestingBeaconStateBuilder, AggregateSignature, Attestation, - AttestationDataAndCustodyBit, BeaconBlock, Bitfield, ChainSpec, Domain, EthSpec, Hash256, - Keypair, SecretKey, Signature, + AttestationDataAndCustodyBit, BeaconBlock, BeaconState, Bitfield, ChainSpec, Domain, EthSpec, + Hash256, Keypair, RelativeEpoch, SecretKey, Signature, Slot, }; +#[derive(Clone, Copy)] +pub enum BuildStrategy { + OnCanonicalHead, + ForkCanonicalChainAt(Slot), +} + pub struct CommonTypes where L: LmdGhost, @@ -82,11 +89,11 @@ where } } - pub fn extend_canonical_chain(&self) { + pub fn extend_chain(&self, build_strategy: BuildStrategy) { self.chain.slot_clock.advance_slot(); self.chain.catchup_state().expect("should catchup state"); - let block = self.build_block(); + let block = self.build_block(build_strategy); let outcome = self .chain .process_block(block) @@ -96,18 +103,47 @@ where self.add_attestations_to_op_pool(); } - fn build_block(&self) -> BeaconBlock { - let slot = self.chain.read_slot_clock().unwrap(); + fn get_state(&self, build_strategy: BuildStrategy) -> BeaconState { + match build_strategy { + BuildStrategy::OnCanonicalHead => self.chain.current_state().clone(), + BuildStrategy::ForkCanonicalChainAt(fork_slot) => { + let state_root = self + .chain + .rev_iter_state_roots(self.chain.head().beacon_state.slot - 1) + .find(|(_hash, slot)| *slot == fork_slot) + .map(|(hash, _slot)| hash) + .expect("could not find state root for fork"); - let sk = { - let proposer = self - .chain - .block_proposer(slot) - .expect("should get block propoer"); - &self.keypairs[proposer].sk + self.chain + .store + .get(&state_root) + .expect("should read db") + .expect("should find state root") + } + } + } + + fn build_block(&self, build_strategy: BuildStrategy) -> BeaconBlock { + let mut state = self.get_state(build_strategy); + state.build_all_caches(&self.spec).unwrap(); + + let slot = match build_strategy { + BuildStrategy::OnCanonicalHead => self.chain.read_slot_clock().unwrap(), + BuildStrategy::ForkCanonicalChainAt(slot) => slot, }; - let fork = &self.chain.head().beacon_state.fork; + let proposer_index = match build_strategy { + BuildStrategy::OnCanonicalHead => self + .chain + .block_proposer(slot) + .expect("should get block proposer from chain"), + _ => state + .get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec) + .expect("should get block proposer from state"), + }; + + let sk = &self.keypairs[proposer_index].sk; + let fork = &state.fork.clone(); let randao_reveal = { let epoch = slot.epoch(E::slots_per_epoch()); @@ -118,8 +154,8 @@ where let (mut block, _state) = self .chain - .produce_block(randao_reveal) - .expect("should producer block"); + .produce_block_on_state(state, slot, randao_reveal) + .expect("should produce block"); block.signature = { let message = block.signed_root(); @@ -195,7 +231,7 @@ where } #[cfg(test)] -#[cfg(not(debug_assertions))] +// #[cfg(not(debug_assertions))] mod test { use super::*; use lmd_ghost::ThreadSafeReducedTree; @@ -213,7 +249,7 @@ mod test { > = BeaconChainHarness::new(VALIDATOR_COUNT); for _ in 0..num_blocks_produced { - harness.extend_canonical_chain(); + harness.extend_chain(BuildStrategy::OnCanonicalHead); } let state = &harness.chain.head().beacon_state; diff --git a/beacon_node/network/src/sync/simple_sync.rs b/beacon_node/network/src/sync/simple_sync.rs index 403a8c54b..528edca98 100644 --- a/beacon_node/network/src/sync/simple_sync.rs +++ b/beacon_node/network/src/sync/simple_sync.rs @@ -182,7 +182,7 @@ impl SimpleSync { && (!self .chain .rev_iter_block_roots(local.best_slot) - .any(|root| root == remote.latest_finalized_root)) + .any(|(root, _slot)| root == remote.latest_finalized_root)) && (local.latest_finalized_root != spec.zero_hash) && (remote.latest_finalized_root != spec.zero_hash) { @@ -266,11 +266,12 @@ impl SimpleSync { "start_slot" => req.start_slot, ); - let mut roots: Vec = self + let mut roots: Vec = self .chain .rev_iter_block_roots(req.start_slot + req.count) .skip(1) .take(req.count as usize) + .map(|(block_root, slot)| BlockRootSlot { slot, block_root }) .collect(); if roots.len() as u64 != req.count { @@ -285,16 +286,6 @@ impl SimpleSync { } roots.reverse(); - - let mut roots: Vec = roots - .iter() - .enumerate() - .map(|(i, block_root)| BlockRootSlot { - slot: req.start_slot + Slot::from(i), - block_root: *block_root, - }) - .collect(); - roots.dedup_by_key(|brs| brs.block_root); network.send_rpc_response( @@ -392,6 +383,7 @@ impl SimpleSync { .chain .rev_iter_block_roots(req.start_slot + (count - 1)) .take(count as usize) + .map(|(root, _slot)| root) .collect(); roots.reverse();