Merge pull request #677 from luke-jr/minfee_modes
authorGavin Andresen <gavinandresen@gmail.com>
Tue, 20 Dec 2011 21:09:18 +0000 (13:09 -0800)
committerGavin Andresen <gavinandresen@gmail.com>
Tue, 20 Dec 2011 21:09:18 +0000 (13:09 -0800)
API: GetMinFee modes

1  2 
src/main.cpp
src/main.h
src/wallet.cpp

diff --combined src/main.cpp
@@@ -3,10 -3,10 +3,10 @@@
  // Distributed under the MIT/X11 software license, see the accompanying
  // file license.txt or http://www.opensource.org/licenses/mit-license.php.
  #include "headers.h"
 +#include "checkpoints.h"
  #include "db.h"
  #include "net.h"
  #include "init.h"
 -#include "cryptopp/sha.h"
  #include <boost/filesystem.hpp>
  #include <boost/filesystem/fstream.hpp>
  
@@@ -17,11 -17,6 +17,11 @@@ using namespace boost
  // Global state
  //
  
 +// Name of client reported in the 'version' message. Report the same name
 +// for both bitcoind and bitcoin-qt, to make it harder for attackers to
 +// target servers or GUI users specifically.
 +const std::string CLIENT_NAME("bitcoin-qt");
 +
  CCriticalSection cs_setpwalletRegistered;
  set<CWallet*> setpwalletRegistered;
  
@@@ -35,6 -30,7 +35,6 @@@ map<COutPoint, CInPoint> mapNextTx
  map<uint256, CBlockIndex*> mapBlockIndex;
  uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
  static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
 -const int nTotalBlocksEstimate = 140700; // Conservative estimate of total nr of blocks on main chain
  const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
  CBlockIndex* pindexGenesisBlock = NULL;
  int nBestHeight = -1;
@@@ -44,8 -40,6 +44,8 @@@ uint256 hashBestChain = 0
  CBlockIndex* pindexBest = NULL;
  int64 nTimeBestReceived = 0;
  
 +CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
 +
  map<uint256, CBlock*> mapOrphanBlocks;
  multimap<uint256, CBlock*> mapOrphanBlocksByPrev;
  
@@@ -70,14 -64,16 +70,14 @@@ int fUseUPnP = false
  #endif
  
  
 -
 -
 -
 -
 -
  //////////////////////////////////////////////////////////////////////////////
  //
  // dispatching functions
  //
  
 +// These functions dispatch to one or all registered wallets
 +
 +
  void RegisterWallet(CWallet* pwalletIn)
  {
      CRITICAL_BLOCK(cs_setpwalletRegistered)
@@@ -94,7 -90,6 +94,7 @@@ void UnregisterWallet(CWallet* pwalletI
      }
  }
  
 +// check whether the passed transaction is from us
  bool static IsFromMe(CTransaction& tx)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
      return false;
  }
  
 +// get the wallet transaction with the given hash (if it exists)
  bool static GetTransaction(const uint256& hashTx, CWalletTx& wtx)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
      return false;
  }
  
 +// erases transaction with the given hash from all wallets
  void static EraseFromWallets(uint256 hash)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->EraseFromWallet(hash);
  }
  
 +// make sure all wallets know about the given transaction, in the given block
  void static SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate);
  }
  
 +// notify wallets about a new best chain
  void static SetBestChain(const CBlockLocator& loc)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->SetBestChain(loc);
  }
  
 +// notify wallets about an updated transaction
  void static UpdatedTransaction(const uint256& hashTx)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->UpdatedTransaction(hashTx);
  }
  
 +// dump all wallets
  void static PrintWallets(const CBlock& block)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->PrintWallet(block);
  }
  
 +// notify wallets about an incoming inventory (for request counts)
  void static Inventory(const uint256& hash)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
          pwallet->Inventory(hash);
  }
  
 +// ask wallets to resend their transactions
  void static ResendWalletTransactions()
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
@@@ -246,67 -233,6 +246,67 @@@ bool CTransaction::ReadFromDisk(COutPoi
      return ReadFromDisk(txdb, prevout, txindex);
  }
  
 +bool CTransaction::IsStandard() const
 +{
 +    BOOST_FOREACH(const CTxIn& txin, vin)
 +    {
 +        // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG
 +        // in an OP_EVAL, which is 3 ~80-byte signatures, 3
 +        // ~65-byte public keys, plus a few script ops.
 +        if (txin.scriptSig.size() > 500)
 +            return error("nonstandard txin, size %d is too large\n", txin.scriptSig.size());
 +        if (!txin.scriptSig.IsPushOnly())
 +            return error("nonstandard txin (opcodes other than PUSH): %s", txin.scriptSig.ToString().c_str());
 +    }
 +    BOOST_FOREACH(const CTxOut& txout, vout)
 +        if (!::IsStandard(txout.scriptPubKey))
 +            return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
 +    return true;
 +}
 +
 +//
 +// Check transaction inputs, and make sure any
 +// OP_EVAL transactions are evaluating IsStandard scripts
 +//
 +// Why bother? To avoid denial-of-service attacks; an attacker
 +// can submit a standard DUP HASH... OP_EVAL transaction,
 +// which will get accepted into blocks. The script being
 +// EVAL'ed can be anything; an attacker could use a very
 +// expensive-to-check-upon-redemption script like:
 +//   DUP CHECKSIG DROP ... repeated 100 times... OP_1
 +//
 +bool CTransaction::AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const
 +{
 +    if (fTestNet)
 +        return true; // Allow non-standard on testnet
 +
 +    for (int i = 0; i < vin.size(); i++)
 +    {
 +        COutPoint prevout = vin[i].prevout;
 +        assert(mapInputs.count(prevout.hash) > 0);
 +        CTransaction& txPrev = mapInputs[prevout.hash].second;
 +
 +        vector<vector<unsigned char> > vSolutions;
 +        txnouttype whichType;
 +        // get the scriptPubKey corresponding to this input:
 +        CScript& prevScript = txPrev.vout[prevout.n].scriptPubKey;
 +        if (!Solver(prevScript, whichType, vSolutions))
 +            return error("nonstandard txin (spending nonstandard txout %s)", prevScript.ToString().c_str());
 +        if (whichType == TX_SCRIPTHASH)
 +        {
 +            vector<vector<unsigned char> > stack;
 +            int nUnused;
 +            if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0, true, nUnused))
 +                return false;
 +            CScript subscript(stack.back().begin(), stack.back().end());
 +            if (!::IsStandard(subscript))
 +                return error("nonstandard txin (nonstandard OP_EVAL subscript %s)", subscript.ToString().c_str());
 +        }
 +    }
 +
 +    return true;
 +}
 +
  
  
  int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
