nit: Could go back to returning optional again:
std::optional<Spender> FindSpender(const COutPoint& txo) const;
<details>
<summary>Full diff</summary>
diff --git a/src/index/txospenderindex.cpp b/src/index/txospenderindex.cpp
index 10c104ab14..b120bb804c 100644
--- a/src/index/txospenderindex.cpp
+++ b/src/index/txospenderindex.cpp
@@ -145,26 +145,26 @@ bool TxoSpenderIndex::CustomRemove(const interfaces::BlockInfo& block)
return true;
}
-bool TxoSpenderIndex::ReadTransaction(const CDiskTxPos& tx_pos, CTransactionRef& tx, uint256& block_hash) const
+std::optional<TxoSpenderIndex::Spender> TxoSpenderIndex::ReadTransaction(const CDiskTxPos& tx_pos) const
{
AutoFile file{m_chainstate->m_blockman.OpenBlockFile(tx_pos, true)};
if (file.IsNull()) {
- return false;
+ return std::nullopt;
}
CBlockHeader header;
try {
file >> header;
file.seek(tx_pos.nTxOffset, SEEK_CUR);
+ CTransactionRef tx;
file >> TX_WITH_WITNESS(tx);
- block_hash = header.GetHash();
- return true;
+ return Spender{tx, header.GetHash()};
} catch (const std::exception& e) {
LogError("Deserialize or I/O error - %s\n", e.what());
- return false;
+ return std::nullopt;
}
}
-bool TxoSpenderIndex::FindSpender(const COutPoint& txo, CTransactionRef& tx, uint256& block_hash) const
+std::optional<TxoSpenderIndex::Spender> TxoSpenderIndex::FindSpender(const COutPoint& txo) const
{
uint64_t prefix = CreateKeyPrefix(m_siphash_key, txo);
std::unique_ptr<CDBIterator> it(m_db->NewIterator());
@@ -173,15 +173,15 @@ bool TxoSpenderIndex::FindSpender(const COutPoint& txo, CTransactionRef& tx, uin
// find all keys that start with the outpoint hash, load the transaction at the location specified in the key
// and return it if it does spend the provided outpoint
for (it->Seek(std::pair{DB_TXOSPENDERINDEX, prefix}); it->Valid() && it->GetKey(key) && key.hash == prefix; it->Next()) {
- if (ReadTransaction(key.pos, tx, block_hash)) {
- for (const auto& input : tx->vin) {
+ if (auto res{ReadTransaction(key.pos)}) {
+ for (const auto& input : res->tx->vin) {
if (input.prevout == txo) {
- return true;
+ return res;
}
}
}
}
- return false;
+ return std::nullopt;
}
BaseIndex::DB& TxoSpenderIndex::GetDB() const { return *m_db; }
diff --git a/src/index/txospenderindex.h b/src/index/txospenderindex.h
index 908774d9ef..9324d7cfc0 100644
--- a/src/index/txospenderindex.h
+++ b/src/index/txospenderindex.h
@@ -28,13 +28,19 @@ static constexpr bool DEFAULT_TXOSPENDERINDEX{false};
*/
class TxoSpenderIndex final : public BaseIndex
{
+public:
+ struct Spender {
+ CTransactionRef tx;
+ uint256 block_hash;
+ };
+
private:
std::unique_ptr<BaseIndex::DB> m_db;
std::pair<uint64_t, uint64_t> m_siphash_key;
bool AllowPrune() const override { return false; }
void WriteSpenderInfos(const std::vector<std::pair<COutPoint, CDiskTxPos>>& items);
void EraseSpenderInfos(const std::vector<std::pair<COutPoint, CDiskTxPos>>& items);
- bool ReadTransaction(const CDiskTxPos& pos, CTransactionRef& tx, uint256& block_hash) const;
+ std::optional<Spender> ReadTransaction(const CDiskTxPos& pos) const;
protected:
interfaces::Chain::NotifyOptions CustomOptions() override;
@@ -51,7 +57,7 @@ public:
// Destroys unique_ptr to an incomplete type.
virtual ~TxoSpenderIndex() override;
- bool FindSpender(const COutPoint& txo, CTransactionRef& tx, uint256& block_hash) const;
+ std::optional<Spender> FindSpender(const COutPoint& txo) const;
};
/// The global txo spender index. May be null.
diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp
index 04eac58ffa..a26ae2b791 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -709,13 +709,11 @@ static RPCHelpMan gettxspendingprevout()
// do nothing, caller has selected to only query the mempool
} else if (g_txospenderindex) {
// no spending tx in mempool, query txospender index
- CTransactionRef spending_tx;
- uint256 block_hash;
- if (g_txospenderindex->FindSpender(prevout, spending_tx, block_hash)) {
- o.pushKV("spendingtxid", spending_tx->GetHash().GetHex());
- o.pushKV("blockhash", block_hash.GetHex());
+ if (auto spender{g_txospenderindex->FindSpender(prevout)}) {
+ o.pushKV("spendingtxid", spender->tx->GetHash().GetHex());
+ o.pushKV("blockhash", spender->block_hash.GetHex());
if (return_spending_tx.value_or(false)) {
- o.pushKV("spendingtx", EncodeHexTx(*spending_tx));
+ o.pushKV("spendingtx", EncodeHexTx(*spender->tx));
}
if (!txospenderindex_ready) {
// warn if index is not ready as the spending tx that we found may be stale (it may be reorged out)
diff --git a/src/test/txospenderindex_tests.cpp b/src/test/txospenderindex_tests.cpp
index 870262ac4d..5f930aceb8 100644
--- a/src/test/txospenderindex_tests.cpp
+++ b/src/test/txospenderindex_tests.cpp
@@ -45,12 +45,9 @@ BOOST_FIXTURE_TEST_CASE(txospenderindex_initial_sync, TestChain100Setup)
CBlock block = CreateAndProcessBlock(spender, this->m_coinbase_txns[0]->vout[0].scriptPubKey);
- CTransactionRef tx;
- uint256 block_hash;
-
// Transaction should not be found in the index before it is started.
for (const auto& outpoint : spent) {
- BOOST_CHECK(!txospenderindex.FindSpender(outpoint, tx, block_hash));
+ BOOST_CHECK(!txospenderindex.FindSpender(outpoint));
}
// BlockUntilSyncedToCurrentChain should return false before txospenderindex is started.
@@ -58,9 +55,10 @@ BOOST_FIXTURE_TEST_CASE(txospenderindex_initial_sync, TestChain100Setup)
txospenderindex.Sync();
for (size_t i = 0; i < spent.size(); i++) {
- BOOST_CHECK(txospenderindex.FindSpender(spent[i], tx, block_hash));
- BOOST_CHECK_EQUAL(tx->GetHash(), spender[i].GetHash());
- BOOST_CHECK_EQUAL(block_hash, block.GetHash());
+ auto spend{txospenderindex.FindSpender(spent[i])};
+ BOOST_CHECK(spend.has_value());
+ BOOST_CHECK_EQUAL(spend->tx->GetHash(), spender[i].GetHash());
+ BOOST_CHECK_EQUAL(spend->block_hash, block.GetHash());
}
// It is not safe to stop and destroy the index until it finishes handling
</details>