BTC-Staker is a toolset designed for seamless Bitcoin staking. It consists of two components:
-
stakerd
- Thestakerd
daemon manages connections to the Babylon and Bitcoin nodes. -
stakercli
- Thestakercli
is a command line interface (CLI) to facilitate interaction with thestakerd
daemon . 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-cli
Please 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_bdb
is 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/0
andrpcbind=0.0.0.0
to the bitcoind command. - Start the
bitcoind
with-txindex
option 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
,rpcport
that you used while setting up the bitcoind systemd service. -named createwallet
indicates that a new wallet should be created with the provided name.wallet_name=btcstaker
specifies 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.conf
file.- Setting
load_on_startup=true
ensures that the wallet automatically loads during system startup. descriptors=false
disables 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 \
getnewaddress
where 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 \
getunconfirmedbalance
You 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 $TXID
where $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 \
getbalance
Notes:
- 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.936446
in 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
format
should 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.conf
instead of using flags in thebitcoind
cmd. Please check the Bitcoin signet wiki and this manual here to learn how to setbitcoin.conf
. Ensure you have configured thebitcoind.conf
correctly 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-essential
To get started, clone the repository to your local machine from Github:
git clone https://github.com/babylonlabs-io/btc-staker.git
You 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 install
The 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' >> ~/.profile
The 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.conf
If 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
Key
parameter 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
KeyDirectory
is set to the location where the keyring is stored. -
Ensure to use the
test
keyring backend -
Ensure you use the correct
ChainID
for 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
GasAdjustment
andGasPrices
parameters. 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 = static
Note: 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:29002
To see the complete list of configuration options, check the stakerd.conf
file.
You can start the staker daemon using the following command:
stakerd
This 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 6bf442a2e864172cba73f642ced10c178f6b19097abde41608035fb26a601b10
Note:
- 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 6bf442a2e864172cba73f642ced10c178f6b19097abde41608035fb26a601b10
Note: You can also use this cmd to get the list of all withdrawable staking transactions in db.
stakercli daemon withdrawable-transactions
In order to unstake
you'll need to wait for your staking/unbonding tx to be deep
enough in btc so that the timelock expires.