diff --git a/Cargo.lock b/Cargo.lock index 429bd492..b40252c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2500,7 +2500,6 @@ name = "execution_engine" version = "0.0.0" dependencies = [ "anyhow", - "bls", "either", "ethereum-types", "futures", diff --git a/eth1_api/src/eth1_api.rs b/eth1_api/src/eth1_api.rs index 6d4c8b13..cfc3347a 100644 --- a/eth1_api/src/eth1_api.rs +++ b/eth1_api/src/eth1_api.rs @@ -8,8 +8,8 @@ use ethereum_types::H64; use execution_engine::{ EngineGetPayloadV1Response, EngineGetPayloadV2Response, EngineGetPayloadV3Response, EngineGetPayloadV4Response, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, - ExecutionPayloadV4, ForkChoiceStateV1, ForkChoiceUpdatedResponse, PayloadAttributes, PayloadId, - PayloadStatusV1, + ForkChoiceStateV1, ForkChoiceUpdatedResponse, PayloadAttributes, PayloadId, PayloadStatusV1, + RawExecutionRequests, }; use futures::{channel::mpsc::UnboundedSender, lock::Mutex, Future}; use log::warn; @@ -194,7 +194,7 @@ impl Eth1Api { Ok(deposit_events) } - /// Calls [`engine_newPayloadV1`] or [`engine_newPayloadV2`] or [`engine_newPayloadV3`] depending on `payload`. + /// Calls [`engine_newPayloadV1`] or [`engine_newPayloadV2`] or [`engine_newPayloadV3`] or [`engine_newPayloadV4`] depending on `payload`. /// /// Later versions of `engine_newPayload` accept parameters of all prior versions, /// but using the earlier versions allows the application to work with old execution clients. @@ -202,6 +202,7 @@ impl Eth1Api { /// [`engine_newPayloadV1`]: https://github.com/ethereum/execution-apis/blob/b7c5d3420e00648f456744d121ffbd929862924d/src/engine/paris.md#engine_newpayloadv1 /// [`engine_newPayloadV2`]: https://github.com/ethereum/execution-apis/blob/b7c5d3420e00648f456744d121ffbd929862924d/src/engine/shanghai.md#engine_newpayloadv2 /// [`engine_newPayloadV3`]: https://github.com/ethereum/execution-apis/blob/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine/cancun.md#engine_newpayloadv3 + /// [`engine_newPayloadV4`]: https://github.com/ethereum/execution-apis/blob/4140e528360fea53c34a766d86a000c6c039100e/src/engine/prague.md#engine_newpayloadv4 pub async fn new_payload( &self, payload: ExecutionPayload

, @@ -256,12 +257,16 @@ impl Eth1Api { execution_requests, }), ) => { - let payload_v4 = ExecutionPayloadV4::from((payload, execution_requests)); + let payload_v3 = ExecutionPayloadV3::from(payload); + let raw_execution_requests = RawExecutionRequests::from(execution_requests); + let params = vec![ - serde_json::to_value(payload_v4)?, + serde_json::to_value(payload_v3)?, serde_json::to_value(versioned_hashes)?, serde_json::to_value(parent_beacon_block_root)?, + serde_json::to_value(raw_execution_requests)?, ]; + self.execute( "engine_newPayloadV4", params, @@ -370,7 +375,7 @@ impl Eth1Api { }) } - /// Calls [`engine_getPayloadV1`] or [`engine_getPayloadV2`] or [`engine_getPayloadV3`]depending on `payload_id`. + /// Calls [`engine_getPayloadV1`] or [`engine_getPayloadV2`] or [`engine_getPayloadV3`] or [`engine_getPayloadV4`] depending on `payload_id`. /// /// Newer versions of the method may be used to request payloads from all prior versions, /// but using the old methods allows the application to work with old execution clients. @@ -378,6 +383,7 @@ impl Eth1Api { /// [`engine_getPayloadV1`]: https://github.com/ethereum/execution-apis/blob/b7c5d3420e00648f456744d121ffbd929862924d/src/engine/paris.md#engine_getpayloadv1 /// [`engine_getPayloadV2`]: https://github.com/ethereum/execution-apis/blob/b7c5d3420e00648f456744d121ffbd929862924d/src/engine/shanghai.md#engine_getpayloadv2 /// [`engine_getPayloadV3`]: https://github.com/ethereum/execution-apis/blob/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine/cancun.md#engine_getpayloadv3 + /// [`engine_getPayloadV4`]: https://github.com/ethereum/execution-apis/blob/4140e528360fea53c34a766d86a000c6c039100e/src/engine/prague.md#engine_getpayloadv4 pub async fn get_payload( &self, payload_id: PayloadId, @@ -573,9 +579,12 @@ mod tests { use hex_literal::hex; use httpmock::{Method, MockServer}; use serde_json::json; + use ssz::ContiguousList; use types::{ bellatrix::containers::ExecutionPayload as BellatrixExecutionPayload, - phase0::primitives::H256, preset::Mainnet, + electra::containers::{DepositRequest, ExecutionRequests}, + phase0::primitives::H256, + preset::Mainnet, }; use super::*; @@ -711,6 +720,195 @@ mod tests { Ok(()) } + #[tokio::test] + async fn test_electra_payload_deserialization_with_default_execution_requests() -> Result<()> { + let body = json!({ + "jsonrpc": "2.0", + "id": 0, + "result": { + "executionPayload": { + "parentHash": "0x128133536f44733af5e59ba865744690498529592c1e85655348ec6bb559c658", + "feeRecipient": "0x8943545177806ed17b9f23f0a21ee5948ecaa776", + "stateRoot": "0xfb458127dfb40b16693e70886d0f503160be2ad409ab885fb4051d96b07bdef1", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x4c2db6d476f102aa7b68808f9262c70760e6cd5f23213c039cbe7309437a8d9d", + "blockNumber": "0x29", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x671214b3", + "extraData": "0xd883010e0c846765746888676f312e32332e32856c696e7578", + "baseFeePerGas": "0x403226", + "blockHash": "0x49a38631ab242befe4d9fbb1a49c7059c21363a534542f8bcf419a82b92a229b", + "transactions": [], + "withdrawals": [ + { + "index": "0xbb", + "validatorIndex": "0xd1", + "address": "0x65d08a056c17ae13370565b04cf77d2afa1cb9fa", + "amount": "0x51f0" + }, + { + "index": "0xbc", + "validatorIndex": "0xd2", + "address": "0x65d08a056c17ae13370565b04cf77d2afa1cb9fa", + "amount": "0x51f0" + } + ], + "blobGasUsed": "0x0", + "excessBlobGas": "0x0" + }, + "blockValue": "0x0", + "blobsBundle": { + "commitments": [], + "proofs": [], + "blobs": [] + }, + "executionRequests": [ + "0x", + "0x", + "0x" + ], + "shouldOverrideBuilder": false + } + }); + + let server = MockServer::start(); + + server.mock(|when, then| { + when.method(Method::POST).path("/"); + then.status(200).body(body.to_string()); + }); + + let config = Arc::new(Config::mainnet()); + let auth = Arc::default(); + let server_url = server.url("/").parse()?; + + let eth1_api = Arc::new(Eth1Api::new( + config, + Client::new(), + auth, + vec![server_url], + None, + None, + )); + + let payload_id = PayloadId::Electra(H64(hex!("a5f7426cdca69a73"))); + let payload = eth1_api.get_payload::(payload_id).await?; + + assert_eq!(payload.value.phase(), Phase::Deneb); + assert_eq!( + payload.execution_requests, + Some(ExecutionRequests::default()) + ); + + Ok(()) + } + + #[tokio::test] + async fn test_electra_payload_deserialization_with_non_empty_execution_requests() -> Result<()> + { + let body = json!({ + "jsonrpc": "2.0", + "id": 0, + "result": { + "executionPayload": { + "parentHash": "0x128133536f44733af5e59ba865744690498529592c1e85655348ec6bb559c658", + "feeRecipient": "0x8943545177806ed17b9f23f0a21ee5948ecaa776", + "stateRoot": "0xfb458127dfb40b16693e70886d0f503160be2ad409ab885fb4051d96b07bdef1", + "receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "prevRandao": "0x4c2db6d476f102aa7b68808f9262c70760e6cd5f23213c039cbe7309437a8d9d", + "blockNumber": "0x29", + "gasLimit": "0x1c9c380", + "gasUsed": "0x0", + "timestamp": "0x671214b3", + "extraData": "0xd883010e0c846765746888676f312e32332e32856c696e7578", + "baseFeePerGas": "0x403226", + "blockHash": "0x49a38631ab242befe4d9fbb1a49c7059c21363a534542f8bcf419a82b92a229b", + "transactions": [], + "withdrawals": [ + { + "index": "0xbb", + "validatorIndex": "0xd1", + "address": "0x65d08a056c17ae13370565b04cf77d2afa1cb9fa", + "amount": "0x51f0" + }, + { + "index": "0xbc", + "validatorIndex": "0xd2", + "address": "0x65d08a056c17ae13370565b04cf77d2afa1cb9fa", + "amount": "0x51f0" + } + ], + "blobGasUsed": "0x0", + "excessBlobGas": "0x0" + }, + "blockValue": "0x0", + "blobsBundle": { + "commitments": [], + "proofs": [], + "blobs": [] + }, + "executionRequests": [ + "0x92f9fe7570a6650d030bb2227d699c744303d08a887cd2e1592e30906cd8cedf9646c1a1afd902235bb36620180eb68802000000000000000000000065d08a056c17ae13370565b04cf77d2afa1cb9fa0010a5d4e8000000a13741d65b47825c147201cfce3360438d4011fe81b455e86226c95a2669bfde14712ba36d1c2f44371a98bf28ff38370ce7d28c65872bf65ff88d6014468676029e298903c89c51c27ab5f07e178b8b14d3ca191e2ce3b24703629e3994e05b000000000000000090a58546229c585cef35f3afab904411530303d95c371e246a2e9a1ef6beb5db7a98c2fd79a388709a30ec782576a5d602000000000000000000000065d08a056c17ae13370565b04cf77d2afa1cb9fa0010a5d4e8000000b23e205d2fcfc3e9d3ae58c0f78b55b19f97f59eaf43d85113a1960ee2c38f6b4ef705302e46e0593fc41ba5632b047a14d76dc82bb2619d7c73e0d89da2eda2ea11fff9036c2d08f9d457c07f23b1411ecd13ff0e9c00eeb85d851bae2494e00100000000000000", + "0x", + "0x" + ], + "shouldOverrideBuilder": false + } + }); + + let server = MockServer::start(); + + server.mock(|when, then| { + when.method(Method::POST).path("/"); + then.status(200).body(body.to_string()); + }); + + let config = Arc::new(Config::mainnet()); + let auth = Arc::default(); + let server_url = server.url("/").parse()?; + + let eth1_api = Arc::new(Eth1Api::new( + config, + Client::new(), + auth, + vec![server_url], + None, + None, + )); + + let payload_id = PayloadId::Electra(H64(hex!("a5f7426cdca69a73"))); + let payload = eth1_api.get_payload::(payload_id).await?; + + assert_eq!(payload.value.phase(), Phase::Deneb); + assert_eq!( + payload.execution_requests, + Some(ExecutionRequests { + deposits: ContiguousList::try_from(vec![ + DepositRequest { + pubkey: hex!("92f9fe7570a6650d030bb2227d699c744303d08a887cd2e1592e30906cd8cedf9646c1a1afd902235bb36620180eb688").into(), + withdrawal_credentials: hex!("02000000000000000000000065d08a056c17ae13370565b04cf77d2afa1cb9fa").into(), + amount: 1_000_000_000_000, + signature: hex!("a13741d65b47825c147201cfce3360438d4011fe81b455e86226c95a2669bfde14712ba36d1c2f44371a98bf28ff38370ce7d28c65872bf65ff88d6014468676029e298903c89c51c27ab5f07e178b8b14d3ca191e2ce3b24703629e3994e05b").into(), + index: 0, + }, + DepositRequest { + pubkey: hex!("90a58546229c585cef35f3afab904411530303d95c371e246a2e9a1ef6beb5db7a98c2fd79a388709a30ec782576a5d6").into(), + withdrawal_credentials: hex!("02000000000000000000000065d08a056c17ae13370565b04cf77d2afa1cb9fa").into(), + amount: 1_000_000_000_000, + signature: hex!("b23e205d2fcfc3e9d3ae58c0f78b55b19f97f59eaf43d85113a1960ee2c38f6b4ef705302e46e0593fc41ba5632b047a14d76dc82bb2619d7c73e0d89da2eda2ea11fff9036c2d08f9d457c07f23b1411ecd13ff0e9c00eeb85d851bae2494e0").into(), + index: 1, + } + ])?, + ..Default::default() + }) + ); + + Ok(()) + } + #[tokio::test] async fn test_valid_payload_status_deserialization() -> Result<()> { let body = json!({ diff --git a/execution_engine/Cargo.toml b/execution_engine/Cargo.toml index 49b2df62..9eea0ae7 100644 --- a/execution_engine/Cargo.toml +++ b/execution_engine/Cargo.toml @@ -8,7 +8,6 @@ workspace = true [dependencies] anyhow = { workspace = true } -bls = { workspace = true } either = { workspace = true } ethereum-types = { workspace = true } futures = { workspace = true } diff --git a/execution_engine/src/lib.rs b/execution_engine/src/lib.rs index d4e0d007..a72cd1e8 100644 --- a/execution_engine/src/lib.rs +++ b/execution_engine/src/lib.rs @@ -3,11 +3,12 @@ pub use crate::{ types::{ EngineGetPayloadV1Response, EngineGetPayloadV2Response, EngineGetPayloadV3Response, EngineGetPayloadV4Response, ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3, - ExecutionPayloadV4, ForkChoiceStateV1, ForkChoiceUpdatedResponse, PayloadAttributes, - PayloadAttributesV1, PayloadAttributesV2, PayloadAttributesV3, PayloadId, PayloadStatus, - PayloadStatusV1, PayloadStatusWithBlockHash, PayloadValidationStatus, + ForkChoiceStateV1, ForkChoiceUpdatedResponse, PayloadAttributes, PayloadAttributesV1, + PayloadAttributesV2, PayloadAttributesV3, PayloadId, PayloadStatus, PayloadStatusV1, + PayloadStatusWithBlockHash, PayloadValidationStatus, RawExecutionRequests, }, }; mod execution_engine; +mod ssz_as_prefixed_hex_or_bytes; mod types; diff --git a/execution_engine/src/ssz_as_prefixed_hex_or_bytes.rs b/execution_engine/src/ssz_as_prefixed_hex_or_bytes.rs new file mode 100644 index 00000000..efb684dc --- /dev/null +++ b/execution_engine/src/ssz_as_prefixed_hex_or_bytes.rs @@ -0,0 +1,14 @@ +use serde::{de::Error as _, ser::Error as _, Deserializer, Serializer}; +use ssz::{SszReadDefault, SszWrite}; + +pub fn deserialize<'de, T: SszReadDefault, D: Deserializer<'de>>( + deserializer: D, +) -> Result { + let bytes = serde_utils::prefixed_hex_or_bytes_cow::deserialize(deserializer)?; + T::from_ssz_default(bytes).map_err(D::Error::custom) +} + +pub fn serialize(value: impl SszWrite, serializer: S) -> Result { + let bytes = value.to_ssz().map_err(S::Error::custom)?; + serde_utils::prefixed_hex_or_bytes_slice::serialize(bytes, serializer) +} diff --git a/execution_engine/src/types.rs b/execution_engine/src/types.rs index 40bc3d9a..0cbef1d3 100644 --- a/execution_engine/src/types.rs +++ b/execution_engine/src/types.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use bls::{PublicKeyBytes, SignatureBytes}; use ethereum_types::H64; use serde::{Deserialize, Serialize}; use ssz::{ByteList, ByteVector, ContiguousList}; @@ -362,295 +361,6 @@ impl From> for DenebExecutionPayload

