You already traced single_shard_tx_sim on paper in End-to-End Tests.
Here you change the real test in the repo: add visible trace points, add a committed-height check across validators (and learn when strict equality matches what a single run_until snapshot can show), and relate SimulationStats to how much simulated time you allow.
Everything runs under cargo test with --nocapture so you see effects immediately—no extra harness.
cargo and stable Rust working.SimCluster, run_until, and SubmitTransaction are not new.crates/simulation/tests/scenarios.rs — function single_shard_tx_sim (starts near the top of the single-shard tests section).crates/simulation/src/runner.rs — SimulationStats (Debug) if you want to read field meanings in source.From the hyperscale-rs repository root:
cargo test -p hyperscale-simulation --test scenarios single_shard_tx_sim -- --nocapture
Confirm the test passes. Scroll the output once: note genesis message, transaction hash, “initial consensus”, mempool status, and the final pass banner. You will align your own prints with this rhythm.
In single_shard_tx_sim (scenarios.rs), wrap the portable scenario so you can see setup vs drive:
#[test]
fn single_shard_tx_sim() {
let mut cluster = SimCluster::new(&liveness_config(), 42);
println!("[lab] cluster ready (4 hosts, 1 shard)");
single_shard_tx(&mut cluster);
println!("[lab] single_shard_tx returned");
}
Optional deeper cut: add markers inside hyperscale_scenarios::single_shard_tx around c.submit(...) and the accept assertion (portable body shared with production CI). Re-run the same cargo test command and confirm [lab] cluster ready appears before the scenario completes.
Cluster::committed_height(shard) returns the cluster-wide max tip. To see per-host lag, use host_committed_height(host, shard) on SimCluster (FaultableCluster trait).
[2, 2, 2, 1] (or similar) mid-runCommits propagate through the simulated network with nonzero latency. A snapshot while messages are still in flight can leave one host one height behind. That usually means eventual sync, not a fork.
Takeaway: requiring strict equality at an arbitrary midpoint is stricter than the harness guarantees. Prefer asserting after the scenario’s own await_tx_terminal succeeds, or document a weaker max−min ≤ 1 check.
After single_shard_tx(&mut cluster) returns (tx accepted), collect per-host heights:
use hyperscale_types::ShardId;
let heights: Vec<u64> = (0..4usize)
.map(|host| {
cluster
.host_committed_height(host, ShardId::ROOT)
.expect("host seats ROOT")
.inner()
})
.collect();
println!("[lab] per-host committed heights: {:?}", heights);
assert!(
heights.iter().all(|&h| h == heights[0]),
"validators not aligned on committed height: {:?}",
heights
);
Requirements: always print the vector; if equality fails right after accept, try one more short cluster.run_until(...) budget or document max−min ≤ 1 as your lab note.
Still inside the test (after height checks), peek at the underlying runner stats:
println!("[lab] stats: {:?}", cluster.runner().stats()); // or access via SimCluster fields if public
If runner() is not exposed, open crates/simulation/src/runner.rs and note which SimulationStats fields (events_processed, messages_sent, timers_set) the harness updates — then temporarily log them from a local branch of SimCluster or the scenario body.
Compare a full single_shard_tx run to a deliberately starved budget (e.g. call run_until with a tiny Budget before submit and observe the accept assertion fail). Restore the normal scenario before you finish.
schedule_initial_event for SubmitTransaction and observe how failure mode differs (mempool never sees the tx vs sync invariant).run_until slice, and explain why strict equality at an arbitrary time limit can fail under simulated gossip latency even when validators are not disagreeing on the canonical chain.events_processed and can prevent the transaction from reaching executed state even when heights look aligned.When ready, continue to production harness (Part II) or open #18 for broader transaction/substate test coverage.
After this lab, generalize: pick a real transaction shape, add a focused deterministic test, and open a draft PR with Refs #18.