Skip to content

Commit 1ab8dd4

Browse files
committed
[WIP] Checkpoint
1 parent 9798172 commit 1ab8dd4

23 files changed

+304
-51
lines changed

include/xrpl/protocol/SField.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ class SField
135135
sMD_Always = 0x10, // value when node containing it is affected at all
136136
sMD_BaseTen = 0x20, // value is treated as base 10, overriding behavior
137137
sMD_PseudoAccount = 0x40, // if this field is set in an ACCOUNT_ROOT
138-
// _only_, then it is a pseudo-account
138+
// _only_, then it is a pseudo-account
139+
sMD_NeedsAsset = 0x80, // This field needs to be associated with an
140+
// asset before it is serialized as a ledger
141+
// object. Intended for STNumber.
139142
sMD_Default =
140143
sMD_ChangeOrig | sMD_ChangeNew | sMD_DeleteFinal | sMD_Create
141144
};

include/xrpl/protocol/STAmount.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -731,17 +731,32 @@ getRate(STAmount const& offerOut, STAmount const& offerIn);
731731
* @param rounding Optional Number rounding mode
732732
*
733733
*/
734-
STAmount
734+
[[nodiscard]] STAmount
735735
roundToScale(
736736
STAmount const& value,
737737
std::int32_t scale,
738738
Number::rounding_mode rounding = Number::getround());
739739

