Skip to content

Commit 2271904

Browse files
committed
Avoid exceptions in explore parser.
1 parent 01f79be commit 2271904

File tree

3 files changed

+147
-159
lines changed

3 files changed

+147
-159
lines changed

include/bitcoin/node/rest/rest.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
namespace libbitcoin {
2525
namespace node {
2626

27-
BCN_API network::rpc::request_t path_to_request(const std::string& path) THROWS;
27+
BCN_API code path_to_request(network::rpc::request_t& out,
28+
const std::string& path) NOEXCEPT;
2829

2930
} // namespace network
3031
} // namespace libbitcoin

src/rest/rest.cpp

Lines changed: 112 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -32,98 +32,111 @@ using namespace network::rpc;
3232
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
3333

3434
template <typename Number>
35-
Number to_number(const std::string_view& token) THROWS
35+
static bool to_number(Number& out, const std::string_view& token) NOEXCEPT
3636
{
37-
Number out{};
38-
if (token.empty() || token.front() == '0' ||
39-
!is_ascii_numeric(token) || !deserialize(out, token))
40-
throw std::runtime_error("invalid number");
41-
42-
return out;
37+
return !token.empty() && is_ascii_numeric(token) && token.front() != '0' &&
38+
deserialize(out, token);
4339
}
4440

45-
hash_cptr to_hash(const std::string_view& token) THROWS
41+
static hash_cptr to_hash(const std::string_view& token) NOEXCEPT
4642
{
4743
hash_digest out{};
48-
if (!decode_hash(out, token))
49-
throw std::runtime_error("invalid hash");
50-
51-
return emplace_shared<const hash_digest>(std::move(out));
44+
return decode_hash(out, token) ?
45+
emplace_shared<const hash_digest>(std::move(out)) : hash_cptr{};
5246
}
5347

