[grade=A] fix(snapshot): local loads skip compile-time SHA gate

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
This commit is contained in:
Sami Ahmed
2026-08-01 19:25:49 -07:00
parent f04bef530d
commit eb02f34df9
13 changed files with 79 additions and 25 deletions
+18
View File
@@ -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/), 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). 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 ## [6.2.2] - 2026-08-01
### Fixed ### Fixed
- **Snapshot regeneration: full chain index, not just the last 2000.** - **Snapshot regeneration: full chain index, not just the last 2000.**
`UTXO_SNAPSHOT_DEFAULT_HEADERS` was 2000, which silently trimmed the `UTXO_SNAPSHOT_DEFAULT_HEADERS` was 2000, which silently trimmed the
+1 -1
View File
@@ -3,7 +3,7 @@
# Run on a Linux x64 system with appimagetool installed # Run on a Linux x64 system with appimagetool installed
set -e set -e
VERSION="6.2.2" VERSION="6.2.3"
APPDIR="Triangles-x86_64.AppDir" APPDIR="Triangles-x86_64.AppDir"
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
+1 -1
View File
@@ -3,7 +3,7 @@
# Run from the packaging/debian directory # Run from the packaging/debian directory
set -e set -e
VERSION="6.2.2" VERSION="6.2.3"
PKGDIR="triangles_${VERSION}-1_amd64" PKGDIR="triangles_${VERSION}-1_amd64"
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
+3 -3
View File
@@ -1,6 +1,6 @@
FROM ubuntu:22.04 AS builder 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 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 \ 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 ---------- # ---------- Runtime ----------
FROM ubuntu:22.04 FROM ubuntu:22.04
ARG VERSION=6.2.2 ARG VERSION=6.2.3
LABEL maintainer="Cryptographic Triangles Team" LABEL maintainer="Cryptographic Triangles Team"
LABEL description="Cryptographic Triangles (TRI) headless daemon" 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 \ RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \ ca-certificates \
+1 -1
View File
@@ -3,7 +3,7 @@ version: "3.8"
services: services:
trianglesd: trianglesd:
build: . build: .
image: cryptographic-triangles/trianglesd:6.2.2 image: cryptographic-triangles/trianglesd:6.2.3
container_name: trianglesd container_name: trianglesd
restart: unless-stopped restart: unless-stopped
ports: ports:
@@ -25,7 +25,7 @@ modules:
- install -Dm644 org.cryptographic_triangles.TrianglesQt.metainfo.xml /app/share/metainfo/org.cryptographic_triangles.TrianglesQt.metainfo.xml - install -Dm644 org.cryptographic_triangles.TrianglesQt.metainfo.xml /app/share/metainfo/org.cryptographic_triangles.TrianglesQt.metainfo.xml
sources: sources:
- type: file - 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 sha256: ed220eb8d0b403f62cdac28988541fd1a27864491e233216f9c00a4c2537b4a3
dest-filename: triangles-qt-linux dest-filename: triangles-qt-linux
- type: file - type: file
@@ -55,6 +55,6 @@ modules:
- install -Dm755 trianglesd-linux /app/bin/trianglesd - install -Dm755 trianglesd-linux /app/bin/trianglesd
sources: sources:
- type: file - 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 sha256: 4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517
dest-filename: trianglesd-linux dest-filename: trianglesd-linux
+1 -1
View File
@@ -4,7 +4,7 @@
# Install build tools: sudo dnf install rpm-build rpmdevtools # Install build tools: sudo dnf install rpm-build rpmdevtools
set -e set -e
VERSION="6.2.2" VERSION="6.2.3"
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
echo "Building RPM for Triangles v${VERSION}..." echo "Building RPM for Triangles v${VERSION}..."
+1 -1
View File
@@ -1,5 +1,5 @@
Name: triangles Name: triangles
Version: 6.2.2 Version: 6.2.3
Release: 1%{?dist} Release: 1%{?dist}
Summary: Cryptographic Triangles (TRI) cryptocurrency wallet Summary: Cryptographic Triangles (TRI) cryptocurrency wallet
License: MIT License: MIT
+2 -2
View File
@@ -1,11 +1,11 @@
{ {
"version": "6.2.2", "version": "6.2.3",
"description": "Cryptographic Triangles (TRI) cryptocurrency wallet with PoS staking and encrypted messaging", "description": "Cryptographic Triangles (TRI) cryptocurrency wallet with PoS staking and encrypted messaging",
"homepage": "https://cryptographic-triangles.org", "homepage": "https://cryptographic-triangles.org",
"license": "MIT", "license": "MIT",
"architecture": { "architecture": {
"64bit": { "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" "hash": "6f002a669a7e92aaf3d8dd7b1ae80f06a086c99a15ca05cf107665009ffc06b7"
} }
}, },
@@ -1,5 +1,5 @@
PackageIdentifier: CryptographicTriangles.TrianglesQt PackageIdentifier: CryptographicTriangles.TrianglesQt
PackageVersion: 6.2.2 PackageVersion: 6.2.3
PackageLocale: en-US PackageLocale: en-US
Publisher: Cryptographic Triangles Publisher: Cryptographic Triangles
PublisherUrl: https://cryptographic-triangles.org PublisherUrl: https://cryptographic-triangles.org
@@ -27,7 +27,7 @@ Installers:
- RelativeFilePath: triangles-qt.exe - RelativeFilePath: triangles-qt.exe
PortableCommandAlias: triangles-qt PortableCommandAlias: triangles-qt
ArchiveBinariesDependOnPath: true 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 InstallerSha256: 6F002A669A7E92AAF3D8DD7B1AE80F06A086C99A15CA05CF107665009FFC06B7
ManifestType: singleton ManifestType: singleton
ManifestVersion: 1.6.0 ManifestVersion: 1.6.0
+5 -5
View File
@@ -1,6 +1,6 @@
name: triangles name: triangles
base: core22 base: core22
version: '6.2.2' version: '6.2.3'
summary: Cryptographic Triangles (TRI) cryptocurrency wallet summary: Cryptographic Triangles (TRI) cryptocurrency wallet
description: | description: |
Privacy-focused cryptocurrency featuring Proof-of-Stake consensus, Privacy-focused cryptocurrency featuring Proof-of-Stake consensus,
@@ -51,10 +51,10 @@ apps:
parts: parts:
triangles: triangles:
plugin: dump 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 source-type: file
organize: 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: stage-packages:
- libqt5widgets5 - libqt5widgets5
- libqt5gui5 - libqt5gui5
@@ -73,10 +73,10 @@ parts:
trianglesd: trianglesd:
plugin: dump 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 source-type: file
organize: organize:
Cryptographic-Triangles-v6.2.2-linux-x64-daemon: bin/trianglesd Cryptographic-Triangles-v6.2.3-linux-x64-daemon: bin/trianglesd
desktop-entry: desktop-entry:
plugin: dump plugin: dump
+1 -1
View File
@@ -8,7 +8,7 @@
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it // 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_MAJOR 6
#define CLIENT_VERSION_MINOR 2 #define CLIENT_VERSION_MINOR 2
#define CLIENT_VERSION_REVISION 2 #define CLIENT_VERSION_REVISION 3
#define CLIENT_VERSION_BUILD 0 #define CLIENT_VERSION_BUILD 0
// Converts the parameter X to a string after macro replacement on X has been performed. // Converts the parameter X to a string after macro replacement on X has been performed.
+41 -5
View File
@@ -1298,6 +1298,15 @@ bool AppInit2()
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n"); printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
uiInterface.InitMessage(_("Loading UTXO snapshot...")); 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; std::string strError;
const int snapshotHeight = Checkpoints::GetBestSnapshotHeight(); const int snapshotHeight = Checkpoints::GetBestSnapshotHeight();
uint256 compiledHash; uint256 compiledHash;
@@ -1307,14 +1316,41 @@ bool AppInit2()
const bool hashVerified = hasCompiledHash && const bool hashVerified = hasCompiledHash &&
SnapshotNet::ComputeSnapshotFileHash(snapshotFile, actualHash, strError) && SnapshotNet::ComputeSnapshotFileHash(snapshotFile, actualHash, strError) &&
actualHash == compiledHash; 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 (forceAccept) {
if (strError.empty()) printf("UTXO snapshot SHA256 NOT in compiled map; "
strError = "snapshot SHA256 is not compiled into this release"; "-acceptanylocalsnapshot set, accepting anyway.\n");
printf("UTXO snapshot rejected before import: %s\n", strError.c_str()); 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"); printf("Will proceed with normal sync.\n");
} else if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError, } else if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError,
/*requireCheckpoint=*/true)) { /*requireCheckpoint=*/false)) {
printf("UTXO snapshot loaded successfully.\n"); printf("UTXO snapshot loaded successfully.\n");
} else { } else {
printf("UTXO snapshot load failed: %s\n", strError.c_str()); printf("UTXO snapshot load failed: %s\n", strError.c_str());