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
942 changes: 424 additions & 518 deletions Cargo.lock

Large diffs are not rendered by default.

31 changes: 22 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ name = "zenith-builder-example"
path = "bin/builder.rs"

[dependencies]
init4-bin-base = { version = "0.13.1", features = ["perms", "aws" ] }
init4-bin-base = { version = "0.16.0", features = ["perms", "aws"] }

signet-constants = { version = "0.11.2" }
signet-sim = { version = "0.11.2" }
signet-tx-cache = { version = "0.11.2" }
signet-types = { version = "0.11.2" }
signet-zenith = { version = "0.11.2" }
signet-constants = { version = "0.13.0" }
signet-sim = { version = "0.13.0" }
signet-tx-cache = { version = "0.13.0" }
signet-types = { version = "0.13.0" }
signet-zenith = { version = "0.13.0" }

trevm = { version = "0.29.0", features = ["concurrent-db", "test-utils"] }
trevm = { version = "0.29", features = ["concurrent-db", "test-utils"] }

alloy = { version = "1.0.37", features = [
alloy = { version = "1.0.35", features = [
"full",
"json-rpc",
"signer-aws",
Expand All @@ -39,7 +39,7 @@ alloy = { version = "1.0.37", features = [
"node-bindings",
"serde",
"getrandom",
"provider-mev-api"
"provider-mev-api",
] }

axum = "0.7.5"
Expand All @@ -52,3 +52,16 @@ tracing = "0.1.41"
tokio = { version = "1.36.0", features = ["full", "macros", "rt-multi-thread"] }
tokio-stream = "0.1.17"
url = "2.5.4"

# comment / uncomment for local dev
# [patch.crates-io]
# signet-constants = { path = "../signet-sdk/crates/constants" }
# signet-types = { path = "../signet-sdk/crates/types" }
# signet-zenith = { path = "../signet-sdk/crates/zenith" }
# signet-sim = { path = "../signet-sdk/crates/sim" }
# signet-evm = { path = "../signet-sdk/crates/evm" }
# signet-extract = { path = "../signet-sdk/crates/extract" }
# 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" }
4 changes: 2 additions & 2 deletions bin/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async fn main() -> eyre::Result<()> {
let cache_system = cache_tasks.spawn();

// Set up the metrics task
let metrics = MetricsTask { host_provider };
let metrics = MetricsTask { host_provider: host_provider.clone() };
let (tx_channel, metrics_jh) = metrics.spawn();

// Set up the submit task. This will be either a Flashbots task or a
Expand All @@ -42,7 +42,7 @@ async fn main() -> eyre::Result<()> {
let (submit_channel, submit_jh) = config.spawn_submit_task(tx_channel).await?;

// Set up the simulator
let sim = Simulator::new(&config, ru_provider.clone(), block_env);
let sim = Simulator::new(&config, host_provider, ru_provider, block_env);
let build_jh =
sim.spawn_simulator_task(config.constants.clone(), cache_system.sim_cache, submit_channel);

Expand Down
26 changes: 23 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,18 @@ pub struct BuilderConfig {
)]
pub concurrency_limit: Option<usize>,

/// Optional maximum host gas coefficient to use when building blocks.
/// Defaults to 80% (80) if not set.
#[from_env(
var = "MAX_HOST_GAS_COEFFICIENT",
desc = "Optional maximum host gas coefficient, as a percentage, to use when building blocks",
default = 80
)]
pub max_host_gas_coefficient: Option<u8>,

/// The slot calculator for the builder.
pub slot_calculator: SlotCalculator,

// TODO: Make this compatible with FromEnv again, somehow it broke
/// The signet system constants.
pub constants: SignetSystemConstants,
}
Expand Down Expand Up @@ -301,11 +309,16 @@ impl BuilderConfig {
Ok(Quincey::new_remote(client, url, token))
}

/// Create a [`SignetCfgEnv`] using this config.
pub const fn cfg_env(&self) -> SignetCfgEnv {
/// Create a rollup [`SignetCfgEnv`] using this config.
pub const fn ru_cfg_env(&self) -> SignetCfgEnv {
SignetCfgEnv { chain_id: self.ru_chain_id }
}

/// Create a host [`SignetCfgEnv`] using this config.
pub const fn host_cfg_env(&self) -> SignetCfgEnv {
SignetCfgEnv { chain_id: self.host_chain_id }
}

/// Memoizes the concurrency limit for the current system. Uses [`std::thread::available_parallelism`] if no
/// value is set. If that for some reason fails, it returns the default concurrency limit.
pub fn concurrency_limit(&self) -> usize {
Expand All @@ -324,6 +337,13 @@ impl BuilderConfig {
})
}

/// Returns the maximum host gas to use for block building based on the configured max host gas coefficient.
pub fn max_host_gas(&self, gas_limit: u64) -> u64 {
// Set max host gas to a percentage of the host block gas limit
((gas_limit as u128 * (self.max_host_gas_coefficient.unwrap_or(80) as u128)) / 100u128)
as u64
}

/// Spawn a submit task, either Flashbots or BuilderHelper depending on
/// configuration.
pub async fn spawn_submit_task(
Expand Down
114 changes: 90 additions & 24 deletions src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
//! actor that handles the simulation of a stream of bundles and transactions
//! and turns them into valid Pecorino blocks for network submission.
use crate::{
config::{BuilderConfig, RuProvider},
config::{BuilderConfig, HostProvider, RuProvider},
tasks::env::SimEnv,
};
use alloy::{eips::BlockId, network::Ethereum};
use alloy::{consensus::Header, eips::BlockId, network::Ethereum, providers::Provider};
use init4_bin_base::{
deps::metrics::{counter, histogram},
utils::calc::SlotCalculator,
};
use signet_sim::{BlockBuild, BuiltBlock, SimCache};
use signet_sim::{BlockBuild, BuiltBlock, HostEnv, RollupEnv, SimCache};
use signet_types::constants::SignetSystemConstants;
use std::time::{Duration, Instant};
use tokio::{
Expand All @@ -22,12 +22,12 @@ use tokio::{
};
use tracing::{Instrument, Span, debug, instrument};
use trevm::revm::{
context::BlockEnv,
database::{AlloyDB, WrapDatabaseAsync},
inspector::NoOpInspector,
};

type AlloyDatabaseProvider = WrapDatabaseAsync<AlloyDB<Ethereum, RuProvider>>;
type HostAlloyDatabaseProvider = WrapDatabaseAsync<AlloyDB<Ethereum, HostProvider>>;
type RollupAlloyDatabaseProvider = WrapDatabaseAsync<AlloyDB<Ethereum, RuProvider>>;

/// `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
Expand All @@ -36,6 +36,8 @@ type AlloyDatabaseProvider = WrapDatabaseAsync<AlloyDB<Ethereum, RuProvider>>;
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
Expand All @@ -52,8 +54,18 @@ pub struct SimResult {
}

impl SimResult {
/// Get a reference to the previous host header.
pub const fn prev_host(&self) -> &Header {
self.sim_env.prev_host()
}

/// Get a reference to the previous rollup header.
pub const fn prev_rollup(&self) -> &Header {
self.sim_env.prev_rollup()
}

/// Returns the block number of the built block.
pub const fn block_number(&self) -> u64 {
pub const fn rollup_block_number(&self) -> u64 {
self.block.block_number()
}

Expand Down Expand Up @@ -88,10 +100,11 @@ impl Simulator {
/// 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(), ru_provider, sim_env }
Self { config: config.clone(), host_provider, ru_provider, sim_env }
}

/// Get the slot calculator.
Expand Down Expand Up @@ -124,22 +137,22 @@ impl Simulator {
constants: SignetSystemConstants,
sim_items: SimCache,
finish_by: Instant,
block_env: BlockEnv,
sim_env: SimEnv,
) -> eyre::Result<BuiltBlock> {
let concurrency_limit = self.config.concurrency_limit();
let max_host_gas = self.config.max_host_gas(sim_env.prev_host().gas_limit);

// NB: Build AlloyDB from the previous block number's state, since block_env maps to the in-progress block
let db = self.create_db(block_env.number.to::<u64>() - 1).unwrap();
let (rollup_env, host_env) = self.create_envs(constants, &sim_env).await;
debug!(rollup_env = ?rollup_env, host_env = ?host_env, "building with block environments");

let block_build: BlockBuild<_, NoOpInspector> = BlockBuild::new(
db,
constants,
self.config.cfg_env(),
block_env,
let block_build = BlockBuild::new(
rollup_env,
host_env,
finish_by,
concurrency_limit,
sim_items,
self.config.rollup_block_gas_limit,
max_host_gas,
);

let built_block = block_build.build().in_current_span().await;
Expand All @@ -149,11 +162,53 @@ impl Simulator {
"block simulation completed",
);
counter!("signet.builder.built_blocks").increment(1);
histogram!("signet.builder.built_blocks.tx_count").record(built_block.tx_count() as f64);
histogram!("signet.builder.built_blocks.tx_count").record(built_block.tx_count() as u32);

Ok(built_block)
}

// Helper to create rollup + host envs from the sim env.
#[instrument(skip_all, fields(
rollup_block = sim_env.rollup_block_number(),
host_block = sim_env.host_block_number(),
))]
async fn create_envs(
&self,
constants: SignetSystemConstants,
sim_env: &SimEnv,
) -> (
RollupEnv<RollupAlloyDatabaseProvider, NoOpInspector>,
HostEnv<HostAlloyDatabaseProvider, NoOpInspector>,
) {
// Host DB and Env
let host_db = self.create_host_db().await;

let height = sim_env.host_block_number();
debug!(%height, "creating host env at block number");

let host_env = HostEnv::<HostAlloyDatabaseProvider, NoOpInspector>::new(
host_db,
constants.clone(),
&self.config.host_cfg_env(),
sim_env.host_env(),
);
debug!(?host_env, "created host env");

// Rollup DB and Env
let rollup_block_number = sim_env.rollup_block_number();
let rollup_db = self.create_rollup_db(rollup_block_number);

let rollup_env = RollupEnv::<RollupAlloyDatabaseProvider, NoOpInspector>::new(
rollup_db,
constants,
&self.config.ru_cfg_env(),
sim_env.rollup_env(),
);
debug!(?rollup_env, "created rollup env");

(rollup_env, host_env)
}

/// Spawns the simulator task, which ticks along the simulation loop
/// as it receives block environments.
///
Expand Down Expand Up @@ -217,7 +272,7 @@ impl Simulator {
let sim_cache = cache.clone();

let Ok(block) = self
.handle_build(constants.clone(), sim_cache, finish_by, sim_env.block_env.clone())
.handle_build(constants.clone(), sim_cache, finish_by, sim_env.clone())
.instrument(span.clone())
.await
.inspect_err(|err| span_error!(span, %err, "error during block build"))
Expand Down Expand Up @@ -251,12 +306,24 @@ impl Simulator {
deadline.max(Instant::now())
}

/// Creates an `AlloyDB` instance from the host provider.
async fn create_host_db(&self) -> HostAlloyDatabaseProvider {
let block_height = self.host_provider.get_block_number().await.unwrap();
debug!(%block_height, "creating alloyDB at block height");

let alloy_db = AlloyDB::new(self.host_provider.clone(), BlockId::latest());

// Wrap the AlloyDB instance in a WrapDatabaseAsync and return it.
// This is safe to unwrap because the main function sets the proper runtime settings.
//
// See: https://docs.rs/tokio/latest/tokio/attr.main.html
WrapDatabaseAsync::new(alloy_db).unwrap()
}

/// Creates an `AlloyDB` instance from the rollup provider.
///
/// # Returns
///
/// An `Option` containing the wrapped database or `None` if an error occurs.
fn create_db(&self, latest_block_number: u64) -> Option<AlloyDatabaseProvider> {
fn create_rollup_db(&self, latest_block_number: u64) -> RollupAlloyDatabaseProvider {
debug!(%latest_block_number, "creating rollup alloyDB at block height");

// Make an AlloyDB instance from the rollup provider with that latest block number
let alloy_db: AlloyDB<Ethereum, RuProvider> =
AlloyDB::new(self.ru_provider.clone(), BlockId::from(latest_block_number));
Expand All @@ -265,7 +332,6 @@ impl Simulator {
// This is safe to unwrap because the main function sets the proper runtime settings.
//
// See: https://docs.rs/tokio/latest/tokio/attr.main.html
let wrapped_db: AlloyDatabaseProvider = WrapDatabaseAsync::new(alloy_db).unwrap();
Some(wrapped_db)
WrapDatabaseAsync::new(alloy_db).unwrap()
}
}
17 changes: 10 additions & 7 deletions src/tasks/cache/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use tracing::{debug, info};
#[derive(Debug)]
pub struct CacheTask {
/// The channel to receive the block environment.
env: watch::Receiver<Option<SimEnv>>,
envs: watch::Receiver<Option<SimEnv>>,
/// The channel to receive the transaction bundles.
bundles: mpsc::UnboundedReceiver<TxCacheBundle>,
/// The channel to receive the transactions.
Expand All @@ -30,24 +30,27 @@ impl CacheTask {
bundles: mpsc::UnboundedReceiver<TxCacheBundle>,
txns: mpsc::UnboundedReceiver<TxEnvelope>,
) -> Self {
Self { env, bundles, txns }
Self { envs: env, bundles, txns }
}

async fn task_future(mut self, cache: SimCache) {
loop {
let mut basefee = 0;
tokio::select! {
biased;
res = self.env.changed() => {
res = self.envs.changed() => {
if res.is_err() {
debug!("Cache task: env channel closed, exiting");
break;
}
if let Some(env) = self.env.borrow_and_update().as_ref() {
basefee = env.block_env.basefee;
info!(basefee, block_env_number = env.block_env.number.to::<u64>(), block_env_timestamp = env.block_env.timestamp.to::<u64>(), "rollup block env changed, clearing cache");

if let Some(env) = self.envs.borrow_and_update().as_ref() {
let sim_env = env.rollup_env();

basefee = sim_env.basefee;
info!(basefee, block_env_number = sim_env.number.to::<u64>(), block_env_timestamp = sim_env.timestamp.to::<u64>(), "rollup block env changed, clearing cache");
cache.clean(
env.block_env.number.to(), env.block_env.timestamp.to()
sim_env.number.to(), sim_env.timestamp.to()
);
}
}
Expand Down
Loading
Loading