merge with upstream

This commit is contained in:
realbigsean
2022-11-22 18:38:30 -05:00
70 changed files with 1521 additions and 761 deletions

View File

@@ -78,17 +78,20 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> SignedRoot
{
}
/// Empty block trait for each block variant to implement.
pub trait EmptyBlock {
/// Returns an empty block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self;
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlock<T, Payload> {
// FIXME: deal with capella / eip4844 forks here as well
/// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
if spec.bellatrix_fork_epoch == Some(T::genesis_epoch()) {
Self::Merge(BeaconBlockMerge::empty(spec))
} else if spec.altair_fork_epoch == Some(T::genesis_epoch()) {
Self::Altair(BeaconBlockAltair::empty(spec))
} else {
Self::Base(BeaconBlockBase::empty(spec))
}
map_fork_name!(
spec.fork_name_at_epoch(T::genesis_epoch()),
Self,
EmptyBlock::empty(spec)
)
}
/// Custom SSZ decoder that takes a `ChainSpec` as context.
@@ -117,13 +120,12 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlock<T, Payload> {
/// Usually it's better to prefer `from_ssz_bytes` which will decode the correct variant based
/// on the fork slot.
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
BeaconBlockMerge::from_ssz_bytes(bytes)
.map(BeaconBlock::Merge)
.or_else(|_| {
BeaconBlockAltair::from_ssz_bytes(bytes)
.map(BeaconBlock::Altair)
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
})
BeaconBlockEip4844::from_ssz_bytes(bytes)
.map(BeaconBlock::Eip4844)
.or_else(|_| BeaconBlockCapella::from_ssz_bytes(bytes).map(BeaconBlock::Capella))
.or_else(|_| BeaconBlockMerge::from_ssz_bytes(bytes).map(BeaconBlock::Merge))
.or_else(|_| BeaconBlockAltair::from_ssz_bytes(bytes).map(BeaconBlock::Altair))
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
}
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.
@@ -266,9 +268,8 @@ impl<'a, T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockRefMut<'a, T, P
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBase<T, Payload> {
/// Returns an empty block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockBase<T, Payload> {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockBase {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -291,7 +292,9 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBase<T, Payload> {
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBase<T, Payload> {
/// Return a block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let header = BeaconBlockHeader {
@@ -387,9 +390,9 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBase<T, Payload> {
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockAltair<T, Payload> {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockAltair<T, Payload> {
/// Returns an empty Altair block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockAltair {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -413,7 +416,9 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockAltair<T, Payload>
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockAltair<T, Payload> {
/// Return an Altair block where the block has maximum size.
pub fn full(spec: &ChainSpec) -> Self {
let base_block: BeaconBlockBase<_, Payload> = BeaconBlockBase::full(spec);
@@ -446,9 +451,9 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockAltair<T, Payload>
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockMerge<T, Payload> {
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockMerge<T, Payload> {
/// Returns an empty Merge block to be used during genesis.
pub fn empty(spec: &ChainSpec) -> Self {
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockMerge {
slot: spec.genesis_slot,
proposer_index: 0,
@@ -474,6 +479,67 @@ impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockMerge<T, Payload> {
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockCapella<T, Payload> {
/// Returns an empty Capella block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockCapella {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyCapella {
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
proposer_slashings: VariableList::empty(),
attester_slashings: VariableList::empty(),
attestations: VariableList::empty(),
deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(),
execution_payload: Payload::Capella::default(),
#[cfg(feature = "withdrawals")]
bls_to_execution_changes: VariableList::empty(),
},
}
}
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> EmptyBlock for BeaconBlockEip4844<T, Payload> {
/// Returns an empty Eip4844 block to be used during genesis.
fn empty(spec: &ChainSpec) -> Self {
BeaconBlockEip4844 {
slot: spec.genesis_slot,
proposer_index: 0,
parent_root: Hash256::zero(),
state_root: Hash256::zero(),
body: BeaconBlockBodyEip4844 {
randao_reveal: Signature::empty(),
eth1_data: Eth1Data {
deposit_root: Hash256::zero(),
block_hash: Hash256::zero(),
deposit_count: 0,
},
graffiti: Graffiti::default(),
proposer_slashings: VariableList::empty(),
attester_slashings: VariableList::empty(),
attestations: VariableList::empty(),
deposits: VariableList::empty(),
voluntary_exits: VariableList::empty(),
sync_aggregate: SyncAggregate::empty(),
execution_payload: Payload::Eip4844::default(),
#[cfg(feature = "withdrawals")]
bls_to_execution_changes: VariableList::empty(),
blob_kzg_commitments: VariableList::empty(),
},
}
}
}
// We can convert pre-Bellatrix blocks without payloads into blocks "with" payloads.
impl<E: EthSpec> From<BeaconBlockBase<E, BlindedPayload<E>>>
for BeaconBlockBase<E, FullPayload<E>>

View File

@@ -62,6 +62,10 @@ pub struct BeaconBlockBody<T: EthSpec, Payload: AbstractExecPayload<T> = FullPay
#[superstruct(only(Eip4844), partial_getter(rename = "execution_payload_eip4844"))]
#[serde(flatten)]
pub execution_payload: Payload::Eip4844,
#[cfg(feature = "withdrawals")]
#[superstruct(only(Capella, Eip4844))]
pub bls_to_execution_changes:
VariableList<SignedBlsToExecutionChange, T::MaxBlsToExecutionChanges>,
#[superstruct(only(Eip4844))]
pub blob_kzg_commitments: VariableList<KzgCommitment, T::MaxBlobsPerBlock>,
#[superstruct(only(Base, Altair))]
@@ -72,7 +76,7 @@ pub struct BeaconBlockBody<T: EthSpec, Payload: AbstractExecPayload<T> = FullPay
}
impl<T: EthSpec, Payload: AbstractExecPayload<T>> BeaconBlockBody<T, Payload> {
pub fn execution_payload<'a>(&'a self) -> Result<Payload::Ref<'a>, Error> {
pub fn execution_payload(&self) -> Result<Payload::Ref<'_>, Error> {
self.to_ref().execution_payload()
}
}
@@ -297,6 +301,8 @@ impl<E: EthSpec> From<BeaconBlockBodyCapella<E, FullPayload<E>>>
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
} = body;
(
@@ -313,6 +319,8 @@ impl<E: EthSpec> From<BeaconBlockBodyCapella<E, FullPayload<E>>>
execution_payload: BlindedPayloadCapella {
execution_payload_header: From::from(execution_payload.clone()),
},
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
},
Some(execution_payload),
)
@@ -337,6 +345,8 @@ impl<E: EthSpec> From<BeaconBlockBodyEip4844<E, FullPayload<E>>>
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
blob_kzg_commitments,
} = body;
@@ -354,6 +364,8 @@ impl<E: EthSpec> From<BeaconBlockBodyEip4844<E, FullPayload<E>>>
execution_payload: BlindedPayloadEip4844 {
execution_payload_header: From::from(execution_payload.clone()),
},
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
blob_kzg_commitments,
},
Some(execution_payload),
@@ -421,6 +433,8 @@ impl<E: EthSpec> BeaconBlockBodyCapella<E, FullPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
} = self;
BeaconBlockBodyCapella {
@@ -436,6 +450,8 @@ impl<E: EthSpec> BeaconBlockBodyCapella<E, FullPayload<E>> {
execution_payload: BlindedPayloadCapella {
execution_payload_header: From::from(execution_payload.clone()),
},
#[cfg(feature = "withdrawals")]
bls_to_execution_changes: bls_to_execution_changes.clone(),
}
}
}
@@ -453,6 +469,8 @@ impl<E: EthSpec> BeaconBlockBodyEip4844<E, FullPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
blob_kzg_commitments,
} = self;
@@ -469,6 +487,8 @@ impl<E: EthSpec> BeaconBlockBodyEip4844<E, FullPayload<E>> {
execution_payload: BlindedPayloadEip4844 {
execution_payload_header: From::from(execution_payload.clone()),
},
#[cfg(feature = "withdrawals")]
bls_to_execution_changes: bls_to_execution_changes.clone(),
blob_kzg_commitments: blob_kzg_commitments.clone(),
}
}

