]> Git Repo - VerusCoin.git/blob - src/txmempool.cpp
Merge pull request #4878
[VerusCoin.git] / src / txmempool.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "core.h"
7 #include "txmempool.h"
8 #include "util.h"
9
10 #include <boost/circular_buffer.hpp>
11
12 using namespace std;
13
14 CTxMemPoolEntry::CTxMemPoolEntry():
15     nFee(0), nTxSize(0), nTime(0), dPriority(0.0)
16 {
17     nHeight = MEMPOOL_HEIGHT;
18 }
19
20 CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, int64_t _nFee,
21                                  int64_t _nTime, double _dPriority,
22                                  unsigned int _nHeight):
23     tx(_tx), nFee(_nFee), nTime(_nTime), dPriority(_dPriority), nHeight(_nHeight)
24 {
25     nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
26 }
27
28 CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other)
29 {
30     *this = other;
31 }
32
33 double
34 CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
35 {
36     int64_t nValueIn = tx.GetValueOut()+nFee;
37     double deltaPriority = ((double)(currentHeight-nHeight)*nValueIn)/nTxSize;
38     double dResult = dPriority + deltaPriority;
39     return dResult;
40 }
41
42 //
43 // Keep track of fee/priority for transactions confirmed within N blocks
44 //
45 class CBlockAverage
46 {
47 private:
48     boost::circular_buffer<CFeeRate> feeSamples;
49     boost::circular_buffer<double> prioritySamples;
50
51     template<typename T> std::vector<T> buf2vec(boost::circular_buffer<T> buf) const
52     {
53         std::vector<T> vec(buf.begin(), buf.end());
54         return vec;
55     }
56
57 public:
58     CBlockAverage() : feeSamples(100), prioritySamples(100) { }
59
60     void RecordFee(const CFeeRate& feeRate) {
61         feeSamples.push_back(feeRate);
62     }
63
64     void RecordPriority(double priority) {
65         prioritySamples.push_back(priority);
66     }
67
68     size_t FeeSamples() const { return feeSamples.size(); }
69     size_t GetFeeSamples(std::vector<CFeeRate>& insertInto) const
70     {
71         BOOST_FOREACH(const CFeeRate& f, feeSamples)
72             insertInto.push_back(f);
73         return feeSamples.size();
74     }
75     size_t PrioritySamples() const { return prioritySamples.size(); }
76     size_t GetPrioritySamples(std::vector<double>& insertInto) const
77     {
78         BOOST_FOREACH(double d, prioritySamples)
79             insertInto.push_back(d);
80         return prioritySamples.size();
81     }
82
83     // Used as belt-and-suspenders check when reading to detect
84     // file corruption
85     bool AreSane(const std::vector<CFeeRate>& vecFee, const CFeeRate& minRelayFee)
86     {
87         BOOST_FOREACH(CFeeRate fee, vecFee)
88         {
89             if (fee < CFeeRate(0))
90                 return false;
91             if (fee.GetFeePerK() > minRelayFee.GetFeePerK() * 10000)
92                 return false;
93         }
94         return true;
95     }
96     bool AreSane(const std::vector<double> vecPriority)
97     {
98         BOOST_FOREACH(double priority, vecPriority)
99         {
100             if (priority < 0)
101                 return false;
102         }
103         return true;
104     }
105
106     void Write(CAutoFile& fileout) const
107     {
108         std::vector<CFeeRate> vecFee = buf2vec(feeSamples);
109         fileout << vecFee;
110         std::vector<double> vecPriority = buf2vec(prioritySamples);
111         fileout << vecPriority;
112     }
113
114     void Read(CAutoFile& filein, const CFeeRate& minRelayFee) {
115         std::vector<CFeeRate> vecFee;
116         filein >> vecFee;
117         if (AreSane(vecFee, minRelayFee))
118             feeSamples.insert(feeSamples.end(), vecFee.begin(), vecFee.end());
119         else
120             throw runtime_error("Corrupt fee value in estimates file.");
121         std::vector<double> vecPriority;
122         filein >> vecPriority;
123         if (AreSane(vecPriority))
124             prioritySamples.insert(prioritySamples.end(), vecPriority.begin(), vecPriority.end());
125         else
126             throw runtime_error("Corrupt priority value in estimates file.");
127         if (feeSamples.size() + prioritySamples.size() > 0)
128             LogPrint("estimatefee", "Read %d fee samples and %d priority samples\n",
129                      feeSamples.size(), prioritySamples.size());
130     }
131 };
132
133 class CMinerPolicyEstimator
134 {
135 private:
136     // Records observed averages transactions that confirmed within one block, two blocks,
137     // three blocks etc.
138     std::vector<CBlockAverage> history;
139     std::vector<CFeeRate> sortedFeeSamples;
140     std::vector<double> sortedPrioritySamples;
141
142     int nBestSeenHeight;
143
144     // nBlocksAgo is 0 based, i.e. transactions that confirmed in the highest seen block are
145     // nBlocksAgo == 0, transactions in the block before that are nBlocksAgo == 1 etc.
146     void seenTxConfirm(const CFeeRate& feeRate, const CFeeRate& minRelayFee, double dPriority, int nBlocksAgo)
147     {
148         // Last entry records "everything else".
149         int nBlocksTruncated = min(nBlocksAgo, (int) history.size() - 1);
150         assert(nBlocksTruncated >= 0);
151
152         // We need to guess why the transaction was included in a block-- either
153         // because it is high-priority or because it has sufficient fees.
154         bool sufficientFee = (feeRate > minRelayFee);
155         bool sufficientPriority = AllowFree(dPriority);
156         const char* assignedTo = "unassigned";
157         if (sufficientFee && !sufficientPriority)
158         {
159             history[nBlocksTruncated].RecordFee(feeRate);
160             assignedTo = "fee";
161         }
162         else if (sufficientPriority && !sufficientFee)
163         {
164             history[nBlocksTruncated].RecordPriority(dPriority);
165             assignedTo = "priority";
166         }
167         else
168         {
169             // Neither or both fee and priority sufficient to get confirmed:
170             // don't know why they got confirmed.
171         }
172         LogPrint("estimatefee", "Seen TX confirm: %s : %s fee/%g priority, took %d blocks\n",
173                  assignedTo, feeRate.ToString(), dPriority, nBlocksAgo);
174     }
175
176 public:
177     CMinerPolicyEstimator(int nEntries) : nBestSeenHeight(0)
178     {
179         history.resize(nEntries);
180     }
181
182     void seenBlock(const std::vector<CTxMemPoolEntry>& entries, int nBlockHeight, const CFeeRate minRelayFee)
183     {
184         if (nBlockHeight <= nBestSeenHeight)
185         {
186             // Ignore side chains and re-orgs; assuming they are random
187             // they don't affect the estimate.
188             // And if an attacker can re-org the chain at will, then
189             // you've got much bigger problems than "attacker can influence
190             // transaction fees."
191             return;
192         }
193         nBestSeenHeight = nBlockHeight;
194
195         // Fill up the history buckets based on how long transactions took
196         // to confirm.
197         std::vector<std::vector<const CTxMemPoolEntry*> > entriesByConfirmations;
198         entriesByConfirmations.resize(history.size());
199         BOOST_FOREACH(const CTxMemPoolEntry& entry, entries)
200         {
201             // How many blocks did it take for miners to include this transaction?
202             int delta = nBlockHeight - entry.GetHeight();
203             if (delta <= 0)
204             {
205                 // Re-org made us lose height, this should only happen if we happen
206                 // to re-org on a difficulty transition point: very rare!
207                 continue;
208             }
209             if ((delta-1) >= (int)history.size())
210                 delta = history.size(); // Last bucket is catch-all
211             entriesByConfirmations.at(delta-1).push_back(&entry);
212         }
213         for (size_t i = 0; i < entriesByConfirmations.size(); i++)
214         {
215             std::vector<const CTxMemPoolEntry*> &e = entriesByConfirmations.at(i);
216             // Insert at most 10 random entries per bucket, otherwise a single block
217             // can dominate an estimate:
218             if (e.size() > 10) {
219                 std::random_shuffle(e.begin(), e.end());
220                 e.resize(10);
221             }
222             BOOST_FOREACH(const CTxMemPoolEntry* entry, e)
223             {
224                 // Fees are stored and reported as BTC-per-kb:
225                 CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
226                 double dPriority = entry->GetPriority(entry->GetHeight()); // Want priority when it went IN
227                 seenTxConfirm(feeRate, minRelayFee, dPriority, i);
228             }
229         }
230
231         //After new samples are added, we have to clear the sorted lists,
232         //so they'll be resorted the next time someone asks for an estimate
233         sortedFeeSamples.clear();
234         sortedPrioritySamples.clear();
235
236         for (size_t i = 0; i < history.size(); i++) {
237             if (history[i].FeeSamples() + history[i].PrioritySamples() > 0)
238                 LogPrint("estimatefee", "estimates: for confirming within %d blocks based on %d/%d samples, fee=%s, prio=%g\n", 
239                          i,
240                          history[i].FeeSamples(), history[i].PrioritySamples(),
241                          estimateFee(i+1).ToString(), estimatePriority(i+1));
242         }
243     }
244
245     // Can return CFeeRate(0) if we don't have any data for that many blocks back. nBlocksToConfirm is 1 based.
246     CFeeRate estimateFee(int nBlocksToConfirm)
247     {
248         nBlocksToConfirm--;
249
250         if (nBlocksToConfirm < 0 || nBlocksToConfirm >= (int)history.size())
251             return CFeeRate(0);
252
253         if (sortedFeeSamples.size() == 0)
254         {
255             for (size_t i = 0; i < history.size(); i++)
256                 history.at(i).GetFeeSamples(sortedFeeSamples);
257             std::sort(sortedFeeSamples.begin(), sortedFeeSamples.end(),
258                       std::greater<CFeeRate>());
259         }
260         if (sortedFeeSamples.size() < 11)
261         {
262             // Eleven is Gavin's Favorite Number
263             // ... but we also take a maximum of 10 samples per block so eleven means
264             // we're getting samples from at least two different blocks
265             return CFeeRate(0);
266         }
267
268         int nBucketSize = history.at(nBlocksToConfirm).FeeSamples();
269
270         // Estimates should not increase as number of confirmations goes up,
271         // but the estimates are noisy because confirmations happen discretely
272         // in blocks. To smooth out the estimates, use all samples in the history
273         // and use the nth highest where n is (number of samples in previous bucket +
274         // half the samples in nBlocksToConfirm bucket):
275         size_t nPrevSize = 0;
276         for (int i = 0; i < nBlocksToConfirm; i++)
277             nPrevSize += history.at(i).FeeSamples();
278         size_t index = min(nPrevSize + nBucketSize/2, sortedFeeSamples.size()-1);
279         return sortedFeeSamples[index];
280     }
281     double estimatePriority(int nBlocksToConfirm)
282     {
283         nBlocksToConfirm--;
284
285         if (nBlocksToConfirm < 0 || nBlocksToConfirm >= (int)history.size())
286             return -1;
287
288         if (sortedPrioritySamples.size() == 0)
289         {
290             for (size_t i = 0; i < history.size(); i++)
291                 history.at(i).GetPrioritySamples(sortedPrioritySamples);
292             std::sort(sortedPrioritySamples.begin(), sortedPrioritySamples.end(),
293                       std::greater<double>());
294         }
295         if (sortedPrioritySamples.size() < 11)
296             return -1.0;
297
298         int nBucketSize = history.at(nBlocksToConfirm).PrioritySamples();
299
300         // Estimates should not increase as number of confirmations needed goes up,
301         // but the estimates are noisy because confirmations happen discretely
302         // in blocks. To smooth out the estimates, use all samples in the history
303         // and use the nth highest where n is (number of samples in previous buckets +
304         // half the samples in nBlocksToConfirm bucket).
305         size_t nPrevSize = 0;
306         for (int i = 0; i < nBlocksToConfirm; i++)
307             nPrevSize += history.at(i).PrioritySamples();
308         size_t index = min(nPrevSize + nBucketSize/2, sortedPrioritySamples.size()-1);
309         return sortedPrioritySamples[index];
310     }
311
312     void Write(CAutoFile& fileout) const
313     {
314         fileout << nBestSeenHeight;
315         fileout << history.size();
316         BOOST_FOREACH(const CBlockAverage& entry, history)
317         {
318             entry.Write(fileout);
319         }
320     }
321
322     void Read(CAutoFile& filein, const CFeeRate& minRelayFee)
323     {
324         int nFileBestSeenHeight;
325         filein >> nFileBestSeenHeight;
326         size_t numEntries;
327         filein >> numEntries;
328         if (numEntries <= 0 || numEntries > 10000)
329             throw runtime_error("Corrupt estimates file.  Must have between 1 and 10k entires.");
330
331         std::vector<CBlockAverage> fileHistory;
332         
333         for (size_t i = 0; i < numEntries; i++)
334         {
335             CBlockAverage entry;
336             entry.Read(filein, minRelayFee);
337             fileHistory.push_back(entry);
338         }
339
340         //Now that we've processed the entire fee estimate data file and not
341         //thrown any errors, we can copy it to our history
342         nBestSeenHeight = nFileBestSeenHeight;
343         history = fileHistory;
344         assert(history.size() > 0);
345     }
346 };
347
348
349 CTxMemPool::CTxMemPool(const CFeeRate& _minRelayFee) :
350     nTransactionsUpdated(0),
351     minRelayFee(_minRelayFee)
352 {
353     // Sanity checks off by default for performance, because otherwise
354     // accepting transactions becomes O(N^2) where N is the number
355     // of transactions in the pool
356     fSanityCheck = false;
357
358     // 25 blocks is a compromise between using a lot of disk/memory and
359     // trying to give accurate estimates to people who might be willing
360     // to wait a day or two to save a fraction of a penny in fees.
361     // Confirmation times for very-low-fee transactions that take more
362     // than an hour or three to confirm are highly variable.
363     minerPolicyEstimator = new CMinerPolicyEstimator(25);
364 }
365
366 CTxMemPool::~CTxMemPool()
367 {
368     delete minerPolicyEstimator;
369 }
370
371 void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
372 {
373     LOCK(cs);
374
375     std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0));
376
377     // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx
378     while (it != mapNextTx.end() && it->first.hash == hashTx) {
379         coins.Spend(it->first.n); // and remove those outputs from coins
380         it++;
381     }
382 }
383
384 unsigned int CTxMemPool::GetTransactionsUpdated() const
385 {
386     LOCK(cs);
387     return nTransactionsUpdated;
388 }
389
390 void CTxMemPool::AddTransactionsUpdated(unsigned int n)
391 {
392     LOCK(cs);
393     nTransactionsUpdated += n;
394 }
395
396
397 bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry)
398 {
399     // Add to memory pool without checking anything.
400     // Used by main.cpp AcceptToMemoryPool(), which DOES do
401     // all the appropriate checks.
402     LOCK(cs);
403     {
404         mapTx[hash] = entry;
405         const CTransaction& tx = mapTx[hash].GetTx();
406         for (unsigned int i = 0; i < tx.vin.size(); i++)
407             mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
408         nTransactionsUpdated++;
409         totalTxSize += entry.GetTxSize();
410     }
411     return true;
412 }
413
414
415 void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive)
416 {
417     // Remove transaction from memory pool
418     {
419         LOCK(cs);
420         uint256 hash = tx.GetHash();
421         if (fRecursive) {
422             for (unsigned int i = 0; i < tx.vout.size(); i++) {
423                 std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i));
424                 if (it == mapNextTx.end())
425                     continue;
426                 remove(*it->second.ptx, removed, true);
427             }
428         }
429         if (mapTx.count(hash))
430         {
431             removed.push_front(tx);
432             BOOST_FOREACH(const CTxIn& txin, tx.vin)
433                 mapNextTx.erase(txin.prevout);
434
435             totalTxSize -= mapTx[hash].GetTxSize();
436             mapTx.erase(hash);
437             nTransactionsUpdated++;
438         }
439     }
440 }
441
442 void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
443 {
444     // Remove transactions which depend on inputs of tx, recursively
445     list<CTransaction> result;
446     LOCK(cs);
447     BOOST_FOREACH(const CTxIn &txin, tx.vin) {
448         std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);
449         if (it != mapNextTx.end()) {
450             const CTransaction &txConflict = *it->second.ptx;
451             if (txConflict != tx)
452             {
453                 remove(txConflict, removed, true);
454             }
455         }
456     }
457 }
458
459 // Called when a block is connected. Removes from mempool and updates the miner fee estimator.
460 void CTxMemPool::removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
461                                 std::list<CTransaction>& conflicts)
462 {
463     LOCK(cs);
464     std::vector<CTxMemPoolEntry> entries;
465     BOOST_FOREACH(const CTransaction& tx, vtx)
466     {
467         uint256 hash = tx.GetHash();
468         if (mapTx.count(hash))
469             entries.push_back(mapTx[hash]);
470     }
471     minerPolicyEstimator->seenBlock(entries, nBlockHeight, minRelayFee);
472     BOOST_FOREACH(const CTransaction& tx, vtx)
473     {
474         std::list<CTransaction> dummy;
475         remove(tx, dummy, false);
476         removeConflicts(tx, conflicts);
477         ClearPrioritisation(tx.GetHash());
478     }
479 }
480
481
482 void CTxMemPool::clear()
483 {
484     LOCK(cs);
485     mapTx.clear();
486     mapNextTx.clear();
487     totalTxSize = 0;
488     ++nTransactionsUpdated;
489 }
490
491 void CTxMemPool::check(const CCoinsViewCache *pcoins) const
492 {
493     if (!fSanityCheck)
494         return;
495
496     LogPrint("mempool", "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size());
497
498     uint64_t checkTotal = 0;
499
500     LOCK(cs);
501     for (std::map<uint256, CTxMemPoolEntry>::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
502         unsigned int i = 0;
503         checkTotal += it->second.GetTxSize();
504         const CTransaction& tx = it->second.GetTx();
505         BOOST_FOREACH(const CTxIn &txin, tx.vin) {
506             // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's.
507             std::map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(txin.prevout.hash);
508             if (it2 != mapTx.end()) {
509                 const CTransaction& tx2 = it2->second.GetTx();
510                 assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull());
511             } else {
512                 const CCoins* coins = pcoins->AccessCoins(txin.prevout.hash);
513                 assert(coins && coins->IsAvailable(txin.prevout.n));
514             }
515             // Check whether its inputs are marked in mapNextTx.
516             std::map<COutPoint, CInPoint>::const_iterator it3 = mapNextTx.find(txin.prevout);
517             assert(it3 != mapNextTx.end());
518             assert(it3->second.ptx == &tx);
519             assert(it3->second.n == i);
520             i++;
521         }
522     }
523     for (std::map<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) {
524         uint256 hash = it->second.ptx->GetHash();
525         map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(hash);
526         const CTransaction& tx = it2->second.GetTx();
527         assert(it2 != mapTx.end());
528         assert(&tx == it->second.ptx);
529         assert(tx.vin.size() > it->second.n);
530         assert(it->first == it->second.ptx->vin[it->second.n].prevout);
531     }
532
533     assert(totalTxSize == checkTotal);
534 }
535
536 void CTxMemPool::queryHashes(vector<uint256>& vtxid)
537 {
538     vtxid.clear();
539
540     LOCK(cs);
541     vtxid.reserve(mapTx.size());
542     for (map<uint256, CTxMemPoolEntry>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi)
543         vtxid.push_back((*mi).first);
544 }
545
546 bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const
547 {
548     LOCK(cs);
549     map<uint256, CTxMemPoolEntry>::const_iterator i = mapTx.find(hash);
550     if (i == mapTx.end()) return false;
551     result = i->second.GetTx();
552     return true;
553 }
554
555 CFeeRate CTxMemPool::estimateFee(int nBlocks) const
556 {
557     LOCK(cs);
558     return minerPolicyEstimator->estimateFee(nBlocks);
559 }
560 double CTxMemPool::estimatePriority(int nBlocks) const
561 {
562     LOCK(cs);
563     return minerPolicyEstimator->estimatePriority(nBlocks);
564 }
565
566 bool
567 CTxMemPool::WriteFeeEstimates(CAutoFile& fileout) const
568 {
569     try {
570         LOCK(cs);
571         fileout << 99900; // version required to read: 0.9.99 or later
572         fileout << CLIENT_VERSION; // version that wrote the file
573         minerPolicyEstimator->Write(fileout);
574     }
575     catch (std::exception &e) {
576         LogPrintf("CTxMemPool::WriteFeeEstimates() : unable to write policy estimator data (non-fatal)");
577         return false;
578     }
579     return true;
580 }
581
582 bool
583 CTxMemPool::ReadFeeEstimates(CAutoFile& filein)
584 {
585     try {
586         int nVersionRequired, nVersionThatWrote;
587         filein >> nVersionRequired >> nVersionThatWrote;
588         if (nVersionRequired > CLIENT_VERSION)
589             return error("CTxMemPool::ReadFeeEstimates() : up-version (%d) fee estimate file", nVersionRequired);
590
591         LOCK(cs);
592         minerPolicyEstimator->Read(filein, minRelayFee);
593     }
594     catch (std::exception &e) {
595         LogPrintf("CTxMemPool::ReadFeeEstimates() : unable to read policy estimator data (non-fatal)");
596         return false;
597     }
598     return true;
599 }
600
601 void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash, double dPriorityDelta, int64_t nFeeDelta)
602 {
603     {
604         LOCK(cs);
605         std::pair<double, int64_t> &deltas = mapDeltas[hash];
606         deltas.first += dPriorityDelta;
607         deltas.second += nFeeDelta;
608     }
609     LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, nFeeDelta);
610 }
611
612 void CTxMemPool::ApplyDeltas(const uint256 hash, double &dPriorityDelta, int64_t &nFeeDelta)
613 {
614     LOCK(cs);
615     std::map<uint256, std::pair<double, int64_t> >::iterator pos = mapDeltas.find(hash);
616     if (pos == mapDeltas.end())
617         return;
618     const std::pair<double, int64_t> &deltas = pos->second;
619     dPriorityDelta += deltas.first;
620     nFeeDelta += deltas.second;
621 }
622
623 void CTxMemPool::ClearPrioritisation(const uint256 hash)
624 {
625     LOCK(cs);
626     mapDeltas.erase(hash);
627 }
628
629
630 CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
631
632 bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
633     // If an entry in the mempool exists, always return that one, as it's guaranteed to never
634     // conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
635     // transactions. First checking the underlying cache risks returning a pruned entry instead.
636     CTransaction tx;
637     if (mempool.lookup(txid, tx)) {
638         coins = CCoins(tx, MEMPOOL_HEIGHT);
639         return true;
640     }
641     return (base->GetCoins(txid, coins) && !coins.IsPruned());
642 }
643
644 bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
645     return mempool.exists(txid) || base->HaveCoins(txid);
646 }
647
This page took 0.062429 seconds and 4 git commands to generate.