Skip to content
Open
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ start-deployment-btcstaking-bitcoind-demo:
$(MAKE) -C $(CURDIR)/deployments/btcstaking-bitcoind \
start-deployment-btcstaking-bitcoind-demo

start-deployment-btcstaking-bitcoind-multisig-demo:
USE_DOCKERHUB_IMAGES=$(USE_DOCKERHUB_IMAGES) \
$(MAKE) -C $(CURDIR)/deployments/btcstaking-bitcoind \
start-deployment-btcstaking-bitcoind-multisig-demo

stop-deployment-btcstaking-bitcoind:
$(MAKE) -C $(CURDIR)/deployments/btcstaking-bitcoind \
stop-deployment-btcstaking-bitcoind
2 changes: 1 addition & 1 deletion babylon
Submodule babylon updated 105 files
2 changes: 1 addition & 1 deletion btc-staker
Submodule btc-staker updated 72 files
+4 −4 .github/workflows/ci.yml
+31 −30 .golangci.yml
+14 −0 CHANGELOG.md
+3 −0 Dockerfile
+101 −25 babylonclient/babyloncontroller.go
+25 −1 babylonclient/interface.go
+2 −0 babylonclient/keyringcontroller/codec.go
+2 −0 babylonclient/keyringcontroller/keyring.go
+8 −0 babylonclient/keyringcontroller/keyringcontroller.go
+6 −1 babylonclient/msgsender.go
+25 −6 babylonclient/pop.go
+1 −0 babylonclient/utils.go
+2 −0 cmd/auth.go
+69 −7 cmd/stakercli/admin/admin.go
+216 −2 cmd/stakercli/daemon/daemoncommands.go
+3 −0 cmd/stakercli/helpers/flags.go
+1 −0 cmd/stakercli/main.go
+10 −1 cmd/stakercli/pop/pop.go
+1 −0 cmd/stakercli/transaction/parsers.go
+12 −1 cmd/stakercli/transaction/transactions.go
+21 −7 cmd/stakercli/transaction/transactions_test.go
+9 −2 cmd/stakerd/main.go
+56 −0 docs/multisig-guide.md
+26 −22 go.mod
+52 −50 go.sum
+35 −7 itest/bitcoind_node_setup.go
+1 −1 itest/containers/config.go
+12 −2 itest/containers/containers.go
+416 −10 itest/e2e_test.go
+317 −17 itest/manager.go
+14 −2 itest/testutil/appcli.go
+3 −1 itest/testutil/port.go
+117 −3 itest/testutil/version.go
+89 −0 itest/testutil/version_test.go
+3 −0 metrics/prometheus.go
+3 −1 metrics/prometheus_test.go
+2 −0 metrics/staker.go
+10 −3 staker/babylontypes.go
+8 −0 staker/commands.go
+2 −0 staker/events.go
+15 −0 staker/feeestimator.go
+461 −0 staker/multisig_utils.go
+9 −2 staker/nodebackend.go
+17 −0 staker/pop_creator.go
+3 −0 staker/responses.go
+186 −6 staker/signing.go
+1,026 −56 staker/stakerapp.go
+3 −1 staker/stakercontroller.go
+4 −0 staker/staking.go
+193 −0 staker/types.go
+5 −0 stakercfg/address_parser.go
+4 −0 stakercfg/babylon.go
+2 −0 stakercfg/bitcoind.go
+1 −0 stakercfg/btcd.go
+103 −5 stakercfg/config.go
+4 −0 stakercfg/dbcfg.go
+2 −0 stakercfg/metrics.go
+7 −1 stakercfg/utils.go
+1 −0 stakerdb/errors.go
+1 −1 stakerdb/trackedtransactionstore_test.go
+79 −0 stakerservice/client/rpcclient.go
+2 −0 stakerservice/rpc_func.go
+106 −7 stakerservice/service.go
+16 −0 stakerservice/stakerdresponses.go
+5 −0 types/feeestimation.go
+7 −0 types/nodebackend.go
+5 −0 types/walletbackend.go
+4 −3 utils/btc_utils.go
+13 −1 utils/utils.go
+23 −7 walletcontroller/client.go
+11 −3 walletcontroller/interface.go
+1 −0 walletcontroller/transaction.go
57 changes: 57 additions & 0 deletions contrib/images/bitcoindsim/wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ rpcbind=0.0.0.0
rpcallowip=0.0.0.0/0
" > "$BITCOIN_CONF"

