mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-28 17:40:25 +00:00
Merge branch 'ripple/wasmi' into ripple/wasmi-host-functions
This commit is contained in:
4
BUILD.md
4
BUILD.md
@@ -148,8 +148,8 @@ function extract_version {
|
||||
}
|
||||
|
||||
# Define which recipes to export.
|
||||
recipes=('ed25519' 'grpc' 'openssl' 'secp256k1' 'snappy' 'soci')
|
||||
folders=('all' 'all' '3.x.x' 'all' 'all' 'all')
|
||||
recipes=('ed25519' 'grpc' 'nudb' 'openssl' 'secp256k1' 'snappy' 'soci')
|
||||
folders=('all' 'all' 'all' '3.x.x' 'all' 'all' 'all')
|
||||
|
||||
# Selectively check out the recipes from our CCI fork.
|
||||
cd external
|
||||
|
||||
@@ -30,7 +30,6 @@ target_link_libraries(xrpl_boost
|
||||
Boost::process
|
||||
Boost::program_options
|
||||
Boost::regex
|
||||
Boost::system
|
||||
Boost::thread)
|
||||
if(Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
|
||||
14
conan.lock
14
conan.lock
@@ -12,7 +12,7 @@
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"openssl/3.5.4#1b986e61b38fdfda3b40bebc1b234393%1768312656.257",
|
||||
"nudb/2.0.9#fb8dfd1a5557f5e0528114c2da17721e%1765850143.957",
|
||||
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
"libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03",
|
||||
@@ -24,7 +24,7 @@
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
||||
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
||||
"boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1765850172.862",
|
||||
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1765955095.179",
|
||||
"abseil/20250127.0#99262a368bd01c0ccca8790dfced9719%1766517936.993"
|
||||
],
|
||||
"build_requires": [
|
||||
@@ -43,18 +43,22 @@
|
||||
],
|
||||
"python_requires": [],
|
||||
"overrides": {
|
||||
"boost/1.90.0#d5e8defe7355494953be18524a7f135b": [
|
||||
null,
|
||||
"boost/1.90.0"
|
||||
],
|
||||
"protobuf/5.27.0": [
|
||||
"protobuf/6.32.1"
|
||||
],
|
||||
"lz4/1.9.4": [
|
||||
"lz4/1.10.0"
|
||||
],
|
||||
"boost/1.83.0": [
|
||||
"boost/1.88.0"
|
||||
],
|
||||
"sqlite3/3.44.2": [
|
||||
"sqlite3/3.49.1"
|
||||
],
|
||||
"boost/1.83.0": [
|
||||
"boost/1.90.0"
|
||||
],
|
||||
"lz4/[>=1.9.4 <2]": [
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504"
|
||||
]
|
||||
|
||||
@@ -132,7 +132,7 @@ class Xrpl(ConanFile):
|
||||
transitive_headers_opt = (
|
||||
{"transitive_headers": True} if conan_version.split(".")[0] == "2" else {}
|
||||
)
|
||||
self.requires("boost/1.88.0", force=True, **transitive_headers_opt)
|
||||
self.requires("boost/1.90.0", force=True, **transitive_headers_opt)
|
||||
self.requires("date/3.0.4", **transitive_headers_opt)
|
||||
self.requires("lz4/1.10.0", force=True)
|
||||
self.requires("protobuf/6.32.1", force=True)
|
||||
|
||||
@@ -397,6 +397,9 @@ public:
|
||||
void
|
||||
delField(int index);
|
||||
|
||||
SOEStyle
|
||||
getStyle(SField const& field) const;
|
||||
|
||||
bool
|
||||
hasMatchingEntry(STBase const&);
|
||||
|
||||
|
||||
@@ -100,6 +100,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();
|
||||
|
||||
@@ -580,6 +580,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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
"xrpl::associateAsset",
|
||||
"valid template element style");
|
||||
|
||||
XRPL_ASSERT_PARTS(
|
||||
style != soeDEFAULT || !ta.isDefault(),
|
||||
"xrpl::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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5782,6 +5782,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
|
||||
@@ -5802,6 +6059,7 @@ public:
|
||||
testDelegate();
|
||||
testVaultClawbackBurnShares();
|
||||
testVaultClawbackAssets();
|
||||
testAssetsMaximum();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15,15 +15,8 @@
|
||||
#include <xrpl/protocol/Units.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#if (defined(__clang_major__) && __clang_major__ < 15)
|
||||
#include <experimental/source_location>
|
||||
using source_location = std::experimental::source_location;
|
||||
#else
|
||||
#include <source_location>
|
||||
using std::source_location;
|
||||
#endif
|
||||
#include <vector>
|
||||
|
||||
namespace xrpl {
|
||||
namespace test {
|
||||
@@ -640,7 +633,7 @@ checkMetrics(
|
||||
std::size_t expectedPerLedger,
|
||||
std::uint64_t expectedMinFeeLevel = baseFeeLevel.fee(),
|
||||
std::uint64_t expectedMedFeeLevel = minEscalationFeeLevel.fee(),
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
int line = location.line();
|
||||
char const* file = location.file_name();
|
||||
|
||||
@@ -14,13 +14,7 @@
|
||||
#include <xrpl/protocol/STXChainBridge.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#if (defined(__clang_major__) && __clang_major__ < 15)
|
||||
#include <experimental/source_location>
|
||||
using source_location = std::experimental::source_location;
|
||||
#else
|
||||
#include <source_location>
|
||||
using std::source_location;
|
||||
#endif
|
||||
namespace xrpl {
|
||||
|
||||
namespace test {
|
||||
@@ -114,7 +108,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
||||
Json::Value const& jv,
|
||||
std::string const& err,
|
||||
std::string const& msg,
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
if (BEAST_EXPECT(jv.isMember(jss::status)))
|
||||
BEAST_EXPECTS(
|
||||
@@ -297,7 +291,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
||||
FieldType const typeID,
|
||||
std::string const& expectedError,
|
||||
bool required = true,
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
forAllApiVersions([&, this](unsigned apiVersion) {
|
||||
if (required)
|
||||
@@ -350,7 +344,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
||||
FieldType typeID,
|
||||
std::string const& expectedError,
|
||||
bool required = true,
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
forAllApiVersions([&, this](unsigned apiVersion) {
|
||||
if (required)
|
||||
@@ -407,7 +401,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
||||
runLedgerEntryTest(
|
||||
test::jtx::Env& env,
|
||||
Json::StaticString const& parentField,
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
testMalformedField(
|
||||
env,
|
||||
@@ -431,7 +425,7 @@ class LedgerEntry_test : public beast::unit_test::suite
|
||||
test::jtx::Env& env,
|
||||
Json::StaticString const& parentField,
|
||||
std::vector<Subfield> const& subfields,
|
||||
source_location const location = source_location::current())
|
||||
std::source_location const location = std::source_location::current())
|
||||
{
|
||||
testMalformedField(
|
||||
env,
|
||||
|
||||
@@ -203,11 +203,7 @@ public:
|
||||
|
||||
boost::asio::signal_set m_signals;
|
||||
|
||||
// Once we get C++20, we could use `std::atomic_flag` for `isTimeToStop`
|
||||
// and eliminate the need for the condition variable and the mutex.
|
||||
std::condition_variable stoppingCondition_;
|
||||
mutable std::mutex stoppingMutex_;
|
||||
std::atomic<bool> isTimeToStop = false;
|
||||
std::atomic_flag isTimeToStop;
|
||||
|
||||
std::atomic<bool> checkSigs_;
|
||||
|
||||
@@ -1539,10 +1535,7 @@ ApplicationImp::run()
|
||||
getLoadManager().activateStallDetector();
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lk{stoppingMutex_};
|
||||
stoppingCondition_.wait(lk, [this] { return isTimeToStop.load(); });
|
||||
}
|
||||
isTimeToStop.wait(false, std::memory_order_relaxed);
|
||||
|
||||
JLOG(m_journal.debug()) << "Application stopping";
|
||||
|
||||
@@ -1629,14 +1622,14 @@ ApplicationImp::run()
|
||||
void
|
||||
ApplicationImp::signalStop(std::string msg)
|
||||
{
|
||||
if (!isTimeToStop.exchange(true))
|
||||
if (!isTimeToStop.test_and_set(std::memory_order_acquire))
|
||||
{
|
||||
if (msg.empty())
|
||||
JLOG(m_journal.warn()) << "Server stopping";
|
||||
else
|
||||
JLOG(m_journal.warn()) << "Server stopping: " << msg;
|
||||
|
||||
stoppingCondition_.notify_all();
|
||||
isTimeToStop.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1655,7 +1648,7 @@ ApplicationImp::checkSigs(bool check)
|
||||
bool
|
||||
ApplicationImp::isStopping() const
|
||||
{
|
||||
return isTimeToStop.load();
|
||||
return isTimeToStop.test(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
Reference in New Issue
Block a user