From 565eddd55f10e484b52ce1648448d1565b3d3aec Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Fri, 6 Feb 2026 14:56:05 +0000 Subject: [PATCH 1/6] feat: add isAggregator flag to validator configuration Add support for configuring nodes as aggregators through validator-config.yaml. This allows selective designation of nodes to perform aggregation duties by setting isAggregator: true in the validator configuration. Changes: - Add isAggregator field (default: false) to all validators in both local and ansible configs - Update parse-vc.sh to extract and export isAggregator flag - Modify all client command scripts to pass --is-aggregator flag when enabled - Add isAggregator status to node information output --- ansible-devnet/genesis/validator-config.yaml | 7 +++++++ client-cmds/ethlambda-cmd.sh | 12 ++++++++++-- client-cmds/grandine-cmd.sh | 12 ++++++++++-- client-cmds/lantern-cmd.sh | 12 ++++++++++-- client-cmds/lighthouse-cmd.sh | 12 ++++++++++-- client-cmds/qlean-cmd.sh | 9 +++++++++ client-cmds/ream-cmd.sh | 12 ++++++++++-- client-cmds/zeam-cmd.sh | 12 ++++++++++-- local-devnet/genesis/validator-config.yaml | 7 +++++++ parse-vc.sh | 8 ++++++++ 10 files changed, 91 insertions(+), 12 deletions(-) diff --git a/ansible-devnet/genesis/validator-config.yaml b/ansible-devnet/genesis/validator-config.yaml index 81e80f5..091275a 100644 --- a/ansible-devnet/genesis/validator-config.yaml +++ b/ansible-devnet/genesis/validator-config.yaml @@ -14,6 +14,7 @@ validators: ip: "46.224.123.223" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 # number of indices for this node - name: "ream_0" @@ -25,6 +26,7 @@ validators: ip: "77.42.27.219" quic: 9001 metricsPort: 9095 + isAggregator: false devnet: 1 count: 1 @@ -36,6 +38,7 @@ validators: ip: "46.224.123.220" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 - name: "lantern_0" @@ -47,6 +50,7 @@ validators: ip: "46.224.135.177" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 - name: "lighthouse_0" @@ -58,6 +62,7 @@ validators: ip: "46.224.135.169" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 - name: "grandine_0" @@ -66,6 +71,7 @@ validators: ip: "37.27.250.20" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 - name: "ethlambda_0" @@ -74,4 +80,5 @@ validators: ip: "78.47.44.215" quic: 9001 metricsPort: 9095 + isAggregator: false count: 1 \ No newline at end of file diff --git a/client-cmds/ethlambda-cmd.sh b/client-cmds/ethlambda-cmd.sh index 50bd63c..a7967f2 100644 --- a/client-cmds/ethlambda-cmd.sh +++ b/client-cmds/ethlambda-cmd.sh @@ -4,6 +4,12 @@ binary_path="$scriptDir/../ethlambda/target/release/ethlambda" +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + # Command when running as binary node_binary="$binary_path \ --custom-network-config-dir $configDir \ @@ -11,7 +17,8 @@ node_binary="$binary_path \ --node-id $item \ --node-key $configDir/$item.key \ --metrics-address 0.0.0.0 \ - --metrics-port $metricsPort" + --metrics-port $metricsPort \ + $aggregator_flag" # Command when running as docker container node_docker="ghcr.io/lambdaclass/ethlambda:devnet2 \ @@ -20,6 +27,7 @@ node_docker="ghcr.io/lambdaclass/ethlambda:devnet2 \ --node-id $item \ --node-key /config/$item.key \ --metrics-address 0.0.0.0 \ - --metrics-port $metricsPort" + --metrics-port $metricsPort \ + $aggregator_flag" node_setup="docker" diff --git a/client-cmds/grandine-cmd.sh b/client-cmds/grandine-cmd.sh index cd92472..63c9de3 100644 --- a/client-cmds/grandine-cmd.sh +++ b/client-cmds/grandine-cmd.sh @@ -1,5 +1,11 @@ #!/bin/bash +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + node_binary="$grandine_bin \ --genesis $configDir/config.yaml \ --validator-registry-path $configDir/validators.yaml \ @@ -11,7 +17,8 @@ node_binary="$grandine_bin \ --metrics \ --http-address 0.0.0.0 \ --http-port $metricsPort \ - --hash-sig-key-dir $configDir/hash-sig-keys" + --hash-sig-key-dir $configDir/hash-sig-keys \ + $aggregator_flag" node_docker="sifrai/lean:devnet-2 \ --genesis /config/config.yaml \ @@ -24,7 +31,8 @@ node_docker="sifrai/lean:devnet-2 \ --metrics \ --http-address 0.0.0.0 \ --http-port $metricsPort \ - --hash-sig-key-dir /config/hash-sig-keys" + --hash-sig-key-dir /config/hash-sig-keys \ + $aggregator_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/lantern-cmd.sh b/client-cmds/lantern-cmd.sh index b918355..2a2940f 100755 --- a/client-cmds/lantern-cmd.sh +++ b/client-cmds/lantern-cmd.sh @@ -8,6 +8,12 @@ if [ -n "$devnet" ]; then devnet_flag="--devnet $devnet" fi +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + # Lantern's repo: https://github.com/Pier-Two/lantern node_binary="$scriptDir/lantern/build/lantern_cli \ --data-dir $dataDir/$item \ @@ -22,7 +28,8 @@ node_binary="$scriptDir/lantern/build/lantern_cli \ --metrics-port $metricsPort \ --http-port 5055 \ --log-level debug \ - --hash-sig-key-dir $configDir/hash-sig-keys" + --hash-sig-key-dir $configDir/hash-sig-keys \ + $aggregator_flag" node_docker="$LANTERN_IMAGE --data-dir /data \ --genesis-config /config/config.yaml \ @@ -36,7 +43,8 @@ node_docker="$LANTERN_IMAGE --data-dir /data \ --metrics-port $metricsPort \ --http-port 5055 \ --log-level debug \ - --hash-sig-key-dir /config/hash-sig-keys" + --hash-sig-key-dir /config/hash-sig-keys \ + $aggregator_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/lighthouse-cmd.sh b/client-cmds/lighthouse-cmd.sh index 1e129c2..219b0e1 100644 --- a/client-cmds/lighthouse-cmd.sh +++ b/client-cmds/lighthouse-cmd.sh @@ -3,6 +3,12 @@ # Metrics enabled by default metrics_flag="--metrics" +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + node_binary="$lighthouse_bin lean_node \ --datadir \"$dataDir/$item\" \ --config \"$configDir/config.yaml\" \ @@ -14,7 +20,8 @@ node_binary="$lighthouse_bin lean_node \ --socket-port $quicPort\ $metrics_flag \ --metrics-address 0.0.0.0 \ - --metrics-port $metricsPort" + --metrics-port $metricsPort \ + $aggregator_flag" node_docker="hopinheimer/lighthouse:latest lighthouse lean_node \ --datadir /data \ @@ -27,6 +34,7 @@ node_docker="hopinheimer/lighthouse:latest lighthouse lean_node \ --socket-port $quicPort\ $metrics_flag \ --metrics-address 0.0.0.0 \ - --metrics-port $metricsPort" + --metrics-port $metricsPort \ + $aggregator_flag" node_setup="docker" diff --git a/client-cmds/qlean-cmd.sh b/client-cmds/qlean-cmd.sh index 28de40b..3dd34c3 100644 --- a/client-cmds/qlean-cmd.sh +++ b/client-cmds/qlean-cmd.sh @@ -3,6 +3,13 @@ #-----------------------qlean setup---------------------- # expects "qlean" submodule or symlink inside "lean-quickstart" root directory # https://github.com/qdrvm/qlean-mini + +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + node_binary="$scriptDir/qlean/build/src/executable/qlean \ --modules-dir $scriptDir/qlean/build/src/modules \ --genesis $configDir/config.yaml \ @@ -15,6 +22,7 @@ node_binary="$scriptDir/qlean/build/src/executable/qlean \ --node-id $item --node-key $configDir/$privKeyPath \ --listen-addr /ip4/0.0.0.0/udp/$quicPort/quic-v1 \ --prometheus-port $metricsPort \ + $aggregator_flag \ -ldebug \ -ltrace" @@ -29,6 +37,7 @@ node_docker="qdrvm/qlean-mini:devnet-2 \ --node-id $item --node-key /config/$privKeyPath \ --listen-addr /ip4/0.0.0.0/udp/$quicPort/quic-v1 \ --prometheus-port $metricsPort \ + $aggregator_flag \ -ldebug \ -ltrace" diff --git a/client-cmds/ream-cmd.sh b/client-cmds/ream-cmd.sh index 9985c92..04bd8ec 100755 --- a/client-cmds/ream-cmd.sh +++ b/client-cmds/ream-cmd.sh @@ -4,6 +4,12 @@ # Metrics enabled by default metrics_flag="--metrics" +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + # modify the path to the ream binary as per your system node_binary="$scriptDir/../ream/target/release/ream --data-dir $dataDir/$item \ lean_node \ @@ -15,7 +21,8 @@ node_binary="$scriptDir/../ream/target/release/ream --data-dir $dataDir/$item \ $metrics_flag \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - --http-address 0.0.0.0" + --http-address 0.0.0.0 \ + $aggregator_flag" node_docker="ghcr.io/reamlabs/ream:latest-devnet2 --data-dir /data \ lean_node \ @@ -27,7 +34,8 @@ node_docker="ghcr.io/reamlabs/ream:latest-devnet2 --data-dir /data \ $metrics_flag \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - --http-address 0.0.0.0" + --http-address 0.0.0.0 \ + $aggregator_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/zeam-cmd.sh b/client-cmds/zeam-cmd.sh index 4b56d7a..e9fd36d 100644 --- a/client-cmds/zeam-cmd.sh +++ b/client-cmds/zeam-cmd.sh @@ -6,13 +6,20 @@ # Metrics enabled by default metrics_flag="--metrics_enable" +# Set aggregator flag based on isAggregator value +aggregator_flag="" +if [ "$isAggregator" == "true" ]; then + aggregator_flag="--is-aggregator" +fi + node_binary="$scriptDir/../zig-out/bin/zeam node \ --custom_genesis $configDir \ --validator_config $validatorConfig \ --data-dir $dataDir/$item \ --node-id $item --node-key $configDir/$item.key \ $metrics_flag \ - --api-port $metricsPort" + --api-port $metricsPort \ + $aggregator_flag" node_docker="--security-opt seccomp=unconfined blockblaz/zeam:devnet2 node \ --custom_genesis /config \ @@ -20,7 +27,8 @@ node_docker="--security-opt seccomp=unconfined blockblaz/zeam:devnet2 node \ --data-dir /data \ --node-id $item --node-key /config/$item.key \ $metrics_flag \ - --api-port $metricsPort" + --api-port $metricsPort \ + $aggregator_flag" # choose either binary or docker node_setup="docker" \ No newline at end of file diff --git a/local-devnet/genesis/validator-config.yaml b/local-devnet/genesis/validator-config.yaml index 7f99d48..72d3ad3 100644 --- a/local-devnet/genesis/validator-config.yaml +++ b/local-devnet/genesis/validator-config.yaml @@ -14,6 +14,7 @@ validators: ip: "127.0.0.1" quic: 9001 metricsPort: 8081 + isAggregator: false count: 1 # number of indices for this node - name: "ream_0" @@ -25,6 +26,7 @@ validators: ip: "127.0.0.1" quic: 9002 metricsPort: 8082 + isAggregator: false devnet: 1 count: 1 @@ -36,6 +38,7 @@ validators: ip: "127.0.0.1" quic: 9003 metricsPort: 8083 + isAggregator: false count: 1 - name: "lantern_0" @@ -47,6 +50,7 @@ validators: ip: "127.0.0.1" quic: 9004 metricsPort: 8084 + isAggregator: false count: 1 - name: "lighthouse_0" @@ -58,6 +62,7 @@ validators: ip: "127.0.0.1" quic: 9005 metricsPort: 8085 + isAggregator: false count: 1 - name: "grandine_0" @@ -66,6 +71,7 @@ validators: ip: "127.0.0.1" quic: 9006 metricsPort: 8086 + isAggregator: false count: 1 - name: "ethlambda_0" @@ -77,4 +83,5 @@ validators: ip: "127.0.0.1" quic: 9007 metricsPort: 8087 + isAggregator: false count: 1 diff --git a/parse-vc.sh b/parse-vc.sh index 8b50e82..629f4e1 100644 --- a/parse-vc.sh +++ b/parse-vc.sh @@ -51,6 +51,12 @@ if [ -z "$devnet" ] || [ "$devnet" == "null" ]; then devnet="" fi +# Automatically extract isAggregator flag using yq (defaults to false if not set) +isAggregator=$(yq eval ".validators[] | select(.name == \"$item\") | .isAggregator // false" "$validator_config_file") +if [ -z "$isAggregator" ] || [ "$isAggregator" == "null" ]; then + isAggregator="false" +fi + # Automatically extract private key using yq privKey=$(yq eval ".validators[] | select(.name == \"$item\") | .privkey" "$validator_config_file") @@ -99,10 +105,12 @@ if [ "$keyType" == "hash-sig" ] && [ "$hashSigKeyIndex" != "null" ] && [ -n "$ha echo "Hash-Sig Key Index: $hashSigKeyIndex" echo "Hash-Sig Public Key: $hashSigPkPath" echo "Hash-Sig Secret Key: $hashSigSkPath" + echo "Is Aggregator: $isAggregator" else echo "Node: $item" echo "QUIC Port: $quicPort" echo "Metrics Port: $metricsPort" echo "Devnet: ${devnet:-}" echo "Private Key File: $privKeyPath" + echo "Is Aggregator: $isAggregator" fi From fd6c6d6871354585da79e3f8dc2229f32bea2578 Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Tue, 17 Feb 2026 16:45:01 +0000 Subject: [PATCH 2/6] Add checkpoint-sync-url and restart-client flags for all clients --- README.md | 16 ++++++++++++ client-cmds/ethlambda-cmd.sh | 12 +++++++-- client-cmds/grandine-cmd.sh | 12 +++++++-- client-cmds/lantern-cmd.sh | 12 +++++++-- client-cmds/lighthouse-cmd.sh | 12 +++++++-- client-cmds/qlean-cmd.sh | 8 ++++++ client-cmds/ream-cmd.sh | 12 +++++++-- client-cmds/zeam-cmd.sh | 12 +++++++-- parse-env.sh | 23 ++++++++++++++--- spin-node.sh | 48 +++++++++++++++++++++++++++++++++-- 10 files changed, 150 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 118a821..bcfedf0 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,22 @@ Grafana is started with the two pre-provisioned dashboards from [leanMetrics](ht - On Ctrl+C cleanup, the metrics stack is stopped automatically Note: Client metrics endpoints are always enabled regardless of this flag. +12. `--checkpoint-sync-url` specifies the URL to fetch finalized checkpoint state from for checkpoint sync. Default: `https://leanpoint.leanroadmap.org`. Only used when `--restart-client` is specified. +13. `--restart-client` comma-separated list of client node names (e.g., `zeam_0,ream_0`). When specified together with `--checkpoint-sync-url`, those clients are stopped, their data cleared, and restarted using checkpoint sync from the given URL. Genesis is skipped. All clients accept `--checkpoint-sync-url`; parameter names may vary per client implementation. + +### Restart clients with checkpoint sync + +Restart specific clients by syncing from a remote checkpoint (e.g., leanpoint mainnet): + +```sh +# Restart zeam_0 using default checkpoint URL (https://leanpoint.leanroadmap.org) +NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0 + +# Restart multiple zeam nodes with custom checkpoint URL +NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0,zeam_1 --checkpoint-sync-url https://leanpoint.leanroadmap.org/lean/states/finalized +``` + +> **Note:** All clients accept `--checkpoint-sync-url`. Client implementations may use different parameter names internally; update client-cmd scripts if parameters change. ### Clients supported diff --git a/client-cmds/ethlambda-cmd.sh b/client-cmds/ethlambda-cmd.sh index a7967f2..a71ac71 100644 --- a/client-cmds/ethlambda-cmd.sh +++ b/client-cmds/ethlambda-cmd.sh @@ -10,6 +10,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + # Command when running as binary node_binary="$binary_path \ --custom-network-config-dir $configDir \ @@ -18,7 +24,8 @@ node_binary="$binary_path \ --node-key $configDir/$item.key \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" # Command when running as docker container node_docker="ghcr.io/lambdaclass/ethlambda:devnet2 \ @@ -28,6 +35,7 @@ node_docker="ghcr.io/lambdaclass/ethlambda:devnet2 \ --node-key /config/$item.key \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_setup="docker" diff --git a/client-cmds/grandine-cmd.sh b/client-cmds/grandine-cmd.sh index 63c9de3..d1d550a 100644 --- a/client-cmds/grandine-cmd.sh +++ b/client-cmds/grandine-cmd.sh @@ -6,6 +6,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + node_binary="$grandine_bin \ --genesis $configDir/config.yaml \ --validator-registry-path $configDir/validators.yaml \ @@ -18,7 +24,8 @@ node_binary="$grandine_bin \ --http-address 0.0.0.0 \ --http-port $metricsPort \ --hash-sig-key-dir $configDir/hash-sig-keys \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_docker="sifrai/lean:devnet-2 \ --genesis /config/config.yaml \ @@ -32,7 +39,8 @@ node_docker="sifrai/lean:devnet-2 \ --http-address 0.0.0.0 \ --http-port $metricsPort \ --hash-sig-key-dir /config/hash-sig-keys \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/lantern-cmd.sh b/client-cmds/lantern-cmd.sh index 2a2940f..7d22960 100755 --- a/client-cmds/lantern-cmd.sh +++ b/client-cmds/lantern-cmd.sh @@ -14,6 +14,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + # Lantern's repo: https://github.com/Pier-Two/lantern node_binary="$scriptDir/lantern/build/lantern_cli \ --data-dir $dataDir/$item \ @@ -29,7 +35,8 @@ node_binary="$scriptDir/lantern/build/lantern_cli \ --http-port 5055 \ --log-level debug \ --hash-sig-key-dir $configDir/hash-sig-keys \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_docker="$LANTERN_IMAGE --data-dir /data \ --genesis-config /config/config.yaml \ @@ -44,7 +51,8 @@ node_docker="$LANTERN_IMAGE --data-dir /data \ --http-port 5055 \ --log-level debug \ --hash-sig-key-dir /config/hash-sig-keys \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/lighthouse-cmd.sh b/client-cmds/lighthouse-cmd.sh index 219b0e1..030b79c 100644 --- a/client-cmds/lighthouse-cmd.sh +++ b/client-cmds/lighthouse-cmd.sh @@ -9,6 +9,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + node_binary="$lighthouse_bin lean_node \ --datadir \"$dataDir/$item\" \ --config \"$configDir/config.yaml\" \ @@ -21,7 +27,8 @@ node_binary="$lighthouse_bin lean_node \ $metrics_flag \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_docker="hopinheimer/lighthouse:latest lighthouse lean_node \ --datadir /data \ @@ -35,6 +42,7 @@ node_docker="hopinheimer/lighthouse:latest lighthouse lean_node \ $metrics_flag \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_setup="docker" diff --git a/client-cmds/qlean-cmd.sh b/client-cmds/qlean-cmd.sh index 3dd34c3..0df9e18 100644 --- a/client-cmds/qlean-cmd.sh +++ b/client-cmds/qlean-cmd.sh @@ -10,6 +10,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + node_binary="$scriptDir/qlean/build/src/executable/qlean \ --modules-dir $scriptDir/qlean/build/src/modules \ --genesis $configDir/config.yaml \ @@ -23,6 +29,7 @@ node_binary="$scriptDir/qlean/build/src/executable/qlean \ --listen-addr /ip4/0.0.0.0/udp/$quicPort/quic-v1 \ --prometheus-port $metricsPort \ $aggregator_flag \ + $checkpoint_sync_flag \ -ldebug \ -ltrace" @@ -38,6 +45,7 @@ node_docker="qdrvm/qlean-mini:devnet-2 \ --listen-addr /ip4/0.0.0.0/udp/$quicPort/quic-v1 \ --prometheus-port $metricsPort \ $aggregator_flag \ + $checkpoint_sync_flag \ -ldebug \ -ltrace" diff --git a/client-cmds/ream-cmd.sh b/client-cmds/ream-cmd.sh index 04bd8ec..3585f59 100755 --- a/client-cmds/ream-cmd.sh +++ b/client-cmds/ream-cmd.sh @@ -10,6 +10,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + # modify the path to the ream binary as per your system node_binary="$scriptDir/../ream/target/release/ream --data-dir $dataDir/$item \ lean_node \ @@ -22,7 +28,8 @@ node_binary="$scriptDir/../ream/target/release/ream --data-dir $dataDir/$item \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ --http-address 0.0.0.0 \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_docker="ghcr.io/reamlabs/ream:latest-devnet2 --data-dir /data \ lean_node \ @@ -35,7 +42,8 @@ node_docker="ghcr.io/reamlabs/ream:latest-devnet2 --data-dir /data \ --metrics-address 0.0.0.0 \ --metrics-port $metricsPort \ --http-address 0.0.0.0 \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" # choose either binary or docker node_setup="docker" diff --git a/client-cmds/zeam-cmd.sh b/client-cmds/zeam-cmd.sh index e9fd36d..6f693d4 100644 --- a/client-cmds/zeam-cmd.sh +++ b/client-cmds/zeam-cmd.sh @@ -12,6 +12,12 @@ if [ "$isAggregator" == "true" ]; then aggregator_flag="--is-aggregator" fi +# Set checkpoint sync URL when restarting with checkpoint sync +checkpoint_sync_flag="" +if [ -n "${checkpoint_sync_url:-}" ]; then + checkpoint_sync_flag="--checkpoint-sync-url $checkpoint_sync_url" +fi + node_binary="$scriptDir/../zig-out/bin/zeam node \ --custom_genesis $configDir \ --validator_config $validatorConfig \ @@ -19,7 +25,8 @@ node_binary="$scriptDir/../zig-out/bin/zeam node \ --node-id $item --node-key $configDir/$item.key \ $metrics_flag \ --api-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" node_docker="--security-opt seccomp=unconfined blockblaz/zeam:devnet2 node \ --custom_genesis /config \ @@ -28,7 +35,8 @@ node_docker="--security-opt seccomp=unconfined blockblaz/zeam:devnet2 node \ --node-id $item --node-key /config/$item.key \ $metrics_flag \ --api-port $metricsPort \ - $aggregator_flag" + $aggregator_flag \ + $checkpoint_sync_flag" # choose either binary or docker node_setup="docker" \ No newline at end of file diff --git a/parse-env.sh b/parse-env.sh index 84db521..224772f 100755 --- a/parse-env.sh +++ b/parse-env.sh @@ -80,6 +80,16 @@ while [[ $# -gt 0 ]]; do stopNodes=true shift ;; + --checkpoint-sync-url) + checkpointSyncUrl="$2" + shift + shift + ;; + --restart-client) + restartClient="$2" + shift + shift + ;; --coreDumps) coreDumps="$2" shift # past argument @@ -91,13 +101,18 @@ while [[ $# -gt 0 ]]; do esac done -# if no node has been assigned assume all nodes to be started -if [[ ! -n "$node" ]]; +# if no node and no restart-client specified, exit +if [[ ! -n "$node" ]] && [[ ! -n "$restartClient" ]]; then - echo "no node specified, exiting..." + echo "no node or restart-client specified, exiting..." exit fi; +# When using --restart-client with checkpoint sync, set default checkpoint URL if not provided +if [[ -n "$restartClient" ]] && [[ ! -n "$checkpointSyncUrl" ]]; then + checkpointSyncUrl="https://leanpoint.leanroadmap.org" +fi; + if [ ! -n "$validatorConfig" ] then echo "no external validator config provided, assuming genesis bootnode" @@ -116,3 +131,5 @@ echo "popupTerminal = $popupTerminal" echo "dockerTag = ${dockerTag:-latest}" echo "enableMetrics = $enableMetrics" echo "coreDumps = ${coreDumps:-disabled}" +echo "checkpointSyncUrl = ${checkpointSyncUrl:-}" +echo "restartClient = ${restartClient:-}" diff --git a/spin-node.sh b/spin-node.sh index fd61447..e34f249 100755 --- a/spin-node.sh +++ b/spin-node.sh @@ -97,9 +97,36 @@ fi echo "Detected nodes: ${nodes[@]}" # nodes=("zeam_0" "ream_0" "qlean_0") spin_nodes=() - +restart_with_checkpoint_sync=false + +# When --restart-client is specified, use it as the node list and enable checkpoint sync mode +if [[ -n "$restartClient" ]]; then + restart_with_checkpoint_sync=true + # Skip genesis when restarting with checkpoint sync (we're syncing from remote) + generateGenesis=false + # Parse comma-separated client names + IFS=',' read -r -a requested_nodes <<< "$restartClient" + for requested_node in "${requested_nodes[@]}"; do + requested_node=$(echo "$requested_node" | xargs) # trim whitespace + node_found=false + for available_node in "${nodes[@]}"; do + if [[ "$requested_node" == "$available_node" ]]; then + spin_nodes+=("$available_node") + node_found=true + break + fi + done + if [[ "$node_found" == false ]]; then + echo "Error: Node '$requested_node' not found in validator config" + echo "Available nodes: ${nodes[@]}" + exit 1 + fi + done + echo "Restarting with checkpoint sync: ${spin_nodes[*]} from $checkpointSyncUrl" + cleanData=true # Clear data when restarting with checkpoint sync + node_present=true # Parse comma-separated or space-separated node names or handle single node/all -if [[ "$node" == "all" ]]; then +elif [[ "$node" == "all" ]]; then # Spin all nodes spin_nodes=("${nodes[@]}") node_present=true @@ -262,6 +289,16 @@ for item in "${spin_nodes[@]}"; do printf '%*s' $(tput cols) | tr ' ' '-' echo + # When restarting with checkpoint sync, stop existing container first + if [[ "$restart_with_checkpoint_sync" == "true" ]]; then + echo "Stopping existing container $item..." + if [ -n "$dockerWithSudo" ]; then + sudo docker rm -f "$item" 2>/dev/null || true + else + docker rm -f "$item" 2>/dev/null || true + fi + fi + # create and/or cleanup datadirs itemDataDir="$dataDir/$item" mkdir -p $itemDataDir @@ -277,6 +314,13 @@ for item in "${spin_nodes[@]}"; do # parse validator-config.yaml for $item to load args values source parse-vc.sh + # export checkpoint_sync_url for client-cmd scripts when restarting with checkpoint sync + if [[ "$restart_with_checkpoint_sync" == "true" ]] && [[ -n "$checkpointSyncUrl" ]]; then + export checkpoint_sync_url="$checkpointSyncUrl" + else + unset checkpoint_sync_url 2>/dev/null || true + fi + # extract client config IFS='_' read -r -a elements <<< "$item" client="${elements[0]}" From 4266f6600ce8e7deec219b653d4be6b5981b898f Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Tue, 17 Feb 2026 16:58:47 +0000 Subject: [PATCH 3/6] Add log clarifying --restart-client requires --checkpoint-sync-url --- spin-node.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/spin-node.sh b/spin-node.sh index e34f249..415bfbe 100755 --- a/spin-node.sh +++ b/spin-node.sh @@ -101,6 +101,7 @@ restart_with_checkpoint_sync=false # When --restart-client is specified, use it as the node list and enable checkpoint sync mode if [[ -n "$restartClient" ]]; then + echo "Note: --restart-client is only used with --checkpoint-sync-url (default: https://leanpoint.leanroadmap.org)" restart_with_checkpoint_sync=true # Skip genesis when restarting with checkpoint sync (we're syncing from remote) generateGenesis=false From 66a72464cbfd230f817375334340a45e04255f9a Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Tue, 17 Feb 2026 17:14:49 +0000 Subject: [PATCH 4/6] Update default checkpoint-sync-url to include /lean/v0/states/finalized path --- README.md | 8 ++++---- parse-env.sh | 2 +- spin-node.sh | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bcfedf0..1f3c2d5 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Grafana is started with the two pre-provisioned dashboards from [leanMetrics](ht - On Ctrl+C cleanup, the metrics stack is stopped automatically Note: Client metrics endpoints are always enabled regardless of this flag. -12. `--checkpoint-sync-url` specifies the URL to fetch finalized checkpoint state from for checkpoint sync. Default: `https://leanpoint.leanroadmap.org`. Only used when `--restart-client` is specified. +12. `--checkpoint-sync-url` specifies the URL to fetch finalized checkpoint state from for checkpoint sync. Default: `https://leanpoint.leanroadmap.org/lean/v0/states/finalized`. Only used when `--restart-client` is specified. 13. `--restart-client` comma-separated list of client node names (e.g., `zeam_0,ream_0`). When specified together with `--checkpoint-sync-url`, those clients are stopped, their data cleared, and restarted using checkpoint sync from the given URL. Genesis is skipped. All clients accept `--checkpoint-sync-url`; parameter names may vary per client implementation. ### Restart clients with checkpoint sync @@ -147,11 +147,11 @@ Grafana is started with the two pre-provisioned dashboards from [leanMetrics](ht Restart specific clients by syncing from a remote checkpoint (e.g., leanpoint mainnet): ```sh -# Restart zeam_0 using default checkpoint URL (https://leanpoint.leanroadmap.org) +# Restart zeam_0 using default checkpoint URL (https://leanpoint.leanroadmap.org/lean/v0/states/finalized) NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0 -# Restart multiple zeam nodes with custom checkpoint URL -NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0,zeam_1 --checkpoint-sync-url https://leanpoint.leanroadmap.org/lean/states/finalized +# Restart multiple clients with custom checkpoint URL +NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0,ream_0 --checkpoint-sync-url https://leanpoint.leanroadmap.org/lean/v0/states/finalized ``` > **Note:** All clients accept `--checkpoint-sync-url`. Client implementations may use different parameter names internally; update client-cmd scripts if parameters change. diff --git a/parse-env.sh b/parse-env.sh index 224772f..c3b1028 100755 --- a/parse-env.sh +++ b/parse-env.sh @@ -110,7 +110,7 @@ fi; # When using --restart-client with checkpoint sync, set default checkpoint URL if not provided if [[ -n "$restartClient" ]] && [[ ! -n "$checkpointSyncUrl" ]]; then - checkpointSyncUrl="https://leanpoint.leanroadmap.org" + checkpointSyncUrl="https://leanpoint.leanroadmap.org/lean/v0/states/finalized" fi; if [ ! -n "$validatorConfig" ] diff --git a/spin-node.sh b/spin-node.sh index 415bfbe..73c070a 100755 --- a/spin-node.sh +++ b/spin-node.sh @@ -101,7 +101,7 @@ restart_with_checkpoint_sync=false # When --restart-client is specified, use it as the node list and enable checkpoint sync mode if [[ -n "$restartClient" ]]; then - echo "Note: --restart-client is only used with --checkpoint-sync-url (default: https://leanpoint.leanroadmap.org)" + echo "Note: --restart-client is only used with --checkpoint-sync-url (default: https://leanpoint.leanroadmap.org/lean/v0/states/finalized)" restart_with_checkpoint_sync=true # Skip genesis when restarting with checkpoint sync (we're syncing from remote) generateGenesis=false From cc13e9ee007e1988a03c282a1586eb48f193e128 Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Tue, 17 Feb 2026 20:15:56 +0000 Subject: [PATCH 5/6] Fix Ansible recursion error for --restart-client with ansible deployment - Pass restart nodes (spin_nodes) to run-ansible.sh when using --restart-client - Remove recursive node_names definition in site.yml that caused max recursion depth - Add skip_genesis support for checkpoint-sync restarts (skip genesis generation) --- ansible/playbooks/site.yml | 2 -- run-ansible.sh | 5 +++++ spin-node.sh | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ansible/playbooks/site.yml b/ansible/playbooks/site.yml index fea64cc..52dbbac 100644 --- a/ansible/playbooks/site.yml +++ b/ansible/playbooks/site.yml @@ -22,7 +22,6 @@ import_playbook: clean-node-data.yml vars: genesis_dir: "{{ network_dir }}/genesis" - node_names: "{{ node_names }}" when: clean_data | default(false) | bool - name: Generate Genesis Files @@ -40,5 +39,4 @@ - name: Deploy Nodes import_playbook: deploy-nodes.yml vars: - node_names: "{{ node_names }}" skip_role_cleaning: "{{ clean_data | default(false) | bool }}" diff --git a/run-ansible.sh b/run-ansible.sh index 3afd48c..1d82736 100755 --- a/run-ansible.sh +++ b/run-ansible.sh @@ -27,6 +27,7 @@ sshKeyFile="$6" useRoot="$7" # Flag to use root user (defaults to current user) action="$8" # Action: "stop" to stop nodes, otherwise deploy coreDumps="$9" # Core dump configuration: "all", node names, or client types +skipGenesis="${10}" # Set to "true" to skip genesis generation (e.g. when restarting with checkpoint sync) # Determine SSH user: use root if --useRoot flag is set, otherwise use current user if [ "$useRoot" == "true" ]; then @@ -112,6 +113,10 @@ if [ -n "$coreDumps" ]; then EXTRA_VARS="$EXTRA_VARS enable_core_dumps=$coreDumps" fi +if [ "$skipGenesis" == "true" ]; then + EXTRA_VARS="$EXTRA_VARS skip_genesis=true" +fi + # Determine deployment mode (docker/binary) - read default from group_vars/all.yml # Default to 'docker' if not specified in group_vars GROUP_VARS_FILE="$ANSIBLE_DIR/inventory/group_vars/all.yml" diff --git a/spin-node.sh b/spin-node.sh index 73c070a..920c29e 100755 --- a/spin-node.sh +++ b/spin-node.sh @@ -187,10 +187,21 @@ if [ "$deployment_mode" == "ansible" ]; then echo "✅ Ansible prerequisites validated" + # Determine node list for Ansible: use restartClient/spin_nodes when restarting, else $node + if [[ "$restart_with_checkpoint_sync" == "true" ]]; then + ansible_node_arg=$(IFS=','; echo "${spin_nodes[*]}") + else + ansible_node_arg="$node" + fi + + # Determine skip_genesis for Ansible (true when restarting with checkpoint sync) + ansible_skip_genesis="false" + [[ "$restart_with_checkpoint_sync" == "true" ]] && ansible_skip_genesis="true" + # Handle stop action if [ -n "$stopNodes" ] && [ "$stopNodes" == "true" ]; then echo "Stopping nodes via Ansible..." - if ! "$scriptDir/run-ansible.sh" "$configDir" "$node" "$cleanData" "$validatorConfig" "$validator_config_file" "$sshKeyFile" "$useRoot" "stop" "$coreDumps"; then + if ! "$scriptDir/run-ansible.sh" "$configDir" "$ansible_node_arg" "$cleanData" "$validatorConfig" "$validator_config_file" "$sshKeyFile" "$useRoot" "stop" "$coreDumps" "$ansible_skip_genesis"; then echo "❌ Ansible stop operation failed. Exiting." exit 1 fi @@ -199,7 +210,7 @@ if [ "$deployment_mode" == "ansible" ]; then # Call separate Ansible execution script # If Ansible deployment fails, exit immediately (don't fall through to local deployment) - if ! "$scriptDir/run-ansible.sh" "$configDir" "$node" "$cleanData" "$validatorConfig" "$validator_config_file" "$sshKeyFile" "$useRoot" "" "$coreDumps"; then + if ! "$scriptDir/run-ansible.sh" "$configDir" "$ansible_node_arg" "$cleanData" "$validatorConfig" "$validator_config_file" "$sshKeyFile" "$useRoot" "" "$coreDumps" "$ansible_skip_genesis"; then echo "❌ Ansible deployment failed. Exiting." exit 1 fi From 0e32352a88f0d6b3ae1a018ee09cac807f434aa3 Mon Sep 17 00:00:00 2001 From: ch4r10t33r Date: Wed, 18 Feb 2026 13:12:38 +0000 Subject: [PATCH 6/6] Expand checkpoint sync documentation in README --- README.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1f3c2d5..dace634 100644 --- a/README.md +++ b/README.md @@ -140,20 +140,42 @@ Grafana is started with the two pre-provisioned dashboards from [leanMetrics](ht Note: Client metrics endpoints are always enabled regardless of this flag. 12. `--checkpoint-sync-url` specifies the URL to fetch finalized checkpoint state from for checkpoint sync. Default: `https://leanpoint.leanroadmap.org/lean/v0/states/finalized`. Only used when `--restart-client` is specified. -13. `--restart-client` comma-separated list of client node names (e.g., `zeam_0,ream_0`). When specified together with `--checkpoint-sync-url`, those clients are stopped, their data cleared, and restarted using checkpoint sync from the given URL. Genesis is skipped. All clients accept `--checkpoint-sync-url`; parameter names may vary per client implementation. +13. `--restart-client` comma-separated list of client node names (e.g., `zeam_0,ream_0`). When specified, those clients are stopped, their data cleared, and restarted using checkpoint sync. Genesis is skipped. Use with `--checkpoint-sync-url` to override the default URL. -### Restart clients with checkpoint sync +### Checkpoint sync -Restart specific clients by syncing from a remote checkpoint (e.g., leanpoint mainnet): +Checkpoint sync lets you restart clients by syncing from a remote checkpoint instead of from genesis. This is useful for joining an existing network (e.g., leanpoint mainnet) without replaying the full chain. + +**Basic usage:** ```sh -# Restart zeam_0 using default checkpoint URL (https://leanpoint.leanroadmap.org/lean/v0/states/finalized) +# Restart zeam_0 using the default checkpoint URL NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0 -# Restart multiple clients with custom checkpoint URL -NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0,ream_0 --checkpoint-sync-url https://leanpoint.leanroadmap.org/lean/v0/states/finalized +# Restart multiple clients +NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0,ream_0 +``` + +**Custom checkpoint URL:** + +```sh +NETWORK_DIR=local-devnet ./spin-node.sh --restart-client zeam_0 \ + --checkpoint-sync-url https://leanpoint.leanroadmap.org/lean/v0/states/finalized ``` +**Default checkpoint URL:** `https://leanpoint.leanroadmap.org/lean/v0/states/finalized` + +**What happens:** +1. Existing containers for the specified clients are stopped (no error if already stopped) +2. Data directories are cleared +3. Clients are started with `--checkpoint-sync-url` so they sync from the remote checkpoint instead of genesis + +**Deployment modes:** +- **Local** (`NETWORK_DIR=local-devnet`): Uses Docker directly +- **Ansible** (`NETWORK_DIR=ansible-devnet`): Uses Ansible to deploy to remote hosts + +**Supported clients:** zeam, ream, qlean, lantern, lighthouse, grandine, ethlambda + > **Note:** All clients accept `--checkpoint-sync-url`. Client implementations may use different parameter names internally; update client-cmd scripts if parameters change. ### Clients supported