@@@ -370,24 -296,24 +370,24 @@@ bool CTransaction::CheckTransaction() c
  {
      // Basic checks that don't depend on any context
      if (vin.empty())
 -        return error("CTransaction::CheckTransaction() : vin empty");
 +        return DoS(10, error("CTransaction::CheckTransaction() : vin empty"));
      if (vout.empty())
 -        return error("CTransaction::CheckTransaction() : vout empty");
 +        return DoS(10, error("CTransaction::CheckTransaction() : vout empty"));
      // Size limits
      if (::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE)
 -        return error("CTransaction::CheckTransaction() : size limits failed");
 +        return DoS(100, error("CTransaction::CheckTransaction() : size limits failed"));
  
      // Check for negative or overflow output values
      int64 nValueOut = 0;
      BOOST_FOREACH(const CTxOut& txout, vout)
      {
          if (txout.nValue < 0)
 -            return error("CTransaction::CheckTransaction() : txout.nValue negative");
 +            return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue negative"));
          if (txout.nValue > MAX_MONEY)
 -            return error("CTransaction::CheckTransaction() : txout.nValue too high");
 +            return DoS(100, error("CTransaction::CheckTransaction() : txout.nValue too high"));
          nValueOut += txout.nValue;
          if (!MoneyRange(nValueOut))
 -            return error("CTransaction::CheckTransaction() : txout total out of range");
 +            return DoS(100, error("CTransaction::CheckTransaction() : txout total out of range"));
      }
  
      // Check for duplicate inputs
      if (IsCoinBase())
      {
          if (vin[0].scriptSig.size() < 2 || vin[0].scriptSig.size() > 100)
 -            return error("CTransaction::CheckTransaction() : coinbase script size");
 +            return DoS(100, error("CTransaction::CheckTransaction() : coinbase script size"));
      }
      else
      {
          BOOST_FOREACH(const CTxIn& txin, vin)
              if (txin.prevout.IsNull())
 -                return error("CTransaction::CheckTransaction() : prevout is null");
 +                return DoS(10, error("CTransaction::CheckTransaction() : prevout is null"));
      }
  
      return true;
@@@ -424,12 -350,21 +424,12 @@@ bool CTransaction::AcceptToMemoryPool(C
  
      // Coinbase is only valid in a block, not as a loose transaction
      if (IsCoinBase())
 -        return error("AcceptToMemoryPool() : coinbase as individual tx");
 +        return DoS(100, error("AcceptToMemoryPool() : coinbase as individual tx"));
  
      // To help v0.1.5 clients who would see it as a negative number
 -    if ((int64)nLockTime > INT_MAX)
 +    if ((int64)nLockTime > std::numeric_limits<int>::max())
          return error("AcceptToMemoryPool() : not accepting nLockTime beyond 2038 yet");
  
 -    // Safety limits
 -    unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
 -    // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
 -    // attacks disallow transactions with more than one SigOp per 34 bytes.
 -    // 34 bytes because a TxOut is:
 -    //   20-byte address + 8 byte bitcoin amount + 5 bytes of ops + 1 byte script length
 -    if (GetSigOpCount() > nSize / 34 || nSize < 100)
 -        return error("AcceptToMemoryPool() : nonstandard transaction");
 -
      // Rather not work on nonstandard transactions (unless -testnet)
      if (!fTestNet && !IsStandard())
          return error("AcceptToMemoryPool() : nonstandard transaction type");
  
      if (fCheckInputs)
      {
 -        // Check against previous transactions
 +        map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
          map<uint256, CTxIndex> mapUnused;
 +        if (!FetchInputs(txdb, mapUnused, false, false, mapInputs))
 +        {
 +            if (pfMissingInputs)
 +                *pfMissingInputs = true;
 +            return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
 +        }
 +
 +        // Check for non-standard OP_EVALs in inputs
 +        if (!AreInputsStandard(mapInputs))
 +            return error("AcceptToMemoryPool() : nonstandard transaction input");
 +
 +        // Check against previous transactions
          int64 nFees = 0;
 -        if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false))
 +        int nSigOps = 0;
 +        if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, nSigOps))
          {
              if (pfMissingInputs)
                  *pfMissingInputs = true;
              return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
          }
 +        // Checking ECDSA signatures is a CPU bottleneck, so to avoid denial-of-service
 +        // attacks disallow transactions with more than one SigOp per 65 bytes.
 +        // 65 bytes because that is the minimum size of an ECDSA signature
 +        unsigned int nSize = ::GetSerializeSize(*this, SER_NETWORK);
 +        if (nSigOps > nSize / 65 || nSize < 100)
 +            return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
  
          // Don't accept it if it can't get into a block
-         if (nFees < GetMinFee(1000, true, true))
+         if (nFees < GetMinFee(1000, true, GMF_RELAY))
              return error("AcceptToMemoryPool() : not enough fees");
  
          // Continuously rate-limit free transactions
@@@ -592,7 -508,7 +592,7 @@@ bool CTransaction::RemoveFromMemoryPool
  
  
  
 -int CMerkleTx::GetDepthInMainChain(int& nHeightRet) const
 +int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
  {
      if (hashBlock == 0 || nIndex == -1)
          return 0;
          fMerkleVerified = true;
      }
  
 -    nHeightRet = pindex->nHeight;
 +    pindexRet = pindex;
      return pindexBest->nHeight - pindex->nHeight + 1;
  }
  
@@@ -735,32 -651,11 +735,32 @@@ int64 static GetBlockValue(int nHeight
      return nSubsidy + nFees;
  }
  
 +static const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
 +static const int64 nTargetSpacing = 10 * 60;
 +static const int64 nInterval = nTargetTimespan / nTargetSpacing;
 +
 +//
 +// minimum amount of work that could possibly be required nTime after
 +// minimum work required was nBase
 +//
 +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
 +{
 +    CBigNum bnResult;
 +    bnResult.SetCompact(nBase);
 +    while (nTime > 0 && bnResult < bnProofOfWorkLimit)
 +    {
 +        // Maximum 400% adjustment...
 +        bnResult *= 4;
 +        // ... in best-case exactly 4-times-normal target time
 +        nTime -= nTargetTimespan*4;
 +    }
 +    if (bnResult > bnProofOfWorkLimit)
 +        bnResult = bnProofOfWorkLimit;
 +    return bnResult.GetCompact();
 +}
 +
  unsigned int static GetNextWorkRequired(const CBlockIndex* pindexLast)
  {
 -    const int64 nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
 -    const int64 nTargetSpacing = 10 * 60;
 -    const int64 nInterval = nTargetTimespan / nTargetSpacing;
  
      // Genesis block
      if (pindexLast == NULL)
@@@ -818,15 -713,22 +818,15 @@@ bool CheckProofOfWork(uint256 hash, uns
      return true;
  }
  
 -// Return conservative estimate of total number of blocks, 0 if unknown
 -int GetTotalBlocksEstimate()
 +// Return maximum amount of blocks that other nodes claim to have
 +int GetNumBlocksOfPeers()
  {
 -    if(fTestNet)
 -    {
 -        return 0;
 -    }
 -    else
 -    {
 -        return nTotalBlocksEstimate;
 -    }
 +    return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate());
  }
  
  bool IsInitialBlockDownload()
  {
 -    if (pindexBest == NULL || nBestHeight < (GetTotalBlocksEstimate()-nInitialBlockThreshold))
 +    if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold))
          return true;
      static int64 nLastUpdate;
      static CBlockIndex* pindexLastBest;
