⚠️ WARNING: NOT FOR PRODUCTION USEThis repository is intended for development purposes, NOT for production use. Use at your own risk. The software is provided as-is without any warranties or guarantees.
BTC-Staker is a toolset designed for seamless Bitcoin staking. It consists of two components:
-
stakerd- Thestakerddaemon manages connections to the Babylon and Bitcoin nodes. -
stakercli- Thestakercliis a command line interface (CLI) to facilitate interaction with thestakerddaemon . It enables users to stake funds, withdraw funds, unbond staked funds, retrieve the active finality providers set in Babylon, and more. It serves as an intuitive interface for effortless control and monitoring of your Bitcoin staking activities.
The stakerd daemon requires a running Bitcoin node and a legacy wallet loaded
with signet Bitcoins to perform staking operations.
You can configure stakerd daemon to connect to either
bitcoind or btcd node types. While both are compatible, we recommend
using bitcoind. Ensure that you are using legacy wallets, as stakerd daemon
doesn't currently support descriptor wallets.
Below, we'll guide you through setting up a signet bitcoind node and a legacy
wallet:
# Download Bitcoin Core binary
wget https://bitcoincore.org/bin/bitcoin-core-26.0/bitcoin-26.0-x86_64-linux-gnu.tar.gz
# Extract the downloaded archive
tar -xvf bitcoin-26.0-x86_64-linux-gnu.tar.gz
# Provide execution permissions to binaries
chmod +x bitcoin-26.0/bin/bitcoind
chmod +x bitcoin-26.0/bin/bitcoin-cliPlease update the following configurations in the provided file:
- Replace
<your_rpc_username>and<your_rpc_password>with your own values. These credentials will also be utilized in the btc-staker configuration file later on. - Ensure that the
<user>is set to the machine user. In the guide below, it's set toubuntu. - Note that
deprecatedrpc=create_bdbis necessary to enable the creation of a legacy wallet, which has been deprecated in the latest core version. For more information, refer to the Bitcoin Core 26.0 release page here and this link. - If you want to enable remote connections to the node, you can add
rpcallowip=0.0.0.0/0andrpcbind=0.0.0.0to the bitcoind command. - Start the
bitcoindwith-txindexoption to make sure btc-staker can get all needed bitcoin transaction data.
# Create the service file
sudo tee /etc/systemd/system/bitcoind.service >/dev/null <<EOF
[Unit]
Description=bitcoin signet node
After=network.target
[Service]
User=<user>
Type=simple
ExecStart=/home/ubuntu/bitcoin-26.0/bin/bitcoind \
-deprecatedrpc=create_bdb \
-signet \
-server \
-txindex \
-rpcport=38332 \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password>
Restart=on-failure
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
EOF# Start the service
sudo systemctl daemon-reload
sudo systemctl enable bitcoind
sudo systemctl start bitcoind# Check the status and logs of the service
systemctl status bitcoind
journalctl -u bitcoind -f~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
-named createwallet \
wallet_name=btcstaker \
passphrase="<passphrase>" \
load_on_startup=true \
descriptors=false- Ensure you use the same rpc
rpcuser,rpcpassword,rpcportthat you used while setting up the bitcoind systemd service. -named createwalletindicates that a new wallet should be created with the provided name.wallet_name=btcstakerspecifies the name of the new wallet and<passphrase>corresponds to the wallet pass phrase. Ensure you use the wallet name and passphrase configured here in the walletconfig section of thestakerd.conffile.- Setting
load_on_startup=trueensures that the wallet automatically loads during system startup. descriptors=falsedisables descriptors, which are not currently supported by the btc-staker.
You can load the wallet with the loadwallet command:
~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
loadwallet "btcstaker"where rpcuser, rpcpassword, and rpcport correspond to the RPC configuration you
have set up and "btcstaker" should be replaced with the wallet name that you
created.
You can generate a btc address through the getnewaddress command:
~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
getnewaddresswhere rpcuser, rpcpassword, and rpcport correspond to the RPC configuration you
have set up.
Use the faucet link to request signet BTC to the address generated in the previous step. You can use the following commands if you have received the funds
You can immediately see the amount using getunconfirmedbalance
~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
getunconfirmedbalanceYou can also see info about the transaction that the faucet gave you
using gettransaction
~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
gettransaction $TXIDwhere $TXID is the transaction id that you received from the faucet.
Once the tx is confirmed you can check the funds using getbalance
command
~/bitcoin-26.0/bin/bitcoin-cli -signet \
-rpcuser=<your_rpc_username> \
-rpcpassword=<your_rpc_password> \
-rpcport=38332 \
getbalanceNotes:
- Ensure to run the Bitcoin node on the same network as the one the Babylon node connects to. For the Babylon testnet, we are using the BTC Signet.
- Expected sync times for the BTC node are as follows: Signet takes less than 20 minutes, testnet takes a few hours, and mainnet could take a few days.
- You can check the sync progress in bitcoind systemd logs
using
journalctl -u bitcoind -f. It should show you the progress percentage for example it isprogress=0.936446in this logAlternatively, you can also check the latest block in a btc explorer like https://mempool.space/signet and compare it with the latest block in your node.Jan 29 18:55:50 ip-172-31-85-49 bitcoind[71096]: 2024-01-29T18:55:50Z UpdateTip: new best=00000123354567a29574e6bdd263409b8eab6c05c6ef2abad959b092bf61fe9a height=169100 version=0x20000000 log2_work=40.925924 tx=2319364 date='2023-11-12T19:42:53Z' progress=0.936446 cache=255.6MiB(1455996txo) - Ensure that you use a legacy (non-descriptor) wallet, as BTC Staker doesn't
currently support descriptor wallets. You can check the wallet format using
The output should be similar to this and the
~/bitcoin-26.0/bin/bitcoin-cli -signet \ -rpcuser=<your_rpc_username> \ -rpcpassword=<your_rpc_password> \ -rpcport=38332 \ getwalletinfo
formatshould bebdb:{ "walletname": "btcstaker", "walletversion": 169900, "format": "bdb", "balance": 0.00000000, "unconfirmed_balance": 0.00000000, "immature_balance": 0.00000000, "txcount": 0, "keypoololdest": 1706554908, "keypoolsize": 1000, "hdseedid": "9660319ab465abc05db95ad17cb59a9ec8f106fd", "keypoolsize_hd_internal": 1000, "unlocked_until": 0, "paytxfee": 0.00000000, "private_keys_enabled": true, "avoid_reuse": false, "scanning": false, "descriptors": false, "external_signer": false } - You can also use
bitcoin.confinstead of using flags in thebitcoindcmd. Please check the Bitcoin signet wiki and this manual here to learn how to setbitcoin.conf. Ensure you have configured thebitcoind.confcorrectly and set all the required parameters as shown in the systemd service file above.
This project requires Go version 1.21 or later.
Install Go by following the instructions on the official Go installation guide.
Install essential tools and packages needed to compile and build the binaries.
sudo apt install build-essentialTo get started, clone the repository to your local machine from Github:
git clone https://github.com/babylonlabs-io/btc-staker.gitYou can choose a specific version from the official releases page
cd btc-staker # cd into the project directory
git checkout <release-tag>At the top-level directory of the project
make installThe above command will build and install the following binaries to
$GOPATH/bin:
stakerd: The daemon program for the btc-stakerstakercli: The CLI tool for interacting with the stakerd.
If your shell cannot find the installed binaries, make sure $GOPATH/bin is in
the $PATH of your shell. Usually these commands will do the job
export PATH=$HOME/go/bin:$PATH
echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.profileThe stakerd daemon requires a keyring with loaded Babylon tokens to pay for the
transactions. Follow this
guide
to create a keyring and request funds.
stakercli tool serves as a control plane for the Staker Daemon.
Initialize the home directory for the Staker Daemon and dump the default configuration file to the specified directory.
stakercli admin dump-config --config-file-dir /path/to/stakerd-home/After initialization, the home directory will have the following structure
ls /path/to/stakerd-home/
├── stakerd.confIf the --config-file-dir flag is not specified, then the default home directory
will be used. For different operating systems, those are:
- MacOS
~/Library/Application Support/Stakerd - Linux
~/.Stakerd - Windows
C:\Users\<username>\AppData\Local\Stakerd
In the following, we go through important parameters of the stakerd.conf file.
Notes:
-
The
Keyparameter in the config below is the name of the key in the keyring to use for signing transactions. Use the key name you created in Create a Babylon keyring with funds -
Ensure that the
KeyDirectoryis set to the location where the keyring is stored. -
Ensure to use the
testkeyring backend -
Ensure you use the correct
ChainIDfor the network you're connecting to. For example, for Babylon devnet, the chain ID isbbn-dev-5. -
To change the Babylon RPC/GRPC address, update the following:
RPCAddr = https://rpc.devnet.babylonchain.io:443 # rpc node address GRPCAddr = https://grpc.devnet.babylonchain.io:443 # grpc node address
The above addresses are for Babylon devnet.
-
If you encounter any gas-related errors while performing staking operations, consider adjusting the
GasAdjustmentandGasPricesparameters. For example, you can set:GasAdjustment = 1.5 GasPrices = 0.002ubbn
[babylon]
# Name of the key in the keyring to use for signing transactions
Key = btc-staker
# Chain id of the chain (Babylon)
ChainID = bbn-test
# Address of the chain's RPC server (Babylon)
RPCAddr = http://localhost:26657
# Address of the chain's GRPC server (Babylon)
GRPCAddr = https://localhost:9090
# Directory to store staker keys in
KeyDirectory = /path/to/stakerd-home/Notes:
- BTC configuration should reflect the BTC node that we're running and the network Babylon connects to.
- You can use this faucet to receive signet BTC.
[chain]
# btc network to run on
Network = signet
[btcnodebackend]
# type of node to connect to {bitcoind, btcd}
Nodetype = bitcoind
# type of wallet to connect to {bitcoind, btcwallet}
WalletType = bitcoind
# fee mode to use for fee estimation {static, dynamic}. In dynamic mode fee will be estimated using backend node
FeeMode = staticNote: Make sure you create a BTC wallet, name it appropriately, and load it with enough signet BTC.
[walletconfig]
# name of the wallet to sign Bitcoin transactions.
# this should be the same as set in createwallet command in bitcoind.
WalletName = btcstaker
# passphrase to unlock the wallet
WalletPass = walletpass
[walletrpcconfig]
# location of the wallet rpc server
# note: in case of bitcoind, the wallet host is same as the rpc host
Host = localhost:38332
# user auth for the wallet rpc server
# note: in case of bitcoind, the wallet rpc credentials are same as rpc credentials
# this should be the same as set in the bitcoind daemon
User = your_rpc_username
# password auth for the wallet rpc server. This should be the same as set in the bitcoind daemon
Pass = your_rpc_password
# disables tls for the wallet rpc client
DisableTls = true
Make sure to replace the following important parameters related to bitcoind as per
your setup.
[bitcoind]
# The daemon's rpc listening address
# note: P2P port for signet is 38332/38333
# mainnet 8332/8333
# testnet 18332/18333
# regtest 18443
# ref - https://github.com/bitcoin/bitcoin/blob/03752444cd54df05a731557968d5a9f33a55c55c/src/chainparamsbase.cpp#L39
RPCHost = 127.0.0.1:38332
# Username for RPC connections. This should be the same as set in the bitcoind daemon
RPCUser = your_rpc_username
# Password for RPC connections. This should be the same as set in the bitcoind daemon
RPCPass = your_rpc_password
# The address listening for ZMQ connections to deliver raw block notifications
ZMQPubRawBlock = tcp://127.0.0.1:29001
# The address listening for ZMQ connections to deliver raw transaction notifications
ZMQPubRawTx = tcp://127.0.0.1:29002by default prometheus metrics are disabled, although it is possible to enabled it in the config file.
[metricsconfig]
# if it should be enabled.
Enabled = false
# host of prometheus server.
Host = 127.0.0.1
# port of prometheus server.
ServerPort = 2112To see the complete list of configuration options, check the stakerd.conf file.
The Staker Daemon and stakercli communicate securely using Basic Authentication with a username and password.
These credentials are loaded from the environment. One convenient option is to create a .env file in the same directory as the binaries with the following variables:
BTCSTAKER_USERNAME— Sets the Basic Auth username.BTCSTAKER_PASSWORD— Sets the Basic Auth password.
You can start the staker daemon using the following command:
stakerdThis will start the Staker daemon RPC server at the address specified in the configuration under
the RawRPCListeners field. A custom address can also be specified using
the --rpclisten flag.
stakerd --rpclisten 'localhost:15812'
time="2023-12-08T11:48:04+05:30" level=info msg="Starting StakerApp"All the available CLI options can be viewed using the --help flag. These options
can also be set in the configuration file.
The following guide will show how to stake, withdraw, and unbond Bitcoin.
Find the BTC public key of the finality provider you intend to stake to.
When staking, specify the BTC public key of a single finality provider using the
--finality-providers-pks flag in the stake command.
Note Make sure to use only one finality provider BTC public key in
the --finality-providers-pks flag of the
stake
command, as multiple providers are not currently supported.
stakercli daemon babylon-finality-providers
{
"finality_providers": [
{
"babylon_public_Key": "0294092d0266c8d26544291b692e13f1e4fcba7829c5445ff99fcb3aefb23fe7cd",
"bitcoin_public_Key": "3328782c63404386d9cd905dba5a35975cba629e48192cea4a348937e865d312"
}
],
"total_finality_providers_count": "1"
}Find the BTC address that has sufficient Bitcoin balance that you want to stake from.
Note: In case you don't have addresses with adequate balances, you can use the faucet to receive signet BTC. Visit the faucet link to acquire signet BTC.
stakercli daemon list-outputs
{
"outputs": [
{
"amount": "10 BTC",
"address": "bcrt1q56ehztys752uzg7fzpear08l5mw8w2kxgz7644"
},
{
"amount": "10 BTC",
"address": "bcrt1ql94x9v78ag7qx896f0axka809u55pla8cywsvn"
}
]
}Stake Bitcoin to the finality provider of your choice. The --staking-time flag
specifies the timelock of the staking transaction in BTC blocks.
The --staking-amount
flag specifies the amount in satoshis to stake.
stakercli daemon stake \
--staker-address bcrt1q56ehztys752uzg7fzpear08l5mw8w2kxgz7644 \
--staking-amount 1000000 \
--finality-providers-pks 3328782c63404386d9cd905dba5a35975cba629e48192cea4a348937e865d312 \
--staking-time 10000 # ~70 days
# Transaction details
{
"tx_hash": "6bf442a2e864172cba73f642ced10c178f6b19097abde41608035fb26a601b10"
}Note: You can self delegate i.e. stake to your own finality provider. Follow
the finality provider registration guide
to create and register a finality provider to Babylon. Once the finality provider is
registered, you can use your finality provider BTC public key in
the --finality-providers-pks flag of the stake
command.
The unbond cmd initiates the unbonding flow which involves communication with the
Babylon chain, Covenant emulators, and the BTC chain. It
- Build the unbonding transaction and send it to the Babylon chain
- Wait for the signatures from the covenant emulators
- Send the unbonding transaction to the BTC chain
--staking-transaction-hash is the transaction hash from the response of the stake
command.
stakercli daemon unbond \
--staking-transaction-hash 6bf442a2e864172cba73f642ced10c178f6b19097abde41608035fb26a601b10Note:
- You can also use this cmd to get the list of all staking transactions in db.
stakercli daemon list-staking-transactions
- There is a minimum unbonding time currently set to 50 BTC blocks. After this period, the unbonding timelock will expire, and the staked funds will be unbonded.
The staker can withdraw the staked funds after the timelock of the staking or unbonding transaction expires.
--staking-transaction-hash is the transaction hash from the response of stake
command.
stakercli daemon unstake \
--staking-transaction-hash 6bf442a2e864172cba73f642ced10c178f6b19097abde41608035fb26a601b10Note: You can also use this cmd to get the list of all withdrawable staking transactions in db.
stakercli daemon withdrawable-transactionsIn order to unstake you'll need to wait for your staking/unbonding tx to be deep
enough in btc so that the timelock expires.