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