@@@ -897,78 -799,56 +897,78 @@@ bool CTransaction::DisconnectInputs(CTx
  }
  
  
 -bool CTransaction::ConnectInputs(CTxDB& txdb, map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
 -                                 CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee)
 +bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
 +                               bool fBlock, bool fMiner, map<uint256, pair<CTxIndex, CTransaction> >& inputsRet)
 +{
 +    if (IsCoinBase())
 +        return true; // Coinbase transactions have no inputs to fetch.
 +
 +    for (int i = 0; i < vin.size(); i++)
 +    {
 +        COutPoint prevout = vin[i].prevout;
 +        if (inputsRet.count(prevout.hash))
 +            continue; // Got it already
 +
 +        // Read txindex
 +        CTxIndex& txindex = inputsRet[prevout.hash].first;
 +        bool fFound = true;
 +        if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
 +        {
 +            // Get txindex from current proposed changes
 +            txindex = mapTestPool.find(prevout.hash)->second;
 +        }
 +        else
 +        {
 +            // Read txindex from txdb
 +            fFound = txdb.ReadTxIndex(prevout.hash, txindex);
 +        }
 +        if (!fFound && (fBlock || fMiner))
 +            return fMiner ? false : error("FetchInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 +
 +        // Read txPrev
 +        CTransaction& txPrev = inputsRet[prevout.hash].second;
 +        if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
 +        {
 +            // Get prev tx from single transactions in memory
 +            CRITICAL_BLOCK(cs_mapTransactions)
 +            {
 +                if (!mapTransactions.count(prevout.hash))
 +                    return error("FetchInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 +                txPrev = mapTransactions[prevout.hash];
 +            }
 +            if (!fFound)
 +                txindex.vSpent.resize(txPrev.vout.size());
 +        }
 +        else
 +        {
 +            // Get prev tx from disk
 +            if (!txPrev.ReadFromDisk(txindex.pos))
 +                return error("FetchInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 +        }
 +    }
 +    return true;
 +}
 +
 +bool CTransaction::ConnectInputs(map<uint256, pair<CTxIndex, CTransaction> > inputs,
 +                                 map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
 +                                 CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee)
  {
      // Take over previous transactions' spent pointers
 +    // fBlock is true when this is called from AcceptBlock when a new best-block is added to the blockchain
 +    // fMiner is true when called from the internal bitcoin miner
 +    // ... both are false when called from CTransaction::AcceptToMemoryPool
      if (!IsCoinBase())
      {
          int64 nValueIn = 0;
          for (int i = 0; i < vin.size(); i++)
          {
              COutPoint prevout = vin[i].prevout;
 -
 -            // Read txindex
 -            CTxIndex txindex;
 -            bool fFound = true;
 -            if ((fBlock || fMiner) && mapTestPool.count(prevout.hash))
 -            {
 -                // Get txindex from current proposed changes
 -                txindex = mapTestPool[prevout.hash];
 -            }
 -            else
 -            {
 -                // Read txindex from txdb
 -                fFound = txdb.ReadTxIndex(prevout.hash, txindex);
 -            }
 -            if (!fFound && (fBlock || fMiner))
 -                return fMiner ? false : error("ConnectInputs() : %s prev tx %s index entry not found", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 -
 -            // Read txPrev
 -            CTransaction txPrev;
 -            if (!fFound || txindex.pos == CDiskTxPos(1,1,1))
 -            {
 -                // Get prev tx from single transactions in memory
 -                CRITICAL_BLOCK(cs_mapTransactions)
 -                {
 -                    if (!mapTransactions.count(prevout.hash))
 -                        return error("ConnectInputs() : %s mapTransactions prev not found %s", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 -                    txPrev = mapTransactions[prevout.hash];
 -                }
 -                if (!fFound)
 -                    txindex.vSpent.resize(txPrev.vout.size());
 -            }
 -            else
 -            {
 -                // Get prev tx from disk
 -                if (!txPrev.ReadFromDisk(txindex.pos))
 -                    return error("ConnectInputs() : %s ReadFromDisk prev tx %s failed", GetHash().ToString().substr(0,10).c_str(),  prevout.hash.ToString().substr(0,10).c_str());
 -            }
 +            assert(inputs.count(prevout.hash) > 0);
 +            CTxIndex& txindex = inputs[prevout.hash].first;
 +            CTransaction& txPrev = inputs[prevout.hash].second;
  
              if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
 -                return error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str());
 +                return DoS(100, error("ConnectInputs() : %s prevout.n out of range %d %d %d prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str()));
  
              // If prev is coinbase, check that it's matured
              if (txPrev.IsCoinBase())
                      if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile)
                          return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
  
 -            // Verify signature
 -            if (!VerifySignature(txPrev, *this, i))
 -                return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
 +            // Skip ECDSA signature verification when connecting blocks (fBlock=true) during initial download
 +            // (before the last blockchain checkpoint). This is safe because block merkle hashes are
 +            // still computed and checked, and any change will be caught at the next checkpoint.
 +            if (!(fBlock && IsInitialBlockDownload()))
 +            {
 +                bool fStrictOpEval = true;
 +                // This code should be removed when OP_EVAL has
 +                // a majority of hashing power on the network.
 +                if (fBlock)
 +                {
 +                    // To avoid being on the short end of a block-chain split,
 +                    // interpret OP_EVAL as a NO_OP until blocks with timestamps
 +                    // after opevaltime:
 +                    int64 nEvalSwitchTime = GetArg("opevaltime", 1328054400); // Feb 1, 2012
 +                    fStrictOpEval = (pindexBlock->nTime >= nEvalSwitchTime);
 +                }
 +                // if !fBlock, then always be strict-- don't accept
 +                // invalid-under-new-rules OP_EVAL transactions into
 +                // our memory pool (don't relay them, don't include them
 +                // in blocks we mine).
 +
 +                // Verify signature
 +                if (!VerifySignature(txPrev, *this, i, nSigOpsRet, fStrictOpEval))
 +                    return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
 +            }
  
 -            // Check for conflicts
 +            // Check for conflicts (double-spend)
 +            // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
 +            // for an attacker to attempt to split the network.
              if (!txindex.vSpent[prevout.n].IsNull())
                  return fMiner ? false : error("ConnectInputs() : %s prev tx already used at %s", GetHash().ToString().substr(0,10).c_str(), txindex.vSpent[prevout.n].ToString().c_str());
  
              // Check for negative or overflow input values
              nValueIn += txPrev.vout[prevout.n].nValue;
              if (!MoneyRange(txPrev.vout[prevout.n].nValue) || !MoneyRange(nValueIn))
 -                return error("ConnectInputs() : txin values out of range");
 +                return DoS(100, error("ConnectInputs() : txin values out of range"));
  
              // Mark outpoints as spent
              txindex.vSpent[prevout.n] = posThisTx;
          }
  
          if (nValueIn < GetValueOut())
 -            return error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str());
 +            return DoS(100, error("ConnectInputs() : %s value in < value out", GetHash().ToString().substr(0,10).c_str()));
  
          // Tally transaction fees
          int64 nTxFee = nValueIn - GetValueOut();
          if (nTxFee < 0)
 -            return error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str());
 +            return DoS(100, error("ConnectInputs() : %s nTxFee < 0", GetHash().ToString().substr(0,10).c_str()));
          if (nTxFee < nMinFee)
              return false;
          nFees += nTxFee;
          if (!MoneyRange(nFees))
 -            return error("ConnectInputs() : nFees out of range");
 +            return DoS(100, error("ConnectInputs() : nFees out of range"));
      }
  
      if (fBlock)
@@@ -1073,8 -929,7 +1073,8 @@@ bool CTransaction::ClientConnectInputs(
                  return false;
  
              // Verify signature
 -            if (!VerifySignature(txPrev, *this, i))
 +            int nUnused = 0;
 +            if (!VerifySignature(txPrev, *this, i, nUnused, false))
                  return error("ConnectInputs() : VerifySignature failed");
  
              ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@@ -1132,21 -987,14 +1132,21 @@@ bool CBlock::ConnectBlock(CTxDB& txdb, 
  
      map<uint256, CTxIndex> mapQueuedChanges;
      int64 nFees = 0;
 +    int nSigOps = 0;
      BOOST_FOREACH(CTransaction& tx, vtx)
      {
          CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
          nTxPos += ::GetSerializeSize(tx, SER_DISK);
  
 -        if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false))
 +        map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
 +        if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs))
              return false;
 +        if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, nFees, true, false, nSigOps))
 +            return false;
 +        if (nSigOps > MAX_BLOCK_SIGOPS)
 +            return DoS(100, error("ConnectBlock() : too many sigops"));
      }
 +
      // Write queued txindex changes
      for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
      {
@@@ -1385,11 -1233,11 +1385,11 @@@ bool CBlock::CheckBlock() cons
  
      // Size limits
      if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, SER_NETWORK) > MAX_BLOCK_SIZE)
 -        return error("CheckBlock() : size limits failed");
 +        return DoS(100, error("CheckBlock() : size limits failed"));
  
      // Check proof of work matches claimed amount
      if (!CheckProofOfWork(GetHash(), nBits))
 -        return error("CheckBlock() : proof of work failed");
 +        return DoS(50, error("CheckBlock() : proof of work failed"));
  
      // Check timestamp
      if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
  
      // First transaction must be coinbase, the rest must not be
      if (vtx.empty() || !vtx[0].IsCoinBase())
 -        return error("CheckBlock() : first tx is not coinbase");
 +        return DoS(100, error("CheckBlock() : first tx is not coinbase"));
      for (int i = 1; i < vtx.size(); i++)
          if (vtx[i].IsCoinBase())
 -            return error("CheckBlock() : more than one coinbase");
 +            return DoS(100, error("CheckBlock() : more than one coinbase"));
  
      // Check transactions
      BOOST_FOREACH(const CTransaction& tx, vtx)
          if (!tx.CheckTransaction())
 -            return error("CheckBlock() : CheckTransaction failed");
 +            return DoS(tx.nDoS, error("CheckBlock() : CheckTransaction failed"));
  
 -    // Check that it's not full of nonstandard transactions
 -    if (GetSigOpCount() > MAX_BLOCK_SIGOPS)
 -        return error("CheckBlock() : too many nonstandard transactions");
 +    // This code should be removed when a compatibility-breaking block chain split has passed.
 +    // Compatibility check for old clients that counted sigops differently:
 +    int nSigOps = 0;
 +    BOOST_FOREACH(const CTransaction& tx, vtx)
 +    {
 +        BOOST_FOREACH(const CTxIn& txin, tx.vin)
 +        {
 +            nSigOps += txin.scriptSig.GetSigOpCount();
 +        }
 +        BOOST_FOREACH(const CTxOut& txout, tx.vout)
 +        {
 +            nSigOps += txout.scriptPubKey.GetSigOpCount();
 +        }
 +    }
 +    if (nSigOps > MAX_BLOCK_SIGOPS)
 +        return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount"));
  
      // Check merkleroot
      if (hashMerkleRoot != BuildMerkleTree())
 -        return error("CheckBlock() : hashMerkleRoot mismatch");
 +        return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch"));
  
      return true;
  }
