@@ -85,18 +85,18 @@ iteratePriceData(
85
85
86
86
auto const ledger = context.ledgerMaster .getLedgerBySeq (prevSeq);
87
87
if (!ledger)
88
- return ;
88
+ return ; // LCOV_EXCL_LINE
89
89
90
90
meta = ledger->txRead (prevTx).second ;
91
91
92
+ prevChain = chain;
92
93
for (STObject const & node : meta->getFieldArray (sfAffectedNodes))
93
94
{
94
95
if (node.getFieldU16 (sfLedgerEntryType) != ltORACLE)
95
96
{
96
97
continue ;
97
98
}
98
99
99
- prevChain = chain;
100
100
chain = &node;
101
101
isNew = node.isFieldPresent (sfNewFields);
102
102
// if a meta is for the new and this is the first
@@ -170,21 +170,49 @@ doGetAggregatePrice(RPC::JsonContext& context)
170
170
if (!params.isMember (jss::quote_asset))
171
171
return RPC::missing_field_error (jss::quote_asset);
172
172
173
+ // Lambda to validate uint type
174
+ // support positive int, uint, and a number represented as a string
175
+ auto validUInt = [](Json::Value const & params,
176
+ Json::StaticString const & field) {
177
+ auto const & jv = params[field];
178
+ std::uint32_t v;
179
+ return jv.isUInt () || (jv.isInt () && jv.asInt () >= 0 ) ||
180
+ (jv.isString () && beast::lexicalCastChecked (v, jv.asString ()));
181
+ };
182
+
173
183
// Lambda to get `trim` and `time_threshold` fields. If the field
174
184
// is not included in the input then a default value is returned.
175
- auto getField = [¶ms](
185
+ auto getField = [¶ms, &validUInt ](
176
186
Json::StaticString const & field,
177
187
unsigned int def =
178
188
0 ) -> std::variant<std::uint32_t , error_code_i> {
179
189
if (params.isMember (field))
180
190
{
181
- if (!params[ field]. isConvertibleTo (Json::ValueType::uintValue ))
182
- return rpcORACLE_MALFORMED ;
191
+ if (!validUInt ( params, field))
192
+ return rpcINVALID_PARAMS ;
183
193
return params[field].asUInt ();
184
194
}
185
195
return def;
186
196
};
187
197
198
+ // Lambda to get `base_asset` and `quote_asset`. The values have
199
+ // to conform to the Currency type.
200
+ auto getCurrency =
201
+ [¶ms](SField const & sField , Json::StaticString const & field)
202
+ -> std::variant<Json::Value, error_code_i> {
203
+ try
204
+ {
205
+ if (params[field].asString ().empty ())
206
+ return rpcINVALID_PARAMS;
207
+ currencyFromJson (sField , params[field]);
208
+ return params[field];
209
+ }
210
+ catch (...)
211
+ {
212
+ return rpcINVALID_PARAMS;
213
+ }
214
+ };
215
+
188
216
auto const trim = getField (jss::trim);
189
217
if (std::holds_alternative<error_code_i>(trim))
190
218
{
@@ -206,8 +234,18 @@ doGetAggregatePrice(RPC::JsonContext& context)
206
234
return result;
207
235
}
208
236
209
- auto const & baseAsset = params[jss::base_asset];
210
- auto const & quoteAsset = params[jss::quote_asset];
237
+ auto const baseAsset = getCurrency (sfBaseAsset, jss::base_asset);
238
+ if (std::holds_alternative<error_code_i>(baseAsset))
239
+ {
240
+ RPC::inject_error (std::get<error_code_i>(baseAsset), result);
241
+ return result;
242
+ }
243
+ auto const quoteAsset = getCurrency (sfQuoteAsset, jss::quote_asset);
244
+ if (std::holds_alternative<error_code_i>(quoteAsset))
245
+ {
246
+ RPC::inject_error (std::get<error_code_i>(quoteAsset), result);
247
+ return result;
248
+ }
211
249
212
250
// Collect the dataset into bimap keyed by lastUpdateTime and
213
251
// STAmount (Number is int64 and price is uint64)
@@ -220,8 +258,7 @@ doGetAggregatePrice(RPC::JsonContext& context)
220
258
RPC::inject_error (rpcORACLE_MALFORMED, result);
221
259
return result;
222
260
}
223
- auto const documentID = oracle[jss::oracle_document_id].isConvertibleTo (
224
- Json::ValueType::uintValue)
261
+ auto const documentID = validUInt (oracle, jss::oracle_document_id)
225
262
? std::make_optional (oracle[jss::oracle_document_id].asUInt ())
226
263
: std::nullopt;
227
264
auto const account =
@@ -235,7 +272,7 @@ doGetAggregatePrice(RPC::JsonContext& context)
235
272
std::shared_ptr<ReadView const > ledger;
236
273
result = RPC::lookupLedger (ledger, context);
237
274
if (!ledger)
238
- return result;
275
+ return result; // LCOV_EXCL_LINE
239
276
240
277
auto const sle = ledger->read (keylet::oracle (*account, *documentID));
241
278
iteratePriceData (context, sle, [&](STObject const & node) {
@@ -246,9 +283,9 @@ doGetAggregatePrice(RPC::JsonContext& context)
246
283
series.end (),
247
284
[&](STObject const & o) -> bool {
248
285
return o.getFieldCurrency (sfBaseAsset).getText () ==
249
- baseAsset &&
286
+ std::get<Json::Value>( baseAsset) &&
250
287
o.getFieldCurrency (sfQuoteAsset).getText () ==
251
- quoteAsset &&
288
+ std::get<Json::Value>( quoteAsset) &&
252
289
o.isFieldPresent (sfAssetPrice);
253
290
});
254
291
iter != series.end ())
@@ -287,10 +324,15 @@ doGetAggregatePrice(RPC::JsonContext& context)
287
324
prices.left .erase (
288
325
prices.left .upper_bound (upperBound), prices.left .end ());
289
326
327
+ // At least one element should remain since upperBound is either
328
+ // equal to oldestTime or is less than latestTime, in which case
329
+ // the data is deleted between the oldestTime and upperBound.
290
330
if (prices.empty ())
291
331
{
292
- RPC::inject_error (rpcOBJECT_NOT_FOUND, result);
332
+ // LCOV_EXCL_START
333
+ RPC::inject_error (rpcINTERNAL, result);
293
334
return result;
335
+ // LCOV_EXCL_STOP
294
336
}
295
337
}
296
338
result[jss::time ] = latestTime;
0 commit comments