diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 8db6f722541..e2a8d6df5c5 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -66,13 +66,6 @@ JSS(AuthAccount); // in: AMM Auction Slot JSS(AuthAccounts); // in: AMM Auction Slot JSS(BaseAsset); // in: Oracle JSS(Bridge); // ledger type. -JSS(MPToken); // ledger type. -JSS(MPTokenIssuance); // ledger type. -JSS(MPTokenIssuanceCreate); // transaction type. -JSS(MPTokenIssuanceDestroy); // transaction type. -JSS(MPTokenAuthorize); // transaction type. -JSS(MPTokenIssuanceSet); // transaction type. -JSS(MPTokenIssuanceID); // in: MPTokenIssuanceDestroy, MPTokenAuthorize JSS(Check); // ledger type. JSS(CheckCancel); // transaction type. JSS(CheckCash); // transaction type. @@ -103,6 +96,13 @@ JSS(LedgerHashes); // ledger type. JSS(LimitAmount); // field. JSS(BidMax); // in: AMM Bid JSS(BidMin); // in: AMM Bid +JSS(MPToken); // ledger type. +JSS(MPTokenIssuance); // ledger type. +JSS(MPTokenIssuanceCreate); // transaction type. +JSS(MPTokenIssuanceDestroy); // transaction type. +JSS(MPTokenAuthorize); // transaction type. +JSS(MPTokenIssuanceSet); // transaction type. +JSS(MPTokenIssuanceID); // in: MPTokenIssuanceDestroy, MPTokenAuthorize JSS(NetworkID); // field. JSS(NFTokenBurn); // transaction type. JSS(NFTokenMint); // transaction type. diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index fed1d2df966..950163e3aa5 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -201,6 +201,7 @@ transResults() MAKE_ERROR(temINVALID_COUNT, "Malformed: Count field outside valid range."), MAKE_ERROR(temSEQ_AND_TICKET, "Transaction contains a TicketSequence and a non-zero Sequence."), MAKE_ERROR(temBAD_NFTOKEN_TRANSFER_FEE, "Malformed: The NFToken transfer fee must be between 1 and 5000, inclusive."), + MAKE_ERROR(temBAD_MPTOKEN_TRANSFER_FEE, "Malformed: The MPToken transfer fee must be between 1 and 5000, inclusive."), MAKE_ERROR(temXCHAIN_EQUAL_DOOR_ACCOUNTS, "Malformed: Bridge must have unique door accounts."), MAKE_ERROR(temXCHAIN_BAD_PROOF, "Malformed: Bad cross-chain claim proof."), MAKE_ERROR(temXCHAIN_BRIDGE_BAD_ISSUES, "Malformed: Bad bridge issues."), diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 757e9e9db60..d534b24320a 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -59,7 +59,7 @@ class MPToken_test : public beast::unit_test::suite .metadata = "test", .err = temMALFORMED}); - // tries to set a txfee while not enabling transfer + // tries to set a txfee greater than max mptAlice.create( {.maxAmt = 100, .assetScale = 0, @@ -68,6 +68,14 @@ class MPToken_test : public beast::unit_test::suite .flags = tfMPTCanTransfer, .err = temBAD_MPTOKEN_TRANSFER_FEE}); + // tries to set a txfee while not enabling transfer + mptAlice.create( + {.maxAmt = 100, + .assetScale = 0, + .transferFee = maxTransferFee, + .metadata = "test", + .err = temMALFORMED}); + // empty metadata returns error mptAlice.create( {.maxAmt = 100, diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp index f998858390e..1e9871e0ae3 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.cpp @@ -126,40 +126,41 @@ MPTokenAuthorize::preclaim(PreclaimContext const& ctx) } TER -MPTokenAuthorize::doApply() +MPTokenAuthorize::authorize( + ApplyView& view, + beast::Journal journal, + MPTAuthorizeArgs const& args) { - auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID]; - auto const sleAcct = view().peek(keylet::account(account_)); + auto const sleAcct = view.peek(keylet::account(args.account)); if (!sleAcct) return tecINTERNAL; - auto const holderID = ctx_.tx[~sfMPTokenHolder]; - // If the account that submitted the tx is a holder // Note: `account_` is holder's account // `holderID` is NOT used - if (!holderID) + if (!args.holderID) { // When a holder wants to unauthorize/delete a MPT, the ledger must // - delete mptokenKey from owner directory // - delete the MPToken - if (ctx_.tx.getFlags() & tfMPTUnauthorize) + if (args.flags & tfMPTUnauthorize) { - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account_); - auto const sleMpt = view().peek(mptokenKey); + auto const mptokenKey = + keylet::mptoken(args.mptIssuanceID, args.account); + auto const sleMpt = view.peek(mptokenKey); if (!sleMpt) return tecINTERNAL; - if (!view().dirRemove( - keylet::ownerDir(account_), + if (!view.dirRemove( + keylet::ownerDir(args.account), (*sleMpt)[sfOwnerNode], sleMpt->key(), false)) return tecINTERNAL; - adjustOwnerCount(view(), sleAcct, -1, j_); + adjustOwnerCount(view, sleAcct, -1, journal); - view().erase(sleMpt); + view.erase(sleMpt); return tesSUCCESS; } @@ -169,43 +170,48 @@ MPTokenAuthorize::doApply() std::uint32_t const uOwnerCount = sleAcct->getFieldU32(sfOwnerCount); XRPAmount const reserveCreate( (uOwnerCount < 2) ? XRPAmount(beast::zero) - : view().fees().accountReserve(uOwnerCount + 1)); + : view.fees().accountReserve(uOwnerCount + 1)); - if (mPriorBalance < reserveCreate) + if (args.priorBalance < reserveCreate) return tecINSUFFICIENT_RESERVE; - auto const mptokenKey = keylet::mptoken(mptIssuanceID, account_); + auto const mptokenKey = + keylet::mptoken(args.mptIssuanceID, args.account); - auto const ownerNode = view().dirInsert( - keylet::ownerDir(account_), mptokenKey, describeOwnerDir(account_)); + auto const ownerNode = view.dirInsert( + keylet::ownerDir(args.account), + mptokenKey, + describeOwnerDir(args.account)); if (!ownerNode) return tecDIR_FULL; auto mptoken = std::make_shared(mptokenKey); - (*mptoken)[sfAccount] = account_; - (*mptoken)[sfMPTokenIssuanceID] = mptIssuanceID; + (*mptoken)[sfAccount] = args.account; + (*mptoken)[sfMPTokenIssuanceID] = args.mptIssuanceID; (*mptoken)[sfFlags] = 0; (*mptoken)[sfOwnerNode] = *ownerNode; - view().insert(mptoken); + view.insert(mptoken); // Update owner count. - adjustOwnerCount(view(), sleAcct, 1, j_); + adjustOwnerCount(view, sleAcct, 1, journal); return tesSUCCESS; } - auto const sleMptIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); + auto const sleMptIssuance = + view.read(keylet::mptIssuance(args.mptIssuanceID)); if (!sleMptIssuance) return tecINTERNAL; // If the account that submitted this tx is the issuer of the MPT // Note: `account_` is issuer's account // `holderID` is holder's account - if (account_ != (*sleMptIssuance)[sfIssuer]) + if (args.account != (*sleMptIssuance)[sfIssuer]) return tecINTERNAL; - auto const sleMpt = view().peek(keylet::mptoken(mptIssuanceID, *holderID)); + auto const sleMpt = + view.peek(keylet::mptoken(args.mptIssuanceID, *args.holderID)); if (!sleMpt) return tecINTERNAL; @@ -214,7 +220,7 @@ MPTokenAuthorize::doApply() // Issuer wants to unauthorize the holder, unset lsfMPTAuthorized on // their MPToken - if (ctx_.tx.getFlags() & tfMPTUnauthorize) + if (args.flags & tfMPTUnauthorize) flagsOut &= ~lsfMPTAuthorized; // Issuer wants to authorize a holder, set lsfMPTAuthorized on their // MPToken @@ -224,8 +230,22 @@ MPTokenAuthorize::doApply() if (flagsIn != flagsOut) sleMpt->setFieldU32(sfFlags, flagsOut); - view().update(sleMpt); + view.update(sleMpt); return tesSUCCESS; } +TER +MPTokenAuthorize::doApply() +{ + auto const& tx = ctx_.tx; + return authorize( + ctx_.view(), + ctx_.journal, + {.priorBalance = mPriorBalance, + .mptIssuanceID = tx[sfMPTokenIssuanceID], + .account = account_, + .flags = tx.getFlags(), + .holderID = tx[~sfMPTokenHolder]}); +} + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/MPTokenAuthorize.h b/src/xrpld/app/tx/detail/MPTokenAuthorize.h index b2068412b76..94451a61c88 100644 --- a/src/xrpld/app/tx/detail/MPTokenAuthorize.h +++ b/src/xrpld/app/tx/detail/MPTokenAuthorize.h @@ -24,6 +24,15 @@ namespace ripple { +struct MPTAuthorizeArgs +{ + XRPAmount const& priorBalance; + uint192 const& mptIssuanceID; + AccountID const& account; + std::uint32_t flags; + std::optional holderID; +}; + class MPTokenAuthorize : public Transactor { public: @@ -39,6 +48,12 @@ class MPTokenAuthorize : public Transactor static TER preclaim(PreclaimContext const& ctx); + static TER + authorize( + ApplyView& view, + beast::Journal journal, + MPTAuthorizeArgs const& args); + TER doApply() override; }; diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index 97918758714..de01341e296 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -68,54 +68,75 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) } TER -MPTokenIssuanceCreate::doApply() +MPTokenIssuanceCreate::create( + ApplyView& view, + beast::Journal journal, + MPTCreateArgs const& args) { - auto const acct = view().peek(keylet::account(account_)); + auto const acct = view.peek(keylet::account(args.account)); if (!acct) return tecINTERNAL; - if (mPriorBalance < view().fees().accountReserve((*acct)[sfOwnerCount] + 1)) + if (args.priorBalance < + view.fees().accountReserve((*acct)[sfOwnerCount] + 1)) return tecINSUFFICIENT_RESERVE; auto const mptIssuanceKeylet = - keylet::mptIssuance(account_, ctx_.tx.getSeqProxy().value()); + keylet::mptIssuance(args.account, args.sequence); // create the MPTokenIssuance { - auto const ownerNode = view().dirInsert( - keylet::ownerDir(account_), + auto const ownerNode = view.dirInsert( + keylet::ownerDir(args.account), mptIssuanceKeylet, - describeOwnerDir(account_)); + describeOwnerDir(args.account)); if (!ownerNode) return tecDIR_FULL; auto mptIssuance = std::make_shared(mptIssuanceKeylet); - (*mptIssuance)[sfFlags] = ctx_.tx.getFlags() & ~tfUniversal; - (*mptIssuance)[sfIssuer] = account_; + (*mptIssuance)[sfFlags] = args.flags & ~tfUniversal; + (*mptIssuance)[sfIssuer] = args.account; (*mptIssuance)[sfOutstandingAmount] = 0; (*mptIssuance)[sfOwnerNode] = *ownerNode; - (*mptIssuance)[sfSequence] = ctx_.tx.getSeqProxy().value(); + (*mptIssuance)[sfSequence] = args.sequence; - if (auto const max = ctx_.tx[~sfMaximumAmount]) - (*mptIssuance)[sfMaximumAmount] = *max; + if (args.maxAmount) + (*mptIssuance)[sfMaximumAmount] = *args.maxAmount; - if (auto const scale = ctx_.tx[~sfAssetScale]) - (*mptIssuance)[sfAssetScale] = *scale; + if (args.assetScale) + (*mptIssuance)[sfAssetScale] = *args.assetScale; - if (auto const fee = ctx_.tx[~sfTransferFee]) - (*mptIssuance)[sfTransferFee] = *fee; + if (args.transferFee) + (*mptIssuance)[sfTransferFee] = *args.transferFee; - if (auto const metadata = ctx_.tx[~sfMPTokenMetadata]) - (*mptIssuance)[sfMPTokenMetadata] = *metadata; + if (args.metadata) + (*mptIssuance)[sfMPTokenMetadata] = *args.metadata; - view().insert(mptIssuance); + view.insert(mptIssuance); } // Update owner count. - adjustOwnerCount(view(), acct, 1, j_); + adjustOwnerCount(view, acct, 1, journal); return tesSUCCESS; } +TER +MPTokenIssuanceCreate::doApply() +{ + auto const& tx = ctx_.tx; + return create( + ctx_.view(), + ctx_.journal, + {.priorBalance = mPriorBalance, + .account = account_, + .sequence = tx.getSeqProxy().value(), + .flags = tx.getFlags(), + .maxAmount = tx[~sfMaximumAmount], + .assetScale = tx[~sfAssetScale], + .transferFee = tx[~sfTransferFee], + .metadata = tx[~sfMPTokenMetadata]}); +} + } // namespace ripple diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h index 0c1322667cc..2fdf2bdd152 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h @@ -24,6 +24,18 @@ namespace ripple { +struct MPTCreateArgs +{ + XRPAmount const& priorBalance; + AccountID const& account; + std::uint32_t sequence; + std::uint32_t flags; + std::optional maxAmount; + std::optional assetScale; + std::optional transferFee; + std::optional const& metadata; +}; + class MPTokenIssuanceCreate : public Transactor { public: @@ -38,6 +50,9 @@ class MPTokenIssuanceCreate : public Transactor TER doApply() override; + + static TER + create(ApplyView& view, beast::Journal journal, MPTCreateArgs const& args); }; } // namespace ripple diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 98ef572b548..42275ba4da6 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include