{ } } -/// [`ExecutionPayloadV4`](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#ExecutionPayloadV4) -#[derive(Deserialize, Serialize)] -#[serde(bound = "", rename_all = "camelCase")] -pub struct ExecutionPayloadV4 { - pub parent_hash: ExecutionBlockHash, - pub fee_recipient: ExecutionAddress, - pub state_root: H256, - pub receipts_root: H256, - pub logs_bloom: ByteVector, - pub prev_randao: H256, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub block_number: ExecutionBlockNumber, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub gas_limit: Gas, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub gas_used: Gas, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub timestamp: UnixSeconds, - pub extra_data: Arc>, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub base_fee_per_gas: Wei, - pub block_hash: ExecutionBlockHash, - pub transactions: Arc, P::MaxTransactionsPerPayload>>, - pub withdrawals: ContiguousList, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub blob_gas_used: Gas, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub excess_blob_gas: Gas, - #[serde(rename = "depositRequests")] - pub deposit_requests: ContiguousList, - pub withdrawal_requests: - ContiguousList, - pub consolidation_requests: - ContiguousList, -} - -impl From<(DenebExecutionPayload

, ExecutionRequests

)> for ExecutionPayloadV4

{ - fn from( - (payload, execution_requests): (DenebExecutionPayload

, ExecutionRequests

), - ) -> Self { - let DenebExecutionPayload { - parent_hash, - fee_recipient, - state_root, - receipts_root, - logs_bloom, - prev_randao, - block_number, - gas_limit, - gas_used, - timestamp, - extra_data, - base_fee_per_gas, - block_hash, - transactions, - withdrawals, - blob_gas_used, - excess_blob_gas, - } = payload; - - let ExecutionRequests { - deposits: deposit_requests, - withdrawals: withdrawal_requests, - consolidations: consolidation_requests, - } = execution_requests; - - let withdrawals = withdrawals.map(Into::into); - let deposit_requests = deposit_requests.map(Into::into); - let withdrawal_requests = withdrawal_requests.map(Into::into); - let consolidation_requests = consolidation_requests.map(Into::into); - - Self { - parent_hash, - fee_recipient, - state_root, - receipts_root, - logs_bloom, - prev_randao, - block_number, - gas_limit, - gas_used, - timestamp, - extra_data, - base_fee_per_gas, - block_hash, - transactions, - withdrawals, - blob_gas_used, - excess_blob_gas, - deposit_requests, - withdrawal_requests, - consolidation_requests, - } - } -} - -impl From> for (DenebExecutionPayload

, ExecutionRequests

) { - fn from(payload: ExecutionPayloadV4

) -> Self { - let ExecutionPayloadV4 { - parent_hash, - fee_recipient, - state_root, - receipts_root, - logs_bloom, - prev_randao, - block_number, - gas_limit, - gas_used, - timestamp, - extra_data, - base_fee_per_gas, - block_hash, - transactions, - withdrawals, - blob_gas_used, - excess_blob_gas, - deposit_requests, - withdrawal_requests, - consolidation_requests, - } = payload; - - let withdrawals = withdrawals.map(Into::into); - let deposit_requests = deposit_requests.map(Into::into); - let withdrawal_requests = withdrawal_requests.map(Into::into); - let consolidation_requests = consolidation_requests.map(Into::into); - - let execution_payload = DenebExecutionPayload { - parent_hash, - fee_recipient, - state_root, - receipts_root, - logs_bloom, - prev_randao, - block_number, - gas_limit, - gas_used, - timestamp, - extra_data, - base_fee_per_gas, - block_hash, - transactions, - withdrawals, - blob_gas_used, - excess_blob_gas, - }; - - let execution_requests = ExecutionRequests { - deposits: deposit_requests, - withdrawals: withdrawal_requests, - consolidations: consolidation_requests, - }; - - (execution_payload, execution_requests) - } -} - -#[derive(Deserialize, Serialize)] -#[serde(bound = "", rename_all = "camelCase")] -pub struct DepositRequestV1 { - pub pubkey: PublicKeyBytes, - pub withdrawal_credentials: H256, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub amount: Gwei, - pub signature: SignatureBytes, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub index: u64, -} - -impl From for DepositRequestV1 { - fn from(deposit_request: DepositRequest) -> Self { - let DepositRequest { - pubkey, - withdrawal_credentials, - amount, - signature, - index, - } = deposit_request; - - Self { - pubkey, - withdrawal_credentials, - amount, - signature, - index, - } - } -} - -impl From for DepositRequest { - fn from(deposit_request: DepositRequestV1) -> Self { - let DepositRequestV1 { - pubkey, - withdrawal_credentials, - amount, - signature, - index, - } = deposit_request; - - Self { - pubkey, - withdrawal_credentials, - amount, - signature, - index, - } - } -} - -#[derive(Deserialize, Serialize)] -#[serde(bound = "", rename_all = "camelCase")] -pub struct WithdrawalRequestV1 { - pub source_address: ExecutionAddress, - pub validator_pubkey: PublicKeyBytes, - #[serde(with = "serde_utils::prefixed_hex_quantity")] - pub amount: Gwei, -} - -impl From for WithdrawalRequestV1 { - fn from(withdrawal_request: WithdrawalRequest) -> Self { - let WithdrawalRequest { - source_address, - validator_pubkey, - amount, - } = withdrawal_request; - - Self { - source_address, - validator_pubkey, - amount, - } - } -} - -impl From for WithdrawalRequest { - fn from(withdrawal_request: WithdrawalRequestV1) -> Self { - let WithdrawalRequestV1 { - source_address, - validator_pubkey, - amount, - } = withdrawal_request; - - Self { - source_address, - validator_pubkey, - amount, - } - } -} - -#[derive(Deserialize, Serialize)] -#[serde(bound = "", rename_all = "camelCase")] -pub struct ConsolidationRequestV1 { - pub source_address: ExecutionAddress, - pub source_pubkey: PublicKeyBytes, - pub target_pubkey: PublicKeyBytes, -} - -impl From for ConsolidationRequestV1 { - fn from(consolidation_request: ConsolidationRequest) -> Self { - let ConsolidationRequest { - source_address, - source_pubkey, - target_pubkey, - } = consolidation_request; - - Self { - source_address, - source_pubkey, - target_pubkey, - } - } -} - -impl From for ConsolidationRequest { - fn from(consolidation_request: ConsolidationRequestV1) -> Self { - let ConsolidationRequestV1 { - source_address, - source_pubkey, - target_pubkey, - } = consolidation_request; - - Self { - source_address, - source_pubkey, - target_pubkey, - } - } -} - /// [`BlobsBundleV1`](https://github.com/ethereum/execution-apis/blob/v1.0.0-beta.3/src/engine/experimental/blob-extension.md#blobsbundlev1) #[derive(Deserialize, Serialize)] #[serde(bound = "", rename_all = "camelCase")] @@ -786,11 +496,12 @@ impl From> for WithBlobsAndMev { - pub execution_payload: ExecutionPayloadV4

