mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-02 16:26:48 +00:00
Implicitly authorize Vault and LoanBroker pseudo-accounts (#5976)
- Vault and LoanBroker pseudo-accounts can hold MPTs, regardless of MPTRequireAuth setting. - Add requireAuth check in LoanBrokerCoverDeposit and LoanPay. - Fail attempts to unauthorize pseudo-accounts by MPT issuers.
This commit is contained in:
committed by
GitHub
parent
ecc429e521
commit
ebfca636fc
@@ -1265,6 +1265,126 @@ class LoanBroker_test : public beast::unit_test::suite
|
||||
(void)LoanBrokerCoverDeposit::preclaim(pctx);
|
||||
}
|
||||
|
||||
void
|
||||
testRequireAuth()
|
||||
{
|
||||
testcase("Require Auth - Implicit Pseudo-account authorization");
|
||||
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();
|
||||
|
||||
auto asset = MPTTester({
|
||||
.env = env,
|
||||
.issuer = issuer,
|
||||
.holders = {alice},
|
||||
.flags = MPTDEXFlags | tfMPTRequireAuth | tfMPTCanClawback |
|
||||
tfMPTCanLock,
|
||||
.authHolder = true,
|
||||
});
|
||||
|
||||
env(pay(issuer, alice, asset(100'000)));
|
||||
env.close();
|
||||
|
||||
// Alice is not authorized, can still create the vault
|
||||
asset.authorize(
|
||||
{.account = issuer, .holder = alice, .flags = tfMPTUnauthorize});
|
||||
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;
|
||||
|
||||
// Can't unauthorize Vault pseudo-account
|
||||
asset.authorize(
|
||||
{.account = issuer,
|
||||
.holder = vaultInfo.pseudoAccount,
|
||||
.flags = tfMPTUnauthorize,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
auto forUnauthAuth = [&](auto&& doTx) {
|
||||
for (auto const flag : {tfMPTUnauthorize, 0u})
|
||||
{
|
||||
asset.authorize(
|
||||
{.account = issuer, .holder = alice, .flags = flag});
|
||||
env.close();
|
||||
doTx(flag == 0);
|
||||
env.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Can't deposit into Vault if the vault owner is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(vault.deposit(
|
||||
{.depositor = alice,
|
||||
.id = vaultKeylet.key,
|
||||
.amount = asset(51)}),
|
||||
err);
|
||||
});
|
||||
|
||||
// Can't withdraw from Vault if the vault owner is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(vault.withdraw(
|
||||
{.depositor = alice,
|
||||
.id = vaultKeylet.key,
|
||||
.amount = asset(1)}),
|
||||
err);
|
||||
});
|
||||
|
||||
auto const brokerKeylet =
|
||||
keylet::loanbroker(alice.id(), env.seq(alice));
|
||||
// Can create LoanBroker if the vault owner is not authorized
|
||||
forUnauthAuth([&](auto) { env(set(alice, vaultInfo.vaultID)); });
|
||||
|
||||
auto const broker = env.le(brokerKeylet);
|
||||
if (!BEAST_EXPECT(broker))
|
||||
return;
|
||||
Account brokerPseudo("pseudo", broker->at(sfAccount));
|
||||
|
||||
// Can't unauthorize LoanBroker pseudo-account
|
||||
asset.authorize(
|
||||
{.account = issuer,
|
||||
.holder = brokerPseudo,
|
||||
.flags = tfMPTUnauthorize,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
// Can't cover deposit into Vault if the vault owner is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(coverDeposit(alice, brokerKeylet.key, vaultInfo.asset(10)),
|
||||
err);
|
||||
});
|
||||
|
||||
// Can't cover withdraw from Vault if the vault owner is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(coverWithdraw(alice, brokerKeylet.key, vaultInfo.asset(5)),
|
||||
err);
|
||||
});
|
||||
|
||||
// Issuer can always cover clawback. The holder authorization is n/a.
|
||||
forUnauthAuth([&](bool) {
|
||||
env(coverClawback(issuer),
|
||||
loanBrokerID(brokerKeylet.key),
|
||||
amount(vaultInfo.asset(1)));
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
@@ -1278,6 +1398,7 @@ public:
|
||||
testInvalidLoanBrokerCoverWithdraw();
|
||||
testInvalidLoanBrokerDelete();
|
||||
testInvalidLoanBrokerSet();
|
||||
testRequireAuth();
|
||||
|
||||
// TODO: Write clawback failure tests with an issuer / MPT that doesn't
|
||||
// have the right flags set.
|
||||
|
||||
@@ -5333,6 +5333,65 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testRequireAuth()
|
||||
{
|
||||
testcase("Require Auth - Implicit Pseudo-account authorization");
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
Account const lender{"lender"};
|
||||
Account const issuer{"issuer"};
|
||||
Account const borrower{"borrower"};
|
||||
Env env(*this);
|
||||
|
||||
env.fund(XRP(100'000), issuer, lender, borrower);
|
||||
env.close();
|
||||
|
||||
auto asset = MPTTester({
|
||||
.env = env,
|
||||
.issuer = issuer,
|
||||
.holders = {lender, borrower},
|
||||
.flags = MPTDEXFlags | tfMPTRequireAuth | tfMPTCanClawback |
|
||||
tfMPTCanLock,
|
||||
.authHolder = true,
|
||||
});
|
||||
|
||||
env(pay(issuer, lender, asset(5'000'000)));
|
||||
BrokerInfo brokerInfo{createVaultAndBroker(env, asset, lender)};
|
||||
|
||||
auto const loanSetFee = fee(env.current()->fees().base * 2);
|
||||
STAmount const debtMaximumRequest = brokerInfo.asset(1'000).value();
|
||||
|
||||
auto forUnauthAuth = [&](auto&& doTx) {
|
||||
for (auto const flag : {tfMPTUnauthorize, 0u})
|
||||
{
|
||||
asset.authorize(
|
||||
{.account = issuer, .holder = borrower, .flags = flag});
|
||||
env.close();
|
||||
doTx(flag == 0);
|
||||
env.close();
|
||||
}
|
||||
};
|
||||
|
||||
// Can't create a loan if the borrower is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(set(borrower, brokerInfo.brokerID, debtMaximumRequest),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee,
|
||||
err);
|
||||
});
|
||||
|
||||
std::uint32_t constexpr loanSequence = 1;
|
||||
auto const loanKeylet = keylet::loan(brokerInfo.brokerID, loanSequence);
|
||||
|
||||
// Can't loan pay if the borrower is not authorized
|
||||
forUnauthAuth([&](bool authorized) {
|
||||
auto const err = !authorized ? ter(tecNO_AUTH) : ter(tesSUCCESS);
|
||||
env(pay(borrower, loanKeylet.key, debtMaximumRequest), err);
|
||||
});
|
||||
}
|
||||
|
||||
#if LOANTODO
|
||||
void
|
||||
testCoverDepositAllowsNonTransferableMPT()
|
||||
@@ -6193,6 +6252,8 @@ public:
|
||||
testLoanPayComputePeriodicPaymentValidTotalPrincipalPaidInvariant();
|
||||
testLoanPayComputePeriodicPaymentValidTotalInterestPaidInvariant();
|
||||
testLoanNextPaymentDueDateOverflow();
|
||||
|
||||
testRequireAuth();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user