mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Add a new serialized type: STNumber (#5121)
`STNumber` lets objects and transactions contain multiple fields for quantities of XRP, IOU, or MPT without duplicating information about the "issue" (represented by `STIssue`). It is a straightforward serialization of the `Number` type that uniformly represents those quantities. --------- Co-authored-by: John Freeman <jfreeman08@gmail.com> Co-authored-by: Howard Hinnant <howard.hinnant@gmail.com>
This commit is contained in:
@@ -41,6 +41,14 @@ class Number
|
|||||||
int exponent_{std::numeric_limits<int>::lowest()};
|
int exponent_{std::numeric_limits<int>::lowest()};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// The range for the mantissa when normalized
|
||||||
|
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
|
||||||
|
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
|
||||||
|
|
||||||
|
// The range for the exponent when normalized
|
||||||
|
constexpr static int minExponent = -32768;
|
||||||
|
constexpr static int maxExponent = 32768;
|
||||||
|
|
||||||
struct unchecked
|
struct unchecked
|
||||||
{
|
{
|
||||||
explicit unchecked() = default;
|
explicit unchecked() = default;
|
||||||
@@ -191,14 +199,6 @@ private:
|
|||||||
constexpr bool
|
constexpr bool
|
||||||
isnormal() const noexcept;
|
isnormal() const noexcept;
|
||||||
|
|
||||||
// The range for the mantissa when normalized
|
|
||||||
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
|
|
||||||
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
|
|
||||||
|
|
||||||
// The range for the exponent when normalized
|
|
||||||
constexpr static int minExponent = -32768;
|
|
||||||
constexpr static int maxExponent = 32768;
|
|
||||||
|
|
||||||
class Guard;
|
class Guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ template <int>
|
|||||||
class STBitString;
|
class STBitString;
|
||||||
template <class>
|
template <class>
|
||||||
class STInteger;
|
class STInteger;
|
||||||
|
class STNumber;
|
||||||
class STXChainBridge;
|
class STXChainBridge;
|
||||||
class STVector256;
|
class STVector256;
|
||||||
class STCurrency;
|
class STCurrency;
|
||||||
@@ -70,8 +71,9 @@ class STCurrency;
|
|||||||
STYPE(STI_AMOUNT, 6) \
|
STYPE(STI_AMOUNT, 6) \
|
||||||
STYPE(STI_VL, 7) \
|
STYPE(STI_VL, 7) \
|
||||||
STYPE(STI_ACCOUNT, 8) \
|
STYPE(STI_ACCOUNT, 8) \
|
||||||
|
STYPE(STI_NUMBER, 9) \
|
||||||
\
|
\
|
||||||
/* 9-13 are reserved */ \
|
/* 10-13 are reserved */ \
|
||||||
STYPE(STI_OBJECT, 14) \
|
STYPE(STI_OBJECT, 14) \
|
||||||
STYPE(STI_ARRAY, 15) \
|
STYPE(STI_ARRAY, 15) \
|
||||||
\
|
\
|
||||||
@@ -355,6 +357,7 @@ using SF_ACCOUNT = TypedField<STAccount>;
|
|||||||
using SF_AMOUNT = TypedField<STAmount>;
|
using SF_AMOUNT = TypedField<STAmount>;
|
||||||
using SF_ISSUE = TypedField<STIssue>;
|
using SF_ISSUE = TypedField<STIssue>;
|
||||||
using SF_CURRENCY = TypedField<STCurrency>;
|
using SF_CURRENCY = TypedField<STCurrency>;
|
||||||
|
using SF_NUMBER = TypedField<STNumber>;
|
||||||
using SF_VL = TypedField<STBlob>;
|
using SF_VL = TypedField<STBlob>;
|
||||||
using SF_VECTOR256 = TypedField<STVector256>;
|
using SF_VECTOR256 = TypedField<STVector256>;
|
||||||
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
|
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;
|
||||||
|
|||||||
88
include/xrpl/protocol/STNumber.h
Normal file
88
include/xrpl/protocol/STNumber.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2024 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.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef XRPL_PROTOCOL_STNUMBER_H_INCLUDED
|
||||||
|
#define XRPL_PROTOCOL_STNUMBER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <xrpl/basics/CountedObject.h>
|
||||||
|
#include <xrpl/basics/Number.h>
|
||||||
|
#include <xrpl/protocol/STBase.h>
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A serializable number.
|
||||||
|
*
|
||||||
|
* This type is-a `Number`, and can be used everywhere that is accepted.
|
||||||
|
* This type simply integrates `Number` with the serialization framework,
|
||||||
|
* letting it be used for fields in ledger entries and transactions.
|
||||||
|
* It is effectively an `STAmount` sans `Asset`:
|
||||||
|
* it can represent a value of any token type (XRP, IOU, or MPT)
|
||||||
|
* without paying the storage cost of duplicating asset information
|
||||||
|
* that may be deduced from the context.
|
||||||
|
*/
|
||||||
|
class STNumber : public STBase, public CountedObject<STNumber>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Number value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = Number;
|
||||||
|
|
||||||
|
STNumber() = default;
|
||||||
|
explicit STNumber(SField const& field, Number const& value = Number());
|
||||||
|
STNumber(SerialIter& sit, SField const& field);
|
||||||
|
|
||||||
|
SerializedTypeID
|
||||||
|
getSType() const override;
|
||||||
|
std::string
|
||||||
|
getText() const override;
|
||||||
|
void
|
||||||
|
add(Serializer& s) const override;
|
||||||
|
|
||||||
|
Number const&
|
||||||
|
value() const;
|
||||||
|
void
|
||||||
|
setValue(Number const& v);
|
||||||
|
|
||||||
|
bool
|
||||||
|
isEquivalent(STBase const& t) const override;
|
||||||
|
bool
|
||||||
|
isDefault() const override;
|
||||||
|
|
||||||
|
operator Number() const
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
STBase*
|
||||||
|
copy(std::size_t n, void* buf) const override;
|
||||||
|
STBase*
|
||||||
|
move(std::size_t n, void* buf) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& out, STNumber const& rhs);
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -245,6 +245,8 @@ public:
|
|||||||
getFieldArray(SField const& field) const;
|
getFieldArray(SField const& field) const;
|
||||||
const STCurrency&
|
const STCurrency&
|
||||||
getFieldCurrency(SField const& field) const;
|
getFieldCurrency(SField const& field) const;
|
||||||
|
STNumber const&
|
||||||
|
getFieldNumber(SField const& field) const;
|
||||||
|
|
||||||
/** Get the value of a field.
|
/** Get the value of a field.
|
||||||
@param A TypedField built from an SField value representing the desired
|
@param A TypedField built from an SField value representing the desired
|
||||||
@@ -376,6 +378,8 @@ public:
|
|||||||
void
|
void
|
||||||
setFieldCurrency(SField const& field, STCurrency const&);
|
setFieldCurrency(SField const& field, STCurrency const&);
|
||||||
void
|
void
|
||||||
|
setFieldNumber(SField const& field, STNumber const&);
|
||||||
|
void
|
||||||
setFieldPathSet(SField const& field, STPathSet const&);
|
setFieldPathSet(SField const& field, STPathSet const&);
|
||||||
void
|
void
|
||||||
setFieldV256(SField const& field, STVector256 const& v);
|
setFieldV256(SField const& field, STVector256 const& v);
|
||||||
|
|||||||
@@ -83,12 +83,43 @@ public:
|
|||||||
add8(unsigned char i);
|
add8(unsigned char i);
|
||||||
int
|
int
|
||||||
add16(std::uint16_t i);
|
add16(std::uint16_t i);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(std::is_same_v<
|
||||||
|
std::make_unsigned_t<std::remove_cv_t<T>>,
|
||||||
|
std::uint32_t>)
|
||||||
int
|
int
|
||||||
add32(std::uint32_t i); // ledger indexes, account sequence, timestamps
|
add32(T i)
|
||||||
|
{
|
||||||
|
int ret = mData.size();
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 24) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>(i & 0xff));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
add32(HashPrefix p);
|
add32(HashPrefix p);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(std::is_same_v<
|
||||||
|
std::make_unsigned_t<std::remove_cv_t<T>>,
|
||||||
|
std::uint64_t>)
|
||||||
int
|
int
|
||||||
add64(std::uint64_t i); // native currency amounts
|
add64(T i)
|
||||||
|
{
|
||||||
|
int ret = mData.size();
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 56) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 48) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 40) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 32) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 24) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
|
||||||
|
mData.push_back(static_cast<unsigned char>(i & 0xff));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Integer>
|
template <typename Integer>
|
||||||
int addInteger(Integer);
|
int addInteger(Integer);
|
||||||
@@ -353,9 +384,13 @@ public:
|
|||||||
|
|
||||||
std::uint32_t
|
std::uint32_t
|
||||||
get32();
|
get32();
|
||||||
|
std::int32_t
|
||||||
|
geti32();
|
||||||
|
|
||||||
std::uint64_t
|
std::uint64_t
|
||||||
get64();
|
get64();
|
||||||
|
std::int64_t
|
||||||
|
geti64();
|
||||||
|
|
||||||
template <std::size_t Bits, class Tag = void>
|
template <std::size_t Bits, class Tag = void>
|
||||||
base_uint<Bits, Tag>
|
base_uint<Bits, Tag>
|
||||||
|
|||||||
@@ -191,6 +191,9 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
|||||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||||
|
|
||||||
|
// number (common)
|
||||||
|
TYPED_SFIELD(sfNumber, NUMBER, 1)
|
||||||
|
|
||||||
// currency amount (common)
|
// currency amount (common)
|
||||||
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
||||||
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <xrpl/protocol/STBlob.h>
|
#include <xrpl/protocol/STBlob.h>
|
||||||
#include <xrpl/protocol/STInteger.h>
|
#include <xrpl/protocol/STInteger.h>
|
||||||
#include <xrpl/protocol/STLedgerEntry.h>
|
#include <xrpl/protocol/STLedgerEntry.h>
|
||||||
|
#include <xrpl/protocol/STNumber.h>
|
||||||
#include <xrpl/protocol/STObject.h>
|
#include <xrpl/protocol/STObject.h>
|
||||||
#include <xrpl/protocol/STParsedJSON.h>
|
#include <xrpl/protocol/STParsedJSON.h>
|
||||||
#include <xrpl/protocol/STPathSet.h>
|
#include <xrpl/protocol/STPathSet.h>
|
||||||
|
|||||||
105
src/libxrpl/protocol/STNumber.cpp
Normal file
105
src/libxrpl/protocol/STNumber.cpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2023 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/protocol/STNumber.h>
|
||||||
|
|
||||||
|
#include <xrpl/protocol/SField.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
STNumber::STNumber(SField const& field, Number const& value)
|
||||||
|
: STBase(field), value_(value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
STNumber::STNumber(SerialIter& sit, SField const& field) : STBase(field)
|
||||||
|
{
|
||||||
|
// We must call these methods in separate statements
|
||||||
|
// to guarantee their order of execution.
|
||||||
|
auto mantissa = sit.geti64();
|
||||||
|
auto exponent = sit.geti32();
|
||||||
|
value_ = Number{mantissa, exponent};
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedTypeID
|
||||||
|
STNumber::getSType() const
|
||||||
|
{
|
||||||
|
return STI_NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
STNumber::getText() const
|
||||||
|
{
|
||||||
|
return to_string(value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
STNumber::add(Serializer& s) const
|
||||||
|
{
|
||||||
|
assert(getFName().isBinary());
|
||||||
|
assert(getFName().fieldType == getSType());
|
||||||
|
s.add64(value_.mantissa());
|
||||||
|
s.add32(value_.exponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
Number const&
|
||||||
|
STNumber::value() const
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
STNumber::setValue(Number const& v)
|
||||||
|
{
|
||||||
|
value_ = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
STBase*
|
||||||
|
STNumber::copy(std::size_t n, void* buf) const
|
||||||
|
{
|
||||||
|
return emplace(n, buf, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
STBase*
|
||||||
|
STNumber::move(std::size_t n, void* buf)
|
||||||
|
{
|
||||||
|
return emplace(n, buf, std::move(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
STNumber::isEquivalent(STBase const& t) const
|
||||||
|
{
|
||||||
|
assert(t.getSType() == this->getSType());
|
||||||
|
STNumber const& v = dynamic_cast<STNumber const&>(t);
|
||||||
|
return value_ == v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
STNumber::isDefault() const
|
||||||
|
{
|
||||||
|
return value_ == Number();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& out, STNumber const& rhs)
|
||||||
|
{
|
||||||
|
return out << rhs.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <xrpl/protocol/STArray.h>
|
#include <xrpl/protocol/STArray.h>
|
||||||
#include <xrpl/protocol/STBlob.h>
|
#include <xrpl/protocol/STBlob.h>
|
||||||
#include <xrpl/protocol/STCurrency.h>
|
#include <xrpl/protocol/STCurrency.h>
|
||||||
|
#include <xrpl/protocol/STNumber.h>
|
||||||
#include <xrpl/protocol/STObject.h>
|
#include <xrpl/protocol/STObject.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -665,6 +666,13 @@ STObject::getFieldCurrency(SField const& field) const
|
|||||||
return getFieldByConstRef<STCurrency>(field, empty);
|
return getFieldByConstRef<STCurrency>(field, empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STNumber const&
|
||||||
|
STObject::getFieldNumber(SField const& field) const
|
||||||
|
{
|
||||||
|
static STNumber const empty{};
|
||||||
|
return getFieldByConstRef<STNumber>(field, empty);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
STObject::set(std::unique_ptr<STBase> v)
|
STObject::set(std::unique_ptr<STBase> v)
|
||||||
{
|
{
|
||||||
@@ -765,6 +773,12 @@ STObject::setFieldIssue(SField const& field, STIssue const& v)
|
|||||||
setFieldUsingAssignment(field, v);
|
setFieldUsingAssignment(field, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
STObject::setFieldNumber(SField const& field, STNumber const& v)
|
||||||
|
{
|
||||||
|
setFieldUsingAssignment(field, v);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
STObject::setFieldPathSet(SField const& field, STPathSet const& v)
|
STObject::setFieldPathSet(SField const& field, STPathSet const& v)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <xrpl/protocol/STCurrency.h>
|
#include <xrpl/protocol/STCurrency.h>
|
||||||
#include <xrpl/protocol/STInteger.h>
|
#include <xrpl/protocol/STInteger.h>
|
||||||
#include <xrpl/protocol/STIssue.h>
|
#include <xrpl/protocol/STIssue.h>
|
||||||
|
#include <xrpl/protocol/STNumber.h>
|
||||||
#include <xrpl/protocol/STObject.h>
|
#include <xrpl/protocol/STObject.h>
|
||||||
#include <xrpl/protocol/STPathSet.h>
|
#include <xrpl/protocol/STPathSet.h>
|
||||||
#include <xrpl/protocol/STVector256.h>
|
#include <xrpl/protocol/STVector256.h>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include <xrpl/basics/contract.h>
|
#include <xrpl/basics/contract.h>
|
||||||
#include <xrpl/protocol/Serializer.h>
|
#include <xrpl/protocol/Serializer.h>
|
||||||
#include <xrpl/protocol/digest.h>
|
#include <xrpl/protocol/digest.h>
|
||||||
|
#include <cstdint>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
@@ -34,17 +35,6 @@ Serializer::add16(std::uint16_t i)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
Serializer::add32(std::uint32_t i)
|
|
||||||
{
|
|
||||||
int ret = mData.size();
|
|
||||||
mData.push_back(static_cast<unsigned char>(i >> 24));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>(i & 0xff));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
Serializer::add32(HashPrefix p)
|
Serializer::add32(HashPrefix p)
|
||||||
{
|
{
|
||||||
@@ -56,21 +46,6 @@ Serializer::add32(HashPrefix p)
|
|||||||
return add32(safe_cast<std::uint32_t>(p));
|
return add32(safe_cast<std::uint32_t>(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
Serializer::add64(std::uint64_t i)
|
|
||||||
{
|
|
||||||
int ret = mData.size();
|
|
||||||
mData.push_back(static_cast<unsigned char>(i >> 56));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 48) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 40) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 32) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 24) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
|
|
||||||
mData.push_back(static_cast<unsigned char>(i & 0xff));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
int
|
int
|
||||||
Serializer::addInteger(unsigned char i)
|
Serializer::addInteger(unsigned char i)
|
||||||
@@ -410,6 +385,30 @@ SerialIter::get64()
|
|||||||
(std::uint64_t(t[6]) << 8) + std::uint64_t(t[7]);
|
(std::uint64_t(t[6]) << 8) + std::uint64_t(t[7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::int32_t
|
||||||
|
SerialIter::geti32()
|
||||||
|
{
|
||||||
|
if (remain_ < 4)
|
||||||
|
Throw<std::runtime_error>("invalid SerialIter geti32");
|
||||||
|
auto t = p_;
|
||||||
|
p_ += 4;
|
||||||
|
used_ += 4;
|
||||||
|
remain_ -= 4;
|
||||||
|
return boost::endian::load_big_s32(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int64_t
|
||||||
|
SerialIter::geti64()
|
||||||
|
{
|
||||||
|
if (remain_ < 8)
|
||||||
|
Throw<std::runtime_error>("invalid SerialIter geti64");
|
||||||
|
auto t = p_;
|
||||||
|
p_ += 8;
|
||||||
|
used_ += 8;
|
||||||
|
remain_ -= 8;
|
||||||
|
return boost::endian::load_big_s64(t);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SerialIter::getFieldID(int& type, int& name)
|
SerialIter::getFieldID(int& type, int& name)
|
||||||
{
|
{
|
||||||
|
|||||||
93
src/test/protocol/STNumber_test.cpp
Normal file
93
src/test/protocol/STNumber_test.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2024 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/Issue.h>
|
||||||
|
#include <xrpl/protocol/STAmount.h>
|
||||||
|
#include <xrpl/protocol/STNumber.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
struct STNumber_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
testCombo(Number number)
|
||||||
|
{
|
||||||
|
STNumber const before{sfNumber, number};
|
||||||
|
BEAST_EXPECT(number == before);
|
||||||
|
Serializer s;
|
||||||
|
before.add(s);
|
||||||
|
BEAST_EXPECT(s.size() == 12);
|
||||||
|
SerialIter sit(s.slice());
|
||||||
|
STNumber const after{sit, sfNumber};
|
||||||
|
BEAST_EXPECT(after.isEquivalent(before));
|
||||||
|
BEAST_EXPECT(number == after);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
static_assert(!std::is_convertible_v<STNumber*, Number*>);
|
||||||
|
|
||||||
|
{
|
||||||
|
STNumber const stnum{sfNumber};
|
||||||
|
BEAST_EXPECT(stnum.getSType() == STI_NUMBER);
|
||||||
|
BEAST_EXPECT(stnum.getText() == "0");
|
||||||
|
BEAST_EXPECT(stnum.isDefault() == true);
|
||||||
|
BEAST_EXPECT(stnum.value() == Number{0});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::initializer_list<std::int64_t> const mantissas = {
|
||||||
|
std::numeric_limits<std::int64_t>::min(),
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
std::numeric_limits<std::int64_t>::max()};
|
||||||
|
for (std::int64_t mantissa : mantissas)
|
||||||
|
testCombo(Number{mantissa});
|
||||||
|
|
||||||
|
std::initializer_list<std::int32_t> const exponents = {
|
||||||
|
Number::minExponent, -1, 0, 1, Number::maxExponent - 1};
|
||||||
|
for (std::int32_t exponent : exponents)
|
||||||
|
testCombo(Number{123, exponent});
|
||||||
|
|
||||||
|
{
|
||||||
|
STAmount const strikePrice{noIssue(), 100};
|
||||||
|
STNumber const factor{sfNumber, 100};
|
||||||
|
auto const iouValue = strikePrice.iou();
|
||||||
|
IOUAmount totalValue{iouValue * factor};
|
||||||
|
STAmount const totalAmount{totalValue, strikePrice.issue()};
|
||||||
|
BEAST_EXPECT(totalAmount == Number{10'000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(STNumber, protocol, ripple);
|
||||||
|
|
||||||
|
void
|
||||||
|
testCompile(std::ostream& out)
|
||||||
|
{
|
||||||
|
STNumber number{sfNumber, 42};
|
||||||
|
out << number;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
69
src/test/protocol/Serializer_test.cpp
Normal file
69
src/test/protocol/Serializer_test.cpp
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2024 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/Serializer.h>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
|
||||||
|
struct Serializer_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
void
|
||||||
|
run() override
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::initializer_list<std::int32_t> const values = {
|
||||||
|
std::numeric_limits<std::int32_t>::min(),
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
std::numeric_limits<std::int32_t>::max()};
|
||||||
|
for (std::int32_t value : values)
|
||||||
|
{
|
||||||
|
Serializer s;
|
||||||
|
s.add32(value);
|
||||||
|
BEAST_EXPECT(s.size() == 4);
|
||||||
|
SerialIter sit(s.slice());
|
||||||
|
BEAST_EXPECT(sit.geti32() == value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::initializer_list<std::int64_t> const values = {
|
||||||
|
std::numeric_limits<std::int64_t>::min(),
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
std::numeric_limits<std::int64_t>::max()};
|
||||||
|
for (std::int64_t value : values)
|
||||||
|
{
|
||||||
|
Serializer s;
|
||||||
|
s.add64(value);
|
||||||
|
BEAST_EXPECT(s.size() == 8);
|
||||||
|
SerialIter sit(s.slice());
|
||||||
|
BEAST_EXPECT(sit.geti64() == value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(Serializer, protocol, ripple);
|
||||||
|
|
||||||
|
} // namespace ripple
|
||||||
@@ -88,7 +88,7 @@ Payment::preflight(PreflightContext const& ctx)
|
|||||||
|
|
||||||
if (txFlags & paymentMask)
|
if (txFlags & paymentMask)
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Malformed transaction: " << "Invalid flags set.";
|
JLOG(j.trace()) << "Malformed transaction: Invalid flags set.";
|
||||||
return temINVALID_FLAG;
|
return temINVALID_FLAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +110,9 @@ Payment::preflight(PreflightContext const& ctx)
|
|||||||
if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) ||
|
if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) ||
|
||||||
(!mptDirect && maxSourceAmount.holds<MPTIssue>()))
|
(!mptDirect && maxSourceAmount.holds<MPTIssue>()))
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Malformed transaction: "
|
JLOG(j.trace()) << "Malformed transaction: inconsistent issues: "
|
||||||
<< "inconsistent issues: " << dstAmount.getFullText()
|
<< dstAmount.getFullText() << " "
|
||||||
<< " " << maxSourceAmount.getFullText() << " "
|
<< maxSourceAmount.getFullText() << " "
|
||||||
<< deliverMin.value_or(STAmount{}).getFullText();
|
<< deliverMin.value_or(STAmount{}).getFullText();
|
||||||
return temMALFORMED;
|
return temMALFORMED;
|
||||||
}
|
}
|
||||||
@@ -135,19 +135,19 @@ Payment::preflight(PreflightContext const& ctx)
|
|||||||
}
|
}
|
||||||
if (hasMax && maxSourceAmount <= beast::zero)
|
if (hasMax && maxSourceAmount <= beast::zero)
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Malformed transaction: " << "bad max amount: "
|
JLOG(j.trace()) << "Malformed transaction: bad max amount: "
|
||||||
<< maxSourceAmount.getFullText();
|
<< maxSourceAmount.getFullText();
|
||||||
return temBAD_AMOUNT;
|
return temBAD_AMOUNT;
|
||||||
}
|
}
|
||||||
if (dstAmount <= beast::zero)
|
if (dstAmount <= beast::zero)
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Malformed transaction: "
|
JLOG(j.trace()) << "Malformed transaction: bad dst amount: "
|
||||||
<< "bad dst amount: " << dstAmount.getFullText();
|
<< dstAmount.getFullText();
|
||||||
return temBAD_AMOUNT;
|
return temBAD_AMOUNT;
|
||||||
}
|
}
|
||||||
if (badCurrency() == srcAsset || badCurrency() == dstAsset)
|
if (badCurrency() == srcAsset || badCurrency() == dstAsset)
|
||||||
{
|
{
|
||||||
JLOG(j.trace()) << "Malformed transaction: " << "Bad currency.";
|
JLOG(j.trace()) << "Malformed transaction: Bad currency.";
|
||||||
return temBAD_CURRENCY;
|
return temBAD_CURRENCY;
|
||||||
}
|
}
|
||||||
if (account == dstAccountID && equalTokens(srcAsset, dstAsset) && !hasPaths)
|
if (account == dstAccountID && equalTokens(srcAsset, dstAsset) && !hasPaths)
|
||||||
@@ -550,7 +550,7 @@ Payment::doApply()
|
|||||||
// Vote no. However the transaction might succeed, if applied in
|
// Vote no. However the transaction might succeed, if applied in
|
||||||
// a different order.
|
// a different order.
|
||||||
JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
|
JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
|
||||||
<< " " << to_string(mPriorBalance) << " / "
|
<< to_string(mPriorBalance) << " / "
|
||||||
<< to_string(dstAmount.xrp() + mmm) << " ("
|
<< to_string(dstAmount.xrp() + mmm) << " ("
|
||||||
<< to_string(reserve) << ")";
|
<< to_string(reserve) << ")";
|
||||||
|
|
||||||
|
|||||||
@@ -63,20 +63,17 @@ CachedViewImpl::read(Keylet const& k) const
|
|||||||
hits.increment();
|
hits.increment();
|
||||||
else
|
else
|
||||||
misses.increment();
|
misses.increment();
|
||||||
std::lock_guard lock(mutex_);
|
|
||||||
auto const er = map_.emplace(k.key, *digest);
|
if (!cacheHit)
|
||||||
bool const inserted = er.second;
|
|
||||||
if (sle && !k.check(*sle))
|
|
||||||
{
|
{
|
||||||
if (!inserted)
|
// Avoid acquiring this lock unless necessary. It is only necessary if
|
||||||
{
|
// the key was not found in the map_. The lock is needed to add the key
|
||||||
// On entry, this function did not find this key in map_. Now
|
// and digest.
|
||||||
// something (another thread?) has inserted the sle into the map and
|
std::lock_guard lock(mutex_);
|
||||||
// it has the wrong type.
|
map_.emplace(k.key, *digest);
|
||||||
LogicError("CachedView::read: wrong type");
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
if (!sle || !k.check(*sle))
|
||||||
|
return nullptr;
|
||||||
return sle;
|
return sle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user