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