An easy to use tool for simulating forks/re-orgs on Bitcoin and Dogecoin
For ordinals and other inscription based applications where sequence of inscriptions matter, it is crucial that indexers are able to handle forks in the Bitcoin/Dogecoin blockchains (millions of dollars are on the line).
If you don't handle forks/re-orgs correctly, people will end up with incorrect balances and double spends.
First you will want to install docker
and npm
, then start either a BitIDE Bitcoin or BitIDE Dogecoin docker container:
docker run -p 1337:1337 -it --rm qedprotocol/bitide:latest
docker run -p 1337:1337 -it --rm qedprotocol/bitide-doge:latest
The docker container will start the following services:
- A Bitcoin/Dogecoin Regtest node ready for development
- A Bitcoin/Dogecoin RPC API running at http://devnet:[email protected]:1337/bitcoin-rpc/
- A Block Explorer running at http://127.0.0.1:1337/explorer/
- An Electrs full history API running at http://127.0.0.1:1337/api/
Next run:
docker ps
And you should see something like:
CONTAINER ID IMAGE COMMAND
a9fa22996770 qedprotocol/bitide:latest "bash /app/start.sh"
The 12 hexadecimal characters at the start (in this case, "a9fa22996770") is your container id
To start Forkr, just run
git clone https://github.com/QEDProtocol/forkr
cd forkr
npm install
npm run start [YOUR_CONTAINER_ID]
So in the case of the previous example I would run:
npm run start a9fa22996770
At startup, Forkr creates a fork of the current chain called default
.
To create a a resumable copy of a chain at a given point, send an HTTP get request to:
curl http://localhost:1449/createfork/<name of your new fork>?source=<source of the fork>&activate=<1 or 0, depending on if you want to activate it>
To activate a forked chain, send an HTTP get request to:
curl http://localhost:1449/activatefork/<name of the fork you want to activate>
To view thes status of Forkr, send an HTTP get request to:
curl https://localhost:1449/status
Let's create two forks of the default chain, fork1 and fork2:
curl "http://localhost:1449/createfork/fork1?source=default&activate=1"
curl "http://localhost:1449/createfork/fork2?source=default&activate=1"
Next, let's check the status of forker by running:
curl "http://localhost:1449/status"
We should see something like the following:
{"forks":["default","fork1","fork2"],"currentFork":"fork2","isBusy":false,"rpcIsDisabled":false,"rpcIsRunning":true}
As we can see currentFork
is fork2
, lets switch back to fork1:
curl "http://localhost:1449/activatefork/fork1"
Now that we are on fork1, we can make some transactions using the RPC endpoint http://devnet:[email protected]:1337/bitcoin-rpc/, and we can check them out in the block explorer at http://localhost:1337/explorer/.
These transactions can be ordinal inscriptions/other transactions relevant to your indexer.
Once we've made some transactions, we can switch over to fork2
by running:
curl "http://localhost:1449/activatefork/fork2"
If we now refresh the block explorer, the new transactions we have made will not be there (fork2 will currently be back at the tip of the default
fork).
We can now make some transactions on fork2 (Again, these transactions should be ordinal inscriptions/other transactions relevant to your indexer). For the best simulation of real forking activity, lets mine more blocks than we did on fork1 so it has a higher block height.
Let's switch back to fork1 now:
curl "http://localhost:1449/activatefork/fork1"
Now, you can startup your indexer and index based on fork1 (use jsonrpc-import for txindex based indexers/other RPC-compatible indexers).
In a re-org/fork, an active chain state is superseceded by a longer chain of valid blocks which does not include previously confirmed/indexed blocks.
Since fork2 has a longer chain of valid blocks that don't include some of fork1's transactions, let's test our indexer's ability to handle a re-org by switching to fork2:
curl "http://localhost:1449/activatefork/fork2"
Now check your indexer and see if it handled the re-org correctly =) 🎉
Copyright 2024 QED
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.