diff --git a/include/xrpl/protocol/SField.h b/include/xrpl/protocol/SField.h index a86c8e9640..035735c513 100644 --- a/include/xrpl/protocol/SField.h +++ b/include/xrpl/protocol/SField.h @@ -49,13 +49,14 @@ template class STBitString; template class STInteger; -template -class STTypedInteger; class STNumber; class STXChainBridge; class STVector256; class STCurrency; +template +concept HasValue = requires(Integer i) { i.value(); }; + #pragma push_macro("XMACRO") #undef XMACRO @@ -366,8 +367,8 @@ using SF_UINT512 = TypedField>; // The tag is only applied when deserialized. // // 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; diff --git a/include/xrpl/protocol/STExchange.h b/include/xrpl/protocol/STExchange.h index 9ecb56376c..dba8bf3efb 100644 --- a/include/xrpl/protocol/STExchange.h +++ b/include/xrpl/protocol/STExchange.h @@ -62,22 +62,22 @@ struct STExchange, T> }; template -struct STExchange, T> +struct STExchange>, T> { explicit STExchange() = default; - using value_type = U; + using value_type = TenthBips; static void - get(std::optional& t, STTypedInteger const& u) + get(std::optional& t, STInteger const& u) { t = u.value(); } - static std::unique_ptr> + static std::unique_ptr> set(SField const& f, T const& t) { - return std::make_unique>(f, t); + return std::make_unique>(f, t); } }; diff --git a/include/xrpl/protocol/STInteger.h b/include/xrpl/protocol/STInteger.h index d914566f56..c04131d4a5 100644 --- a/include/xrpl/protocol/STInteger.h +++ b/include/xrpl/protocol/STInteger.h @@ -37,7 +37,7 @@ private: public: explicit STInteger(Integer v); - STInteger(SField const& n, Integer v = 0); + STInteger(SField const& n, Integer v = {}); STInteger(SerialIter& sit, SField const& name); SerializedTypeID @@ -82,42 +82,9 @@ using STUInt16 = STInteger; using STUInt32 = STInteger; using STUInt64 = 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 STTenthBips16 = STTypedInteger; -using STTenthBips32 = STTypedInteger; +using STTenthBips16 = STInteger; +using STTenthBips32 = STInteger; template inline STInteger::STInteger(Integer v) : value_(v) @@ -199,34 +166,6 @@ 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/Serializer.h b/include/xrpl/protocol/Serializer.h index 5724a19f57..ec0fd75af1 100644 --- a/include/xrpl/protocol/Serializer.h +++ b/include/xrpl/protocol/Serializer.h @@ -125,8 +125,17 @@ public: } template + requires(!HasValue) int addInteger(Integer); + template + requires HasValue + int + addInteger(Integer i) + { + return addInteger(i.value()); + } + template int addBitString(base_uint const& v) diff --git a/include/xrpl/protocol/Units.h b/include/xrpl/protocol/Units.h index 20051b0c63..1788456edb 100644 --- a/include/xrpl/protocol/Units.h +++ b/include/xrpl/protocol/Units.h @@ -592,6 +592,17 @@ safe_cast(Src s) noexcept return Dest{safe_cast(s.value())}; } +template +constexpr std::enable_if_t< + std::is_integral_v && + std::is_integral_v, + Dest> +safe_cast(Src s) noexcept +{ + // Dest may not have an explicit value constructor + return Dest{safe_cast(s)}; +} + template constexpr std::enable_if_t< std::is_same_v && @@ -604,6 +615,17 @@ unsafe_cast(Src s) noexcept return Dest{unsafe_cast(s.value())}; } +template +constexpr std::enable_if_t< + std::is_integral_v && + std::is_integral_v, + Dest> +unsafe_cast(Src s) noexcept +{ + // Dest may not have an explicit value constructor + return Dest{unsafe_cast(s)}; +} + } // namespace ripple #endif // PROTOCOL_UNITS_H_INCLUDED diff --git a/src/libxrpl/protocol/STInteger.cpp b/src/libxrpl/protocol/STInteger.cpp index 346702925b..d48c0f8f53 100644 --- a/src/libxrpl/protocol/STInteger.cpp +++ b/src/libxrpl/protocol/STInteger.cpp @@ -229,18 +229,62 @@ STUInt64::getJson(JsonOptions) const return convertToString(value_, 16); // Convert to base 16 } +//------------------------------------------------------------------------------ + +template <> +STTenthBips16::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, TenthBips16(sit.get16())) +{ +} + template <> SerializedTypeID -STTenthBips16::getSType() const override +STTenthBips16::getSType() const { return STI_TENTHBIPS16; } +template <> +std::string +STTenthBips16::getText() const +{ + return std::to_string(value_.value()); +} + +template <> +Json::Value +STTenthBips16::getJson(JsonOptions) const +{ + return value_.value(); +} + +//------------------------------------------------------------------------------ + +template <> +STTenthBips32::STInteger(SerialIter& sit, SField const& name) + : STInteger(name, TenthBips32(sit.get32())) +{ +} + template <> SerializedTypeID -STTenthBips32::getSType() const override +STTenthBips32::getSType() const { return STI_TENTHBIPS32; } +template <> +std::string +STTenthBips32::getText() const +{ + return std::to_string(value_.value()); +} + +template <> +Json::Value +STTenthBips32::getJson(JsonOptions) const +{ + return value_.value(); +} + } // namespace ripple diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index e11448ccda..4a9eaf82ab 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -82,6 +82,31 @@ constexpr std:: return static_cast(value); } +template + requires HasValue +constexpr std::enable_if_t< + std::is_unsigned::value && std::is_signed::value, + U> +to_unsigned(S value) +{ + if (value < 0 || std::numeric_limits::max() < value) + Throw("Value out of range"); + return safe_cast(static_cast(value)); +} + +template + requires HasValue +constexpr std::enable_if_t< + std::is_unsigned::value && + std::is_unsigned::value, + U1> +to_unsigned(U2 value) +{ + if (std::numeric_limits::max() < value) + Throw("Value out of range"); + return safe_cast(static_cast(value)); +} + static std::string make_name(std::string const& object, std::string const& field) { @@ -201,6 +226,119 @@ non_object_in_array(std::string const& item, Json::UInt index) " is not an object. Arrays may only contain objects."); } +template +static std::optional +parseUnsigned( + SField const& field, + std::string const& json_name, + std::string const& fieldName, + SField const* name, + Json::Value const& value, + Json::Value& error) +{ + std::optional ret; + + try + { + if (value.isString()) + { + ret = detail::make_stvar( + field, + safe_cast( + beast::lexicalCastThrow(value.asString()))); + } + else if (value.isInt()) + { + ret = detail::make_stvar( + field, + to_unsigned(value.asInt())); + } + else if (value.isUInt()) + { + ret = detail::make_stvar( + field, + to_unsigned(value.asUInt())); + } + else + { + error = bad_type(json_name, fieldName); + return ret; + } + } + catch (std::exception const&) + { + error = invalid_data(json_name, fieldName); + return ret; + } + + return ret; +} + +template +static std::optional +parseUint16( + SField const& field, + std::string const& json_name, + std::string const& fieldName, + SField const* name, + Json::Value const& value, + Json::Value& error) +{ + std::optional ret; + + try + { + if (value.isString()) + { + std::string const strValue = value.asString(); + + if (!strValue.empty() && + ((strValue[0] < '0') || (strValue[0] > '9'))) + { + if (field == sfTransactionType) + { + ret = detail::make_stvar( + field, + safe_cast( + static_cast( + TxFormats::getInstance().findTypeByName( + strValue)))); + + if (*name == sfGeneric) + name = &sfTransaction; + } + else if (field == sfLedgerEntryType) + { + ret = detail::make_stvar( + field, + safe_cast( + static_cast( + LedgerFormats::getInstance().findTypeByName( + strValue)))); + + if (*name == sfGeneric) + name = &sfLedgerEntry; + } + else + { + error = invalid_data(json_name, fieldName); + return ret; + } + } + } + if (!ret) + return parseUnsigned( + field, json_name, fieldName, name, value, error); + } + catch (std::exception const&) + { + error = invalid_data(json_name, fieldName); + return ret; + } + + return ret; +} + // This function is used by parseObject to parse any JSON type that doesn't // recurse. Everything represented here is a leaf-type. static std::optional @@ -301,105 +439,34 @@ parseLeaf( break; case STI_UINT16: - try - { - if (value.isString()) - { - std::string const strValue = value.asString(); - - if (!strValue.empty() && - ((strValue[0] < '0') || (strValue[0] > '9'))) - { - if (field == sfTransactionType) - { - ret = detail::make_stvar( - field, - static_cast( - TxFormats::getInstance().findTypeByName( - strValue))); - - if (*name == sfGeneric) - name = &sfTransaction; - } - else if (field == sfLedgerEntryType) - { - ret = detail::make_stvar( - field, - static_cast( - LedgerFormats::getInstance().findTypeByName( - strValue))); - - if (*name == sfGeneric) - name = &sfLedgerEntry; - } - else - { - error = invalid_data(json_name, fieldName); - return ret; - } - } - else - { - ret = detail::make_stvar( - field, - beast::lexicalCastThrow(strValue)); - } - } - else if (value.isInt()) - { - ret = detail::make_stvar( - field, to_unsigned(value.asInt())); - } - else if (value.isUInt()) - { - ret = detail::make_stvar( - field, to_unsigned(value.asUInt())); - } - else - { - error = bad_type(json_name, fieldName); - return ret; - } - } - catch (std::exception const&) - { - error = invalid_data(json_name, fieldName); + ret = parseUint16( + field, json_name, fieldName, name, value, error); + if (!ret) + return ret; + + break; + + case STI_TENTHBIPS16: + ret = parseUnsigned( + field, json_name, fieldName, name, value, error); + if (!ret) return ret; - } break; case STI_UINT32: - try - { - if (value.isString()) - { - ret = detail::make_stvar( - field, - beast::lexicalCastThrow( - value.asString())); - } - else if (value.isInt()) - { - ret = detail::make_stvar( - field, to_unsigned(value.asInt())); - } - else if (value.isUInt()) - { - ret = detail::make_stvar( - field, safe_cast(value.asUInt())); - } - else - { - error = bad_type(json_name, fieldName); - return ret; - } - } - catch (std::exception const&) - { - error = invalid_data(json_name, fieldName); + ret = parseUnsigned( + field, json_name, fieldName, name, value, error); + if (!ret) + return ret; + + break; + + case STI_TENTHBIPS32: + ret = parseUnsigned( + field, json_name, fieldName, name, value, error); + if (!ret) return ret; - } break; diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 943065ac41..f27f0c2915 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -805,7 +805,7 @@ class Simulate_test : public beast::unit_test::suite testTx(env, tx, testSimulation); tx[sfSigningPubKey] = ""; - tx[sfTxnSignature] = "x"; + tx[sfTxnSignature] = ""; tx[sfSequence] = env.seq(env.master); tx[sfFee] = env.current()->fees().base.jsonClipped().asString();