The HD seed action was only added to the standard Qt menu bar, which the
skinned GUI hides. Add it to menuOperationsRequested() so users can actually
reach Generate / Reveal-for-backup / Restore from the Operations menu.
Two related bugs in the getheaders handler at main.cpp:4441:
1. Locator-mismatch returns 0 headers:
GetBlockIndex() falls through to pindexGenesisBlock when no locator
hash matches the main chain. pindexGenesisBlock->pnext is null, so
the for-loop exits immediately and the peer gets an empty headers
response. This was the DNS3 headers-first sync stall (pitfall #36):
'getheaders -1 to 0000...' logged repeatedly.
Mirror the getblocks handler (line 4387): detect the mismatch, log
a warning, and serve headers from genesis so the peer can discover
the canonical chain.
2. Broken pnext chain at any height:
Even when GetBlockIndex() returns a valid (non-genesis) block, its
pnext can be null — this happens when LoadBlockIndex() didn't fully
heal pnext links (e.g., the node was bootstrapped from a snapshot,
or the chain was interrupted by a crash). On tridock every pnext
link was null despite 2.2M blocks (the June 11 'Heal pnext links'
commit addressed a similar symptom for GetKernelStakeModifier() but
doesn't reach into the getheaders handler).
Fall back to a tip-backwards walk from pindexBest when pnext is
null: O(N) but correct, and only triggers on the broken path.
(References the design in references/getheaders-pnext-fix.md from
the v5.9.10 deploy notes — that fix was never actually committed,
only prototyped and dropped during the June 4 git cleanup.)
Bump to v5.9.14 (also embeds the embedded-Tor 0700 fix from ed996c8).
Refs: SKILL.md pitfall #36
Internal refactor milestone for the C++20 modernization series.
No protocol or on-disk format change (version.h untouched).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
M1.2 (mechanical):
Convert every CTxDB& parameter and reference across main.{h,cpp},
wallet.{h,cpp}, and smessage.cpp to CTxDBBase&. Local instantiations
like `CTxDB txdb("r");` are deliberately left as concrete LevelDB —
they'll move behind a factory in M1.4 once the parity harness exists.
CTxDB IS-A CTxDBBase, so all existing call sites continue to compile:
a CTxDB instance binds to a CTxDBBase& parameter automatically.
Forward declaration `class CTxDB;` in main.h replaced with
`class CTxDBBase;`.
Parallel work (snapshotnet + version bump to 5.9.4 + checkpoints/init
/protocol/version edits) included so origin/master matches the local
working tree in one push.
NOT YET COMPILE-TESTED: pushed at the user's explicit request before
the build verification step. If CI fails, expected breakage is in
files that include main.h transitively but not txdb-base.h — fix is
to add `#include "txdb-base.h"` (or rely on the existing txdb.h which
pulls it in).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pairs net.h heartbeat-throttle field with the IBD header-sync fix
in 2a484e4, and ships a full RPC reference doc.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nodes syncing from zero would accept blocks normally up to ~6000 then
stall permanently with askfor_queue=0 and no new blocks. Root cause: a
broken feedback loop between the header planner and block downloader.
Blocks consume entries from mapHeaderSync (MAX 15000) while getheaders
refills only 2000 at a time; when the cache drains, hashBestHeaderSync
falls to 0 and every refill site is guarded on it being non-zero, so
the pipeline deadlocks with no recovery path.
Recovery paths added:
- ProcessBlock: when the cache is empty during IBD after accepting a
block, broadcast getheaders to all full-node peers. This restarts
the planner at the exact point it dies.
- Stall detection: send getheaders alongside the existing getblocks.
getblocks alone cannot refill the header cache.
- SendMessages: belt-and-suspenders, re-request headers every 30s
while hashBestHeaderSync == 0 in IBD, independent of stall state.
Also fix a secondary issue: GetHeaderSyncDownloadPath walks back from
the tip and breaks on the first TTL-evicted entry. The accumulated
partial tail has a parent that is neither in mapBlockIndex nor
mapHeaderSync, so requesting those blocks would produce orphans.
Discard the partial path on a gap; the recovery paths above will
re-request the missing range.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to #5. Addresses three risks with the apply=true path:
- MoneyRange sanity gate: refuse to persist a recalculated supply that is
negative or above MAX_MONEY (2,222,222 TRI). A walk that produces an
out-of-range figure indicates a bug (orphan contamination, missing
prevout), not real chain state. Prevents corrupting nMoneySupply with
junk values.
- Atomic apply: wrap every per-block WriteBlockIndex in a single
TxnBegin/TxnCommit so a mid-walk failure leaves on-disk state
untouched instead of half-rewritten.
- Single chain walk: cache (valueOut - valueIn) per block during the
dry-run pass and reuse the cached deltas during apply. Previous code
walked the full chain twice, roughly doubling apply runtime on a
2.2M-block chain.
Help text now warns that the RPC holds cs_main for the full walk and
blocks new blocks, wallet ops, and other RPC for the duration.
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>
- Reduce equal-trust reorg cooldown from 10min to 2min for faster convergence
- Tighten future block drift from 3min to 90sec to shrink competing-block window
- Require 2+ peers before staking (was 1) to prevent isolated fork creation
- Push full blocks directly to peers instead of inv-only (saves 1-2s Tor roundtrip)
- Add periodic 45-second chain-tip sync to detect and resolve silent forks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Version System:
- Unified version display as v5.8.2 (removed trailing .0)
- Single source of truth in clientversion.h
- Fixed version.cpp to use CLIENT_VERSION_* macros
Staking Improvements:
- Enhanced getstakinginfo with detailed diagnostics
- Shows specific reasons when staking is disabled
- Added wallet lock status, mature coins check, peer count
Performance & Sync:
- Added checkpoint at block 2,200,000 (hash: 0a8d0442...)
- 14 total checkpoints for faster sync
- Enhanced recalculatesupply RPC with safety validation
- Prevents changes > 1M TRI, fixes money supply tracking
Anti-Fork Protection:
- Enhanced reorganize logging with fork details
- Shows old/new tips, fork point, disconnect/connect counts
- Works with existing anti-oscillation and chain re-eval fixes
Recovery Tools (Krystie):
- -reindex flag for full block index rebuild
- recalculatesupply RPC to fix money supply from UTXOs
- SumUtxoValues() helper for UTXO set analysis
All changes are non-consensus and wallet-safe.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Krystie <krystie@cryptographic-triangles.org>
Guard pindexBest and pprev dereferences that segfault during IBD
block serving when chain state is incomplete:
- kernel.cpp: CheckStakeKernelHash null pindexBest during PoS validation
- main.cpp: InvalidChainFound null pprev/pindexBest on rejected blocks
- main.cpp: SetBestChain null pprev in trust calculation
- main.cpp: ProcessBlock orphan handler null pindexBest
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>
Replace FutureDrift(GetAdjustedTime()) with GetTime() + 15min in CheckBlock
and header-sync validation. GetAdjustedTime() incorporates peer-reported
time offsets that vary between Tor nodes, causing the same block to be
accepted by some nodes and rejected by others — the primary cause of
persistent chain forks. AcceptBlock still enforces tight 3-min drift rules
deterministically against the previous block timestamp.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes for the fork-oscillation problem where same-version nodes
keep disagreeing on the chain tip:
1. Prune setStakeSeen on reorg — disconnected PoS blocks' stake entries
were never removed, blocking acceptance of valid competing blocks
and preventing chain convergence after reorganizations.
2. Remove global nBestHeight from PastDrift/FutureDrift — the no-argument
overloads used the mutable global nBestHeight to decide between 3-min
and 10-min timestamp drift at the V5.4 fork boundary (block 2186941).
Nodes at different heights applied different validation rules to the
same block, causing a permanent consensus split. Now always uses
post-fork 3-min rules since all nodes are well past the fork.
3. Anti-oscillation for equal-trust reorgs — the hash-based tiebreaker
now only fires for shallow forks (parent in main chain). Deep forks
with equal trust no longer trigger reorgs, preventing the Tor-latency-
induced ping-pong where nodes flip between competing chains.
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>
- Daemon: automatically downloads blockchain snapshot when no data exists
No -bootstrap flag needed. Use -nobootstrap to skip.
- Qt wallet: auto-bootstraps on first run (no question asked)
Existing users still get the optional re-download prompt.
- New users just install and run - blockchain downloads automatically
- Works on all platforms (Windows, Linux, macOS, ARM64)
- 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
The Tor v3 spec requires SHA3-256 (FIPS-202) for the .onion address
checksum computation, but ToStringIP() was using SHA-256 (double-hash).
This caused every reconstructed .onion address to have incorrect suffix
characters, making all outbound Tor connections fail with SOCKS5 'general
failure' - the entire network had 0 Tor peers despite working Tor instances.
Fix: Replace Hash() call with OpenSSL EVP_sha3_256() which is available
in OpenSSL 3.0+ and produces the correct FIPS-202 SHA3-256 checksum.
Tested: All 5 onion seed nodes now connect successfully.
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>
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>
Remove time-based sync check that showed "out of sync" when blocks
were >6 hours old. For PoS chains with few stakers, blocks can be
hours apart - that's idle, not out of sync. Now uses block count
only. Also adds periodic UI refresh every 30s and switches cached
stake weight from volatile to std::atomic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move GetStakeWeight() off the UI thread by caching in the staking
miner thread. Replace blocking LOCK(cs_vNodes) with TRY_LOCK in
clientmodel and staking icon updates. Fix out-of-sync label getting
stuck when disconnected. Add daemon bootstrap and faster IBD pipeline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>