GENERATE_STAKER_MULTISIG="${GENERATE_STAKER_MULTISIG:=true}"
STAKER_CONF_PATH="${STAKER_CONF_PATH:=/home/btcstaker/.stakerd/stakerd.conf}"
STAKER_MULTISIG_WALLET_NAME="${STAKER_MULTISIG_WALLET_NAME:=btcstaker-multisig}"
STAKER_MULTISIG_KEYS_COUNT="${STAKER_MULTISIG_KEYS_COUNT:=3}"
STAKER_MULTISIG_THRESHOLD="${STAKER_MULTISIG_THRESHOLD:=2}"

GENERATE_STAKER_WALLET="${GENERATE_STAKER_WALLET:=true}"
echo "Starting bitcoind..."
bitcoind -regtest -datadir="$BITCOIN_DATA" -conf="$BITCOIN_CONF" -daemon
Expand Down Expand Up @@ -67,6 +73,57 @@ if [[ "$GENERATE_STAKER_WALLET" == "true" ]]; then
bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" getbalance
fi

if [[ "$GENERATE_STAKER_MULTISIG" == "true" ]]; then
if [[ ! -f "$STAKER_CONF_PATH" ]]; then
echo "Staker config not found at $STAKER_CONF_PATH, skipping multisig key injection"
ls -la "$(dirname "$STAKER_CONF_PATH")" || true
fi

echo "Creating a multisig wallet ($STAKER_MULTISIG_WALLET_NAME) with $STAKER_MULTISIG_KEYS_COUNT keys for btc-staker..."
bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" createwallet "$STAKER_MULTISIG_WALLET_NAME" false false "$WALLET_PASS" false false
bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$STAKER_MULTISIG_WALLET_NAME" walletpassphrase "$WALLET_PASS" 60

