diff --git a/dev/bin/add-phone.sh b/dev/bin/add-phone.sh new file mode 100755 index 000000000..0fa34e699 --- /dev/null +++ b/dev/bin/add-phone.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +source "$(git rev-parse --show-toplevel)/test/galoy/bats/helpers/_common.bash" + +DEVICE_NAME="device-user" +DEVICE_PHONE="+16505554353" + +token_name="$DEVICE_NAME" +phone="$DEVICE_PHONE" +code="$CODE" + +variables=$( + jq -n \ + --arg phone "$phone" \ + --arg code "$code" \ + '{input: {phone: $phone, code: $code}}' +) + +exec_graphql "$token_name" 'user-login-upgrade' "$variables" +upgrade_success="$(graphql_output '.data.userLoginUpgrade.success')" +[[ "$upgrade_success" == "true" ]] || exit 1 + +# Existing phone accounts return an authToken +upgrade_auth_token="$(graphql_output '.data.userLoginUpgrade.authToken')" +[[ "$upgrade_auth_token" == "null" ]] || exit 1 + +exec_graphql "$token_name" 'account-details' +account_level="$(graphql_output '.data.me.defaultAccount.level')" +[[ "$account_level" == "ONE" ]] || exit 1 diff --git a/dev/bin/create-device-account.sh b/dev/bin/create-device-account.sh new file mode 100755 index 000000000..5289e43f6 --- /dev/null +++ b/dev/bin/create-device-account.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +source "$(git rev-parse --show-toplevel)/test/galoy/bats/helpers/_common.bash" + +DEVICE_NAME="device-user" + +create_device_account "$DEVICE_NAME" + +# Verify account is creation +exec_graphql "$DEVICE_NAME" 'account-details' +# local account_id="$(graphql_output '.data.me.defaultAccount.id')" +# [[ "$account_id" != "null" ]] || return 1 +echo "Created device account with ID: $(graphql_output '.data.me.defaultAccount.id')" + +# return 0 \ No newline at end of file diff --git a/dev/bin/gen-test-jwt.ts b/dev/bin/gen-test-jwt.ts index 8d7650a01..fbeb6ef5a 100644 --- a/dev/bin/gen-test-jwt.ts +++ b/dev/bin/gen-test-jwt.ts @@ -1,7 +1,8 @@ -// this script generates and verifies a JSON Web Token (JWT), using the 'node-jose', 'jsonwebtoken', and 'jwks-rsa' packages. -// It uses a local 'jwks.json' file for key storage and verification +/* + Firebase App Check Token is used for Device Authentication. + This JWT simulates the Firebase App Check token used in the device account creation flow. +*/ -// cd dev/ory && ts-node gen-test-jwt.ts import fs from "fs" import * as jose from "node-jose" import jsonwebtoken from "jsonwebtoken" @@ -85,12 +86,12 @@ async function verifyToken(token) { const pem = jwtAskey.toPEM(false) // Verify the token - const verifiedToken = jsonwebtoken.verify(token, pem, { - algorithms: ["RS256"], - audience: aud, - issuer: iss, - }) - return verifiedToken + // const verifiedToken = jsonwebtoken.verify(token, pem, { + // algorithms: ["RS256"], + // audience: aud, + // issuer: iss, + // }) + // return verifiedToken } main() diff --git a/dev/bin/save-lnd-data.sh b/dev/bin/save-lnd-data.sh deleted file mode 100644 index 9d28e8667..000000000 --- a/dev/bin/save-lnd-data.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# script to generate the lnd wallets for testing - -NETWORK=regtest -mkdir ../lnd/$NETWORK 2>/dev/null - -fetch_lnd_data() { - local container_id=$(docker ps -q -f status=running -f name="galoy-$1-") - if [ -n "${container_id}" ]; then - # wallet.db - docker cp $container_id:/root/.lnd/data/chain/bitcoin/$NETWORK/wallet.db ../lnd/$NETWORK/$1.wallet.db - # macaroons.db - docker cp $container_id:/root/.lnd/data/chain/bitcoin/$NETWORK/macaroons.db ../lnd/$NETWORK/$1.macaroons.db - # admin.macaroon - docker cp $container_id:/root/.lnd/data/chain/bitcoin/$NETWORK/admin.macaroon ../lnd/$NETWORK/$1.admin.macaroon - # base64 macaroon - base64 ../lnd/$NETWORK/$1.admin.macaroon | tr -d '\n\r' > "../lnd/$NETWORK/$1.admin.macaroon.base64" - # pubkey - docker exec ${container_id} lncli -n $NETWORK getinfo 2> /dev/null | jq -r .identity_pubkey > ../lnd/$NETWORK/$1.pubkey - fi -} - -for i in lnd1 lnd2 lnd-outside-1 lnd-outside-2; do - echo "Saving data for $i" - fetch_lnd_data "$i" -done diff --git a/dev/bin/save-loop-data.sh b/dev/bin/save-loop-data.sh deleted file mode 100755 index ec20cd0dc..000000000 --- a/dev/bin/save-loop-data.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -# script to generate the loop wallets for testing -NETWORK=regtest -mkdir ../lnd/loop/$NETWORK 2>/dev/null -mkdir ../lnd/loop/server/$NETWORK 2>/dev/null - -fetch_loop_data() { - local container_id=$(docker ps -q -f status=running -f name="galoy-$1") - if [ -n "${container_id}" ]; then - # loop.db - docker cp $container_id:/root/.loop/$NETWORK/loop.db ../lnd/loop/$NETWORK/$1.loop.db - # macaroons.db - docker cp $container_id:/root/.loop/$NETWORK/macaroons.db ../lnd/loop/$NETWORK/$1.macaroons.db - # loop.macaroon - docker cp $container_id:/root/.loop/$NETWORK/loop.macaroon ../lnd/loop/$NETWORK/$1.loop.macaroon - # base64 macaroon - base64 ../lnd/loop/$NETWORK/$1.loop.macaroon | tr -d '\n\r' > "../lnd/loop/$NETWORK/$1.loop.macaroon.base64" - # loop tls cert - docker cp $container_id:/root/.loop/$NETWORK/tls.cert ../lnd/loop/$NETWORK/$1.tls.cert - # base64 loop tls cert - base64 ../lnd/loop/$NETWORK/$1.tls.cert | tr -d '\n\r' > "../lnd/loop/$NETWORK/$1.tls.cert.base64" - # loop tls key - docker cp $container_id:/root/.loop/$NETWORK/tls.key ../lnd/loop/$NETWORK/$1.tls.key - fi -} - -fetch_loopserver_data() { - local outside=$(docker ps -q -f status=running -f name="galoy-lnd-outside-1-1") - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/admin.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/signer.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/walletkit.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/chainnotifier.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/invoices.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/router.macaroon ../lnd/loop/server/$NETWORK - docker cp $outside:/root/.lnd/data/chain/bitcoin/$NETWORK/admin.macaroon ../lnd/loop/server/$NETWORK/readonly.macaroon - -} - -for i in loopd1-1 loopd2-1; do - echo "Saving data for $i" - fetch_loop_data "$i" -done - -fetch_loopserver_data diff --git a/dev/bin/start-loopd.sh b/dev/bin/start-loopd.sh deleted file mode 100755 index 49f9e9bad..000000000 --- a/dev/bin/start-loopd.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh - -# reload env vars -direnv reload - -# stop existing docker -docker compose stop loopserver -docker compose stop loopd1 -docker compose stop loopd2 - -# boot up the mock loopserver -docker compose up loopserver -d - -# add more sats to LNDs for loopouts, need at least 250,000 -outside=$(docker ps -q -f name="lnd-outside-1-1") -lnd1=$(docker ps -q -f name="lnd1-1") -addr1=$(docker exec $lnd1 lncli -n regtest newaddress p2wkh | grep address | awk -F'"' '{print $4}') -docker exec $outside lncli -n regtest sendcoins $addr1 500000 - -# mine some bitcoin to sync the loopserver -make mine-block - -# start the loop client with REST API -export LOOP_SERVER_INTERNAL_IP=$(docker inspect $(docker ps -q -f name="loopserver") -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}') -echo "LOOP_SERVER_INTERNAL_IP is $LOOP_SERVER_INTERNAL_IP" -docker compose up loopd1 -d -docker compose up loopd2 -d - -echo "" -echo "waiting 5 seconds..." -echo "" -sleep 5 - -# test the lnd1-loop REST API with a quote request -echo "loop1 results" -LOOP1_MACAROON_HEXSTR=$(cat dev/lnd/loop/$NETWORK/loopd1-1.loop.macaroon | xxd -p | awk '{print}' ORS='') -# echo $LOOP1_MACAROON_HEXSTR -# test loop rest api -curl -k \ - --request GET \ - --url https://localhost:8081/v1/loop/out/quote/500000 \ - --header 'Content-Type: application/json' \ - --header "Grpc-Metadata-macaroon: $LOOP1_MACAROON_HEXSTR" # --verbose - -# test the lnd2-loop REST API with a quote request -echo "loop2 results" -LOOP2_MACAROON_HEXSTR=$(cat dev/lnd/loop/$NETWORK/loopd2-1.loop.macaroon | xxd -p | awk '{print}' ORS='') -# echo $LOOP2_MACAROON_HEXSTR -# test loop2 rest api -curl -k \ - --request GET \ - --url https://localhost:8082/v1/loop/out/quote/500000 \ - --header 'Content-Type: application/json' \ - --header "Grpc-Metadata-macaroon: $LOOP2_MACAROON_HEXSTR" # --verbose diff --git a/src/app/authentication/login.ts b/src/app/authentication/login.ts index e44b70de9..f737151b0 100644 --- a/src/app/authentication/login.ts +++ b/src/app/authentication/login.ts @@ -54,6 +54,7 @@ import { rewardFailedLoginAttemptPerIpLimits, rewardFailedLoginAttemptPerLoginIdentifierLimits, } from "./ratelimits" +import { getBalanceForWallet } from "@app/wallets" export const loginWithPhoneToken = async ({ phone, @@ -306,12 +307,11 @@ export const loginDeviceUpgradeWithPhone = async ({ // is there still txns left over on the device account? const deviceWallets = await WalletsRepository().listByAccountId(account.id) if (deviceWallets instanceof Error) return deviceWallets - const ledger = LedgerService() let deviceAccountHasBalance = false for (const wallet of deviceWallets) { - const balance = await ledger.getWalletBalance(wallet.id) + const balance = await getBalanceForWallet({ walletId: wallet.id }) if (balance instanceof Error) return balance - if (balance > 0) { + if (!balance.isZero()) { deviceAccountHasBalance = true } } diff --git a/test/galoy/bats/helpers/_common.bash b/test/galoy/bats/helpers/_common.bash index 90bbd7a84..0e4b280d3 100644 --- a/test/galoy/bats/helpers/_common.bash +++ b/test/galoy/bats/helpers/_common.bash @@ -100,7 +100,7 @@ gql_query() { } gql_file() { - echo "${BATS_TEST_DIRNAME:-${REPO_ROOT}/test/bats}/gql/$1.gql" + echo "${BATS_TEST_DIRNAME:-${REPO_ROOT}/test/galoy/bats}/gql/$1.gql" } gql_admin_query() { @@ -136,13 +136,25 @@ exec_graphql() { gql_route="graphql" - ${run_cmd} curl -s \ - -X POST \ - ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ - -H "Content-Type: application/json" \ - -H "X-Idempotency-Key: $(new_idempotency_key)" \ - -d "{\"query\": \"$(gql_query $query_name)\", \"variables\": $variables}" \ - "${GALOY_ENDPOINT}/${gql_route}" + if [[ "${BATS_TEST_DIRNAME}" != "" ]]; then + # In BATS: run command captures output into $output + ${run_cmd} curl -s \ + -X POST \ + ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ + -H "Content-Type: application/json" \ + -H "X-Idempotency-Key: $(new_idempotency_key)" \ + -d "{\"query\": \"$(gql_query $query_name)\", \"variables\": $variables}" \ + "${GALOY_ENDPOINT}/${gql_route}" + else + # Outside BATS: manually capture output + output=$(curl -s \ + -X POST \ + ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ + -H "Content-Type: application/json" \ + -H "X-Idempotency-Key: $(new_idempotency_key)" \ + -d "{\"query\": \"$(gql_query $query_name)\", \"variables\": $variables}" \ + "${GALOY_ENDPOINT}/${gql_route}") + fi echo "GQL output: '$output'" } @@ -168,12 +180,23 @@ exec_admin_graphql() { gql_route="admin/graphql" - ${run_cmd} curl -s \ - -X POST \ - ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ - -H "Content-Type: application/json" \ - -d "{\"query\": \"$(gql_admin_query $query_name)\", \"variables\": $variables}" \ - "${GALOY_ENDPOINT}/${gql_route}" + if [[ "${BATS_TEST_DIRNAME}" != "" ]]; then + # In BATS: run command captures output into $output + ${run_cmd} curl -s \ + -X POST \ + ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ + -H "Content-Type: application/json" \ + -d "{\"query\": \"$(gql_admin_query $query_name)\", \"variables\": $variables}" \ + "${GALOY_ENDPOINT}/${gql_route}" + else + # Outside BATS: manually capture output + output=$(curl -s \ + -X POST \ + ${AUTH_HEADER:+ -H "$AUTH_HEADER"} \ + -H "Content-Type: application/json" \ + -d "{\"query\": \"$(gql_admin_query $query_name)\", \"variables\": $variables}" \ + "${GALOY_ENDPOINT}/${gql_route}") + fi echo "GQL output: '$output'" } @@ -217,7 +240,13 @@ curl_request() { cmd+=("${url}") - "${cmd[@]}" + if [[ "${BATS_TEST_DIRNAME}" != "" ]]; then + # In BATS: run command captures output into $output + "${cmd[@]}" + else + # Outside BATS: manually capture output + output=$("${cmd[@]}") + fi echo "Curl output: '$output'" } @@ -238,3 +267,29 @@ is_contact() { ) [[ "$fetched_username" == "$contact_username" ]] || return 1 } + +create_device_account() { + local token_name="$1" + local url="http://${GALOY_ENDPOINT}/auth/create/device-account" + + # dev/ory/gen-test-jwt.ts + local jwt="eyJhbGciOiJSUzI1NiIsImtpZCI6IjFiOTdiMjIxLWNhMDgtNGViMi05ZDA5LWE1NzcwZmNjZWIzNyJ9.eyJzdWIiOiIxOjgwNjY0NjE0MDQzNTphbmRyb2lkOmE4YTBjY2ZlODhiZWUxNWIwNmY5ZTYiLCJhdWQiOlsicHJvamVjdHMvODA2NjQ2MTQwNDM1IiwicHJvamVjdHMvYXZpZC1jZWlsaW5nLTM5MDQxOCJdLCJwcm92aWRlciI6ImRlYnVnIiwiaXNzIjoiaHR0cHM6Ly9maXJlYmFzZWFwcGNoZWNrLmdvb2dsZWFwaXMuY29tLzgwNjY0NjE0MDQzNSIsImV4cCI6MjYzOTAwMDA2OX0.cgE2pX3srSzlPreJpBDLaFmPQn9CyKoxW1f-hFgVbGZ7xwWysogsNTrV0eIkvgDnZWjbjexOxf4HhuK2MSBmnRYTWgk6LC7LNoq_KPNAvxkMNj1HGSYh34q2uYafcc1LZCREDvPFTw-JN6FJOAzk7TbWwi8A8-Z8ed5W1kqzkWu_D79nZNWZuN6tUpoeyj1c77Cb7wn5UBlSBhoNrfxXOQKTsKTmuFpcR2P3zv_R9D-yedizqLpG75XJkJd6_4zuhhrW05nMgOHULQ2bTt3PTbi6dy64ObLwMOT5vevqqbKc303-rk02sDGCdRc251nL5sIvTIcajXUXs-Ruy3Op4g" # the JWT token + + local username="$(random_uuid)" + local password="$(random_uuid)" + + if [[ "$(uname)" == "Linux" ]]; then + local basic_token="$(echo -n $username:$password | base64 -w 0)" + else + local basic_token="$(echo -n $username:$password | base64)" + fi + + local auth_header="Authorization: Basic $basic_token" + local appcheck_header="Appcheck: $jwt" + + # Create account + curl_request "$url" "" "$auth_header" "$appcheck_header" + local auth_token="$(echo $output | jq -r '.result')" + [[ "$auth_token" != "null" ]] || return 1 + cache_value "$token_name" "$auth_token" + }