Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: flashbots_validateBuilderSubmissionV3 #12168

Merged
merged 18 commits into from
Oct 31, 2024
Merged
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions book/cli/reth/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,9 @@ RPC:

[default: 25]

--builder.disallow <PATH>
Path to file containing disallowed addresses, json-encoded list of strings. Block validation API will reject blocks containing transactions from these addresses

RPC State Cache:
--rpc-cache.max-blocks <MAX_BLOCKS>
Max number of blocks in cache
Expand Down
1 change: 1 addition & 0 deletions crates/cli/util/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ eyre.workspace = true
rand.workspace = true
secp256k1 = { workspace = true, features = ["rand"] }
thiserror.workspace = true
serde.workspace = true

tracy-client = { workspace = true, optional = true, features = ["demangle"] }

Expand Down
7 changes: 7 additions & 0 deletions crates/cli/util/src/parsers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use alloy_eips::BlockHashOrNumber;
use alloy_primitives::B256;
use reth_fs_util::FsPathError;
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs},
path::Path,
str::FromStr,
time::Duration,
};
Expand Down Expand Up @@ -82,6 +84,11 @@ pub fn parse_socket_address(value: &str) -> eyre::Result<SocketAddr, SocketAddre
.ok_or_else(|| SocketAddressParsingError::Parse(value.to_string()))
}

