Summary
We are WalletScrutiny, an independent project that verifies whether published wallet binaries can be reproduced from source. We attempted to reproduce liana-13.1-noncodesigned.exe from the v13.1 source tag using the Nix flake build
(nix build .#x86_64-pc-windows-gnu) and found a hash mismatch against the official release artifact. We want to share our findings and ask whether we are missing a step in the build environment setup.
We are open to the possibility that we made an error. If there is something about your build environment that we should be replicating, we would appreciate the guidance.
Environment
|
Our build |
| Host OS |
Ubuntu 24.04 LTS (x86_64) |
| Container |
nixos/nix:2.24.10 (Docker, --privileged) |
| Nix config |
experimental-features = nix-command flakes, sandbox = false |
| Build command |
nix build .#x86_64-pc-windows-gnu |
| Source |
git clone --depth=1 --branch v13.1 → commit 282520ffeba1d56a255bc2d6e8dcb1011441e75c |
flake.lock |
Used as-is from the tag — no modifications |
Hash Comparison
|
SHA256 |
Size |
Our build (liana-gui.exe) |
e6c63491f54454182b0bf19ecb55480a81f1d25a90a909b46e6c1552b2e33c1f |
75,598,728 bytes |
Official (liana-13.1-noncodesigned.exe) |
d417e014eca2f2c2d74d630086f80707a5b5ed1b8d2df4d364d2e8b521c26f06 |
75,599,580 bytes |
| Delta |
— |
−852 bytes |
Our build was self-consistent: running the same nix build .#x86_64-pc-windows-gnu command
twice (once via our automated script, once manually inside a fresh container) produced the
same hash both times.
Investigation Steps
1. Verified the correct build attribute
We inspected flake.nix and contrib/release/release.sh. The release script calls
nix build .#release and then copies the exe from the result:
# release.sh line 45–46
nix build .#release
NIX_BUILD_DIR="$(nix path-info .#release)"
# line 68
cp "$NIX_BUILD_DIR/x86_64-pc-windows-gnu/liana-gui.exe" "$RELEASE_DIR/$LIANA_PREFIX-noncodesigned.exe"
flake.nix lines 199–206 show that .#release is a pkgs.buildEnv wrapping the
x86_64-pc-windows-gnu derivation — not a separate build. So the exe inside .#release
should be byte-identical to the standalone nix build .#x86_64-pc-windows-gnu output.
We attempted nix build .#release but it requires the Xcode 12.2 SDK for the macOS
targets, which we cannot provide. We could not test the .#release path end-to-end.
2. Verified no post-processing
release.sh applies only a plain cp to the exe — no strip, objcopy, wine, or
PE-modifying step. The published noncodesigned.exe should be byte-identical to the Nix
store output.
3. Binary diff analysis
Total differing bytes: 22,820,507 out of 75,598,728 (~30% of binary)
First differing offset: 0x000000d8 (PE header area)
Sampled section mapping (first 40 diff positions):
PE header / DOS stub → 8 differing bytes, first at 0x000000d8
.text → 32 differing bytes, first at 0x00000418
The scale of the diff (~30% of the binary) and its presence in the .text section from
the very start of the code segment suggests a code generation difference rather than a
metadata-only difference (e.g. a PE timestamp would be only 4 bytes). Note: the section
mapping above covers only the first 40 sampled positions, not the full binary.
Both binaries embed the same crane vendor placeholder (eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee)
in embedded source paths, though a strings comparison also revealed differing Nix store
path strings that we could not fully interpret due to adjacent binary context bytes.
Hypothesis
With flake.lock pinning all inputs, we would expect the build to be deterministic across
environments. Our current hypothesis is that the Nix binary version used at release time
differs from 2.24.10, and that this produces a different evaluation outcome or different
binary cache substitutes are fetched. We have not been able to confirm this.
We may be wrong. There could be a build step we are not replicating — for example,
the nix develop .#release shell environment exporting variables that affect the build, or
a requirement to run on a specific OS or kernel version.
Questions
- What version of Nix was used to produce the v13.1 release artifacts?
- Was the release built inside
nix develop .#release before calling nix build, and if
so, does that shell export any variables that affect the Windows build derivation?
- Is there any known reason why
nix build .#x86_64-pc-windows-gnu would not reproduce
the exe shipped in the release, given the same flake.lock?
Note on Linux artifacts
For reference, the Linux artifacts for v13.1 did reproduce successfully in our testing:
liana-13.1-x86_64-linux-gnu.tar.gz — all three binaries (lianad, liana-cli,
liana-gui) matched the official release byte-for-byte via the Guix build pipeline.
liana-13.1-1_amd64.deb — binaries extracted from the deb matched the Guix output.
The non-reproducibility appears isolated to the Windows exe.
Summary
We are WalletScrutiny, an independent project that verifies whether published wallet binaries can be reproduced from source. We attempted to reproduce
liana-13.1-noncodesigned.exefrom the v13.1 source tag using the Nix flake build(
nix build .#x86_64-pc-windows-gnu) and found a hash mismatch against the official release artifact. We want to share our findings and ask whether we are missing a step in the build environment setup.We are open to the possibility that we made an error. If there is something about your build environment that we should be replicating, we would appreciate the guidance.
Environment
nixos/nix:2.24.10(Docker,--privileged)experimental-features = nix-command flakes,sandbox = falsenix build .#x86_64-pc-windows-gnugit clone --depth=1 --branch v13.1→ commit282520ffeba1d56a255bc2d6e8dcb1011441e75cflake.lockHash Comparison
liana-gui.exe)e6c63491f54454182b0bf19ecb55480a81f1d25a90a909b46e6c1552b2e33c1fliana-13.1-noncodesigned.exe)d417e014eca2f2c2d74d630086f80707a5b5ed1b8d2df4d364d2e8b521c26f06Our build was self-consistent: running the same
nix build .#x86_64-pc-windows-gnucommandtwice (once via our automated script, once manually inside a fresh container) produced the
same hash both times.
Investigation Steps
1. Verified the correct build attribute
We inspected
flake.nixandcontrib/release/release.sh. The release script callsnix build .#releaseand then copies the exe from the result:flake.nixlines 199–206 show that.#releaseis apkgs.buildEnvwrapping thex86_64-pc-windows-gnuderivation — not a separate build. So the exe inside.#releaseshould be byte-identical to the standalone
nix build .#x86_64-pc-windows-gnuoutput.We attempted
nix build .#releasebut it requires the Xcode 12.2 SDK for the macOStargets, which we cannot provide. We could not test the
.#releasepath end-to-end.2. Verified no post-processing
release.shapplies only a plaincpto the exe — nostrip,objcopy,wine, orPE-modifying step. The published
noncodesigned.exeshould be byte-identical to the Nixstore output.
3. Binary diff analysis
The scale of the diff (~30% of the binary) and its presence in the
.textsection fromthe very start of the code segment suggests a code generation difference rather than a
metadata-only difference (e.g. a PE timestamp would be only 4 bytes). Note: the section
mapping above covers only the first 40 sampled positions, not the full binary.
Both binaries embed the same crane vendor placeholder (
eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee)in embedded source paths, though a
stringscomparison also revealed differing Nix storepath strings that we could not fully interpret due to adjacent binary context bytes.
Hypothesis
With
flake.lockpinning all inputs, we would expect the build to be deterministic acrossenvironments. Our current hypothesis is that the Nix binary version used at release time
differs from
2.24.10, and that this produces a different evaluation outcome or differentbinary cache substitutes are fetched. We have not been able to confirm this.
We may be wrong. There could be a build step we are not replicating — for example,
the
nix develop .#releaseshell environment exporting variables that affect the build, ora requirement to run on a specific OS or kernel version.
Questions
nix develop .#releasebefore callingnix build, and ifso, does that shell export any variables that affect the Windows build derivation?
nix build .#x86_64-pc-windows-gnuwould not reproducethe exe shipped in the release, given the same
flake.lock?Note on Linux artifacts
For reference, the Linux artifacts for v13.1 did reproduce successfully in our testing:
liana-13.1-x86_64-linux-gnu.tar.gz— all three binaries (lianad,liana-cli,liana-gui) matched the official release byte-for-byte via the Guix build pipeline.liana-13.1-1_amd64.deb— binaries extracted from the deb matched the Guix output.The non-reproducibility appears isolated to the Windows exe.