Direct CCoins references
authoralex <alex@alex-VirtualBox.(none)>
Tue, 21 Jan 2014 21:43:39 +0000 (01:43 +0400)
committeralex <alex@alex-VirtualBox.(none)>
Tue, 21 Jan 2014 21:43:39 +0000 (01:43 +0400)
Using the direct CCoins references to prevent excessive copying of CCoins in and out of the CCoinsView instances.

src/main.cpp
src/main.h

index 8904962..ab7a7ba 100644 (file)
@@ -237,13 +237,30 @@ bool CCoinsViewCache::GetCoinsReadOnly(uint256 txid, CCoins &coins) {
     return false;
 }
 
+std::map<uint256,CCoins>::iterator CCoinsViewCache::FetchCoins(uint256 txid) {
+    std::map<uint256,CCoins>::iterator it = cacheCoins.find(txid);
+    if (it != cacheCoins.end())
+        return it;
+    CCoins tmp;
+    if (!base->GetCoins(txid,tmp))
+        return it;
+    std::pair<std::map<uint256,CCoins>::iterator,bool> ret = cacheCoins.insert(std::make_pair(txid, tmp));
+    return ret.first;
+}
+
+CCoins &CCoinsViewCache::GetCoins(uint256 txid) {
+    std::map<uint256,CCoins>::iterator it = FetchCoins(txid);
+    assert(it != cacheCoins.end());
+    return it->second;
+}
+
 bool CCoinsViewCache::SetCoins(uint256 txid, const CCoins &coins) {
     cacheCoins[txid] = coins;
     return true;
 }
 
 bool CCoinsViewCache::HaveCoins(uint256 txid) {
-    return cacheCoins.count(txid) || base->HaveCoins(txid);
+    return FetchCoins(txid) != cacheCoins.end();
 }
 
 CBlockIndex *CCoinsViewCache::GetBestBlock() {
@@ -410,7 +427,7 @@ bool CTransaction::IsStandard() const
 // expensive-to-check-upon-redemption script like:
 //   DUP CHECKSIG DROP ... repeated 100 times... OP_1
 //
-bool CTransaction::AreInputsStandard(CCoinsView& mapInputs) const
+bool CTransaction::AreInputsStandard(CCoinsViewCache& mapInputs) const
 {
     if (IsCoinBase())
         return true; // Coinbases don't use vin normally
@@ -708,6 +725,9 @@ bool CTxMemPool::accept(CTransaction &tx, bool fCheckInputs, bool* pfMissingInpu
             }
         }
 
+        if (!tx.HaveInputs(view))
+            return error("CTxMemPool::accept() : inputs already spent");
+
         // Check for non-standard pay-to-script-hash in inputs
         if (!tx.AreInputsStandard(view) && !fTestNet)
             return error("CTxMemPool::accept() : nonstandard transaction input");
@@ -1306,37 +1326,26 @@ void CBlock::UpdateTime(const CBlockIndex* pindexPrev)
     nTime = max(GetBlockTime(), GetAdjustedTime());
 }
 
-
-CTxOut CTransaction::GetOutputFor(const CTxIn& input, CCoinsView& view)
+const CTxOut &CTransaction::GetOutputFor(const CTxIn& input, CCoinsViewCache& view)
 {
-    CCoins coins;
-    if (!view.GetCoins(input.prevout.hash, coins))
-        throw std::runtime_error("CTransaction::GetOutputFor() : prevout.hash not found");
-
-    if (input.prevout.n >= coins.vout.size())
-        throw std::runtime_error("CTransaction::GetOutputFor() : prevout.n out of range or already spent");
-
-    const CTxOut &out = coins.vout[input.prevout.n];
-    if (out.IsNull())
-        throw std::runtime_error("CTransaction::GetOutputFor() : already spent");
-
-    return out;
+    const CCoins &coins = view.GetCoins(input.prevout.hash);
+    assert(coins.IsAvailable(input.prevout.n));
+    return coins.vout[input.prevout.n];
 }
 
