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 &&
200 std::holds_alternative<std::uint32_t>(*arg.documentID))
201 {
202 documentID_ = std::get<std::uint32_t>(*arg.documentID);
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 {
228 if (std::holds_alternative<std::uint32_t>(*arg.lastUpdateTime))
229 jv[jss::LastUpdateTime] = to_string(
231 std::get<std::uint32_t>(*arg.lastUpdateTime));
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 {
296 if (std::holds_alternative<AccountID>(*account))
297 jvParams[jss::oracle][jss::account] =
298 to_string(std::get<AccountID>(*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::result) && jr[jss::result].isMember(jss::status))
321 return jr[jss::result];
322 else if (jr.isMember(jss::error))
323 return jr;
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) {
339 if constexpr (std::is_same_v<T, std::string const&>)
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:148
Value & append(Value const &value)
Append value to array at the end.
Definition: json_value.cpp:897
UInt asUInt() const
Definition: json_value.cpp:551
bool isNumeric() const
time_point now() const override
Returns the current time.
A transaction testing environment.
Definition: Env.h:120
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:330
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:117
ManualTimeKeeper & timeKeeper()
Definition: Env.h:272
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:769
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:221
Set a multisignature on a JTx.
Definition: multisign.h:66
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)
@ nullValue
'null' value
Definition: json_value.h:37
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
Definition: Indexes.cpp:512
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
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
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)