Merge branch 'addrman' of https://github.com/sipa/bitcoin
authorGavin Andresen <gavinandresen@gmail.com>
Thu, 22 Mar 2012 13:19:01 +0000 (09:19 -0400)
committerGavin Andresen <gavinandresen@gmail.com>
Thu, 22 Mar 2012 13:19:01 +0000 (09:19 -0400)
1  2 
bitcoin-qt.pro
src/db.cpp
src/db.h
src/init.cpp
src/main.cpp
src/makefile.unix
src/net.cpp
src/net.h

diff --combined bitcoin-qt.pro
@@@ -101,6 -101,7 +101,7 @@@ HEADERS += src/qt/bitcoingui.h 
      src/qt/aboutdialog.h \
      src/qt/editaddressdialog.h \
      src/qt/bitcoinaddressvalidator.h \
+     src/addrman.h \
      src/base58.h \
      src/bignum.h \
      src/checkpoints.h \
      src/init.h \
      src/headers.h \
      src/irc.h \
 +    src/mruset.h \
      src/json/json_spirit_writer_template.h \
      src/json/json_spirit_writer.h \
      src/json/json_spirit_value.h \
@@@ -175,6 -175,7 +176,7 @@@ SOURCES += src/qt/bitcoin.cpp src/qt/bi
      src/net.cpp \
      src/irc.cpp \
      src/checkpoints.cpp \
+     src/addrman.cpp \
      src/db.cpp \
      src/json/json_spirit_writer.cpp \
      src/json/json_spirit_value.cpp \
@@@ -244,7 -245,7 +246,7 @@@ CODECFORTR = UTF-
  TRANSLATIONS = $$files(src/qt/locale/bitcoin_*.ts)
  
  isEmpty(QMAKE_LRELEASE) {
 -    win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\lrelease.exe
 +    win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
      else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
  }
  isEmpty(TS_DIR):TS_DIR = src/qt/locale
@@@ -291,13 -292,12 +293,13 @@@ isEmpty(BOOST_INCLUDE_PATH) 
      macx:BOOST_INCLUDE_PATH = /opt/local/include
  }
  
 -windows:LIBS += -lws2_32 -lshlwapi
 -windows:DEFINES += WIN32
 +windows:LIBS += -lmingwthrd -lws2_32 -lshlwapi
 +windows:DEFINES += _MT WIN32
  windows:RC_FILE = src/qt/res/bitcoin-qt.rc
  
  !windows:!mac {
      DEFINES += LINUX
 +    LIBS += -lrt
  }
  
  macx:HEADERS += src/qt/macdockiconhandler.h
@@@ -312,7 -312,7 +314,7 @@@ INCLUDEPATH += $$BOOST_INCLUDE_PATH $$B
  LIBS += $$join(BOOST_LIB_PATH,,-L,) $$join(BDB_LIB_PATH,,-L,) $$join(OPENSSL_LIB_PATH,,-L,) $$join(QRENCODE_LIB_PATH,,-L,)
  LIBS += -lssl -lcrypto -ldb_cxx$$BDB_LIB_SUFFIX
  # -lgdi32 has to happen after -lcrypto (see  #681)
 -windows:LIBS += -lgdi32
 +windows:LIBS += -lole32 -luuid -lgdi32
  LIBS += -lboost_system$$BOOST_LIB_SUFFIX -lboost_filesystem$$BOOST_LIB_SUFFIX -lboost_program_options$$BOOST_LIB_SUFFIX -lboost_thread$$BOOST_THREAD_LIB_SUFFIX
  
  contains(RELEASE, 1) {
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);
  
@@@ -624,44 -621,65 +624,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;
  }
  
  
