use crate::{env::SimEnv, BuiltBlock, SharedSimEnv, SimCache, SimDb};
use signet_types::constants::SignetSystemConstants;
use std::time::Duration;
use tokio::{select, time::Instant};
use tracing::{debug, info_span, trace, Instrument};
use trevm::{
helpers::Ctx,
revm::{inspector::NoOpInspector, DatabaseRef, Inspector},
Block, Cfg,
};
pub(crate) const SIM_SLEEP_MS: u64 = 50;
#[derive(Debug)]
pub struct BlockBuild<Db, Insp = NoOpInspector> {
env: SharedSimEnv<Db, Insp>,
block: BuiltBlock,
finish_by: std::time::Instant,
max_gas: u64,
}
impl<Db, Insp> BlockBuild<Db, Insp>
where
Db: DatabaseRef + Send + Sync + 'static,
Insp: Inspector<Ctx<SimDb<Db>>> + Default + Sync + 'static,
{
#[allow(clippy::too_many_arguments)] pub fn new<C, B>(
db: Db,
constants: SignetSystemConstants,
cfg: C,
block: B,
finish_by: std::time::Instant,
concurrency_limit: usize,
sim_items: SimCache,
max_gas: u64,
) -> Self
where
C: Cfg,
B: Block,
{
let env = SimEnv::<Db, Insp>::new(
db,
constants,
cfg,
block,
finish_by,
concurrency_limit,
sim_items,
);
let finish_by = env.finish_by();
let number = env.block().number;
Self { env: env.into(), block: BuiltBlock::new(number), finish_by, max_gas }
}
async fn round(&mut self) {
let gas_allowed = self.max_gas - self.block.gas_used();
if let Some(simulated) = self.env.sim_round(gas_allowed).await {
tracing::debug!(score = %simulated.score, gas_used = simulated.gas_used, identifier = %simulated.item.identifier(), "Adding item to block");
self.block.ingest(simulated);
}
}
pub async fn build(mut self) -> BuiltBlock {
let mut i = 1;
loop {
let span = info_span!("build", round = i);
let finish_by = self.finish_by.into();
let next_round_time = Instant::now() + Duration::from_millis(SIM_SLEEP_MS);
if next_round_time >= finish_by {
debug!("Next round time is past the deadline, stopping sim loop");
break;
}
if self.env.sim_items().is_empty() {
tokio::time::sleep_until(next_round_time).await;
continue;
}
let fut = self.round().instrument(span);
select! {
biased;
_ = tokio::time::sleep_until(finish_by) => {
debug!("Deadline reached, stopping sim loop");
break;
},
_ = fut => {
i+= 1;
let remaining = self.env.sim_items().len();
trace!(%remaining, round = i, "Round completed");
}
}
}
debug!(rounds = i, transactions = self.block.transactions.len(), "Building completed",);
self.block
}
}
#[cfg(test)]
mod test {
use std::future::Future;
use super::*;
fn _build_fut_is_send<Db, Insp>(b: BlockBuild<Db, Insp>)
where
Db: DatabaseRef + Send + Sync + 'static,
Insp: Inspector<Ctx<SimDb<Db>>> + Default + Sync + 'static,
{
let _: Box<dyn Future<Output = BuiltBlock> + Send> = Box::new(b.build());
}
}