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