Skip to content

Commit f419c18

Browse files
intelliotthejohnfreemanHowardHinnant
committed
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>
1 parent 0ec17b6 commit f419c18

File tree

15 files changed

+470
-58
lines changed

15 files changed

+470
-58
lines changed

include/xrpl/basics/Number.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ class Number
4141
int exponent_{std::numeric_limits<int>::lowest()};
4242

4343
public:
44+
// The range for the mantissa when normalized
45+
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
46+
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
47+
48+
// The range for the exponent when normalized
49+
constexpr static int minExponent = -32768;
50+
constexpr static int maxExponent = 32768;
51+
4452
struct unchecked
4553
{
4654
explicit unchecked() = default;
@@ -191,14 +199,6 @@ class Number
191199
constexpr bool
192200
isnormal() const noexcept;
193201

194-
// The range for the mantissa when normalized
195-
constexpr static std::int64_t minMantissa = 1'000'000'000'000'000LL;
196-
constexpr static std::int64_t maxMantissa = 9'999'999'999'999'999LL;
197-
198-
// The range for the exponent when normalized
199-
constexpr static int minExponent = -32768;
200-
constexpr static int maxExponent = 32768;
201-
202202
class Guard;
203203
};
204204

include/xrpl/protocol/SField.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ template <int>
4949
class STBitString;
5050
template <class>
5151
class STInteger;
52+
class STNumber;
5253
class STXChainBridge;
5354
class STVector256;
5455
class STCurrency;
@@ -70,8 +71,9 @@ class STCurrency;
7071
STYPE(STI_AMOUNT, 6) \
7172
STYPE(STI_VL, 7) \
7273
STYPE(STI_ACCOUNT, 8) \
74+
STYPE(STI_NUMBER, 9) \
7375
\
74-
/* 9-13 are reserved */ \
76+
/* 10-13 are reserved */ \
7577
STYPE(STI_OBJECT, 14) \
7678
STYPE(STI_ARRAY, 15) \
7779
\
@@ -355,6 +357,7 @@ using SF_ACCOUNT = TypedField<STAccount>;
355357
using SF_AMOUNT = TypedField<STAmount>;
356358
using SF_ISSUE = TypedField<STIssue>;
357359
using SF_CURRENCY = TypedField<STCurrency>;
360+
using SF_NUMBER = TypedField<STNumber>;
358361
using SF_VL = TypedField<STBlob>;
359362
using SF_VECTOR256 = TypedField<STVector256>;
360363
using SF_XCHAIN_BRIDGE = TypedField<STXChainBridge>;

include/xrpl/protocol/STNumber.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2024 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#ifndef XRPL_PROTOCOL_STNUMBER_H_INCLUDED
21+
#define XRPL_PROTOCOL_STNUMBER_H_INCLUDED
22+
23+
#include <xrpl/basics/CountedObject.h>
24+
#include <xrpl/basics/Number.h>
25+
#include <xrpl/protocol/STBase.h>
26+
27+
#include <ostream>
28+
29+
namespace ripple {
30+
31+
/**
32+
* A serializable number.
33+
*
34+
* This type is-a `Number`, and can be used everywhere that is accepted.
35+
* This type simply integrates `Number` with the serialization framework,
36+
* letting it be used for fields in ledger entries and transactions.
37+
* It is effectively an `STAmount` sans `Asset`:
38+
* it can represent a value of any token type (XRP, IOU, or MPT)
39+
* without paying the storage cost of duplicating asset information
40+
* that may be deduced from the context.
41+
*/
42+
class STNumber : public STBase, public CountedObject<STNumber>
43+
{
44+
private:
45+
Number value_;
46+
47+
public:
48+
using value_type = Number;
49+
50+
STNumber() = default;
51+
explicit STNumber(SField const& field, Number const& value = Number());
52+
STNumber(SerialIter& sit, SField const& field);
53+
54+
SerializedTypeID
55+
getSType() const override;
56+
std::string
57+
getText() const override;
58+
void
59+
add(Serializer& s) const override;
60+
61+
Number const&
62+
value() const;
63+
void
64+
setValue(Number const& v);
65+
66+
bool
67+
isEquivalent(STBase const& t) const override;
68+
bool
69+
isDefault() const override;
70+
71+
operator Number() const
72+
{
73+
return value_;
74+
}
75+
76+
private:
77+
STBase*
78+
copy(std::size_t n, void* buf) const override;
79+
STBase*
80+
move(std::size_t n, void* buf) override;
81+
};
82+
83+
std::ostream&
84+
operator<<(std::ostream& out, STNumber const& rhs);
85+
86+
} // namespace ripple
87+
88+
#endif

