diff --git a/beacon_node/store/src/partial_beacon_state.rs b/beacon_node/store/src/partial_beacon_state.rs index 8a22fd1e42..810027e02a 100644 --- a/beacon_node/store/src/partial_beacon_state.rs +++ b/beacon_node/store/src/partial_beacon_state.rs @@ -144,6 +144,13 @@ where List, #[superstruct(only(Electra, EIP7732))] pub pending_consolidations: List, + + #[superstruct(only(EIP7732))] + pub latest_block_hash: ExecutionBlockHash, + #[superstruct(only(EIP7732))] + pub latest_full_slot: Slot, + #[superstruct(only(EIP7732))] + pub latest_withdrawals_root: Hash256, } impl PartialBeaconState { @@ -432,7 +439,10 @@ impl TryInto> for PartialBeaconState { earliest_consolidation_epoch, pending_balance_deposits, pending_partial_withdrawals, - pending_consolidations + pending_consolidations, + latest_block_hash, + latest_full_slot, + latest_withdrawals_root ], [historical_summaries] ), diff --git a/consensus/state_processing/src/genesis.rs b/consensus/state_processing/src/genesis.rs index 00697def5d..62711de29c 100644 --- a/consensus/state_processing/src/genesis.rs +++ b/consensus/state_processing/src/genesis.rs @@ -5,6 +5,7 @@ use crate::common::DepositDataTree; use crate::upgrade::electra::upgrade_state_to_electra; use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, + upgrade_to_eip7732, }; use safe_arith::{ArithError, SafeArith}; use std::sync::Arc; @@ -140,6 +141,20 @@ pub fn initialize_beacon_state_from_eth1( } } + // Upgrade to EIP-7732 if configured from genesis. + if spec + .eip7732_fork_epoch + .map_or(false, |fork_epoch| fork_epoch == E::genesis_epoch()) + { + upgrade_to_eip7732(&mut state, spec)?; + + // Remove intermediate Electra fork from `state.fork`. + state.fork_mut().previous_version = spec.eip7732_fork_version; + + // Here's where we *would* clone the header but there is no header here so.. + // TODO(EIP7732): check this + } + // Now that we have our validators, initialize the caches (including the committees) state.build_caches(spec)?; diff --git a/consensus/state_processing/src/per_slot_processing.rs b/consensus/state_processing/src/per_slot_processing.rs index 6554423199..41574ea97e 100644 --- a/consensus/state_processing/src/per_slot_processing.rs +++ b/consensus/state_processing/src/per_slot_processing.rs @@ -1,6 +1,6 @@ use crate::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, - upgrade_to_electra, + upgrade_to_eip7732, upgrade_to_electra, }; use crate::{per_epoch_processing::EpochProcessingSummary, *}; use safe_arith::{ArithError, SafeArith}; @@ -70,6 +70,10 @@ pub fn per_slot_processing( if spec.electra_fork_epoch == Some(state.current_epoch()) { upgrade_to_electra(state, spec)?; } + // EIP-7732. + if spec.eip7732_fork_epoch == Some(state.current_epoch()) { + upgrade_to_eip7732(state, spec)?; + } // Additionally build all caches so that all valid states that are advanced always have // committee caches built, and we don't have to worry about initialising them at higher diff --git a/consensus/state_processing/src/upgrade.rs b/consensus/state_processing/src/upgrade.rs index 93cafa73d0..5b49bade1b 100644 --- a/consensus/state_processing/src/upgrade.rs +++ b/consensus/state_processing/src/upgrade.rs @@ -2,10 +2,12 @@ pub mod altair; pub mod bellatrix; pub mod capella; pub mod deneb; +pub mod eip7732; pub mod electra; pub use altair::upgrade_to_altair; pub use bellatrix::upgrade_to_bellatrix; pub use capella::upgrade_to_capella; pub use deneb::upgrade_to_deneb; +pub use eip7732::upgrade_to_eip7732; pub use electra::upgrade_to_electra; diff --git a/consensus/state_processing/src/upgrade/eip7732.rs b/consensus/state_processing/src/upgrade/eip7732.rs new file mode 100644 index 0000000000..6a951d48b8 --- /dev/null +++ b/consensus/state_processing/src/upgrade/eip7732.rs @@ -0,0 +1,96 @@ +use bls::Hash256; +use std::mem; +use types::{ + BeaconState, BeaconStateEIP7732, BeaconStateError as Error, ChainSpec, EpochCache, EthSpec, + ExecutionBid, Fork, +}; + +/// Transform an `Electra` state into an `EIP-7732` state. +pub fn upgrade_to_eip7732( + pre_state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let epoch = pre_state.current_epoch(); + let pre = pre_state.as_electra_mut()?; + + let previous_fork_version = pre.fork.current_version; + + // Where possible, use something like `mem::take` to move fields from behind the &mut + // reference. For other fields that don't have a good default value, use `clone`. + // + // Fixed size vectors get cloned because replacing them would require the same size + // allocation as cloning. + let post = BeaconState::EIP7732(BeaconStateEIP7732 { + // Versioning + genesis_time: pre.genesis_time, + genesis_validators_root: pre.genesis_validators_root, + slot: pre.slot, + fork: Fork { + previous_version: previous_fork_version, + current_version: spec.eip7732_fork_version, + epoch, + }, + // History + latest_block_header: pre.latest_block_header.clone(), + block_roots: pre.block_roots.clone(), + state_roots: pre.state_roots.clone(), + historical_roots: mem::take(&mut pre.historical_roots), + // Eth1 + eth1_data: pre.eth1_data.clone(), + eth1_data_votes: mem::take(&mut pre.eth1_data_votes), + eth1_deposit_index: pre.eth1_deposit_index, + // Registry + validators: mem::take(&mut pre.validators), + balances: mem::take(&mut pre.balances), + // Randomness + randao_mixes: pre.randao_mixes.clone(), + // Slashings + slashings: pre.slashings.clone(), + // `Participation + previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation), + current_epoch_participation: mem::take(&mut pre.current_epoch_participation), + // Finality + justification_bits: pre.justification_bits.clone(), + previous_justified_checkpoint: pre.previous_justified_checkpoint, + current_justified_checkpoint: pre.current_justified_checkpoint, + finalized_checkpoint: pre.finalized_checkpoint, + // Inactivity + inactivity_scores: mem::take(&mut pre.inactivity_scores), + // Sync committees + current_sync_committee: pre.current_sync_committee.clone(), + next_sync_committee: pre.next_sync_committee.clone(), + // Execution Bid + latest_execution_bid: ExecutionBid::default(), + // Capella + next_withdrawal_index: pre.next_withdrawal_index, + next_withdrawal_validator_index: pre.next_withdrawal_validator_index, + historical_summaries: pre.historical_summaries.clone(), + // Deneb + // Electra + deposit_requests_start_index: pre.deposit_requests_start_index, + deposit_balance_to_consume: pre.deposit_balance_to_consume, + exit_balance_to_consume: pre.exit_balance_to_consume, + earliest_exit_epoch: pre.earliest_exit_epoch, + consolidation_balance_to_consume: pre.consolidation_balance_to_consume, + earliest_consolidation_epoch: pre.earliest_consolidation_epoch, + pending_balance_deposits: mem::take(&mut pre.pending_balance_deposits), + pending_partial_withdrawals: mem::take(&mut pre.pending_partial_withdrawals), + pending_consolidations: mem::take(&mut pre.pending_consolidations), + // EIP-7732 + latest_block_hash: pre.latest_execution_payload_header.block_hash, + latest_full_slot: pre.slot, + latest_withdrawals_root: Hash256::default(), + // Caches + total_active_balance: pre.total_active_balance, + progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache), + committee_caches: mem::take(&mut pre.committee_caches), + pubkey_cache: mem::take(&mut pre.pubkey_cache), + exit_cache: mem::take(&mut pre.exit_cache), + slashings_cache: mem::take(&mut pre.slashings_cache), + epoch_cache: EpochCache::default(), + }); + + *pre_state = post; + + Ok(()) +} diff --git a/consensus/types/src/beacon_block.rs b/consensus/types/src/beacon_block.rs index d8b1277fb7..e4aa4faf31 100644 --- a/consensus/types/src/beacon_block.rs +++ b/consensus/types/src/beacon_block.rs @@ -734,6 +734,7 @@ impl> BeaconBlockEIP7732 deposits: base_block.body.deposits, voluntary_exits: base_block.body.voluntary_exits, bls_to_execution_changes, + signed_execution_bid: SignedExecutionBid::empty(), sync_aggregate, randao_reveal: Signature::empty(), eth1_data: Eth1Data { @@ -802,6 +803,7 @@ impl> EmptyBlock for BeaconBlockEIP7 voluntary_exits: VariableList::empty(), sync_aggregate: SyncAggregate::empty(), bls_to_execution_changes: VariableList::empty(), + signed_execution_bid: SignedExecutionBid::empty(), _phantom: PhantomData, }, } diff --git a/consensus/types/src/beacon_block_body.rs b/consensus/types/src/beacon_block_body.rs index e97341acec..de938f5f8e 100644 --- a/consensus/types/src/beacon_block_body.rs +++ b/consensus/types/src/beacon_block_body.rs @@ -123,6 +123,8 @@ pub struct BeaconBlockBody = FullPay pub blob_kzg_commitments: KzgCommitments, #[superstruct(only(Electra))] pub execution_requests: ExecutionRequests, + #[superstruct(only(EIP7732))] + pub signed_execution_bid: SignedExecutionBid, #[superstruct(only(Base, Altair, EIP7732))] #[metastruct(exclude_from(fields))] #[ssz(skip_serializing, skip_deserializing)] @@ -745,6 +747,7 @@ impl From>> voluntary_exits, sync_aggregate, bls_to_execution_changes, + signed_execution_bid, _phantom, } = body; @@ -759,6 +762,7 @@ impl From>> voluntary_exits, sync_aggregate, bls_to_execution_changes, + signed_execution_bid, _phantom: PhantomData, } } @@ -782,6 +786,7 @@ impl From>> voluntary_exits, sync_aggregate, bls_to_execution_changes, + signed_execution_bid, _phantom, } = body; @@ -797,6 +802,7 @@ impl From>> voluntary_exits, sync_aggregate, bls_to_execution_changes, + signed_execution_bid, _phantom: PhantomData, }, None, diff --git a/consensus/types/src/beacon_state.rs b/consensus/types/src/beacon_state.rs index d62f47c380..66af3a74fa 100644 --- a/consensus/types/src/beacon_state.rs +++ b/consensus/types/src/beacon_state.rs @@ -537,6 +537,17 @@ where #[superstruct(only(Electra, EIP7732))] pub pending_consolidations: List, + // EIP-7732 + #[superstruct(only(EIP7732), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + pub latest_block_hash: ExecutionBlockHash, + #[superstruct(only(EIP7732), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + pub latest_full_slot: Slot, + #[superstruct(only(EIP7732), partial_getter(copy))] + #[metastruct(exclude_from(tree_lists))] + pub latest_withdrawals_root: Hash256, + // Caching (not in the spec) #[serde(skip_serializing, skip_deserializing)] #[ssz(skip_serializing, skip_deserializing)] diff --git a/consensus/types/src/execution_bid.rs b/consensus/types/src/execution_bid.rs index 0994079557..feceb4dbd1 100644 --- a/consensus/types/src/execution_bid.rs +++ b/consensus/types/src/execution_bid.rs @@ -7,9 +7,9 @@ use tree_hash_derive::TreeHash; #[derive( arbitrary::Arbitrary, + Default, Debug, Clone, - PartialEq, Serialize, Encode, Decode, @@ -18,6 +18,7 @@ use tree_hash_derive::TreeHash; Derivative, TestRandom, )] +#[derivative(PartialEq, Hash)] // This is what Potuz' spec calls an `ExecutionPayload` even though it's clearly a bid. pub struct ExecutionBid { parent_block_hash: ExecutionBlockHash, diff --git a/consensus/types/src/signed_execution_bid.rs b/consensus/types/src/signed_execution_bid.rs index 04fef02c4d..88ce762572 100644 --- a/consensus/types/src/signed_execution_bid.rs +++ b/consensus/types/src/signed_execution_bid.rs @@ -1,5 +1,6 @@ use crate::test_utils::TestRandom; use crate::*; +use derivative::Derivative; use serde::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; @@ -11,17 +12,27 @@ use tree_hash_derive::TreeHash; TreeHash, Debug, Clone, - PartialEq, Encode, Decode, Serialize, Deserialize, + Derivative, )] +#[derivative(PartialEq, Hash)] pub struct SignedExecutionBid { pub message: ExecutionBid, pub signature: Signature, } +impl SignedExecutionBid { + pub fn empty() -> Self { + Self { + message: ExecutionBid::default(), + signature: Signature::empty(), + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/testing/ef_tests/src/cases/fork.rs b/testing/ef_tests/src/cases/fork.rs index f5b25793b0..004ca7bb93 100644 --- a/testing/ef_tests/src/cases/fork.rs +++ b/testing/ef_tests/src/cases/fork.rs @@ -4,7 +4,7 @@ use crate::decode::{ssz_decode_state, yaml_decode_file}; use serde::Deserialize; use state_processing::upgrade::{ upgrade_to_altair, upgrade_to_bellatrix, upgrade_to_capella, upgrade_to_deneb, - upgrade_to_electra, + upgrade_to_eip7732, upgrade_to_electra, }; use types::BeaconState; @@ -71,7 +71,7 @@ impl Case for ForkTest { ForkName::Capella => upgrade_to_capella(&mut result_state, spec).map(|_| result_state), ForkName::Deneb => upgrade_to_deneb(&mut result_state, spec).map(|_| result_state), ForkName::Electra => upgrade_to_electra(&mut result_state, spec).map(|_| result_state), - ForkName::EIP7732 => todo!("upgrade_to_eip7732 not yet implemented"), + ForkName::EIP7732 => upgrade_to_eip7732(&mut result_state, spec).map(|_| result_state), }; compare_beacon_state_results_without_caches(&mut result, &mut expected)