From d8bbd36b2f081b3e7d4eb5c7cb90c4e8ab9343e0 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 1 Nov 2024 14:35:47 -0700 Subject: [PATCH] feat: flashbots_validateBuilderSubmissionV4 (#12243) --- crates/ethereum/node/tests/e2e/rpc.rs | 90 +++++++++++++++++++++++++-- crates/rpc/rpc-api/src/lib.rs | 5 +- crates/rpc/rpc-api/src/validation.rs | 24 +++++++ crates/rpc/rpc/src/validation.rs | 38 ++++++++++- 4 files changed, 149 insertions(+), 8 deletions(-) diff --git a/crates/ethereum/node/tests/e2e/rpc.rs b/crates/ethereum/node/tests/e2e/rpc.rs index c8b127b9b..1f7ac32e0 100644 --- a/crates/ethereum/node/tests/e2e/rpc.rs +++ b/crates/ethereum/node/tests/e2e/rpc.rs @@ -2,12 +2,15 @@ use crate::utils::eth_payload_attributes; use alloy_eips::{calc_next_block_base_fee, eip2718::Encodable2718}; use alloy_primitives::{Address, B256, U256}; use alloy_provider::{network::EthereumWallet, Provider, ProviderBuilder, SendableTx}; -use alloy_rpc_types_beacon::relay::{BidTrace, SignedBidSubmissionV3}; +use alloy_rpc_types_beacon::relay::{BidTrace, SignedBidSubmissionV3, SignedBidSubmissionV4}; use rand::{rngs::StdRng, Rng, SeedableRng}; -use reth::rpc::{ - api::BuilderBlockValidationRequestV3, - compat::engine::payload::block_to_payload_v3, - types::{engine::BlobsBundleV1, TransactionRequest}, +use reth::{ + payload::BuiltPayload, + rpc::{ + api::{BuilderBlockValidationRequestV3, BuilderBlockValidationRequestV4}, + compat::engine::payload::block_to_payload_v3, + types::{engine::BlobsBundleV1, TransactionRequest}, + }, }; use reth_chainspec::{ChainSpecBuilder, MAINNET}; use reth_e2e_test_utils::setup_engine; @@ -115,7 +118,7 @@ async fn test_fee_history() -> eyre::Result<()> { } #[tokio::test] -async fn test_flashbots_validate() -> eyre::Result<()> { +async fn test_flashbots_validate_v3() -> eyre::Result<()> { reth_tracing::init_test_tracing(); let chain_spec = Arc::new( @@ -187,3 +190,78 @@ async fn test_flashbots_validate() -> eyre::Result<()> { .is_err()); Ok(()) } + +#[tokio::test] +async fn test_flashbots_validate_v4() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + + let chain_spec = Arc::new( + ChainSpecBuilder::default() + .chain(MAINNET.chain) + .genesis(serde_json::from_str(include_str!("../assets/genesis.json")).unwrap()) + .prague_activated() + .build(), + ); + + let (mut nodes, _tasks, wallet) = + setup_engine::(1, chain_spec.clone(), false, eth_payload_attributes).await?; + let mut node = nodes.pop().unwrap(); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(EthereumWallet::new(wallet.gen().swap_remove(0))) + .on_http(node.rpc_url()); + + node.advance(100, |_| { + let provider = provider.clone(); + Box::pin(async move { + let SendableTx::Envelope(tx) = + provider.fill(TransactionRequest::default().to(Address::ZERO)).await.unwrap() + else { + unreachable!() + }; + + tx.encoded_2718().into() + }) + }) + .await?; + + let _ = provider.send_transaction(TransactionRequest::default().to(Address::ZERO)).await?; + let (payload, attrs) = node.new_payload().await?; + + let mut request = BuilderBlockValidationRequestV4 { + request: SignedBidSubmissionV4 { + message: BidTrace { + parent_hash: payload.block().parent_hash, + block_hash: payload.block().hash(), + gas_used: payload.block().gas_used, + gas_limit: payload.block().gas_limit, + ..Default::default() + }, + execution_payload: block_to_payload_v3(payload.block().clone()), + blobs_bundle: BlobsBundleV1::new([]), + execution_requests: payload.requests().unwrap_or_default().to_vec(), + signature: Default::default(), + }, + parent_beacon_block_root: attrs.parent_beacon_block_root.unwrap(), + registered_gas_limit: payload.block().gas_limit, + }; + + provider + .raw_request::<_, ()>("flashbots_validateBuilderSubmissionV4".into(), (&request,)) + .await + .expect("request should validate"); + + request.registered_gas_limit -= 1; + assert!(provider + .raw_request::<_, ()>("flashbots_validateBuilderSubmissionV4".into(), (&request,)) + .await + .is_err()); + request.registered_gas_limit += 1; + + request.request.execution_payload.payload_inner.payload_inner.state_root = B256::ZERO; + assert!(provider + .raw_request::<_, ()>("flashbots_validateBuilderSubmissionV4".into(), (&request,)) + .await + .is_err()); + Ok(()) +} diff --git a/crates/rpc/rpc-api/src/lib.rs b/crates/rpc/rpc-api/src/lib.rs index 63e6e5446..0a4fa9f66 100644 --- a/crates/rpc/rpc-api/src/lib.rs +++ b/crates/rpc/rpc-api/src/lib.rs @@ -46,7 +46,10 @@ pub mod servers { rpc::RpcApiServer, trace::TraceApiServer, txpool::TxPoolApiServer, - validation::{BlockSubmissionValidationApiServer, BuilderBlockValidationRequestV3}, + validation::{ + BlockSubmissionValidationApiServer, BuilderBlockValidationRequestV3, + BuilderBlockValidationRequestV4, + }, web3::Web3ApiServer, }; pub use reth_rpc_eth_api::{ diff --git a/crates/rpc/rpc-api/src/validation.rs b/crates/rpc/rpc-api/src/validation.rs index d8f55b668..797eee7ae 100644 --- a/crates/rpc/rpc-api/src/validation.rs +++ b/crates/rpc/rpc-api/src/validation.rs @@ -3,6 +3,7 @@ use alloy_primitives::B256; use alloy_rpc_types_beacon::relay::{ BuilderBlockValidationRequest, BuilderBlockValidationRequestV2, SignedBidSubmissionV3, + SignedBidSubmissionV4, }; use jsonrpsee::proc_macros::rpc; use serde::{Deserialize, Serialize}; @@ -24,6 +25,22 @@ pub struct BuilderBlockValidationRequestV3 { pub parent_beacon_block_root: B256, } +/// A Request to validate a [`SignedBidSubmissionV4`] +/// +/// +#[serde_as] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BuilderBlockValidationRequestV4 { + /// The request to be validated. + #[serde(flatten)] + pub request: SignedBidSubmissionV4, + /// The registered gas limit for the validation request. + #[serde_as(as = "DisplayFromStr")] + pub registered_gas_limit: u64, + /// The parent beacon block root for the validation request. + pub parent_beacon_block_root: B256, +} + /// Block validation rpc interface. #[cfg_attr(not(feature = "client"), rpc(server, namespace = "flashbots"))] #[cfg_attr(feature = "client", rpc(server, client, namespace = "flashbots"))] @@ -48,4 +65,11 @@ pub trait BlockSubmissionValidationApi { &self, request: BuilderBlockValidationRequestV3, ) -> jsonrpsee::core::RpcResult<()>; + + /// A Request to validate a block submission. + #[method(name = "validateBuilderSubmissionV4")] + async fn validate_builder_submission_v4( + &self, + request: BuilderBlockValidationRequestV4, + ) -> jsonrpsee::core::RpcResult<()>; } diff --git a/crates/rpc/rpc/src/validation.rs b/crates/rpc/rpc/src/validation.rs index fe9d0eb44..1476180d4 100644 --- a/crates/rpc/rpc/src/validation.rs +++ b/crates/rpc/rpc/src/validation.rs @@ -20,7 +20,10 @@ use reth_provider::{ StateProviderFactory, WithdrawalsProvider, }; use reth_revm::database::StateProviderDatabase; -use reth_rpc_api::{BlockSubmissionValidationApiServer, BuilderBlockValidationRequestV3}; +use reth_rpc_api::{ + BlockSubmissionValidationApiServer, BuilderBlockValidationRequestV3, + BuilderBlockValidationRequestV4, +}; use reth_rpc_eth_types::EthApiError; use reth_rpc_server_types::{result::internal_rpc_err, ToRpcResult}; use reth_trie::HashedPostState; @@ -415,4 +418,37 @@ where .map_err(|e| RethError::Other(e.into())) .to_rpc_result() } + + /// Validates a block submitted to the relay + async fn validate_builder_submission_v4( + &self, + request: BuilderBlockValidationRequestV4, + ) -> RpcResult<()> { + let block = self + .payload_validator + .ensure_well_formed_payload( + ExecutionPayload::V3(request.request.execution_payload), + ExecutionPayloadSidecar::v4( + CancunPayloadFields { + parent_beacon_block_root: request.parent_beacon_block_root, + versioned_hashes: self + .validate_blobs_bundle(request.request.blobs_bundle) + .map_err(|e| RethError::Other(e.into())) + .to_rpc_result()?, + }, + request.request.execution_requests.into(), + ), + ) + .to_rpc_result()? + .try_seal_with_senders() + .map_err(|_| EthApiError::InvalidTransactionSignature)?; + + self.validate_message_against_block( + block, + request.request.message, + request.registered_gas_limit, + ) + .map_err(|e| RethError::Other(e.into())) + .to_rpc_result() + } }