ovtools 0.0.0-stage → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +121 -2
- package/assets/common/bond-inspect.sh +368 -0
- package/assets/common/config.sh +263 -0
- package/assets/common/extract-data.sh +197 -0
- package/assets/common/harden-host.sh +37 -0
- package/assets/common/install-deps.sh +93 -0
- package/assets/common/node-enroll.sh +65 -0
- package/assets/common/node-setup.sh +150 -0
- package/assets/common/node-start.sh +90 -0
- package/assets/common/node-stop.sh +18 -0
- package/assets/common/node-upgrade.sh +45 -0
- package/assets/common/pre-run.sh +497 -0
- package/assets/common/promote-validator.sh +104 -0
- package/assets/deploy/10-prepare-host.sh +98 -0
- package/assets/deploy/20-install-toolchain.sh +93 -0
- package/assets/deploy/30-setup-node.sh +82 -0
- package/assets/deploy/40-start-node.sh +75 -0
- package/assets/deploy/50-verify.sh +259 -0
- package/assets/deploy/60-enroll.sh +304 -0
- package/assets/deploy/70-promote.sh +152 -0
- package/assets/deploy/lib.sh +398 -0
- package/assets/manage/10-bond-increase.sh +149 -0
- package/assets/manage/10-diagnose-node.sh +169 -0
- package/assets/manage/20-exit-validator.sh +259 -0
- package/assets/manage/25-recover-node.sh +291 -0
- package/assets/manage/30-status.sh +64 -0
- package/assets/manage/40-exit-withdraw.sh +133 -0
- package/assets/manage/bond_inc.sh +318 -0
- package/assets/manage/dashboard_monitor/app.js +1544 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/base.css +113 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/content.css +133 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/controls.css +229 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/data.css +374 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/icons.css +35 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/motion.css +57 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/shell.css +51 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/sidebar.css +242 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/components/topbar.css +138 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/fonts.css +30 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/synteq-ui-v2.css +25 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-dark.css +38 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-light.css +36 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/css/tokens.css +61 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/assets/manage/dashboard_monitor/assets/v2.2/js/synteq-ui-v2.js +869 -0
- package/assets/manage/dashboard_monitor/deploy-dashboard.sh +230 -0
- package/assets/manage/dashboard_monitor/favicon.svg +13 -0
- package/assets/manage/dashboard_monitor/index.html +1197 -0
- package/assets/manage/dashboard_monitor/nginx-octra.conf +76 -0
- package/assets/manage/dashboard_monitor/octra-dashboard.service +29 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.cron +12 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.service +16 -0
- package/assets/manage/dashboard_monitor/octra-node-prune.timer +12 -0
- package/assets/manage/dashboard_monitor/prune-node-junk.sh +329 -0
- package/assets/manage/dashboard_monitor/server.py +1887 -0
- package/assets/manage/dashboard_monitor/ssl-certbot.sh +90 -0
- package/assets/manage/dashboard_monitor/styles.css +327 -0
- package/assets/manage/lib.sh +347 -0
- package/assets/manifest.json +143 -0
- package/assets/migrate/00-backup-identity.sh +67 -0
- package/assets/migrate/01-prepare-target.sh +145 -0
- package/assets/migrate/02-authorize-pull.sh +43 -0
- package/assets/migrate/03-pull-state.sh +102 -0
- package/assets/migrate/04-stop-source.sh +85 -0
- package/assets/migrate/05-finalize-target.sh +140 -0
- package/assets/migrate/06-start-target.sh +57 -0
- package/assets/migrate/07-verify.sh +237 -0
- package/assets/migrate/08-rollback.sh +54 -0
- package/assets/migrate/lib.sh +149 -0
- package/assets/upgrade/10-preflight.sh +120 -0
- package/assets/upgrade/20-upgrade.sh +142 -0
- package/assets/upgrade/30-manual-upgrade.sh +129 -0
- package/assets/upgrade/40-storage-prune.sh +65 -0
- package/assets/upgrade/50-status.sh +58 -0
- package/assets/upgrade/lib.sh +232 -0
- package/bin/ovtools.js +32 -0
- package/dist/api/checks.d.ts +6 -0
- package/dist/api/checks.d.ts.map +1 -0
- package/dist/api/checks.js +173 -0
- package/dist/api/checks.js.map +1 -0
- package/dist/api/hostkeys.d.ts +10 -0
- package/dist/api/hostkeys.d.ts.map +1 -0
- package/dist/api/hostkeys.js +58 -0
- package/dist/api/hostkeys.js.map +1 -0
- package/dist/api/jobs.d.ts +33 -0
- package/dist/api/jobs.d.ts.map +1 -0
- package/dist/api/jobs.js +486 -0
- package/dist/api/jobs.js.map +1 -0
- package/dist/api/nodes.d.ts +16 -0
- package/dist/api/nodes.d.ts.map +1 -0
- package/dist/api/nodes.js +126 -0
- package/dist/api/nodes.js.map +1 -0
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +443 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +301 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +76 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +150 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestrator/base.d.ts +52 -0
- package/dist/orchestrator/base.d.ts.map +1 -0
- package/dist/orchestrator/base.js +219 -0
- package/dist/orchestrator/base.js.map +1 -0
- package/dist/orchestrator/deploy.d.ts +33 -0
- package/dist/orchestrator/deploy.d.ts.map +1 -0
- package/dist/orchestrator/deploy.js +313 -0
- package/dist/orchestrator/deploy.js.map +1 -0
- package/dist/orchestrator/manage.d.ts +28 -0
- package/dist/orchestrator/manage.d.ts.map +1 -0
- package/dist/orchestrator/manage.js +267 -0
- package/dist/orchestrator/manage.js.map +1 -0
- package/dist/orchestrator/migrate.d.ts +31 -0
- package/dist/orchestrator/migrate.d.ts.map +1 -0
- package/dist/orchestrator/migrate.js +442 -0
- package/dist/orchestrator/migrate.js.map +1 -0
- package/dist/ssh/client.d.ts +39 -0
- package/dist/ssh/client.d.ts.map +1 -0
- package/dist/ssh/client.js +418 -0
- package/dist/ssh/client.js.map +1 -0
- package/dist/test/core.test.d.ts +2 -0
- package/dist/test/core.test.d.ts.map +1 -0
- package/dist/test/core.test.js +188 -0
- package/dist/test/core.test.js.map +1 -0
- package/dist/types.d.ts +267 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/assets.d.ts +28 -0
- package/dist/utils/assets.d.ts.map +1 -0
- package/dist/utils/assets.js +583 -0
- package/dist/utils/assets.js.map +1 -0
- package/dist/utils/browser.d.ts +2 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +23 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/dashboard.d.ts +16 -0
- package/dist/utils/dashboard.d.ts.map +1 -0
- package/dist/utils/dashboard.js +177 -0
- package/dist/utils/dashboard.js.map +1 -0
- package/dist/utils/helpers.d.ts +29 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +204 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/scripts.d.ts +60 -0
- package/dist/utils/scripts.d.ts.map +1 -0
- package/dist/utils/scripts.js +174 -0
- package/dist/utils/scripts.js.map +1 -0
- package/dist/utils/state.d.ts +12 -0
- package/dist/utils/state.d.ts.map +1 -0
- package/dist/utils/state.js +44 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/tar.d.ts +11 -0
- package/dist/utils/tar.d.ts.map +1 -0
- package/dist/utils/tar.js +93 -0
- package/dist/utils/tar.js.map +1 -0
- package/dist/utils/update.d.ts +9 -0
- package/dist/utils/update.d.ts.map +1 -0
- package/dist/utils/update.js +134 -0
- package/dist/utils/update.js.map +1 -0
- package/dist/utils/websocket.d.ts +10 -0
- package/dist/utils/websocket.d.ts.map +1 -0
- package/dist/utils/websocket.js +203 -0
- package/dist/utils/websocket.js.map +1 -0
- package/package.json +55 -4
- package/public/app.js +2736 -0
- package/public/assets/v2.2/css/base.css +113 -0
- package/public/assets/v2.2/css/components/content.css +133 -0
- package/public/assets/v2.2/css/components/controls.css +229 -0
- package/public/assets/v2.2/css/components/data.css +373 -0
- package/public/assets/v2.2/css/components/icons.css +35 -0
- package/public/assets/v2.2/css/components/motion.css +57 -0
- package/public/assets/v2.2/css/components/shell.css +51 -0
- package/public/assets/v2.2/css/components/sidebar.css +242 -0
- package/public/assets/v2.2/css/components/topbar.css +138 -0
- package/public/assets/v2.2/css/fonts.css +30 -0
- package/public/assets/v2.2/css/synteq-ui-v2.css +25 -0
- package/public/assets/v2.2/css/theme-dark.css +38 -0
- package/public/assets/v2.2/css/theme-light.css +36 -0
- package/public/assets/v2.2/css/tokens.css +61 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/public/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/public/assets/v2.2/js/synteq-ui-v2.js +869 -0
- package/public/favicon.svg +13 -0
- package/public/index.html +1083 -0
- package/public/styles.css +580 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 25-recover-node.sh - the solved path for a node stuck "ready / outside set"
|
|
4
|
+
#
|
|
5
|
+
# Every action is one the node's own tooling recommends; nothing is forced:
|
|
6
|
+
#
|
|
7
|
+
# 1. diagnose (the same evidence 10-diagnose-node.sh prints),
|
|
8
|
+
# 2. clean restart when the node is frozen or /status times out
|
|
9
|
+
# (`pm2 save` + `pm2 restart`) - a fresh session re-handshakes peers and asks
|
|
10
|
+
# the state-sync source for a new range, which is what unblocks catch-up,
|
|
11
|
+
# 3. `pm2 save` + `pm2 kill` + `pm2 resurrect` when the node's own tool refuses
|
|
12
|
+
# with "data directory is used outside PM2" (the pm2 entry and the running
|
|
13
|
+
# process disagree about the owner, so rejoin.sh cannot run at all),
|
|
14
|
+
# 4. wait (bounded) for head_epoch to move and the lag to shrink. If the
|
|
15
|
+
# state-sync source keeps answering `503 state_sync_range_busy` the step
|
|
16
|
+
# reports WAITING-SYNC with the quoted reason - it never "fixes" it by hand,
|
|
17
|
+
# 5. once in sync: `rejoin.sh --check`, then `--meet-stage` only when the tool
|
|
18
|
+
# itself says the round is behind (`round_lagging`/`not_ready_to_vote`),
|
|
19
|
+
# 6. re-publish readiness (`enroll.sh join`, idempotent: the bond already exists)
|
|
20
|
+
# when enrollment is ready/bonded but the set has not picked it up,
|
|
21
|
+
# 7. verdict: DONE active / DONE scheduled / WAITING-FOLD (with the epoch) /
|
|
22
|
+
# WAITING-SYNC / FAILED (the tool's own words).
|
|
23
|
+
#
|
|
24
|
+
# Nothing here signs a new bond, exits, or replaces the store: `recover.sh
|
|
25
|
+
# --replace-state` is only ever *reported* as the next step when the tool asks.
|
|
26
|
+
#
|
|
27
|
+
# Usage: sudo bash 25-recover-node.sh
|
|
28
|
+
# Env: SYNC_WAIT=<sec> how long catch-up may take before WAITING-SYNC (900)
|
|
29
|
+
# SYNC_MIN=<epochs> head must advance at least this much (3)
|
|
30
|
+
# SYNC_LAG_MAX=<epochs> lag that counts as "in sync" (5)
|
|
31
|
+
# FOLD_WAIT=<sec> wait for scheduled/active after the rejoin (300)
|
|
32
|
+
# PROGRESS_SEC=<sec> progress line interval (60)
|
|
33
|
+
# ALLOW_RESTART=0 report what would be done, restart nothing
|
|
34
|
+
# ===================================================================
|
|
35
|
+
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
source "$(dirname "$0")/lib.sh"
|
|
38
|
+
|
|
39
|
+
require_root
|
|
40
|
+
set_status RUNNING "recover-node"
|
|
41
|
+
|
|
42
|
+
ensure_tool jq jq || die "jq is missing and could not be installed"
|
|
43
|
+
ensure_tool curl curl || die "curl is missing and could not be installed"
|
|
44
|
+
[[ -f "${NODE_ENV}" ]] || die "node.env is missing (${NODE_ENV}) - is this the validator host?"
|
|
45
|
+
[[ -f "${NODE_WALLET}" ]] || die "wallet.json is missing (${NODE_WALLET})"
|
|
46
|
+
|
|
47
|
+
SYNC_WAIT="${SYNC_WAIT:-900}"
|
|
48
|
+
SYNC_MIN="${SYNC_MIN:-3}"
|
|
49
|
+
LAG_MAX="${SYNC_LAG_MAX:-5}"
|
|
50
|
+
FOLD_WAIT="${FOLD_WAIT:-300}"
|
|
51
|
+
ALLOW_RESTART="${ALLOW_RESTART:-1}"
|
|
52
|
+
POLL="${STATUS_POLL:-20}"
|
|
53
|
+
|
|
54
|
+
WALLET=$(wallet_address || echo unknown)
|
|
55
|
+
log "=== recover ${WALLET} on $(hostname) ==="
|
|
56
|
+
|
|
57
|
+
# --- helpers ------------------------------------------------------------
|
|
58
|
+
rpc_up() { [[ "$(head_epoch)" =~ ^[0-9]+$ ]]; }
|
|
59
|
+
|
|
60
|
+
wait_rpc() { # wait_rpc <seconds>
|
|
61
|
+
local limit="$1" start now
|
|
62
|
+
start=$(date +%s)
|
|
63
|
+
while :; do
|
|
64
|
+
rpc_up && return 0
|
|
65
|
+
now=$(date +%s)
|
|
66
|
+
(( now - start >= limit )) && return 1
|
|
67
|
+
sleep 5
|
|
68
|
+
done
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pm2_as() { su - "${NODE_USER}" -c "pm2 $*" 2>&1 | sed 's/^/ /'; }
|
|
72
|
+
|
|
73
|
+
busy_join() { pgrep -af "enroll.sh[ ]join" >/dev/null 2>&1; }
|
|
74
|
+
|
|
75
|
+
# --- 0. never fight another enrollment ---------------------------------
|
|
76
|
+
if busy_join; then
|
|
77
|
+
warn "a join is running right now - not touching the node"
|
|
78
|
+
set_status WAITING-BUSY "a join/enroll is already running; re-run when it finishes"
|
|
79
|
+
exit 2
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# --- 1. diagnose -------------------------------------------------------
|
|
83
|
+
ENROLL=$(rpc_field octra_validatorEnrollment '[]' state)
|
|
84
|
+
HEAD=$(head_epoch)
|
|
85
|
+
CODE=$(status_code)
|
|
86
|
+
log "before : enrollment=${ENROLL:-?} head=${HEAD:-?} /status=HTTP ${CODE:-000} pm2=$(pm2_state)"
|
|
87
|
+
|
|
88
|
+
REJOIN=$(node_script 180 "sh controls/rejoin.sh --check" || true)
|
|
89
|
+
REJOIN_LINE=$(printf '%s\n' "${REJOIN}" | grep -aE '^status = ' | tail -1 || true)
|
|
90
|
+
log "rejoin : ${REJOIN_LINE:-<no status line>}"
|
|
91
|
+
if printf '%s' "${REJOIN_LINE}" | grep -qi "recovery_required"; then
|
|
92
|
+
die "the local store failed its checkpoint check - run: sh controls/recover.sh --replace-state (nothing was changed here)"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
MISMATCH=0
|
|
96
|
+
printf '%s' "${REJOIN_LINE}" | grep -qi "used outside PM2" && MISMATCH=1
|
|
97
|
+
# The tool could not even produce a verdict (the RPC is down): re-creating the pm2
|
|
98
|
+
# session is the safest single action then - it re-applies the env from the dump and
|
|
99
|
+
# gives the node a fresh start, which is also what the "outside PM2" case needs.
|
|
100
|
+
if [[ -z "${REJOIN_LINE}" ]] && ! rpc_up; then
|
|
101
|
+
MISMATCH=1
|
|
102
|
+
warn "rejoin.sh could not reach a verdict (the node does not answer its RPC) - re-creating the pm2 session"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# already fine?
|
|
106
|
+
if printf '%s' "${REJOIN_LINE}" | grep -q "validator_active"; then
|
|
107
|
+
log "this node is already the ACTIVE validator - nothing to recover"
|
|
108
|
+
set_status DONE "already active (nothing to do)"
|
|
109
|
+
echo "RECOVER state=active head=${HEAD:-none}"
|
|
110
|
+
exit 0
|
|
111
|
+
|
|
112
|
+
# --- 2/3. the two things that actually need a restart -------------------
|
|
113
|
+
RESTARTED=0
|
|
114
|
+
if (( MISMATCH )); then
|
|
115
|
+
if (( ALLOW_RESTART )); then
|
|
116
|
+
warn "the data directory is 'used outside PM2' (or the RPC is gone): the pm2 session has to be re-created"
|
|
117
|
+
# `pm2 kill` is NOT used here: it kills the daemon that systemd supervises, so
|
|
118
|
+
# pm2-octra.service ends up 'failed' while the (hung) node keeps running - that
|
|
119
|
+
# is exactly what happened on .99 (2026-11-06). systemd does stop/start, and a
|
|
120
|
+
# node that ignores SIGTERM is reaped by exact process name.
|
|
121
|
+
log "re-creating the pm2 session through systemd (stop -> reap -> start); the node restarts from its own dump"
|
|
122
|
+
systemctl stop pm2-octra 2>/dev/null || true
|
|
123
|
+
systemctl reset-failed pm2-octra 2>/dev/null || true
|
|
124
|
+
sleep 3
|
|
125
|
+
for p in $(pgrep -x octra_node.exe 2>/dev/null || true); do
|
|
126
|
+
warn "the previous node (pid ${p}) survived the stop - sending SIGKILL"
|
|
127
|
+
kill -9 "${p}" 2>/dev/null || true
|
|
128
|
+
done
|
|
129
|
+
sleep 3
|
|
130
|
+
systemctl start pm2-octra 2>/dev/null || warn "systemctl start pm2-octra failed - check: systemctl status pm2-octra"
|
|
131
|
+
sleep 5
|
|
132
|
+
log "pm2-octra : $(systemctl is-active pm2-octra 2>/dev/null || echo unknown)"
|
|
133
|
+
RESTARTED=1
|
|
134
|
+
else
|
|
135
|
+
warn "would re-create the pm2 session (ALLOW_RESTART=0) - the node's own tools cannot run until it is done"
|
|
136
|
+
fi
|
|
137
|
+
elif [[ "${CODE}" != "200" || -z "${HEAD:-}" ]]; then
|
|
138
|
+
if (( ALLOW_RESTART )); then
|
|
139
|
+
warn "the node is not answering its own RPC properly (/status HTTP ${CODE:-000}) - one clean restart"
|
|
140
|
+
pm2_as save >/dev/null 2>&1 || true
|
|
141
|
+
pm2_as restart "$(pm2_name)" || true
|
|
142
|
+
RESTARTED=1
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
if (( RESTARTED )); then
|
|
147
|
+
# opening a 38 GB store takes minutes: 180 s was too optimistic on .99
|
|
148
|
+
if wait_rpc "${RPC_WAIT:-600}"; then
|
|
149
|
+
log "the node answers again: head=$(head_epoch) (pid $(pm2_state | awk '{print $3}'))"
|
|
150
|
+
else
|
|
151
|
+
warn "the RPC did not come back within ${RPC_WAIT:-600}s after the restart"
|
|
152
|
+
MEM_TOTAL=$(awk '/MemTotal/ {printf "%d", $2/1024}' /proc/meminfo 2>/dev/null || echo 0)
|
|
153
|
+
NODE_PID=$(pgrep -x octra_node.exe 2>/dev/null | head -1 || true)
|
|
154
|
+
NODE_RSS=""
|
|
155
|
+
[[ -n "${NODE_PID}" ]] && NODE_RSS=$(awk '/VmRSS/ {printf "%d", $2/1024}' "/proc/${NODE_PID}/status" 2>/dev/null || true)
|
|
156
|
+
warn "the process is up (pid ${NODE_PID:-none}, ${NODE_RSS:-?} MB RSS) on a ${MEM_TOTAL:-?} MB VM"
|
|
157
|
+
if (( ${MEM_TOTAL:-0} > 0 && MEM_TOTAL < 7600 )); then
|
|
158
|
+
warn "the store needs ~8 GB of RAM (16 comfortable): on this VM the node cannot finish opening it -"
|
|
159
|
+
warn "raise the RAM and start it again, or move the node with the Migrate node program"
|
|
160
|
+
set_status WAITING-STARTUP "the node cannot finish starting on a ${MEM_TOTAL} MB VM (needs ~8 GB) - raise the RAM or migrate"
|
|
161
|
+
else
|
|
162
|
+
warn "opening the store can take longer on a slow disk - re-run this step in a few minutes"
|
|
163
|
+
set_status WAITING-RPC "restarted, the local RPC is still silent - it may still be opening the store"
|
|
164
|
+
fi
|
|
165
|
+
echo "RECOVER state=restarted_no_rpc head=none"
|
|
166
|
+
exit 2
|
|
167
|
+
fi
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# --- 4. catch-up: the real gate ----------------------------------------
|
|
171
|
+
START=$(date +%s)
|
|
172
|
+
LAST=${START}
|
|
173
|
+
FIRST=$(head_epoch)
|
|
174
|
+
FIRST="${FIRST:-0}"
|
|
175
|
+
BEST_LAG=""
|
|
176
|
+
VERDICT=""
|
|
177
|
+
while :; do
|
|
178
|
+
NOW_HEAD=$(head_epoch)
|
|
179
|
+
NET=$(network_epoch)
|
|
180
|
+
LAG=""
|
|
181
|
+
if [[ "${NOW_HEAD}" =~ ^[0-9]+$ && "${NET}" =~ ^[0-9]+$ ]]; then
|
|
182
|
+
LAG=$(( NET - NOW_HEAD ))
|
|
183
|
+
(( LAG < 0 )) && LAG=0
|
|
184
|
+
BEST_LAG="${LAG}"
|
|
185
|
+
fi
|
|
186
|
+
MOVED=$(( ${NOW_HEAD:-0} - FIRST ))
|
|
187
|
+
# in sync = head moved at least SYNC_MIN epochs and the lag is within LAG_MAX
|
|
188
|
+
if (( MOVED >= SYNC_MIN )) && [[ -n "${LAG}" ]] && (( LAG <= LAG_MAX )); then
|
|
189
|
+
VERDICT="synced"
|
|
190
|
+
break
|
|
191
|
+
fi
|
|
192
|
+
ELAPSED=$(( $(date +%s) - START ))
|
|
193
|
+
if (( ELAPSED >= SYNC_WAIT )); then
|
|
194
|
+
VERDICT="sync-timeout"
|
|
195
|
+
break
|
|
196
|
+
fi
|
|
197
|
+
if (( $(date +%s) - LAST >= PROGRESS_SEC )); then
|
|
198
|
+
REASON=$(catchup_reason)
|
|
199
|
+
log " catch-up: head ${NOW_HEAD:-?} -> network ${NET:-?} (lag ${LAG:-?}, moved ${MOVED}/${SYNC_MIN})$([[ -n "${REASON}" ]] && echo " - ${REASON}")"
|
|
200
|
+
LAST=$(date +%s)
|
|
201
|
+
fi
|
|
202
|
+
sleep "${POLL}"
|
|
203
|
+
done
|
|
204
|
+
|
|
205
|
+
if [[ "${VERDICT}" == "sync-timeout" ]]; then
|
|
206
|
+
REASON=$(catchup_reason)
|
|
207
|
+
warn "the node is still behind after ${SYNC_WAIT}s (head ${NOW_HEAD:-?}, network ${NET:-?}, lag ${LAG:-?})"
|
|
208
|
+
[[ -n "${REASON}" ]] && warn "the last catch-up attempt said: ${REASON}"
|
|
209
|
+
warn "the node retries by itself - nothing was forced; re-run this step later"
|
|
210
|
+
set_status WAITING-SYNC "catch-up did not finish in ${SYNC_WAIT}s (lag ${LAG:-?}${REASON:+; ${REASON}})"
|
|
211
|
+
echo "RECOVER state=syncing head=${NOW_HEAD:-none} behind=${LAG:-none}"
|
|
212
|
+
exit 2
|
|
213
|
+
fi
|
|
214
|
+
log "in sync: head ${NOW_HEAD:-?} (lag ${BEST_LAG:-?}) - the set logic can run now"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# --- 5. the node's own rejoin path -------------------------------------
|
|
218
|
+
REJOIN=$(node_script 300 "sh controls/rejoin.sh --check" || true)
|
|
219
|
+
REJOIN_LINE=$(printf '%s\n' "${REJOIN}" | grep -aE '^status = ' | tail -1 || true)
|
|
220
|
+
log "rejoin : ${REJOIN_LINE:-<no status line>}"
|
|
221
|
+
REJOIN_STATE=$(printf '%s' "${REJOIN_LINE}" | sed -n 's/^status = \([a-z_]*\).*/\1/p')
|
|
222
|
+
|
|
223
|
+
if [[ "${REJOIN_STATE}" == "validator_active" ]]; then
|
|
224
|
+
set_status DONE "back in the set: validator_active"
|
|
225
|
+
echo "RECOVER state=active head=${NOW_HEAD:-none}"
|
|
226
|
+
exit 0
|
|
227
|
+
fi
|
|
228
|
+
if [[ "${REJOIN_STATE}" == "validator_scheduled" ]]; then
|
|
229
|
+
set_status WAITING-FOLD "admitted to the next set - waiting for the fold ($(post_hint))"
|
|
230
|
+
echo "RECOVER state=scheduled head=${NOW_HEAD:-none}"
|
|
231
|
+
exit 2
|
|
232
|
+
fi
|
|
233
|
+
if printf '%s' "${REJOIN_LINE}" | grep -qi "used outside PM2"; then
|
|
234
|
+
die "the node still reports 'data directory is used outside PM2' - pm2 was not re-created (ALLOW_RESTART=${ALLOW_RESTART})"
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
# the round is behind / readiness is stale: --meet-stage is the tool's own fix and
|
|
238
|
+
# it does not stop the node (only --meet-start would, and that needs a stop)
|
|
239
|
+
if printf '%s' "${REJOIN_LINE}" | grep -qE "round_lagging|round_unobserved|round_unconfirmed|not_ready_to_vote"; then
|
|
240
|
+
warn "the tool says the round/readiness is stale (${REJOIN_STATE}) - staging a rejoin (rejoin.sh --meet-stage)"
|
|
241
|
+
OUT=$(node_script 600 "sh controls/rejoin.sh --meet-stage" || true)
|
|
242
|
+
printf '%s\n' "${OUT}" | grep -aE '^(status|event|round) = ' | sed 's/^/ /' || true
|
|
243
|
+
STAGE_STATE=$(printf '%s\n' "${OUT}" | grep -aE '^status = ' | tail -1 | sed -n 's/^status = \([a-z_]*\).*/\1/p' || true)
|
|
244
|
+
log "meet-stage : ${STAGE_STATE:-<no status line>}"
|
|
245
|
+
case "${STAGE_STATE}" in
|
|
246
|
+
"meet_staged")
|
|
247
|
+
log "the rejoin is staged - the node asks for a round when it is ready (no restart, no exit)" ;;
|
|
248
|
+
"refused")
|
|
249
|
+
warn "rejoin refused: $(printf '%s\n' "${OUT}" | grep -aE '^status = ' | tail -1)" ;;
|
|
250
|
+
*) : ;;
|
|
251
|
+
esac
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# --- 6. re-publish readiness (idempotent: the bond is already on chain) --
|
|
255
|
+
ENROLL=$(rpc_field octra_validatorEnrollment '[]' state)
|
|
256
|
+
if [[ "${ENROLL}" == "bonded" ]]; then
|
|
257
|
+
log "enrollment is 'bonded' without readiness - publishing it again (enroll.sh join)"
|
|
258
|
+
OUT=$(node_script 900 "sh controls/enroll.sh join" || true)
|
|
259
|
+
printf '%s\n' "${OUT}" | grep -aE '^(event|status) = ' | tail -6 | sed 's/^/ /' || true
|
|
260
|
+
elif [[ "${ENROLL}" == "ready" ]]; then
|
|
261
|
+
log "enrollment is 'ready' - the bond and the readiness are on chain; only the set fold is missing"
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# --- 7. wait for the fold ----------------------------------------------
|
|
265
|
+
START=$(date +%s)
|
|
266
|
+
LAST=${START}
|
|
267
|
+
while :; do
|
|
268
|
+
OUT=$(node_script 180 "sh controls/rejoin.sh --check" || true)
|
|
269
|
+
LINE=$(printf '%s\n' "${OUT}" | grep -aE '^status = ' | tail -1 || true)
|
|
270
|
+
STATE=$(printf '%s' "${LINE}" | sed -n 's/^status = \([a-z_]*\).*/\1/p')
|
|
271
|
+
if [[ "${STATE}" == "validator_active" ]]; then
|
|
272
|
+
log "back in the set: $(rpc_field octra_validatorEnrollment '[]' head_epoch)"
|
|
273
|
+
set_status DONE "back in the set: validator_active (rejoin=${STATE})"
|
|
274
|
+
echo "RECOVER state=active head=$(head_epoch)"
|
|
275
|
+
exit 0
|
|
276
|
+
fi
|
|
277
|
+
if (( $(date +%s) - START >= FOLD_WAIT )); then
|
|
278
|
+
warn "the node is in sync but the set has not admitted it yet (${LINE:-no status})"
|
|
279
|
+
warn "this is a chain event now - the console keeps watching and reports the activation"
|
|
280
|
+
set_status WAITING-FOLD "in sync, waiting for the set fold (${STATE:-unknown})"
|
|
281
|
+
echo "RECOVER state=waiting_fold head=$(head_epoch)"
|
|
282
|
+
exit 2
|
|
283
|
+
fi
|
|
284
|
+
if (( $(date +%s) - LAST >= PROGRESS_SEC )); then
|
|
285
|
+
log " waiting for the fold: ${LINE:-no status} ($(post_hint))"
|
|
286
|
+
LAST=$(date +%s)
|
|
287
|
+
fi
|
|
288
|
+
sleep "${POLL}"
|
|
289
|
+
done
|
|
290
|
+
|
|
291
|
+
fi
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 30-status.sh - read-only overview of the ACTIVE node
|
|
4
|
+
#
|
|
5
|
+
# Prints what the console shows for the manage program: identity, membership,
|
|
6
|
+
# bond, wallet balance, the saved bond-increase record and the validator
|
|
7
|
+
# pointer. It signs nothing, restarts nothing.
|
|
8
|
+
#
|
|
9
|
+
# Usage: sudo bash 30-status.sh
|
|
10
|
+
# ===================================================================
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
source "$(dirname "$0")/lib.sh"
|
|
14
|
+
|
|
15
|
+
require_root
|
|
16
|
+
set_status RUNNING "status"
|
|
17
|
+
|
|
18
|
+
ensure_tool jq jq || die "jq is missing and could not be installed"
|
|
19
|
+
ensure_tool curl curl || die "curl is missing and could not be installed"
|
|
20
|
+
|
|
21
|
+
read_state
|
|
22
|
+
ADDR=$(wallet_address || echo unknown)
|
|
23
|
+
PORT=$(node_env_value OCTRA_API_PORT)
|
|
24
|
+
PORT="${PORT:-8080}"
|
|
25
|
+
|
|
26
|
+
rpc() { # rpc <method> <params-json>
|
|
27
|
+
timeout 20 curl -s -X POST -H 'Content-Type: application/json' \
|
|
28
|
+
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"$1\",\"params\":$2}" \
|
|
29
|
+
"http://127.0.0.1:${PORT}/rpc" 2>/dev/null || true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
BALANCE_RAW=$(rpc octra_balance "[\"${ADDR}\"]" | jq -r '.result.balance // empty' 2>/dev/null || true)
|
|
33
|
+
[[ "${BALANCE_RAW}" =~ ^[0-9]+$ ]] || BALANCE_RAW=0
|
|
34
|
+
|
|
35
|
+
log "=== active node: $(hostname) ==="
|
|
36
|
+
log "wallet : ${ADDR}"
|
|
37
|
+
log "role : ${ROLE:-?} process: ${PROCESS:-?} rpc: ${RPC:-?} voting: ${VOTING:-?}"
|
|
38
|
+
log "epoch : ${EPOCH:-?}"
|
|
39
|
+
log "membership : active=${ACTIVE:-?} scheduled=${SCHED:-?} enrollment=${ENROLL:-?}"
|
|
40
|
+
log "bond : $(raw_to_oct "${BOND:-0}") OCT"
|
|
41
|
+
log "wallet balance: $(raw_to_oct "${BALANCE_RAW}") OCT"
|
|
42
|
+
|
|
43
|
+
PATH_SAVED=$(saved_increase_path)
|
|
44
|
+
if [[ -n "${PATH_SAVED}" ]]; then
|
|
45
|
+
log "saved increase:"
|
|
46
|
+
jq -r '" amount = \(.amount) raw, stage = \(.stage), tx = \(.tx_hash)"' "${PATH_SAVED}" 2>/dev/null | sed 's/^/ /' || true
|
|
47
|
+
log " a further increase needs --new (the console adds it automatically)"
|
|
48
|
+
else
|
|
49
|
+
log "saved increase: none (the next increase is the first one, no --new)"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
log "--- enroll.sh status ---"
|
|
53
|
+
node_script 120 "sh controls/enroll.sh status" | grep -E '^(event = (membership|committed_enrollment|enrollment_tx|validator_pointer))' | sed 's/^/ /' || true
|
|
54
|
+
|
|
55
|
+
# an exit leaves the withdrawal to the host timer: say where it stands, so the
|
|
56
|
+
# operator never has to re-run a phase (or read a systemd unit) to find out
|
|
57
|
+
EXIT_WATCH=$(exit_watch_status || true)
|
|
58
|
+
if [[ -n "${EXIT_WATCH}" ]]; then
|
|
59
|
+
log "--- withdrawal ---"
|
|
60
|
+
log " ${EXIT_WATCH}"
|
|
61
|
+
log " retried every 15 minutes by ${EXIT_WATCH_UNIT}.timer (also after a reboot)"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
set_status DONE "role=${ROLE:-?} active=${ACTIVE:-?} bond=$(raw_to_oct "${BOND:-0}") OCT balance=$(raw_to_oct "${BALANCE_RAW}") OCT"
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 40-exit-withdraw.sh - finish an exit: withdraw the stake when the chain allows it
|
|
4
|
+
#
|
|
5
|
+
# Installed by 20-exit-validator.sh as `octra-exit-withdraw.timer` (every 15
|
|
6
|
+
# minutes, also after a reboot). The chain only signs the withdrawal once the
|
|
7
|
+
# unbonding period is over AND the validator is out of the set - which can be
|
|
8
|
+
# weeks after the exit itself, far too long for a console step, and nobody
|
|
9
|
+
# should have to re-run anything by hand. So this job runs on the host:
|
|
10
|
+
#
|
|
11
|
+
# * not yet allowed -> one line in exit-watch.log, the next run retries
|
|
12
|
+
# * the chain allows it -> `controls/enroll.sh withdraw`, wait for
|
|
13
|
+
# validator_bond = 0, then disarm its own timer
|
|
14
|
+
#
|
|
15
|
+
# It never writes /root/manage/status: that file is the verdict of a step the
|
|
16
|
+
# console follows, and a timer must not overwrite it.
|
|
17
|
+
#
|
|
18
|
+
# Usage: sudo bash 40-exit-withdraw.sh (the timer runs this for you)
|
|
19
|
+
# Env: WITHDRAW_TIMEOUT=<sec> bound for enroll.sh withdraw (default 900)
|
|
20
|
+
# BOND_WAIT=<sec> wait for validator_bond = 0 (default 600)
|
|
21
|
+
# ===================================================================
|
|
22
|
+
set -uo pipefail
|
|
23
|
+
source "$(dirname "$0")/lib.sh"
|
|
24
|
+
|
|
25
|
+
# a timer job must never touch a step's verdict, and it must survive a command it
|
|
26
|
+
# is *expecting* to fail (the chain refuses a withdrawal that is too early):
|
|
27
|
+
# lib.sh turns `set -e` on, so it is turned off here again.
|
|
28
|
+
trap - ERR
|
|
29
|
+
set +e
|
|
30
|
+
WATCH_STATUS="${EXIT_WATCH_STATUS:-${MANAGE_DIR}/exit-watch.status}"
|
|
31
|
+
WATCH_LOG="${EXIT_WATCH_LOG:-${MANAGE_DIR}/exit-watch.log}"
|
|
32
|
+
WITHDRAW_TIMEOUT="${WITHDRAW_TIMEOUT:-900}"
|
|
33
|
+
BOND_WAIT="${BOND_WAIT:-600}"
|
|
34
|
+
|
|
35
|
+
mkdir -p "${MANAGE_DIR}" 2>/dev/null || true
|
|
36
|
+
log() { echo -e "[exit-watch] $(date -u +%Y-%m-%dT%H:%M:%SZ) $*" | tee -a "${WATCH_LOG}"; }
|
|
37
|
+
|
|
38
|
+
record() { # record <RESULT> <detail...> - read by the console (30-status.sh)
|
|
39
|
+
local result="$1"
|
|
40
|
+
shift
|
|
41
|
+
printf '%s %s %s\n' "${result}" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*" > "${WATCH_STATUS}"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
summary_value() { printf '%s' "$1" | sed -n "s/.*$2=\([^ ]*\).*/\1/p"; }
|
|
45
|
+
|
|
46
|
+
enrollment_summary() {
|
|
47
|
+
local events state exit_ep withdraw_ep head remaining
|
|
48
|
+
events=$(node_script 120 "sh controls/enroll.sh status" || true)
|
|
49
|
+
state=$(printf '%s\n' "${events}" | grep -oE 'committed_enrollment state = [a-z]+' | awk '{print $4}' | tail -1 || true)
|
|
50
|
+
exit_ep=$(printf '%s\n' "${events}" | grep -oE 'exit_epoch = [0-9]+' | awk '{print $3}' | tail -1 || true)
|
|
51
|
+
withdraw_ep=$(printf '%s\n' "${events}" | grep -oE 'withdraw_epoch = [0-9]+' | awk '{print $3}' | tail -1 || true)
|
|
52
|
+
head=$(printf '%s\n' "${events}" | grep -oE 'head_epoch = [0-9]+' | awk '{print $3}' | tail -1 || true)
|
|
53
|
+
remaining=$(printf '%s\n' "${events}" | grep -oE 'remaining_epochs = [0-9]+' | awk '{print $3}' | tail -1 || true)
|
|
54
|
+
printf 'state=%s exit_epoch=%s withdraw_epoch=%s head=%s remaining=%s\n' \
|
|
55
|
+
"${state:-unknown}" "${exit_ep:-none}" "${withdraw_ep:-none}" "${head:-0}" "${remaining:-none}"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
read_state
|
|
59
|
+
ADDR=$(wallet_address || echo unknown)
|
|
60
|
+
|
|
61
|
+
# --- already finished? -------------------------------------------------
|
|
62
|
+
if [[ "${BOND:-0}" == "0" && "${ENROLL:-}" != "bonded" && "${ENROLL:-}" != "ready" && "${ENROLL:-}" != "exiting" ]]; then
|
|
63
|
+
log "${ADDR}: no bond on chain any more (enrollment=${ENROLL:-absent}) - the exit is complete"
|
|
64
|
+
record DONE "bond 0 (enrollment=${ENROLL:-absent}) - nothing left to withdraw"
|
|
65
|
+
exit_watch_disarm
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
SUMMARY=$(enrollment_summary)
|
|
71
|
+
STATE=$(summary_value "${SUMMARY}" state)
|
|
72
|
+
WITHDRAW_EP=$(summary_value "${SUMMARY}" withdraw_epoch)
|
|
73
|
+
HEAD=$(summary_value "${SUMMARY}" head)
|
|
74
|
+
REMAINING=$(summary_value "${SUMMARY}" remaining)
|
|
75
|
+
|
|
76
|
+
if [[ "${STATE}" != "exiting" ]]; then
|
|
77
|
+
log "${ADDR}: the exit is not committed yet (enrollment=${ENROLL:-?}, state=${STATE:-unknown}) - the console submits it"
|
|
78
|
+
record WAITING "exit not committed yet (state=${STATE:-unknown}, enrollment=${ENROLL:-?})"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
if [[ "${REMAINING}" =~ ^[0-9]+$ ]] && (( REMAINING > 0 )); then
|
|
83
|
+
RATE=$(epoch_seconds || true)
|
|
84
|
+
ETA=""
|
|
85
|
+
if [[ -n "${RATE}" ]]; then
|
|
86
|
+
SECS=$(epoch_eta "${REMAINING}" "${RATE}" || true)
|
|
87
|
+
[[ -n "${SECS}" ]] && ETA=$(fmt_duration "${SECS}")
|
|
88
|
+
fi
|
|
89
|
+
log "${ADDR}: unbonding - head ${HEAD} of unlock epoch ${WITHDRAW_EP}, ${REMAINING} epoch(s) left${ETA:+ (about ${ETA})}"
|
|
90
|
+
record WAITING "unbonding: head ${HEAD} of ${WITHDRAW_EP} (${REMAINING} epoch(s) left${ETA:+, about ${ETA}})"
|
|
91
|
+
exit 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# --- the chain allows the withdrawal now -------------------------------
|
|
95
|
+
if [[ "${ACTIVE:-false}" == "true" || "${SCHED:-false}" == "true" ]]; then
|
|
96
|
+
log "${ADDR}: unbonding over, but the validator is still in the set (active=${ACTIVE:-?} scheduled=${SCHED:-?}) - retrying"
|
|
97
|
+
record WAITING "unbonding over, still in the set (active=${ACTIVE:-?} scheduled=${SCHED:-?})"
|
|
98
|
+
exit 0
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
log "${ADDR}: unbonding over (head ${HEAD}, unlock epoch ${WITHDRAW_EP}) - signing the withdrawal"
|
|
102
|
+
# no `|| true` here: the exit status of the signing attempt decides whether this
|
|
103
|
+
# run is a WAITING (the chain refuses) or an ERROR (something is wrong)
|
|
104
|
+
OUT=$(node_script "${WITHDRAW_TIMEOUT}" "sh controls/enroll.sh withdraw")
|
|
105
|
+
RC=$?
|
|
106
|
+
printf '%s\n' "${OUT}" | sed 's/^/ /' >> "${WATCH_LOG}"
|
|
107
|
+
if (( RC != 0 )) && ! printf '%s' "${OUT}" | grep -qE "already|previously_confirmed"; then
|
|
108
|
+
if printf '%s' "${OUT}" | grep -qE "unbonding period incomplete|waits for removal"; then
|
|
109
|
+
log "the chain still refuses the withdrawal ($(printf '%s' "${OUT}" | grep -oE 'unbonding period incomplete.*|withdrawal waits.*' | tail -1)) - retrying"
|
|
110
|
+
record WAITING "chain refused the withdrawal - retrying every 15 min"
|
|
111
|
+
exit 0
|
|
112
|
+
fi
|
|
113
|
+
log "the withdrawal failed (exit ${RC}) - see the lines above; the next run retries"
|
|
114
|
+
record ERROR "withdraw failed (exit ${RC})"
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
START=$(date +%s)
|
|
119
|
+
while :; do
|
|
120
|
+
read_state
|
|
121
|
+
if [[ "${BOND:-0}" == "0" ]]; then
|
|
122
|
+
log "${ADDR}: stake returned - validator_bond = 0, the exit is complete"
|
|
123
|
+
record DONE "stake returned (bond 0, exit epoch $(summary_value "${SUMMARY}" exit_epoch))"
|
|
124
|
+
exit_watch_disarm
|
|
125
|
+
exit 0
|
|
126
|
+
fi
|
|
127
|
+
if (( $(date +%s) - START >= BOND_WAIT )); then
|
|
128
|
+
log "${ADDR}: withdrawal submitted, the registry still shows $(raw_to_oct "${BOND:-0}") OCT - retrying"
|
|
129
|
+
record WAITING "withdraw submitted, bond still $(raw_to_oct "${BOND:-0}") OCT"
|
|
130
|
+
exit 0
|
|
131
|
+
fi
|
|
132
|
+
sleep 20
|
|
133
|
+
done
|