From 64dd46fd05de1dd8ff5066e192e1345f733c6a1f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 8 Jul 2012 00:06:34 +0200 Subject: [PATCH] Transaction hash caching Use CBlock's vMerkleTree to cache transaction hashes, and pass them along as argument in more function calls. During initial block download, this results in every transaction's hash to be only computed once. --- src/main.cpp | 40 +++++++++++++++++++-------------------- src/main.h | 10 ++++++++-- src/rpcrawtransaction.cpp | 2 +- src/wallet.cpp | 5 ++--- src/wallet.h | 2 +- 5 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index c23aae320..a4f90dda7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,10 +111,10 @@ void static EraseFromWallets(uint256 hash) } // make sure all wallets know about the given transaction, in the given block -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate) +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate) { BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) - pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); + pwallet->AddToWalletIfInvolvingMe(hash, tx, pblock, fUpdate); } // notify wallets about a new best chain @@ -1197,10 +1197,8 @@ unsigned int CTransaction::GetP2SHSigOpCount(CCoinsView& inputs) const return nSigOps; } -bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight) const +bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight, const uint256 &txhash) const { - uint256 hash = GetHash(); - // mark inputs spent if (!IsCoinBase()) { BOOST_FOREACH(const CTxIn &txin, vin) { @@ -1217,7 +1215,7 @@ bool CTransaction::UpdateCoins(CCoinsView &inputs, CTxUndo &txundo, int nHeight) } // add outputs - if (!inputs.SetCoins(hash, CCoins(*this, nHeight))) + if (!inputs.SetCoins(txhash, CCoins(*this, nHeight))) return error("UpdateCoins() : cannot update output"); return true; @@ -1467,8 +1465,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck bool fEnforceBIP30 = !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { - BOOST_FOREACH(CTransaction& tx, vtx) { - uint256 hash = tx.GetHash(); + for (unsigned int i=0; i MAX_BLOCK_SIGOPS) return DoS(100, error("ConnectBlock() : too many sigops")); @@ -1511,7 +1511,7 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck } CTxUndo txundo; - if (!tx.UpdateCoins(view, txundo, pindex->nHeight)) + if (!tx.UpdateCoins(view, txundo, pindex->nHeight, GetTxHash(i))) return error("ConnectBlock() : UpdateInputs failed"); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); @@ -1542,8 +1542,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsView &view, bool fJustCheck return false; // Watch for transactions paying to me - BOOST_FOREACH(CTransaction& tx, vtx) - SyncWithWallets(tx, this, true); + for (unsigned int i=0; i uniqueTx; - BOOST_FOREACH(const CTransaction& tx, vtx) - { - uniqueTx.insert(tx.GetHash()); + for (unsigned int i=0; inHeight+1)) + uint256 hash = tx.GetHash(); + if (!tx.UpdateCoins(viewTemp, txundo, pindexPrev->nHeight+1, hash)) continue; // push changes from the second layer cache to the first one @@ -3749,7 +3750,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) } // Add transactions that depend on this one to the priority queue - uint256 hash = tx.GetHash(); if (mapDependers.count(hash)) { BOOST_FOREACH(COrphan* porphan, mapDependers[hash]) diff --git a/src/main.h b/src/main.h index 0ec68b612..3ee0108f4 100644 --- a/src/main.h +++ b/src/main.h @@ -91,7 +91,7 @@ class CCoinsView; void RegisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn); -void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); +void SyncWithWallets(const uint256 &hash, const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); bool ProcessBlock(CNode* pfrom, CBlock* pblock); bool CheckDiskSpace(uint64 nAdditionalBytes=0); FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false); @@ -577,7 +577,7 @@ public: bool CheckInputs(CCoinsView &view, enum CheckSig_mode csmode, bool fStrictPayToScriptHash=true, bool fStrictEncodings=true) const; // Apply the effects of this transaction on the UTXO set represented by view - bool UpdateCoins(CCoinsView &view, CTxUndo &txundo, int nHeight) const; + bool UpdateCoins(CCoinsView &view, CTxUndo &txundo, int nHeight, const uint256 &txhash) const; // Context-independent validity checks bool CheckTransaction() const; @@ -1111,6 +1111,12 @@ public: return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); } + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + std::vector GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 647384f33..72a89c08c 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -517,7 +517,7 @@ Value sendrawtransaction(const Array& params, bool fHelp) // Not in block, but already in the memory pool; will drop // through to re-relay it. } else { - SyncWithWallets(tx, NULL, true); + SyncWithWallets(hashTx, tx, NULL, true); } RelayMessage(CInv(MSG_TX, hashTx), tx); diff --git a/src/wallet.cpp b/src/wallet.cpp index 5b3203469..1498ff28b 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -479,9 +479,8 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) // 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) +bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fFindBlock) { - uint256 hash = tx.GetHash(); { LOCK(cs_wallet); bool fExisted = mapWallet.count(hash); @@ -756,7 +755,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) block.ReadFromDisk(pindex, true); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) + if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) ret++; } pindex = pindex->pnext; diff --git a/src/wallet.h b/src/wallet.h index 8c98f34a5..43b695c59 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -162,7 +162,7 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn); - bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); + bool AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction& tx, const CBlock* pblock, bool fUpdate = false, bool fFindBlock = false); bool EraseFromWallet(uint256 hash); void WalletUpdateSpent(const CTransaction& prevout); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);