Skip to content

Commit 4a38f40

Browse files
committed
Add utils for bitcoind_rpc, impl first part of handle_get_block.
1 parent 70a1cf0 commit 4a38f40

File tree

2 files changed

+167
-26
lines changed

2 files changed

+167
-26
lines changed

include/bitcoin/node/protocols/protocol_bitcoind_rpc.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class BCN_API protocol_bitcoind_rpc
6868
bool handle_get_best_block_hash(const code& ec,
6969
rpc_interface::get_best_block_hash) NOEXCEPT;
7070
bool handle_get_block(const code& ec,
71-
rpc_interface::get_block, const std::string&, double) NOEXCEPT;
71+
rpc_interface::get_block, const std::string&,
72+
double verbosity) NOEXCEPT;
7273
bool handle_get_block_chain_info(const code& ec,
7374
rpc_interface::get_block_chain_info) NOEXCEPT;
7475
bool handle_get_block_count(const code& ec,
@@ -104,18 +105,38 @@ class BCN_API protocol_bitcoind_rpc
104105
bool handle_verify_tx_out_set(const code& ec,
105106
rpc_interface::verify_tx_out_set, const std::string&) NOEXCEPT;
106107

108+
/// Senders.
109+
void send_error(const code& ec) NOEXCEPT;
110+
void send_error(const code& ec, size_t size_hint) NOEXCEPT;
111+
void send_error(const code& ec, network::rpc::value_option&& error,
112+
size_t size_hint) NOEXCEPT;
113+
void send_text(std::string&& hexidecimal) NOEXCEPT;
114+
void send_result(network::rpc::value_option&& result,
115+
size_t size_hint) NOEXCEPT;
116+
107117
private:
108118
template <class Derived, typename Method, typename... Args>
109119
inline void subscribe(Method&& method, Args&&... args) NOEXCEPT
110120
{
111121
rpc_dispatcher_.subscribe(BIND_SHARED(method, args));
112122
}
113123

114-
// Send the response.
115-
void send_json(boost::json::value&& model, size_t size_hint) NOEXCEPT;
124+
// Senders.
125+
void send_rpc(network::rpc::response_t&& model,
126+
size_t size_hint) NOEXCEPT;
127+
128+
// Cache request for serialization (requires strand).
129+
void set_rpc_request(network::rpc::version version,
130+
const network::rpc::id_option& id,
131+
const network::http::request_cptr& request) NOEXCEPT;
132+
133+
// Obtain cached request and clear cache (requires strand).
134+
network::http::request_cptr reset_rpc_request() NOEXCEPT;
116135

117-
// This is protected by strand.
136+
// These are protected by strand.
118137
rpc_dispatcher rpc_dispatcher_{};
138+
network::rpc::version version_{};
139+
network::rpc::id_option id_{};
119140
};
120141

121142
} // namespace node

src/protocols/protocol_bitcoind_rpc.cpp

Lines changed: 142 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace node {
3131
using namespace system;
3232
using namespace network::rpc;
3333
using namespace network::http;
34+
using namespace network::json;
3435
using namespace network::monad;
3536
using namespace std::placeholders;
3637
using namespace boost::json;
@@ -130,48 +131,99 @@ void protocol_bitcoind_rpc::handle_receive_post(const code& ec,
130131
return;
131132
}
132133

133-
const auto& body = post->body();
134-
if (!body.contains<rpcin_value>())
134+
// Endpoint accepts only json-rpc posts.
135+
if (!post->body().contains<rpcin_value>())
135136
{
136137
send_not_acceptable(*post);
137138
return;
138139
}
139140

141+
// Get the parsed json-rpc request object.
142+
// v1 or v2 both supported, batch not yet supported.
143+
// v1 null id and v2 missing id implies notification and no response.
144+
const auto& request = post->body().get<rpcin_value>().message;
145+
140146
// The post is saved off during asynchonous handling and used in send_json
141147
// to formulate response headers, isolating handlers from http semantics.
142-
set_request(post);
148+
set_rpc_request(request.jsonrpc, request.id, post);
143149

144-
const auto& request = body.get<rpcin_value>().message;
150+
// Dispatch the request to subscribers.
145151
if (const auto code = rpc_dispatcher_.notify(request))
146152
stop(code);
147153
}
148154

155+
template <typename Object, typename ...Args>
156+
std::string to_hex(const Object& object, size_t size, Args&&... args) NOEXCEPT
157+
{
158+
std::string out(two * size, '\0');
159+
stream::out::fast sink{ out };
160+
write::base16::fast writer{ sink };
161+
object.to_data(writer, std::forward<Args>(args)...);
162+
BC_ASSERT(writer);
163+
return out;
164+
}
165+
149166
// Handlers.
150167
// ----------------------------------------------------------------------------
151168
// github.com/bitcoin/bitcoin/blob/master/doc/JSON-RPC-interface.md
152169
// TODO: precompute size for buffer hints.
153170

154-
// {"jsonrpc": "1.0", "id": "curltest", "method": "getbestblockhash", "params": []}
155171
bool protocol_bitcoind_rpc::handle_get_best_block_hash(const code& ec,
156172
rpc_interface::get_best_block_hash) NOEXCEPT
157173
{
158174
if (stopped(ec))
159175
return false;
160176

161-
const auto& query = archive();
162-
const auto hash = query.get_header_key(query.to_confirmed(
163-
query.get_top_confirmed()));
164-
165-
const response_t model{ .result = encode_hash(hash) };
166-
send_json(value_from(model), two * system::hash_size);
177+
const auto hash = archive().get_top_confirmed_hash();
178+
send_result(encode_hash(hash), two * system::hash_size);
167179
return true;
168180
}
169181

