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