@@@ -1441,13 -1276,13 +1441,13 @@@ bool CBlock::AcceptBlock(
      // Get prev block index
      map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashPrevBlock);
      if (mi == mapBlockIndex.end())
 -        return error("AcceptBlock() : prev block not found");
 +        return DoS(10, error("AcceptBlock() : prev block not found"));
      CBlockIndex* pindexPrev = (*mi).second;
      int nHeight = pindexPrev->nHeight+1;
  
      // Check proof of work
      if (nBits != GetNextWorkRequired(pindexPrev))
 -        return error("AcceptBlock() : incorrect proof of work");
 +        return DoS(100, error("AcceptBlock() : incorrect proof of work"));
  
      // Check timestamp against prev
      if (GetBlockTime() <= pindexPrev->GetMedianTimePast())
      // Check that all transactions are finalized
      BOOST_FOREACH(const CTransaction& tx, vtx)
          if (!tx.IsFinal(nHeight, GetBlockTime()))
 -            return error("AcceptBlock() : contains a non-final transaction");
 +            return DoS(10, error("AcceptBlock() : contains a non-final transaction"));
  
      // Check that the block chain matches the known block chain up to a checkpoint
 -    if (!fTestNet)
 -        if ((nHeight ==  11111 && hash != uint256("0x0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")) ||
 -            (nHeight ==  33333 && hash != uint256("0x000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")) ||
 -            (nHeight ==  68555 && hash != uint256("0x00000000001e1b4903550a0b96e9a9405c8a95f387162e4944e8d9fbe501cd6a")) ||
 -            (nHeight ==  70567 && hash != uint256("0x00000000006a49b14bcf27462068f1264c961f11fa2e0eddd2be0791e1d4124a")) ||
 -            (nHeight ==  74000 && hash != uint256("0x0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")) ||
 -            (nHeight == 105000 && hash != uint256("0x00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")) ||
 -            (nHeight == 118000 && hash != uint256("0x000000000000774a7f8a7a12dc906ddb9e17e75d684f15e00f8767f9e8f36553")) ||
 -            (nHeight == 134444 && hash != uint256("0x00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")) ||
 -            (nHeight == 140700 && hash != uint256("0x000000000000033b512028abb90e1626d8b346fd0ed598ac0a3c371138dce2bd")))
 -            return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
 +    if (!Checkpoints::CheckBlock(nHeight, hash))
 +        return DoS(100, error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight));
  
      // Write block to history file
      if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK)))
      return true;
  }
  
 -bool static ProcessBlock(CNode* pfrom, CBlock* pblock)
 +bool ProcessBlock(CNode* pfrom, CBlock* pblock)
  {
      // Check for duplicate
      uint256 hash = pblock->GetHash();
      if (!pblock->CheckBlock())
          return error("ProcessBlock() : CheckBlock FAILED");
  
 +    CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
 +    if (pcheckpoint && pblock->hashPrevBlock != hashBestChain)
 +    {
 +        // Extra checks to prevent "fill up memory by spamming with bogus blocks"
 +        int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
 +        if (deltaTime < 0)
 +        {
 +            pfrom->Misbehaving(100);
 +            return error("ProcessBlock() : block with timestamp before last checkpoint");
 +        }
 +        CBigNum bnNewBlock;
 +        bnNewBlock.SetCompact(pblock->nBits);
 +        CBigNum bnRequired;
 +        bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
 +        if (bnNewBlock > bnRequired)
 +        {
 +            pfrom->Misbehaving(100);
 +            return error("ProcessBlock() : block with too little proof-of-work");
 +        }
 +    }
 +
 +
      // If don't already have its previous block, shunt it off to holding area until we get it
      if (!mapBlockIndex.count(pblock->hashPrevBlock))
      {
@@@ -1940,10 -1762,7 +1940,10 @@@ bool static ProcessMessage(CNode* pfrom
      {
          // Each connection can only send one version message
          if (pfrom->nVersion != 0)
 +        {
 +            pfrom->Misbehaving(1);
              return false;
 +        }
  
          int64 nTime;
          CAddress addrMe;
          // Change version
          if (pfrom->nVersion >= 209)
              pfrom->PushMessage("verack");
 -        pfrom->vSend.SetVersion(min(pfrom->nVersion, VERSION));
 +        pfrom->vSend.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
          if (pfrom->nVersion < 209)
 -            pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
 +            pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
  
          if (!pfrom->fInbound)
          {
          }
  
          // Ask the first connected node for block updates
 -        static int nAskedForBlocks;
 +        static int nAskedForBlocks = 0;
          if (!pfrom->fClient &&
              (pfrom->nVersion < 32000 || pfrom->nVersion >= 32400) &&
               (nAskedForBlocks < 1 || vNodes.size() <= 1))
          pfrom->fSuccessfullyConnected = true;
  
          printf("version message: version %d, blocks=%d\n", pfrom->nVersion, pfrom->nStartingHeight);
 +
 +        cPeerBlockCounts.input(pfrom->nStartingHeight);
      }
  
  
      else if (pfrom->nVersion == 0)
      {
          // Must have a version message before anything else
 +        pfrom->Misbehaving(1);
          return false;
      }
  
  
      else if (strCommand == "verack")
      {
 -        pfrom->vRecv.SetVersion(min(pfrom->nVersion, VERSION));
 +        pfrom->vRecv.SetVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
      }
  
  
          if (pfrom->nVersion < 31402 && mapAddresses.size() > 1000)
              return true;
          if (vAddr.size() > 1000)
 +        {
 +            pfrom->Misbehaving(20);
              return error("message addr size() = %d", vAddr.size());
 +        }
  
          // Store the new addresses
          CAddrDB addrDB;
          vector<CInv> vInv;
          vRecv >> vInv;
          if (vInv.size() > 50000)
 +        {
 +            pfrom->Misbehaving(20);
              return error("message inv size() = %d", vInv.size());
 +        }
  
          CTxDB txdb("r");
          BOOST_FOREACH(const CInv& inv, vInv)
          vector<CInv> vInv;
          vRecv >> vInv;
          if (vInv.size() > 50000)
 +        {
 +            pfrom->Misbehaving(20);
              return error("message getdata size() = %d", vInv.size());
 +        }
  
          BOOST_FOREACH(const CInv& inv, vInv)
          {
              printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
              AddOrphanTx(vMsg);
          }
 +        if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
      }
  
  
  
          if (ProcessBlock(pfrom, &block))
              mapAlreadyAskedFor.erase(inv);
 +        if (block.nDoS) pfrom->Misbehaving(block.nDoS);
      }
  
  
@@@ -2780,25 -2585,15 +2780,25 @@@ int static FormatHashBlocks(void* pbuff
      return blocks;
  }
  
  static const unsigned int pSHA256InitState[8] =
  {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
  
 -inline void SHA256Transform(void* pstate, void* pinput, const void* pinit)
 +void SHA256Transform(void* pstate, void* pinput, const void* pinit)
  {
 -    memcpy(pstate, pinit, 32);
 -    CryptoPP::SHA256::Transform((CryptoPP::word32*)pstate, (CryptoPP::word32*)pinput);
 +    SHA256_CTX ctx;
 +    unsigned char data[64];
 +
 +    SHA256_Init(&ctx);
 +
 +    for (int i = 0; i < 16; i++)
 +        ((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
 +
 +    for (int i = 0; i < 8; i++)
 +        ctx.h[i] = ((uint32_t*)pinit)[i];
 +
 +    SHA256_Update(&ctx, data, sizeof(data));
 +    for (int i = 0; i < 8; i++) 
 +        ((uint32_t*)pstate)[i] = ctx.h[i];
  }
  
  //
@@@ -2956,21 -2751,18 +2956,21 @@@ CBlock* CreateNewBlock(CReserveKey& res
              unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
              if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
                  continue;
  
              // Transaction fee required depends on block size
              bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
-             int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, true);
+             int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
  
              // Connecting shouldn't fail due to dependency on other memory pool transactions
              // because we're already processing them in order of dependency
              map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
 -            if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee))
 +            map<uint256, pair<CTxIndex, CTransaction> > mapInputs;
 +            if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs))
 +                continue;
 +            int nTxSigOps = 0;
 +            if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nTxSigOps, nMinFee))
 +                continue;
 +            if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
                  continue;
              swap(mapTestPool, mapTestPoolTmp);
  
