mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-08 23:08:19 -05:00
feat: add support for testing_ rpc namespace and testing_buildBlockV1 (#20094)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com> Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -9232,6 +9232,7 @@ dependencies = [
|
||||
"alloy-sol-types",
|
||||
"eyre",
|
||||
"futures",
|
||||
"jsonrpsee-core",
|
||||
"rand 0.9.2",
|
||||
"reth-chainspec",
|
||||
"reth-db",
|
||||
@@ -9267,6 +9268,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"similar-asserts",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@@ -10157,6 +10159,7 @@ dependencies = [
|
||||
"reth-db-api",
|
||||
"reth-engine-primitives",
|
||||
"reth-errors",
|
||||
"reth-ethereum-engine-primitives",
|
||||
"reth-ethereum-primitives",
|
||||
"reth-evm",
|
||||
"reth-evm-ethereum",
|
||||
@@ -10219,7 +10222,9 @@ dependencies = [
|
||||
"reth-network-peers",
|
||||
"reth-rpc-eth-api",
|
||||
"reth-trie-common",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -376,11 +376,11 @@ reth-era-utils = { path = "crates/era-utils" }
|
||||
reth-errors = { path = "crates/errors" }
|
||||
reth-eth-wire = { path = "crates/net/eth-wire" }
|
||||
reth-eth-wire-types = { path = "crates/net/eth-wire-types" }
|
||||
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
|
||||
reth-ethereum-cli = { path = "crates/ethereum/cli", default-features = false }
|
||||
reth-ethereum-consensus = { path = "crates/ethereum/consensus", default-features = false }
|
||||
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false }
|
||||
reth-ethereum-forks = { path = "crates/ethereum/hardforks", default-features = false }
|
||||
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
|
||||
reth-ethereum-primitives = { path = "crates/ethereum/primitives", default-features = false }
|
||||
reth-ethereum = { path = "crates/ethereum/reth" }
|
||||
reth-etl = { path = "crates/etl" }
|
||||
|
||||
@@ -61,6 +61,8 @@ reth-node-core.workspace = true
|
||||
reth-e2e-test-utils.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-testing-utils.workspace = true
|
||||
tempfile.workspace = true
|
||||
jsonrpsee-core.workspace = true
|
||||
|
||||
alloy-primitives.workspace = true
|
||||
alloy-provider.workspace = true
|
||||
|
||||
@@ -38,9 +38,9 @@ use reth_payload_primitives::PayloadTypes;
|
||||
use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
|
||||
use reth_rpc::{
|
||||
eth::core::{EthApiFor, EthRpcConverterFor},
|
||||
ValidationApi,
|
||||
TestingApi, ValidationApi,
|
||||
};
|
||||
use reth_rpc_api::servers::BlockSubmissionValidationApiServer;
|
||||
use reth_rpc_api::servers::{BlockSubmissionValidationApiServer, TestingApiServer};
|
||||
use reth_rpc_builder::{config::RethRpcServerConfig, middleware::RethRpcMiddleware};
|
||||
use reth_rpc_eth_api::{
|
||||
helpers::{
|
||||
@@ -313,6 +313,17 @@ where
|
||||
.modules
|
||||
.merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
|
||||
|
||||
// testing_buildBlockV1: only wire when the hidden testing module is explicitly
|
||||
// requested on any transport. Default stays disabled to honor security guidance.
|
||||
let testing_api = TestingApi::new(
|
||||
container.registry.eth_api().clone(),
|
||||
container.registry.evm_config().clone(),
|
||||
)
|
||||
.into_rpc();
|
||||
container
|
||||
.modules
|
||||
.merge_if_module_configured(RethRpcModule::Testing, testing_api)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
mod builder;
|
||||
mod exex;
|
||||
mod testing;
|
||||
|
||||
const fn main() {}
|
||||
|
||||
84
crates/ethereum/node/tests/it/testing.rs
Normal file
84
crates/ethereum/node/tests/it/testing.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
//! E2E tests for the testing RPC namespace.
|
||||
|
||||
use alloy_primitives::{Address, B256};
|
||||
use alloy_rpc_types_engine::ExecutionPayloadEnvelopeV4;
|
||||
use jsonrpsee_core::client::ClientT;
|
||||
use reth_db::test_utils::create_test_rw_db;
|
||||
use reth_ethereum_engine_primitives::EthPayloadAttributes;
|
||||
use reth_node_builder::{NodeBuilder, NodeConfig};
|
||||
use reth_node_core::{
|
||||
args::DatadirArgs,
|
||||
dirs::{DataDirPath, MaybePlatformPath},
|
||||
};
|
||||
use reth_node_ethereum::{node::EthereumAddOns, EthereumNode};
|
||||
use reth_rpc_api::TestingBuildBlockRequestV1;
|
||||
use reth_rpc_server_types::{RethRpcModule, RpcModuleSelection};
|
||||
use reth_tasks::TaskManager;
|
||||
use std::str::FromStr;
|
||||
use tempfile::tempdir;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn testing_rpc_build_block_works() -> eyre::Result<()> {
|
||||
let tasks = TaskManager::current();
|
||||
let mut rpc_args = reth_node_core::args::RpcServerArgs::default().with_http();
|
||||
rpc_args.http_api = Some(RpcModuleSelection::from_iter([RethRpcModule::Testing]));
|
||||
let tempdir = tempdir().expect("temp datadir");
|
||||
let datadir_args = DatadirArgs {
|
||||
datadir: MaybePlatformPath::<DataDirPath>::from_str(tempdir.path().to_str().unwrap())
|
||||
.expect("valid datadir"),
|
||||
static_files_path: Some(tempdir.path().join("static")),
|
||||
};
|
||||
let config = NodeConfig::test().with_datadir_args(datadir_args).with_rpc(rpc_args);
|
||||
let db = create_test_rw_db();
|
||||
|
||||
let (tx, rx): (
|
||||
oneshot::Sender<eyre::Result<ExecutionPayloadEnvelopeV4>>,
|
||||
oneshot::Receiver<eyre::Result<ExecutionPayloadEnvelopeV4>>,
|
||||
) = oneshot::channel();
|
||||
|
||||
let builder = NodeBuilder::new(config)
|
||||
.with_database(db)
|
||||
.with_launch_context(tasks.executor())
|
||||
.with_types::<EthereumNode>()
|
||||
.with_components(EthereumNode::components())
|
||||
.with_add_ons(EthereumAddOns::default())
|
||||
.on_rpc_started(move |ctx, handles| {
|
||||
let Some(client) = handles.rpc.http_client() else { return Ok(()) };
|
||||
|
||||
let chain = ctx.config().chain.clone();
|
||||
let parent_block_hash = chain.genesis_hash();
|
||||
let payload_attributes = EthPayloadAttributes {
|
||||
timestamp: chain.genesis().timestamp + 1,
|
||||
prev_randao: B256::ZERO,
|
||||
suggested_fee_recipient: Address::ZERO,
|
||||
withdrawals: None,
|
||||
parent_beacon_block_root: None,
|
||||
};
|
||||
|
||||
let request = TestingBuildBlockRequestV1 {
|
||||
parent_block_hash,
|
||||
payload_attributes,
|
||||
transactions: vec![],
|
||||
extra_data: None,
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let res: eyre::Result<ExecutionPayloadEnvelopeV4> =
|
||||
client.request("testing_buildBlockV1", [request]).await.map_err(Into::into);
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
// Launch the node with the default engine launcher.
|
||||
let launcher = builder.engine_api_launcher();
|
||||
let _node = builder.launch_with(launcher).await?;
|
||||
|
||||
// Wait for the testing RPC call to return.
|
||||
let res = rx.await.expect("testing_buildBlockV1 response");
|
||||
assert!(res.is_ok(), "testing_buildBlockV1 failed: {:?}", res.err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -168,6 +168,7 @@ where
|
||||
gas_limit: builder_config.gas_limit(parent_header.gas_limit),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root(),
|
||||
withdrawals: Some(attributes.withdrawals().clone()),
|
||||
extra_data: None,
|
||||
},
|
||||
)
|
||||
.map_err(PayloadBuilderError::other)?;
|
||||
|
||||
@@ -28,7 +28,7 @@ use alloy_evm::{
|
||||
block::{BlockExecutorFactory, BlockExecutorFor},
|
||||
precompiles::PrecompilesMap,
|
||||
};
|
||||
use alloy_primitives::{Address, B256};
|
||||
use alloy_primitives::{Address, Bytes, B256};
|
||||
use core::{error::Error, fmt::Debug};
|
||||
use execute::{BasicBlockExecutor, BlockAssembler, BlockBuilder};
|
||||
use reth_execution_errors::BlockExecutionError;
|
||||
@@ -501,6 +501,8 @@ pub struct NextBlockEnvAttributes {
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
/// Withdrawals
|
||||
pub withdrawals: Option<Withdrawals>,
|
||||
/// Optional extra data.
|
||||
pub extra_data: Option<Bytes>,
|
||||
}
|
||||
|
||||
/// Abstraction over transaction environment.
|
||||
|
||||
@@ -35,6 +35,7 @@ alloy-serde.workspace = true
|
||||
alloy-rpc-types-beacon.workspace = true
|
||||
alloy-rpc-types-engine.workspace = true
|
||||
alloy-genesis.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
# misc
|
||||
jsonrpsee = { workspace = true, features = ["server", "macros"] }
|
||||
@@ -46,3 +47,8 @@ client = [
|
||||
"jsonrpsee/async-client",
|
||||
"reth-rpc-eth-api/client",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||||
jsonrpsee = { workspace = true, features = ["client", "async-client", "http-client"] }
|
||||
|
||||
@@ -25,11 +25,14 @@ mod net;
|
||||
mod otterscan;
|
||||
mod reth;
|
||||
mod rpc;
|
||||
mod testing;
|
||||
mod trace;
|
||||
mod txpool;
|
||||
mod validation;
|
||||
mod web3;
|
||||
|
||||
pub use testing::{TestingBuildBlockRequestV1, TESTING_BUILD_BLOCK_V1};
|
||||
|
||||
/// re-export of all server traits
|
||||
pub use servers::*;
|
||||
|
||||
@@ -45,6 +48,7 @@ pub mod servers {
|
||||
otterscan::OtterscanServer,
|
||||
reth::RethApiServer,
|
||||
rpc::RpcApiServer,
|
||||
testing::TestingApiServer,
|
||||
trace::TraceApiServer,
|
||||
txpool::TxPoolApiServer,
|
||||
validation::BlockSubmissionValidationApiServer,
|
||||
@@ -75,6 +79,7 @@ pub mod clients {
|
||||
otterscan::OtterscanClient,
|
||||
reth::RethApiClient,
|
||||
rpc::RpcApiServer,
|
||||
testing::TestingApiClient,
|
||||
trace::TraceApiClient,
|
||||
txpool::TxPoolApiClient,
|
||||
validation::BlockSubmissionValidationApiClient,
|
||||
|
||||
45
crates/rpc/rpc-api/src/testing.rs
Normal file
45
crates/rpc/rpc-api/src/testing.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
//! Testing namespace for building a block in a single call.
|
||||
//!
|
||||
//! This follows the `testing_buildBlockV1` specification. **Highly sensitive:**
|
||||
//! testing-only, powerful enough to include arbitrary transactions; must stay
|
||||
//! disabled by default and never be exposed on public-facing RPC without an
|
||||
//! explicit operator flag.
|
||||
|
||||
use alloy_primitives::{Bytes, B256};
|
||||
use alloy_rpc_types_engine::{
|
||||
ExecutionPayloadEnvelopeV5, PayloadAttributes as EthPayloadAttributes,
|
||||
};
|
||||
use jsonrpsee::proc_macros::rpc;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Capability string for `testing_buildBlockV1`.
|
||||
pub const TESTING_BUILD_BLOCK_V1: &str = "testing_buildBlockV1";
|
||||
|
||||
/// Request payload for `testing_buildBlockV1`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TestingBuildBlockRequestV1 {
|
||||
/// Parent block hash of the block to build.
|
||||
pub parent_block_hash: B256,
|
||||
/// Payload attributes (Cancun version).
|
||||
pub payload_attributes: EthPayloadAttributes,
|
||||
/// Raw signed transactions to force-include in order.
|
||||
pub transactions: Vec<Bytes>,
|
||||
/// Optional extra data for the block header.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub extra_data: Option<Bytes>,
|
||||
}
|
||||
|
||||
/// Testing RPC interface for building a block in a single call.
|
||||
#[cfg_attr(not(feature = "client"), rpc(server, namespace = "testing"))]
|
||||
#[cfg_attr(feature = "client", rpc(server, client, namespace = "testing"))]
|
||||
pub trait TestingApi {
|
||||
/// Builds a block using the provided parent, payload attributes, and transactions.
|
||||
///
|
||||
/// See <https://github.com/marcindsobczak/execution-apis/blob/main/src/testing/testing_buildBlockV1.md>
|
||||
#[method(name = "buildBlockV1")]
|
||||
async fn build_block_v1(
|
||||
&self,
|
||||
request: TestingBuildBlockRequestV1,
|
||||
) -> jsonrpsee::core::RpcResult<ExecutionPayloadEnvelopeV5>;
|
||||
}
|
||||
@@ -52,6 +52,7 @@ use reth_rpc_eth_api::{
|
||||
};
|
||||
use reth_rpc_eth_types::{receipt::EthReceiptConverter, EthConfig, EthSubscriptionIdProvider};
|
||||
use reth_rpc_layer::{AuthLayer, Claims, CompressionLayer, JwtAuthValidator, JwtSecret};
|
||||
pub use reth_rpc_server_types::RethRpcModule;
|
||||
use reth_storage_api::{
|
||||
AccountReader, BlockReader, ChangeSetReader, FullRpcProvider, NodePrimitivesProvider,
|
||||
StateProviderFactory,
|
||||
@@ -76,7 +77,7 @@ use jsonrpsee::server::ServerConfigBuilder;
|
||||
pub use reth_ipc::server::{
|
||||
Builder as IpcServerBuilder, RpcServiceBuilder as IpcRpcServiceBuilder,
|
||||
};
|
||||
pub use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
|
||||
pub use reth_rpc_server_types::{constants, RpcModuleSelection};
|
||||
pub use tower::layer::util::{Identity, Stack};
|
||||
|
||||
/// Auth server utilities.
|
||||
@@ -561,8 +562,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<Provider, Pool, Network, EthApi, BlockExecutor, Consensus>
|
||||
RpcRegistryInner<Provider, Pool, Network, EthApi, BlockExecutor, Consensus>
|
||||
impl<Provider, Pool, Network, EthApi, Evm, Consensus>
|
||||
RpcRegistryInner<Provider, Pool, Network, EthApi, Evm, Consensus>
|
||||
where
|
||||
EthApi: EthApiTypes,
|
||||
{
|
||||
@@ -591,6 +592,11 @@ where
|
||||
&self.provider
|
||||
}
|
||||
|
||||
/// Returns a reference to the evm config
|
||||
pub const fn evm_config(&self) -> &Evm {
|
||||
&self.evm_config
|
||||
}
|
||||
|
||||
/// Returns all installed methods
|
||||
pub fn methods(&self) -> Vec<Methods> {
|
||||
self.modules.values().cloned().collect()
|
||||
@@ -992,18 +998,18 @@ where
|
||||
.into_rpc()
|
||||
.into()
|
||||
}
|
||||
// only relevant for Ethereum and configured in `EthereumAddOns`
|
||||
// implementation
|
||||
// TODO: can we get rid of this here?
|
||||
// Custom modules are not handled here - they should be registered via
|
||||
// extend_rpc_modules
|
||||
RethRpcModule::Flashbots | RethRpcModule::Other(_) => Default::default(),
|
||||
RethRpcModule::Miner => MinerApi::default().into_rpc().into(),
|
||||
RethRpcModule::Mev => {
|
||||
EthSimBundle::new(eth_api.clone(), self.blocking_pool_guard.clone())
|
||||
.into_rpc()
|
||||
.into()
|
||||
}
|
||||
// these are implementation specific and need to be handled during
|
||||
// initialization and should be registered via extend_rpc_modules in the
|
||||
// nodebuilder rpc addon stack
|
||||
RethRpcModule::Flashbots |
|
||||
RethRpcModule::Testing |
|
||||
RethRpcModule::Other(_) => Default::default(),
|
||||
})
|
||||
.clone()
|
||||
})
|
||||
|
||||
@@ -420,6 +420,7 @@ impl<H: BlockHeader> BuildPendingEnv<H> for NextBlockEnvAttributes {
|
||||
gas_limit: parent.gas_limit(),
|
||||
parent_beacon_block_root: parent.parent_beacon_block_root(),
|
||||
withdrawals: parent.withdrawals_root().map(|_| Default::default()),
|
||||
extra_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +323,8 @@ pub enum RethRpcModule {
|
||||
Miner,
|
||||
/// `mev_` module
|
||||
Mev,
|
||||
/// `testing_` module
|
||||
Testing,
|
||||
/// Custom RPC module not part of the standard set
|
||||
#[strum(default)]
|
||||
#[serde(untagged)]
|
||||
@@ -347,6 +349,7 @@ impl RethRpcModule {
|
||||
Self::Flashbots,
|
||||
Self::Miner,
|
||||
Self::Mev,
|
||||
Self::Testing,
|
||||
];
|
||||
|
||||
/// Returns the number of standard variants (excludes Other)
|
||||
@@ -406,6 +409,7 @@ impl AsRef<str> for RethRpcModule {
|
||||
Self::Flashbots => "flashbots",
|
||||
Self::Miner => "miner",
|
||||
Self::Mev => "mev",
|
||||
Self::Testing => "testing",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,6 +432,7 @@ impl FromStr for RethRpcModule {
|
||||
"flashbots" => Self::Flashbots,
|
||||
"miner" => Self::Miner,
|
||||
"mev" => Self::Mev,
|
||||
"testing" => Self::Testing,
|
||||
// Any unknown module becomes Other
|
||||
other => Self::Other(other.to_string()),
|
||||
})
|
||||
|
||||
@@ -38,6 +38,8 @@ reth-rpc-server-types.workspace = true
|
||||
reth-network-types.workspace = true
|
||||
reth-consensus.workspace = true
|
||||
reth-consensus-common.workspace = true
|
||||
reth-ethereum-primitives.workspace = true
|
||||
reth-ethereum-engine-primitives.workspace = true
|
||||
reth-node-api.workspace = true
|
||||
reth-trie-common.workspace = true
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ mod net;
|
||||
mod otterscan;
|
||||
mod reth;
|
||||
mod rpc;
|
||||
mod testing;
|
||||
mod trace;
|
||||
mod txpool;
|
||||
mod validation;
|
||||
@@ -58,6 +59,7 @@ pub use otterscan::OtterscanApi;
|
||||
pub use reth::RethApi;
|
||||
pub use reth_rpc_convert::RpcTypes;
|
||||
pub use rpc::RPCApi;
|
||||
pub use testing::TestingApi;
|
||||
pub use trace::TraceApi;
|
||||
pub use txpool::TxPoolApi;
|
||||
pub use validation::{ValidationApi, ValidationApiConfig};
|
||||
|
||||
127
crates/rpc/rpc/src/testing.rs
Normal file
127
crates/rpc/rpc/src/testing.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
//! Implementation of the `testing` namespace.
|
||||
//!
|
||||
//! This exposes `testing_buildBlockV1`, intended for non-production/debug use.
|
||||
|
||||
use alloy_consensus::{Header, Transaction};
|
||||
use alloy_evm::Evm;
|
||||
use alloy_primitives::U256;
|
||||
use alloy_rpc_types_engine::ExecutionPayloadEnvelopeV5;
|
||||
use async_trait::async_trait;
|
||||
use jsonrpsee::core::RpcResult;
|
||||
use reth_errors::RethError;
|
||||
use reth_ethereum_engine_primitives::EthBuiltPayload;
|
||||
use reth_ethereum_primitives::EthPrimitives;
|
||||
use reth_evm::{execute::BlockBuilder, ConfigureEvm, NextBlockEnvAttributes};
|
||||
use reth_primitives_traits::{AlloyBlockHeader as BlockTrait, Recovered, TxTy};
|
||||
use reth_revm::{database::StateProviderDatabase, db::State};
|
||||
use reth_rpc_api::{TestingApiServer, TestingBuildBlockRequestV1};
|
||||
use reth_rpc_eth_api::{helpers::Call, FromEthApiError};
|
||||
use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError};
|
||||
use reth_storage_api::{BlockReader, HeaderProvider};
|
||||
use revm::context::Block;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Testing API handler.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestingApi<Eth, Evm> {
|
||||
eth_api: Eth,
|
||||
evm_config: Evm,
|
||||
}
|
||||
|
||||
impl<Eth, Evm> TestingApi<Eth, Evm> {
|
||||
/// Create a new testing API handler.
|
||||
pub const fn new(eth_api: Eth, evm_config: Evm) -> Self {
|
||||
Self { eth_api, evm_config }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Eth, Evm> TestingApi<Eth, Evm>
|
||||
where
|
||||
Eth: Call<Provider: BlockReader<Header = Header>>,
|
||||
Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes, Primitives = EthPrimitives>
|
||||
+ 'static,
|
||||
{
|
||||
async fn build_block_v1(
|
||||
&self,
|
||||
request: TestingBuildBlockRequestV1,
|
||||
) -> Result<ExecutionPayloadEnvelopeV5, Eth::Error> {
|
||||
let evm_config = self.evm_config.clone();
|
||||
self.eth_api
|
||||
.spawn_with_state_at_block(request.parent_block_hash, move |eth_api, state| {
|
||||
let state = state.database.0;
|
||||
let mut db = State::builder()
|
||||
.with_bundle_update()
|
||||
.with_database(StateProviderDatabase::new(&state))
|
||||
.build();
|
||||
let parent = eth_api
|
||||
.provider()
|
||||
.sealed_header_by_hash(request.parent_block_hash)?
|
||||
.ok_or_else(|| {
|
||||
EthApiError::HeaderNotFound(request.parent_block_hash.into())
|
||||
})?;
|
||||
|
||||
let env_attrs = NextBlockEnvAttributes {
|
||||
timestamp: request.payload_attributes.timestamp,
|
||||
suggested_fee_recipient: request.payload_attributes.suggested_fee_recipient,
|
||||
prev_randao: request.payload_attributes.prev_randao,
|
||||
gas_limit: parent.gas_limit(),
|
||||
parent_beacon_block_root: request.payload_attributes.parent_beacon_block_root,
|
||||
withdrawals: request.payload_attributes.withdrawals.map(Into::into),
|
||||
extra_data: request.extra_data,
|
||||
};
|
||||
|
||||
let mut builder = evm_config
|
||||
.builder_for_next_block(&mut db, &parent, env_attrs)
|
||||
.map_err(RethError::other)
|
||||
.map_err(Eth::Error::from_eth_err)?;
|
||||
builder.apply_pre_execution_changes().map_err(Eth::Error::from_eth_err)?;
|
||||
|
||||
let mut total_fees = U256::ZERO;
|
||||
let base_fee = builder.evm_mut().block().basefee();
|
||||
|
||||
for tx in request.transactions {
|
||||
let tx: Recovered<TxTy<Evm::Primitives>> = recover_raw_transaction(&tx)?;
|
||||
let tip = tx.effective_tip_per_gas(base_fee).unwrap_or_default();
|
||||
let gas_used =
|
||||
builder.execute_transaction(tx).map_err(Eth::Error::from_eth_err)?;
|
||||
|
||||
total_fees += U256::from(tip) * U256::from(gas_used);
|
||||
}
|
||||
let outcome = builder.finish(&state).map_err(Eth::Error::from_eth_err)?;
|
||||
|
||||
let requests = outcome
|
||||
.block
|
||||
.requests_hash()
|
||||
.is_some()
|
||||
.then_some(outcome.execution_result.requests);
|
||||
|
||||
EthBuiltPayload::new(
|
||||
alloy_rpc_types_engine::PayloadId::default(),
|
||||
Arc::new(outcome.block.into_sealed_block()),
|
||||
total_fees,
|
||||
requests,
|
||||
)
|
||||
.try_into_v5()
|
||||
.map_err(RethError::other)
|
||||
.map_err(Eth::Error::from_eth_err)
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<Eth, Evm> TestingApiServer for TestingApi<Eth, Evm>
|
||||
where
|
||||
Eth: Call<Provider: BlockReader<Header = Header>>,
|
||||
Evm: ConfigureEvm<NextBlockEnvCtx = NextBlockEnvAttributes, Primitives = EthPrimitives>
|
||||
+ 'static,
|
||||
{
|
||||
/// Handles `testing_buildBlockV1` by gating concurrency via a semaphore and offloading heavy
|
||||
/// work to the blocking pool to avoid stalling the async runtime.
|
||||
async fn build_block_v1(
|
||||
&self,
|
||||
request: TestingBuildBlockRequestV1,
|
||||
) -> RpcResult<ExecutionPayloadEnvelopeV5> {
|
||||
self.build_block_v1(request).await.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
@@ -302,7 +302,7 @@ RPC:
|
||||
--http.api <HTTP_API>
|
||||
Rpc Modules to be configured for the HTTP server
|
||||
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev]
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev, testing]
|
||||
|
||||
--http.corsdomain <HTTP_CORSDOMAIN>
|
||||
Http Corsdomain to allow request from
|
||||
@@ -326,7 +326,7 @@ RPC:
|
||||
--ws.api <WS_API>
|
||||
Rpc Modules to be configured for the WS server
|
||||
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev]
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev, testing]
|
||||
|
||||
--ipcdisable
|
||||
Disable the IPC-RPC server
|
||||
|
||||
@@ -302,7 +302,7 @@ RPC:
|
||||
--http.api <HTTP_API>
|
||||
Rpc Modules to be configured for the HTTP server
|
||||
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev]
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev, testing]
|
||||
|
||||
--http.corsdomain <HTTP_CORSDOMAIN>
|
||||
Http Corsdomain to allow request from
|
||||
@@ -326,7 +326,7 @@ RPC:
|
||||
--ws.api <WS_API>
|
||||
Rpc Modules to be configured for the WS server
|
||||
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev]
|
||||
[possible values: admin, debug, eth, net, trace, txpool, web3, rpc, reth, ots, flashbots, miner, mev, testing]
|
||||
|
||||
--ipcdisable
|
||||
Disable the IPC-RPC server
|
||||
|
||||
Reference in New Issue
Block a user