Skip to content

feat: Sim Crate #33

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 17 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ signet-evm = { path = "crates/evm" }
signet-extract = { path = "crates/extract" }
signet-node = { path = "crates/node" }
signet-rpc = { path = "crates/rpc" }
signet-sim = { path = "crates/sim" }
signet-types = { path = "crates/types" }
signet-zenith = { path = "crates/zenith" }

Expand Down
4 changes: 2 additions & 2 deletions crates/evm/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ mod test {
config::{HostConfig, PredeployTokens, RollupConfig},
test_utils::*,
};
use trevm::{revm::database::in_memory_db::InMemoryDB, NoopCfg};
use trevm::revm::database::in_memory_db::InMemoryDB;

/// Make a fake block with a specific number.
pub(super) fn fake_block(number: u64) -> RecoveredBlock<Block> {
Expand Down Expand Up @@ -1038,7 +1038,7 @@ mod test {
}

fn trevm(&self) -> crate::EvmNeedsBlock<InMemoryDB> {
let mut trevm = test_signet_evm().fill_cfg(&NoopCfg);
let mut trevm = test_signet_evm();
for wallet in &self.wallets {
let address = wallet.address();
trevm.test_set_balance(address, U256::from(ETH_TO_WEI * 100));
Expand Down
20 changes: 14 additions & 6 deletions crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,26 @@ where
/// Test utilities for the Signet EVM impl.
#[cfg(any(test, feature = "test-utils"))]
pub mod test_utils {
use reth::revm::{context::CfgEnv, primitives::hardfork::SpecId};
use signet_types::test_utils::*;
use trevm::revm::database::in_memory_db::InMemoryDB;

/// Create a new Signet EVM with an in-memory database for testing.
pub fn test_signet_evm() -> super::EvmNeedsCfg<trevm::revm::database::in_memory_db::InMemoryDB>
pub fn test_signet_evm() -> super::EvmNeedsBlock<trevm::revm::database::in_memory_db::InMemoryDB>
{
let mut trevm = super::signet_evm(InMemoryDB::default(), TEST_CONSTANTS);
super::signet_evm(InMemoryDB::default(), TEST_CONSTANTS).fill_cfg(&TestCfg)
}

/// Test configuration for the Signet EVM.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TestCfg;

trevm.inner_mut_unchecked().data.ctx.modify_cfg(|cfg| {
cfg.chain_id = TEST_RU_CHAIN_ID;
});
impl trevm::Cfg for TestCfg {
fn fill_cfg_env(&self, cfg_env: &mut reth::revm::context::CfgEnv) {
let CfgEnv { chain_id, spec, .. } = cfg_env;

trevm
*chain_id = TEST_RU_CHAIN_ID;
*spec = SpecId::default();
}
}
}
27 changes: 27 additions & 0 deletions crates/sim/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "signet-sim"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true

[dependencies]
alloy.workspace = true
signet-bundle.workspace = true
signet-evm.workspace = true
signet-types.workspace = true
signet-zenith.workspace = true
tokio.workspace = true
tracing.workspace = true
trevm.workspace = true

[dev-dependencies]
signet-types = { workspace = true, features = ["test-utils"] }
signet-evm = { workspace = true, features = ["test-utils"] }
tracing-subscriber.workspace = true

[features]
test-utils = ["signet-types/test-utils", "signet-evm/test-utils"]
154 changes: 154 additions & 0 deletions crates/sim/src/built.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use alloy::{
consensus::{SidecarBuilder, SidecarCoder, TxEnvelope},
eips::Decodable2718,
primitives::{keccak256, Bytes, B256},
rlp::Buf,
};
use core::fmt;
use signet_bundle::SignetEthBundle;
use signet_zenith::{encode_txns, Alloy2718Coder, SignedOrder};
use std::sync::OnceLock;
use tracing::{error, trace};

use crate::{outcome::SimulatedItem, SimItem};

/// A block that has been built by the simulator.
#[derive(Clone, Default)]
pub struct BuiltBlock {
/// The host fill actions.
pub(crate) host_fills: Vec<SignedOrder>,
/// Transactions in the block.
pub(crate) transactions: Vec<TxEnvelope>,

/// The amount of gas used by the block so far
pub(crate) gas_used: u64,

/// Memoized raw encoding of the block.
pub(crate) raw_encoding: OnceLock<Bytes>,
/// Memoized hash of the block.
pub(crate) hash: OnceLock<B256>,
}

impl fmt::Debug for BuiltBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BuiltBlock")
.field("host_fills", &self.host_fills.len())
.field("transactions", &self.transactions.len())
.field("gas_used", &self.gas_used)
.finish()
}
}

impl BuiltBlock {
/// Create a new `BuiltBlock`
pub const fn new() -> Self {
Self {
host_fills: Vec::new(),
transactions: Vec::new(),
gas_used: 0,
raw_encoding: OnceLock::new(),
hash: OnceLock::new(),
}
}

/// Get the amount of gas used by the block.
pub const fn gas_used(&self) -> u64 {
self.gas_used
}

/// Get the number of transactions in the block.
pub fn tx_count(&self) -> usize {
self.transactions.len()
}

/// Check if the block is empty.
pub fn is_empty(&self) -> bool {
self.transactions.is_empty()
}

/// Get the current list of transactions included in this block.
#[allow(clippy::missing_const_for_fn)] // false positive, const deref
pub fn transactions(&self) -> &[TxEnvelope] {
&self.transactions
}

/// Unseal the block
pub(crate) fn unseal(&mut self) {
self.raw_encoding.take();
self.hash.take();
}

/// Seal the block by encoding the transactions and calculating the hash of
/// the block contents.
pub(crate) fn seal(&self) {
self.raw_encoding.get_or_init(|| encode_txns::<Alloy2718Coder>(&self.transactions).into());
self.hash.get_or_init(|| keccak256(self.raw_encoding.get().unwrap().as_ref()));
}

/// Ingest a transaction into the in-progress block.
pub fn ingest_tx(&mut self, tx: TxEnvelope) {
trace!(hash = %tx.tx_hash(), "ingesting tx");
self.unseal();
self.transactions.push(tx);
}

/// Ingest a bundle into the in-progress block.
/// Ignores Signed Orders for now.
pub fn ingest_bundle(&mut self, bundle: SignetEthBundle) {
trace!(replacement_uuid = bundle.replacement_uuid(), "adding bundle to block");

let txs = bundle
.bundle
.txs
.into_iter()
.map(|tx| TxEnvelope::decode_2718(&mut tx.chunk()))
.collect::<Result<Vec<_>, _>>();

if let Ok(txs) = txs {
self.unseal();
// extend the transactions with the decoded transactions.
// As this builder does not provide bundles landing "top of block", its fine to just extend.
self.transactions.extend(txs);

if let Some(host_fills) = bundle.host_fills {
self.host_fills.push(host_fills);
}
} else {
error!("failed to decode bundle. dropping");
}
}

/// Ingest a simulated item, extending the block.
pub fn ingest(&mut self, item: SimulatedItem) {
self.gas_used += item.gas_used;

match item.item {
SimItem::Bundle(bundle) => self.ingest_bundle(bundle),
SimItem::Tx(tx) => self.ingest_tx(tx),
}
}

/// Encode the in-progress block.
pub(crate) fn encode_raw(&self) -> &Bytes {
self.seal();
self.raw_encoding.get().unwrap()
}

/// Calculate the hash of the in-progress block, finishing the block.
pub fn contents_hash(&self) -> &B256 {
self.seal();
self.hash.get().unwrap()
}

/// Convert the in-progress block to sign request contents.
pub fn encode_calldata(&self) -> &Bytes {
self.encode_raw()
}

/// Convert the in-progress block to a blob transaction sidecar.
pub fn encode_blob<T: SidecarCoder + Default>(&self) -> SidecarBuilder<T> {
let mut coder = SidecarBuilder::<T>::default();
coder.ingest(self.encode_raw());
coder
}
}
Loading
Loading