Merge branch 'ximinez/lending-refactoring-3' into ximinez/lending-refactoring-4

This commit is contained in:
Ed Hennis
2025-09-24 15:49:59 -04:00
committed by GitHub
11 changed files with 2199 additions and 143 deletions

View File

@@ -16,16 +16,13 @@ set(CMAKE_CXX_EXTENSIONS OFF)
target_compile_definitions (common
INTERFACE
$<$<CONFIG:Debug>:DEBUG _DEBUG>
#[===[
NOTE: CMAKE release builds already have NDEBUG defined, so no need to add it
explicitly except for the special case of (profile ON) and (assert OFF).
Presumably this is because we don't want profile builds asserting unless
asserts were specifically requested.
]===]
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>
# TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x.
OPENSSL_SUPPRESS_DEPRECATED
)
$<$<AND:$<BOOL:${profile}>,$<NOT:$<BOOL:${assert}>>>:NDEBUG>)
# ^^^^ NOTE: CMAKE release builds already have NDEBUG
# defined, so no need to add it explicitly except for
# this special case of (profile ON) and (assert OFF)
# -- presumably this is because we don't want profile
# builds asserting unless asserts were specifically
# requested
if (MSVC)
# remove existing exception flag since we set it to -EHa

View File

@@ -9,7 +9,7 @@
"rocksdb/10.0.1#85537f46e538974d67da0c3977de48ac%1756234304.347",
"re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976",
"protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614",
"openssl/3.5.2#0c5a5e15ae569f45dff57adcf1770cf7%1756234259.61",
"openssl/1.1.1w#a8f0792d7c5121b954578a7149d23e03%1756223730.729",
"nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107",
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999",
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64",

View File

@@ -27,7 +27,7 @@ class Xrpl(ConanFile):
'grpc/1.50.1',
'libarchive/3.8.1',
'nudb/2.0.9',
'openssl/3.5.2',
'openssl/1.1.1w',
'soci/4.0.3',
'zlib/1.3.1',
]

View File

@@ -74,6 +74,9 @@ public:
Permission&
operator=(Permission const&) = delete;
std::optional<std::string>
getPermissionName(std::uint32_t const value) const;
std::optional<std::uint32_t>
getGranularValue(std::string const& name) const;

View File

@@ -54,34 +54,6 @@ public:
Json::Value error;
};
/** Holds the serialized result of parsing an input JSON array.
This does validation and checking on the provided JSON.
*/
class STParsedJSONArray
{
public:
/** Parses and creates an STParsedJSON array.
The result of the parsing is stored in array and error.
Exceptions:
Does not throw.
@param name The name of the JSON field, used in diagnostics.
@param json The JSON-RPC to parse.
*/
STParsedJSONArray(std::string const& name, Json::Value const& json);
STParsedJSONArray() = delete;
STParsedJSONArray(STParsedJSONArray const&) = delete;
STParsedJSONArray&
operator=(STParsedJSONArray const&) = delete;
~STParsedJSONArray() = default;
/** The STArray if the parse was successful. */
std::optional<STArray> array;
/** On failure, an appropriate set of error values. */
Json::Value error;
};
} // namespace ripple
#endif

View File

