Skip to content
Closed
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ signet-zenith = { version = "0.13.0" }

trevm = { version = "0.29", features = ["concurrent-db", "test-utils"] }

alloy = { version = "1.0.35", features = [
alloy = { version = "1.0.37", features = [
"full",
"json-rpc",
"signer-aws",
Expand Down
20 changes: 10 additions & 10 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ use init4_bin_base::{
perms::OAuthConfig,
utils::{calc::SlotCalculator, provider::ProviderConfig},
};
use signet_constants::SignetSystemConstants;
use signet_constants::{SignetSystemConstants, pecorino};
use std::env;
use std::str::FromStr;
use trevm::revm::{context::BlockEnv, context_interface::block::BlobExcessGasAndPrice};

/// Sets up a block builder with test values
pub fn setup_test_config() -> Result<BuilderConfig> {
Copy link
Member

Choose a reason for hiding this comment

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

we should base this branch on the config changes in #174

let config = BuilderConfig {
// host_chain_id: signet_constants::pecorino::HOST_CHAIN_ID,
let pecorino_config = BuilderConfig {
host_rpc: "ws://host-rpc.pecorino.signet.sh"
.parse::<BuiltInConnectionString>()
.map(ProviderConfig::new)
Expand All @@ -32,10 +31,10 @@ pub fn setup_test_config() -> Result<BuilderConfig> {
.unwrap()
.try_into()
.unwrap(),
flashbots_endpoint: Some("https://relay-sepolia.flashbots.net:443".parse().unwrap()),
flashbots_endpoint: Some("https://host-builder-rpc.pecorino.signet.sh".parse().unwrap()),
quincey_url: "http://localhost:8080".into(),
sequencer_key: None,
builder_key: env::var("SEPOLIA_ETH_PRIV_KEY")
builder_key: env::var("BUILDER_KEY")
.unwrap_or_else(|_| B256::repeat_byte(0x42).to_string()),
builder_port: 8080,
builder_rewards_address: Address::default(),
Expand All @@ -56,20 +55,21 @@ pub fn setup_test_config() -> Result<BuilderConfig> {
max_host_gas_coefficient: Some(80),
constants: SignetSystemConstants::pecorino(),
};
Ok(config)
Ok(pecorino_config)
Copy link
Member

Choose a reason for hiding this comment

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

the test config is not a pecorino config. if we want a pecorino config it should be a separate setupd function

Copy link
Member

Choose a reason for hiding this comment

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

unit and integration tests should generally NOT be run against pecorino. the test config is for local uni ttesting

}

/// Returns a new signed test transaction with the provided nonce, value, and mpfpg.
pub fn new_signed_tx(
/// Returns a new signed test transaction with the provided nonce, value, mpfpg, and max fee.
pub fn new_signed_tx_with_max_fee(
wallet: &PrivateKeySigner,
nonce: u64,
value: U256,
mpfpg: u128,
max_fee_per_gas: u128,
) -> Result<TxEnvelope> {
let tx = TxEip1559 {
chain_id: 11155111,
chain_id: pecorino::RU_CHAIN_ID,
Copy link
Member

Choose a reason for hiding this comment

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

while we're fixing this, let's read it from the config by basing this work on #174 and using crate::config()

nonce,
max_fee_per_gas: 10_000_000,
max_fee_per_gas,
max_priority_fee_per_gas: mpfpg,
to: TxKind::Call(Address::from_str("0x0000000000000000000000000000000000000000").unwrap()),
value,
Expand Down
214 changes: 196 additions & 18 deletions tests/block_builder_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,16 @@ use alloy::{
use builder::{
tasks::{
block::sim::Simulator,
env::{EnvTask, Environment, SimEnv},
env::{Environment, SimEnv},
},
test_utils::{new_signed_tx, setup_logging, setup_test_config, test_block_env},
test_utils::{new_signed_tx_with_max_fee, setup_logging, setup_test_config, test_block_env},
};
use signet_sim::SimCache;
use std::time::{Duration, Instant};

/// Tests the `handle_build` method of the `Simulator`.
///
/// This test sets up a simulated environment using Anvil, creates a block builder,
/// and verifies that the block builder can successfully build a block containing
/// transactions from multiple senders.
#[ignore = "integration test"]
mod harness;
use harness::TestHarness;

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_handle_build() {
setup_logging();
Expand All @@ -41,30 +38,35 @@ async fn test_handle_build() {

// Create a rollup provider
let ru_provider = RootProvider::<Ethereum>::new_http(anvil_instance.endpoint_url());
let host_provider = config.connect_host_provider().await.unwrap();

let block_env =
EnvTask::new(config.clone(), host_provider.clone(), ru_provider.clone()).spawn().0;
// Create a host provider
let host_provider = config.connect_host_provider().await.unwrap();

let block_builder =
Simulator::new(&config, host_provider.clone(), ru_provider.clone(), block_env);
// Provide a dummy env receiver; this test calls handle_build directly and
// doesn't use the env watch channel.
let (_env_tx, env_rx) = tokio::sync::watch::channel(None);
let block_builder = Simulator::new(&config, host_provider, ru_provider.clone(), env_rx);

// Setup a sim cache
let sim_items = SimCache::new();

// Add two transactions from two senders to the sim cache
let tx_1 = new_signed_tx(&test_key_0, 0, U256::from(1_f64), 11_000).unwrap();
let tx_1 =
new_signed_tx_with_max_fee(&test_key_0, 0, U256::from(1_u64), 11_000, 10_000_000).unwrap();
sim_items.add_tx(tx_1, 0);

let tx_2 = new_signed_tx(&test_key_1, 0, U256::from(2_f64), 10_000).unwrap();
let tx_2 =
new_signed_tx_with_max_fee(&test_key_1, 0, U256::from(2_u64), 10_000, 10_000_000).unwrap();
sim_items.add_tx(tx_2, 0);

// Setup the block envs
let finish_by = Instant::now() + Duration::from_secs(2);
let ru_header = ru_provider.get_block(BlockId::latest()).await.unwrap().unwrap().header.inner;
let number = ru_header.number + 1;
let timestamp = ru_header.timestamp + config.slot_calculator.slot_duration();
let block_env = test_block_env(config, number, 7, timestamp);
let target_block_number = ru_header.number + 1;
let target_timestamp = ru_header.timestamp + config.slot_calculator.slot_duration();

assert!(target_timestamp > ru_header.timestamp);
let block_env = test_block_env(config, target_block_number, 7, target_timestamp);

// Spawn the block builder task
let sim_env = SimEnv {
Expand All @@ -78,3 +80,179 @@ async fn test_handle_build() {
assert!(got.is_ok());
assert!(got.unwrap().tx_count() == 2);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_ticks_and_emits() {
setup_logging();

// Build harness
let mut h = TestHarness::new().await.unwrap();

// Prepare two senders and fund them if needed from anvil default accounts
let keys = h.rollup.anvil().keys();
let test_key_0 = PrivateKeySigner::from_signing_key(keys[0].clone().into());

// Start simulator and tick a new SimEnv
h.start().await;

// Add a transaction into the sim cache
h.add_tx(&test_key_0, 0, U256::from(1_u64), 11_000);

// Tick using the latest rollup and host headers
h.mine_blocks(1).await.unwrap();

// Expect a SimResult. Use the harness slot duration plus a small buffer so
// we wait long enough for the simulator to complete heavy simulations.
let wait = Duration::from_secs(h.config.slot_calculator.slot_duration() + 5);
let got = h.recv_result(wait).await.expect("sim result");
assert_eq!(got.block.tx_count(), 1);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_simulates_full_flow() {
setup_logging();

// Build harness
let mut h = TestHarness::new().await.unwrap();

// Prepare two senders and fund them if needed from anvil default accounts
let keys = h.rollup.anvil().keys();
let test_key_0 = PrivateKeySigner::from_signing_key(keys[0].clone().into());
let test_key_1 = PrivateKeySigner::from_signing_key(keys[1].clone().into());

// Add two transactions into the sim cache
h.add_tx(&test_key_0, 0, U256::from(1_u64), 11_000);
h.add_tx(&test_key_1, 0, U256::from(2_u64), 10_000);

// Start simulator and tick a new SimEnv
h.start().await;

h.mine_blocks(1).await.unwrap();

// Expect a SimResult. Use the harness slot duration plus a small buffer.
let wait = Duration::from_secs(h.config.slot_calculator.slot_duration() + 5);
let got = h.recv_result(wait).await.expect("sim result");
assert_eq!(got.block.tx_count(), 2);
}

/// Ensure the harness can manually advance the Anvil chain.
// #[ignore = "integration test"]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_advances_anvil_chain() {
setup_logging();
let h = TestHarness::new().await.unwrap();

let (rollup, host) = h.get_headers().await.unwrap();

h.mine_blocks(2).await.unwrap();

let (new_rollup, new_host) = h.get_headers().await.unwrap();
assert_eq!(new_rollup.number, rollup.number + 2);
assert_eq!(new_host.number, host.number + 2);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_stops() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

h.start().await;

h.stop().await;

assert_eq!(h.simulator_handle.is_none(), true);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_timeout_without_results() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

h.start().await;

let wait = Duration::from_millis(250);
let got = h.recv_result(wait).await;

h.stop().await;

assert!(got.is_none(), "expected timeout when no blocks are mined");
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_start_is_idempotent() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

h.start().await;
let first_id = h.simulator_handle.as_ref().expect("simulator handle").id();

h.start().await;
let second_id = h.simulator_handle.as_ref().expect("simulator handle").id();

h.stop().await;

assert_eq!(first_id, second_id, "second start should reuse existing task");
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_stop_without_start() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

h.stop().await;

assert!(h.simulator_handle.is_none());
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_emits_multiple_results() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

let keys = h.rollup.anvil().keys();
let signer = PrivateKeySigner::from_signing_key(keys[0].clone().into());

// First tick uses a transaction added before the simulator starts.
h.add_tx(&signer, 0, U256::from(1_u64), 11_000);
h.start().await;
h.mine_blocks(1).await.unwrap();

let wait = Duration::from_secs(h.config.slot_calculator.slot_duration() + 5);
let first = h.recv_result(wait).await.expect("first sim result");
assert_eq!(first.block.tx_count(), 1);

// Second tick shouldn't need new transactions to emit a block.
h.mine_blocks(1).await.unwrap();
let second = h.recv_result(wait).await.expect("second sim result");
assert_eq!(second.block.tx_count(), 0);
assert_eq!(second.block.block_number(), first.block.block_number() + 1);

h.stop().await;
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_harness_result_matches_headers() {
setup_logging();
let mut h = TestHarness::new().await.unwrap();

let keys = h.rollup.anvil().keys();
let signer = PrivateKeySigner::from_signing_key(keys[0].clone().into());

// Capture the headers the harness should target.
let (prev_rollup, prev_host) = h.get_headers().await.unwrap();

h.add_tx(&signer, 0, U256::from(1_u64), 11_000);
h.start().await;
h.mine_blocks(1).await.unwrap();

let wait = Duration::from_secs(h.config.slot_calculator.slot_duration() + 5);
let got = h.recv_result(wait).await.expect("sim result");

assert_eq!(got.block.tx_count(), 1);
assert_eq!(got.rollup_block_number(), prev_rollup.number + 2);
assert_eq!(got.host_block_number(), prev_host.number + 2);
assert_eq!(got.prev_rollup().number, prev_rollup.number + 1);
assert_eq!(got.prev_host().number, prev_host.number + 1);

h.stop().await;
}
Loading
Loading