xrpld
Loading...
Searching...
No Matches
FeeVote_test.cpp
1
2#include <test/jtx/Env.h>
3
4#include <xrpld/app/misc/FeeVote.h>
5#include <xrpld/core/Config.h>
6
7#include <xrpl/basics/base_uint.h>
8#include <xrpl/beast/unit_test/suite.h>
9#include <xrpl/config/BasicConfig.h>
10#include <xrpl/ledger/ApplyView.h>
11#include <xrpl/ledger/Ledger.h>
12#include <xrpl/ledger/OpenView.h>
13#include <xrpl/protocol/Feature.h>
14#include <xrpl/protocol/Indexes.h>
15#include <xrpl/protocol/KeyType.h>
16#include <xrpl/protocol/PublicKey.h>
17#include <xrpl/protocol/Rules.h>
18#include <xrpl/protocol/SField.h>
19#include <xrpl/protocol/STTx.h>
20#include <xrpl/protocol/STValidation.h>
21#include <xrpl/protocol/SecretKey.h>
22#include <xrpl/protocol/Serializer.h>
23#include <xrpl/protocol/TER.h>
24#include <xrpl/protocol/TxFormats.h>
25#include <xrpl/protocol/XRPAmount.h>
26#include <xrpl/shamap/SHAMap.h>
27#include <xrpl/shamap/SHAMapMissingNode.h>
28#include <xrpl/tx/apply.h>
29
30#include <cstdint>
31#include <limits>
32#include <memory>
33#include <optional>
34#include <string>
35#include <vector>
36
37namespace xrpl::test {
38
49
50STTx
51createFeeTx(Rules const& rules, std::uint32_t seq, FeeSettingsFields const& fields)
52{
53 auto fill = [&](auto& obj) {
54 obj.setAccountID(sfAccount, AccountID());
55 obj.setFieldU32(sfLedgerSequence, seq);
56
57 if (rules.enabled(featureXRPFees))
58 {
59 // New XRPFees format - all three fields are REQUIRED
60 obj.setFieldAmount(
61 sfBaseFeeDrops, fields.baseFeeDrops ? *fields.baseFeeDrops : XRPAmount{0});
62 obj.setFieldAmount(
63 sfReserveBaseDrops,
64 fields.reserveBaseDrops ? *fields.reserveBaseDrops : XRPAmount{0});
65 obj.setFieldAmount(
66 sfReserveIncrementDrops,
68 }
69 else
70 {
71 // Legacy format - all four fields are REQUIRED
72 obj.setFieldU64(sfBaseFee, fields.baseFee ? *fields.baseFee : 0);
73 obj.setFieldU32(sfReserveBase, fields.reserveBase ? *fields.reserveBase : 0);
74 obj.setFieldU32(
75 sfReserveIncrement, fields.reserveIncrement ? *fields.reserveIncrement : 0);
76 obj.setFieldU32(
77 sfReferenceFeeUnits, fields.referenceFeeUnits ? *fields.referenceFeeUnits : 0);
78 }
79 };
80 return STTx(ttFEE, fill);
81}
82
83STTx
85 Rules const& rules,
86 std::uint32_t seq,
87 bool missingRequiredFields = true,
88 bool wrongFeatureFields = false,
89 std::uint32_t uniqueValue = 42)
90{
91 auto fill = [&](auto& obj) {
92 obj.setAccountID(sfAccount, AccountID());
93 obj.setFieldU32(sfLedgerSequence, seq);
94
95 if (wrongFeatureFields)
96 {
97 if (rules.enabled(featureXRPFees))
98 {
99 obj.setFieldU64(sfBaseFee, 10 + uniqueValue);
100 obj.setFieldU32(sfReserveBase, 200000);
101 obj.setFieldU32(sfReserveIncrement, 50000);
102 obj.setFieldU32(sfReferenceFeeUnits, 10);
103 }
104 else
105 {
106 obj.setFieldAmount(sfBaseFeeDrops, XRPAmount{10 + uniqueValue});
107 obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000});
108 obj.setFieldAmount(sfReserveIncrementDrops, XRPAmount{50000});
109 }
110 }
111 else if (!missingRequiredFields)
112 {
113 // Create valid transaction (all required fields present)
114 if (rules.enabled(featureXRPFees))
115 {
116 obj.setFieldAmount(sfBaseFeeDrops, XRPAmount{10 + uniqueValue});
117 obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000});
118 obj.setFieldAmount(sfReserveIncrementDrops, XRPAmount{50000});
119 }
120 else
121 {
122 obj.setFieldU64(sfBaseFee, 10 + uniqueValue);
123 obj.setFieldU32(sfReserveBase, 200000);
124 obj.setFieldU32(sfReserveIncrement, 50000);
125 obj.setFieldU32(sfReferenceFeeUnits, 10);
126 }
127 }
128 // If missingRequiredFields is true, we don't add the required fields
129 // (default behavior)
130 };
131 return STTx(ttFEE, fill);
132}
133
134bool
136{
137 auto const res = apply(env.app(), view, tx, ApplyFlags::TapNone, env.journal);
138 return isTesSuccess(res.ter);
139}
140
141bool
143 std::shared_ptr<Ledger const> const& ledger,
144 Rules const& rules,
145 FeeSettingsFields const& expected)
146{
147 auto const feeObject = ledger->read(keylet::feeSettings());
148 if (!feeObject)
149 return false;
150
151 auto checkEquality = [&](auto const& field, auto const& expected) {
152 if (!feeObject->isFieldPresent(field))
153 return false;
154 return feeObject->at(field) == expected;
155 };
156
157 if (rules.enabled(featureXRPFees))
158 {
159 if (feeObject->isFieldPresent(sfBaseFee) || feeObject->isFieldPresent(sfReserveBase) ||
160 feeObject->isFieldPresent(sfReserveIncrement) ||
161 feeObject->isFieldPresent(sfReferenceFeeUnits))
162 return false;
163
164 if (!checkEquality(sfBaseFeeDrops, expected.baseFeeDrops.value_or(XRPAmount{0})))
165 return false;
166 if (!checkEquality(sfReserveBaseDrops, expected.reserveBaseDrops.value_or(XRPAmount{0})))
167 return false;
168 if (!checkEquality(
169 sfReserveIncrementDrops, expected.reserveIncrementDrops.value_or(XRPAmount{0})))
170 return false;
171 }
172 else
173 {
174 if (feeObject->isFieldPresent(sfBaseFeeDrops) ||
175 feeObject->isFieldPresent(sfReserveBaseDrops) ||
176 feeObject->isFieldPresent(sfReserveIncrementDrops))
177 return false;
178
179 // Read sfBaseFee as a hex string and compare to expected.baseFee
180 if (!checkEquality(sfBaseFee, expected.baseFee))
181 return false;
182 if (!checkEquality(sfReserveBase, expected.reserveBase))
183 return false;
184 if (!checkEquality(sfReserveIncrement, expected.reserveIncrement))
185 return false;
186 if (!checkEquality(sfReferenceFeeUnits, expected.referenceFeeUnits))
187 return false;
188 }
189
190 return true;
191}
192
195{
197 for (auto i = txSet->begin(); i != txSet->end(); ++i)
198 {
199 auto const data = i->slice();
200 auto serialIter = SerialIter(data);
201 txs.emplace_back(serialIter);
202 }
203 return txs;
204};
205
207{
208 void
210 {
211 FeeSetup const defaultSetup;
212 {
213 // defaults
214 Section const config;
215 auto setup = setupFeeVote(config);
216 BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee);
217 BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve);
218 BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve);
219 }
220 {
221 Section config;
222 config.append(
223 {"reference_fee = 50", "account_reserve = 1234567", "owner_reserve = 1234"});
224 auto setup = setupFeeVote(config);
225 BEAST_EXPECT(setup.referenceFee == 50);
226 BEAST_EXPECT(setup.accountReserve == 1234567);
227 BEAST_EXPECT(setup.ownerReserve == 1234);
228 }
229 {
230 Section config;
231 config.append(
232 {"reference_fee = blah", "account_reserve = yada", "owner_reserve = foo"});
233 // Illegal values are ignored, and the defaults left unchanged
234 auto setup = setupFeeVote(config);
235 BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee);
236 BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve);
237 BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve);
238 }
239 {
240 Section config;
241 config.append(
242 {"reference_fee = -50", "account_reserve = -1234567", "owner_reserve = -1234"});
243 // Illegal values are ignored, and the defaults left unchanged
244 auto setup = setupFeeVote(config);
245 BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee);
246 BEAST_EXPECT(setup.accountReserve == static_cast<std::uint32_t>(-1234567));
247 BEAST_EXPECT(setup.ownerReserve == static_cast<std::uint32_t>(-1234));
248 }
249 {
250 auto const big64 = std::to_string(
252 Section config;
253 config.append(
254 {"reference_fee = " + big64,
255 "account_reserve = " + big64,
256 "owner_reserve = " + big64});
257 // Illegal values are ignored, and the defaults left unchanged
258 auto setup = setupFeeVote(config);
259 BEAST_EXPECT(setup.referenceFee == defaultSetup.referenceFee);
260 BEAST_EXPECT(setup.accountReserve == defaultSetup.accountReserve);
261 BEAST_EXPECT(setup.ownerReserve == defaultSetup.ownerReserve);
262 }
263 }
264
265 void
267 {
268 testcase("Basic SetFee transaction");
269
270 // Test with XRPFees disabled (legacy format)
271 {
272 jtx::Env env(*this, jtx::testableAmendments() - featureXRPFees);
273 auto ledger = std::make_shared<Ledger>(
275 Rules{env.app().config().features},
276 env.app().config().fees.toFees(),
278 env.app().getNodeFamily());
279
280 // Create the next ledger to apply transaction to
281 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
282
283 // Test successful fee transaction with legacy fields
284
285 FeeSettingsFields const fields{
286 .baseFee = 10,
287 .reserveBase = 200000,
288 .reserveIncrement = 50000,
289 .referenceFeeUnits = 10};
290 auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields);
291
292 OpenView accum(ledger.get());
293 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx));
294 accum.apply(*ledger);
295
296 // Verify fee object was created/updated correctly
297 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields));
298 }
299
300 // Test with XRPFees enabled (new format)
301 {
302 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
303 auto ledger = std::make_shared<Ledger>(
305 Rules{env.app().config().features},
306 env.app().config().fees.toFees(),
308 env.app().getNodeFamily());
309
310 // Create the next ledger to apply transaction to
311 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
312
313 FeeSettingsFields const fields{
314 .baseFeeDrops = XRPAmount{10},
315 .reserveBaseDrops = XRPAmount{200000},
316 .reserveIncrementDrops = XRPAmount{50000}};
317 // Test successful fee transaction with new fields
318 auto feeTx = createFeeTx(ledger->rules(), ledger->seq(), fields);
319
320 OpenView accum(ledger.get());
321 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx));
322 accum.apply(*ledger);
323
324 // Verify fee object was created/updated correctly
325 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields));
326 }
327 }
328
329 void
331 {
332 testcase("Fee Transaction Validation");
333
334 {
335 jtx::Env env(*this, jtx::testableAmendments() - featureXRPFees);
336 auto ledger = std::make_shared<Ledger>(
338 Rules{env.app().config().features},
339 env.app().config().fees.toFees(),
341 env.app().getNodeFamily());
342
343 // Create the next ledger to apply transaction to
344 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
345
346 // Test transaction with missing required legacy fields
347 auto invalidTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), true, false, 1);
348 OpenView accum(ledger.get());
349 BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx));
350
351 // Test transaction with new format fields when XRPFees is disabled
352 auto disallowedTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), false, true, 2);
353 BEAST_EXPECT(!applyFeeAndTestResult(env, accum, disallowedTx));
354 }
355
356 {
357 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
358 auto ledger = std::make_shared<Ledger>(
360 Rules{env.app().config().features},
361 env.app().config().fees.toFees(),
363 env.app().getNodeFamily());
364
365 // Create the next ledger to apply transaction to
366 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
367
368 // Test transaction with missing required new fields
369 auto invalidTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), true, false, 3);
370 OpenView accum(ledger.get());
371 BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx));
372
373 // Test transaction with legacy fields when XRPFees is enabled
374 auto disallowedTx = createInvalidFeeTx(ledger->rules(), ledger->seq(), false, true, 4);
375 BEAST_EXPECT(!applyFeeAndTestResult(env, accum, disallowedTx));
376 }
377 }
378
379 void
381 {
382 testcase("Pseudo Transaction Properties");
383
384 jtx::Env env(*this, jtx::testableAmendments());
385 auto ledger = std::make_shared<Ledger>(
387 Rules{env.app().config().features},
388 env.app().config().fees.toFees(),
390 env.app().getNodeFamily());
391
392 // Create the next ledger to apply transaction to
393 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
394
395 auto feeTx = createFeeTx(
396 ledger->rules(),
397 ledger->seq(),
398 {.baseFeeDrops = XRPAmount{10},
399 .reserveBaseDrops = XRPAmount{200000},
400 .reserveIncrementDrops = XRPAmount{50000}});
401
402 // Verify pseudo-transaction properties
403 BEAST_EXPECT(feeTx.getAccountID(sfAccount) == AccountID());
404 BEAST_EXPECT(feeTx.getFieldAmount(sfFee) == XRPAmount{0});
405 BEAST_EXPECT(feeTx.getSigningPubKey().empty());
406 BEAST_EXPECT(feeTx.getSignature().empty());
407 BEAST_EXPECT(!feeTx.isFieldPresent(sfSigners));
408 BEAST_EXPECT(feeTx.getFieldU32(sfSequence) == 0);
409 BEAST_EXPECT(!feeTx.isFieldPresent(sfPreviousTxnID));
410
411 // But can be applied to a closed ledger
412 {
413 OpenView closedAccum(ledger.get());
414 BEAST_EXPECT(applyFeeAndTestResult(env, closedAccum, feeTx));
415 }
416 }
417
418 void
420 {
421 testcase("Multiple Fee Updates");
422
423 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
424 auto ledger = std::make_shared<Ledger>(
426 Rules{env.app().config().features},
427 env.app().config().fees.toFees(),
429 env.app().getNodeFamily());
430
431 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
432
433 FeeSettingsFields const fields1{
434 .baseFeeDrops = XRPAmount{10},
435 .reserveBaseDrops = XRPAmount{200000},
436 .reserveIncrementDrops = XRPAmount{50000}};
437 auto feeTx1 = createFeeTx(ledger->rules(), ledger->seq(), fields1);
438
439 {
440 OpenView accum(ledger.get());
441 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx1));
442 accum.apply(*ledger);
443 }
444
445 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields1));
446
447 // Apply second fee transaction with different values
448 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
449
450 FeeSettingsFields const fields2{
451 .baseFeeDrops = XRPAmount{20},
452 .reserveBaseDrops = XRPAmount{300000},
453 .reserveIncrementDrops = XRPAmount{75000}};
454 auto feeTx2 = createFeeTx(ledger->rules(), ledger->seq(), fields2);
455
456 {
457 OpenView accum(ledger.get());
458 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx2));
459 accum.apply(*ledger);
460 }
461
462 // Verify second update overwrote the first
463 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields2));
464 }
465
466 void
468 {
469 testcase("Wrong Ledger Sequence");
470
471 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
472 auto ledger = std::make_shared<Ledger>(
474 Rules{env.app().config().features},
475 env.app().config().fees.toFees(),
477 env.app().getNodeFamily());
478
479 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
480
481 // Test transaction with wrong ledger sequence
482 auto feeTx = createFeeTx(
483 ledger->rules(),
484 ledger->seq() + 5, // Wrong sequence (should be ledger->seq())
485 {.baseFeeDrops = XRPAmount{10},
486 .reserveBaseDrops = XRPAmount{200000},
487 .reserveIncrementDrops = XRPAmount{50000}});
488
489 OpenView accum(ledger.get());
490
491 // The transaction should still succeed as long as other fields are
492 // valid
493 // The ledger sequence field is only used for informational purposes
494 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx));
495 }
496
497 void
499 {
500 testcase("Partial Field Updates");
501
502 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
503 auto ledger = std::make_shared<Ledger>(
505 Rules{env.app().config().features},
506 env.app().config().fees.toFees(),
508 env.app().getNodeFamily());
509
510 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
511
512 FeeSettingsFields const fields1{
513 .baseFeeDrops = XRPAmount{10},
514 .reserveBaseDrops = XRPAmount{200000},
515 .reserveIncrementDrops = XRPAmount{50000}};
516 auto feeTx1 = createFeeTx(ledger->rules(), ledger->seq(), fields1);
517
518 {
519 OpenView accum(ledger.get());
520 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx1));
521 accum.apply(*ledger);
522 }
523
524 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields1));
525
526 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
527
528 // Apply partial update (only some fields)
529 FeeSettingsFields const fields2{
530 .baseFeeDrops = XRPAmount{20}, .reserveBaseDrops = XRPAmount{200000}};
531 auto feeTx2 = createFeeTx(ledger->rules(), ledger->seq(), fields2);
532
533 {
534 OpenView accum(ledger.get());
535 BEAST_EXPECT(applyFeeAndTestResult(env, accum, feeTx2));
536 accum.apply(*ledger);
537 }
538
539 // Verify the partial update worked
540 BEAST_EXPECT(verifyFeeObject(ledger, ledger->rules(), fields2));
541 }
542
543 void
545 {
546 testcase("Single Invalid Transaction");
547
548 jtx::Env env(*this, jtx::testableAmendments() | featureXRPFees);
549 auto ledger = std::make_shared<Ledger>(
551 Rules{env.app().config().features},
552 env.app().config().fees.toFees(),
554 env.app().getNodeFamily());
555
556 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
557
558 // Test invalid transaction with non-zero account - this should fail
559 // validation
560 auto invalidTx = STTx(ttFEE, [&](auto& obj) {
561 obj.setAccountID(sfAccount,
562 AccountID(1)); // Should be zero (this makes it invalid)
563 obj.setFieldU32(sfLedgerSequence, ledger->seq());
564 obj.setFieldAmount(sfBaseFeeDrops, XRPAmount{10});
565 obj.setFieldAmount(sfReserveBaseDrops, XRPAmount{200000});
566 obj.setFieldAmount(sfReserveIncrementDrops, XRPAmount{50000});
567 });
568
569 OpenView accum(ledger.get());
570 BEAST_EXPECT(!applyFeeAndTestResult(env, accum, invalidTx));
571 }
572
573 void
575 {
576 testcase("doValidation");
577
578 using namespace jtx;
579
580 FeeSetup setup;
581 setup.referenceFee = 42;
582 setup.accountReserve = 1234567;
583 setup.ownerReserve = 7654321;
584
585 // Test with XRPFees enabled
586 {
587 Env env(*this, testableAmendments() | featureXRPFees);
588 auto feeVote = makeFeeVote(setup, env.app().getJournal("FeeVote"));
589
590 auto ledger = std::make_shared<Ledger>(
592 Rules{env.app().config().features},
593 env.app().config().fees.toFees(),
595 env.app().getNodeFamily());
596
597 auto sec = randomSecretKey();
598 auto pub = derivePublicKey(KeyType::Secp256k1, sec);
599
601 env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) {
602 v.setFieldU32(sfLedgerSequence, 12345);
603 });
604
605 // Use the current ledger's fees as the "current" fees for
606 // doValidation
607 auto const& currentFees = ledger->fees();
608
609 feeVote->doValidation(currentFees, ledger->rules(), *val);
610
611 BEAST_EXPECT(val->isFieldPresent(sfBaseFeeDrops));
612 BEAST_EXPECT(val->getFieldAmount(sfBaseFeeDrops) == XRPAmount(setup.referenceFee));
613 }
614
615 // Test with XRPFees disabled (legacy format)
616 {
617 Env env(*this, testableAmendments() - featureXRPFees);
618 auto feeVote = makeFeeVote(setup, env.app().getJournal("FeeVote"));
619
620 auto ledger = std::make_shared<Ledger>(
622 Rules{env.app().config().features},
623 env.app().config().fees.toFees(),
625 env.app().getNodeFamily());
626
627 auto sec = randomSecretKey();
628 auto pub = derivePublicKey(KeyType::Secp256k1, sec);
629
631 env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [](STValidation& v) {
632 v.setFieldU32(sfLedgerSequence, 12345);
633 });
634
635 auto const& currentFees = ledger->fees();
636
637 feeVote->doValidation(currentFees, ledger->rules(), *val);
638
639 // In legacy mode, should vote using legacy fields
640 BEAST_EXPECT(val->isFieldPresent(sfBaseFee));
641 BEAST_EXPECT(val->getFieldU64(sfBaseFee) == setup.referenceFee);
642 }
643 }
644
645 void
647 {
648 testcase("doVoting");
649
650 using namespace jtx;
651
652 FeeSetup setup;
653 setup.referenceFee = 42;
654 setup.accountReserve = 1234567;
655 setup.ownerReserve = 7654321;
656
657 Env env(*this, testableAmendments() | featureXRPFees);
658
659 // establish what the current fees are
660 BEAST_EXPECT(env.current()->fees().base == XRPAmount{UNIT_TEST_REFERENCE_FEE});
661 BEAST_EXPECT(env.current()->fees().reserve == XRPAmount{200'000'000});
662 BEAST_EXPECT(env.current()->fees().increment == XRPAmount{50'000'000});
663
664 auto feeVote = makeFeeVote(setup, env.app().getJournal("FeeVote"));
665 auto ledger = std::make_shared<Ledger>(
667 Rules{env.app().config().features},
668 env.app().config().fees.toFees(),
670 env.app().getNodeFamily());
671
672 // doVoting requires a flag ledger (every 256th ledger)
673 // We need to create a ledger at sequence 256 to make it a flag ledger
674 for (int i = 0; i < 256 - 1; ++i)
675 {
676 ledger = std::make_shared<Ledger>(*ledger, env.app().getTimeKeeper().closeTime());
677 }
678 BEAST_EXPECT(ledger->isFlagLedger());
679
680 // Create some mock validations with fee votes
682
683 for (int i = 0; i < 5; i++)
684 {
685 auto sec = randomSecretKey();
686 auto pub = derivePublicKey(KeyType::Secp256k1, sec);
687
689 env.app().getTimeKeeper().now(), pub, sec, calcNodeID(pub), [&](STValidation& v) {
690 v.setFieldU32(sfLedgerSequence, ledger->seq());
691 // Vote for different fees than current
692 v.setFieldAmount(sfBaseFeeDrops, XRPAmount{setup.referenceFee});
693 v.setFieldAmount(sfReserveBaseDrops, XRPAmount{setup.accountReserve});
694 v.setFieldAmount(sfReserveIncrementDrops, XRPAmount{setup.ownerReserve});
695 });
696 if ((i % 2) != 0)
697 val->setTrusted();
698 validations.push_back(val);
699 }
700
702
703 // This should not throw since we have a flag ledger
704 feeVote->doVoting(ledger, validations, txSet);
705
706 auto const txs = getTxs(txSet);
707 BEAST_EXPECT(txs.size() == 1);
708 auto const& feeTx = txs[0];
709
710 BEAST_EXPECT(feeTx.getTxnType() == ttFEE);
711
712 BEAST_EXPECT(feeTx.getAccountID(sfAccount) == AccountID());
713 BEAST_EXPECT(feeTx.getFieldU32(sfLedgerSequence) == ledger->seq() + 1);
714
715 BEAST_EXPECT(feeTx.isFieldPresent(sfBaseFeeDrops));
716 BEAST_EXPECT(feeTx.isFieldPresent(sfReserveBaseDrops));
717 BEAST_EXPECT(feeTx.isFieldPresent(sfReserveIncrementDrops));
718
719 // The legacy fields should NOT be present
720 BEAST_EXPECT(!feeTx.isFieldPresent(sfBaseFee));
721 BEAST_EXPECT(!feeTx.isFieldPresent(sfReserveBase));
722 BEAST_EXPECT(!feeTx.isFieldPresent(sfReserveIncrement));
723 BEAST_EXPECT(!feeTx.isFieldPresent(sfReferenceFeeUnits));
724
725 // Check the values
726 BEAST_EXPECT(feeTx.getFieldAmount(sfBaseFeeDrops) == XRPAmount{setup.referenceFee});
727 BEAST_EXPECT(feeTx.getFieldAmount(sfReserveBaseDrops) == XRPAmount{setup.accountReserve});
728 BEAST_EXPECT(
729 feeTx.getFieldAmount(sfReserveIncrementDrops) == XRPAmount{setup.ownerReserve});
730 }
731
732 void
746};
747
749
750} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
virtual Config & config()=0
std::unordered_set< uint256, beast::Uhash<> > features
Definition Config.h:261
Manager to process fee votes.
Definition FeeVote.h:11
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
void apply(TxsRawView &to) const
Apply changes.
Definition OpenView.cpp:126
Rules controlling protocol behavior.
Definition Rules.h:33
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:171
void setFieldU32(SField const &field, std::uint32_t)
Definition STObject.cpp:733
void setFieldAmount(SField const &field, STAmount const &)
Definition STObject.cpp:793
Holds a collection of configuration values.
Definition BasicConfig.h:24
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
virtual beast::Journal getJournal(std::string const &name)=0
virtual Family & getNodeFamily()=0
virtual TimeKeeper & getTimeKeeper()=0
time_point now() const override
Returns the current time, using the server's clock.
Definition TimeKeeper.h:44
time_point closeTime() const
Returns the predicted close time, in network time.
Definition TimeKeeper.h:56
void run() override
Runs the suite.
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
beast::Journal const journal
Definition Env.h:184
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
T emplace_back(T... args)
T make_shared(T... args)
T max(T... args)
Keylet const & feeSettings() noexcept
The (fixed) index of the object containing the ledger fees.
Definition Indexes.cpp:221
FeatureBitset testableAmendments()
Definition Env.h:76
bool verifyFeeObject(std::shared_ptr< Ledger const > const &ledger, Rules const &rules, FeeSettingsFields const &expected)
STTx createInvalidFeeTx(Rules const &rules, std::uint32_t seq, bool missingRequiredFields=true, bool wrongFeatureFields=false, std::uint32_t uniqueValue=42)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
STTx createFeeTx(Rules const &rules, std::uint32_t seq, FeeSettingsFields const &fields)
bool applyFeeAndTestResult(jtx::Env &env, OpenView &view, STTx const &tx)
constexpr XRPAmount
Convert XRP to drops (integral types).
Definition TxTest.h:48
std::vector< STTx > getTxs(std::shared_ptr< SHAMap > const &txSet)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
ApplyResult apply(ServiceRegistry &registry, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
Definition apply.cpp:139
CreateGenesisT const kCreateGenesis
std::unique_ptr< FeeVote > makeFeeVote(FeeSetup const &setup, beast::Journal journal)
Create an instance of the FeeVote logic.
FeeSetup setupFeeVote(Section const &section)
Definition Config.cpp:1199
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
@ TapNone
Definition ApplyView.h:13
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
bool isTesSuccess(TER x) noexcept
Definition TER.h:663
T push_back(T... args)
Fee schedule for startup / standalone, and to vote for.
Definition Config.h:49
XRPAmount accountReserve
The account reserve requirement in drops.
Definition Config.h:54
XRPAmount ownerReserve
The per-owned item reserve requirement in drops.
Definition Config.h:57
XRPAmount referenceFee
The cost of a reference transaction in drops.
Definition Config.h:51
std::optional< std::uint64_t > baseFee
std::optional< XRPAmount > reserveIncrementDrops
std::optional< std::uint32_t > reserveBase
std::optional< XRPAmount > baseFeeDrops
std::optional< XRPAmount > reserveBaseDrops
std::optional< std::uint32_t > reserveIncrement
std::optional< std::uint32_t > referenceFeeUnits
T to_string(T... args)