Skip to content

Commit 0003911

Browse files
committed
Introduce new per-txout CCoinsViewCache functions
The new functions are: * CCoinsViewCache::AddCoin: Add a single COutPoint/Coin pair. * CCoinsViewCache::SpendCoin: Remove a single COutPoint. * AddCoins: utility function that invokes CCoinsViewCache::AddCoin for each output in a CTransaction. * AccessByTxid: utility function that searches for any output with a given txid. * CCoinsViewCache::AccessCoin: retrieve the Coin for a COutPoint. * CCoinsViewCache::HaveCoins: check whether a non-empty Coin exists for a given COutPoint. The AddCoin and SpendCoin methods will eventually replace ModifyCoins and ModifyNewCoins, AddCoins will replace CCoins::FromTx, and the new AccessCoins and HaveCoins functions will replace their per-txid counterparts. Note that AccessCoin for now returns a copy of the Coin object. In a later commit it will be change to returning a const reference (which keeps working in all call sites).
1 parent bd83111 commit 0003911

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

src/coins.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "coins.h"
66

7+
#include "consensus/consensus.h"
78
#include "memusage.h"
89
#include "random.h"
910

@@ -70,7 +71,7 @@ size_t CCoinsViewCache::DynamicMemoryUsage() const {
7071
return memusage::DynamicUsage(cacheCoins) + cachedCoinsUsage;
7172
}
7273

73-
CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
74+
CCoinsMap::iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
7475
CCoinsMap::iterator it = cacheCoins.find(txid);
7576
if (it != cacheCoins.end())
7677
return it;
@@ -153,6 +154,58 @@ CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid, bool coinbas
153154
return CCoinsModifier(*this, ret.first, 0);
154155
}
155156

157+
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
158+
assert(!coin.IsPruned());
159+
if (coin.out.scriptPubKey.IsUnspendable()) return;
160+
CCoinsMap::iterator it;
161+
bool inserted;
162+
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint.hash), std::tuple<>());
163+
bool fresh = false;
164+
if (!inserted) {
165+
cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage();
166+
}
167+
if (!possible_overwrite) {
168+
if (it->second.coins.IsAvailable(outpoint.n)) {
169+
throw std::logic_error("Adding new coin that replaces non-pruned entry");
170+
}
171+
fresh = it->second.coins.IsPruned() && !(it->second.flags & CCoinsCacheEntry::DIRTY);
172+
}
173+
if (it->second.coins.vout.size() <= outpoint.n) {
174+
it->second.coins.vout.resize(outpoint.n + 1);
175+
}
176+
it->second.coins.vout[outpoint.n] = std::move(coin.out);
177+
it->second.coins.nHeight = coin.nHeight;
178+
it->second.coins.fCoinBase = coin.fCoinBase;
179+
it->second.flags |= CCoinsCacheEntry::DIRTY | (fresh ? CCoinsCacheEntry::FRESH : 0);
180+
cachedCoinsUsage += it->second.coins.DynamicMemoryUsage();
181+
}
182+
183+
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight) {
184+
bool fCoinbase = tx.IsCoinBase();
185+
const uint256& txid = tx.GetHash();
186+
for (size_t i = 0; i < tx.vout.size(); ++i) {
187+
// Pass fCoinbase as the possible_overwrite flag to AddCoin, in order to correctly
188+
// deal with the pre-BIP30 occurrances of duplicate coinbase transactions.
189+
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), fCoinbase);
190+
}
191+
}
192+
193+
void CCoinsViewCache::SpendCoin(const COutPoint &outpoint, Coin* moveout) {
194+
CCoinsMap::iterator it = FetchCoins(outpoint.hash);
195+
if (it == cacheCoins.end()) return;
196+
cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage();
197+
if (moveout && it->second.coins.IsAvailable(outpoint.n)) {
198+
*moveout = Coin(it->second.coins.vout[outpoint.n], it->second.coins.nHeight, it->second.coins.fCoinBase);
199+
}
200+
it->second.coins.Spend(outpoint.n); // Ignore return value: SpendCoin has no effect if no UTXO found.
201+
if (it->second.coins.IsPruned() && it->second.flags & CCoinsCacheEntry::FRESH) {
202+
cacheCoins.erase(it);
203+
} else {
204+
cachedCoinsUsage += it->second.coins.DynamicMemoryUsage();
205+
it->second.flags |= CCoinsCacheEntry::DIRTY;
206+
}
207+
}
208+
156209
const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
157210
CCoinsMap::const_iterator it = FetchCoins(txid);
158211
if (it == cacheCoins.end()) {
@@ -162,6 +215,18 @@ const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
162215
}
163216
}
164217

