Merge branch '0.4.x' into 0.5.0.x
authorLuke Dashjr <luke-jr+git@utopios.org>
Wed, 21 Mar 2012 17:39:57 +0000 (13:39 -0400)
committerLuke Dashjr <luke-jr+git@utopios.org>
Wed, 21 Mar 2012 17:49:00 +0000 (13:49 -0400)
Conflicts:
src/main.cpp

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

diff --combined src/main.cpp
@@@ -7,6 -7,7 +7,6 @@@
  #include "db.h"
  #include "net.h"
  #include "init.h"
 -#include "cryptopp/sha.h"
  #include <boost/filesystem.hpp>
  #include <boost/filesystem/fstream.hpp>
  
@@@ -30,6 -31,7 +30,6 @@@ map<COutPoint, CInPoint> mapNextTx
  map<uint256, CBlockIndex*> mapBlockIndex;
  uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
  static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
 -const int nInitialBlockThreshold = 120; // Regard blocks up until N-threshold as "initial download"
  CBlockIndex* pindexGenesisBlock = NULL;
  int nBestHeight = -1;
  CBigNum bnBestChainWork = 0;
@@@ -38,8 -40,6 +38,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;
  
@@@ -64,14 -64,16 +64,14 @@@ int fUseUPnP = false
  #endif
  
  
 -
 -
 -
 -
 -
  //////////////////////////////////////////////////////////////////////////////
  //
  // dispatching functions
  //
  
 +// These functions dispatch to one or all registered wallets
 +
 +
  void RegisterWallet(CWallet* pwalletIn)
  {
      CRITICAL_BLOCK(cs_setpwalletRegistered)
@@@ -88,7 -90,6 +88,7 @@@ void UnregisterWallet(CWallet* pwalletI
      }
  }
  
 +// check whether the passed transaction is from us
  bool static IsFromMe(CTransaction& tx)
  {
      BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered)
@@@ -97,7 -98,6 +97,7 @@@
      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)
@@@ -321,24 -314,24 +321,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;
@@@ -375,21 -368,12 +375,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)
          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() : transaction with out-of-bounds SigOpCount");
      // Rather not work on nonstandard transactions (unless -testnet)
      if (!fTestNet && !IsStandard())
          return error("AcceptToMemoryPool() : nonstandard transaction type");
  
      if (fCheckInputs)
      {
-         // Check against previous transactions
+         MapPrevTx mapInputs;
          map<uint256, CTxIndex> mapUnused;
-         int64 nFees = 0;
          bool fInvalid = false;
-         if (!ConnectInputs(txdb, mapUnused, CDiskTxPos(1,1,1), pindexBest, nFees, false, false, 0, fInvalid))
+         if (!FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
          {
              if (fInvalid)
                  return error("AcceptToMemoryPool() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str());
-             return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
+             if (pfMissingInputs)
+                 *pfMissingInputs = true;
+             return error("AcceptToMemoryPool() : FetchInputs failed %s", hash.ToString().substr(0,10).c_str());
          }
  
+         // 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");
++            return error("AcceptToMemoryPool() : transaction with out-of-bounds SigOpCount");
+         int64 nFees = GetValueIn(mapInputs)-GetValueOut();
          // Don't accept it if it can't get into a block
          if (nFees < GetMinFee(1000, true, true))
              return error("AcceptToMemoryPool() : not enough fees");
                  dFreeCount += nSize;
              }
          }
+         // Check against previous transactions
+         // This is done last to help prevent CPU exhaustion denial-of-service attacks.
+         if (!ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false))
+         {
+             return error("AcceptToMemoryPool() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
+         }
      }
  
      // Store transaction in memory
