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