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