From 8f8a02e8ea3358ddec043bb9b1f615d0d1fc9fc7 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 19 Aug 2026 18:17:16 +0000 Subject: build: pin the dependencies that decide consensus, and check what we run The two dependency clones were `git clone --depth 1 ` with no revision, which resolves to whatever the default branch points at the moment the build happens to run. For RandomX that is the proof of work. Its master is not frozen -- v2.0 changed the algorithm in March -- so two builds of the same Bitflash commit, made weeks apart, can hash the same header differently. That is not a supply-chain worry, it is a chain split delivered as a build artifact, with no version recorded anywhere to explain it afterwards. For libsecp256k1 it is signature verification, the rule deciding who may spend. Both now fetch a full commit id, which is the only form here that verifies what it got, since git resolves the object by its own hash. The RandomX pin is the commit this tree has been building and syncing against, so today's behaviour is unchanged; secp256k1 is pinned to the v0.8.0 release commit. appimagetool came from the "continuous" tag, which upstream moves in place on every push, fetched over the network and executed unverified. Pinned to a numbered release and checked by digest before it is allowed to run. It also ran from /tmp on a fixed name, guarded by `[ ! -x /tmp/appimagetool ]` -- an executable already sitting there is taken as the tool, so any local account could leave one and have it run as whoever built. The AppDir had the same shape one step later, through symlinks on the rm -rf. Both moved under the build tree, which the build already owns, and cleaned by `make clean`. (cherry picked from commit 664aebca08d17a9dbae12c25f21cd8ef2abe7aa6) --- Makefile | 108 +++++++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index ea720f1..abd75c5 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,40 @@ NPROC := $(shell nproc 2>/dev/null || echo 2) SUDO := $(shell [ "$$(id -u)" = "0" ] && echo "" || echo "sudo") VERSION := 1.2.15 +# ---- Pinned third-party sources ------------------------------------------- +# +# These were `git clone --depth 1 ` with no revision, which resolves to +# whatever the default branch happens to point at the moment the build runs. +# For a dependency that only had to link, that is untidy. For these two it is +# not: +# +# RandomX is the proof of work. Its master is not frozen -- v2.0 (Mar 2026) +# changed the algorithm -- so two builds of the same Bitflash commit, made a +# few weeks apart, can compute different hashes for the same header. That is +# not a supply-chain worry, it is a chain split arriving as a build artifact, +# with no version recorded anywhere to explain it afterwards. +# +# libsecp256k1 is signature verification, i.e. the rule deciding who may +# spend. Whatever is on master on the day of the build is what enforces it. +# +# Pinned to full commit ids, not tags: a tag is a mutable pointer, and `git +# checkout ` is the only form here that verifies what it fetched (git +# resolves the object by its own hash). Bump deliberately, and rebuild and +# resync before shipping the result. +# +# RandomX pin is the commit this tree has been building and syncing against +# (post-v2.0.1 master, 2026-05-24) -- chosen to change nothing today. +SECP256K1_REPO := https://github.com/bitcoin-core/secp256k1 +SECP256K1_COMMIT := 6e2c8bc4ecdc6e71dbe7a368f360d8d453ce435d +RANDOMX_REPO := https://github.com/tevador/RandomX +RANDOMX_COMMIT := 1e9d4b2df63fa6edf46b06789486e23c0fcaa55a + +# appimagetool came from the "continuous" release, a tag that is moved in place +# on every upstream push, fetched over the network and executed. Pinned to a +# numbered release and checked by digest before it is allowed to run. +APPIMAGETOOL_URL := https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage +APPIMAGETOOL_SHA256 := ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 + # ---- Linux ---------------------------------------------------------------- linux: deps-linux @@ -47,8 +81,12 @@ deps-secp256k1: @if [ ! -f /usr/local/lib/libsecp256k1.a ]; then \ echo "==> building libsecp256k1"; \ rm -rf $(ROOT)/secp256k1-build; \ - git clone --depth 1 https://github.com/bitcoin-core/secp256k1 $(ROOT)/secp256k1-build; \ - cd $(ROOT)/secp256k1-build && ./autogen.sh && \ + git init -q $(ROOT)/secp256k1-build; \ + cd $(ROOT)/secp256k1-build && \ + git remote add origin $(SECP256K1_REPO) && \ + git fetch -q --depth 1 origin $(SECP256K1_COMMIT) && \ + git checkout -q $(SECP256K1_COMMIT) && \ + ./autogen.sh && \ ./configure --enable-module-schnorrsig --enable-module-extrakeys \ --disable-shared --with-pic --disable-benchmark --disable-tests && \ make -j$(NPROC) && $(SUDO) make install && $(SUDO) ldconfig; \ @@ -61,7 +99,11 @@ deps-randomx: @if [ ! -f $(HOME)/RandomX/build/librandomx.a ]; then \ echo "==> building RandomX"; \ rm -rf $(HOME)/RandomX; \ - git clone --depth 1 https://github.com/tevador/RandomX $(HOME)/RandomX; \ + git init -q $(HOME)/RandomX; \ + cd $(HOME)/RandomX && \ + git remote add origin $(RANDOMX_REPO) && \ + git fetch -q --depth 1 origin $(RANDOMX_COMMIT) && \ + git checkout -q $(RANDOMX_COMMIT); \ mkdir -p $(HOME)/RandomX/build; \ cd $(HOME)/RandomX/build && cmake .. -DCMAKE_BUILD_TYPE=Release && \ make -j$(NPROC) randomx; \ @@ -70,37 +112,57 @@ deps-randomx: echo "==> RandomX already built"; \ fi +# APPDIR and the tool both used fixed paths under /tmp, on a directory every +# local account can write. `[ ! -x /tmp/appimagetool ]` means an executable +# already sitting at that name is taken as the tool and run, so anyone with a +# shell on the build machine could have put one there first and had it execute +# as whoever ran make; `rm -rf /tmp/bitflash.AppDir` then mkdir is the same +# problem one step later, through symlinks. Both now live under the build tree, +# which the build already owns. +APPDIR := $(ROOT)/build/bitflash.AppDir +APPIMAGETOOL := $(ROOT)/build/appimagetool + appimage: src/bitflash @echo "==> building AppImage" - @if [ ! -x /tmp/appimagetool ]; then \ - wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" \ - -O /tmp/appimagetool && chmod +x /tmp/appimagetool; \ + @mkdir -p $(ROOT)/build + @if [ ! -x "$(APPIMAGETOOL)" ]; then \ + echo " fetching appimagetool"; \ + wget -q "$(APPIMAGETOOL_URL)" -O "$(APPIMAGETOOL).tmp" || \ + { echo " ERROR: appimagetool download failed"; rm -f "$(APPIMAGETOOL).tmp"; exit 1; }; \ + echo "$(APPIMAGETOOL_SHA256) $(APPIMAGETOOL).tmp" | sha256sum -c - >/dev/null 2>&1 || \ + { echo " ERROR: appimagetool digest mismatch, refusing to run it"; \ + echo " expected $(APPIMAGETOOL_SHA256)"; \ + echo " got $$(sha256sum "$(APPIMAGETOOL).tmp" | cut -d" " -f1)"; \ + rm -f "$(APPIMAGETOOL).tmp"; exit 1; }; \ + chmod +x "$(APPIMAGETOOL).tmp" && mv "$(APPIMAGETOOL).tmp" "$(APPIMAGETOOL)"; \ fi - @rm -rf /tmp/bitflash.AppDir - @mkdir -p /tmp/bitflash.AppDir/usr/bin \ - /tmp/bitflash.AppDir/usr/lib \ - /tmp/bitflash.AppDir/usr/share/applications \ - /tmp/bitflash.AppDir/usr/share/icons/hicolor/256x256/apps - @cp src/bitflash /tmp/bitflash.AppDir/usr/bin/bitflash + @rm -rf $(APPDIR) + @mkdir -p $(APPDIR)/usr/bin \ + $(APPDIR)/usr/lib \ + $(APPDIR)/usr/share/applications \ + $(APPDIR)/usr/share/icons/hicolor/256x256/apps + @cp src/bitflash $(APPDIR)/usr/bin/bitflash @printf '#!/bin/sh\nHERE="$$(dirname "$$(readlink -f "$$0")")"\nexport LD_LIBRARY_PATH="$$HERE/usr/lib:$$LD_LIBRARY_PATH"\nexec "$$HERE/usr/bin/bitflash" "$$@"\n' \ - > /tmp/bitflash.AppDir/AppRun && chmod +x /tmp/bitflash.AppDir/AppRun + > $(APPDIR)/AppRun && chmod +x $(APPDIR)/AppRun @printf '[Desktop Entry]\nName=Bitflash\nExec=bitflash\nIcon=bitflash\nType=Application\nCategories=Finance;\n' \ - > /tmp/bitflash.AppDir/bitflash.desktop - @cp /tmp/bitflash.AppDir/bitflash.desktop \ - /tmp/bitflash.AppDir/usr/share/applications/ - @python3 -c "\ + > $(APPDIR)/bitflash.desktop + @cp $(APPDIR)/bitflash.desktop \ + $(APPDIR)/usr/share/applications/ + @APPDIR=$(APPDIR) python3 -c "\ +import os; \ from PIL import Image; \ +d = os.environ['APPDIR']; \ ico = Image.open('src/rc/bitcoin.ico'); \ img = ico.convert('RGBA'); \ -img.save('/tmp/bitflash.AppDir/bitflash.png'); \ -import shutil; shutil.copy('/tmp/bitflash.AppDir/bitflash.png', \ -'/tmp/bitflash.AppDir/usr/share/icons/hicolor/256x256/apps/bitflash.png')" +img.save(d + '/bitflash.png'); \ +import shutil; shutil.copy(d + '/bitflash.png', \ +d + '/usr/share/icons/hicolor/256x256/apps/bitflash.png')" @for lib in libsodium.so.23 libdb_cxx-5.3.so libglfw.so.3; do \ path=$$(ldconfig -p | grep " $$lib " | awk '{print $$NF}' | head -1); \ [ -z "$$path" ] && path=$$(find /usr/lib /lib -name "$$lib" 2>/dev/null | head -1); \ - [ -n "$$path" ] && cp -L "$$path" /tmp/bitflash.AppDir/usr/lib/ && echo " bundled $$lib" || echo " missing $$lib"; \ + [ -n "$$path" ] && cp -L "$$path" $(APPDIR)/usr/lib/ && echo " bundled $$lib" || echo " missing $$lib"; \ done - @ARCH=x86_64 /tmp/appimagetool --no-appstream /tmp/bitflash.AppDir \ + @ARCH=x86_64 $(APPIMAGETOOL) --no-appstream $(APPDIR) \ $(ROOT)/Bitflash-$(VERSION)-x86_64.AppImage 2>&1 | grep -E "^Built|^Error" || true @echo "Built: Bitflash-$(VERSION)-x86_64.AppImage" @@ -157,7 +219,7 @@ clean: $(MAKE) -C src -f Makefile clean 2>/dev/null || true cd src && make -f makefile.mingw clean 2>/dev/null || true rm -f Bitflash-*.AppImage Bitflash-*.zip - rm -rf Bitflash-*-windows + rm -rf Bitflash-*-windows build tests: $(MAKE) -C src -f Makefile tests -- cgit v1.2.3