740+
/** Round an arbitrary precision Number IN PLACE to the precision of a given
741+
* Asset.
742+
*
743+
* This is used to ensure that calculations do not collect dust for IOUs, or
744+
* fractional amounts for the integral types XRP and MPT.
745+
*
746+
* @param asset The relevant asset
747+
* @param value The lvalue to be rounded
748+
*/
749+
template <AssetType A>
750+
void
751+
roundToAsset(A const& asset, Number& value)
752+
{
753+
value = STAmount{asset, value};
754+
}
755+
740756
/** Round an arbitrary precision Number to the precision of a given Asset.
741757
*
742-
* This is used to ensure that calculations do not collect dust beyond the
743-
* precision of the reference value for IOUs, or fractional amounts for the
744-
* integral types XRP and MPT.
758+
* This is used to ensure that calculations do not collect dust beyond specified
759+
* scale for IOUs, or fractional amounts for the integral types XRP and MPT.
745760
*
746761
* @param asset The relevant asset
747762
* @param value The value to be rounded
@@ -750,7 +765,7 @@ roundToScale(
750765
* @param rounding Optional Number rounding mode
751766
*/
752767
template <AssetType A>
753-
Number
768+
[[nodiscard]] Number
754769
roundToAsset(
755770
A const& asset,
756771
Number const& value,

include/xrpl/protocol/STNumber.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <xrpl/basics/CountedObject.h>
55
#include <xrpl/basics/Number.h>
66
#include <xrpl/protocol/STBase.h>
7+
#include <xrpl/protocol/STTakesAsset.h>
78

89
#include <ostream>
910

@@ -20,7 +21,7 @@ namespace ripple {
2021
* without paying the storage cost of duplicating asset information
2122
* that may be deduced from the context.
2223
*/
23-
class STNumber : public STBase, public CountedObject<STNumber>
24+
class STNumber : public STTakesAsset, public CountedObject<STNumber>
2425
{
2526
private:
2627
Number value_;
@@ -56,6 +57,9 @@ class STNumber : public STBase, public CountedObject<STNumber>
5657
bool
5758
isDefault() const override;
5859

60+
void
61+
associateAsset(Asset const& a) override;
62+
5963
operator Number() const
6064
{
6165
return value_;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef XRPL_PROTOCOL_STTAKESASSET_H_INCLUDED
2+
#define XRPL_PROTOCOL_STTAKESASSET_H_INCLUDED
3+
4+
#include <xrpl/protocol/Asset.h>
5+
#include <xrpl/protocol/STBase.h>
6+
7+
namespace ripple {
8+
9+
class STTakesAsset : public STBase
10+
{
11+
protected:
12+
std::optional<Asset> asset_;
13+
14+
public:
15+
using STBase::STBase;
16+
using STBase::operator=;
17+
18+
virtual void
19+
associateAsset(Asset const& a);
20+
};
21+
22+
class STLedgerEntry;
23+
24+
void
25+
associateAsset(SLE& sle, Asset const& asset);
26+
27+
} // namespace ripple
28+
29+
#endif

include/xrpl/protocol/detail/sfields.macro

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,22 +207,22 @@ TYPED_SFIELD(sfLoanID, UINT256, 38)
207207

208208
// number (common)
209209
TYPED_SFIELD(sfNumber, NUMBER, 1)
210-
TYPED_SFIELD(sfAssetsAvailable, NUMBER, 2)
211-
TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3)
212-
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4)
213-
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5)
214-
TYPED_SFIELD(sfDebtTotal, NUMBER, 6)
215-
TYPED_SFIELD(sfDebtMaximum, NUMBER, 7)
216-
TYPED_SFIELD(sfCoverAvailable, NUMBER, 8)
210+
TYPED_SFIELD(sfAssetsAvailable, NUMBER, 2, SField::sMD_NeedsAsset | SField::sMD_Default)
211+
TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3, SField::sMD_NeedsAsset | SField::sMD_Default)
212+
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4, SField::sMD_NeedsAsset | SField::sMD_Default)
213+
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5, SField::sMD_NeedsAsset | SField::sMD_Default)
214+
TYPED_SFIELD(sfDebtTotal, NUMBER, 6, SField::sMD_NeedsAsset | SField::sMD_Default)
215+
TYPED_SFIELD(sfDebtMaximum, NUMBER, 7, SField::sMD_NeedsAsset | SField::sMD_Default)
216+
TYPED_SFIELD(sfCoverAvailable, NUMBER, 8, SField::sMD_NeedsAsset | SField::sMD_Default)
217217
TYPED_SFIELD(sfLoanOriginationFee, NUMBER, 9)
218218
TYPED_SFIELD(sfLoanServiceFee, NUMBER, 10)
219219
TYPED_SFIELD(sfLatePaymentFee, NUMBER, 11)
220220
TYPED_SFIELD(sfClosePaymentFee, NUMBER, 12)
221-
TYPED_SFIELD(sfPrincipalOutstanding, NUMBER, 13)
221+
TYPED_SFIELD(sfPrincipalOutstanding, NUMBER, 13, SField::sMD_NeedsAsset | SField::sMD_Default)
222222
TYPED_SFIELD(sfPrincipalRequested, NUMBER, 14)
223-
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15)
223+
TYPED_SFIELD(sfTotalValueOutstanding, NUMBER, 15, SField::sMD_NeedsAsset | SField::sMD_Default)
224224
TYPED_SFIELD(sfPeriodicPayment, NUMBER, 16)
225-
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17)
225+
TYPED_SFIELD(sfManagementFeeOutstanding, NUMBER, 17, SField::sMD_NeedsAsset | SField::sMD_Default)
226226

227227
// int32
228228
TYPED_SFIELD(sfLoanScale, INT32, 1)

src/libxrpl/protocol/STNumber.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
#include <xrpl/protocol/STNumber.h>
2+
// Do not remove. Keep STNumber.h first
13
#include <xrpl/basics/Number.h>
24
#include <xrpl/beast/core/LexicalCast.h>
35
#include <xrpl/beast/utility/instrumentation.h>
46
#include <xrpl/protocol/SField.h>
7+
#include <xrpl/protocol/STAmount.h>
58
#include <xrpl/protocol/STBase.h>
6-
#include <xrpl/protocol/STNumber.h>
9+
#include <xrpl/protocol/STIssue.h>
710
#include <xrpl/protocol/Serializer.h>
811

