Browse Source

Merge #9125: Make CBlock a vector of shared_ptr of CTransactions

b4e4ba4 Introduce convenience type CTransactionRef (Pieter Wuille)
1662b43 Make CBlock::vtx a vector of shared_ptr<CTransaction> (Pieter Wuille)
da60506 Add deserializing constructors to CTransaction and CMutableTransaction (Pieter Wuille)
0e85204 Add serialization for unique_ptr and shared_ptr (Pieter Wuille)
0.14
Wladimir J. van der Laan 8 years ago
parent
commit
7490ae8b69
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 14
      src/blockencodings.cpp
  2. 12
      src/blockencodings.h
  3. 2
      src/chainparams.cpp
  4. 6
      src/consensus/merkle.cpp
  5. 4
      src/core_memusage.h
  6. 93
      src/main.cpp
  7. 6
      src/merkleblock.cpp
  8. 12
      src/miner.cpp
  9. 2
      src/primitives/block.cpp
  10. 2
      src/primitives/block.h
  11. 4
      src/primitives/transaction.cpp
  12. 15
      src/primitives/transaction.h
  13. 6
      src/rpc/blockchain.cpp
  14. 5
      src/rpc/mining.cpp
  15. 4
      src/rpc/rawtransaction.cpp
  16. 59
      src/serialize.h
  17. 63
      src/test/blockencodings_tests.cpp
  18. 8
      src/test/mempool_tests.cpp
  19. 8
      src/test/merkle_tests.cpp
  20. 33
      src/test/miner_tests.cpp
  21. 4
      src/test/pmt_tests.cpp
  22. 14
      src/test/policyestimator_tests.cpp
  23. 8
      src/test/test_bitcoin.cpp
  24. 4
      src/test/test_bitcoin.h
  25. 26
      src/txmempool.cpp
  26. 16
      src/txmempool.h
  27. 2
      src/wallet/wallet.cpp

14
src/blockencodings.cpp

@ -24,7 +24,7 @@ CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool f
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase //TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
prefilledtxn[0] = {0, block.vtx[0]}; prefilledtxn[0] = {0, block.vtx[0]};
for (size_t i = 1; i < block.vtx.size(); i++) { for (size_t i = 1; i < block.vtx.size(); i++) {
const CTransaction& tx = block.vtx[i]; const CTransaction& tx = *block.vtx[i];
shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash()); shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
} }
} }
@ -59,7 +59,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
int32_t lastprefilledindex = -1; int32_t lastprefilledindex = -1;
for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) { for (size_t i = 0; i < cmpctblock.prefilledtxn.size(); i++) {
if (cmpctblock.prefilledtxn[i].tx.IsNull()) if (cmpctblock.prefilledtxn[i].tx->IsNull())
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so cant overflow here lastprefilledindex += cmpctblock.prefilledtxn[i].index + 1; //index is a uint16_t, so cant overflow here
@ -71,7 +71,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
// have neither a prefilled txn or a shorttxid! // have neither a prefilled txn or a shorttxid!
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
} }
txn_available[lastprefilledindex] = std::make_shared<CTransaction>(cmpctblock.prefilledtxn[i].tx); txn_available[lastprefilledindex] = cmpctblock.prefilledtxn[i].tx;
} }
prefilled_count = cmpctblock.prefilledtxn.size(); prefilled_count = cmpctblock.prefilledtxn.size();
@ -142,7 +142,7 @@ bool PartiallyDownloadedBlock::IsTxAvailable(size_t index) const {
return txn_available[index] ? true : false; return txn_available[index] ? true : false;
} }
ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const { ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing) const {
assert(!header.IsNull()); assert(!header.IsNull());
block = header; block = header;
block.vtx.resize(txn_available.size()); block.vtx.resize(txn_available.size());
@ -154,7 +154,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
block.vtx[i] = vtx_missing[tx_missing_offset++]; block.vtx[i] = vtx_missing[tx_missing_offset++];
} else } else
block.vtx[i] = *txn_available[i]; block.vtx[i] = txn_available[i];
} }
if (vtx_missing.size() != tx_missing_offset) if (vtx_missing.size() != tx_missing_offset)
return READ_STATUS_INVALID; return READ_STATUS_INVALID;
@ -172,8 +172,8 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size()); LogPrint("cmpctblock", "Successfully reconstructed block %s with %lu txn prefilled, %lu txn from mempool and %lu txn requested\n", header.GetHash().ToString(), prefilled_count, mempool_count, vtx_missing.size());
if (vtx_missing.size() < 5) { if (vtx_missing.size() < 5) {
for(const CTransaction& tx : vtx_missing) for (const auto& tx : vtx_missing)
LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx.GetHash().ToString()); LogPrint("cmpctblock", "Reconstructed block %s required tx %s\n", header.GetHash().ToString(), tx->GetHash().ToString());
} }
return READ_STATUS_OK; return READ_STATUS_OK;

12
src/blockencodings.h

@ -14,9 +14,9 @@ class CTxMemPool;
// Dumb helper to handle CTransaction compression at serialize-time // Dumb helper to handle CTransaction compression at serialize-time
struct TransactionCompressor { struct TransactionCompressor {
private: private:
CTransaction& tx; CTransactionRef& tx;
public: public:
TransactionCompressor(CTransaction& txIn) : tx(txIn) {} TransactionCompressor(CTransactionRef& txIn) : tx(txIn) {}
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -72,7 +72,7 @@ class BlockTransactions {
public: public:
// A BlockTransactions message // A BlockTransactions message
uint256 blockhash; uint256 blockhash;
std::vector<CTransaction> txn; std::vector<CTransactionRef> txn;
BlockTransactions() {} BlockTransactions() {}
BlockTransactions(const BlockTransactionsRequest& req) : BlockTransactions(const BlockTransactionsRequest& req) :
@ -104,7 +104,7 @@ struct PrefilledTransaction {
// Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs, // Used as an offset since last prefilled tx in CBlockHeaderAndShortTxIDs,
// as a proper transaction-in-block-index in PartiallyDownloadedBlock // as a proper transaction-in-block-index in PartiallyDownloadedBlock
uint16_t index; uint16_t index;
CTransaction tx; CTransactionRef tx;
ADD_SERIALIZE_METHODS; ADD_SERIALIZE_METHODS;
@ -193,7 +193,7 @@ public:
class PartiallyDownloadedBlock { class PartiallyDownloadedBlock {
protected: protected:
std::vector<std::shared_ptr<const CTransaction> > txn_available; std::vector<CTransactionRef> txn_available;
size_t prefilled_count = 0, mempool_count = 0; size_t prefilled_count = 0, mempool_count = 0;
CTxMemPool* pool; CTxMemPool* pool;
public: public:
@ -202,7 +202,7 @@ public:
ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock); ReadStatus InitData(const CBlockHeaderAndShortTxIDs& cmpctblock);
bool IsTxAvailable(size_t index) const; bool IsTxAvailable(size_t index) const;
ReadStatus FillBlock(CBlock& block, const std::vector<CTransaction>& vtx_missing) const; ReadStatus FillBlock(CBlock& block, const std::vector<CTransactionRef>& vtx_missing) const;
}; };
#endif #endif

2
src/chainparams.cpp

@ -31,7 +31,7 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
genesis.nBits = nBits; genesis.nBits = nBits;
genesis.nNonce = nNonce; genesis.nNonce = nNonce;
genesis.nVersion = nVersion; genesis.nVersion = nVersion;
genesis.vtx.push_back(txNew); genesis.vtx.push_back(MakeTransactionRef(std::move(txNew)));
genesis.hashPrevBlock.SetNull(); genesis.hashPrevBlock.SetNull();
genesis.hashMerkleRoot = BlockMerkleRoot(genesis); genesis.hashMerkleRoot = BlockMerkleRoot(genesis);
return genesis; return genesis;

6
src/consensus/merkle.cpp

@ -160,7 +160,7 @@ uint256 BlockMerkleRoot(const CBlock& block, bool* mutated)
std::vector<uint256> leaves; std::vector<uint256> leaves;
leaves.resize(block.vtx.size()); leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) { for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetHash(); leaves[s] = block.vtx[s]->GetHash();
} }
return ComputeMerkleRoot(leaves, mutated); return ComputeMerkleRoot(leaves, mutated);
} }
@ -171,7 +171,7 @@ uint256 BlockWitnessMerkleRoot(const CBlock& block, bool* mutated)
leaves.resize(block.vtx.size()); leaves.resize(block.vtx.size());
leaves[0].SetNull(); // The witness hash of the coinbase is 0. leaves[0].SetNull(); // The witness hash of the coinbase is 0.
for (size_t s = 1; s < block.vtx.size(); s++) { for (size_t s = 1; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetWitnessHash(); leaves[s] = block.vtx[s]->GetWitnessHash();
} }
return ComputeMerkleRoot(leaves, mutated); return ComputeMerkleRoot(leaves, mutated);
} }
@ -181,7 +181,7 @@ std::vector<uint256> BlockMerkleBranch(const CBlock& block, uint32_t position)
std::vector<uint256> leaves; std::vector<uint256> leaves;
leaves.resize(block.vtx.size()); leaves.resize(block.vtx.size());
for (size_t s = 0; s < block.vtx.size(); s++) { for (size_t s = 0; s < block.vtx.size(); s++) {
leaves[s] = block.vtx[s].GetHash(); leaves[s] = block.vtx[s]->GetHash();
} }
return ComputeMerkleBranch(leaves, position); return ComputeMerkleBranch(leaves, position);
} }

