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