xrpld
Loading...
Searching...
No Matches
TrustSet_test.cpp
1
2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h> // IWYU pragma: keep
6#include <test/jtx/flags.h>
7#include <test/jtx/pay.h>
8#include <test/jtx/require.h>
9#include <test/jtx/ter.h>
10#include <test/jtx/ticket.h>
11#include <test/jtx/trust.h>
12#include <test/jtx/txflags.h>
13
14#include <xrpl/beast/unit_test/suite.h>
15#include <xrpl/json/json_value.h>
16#include <xrpl/json/to_string.h>
17#include <xrpl/protocol/AccountID.h>
18#include <xrpl/protocol/Feature.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/TxFlags.h>
21#include <xrpl/protocol/UintTypes.h>
22#include <xrpl/protocol/jss.h>
23
24#include <cstdint>
25#include <limits>
26
27namespace xrpl::test {
28
30{
31public:
32 void
34 {
35 testcase("Test deletion of trust lines: revert trust line limit to zero");
36
37 using namespace jtx;
38 Env env(*this);
39
40 Account const alice = Account{"alice"};
41 Account const becky = Account{"becky"};
42
43 env.fund(XRP(10000), becky, alice);
44 env.close();
45
46 // becky wants to hold at most 50 tokens of alice["USD"]
47 // becky is the customer, alice is the issuer
48 // becky can be sent at most 50 tokens of alice's USD
49 env(trust(becky, alice["USD"](50)));
50 env.close();
51
52 // Since the settings of the trust lines are non-default for both
53 // alice and becky, both of them will be charged an owner reserve
54 // Irrespective of whether the issuer or the customer initiated
55 // the trust-line creation, both will be charged
56 env.require(lines(alice, 1));
57 env.require(lines(becky, 1));
58
59 // Fetch the trust-lines via RPC for verification
60 json::Value jv;
61 jv["account"] = becky.human();
62 auto beckyLines = env.rpc("json", "account_lines", to_string(jv));
63
64 jv["account"] = alice.human();
65 auto aliceLines = env.rpc("json", "account_lines", to_string(jv));
66
67 BEAST_EXPECT(aliceLines[jss::result][jss::lines].size() == 1);
68 BEAST_EXPECT(beckyLines[jss::result][jss::lines].size() == 1);
69
70 // reset the trust line limits to zero
71 env(trust(becky, alice["USD"](0)));
72 env.close();
73
74 // the reset of the trust line limits deletes the trust-line
75 // this occurs despite the authorization of the trust-line by the
76 // issuer(alice, in this unit test)
77 env.require(lines(becky, 0));
78 env.require(lines(alice, 0));
79
80 // second verification check via RPC calls
81 jv["account"] = becky.human();
82 beckyLines = env.rpc("json", "account_lines", to_string(jv));
83
84 jv["account"] = alice.human();
85 aliceLines = env.rpc("json", "account_lines", to_string(jv));
86
87 BEAST_EXPECT(aliceLines[jss::result][jss::lines].size() == 0);
88 BEAST_EXPECT(beckyLines[jss::result][jss::lines].size() == 0);
89
90 // additionally, verify that account_objects is an empty array
91 jv["account"] = becky.human();
92 auto const beckyObj = env.rpc("json", "account_objects", to_string(jv));
93 BEAST_EXPECT(beckyObj[jss::result][jss::account_objects].size() == 0);
94
95 jv["account"] = alice.human();
96 auto const aliceObj = env.rpc("json", "account_objects", to_string(jv));
97 BEAST_EXPECT(aliceObj[jss::result][jss::account_objects].size() == 0);
98 }
99
100 void
102 {
103 testcase(
104 "Reset trust line limit with Authorised Lines: Verify "
105 "deletion of trust lines");
106
107 using namespace jtx;
108 Env env(*this);
109
110 Account const alice = Account{"alice"};
111 Account const becky = Account{"becky"};
112
113 env.fund(XRP(10000), becky, alice);
114 env.close();
115
116 // alice wants to ensure that all holders of her tokens are authorised
117 env(fset(alice, asfRequireAuth));
118 env.close();
119
120 // becky wants to hold at most 50 tokens of alice["USD"]
121 // becky is the customer, alice is the issuer
122 // becky can be sent at most 50 tokens of alice's USD
123 env(trust(becky, alice["USD"](50)));
124 env.close();
125
126 // alice authorizes becky to hold alice["USD"] tokens
127 env(trust(alice, alice["USD"](0), becky, tfSetfAuth));
128 env.close();
129
130 // Since the settings of the trust lines are non-default for both
131 // alice and becky, both of them will be charged an owner reserve
132 // Irrespective of whether the issuer or the customer initiated
133 // the trust-line creation, both will be charged
134 env.require(lines(alice, 1));
135 env.require(lines(becky, 1));
136
137 // Fetch the trust-lines via RPC for verification
138 json::Value jv;
139 jv["account"] = becky.human();
140 auto beckyLines = env.rpc("json", "account_lines", to_string(jv));
141
142 jv["account"] = alice.human();
143 auto aliceLines = env.rpc("json", "account_lines", to_string(jv));
144
145 BEAST_EXPECT(aliceLines[jss::result][jss::lines].size() == 1);
146 BEAST_EXPECT(beckyLines[jss::result][jss::lines].size() == 1);
147
148 // reset the trust line limits to zero
149 env(trust(becky, alice["USD"](0)));
150 env.close();
151
152 // the reset of the trust line limits deletes the trust-line
153 // this occurs despite the authorization of the trust-line by the
154 // issuer(alice, in this unit test)
155 env.require(lines(becky, 0));
156 env.require(lines(alice, 0));
157
158 // second verification check via RPC calls
159 jv["account"] = becky.human();
160 beckyLines = env.rpc("json", "account_lines", to_string(jv));
161
162 jv["account"] = alice.human();
163 aliceLines = env.rpc("json", "account_lines", to_string(jv));
164
165 BEAST_EXPECT(aliceLines[jss::result][jss::lines].size() == 0);
166 BEAST_EXPECT(beckyLines[jss::result][jss::lines].size() == 0);
167 }
168
169 void
170 testFreeTrustlines(FeatureBitset features, bool thirdLineCreatesLE, bool createOnHighAcct)
171 {
172 if (thirdLineCreatesLE)
173 {
174 testcase("Allow two free trustlines");
175 }
176 else
177 {
178 testcase("Dynamic reserve for trustline");
179 }
180
181 using namespace jtx;
182 Env env(*this, features);
183
184 auto const gwA = Account{"gwA"};
185 auto const gwB = Account{"gwB"};
186 auto const acctC = Account{"acctC"};
187 auto const acctD = Account{"acctD"};
188
189 auto const& creator = createOnHighAcct ? acctD : acctC;
190 auto const& assistor = createOnHighAcct ? acctC : acctD;
191
192 auto const txFee = env.current()->fees().base;
193 auto const baseReserve = env.current()->fees().reserve;
194 auto const threelineReserve = env.current()->fees().accountReserve(3);
195
196 env.fund(XRP(10000), gwA, gwB, assistor);
197
198 // Fund creator with ...
199 env.fund(
200 baseReserve /* enough to hold an account */
201 + drops(3 * txFee) /* and to pay for 3 transactions */,
202 creator);
203
204 env(trust(creator, gwA["USD"](100)), Require(lines(creator, 1)));
205 env(trust(creator, gwB["USD"](100)), Require(lines(creator, 2)));
206
207 if (thirdLineCreatesLE)
208 {
209 // creator does not have enough for the third trust line
210 env(trust(creator, assistor["USD"](100)),
212 Require(lines(creator, 2)));
213 }
214 else
215 {
216 // First establish opposite trust direction from assistor
217 env(trust(assistor, creator["USD"](100)), Require(lines(creator, 3)));
218
219 // creator does not have enough to create the other direction on
220 // the existing trust line ledger entry
221 env(trust(creator, assistor["USD"](100)), Ter(tecINSUF_RESERVE_LINE));
222 }
223
224 // Fund creator additional amount to cover
225 env(pay(env.master, creator, STAmount{threelineReserve - baseReserve}));
226
227 if (thirdLineCreatesLE)
228 {
229 env(trust(creator, assistor["USD"](100)), Require(lines(creator, 3)));
230 }
231 else
232 {
233 env(trust(creator, assistor["USD"](100)), Require(lines(creator, 3)));
234
235 json::Value jv;
236 jv["account"] = creator.human();
237 auto const lines = env.rpc("json", "account_lines", to_string(jv));
238 // Verify that all lines have 100 limit from creator
239 BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
240 BEAST_EXPECT(lines[jss::result][jss::lines].size() == 3);
241 for (auto const& line : lines[jss::result][jss::lines])
242 {
243 BEAST_EXPECT(line[jss::limit] == "100");
244 }
245 }
246 }
247
248 void
250 {
251 testcase("TrustSet using a ticket");
252
253 using namespace jtx;
254
255 // Verify that TrustSet transactions can use tickets.
256 Env env{*this, features};
257 auto const gw = Account{"gateway"};
258 auto const alice = Account{"alice"};
259 auto const usd = gw["USD"];
260
261 env.fund(XRP(10000), gw, alice);
262 env.close();
263
264 // Cannot pay alice without a trustline.
265 env(pay(gw, alice, usd(200)), Ter(tecPATH_DRY));
266 env.close();
267
268 // Create a ticket.
269 std::uint32_t const ticketSeq{env.seq(alice) + 1};
270 env(ticket::create(alice, 1));
271 env.close();
272
273 // Use that ticket to create a trust line.
274 env(trust(alice, usd(1000)), ticket::Use(ticketSeq));
275 env.close();
276
277 // Now the payment succeeds.
278 env(pay(gw, alice, usd(200)));
279 env.close();
280 }
281
282 static json::Value
284 {
285 json::Value jv;
286 jv[jss::Account] = a.human();
287 jv[jss::LimitAmount] = amt.getJson(JsonOptions::Values::None);
288 jv[jss::TransactionType] = jss::TrustSet;
289 jv[jss::Flags] = 0;
290 return jv;
291 }
292
293 void
295 {
296 testcase("TrustSet checks for malformed transactions");
297
298 using namespace jtx;
299 Env env{*this, features};
300
301 auto const gw = Account{"gateway"};
302 auto const alice = Account{"alice"};
303 env.fund(XRP(10000), gw, alice);
304
305 // Require valid tf flags
306 for (std::uint64_t badFlag = 1u; badFlag <= std::numeric_limits<std::uint32_t>::max();
307 badFlag *= 2)
308 {
309 if ((badFlag & tfTrustSetMask) != 0u)
310 {
311 env(trust(alice, gw["USD"](100), static_cast<std::uint32_t>(badFlag)),
313 }
314 }
315
316 // trust amount can't be XRP
317 env(trustExplicitAmt(alice, drops(10000)), Ter(temBAD_LIMIT));
318
319 // trust amount can't be badCurrency IOU
320 env(trustExplicitAmt(alice, gw[to_string(badCurrency())](100)), Ter(temBAD_CURRENCY));
321
322 // trust amount can't be negative
323 env(trust(alice, gw["USD"](-1000)), Ter(temBAD_LIMIT));
324
325 // trust amount can't be from invalid issuer
326 env(trustExplicitAmt(alice, STAmount{Issue{toCurrency("USD"), noAccount()}, 100}),
328
329 // trust cannot be to self
330 env(trust(alice, alice["USD"](100)), Ter(temDST_IS_SRC));
331
332 // tfSetAuth flag should not be set if not required by lsfRequireAuth
333 env(trust(alice, gw["USD"](100), tfSetfAuth), Ter(tefNO_AUTH_REQUIRED));
334 }
335
336 void
338 {
339 testcase(
340 "Ensure that trust line limits are respected in payment "
341 "transactions");
342
343 using namespace jtx;
344 Env env{*this};
345
346 auto const gw = Account{"gateway"};
347 auto const alice = Account{"alice"};
348 env.fund(XRP(10000), gw, alice);
349
350 // alice wants to hold at most 100 of gw's USD tokens
351 env(trust(alice, gw["USD"](100)));
352 env.close();
353
354 // send a payment for a large quantity through the trust line
355 env(pay(gw, alice, gw["USD"](200)), Ter(tecPATH_PARTIAL));
356 env.close();
357
358 // on the other hand, smaller payments should succeed
359 env(pay(gw, alice, gw["USD"](20)));
360 env.close();
361 }
362
363 void
365 {
366 testcase(
367 "Ensure that authorised trust lines do not allow payments "
368 "from unauthorised counter-parties");
369
370 using namespace jtx;
371 Env env{*this};
372
373 auto const bob = Account{"bob"};
374 auto const alice = Account{"alice"};
375 env.fund(XRP(10000), bob, alice);
376
377 // alice wants to ensure that all holders of her tokens are authorised
378 env(fset(alice, asfRequireAuth));
379 env.close();
380
381 // create a trust line from bob to alice. bob wants to hold at most
382 // 100 of alice's USD tokens. Note: alice hasn't authorised this
383 // trust line yet.
384 env(trust(bob, alice["USD"](100)));
385 env.close();
386
387 // send a payment from alice to bob, validate that the payment fails
388 env(pay(alice, bob, alice["USD"](10)), Ter(tecPATH_DRY));
389 env.close();
390 }
391
392 void
394 {
395 testcase(
396 "Check that trust line limits are respected in conjunction "
397 "with rippling feature");
398
399 using namespace jtx;
400 Env env{*this};
401
402 auto const bob = Account{"bob"};
403 auto const alice = Account{"alice"};
404 env.fund(XRP(10000), bob, alice);
405
406 // create a trust line from bob to alice. bob wants to hold at most
407 // 100 of alice's USD tokens.
408 env(trust(bob, alice["USD"](100)));
409 env.close();
410
411 // archetypical payment transaction from alice to bob must succeed
412 env(pay(alice, bob, alice["USD"](20)), Ter(tesSUCCESS));
413 env.close();
414
415 // Issued tokens are fungible. i.e. alice's USD is identical to bob's
416 // USD
417 env(pay(bob, alice, bob["USD"](10)), Ter(tesSUCCESS));
418 env.close();
419
420 // bob cannot place alice in his debt i.e. alice's balance of the USD
421 // tokens cannot go below zero.
422 env(pay(bob, alice, bob["USD"](11)), Ter(tecPATH_PARTIAL));
423 env.close();
424
425 // payments that respect the trust line limits of alice should succeed
426 env(pay(bob, alice, bob["USD"](10)), Ter(tesSUCCESS));
427 env.close();
428 }
429
430 void
431 testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct)
432 {
433 testcase << "TrustSet " << (createQuality ? "creates" : "removes")
434 << " quality of trustline for " << (createOnHighAcct ? "high" : "low")
435 << " account";
436
437 using namespace jtx;
438 Env env{*this, features};
439
440 auto const alice = Account{"alice"};
441 auto const bob = Account{"bob"};
442
443 auto const& fromAcct = createOnHighAcct ? alice : bob;
444 auto const& toAcct = createOnHighAcct ? bob : alice;
445
446 env.fund(XRP(10000), fromAcct, toAcct);
447
448 auto txWithoutQuality = trust(toAcct, fromAcct["USD"](100));
449 txWithoutQuality["QualityIn"] = "0";
450 txWithoutQuality["QualityOut"] = "0";
451
452 auto txWithQuality = txWithoutQuality;
453 txWithQuality["QualityIn"] = "1000";
454 txWithQuality["QualityOut"] = "1000";
455
456 auto& tx1 = createQuality ? txWithQuality : txWithoutQuality;
457 auto& tx2 = createQuality ? txWithoutQuality : txWithQuality;
458
459 auto checkQuality = [&](bool const exists) {
460 json::Value jv;
461 jv["account"] = toAcct.human();
462 auto const lines = env.rpc("json", "account_lines", to_string(jv));
463 auto quality = exists ? 1000 : 0;
464 BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
465 BEAST_EXPECT(lines[jss::result][jss::lines].size() == 1);
466 BEAST_EXPECT(lines[jss::result][jss::lines][0u][jss::quality_in] == quality);
467 BEAST_EXPECT(lines[jss::result][jss::lines][0u][jss::quality_out] == quality);
468 };
469
470 env(tx1, Require(lines(toAcct, 1)), Require(lines(fromAcct, 1)));
471 checkQuality(createQuality);
472
473 env(tx2, Require(lines(toAcct, 1)), Require(lines(fromAcct, 1)));
474 checkQuality(!createQuality);
475 }
476
477 void
479 {
480 testcase("Create trustline with disallow incoming");
481
482 using namespace test::jtx;
483
484 // fixDisallowIncomingV1
485 {
486 for (bool const withFix : {true, false})
487 {
488 auto const amend = withFix ? features : features - fixDisallowIncomingV1;
489
490 Env env{*this, amend};
491 auto const dist = Account("dist");
492 auto const gw = Account("gw");
493 auto const usd = gw["USD"];
494 auto const distUSD = dist["USD"];
495
496 env.fund(XRP(1000), gw, dist);
497 env.close();
498
499 env(fset(gw, asfRequireAuth));
500 env.close();
501
502 env(fset(dist, asfDisallowIncomingTrustline));
503 env.close();
504
505 env(trust(dist, usd(10000)));
506 env.close();
507
508 // withFix: can set trustline
509 // withOutFix: cannot set trustline
510 auto const trustResult = withFix ? Ter(tesSUCCESS) : Ter(tecNO_PERMISSION);
511 env(trust(gw, distUSD(10000)), Txflags(tfSetfAuth), trustResult);
512 env.close();
513
514 auto const txResult = withFix ? Ter(tesSUCCESS) : Ter(tecPATH_DRY);
515 env(pay(gw, dist, usd(1000)), txResult);
516 env.close();
517 }
518 }
519
520 Env env{*this, features};
521
522 auto const gw = Account{"gateway"};
523 auto const alice = Account{"alice"};
524 auto const bob = Account{"bob"};
525 auto const usd = gw["USD"];
526
527 env.fund(XRP(10000), gw, alice, bob);
528 env.close();
529
530 // Set flag on gateway
531 env(fset(gw, asfDisallowIncomingTrustline));
532 env.close();
533
534 // Create a trustline which will fail
535 env(trust(alice, usd(1000)), Ter(tecNO_PERMISSION));
536 env.close();
537
538 // Unset the flag
539 env(fclear(gw, asfDisallowIncomingTrustline));
540 env.close();
541
542 // Create a trustline which will now succeed
543 env(trust(alice, usd(1000)));
544 env.close();
545
546 // Now the payment succeeds.
547 env(pay(gw, alice, usd(200)));
548 env.close();
549
550 // Set flag on gateway again
551 env(fset(gw, asfDisallowIncomingTrustline));
552 env.close();
553
554 // Destroy the balance by sending it back
555 env(pay(gw, alice, usd(200)));
556 env.close();
557
558 // The trustline still exists in default state
559 // So a further payment should work
560 env(pay(gw, alice, usd(200)));
561 env.close();
562
563 // Also set the flag on bob
564 env(fset(bob, asfDisallowIncomingTrustline));
565 env.close();
566
567 // But now bob can't open a trustline because he didn't already have one
568 env(trust(bob, usd(1000)), Ter(tecNO_PERMISSION));
569 env.close();
570
571 // The gateway also can't open this trustline because bob has the flag
572 // set
573 env(trust(gw, bob["USD"](1000)), Ter(tecNO_PERMISSION));
574 env.close();
575
576 // Unset the flag only on the gateway
577 env(fclear(gw, asfDisallowIncomingTrustline));
578 env.close();
579
580 // Now bob can open a trustline
581 env(trust(bob, usd(1000)));
582 env.close();
583
584 // And the gateway can send bob a balance
585 env(pay(gw, bob, usd(200)));
586 env.close();
587 }
588
589 void
591 {
592 testFreeTrustlines(features, true, false);
593 testFreeTrustlines(features, false, true);
594 testFreeTrustlines(features, false, true);
595 // true, true case doesn't matter since creating a trustline ledger
596 // entry requires reserve from the creator
597 // independent of hi/low account ids for endpoints
598 testTicketTrustSet(features);
599 testMalformedTransaction(features);
600 testModifyQualityOfTrustline(features, false, false);
601 testModifyQualityOfTrustline(features, false, true);
602 testModifyQualityOfTrustline(features, true, false);
603 testModifyQualityOfTrustline(features, true, true);
604 testDisallowIncoming(features);
610 }
611
612public:
613 void
614 run() override
615 {
616 using namespace test::jtx;
617 auto const sa = testableAmendments();
618 testWithFeats(sa);
619 }
620};
622} // namespace xrpl::test
A testsuite class.
Definition suite.h:50
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
Represents a JSON value.
Definition json_value.h:130
A currency issued by an account.
Definition Issue.h:13
json::Value getJson(JsonOptions=JsonOptions::Values::None) const override
Definition STAmount.cpp:734
void run() override
Runs the suite.
void testMalformedTransaction(FeatureBitset features)
void testTicketTrustSet(FeatureBitset features)
void testDisallowIncoming(FeatureBitset features)
void testFreeTrustlines(FeatureBitset features, bool thirdLineCreatesLE, bool createOnHighAcct)
void testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct)
void testWithFeats(FeatureBitset features)
static json::Value trustExplicitAmt(jtx::Account const &a, STAmount const &amt)
Immutable cryptographic account descriptor.
Definition jtx/Account.h:17
std::string const & human() const
Returns the human readable public key.
Definition jtx/Account.h:92
A transaction testing environment.
Definition Env.h:143
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:133
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:296
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:275
Account const & master
Definition Env.h:147
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:864
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
Check a set of conditions.
Definition require.h:45
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
Set a ticket sequence on a JTx.
Definition ticket.h:26
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Definition ticket.cpp:16
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:14
XrpT const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:92
OwnerCount< ltRIPPLE_STATE > lines
Match the number of trust lines in the account's owner directory.
Definition owners.h:67
XRPAmount txFee(Env const &env, std::uint16_t n)
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition flags.h:102
FeatureBitset testableAmendments()
Definition Env.h:76
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 fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition flags.cpp:15
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ tefNO_AUTH_REQUIRED
Definition TER.h:164
bool toCurrency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:633
AccountID const & noAccount()
A placeholder for empty accounts.
@ temBAD_CURRENCY
Definition TER.h:76
@ temINVALID_FLAG
Definition TER.h:97
@ temDST_IS_SRC
Definition TER.h:94
@ temDST_NEEDED
Definition TER.h:95
@ temBAD_LIMIT
Definition TER.h:80
@ tecNO_LINE_INSUF_RESERVE
Definition TER.h:290
@ tecPATH_PARTIAL
Definition TER.h:280
@ tecINSUF_RESERVE_LINE
Definition TER.h:286
@ tecPATH_DRY
Definition TER.h:292
@ tecNO_PERMISSION
Definition TER.h:303
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
@ tesSUCCESS
Definition TER.h:240