diff --git a/.github/workflows/build-all.yml b/.github/workflows/build-all.yml index 0582399..ab1b1ab 100644 --- a/.github/workflows/build-all.yml +++ b/.github/workflows/build-all.yml @@ -9,7 +9,7 @@ on: workflow_dispatch: env: - VERSION: "5.3.6" + VERSION: "5.3.7" jobs: test-linux-unit: diff --git a/DNS2-SEED-NODE-SETUP.md b/DNS2-SEED-NODE-SETUP.md index 1888436..779e609 100644 --- a/DNS2-SEED-NODE-SETUP.md +++ b/DNS2-SEED-NODE-SETUP.md @@ -37,7 +37,7 @@ No building required. Download the pre-built Linux binary from GitHub: ```bash cd /tmp -curl -L -o trianglesd https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-daemon +curl -L -o trianglesd https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon chmod +x trianglesd sudo mv trianglesd /usr/local/bin/ ``` diff --git a/Makefile b/Makefile index 708f431..908d863 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # Generated by qmake (3.1) (Qt 5.15.18) # Project: triangles-qt.pro # Template: app -# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release +# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro ############################################################################# MAKEFILE = Makefile @@ -156,7 +156,7 @@ Makefile: triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.c C:/msys64/mingw64/lib/qtmain.prl \ C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \ src/qt/triangles.qrc - $(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release + $(QMAKE) -o Makefile triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf: C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri: C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf: @@ -244,7 +244,7 @@ C:/msys64/mingw64/lib/qtmain.prl: C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf: src/qt/triangles.qrc: qmake: FORCE - @$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release + @$(QMAKE) -o Makefile triangles-qt.pro qmake_all: FORCE diff --git a/packaging/appimage/build-appimage.sh b/packaging/appimage/build-appimage.sh index 909d3b6..7e51630 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="5.3.6" +VERSION="5.3.7" APPDIR="Triangles-x86_64.AppDir" RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}" diff --git a/packaging/appstream/org.cryptographic_triangles.TrianglesQt.metainfo.xml b/packaging/appstream/org.cryptographic_triangles.TrianglesQt.metainfo.xml index e6ccf27..0b92c8e 100644 --- a/packaging/appstream/org.cryptographic_triangles.TrianglesQt.metainfo.xml +++ b/packaging/appstream/org.cryptographic_triangles.TrianglesQt.metainfo.xml @@ -41,6 +41,11 @@ + + +

Version 5.3.7 release.

+
+

IBD sync optimizations, Linux build fixes, and modern compiler support.

diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD index 7972826..28d7893 100644 --- a/packaging/aur/PKGBUILD +++ b/packaging/aur/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Cryptographic Triangles Team pkgname=triangles-qt-bin -pkgver=5.3.6 +pkgver=5.3.7 pkgrel=1 pkgdesc="Cryptographic Triangles (TRI) cryptocurrency wallet - Qt GUI" arch=('x86_64') diff --git a/packaging/chocolatey/tools/chocolateyInstall.ps1 b/packaging/chocolatey/tools/chocolateyInstall.ps1 index 002ec03..37ee293 100644 --- a/packaging/chocolatey/tools/chocolateyInstall.ps1 +++ b/packaging/chocolatey/tools/chocolateyInstall.ps1 @@ -3,7 +3,7 @@ $ErrorActionPreference = 'Stop' $packageArgs = @{ packageName = 'triangles' unzipLocation = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)" - url64bit = 'https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.6/Cryptographic-Triangles-5.3.6-win-x64.zip' + url64bit = 'https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-5.3.7-win-x64.zip' checksum64 = '6f002a669a7e92aaf3d8dd7b1ae80f06a086c99a15ca05cf107665009ffc06b7' checksumType64 = 'sha256' } diff --git a/packaging/chocolatey/triangles.nuspec b/packaging/chocolatey/triangles.nuspec index 5bb2dac..7cb4457 100644 --- a/packaging/chocolatey/triangles.nuspec +++ b/packaging/chocolatey/triangles.nuspec @@ -2,7 +2,7 @@ triangles - 5.3.6 + 5.3.7 Cryptographic Triangles Cryptographic Triangles Team SamiAhmed7777 @@ -25,6 +25,6 @@ featuring the unique Hash9 algorithm (13-step hash cascade). - Encrypted peer-to-peer messaging - Tor v3 integration for anonymous transactions
- https://github.com/SamiAhmed7777/triangles_v5/releases/tag/v5.3.6 + https://github.com/SamiAhmed7777/triangles_v5/releases/tag/v5.3.7 diff --git a/packaging/debian/DEBIAN/control b/packaging/debian/DEBIAN/control index 283d061..f2edf59 100644 --- a/packaging/debian/DEBIAN/control +++ b/packaging/debian/DEBIAN/control @@ -1,5 +1,5 @@ Package: triangles -Version: 5.3.6-1 +Version: 5.3.7-1 Section: net Priority: optional Architecture: amd64 diff --git a/packaging/debian/build-deb.sh b/packaging/debian/build-deb.sh index 5b3df7e..e4ca054 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="5.3.6" +VERSION="5.3.7" 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 aa9bbed..54f4732 100644 --- a/packaging/docker/Dockerfile +++ b/packaging/docker/Dockerfile @@ -2,9 +2,9 @@ FROM ubuntu:22.04 LABEL maintainer="Cryptographic Triangles Team" LABEL description="Cryptographic Triangles (TRI) headless daemon" -LABEL version="5.3.6" +LABEL version="5.3.7" -ARG VERSION=5.3.6 +ARG VERSION=5.3.7 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ diff --git a/packaging/docker/docker-compose.yml b/packaging/docker/docker-compose.yml index bd411c4..bab0533 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:5.3.6 + image: cryptographic-triangles/trianglesd:5.3.7 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 a9b8663..c613b9f 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/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-qt + url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-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/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-daemon + url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon sha256: 4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517 dest-filename: trianglesd-linux diff --git a/packaging/homebrew/triangles.rb b/packaging/homebrew/triangles.rb index 998637a..f50279e 100644 --- a/packaging/homebrew/triangles.rb +++ b/packaging/homebrew/triangles.rb @@ -2,15 +2,15 @@ class Triangles < Formula desc "Cryptographic Triangles (TRI) cryptocurrency wallet and daemon" homepage "https://cryptographic-triangles.org" license "MIT" - version "5.3.6" + version "5.3.7" on_macos do - url "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.6/Cryptographic-Triangles-v5.3.6-macos-arm64.dmg" + url "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-macos-arm64.dmg" sha256 "3a58e795d898656b455fd639c0ea826a4457d390a64d00ace9a1257598d053be" end on_linux do - url "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-daemon" + url "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon" sha256 "4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517" end @@ -21,7 +21,7 @@ class Triangles < Formula prefix.install "Triangles-Qt.app" bin.write_exec_script prefix/"Triangles-Qt.app/Contents/MacOS/Triangles-Qt" else - bin.install "Cryptographic-Triangles-v5.3.6-linux-x64-daemon" => "trianglesd" + bin.install "Cryptographic-Triangles-v5.3.7-linux-x64-daemon" => "trianglesd" end end diff --git a/packaging/nix/default.nix b/packaging/nix/default.nix index ccb37af..3bacc0a 100644 --- a/packaging/nix/default.nix +++ b/packaging/nix/default.nix @@ -14,7 +14,7 @@ }: let - version = "5.3.6"; + version = "5.3.7"; desktopItem = makeDesktopItem { name = "triangles-qt"; diff --git a/packaging/rpm/build-rpm.sh b/packaging/rpm/build-rpm.sh index a7852fd..ef15c8e 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="5.3.6" +VERSION="5.3.7" 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 1debc9f..ccc2226 100644 --- a/packaging/rpm/triangles.spec +++ b/packaging/rpm/triangles.spec @@ -1,5 +1,5 @@ Name: triangles -Version: 5.3.6 +Version: 5.3.7 Release: 1%{?dist} Summary: Cryptographic Triangles (TRI) cryptocurrency wallet License: MIT diff --git a/packaging/winget/CryptographicTriangles.TrianglesQt.yaml b/packaging/winget/CryptographicTriangles.TrianglesQt.yaml index 8834ee1..336f744 100644 --- a/packaging/winget/CryptographicTriangles.TrianglesQt.yaml +++ b/packaging/winget/CryptographicTriangles.TrianglesQt.yaml @@ -1,5 +1,5 @@ PackageIdentifier: CryptographicTriangles.TrianglesQt -PackageVersion: 5.3.6 +PackageVersion: 5.3.7 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/v5.3.6/Cryptographic-Triangles-5.3.6-win-x64.zip + InstallerUrl: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-5.3.7-win-x64.zip InstallerSha256: 6F002A669A7E92AAF3D8DD7B1AE80F06A086C99A15CA05CF107665009FFC06B7 ManifestType: singleton ManifestVersion: 1.6.0 diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 9b958e6..26096e5 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,6 +1,6 @@ name: triangles base: core22 -version: '5.3.6' +version: '5.3.7' 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/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-qt + source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-qt source-type: file organize: - Cryptographic-Triangles-v5.3.6-linux-x64-qt: bin/triangles-qt + Cryptographic-Triangles-v5.3.7-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/v5.3.6/Cryptographic-Triangles-v5.3.6-linux-x64-daemon + source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon source-type: file organize: - Cryptographic-Triangles-v5.3.6-linux-x64-daemon: bin/trianglesd + Cryptographic-Triangles-v5.3.7-linux-x64-daemon: bin/trianglesd desktop-entry: plugin: dump diff --git a/src/bootstrap.cpp b/src/bootstrap.cpp index e03f627..ed6f957 100644 --- a/src/bootstrap.cpp +++ b/src/bootstrap.cpp @@ -10,10 +10,18 @@ #include +#include "version.h" +#include "uint256.h" + #include #include #include #include +#include + +// Forward declarations to avoid pulling in heavy consensus headers +extern bool fTestNet; +namespace Checkpoints { bool IsKnownCheckpoint(int nHeight, const uint256& hash); } namespace fs = boost::filesystem; using boost::asio::ip::tcp; @@ -311,6 +319,112 @@ static bool ExtractTarGz(const fs::path& tarGzPath, } // anonymous namespace +bool ParseManifest(const fs::path& manifestPath, + SnapshotManifest& manifest, + std::string& strError) +{ + std::ifstream in(manifestPath.string().c_str()); + if (!in.is_open()) { + strError = "Cannot open " + manifestPath.string(); + return false; + } + + manifest.format = 0; + manifest.network.clear(); + manifest.height = -1; + manifest.hash.clear(); + manifest.dbversion = 0; + + std::string line; + while (std::getline(in, line)) { + boost::trim(line); + if (line.empty() || line[0] == '#') + continue; + + size_t eq = line.find('='); + if (eq == std::string::npos) + continue; + + std::string key = line.substr(0, eq); + std::string val = line.substr(eq + 1); + boost::trim(key); + boost::trim(val); + + if (key == "format") + manifest.format = std::atoi(val.c_str()); + else if (key == "network") + manifest.network = val; + else if (key == "height") + manifest.height = std::atoi(val.c_str()); + else if (key == "hash") + manifest.hash = val; + else if (key == "dbversion") + manifest.dbversion = std::atoi(val.c_str()); + } + in.close(); + + if (manifest.format == 0) { + strError = "Manifest missing 'format' field"; + return false; + } + if (manifest.network.empty()) { + strError = "Manifest missing 'network' field"; + return false; + } + if (manifest.height < 0) { + strError = "Manifest missing or invalid 'height' field"; + return false; + } + if (manifest.hash.empty()) { + strError = "Manifest missing 'hash' field"; + return false; + } + if (manifest.dbversion == 0) { + strError = "Manifest missing 'dbversion' field"; + return false; + } + + return true; +} + +bool VerifyManifest(const SnapshotManifest& manifest, + std::string& strError) +{ + if (manifest.format != 1) { + strError = "Unsupported manifest format: " + std::to_string(manifest.format); + return false; + } + + std::string expectedNetwork = fTestNet ? "test" : "main"; + if (manifest.network != expectedNetwork) { + strError = "Network mismatch: manifest says '" + manifest.network + + "', expected '" + expectedNetwork + "'"; + return false; + } + + if (manifest.dbversion != DATABASE_VERSION) { + strError = "DB version mismatch: manifest says " + + std::to_string(manifest.dbversion) + + ", binary expects " + std::to_string(DATABASE_VERSION); + return false; + } + + uint256 manifestHash(manifest.hash); + if (manifestHash == 0) { + strError = "Invalid hash in manifest: " + manifest.hash; + return false; + } + + if (!Checkpoints::IsKnownCheckpoint(manifest.height, manifestHash)) { + strError = "Height " + std::to_string(manifest.height) + + " / hash " + manifest.hash + + " is not a known checkpoint"; + return false; + } + + return true; +} + bool DownloadBootstrap(const std::string& host, const fs::path& dataDir, ProgressCallback progressFn, @@ -362,16 +476,55 @@ bool DownloadBootstrap(const std::string& host, return false; } - // Remove any extracted txleveldb/ and database/ - they were built on - // a different machine and won't work here. FastImportBlockFile() will - // rebuild the index directly from blk0001.dat on next startup. + // Check if the archive included a trusted pre-built index (txleveldb/) + // with a valid snapshot.manifest. If verified, keep it to skip the + // multi-hour FastImportBlockFile() rebuild. fs::path txleveldb = dataDir / "txleveldb"; - fs::path database = dataDir / "database"; - if (fs::exists(txleveldb)) - fs::remove_all(txleveldb); + fs::path database = dataDir / "database"; + fs::path manifestPath = dataDir / "snapshot.manifest"; + + bool keepIndex = false; + + if (fs::exists(manifestPath) && fs::exists(txleveldb)) { + SnapshotManifest manifest; + std::string manifestError; + + if (ParseManifest(manifestPath, manifest, manifestError)) { + printf("Bootstrap: snapshot.manifest found (format=%d, network=%s, " + "height=%d, dbversion=%d)\n", + manifest.format, manifest.network.c_str(), + manifest.height, manifest.dbversion); + + if (VerifyManifest(manifest, manifestError)) { + printf("Bootstrap: manifest verified - keeping pre-built index " + "(height %d, checkpoint match)\n", manifest.height); + keepIndex = true; + } else { + printf("Bootstrap: manifest verification failed: %s\n", + manifestError.c_str()); + } + } else { + printf("Bootstrap: cannot parse snapshot.manifest: %s\n", + manifestError.c_str()); + } + } + + if (!keepIndex) { + // No valid manifest or verification failed - delete the index. + // FastImportBlockFile() will rebuild from blk0001.dat on next startup. + printf("Bootstrap: removing extracted txleveldb/ (will rebuild index from blk0001.dat)\n"); + if (fs::exists(txleveldb)) + fs::remove_all(txleveldb); + } + + // Always remove BDB database/ dir (wallet environment from another machine) if (fs::exists(database)) fs::remove_all(database); + // Clean up manifest file (not needed after verification) + if (fs::exists(manifestPath)) + fs::remove(manifestPath); + return true; } diff --git a/src/bootstrap.h b/src/bootstrap.h index 87f3fca..8a52f82 100644 --- a/src/bootstrap.h +++ b/src/bootstrap.h @@ -41,6 +41,24 @@ namespace Bootstrap { ProgressCallback progressFn, std::string& strError); + // Snapshot manifest (parsed from snapshot.manifest in bootstrap archive) + struct SnapshotManifest { + int format; // format version, must be 1 + std::string network; // "main" or "test" + int height; // block height of the snapshot tip + std::string hash; // block hash at that height (hex, no 0x prefix) + int dbversion; // DATABASE_VERSION the txleveldb was built with + }; + + // Parse a snapshot.manifest file into a SnapshotManifest struct. + bool ParseManifest(const boost::filesystem::path& manifestPath, + SnapshotManifest& manifest, + std::string& strError); + + // Verify a parsed manifest against compiled-in checkpoints and config. + bool VerifyManifest(const SnapshotManifest& manifest, + std::string& strError); + } // namespace Bootstrap #endif // TRIANGLES_BOOTSTRAP_H diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index 4bb98af..19edede 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -60,6 +60,14 @@ namespace Checkpoints return hash == i->second; } + bool IsKnownCheckpoint(int nHeight, const uint256& hash) + { + MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); + MapCheckpoints::const_iterator i = checkpoints.find(nHeight); + if (i == checkpoints.end()) return false; + return hash == i->second; + } + int GetTotalBlocksEstimate() { MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints); diff --git a/src/checkpoints.h b/src/checkpoints.h index d6c0557..24c222b 100644 --- a/src/checkpoints.h +++ b/src/checkpoints.h @@ -39,6 +39,9 @@ namespace Checkpoints // Returns true if block passes checkpoint checks bool CheckHardened(int nHeight, const uint256& hash); + // Returns true only if (nHeight, hash) is an exact entry in mapCheckpoints + bool IsKnownCheckpoint(int nHeight, const uint256& hash); + // Return conservative estimate of total number of blocks, 0 if unknown int GetTotalBlocksEstimate(); diff --git a/src/clientversion.h b/src/clientversion.h index 55bfdff..2e3edfa 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 5 #define CLIENT_VERSION_MINOR 3 -#define CLIENT_VERSION_REVISION 6 +#define CLIENT_VERSION_REVISION 7 #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 f0e400d..dd75e77 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -880,6 +880,22 @@ bool AppInit2() } printf(" block index %15" PRId64 "ms\n", GetTimeMillis() - nStart); + // Diagnostic: check for blocks in mapBlockIndex above pindexBest + { + int nMaxIndexHeight = 0; + int nAboveBest = 0; + for (std::map::iterator it = mapBlockIndex.begin(); + it != mapBlockIndex.end(); ++it) + { + if (it->second->nHeight > nMaxIndexHeight) + nMaxIndexHeight = it->second->nHeight; + if (it->second->nHeight > nBestHeight) + nAboveBest++; + } + printf("SYNC-DIAG: mapBlockIndex=%d entries, maxHeight=%d, bestHeight=%d, aboveBest=%d\n", + (int)mapBlockIndex.size(), nMaxIndexHeight, nBestHeight, nAboveBest); + } + if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree")) { PrintBlockTree(); diff --git a/src/main.cpp b/src/main.cpp index cf2ca8b..963a98f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,6 +60,8 @@ int nCoinbaseMaturity = 7; //overall maturity: currently 7 blocks, maybe subject CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; +int nHighestInvWalk = 0; // height of walk-forward progress through already-have inv +uint256 hashHighestInvWalk = 0; // hash of that block uint256 nBestChainTrust = 0; uint256 nBestInvalidTrust = 0; @@ -3941,7 +3943,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) nBlockInv, nTxInv, pfrom->addr.ToString().c_str(), nBestHeight); CTxDB txdb("r"); - int nNew = 0, nAlready = 0; + int nNew = 0, nAlready = 0, nAboveBest = 0; + int nFirstInvHeight = -1, nLastInvHeight = -1; for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) { const CInv &inv = vInv[nInv]; @@ -3952,7 +3955,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fAlreadyHave = AlreadyHave(txdb, inv); if (inv.type == MSG_BLOCK) { - if (fAlreadyHave) nAlready++; else nNew++; + if (fAlreadyHave) { + nAlready++; + std::map::iterator mi = mapBlockIndex.find(inv.hash); + if (mi != mapBlockIndex.end()) { + int h = mi->second->nHeight; + if (nFirstInvHeight == -1) nFirstInvHeight = h; + nLastInvHeight = h; + if (h > nBestHeight) nAboveBest++; + } + } else { + nNew++; + } } if (!fAlreadyHave) @@ -3960,15 +3974,30 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) { pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash])); } else if (nInv == nLastBlock) { + // Continuation: walk forward from the last inv block. + // Don't jump to pindexBest — its CBlockLocator exponential + // spacing can map back to the same old match point, looping. + // Walking from the last inv block progresses linearly through + // the "already have" zone until we reach new blocks. + int nInvH = mapBlockIndex[inv.hash]->nHeight; + if (nInvH > nHighestInvWalk) { + nHighestInvWalk = nInvH; + hashHighestInvWalk = inv.hash; + } + pfrom->pindexLastGetBlocksBegin = NULL; // reset dedup pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0)); - printf("IBD-DIAG: inv last block already known, pushing getblocks from %d\n", - mapBlockIndex[inv.hash]->nHeight); + printf("SYNC-DIAG: inv walk-forward from %d (best=%d, walk=%d)\n", + nInvH, nBestHeight, nHighestInvWalk); } Inventory(inv.hash); } - if (nBlockInv > 0) - printf("IBD-DIAG: inv result: %d new blocks requested, %d already have\n", nNew, nAlready); + if (nBlockInv > 0) { + printf("SYNC-DIAG: inv result: %d new, %d already have (%d above best=%d), range=%d..%d\n", + nNew, nAlready, nAboveBest, nBestHeight, nFirstInvHeight, nLastInvHeight); + if (nNew > 0 && nAlready > 0) + printf("SYNC-DIAG: *** FORK POINT CROSSED *** - downloading %d new blocks from canonical chain\n", nNew); + } } @@ -4770,27 +4799,43 @@ bool SendMessages(CNode* pto, bool fSendTrickle) // - // Stall detection: if IBD and no new blocks for 5 seconds, re-request. - // Tighter than the old 10s to rotate away from slow peers faster. + // Stall detection: if we're still catching up and no new blocks for + // a while, re-request. Active during IBD (5s timeout) and also + // post-IBD when we're behind peers (30s timeout) to handle the case + // where IBD flips to false during a transient download gap. // - if (IsInitialBlockDownload() && !pto->fClient) + if (!pto->fClient && nBestHeight < GetNumBlocksOfPeers()) { static int64_t nLastBlockReceived = 0; static int nLastHeight = 0; static int64_t nLastStallLog = 0; + int nStallTimeout = IsInitialBlockDownload() ? 5 : 15; if (nBestHeight > nLastHeight) { nLastHeight = nBestHeight; nLastBlockReceived = GetTime(); - } else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) { - if (GetTime() - nLastStallLog >= 30) { // log every 30s max - printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n", - nBestHeight, (int)(GetTime() - nLastBlockReceived), + } else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > nStallTimeout) { + if (GetTime() - nLastStallLog >= 15) { // log every 15s max + printf("SYNC-DIAG: STALL at height %d/%d for %ds (IBD=%d walk=%d), peer=%s askfor_queue=%d\n", + nBestHeight, GetNumBlocksOfPeers(), + (int)(GetTime() - nLastBlockReceived), + IsInitialBlockDownload(), nHighestInvWalk, pto->addr.ToString().c_str(), - (int)pto->mapAskFor.size(), (int)pto->nSendSize); + (int)pto->mapAskFor.size()); nLastStallLog = GetTime(); } + // Use the walk-forward progress point if available, to avoid + // restarting from pindexBest (which hits the CBlockLocator + // exponential gap and starts the walk-forward from scratch). pto->pindexLastGetBlocksBegin = NULL; - pto->PushGetBlocks(pindexBest, uint256(0)); + if (nHighestInvWalk > nBestHeight && hashHighestInvWalk != 0 && + mapBlockIndex.count(hashHighestInvWalk)) + { + pto->PushGetBlocks(mapBlockIndex[hashHighestInvWalk], uint256(0)); + printf("SYNC-DIAG: stall re-request from walk=%d (not best=%d)\n", + nHighestInvWalk, nBestHeight); + } else { + pto->PushGetBlocks(pindexBest, uint256(0)); + } nLastBlockReceived = GetTime(); } } diff --git a/src/qt/messagemodel.cpp b/src/qt/messagemodel.cpp index f22f403..4254571 100644 --- a/src/qt/messagemodel.cpp +++ b/src/qt/messagemodel.cpp @@ -19,6 +19,7 @@ #include #include #include +#include Q_DECLARE_METATYPE(std::vector); @@ -27,6 +28,24 @@ QList ambiguous; /**< Specifies Ambiguous addresses */ const QString MessageModel::Sent = "Sent"; const QString MessageModel::Received = "Received"; +namespace { + +static QString FormatShortMessage(const QString& message) +{ + static const int kMaxPreviewChars = 80; + + QTextDocument doc; + doc.setHtml(message); + + QString preview = doc.toPlainText().simplified(); + if (preview.length() <= kMaxPreviewChars) + return preview; + + return preview.left(kMaxPreviewChars - 3) + "..."; +} + +} // namespace + struct MessageTableEntryLessThan { bool operator()(const MessageTableEntry &a, const MessageTableEntry &b) const {return a.received_datetime < b.received_datetime;}; @@ -485,7 +504,7 @@ QVariant MessageModel::data(const QModelIndex &index, int role) const case FilterAddressRole: return (rec->type == MessageTableEntry::Sent ? rec->to_address + rec->from_address : rec->from_address + rec->to_address); case LabelRole: return rec->label; case MessageRole: return rec->message; - case ShortMessageRole: return rec->message; // TODO: Short message + case ShortMessageRole: return FormatShortMessage(rec->message); case HTMLRole: return rec->received_datetime.toString() + "
" + (rec->label.isEmpty() ? rec->from_address : rec->label) + "
" + rec->message; case Ambiguous: int it; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 07e6e7f..0823db5 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -52,11 +52,13 @@ class TransactionTablePriv public: TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent): wallet(wallet), - parent(parent) + parent(parent), + fInitialLoadDone(false) { } CWallet *wallet; TransactionTableModel *parent; + bool fInitialLoadDone; /* Local cache of wallet. * As it is in the same order as the CWallet, by definition @@ -68,22 +70,19 @@ public: */ void refreshWallet() { - OutputDebugStringF("refreshWallet\n"); + OutputDebugStringF("refreshWallet: fInitialLoadDone=%d mapWallet.size=%u\n", + (int)fInitialLoadDone, (unsigned)wallet->mapWallet.size()); cachedWallet.clear(); { - TRY_LOCK(wallet->cs_wallet, lockWallet); - if(!lockWallet) - { - // Lock busy (block processing), retry in 500ms - QTimer::singleShot(500, parent, SLOT(refreshWallet())); - return; - } + LOCK(wallet->cs_wallet); for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) { if(TransactionRecord::showTransaction(it->second)) cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); } + fInitialLoadDone = true; } + OutputDebugStringF("refreshWallet: loaded %d transaction records\n", cachedWallet.size()); } /* Update our model of the wallet incrementally, to synchronize our model of the wallet @@ -97,7 +96,12 @@ public: { TRY_LOCK(wallet->cs_wallet, lockWallet); if (!lockWallet) + { + // Lock busy - schedule a full refresh to pick up missed updates. + // This avoids silently dropping CT_NEW notifications. + QTimer::singleShot(500, parent, SLOT(refreshWallet())); return; + } // Find transaction in wallet std::map::iterator mi = wallet->mapWallet.find(hash); @@ -243,7 +247,12 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren cachedNumBlocks(0) { columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount"); - QTimer::singleShot(0, this, SLOT(refreshWallet())); + + // Load transactions synchronously in the constructor so they're + // available before the event loop starts. The deferred QTimer approach + // was never firing because queued updateTransaction events from sync + // would flood the event queue first. + priv->refreshWallet(); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations())); @@ -267,8 +276,10 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status) void TransactionTableModel::refreshWallet() { + beginResetModel(); priv->refreshWallet(); - reset(); + endResetModel(); + OutputDebugStringF("TransactionTableModel::refreshWallet: rowCount=%d\n", priv->size()); } void TransactionTableModel::updateConfirmations() diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index dc01fbe..89afa3b 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -78,20 +78,22 @@ void WalletModel::pollBalanceChanged() { if(nBestHeight != cachedNumBlocks) { - // Balance and number of transactions might have changed - cachedNumBlocks = nBestHeight; - checkBalanceChanged(); + // Balance and number of transactions might have changed. + // Only update cachedNumBlocks AFTER a successful balance check, + // otherwise a TRY_LOCK failure loses the update permanently. + if(checkBalanceChanged()) + cachedNumBlocks = nBestHeight; } } -void WalletModel::checkBalanceChanged() +bool WalletModel::checkBalanceChanged() { // Get all balances in a single lock acquisition + single pass. // Uses TRY_LOCK internally - if cs_wallet is busy (block processing), // skip this cycle. The timer will retry in 2.5 seconds. int64_t newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0; if (!wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance)) - return; + return false; if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) { @@ -101,6 +103,7 @@ void WalletModel::checkBalanceChanged() cachedImmatureBalance = newImmatureBalance; emit balanceChanged(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance); } + return true; } void WalletModel::updateTransaction(const QString &hash, int status) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 1af8980..95e2197 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -152,7 +152,7 @@ private: void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); - void checkBalanceChanged(); + bool checkBalanceChanged(); public slots: diff --git a/src/trianglesrpc.cpp b/src/trianglesrpc.cpp index 30018da..c78cc23 100644 --- a/src/trianglesrpc.cpp +++ b/src/trianglesrpc.cpp @@ -787,10 +787,16 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); - // TODO: Actually handle errors if (error) { + if (error != asio::error::operation_aborted) + printf("RPC accept error from %s: %s (%d)\n", + tcp_conn ? tcp_conn->peer.address().to_string().c_str() : "unknown peer", + error.message().c_str(), + error.value()); delete conn; + vnThreadsRunning[THREAD_RPCLISTENER]--; + return; } // Restrict callers by IP. It is important to diff --git a/src/version.h b/src/version.h index 2a765d0..3e337e2 100644 --- a/src/version.h +++ b/src/version.h @@ -53,7 +53,7 @@ static const int MEMPOOL_GD_VERSION = 60002; #define DISPLAY_VERSION_MAJOR 5 #define DISPLAY_VERSION_MINOR 3 -#define DISPLAY_VERSION_REVISION 6 +#define DISPLAY_VERSION_REVISION 7 #define DISPLAY_VERSION_BUILD 0 #endif