Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions include/bitcoin/server/protocols/protocol_electrum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,26 +210,21 @@ class BCS_API protocol_electrum
using history = database::history;
using unspents = database::unspents;
using histories = database::histories;
using cursor_t = database::address_link;
using midstate = system::accumulator<system::sha256>;
enum class notify_t { address, scripthash, scriptpubkey };

// Status hash optimization (~200 bytes).
struct midstate
{
hash_digest status{};
system::stream::out::fast stream{ status };
system::hash::sha256::fast writer{ stream };
};

// Subscription to address/scripthash/scruptpubkey.
struct address_subscription
struct address_subscription final
{
notify_t type{};
midstate state{};
database::address_link cursor{};
cursor_t cursor{};
hash_digest status{};
midstate accumulator{};
};

// Subscription to outpoint.
struct outpoint_subscription
struct outpoint_subscription final
{
database::history outpoint{};
database::histories spenders{};
Expand Down Expand Up @@ -336,7 +331,7 @@ class BCS_API protocol_electrum
// Transformations.
static array_t transform(const unspents& unspents) NOEXCEPT;
static array_t transform(const histories& histories) NOEXCEPT;
static void write_status(system::writer& writer,
static void write_status(midstate& accumulator,
const history& history) NOEXCEPT;
static bool is_valid_hint(const std::string& hint) NOEXCEPT;
static std::string to_method_name(notify_t type) NOEXCEPT;
Expand Down
6 changes: 3 additions & 3 deletions src/protocols/electrum/protocol_electrum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,9 @@ void protocol_electrum::do_reorganized(node::header_t) NOEXCEPT

for (auto& [key, sub]: address_subscriptions_)
{
// writer.flush resets hash accumulator, sub.type remains unchanged.
sub.state.writer.flush();
sub.state.status = {};
// flush resets hash accumulator, sub.type remains unchanged.
sub.accumulator.flush();
sub.status = {};
sub.cursor = {};
}
}
Expand Down
45 changes: 23 additions & 22 deletions src/protocols/electrum/protocol_electrum_subscribe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ void protocol_electrum::do_scripthash_subscribe(const hash_digest& hash,
code ec{ error::subscription_limit };
if (address_subscriptions_.size() < options_.maximum_subscriptions)
{
using mid = midstate;
const auto at = address_subscriptions_.try_emplace(hash, type, mid{});
const auto at = address_subscriptions_
.try_emplace(hash, address_subscription{ type });

// Initial subscription is limited by configured maximum history.
const auto limit = at.second ? options().maximum_history : max_size_t;

// Partially-cached result idempotent (new or redundant subscription).
ec = get_scripthash_history(at.first->second, at.first->first, limit);
status = at.first->second.state.status;
status = at.first->second.status;
subscribed_address_.store(true, relaxed);
}

Expand Down Expand Up @@ -204,16 +204,18 @@ void protocol_electrum::do_scripthash(node::header_t) NOEXCEPT
// Depth limit is never imposed once a subscription is accepted.
if (const auto ec = get_scripthash_history(sub, key))
{
if (ec == database::error::query_canceled ||
ec == error::not_found)
if (ec == database::error::query_canceled)
return;

if (ec == error::not_found)
continue;

LOGF("Electrum::do_scripthash, " << ec.message());
}
else
{
// Asio-buffered message (small, not under caller control).
POST(scripthash_notify, sub.state.status, key, sub.type);
POST(scripthash_notify, sub.status, key, sub.type);
}
}
}
Expand Down Expand Up @@ -252,13 +254,13 @@ std::string protocol_electrum::to_method_name(notify_t type) NOEXCEPT

