BTC Labnet is a private Bitcoin network developed by the Blockchain and Cryptocurrency Research Group at the Universitat Autònoma de Barcelona. Its node is a patched Bitcoin Core v30.0 that keeps the mainnet parameters (genesis block, address formats, ports), so real wallets and tools can be used against a chain nobody else is mining.
This repository contains the node. Optional services that run next to it (Knots, Electrum, mempool, mainnet-observer, faucet) are in services/.
The node is Bitcoin Core v30.0 with two source files patched at build time: core/Dockerfile clones the upstream tag and applies the diffs in core/patches/.
src/kernel/chainparams.cpp (mainnet section):
| Parameter | Upstream | Labnet | Why |
|---|---|---|---|
SegwitHeight |
481824 | 0 | SegWit active from the genesis block |
Taproot min_activation_height |
709632 | 0 | Taproot active from the genesis block |
fPowNoRetargeting |
false | true | No automatic retargeting: the chain has a single small miner |
nMinimumChainWork |
mainnet value | 0 | The private chain has very little work |
defaultAssumeValid |
mainnet block | none | No mainnet block exists on this chain |
powLimit stays at the mainnet value. Together with fPowNoRetargeting, this keeps the difficulty from ever going below 1. Do not replace it with the regtest powLimit (7fff…): that allows blocks with difficulty below 1, which nodes built from this repository reject (high-hash), and the network splits.
src/validation.cpp:
IsInitialBlockDownload()no longer returnstruewhen the tip is older thanmax_tip_age. Without this, the node stays in IBD whenever nobody has mined for a while,getblocktemplatefails, and the pool cannot give work to the miner.ContextualCheckBlockHeader()skips thebad-diffbitscheck whenfPowNoRetargetingis set. Upstream then requires every block to repeat the previous block'snBits; with the patch, anynBitswithinpowLimitis accepted.
services/knots/ applies the same patches to Bitcoin Knots v29.2.knots20251110, except for the IBD change (Knots does not serve block templates).
Requirements: Docker 20.10 or later.
-
Copy .env.example to
.envand fill it in: RPC credentials, an optionalrpcauthline for extra users (mempool), the peer to sync from, and the hosts outside the Docker network that may use RPC (mining pool, wallet clients, services on another machine). -
Build the image and start the node:
./start.sh
With
LABNET_PEERset, the node joins that Labnet and syncs its chain. Without it, the node starts a new chain from the genesis block. -
Check it:
docker exec core-v30.0-v1 bitcoin-cli -rpcuser=<user> -rpcpassword='<password>' getblockchaininfo
Use single quotes around the password if it contains !, otherwise bash expands it.
The node gets the fixed IP 172.18.0.3 on the Docker network bitcoin_network (172.18.0.0/16), which the services expect.
The node is mined through public-pool, which fetches block templates over RPC, and a Bitaxe connected to the pool over Stratum. Bitcoin Core 30 rejects the numeric jsonrpc field sent by public-pool's RPC library, so its Dockerfile needs this after npm i:
sed -i 's/jsonrpc: 1.0/jsonrpc: "1.0"/' node_modules/rpc-bitcoin/build/src/rpc.jsAt difficulty 1, generatetoaddress and CPU miners do not find blocks in practice.
- Always recreate the container with its fixed
--ip. The services reference the node by IP. - The
bitcoin.confbaked into the image is only copied to the data volume the first time. After that, edit the copy inside the volume. - Changes made with
docker execare lost when the container is recreated. Permanent changes go into core/. - A
peers.datrestored from a backup can make an "isolated" node connect to the real Bitcoin network. Use-connect=and deletepeers.datandanchors.dat.
Contributions are welcome. Please open an issue or a pull request.