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