@@@ -771,16 -793,20 +796,16 @@@ int CWalletDB::LoadWallet(CWallet* pwal
      vector<uint256> vWalletUpgrade;
      bool fIsEncrypted = false;
  
 -    // Modify defaults
 -#ifndef WIN32
 -    // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
 -    fMinimizeToTray = false;
 -    fMinimizeOnClose = false;
 -#endif
 -
      //// todo: shouldn't we catch exceptions and try to recover and continue?
      CRITICAL_BLOCK(pwallet->cs_wallet)
      {
          // Get cursor
          Dbc* pcursor = GetCursor();
          if (!pcursor)
 +        {
 +            printf("Error getting wallet database cursor\n");
              return DB_CORRUPT;
 +        }
  
          loop
          {
              if (ret == DB_NOTFOUND)
                  break;
              else if (ret != 0)
 +            {
 +                printf("Error reading next record from wallet database\n");
                  return DB_CORRUPT;
 +            }
  
              // Unserialize
              // Taking advantage of the fact that pair serialization
                      ssValue >> pkey;
                      key.SetPubKey(vchPubKey);
                      key.SetPrivKey(pkey);
 -                    if (key.GetPubKey() != vchPubKey || !key.IsValid())
 +                    if (key.GetPubKey() != vchPubKey)
 +                    {
 +                        printf("Error reading wallet database: CPrivKey pubkey inconsistency\n");
                          return DB_CORRUPT;
 +                    }
 +                    if (!key.IsValid())
 +                    {
 +                        printf("Error reading wallet database: invalid CPrivKey\n");
 +                        return DB_CORRUPT;
 +                    }
                  }
                  else
                  {
                      ssValue >> wkey;
                      key.SetPubKey(vchPubKey);
                      key.SetPrivKey(wkey.vchPrivKey);
 -                    if (key.GetPubKey() != vchPubKey || !key.IsValid())
 +                    if (key.GetPubKey() != vchPubKey)
 +                    {
 +                        printf("Error reading wallet database: CWalletKey pubkey inconsistency\n");
                          return DB_CORRUPT;
 +                    }
 +                    if (!key.IsValid())
 +                    {
 +                        printf("Error reading wallet database: invalid CWalletKey\n");
 +                        return DB_CORRUPT;
 +                    }
                  }
                  if (!pwallet->LoadKey(key))
 +                {
 +                    printf("Error reading wallet database: LoadKey failed\n");
                      return DB_CORRUPT;
 +                }
              }
              else if (strType == "mkey")
              {
                  CMasterKey kMasterKey;
                  ssValue >> kMasterKey;
                  if(pwallet->mapMasterKeys.count(nID) != 0)
 +                {
 +                    printf("Error reading wallet database: duplicate CMasterKey id %u\n", nID);
                      return DB_CORRUPT;
 +                }
                  pwallet->mapMasterKeys[nID] = kMasterKey;
                  if (pwallet->nMasterKeyMaxID < nID)
                      pwallet->nMasterKeyMaxID = nID;
                  vector<unsigned char> vchPrivKey;
                  ssValue >> vchPrivKey;
                  if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
 +                {
 +                    printf("Error reading wallet database: LoadCryptedKey failed\n");
                      return DB_CORRUPT;
 +                }
                  fIsEncrypted = true;
              }
              else if (strType == "defaultkey")
                  if (nFileVersion == 10300)
                      nFileVersion = 300;
              }
 -            else if (strType == "setting")
 -            {
 -                string strKey;
 -                ssKey >> strKey;
 -
 -                // Options
 -#ifndef QT_GUI
 -                if (strKey == "fGenerateBitcoins")  ssValue >> fGenerateBitcoins;
 -#endif
 -                if (strKey == "nTransactionFee")    ssValue >> nTransactionFee;
 -                if (strKey == "fLimitProcessors")   ssValue >> fLimitProcessors;
 -                if (strKey == "nLimitProcessors")   ssValue >> nLimitProcessors;
 -                if (strKey == "fMinimizeToTray")    ssValue >> fMinimizeToTray;
 -                if (strKey == "fMinimizeOnClose")   ssValue >> fMinimizeOnClose;
 -                if (strKey == "fUseProxy")          ssValue >> fUseProxy;
 -                if (strKey == "addrProxy")          ssValue >> addrProxy;
 -                if (fHaveUPnP && strKey == "fUseUPnP")           ssValue >> fUseUPnP;
 -            }
              else if (strType == "minversion")
              {
                  int nMinVersion = 0;
                  CScript script;
                  ssValue >> script;
                  if (!pwallet->LoadCScript(script))
 +                {
 +                    printf("Error reading wallet database: LoadCScript failed\n");
                      return DB_CORRUPT;
 +                }
              }
          }
          pcursor->close();
          WriteTx(hash, pwallet->mapWallet[hash]);
  
      printf("nFileVersion = %d\n", nFileVersion);
 -    printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
 -    printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
 -    printf("fMinimizeToTray = %d\n", fMinimizeToTray);
 -    printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
 -    printf("fUseProxy = %d\n", fUseProxy);
 -    printf("addrProxy = %s\n", addrProxy.ToString().c_str());
 -    if (fHaveUPnP)
 -        printf("fUseUPnP = %d\n", fUseUPnP);
  
  
      // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
diff --combined src/db.h
+++ b/src/db.h
@@@ -16,6 -16,7 +16,7 @@@
  class CAccount;
  class CAccountingEntry;
  class CAddress;
+ class CAddrMan;
  class CBlockLocator;
  class CDiskBlockIndex;
  class CDiskTxPos;
@@@ -307,7 -308,8 +308,8 @@@ private
  public:
      bool WriteAddress(const CAddress& addr);
      bool EraseAddress(const CAddress& addr);
-     bool LoadAddresses();
+     bool WriteAddrman(const CAddrMan& addr);
+     bool LoadAddresses(bool &fUpdate);
  };
  
  bool LoadAddresses();
