rippled
Freeze_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/AccountID.h>
20 #include <ripple/protocol/Feature.h>
21 #include <ripple/protocol/SField.h>
22 #include <ripple/protocol/TxFlags.h>
23 #include <ripple/protocol/jss.h>
24 #include <test/jtx.h>
25 
26 namespace ripple {
27 
28 class Freeze_test : public beast::unit_test::suite
29 {
30  void
32  {
33  testcase("RippleState Freeze");
34 
35  using namespace test::jtx;
36  Env env(*this, features);
37 
38  Account G1{"G1"};
39  Account alice{"alice"};
40  Account bob{"bob"};
41 
42  env.fund(XRP(1000), G1, alice, bob);
43  env.close();
44 
45  env.trust(G1["USD"](100), bob);
46  env.trust(G1["USD"](100), alice);
47  env.close();
48 
49  env(pay(G1, bob, G1["USD"](10)));
50  env(pay(G1, alice, G1["USD"](100)));
51  env.close();
52 
53  env(offer(alice, XRP(500), G1["USD"](100)));
54  env.close();
55 
56  {
57  auto lines = getAccountLines(env, bob);
58  if (!BEAST_EXPECT(checkArraySize(lines[jss::lines], 1u)))
59  return;
60  BEAST_EXPECT(lines[jss::lines][0u][jss::account] == G1.human());
61  BEAST_EXPECT(lines[jss::lines][0u][jss::limit] == "100");
62  BEAST_EXPECT(lines[jss::lines][0u][jss::balance] == "10");
63  }
64 
65  {
66  auto lines = getAccountLines(env, alice);
67  if (!BEAST_EXPECT(checkArraySize(lines[jss::lines], 1u)))
68  return;
69  BEAST_EXPECT(lines[jss::lines][0u][jss::account] == G1.human());
70  BEAST_EXPECT(lines[jss::lines][0u][jss::limit] == "100");
71  BEAST_EXPECT(lines[jss::lines][0u][jss::balance] == "100");
72  }
73 
74  {
75  // Account with line unfrozen (proving operations normally work)
76  // test: can make Payment on that line
77  env(pay(alice, bob, G1["USD"](1)));
78 
79  // test: can receive Payment on that line
80  env(pay(bob, alice, G1["USD"](1)));
81  env.close();
82  }
83 
84  {
85  // Is created via a TrustSet with SetFreeze flag
86  // test: sets LowFreeze | HighFreeze flags
87  env(trust(G1, bob["USD"](0), tfSetFreeze));
88  auto affected = env.meta()->getJson(
90  if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
91  return;
92  auto ff =
94  BEAST_EXPECT(
95  ff[sfLowLimit.fieldName] ==
96  G1["USD"](0).value().getJson(JsonOptions::none));
97  BEAST_EXPECT(ff[jss::Flags].asUInt() & lsfLowFreeze);
98  BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze));
99  env.close();
100  }
101 
102  {
103  // Account with line frozen by issuer
104  // test: can buy more assets on that line
105  env(offer(bob, G1["USD"](5), XRP(25)));
106  auto affected = env.meta()->getJson(
108  if (!BEAST_EXPECT(checkArraySize(affected, 5u)))
109  return;
110  auto ff =
112  BEAST_EXPECT(
113  ff[sfHighLimit.fieldName] ==
114  bob["USD"](100).value().getJson(JsonOptions::none));
115  auto amt = STAmount{Issue{to_currency("USD"), noAccount()}, -15}
116  .value()
117  .getJson(JsonOptions::none);
118  BEAST_EXPECT(ff[sfBalance.fieldName] == amt);
119  env.close();
120  }
121 
122  {
123  // test: can not sell assets from that line
124  env(offer(bob, XRP(1), G1["USD"](5)), ter(tecUNFUNDED_OFFER));
125 
126  // test: can receive Payment on that line
127  env(pay(alice, bob, G1["USD"](1)));
128 
129  // test: can not make Payment from that line
130  env(pay(bob, alice, G1["USD"](1)), ter(tecPATH_DRY));
131  }
132 
133  {
134  // check G1 account lines
135  // test: shows freeze
136  auto lines = getAccountLines(env, G1);
137  Json::Value bobLine;
138  for (auto const& it : lines[jss::lines])
139  {
140  if (it[jss::account] == bob.human())
141  {
142  bobLine = it;
143  break;
144  }
145  }
146  if (!BEAST_EXPECT(bobLine))
147  return;
148  BEAST_EXPECT(bobLine[jss::freeze] == true);
149  BEAST_EXPECT(bobLine[jss::balance] == "-16");
150  }
151 
152  {
153  // test: shows freeze peer
154  auto lines = getAccountLines(env, bob);
155  Json::Value g1Line;
156  for (auto const& it : lines[jss::lines])
157  {
158  if (it[jss::account] == G1.human())
159  {
160  g1Line = it;
161  break;
162  }
163  }
164  if (!BEAST_EXPECT(g1Line))
165  return;
166  BEAST_EXPECT(g1Line[jss::freeze_peer] == true);
167  BEAST_EXPECT(g1Line[jss::balance] == "16");
168  }
169 
170  {
171  // Is cleared via a TrustSet with ClearFreeze flag
172  // test: sets LowFreeze | HighFreeze flags
173  env(trust(G1, bob["USD"](0), tfClearFreeze));
174  auto affected = env.meta()->getJson(
176  if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
177  return;
178  auto ff =
180  BEAST_EXPECT(
181  ff[sfLowLimit.fieldName] ==
182  G1["USD"](0).value().getJson(JsonOptions::none));
183  BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfLowFreeze));
184  BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze));
185  env.close();
186  }
187  }
188 
189  void
191  {
192  testcase("Global Freeze");
193 
194  using namespace test::jtx;
195  Env env(*this, features);
196 
197  Account G1{"G1"};
198  Account A1{"A1"};
199  Account A2{"A2"};
200  Account A3{"A3"};
201  Account A4{"A4"};
202 
203  env.fund(XRP(12000), G1);
204  env.fund(XRP(1000), A1);
205  env.fund(XRP(20000), A2, A3, A4);
206  env.close();
207 
208  env.trust(G1["USD"](1200), A1);
209  env.trust(G1["USD"](200), A2);
210  env.trust(G1["BTC"](100), A3);
211  env.trust(G1["BTC"](100), A4);
212  env.close();
213 
214  env(pay(G1, A1, G1["USD"](1000)));
215  env(pay(G1, A2, G1["USD"](100)));
216  env(pay(G1, A3, G1["BTC"](100)));
217  env(pay(G1, A4, G1["BTC"](100)));
218  env.close();
219 
220  env(offer(G1, XRP(10000), G1["USD"](100)), txflags(tfPassive));
221  env(offer(G1, G1["USD"](100), XRP(10000)), txflags(tfPassive));
222  env(offer(A1, XRP(10000), G1["USD"](100)), txflags(tfPassive));
223  env(offer(A2, G1["USD"](100), XRP(10000)), txflags(tfPassive));
224  env.close();
225 
226  {
227  // Is toggled via AccountSet using SetFlag and ClearFlag
228  // test: SetFlag GlobalFreeze
229  env.require(nflags(G1, asfGlobalFreeze));
230  env(fset(G1, asfGlobalFreeze));
231  env.require(flags(G1, asfGlobalFreeze));
232  env.require(nflags(G1, asfNoFreeze));
233 
234  // test: ClearFlag GlobalFreeze
235  env(fclear(G1, asfGlobalFreeze));
236  env.require(nflags(G1, asfGlobalFreeze));
237  env.require(nflags(G1, asfNoFreeze));
238  }
239 
240  {
241  // Account without GlobalFreeze (proving operations normally work)
242  // test: visible offers where taker_pays is unfrozen issuer
243  auto offers = env.rpc(
244  "book_offers",
245  std::string("USD/") + G1.human(),
246  "XRP")[jss::result][jss::offers];
247  if (!BEAST_EXPECT(checkArraySize(offers, 2u)))
248  return;
249  std::set<std::string> accounts;
250  for (auto const& offer : offers)
251  {
252  accounts.insert(offer[jss::Account].asString());
253  }
254  BEAST_EXPECT(accounts.find(A2.human()) != std::end(accounts));
255  BEAST_EXPECT(accounts.find(G1.human()) != std::end(accounts));
256 
257  // test: visible offers where taker_gets is unfrozen issuer
258  offers = env.rpc(
259  "book_offers",
260  "XRP",
261  std::string("USD/") + G1.human())[jss::result][jss::offers];
262  if (!BEAST_EXPECT(checkArraySize(offers, 2u)))
263  return;
264  accounts.clear();
265  for (auto const& offer : offers)
266  {
267  accounts.insert(offer[jss::Account].asString());
268  }
269  BEAST_EXPECT(accounts.find(A1.human()) != std::end(accounts));
270  BEAST_EXPECT(accounts.find(G1.human()) != std::end(accounts));
271  }
272 
273  {
274  // Offers/Payments
275  // test: assets can be bought on the market
276  env(offer(A3, G1["BTC"](1), XRP(1)));
277 
278  // test: assets can be sold on the market
279  env(offer(A4, XRP(1), G1["BTC"](1)));
280 
281  // test: direct issues can be sent
282  env(pay(G1, A2, G1["USD"](1)));
283 
284  // test: direct redemptions can be sent
285  env(pay(A2, G1, G1["USD"](1)));
286 
287  // test: via rippling can be sent
288  env(pay(A2, A1, G1["USD"](1)));
289 
290  // test: via rippling can be sent back
291  env(pay(A1, A2, G1["USD"](1)));
292  }
293 
294  {
295  // Account with GlobalFreeze
296  // set GlobalFreeze first
297  // test: SetFlag GlobalFreeze will toggle back to freeze
298  env.require(nflags(G1, asfGlobalFreeze));
299  env(fset(G1, asfGlobalFreeze));
300  env.require(flags(G1, asfGlobalFreeze));
301  env.require(nflags(G1, asfNoFreeze));
302 
303  // test: assets can't be bought on the market
304  env(offer(A3, G1["BTC"](1), XRP(1)), ter(tecFROZEN));
305 
306  // test: assets can't be sold on the market
307  env(offer(A4, XRP(1), G1["BTC"](1)), ter(tecFROZEN));
308  }
309 
310  {
311  // offers are filtered (seems to be broken?)
312  // test: account_offers always shows own offers
313  auto offers = getAccountOffers(env, G1)[jss::offers];
314  if (!BEAST_EXPECT(checkArraySize(offers, 2u)))
315  return;
316 
317  // test: book_offers shows offers
318  // (should these actually be filtered?)
319  offers = env.rpc(
320  "book_offers",
321  "XRP",
322  std::string("USD/") + G1.human())[jss::result][jss::offers];
323  if (!BEAST_EXPECT(checkArraySize(offers, 2u)))
324  return;
325 
326  offers = env.rpc(
327  "book_offers",
328  std::string("USD/") + G1.human(),
329  "XRP")[jss::result][jss::offers];
330  if (!BEAST_EXPECT(checkArraySize(offers, 2u)))
331  return;
332  }
333 
334  {
335  // Payments
336  // test: direct issues can be sent
337  env(pay(G1, A2, G1["USD"](1)));
338 
339  // test: direct redemptions can be sent
340  env(pay(A2, G1, G1["USD"](1)));
341 
342  // test: via rippling cant be sent
343  env(pay(A2, A1, G1["USD"](1)), ter(tecPATH_DRY));
344  }
345  }
346 
347  void
349  {
350  testcase("No Freeze");
351 
352  using namespace test::jtx;
353  Env env(*this, features);
354 
355  Account G1{"G1"};
356  Account A1{"A1"};
357 
358  env.fund(XRP(12000), G1);
359  env.fund(XRP(1000), A1);
360  env.close();
361 
362  env.trust(G1["USD"](1000), A1);
363  env.close();
364 
365  env(pay(G1, A1, G1["USD"](1000)));
366  env.close();
367 
368  // TrustSet NoFreeze
369  // test: should set NoFreeze in Flags
370  env.require(nflags(G1, asfNoFreeze));
371  env(fset(G1, asfNoFreeze));
372  env.require(flags(G1, asfNoFreeze));
373  env.require(nflags(G1, asfGlobalFreeze));
374 
375  // test: cannot be cleared
376  env(fclear(G1, asfNoFreeze));
377  env.require(flags(G1, asfNoFreeze));
378  env.require(nflags(G1, asfGlobalFreeze));
379 
380  // test: can set GlobalFreeze
381  env(fset(G1, asfGlobalFreeze));
382  env.require(flags(G1, asfNoFreeze));
383  env.require(flags(G1, asfGlobalFreeze));
384 
385  // test: cannot unset GlobalFreeze
386  env(fclear(G1, asfGlobalFreeze));
387  env.require(flags(G1, asfNoFreeze));
388  env.require(flags(G1, asfGlobalFreeze));
389 
390  // test: trustlines can't be frozen
391  env(trust(G1, A1["USD"](0), tfSetFreeze));
392  auto affected =
393  env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
394  if (!BEAST_EXPECT(checkArraySize(affected, 1u)))
395  return;
396 
397  auto let =
399  BEAST_EXPECT(let == jss::AccountRoot);
400  }
401 
402  void
404  {
405  testcase("Offers for Frozen Trust Lines");
406 
407  using namespace test::jtx;
408  Env env(*this, features);
409 
410  Account G1{"G1"};
411  Account A2{"A2"};
412  Account A3{"A3"};
413  Account A4{"A4"};
414 
415  env.fund(XRP(1000), G1, A3, A4);
416  env.fund(XRP(2000), A2);
417  env.close();
418 
419  env.trust(G1["USD"](1000), A2);
420  env.trust(G1["USD"](2000), A3);
421  env.trust(G1["USD"](2000), A4);
422  env.close();
423 
424  env(pay(G1, A3, G1["USD"](2000)));
425  env(pay(G1, A4, G1["USD"](2000)));
426  env.close();
427 
428  env(offer(A3, XRP(1000), G1["USD"](1000)), txflags(tfPassive));
429  env.close();
430 
431  // removal after successful payment
432  // test: make a payment with partially consuming offer
433  env(pay(A2, G1, G1["USD"](1)), paths(G1["USD"]), sendmax(XRP(1)));
434  env.close();
435 
436  // test: offer was only partially consumed
437  auto offers = getAccountOffers(env, A3)[jss::offers];
438  if (!BEAST_EXPECT(checkArraySize(offers, 1u)))
439  return;
440  BEAST_EXPECT(
441  offers[0u][jss::taker_gets] ==
442  G1["USD"](999).value().getJson(JsonOptions::none));
443 
444  // test: someone else creates an offer providing liquidity
445  env(offer(A4, XRP(999), G1["USD"](999)));
446  env.close();
447 
448  // test: owner of partially consumed offers line is frozen
449  env(trust(G1, A3["USD"](0), tfSetFreeze));
450  auto affected =
451  env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
452  if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
453  return;
454  auto ff =
456  BEAST_EXPECT(
457  ff[sfHighLimit.fieldName] ==
458  G1["USD"](0).value().getJson(JsonOptions::none));
459  BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfLowFreeze));
460  BEAST_EXPECT(ff[jss::Flags].asUInt() & lsfHighFreeze);
461  env.close();
462 
463  // verify offer on the books
464  offers = getAccountOffers(env, A3)[jss::offers];
465  if (!BEAST_EXPECT(checkArraySize(offers, 1u)))
466  return;
467 
468  // test: Can make a payment via the new offer
469  env(pay(A2, G1, G1["USD"](1)), paths(G1["USD"]), sendmax(XRP(1)));
470  env.close();
471 
472  // test: Partially consumed offer was removed by tes* payment
473  offers = getAccountOffers(env, A3)[jss::offers];
474  if (!BEAST_EXPECT(checkArraySize(offers, 0u)))
475  return;
476 
477  // removal buy successful OfferCreate
478  // test: freeze the new offer
479  env(trust(G1, A4["USD"](0), tfSetFreeze));
480  affected =
481  env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
482  if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
483  return;
484  ff = affected[0u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
485  BEAST_EXPECT(
486  ff[sfLowLimit.fieldName] ==
487  G1["USD"](0).value().getJson(JsonOptions::none));
488  BEAST_EXPECT(ff[jss::Flags].asUInt() & lsfLowFreeze);
489  BEAST_EXPECT(!(ff[jss::Flags].asUInt() & lsfHighFreeze));
490  env.close();
491 
492  // test: can no longer create a crossing offer
493  env(offer(A2, G1["USD"](999), XRP(999)));
494  affected =
495  env.meta()->getJson(JsonOptions::none)[sfAffectedNodes.fieldName];
496  if (!BEAST_EXPECT(checkArraySize(affected, 8u)))
497  return;
498  auto created = affected[0u][sfCreatedNode.fieldName];
499  BEAST_EXPECT(
500  created[sfNewFields.fieldName][jss::Account] == A2.human());
501  env.close();
502 
503  // test: offer was removed by offer_create
504  offers = getAccountOffers(env, A4)[jss::offers];
505  if (!BEAST_EXPECT(checkArraySize(offers, 0u)))
506  return;
507  }
508 
509 public:
510  void
511  run() override
512  {
513  auto testAll = [this](FeatureBitset features) {
514  testRippleState(features);
515  testGlobalFreeze(features);
516  testNoFreeze(features);
517  testOffersWhenFrozen(features);
518  };
519  using namespace test::jtx;
520  auto const sa = supported_amendments();
521  testAll(sa - featureFlowCross);
522  testAll(sa);
523  }
524 };
525 
526 BEAST_DEFINE_TESTSUITE(Freeze, app, ripple);
527 } // namespace ripple
ripple::tecUNFUNDED_OFFER
@ tecUNFUNDED_OFFER
Definition: TER.h:262
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::tecFROZEN
@ tecFROZEN
Definition: TER.h:281
ripple::Issue
A currency issued by an account.
Definition: Issue.h:35
std::string
STL class.
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::Freeze_test::testOffersWhenFrozen
void testOffersWhenFrozen(FeatureBitset features)
Definition: Freeze_test.cpp:403
ripple::asfNoFreeze
constexpr std::uint32_t asfNoFreeze
Definition: TxFlags.h:79
std::set::find
T find(T... args)
ripple::SField::fieldName
const std::string fieldName
Definition: SField.h:137
ripple::sfFinalFields
const SField sfFinalFields
ripple::tfPassive
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:94
std::set::clear
T clear(T... args)
ripple::sfLowLimit
const SF_AMOUNT sfLowLimit
ripple::Freeze_test::testGlobalFreeze
void testGlobalFreeze(FeatureBitset features)
Definition: Freeze_test.cpp:190
ripple::sfNewFields
const SField sfNewFields
ripple::JsonOptions::none
@ none
ripple::sfAffectedNodes
const SField sfAffectedNodes
ripple::sfModifiedNode
const SField sfModifiedNode
ripple::STAmount
Definition: STAmount.h:46
ripple::Freeze_test::testNoFreeze
void testNoFreeze(FeatureBitset features)
Definition: Freeze_test.cpp:348
ripple::Freeze_test::testRippleState
void testRippleState(FeatureBitset features)
Definition: Freeze_test.cpp:31
ripple::sfHighLimit
const SF_AMOUNT sfHighLimit
ripple::lsfHighFreeze
@ lsfHighFreeze
Definition: LedgerFormats.h:287
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:291
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::sfLedgerEntryType
const SF_UINT16 sfLedgerEntryType
ripple::tfSetFreeze
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:112
std::set::insert
T insert(T... args)
ripple::sfCreatedNode
const SField sfCreatedNode
ripple::sfBalance
const SF_AMOUNT sfBalance
ripple::FeatureBitset
Definition: Feature.h:113
ripple::tecPATH_DRY
@ tecPATH_DRY
Definition: TER.h:272
ripple::asfGlobalFreeze
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:80
ripple::tfClearFreeze
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:113
ripple::Freeze_test::run
void run() override
Definition: Freeze_test.cpp:511
std::end
T end(T... args)
ripple::lsfLowFreeze
@ lsfLowFreeze
Definition: LedgerFormats.h:286
ripple::featureFlowCross
const uint256 featureFlowCross
std::set< std::string >
ripple::noAccount
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:175
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::Freeze_test
Definition: Freeze_test.cpp:28