View File

@@ -296,14 +296,11 @@ where
// Withdrawals
#[cfg(feature = "withdrawals")]
#[superstruct(only(Capella, Eip4844))]
pub withdrawal_queue: VariableList<Withdrawal, T::WithdrawalQueueLimit>,
#[cfg(feature = "withdrawals")]
#[superstruct(only(Capella, Eip4844))]
#[superstruct(only(Capella, Eip4844), partial_getter(copy))]
pub next_withdrawal_index: u64,
#[cfg(feature = "withdrawals")]
#[superstruct(only(Capella, Eip4844))]
pub next_partial_withdrawal_validator_index: u64,
#[superstruct(only(Capella, Eip4844), partial_getter(copy))]
pub next_withdrawal_validator_index: u64,
// Caching (not in the spec)
#[serde(skip_serializing, skip_deserializing)]
@@ -1787,6 +1784,8 @@ impl<T: EthSpec> CompareFields for BeaconState<T> {
(BeaconState::Base(x), BeaconState::Base(y)) => x.compare_fields(y),
(BeaconState::Altair(x), BeaconState::Altair(y)) => x.compare_fields(y),
(BeaconState::Merge(x), BeaconState::Merge(y)) => x.compare_fields(y),
(BeaconState::Capella(x), BeaconState::Capella(y)) => x.compare_fields(y),
(BeaconState::Eip4844(x), BeaconState::Eip4844(y)) => x.compare_fields(y),
_ => panic!("compare_fields: mismatched state variants",),
}
}

