bank: full-scale money inflation for auto-confirm the explorer bank account

This commit is contained in:
Hernâni Marques 2026-07-10 20:08:44 +02:00
parent 38dd829d26
commit 468fdb5fe1
No known key found for this signature in database

View file

@ -1,18 +1,25 @@
#!/bin/bash #!/bin/bash
# Auto-confirm bank withdrawals once the wallet has selected exchange+reserve. # Auto-confirm bank withdrawals for the community demo pool ONLY.
# Required for regional x-taler-bank: confirm wires GOA from user → exchange. #
# Only account: explorer (override only if you really mean another pool user
# via BANK_USER, but still confirms with that user's token only — never
# confirms other customers' withdrawals).
# #
# Run once: auto-confirm-withdrawals.sh # Run once: auto-confirm-withdrawals.sh
# Loop: auto-confirm-withdrawals.sh --loop [SECS] # Loop: auto-confirm-withdrawals.sh --loop [SECS]
# #
# Env: # Env:
# BANK_URL (default http://127.0.0.1:9012) # BANK_URL (default http://127.0.0.1:9012)
# BANK_USER (default explorer) — account that owns the demo withdrawals # BANK_USER (default explorer) — must be the pool account
# BANK_PASS or /root/bank-explorer-password.txt / /root/bank-${USER}-password.txt # BANK_PASS or /root/bank-explorer-password.txt
# LANDING_DIR (default /var/www/bank-landing)
# ALLOW_NON_EXPLORER=1 — allow BANK_USER other than explorer (off by default)
set -euo pipefail set -euo pipefail
BANK="${BANK_URL:-http://127.0.0.1:9012}" BANK="${BANK_URL:-http://127.0.0.1:9012}"
BANK="${BANK%/}"
USER="${BANK_USER:-explorer}" USER="${BANK_USER:-explorer}"
LANDING_DIR="${LANDING_DIR:-/var/www/bank-landing}"
LOOP=0 LOOP=0
SLEEP=5 SLEEP=5
if [ "${1:-}" = "--loop" ]; then if [ "${1:-}" = "--loop" ]; then
@ -20,6 +27,12 @@ if [ "${1:-}" = "--loop" ]; then
SLEEP="${2:-5}" SLEEP="${2:-5}"
fi fi
# Safety: only the shared community account unless explicitly overridden
if [ "$USER" != "explorer" ] && [ "${ALLOW_NON_EXPLORER:-0}" != "1" ]; then
echo "refusing BANK_USER=$USER — auto-confirm is for explorer only (set ALLOW_NON_EXPLORER=1 to override)" >&2
exit 1
fi
PASS="${BANK_PASS:-}" PASS="${BANK_PASS:-}"
if [ -z "$PASS" ]; then if [ -z "$PASS" ]; then
for f in "/root/bank-${USER}-password.txt" /root/bank-explorer-password.txt; do for f in "/root/bank-${USER}-password.txt" /root/bank-explorer-password.txt; do
@ -28,62 +41,89 @@ if [ -z "$PASS" ]; then
fi fi
[ -n "$PASS" ] || { echo "no password for $USER" >&2; exit 1; } [ -n "$PASS" ] || { echo "no password for $USER" >&2; exit 1; }
# JSON field extract without python (bank container may lack python3)
json_str() {
# json_str FIELD < json-text-or-file
local field="$1"
local data
if [ -f "${2:-}" ]; then data=$(cat "$2"); else data=$(cat); fi
printf '%s' "$data" | sed -n "s/.*\"${field}\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" | head -1
}
token() { token() {
echo '{"scope":"readwrite"}' > /tmp/acw-tok-req.json
curl -sS -m 12 -u "${USER}:${PASS}" \ curl -sS -m 12 -u "${USER}:${PASS}" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d @/tmp/acw-tok-req.json \ -d '{"scope":"readwrite","refreshable":true}' \
"${BANK}/accounts/${USER}/token" "${BANK}/accounts/${USER}/token"
} }
# Extract withdrawal IDs we care about: # IDs created for the community pool (landing + watch list only)
# 1) public landing withdraw.uri
# 2) any ID passed as args after --loop secs
known_ids() { known_ids() {
if [ -f /var/www/bank-landing/withdraw.uri ]; then if [ -f "${LANDING_DIR}/withdraw.uri" ]; then
basename "$(tr -d '\n' </var/www/bank-landing/withdraw.uri)" basename "$(tr -d '\n' <"${LANDING_DIR}/withdraw.uri")"
fi fi
# optional list file of UUIDs if [ -f "${LANDING_DIR}/withdraw-watch.ids" ]; then
if [ -f /var/www/bank-landing/withdraw-watch.ids ]; then # strip empty / comments
cat /var/www/bank-landing/withdraw-watch.ids grep -E '^[0-9a-fA-F-]{36}$' "${LANDING_DIR}/withdraw-watch.ids" || true
fi fi
} }
confirm_one() { confirm_one() {
local wid="$1" local wid="$1"
local tok="$2" local tok="$2"
local info conf local info st uname conf
# Public status — must belong to explorer (pool), not another customer
info=$(curl -sS -m 10 "${BANK}/withdrawals/${wid}" 2>/dev/null || true) info=$(curl -sS -m 10 "${BANK}/withdrawals/${wid}" 2>/dev/null || true)
[ -n "$info" ] || return 0 [ -n "$info" ] || return 0
python3 -c "import json,sys; d=json.loads(sys.argv[1]); sys.exit(0 if d.get('status')=='selected' else 1)" "$info" 2>/dev/null || {
st=$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('status','?'))" "$info" 2>/dev/null || echo '?') st=$(printf '%s' "$info" | sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
echo "skip $wid status=$st" uname=$(printf '%s' "$info" | sed -n 's/.*"username"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
# Only confirm withdrawals owned by the pool account
if [ -n "$uname" ] && [ "$uname" != "$USER" ]; then
echo "skip $wid owner=$uname (only confirm $USER)"
return 0 return 0
} fi
echo "confirming $wid ..." # If API omits username, still only confirm via explorer token (cannot confirm others)
if [ -z "$uname" ]; then
# require sender_wire / payto to mention explorer when present
case "$info" in
*explorer*) ;;
*)
# still try only if status selected — confirm endpoint is under explorer account
;;
esac
fi
if [ "$st" != "selected" ]; then
echo "skip $wid status=${st:-?} owner=${uname:-?}"
return 0
fi
echo "confirming $wid as $USER (community pool) ..."
conf=$(curl -sS -m 15 -o /tmp/acw-conf.out -w '%{http_code}' \ conf=$(curl -sS -m 15 -o /tmp/acw-conf.out -w '%{http_code}' \
-X POST \ -X POST \
-H "Authorization: Bearer ${tok}" \ -H "Authorization: Bearer ${tok}" \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{}' \ -d '{}' \
"${BANK}/accounts/${USER}/withdrawals/${wid}/confirm") "${BANK}/accounts/${USER}/withdrawals/${wid}/confirm")
echo " HTTP $conf $(cat /tmp/acw-conf.out 2>/dev/null | head -c 200)" echo " HTTP $conf $(head -c 200 /tmp/acw-conf.out 2>/dev/null || true)"
# re-check curl -sS -m 8 "${BANK}/withdrawals/${wid}" 2>/dev/null \
curl -sS -m 8 "${BANK}/withdrawals/${wid}" | python3 -c "import json,sys; d=json.load(sys.stdin); print(' now', d.get('status'), d.get('amount'))" 2>/dev/null || true | sed -n 's/.*"status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/ now status=\1/p' | head -1 || true
} }
once() { once() {
local tjson tok local tjson tok ids
tjson=$(token) tjson=$(token)
tok=$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('access_token',''))" "$tjson") tok=$(printf '%s' "$tjson" | sed -n 's/.*"access_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ -z "$tok" ]; then if [ -z "$tok" ]; then
echo "token fail: $tjson" >&2 echo "token fail for $USER: $tjson" >&2
return 1 return 1
fi fi
local ids
ids=$(known_ids | sort -u) ids=$(known_ids | sort -u)
if [ -z "$ids" ]; then if [ -z "$ids" ]; then
echo "no withdrawal ids to watch" echo "no withdrawal ids to watch (landing withdraw.uri / withdraw-watch.ids)"
return 0 return 0
fi fi
while read -r wid; do while read -r wid; do
@ -93,7 +133,7 @@ once() {
} }
if [ "$LOOP" -eq 1 ]; then if [ "$LOOP" -eq 1 ]; then
echo "auto-confirm loop every ${SLEEP}s for user=$USER" echo "auto-confirm loop every ${SLEEP}s for pool user=$USER only"
while true; do while true; do
once || true once || true
sleep "$SLEEP" sleep "$SLEEP"