@@@ -473,24 -475,18 +475,24 @@@ public
          return Erase(std::make_pair(std::string("pool"), nPool));
      }
  
 +    // Settings are no longer stored in wallet.dat; these are
 +    // used only for backwards compatibility:
      template<typename T>
      bool ReadSetting(const std::string& strKey, T& value)
      {
          return Read(std::make_pair(std::string("setting"), strKey), value);
      }
 -
      template<typename T>
      bool WriteSetting(const std::string& strKey, const T& value)
      {
          nWalletDBUpdated++;
          return Write(std::make_pair(std::string("setting"), strKey), value);
      }
 +    bool EraseSetting(const std::string& strKey)
 +    {
 +        nWalletDBUpdated++;
 +        return Erase(std::make_pair(std::string("setting"), strKey));
 +    }
  
      bool WriteMinVersion(int nVersion)
      {
diff --combined src/init.cpp
@@@ -150,15 -150,27 +150,15 @@@ bool AppInit2(int argc, char* argv[]
      //
      // Parameters
      //
 -    // If Qt is used, parameters are parsed in qt/bitcoin.cpp's main()
 +    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
  #if !defined(QT_GUI)
      ParseParameters(argc, argv);
 -#endif
 -
 -    if (mapArgs.count("-datadir"))
 +    if (!ReadConfigFile(mapArgs, mapMultiArgs))
      {
 -        if (filesystem::is_directory(filesystem::system_complete(mapArgs["-datadir"])))
 -        {
 -            filesystem::path pathDataDir = filesystem::system_complete(mapArgs["-datadir"]);
 -            strlcpy(pszSetDataDir, pathDataDir.string().c_str(), sizeof(pszSetDataDir));
 -        }
 -        else
 -        {
 -            fprintf(stderr, "Error: Specified directory does not exist\n");
 -            Shutdown(NULL);
 -        }
 +        fprintf(stderr, "Error: Specified directory does not exist\n");
 +        Shutdown(NULL);
      }
 -
 -
 -    ReadConfigFile(mapArgs, mapMultiArgs); // Must be done after processing datadir
 +#endif
  
      if (mapArgs.count("-?") || mapArgs.count("--help"))
      {
              "  -gen             \t\t  " + _("Generate coins") + "\n" +
              "  -gen=0           \t\t  " + _("Don't generate coins") + "\n" +
              "  -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" +
          else if (nLoadWalletRet == DB_NEED_REWRITE)
          {
              strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n";
 +            printf("%s", strErrors.str().c_str());
              wxMessageBox(strErrors.str(), "Bitcoin", wxOK | wxICON_ERROR);
              return false;
          }
          else
              strErrors << _("Error loading wallet.dat") << "\n";
      }
 +    printf("%s", strErrors.str().c_str());
      printf(" wallet      %15"PRI64d"ms\n", GetTimeMillis() - nStart);
  
      RegisterWallet(pwalletMain);
          return false;
      }
  
 -    fGenerateBitcoins = GetBoolArg("-gen");
 -
      if (mapArgs.count("-proxy"))
      {
          fUseProxy = true;
      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", "1330578000"); // Mar 1
 -
 -        // 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));
 -    }
 -
 -    // Command-line args override in-wallet settings:
 -#if USE_UPNP
 -    fUseUPnP = GetBoolArg("-upnp", true);
 -#else
 -    fUseUPnP = GetBoolArg("-upnp", false);
 -#endif
 +    // 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"));
          }
      }
  
diff --combined src/main.cpp
@@@ -57,12 -57,22 +57,12 @@@ CScript COINBASE_FLAGS
  
  const string strMessageMagic = "Bitcoin Signed Message:\n";
  
 -
  double dHashesPerSec;
  int64 nHPSTimerStart;
  
  // Settings
 -int fGenerateBitcoins = false;
  int64 nTransactionFee = 0;
 -int fLimitProcessors = false;
 -int nLimitProcessors = 1;
 -int fMinimizeToTray = true;
 -int fMinimizeOnClose = true;
 -#if USE_UPNP
 -int fUseUPnP = true;
 -#else
 -int fUseUPnP = false;
 -#endif
 +
  
  
  //////////////////////////////////////////////////////////////////////////////
@@@ -167,14 -177,13 +167,14 @@@ void static ResendWalletTransactions(
  // mapOrphanTransactions
  //
  
 -void static AddOrphanTx(const CDataStream& vMsg)
 +void AddOrphanTx(const CDataStream& vMsg)
  {
      CTransaction tx;
      CDataStream(vMsg) >> tx;
      uint256 hash = tx.GetHash();
      if (mapOrphanTransactions.count(hash))
          return;
 +
      CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg);
      BOOST_FOREACH(const CTxIn& txin, tx.vin)
          mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg));
@@@ -202,23 -211,6 +202,23 @@@ void static EraseOrphanTx(uint256 hash
      mapOrphanTransactions.erase(hash);
  }
  
 +int LimitOrphanTxSize(int nMaxOrphans)
 +{
 +    int nEvicted = 0;
 +    while (mapOrphanTransactions.size() > nMaxOrphans)
 +    {
 +        // Evict a random orphan:
 +        std::vector<unsigned char> randbytes(32);
 +        RAND_bytes(&randbytes[0], 32);
 +        uint256 randomhash(randbytes);
 +        map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
 +        if (it == mapOrphanTransactions.end())
 +            it = mapOrphanTransactions.begin();
 +        EraseOrphanTx(it->first);
 +        ++nEvicted;
 +    }
 +    return nEvicted;
 +}
  
  
  
@@@ -976,10 -968,8 +976,10 @@@ bool CTransaction::DisconnectInputs(CTx
      }
  
      // Remove transaction from index
 -    if (!txdb.EraseTxIndex(*this))
 -        return error("DisconnectInputs() : EraseTxPos failed");
 +    // This can fail if a duplicate of this transaction was in a chain that got
 +    // reorganized away. This is only possible if this transaction was completely
 +    // spent, so erasing it would be a no-op anway.
 +    txdb.EraseTxIndex(*this);
  
      return true;
  }
