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