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