-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add script to setup two gl nodes with one local CLN node
- Loading branch information
1 parent
4aac888
commit e1afd63
Showing
3 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
const path = require('path'); | ||
const axios = require('axios'); | ||
const protobuf = require('protobufjs'); | ||
|
||
async function encodePayload(clnNode, method, payload) { | ||
const methodRequest = clnNode.lookupType(`cln.${method}Request`); | ||
const errMsg = methodRequest.verify(payload); | ||
if (errMsg) throw new Error(errMsg); | ||
const requestPayload = methodRequest.create(payload); | ||
const encodedPayload = methodRequest.encode(requestPayload).finish(); | ||
const flags = Buffer.alloc(1); | ||
flags.writeUInt8(0, 0); | ||
const header = Buffer.alloc(4); | ||
header.writeUInt32BE(encodedPayload.length, 0); | ||
return Buffer.concat([flags, header, encodedPayload]); | ||
} | ||
|
||
async function sendRequest(port, authPubkey, methodUrl, encodedPayload) { | ||
const buffer = Buffer.alloc(8); | ||
buffer.writeUInt32BE(Math.floor(Date.now() / 1000), 4); | ||
const axiosConfig = { | ||
responseType: 'arraybuffer', | ||
headers: { | ||
'content-type': 'application/grpc', | ||
'accept': 'application/grpc', | ||
'glauthpubkey': Buffer.from(authPubkey, 'hex').toString('base64'), | ||
'glauthsig': 'A'.repeat(64), | ||
'glts': buffer.toString('base64'), | ||
}, | ||
}; | ||
return await axios.post(`http://localhost:${port}/cln.Node/${methodUrl}`, encodedPayload, axiosConfig); | ||
} | ||
|
||
function transformValue(key, value) { | ||
if ((value.type && value.type === "Buffer") || value instanceof Buffer || value instanceof Uint8Array) { | ||
return Buffer.from(value).toString('hex'); | ||
} | ||
if (value.msat && !Number.isNaN(parseInt(value.msat))) { | ||
return parseInt(value.msat); | ||
} | ||
return value; | ||
} | ||
|
||
function decodeResponse(clnNode, method, response) { | ||
const methodResponse = clnNode.lookupType(`cln.${method}Response`); | ||
const dataBuffer = Buffer.from(response.data); | ||
const resFlag = dataBuffer.subarray(0, 1); | ||
const resDataLength = dataBuffer.subarray(1, 5); | ||
const responseData = dataBuffer.subarray(5); | ||
const grpcStatus = +response.headers['grpc-status']; | ||
if (grpcStatus !== 0) { | ||
let errorMessage = 'None'; | ||
try { | ||
errorMessage = decodeURIComponent(new TextDecoder('utf-8').decode(responseData)).trim(); | ||
if (errorMessage == 'None') { | ||
errorMessage = grpcStatus; | ||
} | ||
} catch (decodeError) { | ||
errorMessage = decodeError; | ||
} | ||
throw new Error(errorMessage); | ||
} | ||
const decodedRes = methodResponse.decode(responseData); | ||
const decodedResObject = methodResponse.toObject(decodedRes, { | ||
longs: String, | ||
enums: String, | ||
bytes: Buffer, | ||
defaults: true, | ||
arrays: true, | ||
objects: true, | ||
}); | ||
return JSON.parse(JSON.stringify(decodedResObject, transformValue)); | ||
} | ||
|
||
async function callMethod(port, auth_pubkey, clnNode, method, methodReq, methodRes, methodPayload) { | ||
await new Promise(resolve => setTimeout(resolve, 1000)); | ||
const encodedPayload = await encodePayload(clnNode, methodReq, methodPayload); | ||
try { | ||
const response = await sendRequest(port, auth_pubkey, method, encodedPayload); | ||
const responseJSON = decodeResponse(clnNode, methodRes, response); | ||
console.log(JSON.stringify(responseJSON, null, 2)); | ||
} catch (error) { | ||
throw error; | ||
} | ||
} | ||
|
||
async function main() { | ||
try { | ||
const args = process.argv.slice(2); | ||
const port = args[0]; | ||
const auth_pubkey = args[1]; | ||
const method = args[2]; | ||
const methodReq = args[3]; | ||
const methodRes = args[4]; | ||
const methodPayload = JSON.parse(args[5]); | ||
const proto_path = [ | ||
path.join(process.cwd(), '../../libs/gl-client/.resources/proto/node.proto'), | ||
path.join(process.cwd(), '../../libs/gl-client/.resources/proto/primitives.proto') | ||
]; | ||
const clnNode = new protobuf.Root().loadSync(proto_path, { keepCase: true }); | ||
await callMethod(port, auth_pubkey, clnNode, method, methodReq, methodRes, methodPayload); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
} | ||
|
||
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
#!/bin/bash | ||
# shellcheck disable=SC2317 | ||
|
||
# Set AUTO_START to the first argument, default to "yes" if not provided | ||
AUTO_START=${1:-"start"} | ||
|
||
# Read environment variables from metadata.json | ||
GL_SERVER_DATA_PATH="$HOME/workspace/greenlight/.gltestserver" | ||
GL_CA_CRT=$(jq -r '.ca_crt_path' ./metadata.json) | ||
GL_NOBODY_CRT=$(jq -r '.nobody_crt_path' ./metadata.json) | ||
GL_NOBODY_KEY=$(jq -r '.nobody_key_path' ./metadata.json) | ||
GL_SCHEDULER_GRPC_URI=$(jq -r '.scheduler_grpc_uri' ./metadata.json) | ||
GL_BITCOIN_RPC_URI=$(jq -r '.bitcoind_rpc_uri' ./metadata.json) | ||
GL_GRPC_WEB_PROXY_URI=$(jq -r '.grpc_web_proxy_uri' ./metadata.json) | ||
GL_GRPC_PORT=$(echo "$GL_GRPC_WEB_PROXY_URI" | sed -E 's/.*:([0-9]+)$/\1/') | ||
LSP_LIGHTNING_DIR="/tmp/.lightning" | ||
|
||
# Extract values using parameter expansion and regular expressions | ||
RPC_USER=$(echo "$GL_BITCOIN_RPC_URI" | sed -E 's|^http://([^:]+):([^@]+)@([^:]+):([0-9]+)$|\1|') | ||
RPC_PASS=$(echo "$GL_BITCOIN_RPC_URI" | sed -E 's|^http://([^:]+):([^@]+)@([^:]+):([0-9]+)$|\2|') | ||
BITCOIN_HOST=$(echo "$GL_BITCOIN_RPC_URI" | sed -E 's|^http://([^:]+):([^@]+)@([^:]+):([0-9]+)$|\3|') | ||
BITCOIN_PORT=$(echo "$GL_BITCOIN_RPC_URI" | sed -E 's|^http://([^:]+):([^@]+)@([^:]+):([0-9]+)$|\4|') | ||
WALLET_NAME="testwallet" | ||
NODE_PUBKEY_1="0279da9a93e50b008a7ba6bd25355fb7132f5015b790a05ee9f41bc9fbdeb30d19" | ||
NODE_PUBKEY_2="036fc50c7183d47baf1b80f312f1ea53b25c117183456b788c05f91b3b4507c8d3" | ||
LSP_NODE_PUBKEY="0208d70da220f05b859512fd805365af6a6b8583fa3ff10fbd00b6b7fbb29acd32" | ||
|
||
# Function to handle Ctrl+C | ||
trap 'echo "Stopping terminals"; pkill -f "gnome-terminal"; exit' SIGINT | ||
|
||
# Function to print environment variables | ||
print_envs() { | ||
echo "GL_SERVER_DATA_PATH=$GL_SERVER_DATA_PATH" | ||
echo "GL_CA_CRT=$GL_CA_CRT" | ||
echo "GL_NOBODY_CRT=$GL_NOBODY_CRT" | ||
echo "GL_NOBODY_KEY=$GL_NOBODY_KEY" | ||
echo "GL_SCHEDULER_GRPC_URI=$GL_SCHEDULER_GRPC_URI" | ||
echo "GL_BITCOIN_RPC_URI=$GL_BITCOIN_RPC_URI" | ||
echo "GL_GRPC_WEB_PROXY_URI=$GL_GRPC_WEB_PROXY_URI" | ||
echo "GL_GRPC_PORT=$GL_GRPC_PORT" | ||
echo "LSP_LIGHTNING_DIR=$LSP_LIGHTNING_DIR" | ||
echo "RPC_USER=$RPC_USER" | ||
echo "RPC_PASS=$RPC_PASS" | ||
echo "BITCOIN_HOST=$BITCOIN_HOST" | ||
echo "BITCOIN_PORT=$BITCOIN_PORT" | ||
echo "WALLET_NAME=testwallet" | ||
echo "NODE_PUBKEY_1=0279da9a93e50b008a7ba6bd25355fb7132f5015b790a05ee9f41bc9fbdeb30d19" | ||
echo "NODE_PUBKEY_2=036fc50c7183d47baf1b80f312f1ea53b25c117183456b788c05f91b3b4507c8d3" | ||
echo "LSP_NODE_PUBKEY=0208d70da220f05b859512fd805365af6a6b8583fa3ff10fbd00b6b7fbb29acd32" | ||
} | ||
|
||
# Function to register and schedule a node | ||
run_scheduler_for_gl_node() { | ||
local node="$1" | ||
local hsm_secret="$2" | ||
if [ -z "$node" ] || [ -z "$hsm_secret" ]; then | ||
echo "Forgot params node/hsm_secret? Exiting..." | ||
else | ||
echo "Scheduler for $node" | ||
rm -rf "${GL_SERVER_DATA_PATH}/${node}" && mkdir -p "${GL_SERVER_DATA_PATH}/${node}" | ||
printf '%b' "$hsm_secret" > "${GL_SERVER_DATA_PATH}/${node}/hsm_secret" | ||
gnome-terminal --title="Scheduler $node" -- bash -c \ | ||
"GL_CA_CRT=$GL_CA_CRT \ | ||
GL_NOBODY_CRT=$GL_NOBODY_CRT \ | ||
GL_NOBODY_KEY=$GL_NOBODY_KEY \ | ||
GL_SCHEDULER_GRPC_URI=$GL_SCHEDULER_GRPC_URI \ | ||
cargo run --bin glcli scheduler register \ | ||
--network=regtest \ | ||
--data-dir=${GL_SERVER_DATA_PATH}/${node} && \ | ||
GL_CA_CRT=$GL_CA_CRT \ | ||
GL_NOBODY_CRT=$GL_NOBODY_CRT \ | ||
GL_NOBODY_KEY=$GL_NOBODY_KEY \ | ||
GL_SCHEDULER_GRPC_URI=$GL_SCHEDULER_GRPC_URI \ | ||
cargo run --bin glcli scheduler schedule \ | ||
--verbose \ | ||
--network=regtest \ | ||
--data-dir=${GL_SERVER_DATA_PATH}/${node}; \ | ||
echo 'Press Enter to close this terminal...'; \ | ||
read" | ||
fi | ||
} | ||
|
||
# Function to start the signer for a node | ||
run_signer_for_gl_node() { | ||
local node="$1" | ||
if [ -z "$node" ]; then | ||
echo "Forgot param node? Exiting..." | ||
else | ||
echo "Signer for $node" | ||
gnome-terminal --title="Signer $node" -- bash -c \ | ||
"GL_CA_CRT=$GL_CA_CRT \ | ||
GL_NOBODY_CRT=$GL_NOBODY_CRT \ | ||
GL_NOBODY_KEY=$GL_NOBODY_KEY \ | ||
GL_SCHEDULER_GRPC_URI=$GL_SCHEDULER_GRPC_URI \ | ||
cargo run --bin glcli signer run --verbose --network=regtest --data-dir=${GL_SERVER_DATA_PATH}/${node}; \ | ||
echo 'Press Enter to close this terminal...'; \ | ||
read" | ||
fi | ||
} | ||
|
||
# Function to start the local CLN node | ||
run_local_lsp_node() { | ||
local node="$1" | ||
local hsm_secret="$2" | ||
if [ -z "$node" ] || [ -z "$hsm_secret" ]; then | ||
echo "Forgot params node/hsm_secret? Exiting..." | ||
else | ||
echo "Running $node node" | ||
rm -rf $LSP_LIGHTNING_DIR && mkdir -p "$LSP_LIGHTNING_DIR/regtest" | ||
printf '%b' "$hsm_secret" > "$LSP_LIGHTNING_DIR/regtest/hsm_secret" | ||
gnome-terminal --title="Local LSP node" -- bash -c \ | ||
"lightningd --network=regtest --log-level=debug --lightning-dir=$LSP_LIGHTNING_DIR --bitcoin-rpcconnect=$BITCOIN_HOST --bitcoin-rpcport=$BITCOIN_PORT --bitcoin-datadir=$GL_SERVER_DATA_PATH/gl-testserver --bitcoin-rpcuser=$RPC_USER --bitcoin-rpcpassword=$RPC_PASS --alias=CLNLocalLSPNode --bind-addr=0.0.0.0:9735 --bind-addr=ws:0.0.0.0:5019 --announce-addr=0.0.0.0:9735 --addr=localhost:7171 --grpc-port=9737 --clnrest-port=3010 --clnrest-protocol=http; \ | ||
echo 'Press Enter to close this terminal...'; \ | ||
read" | ||
fi | ||
} | ||
|
||
connect_and_fund_channels_from_gl_nodes() { | ||
cd "./examples/javascript" || exit | ||
CONNECT_PAYLOAD='{"id": "'"$LSP_NODE_PUBKEY"'", "host": "127.0.0.1", "port": 9735}' | ||
echo "gl1 + LSP" | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_1" "ConnectPeer" "Connect" "Connect" "$CONNECT_PAYLOAD" | ||
echo "gl2 + LSP" | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_2" "ConnectPeer" "Connect" "Connect" "$CONNECT_PAYLOAD" | ||
BUFFER_ID=$(echo -n "$LSP_NODE_PUBKEY" | xxd -r -p | base64) | ||
FUND_PAYLOAD='{"id": "'"$BUFFER_ID"'", "amount": {"amount": {"msat": 11000000000}}}' | ||
echo "gl1 -> LSP" | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_1" "FundChannel" "Fundchannel" "Fundchannel" "$FUND_PAYLOAD" | ||
echo "gl2 -> LSP" | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_2" "FundChannel" "Fundchannel" "Fundchannel" "$FUND_PAYLOAD" | ||
cd "../.." || exit | ||
mine_blocks 6 | ||
} | ||
|
||
fund_channels_from_local_node() { | ||
echo "LSP -> gl1" | ||
lightning-cli --network=regtest --lightning-dir="$LSP_LIGHTNING_DIR" fundchannel "$NODE_PUBKEY_1" 12000000 | ||
mine_blocks 6 | ||
|
||
echo "LSP -> gl2" | ||
lightning-cli --network=regtest --lightning-dir="$LSP_LIGHTNING_DIR" fundchannel "$NODE_PUBKEY_2" 12000000 | ||
mine_blocks 6 | ||
} | ||
|
||
create_invoice_and_pay() { | ||
cd "./examples/javascript" || exit | ||
echo "Create invoice 1" | ||
INVOICE_1_PAYLOAD=$(jq -nc --arg msat "120000000" --arg desc "Learning Bitcoin Book" --arg label "bookinvat$(date +%s)" \ | ||
'{"amount_msat": {"amount": {"msat": ($msat | tonumber)}}, "description": $desc, "label": $label}') | ||
INVOICE_1_RESPONSE=$(node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_1" "Invoice" "Invoice" "Invoice" "$INVOICE_1_PAYLOAD") | ||
BOLT11_1=$(echo "$INVOICE_1_RESPONSE" | jq -r '.bolt11') | ||
echo "Invoice 1: $BOLT11_1" | ||
echo "Create invoice 2" | ||
INVOICE_2_PAYLOAD=$(jq -nc --arg msat "5000000" --arg desc "My coffee" --arg label "coffeeinvat$(date +%s)" \ | ||
'{"amount_msat": {"amount": {"msat": ($msat | tonumber)}}, "description": $desc, "label": $label}') | ||
INVOICE_2_RESPONSE=$(node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_2" "Invoice" "Invoice" "Invoice" "$INVOICE_2_PAYLOAD") | ||
BOLT11_2=$(echo "$INVOICE_2_RESPONSE" | jq -r '.bolt11') | ||
echo "Invoice 2: $BOLT11_2" | ||
echo "Pay invoice 1" | ||
PAY_PAYLOAD_1='{"bolt11": "'"$BOLT11_1"'"}' | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_2" "Pay" "Pay" "Pay" "$PAY_PAYLOAD_1" | ||
echo "Pay invoice 2" | ||
PAY_PAYLOAD_2='{"bolt11": "'"$BOLT11_2"'"}' | ||
node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_1" "Pay" "Pay" "Pay" "$PAY_PAYLOAD_2" | ||
cd "../.." || exit | ||
} | ||
|
||
start_nodes() { | ||
run_scheduler_for_gl_node "gl1" '\x0f\x29\xa7\x60\xea\xa1\xa3\xba\xbb\x7b\xa2\x5b\x7a\x82\xd1\x0b\x38\x55\xb9\xd2\xc6\x39\xb2\x79\xa9\xa0\x8d\x9b\xd1\xe6\x67\x9d' | ||
run_signer_for_gl_node "gl1" | ||
|
||
run_scheduler_for_gl_node "gl2" '\x5d\x74\x25\x7e\x03\xb0\xee\x2f\xf4\x7a\x06\x97\x3a\xd9\x14\xf1\x7c\x61\xec\xac\x20\xb1\xf9\x3b\xb2\x33\x98\xc9\x40\x86\xad\x67' | ||
run_signer_for_gl_node "gl2" | ||
|
||
run_local_lsp_node "local1" '\x9f\xaa\x67\x08\x67\x05\x8c\x9d\xcc\x1f\xea\xd9\x9c\xe4\x91\xe2\x85\x95\x6d\xdb\x66\xa8\xed\x05\x85\xf3\x2a\x77\x0e\x1d\x14\xa6' | ||
} | ||
|
||
wait_for_local_funds_confirmation() { | ||
local is_confirmed=false | ||
while ! $is_confirmed; do | ||
if lightning-cli --network=regtest --lightning-dir="$LSP_LIGHTNING_DIR" listfunds | jq -e '.outputs[] | select(.status == "confirmed" and .reserved == false)' > /dev/null; then | ||
is_confirmed=true | ||
else | ||
echo "Waiting for funds to confirm on LSP..." | ||
sleep 1 | ||
fi | ||
done | ||
} | ||
|
||
wait_for_gl_funds_confirmation() { | ||
cd "./examples/javascript" || exit | ||
while ! node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_1" "ListFunds" "Listfunds" "Listfunds" "{}" | grep -q "outputs"; do | ||
echo "Waiting for funds to confirm on gl node 1..." | ||
sleep 1 | ||
done | ||
while ! node node-operations.js "$GL_GRPC_PORT" "$NODE_PUBKEY_2" "ListFunds" "Listfunds" "Listfunds" "{}" | grep -q "outputs"; do | ||
echo "Waiting for funds to confirm on gl node 2..." | ||
sleep 1 | ||
done | ||
cd "../.." || exit | ||
} | ||
|
||
mine_blocks() { | ||
local number_of_blocks=${1:-"6"} | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" -generate "$number_of_blocks" > /dev/null | ||
wait_for_gl_funds_confirmation | ||
wait_for_local_funds_confirmation | ||
} | ||
|
||
fund_nodes() { | ||
rm -rf "${GL_SERVER_DATA_PATH}/gl-testserver/regtest/${WALLET_NAME}" | ||
echo "Creating bitcoin wallet" | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" createwallet "$WALLET_NAME" > /dev/null | ||
echo "Generating 101 blocks" | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" -generate 101 > /dev/null | ||
echo "Sending bitcoin to local node" | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" sendtoaddress "bcrt1qaew9v5m5q8cjjuh9mdruuujykzxrcufma28x82" 1 > /dev/null | ||
echo "Sending bitcoin to gl1" | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" sendtoaddress "bcrt1q0mlp2u676wv9rgz5e6nrrq2vc76rllxcazcldy" 1 > /dev/null | ||
echo "Sending bitcoin to gl2" | ||
bitcoin-cli -rpcconnect="$BITCOIN_HOST" -rpcport="$BITCOIN_PORT" -rpcuser="$RPC_USER" -rpcpassword="$RPC_PASS" -rpcwallet="$WALLET_NAME" sendtoaddress "bcrt1q68n5mqlkf0l877chhpa2w2zxlug343kn0p8c6r" 1 > /dev/null | ||
mine_blocks 1 | ||
} | ||
|
||
if [ "$AUTO_START" = "start" ]; then | ||
print_envs | ||
start_nodes | ||
fund_nodes | ||
connect_and_fund_channels_from_gl_nodes | ||
fund_channels_from_local_node | ||
create_invoice_and_pay | ||
else | ||
echo "List of functions: " | ||
echo "print_envs: Prints all environment variables used in the script." | ||
echo "start_nodes: Starts all nodes (GL nodes and the local CLN node)." | ||
echo "fund_nodes: Creates a Bitcoin wallet, generates blocks, and sends funds to nodes." | ||
echo "connect_and_fund_channels_from_gl_nodes: Connects and funds channels between Greenlight (GL) nodes and the local CLN node." | ||
echo "fund_channels_from_local_node: Funds channels from the local CLN node to GL nodes." | ||
echo "create_invoice_and_pay: Creates invoices and simulates payments between nodes." | ||
echo "run_scheduler_for_gl_node: Registers and schedules a Greenlight (GL) node." | ||
echo "run_signer_for_gl_node: Starts the signer for a Greenlight (GL) node." | ||
echo "run_local_lsp_node: Starts the local Core Lightning node." | ||
echo "wait_for_local_funds_confirmation: Waits for funds to be confirmed on the local CLN node." | ||
echo "wait_for_gl_funds_confirmation: Waits for funds to be confirmed on Greenlight (GL) nodes." | ||
echo "mine_blocks: Mines a specified number of blocks and waits for funds to be confirmed." | ||
echo 'lightning-cli --network=regtest --lightning-dir="$LSP_LIGHTNING_DIR" listfunds' | ||
echo 'cd "./examples/javascript" && node node-operations.js $GL_GRPC_PORT $NODE_PUBKEY_1 "ListFunds" "Listfunds" "Listfunds" "{}" && cd "../.."' | ||
fi | ||
|
||
# Keep the script running to listen for Ctrl+C | ||
wait |