robosats/scripts/traditional/regtest-nodes
2025-05-28 17:32:04 +02:00

1439 lines
39 KiB
Bash
Executable File

#!/bin/sh
_command_exist() {
if command -v "$1" >/dev/null 2>&1; then
return 0
else
echo "error: $1 command not found" >&2
return 1
fi
}
_function_exist() {
if command -V "$1" 2>/dev/null | grep -qwi function >/dev/null 2>&1; then
return 0
else
echo "error: function $1 not set" >&2
return 1
fi
}
_create_dir() {
if [ ! -e "$1" ]; then
mkdir -p "$1" || return "$?"
elif [ ! -d "$1" ]; then
echo "error: $1 is not a directory" >&2
return 1
fi
}
# if $2 is provided use it as default
# otherwise return error when not found
_get_env_var() {
if ! env_var="$(dotenv -f ".env" get "$1" 2>/dev/null)"; then
if [ "$#" -ge 2 ]; then
env_var="$2"
else
echo "error: getting $1 from .env" >&2
return 1
fi
fi
printf "%s\n" "$env_var"
return 0
}
# transform relative path into absolute and remove trailing slashes
_get_env_var_path() {
env_var="$(_get_env_var "$@")" || return "$?"
real_path="$(realpath -m "$env_var")" || return "$?"
printf "%s\n" "$real_path"
return 0
}
# extract the port from an env variable
_get_env_var_port() {
env_var="$(_get_env_var "$@")" || return "$?"
printf "%s\n" "$env_var" |
rev |
cut -d ":" -f 1 |
rev
return 0
}
_nodes_environment_set() {
if [ ! -f "scripts/traditional/regtest-nodes" ]; then
echo "error: source this script from the project root" >&2
return 1
fi
if [ ! -f ".env" ]; then
echo "error: .env is not present" >&2
return 1
fi
if ! _command_exist dotenv; then
return 1
fi
TESTING="$(_get_env_var TESTING false)" || return "$?"
case "$(printf "%s\n" "$TESTING" | tr '[:upper:]' '[:lower:]')" in
true|yes|on|1)
TESTING=true
;;
*)
TESTING=false
;;
esac
RUNSERVER_PORT="$(_get_env_var "RUNSERVER_PORT")" || return "$?"
cln_set=false
lnd_set=false
LNVENDOR_COORD="$(_get_env_var LNVENDOR)" || return "$?"
LNVENDOR_COORD="$(printf "%s\n" "$LNVENDOR_COORD" | tr '[:upper:]' '[:lower:]')"
case "$LNVENDOR_COORD" in
cln) cln_set=true ;;
lnd) lnd_set=true ;;
*)
echo "error: LNVENDOR can be cln or lnd" >&2
return 1
;;
esac
LNVENDOR_USER="$(_get_env_var LNVENDOR_USER)" || return "$?"
LNVENDOR_USER="$(printf "%s\n" "$LNVENDOR_USER" | tr '[:upper:]' '[:lower:]')"
case "$LNVENDOR_USER" in
cln) cln_set=true ;;
lnd) lnd_set=true ;;
*)
echo "error: LNVENDOR_USER can be cln or lnd" >&2
return 1
;;
esac
if [ "$TESTING" = true ] && [ "$LNVENDOR_USER" != "lnd" ]; then
echo "error: LNVENDOR_USER should be lnd when running tests" >&2
return 1
fi
BITCOIND_BIN="$(_get_env_var_path "BITCOIND_BIN")" || return "$?"
BITCOIN_CLI_BIN="$(_get_env_var_path "BITCOIN_CLI_BIN")" || return "$?"
if [ "$cln_set" = true ]; then
LIGHTNINGD_BIN="$(_get_env_var_path "LIGHTNINGD_BIN")" || return "$?"
LIGHTNING_CLI_BIN="$(_get_env_var_path "LIGHTNING_CLI_BIN")" || return "$?"
HOLDINVOICE_PLUGIN_BIN="$(_get_env_var_path "HOLDINVOICE_PLUGIN_BIN")" || return "$?"
fi
if [ "$lnd_set" = true ]; then
LND_BIN="$(_get_env_var_path "LND_BIN")" || return "$?"
LNCLI_BIN="$(_get_env_var_path "LNCLI_BIN")" || return "$?"
fi
REGTEST_NODES_DIR="$(_get_env_var_path "TRADITIONAL_NODES_DIR")" || return "$?"
REGTEST_LOGS_DIR="$(_get_env_var_path "TRADITIONAL_LOGS_DIR")" || return "$?"
ROBOAUTO_GIT_DIR="$(_get_env_var_path "ROBOAUTO_GIT_DIR" false)" || return "$?"
BITCOIN_REGTEST_RPC_PORT="$(_get_env_var_port "BITCOIND_RPCURL")" || return "$?"
BITCOIN_REGTEST_RPC_USER="$(_get_env_var "BITCOIND_RPCUSER")" || return "$?"
BITCOIN_REGTEST_RPC_PASS="$(_get_env_var "BITCOIND_RPCPASSWORD")" || return "$?"
BITCOIN_REGTEST_ZMQ_BLOCK_PORT="$(_get_env_var "BITCOIN_TEST_ZMQ_BLOCK_PORT")" || return "$?"
BITCOIN_REGTEST_ZMQ_TX_PORT="$(_get_env_var "BITCOIN_TEST_ZMQ_TX_PORT")" || return "$?"
if [ "$cln_set" = true ]; then
CLN_COORD_GRPC_PORT="$(_get_env_var_port "CLN_GRPC_HOST")" || return "$?"
CLN_COORD_HOLD_PORT="$(_get_env_var_port "CLN_GRPC_HOLD_HOST")" || return "$?"
CLN_COORD_LISTEN_PORT="$(_get_env_var "CLN_TEST_COORD_LISTEN_PORT")" || return "$?"
CLN_USER_GRPC_PORT="$(_get_env_var "CLN_TEST_USER_GRPC_PORT")" || return "$?"
CLN_USER_LISTEN_PORT="$(_get_env_var "CLN_TEST_USER_LISTEN_PORT")" || return "$?"
fi
if [ "$lnd_set" = true ]; then
LND_COORD_RPC_PORT="$(_get_env_var_port "LND_GRPC_HOST")" || return "$?"
LND_COORD_LISTEN_PORT="$(_get_env_var "LND_TEST_COORD_LISTEN_PORT")" || return "$?"
LND_COORD_REST_PORT="$(_get_env_var "LND_TEST_COORD_REST_PORT")" || return "$?"
LND_USER_RPC_PORT="$(_get_env_var "LND_TEST_USER_GRPC_PORT")" || return "$?"
LND_USER_LISTEN_PORT="$(_get_env_var "LND_TEST_USER_LISTEN_PORT")" || return "$?"
LND_USER_REST_PORT="$(_get_env_var "LND_TEST_USER_REST_PORT")" || return "$?"
fi
if [ -z "$BITCOIND_BIN" ]; then
BITCOIND_BIN="bitcoind"
fi
if ! _command_exist "$BITCOIND_BIN"; then
return 1
fi
if [ -z "$BITCOIN_CLI_BIN" ]; then
BITCOIN_CLI_BIN="bitcoin-cli"
fi
if ! _command_exist "$BITCOIN_CLI_BIN"; then
return 1
fi
if [ "$cln_set" = true ]; then
if [ -z "$LIGHTNINGD_BIN" ]; then
LIGHTNINGD_BIN="lightningd"
fi
if ! _command_exist "$LIGHTNINGD_BIN"; then
return 1
fi
if [ -z "$LIGHTNING_CLI_BIN" ]; then
LIGHTNING_CLI_BIN="lightning-cli"
fi
if ! _command_exist "$LIGHTNING_CLI_BIN"; then
return 1
fi
if [ -z "$HOLDINVOICE_PLUGIN_BIN" ]; then
echo "error: $HOLDINVOICE_PLUGIN_BIN not set" >&2
return 1
fi
if [ ! -f "$HOLDINVOICE_PLUGIN_BIN" ]; then
echo "error: $HOLDINVOICE_PLUGIN_BIN plugin not found" >&2
return 1
fi
fi
if [ "$lnd_set" = true ]; then
if [ -z "$LND_BIN" ]; then
LND_BIN="lnd"
fi
if ! _command_exist "$LND_BIN"; then
return 1
fi
if [ -z "$LNCLI_BIN" ]; then
LNCLI_BIN="lncli"
fi
if ! _command_exist "$LNCLI_BIN"; then
return 1
fi
fi
if [ -z "$REGTEST_NODES_DIR" ]; then
echo "error: REGTEST_NODES_DIR not set" >&2
return 1
fi
_create_dir "$REGTEST_NODES_DIR" || return "$?"
if [ -z "$REGTEST_LOGS_DIR" ]; then
echo "error: REGTEST_LOGS_DIR not set" >&2
return 1
fi
_create_dir "$REGTEST_LOGS_DIR" || return "$?"
BITCOIN_DIR="$REGTEST_NODES_DIR/bitcoin"
ROBOAUTO_DIR="$REGTEST_NODES_DIR/roboauto"
if [ "$cln_set" = true ]; then
CLN_COORD_DIR="$REGTEST_NODES_DIR/cln-coord"
CLN_USER_DIR="$REGTEST_NODES_DIR/cln-user"
cln_coor_dir_env="$(_get_env_var_path "CLN_DIR")" || return "$?"
if [ "$cln_coor_dir_env" != "$CLN_COORD_DIR/regtest" ]; then
echo "error: CLN_DIR not consistent with REGTEST_NODES_DIR" >&2
return 1
fi
fi
if [ "$lnd_set" = true ]; then
LND_COORD_DIR="$REGTEST_NODES_DIR/lnd-coord"
LND_USER_DIR="$REGTEST_NODES_DIR/lnd-user"
lnd_coord_dir_env="$(_get_env_var_path "LND_DIR")" || return "$?"
if [ "$lnd_coord_dir_env" != "$LND_COORD_DIR" ]; then
echo "error: LND_DIR not consistent with REGTEST_NODES_DIR" >&2
return 1
fi
correct_macaroon_path="data/chain/bitcoin/regtest/admin.macaroon"
macaroon_path="$(
_get_env_var "MACAROON_PATH"
)" || return "$?"
if [ "$macaroon_path" != "$correct_macaroon_path" ]; then
echo "error: MACAROON_PATH should be $correct_macaroon_path" >&2
return 1
fi
lnd_test_coord_macaroon_path="$(
_get_env_var_path "LND_TEST_COORD_MACAROON_PATH"
)" || return "$?"
if [ \
"$lnd_test_coord_macaroon_path" != \
"$LND_COORD_DIR/$correct_macaroon_path" \
]; then
echo "error: LND_TEST_COORD_MACAROON_PATH not consistent with REGTEST_NODES_DIR" >&2
return 1
fi
lnd_test_user_macaroon_path="$(
_get_env_var_path "LND_TEST_USER_MACAROON_PATH"
)" || return "$?"
if [ \
"$lnd_test_user_macaroon_path" != \
"$LND_USER_DIR/$correct_macaroon_path" \
]; then
echo "error: LND_TEST_USER_MACAROON_PATH not consistent with REGTEST_NODES_DIR" >&2
return 1
fi
fi
}
_pgrep_bitcoin_regtest() {
pgrep -f "$BITCOIND_BIN -datadir=$BITCOIN_DIR -regtest -daemon" >/dev/null
}
_bitcoin_regtest_check_started() {
if ! _pgrep_bitcoin_regtest; then
echo "error: bitcoin regtest not started" >&2
return 1
fi
if ! _function_exist btc_reg; then
return 1
fi
return 0
}
bitcoin_regtest_mine() {
_bitcoin_regtest_check_started || return "$?"
if [ "$#" -lt 2 ]; then
return 1
fi
if [ "$2" = "new" ]; then
if ! new_address="$(btc_reg getnewaddress)"; then
echo "error: could not create new address" >&2
return 1
fi
else
new_address="$2"
fi
if ! new_block_hash="$(
btc_reg -named generatetoaddress nblocks="$1" address="$new_address" |
jq -r '.[-1]'
)"; then
echo "error: could not generate to $new_address" >&2
return 1
fi
while [ "$(btc_reg getbestblockhash)" != "$new_block_hash" ]; do
echo "waiting for $1 blocks to be mined..."
sleep 1
done
unset new_block_hash
echo "mined $1 blocks to $new_address"
unset new_address
}
bitcoin_regtest_start() {
_create_dir "$BITCOIN_DIR" || return "$?"
echo "writing bitcoin regtest config"
cat << EOF > "$BITCOIN_DIR/bitcoin.conf"
txindex=1
rest=1
server=1
logips=1
listenonion=0
zmqpubrawblock=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_BLOCK_PORT
zmqpubrawtx=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_TX_PORT
[regtest]
rpcport=$BITCOIN_REGTEST_RPC_PORT
rpcuser=$BITCOIN_REGTEST_RPC_USER
rpcpassword=$BITCOIN_REGTEST_RPC_PASS
EOF
if ! _pgrep_bitcoin_regtest; then
echo "starting bitcoin regtest"
if ! "$BITCOIND_BIN" -datadir="$BITCOIN_DIR" -regtest -daemon >/dev/null; then
echo "error: starting bitcoind regtest daemon" >&2
return 1
fi
else
echo "bitcoin regtest already started"
fi
btc_reg() {
"$BITCOIN_CLI_BIN" \
-datadir="$BITCOIN_DIR" \
-regtest \
-rpcuser="$BITCOIN_REGTEST_RPC_USER" \
-rpcpassword="$BITCOIN_REGTEST_RPC_PASS" \
"$@"
}
# wait for bitcoin to start
while ! btc_reg ping >/dev/null 2>&1; do
echo "waiting for bitcoind regtes to start..."
sleep 1
done
# check if default wallet exists
if
! btc_reg listwalletdir |
jq -r '.wallets[] | .name' |
grep "^default$" >/dev/null 2>&1
then
if ! btc_reg -named createwallet \
wallet_name=default \
disable_private_keys=false \
blank=false \
avoid_reuse=false \
descriptors=true \
load_on_startup=true \
external_signer=false >/dev/null
then
echo "error: could not create default wallet" >&2
return 1
fi
echo "default bitcoind wallet successfully created"
fi
# check if default wallet is loaded
if
! btc_reg listwallets |
jq -r '.[]' |
grep "^default$" >/dev/null 2>&1
then
if ! btc_reg loadwallet default; then
echo "error: could not load default wallet" >&2
return 1
fi
echo "default bitcoind wallet successfully loaded"
fi
bitcoin_regtest_mine 1 "new" || return "$?"
while [ "$(btc_reg getblockchaininfo | jq -r '.initialblockdownload')" != false ]; do
echo "waiting for bitcoind regtes to download blocks..."
sleep 1
done
echo "bitcoin regtest started, data directory is $BITCOIN_DIR"
echo "btc_reg function set"
}
_bitcoin_regtest_stop() {
if ! _pgrep_bitcoin_regtest; then
echo "bitcoin regtest already stopped"
else
if
! "$BITCOIN_CLI_BIN" \
-datadir="$BITCOIN_DIR" \
-regtest \
stop >/dev/null
then
echo "error: bitcoin regtest not stopped" >&2
fi
while _pgrep_bitcoin_regtest; do
echo "waiting for bitcoin regtest to stop..."
sleep 1
done
echo "bitcoin regtest stopped"
fi
}
bitcoin_regtest_stop_and_remove() {
_bitcoin_regtest_stop || return "$?"
if [ ! -e "$BITCOIN_DIR" ]; then
echo "bitcoin directory not present"
else
if ! rm -rf "$BITCOIN_DIR"; then
echo "error: removing bitcoin directory $BITCOIN_DIR"
return 1
fi
echo "removed bitcoin directory $BITCOIN_DIR"
fi
}
_pgrep_cln_coord() {
pgrep -f \
"$LIGHTNINGD_BIN --lightning-dir=$CLN_COORD_DIR --regtest --daemon" \
>/dev/null
}
cln_coord_start() {
_bitcoin_regtest_check_started || return "$?"
_create_dir "$CLN_COORD_DIR" || return "$?"
echo "writing cln coordinator config"
cat << EOF > "$CLN_COORD_DIR/config"
bitcoin-cli=$BITCOIN_CLI_BIN
bitcoin-datadir=$BITCOIN_DIR
bitcoin-rpcuser=$BITCOIN_REGTEST_RPC_USER
bitcoin-rpcpassword=$BITCOIN_REGTEST_RPC_PASS
bitcoin-rpcport=$BITCOIN_REGTEST_RPC_PORT
alias=cln-coordinator
rgb=100000
fee-base=0
fee-per-satoshi=0
min-capacity-sat=990000
ignore-fee-limits=true
funding-confirms=1
max-concurrent-htlcs=64
max-dust-htlc-exposure-msat=1000000000
cltv-delta=144
cltv-final=144
bitcoin-retry-timeout=120
database-upgrade=true
log-file=lightning.log
addr=localhost:$CLN_COORD_LISTEN_PORT
grpc-port=$CLN_COORD_GRPC_PORT
important-plugin=$HOLDINVOICE_PLUGIN_BIN
grpc-hold-port=$CLN_COORD_HOLD_PORT
disable-plugin=clnrest
disable-plugin=wss-proxy
EOF
if ! _pgrep_cln_coord; then
echo "starting cln coordinator"
if
! "$LIGHTNINGD_BIN" \
--lightning-dir="$CLN_COORD_DIR" \
--regtest \
--daemon \
>/dev/null
then
echo "error: starting cln coordinator daemon" >&2
return 1
fi
else
echo "cln coordinator already started"
fi
cln_coord() {
"$LIGHTNING_CLI_BIN" \
--lightning-dir="$CLN_COORD_DIR" \
--regtest \
"$@"
}
while [ \
"$(cln_coord getinfo | jq -r '.blockheight')" -ne \
"$(btc_reg getblockcount)" \
]; do
echo "waiting for cln coordinator to sync with the chain..."
sleep 1
done
echo "cln coordinator started, data directory is $CLN_COORD_DIR"
echo "cln_coord function set"
}
_cln_coord_stop() {
if ! _pgrep_cln_coord; then
echo "cln coordinator already stopped"
else
if
! "$LIGHTNING_CLI_BIN" \
--lightning-dir="$CLN_COORD_DIR" \
--regtest \
stop >/dev/null
then
echo "error: cln coordinator not stopped" >&2
fi
while _pgrep_cln_coord; do
echo "waiting for cln coordinator to stop..."
sleep 1
done
echo "cln coordinator stopped"
fi
}
cln_coord_stop_and_remove() {
_cln_coord_stop || return "$?"
if [ ! -e "$CLN_COORD_DIR" ]; then
echo "cln coordinator directory not present"
else
if ! rm -rf "$CLN_COORD_DIR"; then
echo "error: removing cln coordinator directory $CLN_COORD_DIR"
return 1
fi
echo "removed cln coordinator directory $CLN_COORD_DIR"
fi
}
_pgrep_cln_user() {
pgrep -f \
"$LIGHTNINGD_BIN --lightning-dir=$CLN_USER_DIR --regtest --daemon" \
>/dev/null
}
cln_user_start() {
_bitcoin_regtest_check_started || return "$?"
_create_dir "$CLN_USER_DIR" || return "$?"
echo "writing cln user config"
cat << EOF > "$CLN_USER_DIR/config"
bitcoin-cli=$BITCOIN_CLI_BIN
bitcoin-datadir=$BITCOIN_DIR
bitcoin-rpcuser=$BITCOIN_REGTEST_RPC_USER
bitcoin-rpcpassword=$BITCOIN_REGTEST_RPC_PASS
bitcoin-rpcport=$BITCOIN_REGTEST_RPC_PORT
alias=cln-user
rgb=200000
fee-base=0
fee-per-satoshi=0
min-capacity-sat=990000
ignore-fee-limits=true
funding-confirms=1
max-concurrent-htlcs=64
max-dust-htlc-exposure-msat=1000000000
cltv-delta=144
cltv-final=144
bitcoin-retry-timeout=120
database-upgrade=true
log-file=lightning.log
addr=localhost:$CLN_USER_LISTEN_PORT
grpc-port=$CLN_USER_GRPC_PORT
disable-plugin=clnrest
disable-plugin=wss-proxy
EOF
if ! _pgrep_cln_user; then
echo "starting cln user"
if
! "$LIGHTNINGD_BIN" \
--lightning-dir="$CLN_USER_DIR" \
--regtest \
--daemon \
>/dev/null
then
echo "error: starting cln user daemon" >&2
return 1
fi
else
echo "cln user already started"
fi
cln_user() {
"$LIGHTNING_CLI_BIN" \
--lightning-dir="$CLN_USER_DIR" \
--regtest \
"$@"
}
while [ \
"$(cln_user getinfo | jq -r '.blockheight')" -ne \
"$(btc_reg getblockcount)" \
]; do
echo "waiting for cln user to sync with the chain..."
sleep 1
done
echo "cln user started, data directory is $CLN_USER_DIR"
echo "cln_user function set"
}
_cln_user_stop() {
if ! _pgrep_cln_user; then
echo "cln user already stopped"
else
if
! "$LIGHTNING_CLI_BIN" \
--lightning-dir="$CLN_USER_DIR" \
--regtest \
stop >/dev/null
then
echo "error: cln user not stopped" >&2
fi
while _pgrep_cln_user; do
echo "waiting for cln user to stop..."
sleep 1
done
echo "cln user stopped"
fi
}
cln_user_stop_and_remove() {
_cln_user_stop || return "$?"
if [ ! -e "$CLN_USER_DIR" ]; then
echo "cln user directory not present"
else
if ! rm -rf "$CLN_USER_DIR"; then
echo "error: removing cln user directory $CLN_USER_DIR"
return 1
fi
echo "removed cln user directory $CLN_USER_DIR"
fi
}
_pgrep_lnd_coord() {
pgrep -f "$LND_BIN --lnddir=$LND_COORD_DIR --bitcoin.regtest" >/dev/null
}
lnd_coord_start() {
_bitcoin_regtest_check_started || return "$?"
_create_dir "$LND_COORD_DIR" || return "$?"
echo "writing lnd coordinator config"
cat << EOF > "$LND_COORD_DIR/lnd.conf"
[Application Options]
listen=localhost:$LND_COORD_LISTEN_PORT
rpclisten=localhost:$LND_COORD_RPC_PORT
restlisten=localhost:$LND_COORD_REST_PORT
no-rest-tls=true
tlsdisableautofill=true
noseedbackup=true
maxpendingchannels=16
minchansize=20000
alias=lnd-coordinator
color=#300000
[Bitcoin]
bitcoin.node=bitcoind
bitcoin.defaultchanconfs=1
bitcoin.basefee=0
bitcoin.feerate=0
[Bitcoind]
bitcoind.dir=$BITCOIN_DIR
bitcoind.config=$BITCOIN_DIR/bitcoin.conf
bitcoind.rpchost=localhost:$BITCOIN_REGTEST_RPC_PORT
bitcoind.rpcuser=$BITCOIN_REGTEST_RPC_USER
bitcoind.rpcpass=$BITCOIN_REGTEST_RPC_PASS
bitcoind.zmqpubrawblock=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_BLOCK_PORT
bitcoind.zmqpubrawtx=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_TX_PORT
[protocol]
protocol.wumbo-channels=true
EOF
if ! _pgrep_lnd_coord; then
echo "starting lnd coordinator"
"$LND_BIN" \
--lnddir="$LND_COORD_DIR" \
--bitcoin.regtest \
>"$REGTEST_LOGS_DIR/lnd-coord" 2>&1 &
else
echo "lnd coordinator already started"
fi
lnd_coord() {
"$LNCLI_BIN" \
--lnddir="$LND_COORD_DIR" \
--network regtest \
--rpcserver localhost:"$LND_COORD_RPC_PORT" \
"$@"
}
while [ "$(lnd_coord getinfo 2>/dev/null | jq -r '.synced_to_chain')" != true ]; do
echo "waiting for lnd coordinator to sync with the chain..."
sleep 1
done
# while [ "$(lnd_coord getinfo 2>/dev/null | jq -r '.synced_to_graph')" != true ]; do
# echo "waiting for lnd coordinator to sync with the graph..."
# sleep 1
# done
echo "lnd coordinator started, data directory is $LND_COORD_DIR"
echo "lnd_coord function set"
}
_lnd_coord_stop() {
if ! _pgrep_lnd_coord; then
echo "lnd coordinator already stopped"
else
if
! "$LNCLI_BIN" \
--lnddir="$LND_COORD_DIR" \
--network regtest \
--rpcserver localhost:"$LND_COORD_RPC_PORT" \
stop >/dev/null
then
echo "error: lnd coordinator not stopped" >&2
fi
while _pgrep_lnd_coord; do
echo "waiting for lnd coordinator to stop..."
sleep 1
done
echo "lnd coordinator stopped"
fi
}
lnd_coord_stop_and_remove() {
_lnd_coord_stop || return "$?"
if [ ! -e "$LND_COORD_DIR" ]; then
echo "lnd coordinator directory not present"
else
if ! rm -rf "$LND_COORD_DIR"; then
echo "error: removing lnd coordinator directory $LND_COORD_DIR"
return 1
fi
echo "removed lnd coordinator directory $LND_COORD_DIR"
fi
}
_pgrep_lnd_user() {
pgrep -f "$LND_BIN --lnddir=$LND_USER_DIR --bitcoin.regtest" >/dev/null
}
lnd_user_start() {
_bitcoin_regtest_check_started || return "$?"
_create_dir "$LND_USER_DIR" || return "$?"
echo "writing lnd user config"
cat << EOF > "$LND_USER_DIR/lnd.conf"
[Application Options]
listen=localhost:$LND_USER_LISTEN_PORT
rpclisten=localhost:$LND_USER_RPC_PORT
restlisten=localhost:$LND_USER_REST_PORT
no-rest-tls=true
tlsdisableautofill=true
noseedbackup=true
maxpendingchannels=16
minchansize=20000
alias=lnd-user
color=#400000
[Bitcoin]
bitcoin.node=bitcoind
bitcoin.defaultchanconfs=1
bitcoin.basefee=0
bitcoin.feerate=0
[Bitcoind]
bitcoind.dir=$BITCOIN_DIR
bitcoind.config=$BITCOIN_DIR/bitcoin.conf
bitcoind.rpchost=localhost:$BITCOIN_REGTEST_RPC_PORT
bitcoind.rpcuser=$BITCOIN_REGTEST_RPC_USER
bitcoind.rpcpass=$BITCOIN_REGTEST_RPC_PASS
bitcoind.zmqpubrawblock=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_BLOCK_PORT
bitcoind.zmqpubrawtx=tcp://127.0.0.1:$BITCOIN_REGTEST_ZMQ_TX_PORT
[protocol]
protocol.wumbo-channels=true
EOF
if ! _pgrep_lnd_user; then
echo "starting lnd user"
"$LND_BIN" \
--lnddir="$LND_USER_DIR" \
--bitcoin.regtest \
>"$REGTEST_LOGS_DIR/lnd-user" 2>&1 &
else
echo "lnd user already started"
fi
lnd_user() {
"$LNCLI_BIN" \
--lnddir="$LND_USER_DIR" \
--network regtest \
--rpcserver localhost:"$LND_USER_RPC_PORT" \
"$@"
}
while [ "$(lnd_user getinfo 2>/dev/null | jq -r '.synced_to_chain')" != true ]; do
echo "waiting for lnd user to sync with the chain..."
sleep 1
done
# while [ "$(lnd_user getinfo 2>/dev/null | jq -r '.synced_to_graph')" != true ]; do
# echo "waiting for lnd user to sync with the graph..."
# sleep 1
# done
echo "lnd user started, data directory is $LND_USER_DIR"
echo "lnd_user function set"
}
_lnd_user_stop() {
if ! _pgrep_lnd_user; then
echo "lnd user already stopped"
else
if
! "$LNCLI_BIN" \
--lnddir="$LND_USER_DIR" \
--network regtest \
--rpcserver localhost:"$LND_USER_RPC_PORT" \
stop >/dev/null
then
echo "error: lnd user not stopped" >&2
fi
while _pgrep_lnd_user; do
echo "waiting for lnd user to stop..."
sleep 1
done
echo "lnd user stopped"
fi
}
lnd_user_stop_and_remove() {
_lnd_user_stop || return "$?"
if [ ! -e "$LND_USER_DIR" ]; then
echo "lnd user directory not present"
else
if ! rm -rf "$LND_USER_DIR"; then
echo "error: removing lnd user directory $LND_USER_DIR"
return 1
fi
echo "removed lnd user directory $LND_USER_DIR"
fi
}
_mine_blocks_coordinator() {
coord_node="$1"
case "$coord_node" in
cln)
if ! coord_address="$(
cln_coord -k newaddr addresstype=p2tr | jq -r '.p2tr'
)"; then
echo "error: generating cln coordinator address" >&2
return 1
fi
;;
lnd)
if ! coord_address="$(
lnd_coord newaddress p2tr | jq -r '.address'
)"; then
echo "error: generating lnd coordinator address" >&2
return 1
fi
;;
esac
bitcoin_regtest_mine 101 "$coord_address" || return "$?"
unset user_address
}
_robosats_regtest_channel_create_cln_user() {
if [ "$#" -lt 1 ]; then
echo "error: insert coordinator node" >&2
return 1
fi
coord_node="$1"
shift 1
_bitcoin_regtest_check_started || return "$?"
case "$coord_node" in
cln)
if ! _pgrep_cln_coord; then
echo "error: cln coordinator not started" >&2
return 1
fi
if ! _function_exist cln_coord; then
return 1
fi
coord_id="$(cln_coord getinfo | jq -r '.id')"
coord_port="$CLN_COORD_LISTEN_PORT"
;;
lnd)
if ! _pgrep_lnd_coord; then
echo "error: cln coordinator not started" >&2
return 1
fi
if ! _function_exist lnd_coord; then
return 1
fi
coord_id="$(lnd_coord getinfo | jq -r '.identity_pubkey')"
coord_port="$LND_COORD_LISTEN_PORT"
;;
*)
echo "error: coordinator node can only be cln and lnd" >&2
return 1
;;
esac
if ! _pgrep_cln_user; then
echo "error: cln user not started" >&2
return 1
fi
if ! _function_exist cln_user; then
return 1
fi
if ! cln_user connect "$coord_id"@localhost:"$coord_port" >/dev/null; then
echo "error: connection lightning nodes" >&2
return 1
fi
echo "lightning nodes connected"
# check if channel not already present
if [ "$(
cln_user listpeerchannels "$coord_id" |
jq -r '.channels | length'
)" -ge 1 ]; then
echo "lightning nodes already have a channel, not opening a new one"
else
echo "mining blocks to coordinator $coord_node"
_mine_blocks_coordinator "$coord_node" || return "$?"
previous_output_number="$(cln_user listfunds | jq -r '.outputs | length')"
if ! user_address="$(cln_user -k newaddr addresstype=p2tr | jq -r '.p2tr')"; then
echo "error: generating user address" >&2
return 1
fi
echo "mining blocks to user cln"
bitcoin_regtest_mine 101 "$user_address" || return "$?"
unset user_address
while [ \
"$(cln_user listfunds | jq -r '.outputs | length')" -le \
"$previous_output_number" \
]; do
echo "waiting for cln user to see the new blocks..."
sleep 5
done
while [ "$(cln_user listfunds | jq -r '.outputs[0].status')" != "confirmed" ]; do
echo "waiting for cln user funds to mature..."
sleep 1
done
unset previous_output_number
if ! cln_user -k fundchannel \
id="$coord_id" \
amount=1btc \
feerate=10000perkb \
announce=true \
>/dev/null
then
echo "error: funding lightning channel" >&2
return 1
fi
echo "lightning channel created"
bitcoin_regtest_mine 10 "new" || return "$?"
while [ "$(
cln_user listpeerchannels "$coord_id" |
jq -r '.channels | length'
)" -lt 1 ]; do
echo "waiting for channel to confirm..."
sleep 1
done
echo "lightning channel opened"
fi
while [ "$(
cln_user listpeerchannels "$coord_id" |
jq -r '.channels[].state'
)" != "CHANNELD_NORMAL" ]; do
echo "waiting for channel to be active..."
sleep 5
done
echo "lightning channel is active"
unset coord_port
unset coord_id
unset coord_node
}
_robosats_regtest_channel_create_lnd_user() {
if [ "$#" -lt 1 ]; then
echo "error: insert coordinator node" >&2
return 1
fi
coord_node="$1"
shift 1
_bitcoin_regtest_check_started || return "$?"
case "$coord_node" in
cln)
if ! _pgrep_cln_coord; then
echo "error: cln coordinator not started" >&2
return 1
fi
if ! _function_exist cln_coord; then
return 1
fi
coord_id="$(cln_coord getinfo | jq -r '.id')"
coord_port="$CLN_COORD_LISTEN_PORT"
;;
lnd)
if ! _pgrep_lnd_coord; then
echo "error: cln coordinator not started" >&2
return 1
fi
if ! _function_exist lnd_coord; then
return 1
fi
coord_id="$(lnd_coord getinfo | jq -r '.identity_pubkey')"
coord_port="$LND_COORD_LISTEN_PORT"
;;
*)
echo "error: coordinator node can only be cln and lnd" >&2
return 1
;;
esac
if ! _pgrep_lnd_user; then
echo "error: lnd user not started" >&2
return 1
fi
if ! _function_exist lnd_user; then
return 1
fi
already_connected=false
for pub_key in $(lnd_user listpeers | jq -r '.peers[].pub_key'); do
if [ "$pub_key" = "$coord_id" ]; then
already_connected=true
break
fi
done
unset pub_key
if [ "$already_connected" = false ]; then
if ! lnd_user connect "$coord_id"@localhost:"$coord_port" >/dev/null; then
echo "error: connection lightning nodes" >&2
return 1
fi
fi
unset already_connected
echo "lightning nodes connected"
# check if channel not already present
if [ "$(
lnd_user listchannels --peer "$coord_id" |
jq -r '.channels | length'
)" -ge 1 ]; then
echo "lightning nodes already have a channel, not opening a new one"
else
echo "mining blocks to coordinator $coord_node"
_mine_blocks_coordinator "$coord_node" || return "$?"
previous_output_number="$(lnd_user listunspent | jq -r '.utxos | length')"
if ! user_address="$(lnd_user newaddress p2tr | jq -r '.address')"; then
echo "error: generating user address" >&2
return 1
fi
echo "mining blocks to user lnd"
bitcoin_regtest_mine 101 "$user_address" || return "$?"
unset user_address
while [ \
"$(lnd_user listunspent | jq -r '.utxos | length')" -le \
"$previous_output_number" \
]; do
echo "waiting for lnd user to see the new blocks..."
sleep 5
done
while [ "$(lnd_user listunspent | jq -r '.utxos[0].confirmations')" -lt 100 ]; do
echo "waiting for lnd user funds to mature..."
sleep 1
done
unset previous_output_number
if ! lnd_user openchannel \
--node_key "$coord_id" \
--local_amt "100000000" \
--sat_per_vbyte "10" \
--min_confs "1" \
--channel_type "anchors" \
>/dev/null
then
echo "error: funding lightning channel" >&2
return 1
fi
echo "lightning channel created"
bitcoin_regtest_mine 10 "new" || return "$?"
while [ "$(
lnd_user listchannels --peer "$coord_id" |
jq -r '.channels | length'
)" -lt 1 ]; do
echo "waiting for channel to confirm..."
sleep 1
done
echo "lightning channel opened"
fi
while [ "$(
lnd_user listchannels --peer "$coord_id" |
jq -r '.channels[].active'
)" != true ]; do
echo "waiting for channel to be active..."
sleep 5
done
echo "lightning channel is active"
unset coord_port
unset coord_id
unset coord_node
}
robosats_regtest_channel_create() {
if [ "$#" -lt 1 ]; then
echo "error: insert coordinator node" >&2
return 1
fi
coord_node="$1"
shift 1
if [ "$#" -lt 1 ]; then
echo "error: insert user node" >&2
return 1
fi
user_node="$1"
shift 1
case "$coord_node" in
cln|lnd) ;;
*)
echo "error: $coord_node should be cln or lnd" >&2
return 1
;;
esac
case "$user_node" in
cln)
_robosats_regtest_channel_create_cln_user "$coord_node"
;;
lnd)
_robosats_regtest_channel_create_lnd_user "$coord_node"
;;
*)
echo "error: $user_node should be cln or lnd" >&2
return 1
;;
esac
unset user_node
unset coord_node
}
roboauto_regtest_setup() {
if [ "$ROBOAUTO_GIT_DIR" = false ]; then
echo "error: roboauto is disable, set ROBOAUTO_GIT_DIR in .env to activate it" >&2
return 1
fi
_command_exist roboauto || return "$?"
_create_dir "$ROBOAUTO_DIR" || return "$?"
ra_reg() {
roboauto \
--config-dir "$REGTEST_NODES_DIR/roboauto" \
--data-dir "$REGTEST_NODES_DIR/roboauto" \
"$@"
}
echo "writing roboauto regtest config"
cat << EOF > "$ROBOAUTO_DIR/config.ini"
[federation]
exp = None
sau = None
tos = None
tbl = None
bve = None
loc = "http://127.0.0.1:$RUNSERVER_PORT"
EOF
case "$LNVENDOR_USER" in
cln)
roboauto_cln_script="$ROBOAUTO_GIT_DIR/data/lightning-node-core-lightning"
if [ ! -f "$roboauto_cln_script" ]; then
echo "error: roboauto cln script not found in $ROBOAUTO_GIT_DIR" >&2
return 1
fi
search_data=$(cat << EOF
lightning-cli "\$@"
EOF
)
new_data=$(cat << EOF
"$LIGHTNING_CLI_BIN" \
--lightning-dir="$CLN_USER_DIR" \
--regtest \
"\$@"
EOF
)
sed "s|$search_data|$new_data|" \
"$roboauto_cln_script" > "$ROBOAUTO_DIR/lightning-node" || \
return "$?"
echo "roboauto cln node script set up"
;;
lnd)
roboauto_lnd_script="$ROBOAUTO_GIT_DIR/data/lightning-node-lnd"
if [ ! -f "$roboauto_lnd_script" ]; then
echo "error: roboauto lnd script not found in $ROBOAUTO_GIT_DIR" >&2
return 1
fi
search_data=$(cat << EOF
lncli "\$@"
EOF
)
new_data=$(cat << EOF
"$LNCLI_BIN" \
--lnddir="$LND_USER_DIR" \
--network regtest \
--rpcserver localhost:"$LND_USER_RPC_PORT" \
"\$@"
EOF
)
sed "s|$search_data|$new_data|" \
"$roboauto_lnd_script" > "$ROBOAUTO_DIR/lightning-node" || \
return "$?"
echo "roboauto lnd node script set up"
;;
esac
if ! chmod u+x "$ROBOAUTO_DIR/lightning-node"; then
echo "error: changing file mode on roboauto lightning-node" >&2
return 1
fi
}
roboauto_regtest_remove() {
if ! rm -rf "$ROBOAUTO_DIR"; then
echo "error: removing roboauto directory $ROBOAUTO_DIR" >&2
return 1
fi
}
robosats_regtest_stop_all() {
_lnd_user_stop
_lnd_coord_stop
_cln_user_stop
_cln_coord_stop
_bitcoin_regtest_stop
}
robosats_regtest_stop_and_remove_all() {
roboauto_regtest_remove
lnd_user_stop_and_remove
lnd_coord_stop_and_remove
cln_user_stop_and_remove
cln_coord_stop_and_remove
bitcoin_regtest_stop_and_remove
}
_node_test_setup() {
bitcoin_regtest_start || return "$?"
bitcoin_regtest_mine 1 "new" || return "$?"
printf "\n"
case "$LNVENDOR_COORD" in
cln)
cln_coord_start || return "$?"
;;
lnd)
lnd_coord_start || return "$?"
;;
esac
printf "\n"
case "$LNVENDOR_USER" in
cln)
cln_user_start || return "$?"
;;
lnd)
lnd_user_start || return "$?"
;;
esac
}
_node_server_setup() {
_node_test_setup || return "$?"
printf "\n"
robosats_regtest_channel_create \
"$LNVENDOR_COORD" "$LNVENDOR_USER" || return "$?"
if [ "$ROBOAUTO_GIT_DIR" != false ]; then
printf "\n"
roboauto_regtest_setup
fi
}
_robosats_regtest_info_print() {
cat << EOF
regtest nodes directory is $REGTEST_NODES_DIR
available command:
bitcoin_regtest_start
bitcoin_regtest_stop_and_remove
bitcoin_regtest_mine number-of-blocks <address>|new
cln_coord_start
cln_coord_stop_and_remove
cln_user_start
cln_user_stop_and_remove
lnd_coord_start
lnd_coord_stop_and_remove
lnd_user_start
lnd_user_stop_and_remove
roboauto_regtest_setup
roboauto_regtest_remove
robosats_regtest_stop_all
robosats_regtest_stop_and_remove_all
robosats_regtest_channel_create cln|lnd cln|lnd
EOF
}
_nodes_main() {
_nodes_environment_set || return "$?"
# if shell is bash and script is sourced source also bash completions
if [ -n "$BASH_VERSION" ]; then
case "$0" in
/*|./*|../*) ;;
*)
if [ -f "scripts/traditional/robosats.bash-completion" ]; then
# shellcheck disable=SC1091
. scripts/traditional/robosats.bash-completion
fi
if
[ "$ROBOAUTO_GIT_DIR" != false ] &&
[ -f "$ROBOAUTO_GIT_DIR/completions/roboauto.bash-completion" ]
then
# shellcheck disable=SC1091
# shellcheck disable=SC3044
. "$ROBOAUTO_GIT_DIR/completions/roboauto.bash-completion" &&
complete -F __roboauto_completion ra_reg
fi
;;
esac
fi
if [ "$#" -lt 1 ]; then
_robosats_regtest_info_print || return "$?"
else
case "$1" in
-h|--help)
cat << EOF
regtest-nodes [server|test]
EOF
return 0
;;
test)
_robosats_regtest_info_print || return "$?"
printf "\n"
_node_test_setup || return "$?"
;;
server)
_robosats_regtest_info_print || return "$?"
printf "\n"
_node_server_setup || return "$?"
;;
*)
echo "error: action $1 not recognized" >&2
return 1
;;
esac
fi
}
_nodes_main "$@"