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));
396 for (
int i = 0; i < 8; ++i)
401 env.require(owners(env.master, 0), tickets(env.master, 0));
404 env(ticket::create(env.master, 2));
407 env.require(owners(env.master, 2), tickets(env.master, 2));
409 env(noop(env.master), ticket::use(ticketSeq++));
412 env.require(owners(env.master, 1), tickets(env.master, 1));
415 ticket::use(ticketSeq++),
419 env.require(owners(env.master, 0), tickets(env.master, 0));
425 testcase(
"Create Tickets that fail Preflight");
427 using namespace test::jtx;
430 Account
const master{env.master};
438 env(ticket::create(master, 1), fee(XRP(10)));
441 env.require(owners(master, 1), tickets(master, 1));
443 env(ticket::create(master, 1), fee(XRP(-1)), ter(
temBAD_FEE));
450 env.require(owners(master, 2), tickets(master, 2));
454 env.require(owners(master, 2), tickets(master, 2));
458 env(noop(master), ticket::use(ticketSeq_A));
461 env.require(owners(master, 1), tickets(master, 1));
463 env(ticket::create(master, 250), ticket::use(ticketSeq_B));
466 env.require(owners(master, 250), tickets(master, 250));
472 testcase(
"Create Tickets that fail Preclaim");
474 using namespace test::jtx;
478 Account alice{
"alice"};
481 env(ticket::create(alice, 1),
482 json(jss::Sequence, 1),
489 Account alice{
"alice"};
491 env.fund(XRP(100000), alice);
494 env(ticket::create(alice, 250));
497 env.require(owners(alice, 250), tickets(alice, 250));
501 env(ticket::create(alice, 1), ticket::use(ticketSeq + 0));
504 env.require(owners(alice, 250), tickets(alice, 250));
507 env(ticket::create(alice, 2),
508 ticket::use(ticketSeq + 1),
511 env.require(owners(alice, 249), tickets(alice, 249));
514 env(ticket::create(alice, 2), ticket::use(ticketSeq + 2));
517 env.require(owners(alice, 250), tickets(alice, 250));
523 env.require(owners(alice, 250), tickets(alice, 250));
528 Account alice{
"alice"};
530 env.fund(XRP(100000), alice);
534 env(ticket::create(alice, 2));
537 env.require(owners(alice, 2), tickets(alice, 2));
541 env(ticket::create(alice, 250),
542 ticket::use(ticketSeq_AB + 0),
545 env.require(owners(alice, 1), tickets(alice, 1));
551 env.require(owners(alice, 1), tickets(alice, 1));
554 env(ticket::create(alice, 250), ticket::use(ticketSeq_AB + 1));
557 env.require(owners(alice, 250), tickets(alice, 250));
564 testcase(
"Create Ticket Insufficient Reserve");
566 using namespace test::jtx;
568 Account alice{
"alice"};
571 env.fund(env.current()->fees().accountReserve(1) - drops(1), alice);
576 env.require(owners(alice, 0), tickets(alice, 0));
582 env.current()->fees().accountReserve(1) - env.balance(alice)));
585 env(ticket::create(alice, 1));
588 env.require(owners(alice, 1), tickets(alice, 1));
595 env.current()->fees().accountReserve(250) - drops(1) -
596 env.balance(alice)));
603 env.require(owners(alice, 1), tickets(alice, 1));
610 env.current()->fees().accountReserve(250) - env.balance(alice)));
614 env(ticket::create(alice, 249));
617 env.require(owners(alice, 250), tickets(alice, 250));
618 BEAST_EXPECT(ticketSeq + 249 == env.seq(alice));
624 testcase(
"Using Tickets");
626 using namespace test::jtx;
628 Account alice{
"alice"};
630 env.fund(XRP(10000), alice);
635 env(ticket::create(alice, 2));
638 env.require(owners(alice, 2), tickets(alice, 2));
639 BEAST_EXPECT(ticketSeq_AB + 2 == env.seq(alice));
643 env(ticket::create(alice, 1), ticket::use(ticketSeq_AB + 0));
646 env.require(owners(alice, 2), tickets(alice, 2));
647 BEAST_EXPECT(ticketSeq_C + 1 == env.seq(alice));
651 env(ticket::create(alice, 2), ticket::use(ticketSeq_AB + 1));
654 env.require(owners(alice, 3), tickets(alice, 3));
655 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
658 env(noop(alice), ticket::use(ticketSeq_DE + 0));
661 env.require(owners(alice, 2), tickets(alice, 2));
662 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
664 env(pay(alice, env.master, XRP(20)), ticket::use(ticketSeq_DE + 1));
667 env.require(owners(alice, 1), tickets(alice, 1));
668 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
670 env(trust(alice, env.master[
"USD"](20)), ticket::use(ticketSeq_C));
673 env.require(owners(alice, 1), tickets(alice, 0));
674 BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
677 env(noop(alice), ticket::use(ticketSeq_C), ter(
tefNO_TICKET));
682 env(noop(alice), ticket::use(ticketSeq_F), ter(
terPRE_TICKET));
686 env(ticket::create(alice, 1));
689 env.require(owners(alice, 1), tickets(alice, 0));
690 BEAST_EXPECT(ticketSeq_F + 1 == env.seq(alice));
695 env(ticket::create(alice, 1));
700 ticket::use(ticketSeq_G),
701 json(R
"({"AccountTxnID": "0"})"),
704 env.require(owners(alice, 2), tickets(alice, 1));
720 testcase(
"Transaction Database With Tickets");
722 using namespace test::jtx;
724 Account alice{
"alice"};
726 env.fund(XRP(10000), alice);
730 auto getTxID = [&env,
this]() ->
uint256 {
732 if (!BEAST_EXPECTS(tx,
"Transaction not found"))
733 Throw<std::invalid_argument>(
"Invalid transaction ID");
735 return tx->getTransactionID();
749 env(ticket::create(alice, ticketCount));
750 uint256 const txHash_1{getTxID()};
753 ticketSeq += ticketCount;
754 env(noop(alice), ticket::use(--ticketSeq));
755 uint256 const txHash_2{getTxID()};
757 env(pay(alice, env.master, XRP(200)), ticket::use(--ticketSeq));
758 uint256 const txHash_3{getTxID()};
760 env(deposit::auth(alice, env.master), ticket::use(--ticketSeq));
761 uint256 const txHash_4{getTxID()};
767 env(pay(alice, env.master, XRP(300)), ticket::use(--ticketSeq));
768 uint256 const txHash_5{getTxID()};
770 env(pay(alice, env.master, XRP(400)), ticket::use(--ticketSeq));
771 uint256 const txHash_6{getTxID()};
773 env(deposit::unauth(alice, env.master), ticket::use(--ticketSeq));
774 uint256 const txHash_7{getTxID()};
776 env(noop(alice), ticket::use(--ticketSeq));
777 uint256 const txHash_8{getTxID()};
787 auto checkTxFromDB = [&env,
this](
791 boost::optional<std::uint32_t> ticketSeq,
797 BEAST_EXPECT(tx->getLedger() == ledgerSeq);
809 checkTxFromDB(txHash_3, 4, 0, 12,
ttPAYMENT);
812 checkTxFromDB(txHash_5, 5, 0, 10,
ttPAYMENT);
813 checkTxFromDB(txHash_6, 5, 0, 9,
ttPAYMENT);