4
src/core_memusage.h

@ -69,8 +69,8 @@ static inline size_t RecursiveDynamicUsage(const CMutableTransaction& tx) {
static inline size_t RecursiveDynamicUsage(const CBlock& block) { static inline size_t RecursiveDynamicUsage(const CBlock& block) {
size_t mem = memusage::DynamicUsage(block.vtx); size_t mem = memusage::DynamicUsage(block.vtx);
for (std::vector<CTransaction>::const_iterator it = block.vtx.begin(); it != block.vtx.end(); it++) { for (const auto& tx : block.vtx) {
mem += RecursiveDynamicUsage(*it); mem += memusage::DynamicUsage(tx) + RecursiveDynamicUsage(*tx);
} }
return mem; return mem;
} }

93
src/main.cpp

@ -233,7 +233,7 @@ namespace {
int nPeersWithValidatedDownloads = 0; int nPeersWithValidatedDownloads = 0;
/** Relay map, protected by cs_main. */ /** Relay map, protected by cs_main. */
typedef std::map<uint256, std::shared_ptr<const CTransaction>> MapRelay; typedef std::map<uint256, CTransactionRef> MapRelay;
MapRelay mapRelay; MapRelay mapRelay;
/** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */ /** Expiration-time ordered list of (expire time, relay map entry) pairs, protected by cs_main). */
std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration; std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration;
@ -1639,7 +1639,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
LOCK(cs_main); LOCK(cs_main);
std::shared_ptr<const CTransaction> ptx = mempool.get(hash); CTransactionRef ptx = mempool.get(hash);
if (ptx) if (ptx)
{ {
txOut = *ptx; txOut = *ptx;
@ -1682,9 +1682,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::P
if (pindexSlow) { if (pindexSlow) {
CBlock block; CBlock block;
if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) { if (ReadBlockFromDisk(block, pindexSlow, consensusParams)) {
BOOST_FOREACH(const CTransaction &tx, block.vtx) { for (const auto& tx : block.vtx) {
if (tx.GetHash() == hash) { if (tx->GetHash() == hash) {
txOut = tx; txOut = *tx;
hashBlock = pindexSlow->GetBlockHash(); hashBlock = pindexSlow->GetBlockHash();
return true; return true;
} }
@ -2223,7 +2223,7 @@ bool DisconnectBlock(const CBlock& block, CValidationState& state, const CBlockI
// undo transactions in reverse order // undo transactions in reverse order
for (int i = block.vtx.size() - 1; i >= 0; i--) { for (int i = block.vtx.size() - 1; i >= 0; i--) {
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = *(block.vtx[i]);
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
// Check that all outputs are available and match the outputs in the block itself // Check that all outputs are available and match the outputs in the block itself
@ -2417,8 +2417,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash));
if (fEnforceBIP30) { if (fEnforceBIP30) {
BOOST_FOREACH(const CTransaction& tx, block.vtx) { for (const auto& tx : block.vtx) {
const CCoins* coins = view.AccessCoins(tx.GetHash()); const CCoins* coins = view.AccessCoins(tx->GetHash());
if (coins && !coins->IsPruned()) if (coins && !coins->IsPruned())
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"), return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
REJECT_INVALID, "bad-txns-BIP30"); REJECT_INVALID, "bad-txns-BIP30");
@ -2474,7 +2474,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const CTransaction &tx = block.vtx[i]; const CTransaction &tx = *(block.vtx[i]);
nInputs += tx.vin.size(); nInputs += tx.vin.size();
@ -2544,10 +2544,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001);
CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus());
if (block.vtx[0].GetValueOut() > blockReward) if (block.vtx[0]->GetValueOut() > blockReward)
return state.DoS(100, return state.DoS(100,
error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)",
block.vtx[0].GetValueOut(), blockReward), block.vtx[0]->GetValueOut(), blockReward),
REJECT_INVALID, "bad-cb-amount"); REJECT_INVALID, "bad-cb-amount");
if (!control.Wait()) if (!control.Wait())
@ -2590,7 +2590,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
// Watch for changes to the previous coinbase transaction. // Watch for changes to the previous coinbase transaction.
static uint256 hashPrevBestCoinBase; static uint256 hashPrevBestCoinBase;
GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase); GetMainSignals().UpdatedTransaction(hashPrevBestCoinBase);
hashPrevBestCoinBase = block.vtx[0].GetHash(); hashPrevBestCoinBase = block.vtx[0]->GetHash();
// Erase orphan transactions include or precluded by this block // Erase orphan transactions include or precluded by this block
if (vOrphanErase.size()) { if (vOrphanErase.size()) {
@ -2807,7 +2807,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
if (!fBare) { if (!fBare) {
// Resurrect mempool transactions from the disconnected block. // Resurrect mempool transactions from the disconnected block.
std::vector<uint256> vHashUpdate; std::vector<uint256> vHashUpdate;
BOOST_FOREACH(const CTransaction &tx, block.vtx) { for (const auto& it : block.vtx) {
const CTransaction& tx = *it;
// ignore validation errors in resurrected transactions // ignore validation errors in resurrected transactions
CValidationState stateDummy; CValidationState stateDummy;
if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) { if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL, true)) {
@ -2828,8 +2829,8 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara
UpdateTip(pindexDelete->pprev, chainparams); UpdateTip(pindexDelete->pprev, chainparams);
// Let wallets know transactions went from 1-confirmed to // Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted: // 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) { for (const auto& tx : block.vtx) {
GetMainSignals().SyncTransaction(tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); GetMainSignals().SyncTransaction(*tx, pindexDelete->pprev, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
} }
return true; return true;
} }
@ -2844,7 +2845,7 @@ static int64_t nTimePostConnect = 0;
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk. * corresponding to pindexNew, to bypass loading it again from disk.
*/ */
bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::vector<std::shared_ptr<const CTransaction>> &txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>> &txChanged) bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const CBlock* pblock, std::vector<CTransactionRef> &txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> &txChanged)
{ {
assert(pindexNew->pprev == chainActive.Tip()); assert(pindexNew->pprev == chainActive.Tip());
// Read block from disk. // Read block from disk.
@ -2884,7 +2885,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams,
// Update chainActive & related variables. // Update chainActive & related variables.
UpdateTip(pindexNew, chainparams); UpdateTip(pindexNew, chainparams);
for(unsigned int i=0; i < pblock->vtx.size(); i++) for (unsigned int i=0; i < pblock->vtx.size(); i++)
txChanged.emplace_back(pblock->vtx[i], pindexNew, i); txChanged.emplace_back(pblock->vtx[i], pindexNew, i);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
@ -2967,7 +2968,7 @@ static void PruneBlockIndexCandidates() {
* Try to make some progress towards making pindexMostWork the active block. * Try to make some progress towards making pindexMostWork the active block.
* pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork.
*/ */
static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::vector<std::shared_ptr<const CTransaction>>& txConflicted, std::vector<std::tuple<CTransaction,CBlockIndex*,int>>& txChanged) static bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const CBlock* pblock, bool& fInvalidFound, std::vector<CTransactionRef>& txConflicted, std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>>& txChanged)
{ {
AssertLockHeld(cs_main); AssertLockHeld(cs_main);
const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexOldTip = chainActive.Tip();
@ -3068,7 +3069,7 @@ static void NotifyHeaderTip() {
bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) {
CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexMostWork = NULL;
CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexNewTip = NULL;
std::vector<std::tuple<CTransaction,CBlockIndex*,int>> txChanged; std::vector<std::tuple<CTransactionRef,CBlockIndex*,int>> txChanged;
if (pblock) if (pblock)
txChanged.reserve(pblock->vtx.size()); txChanged.reserve(pblock->vtx.size());
do { do {
@ -3078,7 +3079,7 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
break; break;
const CBlockIndex *pindexFork; const CBlockIndex *pindexFork;
std::vector<std::shared_ptr<const CTransaction>> txConflicted; std::vector<CTransactionRef> txConflicted;
bool fInitialDownload; bool fInitialDownload;
{ {
LOCK(cs_main); LOCK(cs_main);
@ -3109,13 +3110,13 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams,
// throw all transactions though the signal-interface // throw all transactions though the signal-interface
// while _not_ holding the cs_main lock // while _not_ holding the cs_main lock
for(std::shared_ptr<const CTransaction> tx : txConflicted) for (const auto& tx : txConflicted)
{ {
GetMainSignals().SyncTransaction(*tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK); GetMainSignals().SyncTransaction(*tx, pindexNewTip, CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK);
} }
// ... and about transactions that got confirmed: // ... and about transactions that got confirmed:
for(unsigned int i = 0; i < txChanged.size(); i++) for (unsigned int i = 0; i < txChanged.size(); i++)
GetMainSignals().SyncTransaction(std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i])); GetMainSignals().SyncTransaction(*std::get<0>(txChanged[i]), std::get<1>(txChanged[i]), std::get<2>(txChanged[i]));
// Notify external listeners about the new tip. // Notify external listeners about the new tip.
GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload);
@ -3454,22 +3455,22 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed");
// First transaction must be coinbase, the rest must not be // First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) if (block.vtx.empty() || !block.vtx[0]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase"); return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase");
for (unsigned int i = 1; i < block.vtx.size(); i++) for (unsigned int i = 1; i < block.vtx.size(); i++)
if (block.vtx[i].IsCoinBase()) if (block.vtx[i]->IsCoinBase())
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase"); return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase");
// Check transactions // Check transactions
for (const auto& tx : block.vtx) for (const auto& tx : block.vtx)
if (!CheckTransaction(tx, state, false)) if (!CheckTransaction(*tx, state, false))
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(),
strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage()));
unsigned int nSigOps = 0; unsigned int nSigOps = 0;
for (const auto& tx : block.vtx) for (const auto& tx : block.vtx)
{ {
nSigOps += GetLegacySigOpCount(tx); nSigOps += GetLegacySigOpCount(*tx);
} }
if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST) if (nSigOps * WITNESS_SCALE_FACTOR > MAX_BLOCK_SIGOPS_COST)
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");
@ -3505,8 +3506,8 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa
static int GetWitnessCommitmentIndex(const CBlock& block) static int GetWitnessCommitmentIndex(const CBlock& block)
{ {
int commitpos = -1; int commitpos = -1;
for (size_t o = 0; o < block.vtx[0].vout.size(); o++) { for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
if (block.vtx[0].vout[o].scriptPubKey.size() >= 38 && block.vtx[0].vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0].vout[o].scriptPubKey[1] == 0x24 && block.vtx[0].vout[o].scriptPubKey[2] == 0xaa && block.vtx[0].vout[o].scriptPubKey[3] == 0x21 && block.vtx[0].vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0].vout[o].scriptPubKey[5] == 0xed) { if (block.vtx[0]->vout[o].scriptPubKey.size() >= 38 && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN && block.vtx[0]->vout[o].scriptPubKey[1] == 0x24 && block.vtx[0]->vout[o].scriptPubKey[2] == 0xaa && block.vtx[0]->vout[o].scriptPubKey[3] == 0x21 && block.vtx[0]->vout[o].scriptPubKey[4] == 0xa9 && block.vtx[0]->vout[o].scriptPubKey[5] == 0xed) {
commitpos = o; commitpos = o;
} }
} }
@ -3517,10 +3518,12 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
{ {
int commitpos = GetWitnessCommitmentIndex(block); int commitpos = GetWitnessCommitmentIndex(block);
static const std::vector<unsigned char> nonce(32, 0x00); static const std::vector<unsigned char> nonce(32, 0x00);
if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0].wit.IsEmpty()) { if (commitpos != -1 && IsWitnessEnabled(pindexPrev, consensusParams) && block.vtx[0]->wit.IsEmpty()) {
block.vtx[0].wit.vtxinwit.resize(1); CMutableTransaction tx(*block.vtx[0]);
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.resize(1); tx.wit.vtxinwit.resize(1);
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0] = nonce; tx.wit.vtxinwit[0].scriptWitness.stack.resize(1);
tx.wit.vtxinwit[0].scriptWitness.stack[0] = nonce;
block.vtx[0] = MakeTransactionRef(std::move(tx));
} }
} }
@ -3530,7 +3533,7 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
int commitpos = GetWitnessCommitmentIndex(block); int commitpos = GetWitnessCommitmentIndex(block);
bool fHaveWitness = false; bool fHaveWitness = false;
for (size_t t = 1; t < block.vtx.size(); t++) { for (size_t t = 1; t < block.vtx.size(); t++) {
if (!block.vtx[t].wit.IsNull()) { if (!block.vtx[t]->wit.IsNull()) {
fHaveWitness = true; fHaveWitness = true;
break; break;
} }
@ -3551,8 +3554,8 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
out.scriptPubKey[5] = 0xed; out.scriptPubKey[5] = 0xed;
memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32); memcpy(&out.scriptPubKey[6], witnessroot.begin(), 32);
commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end()); commitment = std::vector<unsigned char>(out.scriptPubKey.begin(), out.scriptPubKey.end());
const_cast<std::vector<CTxOut>*>(&block.vtx[0].vout)->push_back(out); const_cast<std::vector<CTxOut>*>(&block.vtx[0]->vout)->push_back(out);
block.vtx[0].UpdateHash(); block.vtx[0]->UpdateHash();
} }
} }
UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams); UpdateUncommittedBlockStructures(block, pindexPrev, consensusParams);
@ -3601,7 +3604,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// Check that all transactions are finalized // Check that all transactions are finalized
for (const auto& tx : block.vtx) { for (const auto& tx : block.vtx) {
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction");
} }
} }
@ -3610,8 +3613,8 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
if (nHeight >= consensusParams.BIP34Height) if (nHeight >= consensusParams.BIP34Height)
{ {
CScript expect = CScript() << nHeight; CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { !std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) {
return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase");
} }
} }
@ -3633,11 +3636,11 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// The malleation check is ignored; as the transaction tree itself // The malleation check is ignored; as the transaction tree itself
// already does not permit it, it is impossible to trigger in the // already does not permit it, it is impossible to trigger in the
// witness tree. // witness tree.
if (block.vtx[0].wit.vtxinwit.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) { if (block.vtx[0]->wit.vtxinwit.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack.size() != 1 || block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0].size() != 32) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__)); return state.DoS(100, false, REJECT_INVALID, "bad-witness-nonce-size", true, strprintf("%s : invalid witness nonce size", __func__));
} }
CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0].wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin()); CHash256().Write(hashWitness.begin(), 32).Write(&block.vtx[0]->wit.vtxinwit[0].scriptWitness.stack[0][0], 32).Finalize(hashWitness.begin());
if (memcmp(hashWitness.begin(), &block.vtx[0].vout[commitpos].scriptPubKey[6], 32)) { if (memcmp(hashWitness.begin(), &block.vtx[0]->vout[commitpos].scriptPubKey[6], 32)) {
return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__)); return state.DoS(100, false, REJECT_INVALID, "bad-witness-merkle-match", true, strprintf("%s : witness merkle commitment mismatch", __func__));
} }
fHaveWitness = true; fHaveWitness = true;
@ -3647,7 +3650,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Co
// No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam // No witness data is allowed in blocks that don't commit to witness data, as this would otherwise leave room for spam
if (!fHaveWitness) { if (!fHaveWitness) {
for (size_t i = 0; i < block.vtx.size(); i++) { for (size_t i = 0; i < block.vtx.size(); i++) {
if (!block.vtx[i].wit.IsNull()) { if (!block.vtx[i]->wit.IsNull()) {
return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__)); return state.DoS(100, false, REJECT_INVALID, "unexpected-witness", true, strprintf("%s : unexpected witness data found", __func__));
} }
} }
@ -4953,7 +4956,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
// however we MUST always provide at least what the remote peer needs // however we MUST always provide at least what the remote peer needs
typedef std::pair<unsigned int, uint256> PairType; typedef std::pair<unsigned int, uint256> PairType;
BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn)
connman.PushMessageWithFlag(pfrom, SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, block.vtx[pair.first]); connman.PushMessageWithFlag(pfrom, SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *block.vtx[pair.first]);
} }
// else // else
// no response // no response

