18        using namespace std::string_literals;
 
   23                tx[sfTransactionType.jsonName].asString();
 
   26                    txType == jss::TicketCreate,
 
   27                    "Unexpected TransactionType: "s + txType))
 
   31        std::uint32_t const count = {tx[sfTicketCount.jsonName].asUInt()};
 
   37        std::uint32_t const txSeq = {tx[sfSequence.jsonName].asUInt()};
 
   38        std::string const account = tx[sfAccount.jsonName].asString();
 
   42                metadata.
isMember(sfTransactionResult.jsonName) &&
 
   43                    metadata[sfTransactionResult.jsonName].
asString() ==
 
   45                "Not metadata for successful TicketCreate."))
 
   48        BEAST_EXPECT(metadata.
isMember(sfAffectedNodes.jsonName));
 
   49        BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].
isArray());
 
   51        bool directoryChanged = 
false;
 
   55        for (
Json::Value const& node : metadata[sfAffectedNodes.jsonName])
 
   57            if (node.isMember(sfModifiedNode.jsonName))
 
   59                Json::Value const& modified = node[sfModifiedNode.jsonName];
 
   61                    modified[sfLedgerEntryType.jsonName].
asString();
 
   62                if (entryType == jss::AccountRoot)
 
   64                    auto const& previousFields =
 
   65                        modified[sfPreviousFields.jsonName];
 
   66                    auto const& finalFields = modified[sfFinalFields.jsonName];
 
   70                            previousFields[sfSequence.jsonName].asUInt();
 
   73                            finalFields[sfSequence.jsonName].asUInt();
 
   78                            BEAST_EXPECT(acctRootFinalSeq == prevSeq + count);
 
   83                            BEAST_EXPECT(prevSeq == txSeq);
 
   85                                acctRootFinalSeq == prevSeq + count + 1);
 
   90                        txSeq == 0u ? 1u : 0u};
 
   99                    bool const unreportedPrevTicketCount = {
 
  100                        count == 1 && txSeq == 0};
 
  103                    if (unreportedPrevTicketCount)
 
  108                            !previousFields.isMember(sfOwnerCount.jsonName));
 
  114                            previousFields[sfOwnerCount.jsonName].asUInt()};
 
  117                            finalFields[sfOwnerCount.jsonName].asUInt()};
 
  120                            prevCount + count - consumedTickets == finalCount);
 
  124                    BEAST_EXPECT(finalFields.isMember(sfTicketCount.jsonName));
 
  126                    if (unreportedPrevTicketCount)
 
  131                            !previousFields.isMember(sfTicketCount.jsonName));
 
  138                            previousFields.isMember(sfTicketCount.jsonName)
 
  139                                ? previousFields[sfTicketCount.jsonName]
 
  145                            previousFields.isMember(sfTicketCount.jsonName));
 
  148                            startCount + count - consumedTickets ==
 
  149                            finalFields[sfTicketCount.jsonName]);
 
  152                else if (entryType == jss::DirectoryNode)
 
  154                    directoryChanged = 
true;
 
  159                        "Unexpected modified node: "s + entryType,
 
  164            else if (node.isMember(sfCreatedNode.jsonName))
 
  166                Json::Value const& created = node[sfCreatedNode.jsonName];
 
  168                    created[sfLedgerEntryType.jsonName].
asString();
 
  169                if (entryType == jss::Ticket)
 
  171                    auto const& newFields = created[sfNewFields.jsonName];
 
  174                        newFields[sfAccount.jsonName].asString() == account);
 
  176                        newFields[sfTicketSequence.jsonName].asUInt());
 
  178                else if (entryType == jss::DirectoryNode)
 
  180                    directoryChanged = 
true;
 
  185                        "Unexpected created node: "s + entryType,
 
  190            else if (node.isMember(sfDeletedNode.jsonName))
 
  192                Json::Value const& deleted = node[sfDeletedNode.jsonName];
 
  194                    deleted[sfLedgerEntryType.jsonName].asString();
 
  196                if (entryType == jss::Ticket)
 
  199                    BEAST_EXPECT(txSeq == 0);
 
  202                    auto const& finalFields = deleted[sfFinalFields.jsonName];
 
  204                        finalFields[sfAccount.jsonName].asString() == account);
 
  208                        finalFields[sfTicketSequence.jsonName].asUInt() ==
 
  209                        tx[sfTicketSequence.jsonName].asUInt());
 
  215                    "Unexpected node type in TicketCreate metadata.",
 
  220        BEAST_EXPECT(directoryChanged);
 
  223        BEAST_EXPECT(ticketSeqs.
size() == count);
 
  228        BEAST_EXPECT(*ticketSeqs.
rbegin() == acctRootFinalSeq - 1);
 
 
  263        BEAST_EXPECT(tx[sfSequence.jsonName].asUInt() == 0);
 
  264        std::string const account{tx[sfAccount.jsonName].asString()};
 
  266                tx.isMember(sfTicketSequence.jsonName),
 
  267                "Not metadata for a ticket consuming transaction."))
 
  270        std::uint32_t const ticketSeq{tx[sfTicketSequence.jsonName].asUInt()};
 
  274                metadata.isMember(sfTransactionResult.jsonName),
 
  275                "Metadata is missing TransactionResult."))
 
  280                metadata[sfTransactionResult.jsonName].asString()};
 
  282                    transactionResult == 
"tesSUCCESS" ||
 
  283                        transactionResult.compare(0, 3, 
"tec") == 0,
 
  284                    transactionResult + 
" neither tesSUCCESS nor tec"))
 
  288        BEAST_EXPECT(metadata.isMember(sfAffectedNodes.jsonName));
 
  289        BEAST_EXPECT(metadata[sfAffectedNodes.jsonName].isArray());
 
  291        bool acctRootFound{
false};
 
  293        int ticketsRemoved{0};
 
  294        for (
Json::Value const& node : metadata[sfAffectedNodes.jsonName])
 
  296            if (node.isMember(sfModifiedNode.jsonName))
 
  298                Json::Value const& modified{node[sfModifiedNode.jsonName]};
 
  300                    modified[sfLedgerEntryType.jsonName].asString();
 
  301                if (entryType == 
"AccountRoot" &&
 
  302                    modified[sfFinalFields.jsonName][sfAccount.jsonName]
 
  303                            .asString() == account)
 
  305                    acctRootFound = 
