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