From a5376c7d761b73a18f511d2d145226241193b28f Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Wed, 23 Apr 2025 19:53:12 -0400 Subject: [PATCH] [WIP] Turns out that adding a new serialized type is harder than it looks --- include/xrpl/protocol/SField.h | 19 +++--- include/xrpl/protocol/STExchange.h | 20 +++++++ include/xrpl/protocol/STInteger.h | 68 ++++++++++++++++++++-- include/xrpl/protocol/Units.h | 1 + include/xrpl/protocol/detail/sfields.macro | 19 +++--- src/libxrpl/protocol/SField.cpp | 11 ---- src/libxrpl/protocol/STInteger.cpp | 14 +++++ src/libxrpl/protocol/STVar.cpp | 6 ++ src/test/jtx/TestHelpers.h | 13 ++--- src/xrpld/app/tx/detail/LoanBrokerSet.cpp | 4 +- 10 files changed, 129 insertions(+), 46 deletions(-) diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index 88b4086713..a86c8e9640 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -49,6 +49,8 @@ template class STBitString; template class STInteger; +template +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>; using SF_UINT384 = TypedField>; using SF_UINT512 = TypedField>; -// 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>; -using SF_BIPS32 = TypedField>; // Tenth of a basis point values: -using SF_TENTHBIPS16 = TypedField>; -using SF_TENTHBIPS32 = TypedField>; +using SF_TENTHBIPS16 = TypedField>; +using SF_TENTHBIPS32 = TypedField>; using SF_ACCOUNT = TypedField; using SF_AMOUNT = TypedField; @@ -384,23 +385,17 @@ using SF_XCHAIN_BRIDGE = TypedField; #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 -#undef INTERPRETED_SFIELD -#pragma pop_macro("INTERPRETED_SFIELD") #undef TYPED_SFIELD #pragma pop_macro("TYPED_SFIELD") #undef UNTYPED_SFIELD diff --git a/include/xrpl/protocol/STExchange.h b/include/xrpl/protocol/STExchange.h index dd7c4834a6..9ecb56376c 100644 --- a/include/xrpl/protocol/STExchange.h +++ b/include/xrpl/protocol/STExchange.h @@ -61,6 +61,26 @@ struct STExchange, T> } }; +template +struct STExchange, T> +{ + explicit STExchange() = default; + + using value_type = U; + + static void + get(std::optional& t, STTypedInteger const& u) + { + t = u.value(); + } + + static std::unique_ptr> + set(SField const& f, T const& t) + { + return std::make_unique>(f, t); + } +}; + template <> struct STExchange { diff --git a/include/xrpl/protocol/STInteger.h b/include/xrpl/protocol/STInteger.h index f89f803533..d914566f56 100644 --- a/include/xrpl/protocol/STInteger.h +++ b/include/xrpl/protocol/STInteger.h @@ -82,12 +82,42 @@ using STUInt16 = STInteger; using STUInt32 = STInteger; using STUInt64 = STInteger; -// Basis points (bips) values: -using SFBips16 = STInteger; -using SFBips32 = STInteger; +template +class STTypedInteger : public STInteger, + public CountedObject> +{ +public: + using value_type = TypedInteger; + using base_value_type = TypedInteger::value_type; + using base = STInteger; + +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; -using SFTenthBips32 = STInteger; +using STTenthBips16 = STTypedInteger; +using STTenthBips32 = STTypedInteger; template inline STInteger::STInteger(Integer v) : value_(v) @@ -169,6 +199,34 @@ inline STInteger::operator Integer() const return value_; } +template +inline STTypedInteger& +STTypedInteger::operator=(value_type const& v) +{ + base::setValue(v.value()); + return *this; +} + +template +inline typename STTypedInteger::value_type +STTypedInteger::value() const noexcept +{ + return value_type(base::value()); +} + +template +inline void +STTypedInteger::setValue(TypedInteger v) +{ + base::setValue(v.value()); +} + +template +inline STTypedInteger::operator TypedInteger() const +{ + return TypedInteger(base::value()); +} + } // namespace ripple #endif diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index 0e2aee96f3..20051b0c63 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -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 { diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 3535630374..41269e6d44 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -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) diff --git a/src/libxrpl/protocol/SField.cpp b/src/libxrpl/protocol/SField.cpp index a43ff8bdba..8846682ba2 100644 --- a/src/libxrpl/protocol/SField.cpp +++ b/src/libxrpl/protocol/SField.cpp @@ -54,8 +54,6 @@ TypedField::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::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 -#undef INTERPRETED_SFIELD -#pragma pop_macro("INTERPRETED_SFIELD") #undef TYPED_SFIELD #pragma pop_macro("TYPED_SFIELD") #undef UNTYPED_SFIELD diff --git a/src/libxrpl/protocol/STInteger.cpp b/src/libxrpl/protocol/STInteger.cpp index bc5b7e855e..346702925b 100644 --- a/src/libxrpl/protocol/STInteger.cpp +++ b/src/libxrpl/protocol/STInteger.cpp @@ -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 diff --git a/src/libxrpl/protocol/STVar.cpp b/src/libxrpl/protocol/STVar.cpp index 24954c4add..744ccbc07f 100644 --- a/src/libxrpl/protocol/STVar.cpp +++ b/src/libxrpl/protocol/STVar.cpp @@ -190,6 +190,12 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args) case STI_UINT64: construct(std::forward(args)...); return; + case STI_TENTHBIPS16: + construct(std::forward(args)...); + return; + case STI_TENTHBIPS32: + construct(std::forward(args)...); + return; case STI_AMOUNT: construct(std::forward(args)...); return; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index 5b3d2fb593..c26ba461ed 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -174,20 +174,15 @@ struct valueUnitField using OV = ValueType; using base = JTxField; + static_assert(std::is_same_v); static_assert( - std::is_same_v); - static_assert(std::is_same_v< - OV, - typename SField::type::value_type::value_type::value_type>); + std::is_same_v); 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(sfCoverRateMinimum); auto const coverRateLiquidation = - simpleField(sfCoverRateLiquidation); + valueUnitWrapper(sfCoverRateLiquidation); } // namespace loanBroker diff --git a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp index 49dd923672..01772b6e4b 100644 --- a/src/xrpld/app/tx/detail/LoanBrokerSet.cpp +++ b/src/xrpld/app/tx/detail/LoanBrokerSet.cpp @@ -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;