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.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
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: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
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: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
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)