rippled
Loading...
Searching...
No Matches
TxQ_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx.h>
21#include <test/jtx/TestSuite.h>
22#include <test/jtx/WSClient.h>
23#include <test/jtx/envconfig.h>
24#include <test/jtx/ticket.h>
25#include <xrpld/app/main/Application.h>
26#include <xrpld/app/misc/LoadFeeTrack.h>
27#include <xrpld/app/misc/TxQ.h>
28#include <xrpld/app/tx/apply.h>
29#include <xrpl/protocol/ErrorCodes.h>
30#include <xrpl/protocol/jss.h>
31#include <xrpl/protocol/st.h>
32
33namespace ripple {
34
35namespace test {
36
38{
39 void
41 int line,
42 jtx::Env& env,
43 std::size_t expectedCount,
44 std::optional<std::size_t> expectedMaxCount,
45 std::size_t expectedInLedger,
46 std::size_t expectedPerLedger,
47 std::uint64_t expectedMinFeeLevel,
48 std::uint64_t expectedMedFeeLevel = 256 * 500)
49 {
50 FeeLevel64 const expectedMin{expectedMinFeeLevel};
51 FeeLevel64 const expectedMed{expectedMedFeeLevel};
52 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
53 using namespace std::string_literals;
54
55 metrics.referenceFeeLevel == FeeLevel64{256}
56 ? pass()
57 : fail(
58 "reference: "s +
59 std::to_string(metrics.referenceFeeLevel.value()) +
60 "/256",
61 __FILE__,
62 line);
63
64 metrics.txCount == expectedCount
65 ? pass()
66 : fail(
67 "txCount: "s + std::to_string(metrics.txCount) + "/" +
68 std::to_string(expectedCount),
69 __FILE__,
70 line);
71
72 metrics.txQMaxSize == expectedMaxCount
73 ? pass()
74 : fail(
75 "txQMaxSize: "s +
76 std::to_string(metrics.txQMaxSize.value_or(0)) + "/" +
77 std::to_string(expectedMaxCount.value_or(0)),
78 __FILE__,
79 line);
80
81 metrics.txInLedger == expectedInLedger
82 ? pass()
83 : fail(
84 "txInLedger: "s + std::to_string(metrics.txInLedger) + "/" +
85 std::to_string(expectedInLedger),
86 __FILE__,
87 line);
88
89 metrics.txPerLedger == expectedPerLedger
90 ? pass()
91 : fail(
92 "txPerLedger: "s + std::to_string(metrics.txPerLedger) + "/" +
93 std::to_string(expectedPerLedger),
94 __FILE__,
95 line);
96
97 metrics.minProcessingFeeLevel == expectedMin
98 ? pass()
99 : fail(
100 "minProcessingFeeLevel: "s +
101 std::to_string(metrics.minProcessingFeeLevel.value()) +
102 "/" + std::to_string(expectedMin.value()),
103 __FILE__,
104 line);
105
106 metrics.medFeeLevel == expectedMed
107 ? pass()
108 : fail(
109 "medFeeLevel: "s +
110 std::to_string(metrics.medFeeLevel.value()) + "/" +
111 std::to_string(expectedMed.value()),
112 __FILE__,
113 line);
114
115 auto const expectedCurFeeLevel = expectedInLedger > expectedPerLedger
116 ? expectedMed * expectedInLedger * expectedInLedger /
117 (expectedPerLedger * expectedPerLedger)
118 : metrics.referenceFeeLevel;
119
120 metrics.openLedgerFeeLevel == expectedCurFeeLevel
121 ? pass()
122 : fail(
123 "openLedgerFeeLevel: "s +
124 std::to_string(metrics.openLedgerFeeLevel.value()) + "/" +
125 std::to_string(expectedCurFeeLevel.value()),
126 __FILE__,
127 line);
128 }
129
130 void
131 fillQueue(jtx::Env& env, jtx::Account const& account)
132 {
133 auto metrics = env.app().getTxQ().getMetrics(*env.current());
134 for (int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
135 env(noop(account));
136 }
137
138 auto
140 {
141 using namespace jtx;
142
143 auto const& view = *env.current();
144 auto metrics = env.app().getTxQ().getMetrics(view);
145 auto const base = [&view]() {
146 auto base = view.fees().base;
147 if (!base)
148 base += 1;
149 return base;
150 }();
151
152 // Don't care about the overflow flag
153 return fee(toDrops(metrics.openLedgerFeeLevel, base) + 1);
154 }
155
159 std::map<std::string, std::string> extraVoting = {})
160 {
161 auto p = test::jtx::envconfig();
162 auto& section = p->section("transaction_queue");
163 section.set("ledgers_in_queue", "2");
164 section.set("minimum_queue_size", "2");
165 section.set("min_ledgers_to_compute_size_limit", "3");
166 section.set("max_ledger_counts_to_store", "100");
167 section.set("retry_sequence_percent", "25");
168 section.set("normal_consensus_increase_percent", "0");
169
170 for (auto const& [k, v] : extraTxQ)
171 section.set(k, v);
172
173 // Some tests specify different fee settings that are enabled by
174 // a FeeVote
175 if (!extraVoting.empty())
176 {
177 auto& votingSection = p->section("voting");
178 for (auto const& [k, v] : extraVoting)
179 {
180 votingSection.set(k, v);
181 }
182
183 // In order for the vote to occur, we must run as a validator
184 p->section("validation_seed")
185 .legacy("shUwVw52ofnCUX5m7kPTKzJdr4HEH");
186 }
187 return p;
188 }
189
192 jtx::Env& env,
193 std::size_t expectedPerLedger,
194 std::size_t ledgersInQueue,
195 std::uint32_t base,
197 std::uint32_t increment)
198 {
199 // Run past the flag ledger so that a Fee change vote occurs and
200 // lowers the reserve fee. (It also activates all supported
201 // amendments.) This will allow creating accounts with lower
202 // reserves and balances.
203 for (auto i = env.current()->seq(); i <= 257; ++i)
204 env.close();
205 // The ledger after the flag ledger creates all the
206 // fee (1) and amendment (numUpVotedAmendments())
207 // pseudotransactions. The queue treats the fees on these
208 // transactions as though they are ordinary transactions.
209 auto const flagPerLedger = 1 + ripple::detail::numUpVotedAmendments();
210 auto const flagMaxQueue = ledgersInQueue * flagPerLedger;
211 checkMetrics(__LINE__, env, 0, flagMaxQueue, 0, flagPerLedger, 256);
212
213 // Pad a couple of txs with normal fees so the median comes
214 // back down to normal
215 env(noop(env.master));
216 env(noop(env.master));
217
218 // Close the ledger with a delay, which causes all the TxQ
219 // metrics to reset to defaults, EXCEPT the maxQueue size.
220 using namespace std::chrono_literals;
221 env.close(env.now() + 5s, 10000ms);
222 checkMetrics(__LINE__, env, 0, flagMaxQueue, 0, expectedPerLedger, 256);
223 auto const fees = env.current()->fees();
224 BEAST_EXPECT(fees.base == XRPAmount{base});
225 BEAST_EXPECT(fees.reserve == XRPAmount{reserve});
226 BEAST_EXPECT(fees.increment == XRPAmount{increment});
227
228 return flagMaxQueue;
229 }
230
231public:
232 void
234 {
235 using namespace jtx;
236 using namespace std::chrono;
237 testcase("queue sequence");
238
239 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
240
241 auto alice = Account("alice");
242 auto bob = Account("bob");
243 auto charlie = Account("charlie");
244 auto daria = Account("daria");
245 auto elmo = Account("elmo");
246 auto fred = Account("fred");
247 auto gwen = Account("gwen");
248 auto hank = Account("hank");
249 auto iris = Account("iris");
250
251 auto queued = ter(terQUEUED);
252
253 BEAST_EXPECT(env.current()->fees().base == 10);
254
255 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
256
257 // Create several accounts while the fee is cheap so they all apply.
258 env.fund(XRP(50000), noripple(alice, bob, charlie, daria));
259 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
260
261 // Alice - price starts exploding: held
262 env(noop(alice), queued);
263 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 3, 256);
264
265 // Bob with really high fee - applies
266 env(noop(bob), openLedgerFee(env));
267 checkMetrics(__LINE__, env, 1, std::nullopt, 5, 3, 256);
268
269 // Daria with low fee: hold
270 env(noop(daria), fee(1000), queued);
271 checkMetrics(__LINE__, env, 2, std::nullopt, 5, 3, 256);
272
273 env.close();
274 // Verify that the held transactions got applied
275 checkMetrics(__LINE__, env, 0, 10, 2, 5, 256);
276
278
279 // Make some more accounts. We'll need them later to abuse the queue.
280 env.fund(XRP(50000), noripple(elmo, fred, gwen, hank));
281 checkMetrics(__LINE__, env, 0, 10, 6, 5, 256);
282
283 // Now get a bunch of transactions held.
284 env(noop(alice), fee(12), queued);
285 checkMetrics(__LINE__, env, 1, 10, 6, 5, 256);
286
287 env(noop(bob), fee(10), queued); // won't clear the queue
288 env(noop(charlie), fee(20), queued);
289 env(noop(daria), fee(15), queued);
290 env(noop(elmo), fee(11), queued);
291 env(noop(fred), fee(19), queued);
292 env(noop(gwen), fee(16), queued);
293 env(noop(hank), fee(18), queued);
294 checkMetrics(__LINE__, env, 8, 10, 6, 5, 256);
295
296 env.close();
297 // Verify that the held transactions got applied
298 checkMetrics(__LINE__, env, 1, 12, 7, 6, 256);
299
300 // Bob's transaction is still stuck in the queue.
301
303
304 // Hank sends another txn
305 env(noop(hank), fee(10), queued);
306 // But he's not going to leave it in the queue
307 checkMetrics(__LINE__, env, 2, 12, 7, 6, 256);
308
309 // Hank sees his txn got held and bumps the fee,
310 // but doesn't even bump it enough to requeue
311 env(noop(hank), fee(11), ter(telCAN_NOT_QUEUE_FEE));
312 checkMetrics(__LINE__, env, 2, 12, 7, 6, 256);
313
314 // Hank sees his txn got held and bumps the fee,
315 // enough to requeue, but doesn't bump it enough to
316 // apply to the ledger
317 env(noop(hank), fee(6000), queued);
318 // But he's not going to leave it in the queue
319 checkMetrics(__LINE__, env, 2, 12, 7, 6, 256);
320
321 // Hank sees his txn got held and bumps the fee,
322 // high enough to get into the open ledger, because
323 // he doesn't want to wait.
324 env(noop(hank), openLedgerFee(env));
325 checkMetrics(__LINE__, env, 1, 12, 8, 6, 256);
326
327 // Hank then sends another, less important txn
328 // (In addition to the metrics, this will verify that
329 // the original txn got removed.)
330 env(noop(hank), fee(6000), queued);
331 checkMetrics(__LINE__, env, 2, 12, 8, 6, 256);
332
333 env.close();
334
335 // Verify that bob and hank's txns were applied
336 checkMetrics(__LINE__, env, 0, 16, 2, 8, 256);
337
338 // Close again with a simulated time leap to
339 // reset the escalation limit down to minimum
340 env.close(env.now() + 5s, 10000ms);
341 checkMetrics(__LINE__, env, 0, 16, 0, 3, 256);
342 // Then close once more without the time leap
343 // to reset the queue maxsize down to minimum
344 env.close();
345 checkMetrics(__LINE__, env, 0, 6, 0, 3, 256);
346
348
349 // Stuff the ledger and queue so we can verify that
350 // stuff gets kicked out.
351 env(noop(hank), fee(7000));
352 env(noop(gwen), fee(7000));
353 env(noop(fred), fee(7000));
354 env(noop(elmo), fee(7000));
355 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
356
357 // Use explicit fees so we can control which txn
358 // will get dropped
359 // This one gets into the queue, but gets dropped when the
360 // higher fee one is added later.
361 env(noop(daria), fee(15), queued);
362 // These stay in the queue.
363 env(noop(elmo), fee(16), queued);
364 env(noop(fred), fee(17), queued);
365 env(noop(gwen), fee(18), queued);
366 env(noop(hank), fee(19), queued);
367 env(noop(alice), fee(20), queued);
368
369 // Queue is full now.
370 checkMetrics(__LINE__, env, 6, 6, 4, 3, 385);
371
372 // Try to add another transaction with the default (low) fee,
373 // it should fail because the queue is full.
374 env(noop(charlie), ter(telCAN_NOT_QUEUE_FULL));
375
376 // Add another transaction, with a higher fee,
377 // Not high enough to get into the ledger, but high
378 // enough to get into the queue (and kick somebody out)
379 env(noop(charlie), fee(100), queued);
380
381 // Queue is still full, of course, but the min fee has gone up
382 checkMetrics(__LINE__, env, 6, 6, 4, 3, 410);
383
384 // Close out the ledger, the transactions are accepted, the
385 // queue is cleared, then the localTxs are retried. At this
386 // point, daria's transaction that was dropped from the queue
387 // is put back in. Neat.
388 env.close();
389 checkMetrics(__LINE__, env, 2, 8, 5, 4, 256, 256 * 700);
390
391 env.close();
392 checkMetrics(__LINE__, env, 0, 10, 2, 5, 256);
393
395
396 // Attempt to put a transaction in the queue for an account
397 // that is not yet funded.
398 env.memoize(iris);
399
400 env(noop(alice));
401 env(noop(bob));
402 env(noop(charlie));
403 env(noop(daria));
404 env(pay(alice, iris, XRP(1000)), queued);
405 env(noop(iris), seq(1), fee(20), ter(terNO_ACCOUNT));
406 checkMetrics(__LINE__, env, 1, 10, 6, 5, 256);
407
408 env.close();
409 checkMetrics(__LINE__, env, 0, 12, 1, 6, 256);
410
411 env.require(balance(iris, XRP(1000)));
412 BEAST_EXPECT(env.seq(iris) == 11);
413
415 // Cleanup:
416
417 // Create a few more transactions, so that
418 // we can be sure that there's one in the queue when the
419 // test ends and the TxQ is destructed.
420
421 auto metrics = env.app().getTxQ().getMetrics(*env.current());
422 BEAST_EXPECT(metrics.txCount == 0);
423
424 // Stuff the ledger.
425 for (int i = metrics.txInLedger; i <= metrics.txPerLedger; ++i)
426 {
427 env(noop(env.master));
428 }
429
430 // Queue one straightforward transaction
431 env(noop(env.master), fee(20), queued);
432 ++metrics.txCount;
433
435 __LINE__,
436 env,
437 metrics.txCount,
438 metrics.txQMaxSize,
439 metrics.txPerLedger + 1,
440 metrics.txPerLedger,
441 256);
442 }
443
444 void
446 {
447 using namespace jtx;
448 testcase("queue ticket");
449
450 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
451
452 auto alice = Account("alice");
453
454 auto queued = ter(terQUEUED);
455
456 BEAST_EXPECT(env.current()->fees().base == 10);
457
458 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
459
460 // Fund alice and then fill the ledger.
461 env.fund(XRP(50000), noripple(alice));
462 env(noop(alice));
463 env(noop(alice));
464 env(noop(alice));
465 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
466
468
469 // Alice requests tickets, but that transaction is queued. So
470 // Alice can't queue ticketed transactions yet.
471 std::uint32_t const tkt1{env.seq(alice) + 1};
472 env(ticket::create(alice, 250), seq(tkt1 - 1), queued);
473
474 env(noop(alice), ticket::use(tkt1 - 2), ter(tefNO_TICKET));
475 env(noop(alice), ticket::use(tkt1 - 1), ter(terPRE_TICKET));
476 env.require(owners(alice, 0), tickets(alice, 0));
477 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 3, 256);
478
479 env.close();
480 env.require(owners(alice, 250), tickets(alice, 250));
481 checkMetrics(__LINE__, env, 0, 8, 1, 4, 256);
482 BEAST_EXPECT(env.seq(alice) == tkt1 + 250);
483
485
486 // Unlike queued sequence-based transactions, ticket-based
487 // transactions _do_ move out of the queue largest fee first,
488 // even within one account, since they can be applied in any order.
489 // Demonstrate that.
490
491 // Fill the ledger so we can start queuing things.
492 env(noop(alice), ticket::use(tkt1 + 1), fee(11));
493 env(noop(alice), ticket::use(tkt1 + 2), fee(12));
494 env(noop(alice), ticket::use(tkt1 + 3), fee(13));
495 env(noop(alice), ticket::use(tkt1 + 4), fee(14));
496 env(noop(alice), ticket::use(tkt1 + 5), fee(15), queued);
497 env(noop(alice), ticket::use(tkt1 + 6), fee(16), queued);
498 env(noop(alice), ticket::use(tkt1 + 7), fee(17), queued);
499 env(noop(alice), ticket::use(tkt1 + 8), fee(18), queued);
500 env(noop(alice), ticket::use(tkt1 + 9), fee(19), queued);
501 env(noop(alice), ticket::use(tkt1 + 10), fee(20), queued);
502 env(noop(alice), ticket::use(tkt1 + 11), fee(21), queued);
503 env(noop(alice), ticket::use(tkt1 + 12), fee(22), queued);
504 env(noop(alice),
505 ticket::use(tkt1 + 13),
506 fee(23),
508 checkMetrics(__LINE__, env, 8, 8, 5, 4, 385);
509
510 // Check which of the queued transactions got into the ledger by
511 // attempting to replace them.
512 // o Get tefNO_TICKET if the ticket has already been used.
513 // o Get telCAN_NOT_QUEUE_FEE if the transaction is still in the queue.
514 env.close();
515 env.require(owners(alice, 240), tickets(alice, 240));
516
517 // These 4 went straight to the ledger:
518 env(noop(alice), ticket::use(tkt1 + 1), ter(tefNO_TICKET));
519 env(noop(alice), ticket::use(tkt1 + 2), ter(tefNO_TICKET));
520 env(noop(alice), ticket::use(tkt1 + 3), ter(tefNO_TICKET));
521 env(noop(alice), ticket::use(tkt1 + 4), ter(tefNO_TICKET));
522
523 // These two are still in the TxQ:
524 env(noop(alice), ticket::use(tkt1 + 5), ter(telCAN_NOT_QUEUE_FEE));
525 env(noop(alice), ticket::use(tkt1 + 6), ter(telCAN_NOT_QUEUE_FEE));
526
527 // These six were moved from the queue into the open ledger
528 // since those with the highest fees go first.
529 env(noop(alice), ticket::use(tkt1 + 7), ter(tefNO_TICKET));
530 env(noop(alice), ticket::use(tkt1 + 8), ter(tefNO_TICKET));
531 env(noop(alice), ticket::use(tkt1 + 9), ter(tefNO_TICKET));
532 env(noop(alice), ticket::use(tkt1 + 10), ter(tefNO_TICKET));
533 env(noop(alice), ticket::use(tkt1 + 11), ter(tefNO_TICKET));
534 env(noop(alice), ticket::use(tkt1 + 12), ter(tefNO_TICKET));
535
536 // This last one was moved from the local transactions into
537 // the queue.
538 env(noop(alice), ticket::use(tkt1 + 13), ter(telCAN_NOT_QUEUE_FEE));
539
540 checkMetrics(__LINE__, env, 3, 10, 6, 5, 256);
541
543
544 // Do some experiments with putting sequence-based transactions
545 // into the queue while there are ticket-based transactions
546 // already in the queue.
547
548 // Alice still has three ticket-based transactions in the queue.
549 // The fee is escalated so unless we pay a sufficient fee
550 // transactions will go straight to the queue.
551 std::uint32_t const nextSeq{env.seq(alice)};
552 env(noop(alice), seq(nextSeq + 1), ter(terPRE_SEQ));
553 env(noop(alice), seq(nextSeq - 1), ter(tefPAST_SEQ));
554 env(noop(alice), seq(nextSeq + 0), queued);
555
556 // Now that nextSeq is in the queue, we should be able to queue
557 // nextSeq + 1.
558 env(noop(alice), seq(nextSeq + 1), queued);
559
560 // Fill the queue with sequence-based transactions. When the
561 // ledger closes we should find the three ticket-based
562 // transactions gone from the queue (because they had the
563 // highest fee). Then the earliest of the sequence-based
564 // transactions should also be gone from the queue.
565 env(noop(alice), seq(nextSeq + 2), queued);
566 env(noop(alice), seq(nextSeq + 3), queued);
567 env(noop(alice), seq(nextSeq + 4), queued);
568 env(noop(alice), seq(nextSeq + 5), queued);
569 env(noop(alice), seq(nextSeq + 6), queued);
570 env(noop(alice), seq(nextSeq + 7), ter(telCAN_NOT_QUEUE_FULL));
571 checkMetrics(__LINE__, env, 10, 10, 6, 5, 257);
572
573 // Check which of the queued transactions got into the ledger by
574 // attempting to replace them.
575 // o Get tefNo_TICKET if the ticket has already been used.
576 // o Get tefPAST_SEQ if the sequence moved out of the queue.
577 // o Get telCAN_NOT_QUEUE_FEE if the transaction is still in
578 // the queue.
579 env.close();
580 env.require(owners(alice, 237), tickets(alice, 237));
581
582 // The four ticket-based transactions went out first, since
583 // they paid the highest fee.
584 env(noop(alice), ticket::use(tkt1 + 4), ter(tefNO_TICKET));
585 env(noop(alice), ticket::use(tkt1 + 5), ter(tefNO_TICKET));
586 env(noop(alice), ticket::use(tkt1 + 12), ter(tefNO_TICKET));
587 env(noop(alice), ticket::use(tkt1 + 13), ter(tefNO_TICKET));
588
589 // Three of the sequence-based transactions also moved out of
590 // the queue.
591 env(noop(alice), seq(nextSeq + 1), ter(tefPAST_SEQ));
592 env(noop(alice), seq(nextSeq + 2), ter(tefPAST_SEQ));
593 env(noop(alice), seq(nextSeq + 3), ter(tefPAST_SEQ));
594 env(noop(alice), seq(nextSeq + 4), ter(telCAN_NOT_QUEUE_FEE));
595 env(noop(alice), seq(nextSeq + 5), ter(telCAN_NOT_QUEUE_FEE));
596 env(noop(alice), seq(nextSeq + 6), ter(telCAN_NOT_QUEUE_FEE));
597 env(noop(alice), seq(nextSeq + 7), ter(telCAN_NOT_QUEUE_FEE));
598
599 checkMetrics(__LINE__, env, 4, 12, 7, 6, 256);
600 BEAST_EXPECT(env.seq(alice) == nextSeq + 4);
601
603
604 // We haven't yet shown that ticket-based transactions can be added
605 // to the queue in any order. We should do that...
606 std::uint32_t tkt250 = tkt1 + 249;
607 env(noop(alice), ticket::use(tkt250 - 0), fee(30), queued);
608 env(noop(alice), ticket::use(tkt1 + 14), fee(29), queued);
609 env(noop(alice), ticket::use(tkt250 - 1), fee(28), queued);
610 env(noop(alice), ticket::use(tkt1 + 15), fee(27), queued);
611 env(noop(alice), ticket::use(tkt250 - 2), fee(26), queued);
612 env(noop(alice), ticket::use(tkt1 + 16), fee(25), queued);
613 env(noop(alice),
614 ticket::use(tkt250 - 3),
615 fee(24),
617 env(noop(alice),
618 ticket::use(tkt1 + 17),
619 fee(23),
621 env(noop(alice),
622 ticket::use(tkt250 - 4),
623 fee(22),
625 env(noop(alice),
626 ticket::use(tkt1 + 18),
627 fee(21),
629
630 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
631
632 env.close();
633 env.require(owners(alice, 231), tickets(alice, 231));
634
635 // These three ticket-based transactions escaped the queue.
636 env(noop(alice), ticket::use(tkt1 + 14), ter(tefNO_TICKET));
637 env(noop(alice), ticket::use(tkt1 + 15), ter(tefNO_TICKET));
638 env(noop(alice), ticket::use(tkt1 + 16), ter(tefNO_TICKET));
639
640 // But these four ticket-based transactions are in the queue
641 // now; they moved into the TxQ from local transactions.
642 env(noop(alice), ticket::use(tkt250 - 3), ter(telCAN_NOT_QUEUE_FEE));
643 env(noop(alice), ticket::use(tkt1 + 17), ter(telCAN_NOT_QUEUE_FEE));
644 env(noop(alice), ticket::use(tkt250 - 4), ter(telCAN_NOT_QUEUE_FEE));
645 env(noop(alice), ticket::use(tkt1 + 18), ter(telCAN_NOT_QUEUE_FEE));
646
647 // These three ticket-based transactions also escaped the queue.
648 env(noop(alice), ticket::use(tkt250 - 2), ter(tefNO_TICKET));
649 env(noop(alice), ticket::use(tkt250 - 1), ter(tefNO_TICKET));
650 env(noop(alice), ticket::use(tkt250 - 0), ter(tefNO_TICKET));
651
652 // These sequence-based transactions escaped the queue.
653 env(noop(alice), seq(nextSeq + 4), ter(tefPAST_SEQ));
654 env(noop(alice), seq(nextSeq + 5), ter(tefPAST_SEQ));
655
656 // But these sequence-based transactions are still stuck in the queue.
657 env(noop(alice), seq(nextSeq + 6), ter(telCAN_NOT_QUEUE_FEE));
658 env(noop(alice), seq(nextSeq + 7), ter(telCAN_NOT_QUEUE_FEE));
659
660 BEAST_EXPECT(env.seq(alice) == nextSeq + 6);
661 checkMetrics(__LINE__, env, 6, 14, 8, 7, 256);
662
664
665 // Since we still have two ticket-based transactions in the queue
666 // let's try replacing them.
667
668 // 26 drops is less than 21 * 1.25
669 env(noop(alice),
670 ticket::use(tkt1 + 18),
671 fee(26),
673
674 // 27 drops is more than 21 * 1.25
675 env(noop(alice), ticket::use(tkt1 + 18), fee(27), queued);
676
677 // 27 drops is less than 22 * 1.25
678 env(noop(alice),
679 ticket::use(tkt250 - 4),
680 fee(27),
682
683 // 28 drops is more than 22 * 1.25
684 env(noop(alice), ticket::use(tkt250 - 4), fee(28), queued);
685
686 env.close();
687 env.require(owners(alice, 227), tickets(alice, 227));
688
689 // Verify that all remaining transactions made it out of the TxQ.
690 env(noop(alice), ticket::use(tkt1 + 18), ter(tefNO_TICKET));
691 env(noop(alice), ticket::use(tkt250 - 4), ter(tefNO_TICKET));
692 env(noop(alice), seq(nextSeq + 4), ter(tefPAST_SEQ));
693 env(noop(alice), seq(nextSeq + 5), ter(tefPAST_SEQ));
694 env(noop(alice), seq(nextSeq + 6), ter(tefPAST_SEQ));
695 env(noop(alice), seq(nextSeq + 7), ter(tefPAST_SEQ));
696
697 BEAST_EXPECT(env.seq(alice) == nextSeq + 8);
698 checkMetrics(__LINE__, env, 0, 16, 6, 8, 256);
699 }
700
701 void
703 {
704 using namespace jtx;
705 testcase("queue tec");
706
707 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}}));
708
709 auto alice = Account("alice");
710 auto gw = Account("gw");
711 auto USD = gw["USD"];
712
713 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
714
715 // Create accounts
716 env.fund(XRP(50000), noripple(alice, gw));
717 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 2, 256);
718 env.close();
719 checkMetrics(__LINE__, env, 0, 4, 0, 2, 256);
720
721 // Alice creates an unfunded offer while the ledger is not full
722 env(offer(alice, XRP(1000), USD(1000)), ter(tecUNFUNDED_OFFER));
723 checkMetrics(__LINE__, env, 0, 4, 1, 2, 256);
724
725 fillQueue(env, alice);
726 checkMetrics(__LINE__, env, 0, 4, 3, 2, 256);
727
728 // Alice creates an unfunded offer that goes in the queue
729 env(offer(alice, XRP(1000), USD(1000)), ter(terQUEUED));
730 checkMetrics(__LINE__, env, 1, 4, 3, 2, 256);
731
732 // The offer comes out of the queue
733 env.close();
734 checkMetrics(__LINE__, env, 0, 6, 1, 3, 256);
735 }
736
737 void
739 {
740 using namespace jtx;
741 using namespace std::chrono;
742 testcase("local tx retry");
743
744 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}}));
745
746 auto alice = Account("alice");
747 auto bob = Account("bob");
748 auto charlie = Account("charlie");
749
750 auto queued = ter(terQUEUED);
751
752 BEAST_EXPECT(env.current()->fees().base == 10);
753
754 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
755
756 // Create several accounts while the fee is cheap so they all apply.
757 env.fund(XRP(50000), noripple(alice, bob, charlie));
758 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 2, 256);
759
760 // Future transaction for Alice - fails
761 env(noop(alice),
762 openLedgerFee(env),
763 seq(env.seq(alice) + 1),
764 ter(terPRE_SEQ));
765 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 2, 256);
766
767 // Current transaction for Alice: held
768 env(noop(alice), queued);
769 checkMetrics(__LINE__, env, 1, std::nullopt, 3, 2, 256);
770
771 // Alice - sequence is too far ahead, so won't queue.
772 env(noop(alice), seq(env.seq(alice) + 2), ter(telCAN_NOT_QUEUE));
773 checkMetrics(__LINE__, env, 1, std::nullopt, 3, 2, 256);
774
775 // Bob with really high fee - applies
776 env(noop(bob), openLedgerFee(env));
777 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 2, 256);
778
779 // Daria with low fee: hold
780 env(noop(charlie), fee(1000), queued);
781 checkMetrics(__LINE__, env, 2, std::nullopt, 4, 2, 256);
782
783 // Alice with normal fee: hold
784 env(noop(alice), seq(env.seq(alice) + 1), queued);
785 checkMetrics(__LINE__, env, 3, std::nullopt, 4, 2, 256);
786
787 env.close();
788 // Verify that the held transactions got applied
789 // Alice's bad transaction applied from the
790 // Local Txs.
791 checkMetrics(__LINE__, env, 0, 8, 4, 4, 256);
792 }
793
794 void
796 {
797 using namespace jtx;
798 using namespace std::chrono;
799 testcase("last ledger sequence");
800
801 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}}));
802
803 auto alice = Account("alice");
804 auto bob = Account("bob");
805 auto charlie = Account("charlie");
806 auto daria = Account("daria");
807 auto edgar = Account("edgar");
808 auto felicia = Account("felicia");
809
810 auto queued = ter(terQUEUED);
811
812 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
813
814 // Fund across several ledgers so the TxQ metrics stay restricted.
815 env.fund(XRP(1000), noripple(alice, bob));
816 env.close(env.now() + 5s, 10000ms);
817 env.fund(XRP(1000), noripple(charlie, daria));
818 env.close(env.now() + 5s, 10000ms);
819 env.fund(XRP(1000), noripple(edgar, felicia));
820 env.close(env.now() + 5s, 10000ms);
821
822 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
823 env(noop(bob));
824 env(noop(charlie));
825 env(noop(daria));
826 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 2, 256);
827
828 BEAST_EXPECT(env.current()->info().seq == 6);
829 // Fail to queue an item with a low LastLedgerSeq
830 env(noop(alice),
831 json(R"({"LastLedgerSequence":7})"),
833 // Queue an item with a sufficient LastLedgerSeq.
834 env(noop(alice), json(R"({"LastLedgerSequence":8})"), queued);
835 // Queue items with higher fees to force the previous
836 // txn to wait.
837 env(noop(bob), fee(7000), queued);
838 env(noop(charlie), fee(7000), queued);
839 env(noop(daria), fee(7000), queued);
840 env(noop(edgar), fee(7000), queued);
841 checkMetrics(__LINE__, env, 5, std::nullopt, 3, 2, 256);
842 {
843 auto& txQ = env.app().getTxQ();
844 auto aliceStat = txQ.getAccountTxs(alice.id());
845 BEAST_EXPECT(aliceStat.size() == 1);
846 BEAST_EXPECT(aliceStat.begin()->feeLevel == FeeLevel64{256});
848 aliceStat.begin()->lastValid &&
849 *aliceStat.begin()->lastValid == 8);
850 BEAST_EXPECT(!aliceStat.begin()->consequences.isBlocker());
851
852 auto bobStat = txQ.getAccountTxs(bob.id());
853 BEAST_EXPECT(bobStat.size() == 1);
855 bobStat.begin()->feeLevel == FeeLevel64{7000 * 256 / 10});
856 BEAST_EXPECT(!bobStat.begin()->lastValid);
857 BEAST_EXPECT(!bobStat.begin()->consequences.isBlocker());
858
859 auto noStat = txQ.getAccountTxs(Account::master.id());
860 BEAST_EXPECT(noStat.empty());
861 }
862
863 env.close();
864 checkMetrics(__LINE__, env, 1, 6, 4, 3, 256);
865
866 // Keep alice's transaction waiting.
867 env(noop(bob), fee(7000), queued);
868 env(noop(charlie), fee(7000), queued);
869 env(noop(daria), fee(7000), queued);
870 env(noop(edgar), fee(7000), queued);
871 env(noop(felicia), fee(6999), queued);
872 checkMetrics(__LINE__, env, 6, 6, 4, 3, 257);
873
874 env.close();
875 // alice's transaction is still hanging around
876 checkMetrics(__LINE__, env, 1, 8, 5, 4, 256, 700 * 256);
877 BEAST_EXPECT(env.seq(alice) == 3);
878
879 // Keep alice's transaction waiting.
880 env(noop(bob), fee(8000), queued);
881 env(noop(charlie), fee(8000), queued);
882 env(noop(daria), fee(8000), queued);
883 env(noop(daria), fee(8000), seq(env.seq(daria) + 1), queued);
884 env(noop(edgar), fee(8000), queued);
885 env(noop(felicia), fee(7999), queued);
886 env(noop(felicia), fee(7999), seq(env.seq(felicia) + 1), queued);
887 checkMetrics(__LINE__, env, 8, 8, 5, 4, 257, 700 * 256);
888
889 env.close();
890 // alice's transaction expired without getting
891 // into the ledger, so her transaction is gone,
892 // though one of felicia's is still in the queue.
893 checkMetrics(__LINE__, env, 1, 10, 6, 5, 256, 700 * 256);
894 BEAST_EXPECT(env.seq(alice) == 3);
895 BEAST_EXPECT(env.seq(felicia) == 7);
896
897 env.close();
898 // And now the queue is empty
899 checkMetrics(__LINE__, env, 0, 12, 1, 6, 256, 800 * 256);
900 BEAST_EXPECT(env.seq(alice) == 3);
901 BEAST_EXPECT(env.seq(felicia) == 8);
902 }
903
904 void
906 {
907 using namespace jtx;
908 using namespace std::chrono;
909 testcase("zero transaction fee");
910
911 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}}));
912
913 auto alice = Account("alice");
914 auto bob = Account("bob");
915 auto carol = Account("carol");
916
917 auto queued = ter(terQUEUED);
918
919 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
920
921 // Fund across several ledgers so the TxQ metrics stay restricted.
922 env.fund(XRP(1000), noripple(alice, bob));
923 env.close(env.now() + 5s, 10000ms);
924 env.fund(XRP(1000), noripple(carol));
925 env.close(env.now() + 5s, 10000ms);
926
927 // Fill the ledger
928 env(noop(alice));
929 env(noop(alice));
930 env(noop(alice));
931 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 2, 256);
932
933 env(noop(bob), queued);
934 checkMetrics(__LINE__, env, 1, std::nullopt, 3, 2, 256);
935
936 // Since Alice's queue is empty this blocker can go into her queue.
937 env(regkey(alice, bob), fee(0), queued);
938 checkMetrics(__LINE__, env, 2, std::nullopt, 3, 2, 256);
939
940 // Close out this ledger so we can get a maxsize
941 env.close();
942 checkMetrics(__LINE__, env, 0, 6, 2, 3, 256);
943
944 fillQueue(env, alice);
945 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
946
947 auto feeAlice = 30;
948 auto seqAlice = env.seq(alice);
949 for (int i = 0; i < 4; ++i)
950 {
951 env(noop(alice), fee(feeAlice), seq(seqAlice), queued);
952 feeAlice = (feeAlice + 1) * 125 / 100;
953 ++seqAlice;
954 }
955 checkMetrics(__LINE__, env, 4, 6, 4, 3, 256);
956
957 // Bob adds a zero fee blocker to his queue.
958 auto const seqBob = env.seq(bob);
959 env(regkey(bob, alice), fee(0), queued);
960 checkMetrics(__LINE__, env, 5, 6, 4, 3, 256);
961
962 // Carol fills the queue.
963 auto feeCarol = feeAlice;
964 auto seqCarol = env.seq(carol);
965 for (int i = 0; i < 4; ++i)
966 {
967 env(noop(carol), fee(feeCarol), seq(seqCarol), queued);
968 feeCarol = (feeCarol + 1) * 125 / 100;
969 ++seqCarol;
970 }
971 checkMetrics(__LINE__, env, 6, 6, 4, 3, 3 * 256 + 1);
972
973 // Carol submits high enough to beat Bob's average fee which kicks
974 // out Bob's queued transaction. However Bob's transaction stays
975 // in the localTx queue, so it will return to the TxQ next time
976 // around.
977 env(noop(carol), fee(feeCarol), seq(seqCarol), ter(terQUEUED));
978
979 env.close();
980 // Some of Alice's transactions stay in the queue. Bob's
981 // transaction returns to the TxQ.
982 checkMetrics(__LINE__, env, 5, 8, 5, 4, 256);
983 BEAST_EXPECT(env.seq(alice) == seqAlice - 4);
984 BEAST_EXPECT(env.seq(bob) == seqBob);
985 BEAST_EXPECT(env.seq(carol) == seqCarol + 1);
986
987 env.close();
988 // The remaining queued transactions flush through to the ledger.
989 checkMetrics(__LINE__, env, 0, 10, 5, 5, 256);
990 BEAST_EXPECT(env.seq(alice) == seqAlice);
991 BEAST_EXPECT(env.seq(bob) == seqBob + 1);
992 BEAST_EXPECT(env.seq(carol) == seqCarol + 1);
993
994 env.close();
995 checkMetrics(__LINE__, env, 0, 10, 0, 5, 256);
996 BEAST_EXPECT(env.seq(alice) == seqAlice);
997 BEAST_EXPECT(env.seq(bob) == seqBob + 1);
998 BEAST_EXPECT(env.seq(carol) == seqCarol + 1);
999 }
1000
1001 void
1003 {
1004 using namespace jtx;
1005
1006 Env env(*this, makeConfig());
1007 testcase("fail in preclaim");
1008
1009 auto alice = Account("alice");
1010 auto bob = Account("bob");
1011
1012 env.fund(XRP(1000), noripple(alice));
1013
1014 // These types of checks are tested elsewhere, but
1015 // this verifies that TxQ handles the failures as
1016 // expected.
1017
1018 // Fail in preflight
1019 env(pay(alice, bob, XRP(-1000)), ter(temBAD_AMOUNT));
1020
1021 // Fail in preflight
1022 env(pay(alice, alice, XRP(100)), ter(temREDUNDANT));
1023
1024 // Fail in preclaim
1025 env(noop(alice), fee(XRP(100000)), ter(terINSUF_FEE_B));
1026 }
1027
1028 void
1030 {
1031 using namespace jtx;
1032 testcase("queued tx fails");
1033
1034 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "2"}}));
1035
1036 auto alice = Account("alice");
1037 auto bob = Account("bob");
1038
1039 auto queued = ter(terQUEUED);
1040
1041 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
1042
1043 env.fund(XRP(1000), noripple(alice, bob));
1044
1045 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 2, 256);
1046
1047 // Fill the ledger
1048 env(noop(alice));
1049 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 2, 256);
1050
1051 // Put a transaction in the queue
1052 env(noop(alice), queued);
1053 checkMetrics(__LINE__, env, 1, std::nullopt, 3, 2, 256);
1054
1055 // Now cheat, and bypass the queue.
1056 {
1057 auto const& jt = env.jt(noop(alice));
1058 BEAST_EXPECT(jt.stx);
1059
1060 Env::ParsedResult parsed;
1061
1062 env.app().openLedger().modify(
1063 [&](OpenView& view, beast::Journal j) {
1064 auto const result = ripple::apply(
1065 env.app(), view, *jt.stx, tapNONE, env.journal);
1066 parsed.ter = result.ter;
1067 return result.applied;
1068 });
1069 env.postconditions(jt, parsed);
1070 }
1071 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 2, 256);
1072
1073 env.close();
1074 // Alice's queued transaction failed in TxQ::accept
1075 // with tefPAST_SEQ
1076 checkMetrics(__LINE__, env, 0, 8, 0, 4, 256);
1077 }
1078
1079 void
1081 {
1082 using namespace jtx;
1083 testcase("multi tx per account");
1084
1085 Env env(
1086 *this,
1087 makeConfig(
1088 {{"minimum_txn_in_ledger_standalone", "3"}},
1089 {{"account_reserve", "200"}, {"owner_reserve", "50"}}));
1090
1091 auto alice = Account("alice");
1092 auto bob = Account("bob");
1093 auto charlie = Account("charlie");
1094 auto daria = Account("daria");
1095
1096 auto queued = ter(terQUEUED);
1097
1098 BEAST_EXPECT(env.current()->fees().base == 10);
1099
1100 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
1101
1102 // ledgers in queue is 2 because of makeConfig
1103 auto const initQueueMax = initFee(env, 3, 2, 10, 200, 50);
1104
1105 // Create several accounts while the fee is cheap so they all apply.
1106 env.fund(drops(2000), noripple(alice));
1107 env.fund(XRP(500000), noripple(bob, charlie, daria));
1108 checkMetrics(__LINE__, env, 0, initQueueMax, 4, 3, 256);
1109
1110 // Alice - price starts exploding: held
1111 env(noop(alice), fee(11), queued);
1112 checkMetrics(__LINE__, env, 1, initQueueMax, 4, 3, 256);
1113
1114 auto aliceSeq = env.seq(alice);
1115 auto bobSeq = env.seq(bob);
1116 auto charlieSeq = env.seq(charlie);
1117
1118 // Alice - try to queue a second transaction, but leave a gap
1119 env(noop(alice), seq(aliceSeq + 2), fee(100), ter(telCAN_NOT_QUEUE));
1120 checkMetrics(__LINE__, env, 1, initQueueMax, 4, 3, 256);
1121
1122 // Alice - queue a second transaction. Yay!
1123 env(noop(alice), seq(aliceSeq + 1), fee(13), queued);
1124 checkMetrics(__LINE__, env, 2, initQueueMax, 4, 3, 256);
1125
1126 // Alice - queue a third transaction. Yay.
1127 env(noop(alice), seq(aliceSeq + 2), fee(17), queued);
1128 checkMetrics(__LINE__, env, 3, initQueueMax, 4, 3, 256);
1129
1130 // Bob - queue a transaction
1131 env(noop(bob), queued);
1132 checkMetrics(__LINE__, env, 4, initQueueMax, 4, 3, 256);
1133
1134 // Bob - queue a second transaction
1135 env(noop(bob), seq(bobSeq + 1), fee(50), queued);
1136 checkMetrics(__LINE__, env, 5, initQueueMax, 4, 3, 256);
1137
1138 // Charlie - queue a transaction, with a higher fee
1139 // than default
1140 env(noop(charlie), fee(15), queued);
1141 checkMetrics(__LINE__, env, 6, initQueueMax, 4, 3, 256);
1142
1143 BEAST_EXPECT(env.seq(alice) == aliceSeq);
1144 BEAST_EXPECT(env.seq(bob) == bobSeq);
1145 BEAST_EXPECT(env.seq(charlie) == charlieSeq);
1146
1147 env.close();
1148 // Verify that all of but one of the queued transactions
1149 // got applied.
1150 checkMetrics(__LINE__, env, 1, 8, 5, 4, 256);
1151
1152 // Verify that the stuck transaction is Bob's second.
1153 // Even though it had a higher fee than Alice's and
1154 // Charlie's, it didn't get attempted until the fee escalated.
1155 BEAST_EXPECT(env.seq(alice) == aliceSeq + 3);
1156 BEAST_EXPECT(env.seq(bob) == bobSeq + 1);
1157 BEAST_EXPECT(env.seq(charlie) == charlieSeq + 1);
1158
1159 // Alice - fill up the queue
1160 std::int64_t aliceFee = 27;
1161 aliceSeq = env.seq(alice);
1162 auto lastLedgerSeq = env.current()->info().seq + 2;
1163 for (auto i = 0; i < 7; i++)
1164 {
1165 env(noop(alice),
1166 seq(aliceSeq),
1167 json(jss::LastLedgerSequence, lastLedgerSeq + i),
1168 fee(--aliceFee),
1169 queued);
1170 ++aliceSeq;
1171 }
1172 checkMetrics(__LINE__, env, 8, 8, 5, 4, 513);
1173 {
1174 auto& txQ = env.app().getTxQ();
1175 auto aliceStat = txQ.getAccountTxs(alice.id());
1176 aliceFee = 27;
1177 auto const& baseFee = env.current()->fees().base;
1178 auto seq = env.seq(alice);
1179 BEAST_EXPECT(aliceStat.size() == 7);
1180 for (auto const& tx : aliceStat)
1181 {
1182 BEAST_EXPECT(tx.seqProxy.isSeq() && tx.seqProxy.value() == seq);
1184 tx.feeLevel == toFeeLevel(XRPAmount(--aliceFee), baseFee));
1185 BEAST_EXPECT(tx.lastValid);
1187 (tx.consequences.fee() == drops(aliceFee) &&
1188 tx.consequences.potentialSpend() == drops(0) &&
1189 !tx.consequences.isBlocker()) ||
1190 tx.seqProxy.value() == env.seq(alice) + 6);
1191 ++seq;
1192 }
1193 }
1194
1195 // Alice attempts to add another item to the queue,
1196 // but you can't force your own earlier txn off the
1197 // queue.
1198 env(noop(alice),
1199 seq(aliceSeq),
1200 json(jss::LastLedgerSequence, lastLedgerSeq + 7),
1201 fee(aliceFee),
1203 checkMetrics(__LINE__, env, 8, 8, 5, 4, 513);
1204
1205 // Charlie - try to add another item to the queue,
1206 // which fails because fee is lower than Alice's
1207 // queued average.
1208 env(noop(charlie), fee(19), ter(telCAN_NOT_QUEUE_FULL));
1209 checkMetrics(__LINE__, env, 8, 8, 5, 4, 513);
1210
1211 // Charlie - add another item to the queue, which
1212 // causes Alice's last txn to drop
1213 env(noop(charlie), fee(30), queued);
1214 checkMetrics(__LINE__, env, 8, 8, 5, 4, 538);
1215
1216 // Alice - now attempt to add one more to the queue,
1217 // which fails because the last tx was dropped, so
1218 // there is no complete chain.
1219 env(noop(alice), seq(aliceSeq), fee(aliceFee), ter(telCAN_NOT_QUEUE));
1220 checkMetrics(__LINE__, env, 8, 8, 5, 4, 538);
1221
1222 // Alice wants this tx more than the dropped tx,
1223 // so resubmits with higher fee, but the queue
1224 // is full, and her account is the cheapest.
1225 env(noop(alice),
1226 seq(aliceSeq - 1),
1227 fee(aliceFee),
1229 checkMetrics(__LINE__, env, 8, 8, 5, 4, 538);
1230
1231 // Try to replace a middle item in the queue
1232 // without enough fee.
1233 aliceSeq = env.seq(alice) + 2;
1234 aliceFee = 29;
1235 env(noop(alice),
1236 seq(aliceSeq),
1237 fee(aliceFee),
1239 checkMetrics(__LINE__, env, 8, 8, 5, 4, 538);
1240
1241 // Replace a middle item from the queue successfully
1242 ++aliceFee;
1243 env(noop(alice), seq(aliceSeq), fee(aliceFee), queued);
1244 checkMetrics(__LINE__, env, 8, 8, 5, 4, 538);
1245
1246 env.close();
1247 // Alice's transactions processed, along with
1248 // Charlie's, and the lost one is replayed and
1249 // added back to the queue.
1250 checkMetrics(__LINE__, env, 4, 10, 6, 5, 256);
1251
1252 aliceSeq = env.seq(alice) + 1;
1253
1254 // Try to replace that item with a transaction that will
1255 // bankrupt Alice. Fails, because an account can't have
1256 // more than the minimum reserve in flight before the
1257 // last queued transaction
1258 aliceFee =
1259 env.le(alice)->getFieldAmount(sfBalance).xrp().drops() - (62);
1260 env(noop(alice),
1261 seq(aliceSeq),
1262 fee(aliceFee),
1264 checkMetrics(__LINE__, env, 4, 10, 6, 5, 256);
1265
1266 // Try to spend more than Alice can afford with all the other txs.
1267 aliceSeq += 2;
1268 env(noop(alice), seq(aliceSeq), fee(aliceFee), ter(terINSUF_FEE_B));
1269 checkMetrics(__LINE__, env, 4, 10, 6, 5, 256);
1270
1271 // Replace the last queued item with a transaction that will
1272 // bankrupt Alice
1273 --aliceFee;
1274 env(noop(alice), seq(aliceSeq), fee(aliceFee), queued);
1275 checkMetrics(__LINE__, env, 4, 10, 6, 5, 256);
1276
1277 // Alice - Attempt to queue a last transaction, but it
1278 // fails because the fee in flight is too high, before
1279 // the fee is checked against the balance
1280 aliceFee /= 5;
1281 ++aliceSeq;
1282 env(noop(alice),
1283 seq(aliceSeq),
1284 fee(aliceFee),
1286 checkMetrics(__LINE__, env, 4, 10, 6, 5, 256);
1287
1288 env.close();
1289 // All of Alice's transactions applied.
1290 checkMetrics(__LINE__, env, 0, 12, 4, 6, 256);
1291
1292 env.close();
1293 checkMetrics(__LINE__, env, 0, 12, 0, 6, 256);
1294
1295 // Alice is broke
1296 env.require(balance(alice, XRP(0)));
1297 env(noop(alice), ter(terINSUF_FEE_B));
1298
1299 // Bob tries to queue up more than the single
1300 // account limit (10) txs.
1301 fillQueue(env, bob);
1302 bobSeq = env.seq(bob);
1303 checkMetrics(__LINE__, env, 0, 12, 7, 6, 256);
1304 for (int i = 0; i < 10; ++i)
1305 env(noop(bob), seq(bobSeq + i), queued);
1306 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
1307 // Bob hit the single account limit
1308 env(noop(bob), seq(bobSeq + 10), ter(telCAN_NOT_QUEUE_FULL));
1309 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
1310 // Bob can replace one of the earlier txs regardless
1311 // of the limit
1312 env(noop(bob), seq(bobSeq + 5), fee(20), queued);
1313 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
1314
1315 // Try to replace a middle item in the queue
1316 // with enough fee to bankrupt bob and make the
1317 // later transactions unable to pay their fees
1318 std::int64_t bobFee =
1319 env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10 - 1);
1320 env(noop(bob),
1321 seq(bobSeq + 5),
1322 fee(bobFee),
1324 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
1325
1326 // Attempt to replace a middle item in the queue with enough fee
1327 // to bankrupt bob, and also to use fee averaging to clear out the
1328 // first six transactions.
1329 //
1330 // The attempt fails because the sum of bob's fees now exceeds the
1331 // (artificially lowered to 200 drops) account reserve.
1332 bobFee =
1333 env.le(bob)->getFieldAmount(sfBalance).xrp().drops() - (9 * 10);
1334 env(noop(bob),
1335 seq(bobSeq + 5),
1336 fee(bobFee),
1338 checkMetrics(__LINE__, env, 10, 12, 7, 6, 256);
1339
1340 // Close the ledger and verify that the queued transactions succeed
1341 // and bob has the right ending balance.
1342 env.close();
1343 checkMetrics(__LINE__, env, 3, 14, 8, 7, 256);
1344 env.close();
1345 checkMetrics(__LINE__, env, 0, 16, 3, 8, 256);
1346 env.require(balance(bob, drops(499'999'999'750)));
1347 }
1348
1349 void
1351 {
1352 using namespace jtx;
1353 using namespace std::chrono;
1354 testcase("tie breaking");
1355
1356 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "4"}}));
1357
1358 auto alice = Account("alice");
1359 auto bob = Account("bob");
1360 auto charlie = Account("charlie");
1361 auto daria = Account("daria");
1362 auto elmo = Account("elmo");
1363 auto fred = Account("fred");
1364 auto gwen = Account("gwen");
1365 auto hank = Account("hank");
1366
1367 auto queued = ter(terQUEUED);
1368
1369 BEAST_EXPECT(env.current()->fees().base == 10);
1370
1371 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 4, 256);
1372
1373 // Create several accounts while the fee is cheap so they all apply.
1374 env.fund(XRP(50000), noripple(alice, bob, charlie, daria));
1375 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 4, 256);
1376
1377 env.close();
1378 checkMetrics(__LINE__, env, 0, 8, 0, 4, 256);
1379
1380 env.fund(XRP(50000), noripple(elmo, fred, gwen, hank));
1381 checkMetrics(__LINE__, env, 0, 8, 4, 4, 256);
1382
1383 env.close();
1384 checkMetrics(__LINE__, env, 0, 8, 0, 4, 256);
1385
1387
1388 // Stuff the ledger and queue so we can verify that
1389 // stuff gets kicked out.
1390 env(noop(gwen));
1391 env(noop(hank));
1392 env(noop(gwen));
1393 env(noop(fred));
1394 env(noop(elmo));
1395 checkMetrics(__LINE__, env, 0, 8, 5, 4, 256);
1396
1397 auto aliceSeq = env.seq(alice);
1398 auto bobSeq = env.seq(bob);
1399 auto charlieSeq = env.seq(charlie);
1400 auto dariaSeq = env.seq(daria);
1401 auto elmoSeq = env.seq(elmo);
1402 auto fredSeq = env.seq(fred);
1403 auto gwenSeq = env.seq(gwen);
1404 auto hankSeq = env.seq(hank);
1405
1406 // This time, use identical fees.
1407
1408 // All of these get into the queue, but one gets dropped when the
1409 // higher fee one is added later. Which one depends on ordering.
1410 env(noop(alice), fee(15), queued);
1411 env(noop(bob), fee(15), queued);
1412 env(noop(charlie), fee(15), queued);
1413 env(noop(daria), fee(15), queued);
1414 env(noop(elmo), fee(15), queued);
1415 env(noop(fred), fee(15), queued);
1416 env(noop(gwen), fee(15), queued);
1417 env(noop(hank), fee(15), queued);
1418
1419 // Queue is full now. Minimum fee now reflects the
1420 // lowest fee in the queue.
1421 checkMetrics(__LINE__, env, 8, 8, 5, 4, 385);
1422
1423 // Try to add another transaction with the default (low) fee,
1424 // it should fail because it can't replace the one already
1425 // there.
1426 env(noop(charlie), ter(telCAN_NOT_QUEUE_FEE));
1427
1428 // Add another transaction, with a higher fee,
1429 // Not high enough to get into the ledger, but high
1430 // enough to get into the queue (and kick somebody out)
1431 env(noop(charlie), fee(100), seq(charlieSeq + 1), queued);
1432
1433 // Queue is still full.
1434 checkMetrics(__LINE__, env, 8, 8, 5, 4, 385);
1435
1436 // Six txs are processed out of the queue into the ledger,
1437 // leaving two txs. The dropped tx is retried from localTxs, and
1438 // put back into the queue.
1439 env.close();
1440 checkMetrics(__LINE__, env, 3, 10, 6, 5, 256);
1441
1442 // This next test should remain unchanged regardless of
1443 // transaction ordering
1445 aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq +
1446 gwenSeq + hankSeq + 6 ==
1447 env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) +
1448 env.seq(elmo) + env.seq(fred) + env.seq(gwen) + env.seq(hank));
1449 // These tests may change if TxQ ordering is changed
1450 using namespace std::string_literals;
1451 BEAST_EXPECTS(
1452 aliceSeq == env.seq(alice),
1453 "alice: "s + std::to_string(aliceSeq) + ", " +
1454 std::to_string(env.seq(alice)));
1455 BEAST_EXPECTS(
1456 bobSeq + 1 == env.seq(bob),
1457 "bob: "s + std::to_string(bobSeq) + ", " +
1458 std::to_string(env.seq(bob)));
1459 BEAST_EXPECTS(
1460 charlieSeq + 2 == env.seq(charlie),
1461 "charlie: "s + std::to_string(charlieSeq) + ", " +
1462 std::to_string(env.seq(charlie)));
1463 BEAST_EXPECTS(
1464 dariaSeq + 1 == env.seq(daria),
1465 "daria: "s + std::to_string(dariaSeq) + ", " +
1466 std::to_string(env.seq(daria)));
1467 BEAST_EXPECTS(
1468 elmoSeq + 1 == env.seq(elmo),
1469 "elmo: "s + std::to_string(elmoSeq) + ", " +
1470 std::to_string(env.seq(elmo)));
1471 BEAST_EXPECTS(
1472 fredSeq == env.seq(fred),
1473 "fred: "s + std::to_string(fredSeq) + ", " +
1474 std::to_string(env.seq(fred)));
1475 BEAST_EXPECTS(
1476 gwenSeq == env.seq(gwen),
1477 "gwen: "s + std::to_string(gwenSeq) + ", " +
1478 std::to_string(env.seq(gwen)));
1479 BEAST_EXPECTS(
1480 hankSeq + 1 == env.seq(hank),
1481 "hank: "s + std::to_string(hankSeq) + ", " +
1482 std::to_string(env.seq(hank)));
1483
1484 // Which sequences get incremented may change if TxQ ordering is
1485 // changed
1486 //++aliceSeq;
1487 ++bobSeq;
1488 ++(++charlieSeq);
1489 ++dariaSeq;
1490 ++elmoSeq;
1491 // ++fredSeq;
1492 //++gwenSeq;
1493 ++hankSeq;
1494
1495 auto getTxsQueued = [&]() {
1496 auto const txs = env.app().getTxQ().getTxs();
1498 for (auto const& tx : txs)
1499 {
1500 ++result[tx.txn->at(sfAccount)];
1501 }
1502 return result;
1503 };
1504 auto qTxCount1 = getTxsQueued();
1505 BEAST_EXPECT(qTxCount1.size() <= 3);
1506
1507 // Fill up the queue again
1508 env(noop(alice),
1509 seq(aliceSeq + qTxCount1[alice.id()]++),
1510 fee(15),
1511 queued);
1512 env(noop(bob), seq(bobSeq + qTxCount1[bob.id()]++), fee(15), queued);
1513 env(noop(charlie),
1514 seq(charlieSeq + qTxCount1[charlie.id()]++),
1515 fee(15),
1516 queued);
1517 env(noop(daria),
1518 seq(dariaSeq + qTxCount1[daria.id()]++),
1519 fee(15),
1520 queued);
1521 env(noop(elmo), seq(elmoSeq + qTxCount1[elmo.id()]++), fee(15), queued);
1522 env(noop(fred), seq(fredSeq + qTxCount1[fred.id()]++), fee(15), queued);
1523 env(noop(gwen), seq(gwenSeq + qTxCount1[gwen.id()]++), fee(15), queued);
1524 checkMetrics(__LINE__, env, 10, 10, 6, 5, 385);
1525
1526 // Add another transaction, with a higher fee,
1527 // Not high enough to get into the ledger, but high
1528 // enough to get into the queue (and kick somebody out)
1529 env(noop(alice),
1530 fee(100),
1531 seq(aliceSeq + qTxCount1[alice.id()]++),
1532 queued);
1533
1534 checkMetrics(__LINE__, env, 10, 10, 6, 5, 385);
1535
1536 // Seven txs are processed out of the queue, leaving 3. One
1537 // dropped tx is retried from localTxs, and put back into the
1538 // queue.
1539 env.close();
1540 checkMetrics(__LINE__, env, 4, 12, 7, 6, 256);
1541
1542 // Refresh the queue counts
1543 auto qTxCount2 = getTxsQueued();
1544 BEAST_EXPECT(qTxCount2.size() <= 4);
1545
1546 // This next test should remain unchanged regardless of
1547 // transaction ordering
1549 aliceSeq + bobSeq + charlieSeq + dariaSeq + elmoSeq + fredSeq +
1550 gwenSeq + hankSeq + 7 ==
1551 env.seq(alice) + env.seq(bob) + env.seq(charlie) + env.seq(daria) +
1552 env.seq(elmo) + env.seq(fred) + env.seq(gwen) + env.seq(hank));
1553 // These tests may change if TxQ ordering is changed
1554 BEAST_EXPECTS(
1555 aliceSeq + qTxCount1[alice.id()] - qTxCount2[alice.id()] ==
1556 env.seq(alice),
1557 "alice: "s + std::to_string(aliceSeq) + ", " +
1558 std::to_string(env.seq(alice)));
1559 BEAST_EXPECTS(
1560 bobSeq + qTxCount1[bob.id()] - qTxCount2[bob.id()] == env.seq(bob),
1561 "bob: "s + std::to_string(bobSeq) + ", " +
1562 std::to_string(env.seq(bob)));
1563 BEAST_EXPECTS(
1564 charlieSeq + qTxCount1[charlie.id()] - qTxCount2[charlie.id()] ==
1565 env.seq(charlie),
1566 "charlie: "s + std::to_string(charlieSeq) + ", " +
1567 std::to_string(env.seq(charlie)));
1568 BEAST_EXPECTS(
1569 dariaSeq + qTxCount1[daria.id()] - qTxCount2[daria.id()] ==
1570 env.seq(daria),
1571 "daria: "s + std::to_string(dariaSeq) + ", " +
1572 std::to_string(env.seq(daria)));
1573 BEAST_EXPECTS(
1574 elmoSeq + qTxCount1[elmo.id()] - qTxCount2[elmo.id()] ==
1575 env.seq(elmo),
1576 "elmo: "s + std::to_string(elmoSeq) + ", " +
1577 std::to_string(env.seq(elmo)));
1578 BEAST_EXPECTS(
1579 fredSeq + qTxCount1[fred.id()] - qTxCount2[fred.id()] ==
1580 env.seq(fred),
1581 "fred: "s + std::to_string(fredSeq) + ", " +
1582 std::to_string(env.seq(fred)));
1583 BEAST_EXPECTS(
1584 gwenSeq + qTxCount1[gwen.id()] - qTxCount2[gwen.id()] ==
1585 env.seq(gwen),
1586 "gwen: "s + std::to_string(gwenSeq) + ", " +
1587 std::to_string(env.seq(gwen)));
1588 BEAST_EXPECTS(
1589 hankSeq + qTxCount1[hank.id()] - qTxCount2[hank.id()] ==
1590 env.seq(hank),
1591 "hank: "s + std::to_string(hankSeq) + ", " +
1592 std::to_string(env.seq(hank)));
1593 }
1594
1595 void
1597 {
1598 using namespace jtx;
1599 testcase("acct tx id");
1600
1601 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "1"}}));
1602
1603 auto alice = Account("alice");
1604
1605 BEAST_EXPECT(env.current()->fees().base == 10);
1606
1607 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 1, 256);
1608
1609 env.fund(XRP(50000), noripple(alice));
1610 checkMetrics(__LINE__, env, 0, std::nullopt, 1, 1, 256);
1611
1612 env(fset(alice, asfAccountTxnID));
1613 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 1, 256);
1614
1615 // Immediately after the fset, the sfAccountTxnID field
1616 // is still uninitialized, so preflight succeeds here,
1617 // and this txn fails because it can't be stored in the queue.
1618 env(noop(alice),
1619 json(R"({"AccountTxnID": "0"})"),
1621
1622 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 1, 256);
1623 env.close();
1624 // The failed transaction is retried from LocalTx
1625 // and succeeds.
1626 checkMetrics(__LINE__, env, 0, 4, 1, 2, 256);
1627
1628 env(noop(alice));
1629 checkMetrics(__LINE__, env, 0, 4, 2, 2, 256);
1630
1631 env(noop(alice), json(R"({"AccountTxnID": "0"})"), ter(tefWRONG_PRIOR));
1632 }
1633
1634 void
1636 {
1637 using namespace jtx;
1638 using namespace std::string_literals;
1639 testcase("maximum tx");
1640
1641 {
1642 Env env(
1643 *this,
1644 makeConfig(
1645 {{"minimum_txn_in_ledger_standalone", "2"},
1646 {"minimum_txn_in_ledger", "5"},
1647 {"target_txn_in_ledger", "4"},
1648 {"maximum_txn_in_ledger", "5"}}));
1649
1650 auto alice = Account("alice");
1651
1652 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 2, 256);
1653
1654 env.fund(XRP(50000), noripple(alice));
1655 checkMetrics(__LINE__, env, 0, std::nullopt, 1, 2, 256);
1656
1657 for (int i = 0; i < 10; ++i)
1658 env(noop(alice), openLedgerFee(env));
1659
1660 checkMetrics(__LINE__, env, 0, std::nullopt, 11, 2, 256);
1661
1662 env.close();
1663 // If not for the maximum, the per ledger would be 11.
1664 checkMetrics(__LINE__, env, 0, 10, 0, 5, 256, 800025);
1665 }
1666
1667 try
1668 {
1669 Env env(
1670 *this,
1671 makeConfig(
1672 {{"minimum_txn_in_ledger", "200"},
1673 {"minimum_txn_in_ledger_standalone", "200"},
1674 {"target_txn_in_ledger", "4"},
1675 {"maximum_txn_in_ledger", "5"}}));
1676 // should throw
1677 fail();
1678 }
1679 catch (std::runtime_error const& e)
1680 {
1682 e.what() ==
1683 "The minimum number of low-fee transactions allowed "
1684 "per ledger (minimum_txn_in_ledger) exceeds "
1685 "the maximum number of low-fee transactions allowed per "
1686 "ledger (maximum_txn_in_ledger)."s);
1687 }
1688 try
1689 {
1690 Env env(
1691 *this,
1692 makeConfig(
1693 {{"minimum_txn_in_ledger", "200"},
1694 {"minimum_txn_in_ledger_standalone", "2"},
1695 {"target_txn_in_ledger", "4"},
1696 {"maximum_txn_in_ledger", "5"}}));
1697 // should throw
1698 fail();
1699 }
1700 catch (std::runtime_error const& e)
1701 {
1703 e.what() ==
1704 "The minimum number of low-fee transactions allowed "
1705 "per ledger (minimum_txn_in_ledger) exceeds "
1706 "the maximum number of low-fee transactions allowed per "
1707 "ledger (maximum_txn_in_ledger)."s);
1708 }
1709 try
1710 {
1711 Env env(
1712 *this,
1713 makeConfig(
1714 {{"minimum_txn_in_ledger", "2"},
1715 {"minimum_txn_in_ledger_standalone", "200"},
1716 {"target_txn_in_ledger", "4"},
1717 {"maximum_txn_in_ledger", "5"}}));
1718 // should throw
1719 fail();
1720 }
1721 catch (std::runtime_error const& e)
1722 {
1724 e.what() ==
1725 "The minimum number of low-fee transactions allowed "
1726 "per ledger (minimum_txn_in_ledger_standalone) exceeds "
1727 "the maximum number of low-fee transactions allowed per "
1728 "ledger (maximum_txn_in_ledger)."s);
1729 }
1730 }
1731
1732 void
1734 {
1735 using namespace jtx;
1736 testcase("unexpected balance change");
1737
1738 Env env(
1739 *this,
1740 makeConfig(
1741 {{"minimum_txn_in_ledger_standalone", "3"}},
1742 {{"account_reserve", "200"}, {"owner_reserve", "50"}}));
1743
1744 auto alice = Account("alice");
1745 auto bob = Account("bob");
1746
1747 auto queued = ter(terQUEUED);
1748
1749 // ledgers in queue is 2 because of makeConfig
1750 auto const initQueueMax = initFee(env, 3, 2, 10, 200, 50);
1751
1752 BEAST_EXPECT(env.current()->fees().base == 10);
1753
1754 checkMetrics(__LINE__, env, 0, initQueueMax, 0, 3, 256);
1755
1756 env.fund(drops(5000), noripple(alice));
1757 env.fund(XRP(50000), noripple(bob));
1758 checkMetrics(__LINE__, env, 0, initQueueMax, 2, 3, 256);
1759 auto USD = bob["USD"];
1760
1761 env(offer(alice, USD(5000), drops(5000)), require(owners(alice, 1)));
1762 checkMetrics(__LINE__, env, 0, initQueueMax, 3, 3, 256);
1763
1764 env.close();
1765 checkMetrics(__LINE__, env, 0, 6, 0, 3, 256);
1766
1767 // Fill up the ledger
1768 fillQueue(env, alice);
1769 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
1770
1771 // Queue up a couple of transactions, plus one
1772 // more expensive one.
1773 auto aliceSeq = env.seq(alice);
1774 env(noop(alice), seq(aliceSeq++), queued);
1775 env(noop(alice), seq(aliceSeq++), queued);
1776 env(noop(alice), seq(aliceSeq++), queued);
1777 env(noop(alice), fee(drops(1000)), seq(aliceSeq), queued);
1778 checkMetrics(__LINE__, env, 4, 6, 4, 3, 256);
1779
1780 // This offer should take Alice's offer
1781 // up to Alice's reserve.
1782 env(offer(bob, drops(5000), USD(5000)),
1783 openLedgerFee(env),
1784 require(
1785 balance(alice, drops(250)), owners(alice, 1), lines(alice, 1)));
1786 checkMetrics(__LINE__, env, 4, 6, 5, 3, 256);
1787
1788 // Try adding a new transaction.
1789 // Too many fees in flight.
1790 env(noop(alice),
1791 fee(drops(200)),
1792 seq(aliceSeq + 1),
1794 checkMetrics(__LINE__, env, 4, 6, 5, 3, 256);
1795
1796 // Close the ledger. All of Alice's transactions
1797 // take a fee, except the last one.
1798 env.close();
1799 checkMetrics(__LINE__, env, 1, 10, 3, 5, 256);
1800 env.require(balance(alice, drops(250 - 30)));
1801
1802 // Still can't add a new transaction for Alice,
1803 // no matter the fee.
1804 env(noop(alice),
1805 fee(drops(200)),
1806 seq(aliceSeq + 1),
1808 checkMetrics(__LINE__, env, 1, 10, 3, 5, 256);
1809
1810 /* At this point, Alice's transaction is indefinitely
1811 stuck in the queue. Eventually it will either
1812 expire, get forced off the end by more valuable
1813 transactions, get replaced by Alice, or Alice
1814 will get more XRP, and it'll process.
1815 */
1816
1817 for (int i = 0; i < 9; ++i)
1818 {
1819 env.close();
1820 checkMetrics(__LINE__, env, 1, 10, 0, 5, 256);
1821 }
1822
1823 // And Alice's transaction expires (via the retry limit,
1824 // not LastLedgerSequence).
1825 env.close();
1826 checkMetrics(__LINE__, env, 0, 10, 0, 5, 256);
1827 }
1828
1829 void
1831 {
1832 using namespace jtx;
1833 testcase("blockers sequence");
1834
1835 auto alice = Account("alice");
1836 auto bob = Account("bob");
1837 auto charlie = Account("charlie");
1838 auto daria = Account("daria");
1839
1840 auto queued = ter(terQUEUED);
1841
1842 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
1843
1844 BEAST_EXPECT(env.current()->fees().base == 10);
1845
1846 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
1847
1848 env.fund(XRP(50000), noripple(alice, bob));
1849 env.memoize(charlie);
1850 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 3, 256);
1851 {
1852 // Cannot put a blocker in an account's queue if that queue
1853 // already holds two or more (non-blocker) entries.
1854
1855 // Fill up the open ledger
1856 env(noop(alice));
1857 // Set a regular key just to clear the password spent flag
1858 env(regkey(alice, charlie));
1859 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
1860
1861 // Put two "normal" txs in the queue
1862 auto const aliceSeq = env.seq(alice);
1863 env(noop(alice), seq(aliceSeq + 0), queued);
1864 env(noop(alice), seq(aliceSeq + 1), queued);
1865
1866 // Can't replace either queued transaction with a blocker
1867 env(fset(alice, asfAccountTxnID),
1868 seq(aliceSeq + 0),
1869 fee(20),
1871
1872 env(regkey(alice, bob),
1873 seq(aliceSeq + 1),
1874 fee(20),
1876
1877 // Can't append a blocker to the queue.
1878 env(signers(alice, 2, {{bob}, {charlie}, {daria}}),
1879 seq(aliceSeq + 2),
1880 fee(20),
1882
1883 // Other accounts are not affected
1884 env(noop(bob), queued);
1885 checkMetrics(__LINE__, env, 3, std::nullopt, 4, 3, 256);
1886
1887 // Drain the queue.
1888 env.close();
1889 checkMetrics(__LINE__, env, 0, 8, 4, 4, 256);
1890 }
1891 {
1892 // Replace a lone non-blocking tx with a blocker.
1893
1894 // Fill up the open ledger and put just one entry in the TxQ.
1895 env(noop(alice));
1896
1897 auto const aliceSeq = env.seq(alice);
1898 env(noop(alice), seq(aliceSeq + 0), queued);
1899
1900 // Since there's only one entry in the queue we can replace
1901 // that entry with a blocker.
1902 env(regkey(alice, bob), seq(aliceSeq + 0), fee(20), queued);
1903
1904 // Now that there's a blocker in the queue we can't append to
1905 // the queue.
1906 env(noop(alice), seq(aliceSeq + 1), ter(telCAN_NOT_QUEUE_BLOCKED));
1907
1908 // Other accounts are unaffected.
1909 env(noop(bob), queued);
1910
1911 // We can replace the blocker with a different blocker.
1912 env(signers(alice, 2, {{bob}, {charlie}, {daria}}),
1913 seq(aliceSeq + 0),
1914 fee(26),
1915 queued);
1916
1917 // Prove that the queue is still blocked.
1918 env(noop(alice), seq(aliceSeq + 1), ter(telCAN_NOT_QUEUE_BLOCKED));
1919
1920 // We can replace the blocker with a non-blocker. Then we can
1921 // successfully append to the queue.
1922 env(noop(alice), seq(aliceSeq + 0), fee(33), queued);
1923 env(noop(alice), seq(aliceSeq + 1), queued);
1924
1925 // Drain the queue.
1926 env.close();
1927 checkMetrics(__LINE__, env, 0, 10, 3, 5, 256);
1928 }
1929 {
1930 // Put a blocker in an empty queue.
1931
1932 // Fill up the open ledger and put a blocker as Alice's first
1933 // entry in the (empty) TxQ.
1934 env(noop(alice));
1935 env(noop(alice));
1936 env(noop(alice));
1937
1938 auto const aliceSeq = env.seq(alice);
1939 env(fset(alice, asfAccountTxnID), seq(aliceSeq + 0), queued);
1940
1941 // Since there's a blocker in the queue we can't append to
1942 // the queue.
1943 env(noop(alice), seq(aliceSeq + 1), ter(telCAN_NOT_QUEUE_BLOCKED));
1944
1945 // Other accounts are unaffected.
1946 env(noop(bob), queued);
1947
1948 // We can replace the blocker with a non-blocker. Then we can
1949 // successfully append to the queue.
1950 env(noop(alice), seq(aliceSeq + 0), fee(20), queued);
1951 env(noop(alice), seq(aliceSeq + 1), queued);
1952
1953 // Drain the queue.
1954 env.close();
1955 checkMetrics(__LINE__, env, 0, 12, 3, 6, 256);
1956 }
1957 }
1958
1959 void
1961 {
1962 using namespace jtx;
1963 testcase("blockers ticket");
1964
1965 auto alice = Account("alice");
1966 auto bob = Account("bob");
1967 auto charlie = Account("charlie");
1968 auto daria = Account("daria");
1969
1970 auto queued = ter(terQUEUED);
1971
1972 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
1973
1974 BEAST_EXPECT(env.current()->fees().base == 10);
1975
1976 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
1977
1978 env.fund(XRP(50000), noripple(alice, bob));
1979 env.memoize(charlie);
1980
1981 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 3, 256);
1982
1983 std::uint32_t tkt{env.seq(alice) + 1};
1984 {
1985 // Cannot put a blocker in an account's queue if that queue
1986 // already holds two or more (non-blocker) entries.
1987
1988 // Fill up the open ledger
1989 env(ticket::create(alice, 250), seq(tkt - 1));
1990 // Set a regular key just to clear the password spent flag
1991 env(regkey(alice, charlie));
1992 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
1993
1994 // Put two "normal" txs in the queue
1995 auto const aliceSeq = env.seq(alice);
1996 env(noop(alice), ticket::use(tkt + 2), queued);
1997 env(noop(alice), ticket::use(tkt + 1), queued);
1998
1999 // Can't replace either queued transaction with a blocker
2000 env(fset(alice, asfAccountTxnID),
2001 ticket::use(tkt + 1),
2002 fee(20),
2004
2005 env(regkey(alice, bob),
2006 ticket::use(tkt + 2),
2007 fee(20),
2009
2010 // Can't append a blocker to the queue.
2011 env(signers(alice, 2, {{bob}, {charlie}, {daria}}),
2012 fee(20),
2014
2015 env(signers(alice, 2, {{bob}, {charlie}, {daria}}),
2016 ticket::use(tkt + 0),
2017 fee(20),
2019
2020 // Other accounts are not affected
2021 env(noop(bob), queued);
2022 checkMetrics(__LINE__, env, 3, std::nullopt, 4, 3, 256);
2023
2024 // Drain the queue and local transactions.
2025 env.close();
2026 checkMetrics(__LINE__, env, 0, 8, 5, 4, 256);
2027
2028 // Show that the local transactions have flushed through as well.
2029 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
2030 env(noop(alice), ticket::use(tkt + 0), ter(tefNO_TICKET));
2031 env(noop(alice), ticket::use(tkt + 1), ter(tefNO_TICKET));
2032 env(noop(alice), ticket::use(tkt + 2), ter(tefNO_TICKET));
2033 tkt += 3;
2034 }
2035 {
2036 // Replace a lone non-blocking tx with a blocker.
2037
2038 // Put just one entry in the TxQ.
2039 auto const aliceSeq = env.seq(alice);
2040 env(noop(alice), ticket::use(tkt + 0), queued);
2041
2042 // Since there's an entry in the queue we cannot append a
2043 // blocker to the account's queue.
2044 env(regkey(alice, bob), fee(20), ter(telCAN_NOT_QUEUE_BLOCKS));
2045 env(regkey(alice, bob),
2046 ticket::use(tkt + 1),
2047 fee(20),
2049
2050 // However we can _replace_ that lone entry with a blocker.
2051 env(regkey(alice, bob), ticket::use(tkt + 0), fee(20), queued);
2052
2053 // Now that there's a blocker in the queue we can't append to
2054 // the queue.
2055 env(noop(alice), ter(telCAN_NOT_QUEUE_BLOCKED));
2056 env(noop(alice),
2057 ticket::use(tkt + 1),
2059
2060 // Other accounts are unaffected.
2061 env(noop(bob), queued);
2062
2063 // We can replace the blocker with a different blocker.
2064 env(signers(alice, 2, {{bob}, {charlie}, {daria}}),
2065 ticket::use(tkt + 0),
2066 fee(26),
2067 queued);
2068
2069 // Prove that the queue is still blocked.
2070 env(noop(alice), ter(telCAN_NOT_QUEUE_BLOCKED));
2071 env(noop(alice),
2072 ticket::use(tkt + 1),
2074
2075 // We can replace the blocker with a non-blocker. Then we can
2076 // successfully append to the queue.
2077 env(noop(alice), ticket::use(tkt + 0), fee(33), queued);
2078 env(noop(alice), ticket::use(tkt + 1), queued);
2079 env(noop(alice), seq(aliceSeq), queued);
2080
2081 // Drain the queue.
2082 env.close();
2083 checkMetrics(__LINE__, env, 0, 10, 4, 5, 256);
2084
2085 // Show that the local transactions have flushed through as well.
2086 BEAST_EXPECT(env.seq(alice) == aliceSeq + 1);
2087 env(noop(alice), ticket::use(tkt + 0), ter(tefNO_TICKET));
2088 env(noop(alice), ticket::use(tkt + 1), ter(tefNO_TICKET));
2089 tkt += 2;
2090 }
2091 {
2092 // Put a blocker in an empty queue.
2093
2094 // Fill up the open ledger and put a blocker as Alice's first
2095 // entry in the (empty) TxQ.
2096 env(noop(alice));
2097 env(noop(alice));
2098
2099 env(fset(alice, asfAccountTxnID), ticket::use(tkt + 2), queued);
2100
2101 // Since there's a blocker in the queue we can't append to
2102 // the queue.
2103 env(noop(alice),
2104 ticket::use(tkt + 1),
2106
2107 // Other accounts are unaffected.
2108 env(noop(bob), queued);
2109
2110 // We can replace the blocker with a non-blocker. Then we can
2111 // successfully append to the queue.
2112 env(noop(alice), ticket::use(tkt + 2), fee(20), queued);
2113 env(noop(alice), ticket::use(tkt + 1), queued);
2114
2115 // Drain the queue.
2116 env.close();
2117 checkMetrics(__LINE__, env, 0, 12, 3, 6, 256);
2118 }
2119 }
2120
2121 void
2123 {
2124 using namespace jtx;
2125 testcase("In-flight balance checks");
2126
2127 Env env(
2128 *this,
2129 makeConfig(
2130 {{"minimum_txn_in_ledger_standalone", "3"}},
2131 {{"account_reserve", "200"}, {"owner_reserve", "50"}}));
2132
2133 auto alice = Account("alice");
2134 auto charlie = Account("charlie");
2135 auto gw = Account("gw");
2136
2137 auto queued = ter(terQUEUED);
2138
2139 // Set the fee reserves _really_ low so transactions with fees
2140 // in the ballpark of the reserves can be queued. With default
2141 // reserves, a couple hundred transactions would have to be
2142 // queued before the open ledger fee approached the reserve,
2143 // which would unnecessarily slow down this test.
2144 // ledgers in queue is 2 because of makeConfig
2145 auto const initQueueMax = initFee(env, 3, 2, 10, 200, 50);
2146
2147 auto limit = 3;
2148
2149 checkMetrics(__LINE__, env, 0, initQueueMax, 0, limit, 256);
2150
2151 env.fund(XRP(50000), noripple(alice, charlie), gw);
2152 checkMetrics(__LINE__, env, 0, initQueueMax, limit + 1, limit, 256);
2153
2154 auto USD = gw["USD"];
2155 auto BUX = gw["BUX"];
2156
2158 // Offer with high XRP out and low fee doesn't block
2159 auto aliceSeq = env.seq(alice);
2160 auto aliceBal = env.balance(alice);
2161
2162 env.require(balance(alice, XRP(50000)), owners(alice, 0));
2163
2164 // If this offer crosses, all of alice's
2165 // XRP will be taken (except the reserve).
2166 env(offer(alice, BUX(5000), XRP(50000)), queued);
2167 checkMetrics(__LINE__, env, 1, initQueueMax, limit + 1, limit, 256);
2168
2169 // But because the reserve is protected, another
2170 // transaction will be allowed to queue
2171 env(noop(alice), seq(aliceSeq + 1), queued);
2172 checkMetrics(__LINE__, env, 2, initQueueMax, limit + 1, limit, 256);
2173
2174 env.close();
2175 ++limit;
2176 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2177
2178 // But once we close the ledger, we find alice
2179 // has plenty of XRP, because the offer didn't
2180 // cross (of course).
2181 env.require(balance(alice, aliceBal - drops(20)), owners(alice, 1));
2182 // cancel the offer
2183 env(offer_cancel(alice, aliceSeq));
2184
2186 // Offer with high XRP out and high total fee blocks later txs
2187 fillQueue(env, alice);
2188 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2189 aliceSeq = env.seq(alice);
2190 aliceBal = env.balance(alice);
2191
2192 env.require(owners(alice, 0));
2193
2194 // Alice creates an offer with a fee of half the reserve
2195 env(offer(alice, BUX(5000), XRP(50000)), fee(drops(100)), queued);
2196 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit, 256);
2197
2198 // Alice creates another offer with a fee
2199 // that brings the total to just shy of the reserve
2200 env(noop(alice), fee(drops(99)), seq(aliceSeq + 1), queued);
2201 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2202
2203 // So even a noop will look like alice
2204 // doesn't have the balance to pay the fee
2205 env(noop(alice),
2206 fee(drops(51)),
2207 seq(aliceSeq + 2),
2209 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2210
2211 env.close();
2212 ++limit;
2213 checkMetrics(__LINE__, env, 0, limit * 2, 3, limit, 256);
2214
2215 // But once we close the ledger, we find alice
2216 // has plenty of XRP, because the offer didn't
2217 // cross (of course).
2218 env.require(balance(alice, aliceBal - drops(250)), owners(alice, 1));
2219 // cancel the offer
2220 env(offer_cancel(alice, aliceSeq));
2221
2223 // Offer with high XRP out and super high fee blocks later txs
2224 fillQueue(env, alice);
2225 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2226 aliceSeq = env.seq(alice);
2227 aliceBal = env.balance(alice);
2228
2229 env.require(owners(alice, 0));
2230
2231 // Alice creates an offer with a fee larger than the reserve
2232 // This one can queue because it's the first in the queue for alice
2233 env(offer(alice, BUX(5000), XRP(50000)), fee(drops(300)), queued);
2234 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit, 256);
2235
2236 // So even a noop will look like alice
2237 // doesn't have the balance to pay the fee
2238 env(noop(alice),
2239 fee(drops(51)),
2240 seq(aliceSeq + 1),
2242 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit, 256);
2243
2244 env.close();
2245 ++limit;
2246 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2247
2248 // But once we close the ledger, we find alice
2249 // has plenty of XRP, because the offer didn't
2250 // cross (of course).
2251 env.require(balance(alice, aliceBal - drops(351)), owners(alice, 1));
2252 // cancel the offer
2253 env(offer_cancel(alice, aliceSeq));
2254
2256 // Offer with low XRP out allows later txs
2257 fillQueue(env, alice);
2258 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2259 aliceSeq = env.seq(alice);
2260 aliceBal = env.balance(alice);
2261
2262 // If this offer crosses, just a bit
2263 // of alice's XRP will be taken.
2264 env(offer(alice, BUX(50), XRP(500)), queued);
2265
2266 // And later transactions are just fine
2267 env(noop(alice), seq(aliceSeq + 1), queued);
2268 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2269
2270 env.close();
2271 ++limit;
2272 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2273
2274 // But once we close the ledger, we find alice
2275 // has plenty of XRP, because the offer didn't
2276 // cross (of course).
2277 env.require(balance(alice, aliceBal - drops(20)), owners(alice, 1));
2278 // cancel the offer
2279 env(offer_cancel(alice, aliceSeq));
2280
2282 // Large XRP payment doesn't block later txs
2283 fillQueue(env, alice);
2284 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2285
2286 aliceSeq = env.seq(alice);
2287 aliceBal = env.balance(alice);
2288
2289 // If this payment succeeds, alice will
2290 // send her entire balance to charlie
2291 // (minus the reserve).
2292 env(pay(alice, charlie, XRP(50000)), queued);
2293
2294 // But because the reserve is protected, another
2295 // transaction will be allowed to queue
2296 env(noop(alice), seq(aliceSeq + 1), queued);
2297 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2298
2299 env.close();
2300 ++limit;
2301 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2302
2303 // But once we close the ledger, we find alice
2304 // still has most of her balance, because the
2305 // payment was unfunded!
2306 env.require(balance(alice, aliceBal - drops(20)), owners(alice, 0));
2307
2309 // Small XRP payment allows later txs
2310 fillQueue(env, alice);
2311 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2312
2313 aliceSeq = env.seq(alice);
2314 aliceBal = env.balance(alice);
2315
2316 // If this payment succeeds, alice will
2317 // send just a bit of balance to charlie
2318 env(pay(alice, charlie, XRP(500)), queued);
2319
2320 // And later transactions are just fine
2321 env(noop(alice), seq(aliceSeq + 1), queued);
2322 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2323
2324 env.close();
2325 ++limit;
2326 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2327
2328 // The payment succeeds
2329 env.require(
2330 balance(alice, aliceBal - XRP(500) - drops(20)), owners(alice, 0));
2331
2333 // Large IOU payment allows later txs
2334 auto const amount = USD(500000);
2335 env(trust(alice, USD(50000000)));
2336 env(trust(charlie, USD(50000000)));
2337 checkMetrics(__LINE__, env, 0, limit * 2, 4, limit, 256);
2338 // Close so we don't have to deal
2339 // with tx ordering in consensus.
2340 env.close();
2341
2342 env(pay(gw, alice, amount));
2343 checkMetrics(__LINE__, env, 0, limit * 2, 1, limit, 256);
2344 // Close so we don't have to deal
2345 // with tx ordering in consensus.
2346 env.close();
2347
2348 fillQueue(env, alice);
2349 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2350
2351 aliceSeq = env.seq(alice);
2352 aliceBal = env.balance(alice);
2353 auto aliceUSD = env.balance(alice, USD);
2354
2355 // If this payment succeeds, alice will
2356 // send her entire USD balance to charlie.
2357 env(pay(alice, charlie, amount), queued);
2358
2359 // But that's fine, because it doesn't affect
2360 // alice's XRP balance (other than the fee, of course).
2361 env(noop(alice), seq(aliceSeq + 1), queued);
2362 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2363
2364 env.close();
2365 ++limit;
2366 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2367
2368 // So once we close the ledger, alice has her
2369 // XRP balance, but her USD balance went to charlie.
2370 env.require(
2371 balance(alice, aliceBal - drops(20)),
2372 balance(alice, USD(0)),
2373 balance(charlie, aliceUSD),
2374 owners(alice, 1),
2375 owners(charlie, 1));
2376
2378 // Large XRP to IOU payment doesn't block later txs.
2379
2380 env(offer(gw, XRP(500000), USD(50000)));
2381 // Close so we don't have to deal
2382 // with tx ordering in consensus.
2383 env.close();
2384
2385 fillQueue(env, charlie);
2386 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2387
2388 aliceSeq = env.seq(alice);
2389 aliceBal = env.balance(alice);
2390 auto charlieUSD = env.balance(charlie, USD);
2391
2392 // If this payment succeeds, and uses the
2393 // entire sendMax, alice will send her
2394 // entire XRP balance to charlie in the
2395 // form of USD.
2396 BEAST_EXPECT(XRP(60000) > aliceBal);
2397 env(pay(alice, charlie, USD(1000)), sendmax(XRP(60000)), queued);
2398
2399 // But because the reserve is protected, another
2400 // transaction will be allowed to queue
2401 env(noop(alice), seq(aliceSeq + 1), queued);
2402 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2403
2404 env.close();
2405 ++limit;
2406 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2407
2408 // So once we close the ledger, alice sent a payment
2409 // to charlie using only a portion of her XRP balance
2410 env.require(
2411 balance(alice, aliceBal - XRP(10000) - drops(20)),
2412 balance(alice, USD(0)),
2413 balance(charlie, charlieUSD + USD(1000)),
2414 owners(alice, 1),
2415 owners(charlie, 1));
2416
2418 // Small XRP to IOU payment allows later txs.
2419
2420 fillQueue(env, charlie);
2421 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2422
2423 aliceSeq = env.seq(alice);
2424 aliceBal = env.balance(alice);
2425 charlieUSD = env.balance(charlie, USD);
2426
2427 // If this payment succeeds, and uses the
2428 // entire sendMax, alice will only send
2429 // a portion of her XRP balance to charlie
2430 // in the form of USD.
2431 BEAST_EXPECT(aliceBal > XRP(6001));
2432 env(pay(alice, charlie, USD(500)), sendmax(XRP(6000)), queued);
2433
2434 // And later transactions are just fine
2435 env(noop(alice), seq(aliceSeq + 1), queued);
2436 checkMetrics(__LINE__, env, 2, limit * 2, limit + 1, limit, 256);
2437
2438 env.close();
2439 ++limit;
2440 checkMetrics(__LINE__, env, 0, limit * 2, 2, limit, 256);
2441
2442 // So once we close the ledger, alice sent a payment
2443 // to charlie using only a portion of her XRP balance
2444 env.require(
2445 balance(alice, aliceBal - XRP(5000) - drops(20)),
2446 balance(alice, USD(0)),
2447 balance(charlie, charlieUSD + USD(500)),
2448 owners(alice, 1),
2449 owners(charlie, 1));
2450
2452 // Edge case: what happens if the balance is below the reserve?
2453 env(noop(alice), fee(env.balance(alice) - drops(30)));
2454 env.close();
2455
2456 fillQueue(env, charlie);
2457 checkMetrics(__LINE__, env, 0, limit * 2, limit + 1, limit, 256);
2458
2459 aliceSeq = env.seq(alice);
2460 aliceBal = env.balance(alice);
2461 BEAST_EXPECT(aliceBal == drops(30));
2462
2463 env(noop(alice), fee(drops(25)), queued);
2464 env(noop(alice), seq(aliceSeq + 1), ter(terINSUF_FEE_B));
2465 BEAST_EXPECT(env.balance(alice) == drops(30));
2466
2467 checkMetrics(__LINE__, env, 1, limit * 2, limit + 1, limit, 256);
2468
2469 env.close();
2470 ++limit;
2471 checkMetrics(__LINE__, env, 0, limit * 2, 1, limit, 256);
2472 BEAST_EXPECT(env.balance(alice) == drops(5));
2473 }
2474
2475 void
2477 {
2478 using namespace jtx;
2479 using namespace std::chrono;
2480 testcase("consequences");
2481
2482 Env env(*this);
2483 auto const alice = Account("alice");
2484 env.memoize(alice);
2485 env.memoize("bob");
2486 env.memoize("carol");
2487 {
2488 auto const jtx = env.jt(offer_cancel(alice, 3), seq(5), fee(10));
2489 auto const pf = preflight(
2490 env.app(),
2491 env.current()->rules(),
2492 *jtx.stx,
2493 tapNONE,
2494 env.journal);
2495 BEAST_EXPECT(pf.ter == tesSUCCESS);
2496 BEAST_EXPECT(!pf.consequences.isBlocker());
2497 BEAST_EXPECT(pf.consequences.fee() == drops(10));
2498 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
2499 }
2500
2501 {
2502 auto USD = alice["USD"];
2503
2504 auto const jtx =
2505 env.jt(trust("carol", USD(50000000)), seq(1), fee(10));
2506 auto const pf = preflight(
2507 env.app(),
2508 env.current()->rules(),
2509 *jtx.stx,
2510 tapNONE,
2511 env.journal);
2512 BEAST_EXPECT(pf.ter == tesSUCCESS);
2513 BEAST_EXPECT(!pf.consequences.isBlocker());
2514 BEAST_EXPECT(pf.consequences.fee() == drops(10));
2515 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
2516 }
2517
2518 {
2519 auto const jtx = env.jt(ticket::create(alice, 1), seq(1), fee(10));
2520 auto const pf = preflight(
2521 env.app(),
2522 env.current()->rules(),
2523 *jtx.stx,
2524 tapNONE,
2525 env.journal);
2526 BEAST_EXPECT(pf.ter == tesSUCCESS);
2527 BEAST_EXPECT(!pf.consequences.isBlocker());
2528 BEAST_EXPECT(pf.consequences.fee() == drops(10));
2529 BEAST_EXPECT(pf.consequences.potentialSpend() == XRP(0));
2530 }
2531 }
2532
2533 void
2535 {
2536 // It is possible for an account to be present in the queue but have
2537 // no queued transactions. This has been the source of at least one
2538 // bug where an insufficiently informed developer assumed that if an
2539 // account was present in the queue then it also had at least one
2540 // queued transaction.
2541 //
2542 // This test does touch testing to verify that, at least, that bug
2543 // is addressed.
2544 using namespace jtx;
2545 testcase("acct in queue but empty");
2546
2547 auto alice = Account("alice");
2548 auto bob = Account("bob");
2549 auto charlie = Account("charlie");
2550
2551 auto queued = ter(terQUEUED);
2552
2553 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
2554
2555 BEAST_EXPECT(env.current()->fees().base == 10);
2556
2557 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
2558
2559 // Fund accounts while the fee is cheap so they all apply.
2560 env.fund(XRP(50000), noripple(alice, bob, charlie));
2561 checkMetrics(__LINE__, env, 0, std::nullopt, 3, 3, 256);
2562
2563 // Alice - no fee change yet
2564 env(noop(alice));
2565 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
2566
2567 // Bob with really high fee - applies
2568 env(noop(bob), openLedgerFee(env));
2569 checkMetrics(__LINE__, env, 0, std::nullopt, 5, 3, 256);
2570
2571 // Charlie with low fee: queued
2572 env(noop(charlie), fee(1000), queued);
2573 checkMetrics(__LINE__, env, 1, std::nullopt, 5, 3, 256);
2574
2575 env.close();
2576 // Verify that the queued transaction was applied
2577 checkMetrics(__LINE__, env, 0, 10, 1, 5, 256);
2578
2580
2581 // Stuff the ledger and queue so we can verify that
2582 // stuff gets kicked out.
2583 env(noop(bob), fee(1000));
2584 env(noop(bob), fee(1000));
2585 env(noop(bob), fee(1000));
2586 env(noop(bob), fee(1000));
2587 env(noop(bob), fee(1000));
2588 checkMetrics(__LINE__, env, 0, 10, 6, 5, 256);
2589
2590 // Use explicit fees so we can control which txn
2591 // will get dropped
2592 // This one gets into the queue, but gets dropped when the
2593 // higher fee one is added later.
2594 std::uint32_t const charlieSeq{env.seq(charlie)};
2595 env(noop(charlie), fee(15), seq(charlieSeq), queued);
2596
2597 // These stay in the queue.
2598 std::uint32_t aliceSeq{env.seq(alice)};
2599 std::uint32_t bobSeq{env.seq(bob)};
2600
2601 env(noop(alice), fee(16), seq(aliceSeq++), queued);
2602 env(noop(bob), fee(16), seq(bobSeq++), queued);
2603 env(noop(alice), fee(17), seq(aliceSeq++), queued);
2604 env(noop(bob), fee(17), seq(bobSeq++), queued);
2605 env(noop(alice), fee(18), seq(aliceSeq++), queued);
2606 env(noop(bob), fee(19), seq(bobSeq++), queued);
2607 env(noop(alice), fee(20), seq(aliceSeq++), queued);
2608 env(noop(bob), fee(20), seq(bobSeq++), queued);
2609 env(noop(alice), fee(21), seq(aliceSeq++), queued);
2610
2611 // Queue is full now.
2612 checkMetrics(__LINE__, env, 10, 10, 6, 5, 385);
2613
2614 // Try to add another transaction with the default (low) fee,
2615 // it should fail because the queue is full.
2616 env(noop(alice), seq(aliceSeq++), ter(telCAN_NOT_QUEUE_FULL));
2617
2618 // Add another transaction, with a higher fee,
2619 // not high enough to get into the ledger, but high
2620 // enough to get into the queue (and kick Charlie's out)
2621 env(noop(bob), fee(22), seq(bobSeq++), queued);
2622
2624
2625 // That was the setup for the actual test :-). Now make
2626 // sure we get the right results if we try to add a
2627 // transaction for Charlie (who's in the queue, but has no queued
2628 // transactions) with the wrong sequence numbers.
2629 //
2630 // Charlie is paying a high enough fee to go straight into the
2631 // ledger in order to get into the vicinity of an assert which
2632 // should no longer fire :-).
2633 env(noop(charlie), fee(8000), seq(charlieSeq - 1), ter(tefPAST_SEQ));
2634 env(noop(charlie), fee(8000), seq(charlieSeq + 1), ter(terPRE_SEQ));
2635 env(noop(charlie), fee(8000), seq(charlieSeq), ter(tesSUCCESS));
2636 }
2637
2638 void
2640 {
2641 using namespace jtx;
2642 testcase("rpc");
2643
2644 Env env(*this);
2645
2646 auto fee = env.rpc("fee");
2647
2648 if (BEAST_EXPECT(fee.isMember(jss::result)) &&
2649 BEAST_EXPECT(!RPC::contains_error(fee[jss::result])))
2650 {
2651 auto const& result = fee[jss::result];
2653 result.isMember(jss::ledger_current_index) &&
2654 result[jss::ledger_current_index] == 3);
2655 BEAST_EXPECT(result.isMember(jss::current_ledger_size));
2656 BEAST_EXPECT(result.isMember(jss::current_queue_size));
2657 BEAST_EXPECT(result.isMember(jss::expected_ledger_size));
2658 BEAST_EXPECT(!result.isMember(jss::max_queue_size));
2659 BEAST_EXPECT(result.isMember(jss::drops));
2660 auto const& drops = result[jss::drops];
2661 BEAST_EXPECT(drops.isMember(jss::base_fee));
2662 BEAST_EXPECT(drops.isMember(jss::median_fee));
2663 BEAST_EXPECT(drops.isMember(jss::minimum_fee));
2664 BEAST_EXPECT(drops.isMember(jss::open_ledger_fee));
2665 BEAST_EXPECT(result.isMember(jss::levels));
2666 auto const& levels = result[jss::levels];
2667 BEAST_EXPECT(levels.isMember(jss::median_level));
2668 BEAST_EXPECT(levels.isMember(jss::minimum_level));
2669 BEAST_EXPECT(levels.isMember(jss::open_ledger_level));
2670 BEAST_EXPECT(levels.isMember(jss::reference_level));
2671 }
2672
2673 env.close();
2674
2675 fee = env.rpc("fee");
2676
2677 if (BEAST_EXPECT(fee.isMember(jss::result)) &&
2678 BEAST_EXPECT(!RPC::contains_error(fee[jss::result])))
2679 {
2680 auto const& result = fee[jss::result];
2682 result.isMember(jss::ledger_current_index) &&
2683 result[jss::ledger_current_index] == 4);
2684 BEAST_EXPECT(result.isMember(jss::current_ledger_size));
2685 BEAST_EXPECT(result.isMember(jss::current_queue_size));
2686 BEAST_EXPECT(result.isMember(jss::expected_ledger_size));
2687 BEAST_EXPECT(result.isMember(jss::max_queue_size));
2688 auto const& drops = result[jss::drops];
2689 BEAST_EXPECT(drops.isMember(jss::base_fee));
2690 BEAST_EXPECT(drops.isMember(jss::median_fee));
2691 BEAST_EXPECT(drops.isMember(jss::minimum_fee));
2692 BEAST_EXPECT(drops.isMember(jss::open_ledger_fee));
2693 BEAST_EXPECT(result.isMember(jss::levels));
2694 auto const& levels = result[jss::levels];
2695 BEAST_EXPECT(levels.isMember(jss::median_level));
2696 BEAST_EXPECT(levels.isMember(jss::minimum_level));
2697 BEAST_EXPECT(levels.isMember(jss::open_ledger_level));
2698 BEAST_EXPECT(levels.isMember(jss::reference_level));
2699 }
2700 }
2701
2702 void
2704 {
2705 /* This test is based on a reported regression where a
2706 replacement candidate transaction found the tx it was trying
2707 to replace did not have `consequences` set
2708
2709 Hypothesis: The queue had '22 through '25. At some point(s),
2710 both the original '22 and '23 expired and were removed from
2711 the queue. A second '22 was submitted, and the multi-tx logic
2712 did not kick in, because it matched the account's sequence
2713 number (a_seq == t_seq). The third '22 was submitted and found
2714 the '22 in the queue did not have consequences.
2715 */
2716 using namespace jtx;
2717 testcase("expiration replacement");
2718
2719 Env env(
2720 *this,
2721 makeConfig(
2722 {{"minimum_txn_in_ledger_standalone", "1"},
2723 {"ledgers_in_queue", "10"},
2724 {"maximum_txn_per_account", "20"}}));
2725
2726 // Alice will recreate the scenario. Bob will block.
2727 auto const alice = Account("alice");
2728 auto const bob = Account("bob");
2729
2730 env.fund(XRP(500000), noripple(alice, bob));
2731 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 1, 256);
2732
2733 auto const aliceSeq = env.seq(alice);
2734 BEAST_EXPECT(env.current()->info().seq == 3);
2735 env(noop(alice),
2736 seq(aliceSeq),
2737 json(R"({"LastLedgerSequence":5})"),
2738 ter(terQUEUED));
2739 env(noop(alice),
2740 seq(aliceSeq + 1),
2741 json(R"({"LastLedgerSequence":5})"),
2742 ter(terQUEUED));
2743 env(noop(alice),
2744 seq(aliceSeq + 2),
2745 json(R"({"LastLedgerSequence":10})"),
2746 ter(terQUEUED));
2747 env(noop(alice),
2748 seq(aliceSeq + 3),
2749 json(R"({"LastLedgerSequence":11})"),
2750 ter(terQUEUED));
2751 checkMetrics(__LINE__, env, 4, std::nullopt, 2, 1, 256);
2752 auto const bobSeq = env.seq(bob);
2753 // Ledger 4 gets 3,
2754 // Ledger 5 gets 4,
2755 // Ledger 6 gets 5.
2756 for (int i = 0; i < 3 + 4 + 5; ++i)
2757 {
2758 env(noop(bob), seq(bobSeq + i), fee(200), ter(terQUEUED));
2759 }
2760 checkMetrics(__LINE__, env, 4 + 3 + 4 + 5, std::nullopt, 2, 1, 256);
2761 // Close ledger 3
2762 env.close();
2763 checkMetrics(__LINE__, env, 4 + 4 + 5, 20, 3, 2, 256);
2764 // Close ledger 4
2765 env.close();
2766 checkMetrics(__LINE__, env, 4 + 5, 30, 4, 3, 256);
2767 // Close ledger 5
2768 env.close();
2769 // Alice's first two txs expired.
2770 checkMetrics(__LINE__, env, 2, 40, 5, 4, 256);
2771
2772 // Because aliceSeq is missing, aliceSeq + 1 fails
2773 env(noop(alice), seq(aliceSeq + 1), ter(terPRE_SEQ));
2774
2775 // Cannot fill the gap with a blocker since Alice's queue is not empty.
2776 env(fset(alice, asfAccountTxnID),
2777 seq(aliceSeq),
2779 checkMetrics(__LINE__, env, 2, 40, 5, 4, 256);
2780
2781 // However we can fill the gap with a non-blocker.
2782 env(noop(alice), seq(aliceSeq), fee(20), ter(terQUEUED));
2783 checkMetrics(__LINE__, env, 3, 40, 5, 4, 256);
2784
2785 // Attempt to queue up a new aliceSeq + 1 tx that's a blocker.
2786 env(fset(alice, asfAccountTxnID),
2787 seq(aliceSeq + 1),
2789 checkMetrics(__LINE__, env, 3, 40, 5, 4, 256);
2790
2791 // Queue up a non-blocker replacement for aliceSeq + 1.
2792 env(noop(alice), seq(aliceSeq + 1), fee(20), ter(terQUEUED));
2793 checkMetrics(__LINE__, env, 4, 40, 5, 4, 256);
2794
2795 // Close ledger 6
2796 env.close();
2797 // We expect that all of alice's queued tx's got into
2798 // the open ledger.
2799 checkMetrics(__LINE__, env, 0, 50, 4, 5, 256);
2800 BEAST_EXPECT(env.seq(alice) == aliceSeq + 4);
2801 }
2802
2803 void
2805 {
2806 // This test focuses on which gaps in queued transactions are
2807 // allowed to be filled even when the account's queue is full.
2808
2809 // NOTE: This test is fragile and dependent on ordering of
2810 // transactions, which is affected by the closed/validated
2811 // ledger hash. This test may need to be edited if changes
2812 // are made that impact the ledger hash.
2813 // TODO: future-proof this test.
2814 using namespace jtx;
2815 testcase("full queue gap handling");
2816
2817 Env env(
2818 *this,
2819 makeConfig(
2820 {{"minimum_txn_in_ledger_standalone", "1"},
2821 {"ledgers_in_queue", "10"},
2822 {"maximum_txn_per_account", "11"}}));
2823
2824 // Alice will have the gaps. Bob will keep the queue busy with
2825 // high fee transactions so alice's transactions can expire to leave
2826 // gaps.
2827 auto const alice = Account("alice");
2828 auto const bob = Account("bob");
2829
2830 env.fund(XRP(500000), noripple(alice, bob));
2831 checkMetrics(__LINE__, env, 0, std::nullopt, 2, 1, 256);
2832
2833 auto const aliceSeq = env.seq(alice);
2834 BEAST_EXPECT(env.current()->info().seq == 3);
2835
2836 // Start by procuring tickets for alice to use to keep her queue full
2837 // without affecting the sequence gap that will appear later.
2838 env(ticket::create(alice, 11),
2839 seq(aliceSeq + 0),
2840 fee(201),
2841 ter(terQUEUED));
2842 env(noop(alice),
2843 seq(aliceSeq + 11),
2844 json(R"({"LastLedgerSequence":11})"),
2845 ter(terQUEUED));
2846 env(noop(alice),
2847 seq(aliceSeq + 12),
2848 json(R"({"LastLedgerSequence":11})"),
2849 ter(terQUEUED));
2850 env(noop(alice),
2851 seq(aliceSeq + 13),
2852 json(R"({"LastLedgerSequence":11})"),
2853 ter(terQUEUED));
2854 env(noop(alice),
2855 seq(aliceSeq + 14),
2856 json(R"({"LastLedgerSequence":11})"),
2857 ter(terQUEUED));
2858 env(noop(alice),
2859 seq(aliceSeq + 15),
2860 json(R"({"LastLedgerSequence":11})"),
2861 ter(terQUEUED));
2862 env(noop(alice),
2863 seq(aliceSeq + 16),
2864 json(R"({"LastLedgerSequence": 5})"),
2865 ter(terQUEUED));
2866 env(noop(alice),
2867 seq(aliceSeq + 17),
2868 json(R"({"LastLedgerSequence": 5})"),
2869 ter(terQUEUED));
2870 env(noop(alice),
2871 seq(aliceSeq + 18),
2872 json(R"({"LastLedgerSequence": 5})"),
2873 ter(terQUEUED));
2874 env(noop(alice),
2875 seq(aliceSeq + 19),
2876 json(R"({"LastLedgerSequence":11})"),
2877 ter(terQUEUED));
2878 checkMetrics(__LINE__, env, 10, std::nullopt, 2, 1, 256);
2879
2880 auto const bobSeq = env.seq(bob);
2881 // Ledger 4 gets 2 from bob and 1 from alice,
2882 // Ledger 5 gets 4 from bob,
2883 // Ledger 6 gets 5 from bob.
2884 for (int i = 0; i < 2 + 4 + 5; ++i)
2885 {
2886 env(noop(bob), seq(bobSeq + i), fee(200), ter(terQUEUED));
2887 }
2888 checkMetrics(__LINE__, env, 10 + 2 + 4 + 5, std::nullopt, 2, 1, 256);
2889 // Close ledger 3
2890 env.close();
2891 checkMetrics(__LINE__, env, 9 + 4 + 5, 20, 3, 2, 256);
2892 BEAST_EXPECT(env.seq(alice) == aliceSeq + 12);
2893
2894 // Close ledger 4
2895 env.close();
2896 checkMetrics(__LINE__, env, 9 + 5, 30, 4, 3, 256);
2897 BEAST_EXPECT(env.seq(alice) == aliceSeq + 12);
2898
2899 // Close ledger 5
2900 env.close();
2901 // Three of Alice's txs expired.
2902 checkMetrics(__LINE__, env, 6, 40, 5, 4, 256);
2903 BEAST_EXPECT(env.seq(alice) == aliceSeq + 12);
2904
2905 // Top off Alice's queue again using Tickets so the sequence gap is
2906 // unaffected.
2907 env(noop(alice), ticket::use(aliceSeq + 1), ter(terQUEUED));
2908 env(noop(alice), ticket::use(aliceSeq + 2), ter(terQUEUED));
2909 env(noop(alice), ticket::use(aliceSeq + 3), ter(terQUEUED));
2910 env(noop(alice), ticket::use(aliceSeq + 4), ter(terQUEUED));
2911 env(noop(alice), ticket::use(aliceSeq + 5), ter(terQUEUED));
2912 env(noop(alice), ticket::use(aliceSeq + 6), ter(telCAN_NOT_QUEUE_FULL));
2913 checkMetrics(__LINE__, env, 11, 40, 5, 4, 256);
2914
2915 // Even though alice's queue is full we can still slide in a couple
2916 // more transactions because she has a sequence gap. But we
2917 // can only install a transaction that fills the bottom of the gap.
2918 // Explore that...
2919
2920 // Verify that we can't queue a sequence-based transaction that
2921 // follows a gap.
2922 env(noop(alice), seq(aliceSeq + 20), ter(telCAN_NOT_QUEUE_FULL));
2923
2924 // Verify that the transaction in front of the gap is still present
2925 // by attempting to replace it without a sufficient fee.
2926 env(noop(alice), seq(aliceSeq + 15), ter(telCAN_NOT_QUEUE_FEE));
2927
2928 // We can't queue a transaction into the middle of the gap. It must
2929 // go at the front.
2930 env(noop(alice), seq(aliceSeq + 18), ter(telCAN_NOT_QUEUE_FULL));
2931 env(noop(alice), seq(aliceSeq + 17), ter(telCAN_NOT_QUEUE_FULL));
2932
2933 // Successfully put this transaction into the front of the gap.
2934 env(noop(alice), seq(aliceSeq + 16), ter(terQUEUED));
2935
2936 // Still can't put a sequence-based transaction at the end of the gap.
2937 env(noop(alice), seq(aliceSeq + 18), ter(telCAN_NOT_QUEUE_FULL));
2938
2939 // But we can still fill the gap from the front.
2940 env(noop(alice), seq(aliceSeq + 17), ter(terQUEUED));
2941
2942 // Finally we can fill in the entire gap.
2943 env(noop(alice), seq(aliceSeq + 18), ter(terQUEUED));
2944 checkMetrics(__LINE__, env, 14, 40, 5, 4, 256);
2945
2946 // Verify that nothing can be added now that the gap is filled.
2947 env(noop(alice), seq(aliceSeq + 20), ter(telCAN_NOT_QUEUE_FULL));
2948
2949 // Close ledger 6. That removes some of alice's transactions,
2950 // but alice adds some more transaction(s) so expectedCount
2951 // may not reduce to 8.
2952 env.close();
2953 checkMetrics(__LINE__, env, 9, 50, 6, 5, 256);
2954 BEAST_EXPECT(env.seq(alice) == aliceSeq + 15);
2955
2956 // Close ledger 7. That should remove 4 more of alice's transactions.
2957 env.close();
2958 checkMetrics(__LINE__, env, 2, 60, 7, 6, 256);
2959 BEAST_EXPECT(env.seq(alice) == aliceSeq + 19);
2960
2961 // Close one last ledger to see all of alice's transactions moved
2962 // into the ledger, including the tickets
2963 env.close();
2964 checkMetrics(__LINE__, env, 0, 70, 2, 7, 256);
2965 BEAST_EXPECT(env.seq(alice) == aliceSeq + 21);
2966 }
2967
2968 void
2970 {
2971 testcase("Autofilled sequence should account for TxQ");
2972 using namespace jtx;
2973 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "6"}}));
2974 Env_ss envs(env);
2975 auto const& txQ = env.app().getTxQ();
2976
2977 auto const alice = Account("alice");
2978 auto const bob = Account("bob");
2979 env.fund(XRP(100000), alice, bob);
2980
2981 fillQueue(env, alice);
2982 checkMetrics(__LINE__, env, 0, std::nullopt, 7, 6, 256);
2983
2984 // Queue up several transactions for alice sign-and-submit
2985 auto const aliceSeq = env.seq(alice);
2986 auto const lastLedgerSeq = env.current()->info().seq + 2;
2987
2989 for (int i = 0; i < 5; ++i)
2990 {
2991 if (i == 2)
2992 envs(
2993 noop(alice),
2994 fee(1000),
2995 seq(none),
2996 json(jss::LastLedgerSequence, lastLedgerSeq),
2998 else
2999 envs(noop(alice), fee(1000), seq(none), ter(terQUEUED))(
3000 submitParams);
3001 }
3002 checkMetrics(__LINE__, env, 5, std::nullopt, 7, 6, 256);
3003 {
3004 auto aliceStat = txQ.getAccountTxs(alice.id());
3005 SeqProxy seq = SeqProxy::sequence(aliceSeq);
3006 BEAST_EXPECT(aliceStat.size() == 5);
3007 for (auto const& tx : aliceStat)
3008 {
3009 BEAST_EXPECT(tx.seqProxy == seq);
3010 BEAST_EXPECT(tx.feeLevel == FeeLevel64{25600});
3011 if (seq.value() == aliceSeq + 2)
3012 {
3014 tx.lastValid && *tx.lastValid == lastLedgerSeq);
3015 }
3016 else
3017 {
3018 BEAST_EXPECT(!tx.lastValid);
3019 }
3020 seq.advanceBy(1);
3021 }
3022 }
3023 // Put some txs in the queue for bob.
3024 // Give them a higher fee so they'll beat alice's.
3025 for (int i = 0; i < 8; ++i)
3026 envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
3027 checkMetrics(__LINE__, env, 13, std::nullopt, 7, 6, 256);
3028
3029 env.close();
3030 checkMetrics(__LINE__, env, 5, 14, 8, 7, 256);
3031 // Put some more txs in the queue for bob.
3032 // Give them a higher fee so they'll beat alice's.
3033 fillQueue(env, bob);
3034 for (int i = 0; i < 9; ++i)
3035 envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
3036 checkMetrics(__LINE__, env, 14, 14, 8, 7, 25601);
3037 env.close();
3038 // Put some more txs in the queue for bob.
3039 // Give them a higher fee so they'll beat alice's.
3040 fillQueue(env, bob);
3041 for (int i = 0; i < 10; ++i)
3042 envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
3043 checkMetrics(__LINE__, env, 15, 16, 9, 8, 256);
3044 env.close();
3045 checkMetrics(__LINE__, env, 4, 18, 10, 9, 256);
3046 {
3047 // Bob has nothing left in the queue.
3048 auto bobStat = txQ.getAccountTxs(bob.id());
3049 BEAST_EXPECT(bobStat.empty());
3050 }
3051 // Verify alice's tx got dropped as we BEAST_EXPECT, and that there's
3052 // a gap in her queued txs.
3053 {
3054 auto aliceStat = txQ.getAccountTxs(alice.id());
3055 auto seq = aliceSeq;
3056 BEAST_EXPECT(aliceStat.size() == 4);
3057 for (auto const& tx : aliceStat)
3058 {
3059 // Skip over the missing one.
3060 if (seq == aliceSeq + 2)
3061 ++seq;
3062
3063 BEAST_EXPECT(tx.seqProxy.isSeq() && tx.seqProxy.value() == seq);
3064 BEAST_EXPECT(tx.feeLevel == FeeLevel64{25600});
3065 BEAST_EXPECT(!tx.lastValid);
3066 ++seq;
3067 }
3068 }
3069 // Now, fill the gap.
3070 envs(noop(alice), fee(1000), seq(none), ter(terQUEUED))(submitParams);
3071 checkMetrics(__LINE__, env, 5, 18, 10, 9, 256);
3072 {
3073 auto aliceStat = txQ.getAccountTxs(alice.id());
3074 auto seq = aliceSeq;
3075 BEAST_EXPECT(aliceStat.size() == 5);
3076 for (auto const& tx : aliceStat)
3077 {
3078 BEAST_EXPECT(tx.seqProxy.isSeq() && tx.seqProxy.value() == seq);
3079 BEAST_EXPECT(tx.feeLevel == FeeLevel64{25600});
3080 BEAST_EXPECT(!tx.lastValid);
3081 ++seq;
3082 }
3083 }
3084
3085 env.close();
3086 checkMetrics(__LINE__, env, 0, 20, 5, 10, 256);
3087 {
3088 // Bob's data has been cleaned up.
3089 auto bobStat = txQ.getAccountTxs(bob.id());
3090 BEAST_EXPECT(bobStat.empty());
3091 }
3092 {
3093 auto aliceStat = txQ.getAccountTxs(alice.id());
3094 BEAST_EXPECT(aliceStat.empty());
3095 }
3096 }
3097
3098 void
3100 {
3101 using namespace jtx;
3102 testcase("account info");
3103
3104 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
3105 Env_ss envs(env);
3106
3107 Account const alice{"alice"};
3108 env.fund(XRP(1000000), alice);
3109 env.close();
3110
3111 auto const withQueue =
3112 R"({ "account": ")" + alice.human() + R"(", "queue": true })";
3113 auto const withoutQueue = R"({ "account": ")" + alice.human() + R"("})";
3114 auto const prevLedgerWithQueue = R"({ "account": ")" + alice.human() +
3115 R"(", "queue": true, "ledger_index": 3 })";
3116 BEAST_EXPECT(env.current()->info().seq > 3);
3117
3118 {
3119 // account_info without the "queue" argument.
3120 auto const info = env.rpc("json", "account_info", withoutQueue);
3122 info.isMember(jss::result) &&
3123 info[jss::result].isMember(jss::account_data));
3124 BEAST_EXPECT(!info[jss::result].isMember(jss::queue_data));
3125 }
3126 {
3127 // account_info with the "queue" argument.
3128 auto const info = env.rpc("json", "account_info", withQueue);
3130 info.isMember(jss::result) &&
3131 info[jss::result].isMember(jss::account_data));
3132 auto const& result = info[jss::result];
3133 BEAST_EXPECT(result.isMember(jss::queue_data));
3134 auto const& queue_data = result[jss::queue_data];
3136 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3137 BEAST_EXPECT(queue_data[jss::txn_count] == 0);
3138 BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
3139 BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
3140 BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
3141 BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
3142 BEAST_EXPECT(!queue_data.isMember(jss::transactions));
3143 }
3144 checkMetrics(__LINE__, env, 0, 6, 0, 3, 256);
3145
3146 fillQueue(env, alice);
3147 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
3148
3149 {
3150 auto const info = env.rpc("json", "account_info", withQueue);
3152 info.isMember(jss::result) &&
3153 info[jss::result].isMember(jss::account_data));
3154 auto const& result = info[jss::result];
3155 BEAST_EXPECT(result.isMember(jss::queue_data));
3156 auto const& queue_data = result[jss::queue_data];
3158 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3159 BEAST_EXPECT(queue_data[jss::txn_count] == 0);
3160 BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
3161 BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
3162 BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
3163 BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
3164 BEAST_EXPECT(!queue_data.isMember(jss::transactions));
3165 }
3166
3172 checkMetrics(__LINE__, env, 4, 6, 4, 3, 256);
3173
3174 {
3175 auto const info = env.rpc("json", "account_info", withQueue);
3177 info.isMember(jss::result) &&
3178 info[jss::result].isMember(jss::account_data));
3179 auto const& result = info[jss::result];
3180 auto const& data = result[jss::account_data];
3181 BEAST_EXPECT(result.isMember(jss::queue_data));
3182 auto const& queue_data = result[jss::queue_data];
3184 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3185 BEAST_EXPECT(queue_data[jss::txn_count] == 4);
3186 BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
3188 queue_data[jss::lowest_sequence] == data[jss::Sequence]);
3189 BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
3191 queue_data[jss::highest_sequence] ==
3192 data[jss::Sequence].asUInt() +
3193 queue_data[jss::txn_count].asUInt() - 1);
3194 BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued));
3195 BEAST_EXPECT(queue_data[jss::auth_change_queued] == false);
3196 BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total));
3197 BEAST_EXPECT(queue_data[jss::max_spend_drops_total] == "400");
3198 BEAST_EXPECT(queue_data.isMember(jss::transactions));
3199 auto const& queued = queue_data[jss::transactions];
3200 BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
3201 for (unsigned i = 0; i < queued.size(); ++i)
3202 {
3203 auto const& item = queued[i];
3204 BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
3205 BEAST_EXPECT(item[jss::fee_level] == "2560");
3206 BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
3207
3208 BEAST_EXPECT(item.isMember(jss::fee));
3209 BEAST_EXPECT(item[jss::fee] == "100");
3210 BEAST_EXPECT(item.isMember(jss::max_spend_drops));
3211 BEAST_EXPECT(item[jss::max_spend_drops] == "100");
3212 BEAST_EXPECT(item.isMember(jss::auth_change));
3213 BEAST_EXPECT(item[jss::auth_change].asBool() == false);
3214 }
3215 }
3216
3217 // Drain the queue so we can queue up a blocker.
3218 env.close();
3219 checkMetrics(__LINE__, env, 0, 8, 4, 4, 256);
3220
3221 // Fill the ledger and then queue up a blocker.
3223
3225 fset(alice, asfAccountTxnID),
3226 fee(100),
3227 seq(none),
3228 json(jss::LastLedgerSequence, 10),
3230 checkMetrics(__LINE__, env, 1, 8, 5, 4, 256);
3231
3232 {
3233 auto const info = env.rpc("json", "account_info", withQueue);
3235 info.isMember(jss::result) &&
3236 info[jss::result].isMember(jss::account_data));
3237 auto const& result = info[jss::result];
3238 auto const& data = result[jss::account_data];
3239 BEAST_EXPECT(result.isMember(jss::queue_data));
3240 auto const& queue_data = result[jss::queue_data];
3242 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3243 BEAST_EXPECT(queue_data[jss::txn_count] == 1);
3244 BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
3246 queue_data[jss::lowest_sequence] == data[jss::Sequence]);
3247 BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
3249 queue_data[jss::highest_sequence] ==
3250 data[jss::Sequence].asUInt() +
3251 queue_data[jss::txn_count].asUInt() - 1);
3252 BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued));
3253 BEAST_EXPECT(queue_data[jss::auth_change_queued] == true);
3254 BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total));
3255 BEAST_EXPECT(queue_data[jss::max_spend_drops_total] == "100");
3256 BEAST_EXPECT(queue_data.isMember(jss::transactions));
3257 auto const& queued = queue_data[jss::transactions];
3258 BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
3259 for (unsigned i = 0; i < queued.size(); ++i)
3260 {
3261 auto const& item = queued[i];
3262 BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
3263 BEAST_EXPECT(item[jss::fee_level] == "2560");
3264 BEAST_EXPECT(item.isMember(jss::fee));
3265 BEAST_EXPECT(item[jss::fee] == "100");
3266 BEAST_EXPECT(item.isMember(jss::max_spend_drops));
3267 BEAST_EXPECT(item[jss::max_spend_drops] == "100");
3268 BEAST_EXPECT(item.isMember(jss::auth_change));
3269
3270 if (i == queued.size() - 1)
3271 {
3272 BEAST_EXPECT(item[jss::auth_change].asBool() == true);
3273 BEAST_EXPECT(item.isMember(jss::LastLedgerSequence));
3274 BEAST_EXPECT(item[jss::LastLedgerSequence] == 10);
3275 }
3276 else
3277 {
3278 BEAST_EXPECT(item[jss::auth_change].asBool() == false);
3279 BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
3280 }
3281 }
3282 }
3283
3285 submitParams);
3286 checkMetrics(__LINE__, env, 1, 8, 5, 4, 256);
3287
3288 {
3289 auto const info = env.rpc("json", "account_info", withQueue);
3291 info.isMember(jss::result) &&
3292 info[jss::result].isMember(jss::account_data));
3293 auto const& result = info[jss::result];
3294 auto const& data = result[jss::account_data];
3295 BEAST_EXPECT(result.isMember(jss::queue_data));
3296 auto const& queue_data = result[jss::queue_data];
3298 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3299 BEAST_EXPECT(queue_data[jss::txn_count] == 1);
3300 BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
3302 queue_data[jss::lowest_sequence] == data[jss::Sequence]);
3303 BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
3305 queue_data[jss::highest_sequence] ==
3306 data[jss::Sequence].asUInt() +
3307 queue_data[jss::txn_count].asUInt() - 1);
3308 BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued));
3309 BEAST_EXPECT(queue_data[jss::auth_change_queued].asBool());
3310 BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total));
3311 BEAST_EXPECT(queue_data[jss::max_spend_drops_total] == "100");
3312 BEAST_EXPECT(queue_data.isMember(jss::transactions));
3313 auto const& queued = queue_data[jss::transactions];
3314 BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
3315 for (unsigned i = 0; i < queued.size(); ++i)
3316 {
3317 auto const& item = queued[i];
3318 BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
3319 BEAST_EXPECT(item[jss::fee_level] == "2560");
3320
3321 if (i == queued.size() - 1)
3322 {
3323 BEAST_EXPECT(item.isMember(jss::fee));
3324 BEAST_EXPECT(item[jss::fee] == "100");
3325 BEAST_EXPECT(item.isMember(jss::max_spend_drops));
3326 BEAST_EXPECT(item[jss::max_spend_drops] == "100");
3327 BEAST_EXPECT(item.isMember(jss::auth_change));
3328 BEAST_EXPECT(item[jss::auth_change].asBool());
3329 BEAST_EXPECT(item.isMember(jss::LastLedgerSequence));
3330 BEAST_EXPECT(item[jss::LastLedgerSequence] == 10);
3331 }
3332 else
3333 {
3334 BEAST_EXPECT(item.isMember(jss::fee));
3335 BEAST_EXPECT(item[jss::fee] == "100");
3336 BEAST_EXPECT(item.isMember(jss::max_spend_drops));
3337 BEAST_EXPECT(item[jss::max_spend_drops] == "100");
3338 BEAST_EXPECT(item.isMember(jss::auth_change));
3339 BEAST_EXPECT(!item[jss::auth_change].asBool());
3340 BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
3341 }
3342 }
3343 }
3344
3345 {
3346 auto const info =
3347 env.rpc("json", "account_info", prevLedgerWithQueue);
3349 info.isMember(jss::result) &&
3350 RPC::contains_error(info[jss::result]));
3351 }
3352
3353 env.close();
3354 checkMetrics(__LINE__, env, 0, 10, 2, 5, 256);
3355 env.close();
3356 checkMetrics(__LINE__, env, 0, 10, 0, 5, 256);
3357
3358 {
3359 auto const info = env.rpc("json", "account_info", withQueue);
3361 info.isMember(jss::result) &&
3362 info[jss::result].isMember(jss::account_data));
3363 auto const& result = info[jss::result];
3364 BEAST_EXPECT(result.isMember(jss::queue_data));
3365 auto const& queue_data = result[jss::queue_data];
3367 BEAST_EXPECT(queue_data.isMember(jss::txn_count));
3368 BEAST_EXPECT(queue_data[jss::txn_count] == 0);
3369 BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
3370 BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
3371 BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
3372 BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
3373 BEAST_EXPECT(!queue_data.isMember(jss::transactions));
3374 }
3375 }
3376
3377 void
3379 {
3380 using namespace jtx;
3381 testcase("server info");
3382
3383 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
3384 Env_ss envs(env);
3385
3386 Account const alice{"alice"};
3387 env.fund(XRP(1000000), alice);
3388 env.close();
3389
3390 {
3391 auto const server_info = env.rpc("server_info");
3393 server_info.isMember(jss::result) &&
3394 server_info[jss::result].isMember(jss::info));
3395 auto const& info = server_info[jss::result][jss::info];
3397 info.isMember(jss::load_factor) && info[jss::load_factor] == 1);
3398 BEAST_EXPECT(!info.isMember(jss::load_factor_server));
3399 BEAST_EXPECT(!info.isMember(jss::load_factor_local));
3400 BEAST_EXPECT(!info.isMember(jss::load_factor_net));
3401 BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
3402 }
3403 {
3404 auto const server_state = env.rpc("server_state");
3405 auto const& state = server_state[jss::result][jss::state];
3407 state.isMember(jss::load_factor) &&
3408 state[jss::load_factor] == 256);
3410 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3412 state.isMember(jss::load_factor_server) &&
3413 state[jss::load_factor_server] == 256);
3415 state.isMember(jss::load_factor_fee_escalation) &&
3416 state[jss::load_factor_fee_escalation] == 256);
3418 state.isMember(jss::load_factor_fee_queue) &&
3419 state[jss::load_factor_fee_queue] == 256);
3421 state.isMember(jss::load_factor_fee_reference) &&
3422 state[jss::load_factor_fee_reference] == 256);
3423 }
3424
3425 checkMetrics(__LINE__, env, 0, 6, 0, 3, 256);
3426
3427 fillQueue(env, alice);
3428 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
3429
3430 auto aliceSeq = env.seq(alice);
3432 for (auto i = 0; i < 4; ++i)
3433 envs(noop(alice), fee(100), seq(aliceSeq + i), ter(terQUEUED))(
3434 submitParams);
3435 checkMetrics(__LINE__, env, 4, 6, 4, 3, 256);
3436
3437 {
3438 auto const server_info = env.rpc("server_info");
3440 server_info.isMember(jss::result) &&
3441 server_info[jss::result].isMember(jss::info));
3442 auto const& info = server_info[jss::result][jss::info];
3443 // Avoid double rounding issues by comparing to a range.
3445 info.isMember(jss::load_factor) &&
3446 info[jss::load_factor] > 888.88 &&
3447 info[jss::load_factor] < 888.89);
3449 info.isMember(jss::load_factor_server) &&
3450 info[jss::load_factor_server] == 1);
3451 BEAST_EXPECT(!info.isMember(jss::load_factor_local));
3452 BEAST_EXPECT(!info.isMember(jss::load_factor_net));
3454 info.isMember(jss::load_factor_fee_escalation) &&
3455 info[jss::load_factor_fee_escalation] > 888.88 &&
3456 info[jss::load_factor_fee_escalation] < 888.89);
3457 }
3458 {
3459 auto const server_state = env.rpc("server_state");
3460 auto const& state = server_state[jss::result][jss::state];
3462 state.isMember(jss::load_factor) &&
3463 state[jss::load_factor] == 227555);
3465 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3467 state.isMember(jss::load_factor_server) &&
3468 state[jss::load_factor_server] == 256);
3470 state.isMember(jss::load_factor_fee_escalation) &&
3471 state[jss::load_factor_fee_escalation] == 227555);
3473 state.isMember(jss::load_factor_fee_queue) &&
3474 state[jss::load_factor_fee_queue] == 256);
3476 state.isMember(jss::load_factor_fee_reference) &&
3477 state[jss::load_factor_fee_reference] == 256);
3478 }
3479
3480 env.app().getFeeTrack().setRemoteFee(256000);
3481
3482 {
3483 auto const server_info = env.rpc("server_info");
3485 server_info.isMember(jss::result) &&
3486 server_info[jss::result].isMember(jss::info));
3487 auto const& info = server_info[jss::result][jss::info];
3488 // Avoid double rounding issues by comparing to a range.
3490 info.isMember(jss::load_factor) &&
3491 info[jss::load_factor] == 1000);
3492 BEAST_EXPECT(!info.isMember(jss::load_factor_server));
3493 BEAST_EXPECT(!info.isMember(jss::load_factor_local));
3495 info.isMember(jss::load_factor_net) &&
3496 info[jss::load_factor_net] == 1000);
3498 info.isMember(jss::load_factor_fee_escalation) &&
3499 info[jss::load_factor_fee_escalation] > 888.88 &&
3500 info[jss::load_factor_fee_escalation] < 888.89);
3501 }
3502 {
3503 auto const server_state = env.rpc("server_state");
3504 auto const& state = server_state[jss::result][jss::state];
3506 state.isMember(jss::load_factor) &&
3507 state[jss::load_factor] == 256000);
3509 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3511 state.isMember(jss::load_factor_server) &&
3512 state[jss::load_factor_server] == 256000);
3514 state.isMember(jss::load_factor_fee_escalation) &&
3515 state[jss::load_factor_fee_escalation] == 227555);
3517 state.isMember(jss::load_factor_fee_queue) &&
3518 state[jss::load_factor_fee_queue] == 256);
3520 state.isMember(jss::load_factor_fee_reference) &&
3521 state[jss::load_factor_fee_reference] == 256);
3522 }
3523
3524 env.app().getFeeTrack().setRemoteFee(256);
3525
3526 // Increase the server load
3527 for (int i = 0; i < 5; ++i)
3528 env.app().getFeeTrack().raiseLocalFee();
3529 BEAST_EXPECT(env.app().getFeeTrack().getLoadFactor() == 625);
3530
3531 {
3532 auto const server_info = env.rpc("server_info");
3534 server_info.isMember(jss::result) &&
3535 server_info[jss::result].isMember(jss::info));
3536 auto const& info = server_info[jss::result][jss::info];
3537 // Avoid double rounding issues by comparing to a range.
3539 info.isMember(jss::load_factor) &&
3540 info[jss::load_factor] > 888.88 &&
3541 info[jss::load_factor] < 888.89);
3542 // There can be a race between LoadManager lowering the fee,
3543 // and the call to server_info, so check a wide range.
3544 // The important thing is that it's not 1.
3546 info.isMember(jss::load_factor_server) &&
3547 info[jss::load_factor_server] > 1.245 &&
3548 info[jss::load_factor_server] < 2.4415);
3550 info.isMember(jss::load_factor_local) &&
3551 info[jss::load_factor_local] > 1.245 &&
3552 info[jss::load_factor_local] < 2.4415);
3553 BEAST_EXPECT(!info.isMember(jss::load_factor_net));
3555 info.isMember(jss::load_factor_fee_escalation) &&
3556 info[jss::load_factor_fee_escalation] > 888.88 &&
3557 info[jss::load_factor_fee_escalation] < 888.89);
3558 }
3559 {
3560 auto const server_state = env.rpc("server_state");
3561 auto const& state = server_state[jss::result][jss::state];
3563 state.isMember(jss::load_factor) &&
3564 state[jss::load_factor] == 227555);
3566 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3567 // There can be a race between LoadManager lowering the fee,
3568 // and the call to server_info, so check a wide range.
3569 // The important thing is that it's not 256.
3571 state.isMember(jss::load_factor_server) &&
3572 state[jss::load_factor_server] >= 320 &&
3573 state[jss::load_factor_server] <= 625);
3575 state.isMember(jss::load_factor_fee_escalation) &&
3576 state[jss::load_factor_fee_escalation] == 227555);
3578 state.isMember(jss::load_factor_fee_queue) &&
3579 state[jss::load_factor_fee_queue] == 256);
3581 state.isMember(jss::load_factor_fee_reference) &&
3582 state[jss::load_factor_fee_reference] == 256);
3583 }
3584
3585 env.close();
3586
3587 {
3588 auto const server_info = env.rpc("server_info");
3590 server_info.isMember(jss::result) &&
3591 server_info[jss::result].isMember(jss::info));
3592 auto const& info = server_info[jss::result][jss::info];
3593 // Avoid double rounding issues by comparing to a range.
3594
3595 // There can be a race between LoadManager lowering the fee,
3596 // and the call to server_info, so check a wide range.
3597 // The important thing is that it's not 1.
3599 info.isMember(jss::load_factor) &&
3600 info[jss::load_factor] > 1.245 &&
3601 info[jss::load_factor] < 2.4415);
3602 BEAST_EXPECT(!info.isMember(jss::load_factor_server));
3604 info.isMember(jss::load_factor_local) &&
3605 info[jss::load_factor_local] > 1.245 &&
3606 info[jss::load_factor_local] < 2.4415);
3607 BEAST_EXPECT(!info.isMember(jss::load_factor_net));
3608 BEAST_EXPECT(!info.isMember(jss::load_factor_fee_escalation));
3609 }
3610 {
3611 auto const server_state = env.rpc("server_state");
3612 auto const& state = server_state[jss::result][jss::state];
3614 state.isMember(jss::load_factor) &&
3615 state[jss::load_factor] >= 320 &&
3616 state[jss::load_factor] <= 625);
3618 state.isMember(jss::load_base) && state[jss::load_base] == 256);
3619 // There can be a race between LoadManager lowering the fee,
3620 // and the call to server_info, so check a wide range.
3621 // The important thing is that it's not 256.
3623 state.isMember(jss::load_factor_server) &&
3624 state[jss::load_factor_server] >= 320 &&
3625 state[jss::load_factor_server] <= 625);
3627 state.isMember(jss::load_factor_fee_escalation) &&
3628 state[jss::load_factor_fee_escalation] == 256);
3630 state.isMember(jss::load_factor_fee_queue) &&
3631 state[jss::load_factor_fee_queue] == 256);
3633 state.isMember(jss::load_factor_fee_reference) &&
3634 state[jss::load_factor_fee_reference] == 256);
3635 }
3636 }
3637
3638 void
3640 {
3641 using namespace jtx;
3642 testcase("server subscribe");
3643
3644 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
3645
3646 Json::Value stream;
3647 stream[jss::streams] = Json::arrayValue;
3648 stream[jss::streams].append("server");
3649 auto wsc = makeWSClient(env.app().config());
3650 {
3651 auto jv = wsc->invoke("subscribe", stream);
3652 BEAST_EXPECT(jv[jss::status] == "success");
3653 }
3654
3655 Account a{"a"}, b{"b"}, c{"c"}, d{"d"}, e{"e"}, f{"f"}, g{"g"}, h{"h"},
3656 i{"i"};
3657
3658 // Fund the first few accounts at non escalated fee
3659 env.fund(XRP(50000), noripple(a, b, c, d));
3660 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
3661
3662 // First transaction establishes the messaging
3663 using namespace std::chrono_literals;
3664 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3665 return jv[jss::type] == "serverStatus" &&
3666 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3667 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3668 jv.isMember(jss::load_factor_server) &&
3669 jv[jss::load_factor_server] == 256 &&
3670 jv.isMember(jss::load_factor_fee_escalation) &&
3671 jv[jss::load_factor_fee_escalation] == 256 &&
3672 jv.isMember(jss::load_factor_fee_queue) &&
3673 jv[jss::load_factor_fee_queue] == 256 &&
3674 jv.isMember(jss::load_factor_fee_reference) &&
3675 jv[jss::load_factor_fee_reference] == 256;
3676 }));
3677 // Last transaction escalates the fee
3678 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3679 return jv[jss::type] == "serverStatus" &&
3680 jv.isMember(jss::load_factor) &&
3681 jv[jss::load_factor] == 227555 && jv.isMember(jss::load_base) &&
3682 jv[jss::load_base] == 256 &&
3683 jv.isMember(jss::load_factor_server) &&
3684 jv[jss::load_factor_server] == 256 &&
3685 jv.isMember(jss::load_factor_fee_escalation) &&
3686 jv[jss::load_factor_fee_escalation] == 227555 &&
3687 jv.isMember(jss::load_factor_fee_queue) &&
3688 jv[jss::load_factor_fee_queue] == 256 &&
3689 jv.isMember(jss::load_factor_fee_reference) &&
3690 jv[jss::load_factor_fee_reference] == 256;
3691 }));
3692
3693 env.close();
3694
3695 // Closing ledger should publish a status update
3696 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3697 return jv[jss::type] == "serverStatus" &&
3698 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3699 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3700 jv.isMember(jss::load_factor_server) &&
3701 jv[jss::load_factor_server] == 256 &&
3702 jv.isMember(jss::load_factor_fee_escalation) &&
3703 jv[jss::load_factor_fee_escalation] == 256 &&
3704 jv.isMember(jss::load_factor_fee_queue) &&
3705 jv[jss::load_factor_fee_queue] == 256 &&
3706 jv.isMember(jss::load_factor_fee_reference) &&
3707 jv[jss::load_factor_fee_reference] == 256;
3708 }));
3709
3710 checkMetrics(__LINE__, env, 0, 8, 0, 4, 256);
3711
3712 // Fund then next few accounts at non escalated fee
3713 env.fund(XRP(50000), noripple(e, f, g, h, i));
3714
3715 // Extra transactions with low fee are queued
3716 auto queued = ter(terQUEUED);
3717 env(noop(a), fee(10), queued);
3718 env(noop(b), fee(10), queued);
3719 env(noop(c), fee(10), queued);
3720 env(noop(d), fee(10), queued);
3721 env(noop(e), fee(10), queued);
3722 env(noop(f), fee(10), queued);
3723 env(noop(g), fee(10), queued);
3724 checkMetrics(__LINE__, env, 7, 8, 5, 4, 256);
3725
3726 // Last transaction escalates the fee
3727 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3728 return jv[jss::type] == "serverStatus" &&
3729 jv.isMember(jss::load_factor) &&
3730 jv[jss::load_factor] == 200000 && jv.isMember(jss::load_base) &&
3731 jv[jss::load_base] == 256 &&
3732 jv.isMember(jss::load_factor_server) &&
3733 jv[jss::load_factor_server] == 256 &&
3734 jv.isMember(jss::load_factor_fee_escalation) &&
3735 jv[jss::load_factor_fee_escalation] == 200000 &&
3736 jv.isMember(jss::load_factor_fee_queue) &&
3737 jv[jss::load_factor_fee_queue] == 256 &&
3738 jv.isMember(jss::load_factor_fee_reference) &&
3739 jv[jss::load_factor_fee_reference] == 256;
3740 }));
3741
3742 env.close();
3743 // Ledger close publishes with escalated fees for queued transactions
3744 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3745 return jv[jss::type] == "serverStatus" &&
3746 jv.isMember(jss::load_factor) &&
3747 jv[jss::load_factor] == 184320 && jv.isMember(jss::load_base) &&
3748 jv[jss::load_base] == 256 &&
3749 jv.isMember(jss::load_factor_server) &&
3750 jv[jss::load_factor_server] == 256 &&
3751 jv.isMember(jss::load_factor_fee_escalation) &&
3752 jv[jss::load_factor_fee_escalation] == 184320 &&
3753 jv.isMember(jss::load_factor_fee_queue) &&
3754 jv[jss::load_factor_fee_queue] == 256 &&
3755 jv.isMember(jss::load_factor_fee_reference) &&
3756 jv[jss::load_factor_fee_reference] == 256;
3757 }));
3758
3759 env.close();
3760 // ledger close clears queue so fee is back to normal
3761 BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
3762 return jv[jss::type] == "serverStatus" &&
3763 jv.isMember(jss::load_factor) && jv[jss::load_factor] == 256 &&
3764 jv.isMember(jss::load_base) && jv[jss::load_base] == 256 &&
3765 jv.isMember(jss::load_factor_server) &&
3766 jv[jss::load_factor_server] == 256 &&
3767 jv.isMember(jss::load_factor_fee_escalation) &&
3768 jv[jss::load_factor_fee_escalation] == 256 &&
3769 jv.isMember(jss::load_factor_fee_queue) &&
3770 jv[jss::load_factor_fee_queue] == 256 &&
3771 jv.isMember(jss::load_factor_fee_reference) &&
3772 jv[jss::load_factor_fee_reference] == 256;
3773 }));
3774
3775 BEAST_EXPECT(!wsc->findMsg(1s, [&](auto const& jv) {
3776 return jv[jss::type] == "serverStatus";
3777 }));
3778
3779 auto jv = wsc->invoke("unsubscribe", stream);
3780 BEAST_EXPECT(jv[jss::status] == "success");
3781 }
3782
3783 void
3785 {
3786 using namespace jtx;
3787 testcase("clear queued acct txs");
3788
3789 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
3790 auto alice = Account("alice");
3791 auto bob = Account("bob");
3792
3793 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
3794 env.fund(XRP(50000000), alice, bob);
3795
3796 fillQueue(env, alice);
3797
3798 auto calcTotalFee = [&](std::int64_t alreadyPaid,
3799 std::optional<std::size_t> numToClear =
3800 std::nullopt) -> std::uint64_t {
3801 auto totalFactor = 0;
3802 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
3803 if (!numToClear)
3804 numToClear.emplace(metrics.txCount + 1);
3805 for (int i = 0; i < *numToClear; ++i)
3806 {
3807 auto inLedger = metrics.txInLedger + i;
3808 totalFactor += inLedger * inLedger;
3809 }
3810 auto result = toDrops(
3811 metrics.medFeeLevel * totalFactor /
3812 (metrics.txPerLedger * metrics.txPerLedger),
3813 env.current()->fees().base)
3814 .drops();
3815 // Subtract the fees already paid
3816 result -= alreadyPaid;
3817 // round up
3818 ++result;
3819 return result;
3820 };
3821
3822 testcase("straightfoward positive case");
3823 {
3824 // Queue up some transactions at a too-low fee.
3825 auto aliceSeq = env.seq(alice);
3826 for (int i = 0; i < 2; ++i)
3827 {
3828 env(noop(alice), fee(100), seq(aliceSeq++), ter(terQUEUED));
3829 }
3830
3831 // Queue up a transaction paying the open ledger fee
3832 // This will be the first tx to call the operative function,
3833 // but it won't succeed.
3834 env(noop(alice),
3835 openLedgerFee(env),
3836 seq(aliceSeq++),
3837 ter(terQUEUED));
3838
3839 checkMetrics(__LINE__, env, 3, std::nullopt, 4, 3, 256);
3840
3841 // Figure out how much it would cost to cover all the
3842 // queued txs + itself
3843 std::uint64_t totalFee1 = calcTotalFee(100 * 2 + 8889);
3844 --totalFee1;
3845
3846 BEAST_EXPECT(totalFee1 == 60911);
3847 // Submit a transaction with that fee. It will get queued
3848 // because the fee level calculation rounds down. This is
3849 // the edge case test.
3850 env(noop(alice), fee(totalFee1), seq(aliceSeq++), ter(terQUEUED));
3851
3852 checkMetrics(__LINE__, env, 4, std::nullopt, 4, 3, 256);
3853
3854 // Now repeat the process including the new tx
3855 // and avoiding the rounding error
3856 std::uint64_t const totalFee2 =
3857 calcTotalFee(100 * 2 + 8889 + 60911);
3858 BEAST_EXPECT(totalFee2 == 35556);
3859 // Submit a transaction with that fee. It will succeed.
3860 env(noop(alice), fee(totalFee2), seq(aliceSeq++));
3861
3862 checkMetrics(__LINE__, env, 0, std::nullopt, 9, 3, 256);
3863 }
3864
3865 testcase("replace last tx with enough to clear queue");
3866 {
3867 // Queue up some transactions at a too-low fee.
3868 auto aliceSeq = env.seq(alice);
3869 for (int i = 0; i < 2; ++i)
3870 {
3871 env(noop(alice), fee(100), seq(aliceSeq++), ter(terQUEUED));
3872 }
3873
3874 // Queue up a transaction paying the open ledger fee
3875 // This will be the first tx to call the operative function,
3876 // but it won't succeed.
3877 env(noop(alice),
3878 openLedgerFee(env),
3879 seq(aliceSeq++),
3880 ter(terQUEUED));
3881
3882 checkMetrics(__LINE__, env, 3, std::nullopt, 9, 3, 256);
3883
3884 // Figure out how much it would cost to cover all the
3885 // queued txs + itself
3886 auto const metrics = env.app().getTxQ().getMetrics(*env.current());
3887 std::uint64_t const totalFee =
3888 calcTotalFee(100 * 2, metrics.txCount);
3889 BEAST_EXPECT(totalFee == 167578);
3890 // Replacing the last tx with the large fee succeeds.
3891 --aliceSeq;
3892 env(noop(alice), fee(totalFee), seq(aliceSeq++));
3893
3894 // The queue is clear
3895 checkMetrics(__LINE__, env, 0, std::nullopt, 12, 3, 256);
3896
3897 env.close();
3898 checkMetrics(__LINE__, env, 0, 24, 0, 12, 256);
3899 }
3900
3901 testcase("replace middle tx with enough to clear queue");
3902 {
3903 fillQueue(env, alice);
3904 // Queue up some transactions at a too-low fee.
3905 auto aliceSeq = env.seq(alice);
3906 for (int i = 0; i < 5; ++i)
3907 {
3908 env(noop(alice), fee(100), seq(aliceSeq++), ter(terQUEUED));
3909 }
3910
3911 checkMetrics(__LINE__, env, 5, 24, 13, 12, 256);
3912
3913 // Figure out how much it would cost to cover 3 txns
3914 std::uint64_t const totalFee = calcTotalFee(100 * 2, 3);
3915 BEAST_EXPECT(totalFee == 20287);
3916 // Replacing the last tx with the large fee succeeds.
3917 aliceSeq -= 3;
3918 env(noop(alice), fee(totalFee), seq(aliceSeq++));
3919
3920 checkMetrics(__LINE__, env, 2, 24, 16, 12, 256);
3921 auto const aliceQueue =
3922 env.app().getTxQ().getAccountTxs(alice.id());
3923 BEAST_EXPECT(aliceQueue.size() == 2);
3924 SeqProxy seq = SeqProxy::sequence(aliceSeq);
3925 for (auto const& tx : aliceQueue)
3926 {
3927 BEAST_EXPECT(tx.seqProxy == seq);
3928 BEAST_EXPECT(tx.feeLevel == FeeLevel64{2560});
3929 seq.advanceBy(1);
3930 }
3931
3932 // Close the ledger to clear the queue
3933 env.close();
3934 checkMetrics(__LINE__, env, 0, 32, 2, 16, 256);
3935 }
3936
3937 testcase("clear queue failure (load)");
3938 {
3939 fillQueue(env, alice);
3940 // Queue up some transactions at a too-low fee.
3941 auto aliceSeq = env.seq(alice);
3942 for (int i = 0; i < 2; ++i)
3943 {
3944 env(noop(alice), fee(200), seq(aliceSeq++), ter(terQUEUED));
3945 }
3946 for (int i = 0; i < 2; ++i)
3947 {
3948 env(noop(alice), fee(22), seq(aliceSeq++), ter(terQUEUED));
3949 }
3950
3951 checkMetrics(__LINE__, env, 4, 32, 17, 16, 256);
3952
3953 // Figure out how much it would cost to cover all the txns
3954 // + 1
3955 std::uint64_t const totalFee = calcTotalFee(200 * 2 + 22 * 2);
3956 BEAST_EXPECT(totalFee == 35006);
3957 // This fee should be enough, but oh no! Server load went up!
3958 auto& feeTrack = env.app().getFeeTrack();
3959 auto const origFee = feeTrack.getRemoteFee();
3960 feeTrack.setRemoteFee(origFee * 5);
3961 // Instead the tx gets queued, and all of the queued
3962 // txs stay in the queue.
3963 env(noop(alice), fee(totalFee), seq(aliceSeq++), ter(terQUEUED));
3964
3965 // The original last transaction is still in the queue
3966 checkMetrics(__LINE__, env, 5, 32, 17, 16, 256);
3967
3968 // With high load, some of the txs stay in the queue
3969 env.close();
3970 checkMetrics(__LINE__, env, 3, 34, 2, 17, 256);
3971
3972 // Load drops back down
3973 feeTrack.setRemoteFee(origFee);
3974
3975 // Because of the earlier failure, alice can not clear the queue,
3976 // no matter how high the fee
3977 fillQueue(env, bob);
3978 checkMetrics(__LINE__, env, 3, 34, 18, 17, 256);
3979
3980 env(noop(alice), fee(XRP(1)), seq(aliceSeq++), ter(terQUEUED));
3981 checkMetrics(__LINE__, env, 4, 34, 18, 17, 256);
3982
3983 // With normal load, those txs get into the ledger
3984 env.close();
3985 checkMetrics(__LINE__, env, 0, 36, 4, 18, 256);
3986 }
3987 }
3988
3989 void
3991 {
3992 using namespace jtx;
3993 using namespace std::chrono_literals;
3994 testcase("scaling");
3995
3996 {
3997 Env env(
3998 *this,
3999 makeConfig(
4000 {{"minimum_txn_in_ledger_standalone", "3"},
4001 {"normal_consensus_increase_percent", "25"},
4002 {"slow_consensus_decrease_percent", "50"},
4003 {"target_txn_in_ledger", "10"},
4004 {"maximum_txn_per_account", "200"}}));
4005 auto alice = Account("alice");
4006
4007 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
4008 env.fund(XRP(50000000), alice);
4009
4010 fillQueue(env, alice);
4011 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
4012 auto seqAlice = env.seq(alice);
4013 auto txCount = 140;
4014 for (int i = 0; i < txCount; ++i)
4015 env(noop(alice), seq(seqAlice++), ter(terQUEUED));
4016 checkMetrics(__LINE__, env, txCount, std::nullopt, 4, 3, 256);
4017
4018 // Close a few ledgers successfully, so the limit grows
4019
4020 env.close();
4021 // 4 + 25% = 5
4022 txCount -= 6;
4023 checkMetrics(__LINE__, env, txCount, 10, 6, 5, 257);
4024
4025 env.close();
4026 // 6 + 25% = 7
4027 txCount -= 8;
4028 checkMetrics(__LINE__, env, txCount, 14, 8, 7, 257);
4029
4030 env.close();
4031 // 8 + 25% = 10
4032 txCount -= 11;
4033 checkMetrics(__LINE__, env, txCount, 20, 11, 10, 257);
4034
4035 env.close();
4036 // 11 + 25% = 13
4037 txCount -= 14;
4038 checkMetrics(__LINE__, env, txCount, 26, 14, 13, 257);
4039
4040 env.close();
4041 // 14 + 25% = 17
4042 txCount -= 18;
4043 checkMetrics(__LINE__, env, txCount, 34, 18, 17, 257);
4044
4045 env.close();
4046 // 18 + 25% = 22
4047 txCount -= 23;
4048 checkMetrics(__LINE__, env, txCount, 44, 23, 22, 257);
4049
4050 env.close();
4051 // 23 + 25% = 28
4052 txCount -= 29;
4053 checkMetrics(__LINE__, env, txCount, 56, 29, 28, 256);
4054
4055 // From 3 expected to 28 in 7 "fast" ledgers.
4056
4057 // Close the ledger with a delay.
4058 env.close(env.now() + 5s, 10000ms);
4059 txCount -= 15;
4060 checkMetrics(__LINE__, env, txCount, 56, 15, 14, 256);
4061
4062 // Close the ledger with a delay.
4063 env.close(env.now() + 5s, 10000ms);
4064 txCount -= 8;
4065 checkMetrics(__LINE__, env, txCount, 56, 8, 7, 256);
4066
4067 // Close the ledger with a delay.
4068 env.close(env.now() + 5s, 10000ms);
4069 txCount -= 4;
4070 checkMetrics(__LINE__, env, txCount, 56, 4, 3, 256);
4071
4072 // From 28 expected back down to 3 in 3 "slow" ledgers.
4073
4074 // Confirm the minimum sticks
4075 env.close(env.now() + 5s, 10000ms);
4076 txCount -= 4;
4077 checkMetrics(__LINE__, env, txCount, 56, 4, 3, 256);
4078
4079 BEAST_EXPECT(!txCount);
4080 }
4081
4082 {
4083 Env env(
4084 *this,
4085 makeConfig(
4086 {{"minimum_txn_in_ledger_standalone", "3"},
4087 {"normal_consensus_increase_percent", "150"},
4088 {"slow_consensus_decrease_percent", "150"},
4089 {"target_txn_in_ledger", "10"},
4090 {"maximum_txn_per_account", "200"}}));
4091 auto alice = Account("alice");
4092
4093 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
4094 env.fund(XRP(50000000), alice);
4095
4096 fillQueue(env, alice);
4097 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
4098 auto seqAlice = env.seq(alice);
4099 auto txCount = 43;
4100 for (int i = 0; i < txCount; ++i)
4101 env(noop(alice), seq(seqAlice++), ter(terQUEUED));
4102 checkMetrics(__LINE__, env, txCount, std::nullopt, 4, 3, 256);
4103
4104 // Close a few ledgers successfully, so the limit grows
4105
4106 env.close();
4107 // 4 + 150% = 10
4108 txCount -= 11;
4109 checkMetrics(__LINE__, env, txCount, 20, 11, 10, 257);
4110
4111 env.close();
4112 // 11 + 150% = 27
4113 txCount -= 28;
4114 checkMetrics(__LINE__, env, txCount, 54, 28, 27, 256);
4115
4116 // From 3 expected to 28 in 7 "fast" ledgers.
4117
4118 // Close the ledger with a delay.
4119 env.close(env.now() + 5s, 10000ms);
4120 txCount -= 4;
4121 checkMetrics(__LINE__, env, txCount, 54, 4, 3, 256);
4122
4123 // From 28 expected back down to 3 in 3 "slow" ledgers.
4124
4125 BEAST_EXPECT(!txCount);
4126 }
4127 }
4128
4129 void
4131 {
4132 // Test the situation where a transaction with an account and
4133 // sequence that's in the queue also appears in the ledger.
4134 //
4135 // Normally this situation can only happen on a network
4136 // when a transaction gets validated by most of the network,
4137 // but one or more nodes have that transaction (or a different
4138 // transaction with the same sequence) queued. And, yes, this
4139 // situation has been observed (rarely) in the wild.
4140 testcase("Sequence in queue and open ledger");
4141 using namespace jtx;
4142
4143 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
4144
4145 auto const alice = Account("alice");
4146
4147 auto const queued = ter(terQUEUED);
4148
4149 BEAST_EXPECT(env.current()->fees().base == 10);
4150
4151 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
4152
4153 // Create account
4154 env.fund(XRP(50000), noripple(alice));
4155 checkMetrics(__LINE__, env, 0, std::nullopt, 1, 3, 256);
4156
4157 fillQueue(env, alice);
4158 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
4159
4160 // Queue a transaction
4161 auto const aliceSeq = env.seq(alice);
4162 env(noop(alice), queued);
4163 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 3, 256);
4164
4165 // Now, apply a (different) transaction directly
4166 // to the open ledger, bypassing the queue
4167 // (This requires calling directly into the open ledger,
4168 // which won't work if unit tests are separated to only
4169 // be callable via RPC.)
4170 env.app().openLedger().modify([&](OpenView& view, beast::Journal j) {
4171 auto const tx =
4172 env.jt(noop(alice), seq(aliceSeq), openLedgerFee(env));
4173 auto const result =
4174 ripple::apply(env.app(), view, *tx.stx, tapUNLIMITED, j);
4175 BEAST_EXPECT(result.ter == tesSUCCESS && result.applied);
4176 return result.applied;
4177 });
4178 // the queued transaction is still there
4179 checkMetrics(__LINE__, env, 1, std::nullopt, 5, 3, 256);
4180
4181 // The next transaction should be able to go into the open
4182 // ledger, even though aliceSeq is queued. In earlier incarnations
4183 // of the TxQ this would cause an assert.
4184 env(noop(alice), seq(aliceSeq + 1), openLedgerFee(env));
4185 checkMetrics(__LINE__, env, 1, std::nullopt, 6, 3, 256);
4186 // Now queue a couple more transactions to make sure
4187 // they succeed despite aliceSeq being queued
4188 env(noop(alice), seq(aliceSeq + 2), queued);
4189 env(noop(alice), seq(aliceSeq + 3), queued);
4190 checkMetrics(__LINE__, env, 3, std::nullopt, 6, 3, 256);
4191
4192 // Now close the ledger. One of the queued transactions
4193 // (aliceSeq) should be dropped.
4194 env.close();
4195 checkMetrics(__LINE__, env, 0, 12, 2, 6, 256);
4196 }
4197
4198 void
4200 {
4201 // Test the situation where a transaction with an account and
4202 // ticket that's in the queue also appears in the ledger.
4203 //
4204 // Although this situation has not (yet) been observed in the wild,
4205 // it is a direct analogy to the previous sequence based test. So
4206 // there is no reason to not expect to see it in the wild.
4207 testcase("Ticket in queue and open ledger");
4208 using namespace jtx;
4209
4210 Env env(*this, makeConfig({{"minimum_txn_in_ledger_standalone", "3"}}));
4211
4212 auto alice = Account("alice");
4213
4214 auto queued = ter(terQUEUED);
4215
4216 BEAST_EXPECT(env.current()->fees().base == 10);
4217
4218 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
4219
4220 // Create account
4221 env.fund(XRP(50000), noripple(alice));
4222 checkMetrics(__LINE__, env, 0, std::nullopt, 1, 3, 256);
4223
4224 // Create tickets
4225 std::uint32_t const tktSeq0{env.seq(alice) + 1};
4226 env(ticket::create(alice, 4));
4227
4228 // Fill the queue so the next transaction will be queued.
4229 fillQueue(env, alice);
4230 checkMetrics(__LINE__, env, 0, std::nullopt, 4, 3, 256);
4231
4232 // Queue a transaction with a ticket. Leave an unused ticket
4233 // on either side.
4234 env(noop(alice), ticket::use(tktSeq0 + 1), queued);
4235 checkMetrics(__LINE__, env, 1, std::nullopt, 4, 3, 256);
4236
4237 // Now, apply a (different) transaction directly
4238 // to the open ledger, bypassing the queue
4239 // (This requires calling directly into the open ledger,
4240 // which won't work if unit tests are separated to only
4241 // be callable via RPC.)
4242 env.app().openLedger().modify([&](OpenView& view, beast::Journal j) {
4243 auto const tx = env.jt(
4244 noop(alice), ticket::use(tktSeq0 + 1), openLedgerFee(env));
4245 auto const result =
4246 ripple::apply(env.app(), view, *tx.stx, tapUNLIMITED, j);
4247 BEAST_EXPECT(result.ter == tesSUCCESS && result.applied);
4248 return result.applied;
4249 });
4250 // the queued transaction is still there
4251 checkMetrics(__LINE__, env, 1, std::nullopt, 5, 3, 256);
4252
4253 // The next (sequence-based) transaction should be able to go into
4254 // the open ledger, even though tktSeq0 is queued. Note that this
4255 // sequence-based transaction goes in front of the queued
4256 // transaction, so the queued transaction is left in the queue.
4257 env(noop(alice), openLedgerFee(env));
4258 checkMetrics(__LINE__, env, 1, std::nullopt, 6, 3, 256);
4259
4260 // We should be able to do the same thing with a ticket that goes
4261 // if front of the queued transaction. This one too will leave
4262 // the queued transaction in place.
4263 env(noop(alice), ticket::use(tktSeq0 + 0), openLedgerFee(env));
4264 checkMetrics(__LINE__, env, 1, std::nullopt, 7, 3, 256);
4265
4266 // We have one ticketed transaction in the queue. We should able
4267 // to add another to the queue.
4268 env(noop(alice), ticket::use(tktSeq0 + 2), queued);
4269 checkMetrics(__LINE__, env, 2, std::nullopt, 7, 3, 256);
4270
4271 // Here we try to force the queued transactions into the ledger by
4272 // adding one more queued (ticketed) transaction that pays enough
4273 // so fee averaging kicks in. It doesn't work. It only succeeds in
4274 // forcing just the one ticketed transaction into the ledger.
4275 //
4276 // The fee averaging functionality makes sense for sequence-based
4277 // transactions because if there are several sequence-based
4278 // transactions queued, the transactions in front must go into the
4279 // ledger before the later ones can go in.
4280 //
4281 // Fee averaging does not make sense with tickets. Every ticketed
4282 // transaction is equally capable of going into the ledger independent
4283 // of all other ticket- or sequence-based transactions.
4284 env(noop(alice), ticket::use(tktSeq0 + 3), fee(XRP(1)));
4285 checkMetrics(__LINE__, env, 2, std::nullopt, 8, 3, 256);
4286
4287 // Now close the ledger. One of the queued transactions
4288 // (the one with tktSeq0 + 1) should be dropped.
4289 env.close();
4290 checkMetrics(__LINE__, env, 0, 16, 1, 8, 256);
4291 }
4292
4293 void
4295 {
4296 // The TxQ caches preflight results. But there are situations where
4297 // that cache must become invalidated, like if amendments change.
4298 //
4299 // This test puts transactions into the TxQ and then enables an
4300 // amendment. We won't really see much interesting here in the unit
4301 // test, but the code that checks for cache invalidation should be
4302 // exercised. You can see that in improved code coverage,
4303 testcase("Re-execute preflight");
4304 using namespace jtx;
4305
4306 Account const alice("alice");
4307 Account const bob("bob");
4308 Account const carol("carol");
4309 Account const daria("daria");
4310 Account const ellie("ellie");
4311 Account const fiona("fiona");
4312
4313 auto cfg = makeConfig(
4314 {{"minimum_txn_in_ledger_standalone", "1"},
4315 {"ledgers_in_queue", "5"},
4316 {"maximum_txn_per_account", "10"}},
4317 {{"account_reserve", "1000"}, {"owner_reserve", "50"}});
4318
4319 Env env(*this, std::move(cfg));
4320
4321 env.fund(XRP(10000), alice);
4322 env.close();
4323 env.fund(XRP(10000), bob);
4324 env.close();
4325 env.fund(XRP(10000), carol);
4326 env.close();
4327 env.fund(XRP(10000), daria);
4328 env.close();
4329 env.fund(XRP(10000), ellie);
4330 env.close();
4331 env.fund(XRP(10000), fiona);
4332 env.close();
4333 checkMetrics(__LINE__, env, 0, 10, 0, 2, 256);
4334
4335 // Close ledgers until the amendments show up.
4336 int i = 0;
4337 for (i = 0; i <= 257; ++i)
4338 {
4339 env.close();
4340 if (!getMajorityAmendments(*env.closed()).empty())
4341 break;
4342 }
4343 auto expectedPerLedger = ripple::detail::numUpVotedAmendments() + 1;
4345 __LINE__, env, 0, 5 * expectedPerLedger, 0, expectedPerLedger, 256);
4346
4347 // Now wait 2 weeks modulo 256 ledgers for the amendments to be
4348 // enabled. Speed the process by closing ledgers every 80 minutes,
4349 // which should get us to just past 2 weeks after 256 ledgers.
4350 using namespace std::chrono_literals;
4351 auto closeDuration = 80min;
4352 for (i = 0; i <= 255; ++i)
4353 env.close(closeDuration);
4354
4355 // We're very close to the flag ledger. Fill the ledger.
4356 fillQueue(env, alice);
4358 __LINE__,
4359 env,
4360 0,
4361 5 * expectedPerLedger,
4362 expectedPerLedger + 1,
4363 expectedPerLedger,
4364 256);
4365
4366 // Fill everyone's queues.
4367 auto seqAlice = env.seq(alice);
4368 auto seqBob = env.seq(bob);
4369 auto seqCarol = env.seq(carol);
4370 auto seqDaria = env.seq(daria);
4371 auto seqEllie = env.seq(ellie);
4372 auto seqFiona = env.seq(fiona);
4373 // Use fees to guarantee order
4374 int txFee{90};
4375 for (int i = 0; i < 10; ++i)
4376 {
4377 env(noop(alice), seq(seqAlice++), fee(--txFee), ter(terQUEUED));
4378 env(noop(bob), seq(seqBob++), fee(--txFee), ter(terQUEUED));
4379 env(noop(carol), seq(seqCarol++), fee(--txFee), ter(terQUEUED));
4380 env(noop(daria), seq(seqDaria++), fee(--txFee), ter(terQUEUED));
4381 env(noop(ellie), seq(seqEllie++), fee(--txFee), ter(terQUEUED));
4382 env(noop(fiona), seq(seqFiona++), fee(--txFee), ter(terQUEUED));
4383 }
4384 std::size_t expectedInQueue = 60;
4386 __LINE__,
4387 env,
4388 expectedInQueue,
4389 5 * expectedPerLedger,
4390 expectedPerLedger + 1,
4391 expectedPerLedger,
4392 256);
4393
4394 // The next close should cause the in-ledger amendments to change.
4395 // Alice's queued transactions have a cached PreflightResult
4396 // that resulted from running against the Rules in the previous
4397 // ledger. Since the amendments change in this newest ledger
4398 // The TxQ must re-run preflight using the new rules.
4399 //
4400 // These particular amendments don't impact any of the queued
4401 // transactions, so we won't see any change in the transaction
4402 // outcomes. But code coverage is affected.
4403 do
4404 {
4405 env.close(closeDuration);
4406 auto expectedInLedger = expectedInQueue;
4407 expectedInQueue =
4408 (expectedInQueue > expectedPerLedger + 2
4409 ? expectedInQueue - (expectedPerLedger + 2)
4410 : 0);
4411 expectedInLedger -= expectedInQueue;
4412 ++expectedPerLedger;
4414 __LINE__,
4415 env,
4416 expectedInQueue,
4417 5 * expectedPerLedger,
4418 expectedInLedger,
4419 expectedPerLedger,
4420 256);
4421 {
4422 auto const expectedPerAccount = expectedInQueue / 6;
4423 auto const expectedRemainder = expectedInQueue % 6;
4424 BEAST_EXPECT(env.seq(alice) == seqAlice - expectedPerAccount);
4426 env.seq(bob) ==
4427 seqBob - expectedPerAccount -
4428 (expectedRemainder > 4 ? 1 : 0));
4430 env.seq(carol) ==
4431 seqCarol - expectedPerAccount -
4432 (expectedRemainder > 3 ? 1 : 0));
4434 env.seq(daria) ==
4435 seqDaria - expectedPerAccount -
4436 (expectedRemainder > 2 ? 1 : 0));
4438 env.seq(ellie) ==
4439 seqEllie - expectedPerAccount -
4440 (expectedRemainder > 1 ? 1 : 0));
4442 env.seq(fiona) ==
4443 seqFiona - expectedPerAccount -
4444 (expectedRemainder > 0 ? 1 : 0));
4445 }
4446 } while (expectedInQueue > 0);
4447 }
4448
4449 void
4451 {
4452 // If...
4453 // o The queue is close to full,
4454 // o An account has multiple txs queued, and
4455 // o That same account has a transaction fail
4456 // Then drop the last transaction for the account if possible.
4457 //
4458 // Verify that happens.
4459 testcase("Queue full drop penalty");
4460 using namespace jtx;
4461
4462 // Because we're looking at a phenomenon that occurs when the TxQ
4463 // is at 95% capacity or greater, we need to have lots of entries
4464 // in the queue. You can't even see 95% capacity unless there are
4465 // 20 entries in the queue.
4466 Account const alice("alice");
4467 Account const bob("bob");
4468 Account const carol("carol");
4469 Account const daria("daria");
4470 Account const ellie("ellie");
4471 Account const fiona("fiona");
4472
4473 // We'll be using fees to control which entries leave the queue in
4474 // which order. There's no "lowFee" -- that's the default fee from
4475 // the unit test.
4476 int const medFee = 100;
4477 int const hiFee = 1000;
4478
4479 auto cfg = makeConfig(
4480 {{"minimum_txn_in_ledger_standalone", "5"},
4481 {"ledgers_in_queue", "5"},
4482 {"maximum_txn_per_account", "30"},
4483 {"minimum_queue_size", "50"}});
4484
4485 Env env(*this, std::move(cfg));
4486
4487 // The noripple is to reduce the number of transactions required to
4488 // fund the accounts. There is no rippling in this test.
4489 env.fund(XRP(10000), noripple(alice, bob, carol, daria, ellie, fiona));
4490 env.close();
4491
4492 // Get bob some tickets.
4493 std::uint32_t const bobTicketSeq = env.seq(bob) + 1;
4494 env(ticket::create(bob, 10));
4495 env.close();
4496
4497 // Get the dropPenalty flag set on alice and bob by having one
4498 // of their transactions expire out of the queue. To start out
4499 // alice fills the ledger.
4500 fillQueue(env, alice);
4501 checkMetrics(__LINE__, env, 0, 50, 7, 6, 256);
4502
4503 // Now put a few transactions into alice's queue, including one that
4504 // will expire out soon.
4505 auto seqAlice = env.seq(alice);
4506 auto const seqSaveAlice = seqAlice;
4507 int feeDrops = 40;
4508 env(noop(alice),
4509 seq(seqAlice++),
4510 fee(--feeDrops),
4511 json(R"({"LastLedgerSequence": 7})"),
4512 ter(terQUEUED));
4513 env(noop(alice), seq(seqAlice++), fee(--feeDrops), ter(terQUEUED));
4514 env(noop(alice), seq(seqAlice++), fee(--feeDrops), ter(terQUEUED));
4515 BEAST_EXPECT(env.seq(alice) == seqSaveAlice);
4516
4517 // Similarly for bob, but bob uses tickets in his transactions.
4518 // The drop penalty works a little differently with tickets.
4519 env(noop(bob),
4520 ticket::use(bobTicketSeq + 0),
4521 json(R"({"LastLedgerSequence": 7})"),
4522 ter(terQUEUED));
4523 env(noop(bob),
4524 ticket::use(bobTicketSeq + 1),
4525 fee(--feeDrops),
4526 ter(terQUEUED));
4527 env(noop(bob),
4528 ticket::use(bobTicketSeq + 2),
4529 fee(--feeDrops),
4530 ter(terQUEUED));
4531
4532 // Fill the queue with higher fee transactions so alice's and
4533 // bob's transactions are stuck in the queue.
4534 auto seqCarol = env.seq(carol);
4535 auto seqDaria = env.seq(daria);
4536 auto seqEllie = env.seq(ellie);
4537 auto seqFiona = env.seq(fiona);
4538 feeDrops = medFee;
4539 for (int i = 0; i < 7; ++i)
4540 {
4541 env(noop(carol), seq(seqCarol++), fee(--feeDrops), ter(terQUEUED));
4542 env(noop(daria), seq(seqDaria++), fee(--feeDrops), ter(terQUEUED));
4543 env(noop(ellie), seq(seqEllie++), fee(--feeDrops), ter(terQUEUED));
4544 env(noop(fiona), seq(seqFiona++), fee(--feeDrops), ter(terQUEUED));
4545 }
4546
4547 checkMetrics(__LINE__, env, 34, 50, 7, 6, 256);
4548 env.close();
4549 checkMetrics(__LINE__, env, 26, 50, 8, 7, 256);
4550
4551 // Re-fill the queue so alice and bob stay stuck.
4552 feeDrops = medFee;
4553 for (int i = 0; i < 3; ++i)
4554 {
4555 env(noop(carol), seq(seqCarol++), fee(--feeDrops), ter(terQUEUED));
4556 env(noop(daria), seq(seqDaria++), fee(--feeDrops), ter(terQUEUED));
4557 env(noop(ellie), seq(seqEllie++), fee(--feeDrops), ter(terQUEUED));
4558 env(noop(fiona), seq(seqFiona++), fee(--feeDrops), ter(terQUEUED));
4559 }
4560 checkMetrics(__LINE__, env, 38, 50, 8, 7, 256);
4561 env.close();
4562 checkMetrics(__LINE__, env, 29, 50, 9, 8, 256);
4563
4564 // One more time...
4565 feeDrops = medFee;
4566 for (int i = 0; i < 3; ++i)
4567 {
4568 env(noop(carol), seq(seqCarol++), fee(--feeDrops), ter(terQUEUED));
4569 env(noop(daria), seq(seqDaria++), fee(--feeDrops), ter(terQUEUED));
4570 env(noop(ellie), seq(seqEllie++), fee(--feeDrops), ter(terQUEUED));
4571 env(noop(fiona), seq(seqFiona++), fee(--feeDrops), ter(terQUEUED));
4572 }
4573 checkMetrics(__LINE__, env, 41, 50, 9, 8, 256);
4574 env.close();
4575 checkMetrics(__LINE__, env, 29, 50, 10, 9, 256);
4576
4577 // Finally the stage is set. alice's and bob's transactions expired
4578 // out of the queue which caused the dropPenalty flag to be set on
4579 // their accounts.
4580 //
4581 // This also means that alice has a sequence gap in her transactions,
4582 // and thus can't queue any more.
4583 env(noop(alice), seq(seqAlice), fee(hiFee), ter(telCAN_NOT_QUEUE));
4584
4585 // Once again, fill the queue almost to the brim.
4586 feeDrops = medFee;
4587 for (int i = 0; i < 4; ++i)
4588 {
4589 env(noop(carol), seq(seqCarol++), fee(--feeDrops), ter(terQUEUED));
4590 env(noop(daria), seq(seqDaria++), fee(--feeDrops), ter(terQUEUED));
4591 env(noop(ellie), seq(seqEllie++), fee(--feeDrops), ter(terQUEUED));
4592 env(noop(fiona), seq(seqFiona++), fee(--feeDrops), ter(terQUEUED));
4593 }
4594 env(noop(carol), seq(seqCarol++), fee(--feeDrops), ter(terQUEUED));
4595 env(noop(daria), seq(seqDaria++), fee(--feeDrops), ter(terQUEUED));
4596 env(noop(ellie), seq(seqEllie++), fee(--feeDrops), ter(terQUEUED));
4597 checkMetrics(__LINE__, env, 48, 50, 10, 9, 256);
4598
4599 // Now induce a fee jump which should cause all the transactions
4600 // in the queue to fail with telINSUF_FEE_P.
4601 //
4602 // *NOTE* raiseLocalFee() is tricky to use since the local fee is
4603 // asynchronously lowered by LoadManager. Here we're just
4604 // pushing the local fee up really high and then hoping that we
4605 // outrace LoadManager undoing our work.
4606 for (int i = 0; i < 30; ++i)
4607 env.app().getFeeTrack().raiseLocalFee();
4608
4609 // Now close the ledger, which will attempt to process alice's
4610 // and bob's queued transactions.
4611 // o The _last_ transaction should be dropped from alice's queue.
4612 // o The first failing transaction should be dropped from bob's queue.
4613 env.close();
4614 checkMetrics(__LINE__, env, 46, 50, 0, 10, 256);
4615
4616 // Run the local fee back down.
4617 while (env.app().getFeeTrack().lowerLocalFee())
4618 ;
4619
4620 // bob fills the ledger so it's easier to probe the TxQ.
4621 fillQueue(env, bob);
4622 checkMetrics(__LINE__, env, 46, 50, 11, 10, 256);
4623
4624 // Before the close() alice had two transactions in her queue.
4625 // We now expect her to have one. Here's the state of alice's queue.
4626 //
4627 // 0. The transaction that used to be first in her queue expired
4628 // out two env.close() calls back. That left a gap in alice's
4629 // queue which has not been filled yet.
4630 //
4631 // 1. The first transaction in the queue failed to apply because
4632 // of the sequence gap. But it is retained in the queue.
4633 //
4634 // 2. The last (second) transaction in alice's queue was removed
4635 // as "punishment"...
4636 // a) For already having a transaction expire out of her queue, and
4637 // b) For just now having a queued transaction fail on apply()
4638 // because of the sequence gap.
4639 //
4640 // Verify that none of alice's queued transactions actually applied to
4641 // her account.
4642 BEAST_EXPECT(env.seq(alice) == seqSaveAlice);
4643 seqAlice = seqSaveAlice;
4644
4645 // Verify that there's a gap at the front of alice's queue by
4646 // queuing another low fee transaction into that spot.
4647 env(noop(alice), seq(seqAlice++), fee(11), ter(terQUEUED));
4648
4649 // Verify that the first entry in alice's queue is still there
4650 // by trying to replace it and having that fail.
4651 env(noop(alice), seq(seqAlice++), ter(telCAN_NOT_QUEUE_FEE));
4652
4653 // Verify that the last transaction in alice's queue was removed by
4654 // appending to her queue with a very low fee.
4655 env(noop(alice), seq(seqAlice++), ter(terQUEUED));
4656
4657 // Before the close() bob had two transactions in his queue.
4658 // We now expect him to have one. Here's the state of bob's queue.
4659 //
4660 // 0. The transaction that used to be first in his queue expired out
4661 // two env.close() calls back. That is how the dropPenalty flag
4662 // got set on bob's queue.
4663 //
4664 // 1. Since bob's remaining transactions all have the same fee, the
4665 // TxQ attempted to apply bob's second transaction to the ledger,
4666 // but the fee was too low. So the TxQ threw that transaction
4667 // (not bob's last transaction) out of the queue.
4668 //
4669 // 2. The last of bob's transactions remains in the TxQ.
4670
4671 // Verify that bob's first transaction was removed from the queue
4672 // by queueing another low fee transaction into that spot.
4673 env(noop(bob), ticket::use(bobTicketSeq + 0), fee(12), ter(terQUEUED));
4674
4675 // Verify that bob's second transaction was removed from the queue
4676 // by queueing another low fee transaction into that spot.
4677 env(noop(bob), ticket::use(bobTicketSeq + 1), fee(11), ter(terQUEUED));
4678
4679 // Verify that the last entry in bob's queue is still there
4680 // by trying to replace it and having that fail.
4681 env(noop(bob),
4682 ticket::use(bobTicketSeq + 2),
4684 }
4685
4686 void
4688 {
4689 testcase("Cancel queued offers");
4690 using namespace jtx;
4691
4692 Account const alice("alice");
4693 auto gw = Account("gw");
4694 auto USD = gw["USD"];
4695
4696 auto cfg = makeConfig(
4697 {{"minimum_txn_in_ledger_standalone", "5"},
4698 {"ledgers_in_queue", "5"},
4699 {"maximum_txn_per_account", "30"},
4700 {"minimum_queue_size", "50"}});
4701
4702 Env env(*this, std::move(cfg));
4703
4704 // The noripple is to reduce the number of transactions required to
4705 // fund the accounts. There is no rippling in this test.
4706 env.fund(XRP(100000), noripple(alice));
4707 env.close();
4708
4709 {
4710 // ------- Sequence-based transactions -------
4711 fillQueue(env, alice);
4712
4713 // Alice creates a couple offers
4714 auto const aliceSeq = env.seq(alice);
4715 env(offer(alice, USD(1000), XRP(1000)), ter(terQUEUED));
4716
4717 env(offer(alice, USD(1000), XRP(1001)),
4718 seq(aliceSeq + 1),
4719 ter(terQUEUED));
4720
4721 // Alice creates transactions that cancel the first set of
4722 // offers, one through another offer, and one cancel
4723 env(offer(alice, USD(1000), XRP(1002)),
4724 seq(aliceSeq + 2),
4725 json(jss::OfferSequence, aliceSeq),
4726 ter(terQUEUED));
4727
4728 env(offer_cancel(alice, aliceSeq + 1),
4729 seq(aliceSeq + 3),
4730 ter(terQUEUED));
4731
4732 env.close();
4733
4734 checkMetrics(__LINE__, env, 0, 50, 4, 6, 256);
4735 }
4736
4737 {
4738 // ------- Ticket-based transactions -------
4739
4740 // Alice creates some tickets
4741 auto const aliceTkt = env.seq(alice);
4742 env(ticket::create(alice, 6));
4743 env.close();
4744
4745 fillQueue(env, alice);
4746
4747 // Alice creates a couple offers using tickets, consuming the
4748 // tickets in reverse order
4749 auto const aliceSeq = env.seq(alice);
4750 env(offer(alice, USD(1000), XRP(1000)),
4751 ticket::use(aliceTkt + 4),
4752 ter(terQUEUED));
4753
4754 env(offer(alice, USD(1000), XRP(1001)),
4755 ticket::use(aliceTkt + 3),
4756 ter(terQUEUED));
4757
4758 // Alice creates a couple more transactions that cancel the first
4759 // set of offers, also in reverse order. This allows Alice to submit
4760 // a tx with a lower ticket value than the offer it's cancelling.
4761 // These transactions succeed because Ticket ordering is arbitrary
4762 // and it's up to the user to ensure they don't step on their own
4763 // feet.
4764 env(offer(alice, USD(1000), XRP(1002)),
4765 ticket::use(aliceTkt + 2),
4766 json(jss::OfferSequence, aliceTkt + 4),
4767 ter(terQUEUED));
4768
4769 env(offer_cancel(alice, aliceTkt + 3),
4770 ticket::use(aliceTkt + 1),
4771 ter(terQUEUED));
4772
4773 // Create a couple more offers using sequences
4774 env(offer(alice, USD(1000), XRP(1000)), ter(terQUEUED));
4775
4776 env(offer(alice, USD(1000), XRP(1001)),
4777 seq(aliceSeq + 1),
4778 ter(terQUEUED));
4779
4780 // And try to cancel those using tickets
4781 env(offer(alice, USD(1000), XRP(1002)),
4782 ticket::use(aliceTkt + 5),
4783 json(jss::OfferSequence, aliceSeq),
4784 ter(terQUEUED));
4785
4786 env(offer_cancel(alice, aliceSeq + 1),
4787 ticket::use(aliceTkt + 6),
4788 ter(terQUEUED));
4789
4790 env.close();
4791
4792 // The ticket transactions that didn't succeed or get queued succeed
4793 // this time because the tickets got consumed when the offers came
4794 // out of the queue
4795 checkMetrics(__LINE__, env, 0, 50, 8, 7, 256);
4796 }
4797 }
4798
4799 void
4801 {
4802 testcase("Zero reference fee");
4803 using namespace jtx;
4804
4805 Account const alice("alice");
4806 auto const queued = ter(terQUEUED);
4807
4808 Env env(
4809 *this,
4810 makeConfig(
4811 {{"minimum_txn_in_ledger_standalone", "3"}},
4812 {{"reference_fee", "0"},
4813 {"account_reserve", "0"},
4814 {"owner_reserve", "0"}}));
4815
4816 BEAST_EXPECT(env.current()->fees().base == 10);
4817
4818 checkMetrics(__LINE__, env, 0, std::nullopt, 0, 3, 256);
4819
4820 // ledgers in queue is 2 because of makeConfig
4821 auto const initQueueMax = initFee(env, 3, 2, 0, 0, 0);
4822
4823 BEAST_EXPECT(env.current()->fees().base == 0);
4824
4825 {
4826 auto const fee = env.rpc("fee");
4827
4828 if (BEAST_EXPECT(fee.isMember(jss::result)) &&
4829 BEAST_EXPECT(!RPC::contains_error(fee[jss::result])))
4830 {
4831 auto const& result = fee[jss::result];
4832
4833 BEAST_EXPECT(result.isMember(jss::levels));
4834 auto const& levels = result[jss::levels];
4836 levels.isMember(jss::median_level) &&
4837 levels[jss::median_level] == "128000");
4839 levels.isMember(jss::minimum_level) &&
4840 levels[jss::minimum_level] == "256");
4842 levels.isMember(jss::open_ledger_level) &&
4843 levels[jss::open_ledger_level] == "256");
4845 levels.isMember(jss::reference_level) &&
4846 levels[jss::reference_level] == "256");
4847
4848 auto const& drops = result[jss::drops];
4850 drops.isMember(jss::base_fee) &&
4851 drops[jss::base_fee] == "0");
4853 drops.isMember(jss::median_fee) &&
4854 drops[jss::median_fee] == "0");
4856 drops.isMember(jss::minimum_fee) &&
4857 drops[jss::minimum_fee] == "0");
4859 drops.isMember(jss::open_ledger_fee) &&
4860 drops[jss::open_ledger_fee] == "0");
4861 }
4862 }
4863
4864 checkMetrics(__LINE__, env, 0, initQueueMax, 0, 3, 256);
4865
4866 // The noripple is to reduce the number of transactions required to
4867 // fund the accounts. There is no rippling in this test.
4868 env.fund(XRP(100000), noripple(alice));
4869
4870 checkMetrics(__LINE__, env, 0, initQueueMax, 1, 3, 256);
4871
4872 env.close();
4873
4874 checkMetrics(__LINE__, env, 0, 6, 0, 3, 256);
4875
4876 fillQueue(env, alice);
4877
4878 checkMetrics(__LINE__, env, 0, 6, 4, 3, 256);
4879
4880 env(noop(alice), openLedgerFee(env));
4881
4882 checkMetrics(__LINE__, env, 0, 6, 5, 3, 256);
4883
4884 auto aliceSeq = env.seq(alice);
4885 env(noop(alice), queued);
4886
4887 checkMetrics(__LINE__, env, 1, 6, 5, 3, 256);
4888
4889 env(noop(alice), seq(aliceSeq + 1), fee(10), queued);
4890
4891 checkMetrics(__LINE__, env, 2, 6, 5, 3, 256);
4892
4893 {
4894 auto const fee = env.rpc("fee");
4895
4896 if (BEAST_EXPECT(fee.isMember(jss::result)) &&
4897 BEAST_EXPECT(!RPC::contains_error(fee[jss::result])))
4898 {
4899 auto const& result = fee[jss::result];
4900
4901 BEAST_EXPECT(result.isMember(jss::levels));
4902 auto const& levels = result[jss::levels];
4904 levels.isMember(jss::median_level) &&
4905 levels[jss::median_level] == "128000");
4907 levels.isMember(jss::minimum_level) &&
4908 levels[jss::minimum_level] == "256");
4910 levels.isMember(jss::open_ledger_level) &&
4911 levels[jss::open_ledger_level] == "355555");
4913 levels.isMember(jss::reference_level) &&
4914 levels[jss::reference_level] == "256");
4915
4916 auto const& drops = result[jss::drops];
4918 drops.isMember(jss::base_fee) &&
4919 drops[jss::base_fee] == "0");
4921 drops.isMember(jss::median_fee) &&
4922 drops[jss::median_fee] == "0");
4924 drops.isMember(jss::minimum_fee) &&
4925 drops[jss::minimum_fee] == "0");
4927 drops.isMember(jss::open_ledger_fee) &&
4928 drops[jss::open_ledger_fee] == "1389");
4929 }
4930 }
4931
4932 env.close();
4933
4934 checkMetrics(__LINE__, env, 0, 10, 2, 5, 256);
4935 }
4936
4937 void
4938 run() override
4939 {
4940 testQueueSeq();
4942 testTecResult();
4950 testAcctTxnID();
4951 testMaximum();
4957 }
4958
4959 void
4961 {
4963 testRPC();
4971 testScaling();
4978 }
4979};
4980
4982{
4983 void
4984 run() override
4985 {
4986 runMetaInfo();
4987 }
4988};
4989
4990BEAST_DEFINE_TESTSUITE_PRIO(TxQPosNegFlows, app, ripple, 1);
4991BEAST_DEFINE_TESTSUITE_PRIO(TxQMetaInfo, app, ripple, 1);
4992
4993} // namespace test
4994} // namespace ripple
Represents a JSON value.
Definition: json_value.h:148
A generic endpoint for log messages.
Definition: Journal.h:60
A testsuite class.
Definition: suite.h:55
void pass()
Record a successful test condition.
Definition: suite.h:511
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
virtual OpenLedger & openLedger()=0
virtual TxQ & getTxQ()=0
void setRemoteFee(std::uint32_t f)
Definition: LoadFeeTrack.h:60
std::uint32_t getRemoteFee() const
Definition: LoadFeeTrack.h:68
std::uint32_t getLoadFactor() const
Definition: LoadFeeTrack.h:95
bool modify(modify_type const &f)
Modify the open ledger.
Definition: OpenLedger.cpp:56
Writable ledger view that accumulates state and tx changes.
Definition: OpenView.h:56
A type that represents either a sequence value or a ticket value.
Definition: SeqProxy.h:56
static constexpr SeqProxy sequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Definition: SeqProxy.h:76
std::vector< TxDetails > getTxs() const
Returns information about all transactions currently in the queue.
Definition: TxQ.cpp:1844
Metrics getMetrics(OpenView const &view) const
Returns fee metrics in reference fee level units.
Definition: TxQ.cpp:1777
std::vector< TxDetails > getAccountTxs(AccountID const &account) const
Returns information about the transactions currently in the queue for the account.
Definition: TxQ.cpp:1823
constexpr value_type drops() const
Returns the number of drops.
Definition: XRPAmount.h:177
void run() override
Runs the suite.
Definition: TxQ_test.cpp:4984
checkMetrics(__LINE__, env, 0, 8, 4, 4, 256)
BEAST_EXPECT(queue_data[jss::highest_sequence]==data[jss::Sequence].asUInt()+queue_data[jss::txn_count].asUInt() - 1)
envs(noop(alice), fee(100), seq(none), ter(terQUEUED))(submitParams)
envs(noop(alice), fee(100), seq(none), ter(telCAN_NOT_QUEUE_BLOCKED))(submitParams)
envs(fset(alice, asfAccountTxnID), fee(100), seq(none), json(jss::LastLedgerSequence, 10), ter(terQUEUED))(submitParams)
BEAST_EXPECT(queue_data[jss::txn_count]==0)
checkMetrics(__LINE__, env, 1, 8, 5, 4, 256)
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={})
Definition: TxQ_test.cpp:157
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, std::uint64_t expectedMedFeeLevel=256 *500)
Definition: TxQ_test.cpp:40
checkMetrics(__LINE__, env, 0, 6, 4, 3, 256)
BEAST_EXPECT(info.isMember(jss::result) &&RPC::contains_error(info[jss::result]))
BEAST_EXPECT(queue_data[jss::auth_change_queued]==true)
BEAST_EXPECT(queue_data.isMember(jss::transactions))
BEAST_EXPECT(queue_data[jss::max_spend_drops_total]=="100")
BEAST_EXPECT(info.isMember(jss::result) &&info[jss::result].isMember(jss::account_data))
BEAST_EXPECT(queue_data[jss::txn_count]==4)
BEAST_EXPECT(queue_data[jss::lowest_sequence]==data[jss::Sequence])
checkMetrics(__LINE__, env, 0, 6, 0, 3, 256)
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)
Definition: TxQ_test.cpp:131
BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total))
BEAST_EXPECT(queue_data[jss::auth_change_queued]==false)
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)
Definition: TxQ_test.cpp:191
BEAST_EXPECT(queued.size()==queue_data[jss::txn_count])
checkMetrics(__LINE__, env, 0, 10, 0, 5, 256)
BEAST_EXPECT(queue_data[jss::max_spend_drops_total]=="400")
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued))
checkMetrics(__LINE__, env, 4, 6, 4, 3, 256)
BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence))
auto openLedgerFee(jtx::Env &env)
Definition: TxQ_test.cpp:139
BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence))
BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence))
void run() override
Runs the suite.
Definition: TxQ_test.cpp:4938
BEAST_EXPECT(queue_data.isMember(jss::highest_sequence))
BEAST_EXPECT(queue_data.isMember(jss::txn_count))
checkMetrics(__LINE__, env, 0, 10, 2, 5, 256)
BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued))
BEAST_EXPECT(queue_data[jss::txn_count]==1)
Immutable cryptographic account descriptor.
Definition: Account.h:39
static Account const master
The master account.
Definition: Account.h:48
A transaction testing environment wrapper.
Definition: Env_ss.h:34
A transaction testing environment.
Definition: Env.h:118
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:210
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:530
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:326
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:115
void postconditions(JTx const &jt, ParsedResult const &parsed, Json::Value const &jr=Json::Value())
Check expected postconditions of JTx submission.
Definition: Env.cpp:385
Account const & master
Definition: Env.h:122
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
Definition: Env.h:491
NetClock::time_point now()
Returns the current network time.
Definition: Env.h:279
Application & app()
Definition: Env.h:256
beast::Journal const journal
Definition: Env.h:159
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:765
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:231
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
Definition: Env.cpp:177
void memoize(Account const &account)
Associate AccountID with account.
Definition: Env.cpp:150
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:219
A balance matches.
Definition: balance.h:39
Set the fee on a JTx.
Definition: fee.h:36
Inject raw JSON.
Definition: jtx_json.h:32
Match the number of items in the account's owner directory.
Definition: owners.h:71
Check a set of conditions.
Definition: require.h:65
Sets the SendMax on a JTx.
Definition: sendmax.h:32
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:35
Set a ticket sequence on a JTx.
Definition: ticket.h:48
T empty(T... args)
@ arrayValue
array value (ordered list)
Definition: json_value.h:43
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Definition: ErrorCodes.cpp:201
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:374
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition: ticket.cpp:30
owner_count< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
Definition: owners.h:87
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Definition: regkey.cpp:28
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
Definition: multisign.cpp:33
static none_t const none
Definition: tags.h:34
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.
Definition: trust.cpp:31
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition: envconfig.h:54
Json::Value noop(Account const &account)
The null transaction.
Definition: noop.h:31
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:28
owner_count< ltTICKET > tickets
Match the number of tickets on the account.
Definition: ticket.h:64
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:104
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Definition: offer.cpp:45
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.
Definition: WSClient.cpp:301
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
PreflightResult preflight(Application &app, Rules const &rules, STTx const &tx, ApplyFlags flags, beast::Journal j)
Gate a transaction based on static information.
Definition: applySteps.cpp:296
@ telCAN_NOT_QUEUE_BLOCKED
Definition: TER.h:62
@ telCAN_NOT_QUEUE_FULL
Definition: TER.h:64
@ telCAN_NOT_QUEUE
Definition: TER.h:59
@ telCAN_NOT_QUEUE_BALANCE
Definition: TER.h:60
@ telCAN_NOT_QUEUE_FEE
Definition: TER.h:63
@ telCAN_NOT_QUEUE_BLOCKS
Definition: TER.h:61
@ tefPAST_SEQ
Definition: TER.h:175
@ tefNO_TICKET
Definition: TER.h:185
@ tefWRONG_PRIOR
Definition: TER.h:176
constexpr std::uint32_t asfAccountTxnID
Definition: TxFlags.h:80
@ tecUNFUNDED_OFFER
Definition: TER.h:271
FeeLevel64 toFeeLevel(XRPAmount const &drops, XRPAmount const &baseFee)
Definition: TxQ.h:869
@ tesSUCCESS
Definition: TER.h:242
ApplyResult apply(Application &app, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition: apply.cpp:109
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:812
XRPAmount toDrops(FeeLevel< T > const &level, XRPAmount baseFee)
Definition: TxQ.h:862
@ tapUNLIMITED
Definition: ApplyView.h:42
@ tapNONE
Definition: ApplyView.h:31
@ terINSUF_FEE_B
Definition: TER.h:216
@ terNO_ACCOUNT
Definition: TER.h:217
@ terPRE_SEQ
Definition: TER.h:221
@ terQUEUED
Definition: TER.h:225
@ terPRE_TICKET
Definition: TER.h:226
@ temBAD_AMOUNT
Definition: TER.h:89
@ temREDUNDANT
Definition: TER.h:112
FeeLevel64 referenceFeeLevel
Reference transaction fee level.
Definition: TxQ.h:177
std::size_t txInLedger
Number of transactions currently in the open ledger.
Definition: TxQ.h:173
Used by parseResult() and postConditions()
Definition: Env.h:126
std::optional< TER > ter
Definition: Env.h:127
Set the sequence number on a JTx.
Definition: seq.h:34
T to_string(T... args)
T value_or(T... args)
T what(T... args)