912
#include <boost/lexical_cast.hpp>
@@ -17,11 +20,11 @@
1720
namespace ripple {
1821

1922
STNumber::STNumber(SField const& field, Number const& value)
20-
: STBase(field), value_(value)
23+
: STTakesAsset(field), value_(value)
2124
{
2225
}
2326

24-
STNumber::STNumber(SerialIter& sit, SField const& field) : STBase(field)
27+
STNumber::STNumber(SerialIter& sit, SField const& field) : STTakesAsset(field)
2528
{
2629
// We must call these methods in separate statements
2730
// to guarantee their order of execution.
@@ -42,6 +45,19 @@ STNumber::getText() const
4245
return to_string(value_);
4346
}
4447

48+
void
49+
STNumber::associateAsset(Asset const& a)
50+
{
51+
STTakesAsset::associateAsset(a);
52+
53+
XRPL_ASSERT_PARTS(
54+
getFName().shouldMeta(SField::sMD_NeedsAsset),
55+
"STNumber::associateAsset",
56+
"field needs asset");
57+
58+
roundToAsset(a, value_);
59+
}
60+
4561
void
4662
STNumber::add(Serializer& s) const
4763
{
@@ -51,8 +67,47 @@ STNumber::add(Serializer& s) const
5167
getFName().fieldType == getSType(),
5268
"ripple::STNumber::add : field type match");
5369

54-
auto const mantissa = value_.mantissa();
55-
auto const exponent = value_.exponent();
70+
SField const& field = getFName();
71+
auto value = value_;
72+
#if 0
73+
if (field.shouldMeta(SField::sMD_NeedsAsset))
74+
{
75+
// asset is defined in the STTakesAsset base class
76+
if (asset_)
77+
roundToAsset(*asset_, value);
78+
else
79+
{
80+
// There may be circumstances where an already-rounded Number is
81+
// passed through to serialization without an asset. We can't be
82+
// 100% sure, but we can check a couple of conditions that indicate
83+
// it's _probably_ rounded.
84+
XRPL_ASSERT_PARTS(
85+
Number::getMantissaScale() == MantissaRange::small,
86+
"ripple::STNumber::add",
87+
"STNumber only used with large mantissa scale");
88+
std::uint64_t mantissa =
89+
value < beast::zero ? -value.mantissa() : value.mantissa();
90+
auto exponent = value.exponent();
91+
if (mantissa < Number::minMantissa())
92+
{
93+
mantissa *= 10;
94+
--exponent;
95+
}
96+
XRPL_ASSERT_PARTS(
97+
exponent <= 0 || (mantissa % 1000 == 0),
98+
"ripple::STNumber::add",
99+
"STNumber is probably already rounded");
100+
/*
101+
Throw<std::runtime_error>(
102+
"ripple::STNumber::add : asset required for field " +
103+
field.getName());
104+
*/
105+
}
106+
}
107+
#endif
108+
109+
auto const mantissa = value.mantissa();
110+
auto const exponent = value.exponent();
56111
XRPL_ASSERT_PARTS(
57112
mantissa <= std::numeric_limits<std::int64_t>::max() &&
58113
mantissa >= std::numeric_limits<std::int64_t>::min(),
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <xrpl/protocol/STTakesAsset.h>
2+
// Do not remove. Force STTakesAsset.h first
3+
#include <xrpl/protocol/STLedgerEntry.h>
4+
5+
namespace ripple {
6+
7+
void
8+
STTakesAsset::associateAsset(Asset const& a)
9+
{
10+
asset_.emplace(a);
11+
}
12+
13+
void
14+
associateAsset(SLE& sle, Asset const& asset)
15+
{
16+
#if 1
17+
// Iterating by offset is the only way to get non-const references
18+
for (int i = 0; i < sle.getCount(); ++i)
19+
{
20+
STBase& entry = sle.getIndex(i);
21+
SField const& field = entry.getFName();
22+
if (field.shouldMeta(SField::sMD_NeedsAsset))
23+
{
24+
auto const type = entry.getSType();
25+
// If the field is not set or present, skip it.
26+
if (type == STI_NOTPRESENT)
27+
continue;
28+
// If the type doesn't downcast, then the flag shouldn't be on the
29+
// SField
30+
auto& ta = entry.downcast<STTakesAsset>();
31+
ta.associateAsset(asset);
32+
}
33+
}
34+
#endif
35+
}
36+
37+
} // namespace ripple

src/test/app/Loan_test.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,14 @@ class Loan_test : public beast::unit_test::suite
350350
env.balance(account, broker.asset) -
351351
(balanceBefore - balanceChangeAmount),
352352
borrowerScale);
353-
env.test.BEAST_EXPECT(
354-
roundToScale(difference, loanScale) >= beast::zero);
353+
env.test.expect(
354+
roundToScale(difference, loanScale) >= beast::zero,
355+
"Balance before: " + to_string(balanceBefore.value()) +
356+
", expected change: " + to_string(balanceChangeAmount) +
357+
", difference (balance after - expected): " +
358+
to_string(difference),
359+
__FILE__,
360+
__LINE__);
355361
}
356362
}
357363

