merge with upstream

This commit is contained in:
realbigsean
2022-12-01 11:13:07 -05:00
135 changed files with 4516 additions and 1734 deletions

View File

@@ -22,7 +22,7 @@ pub trait BitfieldBehaviour: Clone {}
/// A marker struct used to declare SSZ `Variable` behaviour on a `Bitfield`.
///
/// See the [`Bitfield`](struct.Bitfield.html) docs for usage.
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Variable<N> {
_phantom: PhantomData<N>,
}
@@ -30,7 +30,7 @@ pub struct Variable<N> {
/// A marker struct used to declare SSZ `Fixed` behaviour on a `Bitfield`.
///
/// See the [`Bitfield`](struct.Bitfield.html) docs for usage.
#[derive(Clone, PartialEq, Debug)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Fixed<N> {
_phantom: PhantomData<N>,
}
@@ -96,7 +96,7 @@ pub type BitVector<N> = Bitfield<Fixed<N>>;
/// byte (by `Vec` index) stores the lowest bit-indices and the right-most bit stores the lowest
/// bit-index. E.g., `smallvec![0b0000_0001, 0b0000_0010]` has bits `0, 9` set.
#[derive(Clone, Debug, Derivative)]
#[derivative(PartialEq, Hash(bound = ""))]
#[derivative(PartialEq, Eq, Hash(bound = ""))]
pub struct Bitfield<T> {
bytes: SmallVec<[u8; SMALLVEC_LEN]>,
len: usize,

View File

@@ -1,9 +1,13 @@
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 std::sync::Arc;
use tree_hash::TreeHash;
use types::{
AbstractExecPayload, BeaconState, BeaconStateError, BlobsSidecar, ChainSpec, EthSpec,
ExecPayload, Hash256, SignedBeaconBlock, Slot,
AbstractExecPayload, Attestation, AttestationData, BeaconState, BeaconStateError, BitList,
BlobsSidecar, ChainSpec, Epoch, EthSpec, ExecPayload, Hash256, IndexedAttestation,
SignedBeaconBlock, Slot,
};
#[derive(Debug)]
@@ -14,6 +18,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>>,
/// Should only be populated if the sidecar has not been validated.
blobs_sidecar: Option<Arc<BlobsSidecar<T>>>,
/// Whether `validate_blobs_sidecar` has successfully passed.
@@ -26,6 +33,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 {
@@ -40,6 +48,7 @@ impl<T: EthSpec> ConsensusContext<T> {
slot,
proposer_index: None,
current_block_root: None,
indexed_attestations: HashMap::new(),
blobs_sidecar: None,
blobs_sidecar_validated: false,
blobs_verified_vs_txs: false,
@@ -51,13 +60,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);
}
@@ -98,6 +133,41 @@ 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()
}
pub fn set_blobs_sidecar_validated(mut self, blobs_sidecar_validated: bool) -> Self {
self.blobs_sidecar_validated = blobs_sidecar_validated;
self

View File

@@ -117,16 +117,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))

View File

@@ -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)
}

View File

@@ -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() {

View File

@@ -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)
}

View File

@@ -9,6 +9,8 @@ name = "benches"
harness = false
[dependencies]
serde-big-array = {version = "0.3.2", features = ["const-generics"]}
merkle_proof = { path = "../../consensus/merkle_proof" }
bls = { path = "../../crypto/bls" }
kzg = { path = "../../crypto/kzg" }
compare_fields = { path = "../../common/compare_fields" }

View File

