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