, + pub execution_payload: ExecutionPayloadV3

, #[serde(with = "serde_utils::prefixed_hex_quantity")] pub block_value: Wei, pub blobs_bundle: BlobsBundleV1

, pub should_override_builder: bool, + pub execution_requests: RawExecutionRequests

, } impl From> for WithBlobsAndMev, P> { @@ -799,10 +510,11 @@ impl From> for WithBlobsAndMev From> for WithBlobsAndMev for PayloadStatusV1 { } } +#[derive(Deserialize, Serialize)] +#[cfg_attr(test, derive(Default))] +pub struct RawExecutionRequests( + #[serde(with = "crate::ssz_as_prefixed_hex_or_bytes")] + ContiguousList, + #[serde(with = "crate::ssz_as_prefixed_hex_or_bytes")] + ContiguousList, + #[serde(with = "crate::ssz_as_prefixed_hex_or_bytes")] + ContiguousList, +); + +impl From> for RawExecutionRequests

{ + fn from(execution_requests: ExecutionRequests

) -> Self { + let ExecutionRequests { + deposits, + withdrawals, + consolidations, + } = execution_requests; + + Self(deposits, withdrawals, consolidations) + } +} + +impl From> for ExecutionRequests

{ + fn from(raw_execution_requests: RawExecutionRequests

) -> Self { + let RawExecutionRequests(deposits, withdrawals, consolidations) = raw_execution_requests; + + Self { + deposits, + withdrawals, + consolidations, + } + } +} + #[cfg(test)] mod tests { use anyhow::Result; @@ -1057,6 +804,13 @@ mod tests { Ok(()) } + #[test] + fn test_default_raw_execution_requests_serialization() -> Result<()> { + let serialized = serde_json::to_value(RawExecutionRequests::::default())?; + assert_eq!(serialized, json!(["0x", "0x", "0x"])); + Ok(()) + } + #[test] fn test_payload_status_v1_round_trip() -> Result<()> { let json = json!({