Skip to content

Commit a4ff608

Browse files
authored
Merge pull request #874 from evoskuil/master
Stub in first functional store request/response.
2 parents 70faf9c + 8e6a1e1 commit a4ff608

File tree

2 files changed

+205
-3
lines changed

2 files changed

+205
-3
lines changed

include/bitcoin/node/protocols/protocol_explore.hpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,87 @@ class BCN_API protocol_explore
4848
}
4949

5050
protected:
51+
52+
/// TODO: move to own source.
53+
/// Pagination and filtering are via query string.
54+
enum targets
55+
{
56+
/// /v[]/block/hash/[bkhash] {1}
57+
/// /v[]/block/height/[height] {1}s
58+
block,
59+
60+
/// /v[]/block/hash/[bkhash]/filter {1}
61+
/// /v[]/block/height/[height]/filter {1}
62+
filter,
63+
64+
/// /v[]/block/hash/[bkhash]/header {1}
65+
/// /v[]/block/height/[height]/header {1}
66+
header,
67+
68+
/// /v[]/transaction/hash/[txhash] {1}
69+
/// /v[]/block/hash/[bkhash]/transaction/[position] {1}
70+
/// /v[]/block/height/[height]/transaction/[position] {1}
71+
transaction,
72+
73+
/// /v[]/block/hash/[bkhash]/transactions {all txs in the block}
74+
/// /v[]/block/height/[height]/transactions {all txs in the block}
75+
transactions,
76+
77+
// --------------------------------------------------------------------
78+
79+
/// /v[]/input/[txhash]/[index] {1}
80+
input,
81+
82+
/// /v[]/inputs/[txhash] {all inputs in the tx}
83+
inputs,
84+
85+
/// /v[]/input/[txhash]/[index]/script {1}
86+
input_script,
87+
88+
/// /v[]/input/[txhash]/scripts {all input scripts in the tx}
89+
input_scripts,
90+
91+
/// /v[]/input/[txhash]/[index]/witness {1}
92+
input_witness,
93+
94+
/// /v[]/input/[txhash]/witnesses {all witnesses in the tx}
95+
input_witnesses,
96+
97+
// --------------------------------------------------------------------
98+
99+
/// /v[]/output/[txhash]/[index] {1}
100+
output,
101+
102+
/// /v[]/outputs/[txhash] {all outputs in the tx}
103+
outputs,
104+
105+
/// /v[]/output/[txhash]/[index]/script {1}
106+
output_script,
107+
108+
/// /v[]/output/[txhash]/scripts {all output scripts in the tx}
109+
output_scripts,
110+
111+
/// /v[]/output/[txhash]/[index]/spender {1 - confirmed}
112+
output_spender,
113+
114+
/// /v[]/output/[txhash]/spenders {all}
115+
output_spenders,
116+
117+
// --------------------------------------------------------------------
118+
119+
/// /v[]/address/[output-script-hash] {all}
120+
address
121+
};
122+
123+
/// Senders.
124+
void send_json(const network::http::request& request,
125+
boost::json::value&& model) NOEXCEPT;
126+
void send_data(const network::http::request& request,
127+
system::data_chunk&& data) NOEXCEPT;
128+
void send_text(const network::http::request& request,
129+
std::string&& hexidecimal) NOEXCEPT;
130+
131+
/// Receivers.
51132
void handle_receive_get(const code& ec,
52133
const network::http::method::get& request) NOEXCEPT override;
53134
};

