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 e3e33a5274f1..768a50d8fcb7 100644 --- a/bridges/snowbridge/primitives/beacon/src/types.rs +++ b/bridges/snowbridge/primitives/beacon/src/types.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2023 Snowfork use codec::{Decode, Encode, MaxEncodedLen}; @@ -367,6 +368,7 @@ pub struct CompactBeaconState { pub enum VersionedExecutionPayloadHeader { Capella(ExecutionPayloadHeader), Deneb(deneb::ExecutionPayloadHeader), + Electra(electra::ExecutionPayloadHeader), } impl VersionedExecutionPayloadHeader { @@ -380,6 +382,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()?, + ), } } @@ -389,6 +395,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, } } @@ -398,6 +406,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, } } @@ -407,6 +417,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, } } } @@ -618,3 +630,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] + } +}