Skip to content

Commit d035e95

Browse files
committed
WIP refactor and partial impl for electrum subscribers.
1 parent 29ac4bf commit d035e95

4 files changed

Lines changed: 207 additions & 124 deletions

File tree

include/bitcoin/server/protocols/protocol_electrum.hpp

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -204,22 +204,29 @@ class BCS_API protocol_electrum
204204
rpc_interface::mempool_get_info) NOEXCEPT;
205205

206206
protected:
207+
using hash_digest = system::hash_digest;
208+
enum class notify_t { address, scripthash, scriptpubkey };
209+
typedef std::function<void(const code&, const hash_digest&,
210+
const hash_digest&)> status_handler;
211+
207212
/// Common implementation for block_header/s.
208213
void blockchain_block_headers(size_t starting, size_t quantity,
209214
size_t waypoint, bool multiplicity) NOEXCEPT;
210215

211216
/// Completion handlers (for long-running address queries).
212217
/// -----------------------------------------------------------------------
213218

214-
void get_balance(const system::hash_digest& hash) NOEXCEPT;
215-
void get_history(const system::hash_digest& hash) NOEXCEPT;
216-
void get_mempool(const system::hash_digest& hash) NOEXCEPT;
217-
void list_unspent(const system::hash_digest& hash) NOEXCEPT;
219+
void get_balance(const hash_digest& hash) NOEXCEPT;
220+
void get_history(const hash_digest& hash) NOEXCEPT;
221+
void get_mempool(const hash_digest& hash) NOEXCEPT;
222+
void list_unspent(const hash_digest& hash) NOEXCEPT;
218223

219-
void do_get_balance(const system::hash_digest& hash) NOEXCEPT;
220-
void do_get_history(const system::hash_digest& hash) NOEXCEPT;
221-
void do_get_mempool(const system::hash_digest& hash) NOEXCEPT;
222-
void do_list_unspent(const system::hash_digest& hash) NOEXCEPT;
224+
void do_get_balance(const hash_digest& hash) NOEXCEPT;
225+
void do_get_history(const hash_digest& hash) NOEXCEPT;
226+
void do_get_mempool(const hash_digest& hash) NOEXCEPT;
227+
void do_list_unspent(const hash_digest& hash) NOEXCEPT;
228+
void do_status(const hash_digest& hash,
229+
const status_handler& sender) NOEXCEPT;
223230

224231
void complete_get_balance(const code& ec, uint64_t confirmed,
225232
int64_t unconfirmed) NOEXCEPT;
@@ -229,16 +236,22 @@ class BCS_API protocol_electrum
229236
const database::histories& histories) NOEXCEPT;
230237
void complete_list_unspent(const code& ec,
231238
const database::unspents& unspents) NOEXCEPT;
239+
void complete_status(const code& ec, const hash_digest& hash,
240+
const hash_digest& status, const status_handler& sender) NOEXCEPT;
241+
242+
void send_status(const code& ec, const hash_digest& hash,
243+
const hash_digest& status) NOEXCEPT;
244+
void notify_status(const code& ec, const hash_digest& hash,
245+
const hash_digest& status, notify_t type,
246+
node::address_t link) NOEXCEPT;
232247

233248
/// Notification senders and send handlers.
234249
/// -----------------------------------------------------------------------
235250

236251
void do_height(node::header_t link) NOEXCEPT;
237252
void do_header(node::header_t link) NOEXCEPT;
238253
void do_outpoint(node::header_t link) NOEXCEPT;
239-
void do_address(node::address_t link) NOEXCEPT;
240254
void do_scripthash(node::address_t link) NOEXCEPT;
241-
void do_scriptpubkey(node::address_t link) NOEXCEPT;
242255

243256
/// Utilities.
244257
/// -----------------------------------------------------------------------
@@ -254,19 +267,32 @@ class BCS_API protocol_electrum
254267
}
255268

256269
private:
270+
// Status hash optimization (~200 bytes).
271+
struct midstate
272+
{
273+
hash_digest status{};
274+
system::stream::out::fast stream{ status };
275+
system::hash::sha256::fast writer{ stream };
276+
};
277+
278+
// Aliases.
257279
using array_t = network::rpc::array_t;
258280
using object_t = network::rpc::object_t;
259281
using version_t = protocol_electrum_version;
260282
static constexpr electrum::version minimum = version_t::minimum;
261283
static constexpr electrum::version maximum = version_t::maximum;
262284

285+
// Address transformations.
286+
static array_t transform(const database::histories& histories) NOEXCEPT;
287+
static array_t transform(const database::unspents& unspents) NOEXCEPT;
288+
static std::string to_method_name(notify_t type) NOEXCEPT;
289+
263290
// Compute server.features.hosts value from config.
264291
object_t self_hosts() const NOEXCEPT;
265292
array_t more_hosts() const NOEXCEPT;
266293

267294
// Convert between legacy bitcoin payment address and scripthash.
268-
system::hash_digest extract_scripthash(
269-
const std::string& address) const NOEXCEPT;
295+
hash_digest extract_scripthash(const std::string& address) const NOEXCEPT;
270296
system::wallet::payment_address extract_address(
271297
const system::chain::script& script) const NOEXCEPT;
272298

@@ -275,15 +301,13 @@ class BCS_API protocol_electrum
275301
code validate_tx(const system::chain::transaction& tx) const NOEXCEPT;
276302
code broadcast_tx(const system::chain::transaction::cptr& tx) NOEXCEPT;
277303

278-
// Address transformations.
279-
array_t transform(const database::histories& histories) NOEXCEPT;
280-
array_t transform(const database::unspents& unspents) NOEXCEPT;
281-
282-
// Shared implementations.
283-
void send_scripthash_unsubscribe(const system::hash_digest& hash) NOEXCEPT;
284-
void send_scripthash_subscribe(const system::hash_digest& hash) NOEXCEPT;
285-
bool send_get_status(const system::chain::point& prevout,
304+
// Shared send/get implementations.
305+
void send_scripthash_unsubscribe(const hash_digest& hash) NOEXCEPT;
306+
void send_scripthash_subscribe(const hash_digest& hash) NOEXCEPT;
307+
bool send_outpoint_status(const system::chain::point& prevout,
286308
const std::string& spk_hint) NOEXCEPT;
309+
bool get_outpoint_status(object_t& status,
310+
const system::chain::point& prevout) const NOEXCEPT;
287311

288312
// These are thread safe.
289313
const options_t& options_;
@@ -294,15 +318,13 @@ class BCS_API protocol_electrum
294318
std::atomic_bool subscribed_height_{};
295319
std::atomic_bool subscribed_header_{};
296320
std::atomic_bool subscribed_outpoint_{};
297-
std::atomic_bool subscribed_address_{};
298321
std::atomic_bool subscribed_scripthash_{};
299-
std::atomic_bool subscribed_scriptpubkey_{};
300322

301323
// This is mostly thread safe, and used in a thread safe manner.
302324
const channel_t::ptr channel_;
303325

304326
// This is protected by strand.
305-
std::unordered_set<system::hash_digest> subscriptions_{};
327+
std::unordered_set<hash_digest> subscriptions_{};
306328
};
307329

308330
} // namespace server

src/protocols/electrum/protocol_electrum.cpp

Lines changed: 58 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -165,23 +165,14 @@ bool protocol_electrum::handle_event(const code&, node::chase event_,
165165
POST(do_outpoint, std::get<node::address_t>(value));
166166
}
167167

168-
if (subscribed_address_.load(relaxed))
169-
{
170-
BC_ASSERT(std::holds_alternative<node::address_t>(value));
171-
POST(do_address, std::get<node::address_t>(value));
172-
}
173-
174168
if (subscribed_scripthash_.load(relaxed))
175169
{
170+
BC_ASSERT(archive().address_enabled());
176171
BC_ASSERT(std::holds_alternative<node::address_t>(value));
177172
POST(do_scripthash, std::get<node::address_t>(value));
178173
}
179174

180-
if (subscribed_scriptpubkey_.load(relaxed))
181-
{
182-
BC_ASSERT(std::holds_alternative<node::address_t>(value));
183-
POST(do_scriptpubkey, std::get<node::address_t>(value));
184-
}
175+
break;
185176
}
186177
default:
187178
{
@@ -206,7 +197,7 @@ void protocol_electrum::do_height(node::header_t link) NOEXCEPT
206197

207198
if (height.is_terminal())
208199
{
209-
LOGF("Electrum::do_height, object not found (" << link << ").");
200+
LOGF("Electrum::do_height, height not found (" << link << ").");
210201
return;
211202
}
212203

@@ -227,7 +218,7 @@ void protocol_electrum::do_header(node::header_t link) NOEXCEPT
227218

228219
if (height.is_terminal())
229220
{
230-
LOGF("Electrum::do_header, object not found (" << link << ").");
221+
LOGF("Electrum::do_header, header not found (" << link << ").");
231222
return;
232223
}
233224

@@ -239,77 +230,79 @@ void protocol_electrum::do_header(node::header_t link) NOEXCEPT
239230
}
240231

241232
// Notifier for blockchain_outpoint_subscribe events.
242-
void protocol_electrum::do_outpoint(node::address_t) NOEXCEPT
233+
void protocol_electrum::do_outpoint(node::address_t link) NOEXCEPT
243234
{
244-
chain::point point{};
235+
BC_ASSERT(stranded());
236+
237+
// TODO: get prevout from event.
238+
///////////////////////////////////////////////////////////////////////////
239+
chain::point prevout{};
240+
///////////////////////////////////////////////////////////////////////////
241+
242+
object_t status{};
243+
if (!get_outpoint_status(status, prevout))
244+
{
245+
LOGF("Electrum::do_outpoint, outpoint not found (" << link << ").");
246+
return;
247+
}
245248

246-
// TODO: compute and return outpoint status from a store query.
247-
// TODO: unlike scripthash, this is not a cumulative "status" hash.
248249
send_notification("blockchain.outpoint.subscribe", array_t
249250
{
250-
array_t{ encode_hash(point.hash()), point.index() },
251-
object_t{}
251+
array_t{ encode_hash(prevout.hash()), prevout.index() },
252+
std::move(status)
252253
}, 128, BIND(handle_send, _1));
253254
}
254255

255-
// This struct is small and stack allocated (208 bytes).
256-
// Writer holds stream ref to status and midstate via accumulator.
257-
// Writer flush rewrites status via stream and resets accumulator.
258-
// Pass writer to store method to accumulate confirmed address midstate.
259-
// Once initialized, copy writer after adding state and flushing for status.
260-
struct midstate
261-
{
262-
hash_digest status{};
263-
stream::out::fast stream{ status };
264-
hash::sha256::fast writer{ stream };
265-
};
266-
267-
// Notifier for blockchain_address_subscribe events.
268-
void protocol_electrum::do_address(node::address_t ) NOEXCEPT
256+
// Notifier for blockchain_scripthash_subscribe events.
257+
void protocol_electrum::do_scripthash(node::address_t link) NOEXCEPT
269258
{
270-
std::string status_hash{};
271-
std::string script_hash{};
259+
BC_ASSERT(stranded());
272260

273-
midstate value{};
274-
auto copy = value;
261+
// TODO: get hash/type from event.
262+
///////////////////////////////////////////////////////////////////////////
263+
hash_digest hash{};
264+
constexpr auto type = notify_t::scripthash;
265+
///////////////////////////////////////////////////////////////////////////
275266

276-
// EXAMPLE:
277-
// script_hash is a payment address for address.
278-
send_notification("blockchain.address.subscribe", array_t
279-
{
280-
script_hash,
281-
status_hash
282-
}, 128, BIND(handle_send, _1));
267+
// Address status is long-running, so cannot tie up strand.
268+
PARALLEL(do_status, hash, BIND(notify_status, _1, _2, _3, type, link));
283269
}
284270

285-
// Notifier for blockchain_scripthash_subscribe events.
286-
void protocol_electrum::do_scripthash(node::address_t) NOEXCEPT
271+
void protocol_electrum::notify_status(const code& ec, const hash_digest& hash,
272+
const hash_digest& status, notify_t type, node::address_t link) NOEXCEPT
287273
{
288-
std::string status_hash{};
289-
std::string script_hash{};
274+
BC_ASSERT(stranded());
290275

291-
// EXAMPLE:
292-
// script_hash is a payment address for address.
293-
send_notification("blockchain.scripthash.subscribe", array_t
276+
if (ec)
294277
{
295-
script_hash,
296-
status_hash
297-
}, 128, BIND(handle_send, _1));
278+
LOGF("Electrum::do_scripthash, address not found (" << link << ").");
279+
return;
280+
}
281+
282+
send_notification(to_method_name(type), array_t
283+
{
284+
encode_hash(hash),
285+
status == null_hash ? value_t{} : value_t{ encode_hash(status) }
286+
}, 128, BIND(complete, _1));
298287
}
299288

300-
// Notifier for blockchain_scriptpubkey_subscribe events.
301-
void protocol_electrum::do_scriptpubkey(node::address_t) NOEXCEPT
302-
{
303-
std::string status_hash{};
304-
std::string script_hash{};
289+
// utilities
290+
// ----------------------------------------------------------------------------
291+
// private/static
305292

306-
// EXAMPLE:
307-
// script_hash is a payment address for address.
308-
send_notification("blockchain.scriptpubkey.subscribe", array_t
293+
// Convert enumeration to json-rpc notification method name.
294+
std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT
295+
{
296+
switch (type)
309297
{
310-
script_hash,
311-
status_hash
312-
}, 128, BIND(handle_send, _1));
298+
case notify_t::address:
299+
return "blockchain.address.subscribe";
300+
case notify_t::scripthash:
301+
return "blockchain.scripthash.subscribe";
302+
default:
303+
case notify_t::scriptpubkey:
304+
return "blockchain.scriptpubkey.subscribe";
305+
}
313306
}
314307

315308
BC_POP_WARNING()

0 commit comments

Comments
 (0)