Skip to content

Commit 32b7587

Browse files
committed
Send multiple notifications for multiple outpoint spenders.
1 parent 28c976e commit 32b7587

3 files changed

Lines changed: 71 additions & 35 deletions

File tree

include/bitcoin/server/protocols/protocol_electrum.hpp

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -247,49 +247,31 @@ class BCS_API protocol_electrum
247247
/// Address.
248248
/// -----------------------------------------------------------------------
249249

250-
// subscribe.
251250
void scripthash_subscribe(const hash_digest& hash,
252251
notify_t type) NOEXCEPT;
253252
void do_scripthash_subscribe(const hash_digest& hash,
254253
notify_t type) NOEXCEPT;
255254
void complete_scripthash_subscribe(const code& ec,
256255
hash_digest& status, const hash_digest& hash) NOEXCEPT;
257-
258-
// unsubscribe.
259256
void scripthash_unsubscribe(const hash_digest& hash) NOEXCEPT;
260257
void do_scripthash_unsubscribe(const hash_digest& hash) NOEXCEPT;
261258
void complete_scripthash_unsubscribe(bool found) NOEXCEPT;
262-
263-
// notify (do_scripthash()).
264259
void scripthash_notify(const hash_digest& status, const hash_digest& hash,
265260
notify_t type) NOEXCEPT;
266261

267262
/// Outpoint.
268263
/// -----------------------------------------------------------------------
269264

270-
// subscribe.
271265
void do_outpoint_subscribe(const system::chain::point& prevout,
272266
const std::string& hint) NOEXCEPT;
273267
void complete_outpoint_subscribe(const code& ec,
274268
const system::chain::point& prevout,
275269
const std::string& hint) NOEXCEPT;
276-
277-
// unsubscribe.
278270
void do_outpoint_unsubscribe(const system::chain::point& prevout) NOEXCEPT;
279271
void complete_outpoint_unsubscribe(bool found) NOEXCEPT;
280-
281-
// notify (do_outpoint()).
282272
void outpoint_notify(const std::unique_ptr<interface::object_t>& status,
283273
const system::chain::point& prevout) NOEXCEPT;
284274

285-
// utility.
286-
bool get_outpoint_status(interface::object_t& status,
287-
const system::chain::point& prevout) const NOEXCEPT;
288-
bool send_outpoint_status(const system::chain::point& prevout,
289-
const std::string& hint) NOEXCEPT;
290-
291-
// notify.
292-
293275
/// Utilities.
294276
/// -----------------------------------------------------------------------
295277

@@ -337,9 +319,15 @@ class BCS_API protocol_electrum
337319
static constexpr electrum::version minimum = version_t::minimum;
338320
static constexpr electrum::version maximum = version_t::maximum;
339321

340-
// Scripthash status.
322+
// Status utilities.
341323
code get_scripthash_status(hash_digest& out, subscription& sub,
342324
const hash_digest& hash) NOEXCEPT;
325+
bool get_outpoint_statuses(std::vector<interface::object_t>& out,
326+
const system::chain::point& prevout) const NOEXCEPT;
327+
bool get_outpoint_status(interface::object_t& out,
328+
const system::chain::point& prevout) const NOEXCEPT;
329+
bool send_outpoint_status(const system::chain::point& prevout,
330+
const std::string& hint) NOEXCEPT;
343331

344332
// Transformations.
345333
static array_t transform(const unspents& unspents) NOEXCEPT;

src/protocols/electrum/protocol_electrum_outputs.cpp

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
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;
3334
constexpr auto relaxed = std::memory_order_relaxed;
3435

3536
BC_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

3740
void 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()
322369
BC_POP_WARNING()
323370

324371
} // namespace server

src/protocols/electrum/protocol_electrum_scripthash_subscribe.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,9 @@ void protocol_electrum::do_regressed(node::header_t) NOEXCEPT
236236

237237
// utility
238238
// ----------------------------------------------------------------------------
239-
// private/static
239+
// private
240240

241+
// static
241242
// Convert enumeration to json-rpc notification method name.
242243
std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT
243244
{
@@ -253,6 +254,7 @@ std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT
253254
}
254255
}
255256

257+
// static
256258
// Height is zero (rooted) or max_size_t for unconfirmed history txs.
257259
// TODO: this can be implemented as electrum json serializer (see bitcoind).
258260
hash_digest protocol_electrum::to_status(const histories& histories) NOEXCEPT
@@ -273,7 +275,6 @@ hash_digest protocol_electrum::to_status(const histories& histories) NOEXCEPT
273275
return out.status;
274276
}
275277

276-
// non-static
277278
code protocol_electrum::get_scripthash_status(hash_digest& out,
278279
subscription& /* sub */, const hash_digest& hash) NOEXCEPT
279280
{

0 commit comments

Comments
 (0)