View File

@@ -363,6 +363,16 @@ impl<T: EthSpec> BeaconTreeHashCacheInner<T> {
hasher.write(payload_header.tree_hash_root().as_bytes())?;
}
// Withdrawal indices (Capella and later).
#[cfg(feature = "withdrawals")]
if let Ok(next_withdrawal_index) = state.next_withdrawal_index() {
hasher.write(next_withdrawal_index.tree_hash_root().as_bytes())?;
}
#[cfg(feature = "withdrawals")]
if let Ok(next_withdrawal_validator_index) = state.next_withdrawal_validator_index() {
hasher.write(next_withdrawal_validator_index.tree_hash_root().as_bytes())?;
}
let root = hasher.finish()?;
self.previous_state = Some((root, state.slot()));

View File

@@ -4,7 +4,6 @@ use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz_derive::{Decode, Encode};
use ssz_types::VariableList;
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
@@ -23,6 +22,7 @@ impl<T: EthSpec> BlobsSidecar<T> {
pub fn empty() -> Self {
Self::default()
}
#[allow(clippy::integer_arithmetic)]
pub fn max_size() -> usize {
// Fixed part
Self::empty().as_ssz_bytes().len()

View File

@@ -0,0 +1,30 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::PublicKeyBytes;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// A deposit to potentially become a beacon chain validator.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct BlsToExecutionChange {
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub validator_index: u64,
pub from_bls_pubkey: PublicKeyBytes,
pub to_execution_address: Address,
}
impl SignedRoot for BlsToExecutionChange {}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(BlsToExecutionChange);
}

View File

