ovtools 0.0.0-stage → 1.0.0
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 +483 -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 +2712 -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 +7 -0
- package/public/index.html +1080 -0
- package/public/styles.css +552 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 01-prepare-target.sh — prepare the NEW VPS for the data pull
|
|
4
|
+
#
|
|
5
|
+
# * installs the tools needed by the migration (rsync, jq, git, ...)
|
|
6
|
+
# * creates the `octra` system user (same as scripts/install-deps.sh)
|
|
7
|
+
# * creates the node directories on the 1 TB volume
|
|
8
|
+
# * generates an SSH key for root so it can pull from the OLD VPS
|
|
9
|
+
#
|
|
10
|
+
# Run on the NEW VPS AFTER scripts/pre-run.sh:
|
|
11
|
+
# sudo bash 01-prepare-target.sh
|
|
12
|
+
# Optional:
|
|
13
|
+
# FORCE=1 keep an existing data set (resumable copy of this migration)
|
|
14
|
+
# RESET_TARGET=1 drop a stale/foreign node state and start clean
|
|
15
|
+
#
|
|
16
|
+
# Idempotent - safe to re-run.
|
|
17
|
+
# ===================================================================
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
source "$(dirname "$0")/lib.sh"
|
|
21
|
+
|
|
22
|
+
require_root
|
|
23
|
+
set_status RUNNING "prepare-target"
|
|
24
|
+
rm -f "${MIG_DIR}/prep.blocked"
|
|
25
|
+
|
|
26
|
+
# --- Freshness guard -------------------------------------------------
|
|
27
|
+
# Never prepare over an existing node: a second copy of the same wallet is
|
|
28
|
+
# exactly what must not happen.
|
|
29
|
+
#
|
|
30
|
+
# A provider block volume SURVIVES an OS re-install, so "I just reinstalled the
|
|
31
|
+
# VPS" does not guarantee an empty chain state. pre-run.sh formats any volume
|
|
32
|
+
# that does not carry an Octra data set (automatic, no checkbox) and records
|
|
33
|
+
# what it did in /root/migration/prerun.env. The orchestrator reads that file and
|
|
34
|
+
# passes the matching flag, so this script never contradicts pre-run.sh:
|
|
35
|
+
#
|
|
36
|
+
# (nothing) fresh target - nothing to protect
|
|
37
|
+
# FORCE=1 the data set is a resumable copy of THIS migration -
|
|
38
|
+
# keep it and prepare anyway
|
|
39
|
+
# RESET_TARGET=1 the data set is stale/foreign - drop the node state
|
|
40
|
+
# (chain state, sync stage, source tree + keys) and start
|
|
41
|
+
# clean; the pull copies the source's state right after
|
|
42
|
+
# WIPE_VOLUME=1 ... bash /root/pre-run.sh (reformat the volume instead)
|
|
43
|
+
FRESH=1
|
|
44
|
+
if [[ -f "${NODE_SRC_DIR}/.keys/validator/wallet.json" ]]; then
|
|
45
|
+
FRESH=0
|
|
46
|
+
fi
|
|
47
|
+
if [[ -f "${NODE_DATA_DIR}/HEAD.json" ]]; then
|
|
48
|
+
FRESH=0
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
reset_target_state() {
|
|
52
|
+
local path
|
|
53
|
+
warn "RESET_TARGET=1 - dropping the stale node state on $(hostname):"
|
|
54
|
+
for path in "${NODE_DATA_DIR}" "${NODE_SYNC_STAGE}" "${NODE_SRC_DIR}"; do
|
|
55
|
+
[[ -e "${path}" ]] || continue
|
|
56
|
+
warn " rm -rf ${path}"
|
|
57
|
+
rm -rf "${path}"
|
|
58
|
+
done
|
|
59
|
+
log "node state cleared - the pull will copy the source state next"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if [[ "${RESET_TARGET:-0}" == "1" ]]; then
|
|
63
|
+
if [[ -n "$(node_processes)" ]]; then
|
|
64
|
+
die "RESET_TARGET=1 refused: a node is already running on this host ($(node_processes | awk '{print $1}' | head -1)) - stop it first, this host would be a live validator"
|
|
65
|
+
fi
|
|
66
|
+
if (( FRESH == 0 )); then
|
|
67
|
+
reset_target_state
|
|
68
|
+
else
|
|
69
|
+
log "RESET_TARGET=1 requested, but the target is already fresh - nothing to clear"
|
|
70
|
+
fi
|
|
71
|
+
elif [[ "${FORCE:-0}" == "1" ]]; then
|
|
72
|
+
if (( FRESH == 0 )); then
|
|
73
|
+
warn "FORCE=1 - keeping the existing data set on this host (resuming the same migration)"
|
|
74
|
+
fi
|
|
75
|
+
elif (( FRESH == 0 )); then
|
|
76
|
+
if [[ -f "${NODE_SRC_DIR}/.keys/validator/wallet.json" ]]; then
|
|
77
|
+
FOUND="${NODE_SRC_DIR}/.keys/validator/wallet.json"
|
|
78
|
+
else
|
|
79
|
+
FOUND="${NODE_DATA_DIR}/HEAD.json"
|
|
80
|
+
fi
|
|
81
|
+
# Machine-readable marker: the orchestrator turns it into a self-heal
|
|
82
|
+
# (reformat the volume, or RESET_TARGET=1) instead of retrying the same call.
|
|
83
|
+
printf 'target-not-fresh %s\n' "${FOUND}" > "${MIG_DIR}/prep.blocked"
|
|
84
|
+
die "${FOUND} already exists - the target still holds a node (nothing of this migration has been copied yet): pre-run.sh with WIPE_VOLUME=1 reformats the volume, RESET_TARGET=1 drops just the node state, FORCE=1 keeps it (resume)"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
log "Checking the migration tooling (rsync, jq, git, curl, psmisc)..."
|
|
88
|
+
export DEBIAN_FRONTEND=noninteractive
|
|
89
|
+
export TERM="${TERM:-dumb}"
|
|
90
|
+
# check -> install -> continue: a fresh VPS may have none of these, and a retry
|
|
91
|
+
# must not re-run a full apt install for tools that are already there
|
|
92
|
+
ensure_package ca-certificates || die "cannot install ca-certificates"
|
|
93
|
+
ensure_tool rsync rsync || die "rsync is missing and could not be installed"
|
|
94
|
+
ensure_tool jq jq || die "jq is missing and could not be installed"
|
|
95
|
+
ensure_tool git git || die "git is missing and could not be installed"
|
|
96
|
+
ensure_tool curl curl || die "curl is missing and could not be installed"
|
|
97
|
+
ensure_tool wget wget || die "wget is missing and could not be installed"
|
|
98
|
+
ensure_tool pgrep psmisc || die "pgrep (psmisc) is missing and could not be installed"
|
|
99
|
+
ensure_tool pkill psmisc || die "pkill (psmisc) is missing and could not be installed"
|
|
100
|
+
ensure_package procps || die "cannot install procps"
|
|
101
|
+
log "Packages ready: $(rsync --version | head -1)"
|
|
102
|
+
|
|
103
|
+
# --- octra user (same convention as scripts/install-deps.sh) --------
|
|
104
|
+
if ! id "${NODE_USER}" &>/dev/null; then
|
|
105
|
+
log "Creating system user '${NODE_USER}'..."
|
|
106
|
+
useradd -r -m -d "/home/${NODE_USER}" -s /bin/bash "${NODE_USER}"
|
|
107
|
+
else
|
|
108
|
+
log "User '${NODE_USER}' already exists ($(id -u "${NODE_USER}"))"
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# --- Data store must exist before we write anything -----------------
|
|
112
|
+
# pre-run.sh has already mounted a 1 TB volume, or (no volume attached) created
|
|
113
|
+
# the directory on the largest filesystem. Either way the path must exist and be
|
|
114
|
+
# writable, and every sub-directory is created here when missing.
|
|
115
|
+
ensure_dirs
|
|
116
|
+
if ! mountpoint -q "${NODE_DATA_ROOT}" 2>/dev/null; then
|
|
117
|
+
warn "${NODE_DATA_ROOT} is not a separate mount point - the node data lives on the root filesystem"
|
|
118
|
+
fi
|
|
119
|
+
FREE_GB=$(df -BG --output=avail "${NODE_DATA_ROOT}" 2>/dev/null | tail -1 | tr -dc '0-9')
|
|
120
|
+
if [[ -n "${FREE_GB}" ]] && (( FREE_GB < 60 )); then
|
|
121
|
+
die "not enough free space on ${NODE_DATA_ROOT} (${FREE_GB}G free, ~41 GB is copied)"
|
|
122
|
+
fi
|
|
123
|
+
log "Data root: ${NODE_DATA_ROOT} ($(df -h "${NODE_DATA_ROOT}" | tail -1 | awk '{print $2}' ) total, $(df -h "${NODE_DATA_ROOT}" | tail -1 | awk '{print $4}') free)"
|
|
124
|
+
|
|
125
|
+
# --- Root SSH key used to pull data from the OLD VPS ----------------
|
|
126
|
+
# NOTE: we deliberately do NOT touch sshd_config. Password login is the
|
|
127
|
+
# operator's out-of-band access path; pubkey auth is already enabled by
|
|
128
|
+
# default on Debian, which is all the pull needs.
|
|
129
|
+
install -d -m 700 /root/.ssh
|
|
130
|
+
if [[ ! -f /root/.ssh/id_ed25519 ]]; then
|
|
131
|
+
log "Generating root SSH key for the data pull..."
|
|
132
|
+
ssh-keygen -t ed25519 -N "" -C "octra-migration@$(hostname)" -f /root/.ssh/id_ed25519 >/dev/null
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
cp /root/.ssh/id_ed25519.pub "${MIG_DIR}/target_ssh_key.pub"
|
|
136
|
+
chmod 644 "${MIG_DIR}/target_ssh_key.pub"
|
|
137
|
+
|
|
138
|
+
log "Target prepared."
|
|
139
|
+
log "Disk: $(df -h "${NODE_DATA_ROOT}" | tail -1 | awk '{print $2" total, "$4" free"}')"
|
|
140
|
+
log "Swap: $(free -h | awk '/^Swap:/ {print $2}')"
|
|
141
|
+
echo
|
|
142
|
+
log "PUBLIC KEY (must be appended to ${OLD_USER}@${OLD_HOST:-<old-vps>}:~/.ssh/authorized_keys):"
|
|
143
|
+
cat "${MIG_DIR}/target_ssh_key.pub"
|
|
144
|
+
|
|
145
|
+
set_status DONE "prepare-target"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 02-authorize-pull.sh — run ON THE OLD VPS
|
|
4
|
+
#
|
|
5
|
+
# Appends the NEW VPS root public key to root's authorized_keys so the
|
|
6
|
+
# target can rsync the node state directly from the source machine.
|
|
7
|
+
#
|
|
8
|
+
# Usage (on the OLD vps):
|
|
9
|
+
# bash 02-authorize-pull.sh "ssh-ed25519 AAAA... octra-migration@onode"
|
|
10
|
+
# bash 02-authorize-pull.sh /root/migration/target_ssh_key.pub
|
|
11
|
+
#
|
|
12
|
+
# Idempotent — the key is only added once.
|
|
13
|
+
# ===================================================================
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
source "$(dirname "$0")/lib.sh"
|
|
17
|
+
|
|
18
|
+
require_root
|
|
19
|
+
|
|
20
|
+
KEY_INPUT="${1:-${MIG_DIR}/target_ssh_key.pub}"
|
|
21
|
+
[[ -n "${KEY_INPUT}" ]] || die "usage: $0 <pubkey-line|pubkey-file>"
|
|
22
|
+
|
|
23
|
+
if [[ -f "${KEY_INPUT}" ]]; then
|
|
24
|
+
PUBKEY=$(tr -d '\r\n' < "${KEY_INPUT}")
|
|
25
|
+
else
|
|
26
|
+
PUBKEY=$(printf '%s' "${KEY_INPUT}" | tr -d '\r\n')
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
[[ "${PUBKEY}" == ssh-* ]] || die "not a public key: ${PUBKEY:0:40}..."
|
|
30
|
+
|
|
31
|
+
install -d -m 700 /root/.ssh
|
|
32
|
+
touch /root/.ssh/authorized_keys
|
|
33
|
+
chmod 600 /root/.ssh/authorized_keys
|
|
34
|
+
|
|
35
|
+
if grep -qxF "${PUBKEY}" /root/.ssh/authorized_keys; then
|
|
36
|
+
log "Key already authorized — nothing to do"
|
|
37
|
+
else
|
|
38
|
+
printf '%s\n' "${PUBKEY}" >> /root/.ssh/authorized_keys
|
|
39
|
+
log "Key authorized for root"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
log "authorized_keys now holds $(wc -l < /root/.ssh/authorized_keys) key(s)"
|
|
43
|
+
set_status DONE "authorize-pull"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 03-pull-state.sh — copy the whole validator from OLD VPS to this one
|
|
4
|
+
#
|
|
5
|
+
# pull 1 (initial) : while the old node is STILL RUNNING
|
|
6
|
+
# → whole-file mode (-W), fastest for a cold copy
|
|
7
|
+
# pull 2 (final) : after 04-stop-source.sh has stopped the old node
|
|
8
|
+
# → rolling-checksum delta, so only appended bytes
|
|
9
|
+
# of the live store suffixes are sent
|
|
10
|
+
#
|
|
11
|
+
# Usage (on the NEW vps):
|
|
12
|
+
# OLD_HOST=203.0.113.27 bash 03-pull-state.sh # initial
|
|
13
|
+
# OLD_HOST=203.0.113.27 bash 03-pull-state.sh --final # delta
|
|
14
|
+
#
|
|
15
|
+
# Copies:
|
|
16
|
+
# /opt/octra/libv_litecore/ (source tree, toolchain, binaries, .keys)
|
|
17
|
+
# /var/lib/octra/devnet/ (chain state, 39 GB)
|
|
18
|
+
# /var/lib/octra/devnet.state_sync/
|
|
19
|
+
# ===================================================================
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
source "$(dirname "$0")/lib.sh"
|
|
23
|
+
|
|
24
|
+
require_root
|
|
25
|
+
require_old_host
|
|
26
|
+
require_cmd rsync
|
|
27
|
+
|
|
28
|
+
MODE="initial"
|
|
29
|
+
[[ "${1:-}" == "--final" ]] && MODE="final"
|
|
30
|
+
|
|
31
|
+
RSYNC_OPTS=(-aH --numeric-ids --info=progress2 --stats --no-inc-recursive)
|
|
32
|
+
if [[ "${MODE}" == "initial" ]]; then
|
|
33
|
+
RSYNC_OPTS+=(-W)
|
|
34
|
+
else
|
|
35
|
+
# delta mode: list everything so appended store bytes are found quickly
|
|
36
|
+
RSYNC_OPTS=(-aH --numeric-ids --info=progress2 --stats --delete --delete-delay)
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
FREE_GB=$(df -BG --output=avail "${NODE_DATA_ROOT}" | tail -1 | tr -dc '0-9')
|
|
40
|
+
[[ "${FREE_GB}" -ge 60 ]] || die "not enough free space on ${NODE_DATA_ROOT} (${FREE_GB}G)"
|
|
41
|
+
|
|
42
|
+
# While the source node is RUNNING its logs/WAL/receipt files vanish under
|
|
43
|
+
# rsync's feet, which makes rsync exit 23/24. That is expected: the final
|
|
44
|
+
# delta pass (run with the source stopped) reconciles whatever moved.
|
|
45
|
+
RSYNC_TOLERANT=(23 24)
|
|
46
|
+
run_rsync() { # run_rsync <label> <src> <dst>
|
|
47
|
+
local label="$1" src="$2" dst="$3" rc=0
|
|
48
|
+
log "--- ${label} ---"
|
|
49
|
+
# rsync 3.5 prints one progress line per parallel transfer thread, which
|
|
50
|
+
# produced multi-megabyte single-line log entries. Collapse each update to
|
|
51
|
+
# its last "bytes N% rate" field and keep byte totals / warnings, so the
|
|
52
|
+
# log stays readable while the orchestrator can still poll the percentage.
|
|
53
|
+
# set +e / PIPESTATUS: grep returns 1 when nothing matches, and we must not
|
|
54
|
+
# lose rsync's own exit code.
|
|
55
|
+
set +e
|
|
56
|
+
rsync "${RSYNC_OPTS[@]}" \
|
|
57
|
+
-e "ssh ${SSH_OPTS[*]} -o Compression=no" \
|
|
58
|
+
"${src}" "${dst}" 2>&1 \
|
|
59
|
+
| tr '\r' '\n' \
|
|
60
|
+
| grep -a --line-buffered -E '[0-9]+%|^sent |^total size|^rsync' \
|
|
61
|
+
| sed -u -E 's/^.* ([0-9,]+ +[0-9]+% +[0-9.]+[kMG]?B\/s).*$/\1/'
|
|
62
|
+
rc=${PIPESTATUS[0]}
|
|
63
|
+
set -e
|
|
64
|
+
if (( rc == 0 )); then
|
|
65
|
+
return 0
|
|
66
|
+
fi
|
|
67
|
+
local tolerated=0
|
|
68
|
+
for code in "${RSYNC_TOLERANT[@]}"; do
|
|
69
|
+
[[ "${rc}" -eq "${code}" ]] && tolerated=1
|
|
70
|
+
done
|
|
71
|
+
if (( tolerated == 1 )); then
|
|
72
|
+
warn "${label}: rsync exit ${rc} (files changed during the copy) — tolerated on a live node; the --final pass fixes it"
|
|
73
|
+
return 0
|
|
74
|
+
fi
|
|
75
|
+
die "${label}: rsync failed with exit ${rc}"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
set_status RUNNING "pull-${MODE}"
|
|
79
|
+
START=$(date +%s)
|
|
80
|
+
log "=== pull (${MODE}) from ${OLD_USER}@${OLD_HOST} ==="
|
|
81
|
+
|
|
82
|
+
run_rsync "1/3 source tree ${NODE_SRC_DIR}" \
|
|
83
|
+
"${OLD_USER}@${OLD_HOST}:${NODE_SRC_DIR}/" "${NODE_SRC_DIR}/"
|
|
84
|
+
|
|
85
|
+
mkdir -p "${NODE_DATA_DIR}"
|
|
86
|
+
run_rsync "2/3 chain state ${NODE_DATA_DIR}" \
|
|
87
|
+
"${OLD_USER}@${OLD_HOST}:${NODE_DATA_DIR}/" "${NODE_DATA_DIR}/"
|
|
88
|
+
|
|
89
|
+
mkdir -p "${NODE_SYNC_STAGE}"
|
|
90
|
+
if on_old "test -d ${NODE_SYNC_STAGE}"; then
|
|
91
|
+
run_rsync "3/3 state-sync stage ${NODE_SYNC_STAGE}" \
|
|
92
|
+
"${OLD_USER}@${OLD_HOST}:${NODE_SYNC_STAGE}/" "${NODE_SYNC_STAGE}/"
|
|
93
|
+
else
|
|
94
|
+
warn "state-sync stage does not exist on the source — skipped"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
ELAPSED=$(( $(date +%s) - START ))
|
|
98
|
+
log "=== pull (${MODE}) finished in $((ELAPSED / 60))m$((ELAPSED % 60))s ==="
|
|
99
|
+
log "Local size: src $(du -sh "${NODE_SRC_DIR}" | awk '{print $1}'), data $(du -sh "${NODE_DATA_DIR}" | awk '{print $1}')"
|
|
100
|
+
log "Source size: $(on_old "du -sh ${NODE_SRC_DIR} ${NODE_DATA_DIR} 2>/dev/null | tr '\n' ' '")"
|
|
101
|
+
|
|
102
|
+
set_status DONE "pull-${MODE}" "${ELAPSED}s"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 04-stop-source.sh — stop the validator on the OLD VPS (decommission)
|
|
4
|
+
#
|
|
5
|
+
# A validator wallet must NEVER be active on two machines at once
|
|
6
|
+
# (double-signing / equivocation). This step:
|
|
7
|
+
# 1. records the last status of the old node
|
|
8
|
+
# 2. stops it via the node's own controls/stop.sh (pm2)
|
|
9
|
+
# 3. proves no octra_node process is left
|
|
10
|
+
# 4. disables the pm2 boot unit so it cannot resurrect after a reboot
|
|
11
|
+
# 5. drops a decommission note in the old node's home
|
|
12
|
+
#
|
|
13
|
+
# Usage (on the NEW vps):
|
|
14
|
+
# OLD_HOST=203.0.113.27 bash 04-stop-source.sh
|
|
15
|
+
# ===================================================================
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
source "$(dirname "$0")/lib.sh"
|
|
19
|
+
|
|
20
|
+
require_root
|
|
21
|
+
require_old_host
|
|
22
|
+
set_status RUNNING "stop-source"
|
|
23
|
+
log "=== stopping node on ${OLD_HOST} ==="
|
|
24
|
+
|
|
25
|
+
# --- 1. record final status of the source node ----------------------
|
|
26
|
+
# Keep the first (running) snapshot: on a re-run the node is already down
|
|
27
|
+
# and the status no longer carries the live epoch/voting fields.
|
|
28
|
+
if [[ ! -s "${MIG_DIR}/source-final-stat.txt" ]] || ! grep -q '^address = ' "${MIG_DIR}/source-final-stat.txt"; then
|
|
29
|
+
log "Capturing final status of the old node..."
|
|
30
|
+
on_old "su - ${NODE_USER} -c 'cd ${NODE_SRC_DIR} && sh controls/stat.sh' 2>&1" \
|
|
31
|
+
| tee "${MIG_DIR}/source-final-stat.txt" | grep -E '^(role|address|epoch|head_epoch|voting|validator_active) =' || true
|
|
32
|
+
else
|
|
33
|
+
log "Keeping existing source status snapshot ($(grep -m1 '^address = ' "${MIG_DIR}/source-final-stat.txt"))"
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
WALLET_ADDRESS=$(grep -m1 '^address = ' "${MIG_DIR}/source-final-stat.txt" | awk -F' = ' '{print $2}' || true)
|
|
37
|
+
[[ -n "${WALLET_ADDRESS}" ]] || warn "could not read wallet address from source status"
|
|
38
|
+
log "Source wallet: ${WALLET_ADDRESS:-unknown}"
|
|
39
|
+
|
|
40
|
+
# --- 2. graceful stop ----------------------------------------------
|
|
41
|
+
log "Running controls/stop.sh on the source..."
|
|
42
|
+
on_old "su - ${NODE_USER} -c 'cd ${NODE_SRC_DIR} && sh controls/stop.sh' 2>&1" | tee -a "${MIG_LOG}"
|
|
43
|
+
|
|
44
|
+
# --- 3. verify nothing is left running -----------------------------
|
|
45
|
+
LEFT=$(on_old_node_processes)
|
|
46
|
+
for i in {1..20}; do
|
|
47
|
+
[[ -z "${LEFT}" ]] && break
|
|
48
|
+
sleep 1
|
|
49
|
+
LEFT=$(on_old_node_processes)
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
if [[ -n "${LEFT}" ]]; then
|
|
53
|
+
warn "node processes still alive after stop — sending SIGTERM"
|
|
54
|
+
on_old "pkill -TERM -f '${NODE_PROC_RE}' || true"
|
|
55
|
+
sleep 5
|
|
56
|
+
LEFT=$(on_old_node_processes)
|
|
57
|
+
fi
|
|
58
|
+
if [[ -n "${LEFT}" ]]; then
|
|
59
|
+
die "could not stop the source node — DO NOT start the new node (wallet would be double-active)"
|
|
60
|
+
fi
|
|
61
|
+
log "Source node processes are gone (none left)"
|
|
62
|
+
|
|
63
|
+
# --- 4. make sure it cannot come back on reboot ---------------------
|
|
64
|
+
if on_old "systemctl is-enabled --quiet pm2-${NODE_USER} 2>/dev/null"; then
|
|
65
|
+
on_old "systemctl disable pm2-${NODE_USER} 2>/dev/null || true"
|
|
66
|
+
log "Disabled pm2-${NODE_USER} boot unit on the source"
|
|
67
|
+
else
|
|
68
|
+
log "pm2 boot unit on the source already disabled"
|
|
69
|
+
fi
|
|
70
|
+
on_old "su - ${NODE_USER} -c 'pm2 save --force' >/dev/null 2>&1 || true"
|
|
71
|
+
|
|
72
|
+
# --- 5. decommission note ------------------------------------------
|
|
73
|
+
on_old "cat > /root/MIGRATED-AWAY.txt <<'EOF'
|
|
74
|
+
This VPS no longer runs the Octra validator.
|
|
75
|
+
The validator (wallet ${WALLET_ADDRESS:-unknown}) was migrated on $(date -u +%Y-%m-%dT%H:%M:%SZ).
|
|
76
|
+
New host: $(hostname) / see the operator's records.
|
|
77
|
+
DO NOT start the node here again while the new host is live: the same wallet
|
|
78
|
+
would be active twice and the validator would equivocate.
|
|
79
|
+
EOF"
|
|
80
|
+
log "Decommission note written to ${OLD_HOST}:/root/MIGRATED-AWAY.txt"
|
|
81
|
+
|
|
82
|
+
log "=== source stopped — safe to run the final delta pull ==="
|
|
83
|
+
date -u +%Y-%m-%dT%H:%M:%SZ > "${MIG_DIR}/downtime-start.txt"
|
|
84
|
+
log "downtime clock started: $(cat "${MIG_DIR}/downtime-start.txt")"
|
|
85
|
+
set_status DONE "stop-source"
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 05-finalize-target.sh — make the copied tree bootable on this host
|
|
4
|
+
#
|
|
5
|
+
# 1. back up node.env and point OCTRA_ADVERTISE_ENDPOINT at this VPS
|
|
6
|
+
# 2. verify the binary hashes still match node.env (integrity gate)
|
|
7
|
+
# 3. fix ownership (the copy may carry the source's numeric uid)
|
|
8
|
+
# 4. run controls/install.sh (runtime libs + nodejs + pm2 + boot unit)
|
|
9
|
+
# 5. run controls/lib/validator_config.py --check-runtime
|
|
10
|
+
#
|
|
11
|
+
# Nothing here touches config/network.env, the chain data or the wallet:
|
|
12
|
+
# a migrated validator must NOT re-sync and must NOT re-enroll.
|
|
13
|
+
#
|
|
14
|
+
# Usage (on the NEW vps):
|
|
15
|
+
# NEW_HOST=203.0.113.133 bash 05-finalize-target.sh
|
|
16
|
+
# ===================================================================
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
source "$(dirname "$0")/lib.sh"
|
|
20
|
+
|
|
21
|
+
require_root
|
|
22
|
+
NEW_HOST="${NEW_HOST:-${1:-}}"
|
|
23
|
+
[[ -n "${NEW_HOST}" ]] || NEW_HOST=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null || hostname -I | awk '{print $1}')
|
|
24
|
+
|
|
25
|
+
[[ -f "${NODE_ENV}" ]] || die "node.env missing at ${NODE_ENV} — did the pull complete?"
|
|
26
|
+
[[ -f "${NODE_SRC_DIR}/.keys/validator/wallet.json" ]] || die "wallet.json missing"
|
|
27
|
+
set_status RUNNING "finalize-target"
|
|
28
|
+
|
|
29
|
+
# --- 1. advertise endpoint (the only host-specific field) -----------
|
|
30
|
+
log "=== finalize (advertise → ${NEW_HOST}:${CONSENSUS_PORT}) ==="
|
|
31
|
+
CURRENT_ADV=$(grep -m1 '^OCTRA_ADVERTISE_ENDPOINT=' "${NODE_ENV}" | cut -d= -f2-)
|
|
32
|
+
log "current advertise: ${CURRENT_ADV}"
|
|
33
|
+
|
|
34
|
+
if [[ "${CURRENT_ADV}" == "${NEW_HOST}:${CONSENSUS_PORT}" ]]; then
|
|
35
|
+
log "advertise already correct"
|
|
36
|
+
else
|
|
37
|
+
cp -a "${NODE_ENV}" "${NODE_ENV}.pre-migration.bak"
|
|
38
|
+
sed -i "s|^OCTRA_ADVERTISE_ENDPOINT=.*|OCTRA_ADVERTISE_ENDPOINT=${NEW_HOST}:${CONSENSUS_PORT}|" "${NODE_ENV}"
|
|
39
|
+
log "advertise updated: $(grep -m1 '^OCTRA_ADVERTISE_ENDPOINT=' "${NODE_ENV}")"
|
|
40
|
+
log "backup kept at ${NODE_ENV}.pre-migration.bak"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# --- 2. integrity gate: binaries must still match node.env ---------
|
|
44
|
+
log "Verifying binary hashes against node.env..."
|
|
45
|
+
check_hash() { # check_hash <env_key> <file>
|
|
46
|
+
local key="$1" file="$2" want got
|
|
47
|
+
want=$(grep -m1 "^${key}=" "${NODE_ENV}" | cut -d= -f2-)
|
|
48
|
+
got=$(sha256sum "${file}" | awk '{print $1}')
|
|
49
|
+
if [[ "${want}" != "${got}" ]]; then
|
|
50
|
+
die "${key} mismatch: env=${want} file=${got} (${file})"
|
|
51
|
+
fi
|
|
52
|
+
log " ${key} OK"
|
|
53
|
+
}
|
|
54
|
+
check_hash OCTRA_BINARY_HASH "${NODE_SRC_DIR}/_build/default/bin/octra_node.exe"
|
|
55
|
+
check_hash OCTRA_PVAC_VERIFY_WORKER_HASH "${NODE_SRC_DIR}/_build/default/bin/octra_pvac_worker.exe"
|
|
56
|
+
check_hash OCTRA_OPERATOR_SYNC_BINARY_HASH "${NODE_SRC_DIR}/_build/default/bin/octra_state_sync_client.exe"
|
|
57
|
+
check_hash OCTRA_OPERATOR_CONTROL_BINARY_HASH "${NODE_SRC_DIR}/_build/default/bin/bft_control_tx.exe"
|
|
58
|
+
|
|
59
|
+
NET_WANT=$(grep -m1 '^OCTRA_OPERATOR_NETWORK_SHA256=' "${NODE_ENV}" | cut -d= -f2-)
|
|
60
|
+
for f in "${NODE_SRC_DIR}/.keys/validator/network.env" "${NODE_SRC_DIR}/config/network.env"; do
|
|
61
|
+
[[ "$(sha256sum "$f" | awk '{print $1}')" == "${NET_WANT}" ]] || die "network bundle hash mismatch: $f"
|
|
62
|
+
done
|
|
63
|
+
log " network bundle OK ($(grep -m1 '^OCTRA_CHAIN_ID=' "${NODE_ENV}"))"
|
|
64
|
+
|
|
65
|
+
# --- 3. ownership ---------------------------------------------------
|
|
66
|
+
log "Fixing ownership (${NODE_USER})..."
|
|
67
|
+
chown -R "${NODE_USER}:${NODE_USER}" "${NODE_SRC_DIR}" "${NODE_DATA_ROOT}"
|
|
68
|
+
chmod 700 "${NODE_SRC_DIR}/.keys/validator" "${NODE_DATA_DIR}" 2>/dev/null || true
|
|
69
|
+
chmod 600 "${NODE_SRC_DIR}/.keys/validator/"*.json "${NODE_ENV}" 2>/dev/null || true
|
|
70
|
+
|
|
71
|
+
# --- 4. runtime packages + process manager --------------------------
|
|
72
|
+
# `install.sh` runs apt-get + npm, which can take minutes. Holding a single SSH
|
|
73
|
+
# session open for that long is what killed the 2026-10-30 run ("Network error:
|
|
74
|
+
# Software caused connection abort" mid-apt), so it runs detached in a systemd
|
|
75
|
+
# unit and this script only polls a status file. A dropped SSH is then harmless:
|
|
76
|
+
# re-running this step picks up the finished install.
|
|
77
|
+
if command -v pm2 >/dev/null 2>&1 && dpkg -s libev4 libgmp10 liblmdb0 nodejs npm >/dev/null 2>&1; then
|
|
78
|
+
log "Runtime packages + pm2 already installed - skipping install.sh"
|
|
79
|
+
else
|
|
80
|
+
INSTALL_UNIT="octra-finalize-install"
|
|
81
|
+
INSTALL_LOG="${MIG_DIR}/install-target.log"
|
|
82
|
+
INSTALL_STATUS="${MIG_DIR}/install-target.status"
|
|
83
|
+
INSTALL_TIMEOUT="${INSTALL_TIMEOUT:-1800}"
|
|
84
|
+
rm -f "${INSTALL_LOG}" "${INSTALL_STATUS}"
|
|
85
|
+
systemctl reset-failed "${INSTALL_UNIT}" 2>/dev/null || true
|
|
86
|
+
|
|
87
|
+
log "Running controls/install.sh detached (runtime libs, nodejs, pm2, boot unit)..."
|
|
88
|
+
systemd-run --unit="${INSTALL_UNIT}" --collect --no-block \
|
|
89
|
+
--working-directory="${NODE_SRC_DIR}" \
|
|
90
|
+
--setenv=OCTRA_OPERATOR_USER="${NODE_USER}" \
|
|
91
|
+
--setenv=OCTRA_DATA_ROOT="${NODE_DATA_ROOT}" \
|
|
92
|
+
--setenv=DEBIAN_FRONTEND=noninteractive \
|
|
93
|
+
--setenv=TERM=dumb \
|
|
94
|
+
bash -c "sh controls/install.sh > '${INSTALL_LOG}' 2>&1; echo \$? > '${INSTALL_STATUS}'"
|
|
95
|
+
|
|
96
|
+
log "install.sh runs as systemd unit ${INSTALL_UNIT} - polling (SSH may drop safely)"
|
|
97
|
+
START_TS=$(date +%s)
|
|
98
|
+
LAST_LOG=$START_TS
|
|
99
|
+
while [[ ! -s "${INSTALL_STATUS}" ]]; do
|
|
100
|
+
ELAPSED=$(( $(date +%s) - START_TS ))
|
|
101
|
+
if (( ELAPSED > INSTALL_TIMEOUT )); then
|
|
102
|
+
die "install.sh did not finish in ${INSTALL_TIMEOUT}s (see ${INSTALL_LOG})"
|
|
103
|
+
fi
|
|
104
|
+
# One line every 30 s: a \r progress line becomes its own row in the console,
|
|
105
|
+
# so a 5-minute apt run used to print ~30 near-identical rows.
|
|
106
|
+
if (( $(date +%s) - LAST_LOG >= 30 )); then
|
|
107
|
+
log " install.sh still running... ${ELAPSED}s of ${INSTALL_TIMEOUT}s (unit ${INSTALL_UNIT})"
|
|
108
|
+
LAST_LOG=$(date +%s)
|
|
109
|
+
fi
|
|
110
|
+
sleep 10
|
|
111
|
+
done
|
|
112
|
+
log "install.sh finished in $(( $(date +%s) - START_TS ))s"
|
|
113
|
+
|
|
114
|
+
INSTALL_RC=$(tr -dc '0-9' < "${INSTALL_STATUS}")
|
|
115
|
+
tail -n 8 "${INSTALL_LOG}" 2>/dev/null | sed 's/^/ /' || true
|
|
116
|
+
[[ "${INSTALL_RC}" == "0" ]] || die "install.sh failed (exit ${INSTALL_RC}) - log: ${INSTALL_LOG}"
|
|
117
|
+
fi
|
|
118
|
+
command -v pm2 >/dev/null || die "pm2 was not installed by install.sh"
|
|
119
|
+
log "pm2: $(pm2 -v)"
|
|
120
|
+
|
|
121
|
+
# --- 5. runtime binding check --------------------------------------
|
|
122
|
+
log "Checking runtime binding (--check-runtime)..."
|
|
123
|
+
as_octra "cd ${NODE_SRC_DIR} && python3 controls/lib/validator_config.py --check-runtime --config ${NODE_ENV}" \
|
|
124
|
+
|| die "runtime binding check failed"
|
|
125
|
+
log "runtime binding OK"
|
|
126
|
+
|
|
127
|
+
ADDRESS=$(jq -r '.address' "${NODE_SRC_DIR}/.keys/validator/wallet.json")
|
|
128
|
+
log "wallet address on target: ${ADDRESS}"
|
|
129
|
+
if [[ -f "${MIG_DIR}/source-final-stat.txt" ]]; then
|
|
130
|
+
SRC_ADDR=$(grep -m1 '^address = ' "${MIG_DIR}/source-final-stat.txt" | awk -F' = ' '{print $2}' || true)
|
|
131
|
+
if [[ -n "${SRC_ADDR}" && "${SRC_ADDR}" != "${ADDRESS}" ]]; then
|
|
132
|
+
die "wallet mismatch! source=${SRC_ADDR} target=${ADDRESS}"
|
|
133
|
+
fi
|
|
134
|
+
log "wallet matches the source validator: ${SRC_ADDR:-unknown}"
|
|
135
|
+
fi
|
|
136
|
+
# recorded for 07-verify.sh (and for the runbook)
|
|
137
|
+
printf '%s\n' "${ADDRESS}" > "${MIG_DIR}/source-address.txt"
|
|
138
|
+
|
|
139
|
+
log "=== finalize done — target is ready to start ==="
|
|
140
|
+
set_status DONE "finalize-target" "${ADDRESS}"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ===================================================================
|
|
3
|
+
# 06-start-target.sh — start the migrated validator on the NEW vps
|
|
4
|
+
#
|
|
5
|
+
# Uses the node's own controls/run.sh, which performs:
|
|
6
|
+
# --check-runtime → validator_recover → validator_guard →
|
|
7
|
+
# validator_process → pm2 start
|
|
8
|
+
#
|
|
9
|
+
# Usage (on the NEW vps):
|
|
10
|
+
# bash 06-start-target.sh
|
|
11
|
+
# ===================================================================
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
source "$(dirname "$0")/lib.sh"
|
|
15
|
+
|
|
16
|
+
require_root
|
|
17
|
+
[[ -f "${NODE_ENV}" ]] || die "node.env missing — run 05-finalize-target.sh first"
|
|
18
|
+
set_status RUNNING "start-target"
|
|
19
|
+
|
|
20
|
+
# --- Double-signing guard -------------------------------------------
|
|
21
|
+
# Two live copies of one validator wallet would equivocate. If OLD_HOST is
|
|
22
|
+
# given, refuse to start unless the source node is provably stopped.
|
|
23
|
+
if [[ -n "${OLD_HOST}" ]]; then
|
|
24
|
+
require_old_host
|
|
25
|
+
if [[ -n "$(on_old_node_processes)" ]]; then
|
|
26
|
+
die "the source node on ${OLD_HOST} is STILL RUNNING — refusing to start a second copy of the wallet"
|
|
27
|
+
fi
|
|
28
|
+
log "source node on ${OLD_HOST} confirmed stopped"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
log "=== starting the node on $(hostname) ==="
|
|
32
|
+
# Idempotent: never restart a node that is already up. A restart costs a full
|
|
33
|
+
# ~38 GB store open plus RPC catch-up, so "start" must mean "ensure running".
|
|
34
|
+
# A node that is already online is normal here: controls/install.sh (finalize)
|
|
35
|
+
# starts it, and this step only has to prove it is up.
|
|
36
|
+
if [[ -n "$(node_processes)" ]]; then
|
|
37
|
+
log "node already running - not restarting: $(node_processes | awk '{print $1, $2}' | head -1)"
|
|
38
|
+
else
|
|
39
|
+
as_octra "cd ${NODE_SRC_DIR} && sh controls/run.sh" 2>&1 | tee -a "${MIG_LOG}"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
log "Waiting for the process to come up..."
|
|
43
|
+
sleep 10
|
|
44
|
+
|
|
45
|
+
log "--- pm2 ---"
|
|
46
|
+
as_octra "pm2 list" 2>/dev/null || warn "pm2 list failed"
|
|
47
|
+
|
|
48
|
+
log "--- status ---"
|
|
49
|
+
node_stat | tee "${MIG_DIR}/target-initial-stat.txt" | \
|
|
50
|
+
grep -E '^(node|role|address|process|pid|rpc|epoch|head_epoch|voting|validator_active|validator_enrollment|p2p_connected) =' || true
|
|
51
|
+
|
|
52
|
+
if grep -q '^process = online' "${MIG_DIR}/target-initial-stat.txt"; then
|
|
53
|
+
log "=== node is up ==="
|
|
54
|
+
set_status DONE "start-target"
|
|
55
|
+
else
|
|
56
|
+
die "node did not reach process = online — check pm2 logs: pm2 logs ${NODE_PM2_NAME:-octra-node-1}"
|
|
57
|
+
fi
|