171 lines
5.7 KiB
Rust
171 lines
5.7 KiB
Rust
use crate::common::get_indexed_attestation;
|
|
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
|
use std::collections::{hash_map::Entry, HashMap};
|
|
use tree_hash::TreeHash;
|
|
use types::{
|
|
AbstractExecPayload, Attestation, AttestationData, BeaconState, BeaconStateError, BitList,
|
|
ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, SignedBeaconBlock, Slot,
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ConsensusContext<T: EthSpec> {
|
|
/// Slot to act as an identifier/safeguard
|
|
slot: Slot,
|
|
/// Proposer index of the block at `slot`.
|
|
proposer_index: Option<u64>,
|
|
/// Block root of the block at `slot`.
|
|
current_block_root: Option<Hash256>,
|
|
/// Cache of indexed attestations constructed during block processing.
|
|
indexed_attestations:
|
|
HashMap<(AttestationData, BitList<T::MaxValidatorsPerCommittee>), IndexedAttestation<T>>,
|
|
/// Whether `verify_kzg_commitments_against_transactions` has successfully passed.
|
|
kzg_commitments_consistent: bool,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum ContextError {
|
|
BeaconState(BeaconStateError),
|
|
SlotMismatch { slot: Slot, expected: Slot },
|
|
EpochMismatch { epoch: Epoch, expected: Epoch },
|
|
}
|
|
|
|
impl From<BeaconStateError> for ContextError {
|
|
fn from(e: BeaconStateError) -> Self {
|
|
Self::BeaconState(e)
|
|
}
|
|
}
|
|
|
|
impl<T: EthSpec> ConsensusContext<T> {
|
|
pub fn new(slot: Slot) -> Self {
|
|
Self {
|
|
slot,
|
|
proposer_index: None,
|
|
current_block_root: None,
|
|
indexed_attestations: HashMap::new(),
|
|
kzg_commitments_consistent: false,
|
|
}
|
|
}
|
|
|
|
pub fn set_proposer_index(mut self, proposer_index: u64) -> Self {
|
|
self.proposer_index = Some(proposer_index);
|
|
self
|
|
}
|
|
|
|
/// Strict method for fetching the proposer index.
|
|
///
|
|
/// Gets the proposer index for `self.slot` while ensuring that it matches `state.slot()`. This
|
|
/// method should be used in block processing and almost everywhere the proposer index is
|
|
/// required. If the slot check is too restrictive, see `get_proposer_index_from_epoch_state`.
|
|
pub fn get_proposer_index(
|
|
&mut self,
|
|
state: &BeaconState<T>,
|
|
spec: &ChainSpec,
|
|
) -> Result<u64, ContextError> {
|
|
self.check_slot(state.slot())?;
|
|
self.get_proposer_index_no_checks(state, spec)
|
|
}
|
|
|
|
/// More liberal method for fetching the proposer index.
|
|
///
|
|
/// Fetches the proposer index for `self.slot` but does not require the state to be from an
|
|
/// exactly matching slot (merely a matching epoch). This is useful in batch verification where
|
|
/// we want to extract the proposer index from a single state for every slot in the epoch.
|
|
pub fn get_proposer_index_from_epoch_state(
|
|
&mut self,
|
|
state: &BeaconState<T>,
|
|
spec: &ChainSpec,
|
|
) -> Result<u64, ContextError> {
|
|
self.check_epoch(state.current_epoch())?;
|
|
self.get_proposer_index_no_checks(state, spec)
|
|
}
|
|
|
|
fn get_proposer_index_no_checks(
|
|
&mut self,
|
|
state: &BeaconState<T>,
|
|
spec: &ChainSpec,
|
|
) -> Result<u64, ContextError> {
|
|
if let Some(proposer_index) = self.proposer_index {
|
|
return Ok(proposer_index);
|
|
}
|
|
|
|
let proposer_index = state.get_beacon_proposer_index(self.slot, spec)? as u64;
|
|
self.proposer_index = Some(proposer_index);
|
|
Ok(proposer_index)
|
|
}
|
|
|
|
pub fn set_current_block_root(mut self, block_root: Hash256) -> Self {
|
|
self.current_block_root = Some(block_root);
|
|
self
|
|
}
|
|
|
|
pub fn get_current_block_root<Payload: AbstractExecPayload<T>>(
|
|
&mut self,
|
|
block: &SignedBeaconBlock<T, Payload>,
|
|
) -> Result<Hash256, ContextError> {
|
|
self.check_slot(block.slot())?;
|
|
|
|
if let Some(current_block_root) = self.current_block_root {
|
|
return Ok(current_block_root);
|
|
}
|
|
|
|
let current_block_root = block.message().tree_hash_root();
|
|
self.current_block_root = Some(current_block_root);
|
|
Ok(current_block_root)
|
|
}
|
|
|
|
fn check_slot(&self, slot: Slot) -> Result<(), ContextError> {
|
|
if slot == self.slot {
|
|
Ok(())
|
|
} else {
|
|
Err(ContextError::SlotMismatch {
|
|
slot,
|
|
expected: self.slot,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn check_epoch(&self, epoch: Epoch) -> Result<(), ContextError> {
|
|
let expected = self.slot.epoch(T::slots_per_epoch());
|
|
if epoch == expected {
|
|
Ok(())
|
|
} else {
|
|
Err(ContextError::EpochMismatch { epoch, expected })
|
|
}
|
|
}
|
|
|
|
pub fn get_indexed_attestation(
|
|
&mut self,
|
|
state: &BeaconState<T>,
|
|
attestation: &Attestation<T>,
|
|
) -> Result<&IndexedAttestation<T>, BlockOperationError<AttestationInvalid>> {
|
|
let key = (
|
|
attestation.data.clone(),
|
|
attestation.aggregation_bits.clone(),
|
|
);
|
|
|
|
match self.indexed_attestations.entry(key) {
|
|
Entry::Occupied(occupied) => Ok(occupied.into_mut()),
|
|
Entry::Vacant(vacant) => {
|
|
let committee =
|
|
state.get_beacon_committee(attestation.data.slot, attestation.data.index)?;
|
|
let indexed_attestation =
|
|
get_indexed_attestation(committee.committee, attestation)?;
|
|
Ok(vacant.insert(indexed_attestation))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn num_cached_indexed_attestations(&self) -> usize {
|
|
self.indexed_attestations.len()
|
|
}
|
|
|
|
pub fn set_kzg_commitments_consistent(mut self, kzg_commitments_consistent: bool) -> Self {
|
|
self.kzg_commitments_consistent = kzg_commitments_consistent;
|
|
self
|
|
}
|
|
|
|
pub fn kzg_commitments_consistent(&self) -> bool {
|
|
self.kzg_commitments_consistent
|
|
}
|
|
}
|