true;
 
  307                    auto const& previousFields =
 
  308                        modified[sfPreviousFields.jsonName];
 
  309                    auto const& finalFields = modified[sfFinalFields.jsonName];
 
  311                    acctRootSeq = finalFields[sfSequence.jsonName].asUInt();
 
  317                            previousFields.isMember(sfTicketCount.jsonName),
 
  318                            "AccountRoot previous is missing TicketCount"))
 
  322                        previousFields[sfTicketCount.jsonName].asUInt();
 
  324                    BEAST_EXPECT(prevTicketCount > 0);
 
  325                    if (prevTicketCount == 1)
 
  327                            !finalFields.isMember(sfTicketCount.jsonName));
 
  330                            finalFields.isMember(sfTicketCount.jsonName) &&
 
  331                            finalFields[sfTicketCount.jsonName].asUInt() ==
 
  332                                prevTicketCount - 1);
 
  335            else if (node.isMember(sfDeletedNode.jsonName))
 
  337                Json::Value const& deleted{node[sfDeletedNode.jsonName]};
 
  339                    deleted[sfLedgerEntryType.jsonName].asString()};
 
  341                if (entryType == jss::Ticket)
 
  345                        deleted[sfFinalFields.jsonName][sfAccount.jsonName]
 
  346                            .asString() == account);
 
  350                        deleted[sfFinalFields.jsonName]
 
  351                               [sfTicketSequence.jsonName]
 
  352                                   .asUInt() == ticketSeq);
 
  358        BEAST_EXPECT(acctRootFound);
 
  359        BEAST_EXPECT(ticketsRemoved == 1);
 
  360        BEAST_EXPECT(ticketSeq < acctRootSeq);
 
 
  368        using namespace test::jtx;
 
  369        Env env{*
this, testable_amendments() - featureTicketBatch};
 
  371        env(ticket::create(env.master, 1), ter(
temDISABLED));
 
  373        env.require(owners(env.master, 0), tickets(env.master, 0));
 
  376        env(
noop(env.master),
 
  378            seq(env.seq(env.master)),
 
  383        for (
int i = 0; i < 8; ++i)
 
  386        env.enableFeature(featureTicketBatch);
 
  388        env.require(owners(env.master, 0), tickets(env.master, 0));
 
  391        env(ticket::create(env.master, 2));
 
  394        env.require(owners(env.master, 2), tickets(env.master, 2));
 
  396        env(
noop(env.master), ticket::use(ticketSeq++));
 
  399        env.require(owners(env.master, 1), tickets(env.master, 1));
 
  402            ticket::use(ticketSeq++),
 
  406        env.require(owners(env.master, 0), tickets(env.master, 0));
 
 
  412        testcase(
"Create Tickets that fail Preflight");
 
  414        using namespace test::jtx;
 
  417        Account 
const master{env.master};
 
  425        env(ticket::create(master, 1), fee(XRP(10)));
 
  428        env.require(owners(master, 1), tickets(master, 1));
 
  430        env(ticket::create(master, 1), fee(XRP(-1)), ter(
temBAD_FEE));
 
  437        env.require(owners(master, 2), tickets(master, 2));
 
  441        env.require(owners(master, 2), tickets(master, 2));
 
  445        env(
noop(master), ticket::use(ticketSeq_A));
 
  448        env.require(owners(master, 1), tickets(master, 1));
 
  450        env(ticket::create(master, 250), ticket::use(ticketSeq_B));
 
  453        env.require(owners(master, 250), tickets(master, 250));
 
 
  459        testcase(
"Create Tickets that fail Preclaim");
 
  461        using namespace test::jtx;
 
  465            Account alice{
"alice"};
 
  468            env(ticket::create(alice, 1),
 
  469                json(jss::Sequence, 1),
 
  476            Account alice{
"alice"};
 
  478            env.fund(XRP(100000), alice);
 
  481            env(ticket::create(alice, 250));
 
  484            env.require(owners(alice, 250), tickets(alice, 250));
 
  488            env(ticket::create(alice, 1), ticket::use(ticketSeq + 0));
 
  491            env.require(owners(alice, 250), tickets(alice, 250));
 
  494            env(ticket::create(alice, 2),
 
  495                ticket::use(ticketSeq + 1),
 
  498            env.require(owners(alice, 249), tickets(alice, 249));
 
  501            env(ticket::create(alice, 2), ticket::use(ticketSeq + 2));
 
  504            env.require(owners(alice, 250), tickets(alice, 250));
 
  510            env.require(owners(alice, 250), tickets(alice, 250));
 
  515            Account alice{
"alice"};
 
  517            env.fund(XRP(100000), alice);
 
  521            env(ticket::create(alice, 2));
 
  524            env.require(owners(alice, 2), tickets(alice, 2));
 
  528            env(ticket::create(alice, 250),
 
  529                ticket::use(ticketSeq_AB + 0),
 
  532            env.require(owners(alice, 1), tickets(alice, 1));
 
  538            env.require(owners(alice, 1), tickets(alice, 1));
 
  541            env(ticket::create(alice, 250), ticket::use(ticketSeq_AB + 1));
 
  544            env.require(owners(alice, 250), tickets(alice, 250));
 
 
  551        testcase(
"Create Ticket Insufficient Reserve");
 
  553        using namespace test::jtx;
 
  555        Account alice{
"alice"};
 
  558        env.fund(env.current()->fees().accountReserve(1) - drops(1), alice);
 
  563        env.require(owners(alice, 0), tickets(alice, 0));
 
  569                env.current()->fees().accountReserve(1) - env.balance(alice)));
 
  572        env(ticket::create(alice, 1));
 
  575        env.require(owners(alice, 1), tickets(alice, 1));
 
  582                env.current()->fees().accountReserve(250) - drops(1) -
 
  583                    env.balance(alice)));
 
  590        env.require(owners(alice, 1), tickets(alice, 1));
 
  597            env.current()->fees().accountReserve(250) - env.balance(alice)));
 
  601        env(ticket::create(alice, 249));
 
  604        env.require(owners(alice, 250), tickets(alice, 250));
 
  605        BEAST_EXPECT(ticketSeq + 249 == env.seq(alice));
 
 
  613        using namespace test::jtx;
 
  615        Account alice{
"alice"};
 
  617        env.fund(XRP(10000), alice);
 
  622        env(ticket::create(alice, 2));
 
  625        env.require(owners(alice, 2), tickets(alice, 2));
 
  626        BEAST_EXPECT(ticketSeq_AB + 2 == env.seq(alice));
 
  630        env(ticket::create(alice, 1), ticket::use(ticketSeq_AB + 0));
 
  633        env.require(owners(alice, 2), tickets(alice, 2));
 
  634        BEAST_EXPECT(ticketSeq_C + 1 == env.seq(alice));
 
  638        env(ticket::create(alice, 2), ticket::use(ticketSeq_AB + 1));
 
  641        env.require(owners(alice, 3), tickets(alice, 3));
 
  642        BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
 
  645        env(
noop(alice), ticket::use(ticketSeq_DE + 0));
 
  648        env.require(owners(alice, 2), tickets(alice, 2));
 
  649        BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
 
  651        env(pay(alice, env.master, XRP(20)), ticket::use(ticketSeq_DE + 1));
 
  654        env.require(owners(alice, 1), tickets(alice, 1));
 
  655        BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
 
  657        env(trust(alice, env.master[
"USD"](20)), ticket::use(ticketSeq_C));
 
  660        env.require(owners(alice, 1), tickets(alice, 0));
 
  661        BEAST_EXPECT(ticketSeq_DE + 2 == env.seq(alice));
 
  673        env(ticket::create(alice, 1));
 
  676        env.require(owners(alice, 1), tickets(alice, 0));
 
  677        BEAST_EXPECT(ticketSeq_F + 1 == env.seq(alice));
 
  682        env(ticket::create(alice, 1));
 
  687            ticket::use(ticketSeq_G),
 
  688            json(R
"({"AccountTxnID": "0"})"), 
  691        env.require(owners(alice, 2), tickets(alice, 1)); 
 
  707        testcase(
"Transaction Database With Tickets");
 
  709        using namespace test::jtx;
 
  711        Account alice{
"alice"};
 
  713        env.fund(XRP(10000), alice);
 
  717        auto getTxID = [&env, 
this]() -> 
uint256 {
 
  719            if (!BEAST_EXPECTS(tx, 
"Transaction not found"))
 
  720                Throw<std::invalid_argument>(
"Invalid transaction ID");
 
  722            return tx->getTransactionID();
 
  736        env(ticket::create(alice, ticketCount));
 
  737        uint256 const txHash_1{getTxID()};
 
  740        ticketSeq += ticketCount;
 
  741        env(
noop(alice), ticket::use(--ticketSeq));
 
  742        uint256 const txHash_2{getTxID()};
 
  744        env(pay(alice, env.master, XRP(200)), ticket::use(--ticketSeq));
 
  745        uint256 const txHash_3{getTxID()};
 
  747        env(deposit::auth(alice, env.master), ticket::use(--ticketSeq));
 
  748        uint256 const txHash_4{getTxID()};
 
  754        env(pay(alice, env.master, XRP(300)), ticket::use(--ticketSeq));
 
  755        uint256 const txHash_5{getTxID()};
 
  757        env(pay(alice, env.master, XRP(400)), ticket::use(--ticketSeq));
 
  758        uint256 const txHash_6{getTxID()};
 
  760        env(deposit::unauth(alice, env.master), ticket::use(--ticketSeq));
 
  761        uint256 const txHash_7{getTxID()};
 
  763        env(
noop(alice), ticket::use(--ticketSeq));
 
  764        uint256 const txHash_8{getTxID()};
 
  774        auto checkTxFromDB = [&env, 
this](
 
  791                BEAST_EXPECT(tx->getLedger() == ledgerSeq);
 
  793                BEAST_EXPECT((*sttx)[sfSequence] == txSeq);
 
  795                    BEAST_EXPECT((*sttx)[sfTicketSequence] == *ticketSeq);
 
  796                BEAST_EXPECT((*sttx)[sfTransactionType] == txType);
 
  800                fail(
"Expected transaction was not found");
 
  805        checkTxFromDB(txHash_1, 4, 4, {}, ttTICKET_CREATE);
 
  806        checkTxFromDB(txHash_2, 4, 0, 13, ttACCOUNT_SET);
 
  807        checkTxFromDB(txHash_3, 4, 0, 12, ttPAYMENT);
 
  808        checkTxFromDB(txHash_4, 4, 0, 11, ttDEPOSIT_PREAUTH);
 
  810        checkTxFromDB(txHash_5, 5, 0, 10, ttPAYMENT);
 
  811        checkTxFromDB(txHash_6, 5, 0, 9, ttPAYMENT);
 
  812        checkTxFromDB(txHash_7, 5, 0, 8, ttDEPOSIT_PREAUTH);
 
  813        checkTxFromDB(txHash_8, 5, 0, 7, ttACCOUNT_SET);
 
 
  823        testcase(
"Sign with TicketSequence");
 
  825        using namespace test::jtx;
 
  827        Account alice{
"alice"};
 
  829        env.fund(XRP(10000), alice);
 
  834        env(ticket::create(alice, 2));
 
  837        env.require(owners(alice, 2), tickets(alice, 2));
 
  838        BEAST_EXPECT(ticketSeq + 2 == env.seq(alice));
 
  847            tx[jss::tx_json] = 
noop(alice);
 
  848            tx[jss::tx_json][sfTicketSequence.jsonName] = ticketSeq;
 
  852            BEAST_EXPECT(!tx[jss::tx_json].isMember(sfSequence.jsonName));
 
  859            if (BEAST_EXPECT(jr[jss::result][jss::tx_json].isMember(
 
  860                    sfSequence.jsonName)))
 
  863                    jr[jss::result][jss::tx_json][sfSequence.jsonName] == 0);
 
  868            env.require(owners(alice, 2), tickets(alice, 2));
 
  871            env.rpc(
"submit", jr[jss::result][jss::tx_blob].asString());
 
  873            env.require(owners(alice, 1), tickets(alice, 1));
 
  882            tx[jss::tx_json] = 
noop(alice);
 
  883            tx[jss::tx_json][sfTicketSequence.jsonName] = ticketSeq + 1;
 
  887            BEAST_EXPECT(!tx[jss::tx_json].isMember(sfSequence.jsonName));
 
  894            if (BEAST_EXPECT(jr[jss::result][jss::tx_json].isMember(
 
  895                    sfSequence.jsonName)))
 
  898                    jr[jss::result][jss::tx_json][sfSequence.jsonName] == 0);
 
  903            env.require(owners(alice, 0), tickets(alice, 0));
 
 
  912        testcase(
"Fix both Seq and Ticket");
 
  915        using namespace test::jtx;
 
  917            Env env{*
this, testable_amendments() - featureTicketBatch};
 
  918            Account alice{
"alice"};
 
  920            env.fund(XRP(10000), alice);
 
  927            env.require(owners(alice, 0), tickets(alice, 0));
 
  928            BEAST_EXPECT(ticketSeq == env.seq(alice) + 1);
 
  934                ticket::use(ticketSeq),
 
  941            Env env{*
this, testable_amendments()};
 
  942            Account alice{
"alice"};
 
  944            env.fund(XRP(10000), alice);
 
  949            env(ticket::create(alice, 1));
 
  951            env.require(owners(alice, 1), tickets(alice, 1));
 
  952            BEAST_EXPECT(ticketSeq + 1 == env.seq(alice));
 
  957                ticket::use(ticketSeq),
 
  964            env.require(owners(alice, 1), tickets(alice, 1));
 
  965            BEAST_EXPECT(ticketSeq + 1 == env.seq(alice));