6
src/merkleblock.cpp

@ -23,8 +23,8 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter)
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const uint256& hash = block.vtx[i].GetHash(); const uint256& hash = block.vtx[i]->GetHash();
if (filter.IsRelevantAndUpdate(block.vtx[i])) if (filter.IsRelevantAndUpdate(*block.vtx[i]))
{ {
vMatch.push_back(true); vMatch.push_back(true);
vMatchedTxn.push_back(make_pair(i, hash)); vMatchedTxn.push_back(make_pair(i, hash));
@ -49,7 +49,7 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids)
for (unsigned int i = 0; i < block.vtx.size(); i++) for (unsigned int i = 0; i < block.vtx.size(); i++)
{ {
const uint256& hash = block.vtx[i].GetHash(); const uint256& hash = block.vtx[i]->GetHash();
if (txids.count(hash)) if (txids.count(hash))
vMatch.push_back(true); vMatch.push_back(true);
else else

12
src/miner.cpp

@ -134,7 +134,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
pblock = &pblocktemplate->block; // pointer for convenience pblock = &pblocktemplate->block; // pointer for convenience
// Add dummy coinbase tx as first transaction // Add dummy coinbase tx as first transaction
pblock->vtx.push_back(CTransaction()); pblock->vtx.emplace_back();
pblocktemplate->vTxFees.push_back(-1); // updated at end pblocktemplate->vTxFees.push_back(-1); // updated at end
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
@ -178,7 +178,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn; coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = coinbaseTx; pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
pblocktemplate->vTxFees[0] = -nFees; pblocktemplate->vTxFees[0] = -nFees;
@ -190,7 +190,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev); UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus()); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0; pblock->nNonce = 0;
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(pblock->vtx[0]); pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
CValidationState state; CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
@ -312,7 +312,7 @@ bool BlockAssembler::TestForBlock(CTxMemPool::txiter iter)
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
{ {
pblock->vtx.push_back(iter->GetTx()); pblock->vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee()); pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost()); pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost());
if (fNeedSizeAccounting) { if (fNeedSizeAccounting) {
@ -601,10 +601,10 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
} }
++nExtraNonce; ++nExtraNonce;
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(pblock->vtx[0]); CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= 100); assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = txCoinbase; pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
} }

