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{
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, ripple);
622} // namespace test
623} // namespace ripple
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: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)
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
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...
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