@@@ -1148,14 -1138,7 +1148,14 @@@ bool CTransaction::ConnectInputs(MapPre
              {
                  // Verify signature
                  if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
 +                {
 +                    // only during transition phase for P2SH: do not invoke anti-DoS code for
 +                    // potentially old clients relaying bad P2SH transactions
 +                    if (fStrictPayToScriptHash && VerifySignature(txPrev, *this, i, false, 0))
 +                        return error("ConnectInputs() : %s P2SH VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
 +
                      return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
 +                }
              }
  
              // Mark outpoints as spent
@@@ -1258,29 -1241,12 +1258,29 @@@ bool CBlock::ConnectBlock(CTxDB& txdb, 
      if (!CheckBlock())
          return false;
  
 -    // To avoid being on the short end of a block-chain split,
 -    // don't do secondary validation of pay-to-script-hash transactions
 -    // until blocks with timestamps after paytoscripthashtime (see init.cpp for default).
 -    // This code can be removed once a super-majority of the network has upgraded.
 -    int64 nEvalSwitchTime = GetArg("-paytoscripthashtime", std::numeric_limits<int64_t>::max());
 -    bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime);
 +    // 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
 +    // can be duplicated to remove the ability to spend the first instance -- even after
 +    // being sent to another address.
 +    // See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
 +    // This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
 +    // already refuses previously-known transaction id's entirely.
 +    // This rule applies to all blocks whose timestamp is after March 15, 2012, 0:00 UTC.
 +    // On testnet it is enabled as of februari 20, 2012, 0:00 UTC.
 +    if (pindex->nTime > 1331769600 || (fTestNet && pindex->nTime > 1329696000))
 +        BOOST_FOREACH(CTransaction& tx, vtx)
 +        {
 +            CTxIndex txindexOld;
 +            if (txdb.ReadTxIndex(tx.GetHash(), txindexOld))
 +                BOOST_FOREACH(CDiskTxPos &pos, txindexOld.vSpent)
 +                    if (pos.IsNull())
 +                        return false;
 +        }
 +
 +    // BIP16 didn't become active until Apr 1 2012 (Feb 15 on testnet)
 +    int64 nBIP16SwitchTime = fTestNet ? 1329264000 : 1333238400;
 +    bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime);
  
      //// issue here: it doesn't know the version
      unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size());
@@@ -1379,9 -1345,6 +1379,9 @@@ bool static Reorganize(CTxDB& txdb, CBl
          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;
  }
@@@ -1456,31 -1420,6 +1456,31 @@@ runCommand(std::string strCommand
          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)
@@@ -1705,11 -1626,10 +1705,11 @@@ bool CBlock::AcceptBlock(
          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;
@@@ -2187,7 -2107,7 +2187,7 @@@ bool static ProcessMessage(CNode* pfrom
          vRecv >> pfrom->nVersion >> pfrom->nServices >> nTime >> addrMe;
          if (pfrom->nVersion < 209)
          {
 -            // Since Februari 20, 2012, the protocol is initiated at version 209,
 +            // Since February 20, 2012, the protocol is initiated at version 209,
              // and earlier versions are no longer supported
              printf("partner %s using obsolete version %i; disconnecting\n", pfrom->addr.ToString().c_str(), pfrom->nVersion);
              pfrom->fDisconnect = true;
              }
  
              // Get recent addresses
-             if (pfrom->nVersion >= 31402 || mapAddresses.size() < 1000)
+             if (pfrom->nVersion >= 31402 || addrman.size() < 1000)
              {
                  pfrom->PushMessage("getaddr");
                  pfrom->fGetAddr = true;
              }
+             addrman.Good(pfrom->addr);
+         } else {
+             if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom)
+             {
+                 addrman.Add(addrFrom, addrFrom);
+                 addrman.Good(addrFrom);
+             }
          }
  
          // Ask the first connected node for block updates
          vRecv >> vAddr;
  
          // Don't want addr from older versions unless seeding
-         if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000)
+         if (pfrom->nVersion < 31402 && addrman.size() > 1000)
              return true;
          if (vAddr.size() > 1000)
          {
          }
  
          // Store the new addresses
-         CAddrDB addrDB;
-         addrDB.TxnBegin();
          int64 nNow = GetAdjustedTime();
          int64 nSince = nNow - 10 * 60;
          BOOST_FOREACH(CAddress& addr, vAddr)
                  continue;
              if (addr.nTime <= 100000000 || addr.nTime > nNow + 10 * 60)
                  addr.nTime = nNow - 5 * 24 * 60 * 60;
-             AddAddress(addr, 2 * 60 * 60, &addrDB);
              pfrom->AddAddressKnown(addr);
              if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable())
              {
                  }
              }
          }
