From fb409ed1a9f2b858fdf44935a5a144ee0332cf52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:33:07 -0300 Subject: [PATCH 01/23] chore: add preview-config.nix file --- preview-config.nix | 330 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 preview-config.nix diff --git a/preview-config.nix b/preview-config.nix new file mode 100644 index 00000000..8674eef3 --- /dev/null +++ b/preview-config.nix @@ -0,0 +1,330 @@ +# NixOS configuration for ethlambda preview containers (4-node devnet) +# Copy this file to the root of your repo as preview-config.nix and adapt it. +# Built by: tekton's preview.sh using nix build --impure --expr +# +# system.build.previewMeta -- read by tekton before container start (routing, services, DB) +# environment.etc."preview-meta.json" -- same JSON available inside the running container +# +# Deploys a 4-node ethlambda devnet using lean-quickstart genesis tooling. +# Each node runs as a separate systemd service with its own QUIC port, metrics +# endpoint, and RocksDB data directory. +# +# Ports: +# QUIC (P2P): 9001-9004 (UDP) +# Metrics/RPC: 8081-8084 (TCP) -- serves /lean/v0/* API and /metrics +# +# Prerequisites: +# - Docker (or podman with dockerCompat) for genesis generation tools: +# hash-sig-cli (XMSS key generation) and eth-beacon-genesis (genesis state) +# - If Docker is unavailable in the container, replace virtualisation.docker +# with: virtualisation.podman = { enable = true; dockerCompat = true; }; +{ config, lib, pkgs, ... }: + +let + meta = { + # Oneshot service that clones, builds, and generates genesis. + setupService = "setup-devnet"; + + # Long-running ethlambda node services started after setup completes. + appServices = [ "ethlambda-0" "ethlambda-1" "ethlambda-2" "ethlambda-3" ]; + + # "container": no external DB needed; each node uses its own RocksDB. + database = "container"; + + # Caddy routing: most-specific path wins; "/" must be last. + # /node/N/ routes to each node's RPC/metrics port (prefix stripped). + # / routes to node 0 for quick health checks. + # + # Useful endpoints on each node: + # /lean/v0/health -- health check + # /lean/v0/states/head -- current head state + # /lean/v0/checkpoints/finalized -- finalized checkpoint + # /metrics -- Prometheus metrics + routes = [ + { path = "/node/0/"; port = 8081; stripPrefix = true; } + { path = "/node/1/"; port = 8082; stripPrefix = true; } + { path = "/node/2/"; port = 8083; stripPrefix = true; } + { path = "/node/3/"; port = 8084; stripPrefix = true; } + { path = "/"; port = 8081; } + ]; + + # No third-party API keys required. + hostSecrets = []; + + extraHosts = []; + }; + + # Paths used throughout the config + appDir = "/home/preview/app"; + quickstartDir = "/home/preview/lean-quickstart"; + genesisDir = "/home/preview/devnet/genesis"; + dataDir = "/home/preview/devnet/data"; + + # nix build produces result/bin/ethlambda via the repo's flake.nix + binaryPath = "${appDir}/result/bin/ethlambda"; + + # Helper: create a systemd service for ethlambda node N + mkNodeService = idx: + let + name = "ethlambda_${toString idx}"; + gossipPort = 9001 + idx; + metricsPort = 8081 + idx; + in + { + description = "ethlambda node ${toString idx} (gossip=${toString gossipPort}, rpc=${toString metricsPort})"; + after = [ "setup-devnet.service" ]; + requires = [ "setup-devnet.service" ]; + path = with pkgs; [ bash coreutils ]; + serviceConfig = { + Type = "simple"; + User = "preview"; + WorkingDirectory = "${dataDir}/${name}"; + ExecStart = builtins.concatStringsSep " " [ + binaryPath + "--custom-network-config-dir" genesisDir + "--gossipsub-port" (toString gossipPort) + "--node-id" name + "--node-key" "${genesisDir}/${name}.key" + "--metrics-address" "0.0.0.0" + "--metrics-port" (toString metricsPort) + ]; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + + nodeServices = builtins.listToAttrs (map (idx: { + name = "ethlambda-${toString idx}"; + value = mkNodeService idx; + }) [ 0 1 2 3 ]); + +in +{ + boot.isContainer = true; + + # Networking: static IP is set by nixos-container, disable DHCP + networking.useDHCP = false; + networking.useHostResolvConf = false; + services.resolved = { + enable = true; + settings.Resolve.FallbackDNS = [ "8.8.8.8" "1.1.1.1" ]; + }; + networking.nameservers = [ "8.8.8.8" "1.1.1.1" ]; + + # Open ports: 4 RPC/metrics (TCP) + 4 QUIC P2P (UDP) + networking.firewall.allowedTCPPorts = [ 8081 8082 8083 8084 ]; + networking.firewall.allowedUDPPorts = [ 9001 9002 9003 9004 ]; + + # Docker for genesis generation (hash-sig-cli, eth-beacon-genesis). + # Requires cgroup delegation from the host for systemd-nspawn containers. + # Alternative: virtualisation.podman = { enable = true; dockerCompat = true; }; + virtualisation.docker.enable = true; + + # Nix: enable flakes for 'nix build' from the repo's flake.nix + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + # ── Setup service ───────────────────────────────────────────────────── + # Clones the repo, builds ethlambda via nix build (using the repo's + # flake.nix for exact Rust version and reproducible builds), generates + # genesis for a 4-node ethlambda-only devnet, and creates per-node + # data directories. + systemd.services = { + setup-devnet = { + description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; + after = [ "systemd-resolved.service" "docker.service" ]; + wants = [ "systemd-resolved.service" "docker.service" ]; + before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; + path = with pkgs; [ + bash coreutils git nix + # Genesis generation + yq-go docker + ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "preview"; + Group = "preview"; + WorkingDirectory = "/home/preview"; + TimeoutStartSec = "900"; # nix build fetches deps + compiles Rust + }; + script = '' + set -euo pipefail + + # ── Load preview environment ── + if [ ! -f /etc/preview.env ]; then + echo "ERROR: /etc/preview.env not found" + exit 1 + fi + set -a + source /etc/preview.env + set +a + + SECRETS_FILE="/home/preview/.devnet-secrets.env" + + # Generate stable per-preview secrets on first run. + if [ ! -f "$SECRETS_FILE" ]; then + echo "Generating preview secrets..." + { + echo "RUST_LOG=info" + } > "$SECRETS_FILE" + chmod 600 "$SECRETS_FILE" + fi + + set -a + source "$SECRETS_FILE" + source /etc/preview.env + set +a + + # On container restart, skip setup if already built and genesis exists. + # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. + if [ -f "${binaryPath}" ] \ + && [ -d "${genesisDir}/hash-sig-keys" ] \ + && [ -f "${genesisDir}/config.yaml" ] \ + && [ ! -f /tmp/force-rebuild ]; then + echo "Devnet already set up, skipping (container restart)." + exit 0 + fi + rm -f /tmp/force-rebuild + + # ── 1. Clone ethlambda ── + PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") + AUTHED_URL=$(echo "$PREVIEW_REPO_URL" | sed "s|https://|https://x-access-token:$PREVIEW_TOKEN@|") + + if [ -d "${appDir}/.git" ]; then + echo "Updating ethlambda repo..." + ${pkgs.git}/bin/git -C "${appDir}" remote set-url origin "$AUTHED_URL" + ${pkgs.git}/bin/git -C "${appDir}" fetch origin + ${pkgs.git}/bin/git -C "${appDir}" reset --hard "origin/$PREVIEW_BRANCH" + else + echo "Cloning ethlambda (branch: $PREVIEW_BRANCH)..." + ${pkgs.git}/bin/git clone --depth 1 --branch "$PREVIEW_BRANCH" --single-branch "$AUTHED_URL" "${appDir}" + fi + + # ── 2. Build ethlambda ── + # Uses the repo's flake.nix which pins the exact Rust version (1.92.0) + # and handles all build dependencies via crane + rust-overlay. + echo "Building ethlambda with nix build..." + cd "${appDir}" + nix build 2>&1 + + if [ ! -f "${binaryPath}" ]; then + echo "ERROR: Binary not found at ${binaryPath} after build" + exit 1 + fi + echo "Build complete: $(readlink -f ${appDir}/result)" + + # ── 3. Clone lean-quickstart (for genesis generation scripts) ── + if [ ! -d "${quickstartDir}/.git" ]; then + echo "Cloning lean-quickstart..." + ${pkgs.git}/bin/git clone --depth 1 --single-branch \ + https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" + fi + + # ── 4. Write 4-node ethlambda-only validator config ── + # Four ethlambda nodes, each with 1 validator, on localhost with + # unique QUIC and metrics ports. Private keys are secp256k1 keys + # for P2P identity (reused from the standard devnet config). + mkdir -p "${genesisDir}" + + cat > "${genesisDir}/validator-config.yaml" << 'VCEOF' +shuffle: roundrobin +deployment_mode: local +config: + activeEpoch: 18 + keyType: "hash-sig" +validators: + - name: "ethlambda_0" + privkey: "4fd22cf461fbeae4947a3fdaef8d533fc7fd1ef1ce4cd98e993210c18234df3f" + enrFields: + ip: "127.0.0.1" + quic: 9001 + metricsPort: 8081 + count: 1 + + - name: "ethlambda_1" + privkey: "64a7f5ab53907966374ca23af36392910af682eec82c12e3abbb6c2ccdf39a72" + enrFields: + ip: "127.0.0.1" + quic: 9002 + metricsPort: 8082 + count: 1 + + - name: "ethlambda_2" + privkey: "299550529a79bc2dce003747c52fb0639465c893e00b0440ac66144d625e066a" + enrFields: + ip: "127.0.0.1" + quic: 9003 + metricsPort: 8083 + count: 1 + + - name: "ethlambda_3" + privkey: "bdf953adc161873ba026330c56450453f582e3c4ee6cb713644794bcfdd85fe5" + enrFields: + ip: "127.0.0.1" + quic: 9004 + metricsPort: 8084 + count: 1 +VCEOF + + # ── 5. Generate genesis ── + # Runs lean-quickstart's genesis generator which uses Docker to: + # 1. Generate XMSS key pairs (hash-sig-cli) + # 2. Generate genesis state (eth-beacon-genesis) + # Output: config.yaml, validators.yaml, nodes.yaml, genesis.json, + # genesis.ssz, annotated_validators.yaml, *.key files, + # hash-sig-keys/ directory + echo "Generating genesis for 4-node devnet..." + cd "${quickstartDir}" + bash generate-genesis.sh "${genesisDir}" --mode local + + # Verify critical output files + for f in config.yaml validators.yaml nodes.yaml genesis.json annotated_validators.yaml; do + if [ ! -f "${genesisDir}/$f" ]; then + echo "ERROR: Genesis generation did not produce $f" + exit 1 + fi + done + + # ── 6. Create per-node data directories ── + for i in 0 1 2 3; do + mkdir -p "${dataDir}/ethlambda_$i" + done + + echo "Devnet setup complete. 4 ethlambda nodes ready to start." + ''; + }; + } // nodeServices; + + # Preview user (non-root): all build and node processes run as this user + users.users.preview = { + isNormalUser = true; + home = "/home/preview"; + shell = pkgs.bash; + extraGroups = [ "docker" ]; # Needed for genesis generation via Docker + }; + + # SSH access for debugging + services.openssh = { + enable = true; + settings.PermitRootLogin = "yes"; + }; + + users.users.root = { + password = "changeme"; + }; + + # Required: expose meta to tekton before container boot, and inside the container. + system.build.previewMeta = pkgs.writeText "preview-meta.json" (builtins.toJSON meta); + environment.etc."preview-meta.json".text = builtins.toJSON meta; + + environment.systemPackages = with pkgs; [ + git + curl + jq + yq-go + htop # Useful for debugging resource usage + docker # For genesis tool access via SSH + ]; + + system.stateVersion = "24.11"; +} From 1f4f82d8275ef1a50ace10b4d5d77b2cdfb8d9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:58:00 -0300 Subject: [PATCH 02/23] fix: use SSH key for authentication --- preview-config.nix | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 8674eef3..d8bc4078 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -303,15 +303,18 @@ VCEOF extraGroups = [ "docker" ]; # Needed for genesis generation via Docker }; - # SSH access for debugging + # SSH access for debugging (key-only, no passwords) services.openssh = { enable = true; - settings.PermitRootLogin = "yes"; + settings = { + PermitRootLogin = "prohibit-password"; + PasswordAuthentication = false; + }; }; - users.users.root = { - password = "changeme"; - }; + users.users.root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEYfIsBaa3Jr/0Ij7QOwWcLp1I1fnNp86yW0Bzsg4Ylg" + ]; # Required: expose meta to tekton before container boot, and inside the container. system.build.previewMeta = pkgs.writeText "preview-meta.json" (builtins.toJSON meta); From 4a64f69477f97f5e4aa5f5ce9330f9adb647d357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 10:58:16 -0300 Subject: [PATCH 03/23] fix: specify empty groups for preview --- preview-config.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/preview-config.nix b/preview-config.nix index d8bc4078..d7777f8a 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -303,6 +303,8 @@ VCEOF extraGroups = [ "docker" ]; # Needed for genesis generation via Docker }; + users.groups.preview = {}; + # SSH access for debugging (key-only, no passwords) services.openssh = { enable = true; From 7028f7fff596005287a339e532140b56d1b6262e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:12:04 -0300 Subject: [PATCH 04/23] chore: address review comments --- preview-config.nix | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index d7777f8a..e7c948f2 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -1,5 +1,5 @@ # NixOS configuration for ethlambda preview containers (4-node devnet) -# Copy this file to the root of your repo as preview-config.nix and adapt it. +# Used by tekton's preview.sh to build a NixOS container for PR previews. # Built by: tekton's preview.sh using nix build --impure --expr # # system.build.previewMeta -- read by tekton before container start (routing, services, DB) @@ -159,21 +159,7 @@ in source /etc/preview.env set +a - SECRETS_FILE="/home/preview/.devnet-secrets.env" - - # Generate stable per-preview secrets on first run. - if [ ! -f "$SECRETS_FILE" ]; then - echo "Generating preview secrets..." - { - echo "RUST_LOG=info" - } > "$SECRETS_FILE" - chmod 600 "$SECRETS_FILE" - fi - - set -a - source "$SECRETS_FILE" - source /etc/preview.env - set +a + export RUST_LOG=info # On container restart, skip setup if already built and genesis exists. # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. @@ -185,6 +171,7 @@ in exit 0 fi rm -f /tmp/force-rebuild + rm -rf "${genesisDir}" # ── 1. Clone ethlambda ── PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") From 8cf2ce9142c348e7e4e4e409ccf8ab48ac451d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:04:10 -0300 Subject: [PATCH 05/23] fix: specify group for preview --- preview-config.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/preview-config.nix b/preview-config.nix index e7c948f2..db94a9bd 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -286,6 +286,7 @@ VCEOF users.users.preview = { isNormalUser = true; home = "/home/preview"; + group = "preview"; shell = pkgs.bash; extraGroups = [ "docker" ]; # Needed for genesis generation via Docker }; From 2c6fdb167927bd475251368fb5293f8825ca1d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 16:54:06 -0300 Subject: [PATCH 06/23] fix: add some gnu coreutils used by the script --- preview-config.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/preview-config.nix b/preview-config.nix index db94a9bd..35b3600f 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -136,8 +136,9 @@ in before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ bash coreutils git nix - # Genesis generation + # Genesis generation (generate-genesis.sh needs these) yq-go docker + gawk gnugrep gnused which ]; serviceConfig = { Type = "oneshot"; From ccd7abb8e970e914167013713ec7c5d3ad159026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 17:41:14 -0300 Subject: [PATCH 07/23] fix: switch to podman --- preview-config.nix | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 35b3600f..a46b436c 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -14,10 +14,10 @@ # Metrics/RPC: 8081-8084 (TCP) -- serves /lean/v0/* API and /metrics # # Prerequisites: -# - Docker (or podman with dockerCompat) for genesis generation tools: +# - Podman (rootless, with dockerCompat) for genesis generation tools: # hash-sig-cli (XMSS key generation) and eth-beacon-genesis (genesis state) -# - If Docker is unavailable in the container, replace virtualisation.docker -# with: virtualisation.podman = { enable = true; dockerCompat = true; }; +# - Podman rootless uses user namespaces instead of cgroup BPF, so it works +# inside systemd-nspawn containers where Docker's cgroup device access fails. { config, lib, pkgs, ... }: let @@ -115,10 +115,12 @@ in networking.firewall.allowedTCPPorts = [ 8081 8082 8083 8084 ]; networking.firewall.allowedUDPPorts = [ 9001 9002 9003 9004 ]; - # Docker for genesis generation (hash-sig-cli, eth-beacon-genesis). - # Requires cgroup delegation from the host for systemd-nspawn containers. - # Alternative: virtualisation.podman = { enable = true; dockerCompat = true; }; - virtualisation.docker.enable = true; + # Podman (rootless) for genesis generation (hash-sig-cli, eth-beacon-genesis). + # dockerCompat provides a `docker` CLI alias so generate-genesis.sh works unmodified. + virtualisation.podman = { + enable = true; + dockerCompat = true; + }; # Nix: enable flakes for 'nix build' from the repo's flake.nix nix.settings.experimental-features = [ "nix-command" "flakes" ]; @@ -131,13 +133,13 @@ in systemd.services = { setup-devnet = { description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; - after = [ "systemd-resolved.service" "docker.service" ]; - wants = [ "systemd-resolved.service" "docker.service" ]; + after = [ "systemd-resolved.service" ]; + wants = [ "systemd-resolved.service" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ bash coreutils git nix # Genesis generation (generate-genesis.sh needs these) - yq-go docker + yq-go podman gawk gnugrep gnused which ]; serviceConfig = { @@ -255,7 +257,7 @@ validators: VCEOF # ── 5. Generate genesis ── - # Runs lean-quickstart's genesis generator which uses Docker to: + # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) # Output: config.yaml, validators.yaml, nodes.yaml, genesis.json, @@ -289,7 +291,7 @@ VCEOF home = "/home/preview"; group = "preview"; shell = pkgs.bash; - extraGroups = [ "docker" ]; # Needed for genesis generation via Docker + extraGroups = []; # Podman rootless needs no special groups }; users.groups.preview = {}; @@ -317,7 +319,7 @@ VCEOF jq yq-go htop # Useful for debugging resource usage - docker # For genesis tool access via SSH + podman # For genesis tool access via SSH ]; system.stateVersion = "24.11"; From f6008a36bc9585cea3df60e19f0e059476b1adb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:32:40 -0300 Subject: [PATCH 08/23] fix?: wait for github.com to resolve --- preview-config.nix | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index a46b436c..8dd08d72 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -133,8 +133,8 @@ in systemd.services = { setup-devnet = { description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; - after = [ "systemd-resolved.service" ]; - wants = [ "systemd-resolved.service" ]; + after = [ "systemd-resolved.service" "network-online.target" ]; + wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ bash coreutils git nix @@ -164,6 +164,22 @@ in export RUST_LOG=info + # ── Wait for DNS resolution ── + # DNS may not be available immediately at service start (e.g. network + # not yet routable, resolved still configuring upstream servers). + # Poll until github.com resolves (max ~30s). + for i in $(seq 1 30); do + if getent hosts github.com >/dev/null 2>&1; then + break + fi + echo "Waiting for DNS resolution... ($i/30)" + sleep 1 + done + if ! getent hosts github.com >/dev/null 2>&1; then + echo "ERROR: DNS resolution failed after 30s" + exit 1 + fi + # On container restart, skip setup if already built and genesis exists. # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. if [ -f "${binaryPath}" ] \ From f488392d1ba45341ef780a8ef72b511c6945a2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:33:14 -0300 Subject: [PATCH 09/23] fix: avoid cloning repositories --- preview-config.nix | 110 ++++++++++++++++----------------------------- 1 file changed, 38 insertions(+), 72 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 8dd08d72..b330c08e 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -9,6 +9,11 @@ # Each node runs as a separate systemd service with its own QUIC port, metrics # endpoint, and RocksDB data directory. # +# The ethlambda binary and lean-quickstart source are fetched and built at Nix +# eval time on the tekton host (via builtins.getFlake and builtins.fetchGit). +# This eliminates runtime network dependencies for git/nix operations; the +# container only needs network access for Podman image pulls during genesis. +# # Ports: # QUIC (P2P): 9001-9004 (UDP) # Metrics/RPC: 8081-8084 (TCP) -- serves /lean/v0/* API and /metrics @@ -22,7 +27,7 @@ let meta = { - # Oneshot service that clones, builds, and generates genesis. + # Oneshot service that generates genesis (binary is pre-built at nix eval time). setupService = "setup-devnet"; # Long-running ethlambda node services started after setup completes. @@ -54,14 +59,30 @@ let extraHosts = []; }; + # ── Build-time fetches (run on tekton host at Nix eval time) ────────── + # builtins.getFlake and builtins.getEnv require --impure, which tekton's + # preview.sh already passes. PREVIEW_BRANCH must be exported in the host + # shell before running nix build (tekton sets this from the PR metadata). + + # Fetch + build ethlambda from the PR branch's flake.nix (crane + rust-overlay). + previewBranch = let b = builtins.getEnv "PREVIEW_BRANCH"; in + if b == "" then "tekton-integration" else b; + ethlambdaFlake = builtins.getFlake "github:lambdaclass/ethlambda/${previewBranch}"; + ethlambdaBin = ethlambdaFlake.packages.x86_64-linux.default; + + # Fetch lean-quickstart genesis scripts (public repo, no auth needed). + quickstartSrc = builtins.fetchGit { + url = "https://github.com/blockblaz/lean-quickstart"; + ref = "main"; + }; + # Paths used throughout the config - appDir = "/home/preview/app"; - quickstartDir = "/home/preview/lean-quickstart"; genesisDir = "/home/preview/devnet/genesis"; dataDir = "/home/preview/devnet/data"; - # nix build produces result/bin/ethlambda via the repo's flake.nix - binaryPath = "${appDir}/result/bin/ethlambda"; + # Binary is in the nix store; quickstart source is a nix store path. + binaryPath = "${ethlambdaBin}/bin/ethlambda"; + quickstartDir = "${quickstartSrc}"; # Helper: create a systemd service for ethlambda node N mkNodeService = idx: @@ -122,22 +143,18 @@ in dockerCompat = true; }; - # Nix: enable flakes for 'nix build' from the repo's flake.nix - nix.settings.experimental-features = [ "nix-command" "flakes" ]; - # ── Setup service ───────────────────────────────────────────────────── - # Clones the repo, builds ethlambda via nix build (using the repo's - # flake.nix for exact Rust version and reproducible builds), generates - # genesis for a 4-node ethlambda-only devnet, and creates per-node - # data directories. + # Generates genesis for a 4-node ethlambda-only devnet and creates + # per-node data directories. The ethlambda binary and lean-quickstart + # source are already in the nix store (fetched at build time on the host). systemd.services = { setup-devnet = { - description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; + description = "Setup ethlambda 4-node devnet (genesis generation)"; after = [ "systemd-resolved.service" "network-online.target" ]; wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ - bash coreutils git nix + bash coreutils # Genesis generation (generate-genesis.sh needs these) yq-go podman gawk gnugrep gnused which @@ -148,7 +165,7 @@ in User = "preview"; Group = "preview"; WorkingDirectory = "/home/preview"; - TimeoutStartSec = "900"; # nix build fetches deps + compiles Rust + TimeoutStartSec = "300"; # genesis generation + podman image pulls }; script = '' set -euo pipefail @@ -164,26 +181,10 @@ in export RUST_LOG=info - # ── Wait for DNS resolution ── - # DNS may not be available immediately at service start (e.g. network - # not yet routable, resolved still configuring upstream servers). - # Poll until github.com resolves (max ~30s). - for i in $(seq 1 30); do - if getent hosts github.com >/dev/null 2>&1; then - break - fi - echo "Waiting for DNS resolution... ($i/30)" - sleep 1 - done - if ! getent hosts github.com >/dev/null 2>&1; then - echo "ERROR: DNS resolution failed after 30s" - exit 1 - fi - - # On container restart, skip setup if already built and genesis exists. + # On container restart, skip setup if genesis already exists. + # The binary is always present (built into the nix store at host build time). # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. - if [ -f "${binaryPath}" ] \ - && [ -d "${genesisDir}/hash-sig-keys" ] \ + if [ -d "${genesisDir}/hash-sig-keys" ] \ && [ -f "${genesisDir}/config.yaml" ] \ && [ ! -f /tmp/force-rebuild ]; then echo "Devnet already set up, skipping (container restart)." @@ -192,41 +193,7 @@ in rm -f /tmp/force-rebuild rm -rf "${genesisDir}" - # ── 1. Clone ethlambda ── - PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") - AUTHED_URL=$(echo "$PREVIEW_REPO_URL" | sed "s|https://|https://x-access-token:$PREVIEW_TOKEN@|") - - if [ -d "${appDir}/.git" ]; then - echo "Updating ethlambda repo..." - ${pkgs.git}/bin/git -C "${appDir}" remote set-url origin "$AUTHED_URL" - ${pkgs.git}/bin/git -C "${appDir}" fetch origin - ${pkgs.git}/bin/git -C "${appDir}" reset --hard "origin/$PREVIEW_BRANCH" - else - echo "Cloning ethlambda (branch: $PREVIEW_BRANCH)..." - ${pkgs.git}/bin/git clone --depth 1 --branch "$PREVIEW_BRANCH" --single-branch "$AUTHED_URL" "${appDir}" - fi - - # ── 2. Build ethlambda ── - # Uses the repo's flake.nix which pins the exact Rust version (1.92.0) - # and handles all build dependencies via crane + rust-overlay. - echo "Building ethlambda with nix build..." - cd "${appDir}" - nix build 2>&1 - - if [ ! -f "${binaryPath}" ]; then - echo "ERROR: Binary not found at ${binaryPath} after build" - exit 1 - fi - echo "Build complete: $(readlink -f ${appDir}/result)" - - # ── 3. Clone lean-quickstart (for genesis generation scripts) ── - if [ ! -d "${quickstartDir}/.git" ]; then - echo "Cloning lean-quickstart..." - ${pkgs.git}/bin/git clone --depth 1 --single-branch \ - https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" - fi - - # ── 4. Write 4-node ethlambda-only validator config ── + # ── 1. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with # unique QUIC and metrics ports. Private keys are secp256k1 keys # for P2P identity (reused from the standard devnet config). @@ -272,7 +239,7 @@ validators: count: 1 VCEOF - # ── 5. Generate genesis ── + # ── 2. Generate genesis ── # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) @@ -291,7 +258,7 @@ VCEOF fi done - # ── 6. Create per-node data directories ── + # ── 3. Create per-node data directories ── for i in 0 1 2 3; do mkdir -p "${dataDir}/ethlambda_$i" done @@ -330,7 +297,6 @@ VCEOF environment.etc."preview-meta.json".text = builtins.toJSON meta; environment.systemPackages = with pkgs; [ - git curl jq yq-go From dae9d27a8888c639c523b9355c2e43f6892a25c5 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:17:51 -0300 Subject: [PATCH 10/23] Fix flake.nix file --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 3a1e95ac..43fd4e2b 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,11 @@ commonArgs = { pname = "ethlambda"; - src = craneLib.cleanCargoSource ./.; + src = pkgs.lib.cleanSourceWith { + src = ./.; + filter = path: type: + (craneLib.filterCargoSources path type) || (builtins.match ".*\\.html$" path != null); + }; strictDeps = true; nativeBuildInputs = with pkgs; [ From 489f94512714ed9e83c1e8b6d161cfb5cc7cb195 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:34:12 -0300 Subject: [PATCH 11/23] Test fix in preview config for podman --- preview-config.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/preview-config.nix b/preview-config.nix index b330c08e..415dee03 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -158,6 +158,7 @@ in # Genesis generation (generate-genesis.sh needs these) yq-go podman gawk gnugrep gnused which + (writeShellScriptBin "docker" ''exec podman "$@"'') ]; serviceConfig = { Type = "oneshot"; From 0f8157c0815b65e8f33ea6b62181cf4651f8c8cb Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:48:49 -0300 Subject: [PATCH 12/23] More fixes --- preview-config.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 415dee03..e8420bc7 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -157,7 +157,7 @@ in bash coreutils # Genesis generation (generate-genesis.sh needs these) yq-go podman - gawk gnugrep gnused which + gawk gnugrep gnused which shadow (writeShellScriptBin "docker" ''exec podman "$@"'') ]; serviceConfig = { @@ -275,7 +275,9 @@ VCEOF home = "/home/preview"; group = "preview"; shell = pkgs.bash; - extraGroups = []; # Podman rootless needs no special groups + extraGroups = []; + subUidRanges = [{ startUid = 100000; count = 65536; }]; + subGidRanges = [{ startGid = 100000; count = 65536; }]; }; users.groups.preview = {}; From 3337dc1a3be0626e3930e05bb26f9d2dee61db3e Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:51:22 -0300 Subject: [PATCH 13/23] more fixes --- preview-config.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index e8420bc7..8ae1f8c6 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -163,8 +163,6 @@ in serviceConfig = { Type = "oneshot"; RemainAfterExit = true; - User = "preview"; - Group = "preview"; WorkingDirectory = "/home/preview"; TimeoutStartSec = "300"; # genesis generation + podman image pulls }; @@ -264,6 +262,8 @@ VCEOF mkdir -p "${dataDir}/ethlambda_$i" done + chown -R preview:preview /home/preview/devnet + echo "Devnet setup complete. 4 ethlambda nodes ready to start." ''; }; From aa8d65da41208add8666534cbb44ef87fd8c7342 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:54:14 -0300 Subject: [PATCH 14/23] fix --- preview-config.nix | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/preview-config.nix b/preview-config.nix index 8ae1f8c6..f97ce2fd 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -180,6 +180,15 @@ in export RUST_LOG=info + # Podman inside nspawn: disable BPF cgroup device manager + mkdir -p /etc/containers + cat > /etc/containers/containers.conf << 'CONFEOF' + [engine] + cgroup_manager = "cgroupfs" + [containers] + cgroups = "disabled" + CONFEOF + # On container restart, skip setup if genesis already exists. # The binary is always present (built into the nix store at host build time). # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. From 0c4273f17b0fee089a448afbb4ed1fda43d7d11f Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:55:35 -0300 Subject: [PATCH 15/23] test --- preview-config.nix | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index f97ce2fd..e529c16e 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -143,6 +143,13 @@ in dockerCompat = true; }; + environment.etc."containers/containers.conf".text = '' + [engine] + cgroup_manager = "cgroupfs" + [containers] + cgroups = "disabled" + ''; + # ── Setup service ───────────────────────────────────────────────────── # Generates genesis for a 4-node ethlambda-only devnet and creates # per-node data directories. The ethlambda binary and lean-quickstart @@ -180,15 +187,6 @@ in export RUST_LOG=info - # Podman inside nspawn: disable BPF cgroup device manager - mkdir -p /etc/containers - cat > /etc/containers/containers.conf << 'CONFEOF' - [engine] - cgroup_manager = "cgroupfs" - [containers] - cgroups = "disabled" - CONFEOF - # On container restart, skip setup if genesis already exists. # The binary is always present (built into the nix store at host build time). # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. From a10a77d775620960aa099d378269dc6bed6ffa25 Mon Sep 17 00:00:00 2001 From: Javier Chatruc Date: Mon, 2 Mar 2026 11:56:38 -0300 Subject: [PATCH 16/23] test --- preview-config.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preview-config.nix b/preview-config.nix index e529c16e..9db98f64 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -143,7 +143,7 @@ in dockerCompat = true; }; - environment.etc."containers/containers.conf".text = '' + environment.etc."containers/containers.conf".text = lib.mkForce '' [engine] cgroup_manager = "cgroupfs" [containers] From 6a499ba6f5bb28a1de600866a81dff0418f61a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 13:06:39 -0300 Subject: [PATCH 17/23] fix: set one node as aggregator --- preview-config.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 9db98f64..26258275 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -84,12 +84,16 @@ let binaryPath = "${ethlambdaBin}/bin/ethlambda"; quickstartDir = "${quickstartSrc}"; - # Helper: create a systemd service for ethlambda node N + # Helper: create a systemd service for ethlambda node N. + # Node 0 runs with --is-aggregator so the devnet can finalize blocks: + # without at least one aggregator, attestations are never packed into + # blocks and justified_slot/finalized_slot stay at 0 forever. mkNodeService = idx: let name = "ethlambda_${toString idx}"; gossipPort = 9001 + idx; metricsPort = 8081 + idx; + aggregatorArgs = lib.optionals (idx == 0) [ "--is-aggregator" ]; in { description = "ethlambda node ${toString idx} (gossip=${toString gossipPort}, rpc=${toString metricsPort})"; @@ -100,7 +104,7 @@ let Type = "simple"; User = "preview"; WorkingDirectory = "${dataDir}/${name}"; - ExecStart = builtins.concatStringsSep " " [ + ExecStart = builtins.concatStringsSep " " ([ binaryPath "--custom-network-config-dir" genesisDir "--gossipsub-port" (toString gossipPort) @@ -108,7 +112,7 @@ let "--node-key" "${genesisDir}/${name}.key" "--metrics-address" "0.0.0.0" "--metrics-port" (toString metricsPort) - ]; + ] ++ aggregatorArgs); Restart = "on-failure"; RestartSec = 5; }; From 91f98963e2e7e59051e043fd960ce610f465ac65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:39:46 -0300 Subject: [PATCH 18/23] chore: address review comments --- preview-config.nix | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 26258275..57021658 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -65,8 +65,12 @@ let # shell before running nix build (tekton sets this from the PR metadata). # Fetch + build ethlambda from the PR branch's flake.nix (crane + rust-overlay). - previewBranch = let b = builtins.getEnv "PREVIEW_BRANCH"; in - if b == "" then "tekton-integration" else b; + # PREVIEW_BRANCH is injected by tekton from PR metadata. Abort if unset so that + # a CI misconfiguration produces a clear error rather than silently evaluating + # the wrong branch. + previewBranch = + let b = builtins.getEnv "PREVIEW_BRANCH"; in + if b == "" then abort "PREVIEW_BRANCH env var must be set (pass --impure to nix build)" else b; ethlambdaFlake = builtins.getFlake "github:lambdaclass/ethlambda/${previewBranch}"; ethlambdaBin = ethlambdaFlake.packages.x86_64-linux.default; @@ -80,9 +84,7 @@ let genesisDir = "/home/preview/devnet/genesis"; dataDir = "/home/preview/devnet/data"; - # Binary is in the nix store; quickstart source is a nix store path. binaryPath = "${ethlambdaBin}/bin/ethlambda"; - quickstartDir = "${quickstartSrc}"; # Helper: create a systemd service for ethlambda node N. # Node 0 runs with --is-aggregator so the devnet can finalize blocks: @@ -130,11 +132,14 @@ in # Networking: static IP is set by nixos-container, disable DHCP networking.useDHCP = false; networking.useHostResolvConf = false; + # systemd-resolved handles DNS; FallbackDNS is used when the container's + # upstream resolver is unavailable. Don't set networking.nameservers here: + # with resolved enabled, NixOS routes DNS through 127.0.0.53, and setting + # nameservers directly would bypass resolved and produce conflicting config. services.resolved = { enable = true; settings.Resolve.FallbackDNS = [ "8.8.8.8" "1.1.1.1" ]; }; - networking.nameservers = [ "8.8.8.8" "1.1.1.1" ]; # Open ports: 4 RPC/metrics (TCP) + 4 QUIC P2P (UDP) networking.firewall.allowedTCPPorts = [ 8081 8082 8083 8084 ]; @@ -201,12 +206,16 @@ in exit 0 fi rm -f /tmp/force-rebuild - rm -rf "${genesisDir}" + # Clear both genesis and RocksDB data dirs: new genesis produces new XMSS keys + # and a new genesis state, so any existing chain state is incompatible. + rm -rf "${genesisDir}" "${dataDir}" # ── 1. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with - # unique QUIC and metrics ports. Private keys are secp256k1 keys - # for P2P identity (reused from the standard devnet config). + # unique QUIC and metrics ports. The privkeys below are secp256k1 + # P2P identity keys reused from the standard devnet config — they are + # publicly known and are NOT secrets. Validator signing uses XMSS keys + # generated fresh by hash-sig-cli during genesis. mkdir -p "${genesisDir}" cat > "${genesisDir}/validator-config.yaml" << 'VCEOF' @@ -257,7 +266,7 @@ VCEOF # genesis.ssz, annotated_validators.yaml, *.key files, # hash-sig-keys/ directory echo "Generating genesis for 4-node devnet..." - cd "${quickstartDir}" + cd "${quickstartSrc}" bash generate-genesis.sh "${genesisDir}" --mode local # Verify critical output files From b8a0c188b6e5a9f12398840cf27ac1f2e09f219f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:23:55 -0300 Subject: [PATCH 19/23] fix: use cargo build --- preview-config.nix | 108 ++++++++++++++++++++++++++------------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 57021658..2bc707f0 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -9,10 +9,9 @@ # Each node runs as a separate systemd service with its own QUIC port, metrics # endpoint, and RocksDB data directory. # -# The ethlambda binary and lean-quickstart source are fetched and built at Nix -# eval time on the tekton host (via builtins.getFlake and builtins.fetchGit). -# This eliminates runtime network dependencies for git/nix operations; the -# container only needs network access for Podman image pulls during genesis. +# The setup service clones the PR branch and builds ethlambda at container start, +# so previews always run the exact binary from the branch under review. +# PREVIEW_BRANCH is available at runtime via /etc/preview.env (injected by tekton). # # Ports: # QUIC (P2P): 9001-9004 (UDP) @@ -27,7 +26,7 @@ let meta = { - # Oneshot service that generates genesis (binary is pre-built at nix eval time). + # Oneshot service that clones, builds, and generates genesis. setupService = "setup-devnet"; # Long-running ethlambda node services started after setup completes. @@ -59,32 +58,13 @@ let extraHosts = []; }; - # ── Build-time fetches (run on tekton host at Nix eval time) ────────── - # builtins.getFlake and builtins.getEnv require --impure, which tekton's - # preview.sh already passes. PREVIEW_BRANCH must be exported in the host - # shell before running nix build (tekton sets this from the PR metadata). - - # Fetch + build ethlambda from the PR branch's flake.nix (crane + rust-overlay). - # PREVIEW_BRANCH is injected by tekton from PR metadata. Abort if unset so that - # a CI misconfiguration produces a clear error rather than silently evaluating - # the wrong branch. - previewBranch = - let b = builtins.getEnv "PREVIEW_BRANCH"; in - if b == "" then abort "PREVIEW_BRANCH env var must be set (pass --impure to nix build)" else b; - ethlambdaFlake = builtins.getFlake "github:lambdaclass/ethlambda/${previewBranch}"; - ethlambdaBin = ethlambdaFlake.packages.x86_64-linux.default; - - # Fetch lean-quickstart genesis scripts (public repo, no auth needed). - quickstartSrc = builtins.fetchGit { - url = "https://github.com/blockblaz/lean-quickstart"; - ref = "main"; - }; - # Paths used throughout the config - genesisDir = "/home/preview/devnet/genesis"; - dataDir = "/home/preview/devnet/data"; + appDir = "/home/preview/app"; + quickstartDir = "/home/preview/lean-quickstart"; + genesisDir = "/home/preview/devnet/genesis"; + dataDir = "/home/preview/devnet/data"; - binaryPath = "${ethlambdaBin}/bin/ethlambda"; + binaryPath = "${appDir}/target/release/ethlambda"; # Helper: create a systemd service for ethlambda node N. # Node 0 runs with --is-aggregator so the devnet can finalize blocks: @@ -160,17 +140,16 @@ in ''; # ── Setup service ───────────────────────────────────────────────────── - # Generates genesis for a 4-node ethlambda-only devnet and creates - # per-node data directories. The ethlambda binary and lean-quickstart - # source are already in the nix store (fetched at build time on the host). + # Clones the PR branch, builds ethlambda via nix build, clones lean-quickstart, + # generates genesis for the 4-node devnet, and creates per-node data directories. systemd.services = { setup-devnet = { - description = "Setup ethlambda 4-node devnet (genesis generation)"; + description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; after = [ "systemd-resolved.service" "network-online.target" ]; wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ - bash coreutils + bash coreutils git rustup pkg-config llvmPackages.libclang # Genesis generation (generate-genesis.sh needs these) yq-go podman gawk gnugrep gnused which shadow @@ -180,7 +159,7 @@ in Type = "oneshot"; RemainAfterExit = true; WorkingDirectory = "/home/preview"; - TimeoutStartSec = "300"; # genesis generation + podman image pulls + TimeoutStartSec = "900"; # rustup toolchain install + cargo build --release }; script = '' set -euo pipefail @@ -196,10 +175,10 @@ in export RUST_LOG=info - # On container restart, skip setup if genesis already exists. - # The binary is always present (built into the nix store at host build time). + # On container restart, skip setup if the binary and genesis already exist. # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. - if [ -d "${genesisDir}/hash-sig-keys" ] \ + if [ -f "${binaryPath}" ] \ + && [ -d "${genesisDir}/hash-sig-keys" ] \ && [ -f "${genesisDir}/config.yaml" ] \ && [ ! -f /tmp/force-rebuild ]; then echo "Devnet already set up, skipping (container restart)." @@ -210,7 +189,45 @@ in # and a new genesis state, so any existing chain state is incompatible. rm -rf "${genesisDir}" "${dataDir}" - # ── 1. Write 4-node ethlambda-only validator config ── + # ── 1. Clone ethlambda ── + PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") + AUTHED_URL=$(echo "$PREVIEW_REPO_URL" | sed "s|https://|https://x-access-token:$PREVIEW_TOKEN@|") + + if [ -d "${appDir}/.git" ]; then + echo "Updating ethlambda repo (branch: $PREVIEW_BRANCH)..." + git -C "${appDir}" remote set-url origin "$AUTHED_URL" + git -C "${appDir}" fetch origin + git -C "${appDir}" reset --hard "origin/$PREVIEW_BRANCH" + else + echo "Cloning ethlambda (branch: $PREVIEW_BRANCH)..." + git clone --depth 1 --branch "$PREVIEW_BRANCH" --single-branch "$AUTHED_URL" "${appDir}" + fi + + # ── 2. Build ethlambda ── + # cargo build instead of nix build: the flake has no binary cache, so nix + # build would recompile from scratch on every preview start (10-15 min). + # rustup reads rust-toolchain.toml automatically and installs the pinned version. + export LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib" + export RUSTUP_HOME=/home/preview/.rustup + export CARGO_HOME=/home/preview/.cargo + cd "${appDir}" + rustup toolchain install # installs from rust-toolchain.toml + cargo build --release --bin ethlambda + + if [ ! -f "${appDir}/target/release/ethlambda" ]; then + echo "ERROR: Binary not found after cargo build" + exit 1 + fi + echo "Build complete: ${appDir}/target/release/ethlambda" + + # ── 3. Clone lean-quickstart (for genesis generation scripts) ── + if [ ! -d "${quickstartDir}/.git" ]; then + echo "Cloning lean-quickstart..." + git clone --depth 1 --single-branch \ + https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" + fi + + # ── 4. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with # unique QUIC and metrics ports. The privkeys below are secp256k1 # P2P identity keys reused from the standard devnet config — they are @@ -258,7 +275,7 @@ validators: count: 1 VCEOF - # ── 2. Generate genesis ── + # ── 5. Generate genesis ── # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) @@ -266,7 +283,7 @@ VCEOF # genesis.ssz, annotated_validators.yaml, *.key files, # hash-sig-keys/ directory echo "Generating genesis for 4-node devnet..." - cd "${quickstartSrc}" + cd "${quickstartDir}" bash generate-genesis.sh "${genesisDir}" --mode local # Verify critical output files @@ -277,12 +294,12 @@ VCEOF fi done - # ── 3. Create per-node data directories ── + # ── 6. Create per-node data directories ── for i in 0 1 2 3; do mkdir -p "${dataDir}/ethlambda_$i" done - chown -R preview:preview /home/preview/devnet + chown -R preview:preview /home/preview/devnet /home/preview/lean-quickstart echo "Devnet setup complete. 4 ethlambda nodes ready to start." ''; @@ -323,8 +340,9 @@ VCEOF curl jq yq-go - htop # Useful for debugging resource usage - podman # For genesis tool access via SSH + htop # Useful for debugging resource usage + podman # For genesis tool access via SSH + rustup # For inspecting/rebuilding ethlambda over SSH ]; system.stateVersion = "24.11"; From 62340cbf1951b0b1eb9f897516e9e7499a15e2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:31:32 -0300 Subject: [PATCH 20/23] fix: use nix build --- preview-config.nix | 77 +++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 2bc707f0..20b65938 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -9,8 +9,8 @@ # Each node runs as a separate systemd service with its own QUIC port, metrics # endpoint, and RocksDB data directory. # -# The setup service clones the PR branch and builds ethlambda at container start, -# so previews always run the exact binary from the branch under review. +# The setup service builds ethlambda via `nix build github:lambdaclass/ethlambda/$BRANCH` +# at container start, so previews always run the exact binary from the branch under review. # PREVIEW_BRANCH is available at runtime via /etc/preview.env (injected by tekton). # # Ports: @@ -64,7 +64,8 @@ let genesisDir = "/home/preview/devnet/genesis"; dataDir = "/home/preview/devnet/data"; - binaryPath = "${appDir}/target/release/ethlambda"; + # nix build outputs a store path symlinked at appDir; binary is at bin/ethlambda + binaryPath = "${appDir}/bin/ethlambda"; # Helper: create a systemd service for ethlambda node N. # Node 0 runs with --is-aggregator so the devnet can finalize blocks: @@ -140,16 +141,17 @@ in ''; # ── Setup service ───────────────────────────────────────────────────── - # Clones the PR branch, builds ethlambda via nix build, clones lean-quickstart, - # generates genesis for the 4-node devnet, and creates per-node data directories. + # Builds ethlambda via `nix build github:lambdaclass/ethlambda/$PREVIEW_BRANCH`, + # clones lean-quickstart, generates genesis for the 4-node devnet, and creates + # per-node data directories. systemd.services = { setup-devnet = { - description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; + description = "Setup ethlambda 4-node devnet (nix build, genesis)"; after = [ "systemd-resolved.service" "network-online.target" ]; wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ - bash coreutils git rustup pkg-config llvmPackages.libclang + bash coreutils git # Genesis generation (generate-genesis.sh needs these) yq-go podman gawk gnugrep gnused which shadow @@ -159,7 +161,7 @@ in Type = "oneshot"; RemainAfterExit = true; WorkingDirectory = "/home/preview"; - TimeoutStartSec = "900"; # rustup toolchain install + cargo build --release + TimeoutStartSec = "900"; # nix build compiles ethlambda from source on first run }; script = '' set -euo pipefail @@ -173,8 +175,6 @@ in source /etc/preview.env set +a - export RUST_LOG=info - # On container restart, skip setup if the binary and genesis already exist. # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. if [ -f "${binaryPath}" ] \ @@ -189,45 +189,32 @@ in # and a new genesis state, so any existing chain state is incompatible. rm -rf "${genesisDir}" "${dataDir}" - # ── 1. Clone ethlambda ── + # ── 1. Build ethlambda via nix build ── + # Fetches the flake from GitHub and builds the ethlambda binary. + # crane's two-phase build (buildDepsOnly + buildPackage) means Rust + # dependencies are a separate derivation: once compiled they stay in + # /nix/store and only ethlambda itself needs recompiling on branch updates. PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") - AUTHED_URL=$(echo "$PREVIEW_REPO_URL" | sed "s|https://|https://x-access-token:$PREVIEW_TOKEN@|") - - if [ -d "${appDir}/.git" ]; then - echo "Updating ethlambda repo (branch: $PREVIEW_BRANCH)..." - git -C "${appDir}" remote set-url origin "$AUTHED_URL" - git -C "${appDir}" fetch origin - git -C "${appDir}" reset --hard "origin/$PREVIEW_BRANCH" - else - echo "Cloning ethlambda (branch: $PREVIEW_BRANCH)..." - git clone --depth 1 --branch "$PREVIEW_BRANCH" --single-branch "$AUTHED_URL" "${appDir}" - fi - - # ── 2. Build ethlambda ── - # cargo build instead of nix build: the flake has no binary cache, so nix - # build would recompile from scratch on every preview start (10-15 min). - # rustup reads rust-toolchain.toml automatically and installs the pinned version. - export LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib" - export RUSTUP_HOME=/home/preview/.rustup - export CARGO_HOME=/home/preview/.cargo - cd "${appDir}" - rustup toolchain install # installs from rust-toolchain.toml - cargo build --release --bin ethlambda - - if [ ! -f "${appDir}/target/release/ethlambda" ]; then - echo "ERROR: Binary not found after cargo build" + echo "Building ethlambda (branch: $PREVIEW_BRANCH) via nix build..." + nix build "github:lambdaclass/ethlambda/$PREVIEW_BRANCH" \ + --out-link "${appDir}" \ + --extra-experimental-features "nix-command flakes" \ + --option access-tokens "github.com=$PREVIEW_TOKEN" + + if [ ! -f "${binaryPath}" ]; then + echo "ERROR: Binary not found after nix build" exit 1 fi - echo "Build complete: ${appDir}/target/release/ethlambda" + echo "Build complete: ${binaryPath}" - # ── 3. Clone lean-quickstart (for genesis generation scripts) ── + # ── 2. Clone lean-quickstart (for genesis generation scripts) ── if [ ! -d "${quickstartDir}/.git" ]; then echo "Cloning lean-quickstart..." git clone --depth 1 --single-branch \ https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" fi - # ── 4. Write 4-node ethlambda-only validator config ── + # ── 3. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with # unique QUIC and metrics ports. The privkeys below are secp256k1 # P2P identity keys reused from the standard devnet config — they are @@ -275,7 +262,7 @@ validators: count: 1 VCEOF - # ── 5. Generate genesis ── + # ── 4. Generate genesis ── # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) @@ -294,7 +281,7 @@ VCEOF fi done - # ── 6. Create per-node data directories ── + # ── 5. Create per-node data directories ── for i in 0 1 2 3; do mkdir -p "${dataDir}/ethlambda_$i" done @@ -342,8 +329,14 @@ VCEOF yq-go htop # Useful for debugging resource usage podman # For genesis tool access via SSH - rustup # For inspecting/rebuilding ethlambda over SSH ]; + # nix build inside systemd-nspawn containers cannot create nested namespaces, + # so the nix sandbox must be disabled. Flakes are needed for `nix build github:...`. + nix.settings = { + experimental-features = [ "nix-command" "flakes" ]; + sandbox = false; + }; + system.stateVersion = "24.11"; } From 663f35fdac083ff9c69669638052eb75fe0a66c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:47:51 -0300 Subject: [PATCH 21/23] fix: add flake.nix as a dependency --- preview-config.nix | 67 ++++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 20b65938..55a600a4 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -9,8 +9,8 @@ # Each node runs as a separate systemd service with its own QUIC port, metrics # endpoint, and RocksDB data directory. # -# The setup service builds ethlambda via `nix build github:lambdaclass/ethlambda/$BRANCH` -# at container start, so previews always run the exact binary from the branch under review. +# The ethlambda binary is built from the PR branch at Nix eval time via builtins.getFlake, +# so the container image always contains the exact binary from the branch under review. # PREVIEW_BRANCH is available at runtime via /etc/preview.env (injected by tekton). # # Ports: @@ -26,7 +26,7 @@ let meta = { - # Oneshot service that clones, builds, and generates genesis. + # Oneshot service that generates genesis state for the devnet. setupService = "setup-devnet"; # Long-running ethlambda node services started after setup completes. @@ -58,14 +58,17 @@ let extraHosts = []; }; + # Build ethlambda from the current repo at eval time (requires --impure). + # tekton evaluates preview-config.nix from the cloned PR branch, so path:. + # resolves to the branch under review — the container always runs that branch's binary. + ethlambdaPkg = (builtins.getFlake "path:.").packages.x86_64-linux.ethlambda; + # Paths used throughout the config - appDir = "/home/preview/app"; quickstartDir = "/home/preview/lean-quickstart"; genesisDir = "/home/preview/devnet/genesis"; dataDir = "/home/preview/devnet/data"; - # nix build outputs a store path symlinked at appDir; binary is at bin/ethlambda - binaryPath = "${appDir}/bin/ethlambda"; + binaryPath = "${ethlambdaPkg}/bin/ethlambda"; # Helper: create a systemd service for ethlambda node N. # Node 0 runs with --is-aggregator so the devnet can finalize blocks: @@ -141,12 +144,12 @@ in ''; # ── Setup service ───────────────────────────────────────────────────── - # Builds ethlambda via `nix build github:lambdaclass/ethlambda/$PREVIEW_BRANCH`, - # clones lean-quickstart, generates genesis for the 4-node devnet, and creates - # per-node data directories. + # Clones lean-quickstart, generates genesis for the 4-node devnet, and creates + # per-node data directories. The ethlambda binary is already in the container + # image (built from path:. via builtins.getFlake at eval time). systemd.services = { setup-devnet = { - description = "Setup ethlambda 4-node devnet (nix build, genesis)"; + description = "Setup ethlambda 4-node devnet (genesis)"; after = [ "systemd-resolved.service" "network-online.target" ]; wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; @@ -161,7 +164,7 @@ in Type = "oneshot"; RemainAfterExit = true; WorkingDirectory = "/home/preview"; - TimeoutStartSec = "900"; # nix build compiles ethlambda from source on first run + TimeoutStartSec = "300"; # genesis generation (podman image pull + XMSS key gen) }; script = '' set -euo pipefail @@ -175,10 +178,11 @@ in source /etc/preview.env set +a - # On container restart, skip setup if the binary and genesis already exist. - # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. - if [ -f "${binaryPath}" ] \ - && [ -d "${genesisDir}/hash-sig-keys" ] \ + # On container restart, skip setup if genesis already exists. + # Touch /tmp/force-rebuild to force re-genesis on 'preview update'. + # (The ethlambda binary is baked into the container image at eval time — + # no runtime build needed.) + if [ -d "${genesisDir}/hash-sig-keys" ] \ && [ -f "${genesisDir}/config.yaml" ] \ && [ ! -f /tmp/force-rebuild ]; then echo "Devnet already set up, skipping (container restart)." @@ -189,32 +193,14 @@ in # and a new genesis state, so any existing chain state is incompatible. rm -rf "${genesisDir}" "${dataDir}" - # ── 1. Build ethlambda via nix build ── - # Fetches the flake from GitHub and builds the ethlambda binary. - # crane's two-phase build (buildDepsOnly + buildPackage) means Rust - # dependencies are a separate derivation: once compiled they stay in - # /nix/store and only ethlambda itself needs recompiling on branch updates. - PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") - echo "Building ethlambda (branch: $PREVIEW_BRANCH) via nix build..." - nix build "github:lambdaclass/ethlambda/$PREVIEW_BRANCH" \ - --out-link "${appDir}" \ - --extra-experimental-features "nix-command flakes" \ - --option access-tokens "github.com=$PREVIEW_TOKEN" - - if [ ! -f "${binaryPath}" ]; then - echo "ERROR: Binary not found after nix build" - exit 1 - fi - echo "Build complete: ${binaryPath}" - - # ── 2. Clone lean-quickstart (for genesis generation scripts) ── + # ── 1. Clone lean-quickstart (for genesis generation scripts) ── if [ ! -d "${quickstartDir}/.git" ]; then echo "Cloning lean-quickstart..." git clone --depth 1 --single-branch \ https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" fi - # ── 3. Write 4-node ethlambda-only validator config ── + # ── 2. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with # unique QUIC and metrics ports. The privkeys below are secp256k1 # P2P identity keys reused from the standard devnet config — they are @@ -262,7 +248,7 @@ validators: count: 1 VCEOF - # ── 4. Generate genesis ── + # ── 3. Generate genesis ── # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) @@ -281,7 +267,7 @@ VCEOF fi done - # ── 5. Create per-node data directories ── + # ── 4. Create per-node data directories ── for i in 0 1 2 3; do mkdir -p "${dataDir}/ethlambda_$i" done @@ -331,12 +317,5 @@ VCEOF podman # For genesis tool access via SSH ]; - # nix build inside systemd-nspawn containers cannot create nested namespaces, - # so the nix sandbox must be disabled. Flakes are needed for `nix build github:...`. - nix.settings = { - experimental-features = [ "nix-command" "flakes" ]; - sandbox = false; - }; - system.stateVersion = "24.11"; } From 29f01971bf8eeaf56d5de4e654b3fc08d77127c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 15:51:46 -0300 Subject: [PATCH 22/23] fix: get absolute path --- preview-config.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/preview-config.nix b/preview-config.nix index 55a600a4..1427e58f 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -61,7 +61,7 @@ let # Build ethlambda from the current repo at eval time (requires --impure). # tekton evaluates preview-config.nix from the cloned PR branch, so path:. # resolves to the branch under review — the container always runs that branch's binary. - ethlambdaPkg = (builtins.getFlake "path:.").packages.x86_64-linux.ethlambda; + ethlambdaPkg = (builtins.getFlake (toString ./.)).packages.x86_64-linux.ethlambda; # Paths used throughout the config quickstartDir = "/home/preview/lean-quickstart"; From 04a62ac96d5018a7947bb5070b2c170c7834c2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Gr=C3=BCner?= <47506558+MegaRedHand@users.noreply.github.com> Date: Mon, 2 Mar 2026 16:57:18 -0300 Subject: [PATCH 23/23] fix: install clang --- preview-config.nix | 76 +++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/preview-config.nix b/preview-config.nix index 1427e58f..37acb7e3 100644 --- a/preview-config.nix +++ b/preview-config.nix @@ -9,8 +9,8 @@ # Each node runs as a separate systemd service with its own QUIC port, metrics # endpoint, and RocksDB data directory. # -# The ethlambda binary is built from the PR branch at Nix eval time via builtins.getFlake, -# so the container image always contains the exact binary from the branch under review. +# The setup service clones the PR branch and builds ethlambda at container start, +# so previews always run the exact binary from the branch under review. # PREVIEW_BRANCH is available at runtime via /etc/preview.env (injected by tekton). # # Ports: @@ -26,7 +26,7 @@ let meta = { - # Oneshot service that generates genesis state for the devnet. + # Oneshot service that clones, builds, and generates genesis. setupService = "setup-devnet"; # Long-running ethlambda node services started after setup completes. @@ -58,17 +58,13 @@ let extraHosts = []; }; - # Build ethlambda from the current repo at eval time (requires --impure). - # tekton evaluates preview-config.nix from the cloned PR branch, so path:. - # resolves to the branch under review — the container always runs that branch's binary. - ethlambdaPkg = (builtins.getFlake (toString ./.)).packages.x86_64-linux.ethlambda; - # Paths used throughout the config + appDir = "/home/preview/app"; quickstartDir = "/home/preview/lean-quickstart"; genesisDir = "/home/preview/devnet/genesis"; dataDir = "/home/preview/devnet/data"; - binaryPath = "${ethlambdaPkg}/bin/ethlambda"; + binaryPath = "${appDir}/target/release/ethlambda"; # Helper: create a systemd service for ethlambda node N. # Node 0 runs with --is-aggregator so the devnet can finalize blocks: @@ -144,17 +140,16 @@ in ''; # ── Setup service ───────────────────────────────────────────────────── - # Clones lean-quickstart, generates genesis for the 4-node devnet, and creates - # per-node data directories. The ethlambda binary is already in the container - # image (built from path:. via builtins.getFlake at eval time). + # Clones the PR branch, builds ethlambda via nix build, clones lean-quickstart, + # generates genesis for the 4-node devnet, and creates per-node data directories. systemd.services = { setup-devnet = { - description = "Setup ethlambda 4-node devnet (genesis)"; + description = "Setup ethlambda 4-node devnet (clone, build, genesis)"; after = [ "systemd-resolved.service" "network-online.target" ]; wants = [ "systemd-resolved.service" "network-online.target" ]; before = map (idx: "ethlambda-${toString idx}.service") [ 0 1 2 3 ]; path = with pkgs; [ - bash coreutils git + bash coreutils git rustup pkg-config llvmPackages.libclang llvmPackages.clang # Genesis generation (generate-genesis.sh needs these) yq-go podman gawk gnugrep gnused which shadow @@ -164,7 +159,7 @@ in Type = "oneshot"; RemainAfterExit = true; WorkingDirectory = "/home/preview"; - TimeoutStartSec = "300"; # genesis generation (podman image pull + XMSS key gen) + TimeoutStartSec = "900"; # rustup toolchain install + cargo build --release }; script = '' set -euo pipefail @@ -178,11 +173,12 @@ in source /etc/preview.env set +a - # On container restart, skip setup if genesis already exists. - # Touch /tmp/force-rebuild to force re-genesis on 'preview update'. - # (The ethlambda binary is baked into the container image at eval time — - # no runtime build needed.) - if [ -d "${genesisDir}/hash-sig-keys" ] \ + export RUST_LOG=info + + # On container restart, skip setup if the binary and genesis already exist. + # Touch /tmp/force-rebuild to force a full rebuild on 'preview update'. + if [ -f "${binaryPath}" ] \ + && [ -d "${genesisDir}/hash-sig-keys" ] \ && [ -f "${genesisDir}/config.yaml" ] \ && [ ! -f /tmp/force-rebuild ]; then echo "Devnet already set up, skipping (container restart)." @@ -193,14 +189,45 @@ in # and a new genesis state, so any existing chain state is incompatible. rm -rf "${genesisDir}" "${dataDir}" - # ── 1. Clone lean-quickstart (for genesis generation scripts) ── + # ── 1. Clone ethlambda ── + PREVIEW_TOKEN=$(cat /etc/preview-token 2>/dev/null || echo "") + AUTHED_URL=$(echo "$PREVIEW_REPO_URL" | sed "s|https://|https://x-access-token:$PREVIEW_TOKEN@|") + + if [ -d "${appDir}/.git" ]; then + echo "Updating ethlambda repo (branch: $PREVIEW_BRANCH)..." + git -C "${appDir}" remote set-url origin "$AUTHED_URL" + git -C "${appDir}" fetch origin + git -C "${appDir}" reset --hard "origin/$PREVIEW_BRANCH" + else + echo "Cloning ethlambda (branch: $PREVIEW_BRANCH)..." + git clone --depth 1 --branch "$PREVIEW_BRANCH" --single-branch "$AUTHED_URL" "${appDir}" + fi + + # ── 2. Build ethlambda ── + # cargo build instead of nix build: the flake has no binary cache, so nix + # build would recompile from scratch on every preview start (10-15 min). + # rustup reads rust-toolchain.toml automatically and installs the pinned version. + export LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib" + export RUSTUP_HOME=/home/preview/.rustup + export CARGO_HOME=/home/preview/.cargo + cd "${appDir}" + rustup toolchain install # installs from rust-toolchain.toml + cargo build --release --bin ethlambda + + if [ ! -f "${appDir}/target/release/ethlambda" ]; then + echo "ERROR: Binary not found after cargo build" + exit 1 + fi + echo "Build complete: ${appDir}/target/release/ethlambda" + + # ── 3. Clone lean-quickstart (for genesis generation scripts) ── if [ ! -d "${quickstartDir}/.git" ]; then echo "Cloning lean-quickstart..." git clone --depth 1 --single-branch \ https://github.com/blockblaz/lean-quickstart.git "${quickstartDir}" fi - # ── 2. Write 4-node ethlambda-only validator config ── + # ── 4. Write 4-node ethlambda-only validator config ── # Four ethlambda nodes, each with 1 validator, on localhost with # unique QUIC and metrics ports. The privkeys below are secp256k1 # P2P identity keys reused from the standard devnet config — they are @@ -248,7 +275,7 @@ validators: count: 1 VCEOF - # ── 3. Generate genesis ── + # ── 5. Generate genesis ── # Runs lean-quickstart's genesis generator which uses container images to: # 1. Generate XMSS key pairs (hash-sig-cli) # 2. Generate genesis state (eth-beacon-genesis) @@ -267,7 +294,7 @@ VCEOF fi done - # ── 4. Create per-node data directories ── + # ── 6. Create per-node data directories ── for i in 0 1 2 3; do mkdir -p "${dataDir}/ethlambda_$i" done @@ -315,6 +342,7 @@ VCEOF yq-go htop # Useful for debugging resource usage podman # For genesis tool access via SSH + rustup # For inspecting/rebuilding ethlambda over SSH ]; system.stateVersion = "24.11";