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