Sixty small programs under crates/cli/examples/,
each answering one question about a running or stopped node. They are
cargo examples rather than subcommands so they stay out of the node binary
and out of its argument surface: a diagnostic that only three people will
ever run should not be a flag everyone has to read past.
Build and run one with:
cargo run --release -p rustock-cli --example <name> -- <args>Most take a data directory and open it read-only, so they are safe to run against the live node. The ones marked (writes) do not: they modify the database, the head pointers or a segment store, and want the node stopped.
Each entry's description is the first line of the tool's own //! comment;
the source is the reference, and run a tool with no arguments to see its
usage.
| tool | what it answers |
|---|---|
powpeg_signature_audit |
Did a PowPeg functionary ever submit a signature the Bridge refused? |
federation_intervals |
Which PowPeg functionary keys were active over which block range. |
block_fees |
What did a block's transactions pay in fees? Reads paidFees from the header rather than summing receipts or reading REMASC payouts; parallel, --verify checks it against the receipts sum. |
bridge_events |
Query the Bridge event index: every occurrence of one event, in block order. |
bridge_method |
Identify a Bridge method from its 4-byte ABI selector, using the node's own table rather than a guess. |
bridge_subtree |
How big is the Bridge's storage subtree, and how much of it is missing from a chunk? |
pegin_refunds |
Did the Bridge's peg-in refund path ever execute on mainnet? |
scan_pegin_calls |
Find registerBtcTransaction calls by reading blocks, without executing them. |
scan_silent |
Read the silent-decision store: what the Bridge decided without logging it. |
flyover_find |
At which block did the Bridge register each Flyover peg-in? Binary-searches the fastBridgeFederationInformation slot, which is absent before registration and present after — ~24 probes per address instead of replaying the chain. |
flyover_registry |
Did the Bridge register this flyover address, and under which derivation? One trie read per address, no replay — and it separates "never registered" from "this store is pruned and cannot say". |
legacy_events |
Query the peg events reconstructed for the era the Bridge logged nothing, and merge them with the node's real event index into one history. |
check_supply |
Replay canonical blocks and report any that do not conserve the native supply, changing nothing on disk. |
bridge_btc_holdings |
How much bitcoin the peg holds and where it sits, listed outpoint by outpoint — the federation's own script and every Flyover-derived address, read from the unitrie without executing anything. |
| tool | what it answers |
|---|---|
block_info |
Prints stored block metadata for given heights, read-only. |
height_peek |
Every block stored at a height, canonical or not, read-only. |
dump_block |
Writes a block's stored bytes to disk, for attaching to a bug report. |
inspect_block |
Execute one block against an archival trie and print what rustock computed: per-transaction gas and status, block gas, paid fees, and every root. Used to tell "we disagree about a fee" from "we disagree about what happened". |
block_writes |
What did rustock write while executing one block? |
scan_addr |
Diagnostic: scan canonical block bodies for transactions whose to (or sender, via recovered address) involves a target address. Used to locate the block/op that creates a given account in rskj but not rustock. |
scan_edges |
Where on this chain is RSKIP-144 parallel execution actually used? |
uncle_rpc_check |
Renders eth_getUncleByBlockHashAndIndex against the real mainnet store, read-only, alongside the running node. |
dump_canonical_receipts |
Decode the canonical TransactionReceipts for a block from the snapshot receipts DB and print each receipt's fields (postTxState, cumulativeGas, gasUsed, status, bloom, #logs). Receipts DB is keyed by txHash -> RLP[ TransactionInfo[ [receiptRLP, blockHash, index], ... ] ]. |
| tool | what it answers |
|---|---|
dump_state |
Diagnostic: dump every leaf (key=value) of rustock's persisted unitrie at the executed head. Used to diff against rskj's unitrie for the same block to localize receipts-invisible state divergences. |
diff_state |
Localize a post-wasabi state-root divergence to the exact diverging trie leaves. Replays canonical blocks from the executed head up to <number> in memory (computing rustock's — possibly wrong — post-state trie), then diffs it against rskj's own unitrie for the header's state root, read directly from rskj's LevelDB. The dual walk descends only where the two tries' subtree hashes differ, so it visits a handful of nodes and finishes instantly even on a multi-million-leaf state. |
diff_roots |
Diff two unitrie roots that live in two different stores. |
diff_self |
Diagnostic: show the unitrie leaf delta produced by executing a single block. Replays from the executed head to -1 (the base), then executes and hash-prune-diffs the two rustock-computed tries. Prints only the leaves that changed — the exact state delta of . No rskj DB needed (both sides are rustock-computed); use when rskj isn't synced past the target. |
trie_census |
What the state contains at one block — accounts, contracts, storage cells, code sizes — as JSON for a recurring consumer. Parallel, ~200k nodes/s, full mainnet trie in about a minute. See trie-census.md. |
rbtc_supply |
Sum every rBTC balance in the unitrie at one block: a parallel whole-trie walk split by the committed children_size, reporting the account census, the conservation check against the premine, and the Bridge and REMASC balances. |
trie_probe |
Is this node hash in this rustock-format trie store? Opens the trie_nodes column family, which rskj_root_probe does not — probing one of our stores with that tool reports ABSENT for everything. |
trie_footprint |
Measure the trie working set of a range of blocks. |
trie_window |
Measure the miss rate of a sliding-window trie cache against window length. |
lsm_stats |
LSM shape and table properties of a trie store: level layout, and whether the SSTs carry bloom filters at all. |
detach_trie (writes) |
Copies the trie_nodes column family out of a node database into a standalone trie database. |
drop_trie_cf (writes) |
Drops the trie_nodes column family from a node database. |
flatdb_convert (writes) |
Copy a RocksDB trie store into a FlatDB one, node for node. |
flatdb_migrate (writes) |
Move an epoch store from RocksDB to FlatDB, in place, node stopped. |
flatdb_stats |
What a FlatDB store actually holds: record sizes, and how many cross the threshold that decides whether a key is stored or recomputed. |
synthetic_state (writes) |
Build a synthetic world state of a chosen size, so that execution benchmarks start from a trie like a real one. |
pre_unitrie_roots |
Replay the pre-RSKIP126 era from genesis, recording the Unitrie root each block computes, and optionally validating it against the header. |
| tool | what it answers |
|---|---|
build_segments (writes) |
Replay a block range, verifying every state root, and build the range's trie segments as a by-product. |
audit_chunk |
What does a segment chunk NOT contain that a consumer needs? |
patch_size |
How many nodes would it take to make a chunk answer the lookups it cannot? |
repair_chunk (writes) |
Make a segment chunk answer the lookups a foreign client issues. |
probe_seed |
Is each segment chunk missing the parent state root of its own first block? |
snap_serve_check |
Does snapshot sync work on the real trie? |
pin_cost |
What does pinning the snapshot checkpoint cost the collector? |
| tool | what it answers |
|---|---|
reset_exec_head (writes) |
Reset execution to genesis WITHOUT discarding downloaded blocks. |
set_exec_head (writes) |
Rewinds (or advances) the executed head to a chosen canonical block. |
rewind_to (writes) |
Rewind the download + executed head to a clean block and drop the canonical pointers above it, so the node re-downloads and re-executes the tail cleanly. |
resume_point |
Where would execution resume? A read-only diagnostic. |
replay_block |
Re-execute canonical blocks from the executed head up to a target block (in memory, nothing persisted) and report the target's per-transaction results, logs, and computed state/receipts roots vs the header. Deterministic, offline diagnostic for gas/state/receipts divergences. |
verify_range |
Verify a range of blocks against rskj's own state, reporting every divergence rather than stopping at the first. |
| tool | what it answers |
|---|---|
rskj_blocks_probe |
Probe: can RocksDB open rskj's blocks LevelDB, and how far does it reach? |
rskj_root_probe |
Does this rskj database still hold the state for a given block? |
rskj_state_roots |
Look up rskj's stateRoots translation table. |
dump_rskj_unitrie |
Dump every leaf of an rskj unitrie directly from its LevelDB, given a root hash. Post-RSKIP126 (wasabi100, #1,591,000) a header's state_root IS the unitrie root, so this lets us oracle-diff any post-wasabi block's state from Rust (no Java). rskj's unitrie LevelDB is content-addressed: nodeHash -> node message bytes, the same scheme rustock uses. |
| tool | what it answers |
|---|---|
replay_bench |
Replay a block range against one trie store and report throughput. |
read_bench |
Isolates read cost: one lookup in a large store versus N lookups across small ones. |
batch_read_bench |
What a batched read buys, measured on the workload that uses one. |
loadtest |
Measure block execution against a realistic trie, at a chosen number of threads. |
gc_bench |
Synthetic trie-churn benchmark for the epoch garbage collector. |
header_verify_bench |
How fast can this node verify headers under the full consensus rules? |
btc_cache_bench |
Measure BTC main-chain block retrieval with and without the block cache, against a real database. |
canonical_index_cost |
What the canonical number -> hash index costs. |
validation_cost |
What a real block costs outside execution. |
parallel_workload |
Generate a block workload with a known collision structure, mine it, and report how the RSKIP-144 miner partitioned it. |
| tool | what it answers |
|---|---|
check_alerts_config |
Validate a peg-out alert configuration without starting a node or sending mail: parse it, resolve ${NAME} references against the env file, and report what would happen. Secrets are never printed — only whether they resolved and how long they are. |
rsk-evm-consensus-rules.md states the rules
themselves — gas schedule, account existence without EIP-161, call semantics,
block context — organised by EVM subsystem and naming no client. It is a view
over the java-compatibility.md catalogue and cites its § numbers for the
derivations.
registerFlyoverBtcTransaction calls no eventLogger, in rskj or in our
port, so from iris300 (#3,614,800) every successful Flyover peg-in is missing
from bridge_events — there is nothing to index. The Bridge's state knows,
though, and flyover_registry reads it.
flyover-peg-ins.md has the evidence on both sides, the
contract-only caller rule, three flyover-shaped addresses that were never
registered and had to be recovered by hand, and the three tooling traps that
each produced a wrong answer along the way.
rbtc_supply and bridge_btc_holdings answer one question between them —
how much rBTC is outstanding, and how much bitcoin backs it — but their two
figures cannot simply be subtracted. Locked REMASC fees, bitcoin the Bridge
has not yet registered, and peg-out fees paid from peg funds all sit between
them, and the terms are large enough to flip the sign of the result.
peg-accounting.md gives the method, how to read each
tool's output, and a worked example where the naive subtraction gets it
backwards.
Two of these were written together to answer "did a PowPeg functionary ever submit an invalid signature", and they are worth a paragraph because the answer is not where you would first look for it.
There is no invalid-signature event. addSignature verifies each
signature against its input's sighash and, on the first that does not
verify, returns without applying anything: the call succeeds, spends its
gas, changes no state and logs nothing. A malformed DER signature reverts; a
well-formed signature over the wrong message is silent.
a_member_signature_over_the_wrong_sighash_is_not_applied in
crates/execution/src/bridge/peg.rs pins that.
What is observable is add_signature, and RSKIP326 (fingerroot500, mainnet
#5,468,000) changed when it fires: from there, only once a signature has
actually been applied. Below that height it fires before verification and
cannot distinguish a good signature from a bad one.
So:
-
federation_intervals <data-dir> [out.json]builds the ad-hoc database of which functionary keys could sign over which heights, from the sixteencommit_federationevents — no state, no execution, which matters because a pruning node no longer holds the state those heights would need. It checks that every handover's outgoing key set is the set committed before it, and refuses to be trusted quietly if one does not close. -
powpeg_signature_audit <data-dir> <federations.json> [from] [to] [threads]scans for the calls: workers take contiguous height ranges and read bodies straight from the block store, pairing each call against the event index. Two benign causes are excluded — a pegout already released when the call arrived, and a federator that had already signed that pegout — and whatever survives goes topowpeg_unexplained.csv.Around 9.4 million blocks in half an hour on eight threads, with a progress line every thirty seconds.
Run them in that order; the second needs the first's JSON.