diff --git a/scripts/taler-monitoring/check_goa_ladder.sh b/scripts/taler-monitoring/check_goa_ladder.sh index ea3767e..91bc43e 100755 --- a/scripts/taler-monitoring/check_goa_ladder.sh +++ b/scripts/taler-monitoring/check_goa_ladder.sh @@ -346,16 +346,17 @@ for AMT in "$@"; do "${BANK}/accounts/${EXP_USER}/withdrawals/${WID}/confirm" } extract_rpub() { + # Prefer *last* match (current withdraw), not first (stale from older accepts/tx). python3 -c ' import re, sys, json paths = sys.argv[1:] +found = [] blob = "" for p in paths: try: blob += open(p, errors="replace").read() + "\n" except Exception: pass -# JSON fields for pat in ( r"\"reserve_pub\"\s*:\s*\"([A-Z0-9]+)\"", r"\"reservePub\"\s*:\s*\"([A-Z0-9]+)\"", @@ -363,29 +364,26 @@ for pat in ( r"reservePub[\"\s:=]+([A-Z0-9]{40,})", r"reserve[_ ]?pub[\"=: ]+([A-Z0-9]{40,})", ): - m = re.search(pat, blob, re.I) - if m: - print(m.group(1)) - raise SystemExit -# line-wise JSON + found.extend(m.group(1) for m in re.finditer(pat, blob, re.I)) +def walk(o): + if isinstance(o, dict): + for k, v in o.items(): + if k.lower() in ("reserve_pub", "reservepub") and isinstance(v, str) and len(v) >= 40: + found.append(v) + walk(v) + elif isinstance(o, list): + for i in o: + walk(i) for line in blob.splitlines(): line = line.strip() if not line.startswith("{"): continue try: - d = json.loads(line) + walk(json.loads(line)) except Exception: - continue - def walk(o): - if isinstance(o, dict): - for k, v in o.items(): - if k.lower() in ("reserve_pub", "reservepub") and isinstance(v, str) and len(v) >= 40: - print(v); raise SystemExit - walk(v) - elif isinstance(o, list): - for i in o: - walk(i) - walk(d) + pass +if found: + print(found[-1]) ' "$@" 2>/dev/null || true } @@ -393,10 +391,11 @@ for line in blob.splitlines(): local st_now="$1" [ "$st_now" = "pending" ] || [ -z "$st_now" ] || return 0 local rpub epayto code_fs - rpub=$(extract_rpub "$SCRATCH/accept-$tag.out" "$SCRATCH/tx-$tag.json") + # current accept only first — avoid reusing reserve from previous rungs via tx dump + rpub=$(extract_rpub "$SCRATCH/accept-$tag.out") if [ -z "$rpub" ]; then wcli transactions >"$SCRATCH/tx-$tag.json" 2>&1 || true - rpub=$(extract_rpub "$SCRATCH/accept-$tag.out" "$SCRATCH/tx-$tag.json") + rpub=$(extract_rpub "$SCRATCH/tx-$tag.json") fi epayto=$(curl -sS -m 10 "${EX%/}/keys" 2>/dev/null | python3 -c ' import json,sys @@ -414,7 +413,16 @@ else: -H 'Content-Type: application/json' \ -d "{\"reserve_pub\":\"${rpub}\",\"selected_exchange\":\"${epayto}\"}" \ "${BANK}/taler-integration/withdrawal-operation/${WID}" 2>/dev/null || echo "000") - info "force-select" "HTTP ${code_fs} rpub=${rpub:0:12}…" + # 409: conflict (wrong/stale reserve, or already bound) — log body once, keep polling + if [ "$code_fs" = "409" ]; then + if [ "${FORCE_SEL_409_LOGGED:-0}" != "1" ]; then + info "force-select" "HTTP 409 rpub=${rpub:0:12}… body=$(tr '\n' ' ' <"$SCRATCH/force-sel-$tag.json" | head -c 160)" + FORCE_SEL_409_LOGGED=1 + fi + else + info "force-select" "HTTP ${code_fs} rpub=${rpub:0:12}…" + FORCE_SEL_409_LOGGED=0 + fi else _rpub_empty=no [ -z "$rpub" ] && _rpub_empty=yes @@ -452,13 +460,13 @@ else: break ;; esac - # aggressive force-select while pending (no run-until-done) + # force-select while pending (no run-until-done); avoid spam on repeated 409 if [ "$st" = "pending" ] || [ -z "$st" ]; then - if [ "$i" -eq 1 ] || [ $((i % 3)) -eq 0 ]; then + if [ "$i" -eq 1 ] || [ "$i" -eq 2 ] || [ $((i % 5)) -eq 0 ]; then force_select_if_needed "$st" fi fi - sleep 0.4 + sleep 0.35 done ms_confirm=$(elapsed_ms "$t0") st=$(printf '%s' "${st:-}" | tr -d '\r\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')