@@ -101,6 +101,22 @@ Permission::getInstance()
return instance;
}
std::optional<std::string>
Permission::getPermissionName(std::uint32_t const value) const
{
auto const permissionValue = static_cast<GranularPermissionType>(value);
if (auto const granular = getGranularName(permissionValue))
return *granular;
// not a granular permission, check if it maps to a transaction type
auto const txType = permissionToTxType(value);
if (auto const* item = TxFormats::getInstance().findByType(txType);
item != nullptr)
return item->getName();
return std::nullopt;
}
std::optional<std::uint32_t>
Permission::getGranularValue(std::string const& name) const
{

View File

@@ -62,8 +62,10 @@ STUInt8::getText() const
if (transResultInfo(TER::fromInt(value_), token, human))
return human;
// LCOV_EXCL_START
JLOG(debugLog().error())
<< "Unknown result code in metadata: " << value_;
// LCOV_EXCL_STOP
}
return std::to_string(value_);
@@ -80,8 +82,10 @@ STUInt8::getJson(JsonOptions) const
if (transResultInfo(TER::fromInt(value_), token, human))
return token;
// LCOV_EXCL_START
JLOG(debugLog().error())
<< "Unknown result code in metadata: " << value_;
// LCOV_EXCL_STOP
}
return value_;
@@ -171,6 +175,13 @@ template <>
std::string
STUInt32::getText() const
{
if (getFName() == sfPermissionValue)
{
auto const permissionName =
Permission::getInstance().getPermissionName(value_);
if (permissionName)
return *permissionName;
}
return std::to_string(value_);
}
@@ -180,23 +191,10 @@ STUInt32::getJson(JsonOptions) const
{
if (getFName() == sfPermissionValue)
{
auto const permissionValue =
static_cast<GranularPermissionType>(value_);
auto const granular =
Permission::getInstance().getGranularName(permissionValue);
if (granular)
{
return *granular;
}
else
{
auto const txType =
Permission::getInstance().permissionToTxType(value_);
auto item = TxFormats::getInstance().findByType(txType);
if (item != nullptr)
return item->getName();
}
auto const permissionName =
Permission::getInstance().getPermissionName(value_);
if (permissionName)
return *permissionName;
}
return value_;

View File

@@ -83,7 +83,8 @@ constexpr std::
return static_cast<U1>(value);
}
static std::string
// LCOV_EXCL_START
static inline std::string
make_name(std::string const& object, std::string const& field)
{
if (field.empty())
@@ -92,7 +93,7 @@ make_name(std::string const& object, std::string const& field)
return object + "." + field;
}
static Json::Value
static inline Json::Value
not_an_object(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -100,20 +101,20 @@ not_an_object(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is not a JSON object.");
}
static Json::Value
static inline Json::Value
not_an_object(std::string const& object)
{
return not_an_object(object, "");
}
static Json::Value
static inline Json::Value
not_an_array(std::string const& object)
{
return RPC::make_error(
rpcINVALID_PARAMS, "Field '" + object + "' is not a JSON array.");
}
static Json::Value
static inline Json::Value
unknown_field(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -121,7 +122,7 @@ unknown_field(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is unknown.");
}
static Json::Value
static inline Json::Value
out_of_range(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -129,7 +130,7 @@ out_of_range(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' is out of range.");
}
static Json::Value
static inline Json::Value
bad_type(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -137,7 +138,7 @@ bad_type(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' has bad type.");
}
static Json::Value
static inline Json::Value
invalid_data(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -145,13 +146,13 @@ invalid_data(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' has invalid data.");
}
static Json::Value
static inline Json::Value
invalid_data(std::string const& object)
{
return invalid_data(object, "");
}
static Json::Value
static inline Json::Value
array_expected(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -159,7 +160,7 @@ array_expected(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' must be a JSON array.");
}
static Json::Value
static inline Json::Value
string_expected(std::string const& object, std::string const& field)
{
return RPC::make_error(
@@ -167,7 +168,7 @@ string_expected(std::string const& object, std::string const& field)
"Field '" + make_name(object, field) + "' must be a string.");
}
static Json::Value
static inline Json::Value
too_deep(std::string const& object)
{
return RPC::make_error(
@@ -175,7 +176,7 @@ too_deep(std::string const& object)
"Field '" + object + "' exceeds nesting depth limit.");
}
static Json::Value
static inline Json::Value
singleton_expected(std::string const& object, unsigned int index)
{
return RPC::make_error(
@@ -184,7 +185,7 @@ singleton_expected(std::string const& object, unsigned int index)
"]' must be an object with a single key/object value.");
}
static Json::Value
static inline Json::Value
template_mismatch(SField const& sField)
{
return RPC::make_error(
@@ -193,7 +194,7 @@ template_mismatch(SField const& sField)
"' contents did not meet requirements for that type.");
}
static Json::Value
static inline Json::Value
non_object_in_array(std::string const& item, Json::UInt index)
{
return RPC::make_error(
@@ -201,6 +202,7 @@ non_object_in_array(std::string const& item, Json::UInt index)
"Item '" + item + "' at index " + std::to_string(index) +
" is not an object. Arrays may only contain objects.");
}
// LCOV_EXCL_STOP
template <class STResult, class Integer>
static std::optional<detail::STVar>
@@ -385,10 +387,13 @@ parseLeaf(
auto const& field = SField::getField(fieldName);
// checked in parseObject
if (field == sfInvalid)
{
// LCOV_EXCL_START
error = unknown_field(json_name, fieldName);
return ret;
// LCOV_EXCL_STOP
}
switch (field.fieldType)
@@ -760,6 +765,12 @@ parseLeaf(
AccountID uAccount, uIssuer;
Currency uCurrency;
if (!account && !currency && !issuer)
{
error = invalid_data(element_name);
return ret;
}
if (account)
{
// human account id
@@ -1153,24 +1164,4 @@ STParsedJSONObject::STParsedJSONObject(
object = parseObject(name, json, sfGeneric, 0, error);
}
//------------------------------------------------------------------------------
STParsedJSONArray::STParsedJSONArray(
std::string const& name,
Json::Value const& json)
{
using namespace STParsedJSONDetail;
auto arr = parseArray(name, json, sfGeneric, 0, error);
if (!arr)
array.reset();
else
{
auto p = dynamic_cast<STArray*>(&arr->get());
if (p == nullptr)
array.reset();
else
array = std::move(*p);
}
}
} // namespace ripple

View File

@@ -0,0 +1,135 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2025 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <xrpl/beast/unit_test.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/Permissions.h>
#include <xrpl/protocol/STInteger.h>
#include <xrpl/protocol/TxFormats.h>
namespace ripple {
struct STInteger_test : public beast::unit_test::suite
{
void
testUInt8()
{
STUInt8 u8(255);
BEAST_EXPECT(u8.value() == 255);
BEAST_EXPECT(u8.getText() == "255");
BEAST_EXPECT(u8.getSType() == STI_UINT8);
BEAST_EXPECT(u8.getJson(JsonOptions::none) == 255);
// there is some special handling for sfTransactionResult
STUInt8 tr(sfTransactionResult, 0);
BEAST_EXPECT(tr.value() == 0);
BEAST_EXPECT(
tr.getText() ==
"The transaction was applied. Only final in a validated ledger.");
BEAST_EXPECT(tr.getSType() == STI_UINT8);
BEAST_EXPECT(tr.getJson(JsonOptions::none) == "tesSUCCESS");
// invalid transaction result
STUInt8 tr2(sfTransactionResult, 255);
BEAST_EXPECT(tr2.value() == 255);
BEAST_EXPECT(tr2.getText() == "255");
BEAST_EXPECT(tr2.getSType() == STI_UINT8);
BEAST_EXPECT(tr2.getJson(JsonOptions::none) == 255);
}
void
testUInt16()
{
STUInt16 u16(65535);
BEAST_EXPECT(u16.value() == 65535);
BEAST_EXPECT(u16.getText() == "65535");
BEAST_EXPECT(u16.getSType() == STI_UINT16);
BEAST_EXPECT(u16.getJson(JsonOptions::none) == 65535);
// there is some special handling for sfLedgerEntryType
STUInt16 let(sfLedgerEntryType, ltACCOUNT_ROOT);
BEAST_EXPECT(let.value() == ltACCOUNT_ROOT);
BEAST_EXPECT(let.getText() == "AccountRoot");
BEAST_EXPECT(let.getSType() == STI_UINT16);
BEAST_EXPECT(let.getJson(JsonOptions::none) == "AccountRoot");
// there is some special handling for sfTransactionType
STUInt16 tlt(sfTransactionType, ttPAYMENT);
BEAST_EXPECT(tlt.value() == ttPAYMENT);
BEAST_EXPECT(tlt.getText() == "Payment");
BEAST_EXPECT(tlt.getSType() == STI_UINT16);
BEAST_EXPECT(tlt.getJson(JsonOptions::none) == "Payment");
}
void
testUInt32()
{
STUInt32 u32(4'294'967'295u);
BEAST_EXPECT(u32.value() == 4'294'967'295u);
BEAST_EXPECT(u32.getText() == "4294967295");
BEAST_EXPECT(u32.getSType() == STI_UINT32);
BEAST_EXPECT(u32.getJson(JsonOptions::none) == 4'294'967'295u);
// there is some special handling for sfPermissionValue
STUInt32 pv(sfPermissionValue, ttPAYMENT + 1);
BEAST_EXPECT(pv.value() == ttPAYMENT + 1);
BEAST_EXPECT(pv.getText() == "Payment");
BEAST_EXPECT(pv.getSType() == STI_UINT32);
BEAST_EXPECT(pv.getJson(JsonOptions::none) == "Payment");
STUInt32 pv2(sfPermissionValue, PaymentMint);
BEAST_EXPECT(pv2.value() == PaymentMint);
BEAST_EXPECT(pv2.getText() == "PaymentMint");
BEAST_EXPECT(pv2.getSType() == STI_UINT32);
BEAST_EXPECT(pv2.getJson(JsonOptions::none) == "PaymentMint");
}
void
testUInt64()
{
STUInt64 u64(0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64.value() == 0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64.getText() == "18446744073709551615");
BEAST_EXPECT(u64.getSType() == STI_UINT64);
// By default, getJson returns hex string
auto jsonVal = u64.getJson(JsonOptions::none);
BEAST_EXPECT(jsonVal.isString());
BEAST_EXPECT(jsonVal.asString() == "ffffffffffffffff");
STUInt64 u64_2(sfMaximumAmount, 0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64_2.value() == 0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64_2.getText() == "18446744073709551615");
BEAST_EXPECT(u64_2.getSType() == STI_UINT64);
BEAST_EXPECT(
u64_2.getJson(JsonOptions::none) == "18446744073709551615");
}
void
run() override
{
testUInt8();
testUInt16();
testUInt32();
testUInt64();
}
};
BEAST_DEFINE_TESTSUITE(STInteger, protocol, ripple);
} // namespace ripple

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,34 @@
*/
//==============================================================================
/**
*
* TODO: Remove ripple::basic_semaphore (and this file) and use
* std::counting_semaphore.
*
* Background:
* - PR: https://github.com/XRPLF/rippled/pull/5512/files
* - std::counting_semaphore had a bug fixed in both GCC and Clang:
* * GCC PR 104928: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928
* * LLVM PR 79265: https://github.com/llvm/llvm-project/pull/79265
*
* GCC:
* According to GCC Bugzilla PR104928
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928#c15), the fix is
* scheduled for inclusion in GCC 16.0 (see comment #15, Target
* Milestone: 16.0). It is not included in GCC 14.x or earlier, and there is no
* indication that it will be backported to GCC 13.x or 14.x branches.
*
* Clang:
* The fix for is included in Clang 19.1.0+
*
* Once the minimum compiler version is updated to > GCC 16.0 or Clang 19.1.0,
* we can remove this file.
*
* WARNING: Avoid using std::counting_semaphore until the minimum compiler
* version is updated.
*/
#ifndef RIPPLE_CORE_SEMAPHORE_H_INCLUDED
#define RIPPLE_CORE_SEMAPHORE_H_INCLUDED