|
|
|
|
@ -55,6 +55,18 @@ function _sc_list --description "list storageclasses as menu"
|
|
|
|
|
' 2>/dev/null
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# convert XMR -> piconero (atomic units) with Python (exact
|
|
|
|
|
function _xmr_to_atomic -a AMT
|
|
|
|
|
_need python3; or return 1
|
|
|
|
|
printf '%s' 'from decimal import Decimal, getcontext
|
|
|
|
|
import sys
|
|
|
|
|
getcontext().prec = 50
|
|
|
|
|
amt = Decimal(sys.argv[1])
|
|
|
|
|
atomic = int((amt * Decimal("1000000000000")).to_integral_value(rounding="ROUND_HALF_UP"))
|
|
|
|
|
print(atomic)
|
|
|
|
|
' | python3 - "$AMT"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Choose SC (menu -> stderr; selection -> stdout)
|
|
|
|
|
function _choose_sc --description "interactive StorageClass picker"
|
|
|
|
|
set lines (_sc_list)
|
|
|
|
|
@ -141,8 +153,6 @@ function _rpc_call --description "call wallet JSON-RPC via localhost pf" -a RPCU
|
|
|
|
|
else
|
|
|
|
|
set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s","params":%s}' $METHOD "$PARAMS")
|
|
|
|
|
end
|
|
|
|
|
# Use --digest so we satisfy the server's challenge
|
|
|
|
|
# Note: callers should check for an empty response (curl --fail returns nothing).
|
|
|
|
|
curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H "Content-Type: application/json" \
|
|
|
|
|
-d "$payload" "$url"
|
|
|
|
|
@ -156,65 +166,97 @@ function _probe_monerod -a ADDR
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Loud early block on suspicious names
|
|
|
|
|
if string match -qi '*qubic*' -- $ADDR
|
|
|
|
|
echo (set_color red)"Refusing daemon '$ADDR' (contains 'qubic'). This is NOT a Monero node."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Try /get_info (HTTP) first (most public nodes support this)
|
|
|
|
|
set raw (curl -s --max-time 7 "http://$ADDR/get_info" 2>/dev/null)
|
|
|
|
|
set ok 0
|
|
|
|
|
if test -n "$raw"
|
|
|
|
|
set status (echo $raw | jq -r '.status // "OK"')
|
|
|
|
|
set testnet (echo $raw | jq -r '.testnet // false')
|
|
|
|
|
set stagenet (echo $raw | jq -r '.stagenet // false')
|
|
|
|
|
set height (echo $raw | jq -r '.height // .target_height // 0')
|
|
|
|
|
if test "$status" = "OK"
|
|
|
|
|
set ok 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Fallback to JSON-RPC get_info (some nodes only expose /json_rpc)
|
|
|
|
|
if test $ok -eq 0
|
|
|
|
|
set raw (curl -s --max-time 7 -H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' "http://$ADDR/json_rpc" \
|
|
|
|
|
| jq -r '.result? | @json' 2>/dev/null)
|
|
|
|
|
# Try up to 3 short attempts
|
|
|
|
|
for t in (seq 1 3)
|
|
|
|
|
set ok 0
|
|
|
|
|
set raw (curl -s --max-time 7 "http://$ADDR/get_info" 2>/dev/null)
|
|
|
|
|
if test -n "$raw"
|
|
|
|
|
set status (echo $raw | jq -r '.status // "OK"')
|
|
|
|
|
set testnet (echo $raw | jq -r '.testnet // false')
|
|
|
|
|
set mstatus (echo $raw | jq -r '.status // "OK"')
|
|
|
|
|
set testnet (echo $raw | jq -r '.testnet // false')
|
|
|
|
|
set stagenet (echo $raw | jq -r '.stagenet // false')
|
|
|
|
|
set height (echo $raw | jq -r '.height // .target_height // 0')
|
|
|
|
|
if test "$status" = "OK"
|
|
|
|
|
set height (echo $raw | jq -r '.height // .target_height // 0')
|
|
|
|
|
if test "$mstatus" = "OK"
|
|
|
|
|
set ok 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test $ok -eq 0
|
|
|
|
|
set raw (curl -s --max-time 7 -H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_info"}' "http://$ADDR/json_rpc" \
|
|
|
|
|
| jq -r '.result? | @json' 2>/dev/null)
|
|
|
|
|
if test -n "$raw"
|
|
|
|
|
set mstatus (echo $raw | jq -r '.status // "OK"')
|
|
|
|
|
set testnet (echo $raw | jq -r '.testnet // false')
|
|
|
|
|
set stagenet (echo $raw | jq -r '.stagenet // false')
|
|
|
|
|
set height (echo $raw | jq -r '.height // .target_height // 0')
|
|
|
|
|
if test "$mstatus" = "OK"
|
|
|
|
|
set ok 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test $ok -eq 1
|
|
|
|
|
if test "$testnet" = "true" -o "$stagenet" = "true"
|
|
|
|
|
echo (set_color red)"Refusing non-mainnet daemon (testnet=$testnet stagenet=$stagenet) at $ADDR."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
echo "Daemon OK: Monero mainnet, height=$height"
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
sleep 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test $ok -eq 0
|
|
|
|
|
echo (set_color red)"Could not speak Monero /get_info to $ADDR. Not a monerod (or blocked). Aborting."(set_color normal) >&2
|
|
|
|
|
echo "Tip: run your own monerod and use its ClusterIP:Port, or set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass." >&2
|
|
|
|
|
echo (set_color red)"Could not speak Monero /get_info to $ADDR. Not a monerod (or blocked)."(set_color normal) >&2
|
|
|
|
|
echo "Tip: run your own monerod or set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass." >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Wait for wallet RPC to accept connections (digest auth get_version)
|
|
|
|
|
function walletsvc_wait_ready -a NS SVC SECS
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$SECS"; set SECS 60; end
|
|
|
|
|
|
|
|
|
|
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d 2>/dev/null)
|
|
|
|
|
set RPCPASS (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d 2>/dev/null)
|
|
|
|
|
if test -z "$RPCUSER" -o -z "$RPCPASS"
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" missing RPC creds in secret {$SVC}-rpc-auth" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test "$testnet" = "true" -o "$stagenet" = "true"
|
|
|
|
|
echo (set_color red)"Refusing non-mainnet daemon (testnet=$testnet stagenet=$stagenet) at $ADDR."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
_pf_start $NS $SVC 18083 18083
|
|
|
|
|
set tries (math "ceil($SECS/2)")
|
|
|
|
|
for i in (seq 1 $tries)
|
|
|
|
|
set code (curl -s -o /dev/null -w "%{http_code}" --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
|
|
|
|
|
http://127.0.0.1:18083/json_rpc)
|
|
|
|
|
if test "$code" = "200"
|
|
|
|
|
_pf_stop
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
sleep 2
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo "Daemon OK: Monero mainnet, height=$height"
|
|
|
|
|
_pf_stop
|
|
|
|
|
echo (set_color red)"RPC not ready after $SECS seconds."(set_color normal) >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
### ------- main workflow ---------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function walletsvc_bootstrap --description "Interactive setup of monero-wallet-rpc (multi-wallet) with PVC + secrets"
|
|
|
|
|
function walletsvc_bootstrap --description "Interactive setup of monero-wallet-rpc with PVC+secrets; creates default wallet and prints recovery info"
|
|
|
|
|
_need kubectl jq curl awk; or return 1
|
|
|
|
|
|
|
|
|
|
_banner "Monero wallet RPC bootstrap"
|
|
|
|
|
|
|
|
|
|
# --- Namespace
|
|
|
|
|
read -P "Namespace [monero]: " ns_raw
|
|
|
|
|
if test -z "$ns_raw"; set ns_raw monero; end
|
|
|
|
|
read -P "Namespace [crypto]: " ns_raw
|
|
|
|
|
if test -z "$ns_raw"; set ns_raw crypto; end
|
|
|
|
|
set ns (_k8s_name $ns_raw)
|
|
|
|
|
_require "Namespace" $ns; or return 1
|
|
|
|
|
|
|
|
|
|
@ -231,28 +273,64 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
echo "TIP: Prefer a class with reclaimPolicy=Retain so a PVC delete won't delete the PV."
|
|
|
|
|
set sc (_choose_sc); or return 1
|
|
|
|
|
|
|
|
|
|
# --- Names + sizes
|
|
|
|
|
read -P "PVC name (for wallet data) [wallet-data]: " pvc_name_raw
|
|
|
|
|
if test -z "$pvc_name_raw"; set pvc_name_raw wallet-data; end
|
|
|
|
|
set pvc_name (_k8s_name $pvc_name_raw)
|
|
|
|
|
_require "PVC name" $pvc_name; or return 1
|
|
|
|
|
|
|
|
|
|
read -P "PVC size [5Gi]: " pvc_size
|
|
|
|
|
if test -z "$pvc_size"; set pvc_size 5Gi; end
|
|
|
|
|
_require "PVC size" $pvc_size; or return 1
|
|
|
|
|
|
|
|
|
|
read -P "App label / Deployment name [wallet-rpc]: " app_raw
|
|
|
|
|
if test -z "$app_raw"; set app_raw wallet-rpc; end
|
|
|
|
|
set app_name (_k8s_name $app_raw)
|
|
|
|
|
_require "App/Deployment name" $app_name; or return 1
|
|
|
|
|
|
|
|
|
|
read -P "Service name (blank = same as app): " svc_raw
|
|
|
|
|
if test -z "$svc_raw"
|
|
|
|
|
set svc_name $app_name
|
|
|
|
|
else
|
|
|
|
|
set svc_name (_k8s_name $svc_raw)
|
|
|
|
|
# --- Unified name (prefix wallet-monero-)
|
|
|
|
|
read -P "Wallet name (no spaces, e.g. 'brad', base will be wallet-monero-brad): " wallet_raw
|
|
|
|
|
_require "Wallet name" $wallet_raw; or return 1
|
|
|
|
|
if string match -rq '\s' -- $wallet_raw
|
|
|
|
|
echo (set_color red)"Error:"(set_color normal)" Wallet name cannot contain spaces." >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
set base (_k8s_name $wallet_raw)
|
|
|
|
|
set base (string replace -r '^wallet-monero-' '' -- $base)
|
|
|
|
|
set name "wallet-monero-$base"
|
|
|
|
|
|
|
|
|
|
# Bind all k8s object names to the unified base
|
|
|
|
|
set pvc_name $name
|
|
|
|
|
set app_name $name
|
|
|
|
|
set svc_name $name
|
|
|
|
|
set rpc_secret_name "$app_name-rpc-auth"
|
|
|
|
|
set wpass_secret_name "$app_name-wallet-pass"
|
|
|
|
|
|
|
|
|
|
# --- Detect existing resources (all of them)
|
|
|
|
|
set exists_any 0
|
|
|
|
|
set found_list
|
|
|
|
|
for r in "deploy/$app_name" "svc/$svc_name" "netpol/{$app_name}-ingress"
|
|
|
|
|
if kubectl -n $ns get $r >/dev/null 2>&1
|
|
|
|
|
set -a found_list $r
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
|
|
|
|
|
set -a found_list "pvc/$pvc_name"
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
if kubectl -n $ns get secret $rpc_secret_name >/dev/null 2>&1
|
|
|
|
|
set -a found_list "secret/$rpc_secret_name"
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
if kubectl -n $ns get secret $wpass_secret_name >/dev/null 2>&1
|
|
|
|
|
set -a found_list "secret/$wpass_secret_name"
|
|
|
|
|
set exists_any 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if test $exists_any -eq 1
|
|
|
|
|
_banner "Existing resources detected for $name"
|
|
|
|
|
for r in $found_list
|
|
|
|
|
echo " - $ns/$r"
|
|
|
|
|
end
|
|
|
|
|
read -P "Remove EVERYTHING (deploy/svc/netpol/PVC/SECRETS) and recreate? (y/N): " wipe
|
|
|
|
|
if string match -qi 'y*' -- $wipe
|
|
|
|
|
kubectl -n $ns delete deploy $app_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete svc $svc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete netpol {$app_name}-ingress --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete pvc $pvc_name --ignore-not-found --wait=true >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete secret $rpc_secret_name --ignore-not-found >/dev/null 2>&1; or true
|
|
|
|
|
kubectl -n $ns delete secret $wpass_secret_name --ignore-not-found >/dev/null 2>&1; or true
|
|
|
|
|
echo "Cleaned up all previous resources."
|
|
|
|
|
else
|
|
|
|
|
echo "Reusing existing resources; they will be applied in-place."
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
_require "Service name" $svc_name; or return 1
|
|
|
|
|
|
|
|
|
|
# --- Node scheduling (worker-only by default)
|
|
|
|
|
read -P "Schedule only on worker nodes? (y/N) [y]: " pin_workers
|
|
|
|
|
@ -266,17 +344,25 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
if test -z "$node_label_val"; set node_label_val true; end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- Daemon + image
|
|
|
|
|
# --- Daemon (auto-use local monerod)
|
|
|
|
|
echo
|
|
|
|
|
echo "Monero node (daemon) the wallet will talk to."
|
|
|
|
|
echo " - Best: your own monerod (trusted). Example (same ns): monerod:18081"
|
|
|
|
|
echo " - Remote nodes work, but reduce privacy and add trust."
|
|
|
|
|
read -P "Daemon address (host:port) [REQUIRED]: " daemon_addr
|
|
|
|
|
_require "Daemon address" $daemon_addr; or return 1
|
|
|
|
|
echo "Monero daemon: using your cluster-local monerod by default."
|
|
|
|
|
if test "$ns" = "crypto"
|
|
|
|
|
set daemon_addr monerod:18081
|
|
|
|
|
else
|
|
|
|
|
set daemon_addr monerod.crypto.svc.cluster.local:18081
|
|
|
|
|
end
|
|
|
|
|
echo "Daemon address (for the Deployment): $daemon_addr"
|
|
|
|
|
|
|
|
|
|
if test -z "$WALLETSVC_SKIP_DAEMON_CHECK"
|
|
|
|
|
_banner "Probing daemon"
|
|
|
|
|
_probe_monerod $daemon_addr; or return 1
|
|
|
|
|
_banner "Probing daemon via temporary port-forward"
|
|
|
|
|
set PF_LOCAL 28081
|
|
|
|
|
_pf_start crypto monerod $PF_LOCAL 18081
|
|
|
|
|
if not _probe_monerod 127.0.0.1:$PF_LOCAL
|
|
|
|
|
_pf_stop
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
_pf_stop
|
|
|
|
|
else
|
|
|
|
|
echo "Skipping daemon probe due to WALLETSVC_SKIP_DAEMON_CHECK=1"
|
|
|
|
|
end
|
|
|
|
|
@ -286,28 +372,49 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
if test -z "$image"; set image docker.io/sethsimmons/simple-monero-wallet-rpc:latest; end
|
|
|
|
|
_require "Container image" $image; or return 1
|
|
|
|
|
|
|
|
|
|
# --- RPC + wallet credentials
|
|
|
|
|
read -P "RPC username [walletrpc]: " rpc_user
|
|
|
|
|
if test -z "$rpc_user"; set rpc_user walletrpc; end
|
|
|
|
|
# --- Secrets (defaults: RPC user=wallet name, passwords auto if missing)
|
|
|
|
|
set existing_rpc_user (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.username}' 2>/dev/null | base64 -d 2>/dev/null)
|
|
|
|
|
set existing_rpc_pass (kubectl -n $ns get secret $rpc_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)
|
|
|
|
|
set existing_wpass (kubectl -n $ns get secret $wpass_secret_name -o jsonpath='{.data.password}' 2>/dev/null | base64 -d 2>/dev/null)
|
|
|
|
|
|
|
|
|
|
set rpc_user_default $base
|
|
|
|
|
if test -n "$existing_rpc_user"
|
|
|
|
|
set rpc_user_default $existing_rpc_user
|
|
|
|
|
end
|
|
|
|
|
read -P "RPC username [$rpc_user_default]: " rpc_user
|
|
|
|
|
if test -z "$rpc_user"; set rpc_user $rpc_user_default; end
|
|
|
|
|
_require "RPC username" $rpc_user; or return 1
|
|
|
|
|
|
|
|
|
|
read -s -P "RPC password [auto-generate 32 alnum if blank]: " rpc_pass
|
|
|
|
|
read -s -P "RPC password [leave blank to keep current or auto-generate if none]: " rpc_pass
|
|
|
|
|
echo
|
|
|
|
|
if test -z "$rpc_pass"; set rpc_pass (_rand_alnum32); end
|
|
|
|
|
if test -z "$rpc_pass"
|
|
|
|
|
if test -n "$existing_rpc_pass"
|
|
|
|
|
set rpc_pass $existing_rpc_pass
|
|
|
|
|
else
|
|
|
|
|
set rpc_pass (_rand_alnum32)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
read -P "Wallet filename (no path) [main]: " wallet_file_raw
|
|
|
|
|
if test -z "$wallet_file_raw"; set wallet_file_raw main; end
|
|
|
|
|
set wallet_file (_k8s_name $wallet_file_raw)
|
|
|
|
|
_require "Wallet filename" $wallet_file; or return 1
|
|
|
|
|
|
|
|
|
|
read -s -P "Wallet password [auto-generate 32 alnum if blank]: " wallet_pass
|
|
|
|
|
read -s -P "Wallet password [leave blank to keep current or auto-generate if none]: " wallet_pass
|
|
|
|
|
echo
|
|
|
|
|
if test -z "$wallet_pass"; set wallet_pass (_rand_alnum32); end
|
|
|
|
|
if test -z "$wallet_pass"
|
|
|
|
|
if test -n "$existing_wpass"
|
|
|
|
|
set wallet_pass $existing_wpass
|
|
|
|
|
else
|
|
|
|
|
set wallet_pass (_rand_alnum32)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Summary"
|
|
|
|
|
echo " Namespace: $ns"
|
|
|
|
|
echo " StorageClass: $sc"
|
|
|
|
|
echo " PVC: $pvc_name ($pvc_size)"
|
|
|
|
|
echo " Base name: $name"
|
|
|
|
|
echo " PVC: $pvc_name"
|
|
|
|
|
echo " App/Deployment: $app_name"
|
|
|
|
|
echo " Service: $svc_name"
|
|
|
|
|
echo " Daemon address: $daemon_addr"
|
|
|
|
|
@ -323,9 +430,6 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set rpc_secret_name "$app_name-rpc-auth"
|
|
|
|
|
set wpass_secret_name "$app_name-wallet-pass"
|
|
|
|
|
|
|
|
|
|
_banner "Applying secrets"
|
|
|
|
|
kubectl -n $ns create secret generic $rpc_secret_name \
|
|
|
|
|
--from-literal=username="$rpc_user" \
|
|
|
|
|
@ -336,29 +440,35 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
--from-literal=password="$wallet_pass" \
|
|
|
|
|
--dry-run=client -o yaml | kubectl -n $ns apply -f -; or return 1
|
|
|
|
|
|
|
|
|
|
# Annotate with timestamp so pod restarts when only secrets change
|
|
|
|
|
set ts (date -Is)
|
|
|
|
|
kubectl -n $ns annotate secret $rpc_secret_name walletsvc.titan/update-ts="$ts" --overwrite >/dev/null 2>&1
|
|
|
|
|
kubectl -n $ns annotate secret $wpass_secret_name walletsvc.titan/update-ts="$ts" --overwrite >/dev/null 2>&1
|
|
|
|
|
|
|
|
|
|
echo "RPC creds: user='$rpc_user' pass='$rpc_pass'"
|
|
|
|
|
echo "Wallet pass: $wallet_pass"
|
|
|
|
|
|
|
|
|
|
_banner "Applying PVC"
|
|
|
|
|
set pvcfile (mktemp -t {$pvc_name}-pvc.XXXX.yaml)
|
|
|
|
|
begin
|
|
|
|
|
echo "apiVersion: v1"
|
|
|
|
|
echo "kind: PersistentVolumeClaim"
|
|
|
|
|
echo "metadata:"
|
|
|
|
|
echo " name: $pvc_name"
|
|
|
|
|
echo " namespace: $ns"
|
|
|
|
|
echo "spec:"
|
|
|
|
|
echo " accessModes: [\"ReadWriteOnce\"]"
|
|
|
|
|
echo " storageClassName: $sc"
|
|
|
|
|
echo " resources:"
|
|
|
|
|
echo " requests:"
|
|
|
|
|
echo " storage: $pvc_size"
|
|
|
|
|
end > $pvcfile
|
|
|
|
|
echo "--- PVC manifest ($pvcfile) ---"
|
|
|
|
|
cat $pvcfile
|
|
|
|
|
kubectl apply -f $pvcfile; or return 1
|
|
|
|
|
if kubectl -n $ns get pvc $pvc_name >/dev/null 2>&1
|
|
|
|
|
echo "PVC $ns/$pvc_name exists; keeping as-is."
|
|
|
|
|
else
|
|
|
|
|
set pvcfile (mktemp -t {$pvc_name}-pvc.XXXX.yaml)
|
|
|
|
|
begin
|
|
|
|
|
echo "apiVersion: v1"
|
|
|
|
|
echo "kind: PersistentVolumeClaim"
|
|
|
|
|
echo "metadata:"
|
|
|
|
|
echo " name: $pvc_name"
|
|
|
|
|
echo " namespace: $ns"
|
|
|
|
|
echo "spec:"
|
|
|
|
|
echo " accessModes: [\"ReadWriteOnce\"]"
|
|
|
|
|
echo " storageClassName: $sc"
|
|
|
|
|
echo " resources:"
|
|
|
|
|
echo " requests:"
|
|
|
|
|
echo " storage: 5Gi"
|
|
|
|
|
end > $pvcfile
|
|
|
|
|
echo "--- PVC manifest ($pvcfile) ---"
|
|
|
|
|
cat $pvcfile
|
|
|
|
|
kubectl apply -f $pvcfile; or return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Applying Deployment"
|
|
|
|
|
set dply (mktemp -t {$app_name}-deploy.XXXX.yaml)
|
|
|
|
|
@ -483,7 +593,6 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
cat $np
|
|
|
|
|
kubectl apply -f $np; or return 1
|
|
|
|
|
|
|
|
|
|
# If Deployment existed and only secrets changed, force a restart so new creds are read
|
|
|
|
|
_banner "Ensuring pod picks up latest secrets"
|
|
|
|
|
kubectl -n $ns rollout restart deploy/$app_name >/dev/null 2>&1; or true
|
|
|
|
|
|
|
|
|
|
@ -491,61 +600,80 @@ function walletsvc_bootstrap --description "Interactive setup of monero-wallet-r
|
|
|
|
|
if not kubectl -n $ns rollout status deploy/$app_name --timeout=180s
|
|
|
|
|
echo (set_color red)"Rollout did not finish in time. Recent events:"(set_color normal) >&2
|
|
|
|
|
kubectl -n $ns get events --sort-by=.lastTimestamp | tail -n 40
|
|
|
|
|
echo (set_color yellow)"Hint:"(set_color normal)" if you see Multi-Attach for the PVC, delete any older pod (e.g. 'kubectl -n $ns delete pod <oldpod>') and the new one will attach."
|
|
|
|
|
echo (set_color yellow)"Hint:"(set_color normal)" if you see Multi-Attach for the PVC, delete any older pod."
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
_banner "Creating/opening wallet via JSON-RPC"
|
|
|
|
|
_banner "Waiting for wallet RPC readiness"
|
|
|
|
|
walletsvc_wait_ready $ns $svc_name 60; or return 1
|
|
|
|
|
|
|
|
|
|
# sanity: verify /data is writable before we try to create anything
|
|
|
|
|
_banner "Creating/opening wallet via JSON-RPC"
|
|
|
|
|
if not walletsvc_check_write $ns $app_name >/dev/null
|
|
|
|
|
echo (set_color red)"PVC is not writable; aborting."(set_color normal)
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# create (idempotent) then open
|
|
|
|
|
# Always attempt create; if it exists, open it.
|
|
|
|
|
if not walletsvc_create_wallet $ns $svc_name $wallet_file $wallet_pass
|
|
|
|
|
echo (set_color red)"Failed to create wallet $wallet_file. See pod logs."(set_color normal)
|
|
|
|
|
echo (set_color yellow)"create_wallet returned non-zero; attempting open anyway…"(set_color normal)
|
|
|
|
|
end
|
|
|
|
|
if not walletsvc_open $ns $svc_name $wallet_file $wallet_pass
|
|
|
|
|
echo (set_color red)"Failed to open wallet $wallet_file. If this wallet already exists, the on-disk password may differ from the Secret. Use walletsvc_open with the correct password or walletsvc_change_wallet_password."(set_color normal)
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if not walletsvc_open $ns $svc_name $wallet_file $wallet_pass
|
|
|
|
|
echo (set_color red)"Failed to open wallet $wallet_file. See pod logs."(set_color normal)
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
# --- Gather summary data (address, seed if enabled)
|
|
|
|
|
set PRIMARY (walletsvc_primary_address $ns $svc_name 2>/dev/null)
|
|
|
|
|
set NEWADDR (walletsvc_new_address $ns $svc_name "bootstrap-deposit" 0 2>/dev/null | awk '/New address:/{print $3}' )
|
|
|
|
|
set SEEDRAW (walletsvc_show_seed $ns $svc_name 2>&1)
|
|
|
|
|
set SEED (echo $SEEDRAW | awk '/Mnemonic seed:/,0{if($0!~/Mnemonic seed:/)print}' )
|
|
|
|
|
|
|
|
|
|
_banner "Done"
|
|
|
|
|
echo "Namespace: $ns"
|
|
|
|
|
echo "App/Deployment: $app_name"
|
|
|
|
|
echo "Service: $svc_name (ClusterIP)"
|
|
|
|
|
echo "Base name: $name"
|
|
|
|
|
echo "Service (in-cluster): $svc_name:18083"
|
|
|
|
|
echo "PVC: $pvc_name via StorageClass '$sc'"
|
|
|
|
|
echo "RPC auth: user='$rpc_user'"
|
|
|
|
|
echo "RPC pass: $rpc_pass"
|
|
|
|
|
echo "Wallet pass: $wallet_pass"
|
|
|
|
|
echo "Wallet file: /data/$wallet_file"
|
|
|
|
|
echo "Primary address (acct 0):"
|
|
|
|
|
echo " $PRIMARY"
|
|
|
|
|
if test -n "$NEWADDR"
|
|
|
|
|
echo "Fresh deposit subaddress (acct 0):"
|
|
|
|
|
echo " $NEWADDR"
|
|
|
|
|
end
|
|
|
|
|
if string match -qi '*Mnemonic seed:*' -- $SEEDRAW
|
|
|
|
|
echo (set_color yellow)"Mnemonic seed (WRITE THIS DOWN, OFFLINE):"(set_color normal)
|
|
|
|
|
echo $SEED
|
|
|
|
|
else
|
|
|
|
|
echo "Mnemonic seed not retrievable via RPC (image may disable it)."
|
|
|
|
|
echo "If needed and supported by your image: walletsvc_show_seed $ns $svc_name"
|
|
|
|
|
end
|
|
|
|
|
echo
|
|
|
|
|
echo "Access locally: walletsvc_portforward $ns $svc_name"
|
|
|
|
|
echo "Show seed (if enabled by image): walletsvc_show_seed $ns $svc_name"
|
|
|
|
|
echo "Access locally: walletsvc_portforward $ns $svc_name # then http://127.0.0.1:18083/json_rpc"
|
|
|
|
|
echo "Quick overview: walletsvc_overview $ns $svc_name"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### ------- utilities -------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
function walletsvc_portforward --description "port-forward RPC to localhost:18083" -a NS SVC
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
echo "Forwarding $NS/$SVC → http://127.0.0.1:18083 (Ctrl+C to stop)…"
|
|
|
|
|
kubectl -n $NS port-forward svc/$SVC 18083:18083
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function walletsvc_status --description "show pod/service status" -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
kubectl -n $NS get deploy,po,svc,netpol -l app=$APP -o wide
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function walletsvc_stop --description "stop the RPC but keep PVC + secrets" -a NS APP SVC
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
if test -z "$SVC"; set SVC $APP; end
|
|
|
|
|
kubectl -n $NS delete deploy $APP --ignore-not-found
|
|
|
|
|
@ -554,7 +682,7 @@ function walletsvc_stop --description "stop the RPC but keep PVC + secrets" -a N
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function walletsvc_purge --description "IRREVERSIBLY delete PVC + secrets (keys on disk!)" -a NS APP PVC SVC
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
if test -z "$PVC"; set PVC wallet-data; end
|
|
|
|
|
if test -z "$SVC"; set SVC $APP; end
|
|
|
|
|
@ -574,7 +702,7 @@ end
|
|
|
|
|
|
|
|
|
|
function walletsvc_show_seed --description "Try to fetch mnemonic via RPC query_key (wallet must be open)" -a NS SVC RPCUSER RPCPASS
|
|
|
|
|
_need jq curl; or return 1
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$RPCUSER"
|
|
|
|
|
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
|
|
|
|
|
@ -597,7 +725,7 @@ end
|
|
|
|
|
|
|
|
|
|
# ---------- core JSON-RPC wrapper (raw JSON to stdout) ----------
|
|
|
|
|
function walletsvc_rpc_call --description "RPC call with secrets (auto port-forward)" -a NS SVC METHOD PARAMS_JSON
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$METHOD"
|
|
|
|
|
echo "Usage: walletsvc_rpc_call [ns] [svc] <method> [params_json]" >&2
|
|
|
|
|
@ -614,7 +742,6 @@ function walletsvc_rpc_call --description "RPC call with secrets (auto port-forw
|
|
|
|
|
set payload (printf '{"jsonrpc":"2.0","id":"0","method":"%s"}' $METHOD)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# capture HTTP code and body without using --fail so we always get the body
|
|
|
|
|
set tmp (mktemp)
|
|
|
|
|
set code (curl -s --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
@ -648,7 +775,7 @@ end
|
|
|
|
|
# Rotate RPC username/password (updates Secret & restarts Deploy)
|
|
|
|
|
function walletsvc_set_rpc_credentials --description "rotate RPC Basic creds" -a NS APP
|
|
|
|
|
_need kubectl; or return 1
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
set SNAME {$APP}-rpc-auth
|
|
|
|
|
|
|
|
|
|
@ -676,7 +803,7 @@ end
|
|
|
|
|
|
|
|
|
|
# Change wallet file password via RPC
|
|
|
|
|
function walletsvc_change_wallet_password --description "change password of a wallet file via RPC" -a NS SVC
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
|
|
|
|
|
read -P "Wallet filename (under /data): " WFILE
|
|
|
|
|
@ -690,18 +817,14 @@ function walletsvc_change_wallet_password --description "change password of a wa
|
|
|
|
|
echo
|
|
|
|
|
if test -z "$NEW"; set NEW (_rand_alnum32); end
|
|
|
|
|
|
|
|
|
|
# open the wallet (in case it's not already)
|
|
|
|
|
set res (walletsvc_rpc_call $NS $SVC open_wallet (printf '{"filename":"%s","password":"%s"}' $WFILE $OLD))
|
|
|
|
|
# ignore error here; we try change anyway so user can see the RPC error if wrong
|
|
|
|
|
|
|
|
|
|
# change password
|
|
|
|
|
walletsvc_rpc_call $NS $SVC change_wallet_password (printf '{"old_password":"%s","new_password":"%s"}' $OLD $NEW)
|
|
|
|
|
echo "New wallet password: $NEW"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- logs from the deployment (quick tail) ---
|
|
|
|
|
function walletsvc_logs -a NS APP LINES
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
if test -z "$LINES"; set LINES 200; end
|
|
|
|
|
kubectl -n $NS logs deploy/$APP --tail=$LINES
|
|
|
|
|
@ -709,7 +832,7 @@ end
|
|
|
|
|
|
|
|
|
|
# --- list files in /data inside the pod ---
|
|
|
|
|
function walletsvc_wallet_ls -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
echo "Listing /data in $NS/$APP…" >&2
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'ls -la /data || true'
|
|
|
|
|
@ -717,7 +840,7 @@ end
|
|
|
|
|
|
|
|
|
|
# --- create a wallet (uses Secret password if PASS omitted) ---
|
|
|
|
|
function walletsvc_create_wallet -a NS SVC FILE PASS LANG
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$FILE"
|
|
|
|
|
echo "Usage: walletsvc_create_wallet [ns] [svc] <filename> [pass] [language]" >&2
|
|
|
|
|
@ -751,7 +874,7 @@ end
|
|
|
|
|
|
|
|
|
|
# --- improved opener: closes first, checks files, hints if password mismatch ---
|
|
|
|
|
function walletsvc_open -a NS SVC FILE PASS
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$FILE"
|
|
|
|
|
echo "Usage: walletsvc_open [ns] [svc] <filename> [password]" >&2
|
|
|
|
|
@ -804,31 +927,84 @@ function walletsvc_open -a NS SVC FILE PASS
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function walletsvc_check_write -a NS APP
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$APP"; set APP wallet-rpc; end
|
|
|
|
|
kubectl -n $NS exec deploy/$APP -- sh -lc 'echo test-$(date +%s) > /data/.write-test && ls -l /data/.write-test && rm -f /data/.write-test'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- ensure wallet is open (create if missing) ---
|
|
|
|
|
function walletsvc_ensure_open -a NS SVC FILE
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$FILE"; set FILE main; end
|
|
|
|
|
|
|
|
|
|
# If files missing, create using secret password
|
|
|
|
|
set exists (kubectl -n $NS exec deploy/$SVC -- sh -lc (printf 'if [ -e "/data/%s" ] || [ -e "/data/%s.keys" ]; then echo yes; else echo no; fi' $FILE $FILE) 2>/dev/null)
|
|
|
|
|
if test "$exists" = "no"
|
|
|
|
|
echo "No /data/$FILE found. Creating…" >&2
|
|
|
|
|
walletsvc_create_wallet $NS $SVC $FILE; or return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Open with secret password
|
|
|
|
|
walletsvc_open $NS $SVC $FILE; or return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- quick primary address print (account 0)
|
|
|
|
|
function walletsvc_primary_address -a NS SVC
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
_pf_start $NS $SVC 18083 18083
|
|
|
|
|
set resp (curl -s --fail --digest \
|
|
|
|
|
-u (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d):(kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.password}' | base64 -d) \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":0}}' \
|
|
|
|
|
http://127.0.0.1:18083/json_rpc)
|
|
|
|
|
_pf_stop
|
|
|
|
|
echo $resp | jq -r '.result.address'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- show unlocked balance quickly
|
|
|
|
|
function walletsvc_unlocked -a NS SVC
|
|
|
|
|
_need python3; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
set res (walletsvc_rpc_call $NS $SVC get_balance '{"account_index":0}')
|
|
|
|
|
set bal (echo $res | jq -r '.result.balance // 0')
|
|
|
|
|
set unl (echo $res | jq -r '.result.unlocked_balance // 0')
|
|
|
|
|
|
|
|
|
|
# Convert piconero -> XMR using Python (fish-safe)
|
|
|
|
|
set balx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $bal)
|
|
|
|
|
set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)
|
|
|
|
|
|
|
|
|
|
echo "balance=$balx XMR unlocked=$unlx XMR"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- wait until unlocked balance >= threshold (XMR), timeout seconds
|
|
|
|
|
function walletsvc_wait_unlocked -a NS SVC XMR_MIN TIMEOUT
|
|
|
|
|
_need python3; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$XMR_MIN"; set XMR_MIN 0; end
|
|
|
|
|
if test -z "$TIMEOUT"; set TIMEOUT 300; end
|
|
|
|
|
set start (date +%s)
|
|
|
|
|
while true
|
|
|
|
|
set res (walletsvc_rpc_call $NS $SVC get_balance '{"account_index":0}')
|
|
|
|
|
set unl (echo $res | jq -r '.result.unlocked_balance // 0')
|
|
|
|
|
set unlx (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $unl)
|
|
|
|
|
set ge (python3 -c 'from decimal import Decimal; import sys; print(1 if Decimal(sys.argv[1]) >= Decimal(sys.argv[2]) else 0)' -- $unlx $XMR_MIN)
|
|
|
|
|
if test $ge -eq 1
|
|
|
|
|
echo "Unlocked: $unlx XMR (≥ $XMR_MIN)"
|
|
|
|
|
return 0
|
|
|
|
|
end
|
|
|
|
|
if test (math (date +%s) - $start) -ge $TIMEOUT
|
|
|
|
|
echo "Timed out waiting for unlocked balance ≥ $XMR_MIN XMR (current $unlx XMR)."
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
sleep 6
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# ---------- friendly overview ----------
|
|
|
|
|
function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$ACCOUNT"; set ACCOUNT 0; end
|
|
|
|
|
|
|
|
|
|
@ -838,28 +1014,25 @@ function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
_pf_start $NS $SVC 18083 18083
|
|
|
|
|
echo "== Wallet RPC overview (ns=$NS svc=$SVC acct=$ACCOUNT)"
|
|
|
|
|
|
|
|
|
|
# Version
|
|
|
|
|
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_version"}' \
|
|
|
|
|
http://127.0.0.1:18083/json_rpc)
|
|
|
|
|
echo $resp | jq -r '"-- Version:\n version=\(.result.version) release=\(.result.release)"'
|
|
|
|
|
|
|
|
|
|
# Height (show error clearly if wallet not open)
|
|
|
|
|
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_height"}' \
|
|
|
|
|
http://127.0.0.1:18083/json_rpc)
|
|
|
|
|
set err (echo $resp | jq -r '.error.message // empty')
|
|
|
|
|
echo "-- Height:"
|
|
|
|
|
if test -n "$err"
|
|
|
|
|
echo "-- Height:"
|
|
|
|
|
echo " ERROR: $err"
|
|
|
|
|
echo " (Tip: open your wallet: walletsvc_open $NS $SVC main)"
|
|
|
|
|
else
|
|
|
|
|
echo $resp | jq -r '"-- Height:\n wallet_height=\(.result.height)"'
|
|
|
|
|
echo $resp | jq -r '" wallet_height=\(.result.height)"'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Accounts
|
|
|
|
|
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d '{"jsonrpc":"2.0","id":"0","method":"get_accounts"}' \
|
|
|
|
|
@ -873,7 +1046,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
| " [\(.account_index)] \(.label) total=\(.balance/1000000000000) XMR unlocked=\(.unlocked_balance/1000000000000) XMR"'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Balance for selected account
|
|
|
|
|
set BODY (printf '{"account_index":%s}' $ACCOUNT)
|
|
|
|
|
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
@ -887,7 +1059,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
echo $resp | jq -r '" balance=\(.result.balance/1000000000000) XMR unlocked=\(.result.unlocked_balance/1000000000000) XMR"'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Primary address
|
|
|
|
|
set resp (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
-d (printf '{"jsonrpc":"2.0","id":"0","method":"get_address","params":{"account_index":%s}}' $ACCOUNT) \
|
|
|
|
|
@ -900,7 +1071,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
echo $resp | jq -r '.result.address'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# First 5 subaddresses
|
|
|
|
|
echo "-- First 5 subaddresses:"
|
|
|
|
|
if test -n "$err"
|
|
|
|
|
echo " (skipped due to error above)"
|
|
|
|
|
@ -908,7 +1078,6 @@ function walletsvc_overview -a NS SVC ACCOUNT
|
|
|
|
|
echo $resp | jq -r '.result.addresses[:5][] | " [\(.address_index)] \(.address) label=\(.label) used=\(.used)"'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Recent transfers
|
|
|
|
|
set BODY (printf '{"account_index":%s,"in":true}' $ACCOUNT)
|
|
|
|
|
set in (curl -s --fail --digest -u "$RPCUSER:$RPCPASS" \
|
|
|
|
|
-H 'Content-Type: application/json' \
|
|
|
|
|
@ -938,7 +1107,7 @@ end
|
|
|
|
|
|
|
|
|
|
# ---------- list addresses ----------
|
|
|
|
|
function walletsvc_list_addresses -a NS SVC ACCOUNT
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$ACCOUNT"; set ACCOUNT 0; end
|
|
|
|
|
set RPCUSER (kubectl -n $NS get secret {$SVC}-rpc-auth -o jsonpath='{.data.username}' | base64 -d)
|
|
|
|
|
@ -960,7 +1129,7 @@ end
|
|
|
|
|
|
|
|
|
|
# ---------- new subaddress ----------
|
|
|
|
|
function walletsvc_new_address -a NS SVC LABEL ACCOUNT
|
|
|
|
|
if test -z "$NS"; set NS monero; end
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$ACCOUNT"; set ACCOUNT 0; end
|
|
|
|
|
if test -z "$LABEL"; set LABEL (printf "deposit-%s" (date +%Y%m%d-%H%M%S)); end
|
|
|
|
|
@ -981,47 +1150,116 @@ function walletsvc_new_address -a NS SVC LABEL ACCOUNT
|
|
|
|
|
echo $resp | jq -r --arg L "$LABEL" '"New address: \(.result.address) [index=\(.result.address_index)] label=" + $L'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- send a specific amount XMR to a single address (optionally payment_id)
|
|
|
|
|
# Usage: walletsvc_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]
|
|
|
|
|
function walletsvc_send -a NS SVC TO AMT_XMR PID
|
|
|
|
|
_need jq curl python3; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$TO" -o -z "$AMT_XMR"
|
|
|
|
|
echo "Usage: walletsvc_send [ns] [svc] <to_address> <amount_xmr> [payment_id_hex]" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Convert amount to atomic units
|
|
|
|
|
set ATOMIC (_xmr_to_atomic $AMT_XMR)
|
|
|
|
|
if test -z "$ATOMIC"
|
|
|
|
|
echo "Failed to convert amount '$AMT_XMR' to piconero." >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Build JSON
|
|
|
|
|
set body_base (printf '{"destinations":[{"amount":%s,"address":"%s"}],"account_index":0,"priority":1,"get_tx_key":true,"do_not_relay":false}' $ATOMIC $TO)
|
|
|
|
|
if test -n "$PID"
|
|
|
|
|
set body (string replace '}' ',"payment_id":"'$PID'"}' -- $body_base)
|
|
|
|
|
else
|
|
|
|
|
set body $body_base
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set res (walletsvc_rpc_call $NS $SVC transfer $body)
|
|
|
|
|
set err (echo $res | jq -r '.error.message // empty')
|
|
|
|
|
if test -n "$err"
|
|
|
|
|
echo "ERROR: $err" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set fee (echo $res | jq -r '.result.fee // 0')
|
|
|
|
|
set tx (echo $res | jq -r '.result.tx_hash // (.result.tx_hash_list[0] // "")')
|
|
|
|
|
|
|
|
|
|
# piconero -> XMR without heredoc
|
|
|
|
|
set fee_xmr (python3 -c 'from decimal import Decimal; import sys; print(Decimal(sys.argv[1]) / Decimal("1e12"))' -- $fee)
|
|
|
|
|
|
|
|
|
|
echo "Sent transaction:"
|
|
|
|
|
echo " tx: $tx"
|
|
|
|
|
echo " fee: $fee_xmr XMR ($fee piconero)"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# --- sweep all unlocked funds to an address (account 0)
|
|
|
|
|
# Usage: walletsvc_sweep_all [ns] [svc] <to_address>
|
|
|
|
|
function walletsvc_sweep_all -a NS SVC TO
|
|
|
|
|
_need jq curl; or return 1
|
|
|
|
|
if test -z "$NS"; set NS crypto; end
|
|
|
|
|
if test -z "$SVC"; set SVC wallet-rpc; end
|
|
|
|
|
if test -z "$TO"
|
|
|
|
|
echo "Usage: walletsvc_sweep_all [ns] [svc] <to_address>" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
set body (printf '{"address":"%s","account_index":0,"priority":1,"do_not_relay":false,"get_tx_keys":true}' $TO)
|
|
|
|
|
set res (walletsvc_rpc_call $NS $SVC sweep_all $body)
|
|
|
|
|
set err (echo $res | jq -r '.error.message // empty')
|
|
|
|
|
if test -n "$err"
|
|
|
|
|
echo "ERROR: $err" >&2
|
|
|
|
|
return 1
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
echo "Sweep transactions:"
|
|
|
|
|
echo $res | jq -r '.result.tx_hash_list[]? | " tx: " + .'
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Optional: non-sensitive cluster snapshot to help tune manifests.
|
|
|
|
|
function cluster_probe
|
|
|
|
|
set OUT cluster-snapshot.txt
|
|
|
|
|
echo "# Cluster snapshot "(date -Is) > $OUT
|
|
|
|
|
echo -e "\n## kubectl version" >> $OUT
|
|
|
|
|
printf "# Cluster snapshot %s\n" (date -Is) > $OUT
|
|
|
|
|
|
|
|
|
|
printf "\n## kubectl version\n" >> $OUT
|
|
|
|
|
kubectl version --short >> $OUT 2>&1
|
|
|
|
|
|
|
|
|
|
echo -e "\n## Nodes" >> $OUT
|
|
|
|
|
printf "\n## Nodes\n" >> $OUT
|
|
|
|
|
kubectl get nodes -o wide >> $OUT
|
|
|
|
|
echo -e "\n### Node resources" >> $OUT
|
|
|
|
|
|
|
|
|
|
printf "\n### Node resources\n" >> $OUT
|
|
|
|
|
kubectl describe nodes | egrep -i 'Name:|Roles:|Capacity:|Allocatable:|cpu|memory|ephemeral' >> $OUT
|
|
|
|
|
|
|
|
|
|
echo -e "\n## Namespaces" >> $OUT
|
|
|
|
|
printf "\n## Namespaces\n" >> $OUT
|
|
|
|
|
kubectl get ns >> $OUT
|
|
|
|
|
|
|
|
|
|
echo -e "\n## StorageClasses (summary)" >> $OUT
|
|
|
|
|
printf "\n## StorageClasses (summary)\n" >> $OUT
|
|
|
|
|
kubectl get storageclass >> $OUT
|
|
|
|
|
echo -e "\n## StorageClasses (yaml)" >> $OUT
|
|
|
|
|
printf "\n## StorageClasses (yaml)\n" >> $OUT
|
|
|
|
|
kubectl get storageclass -o yaml >> $OUT
|
|
|
|
|
|
|
|
|
|
echo -e "\n## PV/PVC" >> $OUT
|
|
|
|
|
kubectl get pv >> $OUT 2>&1
|
|
|
|
|
kubectl get pvc -A >> $OUT 2>&1
|
|
|
|
|
printf "\n## PV/PVC\n" >> $OUT
|
|
|
|
|
kubectl get pv >> $OUT 2>/dev/null
|
|
|
|
|
kubectl get pvc -A >> $OUT 2>/dev/null
|
|
|
|
|
|
|
|
|
|
echo -e "\n## Ingress / Services" >> $OUT
|
|
|
|
|
kubectl get ingressclass >> $OUT 2>&1
|
|
|
|
|
kubectl get ingress -A >> $OUT 2>&1
|
|
|
|
|
printf "\n## Ingress / Services\n" >> $OUT
|
|
|
|
|
kubectl get ingressclass >> $OUT 2>/dev/null
|
|
|
|
|
kubectl get ingress -A >> $OUT 2>/dev/null
|
|
|
|
|
kubectl get svc -A | egrep -i 'NAMESPACE|LoadBalancer|EXTERNAL-IP|traefik|ingress' >> $OUT
|
|
|
|
|
|
|
|
|
|
echo -e "\n## kube-system pods (CNI hint)" >> $OUT
|
|
|
|
|
kubectl -n kube-system get pods -o wide >> $OUT 2>&1
|
|
|
|
|
printf "\n## kube-system pods (CNI hint)\n" >> $OUT
|
|
|
|
|
kubectl -n kube-system get pods -o wide >> $OUT 2>/dev/null
|
|
|
|
|
|
|
|
|
|
echo -e "\n## Default limits/quotas" >> $OUT
|
|
|
|
|
kubectl -n default get limitrange,resourcequota >> $OUT 2>&1
|
|
|
|
|
printf "\n## Default limits/quotas\n" >> $OUT
|
|
|
|
|
kubectl -n default get limitrange,resourcequota >> $OUT 2>/dev/null
|
|
|
|
|
|
|
|
|
|
echo -e "\n## Tools present on your machine" >> $OUT
|
|
|
|
|
printf "\n## Tools present on your machine\n" >> $OUT
|
|
|
|
|
for t in kubectl helm jq curl awk envsubst fish
|
|
|
|
|
if type -q $t
|
|
|
|
|
echo "$t: OK" >> $OUT
|
|
|
|
|
printf "%s: OK\n" $t >> $OUT
|
|
|
|
|
else
|
|
|
|
|
echo "$t: MISSING" >> $OUT
|
|
|
|
|
printf "%s: MISSING\n" $t >> $OUT
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
@ -1030,8 +1268,8 @@ end
|
|
|
|
|
|
|
|
|
|
# --- one-line command reference (keep in sync) ---
|
|
|
|
|
function walletsvc_help
|
|
|
|
|
echo "walletsvc_bootstrap # interactive deploy + secrets + PVC + first wallet (multiwallet RPC)"
|
|
|
|
|
echo "walletsvc_status [ns] [app] # show deploy, pods, service, netpol"
|
|
|
|
|
echo "walletsvc_bootstrap # interactive deploy + secrets + PVC + first wallet (multiwallet RPC), idempotent"
|
|
|
|
|
echo "walletsvc_status [ns] [app] # show deploy, pods, service, netpol (default ns=crypto)"
|
|
|
|
|
echo "walletsvc_portforward [ns] [svc] # port-forward RPC to localhost:18083 (CTRL+C to stop)"
|
|
|
|
|
echo "walletsvc_logs [ns] [app] [N] # tail N (default 200) lines of RPC pod logs"
|
|
|
|
|
echo "walletsvc_wallet_ls [ns] [app] # list files under /data in the RPC pod (check wallet files)"
|
|
|
|
|
@ -1044,67 +1282,19 @@ function walletsvc_help
|
|
|
|
|
echo "walletsvc_list_addresses [ns] [svc] [acct=0] # list all subaddresses for account"
|
|
|
|
|
echo "walletsvc_new_address [ns] [svc] [label] [acct=0] # create labeled subaddress"
|
|
|
|
|
echo "walletsvc_set_rpc_credentials [ns] [app] # rotate RPC basic auth (secret + rollout)"
|
|
|
|
|
echo "walletsvc_change_wallet_password [ns] [svc] # change password of a wallet file via RPC"
|
|
|
|
|
echo "walletsvc_change_wallet_password [ns] [svc] # change wallet file password via RPC"
|
|
|
|
|
echo "walletsvc_stop [ns] [app] [svc] # delete deployment+service only"
|
|
|
|
|
echo "walletsvc_purge [ns] [app] [pvc] [svc] # delete deploy+svc+PVC+secrets (danger!)"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
function walletsvc_help_detailed
|
|
|
|
|
echo "Commands (defaults: ns=monero, svc/app=wallet-rpc)"
|
|
|
|
|
echo "Commands (defaults: ns=crypto, svc/app=wallet-rpc)"
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_bootstrap"
|
|
|
|
|
echo " Interactive deploy: creates namespace, secrets, PVC, Deployment (fsGroup + init perms), Service, NetPolicy."
|
|
|
|
|
echo " Prompts you for a *Monero* daemon (monerod) with NO default; probes it via get_info to ensure mainnet Monero."
|
|
|
|
|
echo " Set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass the probe (not recommended)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_status [ns] [app]"
|
|
|
|
|
echo " Show Deployment/Pod/Service/NetPolicy for the app label."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_portforward [ns] [svc]"
|
|
|
|
|
echo " Port-forward RPC to http://127.0.0.1:18083 (Ctrl+C to stop)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_logs [ns] [app] [N]"
|
|
|
|
|
echo " Tail N lines (default 200) from the wallet RPC pod logs."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_wallet_ls [ns] [app]"
|
|
|
|
|
echo " List /data contents inside the pod (quick sanity for wallet files)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_check_write [ns] [app]"
|
|
|
|
|
echo " Touch/remove a temp file under /data to prove the PVC is writable."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_rpc_test [ns] [svc]"
|
|
|
|
|
echo " JSON-RPC get_version (sanity)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_rpc_call [ns] [svc] <method> [params_json]"
|
|
|
|
|
echo " Raw JSON-RPC with digest auth; prints body; non-200 returns non-zero."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_create_wallet [ns] [svc] <file> [pass] [language]"
|
|
|
|
|
echo " Create /data/<file>{,.keys}. Pass defaults to Secret <svc>-wallet-pass. Language defaults to English."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_open [ns] [svc] <file> [pass]"
|
|
|
|
|
echo " Close any open wallet, verify files exist, then open with the given (or Secret) password."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_ensure_open [ns] [svc] [file]"
|
|
|
|
|
echo " If files missing, create with Secret pass, then open."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_overview [ns] [svc] [acct=0]"
|
|
|
|
|
echo " Pretty view: version, height, accounts, balances, addresses, recent transfers."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_list_addresses [ns] [svc] [acct=0]"
|
|
|
|
|
echo " List all subaddresses for the account."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_new_address [ns] [svc] [label] [acct=0]"
|
|
|
|
|
echo " Create a new subaddress with optional label (default: timestamped)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_set_rpc_credentials [ns] [app]"
|
|
|
|
|
echo " Rotate RPC basic auth (updates Secret and restarts Deployment)."
|
|
|
|
|
echo
|
|
|
|
|
echo "walletsvc_change_wallet_password [ns] [svc]"
|
|
|
|
|
echo " Change wallet file password via RPC."
|
|
|
|
|
echo
|
|
|
|
|
echo "Notes:"
|
|
|
|
|
echo " - Your wallet must talk to a Monero daemon (monerod). p2pool is a mining pool, not a daemon."
|
|
|
|
|
echo " - For the most trust-minimized setup, run your own monerod and point the wallet at it."
|
|
|
|
|
echo " - This script blocks suspicious daemon addresses (e.g. containing 'qubic') and probes that the daemon"
|
|
|
|
|
echo " speaks Monero mainnet before proceeding."
|
|
|
|
|
echo " Interactive deploy: creates/updates secrets, PVC, Deployment (fsGroup+init perms), Service, NetPolicy."
|
|
|
|
|
echo " Idempotent: if deploy/svc/netpol already exist, you can clean them up (PVC+secrets kept) and continue."
|
|
|
|
|
echo " Uses your in-cluster monerod by default (monerod:18081 in ns=crypto)."
|
|
|
|
|
echo " Probes it via a temporary port-forward so it works from your workstation."
|
|
|
|
|
echo " Set WALLETSVC_SKIP_DAEMON_CHECK=1 to bypass the daemon probe (not recommended)."
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|