|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +# ---------- config ---------- |
| 5 | +SESSION="${SESSION:-amaru-demo}" |
| 6 | + |
| 7 | +# Derive AMARU_DIR from script location (scripts/relay-1/demo.sh -> amaru root) |
| 8 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 9 | +AMARU_DIR="${AMARU_DIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}" |
| 10 | + |
| 11 | +LOGDIR="${LOGDIR:-/tmp/amaru-relay-1}" |
| 12 | +RUNDIR="${RUNDIR:-$AMARU_DIR/scripts/relay-1/run}" |
| 13 | + |
| 14 | +# Cardano node configuration |
| 15 | +CARDANO_NODE="${CARDANO_NODE:-}" # path to cardano-node executable |
| 16 | +CARDANO_NODE_CONFIG_DIR="${CARDANO_NODE_CONFIG_DIR:-}" # directory with config.json, topology.json, etc. |
| 17 | + |
| 18 | +# Deterministic ports (can be overridden with env vars) |
| 19 | +UPSTREAM_PORT="${UPSTREAM_PORT:-3001}" # cardano-node listener |
| 20 | +LISTEN_PORT="${LISTEN_PORT:-4001}" # amaru listener (for downstream) |
| 21 | +DOWNSTREAM_LISTEN_PORT="${DOWNSTREAM_LISTEN_PORT:-4002}" # amaru downstream listener |
| 22 | + |
| 23 | +# ---------- helpers ---------- |
| 24 | +ensure_dirs() { |
| 25 | + mkdir -p "$LOGDIR" "$RUNDIR" |
| 26 | +} |
| 27 | + |
| 28 | +die() { echo "error: $*" >&2; exit 1; } |
| 29 | + |
| 30 | +have() { command -v "$1" >/dev/null 2>&1; } |
| 31 | + |
| 32 | +tmux_new_session() { |
| 33 | + tmux new-session -d -s "$SESSION" -c "$AMARU_DIR" -n nodes |
| 34 | + tmux set-option -t "$SESSION" remain-on-exit on |
| 35 | + # Disable automatic renaming so pane titles stay fixed |
| 36 | + tmux set-option -t "$SESSION" allow-rename off |
| 37 | + tmux set-option -t "$SESSION" automatic-rename off |
| 38 | + # Show pane titles in the border with colors |
| 39 | + tmux set-option -t "$SESSION" pane-border-status top |
| 40 | + tmux set-option -t "$SESSION" pane-border-format " #[fg=white,bg=blue,bold] #{pane_title} #[default] " |
| 41 | + tmux set-option -t "$SESSION" pane-border-style "fg=brightblack" |
| 42 | + tmux set-option -t "$SESSION" pane-active-border-style "fg=blue,bold" |
| 43 | + # Enable mouse support and clipboard integration |
| 44 | + tmux set-option -t "$SESSION" mouse on |
| 45 | + tmux set-option -t "$SESSION" set-clipboard on |
| 46 | +} |
| 47 | + |
| 48 | +tmux_kill_session() { |
| 49 | + tmux kill-session -t "$SESSION" 2>/dev/null || true |
| 50 | +} |
| 51 | + |
| 52 | +# Run a command in a pane, with nice bash defaults |
| 53 | +# Usage: pane_run <target-pane> <command> |
| 54 | +pane_run() { |
| 55 | + local target="$1"; shift |
| 56 | + local cmd="$*" |
| 57 | + # Join multi-line commands into a single line to avoid multiple prompts |
| 58 | + local oneline |
| 59 | + oneline=$(printf '%s' "$cmd" | tr '\n' ';' | sed 's/;;*/;/g; s/^;//; s/;$//') |
| 60 | + tmux send-keys -t "$target" "set -eo pipefail; cd '$AMARU_DIR'; $oneline" C-m |
| 61 | +} |
| 62 | + |
| 63 | +# ---------- commands you must fill ---------- |
| 64 | +cmd_upstream() { |
| 65 | + cat <<EOF |
| 66 | +printf '\033]2;upstream\033\\\\' |
| 67 | +echo "[upstream] starting..." |
| 68 | +$CARDANO_NODE run --config $CARDANO_NODE_CONFIG_DIR/config.json --topology $CARDANO_NODE_CONFIG_DIR/topology.json --database-path $CARDANO_NODE_CONFIG_DIR/db --socket-path $CARDANO_NODE_CONFIG_DIR/node.socket --port $UPSTREAM_PORT 2>&1 | tee '$LOGDIR/upstream.log' |
| 69 | +sleep 999999 |
| 70 | +EOF |
| 71 | +} |
| 72 | + |
| 73 | +cmd_amaru() { |
| 74 | + local delay="${UPSTREAM_INIT_DELAY:-10}" |
| 75 | + cat <<EOF |
| 76 | +printf '\033]2;amaru\033\\\\' |
| 77 | +echo "[amaru] waiting ${delay}s for cardano-node to initialize..." |
| 78 | +sleep $delay |
| 79 | +echo "[amaru] starting..." |
| 80 | +cd $AMARU_DIR |
| 81 | +export AMARU_TRACE=warn,amaru_consensus=debug,amaru::ledger=info |
| 82 | +ulimit -n 65536 |
| 83 | +cargo run --profile dev -- --with-json-traces run --peer-address 127.0.0.1:$UPSTREAM_PORT --listen-address 0.0.0.0:$LISTEN_PORT --chain-dir $RUNDIR/amaru/chain.preprod.db --ledger-dir $RUNDIR/amaru/ledger.preprod.db 2>&1 | tee '$LOGDIR/amaru.log' |
| 84 | +sleep 999999 |
| 85 | +EOF |
| 86 | +} |
| 87 | + |
| 88 | +cmd_amaru_downstream() { |
| 89 | + local delay="${DOWNSTREAM_INIT_DELAY:-15}" |
| 90 | + cat <<EOF |
| 91 | +printf '\033]2;amaru-downstream\033\\\\' |
| 92 | +echo "[amaru-downstream] waiting ${delay}s for amaru to initialize..." |
| 93 | +sleep $delay |
| 94 | +echo "[amaru-downstream] starting..." |
| 95 | +cd $AMARU_DIR |
| 96 | +export AMARU_TRACE=warn,amaru_consensus=debug,amaru::ledger=info |
| 97 | +ulimit -n 65536 |
| 98 | +cargo run --profile dev -- --with-json-traces run --peer-address 127.0.0.1:$LISTEN_PORT --listen-address 0.0.0.0:$DOWNSTREAM_LISTEN_PORT --chain-dir $RUNDIR/amaru-downstream/chain.preprod.db --ledger-dir $RUNDIR/amaru-downstream/ledger.preprod.db 2>&1 | tee '$LOGDIR/amaru-downstream.log' |
| 99 | +sleep 999999 |
| 100 | +EOF |
| 101 | +} |
| 102 | + |
| 103 | +cmd_watch() { |
| 104 | + # Curated view: tweak grep patterns to your log messages (handshake, ChainSync, BlockFetch…) |
| 105 | + cat <<EOF |
| 106 | +printf '\033]2;watch\033\\\\' |
| 107 | +echo "[watch] tailing logs (Ctrl-c in this pane won't stop the session; use ./demo.sh stop)" |
| 108 | +( tail -n +1 -F '$LOGDIR/upstream.log' '$LOGDIR/amaru.log' '$LOGDIR/amaru-downstream.log' | sed -E -e 's|^|[log] |' -e 's|\\[upstream\\]|[upstream]|g' ) | grep -E --line-buffered -i 'connected|handshake|chainsync|blockfetch|tip|accepted|peer|error|warn|TODO|\\[log\\]' || true |
| 109 | +EOF |
| 110 | +} |
| 111 | + |
| 112 | +# ---------- main ---------- |
| 113 | +start() { |
| 114 | + have tmux || die "tmux not found" |
| 115 | + [[ -n "$CARDANO_NODE" ]] || die "CARDANO_NODE must be set (path to cardano-node executable)" |
| 116 | + [[ -x "$CARDANO_NODE" ]] || die "CARDANO_NODE is not executable: $CARDANO_NODE" |
| 117 | + [[ -n "$CARDANO_NODE_CONFIG_DIR" ]] || die "CARDANO_NODE_CONFIG_DIR must be set (directory with config.json, topology.json, etc.)" |
| 118 | + [[ -d "$CARDANO_NODE_CONFIG_DIR" ]] || die "CARDANO_NODE_CONFIG_DIR does not exist: $CARDANO_NODE_CONFIG_DIR" |
| 119 | + [[ -f "$CARDANO_NODE_CONFIG_DIR/config.json" ]] || die "config.json not found in $CARDANO_NODE_CONFIG_DIR" |
| 120 | + [[ -f "$CARDANO_NODE_CONFIG_DIR/topology.json" ]] || die "topology.json not found in $CARDANO_NODE_CONFIG_DIR" |
| 121 | + [[ -d "$AMARU_DIR" ]] || die "AMARU_DIR does not exist: $AMARU_DIR" |
| 122 | + |
| 123 | + ensure_dirs |
| 124 | + rm -f "$LOGDIR"/*.log 2>/dev/null || true |
| 125 | + |
| 126 | + # Copy databases into isolated run directories |
| 127 | + rm -rf "$RUNDIR/amaru" "$RUNDIR/amaru-downstream" |
| 128 | + mkdir -p "$RUNDIR/amaru" "$RUNDIR/amaru-downstream" |
| 129 | + cp -r "$AMARU_DIR/chain.preprod.db" "$RUNDIR/amaru/chain.preprod.db" |
| 130 | + cp -r "$AMARU_DIR/ledger.preprod.db" "$RUNDIR/amaru/ledger.preprod.db" |
| 131 | + cp -r "$AMARU_DIR/chain.preprod.db" "$RUNDIR/amaru-downstream/chain.preprod.db" |
| 132 | + cp -r "$AMARU_DIR/ledger.preprod.db" "$RUNDIR/amaru-downstream/ledger.preprod.db" |
| 133 | + |
| 134 | + # reset session |
| 135 | + tmux_kill_session |
| 136 | + tmux_new_session |
| 137 | + |
| 138 | + # Layout: |
| 139 | + # nodes window: left split top/bottom = upstream/downstream, right = amaru |
| 140 | + # Pane numbers after splits: 0=top-left, 1=bottom-left, 2=right |
| 141 | + tmux split-window -t "$SESSION:nodes" -h -c "$AMARU_DIR" # create right pane |
| 142 | + tmux split-window -t "$SESSION:nodes.0" -v -c "$AMARU_DIR" # split left into upstream/downstream |
| 143 | + |
| 144 | + tmux select-pane -t "$SESSION:nodes.0" \; select-pane -t "$SESSION:nodes.1" |
| 145 | + |
| 146 | + # Name panes |
| 147 | + tmux select-pane -t "$SESSION:nodes.0" -T "upstream" |
| 148 | + tmux select-pane -t "$SESSION:nodes.1" -T "amaru-downstream" |
| 149 | + tmux select-pane -t "$SESSION:nodes.2" -T "amaru" |
| 150 | + |
| 151 | + # Add watch window |
| 152 | + tmux new-window -t "$SESSION" -n watch -c "$AMARU_DIR" |
| 153 | + tmux select-pane -t "$SESSION:watch.0" -T "watch" |
| 154 | + |
| 155 | + # Run commands |
| 156 | + pane_run "$SESSION:nodes.0" "$(cmd_upstream)" |
| 157 | + pane_run "$SESSION:nodes.1" "$(cmd_amaru_downstream)" |
| 158 | + pane_run "$SESSION:nodes.2" "$(cmd_amaru)" |
| 159 | + pane_run "$SESSION:watch.0" "$(cmd_watch)" |
| 160 | + |
| 161 | + # Attach |
| 162 | + tmux select-window -t "$SESSION:nodes" |
| 163 | + exec tmux attach -t "$SESSION" |
| 164 | +} |
| 165 | + |
| 166 | +restart_pane() { |
| 167 | + local target="$1"; shift |
| 168 | + local cmd="$*" |
| 169 | + tmux send-keys -t "$target" C-c 2>/dev/null || true |
| 170 | + sleep 1 |
| 171 | + pane_run "$target" "$cmd" |
| 172 | +} |
| 173 | + |
| 174 | +restart() { |
| 175 | + local pane="${1:?usage: $0 restart <upstream|amaru|amaru-downstream>}" |
| 176 | + [[ -n "$CARDANO_NODE" ]] || die "CARDANO_NODE must be set (path to cardano-node executable)" |
| 177 | + [[ -x "$CARDANO_NODE" ]] || die "CARDANO_NODE is not executable: $CARDANO_NODE" |
| 178 | + [[ -n "$CARDANO_NODE_CONFIG_DIR" ]] || die "CARDANO_NODE_CONFIG_DIR must be set" |
| 179 | + [[ -d "$CARDANO_NODE_CONFIG_DIR" ]] || die "CARDANO_NODE_CONFIG_DIR does not exist: $CARDANO_NODE_CONFIG_DIR" |
| 180 | + [[ -d "$AMARU_DIR" ]] || die "AMARU_DIR does not exist: $AMARU_DIR" |
| 181 | + case "$pane" in |
| 182 | + upstream) restart_pane "$SESSION:nodes.0" "$(cmd_upstream)" ;; |
| 183 | + amaru-downstream) restart_pane "$SESSION:nodes.1" "$(cmd_amaru_downstream)" ;; |
| 184 | + amaru) restart_pane "$SESSION:nodes.2" "$(cmd_amaru)" ;; |
| 185 | + *) die "unknown pane: $pane (choose upstream, amaru, or amaru-downstream)" ;; |
| 186 | + esac |
| 187 | +} |
| 188 | + |
| 189 | +stop() { |
| 190 | + # Kill processes running in the tmux panes before killing the session |
| 191 | + if tmux has-session -t "$SESSION" 2>/dev/null; then |
| 192 | + # Send SIGTERM to all processes in each pane |
| 193 | + for pane in 0 1 2; do |
| 194 | + tmux send-keys -t "$SESSION:nodes.$pane" C-c 2>/dev/null || true |
| 195 | + done |
| 196 | + tmux send-keys -t "$SESSION:watch.0" C-c 2>/dev/null || true |
| 197 | + sleep 1 |
| 198 | + fi |
| 199 | + |
| 200 | + # Also kill any lingering processes (scoped to this demo only) |
| 201 | + pkill -f -- "--chain-dir $RUNDIR/amaru/chain.preprod.db" 2>/dev/null || true |
| 202 | + pkill -f -- "--chain-dir $RUNDIR/amaru-downstream/chain.preprod.db" 2>/dev/null || true |
| 203 | + pkill -f -- "--socket-path $CARDANO_NODE_CONFIG_DIR/node.socket" 2>/dev/null || true |
| 204 | + |
| 205 | + tmux_kill_session |
| 206 | + echo "stopped tmux session: $SESSION" |
| 207 | +} |
| 208 | + |
| 209 | +status() { |
| 210 | + if tmux has-session -t "$SESSION" 2>/dev/null; then |
| 211 | + echo "running: $SESSION" |
| 212 | + tmux list-windows -t "$SESSION" |
| 213 | + else |
| 214 | + echo "not running: $SESSION" |
| 215 | + fi |
| 216 | +} |
| 217 | + |
| 218 | +case "${1:-start}" in |
| 219 | + start) start ;; |
| 220 | + stop) stop ;; |
| 221 | + restart) restart "${2:-}" ;; |
| 222 | + status) status ;; |
| 223 | + *) die "usage: $0 {start|stop|restart <pane>|status}" ;; |
| 224 | +esac |
0 commit comments