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