218+
static const Coin coinEmpty;
219+
220+
const Coin CCoinsViewCache::AccessCoin(const COutPoint &outpoint) const {
221+
CCoinsMap::const_iterator it = FetchCoins(outpoint.hash);
222+
if (it == cacheCoins.end() || !it->second.coins.IsAvailable(outpoint.n)) {
223+
return coinEmpty;
224+
} else {
225+
return Coin(it->second.coins.vout[outpoint.n], it->second.coins.nHeight, it->second.coins.fCoinBase);
226+
}
227+
}
228+
229+
165230
bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
166231
CCoinsMap::const_iterator it = FetchCoins(txid);
167232
// We're using vtx.empty() instead of IsPruned here for performance reasons,
@@ -171,6 +236,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
171236
return (it != cacheCoins.end() && !it->second.coins.vout.empty());
172237
}
173238

239+
bool CCoinsViewCache::HaveCoins(const COutPoint &outpoint) const {
240+
CCoinsMap::const_iterator it = FetchCoins(outpoint.hash);
241+
return (it != cacheCoins.end() && it->second.coins.IsAvailable(outpoint.n));
242+
}
243+
174244
bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const {
175245
CCoinsMap::const_iterator it = cacheCoins.find(txid);
176246
return it != cacheCoins.end();
@@ -318,3 +388,16 @@ CCoinsModifier::~CCoinsModifier()
318388
CCoinsViewCursor::~CCoinsViewCursor()
319389
{
320390
}
391+
392+
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_BASE_SIZE / ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); // TODO: merge with similar definition in undo.h.
393+
394+
const Coin AccessByTxid(const CCoinsViewCache& view, const uint256& txid)
395+
{
396+
COutPoint iter(txid, 0);
397+
while (iter.n < MAX_OUTPUTS_PER_BLOCK) {
398+
const Coin& alternate = view.AccessCoin(iter);
399+
if (!alternate.IsPruned()) return alternate;
400+
++iter.n;
401+
}
402+
return coinEmpty;
403+
}

src/coins.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ class CCoinsViewCache : public CCoinsViewBacked
452452
// Standard CCoinsView methods
453453
bool GetCoins(const uint256 &txid, CCoins &coins) const;
454454
bool HaveCoins(const uint256 &txid) const;
455+
bool HaveCoins(const COutPoint &outpoint) const;
455456
uint256 GetBestBlock() const;
456457
void SetBestBlock(const uint256 &hashBlock);
457458
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
@@ -470,6 +471,14 @@ class CCoinsViewCache : public CCoinsViewBacked
470471
*/
471472
const CCoins* AccessCoins(const uint256 &txid) const;
472473

474+
/**
475+
* Return a copy of a Coin in the cache, or a pruned one if not found. This is
476+
* more efficient than GetCoins. Modifications to other cache entries are
477+
* allowed while accessing the returned pointer.
478+
* TODO: return a reference to a Coin after changing CCoinsViewCache storage.
479+
*/
480+
const Coin AccessCoin(const COutPoint &output) const;
481+
473482
/**
474483
* Return a modifiable reference to a CCoins. If no entry with the given
475484
* txid exists, a new one is created. Simultaneous modifications are not
@@ -488,6 +497,19 @@ class CCoinsViewCache : public CCoinsViewBacked
488497
*/
489498
CCoinsModifier ModifyNewCoins(const uint256 &txid, bool coinbase);
490499

500+
/**
501+
* Add a coin. Set potential_overwrite to true if a non-pruned version may
502+
* already exist.
503+
*/
504+
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool potential_overwrite);
505+
506+
/**
507+
* Spend a coin. Pass moveto in order to get the deleted data.
508+
* If no unspent output exists for the passed outpoint, this call
509+
* has no effect.
510+
*/
511+
void SpendCoin(const COutPoint &outpoint, Coin* moveto = nullptr);
512+
491513
/**
492514
* Push the modifications applied to this cache to its base.
493515
* Failure to call this method before destruction will cause the changes to be forgotten.
@@ -525,12 +547,21 @@ class CCoinsViewCache : public CCoinsViewBacked
525547
friend class CCoinsModifier;
526548

527549
private:
528-
CCoinsMap::const_iterator FetchCoins(const uint256 &txid) const;
550+
CCoinsMap::iterator FetchCoins(const uint256 &txid) const;
529551

530552
/**
531553
* By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache.
532554
*/
533555
CCoinsViewCache(const CCoinsViewCache &);
534556
};
535557

558+
//! Utility function to add all of a transaction's outputs to a cache.
559+
// It assumes that overwrites are only possible for coinbase transactions,
560+
// TODO: pass in a boolean to limit these possible overwrites to known
561+
// (pre-BIP34) cases.
562+
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight);
563+
564+
//! Utility function to find any unspent output with a given txid.
565+
const Coin AccessByTxid(const CCoinsViewCache& cache, const uint256& txid);
566+
536567
#endif // BITCOIN_COINS_H

0 commit comments

Comments
 (0)