On 5 October 2026 the Mac's host (shard program id 0x0559759b...) rejected every
proof from PC 2's host (0x05db1aca...). Both were built from the same guest
sources: host/build.rs compiled the guests on each machine and the ELF depends
on where it is built (cargo's -C metadata for a path crate includes the checkout
path; a worktree on the same Mac gave a third id, 0x0dfade07...). The node's
verifier also spent 114 s to 138 s per proof in the prover client and both key
setups before a 0.1 s to 0.4 s verify.
- elf/: both guest ELFs, their verifying keys and manifest.json (sha256, ids);
host/src/pinned.rs embeds and checks them at every start; the prove modes
refuse when SP1's setup does not derive the manifest's id
- --mode verify: LightProver with the pinned key, no prover client, no key
setup; prints the proof's own program id next to ours ("IS NOT OURS")
- --mode id; igneum-prove-pin and pin-guests.sh to re-pin; build.rs builds a
guest only under IGNEUM_BUILD_GUESTS=1
- tools/ci/pinned-guests-check.sh: elf/ must match its manifest, no script
builds a guest outside pin-guests.sh; make-package.sh and build-dmg.sh print
the pinned ids
- unit tests on the pinned set; bench-log entry with the three ids, the cause
and the timing: 127.0 s wall per verify before, 1.8 s to 2.4 s after
- rollout order in proving/README.md: every prover and verifier moves together
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
146 lines
9.4 KiB
Bash
Executable file
146 lines
9.4 KiB
Bash
Executable file
#!/bin/bash
|
|
# Builds packaging/mac/dist/Igneum-Miner-<version>.dmg: "Igneum Miner.app" + README.txt + "Stop Igneum Miner.command" +
|
|
# a link to /Applications, on a branded image (volume "Igneum Miner", the rounded mark as the volume icon, background with the drag arrow,
|
|
# icons placed). 3 October 2026; 0.2.0 for devnet v4 on 4 October 2026; 0.3.0 the same day: the app (no Terminal).
|
|
#
|
|
# The bundle, 0.3.0:
|
|
# Contents/MacOS/Igneum Miner the window (app/mac/IgneumMiner.swift, WKWebView + menu-bar item), compiled here
|
|
# Contents/MacOS/igneum-app the engine (app/igneum-app, cargo --release), compiled here unless ENGINE= names one
|
|
# Contents/Resources/bin/ igneumd, igneum-miner (vendor/igneum-node/target-integration/release, the devnet-v4
|
|
# integration build) and igneum-bench (the Metal worker, rebuilt from proto-metal/main.swift
|
|
# with -target arm64-apple-macos11 so it loads on any Apple silicon macOS)
|
|
# Contents/Resources/igneum-app.json the update manifest URL (token from ~/.config/igneum/dl-token, not in the repo),
|
|
# the log intake, the live page (packaged-config.sh)
|
|
# Contents/Resources/igneum.icns the master mark, a plain black square (brand/master/igneum-mark-square.svg; Tahoe masks it)
|
|
# Copies of the binaries are stripped and re-signed ad hoc (strip invalidates the linker signature and arm64 macOS
|
|
# refuses an unsigned binary); the originals are not touched.
|
|
#
|
|
# packaging/mac/build-dmg.sh build (the version is app/igneum-app/Cargo.toml's; VERSION= overrides it for a test build)
|
|
# packaging/ota/publish-manifest.sh --version <v> --mac dist/Igneum-Miner-<v>.dmg --notes "..." then publishes it to the apps
|
|
# NODE=<path> MINER=<path> WORKER=<path> ENGINE=<path> use other binaries; REBUILD_WORKER=0 ships proto-metal/igneum-bench-hotswap
|
|
# Needs: brand/icons/igneum.icns, igneum-volume.icns and dmg-background.tiff (python3 brand/icons/make-icons.py), swiftc, cargo (rustup), and
|
|
# dmgbuild (pip3 install dmgbuild) for the icon layout. Without dmgbuild a plain hdiutil image is built and said so.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
VERSION="${VERSION:-$(sed -n 's/^version = "\(.*\)"/\1/p' "$ROOT/app/igneum-app/Cargo.toml" | head -1)}"
|
|
NODE="${NODE:-$ROOT/vendor/igneum-node/target-integration/release/igneumd}"
|
|
MINER="${MINER:-$ROOT/vendor/igneum-node/target-integration/release/igneum-miner}"
|
|
WORKER="${WORKER:-}"
|
|
ENGINE="${ENGINE:-}"
|
|
# proving v0: the SP1 host and the exporter next to the node (CPU proving on a Mac; app/igneum-app/src/prover.rs)
|
|
PROVE_HOST="${PROVE_HOST:-$ROOT/proving/igneum-prove/target/release/igneum-prove-host}"
|
|
PROVE_EXPORT="${PROVE_EXPORT:-$ROOT/proving/igneum-prove/target/release/igneum-prove-export}"
|
|
REBUILD_WORKER="${REBUILD_WORKER:-1}"
|
|
ICONS="$ROOT/brand/icons"
|
|
BUILD="$HERE/build"
|
|
DIST="$HERE/dist"
|
|
DMG="$DIST/Igneum-Miner-$VERSION.dmg"
|
|
STAGE="$BUILD/dmg"
|
|
APP="$STAGE/Igneum Miner.app"
|
|
STAMP="$(date -u +%Y%m%d%H%M)"
|
|
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:$PATH"
|
|
. "$HERE/packaged-config.sh"
|
|
|
|
if [ ! -x "$NODE" ]; then
|
|
if [ -x "$ROOT/vendor/igneum-node/target/release/kaspad" ]; then
|
|
echo "note: $NODE is missing; using target/release/kaspad renamed to igneumd"
|
|
NODE="$ROOT/vendor/igneum-node/target/release/kaspad"
|
|
else
|
|
echo "no node binary at $NODE"; exit 1
|
|
fi
|
|
fi
|
|
[ -x "$MINER" ] || { echo "missing: $MINER"; exit 1; }
|
|
for f in "$NODE" "$MINER"; do
|
|
file "$f" | grep -q 'arm64' || { echo "$f is not an arm64 binary"; exit 1; }
|
|
done
|
|
for f in igneum.icns igneum-volume.icns; do [ -f "$ICONS/$f" ] || { echo "no $ICONS/$f; run: python3 brand/icons/make-icons.py"; exit 1; }; done
|
|
command -v swiftc >/dev/null 2>&1 || { echo "swiftc is needed for the window (xcode-select --install)"; exit 1; }
|
|
|
|
rm -rf "$BUILD"
|
|
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources/bin" "$DIST" "$BUILD/worker" "$BUILD/window"
|
|
|
|
# the Metal worker: rebuilt from main.swift for macOS 11 and later (about 7 s), else the committed hot-swap binary
|
|
if [ -z "$WORKER" ]; then
|
|
if [ "$REBUILD_WORKER" = 1 ]; then
|
|
echo "building the Metal worker from proto-metal/main.swift with -target arm64-apple-macos11"
|
|
(cd "$ROOT/proto-metal" && nice -n 19 swiftc -O -target arm64-apple-macos11 -o "$BUILD/worker/igneum-bench" main.swift -framework Metal)
|
|
WORKER="$BUILD/worker/igneum-bench"
|
|
else
|
|
WORKER="$ROOT/proto-metal/igneum-bench-hotswap"
|
|
echo "note: using $WORKER as built (its load command says minos $(otool -l "$WORKER" | awk '/minos/ { print $2; exit }'))"
|
|
fi
|
|
fi
|
|
[ -x "$WORKER" ] || { echo "missing: $WORKER"; exit 1; }
|
|
file "$WORKER" | grep -q 'arm64' || { echo "$WORKER is not an arm64 binary"; exit 1; }
|
|
echo "worker: $WORKER (minos $(otool -l "$WORKER" | awk '/minos/ { print $2; exit }'))"
|
|
|
|
# the engine (cargo, nice 19, 4 jobs: the devnet is mining on this Mac)
|
|
if [ -z "$ENGINE" ]; then
|
|
echo "building the engine (app/igneum-app, cargo --release)"
|
|
(cd "$ROOT/app/igneum-app" && nice -n 19 cargo build --release -j 4 --quiet)
|
|
ENGINE="$ROOT/app/igneum-app/target/release/igneum-app"
|
|
fi
|
|
[ -x "$ENGINE" ] || { echo "missing: $ENGINE"; exit 1; }
|
|
"$ENGINE" --version
|
|
|
|
# the window
|
|
echo "building the window (app/mac/IgneumMiner.swift)"
|
|
(cd "$ROOT/app/mac" && nice -n 19 swiftc -O -target arm64-apple-macos11 -o "$BUILD/window/Igneum Miner" IgneumMiner.swift -framework Cocoa -framework WebKit 2>&1 | grep -v 'warning\|^ *\^\|^$' || true)
|
|
[ -x "$BUILD/window/Igneum Miner" ] || { echo "the window did not build"; exit 1; }
|
|
|
|
# the bundle
|
|
sed -e "s/VERSION_STAMP/$STAMP/" "$HERE/app/Info.plist" > "$APP/Contents/Info.plist"
|
|
# the short version is $VERSION whatever the template says (tools/ship-app.mjs keeps the template equal to Cargo.toml;
|
|
# the sed that replaced a literal 0.3.0 here stopped matching at 0.3.1 and the bundles said 0.3.2 after that)
|
|
plutil -replace CFBundleShortVersionString -string "$VERSION" "$APP/Contents/Info.plist"
|
|
plutil -lint "$APP/Contents/Info.plist" >/dev/null
|
|
printf 'APPL????' > "$APP/Contents/PkgInfo"
|
|
cp "$BUILD/window/Igneum Miner" "$APP/Contents/MacOS/Igneum Miner"
|
|
cp "$ENGINE" "$APP/Contents/MacOS/igneum-app"
|
|
cp "$ICONS/igneum.icns" "$APP/Contents/Resources/igneum.icns"
|
|
cp "$NODE" "$APP/Contents/Resources/bin/igneumd"
|
|
cp "$MINER" "$APP/Contents/Resources/bin/igneum-miner"
|
|
cp "$WORKER" "$APP/Contents/Resources/bin/igneum-bench"
|
|
if [ -f "$PROVE_HOST" ] && [ -f "$PROVE_EXPORT" ]; then
|
|
cp "$PROVE_HOST" "$APP/Contents/Resources/bin/igneum-prove-host"
|
|
cp "$PROVE_EXPORT" "$APP/Contents/Resources/bin/igneum-prove-export"
|
|
echo "prover: igneum-prove-host and igneum-prove-export from $(dirname "$PROVE_HOST")"
|
|
"$PROVE_HOST" --mode id || { echo "the prover host's pinned guests do not pass their manifest check"; exit 1; }
|
|
else echo "warning: no $PROVE_HOST / $PROVE_EXPORT (cd proving/igneum-prove && cargo build --release -p igneum-prove-host -p igneum-prove-export); the Proving tile will say the prover is missing"; fi
|
|
write_packaged_config "$APP/Contents/Resources/igneum-app.json"
|
|
chmod 755 "$APP/Contents/MacOS/"* "$APP/Contents/Resources/bin/"*
|
|
|
|
# strip the copies, then sign them ad hoc again (the engine is already stripped by cargo)
|
|
for b in "$APP/Contents/Resources/bin/"* "$APP/Contents/MacOS/"*; do
|
|
before=$(stat -f %z "$b")
|
|
strip "$b" 2>/dev/null || strip -x "$b" 2>/dev/null || true
|
|
codesign -s - -f "$b" 2>/dev/null
|
|
codesign -v "$b"
|
|
echo "$(basename "$b"): $before -> $(stat -f %z "$b") bytes, signed ad hoc"
|
|
done
|
|
codesign -s - -f "$APP" 2>/dev/null || true
|
|
# each copy must still run
|
|
v="$("$APP/Contents/Resources/bin/igneumd" --version 2>&1 || true)"; echo "node: ${v%%$'\n'*}"
|
|
v="$("$APP/Contents/Resources/bin/igneum-miner" 2>&1 || true)"; case "$v" in usage*) ;; *) echo "igneum-miner copy does not run: $v"; exit 1 ;; esac
|
|
case "$v" in *"--evm-address"*) ;; *) echo "igneum-miner copy is not the devnet-v4 miner (no --evm-address in its usage)"; exit 1 ;; esac
|
|
v="$(echo quit | "$APP/Contents/Resources/bin/igneum-bench" --serve 2>&1)"; case "$v" in "ready metal"*) echo "worker: $v" ;; *) echo "igneum-bench copy does not serve: $v"; exit 1 ;; esac
|
|
case "$v" in *"prepare 1"*) ;; *) echo "note: this worker has no prepare support (no hot swap at the hour boundary); the miner falls back to exit 42" ;; esac
|
|
v="$("$APP/Contents/MacOS/igneum-app" --version 2>&1 || true)"; case "$v" in "igneum-app $VERSION") echo "engine: $v" ;; igneum-app*) echo "the engine says '$v' but this DMG is $VERSION (the update manifest would not match; set VERSION= or rebuild the engine)"; exit 1 ;; *) echo "the engine copy does not run: $v"; exit 1 ;; esac
|
|
|
|
# the rest of the image
|
|
cp "$HERE/dmg/README.txt" "$STAGE/README.txt"
|
|
cp "$HERE/app/Stop Igneum Miner.command" "$STAGE/Stop Igneum Miner.command"
|
|
chmod 755 "$STAGE/Stop Igneum Miner.command"
|
|
|
|
rm -f "$DMG"
|
|
if command -v dmgbuild >/dev/null 2>&1; then
|
|
dmgbuild -s "$HERE/dmg/settings.py" -D "app=$APP" -D "stage=$STAGE" -D "icons=$ICONS" "Igneum Miner" "$DMG"
|
|
else
|
|
echo "note: dmgbuild is not installed (pip3 install dmgbuild); building a plain image without icon positions or background"
|
|
ln -s /Applications "$STAGE/Applications"
|
|
cp "$ICONS/igneum-volume.icns" "$STAGE/.VolumeIcon.icns"
|
|
hdiutil create -volname "Igneum Miner" -srcfolder "$STAGE" -ov -format ULFO -fs HFS+ "$DMG" >/dev/null
|
|
fi
|
|
hdiutil verify "$DMG" >/dev/null
|
|
echo "built $DMG ($(du -h "$DMG" | cut -f1), $(stat -f %z "$DMG") bytes, version $VERSION build $STAMP)"
|