rippled
Loading...
Searching...
No Matches
STAmount_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 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
20#include <test/jtx.h>
21
22#include <xrpl/basics/random.h>
23#include <xrpl/beast/unit_test.h>
24#include <xrpl/protocol/STAmount.h>
25
26namespace ripple {
27
29{
30public:
31 static STAmount
33 {
34 Serializer ser;
35 s.add(ser);
36
37 SerialIter sit(ser.slice());
38 return STAmount(sit, sfGeneric);
39 }
40
41 //--------------------------------------------------------------------------
43 roundSelf(STAmount const& amount)
44 {
45 if (amount.native())
46 return amount;
47
48 std::uint64_t mantissa = amount.mantissa();
49 std::uint64_t valueDigits = mantissa % 1000000000;
50
51 if (valueDigits == 1)
52 {
53 mantissa--;
54
55 if (mantissa < STAmount::cMinValue)
56 return {
57 amount.issue(),
58 mantissa,
59 amount.exponent(),
60 amount.negative()};
61
62 return {
63 amount.issue(),
64 mantissa,
65 amount.exponent(),
66 amount.negative(),
68 }
69
70 if (valueDigits == 999999999)
71 {
72 mantissa++;
73
74 if (mantissa > STAmount::cMaxValue)
75 return {
76 amount.issue(),
77 mantissa,
78 amount.exponent(),
79 amount.negative()};
80
81 return {
82 amount.issue(),
83 mantissa,
84 amount.exponent(),
85 amount.negative(),
87 }
88
89 return amount;
90 }
91
92 void
93 roundTest(int n, int d, int m)
94 {
95 // check STAmount rounding
96 STAmount num(noIssue(), n);
97 STAmount den(noIssue(), d);
98 STAmount mul(noIssue(), m);
99 STAmount quot = divide(STAmount(n), STAmount(d), noIssue());
100 STAmount res = roundSelf(multiply(quot, mul, noIssue()));
101
102 BEAST_EXPECT(!res.native());
103
104 STAmount cmp(noIssue(), (n * m) / d);
105
106 BEAST_EXPECT(!cmp.native());
107
108 BEAST_EXPECT(cmp.issue().currency == res.issue().currency);
109
110 if (res != cmp)
111 {
112 log << "(" << num.getText() << "/" << den.getText() << ") X "
113 << mul.getText() << " = " << res.getText() << " not "
114 << cmp.getText();
115 fail("Rounding");
116 return;
117 }
118 }
119
120 void
121 mulTest(int a, int b)
122 {
123 STAmount aa(noIssue(), a);
124 STAmount bb(noIssue(), b);
125 STAmount prod1(multiply(aa, bb, noIssue()));
126
127 BEAST_EXPECT(!prod1.native());
128
129 STAmount prod2(
130 noIssue(),
131 static_cast<std::uint64_t>(a) * static_cast<std::uint64_t>(b));
132
133 if (prod1 != prod2)
134 {
135 log << "nn(" << aa.getFullText() << " * " << bb.getFullText()
136 << ") = " << prod1.getFullText() << " not "
137 << prod2.getFullText();
138 fail("Multiplication result is not exact");
139 }
140 }
141
142 //--------------------------------------------------------------------------
143
144 void
146 std::string const& value,
147 Issue const& issue,
148 bool success = true)
149 {
150 try
151 {
152 STAmount const amount = amountFromString(issue, value);
153 BEAST_EXPECT(amount.getText() == value);
154 }
155 catch (std::exception const&)
156 {
157 BEAST_EXPECT(!success);
158 }
159 }
160
161 void
163 {
164 {
165 testcase("set value (native)");
166
167 Issue const xrp(xrpIssue());
168
169 // fractional XRP (i.e. drops)
170 testSetValue("1", xrp);
171 testSetValue("22", xrp);
172 testSetValue("333", xrp);
173 testSetValue("4444", xrp);
174 testSetValue("55555", xrp);
175 testSetValue("666666", xrp);
176
177 // 1 XRP up to 100 billion, in powers of 10 (in drops)
178 testSetValue("1000000", xrp);
179 testSetValue("10000000", xrp);
180 testSetValue("100000000", xrp);
181 testSetValue("1000000000", xrp);
182 testSetValue("10000000000", xrp);
183 testSetValue("100000000000", xrp);
184 testSetValue("1000000000000", xrp);
185 testSetValue("10000000000000", xrp);
186 testSetValue("100000000000000", xrp);
187 testSetValue("1000000000000000", xrp);
188 testSetValue("10000000000000000", xrp);
189 testSetValue("100000000000000000", xrp);
190
191 // Invalid native values:
192 testSetValue("1.1", xrp, false);
193 testSetValue("100000000000000001", xrp, false);
194 testSetValue("1000000000000000000", xrp, false);
195 }
196
197 {
198 testcase("set value (iou)");
199
200 Issue const usd(Currency(0x5553440000000000), AccountID(0x4985601));
201
202 testSetValue("1", usd);
203 testSetValue("10", usd);
204 testSetValue("100", usd);
205 testSetValue("1000", usd);
206 testSetValue("10000", usd);
207 testSetValue("100000", usd);
208 testSetValue("1000000", usd);
209 testSetValue("10000000", usd);
210 testSetValue("100000000", usd);
211 testSetValue("1000000000", usd);
212 testSetValue("10000000000", usd);
213
214 testSetValue("1234567.1", usd);
215 testSetValue("1234567.12", usd);
216 testSetValue("1234567.123", usd);
217 testSetValue("1234567.1234", usd);
218 testSetValue("1234567.12345", usd);
219 testSetValue("1234567.123456", usd);
220 testSetValue("1234567.1234567", usd);
221 testSetValue("1234567.12345678", usd);
222 testSetValue("1234567.123456789", usd);
223 }
224 }
225
226 //--------------------------------------------------------------------------
227
228 void
230 {
231 testcase("native currency");
232 STAmount zeroSt, one(1), hundred(100);
233 // VFALCO NOTE Why repeat "STAmount fail" so many times??
234 unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail");
235 unexpected(serializeAndDeserialize(one) != one, "STAmount fail");
237 serializeAndDeserialize(hundred) != hundred, "STAmount fail");
238 unexpected(!zeroSt.native(), "STAmount fail");
239 unexpected(!hundred.native(), "STAmount fail");
240 unexpected(zeroSt != beast::zero, "STAmount fail");
241 unexpected(one == beast::zero, "STAmount fail");
242 unexpected(hundred == beast::zero, "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(!(zeroSt == zeroSt), "STAmount fail");
280 unexpected((zeroSt == one), "STAmount fail");
281 unexpected((zeroSt == hundred), "STAmount fail");
282 unexpected((one == zeroSt), "STAmount fail");
283 unexpected(!(one == one), "STAmount fail");
284 unexpected((one == hundred), "STAmount fail");
285 unexpected((hundred == zeroSt), "STAmount fail");
286 unexpected((hundred == one), "STAmount fail");
287 unexpected(!(hundred == hundred), "STAmount fail");
288 unexpected((zeroSt != zeroSt), "STAmount fail");
289 unexpected(!(zeroSt != one), "STAmount fail");
290 unexpected(!(zeroSt != hundred), "STAmount fail");
291 unexpected(!(one != zeroSt), "STAmount fail");
292 unexpected((one != one), "STAmount fail");
293 unexpected(!(one != hundred), "STAmount fail");
294 unexpected(!(hundred != zeroSt), "STAmount fail");
295 unexpected(!(hundred != one), "STAmount fail");
296 unexpected((hundred != hundred), "STAmount fail");
297 unexpected(STAmount().getText() != "0", "STAmount fail");
298 unexpected(STAmount(31).getText() != "31", "STAmount fail");
299 unexpected(STAmount(310).getText() != "310", "STAmount fail");
300 unexpected(to_string(Currency()) != "XRP", "cHC(XRP)");
301 Currency c;
302 unexpected(!to_currency(c, "USD"), "create USD currency");
303 unexpected(to_string(c) != "USD", "check USD currency");
304
305 std::string const cur = "015841551A748AD2C1F76FF6ECB0CCCD00000000";
306 unexpected(!to_currency(c, cur), "create custom currency");
307 unexpected(to_string(c) != cur, "check custom currency");
308 }
309
310 //--------------------------------------------------------------------------
311
312 void
314 {
315 testcase("custom currency");
316 STAmount zeroSt(noIssue()), one(noIssue(), 1), hundred(noIssue(), 100);
317 unexpected(serializeAndDeserialize(zeroSt) != zeroSt, "STAmount fail");
318 unexpected(serializeAndDeserialize(one) != one, "STAmount fail");
320 serializeAndDeserialize(hundred) != hundred, "STAmount fail");
321 unexpected(zeroSt.native(), "STAmount fail");
322 unexpected(hundred.native(), "STAmount fail");
323 unexpected(zeroSt != beast::zero, "STAmount fail");
324 unexpected(one == beast::zero, "STAmount fail");
325 unexpected(hundred == beast::zero, "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(!(zeroSt == zeroSt), "STAmount fail");
363 unexpected((zeroSt == one), "STAmount fail");
364 unexpected((zeroSt == hundred), "STAmount fail");
365 unexpected((one == zeroSt), "STAmount fail");
366 unexpected(!(one == one), "STAmount fail");
367 unexpected((one == hundred), "STAmount fail");
368 unexpected((hundred == zeroSt), "STAmount fail");
369 unexpected((hundred == one), "STAmount fail");
370 unexpected(!(hundred == hundred), "STAmount fail");
371 unexpected((zeroSt != zeroSt), "STAmount fail");
372 unexpected(!(zeroSt != one), "STAmount fail");
373 unexpected(!(zeroSt != hundred), "STAmount fail");
374 unexpected(!(one != zeroSt), "STAmount fail");
375 unexpected((one != one), "STAmount fail");
376 unexpected(!(one != hundred), "STAmount fail");
377 unexpected(!(hundred != zeroSt), "STAmount fail");
378 unexpected(!(hundred != one), "STAmount fail");
379 unexpected((hundred != hundred), "STAmount fail");
380 unexpected(STAmount(noIssue()).getText() != "0", "STAmount fail");
381 unexpected(STAmount(noIssue(), 31).getText() != "31", "STAmount fail");
383 STAmount(noIssue(), 31, 1).getText() != "310", "STAmount fail");
385 STAmount(noIssue(), 31, -1).getText() != "3.1", "STAmount fail");
387 STAmount(noIssue(), 31, -2).getText() != "0.31", "STAmount fail");
390 .getText() != "60",
391 "STAmount multiply fail 1");
394 .getText() != "60",
395 "STAmount multiply fail 2");
397 multiply(STAmount(20), STAmount(3), noIssue()).getText() != "60",
398 "STAmount multiply fail 3");
400 multiply(STAmount(20), STAmount(3), xrpIssue()).getText() != "60",
401 "STAmount multiply fail 4");
402
403 if (divide(STAmount(noIssue(), 60), STAmount(3), noIssue()).getText() !=
404 "20")
405 {
406 log << "60/3 = "
407 << divide(STAmount(noIssue(), 60), STAmount(3), noIssue())
408 .getText();
409 fail("STAmount divide fail");
410 }
411 else
412 {
413 pass();
414 }
415
418 .getText() != "20",
419 "STAmount divide fail");
420
423 .getText() != "20",
424 "STAmount divide fail");
425
428 .getText() != "20",
429 "STAmount divide fail");
430
431 STAmount a1(noIssue(), 60), a2(noIssue(), 10, -1);
432
434 divide(a2, a1, noIssue()) != amountFromQuality(getRate(a1, a2)),
435 "STAmount setRate(getRate) fail");
436
438 divide(a1, a2, noIssue()) != amountFromQuality(getRate(a2, a1)),
439 "STAmount setRate(getRate) fail");
440 }
441
442 //--------------------------------------------------------------------------
443
444 void
446 {
447 testcase("arithmetic");
448
449 // Test currency multiplication and division operations such as
450 // convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed,
451 // and getNeeded
452
454 getRate(STAmount(1), STAmount(10)) !=
455 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
456 "STAmount getRate fail 1");
457
459 getRate(STAmount(10), STAmount(1)) !=
460 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
461 "STAmount getRate fail 2");
462
464 getRate(STAmount(noIssue(), 1), STAmount(noIssue(), 10)) !=
465 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
466 "STAmount getRate fail 3");
467
469 getRate(STAmount(noIssue(), 10), STAmount(noIssue(), 1)) !=
470 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
471 "STAmount getRate fail 4");
472
474 getRate(STAmount(noIssue(), 1), STAmount(10)) !=
475 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
476 "STAmount getRate fail 5");
477
479 getRate(STAmount(noIssue(), 10), STAmount(1)) !=
480 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
481 "STAmount getRate fail 6");
482
484 getRate(STAmount(1), STAmount(noIssue(), 10)) !=
485 (((100ull - 14) << (64 - 8)) | 1000000000000000ull),
486 "STAmount getRate fail 7");
487
489 getRate(STAmount(10), STAmount(noIssue(), 1)) !=
490 (((100ull - 16) << (64 - 8)) | 1000000000000000ull),
491 "STAmount getRate fail 8");
492
493 roundTest(1, 3, 3);
494 roundTest(2, 3, 9);
495 roundTest(1, 7, 21);
496 roundTest(1, 2, 4);
497 roundTest(3, 9, 18);
498 roundTest(7, 11, 44);
499
500 for (int i = 0; i <= 100000; ++i)
501 {
502 mulTest(rand_int(10000000), rand_int(10000000));
503 }
504 }
505
506 //--------------------------------------------------------------------------
507
508 void
510 {
511 testcase("underflow");
512
513 STAmount bigNative(STAmount::cMaxNative / 2);
514 STAmount bigValue(
515 noIssue(),
518 STAmount smallValue(
519 noIssue(),
522 STAmount zeroSt(noIssue(), 0);
523
524 STAmount smallXsmall = multiply(smallValue, smallValue, noIssue());
525
526 BEAST_EXPECT(smallXsmall == beast::zero);
527
528 STAmount bigDsmall = divide(smallValue, bigValue, noIssue());
529
530 BEAST_EXPECT(bigDsmall == beast::zero);
531
532 BEAST_EXPECT(bigDsmall == beast::zero);
533
534 bigDsmall = divide(smallValue, bigValue, xrpIssue());
535
536 BEAST_EXPECT(bigDsmall == beast::zero);
537
538 bigDsmall = divide(smallValue, bigNative, xrpIssue());
539
540 BEAST_EXPECT(bigDsmall == beast::zero);
541
542 // very bad offer
543 std::uint64_t r = getRate(smallValue, bigValue);
544
545 BEAST_EXPECT(r == 0);
546
547 // very good offer
548 r = getRate(bigValue, smallValue);
549
550 BEAST_EXPECT(r == 0);
551 }
552
553 //--------------------------------------------------------------------------
554
555 void
557 {
558 // VFALCO TODO There are no actual tests here, just printed output?
559 // Change this to actually do something.
560
561#if 0
562 beginTestCase ("rounding ");
563
564 std::uint64_t value = 25000000000000000ull;
565 int offset = -14;
566 canonicalizeRound (false, value, offset, true);
567
568 STAmount one (noIssue(), 1);
569 STAmount two (noIssue(), 2);
570 STAmount three (noIssue(), 3);
571
572 STAmount oneThird1 = divRound (one, three, noIssue(), false);
573 STAmount oneThird2 = divide (one, three, noIssue());
574 STAmount oneThird3 = divRound (one, three, noIssue(), true);
575 log << oneThird1;
576 log << oneThird2;
577 log << oneThird3;
578
579 STAmount twoThird1 = divRound (two, three, noIssue(), false);
580 STAmount twoThird2 = divide (two, three, noIssue());
581 STAmount twoThird3 = divRound (two, three, noIssue(), true);
582 log << twoThird1;
583 log << twoThird2;
584 log << twoThird3;
585
586 STAmount oneA = mulRound (oneThird1, three, noIssue(), false);
587 STAmount oneB = multiply (oneThird2, three, noIssue());
588 STAmount oneC = mulRound (oneThird3, three, noIssue(), true);
589 log << oneA;
590 log << oneB;
591 log << oneC;
592
593 STAmount fourThirdsB = twoThird2 + twoThird2;
594 log << fourThirdsA;
595 log << fourThirdsB;
596 log << fourThirdsC;
597
598 STAmount dripTest1 = mulRound (twoThird2, two, xrpIssue (), false);
599 STAmount dripTest2 = multiply (twoThird2, two, xrpIssue ());
600 STAmount dripTest3 = mulRound (twoThird2, two, xrpIssue (), true);
601 log << dripTest1;
602 log << dripTest2;
603 log << dripTest3;
604#endif
605 }
606
607 void
609 {
610 testcase("STAmount to XRPAmount conversions");
611
612 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
613 Issue const xrp{xrpIssue()};
614
615 for (std::uint64_t drops = 100000000000000000; drops != 1;
616 drops = drops / 10)
617 {
618 auto const t = amountFromString(xrp, std::to_string(drops));
619 auto const s = t.xrp();
620 BEAST_EXPECT(s.drops() == drops);
621 BEAST_EXPECT(t == STAmount(XRPAmount(drops)));
622 BEAST_EXPECT(s == XRPAmount(drops));
623 }
624
625 try
626 {
627 auto const t = amountFromString(usd, "136500");
628 fail(to_string(t.xrp()));
629 }
630 catch (std::logic_error const&)
631 {
632 pass();
633 }
634 catch (std::exception const&)
635 {
636 fail("wrong exception");
637 }
638 }
639
640 void
642 {
643 testcase("STAmount to IOUAmount conversions");
644
645 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
646 Issue const xrp{xrpIssue()};
647
648 for (std::uint64_t dollars = 10000000000; dollars != 1;
649 dollars = dollars / 10)
650 {
651 auto const t = amountFromString(usd, std::to_string(dollars));
652 auto const s = t.iou();
653 BEAST_EXPECT(t == STAmount(s, usd));
654 BEAST_EXPECT(s.mantissa() == t.mantissa());
655 BEAST_EXPECT(s.exponent() == t.exponent());
656 }
657
658 try
659 {
660 auto const t = amountFromString(xrp, "136500");
661 fail(to_string(t.iou()));
662 }
663 catch (std::logic_error const&)
664 {
665 pass();
666 }
667 catch (std::exception const&)
668 {
669 fail("wrong exception");
670 }
671 }
672
673 void
675 {
676 testcase("can add xrp");
677
678 // Adding zero
679 {
680 STAmount amt1(XRPAmount(0));
681 STAmount amt2(XRPAmount(1000));
682 BEAST_EXPECT(canAdd(amt1, amt2) == true);
683 }
684
685 // Adding zero
686 {
687 STAmount amt1(XRPAmount(1000));
688 STAmount amt2(XRPAmount(0));
689 BEAST_EXPECT(canAdd(amt1, amt2) == true);
690 }
691
692 // Adding two positive XRP amounts
693 {
694 STAmount amt1(XRPAmount(500));
695 STAmount amt2(XRPAmount(1500));
696 BEAST_EXPECT(canAdd(amt1, amt2) == true);
697 }
698
699 // Adding two negative XRP amounts
700 {
701 STAmount amt1(XRPAmount(-500));
702 STAmount amt2(XRPAmount(-1500));
703 BEAST_EXPECT(canAdd(amt1, amt2) == true);
704 }
705
706 // Adding a positive and a negative XRP amount
707 {
708 STAmount amt1(XRPAmount(1000));
709 STAmount amt2(XRPAmount(-1000));
710 BEAST_EXPECT(canAdd(amt1, amt2) == true);
711 }
712
713 // Overflow check for max XRP amounts
714 {
716 STAmount amt2(XRPAmount(1));
717 BEAST_EXPECT(canAdd(amt1, amt2) == false);
718 }
719
720 // Overflow check for min XRP amounts
721 {
723 amt1 += XRPAmount(1);
724 STAmount amt2(XRPAmount(-1));
725 BEAST_EXPECT(canAdd(amt1, amt2) == false);
726 }
727 }
728
729 void
731 {
732 testcase("can add iou");
733
734 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
735 Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)};
736
737 // Adding two IOU amounts
738 {
739 STAmount amt1(usd, 500);
740 STAmount amt2(usd, 1500);
741 BEAST_EXPECT(canAdd(amt1, amt2) == true);
742 }
743
744 // Adding a positive and a negative IOU amount
745 {
746 STAmount amt1(usd, 1000);
747 STAmount amt2(usd, -1000);
748 BEAST_EXPECT(canAdd(amt1, amt2) == true);
749 }
750
751 // Overflow check for max IOU amounts
752 {
754 STAmount amt2(usd, 1);
755 BEAST_EXPECT(canAdd(amt1, amt2) == false);
756 }
757
758 // Overflow check for min IOU amounts
759 {
761 STAmount amt2(usd, -1);
762 BEAST_EXPECT(canAdd(amt1, amt2) == false);
763 }
764
765 // Adding XRP and IOU
766 {
767 STAmount amt1(XRPAmount(1));
768 STAmount amt2(usd, 1);
769 BEAST_EXPECT(canAdd(amt1, amt2) == false);
770 }
771
772 // Adding different IOU issues (non zero)
773 {
774 STAmount amt1(usd, 1000);
775 STAmount amt2(eur, 500);
776 BEAST_EXPECT(canAdd(amt1, amt2) == false);
777 }
778
779 // Adding different IOU issues (zero)
780 {
781 STAmount amt1(usd, 0);
782 STAmount amt2(eur, 500);
783 BEAST_EXPECT(canAdd(amt1, amt2) == false);
784 }
785 }
786
787 void
789 {
790 testcase("can add mpt");
791
792 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
793 MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}};
794
795 // Adding zero
796 {
797 STAmount amt1(mpt, 0);
798 STAmount amt2(mpt, 1000);
799 BEAST_EXPECT(canAdd(amt1, amt2) == true);
800 }
801
802 // Adding zero
803 {
804 STAmount amt1(mpt, 1000);
805 STAmount amt2(mpt, 0);
806 BEAST_EXPECT(canAdd(amt1, amt2) == true);
807 }
808
809 // Adding two positive MPT amounts
810 {
811 STAmount amt1(mpt, 500);
812 STAmount amt2(mpt, 1500);
813 BEAST_EXPECT(canAdd(amt1, amt2) == true);
814 }
815
816 // Adding two negative MPT amounts
817 {
818 STAmount amt1(mpt, -500);
819 STAmount amt2(mpt, -1500);
820 BEAST_EXPECT(canAdd(amt1, amt2) == true);
821 }
822
823 // Adding a positive and a negative MPT amount
824 {
825 STAmount amt1(mpt, 1000);
826 STAmount amt2(mpt, -1000);
827 BEAST_EXPECT(canAdd(amt1, amt2) == true);
828 }
829
830 // Overflow check for max MPT amounts
831 {
832 STAmount amt1(
834 STAmount amt2(mpt, 1);
835 BEAST_EXPECT(canAdd(amt1, amt2) == false);
836 }
837
838 // Overflow check for min MPT amounts
839 // Note: Cannot check min MPT overflow because you cannot initialize the
840 // STAmount with a negative MPT amount.
841
842 // Adding MPT and XRP
843 {
844 STAmount amt1(XRPAmount(1000));
845 STAmount amt2(mpt, 1000);
846 BEAST_EXPECT(canAdd(amt1, amt2) == false);
847 }
848
849 // Adding different MPT issues (non zero)
850 {
851 STAmount amt1(mpt2, 500);
852 STAmount amt2(mpt, 500);
853 BEAST_EXPECT(canAdd(amt1, amt2) == false);
854 }
855
856 // Adding different MPT issues (non zero)
857 {
858 STAmount amt1(mpt2, 0);
859 STAmount amt2(mpt, 500);
860 BEAST_EXPECT(canAdd(amt1, amt2) == false);
861 }
862 }
863
864 void
866 {
867 testcase("can subtract xrp");
868
869 // Subtracting zero
870 {
871 STAmount amt1(XRPAmount(1000));
872 STAmount amt2(XRPAmount(0));
873 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
874 }
875
876 // Subtracting zero
877 {
878 STAmount amt1(XRPAmount(0));
879 STAmount amt2(XRPAmount(1000));
880 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
881 }
882
883 // Subtracting two positive XRP amounts
884 {
885 STAmount amt1(XRPAmount(1500));
886 STAmount amt2(XRPAmount(500));
887 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
888 }
889
890 // Subtracting two negative XRP amounts
891 {
892 STAmount amt1(XRPAmount(-1500));
893 STAmount amt2(XRPAmount(-500));
894 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
895 }
896
897 // Subtracting a positive and a negative XRP amount
898 {
899 STAmount amt1(XRPAmount(1000));
900 STAmount amt2(XRPAmount(-1000));
901 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
902 }
903
904 // Underflow check for min XRP amounts
905 {
907 amt1 += XRPAmount(1);
908 STAmount amt2(XRPAmount(1));
909 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
910 }
911
912 // Overflow check for max XRP amounts
913 {
915 STAmount amt2(XRPAmount(-1));
916 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
917 }
918 }
919
920 void
922 {
923 testcase("can subtract iou");
924 Issue const usd{Currency(0x5553440000000000), AccountID(0x4985601)};
925 Issue const eur{Currency(0x4555520000000000), AccountID(0x4985601)};
926
927 // Subtracting two IOU amounts
928 {
929 STAmount amt1(usd, 1500);
930 STAmount amt2(usd, 500);
931 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
932 }
933
934 // Subtracting XRP and IOU
935 {
936 STAmount amt1(XRPAmount(1000));
937 STAmount amt2(usd, 1000);
938 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
939 }
940
941 // Subtracting different IOU issues (non zero)
942 {
943 STAmount amt1(usd, 1000);
944 STAmount amt2(eur, 500);
945 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
946 }
947
948 // Subtracting different IOU issues (zero)
949 {
950 STAmount amt1(usd, 0);
951 STAmount amt2(eur, 500);
952 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
953 }
954 }
955
956 void
958 {
959 testcase("can subtract mpt");
960
961 MPTIssue const mpt{MPTIssue{makeMptID(1, AccountID(0x4985601))}};
962 MPTIssue const mpt2{MPTIssue{makeMptID(2, AccountID(0x4985601))}};
963
964 // Subtracting zero
965 {
966 STAmount amt1(mpt, 1000);
967 STAmount amt2(mpt, 0);
968 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
969 }
970
971 // Subtracting zero
972 {
973 STAmount amt1(mpt, 0);
974 STAmount amt2(mpt, 1000);
975 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
976 }
977
978 // Subtracting two positive MPT amounts
979 {
980 STAmount amt1(mpt, 1500);
981 STAmount amt2(mpt, 500);
982 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
983 }
984
985 // Subtracting two negative MPT amounts
986 {
987 STAmount amt1(mpt, -1500);
988 STAmount amt2(mpt, -500);
989 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
990 }
991
992 // Subtracting a positive and a negative MPT amount
993 {
994 STAmount amt1(mpt, 1000);
995 STAmount amt2(mpt, -1000);
996 BEAST_EXPECT(canSubtract(amt1, amt2) == true);
997 }
998
999 // Underflow check for min MPT amounts
1000 // Note: Cannot check min MPT underflow because you cannot initialize
1001 // the STAmount with a negative MPT amount.
1002
1003 // Overflow check for max positive MPT amounts (should fail)
1004 {
1005 STAmount amt1(
1007 STAmount amt2(mpt, -2);
1008 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1009 }
1010
1011 // Subtracting MPT and XRP
1012 {
1013 STAmount amt1(XRPAmount(1000));
1014 STAmount amt2(mpt, 1000);
1015 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1016 }
1017
1018 // Subtracting different MPT issues (non zero)
1019 {
1020 STAmount amt1(mpt, 1000);
1021 STAmount amt2(mpt2, 500);
1022 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1023 }
1024
1025 // Subtracting different MPT issues (zero)
1026 {
1027 STAmount amt1(mpt, 0);
1028 STAmount amt2(mpt2, 500);
1029 BEAST_EXPECT(canSubtract(amt1, amt2) == false);
1030 }
1031 }
1032
1033 //--------------------------------------------------------------------------
1034
1035 void
1036 run() override
1037 {
1038 testSetValue();
1042 testUnderflow();
1043 testRounding();
1046 testCanAddXRP();
1047 testCanAddIOU();
1048 testCanAddMPT();
1052 }
1053};
1054
1055BEAST_DEFINE_TESTSUITE(STAmount, protocol, ripple);
1056
1057} // namespace ripple
A testsuite class.
Definition suite.h:55
log_os< char > log
Logging output stream.
Definition suite.h:152
void pass()
Record a successful test condition.
Definition suite.h:511
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:499
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:533
A currency issued by an account.
Definition Issue.h:33
Currency currency
Definition Issue.h:35
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:452
static int const cMaxOffset
Definition STAmount.h:66
static int const cMinOffset
Definition STAmount.h:65
static std::uint64_t const cMinValue
Definition STAmount.h:69
void add(Serializer &s) const override
Definition STAmount.cpp:803
static std::uint64_t const cMaxValue
Definition STAmount.h:70
std::string getText() const override
Definition STAmount.cpp:706
bool negative() const noexcept
Definition STAmount.h:471
Issue const & issue() const
Definition STAmount.h:496
static std::uint64_t const cMaxNative
Definition STAmount.h:71
std::uint64_t mantissa() const noexcept
Definition STAmount.h:477
std::string getFullText() const override
Definition STAmount.cpp:696
bool native() const noexcept
Definition STAmount.h:458
Slice slice() const noexcept
Definition Serializer.h:66
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:48
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:115
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:93
constexpr Number one
Definition Number.cpp:175
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:608
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:528
STAmount amountFromQuality(std::uint64_t rate)
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:486
base_uint< 160, detail::CurrencyTag > Currency
Currency is a hash representing a specific currency.
Definition UintTypes.h:56
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:123
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:630
STAmount amountFromString(Asset const &asset, std::string const &amount)
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:170
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:84
static void canonicalizeRound(bool native, std::uint64_t &value, int &offset, bool)
T to_string(T... args)