diff --git a/Cargo.lock b/Cargo.lock index d362d6e..357073d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,7 +3456,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3736,7 +3736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4726,7 +4726,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.57.0", ] [[package]] @@ -4944,9 +4944,7 @@ dependencies = [ [[package]] name = "init4-bin-base" -version = "0.18.0-rc.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300b9df7fddc42d13d9d27d8a3def2c8982b1c02771523117984fe96be127843" +version = "0.18.0-rc.8" dependencies = [ "alloy", "async-trait", @@ -5970,7 +5968,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -7070,7 +7068,7 @@ dependencies = [ "once_cell", "socket2 0.6.2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -10412,7 +10410,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -11529,7 +11527,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.3", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -12683,7 +12681,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d471c90..21c22e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ name = "zenith-builder-example" path = "bin/builder.rs" [dependencies] -init4-bin-base = { version = "0.18.0-rc.0", features = ["perms", "aws"] } +init4-bin-base = { version = "0.18.0-rc.7", features = ["perms", "aws", "pylon"] } signet-constants = { version = "0.16.0-rc.8" } signet-sim = { version = "0.16.0-rc.8" } @@ -63,7 +63,7 @@ alloy-hardforks = "0.4.0" alloy-chains = "0.2" # comment / uncomment for local dev -# [patch.crates-io] +[patch.crates-io] # signet-constants = { path = "../signet-sdk/crates/constants" } # signet-types = { path = "../signet-sdk/crates/types" } # signet-zenith = { path = "../signet-sdk/crates/zenith" } @@ -73,4 +73,4 @@ alloy-chains = "0.2" # signet-journal = { path = "../signet-sdk/crates/journal" } # signet-tx-cache = { path = "../signet-sdk/crates/tx-cache" } # signet-bundle = { path = "../signet-sdk/crates/bundle" } -# init4-bin-base = { path = "../bin-base" } +init4-bin-base = { path = "../bin-base" } diff --git a/src/config.rs b/src/config.rs index 1296ccc..79c125a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,7 +12,7 @@ use alloy::{ }; use eyre::Result; use init4_bin_base::{ - perms::{Authenticator, OAuthConfig, SharedToken}, + perms::{Authenticator, OAuthConfig, SharedToken, pylon}, utils::{ calc::SlotCalculator, from_env::FromEnv, @@ -25,6 +25,9 @@ use signet_zenith::Zenith; use std::borrow::Cow; use tokio::join; +/// Pylon client type for blob sidecar submission. +pub type PylonClient = pylon::PylonClient; + /// Type alias for the provider used to simulate against rollup state. pub type RuProvider = RootProvider; @@ -168,6 +171,10 @@ pub struct BuilderConfig { /// The signet system constants. pub constants: SignetSystemConstants, + + /// URL for the Pylon blob server API. + #[from_env(var = "PYLON_URL", desc = "URL for the Pylon blob server API")] + pub pylon_url: url::Url, } impl BuilderConfig { @@ -285,4 +292,9 @@ impl BuilderConfig { ((gas_limit as u128 * (self.max_host_gas_coefficient.unwrap_or(80) as u128)) / 100u128) as u64 } + + /// Connect to the Pylon blob server. + pub fn connect_pylon(&self) -> PylonClient { + PylonClient::new(self.pylon_url.clone(), self.oauth_token()) + } } diff --git a/src/tasks/block/sim.rs b/src/tasks/block/sim.rs index 3963dc4..ea3cfe3 100644 --- a/src/tasks/block/sim.rs +++ b/src/tasks/block/sim.rs @@ -5,7 +5,7 @@ use crate::{ config::{BuilderConfig, HostProvider, RuProvider}, tasks::env::SimEnv, }; -use alloy::consensus::Header; +use alloy::{consensus::Header, eips::Encodable2718, primitives::Bytes}; use init4_bin_base::{ deps::metrics::{counter, histogram}, utils::calc::SlotCalculator, @@ -62,6 +62,21 @@ impl SimResult { pub fn clone_span(&self) -> Span { self.sim_env.clone_span() } + + /// Constructs the MEV bundle body from host transactions and the submission transaction. + /// + /// Combines all host transactions from the rollup block with the prepared rollup block + /// submission transaction, wrapping each as a non-revertible bundle item. + /// + /// The rollup block transaction is always included and placed last in the bundle. + pub fn build_bundle_body(&self, block_tx_bytes: Bytes) -> Vec { + self.block + .host_transactions() + .iter() + .map(|tx| tx.encoded_2718().into()) + .chain(std::iter::once(block_tx_bytes)) + .collect() + } } /// A task that builds blocks based on incoming [`SimEnv`]s and a simulation diff --git a/src/tasks/submit/flashbots.rs b/src/tasks/submit/flashbots.rs index 6b3d998..caee876 100644 --- a/src/tasks/submit/flashbots.rs +++ b/src/tasks/submit/flashbots.rs @@ -1,15 +1,12 @@ //! Flashbots Task receives simulated blocks from an upstream channel and //! submits them to the Flashbots relay as bundles. use crate::{ - config::{BuilderConfig, FlashbotsProvider, HostProvider, ZenithInstance}, + config::{BuilderConfig, FlashbotsProvider, HostProvider, PylonClient, ZenithInstance}, quincey::Quincey, tasks::{block::sim::SimResult, submit::SubmitPrep}, }; use alloy::{ - consensus::TxEnvelope, - eips::Encodable2718, - primitives::{Bytes, TxHash}, - providers::ext::MevApi, + consensus::TxEnvelope, eips::Encodable2718, primitives::TxHash, providers::ext::MevApi, rpc::types::mev::EthSendBundle, }; use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws}; @@ -32,6 +29,8 @@ pub struct FlashbotsTask { signer: LocalOrAws, /// Channel for sending hashes of outbound transactions. outbound: mpsc::UnboundedSender, + /// Pylon client for blob sidecar submission. + pylon: PylonClient, } impl FlashbotsTask { @@ -48,8 +47,9 @@ impl FlashbotsTask { )?; let zenith = config.connect_zenith(host_provider); + let pylon = config.connect_pylon(); - Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, outbound }) + Ok(Self { config, quincey, zenith, flashbots, signer: builder_key, outbound, pylon }) } /// Prepares a MEV bundle from a simulation result. @@ -81,7 +81,7 @@ impl FlashbotsTask { let tx_bytes = block_tx.encoded_2718().into(); // Build the bundle body with the block_tx bytes as the last transaction in the bundle. - let txs = self.build_bundle_body(sim_result, tx_bytes); + let txs = sim_result.build_bundle_body(tx_bytes); // Create the MEV bundle (valid only in the specific host block) Ok(EthSendBundle { @@ -90,7 +90,6 @@ impl FlashbotsTask { ..Default::default() }) } - /// Prepares and signs the submission transaction for the rollup block. /// /// Creates a `SubmitPrep` instance to build the transaction, then fills @@ -130,26 +129,6 @@ impl FlashbotsTask { } } - /// Constructs the MEV bundle body from host transactions and the submission transaction. - /// - /// Combines all host transactions from the rollup block with the prepared rollup block - /// submission transaction, wrapping each as a non-revertible bundle item. - /// - /// The rollup block transaction is placed last in the bundle. - fn build_bundle_body( - &self, - sim_result: &SimResult, - tx_bytes: alloy::primitives::Bytes, - ) -> Vec { - sim_result - .block - .host_transactions() - .iter() - .map(|tx| tx.encoded_2718().into()) - .chain(std::iter::once(tx_bytes)) - .collect() - } - /// Main task loop that processes simulation results and submits bundles to Flashbots. /// /// Receives `SimResult`s from the inbound channel, prepares MEV bundles, and submits @@ -176,7 +155,6 @@ impl FlashbotsTask { // Prepare a MEV bundle with the configured call type from the sim result let result = self.prepare(&sim_result).instrument(span.clone()).await; - let bundle = match result { Ok(bundle) => bundle, Err(error) => { @@ -186,6 +164,10 @@ impl FlashbotsTask { } }; + // Due to the way the bundle is built, the block transaction is the last transaction in the bundle, and will always exist. + // We'll use this to forward the tx to pylon, which will preload the sidecar. + let block_tx = bundle.txs.last().unwrap().clone(); + // Make a child span to cover submission, or use the current span // if debug is not enabled. let _guard = span.enter(); @@ -193,9 +175,10 @@ impl FlashbotsTask { // Send the bundle to Flashbots, instrumenting the send future so // all events inside the async send are attributed to the submit - // span. + // span. If Flashbots accepts, submit envelope to Pylon. let flashbots = self.flashbots().to_owned(); let signer = self.signer.clone(); + let pylon = self.pylon.clone(); tokio::spawn( async move { @@ -209,6 +192,19 @@ impl FlashbotsTask { hash = resp.map(|r| r.bundle_hash.to_string()), "Submitted MEV bundle to Flashbots, received OK response" ); + + match pylon.post_sidecar(block_tx).await { + Ok(()) => { + counter!("signet.builder.pylon.sidecars_submitted") + .increment(1); + debug!("posted sidecar to pylon"); + } + Err(err) => { + counter!("signet.builder.pylon.submission_failures") + .increment(1); + error!(%err, "pylon submission failed"); + } + } } Err(err) => { counter!("signet.builder.flashbots.submission_failures").increment(1); diff --git a/src/test_utils.rs b/src/test_utils.rs index cb9d3e7..20119f4 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -56,6 +56,7 @@ pub fn setup_test_config() -> &'static BuilderConfig { block_query_cutoff_buffer: 3000, max_host_gas_coefficient: Some(80), constants: SignetSystemConstants::parmigiana(), + pylon_url: "http://localhost:8081".parse().unwrap(), } }) }