diff --git a/core/api/test/bats/cron.bats b/bats/core/api/cron.bats similarity index 85% rename from core/api/test/bats/cron.bats rename to bats/core/api/cron.bats index 49d3a1c9c3..27e2f45140 100644 --- a/core/api/test/bats/cron.bats +++ b/bats/core/api/cron.bats @@ -1,24 +1,16 @@ #!/usr/bin/env bats -load "helpers/setup-and-teardown" -load "helpers/ln" +load "../../helpers/_common.bash" +load "../../helpers/cli.bash" +load "../../helpers/ledger.bash" +load "../../helpers/ln.bash" +load "../../helpers/user.bash" + setup_file() { clear_cache - bitcoind_init - start_trigger - start_server - start_exporter - - lnds_init - initialize_user_from_onchain "$ALICE_TOKEN_NAME" "$ALICE_PHONE" "$CODE" -} - -teardown_file() { - stop_trigger - stop_server - stop_exporter + create_user 'alice' } teardown() { @@ -27,12 +19,12 @@ teardown() { fi } -no_pending_lnd1_channels() { - pending_channel="$(lnd_cli pendingchannels | jq -r '.pending_open_channels[0]')" - if [[ "$pending_channel" != "null" ]]; then - bitcoin_cli -generate 6 - exit 1 - fi +teardown_file() { + ./dev/bin/init-lightning.sh +} + +run_cron() { + buck2 run //core/api:dev-cron > .e2e-cron.log } wait_for_bria_hot_balance_at_least() { @@ -43,12 +35,30 @@ wait_for_bria_hot_balance_at_least() { | jq -r '.effectiveSettled' ) - [[ "$amount" -lt "$bria_settled_hot_balance" ]] || return 1 + [[ "$amount" -le "$bria_settled_hot_balance" ]] || return 1 +} + +mempool_not_empty() { + local txid="$(bitcoin_cli getrawmempool | jq -r ".[0]")" + [[ "$txid" != "null" ]] || exit 1 +} + +no_pending_lnd1_channels() { + pending_channel="$(lnd_cli pendingchannels | jq -r '.pending_open_channels[0]')" + if [[ "$pending_channel" != "null" ]]; then + bitcoin_cli -generate 6 + exit 1 + fi +} + +synced_to_graph() { + lnd_cli_value="$1" + is_synced="$(run_with_lnd $lnd_cli_value getinfo | jq -r '.synced_to_graph')" + [[ "$is_synced" == "true" ]] || return 1 } @test "cron: rebalance hot to cold storage" { - login_user "$ALICE_TOKEN_NAME" "$ALICE_PHONE" "$CODE" - token_name="$ALICE_TOKEN_NAME" + token_name='alice' btc_wallet_name="$token_name.btc_wallet_id" # Create address @@ -67,7 +77,7 @@ wait_for_bria_hot_balance_at_least() { local key1="tpubDEaDfeS1EXpqLVASNCW7qAHW1TFPBpk2Z39gUXjFnsfctomZ7N8iDpy6RuGwqdXAAZ5sr5kQZrxyuEn15tqPJjM4mcPSuXzV27AWRD3p9Q4" local key2="tpubDEPCxBfMFRNdfJaUeoTmepLJ6ZQmeTiU1Sko2sdx1R3tmPpZemRUjdAHqtmLfaVrBg1NBx2Yx3cVrsZ2FTyBuhiH9mPSL5ozkaTh1iZUTZx" - + bria_cli import-xpub -x "${key1}" -n cold-key1 -d m/48h/1h/0h/2h || true bria_cli import-xpub -x "${key2}" -n cold-key2 -d m/48h/1h/0h/2h || true bria_cli create-wallet -n cold sorted-multisig -x cold-key1 cold-key2 -t 1 || true @@ -99,11 +109,8 @@ wait_for_bria_hot_balance_at_least() { } @test "cron: rebalance internal channels" { - # NOTE: Not an idempotent test because we haven't implemented accounting for - # closing channels initiated from internal lnds as yet. - # Get onchain funds into lnd1 - token_name="$ALICE_TOKEN_NAME" + token_name='alice' btc_wallet_name="$token_name.btc_wallet_id" variables=$( @@ -133,6 +140,8 @@ wait_for_bria_hot_balance_at_least() { local local_amount="500000" lnd2_local_pubkey="$(lnd2_cli getinfo | jq -r '.identity_pubkey')" lnd_cli connect "${lnd2_local_pubkey}@${COMPOSE_PROJECT_NAME}-lnd2-1:9735" || true + retry 10 1 synced_to_graph lnd_cli + retry 5 1 synced_to_graph lnd2_cli opened=$( lnd_cli openchannel \ --node_key "$lnd2_local_pubkey" \ diff --git a/bats/helpers/cli.bash b/bats/helpers/cli.bash index 28032c6ca9..cb97756d5a 100644 --- a/bats/helpers/cli.bash +++ b/bats/helpers/cli.bash @@ -12,6 +12,14 @@ lnd_cli() { $@ } +lnd2_cli() { + docker exec "${COMPOSE_PROJECT_NAME}-lnd2-1" \ + lncli \ + --macaroonpath /root/.lnd/admin.macaroon \ + --tlscertpath /root/.lnd/tls.cert \ + $@ +} + lnd_outside_cli() { docker exec "${COMPOSE_PROJECT_NAME}-lnd-outside-1-1" \ lncli \ diff --git a/bats/helpers/ln.bash b/bats/helpers/ln.bash index e03ab72df5..213893d1c2 100644 --- a/bats/helpers/ln.bash +++ b/bats/helpers/ln.bash @@ -138,6 +138,8 @@ run_with_lnd() { if [[ "$func_name" == "lnd_cli" ]]; then lnd_cli "$@" + elif [[ "$func_name" == "lnd2_cli" ]]; then + lnd2_cli "$@" elif [[ "$func_name" == "lnd_outside_cli" ]]; then lnd_outside_cli "$@" elif [[ "$func_name" == "lnd_outside_2_cli" ]]; then @@ -147,6 +149,37 @@ run_with_lnd() { fi } +close_partner_initiated_channels_with_external() { + close_channels_with_external() { + lnd_cli_value="$1" + lnd1_pubkey=$(lnd_cli getinfo | jq -r '.identity_pubkey') + lnd2_pubkey=$(lnd2_cli getinfo | jq -r '.identity_pubkey') + + partner_initiated_external_channel_filter=' + .channels[]? + | select(.initiator != true) + | select(.remote_pubkey != $lnd1_pubkey) + | select(.remote_pubkey != $lnd2_pubkey) + | .channel_point + ' + + run_with_lnd "$lnd_cli_value" listchannels \ + | jq -r \ + --arg lnd1_pubkey "$lnd1_pubkey" \ + --arg lnd2_pubkey "$lnd2_pubkey" \ + "$partner_initiated_external_channel_filter" \ + | while read -r channel_point; do + funding_txid="${channel_point%%:*}" + run_with_lnd "$lnd_cli_value" closechannel "$funding_txid" + done + } + + close_channels_with_external lnd_cli + close_channels_with_external lnd2_cli + close_channels_with_external lnd_outside_cli + close_channels_with_external lnd_outside_2_cli +} + rebalance_channel() { lnd_cli_value="$1" lnd_partner_cli_value="$2" diff --git a/core/api/BUCK b/core/api/BUCK index ad519b8599..df396d0053 100644 --- a/core/api/BUCK +++ b/core/api/BUCK @@ -250,6 +250,33 @@ dev_pnpm_task_binary( visibility = ["PUBLIC"], ) +dev_pnpm_task_binary( + name = "dev-cron", + command = "dev:cron", + srcs = [":src"], + visibility = ["PUBLIC"], + env = { + "MONGODB_CON": "mongodb://localhost:27017/galoy", + + "HELMREVISION": "dummy", + "KRATOS_PG_CON": "pg://dummy", + "OATHKEEPER_DECISION_ENDPOINT": "http://dummy", + "NETWORK": "regtest", + "TWILIO_ACCOUNT_SID": "dummy", + "TWILIO_AUTH_TOKEN": "dummy", + "TWILIO_VERIFY_SERVICE_ID": "dummy", + "KRATOS_PUBLIC_API": "http://dummy", + "KRATOS_ADMIN_API": "http://dummy", + "KRATOS_MASTER_USER_PASSWORD": "dummy", + "KRATOS_CALLBACK_API_KEY": "dummy", + "BRIA_HOST": "dummy", + "BRIA_API_KEY": "dummy", + "REDIS_MASTER_NAME": "dummy", + "REDIS_PASSWORD": "dummy", + "REDIS_0_DNS": "dummy", + }, +) + dev_pnpm_task_binary( name = "watch-compile", command = "watch:compile", diff --git a/core/api/package.json b/core/api/package.json index b6ac62790f..c65e1f2e57 100644 --- a/core/api/package.json +++ b/core/api/package.json @@ -28,7 +28,8 @@ "dev:api": "tsx src/servers/graphql-main-server.ts", "dev:api-trigger": "tsx src/servers/trigger.ts", "dev:api-exporter": "tsx src/servers/exporter.ts", - "dev:api-ws-server": "tsx src/servers/ws-server.ts" + "dev:api-ws-server": "tsx src/servers/ws-server.ts", + "dev:cron": "tsx src/servers/cron.ts" }, "engines": { "node": "20" diff --git a/dev/config/lnd/regtest/lnd2.admin.macaroon b/dev/config/lnd/regtest/lnd2.admin.macaroon new file mode 100644 index 0000000000..f414781ae8 Binary files /dev/null and b/dev/config/lnd/regtest/lnd2.admin.macaroon differ diff --git a/dev/config/lnd/regtest/lnd2.admin.macaroon.base64 b/dev/config/lnd/regtest/lnd2.admin.macaroon.base64 new file mode 100644 index 0000000000..22252d2a37 --- /dev/null +++ b/dev/config/lnd/regtest/lnd2.admin.macaroon.base64 @@ -0,0 +1 @@ +AgEDbG5kAvgBAwoQX0BxfhQTxLTiqaceBnGnfBIBMBoWCgdhZGRyZXNzEgRyZWFkEgV3cml0ZRoTCgRpbmZvEgRyZWFkEgV3cml0ZRoXCghpbnZvaWNlcxIEcmVhZBIFd3JpdGUaIQoIbWFjYXJvb24SCGdlbmVyYXRlEgRyZWFkEgV3cml0ZRoWCgdtZXNzYWdlEgRyZWFkEgV3cml0ZRoXCghvZmZjaGFpbhIEcmVhZBIFd3JpdGUaFgoHb25jaGFpbhIEcmVhZBIFd3JpdGUaFAoFcGVlcnMSBHJlYWQSBXdyaXRlGhgKBnNpZ25lchIIZ2VuZXJhdGUSBHJlYWQAAAYgMAKlr1HehfBpn2R5RPE2IuY9r/18QBeLZxYgRidpos4= \ No newline at end of file diff --git a/dev/config/lnd/regtest/lnd2.macaroons.db b/dev/config/lnd/regtest/lnd2.macaroons.db new file mode 100644 index 0000000000..4d55f07bc5 Binary files /dev/null and b/dev/config/lnd/regtest/lnd2.macaroons.db differ diff --git a/dev/config/lnd/regtest/lnd2.pubkey b/dev/config/lnd/regtest/lnd2.pubkey new file mode 100644 index 0000000000..6a3156846d --- /dev/null +++ b/dev/config/lnd/regtest/lnd2.pubkey @@ -0,0 +1 @@ +039341ef13e776dc1611502cf510110d9ac5cdc252141f5997adcfd72cef34c3a7 diff --git a/dev/config/lnd/regtest/lnd2.wallet.db b/dev/config/lnd/regtest/lnd2.wallet.db new file mode 100644 index 0000000000..85dd05c326 Binary files /dev/null and b/dev/config/lnd/regtest/lnd2.wallet.db differ diff --git a/dev/docker-compose.deps.yml b/dev/docker-compose.deps.yml index 466de6e93a..755af0db23 100644 --- a/dev/docker-compose.deps.yml +++ b/dev/docker-compose.deps.yml @@ -173,6 +173,26 @@ services: cp /root/.lnd/macaroons.db /root/.lnd/data/chain/bitcoin/regtest/macaroons.db cp /root/.lnd/admin.macaroon /root/.lnd/data/chain/bitcoin/regtest/admin.macaroon /bin/lnd + lnd2: + image: lightninglabs/lnd:v0.17.3-beta + ports: + - "10010:10009" + volumes: + - ${HOST_PROJECT_PATH:-.}/config/lnd/lnd.conf:/root/.lnd/lnd.conf + - ${HOST_PROJECT_PATH:-.}/config/lnd/tls.key:/root/.lnd/tls.key + - ${HOST_PROJECT_PATH:-.}/config/lnd/tls.cert:/root/.lnd/tls.cert + - ${HOST_PROJECT_PATH:-.}/config/lnd/regtest/lnd2.wallet.db:/root/.lnd/wallet.db + - ${HOST_PROJECT_PATH:-.}/config/lnd/regtest/lnd2.macaroons.db:/root/.lnd/macaroons.db + - ${HOST_PROJECT_PATH:-.}/config/lnd/regtest/lnd2.admin.macaroon:/root/.lnd/admin.macaroon + depends_on: [bitcoind] + entrypoint: ["/bin/sh", "-c"] + command: + - | + mkdir -p /root/.lnd/data/chain/bitcoin/regtest/ + cp /root/.lnd/wallet.db /root/.lnd/data/chain/bitcoin/regtest/wallet.db + cp /root/.lnd/macaroons.db /root/.lnd/data/chain/bitcoin/regtest/macaroons.db + cp /root/.lnd/admin.macaroon /root/.lnd/data/chain/bitcoin/regtest/admin.macaroon + /bin/lnd lnd-outside-1: image: lightninglabs/lnd:v0.17.3-beta ports: diff --git a/toolchains/workspace-pnpm/macros.bzl b/toolchains/workspace-pnpm/macros.bzl index 06275e179c..de1e8de49b 100644 --- a/toolchains/workspace-pnpm/macros.bzl +++ b/toolchains/workspace-pnpm/macros.bzl @@ -1032,7 +1032,18 @@ def madge_check( **kwargs, ) +def dict_to_env_string(input_dict): + env_strings = [] + + # TODO: handle different types of 'value' instead of casting everything to string + for key, value in input_dict.items(): + env_strings.append('export ' + key +'="' + str(value) + '"') + + return '\n'.join(env_strings) + def pnpm_task_binary_impl(ctx: AnalysisContext) -> list[[DefaultInfo, RunInfo]]: + env_file = ctx.actions.write(".env", dict_to_env_string(ctx.attrs.env), is_executable = True) + script = ctx.actions.write("pnpm-run.sh", """\ #!/usr/bin/env bash set -euo pipefail @@ -1040,21 +1051,33 @@ set -euo pipefail rootpath="$(git rev-parse --show-toplevel)" install_node_modules="$1" npm_package_path="$2" -npm_run_command="$3" +env_file="$3" +npm_run_command="$4" cd "$rootpath/$npm_package_path" if [ "$install_node_modules" = "True" ]; then pnpm install --frozen-lockfile fi -if [ "${*:4}" ]; then - exec pnpm run --report-summary "$npm_run_command" -- "${@:4}" + +source "$env_file" + +if [ "${*:5}" ]; then + exec pnpm run --report-summary "$npm_run_command" -- "${@:5}" else exec pnpm run --report-summary "$npm_run_command" fi """, is_executable = True) - args = cmd_args([script, str(ctx.attrs.local_node_modules), ctx.label.package, ctx.attrs.command]) + + args = cmd_args([ + script, + str(ctx.attrs.local_node_modules), + ctx.label.package, + env_file, + ctx.attrs.command + ]) args.hidden([ctx.attrs.deps]) args.hidden([ctx.attrs.srcs]) + return [DefaultInfo(), RunInfo(args = args)] dev_pnpm_task_binary = rule(impl = pnpm_task_binary_impl, attrs = { @@ -1062,6 +1085,13 @@ dev_pnpm_task_binary = rule(impl = pnpm_task_binary_impl, attrs = { "local_node_modules": attrs.bool(default = True, doc = """Need to run pnpm install first?"""), "srcs": attrs.list(attrs.source(), default = [], doc = """List of sources we require"""), "deps": attrs.list(attrs.source(), default = [], doc = """List of dependencies we require"""), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Env values to inject for pnpm command run""" + ), }) def pnpm_task_test_impl(ctx: AnalysisContext) -> list[[DefaultInfo, ExternalRunnerTestInfo]]: