Skip to content

Commit fbe63b5

Browse files
authored
Merge pull request #703 from evoskuil/master
Adapt to database rename of get_[x]_outpoints(), electrum subscribe wip.
2 parents 9eabbab + d035e95 commit fbe63b5

13 files changed

Lines changed: 273 additions & 155 deletions

Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ src_libbitcoin_server_la_SOURCES = \
5757
src/protocols/electrum/protocol_electrum_mempool.cpp \
5858
src/protocols/electrum/protocol_electrum_outputs.cpp \
5959
src/protocols/electrum/protocol_electrum_scripthash.cpp \
60-
src/protocols/electrum/protocol_electrum_scripts.cpp \
60+
src/protocols/electrum/protocol_electrum_scriptpubkey.cpp \
6161
src/protocols/electrum/protocol_electrum_server.cpp \
6262
src/protocols/electrum/protocol_electrum_transactions.cpp \
6363
src/protocols/electrum/protocol_electrum_version.cpp \
@@ -105,7 +105,7 @@ test_libbitcoin_server_test_SOURCES = \
105105
test/protocols/electrum/electrum_mempool.cpp \
106106
test/protocols/electrum/electrum_outputs.cpp \
107107
test/protocols/electrum/electrum_scripthash.cpp \
108-
test/protocols/electrum/electrum_scripts.cpp \
108+
test/protocols/electrum/electrum_scriptpubkey.cpp \
109109
test/protocols/electrum/electrum_server.cpp \
110110
test/protocols/electrum/electrum_transactions.cpp \
111111
test/protocols/electrum/electrum_version.cpp \

builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@
146146
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_mempool.cpp" />
147147
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_outputs.cpp" />
148148
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scripthash.cpp" />
149-
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scripts.cpp" />
149+
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scriptpubkey.cpp" />
150150
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_server.cpp" />
151151
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_transactions.cpp" />
152152
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_version.cpp">

builds/msvc/vs2022/libbitcoin-server-test/libbitcoin-server-test.vcxproj.filters

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scripthash.cpp">
8888
<Filter>src\protocols\electrum</Filter>
8989
</ClCompile>
90-
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scripts.cpp">
90+
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_scriptpubkey.cpp">
9191
<Filter>src\protocols\electrum</Filter>
9292
</ClCompile>
9393
<ClCompile Include="..\..\..\..\test\protocols\electrum\electrum_server.cpp">

builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@
139139
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_mempool.cpp" />
140140
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_outputs.cpp" />
141141
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scripthash.cpp" />
142-
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scripts.cpp" />
142+
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scriptpubkey.cpp" />
143143
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_server.cpp" />
144144
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_transactions.cpp" />
145145
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_version.cpp" />

builds/msvc/vs2022/libbitcoin-server/libbitcoin-server.vcxproj.filters

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scripthash.cpp">
118118
<Filter>src\protocols\electrum</Filter>
119119
</ClCompile>
120-
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scripts.cpp">
120+
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_scriptpubkey.cpp">
121121
<Filter>src\protocols\electrum</Filter>
122122
</ClCompile>
123123
<ClCompile Include="..\..\..\..\src\protocols\electrum\protocol_electrum_server.cpp">

include/bitcoin/server/protocols/protocol_electrum.hpp

Lines changed: 45 additions & 21 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,13 +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 get_status implementation.
283-
bool send_get_status(const std::string& tx_hash, double txout_idx,
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,
284308
const std::string& spk_hint) NOEXCEPT;
309+
bool get_outpoint_status(object_t& status,
310+
const system::chain::point& prevout) const NOEXCEPT;
285311

286312
// These are thread safe.
287313
const options_t& options_;
@@ -292,15 +318,13 @@ class BCS_API protocol_electrum
292318
std::atomic_bool subscribed_height_{};
293319
std::atomic_bool subscribed_header_{};
294320
std::atomic_bool subscribed_outpoint_{};
295-
std::atomic_bool subscribed_address_{};
296321
std::atomic_bool subscribed_scripthash_{};
297-
std::atomic_bool subscribed_scriptpubkey_{};
298322

299323
// This is mostly thread safe, and used in a thread safe manner.
300324
const channel_t::ptr channel_;
301325

302326
// This is protected by strand.
303-
std::unordered_set<system::hash_digest> subscriptions_{};
327+
std::unordered_set<hash_digest> subscriptions_{};
304328
};
305329

306330
} // 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()

src/protocols/electrum/protocol_electrum_addresses.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ void protocol_electrum::handle_blockchain_address_list_unspent(const code& ec,
100100

101101
void protocol_electrum::handle_blockchain_address_subscribe(const code& ec,
102102
rpc_interface::blockchain_address_subscribe,
103-
const std::string& ) NOEXCEPT
103+
const std::string& address) NOEXCEPT
104104
{
105105
if (stopped(ec))
106106
return;
@@ -111,11 +111,14 @@ void protocol_electrum::handle_blockchain_address_subscribe(const code& ec,
111111
return;
112112
}
113113

114-
// TODO: collect the address into a limited notification set.
115-
subscribed_address_.store(true, std::memory_order_relaxed);
114+
const auto hash = extract_scripthash(address);
115+
if (hash == null_hash)
116+
{
117+
send_code(error::invalid_argument);
118+
return;
119+
}
116120

117-
// TODO: compute the status hash in a store query (no mempool).
118-
send_result(array_t{ string_t{ "status-hash" } }, 16, BIND(complete, _1));
121+
send_scripthash_subscribe(hash);
119122
}
120123

121124
// utilities

0 commit comments

Comments
 (0)