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