diff --git a/Cargo.lock b/Cargo.lock index 9db8e318..9a5d8a21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1929,6 +1929,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "signet-bundle", "signet-constants", "signet-sim", "signet-tx-cache", diff --git a/Cargo.toml b/Cargo.toml index 42aed8f8..6ea96301 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,12 +26,14 @@ init4-bin-base = { git = "https://github.com/init4tech/bin-base.git", branch = " # signet-constants = { version = "0.15.0" } # signet-sim = { version = "0.15.0" } # signet-tx-cache = { version = "0.15.0" } +# signet-bundle = { version = "0.15.0" } # signet-types = { version = "0.15.0" } # signet-zenith = { version = "0.15.0" } signet-constants = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } signet-sim = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } signet-tx-cache = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } +signet-bundle = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } signet-types = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } signet-zenith = { git = "https://github.com/init4tech/signet-sdk.git", branch = "dylan/logs-sim-env-errors" } diff --git a/bin/builder.rs b/bin/builder.rs index f8378d58..e3c100bf 100644 --- a/bin/builder.rs +++ b/bin/builder.rs @@ -14,10 +14,19 @@ use tokio::select; async fn main() -> eyre::Result<()> { let _guard = init4_bin_base::init4(); let init_span_guard = info_span!("builder initialization"); - builder::config_from_env(); + let config = builder::config_from_env(); + + let (host_provider, ru_provider, quincey) = tokio::try_join!( + config.connect_host_provider(), + config.connect_ru_provider(), + config.connect_quincey(), + )?; // Set up env and metrics tasks - let (env_task, metrics_task) = tokio::try_join!(EnvTask::new(), MetricsTask::new())?; + let (env_task, metrics_task) = tokio::try_join!( + EnvTask::new(host_provider.clone(), ru_provider.clone(), quincey), + MetricsTask::new() + )?; // Spawn the env and metrics tasks let (block_env, env_jh) = env_task.spawn(); @@ -25,8 +34,8 @@ async fn main() -> eyre::Result<()> { // Set up the cache, submit, and simulator tasks let cache_tasks = CacheTasks::new(block_env.clone()); - let (submit_task, simulator_task) = - tokio::try_join!(FlashbotsTask::new(tx_channel.clone()), SimulatorTask::new(block_env),)?; + let submit_task = FlashbotsTask::new(tx_channel.clone()).await?; + let simulator_task = SimulatorTask::new(block_env, host_provider.clone(), ru_provider.clone()); // Spawn the cache, submit, and simulator tasks let cache_system = cache_tasks.spawn(); diff --git a/src/lib.rs b/src/lib.rs index b727a412..1ce39f27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,8 +34,10 @@ pub mod utils; pub mod test_utils; use init4_bin_base::utils::from_env::FromEnv; + // Anonymous import suppresses warnings about unused imports. use openssl as _; +use signet_bundle as _; use signet_constants::SignetSystemConstants; use std::sync::OnceLock; diff --git a/src/tasks/block/sim.rs b/src/tasks/block/sim.rs index 1674de42..61a7b169 100644 --- a/src/tasks/block/sim.rs +++ b/src/tasks/block/sim.rs @@ -81,13 +81,13 @@ pub struct SimulatorTask { impl SimulatorTask { /// Create a new `SimulatorTask` instance. This task must be spawned to /// begin processing incoming block environments. - pub async fn new(envs: watch::Receiver>) -> eyre::Result { + pub fn new( + envs: watch::Receiver>, + host_provider: HostProvider, + ru_provider: RuProvider, + ) -> Self { let config = crate::config(); - - let (host_provider, ru_provider) = - tokio::try_join!(config.connect_host_provider(), config.connect_ru_provider())?; - - Ok(Self { config, host_provider, ru_provider, envs }) + Self { config, host_provider, ru_provider, envs } } /// Get the slot calculator. @@ -220,7 +220,6 @@ impl SimulatorTask { else { continue; }; - span_debug!(span, tx_count = block.transactions().len(), "built simulated block"); let _ = submit_sender.send(SimResult { block, sim_env }); } diff --git a/src/tasks/env.rs b/src/tasks/env.rs index 91014e11..feb5c567 100644 --- a/src/tasks/env.rs +++ b/src/tasks/env.rs @@ -14,7 +14,7 @@ use signet_constants::SignetSystemConstants; use signet_sim::{HostEnv, RollupEnv}; use tokio::{sync::watch, task::JoinHandle}; use tokio_stream::StreamExt; -use tracing::{Instrument, Span, info_span}; +use tracing::{Instrument, Span, info_span, instrument}; use trevm::revm::{ context::BlockEnv, context_interface::block::BlobExcessGasAndPrice, @@ -202,20 +202,18 @@ pub struct EnvTask { impl EnvTask { /// Create a new [`EnvTask`] with the given config and providers. - pub async fn new() -> eyre::Result { + pub async fn new( + host_provider: HostProvider, + ru_provider: RuProvider, + quincey: Quincey, + ) -> eyre::Result { let config = crate::config(); - - let (host_provider, quincey, ru_provider) = tokio::try_join!( - config.connect_host_provider(), - config.connect_quincey(), - config.connect_ru_provider(), - )?; - Ok(Self { config, host_provider, quincey, ru_provider }) } /// Construct a [`BlockEnv`] for the next host block from the previous host header. - fn construct_host_env(&self, previous: Header) -> Environment { + #[instrument(skip(self, previous), fields(previous_number = %previous.number))] + pub fn construct_host_env(&self, previous: Header) -> Environment { let env = BlockEnv { number: U256::from(previous.number + 1), beneficiary: self.config.builder_rewards_address, @@ -236,7 +234,8 @@ impl EnvTask { } /// Construct a [`BlockEnv`] for the next rollup block from the previous block header. - fn construct_rollup_env(&self, previous: Header) -> Environment { + #[instrument(skip(self, previous), fields(previous_number = %previous.number))] + pub fn construct_rollup_env(&self, previous: Header) -> Environment { let env = BlockEnv { number: U256::from(previous.number + 1), beneficiary: self.config.builder_rewards_address, diff --git a/src/test_utils.rs b/src/test_utils.rs index 143d1991..36e22378 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -36,7 +36,7 @@ pub fn setup_test_config() -> &'static BuilderConfig { flashbots_endpoint: "https://relay-sepolia.flashbots.net:443".parse().unwrap(), quincey_url: "http://localhost:8080".into(), sequencer_key: None, - builder_key: env::var("SEPOLIA_ETH_PRIV_KEY") + builder_key: env::var("BUILDER_KEY") .unwrap_or_else(|_| B256::repeat_byte(0x42).to_string()), builder_port: 8080, builder_rewards_address: Address::default(), @@ -52,7 +52,7 @@ pub fn setup_test_config() -> &'static BuilderConfig { concurrency_limit: None, // NB: Defaults to available parallelism slot_calculator: SlotCalculator::new( 1740681556, // pecorino start timestamp as sane default - 0, 1, + 0, 12, ), block_query_cutoff_buffer: 3000, max_host_gas_coefficient: Some(80), @@ -64,14 +64,15 @@ pub fn setup_test_config() -> &'static BuilderConfig { /// Returns a new signed test transaction with the provided nonce, value, and mpfpg. pub fn new_signed_tx( wallet: &PrivateKeySigner, + chain_id: u64, nonce: u64, value: U256, mpfpg: u128, ) -> Result { let tx = TxEip1559 { - chain_id: 11155111, + chain_id, nonce, - max_fee_per_gas: 10_000_000, + max_fee_per_gas: 50_000_000_000, max_priority_fee_per_gas: mpfpg, to: TxKind::Call(Address::from_str("0x0000000000000000000000000000000000000000").unwrap()), value, @@ -91,8 +92,7 @@ pub fn setup_logging() { let _ = registry.try_init(); } -/// Returns a Pecorino block environment for simulation with the timestamp set to `finish_by`, -/// the block number set to latest + 1, system gas configs, and a beneficiary address. +/// Create a test BlockEnv with the provided parameters pub fn test_block_env(number: u64, basefee: u64, timestamp: u64) -> BlockEnv { let config = setup_test_config(); BlockEnv { diff --git a/tests/block_builder_test.rs b/tests/block_builder_test.rs index d972cc13..477716dc 100644 --- a/tests/block_builder_test.rs +++ b/tests/block_builder_test.rs @@ -1,17 +1,30 @@ //! Tests for the block building task. use alloy::{ - eips::BlockId, node_bindings::Anvil, primitives::U256, providers::Provider, + eips::{BlockId, Encodable2718}, + node_bindings::{Anvil, AnvilInstance}, + primitives::{B256, U256}, + providers::{ + Provider, ProviderBuilder, RootProvider, + fillers::{BlobGasFiller, SimpleNonceManager}, + }, + rpc::types::mev::EthSendBundle, signers::local::PrivateKeySigner, }; use builder::{ + config::{HostProvider, RuProvider}, + constants, tasks::{ block::sim::SimulatorTask, - env::{EnvTask, Environment, SimEnv}, + env::{EnvTask, SimEnv}, }, - test_utils::{new_signed_tx, setup_logging, setup_test_config, test_block_env}, + test_utils::{new_signed_tx, setup_logging, setup_test_config}, }; + +use signet_bundle::SignetEthBundle; use signet_sim::SimCache; +use tokio::sync::{mpsc::unbounded_channel, watch::channel}; + use std::time::{Duration, Instant}; /// Tests the `handle_build` method of the `SimulatorTask`. @@ -19,54 +32,163 @@ use std::time::{Duration, Instant}; /// This test sets up a simulated environment using Anvil, creates a block builder, /// and verifies that the block builder can successfully build a block containing /// transactions from multiple senders. -#[ignore = "integration test"] #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_handle_build() { setup_logging(); + setup_test_config(); - // Make a test config - let config = setup_test_config(); + // Setup quincey + let quincey = builder::config().connect_quincey().await.unwrap(); - // Create an anvil instance for testing - let anvil_instance = Anvil::new().chain_id(signet_constants::pecorino::RU_CHAIN_ID).spawn(); + // Setup host provider + let (_host_anvil, host_provider, _host_signer) = spawn_host_anvil(); - // Create a wallet - let keys = anvil_instance.keys(); - let test_key_0 = PrivateKeySigner::from_signing_key(keys[0].clone().into()); - let test_key_1 = PrivateKeySigner::from_signing_key(keys[1].clone().into()); + // Setup rollup provider + let (_ru_anvil, ru_provider, rollup_signers) = spawn_rollup_anvil(); + let mut rollup_signers = rollup_signers.into_iter(); + let rollup_key = rollup_signers.next().unwrap(); + let rollup_key_two = rollup_signers.next().unwrap(); - let block_env = EnvTask::new().await.unwrap().spawn().0; + // Setup the env task and environments + let env_task = EnvTask::new(host_provider.clone(), ru_provider.clone(), quincey).await.unwrap(); + let sim_env = latest_sim_env(&env_task, &host_provider, &ru_provider).await; - let block_builder = SimulatorTask::new(block_env).await.unwrap(); + let (block_env, _jh) = env_task.spawn(); + let block_builder = SimulatorTask::new(block_env, host_provider, ru_provider); // Setup a sim cache let sim_items = SimCache::new(); // Add two transactions from two senders to the sim cache - let tx_1 = new_signed_tx(&test_key_0, 0, U256::from(1_f64), 11_000).unwrap(); + let tx_1 = new_signed_tx(&rollup_key, constants().ru_chain_id(), 0, U256::from(1_f64), 11_000) + .unwrap(); sim_items.add_tx(tx_1, 0); - let tx_2 = new_signed_tx(&test_key_1, 0, U256::from(2_f64), 10_000).unwrap(); + let tx_2 = + new_signed_tx(&rollup_key_two, constants().ru_chain_id(), 0, U256::from(2_f64), 10_000) + .unwrap(); sim_items.add_tx(tx_2, 0); - // Setup the block envs - let finish_by = Instant::now() + Duration::from_secs(2); - - let ru_provider = builder::config().connect_ru_provider().await.unwrap(); - let ru_header = ru_provider.get_block(BlockId::latest()).await.unwrap().unwrap().header.inner; - let number = ru_header.number + 1; - let timestamp = ru_header.timestamp + config.slot_calculator.slot_duration(); - let block_env = test_block_env(number, 7, timestamp); - // Spawn the block builder task - let sim_env = SimEnv { - host: Environment::for_testing(), - rollup: Environment::new(block_env, ru_header), - span: tracing::Span::none(), - }; + let finish_by = Instant::now() + Duration::from_secs(2); let got = block_builder.handle_build(sim_items, finish_by, &sim_env).await; // Assert on the built block assert!(got.is_ok()); assert!(got.unwrap().tx_count() == 2); } + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn test_bundle_host_txns() { + setup_logging(); + setup_test_config(); + + // Setup host provider + let (_host_anvil, host_provider, host_signer) = spawn_host_anvil(); + let host_chain_id = host_provider.get_chain_id().await.expect("gets host chain id"); + assert_eq!(host_chain_id, constants().host_chain_id()); + + // Setup rollup provider + let (_ru_anvil, ru_provider, rollup_signers) = spawn_rollup_anvil(); + let rollup_signer = rollup_signers.into_iter().next().unwrap(); + let ru_chain_id = ru_provider.get_chain_id().await.expect("gets ru chain id"); + assert_eq!(ru_chain_id, constants().ru_chain_id()); + + let quincey = builder::config().connect_quincey().await.unwrap(); + + // Setup the simulation environments + let env_task = EnvTask::new(host_provider.clone(), ru_provider.clone(), quincey).await.unwrap(); + let sim_env = latest_sim_env(&env_task, &host_provider, &ru_provider).await; + + // Create a simulation environment and plumbing + let (sim_tx, sim_rx) = channel::>(None); + + // Create a host and rollup transaction + let ru_txn = new_signed_tx(&rollup_signer, ru_chain_id, 0, U256::from(1), 10_000) + .unwrap() + .encoded_2718() + .into(); + + let host_txn = new_signed_tx(&host_signer, host_chain_id, 0, U256::from(1), 10_000) + .unwrap() + .encoded_2718() + .into(); + + // Make a bundle out of them + let bundle = SignetEthBundle { + bundle: EthSendBundle { + replacement_uuid: Some("test-replacement-uuid".to_string()), + txs: vec![ru_txn], + block_number: sim_env.rollup_block_number(), + ..Default::default() + }, + host_txs: vec![host_txn], + }; + + // Add it to the sim cache + let sim_items = SimCache::new(); + sim_items.add_bundle(bundle, 7).expect("adds bundle"); + + // Setup the simulator environment + let (submit_tx, mut submit_rx) = unbounded_channel(); + let simulator_task = SimulatorTask::new(sim_rx, host_provider, ru_provider); + let simulator_jh = simulator_task.spawn_simulator_task(sim_items, submit_tx); + + // Send a new environment to tick the block builder simulation loop off + sim_tx.send(Some(sim_env)).unwrap(); + + // Wait for a result and assert on it + let got = submit_rx.recv().await.expect("built block"); + dbg!(&got.block); + assert_eq!(got.block.transactions().len(), 1); + assert_eq!(got.block.host_transactions().len(), 1); + + // Cleanup + simulator_jh.abort(); +} + +async fn latest_sim_env( + env_task: &EnvTask, + host_provider: &HostProvider, + ru_provider: &RuProvider, +) -> SimEnv { + let host_previous = + host_provider.get_block(BlockId::latest()).await.unwrap().unwrap().header.inner; + let ru_previous = ru_provider.get_block(BlockId::latest()).await.unwrap().unwrap().header.inner; + + let host_env = env_task.construct_host_env(host_previous); + let ru_env = env_task.construct_rollup_env(ru_previous); + + SimEnv { host: host_env, rollup: ru_env, span: tracing::Span::none() } +} + +fn spawn_host_anvil() -> (AnvilInstance, HostProvider, PrivateKeySigner) { + let anvil = Anvil::new().chain_id(constants().host_chain_id()).spawn(); + let key = anvil.keys()[0].clone(); + let signer = PrivateKeySigner::from_bytes(&B256::from_slice(&key.to_bytes())).unwrap(); + let wallet = anvil.wallet().expect("anvil wallet"); + let provider = ProviderBuilder::new_with_network() + .disable_recommended_fillers() + .filler(BlobGasFiller) + .with_gas_estimation() + .with_nonce_management(SimpleNonceManager::default()) + .fetch_chain_id() + .wallet(wallet) + .connect_http(anvil.endpoint_url()); + + (anvil, provider, signer) +} + +fn spawn_rollup_anvil() -> (AnvilInstance, RuProvider, Vec) { + let anvil = Anvil::new().chain_id(constants().ru_chain_id()).spawn(); + let signers: Vec<_> = anvil + .keys() + .iter() + .take(2) + .map(|key| PrivateKeySigner::from_bytes(&B256::from_slice(&key.to_bytes())).unwrap()) + .collect(); + assert!(signers.len() >= 2, "rollup anvil must provide at least two accounts"); + let provider = RootProvider::new_http(anvil.endpoint_url()); + + (anvil, provider, signers) +} diff --git a/tests/cache.rs b/tests/cache.rs index 5e9e7967..62e96804 100644 --- a/tests/cache.rs +++ b/tests/cache.rs @@ -9,9 +9,15 @@ use std::time::Duration; #[tokio::test] async fn test_bundle_poller_roundtrip() -> eyre::Result<()> { setup_logging(); - setup_test_config(); + let config = setup_test_config(); - let (block_env, _jh) = EnvTask::new().await?.spawn(); + let (host, rollup, quincey) = tokio::try_join!( + config.connect_host_provider(), + config.connect_ru_provider(), + config.connect_quincey(), + )?; + + let (block_env, _jh) = EnvTask::new(host, rollup, quincey).await?.spawn(); let cache_tasks = CacheTasks::new(block_env); let cache_system = cache_tasks.spawn(); diff --git a/tests/env.rs b/tests/env.rs index c11c7de3..769ea36b 100644 --- a/tests/env.rs +++ b/tests/env.rs @@ -9,7 +9,12 @@ async fn test_bundle_poller_roundtrip() -> eyre::Result<()> { setup_logging(); let _ = setup_test_config(); - let (mut env_watcher, _jh) = EnvTask::new().await?.spawn(); + let (host, rollup, quincey) = tokio::try_join!( + builder::config().connect_host_provider(), + builder::config().connect_ru_provider(), + builder::config().connect_quincey(), + )?; + let (mut env_watcher, _jh) = EnvTask::new(host, rollup, quincey).await?.spawn(); env_watcher.changed().await.unwrap(); let env = env_watcher.borrow_and_update(); diff --git a/tests/tx_poller_test.rs b/tests/tx_poller_test.rs index 075433ae..3de79439 100644 --- a/tests/tx_poller_test.rs +++ b/tests/tx_poller_test.rs @@ -5,6 +5,7 @@ use builder::{ }; // Import the refactored function use eyre::{Ok, Result}; +use signet_constants::pecorino::RU_CHAIN_ID; #[ignore = "integration test"] #[tokio::test] @@ -31,7 +32,7 @@ async fn post_tx() -> Result<()> { let client = reqwest::Client::new(); let wallet = PrivateKeySigner::random(); - let tx_envelope = new_signed_tx(&wallet, 1, U256::from(1), 10_000)?; + let tx_envelope = new_signed_tx(&wallet, RU_CHAIN_ID, 1, U256::from(1), 10_000)?; let url = format!("{}/transactions", builder::config().tx_pool_url); let response = client.post(&url).json(&tx_envelope).send().await?;