2
src/primitives/block.cpp

@ -27,7 +27,7 @@ std::string CBlock::ToString() const
vtx.size()); vtx.size());
for (unsigned int i = 0; i < vtx.size(); i++) for (unsigned int i = 0; i < vtx.size(); i++)
{ {
s << " " << vtx[i].ToString() << "\n"; s << " " << vtx[i]->ToString() << "\n";
} }
return s.str(); return s.str();
} }

2
src/primitives/block.h

@ -73,7 +73,7 @@ class CBlock : public CBlockHeader
{ {
public: public:
// network and disk // network and disk
std::vector<CTransaction> vtx; std::vector<CTransactionRef> vtx;
// memory only // memory only
mutable bool fChecked; mutable bool fChecked;

4
src/primitives/transaction.cpp

@ -78,6 +78,10 @@ CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion
UpdateHash(); UpdateHash();
} }
CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), wit(std::move(tx.wit)), nLockTime(tx.nLockTime) {
UpdateHash();
}
CTransaction& CTransaction::operator=(const CTransaction &tx) { CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<int*>(&nVersion) = tx.nVersion; *const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin; *const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;

15
src/primitives/transaction.h

@ -379,6 +379,7 @@ public:
/** Convert a CMutableTransaction into a CTransaction. */ /** Convert a CMutableTransaction into a CTransaction. */
CTransaction(const CMutableTransaction &tx); CTransaction(const CMutableTransaction &tx);
CTransaction(CMutableTransaction &&tx);
CTransaction& operator=(const CTransaction& tx); CTransaction& operator=(const CTransaction& tx);
@ -392,6 +393,9 @@ public:
} }
} }
template <typename Stream>
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
bool IsNull() const { bool IsNull() const {
return vin.empty() && vout.empty(); return vin.empty() && vout.empty();
} }
@ -460,12 +464,23 @@ struct CMutableTransaction
SerializeTransaction(*this, s, ser_action); SerializeTransaction(*this, s, ser_action);
} }
template <typename Stream>
CMutableTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
/** Compute the hash of this CMutableTransaction. This is computed on the /** Compute the hash of this CMutableTransaction. This is computed on the
* fly, as opposed to GetHash() in CTransaction, which uses a cached result. * fly, as opposed to GetHash() in CTransaction, which uses a cached result.
*/ */
uint256 GetHash() const; uint256 GetHash() const;
}; };
typedef std::shared_ptr<const CTransaction> CTransactionRef;
static inline CTransactionRef MakeTransactionRef() { return std::make_shared<const CTransaction>(); }
template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }
static inline CTransactionRef MakeTransactionRef(const CTransactionRef& txIn) { return txIn; }
static inline CTransactionRef MakeTransactionRef(CTransactionRef&& txIn) { return std::move(txIn); }
/** Compute the weight of a transaction, as defined by BIP 141 */ /** Compute the weight of a transaction, as defined by BIP 141 */
int64_t GetTransactionWeight(const CTransaction &tx); int64_t GetTransactionWeight(const CTransaction &tx);

6
src/rpc/blockchain.cpp

