Harden network security, fix moneysupply tracking, version system overhaul (v5.8.0)

- Fix moneysupply calculation in FastImportBlockFile and ConnectBlock assumevalid path
- Route bootstrap downloads through Tor SOCKS proxy (no more clearnet leaks)
- Remove hardcoded clearnet fallback IP from bootstrap
- Fix snprintf missing argument in walletmodel.cpp narration key (UB/crash)
- Fix potential null deref from db_strerror() in rpcwallet.cpp
- Filter non-.onion addresses from HTTPS seed list parser
- Add periodic re-seeding when node has 0 outbound peers
- Make clientversion.h single source of truth for version display string
- Remove redundant DISPLAY_VERSION macros from version.h
- Update README: max supply 2,222,222, CMake build instructions, Tor-only config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 19:26:12 -07:00
parent b0e9ca334f
commit 7d62e34868
14 changed files with 323 additions and 149 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ if(POLICY CMP0167)
endif()
project(Triangles
VERSION 5.7.9.0
VERSION 5.8.0.0
DESCRIPTION "Cryptographic Triangles Wallet"
LANGUAGES C CXX
)
+46 -28
View File
@@ -1,4 +1,4 @@
# Cryptographic Triangles (TRI) - v5.1.5
# Cryptographic Triangles (TRI)
Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus, Tor v3 onion routing, and built-in encrypted messaging. Originally launched in July 2014, the chain was revived in March 2026 after being frozen since December 2022.
@@ -16,7 +16,7 @@ Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus
|----------|-------|
| Algorithm | Hash9 (PoW blocks 0-9000), PoS from block 9001 |
| Block Time | ~120 seconds |
| Max Supply | 222,222 TRI |
| Max Supply | 2,222,222 TRI |
| PoS Reward | 33% annual, coin-age based |
| P2P Port | 24112 |
| RPC Port | 19112 |
@@ -24,11 +24,7 @@ Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus
## Network Status
The Triangles network is live with seed nodes operating on both clearnet and Tor:
**Clearnet Seeds:**
- `194.233.88.206:24112`
- `74.208.167.19:24112`
The Triangles network operates exclusively over Tor for privacy:
**Tor v3 Seeds:**
- `gxvrhv3qitnc6kobrhsrse46bmcfitnybapor3or3oczzuxn6hfzxyid.onion:24112`
@@ -40,32 +36,46 @@ The Triangles network is live with seed nodes operating on both clearnet and Tor
## Building from Source
Triangles uses CMake. All platforms follow the same build pattern.
### Dependencies
| Dependency | Minimum Version |
|------------|----------------|
| CMake | 3.16+ |
| C++ compiler | C++17 support |
| OpenSSL | 3.x |
| Boost | 1.90+ |
| Berkeley DB | 5.3 (with C++ bindings) |
| libevent | 2.x |
| LevelDB | bundled |
### Linux (Ubuntu 24.04 / Debian 12+)
Install dependencies:
```bash
sudo apt-get install -y build-essential libboost-all-dev libssl-dev \
libdb5.3++-dev libevent-dev zlib1g-dev libminiupnpc-dev
sudo apt-get install -y build-essential cmake ninja-build \
libboost-all-dev libssl-dev libdb5.3++-dev libevent-dev \
zlib1g-dev libminiupnpc-dev
```
Build the daemon:
For the Qt wallet, also install:
```bash
cd src/leveldb && make libleveldb.a libmemenv.a && cd ..
make -j$(nproc) -f makefile.unix USE_UPNP=0
strip trianglesd
sudo apt-get install -y qtbase5-dev qt5-qmake libqrencode-dev
```
Run the unit test suite:
Build:
```bash
make -C src -f makefile.unix test
cmake -B build -G Ninja -DBUILD_QT=ON
cmake --build build
```
### Linux (AlmaLinux 9 / RHEL 9)
Install dependencies:
```bash
sudo dnf install -y gcc-c++ make boost-devel openssl-devel libevent-devel \
zlib-devel miniupnpc-devel
sudo dnf install -y gcc-c++ cmake ninja-build boost-devel openssl-devel \
libevent-devel zlib-devel miniupnpc-devel
```
BDB 5.3 C++ bindings must be built from source on RHEL-based systems (the `libdb-devel` package does not include C++ headers). Download BDB 5.3.28 from Oracle and build with `--enable-cxx`.
@@ -76,17 +86,27 @@ Then build as above.
Open an MSYS2 MinGW64 shell and install:
```bash
pacman -S mingw-w64-x86_64-boost mingw-w64-x86_64-openssl \
pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja \
mingw-w64-x86_64-boost mingw-w64-x86_64-openssl \
mingw-w64-x86_64-db mingw-w64-x86_64-miniupnpc \
mingw-w64-x86_64-qt5-base mingw-w64-x86_64-qrencode
mingw-w64-x86_64-qt5-base mingw-w64-x86_64-qrencode \
mingw-w64-x86_64-libevent
```
Build the Qt wallet:
Build:
```bash
qmake triangles-qt.pro
make -j$(nproc)
cmake -B build -G Ninja -DBUILD_QT=ON
cmake --build build
```
### Build Options
| Option | Default | Description |
|--------|---------|-------------|
| `BUILD_QT` | ON | Build the Qt GUI wallet |
| `BUILD_DAEMON` | ON | Build the headless daemon |
| `BUILD_TESTS` | OFF | Build unit tests |
## Running
### First Run
@@ -103,15 +123,13 @@ txindex=1
listen=1
server=1
daemon=1
addnode=194.233.88.206
addnode=74.208.167.19
externalip=<your-public-ip>
proxy=127.0.0.1:9050
EOF
trianglesd
```
The node will connect to seed nodes and sync the blockchain automatically.
The node will connect to seed nodes over Tor and sync the blockchain automatically.
### Existing Wallet Holders
@@ -155,7 +173,7 @@ Messages are encrypted end-to-end using AES and distributed through the peer net
### Tor Support
To connect through Tor, install the Tor daemon and add to your config:
Triangles is designed as a Tor-only network. Install the Tor daemon and configure your proxy:
```
# triangles.conf
proxy=127.0.0.1:9050
@@ -199,7 +217,7 @@ Then set `externalip=<your-onion-address>` in `triangles.conf`.
- **Block 9001+** - Proof-of-Stake only
- **Block 17,651** - V5 hard fork (removed Tor v2, disabled checkpoint master key)
- **December 8, 2022** - Chain frozen (all nodes offline)
- **March 11, 2026** - Chain revived with v5.0.0.0, staking resumed
- **March 11, 2026** - Chain revived, staking resumed
## Project Structure
+51 -16
View File
@@ -1,6 +1,7 @@
# cmake/GenerateBuildInfoScript.cmake
# Called at build time by the custom target in GenerateBuildInfo.cmake.
# Replicates the logic of share/genbuild.sh.
# Reads the version from clientversion.h (single source of truth) and
# appends git commit info for non-release builds.
# Read existing build.h first line if it exists
set(OLD_LINE "")
@@ -11,31 +12,69 @@ if(EXISTS "${OUTPUT_FILE}")
endif()
endif()
# Try exact tag match first (release builds)
# ── Read version from clientversion.h ──
file(STRINGS "${SOURCE_DIR}/src/clientversion.h" _ver_lines)
foreach(_line ${_ver_lines})
if(_line MATCHES "^#define CLIENT_VERSION_MAJOR +([0-9]+)")
set(VER_MAJOR "${CMAKE_MATCH_1}")
elseif(_line MATCHES "^#define CLIENT_VERSION_MINOR +([0-9]+)")
set(VER_MINOR "${CMAKE_MATCH_1}")
elseif(_line MATCHES "^#define CLIENT_VERSION_REVISION +([0-9]+)")
set(VER_REVISION "${CMAKE_MATCH_1}")
elseif(_line MATCHES "^#define CLIENT_VERSION_BUILD +([0-9]+)")
set(VER_BUILD "${CMAKE_MATCH_1}")
endif()
endforeach()
set(BASE_VERSION "v${VER_MAJOR}.${VER_MINOR}.${VER_REVISION}.${VER_BUILD}")
# ── Get git commit info (suffix only, not the version number) ──
set(GIT_SUFFIX "")
# Get short commit hash
execute_process(
COMMAND git describe --tags --exact-match
COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESC
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE _result
)
# Fall back to tag + commit distance
if(NOT _result EQUAL 0)
if(_result EQUAL 0 AND GIT_HASH)
# Check if working directory is dirty
execute_process(
COMMAND git describe --tags --dirty
COMMAND git diff-index --quiet HEAD --
WORKING_DIRECTORY "${SOURCE_DIR}"
OUTPUT_VARIABLE GIT_DESC
RESULT_VARIABLE _dirty
)
# Check if HEAD is exactly on a tag matching our version
execute_process(
COMMAND git describe --tags --exact-match HEAD
WORKING_DIRECTORY "${SOURCE_DIR}"
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE _result
RESULT_VARIABLE _tag_result
)
if(NOT _result EQUAL 0)
set(GIT_DESC "")
set(_on_release_tag FALSE)
if(_tag_result EQUAL 0 AND GIT_TAG STREQUAL "${BASE_VERSION}")
set(_on_release_tag TRUE)
endif()
# Only add git suffix for non-release builds (not on exact version tag, or dirty)
if(NOT _on_release_tag OR NOT _dirty EQUAL 0)
set(GIT_SUFFIX "-g${GIT_HASH}")
if(NOT _dirty EQUAL 0)
set(GIT_SUFFIX "${GIT_SUFFIX}-dirty")
endif()
endif()
endif()
set(FULL_VERSION "${BASE_VERSION}${GIT_SUFFIX}")
# Get commit timestamp
execute_process(
COMMAND git log -n 1 --format=%ci
@@ -46,11 +85,7 @@ execute_process(
)
# Build new content
if(GIT_DESC)
set(NEW_LINE "#define BUILD_DESC \"${GIT_DESC}\"")
else()
set(NEW_LINE "// No build information available")
endif()
set(NEW_LINE "#define BUILD_DESC \"${FULL_VERSION}\"")
# Only write if changed
if(NOT "${OLD_LINE}" STREQUAL "${NEW_LINE}")
+86 -61
View File
@@ -3,7 +3,6 @@
#include "bootstrap.h"
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/algorithm/string.hpp>
@@ -12,6 +11,8 @@
#include "version.h"
#include "uint256.h"
#include "netbase.h"
#include "net.h"
#include <fstream>
#include <sstream>
@@ -19,12 +20,18 @@
#include <cstring>
#include <cstdlib>
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <unistd.h>
#endif
// 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;
namespace Bootstrap {
@@ -33,26 +40,48 @@ bool NeedsBootstrap(const fs::path& dataDir)
return !fs::exists(dataDir / "blk0001.dat");
}
// Send all bytes on a raw socket
static bool SendAll(SOCKET sock, const char* data, size_t len)
{
while (len > 0) {
int n = send(sock, data, (int)std::min(len, (size_t)65536), MSG_NOSIGNAL);
if (n <= 0) return false;
data += n;
len -= n;
}
return true;
}
// Read until `delim` found in received data. Returns data including delimiter.
static bool RecvUntil(SOCKET sock, std::string& out, const std::string& delim)
{
out.clear();
char c;
while (true) {
int n = recv(sock, &c, 1, 0);
if (n <= 0) return false;
out += c;
if (out.size() >= delim.size() &&
out.compare(out.size() - delim.size(), delim.size(), delim) == 0)
return true;
if (out.size() > 64 * 1024) return false; // header too large
}
}
bool DownloadFile(const std::string& host, const std::string& urlPath,
const fs::path& destPath,
ProgressCallback progressFn,
std::string& strError)
{
try {
boost::asio::io_context io_context;
tcp::resolver resolver(io_context);
boost::system::error_code resolve_ec;
tcp::resolver::results_type endpoints =
resolver.resolve(host, std::to_string(PORT), resolve_ec);
if (resolve_ec) {
strError = "Cannot resolve host: " + host;
// Connect through Tor SOCKS proxy (ConnectSocketByName respects SetProxy)
SOCKET hSocket = INVALID_SOCKET;
CService addr;
if (!ConnectSocketByName(addr, hSocket, host.c_str(), PORT, 30)) {
strError = "Cannot connect to " + host + " (check Tor proxy)";
return false;
}
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoints);
// Send HTTP GET request
std::string request =
"GET " + urlPath + " HTTP/1.1\r\n"
@@ -60,85 +89,81 @@ bool DownloadFile(const std::string& host, const std::string& urlPath,
"Connection: close\r\n"
"User-Agent: Triangles\r\n"
"\r\n";
boost::asio::write(socket, boost::asio::buffer(request));
if (!SendAll(hSocket, request.data(), request.size())) {
closesocket(hSocket);
strError = "Failed to send request to " + host;
return false;
}
// Read response headers
boost::asio::streambuf response_buf;
boost::asio::read_until(socket, response_buf, "\r\n\r\n");
std::string headerData;
if (!RecvUntil(hSocket, headerData, "\r\n\r\n")) {
closesocket(hSocket);
strError = "Failed to read HTTP headers from " + host;
return false;
}
std::istream response_stream(&response_buf);
// Parse status line
std::string http_version;
// Parse status code from "HTTP/1.x NNN ..."
unsigned int status_code = 0;
response_stream >> http_version >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
size_t sp = headerData.find(' ');
if (sp != std::string::npos)
status_code = atoi(headerData.c_str() + sp + 1);
if (status_code != 200) {
closesocket(hSocket);
strError = "HTTP error " + std::to_string(status_code) + " for " + urlPath;
return false;
}
// Parse headers for Content-Length
// Parse Content-Length
int64_t content_length = 0;
std::string header_line;
while (std::getline(response_stream, header_line) && header_line != "\r") {
std::string lower_header = header_line;
std::transform(lower_header.begin(), lower_header.end(),
lower_header.begin(), ::tolower);
if (lower_header.find("content-length:") == 0) {
content_length = std::stoll(header_line.substr(header_line.find(':') + 1));
}
std::string lowerHeaders = headerData;
std::transform(lowerHeaders.begin(), lowerHeaders.end(),
lowerHeaders.begin(), ::tolower);
size_t clPos = lowerHeaders.find("content-length:");
if (clPos != std::string::npos) {
size_t valStart = clPos + 15;
size_t lineEnd = lowerHeaders.find("\r\n", valStart);
if (lineEnd != std::string::npos)
content_length = std::stoll(headerData.substr(valStart, lineEnd - valStart));
}
// Open output file
FILE* file = fopen(destPath.string().c_str(), "wb");
if (!file) {
closesocket(hSocket);
strError = "Cannot create file: " + destPath.string();
return false;
}
// Read body in chunks
int64_t bytes_written = 0;
// Write any data remaining in the header buffer (body starts here)
if (response_buf.size() > 0) {
std::istreambuf_iterator<char> eos;
std::string remaining(std::istreambuf_iterator<char>(response_stream), eos);
if (!remaining.empty()) {
fwrite(remaining.data(), 1, remaining.size(), file);
bytes_written += remaining.size();
}
}
// Read remaining body in chunks
std::vector<char> chunk(65536); // 64 KB
boost::system::error_code ec;
int64_t last_progress = 0;
char chunk[65536];
while (true) {
size_t n = socket.read_some(boost::asio::buffer(chunk), ec);
if (n > 0) {
fwrite(chunk.data(), 1, n, file);
bytes_written += n;
// Report progress every 256 KB
if (progressFn && (bytes_written - last_progress >= 262144)) {
last_progress = bytes_written;
progressFn(bytes_written, content_length);
}
}
if (ec == boost::asio::error::eof)
break;
if (ec) {
int n = recv(hSocket, chunk, sizeof(chunk), 0);
if (n < 0) {
fclose(file);
closesocket(hSocket);
fs::remove(destPath);
strError = "Network error: " + ec.message();
strError = "Network error during download";
return false;
}
if (n == 0) break; // EOF
fwrite(chunk, 1, n, file);
bytes_written += n;
if (progressFn && (bytes_written - last_progress >= 262144)) {
last_progress = bytes_written;
progressFn(bytes_written, content_length);
}
}
fclose(file);
closesocket(hSocket);
// Verify download size if Content-Length was provided
if (content_length > 0 && bytes_written != content_length) {
-1
View File
@@ -13,7 +13,6 @@ namespace Bootstrap {
// Bootstrap server configuration
static const char* DEFAULT_HOST = "bootstrap.cryptographic-triangles.org";
static const char* FALLBACK_HOST = "194.233.88.206";
static const char* BASE_PATH = "/";
static const int PORT = 80;
+2 -2
View File
@@ -7,8 +7,8 @@
// 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 7
#define CLIENT_VERSION_REVISION 9
#define CLIENT_VERSION_MINOR 8
#define CLIENT_VERSION_REVISION 0
#define CLIENT_VERSION_BUILD 0
// Converts the parameter X to a string after macro replacement on X has been performed.
-5
View File
@@ -921,11 +921,6 @@ bool AppInit2()
};
bool success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
if (!success) {
host = Bootstrap::FALLBACK_HOST;
printf("\nBootstrap: primary host failed, trying fallback %s...\n", host.c_str());
success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
}
if (!success) {
printf("\nBootstrap: failed: %s\n", strError.c_str());
+60 -19
View File
@@ -2034,6 +2034,31 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
// Track pending UTXOs so later txs in the same block can find inputs.
if (fAssumeValid)
{
// Track money supply from input/output values
int64_t nTxValueOut = tx.GetValueOut();
nValueOut += nTxValueOut;
if (!tx.IsCoinBase())
{
int64_t nTxValueIn = 0;
for (const CTxIn& txin : tx.vin)
{
// Check in-block pending UTXOs first, then UTXO database
MapPrevTx::iterator it = mapPendingUtxos.find(txin.prevout);
if (it != mapPendingUtxos.end())
nTxValueIn += it->second.nValue;
else
{
CUtxoEntry utxo;
if (txdb.ReadUtxo(txin.prevout.hash, txin.prevout.n, utxo))
nTxValueIn += utxo.nValue;
}
}
nValueIn += nTxValueIn;
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
}
// Add outputs to pending UTXOs
for (unsigned int k = 0; k < tx.vout.size(); k++)
{
@@ -2774,21 +2799,22 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
//
// Chain selection rules:
// 1. Strictly greater trust always wins (normal case).
// 2. Equal trust with shallow fork (parent in main chain): use
// deterministic hash tiebreaker — lower tip hash wins. This
// resolves single-block PoS races where two stakers find valid
// blocks at the same height with identical difficulty.
// 3. Equal trust with deep fork (parent NOT in main chain): do NOT
// reorg. Without this rule, Tor-latency-induced multi-block
// forks cause nodes to oscillate between competing chains of
// similar trust, preventing convergence.
// 2. Equal trust: deterministic hash tiebreaker — lower tip hash wins.
// This ensures all nodes converge on the same chain even when two
// forks have identical cumulative difficulty (common in PoS).
// Rate-limited to one equal-trust reorg per 10 minutes to prevent
// oscillation from Tor-latency-induced competing announcements.
bool fNewBest = false;
static int64_t nLastEqualTrustReorg = 0;
if (pindexNew->nChainTrust > nBestChainTrust)
fNewBest = true;
else if (pindexNew->nChainTrust == nBestChainTrust && pindexBest &&
pindexNew->pprev && pindexNew->pprev->IsInMainChain() &&
pindexNew->GetBlockHash() < pindexBest->GetBlockHash())
pindexNew->GetBlockHash() < pindexBest->GetBlockHash() &&
GetTime() - nLastEqualTrustReorg > 10 * 60)
{
fNewBest = true;
nLastEqualTrustReorg = GetTime();
}
if (fNewBest)
{
@@ -3735,10 +3761,6 @@ bool FastImportBlockFile()
}
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
// Money supply tracking
pindexNew->nMint = 0;
pindexNew->nMoneySupply = (pindexNew->pprev ? pindexNew->pprev->nMoneySupply : 0);
// PoS stake seen set
if (pindexNew->IsProofOfStake())
setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
@@ -3751,10 +3773,10 @@ bool FastImportBlockFile()
if (pindexNew->pprev)
pindexNew->pprev->pnext = pindexNew;
// Write block index to batch
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
// Build tx index + UTXO entries
// Build tx index + UTXO entries, tracking money supply
int64_t nBlockValueIn = 0;
int64_t nBlockValueOut = 0;
int64_t nFees = 0;
unsigned int nTxPos = nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION)
- (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(block.vtx.size());
for (unsigned int i = 0; i < block.vtx.size(); i++)
@@ -3765,11 +3787,23 @@ bool FastImportBlockFile()
txdb.UpdateTxIndex(hashTx, CTxIndex(posThisTx, tx.vout.size()));
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
// UTXO entries
int64_t nTxValueOut = tx.GetValueOut();
nBlockValueOut += nTxValueOut;
// UTXO entries — read input values before erasing for money supply
if (!tx.IsCoinBase())
{
int64_t nTxValueIn = 0;
for (const CTxIn& txin : tx.vin)
{
CUtxoEntry utxo;
if (txdb.ReadUtxo(txin.prevout.hash, txin.prevout.n, utxo))
nTxValueIn += utxo.nValue;
txdb.EraseUtxo(txin.prevout.hash, txin.prevout.n);
}
nBlockValueIn += nTxValueIn;
if (!tx.IsCoinStake())
nFees += nTxValueIn - nTxValueOut;
}
for (unsigned int k = 0; k < tx.vout.size(); k++)
{
@@ -3787,6 +3821,13 @@ bool FastImportBlockFile()
}
}
// Money supply tracking — matches ConnectBlock formula
pindexNew->nMint = nBlockValueOut - nBlockValueIn + nFees;
pindexNew->nMoneySupply = (pindexNew->pprev ? pindexNew->pprev->nMoneySupply : 0) + nBlockValueOut - nBlockValueIn;
// Write block index to batch
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
// Update best chain
if (pindexNew->nChainTrust > nBestChainTrust)
{
+28 -7
View File
@@ -1403,7 +1403,32 @@ void ThreadOnionSeed(void* parg)
// This is the primary discovery mechanism — seeds.cryptographic-triangles.org
ThreadHTTPSeedFetch2(NULL);
printf("ThreadOnionSeed: seeding complete\n");
printf("ThreadOnionSeed: initial seeding complete\n");
// Periodic re-seeding: if the node becomes isolated (0 outbound peers),
// re-fetch the seed list. Check every 10 minutes, re-seed at most once
// per 30 minutes to avoid hammering the seed server.
int64_t nLastReseed = GetTime();
while (!fShutdown) {
for (int i = 0; i < 600 && !fShutdown; i++) // sleep 10 minutes
MilliSleep(1000);
if (fShutdown) break;
int nOutbound = 0;
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
if (!pnode->fInbound)
nOutbound++;
}
if (nOutbound == 0 && GetTime() - nLastReseed > 30 * 60) {
printf("ThreadOnionSeed: no outbound peers, re-seeding...\n");
ThreadHTTPSeedFetch2(NULL);
nLastReseed = GetTime();
}
}
}
@@ -1622,12 +1647,8 @@ void ThreadHTTPSeedFetch2(void* parg)
port = atoi(addrStr.substr(onionPos + 7).c_str());
addrStr = addrStr.substr(0, onionPos + 6); // keep ".onion"
} else if (addrStr.find(".onion") == std::string::npos) {
// Clearnet address - find last colon for port
size_t colonPos = addrStr.rfind(':');
if (colonPos != std::string::npos) {
port = atoi(addrStr.substr(colonPos + 1).c_str());
addrStr = addrStr.substr(0, colonPos);
}
// Tor-native: skip non-.onion addresses
continue;
}
if (port <= 0 || port > 65535)
+1 -1
View File
@@ -389,7 +389,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
for (it = mapStealthNarr.begin(); it != mapStealthNarr.end(); ++it)
{
char key[64];
if (snprintf(key, sizeof(key), "n_%u") < 1)
if (snprintf(key, sizeof(key), "n_%u", it->first) < 1)
{
printf("CreateStealthTransaction(): Error creating narration key.");
continue;
+2 -1
View File
@@ -1944,7 +1944,8 @@ Value clearwallettransactions(const Array& params, bool fHelp)
if (datKey.get_data() == NULL || datValue.get_data() == NULL
|| ret != 0)
{
snprintf(cbuf, sizeof(cbuf), "wallet DB error %d, %s", ret, db_strerror(ret));
const char* dbErr = db_strerror(ret);
snprintf(cbuf, sizeof(cbuf), "wallet DB error %d, %s", ret, dbErr ? dbErr : "unknown");
throw runtime_error(cbuf);
};
+44
View File
@@ -613,6 +613,50 @@ bool CTxDB::LoadBlockIndex()
hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(),
DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str());
// Re-evaluate best chain: scan for competing tips with equal or greater trust.
// This fixes nodes stuck on the wrong fork after consensus rule changes.
{
CBlockIndex* pindexBetter = NULL;
for (const auto& item : mapBlockIndex)
{
CBlockIndex* pindex = item.second;
if (pindex == pindexBest)
continue;
if (pindex->nChainTrust > nBestChainTrust)
{
pindexBetter = pindex;
break;
}
if (pindex->nChainTrust == nBestChainTrust &&
pindex->GetBlockHash() < pindexBest->GetBlockHash())
{
if (!pindexBetter || pindex->GetBlockHash() < pindexBetter->GetBlockHash())
pindexBetter = pindex;
}
}
if (pindexBetter)
{
printf("LoadBlockIndex(): found better chain tip %s at height %d (trust %s vs %s)\n",
pindexBetter->GetBlockHash().ToString().substr(0,20).c_str(),
pindexBetter->nHeight,
CBigNum(pindexBetter->nChainTrust).ToString().c_str(),
CBigNum(nBestChainTrust).ToString().c_str());
CBlock block;
if (block.ReadFromDisk(pindexBetter))
{
CTxDB txdb2;
if (block.SetBestChain(txdb2, pindexBetter))
{
hashBestChain = pindexBetter->GetBlockHash();
pindexBest = pindexBetter;
nBestHeight = pindexBetter->nHeight;
nBestChainTrust = pindexBetter->nChainTrust;
printf("LoadBlockIndex(): switched to better chain tip\n");
}
}
}
}
// triangles: load hashSyncCheckpoint (best-effort, non-fatal)
if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
printf("LoadBlockIndex(): no sync checkpoint in DB, using default\n");
+2 -2
View File
@@ -48,9 +48,9 @@ const std::string CLIENT_NAME("Cryptographic Triangles");
#ifndef BUILD_DESC
# ifdef GIT_COMMIT_ID
# define BUILD_DESC BUILD_DESC_FROM_COMMIT(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD, GIT_COMMIT_ID)
# define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID)
# else
# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(DISPLAY_VERSION_MAJOR, DISPLAY_VERSION_MINOR, DISPLAY_VERSION_REVISION, DISPLAY_VERSION_BUILD)
# define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD)
# endif
#endif
-5
View File
@@ -51,9 +51,4 @@ static const int BIP0031_VERSION = 60000;
// "mempool" command, enhanced "getdata" behavior starts with this version:
static const int MEMPOOL_GD_VERSION = 60002;
#define DISPLAY_VERSION_MAJOR 5
#define DISPLAY_VERSION_MINOR 7
#define DISPLAY_VERSION_REVISION 6
#define DISPLAY_VERSION_BUILD 0
#endif