@@@ -3019,13 -2811,6 +3019,13 @@@ void IncrementExtraNonce(CBlock* pblock
      }
      ++nExtraNonce;
      pblock->vtx[0].vin[0].scriptSig = CScript() << pblock->nTime << CBigNum(nExtraNonce);
 +
 +    // Put "OP_EVAL" in the coinbase so everybody can tell when
 +    // a majority of miners support it
 +    const char* pOpEvalName = GetOpName(OP_EVAL);
 +    pblock->vtx[0].vin[0].scriptSig += CScript() << std::vector<unsigned char>(pOpEvalName, pOpEvalName+strlen(pOpEvalName));
 +    assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
 +
      pblock->hashMerkleRoot = pblock->BuildMerkleTree();
  }
  
@@@ -3109,6 -2894,7 +3109,6 @@@ bool CheckWork(CBlock* pblock, CWallet
              return error("BitcoinMiner : ProcessBlock, block not accepted");
      }
  
 -    Sleep(2000);
      return true;
  }
  
diff --combined src/main.h
@@@ -27,10 -27,6 +27,10 @@@ class CRequestTracker
  class CNode;
  class CBlockIndex;
  
 +static const int CLIENT_VERSION = 59900;
 +static const bool VERSION_IS_BETA = true;
 +extern const std::string CLIENT_NAME;
 +
  static const unsigned int MAX_BLOCK_SIZE = 1000000;
  static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
  static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
@@@ -89,7 -85,6 +89,7 @@@ class CTxIndex
  
  void RegisterWallet(CWallet* pwalletIn);
  void UnregisterWallet(CWallet* pwalletIn);
 +bool ProcessBlock(CNode* pfrom, CBlock* pblock);
  bool CheckDiskSpace(uint64 nAdditionalBytes=0);
  FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb");
  FILE* AppendBlockFile(unsigned int& nFileRet);
@@@ -103,8 -98,7 +103,8 @@@ void IncrementExtraNonce(CBlock* pblock
  void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
  bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
  bool CheckProofOfWork(uint256 hash, unsigned int nBits);
 -int GetTotalBlocksEstimate();
 +unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
 +int GetNumBlocksOfPeers();
  bool IsInitialBlockDownload();
  std::string GetWarnings(std::string strFor);
  
@@@ -258,17 -252,17 +258,17 @@@ public
  
      CTxIn()
      {
 -        nSequence = UINT_MAX;
 +        nSequence = std::numeric_limits<unsigned int>::max();
      }
  
 -    explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
 +    explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
      {
          prevout = prevoutIn;
          scriptSig = scriptSigIn;
          nSequence = nSequenceIn;
      }
  
 -    CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=UINT_MAX)
 +    CTxIn(uint256 hashPrevTx, unsigned int nOut, CScript scriptSigIn=CScript(), unsigned int nSequenceIn=std::numeric_limits<unsigned int>::max())
      {
          prevout = COutPoint(hashPrevTx, nOut);
          scriptSig = scriptSigIn;
  
      bool IsFinal() const
      {
 -        return (nSequence == UINT_MAX);
 +        return (nSequence == std::numeric_limits<unsigned int>::max());
      }
  
      friend bool operator==(const CTxIn& a, const CTxIn& b)
              str += strprintf(", coinbase %s", HexStr(scriptSig).c_str());
          else
              str += strprintf(", scriptSig=%s", scriptSig.ToString().substr(0,24).c_str());
 -        if (nSequence != UINT_MAX)
 +        if (nSequence != std::numeric_limits<unsigned int>::max())
              str += strprintf(", nSequence=%u", nSequence);
          str += ")";
          return str;
@@@ -393,6 -387,13 +393,13 @@@ public
  
  
  
+ enum GetMinFee_mode
+ {
+     GMF_BLOCK,
+     GMF_RELAY,
+     GMF_SEND,
+ };
  //
  // The basic transaction that is broadcasted on the network and contained in
  // blocks.  A transaction can contain multiple inputs and outputs.
@@@ -405,9 -406,6 +412,9 @@@ public
      std::vector<CTxOut> vout;
      unsigned int nLockTime;
  
 +    // Denial-of-service detection:
 +    mutable int nDoS;
 +    bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
  
      CTransaction()
      {
          vin.clear();
          vout.clear();
          nLockTime = 0;
 +        nDoS = 0;  // Denial-of-service prevention
      }
  
      bool IsNull() const
                  return false;
  
          bool fNewer = false;
 -        unsigned int nLowest = UINT_MAX;
 +        unsigned int nLowest = std::numeric_limits<unsigned int>::max();
          for (int i = 0; i < vin.size(); i++)
          {
              if (vin[i].nSequence != old.vin[i].nSequence)
          return (vin.size() == 1 && vin[0].prevout.IsNull());
      }
  
 -    int GetSigOpCount() const
 -    {
 -        int n = 0;
 -        BOOST_FOREACH(const CTxIn& txin, vin)
 -            n += txin.scriptSig.GetSigOpCount();
 -        BOOST_FOREACH(const CTxOut& txout, vout)
 -            n += txout.scriptPubKey.GetSigOpCount();
 -        return n;
 -    }
 -
 -    bool IsStandard() const
 -    {
 -        BOOST_FOREACH(const CTxIn& txin, vin)
 -            if (!txin.scriptSig.IsPushOnly())
 -                return error("nonstandard txin: %s", txin.scriptSig.ToString().c_str());
 -        BOOST_FOREACH(const CTxOut& txout, vout)
 -            if (!::IsStandard(txout.scriptPubKey))
 -                return error("nonstandard txout: %s", txout.scriptPubKey.ToString().c_str());
 -        return true;
 -    }
 +    bool IsStandard() const;
 +    bool AreInputsStandard(std::map<uint256, std::pair<CTxIndex, CTransaction> > mapInputs) const;
  
      int64 GetValueOut() const
      {
          return dPriority > COIN * 144 / 250;
      }
  
-     int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, bool fForRelay=false) const
+     int64 GetMinFee(unsigned int nBlockSize=1, bool fAllowFree=true, enum GetMinFee_mode mode=GMF_BLOCK) const
      {
          // Base fee is either MIN_TX_FEE or MIN_RELAY_TX_FEE
-         int64 nBaseFee = fForRelay ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
+         int64 nBaseFee = (mode == GMF_RELAY) ? MIN_RELAY_TX_FEE : MIN_TX_FEE;
  
          unsigned int nBytes = ::GetSerializeSize(*this, SER_NETWORK);
          unsigned int nNewBlockSize = nBlockSize + nBytes;
      bool ReadFromDisk(CTxDB& txdb, COutPoint prevout);
      bool ReadFromDisk(COutPoint prevout);
      bool DisconnectInputs(CTxDB& txdb);
 -    bool ConnectInputs(CTxDB& txdb, std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
 -                       CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int64 nMinFee=0);
 +
 +    // Fetch from memory and/or disk. inputsRet keys are transaction hashes.
 +    bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
 +                     bool fBlock, bool fMiner, std::map<uint256, std::pair<CTxIndex, CTransaction> >& inputsRet);
 +    bool ConnectInputs(std::map<uint256, std::pair<CTxIndex, CTransaction> > inputs,
 +                       std::map<uint256, CTxIndex>& mapTestPool, CDiskTxPos posThisTx,
 +                       CBlockIndex* pindexBlock, int64& nFees, bool fBlock, bool fMiner, int& nSigOpsRet, int64 nMinFee=0);
      bool ClientConnectInputs();
      bool CheckTransaction() const;
      bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
@@@ -686,8 -696,8 +693,8 @@@ public
  
  
      int SetMerkleBranch(const CBlock* pblock=NULL);
 -    int GetDepthInMainChain(int& nHeightRet) const;
 -    int GetDepthInMainChain() const { int nHeight; return GetDepthInMainChain(nHeight); }
 +    int GetDepthInMainChain(CBlockIndex* &pindexRet) const;
 +    int GetDepthInMainChain() const { CBlockIndex *pindexRet; return GetDepthInMainChain(pindexRet); }
      bool IsInMainChain() const { return GetDepthInMainChain() > 0; }
      int GetBlocksToMaturity() const;
      bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true);
@@@ -749,7 -759,6 +756,7 @@@ public
          return !(a == b);
      }
      int GetDepthInMainChain() const;
 + 
  };
  
  