@ -119,16 +119,16 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx
result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion))); result.push_back(Pair("versionHex", strprintf("%08x", block.nVersion)));
result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex()));
UniValue txs(UniValue::VARR); UniValue txs(UniValue::VARR);
BOOST_FOREACH(const CTransaction&tx, block.vtx) for(const auto& tx : block.vtx)
{ {
if(txDetails) if(txDetails)
{ {
UniValue objTx(UniValue::VOBJ); UniValue objTx(UniValue::VOBJ);
TxToJSON(tx, uint256(), objTx); TxToJSON(*tx, uint256(), objTx);
txs.push_back(objTx); txs.push_back(objTx);
} }
else else
txs.push_back(tx.GetHash().GetHex()); txs.push_back(tx->GetHash().GetHex());
} }
result.push_back(Pair("tx", txs)); result.push_back(Pair("tx", txs));
result.push_back(Pair("time", block.GetBlockTime())); result.push_back(Pair("time", block.GetBlockTime()));

5
src/rpc/mining.cpp

@ -557,7 +557,8 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
UniValue transactions(UniValue::VARR); UniValue transactions(UniValue::VARR);
map<uint256, int64_t> setTxIndex; map<uint256, int64_t> setTxIndex;
int i = 0; int i = 0;
BOOST_FOREACH (CTransaction& tx, pblock->vtx) { for (const auto& it : pblock->vtx) {
const CTransaction& tx = *it;
uint256 txHash = tx.GetHash(); uint256 txHash = tx.GetHash();
setTxIndex[txHash] = i++; setTxIndex[txHash] = i++;
@ -662,7 +663,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex())); result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions)); result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux)); result.push_back(Pair("coinbaseaux", aux));
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0]->vout[0].nValue));
result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex())); result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1));

4
src/rpc/rawtransaction.cpp

@ -288,8 +288,8 @@ UniValue gettxoutproof(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk");
unsigned int ntxFound = 0; unsigned int ntxFound = 0;
BOOST_FOREACH(const CTransaction&tx, block.vtx) for (const auto& tx : block.vtx)
if (setTxids.count(tx.GetHash())) if (setTxids.count(tx->GetHash()))
ntxFound++; ntxFound++;
if (ntxFound != setTxids.size()) if (ntxFound != setTxids.size())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block");

59
src/serialize.h

@ -13,6 +13,7 @@
#include <ios> #include <ios>
#include <limits> #include <limits>
#include <map> #include <map>
#include <memory>
#include <set> #include <set>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
@ -24,6 +25,20 @@
static const unsigned int MAX_SIZE = 0x02000000; static const unsigned int MAX_SIZE = 0x02000000;
/**
* Dummy data type to identify deserializing constructors.
*
* By convention, a constructor of a type T with signature
*
* template <typename Stream> T::T(deserialize_type, Stream& s)
*
* is a deserializing constructor, which builds the type by
* deserializing it from s. If T contains const fields, this
* is likely the only way to do so.
*/
struct deserialize_type {};
constexpr deserialize_type deserialize {};
/** /**
* Used to bypass the rule against non-const reference to temporary * Used to bypass the rule against non-const reference to temporary
* where it makes sense with wrappers such as CFlatData or CTxDB * where it makes sense with wrappers such as CFlatData or CTxDB
@ -521,7 +536,17 @@ template<typename Stream, typename K, typename T, typename Pred, typename A> voi
template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m); template<typename Stream, typename K, typename Pred, typename A> void Serialize(Stream& os, const std::set<K, Pred, A>& m);
template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& m); template<typename Stream, typename K, typename Pred, typename A> void Unserialize(Stream& is, std::set<K, Pred, A>& m);
/**
* shared_ptr
*/
template<typename Stream, typename T> void Serialize(Stream& os, const std::shared_ptr<const T>& p);
template<typename Stream, typename T> void Unserialize(Stream& os, std::shared_ptr<const T>& p);
/**
* unique_ptr
*/
template<typename Stream, typename T> void Serialize(Stream& os, const std::unique_ptr<const T>& p);
template<typename Stream, typename T> void Unserialize(Stream& os, std::unique_ptr<const T>& p);
@ -775,6 +800,40 @@ void Unserialize(Stream& is, std::set<K, Pred, A>& m)
/**
* unique_ptr
*/
template<typename Stream, typename T> void
Serialize(Stream& os, const std::unique_ptr<const T>& p)
{
Serialize(os, *p);
}
template<typename Stream, typename T>
void Unserialize(Stream& is, std::unique_ptr<const T>& p)
{
p.reset(new T(deserialize, is));
}
/**
* shared_ptr
*/
template<typename Stream, typename T> void
Serialize(Stream& os, const std::shared_ptr<const T>& p)
{
Serialize(os, *p);
}
template<typename Stream, typename T>
void Unserialize(Stream& is, std::shared_ptr<const T>& p)
{
p = std::make_shared<const T>(deserialize, is);
}
/** /**
* Support for ADD_SERIALIZE_METHODS and READWRITE macro * Support for ADD_SERIALIZE_METHODS and READWRITE macro
*/ */

63
src/test/blockencodings_tests.cpp

@ -26,21 +26,21 @@ static CBlock BuildBlockTestCase() {
tx.vout[0].nValue = 42; tx.vout[0].nValue = 42;
block.vtx.resize(3); block.vtx.resize(3);
block.vtx[0] = tx; block.vtx[0] = MakeTransactionRef(tx);
block.nVersion = 42; block.nVersion = 42;
block.hashPrevBlock = GetRandHash(); block.hashPrevBlock = GetRandHash();
block.nBits = 0x207fffff; block.nBits = 0x207fffff;
tx.vin[0].prevout.hash = GetRandHash(); tx.vin[0].prevout.hash = GetRandHash();
tx.vin[0].prevout.n = 0; tx.vin[0].prevout.n = 0;
block.vtx[1] = tx; block.vtx[1] = MakeTransactionRef(tx);
tx.vin.resize(10); tx.vin.resize(10);
for (size_t i = 0; i < tx.vin.size(); i++) { for (size_t i = 0; i < tx.vin.size(); i++) {
tx.vin[i].prevout.hash = GetRandHash(); tx.vin[i].prevout.hash = GetRandHash();
tx.vin[i].prevout.n = 0; tx.vin[i].prevout.n = 0;
} }
block.vtx[2] = tx; block.vtx[2] = MakeTransactionRef(tx);
bool mutated; bool mutated;
block.hashMerkleRoot = BlockMerkleRoot(block, &mutated); block.hashMerkleRoot = BlockMerkleRoot(block, &mutated);
@ -59,8 +59,8 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
TestMemPoolEntryHelper entry; TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase()); CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2])); pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
// Do a simple ShortTxIDs RT // Do a simple ShortTxIDs RT
{ {
@ -78,14 +78,14 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK(!partialBlock.IsTxAvailable(1)); BOOST_CHECK(!partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
std::vector<std::shared_ptr<const CTransaction>> removed; std::vector<CTransactionRef> removed;
pool.removeRecursive(block.vtx[2], &removed); pool.removeRecursive(*block.vtx[2], &removed);
BOOST_CHECK_EQUAL(removed.size(), 1); BOOST_CHECK_EQUAL(removed.size(), 1);
CBlock block2; CBlock block2;
std::vector<CTransaction> vtx_missing; std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
vtx_missing.push_back(block.vtx[2]); // Wrong transaction vtx_missing.push_back(block.vtx[2]); // Wrong transaction
@ -152,8 +152,10 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
TestMemPoolEntryHelper entry; TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase()); CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[2].GetHash(), entry.FromTx(block.vtx[2])); pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
// Test with pre-forwarding tx 1, but not coinbase // Test with pre-forwarding tx 1, but not coinbase
{ {
@ -161,8 +163,8 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
shortIDs.prefilledtxn.resize(1); shortIDs.prefilledtxn.resize(1);
shortIDs.prefilledtxn[0] = {1, block.vtx[1]}; shortIDs.prefilledtxn[0] = {1, block.vtx[1]};
shortIDs.shorttxids.resize(2); shortIDs.shorttxids.resize(2);
shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0].GetHash()); shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[0]->GetHash());
shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2].GetHash()); shortIDs.shorttxids[1] = shortIDs.GetShortID(block.vtx[2]->GetHash());
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs; stream << shortIDs;
@ -176,10 +178,10 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
CBlock block2; CBlock block2;
std::vector<CTransaction> vtx_missing; std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_INVALID); // No transactions
vtx_missing.push_back(block.vtx[1]); // Wrong transaction vtx_missing.push_back(block.vtx[1]); // Wrong transaction
@ -194,9 +196,13 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString()); BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
BOOST_CHECK(!mutated); BOOST_CHECK(!mutated);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); txhash = block.vtx[2]->GetHash();
block.vtx.clear();
block2.vtx.clear();
block3.vtx.clear();
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
} }
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
} }
BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
@ -205,8 +211,10 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
TestMemPoolEntryHelper entry; TestMemPoolEntryHelper entry;
CBlock block(BuildBlockTestCase()); CBlock block(BuildBlockTestCase());
pool.addUnchecked(block.vtx[1].GetHash(), entry.FromTx(block.vtx[1])); pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash;
// Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool // Test with pre-forwarding coinbase + tx 2 with tx 1 in mempool
{ {
@ -215,7 +223,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
shortIDs.prefilledtxn[0] = {0, block.vtx[0]}; shortIDs.prefilledtxn[0] = {0, block.vtx[0]};
shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1 shortIDs.prefilledtxn[1] = {1, block.vtx[2]}; // id == 1 as it is 1 after index 1
shortIDs.shorttxids.resize(1); shortIDs.shorttxids.resize(1);
shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1].GetHash()); shortIDs.shorttxids[0] = shortIDs.GetShortID(block.vtx[1]->GetHash());
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION); CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << shortIDs; stream << shortIDs;
@ -229,19 +237,22 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
CBlock block2; CBlock block2;
std::vector<CTransaction> vtx_missing; std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK); BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString()); BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
bool mutated; bool mutated;
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString()); BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());
BOOST_CHECK(!mutated); BOOST_CHECK(!mutated);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); txhash = block.vtx[1]->GetHash();
block.vtx.clear();
block2.vtx.clear();
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1);
} }
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1].GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0);
} }
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
@ -255,7 +266,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
CBlock block; CBlock block;
block.vtx.resize(1); block.vtx.resize(1);
block.vtx[0] = coinbase; block.vtx[0] = MakeTransactionRef(std::move(coinbase));
block.nVersion = 42; block.nVersion = 42;
block.hashPrevBlock = GetRandHash(); block.hashPrevBlock = GetRandHash();
block.nBits = 0x207fffff; block.nBits = 0x207fffff;
@ -280,7 +291,7 @@ BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
BOOST_CHECK(partialBlock.IsTxAvailable(0)); BOOST_CHECK(partialBlock.IsTxAvailable(0));
CBlock block2; CBlock block2;
std::vector<CTransaction> vtx_missing; std::vector<CTransactionRef> vtx_missing;
BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK); BOOST_CHECK(partialBlock.FillBlock(block2, vtx_missing) == READ_STATUS_OK);
BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString()); BOOST_CHECK_EQUAL(block.GetHash().ToString(), block2.GetHash().ToString());
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString()); BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block2, &mutated).ToString());