-         addrDB.TxnCommit();  // Save addresses (it's ok if this fails)
+         addrman.Add(vAddr, pfrom->addr, 2 * 60 * 60);
          if (vAddr.size() < 1000)
              pfrom->fGetAddr = false;
      }
          }
  
          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
          {
              printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
              AddOrphanTx(vMsg);
 +
 +            // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
 +            int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
 +            if (nEvicted > 0)
 +                printf("mapOrphan overflow, removed %d tx\n", nEvicted);
          }
          if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
      }
  
      else if (strCommand == "getaddr")
      {
-         // Nodes rebroadcast an addr every 24 hours
          pfrom->vAddrToSend.clear();
-         int64 nSince = GetAdjustedTime() - 3 * 60 * 60; // in the last 3 hours
-         CRITICAL_BLOCK(cs_mapAddresses)
-         {
-             unsigned int nCount = 0;
-             BOOST_FOREACH(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
-             {
-                 const CAddress& addr = item.second;
-                 if (addr.nTime > nSince)
-                     nCount++;
-             }
-             BOOST_FOREACH(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
-             {
-                 const CAddress& addr = item.second;
-                 if (addr.nTime > nSince && GetRand(nCount) < 2500)
-                     pfrom->PushAddress(addr);
-             }
-         }
+         vector<CAddress> vAddr = addrman.GetAddr();
+         BOOST_FOREACH(const CAddress &addr, vAddr)
+             pfrom->PushAddress(addr);
      }
  
  
@@@ -2850,35 -2749,6 +2839,6 @@@ bool SendMessages(CNode* pto, bool fSen
              nLastRebroadcast = GetTime();
          }
  
-         // Clear out old addresses periodically so it's not too much work at once
-         static int64 nLastClear;
-         if (nLastClear == 0)
-             nLastClear = GetTime();
-         if (GetTime() - nLastClear > 10 * 60 && vNodes.size() >= 3)
-         {
-             nLastClear = GetTime();
-             CRITICAL_BLOCK(cs_mapAddresses)
-             {
-                 CAddrDB addrdb;
-                 int64 nSince = GetAdjustedTime() - 14 * 24 * 60 * 60;
-                 for (map<vector<unsigned char>, CAddress>::iterator mi = mapAddresses.begin();
-                      mi != mapAddresses.end();)
-                 {
-                     const CAddress& addr = (*mi).second;
-                     if (addr.nTime < nSince)
-                     {
-                         if (mapAddresses.size() < 1000 || GetTime() > nLastClear + 20)
-                             break;
-                         addrdb.EraseAddress(addr);
-                         mapAddresses.erase(mi++);
-                     }
-                     else
-                         mi++;
-                 }
-             }
-         }
          //
          // Message: addr
          //
@@@ -3379,10 -3249,6 +3339,10 @@@ bool CheckWork(CBlock* pblock, CWallet
  
  void static ThreadBitcoinMiner(void* parg);
  
 +static bool fGenerateBitcoins = false;
 +static bool fLimitProcessors = false;
 +static int nLimitProcessors = -1;
 +
  void static BitcoinMiner(CWallet *pwallet)
  {
      printf("BitcoinMiner started\n");
@@@ -3558,13 -3424,13 +3518,13 @@@ void static ThreadBitcoinMiner(void* pa
  
  void GenerateBitcoins(bool fGenerate, CWallet* pwallet)
  {
 -    if (fGenerateBitcoins != fGenerate)
 -    {
 -        fGenerateBitcoins = fGenerate;
 -        WriteSetting("fGenerateBitcoins", fGenerateBitcoins);
 -        MainFrameRepaint();
 -    }
 -    if (fGenerateBitcoins)
 +    fGenerateBitcoins = fGenerate;
 +    nLimitProcessors = GetArg("-genproclimit", -1);
 +    if (nLimitProcessors == 0)
 +        fGenerateBitcoins = false;
 +    fLimitProcessors = (nLimitProcessors != -1);
 +
 +    if (fGenerate)
      {
          int nProcessors = boost::thread::hardware_concurrency();
          printf("%d processors\n", nProcessors);
diff --combined src/makefile.unix
@@@ -85,12 -85,13 +85,13 @@@ LIBS+= 
  
  DEBUGFLAGS=-g
  CXXFLAGS=-O2
 -xCXXFLAGS=-pthread -Wextra -Wno-sign-compare -Wno-char-subscripts -Wno-invalid-offsetof -Wformat-security \
 +xCXXFLAGS=-pthread -Wextra -Wno-sign-compare -Wno-char-subscripts -Wno-invalid-offsetof -Wformat -Wformat-security \
      $(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS)
  
  OBJS= \
      obj/checkpoints.o \
      obj/netbase.o \
+     obj/addrman.o \
      obj/crypter.o \
      obj/key.o \
      obj/db.o \
diff --combined src/net.cpp
@@@ -9,6 -9,7 +9,7 @@@
  #include "net.h"
  #include "init.h"
  #include "strlcpy.h"
+ #include "addrman.h"
  
  #ifdef WIN32
  #include <string.h>
@@@ -43,18 -44,16 +44,17 @@@ bool OpenNetworkConnection(const CAddre
  //
  bool fClient = false;
  bool fAllowDNS = false;
 +static bool fUseUPnP = false;
  uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
  CAddress addrLocalHost(CService("0.0.0.0", 0), nLocalServices);
  static CNode* pnodeLocalHost = NULL;
  uint64 nLocalHostNonce = 0;
  array<int, THREAD_MAX> vnThreadsRunning;
  static SOCKET hListenSocket = INVALID_SOCKET;
+ CAddrMan addrman;
  
  vector<CNode*> vNodes;
  CCriticalSection cs_vNodes;
- map<vector<unsigned char>, CAddress> mapAddresses;
- CCriticalSection cs_mapAddresses;
  map<CInv, CDataStream> mapRelay;
  deque<pair<int64, CInv> > vRelayExpiration;
  CCriticalSection cs_mapRelay;
@@@ -280,86 -279,9 +280,9 @@@ void ThreadGetMyExternalIP(void* parg
  
  
  
- bool AddAddress(CAddress addr, int64 nTimePenalty, CAddrDB *pAddrDB)
- {
-     if (!addr.IsRoutable())
-         return false;
-     if ((CService)addr == (CService)addrLocalHost)
-         return false;
-     addr.nTime = max((int64)0, (int64)addr.nTime - nTimePenalty);
-     bool fUpdated = false;
-     bool fNew = false;
-     CAddress addrFound = addr;
-     CRITICAL_BLOCK(cs_mapAddresses)
-     {
-         map<vector<unsigned char>, CAddress>::iterator it = mapAddresses.find(addr.GetKey());
-         if (it == mapAddresses.end())
-         {
-             // New address
-             printf("AddAddress(%s)\n", addr.ToString().c_str());
-             mapAddresses.insert(make_pair(addr.GetKey(), addr));
-             fUpdated = true;
-             fNew = true;
-         }
-         else
-         {
-             addrFound = (*it).second;
-             if ((addrFound.nServices | addr.nServices) != addrFound.nServices)
-             {
-                 // Services have been added
-                 addrFound.nServices |= addr.nServices;
-                 fUpdated = true;
-             }
-             bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
-             int64 nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
-             if (addrFound.nTime < addr.nTime - nUpdateInterval)
-             {
-                 // Periodically update most recently seen time
-                 addrFound.nTime = addr.nTime;
-                 fUpdated = true;
-             }
-         }
-     }
-     // There is a nasty deadlock bug if this is done inside the cs_mapAddresses
-     // CRITICAL_BLOCK:
-     // Thread 1:  begin db transaction (locks inside-db-mutex)
-     //            then AddAddress (locks cs_mapAddresses)
-     // Thread 2:  AddAddress (locks cs_mapAddresses)
-     //             ... then db operation hangs waiting for inside-db-mutex
-     if (fUpdated)
-     {
-         if (pAddrDB)
-             pAddrDB->WriteAddress(addrFound);
-         else
-             CAddrDB().WriteAddress(addrFound);
-     }
-     return fNew;
- }
  void AddressCurrentlyConnected(const CService& addr)
  {
-     CAddress *paddrFound = NULL;
-     CRITICAL_BLOCK(cs_mapAddresses)
-     {
-         // Only if it's been published already
-         map<vector<unsigned char>, CAddress>::iterator it = mapAddresses.find(addr.GetKey());
-         if (it != mapAddresses.end())
-             paddrFound = &(*it).second;
-     }
-     if (paddrFound)
-     {
-         int64 nUpdateInterval = 20 * 60;
-         if (paddrFound->nTime < GetAdjustedTime() - nUpdateInterval)
-         {
-             // Periodically update most recently seen time
-             paddrFound->nTime = GetAdjustedTime();
-             CAddrDB addrdb;
-             addrdb.WriteAddress(*paddrFound);
-         }
-     }
+     addrman.Connected(addr);
  }
  
  
@@@ -506,13 -428,11 +429,11 @@@ CNode* ConnectNode(CAddress addrConnect
      }
  
      /// debug print
-     printf("trying connection %s lastseen=%.1fhrs lasttry=%.1fhrs\n",
+     printf("trying connection %s lastseen=%.1fhrs\n",
          addrConnect.ToString().c_str(),
-         (double)(addrConnect.nTime - GetAdjustedTime())/3600.0,
-         (double)(addrConnect.nLastTry - GetAdjustedTime())/3600.0);
+         (double)(addrConnect.nTime - GetAdjustedTime())/3600.0);
  
-     CRITICAL_BLOCK(cs_mapAddresses)
-         mapAddresses[addrConnect.GetKey()].nLastTry = GetAdjustedTime();
+     addrman.Attempt(addrConnect);
  
      // Connect
      SOCKET hSocket;
@@@ -559,7 -479,6 +480,7 @@@ void CNode::CloseSocketDisconnect(
          printf("disconnecting node %s\n", addr.ToString().c_str());
          closesocket(hSocket);
          hSocket = INVALID_SOCKET;
 +        vRecv.clear();
      }
  }
  
@@@ -814,7 -733,7 +735,7 @@@ void ThreadSocketHandler2(void* parg
              }
              else if (CNode::IsBanned(addr))
              {
 -                printf("connetion from %s dropped (banned)\n", addr.ToString().c_str());
 +                printf("connection from %s dropped (banned)\n", addr.ToString().c_str());
                  closesocket(hSocket);
              }
              else
@@@ -1104,6 -1023,7 +1025,6 @@@ void MapPort(bool fMapPort
      if (fUseUPnP != fMapPort)
      {
          fUseUPnP = fMapPort;
 -        WriteSetting("fUseUPnP", fUseUPnP);
      }
      if (fUseUPnP && vnThreadsRunning[THREAD_UPNP] < 1)
      {
@@@ -1126,12 -1046,15 +1047,15 @@@ void MapPort(bool /* unused fMapPort */
  
  
  
- static const char *strDNSSeed[] = {
-     "bitseed.xf2.org",
-     "dnsseed.bluematt.me",
-     "seed.bitcoin.sipa.be",
-     "dnsseed.bitcoin.dashjr.org",
+ // DNS seeds
+ // Each pair gives a source name and a seed name.
+ // The first name is used as information source for addrman.
+ // The second name should resolve to a list of seed addresses.
+ static const char *strDNSSeed[][2] = {
+     {"xf2.org", "bitseed.xf2.org"},
+     {"bluematt.me", "dnsseed.bluematt.me"},
+     {"bitcoin.sipa.be", "seed.bitcoin.sipa.be"},
+     {"dashjr.org", "dnsseed.bitcoin.dashjr.org"},
  };
  
  void ThreadDNSAddressSeed(void* parg)
@@@ -1164,22 -1087,18 +1088,18 @@@ void ThreadDNSAddressSeed2(void* parg
  
          for (int seed_idx = 0; seed_idx < ARRAYLEN(strDNSSeed); seed_idx++) {
              vector<CNetAddr> vaddr;
-             if (LookupHost(strDNSSeed[seed_idx], vaddr))
+             vector<CAddress> vAdd;
+             if (LookupHost(strDNSSeed[seed_idx][1], vaddr))
              {
-                 CAddrDB addrDB;
-                 addrDB.TxnBegin();
-                 BOOST_FOREACH (CNetAddr& ip, vaddr)
+                 BOOST_FOREACH(CNetAddr& ip, vaddr)
                  {
-                     if (ip.IsRoutable())
-                     {
-                         CAddress addr(CService(ip, GetDefaultPort()), NODE_NETWORK);
-                         addr.nTime = 0;
-                         AddAddress(addr, 0, &addrDB);
-                         found++;
-                     }
+                     CAddress addr = CAddress(CService(ip, GetDefaultPort()));
+                     addr.nTime = 0;
+                     vAdd.push_back(addr);
+                     found++;
                  }
-                 addrDB.TxnCommit();  // Save addresses (it's ok if this fails)
              }
+             addrman.Add(vAdd, CNetAddr(strDNSSeed[seed_idx][0], true));
          }
      }
  
@@@ -1278,7 -1197,37 +1198,37 @@@ unsigned int pnSeed[] 
      0xc461d84a, 0xb2dbe247,
  };
  
+ void DumpAddresses()
+ {
+     CAddrDB adb;
+     adb.WriteAddrman(addrman);
+ }
  
+ void ThreadDumpAddress2(void* parg)
+ {
+     vnThreadsRunning[THREAD_DUMPADDRESS]++;
+     while (!fShutdown)
+     {
+         DumpAddresses();
+         vnThreadsRunning[THREAD_DUMPADDRESS]--;
+         Sleep(100000);
+         vnThreadsRunning[THREAD_DUMPADDRESS]++;
+     }
+     vnThreadsRunning[THREAD_DUMPADDRESS]--;
+ }
+ void ThreadDumpAddress(void* parg)
+ {
+     IMPLEMENT_RANDOMIZE_STACK(ThreadDumpAddress(parg));
+     try
+     {
+         ThreadDumpAddress2(parg);
+     }
+     catch (std::exception& e) {
+         PrintException(&e, "ThreadDumpAddress()");
+     }
+     printf("ThreadDumpAddress exiting\n");
+ }
  
  void ThreadOpenConnections(void* parg)
  {
@@@ -1327,6 -1276,8 +1277,8 @@@ void ThreadOpenConnections2(void* parg
      int64 nStart = GetTime();
      loop
      {
+         int nOutbound = 0;
          vnThreadsRunning[THREAD_OPENCONNECTIONS]--;
          Sleep(500);
          vnThreadsRunning[THREAD_OPENCONNECTIONS]++;
          // Limit outbound connections
          loop
          {
-             int nOutbound = 0;
+             nOutbound = 0;
              CRITICAL_BLOCK(cs_vNodes)
                  BOOST_FOREACH(CNode* pnode, vNodes)
                      if (!pnode->fInbound)
  
          bool fAddSeeds = false;
  
-         CRITICAL_BLOCK(cs_mapAddresses)
-         {
-             // Add seed nodes if IRC isn't working
-             bool fTOR = (fUseProxy && addrProxy.GetPort() == 9050);
-             if (mapAddresses.empty() && (GetTime() - nStart > 60 || fTOR) && !fTestNet)
-                 fAddSeeds = true;
-         }
-         if (fAddSeeds)
+         // Add seed nodes if IRC isn't working
+         bool fTOR = (fUseProxy && addrProxy.GetPort() == 9050);
+         if (addrman.size()==0 && (GetTime() - nStart > 60 || fTOR) && !fTestNet)
          {
+             std::vector<CAddress> vAdd;
              for (int i = 0; i < ARRAYLEN(pnSeed); i++)
              {
                  // It'll only connect to one or two seed nodes because once it connects,
                  memcpy(&ip, &pnSeed[i], sizeof(ip));
                  CAddress addr(CService(ip, GetDefaultPort()));
                  addr.nTime = GetTime()-GetRand(nOneWeek)-nOneWeek;
-                 AddAddress(addr);
+                 vAdd.push_back(addr);
              }
+             addrman.Add(vAdd, CNetAddr("127.0.0.1"));
          }
  
          //
  
          int64 nANow = GetAdjustedTime();
  
-         CRITICAL_BLOCK(cs_mapAddresses)
+         int nTries = 0;
+         loop
          {
-             BOOST_FOREACH(const PAIRTYPE(vector<unsigned char>, CAddress)& item, mapAddresses)
-             {
-                 const CAddress& addr = item.second;
-                 if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.GetGroup()))
-                     continue;
-                 int64 nSinceLastSeen = nANow - addr.nTime;
-                 int64 nSinceLastTry = nANow - addr.nLastTry;
-                 // Randomize the order in a deterministic way, putting the standard port first
-                 int64 nRandomizer = (uint64)(nStart * 4951 + addr.nLastTry * 9567851 + addr.GetHash()) % (2 * 60 * 60);
-                 if (addr.GetPort() != GetDefaultPort())
-                     nRandomizer += 2 * 60 * 60;
-                 // Last seen  Base retry frequency
-                 //   <1 hour   10 min
-                 //    1 hour    1 hour
-                 //    4 hours   2 hours
-                 //   24 hours   5 hours
-                 //   48 hours   7 hours
-                 //    7 days   13 hours
-                 //   30 days   27 hours
-                 //   90 days   46 hours
-                 //  365 days   93 hours
-                 int64 nDelay = (int64)(3600.0 * sqrt(fabs((double)nSinceLastSeen) / 3600.0) + nRandomizer);
-                 // Fast reconnect for one hour after last seen
-                 if (nSinceLastSeen < 60 * 60)
-                     nDelay = 10 * 60;
-                 // Limit retry frequency
-                 if (nSinceLastTry < nDelay)
-                     continue;
+             // use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
+             CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
  
-                 // If we have IRC, we'll be notified when they first come online,
-                 // and again every 24 hours by the refresh broadcast.
-                 if (nGotIRCAddresses > 0 && vNodes.size() >= 2 && nSinceLastSeen > 24 * 60 * 60)
-                     continue;
+             // if we selected an invalid address, restart
+             if (!addr.IsIPv4() || !addr.IsValid() || setConnected.count(addr.GetGroup()) || addr == addrLocalHost)
+                 break;
  
-                 // Only try the old stuff if we don't have enough connections
-                 if (vNodes.size() >= 8 && nSinceLastSeen > 24 * 60 * 60)
-                     continue;
+             nTries++;
  
-                 // If multiple addresses are ready, prioritize by time since
-                 // last seen and time since last tried.
-                 int64 nScore = min(nSinceLastTry, (int64)24 * 60 * 60) - nSinceLastSeen - nRandomizer;
-                 if (nScore > nBest)
-                 {
-                     nBest = nScore;
-                     addrConnect = addr;
-                 }
-             }
+             // only consider very recently tried nodes after 30 failed attempts
+             if (nANow - addr.nLastTry < 600 && nTries < 30)
+                 continue;
+             // do not allow non-default ports, unless after 50 invalid addresses selected already
+             if (addr.GetPort() != GetDefaultPort() && nTries < 50)
+                 continue;
+             addrConnect = addr;
+             break;
          }
  
          if (addrConnect.IsValid())
@@@ -1712,14 -1628,6 +1629,14 @@@ bool BindListenPort(string& strError
  
  void StartNode(void* parg)
  {
 +#ifdef USE_UPNP
 +#if USE_UPNP
 +    fUseUPnP = GetBoolArg("-upnp", true);
 +#else
 +    fUseUPnP = GetBoolArg("-upnp", false);
 +#endif
 +#endif
 +
      if (pnodeLocalHost == NULL)
          pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices));
  
      if (!CreateThread(ThreadMessageHandler, NULL))
          printf("Error: CreateThread(ThreadMessageHandler) failed\n");
  
+     // Dump network addresses
+     if (!CreateThread(ThreadDumpAddress, NULL))
+         printf("Error; CreateThread(ThreadDumpAddress) failed\n");
      // Generate coins in the background
 -    GenerateBitcoins(fGenerateBitcoins, pwalletMain);
 +    GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain);
  }
  
  bool StopNode()
      if (fHaveUPnP && vnThreadsRunning[THREAD_UPNP] > 0) printf("ThreadMapPort still running\n");
      if (vnThreadsRunning[THREAD_DNSSEED] > 0) printf("ThreadDNSAddressSeed still running\n");
      if (vnThreadsRunning[THREAD_ADDEDCONNECTIONS] > 0) printf("ThreadOpenAddedConnections still running\n");
+     if (vnThreadsRunning[THREAD_DUMPADDRESS] > 0) printf("ThreadDumpAddresses still running\n");
      while (vnThreadsRunning[THREAD_MESSAGEHANDLER] > 0 || vnThreadsRunning[THREAD_RPCSERVER] > 0)
          Sleep(20);
      Sleep(50);
+     DumpAddresses();
      return true;
  }
  
diff --combined src/net.h
+++ b/src/net.h
  #include <arpa/inet.h>
  #endif
  
 +#include "mruset.h"
  #include "netbase.h"
  #include "protocol.h"
+ #include "addrman.h"
  
  class CAddrDB;
  class CRequestTracker;
@@@ -32,7 -32,6 +33,6 @@@ static const unsigned int PUBLISH_HOPS 
  
  bool RecvLine(SOCKET hSocket, std::string& strLine);
  bool GetMyExternalIP(CNetAddr& ipRet);
- bool AddAddress(CAddress addr, int64 nTimePenalty=0, CAddrDB *pAddrDB=NULL);
  void AddressCurrentlyConnected(const CService& addr);
  CNode* FindNode(const CNetAddr& ip);
  CNode* FindNode(const CService& ip);
@@@ -80,6 -79,7 +80,7 @@@ enum threadI
      THREAD_UPNP,
      THREAD_DNSSEED,
      THREAD_ADDEDCONNECTIONS,
+     THREAD_DUMPADDRESS,
  
      THREAD_MAX
  };
@@@ -90,11 -90,10 +91,10 @@@ extern uint64 nLocalServices
  extern CAddress addrLocalHost;
  extern uint64 nLocalHostNonce;
  extern boost::array<int, THREAD_MAX> vnThreadsRunning;
+ extern CAddrMan addrman;
  
  extern std::vector<CNode*> vNodes;
  extern CCriticalSection cs_vNodes;
- extern std::map<std::vector<unsigned char>, CAddress> mapAddresses;
- extern CCriticalSection cs_mapAddresses;
  extern std::map<CInv, CDataStream> mapRelay;
  extern std::deque<std::pair<int64, CInv> > vRelayExpiration;
  extern CCriticalSection cs_mapRelay;
@@@ -155,7 -154,7 +155,7 @@@ public
      std::set<uint256> setKnown;
  
      // inventory based relay
 -    std::set<CInv> setInventoryKnown;
 +    mruset<CInv> setInventoryKnown;
      std::vector<CInv> vInventoryToSend;
      CCriticalSection cs_inventory;
      std::multimap<int64, CInv> mapAskFor;
          fGetAddr = false;
          vfSubscribe.assign(256, false);
          nMisbehavior = 0;
 +        setInventoryKnown.max_size(SendBufferSize() / 1000);
  
          // Be shy and don't send version until we hear
          if (!fInbound)