vConnect.push_back(pindex);
reverse(vConnect.begin(), vConnect.end());
+ printf("REORGANIZE: Disconnect %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: Connect %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+
// Disconnect shorter branch
vector<CTransaction> vResurrect;
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect)
if (!block.ReadFromDisk(pindex))
return error("Reorganize() : ReadFromDisk for disconnect failed");
if (!block.DisconnectBlock(txdb, pindex))
- return error("Reorganize() : DisconnectBlock failed");
+ return error("Reorganize() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
// Queue memory transactions to resurrect
BOOST_FOREACH(const CTransaction& tx, block.vtx)
{
// Invalid block
txdb.TxnAbort();
- return error("Reorganize() : ConnectBlock failed");
+ return error("Reorganize() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str());
}
// Queue memory transactions to delete
BOOST_FOREACH(CTransaction& tx, vDelete)
tx.RemoveFromMemoryPool();
- printf("REORGANIZE: Disconnected %i blocks; %s..%s\n", vDisconnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexBest->GetBlockHash().ToString().substr(0,20).c_str());
- printf("REORGANIZE: Connected %i blocks; %s..%s\n", vConnect.size(), pfork->GetBlockHash().ToString().substr(0,20).c_str(), pindexNew->GetBlockHash().ToString().substr(0,20).c_str());
+ printf("REORGANIZE: done\n");
return true;
}
printf("runCommand error: system(%s) returned %d\n", strCommand.c_str(), nErr);
}
+// Called from inside SetBestChain: attaches a block to the new best chain being built
+bool CBlock::SetBestChainInner(CTxDB& txdb, CBlockIndex *pindexNew)
+{
+ uint256 hash = GetHash();
+
+ // Adding to current best branch
+ if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
+ {
+ txdb.TxnAbort();
+ InvalidChainFound(pindexNew);
+ return false;
+ }
+ if (!txdb.TxnCommit())
+ return error("SetBestChain() : TxnCommit failed");
+
+ // Add to current best branch
+ pindexNew->pprev->pnext = pindexNew;
+
+ // Delete redundant memory transactions
+ BOOST_FOREACH(CTransaction& tx, vtx)
+ tx.RemoveFromMemoryPool();
+
+ return true;
+}
+
bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
{
uint256 hash = GetHash();
}
else if (hashPrevBlock == hashBestChain)
{
- // Adding to current best branch
- if (!ConnectBlock(txdb, pindexNew) || !txdb.WriteHashBestChain(hash))
+ if (!SetBestChainInner(txdb, pindexNew))
+ return error("SetBestChain() : SetBestChainInner failed");
+ }
+ else
+ {
+ // the first block in the new chain that will cause it to become the new best chain
+ CBlockIndex *pindexIntermediate = pindexNew;
+
+ // list of blocks that need to be connected afterwards
+ std::vector<CBlockIndex*> vpindexSecondary;
+
+ // Reorganize is costly in terms of db load, as it works in a single db transaction.
+ // Try to limit how much needs to be done inside
+ while (pindexIntermediate->pprev && pindexIntermediate->pprev->bnChainWork > pindexBest->bnChainWork)
{
- txdb.TxnAbort();
- InvalidChainFound(pindexNew);
- return error("SetBestChain() : ConnectBlock failed");
+ vpindexSecondary.push_back(pindexIntermediate);
+ pindexIntermediate = pindexIntermediate->pprev;
}
- if (!txdb.TxnCommit())
- return error("SetBestChain() : TxnCommit failed");
- // Add to current best branch
- pindexNew->pprev->pnext = pindexNew;
+ if (!vpindexSecondary.empty())
+ printf("Postponing %i reconnects\n", vpindexSecondary.size());
- // Delete redundant memory transactions
- BOOST_FOREACH(CTransaction& tx, vtx)
- tx.RemoveFromMemoryPool();
- }
- else
- {
- // New best branch
- if (!Reorganize(txdb, pindexNew))
+ // Switch to new best branch
+ if (!Reorganize(txdb, pindexIntermediate))
{
txdb.TxnAbort();
InvalidChainFound(pindexNew);
return error("SetBestChain() : Reorganize failed");
}
+
+ // Connect futher blocks
+ BOOST_REVERSE_FOREACH(CBlockIndex *pindex, vpindexSecondary)
+ {
+ CBlock block;
+ if (!block.ReadFromDisk(pindex))
+ {
+ printf("SetBestChain() : ReadFromDisk failed\n");
+ break;
+ }
+ txdb.TxnBegin();
+ // errors now are not fatal, we still did a reorganisation to a new chain in a valid way
+ if (!block.SetBestChainInner(txdb, pindex))
+ break;
+ }
}
// Update best block in wallet (so we can detect restored wallets)
return error("AcceptBlock() : AddToBlockIndex failed");
// Relay inventory, but don't relay old inventory during initial block download
+ int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate();
if (hashBestChain == hash)
CRITICAL_BLOCK(cs_vNodes)
BOOST_FOREACH(CNode* pnode, vNodes)
- if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : 140700))
+ if (nBestHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate))
pnode->PushInventory(CInv(MSG_BLOCK, hash));
return true;
}
CTxDB txdb("r");
- BOOST_FOREACH(const CInv& inv, vInv)
+ for (int nInv = 0; nInv < vInv.size(); nInv++)
{
+ const CInv &inv = vInv[nInv];
+
if (fShutdown)
return true;
pfrom->AddInventoryKnown(inv);
if (fDebug)
printf(" got inventory: %s %s\n", inv.ToString().c_str(), fAlreadyHave ? "have" : "new");
- if (!fAlreadyHave)
+ // Always request the last block in an inv bundle (even if we already have it), as it is the
+ // trigger for the other side to send further invs. If we are stuck on a (very long) side chain,
+ // this is necessary to connect earlier received orphan blocks to the chain again.
+ if (!fAlreadyHave || (inv.type == MSG_BLOCK && nInv==vInv.size()-1))
pfrom->AskFor(inv);
- else if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
+ if (inv.type == MSG_BLOCK && mapOrphanBlocks.count(inv.hash))
pfrom->PushGetBlocks(pindexBest, GetOrphanRoot(mapOrphanBlocks[inv.hash]));
// Track requests for our stuff