diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 605e7f6073a..b4dcd6ff875 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -345,6 +345,7 @@ enum TECcodes : TERUnderlyingType { tecMPT_MAX_AMOUNT_EXCEEDED = 193, tecMPT_LOCKED = 194, tecMPT_NOT_SUPPORTED = 195, + tecMPT_ISSUANCE_NOT_FOUND = 196 }; //------------------------------------------------------------------------------ diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 950163e3aa5..1803d836625 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -119,6 +119,7 @@ transResults() MAKE_ERROR(tecMPTOKEN_EXISTS, "The account already owns the MPToken object."), MAKE_ERROR(tecMPT_MAX_AMOUNT_EXCEEDED, "The MPT's maximum amount is exceeded."), MAKE_ERROR(tecMPT_LOCKED, "MPT is locked by the issuer."), + MAKE_ERROR(tecMPT_ISSUANCE_NOT_FOUND, "The MPTokenIssuance object is not found"), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index d534b24320a..0cac8811366 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -980,6 +980,41 @@ class MPToken_test : public beast::unit_test::suite jrr[jss::result][jss::error_message] == "Field 'build_path' not allowed in this context."); } + + // Issuer fails trying to send fund after issuance was destroyed + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {&bob}}); + + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + mptAlice.authorize({.account = &bob}); + + // alice destroys issuance + mptAlice.destroy({.ownerCount = 0}); + + // alice tries to send bob fund after issuance is destroy, should + // fail. + mptAlice.pay(alice, bob, 100, tecMPT_ISSUANCE_NOT_FOUND); + } + + // Issuer fails trying to send to some who doesn't own MPT for a + // issuance that was destroyed + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {&bob}}); + + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + // alice destroys issuance + mptAlice.destroy({.ownerCount = 0}); + + // alice tries to send bob who doesn't own the MPT after issuance is + // destroyed, it should fail + mptAlice.pay(alice, bob, 100, tecMPT_ISSUANCE_NOT_FOUND); + } } void diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 22a8249758e..3a422c0c88a 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -1860,6 +1860,10 @@ rippleCredit( { auto const mptID = keylet::mptIssuance(saAmount.issue().getMptID()); auto const issuer = saAmount.getIssuer(); + + if (!view.exists(mptID)) + return tecMPT_ISSUANCE_NOT_FOUND; + if (uSenderID == issuer) { if (auto sle = view.peek(mptID))