rippled
Loading...
Searching...
No Matches
STAmount.cpp
1#include <xrpl/basics/LocalValue.h>
2#include <xrpl/basics/Log.h>
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/core/LexicalCast.h>
8#include <xrpl/beast/utility/Zero.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/json/json_forwards.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/IOUAmount.h>
15#include <xrpl/protocol/Issue.h>
16#include <xrpl/protocol/MPTAmount.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/Protocol.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STBase.h>
22#include <xrpl/protocol/STNumber.h>
23#include <xrpl/protocol/Serializer.h>
24#include <xrpl/protocol/SystemParameters.h>
25#include <xrpl/protocol/UintTypes.h>
26#include <xrpl/protocol/XRPAmount.h>
27#include <xrpl/protocol/jss.h>
28
29#include <boost/algorithm/string/classification.hpp>
30#include <boost/algorithm/string/split.hpp>
31#include <boost/multiprecision/detail/default_ops.hpp>
32#include <boost/multiprecision/fwd.hpp>
33#include <boost/regex/v5/regbase.hpp>
34#include <boost/regex/v5/regex.hpp>
35#include <boost/regex/v5/regex_fwd.hpp>
36#include <boost/regex/v5/regex_match.hpp>
37
38#include <algorithm>
39#include <cstddef>
40#include <cstdint>
41#include <exception>
42#include <iterator>
43#include <limits>
44#include <memory>
45#include <stdexcept>
46#include <string>
47#include <utility>
48#include <vector>
49
50namespace ripple {
51
52static std::uint64_t const tenTo14 = 100000000000000ull;
53static std::uint64_t const tenTo14m1 = tenTo14 - 1;
54static std::uint64_t const tenTo17 = tenTo14 * 1000;
55
56//------------------------------------------------------------------------------
57static std::int64_t
58getInt64Value(STAmount const& amount, bool valid, char const* error)
59{
60 if (!valid)
61 Throw<std::runtime_error>(error);
62 XRPL_ASSERT(
63 amount.exponent() == 0, "ripple::getInt64Value : exponent is zero");
64
65 auto ret = static_cast<std::int64_t>(amount.mantissa());
66
67 XRPL_ASSERT(
68 static_cast<std::uint64_t>(ret) == amount.mantissa(),
69 "ripple::getInt64Value : mantissa must roundtrip");
70
71 if (amount.negative())
72 ret = -ret;
73
74 return ret;
75}
76
77static std::int64_t
78getSNValue(STAmount const& amount)
79{
80 return getInt64Value(amount, amount.native(), "amount is not native!");
81}
82
83static std::int64_t
84getMPTValue(STAmount const& amount)
85{
86 return getInt64Value(
87 amount, amount.holds<MPTIssue>(), "amount is not MPT!");
88}
89
90static bool
91areComparable(STAmount const& v1, STAmount const& v2)
92{
93 if (v1.holds<Issue>() && v2.holds<Issue>())
94 return v1.native() == v2.native() &&
95 v1.get<Issue>().currency == v2.get<Issue>().currency;
96 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>())
97 return v1.get<MPTIssue>() == v2.get<MPTIssue>();
98 return false;
99}
100
101STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
102{
103 std::uint64_t value = sit.get64();
104
105 // native or MPT
106 if ((value & cIssuedCurrency) == 0)
107 {
108 if ((value & cMPToken) != 0)
109 {
110 // is MPT
111 mOffset = 0;
112 mIsNegative = (value & cPositive) == 0;
113 mValue = (value << 8) | sit.get8();
114 mAsset = sit.get192();
115 return;
116 }
117 // else is XRP
118 mAsset = xrpIssue();
119 // positive
120 if ((value & cPositive) != 0)
121 {
123 mOffset = 0;
124 mIsNegative = false;
125 return;
126 }
127
128 // negative
129 if (value == 0)
130 Throw<std::runtime_error>("negative zero is not canonical");
131
133 mOffset = 0;
134 mIsNegative = true;
135 return;
136 }
137
138 Issue issue;
139 issue.currency = sit.get160();
140
141 if (isXRP(issue.currency))
142 Throw<std::runtime_error>("invalid native currency");
143
144 issue.account = sit.get160();
145
146 if (isXRP(issue.account))
147 Throw<std::runtime_error>("invalid native account");
148
149 // 10 bits for the offset, sign and "not native" flag
150 int offset = static_cast<int>(value >> (64 - 10));
151
152 value &= ~(1023ull << (64 - 10));
153
154 if (value)
155 {
156 bool isNegative = (offset & 256) == 0;
157 offset = (offset & 255) - 97; // center the range
158
159 if (value < cMinValue || value > cMaxValue || offset < cMinOffset ||
160 offset > cMaxOffset)
161 {
162 Throw<std::runtime_error>("invalid currency value");
163 }
164
165 mAsset = issue;
166 mValue = value;
167 mOffset = offset;
168 mIsNegative = isNegative;
169 canonicalize();
170 return;
171 }
172
173 if (offset != 512)
174 Throw<std::runtime_error>("invalid currency value");
175
176 mAsset = issue;
177 mValue = 0;
178 mOffset = 0;
179 mIsNegative = false;
180 canonicalize();
181}
182
184 : STBase(name), mAsset(xrpIssue()), mOffset(0)
185{
186 set(mantissa);
187}
188
189STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative)
190 : STBase(name)
191 , mAsset(xrpIssue())
192 , mValue(mantissa)
193 , mOffset(0)
194 , mIsNegative(negative)
195{
196 XRPL_ASSERT(
198 "ripple::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
199 "mantissa input");
200}
201
202STAmount::STAmount(SField const& name, STAmount const& from)
203 : STBase(name)
204 , mAsset(from.mAsset)
205 , mValue(from.mValue)
206 , mOffset(from.mOffset)
207 , mIsNegative(from.mIsNegative)
208{
209 XRPL_ASSERT(
211 "ripple::STAmount::STAmount(SField, STAmount) : maximum input");
212 canonicalize();
213}
214
215//------------------------------------------------------------------------------
216
217STAmount::STAmount(std::uint64_t mantissa, bool negative)
218 : mAsset(xrpIssue())
219 , mValue(mantissa)
220 , mOffset(0)
221 , mIsNegative(mantissa != 0 && negative)
222{
223 XRPL_ASSERT(
225 "ripple::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
226 "input");
227}
228
230 : mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero)
231{
232 if (mIsNegative)
233 mValue = unsafe_cast<std::uint64_t>(-amount.drops());
234 else
235 mValue = unsafe_cast<std::uint64_t>(amount.drops());
236
237 canonicalize();
238}
239
242{
243 return std::make_unique<STAmount>(sit, name);
244}
245
246STBase*
247STAmount::copy(std::size_t n, void* buf) const
248{
249 return emplace(n, buf, *this);
250}
251
252STBase*
254{
255 return emplace(n, buf, std::move(*this));
256}
257
258//------------------------------------------------------------------------------
259//
260// Conversion
261//
262//------------------------------------------------------------------------------
265{
266 if (!native())
267 Throw<std::logic_error>(
268 "Cannot return non-native STAmount as XRPAmount");
269
270 auto drops = static_cast<XRPAmount::value_type>(mValue);
271 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::xrp : amount is canonical");
272
273 if (mIsNegative)
274 drops = -drops;
275
276 return XRPAmount{drops};
277}
278
281{
282 if (integral())
283 Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
284
285 auto mantissa = static_cast<std::int64_t>(mValue);
286 auto exponent = mOffset;
287
288 if (mIsNegative)
290
291 return {mantissa, exponent};
292}
293
296{
297 if (!holds<MPTIssue>())
298 Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
299
300 auto value = static_cast<MPTAmount::value_type>(mValue);
301 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::mpt : amount is canonical");
302
303 if (mIsNegative)
304 value = -value;
305
306 return MPTAmount{value};
307}
308
311{
312 XRPL_ASSERT(
313 native() == false,
314 "ripple::STAmount::operator=(IOUAmount) : is not XRP");
315 mOffset = iou.exponent();
316 mIsNegative = iou < beast::zero;
317 if (mIsNegative)
318 mValue = static_cast<std::uint64_t>(-iou.mantissa());
319 else
320 mValue = static_cast<std::uint64_t>(iou.mantissa());
321 return *this;
322}
323
324//------------------------------------------------------------------------------
325//
326// Operators
327//
328//------------------------------------------------------------------------------
329
332{
333 *this = *this + a;
334 return *this;
335}
336
339{
340 *this = *this - a;
341 return *this;
342}
343
345operator+(STAmount const& v1, STAmount const& v2)
346{
347 if (!areComparable(v1, v2))
348 Throw<std::runtime_error>("Can't add amounts that are't comparable!");
349
350 if (v2 == beast::zero)
351 return v1;
352
353 if (v1 == beast::zero)
354 {
355 // Result must be in terms of v1 currency and issuer.
356 return {
357 v1.getFName(),
358 v1.asset(),
359 v2.mantissa(),
360 v2.exponent(),
361 v2.negative()};
362 }
363
364 if (v1.native())
365 return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
366 if (v1.holds<MPTIssue>())
367 return {v1.mAsset, v1.mpt().value() + v2.mpt().value()};
368
370 {
371 auto x = v1;
372 x = v1.iou() + v2.iou();
373 return x;
374 }
375
376 int ov1 = v1.exponent(), ov2 = v2.exponent();
377 std::int64_t vv1 = static_cast<std::int64_t>(v1.mantissa());
378 std::int64_t vv2 = static_cast<std::int64_t>(v2.mantissa());
379
380 if (v1.negative())
381 vv1 = -vv1;
382
383 if (v2.negative())
384 vv2 = -vv2;
385
386 while (ov1 < ov2)
387 {
388 vv1 /= 10;
389 ++ov1;
390 }
391
392 while (ov2 < ov1)
393 {
394 vv2 /= 10;
395 ++ov2;
396 }
397
398 // This addition cannot overflow an std::int64_t. It can overflow an
399 // STAmount and the constructor will throw.
400
401 std::int64_t fv = vv1 + vv2;
402
403 if ((fv >= -10) && (fv <= 10))
404 return {v1.getFName(), v1.asset()};
405
406 if (fv >= 0)
407 return STAmount{
408 v1.getFName(),
409 v1.asset(),
410 static_cast<std::uint64_t>(fv),
411 ov1,
412 false};
413
414 return STAmount{
415 v1.getFName(), v1.asset(), static_cast<std::uint64_t>(-fv), ov1, true};
416}
417
419operator-(STAmount const& v1, STAmount const& v2)
420{
421 return v1 + (-v2);
422}
423
424//------------------------------------------------------------------------------
425
426std::uint64_t const STAmount::uRateOne = getRate(STAmount(1), STAmount(1));
427
428void
430{
431 mAsset = asset;
432}
433
434// Convert an offer into an index amount so they sort by rate.
435// A taker will take the best, lowest, rate first.
436// (e.g. a taker will prefer pay 1 get 3 over pay 1 get 2.
437// --> offerOut: takerGets: How much the offerer is selling to the taker.
438// --> offerIn: takerPays: How much the offerer is receiving from the taker.
439// <-- uRate: normalize(offerIn/offerOut)
440// A lower rate is better for the person taking the order.
441// The taker gets more for less with a lower rate.
442// Zero is returned if the offer is worthless.
444getRate(STAmount const& offerOut, STAmount const& offerIn)
445{
446 if (offerOut == beast::zero)
447 return 0;
448 try
449 {
450 STAmount r = divide(offerIn, offerOut, noIssue());
451 if (r == beast::zero) // offer is too good
452 return 0;
453 XRPL_ASSERT(
454 (r.exponent() >= -100) && (r.exponent() <= 155),
455 "ripple::getRate : exponent inside range");
456 std::uint64_t ret = r.exponent() + 100;
457 return (ret << (64 - 8)) | r.mantissa();
458 }
459 catch (std::exception const&)
460 {
461 }
462
463 // overflow -- very bad offer
464 return 0;
465}
466
485bool
486canAdd(STAmount const& a, STAmount const& b)
487{
488 // cannot add different currencies
489 if (!areComparable(a, b))
490 return false;
491
492 // special case: adding anything to zero is always fine
493 if (a == beast::zero || b == beast::zero)
494 return true;
495
496 // XRP case (overflow & underflow check)
497 if (isXRP(a) && isXRP(b))
498 {
499 XRPAmount A = a.xrp();
500 XRPAmount B = b.xrp();
501
502 if ((B > XRPAmount{0} &&
504 B) ||
505 (B < XRPAmount{0} &&
507 B))
508 {
509 return false;
510 }
511 return true;
512 }
513
514 // IOU case (precision check)
515 if (a.holds<Issue>() && b.holds<Issue>())
516 {
517 static STAmount const one{IOUAmount{1, 0}, noIssue()};
518 static STAmount const maxLoss{IOUAmount{1, -4}, noIssue()};
519 STAmount lhs = divide((a - b) + b, a, noIssue()) - one;
520 STAmount rhs = divide((b - a) + a, b, noIssue()) - one;
521 return ((rhs.negative() ? -rhs : rhs) +
522 (lhs.negative() ? -lhs : lhs)) <= maxLoss;
523 }
524
525 // MPT (overflow & underflow check)
526 if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
527 {
528 MPTAmount A = a.mpt();
529 MPTAmount B = b.mpt();
530 if ((B > MPTAmount{0} &&
532 B) ||
533 (B < MPTAmount{0} &&
535 B))
536 {
537 return false;
538 }
539
540 return true;
541 }
542 // LCOV_EXCL_START
543 UNREACHABLE("STAmount::canAdd : unexpected STAmount type");
544 return false;
545 // LCOV_EXCL_STOP
546}
547
565bool
566canSubtract(STAmount const& a, STAmount const& b)
567{
568 // Cannot subtract different currencies
569 if (!areComparable(a, b))
570 return false;
571
572 // Special case: subtracting zero is always fine
573 if (b == beast::zero)
574 return true;
575
576 // XRP case (underflow & overflow check)
577 if (isXRP(a) && isXRP(b))
578 {
579 XRPAmount A = a.xrp();
580 XRPAmount B = b.xrp();
581 // Check for underflow
582 if (B > XRPAmount{0} && A < B)
583 return false;
584
585 // Check for overflow
586 if (B < XRPAmount{0} &&
588 B)
589 return false;
590
591 return true;
592 }
593
594 // IOU case (no underflow)
595 if (a.holds<Issue>() && b.holds<Issue>())
596 {
597 return true;
598 }
599
600 // MPT case (underflow & overflow check)
601 if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
602 {
603 MPTAmount A = a.mpt();
604 MPTAmount B = b.mpt();
605
606 // Underflow check
607 if (B > MPTAmount{0} && A < B)
608 return false;
609
610 // Overflow check
611 if (B < MPTAmount{0} &&
613 B)
614 return false;
615 return true;
616 }
617 // LCOV_EXCL_START
618 UNREACHABLE("STAmount::canSubtract : unexpected STAmount type");
619 return false;
620 // LCOV_EXCL_STOP
621}
622
623void
625{
626 elem = Json::objectValue;
627
628 if (!native())
629 {
630 // It is an error for currency or issuer not to be specified for valid
631 // json.
632 elem[jss::value] = getText();
633 mAsset.setJson(elem);
634 }
635 else
636 {
637 elem = getText();
638 }
639}
640
641//------------------------------------------------------------------------------
642//
643// STBase
644//
645//------------------------------------------------------------------------------
646
649{
650 return STI_AMOUNT;
651}
652
655{
656 std::string ret;
657
658 ret.reserve(64);
659 ret = getText() + "/" + mAsset.getText();
660 return ret;
661}
662
665{
666 // keep full internal accuracy, but make more human friendly if posible
667 if (*this == beast::zero)
668 return "0";
669
670 std::string const raw_value(std::to_string(mValue));
671 std::string ret;
672
673 if (mIsNegative)
674 ret.append(1, '-');
675
676 bool const scientific(
677 (mOffset != 0) && ((mOffset < -25) || (mOffset > -5)));
678
679 if (native() || mAsset.holds<MPTIssue>() || scientific)
680 {
681 ret.append(raw_value);
682
683 if (scientific)
684 {
685 ret.append(1, 'e');
687 }
688
689 return ret;
690 }
691
692 XRPL_ASSERT(mOffset + 43 > 0, "ripple::STAmount::getText : minimum offset");
693
694 size_t const pad_prefix = 27;
695 size_t const pad_suffix = 23;
696
697 std::string val;
698 val.reserve(raw_value.length() + pad_prefix + pad_suffix);
699 val.append(pad_prefix, '0');
700 val.append(raw_value);
701 val.append(pad_suffix, '0');
702
703 size_t const offset(mOffset + 43);
704
705 auto pre_from(val.begin());
706 auto const pre_to(val.begin() + offset);
707
708 auto const post_from(val.begin() + offset);
709 auto post_to(val.end());
710
711 // Crop leading zeroes. Take advantage of the fact that there's always a
712 // fixed amount of leading zeroes and skip them.
713 if (std::distance(pre_from, pre_to) > pad_prefix)
714 pre_from += pad_prefix;
715
716 XRPL_ASSERT(
717 post_to >= post_from,
718 "ripple::STAmount::getText : first distance check");
719
720 pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
721
722 // Crop trailing zeroes. Take advantage of the fact that there's always a
723 // fixed amount of trailing zeroes and skip them.
724 if (std::distance(post_from, post_to) > pad_suffix)
725 post_to -= pad_suffix;
726
727 XRPL_ASSERT(
728 post_to >= post_from,
729 "ripple::STAmount::getText : second distance check");
730
731 post_to = std::find_if(
734 [](char c) { return c != '0'; })
735 .base();
736
737 // Assemble the output:
738 if (pre_from == pre_to)
739 ret.append(1, '0');
740 else
741 ret.append(pre_from, pre_to);
742
743 if (post_to != post_from)
744 {
745 ret.append(1, '.');
746 ret.append(post_from, post_to);
747 }
748
749 return ret;
750}
751
754{
755 Json::Value elem;
756 setJson(elem);
757 return elem;
758}
759
760void
762{
763 if (native())
764 {
765 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::add : zero offset");
766
767 if (!mIsNegative)
769 else
770 s.add64(mValue);
771 }
772 else if (mAsset.holds<MPTIssue>())
773 {
774 auto u8 = static_cast<unsigned char>(cMPToken >> 56);
775 if (!mIsNegative)
776 u8 |= static_cast<unsigned char>(cPositive >> 56);
777 s.add8(u8);
778 s.add64(mValue);
780 }
781 else
782 {
783 if (*this == beast::zero)
785 else if (mIsNegative) // 512 = not native
786 s.add64(
787 mValue |
788 (static_cast<std::uint64_t>(mOffset + 512 + 97) << (64 - 10)));
789 else // 256 = positive
790 s.add64(
791 mValue |
792 (static_cast<std::uint64_t>(mOffset + 512 + 256 + 97)
793 << (64 - 10)));
796 }
797}
798
799bool
801{
802 STAmount const* v = dynamic_cast<STAmount const*>(&t);
803 return v && (*v == *this);
804}
805
806bool
808{
809 return (mValue == 0) && native();
810}
811
812//------------------------------------------------------------------------------
813
814// amount = mValue * [10 ^ mOffset]
815// Representation range is 10^80 - 10^(-80).
816//
817// On the wire:
818// - high bit is 0 for XRP, 1 for issued currency
819// - next bit is 1 for positive, 0 for negative (except 0 issued currency, which
820// is a special case of 0x8000000000000000
821// - for issued currencies, the next 8 bits are (mOffset+97).
822// The +97 is so that this value is always positive.
823// - The remaining bits are significant digits (mantissa)
824// That's 54 bits for issued currency and 62 bits for native
825// (but XRP only needs 57 bits for the max value of 10^17 drops)
826//
827// mValue is zero if the amount is zero, otherwise it's within the range
828// 10^15 to (10^16 - 1) inclusive.
829// mOffset is in the range -96 to +80.
830void
832{
833 if (integral())
834 {
835 // native and MPT currency amounts should always have an offset of zero
836 // log(2^64,10) ~ 19.2
837 if (mValue == 0 || mOffset <= -20)
838 {
839 mValue = 0;
840 mOffset = 0;
841 mIsNegative = false;
842 return;
843 }
844
845 // log(cMaxNativeN, 10) == 17
846 if (native() && mOffset > 17)
847 Throw<std::runtime_error>("Native currency amount out of range");
848 // log(maxMPTokenAmount, 10) ~ 18.96
849 if (mAsset.holds<MPTIssue>() && mOffset > 18)
850 Throw<std::runtime_error>("MPT amount out of range");
851
853 {
854 Number num(
856 auto set = [&](auto const& val) {
857 mIsNegative = val.value() < 0;
858 mValue = mIsNegative ? -val.value() : val.value();
859 };
860 if (native())
861 set(XRPAmount{num});
862 else if (mAsset.holds<MPTIssue>())
863 set(MPTAmount{num});
864 else
865 Throw<std::runtime_error>("Unknown integral asset type");
866 mOffset = 0;
867 }
868 else
869 {
870 while (mOffset < 0)
871 {
872 mValue /= 10;
873 ++mOffset;
874 }
875
876 while (mOffset > 0)
877 {
878 // N.B. do not move the overflow check to after the
879 // multiplication
880 if (native() && mValue > cMaxNativeN)
881 Throw<std::runtime_error>(
882 "Native currency amount out of range");
883 else if (!native() && mValue > maxMPTokenAmount)
884 Throw<std::runtime_error>("MPT amount out of range");
885
886 mValue *= 10;
887 --mOffset;
888 }
889 }
890
891 if (native() && mValue > cMaxNativeN)
892 Throw<std::runtime_error>("Native currency amount out of range");
893 else if (!native() && mValue > maxMPTokenAmount)
894 Throw<std::runtime_error>("MPT amount out of range");
895
896 return;
897 }
898
900 {
901 *this = iou();
902 return;
903 }
904
905 if (mValue == 0)
906 {
907 mOffset = -100;
908 mIsNegative = false;
909 return;
910 }
911
912 while ((mValue < cMinValue) && (mOffset > cMinOffset))
913 {
914 mValue *= 10;
915 --mOffset;
916 }
917
918 while (mValue > cMaxValue)
919 {
920 if (mOffset >= cMaxOffset)
921 Throw<std::runtime_error>("value overflow");
922
923 mValue /= 10;
924 ++mOffset;
925 }
926
927 if ((mOffset < cMinOffset) || (mValue < cMinValue))
928 {
929 mValue = 0;
930 mIsNegative = false;
931 mOffset = -100;
932 return;
933 }
934
935 if (mOffset > cMaxOffset)
936 Throw<std::runtime_error>("value overflow");
937
938 XRPL_ASSERT(
939 (mValue == 0) || ((mValue >= cMinValue) && (mValue <= cMaxValue)),
940 "ripple::STAmount::canonicalize : value inside range");
941 XRPL_ASSERT(
942 (mValue == 0) || ((mOffset >= cMinOffset) && (mOffset <= cMaxOffset)),
943 "ripple::STAmount::canonicalize : offset inside range");
944 XRPL_ASSERT(
945 (mValue != 0) || (mOffset != -100),
946 "ripple::STAmount::canonicalize : value or offset set");
947}
948
949void
951{
952 if (v < 0)
953 {
954 mIsNegative = true;
955 mValue = static_cast<std::uint64_t>(-v);
956 }
957 else
958 {
959 mIsNegative = false;
960 mValue = static_cast<std::uint64_t>(v);
961 }
962}
963
964//------------------------------------------------------------------------------
965
968{
969 if (rate == 0)
970 return STAmount(noIssue());
971
972 std::uint64_t mantissa = rate & ~(255ull << (64 - 8));
973 int exponent = static_cast<int>(rate >> (64 - 8)) - 100;
974
975 return STAmount(noIssue(), mantissa, exponent);
976}
977
978STAmount
979amountFromString(Asset const& asset, std::string const& amount)
980{
981 auto const parts = partsFromString(amount);
982 if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
983 Throw<std::runtime_error>(
984 "XRP and MPT must be specified as integral amount.");
985 return {asset, parts.mantissa, parts.exponent, parts.negative};
986}
987
988STAmount
989amountFromJson(SField const& name, Json::Value const& v)
990{
991 Asset asset;
992
993 Json::Value value;
994 Json::Value currencyOrMPTID;
995 Json::Value issuer;
996 bool isMPT = false;
997
998 if (v.isNull())
999 {
1000 Throw<std::runtime_error>(
1001 "XRP may not be specified with a null Json value");
1002 }
1003 else if (v.isObject())
1004 {
1005 if (!validJSONAsset(v))
1006 Throw<std::runtime_error>("Invalid Asset's Json specification");
1007
1008 value = v[jss::value];
1009 if (v.isMember(jss::mpt_issuance_id))
1010 {
1011 isMPT = true;
1012 currencyOrMPTID = v[jss::mpt_issuance_id];
1013 }
1014 else
1015 {
1016 currencyOrMPTID = v[jss::currency];
1017 issuer = v[jss::issuer];
1018 }
1019 }
1020 else if (v.isArray())
1021 {
1022 value = v.get(Json::UInt(0), 0);
1023 currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue);
1024 issuer = v.get(Json::UInt(2), Json::nullValue);
1025 }
1026 else if (v.isString())
1027 {
1028 std::string val = v.asString();
1029 std::vector<std::string> elements;
1030 boost::split(elements, val, boost::is_any_of("\t\n\r ,/"));
1031
1032 if (elements.size() > 3)
1033 Throw<std::runtime_error>("invalid amount string");
1034
1035 value = elements[0];
1036
1037 if (elements.size() > 1)
1038 currencyOrMPTID = elements[1];
1039
1040 if (elements.size() > 2)
1041 issuer = elements[2];
1042 }
1043 else
1044 {
1045 value = v;
1046 }
1047
1048 bool const native = !currencyOrMPTID.isString() ||
1049 currencyOrMPTID.asString().empty() ||
1050 (currencyOrMPTID.asString() == systemCurrencyCode());
1051
1052 if (native)
1053 {
1054 if (v.isObjectOrNull())
1055 Throw<std::runtime_error>("XRP may not be specified as an object");
1056 asset = xrpIssue();
1057 }
1058 else
1059 {
1060 if (isMPT)
1061 {
1062 // sequence (32 bits) + account (160 bits)
1063 uint192 u;
1064 if (!u.parseHex(currencyOrMPTID.asString()))
1065 Throw<std::runtime_error>("invalid MPTokenIssuanceID");
1066 asset = u;
1067 }
1068 else
1069 {
1070 Issue issue;
1071 if (!to_currency(issue.currency, currencyOrMPTID.asString()))
1072 Throw<std::runtime_error>("invalid currency");
1073 if (!issuer.isString() ||
1074 !to_issuer(issue.account, issuer.asString()))
1075 Throw<std::runtime_error>("invalid issuer");
1076 if (issue.native())
1077 Throw<std::runtime_error>("invalid issuer");
1078 asset = issue;
1079 }
1080 }
1081
1082 NumberParts parts;
1083
1084 if (value.isInt())
1085 {
1086 if (value.asInt() >= 0)
1087 {
1088 parts.mantissa = value.asInt();
1089 }
1090 else
1091 {
1092 parts.mantissa = value.asAbsUInt();
1093 parts.negative = true;
1094 }
1095 }
1096 else if (value.isUInt())
1097 {
1098 parts.mantissa = v.asUInt();
1099 }
1100 else if (value.isString())
1101 {
1102 parts = partsFromString(value.asString());
1103 // Can't specify XRP or MPT using fractional representation
1104 if ((asset.native() || asset.holds<MPTIssue>()) && parts.exponent < 0)
1105 Throw<std::runtime_error>(
1106 "XRP and MPT must be specified as integral amount.");
1107 }
1108 else
1109 {
1110 Throw<std::runtime_error>("invalid amount type");
1111 }
1112
1113 return {name, asset, parts.mantissa, parts.exponent, parts.negative};
1114}
1115
1116bool
1118{
1119 try
1120 {
1121 result = amountFromJson(sfGeneric, jvSource);
1122 return true;
1123 }
1124 catch (std::exception const& e)
1125 {
1126 JLOG(debugLog().warn())
1127 << "amountFromJsonNoThrow: caught: " << e.what();
1128 }
1129 return false;
1130}
1131
1132//------------------------------------------------------------------------------
1133//
1134// Operators
1135//
1136//------------------------------------------------------------------------------
1137
1138bool
1139operator==(STAmount const& lhs, STAmount const& rhs)
1140{
1141 return areComparable(lhs, rhs) && lhs.negative() == rhs.negative() &&
1142 lhs.exponent() == rhs.exponent() && lhs.mantissa() == rhs.mantissa();
1143}
1144
1145bool
1146operator<(STAmount const& lhs, STAmount const& rhs)
1147{
1148 if (!areComparable(lhs, rhs))
1149 Throw<std::runtime_error>(
1150 "Can't compare amounts that are't comparable!");
1151
1152 if (lhs.negative() != rhs.negative())
1153 return lhs.negative();
1154
1155 if (lhs.mantissa() == 0)
1156 {
1157 if (rhs.negative())
1158 return false;
1159 return rhs.mantissa() != 0;
1160 }
1161
1162 // We know that lhs is non-zero and both sides have the same sign. Since
1163 // rhs is zero (and thus not negative), lhs must, therefore, be strictly
1164 // greater than zero. So if rhs is zero, the comparison must be false.
1165 if (rhs.mantissa() == 0)
1166 return false;
1167
1168 if (lhs.exponent() > rhs.exponent())
1169 return lhs.negative();
1170 if (lhs.exponent() < rhs.exponent())
1171 return !lhs.negative();
1172 if (lhs.mantissa() > rhs.mantissa())
1173 return lhs.negative();
1174 if (lhs.mantissa() < rhs.mantissa())
1175 return !lhs.negative();
1176
1177 return false;
1178}
1179
1180STAmount
1181operator-(STAmount const& value)
1182{
1183 if (value.mantissa() == 0)
1184 return value;
1185 return STAmount(
1186 value.getFName(),
1187 value.asset(),
1188 value.mantissa(),
1189 value.exponent(),
1190 !value.negative(),
1192}
1193
1194//------------------------------------------------------------------------------
1195//
1196// Arithmetic
1197//
1198//------------------------------------------------------------------------------
1199
1200// Calculate (a * b) / c when all three values are 64-bit
1201// without loss of precision:
1202static std::uint64_t
1204 std::uint64_t multiplier,
1205 std::uint64_t multiplicand,
1206 std::uint64_t divisor)
1207{
1208 boost::multiprecision::uint128_t ret;
1209
1210 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1211 ret /= divisor;
1212
1214 {
1215 Throw<std::overflow_error>(
1216 "overflow: (" + std::to_string(multiplier) + " * " +
1217 std::to_string(multiplicand) + ") / " + std::to_string(divisor));
1218 }
1219
1220 return static_cast<uint64_t>(ret);
1221}
1222
1223static std::uint64_t
1225 std::uint64_t multiplier,
1226 std::uint64_t multiplicand,
1227 std::uint64_t divisor,
1228 std::uint64_t rounding)
1229{
1230 boost::multiprecision::uint128_t ret;
1231
1232 boost::multiprecision::multiply(ret, multiplier, multiplicand);
1233 ret += rounding;
1234 ret /= divisor;
1235
1237 {
1238 Throw<std::overflow_error>(
1239 "overflow: ((" + std::to_string(multiplier) + " * " +
1240 std::to_string(multiplicand) + ") + " + std::to_string(rounding) +
1241 ") / " + std::to_string(divisor));
1242 }
1243
1244 return static_cast<uint64_t>(ret);
1245}
1246
1247STAmount
1248divide(STAmount const& num, STAmount const& den, Asset const& asset)
1249{
1250 if (den == beast::zero)
1251 Throw<std::runtime_error>("division by zero");
1252
1253 if (num == beast::zero)
1254 return {asset};
1255
1256 std::uint64_t numVal = num.mantissa();
1257 std::uint64_t denVal = den.mantissa();
1258 int numOffset = num.exponent();
1259 int denOffset = den.exponent();
1260
1261 if (num.native() || num.holds<MPTIssue>())
1262 {
1263 while (numVal < STAmount::cMinValue)
1264 {
1265 // Need to bring into range
1266 numVal *= 10;
1267 --numOffset;
1268 }
1269 }
1270
1271 if (den.native() || den.holds<MPTIssue>())
1272 {
1273 while (denVal < STAmount::cMinValue)
1274 {
1275 denVal *= 10;
1276 --denOffset;
1277 }
1278 }
1279
1280 // We divide the two mantissas (each is between 10^15
1281 // and 10^16). To maintain precision, we multiply the
1282 // numerator by 10^17 (the product is in the range of
1283 // 10^32 to 10^33) followed by a division, so the result
1284 // is in the range of 10^16 to 10^15.
1285 return STAmount(
1286 asset,
1287 muldiv(numVal, tenTo17, denVal) + 5,
1288 numOffset - denOffset - 17,
1289 num.negative() != den.negative());
1290}
1291
1292STAmount
1293multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
1294{
1295 if (v1 == beast::zero || v2 == beast::zero)
1296 return STAmount(asset);
1297
1298 if (v1.native() && v2.native() && asset.native())
1299 {
1300 std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2));
1301 std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2));
1302
1303 if (minV > 3000000000ull) // sqrt(cMaxNative)
1304 Throw<std::runtime_error>("Native value overflow");
1305
1306 if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
1307 Throw<std::runtime_error>("Native value overflow");
1308
1309 return STAmount(v1.getFName(), minV * maxV);
1310 }
1311 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
1312 {
1313 std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
1314 std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
1315
1316 if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
1317 Throw<std::runtime_error>("MPT value overflow");
1318
1319 if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
1320 Throw<std::runtime_error>("MPT value overflow");
1321
1322 return STAmount(asset, minV * maxV);
1323 }
1324
1326 {
1327 auto const r = Number{v1} * Number{v2};
1328 return STAmount{asset, r.mantissa(), r.exponent()};
1329 }
1330
1331 std::uint64_t value1 = v1.mantissa();
1332 std::uint64_t value2 = v2.mantissa();
1333 int offset1 = v1.exponent();
1334 int offset2 = v2.exponent();
1335
1336 if (v1.native() || v1.holds<MPTIssue>())
1337 {
1338 while (value1 < STAmount::cMinValue)
1339 {
1340 value1 *= 10;
1341 --offset1;
1342 }
1343 }
1344
1345 if (v2.native() || v2.holds<MPTIssue>())
1346 {
1347 while (value2 < STAmount::cMinValue)
1348 {
1349 value2 *= 10;
1350 --offset2;
1351 }
1352 }
1353
1354 // We multiply the two mantissas (each is between 10^15
1355 // and 10^16), so their product is in the 10^30 to 10^32
1356 // range. Dividing their product by 10^14 maintains the
1357 // precision, by scaling the result to 10^16 to 10^18.
1358 return STAmount(
1359 asset,
1360 muldiv(value1, value2, tenTo14) + 7,
1361 offset1 + offset2 + 14,
1362 v1.negative() != v2.negative());
1363}
1364
1365// This is the legacy version of canonicalizeRound. It's been in use
1366// for years, so it is deeply embedded in the behavior of cross-currency
1367// transactions.
1368//
1369// However in 2022 it was noticed that the rounding characteristics were
1370// surprising. When the code converts from IOU-like to XRP-like there may
1371// be a fraction of the IOU-like representation that is too small to be
1372// represented in drops. `canonicalizeRound()` currently does some unusual
1373// rounding.
1374//
1375// 1. If the fractional part is greater than or equal to 0.1, then the
1376// number of drops is rounded up.
1377//
1378// 2. However, if the fractional part is less than 0.1 (for example,
1379// 0.099999), then the number of drops is rounded down.
1380//
1381// The XRP Ledger has this rounding behavior baked in. But there are
1382// situations where this rounding behavior led to undesirable outcomes.
1383// So an alternative rounding approach was introduced. You'll see that
1384// alternative below.
1385static void
1386canonicalizeRound(bool native, std::uint64_t& value, int& offset, bool)
1387{
1388 if (native)
1389 {
1390 if (offset < 0)
1391 {
1392 int loops = 0;
1393
1394 while (offset < -1)
1395 {
1396 value /= 10;
1397 ++offset;
1398 ++loops;
1399 }
1400
1401 value += (loops >= 2) ? 9 : 10; // add before last divide
1402 value /= 10;
1403 ++offset;
1404 }
1405 }
1406 else if (value > STAmount::cMaxValue)
1407 {
1408 while (value > (10 * STAmount::cMaxValue))
1409 {
1410 value /= 10;
1411 ++offset;
1412 }
1413
1414 value += 9; // add before last divide
1415 value /= 10;
1416 ++offset;
1417 }
1418}
1419
1420// The original canonicalizeRound did not allow the rounding direction to
1421// be specified. It also ignored some of the bits that could contribute to
1422// rounding decisions. canonicalizeRoundStrict() tracks all of the bits in
1423// the value being rounded.
1424static void
1426 bool native,
1427 std::uint64_t& value,
1428 int& offset,
1429 bool roundUp)
1430{
1431 if (native)
1432 {
1433 if (offset < 0)
1434 {
1435 bool hadRemainder = false;
1436
1437 while (offset < -1)
1438 {
1439 // It would be better to use std::lldiv than to separately
1440 // compute the remainder. But std::lldiv does not support
1441 // unsigned arguments.
1442 std::uint64_t const newValue = value / 10;
1443 hadRemainder |= (value != (newValue * 10));
1444 value = newValue;
1445 ++offset;
1446 }
1447 value +=
1448 (hadRemainder && roundUp) ? 10 : 9; // Add before last divide
1449 value /= 10;
1450 ++offset;
1451 }
1452 }
1453 else if (value > STAmount::cMaxValue)
1454 {
1455 while (value > (10 * STAmount::cMaxValue))
1456 {
1457 value /= 10;
1458 ++offset;
1459 }
1460 value += 9; // add before last divide
1461 value /= 10;
1462 ++offset;
1463 }
1464}
1465
1466STAmount
1468 STAmount const& value,
1469 std::int32_t scale,
1470 Number::rounding_mode rounding)
1471{
1472 // Nothing to do for integral types.
1473 if (value.integral())
1474 return value;
1475
1476 // If the value's exponent is greater than or equal to the scale, then
1477 // rounding will do nothing, and might even lose precision, so just return
1478 // the value.
1479 if (value.exponent() >= scale)
1480 return value;
1481
1482 STAmount const referenceValue{
1483 value.asset(), STAmount::cMinValue, scale, value.negative()};
1484
1485 NumberRoundModeGuard mg(rounding);
1486 // With an IOU, the the result of addition will be truncated to the
1487 // precision of the larger value, which in this case is referenceValue. Then
1488 // remove the reference value via subtraction, and we're left with the
1489 // rounded value.
1490 return (value + referenceValue) - referenceValue;
1491}
1492
1493namespace {
1494
1495// We need a class that has an interface similar to NumberRoundModeGuard
1496// but does nothing.
1497class DontAffectNumberRoundMode
1498{
1499public:
1500 explicit DontAffectNumberRoundMode(Number::rounding_mode mode) noexcept
1501 {
1502 }
1503
1504 DontAffectNumberRoundMode(DontAffectNumberRoundMode const&) = delete;
1505
1506 DontAffectNumberRoundMode&
1507 operator=(DontAffectNumberRoundMode const&) = delete;
1508};
1509
1510} // anonymous namespace
1511
1512// Pass the canonicalizeRound function pointer as a template parameter.
1513//
1514// We might need to use NumberRoundModeGuard. Allow the caller
1515// to pass either that or a replacement as a template parameter.
1516template <
1517 void (*CanonicalizeFunc)(bool, std::uint64_t&, int&, bool),
1518 typename MightSaveRound>
1519static STAmount
1521 STAmount const& v1,
1522 STAmount const& v2,
1523 Asset const& asset,
1524 bool roundUp)
1525{
1526 if (v1 == beast::zero || v2 == beast::zero)
1527 return {asset};
1528
1529 bool const xrp = asset.native();
1530
1531 if (v1.native() && v2.native() && xrp)
1532 {
1535
1536 if (minV > 3000000000ull) // sqrt(cMaxNative)
1537 Throw<std::runtime_error>("Native value overflow");
1538
1539 if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
1540 Throw<std::runtime_error>("Native value overflow");
1541
1542 return STAmount(v1.getFName(), minV * maxV);
1543 }
1544
1545 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
1546 {
1547 std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
1548 std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
1549
1550 if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
1551 Throw<std::runtime_error>("MPT value overflow");
1552
1553 if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
1554 Throw<std::runtime_error>("MPT value overflow");
1555
1556 return STAmount(asset, minV * maxV);
1557 }
1558
1559 std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa();
1560 int offset1 = v1.exponent(), offset2 = v2.exponent();
1561
1562 if (v1.native() || v1.holds<MPTIssue>())
1563 {
1564 while (value1 < STAmount::cMinValue)
1565 {
1566 value1 *= 10;
1567 --offset1;
1568 }
1569 }
1570
1571 if (v2.native() || v2.holds<MPTIssue>())
1572 {
1573 while (value2 < STAmount::cMinValue)
1574 {
1575 value2 *= 10;
1576 --offset2;
1577 }
1578 }
1579
1580 bool const resultNegative = v1.negative() != v2.negative();
1581
1582 // We multiply the two mantissas (each is between 10^15
1583 // and 10^16), so their product is in the 10^30 to 10^32
1584 // range. Dividing their product by 10^14 maintains the
1585 // precision, by scaling the result to 10^16 to 10^18.
1586 //
1587 // If the we're rounding up, we want to round up away
1588 // from zero, and if we're rounding down, truncation
1589 // is implicit.
1590 std::uint64_t amount = muldiv_round(
1591 value1, value2, tenTo14, (resultNegative != roundUp) ? tenTo14m1 : 0);
1592
1593 int offset = offset1 + offset2 + 14;
1594 if (resultNegative != roundUp)
1595 {
1596 CanonicalizeFunc(xrp, amount, offset, roundUp);
1597 }
1598 STAmount result = [&]() {
1599 // If appropriate, tell Number to round down. This gives the desired
1600 // result from STAmount::canonicalize.
1601 MightSaveRound const savedRound(Number::towards_zero);
1602 return STAmount(asset, amount, offset, resultNegative);
1603 }();
1604
1605 if (roundUp && !resultNegative && !result)
1606 {
1607 if (xrp)
1608 {
1609 // return the smallest value above zero
1610 amount = 1;
1611 offset = 0;
1612 }
1613 else
1614 {
1615 // return the smallest value above zero
1616 amount = STAmount::cMinValue;
1617 offset = STAmount::cMinOffset;
1618 }
1619 return STAmount(asset, amount, offset, resultNegative);
1620 }
1621 return result;
1622}
1623
1624STAmount
1626 STAmount const& v1,
1627 STAmount const& v2,
1628 Asset const& asset,
1629 bool roundUp)
1630{
1631 return mulRoundImpl<canonicalizeRound, DontAffectNumberRoundMode>(
1632 v1, v2, asset, roundUp);
1633}
1634
1635STAmount
1637 STAmount const& v1,
1638 STAmount const& v2,
1639 Asset const& asset,
1640 bool roundUp)
1641{
1642 return mulRoundImpl<canonicalizeRoundStrict, NumberRoundModeGuard>(
1643 v1, v2, asset, roundUp);
1644}
1645
1646// We might need to use NumberRoundModeGuard. Allow the caller
1647// to pass either that or a replacement as a template parameter.
1648template <typename MightSaveRound>
1649static STAmount
1651 STAmount const& num,
1652 STAmount const& den,
1653 Asset const& asset,
1654 bool roundUp)
1655{
1656 if (den == beast::zero)
1657 Throw<std::runtime_error>("division by zero");
1658
1659 if (num == beast::zero)
1660 return {asset};
1661
1662 std::uint64_t numVal = num.mantissa(), denVal = den.mantissa();
1663 int numOffset = num.exponent(), denOffset = den.exponent();
1664
1665 if (num.native() || num.holds<MPTIssue>())
1666 {
1667 while (numVal < STAmount::cMinValue)
1668 {
1669 numVal *= 10;
1670 --numOffset;
1671 }
1672 }
1673
1674 if (den.native() || den.holds<MPTIssue>())
1675 {
1676 while (denVal < STAmount::cMinValue)
1677 {
1678 denVal *= 10;
1679 --denOffset;
1680 }
1681 }
1682
1683 bool const resultNegative = (num.negative() != den.negative());
1684
1685 // We divide the two mantissas (each is between 10^15
1686 // and 10^16). To maintain precision, we multiply the
1687 // numerator by 10^17 (the product is in the range of
1688 // 10^32 to 10^33) followed by a division, so the result
1689 // is in the range of 10^16 to 10^15.
1690 //
1691 // We round away from zero if we're rounding up or
1692 // truncate if we're rounding down.
1693 std::uint64_t amount = muldiv_round(
1694 numVal, tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0);
1695
1696 int offset = numOffset - denOffset - 17;
1697
1698 if (resultNegative != roundUp)
1700 asset.native() || asset.holds<MPTIssue>(), amount, offset, roundUp);
1701
1702 STAmount result = [&]() {
1703 // If appropriate, tell Number the rounding mode we are using.
1704 // Note that "roundUp == true" actually means "round away from zero".
1705 // Otherwise round toward zero.
1706 using enum Number::rounding_mode;
1707 MightSaveRound const savedRound(
1708 roundUp ^ resultNegative ? upward : downward);
1709 return STAmount(asset, amount, offset, resultNegative);
1710 }();
1711
1712 if (roundUp && !resultNegative && !result)
1713 {
1714 if (asset.native() || asset.holds<MPTIssue>())
1715 {
1716 // return the smallest value above zero
1717 amount = 1;
1718 offset = 0;
1719 }
1720 else
1721 {
1722 // return the smallest value above zero
1723 amount = STAmount::cMinValue;
1724 offset = STAmount::cMinOffset;
1725 }
1726 return STAmount(asset, amount, offset, resultNegative);
1727 }
1728 return result;
1729}
1730
1731STAmount
1733 STAmount const& num,
1734 STAmount const& den,
1735 Asset const& asset,
1736 bool roundUp)
1737{
1738 return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
1739}
1740
1741STAmount
1743 STAmount const& num,
1744 STAmount const& den,
1745 Asset const& asset,
1746 bool roundUp)
1747{
1748 return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
1749}
1750
1751} // namespace ripple
T append(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:131
bool isArray() const
bool isObjectOrNull() const
Int asInt() const
UInt asAbsUInt() const
Correct absolute value from int or unsigned int.
bool isString() const
UInt asUInt() const
bool isObject() const
std::string asString() const
Returns the unquoted string value.
bool isUInt() const
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(char const *key) const
Return true if the object has a member named key.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isInt() const
bool native() const
Definition Asset.h:82
void setJson(Json::Value &jv) const
Definition Asset.cpp:31
constexpr TIss const & get() const
constexpr bool holds() const
Definition Asset.h:133
std::string getText() const
Definition Asset.cpp:25
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:27
int exponent() const noexcept
Definition IOUAmount.h:153
std::int64_t mantissa() const noexcept
Definition IOUAmount.h:159
A currency issued by an account.
Definition Issue.h:14
AccountID account
Definition Issue.h:17
Currency currency
Definition Issue.h:16
bool native() const
Definition Issue.cpp:47
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:27
Identifies fields.
Definition SField.h:127
constexpr bool holds() const noexcept
Definition STAmount.h:457
IOUAmount iou() const
Definition STAmount.cpp:280
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:753
STAmount & operator=(beast::Zero)
Definition STAmount.h:532
int exponent() const noexcept
Definition STAmount.h:438
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:253
static constexpr std::uint64_t cValueMask
Definition STAmount.h:61
static constexpr std::uint64_t cPositive
Definition STAmount.h:59
Asset const & asset() const
Definition STAmount.h:475
constexpr TIss const & get() const
static int const cMaxOffset
Definition STAmount.h:47
static int const cMinOffset
Definition STAmount.h:46
static constexpr std::uint64_t cMPToken
Definition STAmount.h:60
STAmount & operator+=(STAmount const &)
Definition STAmount.cpp:331
static constexpr std::uint64_t cMinValue
Definition STAmount.h:50
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:241
void setIssue(Asset const &asset)
Set the Issue for this amount.
Definition STAmount.cpp:429
XRPAmount xrp() const
Definition STAmount.cpp:264
void setJson(Json::Value &) const
Definition STAmount.cpp:624
bool isDefault() const override
Definition STAmount.cpp:807
STAmount & operator-=(STAmount const &)
Definition STAmount.cpp:338
void add(Serializer &s) const override
Definition STAmount.cpp:761
SerializedTypeID getSType() const override
Definition STAmount.cpp:648
mantissa_type mValue
Definition STAmount.h:39
STAmount const & value() const noexcept
Definition STAmount.h:586
std::string getText() const override
Definition STAmount.cpp:664
bool negative() const noexcept
Definition STAmount.h:463
bool isEquivalent(STBase const &t) const override
Definition STAmount.cpp:800
static constexpr std::uint64_t cIssuedCurrency
Definition STAmount.h:58
static constexpr std::uint64_t cMaxValue
Definition STAmount.h:52
STBase * copy(std::size_t n, void *buf) const override
Definition STAmount.cpp:247
MPTAmount mpt() const
Definition STAmount.cpp:295
exponent_type mOffset
Definition STAmount.h:40
Issue const & issue() const
Definition STAmount.h:488
static constexpr std::uint64_t cMaxNativeN
Definition STAmount.h:57
void set(std::int64_t v)
Definition STAmount.cpp:950
std::uint64_t mantissa() const noexcept
Definition STAmount.h:469
std::string getFullText() const override
Definition STAmount.cpp:654
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:101
bool native() const noexcept
Definition STAmount.h:450
bool integral() const noexcept
Definition STAmount.h:444
static std::uint64_t const uRateOne
Definition STAmount.h:63
A type which can be exported to a well known binary format.
Definition STBase.h:116
SField const & getFName() const
Definition STBase.cpp:124
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:214
unsigned char get8()
std::uint64_t get64()
int addBitString(base_uint< Bits, Tag > const &v)
Definition Serializer.h:112
int add8(unsigned char i)
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:484
T distance(T... args)
T empty(T... args)
T end(T... args)
T find_if(T... args)
T is_same_v
T make_reverse_iterator(T... args)
T max(T... args)
T min(T... args)
@ nullValue
'null' value
Definition json_value.h:20
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
unsigned int UInt
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:96
static void canonicalizeRoundStrict(bool native, std::uint64_t &value, int &offset, bool roundUp)
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:74
NumberParts partsFromString(std::string const &number)
Definition STNumber.cpp:104
constexpr Number one
Definition Number.cpp:223
bool isXRP(AccountID const &c)
Definition AccountID.h:71
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:566
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
static bool areComparable(STAmount const &v1, STAmount const &v2)
Definition STAmount.cpp:91
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition STAmount.cpp:989
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
SerializedTypeID
Definition SField.h:91
static std::uint64_t const tenTo17
Definition STAmount.cpp:54
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:235
STAmount amountFromQuality(std::uint64_t rate)
Definition STAmount.cpp:967
static std::string const & systemCurrencyCode()
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
static std::uint64_t const tenTo14m1
Definition STAmount.cpp:53
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
static std::int64_t getSNValue(STAmount const &amount)
Definition STAmount.cpp:78
bool validJSONAsset(Json::Value const &jv)
Definition Asset.cpp:50
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:457
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:104
static std::int64_t getInt64Value(STAmount const &amount, bool valid, char const *error)
Definition STAmount.cpp:58
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
static std::int64_t getMPTValue(STAmount const &amount)
Definition STAmount.cpp:84
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:204
STAmount roundToScale(STAmount const &value, std::int32_t scale, Number::rounding_mode rounding=Number::getround())
Round an arbitrary precision Amount to the precision of an STAmount that has a given exponent.
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
Number operator-(Number const &x, Number const &y)
Definition Number.h:279
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:979
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
SField const sfGeneric
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:603
static STAmount mulRoundImpl(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:566
static STAmount divRoundImpl(STAmount const &num, STAmount const &den, Asset const &asset, bool roundUp)
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv_round(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding)
static std::uint64_t const tenTo14
Definition STAmount.cpp:52
bool getSTNumberSwitchover()
Definition IOUAmount.cpp:31
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 reserve(T... args)
T length(T... args)
Note, should be treated as flags that can be | and &.
Definition STBase.h:18
std::uint64_t mantissa
Definition STNumber.h:76
T to_string(T... args)
T what(T... args)