11//! `block.rs` contains the Simulator and everything that wires it into an
22//! actor that handles the simulation of a stream of bundles and transactions
33//! and turns them into valid Pecorino blocks for network submission.
4- use crate :: config:: { BuilderConfig , RuProvider } ;
4+ use crate :: {
5+ config:: { BuilderConfig , RuProvider } ,
6+ tasks:: env:: SimEnv ,
7+ } ;
58use alloy:: { eips:: BlockId , network:: Ethereum , providers:: Provider } ;
69use init4_bin_base:: {
710 deps:: tracing:: { debug, error} ,
@@ -17,6 +20,7 @@ use tokio::{
1720 } ,
1821 task:: JoinHandle ,
1922} ;
23+ use tracing:: info;
2024use trevm:: revm:: {
2125 context:: BlockEnv ,
2226 database:: { AlloyDB , WrapDatabaseAsync } ,
@@ -34,9 +38,17 @@ pub struct Simulator {
3438 pub config : BuilderConfig ,
3539 /// A provider that cannot sign transactions, used for interacting with the rollup.
3640 pub ru_provider : RuProvider ,
37-
3841 /// The block configuration environment on which to simulate
39- pub block_env : watch:: Receiver < Option < BlockEnv > > ,
42+ pub sim_env : watch:: Receiver < Option < SimEnv > > ,
43+ }
44+
45+ /// SimResult bundles a BuiltBlock to the BlockEnv it was simulated against.
46+ #[ derive( Debug , Clone ) ]
47+ pub struct SimResult {
48+ /// The block built with the successfully simulated transactions
49+ pub block : BuiltBlock ,
50+ /// The block environment the transactions were simulated against.
51+ pub env : SimEnv ,
4052}
4153
4254impl Simulator {
@@ -46,16 +58,17 @@ impl Simulator {
4658 ///
4759 /// - `config`: The configuration for the builder.
4860 /// - `ru_provider`: A provider for interacting with the rollup.
61+ /// - `block_env`: A receiver for the block environment to simulate against.
4962 ///
5063 /// # Returns
5164 ///
5265 /// A new `Simulator` instance.
5366 pub fn new (
5467 config : & BuilderConfig ,
5568 ru_provider : RuProvider ,
56- block_env : watch:: Receiver < Option < BlockEnv > > ,
69+ sim_env : watch:: Receiver < Option < SimEnv > > ,
5770 ) -> Self {
58- Self { config : config. clone ( ) , ru_provider, block_env }
71+ Self { config : config. clone ( ) , ru_provider, sim_env }
5972 }
6073
6174 /// Get the slot calculator.
@@ -65,11 +78,16 @@ impl Simulator {
6578
6679 /// Handles building a single block.
6780 ///
81+ /// Builds a block in the block environment with items from the simulation cache
82+ /// against the database state. When the `finish_by` deadline is reached, it
83+ /// stops simulating and returns the block.
84+ ///
6885 /// # Arguments
6986 ///
7087 /// - `constants`: The system constants for the rollup.
7188 /// - `sim_items`: The simulation cache containing transactions and bundles.
7289 /// - `finish_by`: The deadline by which the block must be built.
90+ /// - `block_env`: The block environment to simulate against.
7391 ///
7492 /// # Returns
7593 ///
@@ -79,28 +97,35 @@ impl Simulator {
7997 constants : SignetSystemConstants ,
8098 sim_items : SimCache ,
8199 finish_by : Instant ,
82- block : BlockEnv ,
100+ block_env : BlockEnv ,
83101 ) -> eyre:: Result < BuiltBlock > {
102+ debug ! ( block_number = block_env. number, tx_count = sim_items. len( ) , "starting block build" , ) ;
103+
84104 let db = self . create_db ( ) . await . unwrap ( ) ;
105+
85106 let block_build: BlockBuild < _ , NoOpInspector > = BlockBuild :: new (
86107 db,
87108 constants,
88109 self . config . cfg_env ( ) ,
89- block ,
110+ block_env ,
90111 finish_by,
91112 self . config . concurrency_limit ,
92113 sim_items,
93114 self . config . rollup_block_gas_limit ,
94115 ) ;
95116
96117 let built_block = block_build. build ( ) . await ;
97- debug ! ( block_number = ?built_block. block_number( ) , "finished building block" ) ;
118+ debug ! (
119+ tx_count = built_block. tx_count( ) ,
120+ block_number = built_block. block_number( ) ,
121+ "block simulation completed" ,
122+ ) ;
98123
99124 Ok ( built_block)
100125 }
101126
102- /// Spawns the simulator task, which handles the setup and sets the deadline
103- /// for the each round of simulation .
127+ /// Spawns the simulator task, which ticks along the simulation loop
128+ /// as it receives block environments .
104129 ///
105130 /// # Arguments
106131 ///
@@ -115,21 +140,23 @@ impl Simulator {
115140 self ,
116141 constants : SignetSystemConstants ,
117142 cache : SimCache ,
118- submit_sender : mpsc:: UnboundedSender < BuiltBlock > ,
143+ submit_sender : mpsc:: UnboundedSender < SimResult > ,
119144 ) -> JoinHandle < ( ) > {
120145 debug ! ( "starting simulator task" ) ;
121146
122147 tokio:: spawn ( async move { self . run_simulator ( constants, cache, submit_sender) . await } )
123148 }
124149
125- /// Continuously runs the block simulation and submission loop.
150+ /// This function runs indefinitely, waiting for the block environment to be set and checking
151+ /// if the current slot is valid before building a block and sending it along for to the submit channel.
126152 ///
127- /// This function clones the simulation cache, calculates a deadline for block building,
128- /// attempts to build a block using the latest cache and constants, and submits the built
129- /// block through the provided channel. If an error occurs during block building or submission,
130- /// it logs the error and continues the loop.
153+ /// If it is authorized for the current slot, then the simulator task
154+ /// - clones the simulation cache,
155+ /// - calculates a deadline for block building,
156+ /// - attempts to build a block using the latest cache and constants,
157+ /// - then submits the built block through the provided channel.
131158 ///
132- /// This function runs indefinitely and never returns .
159+ /// If an error occurs during block building or submission, it logs the error and continues the loop .
133160 ///
134161 /// # Arguments
135162 ///
@@ -140,26 +167,29 @@ impl Simulator {
140167 mut self ,
141168 constants : SignetSystemConstants ,
142169 cache : SimCache ,
143- submit_sender : mpsc:: UnboundedSender < BuiltBlock > ,
170+ submit_sender : mpsc:: UnboundedSender < SimResult > ,
144171 ) {
145172 loop {
146- let sim_cache = cache. clone ( ) ;
147- let finish_by = self . calculate_deadline ( ) ;
148-
149173 // Wait for the block environment to be set
150- if self . block_env . changed ( ) . await . is_err ( ) {
151- error ! ( "block_env channel closed" ) ;
174+ if self . sim_env . changed ( ) . await . is_err ( ) {
175+ error ! ( "block_env channel closed - shutting down simulator task " ) ;
152176 return ;
153177 }
178+ let Some ( sim_env) = self . sim_env . borrow_and_update ( ) . clone ( ) else { return } ;
179+ info ! ( sim_env. block_env. number, "new block environment received" ) ;
154180
155- // If no env, skip this run
156- let Some ( block_env) = self . block_env . borrow_and_update ( ) . clone ( ) else { return } ;
157- debug ! ( block_env = ?block_env, "building on block env" ) ;
158-
159- match self . handle_build ( constants, sim_cache, finish_by, block_env) . await {
181+ // Calculate the deadline for this block simulation.
182+ // NB: This must happen _after_ taking a reference to the sim cache,
183+ // waiting for a new block, and checking current slot authorization.
184+ let finish_by = self . calculate_deadline ( ) ;
185+ let sim_cache = cache. clone ( ) ;
186+ match self
187+ . handle_build ( constants, sim_cache, finish_by, sim_env. block_env . clone ( ) )
188+ . await
189+ {
160190 Ok ( block) => {
161- debug ! ( block = ?block, "built block" ) ;
162- let _ = submit_sender. send ( block) ;
191+ debug ! ( block = ?block. block_number ( ) , tx_count = block . transactions ( ) . len ( ) , "built simulated block" ) ;
192+ let _ = submit_sender. send ( SimResult { block, env : sim_env } ) ;
163193 }
164194 Err ( e) => {
165195 error ! ( err = %e, "failed to build block" ) ;
@@ -184,11 +214,10 @@ impl Simulator {
184214 let remaining = self . slot_calculator ( ) . slot_duration ( ) - timepoint;
185215
186216 // We add a 1500 ms buffer to account for sequencer stopping signing.
187-
188- let candidate =
217+ let deadline =
189218 Instant :: now ( ) + Duration :: from_secs ( remaining) - Duration :: from_millis ( 1500 ) ;
190219
191- candidate . max ( Instant :: now ( ) )
220+ deadline . max ( Instant :: now ( ) )
192221 }
193222
194223 /// Creates an `AlloyDB` instance from the rollup provider.
0 commit comments