mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Add test case to reproduce RIPD-3459
- Improve a few loan test helper functions. - Make Loan.GracePeriod a default field.
This commit is contained in:
@@ -540,7 +540,7 @@ LEDGER_ENTRY(ltLOAN, 0x0089, Loan, loan, ({
|
|||||||
{sfOverpaymentInterestRate, soeDEFAULT},
|
{sfOverpaymentInterestRate, soeDEFAULT},
|
||||||
{sfStartDate, soeREQUIRED},
|
{sfStartDate, soeREQUIRED},
|
||||||
{sfPaymentInterval, soeREQUIRED},
|
{sfPaymentInterval, soeREQUIRED},
|
||||||
{sfGracePeriod, soeREQUIRED},
|
{sfGracePeriod, soeDEFAULT},
|
||||||
{sfPreviousPaymentDate, soeDEFAULT},
|
{sfPreviousPaymentDate, soeDEFAULT},
|
||||||
{sfNextPaymentDueDate, soeOPTIONAL},
|
{sfNextPaymentDueDate, soeOPTIONAL},
|
||||||
// The loan object tracks these values:
|
// The loan object tracks these values:
|
||||||
|
|||||||
@@ -615,6 +615,19 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
case AssetType::MPT: {
|
case AssetType::MPT: {
|
||||||
|
// Enough to cover initial fees
|
||||||
|
if (!env.le(keylet::account(issuer)))
|
||||||
|
env.fund(
|
||||||
|
env.current()->fees().accountReserve(10) * 10, issuer);
|
||||||
|
if (!env.le(keylet::account(lender)))
|
||||||
|
env.fund(
|
||||||
|
env.current()->fees().accountReserve(10) * 10,
|
||||||
|
noripple(lender));
|
||||||
|
if (!env.le(keylet::account(borrower)))
|
||||||
|
env.fund(
|
||||||
|
env.current()->fees().accountReserve(10) * 10,
|
||||||
|
noripple(borrower));
|
||||||
|
|
||||||
MPTTester mptt{env, issuer, mptInitNoFund};
|
MPTTester mptt{env, issuer, mptInitNoFund};
|
||||||
mptt.create(
|
mptt.create(
|
||||||
{.flags =
|
{.flags =
|
||||||
@@ -644,7 +657,7 @@ protected:
|
|||||||
{
|
{
|
||||||
using namespace jtx;
|
using namespace jtx;
|
||||||
|
|
||||||
Env env(*this, beast::severities::kWarning);
|
Env env(*this);
|
||||||
|
|
||||||
auto const asset = createAsset(
|
auto const asset = createAsset(
|
||||||
env,
|
env,
|
||||||
@@ -653,15 +666,25 @@ protected:
|
|||||||
Account("issuer"),
|
Account("issuer"),
|
||||||
Account("lender"),
|
Account("lender"),
|
||||||
Account("borrower"));
|
Account("borrower"));
|
||||||
|
auto const principal = asset(loanParams.principalRequest).number();
|
||||||
|
auto const interest = loanParams.interest.value_or(TenthBips32{});
|
||||||
|
auto const interval =
|
||||||
|
loanParams.payInterval.value_or(LoanSet::defaultPaymentInterval);
|
||||||
|
auto const total =
|
||||||
|
loanParams.payTotal.value_or(LoanSet::defaultPaymentTotal);
|
||||||
auto const props = computeLoanProperties(
|
auto const props = computeLoanProperties(
|
||||||
asset,
|
asset,
|
||||||
asset(loanParams.principalRequest).number(),
|
principal,
|
||||||
loanParams.interest.value_or(TenthBips32{}),
|
interest,
|
||||||
loanParams.payInterval.value_or(LoanSet::defaultPaymentInterval),
|
interval,
|
||||||
loanParams.payTotal.value_or(LoanSet::defaultPaymentTotal),
|
total,
|
||||||
brokerParams.managementFeeRate,
|
brokerParams.managementFeeRate,
|
||||||
asset(brokerParams.vaultDeposit).number().exponent());
|
asset(brokerParams.vaultDeposit).number().exponent());
|
||||||
log << "Loan properties:\n"
|
log << "Loan properties:\n"
|
||||||
|
<< "\tPrincipal: " << principal << std::endl
|
||||||
|
<< "\tInterest rate: " << interest << std::endl
|
||||||
|
<< "\tPayment interval: " << interval << std::endl
|
||||||
|
<< "\tTotal Payments: " << total << std::endl
|
||||||
<< "\tPeriodic Payment: " << props.periodicPayment << std::endl
|
<< "\tPeriodic Payment: " << props.periodicPayment << std::endl
|
||||||
<< "\tTotal Value: " << props.totalValueOutstanding << std::endl
|
<< "\tTotal Value: " << props.totalValueOutstanding << std::endl
|
||||||
<< "\tManagement Fee: " << props.managementFeeOwedToBroker
|
<< "\tManagement Fee: " << props.managementFeeOwedToBroker
|
||||||
@@ -680,8 +703,7 @@ protected:
|
|||||||
env.journal));
|
env.journal));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<
|
std::optional<std::tuple<BrokerInfo, Keylet, jtx::Account>>
|
||||||
std::tuple<BrokerInfo, Keylet, VerifyLoanStatus, jtx::Account>>
|
|
||||||
createLoan(
|
createLoan(
|
||||||
jtx::Env& env,
|
jtx::Env& env,
|
||||||
AssetType assetType,
|
AssetType assetType,
|
||||||
@@ -707,7 +729,7 @@ protected:
|
|||||||
env(
|
env(
|
||||||
pay((asset.native() ? env.master : issuer),
|
pay((asset.native() ? env.master : issuer),
|
||||||
lender,
|
lender,
|
||||||
asset(brokerParams.vaultDeposit)));
|
asset(brokerParams.vaultDeposit + brokerParams.coverDeposit)));
|
||||||
// Fund the borrower later once we know the total loan
|
// Fund the borrower later once we know the total loan
|
||||||
// size
|
// size
|
||||||
|
|
||||||
@@ -746,10 +768,7 @@ protected:
|
|||||||
|
|
||||||
env.close();
|
env.close();
|
||||||
|
|
||||||
VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
|
return std::make_tuple(broker, loanKeylet, pseudoAcct);
|
||||||
|
|
||||||
return std::make_tuple(
|
|
||||||
broker, loanKeylet, verifyLoanStatus, pseudoAcct);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -919,10 +938,11 @@ protected:
|
|||||||
broker.params.managementFeeRate);
|
broker.params.managementFeeRate);
|
||||||
|
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
paymentComponents.trackedValueDelta == roundedPeriodicPayment ||
|
paymentComponents.trackedValueDelta <= roundedPeriodicPayment ||
|
||||||
(paymentComponents.specialCase ==
|
(paymentComponents.specialCase ==
|
||||||
detail::PaymentSpecialCase::final &&
|
detail::PaymentSpecialCase::final &&
|
||||||
paymentComponents.trackedValueDelta < roundedPeriodicPayment));
|
paymentComponents.trackedValueDelta >=
|
||||||
|
roundedPeriodicPayment));
|
||||||
BEAST_EXPECT(
|
BEAST_EXPECT(
|
||||||
paymentComponents.trackedValueDelta ==
|
paymentComponents.trackedValueDelta ==
|
||||||
paymentComponents.trackedPrincipalDelta +
|
paymentComponents.trackedPrincipalDelta +
|
||||||
@@ -1092,7 +1112,9 @@ protected:
|
|||||||
|
|
||||||
auto broker = std::get<BrokerInfo>(*loanResult);
|
auto broker = std::get<BrokerInfo>(*loanResult);
|
||||||
auto loanKeylet = std::get<Keylet>(*loanResult);
|
auto loanKeylet = std::get<Keylet>(*loanResult);
|
||||||
auto verifyLoanStatus = std::get<VerifyLoanStatus>(*loanResult);
|
auto pseudoAcct = std::get<Account>(*loanResult);
|
||||||
|
|
||||||
|
VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
|
||||||
|
|
||||||
makeLoanPayments(env, broker, loanParams, loanKeylet, verifyLoanStatus);
|
makeLoanPayments(env, broker, loanParams, loanKeylet, verifyLoanStatus);
|
||||||
}
|
}
|
||||||
@@ -6198,25 +6220,20 @@ protected:
|
|||||||
.payInterval = 150,
|
.payInterval = 150,
|
||||||
.gracePd = 0};
|
.gracePd = 0};
|
||||||
|
|
||||||
describeLoan(brokerParams, loanParams, AssetType::XRP);
|
auto const assetType = AssetType::XRP;
|
||||||
|
|
||||||
|
describeLoan(brokerParams, loanParams, assetType);
|
||||||
|
|
||||||
Env env(*this, all);
|
Env env(*this, all);
|
||||||
|
|
||||||
auto loanResult = createLoan(
|
auto loanResult = createLoan(
|
||||||
env,
|
env, assetType, brokerParams, loanParams, issuer, lender, borrower);
|
||||||
AssetType::XRP,
|
|
||||||
brokerParams,
|
|
||||||
loanParams,
|
|
||||||
issuer,
|
|
||||||
lender,
|
|
||||||
borrower);
|
|
||||||
|
|
||||||
if (!BEAST_EXPECT(loanResult))
|
if (!BEAST_EXPECT(loanResult))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto broker = std::get<BrokerInfo>(*loanResult);
|
auto broker = std::get<BrokerInfo>(*loanResult);
|
||||||
auto loanKeylet = std::get<Keylet>(*loanResult);
|
auto loanKeylet = std::get<Keylet>(*loanResult);
|
||||||
// auto verifyLoanStatus = std::get<VerifyLoanStatus>(*loanResult);
|
|
||||||
|
|
||||||
using tp = NetClock::time_point;
|
using tp = NetClock::time_point;
|
||||||
using d = NetClock::duration;
|
using d = NetClock::duration;
|
||||||
@@ -6264,12 +6281,82 @@ protected:
|
|||||||
env.close();
|
env.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testRIPD3459()
|
||||||
|
{
|
||||||
|
testcase("RIPD-3459 - LoanBroker incorrect debt total");
|
||||||
|
|
||||||
|
using namespace jtx;
|
||||||
|
|
||||||
|
Account const issuer("issuer");
|
||||||
|
Account const lender("lender");
|
||||||
|
Account const borrower("borrower");
|
||||||
|
|
||||||
|
BrokerParameters const brokerParams{
|
||||||
|
.vaultDeposit = 200'000,
|
||||||
|
.debtMax = 0,
|
||||||
|
.coverRateMin = TenthBips32{0},
|
||||||
|
// .managementFeeRate = TenthBips16{5919},
|
||||||
|
.coverRateLiquidation = TenthBips32{0}};
|
||||||
|
LoanParameters const loanParams{
|
||||||
|
.account = lender,
|
||||||
|
.counter = borrower,
|
||||||
|
.principalRequest = Number{100'000, -4},
|
||||||
|
.interest = TenthBips32{100'000},
|
||||||
|
.payTotal = 10,
|
||||||
|
// Guess
|
||||||
|
// .payInterval = 10,
|
||||||
|
.gracePd = 0};
|
||||||
|
|
||||||
|
auto const assetType = AssetType::MPT;
|
||||||
|
|
||||||
|
describeLoan(brokerParams, loanParams, assetType);
|
||||||
|
|
||||||
|
Env env(*this, all);
|
||||||
|
|
||||||
|
auto loanResult = createLoan(
|
||||||
|
env, assetType, brokerParams, loanParams, issuer, lender, borrower);
|
||||||
|
|
||||||
|
if (!BEAST_EXPECT(loanResult))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto broker = std::get<BrokerInfo>(*loanResult);
|
||||||
|
auto loanKeylet = std::get<Keylet>(*loanResult);
|
||||||
|
auto pseudoAcct = std::get<Account>(*loanResult);
|
||||||
|
|
||||||
|
VerifyLoanStatus verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
|
||||||
|
|
||||||
|
if (auto const brokerSle = env.le(broker.brokerKeylet());
|
||||||
|
BEAST_EXPECT(brokerSle))
|
||||||
|
{
|
||||||
|
if (auto const loanSle = env.le(loanKeylet); BEAST_EXPECT(loanSle))
|
||||||
|
{
|
||||||
|
BEAST_EXPECT(
|
||||||
|
brokerSle->at(sfDebtTotal) ==
|
||||||
|
loanSle->at(sfTotalValueOutstanding));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeLoanPayments(env, broker, loanParams, loanKeylet, verifyLoanStatus);
|
||||||
|
|
||||||
|
if (auto const brokerSle = env.le(broker.brokerKeylet());
|
||||||
|
BEAST_EXPECT(brokerSle))
|
||||||
|
{
|
||||||
|
if (auto const loanSle = env.le(loanKeylet); BEAST_EXPECT(loanSle))
|
||||||
|
{
|
||||||
|
log << pretty(brokerSle->getJson()) << std::endl
|
||||||
|
<< pretty(loanSle->getJson()) << std::endl;
|
||||||
|
BEAST_EXPECT(
|
||||||
|
brokerSle->at(sfDebtTotal) ==
|
||||||
|
loanSle->at(sfTotalValueOutstanding));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
run() override
|
run() override
|
||||||
{
|
{
|
||||||
testRIPD3831();
|
|
||||||
|
|
||||||
#if LOANTODO
|
#if LOANTODO
|
||||||
testCoverDepositAllowsNonTransferableMPT();
|
testCoverDepositAllowsNonTransferableMPT();
|
||||||
testLoanPayLateFullPaymentBypassesPenalties();
|
testLoanPayLateFullPaymentBypassesPenalties();
|
||||||
@@ -6305,6 +6392,9 @@ public:
|
|||||||
testLoanNextPaymentDueDateOverflow();
|
testLoanNextPaymentDueDateOverflow();
|
||||||
|
|
||||||
testRequireAuth();
|
testRequireAuth();
|
||||||
|
|
||||||
|
testRIPD3831();
|
||||||
|
testRIPD3459();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user