-int64 CTransaction::GetValueIn(CCoinsView& inputs) const
+int64 CTransaction::GetValueIn(CCoinsViewCache& inputs) const
 {
     if (IsCoinBase())
         return 0;
 
     int64 nResult = 0;
     for (unsigned int i = 0; i < vin.size(); i++)
-    {
         nResult += GetOutputFor(vin[i], inputs).nValue;
-    }
+
     return nResult;
 }
 
-unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
+unsigned int CTransaction::GetP2SHSigOpCount(CCoinsViewCache& inputs) const
 {
     if (IsCoinBase())
         return 0;
@@ -1344,29 +1353,25 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const
     unsigned int nSigOps = 0;
     for (unsigned int i = 0; i < vin.size(); i++)
     {
-        CTxOut prevout = GetOutputFor(vin[i], inputs);
+        const CTxOut &prevout = GetOutputFor(vin[i], inputs);
         if (prevout.scriptPubKey.IsPayToScriptHash())
             nSigOps += prevout.scriptPubKey.GetSigOpCount(vin[i].scriptSig);
     }
     return nSigOps;
 }
 
-bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) const
+bool CTransaction::UpdateCoins(CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight, unsigned int nTimeStamp, const uint256 &txhash) const
 {
     // mark inputs spent
     if (!IsCoinBase()) {
         BOOST_FOREACH(const CTxIn &txin, vin) {
-            CCoins coins;
-            if (!inputs.GetCoins(txin.prevout.hash, coins))
-                return error("UpdateCoins() : cannot find prevtx");
+            CCoins &coins = inputs.GetCoins(txin.prevout.hash);
             if (coins.nTime > nTimeStamp)
                 return error("UpdateCoins() : timestamp violation");
             CTxInUndo undo;
             if (!coins.Spend(txin.prevout, undo))
                 return error("UpdateCoins() : cannot spend input");
             txundo.vprevout.push_back(undo);
-            if (!inputs.SetCoins(txin.prevout.hash, coins))
-                return error("UpdateCoins() : cannot update input");
         }
     }
 
@@ -1377,9 +1382,9 @@ bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight,
     return true;
 }
 
