docs: Ledger Live integration, contract deploy learnings, NEXT_STEPS updates
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

- ADD_CHAIN138_TO_LEDGER_LIVE: Ledger form done; public code review repo bis-innovations/LedgerLive; init/push commands
- CONTRACT_DEPLOYMENT_RUNBOOK: Chain 138 gas price 1 gwei, 36-addr check, TransactionMirror workaround
- CONTRACT_*: AddressMapper, MirrorManager deployed 2026-02-12; 36-address on-chain check
- NEXT_STEPS_FOR_YOU: Ledger done; steps completable now (no LAN); run-completable-tasks-from-anywhere
- MASTER_INDEX, OPERATOR_OPTIONAL, SMART_CONTRACTS_INVENTORY_SIMPLE: updates
- LEDGER_BLOCKCHAIN_INTEGRATION_COMPLETE: bis-innovations/LedgerLive reference

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-02-12 15:46:57 -08:00
parent cc8dcaf356
commit fbda1b4beb
5114 changed files with 498901 additions and 4567 deletions

View File

@@ -0,0 +1,28 @@
# Maintenance Scripts
**daily-weekly-checks.sh** — Daily (explorer, indexer lag, RPC) and weekly (config API, thin pool, log reminder).
**schedule-daily-weekly-cron.sh** — Install cron: daily 08:00, weekly Sun 09:00.
**check-and-fix-explorer-lag.sh** — Checks RPC vs Blockscout block; if lag > threshold (default 500), runs `fix-explorer-indexer-lag.sh` (restart Blockscout).
**schedule-explorer-lag-cron.sh** — Install cron for lag check-and-fix: every 6 hours (0, 6, 12, 18). Log: `logs/explorer-lag-fix.log`. Use `--show` to print the line, `--install` to add to crontab, `--remove` to remove.
## Optional: Alerting on failures
The daily/weekly script writes a **metric file** when run (if `MAINTENANCE_METRIC_FILE` is set or default `logs/maintenance-checks.metric`):
```
maintenance_checks_failed 0
maintenance_checks_timestamp 1739123456
```
- **Use in cron:** After the check, if `maintenance_checks_failed` > 0, send alert.
- **Example wrapper (email on failure):**
```bash
cd /path/to/proxmox && bash scripts/maintenance/daily-weekly-checks.sh daily >> logs/daily-weekly-checks.log 2>&1
FAILED=$(grep '^maintenance_checks_failed' logs/maintenance-checks.metric 2>/dev/null | awk '{print $2}')
[ -n "$FAILED" ] && [ "$FAILED" -gt 0 ] && echo "Maintenance checks failed: $FAILED" | mail -s "Explorer/maintenance alert" ops@example.com
```
- **Slack:** Use a small script that reads the metric file and posts to a webhook when `maintenance_checks_failed` > 0.
- **Prometheus/Grafana:** Scrape the metric file or run a node_exporter textfile collector on `logs/maintenance-checks.metric`.
To disable the metric file, set `MAINTENANCE_METRIC_FILE=` (empty) before running the script.

