Skip to content

Commit b8d0d0f

Browse files
committed
Implement electrum_protocol handle_blockchain_block_headers.
1 parent c38bb92 commit b8d0d0f

1 file changed

Lines changed: 82 additions & 1 deletion

File tree

src/protocols/protocol_electrum.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <bitcoin/server/protocols/protocol_electrum.hpp>
2020

2121
#include <algorithm>
22+
#include <ranges>
2223
#include <variant>
2324
#include <bitcoin/server/define.hpp>
2425
#include <bitcoin/server/interfaces/interfaces.hpp>
@@ -167,6 +168,7 @@ void protocol_electrum::handle_blockchain_block_headers(const code& ec,
167168
rpc_interface::blockchain_block_headers, double start_height, double count,
168169
double cp_height) NOEXCEPT
169170
{
171+
using namespace system;
170172
if (stopped(ec))
171173
return;
172174

@@ -181,7 +183,86 @@ void protocol_electrum::handle_blockchain_block_headers(const code& ec,
181183
return;
182184
}
183185

184-
send_code(error::not_implemented);
186+
if (is_add_overflow(starting, quantity))
187+
{
188+
send_code(error::argument_overflow);
189+
return;
190+
}
191+
192+
// The documented requirement: `start_height + (count - 1) <= cp_height` is
193+
// ambiguous at count = 0 so guard must be applied to both args and prover.
194+
const auto target = starting + sub1(quantity);
195+
const auto prove = !is_zero(quantity) && !is_zero(waypoint);
196+
if (prove && target > waypoint)
197+
{
198+
send_code(error::target_overflow);
199+
return;
200+
}
201+
202+
// Recommended to be at least one difficulty retarget period, e.g. 2016.
203+
// The maximum number of headers the server will return in single request.
204+
const auto maximum = server_settings().electrum.maximum_headers;
205+
206+
// Returned headers are assured to be contiguous despite intervening reorg.
207+
// No headers may be returned, which implies start > confirmed top block.
208+
const auto& query = archive();
209+
const auto bound = limit(quantity, maximum);
210+
const auto links = query.get_confirmed_headers(starting, bound);
211+
constexpr auto header_size = chain::header::serialized_size();
212+
auto size = two * header_size * links.size();
213+
214+
// Fetch and serialize headers.
215+
array_t headers{};
216+
headers.reserve(links.size());
217+
for (const auto& link: links)
218+
{
219+
if (const auto header = query.get_header(link); header)
220+
{
221+
// TODO: optimize by query directly returning wire serialization.
222+
headers.push_back(to_hex(*header, header_size));
223+
}
224+
else
225+
{
226+
send_code(error::server_error);
227+
return;
228+
}
229+
};
230+
231+
value_t value
232+
{
233+
object_t
234+
{
235+
{ "count", quantity },
236+
{ "headers", std::move(headers) },
237+
{ "max", maximum }
238+
}
239+
};
240+
241+
// There is a very slim change of inconsistency given an intervening reorg
242+
// because of get_merkle_root_and_proof() use of height-based calculations.
243+
// This is acceptable as it must be verified by caller in any case.
244+
if (prove)
245+
{
246+
hashes proof{};
247+
hash_digest root{};
248+
if (const auto code = query.get_merkle_root_and_proof(root, proof,
249+
target, waypoint))
250+
{
251+
send_code(code);
252+
return;
253+
}
254+
255+
array_t branch(proof.size());
256+
std::ranges::transform(proof, branch.begin(),
257+
[](const auto& hash) { return encode_base16(hash); });
258+
259+
auto& result = std::get<object_t>(value.value());
260+
result["root"] = encode_base16(root);
261+
result["branch"] = std::move(branch);
262+
size += two * hash_size * add1(proof.size());
263+
}
264+
265+
send_result(std::move(value), size + 42u, BIND(complete, _1));
185266
}
186267

187268
void protocol_electrum::handle_blockchain_headers_subscribe(const code& ec,

0 commit comments

Comments
 (0)