bhandras/reorg

★ 0Forks 0GoGitHub ↗Compare

README

Bitcoin reorg statistics

reorgstats groups observed Bitcoin stale headers into connected branches. It counts one-block, two-block, three-block, and deeper branches and can overlay them on an estimated network hash-rate chart.

The program has no third-party Go dependencies.

Bitcoin observed stale-branch depth and estimated hash rate

Rows without a full 80-byte header are reported and excluded from depth counts. Their parent linkage cannot be reconstructed from a block hash alone.

What the depth means

For a simple mining race, one stale header is depth 1. Two linked stale headers are depth 2. The program follows each stale header's previous-block hash and uses the longest linked path as the event's observed depth.

This is not a globally complete log of node reorganizations. A stale branch may have caused one node to disconnect blocks while another node only ever followed the winning branch. Missing stale headers can also make a branch look shorter. The result should therefore be read as observed stale-branch depth.

Deep branches need manual classification. The source dataset includes deliberate chain splits and policy forks in addition to ordinary mining races. A connected 18-block branch is not automatically evidence that Bitcoin mainnet suffered an 18-block probabilistic reorg.

Use a node's activity log when you need proof that the specific node changed from one active tip to another.

Difference from the upstream chart

The chart at https://bitcoin-data.github.io/stale-blocks/ does not plot reorg depth. Its y-axis is stale blocks per 1,000 blocks, smoothed with a 10,000-block rolling average. The table below that chart lists individual stale blocks but does not join their parent links.

reorgstats adds that missing grouping. It follows the previous-block hashes and counts the resulting connected stale branches by depth.

Build

go build -o reorgstats .

Count events by depth

The default data source is bitcoin-data/stale-blocks.

./reorgstats summary

Restrict the time range:

./reorgstats summary --since 2015-01-01 --until 2025-12-31

Machine-readable output:

./reorgstats summary --format csv
./reorgstats summary --format json

List specific depths

./reorgstats list --depth 1
./reorgstats list --depth 2
./reorgstats list --min-depth 3
./reorgstats list --min-depth 2 --format json

The CSV and JSON formats include full block hashes. The table shortens hashes for readability.

Generate the chart

./reorgstats chart --since 2015-01-01 --out reorgs.svg

The chart uses Blockchain.com's daily estimated hash-rate series by default. Hash rate uses a logarithmic axis. Stale-branch depth uses the right-hand axis. Depths above 10 share the 10+ position so short branches remain visible. Use --depth-cap 0 for an uncapped axis.

Both data sources can be local files:

./reorgstats chart \
  --input stale-blocks.csv \
  --hashrate-input hash-rate.json \
  --out reorgs.svg

Data integrity

The CSV reader decodes every 80-byte block header and recomputes its double SHA-256 block hash. It rejects a row when the computed hash differs from the CSV hash.

The tool deduplicates headers by hash. Headers connected through a previous-block hash form one event. If a stale component branches, its depth is the longest path and its block count includes every observed block in the component.

Sources

Contributors

bhandras

Issues