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
Next Next commit
passes sim result to the submit tasks
- adds a SimResult type that binds a BlockEnv to a BuiltBlock
- passess that SimResult to the SubmitTask for gas calculations
  • Loading branch information
dylanlott committed Jun 13, 2025
commit b32114770c7d0e6cbfbb00ea131e4ca7d8f3fe6d
20 changes: 18 additions & 2 deletions bin/submit_transaction.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! A simple transaction submitter that sends a transaction to a recipient address
//! on a regular interval for the purposes of roughly testing rollup mining.
use alloy::{
network::{EthereumWallet, TransactionBuilder},
primitives::{Address, U256},
Expand Down Expand Up @@ -67,18 +69,29 @@ async fn main() {
}
}

/// Sends a transaction to the specified recipient address
async fn send_transaction(provider: &HostProvider, recipient_address: Address) {
// construct simple transaction to send ETH to a recipient
let nonce = match provider.get_transaction_count(provider.default_signer_address()).await {
Ok(count) => count,
Err(e) => {
error!(error = ?e, "failed to get transaction count");
return;
}
};

let tx = TransactionRequest::default()
.with_from(provider.default_signer_address())
.with_to(recipient_address)
.with_value(U256::from(1))
.with_nonce(nonce)
.with_gas_limit(30_000);

// start timer to measure how long it takes to mine the transaction
let dispatch_start_time: Instant = Instant::now();

// dispatch the transaction
debug!(?tx.nonce, "sending transaction with nonce");
let result = provider.send_transaction(tx).await.unwrap();

// wait for the transaction to mine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this what the metrics task is for? when did we move this out of the metrics task?

Expand All @@ -95,10 +108,13 @@ async fn send_transaction(provider: &HostProvider, recipient_address: Address) {
}
};

let hash = receipt.transaction_hash.to_string();
record_metrics(dispatch_start_time, receipt);
}