@@ -98,8 +98,6 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
/*
* New in Capella
*/
type MaxPartialWithdrawalsPerEpoch: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type WithdrawalQueueLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxBlsToExecutionChanges: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxWithdrawalsPerPayload: Unsigned + Clone + Sync + Send + Debug + PartialEq;
/*
@@ -235,16 +233,6 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
Self::BytesPerLogsBloom::to_usize()
}
/// Returns the `MAX_PARTIAL_WITHDRAWALS_PER_EPOCH` constant for this specification.
fn max_partial_withdrawals_per_epoch() -> usize {
Self::MaxPartialWithdrawalsPerEpoch::to_usize()
}
/// Returns the `WITHDRAWAL_QUEUE_LIMIT` constant for this specification.
fn withdrawal_queue_limit() -> usize {
Self::WithdrawalQueueLimit::to_usize()
}
/// Returns the `MAX_BLS_TO_EXECUTION_CHANGES` constant for this specification.
fn max_bls_to_execution_changes() -> usize {
Self::MaxBlsToExecutionChanges::to_usize()
@@ -309,8 +297,6 @@ impl EthSpec for MainnetEthSpec {
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U4096; // 128 max attestations * 32 slots per epoch
type SlotsPerEth1VotingPeriod = U2048; // 64 epochs * 32 slots per epoch
type MaxPartialWithdrawalsPerEpoch = U256;
type WithdrawalQueueLimit = U1099511627776;
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
@@ -338,6 +324,7 @@ impl EthSpec for MinimalEthSpec {
type SyncSubcommitteeSize = U8; // 32 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U1024; // 128 max attestations * 8 slots per epoch
type SlotsPerEth1VotingPeriod = U32; // 4 epochs * 8 slots per epoch
type MaxWithdrawalsPerPayload = U4;
params_from_eth_spec!(MainnetEthSpec {
JustificationBitsLength,
@@ -358,10 +345,7 @@ impl EthSpec for MinimalEthSpec {
GasLimitDenominator,
MinGasLimit,
MaxExtraDataBytes,
MaxPartialWithdrawalsPerEpoch,
WithdrawalQueueLimit,
MaxBlsToExecutionChanges,
MaxWithdrawalsPerPayload,
MaxBlobsPerBlock,
FieldElementsPerBlob
});
@@ -408,8 +392,6 @@ impl EthSpec for GnosisEthSpec {
type SyncSubcommitteeSize = U128; // 512 committee size / 4 sync committee subnet count
type MaxPendingAttestations = U2048; // 128 max attestations * 16 slots per epoch
type SlotsPerEth1VotingPeriod = U1024; // 64 epochs * 16 slots per epoch
type MaxPartialWithdrawalsPerEpoch = U256;
type WithdrawalQueueLimit = U1099511627776;
type MaxBlsToExecutionChanges = U16;
type MaxWithdrawalsPerPayload = U16;
type MaxBlobsPerBlock = U16; // 2**4 = 16

View File

@@ -1,9 +1,8 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz::Encode;
use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use std::slice::Iter;
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
@@ -13,6 +12,8 @@ pub type Transactions<T> = VariableList<
<T as EthSpec>::MaxTransactionsPerPayload,
>;
pub type Withdrawals<T> = VariableList<Withdrawal, <T as EthSpec>::MaxWithdrawalsPerPayload>;
#[superstruct(
variants(Merge, Capella, Eip4844),
variant_attributes(
@@ -82,10 +83,21 @@ pub struct ExecutionPayload<T: EthSpec> {
pub transactions: Transactions<T>,
#[cfg(feature = "withdrawals")]
#[superstruct(only(Capella, Eip4844))]
pub withdrawals: VariableList<Withdrawal, T::MaxWithdrawalsPerPayload>,
pub withdrawals: Withdrawals<T>,
}
impl<T: EthSpec> ExecutionPayload<T> {
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayload: {fork_name}",
))),
ForkName::Merge => ExecutionPayloadMerge::from_ssz_bytes(bytes).map(Self::Merge),
ForkName::Capella => ExecutionPayloadCapella::from_ssz_bytes(bytes).map(Self::Capella),
ForkName::Eip4844 => ExecutionPayloadEip4844::from_ssz_bytes(bytes).map(Self::Eip4844),
}
}
#[allow(clippy::integer_arithmetic)]
/// Returns the maximum size of an execution payload.
pub fn max_execution_payload_merge_size() -> usize {

View File

@@ -1,6 +1,7 @@
use crate::{test_utils::TestRandom, *};
use derivative::Derivative;
use serde_derive::{Deserialize, Serialize};
use ssz::Decode;
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash::TreeHash;
@@ -84,31 +85,34 @@ impl<T: EthSpec> ExecutionPayloadHeader<T> {
pub fn transactions(&self) -> Option<&Transactions<T>> {
None
}
}
impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> {
// FIXME: maybe this could be a derived trait..
pub fn is_default(self) -> bool {
match self {
ExecutionPayloadHeaderRef::Merge(header) => {
*header == ExecutionPayloadHeaderMerge::default()
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair => Err(ssz::DecodeError::BytesInvalid(format!(
"unsupported fork for ExecutionPayloadHeader: {fork_name}",
))),
ForkName::Merge => ExecutionPayloadHeaderMerge::from_ssz_bytes(bytes).map(Self::Merge),
ForkName::Capella => {
ExecutionPayloadHeaderCapella::from_ssz_bytes(bytes).map(Self::Capella)
}
ExecutionPayloadHeaderRef::Capella(header) => {
*header == ExecutionPayloadHeaderCapella::default()
}
ExecutionPayloadHeaderRef::Eip4844(header) => {
*header == ExecutionPayloadHeaderEip4844::default()
ForkName::Eip4844 => {
ExecutionPayloadHeaderEip4844::from_ssz_bytes(bytes).map(Self::Eip4844)
}
}
}
}
impl<'a, T: EthSpec> ExecutionPayloadHeaderRef<'a, T> {
pub fn is_default(self) -> bool {
map_execution_payload_header_ref!(&'a _, self, |inner, cons| {
let _ = cons(inner);
*inner == Default::default()
})
}
}
impl<T: EthSpec> ExecutionPayloadHeaderMerge<T> {
pub fn upgrade_to_capella(&self) -> ExecutionPayloadHeaderCapella<T> {
#[cfg(feature = "withdrawals")]
// TODO: if this is correct we should calculate and hardcode this..
let empty_withdrawals_root =
VariableList::<Withdrawal, T::MaxWithdrawalsPerPayload>::empty().tree_hash_root();
ExecutionPayloadHeaderCapella {
parent_hash: self.parent_hash,
fee_recipient: self.fee_recipient,
@@ -125,8 +129,7 @@ impl<T: EthSpec> ExecutionPayloadHeaderMerge<T> {
block_hash: self.block_hash,
transactions_root: self.transactions_root,
#[cfg(feature = "withdrawals")]
// FIXME: the spec doesn't seem to define what to do here..
withdrawals_root: empty_withdrawals_root,
withdrawals_root: Hash256::zero(),
}
}
}

View File

@@ -14,7 +14,7 @@ pub struct KzgCommitment(#[serde(with = "BigArray")] pub [u8; 48]);
impl Display for KzgCommitment {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", eth2_serde_utils::hex::encode(&self.0))
write!(f, "{}", eth2_serde_utils::hex::encode(self.0))
}
}

View File

@@ -1,7 +1,6 @@
use crate::test_utils::{RngCore, TestRandom};
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use ssz::{Decode, DecodeError, Encode};
use ssz_derive::{Decode, Encode};
use std::fmt;
use tree_hash::{PackedEncoding, TreeHash};
@@ -15,7 +14,7 @@ pub struct KzgProof(#[serde(with = "BigArray")] pub [u8; KZG_PROOF_BYTES_LEN]);
impl fmt::Display for KzgProof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", eth2_serde_utils::hex::encode(&self.0))
write!(f, "{}", eth2_serde_utils::hex::encode(self.0))
}
}

View File

@@ -27,6 +27,7 @@ pub mod beacon_block_body;
pub mod beacon_block_header;
pub mod beacon_committee;
pub mod beacon_state;
pub mod bls_to_execution_change;
pub mod builder_bid;
pub mod chain_spec;
pub mod checkpoint;
@@ -61,6 +62,7 @@ pub mod shuffling_id;
pub mod signed_aggregate_and_proof;
pub mod signed_beacon_block;
pub mod signed_beacon_block_header;
pub mod signed_bls_to_execution_change;
pub mod signed_contribution_and_proof;
pub mod signed_voluntary_exit;
pub mod signing_data;
@@ -108,7 +110,7 @@ pub use crate::attestation_duty::AttestationDuty;
pub use crate::attester_slashing::AttesterSlashing;
pub use crate::beacon_block::{
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockCapella, BeaconBlockEip4844,
BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock,
BeaconBlockMerge, BeaconBlockRef, BeaconBlockRefMut, BlindedBeaconBlock, EmptyBlock,
};
pub use crate::beacon_block_body::{
BeaconBlockBody, BeaconBlockBodyAltair, BeaconBlockBodyBase, BeaconBlockBodyCapella,
@@ -118,6 +120,7 @@ pub use crate::beacon_block_header::BeaconBlockHeader;
pub use crate::beacon_committee::{BeaconCommittee, OwnedBeaconCommittee};
pub use crate::beacon_state::{BeaconTreeHashCache, Error as BeaconStateError, *};
pub use crate::blobs_sidecar::BlobsSidecar;
pub use crate::bls_to_execution_change::BlsToExecutionChange;
pub use crate::chain_spec::{ChainSpec, Config, Domain};
pub use crate::checkpoint::Checkpoint;
pub use crate::config_and_preset::{
@@ -134,7 +137,7 @@ pub use crate::eth_spec::EthSpecId;
pub use crate::execution_block_hash::ExecutionBlockHash;
pub use crate::execution_payload::{
ExecutionPayload, ExecutionPayloadCapella, ExecutionPayloadEip4844, ExecutionPayloadMerge,
ExecutionPayloadRef, Transaction, Transactions,
ExecutionPayloadRef, Transaction, Transactions, Withdrawals,
};
pub use crate::execution_payload_header::{
ExecutionPayloadHeader, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderEip4844,
@@ -171,6 +174,7 @@ pub use crate::signed_beacon_block::{
SignedBlindedBeaconBlock,
};
pub use crate::signed_beacon_block_header::SignedBeaconBlockHeader;
pub use crate::signed_bls_to_execution_change::SignedBlsToExecutionChange;
pub use crate::signed_block_and_blobs::SignedBeaconBlockAndBlobsSidecar;
pub use crate::signed_contribution_and_proof::SignedContributionAndProof;
pub use crate::signed_voluntary_exit::SignedVoluntaryExit;

View File

@@ -36,6 +36,9 @@ pub trait ExecPayload<T: EthSpec>: Debug + Clone + PartialEq + Hash + TreeHash +
fn fee_recipient(&self) -> Address;
fn gas_limit(&self) -> u64;
fn transactions(&self) -> Option<&Transactions<T>>;
/// fork-specific fields
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error>;
/// Is this a default payload? (pre-merge)
fn is_default(&self) -> bool;
@@ -218,13 +221,26 @@ impl<T: EthSpec> ExecPayload<T> for FullPayload<T> {
})
}
fn transactions<'a>(&'a self) -> Option<&Transactions<T>> {
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
Some(&payload.execution_payload.transactions)
})
}
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error> {
match self {
FullPayload::Merge(_) => Err(Error::IncorrectStateVariant),
FullPayload::Capella(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayload::Eip4844(ref inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
}
}
fn is_default<'a>(&'a self) -> bool {
map_full_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
@@ -249,7 +265,7 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
fn to_execution_payload_header<'a>(&'a self) -> ExecutionPayloadHeader<T> {
map_full_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
ExecutionPayloadHeader::from(payload.to_execution_payload_header())
payload.to_execution_payload_header()
})
}
@@ -302,13 +318,26 @@ impl<'b, T: EthSpec> ExecPayload<T> for FullPayloadRef<'b, T> {
})
}
fn transactions<'a>(&'a self) -> Option<&Transactions<T>> {
fn transactions<'a>(&'a self) -> Option<&'a Transactions<T>> {
map_full_payload_ref!(&'a _, self, move |payload, cons| {
cons(payload);
Some(&payload.execution_payload.transactions)
})
}
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error> {
match self {
FullPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant),
FullPayloadRef::Capella(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
FullPayloadRef::Eip4844(inner) => {
Ok(inner.execution_payload.withdrawals.tree_hash_root())
}
}
}
// TODO: can this function be optimized?
fn is_default<'a>(&'a self) -> bool {
map_full_payload_ref!(&'a _, self, move |payload, cons| {
@@ -459,10 +488,23 @@ impl<T: EthSpec> ExecPayload<T> for BlindedPayload<T> {
})
}
fn transactions<'a>(&'a self) -> Option<&Transactions<T>> {
fn transactions(&self) -> Option<&Transactions<T>> {
None
}
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error> {
match self {
BlindedPayload::Merge(_) => Err(Error::IncorrectStateVariant),
BlindedPayload::Capella(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayload::Eip4844(ref inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
}
}
fn is_default<'a>(&'a self) -> bool {
map_blinded_payload_ref!(&'a _, self.to_ref(), move |payload, cons| {
cons(payload);
@@ -532,10 +574,23 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
})
}
fn transactions<'a>(&'a self) -> Option<&Transactions<T>> {
fn transactions(&self) -> Option<&Transactions<T>> {
None
}
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error> {
match self {
BlindedPayloadRef::Merge(_) => Err(Error::IncorrectStateVariant),
BlindedPayloadRef::Capella(inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
BlindedPayloadRef::Eip4844(inner) => {
Ok(inner.execution_payload_header.withdrawals_root)
}
}
}
// TODO: can this function be optimized?
fn is_default<'a>(&'a self) -> bool {
map_blinded_payload_ref!(&'a _, self, move |payload, cons| {
@@ -546,7 +601,7 @@ impl<'b, T: EthSpec> ExecPayload<T> for BlindedPayloadRef<'b, T> {
}
macro_rules! impl_exec_payload_common {
($wrapper_type:ident, $wrapped_type_full:ident, $wrapped_header_type:ident, $wrapped_field:ident, $fork_variant:ident, $block_type_variant:ident, $f:block) => {
($wrapper_type:ident, $wrapped_type_full:ident, $wrapped_header_type:ident, $wrapped_field:ident, $fork_variant:ident, $block_type_variant:ident, $f:block, $g:block) => {
impl<T: EthSpec> ExecPayload<T> for $wrapper_type<T> {
fn block_type() -> BlockType {
BlockType::$block_type_variant
@@ -594,6 +649,12 @@ macro_rules! impl_exec_payload_common {
let f = $f;
f(self)
}
#[cfg(feature = "withdrawals")]
fn withdrawals_root(&self) -> Result<Hash256, Error> {
let g = $g;
g(self)
}
}
impl<T: EthSpec> From<$wrapped_type_full<T>> for $wrapper_type<T> {
@@ -615,7 +676,15 @@ macro_rules! impl_exec_payload_for_fork {
execution_payload_header,
$fork_variant,
Blinded,
{ |_| { None } }
{ |_| { None } },
{
let c: for<'a> fn(&'a $wrapper_type_header<T>) -> Result<Hash256, Error> =
|payload: &$wrapper_type_header<T>| {
let wrapper_ref_type = BlindedPayloadRef::$fork_variant(&payload);
wrapper_ref_type.withdrawals_root()
};
c
}
);
impl<T: EthSpec> TryInto<$wrapper_type_header<T>> for BlindedPayload<T> {
@@ -680,6 +749,14 @@ macro_rules! impl_exec_payload_for_fork {
let c: for<'a> fn(&'a $wrapper_type_full<T>) -> Option<&'a Transactions<T>> =
|payload: &$wrapper_type_full<T>| Some(&payload.execution_payload.transactions);
c
},
{
let c: for<'a> fn(&'a $wrapper_type_full<T>) -> Result<Hash256, Error> =
|payload: &$wrapper_type_full<T>| {
let wrapper_ref_type = FullPayloadRef::$fork_variant(&payload);
wrapper_ref_type.withdrawals_root()
};
c
}
);

View File

@@ -341,6 +341,8 @@ impl<E: EthSpec> SignedBeaconBlockCapella<E, BlindedPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadCapella { .. },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
},
},
signature,
@@ -362,6 +364,8 @@ impl<E: EthSpec> SignedBeaconBlockCapella<E, BlindedPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadCapella { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
},
},
signature,
@@ -393,6 +397,8 @@ impl<E: EthSpec> SignedBeaconBlockEip4844<E, BlindedPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: BlindedPayloadEip4844 { .. },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
blob_kzg_commitments,
},
},
@@ -415,6 +421,8 @@ impl<E: EthSpec> SignedBeaconBlockEip4844<E, BlindedPayload<E>> {
voluntary_exits,
sync_aggregate,
execution_payload: FullPayloadEip4844 { execution_payload },
#[cfg(feature = "withdrawals")]
bls_to_execution_changes,
blob_kzg_commitments,
},
},

View File

@@ -0,0 +1,26 @@
use crate::test_utils::TestRandom;
use crate::*;
use bls::Signature;
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom;
use tree_hash_derive::TreeHash;
/// A deposit to potentially become a beacon chain validator.
///
/// Spec v0.12.1
#[cfg_attr(feature = "arbitrary-fuzz", derive(arbitrary::Arbitrary))]
#[derive(
Debug, PartialEq, Hash, Clone, Serialize, Deserialize, Encode, Decode, TreeHash, TestRandom,
)]
pub struct SignedBlsToExecutionChange {
pub message: BlsToExecutionChange,
pub signature: Signature,
}
#[cfg(test)]
mod tests {
use super::*;
ssz_and_tree_hash_tests!(SignedBlsToExecutionChange);
}

View File

@@ -17,7 +17,7 @@ impl CachedTreeHash<TreeHashCache> for Validator {
/// Efficiently tree hash a `Validator`, assuming it was updated by a valid state transition.
///
/// Specifically, we assume that the `pubkey` and `withdrawal_credentials` fields are constant.
/// Specifically, we assume that the `pubkey` field is constant.
fn recalculate_tree_hash_root(
&self,
arena: &mut CacheArena,
@@ -29,8 +29,8 @@ impl CachedTreeHash<TreeHashCache> for Validator {
.iter_mut(arena)?
.enumerate()
.flat_map(|(i, leaf)| {
// Fields pubkey and withdrawal_credentials are constant
if (i == 0 || i == 1) && cache.initialized {
// Pubkey field (index 0) is constant.
if i == 0 && cache.initialized {
None
} else if process_field_by_index(self, i, leaf, !cache.initialized) {
Some(i)

View File

@@ -1,5 +1,6 @@
use crate::{
test_utils::TestRandom, BeaconState, ChainSpec, Epoch, EthSpec, Hash256, PublicKeyBytes,
test_utils::TestRandom, Address, BeaconState, ChainSpec, Epoch, EthSpec, Hash256,
PublicKeyBytes,
};
use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
@@ -75,6 +76,28 @@ impl Validator {
.unwrap_or(false)
}
/// Get the eth1 withdrawal address if this validator has one initialized.
pub fn get_eth1_withdrawal_address(&self, spec: &ChainSpec) -> Option<Address> {
self.has_eth1_withdrawal_credential(spec)
.then(|| {
self.withdrawal_credentials
.as_bytes()
.get(12..)
.map(Address::from_slice)
})
.flatten()
}
/// Changes withdrawal credentials to the provided eth1 execution address
///
/// WARNING: this function does NO VALIDATION - it just does it!
pub fn change_withdrawal_credentials(&mut self, execution_address: &Address, spec: &ChainSpec) {
let mut bytes = [0u8; 32];
bytes[0] = spec.eth1_address_withdrawal_prefix_byte;
bytes[12..].copy_from_slice(execution_address.as_bytes());
self.withdrawal_credentials = Hash256::from(bytes);
}
/// Returns `true` if the validator is fully withdrawable at some epoch
pub fn is_fully_withdrawable_at(&self, balance: u64, epoch: Epoch, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec) && self.withdrawable_epoch <= epoch && balance > 0