mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-10 20:22:02 +00:00
Merged with unstable
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
use crate::common::get_indexed_attestation;
|
||||
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::marker::PhantomData;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconState, BeaconStateError, ChainSpec, EthSpec, Hash256,
|
||||
SignedBeaconBlock, Slot,
|
||||
AbstractExecPayload, Attestation, AttestationData, BeaconState, BeaconStateError, BitList,
|
||||
ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -13,6 +16,9 @@ pub struct ConsensusContext<T: EthSpec> {
|
||||
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>>,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
@@ -20,6 +26,7 @@ pub struct ConsensusContext<T: EthSpec> {
|
||||
pub enum ContextError {
|
||||
BeaconState(BeaconStateError),
|
||||
SlotMismatch { slot: Slot, expected: Slot },
|
||||
EpochMismatch { epoch: Epoch, expected: Epoch },
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for ContextError {
|
||||
@@ -34,6 +41,7 @@ impl<T: EthSpec> ConsensusContext<T> {
|
||||
slot,
|
||||
proposer_index: None,
|
||||
current_block_root: None,
|
||||
indexed_attestations: HashMap::new(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -43,13 +51,39 @@ impl<T: EthSpec> ConsensusContext<T> {
|
||||
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);
|
||||
}
|
||||
@@ -89,4 +123,39 @@ impl<T: EthSpec> ConsensusContext<T> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,16 +120,13 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
let verify_signatures = match block_signature_strategy {
|
||||
BlockSignatureStrategy::VerifyBulk => {
|
||||
// Verify all signatures in the block at once.
|
||||
let block_root = Some(ctxt.get_current_block_root(signed_block)?);
|
||||
let proposer_index = Some(ctxt.get_proposer_index(state, spec)?);
|
||||
block_verify!(
|
||||
BlockSignatureVerifier::verify_entire_block(
|
||||
state,
|
||||
|i| get_pubkey_from_state(state, i),
|
||||
|pk_bytes| pk_bytes.decompress().ok().map(Cow::Owned),
|
||||
signed_block,
|
||||
block_root,
|
||||
proposer_index,
|
||||
ctxt,
|
||||
spec
|
||||
)
|
||||
.is_ok(),
|
||||
@@ -352,6 +349,7 @@ pub fn get_new_eth1_data<T: EthSpec>(
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload
|
||||
pub fn partially_verify_execution_payload<'payload, T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
state: &BeaconState<T>,
|
||||
block_slot: Slot,
|
||||
payload: Payload::Ref<'payload>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
@@ -372,7 +370,7 @@ pub fn partially_verify_execution_payload<'payload, T: EthSpec, Payload: Abstrac
|
||||
}
|
||||
);
|
||||
|
||||
let timestamp = compute_timestamp_at_slot(state, spec)?;
|
||||
let timestamp = compute_timestamp_at_slot(state, block_slot, spec)?;
|
||||
block_verify!(
|
||||
payload.timestamp() == timestamp,
|
||||
BlockProcessingError::ExecutionInvalidTimestamp {
|
||||
@@ -396,7 +394,7 @@ pub fn process_execution_payload<'payload, T: EthSpec, Payload: AbstractExecPayl
|
||||
payload: Payload::Ref<'payload>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
partially_verify_execution_payload::<T, Payload>(state, payload, spec)?;
|
||||
partially_verify_execution_payload::<T, Payload>(state, state.slot(), payload, spec)?;
|
||||
|
||||
match state.latest_execution_payload_header_mut()? {
|
||||
ExecutionPayloadHeaderRefMut::Merge(header_mut) => {
|
||||
@@ -459,9 +457,10 @@ pub fn is_execution_enabled<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#compute_timestamp_at_slot
|
||||
pub fn compute_timestamp_at_slot<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
block_slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, ArithError> {
|
||||
let slots_since_genesis = state.slot().as_u64().safe_sub(spec.genesis_slot.as_u64())?;
|
||||
let slots_since_genesis = block_slot.as_u64().safe_sub(spec.genesis_slot.as_u64())?;
|
||||
slots_since_genesis
|
||||
.safe_mul(spec.seconds_per_slot)
|
||||
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![allow(clippy::integer_arithmetic)]
|
||||
|
||||
use super::signature_sets::{Error as SignatureSetError, *};
|
||||
use crate::common::get_indexed_attestation;
|
||||
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
||||
use crate::{ConsensusContext, ContextError};
|
||||
use bls::{verify_signature_sets, PublicKey, PublicKeyBytes, SignatureSet};
|
||||
use rayon::prelude::*;
|
||||
use std::borrow::Cow;
|
||||
@@ -28,6 +28,8 @@ pub enum Error {
|
||||
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
|
||||
/// Failed to load a signature set. The block may be invalid or we failed to process it.
|
||||
SignatureSetError(SignatureSetError),
|
||||
/// Error related to the consensus context, likely the proposer index or block root calc.
|
||||
ContextError(ContextError),
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for Error {
|
||||
@@ -36,6 +38,12 @@ impl From<BeaconStateError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContextError> for Error {
|
||||
fn from(e: ContextError) -> Error {
|
||||
Error::ContextError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignatureSetError> for Error {
|
||||
fn from(e: SignatureSetError) -> Error {
|
||||
match e {
|
||||
@@ -122,12 +130,11 @@ where
|
||||
get_pubkey: F,
|
||||
decompressor: D,
|
||||
block: &'a SignedBeaconBlock<T, Payload>,
|
||||
block_root: Option<Hash256>,
|
||||
verified_proposer_index: Option<u64>,
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
spec: &'a ChainSpec,
|
||||
) -> Result<()> {
|
||||
let mut verifier = Self::new(state, get_pubkey, decompressor, spec);
|
||||
verifier.include_all_signatures(block, block_root, verified_proposer_index)?;
|
||||
verifier.include_all_signatures(block, ctxt)?;
|
||||
verifier.verify()
|
||||
}
|
||||
|
||||
@@ -135,11 +142,14 @@ where
|
||||
pub fn include_all_signatures<Payload: AbstractExecPayload<T>>(
|
||||
&mut self,
|
||||
block: &'a SignedBeaconBlock<T, Payload>,
|
||||
block_root: Option<Hash256>,
|
||||
verified_proposer_index: Option<u64>,
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
) -> Result<()> {
|
||||
let block_root = Some(ctxt.get_current_block_root(block)?);
|
||||
let verified_proposer_index =
|
||||
Some(ctxt.get_proposer_index_from_epoch_state(self.state, self.spec)?);
|
||||
|
||||
self.include_block_proposal(block, block_root, verified_proposer_index)?;
|
||||
self.include_all_signatures_except_proposal(block, verified_proposer_index)?;
|
||||
self.include_all_signatures_except_proposal(block, ctxt)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -149,12 +159,14 @@ where
|
||||
pub fn include_all_signatures_except_proposal<Payload: AbstractExecPayload<T>>(
|
||||
&mut self,
|
||||
block: &'a SignedBeaconBlock<T, Payload>,
|
||||
verified_proposer_index: Option<u64>,
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
) -> Result<()> {
|
||||
let verified_proposer_index =
|
||||
Some(ctxt.get_proposer_index_from_epoch_state(self.state, self.spec)?);
|
||||
self.include_randao_reveal(block, verified_proposer_index)?;
|
||||
self.include_proposer_slashings(block)?;
|
||||
self.include_attester_slashings(block)?;
|
||||
self.include_attestations(block)?;
|
||||
self.include_attestations(block, ctxt)?;
|
||||
// Deposits are not included because they can legally have invalid signatures.
|
||||
self.include_exits(block)?;
|
||||
self.include_sync_aggregate(block)?;
|
||||
@@ -262,7 +274,8 @@ where
|
||||
pub fn include_attestations<Payload: AbstractExecPayload<T>>(
|
||||
&mut self,
|
||||
block: &'a SignedBeaconBlock<T, Payload>,
|
||||
) -> Result<Vec<IndexedAttestation<T>>> {
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
) -> Result<()> {
|
||||
self.sets
|
||||
.sets
|
||||
.reserve(block.message().body().attestations().len());
|
||||
@@ -272,28 +285,18 @@ where
|
||||
.body()
|
||||
.attestations()
|
||||
.iter()
|
||||
.try_fold(
|
||||
Vec::with_capacity(block.message().body().attestations().len()),
|
||||
|mut vec, attestation| {
|
||||
let committee = self
|
||||
.state
|
||||
.get_beacon_committee(attestation.data.slot, attestation.data.index)?;
|
||||
let indexed_attestation =
|
||||
get_indexed_attestation(committee.committee, attestation)?;
|
||||
.try_for_each(|attestation| {
|
||||
let indexed_attestation = ctxt.get_indexed_attestation(self.state, attestation)?;
|
||||
|
||||
self.sets.push(indexed_attestation_signature_set(
|
||||
self.state,
|
||||
self.get_pubkey.clone(),
|
||||
&attestation.signature,
|
||||
&indexed_attestation,
|
||||
self.spec,
|
||||
)?);
|
||||
|
||||
vec.push(indexed_attestation);
|
||||
|
||||
Ok(vec)
|
||||
},
|
||||
)
|
||||
self.sets.push(indexed_attestation_signature_set(
|
||||
self.state,
|
||||
self.get_pubkey.clone(),
|
||||
&attestation.signature,
|
||||
indexed_attestation,
|
||||
self.spec,
|
||||
)?);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(Error::into)
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,14 @@ pub mod base {
|
||||
|
||||
// Verify and apply each attestation.
|
||||
for (i, attestation) in attestations.iter().enumerate() {
|
||||
verify_attestation_for_block_inclusion(state, attestation, verify_signatures, spec)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
verify_attestation_for_block_inclusion(
|
||||
state,
|
||||
attestation,
|
||||
ctxt,
|
||||
verify_signatures,
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| e.into_with_index(i))?;
|
||||
|
||||
let pending_attestation = PendingAttestation {
|
||||
aggregation_bits: attestation.aggregation_bits.clone(),
|
||||
@@ -100,19 +106,11 @@ pub mod altair {
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
let proposer_index = ctxt.get_proposer_index(state, spec)?;
|
||||
attestations
|
||||
.iter()
|
||||
.enumerate()
|
||||
.try_for_each(|(i, attestation)| {
|
||||
process_attestation(
|
||||
state,
|
||||
attestation,
|
||||
i,
|
||||
proposer_index,
|
||||
verify_signatures,
|
||||
spec,
|
||||
)
|
||||
process_attestation(state, attestation, i, ctxt, verify_signatures, spec)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -120,16 +118,24 @@ pub mod altair {
|
||||
state: &mut BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
att_index: usize,
|
||||
proposer_index: u64,
|
||||
ctxt: &mut ConsensusContext<T>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
|
||||
state.build_committee_cache(RelativeEpoch::Current, spec)?;
|
||||
|
||||
let indexed_attestation =
|
||||
verify_attestation_for_block_inclusion(state, attestation, verify_signatures, spec)
|
||||
.map_err(|e| e.into_with_index(att_index))?;
|
||||
let proposer_index = ctxt.get_proposer_index(state, spec)?;
|
||||
|
||||
let attesting_indices = &verify_attestation_for_block_inclusion(
|
||||
state,
|
||||
attestation,
|
||||
ctxt,
|
||||
verify_signatures,
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| e.into_with_index(att_index))?
|
||||
.attesting_indices;
|
||||
|
||||
// Matching roots, participation flag indices
|
||||
let data = &attestation.data;
|
||||
@@ -141,7 +147,7 @@ pub mod altair {
|
||||
let total_active_balance = state.get_total_active_balance()?;
|
||||
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
|
||||
let mut proposer_reward_numerator = 0;
|
||||
for index in &indexed_attestation.attesting_indices {
|
||||
for index in attesting_indices {
|
||||
let index = *index as usize;
|
||||
|
||||
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::errors::{AttestationInvalid as Invalid, BlockOperationError};
|
||||
use super::VerifySignatures;
|
||||
use crate::common::get_indexed_attestation;
|
||||
use crate::per_block_processing::is_valid_indexed_attestation;
|
||||
use crate::ConsensusContext;
|
||||
use safe_arith::SafeArith;
|
||||
use types::*;
|
||||
|
||||
@@ -15,12 +15,13 @@ fn error(reason: Invalid) -> BlockOperationError<Invalid> {
|
||||
/// to `state`. Otherwise, returns a descriptive `Err`.
|
||||
///
|
||||
/// Optionally verifies the aggregate signature, depending on `verify_signatures`.
|
||||
pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
|
||||
pub fn verify_attestation_for_block_inclusion<'ctxt, T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
ctxt: &'ctxt mut ConsensusContext<T>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<IndexedAttestation<T>> {
|
||||
) -> Result<&'ctxt IndexedAttestation<T>> {
|
||||
let data = &attestation.data;
|
||||
|
||||
verify!(
|
||||
@@ -39,7 +40,7 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
|
||||
}
|
||||
);
|
||||
|
||||
verify_attestation_for_state(state, attestation, verify_signatures, spec)
|
||||
verify_attestation_for_state(state, attestation, ctxt, verify_signatures, spec)
|
||||
}
|
||||
|
||||
/// Returns `Ok(())` if `attestation` is a valid attestation to the chain that precedes the given
|
||||
@@ -49,12 +50,13 @@ pub fn verify_attestation_for_block_inclusion<T: EthSpec>(
|
||||
/// prior blocks in `state`.
|
||||
///
|
||||
/// Spec v0.12.1
|
||||
pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
pub fn verify_attestation_for_state<'ctxt, T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
attestation: &Attestation<T>,
|
||||
ctxt: &'ctxt mut ConsensusContext<T>,
|
||||
verify_signatures: VerifySignatures,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<IndexedAttestation<T>> {
|
||||
) -> Result<&'ctxt IndexedAttestation<T>> {
|
||||
let data = &attestation.data;
|
||||
|
||||
verify!(
|
||||
@@ -66,9 +68,8 @@ pub fn verify_attestation_for_state<T: EthSpec>(
|
||||
verify_casper_ffg_vote(attestation, state)?;
|
||||
|
||||
// Check signature and bitfields
|
||||
let committee = state.get_beacon_committee(attestation.data.slot, attestation.data.index)?;
|
||||
let indexed_attestation = get_indexed_attestation(committee.committee, attestation)?;
|
||||
is_valid_indexed_attestation(state, &indexed_attestation, verify_signatures, spec)?;
|
||||
let indexed_attestation = ctxt.get_indexed_attestation(state, attestation)?;
|
||||
is_valid_indexed_attestation(state, indexed_attestation, verify_signatures, spec)?;
|
||||
|
||||
Ok(indexed_attestation)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user