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