rippled
Loading...
Searching...
No Matches
LedgerEntry_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/Oracle.h>
3#include <test/jtx/attester.h>
4#include <test/jtx/delegate.h>
5#include <test/jtx/multisign.h>
6#include <test/jtx/xchain_bridge.h>
7
8#include <xrpl/beast/unit_test.h>
9#include <xrpl/json/json_value.h>
10#include <xrpl/protocol/AccountID.h>
11#include <xrpl/protocol/ErrorCodes.h>
12#include <xrpl/protocol/STXChainBridge.h>
13#include <xrpl/protocol/jss.h>
14
15#if (defined(__clang_major__) && __clang_major__ < 15)
16#include <experimental/source_location>
18#else
19#include <source_location>
21#endif
22namespace xrpl {
23
24namespace test {
25
40
42 {jss::account, FieldType::AccountField},
43 {jss::accounts, FieldType::TwoAccountArrayField},
44 {jss::asset, FieldType::IssueField},
45 {jss::asset2, FieldType::IssueField},
46 {jss::authorize, FieldType::AccountField},
47 {jss::authorized, FieldType::AccountField},
48 {jss::credential_type, FieldType::BlobField},
49 {jss::currency, FieldType::CurrencyField},
50 {jss::issuer, FieldType::AccountField},
51 {jss::oracle_document_id, FieldType::UInt32Field},
52 {jss::owner, FieldType::AccountField},
53 {jss::seq, FieldType::UInt32Field},
54 {jss::subject, FieldType::AccountField},
55 {jss::ticket_seq, FieldType::UInt32Field},
56};
57
60{
61 auto it = std::ranges::find_if(mappings, [&fieldName](auto const& pair) {
62 return pair.first == fieldName;
63 });
64 if (it != mappings.end())
65 {
66 return it->second;
67 }
68 else
69 {
70 Throw<std::runtime_error>(
71 "`mappings` is missing field " + std::string(fieldName.c_str()));
72 }
73}
74
77{
78 switch (typeID)
79 {
81 return "AccountID";
83 return "array";
85 return "hex string";
87 return "Currency";
89 return "hex string";
91 return "hex string or object";
93 return "Issue";
95 return "length-2 array of Accounts";
97 return "number";
99 return "number";
100 default:
101 Throw<std::runtime_error>(
102 "unknown type " + std::to_string(static_cast<uint8_t>(typeID)));
103 }
104}
105
107{
108 void
110 Json::Value const& jv,
111 std::string const& err,
112 std::string const& msg,
113 source_location const location = source_location::current())
114 {
115 if (BEAST_EXPECT(jv.isMember(jss::status)))
116 BEAST_EXPECTS(
117 jv[jss::status] == "error", std::to_string(location.line()));
118 if (BEAST_EXPECT(jv.isMember(jss::error)))
119 BEAST_EXPECTS(
120 jv[jss::error] == err,
121 "Expected error " + err + ", received " +
122 jv[jss::error].asString() + ", at line " +
123 std::to_string(location.line()) + ", " +
124 jv.toStyledString());
125 if (msg.empty())
126 {
127 BEAST_EXPECTS(
128 jv[jss::error_message] == Json::nullValue ||
129 jv[jss::error_message] == "",
130 "Expected no error message, received \"" +
131 jv[jss::error_message].asString() + "\", at line " +
132 std::to_string(location.line()) + ", " +
133 jv.toStyledString());
134 }
135 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
136 BEAST_EXPECTS(
137 jv[jss::error_message] == msg,
138 "Expected error message \"" + msg + "\", received \"" +
139 jv[jss::error_message].asString() + "\", at line " +
140 std::to_string(location.line()) + ", " +
141 jv.toStyledString());
142 }
143
146 {
147 static Json::Value const injectObject = []() {
149 obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
150 obj[jss::ledger_index] = "validated";
151 return obj;
152 }();
153 static Json::Value const injectArray = []() {
155 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
156 arr[1u] = "validated";
157 return arr;
158 }();
159 static std::array<Json::Value, 21> const allBadValues = {
160 "", // 0
161 true, // 1
162 1, // 2
163 "1", // 3
164 -1, // 4
165 1.1, // 5
166 "-1", // 6
167 "abcdef", // 7
168 "ABCDEF", // 8
169 "12KK", // 9
170 "0123456789ABCDEFGH", // 10
171 "rJxKV9e9p6wiPw!!!!xrJ4X1n98LosPL1sgcJW", // 11
172 "rPSTrR5yEr11uMkfsz1kHCp9jK4aoa3Avv", // 12
173 "n9K2isxwTxcSHJKxMkJznDoWXAUs7NNy49H9Fknz1pC7oHAH3kH9", // 13
174 "USD", // 14
175 "USDollars", // 15
176 "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6B01403D"
177 "6D", // 16
178 Json::arrayValue, // 17
179 Json::objectValue, // 18
180 injectObject, // 19
181 injectArray // 20
182 };
183
184 auto remove =
187 indices.begin(), indices.end());
189 values.reserve(allBadValues.size() - indexSet.size());
190 for (std::size_t i = 0; i < allBadValues.size(); ++i)
191 {
192 if (indexSet.find(i) == indexSet.end())
193 {
194 values.push_back(allBadValues[i]);
195 }
196 }
197 return values;
198 };
199
200 static auto const& badAccountValues = remove({12});
201 static auto const& badArrayValues = remove({17, 20});
202 static auto const& badBlobValues = remove({3, 7, 8, 16});
203 static auto const& badCurrencyValues = remove({14});
204 static auto const& badHashValues = remove({2, 3, 7, 8, 16});
205 static auto const& badIndexValues = remove({12, 16, 18, 19});
206 static auto const& badUInt32Values = remove({2, 3});
207 static auto const& badUInt64Values = remove({2, 3});
208 static auto const& badIssueValues = remove({});
209
210 switch (fieldType)
211 {
213 return badAccountValues;
216 return badArrayValues;
218 return badBlobValues;
220 return badCurrencyValues;
222 return badHashValues;
224 return badIndexValues;
226 return badIssueValues;
228 return badUInt32Values;
230 return badUInt64Values;
231 default:
232 Throw<std::runtime_error>(
233 "unknown type " +
234 std::to_string(static_cast<uint8_t>(fieldType)));
235 }
236 }
237
240 {
241 static Json::Value const twoAccountArray = []() {
243 arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU";
244 arr[1u] = "r4MrUGTdB57duTnRs6KbsRGQXgkseGb1b5";
245 return arr;
246 }();
247 static Json::Value const issueObject = []() {
249 arr[jss::currency] = "XRP";
250 return arr;
251 }();
252
253 auto const typeID = getFieldType(fieldName);
254 switch (typeID)
255 {
257 return "r4MrUGTdB57duTnRs6KbsRGQXgkseGb1b5";
259 return Json::arrayValue;
261 return "ABCDEF";
263 return "USD";
265 return "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6"
266 "B01403D6D";
268 return issueObject;
270 return "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6"
271 "B01403D6D";
273 return twoAccountArray;
275 return 1;
277 return 1;
278 default:
279 Throw<std::runtime_error>(
280 "unknown type " +
281 std::to_string(static_cast<uint8_t>(typeID)));
282 }
283 }
284
285 void
287 test::jtx::Env& env,
288 Json::Value correctRequest,
289 Json::StaticString const fieldName,
290 FieldType const typeID,
291 std::string const& expectedError,
292 bool required = true,
293 source_location const location = source_location::current())
294 {
295 forAllApiVersions([&, this](unsigned apiVersion) {
296 if (required)
297 {
298 correctRequest.removeMember(fieldName);
299 Json::Value const jrr = env.rpc(
300 apiVersion,
301 "json",
302 "ledger_entry",
303 to_string(correctRequest))[jss::result];
304 if (apiVersion < 2u)
305 checkErrorValue(jrr, "unknownOption", "", location);
306 else
308 jrr,
309 "invalidParams",
310 "No ledger_entry params provided.",
311 location);
312 }
313 auto tryField = [&](Json::Value fieldValue) -> void {
314 correctRequest[fieldName] = fieldValue;
315 Json::Value const jrr = env.rpc(
316 apiVersion,
317 "json",
318 "ledger_entry",
319 to_string(correctRequest))[jss::result];
320 auto const expectedErrMsg =
321 RPC::expected_field_message(fieldName, getTypeName(typeID));
322 checkErrorValue(jrr, expectedError, expectedErrMsg, location);
323 };
324
325 auto const& badValues = getBadValues(typeID);
326 for (auto const& value : badValues)
327 {
328 tryField(value);
329 }
330 if (required)
331 {
332 tryField(Json::nullValue);
333 }
334 });
335 }
336
337 void
339 test::jtx::Env& env,
340 Json::Value correctRequest,
341 Json::StaticString parentFieldName,
342 Json::StaticString fieldName,
343 FieldType typeID,
344 std::string const& expectedError,
345 bool required = true,
346 source_location const location = source_location::current())
347 {
348 forAllApiVersions([&, this](unsigned apiVersion) {
349 if (required)
350 {
351 correctRequest[parentFieldName].removeMember(fieldName);
352 Json::Value const jrr = env.rpc(
353 apiVersion,
354 "json",
355 "ledger_entry",
356 to_string(correctRequest))[jss::result];
358 jrr,
359 "malformedRequest",
361 location);
362
363 correctRequest[parentFieldName][fieldName] = Json::nullValue;
364 Json::Value const jrr2 = env.rpc(
365 apiVersion,
366 "json",
367 "ledger_entry",
368 to_string(correctRequest))[jss::result];
370 jrr2,
371 "malformedRequest",
373 location);
374 }
375 auto tryField = [&](Json::Value fieldValue) -> void {
376 correctRequest[parentFieldName][fieldName] = fieldValue;
377
378 Json::Value const jrr = env.rpc(
379 apiVersion,
380 "json",
381 "ledger_entry",
382 to_string(correctRequest))[jss::result];
384 jrr,
385 expectedError,
386 RPC::expected_field_message(fieldName, getTypeName(typeID)),
387 location);
388 };
389
390 auto const& badValues = getBadValues(typeID);
391 for (auto const& value : badValues)
392 {
393 tryField(value);
394 }
395 });
396 }
397
398 // No subfields
399 void
401 test::jtx::Env& env,
402 Json::StaticString const& parentField,
403 source_location const location = source_location::current())
404 {
406 env,
407 Json::Value{},
408 parentField,
410 "malformedRequest",
411 true,
412 location);
413 }
414
421
422 void
424 test::jtx::Env& env,
425 Json::StaticString const& parentField,
426 std::vector<Subfield> const& subfields,
427 source_location const location = source_location::current())
428 {
430 env,
431 Json::Value{},
432 parentField,
434 "malformedRequest",
435 true,
436 location);
437
438 Json::Value correctOutput;
439 correctOutput[parentField] = Json::objectValue;
440 for (auto const& subfield : subfields)
441 {
442 correctOutput[parentField][subfield.fieldName] =
443 getCorrectValue(subfield.fieldName);
444 }
445
446 for (auto const& subfield : subfields)
447 {
448 auto const fieldType = getFieldType(subfield.fieldName);
450 env,
451 correctOutput,
452 parentField,
453 subfield.fieldName,
454 fieldType,
455 subfield.malformedErrorMsg,
456 subfield.required,
457 location);
458 }
459 }
460
461 void
463 {
464 testcase("Invalid requests");
465 using namespace test::jtx;
466 Env env{*this};
467 Account const alice{"alice"};
468 env.fund(XRP(10000), alice);
469 env.close();
470 {
471 // Missing ledger_entry ledger_hash
472 Json::Value jvParams;
473 jvParams[jss::account_root] = alice.human();
474 jvParams[jss::ledger_hash] =
475 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
476 "AA";
477 auto const jrr = env.rpc(
478 "json", "ledger_entry", to_string(jvParams))[jss::result];
479 checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
480 }
481 {
482 // Missing ledger_entry ledger_hash
483 Json::Value jvParams;
484 jvParams[jss::account_root] = alice.human();
485 auto const typeId = FieldType::HashField;
486
487 forAllApiVersions([&, this](unsigned apiVersion) {
488 auto tryField = [&](Json::Value fieldValue) -> void {
489 jvParams[jss::ledger_hash] = fieldValue;
490 Json::Value const jrr = env.rpc(
491 apiVersion,
492 "json",
493 "ledger_entry",
494 to_string(jvParams))[jss::result];
496 jrr,
497 "invalidParams",
498 "Invalid field 'ledger_hash', not hex string.");
499 };
500
501 auto const& badValues = getBadValues(typeId);
502 for (auto const& value : badValues)
503 {
504 tryField(value);
505 }
506 });
507 }
508
509 {
510 // ask for an zero index
511 Json::Value jvParams;
512 jvParams[jss::ledger_index] = "validated";
513 jvParams[jss::index] =
514 "00000000000000000000000000000000000000000000000000000000000000"
515 "00";
516 auto const jrr = env.rpc(
517 "json", "ledger_entry", to_string(jvParams))[jss::result];
518 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
519 }
520
521 forAllApiVersions([&, this](unsigned apiVersion) {
522 // "features" is not an option supported by ledger_entry.
523 {
525 jvParams[jss::features] =
526 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
527 "AAAAAAAAAA";
528 jvParams[jss::api_version] = apiVersion;
529 Json::Value const jrr = env.rpc(
530 "json", "ledger_entry", to_string(jvParams))[jss::result];
531
532 if (apiVersion < 2u)
533 checkErrorValue(jrr, "unknownOption", "");
534 else
536 jrr,
537 "invalidParams",
538 "No ledger_entry params provided.");
539 }
540 });
541 }
542
543 void
545 {
546 testcase("AccountRoot");
547 using namespace test::jtx;
548
549 auto cfg = envconfig();
550 cfg->FEES.reference_fee = 10;
551 Env env{*this, std::move(cfg)};
552
553 Account const alice{"alice"};
554 env.fund(XRP(10000), alice);
555 env.close();
556
557 std::string const ledgerHash{to_string(env.closed()->header().hash)};
558 {
559 // Exercise ledger_closed along the way.
560 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
561 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
562 BEAST_EXPECT(jrr[jss::ledger_index] == 3);
563 }
564
565 std::string accountRootIndex;
566 {
567 // Request alice's account root.
568 Json::Value jvParams;
569 jvParams[jss::account_root] = alice.human();
570 jvParams[jss::ledger_hash] = ledgerHash;
571 Json::Value const jrr = env.rpc(
572 "json", "ledger_entry", to_string(jvParams))[jss::result];
573 BEAST_EXPECT(jrr.isMember(jss::node));
574 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
575 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
576 accountRootIndex = jrr[jss::index].asString();
577 }
578 {
579 constexpr char alicesAcctRootBinary[]{
580 "1100612200800000240000000425000000032D00000000559CE54C3B934E4"
581 "73A995B477E92EC229F99CED5B62BF4D2ACE4DC42719103AE2F6240000002"
582 "540BE4008114AE123A8556F3CF91154711376AFB0F894F832B3D"};
583
584 // Request alice's account root, but with binary == true;
585 Json::Value jvParams;
586 jvParams[jss::account_root] = alice.human();
587 jvParams[jss::binary] = 1;
588 jvParams[jss::ledger_hash] = ledgerHash;
589 Json::Value const jrr = env.rpc(
590 "json", "ledger_entry", to_string(jvParams))[jss::result];
591 BEAST_EXPECT(jrr.isMember(jss::node_binary));
592 BEAST_EXPECT(jrr[jss::node_binary] == alicesAcctRootBinary);
593 }
594 {
595 // Request alice's account root using the index.
596 Json::Value jvParams;
597 jvParams[jss::index] = accountRootIndex;
598 Json::Value const jrr = env.rpc(
599 "json", "ledger_entry", to_string(jvParams))[jss::result];
600 BEAST_EXPECT(!jrr.isMember(jss::node_binary));
601 BEAST_EXPECT(jrr.isMember(jss::node));
602 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
603 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
604 }
605 {
606 // Request alice's account root by index, but with binary == false.
607 Json::Value jvParams;
608 jvParams[jss::index] = accountRootIndex;
609 jvParams[jss::binary] = 0;
610 Json::Value const jrr = env.rpc(
611 "json", "ledger_entry", to_string(jvParams))[jss::result];
612 BEAST_EXPECT(jrr.isMember(jss::node));
613 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
614 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
615 }
616 {
617 // Check alias
618 Json::Value jvParams;
619 jvParams[jss::account] = alice.human();
620 jvParams[jss::ledger_hash] = ledgerHash;
621 Json::Value const jrr = env.rpc(
622 "json", "ledger_entry", to_string(jvParams))[jss::result];
623 BEAST_EXPECT(jrr.isMember(jss::node));
624 BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human());
625 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000");
626 accountRootIndex = jrr[jss::index].asString();
627 }
628 {
629 // Check malformed cases
630 Json::Value jvParams;
632 env,
633 jvParams,
634 jss::account_root,
636 "malformedAddress");
637 }
638 {
639 // Request an account that is not in the ledger.
640 Json::Value jvParams;
641 jvParams[jss::account_root] = Account("bob").human();
642 jvParams[jss::ledger_hash] = ledgerHash;
643 Json::Value const jrr = env.rpc(
644 "json", "ledger_entry", to_string(jvParams))[jss::result];
645 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
646 }
647 }
648
649 void
651 {
652 testcase("Amendments");
653 using namespace test::jtx;
654 Env env{*this};
655
656 // positive test
657 {
658 Keylet const keylet = keylet::amendments();
659
660 // easier to hack an object into the ledger than generate it
661 // legitimately
662 {
663 auto const amendments = [&](OpenView& view,
664 beast::Journal) -> bool {
665 auto const sle = std::make_shared<SLE>(keylet);
666
667 // Create Amendments vector (enabled amendments)
668 std::vector<uint256> enabledAmendments;
669 enabledAmendments.push_back(
670 uint256::fromVoid("42426C4D4F1009EE67080A9B7965B44656D7"
671 "714D104A72F9B4369F97ABF044EE"));
672 enabledAmendments.push_back(
673 uint256::fromVoid("4C97EBA926031A7CF7D7B36FDE3ED66DDA54"
674 "21192D63DE53FFB46E43B9DC8373"));
675 enabledAmendments.push_back(
676 uint256::fromVoid("03BDC0099C4E14163ADA272C1B6F6FABB448"
677 "CC3E51F522F978041E4B57D9158C"));
678 enabledAmendments.push_back(
679 uint256::fromVoid("35291ADD2D79EB6991343BDA0912269C817D"
680 "0F094B02226C1C14AD2858962ED4"));
681 sle->setFieldV256(
682 sfAmendments, STVector256(enabledAmendments));
683
684 // Create Majorities array
685 STArray majorities;
686
687 auto majority1 = STObject::makeInnerObject(sfMajority);
688 majority1.setFieldH256(
689 sfAmendment,
690 uint256::fromVoid("7BB62DC13EC72B775091E9C71BF8CF97E122"
691 "647693B50C5E87A80DFD6FCFAC50"));
692 majority1.setFieldU32(sfCloseTime, 779561310);
693 majorities.push_back(std::move(majority1));
694
695 auto majority2 = STObject::makeInnerObject(sfMajority);
696 majority2.setFieldH256(
697 sfAmendment,
698 uint256::fromVoid("755C971C29971C9F20C6F080F2ED96F87884"
699 "E40AD19554A5EBECDCEC8A1F77FE"));
700 majority2.setFieldU32(sfCloseTime, 779561310);
701 majorities.push_back(std::move(majority2));
702
703 sle->setFieldArray(sfMajorities, majorities);
704
705 view.rawInsert(sle);
706 return true;
707 };
708 env.app().openLedger().modify(amendments);
709 }
710
711 Json::Value jvParams;
712 jvParams[jss::amendments] = to_string(keylet.key);
713 Json::Value const jrr = env.rpc(
714 "json", "ledger_entry", to_string(jvParams))[jss::result];
715 BEAST_EXPECT(
716 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Amendments);
717 }
718
719 // negative tests
720 runLedgerEntryTest(env, jss::amendments);
721 }
722
723 void
725 {
726 testcase("AMM");
727 using namespace test::jtx;
728 Env env{*this};
729
730 // positive test
731 Account const alice{"alice"};
732 env.fund(XRP(10000), alice);
733 env.close();
734 AMM amm(env, alice, XRP(10), alice["USD"](1000));
735 env.close();
736
737 {
738 Json::Value jvParams;
739 jvParams[jss::amm] = to_string(amm.ammID());
740 auto const result =
741 env.rpc("json", "ledger_entry", to_string(jvParams));
742 BEAST_EXPECT(
743 result.isObject() && result.isMember(jss::result) &&
744 !result[jss::result].isMember(jss::error) &&
745 result[jss::result].isMember(jss::node) &&
746 result[jss::result][jss::node].isMember(
747 sfLedgerEntryType.jsonName) &&
748 result[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
749 jss::AMM);
750 }
751
752 {
753 Json::Value jvParams;
755 {
757 obj[jss::currency] = "XRP";
758 ammParams[jss::asset] = obj;
759 }
760 {
762 obj[jss::currency] = "USD";
763 obj[jss::issuer] = alice.human();
764 ammParams[jss::asset2] = obj;
765 }
766 jvParams[jss::amm] = ammParams;
767 auto const result =
768 env.rpc("json", "ledger_entry", to_string(jvParams));
769 BEAST_EXPECT(
770 result.isObject() && result.isMember(jss::result) &&
771 !result[jss::result].isMember(jss::error) &&
772 result[jss::result].isMember(jss::node) &&
773 result[jss::result][jss::node].isMember(
774 sfLedgerEntryType.jsonName) &&
775 result[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
776 jss::AMM);
777 }
778
779 // negative tests
781 env,
782 jss::amm,
783 {
784 {jss::asset, "malformedRequest"},
785 {jss::asset2, "malformedRequest"},
786 });
787 }
788
789 void
791 {
792 testcase("Check");
793 using namespace test::jtx;
794 Env env{*this};
795 Account const alice{"alice"};
796 env.fund(XRP(10000), alice);
797 env.close();
798
799 auto const checkId = keylet::check(env.master, env.seq(env.master));
800
801 env(check::create(env.master, alice, XRP(100)));
802 env.close();
803
804 std::string const ledgerHash{to_string(env.closed()->header().hash)};
805 {
806 // Request a check.
807 Json::Value jvParams;
808 jvParams[jss::check] = to_string(checkId.key);
809 jvParams[jss::ledger_hash] = ledgerHash;
810 Json::Value const jrr = env.rpc(
811 "json", "ledger_entry", to_string(jvParams))[jss::result];
812 BEAST_EXPECT(
813 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
814 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
815 }
816 {
817 // Request an index that is not a check. We'll use alice's
818 // account root index.
819 std::string accountRootIndex;
820 {
821 Json::Value jvParams;
822 jvParams[jss::account_root] = alice.human();
823 Json::Value const jrr = env.rpc(
824 "json", "ledger_entry", to_string(jvParams))[jss::result];
825 accountRootIndex = jrr[jss::index].asString();
826 }
827 Json::Value jvParams;
828 jvParams[jss::check] = accountRootIndex;
829 jvParams[jss::ledger_hash] = ledgerHash;
830 Json::Value const jrr = env.rpc(
831 "json", "ledger_entry", to_string(jvParams))[jss::result];
833 jrr, "unexpectedLedgerType", "Unexpected ledger type.");
834 }
835 {
836 // Check malformed cases
837 runLedgerEntryTest(env, jss::check);
838 }
839 }
840
841 void
843 {
844 testcase("Credentials");
845
846 using namespace test::jtx;
847
848 Env env(*this);
849 Account const issuer{"issuer"};
850 Account const alice{"alice"};
851 Account const bob{"bob"};
852 char const credType[] = "abcde";
853
854 env.fund(XRP(5000), issuer, alice, bob);
855 env.close();
856
857 // Setup credentials with DepositAuth object for Alice and Bob
858 env(credentials::create(alice, issuer, credType));
859 env.close();
860
861 {
862 // Succeed
863 auto jv = credentials::ledgerEntry(env, alice, issuer, credType);
864 BEAST_EXPECT(
865 jv.isObject() && jv.isMember(jss::result) &&
866 !jv[jss::result].isMember(jss::error) &&
867 jv[jss::result].isMember(jss::node) &&
868 jv[jss::result][jss::node].isMember(
869 sfLedgerEntryType.jsonName) &&
870 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
871 jss::Credential);
872
873 std::string const credIdx = jv[jss::result][jss::index].asString();
874
875 jv = credentials::ledgerEntry(env, credIdx);
876 BEAST_EXPECT(
877 jv.isObject() && jv.isMember(jss::result) &&
878 !jv[jss::result].isMember(jss::error) &&
879 jv[jss::result].isMember(jss::node) &&
880 jv[jss::result][jss::node].isMember(
881 sfLedgerEntryType.jsonName) &&
882 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
883 jss::Credential);
884 }
885
886 {
887 // Fail, credential doesn't exist
888 auto const jv = credentials::ledgerEntry(
889 env,
890 "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B"
891 "E4");
893 jv[jss::result], "entryNotFound", "Entry not found.");
894 }
895
896 {
897 // Check all malformed cases
899 env,
900 jss::credential,
901 {
902 {jss::subject, "malformedRequest"},
903 {jss::issuer, "malformedRequest"},
904 {jss::credential_type, "malformedRequest"},
905 });
906 }
907 }
908
909 void
911 {
912 testcase("Delegate");
913
914 using namespace test::jtx;
915
916 Env env{*this};
917 Account const alice{"alice"};
918 Account const bob{"bob"};
919 env.fund(XRP(10000), alice, bob);
920 env.close();
921 env(delegate::set(alice, bob, {"Payment", "CheckCreate"}));
922 env.close();
923 std::string const ledgerHash{to_string(env.closed()->header().hash)};
924 std::string delegateIndex;
925 {
926 // Request by account and authorize
927 Json::Value jvParams;
928 jvParams[jss::delegate][jss::account] = alice.human();
929 jvParams[jss::delegate][jss::authorize] = bob.human();
930 jvParams[jss::ledger_hash] = ledgerHash;
931 Json::Value const jrr = env.rpc(
932 "json", "ledger_entry", to_string(jvParams))[jss::result];
933 BEAST_EXPECT(
934 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
935 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
936 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
937 delegateIndex = jrr[jss::node][jss::index].asString();
938 }
939 {
940 // Request by index.
941 Json::Value jvParams;
942 jvParams[jss::delegate] = delegateIndex;
943 jvParams[jss::ledger_hash] = ledgerHash;
944 Json::Value const jrr = env.rpc(
945 "json", "ledger_entry", to_string(jvParams))[jss::result];
946 BEAST_EXPECT(
947 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Delegate);
948 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
949 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human());
950 }
951
952 {
953 // Check all malformed cases
955 env,
956 jss::delegate,
957 {
958 {jss::account, "malformedAddress"},
959 {jss::authorize, "malformedAddress"},
960 });
961 }
962 }
963
964 void
966 {
967 testcase("Deposit Preauth");
968
969 using namespace test::jtx;
970
971 Env env{*this};
972 Account const alice{"alice"};
973 Account const becky{"becky"};
974
975 env.fund(XRP(10000), alice, becky);
976 env.close();
977
978 env(deposit::auth(alice, becky));
979 env.close();
980
981 std::string const ledgerHash{to_string(env.closed()->header().hash)};
982 std::string depositPreauthIndex;
983 {
984 // Request a depositPreauth by owner and authorized.
985 Json::Value jvParams;
986 jvParams[jss::deposit_preauth][jss::owner] = alice.human();
987 jvParams[jss::deposit_preauth][jss::authorized] = becky.human();
988 jvParams[jss::ledger_hash] = ledgerHash;
989 Json::Value const jrr = env.rpc(
990 "json", "ledger_entry", to_string(jvParams))[jss::result];
991
992 BEAST_EXPECT(
993 jrr[jss::node][sfLedgerEntryType.jsonName] ==
994 jss::DepositPreauth);
995 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
996 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
997 depositPreauthIndex = jrr[jss::node][jss::index].asString();
998 }
999 {
1000 // Request a depositPreauth by index.
1001 Json::Value jvParams;
1002 jvParams[jss::deposit_preauth] = depositPreauthIndex;
1003 jvParams[jss::ledger_hash] = ledgerHash;
1004 Json::Value const jrr = env.rpc(
1005 "json", "ledger_entry", to_string(jvParams))[jss::result];
1006
1007 BEAST_EXPECT(
1008 jrr[jss::node][sfLedgerEntryType.jsonName] ==
1009 jss::DepositPreauth);
1010 BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human());
1011 BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human());
1012 }
1013 {
1014 // test all missing/malformed field cases
1016 env,
1017 jss::deposit_preauth,
1018 {
1019 {jss::owner, "malformedOwner"},
1020 {jss::authorized, "malformedAuthorized", false},
1021 });
1022 }
1023 }
1024
1025 void
1027 {
1028 testcase("Deposit Preauth with credentials");
1029
1030 using namespace test::jtx;
1031
1032 Env env(*this);
1033 Account const issuer{"issuer"};
1034 Account const alice{"alice"};
1035 Account const bob{"bob"};
1036 char const credType[] = "abcde";
1037
1038 env.fund(XRP(5000), issuer, alice, bob);
1039 env.close();
1040
1041 {
1042 // Setup Bob with DepositAuth
1043 env(fset(bob, asfDepositAuth));
1044 env.close();
1045 env(deposit::authCredentials(bob, {{issuer, credType}}));
1046 env.close();
1047 }
1048
1049 {
1050 // Succeed
1051 Json::Value jvParams;
1052 jvParams[jss::ledger_index] = jss::validated;
1053 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1054
1055 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1057 auto& arr(
1058 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1059
1060 Json::Value jo;
1061 jo[jss::issuer] = issuer.human();
1062 jo[jss::credential_type] = strHex(std::string_view(credType));
1063 arr.append(std::move(jo));
1064 auto const jrr =
1065 env.rpc("json", "ledger_entry", to_string(jvParams));
1066
1067 BEAST_EXPECT(
1068 jrr.isObject() && jrr.isMember(jss::result) &&
1069 !jrr[jss::result].isMember(jss::error) &&
1070 jrr[jss::result].isMember(jss::node) &&
1071 jrr[jss::result][jss::node].isMember(
1072 sfLedgerEntryType.jsonName) &&
1073 jrr[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
1074 jss::DepositPreauth);
1075 }
1076
1077 {
1078 // Failed, invalid account
1079 Json::Value jvParams;
1080 jvParams[jss::ledger_index] = jss::validated;
1081 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1082
1083 auto tryField = [&](Json::Value fieldValue) -> void {
1085 Json::Value jo;
1086 jo[jss::issuer] = fieldValue;
1087 jo[jss::credential_type] = strHex(std::string_view(credType));
1088 arr.append(jo);
1089 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1090 arr;
1091
1092 Json::Value const jrr = env.rpc(
1093 "json", "ledger_entry", to_string(jvParams))[jss::result];
1094 auto const expectedErrMsg = fieldValue.isNull()
1095 ? RPC::missing_field_message(jss::issuer.c_str())
1096 : RPC::expected_field_message(jss::issuer, "AccountID");
1098 jrr, "malformedAuthorizedCredentials", expectedErrMsg);
1099 };
1100
1101 auto const& badValues = getBadValues(FieldType::AccountField);
1102 for (auto const& value : badValues)
1103 {
1104 tryField(value);
1105 }
1106 tryField(Json::nullValue);
1107 }
1108
1109 {
1110 // Failed, duplicates in credentials
1111 Json::Value jvParams;
1112 jvParams[jss::ledger_index] = jss::validated;
1113 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1114
1115 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1117 auto& arr(
1118 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1119
1120 Json::Value jo;
1121 jo[jss::issuer] = issuer.human();
1122 jo[jss::credential_type] = strHex(std::string_view(credType));
1123 arr.append(jo);
1124 arr.append(std::move(jo));
1125 auto const jrr =
1126 env.rpc("json", "ledger_entry", to_string(jvParams));
1128 jrr[jss::result],
1129 "malformedAuthorizedCredentials",
1131 jss::authorized_credentials, "array"));
1132 }
1133
1134 {
1135 // Failed, invalid credential_type
1136 Json::Value jvParams;
1137 jvParams[jss::ledger_index] = jss::validated;
1138 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1139
1140 auto tryField = [&](Json::Value fieldValue) -> void {
1142 Json::Value jo;
1143 jo[jss::issuer] = issuer.human();
1144 jo[jss::credential_type] = fieldValue;
1145 arr.append(jo);
1146 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1147 arr;
1148
1149 Json::Value const jrr = env.rpc(
1150 "json", "ledger_entry", to_string(jvParams))[jss::result];
1151 auto const expectedErrMsg = fieldValue.isNull()
1152 ? RPC::missing_field_message(jss::credential_type.c_str())
1154 jss::credential_type, "hex string");
1156 jrr, "malformedAuthorizedCredentials", expectedErrMsg);
1157 };
1158
1159 auto const& badValues = getBadValues(FieldType::BlobField);
1160 for (auto const& value : badValues)
1161 {
1162 tryField(value);
1163 }
1164 tryField(Json::nullValue);
1165 }
1166
1167 {
1168 // Failed, authorized and authorized_credentials both present
1169 Json::Value jvParams;
1170 jvParams[jss::ledger_index] = jss::validated;
1171 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1172 jvParams[jss::deposit_preauth][jss::authorized] = alice.human();
1173
1174 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1176 auto& arr(
1177 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1178
1179 Json::Value jo;
1180 jo[jss::issuer] = issuer.human();
1181 jo[jss::credential_type] = strHex(std::string_view(credType));
1182 arr.append(std::move(jo));
1183
1184 auto const jrr =
1185 env.rpc("json", "ledger_entry", to_string(jvParams));
1187 jrr[jss::result],
1188 "malformedRequest",
1189 "Must have exactly one of `authorized` and "
1190 "`authorized_credentials`.");
1191 }
1192
1193 {
1194 // Failed, authorized_credentials is not an array
1195 Json::Value jvParams;
1196 jvParams[jss::ledger_index] = jss::validated;
1197 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1199 env,
1200 jvParams,
1201 jss::deposit_preauth,
1202 jss::authorized_credentials,
1204 "malformedAuthorizedCredentials",
1205 false);
1206 }
1207
1208 {
1209 // Failed, authorized_credentials contains string data
1210 Json::Value jvParams;
1211 jvParams[jss::ledger_index] = jss::validated;
1212 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1213 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1215 auto& arr(
1216 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1217 arr.append("foobar");
1218
1219 auto const jrr =
1220 env.rpc("json", "ledger_entry", to_string(jvParams));
1222 jrr[jss::result],
1223 "malformedAuthorizedCredentials",
1224 "Invalid field 'authorized_credentials', not array.");
1225 }
1226
1227 {
1228 // Failed, authorized_credentials contains arrays
1229 Json::Value jvParams;
1230 jvParams[jss::ledger_index] = jss::validated;
1231 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1232 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1234 auto& arr(
1235 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1236 Json::Value payload = Json::arrayValue;
1237 payload.append(42);
1238 arr.append(std::move(payload));
1239
1240 auto const jrr =
1241 env.rpc("json", "ledger_entry", to_string(jvParams));
1243 jrr[jss::result],
1244 "malformedAuthorizedCredentials",
1245 "Invalid field 'authorized_credentials', not array.");
1246 }
1247
1248 {
1249 // Failed, authorized_credentials is empty array
1250 Json::Value jvParams;
1251 jvParams[jss::ledger_index] = jss::validated;
1252 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1253 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1255
1256 auto const jrr =
1257 env.rpc("json", "ledger_entry", to_string(jvParams));
1259 jrr[jss::result],
1260 "malformedAuthorizedCredentials",
1261 "Invalid field 'authorized_credentials', array empty.");
1262 }
1263
1264 {
1265 // Failed, authorized_credentials is too long
1266 static std::array<std::string_view, 9> const credTypes = {
1267 "cred1",
1268 "cred2",
1269 "cred3",
1270 "cred4",
1271 "cred5",
1272 "cred6",
1273 "cred7",
1274 "cred8",
1275 "cred9"};
1276 static_assert(
1277 sizeof(credTypes) / sizeof(credTypes[0]) >
1279
1280 Json::Value jvParams;
1281 jvParams[jss::ledger_index] = jss::validated;
1282 jvParams[jss::deposit_preauth][jss::owner] = bob.human();
1283 jvParams[jss::deposit_preauth][jss::authorized_credentials] =
1285
1286 auto& arr(
1287 jvParams[jss::deposit_preauth][jss::authorized_credentials]);
1288
1289 for (auto cred : credTypes)
1290 {
1291 Json::Value jo;
1292 jo[jss::issuer] = issuer.human();
1293 jo[jss::credential_type] = strHex(std::string_view(cred));
1294 arr.append(std::move(jo));
1295 }
1296
1297 auto const jrr =
1298 env.rpc("json", "ledger_entry", to_string(jvParams));
1300 jrr[jss::result],
1301 "malformedAuthorizedCredentials",
1302 "Invalid field 'authorized_credentials', array too long.");
1303 }
1304 }
1305
1306 void
1308 {
1309 testcase("Directory");
1310 using namespace test::jtx;
1311 Env env{*this};
1312 Account const alice{"alice"};
1313 Account const gw{"gateway"};
1314 auto const USD = gw["USD"];
1315 env.fund(XRP(10000), alice, gw);
1316 env.close();
1317
1318 env.trust(USD(1000), alice);
1319 env.close();
1320
1321 // Run up the number of directory entries so alice has two
1322 // directory nodes.
1323 for (int d = 1'000'032; d >= 1'000'000; --d)
1324 {
1325 env(offer(alice, USD(1), drops(d)));
1326 }
1327 env.close();
1328
1329 std::string const ledgerHash{to_string(env.closed()->header().hash)};
1330 {
1331 // Exercise ledger_closed along the way.
1332 Json::Value const jrr = env.rpc("ledger_closed")[jss::result];
1333 BEAST_EXPECT(jrr[jss::ledger_hash] == ledgerHash);
1334 BEAST_EXPECT(jrr[jss::ledger_index] == 5);
1335 }
1336
1337 std::string const dirRootIndex =
1338 "A33EC6BB85FB5674074C4A3A43373BB17645308F3EAE1933E3E35252162B217D";
1339 {
1340 // Locate directory by index.
1341 Json::Value jvParams;
1342 jvParams[jss::directory] = dirRootIndex;
1343 jvParams[jss::ledger_hash] = ledgerHash;
1344 Json::Value const jrr = env.rpc(
1345 "json", "ledger_entry", to_string(jvParams))[jss::result];
1346 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 32);
1347 }
1348 {
1349 // Locate directory by directory root.
1350 Json::Value jvParams;
1351 jvParams[jss::directory] = Json::objectValue;
1352 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1353 Json::Value const jrr = env.rpc(
1354 "json", "ledger_entry", to_string(jvParams))[jss::result];
1355 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1356 }
1357 {
1358 // Locate directory by owner.
1359 Json::Value jvParams;
1360 jvParams[jss::directory] = Json::objectValue;
1361 jvParams[jss::directory][jss::owner] = alice.human();
1362 jvParams[jss::ledger_hash] = ledgerHash;
1363 Json::Value const jrr = env.rpc(
1364 "json", "ledger_entry", to_string(jvParams))[jss::result];
1365 BEAST_EXPECT(jrr[jss::index] == dirRootIndex);
1366 }
1367 {
1368 // Locate directory by directory root and sub_index.
1369 Json::Value jvParams;
1370 jvParams[jss::directory] = Json::objectValue;
1371 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1372 jvParams[jss::directory][jss::sub_index] = 1;
1373 Json::Value const jrr = env.rpc(
1374 "json", "ledger_entry", to_string(jvParams))[jss::result];
1375 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1376 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1377 }
1378 {
1379 // Locate directory by owner and sub_index.
1380 Json::Value jvParams;
1381 jvParams[jss::directory] = Json::objectValue;
1382 jvParams[jss::directory][jss::owner] = alice.human();
1383 jvParams[jss::directory][jss::sub_index] = 1;
1384 jvParams[jss::ledger_hash] = ledgerHash;
1385 Json::Value const jrr = env.rpc(
1386 "json", "ledger_entry", to_string(jvParams))[jss::result];
1387 BEAST_EXPECT(jrr[jss::index] != dirRootIndex);
1388 BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2);
1389 }
1390 {
1391 // Bad directory argument.
1392 Json::Value jvParams;
1393 jvParams[jss::ledger_hash] = ledgerHash;
1395 env,
1396 jvParams,
1397 jss::directory,
1399 "malformedRequest");
1400 }
1401 {
1402 // Non-integer sub_index.
1403 Json::Value jvParams;
1404 jvParams[jss::directory] = Json::objectValue;
1405 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1406 jvParams[jss::ledger_hash] = ledgerHash;
1408 env,
1409 jvParams,
1410 jss::directory,
1411 jss::sub_index,
1413 "malformedRequest",
1414 false);
1415 }
1416 {
1417 // Malformed owner entry.
1418 Json::Value jvParams;
1419 jvParams[jss::directory] = Json::objectValue;
1420
1421 jvParams[jss::ledger_hash] = ledgerHash;
1423 env,
1424 jvParams,
1425 jss::directory,
1426 jss::owner,
1428 "malformedAddress",
1429 false);
1430 }
1431 {
1432 // Malformed directory object. Specifies both dir_root and owner.
1433 Json::Value jvParams;
1434 jvParams[jss::directory] = Json::objectValue;
1435 jvParams[jss::directory][jss::owner] = alice.human();
1436 jvParams[jss::directory][jss::dir_root] = dirRootIndex;
1437 jvParams[jss::ledger_hash] = ledgerHash;
1438 Json::Value const jrr = env.rpc(
1439 "json", "ledger_entry", to_string(jvParams))[jss::result];
1441 jrr,
1442 "malformedRequest",
1443 "Must have exactly one of `owner` and `dir_root` fields.");
1444 }
1445 {
1446 // Incomplete directory object. Missing both dir_root and owner.
1447 Json::Value jvParams;
1448 jvParams[jss::directory] = Json::objectValue;
1449 jvParams[jss::directory][jss::sub_index] = 1;
1450 jvParams[jss::ledger_hash] = ledgerHash;
1451 Json::Value const jrr = env.rpc(
1452 "json", "ledger_entry", to_string(jvParams))[jss::result];
1454 jrr,
1455 "malformedRequest",
1456 "Must have exactly one of `owner` and `dir_root` fields.");
1457 }
1458 }
1459
1460 void
1462 {
1463 testcase("Escrow");
1464 using namespace test::jtx;
1465 Env env{*this};
1466 Account const alice{"alice"};
1467 env.fund(XRP(10000), alice);
1468 env.close();
1469
1470 // Lambda to create an escrow.
1471 auto escrowCreate = [](test::jtx::Account const& account,
1472 test::jtx::Account const& to,
1473 STAmount const& amount,
1474 NetClock::time_point const& cancelAfter) {
1475 Json::Value jv;
1476 jv[jss::TransactionType] = jss::EscrowCreate;
1477 jv[jss::Account] = account.human();
1478 jv[jss::Destination] = to.human();
1479 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1480 jv[sfFinishAfter.jsonName] =
1481 cancelAfter.time_since_epoch().count() + 2;
1482 return jv;
1483 };
1484
1485 using namespace std::chrono_literals;
1486 env(escrowCreate(alice, alice, XRP(333), env.now() + 2s));
1487 env.close();
1488
1489 std::string const ledgerHash{to_string(env.closed()->header().hash)};
1490 std::string escrowIndex;
1491 {
1492 // Request the escrow using owner and sequence.
1493 Json::Value jvParams;
1494 jvParams[jss::escrow] = Json::objectValue;
1495 jvParams[jss::escrow][jss::owner] = alice.human();
1496 jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1;
1497 Json::Value const jrr = env.rpc(
1498 "json", "ledger_entry", to_string(jvParams))[jss::result];
1499 BEAST_EXPECT(
1500 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1501 escrowIndex = jrr[jss::index].asString();
1502 }
1503 {
1504 // Request the escrow by index.
1505 Json::Value jvParams;
1506 jvParams[jss::escrow] = escrowIndex;
1507 jvParams[jss::ledger_hash] = ledgerHash;
1508 Json::Value const jrr = env.rpc(
1509 "json", "ledger_entry", to_string(jvParams))[jss::result];
1510 BEAST_EXPECT(
1511 jrr[jss::node][jss::Amount] == XRP(333).value().getText());
1512 }
1513 {
1514 // Malformed escrow fields
1516 env,
1517 jss::escrow,
1518 {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}});
1519 }
1520 }
1521
1522 void
1524 {
1525 testcase("Fee Settings");
1526 using namespace test::jtx;
1527 Env env{*this};
1528
1529 // positive test
1530 {
1531 Keylet const keylet = keylet::fees();
1532 Json::Value jvParams;
1533 jvParams[jss::fee] = to_string(keylet.key);
1534 Json::Value const jrr = env.rpc(
1535 "json", "ledger_entry", to_string(jvParams))[jss::result];
1536 BEAST_EXPECT(
1537 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::FeeSettings);
1538 }
1539
1540 // negative tests
1541 runLedgerEntryTest(env, jss::fee);
1542 }
1543
1544 void
1546 {
1547 testcase("Ledger Hashes");
1548 using namespace test::jtx;
1549 Env env{*this};
1550
1551 // positive test
1552 {
1553 Keylet const keylet = keylet::skip();
1554 Json::Value jvParams;
1555 jvParams[jss::hashes] = to_string(keylet.key);
1556 Json::Value const jrr = env.rpc(
1557 "json", "ledger_entry", to_string(jvParams))[jss::result];
1558 BEAST_EXPECT(
1559 jrr[jss::node][sfLedgerEntryType.jsonName] ==
1560 jss::LedgerHashes);
1561 }
1562
1563 // negative tests
1564 runLedgerEntryTest(env, jss::hashes);
1565 }
1566
1567 void
1569 {
1570 testcase("NFT Offer");
1571 using namespace test::jtx;
1572 Env env{*this};
1573
1574 // positive test
1575 Account const issuer{"issuer"};
1576 Account const buyer{"buyer"};
1577 env.fund(XRP(1000), issuer, buyer);
1578
1579 uint256 const nftokenID0 =
1580 token::getNextID(env, issuer, 0, tfTransferable);
1581 env(token::mint(issuer, 0), txflags(tfTransferable));
1582 env.close();
1583 uint256 const offerID = keylet::nftoffer(issuer, env.seq(issuer)).key;
1584 env(token::createOffer(issuer, nftokenID0, drops(1)),
1585 token::destination(buyer),
1587
1588 {
1589 Json::Value jvParams;
1590 jvParams[jss::nft_offer] = to_string(offerID);
1591 Json::Value const jrr = env.rpc(
1592 "json", "ledger_entry", to_string(jvParams))[jss::result];
1593 BEAST_EXPECT(
1594 jrr[jss::node][sfLedgerEntryType.jsonName] ==
1595 jss::NFTokenOffer);
1596 BEAST_EXPECT(jrr[jss::node][sfOwner.jsonName] == issuer.human());
1597 BEAST_EXPECT(
1598 jrr[jss::node][sfNFTokenID.jsonName] == to_string(nftokenID0));
1599 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "1");
1600 }
1601
1602 // negative tests
1603 runLedgerEntryTest(env, jss::nft_offer);
1604 }
1605
1606 void
1608 {
1609 testcase("NFT Page");
1610 using namespace test::jtx;
1611 Env env{*this};
1612
1613 // positive test
1614 Account const issuer{"issuer"};
1615 env.fund(XRP(1000), issuer);
1616
1617 env(token::mint(issuer, 0), txflags(tfTransferable));
1618 env.close();
1619
1620 auto const nftpage = keylet::nftpage_max(issuer);
1621 BEAST_EXPECT(env.le(nftpage) != nullptr);
1622
1623 {
1624 Json::Value jvParams;
1625 jvParams[jss::nft_page] = to_string(nftpage.key);
1626 Json::Value const jrr = env.rpc(
1627 "json", "ledger_entry", to_string(jvParams))[jss::result];
1628 BEAST_EXPECT(
1629 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NFTokenPage);
1630 }
1631
1632 // negative tests
1633 runLedgerEntryTest(env, jss::nft_page);
1634 }
1635
1636 void
1638 {
1639 testcase("Negative UNL");
1640 using namespace test::jtx;
1641 Env env{*this};
1642
1643 // positive test
1644 {
1645 Keylet const keylet = keylet::negativeUNL();
1646
1647 // easier to hack an object into the ledger than generate it
1648 // legitimately
1649 {
1650 auto const nUNL = [&](OpenView& view, beast::Journal) -> bool {
1651 auto const sle = std::make_shared<SLE>(keylet);
1652
1653 // Create DisabledValidators array
1654 STArray disabledValidators;
1655 auto disabledValidator =
1656 STObject::makeInnerObject(sfDisabledValidator);
1657 auto pubKeyBlob = strUnHex(
1658 "ED58F6770DB5DD77E59D28CB650EC3816E2FC95021BB56E720C9A1"
1659 "2DA79C58A3AB");
1660 disabledValidator.setFieldVL(sfPublicKey, *pubKeyBlob);
1661 disabledValidator.setFieldU32(
1662 sfFirstLedgerSequence, 91371264);
1663 disabledValidators.push_back(std::move(disabledValidator));
1664
1665 sle->setFieldArray(
1666 sfDisabledValidators, disabledValidators);
1667 sle->setFieldH256(
1668 sfPreviousTxnID,
1669 uint256::fromVoid("8D47FFE664BE6C335108DF689537625855A6"
1670 "A95160CC6D351341B9"
1671 "2624D9C5E3"));
1672 sle->setFieldU32(sfPreviousTxnLgrSeq, 91442944);
1673
1674 view.rawInsert(sle);
1675 return true;
1676 };
1677 env.app().openLedger().modify(nUNL);
1678 }
1679
1680 Json::Value jvParams;
1681 jvParams[jss::nunl] = to_string(keylet.key);
1682 Json::Value const jrr = env.rpc(
1683 "json", "ledger_entry", to_string(jvParams))[jss::result];
1684 BEAST_EXPECT(
1685 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::NegativeUNL);
1686 }
1687
1688 // negative tests
1689 runLedgerEntryTest(env, jss::nunl);
1690 }
1691
1692 void
1694 {
1695 testcase("Offer");
1696 using namespace test::jtx;
1697 Env env{*this};
1698 Account const alice{"alice"};
1699 Account const gw{"gateway"};
1700 auto const USD = gw["USD"];
1701 env.fund(XRP(10000), alice, gw);
1702 env.close();
1703
1704 env(offer(alice, USD(321), XRP(322)));
1705 env.close();
1706
1707 std::string const ledgerHash{to_string(env.closed()->header().hash)};
1708 std::string offerIndex;
1709 {
1710 // Request the offer using owner and sequence.
1711 Json::Value jvParams;
1712 jvParams[jss::offer] = Json::objectValue;
1713 jvParams[jss::offer][jss::account] = alice.human();
1714 jvParams[jss::offer][jss::seq] = env.seq(alice) - 1;
1715 jvParams[jss::ledger_hash] = ledgerHash;
1716 Json::Value const jrr = env.rpc(
1717 "json", "ledger_entry", to_string(jvParams))[jss::result];
1718 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1719 offerIndex = jrr[jss::index].asString();
1720 }
1721 {
1722 // Request the offer using its index.
1723 Json::Value jvParams;
1724 jvParams[jss::offer] = offerIndex;
1725 Json::Value const jrr = env.rpc(
1726 "json", "ledger_entry", to_string(jvParams))[jss::result];
1727 BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000");
1728 }
1729
1730 {
1731 // Malformed offer fields
1733 env,
1734 jss::offer,
1735 {{jss::account, "malformedAddress"},
1736 {jss::seq, "malformedRequest"}});
1737 }
1738 }
1739
1740 void
1742 {
1743 testcase("Pay Chan");
1744 using namespace test::jtx;
1745 using namespace std::literals::chrono_literals;
1746 Env env{*this};
1747 Account const alice{"alice"};
1748
1749 env.fund(XRP(10000), alice);
1750 env.close();
1751
1752 // Lambda to create a PayChan.
1753 auto payChanCreate = [](test::jtx::Account const& account,
1754 test::jtx::Account const& to,
1755 STAmount const& amount,
1756 NetClock::duration const& settleDelay,
1757 PublicKey const& pk) {
1758 Json::Value jv;
1759 jv[jss::TransactionType] = jss::PaymentChannelCreate;
1760 jv[jss::Account] = account.human();
1761 jv[jss::Destination] = to.human();
1762 jv[jss::Amount] = amount.getJson(JsonOptions::none);
1763 jv[sfSettleDelay.jsonName] = settleDelay.count();
1764 jv[sfPublicKey.jsonName] = strHex(pk.slice());
1765 return jv;
1766 };
1767
1768 env(payChanCreate(alice, env.master, XRP(57), 18s, alice.pk()));
1769 env.close();
1770
1771 std::string const ledgerHash{to_string(env.closed()->header().hash)};
1772
1773 uint256 const payChanIndex{
1774 keylet::payChan(alice, env.master, env.seq(alice) - 1).key};
1775 {
1776 // Request the payment channel using its index.
1777 Json::Value jvParams;
1778 jvParams[jss::payment_channel] = to_string(payChanIndex);
1779 jvParams[jss::ledger_hash] = ledgerHash;
1780 Json::Value const jrr = env.rpc(
1781 "json", "ledger_entry", to_string(jvParams))[jss::result];
1782 BEAST_EXPECT(jrr[jss::node][sfAmount.jsonName] == "57000000");
1783 BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "0");
1784 BEAST_EXPECT(jrr[jss::node][sfSettleDelay.jsonName] == 18);
1785 }
1786 {
1787 // Request an index that is not a payment channel.
1788 Json::Value jvParams;
1789 jvParams[jss::payment_channel] = ledgerHash;
1790 jvParams[jss::ledger_hash] = ledgerHash;
1791 Json::Value const jrr = env.rpc(
1792 "json", "ledger_entry", to_string(jvParams))[jss::result];
1793 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
1794 }
1795
1796 {
1797 // Malformed paychan field
1798 runLedgerEntryTest(env, jss::payment_channel);
1799 }
1800 }
1801
1802 void
1804 {
1805 testcase("RippleState");
1806 using namespace test::jtx;
1807 Env env{*this};
1808 Account const alice{"alice"};
1809 Account const gw{"gateway"};
1810 auto const USD = gw["USD"];
1811 env.fund(XRP(10000), alice, gw);
1812 env.close();
1813
1814 env.trust(USD(999), alice);
1815 env.close();
1816
1817 env(pay(gw, alice, USD(97)));
1818 env.close();
1819
1820 // check both aliases
1821 for (auto const& fieldName : {jss::ripple_state, jss::state})
1822 {
1823 std::string const ledgerHash{
1824 to_string(env.closed()->header().hash)};
1825 {
1826 // Request the trust line using the accounts and currency.
1827 Json::Value jvParams;
1828 jvParams[fieldName] = Json::objectValue;
1829 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1830 jvParams[fieldName][jss::accounts][0u] = alice.human();
1831 jvParams[fieldName][jss::accounts][1u] = gw.human();
1832 jvParams[fieldName][jss::currency] = "USD";
1833 jvParams[jss::ledger_hash] = ledgerHash;
1834 Json::Value const jrr = env.rpc(
1835 "json", "ledger_entry", to_string(jvParams))[jss::result];
1836 BEAST_EXPECT(
1837 jrr[jss::node][sfBalance.jsonName][jss::value] == "-97");
1838 BEAST_EXPECT(
1839 jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999");
1840 }
1841 {
1842 // test basic malformed scenarios
1844 env,
1845 fieldName,
1846 {
1847 {jss::accounts, "malformedRequest"},
1848 {jss::currency, "malformedCurrency"},
1849 });
1850 }
1851 {
1852 // ripple_state one of the accounts is missing.
1853 Json::Value jvParams;
1854 jvParams[fieldName] = Json::objectValue;
1855 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1856 jvParams[fieldName][jss::accounts][0u] = alice.human();
1857 jvParams[fieldName][jss::currency] = "USD";
1858 jvParams[jss::ledger_hash] = ledgerHash;
1859 Json::Value const jrr = env.rpc(
1860 "json", "ledger_entry", to_string(jvParams))[jss::result];
1862 jrr,
1863 "malformedRequest",
1864 "Invalid field 'accounts', not length-2 array of "
1865 "Accounts.");
1866 }
1867 {
1868 // ripple_state more than 2 accounts.
1869 Json::Value jvParams;
1870 jvParams[fieldName] = Json::objectValue;
1871 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1872 jvParams[fieldName][jss::accounts][0u] = alice.human();
1873 jvParams[fieldName][jss::accounts][1u] = gw.human();
1874 jvParams[fieldName][jss::accounts][2u] = alice.human();
1875 jvParams[fieldName][jss::currency] = "USD";
1876 jvParams[jss::ledger_hash] = ledgerHash;
1877 Json::Value const jrr = env.rpc(
1878 "json", "ledger_entry", to_string(jvParams))[jss::result];
1880 jrr,
1881 "malformedRequest",
1882 "Invalid field 'accounts', not length-2 array of "
1883 "Accounts.");
1884 }
1885 {
1886 // ripple_state account[0] / account[1] is not an account.
1887 Json::Value jvParams;
1888 jvParams[fieldName] = Json::objectValue;
1889 auto tryField = [&](Json::Value badAccount) -> void {
1890 {
1891 // account[0]
1892 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1893 jvParams[fieldName][jss::accounts][0u] = badAccount;
1894 jvParams[fieldName][jss::accounts][1u] = gw.human();
1895 jvParams[fieldName][jss::currency] = "USD";
1896
1897 Json::Value const jrr = env.rpc(
1898 "json",
1899 "ledger_entry",
1900 to_string(jvParams))[jss::result];
1902 jrr,
1903 "malformedAddress",
1905 jss::accounts, "array of Accounts"));
1906 }
1907
1908 {
1909 // account[1]
1910 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1911 jvParams[fieldName][jss::accounts][0u] = alice.human();
1912 jvParams[fieldName][jss::accounts][1u] = badAccount;
1913 jvParams[fieldName][jss::currency] = "USD";
1914
1915 Json::Value const jrr = env.rpc(
1916 "json",
1917 "ledger_entry",
1918 to_string(jvParams))[jss::result];
1920 jrr,
1921 "malformedAddress",
1923 jss::accounts, "array of Accounts"));
1924 }
1925 };
1926
1927 auto const& badValues = getBadValues(FieldType::AccountField);
1928 for (auto const& value : badValues)
1929 {
1930 tryField(value);
1931 }
1932 tryField(Json::nullValue);
1933 }
1934 {
1935 // ripple_state account[0] == account[1].
1936 Json::Value jvParams;
1937 jvParams[fieldName] = Json::objectValue;
1938 jvParams[fieldName][jss::accounts] = Json::arrayValue;
1939 jvParams[fieldName][jss::accounts][0u] = alice.human();
1940 jvParams[fieldName][jss::accounts][1u] = alice.human();
1941 jvParams[fieldName][jss::currency] = "USD";
1942 jvParams[jss::ledger_hash] = ledgerHash;
1943 Json::Value const jrr = env.rpc(
1944 "json", "ledger_entry", to_string(jvParams))[jss::result];
1946 jrr,
1947 "malformedRequest",
1948 "Cannot have a trustline to self.");
1949 }
1950 }
1951 }
1952
1953 void
1955 {
1956 testcase("Signer List");
1957 using namespace test::jtx;
1958 Env env{*this};
1959 runLedgerEntryTest(env, jss::signer_list);
1960 }
1961
1962 void
1964 {
1965 testcase("Ticket");
1966 using namespace test::jtx;
1967 Env env{*this};
1968 env.close();
1969
1970 // Create two tickets.
1971 std::uint32_t const tkt1{env.seq(env.master) + 1};
1972 env(ticket::create(env.master, 2));
1973 env.close();
1974
1975 std::string const ledgerHash{to_string(env.closed()->header().hash)};
1976 // Request four tickets: one before the first one we created, the
1977 // two created tickets, and the ticket that would come after the
1978 // last created ticket.
1979 {
1980 // Not a valid ticket requested by index.
1981 Json::Value jvParams;
1982 jvParams[jss::ticket] =
1983 to_string(getTicketIndex(env.master, tkt1 - 1));
1984 jvParams[jss::ledger_hash] = ledgerHash;
1985 Json::Value const jrr = env.rpc(
1986 "json", "ledger_entry", to_string(jvParams))[jss::result];
1987 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
1988 }
1989 {
1990 // First real ticket requested by index.
1991 Json::Value jvParams;
1992 jvParams[jss::ticket] = to_string(getTicketIndex(env.master, tkt1));
1993 jvParams[jss::ledger_hash] = ledgerHash;
1994 Json::Value const jrr = env.rpc(
1995 "json", "ledger_entry", to_string(jvParams))[jss::result];
1996 BEAST_EXPECT(
1997 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Ticket);
1998 BEAST_EXPECT(jrr[jss::node][sfTicketSequence.jsonName] == tkt1);
1999 }
2000 {
2001 // Second real ticket requested by account and sequence.
2002 Json::Value jvParams;
2003 jvParams[jss::ticket] = Json::objectValue;
2004 jvParams[jss::ticket][jss::account] = env.master.human();
2005 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 1;
2006 jvParams[jss::ledger_hash] = ledgerHash;
2007 Json::Value const jrr = env.rpc(
2008 "json", "ledger_entry", to_string(jvParams))[jss::result];
2009 BEAST_EXPECT(
2010 jrr[jss::node][jss::index] ==
2011 to_string(getTicketIndex(env.master, tkt1 + 1)));
2012 }
2013 {
2014 // Not a valid ticket requested by account and sequence.
2015 Json::Value jvParams;
2016 jvParams[jss::ticket] = Json::objectValue;
2017 jvParams[jss::ticket][jss::account] = env.master.human();
2018 jvParams[jss::ticket][jss::ticket_seq] = tkt1 + 2;
2019 jvParams[jss::ledger_hash] = ledgerHash;
2020 Json::Value const jrr = env.rpc(
2021 "json", "ledger_entry", to_string(jvParams))[jss::result];
2022 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2023 }
2024 {
2025 // Request a ticket using an account root entry.
2026 Json::Value jvParams;
2027 jvParams[jss::ticket] = to_string(keylet::account(env.master).key);
2028 jvParams[jss::ledger_hash] = ledgerHash;
2029 Json::Value const jrr = env.rpc(
2030 "json", "ledger_entry", to_string(jvParams))[jss::result];
2032 jrr, "unexpectedLedgerType", "Unexpected ledger type.");
2033 }
2034
2035 {
2036 // test basic malformed scenarios
2038 env,
2039 jss::ticket,
2040 {
2041 {jss::account, "malformedAddress"},
2042 {jss::ticket_seq, "malformedRequest"},
2043 });
2044 }
2045 }
2046
2047 void
2049 {
2050 testcase("DID");
2051 using namespace test::jtx;
2052 using namespace std::literals::chrono_literals;
2053 Env env{*this};
2054 Account const alice{"alice"};
2055
2056 env.fund(XRP(10000), alice);
2057 env.close();
2058
2059 // Lambda to create a DID.
2060 auto didCreate = [](test::jtx::Account const& account) {
2061 Json::Value jv;
2062 jv[jss::TransactionType] = jss::DIDSet;
2063 jv[jss::Account] = account.human();
2064 jv[sfDIDDocument.jsonName] = strHex(std::string{"data"});
2065 jv[sfURI.jsonName] = strHex(std::string{"uri"});
2066 return jv;
2067 };
2068
2069 env(didCreate(alice));
2070 env.close();
2071
2072 std::string const ledgerHash{to_string(env.closed()->header().hash)};
2073
2074 {
2075 // Request the DID using its index.
2076 Json::Value jvParams;
2077 jvParams[jss::did] = alice.human();
2078 jvParams[jss::ledger_hash] = ledgerHash;
2079 Json::Value const jrr = env.rpc(
2080 "json", "ledger_entry", to_string(jvParams))[jss::result];
2081 BEAST_EXPECT(
2082 jrr[jss::node][sfDIDDocument.jsonName] ==
2083 strHex(std::string{"data"}));
2084 BEAST_EXPECT(
2085 jrr[jss::node][sfURI.jsonName] == strHex(std::string{"uri"}));
2086 }
2087 {
2088 // Request an index that is not a DID.
2089 Json::Value jvParams;
2090 jvParams[jss::did] = env.master.human();
2091 jvParams[jss::ledger_hash] = ledgerHash;
2092 Json::Value const jrr = env.rpc(
2093 "json", "ledger_entry", to_string(jvParams))[jss::result];
2094 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2095 }
2096 {
2097 // Malformed DID index
2098 Json::Value jvParams;
2100 env,
2101 jvParams,
2102 jss::did,
2104 "malformedAddress");
2105 }
2106 }
2107
2108 void
2110 {
2111 testcase("Invalid Oracle Ledger Entry");
2112 using namespace xrpl::test::jtx;
2113 using namespace xrpl::test::jtx::oracle;
2114
2115 Env env(*this);
2116 Account const owner("owner");
2117 env.fund(XRP(1'000), owner);
2118 Oracle oracle(
2119 env,
2120 {.owner = owner,
2121 .fee = static_cast<int>(env.current()->fees().base.drops())});
2122
2123 {
2124 // test basic malformed scenarios
2126 env,
2127 jss::oracle,
2128 {
2129 {jss::account, "malformedAccount"},
2130 {jss::oracle_document_id, "malformedDocumentID"},
2131 });
2132 }
2133 }
2134
2135 void
2137 {
2138 testcase("Oracle Ledger Entry");
2139 using namespace xrpl::test::jtx;
2140 using namespace xrpl::test::jtx::oracle;
2141
2142 Env env(*this);
2143 auto const baseFee =
2144 static_cast<int>(env.current()->fees().base.drops());
2145 std::vector<AccountID> accounts;
2147 for (int i = 0; i < 10; ++i)
2148 {
2149 Account const owner(std::string("owner") + std::to_string(i));
2150 env.fund(XRP(1'000), owner);
2151 // different accounts can have the same asset pair
2152 Oracle oracle(
2153 env, {.owner = owner, .documentID = i, .fee = baseFee});
2154 accounts.push_back(owner.id());
2155 oracles.push_back(oracle.documentID());
2156 // same account can have different asset pair
2157 Oracle oracle1(
2158 env, {.owner = owner, .documentID = i + 10, .fee = baseFee});
2159 accounts.push_back(owner.id());
2160 oracles.push_back(oracle1.documentID());
2161 }
2162 for (int i = 0; i < accounts.size(); ++i)
2163 {
2164 auto const jv = [&]() {
2165 // document id is uint32
2166 if (i % 2)
2167 return Oracle::ledgerEntry(env, accounts[i], oracles[i]);
2168 // document id is string
2169 return Oracle::ledgerEntry(
2170 env, accounts[i], std::to_string(oracles[i]));
2171 }();
2172 try
2173 {
2174 BEAST_EXPECT(
2175 jv[jss::node][jss::Owner] == to_string(accounts[i]));
2176 }
2177 catch (...)
2178 {
2179 fail();
2180 }
2181 }
2182 }
2183
2184 void
2186 {
2187 testcase("MPT");
2188 using namespace test::jtx;
2189 using namespace std::literals::chrono_literals;
2190 Env env{*this};
2191 Account const alice{"alice"};
2192 Account const bob("bob");
2193
2194 MPTTester mptAlice(env, alice, {.holders = {bob}});
2195 mptAlice.create(
2196 {.transferFee = 10,
2197 .metadata = "123",
2198 .ownerCount = 1,
2201 mptAlice.authorize({.account = bob, .holderCount = 1});
2202
2203 std::string const ledgerHash{to_string(env.closed()->header().hash)};
2204
2205 std::string const badMptID =
2206 "00000193B9DDCAF401B5B3B26875986043F82CD0D13B4315";
2207 {
2208 // Request the MPTIssuance using its MPTIssuanceID.
2209 Json::Value jvParams;
2210 jvParams[jss::mpt_issuance] = strHex(mptAlice.issuanceID());
2211 jvParams[jss::ledger_hash] = ledgerHash;
2212 Json::Value const jrr = env.rpc(
2213 "json", "ledger_entry", to_string(jvParams))[jss::result];
2214 BEAST_EXPECT(
2215 jrr[jss::node][sfMPTokenMetadata.jsonName] ==
2216 strHex(std::string{"123"}));
2217 BEAST_EXPECT(
2218 jrr[jss::node][jss::mpt_issuance_id] ==
2219 strHex(mptAlice.issuanceID()));
2220 }
2221 {
2222 // Request an index that is not a MPTIssuance.
2223 Json::Value jvParams;
2224 jvParams[jss::mpt_issuance] = badMptID;
2225 jvParams[jss::ledger_hash] = ledgerHash;
2226 Json::Value const jrr = env.rpc(
2227 "json", "ledger_entry", to_string(jvParams))[jss::result];
2228 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2229 }
2230 {
2231 // Request the MPToken using its owner + mptIssuanceID.
2232 Json::Value jvParams;
2233 jvParams[jss::mptoken] = Json::objectValue;
2234 jvParams[jss::mptoken][jss::account] = bob.human();
2235 jvParams[jss::mptoken][jss::mpt_issuance_id] =
2236 strHex(mptAlice.issuanceID());
2237 jvParams[jss::ledger_hash] = ledgerHash;
2238 Json::Value const jrr = env.rpc(
2239 "json", "ledger_entry", to_string(jvParams))[jss::result];
2240 BEAST_EXPECT(
2241 jrr[jss::node][sfMPTokenIssuanceID.jsonName] ==
2242 strHex(mptAlice.issuanceID()));
2243 }
2244 {
2245 // Request the MPToken using a bad mptIssuanceID.
2246 Json::Value jvParams;
2247 jvParams[jss::mptoken] = Json::objectValue;
2248 jvParams[jss::mptoken][jss::account] = bob.human();
2249 jvParams[jss::mptoken][jss::mpt_issuance_id] = badMptID;
2250 jvParams[jss::ledger_hash] = ledgerHash;
2251 Json::Value const jrr = env.rpc(
2252 "json", "ledger_entry", to_string(jvParams))[jss::result];
2253 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2254 }
2255 {
2256 // Malformed MPTIssuance index
2257 Json::Value jvParams;
2259 env,
2260 jvParams,
2261 jss::mptoken,
2263 "malformedRequest");
2264 }
2265 }
2266
2267 void
2269 {
2270 testcase("PermissionedDomain");
2271
2272 using namespace test::jtx;
2273
2274 Env env(*this, testable_amendments() | featurePermissionedDomains);
2275 Account const issuer{"issuer"};
2276 Account const alice{"alice"};
2277 Account const bob{"bob"};
2278
2279 env.fund(XRP(5000), issuer, alice, bob);
2280 env.close();
2281
2282 auto const seq = env.seq(alice);
2283 env(pdomain::setTx(alice, {{alice, "first credential"}}));
2284 env.close();
2285 auto const objects = pdomain::getObjects(alice, env);
2286 if (!BEAST_EXPECT(objects.size() == 1))
2287 return;
2288
2289 {
2290 // Succeed
2291 Json::Value params;
2292 params[jss::ledger_index] = jss::validated;
2293 params[jss::permissioned_domain][jss::account] = alice.human();
2294 params[jss::permissioned_domain][jss::seq] = seq;
2295 auto jv = env.rpc("json", "ledger_entry", to_string(params));
2296 BEAST_EXPECT(
2297 jv.isObject() && jv.isMember(jss::result) &&
2298 !jv[jss::result].isMember(jss::error) &&
2299 jv[jss::result].isMember(jss::node) &&
2300 jv[jss::result][jss::node].isMember(
2301 sfLedgerEntryType.jsonName) &&
2302 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2303 jss::PermissionedDomain);
2304
2305 std::string const pdIdx = jv[jss::result][jss::index].asString();
2306 BEAST_EXPECT(
2307 strHex(keylet::permissionedDomain(alice, seq).key) == pdIdx);
2308
2309 params.clear();
2310 params[jss::ledger_index] = jss::validated;
2311 params[jss::permissioned_domain] = pdIdx;
2312 jv = env.rpc("json", "ledger_entry", to_string(params));
2313 BEAST_EXPECT(
2314 jv.isObject() && jv.isMember(jss::result) &&
2315 !jv[jss::result].isMember(jss::error) &&
2316 jv[jss::result].isMember(jss::node) &&
2317 jv[jss::result][jss::node].isMember(
2318 sfLedgerEntryType.jsonName) &&
2319 jv[jss::result][jss::node][sfLedgerEntryType.jsonName] ==
2320 jss::PermissionedDomain);
2321 }
2322
2323 {
2324 // Fail, invalid permissioned domain index
2325 Json::Value params;
2326 params[jss::ledger_index] = jss::validated;
2327 params[jss::permissioned_domain] =
2328 "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A"
2329 "DE";
2330 auto const jrr = env.rpc("json", "ledger_entry", to_string(params));
2332 jrr[jss::result], "entryNotFound", "Entry not found.");
2333 }
2334 {
2335 // test basic malformed scenarios
2337 env,
2338 jss::permissioned_domain,
2339 {
2340 {jss::account, "malformedAddress"},
2341 {jss::seq, "malformedRequest"},
2342 });
2343 }
2344 }
2345
2346 void
2348 {
2349 testcase("command-line");
2350 using namespace test::jtx;
2351
2352 Env env{*this};
2353 Account const alice{"alice"};
2354 env.fund(XRP(10000), alice);
2355 env.close();
2356
2357 auto const checkId = keylet::check(env.master, env.seq(env.master));
2358
2359 env(check::create(env.master, alice, XRP(100)));
2360 env.close();
2361
2362 std::string const ledgerHash{to_string(env.closed()->header().hash)};
2363 {
2364 // Request a check.
2365 Json::Value const jrr =
2366 env.rpc("ledger_entry", to_string(checkId.key))[jss::result];
2367 BEAST_EXPECT(
2368 jrr[jss::node][sfLedgerEntryType.jsonName] == jss::Check);
2369 BEAST_EXPECT(jrr[jss::node][sfSendMax.jsonName] == "100000000");
2370 }
2371 }
2372
2373public:
2374 void
2375 run() override
2376 {
2377 testInvalid();
2380 testAMM();
2381 testCheck();
2383 testDelegate();
2386 testDirectory();
2387 testEscrow();
2393 testOffer();
2394 testPayChan();
2397 testTicket();
2398 testDID();
2401 testMPT();
2403 testCLI();
2404 }
2405};
2406
2409{
2410 void
2412 Json::Value const& jv,
2413 std::string const& err,
2414 std::string const& msg)
2415 {
2416 if (BEAST_EXPECT(jv.isMember(jss::status)))
2417 BEAST_EXPECT(jv[jss::status] == "error");
2418 if (BEAST_EXPECT(jv.isMember(jss::error)))
2419 BEAST_EXPECT(jv[jss::error] == err);
2420 if (msg.empty())
2421 {
2422 BEAST_EXPECT(
2423 jv[jss::error_message] == Json::nullValue ||
2424 jv[jss::error_message] == "");
2425 }
2426 else if (BEAST_EXPECT(jv.isMember(jss::error_message)))
2427 BEAST_EXPECT(jv[jss::error_message] == msg);
2428 }
2429
2430 void
2432 {
2433 testcase("ledger_entry: bridge");
2434 using namespace test::jtx;
2435
2436 Env mcEnv{*this, features};
2437 Env scEnv(*this, envconfig(), features);
2438
2439 createBridgeObjects(mcEnv, scEnv);
2440
2441 std::string const ledgerHash{to_string(mcEnv.closed()->header().hash)};
2442 std::string bridge_index;
2443 Json::Value mcBridge;
2444 {
2445 // request the bridge via RPC
2446 Json::Value jvParams;
2447 jvParams[jss::bridge_account] = mcDoor.human();
2448 jvParams[jss::bridge] = jvb;
2449 Json::Value const jrr = mcEnv.rpc(
2450 "json", "ledger_entry", to_string(jvParams))[jss::result];
2451
2452 BEAST_EXPECT(jrr.isMember(jss::node));
2453 auto r = jrr[jss::node];
2454
2455 BEAST_EXPECT(r.isMember(jss::Account));
2456 BEAST_EXPECT(r[jss::Account] == mcDoor.human());
2457
2458 BEAST_EXPECT(r.isMember(jss::Flags));
2459
2460 BEAST_EXPECT(r.isMember(sfLedgerEntryType.jsonName));
2461 BEAST_EXPECT(r[sfLedgerEntryType.jsonName] == jss::Bridge);
2462
2463 // we not created an account yet
2464 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2465 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 0);
2466
2467 // we have not claimed a locking chain tx yet
2468 BEAST_EXPECT(r.isMember(sfXChainAccountClaimCount.jsonName));
2469 BEAST_EXPECT(r[sfXChainAccountClaimCount.jsonName].asInt() == 0);
2470
2471 BEAST_EXPECT(r.isMember(jss::index));
2472 bridge_index = r[jss::index].asString();
2473 mcBridge = r;
2474 }
2475 {
2476 // request the bridge via RPC by index
2477 Json::Value jvParams;
2478 jvParams[jss::index] = bridge_index;
2479 Json::Value const jrr = mcEnv.rpc(
2480 "json", "ledger_entry", to_string(jvParams))[jss::result];
2481
2482 BEAST_EXPECT(jrr.isMember(jss::node));
2483 BEAST_EXPECT(jrr[jss::node] == mcBridge);
2484 }
2485 {
2486 // swap door accounts and make sure we get an error value
2487 Json::Value jvParams;
2488 // Sidechain door account is "master", not scDoor
2489 jvParams[jss::bridge_account] = Account::master.human();
2490 jvParams[jss::bridge] = jvb;
2491 jvParams[jss::ledger_hash] = ledgerHash;
2492 Json::Value const jrr = mcEnv.rpc(
2493 "json", "ledger_entry", to_string(jvParams))[jss::result];
2494
2495 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2496 }
2497 {
2498 // create two claim ids and verify that the bridge counter was
2499 // incremented
2501 mcEnv.close();
2503 mcEnv.close();
2504
2505 // request the bridge via RPC
2506 Json::Value jvParams;
2507 jvParams[jss::bridge_account] = mcDoor.human();
2508 jvParams[jss::bridge] = jvb;
2509 Json::Value const jrr = mcEnv.rpc(
2510 "json", "ledger_entry", to_string(jvParams))[jss::result];
2511
2512 BEAST_EXPECT(jrr.isMember(jss::node));
2513 auto r = jrr[jss::node];
2514
2515 // we executed two create claim id txs
2516 BEAST_EXPECT(r.isMember(sfXChainClaimID.jsonName));
2517 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2518 }
2519 }
2520
2521 void
2523 {
2524 testcase("ledger_entry: xchain_claim_id");
2525 using namespace test::jtx;
2526
2527 Env mcEnv{*this, features};
2528 Env scEnv(*this, envconfig(), features);
2529
2530 createBridgeObjects(mcEnv, scEnv);
2531
2533 scEnv.close();
2535 scEnv.close();
2536
2537 std::string bridge_index;
2538 {
2539 // request the xchain_claim_id via RPC
2540 Json::Value jvParams;
2541 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2542 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2543 1;
2544 Json::Value const jrr = scEnv.rpc(
2545 "json", "ledger_entry", to_string(jvParams))[jss::result];
2546
2547 BEAST_EXPECT(jrr.isMember(jss::node));
2548 auto r = jrr[jss::node];
2549
2550 BEAST_EXPECT(r.isMember(jss::Account));
2551 BEAST_EXPECT(r[jss::Account] == scAlice.human());
2552 BEAST_EXPECT(
2553 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2554 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 1);
2555 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2556 }
2557
2558 {
2559 // request the xchain_claim_id via RPC
2560 Json::Value jvParams;
2561 jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC;
2562 jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] =
2563 2;
2564 Json::Value const jrr = scEnv.rpc(
2565 "json", "ledger_entry", to_string(jvParams))[jss::result];
2566
2567 BEAST_EXPECT(jrr.isMember(jss::node));
2568 auto r = jrr[jss::node];
2569
2570 BEAST_EXPECT(r.isMember(jss::Account));
2571 BEAST_EXPECT(r[jss::Account] == scBob.human());
2572 BEAST_EXPECT(
2573 r[sfLedgerEntryType.jsonName] == jss::XChainOwnedClaimID);
2574 BEAST_EXPECT(r[sfXChainClaimID.jsonName].asInt() == 2);
2575 BEAST_EXPECT(r[sfOwnerNode.jsonName].asInt() == 0);
2576 }
2577 }
2578
2579 void
2581 {
2582 testcase("ledger_entry: xchain_create_account_claim_id");
2583 using namespace test::jtx;
2584
2585 Env mcEnv{*this, features};
2586 Env scEnv(*this, envconfig(), features);
2587
2588 // note: signers.size() and quorum are both 5 in createBridgeObjects
2589 createBridgeObjects(mcEnv, scEnv);
2590
2591 auto scCarol =
2592 Account("scCarol"); // Don't fund it - it will be created with the
2593 // xchain transaction
2594 auto const amt = XRP(1000);
2596 mcAlice, jvb, scCarol, amt, reward));
2597 mcEnv.close();
2598
2599 // send less than quorum of attestations (otherwise funds are
2600 // immediately transferred and no "claim" object is created)
2601 size_t constexpr num_attest = 3;
2602 auto attestations = create_account_attestations(
2603 scAttester,
2604 jvb,
2605 mcAlice,
2606 amt,
2607 reward,
2608 payee,
2609 /*wasLockingChainSend*/ true,
2610 1,
2611 scCarol,
2612 signers,
2614 for (size_t i = 0; i < num_attest; ++i)
2615 {
2616 scEnv(attestations[i]);
2617 }
2618 scEnv.close();
2619
2620 {
2621 // request the create account claim_id via RPC
2622 Json::Value jvParams;
2623 jvParams[jss::xchain_owned_create_account_claim_id] =
2625 jvParams[jss::xchain_owned_create_account_claim_id]
2626 [jss::xchain_owned_create_account_claim_id] = 1;
2627 Json::Value const jrr = scEnv.rpc(
2628 "json", "ledger_entry", to_string(jvParams))[jss::result];
2629
2630 BEAST_EXPECT(jrr.isMember(jss::node));
2631 auto r = jrr[jss::node];
2632
2633 BEAST_EXPECT(r.isMember(jss::Account));
2634 BEAST_EXPECT(r[jss::Account] == Account::master.human());
2635
2636 BEAST_EXPECT(r.isMember(sfXChainAccountCreateCount.jsonName));
2637 BEAST_EXPECT(r[sfXChainAccountCreateCount.jsonName].asInt() == 1);
2638
2639 BEAST_EXPECT(
2640 r.isMember(sfXChainCreateAccountAttestations.jsonName));
2641 auto attest = r[sfXChainCreateAccountAttestations.jsonName];
2642 BEAST_EXPECT(attest.isArray());
2643 BEAST_EXPECT(attest.size() == 3);
2644 BEAST_EXPECT(attest[Json::Value::UInt(0)].isMember(
2645 sfXChainCreateAccountProofSig.jsonName));
2646 Json::Value a[num_attest];
2647 for (size_t i = 0; i < num_attest; ++i)
2648 {
2649 a[i] = attest[Json::Value::UInt(0)]
2650 [sfXChainCreateAccountProofSig.jsonName];
2651 BEAST_EXPECT(
2652 a[i].isMember(jss::Amount) &&
2653 a[i][jss::Amount].asInt() == 1000 * drop_per_xrp);
2654 BEAST_EXPECT(
2655 a[i].isMember(jss::Destination) &&
2656 a[i][jss::Destination] == scCarol.human());
2657 BEAST_EXPECT(
2658 a[i].isMember(sfAttestationSignerAccount.jsonName) &&
2660 signers.begin(), signers.end(), [&](signer const& s) {
2661 return a[i][sfAttestationSignerAccount.jsonName] ==
2662 s.account.human();
2663 }));
2664 BEAST_EXPECT(
2665 a[i].isMember(sfAttestationRewardAccount.jsonName) &&
2667 payee.begin(),
2668 payee.end(),
2669 [&](Account const& account) {
2670 return a[i][sfAttestationRewardAccount.jsonName] ==
2671 account.human();
2672 }));
2673 BEAST_EXPECT(
2674 a[i].isMember(sfWasLockingChainSend.jsonName) &&
2675 a[i][sfWasLockingChainSend.jsonName] == 1);
2676 BEAST_EXPECT(
2677 a[i].isMember(sfSignatureReward.jsonName) &&
2678 a[i][sfSignatureReward.jsonName].asInt() ==
2679 1 * drop_per_xrp);
2680 }
2681 }
2682
2683 // complete attestations quorum - CreateAccountClaimID should not be
2684 // present anymore
2685 for (size_t i = num_attest; i < UT_XCHAIN_DEFAULT_NUM_SIGNERS; ++i)
2686 {
2687 scEnv(attestations[i]);
2688 }
2689 scEnv.close();
2690 {
2691 // request the create account claim_id via RPC
2692 Json::Value jvParams;
2693 jvParams[jss::xchain_owned_create_account_claim_id] =
2695 jvParams[jss::xchain_owned_create_account_claim_id]
2696 [jss::xchain_owned_create_account_claim_id] = 1;
2697 Json::Value const jrr = scEnv.rpc(
2698 "json", "ledger_entry", to_string(jvParams))[jss::result];
2699 checkErrorValue(jrr, "entryNotFound", "Entry not found.");
2700 }
2701 }
2702
2703public:
2704 void
2705 run() override
2706 {
2707 testBridge();
2708 testClaimID();
2710 }
2711};
2712
2713BEAST_DEFINE_TESTSUITE(LedgerEntry, rpc, xrpl);
2714BEAST_DEFINE_TESTSUITE(LedgerEntry_XChain, rpc, xrpl);
2715
2716} // namespace test
2717} // namespace xrpl
T any_of(T... args)
T begin(T... args)
Lightweight wrapper to tag static string.
Definition json_value.h:45
constexpr char const * c_str() const
Definition json_value.h:58
Represents a JSON value.
Definition json_value.h:131
Json::UInt UInt
Definition json_value.h:138
Value & append(Value const &value)
Append value to array at the end.
UInt size() const
Number of values in array or object.
std::string toStyledString() const
void clear()
Remove all object members and array elements.
Int asInt() const
bool isObject() const
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:41
A testsuite class.
Definition suite.h:52
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:530
virtual OpenLedger & openLedger()=0
bool modify(modify_type const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:46
void rawInsert(std::shared_ptr< SLE > const &sle) override
Unconditionally insert a state item.
Definition OpenView.cpp:218
A public key.
Definition PublicKey.h:43
void push_back(STObject const &object)
Definition STArray.h:193
static STObject makeInnerObject(SField const &name)
Definition STObject.cpp:76
static base_uint fromVoid(void const *data)
Definition base_uint.h:300
void run() override
Runs the suite.
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg)
void testMalformedSubfield(test::jtx::Env &env, Json::Value correctRequest, Json::StaticString parentFieldName, Json::StaticString fieldName, FieldType typeID, std::string const &expectedError, bool required=true, source_location const location=source_location::current())
void run() override
Runs the suite.
void testMalformedField(test::jtx::Env &env, Json::Value correctRequest, Json::StaticString const fieldName, FieldType const typeID, std::string const &expectedError, bool required=true, source_location const location=source_location::current())
Json::Value getCorrectValue(Json::StaticString fieldName)
void runLedgerEntryTest(test::jtx::Env &env, Json::StaticString const &parentField, std::vector< Subfield > const &subfields, source_location const location=source_location::current())
void runLedgerEntryTest(test::jtx::Env &env, Json::StaticString const &parentField, source_location const location=source_location::current())
std::vector< Json::Value > getBadValues(FieldType fieldType)
void checkErrorValue(Json::Value const &jv, std::string const &err, std::string const &msg, source_location const location=source_location::current())
Convenience class to test AMM functionality.
Definition AMM.h:105
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:99
static Account const master
The master account.
Definition Account.h:29
AccountID id() const
Returns the Account ID.
Definition Account.h:92
A transaction testing environment.
Definition Env.h:102
Application & app()
Definition Env.h:244
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:104
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
Definition Env.cpp:98
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:260
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:272
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:251
Account const & master
Definition Env.h:106
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:303
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:774
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:314
NetClock::time_point now()
Returns the current network time.
Definition Env.h:267
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:144
Oracle class facilitates unit-testing of the Price Oracle feature.
Definition Oracle.h:101
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:16
Sets the optional Destination field on an NFTokenOffer.
Definition token.h:144
Set the flags on a JTx.
Definition txflags.h:12
T empty(T... args)
T end(T... args)
T find_if(T... args)
T is_same_v
@ nullValue
'null' value
Definition json_value.h:20
@ arrayValue
array value (ordered list)
Definition json_value.h:26
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
std::string expected_field_message(std::string const &name, std::string const &type)
Definition ErrorCodes.h:296
std::string missing_field_message(std::string const &name)
Definition ErrorCodes.h:236
Keylet const & skip() noexcept
The index of the "short" skip list.
Definition Indexes.cpp:178
Keylet const & negativeUNL() noexcept
The (fixed) index of the object containing the ledger negativeUNL.
Definition Indexes.cpp:212
Keylet nftpage_max(AccountID const &owner)
A keylet for the owner's last possible NFT page.
Definition Indexes.cpp:393
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:409
Keylet const & amendments() noexcept
The index of the amendment table.
Definition Indexes.cpp:196
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Definition Indexes.cpp:377
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Definition Indexes.cpp:318
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:166
Keylet const & fees() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:204
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:564
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:59
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Definition creds.cpp:13
Json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
Definition delegate.cpp:12
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Definition deposit.cpp:35
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Definition deposit.cpp:13
Json::Value setTx(AccountID const &account, Credentials const &credentials, std::optional< uint256 > domain)
std::map< uint256, Json::Value > getObjects(Account const &account, Env &env, bool withType)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:12
Json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
Definition token.cpp:15
Json::Value createOffer(jtx::Account const &account, uint256 const &nftokenID, STAmount const &amount)
Create an NFTokenOffer.
Definition token.cpp:90
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.
Definition token.cpp:49
Json::Value sidechain_xchain_account_create(Account const &acc, Json::Value const &bridge, Account const &dst, AnyAmount const &amt, AnyAmount const &reward)
constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
require_t required(Args const &... args)
Compose many condition functors into one.
Definition require.h:30
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:11
JValueVec create_account_attestations(jtx::Account const &submittingAccount, Json::Value const &jvBridge, jtx::Account const &sendingAccount, jtx::AnyAmount const &sendingAmount, jtx::AnyAmount const &rewardAmount, std::vector< jtx::Account > const &rewardAccounts, bool wasLockingChainSend, std::uint64_t createCount, jtx::Account const &dst, std::vector< jtx::signer > const &signers, std::size_t const numAtts, std::size_t const fromIdx)
Json::Value xchain_create_claim_id(Account const &acc, Json::Value const &bridge, STAmount const &reward, Account const &otherChainSource)
FeatureBitset testable_amendments()
Definition Env.h:55
auto const amount
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:10
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:10
std::vector< std::pair< Json::StaticString, FieldType > > mappings
FieldType getFieldType(Json::StaticString fieldName)
static uint256 ledgerHash(LedgerHeader const &info)
std::string getTypeName(FieldType typeID)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
constexpr std::uint32_t const tfTransferable
Definition TxFlags.h:123
constexpr std::uint32_t const tfMPTCanTransfer
Definition TxFlags.h:133
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:11
constexpr std::uint32_t const tfMPTRequireAuth
Definition TxFlags.h:130
uint256 getTicketIndex(AccountID const &account, std::uint32_t uSequence)
Definition Indexes.cpp:138
constexpr std::uint32_t asfDepositAuth
Definition TxFlags.h:66
constexpr std::uint32_t const tfMPTCanTrade
Definition TxFlags.h:132
constexpr std::uint32_t const tfMPTCanLock
Definition TxFlags.h:129
constexpr std::uint32_t const tfMPTCanClawback
Definition TxFlags.h:134
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
std::size_t constexpr maxCredentialsArraySize
The maximum number of credentials can be passed in array.
Definition Protocol.h:225
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:158
constexpr std::uint32_t const tfSellNFToken
Definition TxFlags.h:211
constexpr std::uint32_t const tfMPTCanEscrow
Definition TxFlags.h:131
T push_back(T... args)
T reserve(T... args)
T size(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:20
uint256 key
Definition Keylet.h:21
void createBridgeObjects(Env &mcEnv, Env &scEnv)
std::vector< Account > const payee
std::vector< signer > const signers
Set the sequence number on a JTx.
Definition seq.h:15
A signer in a SignerList.
Definition multisign.h:20
T to_string(T... args)