include/xrpl/protocol/STObject.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ class STObject : public STBase, public CountedObject<STObject>
245245
getFieldArray(SField const& field) const;
246246
const STCurrency&
247247
getFieldCurrency(SField const& field) const;
248+
STNumber const&
249+
getFieldNumber(SField const& field) const;
248250

249251
/** Get the value of a field.
250252
@param A TypedField built from an SField value representing the desired
@@ -376,6 +378,8 @@ class STObject : public STBase, public CountedObject<STObject>
376378
void
377379
setFieldCurrency(SField const& field, STCurrency const&);
378380
void
381+
setFieldNumber(SField const& field, STNumber const&);
382+
void
379383
setFieldPathSet(SField const& field, STPathSet const&);
380384
void
381385
setFieldV256(SField const& field, STVector256 const& v);

include/xrpl/protocol/Serializer.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,43 @@ class Serializer
8383
add8(unsigned char i);
8484
int
8585
add16(std::uint16_t i);
86+
87+
template <typename T>
88+
requires(std::is_same_v<
89+
std::make_unsigned_t<std::remove_cv_t<T>>,
90+
std::uint32_t>)
8691
int
87-
add32(std::uint32_t i); // ledger indexes, account sequence, timestamps
92+
add32(T i)
93+
{
94+
int ret = mData.size();
95+
mData.push_back(static_cast<unsigned char>((i >> 24) & 0xff));
96+
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
97+
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
98+
mData.push_back(static_cast<unsigned char>(i & 0xff));
99+
return ret;
100+
}
101+
88102
int
89103
add32(HashPrefix p);
104+
105+
template <typename T>
106+
requires(std::is_same_v<
107+
std::make_unsigned_t<std::remove_cv_t<T>>,
108+
std::uint64_t>)
90109
int
91-
add64(std::uint64_t i); // native currency amounts
110+
add64(T i)
111+
{
112+
int ret = mData.size();
113+
mData.push_back(static_cast<unsigned char>((i >> 56) & 0xff));
114+
mData.push_back(static_cast<unsigned char>((i >> 48) & 0xff));
115+
mData.push_back(static_cast<unsigned char>((i >> 40) & 0xff));
116+
mData.push_back(static_cast<unsigned char>((i >> 32) & 0xff));
117+
mData.push_back(static_cast<unsigned char>((i >> 24) & 0xff));
118+
mData.push_back(static_cast<unsigned char>((i >> 16) & 0xff));
119+
mData.push_back(static_cast<unsigned char>((i >> 8) & 0xff));
120+
mData.push_back(static_cast<unsigned char>(i & 0xff));
121+
return ret;
122+
}
92123

93124
template <typename Integer>
94125
int addInteger(Integer);
@@ -353,9 +384,13 @@ class SerialIter
353384

354385
std::uint32_t
355386
get32();
387+
std::int32_t
388+
geti32();
356389

357390
std::uint64_t
358391
get64();
392+
std::int64_t
393+
geti64();
359394

360395
template <std::size_t Bits, class Tag = void>
361396
base_uint<Bits, Tag>

include/xrpl/protocol/detail/sfields.macro

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ TYPED_SFIELD(sfHookHash, UINT256, 31)
191191
TYPED_SFIELD(sfHookNamespace, UINT256, 32)
192192
TYPED_SFIELD(sfHookSetTxnID, UINT256, 33)
193193

194+
// number (common)
195+
TYPED_SFIELD(sfNumber, NUMBER, 1)
196+
194197
// currency amount (common)
195198
TYPED_SFIELD(sfAmount, AMOUNT, 1)
196199
TYPED_SFIELD(sfBalance, AMOUNT, 2)

include/xrpl/protocol/st.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <xrpl/protocol/STBlob.h>
3030
#include <xrpl/protocol/STInteger.h>
3131
#include <xrpl/protocol/STLedgerEntry.h>
32+
#include <xrpl/protocol/STNumber.h>
3233
#include <xrpl/protocol/STObject.h>
3334
#include <xrpl/protocol/STParsedJSON.h>
3435
#include <xrpl/protocol/STPathSet.h>

src/libxrpl/protocol/STNumber.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//------------------------------------------------------------------------------
2+
/*
3+
This file is part of rippled: https://github.com/ripple/rippled
4+
Copyright (c) 2023 Ripple Labs Inc.
5+
6+
Permission to use, copy, modify, and/or distribute this software for any
7+
purpose with or without fee is hereby granted, provided that the above
8+
copyright notice and this permission notice appear in all copies.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13+
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17+
*/
18+
//==============================================================================
19+
20+
#include <xrpl/protocol/STNumber.h>
21+
22+
#include <xrpl/protocol/SField.h>
23+
24+
namespace ripple {
25+
26+
STNumber::STNumber(SField const& field, Number const& value)
27+
: STBase(field), value_(value)
28+
{
29+
}
30+
31+
STNumber::STNumber(SerialIter& sit, SField const& field) : STBase(field)
32+
{
33+
// We must call these methods in separate statements
34+
// to guarantee their order of execution.
35+
auto mantissa = sit.geti64();
36+
auto exponent = sit.geti32();
37+
value_ = Number{mantissa, exponent};
38+
}
39+
40+
SerializedTypeID
41+
STNumber::getSType() const
42+
{
43+
return STI_NUMBER;
44+
}
45+
46+
std::string
47+
STNumber::getText() const
48+
{
49+
return to_string(value_);
50+
}
51+
52+
void
53+
STNumber::add(Serializer& s) const
54+
{
55+
assert(getFName().isBinary());
56+
assert(getFName().fieldType == getSType());
57+
s.add64(value_.mantissa());
58+
s.add32(value_.exponent());
59+
}
60+
61+
Number const&
62+
STNumber::value() const
63+
{
64+
return value_;
65+
}
66+
67+
void
68+
STNumber::setValue(Number const& v)
69+
{
70+
value_ = v;
71+
}
72+
73+
STBase*
74+
STNumber::copy(std::size_t n, void* buf) const
75+
{
76+
return emplace(n, buf, *this);
77+
}
78+
79+
STBase*
80+
STNumber::move(std::size_t n, void* buf)
81+
{
82+
return emplace(n, buf, std::move(*this));
83+
}
84+
85+
bool
86+
STNumber::isEquivalent(STBase const& t) const
87+
{
88+
assert(t.getSType() == this->getSType());
89+
STNumber const& v = dynamic_cast<STNumber const&>(t);
90+
return value_ == v;
91+
}
92+
93+
bool
94+
STNumber::isDefault() const
95+
{
96+
return value_ == Number();
97+
}
98+
99+
std::ostream&
100+
operator<<(std::ostream& out, STNumber const& rhs)
101+
{
102+
return out << rhs.getText();
103+
}
104+
105+
} // namespace ripple

