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  Env env{*this, features | disallowIncoming};
279 
280  auto const gw = Account{"gateway"};
281  auto const alice = Account{"alice"};
282  auto const bob = Account{"bob"};
283  auto const USD = gw["USD"];
284 
285  env.fund(XRP(10000), gw, alice, bob);
286  env.close();
287 
288  // Set flag on gateway
290  env.close();
291 
292  // Create a trustline which will fail
293  env(trust(alice, USD(1000)), ter(tecNO_PERMISSION));
294  env.close();
295 
296  // Unset the flag
298  env.close();
299 
300  // Create a trustline which will now succeed
301  env(trust(alice, USD(1000)));
302  env.close();
303 
304  // Now the payment succeeds.
305  env(pay(gw, alice, USD(200)));
306  env.close();
307 
308  // Set flag on gateway again
310  env.close();
311 
312  // Destroy the balance by sending it back
313  env(pay(gw, alice, USD(200)));
314  env.close();
315 
316  // The trustline still exists in default state
317  // So a further payment should work
318  env(pay(gw, alice, USD(200)));
319  env.close();
320 
321  // Also set the flag on bob
323  env.close();
324 
325  // But now bob can't open a trustline because he didn't already have one
326  env(trust(bob, USD(1000)), ter(tecNO_PERMISSION));
327  env.close();
328 
329  // The gateway also can't open this trustline because bob has the flag
330  // set
331  env(trust(gw, bob["USD"](1000)), ter(tecNO_PERMISSION));
332  env.close();
333 
334  // Unset the flag only on the gateway
336  env.close();
337 
338  // Now bob can open a trustline
339  env(trust(bob, USD(1000)));
340  env.close();
341 
342  // And the gateway can send bob a balance
343  env(pay(gw, bob, USD(200)));
344  env.close();
345  }
346 
347  void
349  {
350  testFreeTrustlines(features, true, false);
351  testFreeTrustlines(features, false, true);
352  testFreeTrustlines(features, false, true);
353  // true, true case doesn't matter since creating a trustline ledger
354  // entry requires reserve from the creator
355  // independent of hi/low account ids for endpoints
356  testTicketSetTrust(features);
357  testMalformedTransaction(features);
358  testModifyQualityOfTrustline(features, false, false);
359  testModifyQualityOfTrustline(features, false, true);
360  testModifyQualityOfTrustline(features, true, false);
361  testModifyQualityOfTrustline(features, true, true);
362  testDisallowIncoming(features);
363  }
364 
365 public:
366  void
367  run() override
368  {
369  using namespace test::jtx;
370  auto const sa = supported_amendments();
372  testWithFeats(sa);
373  }
374 };
376 } // namespace test
377 } // 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:135
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:34
ripple::tecINSUF_RESERVE_LINE
@ tecINSUF_RESERVE_LINE
Definition: TER.h:255
ripple::test::SetTrust_test::testWithFeats
void testWithFeats(FeatureBitset features)
Definition: SetTrust_test.cpp:348
ripple::tfTrustSetMask
constexpr std::uint32_t tfTrustSetMask
Definition: TxFlags.h:113
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:88
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::test::jtx::trust
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Definition: trust.cpp:30
ripple::STAmount::getJson
Json::Value getJson(JsonOptions) const override
Definition: STAmount.cpp:655
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:106
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:367
ripple::test::jtx::ticket::use
Set a ticket sequence on a JTx.
Definition: ticket.h:47
ripple::temINVALID_FLAG
@ temINVALID_FLAG
Definition: TER.h:109
ripple::test::SetTrust_test
Definition: SetTrust_test.cpp:27
ripple::featureDisallowIncoming
const uint256 featureDisallowIncoming
ripple::temBAD_LIMIT
@ temBAD_LIMIT
Definition: TER.h:92
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:107
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::STAmount
Definition: STAmount.h:45
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:70
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
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:156
ripple::test::jtx::flags
Match set account flags.
Definition: flags.h:108
ripple::tecNO_LINE_INSUF_RESERVE
@ tecNO_LINE_INSUF_RESERVE
Definition: TER.h:259
ripple::test::jtx::pay
Json::Value pay(Account const &account, Account const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
ripple::tfSetfAuth
constexpr std::uint32_t tfSetfAuth
Definition: TxFlags.h:108
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:244
ripple::test::jtx::Env::master
Account const & master
Definition: Env.h:121
ripple::tecNO_PERMISSION
@ tecNO_PERMISSION
Definition: TER.h:272
ripple::FeatureBitset
Definition: Feature.h:113
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:261
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::test::jtx::Env::current
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:300
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:116
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:687
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)