signet_sim/
task.rs

1use crate::{env::SimEnv, BuiltBlock, SharedSimEnv, SimCache, SimDb};
2use signet_types::constants::SignetSystemConstants;
3use tokio::select;
4use tracing::{debug, info_span, trace, Instrument};
5use trevm::{
6    helpers::Ctx,
7    revm::{inspector::NoOpInspector, DatabaseRef, Inspector},
8    Block, Cfg,
9};
10
11/// Builds a single block by repeatedly invoking [`SimEnv`].
12#[derive(Debug)]
13pub struct BlockBuild<Db, Insp = NoOpInspector> {
14    /// The simulation environment.
15    env: SharedSimEnv<Db, Insp>,
16
17    /// The block being built.
18    block: BuiltBlock,
19
20    /// The deadline to produce a block by.
21    finish_by: std::time::Instant,
22
23    /// The maximum amount of gas to use in the built block
24    max_gas: u64,
25}
26
27impl<Db, Insp> BlockBuild<Db, Insp>
28where
29    Db: DatabaseRef + Send + Sync + 'static,
30    Insp: Inspector<Ctx<SimDb<Db>>> + Default + Sync + 'static,
31{
32    /// Create a new block building process.
33    #[allow(clippy::too_many_arguments)] // sadge but.
34    pub fn new<C, B>(
35        db: Db,
36        constants: SignetSystemConstants,
37        cfg: C,
38        block: B,
39        finish_by: std::time::Instant,
40        concurrency_limit: usize,
41        sim_items: SimCache,
42        max_gas: u64,
43    ) -> Self
44    where
45        C: Cfg,
46        B: Block,
47    {
48        let env = SimEnv::<Db, Insp>::new(
49            db,
50            constants,
51            cfg,
52            block,
53            finish_by,
54            concurrency_limit,
55            sim_items,
56        );
57        let finish_by = env.finish_by();
58        let number = env.block().number;
59        Self { env: env.into(), block: BuiltBlock::new(number), finish_by, max_gas }
60    }
61
62    /// Run a simulation round, and accumulate the results into the block.
63    async fn round(&mut self) {
64        let gas_allowed = self.max_gas - self.block.gas_used();
65
66        if let Some(simulated) = self.env.sim_round(gas_allowed).await {
67            tracing::debug!(score = %simulated.score, gas_used = simulated.gas_used, "Adding item to block");
68            self.block.ingest(simulated);
69        }
70    }
71
72    /// Run several rounds, building
73    pub async fn build(mut self) -> BuiltBlock {
74        let mut i = 1;
75        // Run until the deadline is reached.
76        loop {
77            let span = info_span!("build", round = i);
78            let finish_by = self.finish_by.into();
79            let fut = self.round().instrument(span);
80
81            select! {
82                _ = tokio::time::sleep_until(finish_by) => {
83                    debug!("Deadline reached, stopping sim loop");
84                    break;
85                },
86                _ = fut => {
87                    i+= 1;
88                    let remaining = self.env.sim_items().len();
89                    trace!(%remaining, round = i, "Round completed");
90                    if remaining == 0 {
91                        debug!("No more items to simulate, stopping sim loop");
92                        break;
93                    }
94                }
95            }
96        }
97
98        debug!(rounds = i, transactions = self.block.transactions.len(), "Building completed",);
99
100        self.block
101    }
102}
103
104#[cfg(test)]
105mod test {
106    use std::future::Future;
107
108    use super::*;
109
110    /// Compile-time check to ensure that the block building process is
111    /// `Send`.
112    fn _build_fut_is_send<Db, Insp>(b: BlockBuild<Db, Insp>)
113    where
114        Db: DatabaseRef + Send + Sync + 'static,
115        Insp: Inspector<Ctx<SimDb<Db>>> + Default + Sync + 'static,
116    {
117        let _: Box<dyn Future<Output = BuiltBlock> + Send> = Box::new(b.build());
118    }
119}