170-
// method<"getblock", string_t, optional<0_u32>>{ "blockhash", "verbosity" },
171182
bool protocol_bitcoind_rpc::handle_get_block(const code& ec,
172-
rpc_interface::get_block, const std::string&, double) NOEXCEPT
183+
rpc_interface::get_block, const std::string& blockhash,
184+
double verbosity) NOEXCEPT
173185
{
174-
return !ec;
186+
if (stopped(ec))
187+
return false;
188+
189+
hash_digest hash{};
190+
if (!decode_hash(hash, blockhash))
191+
{
192+
send_error(error::not_found, blockhash, blockhash.size());
193+
return true;
194+
}
195+
196+
constexpr auto witness = true;
197+
const auto& query = archive();
198+
const auto link = query.to_header(hash);
199+
200+
if (verbosity == 0.0)
201+
{
202+
const auto block = query.get_block(link, witness);
203+
if (is_null(block))
204+
{
205+
send_error(error::not_found, blockhash, blockhash.size());
206+
return true;
207+
}
208+
209+
send_text(to_hex(*block, block->serialized_size(witness), witness));
210+
return true;
211+
}
212+
213+
if (verbosity == 1.0)
214+
{
215+
send_error(error::not_implemented);
216+
return true;
217+
}
218+
219+
if (verbosity == 2.0)
220+
{
221+
send_error(error::not_implemented);
222+
return true;
223+
}
224+
225+
send_error(error::invalid_argument);
226+
return true;
175227
}
176228

177229
bool protocol_bitcoind_rpc::handle_get_block_chain_info(const code& ec,
@@ -277,26 +329,94 @@ bool protocol_bitcoind_rpc::handle_verify_tx_out_set(const code& ec,
277329
return !ec;
278330
}
279331

280-
// private
332+
// Senders
281333
// ----------------------------------------------------------------------------
282334

283-
// TODO: post-process response for json-rpc version.
284-
void protocol_bitcoind_rpc::send_json(boost::json::value&& model,
335+
void protocol_bitcoind_rpc::send_error(const code& ec) NOEXCEPT
336+
{
337+
send_error(ec, two * ec.message().size());
338+
}
339+
340+
void protocol_bitcoind_rpc::send_error(const code& ec,
341+
size_t size_hint) NOEXCEPT
342+
{
343+
send_error(ec, {}, size_hint);
344+
}
345+
346+
void protocol_bitcoind_rpc::send_error(const code& ec, value_option&& error,
347+
size_t size_hint) NOEXCEPT
348+
{
349+
BC_ASSERT(stranded());
350+
send_rpc(response_t
351+
{
352+
.jsonrpc = version_,
353+
.id = id_,
354+
.error = result_t
355+
{
356+
.code = ec.value(),
357+
.message = ec.message(),
358+
.data = std::move(error)
359+
}
360+
}, size_hint);
361+
}
362+
363+
void protocol_bitcoind_rpc::send_text(std::string&& hexidecimal) NOEXCEPT
364+
{
365+
BC_ASSERT(stranded());
366+
send_result(hexidecimal, hexidecimal.size());
367+
}
368+
369+
void protocol_bitcoind_rpc::send_result(value_option&& result,
370+
size_t size_hint) NOEXCEPT
371+
{
372+
BC_ASSERT(stranded());
373+
send_rpc(response_t
374+
{
375+
.jsonrpc = version_,
376+
.id = id_,
377+
.result = std::move(result)
378+
}, size_hint);
379+
}
380+
381+
// private
382+
void protocol_bitcoind_rpc::send_rpc(response_t&& model,
285383
size_t size_hint) NOEXCEPT
286384
{
287385
BC_ASSERT(stranded());
288-
using namespace network::monad;
289-
const auto request = reset_request();
290-
constexpr auto json = media_type::application_json;
386+
static const auto json = from_media_type(media_type::application_json);
387+
const auto request = reset_rpc_request();
291388
response response{ status::ok, request->version() };
292389
add_common_headers(response, *request);
293390
add_access_control_headers(response, *request);
294-
response.set(field::content_type, from_media_type(json));
295-
response.body() = json_value{ std::move(model), size_hint };
391+
response.set(field::content_type, json);
392+
response.body() = rpcout_value
393+
{
394+
network::json::json_value{ .size_hint = size_hint },
395+
std::move(model),
396+
};
296397
response.prepare_payload();
297398
SEND(std::move(response), handle_complete, _1, error::success);
298399
}
299400

401+
// private
402+
void protocol_bitcoind_rpc::set_rpc_request(version version,
403+
const id_option& id, const request_cptr& request) NOEXCEPT
404+
{
405+
BC_ASSERT(stranded());
406+
id_ = id;
407+
version_ = version;
408+
set_request(request);
409+
}
410+
411+
// private
412+
request_cptr protocol_bitcoind_rpc::reset_rpc_request() NOEXCEPT
413+
{
414+
BC_ASSERT(stranded());
415+
id_.reset();
416+
version_ = version::undefined;
417+
return reset_request();
418+
}
419+
300420
BC_POP_WARNING()
301421
BC_POP_WARNING()
302422
BC_POP_WARNING()

0 commit comments

Comments
 (0)