diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 0b3fffda3..90b89c837 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -515,7 +515,9 @@ impl BeaconChain { }); } - match per_slot_processing(&mut state, &self.spec) { + // Note: supplying some `state_root` when it is known would be a cheap and easy + // optimization. + match per_slot_processing(&mut state, None, &self.spec) { Ok(()) => (), Err(e) => { warn!( @@ -887,7 +889,7 @@ impl BeaconChain { .start_slot(T::EthSpec::slots_per_epoch()) .as_u64() { - per_slot_processing(&mut state, &self.spec)?; + per_slot_processing(&mut state, None, &self.spec)?; } state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; @@ -1258,7 +1260,14 @@ impl BeaconChain { if i > 0 { intermediate_states.push(state.clone()); } - per_slot_processing(&mut state, &self.spec)?; + + let state_root = if i == 0 { + Some(parent_block.state_root) + } else { + None + }; + + per_slot_processing(&mut state, state_root, &self.spec)?; } metrics::stop_timer(catchup_timer); @@ -1426,8 +1435,11 @@ impl BeaconChain { .ok_or_else(|| BlockProductionError::NoEth1ChainConnection)?; // If required, transition the new state to the present slot. + // + // Note: supplying some `state_root` when it it is known would be a cheap and easy + // optimization. while state.slot < produce_at_slot { - per_slot_processing(&mut state, &self.spec)?; + per_slot_processing(&mut state, None, &self.spec)?; } state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; diff --git a/beacon_node/beacon_chain/src/fork_choice.rs b/beacon_node/beacon_chain/src/fork_choice.rs index beed9de03..d62f9f8fb 100644 --- a/beacon_node/beacon_chain/src/fork_choice.rs +++ b/beacon_node/beacon_chain/src/fork_choice.rs @@ -155,7 +155,7 @@ impl ForkChoice { // Fast-forward the state to the start slot of the epoch where it was justified. for _ in block.slot.as_u64()..block_justified_slot.as_u64() { - per_slot_processing(&mut state, &chain.spec) + per_slot_processing(&mut state, None, &chain.spec) .map_err(BeaconChainError::SlotProcessingError)? } diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 737d7f9c4..4c60fe777 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -287,7 +287,7 @@ where } while state.slot < slot { - per_slot_processing(&mut state, &self.spec) + per_slot_processing(&mut state, None, &self.spec) .expect("should be able to advance state to slot"); } diff --git a/beacon_node/beacon_chain/tests/tests.rs b/beacon_node/beacon_chain/tests/tests.rs index f1c7559c4..76756d528 100644 --- a/beacon_node/beacon_chain/tests/tests.rs +++ b/beacon_node/beacon_chain/tests/tests.rs @@ -41,7 +41,7 @@ fn massive_skips() { // Run per_slot_processing until it returns an error. let error = loop { - match per_slot_processing(&mut state, spec) { + match per_slot_processing(&mut state, None, spec) { Ok(_) => continue, Err(e) => break e, } diff --git a/beacon_node/rest_api/src/helpers.rs b/beacon_node/rest_api/src/helpers.rs index b6b897e42..b0c8723aa 100644 --- a/beacon_node/rest_api/src/helpers.rs +++ b/beacon_node/rest_api/src/helpers.rs @@ -216,7 +216,7 @@ pub fn state_root_at_slot( // Ensure the next epoch state caches are built in case of an epoch transition. state.build_committee_cache(RelativeEpoch::Next, spec)?; - state_processing::per_slot_processing(&mut state, spec)?; + state_processing::per_slot_processing(&mut state, None, spec)?; } // Note: this is an expensive operation. Once the tree hash cache is implement it may be diff --git a/beacon_node/store/src/hot_cold_store.rs b/beacon_node/store/src/hot_cold_store.rs index 06c268689..a07810bb0 100644 --- a/beacon_node/store/src/hot_cold_store.rs +++ b/beacon_node/store/src/hot_cold_store.rs @@ -424,7 +424,7 @@ impl HotColdDB { for block in blocks { while state.slot < block.slot { - per_slot_processing(&mut state, &self.spec) + per_slot_processing(&mut state, None, &self.spec) .map_err(HotColdDbError::BlockReplaySlotError)?; } per_block_processing( @@ -438,7 +438,7 @@ impl HotColdDB { } while state.slot < target_slot { - per_slot_processing(&mut state, &self.spec) + per_slot_processing(&mut state, None, &self.spec) .map_err(HotColdDbError::BlockReplaySlotError)?; } diff --git a/eth2/state_processing/src/per_slot_processing.rs b/eth2/state_processing/src/per_slot_processing.rs index 0b8a906c1..3748824f5 100644 --- a/eth2/state_processing/src/per_slot_processing.rs +++ b/eth2/state_processing/src/per_slot_processing.rs @@ -9,12 +9,17 @@ pub enum Error { /// Advances a state forward by one slot, performing per-epoch processing if required. /// +/// If the root of the supplied `state` is known, then it can be passed as `state_root`. If +/// `state_root` is `None`, the root of `state` will be computed using a cached tree hash. +/// Providing the `state_root` makes this function several orders of magniude faster. +/// /// Spec v0.9.1 pub fn per_slot_processing( state: &mut BeaconState, + state_root: Option, spec: &ChainSpec, ) -> Result<(), Error> { - cache_state(state)?; + cache_state(state, state_root)?; if state.slot > spec.genesis_slot && (state.slot + 1) % T::slots_per_epoch() == 0 { per_epoch_processing(state, spec)?; @@ -25,8 +30,15 @@ pub fn per_slot_processing( Ok(()) } -fn cache_state(state: &mut BeaconState) -> Result<(), Error> { - let previous_state_root = state.update_tree_hash_cache()?; +fn cache_state( + state: &mut BeaconState, + state_root: Option, +) -> Result<(), Error> { + let previous_state_root = if let Some(root) = state_root { + root + } else { + state.update_tree_hash_cache()? + }; // Note: increment the state slot here to allow use of our `state_root` and `block_root` // getter/setter functions. diff --git a/lcli/src/transition_blocks.rs b/lcli/src/transition_blocks.rs index 7428011c2..23e19acae 100644 --- a/lcli/src/transition_blocks.rs +++ b/lcli/src/transition_blocks.rs @@ -56,7 +56,7 @@ fn do_transition( // Transition the parent state to the block slot. for i in pre_state.slot.as_u64()..block.slot.as_u64() { - per_slot_processing(&mut pre_state, spec) + per_slot_processing(&mut pre_state, None, spec) .map_err(|e| format!("Failed to advance slot on iteration {}: {:?}", i, e))?; } diff --git a/lighthouse/Cargo.toml b/lighthouse/Cargo.toml index dd1ab7ce5..14a3acac4 100644 --- a/lighthouse/Cargo.toml +++ b/lighthouse/Cargo.toml @@ -19,3 +19,5 @@ environment = { path = "./environment" } futures = "0.1.25" validator_client = { "path" = "../validator_client" } account_manager = { "path" = "../account_manager" } +# TODO: remove this once @agemanning adds a permanent fix. +protobuf = "=2.8.1" diff --git a/tests/ef_tests/src/cases/sanity_blocks.rs b/tests/ef_tests/src/cases/sanity_blocks.rs index 45e018a29..6996b020f 100644 --- a/tests/ef_tests/src/cases/sanity_blocks.rs +++ b/tests/ef_tests/src/cases/sanity_blocks.rs @@ -73,7 +73,7 @@ impl Case for SanityBlocks { .iter() .try_for_each(|block| { while state.slot < block.slot { - per_slot_processing(&mut state, spec).unwrap(); + per_slot_processing(&mut state, None, spec).unwrap(); } state diff --git a/tests/ef_tests/src/cases/sanity_slots.rs b/tests/ef_tests/src/cases/sanity_slots.rs index e9b80a252..6baacb501 100644 --- a/tests/ef_tests/src/cases/sanity_slots.rs +++ b/tests/ef_tests/src/cases/sanity_slots.rs @@ -66,7 +66,7 @@ impl Case for SanitySlots { state.build_all_caches(spec).unwrap(); let mut result = (0..self.slots) - .try_for_each(|_| per_slot_processing(&mut state, spec)) + .try_for_each(|_| per_slot_processing(&mut state, None, spec)) .map(|_| state); compare_beacon_state_results_without_caches(&mut result, &mut expected)