mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-19 13:27:35 +02:00
Add spentness flag to pegin coins
This commit is contained in:
parent
0d07d21a2e
commit
f4adb464e8
7 changed files with 134 additions and 19 deletions
|
|
@ -12,6 +12,8 @@ uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||||
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
|
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
|
||||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
||||||
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
|
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
|
||||||
|
// ELEMENTS:
|
||||||
|
bool CCoinsView::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const { return false; }
|
||||||
|
|
||||||
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +30,8 @@ void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
||||||
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
|
CCoinsViewCursor *CCoinsViewBacked::Cursor() const { return base->Cursor(); }
|
||||||
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
|
size_t CCoinsViewBacked::EstimateSize() const { return base->EstimateSize(); }
|
||||||
|
// ELEMENTS:
|
||||||
|
bool CCoinsViewBacked::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const { return base->IsPeginSpent(outpoint); }
|
||||||
|
|
||||||
SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
|
SaltedOutpointHasher::SaltedOutpointHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
|
||||||
|
|
||||||
|
|
@ -141,6 +145,56 @@ bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
|
||||||
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// ELEMENTS:
|
||||||
|
|
||||||
|
bool CCoinsViewCache::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const {
|
||||||
|
assert(!outpoint.second.hash.IsNull());
|
||||||
|
assert(!outpoint.first.IsNull());
|
||||||
|
|
||||||
|
CCoinsMap::iterator it = cacheCoins.find(outpoint);
|
||||||
|
if (it == cacheCoins.end()) {
|
||||||
|
bool inserted;
|
||||||
|
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(outpoint), std::tuple<>());
|
||||||
|
it->second.peginSpent = base->IsPeginSpent(outpoint);
|
||||||
|
it->second.flags |= CCoinsCacheEntry::PEGIN;
|
||||||
|
if (!it->second.peginSpent)
|
||||||
|
it->second.flags |= CCoinsCacheEntry::FRESH;
|
||||||
|
}
|
||||||
|
return it->second.peginSpent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCoinsViewCache::SetPeginSpent(const std::pair<uint256, COutPoint> &outpoint, bool fSpent) {
|
||||||
|
assert(!outpoint.second.hash.IsNull());
|
||||||
|
assert(!outpoint.first.IsNull());
|
||||||
|
|
||||||
|
CCoinsMap::iterator it = cacheCoins.find(outpoint);
|
||||||
|
|
||||||
|
bool hadSpent;
|
||||||
|
if (it == cacheCoins.end())
|
||||||
|
hadSpent = base->IsPeginSpent(outpoint);
|
||||||
|
else
|
||||||
|
hadSpent = it->second.peginSpent;
|
||||||
|
|
||||||
|
// If we aren't changing spentness, dont do anything at all
|
||||||
|
if (hadSpent == fSpent)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it == cacheCoins.end()) {
|
||||||
|
bool inserted;
|
||||||
|
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct,
|
||||||
|
std::forward_as_tuple(outpoint), std::tuple<>());
|
||||||
|
if (!hadSpent)
|
||||||
|
it->second.flags = CCoinsCacheEntry::FRESH;
|
||||||
|
}
|
||||||
|
it->second.peginSpent = fSpent;
|
||||||
|
it->second.flags |= CCoinsCacheEntry::PEGIN | CCoinsCacheEntry::DIRTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// END ELEMENTS
|
||||||
|
//
|
||||||
|
|
||||||
uint256 CCoinsViewCache::GetBestBlock() const {
|
uint256 CCoinsViewCache::GetBestBlock() const {
|
||||||
if (hashBlock.IsNull())
|
if (hashBlock.IsNull())
|
||||||
hashBlock = base->GetBestBlock();
|
hashBlock = base->GetBestBlock();
|
||||||
|
|
@ -157,17 +211,28 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||||
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
bool fIsPegin = it->second.flags & CCoinsCacheEntry::PEGIN;
|
||||||
|
|
||||||
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
|
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
|
||||||
if (itUs == cacheCoins.end()) {
|
if (itUs == cacheCoins.end()) {
|
||||||
// The parent cache does not have an entry, while the child does
|
// The parent cache does not have an entry, while the child does
|
||||||
// We can ignore it if it's both FRESH and pruned in the child
|
// We can ignore it if it's both FRESH and {pruned, spent pegin} in the child
|
||||||
if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
|
if (!((it->second.flags & CCoinsCacheEntry::FRESH) &&
|
||||||
|
(( fIsPegin && !it->second.peginSpent) ||
|
||||||
|
(!fIsPegin && it->second.coin.IsSpent())))) {
|
||||||
// Otherwise we will need to create it in the parent
|
// Otherwise we will need to create it in the parent
|
||||||
// and move the data up and mark it as dirty
|
// and move the data up and mark it as dirty
|
||||||
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
||||||
entry.coin = std::move(it->second.coin);
|
|
||||||
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
|
|
||||||
entry.flags = CCoinsCacheEntry::DIRTY;
|
entry.flags = CCoinsCacheEntry::DIRTY;
|
||||||
|
if (fIsPegin) {
|
||||||
|
entry.peginSpent = it->second.peginSpent;
|
||||||
|
entry.flags |= CCoinsCacheEntry::PEGIN;
|
||||||
|
} else {
|
||||||
|
entry.coin = it->second.coin;
|
||||||
|
}
|
||||||
|
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
|
||||||
// We can mark it FRESH in the parent if it was FRESH in the child
|
// We can mark it FRESH in the parent if it was FRESH in the child
|
||||||
// Otherwise it might have just been flushed from the parent's cache
|
// Otherwise it might have just been flushed from the parent's cache
|
||||||
// and already exist in the grandparent
|
// and already exist in the grandparent
|
||||||
|
|
@ -185,7 +250,8 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||||
}
|
}
|
||||||
|
|
||||||
// Found the entry in the parent cache
|
// Found the entry in the parent cache
|
||||||
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
|
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) &&
|
||||||
|
((fIsPegin && !it->second.peginSpent) || (!fIsPegin && it->second.coin.IsSpent()))) {
|
||||||
// The grandparent does not have an entry, and the child is
|
// The grandparent does not have an entry, and the child is
|
||||||
// modified and being pruned. This means we can just delete
|
// modified and being pruned. This means we can just delete
|
||||||
// it from the parent.
|
// it from the parent.
|
||||||
|
|
@ -194,7 +260,11 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||||
} else {
|
} else {
|
||||||
// A normal modification.
|
// A normal modification.
|
||||||
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
|
||||||
itUs->second.coin = std::move(it->second.coin);
|
if (fIsPegin) {
|
||||||
|
itUs->second.peginSpent = it->second.peginSpent;
|
||||||
|
} else {
|
||||||
|
itUs->second.coin = it->second.coin;
|
||||||
|
}
|
||||||
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
|
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
|
||||||
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
||||||
// NOTE: It is possible the child has a FRESH flag here in
|
// NOTE: It is possible the child has a FRESH flag here in
|
||||||
|
|
|
||||||
13
src/coins.h
13
src/coins.h
|
|
@ -85,6 +85,8 @@ struct CCoinsCacheEntry
|
||||||
{
|
{
|
||||||
Coin coin; // The actual cached data.
|
Coin coin; // The actual cached data.
|
||||||
unsigned char flags;
|
unsigned char flags;
|
||||||
|
// ELEMENTS:
|
||||||
|
bool peginSpent;
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||||
|
|
@ -94,6 +96,8 @@ struct CCoinsCacheEntry
|
||||||
* flush the changes to the parent cache. It is always safe to
|
* flush the changes to the parent cache. It is always safe to
|
||||||
* not mark FRESH if that condition is not guaranteed.
|
* not mark FRESH if that condition is not guaranteed.
|
||||||
*/
|
*/
|
||||||
|
// ELEMENTS:
|
||||||
|
PEGIN = (1 << 2), // represents a pegin (coins is actually empty/useless, look at peginSpent instead)
|
||||||
};
|
};
|
||||||
|
|
||||||
CCoinsCacheEntry() : flags(0) {}
|
CCoinsCacheEntry() : flags(0) {}
|
||||||
|
|
@ -159,6 +163,10 @@ public:
|
||||||
//! Just check whether a given outpoint is unspent.
|
//! Just check whether a given outpoint is unspent.
|
||||||
virtual bool HaveCoin(const COutPoint &outpoint) const;
|
virtual bool HaveCoin(const COutPoint &outpoint) const;
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
//! Check if a given pegin has been spent
|
||||||
|
virtual bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const;
|
||||||
|
|
||||||
//! Retrieve the block hash whose state this CCoinsView currently represents
|
//! Retrieve the block hash whose state this CCoinsView currently represents
|
||||||
virtual uint256 GetBestBlock() const;
|
virtual uint256 GetBestBlock() const;
|
||||||
|
|
||||||
|
|
@ -199,6 +207,8 @@ public:
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
||||||
CCoinsViewCursor *Cursor() const override;
|
CCoinsViewCursor *Cursor() const override;
|
||||||
size_t EstimateSize() const override;
|
size_t EstimateSize() const override;
|
||||||
|
// ELEMENTS:
|
||||||
|
bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -233,6 +243,9 @@ public:
|
||||||
CCoinsViewCursor* Cursor() const override {
|
CCoinsViewCursor* Cursor() const override {
|
||||||
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
|
throw std::logic_error("CCoinsViewCache cursor iteration not supported.");
|
||||||
}
|
}
|
||||||
|
// ELEMENTS:
|
||||||
|
bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const override;
|
||||||
|
void SetPeginSpent(const std::pair<uint256, COutPoint> &outpoint, bool fSpent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we have the given utxo already loaded in this cache.
|
* Check if we have the given utxo already loaded in this cache.
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out);
|
int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out, const CTxIn& txin, const CScriptWitness& pegin_witness);
|
||||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight);
|
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight);
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
@ -57,10 +57,10 @@ public:
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
// Same optimization used in CCoinsViewDB is to only write dirty entries.
|
||||||
map_[it->first] = it->second.coin;
|
map_[it->first.second] = it->second.coin;
|
||||||
if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
|
if (it->second.coin.IsSpent() && InsecureRandRange(3) == 0) {
|
||||||
// Randomly delete empty entries on write.
|
// Randomly delete empty entries on write.
|
||||||
map_.erase(it->first);
|
map_.erase(it->first.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapCoins.erase(it++);
|
mapCoins.erase(it++);
|
||||||
|
|
@ -407,7 +407,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test)
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsCoinBase()) {
|
||||||
const COutPoint &out = tx.vin[0].prevout;
|
const COutPoint &out = tx.vin[0].prevout;
|
||||||
Coin coin = undo.vprevout[0];
|
Coin coin = undo.vprevout[0];
|
||||||
ApplyTxInUndo(std::move(coin), *(stack.back()), out);
|
ApplyTxInUndo(std::move(coin), *(stack.back()), out, tx.vin[0], tx.vin[0].m_pegin_witness);
|
||||||
}
|
}
|
||||||
// Store as a candidate for reconnection
|
// Store as a candidate for reconnection
|
||||||
disconnected_coins.insert(utxod->first);
|
disconnected_coins.insert(utxod->first);
|
||||||
|
|
@ -525,7 +525,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const static COutPoint OUTPOINT;
|
const static std::pair<uint256, COutPoint> OUTPOINT;
|
||||||
const static CAmount PRUNED = -1;
|
const static CAmount PRUNED = -1;
|
||||||
const static CAmount ABSENT = -2;
|
const static CAmount ABSENT = -2;
|
||||||
const static CAmount FAIL = -3;
|
const static CAmount FAIL = -3;
|
||||||
|
|
@ -608,7 +608,7 @@ public:
|
||||||
static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
|
static void CheckAccessCoin(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
|
||||||
{
|
{
|
||||||
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
|
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
|
||||||
test.cache.AccessCoin(OUTPOINT);
|
test.cache.AccessCoin(OUTPOINT.second);
|
||||||
test.cache.SelfTest();
|
test.cache.SelfTest();
|
||||||
|
|
||||||
CAmount result_value;
|
CAmount result_value;
|
||||||
|
|
@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(ccoins_access)
|
||||||
static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
|
static void CheckSpendCoins(CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
|
||||||
{
|
{
|
||||||
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
|
SingleEntryCacheTest test(base_value, cache_value, cache_flags);
|
||||||
test.cache.SpendCoin(OUTPOINT);
|
test.cache.SpendCoin(OUTPOINT.second);
|
||||||
test.cache.SelfTest();
|
test.cache.SelfTest();
|
||||||
|
|
||||||
CAmount result_value;
|
CAmount result_value;
|
||||||
|
|
@ -716,7 +716,7 @@ static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount mo
|
||||||
try {
|
try {
|
||||||
CTxOut output;
|
CTxOut output;
|
||||||
output.nValue = modify_value;
|
output.nValue = modify_value;
|
||||||
test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase);
|
test.cache.AddCoin(OUTPOINT.second, Coin(std::move(output), 1, coinbase), coinbase);
|
||||||
test.cache.SelfTest();
|
test.cache.SelfTest();
|
||||||
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
|
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
|
||||||
} catch (std::logic_error& e) {
|
} catch (std::logic_error& e) {
|
||||||
|
|
|
||||||
31
src/txdb.cpp
31
src/txdb.cpp
|
|
@ -32,6 +32,9 @@ static const char DB_FLAG = 'F';
|
||||||
static const char DB_REINDEX_FLAG = 'R';
|
static const char DB_REINDEX_FLAG = 'R';
|
||||||
static const char DB_LAST_BLOCK = 'l';
|
static const char DB_LAST_BLOCK = 'l';
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
static const char DB_PEGIN_FLAG = 'w';
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct CoinEntry {
|
struct CoinEntry {
|
||||||
|
|
@ -68,6 +71,11 @@ bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
|
||||||
return db.Exists(CoinEntry(&outpoint));
|
return db.Exists(CoinEntry(&outpoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
bool CCoinsViewDB::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const {
|
||||||
|
return db.Exists(std::make_pair(DB_PEGIN_FLAG, outpoint));
|
||||||
|
}
|
||||||
|
|
||||||
uint256 CCoinsViewDB::GetBestBlock() const {
|
uint256 CCoinsViewDB::GetBestBlock() const {
|
||||||
uint256 hashBestChain;
|
uint256 hashBestChain;
|
||||||
if (!db.Read(DB_BEST_BLOCK, hashBestChain))
|
if (!db.Read(DB_BEST_BLOCK, hashBestChain))
|
||||||
|
|
@ -110,11 +118,24 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
||||||
|
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
CoinEntry entry(&it->first);
|
// ELEMENTS:
|
||||||
if (it->second.coin.IsSpent())
|
if (it->second.flags & CCoinsCacheEntry::PEGIN) {
|
||||||
batch.Erase(entry);
|
if (!it->second.peginSpent) {
|
||||||
else
|
batch.Erase(std::make_pair(DB_PEGIN_FLAG, it->first));
|
||||||
batch.Write(entry, it->second.coin);
|
} else {
|
||||||
|
// Once spent, we don't care about the entry data, so we store
|
||||||
|
// a static byte to indicate spentness.
|
||||||
|
batch.Write(std::make_pair(DB_PEGIN_FLAG, it->first), '1');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Non-pegin entries are stored the same way as in Core.
|
||||||
|
CoinEntry entry(&it->first.second);
|
||||||
|
if (it->second.coin.IsSpent()) {
|
||||||
|
batch.Erase(entry);
|
||||||
|
} else {
|
||||||
|
batch.Write(entry, it->second.coin);
|
||||||
|
}
|
||||||
|
}
|
||||||
changed++;
|
changed++;
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ public:
|
||||||
std::vector<uint256> GetHeadBlocks() const override;
|
std::vector<uint256> GetHeadBlocks() const override;
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override;
|
||||||
CCoinsViewCursor *Cursor() const override;
|
CCoinsViewCursor *Cursor() const override;
|
||||||
|
// ELEMENTS:
|
||||||
|
bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const override;
|
||||||
|
|
||||||
//! Attempt to update from an older database format. Returns whether an error occurred.
|
//! Attempt to update from an older database format. Returns whether an error occurred.
|
||||||
bool Upgrade();
|
bool Upgrade();
|
||||||
|
|
|
||||||
|
|
@ -898,6 +898,11 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
||||||
return base->GetCoin(outpoint, coin);
|
return base->GetCoin(outpoint, coin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
bool CCoinsViewMemPool::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const {
|
||||||
|
return mempool.mapPeginsSpentToTxid.count(outpoint) || base->IsPeginSpent(outpoint);
|
||||||
|
}
|
||||||
|
|
||||||
size_t CTxMemPool::DynamicMemoryUsage() const {
|
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
// Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
// Estimate the overhead of mapTx to be 12 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
||||||
|
|
|
||||||
|
|
@ -499,6 +499,8 @@ public:
|
||||||
const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
uint64_t CalculateDescendantMaximum(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
// ELEMENTS:
|
||||||
|
std::map<std::pair<uint256, COutPoint>, uint256> mapPeginsSpentToTxid;
|
||||||
private:
|
private:
|
||||||
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
||||||
|
|
||||||
|
|
@ -711,6 +713,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
|
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
|
||||||
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override;
|
||||||
|
// ELEMENTS:
|
||||||
|
bool IsPeginSpent(const std::pair<uint256, COutPoint> &outpoint) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue