#!/usr/bin/env bash # 5-node mix simulation using logoscore instances with embedded delivery + RLN modules. # Each node is its own logoscore process — no standalone wakunode2 or HTTP polling. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" DELIVERY_DIR="$(cd ../.. && pwd)" RLN_PROJECT_DIR="$(cd "$DELIVERY_DIR/.." && pwd)" export RISC0_DEV_MODE=1 export TMPDIR=/tmp # --- Node identity constants (from config*.toml) --- NODEKEYS=( "f98e3fba96c32e8d1967d460f1b79457380e1a895f7971cecc8528abe733781a" "09e9d134331953357bd38bbfce8edb377f4b6308b4f3bfbe85c610497053d684" "ed54db994682e857d77cd6fb81be697382dc43aa5cd78e16b0ec8098549f860e" "42f96f29f2d6670938b0864aced65a332dcf5774103b4c44ec4d0ea4ef3c47d6" "3ce887b3c34b7a92dd2868af33941ed1dbec4893b054572cd5078da09dd923d4" ) MIXKEYS=( "a87db88246ec0eedda347b9b643864bee3d6933eb15ba41e6d58cb678d813258" "c86029e02c05a7e25182974b519d0d52fcbafeca6fe191fbb64857fb05be1a53" "b858ac16bbb551c4b2973313b1c8c8f7ea469fca03f1608d200bbf58d388ec7f" "d8bd379bb394b0f22dd236d63af9f1a9bc45266beffc3fbbe19e8b6575f2535b" "780fff09e51e98df574e266bf3266ec6a3a1ddfcf7da826a349a29c137009d49" ) PEER_IDS=( "16Uiu2HAmPiEs2ozjjJF2iN2Pe2FYeMC9w4caRHKYdLdAfjgbWM6o" "16Uiu2HAmLtKaFaSWDohToWhWUZFLtqzYZGPFuXwKrojFVF6az5UF" "16Uiu2HAmTEDHwAziWUSz6ZE23h5vxG2o4Nn7GazhMor4bVuMXTrA" "16Uiu2HAmPwRKZajXtfb1Qsv45VVfRZgK3ENdfmnqzSrVm3BczF6f" "16Uiu2HAmRhxmCHBYdXt1RibXrjAUNJbduAhzaTHwFCZT4qWnqZAu" ) MIX_PUBKEYS=( "9d09ce624f76e8f606265edb9cca2b7de9b41772a6d784bddaf92ffa8fba7d2c" "9231e86da6432502900a84f867004ce78632ab52cd8e30b1ec322cd795710c2a" "275cd6889e1f29ca48e5b9edb800d1a94f49f13d393a0ecf1a07af753506de6c" "e0ed594a8d506681be075e8e23723478388fb182477f7a469309a25e7076fc18" "8fd7a1a7c19b403d231452a9b1ea40eb1cc76f455d918ef8980e7685f9eeeb1f" ) BASE_TCP_PORT=60001 BASE_DISC_PORT=9001 NUM_NODES=5 CONTENT_TOPIC="/toy-chat/2/baixa-chiado/proto" case "$(uname -s)-$(uname -m)" in Darwin-arm64) PLATFORM="darwin-arm64-dev"; EXT="dylib";; Linux-x86_64) PLATFORM="linux-x86_64-dev"; EXT="so";; Linux-aarch64) PLATFORM="linux-aarch64-dev"; EXT="so";; *) echo "Unsupported platform"; exit 1;; esac # --- Cleanup --- SEQUENCER_PID="" INSTANCE_PIDS=() MODULES_DIRS=() WORK_DIR="" cleanup() { echo "" echo "=== Shutting down ===" for pid in "${INSTANCE_PIDS[@]+"${INSTANCE_PIDS[@]}"}"; do if [ -n "$pid" ]; then local children children=$(pgrep -P "$pid" 2>/dev/null || true) kill "$pid" $children 2>/dev/null || true wait "$pid" 2>/dev/null || true fi done pkill -f 'logos_host' 2>/dev/null || true if [ -n "$SEQUENCER_PID" ]; then kill "$SEQUENCER_PID" 2>/dev/null || true wait "$SEQUENCER_PID" 2>/dev/null || true fi for mdir in "${MODULES_DIRS[@]}"; do [ -n "$mdir" ] && rm -rf "$mdir" done if [ -n "$WORK_DIR" ]; then echo " Logs: $WORK_DIR" fi echo "Done." } trap cleanup EXIT echo "=== Mix Simulation (5 LogosCore Instances) ===" echo " RLN project: $RLN_PROJECT_DIR" echo " Delivery: $DELIVERY_DIR" echo "" pkill -f 'logos_host' 2>/dev/null || true sleep 1 rm -f /tmp/logos_* 2>/dev/null || true # ---------- Phase 1: Sequencer ---------- echo "[1/7] Starting sequencer..." (cd "$RLN_PROJECT_DIR" && git submodule update --init lssa) if nc -z 127.0.0.1 3040 2>/dev/null; then OLD_PID=$(lsof -ti tcp:3040 2>/dev/null || true) if [ -n "$OLD_PID" ]; then echo " Port 3040 in use by PID $OLD_PID. Killing..." kill "$OLD_PID" 2>/dev/null || true sleep 1 fi fi rm -rf "$RLN_PROJECT_DIR/lssa/rocksdb" echo " Building sequencer (first run may take several minutes)..." (cd "$RLN_PROJECT_DIR/lssa" && cargo build --features standalone -p sequencer_runner 2>&1 | tail -3) || { echo " FATAL: sequencer build failed" exit 1 } SEQUENCER_BIN="$RLN_PROJECT_DIR/lssa/target/debug/sequencer_runner" (cd "$RLN_PROJECT_DIR/lssa" && env RUST_LOG=info "$SEQUENCER_BIN" sequencer_runner/configs/debug) >/dev/null 2>&1 & SEQUENCER_PID=$! echo " PID: $SEQUENCER_PID" echo " Waiting for port 3040..." for i in $(seq 1 300); do if nc -z 127.0.0.1 3040 2>/dev/null; then echo " Sequencer ready." break fi if ! kill -0 "$SEQUENCER_PID" 2>/dev/null; then echo " ERROR: Sequencer exited unexpectedly." exit 1 fi sleep 1 done if ! nc -z 127.0.0.1 3040 2>/dev/null; then echo " ERROR: Sequencer did not start within 300s." exit 1 fi # ---------- Phase 2: Deploy programs ---------- echo "[2/7] Deploying programs..." # Build guest binaries if missing (required by run_setup and register_member) LEZ_RLN_DIR="$RLN_PROJECT_DIR/lez-rln" GUEST_BIN="$LEZ_RLN_DIR/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/rln_registration.bin" if [ ! -f "$GUEST_BIN" ]; then if ! command -v cargo-risczero &>/dev/null; then echo " FATAL: zkVM guest binaries not found and cargo-risczero not installed." echo " Install with: cargo install cargo-risczero && cargo risczero install" echo " Requires Docker running for cross-compilation." exit 1 fi echo " Building zkVM guest programs (first run may take several minutes)..." (cd "$LEZ_RLN_DIR" && cargo risczero build --manifest-path methods/guest/Cargo.toml 2>&1 | tail -10) || { echo " FATAL: guest program build failed. Is Docker running?" exit 1 } fi export NSSA_WALLET_HOME_DIR="$RLN_PROJECT_DIR/dev" export WALLET_CONFIG="$NSSA_WALLET_HOME_DIR/wallet_config.json" export WALLET_STORAGE="$NSSA_WALLET_HOME_DIR/storage.json" rm -f "$WALLET_CONFIG" "$WALLET_STORAGE" SETUP_OUTPUT=$(cd "$LEZ_RLN_DIR" && cargo run --bin run_setup 2>&1) || { echo " FATAL: run_setup failed:" echo "$SETUP_OUTPUT" exit 1 } echo "$SETUP_OUTPUT" | tail -5 TREE_MAIN_ACCOUNT=$(echo "$SETUP_OUTPUT" | grep "Tree main account:" | awk '{print $NF}') if [ -z "$TREE_MAIN_ACCOUNT" ]; then echo " FATAL: Could not parse tree main account from run_setup output" exit 1 fi echo " Programs deployed." echo " Tree main account: $TREE_MAIN_ACCOUNT" # ---------- Phase 3: Register 5 members & generate keystores ---------- echo "[3/7] Registering $NUM_NODES members and generating keystores..." WORK_DIR=$(mktemp -d) REGISTER_BIN="$LEZ_RLN_DIR/target/release/register_member" (cd "$LEZ_RLN_DIR" && cargo build --release --bin register_member 2>&1 | tail -3) if [ ! -f "$REGISTER_BIN" ]; then echo " FATAL: register_member not found at $REGISTER_BIN" exit 1 fi MANIFEST_FILE="$WORK_DIR/manifest.json" echo "[" > "$MANIFEST_FILE" LEAF_INDICES=() IDENTITY_SECRETS=() CONFIG_ACCOUNT="" for i in $(seq 0 $((NUM_NODES - 1))); do echo " Registering node $((i+1))/$NUM_NODES..." OUTPUT=$(cd "$LEZ_RLN_DIR" && "$REGISTER_BIN" 2>&1) || { echo " FATAL: register_member failed:" echo "$OUTPUT" exit 1 } CONFIG_ACCOUNT=$(echo "$OUTPUT" | grep "^CONFIG_ACCOUNT=" | cut -d= -f2) LEAF_INDEX=$(echo "$OUTPUT" | grep "^LEAF_INDEX=" | cut -d= -f2) IDENTITY_SECRET=$(echo "$OUTPUT" | grep "^IDENTITY_SECRET_HASH=" | cut -d= -f2) if [ -z "$CONFIG_ACCOUNT" ] || [ -z "$LEAF_INDEX" ] || [ -z "$IDENTITY_SECRET" ]; then echo " FATAL: Failed to parse register_member output:" echo "$OUTPUT" exit 1 fi LEAF_INDICES+=("$LEAF_INDEX") IDENTITY_SECRETS+=("$IDENTITY_SECRET") [ "$i" -gt 0 ] && echo "," >> "$MANIFEST_FILE" cat >> "$MANIFEST_FILE" <> "$MANIFEST_FILE" echo " Config account: $CONFIG_ACCOUNT" # Generate keystores echo " Generating keystores..." LIBRLN_FILE="$DELIVERY_DIR/librln_v0.9.0.a" if [ ! -f "$LIBRLN_FILE" ]; then echo " Building librln..." (cd "$DELIVERY_DIR" && make librln 2>&1 | tail -5) fi if [ ! -f "$LIBRLN_FILE" ]; then echo " FATAL: librln not found at $LIBRLN_FILE" exit 1 fi if [ ! -f "$DELIVERY_DIR/nimbus-build-system.paths" ]; then echo " Generating nim paths..." (cd "$DELIVERY_DIR" && make nimbus-build-system-paths 2>&1 | tail -3) fi NIM_PATH_ARGS=() while IFS= read -r line; do line="${line//\"/}" [[ -n "$line" ]] && NIM_PATH_ARGS+=("$line") done < "$DELIVERY_DIR/nimbus-build-system.paths" SETUP_KS_BIN="$WORK_DIR/setup_keystores" nim c -d:release --mm:refc \ "${NIM_PATH_ARGS[@]}" \ --passL:"$LIBRLN_FILE" --passL:"-lm" \ -o:"$SETUP_KS_BIN" \ "$SCRIPT_DIR/setup_keystores.nim" 2>&1 | tail -10 if [ ! -f "$SETUP_KS_BIN" ]; then echo " FATAL: Failed to compile setup_keystores.nim" exit 1 fi (cd "$WORK_DIR" && "$SETUP_KS_BIN" "$MANIFEST_FILE") || { echo " FATAL: setup_keystores failed" exit 1 } KEYSTORE_COUNT=$(ls -1 "$WORK_DIR"/rln_keystore_*.json 2>/dev/null | wc -l | tr -d ' ') echo " Keystores: $KEYSTORE_COUNT" # ---------- Phase 4: Build / check modules ---------- echo "[4/7] Building modules (if needed)..." LOGOSCORE="${LOGOSCORE:-$(nix build github:logos-co/logos-liblogos/7df6195 --override-input logos-cpp-sdk github:logos-co/logos-cpp-sdk/a4bd66c --no-link --print-out-paths)/bin/logoscore}" WALLET_MODULE_RESULT="$RLN_PROJECT_DIR/logos-rln-module/result-wallet" NEED_BUILD=0 [ -f "$RLN_PROJECT_DIR/logos-rln-module/result-rln/lib/liblogos_rln_module.$EXT" ] || NEED_BUILD=1 [ -f "$WALLET_MODULE_RESULT/lib/liblogos_execution_zone_wallet_module.$EXT" ] || NEED_BUILD=1 [ -f "$RLN_PROJECT_DIR/logos-delivery-module/result/lib/delivery_module_plugin.$EXT" ] || NEED_BUILD=1 if [ "$NEED_BUILD" -eq 1 ]; then echo " Some modules missing — running build_modules.sh..." bash "$RLN_PROJECT_DIR/build_modules.sh" || { echo " FATAL: Module build failed." exit 1 } fi [ -f "$RLN_PROJECT_DIR/logos-rln-module/result-rln/lib/liblogos_rln_module.$EXT" ] || { echo " FATAL: RLN module not found after build."; exit 1; } [ -f "$WALLET_MODULE_RESULT/lib/liblogos_execution_zone_wallet_module.$EXT" ] || { echo " FATAL: Wallet module not found after build."; exit 1; } [ -f "$RLN_PROJECT_DIR/logos-delivery-module/result/lib/delivery_module_plugin.$EXT" ] || { echo " FATAL: Delivery module not found after build."; exit 1; } echo " All modules present." # Build chat2mix if not present CHAT2MIX="$DELIVERY_DIR/build/chat2mix" if [ ! -f "$CHAT2MIX" ] || [ "${REBUILD_NIM:-0}" = "1" ]; then echo " Building chat2mix..." (cd "$DELIVERY_DIR" && make chat2mix 2>&1 | tail -5) || { echo " WARNING: chat2mix build failed — chat clients won't be available" } else echo " chat2mix already built." fi # ---------- Phase 5: Stage modules ---------- echo "[5/7] Staging modules for $NUM_NODES instances..." stage_modules() { local mdir mdir=$(mktemp -d) mkdir -p "$mdir/liblogos_execution_zone_wallet_module" cp -L "$WALLET_MODULE_RESULT/lib/liblogos_execution_zone_wallet_module.$EXT" "$mdir/liblogos_execution_zone_wallet_module/" [ -f "$WALLET_MODULE_RESULT/lib/libwallet_ffi.$EXT" ] && \ cp -L "$WALLET_MODULE_RESULT/lib/libwallet_ffi.$EXT" "$mdir/liblogos_execution_zone_wallet_module/" echo "{\"name\":\"liblogos_execution_zone_wallet_module\",\"version\":\"1.0.0\",\"type\":\"core\",\"main\":{\"$PLATFORM\":\"liblogos_execution_zone_wallet_module.$EXT\"},\"dependencies\":[],\"capabilities\":[]}" > "$mdir/liblogos_execution_zone_wallet_module/manifest.json" mkdir -p "$mdir/liblogos_rln_module" cp -L "$RLN_PROJECT_DIR/logos-rln-module/result-rln/lib/liblogos_rln_module.$EXT" "$mdir/liblogos_rln_module/" cp -L "$RLN_PROJECT_DIR/logos-rln-module/result-rln/lib/liblez_rln_ffi.$EXT" "$mdir/liblogos_rln_module/" 2>/dev/null || true echo "{\"name\":\"liblogos_rln_module\",\"version\":\"1.0.0\",\"type\":\"core\",\"main\":{\"$PLATFORM\":\"liblogos_rln_module.$EXT\"},\"dependencies\":[\"liblogos_execution_zone_wallet_module\"],\"capabilities\":[]}" > "$mdir/liblogos_rln_module/manifest.json" mkdir -p "$mdir/delivery_module" cp -L "$RLN_PROJECT_DIR/logos-delivery-module/result/lib/delivery_module_plugin.$EXT" "$mdir/delivery_module/" [ -f "$RLN_PROJECT_DIR/logos-delivery-module/result/lib/liblogosdelivery.$EXT" ] && \ cp -L "$RLN_PROJECT_DIR/logos-delivery-module/result/lib/liblogosdelivery.$EXT" "$mdir/delivery_module/" for pq in "$RLN_PROJECT_DIR"/logos-delivery-module/result/lib/libpq*; do [ -f "$pq" ] && cp -L "$pq" "$mdir/delivery_module/" done echo "{\"name\":\"delivery_module\",\"version\":\"1.0.0\",\"type\":\"core\",\"main\":{\"$PLATFORM\":\"delivery_module_plugin.$EXT\"},\"dependencies\":[],\"capabilities\":[]}" > "$mdir/delivery_module/manifest.json" echo "$mdir" } for i in $(seq 0 $((NUM_NODES - 1))); do MDIR=$(stage_modules) MODULES_DIRS+=("$MDIR") echo " Node $i modules: $MDIR" done LOAD_ORDER="liblogos_execution_zone_wallet_module,liblogos_rln_module,delivery_module" WALLET_CALL="liblogos_execution_zone_wallet_module.open($WALLET_CONFIG,$WALLET_STORAGE)" # ---------- Phase 6: Start 5 logoscore instances ---------- echo "[6/7] Starting $NUM_NODES logoscore instances..." # Write node configs and start instances for i in $(seq 0 $((NUM_NODES - 1))); do TCP_PORT=$((BASE_TCP_PORT + i)) DISC_PORT=$((BASE_DISC_PORT + i)) LEAF_INDEX="${LEAF_INDICES[$i]}" NODE_CONFIG="$WORK_DIR/node${i}_config.json" LOG_FILE="$WORK_DIR/node${i}.log" # Bootstrap: node 0 has no entry nodes, others bootstrap to node 0 if [ "$i" -eq 0 ]; then ENTRY_NODES="[]" else ENTRY_NODES="[\"/ip4/127.0.0.1/tcp/$BASE_TCP_PORT/p2p/${PEER_IDS[0]}\"]" fi # Build mixNodes array: all OTHER nodes' multiaddr:mixPubKey MIX_NODES_JSON="" for j in $(seq 0 $((NUM_NODES - 1))); do [ "$j" -eq "$i" ] && continue J_PORT=$((BASE_TCP_PORT + j)) [ -n "$MIX_NODES_JSON" ] && MIX_NODES_JSON="$MIX_NODES_JSON, " MIX_NODES_JSON="$MIX_NODES_JSON\"/ip4/127.0.0.1/tcp/$J_PORT/p2p/${PEER_IDS[$j]}:${MIX_PUBKEYS[$j]}\"" done cat > "$NODE_CONFIG" <"$LOG_FILE" 2>&1) & INSTANCE_PIDS+=($!) echo " Node $i PID: ${INSTANCE_PIDS[$i]}" # Wait for all 7 -c calls to succeed echo " Waiting for node $i to initialize..." for j in $(seq 1 90); do N=$(grep -c '^Method call successful' "$LOG_FILE" 2>/dev/null || true); N=${N:-0} [ "$N" -ge 7 ] && break if ! kill -0 "${INSTANCE_PIDS[$i]}" 2>/dev/null; then N=$(grep -c '^Method call successful' "$LOG_FILE" 2>/dev/null || true); N=${N:-0} echo " ERROR: Node $i exited after $N/7 calls. Method call lines:" grep 'Method call' "$LOG_FILE" echo " --- Last 15 log lines ---" tail -15 "$LOG_FILE" exit 1 fi sleep 1 done if [ "$N" -lt 7 ]; then echo " ERROR: Node $i did not initialize ($N/7 calls). Log:" grep 'Method call\|Error' "$LOG_FILE" | tail -10 exit 1 fi echo " Node $i ready ($N/7 calls)." # Pause between nodes to avoid resource contention sleep 3 done # Wait for peer discovery across all nodes echo " Waiting for peer discovery (15s)..." sleep 15 # ---------- Phase 7: Ready ---------- echo "" echo "[7/7] Simulation running!" echo "" echo " Sequencer: PID $SEQUENCER_PID (port 3040)" echo " Config: $CONFIG_ACCOUNT" echo " Logs: $WORK_DIR/node*.log" echo "" for i in $(seq 0 $((NUM_NODES - 1))); do TCP_PORT=$((BASE_TCP_PORT + i)) echo " Node $i: PID ${INSTANCE_PIDS[$i]}, port $TCP_PORT, leaf ${LEAF_INDICES[$i]}" done echo "" echo " To inspect logs:" for i in $(seq 0 $((NUM_NODES - 1))); do echo " grep 'Method call' $WORK_DIR/node${i}.log" done echo "" echo " Now start chat clients in separate terminals:" echo " cd $(pwd)" echo " bash run_chat_mix.sh" echo " bash run_chat_mix1.sh" echo "" echo " Press Ctrl+C to stop everything." wait