@@ -125,6 +125,8 @@ pub enum Error {
current_epoch: Epoch,
epoch: Epoch,
},
IndexNotSupported(usize),
MerkleTreeError(merkle_proof::MerkleTreeError),
}
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.
@@ -1735,6 +1737,57 @@ impl<T: EthSpec> BeaconState<T> {
};
Ok(sync_committee)
}
pub fn compute_merkle_proof(
&mut self,
generalized_index: usize,
) -> Result<Vec<Hash256>, Error> {
// 1. Convert generalized index to field index.
let field_index = match generalized_index {
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX
| light_client_update::NEXT_SYNC_COMMITTEE_INDEX => {
// Sync committees are top-level fields, subtract off the generalized indices
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
// in the `BeaconState`:
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
generalized_index
.checked_sub(tree_hash_cache::NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES)
.ok_or(Error::IndexNotSupported(generalized_index))?
}
light_client_update::FINALIZED_ROOT_INDEX => {
// Finalized root is the right child of `finalized_checkpoint`, divide by two to get
// the generalized index of `state.finalized_checkpoint`.
let finalized_checkpoint_generalized_index = generalized_index / 2;
// Subtract off the internal nodes. Result should be 105/2 - 32 = 20 which matches
// position of `finalized_checkpoint` in `BeaconState`.
finalized_checkpoint_generalized_index
.checked_sub(tree_hash_cache::NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES)
.ok_or(Error::IndexNotSupported(generalized_index))?
}
_ => return Err(Error::IndexNotSupported(generalized_index)),
};
// 2. Get all `BeaconState` leaves.
let mut cache = self
.tree_hash_cache_mut()
.take()
.ok_or(Error::TreeHashCacheNotInitialized)?;
let leaves = cache.recalculate_tree_hash_leaves(self)?;
self.tree_hash_cache_mut().restore(cache);
// 3. Make deposit tree.
// Use the depth of the `BeaconState` fields (i.e. `log2(32) = 5`).
let depth = light_client_update::CURRENT_SYNC_COMMITTEE_PROOF_LEN;
let tree = merkle_proof::MerkleTree::create(&leaves, depth);
let (_, mut proof) = tree.generate_proof(field_index, depth)?;
// 4. If we're proving the finalized root, patch in the finalized epoch to complete the proof.
if generalized_index == light_client_update::FINALIZED_ROOT_INDEX {
proof.insert(0, self.finalized_checkpoint().epoch.tree_hash_root());
}
Ok(proof)
}
}
impl From<RelativeEpochError> for Error {
@@ -1767,6 +1820,12 @@ impl From<tree_hash::Error> for Error {
}
}
impl From<merkle_proof::MerkleTreeError> for Error {
fn from(e: merkle_proof::MerkleTreeError) -> Error {
Error::MerkleTreeError(e)
}
}
impl From<ArithError> for Error {
fn from(e: ArithError) -> Error {
Error::ArithError(e)

View File

@@ -18,7 +18,7 @@ use tree_hash::{mix_in_length, MerkleHasher, TreeHash};
///
/// This constant is set with the assumption that there are `> 16` and `<= 32` fields on the
/// `BeaconState`. **Tree hashing will fail if this value is set incorrectly.**
const NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES: usize = 32;
pub const NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES: usize = 32;
/// The number of nodes in the Merkle tree of a validator record.
const NODES_PER_VALIDATOR: usize = 15;
@@ -210,6 +210,90 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
}
}
pub fn recalculate_tree_hash_leaves(
&mut self,
state: &BeaconState<T>,
) -> Result<Vec<Hash256>, Error> {
let mut leaves = vec![
// Genesis data leaves.
state.genesis_time().tree_hash_root(),
state.genesis_validators_root().tree_hash_root(),
// Current fork data leaves.
state.slot().tree_hash_root(),
state.fork().tree_hash_root(),
state.latest_block_header().tree_hash_root(),
// Roots leaves.
state
.block_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.block_roots)?,
state
.state_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.state_roots)?,
state
.historical_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.historical_roots)?,
// Eth1 Data leaves.
state.eth1_data().tree_hash_root(),
self.eth1_data_votes.recalculate_tree_hash_root(state)?,
state.eth1_deposit_index().tree_hash_root(),
// Validator leaves.
self.validators
.recalculate_tree_hash_root(state.validators())?,
state
.balances()
.recalculate_tree_hash_root(&mut self.balances_arena, &mut self.balances)?,
state
.randao_mixes()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.randao_mixes)?,
state
.slashings()
.recalculate_tree_hash_root(&mut self.slashings_arena, &mut self.slashings)?,
];
// Participation
if let BeaconState::Base(state) = state {
leaves.push(state.previous_epoch_attestations.tree_hash_root());
leaves.push(state.current_epoch_attestations.tree_hash_root());
} else {
leaves.push(
self.previous_epoch_participation
.recalculate_tree_hash_root(&ParticipationList::new(
state.previous_epoch_participation()?,
))?,
);
leaves.push(
self.current_epoch_participation
.recalculate_tree_hash_root(&ParticipationList::new(
state.current_epoch_participation()?,
))?,
);
}
// Checkpoint leaves
leaves.push(state.justification_bits().tree_hash_root());
leaves.push(state.previous_justified_checkpoint().tree_hash_root());
leaves.push(state.current_justified_checkpoint().tree_hash_root());
leaves.push(state.finalized_checkpoint().tree_hash_root());
// Inactivity & light-client sync committees (Altair and later).
if let Ok(inactivity_scores) = state.inactivity_scores() {
leaves.push(
self.inactivity_scores
.recalculate_tree_hash_root(inactivity_scores)?,
);
}
if let Ok(current_sync_committee) = state.current_sync_committee() {
leaves.push(current_sync_committee.tree_hash_root());
}
if let Ok(next_sync_committee) = state.next_sync_committee() {
leaves.push(next_sync_committee.tree_hash_root());
}
// Execution payload (merge and later).
if let Ok(payload_header) = state.latest_execution_payload_header() {
leaves.push(payload_header.tree_hash_root());
}
Ok(leaves)
}
/// Updates the cache and returns the tree hash root for the given `state`.
///
/// The provided `state` should be a descendant of the last `state` given to this function, or
@@ -246,121 +330,9 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
let mut hasher = MerkleHasher::with_leaves(NUM_BEACON_STATE_HASH_TREE_ROOT_LEAVES);
hasher.write(state.genesis_time().tree_hash_root().as_bytes())?;
hasher.write(state.genesis_validators_root().tree_hash_root().as_bytes())?;
hasher.write(state.slot().tree_hash_root().as_bytes())?;
hasher.write(state.fork().tree_hash_root().as_bytes())?;
hasher.write(state.latest_block_header().tree_hash_root().as_bytes())?;
hasher.write(
state
.block_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.block_roots)?
.as_bytes(),
)?;
hasher.write(
state
.state_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.state_roots)?
.as_bytes(),
)?;
hasher.write(
state
.historical_roots()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.historical_roots)?
.as_bytes(),
)?;
hasher.write(state.eth1_data().tree_hash_root().as_bytes())?;
hasher.write(
self.eth1_data_votes
.recalculate_tree_hash_root(state)?
.as_bytes(),
)?;
hasher.write(state.eth1_deposit_index().tree_hash_root().as_bytes())?;
hasher.write(
self.validators
.recalculate_tree_hash_root(state.validators())?
.as_bytes(),
)?;
hasher.write(
state
.balances()
.recalculate_tree_hash_root(&mut self.balances_arena, &mut self.balances)?
.as_bytes(),
)?;
hasher.write(
state
.randao_mixes()
.recalculate_tree_hash_root(&mut self.fixed_arena, &mut self.randao_mixes)?
.as_bytes(),
)?;
hasher.write(
state
.slashings()
.recalculate_tree_hash_root(&mut self.slashings_arena, &mut self.slashings)?
.as_bytes(),
)?;
// Participation
if let BeaconState::Base(state) = state {
hasher.write(
state
.previous_epoch_attestations
.tree_hash_root()
.as_bytes(),
)?;
hasher.write(state.current_epoch_attestations.tree_hash_root().as_bytes())?;
} else {
hasher.write(
self.previous_epoch_participation
.recalculate_tree_hash_root(&ParticipationList::new(
state.previous_epoch_participation()?,
))?
.as_bytes(),
)?;
hasher.write(
self.current_epoch_participation
.recalculate_tree_hash_root(&ParticipationList::new(
state.current_epoch_participation()?,
))?
.as_bytes(),
)?;
}
hasher.write(state.justification_bits().tree_hash_root().as_bytes())?;
hasher.write(
state
.previous_justified_checkpoint()
.tree_hash_root()
.as_bytes(),
)?;
hasher.write(
state
.current_justified_checkpoint()
.tree_hash_root()
.as_bytes(),
)?;
hasher.write(state.finalized_checkpoint().tree_hash_root().as_bytes())?;
// Inactivity & light-client sync committees (Altair and later).
if let Ok(inactivity_scores) = state.inactivity_scores() {
hasher.write(
self.inactivity_scores
.recalculate_tree_hash_root(inactivity_scores)?
.as_bytes(),
)?;
}
if let Ok(current_sync_committee) = state.current_sync_committee() {
hasher.write(current_sync_committee.tree_hash_root().as_bytes())?;
}
if let Ok(next_sync_committee) = state.next_sync_committee() {
hasher.write(next_sync_committee.tree_hash_root().as_bytes())?;
}
// Execution payload (merge and later).
if let Ok(payload_header) = state.latest_execution_payload_header() {
hasher.write(payload_header.tree_hash_root().as_bytes())?;
let leaves = self.recalculate_tree_hash_leaves(state)?;
for leaf in leaves {
hasher.write(leaf.as_bytes())?;
}
// Withdrawal indices (Capella and later).

View File

@@ -844,7 +844,7 @@ impl ChainSpec {
domain_sync_committee_selection_proof: 8,
domain_contribution_and_proof: 9,
altair_fork_version: [0x01, 0x00, 0x00, 0x64],
altair_fork_epoch: Some(Epoch::new(256)),
altair_fork_epoch: Some(Epoch::new(512)),
/*
* Merge hard fork params
@@ -855,14 +855,11 @@ impl ChainSpec {
.expect("pow does not overflow"),
proportional_slashing_multiplier_bellatrix: 3,
bellatrix_fork_version: [0x02, 0x00, 0x00, 0x64],
bellatrix_fork_epoch: None,
terminal_total_difficulty: Uint256::MAX
.checked_sub(Uint256::from(2u64.pow(10)))
.expect("subtraction does not overflow")
// Add 1 since the spec declares `2**256 - 2**10` and we use
// `Uint256::MAX` which is `2*256- 1`.
.checked_add(Uint256::one())
.expect("addition does not overflow"),
bellatrix_fork_epoch: Some(Epoch::new(385536)),
terminal_total_difficulty: Uint256::from_dec_str(
"8626000000000000000000058750000000000000000000",
)
.expect("terminal_total_difficulty is a valid integer"),
terminal_block_hash: ExecutionBlockHash::zero(),
terminal_block_hash_activation_epoch: Epoch::new(u64::MAX),
safe_slots_to_import_optimistically: 128u64,

View File

@@ -21,17 +21,15 @@ pub struct LightClientBootstrap<T: EthSpec> {
}
impl<T: EthSpec> LightClientBootstrap<T> {
pub fn from_beacon_state(beacon_state: BeaconState<T>) -> Result<Self, Error> {
pub fn from_beacon_state(beacon_state: &mut BeaconState<T>) -> Result<Self, Error> {
let mut header = beacon_state.latest_block_header().clone();
header.state_root = beacon_state.tree_hash_root();
let current_sync_committee_branch =
beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?;
Ok(LightClientBootstrap {
header,
current_sync_committee: beacon_state.current_sync_committee()?.clone(),
/// TODO(Giulio2002): Generate Merkle Proof, this is just empty hashes
current_sync_committee_branch: FixedVector::new(vec![
Hash256::zero();
CURRENT_SYNC_COMMITTEE_PROOF_LEN
])?,
current_sync_committee_branch: FixedVector::new(current_sync_committee_branch)?,
})
}
}

View File

@@ -31,7 +31,7 @@ impl<T: EthSpec> LightClientFinalityUpdate<T> {
chain_spec: ChainSpec,
beacon_state: BeaconState<T>,
block: BeaconBlock<T>,
attested_state: BeaconState<T>,
attested_state: &mut BeaconState<T>,
finalized_block: BeaconBlock<T>,
) -> Result<Self, Error> {
let altair_fork_epoch = chain_spec
@@ -60,11 +60,12 @@ impl<T: EthSpec> LightClientFinalityUpdate<T> {
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
return Err(Error::InvalidFinalizedBlock);
}
// TODO(Giulio2002): compute proper merkle proofs.
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
Ok(Self {
attested_header: attested_header,
finalized_header: finalized_header,
finality_branch: FixedVector::new(vec![Hash256::zero(); FINALIZED_ROOT_PROOF_LEN])?,
finality_branch: FixedVector::new(finality_branch)?,
sync_aggregate: sync_aggregate.clone(),
signature_slot: block.slot(),
})

View File

@@ -77,7 +77,7 @@ impl<T: EthSpec> LightClientUpdate<T> {
chain_spec: ChainSpec,
beacon_state: BeaconState<T>,
block: BeaconBlock<T>,
attested_state: BeaconState<T>,
attested_state: &mut BeaconState<T>,
finalized_block: BeaconBlock<T>,
) -> Result<Self, Error> {
let altair_fork_epoch = chain_spec
@@ -114,16 +114,15 @@ impl<T: EthSpec> LightClientUpdate<T> {
if finalized_header.tree_hash_root() != beacon_state.finalized_checkpoint().root {
return Err(Error::InvalidFinalizedBlock);
}
// TODO(Giulio2002): compute proper merkle proofs.
let next_sync_committee_branch =
attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?;
let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?;
Ok(Self {
attested_header,
next_sync_committee: attested_state.next_sync_committee()?.clone(),
next_sync_committee_branch: FixedVector::new(vec![
Hash256::zero();
NEXT_SYNC_COMMITTEE_PROOF_LEN
])?,
next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?,
finalized_header,
finality_branch: FixedVector::new(vec![Hash256::zero(); FINALIZED_ROOT_PROOF_LEN])?,
finality_branch: FixedVector::new(finality_branch)?,
sync_aggregate: sync_aggregate.clone(),
signature_slot: block.slot(),
})

View File

@@ -148,8 +148,8 @@ impl<T: EthSpec> From<FullPayload<T>> for ExecutionPayload<T> {
}
impl<'a, T: EthSpec> From<FullPayloadRef<'a, T>> for ExecutionPayload<T> {
fn from(full_payload: FullPayloadRef<'a, T>) -> Self {
match full_payload {
fn from(full_payload_ref: FullPayloadRef<'a, T>) -> Self {
match full_payload_ref {
FullPayloadRef::Merge(payload) => {
ExecutionPayload::Merge(payload.execution_payload.clone())
}
@@ -163,6 +163,17 @@ impl<'a, T: EthSpec> From<FullPayloadRef<'a, T>> for ExecutionPayload<T> {
}
}
// FIXME: can this be implemented as Deref or Clone somehow?
impl<'a, T: EthSpec> From<FullPayloadRef<'a, T>> for FullPayload<T> {
fn from(full_payload_ref: FullPayloadRef<'a, T>) -> Self {
match full_payload_ref {
FullPayloadRef::Merge(payload_ref) => FullPayload::Merge(payload_ref.clone()),
FullPayloadRef::Capella(payload_ref) => FullPayload::Capella(payload_ref.clone()),
FullPayloadRef::Eip4844(payload_ref) => FullPayload::Eip4844(payload_ref.clone()),
}
}
}
impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
fn block_type() -> BlockType {
BlockType::Full

View File

@@ -10,7 +10,7 @@ use tree_hash_derive::TreeHash;
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct Withdrawal {
#[serde(with = "eth2_serde_utils::quoted_u64")]