From b9f9e938f13c410d21be2fd9a3caaca50de3d061 Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Tue, 13 May 2025 15:21:46 +0000 Subject: [PATCH 1/6] Fix existing build warnings --- crates/op-rbuilder/src/args.rs | 1 + crates/op-rbuilder/src/primitives/reth/execution.rs | 7 ------- crates/op-rbuilder/src/primitives/reth/mod.rs | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/op-rbuilder/src/args.rs b/crates/op-rbuilder/src/args.rs index 1e99c7fd2..7c59bfdac 100644 --- a/crates/op-rbuilder/src/args.rs +++ b/crates/op-rbuilder/src/args.rs @@ -3,6 +3,7 @@ //! Copied from OptimismNode to allow easy extension. //! clap [Args](clap::Args) for optimism rollup configuration + use reth_optimism_node::args::RollupArgs; use crate::tx_signer::Signer; diff --git a/crates/op-rbuilder/src/primitives/reth/execution.rs b/crates/op-rbuilder/src/primitives/reth/execution.rs index ff95f8ddc..6ffdd2d11 100644 --- a/crates/op-rbuilder/src/primitives/reth/execution.rs +++ b/crates/op-rbuilder/src/primitives/reth/execution.rs @@ -5,13 +5,6 @@ use reth_node_api::NodePrimitives; use reth_optimism_primitives::OpReceipt; use std::collections::HashSet; -/// Holds the state after execution -#[derive(Debug)] -pub struct ExecutedPayload { - /// Tracked execution info - pub info: ExecutionInfo, -} - #[derive(Default, Debug)] pub struct ExecutionInfo { /// All executed transactions (unrecovered). diff --git a/crates/op-rbuilder/src/primitives/reth/mod.rs b/crates/op-rbuilder/src/primitives/reth/mod.rs index 4b6de4c5b..916207bdc 100644 --- a/crates/op-rbuilder/src/primitives/reth/mod.rs +++ b/crates/op-rbuilder/src/primitives/reth/mod.rs @@ -1,2 +1,2 @@ mod execution; -pub use execution::{ExecutedPayload, ExecutionInfo}; +pub use execution::ExecutionInfo; From f883f36b768c0a1f7379c7a11a131caa73954ea7 Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Wed, 14 May 2025 08:16:17 +0000 Subject: [PATCH 2/6] Add a --playground flag on op-rbuilder to start with the flags required to run the builder on playground Issue: https://github.com/flashbots/op-rbuilder/issues/9 This change adds the ability to autoconfigure op-rbuilder to run a locally deployed builder-playground. Follow the instructions on how to start a builder playground under this repo: https://github.com/SozinM/builder-playground/tree/msozin/feat/flashblocks Now we can use the following startup parameters for op-rbuilder: - `./op-rbuilder node --builder.playground` This will start using the default $HOME/.playground/devnet directory - `./op-rbuilder node --builder.playground=` Will use the provided path as the working directory of the playground --- Cargo.lock | 26 +- Cargo.toml | 2 + crates/op-rbuilder/Cargo.toml | 6 + crates/op-rbuilder/src/args/mod.rs | 5 + .../op-rbuilder/src/{args.rs => args/op.rs} | 18 + crates/op-rbuilder/src/args/playground.rs | 408 ++++++++++++++++++ crates/op-rbuilder/src/main.rs | 2 + crates/op-rbuilder/src/payload_builder.rs | 2 +- 8 files changed, 467 insertions(+), 2 deletions(-) create mode 100644 crates/op-rbuilder/src/args/mod.rs rename crates/op-rbuilder/src/{args.rs => args/op.rs} (73%) create mode 100644 crates/op-rbuilder/src/args/playground.rs diff --git a/Cargo.lock b/Cargo.lock index 94fed3378..8f8dc1857 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5355,11 +5355,13 @@ dependencies = [ "reth-basic-payload-builder", "reth-chain-state", "reth-chainspec", + "reth-cli", "reth-cli-util", "reth-evm", "reth-execution-types", "reth-exex", "reth-metrics", + "reth-network-peers", "reth-node-api", "reth-node-ethereum", "reth-optimism-chainspec", @@ -5389,6 +5391,8 @@ dependencies = [ "serde", "serde_json", "serde_with", + "serde_yaml", + "shellexpand", "thiserror 1.0.69", "tikv-jemallocator", "time", @@ -5398,6 +5402,7 @@ dependencies = [ "tower 0.4.13", "tracing", "tracing-subscriber 0.3.19", + "url", "uuid", ] @@ -5990,7 +5995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.100", @@ -9983,6 +9988,19 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.9.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serdect" version = "0.2.0" @@ -11141,6 +11159,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "unsigned-varint" version = "0.8.0" diff --git a/Cargo.toml b/Cargo.toml index a51cab413..f7e3d7eef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ incremental = false [workspace.dependencies] reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } +reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-db-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } @@ -72,6 +73,7 @@ reth-revm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-payload-builder-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } +reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } reth-testing-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.12" } # reth optimism diff --git a/crates/op-rbuilder/Cargo.toml b/crates/op-rbuilder/Cargo.toml index 413fb3585..7614eff33 100644 --- a/crates/op-rbuilder/Cargo.toml +++ b/crates/op-rbuilder/Cargo.toml @@ -17,6 +17,7 @@ reth-optimism-evm.workspace = true reth-optimism-consensus.workspace = true reth-optimism-primitives.workspace = true reth-optimism-txpool.workspace = true +reth-cli.workspace = true reth-cli-util.workspace = true reth-payload-primitives.workspace = true reth-evm.workspace = true @@ -38,6 +39,7 @@ reth-rpc-layer.workspace = true reth-payload-builder-primitives.workspace = true reth-payload-util.workspace = true reth-transaction-pool.workspace = true +reth-network-peers.workspace = true reth-testing-utils.workspace = true reth-optimism-forks.workspace = true @@ -81,6 +83,7 @@ serde_json.workspace = true tokio-util.workspace = true thiserror.workspace = true parking_lot.workspace = true +url.workspace = true time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } chrono = "0.4" @@ -88,6 +91,9 @@ uuid = { version = "1.6.1", features = ["serde", "v5", "v4"] } tokio-tungstenite = "0.26.2" rand = "0.9.0" tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json"] } +shellexpand = "3.1" +serde_yaml = { version = "0.9" } + # `flashblocks` branch rollup-boost = { git = "http://github.com/flashbots/rollup-boost", rev = "60885346d4cf7f241de82790478195747433d472" } diff --git a/crates/op-rbuilder/src/args/mod.rs b/crates/op-rbuilder/src/args/mod.rs new file mode 100644 index 000000000..254752062 --- /dev/null +++ b/crates/op-rbuilder/src/args/mod.rs @@ -0,0 +1,5 @@ +mod op; +mod playground; + +pub use op::OpRbuilderArgs; +pub use playground::CliExt; diff --git a/crates/op-rbuilder/src/args.rs b/crates/op-rbuilder/src/args/op.rs similarity index 73% rename from crates/op-rbuilder/src/args.rs rename to crates/op-rbuilder/src/args/op.rs index 7c59bfdac..2b4ea0124 100644 --- a/crates/op-rbuilder/src/args.rs +++ b/crates/op-rbuilder/src/args/op.rs @@ -3,6 +3,7 @@ //! Copied from OptimismNode to allow easy extension. //! clap [Args](clap::Args) for optimism rollup configuration +use std::path::PathBuf; use reth_optimism_node::args::RollupArgs; @@ -42,4 +43,21 @@ pub struct OpRbuilderArgs { /// Signals whether to log pool transaction events #[arg(long = "builder.log-pool-transactions", default_value = "false")] pub log_pool_transactions: bool, + + #[arg( + long = "builder.playground", + num_args = 0..=1, + default_missing_value = "$HOME/.playground/devnet/", + value_parser = expand_path, + env = "PLAYGROUND_DIR", + )] + pub playground: Option, +} + +fn expand_path(s: &str) -> Result { + shellexpand::full(s) + .map_err(|e| format!("expansion error for `{}`: {}", s, e))? + .into_owned() + .parse() + .map_err(|e| format!("invalid path after expansion: {}", e)) } diff --git a/crates/op-rbuilder/src/args/playground.rs b/crates/op-rbuilder/src/args/playground.rs new file mode 100644 index 000000000..1768faabc --- /dev/null +++ b/crates/op-rbuilder/src/args/playground.rs @@ -0,0 +1,408 @@ +//! Autmoatic builder playground configuration. +//! +//! This module is used mostly for testing purposes. It allows op-rbuilder to +//! automatically configure itself to run against a running op-builder playground. +//! +//! To setup the playground, checkout this repository: +//! +//! https://github.com/SozinM/builder-playground/ +//! +//! and then switch to the `msozin/feat/flashblocks` branch. +//! +//! Then run the following command: +//! +//! go run main.go cook opstack --external-builder http://host.docker.internal:4444 +//! +//! Wait until the playground is up and running, then run the following command to build +//! op-rbuilder with flashblocks support: +//! +//! cargo build --features flashblocks --bin op-rbuilder -p op-rbuilder +//! +//! then run the following command to start op-rbuilder against the playground: +//! +//! target/debug/op-rbuilder node --builder.playground +//! +//! This will automatically try to detect the playground configuration and apply +//! it to the op-rbuilder startup settings. +//! +//! Optionally you can specify the `--builder.playground` flag with a different +//! directory to use. This is useful for testing against different playground +//! configurations. + +use super::OpRbuilderArgs; +use alloy_primitives::hex; +use clap::{parser::ValueSource, CommandFactory}; +use core::{ + net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, + ops::Range, + time::Duration, +}; +use eyre::{eyre, Result}; +use reth_cli::chainspec::ChainSpecParser; +use reth_network_peers::TrustedPeer; +use reth_optimism_chainspec::OpChainSpec; +use reth_optimism_cli::{chainspec::OpChainSpecParser, commands::Commands}; +use secp256k1::SecretKey; +use serde_json::Value; +use std::{ + fs::read_to_string, + path::{Path, PathBuf}, + sync::Arc, +}; +use url::{Host, Url}; + +/// This trait is used to extend Reth's CLI with additional functionality that +/// populates the default values for the command line arguments when the user +/// specifies that they want to use the playground. +/// +/// The `--builder.playground` flag is used to populate the CLI arguments with +/// default values for running the builder against the playground environment. +/// +/// The values are populated from the default directory of the playground +/// configuration, which is `$HOME/.playground/devnet/` by default. +/// +/// Any manually specified CLI arguments by the user will override the defaults. +pub trait CliExt { + /// Populates the default values for the CLI arguments when the user specifies + /// the `--builder.playground` flag. + fn populate_defaults(self) -> Self; +} + +type Cli = reth_optimism_cli::Cli; + +impl CliExt for Cli { + fn populate_defaults(self) -> Self { + let Commands::Node(ref node_command) = self.command else { + // playground defaults are only relevant if running the node commands. + return self; + }; + + let Some(ref playground_dir) = node_command.ext.playground else { + // not running in playground mode. + return self; + }; + + let options = match PlaygroundOptions::new(playground_dir) { + Ok(options) => options, + Err(e) => exit(e), + }; + + options.apply(self) + } +} + +struct PlaygroundOptions { + /// Sets node.chain in NodeCommand + pub chain: Arc, + + /// Sets node.rpc.http_port in NodeCommand + pub http_port: u16, + + /// Sets node.rpc.auth_addr in NodeCommand + pub authrpc_addr: IpAddr, + + /// Sets node.rpc.authrpc_port in NodeCommand + pub authrpc_port: u16, + + /// Sets node.rpc.authrpc_jwtsecret in NodeCommand + pub authrpc_jwtsecret: PathBuf, + + /// Sets node.network.port in NodeCommand + pub port: u16, + + /// Sets the node.network.trusted_peers in NodeCommand + pub trusted_peer: TrustedPeer, + + /// Sets node.ext.flashblocks_ws_url in NodeCommand + pub flashblocks_ws_addr: SocketAddr, + + /// Sets node.ext.flashblock_block_time in NodeCommand + pub chain_block_time: Duration, +} + +impl PlaygroundOptions { + /// Creates a new `PlaygroundOptions` instance with the specified genesis path. + pub fn new(path: &Path) -> Result { + if !path.exists() { + return Err(eyre!( + "Playground data directory {} does not exist", + path.display() + )); + } + + let chain = OpChainSpecParser::parse(&existing_path(path, "l2-genesis.json")?)?; + + let authrpc_addr = Ipv4Addr::UNSPECIFIED.into(); + let http_port = pick_preferred_port(2222, 3000..9999); + let authrpc_jwtsecret = existing_path(path, "jwtsecret")?.into(); + let port = pick_preferred_port(30333, 30000..65535); + let chain_block_time = extract_chain_block_time(path)?; + let flashblocks_ws_addr = extract_flashblocks_ws_addr(path)?; + let authrpc_port = extract_authrpc_port(path)?; + let trusted_peer = TrustedPeer::from_secret_key( + Host::Ipv4(Ipv4Addr::LOCALHOST), + extract_trusted_peer_port(path)?, + &extract_deterministic_p2p_key(path)?, + ); + + Ok(Self { + chain, + http_port, + authrpc_addr, + authrpc_port, + authrpc_jwtsecret, + port, + trusted_peer, + flashblocks_ws_addr, + chain_block_time, + }) + } + + pub fn apply(self, cli: Cli) -> Cli { + let mut cli = cli; + let Commands::Node(ref mut node) = cli.command else { + // playground defaults are only relevant if running the node commands. + return cli; + }; + + if !node.network.trusted_peers.contains(&self.trusted_peer) { + node.network.trusted_peers.push(self.trusted_peer); + } + + // populate the command line arguments only if they were never set by the user + // either via the command line or an environment variable. Otherwise, don't + // override the user provided values. + let matches = Cli::command().get_matches(); + let matches = matches + .subcommand_matches("node") + .expect("validated that we are in the node command"); + + if matches.value_source("chain").is_default() { + node.chain = self.chain; + } + + if matches.value_source("http").is_default() { + node.rpc.http = true; + } + + if matches.value_source("http_port").is_default() { + node.rpc.http_port = self.http_port; + } + + if matches.value_source("port").is_default() { + node.network.port = self.port; + } + + if matches.value_source("auth_addr").is_default() { + node.rpc.auth_addr = self.authrpc_addr; + } + + if matches.value_source("auth_port").is_default() { + node.rpc.auth_port = self.authrpc_port; + } + + if matches.value_source("auth_jwtsecret").is_default() { + node.rpc.auth_jwtsecret = Some(self.authrpc_jwtsecret); + } + + if matches.value_source("disable_discovery").is_default() { + node.network.discovery.disable_discovery = true; + } + + if matches.value_source("flashblocks_ws_url").is_default() { + node.ext.flashblocks_ws_url = self.flashblocks_ws_addr.to_string(); + } + + if matches.value_source("chain_block_time").is_default() { + node.ext.chain_block_time = self.chain_block_time.as_millis() as u64; + } + + cli + } +} + +/// Following clap's convention, a failure to parse the command line arguments +/// will result in terminating the program with a non-zero exit code. +fn exit(error: eyre::Report) -> ! { + eprintln!("{error}"); + std::process::exit(-1); +} + +fn existing_path(base: &Path, relative: &str) -> Result { + let path = base.join(relative); + if path.exists() { + Ok(path.to_string_lossy().to_string()) + } else { + Err(eyre::eyre!( + "Expected file {relative} is not present in playground directory {}", + base.display() + )) + } +} + +fn pick_random_port(range: Range) -> u16 { + use rand::Rng; + let mut rng = rand::rng(); + + loop { + // Generate a random port number between 30000 and 65535 + let port = rng.random_range(range.clone()); + + // Check if the port is already in use + let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port); + if std::net::TcpListener::bind(socket).is_ok() { + return port; + } + } +} + +fn pick_preferred_port(preferred: u16, fallback_range: Range) -> u16 { + if !is_port_free(preferred) { + return pick_random_port(fallback_range); + } + + preferred +} + +fn is_port_free(port: u16) -> bool { + let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port); + std::net::TcpListener::bind(socket).is_ok() +} + +fn extract_chain_block_time(basepath: &Path) -> Result { + Ok(Duration::from_secs( + serde_json::from_str::(&read_to_string(existing_path(basepath, "rollup.json")?)?)? + .get("block_time") + .and_then(|v| v.as_u64()) + .ok_or_else(|| eyre::eyre!("Missing chain_block_time in rollup.json"))?, + )) +} + +fn extract_deterministic_p2p_key(basepath: &Path) -> Result { + let key = read_to_string(existing_path(basepath, "deterministic_p2p_key.txt")?)?; + Ok(SecretKey::from_slice( + &hex::decode(key).map_err(|e| eyre!("Invalid hex key: {e}"))?, + )?) +} + +fn read_docker_compose(basepath: &Path) -> Result { + // this happens only once on statup so it's fine to read the file multiple times + let docker_compose = read_to_string(existing_path(basepath, "docker-compose.yaml")?)?; + serde_yaml::from_str(&docker_compose).map_err(|e| eyre!("Invalid docker-compose file: {e}")) +} + +fn extract_service_command_flag(basepath: &Path, service: &str, flag: &str) -> Result { + let docker_compose = read_docker_compose(basepath)?; + let args = docker_compose["services"][service]["command"] + .as_sequence() + .ok_or(eyre!( + "docker-compose.yaml is missing command line arguments for {service}" + ))? + .iter() + .map(|s| { + s.as_str().ok_or_else(|| { + eyre!("docker-compose.yaml service command line argument is not a string") + }) + }) + .collect::>>()?; + + let index = args + .iter() + .position(|arg| *arg == flag) + .ok_or_else(|| eyre!("docker_compose: {flag} not found on {service} service"))?; + + let value = args + .get(index + 1) + .ok_or_else(|| eyre!("docker_compose: {flag} value not found"))?; + + Ok(value.to_string()) +} + +fn extract_flashblocks_ws_addr(basepath: &Path) -> Result { + extract_service_command_flag(basepath, "rollup-boost", "--flashblocks-url") + .and_then(|url_str| Url::parse(&url_str).map_err(|e| eyre!("Invalid flashblocks-url: {e}"))) + .and_then(|url| { + url.port() + .ok_or_else(|| eyre!("Invalid flashblocks-url: missing port")) + }) + .map(|port| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port))) +} + +fn extract_authrpc_port(basepath: &Path) -> Result { + let builder_url = extract_service_command_flag(basepath, "rollup-boost", "--builder-url")?; + let url = Url::parse(&builder_url).map_err(|e| eyre!("Invalid builder-url: {e}"))?; + url.port().ok_or_else(|| eyre!("missing builder-url port")) +} + +fn extract_trusted_peer_port(basepath: &Path) -> Result { + let docker_compose = read_docker_compose(basepath)?; + + // first we need to find the internal port of the op-geth service from the docker-compose.yaml + // command line arguments used to start the op-geth service + + let Some(opgeth_args) = docker_compose["services"]["op-geth"]["command"][1].as_str() else { + return Err(eyre!( + "docker-compose.yaml is missing command line arguments for op-geth" + )); + }; + + let opgeth_args = opgeth_args.split_whitespace().collect::>(); + let port_param_position = opgeth_args + .iter() + .position(|arg| *arg == "--port") + .ok_or_else(|| eyre!("docker_compose: --port param not found on op-geth service"))?; + + let port_value = opgeth_args + .get(port_param_position + 1) + .ok_or_else(|| eyre!("docker_compose: --port value not found"))?; + + let port_value = port_value + .parse::() + .map_err(|e| eyre!("Invalid port value: {e}"))?; + + // now we need to find the external port of the op-geth service from the docker-compose.yaml + // ports mapping used to start the op-geth service + let Some(opgeth_ports) = docker_compose["services"]["op-geth"]["ports"].as_sequence() else { + return Err(eyre!( + "docker-compose.yaml is missing ports mapping for op-geth" + )); + }; + let ports_mapping = opgeth_ports + .iter() + .map(|s| { + s.as_str().ok_or_else(|| { + eyre!("docker-compose.yaml service ports mapping in op-geth is not a string") + }) + }) + .collect::>>()?; + + // port mappings is in the format [..., "127.0.0.1:30304:30303", ...] + // we need to find the mapping that contains the port value we found earlier + // and extract the external port from it + let port_mapping = ports_mapping + .iter() + .find(|mapping| mapping.contains(&format!(":{port_value}"))) + .ok_or_else(|| { + eyre!("docker_compose: external port mapping not found for {port_value} for op-geth") + })?; + + // extract the external port from the mapping + let port_mapping = port_mapping + .split(':') + .nth(1) + .ok_or_else(|| eyre!("docker_compose: external port mapping for op-geth is not valid"))?; + + port_mapping + .parse::() + .map_err(|e| eyre!("Invalid external port mapping value for op-geth: {e}")) +} + +trait IsDefaultSource { + fn is_default(&self) -> bool; +} + +impl IsDefaultSource for Option { + fn is_default(&self) -> bool { + matches!(self, Some(ValueSource::DefaultValue)) || self.is_none() + } +} diff --git a/crates/op-rbuilder/src/main.rs b/crates/op-rbuilder/src/main.rs index f08b2ce01..b6ee20ea5 100644 --- a/crates/op-rbuilder/src/main.rs +++ b/crates/op-rbuilder/src/main.rs @@ -1,3 +1,4 @@ +use args::CliExt; use clap::Parser; use reth_optimism_cli::{chainspec::OpChainSpecParser, Cli}; use reth_optimism_node::node::OpAddOnsBuilder; @@ -33,6 +34,7 @@ static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; fn main() { Cli::::parse() + .populate_defaults() .run(|builder, builder_args| async move { let rollup_args = builder_args.rollup_args; diff --git a/crates/op-rbuilder/src/payload_builder.rs b/crates/op-rbuilder/src/payload_builder.rs index b39057e51..6937d87c4 100644 --- a/crates/op-rbuilder/src/payload_builder.rs +++ b/crates/op-rbuilder/src/payload_builder.rs @@ -692,7 +692,7 @@ where // withdrawals root field in block header is used for storage root of L2 predeploy // `l2tol1-message-passer` Some( - isthmus::withdrawals_root(&execution_outcome.state(), state.database.as_ref()) + isthmus::withdrawals_root(execution_outcome.state(), state.database.as_ref()) .map_err(PayloadBuilderError::other)?, ) } else if ctx From 7c05405b8ac9679d33afa5e7aba43a68fd97d59b Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Wed, 14 May 2025 08:24:24 +0000 Subject: [PATCH 3/6] Fixed build warning --- crates/op-rbuilder/src/payload_builder_vanilla.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/op-rbuilder/src/payload_builder_vanilla.rs b/crates/op-rbuilder/src/payload_builder_vanilla.rs index 407593b05..79cb73ea5 100644 --- a/crates/op-rbuilder/src/payload_builder_vanilla.rs +++ b/crates/op-rbuilder/src/payload_builder_vanilla.rs @@ -1,7 +1,7 @@ use crate::{ generator::{BlockCell, BlockPayloadJobGenerator, BuildArguments, PayloadBuilder}, metrics::OpRBuilderMetrics, - primitives::reth::{ExecutedPayload, ExecutionInfo}, + primitives::reth::ExecutionInfo, tx_signer::Signer, }; use alloy_consensus::{ @@ -72,6 +72,13 @@ use tracing::*; // From https://eips.ethereum.org/EIPS/eip-7623 const TOTAL_COST_FLOOR_PER_TOKEN: u64 = 10; +/// Holds the state after execution +#[derive(Debug)] +pub struct ExecutedPayload { + /// Tracked execution info + pub info: ExecutionInfo, +} + #[derive(Debug, Clone, Default)] #[non_exhaustive] pub struct CustomOpPayloadBuilder { From 10ac57e9bd3f9cb2b5e2d9d2ef0531e2ecce029e Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Wed, 14 May 2025 08:52:05 +0000 Subject: [PATCH 4/6] added justfile recepe --- justfile | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 justfile diff --git a/justfile b/justfile new file mode 100644 index 000000000..81f77d2b5 --- /dev/null +++ b/justfile @@ -0,0 +1,3 @@ +run-playground: + cargo build --features flashblocks --bin op-rbuilder -p op-rbuilder + ./target/debug/op-rbuilder node --builder.playground \ No newline at end of file From e5deeb4adc575ffe0c61bfcfe74f029d828e7839 Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Wed, 14 May 2025 21:32:50 +0000 Subject: [PATCH 5/6] Removing flashblocks and using vanilla builder for now --- crates/op-rbuilder/src/args/playground.rs | 29 ++++------------------- justfile | 2 +- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/crates/op-rbuilder/src/args/playground.rs b/crates/op-rbuilder/src/args/playground.rs index 1768faabc..6898eb9e6 100644 --- a/crates/op-rbuilder/src/args/playground.rs +++ b/crates/op-rbuilder/src/args/playground.rs @@ -5,9 +5,7 @@ //! //! To setup the playground, checkout this repository: //! -//! https://github.com/SozinM/builder-playground/ -//! -//! and then switch to the `msozin/feat/flashblocks` branch. +//! https://github.com/flashbots/builder-playground //! //! Then run the following command: //! @@ -16,7 +14,7 @@ //! Wait until the playground is up and running, then run the following command to build //! op-rbuilder with flashblocks support: //! -//! cargo build --features flashblocks --bin op-rbuilder -p op-rbuilder +//! cargo build --bin op-rbuilder -p op-rbuilder //! //! then run the following command to start op-rbuilder against the playground: //! @@ -33,7 +31,7 @@ use super::OpRbuilderArgs; use alloy_primitives::hex; use clap::{parser::ValueSource, CommandFactory}; use core::{ - net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}, + net::{IpAddr, Ipv4Addr, SocketAddr}, ops::Range, time::Duration, }; @@ -113,9 +111,6 @@ struct PlaygroundOptions { /// Sets the node.network.trusted_peers in NodeCommand pub trusted_peer: TrustedPeer, - /// Sets node.ext.flashblocks_ws_url in NodeCommand - pub flashblocks_ws_addr: SocketAddr, - /// Sets node.ext.flashblock_block_time in NodeCommand pub chain_block_time: Duration, } @@ -137,7 +132,6 @@ impl PlaygroundOptions { let authrpc_jwtsecret = existing_path(path, "jwtsecret")?.into(); let port = pick_preferred_port(30333, 30000..65535); let chain_block_time = extract_chain_block_time(path)?; - let flashblocks_ws_addr = extract_flashblocks_ws_addr(path)?; let authrpc_port = extract_authrpc_port(path)?; let trusted_peer = TrustedPeer::from_secret_key( Host::Ipv4(Ipv4Addr::LOCALHOST), @@ -153,7 +147,6 @@ impl PlaygroundOptions { authrpc_jwtsecret, port, trusted_peer, - flashblocks_ws_addr, chain_block_time, }) } @@ -209,10 +202,6 @@ impl PlaygroundOptions { node.network.discovery.disable_discovery = true; } - if matches.value_source("flashblocks_ws_url").is_default() { - node.ext.flashblocks_ws_url = self.flashblocks_ws_addr.to_string(); - } - if matches.value_source("chain_block_time").is_default() { node.ext.chain_block_time = self.chain_block_time.as_millis() as u64; } @@ -279,7 +268,7 @@ fn extract_chain_block_time(basepath: &Path) -> Result { } fn extract_deterministic_p2p_key(basepath: &Path) -> Result { - let key = read_to_string(existing_path(basepath, "deterministic_p2p_key.txt")?)?; + let key = read_to_string(existing_path(basepath, "enode-key-1.txt")?)?; Ok(SecretKey::from_slice( &hex::decode(key).map_err(|e| eyre!("Invalid hex key: {e}"))?, )?) @@ -318,16 +307,6 @@ fn extract_service_command_flag(basepath: &Path, service: &str, flag: &str) -> R Ok(value.to_string()) } -fn extract_flashblocks_ws_addr(basepath: &Path) -> Result { - extract_service_command_flag(basepath, "rollup-boost", "--flashblocks-url") - .and_then(|url_str| Url::parse(&url_str).map_err(|e| eyre!("Invalid flashblocks-url: {e}"))) - .and_then(|url| { - url.port() - .ok_or_else(|| eyre!("Invalid flashblocks-url: missing port")) - }) - .map(|port| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, port))) -} - fn extract_authrpc_port(basepath: &Path) -> Result { let builder_url = extract_service_command_flag(basepath, "rollup-boost", "--builder-url")?; let url = Url::parse(&builder_url).map_err(|e| eyre!("Invalid builder-url: {e}"))?; diff --git a/justfile b/justfile index 81f77d2b5..608ef9c77 100644 --- a/justfile +++ b/justfile @@ -1,3 +1,3 @@ run-playground: - cargo build --features flashblocks --bin op-rbuilder -p op-rbuilder + cargo build --bin op-rbuilder -p op-rbuilder ./target/debug/op-rbuilder node --builder.playground \ No newline at end of file From 1894fb4b79007de691a9fc65bbee5cb88aeb30d4 Mon Sep 17 00:00:00 2001 From: Karim Agha Date: Thu, 15 May 2025 08:41:42 +0000 Subject: [PATCH 6/6] Updated readme --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index 741b46a24..c0d1f1230 100644 --- a/README.md +++ b/README.md @@ -109,3 +109,40 @@ cargo run -- spam ./scenarios/simple.toml http://localhost:2222 --tpb 10 --durat ``` And you should start to see blocks being built and landed on-chain with `contender` transactions. + + +## Builder playground + +You can quickly spin up an op-stack devnet using [builder-playground](https://github.com/flashbots/builder-playground). The quickest workflow to get op-stack running against your local `op-rbuilder` instance is: + +1. Check out the builder playground repo + +``` +git clone git@github.com:flashbots/builder-playground.git +``` + +2. In the builder-playgound spin up an l2 opstack setup specifying that it should use an external block builder: + +``` +go run main.go cook opstack --external-builder http://host.docker.internal:4444 +``` + +3. Run rbuilder in playground mode: + +``` +cargo run --bin op-rbuilder -- node --builder.playground +``` + +You could also run it using: + +``` +just run-playground +``` + + +This will automatically try to detect all settings and ports from the currently running playground. Sometimes you might need to clean up the builder-playground state between runs. This can be done using: + +``` +rm -rf ~/.local/share/reth +sudo rm -rf ~/.playground +``` \ No newline at end of file