-bool CTransaction::HaveInputs(CCoinsView &inputs) const
+bool CTransaction::HaveInputs(CCoinsViewCache &inputs) const
 {
-    if (!IsCoinBase()) {
+    if (!IsCoinBase()) { 
         // first check whether information about the prevout hash is available
         for (unsigned int i = 0; i < vin.size(); i++) {
             const COutPoint &prevout = vin[i].prevout;
@@ -1390,8 +1395,7 @@ bool CTransaction::HaveInputs(CCoinsView &inputs) const
         // then check whether the actual outputs are available
         for (unsigned int i = 0; i < vin.size(); i++) {
             const COutPoint &prevout = vin[i].prevout;
-            CCoins coins;
-            inputs.GetCoins(prevout.hash, coins);
+            const CCoins &coins = inputs.GetCoins(prevout.hash);
             if (!coins.IsAvailable(prevout.n))
                 return false;
         }
@@ -1399,28 +1403,25 @@ bool CTransaction::HaveInputs(CCoinsView &inputs) const
     return true;
 }
 
-bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
+bool CTransaction::CheckInputs(CCoinsViewCache &inputs, enum CheckSig_mode csmode, bool fStrictPayToScriptHash, bool fStrictEncodings, CBlock *pblock) const
 {
     if (!IsCoinBase())
     {
+        // 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 (!HaveInputs(inputs))
+            return error("CheckInputs() : %s inputs unavailable", GetHash().ToString().substr(0,10).c_str());
+
+        CBlockIndex *pindexBlock = inputs.GetBestBlock();
         int64 nValueIn = 0;
         int64 nFees = 0;
         for (unsigned int i = 0; i < vin.size(); i++)
         {
             const COutPoint &prevout = vin[i].prevout;
-            CCoins coins;
-            if (!inputs.GetCoins(prevout.hash, coins))
-                return error("CheckInputs() : cannot find prevout tx");
-
-            // 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 (!coins.IsAvailable(prevout.n))
-                return error("CheckInputs() : %s prev tx already used", GetHash().ToString().substr(0,10).c_str());
+            const CCoins &coins = inputs.GetCoins(prevout.hash);
 
             // If prev is coinbase or coinstake, check that it's matured
             if (coins.IsCoinBase() || coins.IsCoinStake()) {
-                CBlockIndex *pindexBlock = inputs.GetBestBlock();
                 if (pindexBlock->nHeight - coins.nHeight < nCoinbaseMaturity)
                     return error("CheckInputs() : tried to spend %s at depth %d", coins.IsCoinBase() ? "coinbase" : "coinstake", pindexBlock->nHeight - coins.nHeight);
             }
@@ -1480,8 +1481,7 @@ bool CTransaction::CheckInputs(CCoinsView &inputs, enum CheckSig_mode csmode, bo
             (csmode == CS_AFTER_CHECKPOINT && inputs.GetBestBlock()->nHeight >= Checkpoints::GetTotalBlocksEstimate())) {
             for (unsigned int i = 0; i < vin.size(); i++) {
                 const COutPoint &prevout = vin[i].prevout;
-                CCoins coins;
-                inputs.GetCoins(prevout.hash, coins);
+                const CCoins &coins = inputs.GetCoins(prevout.hash);
 
                 // Verify signature
                 if (!VerifySignature(coins, *this, i, fStrictPayToScriptHash, fStrictEncodings, 0)) {
@@ -1546,7 +1546,7 @@ bool CTransaction::ClientCheckInputs() const
     return true;
 }
 
-bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
+bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
 {
     assert(pindex == view.GetBestBlock());
 
@@ -1574,17 +1574,16 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
             continue;
 
         // check that all outputs are available
-        CCoins outs;
-        if (!view.GetCoins(hash, outs))
+        if (!view.HaveCoins(hash))
             return error("DisconnectBlock() : outputs still spent? database corrupted");
+        CCoins &outs = view.GetCoins(hash);
 
         CCoins outsBlock = CCoins(tx, pindex->nHeight, pindex->nTime);
         if (outs != outsBlock)
             return error("DisconnectBlock() : added transaction mismatch? database corrupted");
 
         // remove outputs
-        if (!view.SetCoins(hash, CCoins()))
-            return error("DisconnectBlock() : cannot delete coin outputs");
+        outs = CCoins();
 
         // restore inputs
         if (i > 0) { // not coinbases
@@ -1627,7 +1626,7 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsView &view)
 
 bool FindUndoPos(CChainDB &chaindb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
 
-bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck)
+bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck)
 {
     // Check it again in case a previous version let a bad block in
     if (!CheckBlock(!fJustCheck, !fJustCheck))
@@ -1653,8 +1652,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck
     if (fEnforceBIP30) {
         for (unsigned int i=0; i<vtx.size(); i++) {
             uint256 hash = GetTxHash(i);
-            CCoins coins;
-            if (view.GetCoins(hash, coins) && !coins.IsPruned())
+            if (view.HaveCoins(hash) && !view.GetCoins(hash).IsPruned())
                 return error("ConnectBlock() : tried to overwrite transaction");
         }
     }
index 0db9f45..038eb36 100644 (file)
@@ -109,6 +109,7 @@ class CDiskBlockPos;
 class CCoins;
 class CTxUndo;
 class CCoinsView;
+class CCoinsViewCache;
 
 void RegisterWallet(CWallet* pwalletIn);
 void UnregisterWallet(CWallet* pwalletIn);
@@ -525,7 +526,7 @@ public:
         @return True if all inputs (scriptSigs) use only standard transaction forms
         @see CTransaction::FetchInputs
     */
-    bool AreInputsStandard(CCoinsView& mapInputs) const;
+    bool AreInputsStandard(CCoinsViewCache& mapInputs) const;
 
     /** Count ECDSA signature operations the old-fashioned (pre-0.6) way
         @return number of sigops this transaction's outputs will produce when spent
@@ -539,7 +540,7 @@ public:
         @return maximum number of sigops required to validate this transaction's inputs
         @see CTransaction::FetchInputs
      */
-    unsigned int GetP2SHSigOpCount(CCoinsView& mapInputs) const;
+    unsigned int GetP2SHSigOpCount(CCoinsViewCache& mapInputs) const;
 
     /** Amount of bitcoins spent by this transaction.
         @return sum of all outputs (note: does not include fees)
@@ -564,7 +565,7 @@ public:
         @return        Sum of value of all inputs (scriptSigs)
         @see CTransaction::FetchInputs
      */
-    int64 GetValueIn(CCoinsView& mapInputs) const;
+    int64 GetValueIn(CCoinsViewCache& mapInputs) const;
 
     static bool AllowFree(double dPriority)
     {
@@ -624,14 +625,14 @@ public:
     bool ClientCheckInputs() const;
 
     // Check whether all prevouts of this transaction are present in the UTXO set represented by view
-    bool HaveInputs(CCoinsView &view) const;
+    bool HaveInputs(CCoinsViewCache &view) const;
 
     // Check whether all inputs of this transaction are valid (no double spends, scripts & sigs, amounts)
     // This does not modify the UTXO set
-    bool CheckInputs(CCoinsView &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true, CBlock *pblock=NULL) const;
+    bool CheckInputs(CCoinsViewCache &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true, CBlock *pblock=NULL) const;
 
     // Apply the effects of this transaction on the UTXO set represented by view
-    bool UpdateCoins(CCoinsView &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime, const uint256 &txhash) const;
+    bool UpdateCoins(CCoinsViewCache &view, CTxUndo &txundo, int nHeight, unsigned int nBlockTime, const uint256 &txhash) const;
 
     // Context-independent validity checks
     bool CheckTransaction() const;
@@ -640,7 +641,7 @@ public:
     bool AcceptToMemoryPool(bool fCheckInputs=true, bool* pfMissingInputs=NULL);
     bool GetCoinAge(uint64& nCoinAge) const;  // Get transaction coin age
 protected:
-    static CTxOut GetOutputFor(const CTxIn& input, CCoinsView& mapInputs);
+    static const CTxOut &GetOutputFor(const CTxIn& input, CCoinsViewCache& mapInputs);
 };
 
 
@@ -1376,10 +1377,10 @@ public:
     }
 
     // Undo the effects of this block (with given index) on the UTXO set represented by coins
-    bool DisconnectBlock(CBlockIndex *pindex, CCoinsView &coins);
+    bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins);
 
     // Apply the effects of this block (with given index) on the UTXO set represented by coins
-    bool ConnectBlock(CBlockIndex *pindex, CCoinsView &coins, bool fJustCheck=false);
+    bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false);
 
     // Read a block from disk
     bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
@@ -2023,11 +2024,14 @@ public:
     bool GetCoinsReadOnly(uint256 txid, CCoins &coins);
     bool SetCoins(uint256 txid, const CCoins &coins);
     bool HaveCoins(uint256 txid);
+    CCoins &GetCoins(uint256 txid);
     CBlockIndex *GetBestBlock();
     bool SetBestBlock(CBlockIndex *pindex);
     bool BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex);
     bool Flush();
     unsigned int GetCacheSize();
+private:
+    std::map<uint256,CCoins>::iterator FetchCoins(uint256 txid);
 };
 
 /** CCoinsView that brings transactions from a memorypool into view.