Source bitcoin-node from the bitcoind-gunix flake #6

Merged
vnprc merged 1 commit from agent/bitcoind-gunix-migration into master 2026-07-17 22:50:30 +01:00
Collaborator

Sources the multiprocess Bitcoin Core node from a single flake-locked bitcoind-gunix input whose build verifies byte-for-byte parity with the official GUIX release, replacing the three hand-maintained version+sha256 pins (bitcoin-node.nix, and the BITCOIN_VERSION download blocks in scripts/build.sh + scripts/ship.sh). The Core version and every runtime invocation are unchanged — this is a version-neutral packaging/transport swap that ships byte-identical binaries (official 31.1 tarball b80d9c3e04da78fb6f0569685673418cf686fadba9042d926d13fb87ff503f9e).

Implements the dev plan notes/hashpool/dev-plans/bitcoind-gunix-migration.md.

Closes vnprc/hashpool#4
Refs vnprc/hashpool#2

What changed

  • New bitcoin-node-gunix.nix — pin-free adapter { pkgs, lib, gunix } returning { tarball, release, patched }. release (unpatched, dontFixup, keeps the FHS interpreter /lib64/ld-linux-x86-64.so.2) is the deploy artifact; patched (autoPatchelf + gcc.cc.lib) is the NixOS/devenv PATH variant. Both take gunix.tarball as src — not gunix.bitcoind — because on the v31.1 branch only the tarball derivation carries the sha256 parity gate, so every nix build of a node attr forces the gate transitively. Contains no version string, no hash, no URL.
  • flake.nix — new input bitcoind-gunix.url = "github:0xB10C/bitcoind-gunix/v31.1" (deliberately no inputs.nixpkgs.follows — byte parity depends on gunix's exact pinned toolchain). Exposes packages.bitcoin-node (patched; name/role preserved), packages.bitcoin-node-release, and packages.bitcoin-node-tarball, guarded by lib.optionalAttrs (bitcoind-gunix.packages ? ${system}) so unsupported systems stay eval-clean.
  • devenv.yaml / devenv.nix — declare the same input with the byte-identical ref string; the adapter's patched output reaches PATH exactly as before. The old bitcoin-node.nix import is removed.
  • scripts/ship.sh / scripts/build.sh — the node binaries are now built locally with nix build .#bitcoin-node-release and staged/rsynced to the VPS. The unpatched FHS variant passes the existing check_nix_abi gate, which structurally refuses a /nix/store interpreter — so staging the wrong variant is impossible to miss. The bitcoincore.org node curl, its /tmp cache guard, and the BITCOIN_VERSION pin are removed. The sv2-tp download / cache / staging is byte-for-byte unchanged in both scripts. build.sh --dry-run now returns before the VPS ssh (agent-runnable), and both scripts preflight nix.
  • Cleanup — deletes the dead bitcoind.nix (legacy Sjors sv2-fork binary, imported by nothing) and its update-bitcoind justfile recipe; adds a one-line README mechanism note.
  • Locksflake.lock and devenv.lock both gain bitcoind-gunix at the same rev 4599977e9723e45ce4a16dd076b43f2d8b02b042.

Risk

R2 (Medium), per the plan. The staged production binaries hash-match the official release tarball that both the previous curl path and vnprc/hashpool#2's fetchurl pin already ship, and there is no Core version change — so no datadir/chainstate migration and no downgrade hazard. The only real behavior change is deploy transport (node sourcing moves from a VPS-side curl to a local nix build + rsync). Worst credible post-merge failure is a prod-path script edge case leaving /opt/hashpool with a wrong or missing node binary: blast radius is one VPS service, detected immediately at the human-attended deploy (with check_nix_abi and the staged-hash check as tripwires), and recovered by a single-commit revert + redeploy. There is no config, schema, secret, or generated state to reconcile in either direction.

Validation

Verified in the agent environment (no gunix build required):

  • Flake evaluates. packages.x86_64-linux exposes bitcoin-node, bitcoin-node-release, bitcoin-node-tarball; all three resolve to derivation paths, and release/patched src both equal gunix.tarball's output (the gate-chaining contract).
  • AT0flake.lock and devenv.lock pin the same gunix rev (4599977e…); the ref string appears exactly once in flake.nix and once in devenv.yaml.
  • AT2 byte-identity (gate-hash cross-check, no build) — gunix v31.1's checked-in gate noncodesigned.SHA256SUMS and bitcoincore.org's signed SHA256SUMS both publish b80d9c3e… for bitcoin-31.1-x86_64-linux-gnu.tar.gz. Because gunix fails the build on any mismatch, a successful v31.1 build produces the official byte-identical tarball by construction; gunix CI is green at this rev.
  • AT4 — no BITCOIN_VERSION or bitcoincore.org remains in scripts/; bitcoin-node.nix and bitcoind.nix are gone; no dangling imports; SV2_TP_VERSION retained in both scripts.
  • AT6 dry-runs./scripts/ship.sh all --dry-run and ./scripts/build.sh --dry-run both pass their local nix preflight without touching the VPS.

Deferred to a build-capable environment — this VM's disk cannot fit gunix's tens-of-GB from-source build, and the project's 0xb10c-bitcoind-gunix Cachix cache currently has no v31.1 paths (confirmed 404 + nothing substitutable): AT1 (local build of the three attrs), AT2 (local tarball-hash equality against b80d9c3e…), AT2b (extraction determinism), AT3 (readelf variant properties), and AT3b (devenv shell node version). These are mechanical and low-risk given the static verification above.

Human-gated per the plan: AT5 live regtest devenv up IPC smoke test; the signed master merge; and the ride-along testnet4 production deploy (AT7), whose only added operator duty is the staged-binary hash check before trusting the restart.

Reviewer focus

  • The two deploy-script diffs — the only real behavior change. Confirm the sv2-tp blocks are untouched and the node staging has no conditional/cache path left.
  • bitcoind-gunix rev identical in both locks and at/after 4599977e. Note: the transitive nixpkgs differs between the two locks — the flake inherits gunix's own locked 6b316287, while devenv re-resolves the branch to a newer 4382ed2b. This rev-skew is plan-accepted: each side's build re-verifies parity through the gate, and AT0 only requires the gunix rev itself to match. A follows to force them equal is deliberately avoided (it would destroy the byte reproduction).
  • The adapter derives from tarball, not bitcoind.
Sources the multiprocess Bitcoin Core node from a single flake-locked `bitcoind-gunix` input whose build verifies byte-for-byte parity with the official GUIX release, replacing the three hand-maintained version+sha256 pins (`bitcoin-node.nix`, and the `BITCOIN_VERSION` download blocks in `scripts/build.sh` + `scripts/ship.sh`). The Core version and every runtime invocation are unchanged — this is a version-neutral packaging/transport swap that ships byte-identical binaries (official 31.1 tarball `b80d9c3e04da78fb6f0569685673418cf686fadba9042d926d13fb87ff503f9e`). Implements the dev plan `notes/hashpool/dev-plans/bitcoind-gunix-migration.md`. Closes vnprc/hashpool#4 Refs vnprc/hashpool#2 ## What changed - **New `bitcoin-node-gunix.nix`** — pin-free adapter `{ pkgs, lib, gunix }` returning `{ tarball, release, patched }`. `release` (unpatched, `dontFixup`, keeps the FHS interpreter `/lib64/ld-linux-x86-64.so.2`) is the deploy artifact; `patched` (autoPatchelf + `gcc.cc.lib`) is the NixOS/devenv PATH variant. Both take `gunix.tarball` as `src` — not `gunix.bitcoind` — because on the v31.1 branch only the tarball derivation carries the sha256 parity gate, so every `nix build` of a node attr forces the gate transitively. Contains no version string, no hash, no URL. - **`flake.nix`** — new input `bitcoind-gunix.url = "github:0xB10C/bitcoind-gunix/v31.1"` (deliberately **no** `inputs.nixpkgs.follows` — byte parity depends on gunix's exact pinned toolchain). Exposes `packages.bitcoin-node` (patched; name/role preserved), `packages.bitcoin-node-release`, and `packages.bitcoin-node-tarball`, guarded by `lib.optionalAttrs (bitcoind-gunix.packages ? ${system})` so unsupported systems stay eval-clean. - **`devenv.yaml` / `devenv.nix`** — declare the same input with the byte-identical ref string; the adapter's `patched` output reaches PATH exactly as before. The old `bitcoin-node.nix` import is removed. - **`scripts/ship.sh` / `scripts/build.sh`** — the node binaries are now built locally with `nix build .#bitcoin-node-release` and staged/rsynced to the VPS. The unpatched FHS variant passes the existing `check_nix_abi` gate, which structurally refuses a `/nix/store` interpreter — so staging the wrong variant is impossible to miss. The `bitcoincore.org` node curl, its `/tmp` cache guard, and the `BITCOIN_VERSION` pin are removed. **The sv2-tp download / cache / staging is byte-for-byte unchanged in both scripts.** `build.sh --dry-run` now returns before the VPS ssh (agent-runnable), and both scripts preflight `nix`. - **Cleanup** — deletes the dead `bitcoind.nix` (legacy Sjors sv2-fork binary, imported by nothing) and its `update-bitcoind` justfile recipe; adds a one-line README mechanism note. - **Locks** — `flake.lock` and `devenv.lock` both gain `bitcoind-gunix` at the same rev `4599977e9723e45ce4a16dd076b43f2d8b02b042`. ## Risk **R2 (Medium)**, per the plan. The staged production binaries hash-match the official release tarball that both the previous curl path and vnprc/hashpool#2's fetchurl pin already ship, and there is **no Core version change — so no datadir/chainstate migration and no downgrade hazard**. The only real behavior change is deploy transport (node sourcing moves from a VPS-side curl to a local `nix build` + rsync). Worst credible post-merge failure is a prod-path script edge case leaving `/opt/hashpool` with a wrong or missing node binary: blast radius is one VPS service, detected immediately at the human-attended deploy (with `check_nix_abi` and the staged-hash check as tripwires), and recovered by a single-commit revert + redeploy. There is no config, schema, secret, or generated state to reconcile in either direction. ## Validation Verified in the agent environment (no gunix build required): - **Flake evaluates.** `packages.x86_64-linux` exposes `bitcoin-node`, `bitcoin-node-release`, `bitcoin-node-tarball`; all three resolve to derivation paths, and `release`/`patched` `src` both equal `gunix.tarball`'s output (the gate-chaining contract). - **AT0** — `flake.lock` and `devenv.lock` pin the same gunix rev (`4599977e…`); the ref string appears exactly once in `flake.nix` and once in `devenv.yaml`. - **AT2 byte-identity (gate-hash cross-check, no build)** — gunix v31.1's checked-in gate `noncodesigned.SHA256SUMS` and bitcoincore.org's signed `SHA256SUMS` both publish `b80d9c3e…` for `bitcoin-31.1-x86_64-linux-gnu.tar.gz`. Because gunix fails the build on any mismatch, a successful v31.1 build produces the official byte-identical tarball by construction; gunix CI is green at this rev. - **AT4** — no `BITCOIN_VERSION` or `bitcoincore.org` remains in `scripts/`; `bitcoin-node.nix` and `bitcoind.nix` are gone; no dangling imports; `SV2_TP_VERSION` retained in both scripts. - **AT6 dry-runs** — `./scripts/ship.sh all --dry-run` and `./scripts/build.sh --dry-run` both pass their local `nix` preflight without touching the VPS. Deferred to a build-capable environment — this VM's disk cannot fit gunix's tens-of-GB from-source build, and the project's `0xb10c-bitcoind-gunix` Cachix cache currently has no v31.1 paths (confirmed 404 + nothing substitutable): **AT1** (local build of the three attrs), **AT2** (local tarball-hash equality against `b80d9c3e…`), **AT2b** (extraction determinism), **AT3** (readelf variant properties), and **AT3b** (`devenv shell` node version). These are mechanical and low-risk given the static verification above. Human-gated per the plan: **AT5** live regtest `devenv up` IPC smoke test; the signed `master` merge; and the ride-along testnet4 production deploy (AT7), whose only added operator duty is the staged-binary hash check before trusting the restart. ## Reviewer focus - The two deploy-script diffs — the only real behavior change. Confirm the sv2-tp blocks are untouched and the node staging has no conditional/cache path left. - `bitcoind-gunix` rev identical in both locks and at/after `4599977e`. Note: the *transitive* `nixpkgs` differs between the two locks — the flake inherits gunix's own locked `6b316287`, while devenv re-resolves the branch to a newer `4382ed2b`. This rev-skew is plan-accepted: each side's build re-verifies parity through the gate, and AT0 only requires the gunix rev itself to match. A `follows` to force them equal is deliberately avoided (it would destroy the byte reproduction). - The adapter derives from `tarball`, not `bitcoind`.
Replace the hand-maintained bitcoin-node.nix (version + sha256) and the
BITCOIN_VERSION download pins in scripts/build.sh and scripts/ship.sh with a
single flake-locked bitcoind-gunix input whose build verifies byte-for-byte
parity with the official GUIX release. Core stays 31.1 and every runtime
invocation is unchanged; the staged binaries are byte-identical to the official
31.1 tarball (b80d9c3e04da78fb6f0569685673418cf686fadba9042d926d13fb87ff503f9e).

- New bitcoin-node-gunix.nix adapter: { tarball, release, patched }, all derived
  from gunix's gate-verified tarball (not bitcoind), so every node build forces
  the parity gate transitively. No version, no hash, no URL.
- flake.nix: add bitcoind-gunix input (no nixpkgs.follows); expose bitcoin-node
  (patched), bitcoin-node-release (unpatched FHS), bitcoin-node-tarball under an
  optionalAttrs system guard.
- devenv.yaml/devenv.nix: same input (identical ref string); PATH node comes
  from the adapter's patched variant.
- Deploy scripts: node binaries built locally via nix build and staged/rsynced;
  the unpatched FHS variant passes check_nix_abi. sv2-tp path left untouched.
- Delete dead bitcoind.nix + update-bitcoind justfile recipe; README note.
- flake.lock + devenv.lock both pin gunix rev 4599977e.

Implements notes/hashpool/dev-plans/bitcoind-gunix-migration.md
Refs vnprc/hashpool#2
Author
Collaborator

Read-only implementation review

Ran an independent adversarial review of the migration commit (c1eca3c3), weighted toward the two deploy scripts where all behavioral risk lives. No blockers or majors — every Interface Contract in the plan is correctly implemented, and the highest-risk surface (the deploy scripts) is sound, including the adversarial case of rsync overwriting a pre-existing read-only /tmp/bitcoin from an old curl-based run, and the set -e / set -u edge paths (BITCOIN_NODE_RELEASE is always set before stage_binaries reads it, and is correctly not declared local, so a nix build failure aborts rather than masks).

Verified clean: the sv2-tp download/cache/staging is byte-for-byte unchanged in both scripts; no if-not-exists node cache path remains; require_cmd nix preflights even the --skip-build path; build.sh --dry-run returns before the ssh; the adapter derives release/patched from gunix.tarball (not bitcoind) with dontFixup on release; there is no inputs.nixpkgs.follows; both locks pin the same gunix rev; and the deletions leave no dangling references.

Minor notes for the signed-merge reviewer (no code change made — surfacing for your call):

  • Diff base sanity: reviewing against a stale local master initially made it look like the branch also carried vnprc/hashpool#2 and a cdk-ehash bump. Confirmed a false alarm — against the forge's master (7fe5780b, which already contains #2), this PR adds exactly one commit (c1eca3c3) touching only the 12 planned files.
  • nix build and flakes (optional env hardening): scripts/build.sh and scripts/ship.sh call bare nix build .#bitcoin-node-release, matching the plan's acceptance-test convention. Any hashpool dev/deploy machine already runs devenv/flakes, so this is fine in practice; if you want the deploy scripts self-contained regardless of a machine's nix.conf, add --extra-experimental-features 'nix-command flakes' to those two calls.
  • Dead branch (nit): the remote heredoc's if [ "${DRY_RUN:-0}" -eq 1 ] early-exit in build.sh is now unreachable (the local dry-run returns before the ssh). Harmless; removable for clarity.

Deferred (build-realization) and human-gated items are listed in the PR description.

## Read-only implementation review Ran an independent adversarial review of the migration commit (`c1eca3c3`), weighted toward the two deploy scripts where all behavioral risk lives. **No blockers or majors** — every Interface Contract in the plan is correctly implemented, and the highest-risk surface (the deploy scripts) is sound, including the adversarial case of rsync overwriting a pre-existing read-only `/tmp/bitcoin` from an old curl-based run, and the `set -e` / `set -u` edge paths (`BITCOIN_NODE_RELEASE` is always set before `stage_binaries` reads it, and is correctly not declared `local`, so a `nix build` failure aborts rather than masks). Verified clean: the sv2-tp download/cache/staging is byte-for-byte unchanged in both scripts; no if-not-exists node cache path remains; `require_cmd nix` preflights even the `--skip-build` path; `build.sh --dry-run` returns before the ssh; the adapter derives `release`/`patched` from `gunix.tarball` (not `bitcoind`) with `dontFixup` on `release`; there is no `inputs.nixpkgs.follows`; both locks pin the same gunix rev; and the deletions leave no dangling references. Minor notes for the signed-merge reviewer (no code change made — surfacing for your call): - **Diff base sanity:** reviewing against a stale local `master` initially made it look like the branch also carried vnprc/hashpool#2 and a cdk-ehash bump. Confirmed a false alarm — against the forge's `master` (`7fe5780b`, which already contains #2), this PR adds exactly one commit (`c1eca3c3`) touching only the 12 planned files. - **`nix build` and flakes (optional env hardening):** `scripts/build.sh` and `scripts/ship.sh` call bare `nix build .#bitcoin-node-release`, matching the plan's acceptance-test convention. Any hashpool dev/deploy machine already runs devenv/flakes, so this is fine in practice; if you want the deploy scripts self-contained regardless of a machine's `nix.conf`, add `--extra-experimental-features 'nix-command flakes'` to those two calls. - **Dead branch (nit):** the remote heredoc's `if [ "${DRY_RUN:-0}" -eq 1 ]` early-exit in `build.sh` is now unreachable (the local dry-run returns before the ssh). Harmless; removable for clarity. Deferred (build-realization) and human-gated items are listed in the PR description.
vnprc approved these changes 2026-07-17 22:50:24 +01:00
vnprc merged commit c1eca3c347 into master 2026-07-17 22:50:30 +01:00
vnprc deleted branch agent/bitcoind-gunix-migration 2026-07-17 22:50:31 +01:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
vnprc/hashpool!6
No description provided.