Do not try to search only in cache, flush can discard changes
Signed-off-by: Anthony Fieroni <bvbfan@abv.bg>
This commit is contained in:
parent
18e4372eb9
commit
489edb7e0b
8 changed files with 20 additions and 37 deletions
|
@ -10,14 +10,9 @@
|
||||||
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
|
bool CCoinsView::GetCoin(const COutPoint &outpoint, Coin &coin) const { return false; }
|
||||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
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, bool sync) { return false; }
|
bool CCoinsView::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return false; }
|
||||||
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
|
CCoinsViewCursor *CCoinsView::Cursor() const { return nullptr; }
|
||||||
|
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const { return false; }
|
||||||
bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
|
|
||||||
{
|
|
||||||
Coin coin;
|
|
||||||
return GetCoin(outpoint, coin);
|
|
||||||
}
|
|
||||||
|
|
||||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||||
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
|
bool CCoinsViewBacked::GetCoin(const COutPoint &outpoint, Coin &coin) const { return base->GetCoin(outpoint, coin); }
|
||||||
|
@ -25,7 +20,7 @@ bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->
|
||||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||||
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
|
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
|
||||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return base->BatchWrite(mapCoins, hashBlock, sync); }
|
bool CCoinsViewBacked::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) { return base->BatchWrite(mapCoins, hashBlock, sync); }
|
||||||
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(); }
|
||||||
|
|
||||||
|
@ -127,11 +122,6 @@ bool CCoinsViewCache::HaveCoin(const COutPoint &outpoint) const {
|
||||||
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::HaveCoinInCache(const COutPoint &outpoint) const {
|
|
||||||
CCoinsMap::const_iterator it = cacheCoins.find(outpoint);
|
|
||||||
return (it != cacheCoins.end() && !it->second.coin.IsSpent());
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 CCoinsViewCache::GetBestBlock() const {
|
uint256 CCoinsViewCache::GetBestBlock() const {
|
||||||
if (hashBlock.IsNull())
|
if (hashBlock.IsNull())
|
||||||
hashBlock = base->GetBestBlock();
|
hashBlock = base->GetBestBlock();
|
||||||
|
@ -142,8 +132,8 @@ void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
||||||
hashBlock = hashBlockIn;
|
hashBlock = hashBlockIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool sync) {
|
bool CCoinsViewCache::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlockIn, bool sync) {
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
|
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
|
||||||
// Ignore non-dirty entries (optimization).
|
// Ignore non-dirty entries (optimization).
|
||||||
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
if (!(it->second.flags & CCoinsCacheEntry::DIRTY)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
13
src/coins.h
13
src/coins.h
|
@ -163,7 +163,7 @@ public:
|
||||||
|
|
||||||
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
//! Do a bulk modification (multiple Coin changes + BestBlock change).
|
||||||
//! The passed mapCoins can be modified.
|
//! The passed mapCoins can be modified.
|
||||||
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync);
|
virtual bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync);
|
||||||
|
|
||||||
//! Get a cursor to iterate over the whole state
|
//! Get a cursor to iterate over the whole state
|
||||||
virtual CCoinsViewCursor *Cursor() const;
|
virtual CCoinsViewCursor *Cursor() const;
|
||||||
|
@ -189,7 +189,7 @@ public:
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
std::vector<uint256> GetHeadBlocks() const override;
|
std::vector<uint256> GetHeadBlocks() const override;
|
||||||
void SetBackend(CCoinsView &viewIn);
|
void SetBackend(CCoinsView &viewIn);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
||||||
CCoinsViewCursor *Cursor() const override;
|
CCoinsViewCursor *Cursor() const override;
|
||||||
size_t EstimateSize() const override;
|
size_t EstimateSize() const override;
|
||||||
};
|
};
|
||||||
|
@ -222,18 +222,11 @@ public:
|
||||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
void SetBestBlock(const uint256 &hashBlock);
|
void SetBestBlock(const uint256 &hashBlock);
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
||||||
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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if we have the given utxo already loaded in this cache.
|
|
||||||
* The semantics are the same as HaveCoin(), but no calls to
|
|
||||||
* the backing CCoinsView are made.
|
|
||||||
*/
|
|
||||||
bool HaveCoinInCache(const COutPoint &outpoint) const;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a reference to Coin in the cache, or a pruned one if not found. This is
|
* Return a reference to Coin in the cache, or a pruned one if not found. This is
|
||||||
* more efficient than GetCoin.
|
* more efficient than GetCoin.
|
||||||
|
|
|
@ -1049,8 +1049,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
|
|
||||||
return recentRejects->contains(inv.hash) ||
|
return recentRejects->contains(inv.hash) ||
|
||||||
mempool.exists(inv.hash) ||
|
mempool.exists(inv.hash) ||
|
||||||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
|
pcoinsTip->HaveCoin(COutPoint(inv.hash, 0)) || // Best effort: only try output 0 and 1
|
||||||
pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1));
|
pcoinsTip->HaveCoin(COutPoint(inv.hash, 1));
|
||||||
}
|
}
|
||||||
case MSG_BLOCK:
|
case MSG_BLOCK:
|
||||||
case MSG_WITNESS_BLOCK:
|
case MSG_WITNESS_BLOCK:
|
||||||
|
|
|
@ -54,9 +54,9 @@ public:
|
||||||
|
|
||||||
uint256 GetBestBlock() const override { return hashBestBlock_; }
|
uint256 GetBestBlock() const override { return hashBestBlock_; }
|
||||||
|
|
||||||
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock, bool sync) override
|
bool BatchWrite(const CCoinsMap& mapCoins, const uint256& hashBlock, bool sync) override
|
||||||
{
|
{
|
||||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
|
||||||
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] = it->second.coin;
|
||||||
|
@ -65,7 +65,6 @@ public:
|
||||||
map_.erase(it->first);
|
map_.erase(it->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapCoins.erase(it++);
|
|
||||||
}
|
}
|
||||||
if (!hashBlock.IsNull())
|
if (!hashBlock.IsNull())
|
||||||
hashBestBlock_ = hashBlock;
|
hashBestBlock_ = hashBlock;
|
||||||
|
@ -186,7 +185,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
|
||||||
COutPoint out(txids[InsecureRand32() % txids.size()], 0);
|
COutPoint out(txids[InsecureRand32() % txids.size()], 0);
|
||||||
int cacheid = InsecureRand32() % stack.size();
|
int cacheid = InsecureRand32() % stack.size();
|
||||||
stack[cacheid]->Uncache(out);
|
stack[cacheid]->Uncache(out);
|
||||||
uncached_an_entry |= !stack[cacheid]->HaveCoinInCache(out);
|
uncached_an_entry |= !stack[cacheid]->HaveCoin(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once every 1000 iterations and at the end, verify the full cache.
|
// Once every 1000 iterations and at the end, verify the full cache.
|
||||||
|
@ -199,7 +198,7 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
|
||||||
if (coin.IsSpent()) {
|
if (coin.IsSpent()) {
|
||||||
missed_an_entry = true;
|
missed_an_entry = true;
|
||||||
} else {
|
} else {
|
||||||
BOOST_CHECK(stack.back()->HaveCoinInCache(entry.first));
|
BOOST_CHECK(stack.back()->HaveCoin(entry.first));
|
||||||
found_an_entry = true;
|
found_an_entry = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
|
||||||
return vhashHeadBlocks;
|
return vhashHeadBlocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) {
|
bool CCoinsViewDB::BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) {
|
||||||
|
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
size_t changed = 0;
|
size_t changed = 0;
|
||||||
|
@ -105,7 +105,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, boo
|
||||||
db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock;
|
db << "INSERT OR REPLACE INTO marker VALUES('head_block', ?)" << hashBlock;
|
||||||
auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?";
|
auto dbd = db << "DELETE FROM unspent WHERE txID = ? AND txN = ?";
|
||||||
auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)";
|
auto dbi = db << "INSERT OR REPLACE INTO unspent VALUES(?,?,?,?,?,?,?)";
|
||||||
for (auto it = mapCoins.begin(); it != mapCoins.end(); it = mapCoins.erase(it)) {
|
for (auto it = mapCoins.begin(); it != mapCoins.end(); ++it) {
|
||||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
if (it->second.flags & CCoinsCacheEntry::DIRTY) {
|
||||||
if (it->second.coin.IsSpent()) {
|
if (it->second.coin.IsSpent()) {
|
||||||
// at present the "IsSpent" flag is used for both "spent" and "block going backwards"
|
// at present the "IsSpent" flag is used for both "spent" and "block going backwards"
|
||||||
|
|
|
@ -101,7 +101,7 @@ public:
|
||||||
bool HaveCoin(const COutPoint &outpoint) const override;
|
bool HaveCoin(const COutPoint &outpoint) const override;
|
||||||
uint256 GetBestBlock() const override;
|
uint256 GetBestBlock() const override;
|
||||||
std::vector<uint256> GetHeadBlocks() const override;
|
std::vector<uint256> GetHeadBlocks() const override;
|
||||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
bool BatchWrite(const CCoinsMap &mapCoins, const uint256 &hashBlock, bool sync) override;
|
||||||
CCoinsViewCursor *Cursor() const override;
|
CCoinsViewCursor *Cursor() const override;
|
||||||
size_t EstimateSize() const override;
|
size_t EstimateSize() const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -505,14 +505,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
|
|
||||||
// do all inputs exist?
|
// do all inputs exist?
|
||||||
for (const CTxIn& txin : tx.vin) {
|
for (const CTxIn& txin : tx.vin) {
|
||||||
if (!pcoinsTip->HaveCoinInCache(txin.prevout)) {
|
if (!pcoinsTip->HaveCoin(txin.prevout)) {
|
||||||
coins_to_uncache.push_back(txin.prevout);
|
coins_to_uncache.push_back(txin.prevout);
|
||||||
}
|
}
|
||||||
if (!view.HaveCoin(txin.prevout)) {
|
if (!view.HaveCoin(txin.prevout)) {
|
||||||
// Are inputs missing because we already have the tx?
|
// Are inputs missing because we already have the tx?
|
||||||
for (size_t out = 0; out < tx.vout.size(); out++) {
|
for (size_t out = 0; out < tx.vout.size(); out++) {
|
||||||
// Optimistically just do efficient check of cache for outputs
|
// Optimistically just do efficient check of cache for outputs
|
||||||
if (pcoinsTip->HaveCoinInCache(COutPoint(hash, out))) {
|
if (pcoinsTip->HaveCoin(COutPoint(hash, out))) {
|
||||||
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known");
|
return state.Invalid(false, REJECT_DUPLICATE, "txn-already-known");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
||||||
))['hex']
|
))['hex']
|
||||||
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
|
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
|
||||||
node.generate(1)
|
node.generate(1)
|
||||||
|
self.mempool_size = 0
|
||||||
self.check_mempool_result(
|
self.check_mempool_result(
|
||||||
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}],
|
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': '18: txn-already-known'}],
|
||||||
rawtxs=[raw_tx_in_block],
|
rawtxs=[raw_tx_in_block],
|
||||||
|
|
Loading…
Reference in a new issue