@@@ -785,15 -788,9 +795,15 @@@ bool CheckProofOfWork(uint256 hash, uns
      return true;
  }
  
 +// Return maximum amount of blocks that other nodes claim to have
 +int GetNumBlocksOfPeers()
 +{
 +    return std::max(cPeerBlockCounts.median(), Checkpoints::GetTotalBlocksEstimate());
 +}
 +
  bool IsInitialBlockDownload()
  {
 -    if (pindexBest == NULL || nBestHeight < (Checkpoints::GetTotalBlocksEstimate()-nInitialBlockThreshold))
 +    if (pindexBest == NULL || nBestHeight < Checkpoints::GetTotalBlocksEstimate())
          return true;
      static int64 nLastUpdate;
      static CBlockIndex* pindexLastBest;
@@@ -875,9 -872,8 +885,8 @@@ 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& fInvalid)
+ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTestPool,
+                                bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid)
  {
      // FetchInputs can return false either because we just haven't seen some inputs
      // (in which case the transaction should be stored as an orphan)
      // be dropped).  If tx is definitely invalid, fInvalid will be set to true.
      fInvalid = false;
  
+     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());
+         }
+     }
+     // Make sure all prevout.n's are valid:
+     for (int i = 0; i < vin.size(); i++)
+     {
+         const COutPoint prevout = vin[i].prevout;
+         assert(inputsRet.count(prevout.hash) != 0);
+         const CTxIndex& txindex = inputsRet[prevout.hash].first;
+         const CTransaction& txPrev = inputsRet[prevout.hash].second;
+         if (prevout.n >= txPrev.vout.size() || prevout.n >= txindex.vSpent.size())
+         {
+             // Revisit this if/when transaction replacement is implemented and allows
+             // adding inputs:
+             fInvalid = true;
 -            return error("FetchInputs() : %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("FetchInputs() : %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 true;
+ }
+ const CTxOut& CTransaction::GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const
+ {
+     MapPrevTx::const_iterator mi = inputs.find(input.prevout.hash);
+     if (mi == inputs.end())
+         throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
+     const CTransaction& txPrev = (mi->second).second;
+     if (input.prevout.n >= txPrev.vout.size())
+         throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range");
+     return txPrev.vout[input.prevout.n];
+ }
+ int64 CTransaction::GetValueIn(const MapPrevTx& inputs) const
+ {
+     if (IsCoinBase())
+         return 0;
+     int64 nResult = 0;
+     for (int i = 0; i < vin.size(); i++)
+     {
+         nResult += GetOutputFor(vin[i], inputs).nValue;
+     }
+     return nResult;
+ }
+ int CTransaction::GetP2SHSigOpCount(const MapPrevTx& inputs) const
+ {
+     if (IsCoinBase())
+         return 0;
+     int nSigOps = 0;
+     for (int i = 0; i < vin.size(); i++)
+     {
+         const CTxOut& prevout = GetOutputFor(vin[i], inputs);
+         if (prevout.scriptPubKey.IsPayToScriptHash())
+             nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
+     }
+     return nSigOps;
+ }
+ bool CTransaction::ConnectInputs(MapPrevTx inputs,
+                                  map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
+                                  const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash)
+ {
      // 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
      if (!IsCoinBase())
      {
          int64 nValueIn = 0;
+         int64 nFees = 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())
-             {
-                 // Revisit this if/when transaction replacement is implemented and allows
-                 // adding inputs:
-                 fInvalid = true;
 -                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())
-                 for (CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
+                 for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
                      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);
  
-             // Skip ECDSA signature verification when connecting blocks (fBlock=true)
-             // 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 && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
-                 // Verify signature
-                 if (!VerifySignature(txPrev, *this, i))
-                     return DoS(100,error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str()));
              // 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"));
  
 -            // Verify signature
 -            if (!VerifySignature(txPrev, *this, i, fStrictPayToScriptHash, 0))
 -                return error("ConnectInputs() : %s VerifySignature failed", GetHash().ToString().substr(0,10).c_str());
++            // Skip ECDSA signature verification when connecting blocks (fBlock=true)
++            // 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 && (nBestHeight < Checkpoints::GetTotalBlocksEstimate())))
++            {
++                // 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
              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)
-     {
-         // Add transaction to changes
-         mapTestPool[GetHash()] = CTxIndex(posThisTx, vout.size());
-     }
-     else if (fMiner)
-     {
-         // Add transaction to test pool
-         mapTestPool[GetHash()] = CTxIndex(CDiskTxPos(1,1,1), vout.size());
-     }
      return true;
  }
  
@@@ -1026,7 -1077,7 +1105,7 @@@ bool CTransaction::ClientConnectInputs(
                  return false;
  
              // Verify signature
-             if (!VerifySignature(txPrev, *this, i))
+             if (!VerifySignature(txPrev, *this, i, true, 0))
                  return error("ConnectInputs() : VerifySignature failed");
  
              ///// this is redundant with the mapNextTx stuff, not sure which I want to get rid of
@@@ -1099,20 -1150,51 +1178,51 @@@ bool CBlock::ConnectBlock(CTxDB& txdb, 
                          return false;
          }
  
+     // P2SH didn't become active until Apr 1 2012 (Feb 15 on testnet)
+     int64 nEvalSwitchTime = fTestNet ? 1329264000 : 1333238400;
+     bool fStrictPayToScriptHash = (pindex->nTime >= nEvalSwitchTime);
      //// issue here: it doesn't know the version
      unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size());
  
      map<uint256, CTxIndex> mapQueuedChanges;
      int64 nFees = 0;
+     int nSigOps = 0;
      BOOST_FOREACH(CTransaction& tx, vtx)
      {
+         nSigOps += tx.GetSigOpCount();
+         if (nSigOps > MAX_BLOCK_SIGOPS)
 -            return error("ConnectBlock() : too many sigops");
++            return DoS(100, error("ConnectBlock() : too many sigops"));
          CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos);
          nTxPos += ::GetSerializeSize(tx, SER_DISK);
  
          bool fInvalid;
-         if (!tx.ConnectInputs(txdb, mapQueuedChanges, posThisTx, pindex, nFees, true, false, 0, fInvalid))
-             return false;
+         MapPrevTx mapInputs;
+         if (!tx.IsCoinBase())
+         {
+             if (!tx.FetchInputs(txdb, mapQueuedChanges, true, false, mapInputs, fInvalid))
+                 return false;
+             if (fStrictPayToScriptHash)
+             {
+                 // Add in sigops done by pay-to-script-hash inputs;
+                 // this is to prevent a "rogue miner" from creating
+                 // an incredibly-expensive-to-validate block.
+                 nSigOps += tx.GetP2SHSigOpCount(mapInputs);
+                 if (nSigOps > MAX_BLOCK_SIGOPS)
 -                    return error("ConnectBlock() : too many sigops");
++                    return DoS(100, error("ConnectBlock() : too many sigops"));
+             }
+             nFees += tx.GetValueIn(mapInputs)-tx.GetValueOut();
+             if (!tx.ConnectInputs(mapInputs, mapQueuedChanges, posThisTx, pindex, true, false, fStrictPayToScriptHash))
+                 return false;
+         }
+         mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size());
      }
      // Write queued txindex changes
      for (map<uint256, CTxIndex>::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi)
      {
@@@ -1351,11 -1433,11 +1461,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() : out-of-bounds SigOpCount");
 +        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;
  }