54-
request_t path_to_request(const std::string& url_path) THROWS
48+
code path_to_request(request_t& out, const std::string& path) NOEXCEPT
5549
{
5650
// Avoid conflict with node type.
5751
using object_t = network::rpc::object_t;
5852

5953
// Initialize json-rpc.v2 named params message.
60-
request_t request
54+
out = request_t
6155
{
6256
.jsonrpc = version::v2,
6357
.id = null_t{},
6458
.method = {},
6559
.params = object_t{}
6660
};
6761

68-
auto& method = request.method;
69-
auto& params = std::get<object_t>(request.params.value());
70-
const auto segments = split(url_path, "/", false, true);
62+
auto& method = out.method;
63+
auto& params = std::get<object_t>(out.params.value());
64+
const auto segments = split(path, "/", false, true);
7165
if (segments.empty())
72-
throw std::runtime_error("empty url path");
66+
return error::empty_path;
7367

74-
size_t index{};
75-
if (!segments[index].starts_with('v'))
76-
throw std::runtime_error("missing version");
68+
size_t segment{};
69+
if (!segments[segment].starts_with('v'))
70+
return error::missing_version;
7771

78-
params["version"] = to_number<uint8_t>(segments[index++].substr(one));
79-
if (index == segments.size())
80-
throw std::runtime_error("missing target");
72+
uint8_t version{};
73+
if (!to_number(version, segments[segment++].substr(one)))
74+
return error::invalid_number;
75+
76+
params["version"] = version;
77+
if (segment == segments.size())
78+
return error::missing_target;
8179

8280
// transaction, address, inputs, and outputs are identical excluding names;
8381
// input and output are identical excluding names; block is unique.
84-
const auto target = segments[index++];
82+
const auto target = segments[segment++];
8583
if (target == "transaction")
8684
{
87-
if (index == segments.size())
88-
throw std::runtime_error("missing transaction hash");
85+
if (segment == segments.size())
86+
return error::missing_hash;
87+
88+
const auto hash = to_hash(segments[segment++]);
89+
if (!hash) return error::invalid_hash;
8990

9091
method = "transaction";
91-
params["hash"] = to_hash(segments[index++]);
92+
params["hash"] = hash;
9293
}
9394
else if (target == "address")
9495
{
95-
if (index == segments.size())
96-
throw std::runtime_error("missing address hash");
96+
if (segment == segments.size())
97+
return error::missing_hash;
98+
99+
const auto hash = to_hash(segments[segment++]);
100+
if (!hash) return error::invalid_hash;
97101

98102
method = "address";
99-
params["hash"] = to_hash(segments[index++]);
103+
params["hash"] = hash;
100104
}
101105
else if (target == "inputs")
102106
{
103-
if (index == segments.size())
104-
throw std::runtime_error("missing inputs tx hash");
107+
if (segment == segments.size())
108+
return error::missing_hash;
109+
110+
const auto hash = to_hash(segments[segment++]);
111+
if (!hash) return error::invalid_hash;
105112

106113
method = "inputs";
107-
params["hash"] = to_hash(segments[index++]);
114+
params["hash"] = hash;
108115
}
109116
else if (target == "outputs")
110117
{
111-
if (index == segments.size())
112-
throw std::runtime_error("missing outputs tx hash");
118+
if (segment == segments.size())
119+
return error::missing_hash;
120+
121+
const auto hash = to_hash(segments[segment++]);
122+
if (!hash) return error::invalid_hash;
113123

114124
method = "outputs";
115-
params["hash"] = to_hash(segments[index++]);
125+
params["hash"] = hash;
116126
}
117127
else if (target == "input")
118128
{
119-
if (index == segments.size())
120-
throw std::runtime_error("missing input tx hash");
129+
if (segment == segments.size())
130+
return error::missing_hash;
131+
132+
const auto hash = to_hash(segments[segment++]);
133+
if (!hash) return error::invalid_hash;
121134

122-
params["hash"] = to_hash(segments[index++]);
123-
if (index == segments.size())
124-
throw std::runtime_error("missing input component");
135+
params["hash"] = hash;
136+
if (segment == segments.size())
137+
return error::missing_component;
125138

126-
const auto component = segments[index++];
139+
const auto component = segments[segment++];
127140
if (component == "scripts")
128141
{
129142
method = "input_scripts";
@@ -134,33 +147,40 @@ request_t path_to_request(const std::string& url_path) THROWS
134147
}
135148
else
136149
{
137-
params["index"] = to_number<uint32_t>(component);
138-
if (index == segments.size())
150+
uint32_t index{};
151+
if (!to_number(index, component))
152+
return error::invalid_number;
153+
154+
params["index"] = index;
155+
if (segment == segments.size())
139156
{
140157
method = "input";
141158
}
142159
else
143160
{
144-
auto subcomponent = segments[index++];
161+
auto subcomponent = segments[segment++];
145162
if (subcomponent == "script")
146163
method = "input_script";
147164
else if (subcomponent == "witness")
148165
method = "input_witness";
149166
else
150-
throw std::runtime_error("unexpected input subcomponent");
167+
return error::invalid_subcomponent;
151168
}
152169
}
153170
}
154171
else if (target == "output")
155172
{
156-
if (index == segments.size())
157-
throw std::runtime_error("missing output tx hash");
173+
if (segment == segments.size())
174+
return error::missing_hash;
175+
176+
const auto hash = to_hash(segments[segment++]);
177+
if (!hash) return error::invalid_hash;
158178

159-
params["hash"] = to_hash(segments[index++]);
160-
if (index == segments.size())
161-
throw std::runtime_error("missing output component");
179+
params["hash"] = hash;
180+
if (segment == segments.size())
181+
return error::missing_component;
162182

163-
const auto component = segments[index++];
183+
const auto component = segments[segment++];
164184
if (component == "scripts")
165185
{
166186
method = "output_scripts";
@@ -171,82 +191,94 @@ request_t path_to_request(const std::string& url_path) THROWS
171191
}
172192
else
173193
{
174-
params["index"] = to_number<uint32_t>(component);
175-
if (index == segments.size())
194+
uint32_t index{};
195+
if (!to_number(index, component))
196+
return error::invalid_number;
197+
198+
params["index"] = index;
199+
if (segment == segments.size())
176200
{
177201
method = "output";
178202
}
179203
else
180204
{
181-
auto subcomponent = segments[index++];
205+
auto subcomponent = segments[segment++];
182206
if (subcomponent == "script")
183207
method = "output_script";
184208
else if (subcomponent == "spender")
185209
method = "output_spender";
186210
else
187-
throw std::runtime_error("unexpected output subcomponent");
211+
return error::invalid_subcomponent;
188212
}
189213
}
190214
}
191215
else if (target == "block")
192216
{
193-
if (index == segments.size())
194-
throw std::runtime_error("missing block id");
217+
if (segment == segments.size())
218+
return error::missing_id_type;
195219

196-
const auto by = segments[index++];
220+
const auto by = segments[segment++];
197221
if (by == "hash")
198222
{
199-
if (index == segments.size())
200-
throw std::runtime_error("missing block hash");
223+
if (segment == segments.size())
224+
return error::missing_hash;
225+
226+
const auto hash = to_hash(segments[segment++]);
227+
if (!hash) return error::invalid_hash;
201228

202-
params["hash"] = to_hash(segments[index++]);
203-
params["height"] = value_t{ null_t{} };
229+
params["hash"] = hash;
230+
params["height"] = null_t{};
204231
}
205232
else if (by == "height")
206233
{
207-
if (index == segments.size())
208-
throw std::runtime_error("missing block height");
234+
if (segment == segments.size())
235+
return error::missing_height;
209236

210-
params["hash"] = value_t{ null_t{} };
211-
params["height"] = to_number<uint32_t>(segments[index++]);
237+
uint32_t height{};
238+
if (!to_number(height, segments[segment++]))
239+
return error::invalid_number;
240+
241+
params["hash"] = null_t{};
242+
params["height"] = height;
212243
}
213244
else
214245
{
215-
throw std::runtime_error("invalid block id");
246+
return error::invalid_id_type;
216247
}
217248

218-
if (index == segments.size())
249+
if (segment == segments.size())
219250
{
220251
method = "block";
221252
}
222253
else
223254
{
224-
const auto component = segments[index++];
255+
const auto component = segments[segment++];
225256
if (component == "header")
226257
method = "header";
227258
else if (component == "filter")
228259
method = "filter";
229260
else if (component == "transactions")
230261
method = "block_txs";
231262
else if (component != "transaction")
232-
throw std::runtime_error("invalid block component");
263+
return error::invalid_component;
264+
265+
if (segment == segments.size())
266+
return error::missing_position;
233267

234-
if (index == segments.size())
235-
throw std::runtime_error("missing tx position");
268+
uint32_t position{};
269+
if (!to_number(position, segments[segment++]))
270+
return error::invalid_number;
236271

237-
params["position"] = to_number<uint32_t>(segments[index++]);
272+
params["position"] = position;
238273
method = "block_tx";
239274
}
240275
}
241276
else
242277
{
243-
throw std::runtime_error("unknown target");
278+
return error::invalid_target;
244279
}
245280

246-
if (index != segments.size())
247-
throw std::runtime_error("extra segments");
248-
249-
return request;
281+
return segment == segments.size() ? error::success : error::extra_segment;
250282
}
251283

252284
BC_POP_WARNING()

0 commit comments

Comments
 (0)