rippled
Loading...
Searching...
No Matches
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 <test/jtx.h>
20#include <xrpl/protocol/AccountID.h>
21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/SField.h>
23#include <xrpl/protocol/TxFlags.h>
24#include <xrpl/protocol/jss.h>
25
26namespace ripple {
27
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(
89 JsonOptions::none)[sfAffectedNodes.fieldName];
90 if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
91 return;
92 auto ff =
93 affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
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(
107 JsonOptions::none)[sfAffectedNodes.fieldName];
108 if (!BEAST_EXPECT(checkArraySize(affected, 5u)))
109 return;
110 auto ff =
111 affected[3u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
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(
175 JsonOptions::none)[sfAffectedNodes.fieldName];
176 if (!BEAST_EXPECT(checkArraySize(affected, 2u)))
177 return;
178 auto ff =
179 affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
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 =
398 affected[0u][sfModifiedNode.fieldName][sfLedgerEntryType.fieldName];
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 =
455 affected[1u][sfModifiedNode.fieldName][sfFinalFields.fieldName];
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
509public:
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
526BEAST_DEFINE_TESTSUITE(Freeze, app, ripple);
527} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
A testsuite class.
Definition: suite.h:53
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
void testGlobalFreeze(FeatureBitset features)
void testNoFreeze(FeatureBitset features)
void run() override
Runs the suite.
void testRippleState(FeatureBitset features)
Definition: Freeze_test.cpp:31
void testOffersWhenFrozen(FeatureBitset features)
A currency issued by an account.
Definition: Issue.h:36
T clear(T... args)
T end(T... args)
T find(T... args)
T insert(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
AccountID const & noAccount()
A placeholder for empty accounts.
Definition: AccountID.cpp:177
constexpr std::uint32_t asfGlobalFreeze
Definition: TxFlags.h:82
@ lsfHighFreeze
@ lsfLowFreeze
constexpr std::uint32_t asfNoFreeze
Definition: TxFlags.h:81
constexpr std::uint32_t tfPassive
Definition: TxFlags.h:96
constexpr std::uint32_t tfClearFreeze
Definition: TxFlags.h:116
@ tecUNFUNDED_OFFER
Definition: TER.h:271
@ tecFROZEN
Definition: TER.h:290
@ tecPATH_DRY
Definition: TER.h:281
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
constexpr std::uint32_t tfSetFreeze
Definition: TxFlags.h:115
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition: UintTypes.cpp:80