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