View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
# Check explorer indexer lag; if above threshold, run fix-explorer-indexer-lag.sh (restart Blockscout).
# For use from cron. Run from project root. Requires LAN/SSH to r630-02 for the fix.
# Usage: bash scripts/maintenance/check-and-fix-explorer-lag.sh
# Env: EXPLORER_INDEXER_LAG_THRESHOLD (default 500)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
IP_RPC_2201="${RPC_2201:-192.168.11.221}"
IP_BLOCKSCOUT="${IP_BLOCKSCOUT:-192.168.11.140}"
BLOCKSCOUT_API_PORT="${BLOCKSCOUT_API_PORT:-4000}"
EXPLORER_INDEXER_LAG_THRESHOLD="${EXPLORER_INDEXER_LAG_THRESHOLD:-500}"
get_rpc_block() {
local hex
hex=$(curl -sf --max-time 10 -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
"http://${IP_RPC_2201}:8545" 2>/dev/null | sed -n 's/.*"result":"\(0x[0-9a-fA-F]*\)".*/\1/p')
[ -n "$hex" ] && echo $((hex)) || true
}
get_explorer_block() {
local body block
body=$(curl -sf --max-time 10 "http://${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT}/api/v2/stats" 2>/dev/null || true)
[ -z "$body" ] && return
block=$(echo "$body" | sed -n 's/.*"total_blocks"\s*:\s*"\([0-9]*\)".*/\1/p' | head -1)
[ -z "$block" ] && block=$(echo "$body" | sed -n 's/.*"total_blocks"\s*:\s*\([0-9]*\).*/\1/p' | head -1)
[ -n "$block" ] && echo "$block"
}
rpc_block=$(get_rpc_block)
explorer_block=$(get_explorer_block)
if [ -z "$rpc_block" ] || [ -z "$explorer_block" ]; then
echo "$(date -Iseconds) SKIP (RPC or Blockscout unreachable)"
exit 0
fi
lag=$((rpc_block - explorer_block))
if [ "$lag" -le "${EXPLORER_INDEXER_LAG_THRESHOLD}" ] 2>/dev/null; then
echo "$(date -Iseconds) OK lag=$lag (threshold=${EXPLORER_INDEXER_LAG_THRESHOLD})"
exit 0
fi
echo "$(date -Iseconds) LAG $lag > ${EXPLORER_INDEXER_LAG_THRESHOLD} — running fix"
bash "$PROJECT_ROOT/scripts/fix-explorer-indexer-lag.sh" 2>&1 || true

View File

