interfaces: Change getUnspentOutput return type to avoid multiprocess segfault

Coin serialize method segfaults if IsSpent condition is true. This caused
multiprocess code to segfault when serializing the Coin& output argument to of
the Node::getUnspentOutput method if the coin was not found. Segfault could be
triggered by double clicking and viewing transaction details in the GUI
transaction list.

Fix this by replacing Coin& output argument with optional<Coin> return value to
avoid trying to serializing spent coins.
This commit is contained in:
Ryan Ofsky 2022-09-12 11:09:06 -04:00
parent 4978754c00
commit 156f49d682
3 changed files with 8 additions and 8 deletions

View file

@ -204,8 +204,8 @@ public:
//! Unset RPC timer interface. //! Unset RPC timer interface.
virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0;
//! Get unspent outputs associated with a transaction. //! Get unspent output associated with a transaction.
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0; virtual std::optional<Coin> getUnspentOutput(const COutPoint& output) = 0;
//! Broadcast transaction. //! Broadcast transaction.
virtual TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) = 0; virtual TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) = 0;

View file

@ -328,10 +328,12 @@ public:
std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); } std::vector<std::string> listRpcCommands() override { return ::tableRPC.listCommands(); }
void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); }
void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); }
bool getUnspentOutput(const COutPoint& output, Coin& coin) override std::optional<Coin> getUnspentOutput(const COutPoint& output) override
{ {
LOCK(::cs_main); LOCK(::cs_main);
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin); Coin coin;
if (chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin)) return coin;
return {};
} }
TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) override
{ {

View file

@ -360,12 +360,10 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall
{ {
COutPoint prevout = txin.prevout; COutPoint prevout = txin.prevout;
Coin prev; if (auto prev{node.getUnspentOutput(prevout)}) {
if(node.getUnspentOutput(prevout, prev))
{
{ {
strHTML += "<li>"; strHTML += "<li>";
const CTxOut &vout = prev.out; const CTxOut& vout = prev->out;
CTxDestination address; CTxDestination address;
if (ExtractDestination(vout.scriptPubKey, address)) if (ExtractDestination(vout.scriptPubKey, address))
{ {