From 5f1a4014737ee87796e20b3caca28ddb8073d2e5 Mon Sep 17 00:00:00 2001 From: amackillop Date: Fri, 5 Jun 2026 06:47:35 -0700 Subject: [PATCH] Add Nix dev shell with pinned test binaries Introduce a reproducible development environment so the project builds and tests the same way on any machine. The flake provides the pinned Rust toolchain, nightly rustfmt, and the bitcoind/electrs binaries the integration tests need; .envrc loads it via direnv and the justfile wraps the common check/fmt/test commands to mirror CI's gates. bitcoind is pinned to 27.1 from an older nixpkgs rev because the tests' bundled corepc-node only decodes the 27.x getblockchaininfo RPC schema; the current 25.11 channel ships 30.0 and fails decoding. For electrs we package the exact prebuilt esplora binary CI downloads and patch it for NixOS rather than using nixpkgs' blockstream-electrs. The nixpkgs build is much newer and its initial regtest indexing takes around 80 seconds, which overruns the tests' chain sync timeout and makes every integration test fail locally. Building that old revision from source under the current toolchain is fragile, so reusing the same binary CI runs is the cheapest way to match its behaviour. The prebuilt only exists for x86_64 linux and macOS, the two platforms the download script supports. --- .envrc | 1 + flake.lock | 113 ++++++++++++++++++++++++++++++++++++++++ flake.nix | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++ justfile | 23 ++++++++ 4 files changed, 287 insertions(+) create mode 100644 .envrc create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 justfile diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..3550a30f2 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..52fabf4e2 --- /dev/null +++ b/flake.lock @@ -0,0 +1,113 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1764521362, + "narHash": "sha256-M101xMtWdF1eSD0xhiR8nG8CXRlHmv6V+VoY65Smwf4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "871b9fd269ff6246794583ce4ee1031e1da71895", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-bitcoind": { + "locked": { + "lastModified": 1727390236, + "narHash": "sha256-X2LaWM0WwoxUfu7cQmKyVbkYn0xwdRvBA+vkLg/OgnI=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ab7b6889ae9d484eed2876868209e33eb262511d", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ab7b6889ae9d484eed2876868209e33eb262511d", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "nixpkgs-bitcoind": "nixpkgs-bitcoind", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1777950921, + "narHash": "sha256-NpOgt8ISaHTDNJZjNUfwFfbieKfRXzab4WKM31gZCGA=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "366ea19e0e55b768f74b7a0b2a20f847e7ae828d", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..4d6435815 --- /dev/null +++ b/flake.nix @@ -0,0 +1,150 @@ +{ + description = "LDK Node Development Environment"; + + inputs = { + # Nixpkgs channel. New channels are released every 6 months. + # See: https://github.com/NixOS/nixpkgs/tags + nixpkgs.url = "github:nixos/nixpkgs/25.11"; + + # This makes it easy for the flake to be multi-platform. + # See: https://github.com/numtide/flake-utils + flake-utils.url = "github:numtide/flake-utils"; + + # Provides Rust toolchains. + # See: https://github.com/oxalica/rust-overlay + rust-overlay.url = "github:oxalica/rust-overlay"; + + # Pinned nixpkgs that ships bitcoind 27.1. The integration tests' bundled + # `corepc-node` deserializes the 27.x `getblockchaininfo` schema, so newer + # bitcoind (25.11 ships 30.0) fails RPC decoding. Used only for BITCOIND_EXE. + nixpkgs-bitcoind.url = "github:nixos/nixpkgs/ab7b6889ae9d484eed2876868209e33eb262511d"; + }; + + outputs = + { + self, + nixpkgs, + flake-utils, + rust-overlay, + nixpkgs-bitcoind, + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + # Overlays provide additional packages not available in the channels. + overlays = [ + # Provides the rust-bin package; a set of pre-built Rust toolchains. + (import rust-overlay) + ]; + + # The final set of packages. + pkgs = import nixpkgs { + # Inheriting from system is what makes this multi-platform. + # We also inherit the overlays that we want to use. + inherit system overlays; + }; + + # bitcoind 27.1 from the pinned input (see inputs above). + bitcoind = nixpkgs-bitcoind.legacyPackages.${system}.bitcoind; + + # The specific Rust toolchain that we use in development shells. + # Matches the `rust-version` declared in Cargo.toml. We use the `minimal` + # profile (rustc, cargo, rust-std) plus clippy and rust-src, but + # deliberately exclude `rustfmt`: this repo's `rustfmt.toml` relies on + # nightly-only options, so formatting is supplied by `rustfmt-nightly` + # below to keep `just fmt`/`just check` consistent with CI's nightly job. + rust-toolchain = pkgs.rust-bin.stable."1.85.1".minimal.override { + extensions = [ + "rust-src" # Needed for the rust-analyzer extension to work. + "clippy" # Linter used by `just check`. + ]; + }; + + # Nightly rustfmt only. `cargo fmt` shells out to whichever `rustfmt` is + # on PATH, so a nightly rustfmt lets the nightly-only options in + # `rustfmt.toml` apply even though cargo/rustc are pinned to stable. + rustfmt-nightly = pkgs.rust-bin.nightly.latest.rustfmt; + + # Esplora/HTTP electrs for the integration tests' Esplora chain source. + # + # We deliberately do NOT use nixpkgs' `blockstream-electrs`: it is a far + # newer build whose initial regtest indexing takes ~80s, blowing past the + # tests' sync timeout. Instead we pin the *exact* prebuilt binary CI uses + # (see scripts/download_bitcoind_electrs.sh) and patch it to run on Nix. + # This keeps local test behaviour identical to CI. + electrs-esplora = + let + rev = "a33e97e1a1fc63fa9c20a116bb92579bbf43b254"; + sources = { + x86_64-linux = { + file = "electrs_linux_esplora_${rev}.zip"; + sha256 = "865e26a96e8df77df01d96f2f569dcf9622fc87a8d99a9b8fe30861a4db9ddf1"; + }; + x86_64-darwin = { + file = "electrs_macos_esplora_${rev}.zip"; + sha256 = "2d5ff149e8a2482d3658e9b386830dfc40c8fbd7c175ca7cbac58240a9505bcd"; + }; + }; + src = + sources.${system} + or (throw "no prebuilt esplora electrs for ${system}"); + in + pkgs.stdenv.mkDerivation { + pname = "electrs-esplora"; + version = "esplora-${builtins.substring 0 9 rev}"; + src = pkgs.fetchurl { + url = "https://github.com/RCasatta/electrsd/releases/download/electrs_releases/${src.file}"; + inherit (src) sha256; + }; + nativeBuildInputs = + [ pkgs.unzip ] ++ pkgs.lib.optional pkgs.stdenv.isLinux pkgs.autoPatchelfHook; + buildInputs = pkgs.lib.optionals pkgs.stdenv.isLinux [ pkgs.stdenv.cc.cc.lib ]; + unpackPhase = '' + runHook preUnpack + unzip "$src" + runHook postUnpack + ''; + installPhase = '' + runHook preInstall + install -Dm755 electrs "$out/bin/electrs" + runHook postInstall + ''; + }; + + in + { + # The development shell. Use `nix develop` or direnv to enter it. + devShells.default = pkgs.mkShell { + name = "ldk-node-dev-shell"; + + packages = with pkgs; [ + rustfmt-nightly # Nightly rustfmt; must precede the stable toolchain on PATH. + rust-toolchain # Rust toolchain (no rustfmt; see above). + nodejs # JavaScript runtime, required for MCP tools + mold # Fast linker for Rust/C/C++ + pnpm # Package manager for JavaScript, required for MCP tools + pkg-config # Required by build scripts that link against system libraries + just # Command runner used for `just check`, `just fmt`, etc. + stdenv.cc.cc.lib # C++ standard library for runtime + ]; + + env = { + # OpenSSL configuration for Nix + PKG_CONFIG_PATH = "${pkgs.openssl.dev}/lib/pkgconfig"; + # C++ standard library path for runtime + LD_LIBRARY_PATH = "${pkgs.stdenv.cc.cc.lib}/lib"; + # Integration tests (run with `--cfg no_download`) locate these via env + # rather than downloading generic-linux binaries that can't run on NixOS. + BITCOIND_EXE = "${bitcoind}/bin/bitcoind"; + ELECTRS_EXE = "${electrs-esplora}/bin/electrs"; + }; + + shellHook = '' + echo "LDK Node dev shell" + rustc --version + cargo --version + ''; + }; + } + ); +} diff --git a/justfile b/justfile new file mode 100644 index 000000000..a603aee75 --- /dev/null +++ b/justfile @@ -0,0 +1,23 @@ +default: + @just --list --unsorted + +# Validate: rustfmt check + clippy with warnings denied (matches CI's deny-warnings gate). +check: + cargo fmt --all -- --check + cargo clippy --all-targets -- -D warnings + +# Format all sources in place. +fmt: + cargo fmt --all + +# Apply clippy autofixes across all targets. +fix: + cargo clippy --all-targets --fix --allow-dirty --allow-staged + +# Run library unit tests only. No bitcoind/electrs required. +test: + RUSTFLAGS="-D warnings" cargo test --lib + +# Run full suite incl. integration tests. Needs bitcoind+electrs on PATH (CI's --cfg no_download). +test-all: + RUSTFLAGS="--cfg no_download -D warnings" cargo test