@@@ -1394,13 -1476,13 +1504,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, this))
 -        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 (!Checkpoints::CheckBlock(nHeight, hash))
 -        return error("AcceptBlock() : rejected by checkpoint lockin at %d", nHeight);
 +        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();
          int64 deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
          if (deltaTime < 0)
          {
 +            if (pfrom)
 +                pfrom->Misbehaving(100);
              return error("ProcessBlock() : block with timestamp before last checkpoint");
          }
          CBigNum bnNewBlock;
          bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
          if (bnNewBlock > bnRequired)
          {
 +            if (pfrom)
 +                pfrom->Misbehaving(100);
              return error("ProcessBlock() : block with too little proof-of-work");
          }
      }
@@@ -1895,10 -1973,7 +2005,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;
          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;
      }
  
          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)
          {
              if (nEvicted > 0)
                  printf("mapOrphan overflow, removed %d tx\n", nEvicted);
          }
 +        if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
      }
  
  
  
          if (ProcessBlock(pfrom, &block))
              mapAlreadyAskedFor.erase(inv);
 +        if (block.nDoS) pfrom->Misbehaving(block.nDoS);
      }
  
  
@@@ -2740,25 -2801,15 +2850,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];
  }
  
  //
@@@ -2916,6 -2967,8 +3026,8 @@@ CBlock* CreateNewBlock(CReserveKey& res
              unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK);
              if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
                  continue;
+             // Legacy limits on sigOps:
              int nTxSigOps = tx.GetSigOpCount();
              if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
                  continue;
              // because we're already processing them in order of dependency
              map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
              bool fInvalid;
