Compare commits

..

11 Commits

Author SHA1 Message Date
sami7777 b50eecc56f Fix build: nMisbehavior is protected
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 21:09:48 -07:00
sami7777 8953173403 Add comprehensive IBD diagnostics (IBD-DIAG prefix)
Verbose logging at every critical sync pipeline stage:
- Version handler: whether getblocks was sent and why
- Inv handler: count of new vs already-known blocks
- Block handler: every block received (throttled), ProcessBlock failures
- ProcessBlock: CheckBlock failures with details
- SendMessages: stall detection with queue sizes
- Periodic status: height, peers, askfor queue, orphan count
- Getblocks handler: what range the seed is serving

All lines prefixed with IBD-DIAG for easy grep.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 21:05:11 -07:00
sami7777 2f307c195c Disable checkpoint message relay and processing
DNS2 was sending a stale sync checkpoint (block 2,186,940) to DNS3
on connect. ProcessSyncCheckpoint then called PushGetBlocks with the
checkpoint hash as the stop point, and AskFor'd block 2,186,940
directly — overriding the normal sequential getblocks chain. DNS3
would request a block it can't process (missing 2M predecessors)
instead of syncing from genesis.

Fix: ignore incoming checkpoint messages entirely (master key was
already removed in V5 fork, no new checkpoints possible). Also stop
relaying stored checkpoint messages to new peers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:56:40 -07:00
sami7777 8c5024a78a Fix anti-spam check: use chain tip as fallback, add NULL safety
Instead of skipping the anti-spam difficulty check during IBD, fix it
properly:
- Fall back to pindexBest when sync checkpoint is genesis (height 0)
- Add NULL safety for GetLastBlockIndex in both PoS and PoW cases
- PoS case: if no PoS block exists yet (below 9001), skip gracefully
  since AcceptBlock already rejects PoS below MODIFIER_INTERVAL_SWITCH

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:24:48 -07:00
sami7777 3f823e8583 Skip anti-spam difficulty check during IBD
The anti-spam check in ProcessBlock used GetLastSyncCheckpoint() which
pointed to genesis after our reset. When processing PoS blocks,
GetLastBlockIndex(genesis, true) returned NULL (no PoS blocks at genesis),
causing a crash or Misbehaving(100) which banned the seed node.

Fix: skip the entire anti-spam check during IBD - hardcoded checkpoints
already guarantee chain integrity. Also add NULL safety for the PoS
case after IBD completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:19:39 -07:00
sami7777 a4bfc6012a Fix display version: update DISPLAY_VERSION to 5.2.0
version.h had a separate DISPLAY_VERSION set (5.1.7.0) used by
version.cpp for the user-visible version string. clientversion.h
was updated but version.h was not, causing binaries to report
v5.1.7.0 despite being built from v5.2.0 source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 19:37:24 -07:00
sami7777 136d446157 Disable sync checkpoint system that blocks IBD
The sync checkpoint (hashSyncCheckpoint) was persisted in LevelDB pointing
to block 2,186,940. On startup it was loaded from DB, overriding any code
change to the initial value. CheckSync then rejected every block below
that height during IBD since they weren't in mapBlockIndex yet.

Three-pronged fix:
- CheckSync now always returns true (master key disabled, no new sync
  checkpoints will ever be broadcast)
- AcceptBlock no longer calls sync checkpoint enforcement
- LoadBlockIndex resets sync checkpoint to genesis if stored hash is
  not in the block index (prevents assert crash)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 18:32:11 -07:00