src/protocols/protocol_explore.cpp

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,19 @@ namespace node {
2525

2626
#define CLASS protocol_explore
2727

28-
using namespace system;
29-
using namespace network::http;
28+
using namespace boost::json;
3029
using namespace std::placeholders;
30+
using namespace network::http;
31+
using namespace system;
32+
33+
const auto binary = mime_type::application_octet_stream;
34+
const auto json = mime_type::application_json;
35+
const auto text = mime_type::text_plain;
3136

3237
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3338

3439
// Handle get method.
3540
// ----------------------------------------------------------------------------
36-
// fields[], request->target(), file.is_open() are undeclared noexcept.
3741

3842
void protocol_explore::handle_receive_get(const code& ec,
3943
const method::get& request) NOEXCEPT
@@ -57,6 +61,85 @@ void protocol_explore::handle_receive_get(const code& ec,
5761
return;
5862
}
5963

64+
const auto target = request->target();
65+
if (!is_origin_form(target))
66+
{
67+
send_bad_target(*request);
68+
return;
69+
}
70+
71+
wallet::uri uri{};
72+
if (!uri.decode(target))
73+
{
74+
send_bad_target(*request);
75+
return;
76+
}
77+
78+
///////////////////////////////////////////////////////////////////////////
79+
// TODO: formatted error responses.
80+
// TODO: priority sort and dispatch.
81+
// TODO: URI path parse (see API doc).
82+
// TODO: performance timing header.
83+
///////////////////////////////////////////////////////////////////////////
84+
85+
const auto get_tx = [&](const auto& path) NOEXCEPT
86+
{
87+
hash_digest hash{};
88+
if (!decode_hash(hash, path))
89+
{
90+
send_bad_target(*request);
91+
return chain::transaction::cptr{};
92+
}
93+
94+
const auto& query = archive();
95+
return query.get_transaction(query.to_tx(hash), true);
96+
};
97+
98+
auto params = uri.decode_query();
99+
const auto hex = trim_copy(uri.path(), { "/" });
100+
const auto accepts = to_mime_types((*request)[field::accept]);
101+
102+
if (contains(accepts, json) || params["format"] == "json")
103+
{
104+
const auto tx = get_tx(hex);
105+
if (!tx)
106+
{
107+
send_not_found(*request);
108+
return;
109+
}
110+
111+
send_json(*request, value_from(*tx));
112+
return;
113+
}
114+
115+
if (contains(accepts, binary) || params["format"] == "binary")
116+
{
117+
const auto tx = get_tx(hex);
118+
if (!tx)
119+
{
120+
send_not_found(*request);
121+
return;
122+
}
123+
124+
send_data(*request, tx->to_data(true));
125+
return;
126+
}
127+
128+
if (contains(accepts, text) || params["format"] == "text")
129+
{
130+
const auto tx = get_tx(hex);
131+
if (!tx)
132+
{
133+
send_not_found(*request);
134+
return;
135+
}
136+
137+
send_text(*request, encode_base16(tx->to_data(true)));
138+
return;
139+
}
140+
141+
// Default to html (single page site).
142+
60143
// Empty path implies malformed target (terminal).
61144
auto path = to_local_path(request->target());
62145
if (path.empty())
@@ -87,6 +170,44 @@ void protocol_explore::handle_receive_get(const code& ec,
87170
send_file(*request, std::move(file), file_mime_type(path));
88171
}
89172

173+
// TODO: buffer should be reused, so set at the channel.
174+
// json_value is not a sized body, so this sets chunked encoding.
175+
void protocol_explore::send_json(const request& request,
176+
boost::json::value&& model) NOEXCEPT
177+
{
178+
BC_ASSERT_MSG(stranded(), "strand");
179+
response response{ status::ok, request.version() };
180+
add_common_headers(response, request);
181+
response.set(field::content_type, from_mime_type(json));
182+
response.body() = { std::move(model) };
183+
response.prepare_payload();
184+
SEND(std::move(response), handle_complete, _1, error::success);
185+
}
186+
187+
void protocol_explore::send_data(const request& request,
188+
data_chunk&& data) NOEXCEPT
189+
{
190+
BC_ASSERT_MSG(stranded(), "strand");
191+
data_response response{ status::ok, request.version() };
192+
add_common_headers(response, request);
193+
response.set(field::content_type, from_mime_type(binary));
194+
response.body() = std::move(data);
195+
response.prepare_payload();
196+
SEND(std::move(response), handle_complete, _1, error::success);
197+
}
198+
199+
void protocol_explore::send_text(const request& request,
200+
std::string&& hexidecimal) NOEXCEPT
201+
{
202+
BC_ASSERT_MSG(stranded(), "strand");
203+
string_response response{ status::ok, request.version() };
204+
add_common_headers(response, request);
205+
response.set(field::content_type, from_mime_type(text));
206+
response.body() = std::move(hexidecimal);
207+
response.prepare_payload();
208+
SEND(std::move(response), handle_complete, _1, error::success);
209+
}
210+
90211
BC_POP_WARNING()
91212

92213
} // namespace node

0 commit comments

Comments
 (0)