// static
// Height is zero (rooted) or max_size_t for unconfirmed history txs.
void protocol_electrum::write_status(system::writer& writer,
void protocol_electrum::write_status(midstate& accumulator,
const history& history) NOEXCEPT
{
writer.write_string(encode_hash(history.tx.hash()));
writer.write_string(":");
writer.write_string(std::to_string(to_signed(history.tx.height())));
writer.write_string(":");
accumulator.write(encode_hash(history.tx.hash()));
accumulator.write(":");
accumulator.write(std::to_string(to_signed(history.tx.height())));
accumulator.write(":");
}

code protocol_electrum::get_scripthash_history(address_subscription& sub,
Expand All @@ -279,19 +281,18 @@ code protocol_electrum::get_scripthash_history(address_subscription& sub,
auto it = records.cbegin();
const auto cend = records.cend();
while (it != cend && it->confirmed())
write_status(sub.state.writer, *it++);
write_status(sub.accumulator, *it++);

BC_ASSERT(std::none_of(it, cend, [](const auto& at)
{ return at.confirmed(); }));

// Copy midstate accumulator and write unconfirmeds.
midstate copy = sub.state;
midstate copy = sub.accumulator;
while (it != cend)
write_status(copy.writer, *it++);
write_status(copy, *it++);

// Flush, cache and return status (always updated on initial).
copy.writer.flush();
sub.state.status = std::move(copy.status);
sub.status = copy.flush();
return error::success;
}
else
Expand All @@ -305,10 +306,10 @@ code protocol_electrum::get_scripthash_history(address_subscription& sub,
auto it = records.cbegin();
auto cend = records.cend();
while (it != cend && it->confirmed())
write_status(sub.state.writer, *it++);
write_status(sub.accumulator, *it++);

// Copy midstate accumulator for write of unconfirmeds.
midstate copy = sub.state;
midstate copy = sub.accumulator;
records.clear();

// Update scan queries all unconfirmed independently.
Expand All @@ -322,15 +323,15 @@ code protocol_electrum::get_scripthash_history(address_subscription& sub,

// Accumulate unconfirmed status in order.
while (it != cend)
write_status(copy.writer, *it++);
write_status(copy, *it++);

// Flush, cache and return not found if no writes.
copy.writer.flush();
if (sub.state.status == copy.status)
auto status = copy.flush();
if (sub.status == status)
return error::not_found;

// Set cache into midstate object for next run.
sub.state.status = std::move(copy.status);
sub.status = std::move(status);
return error::success;
}
}
Expand Down
18 changes: 17 additions & 1 deletion test/protocols/electrum/electrum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ electrum_setup_fixture::~electrum_setup_fixture()
BOOST_WARN_MESSAGE(test::clear(test::directory), "electrum cleanup");
}

int64_t electrum_setup_fixture::get_error(const std::string& request)
{
try
{
return get(request).at("error").as_object().at("code").as_int64();
}
catch (const boost::system::system_error&)
{
return -1;
}
}

boost::json::value electrum_setup_fixture::get(const std::string& request)
{
socket_.send(boost::asio::buffer(request));
Expand All @@ -104,7 +116,6 @@ boost::json::value electrum_setup_fixture::get(const std::string& request)
}
catch (const boost::system::system_error&)
{
////BOOST_WARN_MESSAGE(false, e.what());
return boost::json::parse(R"({"dropped":true})");
}

Expand All @@ -114,6 +125,11 @@ boost::json::value electrum_setup_fixture::get(const std::string& request)
return boost::json::parse(response);
}

void electrum_setup_fixture::notify(node::chase event_, node::event_value value)
{
server_.notify(error::success, event_, value);
}

bool electrum_setup_fixture::handshake(electrum::version version,
const std::string& name, network::rpc::code_t id)
{
Expand Down
2 changes: 2 additions & 0 deletions test/protocols/electrum/electrum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ struct electrum_setup_fixture
bool address_index=true);
~electrum_setup_fixture();

int64_t get_error(const std::string& request);
boost::json::value get(const std::string& request);
void notify(node::chase event_, node::event_value value);
bool handshake(electrum::version version,
const std::string& name="test", network::rpc::code_t id=0);

Expand Down
Loading
Loading