20 #include <ripple/protocol/jss.h>
21 #include <test/jtx/Oracle.h>
36 if (
auto const sleAcct = env.
le(acct))
44 testcase(
"Invalid Set");
61 env.
fund(env.
current()->fees().accountReserve(0), owner);
69 env.
current()->fees().accountReserve(1) +
72 Oracle oracle(env, {.owner = owner});
73 BEAST_EXPECT(oracle.exists());
77 {
"XRP",
"EUR", 740, 1},
78 {
"XRP",
"GBP", 740, 1},
79 {
"XRP",
"CNY", 740, 1},
80 {
"XRP",
"CAD", 740, 1},
81 {
"XRP",
"AUD", 740, 1},
89 Oracle oracle(env, {.owner = owner}, false);
93 CreateArg{.flags = tfSellNFToken, .err = ter(temINVALID_FLAG)});
95 // Duplicate token pair
97 .series = {{"XRP", "USD", 740, 1}, {"XRP", "USD", 750, 1}},
98 .err = ter(temMALFORMED)});
100 // Price is not included
101 oracle.set(CreateArg{
103 {{"XRP", "USD", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
104 .err = ter(temMALFORMED)});
106 // Token pair is in update and delete
107 oracle.set(CreateArg{
109 {{"XRP", "USD", 740, 1}, {"XRP", "USD", std::nullopt, 1}},
110 .err = ter(temMALFORMED)});
111 // Token pair is in add and delete
112 oracle.set(CreateArg{
114 {{"XRP", "EUR", 740, 1}, {"XRP", "EUR", std::nullopt, 1}},
115 .err = ter(temMALFORMED)});
117 // Array of token pair is 0 or exceeds 10
118 oracle.set(CreateArg{
120 {{"XRP", "US1", 740, 1},
121 {"XRP", "US2", 750, 1},
122 {"XRP", "US3", 740, 1},
123 {"XRP", "US4", 750, 1},
124 {"XRP", "US5", 740, 1},
125 {"XRP", "US6", 750, 1},
126 {"XRP", "US7", 740, 1},
127 {"XRP", "US8", 750, 1},
128 {"XRP", "US9", 740, 1},
129 {"XRP", "U10", 750, 1},
130 {"XRP", "U11", 740, 1}},
131 .err = ter(temARRAY_TOO_LARGE)});
132 oracle.set(CreateArg{.series = {}, .err = ter(temARRAY_EMPTY)});
135 // Array of token pair exceeds 10 after update
138 env.fund(XRP(1'000), owner);
143 .
owner = owner, .series = {{{
"XRP",
"USD", 740, 1}}}});
147 {
"XRP",
"US1", 740, 1},
148 {
"XRP",
"US2", 750, 1},
149 {
"XRP",
"US3", 740, 1},
150 {
"XRP",
"US4", 750, 1},
151 {
"XRP",
"US5", 740, 1},
152 {
"XRP",
"US6", 750, 1},
153 {
"XRP",
"US7", 740, 1},
154 {
"XRP",
"US8", 750, 1},
155 {
"XRP",
"US9", 740, 1},
156 {
"XRP",
"U10", 750, 1},
164 Oracle oracle(env, {.owner = owner}, false);
166 // Symbol class or provider not included on create
167 oracle.set(CreateArg{
168 .assetClass = std::nullopt,
169 .provider = "provider",
170 .err = ter(temMALFORMED)});
171 oracle.set(CreateArg{
172 .assetClass = "currency",
173 .provider = std::nullopt,
175 .err = ter(temMALFORMED)});
177 // Symbol class or provider are included on update
178 // and don't match the
current values
180 BEAST_EXPECT(oracle.exists());
182 .series = {{
"XRP",
"USD", 740, 1}},
186 .
series = {{
"XRP",
"USD", 740, 1}},
187 .assetClass =
"currency1",
193 env.fund(
XRP(1
'000), owner);
194 Oracle oracle(env, {.owner = owner}, false);
198 std::string assetClass(17, '0
');
200 CreateArg{.assetClass = assetClass, .err = ter(temMALFORMED)});
202 std::string const large(257, '0
');
203 oracle.set(CreateArg{.provider = large, .err = ter(temMALFORMED)});
205 oracle.set(CreateArg{.uri = large, .err = ter(temMALFORMED)});
209 // Different owner creates a new object and fails because
210 // of missing fields currency/provider
212 Account const some("some");
213 env.fund(XRP(1'000), owner);
214 env.fund(
XRP(1
'000), some);
215 Oracle oracle(env, {.owner = owner});
216 BEAST_EXPECT(oracle.exists());
217 oracle.set(UpdateArg{
219 .series = {{"XRP", "USD", 740, 1}},
220 .err = ter(temMALFORMED)});
224 // Invalid update time
225 using namespace std::chrono;
227 env.fund(XRP(1'000), owner);
228 Oracle oracle(env, {.owner = owner});
229 BEAST_EXPECT(oracle.exists());
232 oracle.set(UpdateArg{
233 .series = {{
"XRP",
"USD", 740, 1}},
237 oracle.set(UpdateArg{
238 .series = {{
"XRP",
"USD", 740, 1}},
241 oracle.set(UpdateArg{.series = {{
"XRP",
"USD", 740, 1}}});
246 .series = {{
"XRP",
"USD", 740, 1}},
254 env.fund(
XRP(1
'000), owner);
255 Oracle oracle(env, {.owner = owner});
256 BEAST_EXPECT(oracle.exists());
257 oracle.set(UpdateArg{
258 .series = {{"XRP", "EUR", std::nullopt, std::nullopt}},
259 .err = ter(tecTOKEN_PAIR_NOT_FOUND)});
260 // delete all token pairs
261 oracle.set(UpdateArg{
262 .series = {{"XRP", "USD", std::nullopt, std::nullopt}},
263 .err = ter(tecARRAY_EMPTY)});
267 // same BaseAsset and QuoteAsset
269 env.fund(XRP(1'000), owner);
273 .series = {{
"USD",
"USD", 740, 1}},
280 env.fund(
XRP(1
'000), owner);
284 .series = {{"USD", "BTC", 740, maxPriceScale + 1}},
285 .err = ter(temMALFORMED)});
294 Account const owner("owner");
296 auto test = [&](Env& env, DataSeries const& series, std::uint16_t adj) {
297 env.fund(XRP(1'000), owner);
299 Oracle oracle(env, {.owner = owner, .series = series});
300 BEAST_EXPECT(oracle.exists());
301 BEAST_EXPECT(
ownerCount(env, owner) == (count + adj));
302 BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
308 test(env, {{
"XRP",
"USD", 740, 1}}, 1);
316 {{
"XRP",
"USD", 740, 1},
317 {
"BTC",
"USD", 740, 1},
318 {
"ETH",
"USD", 740, 1},
319 {
"CAN",
"USD", 740, 1},
320 {
"YAN",
"USD", 740, 1},
321 {
"GBP",
"USD", 740, 1}},
328 Account
const some(
"some");
329 env.fund(
XRP(1
'000), owner);
330 env.fund(XRP(1'000), some);
331 Oracle
oracle(env, {.owner = owner});
332 BEAST_EXPECT(
oracle.exists());
334 .owner =
some, .series = {{
"912810RR9",
"USD", 740, 1}}});
342 testcase(
"Invalid Delete");
348 Oracle oracle(env, {.owner = owner});
349 BEAST_EXPECT(oracle.exists());
353 Account const bad("bad");
356 {.owner = bad, .seq = seq(1), .err = ter(terNO_ACCOUNT)});
360 oracle.remove({.documentID = 2, .err = ter(tecNO_ENTRY)});
363 Account const invalid("invalid");
377 Oracle oracle(env, {.owner = owner, .series = series});
378 auto const count = ownerCount(env, owner);
379 BEAST_EXPECT(oracle.exists());
381 BEAST_EXPECT(!oracle.exists());
382 BEAST_EXPECT(ownerCount(env, owner) == (count - adj));
386 // owner count is adjusted by 1
388 test(env, {{"XRP", "USD", 740, 1}}, 1);
392 // owner count is adjusted by 2
397 {"XRP", "USD", 740, 1},
398 {"BTC", "USD", 740, 1},
399 {"ETH", "USD", 740, 1},
400 {"CAN", "USD", 740, 1},
401 {"YAN", "USD", 740, 1},
402 {"GBP", "USD", 740, 1},
408 // deleting the account deletes the oracles
410 auto const alice = Account("alice");
411 auto const acctDelFee{drops(env.current()->fees().increment)};
412 env.fund(XRP(1'000), owner);
415 env, {.owner = owner, .series = {{"XRP", "USD", 740, 1}}});
420 .series = {{"XRP", "EUR", 740, 1}}});
421 BEAST_EXPECT(ownerCount(env, owner) == 2);
422 BEAST_EXPECT(oracle.exists());
423 BEAST_EXPECT(oracle1.exists());
424 auto const index = env.closed()->seq();
425 auto const hash = env.closed()->info().hash;
426 for (int i = 0; i < 256; ++i)
428 env(acctdelete(owner, alice), fee(acctDelFee));
430 BEAST_EXPECT(!oracle.exists());
431 BEAST_EXPECT(!oracle1.exists());
433 // can still get the oracles via the ledger index or hash
434 auto verifyLedgerData = [&](auto const& field, auto const& value) {
435 Json::Value jvParams;
436 jvParams[field] = value;
437 jvParams[jss::binary] = false;
438 jvParams[jss::type] = jss::oracle;
439 Json::Value jrr = env.rpc(
442 boost::lexical_cast<std::string>(jvParams));
443 BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2);
445 verifyLedgerData(jss::ledger_index, index);
446 verifyLedgerData(jss::ledger_hash, to_string(hash));
455 Account const owner("owner");
459 env.fund(XRP(1'000), owner);
461 Oracle oracle(env, {.owner = owner});
462 BEAST_EXPECT(oracle.exists());
466 BEAST_EXPECT(oracle.expectPrice({{
"XRP",
"USD", 740, 2}}));
470 BEAST_EXPECT(
ownerCount(env, owner) == count);
473 oracle.set(UpdateArg{.series = {{
"XRP",
"EUR", 700, 2}}});
474 BEAST_EXPECT(
oracle.expectPrice(
475 {{
"XRP",
"USD", 0, 0}, {
"XRP",
"EUR", 700, 2}}));
477 BEAST_EXPECT(
ownerCount(env, owner) == count);
481 .series = {{
"XRP",
"USD", 741, 2}, {
"XRP",
"EUR", 710, 2}}});
482 BEAST_EXPECT(
oracle.expectPrice(
483 {{
"XRP",
"USD", 741, 2}, {
"XRP",
"EUR", 710, 2}}));
485 BEAST_EXPECT(
ownerCount(env, owner) == count);
490 {
"BTC",
"USD", 741, 2},
491 {
"ETH",
"EUR", 710, 2},
492 {
"YAN",
"EUR", 710, 2},
493 {
"CAN",
"EUR", 710, 2},
496 BEAST_EXPECT(
ownerCount(env, owner) == count);
500 .series = {{
"BTC",
"USD", std::nullopt, std::nullopt}}});
503 {
"XRP",
"USD", 742, 2},
504 {
"XRP",
"EUR", 711, 2},
505 {
"ETH",
"EUR", std::nullopt, std::nullopt},
506 {
"YAN",
"EUR", std::nullopt, std::nullopt},
507 {
"CAN",
"EUR", std::nullopt, std::nullopt}}});
508 BEAST_EXPECT(
oracle.expectPrice(
509 {{
"XRP",
"USD", 742, 2}, {
"XRP",
"EUR", 711, 2}}));
512 BEAST_EXPECT(
ownerCount(env, owner) == count);
519 env.
current()->fees().accountReserve(1) +
520 env.
current()->fees().base * 2,
522 Oracle
oracle(env, {.owner = owner});
523 oracle.set(UpdateArg{.series = {{
"XRP",
"USD", 742, 2}}});
530 testcase(
"Multisig");
532 Oracle::setFee(100
'000);
534 Env env(*this, features);
535 Account const alice{"alice", KeyType::secp256k1};
536 Account const bogie{"bogie", KeyType::secp256k1};
537 Account const ed{"ed", KeyType::secp256k1};
538 Account const becky{"becky", KeyType::ed25519};
539 Account const zelda{"zelda", KeyType::secp256k1};
540 Account const bob{"bob", KeyType::secp256k1};
541 env.fund(XRP(10'000), alice, becky, zelda, ed, bob);
549 env(
signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}),
sig(alie));
563 BEAST_EXPECT(oracle.exists());
567 .
series = {{
"XRP",
"USD", 740, 1}},
571 .
series = {{
"XRP",
"USD", 740, 1}},
575 .
series = {{
"XRP",
"USD", 741, 1}}, .msig =
msig(becky, bogie)});
576 BEAST_EXPECT(oracle.expectPrice({{
"XRP",
"USD", 741, 1}}));
582 env(
signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie));
585 oracle.set(UpdateArg{
586 .series = {{
"XRP",
"USD", 740, 1}},
587 .msig = msig(becky, bogie),
591 .series = {{
"XRP",
"USD", 7412, 2}}, .msig = msig(zelda, bob)});
592 BEAST_EXPECT(
oracle.expectPrice({{
"XRP",
"USD", 7412, 2}}));
594 UpdateArg{.series = {{
"XRP",
"USD", 74245, 3}}, .msig = msig(ed)});
595 BEAST_EXPECT(
oracle.expectPrice({{
"XRP",
"USD", 74245, 3}}));
600 oracle.remove({.msig = msig(ed)});
601 BEAST_EXPECT(!
oracle.exists());
607 testcase(
"Amendment");
612 Env env(*
this, features);
616 Oracle oracle(env, {.owner = owner, .err = ter(temDISABLED)});
620 Oracle oracle(env, {.owner = owner}, false);
621 oracle.remove({.err = ter(temDISABLED)});
628 testcase("Ledger Entry");
632 std::vector<AccountID> accounts;
633 std::vector<std::uint32_t> oracles;
634 for (int i = 0; i < 10; ++i)
636 Account const owner(std::string("owner") + std::to_string(i));
637 env.fund(XRP(1'000), owner);
639 Oracle oracle(env, {.owner = owner, .documentID = i});
640 accounts.push_back(owner.
id());
641 oracles.push_back(oracle.documentID());
643 Oracle oracle1(env, {.owner = owner, .documentID = i + 10});
644 accounts.push_back(owner.
id());
645 oracles.push_back(oracle1.documentID());
647 for (
int i = 0; i < accounts.size(); ++i)
649 auto const jv = [&]() {
652 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
654 return Oracle::ledgerEntry(
660 jv[jss::node][jss::Owner] ==
to_string(accounts[i]));
681 for (
auto const& features :
685 testMultisig(features);