Skip to content

Commit

Permalink
refactor: Rely on returned value of GetCoin instead of parameter
Browse files Browse the repository at this point in the history
Also removed the unused coin parameter of GetCoin.

Co-authored-by: Andrew Toth <[email protected]>
  • Loading branch information
l0rinc and andrewtoth committed Sep 18, 2024
1 parent 46dfbf1 commit 4feaa28
Show file tree
Hide file tree
Showing 16 changed files with 85 additions and 113 deletions.
33 changes: 15 additions & 18 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@
#include <random.h>
#include <util/trace.h>

std::optional<Coin> CCoinsView::GetCoin(const COutPoint& outpoint, Coin& coin) const { return std::nullopt; }
std::optional<Coin> CCoinsView::GetCoin(const COutPoint& outpoint) const { return std::nullopt; }
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
std::vector<uint256> CCoinsView::GetHeadBlocks() const { return std::vector<uint256>(); }
bool CCoinsView::BatchWrite(CoinsViewCacheCursor& cursor, const uint256 &hashBlock) { return false; }
std::unique_ptr<CCoinsViewCursor> CCoinsView::Cursor() const { return nullptr; }

bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
{
Coin coin;
return GetCoin(outpoint, coin).has_value();
return GetCoin(outpoint).has_value();
}

CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
std::optional<Coin> CCoinsViewBacked::GetCoin(const COutPoint& outpoint, Coin& coin) const { return base->GetCoin(outpoint, coin); }
std::optional<Coin> CCoinsViewBacked::GetCoin(const COutPoint& outpoint) const { return base->GetCoin(outpoint); }
bool CCoinsViewBacked::HaveCoin(const COutPoint &outpoint) const { return base->HaveCoin(outpoint); }
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
std::vector<uint256> CCoinsViewBacked::GetHeadBlocks() const { return base->GetHeadBlocks(); }
Expand All @@ -45,26 +44,24 @@ size_t CCoinsViewCache::DynamicMemoryUsage() const {
CCoinsMap::iterator CCoinsViewCache::FetchCoin(const COutPoint &outpoint) const {
const auto [ret, inserted] = cacheCoins.try_emplace(outpoint);
if (inserted) {
if (!base->GetCoin(outpoint, ret->second.coin)) {
if (auto coin{base->GetCoin(outpoint)}) {
ret->second.coin = std::move(*coin);
cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
if (ret->second.coin.IsSpent()) { // TODO GetCoin cannot return spent coins
// The parent only has an empty entry for this outpoint; we can consider our version as fresh.
ret->second.AddFlags(CCoinsCacheEntry::FRESH, *ret, m_sentinel);
}
} else {
cacheCoins.erase(ret);
return cacheCoins.end();
}
if (ret->second.coin.IsSpent()) {
// The parent only has an empty entry for this outpoint; we can consider our version as fresh.
ret->second.AddFlags(CCoinsCacheEntry::FRESH, *ret, m_sentinel);
}
cachedCoinsUsage += ret->second.coin.DynamicMemoryUsage();
}
return ret;
}

std::optional<Coin> CCoinsViewCache::GetCoin(const COutPoint& outpoint, Coin& coin) const
std::optional<Coin> CCoinsViewCache::GetCoin(const COutPoint& outpoint) const
{
CCoinsMap::const_iterator it = FetchCoin(outpoint);
if (it != cacheCoins.end()) {
coin = it->second.coin;
if (!coin.IsSpent()) return coin;
}
if (auto it{FetchCoin(outpoint)}; it != cacheCoins.end() && !it->second.coin.IsSpent()) return it->second.coin;
return std::nullopt;
}

Expand Down Expand Up @@ -382,9 +379,9 @@ static ReturnType ExecuteBackedWrapper(Func func, const std::vector<std::functio
}
}

std::optional<Coin> CCoinsViewErrorCatcher::GetCoin(const COutPoint& outpoint, Coin& coin) const
std::optional<Coin> CCoinsViewErrorCatcher::GetCoin(const COutPoint& outpoint) const
{
return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::GetCoin(outpoint, coin); }, m_err_callbacks);
return ExecuteBackedWrapper<std::optional<Coin>>([&]() { return CCoinsViewBacked::GetCoin(outpoint); }, m_err_callbacks);
}

bool CCoinsViewErrorCatcher::HaveCoin(const COutPoint& outpoint) const
Expand Down
8 changes: 4 additions & 4 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ class CCoinsView
{
public:
//! Retrieve the Coin (unspent transaction output) for a given outpoint.
virtual std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const;
virtual std::optional<Coin> GetCoin(const COutPoint& outpoint) const;

//! Just check whether a given outpoint is unspent.
virtual bool HaveCoin(const COutPoint &outpoint) const;
Expand Down Expand Up @@ -341,7 +341,7 @@ class CCoinsViewBacked : public CCoinsView

public:
CCoinsViewBacked(CCoinsView *viewIn);
std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const override;
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
Expand Down Expand Up @@ -381,7 +381,7 @@ class CCoinsViewCache : public CCoinsViewBacked
CCoinsViewCache(const CCoinsViewCache &) = delete;

// Standard CCoinsView methods
std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const override;
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
void SetBestBlock(const uint256 &hashBlock);
Expand Down Expand Up @@ -511,7 +511,7 @@ class CCoinsViewErrorCatcher final : public CCoinsViewBacked
m_err_callbacks.emplace_back(std::move(f));
}

std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const override;
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint &outpoint) const override;

private:
Expand Down
9 changes: 5 additions & 4 deletions src/node/coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins)
LOCK2(cs_main, node.mempool->cs);
CCoinsViewCache& chain_view = node.chainman->ActiveChainstate().CoinsTip();
CCoinsViewMemPool mempool_view(&chain_view, *node.mempool);
for (auto& coin : coins) {
if (!mempool_view.GetCoin(coin.first, coin.second)) {
// Either the coin is not in the CCoinsViewCache or is spent. Clear it.
coin.second.Clear();
for (auto& [outpoint, coin] : coins) {
if (auto c{mempool_view.GetCoin(outpoint)}) {
coin = std::move(*c);
} else {
coin.Clear(); // Either the coin is not in the CCoinsViewCache or is spent
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,9 +352,7 @@ class NodeImpl : public Node
std::optional<Coin> getUnspentOutput(const COutPoint& output) override
{
LOCK(::cs_main);
Coin coin;
if (chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin)) return coin;
return {};
return chainman().ActiveChainstate().CoinsTip().GetCoin(output);
}
TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override
{
Expand Down
7 changes: 3 additions & 4 deletions src/rest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,10 +870,9 @@ static bool rest_getutxos(const std::any& context, HTTPRequest* req, const std::
{
auto process_utxos = [&vOutPoints, &outs, &hits, &active_height, &active_hash, &chainman](const CCoinsView& view, const CTxMemPool* mempool) EXCLUSIVE_LOCKS_REQUIRED(chainman.GetMutex()) {
for (const COutPoint& vOutPoint : vOutPoints) {
Coin coin;
bool hit = (!mempool || !mempool->isSpent(vOutPoint)) && view.GetCoin(vOutPoint, coin);
hits.push_back(hit);
if (hit) outs.emplace_back(std::move(coin));
auto coin = !mempool || !mempool->isSpent(vOutPoint) ? view.GetCoin(vOutPoint) : std::nullopt;
hits.push_back(coin.has_value());
if (coin) outs.emplace_back(std::move(*coin));
}
active_height = chainman.ActiveHeight();
active_hash = chainman.ActiveTip()->GetBlockHash();
Expand Down
21 changes: 9 additions & 12 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1140,35 +1140,32 @@ static RPCHelpMan gettxout()
if (!request.params[2].isNull())
fMempool = request.params[2].get_bool();

Coin coin;
Chainstate& active_chainstate = chainman.ActiveChainstate();
CCoinsViewCache* coins_view = &active_chainstate.CoinsTip();

std::optional<Coin> coin;
if (fMempool) {
const CTxMemPool& mempool = EnsureMemPool(node);
LOCK(mempool.cs);
CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
return UniValue::VNULL;
}
if (!mempool.isSpent(out)) coin = view.GetCoin(out);
} else {
if (!coins_view->GetCoin(out, coin)) {
return UniValue::VNULL;
}
coin = coins_view->GetCoin(out);
}
if (!coin) return UniValue::VNULL;

const CBlockIndex* pindex = active_chainstate.m_blockman.LookupBlockIndex(coins_view->GetBestBlock());
ret.pushKV("bestblock", pindex->GetBlockHash().GetHex());
if (coin.nHeight == MEMPOOL_HEIGHT) {
if (coin->nHeight == MEMPOOL_HEIGHT) {
ret.pushKV("confirmations", 0);
} else {
ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin.nHeight + 1));
ret.pushKV("confirmations", (int64_t)(pindex->nHeight - coin->nHeight + 1));
}
ret.pushKV("value", ValueFromAmount(coin.out.nValue));
ret.pushKV("value", ValueFromAmount(coin->out.nValue));
UniValue o(UniValue::VOBJ);
ScriptToUniv(coin.out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
ScriptToUniv(coin->out.scriptPubKey, /*out=*/o, /*include_hex=*/true, /*include_address=*/true);
ret.pushKV("scriptPubKey", std::move(o));
ret.pushKV("coinbase", (bool)coin.fCoinBase);
ret.pushKV("coinbase", (bool)coin->fCoinBase);

return ret;
},
Expand Down
16 changes: 6 additions & 10 deletions src/test/coins_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,14 @@ class CCoinsViewTest : public CCoinsView
public:
CCoinsViewTest(FastRandomContext& rng) : m_rng{rng} {}

std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const override
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override
{
std::map<COutPoint, Coin>::const_iterator it = map_.find(outpoint);
if (it == map_.end()) {
return std::nullopt;
}
coin = it->second;
if (coin.IsSpent() && m_rng.randbool() == 0) {
// Randomly return std::nullopt in case of an empty entry.
return std::nullopt;
if (auto it{map_.find(outpoint)}; it != map_.end()) {
if (!it->second.IsSpent() || m_rng.randbool()) {
return it->second; // TODO spent coins shouldn't be returned
}
}
return coin;
return std::nullopt;
}

uint256 GetBestBlock() const override { return hashBestBlock_; }
Expand Down
16 changes: 7 additions & 9 deletions src/test/fuzz/coins_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,22 +162,20 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
const bool exists_using_access_coin = !(coin_using_access_coin == EMPTY_COIN);
const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point);
const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point);
Coin coin_using_get_coin;
const bool exists_using_get_coin = coins_view_cache.GetCoin(random_out_point, coin_using_get_coin).has_value();
if (exists_using_get_coin) {
assert(coin_using_get_coin == coin_using_access_coin);
if (auto coin{coins_view_cache.GetCoin(random_out_point)}) {
assert(*coin == coin_using_access_coin);
assert(exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin);
} else {
assert(!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin);
}
assert((exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin && exists_using_get_coin) ||
(!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin && !exists_using_get_coin));
// If HaveCoin on the backend is true, it must also be on the cache if the coin wasn't spent.
const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point);
if (!coin_using_access_coin.IsSpent() && exists_using_have_coin_in_backend) {
assert(exists_using_have_coin);
}
Coin coin_using_backend_get_coin;
if (backend_coins_view.GetCoin(random_out_point, coin_using_backend_get_coin)) {
if (auto coin{backend_coins_view.GetCoin(random_out_point)}) {
assert(exists_using_have_coin_in_backend);
// Note we can't assert that `coin_using_get_coin == coin_using_backend_get_coin` because the coin in
// Note we can't assert that `coin_using_get_coin == *coin` because the coin in
// the cache may have been modified but not yet flushed.
} else {
assert(!exists_using_have_coin_in_backend);
Expand Down
38 changes: 16 additions & 22 deletions src/test/fuzz/coinscache_sim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,11 @@ class CoinsViewBottom final : public CCoinsView
std::map<COutPoint, Coin> m_data;

public:
std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const final
std::optional<Coin> GetCoin(const COutPoint& outpoint) const final
{
auto it = m_data.find(outpoint);
if (it == m_data.end()) {
return std::nullopt;
} else {
coin = it->second;
return coin; // TODO GetCoin shouldn't return spent coins
}
// TODO GetCoin shouldn't return spent coins
if (auto it = m_data.find(outpoint); it != m_data.end()) return it->second;
return std::nullopt;
}

bool HaveCoin(const COutPoint& outpoint) const final
Expand Down Expand Up @@ -265,17 +261,16 @@ FUZZ_TARGET(coinscache_sim)
// Look up in simulation data.
auto sim = lookup(outpointidx);
// Look up in real caches.
Coin realcoin;
auto real = caches.back()->GetCoin(data.outpoints[outpointidx], realcoin);
auto realcoin = caches.back()->GetCoin(data.outpoints[outpointidx]);
// Compare results.
if (!sim.has_value()) {
assert(!real || realcoin.IsSpent());
assert(!realcoin || realcoin->IsSpent());
} else {
assert(real && !realcoin.IsSpent());
assert(realcoin && !realcoin->IsSpent());
const auto& simcoin = data.coins[sim->first];
assert(realcoin.out == simcoin.out);
assert(realcoin.fCoinBase == simcoin.fCoinBase);
assert(realcoin.nHeight == sim->second);
assert(realcoin->out == simcoin.out);
assert(realcoin->fCoinBase == simcoin.fCoinBase);
assert(realcoin->nHeight == sim->second);
}
},

Expand Down Expand Up @@ -460,16 +455,15 @@ FUZZ_TARGET(coinscache_sim)

// Compare the bottom coinsview (not a CCoinsViewCache) with sim_cache[0].
for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
Coin realcoin;
auto real = bottom.GetCoin(data.outpoints[outpointidx], realcoin);
auto realcoin = bottom.GetCoin(data.outpoints[outpointidx]);
auto sim = lookup(outpointidx, 0);
if (!sim.has_value()) {
assert(!real || realcoin.IsSpent());
assert(!realcoin || realcoin->IsSpent());
} else {
assert(real && !realcoin.IsSpent());
assert(realcoin.out == data.coins[sim->first].out);
assert(realcoin.fCoinBase == data.coins[sim->first].fCoinBase);
assert(realcoin.nHeight == sim->second);
assert(realcoin && !realcoin->IsSpent());
assert(realcoin->out == data.coins[sim->first].out);
assert(realcoin->fCoinBase == data.coins[sim->first].fCoinBase);
assert(realcoin->nHeight == sim->second);
}
}
}
5 changes: 2 additions & 3 deletions src/test/fuzz/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,8 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
// Helper to query an amount
const CCoinsViewMemPool amount_view{WITH_LOCK(::cs_main, return &chainstate.CoinsTip()), tx_pool};
const auto GetAmount = [&](const COutPoint& outpoint) {
Coin c;
Assert(amount_view.GetCoin(outpoint, c));
return c.out.nValue;
auto coin{amount_view.GetCoin(outpoint).value()};
return coin.out.nValue;
};

LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
Expand Down
5 changes: 2 additions & 3 deletions src/test/util/setup_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,9 +432,8 @@ std::pair<CMutableTransaction, CAmount> TestChain100Setup::CreateValidTransactio
std::map<COutPoint, Coin> input_coins;
CAmount inputs_amount{0};
for (const auto& outpoint_to_spend : inputs) {
// - Use GetCoin to properly populate utxo_to_spend,
Coin utxo_to_spend;
assert(coins_cache.GetCoin(outpoint_to_spend, utxo_to_spend));
// Use GetCoin to properly populate utxo_to_spend
auto utxo_to_spend{coins_cache.GetCoin(outpoint_to_spend).value()};
input_coins.insert({outpoint_to_spend, utxo_to_spend});
inputs_amount += utxo_to_spend.out.nValue;
}
Expand Down
6 changes: 3 additions & 3 deletions src/txdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ void CCoinsViewDB::ResizeCache(size_t new_cache_size)
}
}

std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint, Coin& coin) const
std::optional<Coin> CCoinsViewDB::GetCoin(const COutPoint& outpoint) const
{
if (m_db->Read(CoinEntry(&outpoint), coin)) return coin;
else return std::nullopt;
if (Coin coin; m_db->Read(CoinEntry(&outpoint), coin)) return coin;
return std::nullopt;
}

bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
Expand Down
2 changes: 1 addition & 1 deletion src/txdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class CCoinsViewDB final : public CCoinsView
public:
explicit CCoinsViewDB(DBParams db_params, CoinsViewOptions options);

std::optional<Coin> GetCoin(const COutPoint& outpoint, Coin& coin) const override;
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
uint256 GetBestBlock() const override;
std::vector<uint256> GetHeadBlocks() const override;
Expand Down
12 changes: 5 additions & 7 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -988,13 +988,12 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const

CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }

std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint, Coin& coin) const
std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint) const
{
// Check to see if the inputs are made available by another tx in the package.
// These Coins would not be available in the underlying CoinsView.
if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) {
coin = it->second;
return coin;
return it->second;
}

// If an entry in the mempool exists, always return that one, as it's guaranteed to never
Expand All @@ -1003,14 +1002,13 @@ std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint, Coin&
CTransactionRef ptx = mempool.get(outpoint.hash);
if (ptx) {
if (outpoint.n < ptx->vout.size()) {
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);
Coin coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);
m_non_base_coins.emplace(outpoint);
return coin;
} else {
return std::nullopt;
}
return std::nullopt;
}
return base->GetCoin(outpoint, coin);
return base->GetCoin(outpoint);
}

void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx)
Expand Down
Loading

0 comments on commit 4feaa28

Please sign in to comment.