rippled
Loading...
Searching...
No Matches
STAmount_test.cpp
1#include <test/jtx.h>
2
3#include <xrpl/basics/random.h>
4#include <xrpl/beast/unit_test.h>
5#include <xrpl/protocol/STAmount.h>
6#include <xrpl/protocol/XRPAmount.h>
7
8namespace xrpl {
9
11{
12public:
13 static STAmount
15 {
16 Serializer ser;
17 s.add(ser);
18
19 SerialIter sit(ser.slice());
20 return STAmount(sit, sfGeneric);
21 }
22
23 //--------------------------------------------------------------------------
24 static STAmount
25 roundSelf(STAmount const& amount)
26 {
27 if (amount.native())
28 return amount;
29
30 std::uint64_t mantissa = amount.mantissa();
31 std::uint64_t const valueDigits = mantissa % 1000000000;
32
33 if (valueDigits == 1)
34 {
35 mantissa--;
36
37 if (mantissa < STAmount::cMinValue)
38 return {amount.issue(), mantissa, amount.exponent(), amount.negative()};
39
40 return {
41 amount.issue(),
42 mantissa,
43 amount.exponent(),
44 amount.negative(),
46 }
47
48 if (valueDigits == 999999999)
49 {
50 mantissa++;
51
52 if (mantissa > STAmount::cMaxValue)
53 return {amount.issue(), mantissa, amount.exponent(), amount.negative()};
54
55 return {
56 amount.issue(),
57 mantissa,
58 amount.exponent(),
59 amount.negative(),
61 }
62
63 return amount;
64 }
65
66 void
67 roundTest(int n, int d, int m)
68 {
69 // check STAmount rounding
70 STAmount const num(noIssue(), n);
71 STAmount const den(noIssue(), d);
72 STAmount const mul(noIssue(), m);
73 STAmount const quot = divide(STAmount(n), STAmount(d), noIssue());
74 STAmount const res = roundSelf(multiply(quot, mul, noIssue()));
75
76 BEAST_EXPECT(!res.native());
77
78 STAmount const cmp(noIssue(), (n * m) / d);
79
80 BEAST_EXPECT(!cmp.native());
81
82 BEAST_EXPECT(cmp.issue().currency == res.issue().currency);
83
84 if (res != cmp)
85 {
86 log << "(" << num.getText() << "/" << den.getText() << ") X " << mul.getText() << " = "
87 << res.getText() << " not " << cmp.getText();
88 fail("Rounding");
89 return;
90 }
91 }
92
93 void
94 mulTest(int a, int b)
95 {
96 STAmount const aa(noIssue(), a);
97 STAmount const bb(noIssue(), b);
98 STAmount const prod1(multiply(aa, bb, noIssue()));
99
100 BEAST_EXPECT(!prod1.native());
101
102 STAmount const prod2(
103 noIssue(), static_cast<std::uint64_t>(a) * static_cast<std::uint64_t>(b));
104
105 if (prod1 != prod2)
106 {
107 log << "nn(" << aa.getFullText() << " * " << bb.getFullText()
108 << ") = " << prod1.getFullText() << " not " << prod2.getFullText();
109 fail("Multiplication result is not exact");
110 }
111 }
112
113 //--------------------------------------------------------------------------
114
115 void
116 testSetValue(std::string const& value, Issue const& issue, bool success = true)
117 {
118 try
119 {
120 STAmount const amount = amountFromString(issue, value);
121 BEAST_EXPECT(amount.getText() == value);
122 }
123 catch (std::exception const&)
124 {
125 BEAST_EXPECT(!success);
126 }
127 }
128
129 void
131 {
132 {
133 testcase("set value (native)");
134
135 Issue const xrp(xrpIssue());
136
137 // fractional XRP (i.e. drops)
138 testSetValue("1", xrp);
139 testSetValue("22", xrp);
140 testSetValue("333", xrp);
141 testSetValue("4444", xrp);
142 testSetValue("55555", xrp);
143 testSetValue("666666", xrp);
144
145 // 1 XRP up to 100 billion, in powers of 10 (in drops)
146 testSetValue("1000000", xrp);
147 testSetValue("10000000", xrp);
148 testSetValue("100000000", xrp);
149 testSetValue("1000000000", xrp);
150 testSetValue("10000000000", xrp);
151 testSetValue("100000000000", xrp);
152 testSetValue("1000000000000", xrp);
153 testSetValue("10000000000000", xrp);
154 testSetValue("100000000000000", xrp);
155 testSetValue("1000000000000000", xrp);
156 testSetValue("10000000000000000", xrp);
157 testSetValue("100000000000000000", xrp);
158
159 // Invalid native values:
160 testSetValue("1.1", xrp, false);
161 testSetValue("100000000000000001", xrp, false);
162 testSetValue("1000000000000000000", xrp, false);
163 }
164
165 {
166 testcase("set value (iou)");
167
168 Issue const usd(Currency(0x5553440000000000), AccountID(0x4985601));
169
170 testSetValue("1", usd);
171 testSetValue("10", usd);
172 testSetValue("100", usd);
173 testSetValue("1000", usd);
174 testSetValue("10000", usd);
175 testSetValue("100000", usd);
176 testSetValue("1000000", usd);
177 testSetValue("10000000", usd);
178 testSetValue("100000000", usd);
179 testSetValue("1000000000", usd);
180 testSetValue("10000000000", usd);
181
182 testSetValue("1234567.1", usd);
183 testSetValue("1234567.12", usd);
184 testSetValue("1234567.123", usd);
185 testSetValue("1234567.1234", usd);
186 testSetValue("1234567.12345", usd);
187 testSetValue("1234567.123456", usd);
188 testSetValue("1234567.1234567", usd);
189 testSetValue("1234567.12345678", usd);
190 testSetValue("1234567.123456789", usd);
191 }
192 }
193
194 //--------------------------------------------------------------------------
195
196 void
198 {
199 testcase("native currency");
200
201 STAmount const zeroSt;
202 STAmount const one(1);
203 STAmount const hundred(100);
204
205 // VFALCO NOTE Why repeat "STAmount fail" so many times??
206 unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail");
207 unexpected(serializeAndDeserialize(one) != one, "STAmount fail");
208 unexpected(serializeAndDeserialize(hundred) != hundred, "STAmount fail");
209 unexpected(!zeroSt.native(), "STAmount fail");
210 unexpected(!hundred.native(), "STAmount fail");
211 unexpected(zeroSt != beast::zero, "STAmount fail");
212 unexpected(one == beast::zero, "STAmount fail");
213 unexpected(hundred == beast::zero, "STAmount fail");
214 unexpected((zeroSt < zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
215 unexpected(!(zeroSt < one), "STAmount fail");
216 unexpected(!(zeroSt < hundred), "STAmount fail");
217 unexpected((one < zeroSt), "STAmount fail");
218 unexpected((one < one), "STAmount fail"); // NOLINT(misc-redundant-expression)
219 unexpected(!(one < hundred), "STAmount fail");
220 unexpected((hundred < zeroSt), "STAmount fail");
221 unexpected((hundred < one), "STAmount fail");
222 unexpected((hundred < hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
223 unexpected((zeroSt > zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
224 unexpected((zeroSt > one), "STAmount fail");
225 unexpected((zeroSt > hundred), "STAmount fail");
226 unexpected(!(one > zeroSt), "STAmount fail");
227 unexpected((one > one), "STAmount fail"); // NOLINT(misc-redundant-expression)
228 unexpected((one > hundred), "STAmount fail");
229 unexpected(!(hundred > zeroSt), "STAmount fail");
230 unexpected(!(hundred > one), "STAmount fail");
231 unexpected((hundred > hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
232 unexpected(!(zeroSt <= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
233 unexpected(!(zeroSt <= one), "STAmount fail");
234 unexpected(!(zeroSt <= hundred), "STAmount fail");
235 unexpected((one <= zeroSt), "STAmount fail");
236 unexpected(!(one <= one), "STAmount fail"); // NOLINT(misc-redundant-expression)
237 unexpected(!(one <= hundred), "STAmount fail");
238 unexpected((hundred <= zeroSt), "STAmount fail");
239 unexpected((hundred <= one), "STAmount fail");
240 unexpected(!(hundred <= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
241 unexpected(!(zeroSt >= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
242 unexpected((zeroSt >= one), "STAmount fail");
243 unexpected((zeroSt >= hundred), "STAmount fail");
244 unexpected(!(one >= zeroSt), "STAmount fail");
245 unexpected(!(one >= one), "STAmount fail"); // NOLINT(misc-redundant-expression)
246 unexpected((one >= hundred), "STAmount fail");
247 unexpected(!(hundred >= zeroSt), "STAmount fail");
248 unexpected(!(hundred >= one), "STAmount fail");
249 unexpected(!(hundred >= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
250 unexpected(!(zeroSt == zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
251 unexpected((zeroSt == one), "STAmount fail");
252 unexpected((zeroSt == hundred), "STAmount fail");
253 unexpected((one == zeroSt), "STAmount fail");
254 unexpected(!(one == one), "STAmount fail"); // NOLINT(misc-redundant-expression)
255 unexpected((one == hundred), "STAmount fail");
256 unexpected((hundred == zeroSt), "STAmount fail");
257 unexpected((hundred == one), "STAmount fail");
258 unexpected(!(hundred == hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
259 unexpected((zeroSt != zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
260 unexpected(!(zeroSt != one), "STAmount fail");
261 unexpected(!(zeroSt != hundred), "STAmount fail");
262 unexpected(!(one != zeroSt), "STAmount fail");
263 unexpected((one != one), "STAmount fail"); // NOLINT(misc-redundant-expression)
264 unexpected(!(one != hundred), "STAmount fail");
265 unexpected(!(hundred != zeroSt), "STAmount fail");
266 unexpected(!(hundred != one), "STAmount fail");
267 unexpected((hundred != hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
268 unexpected(STAmount().getText() != "0", "STAmount fail");
269 unexpected(STAmount(31).getText() != "31", "STAmount fail");
270 unexpected(STAmount(310).getText() != "310", "STAmount fail");
271 unexpected(to_string(Currency()) != "XRP", "cHC(XRP)");
272 Currency c;
273 unexpected(!to_currency(c, "USD"), "create USD currency");
274 unexpected(to_string(c) != "USD", "check USD currency");
275
276 std::string const cur = "015841551A748AD2C1F76FF6ECB0CCCD00000000";
277 unexpected(!to_currency(c, cur), "create custom currency");
278 unexpected(to_string(c) != cur, "check custom currency");
279 }
280
281 //--------------------------------------------------------------------------
282
283 void
285 {
286 testcase("custom currency");
287
288 STAmount const zeroSt(noIssue());
289 STAmount const one(noIssue(), 1);
290 STAmount const hundred(noIssue(), 100);
291
292 unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail");
293 unexpected(serializeAndDeserialize(one) != one, "STAmount fail");
294 unexpected(serializeAndDeserialize(hundred) != hundred, "STAmount fail");
295 unexpected(zeroSt.native(), "STAmount fail");
296 unexpected(hundred.native(), "STAmount fail");
297 unexpected(zeroSt != beast::zero, "STAmount fail");
298 unexpected(one == beast::zero, "STAmount fail");
299 unexpected(hundred == beast::zero, "STAmount fail");
300 unexpected((zeroSt < zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
301 unexpected(!(zeroSt < one), "STAmount fail");
302 unexpected(!(zeroSt < hundred), "STAmount fail");
303 unexpected((one < zeroSt), "STAmount fail");
304 unexpected((one < one), "STAmount fail"); // NOLINT(misc-redundant-expression)
305 unexpected(!(one < hundred), "STAmount fail");
306 unexpected((hundred < zeroSt), "STAmount fail");
307 unexpected((hundred < one), "STAmount fail");
308 unexpected((hundred < hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
309 unexpected((zeroSt > zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
310 unexpected((zeroSt > one), "STAmount fail");
311 unexpected((zeroSt > hundred), "STAmount fail");
312 unexpected(!(one > zeroSt), "STAmount fail");
313 unexpected((one > one), "STAmount fail"); // NOLINT(misc-redundant-expression)
314 unexpected((one > hundred), "STAmount fail");
315 unexpected(!(hundred > zeroSt), "STAmount fail");
316 unexpected(!(hundred > one), "STAmount fail");
317 unexpected((hundred > hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
318 unexpected(!(zeroSt <= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
319 unexpected(!(zeroSt <= one), "STAmount fail");
320 unexpected(!(zeroSt <= hundred), "STAmount fail");
321 unexpected((one <= zeroSt), "STAmount fail");
322 unexpected(!(one <= one), "STAmount fail"); // NOLINT(misc-redundant-expression)
323 unexpected(!(one <= hundred), "STAmount fail");
324 unexpected((hundred <= zeroSt), "STAmount fail");
325 unexpected((hundred <= one), "STAmount fail");
326 unexpected(!(hundred <= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
327 unexpected(!(zeroSt >= zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
328 unexpected((zeroSt >= one), "STAmount fail");
329 unexpected((zeroSt >= hundred), "STAmount fail");
330 unexpected(!(one >= zeroSt), "STAmount fail");
331 unexpected(!(one >= one), "STAmount fail"); // NOLINT(misc-redundant-expression)
332 unexpected((one >= hundred), "STAmount fail");
333 unexpected(!(hundred >= zeroSt), "STAmount fail");
334 unexpected(!(hundred >= one), "STAmount fail");
335 unexpected(!(hundred >= hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
336 unexpected(!(zeroSt == zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
337 unexpected((zeroSt == one), "STAmount fail");
338 unexpected((zeroSt == hundred), "STAmount fail");
339 unexpected((one == zeroSt), "STAmount fail");
340 unexpected(!(one == one), "STAmount fail"); // NOLINT(misc-redundant-expression)
341 unexpected((one == hundred), "STAmount fail");
342 unexpected((hundred == zeroSt), "STAmount fail");
343 unexpected((hundred == one), "STAmount fail");
344 unexpected(!(hundred == hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
345 unexpected((zeroSt != zeroSt), "STAmount fail"); // NOLINT(misc-redundant-expression)
346 unexpected(!(zeroSt != one), "STAmount fail");
347 unexpected(!(zeroSt != hundred), "STAmount fail");
348 unexpected(!(one != zeroSt), "STAmount fail");
349 unexpected((one != one), "STAmount fail"); // NOLINT(misc-redundant-expression)
350 unexpected(!(one != hundred), "STAmount fail");
351 unexpected(!(hundred != zeroSt), "STAmount fail");
352 unexpected(!(hundred != one), "STAmount fail");
353 unexpected((hundred != hundred), "STAmount fail"); // NOLINT(misc-redundant-expression)
354 unexpected(STAmount(noIssue()).getText() != "0", "STAmount fail");
355 unexpected(STAmount(noIssue(), 31).getText() != "31", "STAmount fail");
356 unexpected(STAmount(noIssue(), 31, 1).getText() != "310", "STAmount fail");
357 unexpected(STAmount(noIssue(), 31, -1).getText() != "3.1", "STAmount fail");
358 unexpected(STAmount(noIssue(), 31, -2).getText() != "0.31", "STAmount fail");
360 multiply(STAmount(noIssue(), 20), STAmount(3), noIssue()).getText() != "60",
361 "STAmount multiply fail 1");
363 multiply(STAmount(noIssue(), 20), STAmount(3), xrpIssue()).getText() != "60",
364 "STAmount multiply fail 2");
366 multiply(STAmount(20), STAmount(3), noIssue()).getText() != "60",
367 "STAmount multiply fail 3");
369 multiply(STAmount(20), STAmount(3), xrpIssue()).getText() != "60",
370 "STAmount multiply fail 4");
371
372 if (divide(STAmount(noIssue(), 60), STAmount(3), noIssue()).getText() != "20")
373 {
374 log << "60/3 = " << divide(STAmount(noIssue(), 60), STAmount(3), noIssue()).getText();
375 fail("STAmount divide fail");
376 }
377 else
378 {
379 pass();
380 }
381
383 divide(STAmount(noIssue(), 60), STAmount(3), xrpIssue()).getText() != "20",
384 "STAmount divide fail");
385
387 divide(STAmount(noIssue(), 60), STAmount(noIssue(), 3), noIssue()).getText() != "20",
388 "STAmount divide fail");
389
391 divide(STAmount(noIssue(), 60), STAmount(noIssue(), 3), xrpIssue()).getText() != "20",
392 "STAmount divide fail");
393
394 STAmount const a1(noIssue(), 60);
395 STAmount const a2(noIssue(), 10, -1);
396
398 divide(a2, a1, noIssue()) != amountFromQuality(getRate(a1, a2)),
399 "STAmount setRate(getRate) fail");
400
402 divide(a1, a2, noIssue()) != amountFromQuality(getRate(a2, a1)),
403 "STAmount setRate(getRate) fail");
404 }
405
406 //--------------------------------------------------------------------------
407
408 void
410 {
411 testcase("arithmetic");
412
413 // Test currency multiplication and division operations such as
414 // convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed,
415 // and getNeeded
416
418 getRate(STAmount(1), STAmount(10)) !=
419 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
420 "STAmount getRate fail 1");
421
423 getRate(STAmount(10), STAmount(1)) !=
424 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
425 "STAmount getRate fail 2");
426
428 getRate(STAmount(noIssue(), 1), STAmount(noIssue(), 10)) !=
429 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
430 "STAmount getRate fail 3");
431
433 getRate(STAmount(noIssue(), 10), STAmount(noIssue(), 1)) !=
434 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
435 "STAmount getRate fail 4");
436
438 getRate(STAmount(noIssue(), 1), STAmount(10)) !=
439 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
440 "STAmount getRate fail 5");
441
443 getRate(STAmount(noIssue(), 10), STAmount(1)) !=
444 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
445 "STAmount getRate fail 6");
446
448 getRate(STAmount(1), STAmount(noIssue(), 10)) !=
449 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
450 "STAmount getRate fail 7");
451
453 getRate(STAmount(10), STAmount(noIssue(), 1)) !=
454 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
455 "STAmount getRate fail 8");
456
457 roundTest(1, 3, 3);
458 roundTest(2, 3, 9);
459 roundTest(1, 7, 21);
460 roundTest(1, 2, 4);
461 roundTest(3, 9, 18);
462 roundTest(7, 11, 44);
463
464 for (int i = 0; i <= 100000; ++i)
465 {
466 mulTest(rand_int(10000000), rand_int(10000000));
467 }
468 }
469
470 //--------------------------------------------------------------------------
471
472 void
474 {
475 testcase("underflow");
476
477 STAmount const bigNative(STAmount::cMaxNative / 2);
478 STAmount const bigValue(
480 STAmount const smallValue(
482 STAmount const zeroSt(noIssue(), 0);
483
484 STAmount const smallXSmall = multiply(smallValue, smallValue, noIssue());
485
486 BEAST_EXPECT(smallXSmall == beast::zero);
487
488 STAmount bigDsmall = divide(smallValue, bigValue, noIssue());
489
490 BEAST_EXPECT(bigDsmall == beast::zero);
491
492 BEAST_EXPECT(bigDsmall == beast::zero);
493
494 bigDsmall = divide(smallValue, bigValue, xrpIssue());
495
496 BEAST_EXPECT(bigDsmall == beast::zero);
497
498 bigDsmall = divide(smallValue, bigNative, xrpIssue());
499
500 BEAST_EXPECT(bigDsmall == beast::zero);
501
502 // very bad offer
503 std::uint64_t r = getRate(smallValue, bigValue);
504
505 BEAST_EXPECT(r == 0);
506
507 // very good offer
508 r = getRate(bigValue, smallValue);
509
510 BEAST_EXPECT(r == 0);
511 }
512
513 //--------------------------------------------------------------------------
514
515 void
517 {
518 // VFALCO TODO There are no actual tests here, just printed output?
519 // Change this to actually do something.
520
521#if 0
522 beginTestCase ("rounding ");
523
524 std::uint64_t value = 25000000000000000ull;
525 int offset = -14;
526 canonicalizeRound (false, value, offset, true);
527
528 STAmount one (noIssue(), 1);
529 STAmount two (noIssue(), 2);
530 STAmount three (noIssue(), 3);
531
532 STAmount oneThird1 = divRound (one, three, noIssue(), false);
533 STAmount oneThird2 = divide (one, three, noIssue());
534 STAmount oneThird3 = divRound (one, three, noIssue(), true);
535 log << oneThird1;
536 log << oneThird2;
537 log << oneThird3;
538
539 STAmount twoThird1 = divRound (two, three, noIssue(), false);
540 STAmount twoThird2 = divide (two, three, noIssue());
541 STAmount twoThird3 = divRound (two, three, noIssue(), true);
542 log << twoThird1;
543 log << twoThird2;
544 log << twoThird3;
545
546 STAmount oneA = mulRound (oneThird1, three, noIssue(), false);
547 STAmount oneB = multiply (oneThird2, three, noIssue());
548 STAmount oneC = mulRound (oneThird3, three, noIssue(), true);
549 log << oneA;
550 log << oneB;
551 log << oneC;
552
553 STAmount fourThirdsB = twoThird2 + twoThird2;
554 log << fourThirdsA;
555 log << fourThirdsB;
556 log << fourThirdsC;
557
558 STAmount dripTest1 = mulRound (twoThird2, two, xrpIssue (), false);
559 STAmount dripTest2 = multiply (twoThird2, two, xrpIssue ());
560 STAmount dripTest3 = mulRound (twoThird2, two, xrpIssue (), true);
561 log << dripTest1;
562 log << dripTest2;
563 log << dripTest3;
564#endif
565 }
566
567 void
569 {
571
572 {
573 STAmount const stnum{sfNumber};
574 BEAST_EXPECT(stnum.getSType() == STI_AMOUNT);
575 BEAST_EXPECT(stnum.getText() == "0");
576 BEAST_EXPECT(stnum.isDefault() == true);
577 BEAST_EXPECT(stnum.value() == Number{0});
578 }
579
580 {
581 BEAST_EXPECT(amountFromJson(sfNumber, Json::Value(42)) == XRPAmount(42));
582 BEAST_EXPECT(amountFromJson(sfNumber, Json::Value(-42)) == XRPAmount(-42));
583
584 BEAST_EXPECT(amountFromJson(sfNumber, Json::UInt(42)) == XRPAmount(42));
585
586 BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
587
588 BEAST_EXPECT(amountFromJson(sfNumber, "123") == XRPAmount(123));
589 BEAST_EXPECT(amountFromJson(sfNumber, "-123") == XRPAmount(-123));
590
591 BEAST_EXPECT(amountFromJson(sfNumber, "3.14e2") == XRPAmount(314));
592 BEAST_EXPECT(amountFromJson(sfNumber, "-3.14e2") == XRPAmount(-314));
593
594 BEAST_EXPECT(amountFromJson(sfNumber, "0") == XRPAmount(0));
595 BEAST_EXPECT(amountFromJson(sfNumber, "-0") == XRPAmount(0));
596
597 constexpr auto imin = std::numeric_limits<int>::min();
598 BEAST_EXPECT(amountFromJson(sfNumber, imin) == XRPAmount(imin));
599 BEAST_EXPECT(amountFromJson(sfNumber, std::to_string(imin)) == XRPAmount(imin));
600
601 constexpr auto imax = std::numeric_limits<int>::max();
602 BEAST_EXPECT(amountFromJson(sfNumber, imax) == XRPAmount(imax));
603 BEAST_EXPECT(amountFromJson(sfNumber, std::to_string(imax)) == XRPAmount(imax));
604
605 constexpr auto umax = std::numeric_limits<unsigned int>::max();
606 BEAST_EXPECT(amountFromJson(sfNumber, umax) == XRPAmount(umax));
607 BEAST_EXPECT(amountFromJson(sfNumber, std::to_string(umax)) == XRPAmount(umax));
608
609 // XRP does not handle fractional part
610 try
611 {
612 auto _ = amountFromJson(sfNumber, "0.0");
613 BEAST_EXPECT(false);
614 }
615 catch (std::runtime_error const& e)
616 {
617 std::string const expected = "XRP and MPT must be specified as integral amount.";
618 BEAST_EXPECT(e.what() == expected);
619 }
620
621 // XRP does not handle fractional part
622 try
623 {
624 auto _ = amountFromJson(sfNumber, "1000e-2");
625 BEAST_EXPECT(false);
626 }
627 catch (std::runtime_error const& e)
628 {
629 std::string const expected = "XRP and MPT must be specified as integral amount.";
630 BEAST_EXPECT(e.what() == expected);
631 }
632
633 // Obvious non-numbers tested here
634 try
635 {
636 auto _ = amountFromJson(sfNumber, "");
637 BEAST_EXPECT(false);
638 }
639 catch (std::runtime_error const& e)
640 {
641 std::string const expected = "'' is not a number";
642 BEAST_EXPECT(e.what() == expected);
643 }
644
645 try
646 {
647 auto _ = amountFromJson(sfNumber, "e");
648 BEAST_EXPECT(false);
649 }
650 catch (std::runtime_error const& e)
651 {
652 std::string const expected = "'e' is not a number";
653 BEAST_EXPECT(e.what() == expected);
654 }
655
656 try
657 {
658 auto _ = amountFromJson(sfNumber, "1e");
659 BEAST_EXPECT(false);
660 }
661 catch (std::runtime_error const& e)
662 {
663 std::string const expected = "'1e' is not a number";
664 BEAST_EXPECT(e.what() == expected);
665 }
666
667 try
668 {
669 auto _ = amountFromJson(sfNumber, "e2");
670 BEAST_EXPECT(false);
671 }
672 catch (std::runtime_error const& e)
673 {
674 std::string const expected = "'e2' is not a number";
675 BEAST_EXPECT(e.what() == expected);
676 }
677
678 try
679 {
680 auto _ = amountFromJson(sfNumber, Json::Value());
681 BEAST_EXPECT(false);
682 }
683 catch (std::runtime_error const& e)
684 {
685 std::string const expected = "XRP may not be specified with a null Json value";
686 BEAST_EXPECT(e.what() == expected);
687 }
688
689 try
690 {
691 auto _ = amountFromJson(
692 sfNumber,
693 "123456789012345678901234567890123456789012345678901234"
694 "5678"
695 "901234567890123456789012345678901234567890123456789012"
696 "3456"
697 "78901234567890123456789012345678901234567890");
698 BEAST_EXPECT(false);
699 }
700 catch (std::bad_cast const& e)
701 {
702 BEAST_EXPECT(true);
703 }
704
705 // We do not handle leading zeros
706 try
707 {
708 auto _ = amountFromJson(sfNumber, "001");
709 BEAST_EXPECT(false);
710 }
711 catch (std::runtime_error const& e)
712 {
713 std::string const expected = "'001' is not a number";
714 BEAST_EXPECT(e.what() == expected);
715 }
716
717 try
718 {
719 auto _ = amountFromJson(sfNumber, "000.0");
720 BEAST_EXPECT(false);
721 }
722 catch (std::runtime_error const& e)
723 {
724 std::string const expected = "'000.0' is not a number";
725 BEAST_EXPECT(e.what() == expected);
726 }
727
728 // We do not handle dangling dot
729 try
730 {
731 auto _ = amountFromJson(sfNumber, ".1");
732 BEAST_EXPECT(false);
733 }
734 catch (std::runtime_error const& e)
735 {
736 std::string const expected = "'.1' is not a number";
737 BEAST_EXPECT(e.what() == expected);
738 }
739
740 try
741 {
742 auto _ = amountFromJson(sfNumber, "1.");
743 BEAST_EXPECT(false);
744 }
745 catch (std::runtime_error const& e)
746 {
747 std::string const expected = "'1.' is not a number";
748 BEAST_EXPECT(e.what() == expected);
749 }
750
751 try
752 {
753 auto _ = amountFromJson(sfNumber, "1.e3");
754 BEAST_EXPECT(false);
755 }
756 catch (std::runtime_error const& e)
757 {
758 std::string const expected = "'1.e3' is not a number";
759 BEAST_EXPECT(e.what() == expected);
760 }
761 }
762 }
763
764 void
766 {
767 testcase("STAmount to XRPAmount conversions");
768
769 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
770 Issue const xrp{xrpIssue()};
771
772 for (std::uint64_t drops = 100000000000000000; drops != 1; drops = drops / 10)
773 {
774 auto const t = amountFromString(xrp, std::to_string(drops));
775 auto const s = t.xrp();
776 BEAST_EXPECT(s.drops() == drops);
777 BEAST_EXPECT(t == STAmount(XRPAmount(drops)));
778 BEAST_EXPECT(s == XRPAmount(drops));
779 }
780
781 try
782 {
783 auto const t = amountFromString(usd, "136500");
784 fail(to_string(t.xrp()));
785 }
786 catch (std::logic_error const&)
787 {
788 pass();
789 }
790 catch (std::exception const&)
791 {
792 fail("wrong exception");
793 }
794 }
795
796 void
798 {
799 testcase("STAmount to IOUAmount conversions");
800
801 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
802 Issue const xrp{xrpIssue()};
803
804 for (std::uint64_t dollars = 10000000000; dollars != 1; dollars = dollars / 10)
805 {
806 auto const t = amountFromString(usd, std::to_string(dollars));
807 auto const s = t.iou();
808 BEAST_EXPECT(t == STAmount(s, usd));
809 BEAST_EXPECT(s.mantissa() == t.mantissa());
810 BEAST_EXPECT(s.exponent() == t.exponent());
811 }
812
813 try
814 {
815 auto const t = amountFromString(xrp, "136500");
816 fail(to_string(t.iou()));
817 }
818 catch (std::logic_error const&)
819 {
820 pass();
821 }
822 catch (std::exception const&)
823 {
824 fail("wrong exception");
825 }
826 }
827
828 void
830 {
831 testcase("can add xrp");
832
833 // Adding zero
834 {
835 STAmount const amt1(XRPAmount(0));
836 STAmount const amt2(XRPAmount(1000));
837 BEAST_EXPECT(canAdd(amt1, amt2) == true);
838 }
839
840 // Adding zero
841 {
842 STAmount const amt1(XRPAmount(1000));
843 STAmount const amt2(XRPAmount(0));
844 BEAST_EXPECT(canAdd(amt1, amt2) == true);
845 }
846
847 // Adding two positive XRP amounts
848 {
849 STAmount const amt1(XRPAmount(500));
850 STAmount const amt2(XRPAmount(1500));
851 BEAST_EXPECT(canAdd(amt1, amt2) == true);
852 }
853
854 // Adding two negative XRP amounts
855 {
856 STAmount const amt1(XRPAmount(-500));
857 STAmount const amt2(XRPAmount(-1500));
858 BEAST_EXPECT(canAdd(amt1, amt2) == true);
859 }
860
861 // Adding a positive and a negative XRP amount
862 {
863 STAmount const amt1(XRPAmount(1000));
864 STAmount const amt2(XRPAmount(-1000));
865 BEAST_EXPECT(canAdd(amt1, amt2) == true);
866 }
867
868 // Overflow check for max XRP amounts
869 {
871 STAmount const amt2(XRPAmount(1));
872 BEAST_EXPECT(canAdd(amt1, amt2) == false);
873 }
874
875 // Overflow check for min XRP amounts
876 {
878 amt1 += XRPAmount(1);
879 STAmount const amt2(XRPAmount(-1));
880 BEAST_EXPECT(canAdd(amt1, amt2) == false);
881 }
882 }
883
884 void
886 {
887 testcase("can add iou");
888
889 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
890 Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)};
891
892 // Adding two IOU amounts
893 {
894 STAmount const amt1(usd, 500);
895 STAmount const amt2(usd, 1500);
896 BEAST_EXPECT(canAdd(amt1, amt2) == true);
897 }
898
899 // Adding a positive and a negative IOU amount
900 {
901 STAmount const amt1(usd, 1000);
902 STAmount const amt2(usd, -1000);
903 BEAST_EXPECT(canAdd(amt1, amt2) == true);
904 }
905
906 // Overflow check for max IOU amounts
907 {
909 STAmount const amt2(usd, 1);
910 BEAST_EXPECT(canAdd(amt1, amt2) == false);
911 }
912
913 // Overflow check for min IOU amounts
914 {
916 STAmount const amt2(usd, -1);
917 BEAST_EXPECT(canAdd(amt1, amt2) == false);
918 }
919
920 // Adding XRP and IOU
921 {
922 STAmount const amt1(XRPAmount(1));
923 STAmount const amt2(usd, 1);
924 BEAST_EXPECT(canAdd(amt1, amt2) == false);
925 }
926
927 // Adding different IOU issues (non zero)
928 {
929 STAmount const amt1(usd, 1000);
930 STAmount const amt2(eur, 500);
931 BEAST_EXPECT(canAdd(amt1, amt2) == false);
932 }
933
934 // Adding different IOU issues (zero)
935 {
936 STAmount const amt1(usd, 0);
937 STAmount const amt2(eur, 500);
938 BEAST_EXPECT(canAdd(amt1, amt2) == false);
939 }
940 }
941
942 void
944 {
945 testcase("can add mpt");
946
947 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
948 MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}};
949
950 // Adding zero
951 {
952 STAmount const amt1(mpt, 0);
953 STAmount const amt2(mpt, 1000);
954 BEAST_EXPECT(canAdd(amt1, amt2) == true);
955 }
956
957 // Adding zero
958 {
959 STAmount const amt1(mpt, 1000);
960 STAmount const amt2(mpt, 0);
961 BEAST_EXPECT(canAdd(amt1, amt2) == true);
962 }
963
964 // Adding two positive MPT amounts
965 {
966 STAmount const amt1(mpt, 500);
967 STAmount const amt2(mpt, 1500);
968 BEAST_EXPECT(canAdd(amt1, amt2) == true);
969 }
970
971 // Adding two negative MPT amounts
972 {
973 STAmount const amt1(mpt, -500);
974 STAmount const amt2(mpt, -1500);
975 BEAST_EXPECT(canAdd(amt1, amt2) == true);
976 }
977
978 // Adding a positive and a negative MPT amount
979 {
980 STAmount const amt1(mpt, 1000);
981 STAmount const amt2(mpt, -1000);
982 BEAST_EXPECT(canAdd(amt1, amt2) == true);
983 }
984
985 // Overflow check for max MPT amounts
986 {
988 STAmount const amt2(mpt, 1);
989 BEAST_EXPECT(canAdd(amt1, amt2) == false);
990 }
991
992 // Overflow check for min MPT amounts
993 // Note: Cannot check min MPT overflow because you cannot initialize the
994 // STAmount with a negative MPT amount.
995
996 // Adding MPT and XRP
997 {
998 STAmount const amt1(XRPAmount(1000));
999 STAmount const amt2(mpt, 1000);
1000 BEAST_EXPECT(canAdd(amt1, amt2) == false);
1001 }
1002
1003 // Adding different MPT issues (non zero)
1004 {
1005 STAmount const amt1(mpt2, 500);
1006 STAmount const amt2(mpt, 500);
1007 BEAST_EXPECT(canAdd(amt1, amt2) == false);
1008 }
1009
1010 // Adding different MPT issues (non zero)
1011 {
1012 STAmount const amt1(mpt2, 0);
1013 STAmount const amt2(mpt, 500);
1014 BEAST_EXPECT(canAdd(amt1, amt2) == false);
1015 }
1016 }
1017
1018 void
1020 {
1021 testcase("can subtract xrp");
1022
1023 // Subtracting zero
1024 {
1025 STAmount const amt1(XRPAmount(1000));
1026 STAmount const amt2(XRPAmount(0));
1027 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1028 }
1029
1030 // Subtracting zero
1031 {
1032 STAmount const amt1(XRPAmount(0));
1033 STAmount const amt2(XRPAmount(1000));
1034 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1035 }
1036
1037 // Subtracting two positive XRP amounts
1038 {
1039 STAmount const amt1(XRPAmount(1500));
1040 STAmount const amt2(XRPAmount(500));
1041 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1042 }
1043
1044 // Subtracting two negative XRP amounts
1045 {
1046 STAmount const amt1(XRPAmount(-1500));
1047 STAmount const amt2(XRPAmount(-500));
1048 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1049 }
1050
1051 // Subtracting a positive and a negative XRP amount
1052 {
1053 STAmount const amt1(XRPAmount(1000));
1054 STAmount const amt2(XRPAmount(-1000));
1055 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1056 }
1057
1058 // Underflow check for min XRP amounts
1059 {
1061 amt1 += XRPAmount(1);
1062 STAmount const amt2(XRPAmount(1));
1063 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1064 }
1065
1066 // Overflow check for max XRP amounts
1067 {
1069 STAmount const amt2(XRPAmount(-1));
1070 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1071 }
1072 }
1073
1074 void
1076 {
1077 testcase("can subtract iou");
1078 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
1079 Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)};
1080
1081 // Subtracting two IOU amounts
1082 {
1083 STAmount const amt1(usd, 1500);
1084 STAmount const amt2(usd, 500);
1085 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1086 }
1087
1088 // Subtracting XRP and IOU
1089 {
1090 STAmount const amt1(XRPAmount(1000));
1091 STAmount const amt2(usd, 1000);
1092 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1093 }
1094
1095 // Subtracting different IOU issues (non zero)
1096 {
1097 STAmount const amt1(usd, 1000);
1098 STAmount const amt2(eur, 500);
1099 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1100 }
1101
1102 // Subtracting different IOU issues (zero)
1103 {
1104 STAmount const amt1(usd, 0);
1105 STAmount const amt2(eur, 500);
1106 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1107 }
1108 }
1109
1110 void
1112 {
1113 testcase("can subtract mpt");
1114
1115 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
1116 MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}};
1117
1118 // Subtracting zero
1119 {
1120 STAmount const amt1(mpt, 1000);
1121 STAmount const amt2(mpt, 0);
1122 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1123 }
1124
1125 // Subtracting zero
1126 {
1127 STAmount const amt1(mpt, 0);
1128 STAmount const amt2(mpt, 1000);
1129 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1130 }
1131
1132 // Subtracting two positive MPT amounts
1133 {
1134 STAmount const amt1(mpt, 1500);
1135 STAmount const amt2(mpt, 500);
1136 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1137 }
1138
1139 // Subtracting two negative MPT amounts
1140 {
1141 STAmount const amt1(mpt, -1500);
1142 STAmount const amt2(mpt, -500);
1143 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1144 }
1145
1146 // Subtracting a positive and a negative MPT amount
1147 {
1148 STAmount const amt1(mpt, 1000);
1149 STAmount const amt2(mpt, -1000);
1150 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
1151 }
1152
1153 // Underflow check for min MPT amounts
1154 // Note: Cannot check min MPT underflow because you cannot initialize
1155 // the STAmount with a negative MPT amount.
1156
1157 // Overflow check for max positive MPT amounts (should fail)
1158 {
1160 STAmount const amt2(mpt, -2);
1161 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1162 }
1163
1164 // Subtracting MPT and XRP
1165 {
1166 STAmount const amt1(XRPAmount(1000));
1167 STAmount const amt2(mpt, 1000);
1168 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1169 }
1170
1171 // Subtracting different MPT issues (non zero)
1172 {
1173 STAmount const amt1(mpt, 1000);
1174 STAmount const amt2(mpt2, 500);
1175 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1176 }
1177
1178 // Subtracting different MPT issues (zero)
1179 {
1180 STAmount const amt1(mpt, 0);
1181 STAmount const amt2(mpt2, 500);
1182 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1183 }
1184 }
1185
1186 //--------------------------------------------------------------------------
1187
1188 void
1189 run() override
1190 {
1191 testSetValue();
1195 testUnderflow();
1196 testRounding();
1197 testParseJson();
1200 testCanAddXRP();
1201 testCanAddIOU();
1202 testCanAddMPT();
1206 }
1207};
1208
1209BEAST_DEFINE_TESTSUITE(STAmount, protocol, xrpl);
1210
1211} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:147
void pass()
Record a successful test condition.
Definition suite.h:497
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:485
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:519
A currency issued by an account.
Definition Issue.h:13
Currency currency
Definition Issue.h:15
Number is a floating point type that can represent a wide range of values.
Definition Number.h:207
static STAmount roundSelf(STAmount const &amount)
void testSetValue(std::string const &value, Issue const &issue, bool success=true)
void run() override
Runs the suite.
void mulTest(int a, int b)
static STAmount serializeAndDeserialize(STAmount const &s)
void roundTest(int n, int d, int m)
std::string getFullText() const override
Definition STAmount.cpp:646
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:51
Issue const & issue() const
Definition STAmount.h:470
void add(Serializer &s) const override
Definition STAmount.cpp:752
std::string getText() const override
Definition STAmount.cpp:656
static int const cMaxOffset
Definition STAmount.h:46
bool native() const noexcept
Definition STAmount.h:432
static constexpr std::uint64_t cMinValue
Definition STAmount.h:49
static int const cMinOffset
Definition STAmount.h:45
static constexpr std::uint64_t cMaxNative
Definition STAmount.h:53
Slice slice() const noexcept
Definition Serializer.h:44
T is_same_v
T max(T... args)
T min(T... args)
unsigned int UInt
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:69
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:36
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:97
static void canonicalizeRound(bool native, std::uint64_t &value, int &offset, bool)
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition STAmount.cpp:996
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
SField const sfGeneric
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:987
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:28
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:492
STAmount amountFromQuality(std::uint64_t rate)
Definition STAmount.cpp:975
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:450
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:560
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:105
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:64
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:151
T to_string(T... args)
T what(T... args)