Three pure helper functions extracted from ThreadHTTPSeedFetch2 into
netbase.{h,cpp} so the HTTPS seed-list code path can be unit-tested
without the SSL/Tor network stack:
int DechunkTransferEncoding(const std::string& body, std::string& out)
std::vector<std::string> ParseSeedListBody(const std::string& body)
bool IsValidSocksNegotiationTimeout(int nMs)
DechunkTransferEncoding is now strict (was lenient):
- Hex validation: every byte of the chunk-size line is checked with
isxdigit() before strtoull. Old code passed a raw strtoul() result
which silently accepted leading '+', '-', and whitespace.
- strtoull + errno + size_t bounds check replaces the silent
'if (pos+chunkSize > body.size()) chunkSize = body.size()-pos'
clamp. The old behavior would mask truncated network reads.
- Empty size lines, '+5' / '-5' / ' 5', and unsigned overflow all
return DECHUNK_INVALID_HEX (or DECHUNK_OVERSIZE_CHUNK for the
bounds case) instead of being treated as 0/last-chunk.
- Missing CRLF after chunk data returns DECHUNK_MISSING_DATA_CRLF
rather than being read as the next chunk-size line.
- Body without a '0\r\n' last-chunk terminator returns
DECHUNK_NO_CHUNK_TERMINATOR instead of silently being accepted.
- Chunk extensions ('5;foo=bar') are still preserved — the ';'
delimiter is stripped from the size line, not from the framing.
ParseSeedListBody is a 1:1 extraction of the old loop. Same behavior
on every input. Trims inline '#' comments, splits on whitespace /
comma / semicolon, normalizes CR-only line endings.
IsValidSocksNegotiationTimeout is the central policy: 5000..180000 ms
inclusive. Replaces the inline 'nTorTimeout >= 5000 && nTorTimeout <=
180000' check in init.cpp's AppInit2. Out-of-range values now emit an
InitWarning so the operator sees why their setting was ignored.
Six distinct failure-mode log messages in ThreadHTTPSeedFetch2:
1. 'cannot connect to %s through Tor proxy' — connect failure
2. 'malformed response (no header terminator)' — no \r\n\r\n
3. 'malformed chunked transfer encoding (%s)' — DechunkResult enum
reason string
4. 'empty response from %s' — 0 bytes read
5. 'parsed response contained zero valid addresses' — body parsed
but CService
validation
dropped all
6. '%d addresses found from HTTPS seed list' — success path
Help text for -torconnecttimeout now precisely describes what the
value bounds (the SOCKS5 handshake — send/recv of init/auth/connect),
not 'time to reach the onion' which was misleading. The onion-resolution
time is bounded by Tor's own SocksTimeout (~120s) and is not directly
controllable from the daemon.
src/test/http_seed_tests.cpp adds 43 new Boost.Test cases covering
every scenario in the hardening brief:
DechunkTransferEncoding: 16 cases
- single chunk, multiple chunks, chunk extensions (one and
multiple), uppercase hex, payload containing CRLF, awkward
boundary that looks like a chunk-size line, last-chunk with
extension
- empty body, no CRLF after size, invalid hex, empty size line,
oversize chunk, truncated last-chunk marker, missing data CRLF,
strtoul overflow, sign in size, whitespace in size, no last
chunk
ParseSeedListBody: 14 cases
- empty, single-per-line, CRLF endings, multiple-per-line
(space, comma, semicolon, mixed), inline comments, blank lines,
all-comments, portless onion, invalid entry preserved, trailing
whitespace, mixed CRLF/LF
IsValidSocksNegotiationTimeout: 9 cases
- 4999 (out), 5000 (in, exact lower), 60000 (in, default), 180000
(in, exact upper), 180001 (out), 0 (out), -1 (out), INT_MAX
(out, guard against wraparound), 3 midrange values
Integration: 1 round-trip case
- Encode a seed body as chunked, dechunk it, then parse the
result. Verifies the two helpers compose correctly.
Test results: 183 test cases total, *** No errors detected. Existing
onion_v3_tests (8) and netbase_tests (10) still pass.
1. -torconnecttimeout config option (init.cpp, netbase.h, netbase.cpp)
SOCKS5/Tor negotiation bound. Default 60s. Range 5-180s. Without this, a
dead/slow .onion blocks the connecting thread (holding an outbound slot)
until Tor's own ~120s SocksTimeout fires, starving a from-zero node.
Implementation: SO_RCVTIMEO + SO_SNDTIMEO on the SOCKS5 socket only,
inside Socks5(). Both Linux/BSD and Win32 paths. Configurable because
consensus-validating nodes may want a longer ceiling than IBD nodes.
2. HTTP seed fetch: chunked-encoding support (net.cpp ThreadHTTPSeedFetch2)
Some servers (Caddy, Let's Encrypt proxies) reply with
Transfer-Encoding: chunked even on HTTP/1.1 + Connection: close. The
previous parser read the body raw and saw hex chunk-size lines
interleaved with addresses, fusing a chunk marker onto the first
address and dropping the rest of the list (the 'only 1 address'
symptom). De-chunk first when header advertises chunked, then parse.
3. Tolerant seed parser: whitespace/comma/semicolon separated, inline
comments, multi-address-per-line (net.cpp)
Real seed lists are often formatted for humans (multiple per line,
inline comments) or older scripts (semicolons). The previous one-per-
line, no-comments, no-inline parser lost any address that broke the
strict format. Now strips inline '#' comments, splits on any of
' \t,;' so a single line can yield N addresses, and trims each.
Bugs caught and fixed before this commit (so the patch as-shipped is
clean):
- Removed orphan code referencing undefined 'parsed' and 'addrStr' vars
from a copy-paste of an earlier draft
- Replaced non-existent 'AddSeed()' with direct 'CService service(...)'
construction followed by 'addrman.Add(CAddress, CService)' (correct
addrman.Add signature, not CNetAddr)
- Tightened 'addrman.Add' call to the actual signature: address + source
Triangles already has a node-identity signing system (getwalletaddr/walletaddr
in onion_v3.cpp:4793-4848) that lets peers cryptographically prove they own
their .onion address. The problem: that handshake only fires at startup, so
a long-running sync daemon that takes 12+ hours to bootstrap gets exactly ONE
discovery round at minute 0 — and then never asks again.
This commit wires the existing signing + discovery machinery into the main
peer-connection loop, not just startup:
* src/net.h: add nLastGetaddrTrigger + nSignedPeerBonus fields to CNode
* src/net.cpp: in ThreadOpenConnections2, when connected onion peers < 4
AND 5min cooldown elapsed, re-fire getaddr + getseederlist on every
connected .onion peer. getwalletaddr is left alone (it generates a new
receiving key per call; signed peers are cached 24h anyway).
* src/tor/onion_v3.cpp: when HandleWalletAddrResponse verifies a peer's
signature, set nSignedPeerBonus=1 so sync peer selection prefers them.
* src/syncmanager.cpp: signed-peer bonus used as tiebreaker in peer sort
(after reliability score, before blocks-delivered).
Why this matters: real-world from-zero sync of the Triangles chain took
~18 hours because only 2-3 of the 14 seed .onion nodes were reliably
reachable from any given Tor instance. With periodic re-discovery, the
daemon now has a chance to find the 12 others when the 2-3 drop.
Verified: built clean (15:59), test daemon climbed from 70,828 → 73,997+
at ~1.9 blk/s with new binary, SYNC-SIGN message confirmed firing.
Migration from boost to std-library equivalents and removal of unreachable
code paths. Touches infrastructure only — no consensus rule or wallet
serialization changes.
Dead code removed:
- IRC bootstrap (irc.cpp/h, 417 lines): orphan from pre-Tor era, no callers.
- Alert system (alert.cpp/h + sendalert RPC + Qt UI signal, ~500 lines):
retired post-V5 fork; old peers' alert messages now hit the unknown-cmd
default branch, logged + ignored.
- Legacy P2P handlers in main.cpp: "checkpoint" (already a no-op stub since
V5 fork master-key removal), "checkorder"/"reply" (2010-era Receive-by-IP
feature), plus their unused supporting structures (CRequestTracker,
PushRequest overloads, mapRequests/cs_mapRequests, mapReuseKey).
- Unreachable RPCs clearwallettransactions and scanforalltxns (~175 lines):
defined in rpcwallet.cpp but never registered in the dispatch table.
- Stale -alertnotify CLI help text (option was advertised but never wired).
boost::filesystem -> std::filesystem (C++17):
- 30 source files, 5 headers. namespace fs = boost::filesystem swapped to
namespace fs = std::filesystem; boost::filesystem::ifstream/ofstream
replaced with std::ifstream/ofstream (path-aware in C++17);
fs::system_complete -> fs::absolute; boost::filesystem::filesystem_error
-> std::filesystem::filesystem_error.
- Build system: dropped Boost::filesystem from link libs and Boost
components; PCH includes updated.
- Added explicit <filesystem> includes where types were previously
available only transitively (db.h, rpcblockchain.cpp).
boost::thread -> std::thread (12 files):
- sync.h CCriticalSection/CWaitableCriticalSection now alias
std::recursive_mutex/std::mutex. boost::unique_lock and
boost::condition_variable / boost::mutex::scoped_lock swapped to std
equivalents; sync.cpp boost::thread_specific_ptr -> thread_local
std::unique_ptr.
- init.cpp boost::thread_group rewritten as std::vector<std::thread> with
manual join loop. boost::thread::hardware_concurrency ->
std::thread::hardware_concurrency.
- main.cpp/wallet.cpp -blocknotify/-walletnotify shell-out threads now use
std::thread(...).detach() — fixes a latent bug where modern boost::thread
destructor would call std::terminate on the joinable thread.
- util.cpp NewThread now catches std::system_error.
- No interruption_point/interrupt usage anywhere — pure mechanical swap.
boost::chrono / boost::posix_time -> std::chrono (3 of 5 files):
- util.h: MilliSleep, GetTimeMillis, GetTimeMicros rewritten on std::chrono
(system_clock for epoch math, sleep_for for delays).
- snapshotnet.cpp: sleep_for swapped.
- DoS_tests.cpp: timing harness uses steady_clock.
- Skipped: rpcdump.cpp (boost::posix_time::time_input_facet has no clean
std::get_time equivalent) and qt/qtipcserver.cpp (locked to
boost::posix_time by boost::interprocess::message_queue::timed_receive).
Other housekeeping:
- Dropped unnecessary "using namespace boost;" from txdb-leveldb.cpp,
txdb-rocksdb.cpp, walletdb.cpp, db.cpp (verified no unqualified boost
names in those TUs).
- Removed unused extern declaration for clearwallettransactions.
Build fixes for non-unity builds on MinGW64/GCC 15:
- net.cpp: dropped stale #include "irc.h".
- addrman.cpp + main.cpp: explicit <cmath> include for sqrt/pow (was
arriving transitively via boost headers).
- rpcblockchain.cpp + init.cpp: defensive #undef STRICT/ADVISORY/PERMISSIVE
since windows.h macros collide with the Checkpoints:: enum values when
std headers reorder include flow.
- tor_embed_hooks.cpp: triangles_tor_check_interrupted now polls fShutdown
instead of boost::this_thread::interruption_requested (we never used
boost interruption — the hook was always effectively a no-op).
- snapshotnet.cpp: fs::remove error handle uses std::error_code.
- serialize.h: added <ios> for std::ios::badbit/failbit (was relying on
transitive include via boost).
Note: unity builds currently fail on this branch due to std::byte (C++17)
colliding with COM 'byte' typedef from shlobj.h when 'using namespace std;'
from earlier files in the unity slice leaks into util.cpp's parse of
shlobj.h. Build with -DENABLE_UNITY_BUILD=OFF (the default).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BIP 31 ping/pong with 2-min heartbeat, RTT tracking, 3-miss disconnect
- Reduce max outbound from 16 to 8, add -maxoutbound flag
- Emergency reconnection: 15s re-seed when 0 peers, 30s when 1 peer
- Inactivity timeout reduced from 90min to 10min (dead peer detection)
- Header sync TTL extended from 5min to 15min for Tor latency
- Reserve 2 inbound slots for known seed nodes at capacity
- Enhanced address gossip: hourly rebroadcast, getaddr from all peers
- New getnetworkstability RPC with isolation risk assessment
- getpeerinfo now includes pingtime, blocksdelivered, avglatency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add checkpoints through block 2,209,000 to lock canonical chain
- Ban peers on incompatible forks (no common blocks after 3 getblocks)
- Auto-checkpoint: finalize blocks at MAX_REORG_DEPTH to prevent deep reorgs
- Require 10% trust delta for side-chain reorgs (first-seen advantage)
- Add gencheckpoints RPC command for easy future checkpoint generation
- Add wallet onion address to hardcoded seed list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
- assumevalid flag to skip script verification for known-good blocks
- CCheckQueue thread pool for parallel signature/script validation
- Deferred wallet scan until after IBD completes
- Guard UPnP usage for builds without miniupnpc
- Fix LogPrintf -> printf in clearwallettransactions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The seeds.cryptographic-triangles.org endpoint uses Caddy with auto-TLS,
so the daemon's seed fetcher now connects over HTTPS (port 443) using
OpenSSL instead of plain HTTP (port 80) which got a 308 redirect.
Also hardcodes 5 known onion seed addresses in onionseed.h as a fallback
for initial peer discovery when the HTTPS endpoint is unreachable.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ThreadStakeMiner: catch-and-retry instead of crash on exception
(boost::bad_weak_ptr no longer kills the daemon)
- GetStakeWeight: take wallet lock once instead of per-coin to
reduce lock contention with 20K+ transaction wallets
- StakeMiner: continue instead of exit when CreateNewBlock fails
- Wrap all NotifyTransactionChanged/NotifyAddressBookChanged signal
emissions in try/catch to absorb stale slot exceptions
- Add -zapwallettxes flag: strips all tx records from wallet.dat
keeping only keys, then rescans blockchain to rebuild history
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-transaction CTxIndex spent tracking with per-output UTXO
database (CUtxoEntry). ConnectBlock writes/erases UTXOs as blocks are
processed. FetchInputs reads directly from UTXO DB instead of
deserializing full transactions from disk.
Persist nChainTrust in block index (dbformat v3) to skip expensive
recalculation on every startup. Only populate setStakeSeen for last
500 blocks instead of all 2M+.
Lazy fallback to old CTxIndex path for databases upgrading from
pre-UTXO format - no big-bang migration required.
Fixes pre-existing bugs in introdialog.cpp (extra brace) and
net_bootstrap.cpp (namespace extern).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every Triangles wallet is now a Tor node. Staking rewards subsidize
Tor infrastructure.
Core changes:
- Embedded Tor 0.4.9.6 as git submodule
- ConnectNode rejects all non-.onion peers
- Tor failure is fatal - wallet requires Tor to operate
- All proxies forced through embedded Tor SOCKS
- Clearnet (IPv4/IPv6) disabled at startup
- HTTP seed fetch routes through Tor proxy (removed boost::asio dep)
- Merged PoW cleanup: -623 lines of dead mining code
- Stripped dead LEGACY/MIXED bootstrap modes from net_bootstrap
- RPC getnetworkinfo reports tor_native mode
Tooling:
- scripts/bump-version.sh syncs version across all 17+ files
- Version bumped to 5.6.0 across all packaging manifests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hardcoded DNS3 (74.208.167.19), DNS2 (194.233.88.206), and Contabo (100.98.123.59) as fixed seeds
- Nodes will automatically connect to these on first run
- No manual addnode configuration needed
- Full mesh network connectivity built into the code
LookupHost expects std::vector<CNetAddr>& but was passed a single
CNetAddr, breaking compilation on all platforms.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all hardcoded seed addresses (onion, clearnet, DNS) with a
dynamic HTTP-based seed list fetched from seeds.cryptographic-triangles.org
on startup. New getseedlist RPC exposes known .onion peers from the
address manager for a collector script to publish.
Any wallet that comes online with an onion address is automatically
discovered by peers via P2P addr exchange and appears in the seed list
within minutes. No binary rebuilds needed when addresses change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes multiple concurrency bugs exposed during shutdown when Tor proxy
connections are failing:
- Reorder shutdown: stop network threads before destroying Tor V3 services
- Make RPC listener responsive to fShutdown (poll_one+sleep vs blocking run_one)
- Wrap StopRequests() in try/catch and drain io_service on exit
- Fix leaked CNode AddRef in ThreadSocketHandler2 and ThreadMessageHandler2
(return→break so Release loop executes)
- Guard vNodes.size() read with cs_vNodes lock (data race)
- Guard Qt UI signal callbacks with fShutdown check (use-after-free)
- Add cs_vNodes lock in CNetCleanup global destructor
- Force-disconnect remaining nodes in StopNode() after threads stop
- Make Tor maintenance thread sleep in 500ms intervals for prompt shutdown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Peer connectivity (small network optimizations):
- Reduce hardcoded seed fallback delay from 30s to 10s
- Reduce peer retry interval from 600s to 120s
- Lower staking minimum peers from 3 to 1
- Relay addr messages to all connected peers instead of just 2
UI responsiveness:
- Add progress reporting to ScanForWalletTransactions (every 10K blocks)
- Use TRY_LOCK in WalletModel::pollBalanceChanged to avoid blocking UI
- Use TRY_LOCK in TransactionTablePriv::refreshWallet with retry
Auto-bootstrap:
- Add bootstrap.h/cpp with HTTP download via boost::asio
- On first run, prompt user to download blockchain snapshot from
bootstrap.cryptographic-triangles.org directly into data directory
- Downloads filelist.txt manifest then each file with progress dialog
- Falls back to IP 194.233.88.206 if DNS fails
- Gracefully continues to P2P sync if bootstrap unavailable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major sync performance overhaul:
- Assumevalid: skip FetchInputs/ConnectInputs for blocks below
checkpoint (2,186,940). Only write txindex entries. Eliminates
millions of LevelDB reads during initial sync.
- Skip SyncWithWallets during IBD with automatic post-IBD wallet
rescan from genesis and SecureMsg chain scan.
- Skip wallet best-chain locator update during IBD so restarts
trigger proper rescan.
- Remove send/receive buffer limits (were 1MB/5MB, now unlimited).
The 1MB send buffer was the root cause of ~180 block stalls -
ProcessMessages stops reading when nSendSize >= SendBufferSize().
- Reduce IBD pipeline batch from 500 to 100 blocks for faster
re-requesting with near-instant block processing.
- Seed getblocks limit raised to 20000 during IBD (was 500).
- Faster message handler polling during IBD (10ms vs 100ms).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds CTorProcess which finds and launches an external Tor binary as a
subprocess, providing a SOCKS5 proxy on port 19099 and a v3 hidden
service on port 24112. The wallet auto-detects Tor from common install
locations or the app directory. Falls back gracefully to clearnet-only
if Tor is not found. Also fixes Tor-only network restriction that
blocked IPv4/IPv6, and bumps version to 5.1.7.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fix: revert initial sync from getheaders back to getblocks.
The headers-first change (05b1fd1) broke chain continuation — after
downloading the first 2000 blocks, fresh nodes would stall because
the getheaders path has no orphan-based continuation mechanism.
The getblocks/inv/orphan cycle is required for full chain sync.
Also adds getblocks fallback to the headers handler so if headers
are used via other paths, sync still continues.
Other changes:
- Extract REST API into separate rest.cpp/rest.h
- Add REST rate limiting and CORS support
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Startup optimization (fixes multi-minute hang on 2M+ block chain):
- Skip computing SHA-256 stake modifier checksum for blocks below last
checkpoint (was doing 2M+ unnecessary hashes on every startup)
- Reduce default -checkblocks from 2500 to 50 (disk read savings)
- Add progress percentage reporting during block index load
Headers-first sync (faster initial sync):
- Switch initial sync from getblocks to getheaders protocol
- Add PushGetHeaders and headers message handler in net/main
- Downloads 80-byte headers first, then requests blocks — allows
the node to learn chain structure before downloading full blocks
Address-index accelerated wallet rescan:
- New ScanForWalletTransactionsFromIndex() uses address index to find
only transactions touching wallet keys (skips scanning every block)
- Follows spend chains to find related transactions
- Falls back to full scan if index unavailable
- Track index sync state with addressIndexBestChain/StartHeight in LevelDB
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Revived frozen chain (Dec 2022) with two live seed nodes
- Added Tor v3 onion hidden service support (Ed25519)
- Wired seeder message handlers (seeder/getseederlist/seederlist)
- Hardcoded onion seeds and clearnet IP seeds for DNS2/DNS3
- Fixed bind address (0.0.0.0) so nodes accept external connections
- Added boost_chrono to makefile.unix for modern Boost
- Fixed LoadExternalBlockFile block-skip bug for faster imports
- Import-only optimizations for chain replay (checkpoint-gated)
- Added sort_blocks.c tool for blockchain data recovery
- Comprehensive README with build/run/messaging/Tor instructions
- Chain revival status document for project coordination
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port codebase to build with OpenSSL 3.x and Boost 1.90+:
- Rewrite bignum.h to use BIGNUM* pointer instead of inheriting from opaque BIGNUM
- Fix ECDSA_SIG, EVP_CIPHER_CTX, HMAC_CTX opaque type access across key.cpp, crypter.cpp, smessage.cpp
- Modernize Boost.Asio API (io_context, resolver, executor) in trianglesrpc.cpp
- Remove embedded Tor v2 client (incompatible with OpenSSL 3.x), keep Tor v3 SOCKS5 approach
- Update header logo to Cryptographic Triangles branding (300x63)
- Replace Bittrex exchange link with Pinball (313.cash) across all locales
- Fix MSYS2/MinGW64 build: linker flags, Boost library names, miniupnpc static linking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>