src/libxrpl/protocol/STObject.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <xrpl/protocol/STArray.h>
2626
#include <xrpl/protocol/STBlob.h>
2727
#include <xrpl/protocol/STCurrency.h>
28+
#include <xrpl/protocol/STNumber.h>
2829
#include <xrpl/protocol/STObject.h>
2930

3031
namespace ripple {
@@ -665,6 +666,13 @@ STObject::getFieldCurrency(SField const& field) const
665666
return getFieldByConstRef<STCurrency>(field, empty);
666667
}
667668

669+
STNumber const&
670+
STObject::getFieldNumber(SField const& field) const
671+
{
672+
static STNumber const empty{};
673+
return getFieldByConstRef<STNumber>(field, empty);
674+
}
675+
668676
void
669677
STObject::set(std::unique_ptr<STBase> v)
670678
{
@@ -765,6 +773,12 @@ STObject::setFieldIssue(SField const& field, STIssue const& v)
765773
setFieldUsingAssignment(field, v);
766774
}
767775

776+
void
777+
STObject::setFieldNumber(SField const& field, STNumber const& v)
778+
{
779+
setFieldUsingAssignment(field, v);
780+
}
781+
768782
void
769783
STObject::setFieldPathSet(SField const& field, STPathSet const& v)
770784
{

src/libxrpl/protocol/STVar.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <xrpl/protocol/STCurrency.h>
3030
#include <xrpl/protocol/STInteger.h>
3131
#include <xrpl/protocol/STIssue.h>
32+
#include <xrpl/protocol/STNumber.h>
3233
#include <xrpl/protocol/STObject.h>
3334
#include <xrpl/protocol/STPathSet.h>
3435
#include <xrpl/protocol/STVector256.h>

0 commit comments

Comments
 (0)