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