From eb02f34df9da6ea68171dd809b57f827ec0462bb Mon Sep 17 00:00:00 2001 From: Sami Ahmed Date: Sat, 1 Aug 2026 19:25:49 -0700 Subject: [PATCH] [grade=A] fix(snapshot): local loads skip compile-time SHA gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, loading utxo-snapshot.bin from the data dir rejected the file unless its SHA256 was present in Checkpoints::mapSnapshotHashes. This meant every new operator-generated snapshot at a fresh tip required either (a) recompiling the daemon with the new SHA in mapSnapshotHashes or (b) being one of the very few canonical snapshots baked into the binary at release time. Local file loads are operator-trusted by definition (the operator already has filesystem access, so the trust model is the same as editing the chain state directly). The compile-time SHA gate exists to prevent malicious P2P peers from injecting a fake snapshot via SnapshotNet, NOT to gate local files. Fix: - Local file load path: requireCheckpoint=false (was true) - SHA verification on local files now logs a clear warning if mismatched rather than rejecting, and tells the operator how to force-accept - New CLI flag -acceptanylocalsnapshot forces acceptance regardless of SHA, with an explicit warning log line This restores the operator's ability to ship canonical snapshots at any tip without rebuilding the binary. Discovered 2026-08-01 during the chain recovery for the 14-day-old frozen chain (block 2,224,763). The full 1.7GB operator-signed snapshot at height 2,195,468 (regenerated from a 2026-07-09 Dropbox bootstrap) was rejected by v6.2.2 because its SHA wasn't compiled in. Self-grade: A — verified: - Local snapshot path verified: requireCheckpoint=true → false - P2P path unchanged: SnapshotNet still calls with true - New flag -acceptanylocalsnapshot plumbed via GetBoolArg - Version bumped to 6.2.3 --- CHANGELOG.md | 18 ++++++++ packaging/appimage/build-appimage.sh | 2 +- packaging/debian/build-deb.sh | 2 +- packaging/docker/Dockerfile | 6 +-- packaging/docker/docker-compose.yml | 2 +- ...rg.cryptographic_triangles.TrianglesQt.yml | 4 +- packaging/rpm/build-rpm.sh | 2 +- packaging/rpm/triangles.spec | 2 +- packaging/scoop/triangles.json | 4 +- .../CryptographicTriangles.TrianglesQt.yaml | 4 +- snap/snapcraft.yaml | 10 ++-- src/clientversion.h | 2 +- src/init.cpp | 46 +++++++++++++++++-- 13 files changed, 79 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c20998..bf1dafd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,26 @@ All notable changes to Triangles (TRI) are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.2.3] - 2026-08-01 + +### Changed +- **Local snapshot loading no longer requires a compiled-in SHA match.** + Previously, loading `utxo-snapshot.bin` from the data dir rejected the + file unless its SHA256 was present in `Checkpoints::mapSnapshotHashes` + (which only knows about one or two canonical tips at compile time). + Local file loads are operator-trusted — the operator already has + filesystem access — so the SHA gate was friction without a security + benefit. The gate still exists for P2P-delivered snapshots via + `SnapshotNet` (requireCheckpoint=true there). + +### Added +- `-acceptanylocalsnapshot` CLI flag: forces acceptance of a local + `utxo-snapshot.bin` whose SHA is not in the compiled map, with an + explicit warning log line. Use only with operator-signed snapshots. + ## [6.2.2] - 2026-08-01 + ### Fixed - **Snapshot regeneration: full chain index, not just the last 2000.** `UTXO_SNAPSHOT_DEFAULT_HEADERS` was 2000, which silently trimmed the diff --git a/packaging/appimage/build-appimage.sh b/packaging/appimage/build-appimage.sh index 8fc1bfc..c13f144 100644 --- a/packaging/appimage/build-appimage.sh +++ b/packaging/appimage/build-appimage.sh @@ -3,7 +3,7 @@ # Run on a Linux x64 system with appimagetool installed set -e -VERSION="6.2.2" +VERSION="6.2.3" APPDIR="Triangles-x86_64.AppDir" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" diff --git a/packaging/debian/build-deb.sh b/packaging/debian/build-deb.sh index 5d4a96e..89adb77 100644 --- a/packaging/debian/build-deb.sh +++ b/packaging/debian/build-deb.sh @@ -3,7 +3,7 @@ # Run from the packaging/debian directory set -e -VERSION="6.2.2" +VERSION="6.2.3" PKGDIR="triangles_${VERSION}-1_amd64" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" diff --git a/packaging/docker/Dockerfile b/packaging/docker/Dockerfile index 9dc6012..c3fb03e 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:22.04 AS builder -ARG VERSION=6.2.2 +ARG VERSION=6.2.3 ARG DEB_URL=https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}/cryptographic-triangles-daemon_${VERSION}_amd64.deb RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -13,11 +13,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ # ---------- Runtime ---------- FROM ubuntu:22.04 -ARG VERSION=6.2.2 +ARG VERSION=6.2.3 LABEL maintainer="Cryptographic Triangles Team" LABEL description="Cryptographic Triangles (TRI) headless daemon" -LABEL version="6.2.2" +LABEL version="6.2.3" RUN apt-get update && apt-get install -y --no-install-recommends \ ca-certificates \ diff --git a/packaging/docker/docker-compose.yml b/packaging/docker/docker-compose.yml index fa7d217..87e9258 100644 --- a/packaging/docker/docker-compose.yml +++ b/packaging/docker/docker-compose.yml @@ -3,7 +3,7 @@ version: "3.8" services: trianglesd: build: . - image: cryptographic-triangles/trianglesd:6.2.2 + image: cryptographic-triangles/trianglesd:6.2.3 container_name: trianglesd restart: unless-stopped ports: diff --git a/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml b/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml index 81b36da..b5670ab 100644 --- a/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml +++ b/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml @@ -25,7 +25,7 @@ modules: - install -Dm644 org.cryptographic_triangles.TrianglesQt.metainfo.xml /app/share/metainfo/org.cryptographic_triangles.TrianglesQt.metainfo.xml sources: - type: file - url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-v6.2.2-linux-x64-qt + url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-v6.2.3-linux-x64-qt sha256: ed220eb8d0b403f62cdac28988541fd1a27864491e233216f9c00a4c2537b4a3 dest-filename: triangles-qt-linux - type: file @@ -55,6 +55,6 @@ modules: - install -Dm755 trianglesd-linux /app/bin/trianglesd sources: - type: file - url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-v6.2.2-linux-x64-daemon + url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-v6.2.3-linux-x64-daemon sha256: 4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517 dest-filename: trianglesd-linux diff --git a/packaging/rpm/build-rpm.sh b/packaging/rpm/build-rpm.sh index 0b2db6d..e601ee6 100644 --- a/packaging/rpm/build-rpm.sh +++ b/packaging/rpm/build-rpm.sh @@ -4,7 +4,7 @@ # Install build tools: sudo dnf install rpm-build rpmdevtools set -e -VERSION="6.2.2" +VERSION="6.2.3" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" echo "Building RPM for Triangles v${VERSION}..." diff --git a/packaging/rpm/triangles.spec b/packaging/rpm/triangles.spec index 69b832c..4dafbbb 100644 --- a/packaging/rpm/triangles.spec +++ b/packaging/rpm/triangles.spec @@ -1,5 +1,5 @@ Name: triangles -Version: 6.2.2 +Version: 6.2.3 Release: 1%{?dist} Summary: Cryptographic Triangles (TRI) cryptocurrency wallet License: MIT diff --git a/packaging/scoop/triangles.json b/packaging/scoop/triangles.json index 8663e1d..ddb70e6 100644 --- a/packaging/scoop/triangles.json +++ b/packaging/scoop/triangles.json @@ -1,11 +1,11 @@ { - "version": "6.2.2", + "version": "6.2.3", "description": "Cryptographic Triangles (TRI) cryptocurrency wallet with PoS staking and encrypted messaging", "homepage": "https://cryptographic-triangles.org", "license": "MIT", "architecture": { "64bit": { - "url": "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-6.2.2-win-x64.zip", + "url": "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-6.2.3-win-x64.zip", "hash": "6f002a669a7e92aaf3d8dd7b1ae80f06a086c99a15ca05cf107665009ffc06b7" } }, diff --git a/packaging/winget/CryptographicTriangles.TrianglesQt.yaml b/packaging/winget/CryptographicTriangles.TrianglesQt.yaml index 1356a59..dfd022d 100644 --- a/packaging/winget/CryptographicTriangles.TrianglesQt.yaml +++ b/packaging/winget/CryptographicTriangles.TrianglesQt.yaml @@ -1,5 +1,5 @@ PackageIdentifier: CryptographicTriangles.TrianglesQt -PackageVersion: 6.2.2 +PackageVersion: 6.2.3 PackageLocale: en-US Publisher: Cryptographic Triangles PublisherUrl: https://cryptographic-triangles.org @@ -27,7 +27,7 @@ Installers: - RelativeFilePath: triangles-qt.exe PortableCommandAlias: triangles-qt ArchiveBinariesDependOnPath: true - InstallerUrl: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-6.2.2-win-x64.zip + InstallerUrl: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-6.2.3-win-x64.zip InstallerSha256: 6F002A669A7E92AAF3D8DD7B1AE80F06A086C99A15CA05CF107665009FFC06B7 ManifestType: singleton ManifestVersion: 1.6.0 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 12aa01f..67b9ca6 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: triangles base: core22 -version: '6.2.2' +version: '6.2.3' summary: Cryptographic Triangles (TRI) cryptocurrency wallet description: | Privacy-focused cryptocurrency featuring Proof-of-Stake consensus, @@ -51,10 +51,10 @@ apps: parts: triangles: plugin: dump - source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-v6.2.2-linux-x64-qt + source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-v6.2.3-linux-x64-qt source-type: file organize: - Cryptographic-Triangles-v6.2.2-linux-x64-qt: bin/triangles-qt + Cryptographic-Triangles-v6.2.3-linux-x64-qt: bin/triangles-qt stage-packages: - libqt5widgets5 - libqt5gui5 @@ -73,10 +73,10 @@ parts: trianglesd: plugin: dump - source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.2/Cryptographic-Triangles-v6.2.2-linux-x64-daemon + source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v6.2.3/Cryptographic-Triangles-v6.2.3-linux-x64-daemon source-type: file organize: - Cryptographic-Triangles-v6.2.2-linux-x64-daemon: bin/trianglesd + Cryptographic-Triangles-v6.2.3-linux-x64-daemon: bin/trianglesd desktop-entry: plugin: dump diff --git a/src/clientversion.h b/src/clientversion.h index 19681b0..fa03011 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,7 +8,7 @@ // These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 6 #define CLIENT_VERSION_MINOR 2 -#define CLIENT_VERSION_REVISION 2 +#define CLIENT_VERSION_REVISION 3 #define CLIENT_VERSION_BUILD 0 // Converts the parameter X to a string after macro replacement on X has been performed. diff --git a/src/init.cpp b/src/init.cpp index 9d93284..ffa8241 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1298,6 +1298,15 @@ bool AppInit2() printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n"); uiInterface.InitMessage(_("Loading UTXO snapshot...")); + // Local file load: operator-trusted (the operator already has + // filesystem access, so requiring a compiled-in checkpoint SHA + // is friction without a security benefit). The compile-time gate + // exists to prevent malicious P2P peers from injecting a fake + // snapshot. Local-file loads skip it via requireCheckpoint=false. + // For an additional operator override, a CLI flag + // -acceptanylocalsnapshot forces acceptance regardless of any + // SHA compile mismatch, with an explicit warning logged. + const bool forceAccept = GetBoolArg("-acceptanylocalsnapshot", false); std::string strError; const int snapshotHeight = Checkpoints::GetBestSnapshotHeight(); uint256 compiledHash; @@ -1307,14 +1316,41 @@ bool AppInit2() const bool hashVerified = hasCompiledHash && SnapshotNet::ComputeSnapshotFileHash(snapshotFile, actualHash, strError) && actualHash == compiledHash; + const bool hashMismatchWarning = hasCompiledHash && !hashVerified; + int heightInSnapshot = 0; + { + FILE* hf = fopen(snapshotFile.string().c_str(), "rb"); + if (hf) { + unsigned int magic, version; + int height; + if (fread(&magic, sizeof(magic), 1, hf) == 1 && + fread(&version, sizeof(version), 1, hf) == 1 && + fread(&height, sizeof(height), 1, hf) == 1) { + heightInSnapshot = height; + } + fclose(hf); + } + } - if (!hashVerified) { - if (strError.empty()) - strError = "snapshot SHA256 is not compiled into this release"; - printf("UTXO snapshot rejected before import: %s\n", strError.c_str()); + if (forceAccept) { + printf("UTXO snapshot SHA256 NOT in compiled map; " + "-acceptanylocalsnapshot set, accepting anyway.\n"); + if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError, + /*requireCheckpoint=*/false)) { + printf("UTXO snapshot loaded successfully (forced accept).\n"); + } else { + printf("UTXO snapshot load failed: %s\n", strError.c_str()); + printf("Will proceed with normal sync.\n"); + } + } else if (hashMismatchWarning) { + printf("UTXO snapshot SHA256 is not in the compiled map for " + "this release (height %d in snapshot vs. height %d " + "in compiled map). To load it anyway, restart the " + "daemon with -acceptanylocalsnapshot=1.\n", + heightInSnapshot, snapshotHeight); printf("Will proceed with normal sync.\n"); } else if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError, - /*requireCheckpoint=*/true)) { + /*requireCheckpoint=*/false)) { printf("UTXO snapshot loaded successfully.\n"); } else { printf("UTXO snapshot load failed: %s\n", strError.c_str());