20 #include <ripple/app/misc/Transaction.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
35 using namespace std::string_literals;
43 txType == jss::TicketCreate,
44 "Unexpected TransactionType: "s + txType))
62 "Not metadata for successful TicketCreate."))
68 bool directoryChanged =
false;
79 if (entryType == jss::AccountRoot)
81 auto const& previousFields =
95 BEAST_EXPECT(acctRootFinalSeq == prevSeq + count);
100 BEAST_EXPECT(prevSeq == txSeq);
102 acctRootFinalSeq == prevSeq + count + 1);
107 txSeq == 0u ? 1u : 0u};
116 bool const unreportedPrevTicketCount = {
117 count == 1 && txSeq == 0};
120 if (unreportedPrevTicketCount)
137 prevCount + count - consumedTickets == finalCount);
143 if (unreportedPrevTicketCount)
165 startCount + count - consumedTickets ==
169 else if (entryType == jss::DirectoryNode)
171 directoryChanged =
true;
176 "Unexpected modified node: "s + entryType,
186 if (entryType == jss::Ticket)
195 else if (entryType == jss::DirectoryNode)
197 directoryChanged =
true;
202 "Unexpected created node: "s + entryType,
213 if (entryType == jss::Ticket)
216 BEAST_EXPECT(txSeq == 0);
232 "Unexpected node type in TicketCreate metadata.",
237 BEAST_EXPECT(directoryChanged);
240 BEAST_EXPECT(ticketSeqs.
size() == count);
245 BEAST_EXPECT(*ticketSeqs.
rbegin() == acctRootFinalSeq - 1);
284 "Not metadata for a ticket consuming transaction."))
292 "Metadata is missing TransactionResult."))
299 transactionResult ==
"tesSUCCESS" ||
300 transactionResult.compare(0, 3,
"tec") == 0,
301 transactionResult +
" neither tesSUCCESS nor tec"))
308 bool acctRootFound{
false};
310 int ticketsRemoved{0};
318 if (entryType ==
"AccountRoot" &&
320 .asString() == account)
322 acctRootFound =
true;
324 auto const& previousFields =
335 "AccountRoot previous is missing TicketCount"))
341 BEAST_EXPECT(prevTicketCount > 0);
342 if (prevTicketCount == 1)
349 prevTicketCount - 1);
358 if (entryType == jss::Ticket)
363 .asString() == account);
369 .asUInt() == ticketSeq);
375 BEAST_EXPECT(acctRootFound);
376 BEAST_EXPECT(ticketsRemoved == 1);
377 BEAST_EXPECT(ticketSeq < acctRootSeq);
383 testcase(
"Feature Not Enabled");
385 using namespace test::jtx;
388 env(ticket::create(env.master, 1), ter(
temDISABLED));
390 env.require(owners(env.master, 0), tickets(env.master, 0));
392 env(noop(env.master), ticket::use(1), ter(
temMALFORMED));
393 env(noop(env.master),
395 seq(env.seq(env.master)),
400 for (
int i = 0; i < 8; ++i)
405 env.require(owners(env.master, 0), tickets(env.master, 0));
408 env(ticket::create(env.master, 2));
411 env.require(owners(env.master, 2), tickets(env.master, 2));
413 env(noop(env.master), ticket::use(ticketSeq++));
416 env.require(owners(env.master, 1), tickets(env.master, 1));
419 ticket::use(ticketSeq++),
423 env.require(owners(env.master, 0), tickets(env.master, 0));
429 testcase(
"Create Tickets that fail Preflight");
431 using namespace test::jtx;
434 Account
const master{env.master};
442 env(ticket::create(master, 1), fee(XRP(10)));
445 env.require(owners(master, 1), tickets(master, 1));
447 env(ticket::create(master, 1), fee(XRP(-1)), ter(
temBAD_FEE));
454 env.require(owners(master, 2), tickets(master, 2));
458 env.require(owners(master, 2), tickets(master, 2));
462 env(noop(master), ticket::use(ticketSeq_A));
465 env.require(owners(master, 1), tickets(master, 1));
467 env(ticket::create(master, 250), ticket::use(ticketSeq_B));
470 env.require(owners(master, 250), tickets(master, 250));
476 testcase(
"Create Tickets that fail Preclaim");
478 using namespace test::jtx;
482 Account alice{
"alice"};
485 env(ticket::create(alice, 1),
486 json(jss::Sequence, 1),
493 Account alice{
"alice"};
495 env.fund(XRP(100000), alice);
498 env(ticket::create(alice, 250));
501 env.require(owners(alice, 250), tickets(alice, 250));
505 env(ticket::create(alice, 1), ticket::use(ticketSeq + 0));
508 env.require(owners(alice, 250), tickets(alice, 250));
511 env(ticket::create(alice, 2),
512 ticket::use(ticketSeq + 1),
515 env.require(owners(alice, 249), tickets(alice, 249));
518 env(ticket::create(alice, 2), ticket::use(ticketSeq + 2));
521 env.require(owners(alice, 250), tickets(alice, 250));
527 env.require(owners(alice, 250), tickets(alice, 250));
532 Account alice{
"alice"};
534 env.fund(XRP(100000), alice);
538 env(ticket::create(alice, 2));
541 env.require(owners(alice, 2), tickets(alice, 2));
545 env(ticket::create(alice, 250),
546 ticket::use(ticketSeq_AB + 0),
549 env.require(owners(alice, 1), tickets(alice, 1));
555 env.require(owners(alice, 1), tickets(alice, 1));
558 env(ticket::create(alice, 250), ticket::use(ticketSeq_AB + 1));
561 env.require(owners(alice, 250), tickets(alice, 250));
568 testcase(
"Create Ticket Insufficient Reserve");
570 using namespace test::jtx;
572 Account alice{
"alice"};
575 env.fund(env.current()->fees().accountReserve(1) - drops(1), alice);
580 env.require(owners(alice, 0), tickets(alice, 0));
586 env.current()->fees().accountReserve(1) - env.balance(alice)));
589 env(ticket::create(alice, 1));
592 env.require(owners(alice, 1), tickets(alice, 1));
599 env.current()->fees().accountReserve(250) - drops(1) -
600 env.balance(alice)));
607 env.require(owners(alice, 1), tickets(alice, 1));
614 env.current()->fees().accountReserve(250) - env.balance(alice)));
618 env(ticket::create(alice, 249));
621 env.require(owners(alice, 250), tickets(alice, 250));
622 BEAST_EXPECT(ticketSeq + 249 == env.seq(alice));
628 testcase(
"Using Tickets");
630 using namespace test::jtx;
632 Account alice{
"alice"};
634 env.fund(XRP(10000), alice);
639 env(ticket::create(alice, 2));
642 env.require(owners(alice, 2), tickets(alice, 2));
643 BEAST_EXPECT(ticketSeq_AB + 2 == env.seq(alice));
647 env(ticket::create(alice, 1), ticket::use(ticketSeq_AB + 0));
650 env.require(owners(alice, 2), tickets(alice, 2));
651 BEAST_EXPECT(ticketSeq_C + 1 == env.seq(alice));
655 env(ticket::create(alice, 2), ticket::use(ticketSeq_AB + 1));
658 env.require(owners(alice, 3), tickets(alice, 3));
659 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
662 env(noop(alice), ticket::use(ticketSeq_DE + 0));
665 env.require(owners(alice, 2), tickets(alice, 2));
666 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
668 env(pay(alice, env.master, XRP(20)), ticket::use(ticketSeq_DE + 1));
671 env.require(owners(alice, 1), tickets(alice, 1));
672 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
674 env(trust(alice, env.master[
"USD"](20)), ticket::use(ticketSeq_C));
677 env.require(owners(alice, 1), tickets(alice, 0));
678 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
681 env(noop(alice), ticket::use(ticketSeq_C), ter(
tefNO_TICKET));
686 env(noop(alice), ticket::use(ticketSeq_F), ter(
terPRE_TICKET));
690 env(ticket::create(alice, 1));
693 env.require(owners(alice, 1), tickets(alice, 0));
694 BEAST_EXPECT(ticketSeq_F + 1 == env.seq(alice));
699 env(ticket::create(alice, 1));
704 ticket::use(ticketSeq_G),
705 json(R
"({"AccountTxnID": "0"})"),
708 env.require(owners(alice, 2), tickets(alice, 1));
724 testcase(
"Transaction Database With Tickets");
726 using namespace test::jtx;
728 Account alice{
"alice"};
730 env.fund(XRP(10000), alice);
734 auto getTxID = [&env,
this]() ->
uint256 {
736 if (!BEAST_EXPECTS(tx,
"Transaction not found"))
737 Throw<std::invalid_argument>(
"Invalid transaction ID");
739 return tx->getTransactionID();
753 env(ticket::create(alice, ticketCount));
754 uint256 const txHash_1{getTxID()};
757 ticketSeq += ticketCount;
758 env(noop(alice), ticket::use(--ticketSeq));
759 uint256 const txHash_2{getTxID()};
761 env(pay(alice, env.master, XRP(200)), ticket::use(--ticketSeq));
762 uint256 const txHash_3{getTxID()};
764 env(deposit::auth(alice, env.master), ticket::use(--ticketSeq));
765 uint256 const txHash_4{getTxID()};
771 env(pay(alice, env.master, XRP(300)), ticket::use(--ticketSeq));
772 uint256 const txHash_5{getTxID()};
774 env(pay(alice, env.master, XRP(400)), ticket::use(--ticketSeq));
775 uint256 const txHash_6{getTxID()};
777 env(deposit::unauth(alice, env.master), ticket::use(--ticketSeq));
778 uint256 const txHash_7{getTxID()};
780 env(noop(alice), ticket::use(--ticketSeq));
781 uint256 const txHash_8{getTxID()};
791 auto checkTxFromDB = [&env,
this](
805 if (
auto txPtr = std::get_if<TxPair>(&maybeTx))
808 BEAST_EXPECT(tx->getLedger() == ledgerSeq);
817 fail(
"Expected transaction was not found");
824 checkTxFromDB(txHash_3, 4, 0, 12,
ttPAYMENT);
827 checkTxFromDB(txHash_5, 5, 0, 10,
ttPAYMENT);
828 checkTxFromDB(txHash_6, 5, 0, 9,
ttPAYMENT);
840 testcase(
"Sign with TicketSequence");
842 using namespace test::jtx;
844 Account alice{
"alice"};
846 env.fund(XRP(10000), alice);
851 env(ticket::create(alice, 2));
854 env.require(owners(alice, 2), tickets(alice, 2));
855 BEAST_EXPECT(ticketSeq + 2 == env.seq(alice));
864 tx[jss::tx_json] = noop(alice);
876 if (BEAST_EXPECT(jr[jss::result][jss::tx_json].isMember(
885 env.require(owners(alice, 2), tickets(alice, 2));
888 env.rpc(
"submit", jr[jss::result][jss::tx_blob].asString());
890 env.require(owners(alice, 1), tickets(alice, 1));
899 tx[jss::tx_json] = noop(alice);
911 if (BEAST_EXPECT(jr[jss::result][jss::tx_json].isMember(
920 env.require(owners(alice, 0), tickets(alice, 0));
929 testcase(
"Fix both Seq and Ticket");
932 using namespace test::jtx;
935 Account alice{
"alice"};
937 env.fund(XRP(10000), alice);
944 env.require(owners(alice, 0), tickets(alice, 0));
945 BEAST_EXPECT(ticketSeq == env.seq(alice) + 1);
951 ticket::use(ticketSeq),
958 Env env{*
this, supported_amendments()};
959 Account alice{
"alice"};
961 env.fund(XRP(10000), alice);
966 env(ticket::create(alice, 1));
968 env.require(owners(alice, 1), tickets(alice, 1));
969 BEAST_EXPECT(ticketSeq + 1 == env.seq(alice));
974 ticket::use(ticketSeq),
981 env.require(owners(alice, 1), tickets(alice, 1));
982 BEAST_EXPECT(ticketSeq + 1 == env.seq(alice));