@@ -2451,22 +2457,34 @@ class Loan_test : public beast::unit_test::suite
24512457
getCurrentState(env, broker, loanKeylet, verifyLoanStatus);
24522458
env.close();
24532459

2460+
BEAST_EXPECT(
2461+
STAmount(broker.asset, state.periodicPayment) ==
2462+
broker.asset(Number(8333457002039338267, -17)));
2463+
24542464
// Make all the payments in one transaction
24552465
// service fee is 2
24562466
auto const startingPayments = state.paymentRemaining;
24572467
STAmount const payoffAmount = [&]() {
2458-
NumberRoundModeGuard mg(Number::upward);
2468+
// NumberRoundModeGuard mg(Number::upward);
24592469
auto const rawPayoff = startingPayments *
24602470
(state.periodicPayment + broker.asset(2).value());
24612471
STAmount const payoffAmount{broker.asset, rawPayoff};
24622472
BEAST_EXPECTS(
24632473
payoffAmount ==
2464-
broker.asset(Number(1024014840139457, -12)),
2474+
broker.asset(Number(1024014840244721, -12)),
24652475
to_string(payoffAmount));
24662476
BEAST_EXPECT(payoffAmount > state.principalOutstanding);
24672477
return payoffAmount;
24682478
}();
24692479

2480+
auto const totalPayoffValue = state.totalValue +
2481+
startingPayments * broker.asset(2).value();
2482+
STAmount const totalPayoffAmount{
2483+
broker.asset, totalPayoffValue};
2484+
2485+
log << "Payoff amount: " << payoffAmount
2486+
<< ". Total Value: " << totalPayoffAmount << std::endl;
2487+
24702488
singlePayment(
24712489
loanKeylet,
24722490
verifyLoanStatus,
@@ -2653,7 +2671,7 @@ class Loan_test : public beast::unit_test::suite
26532671
roundedPeriodicPayment ==
26542672
roundToScale(
26552673
broker.asset(
2656-
Number(8333457001162141, -14), Number::upward),
2674+
Number(8333457002039338267, -17), Number::upward),
26572675
state.loanScale,
26582676
Number::upward));
26592677
// 83334570.01162141
@@ -2668,7 +2686,7 @@ class Loan_test : public beast::unit_test::suite
26682686
totalDue ==
26692687
roundToScale(
26702688
broker.asset(
2671-
Number(8533457001162141, -14), Number::upward),
2689+
Number(8533457002039338267, -17), Number::upward),
26722690
state.loanScale,
26732691
Number::upward));
26742692

@@ -2704,7 +2722,7 @@ class Loan_test : public beast::unit_test::suite
27042722
transactionAmount ==
27052723
roundToScale(
27062724
broker.asset(
2707-
Number(9533457001162141, -14), Number::upward),
2725+
Number(9533457002039400, -14), Number::upward),
27082726
state.loanScale,
27092727
Number::upward));
27102728

@@ -2805,7 +2823,7 @@ class Loan_test : public beast::unit_test::suite
28052823
Number::upward) ==
28062824
roundToScale(
28072825
broker.asset(
2808-
Number(8333228695260180, -14),
2826+
Number(8333228691531218890, -17),
28092827
Number::upward),
28102828
state.loanScale,
28112829
Number::upward));
@@ -3643,7 +3661,7 @@ class Loan_test : public beast::unit_test::suite
36433661
env(pay(issuer, borrower, mptAsset(10'000)));
36443662
env.close();
36453663

3646-
std::array const assets{xrpAsset, mptAsset, iouAsset};
3664+
std::array const assets{iouAsset, xrpAsset, mptAsset};
36473665

36483666
// Create vaults and loan brokers
36493667
std::vector<BrokerInfo> brokers;

0 commit comments

Comments
 (0)