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