MULTISIG_WIFS=()
MULTISIG_ADDRS=()
for i in $(seq 1 1 "$STAKER_MULTISIG_KEYS_COUNT"); do
addr=$(bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$STAKER_MULTISIG_WALLET_NAME" getnewaddress)
MULTISIG_ADDRS+=("$addr")
wif=$(bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$STAKER_MULTISIG_WALLET_NAME" dumpprivkey "$addr")
MULTISIG_WIFS+=("$wif")
done

# Fund each multisig address from the main wallet to mirror btcstaker funding.
bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" walletpassphrase "$WALLET_PASS" 1
for addr in "${MULTISIG_ADDRS[@]}"; do
bitcoin-cli -regtest -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" sendtoaddress "$addr" 10
done
sleep 5

wifs_csv=$(IFS=,; echo "${MULTISIG_WIFS[*]}")
awk -v wifs="$wifs_csv" -v threshold="$STAKER_MULTISIG_THRESHOLD" '
/^[[:space:]]*StakerKeyWIFs[[:space:]]*=/ {
print "StakerKeyWIFs = " wifs
foundW=1
next
}
/^[[:space:]]*StakerThreshold[[:space:]]*=/ {
print "StakerThreshold = " threshold
foundT=1
next
}
{ print }
END {
if (foundW != 1) {
print "StakerKeyWIFs = " wifs
}
if (foundT != 1) {
print "StakerThreshold = " threshold
}
}
' "$STAKER_CONF_PATH" > "${STAKER_CONF_PATH}.tmp" && mv "${STAKER_CONF_PATH}.tmp" "$STAKER_CONF_PATH"
echo "Injected ${#MULTISIG_WIFS[@]} staker multisig keys and threshold ${STAKER_MULTISIG_THRESHOLD} into $STAKER_CONF_PATH"
fi

echo "Generating a block every ${GENERATE_INTERVAL_SECS} seconds."
echo "Press [CTRL+C] to stop..."
while true
Expand Down
10 changes: 8 additions & 2 deletions deployments/btcstaking-bitcoind/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ EXTRA_BUILD_TARGET := build-babylond build-vigilante build-btc-staker build-fina
endif

ifeq ($(USE_DOCKERHUB_IMAGES),TRUE)
# currently doesn't support multisig-demo.sh to work with using dockerhub images. but still support build images locally and test it.
# TODO: after releasing all other repos with multisig support release, switch it.
BABYLOND_IMAGE= "babylonlabs/babylond:v4.2.0"
VIGILANTE_IMAGE= "babylonlabs/vigilante:v0.24.1"
BTCSTAKER_IMAGE= "babylonlabs/btc-staker:v0.17.0"
BTCSTAKER_IMAGE= "babylonlabs/btc-staker:v0.18.1"
FP_IMAGE= "babylonlabs/finality-provider:v2.0.1"
COVENANT_IMAGE= "babylonlabs/covenant-emulator:v0.16.0"
COVENANT_SIGNER_IMAGE= "babylonlabs/covenant-signer:v0.16.0"
Expand Down Expand Up @@ -47,7 +49,8 @@ build-covenant-signer:
build-tmkms:
$(MAKE) -C $(GIT_TOPLEVEL)/contrib/images tmkms

build-deployment-btcstaking-bitcoind: build-bitcoindsim build-tmkms $(EXTRA_BUILD_TARGET)
# TODO: restore build-tmkms once its bug is fixed (issue #47)
build-deployment-btcstaking-bitcoind: build-bitcoindsim $(EXTRA_BUILD_TARGET)

start-deployment-btcstaking-bitcoind: stop-deployment-btcstaking-bitcoind build-deployment-btcstaking-bitcoind
BABYLOND_IMAGE=${BABYLOND_IMAGE} \
Expand All @@ -64,6 +67,9 @@ start-deployment-btcstaking-bitcoind: stop-deployment-btcstaking-bitcoind build-
start-deployment-btcstaking-bitcoind-demo: start-deployment-btcstaking-bitcoind
NUM_FINALITY_PROVIDERS=$(NUM_FINALITY_PROVIDERS) ./btcstaking-demo.sh

start-deployment-btcstaking-bitcoind-multisig-demo: start-deployment-btcstaking-bitcoind
NUM_FINALITY_PROVIDERS=$(NUM_FINALITY_PROVIDERS) ./multisig-demo.sh

stop-deployment-btcstaking-bitcoind:
BABYLOND_IMAGE=$(BABYLOND_IMAGE) \
VIGILANTE_IMAGE=$(VIGILANTE_IMAGE) \
Expand Down
28 changes: 15 additions & 13 deletions deployments/btcstaking-bitcoind/artifacts/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
version: "3"

services:
tmkms:
container_name: tmkms
image: babylonlabs-io/tmkms:latest
volumes:
- ../.testnets/tmkms:/tmkms
command: tmkms start -c /tmkms/config/tmkms.toml
networks:
localnet:
ipv4_address: 192.168.10.18
ports:
- "26658:26658"
# TODO: restore tmkms once its bug is fixed (issue #47)
# tmkms:
# container_name: tmkms
# image: babylonlabs-io/tmkms:latest
# volumes:
# - ../.testnets/tmkms:/tmkms
# command: tmkms start -c /tmkms/config/tmkms.toml
# networks:
# localnet:
# ipv4_address: 192.168.10.18
# ports:
# - "26658:26658"

babylondnode0:
container_name: babylondnode0
Expand All @@ -32,8 +33,8 @@ services:
networks:
localnet:
ipv4_address: 192.168.10.2
depends_on:
- tmkms
# depends_on:
# - tmkms
environment:
- BABYLON_BLS_PASSWORD=password

Expand Down Expand Up @@ -86,6 +87,7 @@ services:
- "29000-29002:29000-29002"
volumes:
- ../.testnets/bitcoin:/bitcoindsim/.bitcoin:Z
- ../.testnets/btc-staker:/home/btcstaker/.stakerd:Z

electrs:
image: mempool/electrs:v3.1.0
Expand Down
9 changes: 9 additions & 0 deletions deployments/btcstaking-bitcoind/artifacts/stakerd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,12 @@ AutoCompactMinAge = 168h0m0s

; Specifies the timeout value to use when opening the wallet database.
DBTimeout = 1m0s

[stakermultisigconfig]
; WIF-encoded staker private keys used for taproot multisig signing (auto-sorted by pubkey later)
StakerKeyWIFs =

; threshold for the staker taproot multisig branch
StakerThreshold = 0

; DecodedWIFs =
8 changes: 4 additions & 4 deletions deployments/btcstaking-bitcoind/btcstaking-demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
echo "Create $NUM_FINALITY_PROVIDERS Bitcoin finality providers"

declare -a btcPks=()

for idx in $(seq 0 $((NUM_FINALITY_PROVIDERS-1))); do
# skips the "Warning: HMAC key not configured. Authentication will not be enabled." with awk
btcPk=$(docker exec eotsmanager /bin/sh -c "
Expand Down Expand Up @@ -96,10 +97,9 @@ echo "Finality Provider with Bitcoin public key $attackerBtcPk submitted a confl
echo "Wait a few minutes for the last, shortest BTC delegation (10 BTC blocks) to expire..."
sleep 10

# TODO: enable ust of expired staked btc funds after making it possible
#echo "Withdraw the expired staked BTC funds (staking tx hash: $btcTxHash)"
#docker exec btc-staker /bin/sh -c \
# "/bin/stakercli dn ust --staking-transaction-hash $btcTxHash"
echo "Withdraw the expired staked BTC funds (staking tx hash: $btcTxHash)"
docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn ust --staking-transaction-hash $btcTxHash"

echo "Unbond staked BTC tokens (staking tx hash: ${txHashes[1]}"
docker exec btc-staker /bin/sh -c \
Expand Down
114 changes: 114 additions & 0 deletions deployments/btcstaking-bitcoind/multisig-demo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/bin/bash -eux

echo "Create $NUM_FINALITY_PROVIDERS Bitcoin finality providers"

declare -a btcPks=()

for idx in $(seq 0 $((NUM_FINALITY_PROVIDERS-1))); do
# skips the "Warning: HMAC key not configured. Authentication will not be enabled." with awk
btcPk=$(docker exec eotsmanager /bin/sh -c "
/bin/eotsd keys add finality-provider$idx --keyring-backend=test --rpc-client "0.0.0.0:15813" --output=json | awk '/^\{/ {p=1} p' | jq -r '.pubkey_hex'
")
btcPks+=("$btcPk")
docker exec finality-provider$idx /bin/sh -c "
/bin/fpd cfp --key-name finality-provider$idx \
--chain-id chain-test \
--eots-pk $btcPk \
--commission-rate 0.05 \
--commission-max-change-rate 0.05 \
--commission-max-rate 0.1 \
--moniker \"Finality Provider $idx\" | head -n -1 | jq -r .finality_provider.btc_pk_hex
"
done

# Restart the finality provider containers so that key creation command above
# takes effect and finality provider is start communication with the chain.
echo "Restarting finality provider containers..."
for idx in $(seq 0 $((NUM_FINALITY_PROVIDERS-1))); do
echo "Restarting finality-provider$idx"
docker restart finality-provider$idx
done
echo "All finality provider containers restarted"


echo "Created $NUM_FINALITY_PROVIDERS Bitcoin finality providers"
echo "Finality provider btc pks" ${btcPks[@]}

echo "Make a delegation to each of the finality providers from a dedicated BTC address"
sleep 10

# Get the available BTC addresses for delegations
delAddrs=($(docker exec btc-staker /bin/sh -c '/bin/stakercli dn list-outputs | jq -r ".outputs[].address" | sort | uniq'))
echo "Delegators Addrs bond vars" $delAddrs

i=0
declare -a txHashes=()
for btcPk in ${btcPks[@]}
do
# Let `X=NUM_FINALITY_PROVIDERS`
# For the first X - 1 requests, we select a staking period of 500 BTC
# blocks. The Xth request will last only for 10 BTC blocks, so that we can
# showcase the reclamation of expired BTC funds afterwards.
if [ $((i % $NUM_FINALITY_PROVIDERS)) -eq $((NUM_FINALITY_PROVIDERS -1)) ];
then
stakingTime=10
else
stakingTime=500
fi

echo "Delegating 1 million Satoshis from BTC address ${delAddrs[i]} using multisig staker keys to Finality Provider with Bitcoin public key $btcPk for $stakingTime BTC blocks";

btcTxHash=$(docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn stake-multisig --funding-address ${delAddrs[i]} --staking-amount 1000000 --finality-providers-pks $btcPk --staking-time $stakingTime | jq -r '.tx_hash'")
echo "Delegation was successful; staking tx hash is $btcTxHash"
txHashes+=("$btcTxHash") # Store the tx hash in the array
i=$((i+1))
done

echo "Made a delegation to each of the finality providers"

echo "Wait a few minutes for the delegations to become active..."
while true; do
allDelegationsActive=$(docker exec finality-provider0 /bin/sh -c \
'fpd ls | jq ".finality_providers[].last_voted_height != null"')

if [[ $allDelegationsActive == *"false"* ]]
then
sleep 10
else
echo "All delegations have become active"
break
fi
done

echo "Create a multisig stake expansion using the long-lived multisig delegation"
msStakeExpandAmount=1500000
msStakeExpandTime=300
msStakeExpandBaseTx=${txHashes[0]}
msStakeExpandFunding=${delAddrs[0]}
msStakeExpandFpPk=${btcPks[0]}
msStakeExpandTxHash=$(docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn stake-expand-multisig --funding-address ${msStakeExpandFunding} --staking-amount ${msStakeExpandAmount} --finality-providers-pks ${msStakeExpandFpPk} --staking-time ${msStakeExpandTime} --staking-transaction-hash ${msStakeExpandBaseTx} | jq -r '.tx_hash'")
echo "Stake expansion (multisig) submitted; tx hash is ${msStakeExpandTxHash}"

echo "Wait a few minutes for the last, shortest BTC delegation (10 BTC blocks) to expire..."
sleep 100

echo "Withdraw the expired multisig staked BTC funds (staking tx hash: $btcTxHash)"
docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn ustm --staking-transaction-hash $btcTxHash"

echo "Unbond multisig staked BTC tokens (staking tx hash: ${txHashes[1]})"
docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn unbond-multisig --staking-transaction-hash ${txHashes[1]}"

echo "Wait for the unbond transaction to expire"
sleep 180

echo "Withdraw the expired multisig staked BTC funds from unbonding (staking tx hash: ${txHashes[1]})"
docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn unstake-multisig --staking-transaction-hash ${txHashes[1]}"

echo "Check staking state for multisig stake expansion"
docker exec btc-staker /bin/sh -c \
"/bin/stakercli dn staking-details --staking-transaction-hash ${msStakeExpandTxHash}"
22 changes: 12 additions & 10 deletions deployments/btcstaking-bitcoind/pre-deployment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ docker run --rm -v $(pwd)/.testnets:/data ${BABYLOND_IMAGE} \
--covenant-pks "2d4ccbe538f846a750d82a77cd742895e51afcf23d86d05004a356b783902748" # should be updated if `covenant-keyring` dir is changed`

# Create tmkms directory that holds the tmkms secrets
mkdir -p .testnets/tmkms
docker run --rm \
-v $(pwd)/.testnets/tmkms:/tmkms \
-v $(pwd)/.testnets/node0:/tmkms/node0 \
babylonlabs-io/tmkms:latest /bin/sh -c " \
tmkms init /tmkms/config && \
tmkms softsign keygen /tmkms/config/secrets/secret_connection_key && \
tmkms softsign import /tmkms/node0/babylond/config/priv_validator_key.json /tmkms/config/secrets/priv_validator_key \
"
# TODO: restore tmkms once its bug is fixed (issue #47)
#mkdir -p .testnets/tmkms
#docker run --rm \
# -v $(pwd)/.testnets/tmkms:/tmkms \
# -v $(pwd)/.testnets/node0:/tmkms/node0 \
# babylonlabs-io/tmkms:latest /bin/sh -c " \
# tmkms init /tmkms/config && \
# tmkms softsign keygen /tmkms/config/secrets/secret_connection_key && \
# tmkms softsign import /tmkms/node0/babylond/config/priv_validator_key.json /tmkms/config/secrets/priv_validator_key \
# "

# Create separate subpaths for each component and copy relevant configuration
mkdir -p .testnets/bitcoin
Expand All @@ -55,4 +56,5 @@ cp artifacts/covd.conf .testnets/covenant-emulator/covd.conf
cp -R artifacts/covenant-emulator-keyring .testnets/covenant-emulator/keyring-test
cp artifacts/covenant-signer.toml .testnets/covenant-signer/config.toml
cp -R artifacts/covenant-signer-keyring .testnets/covenant-signer/keyring-test
cp artifacts/tmkms.toml .testnets/tmkms/config/tmkms.toml
# TODO: restore tmkms once its bug is fixed (issue #47)
#cp artifacts/tmkms.toml .testnets/tmkms/config/tmkms.toml