diff --git a/crates/rpc/rpc-types/src/beacon/macros.rs b/crates/rpc/rpc-types/src/beacon/macros.rs deleted file mode 100644 index 9e397886132b..000000000000 --- a/crates/rpc/rpc-types/src/beacon/macros.rs +++ /dev/null @@ -1,24 +0,0 @@ - -/// A helper type that generates helper types for the beacon API. -macro_rules! beacon_serde_glue { - // Named-Struct - ( - $( #[$meta:meta] )* - $vis:vis struct $name:ident { - $( - $( #[$field_meta:meta] )* - $field_vis:vis $field_name:ident : $field_ty:ty - ),* - $(,)? } - ) => { - $( #[$meta] )* - $vis struct Beacon$name { - $( - $( #[$field_meta] )* - $field_vis $field_name : $field_ty - ),* - } - } -} - -pub(crate) use beacon_serde_glue; diff --git a/crates/rpc/rpc-types/src/beacon/mod.rs b/crates/rpc/rpc-types/src/beacon/mod.rs index 30d410326d98..c0166413ba32 100644 --- a/crates/rpc/rpc-types/src/beacon/mod.rs +++ b/crates/rpc/rpc-types/src/beacon/mod.rs @@ -5,8 +5,8 @@ use alloy_primitives::FixedBytes; use constants::{BLS_PUBLIC_KEY_BYTES_LEN, BLS_SIGNATURE_BYTES_LEN}; pub mod constants; -pub mod macros; pub mod payload; +pub mod withdrawals; /// BLS signature type pub type BlsSignature = FixedBytes; diff --git a/crates/rpc/rpc-types/src/beacon/payload.rs b/crates/rpc/rpc-types/src/beacon/payload.rs index 5fcecde09026..e92bfc14ef41 100644 --- a/crates/rpc/rpc-types/src/beacon/payload.rs +++ b/crates/rpc/rpc-types/src/beacon/payload.rs @@ -1,4 +1,4 @@ -#![allow(missing_docs)] +#![allow(missing_docs, private_interfaces)] //! Payload support for the beacon API. //! //! Internal helper module to deserialize/serialize the payload attributes for the beacon API, which @@ -6,37 +6,103 @@ //! //! This is necessary because we don't want to allow a mixture of both formats, hence `serde` //! aliases are not an option. +//! +//! See also pub use crate::Withdrawal; use crate::{ - eth::{transaction::BlobTransactionSidecar, withdrawal::BeaconAPIWithdrawal}, - ExecutionPayloadV1, + beacon::withdrawals::BeaconWithdrawal, engine::ExecutionPayloadV3, ExecutionPayload, + ExecutionPayloadV1, ExecutionPayloadV2, }; -use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256, U64}; -use c_kzg::{Blob, Bytes48}; -use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -use serde_with::{serde_as, DisplayFromStr}; +use alloy_primitives::{Address, Bloom, Bytes, B256, U256, U64}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs}; +use std::borrow::Cow; +#[serde_as] #[derive(Serialize, Deserialize)] -pub(crate) struct BeaconExecutionPayloadV1 { - pub(crate) parent_hash: B256, - pub(crate) fee_recipient: Address, - pub(crate) state_root: B256, - pub(crate) receipts_root: B256, - pub(crate) logs_bloom: Bloom, - pub(crate) prev_randao: B256, - pub(crate) block_number: U64, - pub(crate) gas_limit: U64, - pub(crate) gas_used: U64, - pub(crate) timestamp: U64, - pub(crate) extra_data: Bytes, - pub(crate) base_fee_per_gas: U256, - pub(crate) block_hash: B256, - pub(crate) transactions: Vec, -} - -impl From for ExecutionPayloadV1 { - fn from(payload: BeaconExecutionPayloadV1) -> Self { +struct BeaconPayloadAttributes { + #[serde_as(as = "DisplayFromStr")] + timestamp: u64, + prev_randao: B256, + suggested_fee_recipient: Address, + #[serde(skip_serializing_if = "Option::is_none")] + #[serde_as(as = "Option>")] + withdrawals: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + parent_beacon_block_root: Option, +} + +/// A helper module for serializing and deserializing the payload attributes for the beacon API. +/// +/// The beacon API encoded object has equivalent fields to the [PayloadAttributes] with two +/// differences: +/// 1) `snake_case` identifiers must be used rather than `camelCase`; +/// 2) integers must be encoded as quoted decimals rather than big-endian hex. +pub mod beacon_api_payload_attributes { + use super::*; + use crate::engine::PayloadAttributes; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Serialize the payload attributes for the beacon API. + pub fn serialize( + payload_attributes: &PayloadAttributes, + serializer: S, + ) -> Result + where + S: Serializer, + { + let beacon_api_payload_attributes = BeaconPayloadAttributes { + timestamp: payload_attributes.timestamp.to(), + prev_randao: payload_attributes.prev_randao, + suggested_fee_recipient: payload_attributes.suggested_fee_recipient, + withdrawals: payload_attributes.withdrawals.clone(), + parent_beacon_block_root: payload_attributes.parent_beacon_block_root, + }; + beacon_api_payload_attributes.serialize(serializer) + } + + /// Deserialize the payload attributes for the beacon API. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let beacon_api_payload_attributes = BeaconPayloadAttributes::deserialize(deserializer)?; + Ok(PayloadAttributes { + timestamp: U64::from(beacon_api_payload_attributes.timestamp), + prev_randao: beacon_api_payload_attributes.prev_randao, + suggested_fee_recipient: beacon_api_payload_attributes.suggested_fee_recipient, + withdrawals: beacon_api_payload_attributes.withdrawals, + parent_beacon_block_root: beacon_api_payload_attributes.parent_beacon_block_root, + }) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct BeaconExecutionPayloadV1<'a> { + pub(crate) parent_hash: Cow<'a, B256>, + pub(crate) fee_recipient: Cow<'a, Address>, + pub(crate) state_root: Cow<'a, B256>, + pub(crate) receipts_root: Cow<'a, B256>, + pub(crate) logs_bloom: Cow<'a, Bloom>, + pub(crate) prev_randao: Cow<'a, B256>, + #[serde_as(as = "DisplayFromStr")] + pub(crate) block_number: u64, + #[serde_as(as = "DisplayFromStr")] + pub(crate) gas_limit: u64, + #[serde_as(as = "DisplayFromStr")] + pub(crate) gas_used: u64, + #[serde_as(as = "DisplayFromStr")] + pub(crate) timestamp: u64, + pub(crate) extra_data: Cow<'a, Bytes>, + pub(crate) base_fee_per_gas: Cow<'a, U256>, + pub(crate) block_hash: Cow<'a, B256>, + pub(crate) transactions: Cow<'a, Vec>, +} + +impl<'a> From> for ExecutionPayloadV1 { + fn from(payload: BeaconExecutionPayloadV1<'a>) -> Self { let BeaconExecutionPayloadV1 { parent_hash, fee_recipient, @@ -54,6 +120,27 @@ impl From for ExecutionPayloadV1 { transactions, } = payload; ExecutionPayloadV1 { + parent_hash: parent_hash.into_owned(), + fee_recipient: fee_recipient.into_owned(), + state_root: state_root.into_owned(), + receipts_root: receipts_root.into_owned(), + logs_bloom: logs_bloom.into_owned(), + prev_randao: prev_randao.into_owned(), + block_number: U64::from(block_number), + gas_limit: U64::from(gas_limit), + gas_used: U64::from(gas_used), + timestamp: U64::from(timestamp), + extra_data: extra_data.into_owned(), + base_fee_per_gas: base_fee_per_gas.into_owned(), + block_hash: block_hash.into_owned(), + transactions: transactions.into_owned(), + } + } +} + +impl<'a> From<&'a ExecutionPayloadV1> for BeaconExecutionPayloadV1<'a> { + fn from(value: &'a ExecutionPayloadV1) -> Self { + let ExecutionPayloadV1 { parent_hash, fee_recipient, state_root, @@ -68,6 +155,23 @@ impl From for ExecutionPayloadV1 { base_fee_per_gas, block_hash, transactions, + } = value; + + BeaconExecutionPayloadV1 { + parent_hash: Cow::Borrowed(parent_hash), + fee_recipient: Cow::Borrowed(fee_recipient), + state_root: Cow::Borrowed(state_root), + receipts_root: Cow::Borrowed(receipts_root), + logs_bloom: Cow::Borrowed(logs_bloom), + prev_randao: Cow::Borrowed(prev_randao), + block_number: block_number.to(), + gas_limit: gas_limit.to(), + gas_used: gas_used.to(), + timestamp: timestamp.to(), + extra_data: Cow::Borrowed(extra_data), + base_fee_per_gas: Cow::Borrowed(base_fee_per_gas), + block_hash: Cow::Borrowed(block_hash), + transactions: Cow::Borrowed(transactions), } } } @@ -76,21 +180,250 @@ impl From for ExecutionPayloadV1 { /// big-endian hex. pub mod beacon_payload_v1 { use super::*; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; + use serde::{Deserializer, Serializer}; + + /// Serialize the payload attributes for the beacon API. + pub fn serialize( + payload_attributes: &ExecutionPayloadV1, + serializer: S, + ) -> Result + where + S: Serializer, + { + BeaconExecutionPayloadV1::from(payload_attributes).serialize(serializer) + } + + /// Deserialize the payload attributes for the beacon API. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + BeaconExecutionPayloadV1::deserialize(deserializer).map(Into::into) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct BeaconExecutionPayloadV2<'a> { + /// Inner V1 payload + #[serde(flatten)] + pub(crate) payload_inner: BeaconExecutionPayloadV1<'a>, + /// Array of [`Withdrawal`] enabled with V2 + /// See + #[serde_as(as = "Vec")] + pub(crate) withdrawals: Vec, +} + +impl<'a> From> for ExecutionPayloadV2 { + fn from(payload: BeaconExecutionPayloadV2<'a>) -> Self { + let BeaconExecutionPayloadV2 { payload_inner, withdrawals } = payload; + ExecutionPayloadV2 { payload_inner: payload_inner.into(), withdrawals } + } +} + +impl<'a> From<&'a ExecutionPayloadV2> for BeaconExecutionPayloadV2<'a> { + fn from(value: &'a ExecutionPayloadV2) -> Self { + let ExecutionPayloadV2 { payload_inner, withdrawals } = value; + BeaconExecutionPayloadV2 { + payload_inner: payload_inner.into(), + withdrawals: withdrawals.clone(), + } + } +} + +/// A helper serde module to convert from/to the Beacon API which uses quoted decimals rather than +/// big-endian hex. +pub mod beacon_payload_v2 { + use super::*; + use serde::{Deserializer, Serializer}; + + /// Serialize the payload attributes for the beacon API. + pub fn serialize( + payload_attributes: &ExecutionPayloadV2, + serializer: S, + ) -> Result + where + S: Serializer, + { + BeaconExecutionPayloadV2::from(payload_attributes).serialize(serializer) + } + + /// Deserialize the payload attributes for the beacon API. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + BeaconExecutionPayloadV2::deserialize(deserializer).map(Into::into) + } +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct BeaconExecutionPayloadV3<'a> { + /// Inner V1 payload + #[serde(flatten)] + pub(crate) payload_inner: BeaconExecutionPayloadV2<'a>, + /// Array of [`U64`] representing blob gas used, enabled with V3 + /// See + #[serde_as(as = "DisplayFromStr")] + pub(crate) blob_gas_used: u64, + /// Array of [`U64`] representing excess blob gas, enabled with V3 + /// See + #[serde_as(as = "DisplayFromStr")] + pub(crate) excess_blob_gas: u64, +} + +impl<'a> From> for ExecutionPayloadV3 { + fn from(payload: BeaconExecutionPayloadV3<'a>) -> Self { + let BeaconExecutionPayloadV3 { payload_inner, blob_gas_used, excess_blob_gas } = payload; + ExecutionPayloadV3 { + payload_inner: payload_inner.into(), + blob_gas_used: U64::from(blob_gas_used), + excess_blob_gas: U64::from(excess_blob_gas), + } + } +} + +impl<'a> From<&'a ExecutionPayloadV3> for BeaconExecutionPayloadV3<'a> { + fn from(value: &'a ExecutionPayloadV3) -> Self { + let ExecutionPayloadV3 { payload_inner, blob_gas_used, excess_blob_gas } = value; + BeaconExecutionPayloadV3 { + payload_inner: payload_inner.into(), + blob_gas_used: blob_gas_used.to(), + excess_blob_gas: excess_blob_gas.to(), + } + } +} + +/// A helper serde module to convert from/to the Beacon API which uses quoted decimals rather than +/// big-endian hex. +pub mod beacon_payload_v3 { + use super::*; + use serde::{Deserializer, Serializer}; + + /// Serialize the payload attributes for the beacon API. + pub fn serialize( + payload_attributes: &ExecutionPayloadV3, + serializer: S, + ) -> Result + where + S: Serializer, + { + BeaconExecutionPayloadV3::from(payload_attributes).serialize(serializer) + } + + /// Deserialize the payload attributes for the beacon API. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + BeaconExecutionPayloadV3::deserialize(deserializer).map(Into::into) + } +} + +/// Represents all possible payload versions. +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum BeaconExecutionPayload<'a> { + /// V1 payload + V1(BeaconExecutionPayloadV1<'a>), + /// V2 payload + V2(BeaconExecutionPayloadV2<'a>), + /// V3 payload + V3(BeaconExecutionPayloadV3<'a>), +} + +// Deserializes untagged ExecutionPayload by trying each variant in falling order +impl<'de> Deserialize<'de> for BeaconExecutionPayload<'de> { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(untagged)] + enum BeaconExecutionPayloadDesc<'a> { + V3(BeaconExecutionPayloadV3<'a>), + V2(BeaconExecutionPayloadV2<'a>), + V1(BeaconExecutionPayloadV1<'a>), + } + match BeaconExecutionPayloadDesc::deserialize(deserializer)? { + BeaconExecutionPayloadDesc::V3(payload) => Ok(Self::V3(payload)), + BeaconExecutionPayloadDesc::V2(payload) => Ok(Self::V2(payload)), + BeaconExecutionPayloadDesc::V1(payload) => Ok(Self::V1(payload)), + } + } +} + +impl<'a> From> for ExecutionPayload { + fn from(payload: BeaconExecutionPayload<'a>) -> Self { + match payload { + BeaconExecutionPayload::V1(payload) => { + ExecutionPayload::V1(ExecutionPayloadV1::from(payload)) + } + BeaconExecutionPayload::V2(payload) => { + ExecutionPayload::V2(ExecutionPayloadV2::from(payload)) + } + BeaconExecutionPayload::V3(payload) => { + ExecutionPayload::V3(ExecutionPayloadV3::from(payload)) + } + } + } +} + +impl<'a> From<&'a ExecutionPayload> for BeaconExecutionPayload<'a> { + fn from(value: &'a ExecutionPayload) -> Self { + match value { + ExecutionPayload::V1(payload) => { + BeaconExecutionPayload::V1(BeaconExecutionPayloadV1::from(payload)) + } + ExecutionPayload::V2(payload) => { + BeaconExecutionPayload::V2(BeaconExecutionPayloadV2::from(payload)) + } + ExecutionPayload::V3(payload) => { + BeaconExecutionPayload::V3(BeaconExecutionPayloadV3::from(payload)) + } + } + } +} + +impl<'a> SerializeAs for BeaconExecutionPayload<'a> { + fn serialize_as(source: &ExecutionPayload, serializer: S) -> Result + where + S: Serializer, + { + beacon_payload::serialize(source, serializer) + } +} + +impl<'de> DeserializeAs<'de, ExecutionPayload> for BeaconExecutionPayload<'de> { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + beacon_payload::deserialize(deserializer) + } +} + +pub mod beacon_payload { + use super::*; + use serde::{Deserializer, Serializer}; /// Serialize the payload attributes for the beacon API. - pub fn serialize(payload_attributes: &Withdrawal, serializer: S) -> Result + pub fn serialize( + payload_attributes: &ExecutionPayload, + serializer: S, + ) -> Result where S: Serializer, { - todo!() + BeaconExecutionPayload::from(payload_attributes).serialize(serializer) } /// Deserialize the payload attributes for the beacon API. - pub fn deserialize<'de, D>(deserializer: D) -> Result + pub fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - todo!() + BeaconExecutionPayload::deserialize(deserializer).map(Into::into) } } diff --git a/crates/rpc/rpc-types/src/beacon/withdrawals.rs b/crates/rpc/rpc-types/src/beacon/withdrawals.rs new file mode 100644 index 000000000000..6a9282588847 --- /dev/null +++ b/crates/rpc/rpc-types/src/beacon/withdrawals.rs @@ -0,0 +1,72 @@ +use alloy_primitives::Address; + +use crate::Withdrawal; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs}; + +/// Same as [Withdrawal] but respects the Beacon API format which uses snake-case and quoted +/// decimals. +#[serde_as] +#[derive(Serialize, Deserialize, Clone)] +pub(crate) struct BeaconWithdrawal { + #[serde_as(as = "DisplayFromStr")] + index: u64, + #[serde_as(as = "DisplayFromStr")] + validator_index: u64, + address: Address, + #[serde_as(as = "DisplayFromStr")] + amount: u64, +} + +impl SerializeAs for BeaconWithdrawal { + fn serialize_as(source: &Withdrawal, serializer: S) -> Result + where + S: Serializer, + { + beacon_withdrawals::serialize(source, serializer) + } +} + +impl<'de> DeserializeAs<'de, Withdrawal> for BeaconWithdrawal { + fn deserialize_as(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + beacon_withdrawals::deserialize(deserializer) + } +} + +/// A helper serde module to convert from/to the Beacon API which uses quoted decimals rather than +/// big-endian hex. +pub mod beacon_withdrawals { + use super::*; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Serialize the payload attributes for the beacon API. + pub fn serialize(payload_attributes: &Withdrawal, serializer: S) -> Result + where + S: Serializer, + { + let withdrawal = BeaconWithdrawal { + index: payload_attributes.index, + validator_index: payload_attributes.validator_index, + address: payload_attributes.address, + amount: payload_attributes.amount, + }; + withdrawal.serialize(serializer) + } + + /// Deserialize the payload attributes for the beacon API. + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let withdrawal = BeaconWithdrawal::deserialize(deserializer)?; + Ok(Withdrawal { + index: withdrawal.index, + validator_index: withdrawal.validator_index, + address: withdrawal.address, + amount: withdrawal.amount, + }) + } +} diff --git a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs index d4f1bc8f4f7e..501494a91f0e 100644 --- a/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs +++ b/crates/rpc/rpc-types/src/eth/engine/beacon_api/events/mod.rs @@ -270,7 +270,7 @@ pub struct PayloadAttributesData { /// /// Note: this uses the beacon API format which uses snake-case and quoted decimals rather than /// big-endian hex. - #[serde(with = "crate::eth::engine::payload::beacon_api_payload_attributes")] + #[serde(with = "crate::beacon::payload::beacon_api_payload_attributes")] pub payload_attributes: PayloadAttributes, } diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 3c87eeb25026..b37a7faaf9ed 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -1,9 +1,8 @@ -use crate::eth::{transaction::BlobTransactionSidecar, withdrawal::BeaconAPIWithdrawal}; +use crate::eth::transaction::BlobTransactionSidecar; pub use crate::Withdrawal; use alloy_primitives::{Address, Bloom, Bytes, B256, B64, U256, U64}; use c_kzg::{Blob, Bytes48}; use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -use serde_with::{serde_as, DisplayFromStr}; /// The execution payload body response that allows for `null` values. pub type ExecutionPayloadBodiesV1 = Vec>; @@ -402,63 +401,6 @@ pub struct PayloadAttributes { pub parent_beacon_block_root: Option, } -#[serde_as] -#[derive(Serialize, Deserialize)] -struct BeaconAPIPayloadAttributes { - #[serde_as(as = "DisplayFromStr")] - timestamp: u64, - prev_randao: B256, - suggested_fee_recipient: Address, - #[serde(skip_serializing_if = "Option::is_none")] - #[serde_as(as = "Option>")] - withdrawals: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - parent_beacon_block_root: Option, -} -/// A helper module for serializing and deserializing the payload attributes for the beacon API. -/// -/// The beacon API encoded object has equivalent fields to the [PayloadAttributes] with two -/// differences: -/// 1) `snake_case` identifiers must be used rather than `camelCase`; -/// 2) integers must be encoded as quoted decimals rather than big-endian hex. -pub mod beacon_api_payload_attributes { - use super::*; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Serialize the payload attributes for the beacon API. - pub fn serialize( - payload_attributes: &PayloadAttributes, - serializer: S, - ) -> Result - where - S: Serializer, - { - let beacon_api_payload_attributes = BeaconAPIPayloadAttributes { - timestamp: payload_attributes.timestamp.to(), - prev_randao: payload_attributes.prev_randao, - suggested_fee_recipient: payload_attributes.suggested_fee_recipient, - withdrawals: payload_attributes.withdrawals.clone(), - parent_beacon_block_root: payload_attributes.parent_beacon_block_root, - }; - beacon_api_payload_attributes.serialize(serializer) - } - - /// Deserialize the payload attributes for the beacon API. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let beacon_api_payload_attributes = BeaconAPIPayloadAttributes::deserialize(deserializer)?; - Ok(PayloadAttributes { - timestamp: U64::from(beacon_api_payload_attributes.timestamp), - prev_randao: beacon_api_payload_attributes.prev_randao, - suggested_fee_recipient: beacon_api_payload_attributes.suggested_fee_recipient, - withdrawals: beacon_api_payload_attributes.withdrawals, - parent_beacon_block_root: beacon_api_payload_attributes.parent_beacon_block_root, - }) - } -} - /// This structure contains the result of processing a payload or fork choice update. #[derive(Clone, Debug, PartialEq, Eq, Deserialize)] #[serde(rename_all = "camelCase")] @@ -928,7 +870,7 @@ mod tests { #[derive(Serialize, Deserialize)] #[serde(transparent)] struct Event { - #[serde(with = "beacon_api_payload_attributes")] + #[serde(with = "crate::beacon::payload::beacon_api_payload_attributes")] payload: PayloadAttributes, } diff --git a/crates/rpc/rpc-types/src/eth/transaction/mod.rs b/crates/rpc/rpc-types/src/eth/transaction/mod.rs index 937e0d3b0ff4..c546ea1c0d2b 100644 --- a/crates/rpc/rpc-types/src/eth/transaction/mod.rs +++ b/crates/rpc/rpc-types/src/eth/transaction/mod.rs @@ -1,3 +1,5 @@ +//! RPC types for transactions + pub use access_list::{AccessList, AccessListItem, AccessListWithGasUsed}; use alloy_primitives::{Address, Bytes, B256, U128, U256, U64}; pub use common::TransactionInfo; diff --git a/crates/rpc/rpc-types/src/eth/withdrawal.rs b/crates/rpc/rpc-types/src/eth/withdrawal.rs index 0ea2322b7d54..286803f455d0 100644 --- a/crates/rpc/rpc-types/src/eth/withdrawal.rs +++ b/crates/rpc/rpc-types/src/eth/withdrawal.rs @@ -1,12 +1,10 @@ //! Withdrawal type and serde helpers. -use std::mem; - use crate::serde_helpers::u64_hex; use alloy_primitives::{Address, U256}; use alloy_rlp::{RlpDecodable, RlpEncodable}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_with::{serde_as, DeserializeAs, DisplayFromStr, SerializeAs}; +use serde::{Deserialize, Serialize}; +use std::mem; /// Multiplier for converting gwei to wei. pub const GWEI_TO_WEI: u64 = 1_000_000_000; @@ -42,73 +40,6 @@ impl Withdrawal { } } -/// Same as [Withdrawal] but respects the Beacon API format which uses snake-case and quoted -/// decimals. -#[serde_as] -#[derive(Serialize, Deserialize)] -pub(crate) struct BeaconAPIWithdrawal { - #[serde_as(as = "DisplayFromStr")] - index: u64, - #[serde_as(as = "DisplayFromStr")] - validator_index: u64, - address: Address, - #[serde_as(as = "DisplayFromStr")] - amount: u64, -} - -impl SerializeAs for BeaconAPIWithdrawal { - fn serialize_as(source: &Withdrawal, serializer: S) -> Result - where - S: Serializer, - { - beacon_api_withdrawals::serialize(source, serializer) - } -} - -impl<'de> DeserializeAs<'de, Withdrawal> for BeaconAPIWithdrawal { - fn deserialize_as(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - beacon_api_withdrawals::deserialize(deserializer) - } -} - -/// A helper serde module to convert from/to the Beacon API which uses quoted decimals rather than -/// big-endian hex. -pub mod beacon_api_withdrawals { - use super::*; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Serialize the payload attributes for the beacon API. - pub fn serialize(payload_attributes: &Withdrawal, serializer: S) -> Result - where - S: Serializer, - { - let withdrawal = BeaconAPIWithdrawal { - index: payload_attributes.index, - validator_index: payload_attributes.validator_index, - address: payload_attributes.address, - amount: payload_attributes.amount, - }; - withdrawal.serialize(serializer) - } - - /// Deserialize the payload attributes for the beacon API. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let withdrawal = BeaconAPIWithdrawal::deserialize(deserializer)?; - Ok(Withdrawal { - index: withdrawal.index, - validator_index: withdrawal.validator_index, - address: withdrawal.address, - amount: withdrawal.amount, - }) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/rpc/rpc-types/src/relay.rs b/crates/rpc/rpc-types/src/relay.rs index 1fc01bd3fbc5..4dfd439562d1 100644 --- a/crates/rpc/rpc-types/src/relay.rs +++ b/crates/rpc/rpc-types/src/relay.rs @@ -59,10 +59,12 @@ pub struct BidTrace { } /// Submission for the `/relay/v1/builder/blocks` endpoint. + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SignedBidSubmission { pub message: BidTrace, pub signature: BlsSignature, + #[serde(with = "crate::beacon::payload::beacon_payload")] pub execution_payload: ExecutionPayload, /// The Deneb block bundle for this bid. /// @@ -89,6 +91,6 @@ mod tests { fn deneb_bid_submission() { let s = r#"{"message":{"slot":"1","parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","builder_pubkey":"0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a", "proposer_pubkey": "0x93247f2209abcacf57b75a51dafae777f9dd38bc7053d1af526f220a7489a6d3a2753e5f3e8b1cfe39b56f43611df74a","proposer_fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","gas_limit":"1","gas_used":"1","value":"1"},"execution_payload":{"parent_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","fee_recipient":"0xabcf8e0d4e9587369b2301d0790347320302cc09","state_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","receipts_root":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","logs_bloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","prev_randao":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","block_number":"1","gas_limit":"1","gas_used":"1","timestamp":"1","extra_data":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","base_fee_per_gas":"1","block_hash":"0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2","transactions":["0x02f878831469668303f51d843b9ac9f9843b9aca0082520894c93269b73096998db66be0441e836d873535cb9c8894a19041886f000080c001a031cc29234036afbf9a1fb9476b463367cb1f957ac0b919b69bbc798436e604aaa018c4e9c3914eb27aadd0b91e10b18655739fcf8c1fc398763a9f1beecb8ddc86"],"withdrawals":[{"index":"1","validator_index":"1","address":"0xabcf8e0d4e9587369b2301d0790347320302cc09","amount":"32000000000"}]},"blobs_bundle":{"commitments":["0x8dab030c51e16e84be9caab84ee3d0b8bbec1db4a0e4de76439da8424d9b957370a10a78851f97e4b54d2ce1ab0d686f"],"proofs":["0xb4021b0de10f743893d4f71e1bf830c019e832958efd6795baf2f83b8699a9eccc5dc99015d8d4d8ec370d0cc333c06a"],"blobs":["0x24564723180fcb3d994104538d351c8dcbde12d541676bb736cf678018ca4739"]},"signature":"0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"}"#; - let bid = serde_json::from_str::(s).unwrap(); + let _bid = serde_json::from_str::(s).unwrap(); } }