Start adding tests for LoanBrokerDelete

- Start fixing the inevitable problems found along the way
- Generalize the LoanBroker lifecycle test
This commit is contained in:
Ed Hennis
2025-04-11 20:13:48 -04:00
parent 853c42fb9e
commit da10ba6fd0
6 changed files with 280 additions and 225 deletions

View File

@@ -548,13 +548,13 @@ TRANSACTION(ttLOAN_BROKER_SET, 74, LoanBrokerSet,
{sfCoverRateLiquidation, soeOPTIONAL},
}))
#if 0
/** This transaction deletes a Loan Broker */
TRANSACTION(ttLOAN_BROKER_DELETE, 75, LoanBrokerDelete,
acctDelete | mayAuthorizeMPT, ({
mustDeleteAcct | mayAuthorizeMPT, ({
{sfLoanBrokerID, soeREQUIRED},
}))
#if 0
/** This transaction deposits First Loss Capital into a Loan Broker */
TRANSACTION(ttLOAN_BROKER_COVER_DEPOSIT, 76, LoanBrokerCoverDeposit, noPriv, ({
{sfLoanBrokerID, soeREQUIRED},

View File

@@ -73,6 +73,10 @@ class LoanBroker_test : public beast::unit_test::suite
using namespace loanBroker;
// Can't create a loan broker regardless of whether the vault exists
env(set(alice, keylet.key), fee(increment), ter(temDISABLED));
auto const brokerKeylet =
keylet::loanbroker(alice.id(), env.seq(alice));
// LoanBrokerDelete is disabled, too.
env(del(alice, brokerKeylet.key), ter(temDISABLED));
};
failAll(all - featureMPTokensV1);
failAll(all - featureSingleAssetVault - featureLendingProtocol);
@@ -80,8 +84,112 @@ class LoanBroker_test : public beast::unit_test::suite
failAll(all - featureLendingProtocol, true);
}
struct VaultInfo
{
jtx::PrettyAsset asset;
uint256 vaultID;
VaultInfo(jtx::PrettyAsset const& asset_, uint256 const& vaultID_)
: asset(asset_), vaultID(vaultID_)
{
}
};
void
testCreateAndUpdate()
lifecycle(
jtx::Env& env,
jtx::Account const& alice,
jtx::Account const& evan,
VaultInfo const& vault,
std::function<void()> createBroker,
std::function<void(SLE::const_ref broker)> checkBroker,
std::function<void(SLE::const_ref broker)> changeBroker,
std::function<void(SLE::const_ref broker)> checkChangedBroker)
{
auto const keylet = keylet::loanbroker(alice.id(), env.seq(alice));
using namespace jtx;
using namespace loanBroker;
createBroker();
env.close();
if (auto broker = env.le(keylet); BEAST_EXPECT(broker))
{
BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
BEAST_EXPECT(broker->at(sfAccount) != alice.id());
BEAST_EXPECT(broker->at(sfOwner) == alice.id());
BEAST_EXPECT(broker->at(sfFlags) == 0);
BEAST_EXPECT(broker->at(sfSequence) == env.seq(alice) - 1);
BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
checkBroker(broker);
// Load the pseudo-account
auto const pseudoKeylet = keylet::account(broker->at(sfAccount));
if (auto const pseudo = env.le(pseudoKeylet); BEAST_EXPECT(pseudo))
{
BEAST_EXPECT(
pseudo->at(sfFlags) ==
(lsfDisableMaster | lsfDefaultRipple | lsfDepositAuth));
BEAST_EXPECT(pseudo->at(sfSequence) == 0);
BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero);
BEAST_EXPECT(
pseudo->at(sfOwnerCount) ==
(vault.asset.raw().native() ? 0 : 1));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
BEAST_EXPECT(!pseudo->isFieldPresent(sfFirstNFTokenSequence));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
BEAST_EXPECT(pseudo->at(sfLoanBrokerID) == keylet.key);
}
// no-op
env(set(alice, vault.vaultID), loanBrokerID(keylet.key));
// Make modifications to the broker
changeBroker(broker);
env.close();
broker = env.le(keylet);
// Check the results of modifications
checkChangedBroker(broker);
/////////////////////////////////////
// try to delete the wrong broker object
env(del(alice, vault.vaultID), ter(tecNO_ENTRY));
// evan tries to delete the broker
env(del(evan, keylet.key), ter(tecNO_PERMISSION));
// TODO: test deletion with an active loan
// delete the broker
env(del(alice, keylet.key));
env.close();
{
broker = env.le(keylet);
BEAST_EXPECT(!broker);
auto pseudo = env.le(pseudoKeylet);
BEAST_EXPECT(!pseudo);
}
}
}
void
testLifecycle()
{
testcase("Create and update");
using namespace jtx;
@@ -121,15 +229,6 @@ class LoanBroker_test : public beast::unit_test::suite
std::array const assets{xrpAsset, iouAsset, mptAsset};
// Create vaults
struct VaultInfo
{
PrettyAsset asset;
uint256 vaultID;
VaultInfo(PrettyAsset const& asset_, uint256 const& vaultID_)
: asset(asset_), vaultID(vaultID_)
{
}
};
std::vector<VaultInfo> vaults;
for (auto const& asset : assets)
{
@@ -150,146 +249,102 @@ class LoanBroker_test : public beast::unit_test::suite
{
using namespace loanBroker;
{
auto badKeylet = keylet::vault(alice.id(), env.seq(alice));
// Try some failure cases
// insufficient fee
env(set(evan, vault.vaultID), ter(telINSUF_FEE_P));
// not the vault owner
env(set(evan, vault.vaultID),
fee(increment),
ter(tecNO_PERMISSION));
// not a vault
env(set(alice, badKeylet.key),
fee(increment),
ter(tecNO_ENTRY));
// flags are checked first
env(set(evan, vault.vaultID, ~tfUniversal),
fee(increment),
ter(temINVALID_FLAG));
// field length validation
// sfData: good length, bad account
env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength, 'X')),
ter(tecNO_PERMISSION));
// sfData: too long
env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength + 1, 'Y')),
ter(temINVALID));
// sfManagementFeeRate: good value, bad account
env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfManagementFeeRate: too big
env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate + 1),
fee(increment),
ter(temINVALID));
// sfCoverRateMinimum: good value, bad account
env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfCoverRateMinimum: too big
env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate + 1),
fee(increment),
ter(temINVALID));
// sfCoverRateLiquidation: good value, bad account
env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfCoverRateLiquidation: too big
env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate + 1),
fee(increment),
ter(temINVALID));
// sfDebtMaximum: good value, bad account
env(set(evan, vault.vaultID),
debtMaximum(Number(0)),
fee(increment),
ter(tecNO_PERMISSION));
// sfDebtMaximum: overflow
env(set(evan, vault.vaultID),
debtMaximum(Number(1, 100)),
fee(increment),
ter(temINVALID));
// sfDebtMaximum: negative
env(set(evan, vault.vaultID),
debtMaximum(Number(-1)),
fee(increment),
ter(temINVALID));
auto badKeylet = keylet::vault(alice.id(), env.seq(alice));
// Try some failure cases
// insufficient fee
env(set(evan, vault.vaultID), ter(telINSUF_FEE_P));
// not the vault owner
env(set(evan, vault.vaultID),
fee(increment),
ter(tecNO_PERMISSION));
// not a vault
env(set(alice, badKeylet.key), fee(increment), ter(tecNO_ENTRY));
// flags are checked first
env(set(evan, vault.vaultID, ~tfUniversal),
fee(increment),
ter(temINVALID_FLAG));
// field length validation
// sfData: good length, bad account
env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength, 'X')),
ter(tecNO_PERMISSION));
// sfData: too long
env(set(evan, vault.vaultID),
fee(increment),
data(std::string(maxDataPayloadLength + 1, 'Y')),
ter(temINVALID));
// sfManagementFeeRate: good value, bad account
env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfManagementFeeRate: too big
env(set(evan, vault.vaultID),
managementFeeRate(maxFeeRate + 1),
fee(increment),
ter(temINVALID));
// sfCoverRateMinimum: good value, bad account
env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfCoverRateMinimum: too big
env(set(evan, vault.vaultID),
coverRateMinimum(maxCoverRate + 1),
fee(increment),
ter(temINVALID));
// sfCoverRateLiquidation: good value, bad account
env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate),
fee(increment),
ter(tecNO_PERMISSION));
// sfCoverRateLiquidation: too big
env(set(evan, vault.vaultID),
coverRateLiquidation(maxCoverRate + 1),
fee(increment),
ter(temINVALID));
// sfDebtMaximum: good value, bad account
env(set(evan, vault.vaultID),
debtMaximum(Number(0)),
fee(increment),
ter(tecNO_PERMISSION));
// sfDebtMaximum: overflow
env(set(evan, vault.vaultID),
debtMaximum(Number(1, 100)),
fee(increment),
ter(temINVALID));
// sfDebtMaximum: negative
env(set(evan, vault.vaultID),
debtMaximum(Number(-1)),
fee(increment),
ter(temINVALID));
auto keylet = keylet::loanbroker(alice.id(), env.seq(alice));
// Successfully create a Loan Broker with all default values.
env(set(alice, vault.vaultID), fee(increment));
env.close();
if (auto broker = env.le(keylet); BEAST_EXPECT(broker))
{
// Check the fields
BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
BEAST_EXPECT(broker->at(sfAccount) != alice.id());
BEAST_EXPECT(broker->at(sfOwner) == alice.id());
lifecycle(
env,
alice,
evan,
vault,
[&]() {
// Successfully create a Loan Broker with all default
// values.
env(set(alice, vault.vaultID), fee(increment));
},
[&](SLE::const_ref broker) {
// Extra checks
BEAST_EXPECT(!broker->isFieldPresent(sfManagementFeeRate));
BEAST_EXPECT(!broker->isFieldPresent(sfCoverRateMinimum));
BEAST_EXPECT(
!broker->isFieldPresent(sfCoverRateLiquidation));
BEAST_EXPECT(broker->at(sfFlags) == 0);
BEAST_EXPECT(broker->at(sfSequence) == env.seq(alice) - 1);
BEAST_EXPECT(!broker->isFieldPresent(sfData));
BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
BEAST_EXPECT(broker->at(sfDebtMaximum) == 0);
BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 0);
BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 0);
// Load the pseudo-account
auto const pseudoKeylet =
keylet::account(broker->at(sfAccount));
if (auto pseudo = env.le(pseudoKeylet);
BEAST_EXPECT(pseudo))
{
BEAST_EXPECT(
pseudo->at(sfFlags) ==
(lsfDisableMaster | lsfDefaultRipple |
lsfDepositAuth));
BEAST_EXPECT(pseudo->at(sfSequence) == 0);
BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero);
BEAST_EXPECT(
pseudo->at(sfOwnerCount) ==
(vault.asset.raw().native() ? 0 : 1));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
BEAST_EXPECT(
!pseudo->isFieldPresent(sfFirstNFTokenSequence));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
BEAST_EXPECT(pseudo->at(sfLoanBrokerID) == keylet.key);
}
},
[&](SLE::const_ref broker) {
// Modifications
// Update the fields
auto nextKeylet =
auto const nextKeylet =
keylet::loanbroker(alice.id(), env.seq(alice));
// no-op
env(set(alice, vault.vaultID), loanBrokerID(keylet.key));
// fields that can't be changed
// LoanBrokerID
env(set(alice, vault.vaultID),
@@ -297,25 +352,25 @@ class LoanBroker_test : public beast::unit_test::suite
ter(tecNO_ENTRY));
// VaultID
env(set(alice, nextKeylet.key),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
ter(tecNO_PERMISSION));
// Owner
env(set(evan, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
ter(tecNO_PERMISSION));
// ManagementFeeRate
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
managementFeeRate(maxFeeRate),
ter(temINVALID));
// CoverRateMinimum
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
coverRateMinimum(maxFeeRate),
ter(temINVALID));
// CoverRateLiquidation
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
coverRateLiquidation(maxFeeRate),
ter(temINVALID));
@@ -323,96 +378,70 @@ class LoanBroker_test : public beast::unit_test::suite
std::string const testData("Test Data 1234");
// Bad data: too long
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
data(std::string(maxDataPayloadLength + 1, 'W')),
ter(temINVALID));
// Debt maximum: explicit 0
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
debtMaximum(Number(0)));
// Check the updated fields
broker = env.le(broker->key());
BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
// Bad debt maximum
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
debtMaximum(Number(-175, -1)),
ter(temINVALID));
// Data & Debt maximum
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
loanBrokerID(broker->key()),
data(testData),
debtMaximum(Number(175, -1)));
env.close();
},
[&](SLE::const_ref broker) {
// Check the updated fields
broker = env.le(keylet);
BEAST_EXPECT(checkVL(broker->at(sfData), testData));
BEAST_EXPECT(broker->at(sfDebtMaximum) == Number(175, -1));
}
});
auto keylet2 = keylet::loanbroker(alice.id(), env.seq(alice));
std::string const testData2("spam spam spam spam");
// Finally, create another Loan Broker with none of the values
// at default
// Successfully create a Loan Broker with no default values.
env(set(alice, vault.vaultID),
data(testData2),
managementFeeRate(123),
debtMaximum(Number(9)),
coverRateMinimum(100),
coverRateLiquidation(200),
fee(increment));
env.close();
if (auto broker = env.le(keylet2); BEAST_EXPECT(broker))
{
// Check the fields
BEAST_EXPECT(broker->at(sfVaultID) == vault.vaultID);
BEAST_EXPECT(broker->at(sfAccount) != alice.id());
BEAST_EXPECT(broker->at(sfOwner) == alice.id());
lifecycle(
env,
alice,
evan,
vault,
[&]() {
std::string const testData2("spam spam spam spam");
// Finally, create another Loan Broker with none of the
// values at default
env(set(alice, vault.vaultID),
data(testData2),
managementFeeRate(123),
debtMaximum(Number(9)),
coverRateMinimum(100),
coverRateLiquidation(200),
fee(increment));
},
[&](SLE::const_ref broker) {
// Extra checks
BEAST_EXPECT(broker->at(sfManagementFeeRate) == 123);
BEAST_EXPECT(broker->at(sfCoverRateMinimum) == 100);
BEAST_EXPECT(broker->at(sfCoverRateLiquidation) == 200);
BEAST_EXPECT(broker->at(sfDebtMaximum) == Number(9));
BEAST_EXPECT(broker->at(sfFlags) == 0);
BEAST_EXPECT(broker->at(sfSequence) == env.seq(alice) - 1);
BEAST_EXPECT(checkVL(broker->at(sfData), testData2));
BEAST_EXPECT(broker->at(sfOwnerCount) == 0);
BEAST_EXPECT(broker->at(sfDebtTotal) == 0);
BEAST_EXPECT(broker->at(sfCoverAvailable) == 0);
// Load the pseudo-account
auto const pseudoKeylet =
keylet::account(broker->at(sfAccount));
if (auto pseudo = env.le(pseudoKeylet);
BEAST_EXPECT(pseudo))
{
BEAST_EXPECT(
pseudo->at(sfFlags) ==
(lsfDisableMaster | lsfDefaultRipple |
lsfDepositAuth));
BEAST_EXPECT(pseudo->at(sfSequence) == 0);
BEAST_EXPECT(pseudo->at(sfBalance) == beast::zero);
BEAST_EXPECT(
pseudo->at(sfOwnerCount) ==
(vault.asset.raw().native() ? 0 : 1));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAccountTxnID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfRegularKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfEmailHash));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletLocator));
BEAST_EXPECT(!pseudo->isFieldPresent(sfWalletSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMessageKey));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTransferRate));
BEAST_EXPECT(!pseudo->isFieldPresent(sfDomain));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTickSize));
BEAST_EXPECT(!pseudo->isFieldPresent(sfTicketCount));
BEAST_EXPECT(!pseudo->isFieldPresent(sfNFTokenMinter));
BEAST_EXPECT(!pseudo->isFieldPresent(sfMintedNFTokens));
BEAST_EXPECT(!pseudo->isFieldPresent(sfBurnedNFTokens));
BEAST_EXPECT(
!pseudo->isFieldPresent(sfFirstNFTokenSequence));
BEAST_EXPECT(!pseudo->isFieldPresent(sfAMMID));
BEAST_EXPECT(!pseudo->isFieldPresent(sfVaultID));
BEAST_EXPECT(pseudo->at(sfLoanBrokerID) == keylet2.key);
}
}
}
},
[&](SLE::const_ref broker) {
// Reset Data & Debt maximum to default values
env(set(alice, vault.vaultID),
loanBrokerID(keylet.key),
data(""),
debtMaximum(Number(0)));
},
[&](SLE::const_ref broker) {
// Check the updated fields
BEAST_EXPECT(!broker->isFieldPresent(sfData));
BEAST_EXPECT(!broker->isFieldPresent(sfDebtMaximum));
});
}
}
@@ -421,7 +450,7 @@ public:
run() override
{
testDisabled();
testCreateAndUpdate();
testLifecycle();
}
};

