From 12f34f9ea00c0848c676bf1090132d44629e7b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 17 Jul 2026 20:02:34 +0200 Subject: [PATCH] monitoring: stage TESTPAYSAN amount ladder (bank max_wire maxima) Reuse check_goa_ladder for stage.*lefrancpaysan.ch: auto max from max_wire_transfer_amount (2000), min denom from /keys, explorer secret via stagepaysan SSH, fewer default steps. Document usage in README. --- scripts/taler-monitoring/README.md | 8 +- scripts/taler-monitoring/TESTS.md | 2 +- scripts/taler-monitoring/check_goa_ladder.sh | 214 +++++++++++++++---- scripts/taler-monitoring/taler-monitoring.sh | 2 +- 4 files changed, 187 insertions(+), 39 deletions(-) diff --git a/scripts/taler-monitoring/README.md b/scripts/taler-monitoring/README.md index c04508e..35dba91 100644 --- a/scripts/taler-monitoring/README.md +++ b/scripts/taler-monitoring/README.md @@ -198,7 +198,13 @@ Other domains: never SSH. Optional **e2e** aborts cleanly on login/KYC. ```bash ./taler-monitoring.sh e2e ./taler-monitoring.sh -d taler.net urls e2e -# customize: +# Amount ladder (withdraw 0 → … → max, then pay): same script for GOA + stage +./taler-monitoring.sh ladder # GOA / current domain +./taler-monitoring.sh -d stage.lefrancpaysan.ch ladder # TESTPAYSAN maxima +# Stage auto-reads bank max_wire_transfer_amount (e.g. 2000) and min denom (0.01). +# Explorer password: francpaysan-stage-user …/bank-explorer-password.txt (or EXP_PW=). +# Override: LADDER_MAX_AMOUNT=100 LADDER_STEPS=7 LADDER_PAY=0 … +# customize e2e: E2E_WITHDRAW_VALUES="20 50 100" E2E_PAY_VALUES="0.05 1 5" ./taler-monitoring.sh e2e E2E_VARIABLE=0 WITHDRAW_AMT=GOA:50 PAY_AMT=GOA:1 ./taler-monitoring.sh e2e # single fixed # GOA shop catalog (local hacktivism): full list in E2E_SHOP_PRODUCTS; each run diff --git a/scripts/taler-monitoring/TESTS.md b/scripts/taler-monitoring/TESTS.md index 96e4512..4ad45e9 100644 --- a/scripts/taler-monitoring/TESTS.md +++ b/scripts/taler-monitoring/TESTS.md @@ -114,7 +114,7 @@ When filing an issue, quote the full id + label, e.g. | **versions.inside-** | packages in containers | | **versions.compare-** | installed vs suite | | **sanity.bank-** / **.exchange-** / **.merchant-** | public + optional server-side per component | -| **ladder.plan-** / **.load-** / **.withdraw-** / **.pay-** / **.report-** | GOA amount ladder | +| **ladder.plan-** / **.load-** / **.withdraw-** / **.pay-** / **.report-** | amount ladder (GOA ceiling or stage TESTPAYSAN `max_wire`) | | **server-** | SSH host ports / processes (flat unless grouped later) | --- diff --git a/scripts/taler-monitoring/check_goa_ladder.sh b/scripts/taler-monitoring/check_goa_ladder.sh index 90a6f1b..d7c60ae 100755 --- a/scripts/taler-monitoring/check_goa_ladder.sh +++ b/scripts/taler-monitoring/check_goa_ladder.sh @@ -1,31 +1,27 @@ #!/usr/bin/env bash -# check_goa_ladder.sh — GOA withdraw ladder for taler-monitoring +# check_goa_ladder.sh — withdraw/pay amount ladder (GOA + stage TESTPAYSAN) # -# bank.hacktivism.ch flow (landing): -# 1) GET /intro/auto-account.json → personal goa-account-* (GOA:0) -# 2) Mint pool withdrawals as explorer (shared pool) + confirm when selected -# 3) wallet-cli accept-uri only (no run-until-done — hangs / developer ban) -# 4) bank confirm when selected; settle = poll balance + bank transfer_done +# Flow (bank landings): +# 1) GET /intro/auto-account.json → personal *account-* (balance 0) +# 2) Mint pool withdrawals as explorer + confirm when selected +# 3) wallet-cli accept-uri only (no run-until-done) +# 4) bank confirm when selected; settle = balance + transfer_done # -# Amounts: random strictly increasing; fixed pins 0, max-1, max (23 default). -# Phase A: withdraw ladder into ONE cumulative wallet (mids scaled up for pay budget). -# Phase B: pay ladder 0 → low → … → max-1 → max (same step count). -# On first hard failure: stop, print timing report, exit 1. -# Soft: GOA:0 / wallet 7006 / absolute max (CEILING_REJECT) → WARN and continue. +# Amounts: random strictly increasing; pins 0, max-1, max. +# Phase A: withdraw ladder (cumulative wallet). Phase B: pay ladder. # -# Env: -# LADDER_STEPS total rungs (default 23) = 0 + (N-3) random + max-1 + max -# LADDER_MAX_AMOUNT absolute last pin (libeufin ceiling 4503599627370496) -# LADDER_WITHDRAW_SCALE mid withdraw amounts × this vs pay mids (default 1.5) -# LADDER_PAY=0 skip payment phase -# LADDER_TIMEOUT_S default 3600 -# LADDER_LOAD=0 skip host load snapshots -# LADDER_SETTLE_ROUNDS / LADDER_SETTLE_SLEEP — balance poll only (no shepherd) -# EXP_PW / EXP_PW_FILE / EXP_USER — explorer pool (default: secrets tree) -# CLI_JS / WALLET_CLI — wallet-cli .mjs (default: find_wallet_cli) -# LADDER_REPORT_DIR, MERCHANT_INSTANCE, … +# Stacks: +# GOA — LADDER_MAX_AMOUNT = libeufin ceiling; explorer from koopa-admin-secrets +# TESTPAYSAN stage — auto max_wire from bank /config, min denom from /keys; +# explorer from stagepaysan secrets / SSH # -# Phase: ./taler-monitoring.sh ladder +# Usage: +# ./taler-monitoring.sh ladder +# ./taler-monitoring.sh -d stage.lefrancpaysan.ch ladder +# LADDER_MAX_AMOUNT=100 LADDER_STEPS=7 ./taler-monitoring.sh -d stage.lefrancpaysan.ch ladder +# +# Env (see body): LADDER_STEPS, LADDER_MAX_AMOUNT, LADDER_MIN_AMOUNT, LADDER_STACK_AUTO, +# LADDER_PAY, EXP_PW / EXP_PW_FILE, CLI_JS, MERCHANT_INSTANCE, … set -euo pipefail ROOT=$(cd "$(dirname "$0")" && pwd) # shellcheck source=lib.sh @@ -33,7 +29,7 @@ source "$ROOT/lib.sh" # When invoked standalone (not via taler-monitoring.sh), still load secrets.env load_monitoring_secrets_env 2>/dev/null || true -# Area ladder.* — GOA withdraw/pay ladder +# Area ladder.* — withdraw/pay amount ladder (GOA + TESTPAYSAN) # Groups: ladder.plan / ladder.load / ladder.withdraw / ladder.pay / ladder.report set_area ladder set_group plan @@ -48,14 +44,16 @@ elapsed_ms() { : "${LADDER_SETTLE_ROUNDS:=18}" : "${LADDER_SETTLE_SLEEP:=2}" : "${EXP_USER:=explorer}" -# EXP_PW_FILE / EXP_PW optional overrides; otherwise read_secret + SECRETS_ROOT +# EXP_PW_FILE / EXP_PW optional overrides; otherwise read_secret + SECRETS_ROOT / stage SSH : "${EXP_PW_FILE:=}" : "${EXP_PW:=}" : "${CLI_JS:=}" +# GOA libeufin amount ceiling (hacktivism). Stage auto-replaces via bank /config. : "${LADDER_MAX_AMOUNT:=4503599627370496}" : "${LADDER_STEPS:=23}" : "${LADDER_LOAD:=1}" : "${LADDER_PAY:=1}" +: "${LADDER_STACK_AUTO:=1}" # Withdraw mids = pay mids × scale (so wallet can afford the pay ladder) : "${LADDER_WITHDRAW_SCALE:=1.5}" # Floor for random mids (must be ≥ smallest exchange coin; GOA min denom ≈ 0.000001) @@ -63,6 +61,11 @@ elapsed_ms() { : "${LADDER_CONFIRM_POLLS:=40}" : "${LADDER_PAY_SETTLE_ROUNDS:=6}" : "${MERCHANT_INSTANCE:=goa-demo-cp4zqk}" +# Public variable template for stage pays (optional; fixed templates used when amount maps) +: "${LADDER_PAY_TEMPLATE:=}" +: "${LADDER_PAY_TEMPLATE_INSTANCE:=fermes-des-collines}" + +GOA_LADDER_CEILING="4503599627370496" # Resolve wallet-cli .mjs (no hardcoded laptop path) if [ -z "${CLI_JS}" ] || [ ! -f "${CLI_JS}" ]; then @@ -78,6 +81,77 @@ BANK="${BANK_PUBLIC%/}" EX="${EXCHANGE_PUBLIC%/}/" MER="${MERCHANT_PUBLIC%/}" INST="${MERCHANT_INSTANCE}" + +# --- Stack-specific ladder maxima (TESTPAYSAN stage) --- +# Uses bank max_wire_transfer_amount and exchange min denom when still on GOA defaults. +apply_ladder_stack_defaults() { + [ "${LADDER_STACK_AUTO}" = "1" ] || return 0 + if [ "${CUR}" != "TESTPAYSAN" ]; then + case "${TALER_DOMAIN:-}" in + stage.lefrancpaysan.ch|stage.bank.lefrancpaysan.ch|stage.exchange.lefrancpaysan.ch|stage.monnaie.lefrancpaysan.ch) + CUR="TESTPAYSAN" + ;; + *) return 0 ;; + esac + fi + + local bank_cfg max_wire min_denom + bank_cfg=$(curl -sS -m 12 "${BANK}/config" 2>/dev/null || true) + max_wire=$(printf '%s' "$bank_cfg" | python3 -c ' +import json,sys +try: + d=json.load(sys.stdin) +except Exception: + print("") + raise SystemExit(0) +a=d.get("max_wire_transfer_amount") or "" +print(a.split(":",1)[-1] if a else "") +' 2>/dev/null || true) + + min_denom=$(curl -sS -m 25 -H 'Accept: application/json' "${EX%/}/keys" 2>/dev/null | python3 -c ' +import json,sys +try: + d=json.load(sys.stdin) +except Exception: + print("") + raise SystemExit(0) +den=d.get("denoms") or d.get("denominations") or [] +vals=[] +for x in den if isinstance(den,list) else []: + v=x.get("value") or x.get("amount") or "" + if not v: continue + try: vals.append(float(str(v).split(":")[-1])) + except Exception: pass +print(min(vals) if vals else "") +' 2>/dev/null || true) + + # Fallback maxima if public config unreachable + [ -n "$max_wire" ] || max_wire="2000" + [ -n "$min_denom" ] || min_denom="0.01" + + # Only rewrite when caller left GOA defaults (explicit LADDER_MAX_AMOUNT=… kept) + if [ -n "$max_wire" ] && { [ "${LADDER_MAX_AMOUNT}" = "${GOA_LADDER_CEILING}" ] || [ "${LADDER_MAX_AMOUNT}" = "4503599627370496" ]; }; then + LADDER_MAX_AMOUNT="$max_wire" + fi + if [ -n "$min_denom" ] && { [ "${LADDER_MIN_AMOUNT}" = "0.000001" ] || [ "${LADDER_MIN_AMOUNT}" = "0.00000001" ]; }; then + LADDER_MIN_AMOUNT="$min_denom" + fi + # Slightly fewer rungs on stage (full GOA 23 still ok if user set LADDER_STEPS) + if [ "${LADDER_STEPS}" = "23" ]; then + LADDER_STEPS=15 + fi + # Stage farmer shops: private goa-demo instance does not exist + if [ "${MERCHANT_INSTANCE}" = "goa-demo-cp4zqk" ]; then + MERCHANT_INSTANCE="${LADDER_PAY_TEMPLATE_INSTANCE:-fermes-des-collines}" + INST="$MERCHANT_INSTANCE" + fi + # Prefer public templates for pays when no merchant token (set later) + : "${E2E_USE_TEMPLATES:=1}" + export E2E_USE_TEMPLATES + export LADDER_MAX_AMOUNT LADDER_MIN_AMOUNT LADDER_STEPS MERCHANT_INSTANCE + info "ladder stack" "TESTPAYSAN · max=${CUR}:${LADDER_MAX_AMOUNT} min=${CUR}:${LADDER_MIN_AMOUNT} steps=${LADDER_STEPS} (from bank max_wire / keys denoms)" +} +apply_ladder_stack_defaults SCRATCH=$(mktemp -d) WDB="$SCRATCH/wallet.sqlite3" REPORT_DIR="${LADDER_REPORT_DIR:-$SCRATCH}" @@ -104,9 +178,9 @@ wcli() { fi } -# Explorer pool password: env → file override → secrets tree → SSH koopa +# Explorer pool password: env → file override → stack secrets → SSH resolve_explorer_pw() { - local pw="" f="" + local pw="" f="" host="" if [ -n "${EXP_PW:-}" ]; then printf '%s' "$EXP_PW" return 0 @@ -115,12 +189,45 @@ resolve_explorer_pw() { tr -d '\n\r' <"$EXP_PW_FILE" return 0 fi - # Prefer shared read_secret (SECRETS_ROOT / ~/.config / koopa SSH) + + # Stage TESTPAYSAN — never use GOA koopa-admin-secrets explorer pw first + if [ "${CUR}" = "TESTPAYSAN" ] \ + || [[ "${TALER_DOMAIN:-}" == stage.*lefrancpaysan* ]] \ + || [[ "${TALER_DOMAIN:-}" == *stage.lefrancpaysan* ]]; then + for f in \ + "${FRANCPAYSAN_SECRETS:-}/stage/bank-explorer-password.txt" \ + "${HOME}/francpaysan-secrets/stage/bank-explorer-password.txt" \ + "${HOME}/src/francpaysan-secrets/stage/bank-explorer-password.txt" \ + "${HOME}/taler/src/francpaysan-secrets/stage/bank-explorer-password.txt" \ + "${HOME}/.config/taler-landing/stage-bank-explorer-password.txt" + do + [ -n "$f" ] && [ -f "$f" ] || continue + tr -d '\n\r' <"$f" + return 0 + done + for host in \ + "${INSIDE_SSH:-}" \ + francpaysan-stage-user \ + francpaysan-host + do + [ -n "$host" ] || continue + pw=$(ssh -o BatchMode=yes -o ConnectTimeout=12 "$host" \ + 'tr -d "\n\r" /dev/null \ + || sudo tr -d "\n\r" /dev/null' \ + 2>/dev/null || true) + if [ -n "$pw" ]; then + printf '%s' "$pw" + return 0 + fi + done + return 1 + fi + + # GOA / default: SECRETS_ROOT / ~/.config / koopa SSH if pw=$(read_secret "taler-bank/bank-explorer-password.txt" 2>/dev/null) && [ -n "$pw" ]; then printf '%s' "$pw" return 0 fi - # Extra local candidates (sibling of admin-log, taler-landing config) for f in \ "${SECRETS_ROOT:+${SECRETS_ROOT}/taler-bank/bank-explorer-password.txt}" \ "${HOME}/src/koopa/koopa-admin-secrets/koopa/host-root/taler-bank/bank-explorer-password.txt" \ @@ -292,7 +399,7 @@ if [ "${LADDER_LOAD:-1}" = "0" ]; then export METRICS_LOAD fi -section "ladder · GOA withdraw (0 → random → max · ${LADDER_STEPS} steps)" +section "ladder · ${CUR} withdraw (0 → random → max · ${LADDER_STEPS} steps)" info "bank" "$BANK" info "exchange" "$EX" info "currency" "$CUR" @@ -303,10 +410,11 @@ else warn "alt_unit_names" "using built-in SI fallback ($ALT_UNITS_FILE)" fi info "budget" "${LADDER_TIMEOUT_S}s" -_max_m1=$(python3 -c 'import sys; print(int(sys.argv[1])-1)' "${LADDER_MAX_AMOUNT}" 2>/dev/null || echo "${LADDER_MAX_AMOUNT}-1") +_max_m1=$(python3 -c 'import sys; from decimal import Decimal; m=Decimal(sys.argv[1]); print(m-1 if m>1 else m)' "${LADDER_MAX_AMOUNT}" 2>/dev/null || echo "${LADDER_MAX_AMOUNT}-1") info "steps" "${LADDER_STEPS} (0 + random≥${LADDER_MIN_AMOUNT} + max-1=${CUR}:${_max_m1} + max=${CUR}:${LADDER_MAX_AMOUNT})" info "withdraw_scale" "${LADDER_WITHDRAW_SCALE}× pay mids (fund pay ladder)" info "pay_phase" "$([ "${LADDER_PAY}" = "1" ] && echo enabled || echo disabled)" +info "currency/domain" "${CUR} · ${TALER_DOMAIN:-?} · bank=${BANK}" set_group load section "ladder · load snapshot (before withdraws)" @@ -320,7 +428,11 @@ if ! EXP_PW=$(resolve_explorer_pw); then secrets_hint 2>/dev/null || true exit 1 fi -info "explorer secret" "${SECRETS_ROOT:+SECRETS_ROOT=${SECRETS_ROOT}}${SECRETS_ROOT:-via EXP_PW/EXP_PW_FILE/SSH}" +if [ -n "${SECRETS_ROOT:-}" ]; then + info "explorer secret" "resolved (SECRETS_ROOT=${SECRETS_ROOT} · stage uses stagepaysan secret first when CUR=TESTPAYSAN)" +else + info "explorer secret" "resolved via EXP_PW / EXP_PW_FILE / stage SSH / koopa" +fi if [ -n "${CLI_JS:-}" ] && [ -f "${CLI_JS}" ]; then info "wallet-cli" "$CLI_JS" else @@ -339,7 +451,7 @@ if ! python3 -c 'import json; d=json.load(open("'"$SCRATCH"'/auto-account.json") exit 1 fi ACCT_USER=$(python3 -c 'import json; print(json.load(open("'"$SCRATCH"'/auto-account.json"))["username"])') -ok "auto-account ${ACCT_USER} (${ms_auto}ms) — personal GOA:0; pool=explorer" +ok "auto-account ${ACCT_USER} (${ms_auto}ms) — personal ${CUR}:0; pool=explorer" info "auto-account password" "(see $SCRATCH/auto-account.json — not logged)" # --- explorer token --- @@ -841,7 +953,7 @@ if [ "${LADDER_PAY}" = "1" ] && [ -n "${PAY_LIST:-}" ] && [ "$FAIL_N_L" -eq 0 ]; set_group pay section "ladder · phase B · pay" metrics_report_coins "before-pay-ladder" || true - # Merchant secret (same as e2e) + # Merchant secret (same as e2e) — stage often has only public shop templates MPW="${E2E_MERCHANT_TOKEN:-${MERCHANT_TOKEN:-}}" if [ -z "$MPW" ]; then MPW=$(read_secret "taler-merchant/merchant-${INST}-password.txt" 2>/dev/null || true) @@ -849,11 +961,41 @@ if [ "${LADDER_PAY}" = "1" ] && [ -n "${PAY_LIST:-}" ] && [ "$FAIL_N_L" -eq 0 ]; if [ -z "$MPW" ]; then MPW=$(read_secret "taler-merchant/merchant-goa-demo-cp4zqk-password.txt" 2>/dev/null || true) fi + if [ -z "$MPW" ] && [ "${CUR}" = "TESTPAYSAN" ]; then + for f in \ + "${FRANCPAYSAN_SECRETS:-}/stage/default-instance-token.txt" \ + "${HOME}/francpaysan-secrets/stage/default-instance-token.txt" \ + "${HOME}/src/francpaysan-secrets/stage/default-instance-token.txt" + do + [ -f "$f" ] || continue + MPW=$(tr -d '\n\r' <"$f") + break + done + if [ -z "$MPW" ]; then + for host in "${INSIDE_SSH:-}" francpaysan-stage-user francpaysan-host; do + [ -n "$host" ] || continue + MPW=$(ssh -o BatchMode=yes -o ConnectTimeout=12 "$host" \ + 'tr -d "\n\r" /dev/null || true' \ + 2>/dev/null || true) + [ -n "$MPW" ] && break + done + fi + fi if [ -z "$MPW" ]; then - warn pay "no merchant token — skip pay ladder (set E2E_MERCHANT_TOKEN or secrets)" + if [ "${CUR}" = "TESTPAYSAN" ]; then + # Stage: withdraw ladder is the main probe; pays need instance token or + # public templates for *exact* face values (not continuous ladder amounts). + info pay "no merchant token — skip pay ladder on TESTPAYSAN (withdraw maxima still covered)" + info pay "hint: set E2E_MERCHANT_TOKEN or use ./taler-monitoring.sh -d stage.lefrancpaysan.ch e2e for shop templates" + else + warn pay "no merchant token — skip pay ladder (set E2E_MERCHANT_TOKEN or secrets)" + fi else ok "merchant token" "instance ${INST}" - AUTH="Authorization: Bearer secret-token:${MPW}" + case "$MPW" in + secret-token:*) AUTH="Authorization: Bearer ${MPW}" ;; + *) AUTH="Authorization: Bearer secret-token:${MPW}" ;; + esac wallet_prepare "main" # shellcheck disable=SC2086 set -- $PAY_LIST diff --git a/scripts/taler-monitoring/taler-monitoring.sh b/scripts/taler-monitoring/taler-monitoring.sh index d8f6197..e63c922 100755 --- a/scripts/taler-monitoring/taler-monitoring.sh +++ b/scripts/taler-monitoring/taler-monitoring.sh @@ -26,7 +26,7 @@ Phases: sanity public + optional server server server-side only (SSH) e2e withdraw + pay (small amounts; remote aborts on login/KYC) - ladder GOA withdraw ladder (auto-account + explorer pool + timings) + ladder withdraw/pay amount ladder (GOA ceiling or stage TESTPAYSAN max_wire) all urls + inside + versions + sanity + e2e (SSH phases only on koopa) Options: