mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-16 16:32:47 +00:00
rpc: Refactor gettxspendingprevout to be easier to parse
Co-authored-by: Matias Furszyfer <matiasfurszyfer@protonmail.com>
This commit is contained in:
parent
ca45461ddb
commit
d236415649
@ -950,14 +950,13 @@ static RPCHelpMan gettxspendingprevout()
|
||||
const bool mempool_only{options.exists("mempool_only") ? options["mempool_only"].get_bool() : !g_txospenderindex};
|
||||
const bool return_spending_tx{options.exists("return_spending_tx") ? options["return_spending_tx"].get_bool() : false};
|
||||
|
||||
// Worklist of outpoints to resolve
|
||||
struct Entry {
|
||||
const COutPoint prevout;
|
||||
const UniValue& input;
|
||||
UniValue output;
|
||||
COutPoint outpoint;
|
||||
const UniValue* raw;
|
||||
};
|
||||
std::vector<Entry> prevouts;
|
||||
prevouts.reserve(output_params.size());
|
||||
|
||||
std::vector<Entry> prevouts_to_process;
|
||||
prevouts_to_process.reserve(output_params.size());
|
||||
for (unsigned int idx = 0; idx < output_params.size(); idx++) {
|
||||
const UniValue& o = output_params[idx].get_obj();
|
||||
|
||||
@ -972,57 +971,68 @@ static RPCHelpMan gettxspendingprevout()
|
||||
if (nOutput < 0) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
|
||||
}
|
||||
prevouts.emplace_back(COutPoint{txid, uint32_t(nOutput)}, o, UniValue{});
|
||||
prevouts_to_process.emplace_back(COutPoint{txid, static_cast<uint32_t>(nOutput)}, &o);
|
||||
}
|
||||
|
||||
// search the mempool first
|
||||
bool missing_from_mempool{false};
|
||||
auto make_output = [return_spending_tx](const Entry& prevout, const CTransaction* spending_tx = nullptr) {
|
||||
UniValue o{*prevout.raw};
|
||||
if (spending_tx) {
|
||||
o.pushKV("spendingtxid", spending_tx->GetHash().ToString());
|
||||
if (return_spending_tx) {
|
||||
o.pushKV("spendingtx", EncodeHexTx(*spending_tx));
|
||||
}
|
||||
}
|
||||
return o;
|
||||
};
|
||||
|
||||
UniValue result{UniValue::VARR};
|
||||
|
||||
// Search the mempool first
|
||||
{
|
||||
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
|
||||
LOCK(mempool.cs);
|
||||
for (auto& entry : prevouts) {
|
||||
const CTransaction* spendingTx = mempool.GetConflictTx(entry.prevout);
|
||||
if (spendingTx != nullptr) {
|
||||
UniValue o{entry.input};
|
||||
o.pushKV("spendingtxid", spendingTx->GetHash().ToString());
|
||||
if (return_spending_tx) {
|
||||
o.pushKV("spendingtx", EncodeHexTx(*spendingTx));
|
||||
}
|
||||
entry.output = std::move(o);
|
||||
} else {
|
||||
missing_from_mempool = true;
|
||||
|
||||
// Make the result if the spending tx appears in the mempool or this is a mempool_only request
|
||||
for (auto it = prevouts_to_process.begin(); it != prevouts_to_process.end(); ) {
|
||||
const CTransaction* spending_tx{mempool.GetConflictTx(it->outpoint)};
|
||||
|
||||
// If the outpoint is not spent in the mempool and this is not a mempool-only
|
||||
// request, we cannot answer it yet.
|
||||
if (!spending_tx && !mempool_only) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push_back(make_output(*it, spending_tx));
|
||||
it = prevouts_to_process.erase(it);
|
||||
}
|
||||
}
|
||||
// if search is not limited to the mempool and no spender was found for an outpoint, search the txospenderindex
|
||||
// we call g_txospenderindex->BlockUntilSyncedToCurrentChain() only if g_txospenderindex is going to be used
|
||||
UniValue result{UniValue::VARR};
|
||||
bool txospenderindex_ready{mempool_only || !missing_from_mempool || (g_txospenderindex && g_txospenderindex->BlockUntilSyncedToCurrentChain())};
|
||||
for (auto& entry : prevouts) {
|
||||
if (!entry.output.isNull()) {
|
||||
result.push_back(std::move(entry.output));
|
||||
continue;
|
||||
|
||||
// Return early if all requests have been handled by the mempool search
|
||||
if (prevouts_to_process.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// At this point the request was not limited to the mempool and some outpoints remain
|
||||
// unresolved. We now rely on the index to determine whether they were spent or not.
|
||||
if (!g_txospenderindex || !g_txospenderindex->BlockUntilSyncedToCurrentChain()) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "Mempool lacks a relevant spend, and txospenderindex is unavailable.");
|
||||
}
|
||||
|
||||
for (const auto& prevout : prevouts_to_process) {
|
||||
const auto spender{g_txospenderindex->FindSpender(prevout.outpoint)};
|
||||
if (!spender) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, spender.error());
|
||||
}
|
||||
UniValue o{entry.input};
|
||||
if (mempool_only) {
|
||||
// do nothing, caller has selected to only query the mempool
|
||||
} else if (!txospenderindex_ready) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, strprintf("No spending tx for the outpoint %s:%d in mempool, and txospenderindex is unavailable.", entry.prevout.hash.GetHex(), entry.prevout.n));
|
||||
|
||||
if (const auto& spender_opt{spender.value()}) {
|
||||
UniValue o{make_output(prevout, spender_opt->tx.get())};
|
||||
o.pushKV("blockhash", spender_opt->block_hash.GetHex());
|
||||
result.push_back(std::move(o));
|
||||
} else {
|
||||
// no spending tx in mempool, query txospender index
|
||||
const auto spender{g_txospenderindex->FindSpender(entry.prevout)};
|
||||
if (!spender) {
|
||||
throw JSONRPCError(RPC_MISC_ERROR, spender.error());
|
||||
}
|
||||
if (spender.value()) {
|
||||
o.pushKV("spendingtxid", spender.value()->tx->GetHash().GetHex());
|
||||
o.pushKV("blockhash", spender.value()->block_hash.GetHex());
|
||||
if (return_spending_tx) {
|
||||
o.pushKV("spendingtx", EncodeHexTx(*spender.value()->tx));
|
||||
}
|
||||
}
|
||||
// Only return the input outpoint itself, which indicates it is unspent.
|
||||
result.push_back(make_output(prevout));
|
||||
}
|
||||
result.push_back(std::move(o));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user