sami7777 1966f49ce2 Fix sync checkpoint blocking IBD, bump to v5.2.0
hashSyncCheckpoint was initialized to block 2,186,940 hash, causing
CheckSync to reject ALL blocks below that height during initial block
download (they aren't in mapBlockIndex yet when checked). Changed to
genesis hash so IBD can proceed from block 0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 18:09:38 -07:00
sami7777 a4da39f23c Update CI version to 5.1.9
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:54:56 -07:00
sami7777 87bfc15712 Bump version to v5.1.9
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:48:24 -07:00
sami7777 6353e9d5fa v5.1.9: Assumevalid fast sync + unlimited network buffers
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>
2026-03-16 17:41:08 -07:00
12 changed files with 355 additions and 170 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
env:
VERSION: "5.1.8"
VERSION: "5.2.0"
jobs:
build-windows-qt:
+3 -3
View File
@@ -3,7 +3,7 @@
# Generated by qmake (3.1) (Qt 5.15.18)
# Project: triangles-qt.pro
# Template: app
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
#############################################################################
MAKEFILE = Makefile
@@ -156,7 +156,7 @@ Makefile: triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.c
C:/msys64/mingw64/lib/qtmain.prl \
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
src/qt/triangles.qrc
$(QMAKE) -o Makefile triangles-qt.pro
$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf:
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf:
@@ -244,7 +244,7 @@ C:/msys64/mingw64/lib/qtmain.prl:
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf:
src/qt/triangles.qrc:
qmake: FORCE
@$(QMAKE) -o Makefile triangles-qt.pro
@$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
qmake_all: FORCE
+6 -26
View File
@@ -87,8 +87,8 @@ namespace Checkpoints
}
// triangles: synchronized checkpoint (centrally broadcasted)
uint256 hashSyncCheckpoint = uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0");
uint256 hashPendingCheckpoint = uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0");
uint256 hashSyncCheckpoint = uint256("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
uint256 hashPendingCheckpoint = uint256("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
CSyncCheckpoint checkpointMessage;
CSyncCheckpoint checkpointMessagePending;
uint256 hashInvalidCheckpoint = 0;
@@ -218,30 +218,10 @@ namespace Checkpoints
}
// Check against synchronized checkpoint
// Disabled: master key removed in V5, no new sync checkpoints possible.
// Always returns true to prevent stale DB-persisted checkpoints from blocking IBD.
bool CheckSync(const uint256& hashBlock, const CBlockIndex* pindexPrev)
{
if (fTestNet) return true; // Testnet has no checkpoints
int nHeight = pindexPrev->nHeight + 1;
LOCK(cs_hashSyncCheckpoint);
// sync-checkpoint should always be accepted block
assert(mapBlockIndex.count(hashSyncCheckpoint));
const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
if (nHeight > pindexSync->nHeight)
{
// trace back to same height as sync-checkpoint
const CBlockIndex* pindex = pindexPrev;
while (pindex->nHeight > pindexSync->nHeight)
if (!(pindex = pindex->pprev))
return error("CheckSync: pprev null - block index structure failure");
if (pindex->nHeight < pindexSync->nHeight || pindex->GetBlockHash() != hashSyncCheckpoint)
return false; // only descendant of sync-checkpoint can pass check
}
if (nHeight == pindexSync->nHeight && hashBlock != hashSyncCheckpoint)
return false; // same height with sync-checkpoint
if (nHeight < pindexSync->nHeight && !mapBlockIndex.count(hashBlock))
return false; // lower height than sync-checkpoint
return true;
}
@@ -361,8 +341,8 @@ namespace Checkpoints
bool IsMatureSyncCheckpoint()
{
LOCK(cs_hashSyncCheckpoint);
// sync-checkpoint should always be accepted block
assert(mapBlockIndex.count(hashSyncCheckpoint));
if (!mapBlockIndex.count(hashSyncCheckpoint))
return true; // no valid sync checkpoint, treat as mature
const CBlockIndex* pindexSync = mapBlockIndex[hashSyncCheckpoint];
return (nBestHeight >= pindexSync->nHeight + nCoinbaseMaturity ||
pindexSync->GetBlockTime() + nStakeMinAge < GetAdjustedTime());
+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 1
#define CLIENT_VERSION_REVISION 8
#define CLIENT_VERSION_MINOR 2
#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.
+2
View File
@@ -140,6 +140,7 @@ void Shutdown(void* parg)
if (fFirstThread)
{
fShutdown = true;
int64_t nDeferredWaitStart = GetTimeMillis();
while (true)
{
@@ -180,6 +181,7 @@ void Shutdown(void* parg)
fs::remove(GetPidFile());
UnregisterWallet(pwalletMain);
delete pwalletMain;
// DB is flushed and wallet saved - safe to force-exit if something hangs
NewThread(ExitTimeout, NULL);
MilliSleep(50);
printf("Triangles exited\n\n");
+213 -95
View File
@@ -1639,6 +1639,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (!CheckBlock(!fJustCheck, !fJustCheck, false))
return false;
// Determine if this block is covered by the hardcoded checkpoint.
// Below checkpoint: skip all input validation, FetchInputs, ConnectInputs,
// and wallet sync. The checkpoint hash guarantees chain integrity for these blocks.
bool fAssumeValid = (pindex->nHeight <= Checkpoints::GetTotalBlocksEstimate());
bool fIsInitialDownload = IsInitialBlockDownload();
//// issue here: it doesn't know the version
unsigned int nTxPos;
if (fJustCheck)
@@ -1658,6 +1664,20 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{
uint256 hashTx = tx.GetHash();
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
if (!fJustCheck)
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
// Fast path: below checkpoint, skip all input validation and spent-tracking.
// Just record where each transaction lives on disk (txindex).
if (fAssumeValid)
{
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
continue;
}
// Full validation path (above checkpoint)
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
// If such overwrites are allowed, coinbases and transactions depending upon those
@@ -1681,10 +1701,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
if (!fJustCheck)
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
MapPrevTx mapInputs;
if (tx.IsCoinBase())
nValueOut += tx.GetValueOut();
@@ -1717,20 +1733,18 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
}
if (IsProofOfWork() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
if (!fAssumeValid)
{
int64_t nReward = GetProofOfWorkReward(nFees);
// Check coinbase reward
if (vtx[0].GetValueOut() > nReward)
return DoS(50, error("ConnectBlock() : coinbase reward exceeded (actual=%"PRId64" vs calculated=%"PRId64")",
vtx[0].GetValueOut(),
nReward));
}
if (IsProofOfStake())
if (IsProofOfWork())
{
// Skip expensive coin age calculation and reward validation for blocks
// covered by the hardcoded checkpoint. The checkpoint guarantees chain integrity.
if (pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
int64_t nReward = GetProofOfWorkReward(nFees);
// Check coinbase reward
if (vtx[0].GetValueOut() > nReward)
return DoS(50, error("ConnectBlock() : coinbase reward exceeded (actual=%"PRId64" vs calculated=%"PRId64")",
vtx[0].GetValueOut(),
nReward));
}
if (IsProofOfStake())
{
// triangles: coin stake tx earns reward instead of paying fee
uint64_t nCoinAge;
@@ -1760,8 +1774,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : UpdateTxIndex failed");
}
// Update address index
if (fAddressIndex)
// Update address index (skip during IBD - will be rebuilt on next start with -reindex)
if (fAddressIndex && !fIsInitialDownload)
{
for (unsigned int i = 0; i < vtx.size(); i++)
{
@@ -1834,9 +1848,13 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : WriteBlockIndex failed");
}
// Watch for transactions paying to me
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
// Skip wallet sync during IBD - a full wallet rescan runs when IBD completes.
// This eliminates millions of per-transaction wallet lookups during sync.
if (!fIsInitialDownload)
{
BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true);
}
return true;
}
@@ -2034,10 +2052,14 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
}
}
// Update best block in wallet (so we can detect restored wallets)
// Update best block in wallet (so we can detect restored wallets).
// During IBD, skip this so the wallet knows it needs rescanning on restart.
bool fIsInitialDownload = IsInitialBlockDownload();
const CBlockLocator locator(pindexNew);
::SetBestChain(locator);
if (!fIsInitialDownload)
{
const CBlockLocator locator(pindexNew);
::SetBestChain(locator);
}
// New best block
hashBestChain = hash;
@@ -2050,8 +2072,8 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust;
// Log every 500 blocks during sync, every block once caught up
if (nBestHeight % 500 == 0 || !IsInitialBlockDownload())
// Log every 5000 blocks during sync, every block once caught up
if (nBestHeight % 5000 == 0 || !IsInitialBlockDownload())
printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%"PRId64" date=%s\n",
hashBestChain.ToString().substr(0,20).c_str(), nBestHeight,
CBigNum(nBestChainTrust).ToString().c_str(),
@@ -2102,6 +2124,40 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
pNotificationQueue->Push(strBlockEvent);
}
// Detect IBD-to-synced transition and trigger deferred work:
// wallet rescan (since SyncWithWallets was skipped) and smsg chain scan.
{
static bool fWasInitialDownload = true;
if (fWasInitialDownload && !fIsInitialDownload)
{
printf("*** Initial block download complete at height %d ***\n", nBestHeight);
// Update wallet best chain locator now that IBD is done
const CBlockLocator locator(pindexBest);
::SetBestChain(locator);
// Wallet rescan: SyncWithWallets was skipped during IBD, so scan
// the entire chain to pick up all wallet transactions.
if (pwalletMain)
{
printf("Starting post-IBD wallet rescan from genesis...\n");
uiInterface.InitMessage(_("Rescanning wallet..."));
int nFound = pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true);
printf("Post-IBD wallet rescan complete: %d transactions found\n", nFound);
}
// Secure messaging: scan chain for public keys needed to decrypt messages
if (fSecMsgEnabled)
{
printf("Starting post-IBD secure message chain scan...\n");
uiInterface.InitMessage(_("Scanning for secure messages..."));
SecureMsgScanBlockChain();
printf("Post-IBD secure message chain scan complete\n");
}
}
fWasInitialDownload = fIsInitialDownload;
}
return true;
}
@@ -2415,18 +2471,10 @@ bool CBlock::AcceptBlock()
}
}
// Before fork: enforce sync checkpoints for historical chain integrity
// After fork: no sync checkpoint enforcement (decentralized)
if (nHeight < FORK_HEIGHT_V5)
{
bool cpSatisfies = Checkpoints::CheckSync(hash, pindexPrev);
if (CheckpointsMode == Checkpoints::STRICT && !cpSatisfies)
return error("AcceptBlock() : rejected by synchronized checkpoint");
if (CheckpointsMode == Checkpoints::ADVISORY && !cpSatisfies)
strMiscWarning = _("WARNING: syncronized checkpoint violation detected, but skipped!");
}
// Sync checkpoint enforcement is disabled:
// - Master key was removed in V5 fork, no new sync checkpoints will be broadcast
// - Hardcoded checkpoints already guarantee chain integrity
// - The persisted hashSyncCheckpoint in LevelDB blocks IBD from progressing
// Enforce rule that the coinbase starts with serialized block height
CScript expect = CScript() << nHeight;
@@ -2502,37 +2550,40 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
// Skip block signature verification during initial block download (below checkpoint).
// The hardcoded checkpoint guarantees historical chain integrity.
if (!pblock->CheckBlock(true, true, !IsInitialBlockDownload()))
return error("ProcessBlock() : CheckBlock FAILED");
CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
if(pcheckpoint && fDebug)
{
const CBlockIndex* pindexLastPos = GetLastBlockIndex(pcheckpoint, true);
if(pindexLastPos)
{
printf("ProcessBlock(): Last POS Block Height: %d \n", pindexLastPos->nHeight);
}
else
{
printf("ProcessBlock(): Previous POS block not found.\n");
}
printf("IBD-DIAG: CheckBlock FAILED for %s (PoS=%d, IBD=%d)\n",
hash.ToString().substr(0,20).c_str(), pblock->IsProofOfStake(), IsInitialBlockDownload());
return error("ProcessBlock() : CheckBlock FAILED");
}
// Anti-spam: reject blocks with insufficient difficulty to prevent memory flooding.
// Use sync checkpoint as reference; fall back to chain tip if checkpoint is genesis.
CBlockIndex* pcheckpoint = Checkpoints::GetLastSyncCheckpoint();
if (!pcheckpoint || pcheckpoint->nHeight == 0)
pcheckpoint = pindexBest;
if (pcheckpoint && pblock->hashPrevBlock != hashBestChain && !Checkpoints::WantedByPendingSyncCheckpoint(hash))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
if (pblock->IsProofOfStake())
bnRequired.SetCompact(ComputeMinStake(GetLastBlockIndex(pcheckpoint, true)->nBits, deltaTime, pblock->nTime));
{
const CBlockIndex* pindexLastPos = GetLastBlockIndex(pcheckpoint, true);
if (pindexLastPos)
bnRequired.SetCompact(ComputeMinStake(pindexLastPos->nBits, deltaTime, pblock->nTime));
// else: no PoS history yet (below block 9001), skip — AcceptBlock rejects PoS below MODIFIER_INTERVAL_SWITCH
}
else
bnRequired.SetCompact(ComputeMinWork(GetLastBlockIndex(pcheckpoint, false)->nBits, deltaTime));
{
const CBlockIndex* pindexLastPow = GetLastBlockIndex(pcheckpoint, false);
if (pindexLastPow)
bnRequired.SetCompact(ComputeMinWork(pindexLastPow->nBits, deltaTime));
}
if (bnNewBlock > bnRequired)
if (bnRequired != 0 && bnNewBlock > bnRequired)
{
if (pfrom)
pfrom->Misbehaving(100);
@@ -2598,7 +2649,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
mapOrphanBlocksByPrev.erase(hashPrev);
}
if (nBestHeight % 500 == 0 || !IsInitialBlockDownload())
if (nBestHeight % 5000 == 0 || !IsInitialBlockDownload())
printf("ProcessBlock: ACCEPTED block %d\n", nBestHeight);
// triangles: if responsible for sync-checkpoint send it
@@ -3211,16 +3262,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
}
// Ask the first connected node for block updates
// Ask connected nodes for block updates
// During IBD, always request blocks from any valid peer (critical for reconnection)
static int nAskedForBlocks = 0;
if (!pfrom->fClient && !pfrom->fOneShot &&
bool fShouldAsk = !pfrom->fClient && !pfrom->fOneShot &&
(pfrom->nStartingHeight > (nBestHeight - 144)) &&
(pfrom->nVersion < NOBLKS_VERSION_START ||
pfrom->nVersion >= NOBLKS_VERSION_END) &&
(nAskedForBlocks < 1 || vNodes.size() <= 1))
(IsInitialBlockDownload() || nAskedForBlocks < 1 || vNodes.size() <= 1);
printf("IBD-DIAG: version handler: peer=%s height=%d ourHeight=%d fClient=%d fOneShot=%d shouldAsk=%d nAskedForBlocks=%d IBD=%d\n",
pfrom->addr.ToString().c_str(), pfrom->nStartingHeight, nBestHeight,
pfrom->fClient, pfrom->fOneShot, fShouldAsk, nAskedForBlocks, IsInitialBlockDownload());
if (fShouldAsk)
{
nAskedForBlocks++;
pfrom->PushGetBlocks(pindexBest, uint256(0));
printf("IBD-DIAG: sent getblocks from height %d to peer %s\n", nBestHeight, pfrom->addr.ToString().c_str());
}
// Relay alerts
@@ -3230,12 +3287,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
item.second.RelayTo(pfrom);
}
// triangles: relay sync-checkpoint
{
LOCK(Checkpoints::cs_hashSyncCheckpoint);
if (!Checkpoints::checkpointMessage.IsNull())
Checkpoints::checkpointMessage.RelayTo(pfrom);
}
// Sync checkpoint relay disabled (master key removed in V5 fork).
// Relaying stale checkpoints causes IBD nodes to request far-future blocks.
pfrom->fSuccessfullyConnected = true;
@@ -3341,13 +3394,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// find last block in inv vector
unsigned int nLastBlock = (unsigned int)(-1);
int nBlockInv = 0, nTxInv = 0;
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++) {
if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK) {
if (vInv[nInv].type == MSG_BLOCK) nBlockInv++;
else nTxInv++;
if (vInv[vInv.size() - 1 - nInv].type == MSG_BLOCK && nLastBlock == (unsigned int)(-1)) {
nLastBlock = vInv.size() - 1 - nInv;
break;
}
}
printf("IBD-DIAG: inv received: %d blocks, %d tx from %s (our height=%d)\n",
nBlockInv, nTxInv, pfrom->addr.ToString().c_str(), nBestHeight);
CTxDB txdb("r");
int nNew = 0, nAlready = 0;
for (unsigned int nInv = 0; nInv < vInv.size(); nInv++)
{
const CInv &inv = vInv[nInv];
@@ -3357,25 +3416,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
pfrom->AddInventoryKnown(inv);
bool fAlreadyHave = AlreadyHave(txdb, inv);
if (fDebug)
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
if (inv.type == MSG_BLOCK) {
if (fAlreadyHave) nAlready++; else nNew++;
}
if (!fAlreadyHave)
pfrom->AskFor(inv);
else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash)) {
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
} else if (nInv == nLastBlock) {
// In case we are on a very long side-chain, it is possible that we already have
// the last block in an inv bundle sent in response to getblocks. Try to detect
// this situation and push another getblocks to continue.
pfrom->PushGetBlocks(mapBlockIndex[inv.hash], uint256(0));
if (fDebug)
printf("force request: %s\n", inv.ToString().c_str());
printf("IBD-DIAG: inv last block already known, pushing getblocks from %d\n",
mapBlockIndex[inv.hash]->nHeight);
}
// Track requests for our stuff
Inventory(inv.hash);
}
if (nBlockInv > 0)
printf("IBD-DIAG: inv result: %d new blocks requested, %d already have\n", nNew, nAlready);
}
@@ -3464,8 +3522,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Send the rest of the chain
if (pindex)
pindex = pindex->pnext;
int nLimit = 500;
printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit);
int nLimit = IsInitialBlockDownload() ? 20000 : 500;
printf("IBD-DIAG: getblocks request from peer %s: start=%d stop=%s limit=%d\n",
pfrom->addr.ToString().c_str(), (pindex ? pindex->nHeight : -1),
hashStop.ToString().substr(0,20).c_str(), nLimit);
for (; pindex; pindex = pindex->pnext)
{
if (pindex->GetBlockHash() == hashStop)
@@ -3490,17 +3550,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
}
else if (strCommand == "checkpoint")
{
CSyncCheckpoint checkpoint;
vRecv >> checkpoint;
if (checkpoint.ProcessSyncCheckpoint(pfrom))
{
// Relay
pfrom->hashCheckpointKnown = checkpoint.hashCheckpoint;
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
checkpoint.RelayTo(pnode);
}
// Sync checkpoint system disabled (master key removed in V5 fork).
// Ignore checkpoint messages — processing them during IBD causes the
// node to request a single far-future block instead of syncing sequentially.
}
else if (strCommand == "getheaders")
@@ -3674,19 +3726,50 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
vRecv >> block;
uint256 hashBlock = block.GetHash();
printf("received block %s\n", hashBlock.ToString().substr(0,20).c_str());
// block.print();
// Log every block during IBD (with throttling after first 100)
static int64_t nLastBlockLog = 0;
static int nBlocksReceived = 0;
nBlocksReceived++;
bool fLogThis = (nBlocksReceived <= 20) || (nBestHeight % 500 == 0) || !IsInitialBlockDownload() || (GetTime() - nLastBlockLog >= 5);
if (fLogThis) {
printf("IBD-DIAG: block received #%d hash=%s from=%s ourHeight=%d\n",
nBlocksReceived, hashBlock.ToString().substr(0,20).c_str(),
pfrom->addr.ToString().c_str(), nBestHeight);
nLastBlockLog = GetTime();
}
CInv inv(MSG_BLOCK, hashBlock);
pfrom->AddInventoryKnown(inv);
if (ProcessBlock(pfrom, &block))
{
mapAlreadyAskedFor.erase(inv);
if (block.nDoS)
if (IsInitialBlockDownload())
{
static int nBlocksSinceRequest = 0;
if (++nBlocksSinceRequest >= 100)
{
nBlocksSinceRequest = 0;
pfrom->pindexLastGetBlocksBegin = NULL;
pfrom->PushGetBlocks(pindexBest, uint256(0));
printf("IBD-DIAG: pipeline refill at height %d\n", nBestHeight);
}
}
}
else
{
printf("IBD-DIAG: ProcessBlock FAILED for block %s (height after prev=%d, DoS=%d)\n",
hashBlock.ToString().substr(0,20).c_str(), nBestHeight, block.nDoS);
}
if (block.nDoS) {
printf("IBD-DIAG: Misbehaving peer %s by %d\n",
pfrom->addr.ToString().c_str(), block.nDoS);
pfrom->Misbehaving(block.nDoS);
if (fSecMsgEnabled)
}
if (fSecMsgEnabled && !IsInitialBlockDownload())
SecureMsgScanBlock(block);
}
@@ -4120,9 +4203,44 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->PushMessage("inv", vInv);
//
// Stall detection: if IBD and no new blocks for 5 seconds, re-request
//
if (IsInitialBlockDownload() && !pto->fClient)
{
static int64_t nLastBlockReceived = 0;
static int nLastHeight = 0;
static int64_t nLastStallLog = 0;
if (nBestHeight > nLastHeight) {
nLastHeight = nBestHeight;
nLastBlockReceived = GetTime();
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) {
if (GetTime() - nLastStallLog >= 10) { // log every 10s max
printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n",
nBestHeight, (int)(GetTime() - nLastBlockReceived),
pto->addr.ToString().c_str(),
(int)pto->mapAskFor.size(), (int)pto->nSendSize);
nLastStallLog = GetTime();
}
pto->pindexLastGetBlocksBegin = NULL;
pto->PushGetBlocks(pindexBest, uint256(0));
nLastBlockReceived = GetTime();
}
}
//
// Message: getdata
//
// Periodic IBD status
if (IsInitialBlockDownload()) {
static int64_t nLastStatus = 0;
if (GetTime() - nLastStatus >= 15) {
printf("IBD-DIAG: STATUS height=%d peers=%d askfor_queued=%d orphans=%d\n",
nBestHeight, (int)vNodes.size(), (int)pto->mapAskFor.size(), (int)mapOrphanBlocks.size());
nLastStatus = GetTime();
}
}
vector<CInv> vGetData;
int64_t nNow = GetTime() * 1000000;
CTxDB txdb("r");
+47 -20
View File
@@ -6,6 +6,7 @@
#include "irc.h"
#include "db.h"
#include "net.h"
#include "main.h"
#include "init.h"
#include "strlcpy.h"
#include "addrman.h"
@@ -889,7 +890,7 @@ void ThreadSocketHandler2(void* parg)
//
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 50000; // frequency to poll pnode->vSend
timeout.tv_usec = IsInitialBlockDownload() ? 10000 : 50000; // faster polling during IBD
fd_set fdsetRecv;
fd_set fdsetSend;
@@ -1574,24 +1575,29 @@ void ThreadOpenConnections2(void* parg)
if (fShutdown)
return;
// Add seed nodes if IRC isn't working
if (addrman.size()==0 && (GetTime() - nStart > 60) && !fTestNet)
// Add hardcoded seed nodes when we have no connections.
// Original check (addrman.size()==0) was too conservative - stale entries
// in peers.dat would prevent fallback to working hardcoded IPs forever.
{
std::vector<CAddress> vAdd;
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
// Seed nodes are given a random 'last seen time' of between one and two
// weeks ago.
const int64_t nOneWeek = 7*24*60*60;
struct in_addr ip;
memcpy(&ip, &pnSeed[i], sizeof(ip));
CAddress addr(CService(ip, GetDefaultPort()));
addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek;
vAdd.push_back(addr);
LOCK(cs_vNodes);
bool fNoOutbound = true;
BOOST_FOREACH(CNode* pnode, vNodes) {
if (!pnode->fInbound) { fNoOutbound = false; break; }
}
if (fNoOutbound && (GetTime() - nStart > 30) && !fTestNet)
{
std::vector<CAddress> vAdd;
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
{
struct in_addr ip;
memcpy(&ip, &pnSeed[i], sizeof(ip));
CAddress addr(CService(ip, GetDefaultPort()));
addr.nTime = GetTime() - GetRand(60*60); // seen recently
vAdd.push_back(addr);
}
addrman.Add(vAdd, CNetAddr("127.0.0.1"));
printf("No outbound connections after 30s, added %d hardcoded seeds\n", (int)vAdd.size());
}
addrman.Add(vAdd, CNetAddr("127.0.0.1"));
}
//
@@ -1809,6 +1815,7 @@ void ThreadMessageHandler2(void* parg)
{
printf("ThreadMessageHandler started\n");
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
bool fWasBoosted = false;
while (!fShutdown)
{
vector<CNode*> vNodesCopy;
@@ -1851,11 +1858,21 @@ void ThreadMessageHandler2(void* parg)
pnode->Release();
}
// Boost thread priority during IBD, restore when caught up
if (IsInitialBlockDownload() && !fWasBoosted) {
SetThreadPriority(THREAD_PRIORITY_NORMAL);
fWasBoosted = true;
} else if (!IsInitialBlockDownload() && fWasBoosted) {
SetThreadPriority(THREAD_PRIORITY_BELOW_NORMAL);
fWasBoosted = false;
}
// Wait and allow messages to bunch up.
// During IBD, use a shorter sleep to maximize block processing throughput.
// Reduce vnThreadsRunning so StopNode has permission to exit while
// we're sleeping, but we must always check fShutdown after doing this.
vnThreadsRunning[THREAD_MESSAGEHANDLER]--;
MilliSleep(100);
MilliSleep(IsInitialBlockDownload() ? 10 : 100);
if (fRequestShutdown)
StartShutdown();
vnThreadsRunning[THREAD_MESSAGEHANDLER]++;
@@ -2149,8 +2166,18 @@ bool StopNode()
if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n");
if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n");
if (vnThreadsRunning[THREAD_STAKE_MINER] > 0) printf("ThreadStakeMiner still running\n");
while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
MilliSleep(20);
{
int64_t nWaitStart = GetTime();
while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCHANDLER] > 0)
{
if (GetTime() - nWaitStart > 10)
{
printf("Timed out waiting for message/RPC threads to stop\n");
break;
}
MilliSleep(20);
}
}
MilliSleep(50);
DumpAddresses();
return true;
+9 -4
View File
@@ -22,12 +22,13 @@
class CRequestTracker;
class CNode;
class CBlockIndex;
bool IsInitialBlockDownload();
extern int nBestHeight;
inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
inline unsigned int ReceiveFloodSize() { return (unsigned int)-1; }
inline unsigned int SendBufferSize() { return (unsigned int)-1; }
void AddOneShot(std::string strDest);
bool RecvLine(SOCKET hSocket, std::string& strLine);
@@ -429,8 +430,12 @@ public:
nNow = std::max(nNow, nLastTime);
nLastTime = nNow;
// Each retry is 2 minutes after the last
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
// During IBD, request immediately (no 2-minute retry delay)
// Normal operation: each retry is 2 minutes after the last
if (nRequestTime > 0 && IsInitialBlockDownload())
nRequestTime = nNow;
else
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
mapAskFor.insert(std::make_pair(nRequestTime, inv));
}
+3 -1
View File
@@ -63,7 +63,9 @@ void ClientModel::updateTimer()
int newNumBlocks = getNumBlocks();
int newNumBlocksOfPeers = getNumBlocksOfPeers();
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers)
// Always emit during IBD so the speed/ETA display stays live
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers
|| newNumBlocks < newNumBlocksOfPeers)
{
cachedNumBlocks = newNumBlocks;
cachedNumBlocksOfPeers = newNumBlocksOfPeers;
+57 -13
View File
@@ -751,24 +751,57 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
QString tooltip;
QString importText;
importText = tr("Synchronizing with network...");
if(count < nTotalBlocks)
{
// Calculate blocks/sec - only update rate when new blocks arrive
static int lastCount = 0;
static qint64 lastRateTime = 0;
static float blocksPerSec = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (count > lastCount) {
// New blocks arrived - recalculate speed
if (lastRateTime > 0) {
float elapsed = (now - lastRateTime) / 1000.0f;
if (elapsed > 0.1f) {
float instantRate = (count - lastCount) / elapsed;
blocksPerSec = (blocksPerSec < 0.1f) ? instantRate : (blocksPerSec * 0.7f + instantRate * 0.3f);
}
}
lastCount = count;
lastRateTime = now;
} else if (lastRateTime > 0 && (now - lastRateTime) > 10000) {
// No blocks for 10+ seconds - show 0
blocksPerSec = 0.0f;
}
int nRemainingBlocks = nTotalBlocks - count;
float nPercentageDone = count / (nTotalBlocks * 0.01f);
progressBarLabel->setText(importText);
// Build informative status text
QString speedText;
if (blocksPerSec >= 1.0f) {
int etaSeconds = (int)(nRemainingBlocks / blocksPerSec);
QString etaStr;
if (etaSeconds < 60)
etaStr = tr("%n sec", "", etaSeconds);
else if (etaSeconds < 3600)
etaStr = tr("%n min", "", etaSeconds / 60);
else
etaStr = tr("%1h %2m").arg(etaSeconds / 3600).arg((etaSeconds % 3600) / 60);
speedText = tr("Syncing: %1 blk/s ~%2 remaining").arg(blocksPerSec, 0, 'f', 1).arg(etaStr);
} else {
speedText = tr("Synchronizing with network...");
}
progressBarLabel->setText(speedText);
progressBarLabel->setVisible(true);
progressBar->setFormat(tr("~%n block(s) remaining", "", nRemainingBlocks));
progressBar->setFormat(tr("Block %1 / %2 (%3%)").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2));
progressBar->setMaximum(nTotalBlocks);
progressBar->setValue(count);
progressBar->setVisible(true);
ui->label_blocks->setText(tr("%n blocks", "", count));
ui->label_blocks->setVisible(true);
ui->label_blocks->setVisible(false);
tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2);
tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2);
}
else
{
@@ -871,16 +904,27 @@ void TrianglesGUI::changeEvent(QEvent *e)
void TrianglesGUI::closeEvent(QCloseEvent *event)
{
#ifndef Q_OS_MAC // Ignored on Mac
if(clientModel)
{
#ifndef Q_OS_MAC // Ignored on Mac
if(!clientModel->getOptionsModel()->getMinimizeToTray() &&
!clientModel->getOptionsModel()->getMinimizeOnClose())
if(clientModel->getOptionsModel()->getMinimizeOnClose())
{
QApplication::quit();
// Minimize to taskbar instead of closing
QMainWindow::showMinimized();
event->ignore();
return;
}
if(clientModel->getOptionsModel()->getMinimizeToTray() && trayIcon)
{
// Hide to system tray instead of closing
hide();
event->ignore();
return;
}
#endif
}
#endif
// Actually closing - quit the application
QApplication::quit();
QMainWindow::closeEvent(event);
}
+10 -3
View File
@@ -484,10 +484,17 @@ 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());
// triangles: load hashSyncCheckpoint
// triangles: load hashSyncCheckpoint (best-effort, non-fatal)
if (!ReadSyncCheckpoint(Checkpoints::hashSyncCheckpoint))
return error("CTxDB::LoadBlockIndex() : hashSyncCheckpoint not loaded");
printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
printf("LoadBlockIndex(): no sync checkpoint in DB, using default\n");
else
printf("LoadBlockIndex(): synchronized checkpoint %s\n", Checkpoints::hashSyncCheckpoint.ToString().c_str());
// If the stored checkpoint isn't in our index, reset to genesis so we don't assert-crash
if (!mapBlockIndex.count(Checkpoints::hashSyncCheckpoint))
{
printf("LoadBlockIndex(): sync checkpoint not in index, resetting to genesis\n");
Checkpoints::hashSyncCheckpoint = (!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet);
}
// Load bnBestInvalidTrust, OK if it doesn't exist
CBigNum bnBestInvalidTrust;
+2 -2
View File
@@ -52,8 +52,8 @@ static const int BIP0031_VERSION = 60000;
static const int MEMPOOL_GD_VERSION = 60002;
#define DISPLAY_VERSION_MAJOR 5
#define DISPLAY_VERSION_MINOR 1
#define DISPLAY_VERSION_REVISION 7
#define DISPLAY_VERSION_MINOR 2
#define DISPLAY_VERSION_REVISION 0
#define DISPLAY_VERSION_BUILD 0
#endif