8000 feat: passes sim result to the submit tasks by dylanlott · Pull Request #100 · init4tech/builder · GitHub
[go: up one dir, main page]

Skip to content

feat: passes sim result to the submit tasks #100

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 16, 2025
Prev Previous commit
Next Next commit
refactor: fetch header in submit task instead of passing through from…
… env task
  • Loading branch information
dylanlott committed Jun 13, 2025
commit 9f5eaefa5927c1de724e8e420e087cfdd092b269
11 changes: 8 additions & 3 deletions bin/ 8000 builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,17 @@ async fn main() -> eyre::Result<()> {
let zenith = config.connect_zenith(host_provider.clone());

// 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();

// Make a Tx submission task
let submit =
SubmitTask { zenith, quincey, config: config.clone(), outbound_tx_channel: tx_channel };
let submit = SubmitTask {
zenith,
quincey,
config: config.clone(),
outbound_tx_channel: tx_channel,
host_provider: host_provider.clone(),
};

// Set up tx submission
let (submit_channel, submit_jh) = submit.spawn();
Expand Down
4 changes: 1 addition & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,7 @@ impl BuilderConfig {
/// Create an [`EnvTask`] using this config.
pub async fn env_task(&self) -> EnvTask {
let ru_provider = self.connect_ru_provider();
let host_provider =
self.connect_host_provider().await.expect("failed to configure host provider");
EnvTask::new(self.clone(), ru_provider, host_provider)
EnvTask::new(self.clone(), ru_provider)
}

/// Spawn a new [`CacheSystem`] using this config. This contains the
Expand Down
4 changes: 2 additions & 2 deletions src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,14 +176,14 @@ impl Simulator {
return;
}
let Some(sim_env) = self.sim_env.borrow_and_update().clone() else { return };
info!(block_number = sim_env.signet.number, "new block environment received");
info!(sim_env.block_env.number, "new block environment received");

// Calculate the deadline for this block simulation.
// NB: This must happen _after_ taking a reference to the sim cache,
// waiting for a new block, and checking current slot authorization.
let finish_by = self.calculate_deadline();
let sim_cache = cache.clone();
match self.handle_build(constants, sim_cache, finish_by, sim_env.signet.clone()).await {
match self.handle_build(constants, sim_cache, finish_by, sim_env.block_env.clone()).await {
Ok(block) => {
debug!(block = ?block.block_number(), tx_count = block.transactions().len(), "built simulated block");
let _ = submit_sender.send(SimResult { block, env: sim_env });
Expand Down
6 changes: 3 additions & 3 deletions src/tasks/cache/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ impl CacheTask {
break;
}
if let Some(env) = self.env.borrow_and_update().as_ref() {
basefee = env.signet.basefee;
info!(basefee, env.signet.number, env.signet.timestamp, "rollup block env changed, clearing cache");
basefee = env.block_env.basefee;
info!(basefee, env.block_env.number, env.block_env.timestamp, "rollup block env changed, clearing cache");
cache.clean(
env.signet.number, env.signet.timestamp
env.block_env.number, env.block_env.timestamp
);
}
}
Expand Down
71 changes: 22 additions & 49 deletions src/tasks/env.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::config::{BuilderConfig, HostProvider, RuProvider};
use crate::config::{BuilderConfig, RuProvider};
use alloy::{
consensus::Header,
eips::{BlockId, BlockNumberOrTag, eip1559::BaseFeeParams},
eips::eip1559::BaseFeeParams,
primitives::{B256, U256},
providers::Provider,
};
Expand All @@ -18,27 +18,21 @@ pub struct EnvTask {
config: BuilderConfig,
/// Rollup provider is used to get the latest rollup block header for simulation.
ru_provider: RuProvider,
/// Host provider is used to get the previous block header for gas estimation.
host_provider: HostProvider,
}

/// Contains a signet BlockEnv and its corresponding host Header.
#[derive(Debug, Clone)]
pub struct SimEnv {
/// The signet block environment, for rollup block simulation.
pub signet: BlockEnv,
/// The host environment header, for host transaction submission pricing.
pub host: Header,
pub block_env: BlockEnv,
/// The header of the previous rollup block.
pub prev_header: Header,
}

impl EnvTask {
/// Create a new [`EnvTask`] with the given config and providers.
pub const fn new(
config: BuilderConfig,
ru_provider: RuProvider,
host_provider: HostProvider,
) -> Self {
Self { config, ru_provider, host_provider }
pub const fn new(config: BuilderConfig, ru_provider: RuProvider) -> Self {
Self { config, ru_provider }
}

/// Construct a [`BlockEnv`] by from the previous block header.
Expand Down Expand Up @@ -89,56 +83,35 @@ impl EnvTask {
info_span!("EnvTask::task_fut::loop", %block_hash, number = tracing::field::Empty);

// Get the rollup header for rollup block simulation environment configuration
let rollup_header =
match self.get_latest_rollup_header(&sender, block_hash, &span).await {
Some(value) => value,
None => continue,
};
debug!(?rollup_header.number, "pulled rollup block for simulation");

// Get the host header for blob transaction submission gas pricing
let host_header = match self.get_host_header().await {
Ok(header) => header,
Err(_) => {
error!("failed to get host header - skipping block");
let rollup_header = match self
.get_latest_rollup_header(&sender, block_hash, &span)
.await
{
Some(value) => value,
None => {
// If we failed to get the rollup header, we skip this iteration.
debug!(%block_hash, "failed to get rollup header - continuint to next block");
continue;
}
};
debug!(?host_header.base_fee_per_gas, "pulled previous host header for gas calculation");
debug!(rollup_header.number, "pulled rollup block for simulation");
span.record("rollup_block_number", rollup_header.number);

// Construct the block env using the previous block header
let signet_env = self.construct_block_env(&host_header);
debug!(
block_number = signet_env.number,
signet_env.basefee, "constructed signet block env"
);
let signet_env = self.construct_block_env(&rollup_header);
debug!(signet_env.number, signet_env.basefee, "constructed signet block env");

if sender.send(Some(SimEnv { signet: signet_env, host: host_header })).is_err() {
if sender
.send(Some(SimEnv { block_env: signet_env, prev_header: rollup_header }))
.is_err()
{
// The receiver has been dropped, so we can stop the task.
debug!("receiver dropped, stopping task");
break;
}
}
}

/// Gets the latest host [`Header`].
/// NB: This doesn't need to correlate perfectly with the rollup blocks,
/// since we only use the previous host block [`Header`] for gas estimation.
async fn get_host_header(&self) -> eyre::Result<Header> {
let previous = self
.host_provider
.get_block(BlockId::Number(BlockNumberOrTag::Latest))
.into_future()
.await?;
debug!(?previous, "got host block for hash");

match previous {
Some(block) => Ok(block.header.inner),
None => Err(eyre::eyre!("host block not found")),
}
}

/// Get latest rollup [`Header`] for the given block hash.
async fn get_latest_rollup_header(
&self,
Expand Down
43 changes: 29 additions & 14 deletions src/tasks/submit.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use crate::{
config::{HostProvider, ZenithInstance},
quincey::Quincey,
tasks::env::SimEnv,
utils::extract_signature_components,
};
use alloy::{
consensus::{Header, SimpleCoder, constants::GWEI_TO_WEI},
eips::BlockNumberOrTag,
consensus::{constants::GWEI_TO_WEI, Header, SimpleCoder},
eips::{BlockId, BlockNumberOrTag},
network::{TransactionBuilder, TransactionBuilder4844},
primitives::{Bytes, FixedBytes, TxHash, U256},
providers::{Provider as _, SendableTx, WalletProvider},
Expand Down Expand Up @@ -196,6 +195,8 @@ pub struct SubmitTask {
pub config: crate::config::BuilderConfig,
/// Channel over which to send pending transactions
pub outbound_tx_channel: mpsc::UnboundedSender<TxHash>,
/// Host provider for sending transactions and fetching block & header info
pub host_provider: HostProvider,
}

impl SubmitTask {
Expand Down Expand Up @@ -241,9 +242,8 @@ impl SubmitTask {
retry_count: usize,
resp: &SignResponse,
block: &BuiltBlock,
sim_env: &SimEnv,
) -> eyre::Result<ControlFlow> {
let tx = self.prepare_tx(retry_count, resp, block, sim_env).await?;
let tx = self.prepare_tx(retry_count, resp, block).await?;

self.send_transaction(resp, tx).await
}
Expand All @@ -255,10 +255,12 @@ impl SubmitTask {
retry_count: usize,
resp: &SignResponse,
block: &BuiltBlock,
sim_env: &SimEnv,
) -> Result<TransactionRequest, eyre::Error> {
// Get the latest host block header for gas estimation
let host_header = self.latest_host_header().await?;

// Create the transaction request with the signature values
let tx: TransactionRequest = self.new_tx_request(retry_count, resp, block, sim_env).await?;
let tx: TransactionRequest = self.new_tx_request(retry_count, resp, block, host_header).await?;

// Simulate the transaction with a call to the host provider and report any errors
if let Err(err) = self.sim_with_call(&tx).await {
Expand All @@ -268,6 +270,21 @@ impl SubmitTask {
Ok(tx)
}

/// Gets the host header from the host provider by fetching the latest block.
async fn latest_host_header(&self) -> eyre::Result<Header> {
let previous = self
.host_provider
.get_block(BlockId::Number(BlockNumberOrTag::Latest))
.into_future()
.await?;
debug!(?previous, "got host block for hash");

match previous {
Some(block) => Ok(block.header.inner),
None => Err(eyre::eyre!("host block not found")),
}
}

/// Simulates the transaction with a call to the host provider to check for reverts.
async fn sim_with_call(&self, tx: &TransactionRequest) -> eyre::Result<()> {
match self.provider().call(tx.clone()).block(BlockNumberOrTag::Pending.into()).await {
Expand All @@ -286,7 +303,7 @@ impl SubmitTask {
retry_count: usize,
resp: &SignResponse,
block: &BuiltBlock,
sim_env: &SimEnv,
host_header: Header,
) -> Result<TransactionRequest, eyre::Error> {
// manually retrieve nonce
let nonce =
Expand All @@ -297,7 +314,7 @@ impl SubmitTask {
let (v, r, s) = extract_signature_components(&resp.sig);

let (max_fee_per_gas, max_priority_fee_per_gas, max_fee_per_blob_gas) =
calculate_gas(retry_count, sim_env.host.clone());
calculate_gas(retry_count, host_header);

// Build the block header
let header: BlockHeader = BlockHeader {
Expand Down Expand Up @@ -379,7 +396,6 @@ impl SubmitTask {
&self,
retry_count: usize,
block: &BuiltBlock,
sim_env: &SimEnv,
) -> eyre::Result<ControlFlow> {
info!(retry_count, txns = block.tx_count(), "handling inbound block");
let Ok(sig_request) = self.construct_sig_request(block).await.inspect_err(|e| {
Expand All @@ -397,14 +413,13 @@ impl SubmitTask {

let signed = self.quincey.get_signature(&sig_request).await?;

self.submit_transaction(retry_count, &signed, block, sim_env).await
self.submit_transaction(retry_count, &signed, block).await
}

/// Handles the retry logic for the inbound block.
async fn retrying_handle_inbound(
&self,
block: &BuiltBlock,
sim_env: &SimEnv,
retry_limit: usize,
) -> eyre::Result<ControlFlow> {
let mut retries = 0;
Expand All @@ -418,7 +433,7 @@ impl SubmitTask {
let span = debug_span!("SubmitTask::retrying_handle_inbound", retries);

let inbound_result =
match self.handle_inbound(retries, block, sim_env).instrument(span.clone()).await {
match self.handle_inbound(retries, block).instrument(span.clone()).await {
Ok(control_flow) => control_flow,
Err(err) => {
// Delay until next slot if we get a 403 error
Expand Down Expand Up @@ -520,7 +535,7 @@ impl SubmitTask {
}

if let Err(e) =
self.retrying_handle_inbound(&sim_result.block, &sim_result.env, 3).await
self.retrying_handle_inbound(&sim_result.block, 3).await
{
error!(error = %e, "error handling inbound block");
continue;
Expand Down
Loading
0