mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-29 10:00:30 +00:00
Add a few minor changes (#6158)
- Updates or fixes a couple of things I noticed while reviewing changes to the spec. - Rename sfPreviousPaymentDate to sfPreviousPaymentDueDate. - Make the vault asset cap check added in #6124 a little more robust: 1. Check in preflight if the vault is _already_ over the limit. 2. Prevent overflow when checking with the loan value. (Subtract instead of adding, in case the values are near maxint. Both return the same result. Also add a unit test so each case is covered.
This commit is contained in:
@@ -541,7 +541,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
||||
{sfStartDate, soeREQUIRED},
|
||||
{sfPaymentInterval, soeREQUIRED},
|
||||
{sfGracePeriod, soeDEFAULT},
|
||||
{sfPreviousPaymentDate, soeDEFAULT},
|
||||
{sfPreviousPaymentDueDate, soeDEFAULT},
|
||||
{sfNextPaymentDueDate, soeDEFAULT},
|
||||
// The loan object tracks these values:
|
||||
//
|
||||
|
||||
@@ -102,7 +102,7 @@ TYPED_SFIELD(sfMutableFlags, UINT32, 53)
|
||||
TYPED_SFIELD(sfStartDate, UINT32, 54)
|
||||
TYPED_SFIELD(sfPaymentInterval, UINT32, 55)
|
||||
TYPED_SFIELD(sfGracePeriod, UINT32, 56)
|
||||
TYPED_SFIELD(sfPreviousPaymentDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfPreviousPaymentDueDate, UINT32, 57)
|
||||
TYPED_SFIELD(sfNextPaymentDueDate, UINT32, 58)
|
||||
TYPED_SFIELD(sfPaymentRemaining, UINT32, 59)
|
||||
TYPED_SFIELD(sfPaymentTotal, UINT32, 60)
|
||||
|
||||
@@ -372,7 +372,7 @@ protected:
|
||||
if (auto loan = env.le(loanKeylet); env.test.BEAST_EXPECT(loan))
|
||||
{
|
||||
env.test.BEAST_EXPECT(
|
||||
loan->at(sfPreviousPaymentDate) == previousPaymentDate);
|
||||
loan->at(sfPreviousPaymentDueDate) == previousPaymentDate);
|
||||
env.test.BEAST_EXPECT(
|
||||
loan->at(sfPaymentRemaining) == paymentRemaining);
|
||||
env.test.BEAST_EXPECT(
|
||||
@@ -507,7 +507,7 @@ protected:
|
||||
if (auto loan = env.le(loanKeylet); BEAST_EXPECT(loan))
|
||||
{
|
||||
return LoanState{
|
||||
.previousPaymentDate = loan->at(sfPreviousPaymentDate),
|
||||
.previousPaymentDate = loan->at(sfPreviousPaymentDueDate),
|
||||
.startDate = tp{d{loan->at(sfStartDate)}},
|
||||
.nextPaymentDate = loan->at(sfNextPaymentDueDate),
|
||||
.paymentRemaining = loan->at(sfPaymentRemaining),
|
||||
@@ -1454,7 +1454,7 @@ protected:
|
||||
BEAST_EXPECT(
|
||||
loan->at(sfPaymentInterval) == *loanParams.payInterval);
|
||||
BEAST_EXPECT(loan->at(sfGracePeriod) == *loanParams.gracePd);
|
||||
BEAST_EXPECT(loan->at(sfPreviousPaymentDate) == 0);
|
||||
BEAST_EXPECT(loan->at(sfPreviousPaymentDueDate) == 0);
|
||||
BEAST_EXPECT(
|
||||
loan->at(sfNextPaymentDueDate) ==
|
||||
startDate + *loanParams.payInterval);
|
||||
@@ -3597,13 +3597,39 @@ protected:
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
testcase("Vault maximum value exceeded");
|
||||
testcase("Vault at maximum value");
|
||||
env(set(issuer, broker.brokerID, principalRequest),
|
||||
counterparty(lender),
|
||||
interestRate(TenthBips32(10'000)),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
fee(env.current()->fees().base * 5),
|
||||
ter(tecLIMIT_EXCEEDED));
|
||||
ter(tecLIMIT_EXCEEDED),
|
||||
THISLINE);
|
||||
},
|
||||
nullptr);
|
||||
|
||||
testCase(
|
||||
[&, this](Env& env, BrokerInfo const& broker, auto&) {
|
||||
using namespace loan;
|
||||
Number const principalRequest = broker.asset(1'000).value();
|
||||
Vault vault{env};
|
||||
auto tx = vault.set({.owner = lender, .id = broker.vaultID});
|
||||
tx[sfAssetsMaximum] =
|
||||
BrokerParameters::defaults().vaultDeposit +
|
||||
broker.asset(1).number();
|
||||
env(tx);
|
||||
env.close();
|
||||
|
||||
testcase("Vault maximum value exceeded");
|
||||
env(set(issuer, broker.brokerID, principalRequest),
|
||||
counterparty(lender),
|
||||
interestRate(TenthBips32(100'000)),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
fee(env.current()->fees().base * 5),
|
||||
paymentTotal(2),
|
||||
paymentInterval(3600 * 24),
|
||||
ter(tecLIMIT_EXCEEDED),
|
||||
THISLINE);
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
@@ -3841,7 +3867,7 @@ protected:
|
||||
BEAST_EXPECT(loan[sfPaymentInterval] == 60);
|
||||
BEAST_EXPECT(loan[sfPeriodicPayment] == "1000000000");
|
||||
BEAST_EXPECT(loan[sfPaymentRemaining] == 1);
|
||||
BEAST_EXPECT(!loan.isMember(sfPreviousPaymentDate));
|
||||
BEAST_EXPECT(!loan.isMember(sfPreviousPaymentDueDate));
|
||||
BEAST_EXPECT(loan[sfPrincipalOutstanding] == "1000000000");
|
||||
BEAST_EXPECT(loan[sfTotalValueOutstanding] == "1000000000");
|
||||
BEAST_EXPECT(!loan.isMember(sfLoanScale));
|
||||
@@ -6048,7 +6074,7 @@ protected:
|
||||
{
|
||||
// --- PoC Summary ----------------------------------------------------
|
||||
// Scenario: Borrower makes one periodic payment early (before next due)
|
||||
// so doPayment sets sfPreviousPaymentDate to the (future)
|
||||
// so doPayment sets sfPreviousPaymentDueDate to the (future)
|
||||
// sfNextPaymentDueDate and advances sfNextPaymentDueDate by one
|
||||
// interval. Borrower then immediately performs a full-payment
|
||||
// (tfLoanFullPayment). Why it matters: Full-payment interest accrual
|
||||
|
||||
@@ -1669,7 +1669,7 @@ loanMakePayment(
|
||||
|
||||
Number const periodicPayment = loan->at(sfPeriodicPayment);
|
||||
|
||||
auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDate);
|
||||
auto prevPaymentDateProxy = loan->at(sfPreviousPaymentDueDate);
|
||||
std::uint32_t const startDate = loan->at(sfStartDate);
|
||||
|
||||
std::uint32_t const paymentInterval = loan->at(sfPaymentInterval);
|
||||
|
||||
@@ -374,7 +374,8 @@ LoanManage::unimpairLoan(
|
||||
loanSle->clearFlag(lsfLoanImpaired);
|
||||
auto const paymentInterval = loanSle->at(sfPaymentInterval);
|
||||
auto const normalPaymentDueDate =
|
||||
std::max(loanSle->at(sfPreviousPaymentDate), loanSle->at(sfStartDate)) +
|
||||
std::max(
|
||||
loanSle->at(sfPreviousPaymentDueDate), loanSle->at(sfStartDate)) +
|
||||
paymentInterval;
|
||||
if (!hasExpired(view, normalPaymentDueDate))
|
||||
{
|
||||
|
||||
@@ -282,6 +282,15 @@ LoanSet::preclaim(PreclaimContext const& ctx)
|
||||
if (!vault)
|
||||
// Should be impossible
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
if (vault->at(sfAssetsMaximum) != 0 &&
|
||||
vault->at(sfAssetsTotal) >= vault->at(sfAssetsMaximum))
|
||||
{
|
||||
JLOG(ctx.j.warn())
|
||||
<< "Vault at maximum assets limit. Can't add another loan.";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
Asset const asset = vault->at(sfAsset);
|
||||
|
||||
auto const vaultPseudo = vault->at(sfAccount);
|
||||
@@ -411,8 +420,12 @@ LoanSet::doApply()
|
||||
principalRequested,
|
||||
properties.loanState.managementFeeDue);
|
||||
|
||||
if (vaultSle->at(sfAssetsMaximum) != 0 &&
|
||||
vaultTotalProxy + state.interestDue > vaultSle->at(sfAssetsMaximum))
|
||||
auto const vaultMaximum = *vaultSle->at(sfAssetsMaximum);
|
||||
XRPL_ASSERT_PARTS(
|
||||
vaultMaximum == 0 || vaultMaximum > *vaultTotalProxy,
|
||||
"ripple::LoanSet::doApply",
|
||||
"Vault is below maximum limit");
|
||||
if (vaultMaximum != 0 && state.interestDue > vaultMaximum - vaultTotalProxy)
|
||||
{
|
||||
JLOG(j_.warn()) << "Loan would exceed the maximum assets of the vault";
|
||||
return tecLIMIT_EXCEEDED;
|
||||
@@ -600,7 +613,7 @@ LoanSet::doApply()
|
||||
loan->at(sfTotalValueOutstanding) = properties.loanState.valueOutstanding;
|
||||
loan->at(sfManagementFeeOutstanding) =
|
||||
properties.loanState.managementFeeDue;
|
||||
loan->at(sfPreviousPaymentDate) = 0;
|
||||
loan->at(sfPreviousPaymentDueDate) = 0;
|
||||
loan->at(sfNextPaymentDueDate) = startDate + paymentInterval;
|
||||
loan->at(sfPaymentRemaining) = paymentTotal;
|
||||
view.insert(loan);
|
||||
|
||||
Reference in New Issue
Block a user