mirror of
https://github.com/XRPLF/rippled.git
synced 2026-01-24 08:35:24 +00:00
Compare commits
7 Commits
ximinez/lo
...
ximinez/as
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c51758d12c | ||
|
|
69dda6b34c | ||
|
|
2132398b7d | ||
|
|
e3644f265c | ||
|
|
8c573cd0bb | ||
|
|
c894cd2b5f | ||
|
|
ecfe43ece7 |
@@ -23,6 +23,7 @@
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
|
||||
@@ -416,6 +416,9 @@ public:
|
||||
void
|
||||
delField(int index);
|
||||
|
||||
SOEStyle
|
||||
getStyle(SField const& field) const;
|
||||
|
||||
bool
|
||||
hasMatchingEntry(STBase const&);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace BuildInfo {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "3.1.0-rc1"
|
||||
char const* const versionString = "3.1.0-rc2"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
|
||||
@@ -142,6 +142,8 @@ areComparable(STAmount const& v1, STAmount const& v2)
|
||||
return false;
|
||||
}
|
||||
|
||||
static_assert(INITIAL_XRP.drops() == STAmount::cMaxNativeN);
|
||||
|
||||
STAmount::STAmount(SerialIter& sit, SField const& name) : STBase(name)
|
||||
{
|
||||
std::uint64_t value = sit.get64();
|
||||
|
||||
@@ -599,6 +599,12 @@ STObject::delField(int index)
|
||||
v_.erase(v_.begin() + index);
|
||||
}
|
||||
|
||||
SOEStyle
|
||||
STObject::getStyle(SField const& field) const
|
||||
{
|
||||
return mType ? mType->style(field) : soeINVALID;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
STObject::getFieldU8(SField const& field) const
|
||||
{
|
||||
@@ -924,6 +930,11 @@ STObject::add(Serializer& s, WhichFields whichFields) const
|
||||
(sType != STI_OBJECT) ||
|
||||
(field->getFName().fieldType == STI_OBJECT),
|
||||
"ripple::STObject::add : valid field type");
|
||||
XRPL_ASSERT_PARTS(
|
||||
getStyle(field->getFName()) != soeDEFAULT || !field->isDefault(),
|
||||
"ripple::STObject::add",
|
||||
"non-default value");
|
||||
|
||||
field->addFieldID(s);
|
||||
field->add(s);
|
||||
if (sType == STI_ARRAY || sType == STI_OBJECT)
|
||||
|
||||
@@ -18,10 +18,28 @@ associateAsset(SLE& sle, Asset const& asset)
|
||||
// If the field is not set or present, skip it.
|
||||
if (type == STI_NOTPRESENT)
|
||||
continue;
|
||||
|
||||
// If the type doesn't downcast, then the flag shouldn't be on the
|
||||
// SField
|
||||
auto& ta = entry.downcast<STTakesAsset>();
|
||||
auto const style = sle.getStyle(ta.getFName());
|
||||
XRPL_ASSERT_PARTS(
|
||||
style != soeINVALID,
|
||||
"ripple::associateAsset",
|
||||
"valid template element style");
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
style != soeDEFAULT || !ta.isDefault(),
|
||||
"ripple::associateAsset",
|
||||
"non-default value");
|
||||
ta.associateAsset(asset);
|
||||
|
||||
// associateAsset in derived classes may change the underlying
|
||||
// value, but it won't know anything about how the value relates to
|
||||
// the SLE. If the template element is soeDEFAULT, and the value
|
||||
// changed to the default value, remove the field.
|
||||
if (style == soeDEFAULT && ta.isDefault())
|
||||
sle.makeFieldAbsent(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5801,6 +5801,263 @@ class Vault_test : public beast::unit_test::suite
|
||||
testCase(MPT, "MPT", owner, depositor, issuer);
|
||||
}
|
||||
|
||||
void
|
||||
testAssetsMaximum()
|
||||
{
|
||||
testcase("Assets Maximum");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env{*this, testable_amendments() | featureSingleAssetVault};
|
||||
Account const owner{"owner"};
|
||||
Account const issuer{"issuer"};
|
||||
|
||||
Vault vault{env};
|
||||
env.fund(XRP(1'000'000), issuer, owner);
|
||||
env.close();
|
||||
|
||||
auto const maxInt64 =
|
||||
std::to_string(std::numeric_limits<std::int64_t>::max());
|
||||
BEAST_EXPECT(maxInt64 == "9223372036854775807");
|
||||
|
||||
// Naming things is hard
|
||||
auto const maxInt64Plus1 = std::to_string(
|
||||
static_cast<std::uint64_t>(
|
||||
std::numeric_limits<std::int64_t>::max()) +
|
||||
1);
|
||||
BEAST_EXPECT(maxInt64Plus1 == "9223372036854775808");
|
||||
|
||||
auto const initialXRP = to_string(INITIAL_XRP);
|
||||
BEAST_EXPECT(initialXRP == "100000000000000000");
|
||||
|
||||
auto const initialXRPPlus1 = to_string(INITIAL_XRP + 1);
|
||||
BEAST_EXPECT(initialXRPPlus1 == "100000000000000001");
|
||||
|
||||
{
|
||||
testcase("Assets Maximum: XRP");
|
||||
|
||||
PrettyAsset const xrpAsset = xrpIssue();
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = xrpAsset});
|
||||
tx[sfData] = "4D65746144617461";
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64;
|
||||
env(tx, ter(tefEXCEPTION), THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRPPlus1;
|
||||
env(tx, ter(tefEXCEPTION), THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRP;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64Plus1;
|
||||
env(tx, ter(tefEXCEPTION), THISLINE);
|
||||
env.close();
|
||||
|
||||
// This value will be rounded
|
||||
auto const insertAt = maxInt64Plus1.size() - 3;
|
||||
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
|
||||
maxInt64Plus1.substr(insertAt); // (max int64+1) / 1000
|
||||
BEAST_EXPECT(decimalTest == "9223372036854775.808");
|
||||
tx[sfAssetsMaximum] = decimalTest;
|
||||
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 9223372036854776);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Assets Maximum: MPT");
|
||||
|
||||
PrettyAsset const mptAsset = [&]() {
|
||||
MPTTester mptt{env, issuer, mptInitNoFund};
|
||||
mptt.create(
|
||||
{.flags =
|
||||
tfMPTCanClawback | tfMPTCanTransfer | tfMPTCanLock});
|
||||
env.close();
|
||||
PrettyAsset const mptAsset = mptt["MPT"];
|
||||
mptt.authorize({.account = owner});
|
||||
env.close();
|
||||
return mptAsset;
|
||||
}();
|
||||
|
||||
env(pay(issuer, owner, mptAsset(100'000)), THISLINE);
|
||||
env.close();
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = mptAsset});
|
||||
tx[sfData] = "4D65746144617461";
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRPPlus1;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRP;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64Plus1;
|
||||
env(tx, ter(tefEXCEPTION), THISLINE);
|
||||
env.close();
|
||||
|
||||
// This value will be rounded
|
||||
auto const insertAt = maxInt64Plus1.size() - 1;
|
||||
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) + "." +
|
||||
maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
|
||||
BEAST_EXPECT(decimalTest == "922337203685477580.8");
|
||||
tx[sfAssetsMaximum] = decimalTest;
|
||||
auto const newKeylet = keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == 922337203685477581);
|
||||
}
|
||||
|
||||
{
|
||||
testcase("Assets Maximum: IOU");
|
||||
|
||||
// Almost anything goes with IOUs
|
||||
PrettyAsset iouAsset = issuer["IOU"];
|
||||
env.trust(iouAsset(1000), owner);
|
||||
env(pay(issuer, owner, iouAsset(200)));
|
||||
env.close();
|
||||
|
||||
auto [tx, keylet] =
|
||||
vault.create({.owner = owner, .asset = iouAsset});
|
||||
tx[sfData] = "4D65746144617461";
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRPPlus1;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = initialXRP;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = maxInt64Plus1;
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = "1000000000000000e80";
|
||||
env.close();
|
||||
|
||||
tx[sfAssetsMaximum] = "1000000000000000e-96";
|
||||
env.close();
|
||||
|
||||
// These values will be rounded to 15 significant digits
|
||||
{
|
||||
auto const insertAt = maxInt64Plus1.size() - 1;
|
||||
auto const decimalTest = maxInt64Plus1.substr(0, insertAt) +
|
||||
"." + maxInt64Plus1.substr(insertAt); // (max int64+1) / 10
|
||||
BEAST_EXPECT(decimalTest == "922337203685477580.8");
|
||||
tx[sfAssetsMaximum] = decimalTest;
|
||||
auto const newKeylet =
|
||||
keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(
|
||||
(vaultSle->at(sfAssetsMaximum) ==
|
||||
Number{9223372036854776, 2, Number::normalized{}}));
|
||||
}
|
||||
{
|
||||
tx[sfAssetsMaximum] =
|
||||
"9223372036854775807e40"; // max int64 * 10^40
|
||||
auto const newKeylet =
|
||||
keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(
|
||||
(vaultSle->at(sfAssetsMaximum) ==
|
||||
Number{9223372036854776, 43, Number::normalized{}}));
|
||||
}
|
||||
{
|
||||
tx[sfAssetsMaximum] =
|
||||
"9223372036854775807e-40"; // max int64 * 10^-40
|
||||
auto const newKeylet =
|
||||
keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(
|
||||
(vaultSle->at(sfAssetsMaximum) ==
|
||||
Number{9223372036854776, -37, Number::normalized{}}));
|
||||
}
|
||||
{
|
||||
tx[sfAssetsMaximum] =
|
||||
"9223372036854775807e-100"; // max int64 * 10^-100
|
||||
auto const newKeylet =
|
||||
keylet::vault(owner.id(), env.seq(owner));
|
||||
env(tx, THISLINE);
|
||||
env.close();
|
||||
|
||||
// Field 'AssetsMaximum' may not be explicitly set to default.
|
||||
auto const vaultSle = env.le(newKeylet);
|
||||
if (!BEAST_EXPECT(vaultSle))
|
||||
return;
|
||||
|
||||
BEAST_EXPECT(vaultSle->at(sfAssetsMaximum) == numZero);
|
||||
}
|
||||
|
||||
// What _can't_ IOUs do?
|
||||
// 1. Exceed maximum exponent / offset
|
||||
tx[sfAssetsMaximum] = "1000000000000000e81";
|
||||
env(tx, ter(tefEXCEPTION), THISLINE);
|
||||
env.close();
|
||||
|
||||
// 2. Mantissa larger than uint64 max
|
||||
try
|
||||
{
|
||||
tx[sfAssetsMaximum] =
|
||||
"18446744073709551617e5"; // uint64 max + 1
|
||||
env(tx, THISLINE);
|
||||
BEAST_EXPECT(false);
|
||||
}
|
||||
catch (parse_error const& e)
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
BEAST_EXPECT(
|
||||
e.what() ==
|
||||
"invalidParamsField 'tx_json.AssetsMaximum' has invalid "
|
||||
"data."s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -5821,6 +6078,7 @@ public:
|
||||
testDelegate();
|
||||
testVaultClawbackBurnShares();
|
||||
testVaultClawbackAssets();
|
||||
testAssetsMaximum();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user