1818 */
1919#include < bitcoin/server/protocols/protocol_electrum.hpp>
2020
21+ #include < ranges>
2122#include < utility>
2223#include < bitcoin/server/define.hpp>
2324
@@ -33,6 +34,8 @@ using namespace std::placeholders;
3334constexpr auto relaxed = std::memory_order_relaxed;
3435
3536BC_PUSH_WARNING (NO_THROW_IN_NOEXCEPT)
37+ BC_PUSH_WARNING (SMART_PTR_NOT_NEEDED)
38+ BC_PUSH_WARNING (NO_VALUE_OR_CONST_REF_SHARED_PTR)
3639
3740void protocol_electrum::handle_blockchain_utxo_get_address (const code& ec,
3841 rpc_interface::blockchain_utxo_get_address, const std::string& tx_hash,
@@ -233,18 +236,22 @@ void protocol_electrum::do_outpoint(node::header_t) NOEXCEPT
233236
234237 for (const auto & prevout: outpoint_subscriptions_)
235238 {
236- if (stopping_)
237- return ;
238-
239- auto status = std::make_unique<object_t >();
240- if (!get_outpoint_status (*status, prevout))
239+ if (stopping_) return ;
240+ std::vector<object_t > statuses{};
241+ if (!get_outpoint_statuses (statuses, prevout))
241242 {
242243 LOGV (" Electrum::do_outpoint, outpoint not found." );
243244 }
244245 else
245246 {
246- // Asio-buffered message (small, not under caller control).
247- POST (outpoint_notify, std::move (status), prevout);
247+ // There can be more than one spender for a given output, so
248+ // instead of picking one arbitrarily, send all independently.
249+ for (auto & status: statuses)
250+ {
251+ // Asio-buffered message (small, not under caller control).
252+ auto ptr = std::make_unique<object_t >(std::move (status));
253+ POST (outpoint_notify, std::move (ptr), prevout);
254+ }
248255 }
249256 }
250257}
@@ -263,22 +270,60 @@ void protocol_electrum::outpoint_notify(const std::unique_ptr<object_t>& status,
263270
264271// utility
265272// ----------------------------------------------------------------------------
273+ // private
266274
267- bool protocol_electrum::get_outpoint_status ( object_t & status ,
275+ bool protocol_electrum::get_outpoint_statuses (std::vector< object_t >& out ,
268276 const point& prevout) const NOEXCEPT
269277{
270- // May be on either network or notification strand (thread safe).
278+ BC_ASSERT (notification_strand_. running_in_this_thread ());
271279
272280 const auto & query = archive ();
273- const auto out = query.get_tx_history (prevout.hash ());
274- if (!out .tx .is_valid ())
281+ const auto history = query.get_tx_history (prevout.hash ());
282+ if (!history .tx .is_valid ())
275283 return false ;
276284
277- status = { { " height" , to_unsigned (out.tx .height ()) } };
285+ out.clear ();
286+ const auto height = to_unsigned (history.tx .height ());
287+ const auto ins = query.get_spenders_history (prevout);
288+
289+ // No spenders, just return unspent singleton.
290+ if (ins.empty ())
291+ {
292+ out.push_back ({ { " height" , height } });
293+ return true ;
294+ }
295+
296+ // One or more spenders, return all.
297+ out.resize (ins.size ());
298+ std::ranges::transform (ins, out.begin (), [height](const auto & in) NOEXCEPT
299+ {
300+ return object_t
301+ {
302+ { " height" , height },
303+ { " spender_txhash" , encode_hash (in.tx .hash ()) },
304+ { " spender_height" , to_unsigned (in.tx .height ()) }
305+ };
306+ });
307+
308+ return true ;
309+ }
310+
311+ bool protocol_electrum::get_outpoint_status (object_t & out,
312+ const point& prevout) const NOEXCEPT
313+ {
314+ BC_ASSERT (stranded ());
315+
316+ const auto & query = archive ();
317+ const auto history = query.get_tx_history (prevout.hash ());
318+ if (!history.tx .is_valid ())
319+ return false ;
320+
321+ // Zero or more spenders, return the first if multiple.
322+ out = { { " height" , to_unsigned (history.tx .height ()) } };
278323 if (const auto ins = query.get_spenders_history (prevout); !ins.empty ())
279324 {
280- status [" spender_txhash" ] = encode_hash (ins.front ().tx .hash ());
281- status [" spender_height" ] = to_unsigned (ins.front ().tx .height ());
325+ out [" spender_txhash" ] = encode_hash (ins.front ().tx .hash ());
326+ out [" spender_height" ] = to_unsigned (ins.front ().tx .height ());
282327 }
283328
284329 return true ;
@@ -319,6 +364,8 @@ bool protocol_electrum::send_outpoint_status(const point& prevout,
319364 return true ;
320365}
321366
367+ BC_POP_WARNING ()
368+ BC_POP_WARNING ()
322369BC_POP_WARNING ()
323370
324371} // namespace server
0 commit comments