21#include <test/jtx/AMM.h>
22#include <test/jtx/xchain_bridge.h>
24#include <xrpld/app/tx/detail/NFTokenMint.h>
26#include <xrpl/json/json_reader.h>
27#include <xrpl/json/json_value.h>
28#include <xrpl/json/to_string.h>
29#include <xrpl/protocol/jss.h>
30#include <xrpl/protocol/nft.h>
32#include <boost/utility/string_ref.hpp>
41 "Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
42 "BookDirectory" : "50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
45 "LedgerEntryType" : "Offer",
50 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
53 "TakerPays" : "100000000",
54 "index" : "29665262716C19830E26AEEC0916E476FC7D8EF195FF3B4F06829E64F82A3B3E"
59 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
65 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
69 "LedgerEntryType" : "RippleState",
72 "issuer" : "r9cZvwKU3zzuZK9JFovGg1JC5n7QiqNL8L",
76 "index" : "D13183BCFFC9AAC9F96AEBB5F66E4A652AD1F5D10273AEB615478302BEBFD4A4"
81 "issuer" : "rrrrrrrrrrrrrrrrrrrrBZbvji",
87 "issuer" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
91 "LedgerEntryType" : "RippleState",
94 "issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
98 "index" : "D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
101 "Account" : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
102 "BookDirectory" : "B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
105 "LedgerEntryType" : "Offer",
110 "issuer" : "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
113 "TakerPays" : "100000000",
114 "index" : "F03ABE26CB8C5F4AFB31A86590BD25C64C5756FCE5CE9704C27AFE291A4A29A1"
131 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
133 resp[jss::result][jss::error_message] ==
134 "Missing field 'account'.");
138 auto testInvalidAccountParam = [&](
auto const& param) {
140 params[jss::account] = param;
142 "json",
"account_objects",
to_string(params))[jss::result];
143 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
145 jrr[jss::error_message] ==
"Invalid field 'account'.");
148 testInvalidAccountParam(1);
149 testInvalidAccountParam(1.1);
150 testInvalidAccountParam(
true);
158 params[jss::account] =
159 "n94JNrQYkDrpt62bbSR7nVEhdyAvcJXRAsjEkFYyqRkh9SUTYEqV";
160 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
162 resp[jss::result][jss::error_message] ==
"Account malformed.");
168 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
170 resp[jss::result][jss::error_message] ==
"Account not found.");
176 params[jss::account] = bob.human();
177 params[jss::ledger_index] = 10;
178 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
180 resp[jss::result][jss::error_message] ==
"ledgerNotFound");
187 params[jss::account] = bob.human();
188 params[jss::type] = 10;
189 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
191 resp[jss::result][jss::error_message] ==
192 "Invalid field 'type', not string.");
197 params[jss::account] = bob.human();
198 params[jss::type] =
"expedited";
199 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
201 resp[jss::result][jss::error_message] ==
202 "Invalid field 'type'.");
207 params[jss::account] = bob.human();
208 params[jss::limit] = -1;
209 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
211 resp[jss::result][jss::error_message] ==
212 "Invalid field 'limit', not unsigned integer.");
218 auto const USD = gw[
"USD"];
219 env.
trust(USD(1000), bob);
220 env(
pay(gw, bob,
XRP(1)));
224 params[jss::account] = bob.human();
225 params[jss::limit] = 1;
226 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
228 auto resume_marker = resp[jss::result][jss::marker];
230 params[jss::marker] = 10;
231 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
233 resp[jss::result][jss::error_message] ==
234 "Invalid field 'marker', not string.");
236 params[jss::marker] =
"This is a string with no comma";
237 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
239 resp[jss::result][jss::error_message] ==
240 "Invalid field 'marker'.");
242 params[jss::marker] =
"This string has a comma, but is not hex";
243 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
245 resp[jss::result][jss::error_message] ==
246 "Invalid field 'marker'.");
249 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
251 resp[jss::result][jss::error_message] ==
252 "Invalid field 'marker'.");
255 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
257 resp[jss::result][jss::error_message] ==
258 "Invalid field 'marker'.");
260 params[jss::marker] =
std::string(&mark[1U], 65) +
"not hex";
261 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
263 resp[jss::result][jss::error_message] ==
264 "Invalid field 'marker'.");
270 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
271 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
287 auto const USD1 = gw1[
"USD"];
288 auto const USD2 = gw2[
"USD"];
290 env.
fund(
XRP(1000), gw1, gw2, bob);
291 env.
trust(USD1(1000), bob);
292 env.
trust(USD2(1000), bob);
294 env(
pay(gw1, bob, USD1(1000)));
295 env(
pay(gw2, bob, USD2(1000)));
301 for (
int i = 0; i < 4; ++i)
308 params[jss::account] = bob.human();
309 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
310 BEAST_EXPECT(!resp.isMember(jss::marker));
312 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 4);
313 for (
int i = 0; i < 4; ++i)
315 auto& aobj = resp[jss::result][jss::account_objects][i];
316 aobj.removeMember(
"PreviousTxnID");
317 aobj.removeMember(
"PreviousTxnLgrSeq");
318 BEAST_EXPECT(aobj == bobj[i]);
324 params[jss::account] = bob.human();
325 params[jss::type] = jss::state;
326 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
327 BEAST_EXPECT(!resp.isMember(jss::marker));
329 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 2);
330 for (
int i = 0; i < 2; ++i)
332 auto& aobj = resp[jss::result][jss::account_objects][i];
333 aobj.removeMember(
"PreviousTxnID");
334 aobj.removeMember(
"PreviousTxnLgrSeq");
335 BEAST_EXPECT(aobj == bobj[i + 1]);
341 params[jss::account] = bob.human();
342 params[jss::limit] = 1;
343 for (
int i = 0; i < 4; ++i)
347 auto& aobjs = resp[jss::result][jss::account_objects];
348 BEAST_EXPECT(aobjs.size() == 1);
349 auto& aobj = aobjs[0U];
351 BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
353 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
355 aobj.removeMember(
"PreviousTxnID");
356 aobj.removeMember(
"PreviousTxnLgrSeq");
358 BEAST_EXPECT(aobj == bobj[i]);
360 params[jss::marker] = resp[jss::result][jss::marker];
371 testcase(
"unsteppedThenSteppedWithNFTs");
380 auto const USD1 = gw1[
"USD"];
381 auto const USD2 = gw2[
"USD"];
383 env.
fund(
XRP(1000), gw1, gw2, bob);
390 params[jss::account] = bob.human();
391 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
392 BEAST_EXPECT(!resp.isMember(jss::marker));
393 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
396 params[jss::limit] = 1;
397 resp = env.
rpc(
"json",
"account_objects",
to_string(params));
398 BEAST_EXPECT(!resp.isMember(jss::marker));
399 BEAST_EXPECT(resp[jss::result][jss::account_objects].size() == 0);
411 params[jss::account] = bob.human();
412 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
413 BEAST_EXPECT(!resp.isMember(jss::marker));
415 unpaged = resp[jss::result][jss::account_objects];
416 BEAST_EXPECT(unpaged.
size() == 1);
421 params[jss::account] = bob.human();
422 params[jss::type] = jss::nft_page;
423 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
424 BEAST_EXPECT(!resp.isMember(jss::marker));
425 Json::Value& aobjs = resp[jss::result][jss::account_objects];
426 BEAST_EXPECT(aobjs.
size() == 1);
428 aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
429 BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].
size() == 1);
434 params[jss::account] = bob.human();
435 params[jss::limit] = 1;
439 Json::Value& aobjs = resp[jss::result][jss::account_objects];
440 BEAST_EXPECT(aobjs.
size() == 1);
441 auto& aobj = aobjs[0U];
442 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
443 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
445 BEAST_EXPECT(aobj == unpaged[0u]);
449 env.
trust(USD1(1000), bob);
450 env.
trust(USD2(1000), bob);
452 env(
pay(gw1, bob, USD1(1000)));
453 env(
pay(gw2, bob, USD2(1000)));
462 params[jss::account] = bob.human();
463 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
464 BEAST_EXPECT(!resp.isMember(jss::marker));
466 unpaged = resp[jss::result][jss::account_objects];
467 BEAST_EXPECT(unpaged.
size() == 5);
472 params[jss::account] = bob.human();
473 params[jss::type] = jss::nft_page;
474 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
475 BEAST_EXPECT(!resp.isMember(jss::marker));
476 Json::Value& aobjs = resp[jss::result][jss::account_objects];
477 BEAST_EXPECT(aobjs.
size() == 1);
479 aobjs[0u][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
480 BEAST_EXPECT(aobjs[0u][sfNFTokens.jsonName].
size() == 1);
485 params[jss::account] = bob.human();
486 params[jss::limit] = 1;
487 for (
int i = 0; i < 5; ++i)
491 Json::Value& aobjs = resp[jss::result][jss::account_objects];
492 BEAST_EXPECT(aobjs.
size() == 1);
493 auto& aobj = aobjs[0U];
496 BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
497 BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
501 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
502 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
505 BEAST_EXPECT(aobj == unpaged[i]);
507 params[jss::marker] = resp[jss::result][jss::marker];
512 for (
int i = 0; i < 32; ++i)
520 params[jss::account] = bob.human();
521 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
522 BEAST_EXPECT(!resp.isMember(jss::marker));
524 unpaged = resp[jss::result][jss::account_objects];
525 BEAST_EXPECT(unpaged.
size() == 6);
530 params[jss::account] = bob.human();
531 params[jss::type] = jss::nft_page;
532 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
533 BEAST_EXPECT(!resp.isMember(jss::marker));
534 Json::Value& aobjs = resp[jss::result][jss::account_objects];
535 BEAST_EXPECT(aobjs.
size() == 2);
540 params[jss::account] = bob.human();
541 params[jss::limit] = 1;
542 for (
int i = 0; i < 6; ++i)
546 Json::Value& aobjs = resp[jss::result][jss::account_objects];
547 BEAST_EXPECT(aobjs.
size() == 1);
548 auto& aobj = aobjs[0U];
551 BEAST_EXPECT(resp[jss::result][jss::limit] == 1);
552 BEAST_EXPECT(resp[jss::result].isMember(jss::marker));
556 BEAST_EXPECT(!resp[jss::result].isMember(jss::limit));
557 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
560 BEAST_EXPECT(aobj == unpaged[i]);
562 params[jss::marker] = resp[jss::result][jss::marker];
578 auto const USD = gw[
"USD"];
581 featurePermissionedDomains;
582 Env env(*
this, features);
585 auto acctObjs = [&env](
593 params[jss::type] = *type;
595 params[jss::limit] = *limit;
597 params[jss::marker] = *marker;
598 params[jss::ledger_index] =
"validated";
599 return env.
rpc(
"json",
"account_objects",
to_string(params));
603 auto acctObjsIsSize = [](
Json::Value const& resp,
unsigned size) {
604 return resp[jss::result][jss::account_objects].
isArray() &&
605 (resp[jss::result][jss::account_objects].
size() == size);
609 auto acctObjsTypeIsInvalid = [](
Json::Value const& resp) {
610 return resp[jss::result].
isMember(jss::error) &&
611 resp[jss::result][jss::error_message] ==
612 "Invalid field \'type\'.";
615 env.
fund(
XRP(10000), gw, alice);
620 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
621 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::check), 0));
622 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::deposit_preauth), 0));
623 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::escrow), 0));
624 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::nft_page), 0));
625 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::offer), 0));
626 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::payment_channel), 0));
627 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::signer_list), 0));
628 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::state), 0));
629 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::ticket), 0));
630 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
631 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::did), 0));
632 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::permissioned_domain), 0));
635 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
636 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
637 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
638 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
639 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
647 Json::Value const resp = acctObjs(gw, jss::nft_page);
648 BEAST_EXPECT(acctObjsIsSize(resp, 1));
650 auto const& nftPage = resp[jss::result][jss::account_objects][0u];
651 BEAST_EXPECT(nftPage[sfNFTokens.jsonName].size() == 1);
653 nftPage[sfNFTokens.jsonName][0u][sfNFToken.jsonName]
654 [sfNFTokenID.jsonName] ==
to_string(nftID));
658 env.
trust(USD(1000), alice);
660 env(
pay(gw, alice, USD(5)));
665 BEAST_EXPECT(acctObjsIsSize(resp, 1));
667 auto const& state = resp[jss::result][jss::account_objects][0u];
668 BEAST_EXPECT(state[sfBalance.jsonName][jss::value].asInt() == -5);
670 state[sfHighLimit.jsonName][jss::value].asUInt() == 1000);
678 BEAST_EXPECT(acctObjsIsSize(resp, 1));
680 auto const& check = resp[jss::result][jss::account_objects][0u];
681 BEAST_EXPECT(check[sfAccount.jsonName] == gw.human());
682 BEAST_EXPECT(check[sfDestination.jsonName] == alice.human());
683 BEAST_EXPECT(check[sfSendMax.jsonName][jss::value].asUInt() == 10);
690 Json::Value const resp = acctObjs(gw, jss::deposit_preauth);
691 BEAST_EXPECT(acctObjsIsSize(resp, 1));
693 auto const& preauth = resp[jss::result][jss::account_objects][0u];
694 BEAST_EXPECT(preauth[sfAccount.jsonName] == gw.human());
695 BEAST_EXPECT(preauth[sfAuthorize.jsonName] == alice.human());
700 jvEscrow[jss::TransactionType] = jss::EscrowCreate;
702 jvEscrow[jss::Account] = gw.human();
703 jvEscrow[jss::Destination] = gw.human();
705 jvEscrow[sfFinishAfter.jsonName] =
712 Json::Value const resp = acctObjs(gw, jss::escrow);
713 BEAST_EXPECT(acctObjsIsSize(resp, 1));
715 auto const&
escrow = resp[jss::result][jss::account_objects][0u];
716 BEAST_EXPECT(
escrow[sfAccount.jsonName] == gw.human());
717 BEAST_EXPECT(
escrow[sfDestination.jsonName] == gw.human());
718 BEAST_EXPECT(
escrow[sfAmount.jsonName].
asUInt() == 100'000'000);
731 Json::Value const resp = acctObjs(gw, jss::permissioned_domain);
732 BEAST_EXPECT(acctObjsIsSize(resp, 1));
734 auto const& permissionedDomain =
735 resp[jss::result][jss::account_objects][0u];
737 permissionedDomain.isMember(jss::Owner) &&
738 (permissionedDomain[jss::Owner] == gw.human()));
739 bool const check1 = BEAST_EXPECT(
740 permissionedDomain.isMember(jss::AcceptedCredentials) &&
741 permissionedDomain[jss::AcceptedCredentials].isArray() &&
742 (permissionedDomain[jss::AcceptedCredentials].size() == 1) &&
743 (permissionedDomain[jss::AcceptedCredentials][0u].isMember(
749 permissionedDomain[jss::AcceptedCredentials][0u]
755 credential.isMember(sfCredentialType.jsonName) &&
757 strHex(credentialType1)));
767 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
769 params[jss::account] = acct.
human();
770 params[jss::type] = type;
771 params[jss::ledger_index] =
"validated";
772 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
778 BEAST_EXPECT(acctObjsIsSize(resp, 1));
779 auto const& acct_bridge =
780 resp[jss::result][jss::account_objects][0u];
784 acct_bridge[sfLedgerEntryType.getJsonName()] ==
"Bridge");
786 acct_bridge[sfXChainClaimID.getJsonName()].asUInt() == 0);
788 acct_bridge[sfXChainAccountClaimCount.getJsonName()].asUInt() ==
791 acct_bridge[sfXChainAccountCreateCount.getJsonName()]
794 acct_bridge[sfMinAccountCreateAmount.getJsonName()].asUInt() ==
797 acct_bridge[sfSignatureReward.getJsonName()].asUInt() ==
799 BEAST_EXPECT(acct_bridge[sfXChainBridge.getJsonName()] == x.
jvb);
814 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
816 params[jss::account] = acct.
human();
817 params[jss::type] = type;
818 params[jss::ledger_index] =
"validated";
819 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
825 scEnvAcctObjs(x.
scAlice, jss::xchain_owned_claim_id);
826 BEAST_EXPECT(acctObjsIsSize(resp, 1));
828 auto const& xchain_seq =
829 resp[jss::result][jss::account_objects][0u];
833 xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 1);
838 scEnvAcctObjs(x.
scBob, jss::xchain_owned_claim_id);
839 BEAST_EXPECT(acctObjsIsSize(resp, 1));
841 auto const& xchain_seq =
842 resp[jss::result][jss::account_objects][0u];
843 BEAST_EXPECT(xchain_seq[sfAccount.jsonName] == x.
scBob.
human());
845 xchain_seq[sfXChainClaimID.getJsonName()].asUInt() == 2);
852 auto const amt =
XRP(1000);
871 auto scEnvAcctObjs = [&](
Account const& acct,
char const* type) {
873 params[jss::account] = acct.
human();
874 params[jss::type] = type;
875 params[jss::ledger_index] =
"validated";
876 return scEnv.
rpc(
"json",
"account_objects",
to_string(params));
883 BEAST_EXPECT(acctObjsIsSize(resp, 1));
885 auto const& xchain_create_account_claim_id =
886 resp[jss::result][jss::account_objects][0u];
888 xchain_create_account_claim_id[sfAccount.jsonName] ==
891 xchain_create_account_claim_id[sfXChainAccountCreateCount
903 BEAST_EXPECT(acctObjsIsSize(resp, 1));
905 auto const&
offer = resp[jss::result][jss::account_objects][0u];
906 BEAST_EXPECT(
offer[sfAccount.jsonName] == gw.human());
907 BEAST_EXPECT(
offer[sfTakerGets.jsonName].
asUInt() == 14'000'000);
908 BEAST_EXPECT(
offer[sfTakerPays.jsonName][jss::value].
asUInt() == 7);
914 jvPayChan[jss::TransactionType] = jss::PaymentChannelCreate;
916 jvPayChan[jss::Account] = gw.human();
917 jvPayChan[jss::Destination] = alice.human();
918 jvPayChan[jss::Amount] =
920 jvPayChan[sfSettleDelay.jsonName] = 24 * 60 * 60;
921 jvPayChan[sfPublicKey.jsonName] =
strHex(gw.pk().slice());
927 Json::Value const resp = acctObjs(gw, jss::payment_channel);
928 BEAST_EXPECT(acctObjsIsSize(resp, 1));
930 auto const& payChan = resp[jss::result][jss::account_objects][0u];
931 BEAST_EXPECT(payChan[sfAccount.jsonName] == gw.human());
932 BEAST_EXPECT(payChan[sfAmount.jsonName].asUInt() == 300'000'000);
934 payChan[sfSettleDelay.jsonName].asUInt() == 24 * 60 * 60);
940 jvDID[jss::TransactionType] = jss::DIDSet;
942 jvDID[jss::Account] = gw.human();
950 BEAST_EXPECT(acctObjsIsSize(resp, 1));
952 auto const& did = resp[jss::result][jss::account_objects][0u];
953 BEAST_EXPECT(did[sfAccount.jsonName] == gw.human());
961 Json::Value const resp = acctObjs(gw, jss::signer_list);
962 BEAST_EXPECT(acctObjsIsSize(resp, 1));
964 auto const& signerList =
965 resp[jss::result][jss::account_objects][0u];
966 BEAST_EXPECT(signerList[sfSignerQuorum.jsonName] == 6);
967 auto const& entry = signerList[sfSignerEntries.jsonName][0u]
968 [sfSignerEntry.jsonName];
969 BEAST_EXPECT(entry[sfAccount.jsonName] == alice.human());
970 BEAST_EXPECT(entry[sfSignerWeight.jsonName].
asUInt() == 7);
974 auto const seq = env.
seq(gw);
980 Json::Value const resp = acctObjs(gw, jss::ticket);
981 BEAST_EXPECT(acctObjsIsSize(resp, 1));
983 auto const& ticket = resp[jss::result][jss::account_objects][0u];
984 BEAST_EXPECT(ticket[sfAccount.jsonName] == gw.human());
985 BEAST_EXPECT(ticket[sfLedgerEntryType.jsonName] == jss::Ticket);
986 BEAST_EXPECT(ticket[sfTicketSequence.jsonName].asUInt() ==
seq + 1);
992 params[jss::account] = gw.human();
993 params[jss::deletion_blockers_only] =
true;
994 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1000 jss::NFTokenPage.c_str(),
1001 jss::RippleState.c_str(),
1002 jss::PayChannel.c_str(),
1003 jss::PermissionedDomain.c_str()};
1011 if (BEAST_EXPECT(acctObjsIsSize(resp, expectedAccountObjects)))
1013 auto const& aobjs = resp[jss::result][jss::account_objects];
1015 gotLedgerTypes.
reserve(expectedAccountObjects);
1019 aobjs[i][
"LedgerEntryType"].asString());
1022 BEAST_EXPECT(gotLedgerTypes == expectedLedgerTypes);
1029 params[jss::account] = gw.human();
1030 params[jss::deletion_blockers_only] =
true;
1031 params[jss::type] = jss::escrow;
1032 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1034 if (BEAST_EXPECT(acctObjsIsSize(resp, 1u)))
1036 auto const& aobjs = resp[jss::result][jss::account_objects];
1037 BEAST_EXPECT(aobjs[0u][
"LedgerEntryType"] == jss::Escrow);
1044 auto const objs = resp[jss::result][jss::account_objects];
1045 for (
auto const& obj : resp[jss::result][jss::account_objects])
1047 obj[sfLedgerEntryType.fieldName].asString());
1048 std::sort(typesOut.begin(), typesOut.end());
1052 auto expectObjects =
1055 if (!acctObjsIsSize(resp, types.
size()))
1058 getTypes(resp, typesOut);
1059 return types == typesOut;
1062 AMM amm(env, gw,
XRP(1'000), USD(1'000));
1063 amm.deposit(alice, USD(1));
1066 BEAST_EXPECT(
lines[jss::lines].size() == 3);
1069 acctObjsIsSize(acctObjs(amm.ammAccount(), jss::amm), 1));
1071 auto resp = acctObjs(amm.ammAccount(), std::nullopt, 2);
1073 getTypes(resp, typesOut);
1079 resp[jss::result][jss::marker].
asString());
1080 getTypes(resp, typesOut);
1085 jss::RippleState.c_str(),
1086 jss::RippleState.c_str(),
1087 jss::RippleState.c_str()}));
1089 resp = acctObjs(amm.ammAccount(), jss::state, 10);
1090 BEAST_EXPECT(expectObjects(
1092 {jss::RippleState.c_str(),
1093 jss::RippleState.c_str(),
1094 jss::RippleState.c_str()}));
1097 acctObjsIsSize(acctObjs(amm.ammAccount(), jss::offer), 0));
1099 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::amm), 0));
1103 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::amendments)));
1104 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::directory)));
1105 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::fee)));
1106 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::hashes)));
1107 BEAST_EXPECT(acctObjsTypeIsInvalid(acctObjs(gw, jss::NegativeUNL)));
1111 for (
int d = 1'000'032; d >= 1'000'000; --d)
1118 BEAST_EXPECT(acctObjsIsSize(acctObjs(gw, jss::account), 0));
1130 using namespace jtx;
1136 static constexpr unsigned nftsSize = 10;
1137 for (
unsigned i = 0; i < nftsSize; i++)
1148 params[jss::account] = bob.human();
1149 params[jss::ledger_index] =
"validated";
1152 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
1160 auto compareNFTs = [&tokenIDs, &env, &bob](
1161 unsigned const limit,
unsigned const lastIndex) {
1163 params[jss::account] = bob.human();
1164 params[jss::limit] = limit;
1165 params[jss::marker] = tokenIDs[lastIndex];
1166 params[jss::ledger_index] =
"validated";
1170 if (resp[jss::result].isMember(jss::error))
1173 Json::Value const& nfts = resp[jss::result][jss::account_nfts];
1174 unsigned const nftsCount = tokenIDs.
size() - lastIndex - 1 < limit
1175 ? tokenIDs.
size() - lastIndex - 1
1178 if (nfts.
size() != nftsCount)
1181 for (
unsigned i = 0; i < nftsCount; i++)
1183 if (nfts[i][
"NFTokenID"] != tokenIDs[lastIndex + 1 + i])
1191 BEAST_EXPECT(compareNFTs(4, 2));
1194 BEAST_EXPECT(compareNFTs(4, 7));
1197 auto testInvalidMarker = [&env, &bob](
1198 auto marker,
char const* errorMessage) {
1200 params[jss::account] = bob.human();
1201 params[jss::limit] = 4;
1202 params[jss::ledger_index] = jss::validated;
1203 params[jss::marker] = marker;
1206 return resp[jss::result][jss::error_message] == errorMessage;
1211 testInvalidMarker(17,
"Invalid field \'marker\', not string."));
1214 BEAST_EXPECT(testInvalidMarker(
1215 "00000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B900000000000000000G",
1216 "Invalid field \'marker\'."));
1221 auto createFakeNFTMarker = [](
AccountID const& issuer,
1232 BEAST_EXPECT(testInvalidMarker(
1233 createFakeNFTMarker(bob.id(), 0x000000000, 0x00000000),
1234 "Invalid field \'marker\'."));
1238 BEAST_EXPECT(testInvalidMarker(
1239 createFakeNFTMarker(bob.id(), 0xFFFFFFFF, 0xFFFFFFFF),
1240 "Invalid field \'marker\'."));
1248 using namespace jtx;
1253 auto testInvalidAccountParam = [&](
auto const& param) {
1255 params[jss::account] = param;
1257 "json",
"account_nfts",
to_string(params))[jss::result];
1258 BEAST_EXPECT(jrr[jss::error] ==
"invalidParams");
1260 jrr[jss::error_message] ==
"Invalid field 'account'.");
1263 testInvalidAccountParam(1);
1264 testInvalidAccountParam(1.1);
1265 testInvalidAccountParam(
true);
1277 using namespace jtx;
1283 env.
fund(
XRP(10000), alice, bob, carol);
1285 unsigned const accountObjectSize = 30;
1286 for (
unsigned i = 0; i < accountObjectSize; i++)
1289 for (
unsigned i = 0; i < 10; i++)
1294 unsigned const limit = 11;
1300 params[jss::account] = bob.human();
1301 params[jss::limit] = limit;
1302 params[jss::ledger_index] =
"validated";
1303 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1304 auto& accountObjects = resp[jss::result][jss::account_objects];
1305 marker = resp[jss::result][jss::marker];
1306 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1307 BEAST_EXPECT(accountObjects.size() == limit);
1313 params[jss::account] = bob.human();
1314 params[jss::limit] = limit;
1315 params[jss::marker] = marker;
1316 params[jss::ledger_index] =
"validated";
1317 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1318 auto& accountObjects = resp[jss::result][jss::account_objects];
1319 marker = resp[jss::result][jss::marker];
1320 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1321 BEAST_EXPECT(accountObjects.size() == limit);
1325 auto testInvalidMarker = [&](
std::string& marker) {
1327 params[jss::account] = bob.human();
1328 params[jss::limit] = limit;
1329 params[jss::ledger_index] = jss::validated;
1330 params[jss::marker] = marker;
1333 return resp[jss::result][jss::error_message] ==
1334 "Invalid field \'marker\'.";
1337 auto const markerStr = marker.
asString();
1338 auto const& idx = markerStr.
find(
',');
1339 auto const dirIndex = markerStr.substr(0, idx);
1340 auto const entryIndex = markerStr.substr(idx + 1);
1345 BEAST_EXPECT(testInvalidMarker(s));
1352 BEAST_EXPECT(testInvalidMarker(s));
1360 BEAST_EXPECT(testInvalidMarker(s));
1368 s = dirIndex +
',' + s;
1369 BEAST_EXPECT(testInvalidMarker(s));
1376 BEAST_EXPECT(testInvalidMarker(s));
1385 params[jss::account] = bob.human();
1386 params[jss::limit] = limit;
1387 params[jss::marker] = s;
1388 params[jss::ledger_index] =
"validated";
1389 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1390 auto& accountObjects = resp[jss::result][jss::account_objects];
1391 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1392 BEAST_EXPECT(accountObjects.size() == limit);
1399 BEAST_EXPECT(testInvalidMarker(s));
1406 BEAST_EXPECT(testInvalidMarker(s));
1413 params[jss::account] = bob.human();
1414 params[jss::limit] = limit;
1415 params[jss::marker] = marker;
1416 params[jss::ledger_index] =
"validated";
1417 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1418 auto& accountObjects = resp[jss::result][jss::account_objects];
1419 BEAST_EXPECT(!resp[jss::result].isMember(jss::error));
1421 accountObjects.size() == accountObjectSize - limit * 2);
1422 BEAST_EXPECT(!resp[jss::result].isMember(jss::marker));
1429 params[jss::account] = carol.human();
1430 params[jss::limit] = 10;
1431 params[jss::marker] =
"0," + entryIndex;
1432 params[jss::ledger_index] =
"validated";
1433 auto resp = env.
rpc(
"json",
"account_objects",
to_string(params));
1434 auto& accountObjects = resp[jss::result][jss::account_objects];
1435 BEAST_EXPECT(accountObjects.size() == 0);
Unserialize a JSON document into a Value.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
bool isMember(const char *key) const
Return true if the object has a member named key.
testcase_t testcase
Memberspace for declaring test cases.
static uint256 createNFTokenID(std::uint16_t flags, std::uint16_t fee, AccountID const &issuer, nft::Taxon taxon, std::uint32_t tokenSeq)
void testUnsteppedThenSteppedWithNFTs()
void testAccountObjectMarker()
void testUnsteppedThenStepped()
void run() override
Runs the suite.
Convenience class to test AMM functionality.
Immutable cryptographic account descriptor.
static Account const master
The master account.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
NetClock::time_point now()
Returns the current network time.
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 fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Set the expected result code for a JTx The test will fail if the code doesn't match.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
Taxon toTaxon(std::uint32_t i)
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
uint256 getNextID(jtx::Env const &env, jtx::Account const &issuer, std::uint32_t nfTokenTaxon, std::uint16_t flags, std::uint16_t xferFee)
Get the next NFTokenID that will be issued.
Json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Json::Value create_account_attestation(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, jtx::Account const &rewardAccount, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, jtx::signer const &signer)
Json::Value escrow(AccountID const &account, AccountID const &to, STAmount const &amount)
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value getAccountLines(Env &env, AccountID const &acctId)
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
XRP_t const XRP
Converts to XRP Issue or STAmount.
FeatureBitset supported_amendments()
static char const * bobs_account_objects[]
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t tfPassive
std::string strHex(FwdIt begin, FwdIt end)
constexpr std::uint32_t tfUniversal
std::string to_string(base_uint< Bits, Tag > const &a)
@ credential
Credentials signature.
constexpr std::uint32_t const tfTransferable
void createScBridgeObjects(Env &scEnv)
std::vector< Account > const payees
std::vector< signer > const signers
Set the sequence number on a JTx.
T time_since_epoch(T... args)