Reorg Endpoint
The /v4/YOUR_API_KEY/reorg/<N> endpoint is a fake bitcoind RPC that drives a
rockshrew-mono indexer to roll back to an arbitrary block height. It
is useful when an alkanes-rs or metashrew upgrade introduces a
state-changing fork (a hardfork height) and you need to re-index from
just before that fork under the new wasm.
When to use this
When an alkanes-rs release notes an activation height (for example
v2.2.0-rc.5's OYL-disband activation at h=950564), any indexer that
ran the previous wasm past that height has applied the old behavior
and is now permanently divergent. Replaying the affected range under
the new wasm is the only way to converge.
Wiping the database and resyncing from genesis takes hours or days. This endpoint is much faster: it forces a targeted rollback at a specific height via metashrew's built-in startup-heal walk-back, then the indexer resumes forward under the new wasm from there.
If you would rather start from a freshly indexed snapshot rather than re-index the affected range yourself, see Snapshot Download.
How it works
rockshrew-mono runs a startup-heal walk-back on every boot: starting
from the locally indexed tip, it asks bitcoind for the block hash at
each height and walks down until the remote hash matches the locally
stored hash; that height becomes the rollback target.
This endpoint pretends to be bitcoind and answers getblockhash(h)
with a synthesized non-matching hash for every h > <N>, and with the
real chain hash for every h <= <N>. So the walk-back keeps stepping
down until it reaches <N>, finds a match (passthrough to the real
chain), and rolls the database back to that height.
After the rollback completes, the endpoint reports getblockcount as
<N>, so the indexer sits idle at that height. You then point
--daemon-rpc-url back at your normal bitcoind and the indexer
resumes forward from <N> + 1 under whatever wasm it now has loaded.
Procedure
1. Stop your indexer
Stop rockshrew-mono (SIGTERM is fine: it shuts down cleanly).
2. Restart with the endpoint as the daemon URL
rockshrew-mono \
--daemon-rpc-url https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/950564 \
--max-reorg-depth 2000 \
...other args unchanged...
Two flags matter:
--daemon-rpc-urlpoints at the reorg endpoint for the target height of your choice. Substitute950564with your target.--max-reorg-depthmust exceedcurrent_tip - target. The default is100, which is almost certainly too low for any intentional hardfork rollback. Set it generously: for a target ~1100 blocks below tip,--max-reorg-depth 2000gives slack and costs nothing at runtime.
The URL must carry your API key. A request to the unkeyed
https://mainnet.subfrost.io/v4/reorg/<N> returns HTTP 401 with
{"code":"INVALID_API_KEY"}.
3. Wait for the rollback to complete
In rockshrew-mono's logs you will see the startup-heal walk-back:
startup-heal: pointer scan — __INTERNAL/height=951646 ...
startup-heal: hash mismatch at height 951646; walking back
startup-heal: hash mismatch at height 951645; walking back
...
startup-heal: hash mismatch at height 950565; walking back
startup-heal: all pointers agree at height 950564 and bitcoind confirms; clean state
Reorg detected. Rolling back to height 950563
Each step is one HTTP request. A 1000-block walk-back takes 10-30
seconds. After the rollback, the indexer reports tip = <N> and sits
idle (because the endpoint says getblockcount = <N>).
4. Switch back to your normal bitcoind
Stop rockshrew-mono again. Restart it with --daemon-rpc-url
pointing at your real bitcoind. It will detect the new tip, resume
indexing from <N> + 1, and apply the new wasm's behavior to every
block from the fork onward.
You can drop --max-reorg-depth back to its default if you set it
high for the rollback step.
API reference
The endpoint mimics the bitcoind JSON-RPC interface. Three methods are synthesized; everything else passes through to the live mainnet chain via our bitcoind bouncer.
getblockcount
Returns the target height <N> regardless of the real chain tip. This
makes the indexer idle once the rollback completes.
curl -X POST -H "content-type: application/json" \
--data '{"jsonrpc":"2.0","method":"getblockcount","id":1}' \
https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/950564
# => {"result":950564,"error":null,"id":1,"jsonrpc":"2.0"}
getblockhash
For h > <N> returns a synthetic non-matching hash of the form
ffffffffffffffffffffffffffffffffffffffffffffffffffffffff{h:016x}.
For h <= <N> passes through to the real chain.
# Above target: fake hash
curl -X POST -H "content-type: application/json" \
--data '{"jsonrpc":"2.0","method":"getblockhash","params":[951000],"id":2}' \
https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/950564
# => {"result":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000e82d8",...}
# At or below target: real chain hash
curl -X POST -H "content-type: application/json" \
--data '{"jsonrpc":"2.0","method":"getblockhash","params":[950564],"id":3}' \
https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/950564
# => {"result":"00000000000000000000f289e6259e2377a32bf4703492ec2ba3381438dc863e",...}
getbestblockhash
Returns the real-chain hash at <N> (i.e. getblockhash(<N>)).
Everything else
getblock, getblockheader, sendrawtransaction, getrawmempool,
etc. pass through unchanged. The startup-heal path only consults
getblockhash, so passthrough is safe for normal operation; if your
indexer happens to request getblock on a synthetic above-target
hash it will get an upstream error, which is the correct signal that
that block is not on the served chain.
GET request returns usage
GET https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/<N> returns a short
plain-text usage doc, so paste-in-browser users see what to do.
Networks
Currently mainnet only:
- mainnet:
https://mainnet.subfrost.io/v4/YOUR_API_KEY/reorg/<N>
Signet and regtest will be added if there is demand. File an issue or contact support.
Caveats
- The endpoint is stateless and per-call. It does nothing
persistent on our side. Two callers pointing different indexers at
different
<N>values get independently correct behavior at the same time. - It does not protect you from typos.
--daemon-rpc-url .../v4/YOUR_API_KEY/reorg/0will attempt to roll all the way back to height 0. No safety cap; treat the URL as authoritative. - Your wasm must already be the post-fork build before you start
the rollback. This endpoint replays history; if you walk back to
950563and then resume with the old wasm, you will re-create the same divergence you started with. --max-reorg-depthis the load-bearing flag. Without it set high enough, the walk-back hits its floor and stops short of<N>, rolling back onlymax_reorg_depthblocks instead of thetip - <N>you wanted.
Related
- Snapshot Download: if you would rather start from a freshly indexed RocksDB snapshot than re-index the affected range yourself.
- Database Rsync Access: authenticated rsync for Business customers needing per-service granularity.