Merge branch 'checklevel' of https://github.com/sipa/bitcoin
authorGavin Andresen <gavinandresen@gmail.com>
Thu, 22 Mar 2012 16:18:56 +0000 (12:18 -0400)
committerGavin Andresen <gavinandresen@gmail.com>
Thu, 22 Mar 2012 16:18:56 +0000 (12:18 -0400)
1  2 
src/db.cpp
src/init.cpp

diff --combined src/db.cpp
@@@ -84,11 -84,8 +84,11 @@@ CDB::CDB(const char* pszFile, const cha
              string strErrorFile = strDataDir + "/db.log";
              printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
  
 +            int nDbCache = GetArg("-dbcache", 25);
              dbenv.set_lg_dir(strLogDir.c_str());
 -            dbenv.set_lg_max(10000000);
 +            dbenv.set_cachesize(nDbCache / 1024, (nDbCache % 1024)*1048576, 1);
 +            dbenv.set_lg_bsize(10485760);
 +            dbenv.set_lg_max(104857600);
              dbenv.set_lk_max_locks(10000);
              dbenv.set_lk_max_objects(10000);
              dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
@@@ -159,7 -156,7 +159,7 @@@ void CDB::Close(
          nMinutes = 1;
      if (strFile == "addr.dat")
          nMinutes = 2;
 -    if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
 +    if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 5000 != 0)
          nMinutes = 1;
      dbenv.txn_checkpoint(0, nMinutes, 0);
  
@@@ -583,19 -580,114 +583,114 @@@ bool CTxDB::LoadBlockIndex(
      ReadBestInvalidWork(bnBestInvalidWork);
  
      // Verify blocks in the best chain
+     int nCheckLevel = GetArg("-checklevel", 1);
+     int nCheckDepth = GetArg( "-checkblocks", 2500);
+     if (nCheckDepth == 0)
+         nCheckDepth = 1000000000; // suffices until the year 19000
+     if (nCheckDepth > nBestHeight)
+         nCheckDepth = nBestHeight;
+     printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
      CBlockIndex* pindexFork = NULL;
+     map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
      for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
      {
-         if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
+         if (pindex->nHeight < nBestHeight-nCheckDepth)
              break;
          CBlock block;
          if (!block.ReadFromDisk(pindex))
              return error("LoadBlockIndex() : block.ReadFromDisk failed");
-         if (!block.CheckBlock())
+         // check level 1: verify block validity
+         if (nCheckLevel>0 && !block.CheckBlock())
          {
              printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
              pindexFork = pindex->pprev;
          }
+         // check level 2: verify transaction index validity
+         if (nCheckLevel>1)
+         {
+             pair<unsigned int, unsigned int> pos = make_pair(pindex->nFile, pindex->nBlockPos);
+             mapBlockPos[pos] = pindex;
+             BOOST_FOREACH(const CTransaction &tx, block.vtx)
+             {
+                 uint256 hashTx = tx.GetHash();
+                 CTxIndex txindex;
+                 if (ReadTxIndex(hashTx, txindex))
+                 {
+                     // check level 3: checker transaction hashes
+                     if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos)
+                     {
+                         // either an error or a duplicate transaction
+                         CTransaction txFound;
+                         if (!txFound.ReadFromDisk(txindex.pos))
+                         {
+                             printf("LoadBlockIndex() : *** cannot read mislocated transaction %s\n", hashTx.ToString().c_str());
+                             pindexFork = pindex->pprev;
+                         }
+                         else
+                             if (txFound.GetHash() != hashTx) // not a duplicate tx
+                             {
+                                 printf("LoadBlockIndex(): *** invalid tx position for %s\n", hashTx.ToString().c_str());
+                                 pindexFork = pindex->pprev;
+                             }
+                     }
+                     // check level 4: check whether spent txouts were spent within the main chain
+                     int nOutput = 0;
+                     if (nCheckLevel>3)
+                         BOOST_FOREACH(const CDiskTxPos &txpos, txindex.vSpent)
+                         {
+                             if (!txpos.IsNull())
+                             {
+                                 pair<unsigned int, unsigned int> posFind = make_pair(txpos.nFile, txpos.nBlockPos);
+                                 if (!mapBlockPos.count(posFind))
+                                 {
+                                     printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str());
+                                     pindexFork = pindex->pprev;
+                                 }
+                                 // check level 6: check whether spent txouts were spent by a valid transaction that consume them
+                                 if (nCheckLevel>5)
+                                 {
+                                     CTransaction txSpend;
+                                     if (!txSpend.ReadFromDisk(txpos))
+                                     {
+                                         printf("LoadBlockIndex(): *** cannot read spending transaction of %s:%i from disk\n", hashTx.ToString().c_str(), nOutput);
+                                         pindexFork = pindex->pprev;
+                                     }
+                                     else if (!txSpend.CheckTransaction())
+                                     {
+                                         printf("LoadBlockIndex(): *** spending transaction of %s:%i is invalid\n", hashTx.ToString().c_str(), nOutput);
+                                         pindexFork = pindex->pprev;
+                                     }
+                                     else
+                                     {
+                                         bool fFound = false;
+                                         BOOST_FOREACH(const CTxIn &txin, txSpend.vin)
+                                             if (txin.prevout.hash == hashTx && txin.prevout.n == nOutput)
+                                                 fFound = true;
+                                         if (!fFound)
+                                         {
+                                             printf("LoadBlockIndex(): *** spending transaction of %s:%i does not spend it\n", hashTx.ToString().c_str(), nOutput);
+                                             pindexFork = pindex->pprev;
+                                         }
+                                     }
+                                 }
+                             }
+                             nOutput++;
+                         }
+                 }
+                 // check level 5: check whether all prevouts are marked spent
+                 if (nCheckLevel>4)
+                      BOOST_FOREACH(const CTxIn &txin, tx.vin)
+                      {
+                           CTxIndex txindex;
+                           if (ReadTxIndex(txin.prevout.hash, txindex))
+                               if (txindex.vSpent.size()-1 < txin.prevout.n || txindex.vSpent[txin.prevout.n].IsNull())
+                               {
+                                   printf("LoadBlockIndex(): *** found unspent prevout %s:%i in %s\n", txin.prevout.hash.ToString().c_str(), txin.prevout.n, hashTx.ToString().c_str());
+                                   pindexFork = pindex->pprev;
+                               }
+                      }
+             }
+         }
      }
      if (pindexFork)
      {
@@@ -624,65 -716,44 +719,65 @@@ bool CAddrDB::WriteAddress(const CAddre
      return Write(make_pair(string("addr"), addr.GetKey()), addr);
  }
  
 +bool CAddrDB::WriteAddrman(const CAddrMan& addrman)
 +{
 +    return Write(string("addrman"), addrman);
 +}
 +
  bool CAddrDB::EraseAddress(const CAddress& addr)
  {
      return Erase(make_pair(string("addr"), addr.GetKey()));
  }
  
 -bool CAddrDB::LoadAddresses()
 +bool CAddrDB::LoadAddresses(bool &fUpdate)
  {
 -    CRITICAL_BLOCK(cs_mapAddresses)
 +    bool fAddrMan = false;
 +    if (Read(string("addrman"), addrman))
      {
 -        // Get cursor
 -        Dbc* pcursor = GetCursor();
 -        if (!pcursor)
 +        printf("Loaded %i addresses\n", addrman.size());
 +        fAddrMan = true;
 +    }
 +
 +    vector<CAddress> vAddr;
 +
 +    // Get cursor
 +    Dbc* pcursor = GetCursor();
 +    if (!pcursor)
 +        return false;
 +
 +    loop
 +    {
 +        // Read next record
 +        CDataStream ssKey;
 +        CDataStream ssValue;
 +        int ret = ReadAtCursor(pcursor, ssKey, ssValue);
 +        if (ret == DB_NOTFOUND)
 +            break;
 +        else if (ret != 0)
              return false;
  
 -        loop
 +        // Unserialize
 +        string strType;
 +        ssKey >> strType;
 +        if (strType == "addr")
          {
 -            // Read next record
 -            CDataStream ssKey;
 -            CDataStream ssValue;
 -            int ret = ReadAtCursor(pcursor, ssKey, ssValue);
 -            if (ret == DB_NOTFOUND)
 -                break;
 -            else if (ret != 0)
 -                return false;
 -
 -            // Unserialize
 -            string strType;
 -            ssKey >> strType;
 -            if (strType == "addr")
 +            if (fAddrMan)
 +                fUpdate = true;
 +            else
              {
                  CAddress addr;
                  ssValue >> addr;
 -                mapAddresses.insert(make_pair(addr.GetKey(), addr));
 +                vAddr.push_back(addr);
              }
 +
          }
 -        pcursor->close();
 +    }
 +    pcursor->close();
  
 -        printf("Loaded %d addresses\n", mapAddresses.size());
 +    if (!fAddrMan)
 +    {
 +        addrman.Add(vAddr, CNetAddr("0.0.0.0"));
 +        printf("Loaded %i addresses\n", addrman.size());
      }
  
      return true;
  
  bool LoadAddresses()
  {
 -    return CAddrDB("cr+").LoadAddresses();
 +    bool fUpdate = false;
 +    bool fRet = CAddrDB("cr+").LoadAddresses(fUpdate);
 +    if (fUpdate)
 +        CDB::Rewrite("addr.dat", "\004addr");
 +    return fRet;
  }
  
  
@@@ -799,14 -866,6 +894,14 @@@ int CWalletDB::LoadWallet(CWallet* pwal
      //// todo: shouldn't we catch exceptions and try to recover and continue?
      CRITICAL_BLOCK(pwallet->cs_wallet)
      {
 +        int nMinVersion = 0;
 +        if (Read((string)"minversion", nMinVersion))
 +        {
 +            if (nMinVersion > CLIENT_VERSION)
 +                return DB_TOO_NEW;
 +            pwallet->LoadMinVersion(nMinVersion);
 +        }
 +
          // Get cursor
          Dbc* pcursor = GetCursor();
          if (!pcursor)
                  if (nFileVersion == 10300)
                      nFileVersion = 300;
              }
 -            else if (strType == "minversion")
 -            {
 -                int nMinVersion = 0;
 -                ssValue >> nMinVersion;
 -                if (nMinVersion > CLIENT_VERSION)
 -                    return DB_TOO_NEW;
 -                pwallet->LoadMinVersion(nMinVersion);
 -            }
              else if (strType == "cscript")
              {
                  uint160 hash;
diff --combined src/init.cpp
@@@ -177,7 -177,6 +177,7 @@@ bool AppInit2(int argc, char* argv[]
              "  -min             \t\t  " + _("Start minimized") + "\n" +
              "  -splash          \t\t  " + _("Show splash screen on startup (default: 1)") + "\n" +
              "  -datadir=<dir>   \t\t  " + _("Specify data directory") + "\n" +
 +            "  -dbcache=<n>     \t\t  " + _("Set database cache size in megabytes (default: 25)") + "\n" +
              "  -timeout=<n>     \t  "   + _("Specify connection timeout (in milliseconds)") + "\n" +
              "  -proxy=<ip:port> \t  "   + _("Connect through socks4 proxy") + "\n" +
              "  -dns             \t  "   + _("Allow DNS lookups for addnode and connect") + "\n" +
              "  -rpcconnect=<ip> \t  "   + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n" +
              "  -blocknotify=<cmd> "     + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n" +
              "  -keypool=<n>     \t  "   + _("Set key pool size to <n> (default: 100)") + "\n" +
-             "  -rescan          \t  "   + _("Rescan the block chain for missing wallet transactions") + "\n";
+             "  -rescan          \t  "   + _("Rescan the block chain for missing wallet transactions") + "\n" +
+             "  -checkblocks=<n> \t\t  " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
+             "  -checklevel=<n>  \t\t  " + _("How thorough the block verification is (0-6, default: 1)") + "\n";
  
  #ifdef USE_SSL
          strUsage += string() +
      fAllowDNS = GetBoolArg("-dns");
      fNoListen = !GetBoolArg("-listen", true);
  
 -    // This code can be removed once a super-majority of the network has upgraded.
 -    if (GetBoolArg("-bip16", true))
 -    {
 -        if (fTestNet)
 -            SoftSetArg("-paytoscripthashtime", "1329264000"); // Feb 15
 -        else
 -            SoftSetArg("-paytoscripthashtime", "1333238400"); // April 1 2012
 -
 -        // Put "/P2SH/" in the coinbase so everybody can tell when
 -        // a majority of miners support it
 -        const char* pszP2SH = "/P2SH/";
 -        COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
 -    }
 -    else
 -    {
 -        const char* pszP2SH = "NOP2SH";
 -        COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
 -    }
 +    // Continue to put "/P2SH/" in the coinbase to monitor
 +    // BIP16 support.
 +    // This can be removed eventually...
 +    const char* pszP2SH = "/P2SH/";
 +    COINBASE_FLAGS << std::vector<unsigned char>(pszP2SH, pszP2SH+strlen(pszP2SH));
  
      if (!fNoListen)
      {
              CAddress addr(CService(strAddr, GetDefaultPort(), fAllowDNS));
              addr.nTime = 0; // so it won't relay unless successfully connected
              if (addr.IsValid())
 -                AddAddress(addr);
 +                addrman.Add(addr, CNetAddr("127.0.0.1"));
          }
      }