@@ -0,0 +1,191 @@
#!/usr/bin/env bash
# Maintenance checks (ALL_IMPROVEMENTS 135139). Run daily (135136) or weekly (137138).
# Explorer: hardened to FAIL when API unreachable; indexer lag check (fail if >500 blocks behind).
# Usage: ./scripts/maintenance/daily-weekly-checks.sh [daily|weekly|all]
# Cron: 0 8 * * * /path/to/daily-weekly-checks.sh daily
# Set EXPLORER_FAIL_WHEN_UNREACHABLE=0 to keep legacy SKIP when explorer unreachable (e.g. off-LAN).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
MODE="${1:-daily}"
# Defaults (override via config or env)
IP_RPC_2201="${RPC_2201:-192.168.11.221}"
IP_BLOCKSCOUT="${IP_BLOCKSCOUT:-192.168.11.140}"
BLOCKSCOUT_API_PORT="${BLOCKSCOUT_API_PORT:-4000}"
DBIS_API_URL="${DBIS_API_URL:-https://dbis-api.d-bis.org}"
PROXMOX_R630_02="${PROXMOX_HOST_R630_02:-192.168.11.12}"
# Fail daily run when explorer API unreachable (set 0 to preserve legacy SKIP when off-LAN)
EXPLORER_FAIL_WHEN_UNREACHABLE="${EXPLORER_FAIL_WHEN_UNREACHABLE:-1}"
# Indexer lag: fail if explorer block is more than this many blocks behind RPC head
# Set 1500 temporarily if indexer is catching up after restart (~50 min at 2s/block).
EXPLORER_INDEXER_LAG_THRESHOLD="${EXPLORER_INDEXER_LAG_THRESHOLD:-500}"
# Optional: write metric file for alerting (FAILED count and timestamp)
MAINTENANCE_METRIC_FILE="${MAINTENANCE_METRIC_FILE:-$PROJECT_ROOT/logs/maintenance-checks.metric}"
FAILED=0
check_rpc() {
echo -n "[136] RPC (${IP_RPC_2201}:8545)... "
if curl -sf --max-time 10 -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
"http://${IP_RPC_2201}:8545" | grep -q '"result"'; then
echo "OK"
else
echo "FAIL"
((FAILED++)) || true
fi
}
# Get RPC chain head block number (decimal). Empty on failure.
get_rpc_block_number() {
local hex
hex=$(curl -sf --max-time 10 -X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
"http://${IP_RPC_2201}:8545" 2>/dev/null | sed -n 's/.*"result":"\(0x[0-9a-fA-F]*\)".*/\1/p')
[ -n "$hex" ] && echo $((hex)) || true
}
# Get Blockscout last indexed block (from /api/v2/stats total_blocks or /api/v2/blocks). Empty on failure.
get_explorer_block_number() {
local body block
body=$(curl -sf --max-time 10 "http://${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT}/api/v2/stats" 2>/dev/null || true)
if [ -n "$body" ] && echo "$body" | grep -qE '"total_blocks"|"total_transactions"'; then
# total_blocks in API v2 can be string or number
block=$(echo "$body" | sed -n 's/.*"total_blocks"\s*:\s*"\([0-9]*\)".*/\1/p' | head -1)
[ -z "$block" ] && block=$(echo "$body" | sed -n 's/.*"total_blocks"\s*:\s*\([0-9]*\).*/\1/p' | head -1)
[ -n "$block" ] && echo "$block" && return
fi
# Fallback: first block from /api/v2/blocks
body=$(curl -sf --max-time 10 "http://${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT}/api/v2/blocks?page_size=1" 2>/dev/null || true)
if [ -n "$body" ]; then
echo "$body" | sed -n 's/.*"height"\s*:\s*\([0-9]*\).*/\1/p' | head -1
fi
}
# [135] Explorer: API must return 200 with total_blocks/total_transactions. FAIL when unreachable if EXPLORER_FAIL_WHEN_UNREACHABLE=1.
check_explorer_sync() {
echo -n "[135] Explorer indexer (${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT})... "
local api_ok=0
if curl -sf --max-time 10 "http://${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT}/api/v2/stats" 2>/dev/null | grep -qE '"total_blocks"|"total_transactions"|"indexer"'; then
api_ok=1
elif curl -sf --max-time 10 "http://${IP_BLOCKSCOUT}:${BLOCKSCOUT_API_PORT}/api?module=stats&action=eth_price" 2>/dev/null | grep -qE '"result"|"eth_price"'; then
api_ok=1
fi
if [ "$api_ok" -eq 1 ]; then
echo "OK"
return
fi
# Try public URL (in case we're off-LAN and only NPMplus path works)
if curl -sf --max-time 10 -k "https://explorer.d-bis.org/api/v2/stats" 2>/dev/null | grep -qE '"total_blocks"|"total_transactions"'; then
echo "OK (public)"
return
fi
if [ "${EXPLORER_FAIL_WHEN_UNREACHABLE}" = "1" ]; then
echo "FAIL (Blockscout unreachable)"
((FAILED++)) || true
else
echo "SKIP (Blockscout unreachable; run from LAN or set EXPLORER_FAIL_WHEN_UNREACHABLE=1)"
fi
}
# [135b] Indexer lag: fail if explorer block is more than EXPLORER_INDEXER_LAG_THRESHOLD behind RPC head.
check_explorer_indexer_lag() {
echo -n "[135b] Explorer indexer lag (RPC vs Blockscout)... "
local rpc_block explorer_block lag
rpc_block=$(get_rpc_block_number)
explorer_block=$(get_explorer_block_number)
if [ -z "$rpc_block" ] || [ -z "$explorer_block" ]; then
echo "SKIP (RPC or Blockscout unreachable)"
return
fi
if [ "$rpc_block" -gt "$explorer_block" ] 2>/dev/null; then
lag=$((rpc_block - explorer_block))
if [ "$lag" -gt "${EXPLORER_INDEXER_LAG_THRESHOLD}" ]; then
echo "FAIL (lag ${lag} > ${EXPLORER_INDEXER_LAG_THRESHOLD})"
((FAILED++)) || true
else
echo "OK (lag ${lag})"
fi
else
echo "OK (explorer caught up)"
fi
}
check_config_api() {
echo -n "[137] Config API (${DBIS_API_URL})... "
if curl -sf --max-time 10 -o /dev/null -w "%{http_code}" "${DBIS_API_URL}/health" 2>/dev/null | grep -q 200; then
echo "OK"
else
echo "SKIP or FAIL (external URL; may be unreachable off-LAN)"
fi
}
# [138a] Weekly: thin pool usage on r630-02 (VMID 5000 host). Warn >85%, fail at 100%.
check_thin_pool_r630_02() {
echo -n "[138a] Thin pool r630-02 (thin1/thin5)... "
local out usage pct
out=$(ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@"${PROXMOX_R630_02}" "pvesm status 2>/dev/null | grep -E 'thin1|thin5' || true" 2>/dev/null || true)
if [ -z "$out" ]; then
echo "SKIP (SSH to ${PROXMOX_R630_02} failed or no thin pool)"
return
fi
# pvesm status: name, type, status, ... sometimes usage % in output; try to get numeric usage
if echo "$out" | grep -q '100%'; then
echo "FAIL (thin pool 100% full)"
((FAILED++)) || true
return
fi
# Check for high usage (e.g. 85% or more) - common format has percent in line
pct=$(echo "$out" | sed -n 's/.*\([0-9]\{2,3\}\)%.*/\1/p' | head -1)
if [ -n "$pct" ] && [ "$pct" -ge 85 ] 2>/dev/null; then
echo "WARN (usage ${pct}% >= 85%)"
# Optional: increment FAILED only at 100%; for now just warn at 85%
else
echo "OK"
fi
}
# Write metric file for alerting (FAILED count, timestamp). Optional.
write_metric_file() {
[ -z "${MAINTENANCE_METRIC_FILE}" ] && return
mkdir -p "$(dirname "$MAINTENANCE_METRIC_FILE")"
echo "maintenance_checks_failed ${FAILED}" > "${MAINTENANCE_METRIC_FILE}.$$"
echo "maintenance_checks_timestamp $(date +%s)" >> "${MAINTENANCE_METRIC_FILE}.$$"
mv "${MAINTENANCE_METRIC_FILE}.$$" "${MAINTENANCE_METRIC_FILE}"
}
echo "=== Maintenance checks ($MODE) $(date -Iseconds) ==="
case "$MODE" in
daily)
check_explorer_sync
check_explorer_indexer_lag
check_rpc
;;
weekly)
check_config_api
check_thin_pool_r630_02
echo "[138] Review explorer logs: pct exec 5000 -- journalctl -u blockscout -n 200 --no-pager (from root@${PROXMOX_R630_02})"
;;
all)
check_explorer_sync
check_explorer_indexer_lag
check_rpc
check_config_api
check_thin_pool_r630_02
echo "[138] Review explorer logs manually; [139] update token list as needed (token-list.json / explorer config)."
;;
*)
echo "Usage: $0 [daily|weekly|all]"
exit 1
;;
esac
write_metric_file
echo "=== Done (failed: $FAILED) ==="
[[ $FAILED -eq 0 ]]

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Schedule daily/weekly maintenance checks (O-1, O-2, O-3). Run from project root.
# Usage: bash scripts/maintenance/schedule-daily-weekly-cron.sh [--install|--show]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
CHECKS_SCRIPT="$PROJECT_ROOT/scripts/maintenance/daily-weekly-checks.sh"
LOG_DIR="$PROJECT_ROOT/logs"
CRON_DAILY="0 8 * * * cd $PROJECT_ROOT && bash $CHECKS_SCRIPT daily >> $LOG_DIR/daily-weekly-checks.log 2>&1"
CRON_WEEKLY="0 9 * * 0 cd $PROJECT_ROOT && bash $CHECKS_SCRIPT weekly >> $LOG_DIR/daily-weekly-checks.log 2>&1"
case "${1:-}" in
--install)
mkdir -p "$LOG_DIR"
(crontab -l 2>/dev/null; echo "$CRON_DAILY"; echo "$CRON_WEEKLY") | crontab -
echo "Installed daily (08:00) and weekly (Sun 09:00):"
echo " $CRON_DAILY"
echo " $CRON_WEEKLY"
;;
--show)
echo "Daily (O-1, O-2): $CRON_DAILY"
echo "Weekly (O-3): $CRON_WEEKLY"
;;
*)
echo "Usage: $0 [--install|--show]"
exit 0
;;
esac

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Schedule cron to check explorer indexer lag and run fix when lag > threshold.
# Run from project root. Use a host that can reach RPC and Blockscout (and SSH to r630-02 for fix).
# Usage: bash scripts/maintenance/schedule-explorer-lag-cron.sh [--install|--show|--remove]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
LAG_SCRIPT="$PROJECT_ROOT/scripts/maintenance/check-and-fix-explorer-lag.sh"
LOG_DIR="$PROJECT_ROOT/logs"
LOG_FILE="$LOG_DIR/explorer-lag-fix.log"
# Every 6 hours (0:00, 6:00, 12:00, 18:00)
CRON_LAG="0 */6 * * * cd $PROJECT_ROOT && bash $LAG_SCRIPT >> $LOG_FILE 2>&1"
case "${1:-}" in
--install)
mkdir -p "$LOG_DIR"
if crontab -l 2>/dev/null | grep -q "check-and-fix-explorer-lag.sh"; then
echo "Explorer lag cron already present in crontab."
else
(crontab -l 2>/dev/null; echo "$CRON_LAG") | crontab -
echo "Installed explorer lag cron (every 6 hours):"
echo " $CRON_LAG"
echo "Log: $LOG_FILE"
fi
;;
--show)
echo "Explorer lag check-and-fix (every 6 hours):"
echo " $CRON_LAG"
echo "Log: $LOG_FILE"
echo "Threshold: set EXPLORER_INDEXER_LAG_THRESHOLD (default 500) in crontab or env."
;;
--remove)
if crontab -l 2>/dev/null | grep -q "check-and-fix-explorer-lag.sh"; then
crontab -l 2>/dev/null | grep -v "check-and-fix-explorer-lag.sh" | crontab -
echo "Removed explorer lag cron."
else
echo "No explorer lag cron found in crontab."
fi
;;
*)
echo "Usage: $0 [--install|--show|--remove]"
exit 0
;;
esac

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Schedule NPMplus backup via cron (W1-8). Run from project root.
# Usage: bash scripts/maintenance/schedule-npmplus-backup-cron.sh [--install|--show]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
BACKUP_SCRIPT="$PROJECT_ROOT/scripts/verify/backup-npmplus.sh"
CRON_LINE="0 3 * * * cd $PROJECT_ROOT && bash $BACKUP_SCRIPT >> $PROJECT_ROOT/logs/npmplus-backup.log 2>&1"
case "${1:-}" in
--install)
mkdir -p "$PROJECT_ROOT/logs"
(crontab -l 2>/dev/null; echo "$CRON_LINE") | crontab -
echo "Installed: $CRON_LINE"
;;
--show)
echo "Crontab line: $CRON_LINE"
;;
*)
echo "Usage: $0 [--install|--show]"
exit 0
;;
esac

