21#include <test/jtx/TestSuite.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/envconfig.h>
24#include <test/jtx/ticket.h>
26#include <xrpld/app/main/Application.h>
27#include <xrpld/app/misc/LoadFeeTrack.h>
28#include <xrpld/app/misc/TxQ.h>
29#include <xrpld/app/tx/apply.h>
31#include <xrpl/protocol/ErrorCodes.h>
32#include <xrpl/protocol/jss.h>
33#include <xrpl/protocol/st.h>
56 FeeLevel64 const expectedMin{expectedMinFeeLevel};
57 FeeLevel64 const expectedMed{expectedMedFeeLevel};
59 using namespace std::string_literals;
70 metrics.txCount == expectedCount
78 metrics.txQMaxSize == expectedMaxCount
87 metrics.txInLedger == expectedInLedger
95 metrics.txPerLedger == expectedPerLedger
103 metrics.minProcessingFeeLevel == expectedMin
106 "minProcessingFeeLevel: "s +
112 metrics.medFeeLevel == expectedMed
121 auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger
122 ? expectedMed * expectedInLedger * expectedInLedger /
123 (expectedPerLedger * expectedPerLedger)
124 : metrics.referenceFeeLevel;
126 metrics.openLedgerFeeLevel == expectedCurFeeLevel
129 "openLedgerFeeLevel: "s +
140 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
149 auto const& view = *env.
current();
150 auto const base = [&view]() {
151 auto base = view.fees().base;
159 return toDrops(metrics.openLedgerFeeLevel, base) + 1;
172 return txs.begin()->feeLevel.fee();
198 auto& section = p->section(
"transaction_queue");
199 section.set(
"ledgers_in_queue",
"2");
200 section.set(
"minimum_queue_size",
"2");
201 section.set(
"min_ledgers_to_compute_size_limit",
"3");
202 section.set(
"max_ledger_counts_to_store",
"100");
203 section.set(
"retry_sequence_percent",
"25");
204 section.set(
"normal_consensus_increase_percent",
"0");
206 for (
auto const& [k, v] : extraTxQ)
211 if (!extraVoting.
empty())
213 auto& votingSection = p->section(
"voting");
214 for (
auto const& [k, v] : extraVoting)
216 votingSection.set(k, v);
220 p->section(
"validation_seed")
221 .legacy(
"shUwVw52ofnCUX5m7kPTKzJdr4HEH");
239 for (
auto i = env.
current()->seq(); i <= 257; ++i)
246 auto const flagMaxQueue = ledgersInQueue * flagPerLedger;
247 checkMetrics(__LINE__, env, 0, flagMaxQueue, 0, flagPerLedger);
256 using namespace std::chrono_literals;
258 checkMetrics(__LINE__, env, 0, flagMaxQueue, 0, expectedPerLedger);
259 auto const fees = env.
current()->fees();
275 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
279 auto charlie =
Account(
"charlie");
288 auto const baseFee = env.
current()->fees().base.drops();
384 constexpr auto largeFeeMultiplier = 700;
385 const auto largeFee = baseFee * largeFeeMultiplier;
389 env(
noop(hank),
fee(largeFee));
390 env(
noop(gwen),
fee(largeFee));
391 env(
noop(fred),
fee(largeFee));
392 env(
noop(elmo),
fee(largeFee));
468 for (
int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
482 metrics.txPerLedger + 1,
483 metrics.txPerLedger);
492 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
497 auto const baseFee = env.
current()->fees().base.drops();
550 checkMetrics(__LINE__, env, 8, 8, 5, 4, expectedMinFeeLevel);
713 fee(baseFee * 2.1 * 1.25 - 1),
717 fee(baseFee * 2.1 * 1.25 + 1),
723 fee(baseFee * 2.2 * 1.25 - 1),
727 fee(baseFee * 2.2 * 1.25 + 1),
751 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"2"}}));
755 auto USD = gw[
"USD"];
788 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"2"}}));
792 auto charlie =
Account(
"charlie");
795 auto const baseFee = env.
current()->fees().base.drops();
844 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"2"}}));
848 auto charlie =
Account(
"charlie");
851 auto felicia =
Account(
"felicia");
854 auto const baseFee = env.
current()->fees().base.drops();
875 json(R
"({"LastLedgerSequence":7})"),
880 constexpr auto largeFeeMultiplier = 700;
881 const auto largeFee = baseFee * largeFeeMultiplier;
896 aliceStat.begin()->lastValid &&
897 *aliceStat.begin()->lastValid == 8);
898 BEAST_EXPECT(!aliceStat.begin()->consequences.isBlocker());
900 auto bobStat = txQ.getAccountTxs(bob.id());
903 bobStat.begin()->feeLevel ==
904 FeeLevel64{baseFeeLevel.fee() * largeFeeMultiplier});
906 BEAST_EXPECT(!bobStat.begin()->consequences.isBlocker());
930 constexpr auto anotherLargeFeeMultiplier = 800;
931 const auto anotherLargeFee = baseFee * anotherLargeFeeMultiplier;
970 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"2"}}));
977 auto const baseFee = env.
current()->fees().base.drops();
1007 constexpr auto aliceFeeMultiplier = 3;
1008 auto feeAlice = baseFee * aliceFeeMultiplier;
1009 auto seqAlice = env.
seq(alice);
1010 for (
int i = 0; i < 4; ++i)
1013 feeAlice = (feeAlice + 1) * 125 / 100;
1019 auto const seqBob = env.
seq(bob);
1024 auto feeCarol = feeAlice;
1025 auto seqCarol = env.
seq(carol);
1026 for (
int i = 0; i < 4; ++i)
1029 feeCarol = (feeCarol + 1) * 125 / 100;
1067 using namespace jtx;
1072 auto alice =
Account(
"alice");
1094 using namespace jtx;
1097 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"2"}}));
1099 auto alice =
Account(
"alice");
1120 auto const& jt = env.
jt(
noop(alice));
1145 using namespace jtx;
1151 {{
"minimum_txn_in_ledger_standalone",
"3"}},
1152 {{
"account_reserve",
"200"}, {
"owner_reserve",
"50"}}));
1154 auto alice =
Account(
"alice");
1156 auto charlie =
Account(
"charlie");
1157 auto daria =
Account(
"daria");
1164 auto const initQueueMax =
initFee(env, 3, 2, 10, 200, 50);
1175 auto aliceSeq = env.
seq(alice);
1176 auto bobSeq = env.
seq(bob);
1177 auto charlieSeq = env.
seq(charlie);
1222 aliceSeq = env.
seq(alice);
1223 auto lastLedgerSeq = env.
current()->info().seq + 2;
1224 for (
auto i = 0; i < 7; i++)
1228 json(jss::LastLedgerSequence, lastLedgerSeq + i),
1238 auto const& baseFee = env.
current()->fees().base;
1239 auto seq = env.
seq(alice);
1241 for (
auto const& tx : aliceStat)
1248 (tx.consequences.fee() ==
drops(aliceFee) &&
1249 tx.consequences.potentialSpend() ==
drops(0) &&
1250 !tx.consequences.isBlocker()) ||
1251 tx.seqProxy.value() == env.
seq(alice) + 6);
1261 json(jss::LastLedgerSequence, lastLedgerSeq + 7),
1294 aliceSeq = env.
seq(alice) + 2;
1313 aliceSeq = env.
seq(alice) + 1;
1320 env.
le(alice)->getFieldAmount(sfBalance).xrp().drops() - (62);
1363 bobSeq = env.
seq(bob);
1365 for (
int i = 0; i < 10; ++i)
1380 env.
le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1);
1394 env.
le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10);
1413 using namespace jtx;
1417 auto cfg =
makeConfig({{
"minimum_txn_in_ledger_standalone",
"4"}});
1418 cfg->FEES.reference_fee = 10;
1419 Env env(*
this, std::move(cfg));
1421 auto alice =
Account(
"alice");
1423 auto charlie =
Account(
"charlie");
1424 auto daria =
Account(
"daria");
1460 auto aliceSeq = env.
seq(alice);
1461 auto bobSeq = env.
seq(bob);
1462 auto charlieSeq = env.
seq(charlie);
1463 auto dariaSeq = env.
seq(daria);
1464 auto elmoSeq = env.
seq(elmo);
1465 auto fredSeq = env.
seq(fred);
1466 auto gwenSeq = env.
seq(gwen);
1467 auto hankSeq = env.
seq(hank);
1485 checkMetrics(__LINE__, env, 8, 8, 5, 4, minFeeLevel + 1);
1498 checkMetrics(__LINE__, env, 8, 8, 5, 4, minFeeLevel + 1);
1509 aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq +
1510 gwenSeq + hankSeq + 6 ==
1511 env.
seq(alice) + env.
seq(bob) + env.
seq(charlie) + env.
seq(daria) +
1512 env.
seq(elmo) + env.
seq(fred) + env.
seq(gwen) + env.
seq(hank));
1514 using namespace std::string_literals;
1516 aliceSeq == env.
seq(alice),
1520 bobSeq + 1 == env.
seq(bob),
1524 charlieSeq + 2 == env.
seq(charlie),
1528 dariaSeq + 1 == env.
seq(daria),
1532 elmoSeq + 1 == env.
seq(elmo),
1536 fredSeq == env.
seq(fred),
1540 gwenSeq == env.
seq(gwen),
1544 hankSeq + 1 == env.
seq(hank),
1559 auto getTxsQueued = [&]() {
1562 for (
auto const& tx : txs)
1564 ++
result[tx.txn->at(sfAccount)];
1568 auto qTxCount1 = getTxsQueued();
1573 seq(aliceSeq + qTxCount1[alice.id()]++),
1578 seq(charlieSeq + qTxCount1[charlie.id()]++),
1582 seq(dariaSeq + qTxCount1[daria.id()]++),
1590 checkMetrics(__LINE__, env, 10, 10, 6, 5, minFeeLevel);
1597 seq(aliceSeq + qTxCount1[alice.id()]++),
1600 checkMetrics(__LINE__, env, 10, 10, 6, 5, minFeeLevel);
1609 auto qTxCount2 = getTxsQueued();
1615 aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq +
1616 gwenSeq + hankSeq + 7 ==
1617 env.
seq(alice) + env.
seq(bob) + env.
seq(charlie) + env.
seq(daria) +
1618 env.
seq(elmo) + env.
seq(fred) + env.
seq(gwen) + env.
seq(hank));
1621 aliceSeq + qTxCount1[alice.id()] - qTxCount2[alice.id()] ==
1626 bobSeq + qTxCount1[bob.id()] - qTxCount2[bob.id()] == env.
seq(bob),
1630 charlieSeq + qTxCount1[charlie.id()] - qTxCount2[charlie.id()] ==
1635 dariaSeq + qTxCount1[daria.id()] - qTxCount2[daria.id()] ==
1640 elmoSeq + qTxCount1[elmo.id()] - qTxCount2[elmo.id()] ==
1645 fredSeq + qTxCount1[fred.id()] - qTxCount2[fred.id()] ==
1650 gwenSeq + qTxCount1[gwen.id()] - qTxCount2[gwen.id()] ==
1655 hankSeq + qTxCount1[hank.id()] - qTxCount2[hank.id()] ==
1664 using namespace jtx;
1667 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"1"}}));
1669 auto alice =
Account(
"alice");
1683 json(R
"({"AccountTxnID": "0"})"),
1701 using namespace jtx;
1702 using namespace std::string_literals;
1709 {{
"minimum_txn_in_ledger_standalone",
"2"},
1710 {
"minimum_txn_in_ledger",
"5"},
1711 {
"target_txn_in_ledger",
"4"},
1712 {
"maximum_txn_in_ledger",
"5"}}));
1713 auto const baseFee = env.
current()->fees().base.drops();
1715 auto alice =
Account(
"alice");
1723 for (
int i = 0; i < 10; ++i)
1731 double const feeMultiplier =
1732 static_cast<double>(cost.drops()) / baseFee;
1754 {{
"minimum_txn_in_ledger",
"200"},
1755 {
"minimum_txn_in_ledger_standalone",
"200"},
1756 {
"target_txn_in_ledger",
"4"},
1757 {
"maximum_txn_in_ledger",
"5"}}));
1765 "The minimum number of low-fee transactions allowed "
1766 "per ledger (minimum_txn_in_ledger) exceeds "
1767 "the maximum number of low-fee transactions allowed per "
1768 "ledger (maximum_txn_in_ledger)."s);
1775 {{
"minimum_txn_in_ledger",
"200"},
1776 {
"minimum_txn_in_ledger_standalone",
"2"},
1777 {
"target_txn_in_ledger",
"4"},
1778 {
"maximum_txn_in_ledger",
"5"}}));
1786 "The minimum number of low-fee transactions allowed "
1787 "per ledger (minimum_txn_in_ledger) exceeds "
1788 "the maximum number of low-fee transactions allowed per "
1789 "ledger (maximum_txn_in_ledger)."s);
1796 {{
"minimum_txn_in_ledger",
"2"},
1797 {
"minimum_txn_in_ledger_standalone",
"200"},
1798 {
"target_txn_in_ledger",
"4"},
1799 {
"maximum_txn_in_ledger",
"5"}}));
1807 "The minimum number of low-fee transactions allowed "
1808 "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1809 "the maximum number of low-fee transactions allowed per "
1810 "ledger (maximum_txn_in_ledger)."s);
1817 using namespace jtx;
1818 testcase(
"unexpected balance change");
1823 {{
"minimum_txn_in_ledger_standalone",
"3"}},
1824 {{
"account_reserve",
"200"}, {
"owner_reserve",
"50"}}));
1826 auto alice =
Account(
"alice");
1832 auto const initQueueMax =
initFee(env, 3, 2, 10, 200, 50);
1839 auto USD = bob[
"USD"];
1853 auto aliceSeq = env.
seq(alice);
1897 for (
int i = 0; i < 9; ++i)
1912 using namespace jtx;
1915 auto alice =
Account(
"alice");
1917 auto charlie =
Account(
"charlie");
1918 auto daria =
Account(
"daria");
1922 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
1923 auto const baseFee = env.
current()->fees().base.drops();
1937 env(
regkey(alice, charlie));
1941 auto const aliceSeq = env.
seq(alice);
1957 env(
signers(alice, 2, {{bob}, {charlie}, {daria}}),
1976 auto const aliceSeq = env.
seq(alice);
1994 env(
signers(alice, 2, {{bob}, {charlie}, {daria}}),
2020 auto const aliceSeq = env.
seq(alice);
2044 using namespace jtx;
2047 auto alice =
Account(
"alice");
2049 auto charlie =
Account(
"charlie");
2050 auto daria =
Account(
"daria");
2054 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
2055 auto const baseFee = env.
current()->fees().base.drops();
2072 env(
regkey(alice, charlie));
2076 auto const aliceSeq = env.
seq(alice);
2092 env(
signers(alice, 2, {{bob}, {charlie}, {daria}}),
2096 env(
signers(alice, 2, {{bob}, {charlie}, {daria}}),
2120 auto const aliceSeq = env.
seq(alice);
2150 env(
signers(alice, 2, {{bob}, {charlie}, {daria}}),
2210 using namespace jtx;
2211 testcase(
"In-flight balance checks");
2216 {{
"minimum_txn_in_ledger_standalone",
"3"}},
2217 {{
"account_reserve",
"200"}, {
"owner_reserve",
"50"}}));
2219 auto alice =
Account(
"alice");
2220 auto charlie =
Account(
"charlie");
2231 auto const initQueueMax =
initFee(env, 3, 2, 10, 200, 50);
2235 checkMetrics(__LINE__, env, 0, initQueueMax, 0, limit);
2238 checkMetrics(__LINE__, env, 0, initQueueMax, limit + 1, limit);
2240 auto USD = gw[
"USD"];
2241 auto BUX = gw[
"BUX"];
2245 auto aliceSeq = env.
seq(alice);
2246 auto aliceBal = env.
balance(alice);
2253 checkMetrics(__LINE__, env, 1, initQueueMax, limit + 1, limit);
2258 checkMetrics(__LINE__, env, 2, initQueueMax, limit + 1, limit);
2274 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2275 aliceSeq = env.
seq(alice);
2276 aliceBal = env.
balance(alice);
2282 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit);
2287 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2295 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2311 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2312 aliceSeq = env.
seq(alice);
2313 aliceBal = env.
balance(alice);
2320 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit);
2328 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit);
2344 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2345 aliceSeq = env.
seq(alice);
2346 aliceBal = env.
balance(alice);
2354 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2370 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2372 aliceSeq = env.
seq(alice);
2373 aliceBal = env.
balance(alice);
2383 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2397 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2399 aliceSeq = env.
seq(alice);
2400 aliceBal = env.
balance(alice);
2408 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2420 auto const amount = USD(500000);
2421 env(
trust(alice, USD(50000000)));
2422 env(
trust(charlie, USD(50000000)));
2428 env(
pay(gw, alice, amount));
2435 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2437 aliceSeq = env.
seq(alice);
2438 aliceBal = env.
balance(alice);
2439 auto aliceUSD = env.
balance(alice, USD);
2443 env(
pay(alice, charlie, amount),
queued);
2448 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2466 env(
offer(gw,
XRP(500000), USD(50000)));
2472 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2474 aliceSeq = env.
seq(alice);
2475 aliceBal = env.
balance(alice);
2476 auto charlieUSD = env.
balance(charlie, USD);
2488 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2499 balance(charlie, charlieUSD + USD(1000)),
2507 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2509 aliceSeq = env.
seq(alice);
2510 aliceBal = env.
balance(alice);
2511 charlieUSD = env.
balance(charlie, USD);
2522 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit);
2533 balance(charlie, charlieUSD + USD(500)),
2543 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit);
2545 aliceSeq = env.
seq(alice);
2546 aliceBal = env.
balance(alice);
2553 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit);
2564 using namespace jtx;
2569 auto const alice =
Account(
"alice");
2588 auto USD = alice[
"USD"];
2630 using namespace jtx;
2631 testcase(
"acct in queue but empty");
2633 auto alice =
Account(
"alice");
2635 auto charlie =
Account(
"charlie");
2639 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
2640 auto const baseFee = env.
current()->fees().base.drops();
2668 env(
noop(bob),
fee(baseFee * 100));
2669 env(
noop(bob),
fee(baseFee * 100));
2670 env(
noop(bob),
fee(baseFee * 100));
2671 env(
noop(bob),
fee(baseFee * 100));
2672 env(
noop(bob),
fee(baseFee * 100));
2698 checkMetrics(__LINE__, env, 10, 10, 6, 5, expectedFeeLevel + 1);
2721 seq(charlieSeq - 1),
2725 seq(charlieSeq + 1),
2736 using namespace jtx;
2741 auto fee = env.
rpc(
"fee");
2748 result.isMember(jss::ledger_current_index) &&
2749 result[jss::ledger_current_index] == 3);
2761 auto const& levels =
result[jss::levels];
2777 result.isMember(jss::ledger_current_index) &&
2778 result[jss::ledger_current_index] == 4);
2789 auto const& levels =
result[jss::levels];
2811 using namespace jtx;
2812 testcase(
"expiration replacement");
2817 {{
"minimum_txn_in_ledger_standalone",
"1"},
2818 {
"ledgers_in_queue",
"10"},
2819 {
"maximum_txn_per_account",
"20"}}));
2821 auto const baseFee = env.
current()->fees().base.drops();
2824 auto const alice =
Account(
"alice");
2825 auto const bob =
Account(
"bob");
2830 auto const aliceSeq = env.
seq(alice);
2834 json(R
"({"LastLedgerSequence":5})"),
2838 json(R"({"LastLedgerSequence":5})"),
2842 json(R"({"LastLedgerSequence":10})"),
2846 json(R"({"LastLedgerSequence":11})"),
2849 auto const bobSeq = env.
seq(bob);
2853 for (
int i = 0; i < 3 + 4 + 5; ++i)
2857 checkMetrics(__LINE__, env, 4 + 3 + 4 + 5, std::nullopt, 2, 1);
2911 using namespace jtx;
2912 testcase(
"full queue gap handling");
2915 {{
"minimum_txn_in_ledger_standalone",
"1"},
2916 {
"ledgers_in_queue",
"10"},
2917 {
"maximum_txn_per_account",
"11"}});
2918 cfg->FEES.reference_fee = 10;
2919 Env env(*
this, std::move(cfg));
2921 auto const baseFee = env.
current()->fees().base.drops();
2926 auto const alice =
Account(
"alice");
2927 auto const bob =
Account(
"bob");
2932 auto const aliceSeq = env.
seq(alice);
2939 fee(baseFee * 20 + 1),
2943 json(R
"({"LastLedgerSequence":11})"),
2947 json(R"({"LastLedgerSequence":11})"),
2951 json(R"({"LastLedgerSequence":11})"),
2955 json(R"({"LastLedgerSequence":11})"),
2959 json(R"({"LastLedgerSequence":11})"),
2963 json(R"({"LastLedgerSequence": 5})"),
2967 json(R"({"LastLedgerSequence": 5})"),
2971 json(R"({"LastLedgerSequence": 5})"),
2975 json(R"({"LastLedgerSequence":11})"),
2979 auto const bobSeq = env.
seq(bob);
2983 for (
int i = 0; i < 2 + 4 + 5; ++i)
2987 checkMetrics(__LINE__, env, 10 + 2 + 4 + 5, std::nullopt, 2, 1);
3070 testcase(
"Autofilled sequence should account for TxQ");
3071 using namespace jtx;
3072 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"6"}}));
3073 auto const baseFee = env.
current()->fees().base.drops();
3077 auto const alice =
Account(
"alice");
3078 auto const bob =
Account(
"bob");
3079 env.
fund(
XRP(100000), alice, bob);
3085 auto const aliceSeq = env.
seq(alice);
3086 auto const lastLedgerSeq = env.
current()->info().seq + 2;
3089 for (
int i = 0; i < 5; ++i)
3096 json(jss::LastLedgerSequence, lastLedgerSeq),
3105 auto aliceStat = txQ.getAccountTxs(alice.id());
3108 for (
auto const& tx : aliceStat)
3112 tx.feeLevel ==
FeeLevel64{baseFeeLevel.fee() * 100});
3113 if (
seq.value() == aliceSeq + 2)
3116 tx.lastValid && *tx.lastValid == lastLedgerSeq);
3127 for (
int i = 0; i < 8; ++i)
3136 for (
int i = 0; i < 9; ++i)
3143 for (
int i = 0; i < 10; ++i)
3150 auto bobStat = txQ.getAccountTxs(bob.id());
3156 auto aliceStat = txQ.getAccountTxs(alice.id());
3157 auto seq = aliceSeq;
3159 for (
auto const& tx : aliceStat)
3162 if (
seq == aliceSeq + 2)
3167 tx.feeLevel ==
FeeLevel64{baseFeeLevel.fee() * 100});
3177 auto aliceStat = txQ.getAccountTxs(alice.id());
3178 auto seq = aliceSeq;
3180 for (
auto const& tx : aliceStat)
3193 auto bobStat = txQ.getAccountTxs(bob.id());
3197 auto aliceStat = txQ.getAccountTxs(alice.id());
3205 using namespace jtx;
3208 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
3209 auto const baseFee = env.
current()->fees().base.drops();
3213 env.
fund(
XRP(1000000), alice);
3216 auto const withQueue =
3217 R
"({ "account": ")" + alice.human() + R"(", "queue": true })";
3220 R"(", "queue": true, "ledger_index": 3 })";
3227 info.isMember(jss::result) &&
3228 info[jss::result].isMember(jss::account_data));
3233 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3235 info.isMember(jss::result) &&
3236 info[jss::result].isMember(jss::account_data));
3255 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3257 info.isMember(jss::result) &&
3258 info[jss::result].isMember(jss::account_data));
3259 auto const&
result = info[jss::result];
3284 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3286 info.isMember(jss::result) &&
3287 info[jss::result].isMember(jss::account_data));
3288 auto const&
result = info[jss::result];
3301 data[jss::Sequence].asUInt() +
3314 auto const& item =
queued[i];
3317 item[jss::fee_level] ==
3327 BEAST_EXPECT(item[jss::auth_change].asBool() ==
false);
3342 json(jss::LastLedgerSequence, 10),
3347 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3349 info.isMember(jss::result) &&
3350 info[jss::result].isMember(jss::account_data));
3351 auto const&
result = info[jss::result];
3352 auto const&
data =
result[jss::account_data];
3364 data[jss::Sequence].asUInt() +
3377 auto const& item =
queued[i];
3380 item[jss::fee_level] ==
3389 if (i ==
queued.size() - 1)
3397 BEAST_EXPECT(item[jss::auth_change].asBool() ==
false);
3411 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3413 info.isMember(jss::result) &&
3414 info[jss::result].isMember(jss::account_data));
3415 auto const&
result = info[jss::result];
3416 auto const&
data =
result[jss::account_data];
3428 data[jss::Sequence].asUInt() +
3441 auto const& item =
queued[i];
3444 item[jss::fee_level] ==
3447 if (i ==
queued.size() - 1)
3454 item[jss::max_spend_drops] ==
3468 item[jss::max_spend_drops] ==
3481 info.isMember(jss::result) &&
3491 auto const info = env.
rpc(
"json",
"account_info", withQueue);
3493 info.isMember(jss::result) &&
3494 info[jss::result].isMember(jss::account_data));
3495 auto const&
result = info[jss::result];
3512 using namespace jtx;
3515 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
3516 auto const baseFee = env.
current()->fees().base.drops();
3520 env.
fund(
XRP(1000000), alice);
3524 auto const server_info = env.
rpc(
"server_info");
3526 server_info.isMember(jss::result) &&
3527 server_info[jss::result].isMember(jss::info));
3528 auto const& info = server_info[jss::result][jss::info];
3530 info.isMember(jss::load_factor) && info[jss::load_factor] == 1);
3534 BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
3537 auto const server_state = env.
rpc(
"server_state");
3538 auto const& state = server_state[jss::result][jss::state];
3540 state.isMember(jss::load_factor) &&
3541 state[jss::load_factor] == 256);
3543 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3545 state.isMember(jss::load_factor_server) &&
3546 state[jss::load_factor_server] == 256);
3548 state.isMember(jss::load_factor_fee_escalation) &&
3549 state[jss::load_factor_fee_escalation] == 256);
3551 state.isMember(jss::load_factor_fee_queue) &&
3552 state[jss::load_factor_fee_queue] == 256);
3554 state.isMember(jss::load_factor_fee_reference) &&
3555 state[jss::load_factor_fee_reference] == 256);
3563 auto aliceSeq = env.
seq(alice);
3565 for (
auto i = 0; i < 4; ++i)
3574 auto const server_info = env.
rpc(
"server_info");
3576 server_info.isMember(jss::result) &&
3577 server_info[jss::result].isMember(jss::info));
3578 auto const& info = server_info[jss::result][jss::info];
3581 info.isMember(jss::load_factor) &&
3582 info[jss::load_factor] > 888.88 &&
3583 info[jss::load_factor] < 888.89);
3585 info.isMember(jss::load_factor_server) &&
3586 info[jss::load_factor_server] == 1);
3590 info.isMember(jss::load_factor_fee_escalation) &&
3591 info[jss::load_factor_fee_escalation] > 888.88 &&
3592 info[jss::load_factor_fee_escalation] < 888.89);
3595 auto const server_state = env.
rpc(
"server_state");
3596 auto const& state = server_state[jss::result][jss::state];
3598 state.isMember(jss::load_factor) &&
3599 state[jss::load_factor] == 227555);
3601 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3603 state.isMember(jss::load_factor_server) &&
3604 state[jss::load_factor_server] == 256);
3606 state.isMember(jss::load_factor_fee_escalation) &&
3607 state[jss::load_factor_fee_escalation] == 227555);
3609 state.isMember(jss::load_factor_fee_queue) &&
3610 state[jss::load_factor_fee_queue] == 256);
3612 state.isMember(jss::load_factor_fee_reference) &&
3613 state[jss::load_factor_fee_reference] == 256);
3619 auto const server_info = env.
rpc(
"server_info");
3621 server_info.isMember(jss::result) &&
3622 server_info[jss::result].isMember(jss::info));
3623 auto const& info = server_info[jss::result][jss::info];
3626 info.isMember(jss::load_factor) &&
3627 info[jss::load_factor] == 1000);
3631 info.isMember(jss::load_factor_net) &&
3632 info[jss::load_factor_net] == 1000);
3634 info.isMember(jss::load_factor_fee_escalation) &&
3635 info[jss::load_factor_fee_escalation] > 888.88 &&
3636 info[jss::load_factor_fee_escalation] < 888.89);
3639 auto const server_state = env.
rpc(
"server_state");
3640 auto const& state = server_state[jss::result][jss::state];
3642 state.isMember(jss::load_factor) &&
3643 state[jss::load_factor] == 256000);
3645 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3647 state.isMember(jss::load_factor_server) &&
3648 state[jss::load_factor_server] == 256000);
3650 state.isMember(jss::load_factor_fee_escalation) &&
3651 state[jss::load_factor_fee_escalation] == 227555);
3653 state.isMember(jss::load_factor_fee_queue) &&
3654 state[jss::load_factor_fee_queue] == 256);
3656 state.isMember(jss::load_factor_fee_reference) &&
3657 state[jss::load_factor_fee_reference] == 256);
3663 for (
int i = 0; i < 5; ++i)
3668 auto const server_info = env.
rpc(
"server_info");
3670 server_info.isMember(jss::result) &&
3671 server_info[jss::result].isMember(jss::info));
3672 auto const& info = server_info[jss::result][jss::info];
3675 info.isMember(jss::load_factor) &&
3676 info[jss::load_factor] > 888.88 &&
3677 info[jss::load_factor] < 888.89);
3682 info.isMember(jss::load_factor_server) &&
3683 info[jss::load_factor_server] > 1.245 &&
3684 info[jss::load_factor_server] < 2.4415);
3686 info.isMember(jss::load_factor_local) &&
3687 info[jss::load_factor_local] > 1.245 &&
3688 info[jss::load_factor_local] < 2.4415);
3691 info.isMember(jss::load_factor_fee_escalation) &&
3692 info[jss::load_factor_fee_escalation] > 888.88 &&
3693 info[jss::load_factor_fee_escalation] < 888.89);
3696 auto const server_state = env.
rpc(
"server_state");
3697 auto const& state = server_state[jss::result][jss::state];
3699 state.isMember(jss::load_factor) &&
3700 state[jss::load_factor] == 227555);
3702 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3707 state.isMember(jss::load_factor_server) &&
3708 state[jss::load_factor_server] >= 320 &&
3709 state[jss::load_factor_server] <= 625);
3711 state.isMember(jss::load_factor_fee_escalation) &&
3712 state[jss::load_factor_fee_escalation] == 227555);
3714 state.isMember(jss::load_factor_fee_queue) &&
3715 state[jss::load_factor_fee_queue] == 256);
3717 state.isMember(jss::load_factor_fee_reference) &&
3718 state[jss::load_factor_fee_reference] == 256);
3724 auto const server_info = env.
rpc(
"server_info");
3726 server_info.isMember(jss::result) &&
3727 server_info[jss::result].isMember(jss::info));
3728 auto const& info = server_info[jss::result][jss::info];
3735 info.isMember(jss::load_factor) &&
3736 info[jss::load_factor] > 1.245 &&
3737 info[jss::load_factor] < 2.4415);
3740 info.isMember(jss::load_factor_local) &&
3741 info[jss::load_factor_local] > 1.245 &&
3742 info[jss::load_factor_local] < 2.4415);
3744 BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
3747 auto const server_state = env.
rpc(
"server_state");
3748 auto const& state = server_state[jss::result][jss::state];
3750 state.isMember(jss::load_factor) &&
3751 state[jss::load_factor] >= 320 &&
3752 state[jss::load_factor] <= 625);
3754 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3759 state.isMember(jss::load_factor_server) &&
3760 state[jss::load_factor_server] >= 320 &&
3761 state[jss::load_factor_server] <= 625);
3763 state.isMember(jss::load_factor_fee_escalation) &&
3764 state[jss::load_factor_fee_escalation] == 256);
3766 state.isMember(jss::load_factor_fee_queue) &&
3767 state[jss::load_factor_fee_queue] == 256);
3769 state.isMember(jss::load_factor_fee_reference) &&
3770 state[jss::load_factor_fee_reference] == 256);
3777 using namespace jtx;
3780 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
3781 auto const baseFee = env.
current()->fees().base.drops();
3785 stream[jss::streams].append(
"server");
3788 auto jv = wsc->invoke(
"subscribe", stream);
3792 Account a{
"a"}, b{
"b"}, c{
"c"}, d{
"d"}, e{
"e"}, f{
"f"}, g{
"g"}, h{
"h"},
3800 using namespace std::chrono_literals;
3802 return jv[jss::type] ==
"serverStatus" &&
3803 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3804 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3805 jv.isMember(jss::load_factor_server) &&
3806 jv[jss::load_factor_server] == 256 &&
3807 jv.isMember(jss::load_factor_fee_escalation) &&
3808 jv[jss::load_factor_fee_escalation] == 256 &&
3809 jv.isMember(jss::load_factor_fee_queue) &&
3810 jv[jss::load_factor_fee_queue] == 256 &&
3811 jv.isMember(jss::load_factor_fee_reference) &&
3812 jv[jss::load_factor_fee_reference] == 256;
3816 return jv[jss::type] ==
"serverStatus" &&
3817 jv.isMember(jss::load_factor) &&
3818 jv[jss::load_factor] == 227555 && jv.isMember(jss::load_base) &&
3819 jv[jss::load_base] == 256 &&
3820 jv.isMember(jss::load_factor_server) &&
3821 jv[jss::load_factor_server] == 256 &&
3822 jv.isMember(jss::load_factor_fee_escalation) &&
3823 jv[jss::load_factor_fee_escalation] == 227555 &&
3824 jv.isMember(jss::load_factor_fee_queue) &&
3825 jv[jss::load_factor_fee_queue] == 256 &&
3826 jv.isMember(jss::load_factor_fee_reference) &&
3827 jv[jss::load_factor_fee_reference] == 256;
3834 return jv[jss::type] ==
"serverStatus" &&
3835 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3836 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3837 jv.isMember(jss::load_factor_server) &&
3838 jv[jss::load_factor_server] == 256 &&
3839 jv.isMember(jss::load_factor_fee_escalation) &&
3840 jv[jss::load_factor_fee_escalation] == 256 &&
3841 jv.isMember(jss::load_factor_fee_queue) &&
3842 jv[jss::load_factor_fee_queue] == 256 &&
3843 jv.isMember(jss::load_factor_fee_reference) &&
3844 jv[jss::load_factor_fee_reference] == 256;
3865 return jv[jss::type] ==
"serverStatus" &&
3866 jv.isMember(jss::load_factor) &&
3867 jv[jss::load_factor] == 200000 && jv.isMember(jss::load_base) &&
3868 jv[jss::load_base] == 256 &&
3869 jv.isMember(jss::load_factor_server) &&
3870 jv[jss::load_factor_server] == 256 &&
3871 jv.isMember(jss::load_factor_fee_escalation) &&
3872 jv[jss::load_factor_fee_escalation] == 200000 &&
3873 jv.isMember(jss::load_factor_fee_queue) &&
3874 jv[jss::load_factor_fee_queue] == 256 &&
3875 jv.isMember(jss::load_factor_fee_reference) &&
3876 jv[jss::load_factor_fee_reference] == 256;
3882 return jv[jss::type] ==
"serverStatus" &&
3883 jv.isMember(jss::load_factor) &&
3884 jv[jss::load_factor] == 184320 && jv.isMember(jss::load_base) &&
3885 jv[jss::load_base] == 256 &&
3886 jv.isMember(jss::load_factor_server) &&
3887 jv[jss::load_factor_server] == 256 &&
3888 jv.isMember(jss::load_factor_fee_escalation) &&
3889 jv[jss::load_factor_fee_escalation] == 184320 &&
3890 jv.isMember(jss::load_factor_fee_queue) &&
3891 jv[jss::load_factor_fee_queue] == 256 &&
3892 jv.isMember(jss::load_factor_fee_reference) &&
3893 jv[jss::load_factor_fee_reference] == 256;
3899 return jv[jss::type] ==
"serverStatus" &&
3900 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3901 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3902 jv.isMember(jss::load_factor_server) &&
3903 jv[jss::load_factor_server] == 256 &&
3904 jv.isMember(jss::load_factor_fee_escalation) &&
3905 jv[jss::load_factor_fee_escalation] == 256 &&
3906 jv.isMember(jss::load_factor_fee_queue) &&
3907 jv[jss::load_factor_fee_queue] == 256 &&
3908 jv.isMember(jss::load_factor_fee_reference) &&
3909 jv[jss::load_factor_fee_reference] == 256;
3913 return jv[jss::type] ==
"serverStatus";
3916 auto jv = wsc->invoke(
"unsubscribe", stream);
3923 using namespace jtx;
3926 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
3927 auto const baseFee = env.
current()->fees().base.drops();
3928 auto alice =
Account(
"alice");
3932 env.
fund(
XRP(50000000), alice, bob);
3939 auto totalFactor = 0;
3942 numToClear.emplace(metrics.txCount + 1);
3943 for (
int i = 0; i < *numToClear; ++i)
3946 totalFactor += inLedger * inLedger;
3949 auto const den = (metrics.txPerLedger * metrics.txPerLedger);
3951 (metrics.medFeeLevel * totalFactor +
FeeLevel64{den - 1}) / den;
3962 testcase(
"straightfoward positive case");
3965 auto aliceSeq = env.
seq(alice);
3967 for (
int i = 0; i < 2; ++i)
3973 totalPaid += baseFee * 10;
4001 totalPaid += totalFee;
4002 totalFee = calcTotalFee(totalPaid);
4005 env(
noop(alice),
fee(totalFee),
seq(aliceSeq++));
4010 testcase(
"replace last tx with enough to clear queue");
4013 auto aliceSeq = env.
seq(alice);
4015 for (
int i = 0; i < 2; ++i)
4021 totalPaid += baseFee * 10;
4038 calcTotalFee(totalPaid, metrics.txCount);
4041 env(
noop(alice),
fee(totalFee),
seq(aliceSeq++));
4050 testcase(
"replace middle tx with enough to clear queue");
4054 auto aliceSeq = env.
seq(alice);
4055 for (
int i = 0; i < 5; ++i)
4066 uint64_t const totalFee = calcTotalFee(baseFee * 10 * 2, 3);
4069 env(
noop(alice),
fee(totalFee),
seq(aliceSeq++));
4072 auto const aliceQueue =
4076 for (
auto const& tx : aliceQueue)
4080 tx.feeLevel ==
FeeLevel64{baseFeeLevel.fee() * 10});
4089 testcase(
"clear queue failure (load)");
4093 auto aliceSeq = env.
seq(alice);
4095 for (
int i = 0; i < 2; ++i)
4101 totalPaid += baseFee * 20;
4103 for (
int i = 0; i < 2; ++i)
4109 totalPaid += baseFee * 2.2;
4120 feeTrack.setRemoteFee(origFee * 5);
4133 feeTrack.setRemoteFee(origFee);
4152 using namespace jtx;
4153 using namespace std::chrono_literals;
4160 {{
"minimum_txn_in_ledger_standalone",
"3"},
4161 {
"normal_consensus_increase_percent",
"25"},
4162 {
"slow_consensus_decrease_percent",
"50"},
4163 {
"target_txn_in_ledger",
"10"},
4164 {
"maximum_txn_per_account",
"200"}}));
4165 auto alice =
Account(
"alice");
4168 env.
fund(
XRP(50000000), alice);
4172 auto seqAlice = env.
seq(alice);
4174 for (
int i = 0; i < txCount; ++i)
4176 checkMetrics(__LINE__, env, txCount, std::nullopt, 4, 3);
4218 env.
close(env.
now() + 5s, 10000ms);
4223 env.
close(env.
now() + 5s, 10000ms);
4228 env.
close(env.
now() + 5s, 10000ms);
4235 env.
close(env.
now() + 5s, 10000ms);
4246 {{
"minimum_txn_in_ledger_standalone",
"3"},
4247 {
"normal_consensus_increase_percent",
"150"},
4248 {
"slow_consensus_decrease_percent",
"150"},
4249 {
"target_txn_in_ledger",
"10"},
4250 {
"maximum_txn_per_account",
"200"}}));
4251 auto alice =
Account(
"alice");
4254 env.
fund(
XRP(50000000), alice);
4258 auto seqAlice = env.
seq(alice);
4260 for (
int i = 0; i < txCount; ++i)
4262 checkMetrics(__LINE__, env, txCount, std::nullopt, 4, 3);
4279 env.
close(env.
now() + 5s, 10000ms);
4300 testcase(
"Sequence in queue and open ledger");
4301 using namespace jtx;
4303 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
4305 auto const alice =
Account(
"alice");
4319 auto const aliceSeq = env.
seq(alice);
4365 testcase(
"Ticket in queue and open ledger");
4366 using namespace jtx;
4368 Env env(*
this,
makeConfig({{
"minimum_txn_in_ledger_standalone",
"3"}}));
4370 auto alice =
Account(
"alice");
4399 auto const tx = env.
jt(
4462 using namespace jtx;
4472 {{
"minimum_txn_in_ledger_standalone",
"1"},
4473 {
"ledgers_in_queue",
"5"},
4474 {
"maximum_txn_per_account",
"10"}},
4475 {{
"account_reserve",
"1000"}, {
"owner_reserve",
"50"}});
4477 auto& votingSection = cfg->section(
"voting");
4483 "reference_fee",
std::to_string(cfg->FEES.reference_fee.drops()));
4485 Env env(*
this, std::move(cfg));
4503 for (i = 0; i <= 257; ++i)
4511 __LINE__, env, 0, 5 * expectedPerLedger, 0, expectedPerLedger);
4516 using namespace std::chrono_literals;
4517 auto closeDuration = 80min;
4518 for (i = 0; i <= 255; ++i)
4520 env.
close(closeDuration);
4523 auto const baseFee = env.
current()->fees().base.drops();
4530 5 * expectedPerLedger,
4531 expectedPerLedger + 1,
4535 auto seqAlice = env.
seq(alice);
4536 auto seqBob = env.
seq(bob);
4537 auto seqCarol = env.
seq(carol);
4538 auto seqDaria = env.
seq(daria);
4539 auto seqEllie = env.
seq(ellie);
4540 auto seqFiona = env.
seq(fiona);
4543 int txFee{
static_cast<int>(baseFee * 9)};
4544 auto prepareFee = [&](
uint64_t multiplier) {
4545 return fee(txFee - multiplier * baseFee / 10);
4549 for (
int i = 0; i < 10; ++i)
4553 prepareFee(++multiplier),
4557 prepareFee(++multiplier),
4561 prepareFee(++multiplier),
4565 prepareFee(++multiplier),
4569 prepareFee(++multiplier),
4573 prepareFee(++multiplier),
4581 5 * expectedPerLedger,
4582 expectedPerLedger + 1,
4596 env.
close(closeDuration);
4597 auto expectedInLedger = expectedInQueue;
4599 (expectedInQueue > expectedPerLedger + 2
4600 ? expectedInQueue - (expectedPerLedger + 2)
4602 expectedInLedger -= expectedInQueue;
4603 ++expectedPerLedger;
4608 5 * expectedPerLedger,
4612 auto const expectedPerAccount = expectedInQueue / 6;
4613 auto const expectedRemainder = expectedInQueue % 6;
4617 seqBob - expectedPerAccount -
4618 (expectedRemainder > 4 ? 1 : 0));
4621 seqCarol - expectedPerAccount -
4622 (expectedRemainder > 3 ? 1 : 0));
4625 seqDaria - expectedPerAccount -
4626 (expectedRemainder > 2 ? 1 : 0));
4629 seqEllie - expectedPerAccount -
4630 (expectedRemainder > 1 ? 1 : 0));
4633 seqFiona - expectedPerAccount -
4634 (expectedRemainder > 0 ? 1 : 0));
4636 }
while (expectedInQueue > 0);
4649 testcase(
"Queue full drop penalty");
4650 using namespace jtx;
4664 {{
"minimum_txn_in_ledger_standalone",
"5"},
4665 {
"ledgers_in_queue",
"5"},
4666 {
"maximum_txn_per_account",
"30"},
4667 {
"minimum_queue_size",
"50"}});
4669 Env env(*
this, std::move(cfg));
4670 auto const baseFee = env.current()->fees().base.drops();
4675 int const medFee = baseFee * 10;
4676 int const hiFee = baseFee * 100;
4680 env.fund(
XRP(10000),
noripple(alice, bob, carol, daria, ellie, fiona));
4696 auto seqAlice = env.seq(alice);
4697 auto const seqSaveAlice = seqAlice;
4698 int feeDrops = baseFee * 4;
4702 json(R
"({"LastLedgerSequence": 7})"),
4712 json(R
"({"LastLedgerSequence": 7})"),
4725 auto seqCarol = env.seq(carol);
4726 auto seqDaria = env.seq(daria);
4727 auto seqEllie = env.seq(ellie);
4728 auto seqFiona = env.seq(fiona);
4730 for (
int i = 0; i < 7; ++i)
4744 for (
int i = 0; i < 3; ++i)
4757 for (
int i = 0; i < 3; ++i)
4778 for (
int i = 0; i < 4; ++i)
4797 for (
int i = 0; i < 30; ++i)
4798 env.app().getFeeTrack().raiseLocalFee();
4808 while (env.app().getFeeTrack().lowerLocalFee())
4834 seqAlice = seqSaveAlice;
4887 using namespace jtx;
4891 auto USD = gw[
"USD"];
4894 {{
"minimum_txn_in_ledger_standalone",
"5"},
4895 {
"ledgers_in_queue",
"5"},
4896 {
"maximum_txn_per_account",
"30"},
4897 {
"minimum_queue_size",
"50"}});
4899 Env env(*
this, std::move(cfg));
4911 auto const aliceSeq = env.seq(alice);
4914 env(
offer(alice, USD(1000),
XRP(1001)),
4920 env(
offer(alice, USD(1000),
XRP(1002)),
4922 json(jss::OfferSequence, aliceSeq),
4938 auto const aliceTkt = env.seq(alice);
4946 auto const aliceSeq = env.seq(alice);
4947 env(
offer(alice, USD(1000),
XRP(1000)),
4951 env(
offer(alice, USD(1000),
XRP(1001)),
4961 env(
offer(alice, USD(1000),
XRP(1002)),
4963 json(jss::OfferSequence, aliceTkt + 4),
4973 env(
offer(alice, USD(1000),
XRP(1001)),
4978 env(
offer(alice, USD(1000),
XRP(1002)),
4980 json(jss::OfferSequence, aliceSeq),
5000 using namespace jtx;
5008 {{
"minimum_txn_in_ledger_standalone",
"3"}},
5009 {{
"reference_fee",
"0"},
5010 {
"account_reserve",
"0"},
5011 {
"owner_reserve",
"0"}}));
5016 auto const initQueueMax =
initFee(env, 3, 2, 0, 0, 0);
5021 auto const fee = env.
rpc(
"fee");
5029 auto const& levels =
result[jss::levels];
5031 levels.isMember(jss::median_level) &&
5032 levels[jss::median_level] ==
"128000");
5034 levels.isMember(jss::minimum_level) &&
5035 levels[jss::minimum_level] ==
"256");
5037 levels.isMember(jss::open_ledger_level) &&
5038 levels[jss::open_ledger_level] ==
"256");
5040 levels.isMember(jss::reference_level) &&
5041 levels[jss::reference_level] ==
"256");
5045 drops.isMember(jss::base_fee) &&
5046 drops[jss::base_fee] ==
"0");
5048 drops.isMember(jss::median_fee) &&
5049 drops[jss::median_fee] ==
"0");
5051 drops.isMember(jss::minimum_fee) &&
5052 drops[jss::minimum_fee] ==
"0");
5054 drops.isMember(jss::open_ledger_fee) &&
5055 drops[jss::open_ledger_fee] ==
"0");
5079 auto aliceSeq = env.
seq(alice);
5089 auto const fee = env.
rpc(
"fee");
5097 auto const& levels =
result[jss::levels];
5099 levels.isMember(jss::median_level) &&
5100 levels[jss::median_level] ==
"128000");
5102 levels.isMember(jss::minimum_level) &&
5103 levels[jss::minimum_level] ==
"256");
5105 levels.isMember(jss::open_ledger_level) &&
5106 levels[jss::open_ledger_level] ==
"355555");
5108 levels.isMember(jss::reference_level) &&
5109 levels[jss::reference_level] ==
"256");
5113 drops.isMember(jss::base_fee) &&
5114 drops[jss::base_fee] ==
"0");
5116 drops.isMember(jss::median_fee) &&
5117 drops[jss::median_fee] ==
"0");
5119 drops.isMember(jss::minimum_fee) &&
5120 drops[jss::minimum_fee] ==
"0");
5122 drops.isMember(jss::open_ledger_fee) &&
5123 drops[jss::open_ledger_fee] ==
"1389");
5185BEAST_DEFINE_TESTSUITE_PRIO(TxQPosNegFlows, app,
ripple, 1);
5186BEAST_DEFINE_TESTSUITE_PRIO(TxQMetaInfo, app,
ripple, 1);
A generic endpoint for log messages.
void pass()
Record a successful test condition.
testcase_t testcase
Memberspace for declaring test cases.
void fail(String const &reason, char const *file, int line)
Record a failure.
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual OpenLedger & openLedger()=0
void setRemoteFee(std::uint32_t f)
std::uint32_t getRemoteFee() const
std::uint32_t getLoadFactor() const
bool modify(modify_type const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
A type that represents either a sequence value or a ticket value.
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
std::vector< TxDetails > getAccountTxs(AccountID const &account) const
Returns information about the transactions currently in the queue for the account.
constexpr value_type drops() const
Returns the number of drops.
constexpr value_type value() const
Returns the underlying value.
constexpr value_type fee() const
Returns the number of drops.
BEAST_EXPECT(queue_data[jss::highest_sequence]==data[jss::Sequence].asUInt()+queue_data[jss::txn_count].asUInt() - 1)
auto calcMedFeeLevel(FeeLevel64 const feeLevel)
void testSignAndSubmitSequence()
void testQueueFullDropPenalty()
BEAST_EXPECT(queue_data[jss::txn_count]==0)
checkMetrics(__LINE__, env, 0, 10, 2, 5)
void testFullQueueGapFill()
void testServerSubscribe()
auto const prevLedgerWithQueue
BEAST_EXPECT(queue_data.isObject())
BEAST_EXPECT(!queue_data.isMember(jss::transactions))
static std::unique_ptr< Config > makeConfig(std::map< std::string, std::string > extraTxQ={}, std::map< std::string, std::string > extraVoting={})
void testZeroReferenceFee()
envs(noop(alice), fee(baseFee *10), seq(none), ter(terQUEUED))(submitParams)
void checkMetrics(int line, jtx::Env &env, std::size_t expectedCount, std::optional< std::size_t > expectedMaxCount, std::size_t expectedInLedger, std::size_t expectedPerLedger, std::uint64_t expectedMinFeeLevel=baseFeeLevel.fee(), std::uint64_t expectedMedFeeLevel=minEscalationFeeLevel.fee())
void testInFlightBalance()
void testInLedgerTicket()
auto calcMedFeeLevel(FeeLevel64 const feeLevel1, FeeLevel64 const feeLevel2)
void testCancelQueuedOffers()
BEAST_EXPECT(info.isMember(jss::result) &&RPC::contains_error(info[jss::result]))
void testReexecutePreflight()
checkMetrics(__LINE__, env, 1, 8, 5, 4)
BEAST_EXPECT(queue_data[jss::auth_change_queued]==true)
void testExpirationReplacement()
envs(noop(alice), fee(baseFee *10), seq(none), ter(telCAN_NOT_QUEUE_BLOCKED))(submitParams)
BEAST_EXPECT(queue_data.isMember(jss::transactions))
static constexpr FeeLevel64 baseFeeLevel
checkMetrics(__LINE__, env, 0, 6, 4, 3)
BEAST_EXPECT(queue_data[jss::max_spend_drops_total]==std::to_string(baseFee *10))
BEAST_EXPECT(info.isMember(jss::result) &&info[jss::result].isMember(jss::account_data))
checkMetrics(__LINE__, env, 4, 6, 4, 3)
checkMetrics(__LINE__, env, 0, 10, 0, 5)
BEAST_EXPECT(queue_data[jss::txn_count]==4)
BEAST_EXPECT(queue_data[jss::lowest_sequence]==data[jss::Sequence])
BEAST_EXPECT(env.current() ->info().seq > 3)
BEAST_EXPECT(result.isMember(jss::queue_data))
envs(noop(alice), seq(none))(submitParams)
BEAST_EXPECT(!info[jss::result].isMember(jss::queue_data))
void fillQueue(jtx::Env &env, jtx::Account const &account)
BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total))
BEAST_EXPECT(queue_data[jss::auth_change_queued]==false)
static constexpr FeeLevel64 minEscalationFeeLevel
checkMetrics(__LINE__, env, 0, 8, 4, 4)
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total))
BEAST_EXPECT(queue_data[jss::auth_change_queued].asBool())
std::size_t initFee(jtx::Env &env, std::size_t expectedPerLedger, std::size_t ledgersInQueue, std::uint32_t base, std::uint32_t reserve, std::uint32_t increment)
BEAST_EXPECT(queued.size()==queue_data[jss::txn_count])
auto openLedgerCost(jtx::Env &env)
auto txFeeLevelByAccount(jtx::Env &env, jtx::Account const &account)
void testFailInPreclaim()
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued))
BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence))
void testClearQueuedAccountTxs()
envs(fset(alice, asfAccountTxnID), fee(baseFee *10), seq(none), json(jss::LastLedgerSequence, 10), ter(terQUEUED))(submitParams)
BEAST_EXPECT(queue_data[jss::max_spend_drops_total]==std::to_string(baseFee *40))
BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence))
BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence))
checkMetrics(__LINE__, env, 0, 6, 0, 3)
void run() override
Runs the suite.
BEAST_EXPECT(queue_data.isMember(jss::highest_sequence))
void testAcctInQueueButEmpty()
BEAST_EXPECT(queue_data.isMember(jss::txn_count))
BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued))
void testBlockersTicket()
BEAST_EXPECT(queue_data[jss::txn_count]==1)
void testUnexpectedBalanceChange()
void testMultiTxnPerAccount()
Immutable cryptographic account descriptor.
static Account const master
The master account.
A transaction testing environment wrapper.
A transaction testing environment.
std::shared_ptr< ReadView const > closed()
Returns the last closed ledger.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
void postconditions(JTx const &jt, ParsedResult const &parsed, Json::Value const &jr=Json::Value())
Check expected postconditions of JTx submission.
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
NetClock::time_point now()
Returns the current network time.
beast::Journal const journal
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void memoize(Account const &account)
Associate AccountID with account.
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Match the number of items in the account's owner directory.
Check a set of conditions.
Sets the SendMax on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Set a ticket sequence on a JTx.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
owner_count< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
std::unique_ptr< WSClient > makeWSClient(Config const &cfg, bool v2, unsigned rpc_version, std::unordered_map< std::string, std::string > const &headers)
Returns a client operating through WebSockets/S.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
@ telCAN_NOT_QUEUE_BLOCKED
@ telCAN_NOT_QUEUE_BALANCE
@ telCAN_NOT_QUEUE_BLOCKS
constexpr std::uint32_t asfAccountTxnID
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
majorityAmendments_t getMajorityAmendments(ReadView const &view)
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
std::size_t txInLedger
Number of transactions currently in the open ledger.
Used by parseResult() and postConditions()
Set the sequence number on a JTx.