rippled
Loading...
Searching...
No Matches
Oracle.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx/Oracle.h>
21
22#include <xrpl/protocol/jss.h>
23
24#include <boost/lexical_cast/try_lexical_convert.hpp>
25#include <boost/regex.hpp>
26
27#include <vector>
28
29namespace ripple {
30namespace test {
31namespace jtx {
32namespace oracle {
33
34Oracle::Oracle(Env& env, CreateArg const& arg, bool submit)
35 : env_(env), owner_{}, documentID_{}
36{
37 // LastUpdateTime is checked to be in range
38 // {close-maxLastUpdateTimeDelta, close+maxLastUpdateTimeDelta}.
39 // To make the validation work and to make the clock consistent
40 // for tests running at different time, simulate Unix time starting
41 // on testStartTime since Ripple epoch.
42 auto const now = env_.timeKeeper().now();
43 if (now.time_since_epoch().count() == 0 || arg.close)
45 if (arg.owner)
46 owner_ = *arg.owner;
47 if (arg.documentID && validDocumentID(*arg.documentID))
49 if (submit)
50 set(arg);
51}
52
53void
55{
56 Json::Value jv;
57 jv[jss::TransactionType] = jss::OracleDelete;
58 jv[jss::Account] = to_string(arg.owner.value_or(owner_));
59 toJson(jv[jss::OracleDocumentID], arg.documentID.value_or(documentID_));
60 if (Oracle::fee != 0)
61 jv[jss::Fee] = std::to_string(Oracle::fee);
62 else if (arg.fee != 0)
63 jv[jss::Fee] = std::to_string(arg.fee);
64 else
65 jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops());
66 if (arg.flags != 0)
67 jv[jss::Flags] = arg.flags;
68 submit(jv, arg.msig, arg.seq, arg.err);
69}
70
71void
73 Json::Value const& jv,
76 std::optional<ter> const& err)
77{
78 if (msig)
79 {
80 if (seq && err)
81 env_(jv, *msig, *seq, *err);
82 else if (seq)
83 env_(jv, *msig, *seq);
84 else if (err)
85 env_(jv, *msig, *err);
86 else
87 env_(jv, *msig);
88 }
89 else if (seq && err)
90 env_(jv, *seq, *err);
91 else if (seq)
92 env_(jv, *seq);
93 else if (err)
94 env_(jv, *err);
95 else
96 env_(jv);
97 env_.close();
98}
99
100bool
101Oracle::exists(Env& env, AccountID const& account, std::uint32_t documentID)
102{
103 assert(account.isNonZero());
104 return env.le(keylet::oracle(account, documentID)) != nullptr;
105}
106
107bool
108Oracle::expectPrice(DataSeries const& series) const
109{
110 if (auto const sle = env_.le(keylet::oracle(owner_, documentID_)))
111 {
112 auto const& leSeries = sle->getFieldArray(sfPriceDataSeries);
113 if (leSeries.size() == 0 || leSeries.size() != series.size())
114 return false;
115 for (auto const& data : series)
116 {
117 if (std::find_if(
118 leSeries.begin(),
119 leSeries.end(),
120 [&](STObject const& o) -> bool {
121 auto const& baseAsset = o.getFieldCurrency(sfBaseAsset);
122 auto const& quoteAsset =
123 o.getFieldCurrency(sfQuoteAsset);
124 auto const& price = o.getFieldU64(sfAssetPrice);
125 auto const& scale = o.getFieldU8(sfScale);
126 return baseAsset.getText() == std::get<0>(data) &&
127 quoteAsset.getText() == std::get<1>(data) &&
128 price == std::get<2>(data) &&
129 scale == std::get<3>(data);
130 }) == leSeries.end())
131 return false;
132 }
133 return true;
134 }
135 return false;
136}
137
138bool
140{
141 auto const sle = env_.le(keylet::oracle(owner_, documentID_));
142 return sle && (*sle)[sfLastUpdateTime] == lastUpdateTime;
143}
144
147 Env& env,
148 std::optional<AnyValue> const& baseAsset,
149 std::optional<AnyValue> const& quoteAsset,
150 std::optional<OraclesData> const& oracles,
151 std::optional<AnyValue> const& trim,
152 std::optional<AnyValue> const& timeThreshold)
153{
154 Json::Value jv;
155 Json::Value jvOracles(Json::arrayValue);
156 if (oracles)
157 {
158 for (auto const& id : *oracles)
159 {
160 Json::Value oracle;
161 if (id.first)
162 oracle[jss::account] = to_string((*id.first).id());
163 if (id.second)
164 toJson(oracle[jss::oracle_document_id], *id.second);
165 jvOracles.append(oracle);
166 }
167 jv[jss::oracles] = jvOracles;
168 }
169 if (trim)
170 toJson(jv[jss::trim], *trim);
171 if (baseAsset)
172 toJson(jv[jss::base_asset], *baseAsset);
173 if (quoteAsset)
174 toJson(jv[jss::quote_asset], *quoteAsset);
175 if (timeThreshold)
176 toJson(jv[jss::time_threshold], *timeThreshold);
177 // Convert "%None%" to None
178 auto str = to_string(jv);
179 str = boost::regex_replace(str, boost::regex(NonePattern), UnquotedNone);
180 auto jr = env.rpc("json", "get_aggregate_price", str);
181
182 if (jr.isObject())
183 {
184 if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
185 return jr[jss::result];
186 else if (jr.isMember(jss::error))
187 return jr;
188 }
189 return Json::nullValue;
190}
191
192void
194{
195 using namespace std::chrono;
196 Json::Value jv;
197 if (arg.owner)
198 owner_ = *arg.owner;
199 if (arg.documentID &&
201 {
203 jv[jss::OracleDocumentID] = documentID_;
204 }
205 else if (arg.documentID)
206 toJson(jv[jss::OracleDocumentID], *arg.documentID);
207 else
208 jv[jss::OracleDocumentID] = documentID_;
209 jv[jss::TransactionType] = jss::OracleSet;
210 jv[jss::Account] = to_string(owner_);
211 if (arg.assetClass)
212 toJsonHex(jv[jss::AssetClass], *arg.assetClass);
213 if (arg.provider)
214 toJsonHex(jv[jss::Provider], *arg.provider);
215 if (arg.uri)
216 toJsonHex(jv[jss::URI], *arg.uri);
217 if (arg.flags != 0)
218 jv[jss::Flags] = arg.flags;
219 if (Oracle::fee != 0)
220 jv[jss::Fee] = std::to_string(Oracle::fee);
221 else if (arg.fee != 0)
222 jv[jss::Fee] = std::to_string(arg.fee);
223 else
224 jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops());
225 // lastUpdateTime if provided is offset from testStartTime
226 if (arg.lastUpdateTime)
227 {
229 jv[jss::LastUpdateTime] = to_string(
232 else
233 toJson(jv[jss::LastUpdateTime], *arg.lastUpdateTime);
234 }
235 else
236 jv[jss::LastUpdateTime] = to_string(
237 duration_cast<seconds>(
238 env_.current()->info().closeTime.time_since_epoch())
239 .count() +
241 Json::Value dataSeries(Json::arrayValue);
242 auto assetToStr = [](std::string const& s) {
243 // assume standard currency
244 if (s.size() == 3)
245 return s;
246 assert(s.size() <= 20);
247 // anything else must be 160-bit hex string
248 std::string h = strHex(s);
249 return strHex(s).append(40 - s.size() * 2, '0');
250 };
251 for (auto const& data : arg.series)
252 {
253 Json::Value priceData;
254 Json::Value price;
255 price[jss::BaseAsset] = assetToStr(std::get<0>(data));
256 price[jss::QuoteAsset] = assetToStr(std::get<1>(data));
257 if (std::get<2>(data))
258 price[jss::AssetPrice] = *std::get<2>(data);
259 if (std::get<3>(data))
260 price[jss::Scale] = *std::get<3>(data);
261 priceData[jss::PriceData] = price;
262 dataSeries.append(priceData);
263 }
264 jv[jss::PriceDataSeries] = dataSeries;
265 submit(jv, arg.msig, arg.seq, arg.err);
266}
267
268void
270{
272 .owner = arg.owner,
273 .documentID = arg.documentID,
274 .series = arg.series,
275 .assetClass = arg.assetClass,
276 .provider = arg.provider,
277 .uri = arg.uri,
278 .lastUpdateTime = arg.lastUpdateTime,
279 .flags = arg.flags,
280 .msig = arg.msig,
281 .seq = arg.seq,
282 .fee = arg.fee,
283 .err = arg.err});
284}
285
288 Env& env,
290 std::optional<AnyValue> const& documentID,
291 std::optional<std::string> const& index)
292{
293 Json::Value jvParams;
294 if (account)
295 {
297 jvParams[jss::oracle][jss::account] =
299 else
300 jvParams[jss::oracle][jss::account] =
301 std::get<std::string>(*account);
302 }
303 if (documentID)
304 toJson(jvParams[jss::oracle][jss::oracle_document_id], *documentID);
305 if (index)
306 {
308 if (boost::conversion::try_lexical_convert(*index, i))
309 jvParams[jss::oracle][jss::ledger_index] = i;
310 else
311 jvParams[jss::oracle][jss::ledger_index] = *index;
312 }
313 // Convert "%None%" to None
314 auto str = to_string(jvParams);
315 str = boost::regex_replace(str, boost::regex(NonePattern), UnquotedNone);
316 auto jr = env.rpc("json", "ledger_entry", str);
317
318 if (jr.isObject())
319 {
320 if (jr.isMember(jss::error))
321 return jr;
322 if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
323 return jr[jss::result];
324 }
325 return Json::nullValue;
326}
327
328void
330{
331 std::visit([&](auto&& arg) { jv = arg; }, v);
332}
333
334void
336{
338 [&]<typename T>(T&& arg) {
340 {
341 if (arg.starts_with("##"))
342 jv = arg.substr(2);
343 else
344 jv = strHex(arg);
345 }
346 else
347 jv = arg;
348 },
349 v);
350}
351
354{
355 Json::Value jv;
356 toJson(jv, v);
357 return jv.asUInt();
358}
359
360bool
362{
363 try
364 {
365 Json::Value jv;
366 toJson(jv, v);
367 jv.asUInt();
368 jv.isNumeric();
369 return true;
370 }
371 catch (...)
372 {
373 }
374 return false;
375}
376
377} // namespace oracle
378} // namespace jtx
379} // namespace test
380} // namespace ripple
T append(T... args)
Represents a JSON value.
Definition json_value.h:149
Value & append(Value const &value)
Append value to array at the end.
UInt asUInt() const
bool isNumeric() const
time_point now() const override
Returns the current time.
A transaction testing environment.
Definition Env.h:121
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
ManualTimeKeeper & timeKeeper()
Definition Env.h:273
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:791
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:277
Set a multisignature on a JTx.
Definition multisign.h:67
static Json::Value aggregatePrice(Env &env, std::optional< AnyValue > const &baseAsset, std::optional< AnyValue > const &quoteAsset, std::optional< OraclesData > const &oracles=std::nullopt, std::optional< AnyValue > const &trim=std::nullopt, std::optional< AnyValue > const &timeTreshold=std::nullopt)
Definition Oracle.cpp:146
static Json::Value ledgerEntry(Env &env, std::optional< std::variant< AccountID, std::string > > const &account, std::optional< AnyValue > const &documentID, std::optional< std::string > const &index=std::nullopt)
Definition Oracle.cpp:287
void set(CreateArg const &arg)
Definition Oracle.cpp:269
Oracle(Env &env, CreateArg const &arg, bool submit=true)
Definition Oracle.cpp:34
void remove(RemoveArg const &arg)
Definition Oracle.cpp:54
static std::uint32_t fee
Definition Oracle.h:123
bool expectPrice(DataSeries const &pricess) const
Definition Oracle.cpp:108
bool expectLastUpdateTime(std::uint32_t lastUpdateTime) const
Definition Oracle.cpp:139
std::uint32_t documentID() const
Definition Oracle.h:157
void submit(Json::Value const &jv, std::optional< jtx::msig > const &msig, std::optional< jtx::seq > const &seq, std::optional< ter > const &err)
Definition Oracle.cpp:72
T count(T... args)
T find_if(T... args)
T is_same_v
@ nullValue
'null' value
Definition json_value.h:38
@ arrayValue
array value (ordered list)
Definition json_value.h:44
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
Definition Indexes.cpp:520
static constexpr std::chrono::seconds testStartTime
Definition Oracle.h:112
constexpr char const * UnquotedNone
Definition Oracle.h:39
std::uint32_t asUInt(AnyValue const &v)
Definition Oracle.cpp:353
void toJson(Json::Value &jv, AnyValue const &v)
Definition Oracle.cpp:329
constexpr char const * NonePattern
Definition Oracle.h:40
bool validDocumentID(AnyValue const &v)
Definition Oracle.cpp:361
void toJsonHex(Json::Value &jv, AnyValue const &v)
Definition Oracle.cpp:335
auto const data
General field definitions, or fields used in multiple transaction namespaces.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
static constexpr std::chrono::seconds epoch_offset
Clock for measuring the network time.
Definition chrono.h:55
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
T size(T... args)
std::optional< AnyValue > uri
Definition Oracle.h:69
std::optional< AnyValue > documentID
Definition Oracle.h:65
std::optional< AnyValue > lastUpdateTime
Definition Oracle.h:70
std::optional< AnyValue > assetClass
Definition Oracle.h:67
std::optional< AccountID > owner
Definition Oracle.h:64
std::optional< ter > err
Definition Oracle.h:75
std::optional< jtx::seq > seq
Definition Oracle.h:73
std::optional< jtx::msig > msig
Definition Oracle.h:72
std::optional< AnyValue > provider
Definition Oracle.h:68
std::optional< AccountID > const & owner
Definition Oracle.h:98
std::optional< AnyValue > const & documentID
Definition Oracle.h:99
std::optional< ter > const & err
Definition Oracle.h:104
std::optional< jtx::msig > const & msig
Definition Oracle.h:101
std::optional< jtx::seq > seq
Definition Oracle.h:102
std::optional< AnyValue > uri
Definition Oracle.h:87
std::optional< AnyValue > assetClass
Definition Oracle.h:85
std::optional< AnyValue > documentID
Definition Oracle.h:83
std::optional< AnyValue > lastUpdateTime
Definition Oracle.h:88
std::optional< AccountID > owner
Definition Oracle.h:82
std::optional< AnyValue > provider
Definition Oracle.h:86
std::optional< jtx::msig > msig
Definition Oracle.h:90
std::optional< jtx::seq > seq
Definition Oracle.h:91
std::optional< ter > err
Definition Oracle.h:93
Set the sequence number on a JTx.
Definition seq.h:34
T to_string(T... args)
T visit(T... args)