@@@ -784,9 -793,6 +791,9 @@@ public
      // memory only
      mutable std::vector<uint256> vMerkleTree;
  
 +    // Denial-of-service detection:
 +    mutable int nDoS;
 +    bool DoS(int nDoSIn, bool fIn) const { nDoS += nDoSIn; return fIn; }
  
      CBlock()
      {
          nNonce = 0;
          vtx.clear();
          vMerkleTree.clear();
 +        nDoS = 0;
      }
  
      bool IsNull() const
          return (int64)nTime;
      }
  
 -    int GetSigOpCount() const
 -    {
 -        int n = 0;
 -        BOOST_FOREACH(const CTransaction& tx, vtx)
 -            n += tx.GetSigOpCount();
 -        return n;
 -    }
  
  
      uint256 BuildMerkleTree() const
          fflush(fileout);
          if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
          {
 -#ifdef __WXMSW__
 +#ifdef WIN32
              _commit(_fileno(fileout));
  #else
              fsync(fileno(fileout));
@@@ -1248,11 -1260,6 +1255,11 @@@ public
              Set((*mi).second);
      }
  
 +    CBlockLocator(const std::vector<uint256>& vHaveIn)
 +    {
 +        vHave = vHaveIn;
 +    }
 +
      IMPLEMENT_SERIALIZE
      (
          if (!(nType & SER_GETHASH))
@@@ -1511,7 -1518,6 +1518,7 @@@ public
  
      bool AppliesTo(int nVersion, std::string strSubVerIn) const
      {
 +        // TODO: rework for client-version-embedded-in-strSubVer ?
          return (IsInEffect() &&
                  nMinVer <= nVersion && nVersion <= nMaxVer &&
                  (setSubVer.empty() || setSubVer.count(strSubVerIn)));
  
      bool AppliesToMe() const
      {
 -        return AppliesTo(VERSION, ::pszSubVer);
 +        return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
      }
  
      bool RelayTo(CNode* pnode) const
diff --combined src/wallet.cpp
@@@ -5,6 -5,7 +5,6 @@@
  
  #include "headers.h"
  #include "db.h"
 -#include "cryptopp/sha.h"
  #include "crypter.h"
  
  using namespace std;
@@@ -39,19 -40,9 +39,19 @@@ bool CWallet::AddCryptedKey(const vecto
          else
              return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret);
      }
 +    return false;
  }
  
 -bool CWallet::Unlock(const string& strWalletPassphrase)
 +bool CWallet::AddCScript(const uint160 &hash, const CScript& redeemScript)
 +{
 +    if (!CCryptoKeyStore::AddCScript(hash, redeemScript))
 +        return false;
 +    if (!fFileBacked)
 +        return true;
 +    return CWalletDB(strWalletFile).WriteCScript(hash, redeemScript);
 +}
 +
 +bool CWallet::Unlock(const SecureString& strWalletPassphrase)
  {
      if (!IsLocked())
          return false;
@@@ -72,7 -63,7 +72,7 @@@
      return false;
  }
  
 -bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const string& strNewWalletPassphrase)
 +bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
  {
      bool fWasLocked = IsLocked();
  
@@@ -131,7 -122,7 +131,7 @@@ public
      )
  };
  
 -bool CWallet::EncryptWallet(const string& strWalletPassphrase)
 +bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
  {
      if (IsCrypted())
          return false;
          }
  
          Lock();
 +        Unlock(strWalletPassphrase);
 +        NewKeyPool();
 +        Lock();
 +
 +        // Need to completely rewrite the wallet file; if we don't, bdb might keep
 +        // bits of the unencrypted private key in slack space in the database file.
 +        CDB::Rewrite(strWalletFile);
      }
  
      return true;
