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