8
src/test/mempool_tests.cpp

@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
CTxMemPool testPool(CFeeRate(0)); CTxMemPool testPool(CFeeRate(0));
std::vector<std::shared_ptr<const CTransaction>> removed; std::vector<CTransactionRef> removed;
// Nothing in pool, remove should do nothing: // Nothing in pool, remove should do nothing:
testPool.removeRecursive(txParent, &removed); testPool.removeRecursive(txParent, &removed);
@ -410,8 +410,8 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
CheckSort<ancestor_score>(pool, sortedOrder); CheckSort<ancestor_score>(pool, sortedOrder);
/* after tx6 is mined, tx7 should move up in the sort */ /* after tx6 is mined, tx7 should move up in the sort */
std::vector<CTransaction> vtx; std::vector<CTransactionRef> vtx;
vtx.push_back(tx6); vtx.push_back(MakeTransactionRef(tx6));
pool.removeForBlock(vtx, 1, NULL, false); pool.removeForBlock(vtx, 1, NULL, false);
sortedOrder.erase(sortedOrder.begin()+1); sortedOrder.erase(sortedOrder.begin()+1);
@ -546,7 +546,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool)); pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool));
pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool)); pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
std::vector<CTransaction> vtx; std::vector<CTransactionRef> vtx;
SetMockTime(42); SetMockTime(42);
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000); BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);

8
src/test/merkle_tests.cpp

@ -15,8 +15,8 @@ static uint256 BlockBuildMerkleTree(const CBlock& block, bool* fMutated, std::ve
{ {
vMerkleTree.clear(); vMerkleTree.clear();
vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. vMerkleTree.reserve(block.vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes.
for (std::vector<CTransaction>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it) for (std::vector<CTransactionRef>::const_iterator it(block.vtx.begin()); it != block.vtx.end(); ++it)
vMerkleTree.push_back(it->GetHash()); vMerkleTree.push_back((*it)->GetHash());
int j = 0; int j = 0;
bool mutated = false; bool mutated = false;
for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) for (int nSize = block.vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
@ -86,7 +86,7 @@ BOOST_AUTO_TEST_CASE(merkle_test)
for (int j = 0; j < ntx; j++) { for (int j = 0; j < ntx; j++) {
CMutableTransaction mtx; CMutableTransaction mtx;
mtx.nLockTime = j; mtx.nLockTime = j;
block.vtx[j] = mtx; block.vtx[j] = MakeTransactionRef(std::move(mtx));
} }
// Compute the root of the block before mutating it. // Compute the root of the block before mutating it.
bool unmutatedMutated = false; bool unmutatedMutated = false;
@ -126,7 +126,7 @@ BOOST_AUTO_TEST_CASE(merkle_test)
std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx); std::vector<uint256> newBranch = BlockMerkleBranch(block, mtx);
std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx); std::vector<uint256> oldBranch = BlockGetMerkleBranch(block, merkleTree, mtx);
BOOST_CHECK(oldBranch == newBranch); BOOST_CHECK(oldBranch == newBranch);
BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx].GetHash(), newBranch, mtx) == oldRoot); BOOST_CHECK(ComputeMerkleRootFromBranch(block.vtx[mtx]->GetHash(), newBranch, mtx) == oldRoot);
} }
} }
} }

33
src/test/miner_tests.cpp

