diff --git a/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs index 7ed9934f039a..92c517507139 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs @@ -18,11 +18,13 @@ use frame_support::{migrations::SteppedMigration, weights::WeightMeter}; use frame_system::RawOrigin; use hex_literal::hex; +use snowbridge_beacon_primitives::Fork; use snowbridge_pallet_ethereum_client_fixtures::*; use snowbridge_beacon_primitives::{ - fast_aggregate_verify, prepare_aggregate_pubkey, prepare_aggregate_signature, - verify_merkle_branch, + fast_aggregate_verify, + merkle_proof::{generalized_index_length, subtree_index}, + prepare_aggregate_pubkey, prepare_aggregate_signature, verify_merkle_branch, }; use util::*; @@ -122,13 +124,43 @@ mod benchmarks { let update = make_sync_committee_update(); let block_root: H256 = update.finalized_header.hash_tree_root().unwrap(); + let fork_versions = ForkVersions { + genesis: Fork { + version: [0, 0, 0, 0], // 0x00000000 + epoch: 0, + }, + altair: Fork { + version: [1, 0, 0, 0], // 0x01000000 + epoch: 0, + }, + bellatrix: Fork { + version: [2, 0, 0, 0], // 0x02000000 + epoch: 0, + }, + capella: Fork { + version: [3, 0, 0, 0], // 0x03000000 + epoch: 0, + }, + deneb: Fork { + version: [4, 0, 0, 0], // 0x04000000 + epoch: 0, + }, + electra: Fork { + version: [5, 0, 0, 0], // 0x05000000 + epoch: 80000000000, + }, + }; + let finalized_root_g_index = EthereumBeaconClient::::finalized_root_gindex_at_slot( + update.attested_header.slot, + fork_versions, + ); #[block] { verify_merkle_branch( block_root, &update.finality_branch, - config::FINALIZED_ROOT_SUBTREE_INDEX, - config::FINALIZED_ROOT_DEPTH, + subtree_index(finalized_root_g_index), + generalized_index_length(finalized_root_g_index), update.attested_header.state_root, ); } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/config/altair.rs b/bridges/snowbridge/pallets/ethereum-client/src/config/altair.rs new file mode 100644 index 000000000000..696caf498d2c --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/src/config/altair.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +/// Generalized Indices +/// related to Merkle proofs +/// get_generalized_index(BeaconState, 'block_roots') +pub const BLOCK_ROOTS_INDEX: usize = 37; +/// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') +pub const FINALIZED_ROOT_INDEX: usize = 105; +/// get_generalized_index(BeaconState, 'current_sync_committee') +pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; +/// get_generalized_index(BeaconState, 'next_sync_committee') +pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; +/// get_generalized_index(BeaconBlockBody, 'execution_payload') +pub const EXECUTION_HEADER_INDEX: usize = 25; diff --git a/bridges/snowbridge/pallets/ethereum-client/src/config/electra.rs b/bridges/snowbridge/pallets/ethereum-client/src/config/electra.rs new file mode 100644 index 000000000000..18b92b5229b3 --- /dev/null +++ b/bridges/snowbridge/pallets/ethereum-client/src/config/electra.rs @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork + +/// Generalized Indices +/// related to Merkle proofs +/// get_generalized_index(BeaconState, 'block_roots') +pub const BLOCK_ROOTS_INDEX: usize = 69; +/// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') +pub const FINALIZED_ROOT_INDEX: usize = 169; +/// get_generalized_index(BeaconState, 'current_sync_committee') +pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 86; +/// get_generalized_index(BeaconState, 'next_sync_committee') +pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 87; +/// get_generalized_index(BeaconBlockBody, 'execution_payload') +pub const EXECUTION_HEADER_INDEX: usize = 25; diff --git a/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs b/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs index 1ab1f67d6397..bd6a1141c07a 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/config/mod.rs @@ -1,35 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork -use snowbridge_beacon_primitives::merkle_proof::{generalized_index_length, subtree_index}; use static_assertions::const_assert; -/// Generalized Indices -/// related to Merkle proofs -/// get_generalized_index(BeaconState, 'block_roots') -pub const BLOCK_ROOTS_INDEX: usize = 37; -pub const BLOCK_ROOTS_SUBTREE_INDEX: usize = subtree_index(BLOCK_ROOTS_INDEX); -pub const BLOCK_ROOTS_DEPTH: usize = generalized_index_length(BLOCK_ROOTS_INDEX); - -/// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root') -pub const FINALIZED_ROOT_INDEX: usize = 105; -pub const FINALIZED_ROOT_SUBTREE_INDEX: usize = subtree_index(FINALIZED_ROOT_INDEX); -pub const FINALIZED_ROOT_DEPTH: usize = generalized_index_length(FINALIZED_ROOT_INDEX); - -/// get_generalized_index(BeaconState, 'current_sync_committee') -pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; -pub const CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(CURRENT_SYNC_COMMITTEE_INDEX); -pub const CURRENT_SYNC_COMMITTEE_DEPTH: usize = - generalized_index_length(CURRENT_SYNC_COMMITTEE_INDEX); - -/// get_generalized_index(BeaconState, 'next_sync_committee') -pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; -pub const NEXT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(NEXT_SYNC_COMMITTEE_INDEX); -pub const NEXT_SYNC_COMMITTEE_DEPTH: usize = generalized_index_length(NEXT_SYNC_COMMITTEE_INDEX); - -/// get_generalized_index(BeaconBlockBody, 'execution_payload') -pub const EXECUTION_HEADER_INDEX: usize = 25; -pub const EXECUTION_HEADER_SUBTREE_INDEX: usize = subtree_index(EXECUTION_HEADER_INDEX); -pub const EXECUTION_HEADER_DEPTH: usize = generalized_index_length(EXECUTION_HEADER_INDEX); +pub mod altair; +pub mod electra; /// Sizes related to SSZ encoding pub const MAX_EXTRA_DATA_BYTES: usize = 32; diff --git a/bridges/snowbridge/pallets/ethereum-client/src/impls.rs b/bridges/snowbridge/pallets/ethereum-client/src/impls.rs index a97d416809c4..deeea8709185 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/impls.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/impls.rs @@ -4,6 +4,7 @@ use super::*; use frame_support::ensure; use snowbridge_beacon_primitives::ExecutionProof; +use snowbridge_beacon_primitives::merkle_proof::{generalized_index_length, subtree_index}; use snowbridge_core::inbound::{ VerificationError::{self, *}, *, @@ -115,12 +116,16 @@ impl Pallet { .hash_tree_root() .map_err(|_| Error::::BlockBodyHashTreeRootFailed)?; + let execution_header_g_index = Self::execution_header_gindex_at_slot( + execution_proof.header.slot, + T::ForkVersions::get(), + ); ensure!( verify_merkle_branch( execution_header_root, &execution_proof.execution_branch, - config::EXECUTION_HEADER_SUBTREE_INDEX, - config::EXECUTION_HEADER_DEPTH, + subtree_index(execution_header_g_index), + generalized_index_length(execution_header_g_index), execution_proof.header.body_root ), Error::::InvalidExecutionHeaderProof diff --git a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs index 09c0bb54d8db..398f3ee5aa87 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/lib.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/lib.rs @@ -41,8 +41,10 @@ use frame_support::{ }; use frame_system::ensure_signed; use snowbridge_beacon_primitives::{ - fast_aggregate_verify, verify_merkle_branch, verify_receipt_proof, BeaconHeader, BlsError, - CompactBeaconState, ForkData, ForkVersion, ForkVersions, PublicKeyPrepared, SigningData, + fast_aggregate_verify, + merkle_proof::{generalized_index_length, subtree_index}, + verify_merkle_branch, verify_receipt_proof, BeaconHeader, BlsError, CompactBeaconState, + ForkData, ForkVersion, ForkVersions, PublicKeyPrepared, SigningData, }; use snowbridge_core::{BasicOperatingMode, RingBufferMap}; use sp_core::H256; @@ -242,13 +244,18 @@ pub mod pallet { .hash_tree_root() .map_err(|_| Error::::SyncCommitteeHashTreeRootFailed)?; + let fork_versions = T::ForkVersions::get(); + let sync_committee_g_index = Self::current_sync_committee_gindex_at_slot( + update.header.slot, + fork_versions.clone(), + ); // Verifies the sync committee in the Beacon state. ensure!( verify_merkle_branch( sync_committee_root, &update.current_sync_committee_branch, - config::CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX, - config::CURRENT_SYNC_COMMITTEE_DEPTH, + subtree_index(sync_committee_g_index), + generalized_index_length(sync_committee_g_index), update.header.state_root ), Error::::InvalidSyncCommitteeMerkleProof @@ -262,12 +269,14 @@ pub mod pallet { // This is used for ancestry proofs in ExecutionHeader updates. This verifies the // BeaconState: the beacon state root is the tree root; the `block_roots` hash is the // tree leaf. + let block_roots_g_index = + Self::block_roots_gindex_at_slot(update.header.slot, fork_versions); ensure!( verify_merkle_branch( update.block_roots_root, &update.block_roots_branch, - config::BLOCK_ROOTS_SUBTREE_INDEX, - config::BLOCK_ROOTS_DEPTH, + subtree_index(block_roots_g_index), + generalized_index_length(block_roots_g_index), update.header.state_root ), Error::::InvalidBlockRootsRootMerkleProof @@ -345,8 +354,13 @@ pub mod pallet { Error::::InvalidFinalizedHeaderGap ); - // Verify that the `finality_branch`, if present, confirms `finalized_header` to match - // the finalized checkpoint root saved in the state of `attested_header`. + let fork_versions = T::ForkVersions::get(); + let finalized_root_g_index = Self::finalized_root_gindex_at_slot( + update.attested_header.slot, + fork_versions.clone(), + ); // TODO check attested / finalized header slot + // Verify that the `finality_branch`, if present, confirms `finalized_header` to match + // the finalized checkpoint root saved in the state of `attested_header`. let finalized_block_root: H256 = update .finalized_header .hash_tree_root() @@ -355,8 +369,8 @@ pub mod pallet { verify_merkle_branch( finalized_block_root, &update.finality_branch, - config::FINALIZED_ROOT_SUBTREE_INDEX, - config::FINALIZED_ROOT_DEPTH, + subtree_index(finalized_root_g_index), + generalized_index_length(finalized_root_g_index), update.attested_header.state_root ), Error::::InvalidHeaderMerkleProof @@ -365,12 +379,16 @@ pub mod pallet { // Though following check does not belong to ALC spec we verify block_roots_root to // match the finalized checkpoint root saved in the state of `finalized_header` so to // cache it for later use in `verify_ancestry_proof`. + let block_roots_g_index = Self::block_roots_gindex_at_slot( + update.finalized_header.slot, + fork_versions.clone(), + ); ensure!( verify_merkle_branch( update.block_roots_root, &update.block_roots_branch, - config::BLOCK_ROOTS_SUBTREE_INDEX, - config::BLOCK_ROOTS_DEPTH, + subtree_index(block_roots_g_index), + generalized_index_length(block_roots_g_index), update.finalized_header.state_root ), Error::::InvalidBlockRootsRootMerkleProof @@ -390,12 +408,16 @@ pub mod pallet { Error::::InvalidSyncCommitteeUpdate ); } + let next_sync_committee_g_index = Self::next_sync_committee_gindex_at_slot( + update.attested_header.slot, + fork_versions, + ); ensure!( verify_merkle_branch( sync_committee_root, &next_sync_committee_update.next_sync_committee_branch, - config::NEXT_SYNC_COMMITTEE_SUBTREE_INDEX, - config::NEXT_SYNC_COMMITTEE_DEPTH, + subtree_index(next_sync_committee_g_index), + generalized_index_length(next_sync_committee_g_index), update.attested_header.state_root ), Error::::InvalidSyncCommitteeMerkleProof @@ -599,6 +621,9 @@ pub mod pallet { /// Returns the fork version based on the current epoch. pub(super) fn select_fork_version(fork_versions: &ForkVersions, epoch: u64) -> ForkVersion { + if epoch >= fork_versions.electra.epoch { + return fork_versions.electra.version + } if epoch >= fork_versions.deneb.epoch { return fork_versions.deneb.version } @@ -670,5 +695,58 @@ pub mod pallet { Pays::Yes } + + pub fn finalized_root_gindex_at_slot(slot: u64, fork_versions: ForkVersions) -> usize { + let epoch = compute_epoch(slot, config::SLOTS_PER_EPOCH as u64); + + if epoch >= fork_versions.electra.epoch { + return config::electra::FINALIZED_ROOT_INDEX; + } + + config::altair::FINALIZED_ROOT_INDEX + } + + pub fn current_sync_committee_gindex_at_slot( + slot: u64, + fork_versions: ForkVersions, + ) -> usize { + let epoch = compute_epoch(slot, config::SLOTS_PER_EPOCH as u64); + + if epoch >= fork_versions.electra.epoch { + return config::electra::CURRENT_SYNC_COMMITTEE_INDEX; + } + + config::altair::CURRENT_SYNC_COMMITTEE_INDEX + } + + pub fn next_sync_committee_gindex_at_slot(slot: u64, fork_versions: ForkVersions) -> usize { + let epoch = compute_epoch(slot, config::SLOTS_PER_EPOCH as u64); + + if epoch >= fork_versions.electra.epoch { + return config::electra::NEXT_SYNC_COMMITTEE_INDEX; + } + + config::altair::NEXT_SYNC_COMMITTEE_INDEX + } + + pub fn block_roots_gindex_at_slot(slot: u64, fork_versions: ForkVersions) -> usize { + let epoch = compute_epoch(slot, config::SLOTS_PER_EPOCH as u64); + + if epoch >= fork_versions.electra.epoch { + return config::electra::BLOCK_ROOTS_INDEX; + } + + config::altair::BLOCK_ROOTS_INDEX + } + + pub fn execution_header_gindex_at_slot(slot: u64, fork_versions: ForkVersions) -> usize { + let epoch = compute_epoch(slot, config::SLOTS_PER_EPOCH as u64); + + if epoch >= fork_versions.electra.epoch { + return config::electra::EXECUTION_HEADER_INDEX; + } + + config::altair::EXECUTION_HEADER_INDEX + } } } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs index 185dfae2f6c4..035a5462602a 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/mock.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/mock.rs @@ -101,8 +101,12 @@ parameter_types! { epoch: 0, }, deneb: Fork { - version: [4, 0, 0, 0], // 0x90000073 + version: [4, 0, 0, 0], // 0x04000000 epoch: 0, + }, + electra: Fork { + version: [5, 0, 0, 0], // 0x05000000 + epoch: 80000000000, } }; } diff --git a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs index bc0ecaed674a..576cf09349b3 100644 --- a/bridges/snowbridge/pallets/ethereum-client/src/tests.rs +++ b/bridges/snowbridge/pallets/ethereum-client/src/tests.rs @@ -17,7 +17,9 @@ use crate::config::{EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SLOTS_PER use frame_support::{assert_err, assert_noop, assert_ok, pallet_prelude::Pays}; use hex_literal::hex; use snowbridge_beacon_primitives::{ - types::deneb, Fork, ForkVersions, NextSyncCommitteeUpdate, VersionedExecutionPayloadHeader, + merkle_proof::{generalized_index_length, subtree_index}, + types::deneb, + Fork, ForkVersions, NextSyncCommitteeUpdate, VersionedExecutionPayloadHeader, }; use snowbridge_core::inbound::{VerificationError, Verifier}; use sp_core::H256; @@ -158,8 +160,8 @@ pub fn verify_merkle_branch_for_finalized_root() { hex!("d2dc4ba9fd4edff6716984136831e70a6b2e74fca27b8097a820cbbaa5a6e3c3").into(), hex!("91f77a19d8afa4a08e81164bb2e570ecd10477b3b65c305566a6d2be88510584").into(), ], - crate::config::FINALIZED_ROOT_INDEX, - crate::config::FINALIZED_ROOT_DEPTH, + subtree_index(crate::config::altair::FINALIZED_ROOT_INDEX), + generalized_index_length(crate::config::altair::FINALIZED_ROOT_INDEX), hex!("e46559327592741956f6beaa0f52e49625eb85dce037a0bd2eff333c743b287f").into() )); }); @@ -175,8 +177,8 @@ pub fn verify_merkle_branch_fails_if_depth_and_branch_dont_match() { hex!("5f6f02af29218292d21a69b64a794a7c0873b3e0f54611972863706e8cbdf371").into(), hex!("e7125ff9ab5a840c44bedb4731f440a405b44e15f2d1a89e27341b432fabe13d").into(), ], - crate::config::FINALIZED_ROOT_INDEX, - crate::config::FINALIZED_ROOT_DEPTH, + subtree_index(crate::config::altair::FINALIZED_ROOT_INDEX), + generalized_index_length(crate::config::altair::FINALIZED_ROOT_INDEX), hex!("e46559327592741956f6beaa0f52e49625eb85dce037a0bd2eff333c743b287f").into() )); }); @@ -231,6 +233,7 @@ fn compute_fork_version() { bellatrix: Fork { version: [0, 0, 0, 2], epoch: 20 }, capella: Fork { version: [0, 0, 0, 3], epoch: 30 }, deneb: Fork { version: [0, 0, 0, 4], epoch: 40 }, + electra: Fork { version: [0, 0, 0, 5], epoch: 50 }, }; new_tester().execute_with(|| { assert_eq!(EthereumBeaconClient::select_fork_version(&mock_fork_versions, 0), [0, 0, 0, 0]); @@ -251,6 +254,14 @@ fn compute_fork_version() { EthereumBeaconClient::select_fork_version(&mock_fork_versions, 32), [0, 0, 0, 3] ); + assert_eq!( + EthereumBeaconClient::select_fork_version(&mock_fork_versions, 40), + [0, 0, 0, 4] + ); + assert_eq!( + EthereumBeaconClient::select_fork_version(&mock_fork_versions, 50), + [0, 0, 0, 5] + ); }); } diff --git a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs index e639dd8771f6..84fccd49f4d6 100644 --- a/bridges/snowbridge/pallets/inbound-queue/src/mock.rs +++ b/bridges/snowbridge/pallets/inbound-queue/src/mock.rs @@ -81,6 +81,10 @@ parameter_types! { deneb: Fork { version: [4, 0, 0, 1], // 0x04000001 epoch: 4294967295, + }, + electra: Fork { + version: [5, 0, 0, 0], // 0x05000000 + epoch: 80000000000, } }; } diff --git a/bridges/snowbridge/primitives/beacon/src/ssz.rs b/bridges/snowbridge/primitives/beacon/src/ssz.rs index 0925c3cc701d..324df83a25ad 100644 --- a/bridges/snowbridge/primitives/beacon/src/ssz.rs +++ b/bridges/snowbridge/primitives/beacon/src/ssz.rs @@ -276,3 +276,93 @@ pub mod deneb { } } } + +pub mod electra { + use crate::{ + config::{EXTRA_DATA_SIZE, FEE_RECIPIENT_SIZE, LOGS_BLOOM_SIZE}, + ssz::hash_tree_root, + types::electra::ExecutionPayloadHeader, + }; + use byte_slice_cast::AsByteSlice; + use sp_core::H256; + use sp_std::{vec, vec::Vec}; + use ssz_rs::{ + prelude::{List, Vector}, + Deserialize, DeserializeError, SimpleSerializeError, Sized, U256, + }; + use ssz_rs_derive::SimpleSerialize as SimpleSerializeDerive; + + #[derive(Default, SimpleSerializeDerive, Clone, Debug)] + pub struct SSZExecutionPayloadHeader { + pub parent_hash: [u8; 32], + pub fee_recipient: Vector, + pub state_root: [u8; 32], + pub receipts_root: [u8; 32], + pub logs_bloom: Vector, + pub prev_randao: [u8; 32], + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + pub extra_data: List, + pub base_fee_per_gas: U256, + pub block_hash: [u8; 32], + pub transactions_root: [u8; 32], + pub withdrawals_root: [u8; 32], + pub blob_gas_used: u64, + pub excess_blob_gas: u64, + pub deposit_requests_root: [u8; 32], + pub withdrawal_requests_root: [u8; 32], + pub consolidation_requests_root: [u8; 32], + } + + impl TryFrom for SSZExecutionPayloadHeader { + type Error = SimpleSerializeError; + + fn try_from(payload: ExecutionPayloadHeader) -> Result { + Ok(SSZExecutionPayloadHeader { + parent_hash: payload.parent_hash.to_fixed_bytes(), + fee_recipient: Vector::::try_from( + payload.fee_recipient.to_fixed_bytes().to_vec(), + ) + .expect("checked statically; qed"), + state_root: payload.state_root.to_fixed_bytes(), + receipts_root: payload.receipts_root.to_fixed_bytes(), + // Logs bloom bytes size is not constrained, so here we do need to check the + // try_from error + logs_bloom: Vector::::try_from(payload.logs_bloom) + .map_err(|(_, err)| err)?, + prev_randao: payload.prev_randao.to_fixed_bytes(), + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + // Extra data bytes size is not constrained, so here we do need to check the + // try_from error + extra_data: List::::try_from(payload.extra_data) + .map_err(|(_, err)| err)?, + base_fee_per_gas: U256::from_bytes_le( + payload + .base_fee_per_gas + .as_byte_slice() + .try_into() + .expect("checked in prep; qed"), + ), + block_hash: payload.block_hash.to_fixed_bytes(), + transactions_root: payload.transactions_root.to_fixed_bytes(), + withdrawals_root: payload.withdrawals_root.to_fixed_bytes(), + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + deposit_requests_root: payload.deposit_requests_root.to_fixed_bytes(), + withdrawal_requests_root: payload.withdrawal_requests_root.to_fixed_bytes(), + consolidation_requests_root: payload.consolidation_requests_root.to_fixed_bytes(), + }) + } + } + + impl ExecutionPayloadHeader { + pub fn hash_tree_root(&self) -> Result { + hash_tree_root::(self.clone().try_into()?) + } + } +} diff --git a/bridges/snowbridge/primitives/beacon/src/types.rs b/bridges/snowbridge/primitives/beacon/src/types.rs index 9a699a1755cc..e8c05f34cae6 100644 --- a/bridges/snowbridge/primitives/beacon/src/types.rs +++ b/bridges/snowbridge/primitives/beacon/src/types.rs @@ -36,6 +36,7 @@ pub struct ForkVersions { pub bellatrix: Fork, pub capella: Fork, pub deneb: Fork, + pub electra: Fork, } #[derive(Clone, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)] @@ -366,6 +367,7 @@ pub struct CompactBeaconState { pub enum VersionedExecutionPayloadHeader { Capella(ExecutionPayloadHeader), Deneb(deneb::ExecutionPayloadHeader), + Electra(electra::ExecutionPayloadHeader), } impl VersionedExecutionPayloadHeader { @@ -379,6 +381,10 @@ impl VersionedExecutionPayloadHeader { hash_tree_root::( execution_payload_header.clone().try_into()?, ), + VersionedExecutionPayloadHeader::Electra(execution_payload_header) => + hash_tree_root::( + execution_payload_header.clone().try_into()?, + ), } } @@ -388,6 +394,8 @@ impl VersionedExecutionPayloadHeader { execution_payload_header.block_hash, VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => execution_payload_header.block_hash, + VersionedExecutionPayloadHeader::Electra(execution_payload_header) => + execution_payload_header.block_hash, } } @@ -397,6 +405,8 @@ impl VersionedExecutionPayloadHeader { execution_payload_header.block_number, VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => execution_payload_header.block_number, + VersionedExecutionPayloadHeader::Electra(execution_payload_header) => + execution_payload_header.block_number, } } @@ -406,6 +416,8 @@ impl VersionedExecutionPayloadHeader { execution_payload_header.receipts_root, VersionedExecutionPayloadHeader::Deneb(execution_payload_header) => execution_payload_header.receipts_root, + VersionedExecutionPayloadHeader::Electra(execution_payload_header) => + execution_payload_header.receipts_root, } } } @@ -617,3 +629,59 @@ pub mod deneb { pub excess_blob_gas: u64, // [New in Deneb:EIP4844] } } + +pub mod electra { + use codec::{Decode, Encode}; + use frame_support::{CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound}; + use scale_info::TypeInfo; + #[cfg(feature = "std")] + use serde::{Deserialize, Serialize}; + use sp_core::{H160, H256, U256}; + use sp_std::prelude::*; + + /// ExecutionPayloadHeader + /// https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#executionpayloadheader + #[derive( + Default, Encode, Decode, CloneNoBound, PartialEqNoBound, RuntimeDebugNoBound, TypeInfo, + )] + #[cfg_attr( + feature = "std", + derive(Serialize, Deserialize), + serde(deny_unknown_fields, bound(serialize = ""), bound(deserialize = "")) + )] + #[codec(mel_bound())] + pub struct ExecutionPayloadHeader { + pub parent_hash: H256, + pub fee_recipient: H160, + pub state_root: H256, + pub receipts_root: H256, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes") + )] + pub logs_bloom: Vec, + pub prev_randao: H256, + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_hex_to_bytes") + )] + pub extra_data: Vec, + #[cfg_attr( + feature = "std", + serde(deserialize_with = "crate::serde_utils::from_int_to_u256") + )] + pub base_fee_per_gas: U256, + pub block_hash: H256, + pub transactions_root: H256, + pub withdrawals_root: H256, + pub blob_gas_used: u64, + pub excess_blob_gas: u64, + pub deposit_requests_root: H256, // [New in Electra:EIP6110] + pub withdrawal_requests_root: H256, // [New in Electra:EIP7002:EIP7251] + pub consolidation_requests_root: H256, // [New in Electra:EIP7251] + } +} diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs index 8c122c461ac8..bf22e57bc8fc 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_ethereum_config.rs @@ -136,6 +136,10 @@ parameter_types! { deneb: Fork { version: [4, 0, 0, 0], // 0x04000000 epoch: 0, + }, + electra: Fork { + version: [5, 0, 0, 0], // 0x05000000 + epoch: 80000000000, } }; } @@ -163,6 +167,10 @@ parameter_types! { version: [144, 0, 0, 115], // 0x90000073 epoch: 132608, }, + electra: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 80000000, + }, }; } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs index f4fa5b9cd365..a411032c4706 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_ethereum_config.rs @@ -137,6 +137,10 @@ parameter_types! { deneb: Fork { version: [4, 0, 0, 0], // 0x04000000 epoch: 0, + }, + electra: Fork { + version: [5, 0, 0, 0], // 0x05000000 + epoch: 80000000000, } }; } @@ -164,6 +168,10 @@ parameter_types! { version: [144, 0, 0, 115], // 0x90000073 epoch: 132608, }, + electra: Fork { + version: [144, 0, 0, 115], // 0x90000073 + epoch: 80000000, + }, }; }