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