Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 16 additions & 33 deletions bin/builder.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use builder::{
config::BuilderConfig,
service::serve_builder,
tasks::{
block::sim::Simulator, cache::CacheTasks, env::EnvTask, metrics::MetricsTask,
block::sim::SimulatorTask, cache::CacheTasks, env::EnvTask, metrics::MetricsTask,
submit::FlashbotsTask,
},
};
use init4_bin_base::{
deps::tracing::{info, info_span},
utils::from_env::FromEnv,
};
use init4_bin_base::deps::tracing::{info, info_span};
use tokio::select;

// Note: Must be set to `multi_thread` to support async tasks.
Expand All @@ -18,46 +14,33 @@ 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();

// Pull the configuration from the environment
let config = BuilderConfig::from_env()?.clone();

// We connect the providers greedily, so we can fail early if the
// RU WS connection is invalid.
let (ru_provider, host_provider) =
tokio::try_join!(config.connect_ru_provider(), config.connect_host_provider(),)?;
let quincey = config.connect_quincey().await?;
// Set up env and metrics tasks
let (env_task, metrics_task) = tokio::try_join!(EnvTask::new(), MetricsTask::new())?;

// Spawn the EnvTask
let env_task =
EnvTask::new(config.clone(), host_provider.clone(), quincey, ru_provider.clone());
// Spawn the env and metrics tasks
let (block_env, env_jh) = env_task.spawn();
let (tx_channel, metrics_jh) = metrics_task.spawn();

// Spawn the cache system
let cache_tasks = CacheTasks::new(config.clone(), block_env.clone());
let cache_system = cache_tasks.spawn();

// Set up the metrics task
let metrics = MetricsTask::new(host_provider.clone());
let (tx_channel, metrics_jh) = metrics.spawn();
// 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),)?;

// Spawn the Flashbots task
let submit = FlashbotsTask::new(config.clone(), tx_channel).await?;
let (submit_channel, submit_jh) = submit.spawn();

// Set up the simulator
let sim = Simulator::new(&config, host_provider, ru_provider, block_env);
let build_jh = sim.spawn_simulator_task(cache_system.sim_cache, submit_channel);
// Spawn the cache, submit, and simulator tasks
let cache_system = cache_tasks.spawn();
let (submit_channel, submit_jh) = submit_task.spawn();
let build_jh = simulator_task.spawn_simulator_task(cache_system.sim_cache, submit_channel);

// Start the healthcheck server
let server = serve_builder(([0, 0, 0, 0], config.builder_port));
let server = serve_builder(([0, 0, 0, 0], builder::config().builder_port));

// We have finished initializing the builder, so we can drop the init span
// guard.
drop(init_span_guard);