View File

@@ -605,13 +605,9 @@ namespace loanBroker {
Json::Value
set(AccountID const& account, uint256 const& vaultId, uint32_t flags = 0);
/*
inline Json::Value
set(Account const& account, uint256 const& vaultId, uint32_t flags = 0)
{
return create(account.id(), vaultId);
}
*/
// Use "del" because "delete" is a reserved word in C++.
Json::Value
del(AccountID const& account, uint256 const& loanBrokerID, uint32_t flags = 0);
auto const loanBrokerID = JTxFieldWrapper<uint256Field>(sfLoanBrokerID);

View File

@@ -405,6 +405,17 @@ set(AccountID const& account, uint256 const& vaultId, uint32_t flags)
return jv;
}
Json::Value
del(AccountID const& account, uint256 const& loanBrokerID, uint32_t flags)
{
Json::Value jv;
jv[sfTransactionType.jsonName] = jss::LoanBrokerDelete;
jv[sfAccount.jsonName] = to_string(account);
jv[sfLoanBrokerID.jsonName] = to_string(loanBrokerID);
jv[sfFlags.jsonName] = flags;
return jv;
}
} // namespace loanBroker
} // namespace jtx

View File

@@ -435,7 +435,6 @@ AccountRootsDeletedClean::finalize(
// be logged
[[maybe_unused]] bool const enforce =
view.rules().enabled(featureInvariantsV1_1) ||
view.rules().enabled(featureSingleAssetVault) ||
view.rules().enabled(featureLendingProtocol);
auto const objectExists = [&view, enforce, &j](auto const& keylet) {

View File

@@ -80,7 +80,7 @@ LoanBrokerDelete::preclaim(PreclaimContext const& ctx)
}
if (sleBroker->at(sfOwnerCount) != 0)
{
JLOG(ctx.j.warn()) << "LoanBrokerSet: Owner count is not zero";
JLOG(ctx.j.warn()) << "LoanBrokerDelete: Owner count is not zero";
return tecHAS_OBLIGATIONS;
}
@@ -138,6 +138,26 @@ LoanBrokerDelete::doApply()
if (!brokerPseudoSLE)
return tefBAD_LEDGER;
// Making the payment 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;
}
if (brokerPseudoSLE->at(sfOwnerCount) != 0)
{
JLOG(j_.warn())
<< "LoanBrokerDelete: Pseudo-account still owns objects";
return tecHAS_OBLIGATIONS;
}
if (auto const directory = keylet::ownerDir(brokerPseudoID);
view.read(directory))
{
JLOG(j_.warn()) << "LoanBrokerDelete: Pseudo-account has a directory";
return tecHAS_OBLIGATIONS;
}
view.erase(brokerPseudoSLE);
view.erase(broker);