diff --git a/CHANGELOG.sf.md b/CHANGELOG.sf.md index 0c0b665575..c141b8c953 100644 --- a/CHANGELOG.sf.md +++ b/CHANGELOG.sf.md @@ -1,5 +1,12 @@ ## v1.2.0-fh +### Added + +* Added Firehose tracing-regression coverage for Base transactions: a prestate-driven test (block + invariants + JSON projection golden) plus an end-to-end `base-system-tests` integration test + tracing a B-20 precompile transfer. The chain-agnostic capture / invariants / projection / golden + framework lives in the shared `firehose-tracer-test` crate (`evm-firehose-tracer-rs` `5.3.0`). + ### Changed * Bumped base to `v1.2.0`. @@ -12,11 +19,10 @@ ### Fixed -* Regenerated the stale `base-firehose-tests` `nop_transfer` golden block. It had not been refreshed - since it was created against reth `v1.11.4-fh`, so it still recorded `gas_limit: 30000000` for the - EIP-4788 beacon-roots system call; the current tracer reports `31566720`. The mismatch was already - present in `v1.1.1-fh-1` and is unrelated to the `v1.2.0` bump — verified by re-running the test - on the pre-merge tree. No other field of the captured block changed. +* Replaced the stale `base-firehose-tests` `nop_transfer` full-protobuf golden with a JSON + projection plus property invariants. The full-block golden recorded `gas_limit: 30000000` for the + EIP-4788 beacon-roots system call while the current tracer reports `31566720`; the projection + excludes volatile fields like gas, so it no longer rots on such changes. ### Notes diff --git a/CLAUDE.md b/CLAUDE.md index 0ff5ae2a6a..ee8af2d6d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,3 +19,7 @@ Use structured tracing instead of interpolated strings. Always use key=value fie Keep unit tests colocated with their implementation. Do not introduce standalone `tests.rs` modules for unit tests; define tests in the same `.rs` implementation file/module inside a `#[cfg(test)] mod tests { ... }` block. `#[cfg(test)] mod tests { ... }` must always be placed at the end of the file, after all non-test code. + +## Firehose tracing tests + +The tracing-regression framework (capture, invariants, JSON projection, golden) is chain-agnostic and lives in the `firehose-tracer-test` crate of `evm-firehose-tracer-rs`; `base-firehose-tests` re-exports it and adds the Base bindings (`BaseFirehoseCapture::install`, `run_prestate`). Regenerate any golden with `GOLDEN_UPDATE=1`. The Docker-backed `base-system-tests --test firehose_b20` must run with `RUSTFLAGS="-C debug-assertions=off"` (reth's `deferred_trie::wait_cloned` debug-asserts from a Rayon worker). diff --git a/Cargo.lock b/Cargo.lock index 40ca627a27..5e8a67f9c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4917,14 +4917,13 @@ dependencies = [ "base-execution-firehose", "eyre", "firehose-tracer", + "firehose-tracer-test", "k256", - "prost 0.14.4", "reth-firehose", "reth-firehose-tests", "reth-primitives-traits", "reth-revm", "revm", - "serde", "serde_json", ] @@ -6457,6 +6456,7 @@ dependencies = [ "base-execution-chainspec", "base-execution-rpc", "base-execution-txpool", + "base-firehose-tests", "base-flashblocks", "base-flashblocks-node", "base-node-core", @@ -9817,8 +9817,9 @@ checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" [[package]] name = "firehose-tracer" -version = "5.2.2" -source = "git+https://github.com/streamingfast/evm-firehose-tracer-rs.git?tag=v5.2.2#8cbc6f9fc175455ddd285fd3dcfdaa44433a76f0" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f116192c9a01744168c80024324eb8d25fa94917f644a84236da685041eb8b" dependencies = [ "alloy-consensus 2.0.5", "alloy-eips 2.0.5", @@ -9837,6 +9838,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "firehose-tracer-test" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23840e080c438da1ff6feb80b7bfcc6474f330c2e49579651c8d5bb7de0be325" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "base64-simd", + "eyre", + "firehose-tracer", + "hex", + "k256", + "prost 0.14.4", + "prost-reflect", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "fixed-cache" version = "0.1.10" @@ -15660,6 +15681,16 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "prost-reflect" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b80ea363c31af2de2b92e3c07ed1156628f7838c4afb4df75ee78a37fedbd1" +dependencies = [ + "prost 0.14.4", + "prost-types 0.14.4", +] + [[package]] name = "prost-types" version = "0.13.5" diff --git a/Cargo.toml b/Cargo.toml index 6519dea55b..f06fc33ba0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -650,13 +650,12 @@ ark-bls12-381 = { version = "0.5.0", default-features = false } # Firehose tracer (defined by the streamingfast reth fork, firehose/2.x branch). reth-firehose = { git = "https://github.com/streamingfast/reth.git", tag = "v2.3.0-fh-5" } reth-firehose-tests = { git = "https://github.com/streamingfast/reth.git", tag = "v2.3.0-fh-5" } -firehose-tracer = "5.2.1" +firehose-tracer = "5.3.0" -[patch.crates-io] -# Hotfix: gate the "mismatch between call logs and receipt logs" panic behind the -# FIREHOSE_TRACER_IGNORE_LOG_MISMATCH env var so it logs and skips instead of crashing. -firehose-tracer = { git = "https://github.com/streamingfast/evm-firehose-tracer-rs.git", tag = "v5.2.2" } +# Shared tracing-regression framework (capture / invariants / projection / golden). +firehose-tracer-test = "5.3.0" +[patch.crates-io] # SF fork of alloy-evm that routes system calls (EIP-4788, EIP-2935, etc.) through the # Inspector, so Firehose tracing observes them. Matches the patch in streamingfast/reth # v2.3.0-fh. See https://github.com/alloy-rs/evm/pull/323 for the upstream PR. diff --git a/crates/execution/firehose-tests/Cargo.toml b/crates/execution/firehose-tests/Cargo.toml index 504ee0b37c..b942e837c5 100644 --- a/crates/execution/firehose-tests/Cargo.toml +++ b/crates/execution/firehose-tests/Cargo.toml @@ -22,6 +22,7 @@ base-execution-chainspec.workspace = true reth-firehose.workspace = true reth-revm.workspace = true firehose-tracer.workspace = true +firehose-tracer-test.workspace = true reth-firehose-tests.workspace = true reth-primitives-traits.workspace = true @@ -36,8 +37,6 @@ revm.workspace = true # encoding & errors eyre.workspace = true -prost.workspace = true -serde.workspace = true serde_json.workspace = true [[test]] diff --git a/crates/execution/firehose-tests/README.md b/crates/execution/firehose-tests/README.md index 6b370d35e6..dd8cfa4959 100644 --- a/crates/execution/firehose-tests/README.md +++ b/crates/execution/firehose-tests/README.md @@ -1 +1,20 @@ -Integration test harness for the Firehose tracer on Base / OP Stack chains. +Base-side bindings for the Firehose tracing-regression framework. + +The framework itself — `FirehoseCapture`, `BlockInvariants`, `BlockProjection` / `SymbolTable` / +`VolatilePolicy`, `Golden` — is chain-agnostic and lives in `firehose-tracer-test` (in +`evm-firehose-tracer-rs`). This crate re-exports it and adds the two Base-specific pieces: + +- `run_prestate` — replays a hand-written `prestate.json` (genesis + block context + one signed + transaction) through the tracer with no node involved. Fast, hermetic, but the fixture is + hand-maintained. +- `BaseFirehoseCapture::install` — the reth binding: installs the process-wide buffer-backed tracer + (`reth_firehose::init_tracer_with_buffer`) and hands the buffer to `FirehoseCapture`, so a test + driving a real node captures the `FIRE BLOCK` lines it emits. + +Assert on the captured `Block` with: + +- `BlockInvariants` — content-independent property assertions that never need regenerating. +- `BlockProjection` + `Golden` — a descriptor-driven JSON golden with volatile fields removed by a + `VolatilePolicy` (`none()` for reproducible prestate replays, `live_node()` for system tests). + +Regenerate every golden with `GOLDEN_UPDATE=1`. diff --git a/crates/execution/firehose-tests/src/capture.rs b/crates/execution/firehose-tests/src/capture.rs new file mode 100644 index 0000000000..c0b5582a6a --- /dev/null +++ b/crates/execution/firehose-tests/src/capture.rs @@ -0,0 +1,40 @@ +//! Base's reth binding for the shared [`FirehoseCapture`] framework. +//! +//! The capture / invariants / projection / golden machinery lives in `firehose-tracer-test` and is +//! chain-agnostic. Installing the process-wide tracer is the one part that cannot: the +//! `GLOBAL_TRACER` singleton and the `is_tracer_initialized()` gate the live engine path checks +//! (`crates/execution/engine-tree/src/validator.rs`) both live in `reth-firehose`. This module is +//! the thin adapter that installs reth's buffer-backed tracer and hands the buffer to +//! [`FirehoseCapture`]. + +use firehose_tracer_test::FirehoseCapture; + +/// Base-specific installer for the shared Firehose capture framework. +#[derive(Debug)] +pub struct BaseFirehoseCapture; + +impl BaseFirehoseCapture { + /// Installs the process-wide buffer-backed tracer and returns a capture handle over it. + /// + /// Must be called before any block is validated by the node under test — the traced execution + /// path is gated on `reth_firehose::is_tracer_initialized()`. Panics if a tracer was already + /// installed in this process (the tracer is a process-wide singleton, so a test binary using + /// this holds a single `#[test]`). + /// + /// The fork timestamps only affect how block contents are mapped, not whether a block is + /// emitted; `Some(0)` means "active from genesis" and `None` means "never". + pub fn install( + chain_id: u64, + shanghai_time: Option, + cancun_time: Option, + prague_time: Option, + ) -> FirehoseCapture { + let buffer = reth_firehose::init_tracer_with_buffer( + chain_id, + shanghai_time, + cancun_time, + prague_time, + ); + FirehoseCapture::new(buffer) + } +} diff --git a/crates/execution/firehose-tests/src/lib.rs b/crates/execution/firehose-tests/src/lib.rs index 4b9a61daf9..7def4a0a6e 100644 --- a/crates/execution/firehose-tests/src/lib.rs +++ b/crates/execution/firehose-tests/src/lib.rs @@ -1,6 +1,13 @@ #![doc = include_str!("../README.md")] mod prestate; -pub use prestate::{assert_block_equals_golden, run_prestate}; +pub use prestate::run_prestate; +mod capture; +pub use capture::BaseFirehoseCapture; +// The tracing-regression framework is chain-agnostic and lives in `firehose-tracer-test`. +pub use firehose_tracer_test::{ + BlockDiff, BlockInvariants, BlockProjection, FirehoseCapture, Golden, InvariantConfig, + SymbolTable, Violation, VolatilePolicy, +}; pub use reth_firehose_tests::RunOutcome; diff --git a/crates/execution/firehose-tests/src/prestate.rs b/crates/execution/firehose-tests/src/prestate.rs index ffe6666f3d..7fa77f9fbe 100644 --- a/crates/execution/firehose-tests/src/prestate.rs +++ b/crates/execution/firehose-tests/src/prestate.rs @@ -34,8 +34,8 @@ use revm::database::{CacheDB, EmptyDB}; /// /// `case_folder` must contain `prestate.json` (Base/OP-style genesis + block context + EIP-2718 /// encoded signed transaction). The captured `Block` is the protobuf parsed from the single -/// `FIRE BLOCK` line emitted for the executed block; assert against a `.binpb` golden via -/// [`assert_block_equals_golden`]. +/// `FIRE BLOCK` line emitted for the executed block; assert against it with `BlockInvariants` and a +/// `BlockProjection` golden (see `tests/prestate.rs`). pub fn run_prestate(case_folder: &Path) -> eyre::Result { use alloy_consensus::Block; use base_common_consensus::BaseBlockBody; @@ -148,5 +148,3 @@ fn build_op_header( ..Default::default() } } - -pub use reth_firehose_tests::assert_block_equals_golden; diff --git a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.binpb b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.binpb deleted file mode 100644 index 04f589f9a0..0000000000 Binary files a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.binpb and /dev/null differ diff --git a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.txt b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.txt deleted file mode 100644 index 9fbbd6412e..0000000000 --- a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.actual.txt +++ /dev/null @@ -1,1512 +0,0 @@ -Block { - hash: [ - 244, - 70, - 55, - 117, - 91, - 165, - 149, - 142, - 239, - 74, - 68, - 35, - 52, - 24, - 151, - 187, - 189, - 56, - 23, - 230, - 133, - 174, - 98, - 242, - 83, - 109, - 2, - 94, - 165, - 189, - 157, - 118, - ], - number: 2099, - size: 704, - header: Some( - BlockHeader { - parent_hash: [ - 48, - 137, - 76, - 139, - 52, - 35, - 133, - 118, - 22, - 71, - 5, - 190, - 45, - 105, - 11, - 56, - 241, - 195, - 122, - 201, - 86, - 191, - 190, - 143, - 155, - 210, - 21, - 20, - 54, - 213, - 95, - 227, - ], - uncle_hash: [ - 29, - 204, - 77, - 232, - 222, - 199, - 93, - 122, - 171, - 133, - 181, - 103, - 182, - 204, - 212, - 26, - 211, - 18, - 69, - 27, - 148, - 138, - 116, - 19, - 240, - 161, - 66, - 253, - 64, - 212, - 147, - 71, - ], - coinbase: [ - 249, - 126, - 24, - 12, - 5, - 14, - 90, - 176, - 114, - 33, - 26, - 210, - 194, - 19, - 235, - 90, - 238, - 77, - 241, - 52, - ], - state_root: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - transactions_root: [ - 45, - 248, - 49, - 224, - 47, - 175, - 228, - 66, - 158, - 133, - 134, - 224, - 213, - 130, - 65, - 27, - 168, - 155, - 228, - 101, - 66, - 102, - 128, - 255, - 58, - 183, - 169, - 160, - 219, - 6, - 168, - 53, - ], - receipt_root: [ - 86, - 232, - 31, - 23, - 27, - 204, - 85, - 166, - 255, - 131, - 69, - 230, - 146, - 192, - 248, - 110, - 91, - 72, - 224, - 27, - 153, - 108, - 173, - 192, - 1, - 98, - 47, - 181, - 227, - 99, - 180, - 33, - ], - logs_bloom: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - difficulty: Some( - BigInt { - bytes: [ - 0, - ], - }, - ), - total_difficulty: None, - number: 2099, - gas_limit: 30000000, - gas_used: 0, - timestamp: Some( - Timestamp { - seconds: 1742240844, - nanos: 0, - }, - ), - extra_data: [], - mix_hash: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - nonce: 0, - hash: [ - 244, - 70, - 55, - 117, - 91, - 165, - 149, - 142, - 239, - 74, - 68, - 35, - 52, - 24, - 151, - 187, - 189, - 56, - 23, - 230, - 133, - 174, - 98, - 242, - 83, - 109, - 2, - 94, - 165, - 189, - 157, - 118, - ], - base_fee_per_gas: Some( - BigInt { - bytes: [ - 7, - ], - }, - ), - withdrawals_root: [ - 86, - 232, - 31, - 23, - 27, - 204, - 85, - 166, - 255, - 131, - 69, - 230, - 146, - 192, - 248, - 110, - 91, - 72, - 224, - 27, - 153, - 108, - 173, - 192, - 1, - 98, - 47, - 181, - 227, - 99, - 180, - 33, - ], - tx_dependency: None, - blob_gas_used: Some( - 0, - ), - excess_blob_gas: Some( - 0, - ), - parent_beacon_root: Some( - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - ), - requests_hash: None, - slot_number: None, - }, - ), - uncles: [], - transaction_traces: [ - TransactionTrace { - to: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - nonce: 0, - gas_price: Some( - BigInt { - bytes: [ - 89, - 104, - 47, - 7, - ], - }, - ), - gas_limit: 21000, - value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - input: [], - v: [], - r: [ - 142, - 102, - 85, - 21, - 150, - 87, - 196, - 63, - 81, - 90, - 146, - 56, - 158, - 25, - 30, - 207, - 74, - 107, - 157, - 31, - 152, - 137, - 57, - 215, - 150, - 254, - 11, - 206, - 232, - 128, - 210, - 128, - ], - s: [ - 38, - 224, - 156, - 207, - 122, - 41, - 70, - 107, - 26, - 103, - 203, - 241, - 57, - 223, - 30, - 1, - 224, - 123, - 146, - 41, - 226, - 20, - 64, - 224, - 31, - 162, - 67, - 98, - 31, - 109, - 105, - 186, - ], - gas_used: 21000, - r#type: TrxTypeDynamicFee, - access_list: [], - max_fee_per_gas: Some( - BigInt { - bytes: [ - 3, - 185, - 172, - 160, - 7, - ], - }, - ), - max_priority_fee_per_gas: Some( - BigInt { - bytes: [ - 89, - 104, - 47, - 0, - ], - }, - ), - index: 0, - hash: [ - 171, - 208, - 102, - 195, - 87, - 28, - 17, - 24, - 11, - 151, - 153, - 85, - 194, - 54, - 204, - 59, - 28, - 91, - 50, - 215, - 231, - 102, - 236, - 65, - 64, - 253, - 24, - 224, - 21, - 48, - 45, - 32, - ], - from: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - return_data: [], - public_key: [], - begin_ordinal: 1, - end_ordinal: 10, - status: Succeeded, - receipt: Some( - TransactionReceipt { - state_root: [], - cumulative_gas_used: 21000, - logs_bloom: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - logs: [], - blob_gas_used: None, - blob_gas_price: None, - }, - ), - calls: [ - Call { - index: 1, - parent_index: 0, - depth: 0, - call_type: Call, - caller: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - address: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - address_delegates_to: None, - value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - gas_limit: 0, - gas_consumed: 0, - return_data: [], - input: [], - executed_code: false, - suicide: false, - keccak_preimages: {}, - storage_changes: [], - balance_changes: [ - BalanceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 173, - 197, - 222, - 160, - 0, - 0, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 145, - 31, - 180, - 78, - 73, - 200, - ], - }, - ), - reason: GasBuy, - ordinal: 2, - }, - BalanceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 145, - 31, - 180, - 78, - 73, - 200, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 54, - 39, - 232, - 218, - 108, - 12, - 234, - 73, - 200, - ], - }, - ), - reason: Transfer, - ordinal: 5, - }, - BalanceChange { - address: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - old_value: None, - new_value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - reason: Transfer, - ordinal: 6, - }, - BalanceChange { - address: [ - 249, - 126, - 24, - 12, - 5, - 14, - 90, - 176, - 114, - 33, - 26, - 210, - 194, - 19, - 235, - 90, - 238, - 77, - 241, - 52, - ], - old_value: Some( - BigInt { - bytes: [ - 58, - 123, - 205, - 225, - 64, - 48, - 190, - 25, - 86, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 58, - 123, - 205, - 253, - 230, - 91, - 13, - 145, - 86, - ], - }, - ), - reason: RewardTransactionFee, - ordinal: 8, - }, - BalanceChange { - address: [ - 66, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 25, - ], - old_value: None, - new_value: Some( - BigInt { - bytes: [ - 2, - 62, - 56, - ], - }, - ), - reason: RewardTransactionFee, - ordinal: 9, - }, - ], - nonce_changes: [ - NonceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: 0, - new_value: 1, - ordinal: 3, - }, - ], - logs: [], - code_changes: [], - gas_changes: [], - status_failed: false, - status_reverted: false, - failure_reason: "", - state_reverted: false, - begin_ordinal: 4, - end_ordinal: 7, - account_creations: [], - }, - ], - blob_gas: None, - blob_gas_fee_cap: None, - blob_hashes: [], - set_code_authorizations: [], - }, - ], - balance_changes: [], - detail_level: DetaillevelExtended, - code_changes: [], - system_calls: [], - withdrawals: [], - ver: 5, -} \ No newline at end of file diff --git a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.binpb b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.binpb deleted file mode 100644 index dfc030f92d..0000000000 Binary files a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.binpb and /dev/null differ diff --git a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.expected.txt b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.expected.txt deleted file mode 100644 index dcb54dded9..0000000000 --- a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.expected.txt +++ /dev/null @@ -1,1618 +0,0 @@ -Block { - hash: [ - 244, - 70, - 55, - 117, - 91, - 165, - 149, - 142, - 239, - 74, - 68, - 35, - 52, - 24, - 151, - 187, - 189, - 56, - 23, - 230, - 133, - 174, - 98, - 242, - 83, - 109, - 2, - 94, - 165, - 189, - 157, - 118, - ], - number: 2099, - size: 704, - header: Some( - BlockHeader { - parent_hash: [ - 48, - 137, - 76, - 139, - 52, - 35, - 133, - 118, - 22, - 71, - 5, - 190, - 45, - 105, - 11, - 56, - 241, - 195, - 122, - 201, - 86, - 191, - 190, - 143, - 155, - 210, - 21, - 20, - 54, - 213, - 95, - 227, - ], - uncle_hash: [ - 29, - 204, - 77, - 232, - 222, - 199, - 93, - 122, - 171, - 133, - 181, - 103, - 182, - 204, - 212, - 26, - 211, - 18, - 69, - 27, - 148, - 138, - 116, - 19, - 240, - 161, - 66, - 253, - 64, - 212, - 147, - 71, - ], - coinbase: [ - 249, - 126, - 24, - 12, - 5, - 14, - 90, - 176, - 114, - 33, - 26, - 210, - 194, - 19, - 235, - 90, - 238, - 77, - 241, - 52, - ], - state_root: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - transactions_root: [ - 45, - 248, - 49, - 224, - 47, - 175, - 228, - 66, - 158, - 133, - 134, - 224, - 213, - 130, - 65, - 27, - 168, - 155, - 228, - 101, - 66, - 102, - 128, - 255, - 58, - 183, - 169, - 160, - 219, - 6, - 168, - 53, - ], - receipt_root: [ - 86, - 232, - 31, - 23, - 27, - 204, - 85, - 166, - 255, - 131, - 69, - 230, - 146, - 192, - 248, - 110, - 91, - 72, - 224, - 27, - 153, - 108, - 173, - 192, - 1, - 98, - 47, - 181, - 227, - 99, - 180, - 33, - ], - logs_bloom: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - difficulty: Some( - BigInt { - bytes: [ - 0, - ], - }, - ), - total_difficulty: None, - number: 2099, - gas_limit: 30000000, - gas_used: 0, - timestamp: Some( - Timestamp { - seconds: 1742240844, - nanos: 0, - }, - ), - extra_data: [], - mix_hash: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - nonce: 0, - hash: [ - 244, - 70, - 55, - 117, - 91, - 165, - 149, - 142, - 239, - 74, - 68, - 35, - 52, - 24, - 151, - 187, - 189, - 56, - 23, - 230, - 133, - 174, - 98, - 242, - 83, - 109, - 2, - 94, - 165, - 189, - 157, - 118, - ], - base_fee_per_gas: Some( - BigInt { - bytes: [ - 7, - ], - }, - ), - withdrawals_root: [ - 86, - 232, - 31, - 23, - 27, - 204, - 85, - 166, - 255, - 131, - 69, - 230, - 146, - 192, - 248, - 110, - 91, - 72, - 224, - 27, - 153, - 108, - 173, - 192, - 1, - 98, - 47, - 181, - 227, - 99, - 180, - 33, - ], - tx_dependency: None, - blob_gas_used: Some( - 0, - ), - excess_blob_gas: Some( - 0, - ), - parent_beacon_root: Some( - [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - ), - requests_hash: None, - slot_number: None, - }, - ), - uncles: [], - transaction_traces: [ - TransactionTrace { - to: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - nonce: 0, - gas_price: Some( - BigInt { - bytes: [ - 89, - 104, - 47, - 7, - ], - }, - ), - gas_limit: 21000, - value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - input: [], - v: [], - r: [ - 142, - 102, - 85, - 21, - 150, - 87, - 196, - 63, - 81, - 90, - 146, - 56, - 158, - 25, - 30, - 207, - 74, - 107, - 157, - 31, - 152, - 137, - 57, - 215, - 150, - 254, - 11, - 206, - 232, - 128, - 210, - 128, - ], - s: [ - 38, - 224, - 156, - 207, - 122, - 41, - 70, - 107, - 26, - 103, - 203, - 241, - 57, - 223, - 30, - 1, - 224, - 123, - 146, - 41, - 226, - 20, - 64, - 224, - 31, - 162, - 67, - 98, - 31, - 109, - 105, - 186, - ], - gas_used: 21000, - r#type: TrxTypeDynamicFee, - access_list: [], - max_fee_per_gas: Some( - BigInt { - bytes: [ - 3, - 185, - 172, - 160, - 7, - ], - }, - ), - max_priority_fee_per_gas: Some( - BigInt { - bytes: [ - 89, - 104, - 47, - 0, - ], - }, - ), - index: 0, - hash: [ - 171, - 208, - 102, - 195, - 87, - 28, - 17, - 24, - 11, - 151, - 153, - 85, - 194, - 54, - 204, - 59, - 28, - 91, - 50, - 215, - 231, - 102, - 236, - 65, - 64, - 253, - 24, - 224, - 21, - 48, - 45, - 32, - ], - from: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - return_data: [], - public_key: [], - begin_ordinal: 3, - end_ordinal: 12, - status: Succeeded, - receipt: Some( - TransactionReceipt { - state_root: [], - cumulative_gas_used: 21000, - logs_bloom: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - logs: [], - blob_gas_used: None, - blob_gas_price: None, - }, - ), - calls: [ - Call { - index: 1, - parent_index: 0, - depth: 0, - call_type: Call, - caller: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - address: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - address_delegates_to: None, - value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - gas_limit: 0, - gas_consumed: 0, - return_data: [], - input: [], - executed_code: false, - suicide: false, - keccak_preimages: {}, - storage_changes: [], - balance_changes: [ - BalanceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 173, - 197, - 222, - 160, - 0, - 0, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 145, - 31, - 180, - 78, - 73, - 200, - ], - }, - ), - reason: GasBuy, - ordinal: 4, - }, - BalanceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: Some( - BigInt { - bytes: [ - 54, - 53, - 201, - 145, - 31, - 180, - 78, - 73, - 200, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 54, - 39, - 232, - 218, - 108, - 12, - 234, - 73, - 200, - ], - }, - ), - reason: Transfer, - ordinal: 7, - }, - BalanceChange { - address: [ - 167, - 20, - 172, - 151, - 243, - 65, - 135, - 152, - 98, - 11, - 68, - 134, - 249, - 63, - 132, - 150, - 147, - 249, - 146, - 100, - ], - old_value: None, - new_value: Some( - BigInt { - bytes: [ - 13, - 224, - 182, - 179, - 167, - 100, - 0, - 0, - ], - }, - ), - reason: Transfer, - ordinal: 8, - }, - BalanceChange { - address: [ - 249, - 126, - 24, - 12, - 5, - 14, - 90, - 176, - 114, - 33, - 26, - 210, - 194, - 19, - 235, - 90, - 238, - 77, - 241, - 52, - ], - old_value: Some( - BigInt { - bytes: [ - 58, - 123, - 205, - 225, - 64, - 48, - 190, - 25, - 86, - ], - }, - ), - new_value: Some( - BigInt { - bytes: [ - 58, - 123, - 205, - 253, - 230, - 91, - 13, - 145, - 86, - ], - }, - ), - reason: RewardTransactionFee, - ordinal: 10, - }, - BalanceChange { - address: [ - 66, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 25, - ], - old_value: None, - new_value: Some( - BigInt { - bytes: [ - 2, - 62, - 56, - ], - }, - ), - reason: RewardTransactionFee, - ordinal: 11, - }, - ], - nonce_changes: [ - NonceChange { - address: [ - 243, - 159, - 214, - 229, - 26, - 173, - 136, - 246, - 244, - 206, - 106, - 184, - 130, - 114, - 121, - 207, - 255, - 185, - 34, - 102, - ], - old_value: 0, - new_value: 1, - ordinal: 5, - }, - ], - logs: [], - code_changes: [], - gas_changes: [], - status_failed: false, - status_reverted: false, - failure_reason: "", - state_reverted: false, - begin_ordinal: 6, - end_ordinal: 9, - account_creations: [], - }, - ], - blob_gas: None, - blob_gas_fee_cap: None, - blob_hashes: [], - set_code_authorizations: [], - }, - ], - balance_changes: [], - detail_level: DetaillevelExtended, - code_changes: [], - system_calls: [ - Call { - index: 1, - parent_index: 0, - depth: 0, - call_type: Call, - caller: [ - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 255, - 254, - ], - address: [ - 0, - 15, - 61, - 246, - 215, - 50, - 128, - 126, - 241, - 49, - 159, - 183, - 184, - 187, - 133, - 34, - 208, - 190, - 172, - 2, - ], - address_delegates_to: None, - value: None, - gas_limit: 31566720, - gas_consumed: 0, - return_data: [], - input: [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ], - executed_code: false, - suicide: false, - keccak_preimages: {}, - storage_changes: [], - balance_changes: [], - nonce_changes: [], - logs: [], - code_changes: [], - gas_changes: [], - status_failed: false, - status_reverted: false, - failure_reason: "", - state_reverted: false, - begin_ordinal: 1, - end_ordinal: 2, - account_creations: [], - }, - ], - withdrawals: [], - ver: 5, -} \ No newline at end of file diff --git a/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.json b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.json new file mode 100644 index 0000000000..637935ac72 --- /dev/null +++ b/crates/execution/firehose-tests/tests/cases/nop_transfer/block.2099.json @@ -0,0 +1,74 @@ +{ + "begin_ordinal": 3, + "calls": [ + { + "address": "@recipient", + "balance_changes": [ + { + "address": "@sender", + "new_value": "0x3635c9911fb44e49c8", + "old_value": "0x3635c9adc5dea00000", + "ordinal": 4, + "reason": "REASON_GAS_BUY" + }, + { + "address": "@sender", + "new_value": "0x3627e8da6c0cea49c8", + "old_value": "0x3635c9911fb44e49c8", + "ordinal": 7, + "reason": "REASON_TRANSFER" + }, + { + "address": "@recipient", + "new_value": "0x0de0b6b3a7640000", + "ordinal": 8, + "reason": "REASON_TRANSFER" + }, + { + "address": "@coinbase", + "new_value": "0x3a7bcdfde65b0d9156", + "old_value": "0x3a7bcde14030be1956", + "ordinal": 10, + "reason": "REASON_REWARD_TRANSACTION_FEE" + }, + { + "address": "0x4200000000000000000000000000000000000019", + "new_value": "0x023e38", + "ordinal": 11, + "reason": "REASON_REWARD_TRANSACTION_FEE" + } + ], + "begin_ordinal": 6, + "call_type": "CALL", + "caller": "@sender", + "end_ordinal": 9, + "index": 1, + "nonce_changes": [ + { + "address": "@sender", + "new_value": 1, + "ordinal": 5 + } + ], + "value": "0x0de0b6b3a7640000" + } + ], + "end_ordinal": 12, + "from": "@sender", + "gas_limit": 21000, + "gas_price": "0x59682f07", + "gas_used": 21000, + "hash": "0xabd066c3571c11180b979955c236cc3b1c5b32d7e766ec4140fd18e015302d20", + "max_fee_per_gas": "0x03b9aca007", + "max_priority_fee_per_gas": "0x59682f00", + "r": "0x8e6655159657c43f515a92389e191ecf4a6b9d1f988939d796fe0bcee880d280", + "receipt": { + "cumulative_gas_used": 21000, + "logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + }, + "s": "0x26e09ccf7a29466b1a67cbf139df1e01e07b9229e21440e01fa243621f6d69ba", + "status": "SUCCEEDED", + "to": "@recipient", + "type": "TRX_TYPE_DYNAMIC_FEE", + "value": "0x0de0b6b3a7640000" +} diff --git a/crates/execution/firehose-tests/tests/prestate.rs b/crates/execution/firehose-tests/tests/prestate.rs index 952a88f15f..645e493b98 100644 --- a/crates/execution/firehose-tests/tests/prestate.rs +++ b/crates/execution/firehose-tests/tests/prestate.rs @@ -1,23 +1,47 @@ +//! Docker-free prestate coverage of the invariant and projection layers. +//! +//! Replays the `nop_transfer` prestate through the tracer and asserts the property invariants plus +//! a projection golden over its single transaction — the same two layers `base-system-tests`' +//! `firehose_b20` uses, without a node or Docker, so a regression in the harness itself surfaces in +//! the default workspace run. + use std::path::PathBuf; -use base_firehose_tests::{assert_block_equals_golden, run_prestate}; +use base_firehose_tests::{ + BlockInvariants, BlockProjection, Golden, SymbolTable, VolatilePolicy, run_prestate, +}; #[test] fn nop_transfer() { - let folder = case_dir("nop_transfer"); + let folder = case_dir(); let outcome = run_prestate(&folder).expect("nop_transfer prestate must succeed"); - let golden = folder.join("block.2099.binpb"); - if std::env::var("GOLDEN_UPDATE").is_ok() { - use prost::Message as _; - std::fs::write(&golden, outcome.block.encode_to_vec()) - .expect("writing golden file must succeed"); - return; - } + BlockInvariants::assert(&outcome.block).expect("captured block must satisfy the invariants"); + + let trace = outcome + .block + .transaction_traces + .first() + .expect("nop_transfer block must hold one transaction"); + + let symbols = SymbolTable::new() + .with(&trace.from, "sender") + .with(&trace.to, "recipient") + .with(&outcome.block.header.as_ref().expect("header").coinbase, "coinbase"); + + // A prestate replay is fully reproducible (no live node moving the base fee or the clock), so + // keep every field — `VolatilePolicy::none()` — and let the golden catch the most it can. + let projection = BlockProjection::new() + .with_symbols(symbols) + .with_policy(VolatilePolicy::none()) + .transaction(&outcome.block, &trace.hash) + .expect("projecting the transaction must succeed"); - assert_block_equals_golden(&outcome.block, &golden).expect("captured block must match golden"); + Golden::is_json_equal(&projection, &folder.join("block.2099.json")) + .expect("comparing against golden must not error") + .assert_equal(); } -fn case_dir(name: &str) -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("cases").join(name) +fn case_dir() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("cases").join("nop_transfer") } diff --git a/etc/systems/Cargo.toml b/etc/systems/Cargo.toml index 7d13eda02c..d347c714c6 100644 --- a/etc/systems/Cargo.toml +++ b/etc/systems/Cargo.toml @@ -93,6 +93,9 @@ testcontainers = { workspace = true, features = ["blocking", "host-port-exposure # cli clap = { workspace = true, features = ["derive"] } +# firehose +base-firehose-tests.workspace = true + # consensus base-consensus-derive.workspace = true base-consensus-engine = { workspace = true, features = ["test-utils"] } diff --git a/etc/systems/tests/firehose_b20.rs b/etc/systems/tests/firehose_b20.rs new file mode 100644 index 0000000000..35b2279433 --- /dev/null +++ b/etc/systems/tests/firehose_b20.rs @@ -0,0 +1,126 @@ +//! Firehose tracing regression coverage for the B-20 precompile, driven by the real system stack. +//! +//! ## Why this lives here +//! +//! `base-system-tests` boots a genuine L1 (Docker reth + lighthouse) and a genuine in-process L2 +//! (builder + sequencer consensus + batcher + follower client + validator consensus). The follower +//! client executes every block it receives through `engine_newPayload`, which is exactly the path +//! `base_engine_tree`'s payload validator routes into the Firehose tracer. Installing a +//! buffer-backed global tracer before the stack starts therefore captures real `FIRE BLOCK` output +//! for real Base-specific transactions — B-20 precompile calls included — without a separate +//! prestate fixture that has to be hand-maintained. +//! +//! ## Why a dedicated test binary with a single test +//! +//! The Firehose tracer is process-wide and may only be installed once. cargo/nextest give each +//! integration-test binary its own process, so this file holds exactly one test; adding a second +//! one here would panic in `BaseFirehoseCapture::install` or interleave two stacks' blocks into one +//! buffer. +//! +//! ## Layers asserted +//! +//! 1. [`BlockInvariants`] over every traced block — property assertions that never need +//! regenerating (ordinal uniqueness and nesting, call-tree shape, receipt/call log agreement, +//! no no-op state changes). +//! 2. A narrow [`BlockProjection`] golden over the B-20 transfer transaction alone, with volatile +//! fields excluded by construction. Regenerate with `GOLDEN_UPDATE=1`. + +mod common; + +use std::{path::PathBuf, time::Duration}; + +use alloy_primitives::{B256, U256}; +use alloy_signer_local::PrivateKeySigner; +use base_common_precompiles::{ActivationFeature, B20FactoryStorage, B20Variant, IB20}; +use base_firehose_tests::{ + BaseFirehoseCapture, BlockInvariants, BlockProjection, Golden, SymbolTable, VolatilePolicy, +}; +use base_system_tests::{ANVIL_ACCOUNT_5, ANVIL_ACCOUNT_6, B20PrecompileClient}; +use eyre::{Result, WrapErr}; + +/// Initial supply minted to the admin when the token is created. +const INITIAL_SUPPLY: u64 = 1_000_000_000; +/// Amount moved by the traced `transfer` call. +const TRANSFER_AMOUNT: u64 = 100_000_000; +/// `CREATE2` salt for the traced token; fixed so the token address is reproducible. +const TOKEN_SALT: u8 = 0x42; +/// How long to wait for the follower node to validate (and therefore trace) the target block. +const TRACE_TIMEOUT: Duration = Duration::from_secs(60); + +#[tokio::test(flavor = "multi_thread")] +async fn b20_transfer_is_traced() -> Result<()> { + // Must happen before the stack starts producing blocks: the traced execution path is gated on + // `reth_firehose::is_tracer_initialized()` at payload-validation time. + let capture = BaseFirehoseCapture::install(common::L2_CHAIN_ID, Some(0), Some(0), None); + + let (_system, provider) = common::start_beryl_system().await?; + let admin = PrivateKeySigner::from_bytes(&ANVIL_ACCOUNT_5.private_key) + .wrap_err("Failed to parse admin private key")?; + let recipient = ANVIL_ACCOUNT_6.address; + common::wait_for_balance(&provider, admin.address()).await?; + + let b20 = B20PrecompileClient::new(&provider, &admin, common::L2_CHAIN_ID) + .with_receipt_timeout(common::TX_RECEIPT_TIMEOUT); + b20.activate_feature(ActivationFeature::B20Asset.id()).await?; + + let salt = B256::repeat_byte(TOKEN_SALT); + let params = B20PrecompileClient::token_params( + "Firehose B20", + "FHB20", + admin.address(), + U256::from(INITIAL_SUPPLY), + admin.address(), + ); + let token = b20.create_token(B20Variant::Asset, params, salt).await?; + b20.wait_for_token_code(token, common::TX_RECEIPT_TIMEOUT, common::BLOCK_POLL_INTERVAL).await?; + + let receipt = b20 + .send_call_receipt( + token, + IB20::transferCall { to: recipient, amount: U256::from(TRANSFER_AMOUNT) }, + "B-20 transfer", + ) + .await?; + let transfer_block = receipt + .inner + .block_number + .ok_or_else(|| eyre::eyre!("B-20 transfer receipt carries no block number"))?; + let transfer_hash = receipt.inner.transaction_hash; + + let block = capture.wait_for_block(transfer_block, TRACE_TIMEOUT).await?; + + // Layer 1: property assertions over every block the follower traced so far. The most recently + // written line may still be in flight, so the tail block is covered by the explicit assert + // below rather than by this sweep. + let traced = capture.traced_block_numbers(); + for number in traced.iter().rev().skip(1).rev() { + let traced_block = capture.block(*number)?; + BlockInvariants::assert(&traced_block) + .wrap_err_with(|| format!("invariants failed on traced block #{number}"))?; + } + BlockInvariants::assert(&block)?; + + // Layer 2: narrow projection golden over the B-20 transfer transaction only. The rest of the + // block (the L1-info deposit) carries the L1 head and is therefore not reproducible. + let symbols = SymbolTable::new() + .with(admin.address(), "admin") + .with(recipient, "recipient") + .with(token, "b20-token") + .with(B20FactoryStorage::ADDRESS, "b20-factory"); + + // A live sequencer moves the base fee, block number and wall clock between runs, so drop what + // the tracer cannot reproduce (`VolatilePolicy::live_node()`): hashes, timestamps, gas, absolute + // ordinals; balance/nonce changes are deltaized; storage values are kept verbatim. + let projection = BlockProjection::new() + .with_symbols(symbols) + .with_policy(VolatilePolicy::live_node()) + .transaction(&block, transfer_hash.as_slice())?; + Golden::is_json_equal(&projection, &golden_path("b20_transfer.json"))?.assert_equal(); + + Ok(()) +} + +/// Resolves `name` inside this crate's golden directory. +fn golden_path(name: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("goldens").join(name) +} diff --git a/etc/systems/tests/goldens/b20_transfer.json b/etc/systems/tests/goldens/b20_transfer.json new file mode 100644 index 0000000000..1cecfc3541 --- /dev/null +++ b/etc/systems/tests/goldens/b20_transfer.json @@ -0,0 +1,81 @@ +{ + "calls": [ + { + "address": "@b20-token", + "balance_changes": [ + { + "address": "@admin", + "reason": "REASON_GAS_BUY" + }, + { + "address": "@admin", + "reason": "REASON_GAS_REFUND" + }, + { + "address": "0x4200000000000000000000000000000000000019", + "reason": "REASON_REWARD_TRANSACTION_FEE" + }, + { + "address": "0x420000000000000000000000000000000000001a", + "reason": "REASON_REWARD_TRANSACTION_FEE" + } + ], + "call_type": "CALL", + "caller": "@admin", + "index": 1, + "input": "0xa9059cbb000000000000000000000000976ea74026e726554db657fa54763abd0c3a0aa90000000000000000000000000000000000000000000000000000000005f5e100", + "logs": [ + { + "address": "@b20-token", + "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009965507d1a55bcc2695c58ba16fb37d819b0a4dc", + "0x000000000000000000000000976ea74026e726554db657fa54763abd0c3a0aa9" + ] + } + ], + "nonce_changes": [ + { + "address": "@admin", + "delta": "+1" + } + ], + "return_data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "storage_changes": [ + { + "address": "@b20-token", + "key": "0xa14eb246d421839fa53a0bb3113dd730a7c59dd0d6c9f6ee0274cb69fde27d03", + "new_value": "0x0000000000000000000000000000000000000000000000000000000035a4e900", + "old_value": "0x000000000000000000000000000000000000000000000000000000003b9aca00" + }, + { + "address": "@b20-token", + "key": "0xfb9e2669d4ad56e6cd9cd11cf03d0045e632f60ef6a978d4f18dad7d94c3c6a0", + "new_value": "0x0000000000000000000000000000000000000000000000000000000005f5e100", + "old_value": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + ] + } + ], + "from": "@admin", + "index": 1, + "input": "0xa9059cbb000000000000000000000000976ea74026e726554db657fa54763abd0c3a0aa90000000000000000000000000000000000000000000000000000000005f5e100", + "receipt": { + "logs": [ + { + "address": "@b20-token", + "data": "0x0000000000000000000000000000000000000000000000000000000005f5e100", + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x0000000000000000000000009965507d1a55bcc2695c58ba16fb37d819b0a4dc", + "0x000000000000000000000000976ea74026e726554db657fa54763abd0c3a0aa9" + ] + } + ] + }, + "return_data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "status": "SUCCEEDED", + "to": "@b20-token", + "type": "TRX_TYPE_DYNAMIC_FEE" +}