-             if (!tx.ConnectInputs(txdb, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, nFees, false, true, nMinFee, fInvalid))
+             MapPrevTx mapInputs;
+             if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
+                 continue;
+             int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
+             if (nTxFees < nMinFee)
+                 continue;
+             nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
+             if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+                 continue;
+             if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
                  continue;
+             mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
              swap(mapTestPool, mapTestPoolTmp);
  
              // Added
              pblock->vtx.push_back(tx);
              nBlockSize += nTxSize;
              nBlockSigOps += nTxSigOps;
+             nFees += nTxFees;
  
              // Add transactions that depend on this one to the priority queue
              uint256 hash = tx.GetHash();
@@@ -3060,6 -3127,7 +3186,6 @@@ bool CheckWork(CBlock* pblock, CWallet
              return error("BitcoinMiner : ProcessBlock, block not accepted");
      }
  
 -    Sleep(2000);
      return true;
  }
  
diff --combined src/main.h
@@@ -86,7 -86,6 +86,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);
@@@ -101,7 -100,6 +101,7 @@@ void FormatHashBuffers(CBlock* pblock, 
  bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
  bool CheckProofOfWork(uint256 hash, unsigned int nBits);
  unsigned int ComputeMinWork(unsigned int nBase, int64 nTime);
 +int GetNumBlocksOfPeers();
  bool IsInitialBlockDownload();
  std::string GetWarnings(std::string strFor);
  
@@@ -388,6 -386,7 +388,7 @@@ public
  };
  
  
+ typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
  
  
  //
@@@ -402,9 -401,6 +403,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 n;
      }
  
+     /** Count ECDSA signature operations in pay-to-script-hash inputs.
+         This is a better measure of how expensive it is to process this transaction.
+         @param[in] mapInputsMap of previous transactions that have outputs we're spending
+         @return maximum number of sigops required to validate this transaction's inputs
+         @see CTransaction::FetchInputs
+     */
+     int GetP2SHSigOpCount(const MapPrevTx& mapInputs) const;
      bool IsStandard() const
      {
          BOOST_FOREACH(const CTxIn& txin, vin)
          return nValueOut;
      }
  
+     /** Amount of bitcoins coming in to this transaction
+         Note that lightweight clients may not know anything besides the hash of previous transactions,
+         so may not be able to calculate this.
+         @param[in] mapInputsMap of previous transactions that have outputs we're spending
+         @returnSum of value of all inputs (scriptSigs)
+         @see CTransaction::FetchInputs
+     */
+     int64 GetValueIn(const MapPrevTx& mapInputs) const;
      static bool AllowFree(double dPriority)
      {
          // Large (in bytes) low-priority (new, small-coin) transactions
      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,
-                        bool& fInvalid);
+     /** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
+      @param[in] txdb  Transaction database
+      @param[in] mapTestPool   List of pending changes to the transaction index database
+      @param[in] fBlock        True if being called to add a new best-block to the chain
+      @param[in] fMiner        True if being called by CreateNewBlock
+      @param[out] inputsRet    Pointers to this transaction's inputs
+      @param[out] fInvalid     returns true if transaction is invalid
+      @return  Returns true if all inputs are in txdb or mapTestPool
+      */
+     bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
+                      bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
+     /** Sanity check previous transactions, then, if all checks succeed,
+         mark them as spent by this transaction.
+         @param[in] inputsPrevious transactions (from FetchInputs)
+         @param[out] mapTestPoolKeeps track of inputs that need to be updated on disk
+         @param[in] posThisTxPosition of this transaction on disk
+         @param[in] pindexBlock
+         @param[in] fBlock  true if called from ConnectBlock
+         @param[in] fMiner  true if called from CreateNewBlock
+         @param[in] fStrictPayToScriptHash  true if fully validating p2sh transactions
+         @return Returns true if all checks succeed
+     */
+     bool ConnectInputs(MapPrevTx inputs,
+                        std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
+                        const CBlockIndex* pindexBlock, bool fBlock, bool fMiner, bool fStrictPayToScriptHash=true);
      bool ClientConnectInputs();
      bool CheckTransaction() const;
      bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
      bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
  protected:
+     const CTxOut& GetOutputFor(const CTxIn& input, const MapPrevTx& inputs) const;
      bool AddToMemoryPoolUnchecked();
  public:
      bool RemoveFromMemoryPool();