@@@ -233,15 -217,6 +233,15 @@@ void CWallet::WalletUpdateSpent(const C
      }
  }
  
 +void CWallet::MarkDirty()
 +{
 +    CRITICAL_BLOCK(cs_wallet)
 +    {
 +        BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
 +            item.second.MarkDirty();
 +    }
 +}
 +
  bool CWallet::AddToWallet(const CWalletTx& wtxIn)
  {
      uint256 hash = wtxIn.GetHash();
          // Inserts only if not already there, returns tx inserted or tx found
          pair<map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(make_pair(hash, wtxIn));
          CWalletTx& wtx = (*ret.first).second;
 -        wtx.pwallet = this;
 +        wtx.BindWallet(this);
          bool fInsertedNew = ret.second;
          if (fInsertedNew)
              wtx.nTimeReceived = GetAdjustedTime();
          if (fInsertedNew || fUpdated)
              if (!wtx.WriteToDisk())
                  return false;
 -
 +#ifndef QT_GUI
          // If default receiving address gets used, replace it with a new one
          CScript scriptDefaultKey;
          scriptDefaultKey.SetBitcoinAddress(vchDefaultKey);
                  }
              }
          }
 -
 +#endif
          // Notify UI
          vWalletUpdated.push_back(hash);
  
      return true;
  }
  
 -bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate)
 +// Add a transaction to the wallet, or update it.
 +// pblock is optional, but should be provided if the transaction is known to be in a block.
 +// If fUpdate is true, existing transactions will be updated.
 +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock)
  {
      uint256 hash = tx.GetHash();
      CRITICAL_BLOCK(cs_wallet)
@@@ -383,24 -355,6 +383,24 @@@ int64 CWallet::GetDebit(const CTxIn &tx
      return 0;
  }
  
 +bool CWallet::IsChange(const CTxOut& txout) const
 +{
 +    CBitcoinAddress address;
 +
 +    // TODO: fix handling of 'change' outputs. The assumption is that any
 +    // payment to a TX_PUBKEYHASH that is mine but isn't in the address book
 +    // is change. That assumption is likely to break when we implement multisignature
 +    // wallets that return change back into a multi-signature-protected address;
 +    // a better way of identifying which outputs are 'the send' and which are
 +    // 'the change' will need to be implemented (maybe extend CWalletTx to remember
 +    // which output, if any, was change).
 +    if (ExtractAddress(txout.scriptPubKey, this, address))
 +        CRITICAL_BLOCK(cs_wallet)
 +            if (!mapAddressBook.count(address))
 +                return true;
 +    return false;
 +}
 +
  int64 CWalletTx::GetTxTime() const
  {
      return nTimeReceived;
@@@ -470,7 -424,8 +470,7 @@@ void CWalletTx::GetAmounts(int64& nGene
          nFee = nDebit - nValueOut;
      }
  
 -    // Sent/received.  Standard client will never generate a send-to-multiple-recipients,
 -    // but non-standard clients might (so return a list of address/amount pairs)
 +    // Sent/received.
      BOOST_FOREACH(const CTxOut& txout, vout)
      {
          CBitcoinAddress address;
@@@ -596,9 -551,6 +596,9 @@@ bool CWalletTx::WriteToDisk(
      return CWalletDB(pwallet->strWalletFile).WriteTx(GetHash(), *this);
  }
  
 +// Scan the block chain (starting in pindexStart) for transactions
 +// from or to us. If fUpdate is true, found transactions that already
 +// exist in the wallet will be updated.
  int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
  {
      int ret = 0;
      return ret;
  }
  
 +int CWallet::ScanForWalletTransaction(const uint256& hashTx)
 +{
 +    CTransaction tx;
 +    tx.ReadFromDisk(COutPoint(hashTx, 0));
 +    if (AddToWalletIfInvolvingMe(tx, NULL, true, true))
 +        return 1;
 +    return 0;
 +}
 +
  void CWallet::ReacceptWalletTransactions()
  {
      CTxDB txdb("r");
@@@ -785,21 -728,6 +785,21 @@@ int64 CWallet::GetBalance() cons
      return nTotal;
  }
  
 +int64 CWallet::GetUnconfirmedBalance() const
 +{
 +    int64 nTotal = 0;
 +    CRITICAL_BLOCK(cs_wallet)
 +    {
 +        for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
 +        {
 +            const CWalletTx* pcoin = &(*it).second;
 +            if (pcoin->IsFinal() && pcoin->IsConfirmed())
 +                continue;
 +            nTotal += pcoin->GetAvailableCredit();
 +        }
 +    }
 +    return nTotal;
 +}
  
  bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
  {
  
      // List of values less than target
      pair<int64, pair<const CWalletTx*,unsigned int> > coinLowestLarger;
 -    coinLowestLarger.first = INT64_MAX;
 +    coinLowestLarger.first = std::numeric_limits<int64>::max();
      coinLowestLarger.second.first = NULL;
      vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
      int64 nTotalLower = 0;
@@@ -968,7 -896,7 +968,7 @@@ bool CWallet::CreateTransaction(const v
      if (vecSend.empty() || nValue < 0)
          return false;
  
 -    wtxNew.pwallet = this;
 +    wtxNew.BindWallet(this);
  
      CRITICAL_BLOCK(cs_main)
      CRITICAL_BLOCK(cs_wallet)
                  int64 nChange = nValueIn - nValue - nFeeRet;
                  // if sub-cent change is required, the fee must be raised to at least MIN_TX_FEE
                  // or until nChange becomes zero
+                 // NOTE: this depends on the exact behaviour of GetMinFee
                  if (nFeeRet < MIN_TX_FEE && nChange > 0 && nChange < CENT)
                  {
                      int64 nMoveToFee = min(nChange, MIN_TX_FEE - nFeeRet);
                      vector<unsigned char> vchPubKey = reservekey.GetReservedKey();
                      // assert(mapKeys.count(vchPubKey));
  
 -                    // Fill a vout to ourself, using same address type as the payment
 +                    // Fill a vout to ourself
 +                    // TODO: pass in scriptChange instead of reservekey so
 +                    // change transaction isn't always pay-to-bitcoin-address
                      CScript scriptChange;
 -                    if (vecSend[0].first.GetBitcoinAddress().IsValid())
 -                        scriptChange.SetBitcoinAddress(vchPubKey);
 -                    else
 -                        scriptChange << vchPubKey << OP_CHECKSIG;
 +                    scriptChange.SetBitcoinAddress(vchPubKey);
  
                      // Insert change txn at random position:
                      vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size());
                  // Check that enough fee is included
                  int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000);
                  bool fAllowFree = CTransaction::AllowFree(dPriority);
-                 int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree);
+                 int64 nMinFee = wtxNew.GetMinFee(1, fAllowFree, GMF_SEND);
                  if (nFeeRet < max(nPayFee, nMinFee))
                  {
                      nFeeRet = max(nPayFee, nMinFee);
@@@ -1105,7 -1035,7 +1106,7 @@@ bool CWallet::CommitTransaction(CWallet
              BOOST_FOREACH(const CTxIn& txin, wtxNew.vin)
              {
                  CWalletTx &coin = mapWallet[txin.prevout.hash];
 -                coin.pwallet = this;
 +                coin.BindWallet(this);
                  coin.MarkSpent(txin.prevout.n);
                  coin.WriteToDisk();
                  vWalletUpdated.push_back(coin.GetHash());
@@@ -1192,18 -1122,6 +1193,18 @@@ int CWallet::LoadWallet(bool& fFirstRun
          return false;
      fFirstRunRet = false;
      int nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);
 +    if (nLoadWalletRet == DB_NEED_REWRITE)
 +    {
 +        if (CDB::Rewrite(strWalletFile, "\x04pool"))
 +        {
 +            setKeyPool.clear();
 +            // Note: can't top-up keypool here, because wallet is locked.
 +            // User will be prompted to unlock wallet the next operation
 +            // the requires a new key.
 +        }
 +        nLoadWalletRet = DB_NEED_REWRITE;
 +    }
 +
      if (nLoadWalletRet != DB_LOAD_OK)
          return nLoadWalletRet;
      fFirstRunRet = vchDefaultKey.empty();
@@@ -1289,34 -1207,6 +1290,34 @@@ bool GetWalletFile(CWallet* pwallet, st
      return true;
  }
  
 +//
 +// Mark old keypool keys as used,
 +// and generate all new keys
 +//
 +bool CWallet::NewKeyPool()
 +{
 +    CRITICAL_BLOCK(cs_wallet)
 +    {
 +        CWalletDB walletdb(strWalletFile);
 +        BOOST_FOREACH(int64 nIndex, setKeyPool)
 +            walletdb.ErasePool(nIndex);
 +        setKeyPool.clear();
 +
 +        if (IsLocked())
 +            return false;
 +
 +        int64 nKeys = max(GetArg("-keypool", 100), (int64)0);
 +        for (int i = 0; i < nKeys; i++)
 +        {
 +            int64 nIndex = i+1;
 +            walletdb.WritePool(nIndex, CKeyPool(GenerateNewKey()));
 +            setKeyPool.insert(nIndex);
 +        }
 +        printf("CWallet::NewKeyPool wrote %"PRI64d" new keys\n", nKeys);
 +    }
 +    return true;
 +}
 +
  bool CWallet::TopUpKeyPool()
  {
      CRITICAL_BLOCK(cs_wallet)
@@@ -1368,22 -1258,6 +1369,22 @@@ void CWallet::ReserveKeyFromKeyPool(int
      }
  }
  
 +int64 CWallet::AddReserveKey(const CKeyPool& keypool)
 +{
 +    CRITICAL_BLOCK(cs_main)
 +    CRITICAL_BLOCK(cs_wallet)
 +    {
 +        CWalletDB walletdb(strWalletFile);
 +
 +        int64 nIndex = 1 + *(--setKeyPool.end());
 +        if (!walletdb.WritePool(nIndex, keypool))
 +            throw runtime_error("AddReserveKey() : writing added key failed");
 +        setKeyPool.insert(nIndex);
 +        return nIndex;
 +    }
 +    return -1;
 +}
 +
  void CWallet::KeepKey(int64 nIndex)
  {
      // Remove from key pool
@@@ -1472,23 -1346,3 +1473,23 @@@ void CReserveKey::ReturnKey(
      vchPubKey.clear();
  }
  
 +void CWallet::GetAllReserveAddresses(set<CBitcoinAddress>& setAddress)
 +{
 +    setAddress.clear();
 +
 +    CWalletDB walletdb(strWalletFile);
 +
 +    CRITICAL_BLOCK(cs_main)
 +    CRITICAL_BLOCK(cs_wallet)
 +    BOOST_FOREACH(const int64& id, setKeyPool)
 +    {
 +        CKeyPool keypool;
 +        if (!walletdb.ReadPool(id, keypool))
 +            throw runtime_error("GetAllReserveKeyHashes() : read failed");
 +        CBitcoinAddress address(keypool.vchPubKey);
 +        assert(!keypool.vchPubKey.empty());
 +        if (!HaveKey(address))
 +            throw runtime_error("GetAllReserveKeyHashes() : unknown key in key pool");
 +        setAddress.insert(address);
 +    }
 +}