[WIP] Turns out that adding a new serialized type is harder than it looks

This commit is contained in:
Ed Hennis
2025-04-23 19:53:12 -04:00
parent 939d3d4446
commit a5376c7d76
10 changed files with 129 additions and 46 deletions

View File

@@ -49,6 +49,8 @@ template <int>
class STBitString;
template <class>
class STInteger;
template <class>
class STTypedInteger;
class STNumber;
class STXChainBridge;
class STVector256;
@@ -89,6 +91,8 @@ class STCurrency;
STYPE(STI_ISSUE, 24) \
STYPE(STI_XCHAIN_BRIDGE, 25) \
STYPE(STI_CURRENCY, 26) \
STYPE(STI_TENTHBIPS16, 27) \
STYPE(STI_TENTHBIPS32, 28) \
\
/* high-level types */ \
/* cannot be serialized inside other types */ \
@@ -358,15 +362,12 @@ using SF_UINT256 = TypedField<STBitString<256>>;
using SF_UINT384 = TypedField<STBitString<384>>;
using SF_UINT512 = TypedField<STBitString<512>>;
// These BIPS and TENTHBIPS values are serialized as the underlying type.
// These TENTHBIPS values are serialized as the underlying type.
// The tag is only applied when deserialized.
//
// Basis points (bips) values:
using SF_BIPS16 = TypedField<STInteger<Bips16>>;
using SF_BIPS32 = TypedField<STInteger<Bips32>>;
// Tenth of a basis point values:
using SF_TENTHBIPS16 = TypedField<STInteger<TenthBips16>>;
using SF_TENTHBIPS32 = TypedField<STInteger<TenthBips32>>;
using SF_TENTHBIPS16 = TypedField<STTypedInteger<TenthBips16>>;
using SF_TENTHBIPS32 = TypedField<STTypedInteger<TenthBips32>>;
using SF_ACCOUNT = TypedField<STAccount>;
using SF_AMOUNT = TypedField<STAmount>;
@@ -384,23 +385,17 @@ using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
#undef UNTYPED_SFIELD
#pragma push_macro("TYPED_SFIELD")
#undef TYPED_SFIELD
#pragma push_macro("INTERPRETED_SFIELD")
#undef INTERPRETED_SFIELD
#define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \
extern SField const sfName;
#define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \
extern SF_##stiSuffix const sfName;
#define INTERPRETED_SFIELD(sfName, stiSuffix, fieldValue, stiInterpreted, ...) \
extern SF_##stiInterpreted const sfName;
extern SField const sfInvalid;
extern SField const sfGeneric;
#include <xrpl/protocol/detail/sfields.macro>
#undef INTERPRETED_SFIELD
#pragma pop_macro("INTERPRETED_SFIELD")
#undef TYPED_SFIELD
#pragma pop_macro("TYPED_SFIELD")
#undef UNTYPED_SFIELD

View File

@@ -61,6 +61,26 @@ struct STExchange<STInteger<U>, T>
}
};
template <class U, class T>
struct STExchange<STTypedInteger<U>, T>
{
explicit STExchange() = default;
using value_type = U;
static void
get(std::optional<T>& t, STTypedInteger<U> const& u)
{
t = u.value();
}
static std::unique_ptr<STInteger<U>>
set(SField const& f, T const& t)
{
return std::make_unique<STTypedInteger<U>>(f, t);
}
};
template <>
struct STExchange<STBlob, Slice>
{

View File

@@ -82,12 +82,42 @@ using STUInt16 = STInteger<std::uint16_t>;
using STUInt32 = STInteger<std::uint32_t>;
using STUInt64 = STInteger<std::uint64_t>;
// Basis points (bips) values:
using SFBips16 = STInteger<Bips16>;
using SFBips32 = STInteger<Bips32>;
template <class TypedInteger>
class STTypedInteger : public STInteger<typename TypedInteger::value_type>,
public CountedObject<STTypedInteger<TypedInteger>>
{
public:
using value_type = TypedInteger;
using base_value_type = TypedInteger::value_type;
using base = STInteger<base_value_type>;
public:
using base::add;
using base::getJson;
using base::getSType;
using base::getText;
using base::isDefault;
using base::isEquivalent;
using base::STInteger;
SerializedTypeID
getSType() const override;
STTypedInteger&
operator=(value_type const& v);
value_type
value() const noexcept;
void
setValue(TypedInteger v);
operator TypedInteger() const;
};
// Tenth of a basis point values:
using SFTenthBips16 = STInteger<TenthBips16>;
using SFTenthBips32 = STInteger<TenthBips32>;
using STTenthBips16 = STTypedInteger<TenthBips16>;
using STTenthBips32 = STTypedInteger<TenthBips32>;
template <typename Integer>
inline STInteger<Integer>::STInteger(Integer v) : value_(v)
@@ -169,6 +199,34 @@ inline STInteger<Integer>::operator Integer() const
return value_;
}
template <typename TypedInteger>
inline STTypedInteger<TypedInteger>&
STTypedInteger<TypedInteger>::operator=(value_type const& v)
{
base::setValue(v.value());
return *this;
}
template <typename TypedInteger>
inline typename STTypedInteger<TypedInteger>::value_type
STTypedInteger<TypedInteger>::value() const noexcept
{
return value_type(base::value());
}
template <typename TypedInteger>
inline void
STTypedInteger<TypedInteger>::setValue(TypedInteger v)
{
base::setValue(v.value());
}
template <typename TypedInteger>
inline STTypedInteger<TypedInteger>::operator TypedInteger() const
{
return TypedInteger(base::value());
}
} // namespace ripple
#endif

View File

@@ -301,6 +301,7 @@ public:
}
/** Returns the number of drops */
// TODO: Move this to a new class, maybe with the old "TaggedFee" name
constexpr value_type
fee() const
{

View File

@@ -61,7 +61,9 @@ TYPED_SFIELD(sfHookEmitCount, UINT16, 18)
TYPED_SFIELD(sfHookExecutionIndex, UINT16, 19)
TYPED_SFIELD(sfHookApiVersion, UINT16, 20)
TYPED_SFIELD(sfLedgerFixType, UINT16, 21)
INTERPRETED_SFIELD(sfManagementFeeRate, UINT16, 22, TENTHBIPS16)
// 16-bit integers represented as 1/10 basis points (bips)
TYPED_SFIELD(sfManagementFeeRate, TENTHBIPS16, 1)
// 32-bit integers (common)
TYPED_SFIELD(sfNetworkID, UINT32, 1)
@@ -123,12 +125,15 @@ TYPED_SFIELD(sfPreviousPaymentDate, UINT32, 55)
TYPED_SFIELD(sfNextPaymentDueDate, UINT32, 56)
TYPED_SFIELD(sfPaymentRemaining, UINT32, 57)
TYPED_SFIELD(sfPaymentTotal, UINT32, 58)
INTERPRETED_SFIELD(sfCoverRateMinimum, UINT32, 59, TENTHBIPS32)
INTERPRETED_SFIELD(sfCoverRateLiquidation, UINT32, 60, TENTHBIPS32)
INTERPRETED_SFIELD(sfInterestRate, UINT32, 61, TENTHBIPS32)
INTERPRETED_SFIELD(sfLateInterestRate, UINT32, 62, TENTHBIPS32)
INTERPRETED_SFIELD(sfCloseInterestRate, UINT32, 63, TENTHBIPS32)
INTERPRETED_SFIELD(sfOverpaymentInterestRate, UINT32, 64, TENTHBIPS32)
// 32-bit integers represented as 1/10 basis points (bips)
TYPED_SFIELD(sfCoverRateMinimum, TENTHBIPS32, 1)
TYPED_SFIELD(sfCoverRateLiquidation, TENTHBIPS32, 2)
TYPED_SFIELD(sfInterestRate, TENTHBIPS32, 3)
TYPED_SFIELD(sfLateInterestRate, TENTHBIPS32, 4)
TYPED_SFIELD(sfCloseInterestRate, TENTHBIPS32, 5)
TYPED_SFIELD(sfOverpaymentInterestRate, TENTHBIPS32, 6)
// 64-bit integers (common)
TYPED_SFIELD(sfIndexNext, UINT64, 1)

View File

@@ -54,8 +54,6 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
#undef UNTYPED_SFIELD
#pragma push_macro("TYPED_SFIELD")
#undef TYPED_SFIELD
#pragma push_macro("INTERPRETED_SFIELD")
#undef INTERPRETED_SFIELD
#define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \
SField const sfName( \
@@ -71,13 +69,6 @@ TypedField<T>::TypedField(private_access_tag_t pat, Args&&... args)
fieldValue, \
std::string_view(#sfName).substr(2).data(), \
##__VA_ARGS__);
#define INTERPRETED_SFIELD(sfName, stiSuffix, fieldValue, stiInterpreted, ...) \
SF_##stiInterpreted const sfName( \
access, \
STI_##stiSuffix, \
fieldValue, \
std::string_view(#sfName).substr(2).data(), \
##__VA_ARGS__);
// SFields which, for historical reasons, do not follow naming conventions.
SField const sfInvalid(access, -1);
@@ -89,8 +80,6 @@ SField const sfIndex(access, STI_UINT256, 258, "index");
#include <xrpl/protocol/detail/sfields.macro>
#undef INTERPRETED_SFIELD
#pragma pop_macro("INTERPRETED_SFIELD")
#undef TYPED_SFIELD
#pragma pop_macro("TYPED_SFIELD")
#undef UNTYPED_SFIELD

View File

@@ -229,4 +229,18 @@ STUInt64::getJson(JsonOptions) const
return convertToString(value_, 16); // Convert to base 16
}
template <>
SerializedTypeID
STTenthBips16::getSType() const override
{
return STI_TENTHBIPS16;
}
template <>
SerializedTypeID
STTenthBips32::getSType() const override
{
return STI_TENTHBIPS32;
}
} // namespace ripple

View File

@@ -190,6 +190,12 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
case STI_UINT64:
construct<STUInt64>(std::forward<Args>(args)...);
return;
case STI_TENTHBIPS16:
construct<STTenthBips16>(std::forward<Args>(args)...);
return;
case STI_TENTHBIPS32:
construct<STTenthBips32>(std::forward<Args>(args)...);
return;
case STI_AMOUNT:
construct<STAmount>(std::forward<Args>(args)...);
return;

View File

@@ -174,20 +174,15 @@ struct valueUnitField
using OV = ValueType;
using base = JTxField<SF, SV, OV>;
static_assert(std::is_same_v<SV, typename SField::type::value_type>);
static_assert(
std::is_same_v<SV, typename SField::type::value_type::value_type>);
static_assert(std::is_same_v<
OV,
typename SField::type::value_type::value_type::value_type>);
std::is_same_v<OV, typename SField::type::value_type::value_type>);
protected:
using base::value_;
public:
explicit valueUnitField(SF const& sfield, SV const& value)
: JTxField(sfield, value)
{
}
using base::JTxField;
OV
value() const override
@@ -675,7 +670,7 @@ auto const coverRateMinimum =
valueUnitWrapper<SF_TENTHBIPS32>(sfCoverRateMinimum);
auto const coverRateLiquidation =
simpleField<SF_TENTHBIPS32>(sfCoverRateLiquidation);
valueUnitWrapper<SF_TENTHBIPS32>(sfCoverRateLiquidation);
} // namespace loanBroker

View File

@@ -193,8 +193,8 @@ LoanBrokerSet::doApply()
return tecINSUFFICIENT_RESERVE;
*/
auto maybePseudo = createPseudoAccount(
view, broker->key(), PseudoAccountOwnerType::LoanBroker);
auto maybePseudo =
createPseudoAccount(view, broker->key(), sfLoanBrokerID);
if (!maybePseudo)
return maybePseudo.error();
auto& pseudo = *maybePseudo;