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