@ -77,7 +77,7 @@ bool TestSequenceLocks(const CTransaction &tx, int flags)
// Implemented as an additional function, rather than a separate test case, // Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity. // to allow reusing the blockchain created in CreateNewBlock_validity.
// Note that this test assumes blockprioritysize is 0. // Note that this test assumes blockprioritysize is 0.
void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransaction *>& txFirst) void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey, std::vector<CTransactionRef>& txFirst)
{ {
// Test the ancestor feerate transaction selection. // Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry; TestMemPoolEntryHelper entry;
@ -108,9 +108,9 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[1].GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
BOOST_CHECK(pblocktemplate->block.vtx[2].GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx);
BOOST_CHECK(pblocktemplate->block.vtx[3].GetHash() == hashMediumFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx);
// Test that a package below the min relay fee doesn't get included // Test that a package below the min relay fee doesn't get included
tx.vin[0].prevout.hash = hashHighFeeTx; tx.vin[0].prevout.hash = hashHighFeeTx;
@ -130,8 +130,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected // Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx);
} }
// Test that packages above the min relay fee do get included, even if one // Test that packages above the min relay fee do get included, even if one
@ -142,8 +142,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
hashLowFeeTx = tx.GetHash(); hashLowFeeTx = tx.GetHash();
mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx));
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[4].GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5].GetHash() == hashLowFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
// Test that transaction selection properly updates ancestor fee // Test that transaction selection properly updates ancestor fee
// calculations as ancestor transactions get included in a block. // calculations as ancestor transactions get included in a block.
@ -166,8 +166,8 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
// Verify that this tx isn't selected. // Verify that this tx isn't selected.
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashFreeTx2); BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx2);
BOOST_CHECK(pblocktemplate->block.vtx[i].GetHash() != hashLowFeeTx2); BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashLowFeeTx2);
} }
// This tx will be mineable, and should cause hashLowFeeTx2 to be selected // This tx will be mineable, and should cause hashLowFeeTx2 to be selected
@ -176,7 +176,7 @@ void TestPackageSelection(const CChainParams& chainparams, CScript scriptPubKey,
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx)); mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx));
pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey); pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[8].GetHash() == hashLowFeeTx2); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
} }
// NOTE: These tests rely on CreateNewBlock doing its own self-validation! // NOTE: These tests rely on CreateNewBlock doing its own self-validation!
@ -203,23 +203,23 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// We can't make transactions until we have inputs // We can't make transactions until we have inputs
// Therefore, load 100 blocks :) // Therefore, load 100 blocks :)
int baseheight = 0; int baseheight = 0;
std::vector<CTransaction*>txFirst; std::vector<CTransactionRef> txFirst;
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i) for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
{ {
CBlock *pblock = &pblocktemplate->block; // pointer for convenience CBlock *pblock = &pblocktemplate->block; // pointer for convenience
pblock->nVersion = 1; pblock->nVersion = 1;
pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1; pblock->nTime = chainActive.Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(pblock->vtx[0]); CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1; txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript(); txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce); txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
txCoinbase.vin[0].scriptSig.push_back(chainActive.Height()); txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
txCoinbase.vout[0].scriptPubKey = CScript(); txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = CTransaction(txCoinbase); pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0) if (txFirst.size() == 0)
baseheight = chainActive.Height(); baseheight = chainActive.Height();
if (txFirst.size() < 4) if (txFirst.size() < 4)
txFirst.push_back(new CTransaction(pblock->vtx[0])); txFirst.push_back(pblock->vtx[0]);
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce; pblock->nNonce = blockinfo[i].nonce;
BOOST_CHECK(ProcessNewBlock(chainparams, pblock, true, NULL, NULL)); BOOST_CHECK(ProcessNewBlock(chainparams, pblock, true, NULL, NULL));
@ -485,9 +485,6 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
TestPackageSelection(chainparams, scriptPubKey, txFirst); TestPackageSelection(chainparams, scriptPubKey, txFirst);
BOOST_FOREACH(CTransaction *_tx, txFirst)
delete _tx;
fCheckpointsEnabled = true; fCheckpointsEnabled = true;
} }

4
src/test/pmt_tests.cpp

@ -45,14 +45,14 @@ BOOST_AUTO_TEST_CASE(pmt_test1)
for (unsigned int j=0; j<nTx; j++) { for (unsigned int j=0; j<nTx; j++) {
CMutableTransaction tx; CMutableTransaction tx;
tx.nLockTime = j; // actual transaction data doesn't matter; just make the nLockTime's unique tx.nLockTime = j; // actual transaction data doesn't matter; just make the nLockTime's unique
block.vtx.push_back(CTransaction(tx)); block.vtx.push_back(MakeTransactionRef(std::move(tx)));
} }
// calculate actual merkle root and height // calculate actual merkle root and height
uint256 merkleRoot1 = BlockMerkleRoot(block); uint256 merkleRoot1 = BlockMerkleRoot(block);
std::vector<uint256> vTxid(nTx, uint256()); std::vector<uint256> vTxid(nTx, uint256());
for (unsigned int j=0; j<nTx; j++) for (unsigned int j=0; j<nTx; j++)
vTxid[j] = block.vtx[j].GetHash(); vTxid[j] = block.vtx[j]->GetHash();
int nHeight = 1, nTx_ = nTx; int nHeight = 1, nTx_ = nTx;
while (nTx_ > 1) { while (nTx_ > 1) {
nTx_ = (nTx_+1)/2; nTx_ = (nTx_+1)/2;

14
src/test/policyestimator_tests.cpp

@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx)); CFeeRate baseRate(basefee, GetVirtualTransactionSize(tx));
// Create a fake block // Create a fake block
std::vector<CTransaction> block; std::vector<CTransactionRef> block;
int blocknum = 0; int blocknum = 0;
// Loop through 200 blocks // Loop through 200 blocks
@ -66,9 +66,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// 9/10 blocks add 2nd highest and so on until ... // 9/10 blocks add 2nd highest and so on until ...
// 1/10 blocks add lowest fee transactions // 1/10 blocks add lowest fee transactions
while (txHashes[9-h].size()) { while (txHashes[9-h].size()) {
std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[9-h].back()); CTransactionRef ptx = mpool.get(txHashes[9-h].back());
if (ptx) if (ptx)
block.push_back(*ptx); block.push_back(ptx);
txHashes[9-h].pop_back(); txHashes[9-h].pop_back();
} }
} }
@ -143,9 +143,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Estimates should still not be below original // Estimates should still not be below original
for (int j = 0; j < 10; j++) { for (int j = 0; j < 10; j++) {
while(txHashes[j].size()) { while(txHashes[j].size()) {
std::shared_ptr<const CTransaction> ptx = mpool.get(txHashes[j].back()); CTransactionRef ptx = mpool.get(txHashes[j].back());
if (ptx) if (ptx)
block.push_back(*ptx); block.push_back(ptx);
txHashes[j].pop_back(); txHashes[j].pop_back();
} }
} }
@ -163,9 +163,9 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
tx.vin[0].prevout.n = 10000*blocknum+100*j+k; tx.vin[0].prevout.n = 10000*blocknum+100*j+k;
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool)); mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Priority(0).Height(blocknum).FromTx(tx, &mpool));
std::shared_ptr<const CTransaction> ptx = mpool.get(hash); CTransactionRef ptx = mpool.get(hash);
if (ptx) if (ptx)
block.push_back(*ptx); block.push_back(ptx);
} }
} }

8
src/test/test_bitcoin.cpp

@ -101,7 +101,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST)
{ {
std::vector<CMutableTransaction> noTxns; std::vector<CMutableTransaction> noTxns;
CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey);
coinbaseTxns.push_back(b.vtx[0]); coinbaseTxns.push_back(*b.vtx[0]);
} }
} }
@ -119,7 +119,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>&
// Replace mempool-selected txns with just coinbase plus passed-in txns: // Replace mempool-selected txns with just coinbase plus passed-in txns:
block.vtx.resize(1); block.vtx.resize(1);
BOOST_FOREACH(const CMutableTransaction& tx, txns) BOOST_FOREACH(const CMutableTransaction& tx, txns)
block.vtx.push_back(tx); block.vtx.push_back(MakeTransactionRef(tx));
// IncrementExtraNonce creates a valid coinbase and merkleRoot // IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0; unsigned int extraNonce = 0;
IncrementExtraNonce(&block, chainActive.Tip(), extraNonce); IncrementExtraNonce(&block, chainActive.Tip(), extraNonce);
@ -137,12 +137,12 @@ TestChain100Setup::~TestChain100Setup()
} }
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPool *pool) { CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx, CTxMemPool *pool) {
CTransaction txn(tx); CTransaction txn(tx);
return FromTx(txn, pool); return FromTx(txn, pool);
} }
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *pool) { CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn, CTxMemPool *pool) {
bool hasNoDependencies = pool ? pool->HasNoInputsOf(txn) : hadNoDependencies; bool hasNoDependencies = pool ? pool->HasNoInputsOf(txn) : hadNoDependencies;
// Hack to assume either its completely dependent on other mempool txs or not at all // Hack to assume either its completely dependent on other mempool txs or not at all
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0; CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;

4
src/test/test_bitcoin.h

@ -79,8 +79,8 @@ struct TestMemPoolEntryHelper
nFee(0), nTime(0), dPriority(0.0), nHeight(1), nFee(0), nTime(0), dPriority(0.0), nHeight(1),
hadNoDependencies(false), spendsCoinbase(false), sigOpCost(4) { } hadNoDependencies(false), spendsCoinbase(false), sigOpCost(4) { }
CTxMemPoolEntry FromTx(CMutableTransaction &tx, CTxMemPool *pool = NULL); CTxMemPoolEntry FromTx(const CMutableTransaction &tx, CTxMemPool *pool = NULL);
CTxMemPoolEntry FromTx(CTransaction &tx, CTxMemPool *pool = NULL); CTxMemPoolEntry FromTx(const CTransaction &tx, CTxMemPool *pool = NULL);
// Change the default value // Change the default value
TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; }

