When the xOneRS store refuses a block, the node now keeps evidence of it: a directory under <datadir>/xone/invalid/ with the block's RLP bytes and the error text. The directory holds at most 16 entries and 1 GiB; the oldest entry goes first, and each drop is counted in a metric. Writing the evidence never changes the import result. This came in PR #1026 (merged 2026-11-15), the first part of issue #588.
A refused block is the moment the node and a peer disagree. For the log line that goes with it, see reading an xOne node log.
What does one entry contain?
Each entry is a directory named <number>-<hash8>: the block number and the first 8 hex characters of its hash (invalid.rs).
| File | Content |
|---|---|
block.rlp |
the RLP bytes of the block |
error.txt |
the error the store gave, one line |
block.rlp is enough to replay the block against another client or a later build.
How much space can it use?
At most 16 entries and 1 GiB over all entries. When a new entry would pass either bound, the oldest entries are dropped first. Each drop adds one to the counter xone_invalid_block_dumps_dropped_total, whose help text names both bounds (metrics.rs). A rising counter means the node refuses more blocks than the directory keeps, which is worth an alert.
What if the evidence cannot be written?
The node logs one line for that block and carries on. The import error stays the same, so a full or read-only disk does not change how the node treats the block. A test checks that a directory that cannot be written gives no entry and no panic.
How is it tested?
PR #1026 lists three tests:
one_entry_has_both_files_and_the_17th_drops_the_oldest: one entry holds both files, and a 17th entry drops the oldest.a_directory_that_cannot_be_written_gives_none_not_a_panic.a_refused_block_leaves_evidence_and_the_same_errorincrates/xone-node/tests/invalid_dump.rs: a tampered Apothem block 1 leaves one entry with both files, the import error is unchanged, and the good block still imports.
What is not kept yet?
Issue #588 also asks for prestate.json, diff.json and receipts.json: the state the block read, the state change it made and its receipts. These need the executor to hand out the read set and the diff of a failed block, which it does not do yet. Issue #588 is open.
The collected competitor articles state no claim about stored invalid-block evidence, so this post has no comparison table. For RPC access to bad blocks, a debug_getBadBlocks method is in review in a separate change.
Frequently asked questions
Where does xOne keep refused blocks?
In <datadir>/xone/invalid/<number>-<hash8>/, with block.rlp and error.txt in each entry.
Can the evidence directory fill my disk?
No. It keeps at most 16 entries and 1 GiB, dropping the oldest first. The counter xone_invalid_block_dumps_dropped_total counts the drops.
Does writing evidence slow down or change the import?
It never changes the import result. If the write fails, the node logs one line and continues.
Is this in the v0.1.0 release?
No. It came in PR #1026 on 2026-11-15.
Sources
This article describes the source and evidence at its review date. Check the linked repository for current implementation and qualification status.
Explore the engineering evidenceEdit this page on GitHub opens the Markdown source (content/blog/invalid-block-evidence/index.md). GitHub may ask you to sign in. Report a correction opens a prefilled GitHub issue for this page.