/// Wrapper around [`reth_fs_util::read_json_file`] which can be used as a clap value parser.
pub fn read_json_from_file<T: serde::de::DeserializeOwned>(path: &str) -> Result<T, FsPathError> {
reth_fs_util::read_json_file(Path::new(path))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
8 changes: 7 additions & 1 deletion crates/e2e-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use reth::{
args::{DiscoveryArgs, NetworkArgs, RpcServerArgs},
builder::{NodeBuilder, NodeConfig, NodeHandle},
network::PeersHandleProvider,
rpc::server_types::RpcModuleSelection,
tasks::TaskManager,
};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
Expand Down Expand Up @@ -147,7 +148,12 @@ where
let node_config = NodeConfig::new(chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(RpcServerArgs::default().with_unused_ports().with_http())
.with_rpc(
RpcServerArgs::default()
.with_unused_ports()
.with_http()
.with_http_api(RpcModuleSelection::All),
)
.set_dev(is_dev);

let span = span!(Level::INFO, "node", idx);
Expand Down
4 changes: 2 additions & 2 deletions crates/ethereum/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ use reth_primitives::{
use std::{fmt::Debug, sync::Arc, time::SystemTime};

/// The bound divisor of the gas limit, used in update calculations.
const GAS_LIMIT_BOUND_DIVISOR: u64 = 1024;
pub const GAS_LIMIT_BOUND_DIVISOR: u64 = 1024;

mod validation;
pub use validation::validate_block_post_execution;

/// Ethereum beacon consensus
///
/// This consensus engine does basic checks as outlined in the execution specs.
#[derive(Debug)]
#[derive(Debug, Clone)]
mattsse marked this conversation as resolved.
Show resolved Hide resolved
pub struct EthBeaconConsensus<ChainSpec> {
/// Configuration
chain_spec: Arc<ChainSpec>,
Expand Down
1 change: 1 addition & 0 deletions crates/ethereum/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ alloy-signer.workspace = true
alloy-eips.workspace = true
alloy-sol-types.workspace = true
alloy-contract.workspace = true
alloy-rpc-types-beacon.workspace = true

[features]
default = []
Expand Down
86 changes: 83 additions & 3 deletions crates/ethereum/node/tests/e2e/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use crate::utils::eth_payload_attributes;
use alloy_eips::calc_next_block_base_fee;
use alloy_primitives::U256;
use alloy_provider::{network::EthereumWallet, Provider, ProviderBuilder};
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 rand::{rngs::StdRng, Rng, SeedableRng};
use reth::rpc::{
api::BuilderBlockValidationRequestV3,
compat::engine::payload::block_to_payload_v3,
types::{engine::BlobsBundleV1, TransactionRequest},
};
use reth_chainspec::{ChainSpecBuilder, MAINNET};
use reth_e2e_test_utils::setup_engine;
use reth_node_ethereum::EthereumNode;
Expand Down Expand Up @@ -107,3 +113,77 @@ async fn test_fee_history() -> eyre::Result<()> {

Ok(())
}

#[tokio::test]
async fn test_flashbots_validate() -> 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())
.cancun_activated()
.build(),
);

let (mut nodes, _tasks, wallet) =
setup_engine::<EthereumNode>(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 = BuilderBlockValidationRequestV3 {
request: SignedBidSubmissionV3 {
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([]),
signature: Default::default(),
},
parent_beacon_block_root: attrs.parent_beacon_block_root.unwrap(),
registered_gas_limit: payload.block().gas_limit,
};

assert!(provider
.raw_request::<_, ()>("flashbots_validateBuilderSubmissionV3".into(), (&request,))
.await
.is_ok());

request.registered_gas_limit -= 1;
assert!(provider
.raw_request::<_, ()>("flashbots_validateBuilderSubmissionV3".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_validateBuilderSubmissionV3".into(), (&request,))
.await
.is_err());
Ok(())
}
3 changes: 3 additions & 0 deletions crates/node/builder/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ pub struct RpcRegistry<Node: FullNodeComponents, EthApi: EthApiTypes> {
Node::Provider,
EthApi,
Node::Executor,
Node::Consensus,
>,
}

Expand All @@ -214,6 +215,7 @@ where
Node::Provider,
EthApi,
Node::Executor,
Node::Consensus,
>;

fn deref(&self) -> &Self::Target {
Expand Down Expand Up @@ -442,6 +444,7 @@ where
.with_executor(node.task_executor().clone())
.with_evm_config(node.evm_config().clone())
.with_block_executor(node.block_executor().clone())
.with_consensus(node.consensus().clone())
.build_with_auth_server(module_config, engine_api, eth_api_builder);

// in dev mode we generate 20 random dev-signer accounts
Expand Down
14 changes: 14 additions & 0 deletions crates/node/core/src/args/rpc_server.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
//! clap [Args](clap::Args) for RPC related arguments.

use std::{
collections::HashSet,
ffi::OsStr,
net::{IpAddr, Ipv4Addr},
path::PathBuf,
};

use alloy_primitives::Address;
use alloy_rpc_types_engine::JwtSecret;
use clap::{
builder::{PossibleValue, RangedU64ValueParser, TypedValueParser},
Expand Down Expand Up @@ -183,6 +185,11 @@ pub struct RpcServerArgs {
#[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", default_value_t = constants::DEFAULT_PROOF_PERMITS)]
pub rpc_proof_permits: usize,

/// Path to file containing disallowed addresses, json-encoded list of strings. Block
/// validation API will reject blocks containing transactions from these addresses.
#[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>)]
pub builder_disallow: Option<HashSet<Address>>,

/// State cache configuration.
#[command(flatten)]
pub rpc_state_cache: RpcStateCacheArgs,
Expand All @@ -199,6 +206,12 @@ impl RpcServerArgs {
self
}

/// Configures modules for the HTTP-RPC server.
pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
self.http_api = Some(http_api);
self
}

/// Enables the WS-RPC server.
pub const fn with_ws(mut self) -> Self {
self.ws = true;
Expand Down Expand Up @@ -318,6 +331,7 @@ impl Default for RpcServerArgs {
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_state_cache: RpcStateCacheArgs::default(),
rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
builder_disallow: Default::default(),
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/payload/validator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct ExecutionPayloadValidator<ChainSpec> {
chain_spec: Arc<ChainSpec>,
}

impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<ChainSpec> {
impl<ChainSpec> ExecutionPayloadValidator<ChainSpec> {
/// Create a new validator.
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec }
Expand All @@ -34,7 +34,9 @@ impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<ChainSpec> {
pub fn chain_spec(&self) -> &ChainSpec {
&self.chain_spec
}
}

impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<ChainSpec> {
/// Returns true if the Cancun hardfork is active at the given timestamp.
#[inline]
fn is_cancun_active_at_timestamp(&self, timestamp: u64) -> bool {
Expand Down
2 changes: 2 additions & 0 deletions crates/rpc/rpc-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ alloy-rpc-types-engine.workspace = true

# misc
jsonrpsee = { workspace = true, features = ["server", "macros"] }
serde.workspace = true
serde_with.workspace = true

[features]
client = [
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/rpc-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub mod servers {
rpc::RpcApiServer,
trace::TraceApiServer,
txpool::TxPoolApiServer,
validation::BlockSubmissionValidationApiServer,
validation::{BlockSubmissionValidationApiServer, BuilderBlockValidationRequestV3},
web3::Web3ApiServer,
};
pub use reth_rpc_eth_api::{
Expand Down
21 changes: 20 additions & 1 deletion crates/rpc/rpc-api/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
//! API for block submission validation.

use alloy_primitives::B256;
use alloy_rpc_types_beacon::relay::{
BuilderBlockValidationRequest, BuilderBlockValidationRequestV2, BuilderBlockValidationRequestV3,
BuilderBlockValidationRequest, BuilderBlockValidationRequestV2, SignedBidSubmissionV3,
};
use jsonrpsee::proc_macros::rpc;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};

/// A Request to validate a [`SignedBidSubmissionV3`]
///
/// <https://github.com/flashbots/builder/blob/7577ac81da21e760ec6693637ce2a81fe58ac9f8/eth/block-validation/api.go#L198-L202>
#[serde_as]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuilderBlockValidationRequestV3 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's temporary until we have a release with alloy-rs/alloy#1577 included

/// The request to be validated.
#[serde(flatten)]
pub request: SignedBidSubmissionV3,
/// 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"))]
Expand Down
1 change: 1 addition & 0 deletions crates/rpc/rpc-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ workspace = true
# reth
reth-ipc.workspace = true
reth-chainspec.workspace = true
reth-consensus.workspace = true
reth-network-api.workspace = true
reth-node-core.workspace = true
reth-provider.workspace = true
Expand Down
Loading
Loading