1#include <test/jtx/Oracle.h>
3#include <xrpl/protocol/jss.h>
30 .fee =
static_cast<int>(env.
current()->fees().base.drops()),
37 env.
fund(env.
current()->fees().accountReserve(0), owner);
41 .fee =
static_cast<int>(env.
current()->fees().base.drops()),
48 env.
current()->fees().accountReserve(1) +
54 .fee =
static_cast<int>(env.
current()->fees().base.drops())});
55 BEAST_EXPECT(oracle.exists());
59 {
"XRP",
"EUR", 740, 1},
60 {
"XRP",
"GBP", 740, 1},
61 {
"XRP",
"CNY", 740, 1},
62 {
"XRP",
"CAD", 740, 1},
63 {
"XRP",
"AUD", 740, 1},
65 .fee =
static_cast<int>(env.
current()->fees().base.drops()),
72 static_cast<int>(env.
current()->fees().base.drops());
74 Oracle oracle(env, {.owner = owner, .fee = baseFee},
false);
84 .
series = {{
"XRP",
"USD", 740, 1}, {
"XRP",
"USD", 750, 1}},
91 {{
"XRP",
"USD", 740, 1}, {
"XRP",
"EUR",
std::nullopt, 1}},
98 {{
"XRP",
"USD", 740, 1}, {
"XRP",
"USD",
std::nullopt, 1}},
104 {{
"XRP",
"EUR", 740, 1}, {
"XRP",
"EUR",
std::nullopt, 1}},
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}},
132 static_cast<int>(env.current()->fees().base.drops());
139 .series = {{{
"XRP",
"USD", 740, 1}}},
144 {
"XRP",
"US1", 740, 1},
145 {
"XRP",
"US2", 750, 1},
146 {
"XRP",
"US3", 740, 1},
147 {
"XRP",
"US4", 750, 1},
148 {
"XRP",
"US5", 740, 1},
149 {
"XRP",
"US6", 750, 1},
150 {
"XRP",
"US7", 740, 1},
151 {
"XRP",
"US8", 750, 1},
152 {
"XRP",
"US9", 740, 1},
153 {
"XRP",
"U10", 750, 1},
162 static_cast<int>(env.
current()->fees().base.drops());
164 Oracle oracle(env, {.owner = owner, .fee = baseFee},
false);
169 .provider =
"provider",
182 .
fee =
static_cast<int>(env.
current()->fees().base.drops())});
183 BEAST_EXPECT(oracle.exists());
185 .
series = {{
"XRP",
"USD", 740, 1}},
186 .provider =
"provider1",
190 .
series = {{
"XRP",
"USD", 740, 1}},
191 .assetClass =
"currency1",
199 static_cast<int>(env.
current()->fees().base.drops());
201 Oracle oracle(env, {.owner = owner, .fee = baseFee},
false);
234 static_cast<int>(env.
current()->fees().base.drops());
238 Oracle oracle(env, {.owner = owner, .fee = baseFee});
239 BEAST_EXPECT(oracle.exists());
242 .series = {{
"XRP",
"USD", 740, 1}},
252 static_cast<int>(env.
current()->fees().base.drops());
253 auto closeTime = [&]() {
254 return duration_cast<seconds>(
257 .closeTime.time_since_epoch() -
262 Oracle oracle(env, {.owner = owner, .fee = baseFee});
263 BEAST_EXPECT(oracle.exists());
267 .
series = {{
"XRP",
"USD", 740, 1}},
268 .lastUpdateTime =
static_cast<std::uint32_t>(closeTime() - 301),
273 .
series = {{
"XRP",
"USD", 740, 1}},
274 .lastUpdateTime =
static_cast<std::uint32_t>(closeTime() + 311),
279 BEAST_EXPECT(oracle.expectLastUpdateTime(
283 .
series = {{
"XRP",
"USD", 740, 1}},
289 .
series = {{
"XRP",
"USD", 740, 1}},
299 static_cast<int>(env.
current()->fees().base.drops());
301 Oracle oracle(env, {.owner = owner, .fee = baseFee});
302 BEAST_EXPECT(oracle.exists());
318 static_cast<int>(env.
current()->fees().base.drops());
323 .series = {{
"USD",
"USD", 740, 1}},
332 static_cast<int>(env.
current()->fees().base.drops());
346 static_cast<int>(env.
current()->fees().base.drops());
348 Oracle oracle(env, {.owner = owner, .fee = baseFee});
352 {
"XRP",
"EUR", 740, 1}},
377 .fee =
static_cast<int>(env.
current()->fees().base.drops())});
392 static_cast<int>(env.
current()->fees().base.drops());
396 env, {.owner = owner, .series = series, .fee = baseFee});
397 BEAST_EXPECT(oracle.exists());
398 BEAST_EXPECT(
ownerCount(env, owner) == (count + adj));
399 auto const entry = oracle.ledgerEntry();
400 BEAST_EXPECT(entry[jss::node][jss::Owner] == owner.
human());
401 if (features[fixIncludeKeyletFields])
404 entry[jss::node][jss::OracleDocumentID] ==
405 oracle.documentID());
409 BEAST_EXPECT(!entry[jss::node].isMember(jss::OracleDocumentID));
411 BEAST_EXPECT(oracle.expectLastUpdateTime(946694810));
416 Env env(*
this, features);
417 test(env, {{
"XRP",
"USD", 740, 1}}, 1);
422 Env env(*
this, features);
425 {{
"XRP",
"USD", 740, 1},
426 {
"BTC",
"USD", 740, 1},
427 {
"ETH",
"USD", 740, 1},
428 {
"CAN",
"USD", 740, 1},
429 {
"YAN",
"USD", 740, 1},
430 {
"GBP",
"USD", 740, 1}},
436 Env env(*
this, features);
438 static_cast<int>(env.
current()->fees().base.drops());
442 Oracle oracle(env, {.owner = owner, .fee = baseFee});
443 BEAST_EXPECT(oracle.exists());
446 .series = {{
"912810RR9",
"USD", 740, 1}},
460 static_cast<int>(env.
current()->fees().base.drops());
463 Oracle oracle(env, {.owner = owner, .fee = baseFee});
464 BEAST_EXPECT(oracle.exists());
506 static_cast<int>(env.
current()->fees().base.drops());
509 env, {.owner = owner, .series = series, .fee = baseFee});
511 BEAST_EXPECT(oracle.exists());
512 oracle.remove({.fee = baseFee});
513 BEAST_EXPECT(!oracle.exists());
514 BEAST_EXPECT(
ownerCount(env, owner) == (count - adj));
520 test(env, {{
"XRP",
"USD", 740, 1}}, 1);
529 {
"XRP",
"USD", 740, 1},
530 {
"BTC",
"USD", 740, 1},
531 {
"ETH",
"USD", 740, 1},
532 {
"CAN",
"USD", 740, 1},
533 {
"YAN",
"USD", 740, 1},
534 {
"GBP",
"USD", 740, 1},
543 static_cast<int>(env.
current()->fees().base.drops());
545 auto const alice =
Account(
"alice");
546 auto const acctDelFee{
drops(env.
current()->fees().increment)};
552 .series = {{
"XRP",
"USD", 740, 1}},
558 .series = {{
"XRP",
"EUR", 740, 1}},
561 BEAST_EXPECT(oracle.exists());
562 BEAST_EXPECT(oracle1.exists());
563 auto const index = env.
closed()->seq();
564 auto const hash = env.
closed()->header().hash;
565 for (
int i = 0; i < 256; ++i)
569 BEAST_EXPECT(!oracle.exists());
570 BEAST_EXPECT(!oracle1.exists());
573 auto verifyLedgerData = [&](
auto const& field,
auto const& value) {
575 jvParams[field] = value;
576 jvParams[jss::binary] =
false;
577 jvParams[jss::type] = jss::oracle;
580 BEAST_EXPECT(jrr[jss::result][jss::state].size() == 2);
582 verifyLedgerData(jss::ledger_index, index);
597 static_cast<int>(env.
current()->fees().base.drops());
600 Oracle oracle(env, {.owner = owner, .fee = baseFee});
601 BEAST_EXPECT(oracle.exists());
606 BEAST_EXPECT(oracle.expectPrice({{
"XRP",
"USD", 740, 2}}));
610 BEAST_EXPECT(
ownerCount(env, owner) == count);
615 BEAST_EXPECT(oracle.expectPrice(
616 {{
"XRP",
"USD", 0, 0}, {
"XRP",
"EUR", 700, 2}}));
618 BEAST_EXPECT(
ownerCount(env, owner) == count);
622 .
series = {{
"XRP",
"USD", 741, 2}, {
"XRP",
"EUR", 710, 2}},
624 BEAST_EXPECT(oracle.expectPrice(
625 {{
"XRP",
"USD", 741, 2}, {
"XRP",
"EUR", 710, 2}}));
627 BEAST_EXPECT(
ownerCount(env, owner) == count);
633 {
"BTC",
"USD", 741, 2},
634 {
"ETH",
"EUR", 710, 2},
635 {
"YAN",
"EUR", 710, 2},
636 {
"CAN",
"EUR", 710, 2},
640 BEAST_EXPECT(
ownerCount(env, owner) == count);
648 {{
"XRP",
"USD", 742, 2},
649 {
"XRP",
"EUR", 711, 2},
654 BEAST_EXPECT(
oracle.expectPrice(
655 {{
"XRP",
"USD", 742, 2}, {
"XRP",
"EUR", 711, 2}}));
658 BEAST_EXPECT(
ownerCount(env, owner) == count);
665 static_cast<int>(env.current()->fees().base.drops());
667 env.current()->fees().accountReserve(1) +
668 env.current()->fees().base * 2,
670 Oracle
oracle(env, {.owner = owner, .fee = baseFee});
672 UpdateArg{.series = {{
"XRP",
"USD", 742, 2}}, .fee = baseFee});
675 for (
bool const withFixOrder : {
false,
true})
683 static_cast<int>(env.current()->fees().base.drops());
685 auto test = [&](Env& env,
DataSeries const& series) {
686 env.fund(
XRP(1'000), owner);
688 env, {.owner = owner, .series = series, .fee = baseFee});
689 BEAST_EXPECT(
oracle.exists());
692 sle->getFieldArray(sfPriceDataSeries).size() ==
695 auto const beforeQuoteAssetName1 =
696 sle->getFieldArray(sfPriceDataSeries)[0]
697 .getFieldCurrency(sfQuoteAsset)
699 auto const beforeQuoteAssetName2 =
700 sle->getFieldArray(sfPriceDataSeries)[1]
701 .getFieldCurrency(sfQuoteAsset)
704 oracle.set(UpdateArg{.series = series, .fee = baseFee});
707 auto const afterQuoteAssetName1 =
708 sle->getFieldArray(sfPriceDataSeries)[0]
709 .getFieldCurrency(sfQuoteAsset)
711 auto const afterQuoteAssetName2 =
712 sle->getFieldArray(sfPriceDataSeries)[1]
713 .getFieldCurrency(sfQuoteAsset)
716 if (env.current()->rules().enabled(fixPriceOracleOrder))
718 BEAST_EXPECT(afterQuoteAssetName1 == beforeQuoteAssetName1);
719 BEAST_EXPECT(afterQuoteAssetName2 == beforeQuoteAssetName2);
723 BEAST_EXPECT(afterQuoteAssetName1 != beforeQuoteAssetName1);
724 BEAST_EXPECT(afterQuoteAssetName2 != beforeQuoteAssetName2);
727 test(env, {{
"XRP",
"USD", 742, 2}, {
"XRP",
"EUR", 711, 2}});
734 testcase(
"Multisig");
736 Oracle::setFee(100'000);
740 static_cast<int>(env.
current()->fees().base.drops());
748 env.
fund(
XRP(10'000), alice, becky, zelda, ed, bob);
756 env(signers(alice, 2, {{becky, 1}, {bogie, 1}, {ed, 2}}),
sig(alie));
773 BEAST_EXPECT(oracle.exists());
777 .
series = {{
"XRP",
"USD", 740, 1}},
782 .
series = {{
"XRP",
"USD", 740, 1}},
787 .
series = {{
"XRP",
"USD", 741, 1}},
788 .msig =
msig(becky, bogie),
790 BEAST_EXPECT(oracle.expectPrice({{
"XRP",
"USD", 741, 1}}));
792 env(signers(alice, jtx::none),
sig(alie));
794 env.require(
owners(alice, 1));
796 env(signers(alice, 2, {{zelda, 1}, {bob, 1}, {ed, 2}}), sig(alie));
799 oracle.set(UpdateArg{
800 .series = {{
"XRP",
"USD", 740, 1}},
801 .msig = msig(becky, bogie),
806 .series = {{
"XRP",
"USD", 7412, 2}},
807 .msig = msig(zelda, bob),
809 BEAST_EXPECT(
oracle.expectPrice({{
"XRP",
"USD", 7412, 2}}));
811 .series = {{
"XRP",
"USD", 74245, 3}},
814 BEAST_EXPECT(
oracle.expectPrice({{
"XRP",
"USD", 74245, 3}}));
818 {.msig = msig(bob), .fee = baseFee, .err = ter(tefBAD_QUORUM)});
820 {.msig = msig(becky),
822 .err = ter(tefBAD_SIGNATURE)});
823 oracle.remove({.msig = msig(ed), .fee = baseFee});
824 BEAST_EXPECT(!
oracle.exists());
830 testcase(
"Amendment");
835 Env env(*
this, features);
837 static_cast<int>(env.
current()->fees().base.drops());
846 Oracle oracle(env, {.owner = owner, .fee = baseFee},
false);
860 testCreate(all - fixIncludeKeyletFields);
testcase_t testcase
Memberspace for declaring test cases.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
void memoize(Account const &account)
Associate AccountID with account.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Set a multisignature on a JTx.
Oracle class facilitates unit-testing of the Price Oracle feature.
Match the number of items in the account's owner directory.
Set the regular signature on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Keylet oracle(AccountID const &account, std::uint32_t const &documentID) noexcept
static constexpr std::chrono::seconds testStartTime
std::vector< std::tuple< std::string, std::string, std::optional< std::uint32_t >, std::optional< std::uint8_t > > > DataSeries
std::uint32_t ownerCount(Env const &env, Account const &account)
XRP_t const XRP
Converts to XRP Issue or STAmount.
FeatureBitset testable_amendments()
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t asfDisableMaster
std::string to_string(base_uint< Bits, Tag > const &a)
std::size_t constexpr maxPriceScale
The maximum price scaling factor.
static constexpr std::chrono::seconds epoch_offset
Clock for measuring the network time.
@ tecINSUFFICIENT_RESERVE
@ tecTOKEN_PAIR_NOT_FOUND
constexpr std::uint32_t const tfSellNFToken
@ invalid
Timely, but invalid signature.
std::optional< AccountID > owner
std::optional< AnyValue > provider
std::optional< AnyValue > assetClass
std::optional< jtx::msig > msig
std::optional< AnyValue > uri
void run() override
Runs the suite.
void testCreate(FeatureBitset features)
std::optional< AccountID > owner
std::optional< AnyValue > documentID
Set the sequence number on a JTx.