rippled
Loading...
Searching...
No Matches
Oracle_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2023 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
23namespace ripple {
24namespace test {
25namespace jtx {
26namespace oracle {
27
29{
30private:
31 void
33 {
34 testcase("Invalid Set");
35
36 using namespace jtx;
37 Account const owner("owner");
38
39 {
40 // Invalid account
41 Env env(*this);
42 Account const bad("bad");
43 env.memoize(bad);
44 Oracle oracle(
45 env, {.owner = bad, .seq = seq(1), .err = ter(terNO_ACCOUNT)});
46 }
47
48 // Insufficient reserve
49 {
50 Env env(*this);
51 env.fund(env.current()->fees().accountReserve(0), owner);
52 Oracle oracle(
53 env, {.owner = owner, .err = ter(tecINSUFFICIENT_RESERVE)});
54 }
55 // Insufficient reserve if the data series extends to greater than 5
56 {
57 Env env(*this);
58 env.fund(
59 env.current()->fees().accountReserve(1) +
60 env.current()->fees().base * 2,
61 owner);
62 Oracle oracle(env, {.owner = owner});
63 BEAST_EXPECT(oracle.exists());
64 oracle.set(UpdateArg{
65 .series =
66 {
67 {"XRP", "EUR", 740, 1},
68 {"XRP", "GBP", 740, 1},
69 {"XRP", "CNY", 740, 1},
70 {"XRP", "CAD", 740, 1},
71 {"XRP", "AUD", 740, 1},
72 },
74 }
75
76 {
77 Env env(*this);
78 env.fund(XRP(1'000), owner);
79 Oracle oracle(env, {.owner = owner}, false);
80
81 // Invalid flag
82 oracle.set(
84
85 // Duplicate token pair
86 oracle.set(CreateArg{
87 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}},
88 .err = ter(temMALFORMED)});
89
90 // Price is not included
91 oracle.set(CreateArg{
92 .series =
93 {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
94 .err = ter(temMALFORMED)});
95
96 // Token pair is in update and delete
97 oracle.set(CreateArg{
98 .series =
99 {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}},
100 .err = ter(temMALFORMED)});
101 // Token pair is in add and delete
102 oracle.set(CreateArg{
103 .series =
104 {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
105 .err = ter(temMALFORMED)});
106
107 // Array of token pair is 0 or exceeds 10
108 oracle.set(CreateArg{
109 .series =
110 {{"XRP", "US1", 740, 1},
111 {"XRP", "US2", 750, 1},
112 {"XRP", "US3", 740, 1},
113 {"XRP", "US4", 750, 1},
114 {"XRP", "US5", 740, 1},
115 {"XRP", "US6", 750, 1},
116 {"XRP", "US7", 740, 1},
117 {"XRP", "US8", 750, 1},
118 {"XRP", "US9", 740, 1},
119 {"XRP", "U10", 750, 1},
120 {"XRP", "U11", 740, 1}},
121 .err = ter(temARRAY_TOO_LARGE)});
122 oracle.set(CreateArg{.series = {}, .err = ter(temARRAY_EMPTY)});
123 }
124
125 // Array of token pair exceeds 10 after update
126 {
127 Env env{*this};
128 env.fund(XRP(1'000), owner);
129
130 Oracle oracle(
131 env,
132 CreateArg{
133 .owner = owner, .series = {{{"XRP", "USD", 740, 1}}}});
134 oracle.set(UpdateArg{
135 .series =
136 {
137 {"XRP", "US1", 740, 1},
138 {"XRP", "US2", 750, 1},
139 {"XRP", "US3", 740, 1},
140 {"XRP", "US4", 750, 1},
141 {"XRP", "US5", 740, 1},
142 {"XRP", "US6", 750, 1},
143 {"XRP", "US7", 740, 1},
144 {"XRP", "US8", 750, 1},
145 {"XRP", "US9", 740, 1},
146 {"XRP", "U10", 750, 1},
147 },
148 .err = ter(tecARRAY_TOO_LARGE)});
149 }
150
151 {
152 Env env(*this);
153 env.fund(XRP(1'000), owner);
154 Oracle oracle(env, {.owner = owner}, false);
155
156 // Asset class or provider not included on create
157 oracle.set(CreateArg{
158 .assetClass = std::nullopt,
159 .provider = "provider",
160 .err = ter(temMALFORMED)});
161 oracle.set(CreateArg{
162 .assetClass = "currency",
163 .provider = std::nullopt,
164 .uri = "URI",
165 .err = ter(temMALFORMED)});
166
167 // Asset class or provider are included on update
168 // and don't match the current values
169 oracle.set(CreateArg{});
170 BEAST_EXPECT(oracle.exists());
171 oracle.set(UpdateArg{
172 .series = {{"XRP", "USD", 740, 1}},
173 .provider = "provider1",
174 .err = ter(temMALFORMED)});
175 oracle.set(UpdateArg{
176 .series = {{"XRP", "USD", 740, 1}},
177 .assetClass = "currency1",
178 .err = ter(temMALFORMED)});
179 }
180
181 {
182 Env env(*this);
183 env.fund(XRP(1'000), owner);
184 Oracle oracle(env, {.owner = owner}, false);
185
186 // Fields too long
187 // Asset class
188 std::string assetClass(17, '0');
189 oracle.set(
190 CreateArg{.assetClass = assetClass, .err = ter(temMALFORMED)});
191 // provider
192 std::string const large(257, '0');
193 oracle.set(CreateArg{.provider = large, .err = ter(temMALFORMED)});
194 // URI
195 oracle.set(CreateArg{.uri = large, .err = ter(temMALFORMED)});
196 // Empty field
197 // Asset class
198 oracle.set(CreateArg{.assetClass = "", .err = ter(temMALFORMED)});
199 // provider
200 oracle.set(CreateArg{.provider = "", .err = ter(temMALFORMED)});
201 // URI
202 oracle.set(CreateArg{.uri = "", .err = ter(temMALFORMED)});
203 }
204
205 {
206 // Different owner creates a new object and fails because
207 // of missing fields currency/provider
208 Env env(*this);
209 Account const some("some");
210 env.fund(XRP(1'000), owner);
211 env.fund(XRP(1'000), some);
212 Oracle oracle(env, {.owner = owner});
213 BEAST_EXPECT(oracle.exists());
214 oracle.set(UpdateArg{
215 .owner = some,
216 .series = {{"XRP", "USD", 740, 1}},
217 .err = ter(temMALFORMED)});
218 }
219
220 {
221 // Invalid update time
222 using namespace std::chrono;
223 Env env(*this);
224 auto closeTime = [&]() {
225 return duration_cast<seconds>(
226 env.current()->info().closeTime.time_since_epoch() -
227 10'000s)
228 .count();
229 };
230 env.fund(XRP(1'000), owner);
231 Oracle oracle(env, {.owner = owner});
232 BEAST_EXPECT(oracle.exists());
233 env.close(seconds(400));
234 // Less than the last close time - 300s
235 oracle.set(UpdateArg{
236 .series = {{"XRP", "USD", 740, 1}},
237 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() - 301),
238 .err = ter(tecINVALID_UPDATE_TIME)});
239 // Greater than last close time + 300s
240 oracle.set(UpdateArg{
241 .series = {{"XRP", "USD", 740, 1}},
242 .lastUpdateTime = static_cast<std::uint32_t>(closeTime() + 311),
243 .err = ter(tecINVALID_UPDATE_TIME)});
244 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 1}}});
245 BEAST_EXPECT(oracle.expectLastUpdateTime(
246 static_cast<std::uint32_t>(testStartTime.count() + 450)));
247 // Less than the previous lastUpdateTime
248 oracle.set(UpdateArg{
249 .series = {{"XRP", "USD", 740, 1}},
250 .lastUpdateTime = static_cast<std::uint32_t>(449),
251 .err = ter(tecINVALID_UPDATE_TIME)});
252 // Less than the epoch time
253 oracle.set(UpdateArg{
254 .series = {{"XRP", "USD", 740, 1}},
255 .lastUpdateTime = static_cast<int>(epoch_offset.count() - 1),
256 .err = ter(tecINVALID_UPDATE_TIME)});
257 }
258
259 {
260 // delete token pair that doesn't exist
261 Env env(*this);
262 env.fund(XRP(1'000), owner);
263 Oracle oracle(env, {.owner = owner});
264 BEAST_EXPECT(oracle.exists());
265 oracle.set(UpdateArg{
266 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
268 // delete all token pairs
269 oracle.set(UpdateArg{
270 .series = {{"XRP", "USD", std::nullopt, std::nullopt}},
271 .err = ter(tecARRAY_EMPTY)});
272 }
273
274 {
275 // same BaseAsset and QuoteAsset
276 Env env(*this);
277 env.fund(XRP(1'000), owner);
278 Oracle oracle(
279 env,
280 {.owner = owner,
281 .series = {{"USD", "USD", 740, 1}},
282 .err = ter(temMALFORMED)});
283 }
284
285 {
286 // Scale is greater than maxPriceScale
287 Env env(*this);
288 env.fund(XRP(1'000), owner);
289 Oracle oracle(
290 env,
291 {.owner = owner,
292 .series = {{"USD", "BTC", 740, maxPriceScale + 1}},
293 .err = ter(temMALFORMED)});
294 }
295
296 {
297 // Updating token pair to add and delete
298 Env env(*this);
299 env.fund(XRP(1'000), owner);
300 Oracle oracle(env, {.owner = owner});
301 oracle.set(UpdateArg{
302 .series =
303 {{"XRP", "EUR", std::nullopt, std::nullopt},
304 {"XRP", "EUR", 740, 1}},
305 .err = ter(temMALFORMED)});
306 // Delete token pair that doesn't exist in this oracle
307 oracle.set(UpdateArg{
308 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
310 // Delete token pair in oracle, which is not in the ledger
311 oracle.set(UpdateArg{
312 .documentID = 10,
313 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
314 .err = ter(temMALFORMED)});
315 }
316
317 {
318 // Bad fee
319 Env env(*this);
320 env.fund(XRP(1'000), owner);
321 Oracle oracle(
322 env, {.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
323 Oracle oracle1(env, {.owner = owner});
324 oracle.set(
325 UpdateArg{.owner = owner, .fee = -1, .err = ter(temBAD_FEE)});
326 }
327 }
328
329 void
331 {
332 testcase("Create");
333 using namespace jtx;
334 Account const owner("owner");
335
336 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
337 env.fund(XRP(1'000), owner);
338 auto const count = ownerCount(env, owner);
339 Oracle oracle(env, {.owner = owner, .series = series});
340 BEAST_EXPECT(oracle.exists());
341 BEAST_EXPECT(ownerCount(env, owner) == (count + adj));
342 BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
343 };
344
345 {
346 // owner count is adjusted by 1
347 Env env(*this);
348 test(env, {{"XRP", "USD", 740, 1}}, 1);
349 }
350
351 {
352 // owner count is adjusted by 2
353 Env env(*this);
354 test(
355 env,
356 {{"XRP", "USD", 740, 1},
357 {"BTC", "USD", 740, 1},
358 {"ETH", "USD", 740, 1},
359 {"CAN", "USD", 740, 1},
360 {"YAN", "USD", 740, 1},
361 {"GBP", "USD", 740, 1}},
362 2);
363 }
364
365 {
366 // Different owner creates a new object
367 Env env(*this);
368 Account const some("some");
369 env.fund(XRP(1'000), owner);
370 env.fund(XRP(1'000), some);
371 Oracle oracle(env, {.owner = owner});
372 BEAST_EXPECT(oracle.exists());
373 oracle.set(CreateArg{
374 .owner = some, .series = {{"912810RR9", "USD", 740, 1}}});
375 BEAST_EXPECT(Oracle::exists(env, some, oracle.documentID()));
376 }
377 }
378
379 void
381 {
382 testcase("Invalid Delete");
383
384 using namespace jtx;
385 Env env(*this);
386 Account const owner("owner");
387 env.fund(XRP(1'000), owner);
388 Oracle oracle(env, {.owner = owner});
389 BEAST_EXPECT(oracle.exists());
390
391 {
392 // Invalid account
393 Account const bad("bad");
394 env.memoize(bad);
395 oracle.remove(
396 {.owner = bad, .seq = seq(1), .err = ter(terNO_ACCOUNT)});
397 }
398
399 // Invalid DocumentID
400 oracle.remove({.documentID = 2, .err = ter(tecNO_ENTRY)});
401
402 // Invalid owner
403 Account const invalid("invalid");
404 env.fund(XRP(1'000), invalid);
405 oracle.remove({.owner = invalid, .err = ter(tecNO_ENTRY)});
406
407 // Invalid flags
408 oracle.remove({.flags = tfSellNFToken, .err = ter(temINVALID_FLAG)});
409
410 // Bad fee
411 oracle.remove({.fee = -1, .err = ter(temBAD_FEE)});
412 }
413
414 void
416 {
417 testcase("Delete");
418 using namespace jtx;
419 Account const owner("owner");
420
421 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
422 env.fund(XRP(1'000), owner);
423 Oracle oracle(env, {.owner = owner, .series = series});
424 auto const count = ownerCount(env, owner);
425 BEAST_EXPECT(oracle.exists());
426 oracle.remove({});
427 BEAST_EXPECT(!oracle.exists());
428 BEAST_EXPECT(ownerCount(env, owner) == (count - adj));
429 };
430
431 {
432 // owner count is adjusted by 1
433 Env env(*this);
434 test(env, {{"XRP", "USD", 740, 1}}, 1);
435 }
436
437 {
438 // owner count is adjusted by 2
439 Env env(*this);
440 test(
441 env,
442 {
443 {"XRP", "USD", 740, 1},
444 {"BTC", "USD", 740, 1},
445 {"ETH", "USD", 740, 1},
446 {"CAN", "USD", 740, 1},
447 {"YAN", "USD", 740, 1},
448 {"GBP", "USD", 740, 1},
449 },
450 2);
451 }
452
453 {
454 // deleting the account deletes the oracles
455 Env env(*this);
456 auto const alice = Account("alice");
457 auto const acctDelFee{drops(env.current()->fees().increment)};
458 env.fund(XRP(1'000), owner);
459 env.fund(XRP(1'000), alice);
460 Oracle oracle(
461 env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}});
462 Oracle oracle1(
463 env,
464 {.owner = owner,
465 .documentID = 2,
466 .series = {{"XRP", "EUR", 740, 1}}});
467 BEAST_EXPECT(ownerCount(env, owner) == 2);
468 BEAST_EXPECT(oracle.exists());
469 BEAST_EXPECT(oracle1.exists());
470 auto const index = env.closed()->seq();
471 auto const hash = env.closed()->info().hash;
472 for (int i = 0; i < 256; ++i)
473 env.close();
474 env(acctdelete(owner, alice), fee(acctDelFee));
475 env.close();
476 BEAST_EXPECT(!oracle.exists());
477 BEAST_EXPECT(!oracle1.exists());
478
479 // can still get the oracles via the ledger index or hash
480 auto verifyLedgerData = [&](auto const& field, auto const& value) {
481 Json::Value jvParams;
482 jvParams[field] = value;
483 jvParams[jss::binary] = false;
484 jvParams[jss::type] = jss::oracle;
485 Json::Value jrr = env.rpc(
486 "json",
487 "ledger_data",
488 boost::lexical_cast<std::string>(jvParams));
489 BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2);
490 };
491 verifyLedgerData(jss::ledger_index, index);
492 verifyLedgerData(jss::ledger_hash, to_string(hash));
493 }
494 }
495
496 void
498 {
499 testcase("Update");
500 using namespace jtx;
501 Account const owner("owner");
502
503 {
504 Env env(*this);
505 env.fund(XRP(1'000), owner);
506 auto count = ownerCount(env, owner);
507 Oracle oracle(env, {.owner = owner});
508 BEAST_EXPECT(oracle.exists());
509
510 // update existing pair
511 oracle.set(UpdateArg{.series = {{"XRP", "USD", 740, 2}}});
512 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 740, 2}}));
513 // owner count is increased by 1 since the oracle object is added
514 // with one token pair
515 count += 1;
516 BEAST_EXPECT(ownerCount(env, owner) == count);
517
518 // add new pairs, not-included pair is reset
519 oracle.set(UpdateArg{.series = {{"XRP", "EUR", 700, 2}}});
520 BEAST_EXPECT(oracle.expectPrice(
521 {{"XRP", "USD", 0, 0}, {"XRP", "EUR", 700, 2}}));
522 // owner count is not changed since the number of pairs is 2
523 BEAST_EXPECT(ownerCount(env, owner) == count);
524
525 // update both pairs
526 oracle.set(UpdateArg{
527 .series = {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}});
528 BEAST_EXPECT(oracle.expectPrice(
529 {{"XRP", "USD", 741, 2}, {"XRP", "EUR", 710, 2}}));
530 // owner count is not changed since the number of pairs is 2
531 BEAST_EXPECT(ownerCount(env, owner) == count);
532
533 // owner count is increased by 1 since the number of pairs is 6
534 oracle.set(UpdateArg{
535 .series = {
536 {"BTC", "USD", 741, 2},
537 {"ETH", "EUR", 710, 2},
538 {"YAN", "EUR", 710, 2},
539 {"CAN", "EUR", 710, 2},
540 }});
541 count += 1;
542 BEAST_EXPECT(ownerCount(env, owner) == count);
543
544 // update two pairs and delete four
545 oracle.set(UpdateArg{
546 .series = {{"BTC", "USD", std::nullopt, std::nullopt}}});
547 oracle.set(UpdateArg{
548 .series = {
549 {"XRP", "USD", 742, 2},
550 {"XRP", "EUR", 711, 2},
551 {"ETH", "EUR", std::nullopt, std::nullopt},
552 {"YAN", "EUR", std::nullopt, std::nullopt},
553 {"CAN", "EUR", std::nullopt, std::nullopt}}});
554 BEAST_EXPECT(oracle.expectPrice(
555 {{"XRP", "USD", 742, 2}, {"XRP", "EUR", 711, 2}}));
556 // owner count is decreased by 1 since the number of pairs is 2
557 count -= 1;
558 BEAST_EXPECT(ownerCount(env, owner) == count);
559 }
560
561 // Min reserve to create and update
562 {
563 Env env(*this);
564 env.fund(
565 env.current()->fees().accountReserve(1) +
566 env.current()->fees().base * 2,
567 owner);
568 Oracle oracle(env, {.owner = owner});
569 oracle.set(UpdateArg{.series = {{"XRP", "USD", 742, 2}}});
570 }
571 }
572
573 void
575 {
576 testcase("Multisig");
577 using namespace jtx;
578 Oracle::setFee(100'000);
579
580 Env env(*this, features);
581 Account const alice{"alice", KeyType::secp256k1};
582 Account const bogie{"bogie", KeyType::secp256k1};
583 Account const ed{"ed", KeyType::secp256k1};
584 Account const becky{"becky", KeyType::ed25519};
585 Account const zelda{"zelda", KeyType::secp256k1};
586 Account const bob{"bob", KeyType::secp256k1};
587 env.fund(XRP(10'000), alice, becky, zelda, ed, bob);
588
589 // alice uses a regular key with the master disabled.
590 Account const alie{"alie", KeyType::secp256k1};
591 env(regkey(alice, alie));
592 env(fset(alice, asfDisableMaster), sig(alice));
593
594 // Attach signers to alice.
595 env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}), sig(alie));
596 env.close();
597 // if multiSignReserve disabled then its 2 + 1 per signer
598 int const signerListOwners{features[featureMultiSignReserve] ? 1 : 5};
599 env.require(owners(alice, signerListOwners));
600
601 // Create
602 // Force close (true) and time advancement because the close time
603 // is no longer 0.
604 Oracle oracle(env, CreateArg{.owner = alice, .close = true}, false);
605 oracle.set(CreateArg{.msig = msig(becky), .err = ter(tefBAD_QUORUM)});
606 oracle.set(
607 CreateArg{.msig = msig(zelda), .err = ter(tefBAD_SIGNATURE)});
608 oracle.set(CreateArg{.msig = msig(becky, bogie)});
609 BEAST_EXPECT(oracle.exists());
610
611 // Update
612 oracle.set(UpdateArg{
613 .series = {{"XRP", "USD", 740, 1}},
614 .msig = msig(becky),
615 .err = ter(tefBAD_QUORUM)});
616 oracle.set(UpdateArg{
617 .series = {{"XRP", "USD", 740, 1}},
618 .msig = msig(zelda),
619 .err = ter(tefBAD_SIGNATURE)});
620 oracle.set(UpdateArg{
621 .series = {{"XRP", "USD", 741, 1}}, .msig = msig(becky, bogie)});
622 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 741, 1}}));
623 // remove the signer list
624 env(signers(alice, jtx::none), sig(alie));
625 env.close();
626 env.require(owners(alice, 1));
627 // create new signer list
628 env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie));
629 env.close();
630 // old list fails
631 oracle.set(UpdateArg{
632 .series = {{"XRP", "USD", 740, 1}},
633 .msig = msig(becky, bogie),
634 .err = ter(tefBAD_SIGNATURE)});
635 // updated list succeeds
636 oracle.set(UpdateArg{
637 .series = {{"XRP", "USD", 7412, 2}}, .msig = msig(zelda, bob)});
638 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 7412, 2}}));
639 oracle.set(
640 UpdateArg{.series = {{"XRP", "USD", 74245, 3}}, .msig = msig(ed)});
641 BEAST_EXPECT(oracle.expectPrice({{"XRP", "USD", 74245, 3}}));
642
643 // Remove
644 oracle.remove({.msig = msig(bob), .err = ter(tefBAD_QUORUM)});
645 oracle.remove({.msig = msig(becky), .err = ter(tefBAD_SIGNATURE)});
646 oracle.remove({.msig = msig(ed)});
647 BEAST_EXPECT(!oracle.exists());
648 }
649
650 void
652 {
653 testcase("Amendment");
654 using namespace jtx;
655
656 auto const features = supported_amendments() - featurePriceOracle;
657 Account const owner("owner");
658 Env env(*this, features);
659
660 env.fund(XRP(1'000), owner);
661 {
662 Oracle oracle(env, {.owner = owner, .err = ter(temDISABLED)});
663 }
664
665 {
666 Oracle oracle(env, {.owner = owner}, false);
667 oracle.remove({.err = ter(temDISABLED)});
668 }
669 }
670
671public:
672 void
673 run() override
674 {
675 using namespace jtx;
676 auto const all = supported_amendments();
677 testInvalidSet();
678 testInvalidDelete();
679 testCreate();
680 testDelete();
681 testUpdate();
682 testAmendment();
683 for (auto const& features :
684 {all,
685 all - featureMultiSignReserve - featureExpandedSignerList,
686 all - featureExpandedSignerList})
687 testMultisig(features);
688 }
689};
690
691BEAST_DEFINE_TESTSUITE(Oracle, app, ripple);
692
693} // namespace oracle
694
695} // namespace jtx
696
697} // namespace test
698
699} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
A testsuite class.
Definition: suite.h:55
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
Immutable cryptographic account descriptor.
Definition: Account.h:39
A transaction testing environment.
Definition: Env.h:118
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition: Env.cpp:109
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:530
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
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
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
void memoize(Account const &account)
Associate AccountID with account.
Definition: Env.cpp:150
Set the fee on a JTx.
Definition: fee.h:36
Set a multisignature on a JTx.
Definition: multisign.h:66
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition: Oracle.h:120
Match the number of items in the account's owner directory.
Definition: owners.h:71
Set the regular signature on a JTx.
Definition: sig.h:35
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
T count(T... args)
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
std::uint32_t ownerCount(Env const &env, Account const &account)
Definition: TestHelpers.cpp:53
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition: regkey.cpp:28
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Definition: acctdelete.cpp:29
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
FeatureBitset supported_amendments()
Definition: Env.h:71
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::size_t constexpr maxPriceScale
The maximum price scaling factor.
Definition: Protocol.h:152
constexpr std::uint32_t const tfSellNFToken
Definition: TxFlags.h:189
constexpr std::uint32_t asfDisableMaster
Definition: TxFlags.h:79
@ tefBAD_QUORUM
Definition: TER.h:180
@ tefBAD_SIGNATURE
Definition: TER.h:179
@ invalid
Timely, but invalid signature.
@ tecNO_ENTRY
Definition: TER.h:293
@ tecARRAY_TOO_LARGE
Definition: TER.h:344
@ tecINVALID_UPDATE_TIME
Definition: TER.h:341
@ tecINSUFFICIENT_RESERVE
Definition: TER.h:294
@ tecARRAY_EMPTY
Definition: TER.h:343
@ tecTOKEN_PAIR_NOT_FOUND
Definition: TER.h:342
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
@ terNO_ACCOUNT
Definition: TER.h:217
@ temBAD_FEE
Definition: TER.h:92
@ temMALFORMED
Definition: TER.h:87
@ temINVALID_FLAG
Definition: TER.h:111
@ temARRAY_EMPTY
Definition: TER.h:140
@ temARRAY_TOO_LARGE
Definition: TER.h:141
@ temDISABLED
Definition: TER.h:114
std::optional< AnyValue > uri
Definition: Oracle.h:69
std::optional< AnyValue > assetClass
Definition: Oracle.h:67
std::optional< AccountID > owner
Definition: Oracle.h:64
std::optional< jtx::msig > msig
Definition: Oracle.h:72
std::optional< AnyValue > provider
Definition: Oracle.h:68
void run() override
Runs the suite.
void testMultisig(FeatureBitset features)
std::optional< AnyValue > documentID
Definition: Oracle.h:83
std::optional< AccountID > owner
Definition: Oracle.h:82
Set the sequence number on a JTx.
Definition: seq.h:34