[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/),
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
+1 -1
View File
@@ -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}"
+1 -1
View File
@@ -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}"
+3 -3
View File
@@ -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 \
+1 -1
View File
@@ -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:
@@ -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
+1 -1
View File
@@ -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}..."
+1 -1
View File
@@ -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
+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",
"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"
}
},
@@ -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
+5 -5
View File
@@ -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
+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
#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.
+41 -5
View File
@@ -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());