rippled
SetTrust_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2016 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 #include <ripple/protocol/TxFlags.h>
20 #include <ripple/protocol/jss.h>
21 #include <test/jtx.h>
22 
23 namespace ripple {
24 
25 namespace test {
26 
27 class SetTrust_test : public beast::unit_test::suite
28 {
30 
31 public:
32  void
34  FeatureBitset features,
35  bool thirdLineCreatesLE,
36  bool createOnHighAcct)
37  {
38  if (thirdLineCreatesLE)
39  testcase("Allow two free trustlines");
40  else
41  testcase("Dynamic reserve for trustline");
42 
43  using namespace jtx;
44  Env env(*this, features);
45 
46  auto const gwA = Account{"gwA"};
47  auto const gwB = Account{"gwB"};
48  auto const acctC = Account{"acctC"};
49  auto const acctD = Account{"acctD"};
50 
51  auto const& creator = createOnHighAcct ? acctD : acctC;
52  auto const& assistor = createOnHighAcct ? acctC : acctD;
53 
54  auto const txFee = env.current()->fees().base;
55  auto const baseReserve = env.current()->fees().accountReserve(0);
56  auto const threelineReserve = env.current()->fees().accountReserve(3);
57 
58  env.fund(XRP(10000), gwA, gwB, assistor);
59 
60  // Fund creator with ...
61  env.fund(
62  baseReserve /* enough to hold an account */
63  + drops(3 * txFee) /* and to pay for 3 transactions */,
64  creator);
65 
66  env(trust(creator, gwA["USD"](100)), require(lines(creator, 1)));
67  env(trust(creator, gwB["USD"](100)), require(lines(creator, 2)));
68 
69  if (thirdLineCreatesLE)
70  {
71  // creator does not have enough for the third trust line
72  env(trust(creator, assistor["USD"](100)),
74  require(lines(creator, 2)));
75  }
76  else
77  {
78  // First establish opposite trust direction from assistor
79  env(trust(assistor, creator["USD"](100)),
80  require(lines(creator, 3)));
81 
82  // creator does not have enough to create the other direction on
83  // the existing trust line ledger entry
84  env(trust(creator, assistor["USD"](100)),
86  }
87 
88  // Fund creator additional amount to cover
89  env(pay(env.master, creator, STAmount{threelineReserve - baseReserve}));
90 
91  if (thirdLineCreatesLE)
92  {
93  env(trust(creator, assistor["USD"](100)),
94  require(lines(creator, 3)));
95  }
96  else
97  {
98  env(trust(creator, assistor["USD"](100)),
99  require(lines(creator, 3)));
100 
101  Json::Value jv;
102  jv["account"] = creator.human();
103  auto const lines = env.rpc("json", "account_lines", to_string(jv));
104  // Verify that all lines have 100 limit from creator
105  BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
106  BEAST_EXPECT(lines[jss::result][jss::lines].size() == 3);
107  for (auto const& line : lines[jss::result][jss::lines])
108  {
109  BEAST_EXPECT(line[jss::limit] == "100");
110  }
111  }
112  }
113 
114  void
116  {
117  testcase("SetTrust using a ticket");
118 
119  using namespace jtx;
120 
121  // Verify that TrustSet transactions can use tickets.
122  Env env{*this, features};
123  auto const gw = Account{"gateway"};
124  auto const alice = Account{"alice"};
125  auto const USD = gw["USD"];
126 
127  env.fund(XRP(10000), gw, alice);
128  env.close();
129 
130  // Cannot pay alice without a trustline.
131  env(pay(gw, alice, USD(200)), ter(tecPATH_DRY));
132  env.close();
133 
134  // Create a ticket.
135  std::uint32_t const ticketSeq{env.seq(alice) + 1};
136  env(ticket::create(alice, 1));
137  env.close();
138 
139  // Use that ticket to create a trust line.
140  env(trust(alice, USD(1000)), ticket::use(ticketSeq));
141  env.close();
142 
143  // Now the payment succeeds.
144  env(pay(gw, alice, USD(200)));
145  env.close();
146  }
147 
150  {
151  Json::Value jv;
152  jv[jss::Account] = a.human();
153  jv[jss::LimitAmount] = amt.getJson(JsonOptions::none);
154  jv[jss::TransactionType] = jss::TrustSet;
155  jv[jss::Flags] = 0;
156  return jv;
157  }
158 
159  void
161  {
162  testcase("SetTrust checks for malformed transactions");
163 
164  using namespace jtx;
165  Env env{*this, features};
166 
167  auto const gw = Account{"gateway"};
168  auto const alice = Account{"alice"};
169  env.fund(XRP(10000), gw, alice);
170 
171  // Require valid tf flags
172  for (std::uint64_t badFlag = 1u;
173  badFlag <= std::numeric_limits<std::uint32_t>::max();
174  badFlag *= 2)
175  {
176  if (badFlag & tfTrustSetMask)
177  env(trust(
178  alice,
179  gw["USD"](100),
180  static_cast<std::uint32_t>(badFlag)),
182  }
183 
184  // trust amount can't be XRP
185  env(trust_explicit_amt(alice, drops(10000)), ter(temBAD_LIMIT));
186 
187  // trust amount can't be badCurrency IOU
188  env(trust_explicit_amt(alice, gw[to_string(badCurrency())](100)),
190 
191  // trust amount can't be negative
192  env(trust(alice, gw["USD"](-1000)), ter(temBAD_LIMIT));
193 
194  // trust amount can't be from invalid issuer
195  env(trust_explicit_amt(
196  alice, STAmount{Issue{to_currency("USD"), noAccount()}, 100}),
197  ter(temDST_NEEDED));
198 
199  // trust cannot be to self
200  env(trust(alice, alice["USD"](100)), ter(temDST_IS_SRC));
201 
202  // tfSetAuth flag should not be set if not required by lsfRequireAuth
203  env(trust(alice, gw["USD"](100), tfSetfAuth), ter(tefNO_AUTH_REQUIRED));
204  }
205 
206  void
208  FeatureBitset features,
209  bool createQuality,
210  bool createOnHighAcct)
211  {
212  testcase << "SetTrust " << (createQuality ? "creates" : "removes")
213  << " quality of trustline for "
214  << (createOnHighAcct ? "high" : "low") << " account";
215 
216  using namespace jtx;
217  Env env{*this, features};
218 
219  auto const alice = Account{"alice"};
220  auto const bob = Account{"bob"};
221 
222  auto const& fromAcct = createOnHighAcct ? alice : bob;
223  auto const& toAcct = createOnHighAcct ? bob : alice;
224 
225  env.fund(XRP(10000), fromAcct, toAcct);
226 
227  auto txWithoutQuality = trust(toAcct, fromAcct["USD"](100));
228  txWithoutQuality["QualityIn"] = "0";
229  txWithoutQuality["QualityOut"] = "0";
230 
231  auto txWithQuality = txWithoutQuality;
232  txWithQuality["QualityIn"] = "1000";
233  txWithQuality["QualityOut"] = "1000";
234 
235  auto& tx1 = createQuality ? txWithQuality : txWithoutQuality;
236  auto& tx2 = createQuality ? txWithoutQuality : txWithQuality;
237 
238  auto check_quality = [&](const bool exists) {
239  Json::Value jv;
240  jv["account"] = toAcct.human();
241  auto const lines = env.rpc("json", "account_lines", to_string(jv));
242  auto quality = exists ? 1000 : 0;
243  BEAST_EXPECT(lines[jss::result][jss::lines].isArray());
244  BEAST_EXPECT(lines[jss::result][jss::lines].size() == 1);
245  BEAST_EXPECT(
246  lines[jss::result][jss::lines][0u][jss::quality_in] == quality);
247  BEAST_EXPECT(
248  lines[jss::result][jss::lines][0u][jss::quality_out] ==
249  quality);
250  };
251 
252  env(tx1, require(lines(toAcct, 1)), require(lines(fromAcct, 1)));
253  check_quality(createQuality);
254 
255  env(tx2, require(lines(toAcct, 1)), require(lines(fromAcct, 1)));
256  check_quality(!createQuality);
257  }
258 
259  void
261  {
262  testcase("Create trustline with disallow incoming");
263 
264  using namespace test::jtx;
265 
266  // test flag doesn't set unless amendment enabled
267  {
268  Env env{*this, features - disallowIncoming};
269  Account const alice{"alice"};
270  env.fund(XRP(10000), alice);
271  env(fset(alice, asfDisallowIncomingTrustline));
272  env.close();
273  auto const sle = env.le(alice);
274  uint32_t flags = sle->getFlags();
275  BEAST_EXPECT(!(flags & lsfDisallowIncomingTrustline));
276  }
277 
278  // fixDisallowIncomingV1
279  {
280  for (bool const withFix : {true, false})
281  {
282  auto const amend = withFix
283  ? features | disallowIncoming
284  : (features | disallowIncoming) - fixDisallowIncomingV1;
285 
286  Env env{*this, amend};
287  auto const dist = Account("dist");
288  auto const gw = Account("gw");
289  auto const USD = gw["USD"];
290  auto const distUSD = dist["USD"];
291 
292  env.fund(XRP(1000), gw, dist);
293  env.close();
294 
295  env(fset(gw, asfRequireAuth));
296  env.close();
297 
299  env.close();
300 
301  env(trust(dist, USD(10000)));
302  env.close();
303 
304  // withFix: can set trustline
305  // withOutFix: cannot set trustline
306  auto const trustResult =
307  withFix ? ter(tesSUCCESS) : ter(tecNO_PERMISSION);
308  env(trust(gw, distUSD(10000)),
310  trustResult);
311  env.close();
312 
313  auto const txResult =
314  withFix ? ter(tesSUCCESS) : ter(tecPATH_DRY);
315  env(pay(gw, dist, USD(1000)), txResult);
316  env.close();
317  }
318  }
319 
320  Env env{*this, features | disallowIncoming};
321 
322  auto const gw = Account{"gateway"};
323  auto const alice = Account{"alice"};
324  auto const bob = Account{"bob"};
325  auto const USD = gw["USD"];
326 
327  env.fund(XRP(10000), gw, alice, bob);
328  env.close();
329 
330  // Set flag on gateway
332  env.close();
333 
334  // Create a trustline which will fail
335  env(trust(alice, USD(1000)), ter(tecNO_PERMISSION));
336  env.close();
337 
338  // Unset the flag
340  env.close();
341 
342  // Create a trustline which will now succeed
343  env(trust(alice, USD(1000)));
344  env.close();
345 
346  // Now the payment succeeds.
347  env(pay(gw, alice, USD(200)));
348  env.close();
349 
350  // Set flag on gateway again
352  env.close();
353 
354  // Destroy the balance by sending it back
355  env(pay(gw, alice, USD(200)));
356  env.close();
357 
358  // The trustline still exists in default state
359  // So a further payment should work
360  env(pay(gw, alice, USD(200)));
361  env.close();
362 
363  // Also set the flag on bob
365  env.close();
366 
367  // But now bob can't open a trustline because he didn't already have one
368  env(trust(bob, USD(1000)), ter(tecNO_PERMISSION));
369  env.close();
370 
371  // The gateway also can't open this trustline because bob has the flag
372  // set
373  env(trust(gw, bob["USD"](1000)), ter(tecNO_PERMISSION));
374  env.close();
375 
376  // Unset the flag only on the gateway
378  env.close();
379 
380  // Now bob can open a trustline
381  env(trust(bob, USD(1000)));
382  env.close();
383 
384  // And the gateway can send bob a balance
385  env(pay(gw, bob, USD(200)));
386  env.close();
387  }
388 
389  void
391  {
392  testFreeTrustlines(features, true, false);
393  testFreeTrustlines(features, false, true);
394  testFreeTrustlines(features, false, true);
395  // true, true case doesn't matter since creating a trustline ledger
396  // entry requires reserve from the creator
397  // independent of hi/low account ids for endpoints
398  testTicketSetTrust(features);
399  testMalformedTransaction(features);
400  testModifyQualityOfTrustline(features, false, false);
401  testModifyQualityOfTrustline(features, false, true);
402  testModifyQualityOfTrustline(features, true, false);
403  testModifyQualityOfTrustline(features, true, true);
404  testDisallowIncoming(features);
405  }
406 
407 public:
408  void
409  run() override
410  {
411  using namespace test::jtx;
412  auto const sa = supported_amendments();
414  testWithFeats(sa);
415  }
416 };
418 } // namespace test
419 } // namespace ripple
ripple::badCurrency
Currency const & badCurrency()
We deliberately disallow the currency that looks like "XRP" because too many people were using it ins...
Definition: UintTypes.cpp:129
ripple::to_currency
bool to_currency(Currency &currency, std::string const &code)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:80
ripple::test::SetTrust_test::testModifyQualityOfTrustline
void testModifyQualityOfTrustline(FeatureBitset features, bool createQuality, bool createOnHighAcct)
Definition: SetTrust_test.cpp:207
ripple::test::jtx::XRP
const XRP_t XRP
Converts to XRP Issue or STAmount.
Definition: amount.cpp:105
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
ripple::tecINSUF_RESERVE_LINE
@ tecINSUF_RESERVE_LINE
Definition: TER.h:269
ripple::test::SetTrust_test::testWithFeats
void testWithFeats(FeatureBitset features)
Definition: SetTrust_test.cpp:390
ripple::tfTrustSetMask
constexpr std::uint32_t tfTrustSetMask
Definition: TxFlags.h:114
ripple::test::jtx::drops
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Definition: amount.h:241
ripple::temBAD_CURRENCY
@ temBAD_CURRENCY
Definition: TER.h:89
ripple::test::jtx::ter
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:33
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:653
ripple::test::jtx::require
Check a set of conditions.
Definition: require.h:63
ripple::test::jtx::Account::human
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
ripple::test::SetTrust_test::trust_explicit_amt
Json::Value trust_explicit_amt(jtx::Account const &a, STAmount const &amt)
Definition: SetTrust_test.cpp:149
ripple::test::SetTrust_test::disallowIncoming
const FeatureBitset disallowIncoming
Definition: SetTrust_test.cpp:29
ripple::test::SetTrust_test::testTicketSetTrust
void testTicketSetTrust(FeatureBitset features)
Definition: SetTrust_test.cpp:115
ripple::temDST_IS_SRC
@ temDST_IS_SRC
Definition: TER.h:107
ripple::test::SetTrust_test::testFreeTrustlines
void testFreeTrustlines(FeatureBitset features, bool thirdLineCreatesLE, bool createOnHighAcct)
Definition: SetTrust_test.cpp:33
ripple::asfDisallowIncomingTrustline
constexpr std::uint32_t asfDisallowIncomingTrustline
Definition: TxFlags.h:90
ripple::test::SetTrust_test::run
void run() override
Definition: SetTrust_test.cpp:409
ripple::test::jtx::ticket::use
Set a ticket sequence on a JTx.
Definition: ticket.h:47
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:110
ripple::test::SetTrust_test
Definition: SetTrust_test.cpp:27
ripple::featureDisallowIncoming
const uint256 featureDisallowIncoming
ripple::temBAD_LIMIT
@ temBAD_LIMIT
Definition: TER.h:93
ripple::JsonOptions::none
@ none
ripple::test::SetTrust_test::testDisallowIncoming
void testDisallowIncoming(FeatureBitset features)
Definition: SetTrust_test.cpp:260
ripple::temDST_NEEDED
@ temDST_NEEDED
Definition: TER.h:108
ripple::SetTrust
Definition: SetTrust.h:31
ripple::test::jtx::fset
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Definition: flags.cpp:28
ripple::test::jtx::txflags
Set the flags on a JTx.
Definition: txflags.h:30
ripple::STAmount
Definition: STAmount.h:46
ripple::test::SetTrust_test::testMalformedTransaction
void testMalformedTransaction(FeatureBitset features)
Definition: SetTrust_test.cpp:160
ripple::test::jtx::supported_amendments
FeatureBitset supported_amendments()
Definition: Env.h:71
std::uint32_t
ripple::test::jtx::fclear
Json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
Definition: flags.h:40
ripple::fixDisallowIncomingV1
const uint256 fixDisallowIncomingV1
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::tefNO_AUTH_REQUIRED
@ tefNO_AUTH_REQUIRED
Definition: TER.h:168
ripple::test::jtx::flags
Match set account flags.
Definition: flags.h:111
ripple::tecNO_LINE_INSUF_RESERVE
@ tecNO_LINE_INSUF_RESERVE
Definition: TER.h:273
ripple::tfSetfAuth
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:109
ripple::asfRequireAuth
constexpr std::uint32_t asfRequireAuth
Definition: TxFlags.h:75
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::lsfDisallowIncomingTrustline
@ lsfDisallowIncomingTrustline
Definition: LedgerFormats.h:275
ripple::test::jtx::Env::master
Account const & master
Definition: Env.h:122
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:286
ripple::FeatureBitset
Definition: Feature.h:113
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:275
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::test::jtx::Account
Immutable cryptographic account descriptor.
Definition: Account.h:37
ripple::tesSUCCESS
@ tesSUCCESS
Definition: TER.h:236
ripple::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:301
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:175
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:117
ripple::test::jtx::Env::rpc
Json::Value rpc(std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition: Env.h:700
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::test::jtx::owner_count
Definition: owners.h:49
ripple::test::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(DeliverMin, app, ripple)