diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 86e0ded3ce..47cbde8ef8 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -32,7 +32,7 @@ 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. - Processed, + Processed { block_root: Hash256 }, /// The blocks parent_root is unknown. ParentUnknown { parent: Hash256 }, /// The block slot is greater than the present slot. @@ -426,6 +426,12 @@ impl BeaconChain { /// Produce an `AttestationData` that is valid for the present `slot` and given `shard`. pub fn produce_attestation_data(&self, shard: u64) -> Result { + let state = self.state.read(); + let head_block_root = self.head().beacon_block_root; + let head_block_slot = self.head().beacon_block.slot; + + self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, &*state) + /* let slots_per_epoch = T::EthSpec::slots_per_epoch(); self.metrics.attestation_production_requests.inc(); @@ -474,6 +480,65 @@ impl BeaconChain { previous_crosslink_root, crosslink_data_root: Hash256::zero(), }) + */ + } + + /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. + pub fn produce_attestation_data_for_block( + &self, + shard: u64, + head_block_root: Hash256, + head_block_slot: Slot, + state: &BeaconState, + ) -> Result { + // Collect some metrics. + self.metrics.attestation_production_requests.inc(); + let timer = self.metrics.attestation_production_times.start_timer(); + + let slots_per_epoch = T::EthSpec::slots_per_epoch(); + let current_epoch_start_slot = state.current_epoch().start_slot(slots_per_epoch); + + // The `target_root` is the root of the first block of the current epoch. + // + // The `state` does not know the root of the block for it's current slot (it only knows + // about blocks from prior slots). This creates an edge-case when the state is on the first + // slot of the epoch -- we're unable to obtain the `target_root` because it is not a prior + // root. + // + // This edge case is handled in two ways: + // + // - If the head block is on the same slot as the state, we use it's root. + // - Otherwise, assume the current slot has been skipped and use the block root from the + // prior slot. + // + // For all other cases, we simply read the `target_root` from `state.latest_block_roots`. + let target_root = if state.slot == current_epoch_start_slot { + if head_block_slot == current_epoch_start_slot { + head_block_root + } else { + *state.get_block_root(current_epoch_start_slot - 1)? + } + } else { + *state.get_block_root(current_epoch_start_slot)? + }; + + let previous_crosslink_root = + Hash256::from_slice(&state.get_current_crosslink(shard)?.tree_hash_root()); + + // Collect some metrics. + self.metrics.attestation_production_successes.inc(); + timer.observe_duration(); + + Ok(AttestationData { + beacon_block_root: head_block_root, + source_epoch: state.current_justified_epoch, + source_root: state.current_justified_root, + target_epoch: state.current_epoch(), + target_root, + shard, + previous_crosslink_root, + crosslink_data_root: Hash256::zero(), + }) } /// Accept a new attestation from the network. @@ -612,9 +677,6 @@ impl BeaconChain { .get(&parent_state_root)? .ok_or_else(|| Error::DBInconsistent(format!("Missing state {}", parent_state_root)))?; - // TODO: check the block proposer signature BEFORE doing a state transition. This will - // significantly lower exposure surface to DoS attacks. - // Transition the parent state to the block slot. let mut state: BeaconState = parent_state; for _ in state.slot.as_u64()..block.slot.as_u64() { @@ -658,7 +720,7 @@ impl BeaconChain { .observe(block.body.attestations.len() as f64); timer.observe_duration(); - Ok(BlockProcessingOutcome::Processed) + Ok(BlockProcessingOutcome::Processed { block_root }) } /// Produce a new block at the present slot. @@ -695,10 +757,7 @@ impl BeaconChain { let timer = self.metrics.block_production_times.start_timer(); // 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)?; - + while state.slot < produce_at_slot { per_slot_processing(&mut state, &self.spec)?; } @@ -711,6 +770,7 @@ impl BeaconChain { } else { state.latest_block_header.canonical_root() }; + dbg!(previous_block_root); let mut graffiti: [u8; 32] = [0; 32]; graffiti.copy_from_slice(GRAFFITI.as_bytes()); @@ -754,6 +814,8 @@ impl BeaconChain { self.metrics.block_production_successes.inc(); timer.observe_duration(); + dbg!(block.canonical_root()); + Ok((block, state)) } @@ -787,6 +849,9 @@ impl BeaconChain { // If we switched to a new chain (instead of building atop the present chain). if self.head().beacon_block_root != beacon_block.previous_block_root { + dbg!("switched head"); + dbg!(self.head().beacon_block.slot); + dbg!(beacon_block.slot); self.metrics.fork_choice_reorg_count.inc(); }; diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 64e482a508..bb308c4076 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -131,15 +131,19 @@ where let outcome = self .chain .process_block(block) - .expect("should process block"); + .expect("should not error during block processing"); - assert_eq!(outcome, BlockProcessingOutcome::Processed); + if let BlockProcessingOutcome::Processed { block_root } = outcome { + // + } else { + panic!("block should be successfully processed"); + } + + self.add_attestations_to_op_pool(); state = new_state; slot += 1; } - - self.add_attestations_to_op_pool(); } fn build_block( @@ -152,41 +156,13 @@ where panic!("produce slot cannot be prior to the state slot"); } - for _ in 0..slot.as_u64() - state.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) - .expect("should be able to build caches"); - + while state.slot < slot { per_slot_processing(&mut state, &self.spec) .expect("should be able to advance state to slot"); } - state.drop_all_caches(); state.build_all_caches(&self.spec).unwrap(); - // dbg!(slot); - // dbg!(state.generate_seed(state.current_epoch(), &self.spec)); - dbg!(state.generate_seed(state.next_epoch(), &self.spec)); - /* - dbg!(self - .chain - .current_state() - .generate_seed(state.current_epoch(), &self.spec)); - // dbg!(state.generate_seed(state.next_epoch(), &self.spec)); - dbg!(state.canonical_root()); - dbg!(&state.committee_caches[0]); - dbg!(self.chain.current_state().canonical_root()); - dbg!(&self.chain.current_state().committee_caches[0]); - - dbg!(state.get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec)); - dbg!(self.chain.current_state().get_beacon_proposer_index( - slot, - RelativeEpoch::Current, - &self.spec - )); - */ - let proposer_index = match build_strategy { BuildStrategy::OnCanonicalHead => self .chain @@ -308,7 +284,7 @@ mod test { #[test] fn can_finalize() { - let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 5; + let num_blocks_produced = MinimalEthSpec::slots_per_epoch() * 1 + 2; let harness = get_harness(VALIDATOR_COUNT); @@ -340,5 +316,7 @@ mod test { state.current_epoch() - 2, "the head should be finalized two behind the current epoch" ); + + panic!(); } } diff --git a/eth2/lmd_ghost/src/reduced_tree.rs b/eth2/lmd_ghost/src/reduced_tree.rs index f35f50e3ae..888b20bf27 100644 --- a/eth2/lmd_ghost/src/reduced_tree.rs +++ b/eth2/lmd_ghost/src/reduced_tree.rs @@ -210,6 +210,8 @@ where } fn find_head_from<'a>(&'a self, start_node: &'a Node) -> Result<&'a Node> { + dbg!(&self.nodes); + if start_node.does_not_have_children() { Ok(start_node) } else { @@ -585,7 +587,7 @@ pub struct Vote { /// /// E.g., a `get` or `insert` to an out-of-bounds element will cause the Vec to grow (using /// Default) to the smallest size required to fulfill the request. -#[derive(Default, Clone)] +#[derive(Default, Clone, Debug)] pub struct ElasticList(Vec); impl ElasticList diff --git a/eth2/types/src/beacon_block.rs b/eth2/types/src/beacon_block.rs index 709bf65e09..18e5a37ec5 100644 --- a/eth2/types/src/beacon_block.rs +++ b/eth2/types/src/beacon_block.rs @@ -5,7 +5,7 @@ use bls::Signature; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::TreeHash; +use tree_hash::{SignedRoot, TreeHash}; use tree_hash_derive::{CachedTreeHash, SignedRoot, TreeHash}; /// A block of the `BeaconChain`. diff --git a/eth2/types/src/beacon_state.rs b/eth2/types/src/beacon_state.rs index 4790d95e7f..f82340017f 100644 --- a/eth2/types/src/beacon_state.rs +++ b/eth2/types/src/beacon_state.rs @@ -703,9 +703,6 @@ impl BeaconState { let active_index_root = self.get_active_index_root(epoch, spec)?; let epoch_bytes = int_to_bytes32(epoch.as_u64()); - dbg!(randao); - dbg!(active_index_root); - let mut preimage = [0; 32 * 3]; preimage[0..32].copy_from_slice(&randao[..]); preimage[32..64].copy_from_slice(&active_index_root[..]); @@ -838,10 +835,12 @@ impl BeaconState { /// Note: whilst this function will preserve already-built caches, it will not build any. pub fn advance_caches(&mut self) { let next = Self::cache_index(RelativeEpoch::Previous); + let current = Self::cache_index(RelativeEpoch::Current); let caches = &mut self.committee_caches[..]; caches.rotate_left(1); caches[next] = CommitteeCache::default(); + caches[current] = CommitteeCache::default(); } fn cache_index(relative_epoch: RelativeEpoch) -> usize {