rippled
Loading...
Searching...
No Matches
STAmount.cpp
1#include <xrpl/basics/LocalValue.h>
2#include <xrpl/basics/Log.h>
3#include <xrpl/basics/Number.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/safe_cast.h>
7#include <xrpl/beast/core/LexicalCast.h>
8#include <xrpl/beast/utility/Zero.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/json/json_forwards.h>
11#include <xrpl/json/json_value.h>
12#include <xrpl/protocol/AccountID.h>
13#include <xrpl/protocol/Asset.h>
14#include <xrpl/protocol/IOUAmount.h>
15#include <xrpl/protocol/Issue.h>
16#include <xrpl/protocol/MPTAmount.h>
17#include <xrpl/protocol/MPTIssue.h>
18#include <xrpl/protocol/Protocol.h>
19#include <xrpl/protocol/SField.h>
20#include <xrpl/protocol/STAmount.h>
21#include <xrpl/protocol/STBase.h>
22#include <xrpl/protocol/STNumber.h>
23#include <xrpl/protocol/Serializer.h>
24#include <xrpl/protocol/SystemParameters.h>
25#include <xrpl/protocol/UintTypes.h>
26#include <xrpl/protocol/XRPAmount.h>
27#include <xrpl/protocol/jss.h>
28
29#include <boost/algorithm/string/classification.hpp>
30#include <boost/algorithm/string/split.hpp>
31#include <boost/multiprecision/detail/default_ops.hpp>
32#include <boost/multiprecision/fwd.hpp>
33#include <boost/regex/v5/regbase.hpp>
34#include <boost/regex/v5/regex.hpp>
35#include <boost/regex/v5/regex_fwd.hpp>
36#include <boost/regex/v5/regex_match.hpp>
37
38#include <algorithm>
39#include <cstddef>
40#include <cstdint>
41#include <exception>
42#include <iterator>
43#include <limits>
44#include <memory>
45#include <stdexcept>
46#include <string>
47#include <utility>
48#include <vector>
49
50namespace ripple {
51
52static std::uint64_t const tenTo14 = 100000000000000ull;
53static std::uint64_t const tenTo14m1 = tenTo14 - 1;
54static std::uint64_t const tenTo17 = tenTo14 * 1000;
55
56//------------------------------------------------------------------------------
57static std::int64_t
58getInt64Value(STAmount const& amount, bool valid, char const* error)
59{
60 if (!valid)
61 Throw<std::runtime_error>(error);
62 XRPL_ASSERT(
63 amount.exponent() == 0, "ripple::getInt64Value : exponent is zero");
64
65 auto ret = static_cast<std::int64_t>(amount.mantissa());
66
67 XRPL_ASSERT(
68 static_cast<std::uint64_t>(ret) == amount.mantissa(),
69 "ripple::getInt64Value : mantissa must roundtrip");
70
71 if (amount.negative())
72 ret = -ret;
73
74 return ret;
75}
76
77static std::int64_t
78getSNValue(STAmount const& amount)
79{
80 return getInt64Value(amount, amount.native(), "amount is not native!");
81}
82
83static std::int64_t
84getMPTValue(STAmount const& amount)
85{
86 return getInt64Value(
87 amount, amount.holds<MPTIssue>(), "amount is not MPT!");
88}
89
90static bool
91areComparable(STAmount const& v1, STAmount const& v2)
92{
93 if (v1.holds<Issue>() && v2.holds<Issue>())
94 return v1.native() == v2.native() &&
95 v1.get<Issue>().currency == v2.get<Issue>().currency;
96 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>())
97 return v1.get<MPTIssue>() == v2.get<MPTIssue>();
98 return false;
99}
100
101STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
102{
103 std::uint64_t value = sit.get64();
104
105 // native or MPT
106 if ((value & cIssuedCurrency) == 0)
107 {
108 if ((value & cMPToken) != 0)
109 {
110 // is MPT
111 mOffset = 0;
112 mIsNegative = (value & cPositive) == 0;
113 mValue = (value << 8) | sit.get8();
114 mAsset = sit.get192();
115 return;
116 }
117 // else is XRP
118 mAsset = xrpIssue();
119 // positive
120 if ((value & cPositive) != 0)
121 {
123 mOffset = 0;
124 mIsNegative = false;
125 return;
126 }
127
128 // negative
129 if (value == 0)
130 Throw<std::runtime_error>("negative zero is not canonical");
131
133 mOffset = 0;
134 mIsNegative = true;
135 return;
136 }
137
138 Issue issue;
139 issue.currency = sit.get160();
140
141 if (isXRP(issue.currency))
142 Throw<std::runtime_error>("invalid native currency");
143
144 issue.account = sit.get160();
145
146 if (isXRP(issue.account))
147 Throw<std::runtime_error>("invalid native account");
148
149 // 10 bits for the offset, sign and "not native" flag
150 int offset = static_cast<int>(value >> (64 - 10));
151
152 value &= ~(1023ull << (64 - 10));
153
154 if (value)
155 {
156 bool isNegative = (offset & 256) == 0;
157 offset = (offset & 255) - 97; // center the range
158
159 if (value < cMinValue || value > cMaxValue || offset < cMinOffset ||
160 offset > cMaxOffset)
161 {
162 Throw<std::runtime_error>("invalid currency value");
163 }
164
165 mAsset = issue;
166 mValue = value;
167 mOffset = offset;
168 mIsNegative = isNegative;
169 canonicalize();
170 return;
171 }
172
173 if (offset != 512)
174 Throw<std::runtime_error>("invalid currency value");
175
176 mAsset = issue;
177 mValue = 0;
178 mOffset = 0;
179 mIsNegative = false;
180 canonicalize();
181}
182
184 : STBase(name), mAsset(xrpIssue()), mOffset(0)
185{
186 set(mantissa);
187}
188
189STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative)
190 : STBase(name)
191 , mAsset(xrpIssue())
192 , mValue(mantissa)
193 , mOffset(0)
194 , mIsNegative(negative)
195{
196 XRPL_ASSERT(
198 "ripple::STAmount::STAmount(SField, std::uint64_t, bool) : maximum "
199 "mantissa input");
200}
201
202STAmount::STAmount(SField const& name, STAmount const& from)
203 : STBase(name)
204 , mAsset(from.mAsset)
205 , mValue(from.mValue)
206 , mOffset(from.mOffset)
207 , mIsNegative(from.mIsNegative)
208{
209 XRPL_ASSERT(
211 "ripple::STAmount::STAmount(SField, STAmount) : maximum input");
212 canonicalize();
213}
214
215//------------------------------------------------------------------------------
216
217STAmount::STAmount(std::uint64_t mantissa, bool negative)
218 : mAsset(xrpIssue())
219 , mValue(mantissa)
220 , mOffset(0)
221 , mIsNegative(mantissa != 0 && negative)
222{
223 XRPL_ASSERT(
225 "ripple::STAmount::STAmount(std::uint64_t, bool) : maximum mantissa "
226 "input");
227}
228
230 : mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero)
231{
232 if (mIsNegative)
233 mValue = unsafe_cast<std::uint64_t>(-amount.drops());
234 else
235 mValue = unsafe_cast<std::uint64_t>(amount.drops());
236
237 canonicalize();
238}
239
242{
243 return std::make_unique<STAmount>(sit, name);
244}
245
246STBase*
247STAmount::copy(std::size_t n, void* buf) const
248{
249 return emplace(n, buf, *this);
250}
251
252STBase*
254{
255 return emplace(n, buf, std::move(*this));
256}
257
258//------------------------------------------------------------------------------
259//
260// Conversion
261//
262//------------------------------------------------------------------------------
265{
266 if (!native())
267 Throw<std::logic_error>(
268 "Cannot return non-native STAmount as XRPAmount");
269
270 auto drops = static_cast<XRPAmount::value_type>(mValue);
271 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::xrp : amount is canonical");
272
273 if (mIsNegative)
274 drops = -drops;
275
276 return XRPAmount{drops};
277}
278
281{
282 if (native() || !holds<Issue>())
283 Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
284
285 auto mantissa = static_cast<std::int64_t>(mValue);
286 auto exponent = mOffset;
287
288 if (mIsNegative)
290
291 return {mantissa, exponent};
292}
293
296{
297 if (!holds<MPTIssue>())
298 Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
299
300 auto value = static_cast<MPTAmount::value_type>(mValue);
301 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::mpt : amount is canonical");
302
303 if (mIsNegative)
304 value = -value;
305
306 return MPTAmount{value};
307}
308
311{
312 XRPL_ASSERT(
313 native() == false,
314 "ripple::STAmount::operator=(IOUAmount) : is not XRP");
315 mOffset = iou.exponent();
316 mIsNegative = iou < beast::zero;
317 if (mIsNegative)
318 mValue = static_cast<std::uint64_t>(-iou.mantissa());
319 else
320 mValue = static_cast<std::uint64_t>(iou.mantissa());
321 return *this;
322}
323
324//------------------------------------------------------------------------------
325//
326// Operators
327//
328//------------------------------------------------------------------------------
329
332{
333 *this = *this + a;
334 return *this;
335}
336
339{
340 *this = *this - a;
341 return *this;
342}
343
345operator+(STAmount const& v1, STAmount const& v2)
346{
347 if (!areComparable(v1, v2))
348 Throw<std::runtime_error>("Can't add amounts that are't comparable!");
349
350 if (v2 == beast::zero)
351 return v1;
352
353 if (v1 == beast::zero)
354 {
355 // Result must be in terms of v1 currency and issuer.
356 return {
357 v1.getFName(),
358 v1.asset(),
359 v2.mantissa(),
360 v2.exponent(),
361 v2.negative()};
362 }
363
364 if (v1.native())
365 return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
366 if (v1.holds<MPTIssue>())
367 return {v1.mAsset, v1.mpt().value() + v2.mpt().value()};
368
370 {
371 auto x = v1;
372 x = v1.iou() + v2.iou();
373 return x;
374 }
375
376 int ov1 = v1.exponent(), ov2 = v2.exponent();
377 std::int64_t vv1 = static_cast<std::int64_t>(v1.mantissa());
378 std::int64_t vv2 = static_cast<std::int64_t>(v2.mantissa());
379
380 if (v1.negative())
381 vv1 = -vv1;
382
383 if (v2.negative())
384 vv2 = -vv2;
385
386 while (ov1 < ov2)
387 {
388 vv1 /= 10;
389 ++ov1;
390 }
391
392 while (ov2 < ov1)
393 {
394 vv2 /= 10;
395 ++ov2;
396 }
397
398 // This addition cannot overflow an std::int64_t. It can overflow an
399 // STAmount and the constructor will throw.
400
401 std::int64_t fv = vv1 + vv2;
402
403 if ((fv >= -10) && (fv <= 10))
404 return {v1.getFName(), v1.asset()};
405
406 if (fv >= 0)
407 return STAmount{
408 v1.getFName(),
409 v1.asset(),
410 static_cast<std::uint64_t>(fv),
411 ov1,
412 false};
413
414 return STAmount{
415 v1.getFName(), v1.asset(), static_cast<std::uint64_t>(-fv), ov1, true};
416}
417
419operator-(STAmount const& v1, STAmount const& v2)
420{
421 return v1 + (-v2);
422}
423
424//------------------------------------------------------------------------------
425
426std::uint64_t const STAmount::uRateOne = getRate(STAmount(1), STAmount(1));
427
428void
430{
431 mAsset = asset;
432}
433
434// Convert an offer into an index amount so they sort by rate.
435// A taker will take the best, lowest, rate first.
436// (e.g. a taker will prefer pay 1 get 3 over pay 1 get 2.
437// --> offerOut: takerGets: How much the offerer is selling to the taker.
438// --> offerIn: takerPays: How much the offerer is receiving from the taker.
439// <-- uRate: normalize(offerIn/offerOut)
440// A lower rate is better for the person taking the order.
441// The taker gets more for less with a lower rate.
442// Zero is returned if the offer is worthless.
444getRate(STAmount const& offerOut, STAmount const& offerIn)
445{
446 if (offerOut == beast::zero)
447 return 0;
448 try
449 {
450 STAmount r = divide(offerIn, offerOut, noIssue());
451 if (r == beast::zero) // offer is too good
452 return 0;
453 XRPL_ASSERT(
454 (r.exponent() >= -100) && (r.exponent() <= 155),
455 "ripple::getRate : exponent inside range");
456 std::uint64_t ret = r.exponent() + 100;
457 return (ret << (64 - 8)) | r.mantissa();
458 }
459 catch (std::exception const&)
460 {
461 }
462
463 // overflow -- very bad offer
464 return 0;
465}
466
485bool
486canAdd(STAmount const& a, STAmount const& b)
487{
488 // cannot add different currencies
489 if (!areComparable(a, b))
490 return false;
491
492 // special case: adding anything to zero is always fine
493 if (a == beast::zero || b == beast::zero)
494 return true;
495
496 // XRP case (overflow & underflow check)
497 if (isXRP(a) && isXRP(b))
498 {
499 XRPAmount A = a.xrp();
500 XRPAmount B = b.xrp();
501
502 if ((B > XRPAmount{0} &&
504 B) ||
505 (B < XRPAmount{0} &&
507 B))
508 {
509 return false;
510 }
511 return true;
512 }
513
514 // IOU case (precision check)
515 if (a.holds<Issue>() && b.holds<Issue>())
516 {
517 static STAmount const one{IOUAmount{1, 0}, noIssue()};
518 static STAmount const maxLoss{IOUAmount{1, -4}, noIssue()};
519 STAmount lhs = divide((a - b) + b, a, noIssue()) - one;
520 STAmount rhs = divide((b - a) + a, b, noIssue()) - one;
521 return ((rhs.negative() ? -rhs : rhs) +
522 (lhs.negative() ? -lhs : lhs)) <= maxLoss;
523 }
524
525 // MPT (overflow & underflow check)
526 if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
527 {
528 MPTAmount A = a.mpt();
529 MPTAmount B = b.mpt();
530 if ((B > MPTAmount{0} &&
532 B) ||
533 (B < MPTAmount{0} &&
535 B))
536 {
537 return false;
538 }
539
540 return true;
541 }
542 // LCOV_EXCL_START
543 UNREACHABLE("STAmount::canAdd : unexpected STAmount type");
544 return false;
545 // LCOV_EXCL_STOP
546}
547
565bool
566canSubtract(STAmount const& a, STAmount const& b)
567{
568 // Cannot subtract different currencies
569 if (!areComparable(a, b))
570 return false;
571
572 // Special case: subtracting zero is always fine
573 if (b == beast::zero)
574 return true;
575
576 // XRP case (underflow & overflow check)
577 if (isXRP(a) && isXRP(b))
578 {
579 XRPAmount A = a.xrp();
580 XRPAmount B = b.xrp();
581 // Check for underflow
582 if (B > XRPAmount{0} && A < B)
583 return false;
584
585 // Check for overflow
586 if (B < XRPAmount{0} &&
588 B)
589 return false;
590
591 return true;
592 }
593
594 // IOU case (no underflow)
595 if (a.holds<Issue>() && b.holds<Issue>())
596 {
597 return true;
598 }
599
600 // MPT case (underflow & overflow check)
601 if (a.holds<MPTIssue>() && b.holds<MPTIssue>())
602 {
603 MPTAmount A = a.mpt();
604 MPTAmount B = b.mpt();
605
606 // Underflow check
607 if (B > MPTAmount{0} && A < B)
608 return false;
609
610 // Overflow check
611 if (B < MPTAmount{0} &&
613 B)
614 return false;
615 return true;
616 }
617 // LCOV_EXCL_START
618 UNREACHABLE("STAmount::canSubtract : unexpected STAmount type");
619 return false;
620 // LCOV_EXCL_STOP
621}
622
623void
625{
626 elem = Json::objectValue;
627
628 if (!native())
629 {
630 // It is an error for currency or issuer not to be specified for valid
631 // json.
632 elem[jss::value] = getText();
633 mAsset.setJson(elem);
634 }
635 else
636 {
637 elem = getText();
638 }
639}
640
641//------------------------------------------------------------------------------
642//
643// STBase
644//
645//------------------------------------------------------------------------------
646
649{
650 return STI_AMOUNT;
651}
652
655{
656 std::string ret;
657
658 ret.reserve(64);
659 ret = getText() + "/" + mAsset.getText();
660 return ret;
661}
662
665{
666 // keep full internal accuracy, but make more human friendly if posible
667 if (*this == beast::zero)
668 return "0";
669
670 std::string const raw_value(std::to_string(mValue));
671 std::string ret;
672
673 if (mIsNegative)
674 ret.append(1, '-');
675
676 bool const scientific(
677 (mOffset != 0) && ((mOffset < -25) || (mOffset > -5)));
678
679 if (native() || mAsset.holds<MPTIssue>() || scientific)
680 {
681 ret.append(raw_value);
682
683 if (scientific)
684 {
685 ret.append(1, 'e');
687 }
688
689 return ret;
690 }
691
692 XRPL_ASSERT(mOffset + 43 > 0, "ripple::STAmount::getText : minimum offset");
693
694 size_t const pad_prefix = 27;
695 size_t const pad_suffix = 23;
696
697 std::string val;
698 val.reserve(raw_value.length() + pad_prefix + pad_suffix);
699 val.append(pad_prefix, '0');
700 val.append(raw_value);
701 val.append(pad_suffix, '0');
702
703 size_t const offset(mOffset + 43);
704
705 auto pre_from(val.begin());
706 auto const pre_to(val.begin() + offset);
707
708 auto const post_from(val.begin() + offset);
709 auto post_to(val.end());
710
711 // Crop leading zeroes. Take advantage of the fact that there's always a
712 // fixed amount of leading zeroes and skip them.
713 if (std::distance(pre_from, pre_to) > pad_prefix)
714 pre_from += pad_prefix;
715
716 XRPL_ASSERT(
717 post_to >= post_from,
718 "ripple::STAmount::getText : first distance check");
719
720 pre_from = std::find_if(pre_from, pre_to, [](char c) { return c != '0'; });
721
722 // Crop trailing zeroes. Take advantage of the fact that there's always a
723 // fixed amount of trailing zeroes and skip them.
724 if (std::distance(post_from, post_to) > pad_suffix)
725 post_to -= pad_suffix;
726
727 XRPL_ASSERT(
728 post_to >= post_from,
729 "ripple::STAmount::getText : second distance check");
730
731 post_to = std::find_if(
734 [](char c) { return c != '0'; })
735 .base();
736
737 // Assemble the output:
738 if (pre_from == pre_to)
739 ret.append(1, '0');
740 else
741 ret.append(pre_from, pre_to);
742
743 if (post_to != post_from)
744 {
745 ret.append(1, '.');
746 ret.append(post_from, post_to);
747 }
748
749 return ret;
750}
751
754{
755 Json::Value elem;
756 setJson(elem);
757 return elem;
758}
759
760void
762{
763 if (native())
764 {
765 XRPL_ASSERT(mOffset == 0, "ripple::STAmount::add : zero offset");
766
767 if (!mIsNegative)
769 else
770 s.add64(mValue);
771 }
772 else if (mAsset.holds<MPTIssue>())
773 {
774 auto u8 = static_cast<unsigned char>(cMPToken >> 56);
775 if (!mIsNegative)
776 u8 |= static_cast<unsigned char>(cPositive >> 56);
777 s.add8(u8);
778 s.add64(mValue);
780 }
781 else
782 {
783 if (*this == beast::zero)
785 else if (mIsNegative) // 512 = not native
786 s.add64(
787 mValue |
788 (static_cast<std::uint64_t>(mOffset + 512 + 97) << (64 - 10)));
789 else // 256 = positive
790 s.add64(
791 mValue |
792 (static_cast<std::uint64_t>(mOffset + 512 + 256 + 97)
793 << (64 - 10)));
796 }
797}
798
799bool
801{
802 STAmount const* v = dynamic_cast<STAmount const*>(&t);
803 return v && (*v == *this);
804}
805
806bool
808{
809 return (mValue == 0) && native();
810}
811
812//------------------------------------------------------------------------------
813
814// amount = mValue * [10 ^ mOffset]
815// Representation range is 10^80 - 10^(-80).
816//
817// On the wire:
818// - high bit is 0 for XRP, 1 for issued currency
819// - next bit is 1 for positive, 0 for negative (except 0 issued currency, which
820// is a special case of 0x8000000000000000
821// - for issued currencies, the next 8 bits are (mOffset+97).
822// The +97 is so that this value is always positive.
823// - The remaining bits are significant digits (mantissa)
824// That's 54 bits for issued currency and 62 bits for native
825// (but XRP only needs 57 bits for the max value of 10^17 drops)
826//
827// mValue is zero if the amount is zero, otherwise it's within the range
828// 10^15 to (10^16 - 1) inclusive.
829// mOffset is in the range -96 to +80.
830void
832{
833 if (native() || mAsset.holds<MPTIssue>())
834 {
835 // native and MPT currency amounts should always have an offset of zero
836 // log(2^64,10) ~ 19.2
837 if (mValue == 0 || mOffset <= -20)
838 {
839 mValue = 0;
840 mOffset = 0;
841 mIsNegative = false;
842 return;
843 }
844
845 // log(cMaxNativeN, 10) == 17
846 if (native() && mOffset > 17)
847 Throw<std::runtime_error>("Native currency amount out of range");
848 // log(maxMPTokenAmount, 10) ~ 18.96
849 if (mAsset.holds<MPTIssue>() && mOffset > 18)
850 Throw<std::runtime_error>("MPT amount out of range");
851
853 {
854 Number num(
856 auto set = [&](auto const& val) {
857 mIsNegative = val.value() < 0;
858 mValue = mIsNegative ? -val.value() : val.value();
859 };
860 if (native())
861 set(XRPAmount{num});
862 else
863 set(MPTAmount{num});
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 "ripple::STAmount::canonicalize : value inside range");
939 XRPL_ASSERT(
940 (mValue == 0) || ((mOffset >= cMinOffset) && (mOffset <= cMaxOffset)),
941 "ripple::STAmount::canonicalize : offset inside range");
942 XRPL_ASSERT(
943 (mValue != 0) || (mOffset != -100),
944 "ripple::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.asInt();
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
1464namespace {
1465
1466// We need a class that has an interface similar to NumberRoundModeGuard
1467// but does nothing.
1468class DontAffectNumberRoundMode
1469{
1470public:
1471 explicit DontAffectNumberRoundMode(Number::rounding_mode mode) noexcept
1472 {
1473 }
1474
1475 DontAffectNumberRoundMode(DontAffectNumberRoundMode const&) = delete;
1476
1477 DontAffectNumberRoundMode&
1478 operator=(DontAffectNumberRoundMode const&) = delete;
1479};
1480
1481} // anonymous namespace
1482
1483// Pass the canonicalizeRound function pointer as a template parameter.
1484//
1485// We might need to use NumberRoundModeGuard. Allow the caller
1486// to pass either that or a replacement as a template parameter.
1487template <
1488 void (*CanonicalizeFunc)(bool, std::uint64_t&, int&, bool),
1489 typename MightSaveRound>
1490static STAmount
1492 STAmount const& v1,
1493 STAmount const& v2,
1494 Asset const& asset,
1495 bool roundUp)
1496{
1497 if (v1 == beast::zero || v2 == beast::zero)
1498 return {asset};
1499
1500 bool const xrp = asset.native();
1501
1502 if (v1.native() && v2.native() && xrp)
1503 {
1506
1507 if (minV > 3000000000ull) // sqrt(cMaxNative)
1508 Throw<std::runtime_error>("Native value overflow");
1509
1510 if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32
1511 Throw<std::runtime_error>("Native value overflow");
1512
1513 return STAmount(v1.getFName(), minV * maxV);
1514 }
1515
1516 if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
1517 {
1518 std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
1519 std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
1520
1521 if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
1522 Throw<std::runtime_error>("MPT value overflow");
1523
1524 if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
1525 Throw<std::runtime_error>("MPT value overflow");
1526
1527 return STAmount(asset, minV * maxV);
1528 }
1529
1530 std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa();
1531 int offset1 = v1.exponent(), offset2 = v2.exponent();
1532
1533 if (v1.native() || v1.holds<MPTIssue>())
1534 {
1535 while (value1 < STAmount::cMinValue)
1536 {
1537 value1 *= 10;
1538 --offset1;
1539 }
1540 }
1541
1542 if (v2.native() || v2.holds<MPTIssue>())
1543 {
1544 while (value2 < STAmount::cMinValue)
1545 {
1546 value2 *= 10;
1547 --offset2;
1548 }
1549 }
1550
1551 bool const resultNegative = v1.negative() != v2.negative();
1552
1553 // We multiply the two mantissas (each is between 10^15
1554 // and 10^16), so their product is in the 10^30 to 10^32
1555 // range. Dividing their product by 10^14 maintains the
1556 // precision, by scaling the result to 10^16 to 10^18.
1557 //
1558 // If the we're rounding up, we want to round up away
1559 // from zero, and if we're rounding down, truncation
1560 // is implicit.
1561 std::uint64_t amount = muldiv_round(
1562 value1, value2, tenTo14, (resultNegative != roundUp) ? tenTo14m1 : 0);
1563
1564 int offset = offset1 + offset2 + 14;
1565 if (resultNegative != roundUp)
1566 {
1567 CanonicalizeFunc(xrp, amount, offset, roundUp);
1568 }
1569 STAmount result = [&]() {
1570 // If appropriate, tell Number to round down. This gives the desired
1571 // result from STAmount::canonicalize.
1572 MightSaveRound const savedRound(Number::towards_zero);
1573 return STAmount(asset, amount, offset, resultNegative);
1574 }();
1575
1576 if (roundUp && !resultNegative && !result)
1577 {
1578 if (xrp)
1579 {
1580 // return the smallest value above zero
1581 amount = 1;
1582 offset = 0;
1583 }
1584 else
1585 {
1586 // return the smallest value above zero
1587 amount = STAmount::cMinValue;
1588 offset = STAmount::cMinOffset;
1589 }
1590 return STAmount(asset, amount, offset, resultNegative);
1591 }
1592 return result;
1593}
1594
1595STAmount
1597 STAmount const& v1,
1598 STAmount const& v2,
1599 Asset const& asset,
1600 bool roundUp)
1601{
1602 return mulRoundImpl<canonicalizeRound, DontAffectNumberRoundMode>(
1603 v1, v2, asset, roundUp);
1604}
1605
1606STAmount
1608 STAmount const& v1,
1609 STAmount const& v2,
1610 Asset const& asset,
1611 bool roundUp)
1612{
1613 return mulRoundImpl<canonicalizeRoundStrict, NumberRoundModeGuard>(
1614 v1, v2, asset, roundUp);
1615}
1616
1617// We might need to use NumberRoundModeGuard. Allow the caller
1618// to pass either that or a replacement as a template parameter.
1619template <typename MightSaveRound>
1620static STAmount
1622 STAmount const& num,
1623 STAmount const& den,
1624 Asset const& asset,
1625 bool roundUp)
1626{
1627 if (den == beast::zero)
1628 Throw<std::runtime_error>("division by zero");
1629
1630 if (num == beast::zero)
1631 return {asset};
1632
1633 std::uint64_t numVal = num.mantissa(), denVal = den.mantissa();
1634 int numOffset = num.exponent(), denOffset = den.exponent();
1635
1636 if (num.native() || num.holds<MPTIssue>())
1637 {
1638 while (numVal < STAmount::cMinValue)
1639 {
1640 numVal *= 10;
1641 --numOffset;
1642 }
1643 }
1644
1645 if (den.native() || den.holds<MPTIssue>())
1646 {
1647 while (denVal < STAmount::cMinValue)
1648 {
1649 denVal *= 10;
1650 --denOffset;
1651 }
1652 }
1653
1654 bool const resultNegative = (num.negative() != den.negative());
1655
1656 // We divide the two mantissas (each is between 10^15
1657 // and 10^16). To maintain precision, we multiply the
1658 // numerator by 10^17 (the product is in the range of
1659 // 10^32 to 10^33) followed by a division, so the result
1660 // is in the range of 10^16 to 10^15.
1661 //
1662 // We round away from zero if we're rounding up or
1663 // truncate if we're rounding down.
1664 std::uint64_t amount = muldiv_round(
1665 numVal, tenTo17, denVal, (resultNegative != roundUp) ? denVal - 1 : 0);
1666
1667 int offset = numOffset - denOffset - 17;
1668
1669 if (resultNegative != roundUp)
1671 asset.native() || asset.holds<MPTIssue>(), amount, offset, roundUp);
1672
1673 STAmount result = [&]() {
1674 // If appropriate, tell Number the rounding mode we are using.
1675 // Note that "roundUp == true" actually means "round away from zero".
1676 // Otherwise round toward zero.
1677 using enum Number::rounding_mode;
1678 MightSaveRound const savedRound(
1679 roundUp ^ resultNegative ? upward : downward);
1680 return STAmount(asset, amount, offset, resultNegative);
1681 }();
1682
1683 if (roundUp && !resultNegative && !result)
1684 {
1685 if (asset.native() || asset.holds<MPTIssue>())
1686 {
1687 // return the smallest value above zero
1688 amount = 1;
1689 offset = 0;
1690 }
1691 else
1692 {
1693 // return the smallest value above zero
1694 amount = STAmount::cMinValue;
1695 offset = STAmount::cMinOffset;
1696 }
1697 return STAmount(asset, amount, offset, resultNegative);
1698 }
1699 return result;
1700}
1701
1702STAmount
1704 STAmount const& num,
1705 STAmount const& den,
1706 Asset const& asset,
1707 bool roundUp)
1708{
1709 return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
1710}
1711
1712STAmount
1714 STAmount const& num,
1715 STAmount const& den,
1716 Asset const& asset,
1717 bool roundUp)
1718{
1719 return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
1720}
1721
1722} // namespace ripple
T append(T... args)
T begin(T... args)
Represents a JSON value.
Definition json_value.h:130
bool isArray() const
bool isObjectOrNull() const
Int asInt() const
bool isString() const
UInt asUInt() const
bool isObject() const
std::string asString() const
Returns the unquoted string value.
bool isUInt() const
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(char const *key) const
Return true if the object has a member named key.
Value get(UInt index, Value const &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
bool isInt() const
bool native() const
Definition Asset.h:82
void setJson(Json::Value &jv) const
Definition Asset.cpp:31
constexpr TIss const & get() const
constexpr bool holds() const
Definition Asset.h:113
std::string getText() const
Definition Asset.cpp:25
Floating point representation of amounts with high dynamic range.
Definition IOUAmount.h:27
int exponent() const noexcept
Definition IOUAmount.h:153
std::int64_t mantissa() const noexcept
Definition IOUAmount.h:159
A currency issued by an account.
Definition Issue.h:14
AccountID account
Definition Issue.h:17
Currency currency
Definition Issue.h:16
bool native() const
Definition Issue.cpp:47
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:114
constexpr MPTID const & getMptID() const
Definition MPTIssue.h:27
Identifies fields.
Definition SField.h:127
constexpr bool holds() const noexcept
Definition STAmount.h:446
IOUAmount iou() const
Definition STAmount.cpp:280
Json::Value getJson(JsonOptions=JsonOptions::none) const override
Definition STAmount.cpp:753
static std::uint64_t const cMaxNativeN
Definition STAmount.h:55
STAmount & operator=(beast::Zero)
Definition STAmount.h:521
int exponent() const noexcept
Definition STAmount.h:433
STBase * move(std::size_t n, void *buf) override
Definition STAmount.cpp:253
Asset const & asset() const
Definition STAmount.h:464
static std::uint64_t const cPositive
Definition STAmount.h:57
constexpr TIss const & get() const
static int const cMaxOffset
Definition STAmount.h:47
static int const cMinOffset
Definition STAmount.h:46
STAmount & operator+=(STAmount const &)
Definition STAmount.cpp:331
static std::uint64_t const cMinValue
Definition STAmount.h:50
static std::unique_ptr< STAmount > construct(SerialIter &, SField const &name)
Definition STAmount.cpp:241
void setIssue(Asset const &asset)
Set the Issue for this amount.
Definition STAmount.cpp:429
XRPAmount xrp() const
Definition STAmount.cpp:264
void setJson(Json::Value &) const
Definition STAmount.cpp:624
bool isDefault() const override
Definition STAmount.cpp:807
STAmount & operator-=(STAmount const &)
Definition STAmount.cpp:338
void add(Serializer &s) const override
Definition STAmount.cpp:761
static std::uint64_t const cIssuedCurrency
Definition STAmount.h:56
static std::uint64_t const cMPToken
Definition STAmount.h:58
SerializedTypeID getSType() const override
Definition STAmount.cpp:648
mantissa_type mValue
Definition STAmount.h:39
static std::uint64_t const cMaxValue
Definition STAmount.h:51
STAmount const & value() const noexcept
Definition STAmount.h:575
std::string getText() const override
Definition STAmount.cpp:664
bool negative() const noexcept
Definition STAmount.h:452
bool isEquivalent(STBase const &t) const override
Definition STAmount.cpp:800
STBase * copy(std::size_t n, void *buf) const override
Definition STAmount.cpp:247
MPTAmount mpt() const
Definition STAmount.cpp:295
static std::uint64_t const cValueMask
Definition STAmount.h:59
exponent_type mOffset
Definition STAmount.h:40
Issue const & issue() const
Definition STAmount.h:477
void set(std::int64_t v)
Definition STAmount.cpp:948
std::uint64_t mantissa() const noexcept
Definition STAmount.h:458
std::string getFullText() const override
Definition STAmount.cpp:654
STAmount(SerialIter &sit, SField const &name)
Definition STAmount.cpp:101
bool native() const noexcept
Definition STAmount.h:439
static std::uint64_t const uRateOne
Definition STAmount.h:61
A type which can be exported to a well known binary format.
Definition STBase.h:116
SField const & getFName() const
Definition STBase.cpp:124
static STBase * emplace(std::size_t n, void *buf, T &&val)
Definition STBase.h:214
unsigned char get8()
std::uint64_t get64()
int addBitString(base_uint< Bits, Tag > const &v)
Definition Serializer.h:112
int add8(unsigned char i)
constexpr value_type drops() const
Returns the number of drops.
Definition XRPAmount.h:158
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:19
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
unsigned int UInt
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
Definition Issue.h:96
static void canonicalizeRoundStrict(bool native, std::uint64_t &value, int &offset, bool roundUp)
STAmount divide(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:74
NumberParts partsFromString(std::string const &number)
Definition STNumber.cpp:104
constexpr Number one
Definition Number.cpp:156
bool isXRP(AccountID const &c)
Definition AccountID.h:71
bool canSubtract(STAmount const &amt1, STAmount const &amt2)
Determines if it is safe to subtract one STAmount from another.
Definition STAmount.cpp:566
bool to_issuer(AccountID &, std::string const &)
Convert hex or base58 string to AccountID.
static bool areComparable(STAmount const &v1, STAmount const &v2)
Definition STAmount.cpp:91
STAmount divRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
STAmount amountFromJson(SField const &name, Json::Value const &v)
Definition STAmount.cpp:987
bool canAdd(STAmount const &amt1, STAmount const &amt2)
Safely checks if two STAmount values can be added without overflow, underflow, or precision loss.
Definition STAmount.cpp:486
SerializedTypeID
Definition SField.h:91
static std::uint64_t const tenTo17
Definition STAmount.cpp:54
std::uint64_t constexpr maxMPTokenAmount
The maximum amount of MPTokenIssuance.
Definition Protocol.h:100
STAmount amountFromQuality(std::uint64_t rate)
Definition STAmount.cpp:965
static std::string const & systemCurrencyCode()
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
static std::uint64_t const tenTo14m1
Definition STAmount.cpp:53
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:34
std::uint64_t getRate(STAmount const &offerOut, STAmount const &offerIn)
Definition STAmount.cpp:444
static std::int64_t getSNValue(STAmount const &amount)
Definition STAmount.cpp:78
bool validJSONAsset(Json::Value const &jv)
Definition Asset.cpp:50
beast::Journal debugLog()
Returns a debug journal.
Definition Log.cpp:457
Issue const & noIssue()
Returns an asset specifier that represents no account and currency.
Definition Issue.h:104
static std::int64_t getInt64Value(STAmount const &amount, bool valid, char const *error)
Definition STAmount.cpp:58
bool amountFromJsonNoThrow(STAmount &result, Json::Value const &jvSource)
static std::int64_t getMPTValue(STAmount const &amount)
Definition STAmount.cpp:84
bool operator<(Slice const &lhs, Slice const &rhs) noexcept
Definition Slice.h:204
STAmount divRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor)
Number operator-(Number const &x, Number const &y)
Definition Number.h:281
STAmount amountFromString(Asset const &asset, std::string const &amount)
Definition STAmount.cpp:977
STAmount mulRound(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
SField const sfGeneric
constexpr base_uint< Bits, Tag > operator+(base_uint< Bits, Tag > const &a, base_uint< Bits, Tag > const &b)
Definition base_uint.h:603
static STAmount mulRoundImpl(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
constexpr bool operator==(base_uint< Bits, Tag > const &lhs, base_uint< Bits, Tag > const &rhs)
Definition base_uint.h:566
static STAmount divRoundImpl(STAmount const &num, STAmount const &den, Asset const &asset, bool roundUp)
STAmount mulRoundStrict(STAmount const &v1, STAmount const &v2, Asset const &asset, bool roundUp)
static std::uint64_t muldiv_round(std::uint64_t multiplier, std::uint64_t multiplicand, std::uint64_t divisor, std::uint64_t rounding)
static std::uint64_t const tenTo14
Definition STAmount.cpp:52
bool getSTNumberSwitchover()
Definition IOUAmount.cpp:31
bool to_currency(Currency &, std::string const &)
Tries to convert a string to a Currency, returns true on success.
Definition UintTypes.cpp:65
static void canonicalizeRound(bool native, std::uint64_t &value, int &offset, bool)
T reserve(T... args)
T length(T... args)
Note, should be treated as flags that can be | and &.
Definition STBase.h:18
std::uint64_t mantissa
Definition STNumber.h:76
T to_string(T... args)
T what(T... args)