mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Extend LoanBroaker and Loan unit-tests. (#5863)
- Add convenience functions to MPT test-framework.
This commit is contained in:
committed by
GitHub
parent
9f5bc8f0da
commit
f60e298627
@@ -851,12 +851,335 @@ class LoanBroker_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(env.ownerCount(alice) == aliceOriginalCount);
|
||||
}
|
||||
|
||||
enum LoanBrokerTest {
|
||||
CoverClawback,
|
||||
CoverDeposit,
|
||||
CoverWithdraw,
|
||||
Delete,
|
||||
Set
|
||||
};
|
||||
|
||||
void
|
||||
testLoanBroker(
|
||||
std::function<jtx::PrettyAsset(
|
||||
jtx::Env&,
|
||||
jtx::Account const&,
|
||||
jtx::Account const&)> getAsset,
|
||||
LoanBrokerTest brokerTest)
|
||||
{
|
||||
using namespace jtx;
|
||||
using namespace loanBroker;
|
||||
Account const issuer{"issuer"};
|
||||
Account const alice{"alice"};
|
||||
Env env(*this);
|
||||
Vault vault{env};
|
||||
|
||||
env.fund(XRP(100'000), issuer, alice);
|
||||
env.close();
|
||||
|
||||
PrettyAsset const asset = [&]() {
|
||||
if (getAsset)
|
||||
return getAsset(env, issuer, alice);
|
||||
env(trust(alice, issuer["IOU"](1'000'000)));
|
||||
env.close();
|
||||
return PrettyAsset(issuer["IOU"]);
|
||||
}();
|
||||
|
||||
env(pay(issuer, alice, asset(100'000)));
|
||||
env.close();
|
||||
|
||||
auto [tx, vaultKeylet] = vault.create({.owner = alice, .asset = asset});
|
||||
env(tx);
|
||||
env.close();
|
||||
auto const le = env.le(vaultKeylet);
|
||||
VaultInfo vaultInfo = [&]() {
|
||||
if (BEAST_EXPECT(le))
|
||||
return VaultInfo{asset, vaultKeylet.key, le->at(sfAccount)};
|
||||
return VaultInfo{asset, {}, {}};
|
||||
}();
|
||||
if (vaultInfo.vaultID == uint256{})
|
||||
return;
|
||||
|
||||
env(vault.deposit(
|
||||
{.depositor = alice, .id = vaultKeylet.key, .amount = asset(50)}));
|
||||
env.close();
|
||||
|
||||
auto const brokerKeylet =
|
||||
keylet::loanbroker(alice.id(), env.seq(alice));
|
||||
env(set(alice, vaultInfo.vaultID));
|
||||
env.close();
|
||||
|
||||
auto broker = env.le(brokerKeylet);
|
||||
if (!BEAST_EXPECT(broker))
|
||||
return;
|
||||
|
||||
if (brokerTest == CoverDeposit)
|
||||
{
|
||||
// preclaim: tecWRONG_ASET
|
||||
env(coverDeposit(alice, brokerKeylet.key, issuer["BAD"](10)),
|
||||
ter(tecWRONG_ASSET));
|
||||
|
||||
// preclaim: tecINSUFFICIENT_FUNDS
|
||||
env(pay(alice, issuer, asset(100'000 - 50)));
|
||||
env.close();
|
||||
env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
|
||||
ter(tecINSUFFICIENT_FUNDS));
|
||||
|
||||
// preclaim: tecFROZEN
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
|
||||
ter(tecFROZEN));
|
||||
}
|
||||
else
|
||||
// Fund the cover deposit
|
||||
env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)));
|
||||
env.close();
|
||||
|
||||
if (brokerTest == CoverWithdraw)
|
||||
{
|
||||
// preclaim: tecWRONG_ASSSET
|
||||
env(coverWithdraw(alice, brokerKeylet.key, issuer["BAD"](10)),
|
||||
ter(tecWRONG_ASSET));
|
||||
|
||||
// preclaim: tecNO_DST
|
||||
Account const bogus{"bogus"};
|
||||
env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
|
||||
destination(bogus),
|
||||
ter(tecNO_DST));
|
||||
|
||||
// preclaim: tecDST_TAG_NEEDED
|
||||
Account const dest{"dest"};
|
||||
env.fund(XRP(1'000), dest);
|
||||
env(fset(dest, asfRequireDest));
|
||||
env.close();
|
||||
env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
|
||||
destination(dest),
|
||||
ter(tecDST_TAG_NEEDED));
|
||||
|
||||
// preclaim: tecNO_PERMISSION
|
||||
env(fclear(dest, asfRequireDest));
|
||||
env(fset(dest, asfDepositAuth));
|
||||
env.close();
|
||||
env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
|
||||
destination(dest),
|
||||
ter(tecNO_PERMISSION));
|
||||
|
||||
// preclaim: tecFROZEN
|
||||
env(trust(dest, asset(1'000)));
|
||||
env(fclear(dest, asfDepositAuth));
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
|
||||
destination(dest),
|
||||
ter(tecFROZEN));
|
||||
|
||||
// preclaim:: tecFROZEN (deep frozen)
|
||||
env(fclear(issuer, asfGlobalFreeze));
|
||||
env(trust(
|
||||
issuer, asset(1'000), dest, tfSetFreeze | tfSetDeepFreeze));
|
||||
env(coverWithdraw(alice, brokerKeylet.key, asset(10)),
|
||||
destination(dest),
|
||||
ter(tecFROZEN));
|
||||
}
|
||||
|
||||
if (brokerTest == CoverClawback)
|
||||
{
|
||||
if (asset.holds<Issue>())
|
||||
{
|
||||
// preclaim: AllowTrustLineClaback is not set
|
||||
env(coverClawback(issuer),
|
||||
loanBrokerID(brokerKeylet.key),
|
||||
amount(vaultInfo.asset(2)),
|
||||
ter(tecNO_PERMISSION));
|
||||
|
||||
// preclaim: NoFreeze is set
|
||||
env(fset(issuer, asfAllowTrustLineClawback | asfNoFreeze));
|
||||
env.close();
|
||||
env(coverClawback(issuer),
|
||||
loanBrokerID(brokerKeylet.key),
|
||||
amount(vaultInfo.asset(2)),
|
||||
ter(tecNO_PERMISSION));
|
||||
}
|
||||
else
|
||||
{
|
||||
// preclaim: MPTCanClawback is not set or MPTCAnLock is not set
|
||||
env(coverClawback(issuer),
|
||||
loanBrokerID(brokerKeylet.key),
|
||||
amount(vaultInfo.asset(2)),
|
||||
ter(tecNO_PERMISSION));
|
||||
}
|
||||
env.close();
|
||||
}
|
||||
|
||||
if (brokerTest == Delete)
|
||||
{
|
||||
Account const borrower{"borrower"};
|
||||
env.fund(XRP(1'000), borrower);
|
||||
env(loan::set(borrower, brokerKeylet.key, asset(50).value()),
|
||||
sig(sfCounterpartySignature, alice),
|
||||
fee(env.current()->fees().base * 2));
|
||||
|
||||
// preclaim: tecHAS_OBLIGATIONS
|
||||
env(del(alice, brokerKeylet.key), ter(tecHAS_OBLIGATIONS));
|
||||
}
|
||||
else
|
||||
env(del(alice, brokerKeylet.key));
|
||||
|
||||
if (brokerTest == Set)
|
||||
{
|
||||
if (asset.holds<Issue>())
|
||||
{
|
||||
env(fclear(issuer, asfDefaultRipple));
|
||||
env.close();
|
||||
// preclaim: DefaultRipple is not set
|
||||
env(set(alice, vaultInfo.vaultID), ter(terNO_RIPPLE));
|
||||
|
||||
env(fset(issuer, asfDefaultRipple));
|
||||
env.close();
|
||||
}
|
||||
|
||||
auto const amt = env.balance(alice) -
|
||||
env.current()->fees().accountReserve(env.ownerCount(alice));
|
||||
env(pay(alice, issuer, amt));
|
||||
|
||||
// preclaim:: tecINSUFFICIENT_RESERVE
|
||||
env(set(alice, vaultInfo.vaultID), ter(tecINSUFFICIENT_RESERVE));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanBrokerCoverClawback()
|
||||
{
|
||||
testcase("Invalid LoanBrokerCoverClawback");
|
||||
using namespace jtx;
|
||||
using namespace loanBroker;
|
||||
|
||||
// preflight
|
||||
{
|
||||
Account const alice{"alice"};
|
||||
Account const issuer{"issuer"};
|
||||
auto const USD = alice["USD"];
|
||||
Env env(*this);
|
||||
env.fund(XRP(100'000), alice);
|
||||
env.close();
|
||||
|
||||
auto jtx = env.jt(coverClawback(alice), amount(USD(100)));
|
||||
|
||||
// holder == account
|
||||
env(jtx, ter(temINVALID));
|
||||
|
||||
// holder == beast::zero
|
||||
STAmount bad(Issue{USD.currency, beast::zero}, 100);
|
||||
jtx.jv[sfAmount] = bad.getJson();
|
||||
jtx.stx = env.ust(jtx);
|
||||
Serializer s;
|
||||
jtx.stx->add(s);
|
||||
auto const jrr = env.rpc("submit", strHex(s.slice()))[jss::result];
|
||||
// fails in doSubmit() on STTx construction
|
||||
BEAST_EXPECT(jrr[jss::error] == "invalidTransaction");
|
||||
BEAST_EXPECT(jrr[jss::error_exception] == "invalid native account");
|
||||
}
|
||||
|
||||
// preclaim
|
||||
|
||||
// Issue:
|
||||
// AllowTrustLineClawback is not set or NoFreeze is set
|
||||
testLoanBroker({}, CoverClawback);
|
||||
|
||||
// MPTIssue:
|
||||
// MPTCanClawback is not set
|
||||
testLoanBroker(
|
||||
[&](Env& env, Account const& issuer, Account const& alice) -> MPT {
|
||||
MPTTester mpt(
|
||||
{.env = env, .issuer = issuer, .holders = {alice}});
|
||||
return mpt;
|
||||
},
|
||||
CoverClawback);
|
||||
// MPTCanLock is not set
|
||||
testLoanBroker(
|
||||
[&](Env& env, Account const& issuer, Account const& alice) -> MPT {
|
||||
MPTTester mpt(
|
||||
{.env = env,
|
||||
.issuer = issuer,
|
||||
.holders = {alice},
|
||||
.flags = MPTDEXFlags | tfMPTCanClawback});
|
||||
return mpt;
|
||||
},
|
||||
CoverClawback);
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanBrokerCoverDeposit()
|
||||
{
|
||||
testcase("Invalid LoanBrokerCoverDeposit");
|
||||
using namespace jtx;
|
||||
|
||||
// preclaim:
|
||||
// tecWRONG_ASSET, tecINSUFFICIENT_FUNDS, frozen asset
|
||||
testLoanBroker({}, CoverDeposit);
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanBrokerCoverWithdraw()
|
||||
{
|
||||
testcase("Invalid LoanBrokerCoverWithdraw");
|
||||
using namespace jtx;
|
||||
|
||||
/*
|
||||
preflight: illegal net
|
||||
isLegalNet() check is probably redundant. STAmount parsing
|
||||
should throw an exception on deserialize
|
||||
|
||||
preclaim: tecWRONG_ASSET, tecNO_DST, tecDST_TAG_NEEDED,
|
||||
tecNO_PERMISSION, checkFrozen failure, checkDeepFrozenFailure,
|
||||
second+third tecINSUFFICIENT_FUNDS (can this happen)?
|
||||
doApply: tecPATH_DRY (can it happen, funds already checked?)
|
||||
*/
|
||||
testLoanBroker({}, CoverWithdraw);
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanBrokerDelete()
|
||||
{
|
||||
using namespace jtx;
|
||||
testcase("Invalid LoanBrokerDelete");
|
||||
/*
|
||||
preclaim: tecHAS_OBLIGATIONS
|
||||
doApply:
|
||||
accountSend failure, removeEmptyHolding failure,
|
||||
all tecHAS_OBLIGATIONS (can any of these happen?)
|
||||
*/
|
||||
testLoanBroker({}, Delete);
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanBrokerSet()
|
||||
{
|
||||
using namespace jtx;
|
||||
testcase("Invalid LoanBrokerSet");
|
||||
|
||||
/*preclaim: canAddHolding failure (can it happen with MPT?
|
||||
can't create Vault if CanTransfer is not enabled.)
|
||||
doApply:
|
||||
first+second dirLink failure, createPseudoAccount failure,
|
||||
addEmptyHolding failure
|
||||
can any of these happen?
|
||||
*/
|
||||
testLoanBroker({}, Set);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testDisabled();
|
||||
testLifecycle();
|
||||
testInvalidLoanBrokerCoverClawback();
|
||||
testInvalidLoanBrokerCoverDeposit();
|
||||
testInvalidLoanBrokerCoverWithdraw();
|
||||
testInvalidLoanBrokerDelete();
|
||||
testInvalidLoanBrokerSet();
|
||||
|
||||
// TODO: Write clawback failure tests with an issuer / MPT that doesn't
|
||||
// have the right flags set.
|
||||
|
||||
@@ -2827,6 +2827,224 @@ class Loan_test : public beast::unit_test::suite
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanDelete()
|
||||
{
|
||||
testcase("Invalid LoanDelete");
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
|
||||
// preflight: temINVALID, LoanID == zero
|
||||
{
|
||||
Account const alice{"alice"};
|
||||
Env env(*this);
|
||||
env.fund(XRP(1'000), alice);
|
||||
env.close();
|
||||
env(del(alice, beast::zero), ter(temINVALID));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanManage()
|
||||
{
|
||||
testcase("Invalid LoanManage");
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
|
||||
// preflight: temINVALID, LoanID == zero
|
||||
{
|
||||
Account const alice{"alice"};
|
||||
Env env(*this);
|
||||
env.fund(XRP(1'000), alice);
|
||||
env.close();
|
||||
env(manage(alice, beast::zero, tfLoanDefault), ter(temINVALID));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanPay()
|
||||
{
|
||||
testcase("Invalid LoanPay");
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
Account const lender{"lender"};
|
||||
Account const issuer{"issuer"};
|
||||
Account const borrower{"borrower"};
|
||||
auto const IOU = issuer["IOU"];
|
||||
|
||||
// preclaim
|
||||
Env env(*this);
|
||||
env.fund(XRP(1'000), lender, issuer, borrower);
|
||||
env(trust(lender, IOU(10'000'000)));
|
||||
env(pay(issuer, lender, IOU(5'000'000)));
|
||||
BrokerInfo brokerInfo{createVaultAndBroker(env, issuer["IOU"], lender)};
|
||||
|
||||
auto const loanSetFee = fee(env.current()->fees().base * 2);
|
||||
STAmount const debtMaximumRequest = brokerInfo.asset(1'000).value();
|
||||
|
||||
env(set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee);
|
||||
|
||||
env.close();
|
||||
|
||||
std::uint32_t const loanSequence = 1;
|
||||
auto const loanKeylet = keylet::loan(brokerInfo.brokerID, loanSequence);
|
||||
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
// preclaim: tecFROZEN
|
||||
env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN));
|
||||
env.close();
|
||||
|
||||
env(fclear(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
auto const pseudoBroker = [&]() -> std::optional<Account> {
|
||||
if (auto brokerSle =
|
||||
env.le(keylet::loanbroker(brokerInfo.brokerID));
|
||||
BEAST_EXPECT(brokerSle))
|
||||
{
|
||||
return Account{"pseudo", brokerSle->at(sfAccount)};
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}();
|
||||
if (!pseudoBroker)
|
||||
return;
|
||||
|
||||
// Lender and pseudoaccount must both be frozen
|
||||
env(trust(
|
||||
issuer,
|
||||
lender["IOU"](1'000),
|
||||
lender,
|
||||
tfSetFreeze | tfSetDeepFreeze));
|
||||
env(trust(
|
||||
issuer,
|
||||
(*pseudoBroker)["IOU"](1'000),
|
||||
*pseudoBroker,
|
||||
tfSetFreeze | tfSetDeepFreeze));
|
||||
env.close();
|
||||
|
||||
// preclaim: tecFROZEN due to deep frozen
|
||||
env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecFROZEN));
|
||||
env.close();
|
||||
|
||||
// Only one needs to be unfrozen
|
||||
env(trust(
|
||||
issuer, lender["IOU"](1'000), tfClearFreeze | tfClearDeepFreeze));
|
||||
env.close();
|
||||
|
||||
env(pay(borrower, loanKeylet.key, debtMaximumRequest));
|
||||
env.close();
|
||||
|
||||
// preclaim: tecKILLED
|
||||
// note that tecKILLED in loanMakePayment()
|
||||
// doesn't happen because of the preclaim check.
|
||||
env(pay(borrower, loanKeylet.key, debtMaximumRequest), ter(tecKILLED));
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanSet()
|
||||
{
|
||||
testcase("Invalid LoanSet");
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
Account const lender{"lender"};
|
||||
Account const issuer{"issuer"};
|
||||
Account const borrower{"borrower"};
|
||||
auto const IOU = issuer["IOU"];
|
||||
|
||||
auto testWrapper = [&](auto&& test) {
|
||||
Env env(*this);
|
||||
env.fund(XRP(1'000), lender, issuer, borrower);
|
||||
env(trust(lender, IOU(10'000'000)));
|
||||
env(pay(issuer, lender, IOU(5'000'000)));
|
||||
BrokerInfo brokerInfo{
|
||||
createVaultAndBroker(env, issuer["IOU"], lender)};
|
||||
|
||||
auto const loanSetFee = fee(env.current()->fees().base * 2);
|
||||
Number const debtMaximumRequest = brokerInfo.asset(1'000).value();
|
||||
test(env, brokerInfo, loanSetFee, debtMaximumRequest);
|
||||
};
|
||||
|
||||
// preflight:
|
||||
testWrapper([&](Env& env,
|
||||
BrokerInfo const& brokerInfo,
|
||||
jtx::fee const& loanSetFee,
|
||||
Number const& debtMaximumRequest) {
|
||||
// first temBAD_SIGNER: TODO
|
||||
|
||||
// preflightCheckSigningKey() failure:
|
||||
// can it happen? the signature is checked before transactor
|
||||
// executes
|
||||
|
||||
JTx tx = env.jt(
|
||||
set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee);
|
||||
STTx local = *(tx.stx);
|
||||
auto counterpartySig =
|
||||
local.getFieldObject(sfCounterpartySignature);
|
||||
auto badPubKey = counterpartySig.getFieldVL(sfSigningPubKey);
|
||||
badPubKey[20] ^= 0xAA;
|
||||
counterpartySig.setFieldVL(sfSigningPubKey, badPubKey);
|
||||
local.setFieldObject(sfCounterpartySignature, counterpartySig);
|
||||
Json::Value jvResult;
|
||||
jvResult[jss::tx_blob] = strHex(local.getSerializer().slice());
|
||||
auto res = env.rpc("json", "submit", to_string(jvResult))["result"];
|
||||
BEAST_EXPECT(
|
||||
res[jss::error] == "invalidTransaction" &&
|
||||
res[jss::error_exception] ==
|
||||
"fails local checks: Counterparty: Invalid signature.");
|
||||
});
|
||||
|
||||
// preclaim:
|
||||
testWrapper([&](Env& env,
|
||||
BrokerInfo const& brokerInfo,
|
||||
jtx::fee const& loanSetFee,
|
||||
Number const& debtMaximumRequest) {
|
||||
// canAddHoldingFailure (IOU only, if MPT doesn't have
|
||||
// MPTCanTransfer set, then can't create Vault/LoanBroker,
|
||||
// and LoanSet will fail with different error
|
||||
env(fclear(issuer, asfDefaultRipple));
|
||||
env.close();
|
||||
env(set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee,
|
||||
ter(terNO_RIPPLE));
|
||||
});
|
||||
|
||||
// doApply:
|
||||
testWrapper([&](Env& env,
|
||||
BrokerInfo const& brokerInfo,
|
||||
jtx::fee const& loanSetFee,
|
||||
Number const& debtMaximumRequest) {
|
||||
auto const amt = env.balance(borrower) -
|
||||
env.current()->fees().accountReserve(env.ownerCount(borrower));
|
||||
env(pay(borrower, issuer, amt));
|
||||
|
||||
// tecINSUFFICIENT_RESERVE
|
||||
env(set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee,
|
||||
ter(tecINSUFFICIENT_RESERVE));
|
||||
|
||||
// addEmptyHolding failure
|
||||
env(pay(issuer, borrower, amt));
|
||||
env(fset(issuer, asfGlobalFreeze));
|
||||
env.close();
|
||||
|
||||
env(set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee,
|
||||
ter(tecFROZEN));
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -2841,6 +3059,11 @@ public:
|
||||
|
||||
testRPC();
|
||||
testBasicMath();
|
||||
|
||||
testInvalidLoanDelete();
|
||||
testInvalidLoanManage();
|
||||
testInvalidLoanPay();
|
||||
testInvalidLoanSet();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ public:
|
||||
operator Asset() const;
|
||||
|
||||
private:
|
||||
using SLEP = std::shared_ptr<SLE const>;
|
||||
using SLEP = SLE::const_pointer;
|
||||
bool
|
||||
forObject(
|
||||
std::function<bool(SLEP const& sle)> const& cb,
|
||||
|
||||
@@ -1549,8 +1549,11 @@ loanMakePayment(
|
||||
if (paymentRemainingProxy == 0 || principalOutstandingProxy == 0)
|
||||
{
|
||||
// Loan complete
|
||||
// This is already checked in LoanPay::preclaim()
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.warn()) << "Loan is already paid off.";
|
||||
return Unexpected(tecKILLED);
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
auto totalValueOutstandingProxy = loan->at(sfTotalValueOutstanding);
|
||||
|
||||
@@ -81,7 +81,7 @@ determineBrokerID(ReadView const& view, STTx const& tx)
|
||||
|
||||
auto const dstAmount = tx[~sfAmount];
|
||||
if (!dstAmount || !dstAmount->holds<Issue>())
|
||||
return Unexpected{tecINTERNAL};
|
||||
return Unexpected{tecINTERNAL}; // LCOV_EXCL_LINE
|
||||
|
||||
// Since we don't have a LoanBrokerID, holder _should_ be the loan broker's
|
||||
// pseudo-account, but we don't know yet whether it is, so use a generic
|
||||
|
||||
@@ -209,7 +209,7 @@ LoanBrokerCoverWithdraw::doApply()
|
||||
Payment::getMaxSourceAmount(brokerPseudoID, amount);
|
||||
SLE::pointer sleDst = view().peek(keylet::account(dstAcct));
|
||||
if (!sleDst)
|
||||
return tecINTERNAL;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
Payment::RipplePaymentParams paymentParams{
|
||||
.ctx = ctx_,
|
||||
|
||||
@@ -89,7 +89,7 @@ LoanBrokerDelete::doApply()
|
||||
broker->key(),
|
||||
false))
|
||||
{
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (!view().dirRemove(
|
||||
keylet::ownerDir(vaultPseudoID),
|
||||
@@ -97,7 +97,7 @@ LoanBrokerDelete::doApply()
|
||||
broker->key(),
|
||||
false))
|
||||
{
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
{
|
||||
@@ -118,26 +118,26 @@ LoanBrokerDelete::doApply()
|
||||
|
||||
auto brokerPseudoSLE = view().peek(keylet::account(brokerPseudoID));
|
||||
if (!brokerPseudoSLE)
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Making the payment and removing the empty holding should have deleted any
|
||||
// obligations associated with the broker or broker pseudo-account.
|
||||
if (*brokerPseudoSLE->at(sfBalance))
|
||||
{
|
||||
JLOG(j_.warn()) << "LoanBrokerDelete: Pseudo-account has a balance";
|
||||
return tecHAS_OBLIGATIONS;
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (brokerPseudoSLE->at(sfOwnerCount) != 0)
|
||||
{
|
||||
JLOG(j_.warn())
|
||||
<< "LoanBrokerDelete: Pseudo-account still owns objects";
|
||||
return tecHAS_OBLIGATIONS;
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
if (auto const directory = keylet::ownerDir(brokerPseudoID);
|
||||
view().read(directory))
|
||||
{
|
||||
JLOG(j_.warn()) << "LoanBrokerDelete: Pseudo-account has a directory";
|
||||
return tecHAS_OBLIGATIONS;
|
||||
return tecHAS_OBLIGATIONS; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
view().erase(brokerPseudoSLE);
|
||||
|
||||
@@ -107,14 +107,14 @@ LoanDelete::doApply()
|
||||
loanSle->at(sfLoanBrokerNode),
|
||||
loanID,
|
||||
false))
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
// Remove LoanID from Directory of the Borrower.
|
||||
if (!view.dirRemove(
|
||||
keylet::ownerDir(borrower),
|
||||
loanSle->at(sfOwnerNode),
|
||||
loanID,
|
||||
false))
|
||||
return tefBAD_LEDGER;
|
||||
return tefBAD_LEDGER; // LCOV_EXCL_LINE
|
||||
|
||||
// Delete the Loan object
|
||||
view.erase(loanSle);
|
||||
|
||||
@@ -241,9 +241,11 @@ LoanManage::defaultLoan(
|
||||
auto vaultLossUnrealizedProxy = vaultSle->at(sfLossUnrealized);
|
||||
if (vaultLossUnrealizedProxy < totalDefaultAmount)
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j.warn())
|
||||
<< "Vault unrealized loss is less than the default amount";
|
||||
return tefBAD_LEDGER;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
vaultLossUnrealizedProxy -= totalDefaultAmount;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user