View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Start container 6201 (firefly-ali-1) on r630-02. Optional; run when Firefly Ali node is needed.
# Usage: bash scripts/maintenance/start-firefly-6201.sh [--dry-run] [--host HOST]
# Requires: SSH key access to the Proxmox host (r630-02).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true
[ -f "${PROJECT_ROOT}/.env" ] && set +u && source "${PROJECT_ROOT}/.env" 2>/dev/null; set -u
VMID=6201
PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--host) PROXMOX_HOST="${2:-192.168.11.12}"; shift 2 ;;
*) shift ;;
esac
done
if [[ "$DRY_RUN" == true ]]; then
echo "[DRY-RUN] Would run: ssh root@${PROXMOX_HOST} 'pct start ${VMID}'"
echo " (Container 6201 = firefly-ali-1 on r630-02. See OPERATIONAL_RUNBOOKS Maintenance.)"
exit 0
fi
echo "Starting CT ${VMID} (firefly-ali-1) on ${PROXMOX_HOST}..."
if ssh -o ConnectTimeout=10 -o BatchMode=yes "root@${PROXMOX_HOST}" "pct start ${VMID}"; then
echo "Started ${VMID}."
else
echo "Failed to start ${VMID} (may already be running or host unreachable). Check: ssh root@${PROXMOX_HOST} 'pct status ${VMID}'"
exit 1
fi