select! {

_ = env_jh => {
info!("env task finished");
},
Expand Down
9 changes: 3 additions & 6 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,10 @@ impl BuilderConfig {
}

/// Connect to a Flashbots bundle provider.
pub async fn connect_flashbots(
&self,
config: &BuilderConfig,
) -> Result<FlashbotsProvider, eyre::Error> {
pub async fn connect_flashbots(&self) -> Result<FlashbotsProvider, eyre::Error> {
let endpoint =
config.flashbots_endpoint.clone().expect("flashbots endpoint must be configured");
let signer = config.connect_builder_signer().await?;
self.flashbots_endpoint.clone().expect("flashbots endpoint must be configured");
let signer = self.connect_builder_signer().await?;
let flashbots: FlashbotsProvider =
ProviderBuilder::new().wallet(signer).connect_http(endpoint);
Ok(flashbots)
Expand Down
36 changes: 36 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,41 @@ pub mod utils;
/// Test utilitites
pub mod test_utils;

use init4_bin_base::utils::from_env::FromEnv;
// Anonymous import suppresses warnings about unused imports.
use openssl as _;
use signet_constants::SignetSystemConstants;
use std::sync::OnceLock;

/// Global static configuration for the Builder binary.
pub static CONFIG: OnceLock<config::BuilderConfig> = OnceLock::new();

/// Load the Builder configuration from the environment and store it in the
/// global static CONFIG variable. Returns a reference to the configuration.
///
/// # Panics
///
/// Panics if the configuration cannot be loaded from the environment AND no
/// other configuration has been previously initialized.
pub fn config_from_env() -> &'static config::BuilderConfig {
CONFIG.get_or_init(|| config::BuilderConfig::from_env().expect("Failed to load Builder config"))
}

/// Get a reference to the global Builder configuration.
///
/// # Panics
///
/// Panics if the configuration has not been initialized.
pub fn config() -> &'static config::BuilderConfig {
CONFIG.get().expect("Builder config not initialized")
}

/// Get a reference to the Signet system constants from the global Builder
/// configuration.
///
/// # Panics
///
/// Panics if the configuration has not been initialized.
pub fn constants() -> &'static SignetSystemConstants {
&config().constants
}
10 changes: 3 additions & 7 deletions src/quincey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use alloy::{
use eyre::bail;
use init4_bin_base::{perms::SharedToken, utils::signer::LocalOrAws};
use reqwest::Client;
use signet_constants::SignetSystemConstants;
use signet_types::{SignRequest, SignResponse};
use tracing::{debug, info, instrument, trace};

Expand Down Expand Up @@ -95,15 +94,12 @@ impl Quincey {
/// Perform a preflight check to ensure that the Quincey service will
/// be able to sign a request with the provided parameters at this
/// point in time.
#[instrument(skip(self, constants))]
pub async fn preflight_check(
&self,
constants: &SignetSystemConstants,
host_block_number: u64,
) -> eyre::Result<()> {
#[instrument(skip(self))]
pub async fn preflight_check(&self, host_block_number: u64) -> eyre::Result<()> {
if self.is_local() {
return Ok(());
}
let constants = crate::constants();
let req = SignRequest {
host_block_number: U256::from(host_block_number),
host_chain_id: U256::from(constants.host_chain_id()),
Expand Down
73 changes: 31 additions & 42 deletions src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,6 @@ use tokio::{
};
use tracing::{Instrument, Span, debug, instrument};

/// `Simulator` is responsible for periodically building blocks and submitting them for
/// signing and inclusion in the blockchain. It wraps a rollup provider and a slot
/// calculator with a builder configuration.
#[derive(Debug)]
pub struct Simulator {
/// Configuration for the builder.
pub config: BuilderConfig,
/// Host Provider to interact with the host chain.
pub host_provider: HostProvider,
/// A provider that cannot sign transactions, used for interacting with the rollup.
pub ru_provider: RuProvider,
/// The block configuration environment on which to simulate
pub sim_env: watch::Receiver<Option<SimEnv>>,
}

/// SimResult bundles a BuiltBlock to the BlockEnv it was simulated against.
#[derive(Debug, Clone)]
pub struct SimResult {
Expand Down Expand Up @@ -79,25 +64,30 @@ impl SimResult {
}
}

impl Simulator {
/// Creates a new `Simulator` instance.
///
/// # Arguments
///
/// - `config`: The configuration for the builder.
/// - `ru_provider`: A provider for interacting with the rollup.
/// - `block_env`: A receiver for the block environment to simulate against.
///
/// # Returns
///
/// A new `Simulator` instance.
pub fn new(
config: &BuilderConfig,
host_provider: HostProvider,
ru_provider: RuProvider,
sim_env: watch::Receiver<Option<SimEnv>>,
) -> Self {
Self { config: config.clone(), host_provider, ru_provider, sim_env }
/// A task that builds blocks based on incoming [`SimEnv`]s and a simulation
/// cache.
#[derive(Debug)]
pub struct SimulatorTask {
/// Configuration for the builder.
config: &'static BuilderConfig,
/// Host Provider to interact with the host chain.
host_provider: HostProvider,
/// A provider that cannot sign transactions, used for interacting with the rollup.
ru_provider: RuProvider,
/// The block configuration environments on which to simulate
envs: watch::Receiver<Option<SimEnv>>,
}

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<Option<SimEnv>>) -> eyre::Result<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 })
}

/// Get the slot calculator.
Expand All @@ -110,18 +100,17 @@ impl Simulator {
&self.config.constants
}

/// Handles building a single block.
/// Build a single block
///
/// Builds a block in the block environment with items from the simulation cache
/// against the database state. When the `finish_by` deadline is reached, it
/// stops simulating and returns the block.
/// Build a block in the sim environment with items from the simulation
/// cache against the database state. When the `finish_by` deadline is
/// reached, it stops simulating and returns the block.
///
/// # Arguments
///
/// - `constants`: The system constants for the rollup.
/// - `sim_items`: The simulation cache containing transactions and bundles.
/// - `finish_by`: The deadline by which the block must be built.
/// - `block_env`: The block environment to simulate against.
/// - `sim_env`: The block environment to simulate against.
///
/// # Returns
///
Expand Down Expand Up @@ -209,11 +198,11 @@ impl Simulator {
) {
loop {
// Wait for the block environment to be set
if self.sim_env.changed().await.is_err() {
if self.envs.changed().await.is_err() {
tracing::error!("block_env channel closed - shutting down simulator task");
return;
}
let Some(sim_env) = self.sim_env.borrow_and_update().clone() else { return };
let Some(sim_env) = self.envs.borrow_and_update().clone() else { return };

let span = sim_env.span();
span_info!(span, "new block environment received");
Expand Down
33 changes: 16 additions & 17 deletions src/tasks/cache/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,33 @@ const POLL_INTERVAL_MS: u64 = 1000;
#[derive(Debug)]
pub struct BundlePoller {
/// The builder configuration values.
pub config: BuilderConfig,
config: &'static BuilderConfig,
/// Authentication module that periodically fetches and stores auth tokens.
pub token: SharedToken,
token: SharedToken,
/// Holds a Reqwest client
pub client: Client,
client: Client,
/// Defines the interval at which the bundler polls the tx-pool for bundles.
pub poll_interval_ms: u64,
poll_interval_ms: u64,
}

impl Default for BundlePoller {
fn default() -> Self {
Self::new()
}
}

/// Implements a poller for the block builder to pull bundles from the tx-pool.
impl BundlePoller {
/// Creates a new BundlePoller from the provided builder config.
pub fn new(config: &BuilderConfig, token: SharedToken) -> Self {
Self {
config: config.clone(),
token,
client: Client::new(),
poll_interval_ms: POLL_INTERVAL_MS,
}
pub fn new() -> Self {
Self::new_with_poll_interval_ms(POLL_INTERVAL_MS)
}

/// Creates a new BundlePoller from the provided builder config and with the specified poll interval in ms.
pub fn new_with_poll_interval_ms(
config: &BuilderConfig,
token: SharedToken,
poll_interval_ms: u64,
) -> Self {
Self { config: config.clone(), token, client: Client::new(), poll_interval_ms }
pub fn new_with_poll_interval_ms(poll_interval_ms: u64) -> Self {
let config = crate::config();
let token = config.oauth_token();
Self { config, token, client: Client::new(), poll_interval_ms }
}

/// Fetches bundles from the transaction cache and returns them.
Expand Down
21 changes: 8 additions & 13 deletions src/tasks/cache/system.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
use signet_sim::SimCache;
use tokio::{sync::watch, task::JoinHandle};

use crate::{
config::BuilderConfig,
tasks::{
cache::{BundlePoller, CacheTask, TxPoller},
env::SimEnv,
},
use crate::tasks::{
cache::{BundlePoller, CacheTask, TxPoller},
env::SimEnv,
};

/// The block builder's cache system.
#[derive(Debug)]
pub struct CacheTasks {
/// The builder config.
pub config: BuilderConfig,
/// The block environment receiver.
pub block_env: watch::Receiver<Option<SimEnv>>,
block_env: watch::Receiver<Option<SimEnv>>,
}

impl CacheTasks {
/// Create a new [`CacheSystem`] with the given components.
pub const fn new(config: BuilderConfig, block_env: watch::Receiver<Option<SimEnv>>) -> Self {
Self { config, block_env }
pub const fn new(block_env: watch::Receiver<Option<SimEnv>>) -> Self {
Self { block_env }
}

/// Spawn a new [`CacheSystem`], which starts the
/// [`CacheTask`], [`TxPoller`], and [`BundlePoller`] internally and yields their [`JoinHandle`]s.
pub fn spawn(&self) -> CacheSystem {
// Tx Poller pulls transactions from the cache
let tx_poller = TxPoller::new(&self.config);
let tx_poller = TxPoller::new();
let (tx_receiver, tx_poller) = tx_poller.spawn();

// Bundle Poller pulls bundles from the cache
let bundle_poller = BundlePoller::new(&self.config, self.config.oauth_token());
let bundle_poller = BundlePoller::new();
let (bundle_receiver, bundle_poller) = bundle_poller.spawn();

// Set up the cache task
Expand Down
Loading