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