diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 99dd9a642..97af43718 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -77,25 +77,31 @@ pub enum AttestationProcessingOutcome { Invalid(AttestationValidationError), } -pub enum StateCow<'a, T: EthSpec> { +/// Effectively a `Cow`, however when it is `Borrowed` it holds a `RwLockReadGuard` (a +/// read-lock on some read/write-locked state). +/// +/// Only has a small subset of the functionality of a `std::borrow::Cow`. +pub enum BeaconStateCow<'a, T: EthSpec> { Borrowed(RwLockReadGuard<'a, CheckPoint>), Owned(BeaconState), } -impl<'a, T: EthSpec> AsRef> for StateCow<'a, T> { - fn as_ref(&self) -> &BeaconState { +impl<'a, T: EthSpec> BeaconStateCow<'a, T> { + pub fn maybe_as_mut_ref(&mut self) -> Option<&mut BeaconState> { match self { - StateCow::Borrowed(checkpoint) => &checkpoint.beacon_state, - StateCow::Owned(state) => &state, + BeaconStateCow::Borrowed(_) => None, + BeaconStateCow::Owned(ref mut state) => Some(state), } } } -impl<'a, T: EthSpec> StateCow<'a, T> { - pub fn as_mut_ref(&mut self) -> Option<&mut BeaconState> { +impl<'a, T: EthSpec> std::ops::Deref for BeaconStateCow<'a, T> { + type Target = BeaconState; + + fn deref(&self) -> &BeaconState { match self { - StateCow::Borrowed(_) => None, - StateCow::Owned(ref mut state) => Some(state), + BeaconStateCow::Borrowed(checkpoint) => &checkpoint.beacon_state, + BeaconStateCow::Owned(state) => &state, } } } @@ -374,11 +380,11 @@ impl BeaconChain { /// /// Returns `None` when the state is not found in the database or there is an error skipping /// to a future state. - pub fn state_at_slot(&self, slot: Slot) -> Result, Error> { + pub fn state_at_slot(&self, slot: Slot) -> Result, Error> { let head_state = &self.head().beacon_state; if slot == head_state.slot { - Ok(StateCow::Borrowed(self.head())) + Ok(BeaconStateCow::Borrowed(self.head())) } else if slot > head_state.slot { let head_state_slot = head_state.slot; let mut state = head_state.clone(); @@ -398,7 +404,7 @@ impl BeaconChain { } }; } - Ok(StateCow::Owned(state)) + Ok(BeaconStateCow::Owned(state)) } else { let state_root = self .rev_iter_state_roots() @@ -406,7 +412,7 @@ impl BeaconChain { .map(|(root, _slot)| root) .ok_or_else(|| Error::NoStateForSlot(slot))?; - Ok(StateCow::Owned( + Ok(BeaconStateCow::Owned( self.store .get(&state_root)? .ok_or_else(|| Error::NoStateForSlot(slot))?, @@ -422,7 +428,7 @@ impl BeaconChain { /// /// Returns `None` when there is an error skipping to a future state or the slot clock cannot /// be read. - pub fn state_now(&self) -> Result, Error> { + pub fn state_now(&self) -> Result, Error> { self.state_at_slot(self.slot()?) } @@ -465,25 +471,24 @@ impl BeaconChain { let head_state = &self.head().beacon_state; let mut state = if epoch(slot) == epoch(head_state.slot) { - StateCow::Borrowed(self.head()) + BeaconStateCow::Borrowed(self.head()) } else { self.state_at_slot(slot)? }; - if let Some(state) = state.as_mut_ref() { + if let Some(state) = state.maybe_as_mut_ref() { state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; } - if epoch(state.as_ref().slot) != epoch(slot) { + if epoch(state.slot) != epoch(slot) { return Err(Error::InvariantViolated(format!( "Epochs in consistent in proposer lookup: state: {}, requested: {}", - epoch(state.as_ref().slot), + epoch(state.slot), epoch(slot) ))); } state - .as_ref() .get_beacon_proposer_index(slot, RelativeEpoch::Current, &self.spec) .map_err(Into::into) } @@ -501,26 +506,25 @@ impl BeaconChain { let head_state = &self.head().beacon_state; let mut state = if epoch == as_epoch(head_state.slot) { - StateCow::Borrowed(self.head()) + BeaconStateCow::Borrowed(self.head()) } else { self.state_at_slot(epoch.start_slot(T::EthSpec::slots_per_epoch()))? }; - if let Some(state) = state.as_mut_ref() { + if let Some(state) = state.maybe_as_mut_ref() { state.build_committee_cache(RelativeEpoch::Current, &self.spec)?; } - if as_epoch(state.as_ref().slot) != epoch { + if as_epoch(state.slot) != epoch { return Err(Error::InvariantViolated(format!( "Epochs in consistent in attestation duties lookup: state: {}, requested: {}", - as_epoch(state.as_ref().slot), + as_epoch(state.slot), epoch ))); } - if let Some(attestation_duty) = state - .as_ref() - .get_attestation_duties(validator_index, RelativeEpoch::Current)? + if let Some(attestation_duty) = + state.get_attestation_duties(validator_index, RelativeEpoch::Current)? { Ok(Some((attestation_duty.slot, attestation_duty.shard))) } else { @@ -541,12 +545,7 @@ impl BeaconChain { 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.as_ref(), - ) + self.produce_attestation_data_for_block(shard, head_block_root, head_block_slot, &*state) } /// Produce an `AttestationData` that attests to the chain denoted by `block_root` and `state`. @@ -868,7 +867,7 @@ impl BeaconChain { match self.state_now() { Ok(state) => self .op_pool - .insert_voluntary_exit(exit, state.as_ref(), &self.spec), + .insert_voluntary_exit(exit, &*state, &self.spec), Err(e) => { error!( &self.log, @@ -884,9 +883,7 @@ impl BeaconChain { /// Accept some transfer and queue it for inclusion in an appropriate block. pub fn process_transfer(&self, transfer: Transfer) -> Result<(), TransferValidationError> { match self.state_now() { - Ok(state) => self - .op_pool - .insert_transfer(transfer, state.as_ref(), &self.spec), + Ok(state) => self.op_pool.insert_transfer(transfer, &*state, &self.spec), Err(e) => { error!( &self.log, @@ -907,7 +904,7 @@ impl BeaconChain { match self.state_now() { Ok(state) => { self.op_pool - .insert_proposer_slashing(proposer_slashing, state.as_ref(), &self.spec) + .insert_proposer_slashing(proposer_slashing, &*state, &self.spec) } Err(e) => { error!( @@ -929,7 +926,7 @@ impl BeaconChain { match self.state_now() { Ok(state) => { self.op_pool - .insert_attester_slashing(attester_slashing, state.as_ref(), &self.spec) + .insert_attester_slashing(attester_slashing, &*state, &self.spec) } Err(e) => { error!( @@ -1150,7 +1147,7 @@ impl BeaconChain { .state_at_slot(slot - 1) .map_err(|_| BlockProductionError::UnableToProduceAtSlot(slot))?; - self.produce_block_on_state(state.as_ref().clone(), slot, randao_reveal) + self.produce_block_on_state(state.clone(), slot, randao_reveal) } /// Produce a block for some `slot` upon the given `state`. diff --git a/beacon_node/rpc/src/validator.rs b/beacon_node/rpc/src/validator.rs index 84995ca50..abc1cffc5 100644 --- a/beacon_node/rpc/src/validator.rs +++ b/beacon_node/rpc/src/validator.rs @@ -32,7 +32,7 @@ impl ValidatorService for ValidatorServiceInstance { let slot = epoch.start_slot(T::EthSpec::slots_per_epoch()); let mut state = if let Ok(state) = self.chain.state_at_slot(slot) { - state.as_ref().clone() + state.clone() } else { let log_clone = self.log.clone(); let f = sink