mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +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()};
|
||||
|
||||
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
|
||||
{
|
||||
explicit unchecked() = default;
|
||||
@@ -191,14 +199,6 @@ private:
|
||||
constexpr bool
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ template <int>
|
||||
class STBitString;
|
||||
template <class>
|
||||
class STInteger;
|
||||
class STNumber;
|
||||
class STXChainBridge;
|
||||
class STVector256;
|
||||
class STCurrency;
|
||||
@@ -70,8 +71,9 @@ class STCurrency;
|
||||
STYPE(STI_AMOUNT, 6) \
|
||||
STYPE(STI_VL, 7) \
|
||||
STYPE(STI_ACCOUNT, 8) \
|
||||
STYPE(STI_NUMBER, 9) \
|
||||
\
|
||||
/* 9-13 are reserved */ \
|
||||
/* 10-13 are reserved */ \
|
||||
STYPE(STI_OBJECT, 14) \
|
||||
STYPE(STI_ARRAY, 15) \
|
||||
\
|
||||
@@ -355,6 +357,7 @@ using SF_ACCOUNT = TypedField<STAccount>;
|
||||
using SF_AMOUNT = TypedField<STAmount>;
|
||||
using SF_ISSUE = TypedField<STIssue>;
|
||||
using SF_CURRENCY = TypedField<STCurrency>;
|
||||
using SF_NUMBER = TypedField<STNumber>;
|
||||
using SF_VL = TypedField<STBlob>;
|
||||
using SF_VECTOR256 = TypedField<STVector256>;
|
||||
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;
|
||||
const STCurrency&
|
||||
getFieldCurrency(SField const& field) const;
|
||||
STNumber const&
|
||||
getFieldNumber(SField const& field) const;
|
||||
|
||||
/** Get the value of a field.
|
||||
@param A TypedField built from an SField value representing the desired
|
||||
@@ -376,6 +378,8 @@ public:
|
||||
void
|
||||
setFieldCurrency(SField const& field, STCurrency const&);
|
||||
void
|
||||
setFieldNumber(SField const& field, STNumber const&);
|
||||
void
|
||||
setFieldPathSet(SField const& field, STPathSet const&);
|
||||
void
|
||||
setFieldV256(SField const& field, STVector256 const& v);
|
||||
|
||||
@@ -83,12 +83,43 @@ public:
|
||||
add8(unsigned char i);
|
||||
int
|
||||
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
|
||||
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
|
||||
add32(HashPrefix p);
|
||||
|
||||
template <typename T>
|
||||
requires(std::is_same_v<
|
||||
std::make_unsigned_t<std::remove_cv_t<T>>,
|
||||
std::uint64_t>)
|
||||
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>
|
||||
int addInteger(Integer);
|
||||
@@ -353,9 +384,13 @@ public:
|
||||
|
||||
std::uint32_t
|
||||
get32();
|
||||
std::int32_t
|
||||
geti32();
|
||||
|
||||
std::uint64_t
|
||||
get64();
|
||||
std::int64_t
|
||||
geti64();
|
||||
|
||||
template <std::size_t Bits, class Tag = void>
|
||||
base_uint<Bits, Tag>
|
||||
|
||||
@@ -191,6 +191,9 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
|
||||
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
|
||||
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
|
||||
|
||||
// number (common)
|
||||
TYPED_SFIELD(sfNumber, NUMBER, 1)
|
||||
|
||||
// currency amount (common)
|
||||
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
||||
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <xrpl/protocol/STBlob.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/STLedgerEntry.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/STParsedJSON.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/STBlob.h>
|
||||
#include <xrpl/protocol/STCurrency.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -665,6 +666,13 @@ STObject::getFieldCurrency(SField const& field) const
|
||||
return getFieldByConstRef<STCurrency>(field, empty);
|
||||
}
|
||||
|
||||
STNumber const&
|
||||
STObject::getFieldNumber(SField const& field) const
|
||||
{
|
||||
static STNumber const empty{};
|
||||
return getFieldByConstRef<STNumber>(field, empty);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::set(std::unique_ptr<STBase> v)
|
||||
{
|
||||
@@ -765,6 +773,12 @@ STObject::setFieldIssue(SField const& field, STIssue const& v)
|
||||
setFieldUsingAssignment(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldNumber(SField const& field, STNumber const& v)
|
||||
{
|
||||
setFieldUsingAssignment(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldPathSet(SField const& field, STPathSet const& v)
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <xrpl/protocol/STCurrency.h>
|
||||
#include <xrpl/protocol/STInteger.h>
|
||||
#include <xrpl/protocol/STIssue.h>
|
||||
#include <xrpl/protocol/STNumber.h>
|
||||
#include <xrpl/protocol/STObject.h>
|
||||
#include <xrpl/protocol/STPathSet.h>
|
||||
#include <xrpl/protocol/STVector256.h>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace ripple {
|
||||
@@ -34,17 +35,6 @@ Serializer::add16(std::uint16_t i)
|
||||
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
|
||||
Serializer::add32(HashPrefix p)
|
||||
{
|
||||
@@ -56,21 +46,6 @@ Serializer::add32(HashPrefix 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 <>
|
||||
int
|
||||
Serializer::addInteger(unsigned char i)
|
||||
@@ -410,6 +385,30 @@ SerialIter::get64()
|
||||
(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
|
||||
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)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: " << "Invalid flags set.";
|
||||
JLOG(j.trace()) << "Malformed transaction: Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
@@ -110,9 +110,9 @@ Payment::preflight(PreflightContext const& ctx)
|
||||
if ((mptDirect && dstAmount.asset() != maxSourceAmount.asset()) ||
|
||||
(!mptDirect && maxSourceAmount.holds<MPTIssue>()))
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: "
|
||||
<< "inconsistent issues: " << dstAmount.getFullText()
|
||||
<< " " << maxSourceAmount.getFullText() << " "
|
||||
JLOG(j.trace()) << "Malformed transaction: inconsistent issues: "
|
||||
<< dstAmount.getFullText() << " "
|
||||
<< maxSourceAmount.getFullText() << " "
|
||||
<< deliverMin.value_or(STAmount{}).getFullText();
|
||||
return temMALFORMED;
|
||||
}
|
||||
@@ -135,19 +135,19 @@ Payment::preflight(PreflightContext const& ctx)
|
||||
}
|
||||
if (hasMax && maxSourceAmount <= beast::zero)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: " << "bad max amount: "
|
||||
JLOG(j.trace()) << "Malformed transaction: bad max amount: "
|
||||
<< maxSourceAmount.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (dstAmount <= beast::zero)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: "
|
||||
<< "bad dst amount: " << dstAmount.getFullText();
|
||||
JLOG(j.trace()) << "Malformed transaction: bad dst amount: "
|
||||
<< dstAmount.getFullText();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (badCurrency() == srcAsset || badCurrency() == dstAsset)
|
||||
{
|
||||
JLOG(j.trace()) << "Malformed transaction: " << "Bad currency.";
|
||||
JLOG(j.trace()) << "Malformed transaction: Bad currency.";
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
if (account == dstAccountID && equalTokens(srcAsset, dstAsset) && !hasPaths)
|
||||
@@ -550,7 +550,7 @@ Payment::doApply()
|
||||
// Vote no. However the transaction might succeed, if applied in
|
||||
// a different order.
|
||||
JLOG(j_.trace()) << "Delay transaction: Insufficient funds: "
|
||||
<< " " << to_string(mPriorBalance) << " / "
|
||||
<< to_string(mPriorBalance) << " / "
|
||||
<< to_string(dstAmount.xrp() + mmm) << " ("
|
||||
<< to_string(reserve) << ")";
|
||||
|
||||
|
||||
@@ -63,20 +63,17 @@ CachedViewImpl::read(Keylet const& k) const
|
||||
hits.increment();
|
||||
else
|
||||
misses.increment();
|
||||
std::lock_guard lock(mutex_);
|
||||
auto const er = map_.emplace(k.key, *digest);
|
||||
bool const inserted = er.second;
|
||||
if (sle && !k.check(*sle))
|
||||
|
||||
if (!cacheHit)
|
||||
{
|
||||
if (!inserted)
|
||||
{
|
||||
// On entry, this function did not find this key in map_. Now
|
||||
// something (another thread?) has inserted the sle into the map and
|
||||
// it has the wrong type.
|
||||
LogicError("CachedView::read: wrong type");
|
||||
}
|
||||
return nullptr;
|
||||
// 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
|
||||
// and digest.
|
||||
std::lock_guard lock(mutex_);
|
||||
map_.emplace(k.key, *digest);
|
||||
}
|
||||
if (!sle || !k.check(*sle))
|
||||
return nullptr;
|
||||
return sle;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user