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

This commit is contained in:
Ed Hennis
2025-09-26 19:26:06 -04:00
committed by GitHub
22 changed files with 341 additions and 97 deletions

View File

@@ -249,4 +249,33 @@ STUInt64::getJson(JsonOptions) const
return convertToString(value_, 16); // Convert to base 16
}
//------------------------------------------------------------------------------
template <>
STInteger<std::int32_t>::STInteger(SerialIter& sit, SField const& name)
: STInteger(name, sit.get32())
{
}
template <>
SerializedTypeID
STInt32::getSType() const
{
return STI_INT32;
}
template <>
std::string
STInt32::getText() const
{
return std::to_string(value_);
}
template <>
Json::Value
STInt32::getJson(JsonOptions) const
{
return value_;
}
} // namespace ripple

View File

@@ -647,6 +647,12 @@ STObject::getFieldH256(SField const& field) const
return getFieldByValue<STUInt256>(field);
}
std::int32_t
STObject::getFieldI32(SField const& field) const
{
return getFieldByValue<STInt32>(field);
}
AccountID
STObject::getAccountID(SField const& field) const
{
@@ -761,6 +767,12 @@ STObject::setFieldH256(SField const& field, uint256 const& v)
setFieldUsingSetValue<STUInt256>(field, v);
}
void
STObject::setFieldI32(SField const& field, std::int32_t v)
{
setFieldUsingSetValue<STInt32>(field, v);
}
void
STObject::setFieldV256(SField const& field, STVector256 const& v)
{

View File

@@ -563,30 +563,6 @@ parseLeaf(
break;
}
case STI_UINT192: {
if (!value.isString())
{
error = bad_type(json_name, fieldName);
return ret;
}
uint192 num;
if (auto const s = value.asString(); !num.parseHex(s))
{
if (!s.empty())
{
error = invalid_data(json_name, fieldName);
return ret;
}
num.zero();
}
ret = detail::make_stvar<STUInt192>(field, num);
break;
}
case STI_UINT160: {
if (!value.isString())
{
@@ -611,6 +587,30 @@ parseLeaf(
break;
}
case STI_UINT192: {
if (!value.isString())
{
error = bad_type(json_name, fieldName);
return ret;
}
uint192 num;
if (auto const s = value.asString(); !num.parseHex(s))
{
if (!s.empty())
{
error = invalid_data(json_name, fieldName);
return ret;
}
num.zero();
}
ret = detail::make_stvar<STUInt192>(field, num);
break;
}
case STI_UINT256: {
if (!value.isString())
{
@@ -635,6 +635,52 @@ parseLeaf(
break;
}
case STI_INT32:
try
{
if (value.isString())
{
ret = detail::make_stvar<STInt32>(
field,
beast::lexicalCastThrow<std::int32_t>(
value.asString()));
}
else if (value.isInt())
{
// future-proofing - a static assert failure if the JSON
// library ever supports larger ints
// In such case, we will need additional bounds checks here
static_assert(
std::is_same_v<decltype(value.asInt()), std::int32_t>);
ret = detail::make_stvar<STInt32>(field, value.asInt());
}
else if (value.isUInt())
{
auto const uintValue = value.asUInt();
if (uintValue >
static_cast<std::uint32_t>(
std::numeric_limits<std::int32_t>::max()))
{
error = out_of_range(json_name, fieldName);
return ret;
}
ret = detail::make_stvar<STInt32>(
field, static_cast<std::int32_t>(uintValue));
}
else
{
error = bad_type(json_name, fieldName);
return ret;
}
}
catch (std::exception const&)
{
error = invalid_data(json_name, fieldName);
return ret;
}
break;
case STI_VL:
if (!value.isString())
{
@@ -1120,8 +1166,7 @@ parseArray(
Json::Value const objectFields(json[i][objectName]);
std::stringstream ss;
ss << json_name << "."
<< "[" << i << "]." << objectName;
ss << json_name << "." << "[" << i << "]." << objectName;
auto ret = parseObject(
ss.str(), objectFields, nameField, depth + 1, error);

View File

@@ -208,6 +208,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
case STI_UINT256:
construct<STUInt256>(std::forward<Args>(args)...);
return;
case STI_INT32:
construct<STInt32>(std::forward<Args>(args)...);
return;
case STI_VECTOR256:
construct<STVector256>(std::forward<Args>(args)...);
return;

View File

@@ -83,6 +83,12 @@ Serializer::addInteger(std::uint64_t i)
{
return add64(i);
}
template <>
int
Serializer::addInteger(std::int32_t i)
{
return add32(i);
}
int
Serializer::addRaw(Blob const& vector)

View File

@@ -122,10 +122,27 @@ struct STAccount_test : public beast::unit_test::suite
}
}
void
testAccountID()
{
auto const s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
if (auto const parsed = parseBase58<AccountID>(s); BEAST_EXPECT(parsed))
{
BEAST_EXPECT(toBase58(*parsed) == s);
}
{
auto const s =
"âabcd1rNxp4h8apvRis6mJf9Sh8C6iRxfrDWNâabcdAVâ\xc2\x80\xc2\x8f";
BEAST_EXPECT(!parseBase58<AccountID>(s));
}
}
void
run() override
{
testSTAccount();
testAccountID();
}
};

View File

@@ -30,6 +30,7 @@ struct STInteger_test : public beast::unit_test::suite
void
testUInt8()
{
testcase("UInt8");
STUInt8 u8(255);
BEAST_EXPECT(u8.value() == 255);
BEAST_EXPECT(u8.getText() == "255");
@@ -56,6 +57,7 @@ struct STInteger_test : public beast::unit_test::suite
void
testUInt16()
{
testcase("UInt16");
STUInt16 u16(65535);
BEAST_EXPECT(u16.value() == 65535);
BEAST_EXPECT(u16.getText() == "65535");
@@ -80,6 +82,7 @@ struct STInteger_test : public beast::unit_test::suite
void
testUInt32()
{
testcase("UInt32");
STUInt32 u32(4'294'967'295u);
BEAST_EXPECT(u32.value() == 4'294'967'295u);
BEAST_EXPECT(u32.getText() == "4294967295");
@@ -102,6 +105,7 @@ struct STInteger_test : public beast::unit_test::suite
void
testUInt64()
{
testcase("UInt64");
STUInt64 u64(0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64.value() == 0xFFFFFFFFFFFFFFFFull);
BEAST_EXPECT(u64.getText() == "18446744073709551615");
@@ -120,6 +124,29 @@ struct STInteger_test : public beast::unit_test::suite
u64_2.getJson(JsonOptions::none) == "18446744073709551615");
}
void
testInt32()
{
testcase("Int32");
{
int const minInt32 = -2147483648;
STInt32 i32(minInt32);
BEAST_EXPECT(i32.value() == minInt32);
BEAST_EXPECT(i32.getText() == "-2147483648");
BEAST_EXPECT(i32.getSType() == STI_INT32);
BEAST_EXPECT(i32.getJson(JsonOptions::none) == minInt32);
}
{
int const maxInt32 = 2147483647;
STInt32 i32(maxInt32);
BEAST_EXPECT(i32.value() == maxInt32);
BEAST_EXPECT(i32.getText() == "2147483647");
BEAST_EXPECT(i32.getSType() == STI_INT32);
BEAST_EXPECT(i32.getJson(JsonOptions::none) == maxInt32);
}
}
void
run() override
{
@@ -127,6 +154,7 @@ struct STInteger_test : public beast::unit_test::suite
testUInt16();
testUInt32();
testUInt64();
testInt32();
}
};

View File

@@ -736,6 +736,107 @@ class STParsedJSON_test : public beast::unit_test::suite
}
}
void
testInt32()
{
testcase("Int32");
{
Json::Value j;
int const minInt32 = -2147483648;
j[sfDummyInt32] = minInt32;
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(obj.object.has_value());
if (BEAST_EXPECT(obj.object->isFieldPresent(sfDummyInt32)))
BEAST_EXPECT(obj.object->getFieldI32(sfDummyInt32) == minInt32);
}
// max value
{
Json::Value j;
int const maxInt32 = 2147483647;
j[sfDummyInt32] = maxInt32;
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(obj.object.has_value());
if (BEAST_EXPECT(obj.object->isFieldPresent(sfDummyInt32)))
BEAST_EXPECT(obj.object->getFieldI32(sfDummyInt32) == maxInt32);
}
// max uint value
{
Json::Value j;
unsigned int const maxUInt32 = 2147483647u;
j[sfDummyInt32] = maxUInt32;
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(obj.object.has_value());
if (BEAST_EXPECT(obj.object->isFieldPresent(sfDummyInt32)))
BEAST_EXPECT(
obj.object->getFieldI32(sfDummyInt32) ==
static_cast<int32_t>(maxUInt32));
}
// Test with string value
{
Json::Value j;
j[sfDummyInt32] = "2147483647";
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(obj.object.has_value());
if (BEAST_EXPECT(obj.object->isFieldPresent(sfDummyInt32)))
BEAST_EXPECT(
obj.object->getFieldI32(sfDummyInt32) == 2147483647u);
}
// Test with string negative value
{
Json::Value j;
int value = -2147483648;
j[sfDummyInt32] = std::to_string(value);
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(obj.object.has_value());
if (BEAST_EXPECT(obj.object->isFieldPresent(sfDummyInt32)))
BEAST_EXPECT(obj.object->getFieldI32(sfDummyInt32) == value);
}
// Test out of range value for int32 (negative)
{
Json::Value j;
j[sfDummyInt32] = "-2147483649";
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(!obj.object.has_value());
}
// Test out of range value for int32 (positive)
{
Json::Value j;
j[sfDummyInt32] = 2147483648u;
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(!obj.object.has_value());
}
// Test string value out of range
{
Json::Value j;
j[sfDummyInt32] = "2147483648";
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(!obj.object.has_value());
}
// Test bad_type (arrayValue)
{
Json::Value j;
j[sfDummyInt32] = Json::Value(Json::arrayValue);
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(!obj.object.has_value());
}
// Test bad_type (objectValue)
{
Json::Value j;
j[sfDummyInt32] = Json::Value(Json::objectValue);
STParsedJSONObject obj("Test", j);
BEAST_EXPECT(!obj.object.has_value());
}
}
void
testBlob()
{
@@ -1338,8 +1439,7 @@ class STParsedJSON_test : public beast::unit_test::suite
issueJson["issuer"] = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
j[sfAsset] = issueJson;
STParsedJSONObject obj("Test", j);
if (BEAST_EXPECTS(
obj.object.has_value(), obj.error.toStyledString()))
if (BEAST_EXPECT(obj.object.has_value()))
{
BEAST_EXPECT(obj.object->isFieldPresent(sfAsset));
auto const& issueField = (*obj.object)[sfAsset];
@@ -2235,6 +2335,7 @@ class STParsedJSON_test : public beast::unit_test::suite
testUInt160();
testUInt192();
testUInt256();
testInt32();
testBlob();
testVector256();
testAccount();

View File

@@ -1,52 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 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/UintTypes.h>
namespace ripple {
struct types_test : public beast::unit_test::suite
{
void
testAccountID()
{
auto const s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
if (auto const parsed = parseBase58<AccountID>(s); BEAST_EXPECT(parsed))
{
BEAST_EXPECT(toBase58(*parsed) == s);
}
{
auto const s =
"âabcd1rNxp4h8apvRis6mJf9Sh8C6iRxfrDWNâabcdAVâ\xc2\x80\xc2\x8f";
BEAST_EXPECT(!parseBase58<AccountID>(s));
}
}
void
run() override
{
testAccountID();
}
};
BEAST_DEFINE_TESTSUITE(types, protocol, ripple);
} // namespace ripple

View File

@@ -19,6 +19,8 @@
#include <test/jtx.h>
#include <xrpld/app/tx/apply.h>
#include <xrpl/protocol/AmountConversions.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Quality.h>
@@ -578,6 +580,32 @@ public:
env.close();
}
void
testBadSigningKey()
{
using namespace test::jtx;
testcase("Bad singing key");
Env env(*this);
Account const alice("alice");
env.fund(XRP(10000), alice);
env.close();
auto jtx = env.jt(noop("alice"), ter(temBAD_SIGNATURE));
if (!BEAST_EXPECT(jtx.stx))
return;
auto stx = std::make_shared<STTx>(*jtx.stx);
stx->at(sfSigningPubKey) = makeSlice(std::string("badkey"));
env.app().openLedger().modify([&](OpenView& view, beast::Journal j) {
auto const result =
ripple::apply(env.app(), view, *stx, tapNONE, j);
BEAST_EXPECT(result.ter == temBAD_SIGNATURE);
BEAST_EXPECT(!result.applied);
return result.applied;
});
}
void
run() override
{
@@ -594,6 +622,7 @@ public:
testRequireAuthWithDir();
testTransferRate();
testTicket();
testBadSigningKey();
}
};

View File

@@ -311,11 +311,20 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx)
XRPAmount
Transactor::calculateOwnerReserveFee(ReadView const& view, STTx const& tx)
{
// One reserve increment is typically much greater than one base fee.
// Assumption: One reserve increment is typically much greater than one base
// fee.
// This check is in an assert so that it will come to the attention of
// developers if that assumption is not correct. If the owner reserve is not
// significantly larger than the base fee (or even worse, smaller), we will
// need to rethink charging an owner reserve as a transaction fee.
// TODO: This function is static, and I don't want to add more parameters.
// When it is finally refactored to be in a context that has access to the
// Application, include "app().overlay().networkID() > 2 ||" in the
// condition.
XRPL_ASSERT(
view.fees().increment > view.fees().base * 100,
"ripple::Transactor::calculateOwnerReserveFee : Owner reserve is much "
"greater than base fee");
"ripple::Transactor::calculateOwnerReserveFee : Owner reserve is "
"reasonable");
return view.fees().increment;
}