@@@ -794,9 -835,6 +841,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
          fflush(fileout);
          if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
          {
 -#ifdef __WXMSW__
 +#ifdef WIN32
              _commit(_fileno(fileout));
  #else
              fsync(fileno(fileout));
diff --combined src/script.cpp
@@@ -1132,7 -1132,6 +1132,7 @@@ bool static ExtractAddressInner(const C
          if (keystore == NULL || keystore->HaveKey(addressRet))
              return true;
      }
 +
      return false;
  }
  
@@@ -1147,16 -1146,40 +1147,40 @@@ bool ExtractAddress(const CScript& scri
  }
  
  
- bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType)
+ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
+                   bool fValidatePayToScriptHash, int nHashType)
  {
-     vector<vector<unsigned char> > stack;
+     vector<vector<unsigned char> > stack, stackCopy;
      if (!EvalScript(stack, scriptSig, txTo, nIn, nHashType))
          return false;
+     if (fValidatePayToScriptHash)
+         stackCopy = stack;
      if (!EvalScript(stack, scriptPubKey, txTo, nIn, nHashType))
          return false;
      if (stack.empty())
          return false;
-     return CastToBool(stack.back());
+     if (CastToBool(stack.back()) == false)
+         return false;
+     // Additional validation for spend-to-script-hash transactions:
+     if (fValidatePayToScriptHash && scriptPubKey.IsPayToScriptHash())
+     {
+         if (!scriptSig.IsPushOnly()) // scriptSig must be literals-only
+             return false;            // or validation fails
+         const valtype& pubKeySerialized = stackCopy.back();
+         CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
+         popstack(stackCopy);
+         if (!EvalScript(stackCopy, pubKey2, txTo, nIn, nHashType))
+             return false;
+         if (stackCopy.empty())
+             return false;
+         return CastToBool(stackCopy.back());
+     }
+     return true;
  }
  
  
@@@ -1178,14 -1201,14 +1202,14 @@@ bool SignSignature(const CKeyStore &key
  
      // Test solution
      if (scriptPrereq.empty())
-         if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, 0))
+         if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, true, 0))
              return false;
  
      return true;
  }
  
  
- bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType)
+ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType)
  {
      assert(nIn < txTo.vin.size());
      const CTxIn& txin = txTo.vin[nIn];
      if (txin.prevout.hash != txFrom.GetHash())
          return false;
  
-     if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, nHashType))
+     if (!VerifyScript(txin.scriptSig, txout.scriptPubKey, txTo, nIn, fValidatePayToScriptHash, nHashType))
          return false;
  
      return true;
  }
+ int CScript::GetSigOpCount(bool fAccurate) const
+ {
+     int n = 0;
+     const_iterator pc = begin();
+     opcodetype lastOpcode = OP_INVALIDOPCODE;
+     while (pc < end())
+     {
+         opcodetype opcode;
+         if (!GetOp(pc, opcode))
+             break;
+         if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY)
+             n++;
+         else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY)
+         {
+             if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16)
+                 n += DecodeOP_N(lastOpcode);
+             else
+                 n += 20;
+         }
+         lastOpcode = opcode;
+     }
+     return n;
+ }
+ int CScript::GetSigOpCount(const CScript& scriptSig) const
+ {
+     if (!IsPayToScriptHash())
+         return GetSigOpCount(true);
+     // This is a pay-to-script-hash scriptPubKey;
+     // get the last item that the scriptSig
+     // pushes onto the stack:
+     const_iterator pc = scriptSig.begin();
+     vector<unsigned char> data;
+     while (pc < scriptSig.end())
+     {
+         opcodetype opcode;
+         if (!scriptSig.GetOp(pc, opcode, data))
+             return 0;
+         if (opcode > OP_16)
+             return 0;
+     }
+     /// ... and return it's opcount:
+     CScript subscript(data.begin(), data.end());
+     return subscript.GetSigOpCount(true);
+ }
+ bool CScript::IsPayToScriptHash() const
+ {
+     // Extra-fast test for pay-to-script-hash CScripts:
+     return (this->size() == 23 &&
+             this->at(0) == OP_HASH160 &&
+             this->at(1) == 0x14 &&
+             this->at(22) == OP_EQUAL);
+ }