upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/db.cpp41
-rw-r--r--src/main.cpp75
-rw-r--r--src/main.h2
-rw-r--r--src/main_gui.cpp5
4 files changed, 123 insertions, 0 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 4cc54ac..ce7b875 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -435,6 +435,47 @@ bool CTxDB::LoadBlockIndex()
435 nBestHeight = pindexBest->nHeight; 435 nBestHeight = pindexBest->nHeight;
436 printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight); 436 printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight);
437 437
438 // Verify the tail of the best chain, and fall back to the last good block
439 // if any of it fails.
440 //
441 // A node that accepted blocks its current rules would reject -- because it
442 // was running an older build, or because the tip it landed on was never
443 // valid -- has no way to notice on its own. It keeps the branch, keeps
444 // building on it, and keeps mining coins that the rest of the network
445 // throws away. The only cure used to be deleting blk*.dat by hand, which
446 // requires knowing something is wrong in the first place. That is how a
447 // node here once mined a 323-block fork for five days.
448 //
449 // Checking every block on every start would mean a RandomX hash per block,
450 // so only the tail is checked by default. /checkblocks=0 does the lot.
451 int nCheckBlocks = nCheckBlocksOnLoad;
452 CBlockIndex* pindexFork = NULL;
453 int nChecked = 0;
454 for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
455 {
456 if (nCheckBlocks > 0 && nChecked >= nCheckBlocks)
457 break;
458 nChecked++;
459 CBlock block;
460 if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true))
461 return error("LoadBlockIndex() : ReadFromDisk failed at height %d", pindex->nHeight);
462 if (!block.CheckBlock())
463 {
464 printf("LoadBlockIndex() : *** bad block at height %d, hash=%s\n",
465 pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str());
466 // Keep going: an older bad block further back wins, because
467 // everything after it has to come off too.
468 pindexFork = pindex->pprev;
469 }
470 }
471 printf("LoadBlockIndex(): verified %d block(s)\n", nChecked);
472
473 // The repair itself happens in the caller, after this handle is closed.
474 // The cursor above is still open and holds read locks, so a write
475 // transaction opened here waits on it forever -- measured: the node came
476 // up, printed the diagnosis, and hung with no error anywhere.
477 pindexBadChainFork = pindexFork;
478
438 return true; 479 return true;
439} 480}
440 481
diff --git a/src/main.cpp b/src/main.cpp
index 15805ca..dbb2c41 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -49,6 +49,15 @@ uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea9
49CBlockIndex* pindexGenesisBlock = NULL; 49CBlockIndex* pindexGenesisBlock = NULL;
50int nBestHeight = -1; 50int nBestHeight = -1;
51uint256 hashBestChain = 0; 51uint256 hashBestChain = 0;
52// Blocks at the tip to re-verify on startup. 0 means the whole chain. 288 is
53// about ten hours at a two-minute target -- far enough back to catch a bad tip,
54// short enough that startup does not pay a RandomX hash per block for the whole
55// history. /checkblocks=N overrides it.
56int nCheckBlocksOnLoad = 288;
57// Set by CTxDB::LoadBlockIndex when the tail of the chain fails verification.
58// The repair runs in LoadBlockIndex() below, once that read-only handle and its
59// cursor are closed.
60CBlockIndex* pindexBadChainFork = NULL;
52CBlockIndex* pindexBest = NULL; 61CBlockIndex* pindexBest = NULL;
53 62
54map<uint256, CBlock*> mapOrphanBlocks; 63map<uint256, CBlock*> mapOrphanBlocks;
@@ -1577,6 +1586,72 @@ bool LoadBlockIndex(bool fAllowNew)
1577 return false; 1586 return false;
1578 txdb.Close(); 1587 txdb.Close();
1579 1588
1589 // Repair a bad tip found during verification.
1590 //
1591 // This has to happen after the handle above is closed. CTxDB::LoadBlockIndex
1592 // walks the index with a cursor it never closes, so it is still holding read
1593 // locks -- a write transaction opened alongside it waits on them forever.
1594 // Measured, not guessed: the node came up, printed the diagnosis, and hung
1595 // there with nothing in any log.
1596 if (pindexBadChainFork)
1597 {
1598 CBlockIndex* pindexFork = pindexBadChainFork;
1599 pindexBadChainFork = NULL;
1600
1601 printf("LoadBlockIndex() : *** moving best chain back to height %d, discarding %d block(s)\n",
1602 pindexFork->nHeight, nBestHeight - pindexFork->nHeight);
1603
1604 vector<CBlockIndex*> vDisconnect;
1605 for (CBlockIndex* pindex = pindexBest; pindex && pindex != pindexFork; pindex = pindex->pprev)
1606 vDisconnect.push_back(pindex);
1607
1608 bool fRolledBack = false;
1609 try
1610 {
1611 CTxDB txdbWrite;
1612 txdbWrite.TxnBegin();
1613 foreach(CBlockIndex* pindex, vDisconnect)
1614 {
1615 CBlock block;
1616 if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true))
1617 throw runtime_error("ReadFromDisk for disconnect failed");
1618 if (!block.DisconnectBlock(txdbWrite, pindex))
1619 throw runtime_error("DisconnectBlock failed");
1620 }
1621 if (!txdbWrite.WriteHashBestChain(pindexFork->GetBlockHash()))
1622 throw runtime_error("WriteHashBestChain failed");
1623 txdbWrite.TxnCommit();
1624 txdbWrite.Close();
1625 fRolledBack = true;
1626 }
1627 catch (const std::exception& e)
1628 {
1629 printf("LoadBlockIndex() : rollback failed: %s\n", e.what());
1630 }
1631 catch (...)
1632 {
1633 printf("LoadBlockIndex() : rollback failed\n");
1634 }
1635
1636 if (fRolledBack)
1637 {
1638 foreach(CBlockIndex* pindex, vDisconnect)
1639 if (pindex->pprev)
1640 pindex->pprev->pnext = NULL;
1641
1642 pindexBest = pindexFork;
1643 hashBestChain = pindexBest->GetBlockHash();
1644 nBestHeight = pindexBest->nHeight;
1645 printf("LoadBlockIndex() : now at height %d, will re-download from peers\n", nBestHeight);
1646 }
1647 else
1648 {
1649 // Start anyway. The operator needs a running node to read this from.
1650 printf("LoadBlockIndex() : *** still on the bad chain at height %d -- back up the data directory and report this\n",
1651 nBestHeight);
1652 }
1653 }
1654
1580 // 1655 //
1581 // Init with genesis block 1656 // Init with genesis block
1582 // 1657 //
diff --git a/src/main.h b/src/main.h
index 750d3f1..eea10b1 100644
--- a/src/main.h
+++ b/src/main.h
@@ -52,6 +52,8 @@ extern unsigned int GENESIS_NONCE;
52extern CBlockIndex* pindexGenesisBlock; 52extern CBlockIndex* pindexGenesisBlock;
53extern int nBestHeight; 53extern int nBestHeight;
54extern uint256 hashBestChain; 54extern uint256 hashBestChain;
55extern int nCheckBlocksOnLoad;
56extern CBlockIndex* pindexBadChainFork;
55extern CBlockIndex* pindexBest; 57extern CBlockIndex* pindexBest;
56extern unsigned int nTransactionsUpdated; 58extern unsigned int nTransactionsUpdated;
57extern string strSetDataDir; 59extern string strSetDataDir;
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 852ad4d..99bcb6f 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -54,6 +54,7 @@ static void PrintUsage()
54 printf(" /participant=POOL_BTF_ADDRESS\n"); 54 printf(" /participant=POOL_BTF_ADDRESS\n");
55 printf(" /solomine\n"); 55 printf(" /solomine\n");
56 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); 56 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n");
57 printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n");
57 printf("\n"); 58 printf("\n");
58 printf("Pool operator announcement:\n"); 59 printf("Pool operator announcement:\n");
59 printf(" /poolname=NAME\n"); 60 printf(" /poolname=NAME\n");
@@ -101,6 +102,10 @@ static void ParseStartupArguments(int argc, char* argv[])
101 if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine")) 102 if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine"))
102 fSoloMineTest = true; 103 fSoloMineTest = true;
103 104
105 string strCheckBlocks = argval2(argc, argv, "/checkblocks", "-checkblocks");
106 if (!strCheckBlocks.empty())
107 nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str());
108
104 // /genproclimit=N -- threads to hash with. 0 or absent means automatic, 109 // /genproclimit=N -- threads to hash with. 0 or absent means automatic,
105 // which is every core but one. Named after Bitcoin's own option so it reads 110 // which is every core but one. Named after Bitcoin's own option so it reads
106 // familiarly to anyone who has run one of these before. 111 // familiarly to anyone who has run one of these before.