xrpld
Loading...
Searching...
No Matches
PayStrandMPT_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/balance.h>
7#include <test/jtx/mpt.h>
8#include <test/jtx/offer.h>
9#include <test/jtx/paths.h>
10#include <test/jtx/pay.h>
11#include <test/jtx/sendmax.h>
12#include <test/jtx/ter.h>
13#include <test/jtx/txflags.h>
14
15#include <xrpl/beast/unit_test/suite.h>
16#include <xrpl/ledger/ApplyView.h>
17#include <xrpl/ledger/PaymentSandbox.h>
18#include <xrpl/protocol/AccountID.h>
19#include <xrpl/protocol/Asset.h>
20#include <xrpl/protocol/Book.h>
21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/Issue.h>
23#include <xrpl/protocol/STAmount.h>
24#include <xrpl/protocol/STPathSet.h>
25#include <xrpl/protocol/TER.h>
26#include <xrpl/protocol/TxFlags.h>
27#include <xrpl/protocol/UintTypes.h>
28#include <xrpl/tx/paths/RippleCalc.h>
29#include <xrpl/tx/paths/detail/Steps.h>
30#include <xrpl/tx/transactors/dex/AMMContext.h>
31
32#include <optional>
33#include <type_traits>
34
35namespace xrpl::test {
36
38{
40 makeEndpointStep(jtx::Account const& src, jtx::Account const& dst, jtx::IOU const& iou)
41 {
42 return jtx::DirectStepInfo{.src = src, .dst = dst, .currency = iou.currency};
43 }
45 makeEndpointStep(jtx::Account const& src, jtx::Account const& dst, jtx::MPT const& mpt)
46 {
47 return jtx::MPTEndpointStepInfo{.src = src, .dst = dst, .mptid = mpt.mpt()};
48 }
49
50 void
52 {
53 testcase("To Strand");
54
55 using namespace jtx;
56
57 auto const alice = Account("alice");
58 auto const bob = Account("bob");
59 auto const carol = Account("carol");
60 auto const gw = Account("gw");
61
62 using M = MPTEndpointStepInfo;
63 using B = xrpl::Book;
64 using XRPS = XRPEndpointStepInfo;
65
66 AMMContext ammContext(alice, false);
67
68 auto test = [&, this](
69 jtx::Env& env,
70 Asset const& deliver,
71 std::optional<Asset> const& sendMaxIssue,
72 STPath const& path,
73 TER expTer,
74 auto&&... expSteps) {
75 auto [ter, strand] = toStrand(
76 *env.current(),
77 alice,
78 bob,
79 deliver,
80 std::nullopt,
81 sendMaxIssue,
82 path,
83 true,
85 ammContext,
86 std::nullopt,
87 env.app().getLogs().journal("Flow"));
88 BEAST_EXPECT(ter == expTer);
89 if (sizeof...(expSteps) != 0)
90 BEAST_EXPECT(jtx::equal(strand, std::forward<decltype(expSteps)>(expSteps)...));
91 };
92
93 {
94 auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
95 Env env(*this, features);
96 env.fund(XRP(10'000), alice, bob, gw);
97 MPT const usd =
98 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
99 auto const bobUSD = issue1(
100 {.env = env,
101 .token = "USD",
102 .issuer = bob,
103 .holders = {alice},
104 .limit = 1'000});
105 MPT const eur =
106 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
107 auto const bobEUR = issue2(
108 {.env = env,
109 .token = "EUR",
110 .issuer = bob,
111 .holders = {alice},
112 .limit = 1'000});
113 env(pay(gw, alice, eur(100)));
114
115 {
116 // Original test is
117 // STPath({ipe(bob["USD"]), cpe(EUR.currency)});
118 // which ripples through same currency, different issuer.
119 // This results in 5 steps:
120 // 1 DirectStep alice -> gw EUR/gw
121 // 2 Book EUR/gw USD/bob
122 // 3 Book USD/bob EUR/bob
123 // 4 Book EUR/bob XRP
124 // 5 XRPEndpoint
125 // This is somewhat equivalent path with MPT
126 STPath const path = STPath({ipe(bobUSD), ipe(bobEUR), cpe(xrpCurrency())});
127 auto [ter, _] = toStrand(
128 *env.current(),
129 alice,
130 alice,
131 /*deliver*/ xrpIssue(),
132 /*limitQuality*/ std::nullopt,
133 /*sendMaxIssue*/ eur,
134 path,
135 true,
137 ammContext,
138 std::nullopt,
139 env.app().getLogs().journal("Flow"));
140 (void)_;
141 BEAST_EXPECT(ter == tesSUCCESS);
142 }
143 {
144 STPath const path = STPath({ipe(usd), cpe(xrpCurrency())});
145 auto [ter, _] = toStrand(
146 *env.current(),
147 alice,
148 alice,
149 /*deliver*/ xrpIssue(),
150 /*limitQuality*/ std::nullopt,
151 /*sendMaxIssue*/ eur,
152 path,
153 true,
155 ammContext,
156 std::nullopt,
157 env.app().getLogs().journal("Flow"));
158 (void)_;
159 BEAST_EXPECT(ter == tesSUCCESS);
160 }
161 };
162 testHelper2TokensMix(testMultiToken);
163 }
164 {
165 auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
166 Env env(*this, features);
167 env.fund(XRP(10'000), alice, bob, carol, gw);
168 auto usd = issue1({.env = env, .token = "USD", .issuer = gw, .limit = 1'000});
169 using tUSD = std::decay_t<decltype(usd)>;
170 auto eur = issue2({.env = env, .token = "EUR", .issuer = gw, .limit = 1'000});
171 using tEUR = std::decay_t<decltype(eur)>;
172
173 auto const err = [&]() {
174 if constexpr (std::is_same_v<tUSD, MPT>)
175 {
176 return tecNO_AUTH;
177 }
178 else
179 {
180 return terNO_LINE;
181 }
182 }();
183 test(env, usd, std::nullopt, STPath(), err);
184
185 if constexpr (std::is_same_v<tUSD, MPT>)
186 {
187 MPTTester(env, gw, usd).authorizeHolders({alice, bob, carol});
188 }
189 else
190 {
191 env.trust(usd(1'000), alice, bob, carol);
192 }
193
194 test(env, usd, std::nullopt, STPath(), tecPATH_DRY);
195
196 env(pay(gw, alice, usd(100)));
197 env(pay(gw, carol, usd(100)));
198
199 // Insert implied account
200 test(
201 env,
202 usd,
203 std::nullopt,
204 STPath(),
206 makeEndpointStep(alice, gw, usd),
207 makeEndpointStep(gw, bob, usd));
208 if constexpr (std::is_same_v<tEUR, MPT>)
209 {
210 MPTTester(env, gw, eur).authorizeHolders({alice, bob});
211 }
212 else
213 {
214 env.trust(eur(1'000), alice, bob);
215 }
216
217 // Insert implied offer
218 test(
219 env,
220 eur,
221 usd,
222 STPath(),
224 makeEndpointStep(alice, gw, usd),
225 B{usd, eur, std::nullopt},
226 makeEndpointStep(gw, bob, eur));
227
228 // Path with explicit offer
229 test(
230 env,
231 eur,
232 usd,
233 STPath({ipe(eur)}),
235 makeEndpointStep(alice, gw, usd),
236 B{usd, eur, std::nullopt},
237 makeEndpointStep(gw, bob, eur));
238
239 // Path with XRP src currency
240 test(
241 env,
242 usd,
243 xrpIssue(),
244 STPath({ipe(usd)}),
246 XRPS{alice},
247 B{XRP, usd, std::nullopt},
248 makeEndpointStep(gw, bob, usd));
249
250 // Path with XRP dst currency.
251 test(
252 env,
253 xrpIssue(),
254 usd,
258 makeEndpointStep(alice, gw, usd),
259 B{usd, XRP, std::nullopt},
260 XRPS{bob});
261
262 // Path with XRP cross currency bridged payment
263 test(
264 env,
265 eur,
266 usd,
267 STPath({cpe(xrpCurrency())}),
269 makeEndpointStep(alice, gw, usd),
270 B{usd, XRP, std::nullopt},
271 B{XRP, eur, std::nullopt},
272 makeEndpointStep(gw, bob, eur));
273
274 // Create an offer with the same in/out issue
275 test(env, eur, usd, STPath({ipe(usd), ipe(eur)}), temBAD_PATH);
276
277 // The same offer can't appear more than once on a path
278 test(env, eur, usd, STPath({ipe(eur), ipe(usd), ipe(eur)}), temBAD_PATH_LOOP);
279 };
280 testHelper2TokensMix(testMultiToken);
281 }
282
283 {
284 // cannot have more than one offer with the same output issue
285
286 using namespace jtx;
287
288 auto testMultiToken = [&](auto&& issue1, auto&& issue2) {
289 Env env(*this, features);
290
291 env.fund(XRP(10'000), alice, bob, carol, gw);
292
293 auto const usd = issue1(
294 {.env = env,
295 .token = "USD",
296 .issuer = gw,
297 .holders = {alice, bob, carol},
298 .limit = 10'000});
299 auto const eur = issue2(
300 {.env = env,
301 .token = "EUR",
302 .issuer = gw,
303 .holders = {alice, bob, carol},
304 .limit = 10'000});
305
306 env(pay(gw, bob, usd(100)));
307 env(pay(gw, bob, eur(100)));
308
309 env(offer(bob, XRP(100), usd(100)));
310 env(offer(bob, usd(100), eur(100)), Txflags(tfPassive));
311 env(offer(bob, eur(100), usd(100)), Txflags(tfPassive));
312
313 // payment path: XRP -> XRP/USD -> USD/EUR -> EUR/USD
314 env(pay(alice, carol, usd(100)),
315 Path(~usd, ~eur, ~usd),
316 Sendmax(XRP(200)),
317 Txflags(tfNoRippleDirect),
319 };
320 testHelper2TokensMix(testMultiToken);
321 }
322
323 {
324 // check global freeze
325 Env env(*this, features);
326 env.fund(XRP(10000), alice, bob, gw);
327 auto usdm = MPTTester(
328 {.env = env,
329 .issuer = gw,
330 .holders = {alice, bob},
331 .flags = kMptDexFlags | tfMPTCanLock,
332 .maxAmt = 1'000});
333 MPT const usd = usdm;
334 env(pay(gw, alice, usd(100)));
335
336 // Account can't issue payments
337 usdm.set({.holder = alice, .flags = tfMPTLock});
338 test(env, usd, std::nullopt, STPath(), terLOCKED);
339 usdm.set({.holder = alice, .flags = tfMPTUnlock});
340 test(env, usd, std::nullopt, STPath(), tesSUCCESS);
341
342 // Account can not issue funds
343 usdm.set({.flags = tfMPTLock});
344 test(env, usd, std::nullopt, STPath(), terLOCKED);
345 usdm.set({.flags = tfMPTUnlock});
346 test(env, usd, std::nullopt, STPath(), tesSUCCESS);
347
348 // Account can not receive funds
349 usdm.set({.holder = bob, .flags = tfMPTLock});
350 test(env, usd, std::nullopt, STPath(), terLOCKED);
351 usdm.set({.holder = bob, .flags = tfMPTUnlock});
352 test(env, usd, std::nullopt, STPath(), tesSUCCESS);
353 }
354
355 {
356 // check no auth
357 // An account may require authorization to receive MPTs from an
358 // issuer
359 Env env(*this, features);
360 env.fund(XRP(10'000), alice, bob, gw);
361 auto usdm = MPTTester(
362 {.env = env,
363 .issuer = gw,
364 .flags = kMptDexFlags | tfMPTRequireAuth,
365 .maxAmt = 1'000});
366 MPT const usd = usdm;
367
368 // Authorize alice but not bob
369 usdm.authorize({.account = alice});
370 usdm.authorize({.holder = alice});
371 env(pay(gw, alice, usd(100)));
372 env.require(Balance(alice, usd(100)));
373 test(env, usd, std::nullopt, STPath(), tecNO_AUTH);
374
375 // Check pure issue redeem still works
376 auto [ter, strand] = toStrand(
377 *env.current(),
378 alice,
379 gw,
380 usd,
381 std::nullopt,
382 std::nullopt,
383 STPath(),
384 true,
386 ammContext,
387 std::nullopt,
388 env.app().getLogs().journal("Flow"));
389 BEAST_EXPECT(ter == tesSUCCESS);
390 BEAST_EXPECT(equal(strand, M{alice, gw, usd}));
391 }
392
393 {
394 // last step xrp from offer
395 Env env(*this, features);
396 env.fund(XRP(10'000), alice, bob, gw);
397 MPT const usd =
398 MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}, .maxAmt = 1'000});
399 env(pay(gw, alice, usd(100)));
400
401 // alice -> USD/XRP -> bob
402 STPath path;
403 path.emplaceBack(std::nullopt, xrpCurrency(), std::nullopt);
404
405 auto [ter, strand] = toStrand(
406 *env.current(),
407 alice,
408 bob,
409 XRP,
410 std::nullopt,
411 usd,
412 path,
413 false,
415 ammContext,
416 std::nullopt,
417 env.app().getLogs().journal("Flow"));
418 BEAST_EXPECT(ter == tesSUCCESS);
419 BEAST_EXPECT(
420 equal(strand, M{alice, gw, usd}, B{usd, xrpIssue(), std::nullopt}, XRPS{bob}));
421 }
422 }
423
424 void
426 {
427 using namespace jtx;
428 testcase("RIPD1373");
429
430 auto const alice = Account("alice");
431 auto const bob = Account("bob");
432 auto const carol = Account("carol");
433 auto const gw = Account("gw");
434
435 {
436 Env env(*this, features);
437
438 env.fund(XRP(10000), alice, bob, carol, gw);
439 MPT const usd = MPTTester(
440 {.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
441
442 env(pay(gw, bob, usd(100)));
443
444 env(offer(bob, XRP(100), usd(100)), Txflags(tfPassive));
445 env(offer(bob, usd(100), XRP(100)), Txflags(tfPassive));
446
447 // payment path: XRP -> XRP/USD -> USD/XRP
448 env(pay(alice, carol, XRP(100)),
449 Path(~usd, ~XRP),
450 Txflags(tfNoRippleDirect),
452 }
453
454 {
455 Env env(*this, features);
456
457 env.fund(XRP(10000), alice, bob, carol, gw);
458 MPT const usd = MPTTester(
459 {.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
460
461 env(pay(gw, bob, usd(100)));
462
463 env(offer(bob, XRP(100), usd(100)), Txflags(tfPassive));
464 env(offer(bob, usd(100), XRP(100)), Txflags(tfPassive));
465
466 // payment path: XRP -> XRP/USD -> USD/XRP
467 env(pay(alice, carol, XRP(100)),
468 Path(~usd, ~XRP),
469 Sendmax(XRP(200)),
470 Txflags(tfNoRippleDirect),
472 }
473 }
474
475 void
477 {
478 testcase("test loop");
479 using namespace jtx;
480
481 auto const alice = Account("alice");
482 auto const bob = Account("bob");
483 auto const carol = Account("carol");
484 auto const gw = Account("gw");
485 auto const eur = gw["EUR"];
486 auto const cny = gw["CNY"];
487
488 {
489 Env env(*this, features);
490
491 env.fund(XRP(10'000), alice, bob, carol, gw);
492 MPT const usd = MPTTester(
493 {.env = env, .issuer = gw, .holders = {alice, bob, carol}, .maxAmt = 10'000});
494
495 env(pay(gw, bob, usd(100)));
496 env(pay(gw, alice, usd(100)));
497
498 env(offer(bob, XRP(100), usd(100)), Txflags(tfPassive));
499 env(offer(bob, usd(100), XRP(100)), Txflags(tfPassive));
500
501 // payment path: USD -> USD/XRP -> XRP/USD
502 env(pay(alice, carol, usd(100)),
503 Sendmax(usd(100)),
504 Path(~XRP, ~usd),
505 Txflags(tfNoRippleDirect),
507 }
508 {
509 auto testMultiToken = [&](auto&& issue1, auto&& issue2, auto&& issue3) {
510 Env env(*this, features);
511
512 env.fund(XRP(10'000), alice, bob, carol, gw);
513 auto const usd = issue1(
514 {.env = env,
515 .token = "USD",
516 .issuer = gw,
517 .holders = {alice, bob, carol},
518 .limit = 10'000});
519 auto const eur = issue2(
520 {.env = env,
521 .token = "EUR",
522 .issuer = gw,
523 .holders = {alice, bob, carol},
524 .limit = 10'000});
525 auto const cny = issue3(
526 {.env = env,
527 .token = "CNY",
528 .issuer = gw,
529 .holders = {alice, bob, carol},
530 .limit = 10'000});
531
532 env(pay(gw, bob, usd(100)));
533 env(pay(gw, bob, eur(100)));
534 env(pay(gw, bob, cny(100)));
535
536 env(offer(bob, XRP(100), usd(100)), Txflags(tfPassive));
537 env(offer(bob, usd(100), eur(100)), Txflags(tfPassive));
538 env(offer(bob, eur(100), cny(100)), Txflags(tfPassive));
539
540 // payment path: XRP->XRP/USD->USD/EUR->USD/CNY
541 env(pay(alice, carol, cny(100)),
542 Sendmax(XRP(100)),
543 Path(~usd, ~eur, ~usd, ~cny),
544 Txflags(tfNoRippleDirect),
546 };
547 testHelper3TokensMix(testMultiToken);
548 }
549 }
550
551 void
553 {
554 testcase("test no account");
555 using namespace jtx;
556
557 auto const alice = Account("alice");
558 auto const bob = Account("bob");
559 auto const gw = Account("gw");
560
561 Env env(*this, features);
562 env.fund(XRP(10'000), alice, bob, gw);
563 MPT const usd = MPTTester({.env = env, .issuer = gw, .holders = {alice, bob}});
564
565 STAmount const sendMax{usd, 100, 1};
566 STAmount const noAccountAmount{MPTIssue{0, noAccount()}, 100, 1};
567 STAmount const deliver;
568 AccountID const srcAcc = alice.id();
569 AccountID const dstAcc = bob.id();
570 STPathSet const pathSet;
572 inputs.defaultPathsAllowed = true;
573 try
574 {
575 PaymentSandbox sb{env.current().get(), TapNone};
576 {
578 sb,
579 sendMax,
580 deliver,
581 dstAcc,
582 noAccount(),
583 pathSet,
584 std::nullopt,
585 env.app(),
586 &inputs);
587 BEAST_EXPECT(r.result() == temBAD_PATH);
588 }
589 {
591 sb,
592 sendMax,
593 deliver,
594 noAccount(),
595 srcAcc,
596 pathSet,
597 std::nullopt,
598 env.app(),
599 &inputs);
600 BEAST_EXPECT(r.result() == temBAD_PATH);
601 }
602 {
604 sb,
605 noAccountAmount,
606 deliver,
607 dstAcc,
608 srcAcc,
609 pathSet,
610 std::nullopt,
611 env.app(),
612 &inputs);
613 BEAST_EXPECT(r.result() == temBAD_PATH);
614 }
615 {
617 sb,
618 sendMax,
619 noAccountAmount,
620 dstAcc,
621 srcAcc,
622 pathSet,
623 std::nullopt,
624 env.app(),
625 &inputs);
626 BEAST_EXPECT(r.result() == temBAD_PATH);
627 }
628 }
629 catch (...)
630 {
631 this->fail();
632 }
633 }
634
635 void
636 run() override
637 {
638 using namespace jtx;
639 auto const sa = testableAmendments();
640 testToStrand(sa);
641
642 testRIPD1373(sa);
643
644 testLoop(sa);
645
646 testNoAccount(sa);
647 }
648};
649
650BEAST_DEFINE_TESTSUITE(PayStrandMPT, app, xrpl);
651
652} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:522
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Maintains AMM info per overall payment engine execution and individual iteration.
Definition AMMContext.h:16
Specifies an order book.
Definition Book.h:16
beast::Journal journal(std::string const &name)
Definition Log.cpp:137
A wrapper which makes credits unavailable to balances.
virtual Logs & getLogs()=0
static Output rippleCalculate(PaymentSandbox &view, STAmount const &saMaxAmountReq, STAmount const &saDstAmountReq, AccountID const &uDstAccountID, AccountID const &uSrcAccountID, STPathSet const &spsPaths, std::optional< uint256 > const &domainID, ServiceRegistry &registry, Input const *const pInputs=nullptr)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
A transaction testing environment.
Definition Env.h:143
Application & app()
Definition Env.h:280
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
PrettyAmount limit(Account const &account, Issue const &issue) const
Returns the IOU limit on an account.
Definition Env.cpp:254
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
Definition Env.cpp:327
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:605
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:353
Converts to IOU Issue or STAmount.
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
Definition mpt.h:385
void authorizeHolders(Holders const &holders)
Definition mpt.cpp:430
Converts to MPT Issue or STAmount.
xrpl::MPTID const & mpt() const
Add a path.
Definition paths.h:39
Sets the SendMax on a JTx.
Definition sendmax.h:13
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:13
Set the flags on a JTx.
Definition txflags.h:9
T forward(T... args)
T is_same_v
auto const kMptDexFlags
Definition mpt.h:25
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
void testHelper2TokensMix(TTester &&tester)
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
FeatureBitset testableAmendments()
Definition Env.h:76
void testHelper3TokensMix(TTester &&tester)
STPathElement cpe(PathAsset const &pa)
STPathElement ipe(Asset const &asset)
bool equal(STAmount const &sa1, STAmount const &sa2)
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition offer.cpp:14
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_LINE
Definition TER.h:211
@ terLOCKED
Definition TER.h:223
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
std::pair< TER, Strand > toStrand(ReadView const &sb, AccountID const &src, AccountID const &dst, Asset const &deliver, std::optional< Quality > const &limitQuality, std::optional< Asset > const &sendMaxAsset, STPath const &path, bool ownerPaysTransferFee, OfferCrossing offerCrossing, AMMContext &ammContext, std::optional< uint256 > const &domainID, beast::Journal j)
Create a Strand for the specified path.
Definition PaySteps.cpp:170
Currency const & xrpCurrency()
XRP currency.
Definition UintTypes.cpp:99
@ TapNone
Definition ApplyView.h:13
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
AccountID const & noAccount()
A placeholder for empty accounts.
@ temBAD_PATH
Definition TER.h:82
@ temBAD_SEND_XRP_PATHS
Definition TER.h:89
@ temBAD_SEND_XRP_MAX
Definition TER.h:86
@ temBAD_PATH_LOOP
Definition TER.h:83
TERSubset< CanCvtToTER > TER
Definition TER.h:634
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecPATH_DRY
Definition TER.h:292
@ tecNO_AUTH
Definition TER.h:298
@ tesSUCCESS
Definition TER.h:240
void testToStrand(FeatureBitset features)
void testNoAccount(FeatureBitset features)
static jtx::DirectStepInfo makeEndpointStep(jtx::Account const &src, jtx::Account const &dst, jtx::IOU const &iou)
static jtx::MPTEndpointStepInfo makeEndpointStep(jtx::Account const &src, jtx::Account const &dst, jtx::MPT const &mpt)
void testLoop(FeatureBitset features)
void testRIPD1373(FeatureBitset features)
void run() override
Runs the suite.