From fd627b134c686e7c9afb646dc37c573c5d36fd4a Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 05:22:36 +0000 Subject: build: stop rm -rf'ing a RandomX tree the build did not create deps-randomx built into $(HOME)/RandomX and deleted that path whenever the library was missing, taking out hand-managed checkouts the build never owned. Default the build to the in-tree $(ROOT)/randomx-build, like secp256k1-build, overridable via RANDOMX_DIR and exported so src/Makefile's own fallback resolves to the same tree. As a backstop, the recipe writes a .bitflash-built marker after a successful build and refuses to delete any directory that lacks it, so a user-supplied RANDOMX_DIR pointing at a pre-existing tree is never removed. Closes #27 --- Makefile | 31 ++++++++++++++++++++++++------- src/Makefile | 4 +++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 9f97b73..83a2c73 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,17 @@ APPIMAGETOOL_SHA256 := ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3c # ---- Linux ---------------------------------------------------------------- +# RandomX used to build in $(HOME)/RandomX, and deps-randomx would rm -rf that +# path whenever the library was missing -- deleting a tree the build did not +# create and does not own (issue #27; a hand-managed pinned checkout there is +# what this machine's miners build against). The default is now in-tree, like +# secp256k1-build. A host that deliberately keeps RandomX elsewhere can set +# RANDOMX_DIR=... explicitly; the recipe below will still never delete a +# directory that lacks the marker file this build writes after a successful +# build. Exported so src/Makefile's own RANDOMX_DIR ?= picks the same tree. +RANDOMX_DIR ?= $(ROOT)/randomx-build +export RANDOMX_DIR + linux: deps-linux $(MAKE) -C src -f Makefile -j$(NPROC) strip src/bitflash @@ -109,19 +120,25 @@ deps-secp256k1: fi deps-randomx: - @if [ ! -f $(HOME)/RandomX/build/librandomx.a ]; then \ + @if [ ! -f $(RANDOMX_DIR)/build/librandomx.a ]; then \ + if [ -d $(RANDOMX_DIR) ] && [ ! -f $(RANDOMX_DIR)/.bitflash-built ]; then \ + echo "ERROR: $(RANDOMX_DIR) exists but was not created by this build"; \ + echo " refusing to rm -rf it -- remove it yourself or set RANDOMX_DIR"; \ + exit 1; \ + fi; \ echo "==> building RandomX"; \ - rm -rf $(HOME)/RandomX; \ - git init -q $(HOME)/RandomX; \ - cd $(HOME)/RandomX && \ + rm -rf $(RANDOMX_DIR); \ + git init -q $(RANDOMX_DIR); \ + cd $(RANDOMX_DIR) && \ git remote add origin $(RANDOMX_REPO) && \ git fetch -q --depth 1 origin $(RANDOMX_COMMIT) && \ git checkout -q --detach FETCH_HEAD && \ test "$$(git rev-parse HEAD)" = "$(RANDOMX_COMMIT)" && \ echo "==> RandomX pinned at $(RANDOMX_COMMIT)" && \ - mkdir -p $(HOME)/RandomX/build; \ - cd $(HOME)/RandomX/build && cmake .. -DCMAKE_BUILD_TYPE=Release && \ - make -j$(NPROC) randomx; \ + mkdir -p $(RANDOMX_DIR)/build && \ + cd $(RANDOMX_DIR)/build && cmake .. -DCMAKE_BUILD_TYPE=Release && \ + make -j$(NPROC) randomx && \ + touch $(RANDOMX_DIR)/.bitflash-built && \ echo "==> RandomX done"; \ else \ echo "==> RandomX already built"; \ diff --git a/src/Makefile b/src/Makefile index 1bc3ae8..2c6d304 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,7 +6,9 @@ FUZZ_CXX ?= clang++ SECP_INC ?= /usr/local/include SECP_LIB ?= /usr/local/lib -RANDOMX_DIR ?= $(HOME)/RandomX +# The root Makefile exports RANDOMX_DIR (default: /randomx-build, built +# by deps-randomx). This fallback only applies when src/ is built standalone. +RANDOMX_DIR ?= $(abspath $(CURDIR)/..)/randomx-build IMGUI_DIR = imgui -- cgit v1.2.3 From 34cb6e65f171251ad331bdd67f33511904f36fb2 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 05:23:05 +0000 Subject: build: ask apt for libboost-dev, and update the package lists first libboost-system-dev is gone from Ubuntu 26.04 -- Boost.System has been header-only since 1.69, and this tree only includes two boost headers and links no boost library. The recipe also installed without an apt-get update, so a fresh image failed on stale or empty lists. Closes #28 --- Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 83a2c73..0eec4c2 100644 --- a/Makefile +++ b/Makefile @@ -89,11 +89,17 @@ linux-node: deps-linux deps-linux: deps-apt deps-secp256k1 deps-randomx +# libboost-dev, not libboost-system-dev: Boost.System has been header-only +# since 1.69 and Ubuntu 26.04 (resolute) no longer ships the -system package. +# This tree only includes two boost headers and links no boost library. The +# apt-get update comes first: a fresh image's package lists are empty, and +# install then fails on the first name it cannot find. deps-apt: @if command -v apt-get >/dev/null 2>&1; then \ + $(SUDO) apt-get update; \ $(SUDO) apt-get install -y build-essential cmake git pkg-config autoconf \ libtool libssl-dev libdb5.3++-dev libsodium-dev nlohmann-json3-dev \ - libboost-system-dev libglfw3-dev libgl-dev python3-pil clang; \ + libboost-dev libglfw3-dev libgl-dev python3-pil clang; \ else \ echo "Not an apt system — install: g++ cmake git autoconf libtool"; \ echo " libssl libdb++ libsodium nlohmann-json boost glfw3 opengl python3-pil"; \ -- cgit v1.2.3 From fcd0e6697038e5959df733e389124f6ccfa4a6d5 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 05:24:26 +0000 Subject: build: let appimagetool's exit status decide, and check the artefact The appimage recipe piped the tool through grep ... || true, so a failed run still ended with an unconditional "Built:" echo and make reported success with no AppImage on disk. Run appimagetool bare so its status propagates, then require the artefact to exist, be non-empty, and be newer than src/bitflash before announcing it. Closes #29 --- Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0eec4c2..602c154 100644 --- a/Makefile +++ b/Makefile @@ -200,8 +200,17 @@ d + '/usr/share/icons/hicolor/256x256/apps/bitflash.png')" [ -z "$$path" ] && path=$$(find /usr/lib /lib -name "$$lib" 2>/dev/null | head -1); \ [ -n "$$path" ] && cp -L "$$path" $(APPDIR)/usr/lib/ && echo " bundled $$lib" || echo " missing $$lib"; \ done +# appimagetool's output used to be piped through grep ... || true, which +# discarded its exit status: the target succeeded and announced "Built:" +# even when no AppImage existed. Run the tool bare so its status decides, +# then require the artefact to exist, be non-empty, and be newer than the +# binary it was packaged from before saying so. @ARCH=x86_64 $(APPIMAGETOOL) --no-appstream $(APPDIR) \ - $(ROOT)/Bitflash-$(VERSION)-x86_64.AppImage 2>&1 | grep -E "^Built|^Error" || true + $(ROOT)/Bitflash-$(VERSION)-x86_64.AppImage + @test -s $(ROOT)/Bitflash-$(VERSION)-x86_64.AppImage || \ + { echo "ERROR: Bitflash-$(VERSION)-x86_64.AppImage missing or empty"; exit 1; } + @test $(ROOT)/Bitflash-$(VERSION)-x86_64.AppImage -nt src/bitflash || \ + { echo "ERROR: Bitflash-$(VERSION)-x86_64.AppImage is not newer than src/bitflash"; exit 1; } @echo "Built: Bitflash-$(VERSION)-x86_64.AppImage" # ---- Windows (MSYS2 UCRT64) ----------------------------------------------- -- cgit v1.2.3 From 098074193b7a3147f4ad0e0424618b52fe370026 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 16:55:03 +0000 Subject: build: quote the RandomX path so the guard cannot be stepped around The marker guard added in fd627b1 used $(RANDOMX_DIR) unquoted, so an operator-supplied value that expands to anything other than one word walked straight past it. With a glob matching two directories, `[ -d $(RANDOMX_DIR) ]` became a two-argument test, died with "unexpected operator" rather than answering, and short-circuited the && before the marker was ever looked for -- leaving `rm -rf` to run against every match. A value holding a space or a `&&` broke the outer test the same way and reported "already built", and an apostrophe made the recipe unparseable. So the commit's claim to refuse any directory lacking the marker was falsifiable. Quote every use, and reject up front the values that cannot name one directory: empty and / now refuse deliberately rather than by way of the one-argument `[ -d ]` quirk that happened to catch them, and whitespace and glob characters are refused outright because src/Makefile interpolates the same value into -I and -L unquoted, where neither survives. A relative value is refused too: it named a different directory here than in src/, and cd'ing into it made the later mkdir -p resolve against the new cwd, so the build landed in randomx-build/randomx-build/build and cmake found nothing. Verified against the recipe text as make expands it, with rm replaced by an echo, over empty, /, globs matching one and two directories, spaces, tabs, relative paths, symlinks, apostrophes, semicolons, backticks and newlines, under both dash and bash: every run either refuses with a message or passes rm exactly one argument, identical to the path the guard tested. --- Makefile | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 602c154..c891001 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,21 @@ APPIMAGETOOL_SHA256 := ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3c # RANDOMX_DIR=... explicitly; the recipe below will still never delete a # directory that lacks the marker file this build writes after a successful # build. Exported so src/Makefile's own RANDOMX_DIR ?= picks the same tree. +# +# Every use of it below is quoted, and the recipe rejects a value that cannot +# name exactly one directory before it reaches anything destructive. Unquoted, +# the marker guard could be stepped around: a value holding a glob that matches +# two directories made `[ -d $(RANDOMX_DIR) ]` a two-argument test, which dies +# with "unexpected operator" instead of answering, so the && short-circuited, +# the guard was skipped, and the rm -rf below ran against every match. A value +# holding a space or a `&&` broke the outer test the same way and reported +# "already built". Whitespace and glob characters are refused outright rather +# than merely quoted, because src/Makefile interpolates this same value into +# -I$(RANDOMX_DIR)/src and -L$(RANDOMX_DIR)/build unquoted, where neither can +# survive. A relative value is refused rather than silently made absolute: it +# would mean two different directories to this Makefile and to src/Makefile, +# whose cwd is src/, and a command-line assignment reaches the sub-make through +# MAKEFLAGS, where it outranks anything exported from here. RANDOMX_DIR ?= $(ROOT)/randomx-build export RANDOMX_DIR @@ -126,25 +141,34 @@ deps-secp256k1: fi deps-randomx: - @if [ ! -f $(RANDOMX_DIR)/build/librandomx.a ]; then \ - if [ -d $(RANDOMX_DIR) ] && [ ! -f $(RANDOMX_DIR)/.bitflash-built ]; then \ + @ws="$$(printf ' \t')"; \ + case "$(RANDOMX_DIR)" in \ + "") echo "ERROR: RANDOMX_DIR is empty"; exit 1 ;; \ + /) echo "ERROR: RANDOMX_DIR is /, refusing"; exit 1 ;; \ + *[$$ws]*) echo "ERROR: RANDOMX_DIR contains whitespace: $(RANDOMX_DIR)"; exit 1 ;; \ + *[*?[]*) echo "ERROR: RANDOMX_DIR contains a glob character: $(RANDOMX_DIR)"; exit 1 ;; \ + /*) ;; \ + *) echo "ERROR: RANDOMX_DIR must be an absolute path: $(RANDOMX_DIR)"; exit 1 ;; \ + esac + @if [ ! -f "$(RANDOMX_DIR)/build/librandomx.a" ]; then \ + if [ -d "$(RANDOMX_DIR)" ] && [ ! -f "$(RANDOMX_DIR)/.bitflash-built" ]; then \ echo "ERROR: $(RANDOMX_DIR) exists but was not created by this build"; \ echo " refusing to rm -rf it -- remove it yourself or set RANDOMX_DIR"; \ exit 1; \ fi; \ echo "==> building RandomX"; \ - rm -rf $(RANDOMX_DIR); \ - git init -q $(RANDOMX_DIR); \ - cd $(RANDOMX_DIR) && \ + rm -rf "$(RANDOMX_DIR)" && \ + git init -q "$(RANDOMX_DIR)" && \ + cd "$(RANDOMX_DIR)" && \ git remote add origin $(RANDOMX_REPO) && \ git fetch -q --depth 1 origin $(RANDOMX_COMMIT) && \ git checkout -q --detach FETCH_HEAD && \ test "$$(git rev-parse HEAD)" = "$(RANDOMX_COMMIT)" && \ echo "==> RandomX pinned at $(RANDOMX_COMMIT)" && \ - mkdir -p $(RANDOMX_DIR)/build && \ - cd $(RANDOMX_DIR)/build && cmake .. -DCMAKE_BUILD_TYPE=Release && \ + mkdir -p "$(RANDOMX_DIR)/build" && \ + cd "$(RANDOMX_DIR)/build" && cmake .. -DCMAKE_BUILD_TYPE=Release && \ make -j$(NPROC) randomx && \ - touch $(RANDOMX_DIR)/.bitflash-built && \ + touch "$(RANDOMX_DIR)/.bitflash-built" && \ echo "==> RandomX done"; \ else \ echo "==> RandomX already built"; \ -- cgit v1.2.3 From 9886a4680cd669f376eec1bf18da7ad10057dd38 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 16:58:44 +0000 Subject: build: let a failed pin check decide the exit status everywhere fd627b1's message said the rewrite hardened a pin check that used `;` where it needed `&&`. It hardened one of four. Three recipes still ended their chain with `echo "==> ... done"; ` after a semicolon, so a mismatched pin failed the `test`, skipped every build step, and then ran the echo anyway -- exit 0, and make announced a pinned dependency that nothing had verified. deps-windows also had the same break after mkdir and after `make randomx`. The one the earlier message did not mention is deps-secp256k1, on the Linux path: libsecp256k1 is signature verification, so that was the worst of the three to leave reporting success on an unverified checkout. Joined each chain with && the whole way to the closing echo, in deps-secp256k1, and in both halves of deps-windows. Verified by extracting each recipe as make expands it, splitting it into logical recipe lines and running each in its own shell the way make does, with git reporting a sha that does not match the pin: all three previously exited 0 and now exit 1, and each deps-windows half fails independently rather than relying on the other. With a matching sha the recipes still exit 0, still print "done", and still write the marker. The Windows path was not executed -- there is no MSYS2 here -- so pacman was shimmed and only the shell logic below it was measured. --- Makefile | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c891001..c5cf31c 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,12 @@ VERSION := 1.2.19+sec # resolves the object by its own hash). Bump deliberately, and rebuild and # resync before shipping the result. # +# The check has to decide the exit status, so each recipe below joins its +# commands with && the whole way to the closing echo. Ending that chain with a +# `;` meant a failed `test` only skipped the build steps: the echo still ran, +# the target still exited 0, and make announced a pinned dependency that +# nothing had verified. +# # RandomX pin is the commit this tree has been building and syncing against # (post-v2.0.1 master, 2026-05-24) -- identical to upstream's pin. # @@ -123,8 +129,8 @@ deps-apt: deps-secp256k1: @if [ ! -f /usr/local/lib/libsecp256k1.a ]; then \ echo "==> building libsecp256k1"; \ - rm -rf $(ROOT)/secp256k1-build; \ - git init -q $(ROOT)/secp256k1-build; \ + rm -rf $(ROOT)/secp256k1-build && \ + git init -q $(ROOT)/secp256k1-build && \ cd $(ROOT)/secp256k1-build && \ git remote add origin $(SECP256K1_REPO) && \ git fetch -q --depth 1 origin $(SECP256K1_COMMIT) && \ @@ -134,7 +140,7 @@ deps-secp256k1: cd $(ROOT)/secp256k1-build && ./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; \ + make -j$(NPROC) && $(SUDO) make install && $(SUDO) ldconfig && \ echo "==> libsecp256k1 done"; \ else \ echo "==> libsecp256k1 already installed"; \ @@ -252,8 +258,8 @@ deps-windows: mingw-w64-ucrt-x86_64-cmake \ autoconf automake libtool @if [ ! -f deps/lib/libsecp256k1.a ]; then \ - rm -rf secp256k1-build; \ - git init -q secp256k1-build; \ + rm -rf secp256k1-build && \ + git init -q secp256k1-build && \ cd secp256k1-build && \ git remote add origin $(SECP256K1_REPO) && \ git fetch -q --depth 1 origin $(SECP256K1_COMMIT) && \ @@ -264,23 +270,23 @@ deps-windows: ./configure --prefix=$(ROOT)/deps \ --enable-module-schnorrsig --enable-module-extrakeys \ --disable-shared --with-pic --disable-benchmark --disable-tests && \ - make install; \ + make install && \ echo "==> secp256k1 done"; \ else \ echo "==> secp256k1 already built"; \ fi @if [ ! -f RandomX/build/librandomx.a ]; then \ - rm -rf RandomX; \ - git init -q RandomX; \ + rm -rf RandomX && \ + git init -q RandomX && \ cd RandomX && \ git remote add origin $(RANDOMX_REPO) && \ git fetch -q --depth 1 origin $(RANDOMX_COMMIT) && \ git checkout -q --detach FETCH_HEAD && \ test "$$(git rev-parse HEAD)" = "$(RANDOMX_COMMIT)" && \ echo "==> RandomX pinned at $(RANDOMX_COMMIT)" && \ - mkdir -p $(ROOT)/RandomX/build; \ + mkdir -p $(ROOT)/RandomX/build && \ cd $(ROOT)/RandomX/build && cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release && \ - make randomx; \ + make randomx && \ echo "==> RandomX done"; \ else \ echo "==> RandomX already built"; \ -- cgit v1.2.3 From 3d6373874f3972b776dd48e86321b1f8c78cd23b Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 16:59:30 +0000 Subject: build: say so when apt-get update fails instead of passing over it 34cb6e6 added `apt-get update;` before the install. The semicolon means a failed update does not stop the install, which is the behaviour we want -- one unreachable repository should not block an install the lists already on disk can satisfy -- but it was punctuation rather than a decision, and it was silent. The install then failed several seconds later with "unable to locate package" and nothing tied the two together. Kept it non-fatal, made it explicit with `|| echo`, and wrote the reasoning down next to it. The install itself is still the last command in the branch, so a failed install is still the target's status. Measured all four combinations with apt-get shimmed: a failed update now warns and carries on, a failed install still aborts. --- Makefile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c5cf31c..ace07d8 100644 --- a/Makefile +++ b/Makefile @@ -115,9 +115,16 @@ deps-linux: deps-apt deps-secp256k1 deps-randomx # This tree only includes two boost headers and links no boost library. The # apt-get update comes first: a fresh image's package lists are empty, and # install then fails on the first name it cannot find. +# +# A failed update is deliberately not fatal: one unreachable repository should +# not block an install the lists already on disk can satisfy. It does say so, +# though -- "unable to locate package" several seconds later is otherwise hard +# to connect back to it. The install is the last command in the branch, so its +# status is the target's, and that one does abort. deps-apt: @if command -v apt-get >/dev/null 2>&1; then \ - $(SUDO) apt-get update; \ + $(SUDO) apt-get update || \ + echo " warning: apt-get update failed, using the package lists on disk"; \ $(SUDO) apt-get install -y build-essential cmake git pkg-config autoconf \ libtool libssl-dev libdb5.3++-dev libsodium-dev nlohmann-json3-dev \ libboost-dev libglfw3-dev libgl-dev python3-pil clang; \ -- cgit v1.2.3