v5.3.7: Fix sync stall, transaction display, balance updates, and bootstrap snapshots
Sync fixes: - Extend stall detection beyond IBD to catch post-IBD sync gaps - Walk-forward inv continuation to avoid CBlockLocator exponential gap loop - Track walk-forward progress for stall recovery without restarting from scratch GUI fixes: - Load transactions synchronously in constructor (deferred QTimer never fired) - Use beginResetModel/endResetModel instead of deprecated reset() - Schedule full refresh on TRY_LOCK failure to avoid dropped CT_NEW notifications - Only update cachedNumBlocks after successful balance check (prevents permanent loss) - Add GetAllBalances() single-pass balance retrieval with TRY_LOCK Bootstrap: - Add trusted snapshot manifest verification for bootstrap archives - Add IsKnownCheckpoint() to validate manifest against compiled-in checkpoints - Skip txleveldb rebuild when verified manifest is present Bump version to 5.3.7 across all packaging manifests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "5.3.6"
|
VERSION: "5.3.7"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-linux-unit:
|
test-linux-unit:
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ No building required. Download the pre-built Linux binary from GitHub:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /tmp
|
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
|
chmod +x trianglesd
|
||||||
sudo mv trianglesd /usr/local/bin/
|
sudo mv trianglesd /usr/local/bin/
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Generated by qmake (3.1) (Qt 5.15.18)
|
# Generated by qmake (3.1) (Qt 5.15.18)
|
||||||
# Project: triangles-qt.pro
|
# Project: triangles-qt.pro
|
||||||
# Template: app
|
# 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
|
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/lib/qtmain.prl \
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
|
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
|
||||||
src/qt/triangles.qrc
|
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/features/spec_pre.prf:
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
|
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf:
|
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:
|
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf:
|
||||||
src/qt/triangles.qrc:
|
src/qt/triangles.qrc:
|
||||||
qmake: FORCE
|
qmake: FORCE
|
||||||
@$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
@$(QMAKE) -o Makefile triangles-qt.pro
|
||||||
|
|
||||||
qmake_all: FORCE
|
qmake_all: FORCE
|
||||||
|
|
||||||
|
|||||||
@@ -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="5.3.6"
|
VERSION="5.3.7"
|
||||||
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}"
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,11 @@
|
|||||||
</provides>
|
</provides>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release version="5.3.7" date="2026-03-24">
|
||||||
|
<description>
|
||||||
|
<p>Version 5.3.7 release.</p>
|
||||||
|
</description>
|
||||||
|
</release>
|
||||||
<release version="5.3.6" date="2026-03-23">
|
<release version="5.3.6" date="2026-03-23">
|
||||||
<description>
|
<description>
|
||||||
<p>IBD sync optimizations, Linux build fixes, and modern compiler support.</p>
|
<p>IBD sync optimizations, Linux build fixes, and modern compiler support.</p>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Cryptographic Triangles Team
|
# Maintainer: Cryptographic Triangles Team
|
||||||
pkgname=triangles-qt-bin
|
pkgname=triangles-qt-bin
|
||||||
pkgver=5.3.6
|
pkgver=5.3.7
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Cryptographic Triangles (TRI) cryptocurrency wallet - Qt GUI"
|
pkgdesc="Cryptographic Triangles (TRI) cryptocurrency wallet - Qt GUI"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ $ErrorActionPreference = 'Stop'
|
|||||||
$packageArgs = @{
|
$packageArgs = @{
|
||||||
packageName = 'triangles'
|
packageName = 'triangles'
|
||||||
unzipLocation = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)"
|
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'
|
checksum64 = '6f002a669a7e92aaf3d8dd7b1ae80f06a086c99a15ca05cf107665009ffc06b7'
|
||||||
checksumType64 = 'sha256'
|
checksumType64 = 'sha256'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>triangles</id>
|
<id>triangles</id>
|
||||||
<version>5.3.6</version>
|
<version>5.3.7</version>
|
||||||
<title>Cryptographic Triangles</title>
|
<title>Cryptographic Triangles</title>
|
||||||
<authors>Cryptographic Triangles Team</authors>
|
<authors>Cryptographic Triangles Team</authors>
|
||||||
<owners>SamiAhmed7777</owners>
|
<owners>SamiAhmed7777</owners>
|
||||||
@@ -25,6 +25,6 @@ featuring the unique Hash9 algorithm (13-step hash cascade).
|
|||||||
- Encrypted peer-to-peer messaging
|
- Encrypted peer-to-peer messaging
|
||||||
- Tor v3 integration for anonymous transactions
|
- Tor v3 integration for anonymous transactions
|
||||||
</description>
|
</description>
|
||||||
<releaseNotes>https://github.com/SamiAhmed7777/triangles_v5/releases/tag/v5.3.6</releaseNotes>
|
<releaseNotes>https://github.com/SamiAhmed7777/triangles_v5/releases/tag/v5.3.7</releaseNotes>
|
||||||
</metadata>
|
</metadata>
|
||||||
</package>
|
</package>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Package: triangles
|
Package: triangles
|
||||||
Version: 5.3.6-1
|
Version: 5.3.7-1
|
||||||
Section: net
|
Section: net
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: amd64
|
Architecture: amd64
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Run from the packaging/debian directory
|
# Run from the packaging/debian directory
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
VERSION="5.3.6"
|
VERSION="5.3.7"
|
||||||
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}"
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ FROM ubuntu:22.04
|
|||||||
|
|
||||||
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="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 \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ version: "3.8"
|
|||||||
services:
|
services:
|
||||||
trianglesd:
|
trianglesd:
|
||||||
build: .
|
build: .
|
||||||
image: cryptographic-triangles/trianglesd:5.3.6
|
image: cryptographic-triangles/trianglesd:5.3.7
|
||||||
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/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
|
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/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
|
sha256: 4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517
|
||||||
dest-filename: trianglesd-linux
|
dest-filename: trianglesd-linux
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ class Triangles < Formula
|
|||||||
desc "Cryptographic Triangles (TRI) cryptocurrency wallet and daemon"
|
desc "Cryptographic Triangles (TRI) cryptocurrency wallet and daemon"
|
||||||
homepage "https://cryptographic-triangles.org"
|
homepage "https://cryptographic-triangles.org"
|
||||||
license "MIT"
|
license "MIT"
|
||||||
version "5.3.6"
|
version "5.3.7"
|
||||||
|
|
||||||
on_macos do
|
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"
|
sha256 "3a58e795d898656b455fd639c0ea826a4457d390a64d00ace9a1257598d053be"
|
||||||
end
|
end
|
||||||
|
|
||||||
on_linux do
|
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"
|
sha256 "4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class Triangles < Formula
|
|||||||
prefix.install "Triangles-Qt.app"
|
prefix.install "Triangles-Qt.app"
|
||||||
bin.write_exec_script prefix/"Triangles-Qt.app/Contents/MacOS/Triangles-Qt"
|
bin.write_exec_script prefix/"Triangles-Qt.app/Contents/MacOS/Triangles-Qt"
|
||||||
else
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
version = "5.3.6";
|
version = "5.3.7";
|
||||||
|
|
||||||
desktopItem = makeDesktopItem {
|
desktopItem = makeDesktopItem {
|
||||||
name = "triangles-qt";
|
name = "triangles-qt";
|
||||||
|
|||||||
@@ -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="5.3.6"
|
VERSION="5.3.7"
|
||||||
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,5 +1,5 @@
|
|||||||
Name: triangles
|
Name: triangles
|
||||||
Version: 5.3.6
|
Version: 5.3.7
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Cryptographic Triangles (TRI) cryptocurrency wallet
|
Summary: Cryptographic Triangles (TRI) cryptocurrency wallet
|
||||||
License: MIT
|
License: MIT
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
PackageIdentifier: CryptographicTriangles.TrianglesQt
|
PackageIdentifier: CryptographicTriangles.TrianglesQt
|
||||||
PackageVersion: 5.3.6
|
PackageVersion: 5.3.7
|
||||||
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/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
|
InstallerSha256: 6F002A669A7E92AAF3D8DD7B1AE80F06A086C99A15CA05CF107665009FFC06B7
|
||||||
ManifestType: singleton
|
ManifestType: singleton
|
||||||
ManifestVersion: 1.6.0
|
ManifestVersion: 1.6.0
|
||||||
|
|||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
name: triangles
|
name: triangles
|
||||||
base: core22
|
base: core22
|
||||||
version: '5.3.6'
|
version: '5.3.7'
|
||||||
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/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
|
source-type: file
|
||||||
organize:
|
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:
|
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/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
|
source-type: file
|
||||||
organize:
|
organize:
|
||||||
Cryptographic-Triangles-v5.3.6-linux-x64-daemon: bin/trianglesd
|
Cryptographic-Triangles-v5.3.7-linux-x64-daemon: bin/trianglesd
|
||||||
|
|
||||||
desktop-entry:
|
desktop-entry:
|
||||||
plugin: dump
|
plugin: dump
|
||||||
|
|||||||
+159
-6
@@ -10,10 +10,18 @@
|
|||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
#include "uint256.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
// 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;
|
namespace fs = boost::filesystem;
|
||||||
using boost::asio::ip::tcp;
|
using boost::asio::ip::tcp;
|
||||||
@@ -311,6 +319,112 @@ static bool ExtractTarGz(const fs::path& tarGzPath,
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // 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,
|
bool DownloadBootstrap(const std::string& host,
|
||||||
const fs::path& dataDir,
|
const fs::path& dataDir,
|
||||||
ProgressCallback progressFn,
|
ProgressCallback progressFn,
|
||||||
@@ -362,16 +476,55 @@ bool DownloadBootstrap(const std::string& host,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any extracted txleveldb/ and database/ - they were built on
|
// Check if the archive included a trusted pre-built index (txleveldb/)
|
||||||
// a different machine and won't work here. FastImportBlockFile() will
|
// with a valid snapshot.manifest. If verified, keep it to skip the
|
||||||
// rebuild the index directly from blk0001.dat on next startup.
|
// multi-hour FastImportBlockFile() rebuild.
|
||||||
fs::path txleveldb = dataDir / "txleveldb";
|
fs::path txleveldb = dataDir / "txleveldb";
|
||||||
fs::path database = dataDir / "database";
|
fs::path database = dataDir / "database";
|
||||||
if (fs::exists(txleveldb))
|
fs::path manifestPath = dataDir / "snapshot.manifest";
|
||||||
fs::remove_all(txleveldb);
|
|
||||||
|
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))
|
if (fs::exists(database))
|
||||||
fs::remove_all(database);
|
fs::remove_all(database);
|
||||||
|
|
||||||
|
// Clean up manifest file (not needed after verification)
|
||||||
|
if (fs::exists(manifestPath))
|
||||||
|
fs::remove(manifestPath);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,24 @@ namespace Bootstrap {
|
|||||||
ProgressCallback progressFn,
|
ProgressCallback progressFn,
|
||||||
std::string& strError);
|
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
|
} // namespace Bootstrap
|
||||||
|
|
||||||
#endif // TRIANGLES_BOOTSTRAP_H
|
#endif // TRIANGLES_BOOTSTRAP_H
|
||||||
|
|||||||
@@ -60,6 +60,14 @@ namespace Checkpoints
|
|||||||
return hash == i->second;
|
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()
|
int GetTotalBlocksEstimate()
|
||||||
{
|
{
|
||||||
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
|
MapCheckpoints& checkpoints = (fTestNet ? mapCheckpointsTestnet : mapCheckpoints);
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ namespace Checkpoints
|
|||||||
// Returns true if block passes checkpoint checks
|
// Returns true if block passes checkpoint checks
|
||||||
bool CheckHardened(int nHeight, const uint256& hash);
|
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
|
// Return conservative estimate of total number of blocks, 0 if unknown
|
||||||
int GetTotalBlocksEstimate();
|
int GetTotalBlocksEstimate();
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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 5
|
#define CLIENT_VERSION_MAJOR 5
|
||||||
#define CLIENT_VERSION_MINOR 3
|
#define CLIENT_VERSION_MINOR 3
|
||||||
#define CLIENT_VERSION_REVISION 6
|
#define CLIENT_VERSION_REVISION 7
|
||||||
#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.
|
||||||
|
|||||||
@@ -880,6 +880,22 @@ bool AppInit2()
|
|||||||
}
|
}
|
||||||
printf(" block index %15" PRId64 "ms\n", GetTimeMillis() - nStart);
|
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<uint256, CBlockIndex*>::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"))
|
if (GetBoolArg("-printblockindex") || GetBoolArg("-printblocktree"))
|
||||||
{
|
{
|
||||||
PrintBlockTree();
|
PrintBlockTree();
|
||||||
|
|||||||
+60
-15
@@ -60,6 +60,8 @@ int nCoinbaseMaturity = 7; //overall maturity: currently 7 blocks, maybe subject
|
|||||||
|
|
||||||
CBlockIndex* pindexGenesisBlock = NULL;
|
CBlockIndex* pindexGenesisBlock = NULL;
|
||||||
int nBestHeight = -1;
|
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 nBestChainTrust = 0;
|
||||||
uint256 nBestInvalidTrust = 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);
|
nBlockInv, nTxInv, pfrom->addr.ToString().c_str(), nBestHeight);
|
||||||
|
|
||||||
CTxDB txdb("r");
|
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++)
|
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
|
||||||
{
|
{
|
||||||
const CInv &inv = vInv[nInv];
|
const CInv &inv = vInv[nInv];
|
||||||
@@ -3952,7 +3955,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
|
|
||||||
bool fAlreadyHave = AlreadyHave(txdb, inv);
|
bool fAlreadyHave = AlreadyHave(txdb, inv);
|
||||||
if (inv.type == MSG_BLOCK) {
|
if (inv.type == MSG_BLOCK) {
|
||||||
if (fAlreadyHave) nAlready++; else nNew++;
|
if (fAlreadyHave) {
|
||||||
|
nAlready++;
|
||||||
|
std::map<uint256, CBlockIndex*>::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)
|
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)) {
|
else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) {
|
||||||
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
|
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
|
||||||
} else if (nInv == nLastBlock) {
|
} 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));
|
pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0));
|
||||||
printf("IBD-DIAG: inv last block already known, pushing getblocks from %d\n",
|
printf("SYNC-DIAG: inv walk-forward from %d (best=%d, walk=%d)\n",
|
||||||
mapBlockIndex[inv.hash]->nHeight);
|
nInvH, nBestHeight, nHighestInvWalk);
|
||||||
}
|
}
|
||||||
|
|
||||||
Inventory(inv.hash);
|
Inventory(inv.hash);
|
||||||
}
|
}
|
||||||
if (nBlockInv > 0)
|
if (nBlockInv > 0) {
|
||||||
printf("IBD-DIAG: inv result: %d new blocks requested, %d already have\n", nNew, nAlready);
|
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.
|
// Stall detection: if we're still catching up and no new blocks for
|
||||||
// Tighter than the old 10s to rotate away from slow peers faster.
|
// 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 int64_t nLastBlockReceived = 0;
|
||||||
static int nLastHeight = 0;
|
static int nLastHeight = 0;
|
||||||
static int64_t nLastStallLog = 0;
|
static int64_t nLastStallLog = 0;
|
||||||
|
int nStallTimeout = IsInitialBlockDownload() ? 5 : 15;
|
||||||
if (nBestHeight > nLastHeight) {
|
if (nBestHeight > nLastHeight) {
|
||||||
nLastHeight = nBestHeight;
|
nLastHeight = nBestHeight;
|
||||||
nLastBlockReceived = GetTime();
|
nLastBlockReceived = GetTime();
|
||||||
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) {
|
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > nStallTimeout) {
|
||||||
if (GetTime() - nLastStallLog >= 30) { // log every 30s max
|
if (GetTime() - nLastStallLog >= 15) { // log every 15s max
|
||||||
printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n",
|
printf("SYNC-DIAG: STALL at height %d/%d for %ds (IBD=%d walk=%d), peer=%s askfor_queue=%d\n",
|
||||||
nBestHeight, (int)(GetTime() - nLastBlockReceived),
|
nBestHeight, GetNumBlocksOfPeers(),
|
||||||
|
(int)(GetTime() - nLastBlockReceived),
|
||||||
|
IsInitialBlockDownload(), nHighestInvWalk,
|
||||||
pto->addr.ToString().c_str(),
|
pto->addr.ToString().c_str(),
|
||||||
(int)pto->mapAskFor.size(), (int)pto->nSendSize);
|
(int)pto->mapAskFor.size());
|
||||||
nLastStallLog = GetTime();
|
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->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();
|
nLastBlockReceived = GetTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-1
@@ -19,6 +19,7 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QTextDocument>
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::vector<unsigned char>);
|
Q_DECLARE_METATYPE(std::vector<unsigned char>);
|
||||||
|
|
||||||
@@ -27,6 +28,24 @@ QList<QString> ambiguous; /**< Specifies Ambiguous addresses */
|
|||||||
const QString MessageModel::Sent = "Sent";
|
const QString MessageModel::Sent = "Sent";
|
||||||
const QString MessageModel::Received = "Received";
|
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
|
struct MessageTableEntryLessThan
|
||||||
{
|
{
|
||||||
bool operator()(const MessageTableEntry &a, const MessageTableEntry &b) const {return a.received_datetime < b.received_datetime;};
|
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 FilterAddressRole: return (rec->type == MessageTableEntry::Sent ? rec->to_address + rec->from_address : rec->from_address + rec->to_address);
|
||||||
case LabelRole: return rec->label;
|
case LabelRole: return rec->label;
|
||||||
case MessageRole: return rec->message;
|
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() + "<br>" + (rec->label.isEmpty() ? rec->from_address : rec->label) + "<br>" + rec->message;
|
case HTMLRole: return rec->received_datetime.toString() + "<br>" + (rec->label.isEmpty() ? rec->from_address : rec->label) + "<br>" + rec->message;
|
||||||
case Ambiguous:
|
case Ambiguous:
|
||||||
int it;
|
int it;
|
||||||
|
|||||||
@@ -52,11 +52,13 @@ class TransactionTablePriv
|
|||||||
public:
|
public:
|
||||||
TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
|
TransactionTablePriv(CWallet *wallet, TransactionTableModel *parent):
|
||||||
wallet(wallet),
|
wallet(wallet),
|
||||||
parent(parent)
|
parent(parent),
|
||||||
|
fInitialLoadDone(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
CWallet *wallet;
|
CWallet *wallet;
|
||||||
TransactionTableModel *parent;
|
TransactionTableModel *parent;
|
||||||
|
bool fInitialLoadDone;
|
||||||
|
|
||||||
/* Local cache of wallet.
|
/* Local cache of wallet.
|
||||||
* As it is in the same order as the CWallet, by definition
|
* As it is in the same order as the CWallet, by definition
|
||||||
@@ -68,22 +70,19 @@ public:
|
|||||||
*/
|
*/
|
||||||
void refreshWallet()
|
void refreshWallet()
|
||||||
{
|
{
|
||||||
OutputDebugStringF("refreshWallet\n");
|
OutputDebugStringF("refreshWallet: fInitialLoadDone=%d mapWallet.size=%u\n",
|
||||||
|
(int)fInitialLoadDone, (unsigned)wallet->mapWallet.size());
|
||||||
cachedWallet.clear();
|
cachedWallet.clear();
|
||||||
{
|
{
|
||||||
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
LOCK(wallet->cs_wallet);
|
||||||
if(!lockWallet)
|
|
||||||
{
|
|
||||||
// Lock busy (block processing), retry in 500ms
|
|
||||||
QTimer::singleShot(500, parent, SLOT(refreshWallet()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
|
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
|
||||||
{
|
{
|
||||||
if(TransactionRecord::showTransaction(it->second))
|
if(TransactionRecord::showTransaction(it->second))
|
||||||
cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, 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
|
/* 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);
|
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
||||||
if (!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;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Find transaction in wallet
|
// Find transaction in wallet
|
||||||
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
|
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
|
||||||
@@ -243,7 +247,12 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
|
|||||||
cachedNumBlocks(0)
|
cachedNumBlocks(0)
|
||||||
{
|
{
|
||||||
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
|
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);
|
QTimer *timer = new QTimer(this);
|
||||||
connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations()));
|
connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations()));
|
||||||
@@ -267,8 +276,10 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status)
|
|||||||
|
|
||||||
void TransactionTableModel::refreshWallet()
|
void TransactionTableModel::refreshWallet()
|
||||||
{
|
{
|
||||||
|
beginResetModel();
|
||||||
priv->refreshWallet();
|
priv->refreshWallet();
|
||||||
reset();
|
endResetModel();
|
||||||
|
OutputDebugStringF("TransactionTableModel::refreshWallet: rowCount=%d\n", priv->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionTableModel::updateConfirmations()
|
void TransactionTableModel::updateConfirmations()
|
||||||
|
|||||||
@@ -78,20 +78,22 @@ void WalletModel::pollBalanceChanged()
|
|||||||
{
|
{
|
||||||
if(nBestHeight != cachedNumBlocks)
|
if(nBestHeight != cachedNumBlocks)
|
||||||
{
|
{
|
||||||
// Balance and number of transactions might have changed
|
// Balance and number of transactions might have changed.
|
||||||
cachedNumBlocks = nBestHeight;
|
// Only update cachedNumBlocks AFTER a successful balance check,
|
||||||
checkBalanceChanged();
|
// 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.
|
// Get all balances in a single lock acquisition + single pass.
|
||||||
// Uses TRY_LOCK internally - if cs_wallet is busy (block processing),
|
// Uses TRY_LOCK internally - if cs_wallet is busy (block processing),
|
||||||
// skip this cycle. The timer will retry in 2.5 seconds.
|
// skip this cycle. The timer will retry in 2.5 seconds.
|
||||||
int64_t newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0;
|
int64_t newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0;
|
||||||
if (!wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance))
|
if (!wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
|
if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
|
||||||
{
|
{
|
||||||
@@ -101,6 +103,7 @@ void WalletModel::checkBalanceChanged()
|
|||||||
cachedImmatureBalance = newImmatureBalance;
|
cachedImmatureBalance = newImmatureBalance;
|
||||||
emit balanceChanged(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance);
|
emit balanceChanged(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletModel::updateTransaction(const QString &hash, int status)
|
void WalletModel::updateTransaction(const QString &hash, int status)
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ private:
|
|||||||
|
|
||||||
void subscribeToCoreSignals();
|
void subscribeToCoreSignals();
|
||||||
void unsubscribeFromCoreSignals();
|
void unsubscribeFromCoreSignals();
|
||||||
void checkBalanceChanged();
|
bool checkBalanceChanged();
|
||||||
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|||||||
@@ -787,10 +787,16 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
|
|||||||
|
|
||||||
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
|
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
|
||||||
|
|
||||||
// TODO: Actually handle errors
|
|
||||||
if (error)
|
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;
|
delete conn;
|
||||||
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restrict callers by IP. It is important to
|
// Restrict callers by IP. It is important to
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ static const int MEMPOOL_GD_VERSION = 60002;
|
|||||||
|
|
||||||
#define DISPLAY_VERSION_MAJOR 5
|
#define DISPLAY_VERSION_MAJOR 5
|
||||||
#define DISPLAY_VERSION_MINOR 3
|
#define DISPLAY_VERSION_MINOR 3
|
||||||
#define DISPLAY_VERSION_REVISION 6
|
#define DISPLAY_VERSION_REVISION 7
|
||||||
#define DISPLAY_VERSION_BUILD 0
|
#define DISPLAY_VERSION_BUILD 0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user