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