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