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#include <xrpl/protocol/jss.h>
22
23#include <boost/lexical_cast/try_lexical_convert.hpp>
24#include <boost/regex.hpp>
25
26#include <vector>
27
28namespace ripple {
29namespace test {
30namespace jtx {
31namespace oracle {
32
33Oracle::Oracle(Env& env, CreateArg const& arg, bool submit)
34 : env_(env), owner_{}, documentID_{}
35{
36 // LastUpdateTime is checked to be in range
37 // {close-maxLastUpdateTimeDelta, close+maxLastUpdateTimeDelta}.
38 // To make the validation work and to make the clock consistent
39 // for tests running at different time, simulate Unix time starting
40 // on testStartTime since Ripple epoch.
41 auto const now = env_.timeKeeper().now();
42 if (now.time_since_epoch().count() == 0 || arg.close)
44 if (arg.owner)
45 owner_ = *arg.owner;
46 if (arg.documentID && validDocumentID(*arg.documentID))
48 if (submit)
49 set(arg);
50}
51
52void
54{
55 Json::Value jv;
56 jv[jss::TransactionType] = jss::OracleDelete;
57 jv[jss::Account] = to_string(arg.owner.value_or(owner_));
58 toJson(jv[jss::OracleDocumentID], arg.documentID.value_or(documentID_));
59 if (Oracle::fee != 0)
60 jv[jss::Fee] = std::to_string(Oracle::fee);
61 else if (arg.fee != 0)
62 jv[jss::Fee] = std::to_string(arg.fee);
63 else
64 jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops());
65 if (arg.flags != 0)
66 jv[jss::Flags] = arg.flags;
67 submit(jv, arg.msig, arg.seq, arg.err);
68}
69
70void
72 Json::Value const& jv,
75 std::optional<ter> const& err)
76{
77 if (msig)
78 {
79 if (seq && err)
80 env_(jv, *msig, *seq, *err);
81 else if (seq)
82 env_(jv, *msig, *seq);
83 else if (err)
84 env_(jv, *msig, *err);
85 else
86 env_(jv, *msig);
87 }
88 else if (seq && err)
89 env_(jv, *seq, *err);
90 else if (seq)
91 env_(jv, *seq);
92 else if (err)
93 env_(jv, *err);
94 else
95 env_(jv);
96 env_.close();
97}
98
99bool
100Oracle::exists(Env& env, AccountID const& account, std::uint32_t documentID)
101{
102 assert(account.isNonZero());
103 return env.le(keylet::oracle(account, documentID)) != nullptr;
104}
105
106bool
107Oracle::expectPrice(DataSeries const& series) const
108{
109 if (auto const sle = env_.le(keylet::oracle(owner_, documentID_)))
110 {
111 auto const& leSeries = sle->getFieldArray(sfPriceDataSeries);
112 if (leSeries.size() == 0 || leSeries.size() != series.size())
113 return false;
114 for (auto const& data : series)
115 {
116 if (std::find_if(
117 leSeries.begin(),
118 leSeries.end(),
119 [&](STObject const& o) -> bool {
120 auto const& baseAsset = o.getFieldCurrency(sfBaseAsset);
121 auto const& quoteAsset =
122 o.getFieldCurrency(sfQuoteAsset);
123 auto const& price = o.getFieldU64(sfAssetPrice);
124 auto const& scale = o.getFieldU8(sfScale);
125 return baseAsset.getText() == std::get<0>(data) &&
126 quoteAsset.getText() == std::get<1>(data) &&
127 price == std::get<2>(data) &&
128 scale == std::get<3>(data);
129 }) == leSeries.end())
130 return false;
131 }
132 return true;
133 }
134 return false;
135}
136
137bool
139{
140 auto const sle = env_.le(keylet::oracle(owner_, documentID_));
141 return sle && (*sle)[sfLastUpdateTime] == lastUpdateTime;
142}
143
146 Env& env,
147 std::optional<AnyValue> const& baseAsset,
148 std::optional<AnyValue> const& quoteAsset,
149 std::optional<OraclesData> const& oracles,
150 std::optional<AnyValue> const& trim,
151 std::optional<AnyValue> const& timeThreshold)
152{
153 Json::Value jv;
154 Json::Value jvOracles(Json::arrayValue);
155 if (oracles)
156 {
157 for (auto const& id : *oracles)
158 {
159 Json::Value oracle;
160 if (id.first)
161 oracle[jss::account] = to_string((*id.first).id());
162 if (id.second)
163 toJson(oracle[jss::oracle_document_id], *id.second);
164 jvOracles.append(oracle);
165 }
166 jv[jss::oracles] = jvOracles;
167 }
168 if (trim)
169 toJson(jv[jss::trim], *trim);
170 if (baseAsset)
171 toJson(jv[jss::base_asset], *baseAsset);
172 if (quoteAsset)
173 toJson(jv[jss::quote_asset], *quoteAsset);
174 if (timeThreshold)
175 toJson(jv[jss::time_threshold], *timeThreshold);
176 // Convert "%None%" to None
177 auto str = to_string(jv);
178 str = boost::regex_replace(str, boost::regex(NonePattern), UnquotedNone);
179 auto jr = env.rpc("json", "get_aggregate_price", str);
180
181 if (jr.isObject())
182 {
183 if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
184 return jr[jss::result];
185 else if (jr.isMember(jss::error))
186 return jr;
187 }
188 return Json::nullValue;
189}
190
191void
193{
194 using namespace std::chrono;
195 Json::Value jv;
196 if (arg.owner)
197 owner_ = *arg.owner;
198 if (arg.documentID &&
199 std::holds_alternative<std::uint32_t>(*arg.documentID))
200 {
201 documentID_ = std::get<std::uint32_t>(*arg.documentID);
202 jv[jss::OracleDocumentID] = documentID_;
203 }
204 else if (arg.documentID)
205 toJson(jv[jss::OracleDocumentID], *arg.documentID);
206 else
207 jv[jss::OracleDocumentID] = documentID_;
208 jv[jss::TransactionType] = jss::OracleSet;
209 jv[jss::Account] = to_string(owner_);
210 if (arg.assetClass)
211 toJsonHex(jv[jss::AssetClass], *arg.assetClass);
212 if (arg.provider)
213 toJsonHex(jv[jss::Provider], *arg.provider);
214 if (arg.uri)
215 toJsonHex(jv[jss::URI], *arg.uri);
216 if (arg.flags != 0)
217 jv[jss::Flags] = arg.flags;
218 if (Oracle::fee != 0)
219 jv[jss::Fee] = std::to_string(Oracle::fee);
220 else if (arg.fee != 0)
221 jv[jss::Fee] = std::to_string(arg.fee);
222 else
223 jv[jss::Fee] = std::to_string(env_.current()->fees().increment.drops());
224 // lastUpdateTime if provided is offset from testStartTime
225 if (arg.lastUpdateTime)
226 {
227 if (std::holds_alternative<std::uint32_t>(*arg.lastUpdateTime))
228 jv[jss::LastUpdateTime] = to_string(
230 std::get<std::uint32_t>(*arg.lastUpdateTime));
231 else
232 toJson(jv[jss::LastUpdateTime], *arg.lastUpdateTime);
233 }
234 else
235 jv[jss::LastUpdateTime] = to_string(
236 duration_cast<seconds>(
237 env_.current()->info().closeTime.time_since_epoch())
238 .count() +
240 Json::Value dataSeries(Json::arrayValue);
241 auto assetToStr = [](std::string const& s) {
242 // assume standard currency
243 if (s.size() == 3)
244 return s;
245 assert(s.size() <= 20);
246 // anything else must be 160-bit hex string
247 std::string h = strHex(s);
248 return strHex(s).append(40 - s.size() * 2, '0');
249 };
250 for (auto const& data : arg.series)
251 {
252 Json::Value priceData;
253 Json::Value price;
254 price[jss::BaseAsset] = assetToStr(std::get<0>(data));
255 price[jss::QuoteAsset] = assetToStr(std::get<1>(data));
256 if (std::get<2>(data))
257 price[jss::AssetPrice] = *std::get<2>(data);
258 if (std::get<3>(data))
259 price[jss::Scale] = *std::get<3>(data);
260 priceData[jss::PriceData] = price;
261 dataSeries.append(priceData);
262 }
263 jv[jss::PriceDataSeries] = dataSeries;
264 submit(jv, arg.msig, arg.seq, arg.err);
265}
266
267void
269{
271 .owner = arg.owner,
272 .documentID = arg.documentID,
273 .series = arg.series,
274 .assetClass = arg.assetClass,
275 .provider = arg.provider,
276 .uri = arg.uri,
277 .lastUpdateTime = arg.lastUpdateTime,
278 .flags = arg.flags,
279 .msig = arg.msig,
280 .seq = arg.seq,
281 .fee = arg.fee,
282 .err = arg.err});
283}
284
287 Env& env,
289 std::optional<AnyValue> const& documentID,
290 std::optional<std::string> const& index)
291{
292 Json::Value jvParams;
293 if (account)
294 {
295 if (std::holds_alternative<AccountID>(*account))
296 jvParams[jss::oracle][jss::account] =
297 to_string(std::get<AccountID>(*account));
298 else
299 jvParams[jss::oracle][jss::account] =
300 std::get<std::string>(*account);
301 }
302 if (documentID)
303 toJson(jvParams[jss::oracle][jss::oracle_document_id], *documentID);
304 if (index)
305 {
307 if (boost::conversion::try_lexical_convert(*index, i))
308 jvParams[jss::oracle][jss::ledger_index] = i;
309 else
310 jvParams[jss::oracle][jss::ledger_index] = *index;
311 }
312 // Convert "%None%" to None
313 auto str = to_string(jvParams);
314 str = boost::regex_replace(str, boost::regex(NonePattern), UnquotedNone);
315 auto jr = env.rpc("json", "ledger_entry", str);
316
317 if (jr.isObject())
318 {
319 if (jr.isMember(jss::result) && jr[jss::result].isMember(jss::status))
320 return jr[jss::result];
321 else if (jr.isMember(jss::error))
322 return jr;
323 }
324 return Json::nullValue;
325}
326
327void
329{
330 std::visit([&](auto&& arg) { jv = arg; }, v);
331}
332
333void
335{
337 [&]<typename T>(T&& arg) {
338 if constexpr (std::is_same_v<T, std::string const&>)
339 {
340 if (arg.starts_with("##"))
341 jv = arg.substr(2);
342 else
343 jv = strHex(arg);
344 }
345 else
346 jv = arg;
347 },
348 v);
349}
350
353{
354 Json::Value jv;
355 toJson(jv, v);
356 return jv.asUInt();
357}
358
359bool
361{
362 try
363 {
364 Json::Value jv;
365 toJson(jv, v);
366 jv.asUInt();
367 jv.isNumeric();
368 return true;
369 }
370 catch (...)
371 {
372 }
373 return false;
374}
375
376} // namespace oracle
377} // namespace jtx
378} // namespace test
379} // namespace ripple
T append(T... args)
Represents a JSON value.
Definition: json_value.h:148
UInt asUInt() const
Definition: json_value.cpp:551
Value & append(const Value &value)
Append value to array at the end.
Definition: json_value.cpp:897
bool isNumeric() const
time_point now() const override
Returns the current time.
A transaction testing environment.
Definition: Env.h:118
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:326
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
ManualTimeKeeper & timeKeeper()
Definition: Env.h:268
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:765
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:219
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:145
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:286
void set(CreateArg const &arg)
Definition: Oracle.cpp:268
Oracle(Env &env, CreateArg const &arg, bool submit=true)
Definition: Oracle.cpp:33
void remove(RemoveArg const &arg)
Definition: Oracle.cpp:53
static std::uint32_t fee
Definition: Oracle.h:123
bool expectPrice(DataSeries const &pricess) const
Definition: Oracle.cpp:107
bool expectLastUpdateTime(std::uint32_t lastUpdateTime) const
Definition: Oracle.cpp:138
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:71
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:503
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:352
void toJson(Json::Value &jv, AnyValue const &v)
Definition: Oracle.cpp:328
constexpr char const * NonePattern
Definition: Oracle.h:40
bool validDocumentID(AnyValue const &v)
Definition: Oracle.cpp:360
void toJsonHex(Json::Value &jv, AnyValue const &v)
Definition: Oracle.cpp:334
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)