26
src/txmempool.cpp

@ -24,7 +24,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
int64_t _nTime, double _entryPriority, unsigned int _entryHeight, int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool poolHasNoInputsOf, CAmount _inChainInputValue,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp): bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight), tx(MakeTransactionRef(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue), hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp) spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
{ {
@ -503,7 +503,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries &setDescendants
} }
} }
void CTxMemPool::removeRecursive(const CTransaction &origTx, std::vector<std::shared_ptr<const CTransaction>>* removed) void CTxMemPool::removeRecursive(const CTransaction &origTx, std::vector<CTransactionRef>* removed)
{ {
// Remove transaction from memory pool // Remove transaction from memory pool
{ {
@ -576,7 +576,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
RemoveStaged(setAllRemoves, false); RemoveStaged(setAllRemoves, false);
} }
void CTxMemPool::removeConflicts(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed) void CTxMemPool::removeConflicts(const CTransaction &tx, std::vector<CTransactionRef>* removed)
{ {
// Remove transactions which depend on inputs of tx, recursively // Remove transactions which depend on inputs of tx, recursively
LOCK(cs); LOCK(cs);
@ -596,29 +596,29 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::vector<std::shared
/** /**
* Called when a block is connected. Removes from mempool and updates the miner fee estimator. * Called when a block is connected. Removes from mempool and updates the miner fee estimator.
*/ */
void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight,
std::vector<std::shared_ptr<const CTransaction>>* conflicts, bool fCurrentEstimate) std::vector<CTransactionRef>* conflicts, bool fCurrentEstimate)
{ {
LOCK(cs); LOCK(cs);
std::vector<CTxMemPoolEntry> entries; std::vector<CTxMemPoolEntry> entries;
BOOST_FOREACH(const CTransaction& tx, vtx) for (const auto& tx : vtx)
{ {
uint256 hash = tx.GetHash(); uint256 hash = tx->GetHash();
indexed_transaction_set::iterator i = mapTx.find(hash); indexed_transaction_set::iterator i = mapTx.find(hash);
if (i != mapTx.end()) if (i != mapTx.end())
entries.push_back(*i); entries.push_back(*i);
} }
BOOST_FOREACH(const CTransaction& tx, vtx) for (const auto& tx : vtx)
{ {
txiter it = mapTx.find(tx.GetHash()); txiter it = mapTx.find(tx->GetHash());
if (it != mapTx.end()) { if (it != mapTx.end()) {
setEntries stage; setEntries stage;
stage.insert(it); stage.insert(it);
RemoveStaged(stage, true); RemoveStaged(stage, true);
} }
removeConflicts(tx, conflicts); removeConflicts(*tx, conflicts);
ClearPrioritisation(tx.GetHash()); ClearPrioritisation(tx->GetHash());
} }
// After the txs in the new block have been removed from the mempool, update policy estimates // After the txs in the new block have been removed from the mempool, update policy estimates
minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate); minerPolicyEstimator->processBlock(nBlockHeight, entries, fCurrentEstimate);
@ -851,7 +851,7 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
return ret; return ret;
} }
std::shared_ptr<const CTransaction> CTxMemPool::get(const uint256& hash) const CTransactionRef CTxMemPool::get(const uint256& hash) const
{ {
LOCK(cs); LOCK(cs);
indexed_transaction_set::const_iterator i = mapTx.find(hash); indexed_transaction_set::const_iterator i = mapTx.find(hash);
@ -978,7 +978,7 @@ bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
// If an entry in the mempool exists, always return that one, as it's guaranteed to never // If an entry in the mempool exists, always return that one, as it's guaranteed to never
// conflict with the underlying cache, and it cannot have pruned entries (as it contains full) // conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
// transactions. First checking the underlying cache risks returning a pruned entry instead. // transactions. First checking the underlying cache risks returning a pruned entry instead.
shared_ptr<const CTransaction> ptx = mempool.get(txid); CTransactionRef ptx = mempool.get(txid);
if (ptx) { if (ptx) {
coins = CCoins(*ptx, MEMPOOL_HEIGHT); coins = CCoins(*ptx, MEMPOOL_HEIGHT);
return true; return true;

16
src/txmempool.h

@ -80,7 +80,7 @@ class CTxMemPool;
class CTxMemPoolEntry class CTxMemPoolEntry
{ {
private: private:
std::shared_ptr<const CTransaction> tx; CTransactionRef tx;
CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups
size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize())
size_t nModSize; //!< ... and modified size for priority size_t nModSize; //!< ... and modified size for priority
@ -118,7 +118,7 @@ public:
CTxMemPoolEntry(const CTxMemPoolEntry& other); CTxMemPoolEntry(const CTxMemPoolEntry& other);
const CTransaction& GetTx() const { return *this->tx; } const CTransaction& GetTx() const { return *this->tx; }
std::shared_ptr<const CTransaction> GetSharedTx() const { return this->tx; } CTransactionRef GetSharedTx() const { return this->tx; }
/** /**
* Fast calculation of lower bound of current priority as update * Fast calculation of lower bound of current priority as update
* from entry priority. Only inputs that were originally in-chain will age. * from entry priority. Only inputs that were originally in-chain will age.
@ -322,7 +322,7 @@ class CBlockPolicyEstimator;
struct TxMempoolInfo struct TxMempoolInfo
{ {
/** The transaction itself */ /** The transaction itself */
std::shared_ptr<const CTransaction> tx; CTransactionRef tx;
/** Time the transaction entered the mempool. */ /** Time the transaction entered the mempool. */
int64_t nTime; int64_t nTime;
@ -527,11 +527,11 @@ public:
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true); bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool fCurrentEstimate = true);
void removeRecursive(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed = NULL); void removeRecursive(const CTransaction &tx, std::vector<CTransactionRef>* removed = NULL);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx, std::vector<std::shared_ptr<const CTransaction>>* removed = NULL); void removeConflicts(const CTransaction &tx, std::vector<CTransactionRef>* removed = NULL);
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight, void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight,
std::vector<std::shared_ptr<const CTransaction>>* conflicts = NULL, bool fCurrentEstimate = true); std::vector<CTransactionRef>* conflicts = NULL, bool fCurrentEstimate = true);
void clear(); void clear();
void _clear(); //lock free void _clear(); //lock free
bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb);
@ -623,7 +623,7 @@ public:
return (mapTx.count(hash) != 0); return (mapTx.count(hash) != 0);
} }
std::shared_ptr<const CTransaction> get(const uint256& hash) const; CTransactionRef get(const uint256& hash) const;
TxMempoolInfo info(const uint256& hash) const; TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const; std::vector<TxMempoolInfo> infoAll() const;

2
src/wallet/wallet.cpp

@ -1492,7 +1492,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
int posInBlock; int posInBlock;
for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++) for (posInBlock = 0; posInBlock < (int)block.vtx.size(); posInBlock++)
{ {
if (AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex, posInBlock, fUpdate)) if (AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate))
ret++; ret++;
} }
pindex = chainActive.Next(pindex); pindex = chainActive.Next(pindex);

Loading…
Cancel
Save