mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 19:25:51 +00:00
Don't create empty holding for MPT or trust line issuer (#5877)
- Adds a check to the MPToken creation invariant to ensure none are created for the issuer. - `addEmptyHolding()` will return success without doing anything for these scenarios. There is nothing to do, as with XRP. --------- Co-authored-by: Ed Hennis <ed@ripple.com>
This commit is contained in:
committed by
GitHub
parent
78ef800e30
commit
88a770c71b
@@ -1406,8 +1406,8 @@ addEmptyHolding(
|
||||
Issue const& issue,
|
||||
beast::Journal journal)
|
||||
{
|
||||
// Every account can hold XRP.
|
||||
if (issue.native())
|
||||
// Every account can hold XRP. An issuer can issue directly.
|
||||
if (issue.native() || accountID == issue.getIssuer())
|
||||
return tesSUCCESS;
|
||||
|
||||
auto const& issuerId = issue.getIssuer();
|
||||
@@ -1468,6 +1468,8 @@ addEmptyHolding(
|
||||
return tefINTERNAL; // LCOV_EXCL_LINE
|
||||
if (view.peek(keylet::mptoken(mptID, accountID)))
|
||||
return tecDUPLICATE;
|
||||
if (accountID == mptIssue.getIssuer())
|
||||
return tesSUCCESS;
|
||||
|
||||
return authorizeMPToken(view, priorBalance, mptID, accountID, journal);
|
||||
}
|
||||
|
||||
@@ -2827,6 +2827,46 @@ class Loan_test : public beast::unit_test::suite
|
||||
pass();
|
||||
}
|
||||
|
||||
void
|
||||
testIssuerLoan()
|
||||
{
|
||||
testcase << "Issuer Loan";
|
||||
|
||||
using namespace jtx;
|
||||
using namespace loan;
|
||||
Account const issuer("issuer");
|
||||
Account const borrower = issuer;
|
||||
Account const lender("lender");
|
||||
Env env(*this);
|
||||
|
||||
env.fund(XRP(1'000), issuer, lender);
|
||||
|
||||
std::int64_t constexpr issuerBalance = 10'000'000;
|
||||
MPTTester asset(
|
||||
{.env = env,
|
||||
.issuer = issuer,
|
||||
.holders = {lender},
|
||||
.pay = issuerBalance});
|
||||
|
||||
auto const broker = createVaultAndBroker(env, asset, lender, 200);
|
||||
auto const loanSetFee = fee(env.current()->fees().base * 2);
|
||||
// Create Loan
|
||||
env(set(borrower, broker.brokerID, 200),
|
||||
sig(sfCounterpartySignature, lender),
|
||||
loanSetFee);
|
||||
env.close();
|
||||
// Issuer should not create MPToken
|
||||
BEAST_EXPECT(!env.le(keylet::mptoken(asset.issuanceID(), issuer)));
|
||||
// Issuer "borrowed" 200, OutstandingAmount decreased by 200
|
||||
BEAST_EXPECT(env.balance(issuer, asset) == asset(-issuerBalance + 200));
|
||||
// Pay Loan
|
||||
auto const loanKeylet = keylet::loan(broker.brokerID, 1);
|
||||
env(pay(borrower, loanKeylet.key, asset(200)));
|
||||
env.close();
|
||||
// Issuer "re-payed" 200, OutstandingAmount increased by 200
|
||||
BEAST_EXPECT(env.balance(issuer, asset) == asset(-issuerBalance));
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidLoanDelete()
|
||||
{
|
||||
@@ -3049,6 +3089,7 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testIssuerLoan();
|
||||
testDisabled();
|
||||
testSelfLoan();
|
||||
testLifecycle();
|
||||
|
||||
@@ -1479,7 +1479,12 @@ ValidMPTIssuance::visitEntry(
|
||||
if (isDelete)
|
||||
mptokensDeleted_++;
|
||||
else if (!before)
|
||||
{
|
||||
mptokensCreated_++;
|
||||
MPTIssue const mptIssue{after->at(sfMPTokenIssuanceID)};
|
||||
if (mptIssue.getIssuer() == after->at(sfAccount))
|
||||
mptCreatedByIssuer_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1493,6 +1498,25 @@ ValidMPTIssuance::finalize(
|
||||
{
|
||||
if (result == tesSUCCESS)
|
||||
{
|
||||
auto const& rules = view.rules();
|
||||
[[maybe_unused]]
|
||||
bool enforceCreatedByIssuer = rules.enabled(featureSingleAssetVault) ||
|
||||
rules.enabled(featureLendingProtocol);
|
||||
if (mptCreatedByIssuer_)
|
||||
{
|
||||
JLOG(j.fatal())
|
||||
<< "Invariant failed: MPToken created for the MPT issuer";
|
||||
// The comment above starting with "assert(enforce)" explains this
|
||||
// assert.
|
||||
XRPL_ASSERT_PARTS(
|
||||
enforceCreatedByIssuer,
|
||||
"ripple::ValidMPTIssuance::finalize",
|
||||
"no issuer MPToken");
|
||||
if (enforceCreatedByIssuer)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const txnType = tx.getTxnType();
|
||||
if (hasPrivilege(tx, createMPTIssuance))
|
||||
{
|
||||
if (mptIssuancesCreated_ == 0)
|
||||
@@ -1540,7 +1564,7 @@ ValidMPTIssuance::finalize(
|
||||
// ttESCROW_FINISH may authorize an MPT, but it can't have the
|
||||
// mayAuthorizeMPT privilege, because that may cause
|
||||
// non-amendment-gated side effects.
|
||||
bool const enforceEscrowFinish = (tx.getTxnType() == ttESCROW_FINISH) &&
|
||||
bool const enforceEscrowFinish = (txnType == ttESCROW_FINISH) &&
|
||||
(view.rules().enabled(featureSingleAssetVault) ||
|
||||
lendingProtocolEnabled);
|
||||
if (hasPrivilege(tx, mustAuthorizeMPT | mayAuthorizeMPT) ||
|
||||
@@ -1591,7 +1615,7 @@ ValidMPTIssuance::finalize(
|
||||
|
||||
return true;
|
||||
}
|
||||
if (tx.getTxnType() == ttESCROW_FINISH)
|
||||
if (txnType == ttESCROW_FINISH)
|
||||
{
|
||||
// ttESCROW_FINISH may authorize an MPT, but it can't have the
|
||||
// mayAuthorizeMPT privilege, because that may cause
|
||||
|
||||
@@ -576,6 +576,9 @@ class ValidMPTIssuance
|
||||
|
||||
std::uint32_t mptokensCreated_ = 0;
|
||||
std::uint32_t mptokensDeleted_ = 0;
|
||||
// non-MPT transactions may attempt to create
|
||||
// MPToken by an issuer
|
||||
bool mptCreatedByIssuer_ = false;
|
||||
|
||||
public:
|
||||
void
|
||||
|
||||
Reference in New Issue
Block a user