// record metrics for how long it took to mine the transaction
/// Record metrics for how long it took to mine the transaction
fn record_metrics(dispatch_start_time: Instant, receipt: alloy::rpc::types::TransactionReceipt) {
let mine_time = dispatch_start_time.elapsed().as_secs();
let hash = receipt.transaction_hash.to_string();
debug!(success = receipt.status(), mine_time, hash, "transaction mined");
histogram!("txn_submitter.tx_mine_time").record(mine_time as f64);
}
83 changes: 65 additions & 18 deletions src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use init4_bin_base::{
};
use signet_sim::{BlockBuild, BuiltBlock, SimCache};
use signet_types::constants::SignetSystemConstants;
use std::time::{Duration, Instant};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use tokio::{
sync::{
mpsc::{self},
watch,
},
task::JoinHandle,
};
use tracing::trace;
use trevm::revm::{
context::BlockEnv,
database::{AlloyDB, WrapDatabaseAsync},
Expand All @@ -34,18 +35,27 @@ pub struct Simulator {
pub config: BuilderConfig,
/// 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 block_env: watch::Receiver<Option<BlockEnv>>,
}

/// SimResult bundles a BuiltBlock to the BlockEnv it was simulated against.
#[derive(Debug, Clone)]
pub struct SimResult {
/// The block built with the successfully simulated transactions
pub block: BuiltBlock,
/// The block environment the transactions were simulated against.
pub env: BlockEnv,
}

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
///
Expand All @@ -70,6 +80,7 @@ impl Simulator {
/// - `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.
///
/// # Returns
///
Expand All @@ -79,22 +90,34 @@ impl Simulator {
constants: SignetSystemConstants,
sim_items: SimCache,
finish_by: Instant,
block: BlockEnv,
block_env: BlockEnv,
) -> eyre::Result<BuiltBlock> {
debug!(
block_number = block_env.number,
deadline = ?self.instant_to_timestamp(finish_by),
tx_count= sim_items.len(),
"starting block build",
);

let db = self.create_db().await.unwrap();

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

let built_block = block_build.build().await;
debug!(block_number = ?built_block.block_number(), "finished building block");
debug!(
tx_count = built_block.tx_count(),
block_number = ?built_block.block_number(),
"block simulation completed",
);

Ok(built_block)
}
Expand All @@ -115,7 +138,7 @@ impl Simulator {
self,
constants: SignetSystemConstants,
cache: SimCache,
submit_sender: mpsc::UnboundedSender<BuiltBlock>,
submit_sender: mpsc::UnboundedSender<SimResult>,
) -> JoinHandle<()> {
debug!("starting simulator task");

Expand All @@ -140,26 +163,23 @@ impl Simulator {
mut self,
constants: SignetSystemConstants,
cache: SimCache,
submit_sender: mpsc::UnboundedSender<BuiltBlock>,
submit_sender: mpsc::UnboundedSender<SimResult>,
) {
loop {
let sim_cache = cache.clone();
let finish_by = self.calculate_deadline();

// Wait for the block environment to be set
if self.block_env.changed().await.is_err() {
error!("block_env channel closed");
return;
}

// If no env, skip this run
let Some(block_env) = self.block_env.borrow_and_update().clone() else { return };
debug!(block_env = ?block_env, "building on block env");

match self.handle_build(constants, sim_cache, finish_by, block_env).await {
let finish_by = self.calculate_deadline();
let sim_cache = cache.clone();
match self.handle_build(constants, sim_cache, finish_by, block_env.clone()).await {
Ok(block) => {
debug!(block = ?block, "built block");
let _ = submit_sender.send(block);
debug!(block = ?block.block_number(), tx_count = block.transactions().len(), "built block");
let _ = submit_sender.send(SimResult { block, env: block_env });
}
Err(e) => {
error!(err = %e, "failed to build block");
Expand All @@ -178,17 +198,25 @@ impl Simulator {
pub fn calculate_deadline(&self) -> Instant {
// Get the current timepoint within the slot.
let timepoint = self.slot_calculator().current_timepoint_within_slot();
trace!(timepoint, "current timepoint within slot");

// We have the timepoint in seconds into the slot. To find out what's
// remaining, we need to subtract it from the slot duration
let remaining = self.slot_calculator().slot_duration() - timepoint;
trace!(remaining, "time remaining in slot");

// We add a 1500 ms buffer to account for sequencer stopping signing.

let candidate =
let deadline =
Instant::now() + Duration::from_secs(remaining) - Duration::from_millis(1500);
trace!(deadline = ?self.instant_to_timestamp(deadline), "calculated deadline for block simulation");

let buffered_deadline = deadline.max(Instant::now());
trace!(?buffered_deadline, "final deadline for block simulation");

candidate.max(Instant::now())
let timestamp = self.instant_to_timestamp(buffered_deadline);
trace!(?timestamp, "deadline converted to timestamp");

buffered_deadline
}

/// Creates an `AlloyDB` instance from the rollup provider.
Expand Down Expand Up @@ -217,4 +245,23 @@ impl Simulator {
let wrapped_db: AlloyDatabaseProvider = WrapDatabaseAsync::new(alloy_db).unwrap();
Some(wrapped_db)
}

/// Converts an `Instant` to a UNIX timestamp in seconds and milliseconds.
pub fn instant_to_timestamp(&self, instant: Instant) -> (u64, u128) {
let now_instant = Instant::now();
let now_system = SystemTime::now();

let duration_from_now = now_instant.duration_since(instant);

// Subtract that duration from the system time
let target_system_time = now_system - duration_from_now;

let duration_since_epoch =
target_system_time.duration_since(UNIX_EPOCH).expect("Time went backwards");

let seconds = duration_since_epoch.as_secs();
let milliseconds = duration_since_epoch.as_millis();

(seconds, milliseconds)
}
}
5 changes: 2 additions & 3 deletions src/tasks/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ impl EnvTask {
let Some(block) = blocks.last() else {
// This case occurs when there are no changes to the block,
// so we do nothing.
debug!("empty filter changes");
continue;
};
let span = info_span!("EnvTask::task_fut::loop", hash = %block, number = tracing::field::Empty);
Expand Down Expand Up @@ -96,10 +95,10 @@ impl EnvTask {
}
};
span.record("number", previous.number);
debug!("retrieved latest block");

let env = self.construct_block_env(&previous);
debug!(?env, "constructed block env");
debug!(block_number = ?env.number, env.basefee, "constructed latest block env");

if sender.send(Some(env)).is_err() {
// The receiver has been dropped, so we can stop the task.
debug!("receiver dropped, stopping task");
Expand Down
Loading
0