scripts: sanity/dns/wallet-cli helpers; ops cleanup
This commit is contained in:
parent
1181680a4b
commit
be05666737
18 changed files with 1440 additions and 0 deletions
106
scripts/ops/cleanup-koopa-all.sh
Executable file
106
scripts/ops/cleanup-koopa-all.sh
Executable file
|
|
@ -0,0 +1,106 @@
|
|||
#!/bin/bash
|
||||
# Clean agent scratch on koopa host (/root, /home/hernani, /tmp) + taler containers.
|
||||
# KEEP: passwords, koopa-admin-log, user media, container configs/data/logs essential.
|
||||
# Run as root on koopa.
|
||||
set +e
|
||||
[ "$(id -u)" -eq 0 ] || { echo "root only"; exit 1; }
|
||||
OUT="${1:-/tmp/cleanup-koopa-all.out}"
|
||||
exec > >(tee "$OUT") 2>&1
|
||||
n=0
|
||||
rmf() {
|
||||
for f in "$@"; do
|
||||
[ -e "$f" ] || [ -L "$f" ] || continue
|
||||
rm -rf -- "$f" && n=$((n+1)) && echo "RM $f"
|
||||
done
|
||||
}
|
||||
|
||||
echo "=== A. /tmp agent files ==="
|
||||
# delete known agent patterns only (not systemd private dirs)
|
||||
find /tmp -maxdepth 1 -type f \( \
|
||||
-name 'bench-*' -o -name 'settle-*' -o -name 'extreme-*' -o -name 'deploy-*' \
|
||||
-o -name 'force-*' -o -name 'fix-*' -o -name 'fix2*' -o -name 'wd*' \
|
||||
-o -name 'ord*' -o -name 'ex-*' -o -name 'tok*' -o -name 'facade*' \
|
||||
-o -name 'rev*' -o -name 'acc*' -o -name 'acw*' -o -name 'at.*' \
|
||||
-o -name 'et-*' -o -name '*settle*' -o -name '*wire*' -o -name '*offline*' \
|
||||
-o -name '*denom*' -o -name '*probe*' -o -name '*diag*' -o -name '*e2e*' \
|
||||
-o -name 'oid*' -o -name 'otok*' -o -name 'wuri*' -o -name 'wid*' \
|
||||
-o -name 'pay*' -o -name 'mk-*' -o -name 'koopa-*' -o -name 'incont*' \
|
||||
-o -name 'in-container*' -o -name 'credit*' -o -name 'apply-*' -o -name 'tos*' \
|
||||
-o -name 'redeploy*' -o -name 'ensure*' -o -name 'xfer*' -o -name 'tpl*' \
|
||||
-o -name 'future-keys.json' -o -name 'signed-keys.json' -o -name 'keys.json' \
|
||||
-o -name 'ex-acc*' -o -name 'bpw*' -o -name 'mpw*' -o -name 'wg*' \
|
||||
-o -name 'cleanup-*' -o -name 'koopa-clean-*' -o -name 'force-confirm*' \
|
||||
-o -name 'denom-probe*' -o -name 'deploy-large*' -o -name 'bench-setup*' \
|
||||
-o -name '*.out' -o -name '*.sh' -o -name '*.json' -o -name '*.sql' \
|
||||
-o -name '*.uri' \
|
||||
\) -print -delete 2>/dev/null
|
||||
# leftover txt from agent (not system)
|
||||
find /tmp -maxdepth 1 -type f -name '*.txt' \( -user root -o -user hernani \) -print -delete 2>/dev/null
|
||||
echo "tmp files left: $(find /tmp -maxdepth 1 -type f 2>/dev/null | wc -l)"
|
||||
|
||||
echo "=== B. /root loose agent files (keep *password*) ==="
|
||||
for f in /root/*; do
|
||||
base=$(basename "$f")
|
||||
[ -e "$f" ] || continue
|
||||
case "$base" in
|
||||
*password*|*secret*|koopa-admin-log|.ssh|.bashrc|.profile|.bash_history)
|
||||
echo "KEEP $f"; continue ;;
|
||||
esac
|
||||
# keep directories that look like system (not agent one-offs)
|
||||
if [ -d "$f" ]; then
|
||||
case "$base" in
|
||||
koopa-admin-log|go|.cache|.config|.local) echo "KEEP dir $f"; continue ;;
|
||||
esac
|
||||
# only remove empty agent-looking dirs
|
||||
continue
|
||||
fi
|
||||
case "$base" in
|
||||
*.sh|*.out|*.json|*.uri|*.py|*.sql|*.log|*.conf)
|
||||
rmf "$f" ;;
|
||||
c1|chk-order|mk-order|d3|d4|d5|d6|d7|d8|dd|dd2|f1|fdo|fsn|ps|rl|sc|ss|ww4)
|
||||
rmf "$f" ;;
|
||||
esac
|
||||
done
|
||||
rmf /root/.tmp /root/.tmp-cleanup
|
||||
|
||||
echo "=== C. /home/hernani agent staging ==="
|
||||
H=/home/hernani
|
||||
rmf "$H/ex-acc.secret.conf" "$H/ex-acc2.secret.conf"
|
||||
rmf "$H/bank-bench-fat-password.txt"
|
||||
find "$H" -maxdepth 1 -type f \( \
|
||||
-name '*.sh' -o -name '*.out' -o -name '*.json' -o -name '*.uri' -o -name '*.sql' \
|
||||
-o -name 'ex-acc*' -o -name 'exchange-coins.conf' -o -name 'bench-*' \
|
||||
-o -name 'oid-*' -o -name 'otok-*' -o -name 'wuri.txt' -o -name 'wid.txt' \
|
||||
-o -name 'pay*.uri' -o -name 'e2e-*' -o -name 'd[0-9]*' -o -name 'f[0-9]*' \
|
||||
-o -name 'ww*' -o -name 'dd*' -o -name 'fix-*' -o -name 'mk-*' \
|
||||
-o -name 'cycle-*' -o -name 'inside-*' -o -name 'curl' -o -name 'et-long.json' \
|
||||
-o -name 'find-delay*' -o -name 'deploy-delays*' -o -name 'offline-sign*' \
|
||||
-o -name 'in-container*' -o -name 'facade*' -o -name '*password*' \
|
||||
\) -print -delete 2>/dev/null
|
||||
# chown anything we might have left wrong
|
||||
chown -R hernani:hernani "$H" 2>/dev/null || true
|
||||
|
||||
echo "=== D. containers (tmp/scratch only) ==="
|
||||
# list
|
||||
su - hernani -c 'podman ps --format "{{.Names}}"' | tee /tmp/pod-names.txt
|
||||
while read -r c; do
|
||||
[ -n "$c" ] || continue
|
||||
echo "--- clean container $c ---"
|
||||
su - hernani -c "podman exec -u root $c bash -c '
|
||||
set +e
|
||||
# agent scratch in /tmp and /root
|
||||
find /tmp -maxdepth 1 -type f \( -name \"*.out\" -o -name \"*.sh\" -o -name \"*.json\" -o -name \"future-keys.json\" -o -name \"signed-keys.json\" -o -name \"bench*\" -o -name \"settle*\" -o -name \"facade*\" -o -name \"ex-acc*\" -o -name \"wd*\" -o -name \"ord*\" -o -name \"tpl*\" -o -name \"keys.json\" -o -name \"in-container*\" -o -name \"offline*\" \) -delete 2>/dev/null
|
||||
find /root -maxdepth 1 -type f \( -name \"*.out\" -o -name \"*.sh\" -o -name \"*.json\" -o -name \"in-container*\" -o -name \"offline-sign*\" -o -name \"bench*\" -o -name \"facade*\" \) ! -name \".bashrc\" ! -name \".profile\" -delete 2>/dev/null
|
||||
# never delete /etc configs, /var/lib data, password secrets under /etc
|
||||
echo tmp_files=\$(find /tmp -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
echo root_loose=\$(find /root -maxdepth 1 -type f 2>/dev/null | wc -l)
|
||||
'" 2>&1 | tail -20
|
||||
done < /tmp/pod-names.txt
|
||||
|
||||
echo "=== E. summary host ==="
|
||||
echo "root:"; ls -la /root | head -40
|
||||
echo "hernani top:"; ls -la /home/hernani | head -30
|
||||
echo "tmp count: $(find /tmp -maxdepth 1 -type f 2>/dev/null | wc -l)"
|
||||
echo "passwords:"; ls -la /root/*password* 2>/dev/null
|
||||
echo "n_rm_hostish=$n"
|
||||
echo CLEAN_ALL_OK
|
||||
84
scripts/ops/cleanup-root-scratch.sh
Normal file
84
scripts/ops/cleanup-root-scratch.sh
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#!/bin/bash
|
||||
# Clean agent/ops clutter under /root on koopa (host).
|
||||
# Preferred scratch is LOCAL: koopa-admin-log/.tmp/ (see ops/ROOT_HYGIENE.md).
|
||||
# This script only removes leftover one-offs that landed in /root by mistake.
|
||||
#
|
||||
# Usage (as root on koopa):
|
||||
# cleanup-root-scratch.sh
|
||||
# cleanup-root-scratch.sh --dry-run
|
||||
set -euo pipefail
|
||||
[ "$(id -u)" -eq 0 ] || { echo "root only"; exit 1; }
|
||||
|
||||
DRY=0
|
||||
[ "${1:-}" = "--dry-run" ] && DRY=1
|
||||
|
||||
# Staging on host only to delete (not the canonical scratch location)
|
||||
TMP_BASE=/root/.tmp-cleanup
|
||||
SESSION_DIR="$TMP_BASE/session-$(date -u +%Y%m%d-%H%M)-cleanup"
|
||||
mkdir -p "$SESSION_DIR"
|
||||
|
||||
# One-off probe filenames created during taler settle/withdraw debugging (not durable).
|
||||
JUNK_GLOBS=(
|
||||
c1.sh chk-order.sh mk-order.sh mk-secret.py
|
||||
d3.sh d4.sh d5.sh d6.sh d7.sh d8.sh dd.sh dd2.sh
|
||||
f1.sh fdo.sh fsn.sh fix-perm2.sh fix-ww.sh
|
||||
ps.sh rl.sh sc.sh ss.sh ww4.sh
|
||||
exchange-overrides.conf
|
||||
)
|
||||
|
||||
run() {
|
||||
if [ "$DRY" -eq 1 ]; then
|
||||
echo "DRY: $*"
|
||||
else
|
||||
eval "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== move junk scripts to $SESSION_DIR ==="
|
||||
mkdir -p "$SESSION_DIR"
|
||||
moved=0
|
||||
for name in "${JUNK_GLOBS[@]}"; do
|
||||
f="/root/$name"
|
||||
if [ -e "$f" ]; then
|
||||
run "mv -f $(printf %q "$f") $(printf %q "$SESSION_DIR/")"
|
||||
echo " moved $name"
|
||||
moved=$((moved + 1))
|
||||
fi
|
||||
done
|
||||
echo "moved=$moved"
|
||||
|
||||
# Durable scripts that belong in admin-log — re-install from tree if present, remove loose copies
|
||||
ADMIN="${KOOPA_ADMIN_LOG:-/root/koopa-admin-log}"
|
||||
if [ ! -d "$ADMIN/scripts" ]; then
|
||||
ADMIN=/home/hernani/koopa-admin-log
|
||||
fi
|
||||
for pair in \
|
||||
"auto-confirm-withdrawals.sh:taler-bank/auto-confirm-withdrawals.sh" \
|
||||
"make-demo-withdraw-qr.sh:taler-bank/make-demo-withdraw-qr.sh"
|
||||
do
|
||||
base="${pair%%:*}"
|
||||
rel="${pair#*:}"
|
||||
src="$ADMIN/scripts/$rel"
|
||||
if [ -f "/root/$base" ]; then
|
||||
if [ -f "$src" ]; then
|
||||
echo " /root/$base is durable → install to /usr/local/bin, remove from /root"
|
||||
run "install -m 755 $(printf %q "$src") /usr/local/bin/$base"
|
||||
run "rm -f /root/$base"
|
||||
else
|
||||
echo " keep /root/$base (no admin-log copy at $src); move to $ADMIN/scripts if you can"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=== delete staged junk (canonical scratch is local koopa-admin-log/.tmp/) ==="
|
||||
if [ "$DRY" -eq 1 ]; then
|
||||
ls -la "$SESSION_DIR" 2>/dev/null || true
|
||||
else
|
||||
rm -rf "$TMP_BASE"
|
||||
# remove mistaken host scratch if present
|
||||
rm -rf /root/.tmp
|
||||
fi
|
||||
|
||||
echo "=== remaining non-dot files in /root ==="
|
||||
ls -la /root | grep -v '^\.' | head -60
|
||||
echo "DONE"
|
||||
48
scripts/taler-dns/README.md
Normal file
48
scripts/taler-dns/README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
# Container DNS pins (hacktivism.ch)
|
||||
|
||||
Pasta networking often has **no usable DNS**. Wirewatch needs:
|
||||
|
||||
```text
|
||||
https://bank.hacktivism.ch/accounts/exchange/taler-wire-gateway/
|
||||
```
|
||||
|
||||
## Bake into images (preferred)
|
||||
|
||||
On koopa:
|
||||
|
||||
```bash
|
||||
# copy bake assets, then:
|
||||
SRC_DIR=/tmp/taler-dns-bake ./bake-hosts-into-images.sh
|
||||
```
|
||||
|
||||
This:
|
||||
|
||||
1. Installs `/usr/local/bin/pin-hacktivism-hosts` **inside** each container
|
||||
2. Hooks `start_base_services_for_taler*.sh` (bank + merchant)
|
||||
3. Enables systemd `pin-hacktivism-hosts.service` (exchange, before wirewatch)
|
||||
4. **Commits** images:
|
||||
- `localhost/taler-hacktivism-banking:live` (+ `:hosts-pinned`)
|
||||
- `localhost/taler-hacktivism-live:landing` (+ `:hosts-pinned`)
|
||||
- `localhost/taler-hacktivism-exchange-ansible:landing` (+ `:hosts-pinned`)
|
||||
|
||||
Pin IP default: `212.51.151.254` (`PIN_IP=` to override).
|
||||
|
||||
## One-shot without bake
|
||||
|
||||
```bash
|
||||
./pin-container-hosts.sh # apply now
|
||||
./pin-container-hosts.sh --check # report resolve + /config
|
||||
```
|
||||
|
||||
## After recreate
|
||||
|
||||
If you start from an **old** image without the bake, run `pin-container-hosts.sh` once.
|
||||
If you use `:hosts-pinned` / updated `:live`/`:landing` images, start_base / systemd re-pin on boot.
|
||||
|
||||
Optional podman flags when recreating (extra safety):
|
||||
|
||||
```bash
|
||||
--add-host=bank.hacktivism.ch:212.51.151.254 \
|
||||
--add-host=exchange.hacktivism.ch:212.51.151.254 \
|
||||
--add-host=taler.hacktivism.ch:212.51.151.254
|
||||
```
|
||||
116
scripts/taler-dns/bake-hosts-into-images.sh
Normal file
116
scripts/taler-dns/bake-hosts-into-images.sh
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#!/bin/bash
|
||||
# Install in-container pin script (+ systemd on exchange), hook start_base scripts,
|
||||
# commit images so recreate keeps the bake.
|
||||
#
|
||||
# Run on koopa:
|
||||
# ./bake-hosts-into-images.sh
|
||||
set -euo pipefail
|
||||
|
||||
PIN_IP="${PIN_IP:-212.51.151.254}"
|
||||
SRC_DIR="${SRC_DIR:-/tmp/taler-dns-bake}"
|
||||
mkdir -p "$SRC_DIR"
|
||||
|
||||
# expect these next to us or in SRC_DIR
|
||||
SCRIPT_IN="${SCRIPT_IN:-$SRC_DIR/pin-hacktivism-hosts.in-container.sh}"
|
||||
UNIT_IN="${UNIT_IN:-$SRC_DIR/pin-hacktivism-hosts.service}"
|
||||
|
||||
[ -f "$SCRIPT_IN" ] || { echo "missing $SCRIPT_IN" >&2; exit 1; }
|
||||
|
||||
install_into() {
|
||||
local C="$1"
|
||||
if ! podman ps --format '{{.Names}}' | grep -qx "$C"; then
|
||||
echo "SKIP $C (not running)"
|
||||
return 0
|
||||
fi
|
||||
echo "=== install into $C ==="
|
||||
podman cp "$SCRIPT_IN" "$C:/usr/local/bin/pin-hacktivism-hosts"
|
||||
podman exec "$C" chmod 755 /usr/local/bin/pin-hacktivism-hosts
|
||||
# run once now
|
||||
podman exec -e PIN_IP="$PIN_IP" "$C" /usr/local/bin/pin-hacktivism-hosts
|
||||
podman exec "$C" grep hacktivism /etc/hosts || true
|
||||
}
|
||||
|
||||
hook_start_base() {
|
||||
local C="$1" base="$2"
|
||||
podman exec "$C" bash -lc "
|
||||
set -e
|
||||
f='$base'
|
||||
if [ ! -f \"\$f\" ]; then echo \"no \$f\"; exit 0; fi
|
||||
if grep -q pin-hacktivism-hosts \"\$f\"; then
|
||||
echo \"already hooked: \$f\"
|
||||
exit 0
|
||||
fi
|
||||
# insert right after shebang / first line block
|
||||
cp \"\$f\" \"\$f.bak-before-pin\"
|
||||
{
|
||||
head -n 1 \"\$f\"
|
||||
echo ''
|
||||
echo '# Pin public GOA hostnames (pasta DNS). Baked by bake-hosts-into-images.sh'
|
||||
echo 'if [ -x /usr/local/bin/pin-hacktivism-hosts ]; then'
|
||||
echo ' PIN_IP=${PIN_IP} /usr/local/bin/pin-hacktivism-hosts || true'
|
||||
echo 'fi'
|
||||
tail -n +2 \"\$f\"
|
||||
} > \"\$f.new\"
|
||||
mv \"\$f.new\" \"\$f\"
|
||||
chmod +x \"\$f\"
|
||||
echo \"hooked \$f\"
|
||||
"
|
||||
}
|
||||
|
||||
# --- bank ---
|
||||
install_into taler-hacktivism-bank
|
||||
hook_start_base taler-hacktivism-bank /root/start_base_services_for_taler_bank.sh
|
||||
|
||||
# --- merchant ---
|
||||
install_into taler-hacktivism
|
||||
hook_start_base taler-hacktivism /root/start_base_services_for_taler.sh
|
||||
|
||||
# --- exchange (systemd) ---
|
||||
install_into taler-hacktivism-exchange-ansible
|
||||
if [ -f "$UNIT_IN" ]; then
|
||||
podman cp "$UNIT_IN" taler-hacktivism-exchange-ansible:/etc/systemd/system/pin-hacktivism-hosts.service
|
||||
podman exec taler-hacktivism-exchange-ansible bash -lc '
|
||||
sed -i "s/PIN_IP=212.51.151.254/PIN_IP='"${PIN_IP}"'/" /etc/systemd/system/pin-hacktivism-hosts.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable pin-hacktivism-hosts.service
|
||||
systemctl start pin-hacktivism-hosts.service
|
||||
systemctl is-enabled pin-hacktivism-hosts.service
|
||||
# ensure wirewatch starts after pin
|
||||
mkdir -p /etc/systemd/system/taler-exchange-wirewatch.service.d
|
||||
cat > /etc/systemd/system/taler-exchange-wirewatch.service.d/pin-hosts.conf <<EOF
|
||||
[Unit]
|
||||
After=pin-hacktivism-hosts.service
|
||||
Requires=pin-hacktivism-hosts.service
|
||||
EOF
|
||||
systemctl daemon-reload
|
||||
systemctl restart taler-exchange-wirewatch 2>/dev/null || true
|
||||
'
|
||||
fi
|
||||
|
||||
# --- commit images ---
|
||||
echo "=== commit images ==="
|
||||
podman commit taler-hacktivism-bank \
|
||||
localhost/taler-hacktivism-banking:live
|
||||
podman commit taler-hacktivism-bank \
|
||||
localhost/taler-hacktivism-banking:hosts-pinned
|
||||
|
||||
podman commit taler-hacktivism \
|
||||
localhost/taler-hacktivism-live:landing
|
||||
podman commit taler-hacktivism \
|
||||
localhost/taler-hacktivism-live:hosts-pinned
|
||||
|
||||
podman commit taler-hacktivism-exchange-ansible \
|
||||
localhost/taler-hacktivism-exchange-ansible:landing
|
||||
podman commit taler-hacktivism-exchange-ansible \
|
||||
localhost/taler-hacktivism-exchange-ansible:hosts-pinned
|
||||
|
||||
echo
|
||||
echo "Images updated:"
|
||||
podman images | grep -E 'hacktivism|hosts-pinned' | head -20
|
||||
echo
|
||||
echo "Verify:"
|
||||
for C in taler-hacktivism-bank taler-hacktivism-exchange-ansible taler-hacktivism; do
|
||||
echo -n "$C: "
|
||||
podman exec "$C" getent ahostsv4 bank.hacktivism.ch 2>/dev/null | head -1 || echo FAIL
|
||||
done
|
||||
echo "done."
|
||||
82
scripts/taler-dns/pin-container-hosts.sh
Executable file
82
scripts/taler-dns/pin-container-hosts.sh
Executable file
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
# Pin public GOA hostnames inside Taler containers (pasta often has no useful DNS).
|
||||
# Required so wirewatch can reach https://bank.hacktivism.ch/... wire-gateway.
|
||||
#
|
||||
# Run on koopa (host):
|
||||
# ./pin-container-hosts.sh # apply + show resolve
|
||||
# ./pin-container-hosts.sh --check # only show
|
||||
# PIN_IP=212.51.151.254 ./pin-container-hosts.sh
|
||||
set -euo pipefail
|
||||
|
||||
CHECK_ONLY=0
|
||||
[ "${1:-}" = "--check" ] && CHECK_ONLY=1
|
||||
|
||||
DOMAINS=(bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch)
|
||||
CONTAINERS=(
|
||||
taler-hacktivism-bank
|
||||
taler-hacktivism-exchange-ansible
|
||||
taler-hacktivism
|
||||
)
|
||||
|
||||
PIN_IP="${PIN_IP:-}"
|
||||
if [ -z "$PIN_IP" ]; then
|
||||
PIN_IP=$(getent ahostsv4 bank.hacktivism.ch 2>/dev/null | awk '{print $1; exit}' || true)
|
||||
fi
|
||||
[ -n "$PIN_IP" ] || PIN_IP=212.51.151.254
|
||||
|
||||
echo "PIN_IP=$PIN_IP"
|
||||
echo "domains: ${DOMAINS[*]}"
|
||||
echo
|
||||
|
||||
pin_one() {
|
||||
local C="$1"
|
||||
if ! podman ps --format '{{.Names}}' | grep -qx "$C"; then
|
||||
echo "=== $C === SKIP (not running)"
|
||||
return 0
|
||||
fi
|
||||
if [ "$CHECK_ONLY" != "1" ]; then
|
||||
# Keep localhost lines; drop previous hacktivism pins; append ours
|
||||
podman exec "$C" bash -lc "
|
||||
set -e
|
||||
TMP=\$(mktemp)
|
||||
# keep non-hacktivism lines
|
||||
if [ -f /etc/hosts ]; then
|
||||
grep -vE 'hacktivism\\.ch|[[:space:]]bank\\.hacktivism|[[:space:]]exchange\\.hacktivism|[[:space:]]taler\\.hacktivism' /etc/hosts > \"\$TMP\" || true
|
||||
fi
|
||||
# ensure localhost
|
||||
grep -qE '^127\\.0\\.0\\.1[[:space:]]+localhost' \"\$TMP\" 2>/dev/null || echo '127.0.0.1 localhost' >> \"\$TMP\"
|
||||
grep -qE '^::1[[:space:]]' \"\$TMP\" 2>/dev/null || echo '::1 localhost ip6-localhost ip6-loopback' >> \"\$TMP\"
|
||||
echo '${PIN_IP} bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch' >> \"\$TMP\"
|
||||
# de-dupe consecutive blank lines lightly
|
||||
cat \"\$TMP\" > /etc/hosts
|
||||
rm -f \"\$TMP\"
|
||||
"
|
||||
# wirewatch needs bank DNS after pin
|
||||
if [ "$C" = "taler-hacktivism-exchange-ansible" ]; then
|
||||
podman exec "$C" systemctl try-restart taler-exchange-wirewatch 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== $C ==="
|
||||
podman exec "$C" grep -E 'hacktivism|localhost' /etc/hosts 2>/dev/null || true
|
||||
for h in "${DOMAINS[@]}"; do
|
||||
# ahostsv4 first (matches what wire tools need)
|
||||
ip=$(podman exec "$C" getent ahostsv4 "$h" 2>/dev/null | awk '{print $1; exit}' || true)
|
||||
[ -n "$ip" ] || ip=$(podman exec "$C" getent hosts "$h" 2>/dev/null | awk '{print $1; exit}' || true)
|
||||
code=$(podman exec "$C" curl -skS -m 4 -o /dev/null -w '%{http_code}' "https://${h}/config" 2>/dev/null || echo 000)
|
||||
if [ -n "$ip" ] && [ "$code" = "200" ]; then
|
||||
echo " OK $h → $ip /config=$code"
|
||||
elif [ -n "$ip" ]; then
|
||||
echo " WARN $h → $ip /config=$code"
|
||||
else
|
||||
echo " FAIL $h → (no resolve) /config=$code"
|
||||
fi
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
for C in "${CONTAINERS[@]}"; do
|
||||
pin_one "$C"
|
||||
done
|
||||
|
||||
echo "done (CHECK_ONLY=$CHECK_ONLY)"
|
||||
31
scripts/taler-dns/pin-hacktivism-hosts.in-container.sh
Normal file
31
scripts/taler-dns/pin-hacktivism-hosts.in-container.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
# Runs *inside* a Taler container. Pasta regenerates /etc/hosts on start —
|
||||
# call this from systemd (exchange) or start_base_*.sh (merchant/bank).
|
||||
set -euo pipefail
|
||||
|
||||
PIN_IP="${PIN_IP:-212.51.151.254}"
|
||||
MARKER="# hacktivism-goa-pin"
|
||||
|
||||
TMP=$(mktemp)
|
||||
if [ -f /etc/hosts ]; then
|
||||
# drop any previous pin marker lines and lines that name our domains
|
||||
grep -vE "${MARKER}|hacktivism\\.ch" /etc/hosts >"$TMP" || true
|
||||
else
|
||||
: >"$TMP"
|
||||
fi
|
||||
|
||||
grep -qE '^127\.0\.0\.1[[:space:]]' "$TMP" 2>/dev/null \
|
||||
|| echo '127.0.0.1 localhost' >>"$TMP"
|
||||
grep -qE '^::1[[:space:]]' "$TMP" 2>/dev/null \
|
||||
|| echo '::1 localhost ip6-localhost ip6-loopback' >>"$TMP"
|
||||
|
||||
{
|
||||
cat "$TMP"
|
||||
echo "$MARKER"
|
||||
echo "${PIN_IP} bank.hacktivism.ch exchange.hacktivism.ch taler.hacktivism.ch"
|
||||
echo "$MARKER"
|
||||
} > /etc/hosts
|
||||
rm -f "$TMP"
|
||||
|
||||
logger -t pin-hacktivism-hosts "pinned ${PIN_IP} → bank/exchange/taler.hacktivism.ch" 2>/dev/null || true
|
||||
exit 0
|
||||
16
scripts/taler-dns/pin-hacktivism-hosts.service
Normal file
16
scripts/taler-dns/pin-hacktivism-hosts.service
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[Unit]
|
||||
Description=Pin bank/exchange/taler.hacktivism.ch in /etc/hosts (pasta DNS)
|
||||
DefaultDependencies=no
|
||||
After=local-fs.target
|
||||
Before=network-online.target taler-exchange-wirewatch.service taler-exchange-httpd.service
|
||||
Wants=network-pre.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
Environment=PIN_IP=212.51.151.254
|
||||
ExecStart=/usr/local/bin/pin-hacktivism-hosts
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=taler-exchange-wirewatch.service
|
||||
31
scripts/taler-sanity/README.md
Normal file
31
scripts/taler-sanity/README.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Taler sanity checks (koopa)
|
||||
|
||||
Run **on koopa as root** (reads `/root/*-password.txt`, may use `podman` as `hernani`).
|
||||
|
||||
| Script | What it checks |
|
||||
|--------|----------------|
|
||||
| `check_helpers-running.sh` | **No systemd:** starts missing exchange/merchant helpers (`ensure_*`), then requires transfer/aggregator/wirewatch/… |
|
||||
| `check_stack-health.sh` | Public + local `/config` and exchange `/keys` HTTP 200; merchant has CHF+GOA |
|
||||
| `check_merchant-delays.sh` | Global + instance delays are short (pay/refund/wire ≤ 120s) |
|
||||
| `check_exchange-wirewatch.sh` | Wire gateway reachable with **bearer**; auth method; hosts pin |
|
||||
| `check_settlement.sh` | Paid orders: `wired`, deposit_total, bank balances/transfers |
|
||||
| `run_all.sh` | Runs all of the above |
|
||||
|
||||
Helper ensure scripts (in containers, no systemd):
|
||||
|
||||
| Script | Role |
|
||||
|--------|------|
|
||||
| `../taler-exchange/ensure_exchange_helpers.sh` | `nohup` aggregator, closer, wirewatch, **transfer** |
|
||||
| `../taler-merchant/ensure_merchant_helpers.sh` | `nohup` wirewatch, depositcheck, kyccheck, webhook, … |
|
||||
| Health: `check_*-health.sh` | Calls ensure (unless `SKIP_ENSURE=1`), then **FAIL** if still missing |
|
||||
|
||||
```bash
|
||||
# on koopa
|
||||
cd /path/to/koopa-admin-log/scripts/taler-sanity
|
||||
./run_all.sh
|
||||
./check_settlement.sh 2026.190-03V36SPAZ8CK6
|
||||
```
|
||||
|
||||
Env overrides: `BANK_URL`, `MERCHANT_URL`, `EXCHANGE_PUBLIC`, `BANK_PUBLIC`, `MERCHANT_PUBLIC`, `MERCHANT_INSTANCE`.
|
||||
|
||||
See also: `../taler-wallet-cli/` for end-to-end withdraw → pay → settlement.
|
||||
92
scripts/taler-sanity/check_exchange-wirewatch.sh
Normal file
92
scripts/taler-sanity/check_exchange-wirewatch.sh
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/bash
|
||||
# Sanity: exchange can read bank wire gateway (bearer + public URL).
|
||||
# Run on koopa as root (needs /root/bank-exchange-password.txt + podman).
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
# shellcheck source=lib.sh
|
||||
source "$ROOT/lib.sh"
|
||||
need_root
|
||||
|
||||
echo "=== Exchange wirewatch / wire gateway ==="
|
||||
|
||||
if ! read_pw EPW /root/bank-exchange-password.txt; then
|
||||
fail "missing /root/bank-exchange-password.txt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ET=$(bank_token exchange "$EPW")
|
||||
if [ -z "$ET" ]; then
|
||||
fail "could not get bank token for exchange user"
|
||||
exit 1
|
||||
fi
|
||||
pass "bank token for exchange user"
|
||||
|
||||
# History must work with Bearer (Basic is rejected by libeufin on history)
|
||||
tmp=$(mktemp)
|
||||
code=$(curl -sk -m 15 -o "$tmp" -w '%{http_code}' \
|
||||
-H "Authorization: Bearer ${ET}" \
|
||||
"${BANK_PUBLIC}/accounts/exchange/taler-wire-gateway/history/incoming?delta=-10")
|
||||
if [ "$code" = "200" ]; then
|
||||
pass "wire gateway history via ${BANK_PUBLIC} (HTTP $code)"
|
||||
python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); print(" incoming_count", len(d.get("incoming_transactions") or []))' "$tmp"
|
||||
else
|
||||
# fallback local
|
||||
code2=$(curl -sS -m 15 -o "$tmp" -w '%{http_code}' \
|
||||
-H "Authorization: Bearer ${ET}" \
|
||||
"${BANK_URL}/accounts/exchange/taler-wire-gateway/history/incoming?delta=-10")
|
||||
if [ "$code2" = "200" ]; then
|
||||
warn "public wire gateway HTTP $code; local BANK_URL OK ($code2) — check DNS/hosts in exchange container"
|
||||
else
|
||||
fail "wire gateway history public=$code local=$code2"
|
||||
fi
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
|
||||
# Basic must fail on history (documents expected auth mode)
|
||||
code_b=$(curl -sk -m 10 -o /dev/null -w '%{http_code}' \
|
||||
-u "exchange:${EPW}" \
|
||||
"${BANK_URL}/accounts/exchange/taler-wire-gateway/history/incoming?delta=-5")
|
||||
if [ "$code_b" = "401" ]; then
|
||||
pass "Basic auth correctly rejected on history ($code_b) — use bearer TOKEN"
|
||||
else
|
||||
warn "Basic on history returned $code_b (expected 401)"
|
||||
fi
|
||||
|
||||
# Container credential + wirewatch process
|
||||
if su - hernani -c 'podman exec taler-exchange-hacktivism true' 2>/dev/null; then
|
||||
su - hernani -c 'podman exec taler-exchange-hacktivism bash -c "
|
||||
set +e
|
||||
echo \"--- secret (redacted) ---\"
|
||||
if [ -r /etc/taler-exchange/secrets/exchange-accountcredentials-1.secret.conf ]; then
|
||||
sed \"s/secret-token:.*/secret-token:***/; s/^TOKEN = .*/TOKEN = ***/; s/^PASSWORD = .*/PASSWORD = ***/\" \
|
||||
/etc/taler-exchange/secrets/exchange-accountcredentials-1.secret.conf
|
||||
else
|
||||
# root-only file: cat as root via outer exec
|
||||
true
|
||||
fi
|
||||
echo \"--- hosts bank ---\"
|
||||
grep bank.hacktivism.ch /etc/hosts || echo \"(no hosts pin)\"
|
||||
echo \"--- wirewatch ---\"
|
||||
ps -eo pid,stat,args | grep \"[w]irewatch\" || echo \"(no wirewatch process)\"
|
||||
"' 2>&1 || warn "podman exec exchange failed"
|
||||
|
||||
AUTH_METHOD=$(su - hernani -c 'podman exec -u root taler-exchange-hacktivism \
|
||||
grep -E "^WIRE_GATEWAY_AUTH_METHOD" /etc/taler-exchange/secrets/exchange-accountcredentials-1.secret.conf' 2>/dev/null | awk '{print $3}')
|
||||
GW_URL=$(su - hernani -c 'podman exec -u root taler-exchange-hacktivism \
|
||||
grep -E "^WIRE_GATEWAY_URL" /etc/taler-exchange/secrets/exchange-accountcredentials-1.secret.conf' 2>/dev/null | awk '{print $3}')
|
||||
info "WIRE_GATEWAY_URL=$GW_URL"
|
||||
info "WIRE_GATEWAY_AUTH_METHOD=$AUTH_METHOD"
|
||||
if [ "$AUTH_METHOD" = "bearer" ]; then pass "auth method bearer"
|
||||
else fail "auth method is '$AUTH_METHOD' (want bearer + TOKEN=)"
|
||||
fi
|
||||
case "$GW_URL" in
|
||||
https://bank.hacktivism.ch/*) pass "gateway uses public bank URL" ;;
|
||||
http://127.*|http://host.containers*) warn "gateway uses local URL: $GW_URL" ;;
|
||||
*) warn "unexpected gateway URL: $GW_URL" ;;
|
||||
esac
|
||||
else
|
||||
warn "exchange container not reachable via podman"
|
||||
fi
|
||||
|
||||
echo "=== summary fails=$FAILS ==="
|
||||
exit "$FAILS"
|
||||
81
scripts/taler-sanity/check_helpers-running.sh
Executable file
81
scripts/taler-sanity/check_helpers-running.sh
Executable file
|
|
@ -0,0 +1,81 @@
|
|||
#!/bin/bash
|
||||
# Sanity: ensure exchange + merchant helpers are up (no systemd).
|
||||
# Runs on koopa as root; uses podman into both containers.
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
# shellcheck source=lib.sh
|
||||
source "$ROOT/lib.sh"
|
||||
need_root
|
||||
|
||||
fail=0
|
||||
pass() { echo "OK $1"; }
|
||||
warn() { echo "WARN $1"; }
|
||||
bad() { echo "FAIL $1"; fail=$((fail + 1)); }
|
||||
|
||||
EXC_CT="${EXCHANGE_CONTAINER:-taler-exchange-hacktivism}"
|
||||
MER_CT="${MERCHANT_CONTAINER:-taler-hacktivism}"
|
||||
|
||||
echo "=== ensure + check helpers (no systemd) ==="
|
||||
|
||||
# Deploy/ensure scripts if present on host under admin-log mount or /tmp
|
||||
deploy_into() {
|
||||
local ct="$1"
|
||||
local src="$2"
|
||||
local dst="$3"
|
||||
if [ -f "$src" ]; then
|
||||
su - hernani -c "podman cp $(printf %q "$src") ${ct}:${dst}" 2>/dev/null \
|
||||
|| podman cp "$src" "${ct}:${dst}" 2>/dev/null || true
|
||||
su - hernani -c "podman exec ${ct} chmod 755 ${dst}" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
ADMIN="${KOOPA_ADMIN_LOG:-/home/hernani/koopa-admin-log}"
|
||||
# Prefer workspace copy if synced; else scripts next to this file
|
||||
EX_SCRIPTS="$ROOT/../taler-exchange"
|
||||
MER_SCRIPTS="$ROOT/../taler-merchant"
|
||||
[ -d "$ADMIN/scripts/taler-exchange" ] && EX_SCRIPTS="$ADMIN/scripts/taler-exchange"
|
||||
[ -d "$ADMIN/scripts/taler-merchant" ] && MER_SCRIPTS="$ADMIN/scripts/taler-merchant"
|
||||
|
||||
deploy_into "$EXC_CT" "$EX_SCRIPTS/ensure_exchange_helpers.sh" /usr/local/bin/ensure_exchange_helpers.sh
|
||||
deploy_into "$EXC_CT" "$EX_SCRIPTS/check_exchange-health.sh" /usr/local/bin/check_exchange-health.sh
|
||||
deploy_into "$MER_CT" "$MER_SCRIPTS/ensure_merchant_helpers.sh" /usr/local/bin/ensure_merchant_helpers.sh
|
||||
deploy_into "$MER_CT" "$MER_SCRIPTS/check_merchant-health.sh" /usr/local/bin/check_merchant-health.sh
|
||||
|
||||
echo "--- exchange container: ensure ---"
|
||||
if su - hernani -c "podman exec $EXC_CT /usr/local/bin/ensure_exchange_helpers.sh"; then
|
||||
pass "exchange helpers ensure"
|
||||
else
|
||||
bad "exchange helpers ensure failed"
|
||||
fi
|
||||
|
||||
echo "--- merchant container: ensure ---"
|
||||
if su - hernani -c "podman exec $MER_CT /usr/local/bin/ensure_merchant_helpers.sh"; then
|
||||
pass "merchant helpers ensure"
|
||||
else
|
||||
bad "merchant helpers ensure failed"
|
||||
fi
|
||||
|
||||
echo "--- process presence ---"
|
||||
check_ct_proc() {
|
||||
local ct="$1"
|
||||
local p="$2"
|
||||
# COMM is 15 chars — match full cmdline
|
||||
if su - hernani -c "podman exec $ct pgrep -f '(^|/)${p}( |$)'" >/dev/null 2>&1; then
|
||||
pass "$ct: $p"
|
||||
else
|
||||
bad "$ct: $p missing"
|
||||
fi
|
||||
}
|
||||
|
||||
for p in taler-exchange-httpd taler-exchange-aggregator taler-exchange-transfer \
|
||||
taler-exchange-wirewatch taler-exchange-closer; do
|
||||
check_ct_proc "$EXC_CT" "$p"
|
||||
done
|
||||
for p in taler-merchant-httpd taler-merchant-wirewatch taler-merchant-depositcheck \
|
||||
taler-merchant-webhook taler-merchant-kyccheck \
|
||||
taler-merchant-exchangekeyupdate taler-merchant-reconciliation; do
|
||||
check_ct_proc "$MER_CT" "$p"
|
||||
done
|
||||
|
||||
echo "=== summary fails=$fail ==="
|
||||
exit "$fail"
|
||||
72
scripts/taler-sanity/check_merchant-delays.sh
Normal file
72
scripts/taler-sanity/check_merchant-delays.sh
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#!/bin/bash
|
||||
# Sanity: merchant global + demo instance delays are demo-short (pay/refund/wire).
|
||||
# Run on koopa (network to :9010 / public merchant).
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
# shellcheck source=lib.sh
|
||||
source "$ROOT/lib.sh"
|
||||
|
||||
# Expected upper bounds (seconds) — fail if longer (package defaults 1d/15d/1w)
|
||||
MAX_PAY_S=${MAX_PAY_S:-120}
|
||||
MAX_REFUND_S=${MAX_REFUND_S:-120}
|
||||
MAX_WIRE_S=${MAX_WIRE_S:-120}
|
||||
|
||||
echo "=== Merchant delay defaults (expect short demo values) ==="
|
||||
|
||||
tmp=$(mktemp)
|
||||
curl -sk -m 12 -o "$tmp" "${MERCHANT_PUBLIC}/config" || curl -sk -m 12 -o "$tmp" "${MERCHANT_URL}/config"
|
||||
python3 - "$tmp" "$MAX_PAY_S" "$MAX_REFUND_S" "$MAX_WIRE_S" <<'PY'
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
max_pay, max_ref, max_wire = map(float, sys.argv[2:5])
|
||||
fails=0
|
||||
for key, mx in (
|
||||
("default_pay_delay", max_pay),
|
||||
("default_refund_delay", max_ref),
|
||||
("default_wire_transfer_delay", max_wire),
|
||||
):
|
||||
us=d.get(key,{}).get("d_us")
|
||||
s=(us or 0)/1e6
|
||||
ok = us is not None and s <= mx
|
||||
print(f"{'OK' if ok else 'FAIL'} {key}: {s:.0f}s (max {mx:.0f}s)")
|
||||
if not ok: fails+=1
|
||||
print("currency", d.get("currency"))
|
||||
sys.exit(fails)
|
||||
PY
|
||||
ec=$?
|
||||
rm -f "$tmp"
|
||||
FAILS=$((FAILS + ec))
|
||||
|
||||
# Instance goa-demo if password present
|
||||
if [ -f /root/merchant-goa-demo-cp4zqk-password.txt ]; then
|
||||
MPW=$(tr -d '\n' </root/merchant-goa-demo-cp4zqk-password.txt)
|
||||
tmp=$(mktemp)
|
||||
curl -sk -m 12 -H "Authorization: Bearer secret-token:${MPW}" \
|
||||
-o "$tmp" "${MERCHANT_URL}/instances/goa-demo-cp4zqk/private/" || true
|
||||
if [ -s "$tmp" ]; then
|
||||
python3 - "$tmp" "$MAX_PAY_S" "$MAX_REFUND_S" "$MAX_WIRE_S" <<'PY'
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
max_pay, max_ref, max_wire = map(float, sys.argv[2:5])
|
||||
fails=0
|
||||
for key, mx in (
|
||||
("default_pay_delay", max_pay),
|
||||
("default_refund_delay", max_ref),
|
||||
("default_wire_transfer_delay", max_wire),
|
||||
):
|
||||
us=d.get(key,{}).get("d_us")
|
||||
s=(us or 0)/1e6
|
||||
ok = us is not None and s <= mx
|
||||
print(f"{'OK' if ok else 'FAIL'} instance {key}: {s:.0f}s")
|
||||
if not ok: fails+=1
|
||||
sys.exit(fails)
|
||||
PY
|
||||
FAILS=$((FAILS + $?))
|
||||
else
|
||||
warn "could not load instance goa-demo-cp4zqk"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
fi
|
||||
|
||||
echo "=== summary fails=$FAILS ==="
|
||||
exit "$FAILS"
|
||||
119
scripts/taler-sanity/check_settlement.sh
Normal file
119
scripts/taler-sanity/check_settlement.sh
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#!/bin/bash
|
||||
# Sanity / ops: check paid orders wired? bank transfers? balances?
|
||||
# Run on koopa as root.
|
||||
#
|
||||
# Usage:
|
||||
# check_settlement.sh # all paid orders for goa-demo
|
||||
# check_settlement.sh ORDER_ID # one order
|
||||
# MERCHANT_INSTANCE=foo check_settlement.sh
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
# shellcheck source=lib.sh
|
||||
source "$ROOT/lib.sh"
|
||||
need_root
|
||||
|
||||
INST="${MERCHANT_INSTANCE:-goa-demo-cp4zqk}"
|
||||
ORDER_ID="${1:-}"
|
||||
|
||||
if ! read_pw MPW "/root/merchant-${INST}-password.txt"; then
|
||||
read_pw MPW /root/merchant-goa-demo-cp4zqk-password.txt || true
|
||||
fi
|
||||
if [ -z "${MPW:-}" ]; then
|
||||
fail "no merchant password for $INST"
|
||||
exit 1
|
||||
fi
|
||||
AUTH=$(merchant_auth_header "$MPW")
|
||||
|
||||
echo "=== Settlement check instance=$INST ==="
|
||||
|
||||
tmp=$(mktemp)
|
||||
curl -sk -m 15 -H "$AUTH" -o "$tmp" \
|
||||
"${MERCHANT_URL}/instances/${INST}/private/orders?paid=YES"
|
||||
|
||||
python3 - "$tmp" "$ORDER_ID" <<'PY'
|
||||
import json,sys,time
|
||||
d=json.load(open(sys.argv[1]))
|
||||
want=sys.argv[2] or None
|
||||
orders=d.get("orders") or []
|
||||
if want:
|
||||
orders=[o for o in orders if o.get("order_id")==want]
|
||||
if not orders:
|
||||
print("FAIL no paid orders" + (f" matching {want}" if want else ""))
|
||||
sys.exit(1)
|
||||
print(f"INFO paid_orders={len(orders)}")
|
||||
for o in orders:
|
||||
print(f" - {o.get('order_id')} amount={o.get('amount')} summary={o.get('summary')!r} paid={o.get('paid')}")
|
||||
sys.exit(0)
|
||||
PY
|
||||
ec=$?
|
||||
[ "$ec" -eq 0 ] || FAILS=$((FAILS + 1))
|
||||
|
||||
# Detail each order
|
||||
mapfile -t OIDS < <(python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); want=sys.argv[2] or None
|
||||
oids=[o["order_id"] for o in d.get("orders",[])]
|
||||
print("\n".join([x for x in oids if not want or x==want]))' "$tmp" "$ORDER_ID")
|
||||
|
||||
for oid in "${OIDS[@]}"; do
|
||||
echo "--- order $oid ---"
|
||||
ot=$(mktemp)
|
||||
curl -sk -m 15 -H "$AUTH" -o "$ot" \
|
||||
"${MERCHANT_URL}/instances/${INST}/private/orders/${oid}"
|
||||
python3 - "$ot" <<'PY'
|
||||
import json,sys,time
|
||||
d=json.load(open(sys.argv[1]))
|
||||
ct=d.get("contract_terms") or {}
|
||||
ts=(ct.get("timestamp") or {}).get("t_s")
|
||||
rd=(ct.get("refund_deadline") or {}).get("t_s")
|
||||
wd=(ct.get("wire_transfer_deadline") or {}).get("t_s")
|
||||
now=int(time.time())
|
||||
print(" status", d.get("order_status"), "wired", d.get("wired"), "deposit_total", d.get("deposit_total"))
|
||||
print(" amount", ct.get("amount"), "summary", ct.get("summary"))
|
||||
if ts and rd and wd:
|
||||
print(f" age_s={now-ts} refund_in_s={rd-now} wire_in_s={wd-now}")
|
||||
print(f" windows: refund={rd-ts}s wire_from_pay={wd-ts}s (wire_after_refund={wd-rd}s)")
|
||||
if now >= wd and not d.get("wired"):
|
||||
print(" FAIL wire deadline passed but wired=false")
|
||||
sys.exit(2)
|
||||
if now < wd:
|
||||
print(" INFO still before wire deadline — settlement not due yet")
|
||||
if d.get("wired"):
|
||||
print(" OK wired=true")
|
||||
sys.exit(0)
|
||||
PY
|
||||
ec=$?
|
||||
[ "$ec" -eq 2 ] && FAILS=$((FAILS + 1))
|
||||
rm -f "$ot"
|
||||
done
|
||||
|
||||
echo "=== private/transfers ==="
|
||||
tr=$(mktemp)
|
||||
curl -sk -m 15 -H "$AUTH" -o "$tr" \
|
||||
"${MERCHANT_URL}/instances/${INST}/private/transfers"
|
||||
python3 -c 'import json,sys; d=json.load(open(sys.argv[1])); t=d.get("transfers") or []; print(f" transfers={len(t)}");
|
||||
[print(" ", x) for x in t[:10]]' "$tr"
|
||||
rm -f "$tr" "$tmp"
|
||||
|
||||
# Bank balances
|
||||
BANK_USER="${BANK_USER:-$INST}"
|
||||
if read_pw BPW "/root/bank-${BANK_USER}-password.txt" || read_pw BPW /root/bank-goa-demo-cp4zqk-password.txt; then
|
||||
BT=$(bank_token "$BANK_USER" "$BPW")
|
||||
if [ -n "$BT" ]; then
|
||||
curl -sS -m 12 -H "Authorization: Bearer ${BT}" \
|
||||
"${BANK_URL}/accounts/${BANK_USER}" | python3 -c 'import sys,json; d=json.load(sys.stdin); print("merchant_bank_balance", d.get("balance"))'
|
||||
curl -sS -m 12 -H "Authorization: Bearer ${BT}" \
|
||||
"${BANK_URL}/accounts/${BANK_USER}/transactions?delta=-10" \
|
||||
| python3 -c 'import sys,json; d=json.load(sys.stdin); txs=d.get("transactions") or []; print(f"merchant_bank_tx={len(txs)}");
|
||||
[print(f" {t.get(\"direction\")} {t.get(\"amount\")} {t.get(\"subject\",\"\")[:60]}") for t in txs[:8]]'
|
||||
fi
|
||||
fi
|
||||
|
||||
if read_pw EPW /root/bank-exchange-password.txt; then
|
||||
ET=$(bank_token exchange "$EPW")
|
||||
if [ -n "$ET" ]; then
|
||||
curl -sS -m 12 -H "Authorization: Bearer ${ET}" \
|
||||
"${BANK_URL}/accounts/exchange" | python3 -c 'import sys,json; d=json.load(sys.stdin); print("exchange_bank_balance", d.get("balance"))'
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "=== summary fails=$FAILS ==="
|
||||
exit "$FAILS"
|
||||
73
scripts/taler-sanity/check_stack-health.sh
Normal file
73
scripts/taler-sanity/check_stack-health.sh
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
#!/bin/bash
|
||||
# Sanity: public + local Taler endpoints respond (GOA stack).
|
||||
# Run on koopa as root (or any user with network to services).
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
# shellcheck source=lib.sh
|
||||
source "$ROOT/lib.sh"
|
||||
|
||||
echo "=== Taler stack health ==="
|
||||
|
||||
# Public HTTPS (via Caddy / VeciGate)
|
||||
for url in \
|
||||
"${EXCHANGE_PUBLIC}/config" \
|
||||
"${EXCHANGE_PUBLIC}/keys" \
|
||||
"${BANK_PUBLIC}/config" \
|
||||
"${MERCHANT_PUBLIC}/config"
|
||||
do
|
||||
code=$(http_code "$url")
|
||||
if [ "$code" = "200" ]; then pass "$url -> $code"
|
||||
else fail "$url -> $code"
|
||||
fi
|
||||
done
|
||||
|
||||
# Local loopback ports (containers via pasta)
|
||||
for spec in \
|
||||
"bank ${BANK_URL}/config" \
|
||||
"merchant ${MERCHANT_URL}/config" \
|
||||
"exchange-via-public ${EXCHANGE_PUBLIC}/config"
|
||||
do
|
||||
name=${spec%% *}
|
||||
url=${spec#* }
|
||||
code=$(http_code "$url")
|
||||
if [ "$code" = "200" ]; then pass "local $name -> $code"
|
||||
else fail "local $name -> $code"
|
||||
fi
|
||||
done
|
||||
|
||||
# Merchant multi-currency
|
||||
tmp=$(mktemp)
|
||||
curl -sk -m 12 -o "$tmp" "${MERCHANT_PUBLIC}/config" || true
|
||||
if python3 - "$tmp" <<'PY'
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
ex=[e.get("currency") for e in d.get("exchanges") or []]
|
||||
cur=list((d.get("currencies") or {}).keys())
|
||||
ok = "GOA" in ex and "CHF" in ex and "GOA" in cur
|
||||
print("currency_default", d.get("currency"))
|
||||
print("exchanges", ex)
|
||||
print("currencies", cur)
|
||||
sys.exit(0 if ok else 1)
|
||||
PY
|
||||
then pass "merchant has CHF + GOA exchanges"
|
||||
else fail "merchant missing CHF/GOA in /config"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
|
||||
# Exchange currency GOA
|
||||
tmp=$(mktemp)
|
||||
curl -sk -m 12 -o "$tmp" "${EXCHANGE_PUBLIC}/config" || true
|
||||
if python3 - "$tmp" <<'PY'
|
||||
import json,sys
|
||||
d=json.load(open(sys.argv[1]))
|
||||
c=(d.get("currency") or d.get("currency_specification",{}).get("currency"))
|
||||
print("exchange_currency", c)
|
||||
sys.exit(0 if c=="GOA" else 1)
|
||||
PY
|
||||
then pass "exchange currency GOA"
|
||||
else fail "exchange currency not GOA"
|
||||
fi
|
||||
rm -f "$tmp"
|
||||
|
||||
echo "=== summary fails=$FAILS ==="
|
||||
exit "$FAILS"
|
||||
60
scripts/taler-sanity/lib.sh
Normal file
60
scripts/taler-sanity/lib.sh
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# shellcheck shell=bash
|
||||
# Common helpers for Taler sanity checks (run on koopa host as root).
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
: "${BANK_URL:=http://127.0.0.1:9012}"
|
||||
: "${MERCHANT_URL:=https://127.0.0.1:9010}"
|
||||
: "${EXCHANGE_PUBLIC:=https://exchange.hacktivism.ch}"
|
||||
: "${BANK_PUBLIC:=https://bank.hacktivism.ch}"
|
||||
: "${MERCHANT_PUBLIC:=https://taler.hacktivism.ch}"
|
||||
|
||||
pass() { echo "OK $*"; }
|
||||
fail() { echo "FAIL $*"; FAILS=$((FAILS + 1)); }
|
||||
warn() { echo "WARN $*"; }
|
||||
info() { echo "INFO $*"; }
|
||||
|
||||
FAILS=0
|
||||
|
||||
need_root() {
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo "This check expects root on koopa (password files under /root)." >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
read_pw() {
|
||||
# read_pw VAR path
|
||||
local _v="$1" _p="$2"
|
||||
if [ ! -f "$_p" ]; then
|
||||
eval "$_v="
|
||||
return 1
|
||||
fi
|
||||
eval "$_v=\$(tr -d '\\n' <\"$_p\")"
|
||||
}
|
||||
|
||||
bank_token() {
|
||||
# bank_token USER PASS -> prints access_token
|
||||
local user="$1" pass="$2"
|
||||
curl -sS -m 15 -u "${user}:${pass}" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"scope":"readwrite","refreshable":true}' \
|
||||
"${BANK_URL}/accounts/${user}/token" \
|
||||
| python3 -c 'import sys,json; print(json.load(sys.stdin).get("access_token",""))'
|
||||
}
|
||||
|
||||
merchant_auth_header() {
|
||||
# merchant_auth_header PASSWORD
|
||||
printf 'Authorization: Bearer secret-token:%s' "$1"
|
||||
}
|
||||
|
||||
http_code() {
|
||||
# http_code URL [curl args...]
|
||||
local url="$1"
|
||||
shift
|
||||
curl -sk -m 12 -o /dev/null -w '%{http_code}' "$@" "$url" 2>/dev/null || echo "000"
|
||||
}
|
||||
|
||||
json_get() {
|
||||
# json_get FILE python-expr-on-d
|
||||
python3 -c "import json,sys; d=json.load(open(sys.argv[1])); print($2)" "$1"
|
||||
}
|
||||
22
scripts/taler-sanity/run_all.sh
Normal file
22
scripts/taler-sanity/run_all.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/bash
|
||||
# Run all sanity checks; exit non-zero if any failed.
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")" && pwd)
|
||||
ec=0
|
||||
for s in \
|
||||
check_helpers-running.sh \
|
||||
check_stack-health.sh \
|
||||
check_merchant-delays.sh \
|
||||
check_exchange-wirewatch.sh \
|
||||
check_settlement.sh
|
||||
do
|
||||
echo ""
|
||||
echo "########## $s ##########"
|
||||
if bash "$ROOT/$s"; then
|
||||
echo "PASS $s"
|
||||
else
|
||||
echo "FAIL $s (exit $?)"
|
||||
ec=1
|
||||
fi
|
||||
done
|
||||
exit $ec
|
||||
3
scripts/taler-wallet-cli/run-amount-ladder-bench.sh
Executable file
3
scripts/taler-wallet-cli/run-amount-ladder-bench.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
# Compatibility wrapper — canonical benchmark lives under benchmarks/amount-ladder/
|
||||
exec "$(cd "$(dirname "$0")/../.." && pwd)/benchmarks/amount-ladder/run.sh" "$@"
|
||||
350
scripts/taler-wallet-cli/run-extreme-amount-tests.sh
Executable file
350
scripts/taler-wallet-cli/run-extreme-amount-tests.sh
Executable file
|
|
@ -0,0 +1,350 @@
|
|||
#!/bin/bash
|
||||
# Extreme amount tests for GOA: largest practical price vs atomic/tiny coin.
|
||||
#
|
||||
# Live denoms (exchange.hacktivism.ch): GOA:0.000001 … GOA:10
|
||||
# Currency TINY / round unit: GOA:0.00000001 (Atomic-GOA)
|
||||
#
|
||||
# Run on the local machine (wallet) with ssh BatchMode to koopa for bank/merchant ops.
|
||||
# Requires: monorepo taler-wallet-cli, passwords on koopa /root/*-password.txt
|
||||
#
|
||||
# Usage:
|
||||
# ./run-extreme-amount-tests.sh
|
||||
# HIGH_AMOUNT=GOA:500 TINY_AMOUNT=GOA:0.000001 ./run-extreme-amount-tests.sh
|
||||
set -euo pipefail
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
SCRATCH="${ROOT}/.tmp/extreme-$(date -u +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$SCRATCH"
|
||||
chmod 700 "$SCRATCH"
|
||||
|
||||
MONO="${MONO:-/Users/newkamek/src/taler/taler-typescript-core/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs}"
|
||||
WDB="${WDB:-$SCRATCH/wallet.sqlite3}"
|
||||
EX="${EXCHANGE_URL:-https://exchange.hacktivism.ch/}"
|
||||
KOOPA="${KOOPA_HOST:-koopa}"
|
||||
INST="${MERCHANT_INSTANCE:-goa-demo-cp4zqk}"
|
||||
BANK_PUBLIC="${BANK_PUBLIC:-https://bank.hacktivism.ch}"
|
||||
MER_PUBLIC="${MER_PUBLIC:-https://taler.hacktivism.ch}"
|
||||
|
||||
# High price: many max-denom (GOA:10) coins. Tiny: smallest issued coin.
|
||||
HIGH_AMOUNT="${HIGH_AMOUNT:-GOA:1000}"
|
||||
TINY_AMOUNT="${TINY_AMOUNT:-GOA:0.000001}"
|
||||
# True atomic (round unit) — may fail if no coin below 1e-6
|
||||
ATOMIC_AMOUNT="${ATOMIC_AMOUNT:-GOA:0.00000001}"
|
||||
# Fat bank balance for withdraw buffer
|
||||
FAT_CREDIT="${FAT_CREDIT:-GOA:50000}"
|
||||
FAT_USER="${FAT_USER:-extreme-fat}"
|
||||
# withdraw a bit more than HIGH
|
||||
WITHDRAW_AMOUNT="${WITHDRAW_AMOUNT:-GOA:1500}"
|
||||
|
||||
wcli() { node "$MONO" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"; }
|
||||
|
||||
log() { printf '%s %s\n' "$(date -u +%H:%M:%S)" "$*"; }
|
||||
die() { echo "FAIL: $*" >&2; exit 1; }
|
||||
|
||||
command -v node >/dev/null || die "node missing"
|
||||
[ -f "$MONO" ] || die "wallet missing: $MONO"
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=10 "$KOOPA" 'echo ok' >/dev/null || die "ssh $KOOPA failed"
|
||||
|
||||
log "scratch=$SCRATCH"
|
||||
log "HIGH=$HIGH_AMOUNT TINY=$TINY_AMOUNT ATOMIC=$ATOMIC_AMOUNT WITHDRAW=$WITHDRAW_AMOUNT FAT=$FAT_USER+$FAT_CREDIT"
|
||||
|
||||
# --- 1) koopa: bank account + credit + templates + orders ---
|
||||
log "=== koopa setup (admin credit, templates, orders) ==="
|
||||
# Generate bank password locally (not committed)
|
||||
FAT_PW=$(python3 -c 'import secrets; print(secrets.token_urlsafe(12))')
|
||||
echo "$FAT_PW" >"$SCRATCH/fat-bank.password"
|
||||
chmod 600 "$SCRATCH/fat-bank.password"
|
||||
|
||||
cat >"$SCRATCH/koopa-setup.sh" <<EOS
|
||||
#!/bin/bash
|
||||
set +e
|
||||
exec > /tmp/extreme-setup.out 2>&1
|
||||
set -x
|
||||
INST=$INST
|
||||
FAT_USER=$FAT_USER
|
||||
FAT_PW='$(printf %s "$FAT_PW" | sed "s/'/'\\\\''/g")'
|
||||
FAT_CREDIT=$FAT_CREDIT
|
||||
HIGH_AMOUNT=$HIGH_AMOUNT
|
||||
TINY_AMOUNT=$TINY_AMOUNT
|
||||
ATOMIC_AMOUNT=$ATOMIC_AMOUNT
|
||||
BANK=http://127.0.0.1:9012
|
||||
MER=https://127.0.0.1:9010
|
||||
|
||||
ADMIN_PASS=\$(tr -d '\\n' </root/bank-admin-password.txt)
|
||||
MPW=\$(tr -d '\\n' </root/merchant-\${INST}-password.txt)
|
||||
AUTH="Authorization: Bearer secret-token:\${MPW}"
|
||||
|
||||
# admin token
|
||||
AT=\$(curl -sS -u "admin:\${ADMIN_PASS}" -H 'Content-Type: application/json' \\
|
||||
-d '{"scope":"readwrite"}' "\${BANK}/accounts/admin/token" \\
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin).get("access_token",""))')
|
||||
[ -n "\$AT" ] || { echo FAIL_admin_token; exit 1; }
|
||||
|
||||
# create or reuse bank account
|
||||
code=\$(curl -sS -o /tmp/acc-create.json -w '%{http_code}' -X POST \\
|
||||
-H "Authorization: Bearer \${AT}" -H 'Content-Type: application/json' \\
|
||||
-d "{\\"username\\":\\"\${FAT_USER}\\",\\"password\\":\\"\${FAT_PW}\\",\\"name\\":\\"Extreme Fat Wallet\\",\\"is_public\\":false,\\"is_taler_exchange\\":false,\\"debit_threshold\\":\\"GOA:1000000\\"}" \\
|
||||
"\${BANK}/accounts")
|
||||
echo create_account_http=\$code
|
||||
cat /tmp/acc-create.json; echo
|
||||
# if exists (409), reset password via admin if possible
|
||||
if [ "\$code" != "200" ] && [ "\$code" != "201" ] && [ "\$code" != "204" ]; then
|
||||
echo "account may exist; try credit anyway"
|
||||
fi
|
||||
|
||||
# credit from admin (idempotent enough — we just need lots of GOA)
|
||||
python3 - <<'PY'
|
||||
import json, os, urllib.request, base64
|
||||
bank=os.environ.get("BANK","http://127.0.0.1:9012")
|
||||
PY
|
||||
# use host credit-account if present
|
||||
if [ -x /root/koopa-admin-log/scripts/taler-bank/credit-account.sh ]; then
|
||||
/root/koopa-admin-log/scripts/taler-bank/credit-account.sh "\$FAT_USER" "\$FAT_CREDIT" || true
|
||||
elif [ -x /Users/newkamek/src/koopa/koopa-admin-log/scripts/taler-bank/credit-account.sh ]; then
|
||||
true
|
||||
else
|
||||
# inline credit
|
||||
PAYTO="payto://x-taler-bank/bank.hacktivism.ch/\${FAT_USER}?receiver-name=\${FAT_USER}&message=extreme-credit"
|
||||
UID=\$(python3 -c 'import os; a="0123456789ABCDEFGHJKMNPQRSTVWXYZ"; b=os.urandom(32); bits=0;v=0;o=[]
|
||||
for byte in b:
|
||||
v=(v<<8)|byte; bits+=8
|
||||
while bits>=5:
|
||||
bits-=5; o.append(a[(v>>bits)&31])
|
||||
if bits: o.append(a[(v<<(5-bits))&31])
|
||||
print("".join(o))')
|
||||
curl -sS -H "Authorization: Bearer \${AT}" -H 'Content-Type: application/json' \\
|
||||
-d "{\\"payto_uri\\":\\"\${PAYTO}\\",\\"amount\\":\\"\${FAT_CREDIT}\\",\\"request_uid\\":\\"\${UID}\\"}" \\
|
||||
"\${BANK}/accounts/admin/transactions" | tee /tmp/credit.out
|
||||
echo
|
||||
fi
|
||||
|
||||
# balance
|
||||
curl -sS -H "Authorization: Bearer \${AT}" "\${BANK}/accounts/\${FAT_USER}" | tee /tmp/fat-bal.json
|
||||
echo
|
||||
python3 -c 'import json;d=json.load(open("/tmp/fat-bal.json"));print("FAT_BALANCE", (d.get("balance") or {}).get("amount"), (d.get("balance") or {}).get("credit_debit_indicator"))'
|
||||
|
||||
# store bank pw for host-side withdraw
|
||||
printf '%s' "\$FAT_PW" > /root/bank-\${FAT_USER}-password.txt
|
||||
chmod 600 /root/bank-\${FAT_USER}-password.txt
|
||||
|
||||
# templates
|
||||
for id_summary_amount in \\
|
||||
"extreme-high|extreme high price|\${HIGH_AMOUNT}" \\
|
||||
"extreme-tiny|extreme tiny coin|\${TINY_AMOUNT}" \\
|
||||
"extreme-atomic|true atomic round unit|\${ATOMIC_AMOUNT}"
|
||||
do
|
||||
IFS='|' read -r TID SUM AMT <<<"\$id_summary_amount"
|
||||
body=\$(python3 -c "import json; print(json.dumps({
|
||||
'template_id': '\$TID',
|
||||
'template_description': '\$SUM',
|
||||
'editable_defaults': False,
|
||||
'template_contract': {
|
||||
'summary': '\$SUM',
|
||||
'amount': '\$AMT',
|
||||
'minimum_age': 0,
|
||||
},
|
||||
}))")
|
||||
# try POST then PATCH
|
||||
code=\$(curl -sk -o /tmp/tpl.json -w '%{http_code}' -X POST -H "\$AUTH" -H 'Content-Type: application/json' \\
|
||||
-d "\$body" "\${MER}/instances/\${INST}/private/templates")
|
||||
echo "template_\$TID post=\$code \$(head -c 120 /tmp/tpl.json)"
|
||||
if [ "\$code" = "409" ] || [ "\$code" = "400" ]; then
|
||||
curl -sk -X PATCH -H "\$AUTH" -H 'Content-Type: application/json' \\
|
||||
-d "\$body" "\${MER}/instances/\${INST}/private/templates/\${TID}" -w " patch=%{http_code}\\n"
|
||||
fi
|
||||
done
|
||||
|
||||
# also list templates
|
||||
curl -sk -H "\$AUTH" "\${MER}/instances/\${INST}/private/templates" | python3 -m json.tool | head -80
|
||||
|
||||
# create two firm orders (high + tiny) for deterministic pay URIs
|
||||
create_order() {
|
||||
local sum="\$1" amt="\$2" tag="\$3"
|
||||
curl -sk -X POST -H "\$AUTH" -H 'Content-Type: application/json' \\
|
||||
-d "{\\"order\\":{\\"summary\\":\\"\${sum}\\",\\"amount\\":\\"\${amt}\\",\\"fulfillment_message\\":\\"extreme-ok\\"},\\"create_token\\":true}" \\
|
||||
"\${MER}/instances/\${INST}/private/orders" > "/tmp/ord-\${tag}.json"
|
||||
python3 - "\$tag" <<'PY'
|
||||
import json,sys
|
||||
tag=sys.argv[1]
|
||||
d=json.load(open(f"/tmp/ord-{tag}.json"))
|
||||
print(f"ORDER_{tag.upper()}", d.get("order_id"), "token", (d.get("token") or "")[:12])
|
||||
open(f"/tmp/oid-{tag}.txt","w").write(d.get("order_id") or "")
|
||||
open(f"/tmp/otok-{tag}.txt","w").write(d.get("token") or "")
|
||||
PY
|
||||
}
|
||||
create_order "extreme-high-order" "\$HIGH_AMOUNT" high
|
||||
create_order "extreme-tiny-order" "\$TINY_AMOUNT" tiny
|
||||
create_order "extreme-atomic-order" "\$ATOMIC_AMOUNT" atomic
|
||||
|
||||
# create bank integrated withdrawal for fat user
|
||||
BT=\$(curl -sS -u "\${FAT_USER}:\${FAT_PW}" -H 'Content-Type: application/json' \\
|
||||
-d '{"scope":"readwrite"}' "\${BANK}/accounts/\${FAT_USER}/token" \\
|
||||
| python3 -c 'import sys,json;print(json.load(sys.stdin).get("access_token",""))')
|
||||
curl -sS -H "Authorization: Bearer \${BT}" -H 'Content-Type: application/json' \\
|
||||
-d "{\\"amount\\":\\"\$WITHDRAW_AMOUNT\\",\\"exchange_url\\":\\"https://exchange.hacktivism.ch/\\"}" \\
|
||||
"\${BANK}/accounts/\${FAT_USER}/withdrawals" | tee /tmp/wd-create.json
|
||||
echo
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
d=json.load(open("/tmp/wd-create.json"))
|
||||
wid=d.get("withdrawal_id") or d.get("id")
|
||||
uri=d.get("taler_withdraw_uri")
|
||||
print("WID", wid)
|
||||
print("WURI", uri)
|
||||
open("/tmp/wid.txt","w").write(wid or "")
|
||||
open("/tmp/wuri.txt","w").write(uri or "")
|
||||
PY
|
||||
|
||||
# publish artifacts for hernani-readable pull
|
||||
cp /tmp/oid-*.txt /tmp/otok-*.txt /tmp/wuri.txt /tmp/wid.txt /tmp/fat-bal.json /tmp/extreme-setup.out /home/hernani/ 2>/dev/null || true
|
||||
chmod 644 /home/hernani/oid-*.txt /home/hernani/otok-*.txt /home/hernani/wuri.txt /home/hernani/wid.txt 2>/dev/null || true
|
||||
|
||||
echo SETUP_EXTREME_OK
|
||||
EOS
|
||||
|
||||
# inject WITHDRAW_AMOUNT into script (heredoc already expanded partially - fix)
|
||||
# The setup script uses $WITHDRAW_AMOUNT as shell var on koopa - need to export into script
|
||||
sed -i.bak "s|\$WITHDRAW_AMOUNT|$WITHDRAW_AMOUNT|g" "$SCRATCH/koopa-setup.sh" 2>/dev/null || \
|
||||
sed -i '' "s|\$WITHDRAW_AMOUNT|$WITHDRAW_AMOUNT|g" "$SCRATCH/koopa-setup.sh"
|
||||
|
||||
scp -o BatchMode=yes "$SCRATCH/koopa-setup.sh" "${KOOPA}:/tmp/extreme-setup.sh"
|
||||
# also scp credit-account for reliable credit
|
||||
scp -o BatchMode=yes "$ROOT/scripts/taler-bank/credit-account.sh" "${KOOPA}:/tmp/credit-account.sh" || true
|
||||
|
||||
# run via screen window 0 (root@koopa) — screen-run cannot source local-machine paths
|
||||
SESSION=$(screen -ls 2>/dev/null | awk 'match($0, /[0-9]+\.[A-Za-z0-9._-]+/) { print substr($0, RSTART, RLENGTH); exit }')
|
||||
[ -n "$SESSION" ] || die "no screen session"
|
||||
screen -S "$SESSION" -p 0 -X stuff $'bash /tmp/extreme-setup.sh; chmod 644 /tmp/extreme-setup.out /home/hernani/*.txt 2>/dev/null\n'
|
||||
|
||||
for i in $(seq 1 90); do
|
||||
if ssh -o BatchMode=yes "$KOOPA" 'grep -q SETUP_EXTREME_OK /tmp/extreme-setup.out 2>/dev/null'; then
|
||||
log "setup ready @${i}s"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
[ "$i" -eq 90 ] && { ssh -o BatchMode=yes "$KOOPA" 'tail -80 /tmp/extreme-setup.out' || true; die "setup timeout"; }
|
||||
done
|
||||
ssh -o BatchMode=yes "$KOOPA" 'cat /tmp/extreme-setup.out' >"$SCRATCH/koopa-setup.out" || true
|
||||
# pull artifacts (hernani-readable)
|
||||
scp -o BatchMode=yes "${KOOPA}:/home/hernani/wuri.txt" "${KOOPA}:/home/hernani/wid.txt" \
|
||||
"${KOOPA}:/home/hernani/oid-high.txt" "${KOOPA}:/home/hernani/oid-tiny.txt" \
|
||||
"${KOOPA}:/home/hernani/oid-atomic.txt" \
|
||||
"${KOOPA}:/home/hernani/otok-high.txt" "${KOOPA}:/home/hernani/otok-tiny.txt" \
|
||||
"${KOOPA}:/home/hernani/otok-atomic.txt" \
|
||||
"$SCRATCH/" 2>/dev/null || true
|
||||
|
||||
WURI=$(tr -d '\n' <"$SCRATCH/wuri.txt" 2>/dev/null || true)
|
||||
WID=$(tr -d '\n' <"$SCRATCH/wid.txt" 2>/dev/null || true)
|
||||
OID_HIGH=$(tr -d '\n' <"$SCRATCH/oid-high.txt" 2>/dev/null || true)
|
||||
OID_TINY=$(tr -d '\n' <"$SCRATCH/oid-tiny.txt" 2>/dev/null || true)
|
||||
OID_ATOMIC=$(tr -d '\n' <"$SCRATCH/oid-atomic.txt" 2>/dev/null || true)
|
||||
TOK_HIGH=$(tr -d '\n' <"$SCRATCH/otok-high.txt" 2>/dev/null || true)
|
||||
TOK_TINY=$(tr -d '\n' <"$SCRATCH/otok-tiny.txt" 2>/dev/null || true)
|
||||
TOK_ATOMIC=$(tr -d '\n' <"$SCRATCH/otok-atomic.txt" 2>/dev/null || true)
|
||||
|
||||
log "WURI=$WURI"
|
||||
log "orders high=$OID_HIGH tiny=$OID_TINY atomic=$OID_ATOMIC"
|
||||
[ -n "$WURI" ] || die "no withdraw URI"
|
||||
[ -n "$OID_HIGH" ] && [ -n "$OID_TINY" ] || die "orders missing"
|
||||
|
||||
# ensure auto-confirm watches this withdrawal
|
||||
ssh -o BatchMode=yes "$KOOPA" "echo $WID >> /var/www/bank-landing/withdraw-watch.ids 2>/dev/null; sort -u /var/www/bank-landing/withdraw-watch.ids -o /var/www/bank-landing/withdraw-watch.ids 2>/dev/null; true"
|
||||
# root confirm via screen
|
||||
screen -S "$SESSION" -p 0 -X stuff $'BPW=$(tr -d "\\n" </root/bank-'"$FAT_USER"'-password.txt); BT=$(curl -sS -u "'"$FAT_USER"':${BPW}" -H "Content-Type: application/json" -d "{\"scope\":\"readwrite\"}" http://127.0.0.1:9012/accounts/'"$FAT_USER"'/token | python3 -c "import sys,json;print(json.load(sys.stdin).get(\"access_token\",\"\"))"); curl -sS -X POST -H "Authorization: Bearer ${BT}" http://127.0.0.1:9012/accounts/'"$FAT_USER"'/withdrawals/'"$WID"'/confirm; echo CONF_DONE\n'
|
||||
|
||||
# --- 2) wallet withdraw ---
|
||||
log "=== wallet: add exchange + ToS ==="
|
||||
wcli exchanges add "$EX" 2>&1 | tee "$SCRATCH/ex-add.out" || true
|
||||
wcli exchanges update "$EX" 2>&1 | tee "$SCRATCH/ex-upd.out" || true
|
||||
wcli exchanges tos "$EX" 2>&1 | tee "$SCRATCH/ex-tos.out" || true
|
||||
wcli exchanges accept-tos "$EX" 2>&1 | tee "$SCRATCH/ex-accept.out" || true
|
||||
|
||||
log "=== withdraw accept-uri ==="
|
||||
wcli withdraw accept-uri --exchange "$EX" "$WURI" 2>&1 | tee "$SCRATCH/accept-uri.out" || true
|
||||
|
||||
for round in $(seq 1 20); do
|
||||
log "run-until-done round $round"
|
||||
wcli run-until-done 2>&1 | tee -a "$SCRATCH/rud.out" || true
|
||||
wcli balance 2>&1 | tee "$SCRATCH/bal-$round.out"
|
||||
if grep -qE "GOA:" "$SCRATCH/bal-$round.out"; then
|
||||
# check non-zero available
|
||||
if python3 -c 'import re,sys; t=open(sys.argv[1]).read();
|
||||
m=re.search(r"available[^0-9A-Z]*GOA:([0-9.]+)", t)
|
||||
print(m.group(1) if m else ""); sys.exit(0 if m and float(m.group(1))>0 else 1)' "$SCRATCH/bal-$round.out" 2>/dev/null; then
|
||||
log "coins available"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
# re-confirm withdraw on bank
|
||||
screen -S "$SESSION" -p 0 -X stuff $'BPW=$(tr -d "\\n" </root/bank-'"$FAT_USER"'-password.txt); BT=$(curl -sS -u "'"$FAT_USER"':${BPW}" -H "Content-Type: application/json" -d "{\"scope\":\"readwrite\"}" http://127.0.0.1:9012/accounts/'"$FAT_USER"'/token | python3 -c "import sys,json;print(json.load(sys.stdin).get(\"access_token\",\"\"))"); curl -sS -X POST -H "Authorization: Bearer ${BT}" http://127.0.0.1:9012/accounts/'"$FAT_USER"'/withdrawals/'"$WID"'/confirm 2>/dev/null; echo\n'
|
||||
sleep 3
|
||||
done
|
||||
|
||||
wcli balance 2>&1 | tee "$SCRATCH/balance-after-withdraw.out"
|
||||
wcli transactions 2>&1 | tee "$SCRATCH/tx-after-withdraw.out" | head -100
|
||||
|
||||
# --- 3) pay high + tiny (+ try atomic) ---
|
||||
pay_order() {
|
||||
local tag="$1" oid="$2" tok="$3"
|
||||
local uri="taler://pay/taler.hacktivism.ch/instances/${INST}/${oid}/?c=${tok}"
|
||||
echo "$uri" >"$SCRATCH/pay-${tag}.uri"
|
||||
log "=== pay $tag order=$oid ==="
|
||||
log "uri=$uri"
|
||||
wcli handle-uri "$uri" 2>&1 | tee "$SCRATCH/pay-${tag}-handle.out" || true
|
||||
# some wallets use payments prepare/confirm
|
||||
wcli run-until-done 2>&1 | tee -a "$SCRATCH/pay-${tag}-rud.out" || true
|
||||
# check merchant
|
||||
ssh -o BatchMode=yes "$KOOPA" "MPW=\$(tr -d '\\n' </root/merchant-${INST}-password.txt); curl -sk -H \"Authorization: Bearer secret-token:\${MPW}\" https://127.0.0.1:9010/instances/${INST}/private/orders/${oid}" \
|
||||
>"$SCRATCH/order-${tag}-status.json" 2>/dev/null || true
|
||||
python3 - "$SCRATCH/order-${tag}-status.json" "$tag" <<'PY' || true
|
||||
import json,sys
|
||||
p,tag=sys.argv[1],sys.argv[2]
|
||||
try:
|
||||
d=json.load(open(p))
|
||||
except Exception as e:
|
||||
print(f"STATUS_{tag} unreadable", e); raise SystemExit
|
||||
print(f"STATUS_{tag}", "order_status=", d.get("order_status"), "paid=", d.get("paid"),
|
||||
"wired=", d.get("wired"), "deposit_total=", d.get("deposit_total"),
|
||||
"amount=", (d.get("contract_terms") or {}).get("amount"))
|
||||
PY
|
||||
}
|
||||
|
||||
pay_order high "$OID_HIGH" "$TOK_HIGH"
|
||||
wcli balance 2>&1 | tee "$SCRATCH/balance-after-high.out"
|
||||
|
||||
pay_order tiny "$OID_TINY" "$TOK_TINY"
|
||||
wcli balance 2>&1 | tee "$SCRATCH/balance-after-tiny.out"
|
||||
|
||||
if [ -n "$OID_ATOMIC" ] && [ -n "$TOK_ATOMIC" ]; then
|
||||
pay_order atomic "$OID_ATOMIC" "$TOK_ATOMIC" || true
|
||||
wcli balance 2>&1 | tee "$SCRATCH/balance-after-atomic.out" || true
|
||||
fi
|
||||
|
||||
# template URIs for docs
|
||||
{
|
||||
echo "taler://pay-template/taler.hacktivism.ch/instances/${INST}/extreme-high/"
|
||||
echo "taler://pay-template/taler.hacktivism.ch/instances/${INST}/extreme-tiny/"
|
||||
echo "taler://pay-template/taler.hacktivism.ch/instances/${INST}/extreme-atomic/"
|
||||
} | tee "$SCRATCH/template-uris.txt"
|
||||
|
||||
log "=== summary ==="
|
||||
python3 - <<PY
|
||||
import json,glob,os
|
||||
scratch="$SCRATCH"
|
||||
print("scratch", scratch)
|
||||
for tag in ("high","tiny","atomic"):
|
||||
p=f"{scratch}/order-{tag}-status.json"
|
||||
if not os.path.isfile(p):
|
||||
print(tag, "no status file"); continue
|
||||
try:
|
||||
d=json.load(open(p))
|
||||
print(tag, "paid=", d.get("paid"), "status=", d.get("order_status"),
|
||||
"amount=", (d.get("contract_terms") or {}).get("amount"),
|
||||
"deposit_total=", d.get("deposit_total"))
|
||||
except Exception as e:
|
||||
print(tag, "err", e)
|
||||
print("template_uris:")
|
||||
print(open(f"{scratch}/template-uris.txt").read())
|
||||
PY
|
||||
|
||||
log "done — details in $SCRATCH"
|
||||
54
scripts/taler-wallet-cli/withdraw-qr1-goa.sh
Executable file
54
scripts/taler-wallet-cli/withdraw-qr1-goa.sh
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
#!/bin/bash
|
||||
# Local machine: withdraw GOA via bank-integrated QR1 URI (explorer demo account).
|
||||
# Requires: monorepo wallet, ssh koopa, auto-confirm on host.
|
||||
set -euo pipefail
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
SCRATCH="${ROOT}/.tmp/session-withdraw-$(date -u +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "$SCRATCH"
|
||||
MONO="${MONO:-/Users/newkamek/src/taler/taler-typescript-core/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs}"
|
||||
WDB="${WDB:-$SCRATCH/wallet.sqlite3}"
|
||||
EX="${EXCHANGE_URL:-https://exchange.hacktivism.ch/}"
|
||||
URI="${1:-}"
|
||||
|
||||
wcli() { node "$MONO" --wallet-db="$WDB" --no-throttle --skip-defaults "$@"; }
|
||||
|
||||
echo "scratch=$SCRATCH"
|
||||
echo "=== add + accept ToS for $EX ==="
|
||||
wcli exchanges add "$EX" 2>&1 | tee "$SCRATCH/ex-add.out" || true
|
||||
wcli exchanges update "$EX" 2>&1 | tee "$SCRATCH/ex-upd.out" || true
|
||||
wcli exchanges tos "$EX" 2>&1 | tee "$SCRATCH/ex-tos.out" || true
|
||||
wcli exchanges accept-tos "$EX" 2>&1 | tee "$SCRATCH/ex-accept-tos.out"
|
||||
|
||||
if [ -z "$URI" ]; then
|
||||
URI=$(ssh -o BatchMode=yes koopa 'tr -d "\n" </var/www/bank-landing/withdraw.uri')
|
||||
fi
|
||||
echo "URI=$URI"
|
||||
echo "$URI" >"$SCRATCH/withdraw.uri"
|
||||
|
||||
echo "=== withdraw accept-uri ==="
|
||||
wcli withdraw accept-uri --exchange "$EX" "$URI" 2>&1 | tee "$SCRATCH/accept-uri.out"
|
||||
|
||||
WID=$(basename "$URI")
|
||||
echo "=== ensure watch + auto-confirm on koopa ==="
|
||||
ssh -o BatchMode=yes koopa "echo $WID >> /var/www/bank-landing/withdraw-watch.ids; sort -u /var/www/bank-landing/withdraw-watch.ids -o /var/www/bank-landing/withdraw-watch.ids"
|
||||
# one-shot confirm as root via ssh if hernani can sudo? usually not — use screen later
|
||||
# public status
|
||||
curl -sS "https://bank.hacktivism.ch/taler-integration/withdrawals/${WID}" 2>/dev/null | head -c 500 || true
|
||||
echo
|
||||
|
||||
for round in 1 2 3 4 5 6 7 8; do
|
||||
echo "=== run-until-done round $round ==="
|
||||
wcli run-until-done &
|
||||
PID=$!
|
||||
( sleep 20; kill $PID 2>/dev/null ) &
|
||||
wait $PID 2>/dev/null || true
|
||||
wcli balance 2>&1 | tee "$SCRATCH/bal-$round.out"
|
||||
wcli transactions 2>&1 | tee "$SCRATCH/tx-$round.out" | head -80
|
||||
if grep -qE '"available": "GOA:[1-9]' "$SCRATCH/bal-$round.out"; then
|
||||
echo "OK coins received"
|
||||
exit 0
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
echo "FAIL no balance" >&2
|
||||
exit 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue