[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; class STBitString;
template <class> template <class>
class STInteger; class STInteger;
template <class>
class STTypedInteger;
class STNumber; class STNumber;
class STXChainBridge; class STXChainBridge;
class STVector256; class STVector256;
@@ -89,6 +91,8 @@ class STCurrency;
STYPE(STI_ISSUE, 24) \ STYPE(STI_ISSUE, 24) \
STYPE(STI_XCHAIN_BRIDGE, 25) \ STYPE(STI_XCHAIN_BRIDGE, 25) \
STYPE(STI_CURRENCY, 26) \ STYPE(STI_CURRENCY, 26) \
STYPE(STI_TENTHBIPS16, 27) \
STYPE(STI_TENTHBIPS32, 28) \
\ \
/* high-level types */ \ /* high-level types */ \
/* cannot be serialized inside other 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_UINT384 = TypedField<STBitString<384>>;
using SF_UINT512 = TypedField<STBitString<512>>; 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. // 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: // Tenth of a basis point values:
using SF_TENTHBIPS16 = TypedField<STInteger<TenthBips16>>; using SF_TENTHBIPS16 = TypedField<STTypedInteger<TenthBips16>>;
using SF_TENTHBIPS32 = TypedField<STInteger<TenthBips32>>; using SF_TENTHBIPS32 = TypedField<STTypedInteger<TenthBips32>>;
using SF_ACCOUNT = TypedField<STAccount>; using SF_ACCOUNT = TypedField<STAccount>;
using SF_AMOUNT = TypedField<STAmount>; using SF_AMOUNT = TypedField<STAmount>;
@@ -384,23 +385,17 @@ using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
#undef UNTYPED_SFIELD #undef UNTYPED_SFIELD
#pragma push_macro("TYPED_SFIELD") #pragma push_macro("TYPED_SFIELD")
#undef TYPED_SFIELD #undef TYPED_SFIELD
#pragma push_macro("INTERPRETED_SFIELD")
#undef INTERPRETED_SFIELD
#define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \ #define UNTYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \
extern SField const sfName; extern SField const sfName;
#define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \ #define TYPED_SFIELD(sfName, stiSuffix, fieldValue, ...) \
extern SF_##stiSuffix const sfName; extern SF_##stiSuffix const sfName;
#define INTERPRETED_SFIELD(sfName, stiSuffix, fieldValue, stiInterpreted, ...) \
extern SF_##stiInterpreted const sfName;
extern SField const sfInvalid; extern SField const sfInvalid;
extern SField const sfGeneric; extern SField const sfGeneric;
#include <xrpl/protocol/detail/sfields.macro> #include <xrpl/protocol/detail/sfields.macro>
#undef INTERPRETED_SFIELD
#pragma pop_macro("INTERPRETED_SFIELD")
#undef TYPED_SFIELD #undef TYPED_SFIELD
#pragma pop_macro("TYPED_SFIELD") #pragma pop_macro("TYPED_SFIELD")
#undef UNTYPED_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 <> template <>
struct STExchange<STBlob, Slice> struct STExchange<STBlob, Slice>
{ {

View File

@@ -82,12 +82,42 @@ using STUInt16 = STInteger<std::uint16_t>;
using STUInt32 = STInteger<std::uint32_t>; using STUInt32 = STInteger<std::uint32_t>;
using STUInt64 = STInteger<std::uint64_t>; using STUInt64 = STInteger<std::uint64_t>;
// Basis points (bips) values: template <class TypedInteger>
using SFBips16 = STInteger<Bips16>; class STTypedInteger : public STInteger<typename TypedInteger::value_type>,
using SFBips32 = STInteger<Bips32>; 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: // Tenth of a basis point values:
using SFTenthBips16 = STInteger<TenthBips16>; using STTenthBips16 = STTypedInteger<TenthBips16>;
using SFTenthBips32 = STInteger<TenthBips32>; using STTenthBips32 = STTypedInteger<TenthBips32>;
template <typename Integer> template <typename Integer>
inline STInteger<Integer>::STInteger(Integer v) : value_(v) inline STInteger<Integer>::STInteger(Integer v) : value_(v)
@@ -169,6 +199,34 @@ inline STInteger<Integer>::operator Integer() const
return value_; 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 } // namespace ripple
#endif #endif

View File

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

View File

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

View File

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

View File

@@ -229,4 +229,18 @@ STUInt64::getJson(JsonOptions) const
return convertToString(value_, 16); // Convert to base 16 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 } // namespace ripple

View File

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

View File

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

View File

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