mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
Introduce MPT support (XLS-33d): (#5143)
Amendment: - MPTokensV1 New Transactions: - MPTokenIssuanceCreate - MPTokenIssuanceDestroy - MPTokenIssuanceSet - MPTokenAuthorize Modified Transactions: - Payment - Clawback New Objects: - MPTokenIssuance - MPToken API updates: - ledger_entry - account_objects - ledger_data Other: - Add += and -= operators to ValueProxy Read full spec: https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0033d-multi-purpose-tokens --------- Co-authored-by: Shawn Xie <shawnxie920@gmail.com> Co-authored-by: Howard Hinnant <howard.hinnant@gmail.com> Co-authored-by: Ed Hennis <ed@ripple.com> Co-authored-by: John Freeman <jfreeman08@gmail.com>
This commit is contained in:
committed by
GitHub
parent
63209c2646
commit
23c37fa506
@@ -21,6 +21,7 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/safe_cast.h>
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/SystemParameters.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
@@ -63,10 +64,11 @@ static const std::uint64_t tenTo17 = tenTo14 * 1000;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static std::int64_t
|
||||
getSNValue(STAmount const& amount)
|
||||
getInt64Value(STAmount const& amount, bool valid, const char* error)
|
||||
{
|
||||
if (!amount.native())
|
||||
Throw<std::runtime_error>("amount is not native!");
|
||||
if (!valid)
|
||||
Throw<std::runtime_error>(error);
|
||||
assert(amount.exponent() == 0);
|
||||
|
||||
auto ret = static_cast<std::int64_t>(amount.mantissa());
|
||||
|
||||
@@ -78,26 +80,53 @@ getSNValue(STAmount const& amount)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::int64_t
|
||||
getSNValue(STAmount const& amount)
|
||||
{
|
||||
return getInt64Value(amount, amount.native(), "amount is not native!");
|
||||
}
|
||||
|
||||
static std::int64_t
|
||||
getMPTValue(STAmount const& amount)
|
||||
{
|
||||
return getInt64Value(
|
||||
amount, amount.holds<MPTIssue>(), "amount is not MPT!");
|
||||
}
|
||||
|
||||
static bool
|
||||
areComparable(STAmount const& v1, STAmount const& v2)
|
||||
{
|
||||
return v1.native() == v2.native() &&
|
||||
v1.issue().currency == v2.issue().currency;
|
||||
if (v1.holds<Issue>() && v2.holds<Issue>())
|
||||
return v1.native() == v2.native() &&
|
||||
v1.get<Issue>().currency == v2.get<Issue>().currency;
|
||||
if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>())
|
||||
return v1.get<MPTIssue>() == v2.get<MPTIssue>();
|
||||
return false;
|
||||
}
|
||||
|
||||
STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
{
|
||||
std::uint64_t value = sit.get64();
|
||||
|
||||
// native
|
||||
if ((value & cNotNative) == 0)
|
||||
// native or MPT
|
||||
if ((value & cIssuedCurrency) == 0)
|
||||
{
|
||||
// positive
|
||||
if ((value & cPosNative) != 0)
|
||||
if ((value & cMPToken) != 0)
|
||||
{
|
||||
mValue = value & ~cPosNative;
|
||||
// is MPT
|
||||
mOffset = 0;
|
||||
mIsNegative = (value & cPositive) == 0;
|
||||
mValue = (value << 8) | sit.get8();
|
||||
mAsset = sit.get192();
|
||||
return;
|
||||
}
|
||||
// else is XRP
|
||||
mAsset = xrpIssue();
|
||||
// positive
|
||||
if ((value & cPositive) != 0)
|
||||
{
|
||||
mValue = value & cValueMask;
|
||||
mOffset = 0;
|
||||
mIsNative = true;
|
||||
mIsNegative = false;
|
||||
return;
|
||||
}
|
||||
@@ -106,9 +135,8 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
if (value == 0)
|
||||
Throw<std::runtime_error>("negative zero is not canonical");
|
||||
|
||||
mValue = value;
|
||||
mValue = value & cValueMask;
|
||||
mOffset = 0;
|
||||
mIsNative = true;
|
||||
mIsNegative = true;
|
||||
return;
|
||||
}
|
||||
@@ -140,7 +168,7 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
Throw<std::runtime_error>("invalid currency value");
|
||||
}
|
||||
|
||||
mIssue = issue;
|
||||
mAsset = issue;
|
||||
mValue = value;
|
||||
mOffset = offset;
|
||||
mIsNegative = isNegative;
|
||||
@@ -151,97 +179,32 @@ STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
if (offset != 512)
|
||||
Throw<std::runtime_error>("invalid currency value");
|
||||
|
||||
mIssue = issue;
|
||||
mAsset = issue;
|
||||
mValue = 0;
|
||||
mOffset = 0;
|
||||
mIsNegative = false;
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
SField const& name,
|
||||
Issue const& issue,
|
||||
mantissa_type mantissa,
|
||||
exponent_type exponent,
|
||||
bool native,
|
||||
bool negative,
|
||||
unchecked)
|
||||
: STBase(name)
|
||||
, mIssue(issue)
|
||||
, mValue(mantissa)
|
||||
, mOffset(exponent)
|
||||
, mIsNative(native)
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
Issue const& issue,
|
||||
mantissa_type mantissa,
|
||||
exponent_type exponent,
|
||||
bool native,
|
||||
bool negative,
|
||||
unchecked)
|
||||
: mIssue(issue)
|
||||
, mValue(mantissa)
|
||||
, mOffset(exponent)
|
||||
, mIsNative(native)
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
SField const& name,
|
||||
Issue const& issue,
|
||||
mantissa_type mantissa,
|
||||
exponent_type exponent,
|
||||
bool native,
|
||||
bool negative)
|
||||
: STBase(name)
|
||||
, mIssue(issue)
|
||||
, mValue(mantissa)
|
||||
, mOffset(exponent)
|
||||
, mIsNative(native)
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(SField const& name, std::int64_t mantissa)
|
||||
: STBase(name), mOffset(0), mIsNative(true)
|
||||
: STBase(name), mAsset(xrpIssue()), mOffset(0)
|
||||
{
|
||||
set(mantissa);
|
||||
}
|
||||
|
||||
STAmount::STAmount(SField const& name, std::uint64_t mantissa, bool negative)
|
||||
: STBase(name)
|
||||
, mAsset(xrpIssue())
|
||||
, mValue(mantissa)
|
||||
, mOffset(0)
|
||||
, mIsNative(true)
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
assert(mValue <= std::numeric_limits<std::int64_t>::max());
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
SField const& name,
|
||||
Issue const& issue,
|
||||
std::uint64_t mantissa,
|
||||
int exponent,
|
||||
bool negative)
|
||||
: STBase(name)
|
||||
, mIssue(issue)
|
||||
, mValue(mantissa)
|
||||
, mOffset(exponent)
|
||||
, mIsNegative(negative)
|
||||
{
|
||||
assert(mValue <= std::numeric_limits<std::int64_t>::max());
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(SField const& name, STAmount const& from)
|
||||
: STBase(name)
|
||||
, mIssue(from.mIssue)
|
||||
, mAsset(from.mAsset)
|
||||
, mValue(from.mValue)
|
||||
, mOffset(from.mOffset)
|
||||
, mIsNegative(from.mIsNegative)
|
||||
@@ -253,62 +216,16 @@ STAmount::STAmount(SField const& name, STAmount const& from)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
STAmount::STAmount(std::uint64_t mantissa, bool negative)
|
||||
: mValue(mantissa)
|
||||
: mAsset(xrpIssue())
|
||||
, mValue(mantissa)
|
||||
, mOffset(0)
|
||||
, mIsNative(true)
|
||||
, mIsNegative(mantissa != 0 && negative)
|
||||
{
|
||||
assert(mValue <= std::numeric_limits<std::int64_t>::max());
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
Issue const& issue,
|
||||
std::uint64_t mantissa,
|
||||
int exponent,
|
||||
bool negative)
|
||||
: mIssue(issue), mValue(mantissa), mOffset(exponent), mIsNegative(negative)
|
||||
{
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(Issue const& issue, std::int64_t mantissa, int exponent)
|
||||
: mIssue(issue), mOffset(exponent)
|
||||
{
|
||||
set(mantissa);
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(
|
||||
Issue const& issue,
|
||||
std::uint32_t mantissa,
|
||||
int exponent,
|
||||
bool negative)
|
||||
: STAmount(issue, safe_cast<std::uint64_t>(mantissa), exponent, negative)
|
||||
{
|
||||
}
|
||||
|
||||
STAmount::STAmount(Issue const& issue, int mantissa, int exponent)
|
||||
: STAmount(issue, safe_cast<std::int64_t>(mantissa), exponent)
|
||||
{
|
||||
}
|
||||
|
||||
// Legacy support for new-style amounts
|
||||
STAmount::STAmount(IOUAmount const& amount, Issue const& issue)
|
||||
: mIssue(issue)
|
||||
, mOffset(amount.exponent())
|
||||
, mIsNative(false)
|
||||
, mIsNegative(amount < beast::zero)
|
||||
{
|
||||
if (mIsNegative)
|
||||
mValue = static_cast<std::uint64_t>(-amount.mantissa());
|
||||
else
|
||||
mValue = static_cast<std::uint64_t>(amount.mantissa());
|
||||
|
||||
canonicalize();
|
||||
}
|
||||
|
||||
STAmount::STAmount(XRPAmount const& amount)
|
||||
: mOffset(0), mIsNative(true), mIsNegative(amount < beast::zero)
|
||||
: mAsset(xrpIssue()), mOffset(0), mIsNegative(amount < beast::zero)
|
||||
{
|
||||
if (mIsNegative)
|
||||
mValue = unsafe_cast<std::uint64_t>(-amount.drops());
|
||||
@@ -344,7 +261,7 @@ STAmount::move(std::size_t n, void* buf)
|
||||
XRPAmount
|
||||
STAmount::xrp() const
|
||||
{
|
||||
if (!mIsNative)
|
||||
if (!native())
|
||||
Throw<std::logic_error>(
|
||||
"Cannot return non-native STAmount as XRPAmount");
|
||||
|
||||
@@ -359,8 +276,8 @@ STAmount::xrp() const
|
||||
IOUAmount
|
||||
STAmount::iou() const
|
||||
{
|
||||
if (mIsNative)
|
||||
Throw<std::logic_error>("Cannot return native STAmount as IOUAmount");
|
||||
if (native() || !holds<Issue>())
|
||||
Throw<std::logic_error>("Cannot return non-IOU STAmount as IOUAmount");
|
||||
|
||||
auto mantissa = static_cast<std::int64_t>(mValue);
|
||||
auto exponent = mOffset;
|
||||
@@ -371,10 +288,24 @@ STAmount::iou() const
|
||||
return {mantissa, exponent};
|
||||
}
|
||||
|
||||
MPTAmount
|
||||
STAmount::mpt() const
|
||||
{
|
||||
if (!holds<MPTIssue>())
|
||||
Throw<std::logic_error>("Cannot return STAmount as MPTAmount");
|
||||
|
||||
auto value = static_cast<MPTAmount::value_type>(mValue);
|
||||
|
||||
if (mIsNegative)
|
||||
value = -value;
|
||||
|
||||
return MPTAmount{value};
|
||||
}
|
||||
|
||||
STAmount&
|
||||
STAmount::operator=(IOUAmount const& iou)
|
||||
{
|
||||
assert(mIsNative == false);
|
||||
assert(native() == false);
|
||||
mOffset = iou.exponent();
|
||||
mIsNegative = iou < beast::zero;
|
||||
if (mIsNegative)
|
||||
@@ -418,7 +349,7 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
// Result must be in terms of v1 currency and issuer.
|
||||
return {
|
||||
v1.getFName(),
|
||||
v1.issue(),
|
||||
v1.asset(),
|
||||
v2.mantissa(),
|
||||
v2.exponent(),
|
||||
v2.negative()};
|
||||
@@ -426,6 +357,8 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
|
||||
if (v1.native())
|
||||
return {v1.getFName(), getSNValue(v1) + getSNValue(v2)};
|
||||
if (v1.holds<MPTIssue>())
|
||||
return {v1.mAsset, v1.mpt().value() + v2.mpt().value()};
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
@@ -462,18 +395,18 @@ operator+(STAmount const& v1, STAmount const& v2)
|
||||
std::int64_t fv = vv1 + vv2;
|
||||
|
||||
if ((fv >= -10) && (fv <= 10))
|
||||
return {v1.getFName(), v1.issue()};
|
||||
return {v1.getFName(), v1.asset()};
|
||||
|
||||
if (fv >= 0)
|
||||
return STAmount{
|
||||
v1.getFName(),
|
||||
v1.issue(),
|
||||
v1.asset(),
|
||||
static_cast<std::uint64_t>(fv),
|
||||
ov1,
|
||||
false};
|
||||
|
||||
return STAmount{
|
||||
v1.getFName(), v1.issue(), static_cast<std::uint64_t>(-fv), ov1, true};
|
||||
v1.getFName(), v1.asset(), static_cast<std::uint64_t>(-fv), ov1, true};
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -487,10 +420,9 @@ operator-(STAmount const& v1, STAmount const& v2)
|
||||
std::uint64_t const STAmount::uRateOne = getRate(STAmount(1), STAmount(1));
|
||||
|
||||
void
|
||||
STAmount::setIssue(Issue const& issue)
|
||||
STAmount::setIssue(Asset const& asset)
|
||||
{
|
||||
mIssue = issue;
|
||||
mIsNative = isXRP(*this);
|
||||
mAsset = asset;
|
||||
}
|
||||
|
||||
// Convert an offer into an index amount so they sort by rate.
|
||||
@@ -529,13 +461,12 @@ STAmount::setJson(Json::Value& elem) const
|
||||
{
|
||||
elem = Json::objectValue;
|
||||
|
||||
if (!mIsNative)
|
||||
if (!native())
|
||||
{
|
||||
// It is an error for currency or issuer not to be specified for valid
|
||||
// json.
|
||||
elem[jss::value] = getText();
|
||||
elem[jss::currency] = to_string(mIssue.currency);
|
||||
elem[jss::issuer] = to_string(mIssue.account);
|
||||
mAsset.setJson(elem);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -561,7 +492,7 @@ STAmount::getFullText() const
|
||||
std::string ret;
|
||||
|
||||
ret.reserve(64);
|
||||
ret = getText() + "/" + mIssue.getText();
|
||||
ret = getText() + "/" + mAsset.getText();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -581,7 +512,7 @@ STAmount::getText() const
|
||||
bool const scientific(
|
||||
(mOffset != 0) && ((mOffset < -25) || (mOffset > -5)));
|
||||
|
||||
if (mIsNative || scientific)
|
||||
if (native() || mAsset.holds<MPTIssue>() || scientific)
|
||||
{
|
||||
ret.append(raw_value);
|
||||
|
||||
@@ -661,19 +592,28 @@ STAmount::getJson(JsonOptions) const
|
||||
void
|
||||
STAmount::add(Serializer& s) const
|
||||
{
|
||||
if (mIsNative)
|
||||
if (native())
|
||||
{
|
||||
assert(mOffset == 0);
|
||||
|
||||
if (!mIsNegative)
|
||||
s.add64(mValue | cPosNative);
|
||||
s.add64(mValue | cPositive);
|
||||
else
|
||||
s.add64(mValue);
|
||||
}
|
||||
else if (mAsset.holds<MPTIssue>())
|
||||
{
|
||||
auto u8 = static_cast<unsigned char>(cMPToken >> 56);
|
||||
if (!mIsNegative)
|
||||
u8 |= static_cast<unsigned char>(cPositive >> 56);
|
||||
s.add8(u8);
|
||||
s.add64(mValue);
|
||||
s.addBitString(mAsset.get<MPTIssue>().getMptID());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*this == beast::zero)
|
||||
s.add64(cNotNative);
|
||||
s.add64(cIssuedCurrency);
|
||||
else if (mIsNegative) // 512 = not native
|
||||
s.add64(
|
||||
mValue |
|
||||
@@ -683,9 +623,8 @@ STAmount::add(Serializer& s) const
|
||||
mValue |
|
||||
(static_cast<std::uint64_t>(mOffset + 512 + 256 + 97)
|
||||
<< (64 - 10)));
|
||||
|
||||
s.addBitString(mIssue.currency);
|
||||
s.addBitString(mIssue.account);
|
||||
s.addBitString(mAsset.get<Issue>().currency);
|
||||
s.addBitString(mAsset.get<Issue>().account);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,7 +638,7 @@ STAmount::isEquivalent(const STBase& t) const
|
||||
bool
|
||||
STAmount::isDefault() const
|
||||
{
|
||||
return (mValue == 0) && mIsNative;
|
||||
return (mValue == 0) && native();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -723,11 +662,9 @@ STAmount::isDefault() const
|
||||
void
|
||||
STAmount::canonicalize()
|
||||
{
|
||||
if (isXRP(*this))
|
||||
if (native() || mAsset.holds<MPTIssue>())
|
||||
{
|
||||
// native currency amounts should always have an offset of zero
|
||||
mIsNative = true;
|
||||
|
||||
// native and MPT currency amounts should always have an offset of zero
|
||||
// log(2^64,10) ~ 19.2
|
||||
if (mValue == 0 || mOffset <= -20)
|
||||
{
|
||||
@@ -740,18 +677,26 @@ STAmount::canonicalize()
|
||||
if (getSTAmountCanonicalizeSwitchover())
|
||||
{
|
||||
// log(cMaxNativeN, 10) == 17
|
||||
if (mOffset > 17)
|
||||
if (native() && mOffset > 17)
|
||||
Throw<std::runtime_error>(
|
||||
"Native currency amount out of range");
|
||||
// log(maxMPTokenAmount, 10) ~ 18.96
|
||||
if (mAsset.holds<MPTIssue>() && mOffset > 18)
|
||||
Throw<std::runtime_error>("MPT amount out of range");
|
||||
}
|
||||
|
||||
if (getSTNumberSwitchover() && getSTAmountCanonicalizeSwitchover())
|
||||
{
|
||||
Number num(
|
||||
mIsNegative ? -mValue : mValue, mOffset, Number::unchecked{});
|
||||
XRPAmount xrp{num};
|
||||
mIsNegative = xrp.drops() < 0;
|
||||
mValue = mIsNegative ? -xrp.drops() : xrp.drops();
|
||||
auto set = [&](auto const& val) {
|
||||
mIsNegative = val.value() < 0;
|
||||
mValue = mIsNegative ? -val.value() : val.value();
|
||||
};
|
||||
if (native())
|
||||
set(XRPAmount{num});
|
||||
else
|
||||
set(MPTAmount{num});
|
||||
mOffset = 0;
|
||||
}
|
||||
else
|
||||
@@ -768,23 +713,25 @@ STAmount::canonicalize()
|
||||
{
|
||||
// N.B. do not move the overflow check to after the
|
||||
// multiplication
|
||||
if (mValue > cMaxNativeN)
|
||||
if (native() && mValue > cMaxNativeN)
|
||||
Throw<std::runtime_error>(
|
||||
"Native currency amount out of range");
|
||||
else if (!native() && mValue > maxMPTokenAmount)
|
||||
Throw<std::runtime_error>("MPT amount out of range");
|
||||
}
|
||||
mValue *= 10;
|
||||
--mOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (mValue > cMaxNativeN)
|
||||
if (native() && mValue > cMaxNativeN)
|
||||
Throw<std::runtime_error>("Native currency amount out of range");
|
||||
else if (!native() && mValue > maxMPTokenAmount)
|
||||
Throw<std::runtime_error>("MPT amount out of range");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mIsNative = false;
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
{
|
||||
*this = iou();
|
||||
@@ -860,7 +807,7 @@ amountFromQuality(std::uint64_t rate)
|
||||
}
|
||||
|
||||
STAmount
|
||||
amountFromString(Issue const& issue, std::string const& amount)
|
||||
amountFromString(Asset const& asset, std::string const& amount)
|
||||
{
|
||||
static boost::regex const reNumber(
|
||||
"^" // the beginning of the string
|
||||
@@ -892,9 +839,10 @@ amountFromString(Issue const& issue, std::string const& amount)
|
||||
|
||||
bool negative = (match[1].matched && (match[1] == "-"));
|
||||
|
||||
// Can't specify XRP using fractional representation
|
||||
if (isXRP(issue) && match[3].matched)
|
||||
Throw<std::runtime_error>("XRP must be specified in integral drops.");
|
||||
// Can't specify XRP or MPT using fractional representation
|
||||
if ((asset.native() || asset.holds<MPTIssue>()) && match[3].matched)
|
||||
Throw<std::runtime_error>(
|
||||
"XRP and MPT must be specified as integral amount.");
|
||||
|
||||
std::uint64_t mantissa;
|
||||
int exponent;
|
||||
@@ -921,7 +869,7 @@ amountFromString(Issue const& issue, std::string const& amount)
|
||||
exponent += beast::lexicalCastThrow<int>(std::string(match[7]));
|
||||
}
|
||||
|
||||
return {issue, mantissa, exponent, negative};
|
||||
return {asset, mantissa, exponent, negative};
|
||||
}
|
||||
|
||||
STAmount
|
||||
@@ -930,11 +878,12 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
STAmount::mantissa_type mantissa = 0;
|
||||
STAmount::exponent_type exponent = 0;
|
||||
bool negative = false;
|
||||
Issue issue;
|
||||
Asset asset;
|
||||
|
||||
Json::Value value;
|
||||
Json::Value currency;
|
||||
Json::Value currencyOrMPTID;
|
||||
Json::Value issuer;
|
||||
bool isMPT = false;
|
||||
|
||||
if (v.isNull())
|
||||
{
|
||||
@@ -943,14 +892,25 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
}
|
||||
else if (v.isObject())
|
||||
{
|
||||
if (!validJSONAsset(v))
|
||||
Throw<std::runtime_error>("Invalid Asset's Json specification");
|
||||
|
||||
value = v[jss::value];
|
||||
currency = v[jss::currency];
|
||||
issuer = v[jss::issuer];
|
||||
if (v.isMember(jss::mpt_issuance_id))
|
||||
{
|
||||
isMPT = true;
|
||||
currencyOrMPTID = v[jss::mpt_issuance_id];
|
||||
}
|
||||
else
|
||||
{
|
||||
currencyOrMPTID = v[jss::currency];
|
||||
issuer = v[jss::issuer];
|
||||
}
|
||||
}
|
||||
else if (v.isArray())
|
||||
{
|
||||
value = v.get(Json::UInt(0), 0);
|
||||
currency = v.get(Json::UInt(1), Json::nullValue);
|
||||
currencyOrMPTID = v.get(Json::UInt(1), Json::nullValue);
|
||||
issuer = v.get(Json::UInt(2), Json::nullValue);
|
||||
}
|
||||
else if (v.isString())
|
||||
@@ -965,7 +925,7 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
value = elements[0];
|
||||
|
||||
if (elements.size() > 1)
|
||||
currency = elements[1];
|
||||
currencyOrMPTID = elements[1];
|
||||
|
||||
if (elements.size() > 2)
|
||||
issuer = elements[2];
|
||||
@@ -975,26 +935,38 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
value = v;
|
||||
}
|
||||
|
||||
bool const native = !currency.isString() || currency.asString().empty() ||
|
||||
(currency.asString() == systemCurrencyCode());
|
||||
bool const native = !currencyOrMPTID.isString() ||
|
||||
currencyOrMPTID.asString().empty() ||
|
||||
(currencyOrMPTID.asString() == systemCurrencyCode());
|
||||
|
||||
if (native)
|
||||
{
|
||||
if (v.isObjectOrNull())
|
||||
Throw<std::runtime_error>("XRP may not be specified as an object");
|
||||
issue = xrpIssue();
|
||||
asset = xrpIssue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-XRP
|
||||
if (!to_currency(issue.currency, currency.asString()))
|
||||
Throw<std::runtime_error>("invalid currency");
|
||||
|
||||
if (!issuer.isString() || !to_issuer(issue.account, issuer.asString()))
|
||||
Throw<std::runtime_error>("invalid issuer");
|
||||
|
||||
if (isXRP(issue.currency))
|
||||
Throw<std::runtime_error>("invalid issuer");
|
||||
if (isMPT)
|
||||
{
|
||||
// sequence (32 bits) + account (160 bits)
|
||||
uint192 u;
|
||||
if (!u.parseHex(currencyOrMPTID.asString()))
|
||||
Throw<std::runtime_error>("invalid MPTokenIssuanceID");
|
||||
asset = u;
|
||||
}
|
||||
else
|
||||
{
|
||||
Issue issue;
|
||||
if (!to_currency(issue.currency, currencyOrMPTID.asString()))
|
||||
Throw<std::runtime_error>("invalid currency");
|
||||
if (!issuer.isString() ||
|
||||
!to_issuer(issue.account, issuer.asString()))
|
||||
Throw<std::runtime_error>("invalid issuer");
|
||||
if (issue.native())
|
||||
Throw<std::runtime_error>("invalid issuer");
|
||||
asset = issue;
|
||||
}
|
||||
}
|
||||
|
||||
if (value.isInt())
|
||||
@@ -1015,7 +987,7 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
}
|
||||
else if (value.isString())
|
||||
{
|
||||
auto const ret = amountFromString(issue, value.asString());
|
||||
auto const ret = amountFromString(asset, value.asString());
|
||||
|
||||
mantissa = ret.mantissa();
|
||||
exponent = ret.exponent();
|
||||
@@ -1026,7 +998,7 @@ amountFromJson(SField const& name, Json::Value const& v)
|
||||
Throw<std::runtime_error>("invalid amount type");
|
||||
}
|
||||
|
||||
return {name, issue, mantissa, exponent, native, negative};
|
||||
return {name, asset, mantissa, exponent, negative};
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1100,10 +1072,9 @@ operator-(STAmount const& value)
|
||||
return value;
|
||||
return STAmount(
|
||||
value.getFName(),
|
||||
value.issue(),
|
||||
value.asset(),
|
||||
value.mantissa(),
|
||||
value.exponent(),
|
||||
value.native(),
|
||||
!value.negative(),
|
||||
STAmount::unchecked{});
|
||||
}
|
||||
@@ -1162,20 +1133,20 @@ muldiv_round(
|
||||
}
|
||||
|
||||
STAmount
|
||||
divide(STAmount const& num, STAmount const& den, Issue const& issue)
|
||||
divide(STAmount const& num, STAmount const& den, Asset const& asset)
|
||||
{
|
||||
if (den == beast::zero)
|
||||
Throw<std::runtime_error>("division by zero");
|
||||
|
||||
if (num == beast::zero)
|
||||
return {issue};
|
||||
return {asset};
|
||||
|
||||
std::uint64_t numVal = num.mantissa();
|
||||
std::uint64_t denVal = den.mantissa();
|
||||
int numOffset = num.exponent();
|
||||
int denOffset = den.exponent();
|
||||
|
||||
if (num.native())
|
||||
if (num.native() || num.holds<MPTIssue>())
|
||||
{
|
||||
while (numVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1185,7 +1156,7 @@ divide(STAmount const& num, STAmount const& den, Issue const& issue)
|
||||
}
|
||||
}
|
||||
|
||||
if (den.native())
|
||||
if (den.native() || den.holds<MPTIssue>())
|
||||
{
|
||||
while (denVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1200,24 +1171,22 @@ divide(STAmount const& num, STAmount const& den, Issue const& issue)
|
||||
// 10^32 to 10^33) followed by a division, so the result
|
||||
// is in the range of 10^16 to 10^15.
|
||||
return STAmount(
|
||||
issue,
|
||||
asset,
|
||||
muldiv(numVal, tenTo17, denVal) + 5,
|
||||
numOffset - denOffset - 17,
|
||||
num.negative() != den.negative());
|
||||
}
|
||||
|
||||
STAmount
|
||||
multiply(STAmount const& v1, STAmount const& v2, Issue const& issue)
|
||||
multiply(STAmount const& v1, STAmount const& v2, Asset const& asset)
|
||||
{
|
||||
if (v1 == beast::zero || v2 == beast::zero)
|
||||
return STAmount(issue);
|
||||
return STAmount(asset);
|
||||
|
||||
if (v1.native() && v2.native() && isXRP(issue))
|
||||
if (v1.native() && v2.native() && asset.native())
|
||||
{
|
||||
std::uint64_t const minV =
|
||||
getSNValue(v1) < getSNValue(v2) ? getSNValue(v1) : getSNValue(v2);
|
||||
std::uint64_t const maxV =
|
||||
getSNValue(v1) < getSNValue(v2) ? getSNValue(v2) : getSNValue(v1);
|
||||
std::uint64_t const minV = std::min(getSNValue(v1), getSNValue(v2));
|
||||
std::uint64_t const maxV = std::max(getSNValue(v1), getSNValue(v2));
|
||||
|
||||
if (minV > 3000000000ull) // sqrt(cMaxNative)
|
||||
Throw<std::runtime_error>("Native value overflow");
|
||||
@@ -1227,16 +1196,32 @@ multiply(STAmount const& v1, STAmount const& v2, Issue const& issue)
|
||||
|
||||
return STAmount(v1.getFName(), minV * maxV);
|
||||
}
|
||||
if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
|
||||
{
|
||||
std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
|
||||
std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
|
||||
|
||||
if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
|
||||
Throw<std::runtime_error>("MPT value overflow");
|
||||
|
||||
if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
|
||||
Throw<std::runtime_error>("MPT value overflow");
|
||||
|
||||
return STAmount(asset, minV * maxV);
|
||||
}
|
||||
|
||||
if (getSTNumberSwitchover())
|
||||
return {IOUAmount{Number{v1} * Number{v2}}, issue};
|
||||
{
|
||||
auto const r = Number{v1} * Number{v2};
|
||||
return STAmount{asset, r.mantissa(), r.exponent()};
|
||||
}
|
||||
|
||||
std::uint64_t value1 = v1.mantissa();
|
||||
std::uint64_t value2 = v2.mantissa();
|
||||
int offset1 = v1.exponent();
|
||||
int offset2 = v2.exponent();
|
||||
|
||||
if (v1.native())
|
||||
if (v1.native() || v1.holds<MPTIssue>())
|
||||
{
|
||||
while (value1 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1245,7 +1230,7 @@ multiply(STAmount const& v1, STAmount const& v2, Issue const& issue)
|
||||
}
|
||||
}
|
||||
|
||||
if (v2.native())
|
||||
if (v2.native() || v2.holds<MPTIssue>())
|
||||
{
|
||||
while (value2 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1259,7 +1244,7 @@ multiply(STAmount const& v1, STAmount const& v2, Issue const& issue)
|
||||
// range. Dividing their product by 10^14 maintains the
|
||||
// precision, by scaling the result to 10^16 to 10^18.
|
||||
return STAmount(
|
||||
issue,
|
||||
asset,
|
||||
muldiv(value1, value2, tenTo14) + 7,
|
||||
offset1 + offset2 + 14,
|
||||
v1.negative() != v2.negative());
|
||||
@@ -1396,20 +1381,18 @@ static STAmount
|
||||
mulRoundImpl(
|
||||
STAmount const& v1,
|
||||
STAmount const& v2,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
if (v1 == beast::zero || v2 == beast::zero)
|
||||
return {issue};
|
||||
return {asset};
|
||||
|
||||
bool const xrp = isXRP(issue);
|
||||
bool const xrp = asset.native();
|
||||
|
||||
if (v1.native() && v2.native() && xrp)
|
||||
{
|
||||
std::uint64_t minV =
|
||||
(getSNValue(v1) < getSNValue(v2)) ? getSNValue(v1) : getSNValue(v2);
|
||||
std::uint64_t maxV =
|
||||
(getSNValue(v1) < getSNValue(v2)) ? getSNValue(v2) : getSNValue(v1);
|
||||
std::uint64_t minV = std::min(getSNValue(v1), getSNValue(v2));
|
||||
std::uint64_t maxV = std::max(getSNValue(v1), getSNValue(v2));
|
||||
|
||||
if (minV > 3000000000ull) // sqrt(cMaxNative)
|
||||
Throw<std::runtime_error>("Native value overflow");
|
||||
@@ -1420,10 +1403,24 @@ mulRoundImpl(
|
||||
return STAmount(v1.getFName(), minV * maxV);
|
||||
}
|
||||
|
||||
if (v1.holds<MPTIssue>() && v2.holds<MPTIssue>() && asset.holds<MPTIssue>())
|
||||
{
|
||||
std::uint64_t const minV = std::min(getMPTValue(v1), getMPTValue(v2));
|
||||
std::uint64_t const maxV = std::max(getMPTValue(v1), getMPTValue(v2));
|
||||
|
||||
if (minV > 3037000499ull) // sqrt(maxMPTokenAmount) ~ 3037000499.98
|
||||
Throw<std::runtime_error>("MPT value overflow");
|
||||
|
||||
if (((maxV >> 32) * minV) > 2147483648ull) // maxMPTokenAmount / 2^32
|
||||
Throw<std::runtime_error>("MPT value overflow");
|
||||
|
||||
return STAmount(asset, minV * maxV);
|
||||
}
|
||||
|
||||
std::uint64_t value1 = v1.mantissa(), value2 = v2.mantissa();
|
||||
int offset1 = v1.exponent(), offset2 = v2.exponent();
|
||||
|
||||
if (v1.native())
|
||||
if (v1.native() || v1.holds<MPTIssue>())
|
||||
{
|
||||
while (value1 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1432,7 +1429,7 @@ mulRoundImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (v2.native())
|
||||
if (v2.native() || v2.holds<MPTIssue>())
|
||||
{
|
||||
while (value2 < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1463,7 +1460,7 @@ mulRoundImpl(
|
||||
// If appropriate, tell Number to round down. This gives the desired
|
||||
// result from STAmount::canonicalize.
|
||||
MightSaveRound const savedRound(Number::towards_zero);
|
||||
return STAmount(issue, amount, offset, resultNegative);
|
||||
return STAmount(asset, amount, offset, resultNegative);
|
||||
}();
|
||||
|
||||
if (roundUp && !resultNegative && !result)
|
||||
@@ -1480,7 +1477,7 @@ mulRoundImpl(
|
||||
amount = STAmount::cMinValue;
|
||||
offset = STAmount::cMinOffset;
|
||||
}
|
||||
return STAmount(issue, amount, offset, resultNegative);
|
||||
return STAmount(asset, amount, offset, resultNegative);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1489,22 +1486,22 @@ STAmount
|
||||
mulRound(
|
||||
STAmount const& v1,
|
||||
STAmount const& v2,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
return mulRoundImpl<canonicalizeRound, DontAffectNumberRoundMode>(
|
||||
v1, v2, issue, roundUp);
|
||||
v1, v2, asset, roundUp);
|
||||
}
|
||||
|
||||
STAmount
|
||||
mulRoundStrict(
|
||||
STAmount const& v1,
|
||||
STAmount const& v2,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
return mulRoundImpl<canonicalizeRoundStrict, NumberRoundModeGuard>(
|
||||
v1, v2, issue, roundUp);
|
||||
v1, v2, asset, roundUp);
|
||||
}
|
||||
|
||||
// We might need to use NumberRoundModeGuard. Allow the caller
|
||||
@@ -1514,19 +1511,19 @@ static STAmount
|
||||
divRoundImpl(
|
||||
STAmount const& num,
|
||||
STAmount const& den,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
if (den == beast::zero)
|
||||
Throw<std::runtime_error>("division by zero");
|
||||
|
||||
if (num == beast::zero)
|
||||
return {issue};
|
||||
return {asset};
|
||||
|
||||
std::uint64_t numVal = num.mantissa(), denVal = den.mantissa();
|
||||
int numOffset = num.exponent(), denOffset = den.exponent();
|
||||
|
||||
if (num.native())
|
||||
if (num.native() || num.holds<MPTIssue>())
|
||||
{
|
||||
while (numVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1535,7 +1532,7 @@ divRoundImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (den.native())
|
||||
if (den.native() || den.holds<MPTIssue>())
|
||||
{
|
||||
while (denVal < STAmount::cMinValue)
|
||||
{
|
||||
@@ -1560,7 +1557,8 @@ divRoundImpl(
|
||||
int offset = numOffset - denOffset - 17;
|
||||
|
||||
if (resultNegative != roundUp)
|
||||
canonicalizeRound(isXRP(issue), amount, offset, roundUp);
|
||||
canonicalizeRound(
|
||||
asset.native() || asset.holds<MPTIssue>(), amount, offset, roundUp);
|
||||
|
||||
STAmount result = [&]() {
|
||||
// If appropriate, tell Number the rounding mode we are using.
|
||||
@@ -1569,12 +1567,12 @@ divRoundImpl(
|
||||
using enum Number::rounding_mode;
|
||||
MightSaveRound const savedRound(
|
||||
roundUp ^ resultNegative ? upward : downward);
|
||||
return STAmount(issue, amount, offset, resultNegative);
|
||||
return STAmount(asset, amount, offset, resultNegative);
|
||||
}();
|
||||
|
||||
if (roundUp && !resultNegative && !result)
|
||||
{
|
||||
if (isXRP(issue))
|
||||
if (asset.native() || asset.holds<MPTIssue>())
|
||||
{
|
||||
// return the smallest value above zero
|
||||
amount = 1;
|
||||
@@ -1586,7 +1584,7 @@ divRoundImpl(
|
||||
amount = STAmount::cMinValue;
|
||||
offset = STAmount::cMinOffset;
|
||||
}
|
||||
return STAmount(issue, amount, offset, resultNegative);
|
||||
return STAmount(asset, amount, offset, resultNegative);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1595,20 +1593,20 @@ STAmount
|
||||
divRound(
|
||||
STAmount const& num,
|
||||
STAmount const& den,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
return divRoundImpl<DontAffectNumberRoundMode>(num, den, issue, roundUp);
|
||||
return divRoundImpl<DontAffectNumberRoundMode>(num, den, asset, roundUp);
|
||||
}
|
||||
|
||||
STAmount
|
||||
divRoundStrict(
|
||||
STAmount const& num,
|
||||
STAmount const& den,
|
||||
Issue const& issue,
|
||||
Asset const& asset,
|
||||
bool roundUp)
|
||||
{
|
||||
return divRoundImpl<NumberRoundModeGuard>(num, den, issue, roundUp);
|
||||
return divRoundImpl<NumberRoundModeGuard>(num, den, asset, roundUp);
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user