diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index 7c504f6bdd3..68205e27e62 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -190,17 +190,6 @@ enum LedgerEntryType : std::uint16_t { LSF_FLAG(lsfMPTCanClawback, 0x00000040) \ LSF_FLAG(lsfMPTCanHoldConfidentialBalance, 0x00000080)) \ \ - LEDGER_OBJECT(MPTokenIssuanceMutable, \ - LSF_FLAG(lsmfMPTCanEnableCanLock, 0x00000002) \ - LSF_FLAG(lsmfMPTCanEnableRequireAuth, 0x00000004) \ - LSF_FLAG(lsmfMPTCanEnableCanEscrow, 0x00000008) \ - LSF_FLAG(lsmfMPTCanEnableCanTrade, 0x00000010) \ - LSF_FLAG(lsmfMPTCanEnableCanTransfer, 0x00000020) \ - LSF_FLAG(lsmfMPTCanEnableCanClawback, 0x00000040) \ - LSF_FLAG(lsmfMPTCannotEnableCanHoldConfidentialBalance, 0x00000080) \ - LSF_FLAG(lsmfMPTCanMutateMetadata, 0x00010000) \ - LSF_FLAG(lsmfMPTCanMutateTransferFee, 0x00020000)) \ - \ LEDGER_OBJECT(MPToken, \ LSF_FLAG2(lsfMPTLocked, 0x00000001) \ LSF_FLAG(lsfMPTAuthorized, 0x00000002) \ @@ -294,6 +283,17 @@ getAllLedgerFlags() #pragma pop_macro("TO_MAP") #pragma pop_macro("ALL_LEDGER_FLAGS") +// MPTokenIssuance ImmutableFlags (sfImmutableFlags) +inline constexpr std::uint32_t lsifMPTCanLock = 0x00000002; +inline constexpr std::uint32_t lsifMPTRequireAuth = 0x00000004; +inline constexpr std::uint32_t lsifMPTCanEscrow = 0x00000008; +inline constexpr std::uint32_t lsifMPTCanTrade = 0x00000010; +inline constexpr std::uint32_t lsifMPTCanTransfer = 0x00000020; +inline constexpr std::uint32_t lsifMPTCanClawback = 0x00000040; +inline constexpr std::uint32_t lsifMPTCanHoldConfidentialBalance = 0x00000080; +inline constexpr std::uint32_t lsifMPTMetadata = 0x00010000; +inline constexpr std::uint32_t lsifMPTTransferFee = 0x00020000; + //------------------------------------------------------------------------------ /** diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 0afdebb898a..6e504360b88 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -152,7 +152,14 @@ inline constexpr FlagValue tfUniversalMask = ~tfUniversal; \ TRANSACTION(MPTokenIssuanceSet, \ TF_FLAG(tfMPTLock, 0x00000001) \ - TF_FLAG(tfMPTUnlock, 0x00000002), \ + TF_FLAG(tfMPTUnlock, 0x00000002) \ + TF_FLAG(tfMPTSetCanLock, 0x00000004) \ + TF_FLAG(tfMPTSetRequireAuth, 0x00000008) \ + TF_FLAG(tfMPTSetCanEscrow, 0x00000010) \ + TF_FLAG(tfMPTSetCanTrade, 0x00000020) \ + TF_FLAG(tfMPTSetCanTransfer, 0x00000040) \ + TF_FLAG(tfMPTSetCanClawback, 0x00000080) \ + TF_FLAG(tfMPTSetCanHoldConfidentialBalance, 0x00000100), \ MASK_ADJ(0)) \ \ TRANSACTION(NFTokenCreateOffer, \ @@ -356,38 +363,25 @@ inline constexpr FlagValue tfMPTPaymentMask = ~(tfUniversal | tfPartialPayment); inline constexpr FlagValue tfTrustSetPermissionMask = ~(tfUniversal | tfSetfAuth | tfSetFreeze | tfClearFreeze); -// MPTokenIssuanceCreate MutableFlags: +// MPTokenIssuanceCreate ImmutableFlags: // Indicating specific fields or flags may be changed after issuance. -inline constexpr FlagValue tmfMPTCanEnableCanLock = lsmfMPTCanEnableCanLock; -inline constexpr FlagValue tmfMPTCanEnableRequireAuth = lsmfMPTCanEnableRequireAuth; -inline constexpr FlagValue tmfMPTCanEnableCanEscrow = lsmfMPTCanEnableCanEscrow; -inline constexpr FlagValue tmfMPTCanEnableCanTrade = lsmfMPTCanEnableCanTrade; -inline constexpr FlagValue tmfMPTCanEnableCanTransfer = lsmfMPTCanEnableCanTransfer; -inline constexpr FlagValue tmfMPTCanEnableCanClawback = lsmfMPTCanEnableCanClawback; -inline constexpr FlagValue tmfMPTCanMutateMetadata = lsmfMPTCanMutateMetadata; -inline constexpr FlagValue tmfMPTCanMutateTransferFee = lsmfMPTCanMutateTransferFee; -inline constexpr FlagValue tmfMPTCannotEnableCanHoldConfidentialBalance = - lsmfMPTCannotEnableCanHoldConfidentialBalance; -inline constexpr FlagValue tmfMPTokenIssuanceCreateMutableMask = - ~(tmfMPTCanEnableCanLock | tmfMPTCanEnableRequireAuth | tmfMPTCanEnableCanEscrow | - tmfMPTCanEnableCanTrade | tmfMPTCanEnableCanTransfer | tmfMPTCanEnableCanClawback | - tmfMPTCanMutateMetadata | tmfMPTCanMutateTransferFee | - tmfMPTCannotEnableCanHoldConfidentialBalance); - -// MPTokenIssuanceSet MutableFlags: -// Enable mutable capability flags. These flags are one-way: once enabled, -// the corresponding capability cannot be disabled by MPTokenIssuanceSet. - -inline constexpr FlagValue tmfMPTSetCanLock = 0x00000001; -inline constexpr FlagValue tmfMPTSetRequireAuth = 0x00000002; -inline constexpr FlagValue tmfMPTSetCanEscrow = 0x00000004; -inline constexpr FlagValue tmfMPTSetCanTrade = 0x00000008; -inline constexpr FlagValue tmfMPTSetCanTransfer = 0x00000010; -inline constexpr FlagValue tmfMPTSetCanClawback = 0x00000020; -inline constexpr FlagValue tmfMPTSetCanHoldConfidentialBalance = 0x00000040; -inline constexpr FlagValue tmfMPTokenIssuanceSetMutableMask = - ~(tmfMPTSetCanLock | tmfMPTSetRequireAuth | tmfMPTSetCanEscrow | tmfMPTSetCanTrade | - tmfMPTSetCanTransfer | tmfMPTSetCanClawback | tmfMPTSetCanHoldConfidentialBalance); +inline constexpr FlagValue tifMPTCanLock = lsifMPTCanLock; +inline constexpr FlagValue tifMPTRequireAuth = lsifMPTRequireAuth; +inline constexpr FlagValue tifMPTCanEscrow = lsifMPTCanEscrow; +inline constexpr FlagValue tifMPTCanTrade = lsifMPTCanTrade; +inline constexpr FlagValue tifMPTCanTransfer = lsifMPTCanTransfer; +inline constexpr FlagValue tifMPTCanClawback = lsifMPTCanClawback; +inline constexpr FlagValue tifMPTMetadata = lsifMPTMetadata; +inline constexpr FlagValue tifMPTTransferFee = lsifMPTTransferFee; +inline constexpr FlagValue tifMPTCanHoldConfidentialBalance = lsifMPTCanHoldConfidentialBalance; +inline constexpr FlagValue tifMPTokenIssuanceCreateImmutableMask = + ~(tifMPTCanLock | tifMPTRequireAuth | tifMPTCanEscrow | tifMPTCanTrade | tifMPTCanTransfer | + tifMPTCanClawback | tifMPTMetadata | tifMPTTransferFee | tifMPTCanHoldConfidentialBalance); + +// MPTokenIssuanceSet set of flags that is used to enable capabilities on an MPTokenIssuance. +inline constexpr FlagValue tfMPTokenIssuanceSetEnableFlagMask = tfMPTSetCanLock | + tfMPTSetRequireAuth | tfMPTSetCanEscrow | tfMPTSetCanTrade | tfMPTSetCanTransfer | + tfMPTSetCanClawback | tfMPTSetCanHoldConfidentialBalance; // Prior to fixRemoveNFTokenAutoTrustLine, transfer of an NFToken between accounts allowed a // TrustLine to be added to the issuer of that token without explicit permission from that issuer. diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 90810e06d2d..cc2eff0c52f 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -404,7 +404,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({ {sfPreviousTxnID, SoeRequired}, {sfPreviousTxnLgrSeq, SoeRequired}, {sfDomainID, SoeOptional}, - {sfMutableFlags, SoeDefault}, + {sfImmutableFlags, SoeDefault}, {sfReferenceHolding, SoeOptional}, {sfIssuerEncryptionKey, SoeOptional}, {sfAuditorEncryptionKey, SoeOptional}, diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 4ef76c8b759..56527628c96 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -97,7 +97,7 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48) TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) -TYPED_SFIELD(sfMutableFlags, UINT32, 53) +TYPED_SFIELD(sfImmutableFlags, UINT32, 53) TYPED_SFIELD(sfStartDate, UINT32, 54) TYPED_SFIELD(sfPaymentInterval, UINT32, 55) TYPED_SFIELD(sfGracePeriod, UINT32, 56) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index e805596c008..dc03cf7c59e 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -705,7 +705,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, {sfMaximumAmount, SoeOptional}, {sfMPTokenMetadata, SoeOptional}, {sfDomainID, SoeOptional}, - {sfMutableFlags, SoeOptional}, + {sfImmutableFlags, SoeOptional}, })) /** This transaction type destroys a MPTokensIssuance instance */ @@ -734,7 +734,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, {sfDomainID, SoeOptional}, {sfMPTokenMetadata, SoeOptional}, {sfTransferFee, SoeOptional}, - {sfMutableFlags, SoeOptional}, + {sfImmutableFlags, SoeOptional}, {sfIssuerEncryptionKey, SoeOptional}, {sfAuditorEncryptionKey, SoeOptional}, })) diff --git a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h index 8518a0fe14c..6a2caf52ae2 100644 --- a/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h +++ b/include/xrpl/protocol_autogen/ledger_entries/MPTokenIssuance.h @@ -256,27 +256,27 @@ class MPTokenIssuance : public LedgerEntryBase } /** - * @brief Get sfMutableFlags (SoeDefault) + * @brief Get sfImmutableFlags (SoeDefault) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] protocol_autogen::Optional - getMutableFlags() const + getImmutableFlags() const { - if (hasMutableFlags()) - return this->sle_->at(sfMutableFlags); + if (hasImmutableFlags()) + return this->sle_->at(sfImmutableFlags); return std::nullopt; } /** - * @brief Check if sfMutableFlags is present. + * @brief Check if sfImmutableFlags is present. * @return True if the field is present, false otherwise. */ [[nodiscard]] bool - hasMutableFlags() const + hasImmutableFlags() const { - return this->sle_->isFieldPresent(sfMutableFlags); + return this->sle_->isFieldPresent(sfImmutableFlags); } /** @@ -557,13 +557,13 @@ class MPTokenIssuanceBuilder : public LedgerEntryBuilderBase const& value) + setImmutableFlags(std::decay_t const& value) { - object_[sfMutableFlags] = value; + object_[sfImmutableFlags] = value; return *this; } diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h index e6fece83545..82ffba99969 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceCreate.h @@ -178,29 +178,29 @@ class MPTokenIssuanceCreate : public TransactionBase } /** - * @brief Get sfMutableFlags (SoeOptional) + * @brief Get sfImmutableFlags (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] protocol_autogen::Optional - getMutableFlags() const + getImmutableFlags() const { - if (hasMutableFlags()) + if (hasImmutableFlags()) { - return this->tx_->at(sfMutableFlags); + return this->tx_->at(sfImmutableFlags); } return std::nullopt; } /** - * @brief Check if sfMutableFlags is present. + * @brief Check if sfImmutableFlags is present. * @return True if the field is present, false otherwise. */ [[nodiscard]] bool - hasMutableFlags() const + hasImmutableFlags() const { - return this->tx_->isFieldPresent(sfMutableFlags); + return this->tx_->isFieldPresent(sfImmutableFlags); } }; @@ -302,13 +302,13 @@ class MPTokenIssuanceCreateBuilder : public TransactionBuilderBase const& value) + setImmutableFlags(std::decay_t const& value) { - object_[sfMutableFlags] = value; + object_[sfImmutableFlags] = value; return *this; } diff --git a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h index 803868c6401..ed7e1f0f6c3 100644 --- a/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h +++ b/include/xrpl/protocol_autogen/transactions/MPTokenIssuanceSet.h @@ -163,29 +163,29 @@ class MPTokenIssuanceSet : public TransactionBase } /** - * @brief Get sfMutableFlags (SoeOptional) + * @brief Get sfImmutableFlags (SoeOptional) * @return The field value, or std::nullopt if not present. */ [[nodiscard]] protocol_autogen::Optional - getMutableFlags() const + getImmutableFlags() const { - if (hasMutableFlags()) + if (hasImmutableFlags()) { - return this->tx_->at(sfMutableFlags); + return this->tx_->at(sfImmutableFlags); } return std::nullopt; } /** - * @brief Check if sfMutableFlags is present. + * @brief Check if sfImmutableFlags is present. * @return True if the field is present, false otherwise. */ [[nodiscard]] bool - hasMutableFlags() const + hasImmutableFlags() const { - return this->tx_->isFieldPresent(sfMutableFlags); + return this->tx_->isFieldPresent(sfImmutableFlags); } /** @@ -341,13 +341,13 @@ class MPTokenIssuanceSetBuilder : public TransactionBuilderBase const& value) + setImmutableFlags(std::decay_t const& value) { - object_[sfMutableFlags] = value; + object_[sfImmutableFlags] = value; return *this; } diff --git a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h index 5d35f65f44b..1aa853d6e27 100644 --- a/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h +++ b/include/xrpl/tx/transactors/token/MPTokenIssuanceCreate.h @@ -32,7 +32,7 @@ struct MPTCreateArgs std::optional transferFee = std::nullopt; std::optional const& metadata{}; std::optional domainId = std::nullopt; - std::optional mutableFlags = std::nullopt; + std::optional immutableFlags = std::nullopt; // Set only by callers that issue an MPT representing a wrapped asset // (e.g. VaultCreate's share token). The keylet must point to an // existing MPToken or RippleState owned by `account`. Surfaces on diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp index aad1642f68e..6970942616a 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceCreate.cpp @@ -35,17 +35,16 @@ MPTokenIssuanceCreate::checkExtraFeatures(PreflightContext const& ctx) ctx.rules.enabled(featureSingleAssetVault))) return false; - if (ctx.tx.isFieldPresent(sfMutableFlags) && !ctx.rules.enabled(featureDynamicMPT)) + if (ctx.tx.isFieldPresent(sfImmutableFlags) && !ctx.rules.enabled(featureDynamicMPT)) return false; if (ctx.tx.isFlag(tfMPTCanHoldConfidentialBalance) && !ctx.rules.enabled(featureConfidentialTransfer)) return false; - // can not set tmfMPTCannotEnableCanHoldConfidentialBalance without featureConfidentialTransfer - auto const mutableFlags = ctx.tx[~sfMutableFlags]; - return !mutableFlags || - ((*mutableFlags & tmfMPTCannotEnableCanHoldConfidentialBalance) == 0u) || + // can not set tifMPTCanHoldConfidentialBalance without featureConfidentialTransfer + auto const immutableFlags = ctx.tx[~sfImmutableFlags]; + return !immutableFlags || ((*immutableFlags & tifMPTCanHoldConfidentialBalance) == 0u) || ctx.rules.enabled(featureConfidentialTransfer); } @@ -64,10 +63,11 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) if (ctx.rules.enabled(fixCleanup3_2_0) && ctx.tx.isFieldPresent(sfReferenceHolding)) return temMALFORMED; - // If the mutable flags field is included, at least one flag must be - // specified. - if (auto const mutableFlags = ctx.tx[~sfMutableFlags]; mutableFlags && - ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceCreateMutableMask) != 0u))) + // If the immutable flags field is included, at least one flag must be + // specified, and undefined flags must not be specified. + if (auto const immutableFlags = ctx.tx[~sfImmutableFlags]; immutableFlags && + ((*immutableFlags == 0u) || + ((*immutableFlags & tifMPTokenIssuanceCreateImmutableMask) != 0u))) return temINVALID_FLAG; if (auto const fee = ctx.tx[~sfTransferFee]) @@ -170,8 +170,8 @@ MPTokenIssuanceCreate::create( if (args.domainId) (*mptIssuance)[sfDomainID] = *args.domainId; - if (args.mutableFlags) - (*mptIssuance)[sfMutableFlags] = *args.mutableFlags; + if (args.immutableFlags) + (*mptIssuance)[sfImmutableFlags] = *args.immutableFlags; if (args.referenceHolding) { @@ -217,7 +217,7 @@ MPTokenIssuanceCreate::doApply() .transferFee = tx[~sfTransferFee], .metadata = tx[~sfMPTokenMetadata], .domainId = tx[~sfDomainID], - .mutableFlags = tx[~sfMutableFlags], + .immutableFlags = tx[~sfImmutableFlags], }); return result ? tesSUCCESS : result.error(); } diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index d5262510696..5a29c6a8ebc 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -39,56 +39,57 @@ MPTokenIssuanceSet::getFlagsMask(PreflightContext const& ctx) return tfMPTokenIssuanceSetMask; } -// Maps each MPTokenIssuanceSet MutableFlags to the corresponding mutable -// flag and the target ledger flag to mutate. -struct MPTMutabilityFlags +// Maps each MPTokenIssuanceSet mutable flag to the issuance's +// corresponding immutable flag and the target ledger flag. +struct MPTFlagMapping { std::uint32_t setFlag; - std::uint32_t canEnableFlag; + std::uint32_t immutableFlag; std::uint32_t ledgerFlag; }; -static constexpr std::array kMptMutabilityFlags = { - {{.setFlag = tmfMPTSetCanLock, - .canEnableFlag = lsmfMPTCanEnableCanLock, - .ledgerFlag = lsfMPTCanLock}, - {.setFlag = tmfMPTSetRequireAuth, - .canEnableFlag = lsmfMPTCanEnableRequireAuth, +static constexpr std::array flagMapping = { + {{.setFlag = tfMPTSetCanLock, .immutableFlag = lsifMPTCanLock, .ledgerFlag = lsfMPTCanLock}, + {.setFlag = tfMPTSetRequireAuth, + .immutableFlag = lsifMPTRequireAuth, .ledgerFlag = lsfMPTRequireAuth}, - {.setFlag = tmfMPTSetCanEscrow, - .canEnableFlag = lsmfMPTCanEnableCanEscrow, + {.setFlag = tfMPTSetCanEscrow, + .immutableFlag = lsifMPTCanEscrow, .ledgerFlag = lsfMPTCanEscrow}, - {.setFlag = tmfMPTSetCanTrade, - .canEnableFlag = lsmfMPTCanEnableCanTrade, - .ledgerFlag = lsfMPTCanTrade}, - {.setFlag = tmfMPTSetCanTransfer, - .canEnableFlag = lsmfMPTCanEnableCanTransfer, + {.setFlag = tfMPTSetCanTrade, .immutableFlag = lsifMPTCanTrade, .ledgerFlag = lsfMPTCanTrade}, + {.setFlag = tfMPTSetCanTransfer, + .immutableFlag = lsifMPTCanTransfer, .ledgerFlag = lsfMPTCanTransfer}, - {.setFlag = tmfMPTSetCanClawback, - .canEnableFlag = lsmfMPTCanEnableCanClawback, - .ledgerFlag = lsfMPTCanClawback}}}; + {.setFlag = tfMPTSetCanClawback, + .immutableFlag = lsifMPTCanClawback, + .ledgerFlag = lsfMPTCanClawback}, + {.setFlag = tfMPTSetCanHoldConfidentialBalance, + .immutableFlag = lsifMPTCanHoldConfidentialBalance, + .ledgerFlag = lsfMPTCanHoldConfidentialBalance}}}; NotTEC MPTokenIssuanceSet::preflight(PreflightContext const& ctx) { - auto const mutableFlags = ctx.tx[~sfMutableFlags]; + auto const txFlags = ctx.tx.getFlags(); + auto const enableFlags = txFlags & tfMPTokenIssuanceSetEnableFlagMask; auto const metadata = ctx.tx[~sfMPTokenMetadata]; auto const transferFee = ctx.tx[~sfTransferFee]; - auto const isMutate = mutableFlags || metadata || transferFee; + auto const immutableFlags = ctx.tx[~sfImmutableFlags]; + auto const isMutate = (enableFlags != 0u) || metadata || transferFee || immutableFlags; auto const hasIssuerElGamalKey = ctx.tx.isFieldPresent(sfIssuerEncryptionKey); auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorEncryptionKey); - auto const txFlags = ctx.tx.getFlags(); - - bool const enablePrivacy = - mutableFlags && (*mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u; + bool const enablePrivacy = (enableFlags & tfMPTSetCanHoldConfidentialBalance) != 0u; auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID); auto const hasHolder = ctx.tx.isFieldPresent(sfHolder); if (isMutate && !ctx.rules.enabled(featureDynamicMPT)) return temDISABLED; - if ((hasIssuerElGamalKey || hasAuditorElGamalKey || enablePrivacy) && + bool const setConfidentialBalanceImmutable = + immutableFlags && (*immutableFlags & tifMPTCanHoldConfidentialBalance) != 0u; + if ((hasIssuerElGamalKey || hasAuditorElGamalKey || enablePrivacy || + setConfidentialBalanceImmutable) && !ctx.rules.enabled(featureConfidentialTransfer)) return temDISABLED; @@ -122,8 +123,9 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) if (isMutate && holderID) return temMALFORMED; - // Can not set flags when mutating MPTokenIssuance - if (isMutate && ((ctx.tx.getFlags() & tfUniversalMask) != 0u)) + // A single transaction may either lock/unlock or mutate capability + // flags, but not both. + if (isMutate && (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))) return temMALFORMED; if (transferFee && *transferFee > kMaxTransferFee) @@ -135,11 +137,12 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) if (metadata && metadata->length() > kMaxMpTokenMetadataLength) return temMALFORMED; - if (mutableFlags) - { - if ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceSetMutableMask) != 0u)) - return temINVALID_FLAG; - } + // If the immutable flags field is included, at least one flag must be + // specified, and undefined flags must not be specified. + if (immutableFlags && + ((*immutableFlags == 0u) || + ((*immutableFlags & tifMPTokenIssuanceCreateImmutableMask) != 0u))) + return temINVALID_FLAG; } if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey)) @@ -207,38 +210,31 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) } } - // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on + // sfImmutableFlags is soeDEFAULT, defaulting to 0 if not specified on // the ledger. - auto const currentMutableFlags = sleMptIssuance->getFieldU32(sfMutableFlags); + auto const currentImmutableFlags = sleMptIssuance->getFieldU32(sfImmutableFlags); - auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { - return currentMutableFlags & mutableFlag; - }; + auto isImmutable = [&](std::uint32_t flag) -> bool { return currentImmutableFlags & flag; }; - auto const mutableFlags = ctx.tx[~sfMutableFlags]; + auto const enableFlags = ctx.tx.getFlags() & tfMPTokenIssuanceSetEnableFlagMask; // Whether the transaction is enabling confidential amounts. - bool const enablesConfidentialAmount = - mutableFlags && (*mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u; - if (mutableFlags) + if (enableFlags) { - if (std::ranges::any_of(kMptMutabilityFlags, [mutableFlags, &isMutableFlag](auto const& f) { - return !isMutableFlag(f.canEnableFlag) && ((*mutableFlags & f.setFlag) != 0u); + // If any of the flags to be set is immutable at issuance, return tecNO_PERMISSION. + if (std::ranges::any_of(flagMapping, [&](auto const& f) { + return isImmutable(f.immutableFlag) && ctx.tx.isFlag(f.setFlag); })) return tecNO_PERMISSION; - - if (enablesConfidentialAmount && - isMutableFlag(lsmfMPTCannotEnableCanHoldConfidentialBalance)) - return tecNO_PERMISSION; } - if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata)) + if (isImmutable(lsifMPTMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata)) return tecNO_PERMISSION; if (auto const fee = ctx.tx[~sfTransferFee]) { // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag // was previously enabled (at issuance or via a prior mutation). Setting - // it by tmfMPTSetCanTransfer in the current transaction does not meet + // it by tfMPTSetCanTransfer in the current transaction does not meet // this requirement. if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer)) return tecNO_PERMISSION; @@ -248,7 +244,7 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) if (fee > 0u && sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance)) return tecNO_PERMISSION; - if (!isMutableFlag(lsmfMPTCanMutateTransferFee)) + if (isImmutable(lsifMPTTransferFee)) return tecNO_PERMISSION; } @@ -266,27 +262,29 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) return tecNO_PERMISSION; // LCOV_EXCL_LINE } - if (enablesConfidentialAmount && sleMptIssuance->isFieldPresent(sfTransferFee) && + auto const enablesConfidentialBalance = + (enableFlags & tfMPTSetCanHoldConfidentialBalance) != 0u; + if (enablesConfidentialBalance && sleMptIssuance->isFieldPresent(sfTransferFee) && (*sleMptIssuance)[sfTransferFee] > 0u) return tecNO_PERMISSION; // Encryption keys can only be set if confidential amounts are already // enabled on the issuance OR if the transaction is enabling it if (ctx.tx.isFieldPresent(sfIssuerEncryptionKey) && - !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialAmount) + !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialBalance) { return tecNO_PERMISSION; } if (ctx.tx.isFieldPresent(sfAuditorEncryptionKey) && - !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialAmount) + !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialBalance) { return tecNO_PERMISSION; } // cannot upload key if there's circulating supply of COA if ((ctx.tx.isFieldPresent(sfIssuerEncryptionKey) || - ctx.tx.isFieldPresent(sfAuditorEncryptionKey) || enablesConfidentialAmount) && + ctx.tx.isFieldPresent(sfAuditorEncryptionKey) || enablesConfidentialBalance) && (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0) { return tecNO_PERMISSION; // LCOV_EXCL_LINE @@ -327,23 +325,42 @@ MPTokenIssuanceSet::doApply() flagsOut &= ~lsfMPTLocked; } - if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0)) + if (auto const enableFlags = (ctx_.tx.getFlags() & tfMPTokenIssuanceSetEnableFlagMask)) { - for (auto const& f : kMptMutabilityFlags) + for (auto const& f : flagMapping) { - if ((mutableFlags & f.setFlag) != 0u) + if (ctx_.tx.isFlag(f.setFlag)) { flagsOut |= f.ledgerFlag; } } - - if ((mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u) - flagsOut |= lsfMPTCanHoldConfidentialBalance; } if (flagsIn != flagsOut) sle->setFieldU32(sfFlags, flagsOut); + if (auto const immutableFlags = ctx_.tx[~sfImmutableFlags]) + { + // sle is ltMPTOKEN_ISSUANCE here, not ltMPTOKEN, because sfHolder and + // sfImmutableFlags can never both be present on the same + // transaction: sfImmutableFlags makes isMutate true in preflight, + // and preflight rejects any isMutate transaction that also carries + // sfHolder (temMALFORMED, see the "Holder field is not allowed when + // mutating MPTokenIssuance" check above). Since holderID's absence + // is exactly what made doApply peek the MPTokenIssuance keylet + // (rather than the per-holder MPToken keylet) a few lines up, sle's + // type is guaranteed here. The runtime check below is a defensive + // fallback in case that invariant is ever broken by a future change. + XRPL_ASSERT( + sle->getType() == ltMPTOKEN_ISSUANCE, + "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance"); + + if (sle->getType() != ltMPTOKEN_ISSUANCE) + return tecINTERNAL; // LCOV_EXCL_LINE + + (*sle)[sfImmutableFlags] = (*sle)[sfImmutableFlags] | *immutableFlags; + } + if (auto const transferFee = ctx_.tx[~sfTransferFee]) { // TransferFee uses soeDEFAULT style: diff --git a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp index e1f5873a897..d793b3a1e26 100644 --- a/src/libxrpl/tx/transactors/vault/VaultCreate.cpp +++ b/src/libxrpl/tx/transactors/vault/VaultCreate.cpp @@ -209,7 +209,6 @@ VaultCreate::doApply() .transferFee = std::nullopt, .metadata = tx[~sfMPTokenMetadata], .domainId = tx[~sfDomainID], - .mutableFlags = std::nullopt, .referenceHolding = referenceHolding, }); if (!maybeShare) diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index d3e0182db52..864de165dab 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -616,7 +616,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, }); } @@ -637,7 +637,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .issuerPubKey = mptAlice.getPubKey(alice), .auditorPubKey = mptAlice.getPubKey(auditor), }); @@ -880,11 +880,11 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase Account const alice("alice"); MPTTester mptAlice(env, alice, {.holders = {}}); - // Create with tmfMPTCannotEnableCanHoldConfidentialBalance + // Create with tifMPTCanHoldConfidentialBalance mptAlice.create({ .ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock, - .mutableFlags = tmfMPTCannotEnableCanHoldConfidentialBalance, + .immutableFlags = tifMPTCanHoldConfidentialBalance, }); mptAlice.generateKeyPair(alice); @@ -893,7 +893,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase // because the issuance cannot mutate canConfidentialAmount mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .issuerPubKey = mptAlice.getPubKey(alice), .err = tecNO_PERMISSION, }); @@ -965,15 +965,11 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase Account const alice("alice"); MPTTester mptAlice(env, alice, {.holders = {}}); - mptAlice.create({ - .ownerCount = 1, - .flags = tfMPTCanTransfer | tfMPTCanLock, - .mutableFlags = tmfMPTCanMutateTransferFee, - }); + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock}); mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .transferFee = 100, .err = temBAD_TRANSFER_FEE, }); @@ -986,16 +982,12 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase Account const alice("alice"); MPTTester mptAlice(env, alice, {.holders = {}}); - mptAlice.create({ - .transferFee = 100, - .ownerCount = 1, - .flags = tfMPTCanTransfer | tfMPTCanLock, - .mutableFlags = tmfMPTCanMutateTransferFee, - }); + mptAlice.create( + {.transferFee = 100, .ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock}); mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .err = tecNO_PERMISSION, }); } @@ -1007,11 +999,9 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase Account const alice("alice"); MPTTester mptAlice(env, alice, {.holders = {}}); - mptAlice.create({ - .ownerCount = 1, - .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance, - .mutableFlags = tmfMPTCanMutateTransferFee, - }); + mptAlice.create( + {.ownerCount = 1, + .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanHoldConfidentialBalance}); mptAlice.set({ .account = alice, @@ -5087,7 +5077,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase testcase("mutate lsfMPTCanHoldConfidentialBalance"); using namespace test::jtx; - // can not create mpt issuance with tmfMPTCannotEnableCanHoldConfidentialBalance + // can not create mpt issuance with tifMPTCanHoldConfidentialBalance // when featureDynamicMPT is disabled { Env env{*this, features - featureDynamicMPT}; @@ -5097,12 +5087,12 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.create({ .ownerCount = 0, - .mutableFlags = tmfMPTCannotEnableCanHoldConfidentialBalance, + .immutableFlags = tifMPTCanHoldConfidentialBalance, .err = temDISABLED, }); } - // can not create mpt issuance with tmfMPTCannotEnableCanHoldConfidentialBalance when + // can not create mpt issuance with tifMPTCanHoldConfidentialBalance when // featureConfidentialTransfer is disabled { Env env{*this, features - featureConfidentialTransfer}; @@ -5112,12 +5102,12 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.create({ .ownerCount = 0, - .mutableFlags = tmfMPTCannotEnableCanHoldConfidentialBalance, + .immutableFlags = tifMPTCanHoldConfidentialBalance, .err = temDISABLED, }); } - // if lsmfMPTCannotEnableCanHoldConfidentialBalance is set, can not set/clear + // if lsifMPTCanHoldConfidentialBalance is set, can not set/clear // lsfMPTCanHoldConfidentialBalance { Env env{*this, features}; @@ -5128,12 +5118,12 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.create({ .ownerCount = 1, .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCannotEnableCanHoldConfidentialBalance, + .immutableFlags = tifMPTCanHoldConfidentialBalance, }); mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .err = tecNO_PERMISSION, }); } @@ -5148,7 +5138,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase mptAlice.create({ .ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanHoldConfidentialBalance, - .mutableFlags = tmfMPTCanEnableCanLock, + .immutableFlags = tifMPTCanLock, }); mptAlice.authorize({ @@ -5200,14 +5190,14 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase // lsfMPTCanHoldConfidentialBalance was already set mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, }); verifyToggle(tesSUCCESS, 10); - // set tmfMPTSetCanHoldConfidentialBalance again + // set tfMPTSetCanHoldConfidentialBalance again mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, }); verifyToggle(tesSUCCESS, 30); } @@ -5220,7 +5210,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase Account const bob("bob"); MPTTester mptAlice(env, alice, {.holders = {bob}}); - // lsmfMPTCannotEnableCanHoldConfidentialBalance is false by default, + // lsifMPTCanHoldConfidentialBalance is false by default, // so that lsfMPTCanHoldConfidentialBalance can be mutated mptAlice.create({ .ownerCount = 1, @@ -5243,7 +5233,7 @@ class ConfidentialTransfer_test : public ConfidentialTransferTestBase // confidential outstanding balance mptAlice.set({ .account = alice, - .mutableFlags = tmfMPTSetCanHoldConfidentialBalance, + .flags = tfMPTSetCanHoldConfidentialBalance, .err = tecNO_PERMISSION, }); } diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 257ed336191..442c1d98417 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -2167,11 +2167,12 @@ class Delegate_test : public beast::unit_test::Suite env(delegate::set(alice, bob, {"MPTokenIssuanceLock"})); env.close(); - // Field is not permitted, permitted fields for delegation is defined in - // permissions.macro. + // tfMPTSetCanLock is a valid MPTokenIssuanceSet flag but is not + // covered by the MPTokenIssuanceLock granular permission, so a + // delegate holding only that permission cannot set it. mpt.set( {.account = alice, - .mutableFlags = 2, + .flags = tfMPTSetCanLock, .delegate = bob, .err = terNO_DELEGATE_PERMISSION}); diff --git a/src/test/app/Loan_test.cpp b/src/test/app/Loan_test.cpp index 231a3b405a7..c86fbcceff6 100644 --- a/src/test/app/Loan_test.cpp +++ b/src/test/app/Loan_test.cpp @@ -5448,8 +5448,7 @@ class Loan_test : public beast::unit_test::Suite {.env = env, .issuer = issuer, .holders = {lender, borrower}, - .flags = tfMPTCanTransfer | tfMPTCanLock, - .mutableFlags = tmfMPTCanEnableCanTrade}); + .flags = tfMPTCanTransfer | tfMPTCanLock}); PrettyAsset const asset = mpt.issuanceID(); env(pay(issuer, lender, asset(10'000'000))); env(pay(issuer, borrower, asset(100'000))); @@ -5484,7 +5483,7 @@ class Loan_test : public beast::unit_test::Suite env.close(); // Enable CanTrade and verify the DEX path is restored. - mpt.set({.mutableFlags = tmfMPTSetCanTrade}); + mpt.set({.flags = tfMPTSetCanTrade}); env.close(); env(offer(lender, XRP(1), asset(10))); diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index befc46e2aea..44298e8d430 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -602,9 +602,9 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.authorize({.account = bob, .holderCount = 1}); - // test invalid flag - only valid flags are tfMPTLock (1) and Unlock - // (2) - mptAlice.set({.account = alice, .flags = 0x00000008, .err = temINVALID_FLAG}); + // test invalid flag - an unrecognized flag bit is always + // rejected, regardless of which amendments are enabled + mptAlice.set({.account = alice, .flags = 0x00001000, .err = temINVALID_FLAG}); if (!features[featureSingleAssetVault] && !features[featureDynamicMPT] && !features[featureConfidentialTransfer]) @@ -3393,26 +3393,26 @@ class MPToken_test : public beast::unit_test::Suite using namespace test::jtx; Account const alice("alice"); - // Can not provide MutableFlags when DynamicMPT amendment is not enabled + // Can not provide ImmutableFlags when DynamicMPT amendment is not enabled { Env env{*this, features - featureDynamicMPT}; MPTTester mptAlice(env, alice); - mptAlice.create({.ownerCount = 0, .mutableFlags = 2, .err = temDISABLED}); - mptAlice.create({.ownerCount = 0, .mutableFlags = 0, .err = temDISABLED}); + mptAlice.create({.ownerCount = 0, .immutableFlags = 2, .err = temDISABLED}); + mptAlice.create({.ownerCount = 0, .immutableFlags = 0, .err = temDISABLED}); } - // MutableFlags contains invalid values + // ImmutableFlags contains invalid values { Env env{*this, features}; MPTTester mptAlice(env, alice); // Value 1 is reserved for MPT lock. - mptAlice.create({.ownerCount = 0, .mutableFlags = 1, .err = temINVALID_FLAG}); - mptAlice.create({.ownerCount = 0, .mutableFlags = 17, .err = temINVALID_FLAG}); - mptAlice.create({.ownerCount = 0, .mutableFlags = 65535, .err = temINVALID_FLAG}); + mptAlice.create({.ownerCount = 0, .immutableFlags = 1, .err = temINVALID_FLAG}); + mptAlice.create({.ownerCount = 0, .immutableFlags = 17, .err = temINVALID_FLAG}); + mptAlice.create({.ownerCount = 0, .immutableFlags = 65535, .err = temINVALID_FLAG}); - // MutableFlags can not be 0 - mptAlice.create({.ownerCount = 0, .mutableFlags = 0, .err = temINVALID_FLAG}); + // ImmutableFlags can not be 0 + mptAlice.create({.ownerCount = 0, .immutableFlags = 0, .err = temINVALID_FLAG}); } } @@ -3425,16 +3425,16 @@ class MPToken_test : public beast::unit_test::Suite Account const alice("alice"); Account const bob("bob"); - // Can not provide MutableFlags, MPTokenMetadata or TransferFee when + // Can not provide mutate related flags, MPTokenMetadata or TransferFee when // DynamicMPT amendment is not enabled { Env env{*this, features - featureDynamicMPT}; MPTTester mptAlice(env, alice, {.holders = {bob}}); auto const mptID = makeMptID(env.seq(alice), alice); - // MutableFlags is not allowed when DynamicMPT is not enabled - mptAlice.set({.account = alice, .id = mptID, .mutableFlags = 2, .err = temDISABLED}); - mptAlice.set({.account = alice, .id = mptID, .mutableFlags = 0, .err = temDISABLED}); + // mutate related flags is not allowed when DynamicMPT is not enabled + mptAlice.set( + {.account = alice, .id = mptID, .flags = tfMPTSetCanLock, .err = temDISABLED}); // MPTokenMetadata is not allowed when DynamicMPT is not enabled mptAlice.set({.account = alice, .id = mptID, .metadata = "test", .err = temDISABLED}); @@ -3445,19 +3445,19 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.set({.account = alice, .id = mptID, .transferFee = 0, .err = temDISABLED}); } - // Can not provide holder when MutableFlags, MPTokenMetadata or + // Can not provide holder when mutate related flags, MPTokenMetadata or // TransferFee is present { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); auto const mptID = makeMptID(env.seq(alice), alice); - // Holder is not allowed when MutableFlags is present + // Holder is not allowed when mutate related flags is present mptAlice.set( {.account = alice, .holder = bob, .id = mptID, - .mutableFlags = 2, + .flags = tfMPTSetCanLock, .err = temMALFORMED}); // Holder is not allowed when MPTokenMetadata is present @@ -3477,27 +3477,24 @@ class MPToken_test : public beast::unit_test::Suite .err = temMALFORMED}); } - // Can not set Flags when MutableFlags, MPTokenMetadata or + // Can not lock when mutate related flags, MPTokenMetadata or // TransferFee is present { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .mutableFlags = tmfMPTCanMutateMetadata | tmfMPTCanEnableCanLock | - tmfMPTCanMutateTransferFee}); + mptAlice.create({.ownerCount = 1}); - // Setting flags is not allowed when MutableFlags is present + // lock is not allowed when mutate related flags is present mptAlice.set( - {.account = alice, .flags = tfMPTCanLock, .mutableFlags = 2, .err = temMALFORMED}); + {.account = alice, .flags = tfMPTLock | tfMPTSetCanLock, .err = temMALFORMED}); - // Setting flags is not allowed when MPTokenMetadata is present + // lock is not allowed when MPTokenMetadata is present mptAlice.set( - {.account = alice, .flags = tfMPTCanLock, .metadata = "test", .err = temMALFORMED}); + {.account = alice, .flags = tfMPTLock, .metadata = "test", .err = temMALFORMED}); - // setting flags is not allowed when TransferFee is present + // lock is not allowed when TransferFee is present mptAlice.set( - {.account = alice, .flags = tfMPTCanLock, .transferFee = 100, .err = temMALFORMED}); + {.account = alice, .flags = tfMPTLock, .transferFee = 100, .err = temMALFORMED}); } // Flags being 0 or tfFullyCanonicalSig is fine @@ -3509,48 +3506,48 @@ class MPToken_test : public beast::unit_test::Suite {.transferFee = 10, .ownerCount = 1, .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanMutateTransferFee | tmfMPTCanMutateMetadata}); + .immutableFlags = tifMPTTransferFee}); - mptAlice.set({.account = alice, .flags = 0, .transferFee = 100, .metadata = "test"}); - mptAlice.set( - {.account = alice, - .flags = tfFullyCanonicalSig, - .transferFee = 200, - .metadata = "test2"}); + mptAlice.set({.account = alice, .flags = 0, .metadata = "test"}); + mptAlice.set({.account = alice, .flags = tfFullyCanonicalSig, .metadata = "test2"}); } - // Invalid MutableFlags + // Invalid flags { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); auto const mptID = makeMptID(env.seq(alice), alice); - for (auto const flags : {10000, 0, 5000}) + for (auto const flags : {10000, 5000}) { mptAlice.set( - {.account = alice, .id = mptID, .mutableFlags = flags, .err = temINVALID_FLAG}); + {.account = alice, .id = mptID, .flags = flags, .err = temINVALID_FLAG}); } } - // Can not mutate flag which is not mutable + // Can not mutate flag which is immutable { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1}); - - auto const mutableFlags = { - tmfMPTSetCanLock, - tmfMPTSetRequireAuth, - tmfMPTSetCanEscrow, - tmfMPTSetCanTrade, - tmfMPTSetCanTransfer, - tmfMPTSetCanClawback}; - - for (auto const& mutableFlag : mutableFlags) + mptAlice.create( + {.ownerCount = 1, + .immutableFlags = tifMPTCanLock | tifMPTCanTrade | tifMPTCanTransfer | + tifMPTCanClawback | tifMPTCanEscrow | tifMPTRequireAuth | + tifMPTCanHoldConfidentialBalance}); + + auto const setFlags = { + tfMPTSetCanLock, + tfMPTSetRequireAuth, + tfMPTSetCanEscrow, + tfMPTSetCanTrade, + tfMPTSetCanTransfer, + tfMPTSetCanClawback, + tfMPTSetCanHoldConfidentialBalance}; + + for (auto const& mutableFlag : setFlags) { - mptAlice.set( - {.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); + mptAlice.set({.account = alice, .flags = mutableFlag, .err = tecNO_PERMISSION}); } } @@ -3559,18 +3556,18 @@ class MPToken_test : public beast::unit_test::Suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1, .mutableFlags = tmfMPTCanMutateMetadata}); + mptAlice.create({.ownerCount = 1}); std::string const metadata(kMaxMpTokenMetadataLength + 1, 'a'); mptAlice.set({.account = alice, .metadata = metadata, .err = temMALFORMED}); } - // Can not mutate metadata when it is not mutable + // Can not mutate metadata when it is immutable { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.ownerCount = 1}); + mptAlice.create({.ownerCount = 1, .immutableFlags = tifMPTMetadata}); mptAlice.set({.account = alice, .metadata = "test", .err = tecNO_PERMISSION}); } @@ -3580,7 +3577,7 @@ class MPToken_test : public beast::unit_test::Suite MPTTester mptAlice(env, alice, {.holders = {bob}}); auto const mptID = makeMptID(env.seq(alice), alice); - mptAlice.create({.ownerCount = 1, .mutableFlags = tmfMPTCanMutateTransferFee}); + mptAlice.create({.ownerCount = 1}); mptAlice.set( {.account = alice, @@ -3594,9 +3591,7 @@ class MPToken_test : public beast::unit_test::Suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .mutableFlags = tmfMPTCanMutateTransferFee | tmfMPTCanEnableCanTransfer}); + mptAlice.create({.ownerCount = 1}); mptAlice.set({.account = alice, .transferFee = 100, .err = tecNO_PERMISSION}); @@ -3605,7 +3600,7 @@ class MPToken_test : public beast::unit_test::Suite // fee can be set in a separate transaction. mptAlice.set( {.account = alice, - .mutableFlags = tmfMPTSetCanTransfer, + .flags = tfMPTSetCanTransfer, .transferFee = 100, .err = tecNO_PERMISSION}); } @@ -3615,45 +3610,38 @@ class MPToken_test : public beast::unit_test::Suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create({.transferFee = 10, .ownerCount = 1, .flags = tfMPTCanTransfer}); + mptAlice.create( + {.transferFee = 10, + .ownerCount = 1, + .flags = tfMPTCanTransfer, + .immutableFlags = tifMPTTransferFee}); mptAlice.set({.account = alice, .transferFee = 100, .err = tecNO_PERMISSION}); mptAlice.set({.account = alice, .transferFee = 0, .err = tecNO_PERMISSION}); } - // Set some flags mutable. Can not mutate the others + // Set some flags immutable. Others can still be mutated (enable once). { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); mptAlice.create( {.ownerCount = 1, - .mutableFlags = tmfMPTCanEnableCanTrade | tmfMPTCanEnableCanTransfer | - tmfMPTCanMutateMetadata}); + .immutableFlags = tifMPTCanTrade | tifMPTCanTransfer | tifMPTMetadata}); - // Can not mutate transfer fee - mptAlice.set({.account = alice, .transferFee = 100, .err = tecNO_PERMISSION}); + auto const canEnableFlags = { + tfMPTSetCanLock, tfMPTSetRequireAuth, tfMPTSetCanEscrow, tfMPTSetCanClawback}; - auto const invalidFlags = { - tmfMPTSetCanLock, tmfMPTSetRequireAuth, tmfMPTSetCanEscrow, tmfMPTSetCanClawback}; + // Can not enable immutable flags + mptAlice.set({.account = alice, .flags = tfMPTSetCanTrade, .err = tecNO_PERMISSION}); + mptAlice.set({.account = alice, .flags = tfMPTSetCanTransfer, .err = tecNO_PERMISSION}); - // Can not mutate flags which are not mutable - for (auto const& mutableFlag : invalidFlags) + // Can enable flags which are not immutable + for (auto const& mutableFlag : canEnableFlags) { - mptAlice.set( - {.account = alice, .mutableFlags = mutableFlag, .err = tecNO_PERMISSION}); + mptAlice.set({.account = alice, .flags = mutableFlag}); } - - // Can mutate MPTCanTrade - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanTrade}); - - // Can mutate MPTCanTransfer - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanTransfer}); - - // Can mutate metadata - mptAlice.set({.account = alice, .metadata = "test"}); - mptAlice.set({.account = alice, .metadata = ""}); } } @@ -3669,8 +3657,7 @@ class MPToken_test : public beast::unit_test::Suite { Env env{*this, features}; MPTTester mptAlice(env, alice); - mptAlice.create( - {.metadata = "test", .ownerCount = 1, .mutableFlags = tmfMPTCanMutateMetadata}); + mptAlice.create({.metadata = "test", .ownerCount = 1}); std::vector const metadatas = { "mutate metadata", @@ -3699,8 +3686,7 @@ class MPToken_test : public beast::unit_test::Suite {.transferFee = 100, .metadata = "test", .ownerCount = 1, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanMutateTransferFee}); + .flags = tfMPTCanTransfer}); for (std::uint16_t const fee : std::initializer_list{1, 10, 100, 200, 500, 1000, kMaxTransferFee}) @@ -3725,19 +3711,19 @@ class MPToken_test : public beast::unit_test::Suite MPTTester mptAlice(env, alice); // Create the MPT object with the specified initial flags - mptAlice.create({.metadata = "test", .ownerCount = 1, .mutableFlags = createFlags}); + mptAlice.create({.metadata = "test", .ownerCount = 1}); // Setting the same mutable capability more than once is harmless. - mptAlice.set({.account = alice, .mutableFlags = setFlags}); - mptAlice.set({.account = alice, .mutableFlags = setFlags}); + mptAlice.set({.account = alice, .flags = setFlags}); + mptAlice.set({.account = alice, .flags = setFlags}); }; - testFlagSet(tmfMPTCanEnableCanLock, tmfMPTSetCanLock); - testFlagSet(tmfMPTCanEnableRequireAuth, tmfMPTSetRequireAuth); - testFlagSet(tmfMPTCanEnableCanEscrow, tmfMPTSetCanEscrow); - testFlagSet(tmfMPTCanEnableCanTrade, tmfMPTSetCanTrade); - testFlagSet(tmfMPTCanEnableCanTransfer, tmfMPTSetCanTransfer); - testFlagSet(tmfMPTCanEnableCanClawback, tmfMPTSetCanClawback); + testFlagSet(tifMPTCanLock, tfMPTSetCanLock); + testFlagSet(tifMPTRequireAuth, tfMPTSetRequireAuth); + testFlagSet(tifMPTCanEscrow, tfMPTSetCanEscrow); + testFlagSet(tifMPTCanTrade, tfMPTSetCanTrade); + testFlagSet(tifMPTCanTransfer, tfMPTSetCanTransfer); + testFlagSet(tifMPTCanClawback, tfMPTSetCanClawback); } } @@ -3754,71 +3740,34 @@ class MPToken_test : public beast::unit_test::Suite { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .holderCount = 0, - .flags = tfMPTCanLock | tfMPTCanTransfer, - .mutableFlags = tmfMPTCanEnableCanLock | tmfMPTCanEnableCanTrade | - tmfMPTCanMutateTransferFee}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); mptAlice.authorize({.account = bob, .holderCount = 1}); - // Lock bob's mptoken - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); + // Lock bob's mptoken fails because alice has not enabled MPTCanLock + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); // Can mutate the mutable flags and fields - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanLock}); - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanTrade}); - mptAlice.set({.account = alice, .transferFee = 200}); - } - - // Global lock - { - Env env{*this, features}; - MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .holderCount = 0, - .flags = tfMPTCanLock, - .mutableFlags = tmfMPTCanEnableCanLock | tmfMPTCanEnableCanClawback | - tmfMPTCanMutateMetadata}); - mptAlice.authorize({.account = bob, .holderCount = 1}); - - // Lock issuance - mptAlice.set({.account = alice, .flags = tfMPTLock}); + mptAlice.set({.account = alice, .flags = tfMPTSetCanLock}); - // Can mutate the mutable flags and fields - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanLock}); - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanClawback}); - mptAlice.set({.account = alice, .metadata = "mutate"}); + // Now can lock + mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); } - // Test lock and unlock after enabling MPTCanLock + // Global lock { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .holderCount = 0, - .mutableFlags = tmfMPTCanEnableCanLock | tmfMPTCanEnableCanClawback | - tmfMPTCanMutateMetadata}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); mptAlice.authorize({.account = bob, .holderCount = 1}); - // Can not lock or unlock before MPTCanLock is enabled + // Lock issuance fails because alice has not enabled MPTCanLock mptAlice.set({.account = alice, .flags = tfMPTLock, .err = tecNO_PERMISSION}); - mptAlice.set({.account = alice, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); - mptAlice.set( - {.account = alice, .holder = bob, .flags = tfMPTLock, .err = tecNO_PERMISSION}); - mptAlice.set( - {.account = alice, .holder = bob, .flags = tfMPTUnlock, .err = tecNO_PERMISSION}); - - // Set MPTCanLock - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanLock}); - // Can lock and unlock + // Can mutate the mutable flags and fields + mptAlice.set({.account = alice, .flags = tfMPTSetCanLock}); + // Now can lock mptAlice.set({.account = alice, .flags = tfMPTLock}); - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); - mptAlice.set({.account = alice, .flags = tfMPTUnlock}); - mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTUnlock}); } } @@ -3835,16 +3784,13 @@ class MPToken_test : public beast::unit_test::Suite Account const bob("bob"); MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanEnableRequireAuth}); + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer}); mptAlice.authorize({.account = bob}); mptAlice.pay(alice, bob, 1000); // Set RequireAuth because it is mutable. - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetRequireAuth}); + mptAlice.set({.account = alice, .flags = tfMPTSetRequireAuth}); // This should fail because bob is not authorized yet. mptAlice.pay(alice, bob, 1000, tecNO_AUTH); @@ -3868,11 +3814,7 @@ class MPToken_test : public beast::unit_test::Suite auto const carol = Account("carol"); MPTTester mptAlice(env, alice, {.holders = {carol, bob}}); - mptAlice.create( - {.ownerCount = 1, - .holderCount = 0, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanEnableCanEscrow}); + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer}); mptAlice.authorize({.account = carol}); mptAlice.authorize({.account = bob}); @@ -3889,7 +3831,7 @@ class MPToken_test : public beast::unit_test::Suite Ter(tecNO_PERMISSION)); // MPTCanEscrow is enabled now - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanEscrow}); + mptAlice.set({.account = alice, .flags = tfMPTSetCanEscrow}); env(escrow::create(carol, bob, mpt(3)), escrow::kCondition(escrow::kCb1), escrow::kFinishTime(env.now() + 1s), @@ -3910,9 +3852,7 @@ class MPToken_test : public beast::unit_test::Suite Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); - mptAlice.create( - {.ownerCount = 1, - .mutableFlags = tmfMPTCanEnableCanTransfer | tmfMPTCanMutateTransferFee}); + mptAlice.create({.ownerCount = 1}); mptAlice.authorize({.account = bob}); mptAlice.authorize({.account = carol}); @@ -3930,12 +3870,12 @@ class MPToken_test : public beast::unit_test::Suite // MPTCanTransfer at the same time mptAlice.set( {.account = alice, - .mutableFlags = tmfMPTSetCanTransfer, + .flags = tfMPTSetCanTransfer, .transferFee = 100, .err = tecNO_PERMISSION}); // Alice sets MPTCanTransfer - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanTransfer}); + mptAlice.set({.account = alice, .flags = tfMPTSetCanTransfer}); // Can set transfer fee now BEAST_EXPECT(!mptAlice.isTransferFeePresent()); @@ -3958,16 +3898,13 @@ class MPToken_test : public beast::unit_test::Suite } } - // Can set transfer fee to zero when tmfMPTCanMutateTransferFee is set. + // Can set transfer fee to zero when transfer fee is mutable (i.e. + // tifMPTTransferFee is not set). { Env env{*this, features}; MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); - mptAlice.create( - {.transferFee = 100, - .ownerCount = 1, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanMutateTransferFee}); + mptAlice.create({.transferFee = 100, .ownerCount = 1, .flags = tfMPTCanTransfer}); BEAST_EXPECT(mptAlice.checkTransferFee(100)); @@ -3989,8 +3926,7 @@ class MPToken_test : public beast::unit_test::Suite MPTTester mptAlice(env, alice, {.holders = {bob}}); - mptAlice.create( - {.ownerCount = 1, .holderCount = 0, .mutableFlags = tmfMPTCanEnableCanClawback}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); // Bob creates an MPToken mptAlice.authorize({.account = bob}); @@ -4002,12 +3938,98 @@ class MPToken_test : public beast::unit_test::Suite mptAlice.claw(alice, bob, 1, tecNO_PERMISSION); // Enable MPTCanClawback - mptAlice.set({.account = alice, .mutableFlags = tmfMPTSetCanClawback}); + mptAlice.set({.account = alice, .flags = tfMPTSetCanClawback}); // Can clawback now mptAlice.claw(alice, bob, 1); } + void + testMutateImmutableFlags(FeatureBitset features) + { + testcase("Mutate MPT ImmutableFlags via MPTokenIssuanceSet"); + + using namespace test::jtx; + Account const alice{"alice"}; + Account const bob{"bob"}; + + // ImmutableFlags requires featureDynamicMPT. + { + Env env(*this, features - featureDynamicMPT); + MPTTester mptAlice(env, alice); + mptAlice.create({.ownerCount = 1}); + + mptAlice.set( + {.account = alice, .immutableFlags = tifMPTCanClawback, .err = temDISABLED}); + } + + // ImmutableFlags containing tifMPTCanHoldConfidentialBalance requires + // featureConfidentialTransfer. + { + Env env(*this, features - featureConfidentialTransfer); + MPTTester mptAlice(env, alice); + mptAlice.create({.ownerCount = 1}); + + mptAlice.set( + {.account = alice, + .immutableFlags = tifMPTCanHoldConfidentialBalance, + .err = temDISABLED}); + } + + // ImmutableFlags of 0, or containing unknown bits, is rejected. + { + Env env(*this, features); + MPTTester mptAlice(env, alice); + mptAlice.create({.ownerCount = 1}); + + mptAlice.set({.account = alice, .immutableFlags = 0, .err = temINVALID_FLAG}); + mptAlice.set({.account = alice, .immutableFlags = 1, .err = temINVALID_FLAG}); + } + + // Holder is not allowed alongside ImmutableFlags, and ImmutableFlags + // can not be combined with Lock/Unlock in the same transaction. + { + Env env(*this, features); + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create({.ownerCount = 1, .holderCount = 0}); + + mptAlice.set( + {.account = alice, + .holder = bob, + .immutableFlags = tifMPTCanClawback, + .err = temMALFORMED}); + + mptAlice.set( + {.account = alice, + .flags = tfMPTLock, + .immutableFlags = tifMPTCanClawback, + .err = temMALFORMED}); + } + + // Setting ImmutableFlags persists to the ledger, permanently blocks + // enabling the corresponding capability, and merges (rather than + // overwrites) across multiple transactions. + { + Env env(*this, features); + MPTTester mptAlice(env, alice); + mptAlice.create({.ownerCount = 1}); + + mptAlice.set({.account = alice, .immutableFlags = tifMPTCanClawback}); + BEAST_EXPECT(mptAlice.checkImmutableFlags(tifMPTCanClawback)); + + // The capability can no longer be enabled. + mptAlice.set({.account = alice, .flags = tfMPTSetCanClawback, .err = tecNO_PERMISSION}); + + // A distinct bit merges with the first rather than overwriting it. + mptAlice.set({.account = alice, .immutableFlags = tifMPTCanTrade}); + BEAST_EXPECT(mptAlice.checkImmutableFlags(tifMPTCanClawback | tifMPTCanTrade)); + + // Setting the same bit again is a harmless no-op. + mptAlice.set({.account = alice, .immutableFlags = tifMPTCanClawback}); + BEAST_EXPECT(mptAlice.checkImmutableFlags(tifMPTCanClawback | tifMPTCanTrade)); + } + } + void testMultiSendMaximumAmount(FeatureBitset features) { @@ -4398,14 +4420,14 @@ class MPToken_test : public beast::unit_test::Suite .holders = {alice, carol}, .pay = 100, .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanEnableCanTrade}); + .immutableFlags = tifMPTCanTrade}); MPTTester const eth( {.env = env, .issuer = gw, .holders = {alice, carol}, .pay = 100, .flags = tfMPTCanTrade, - .mutableFlags = tmfMPTCanEnableCanTrade}); + .immutableFlags = tifMPTCanTrade}); // Can't create env(offer(gw, eth(10), btc(10)), Ter(tecNO_PERMISSION)); @@ -4641,30 +4663,25 @@ class MPToken_test : public beast::unit_test::Suite .issuer = gw, .holders = {alice, carol, bob}, .pay = 1'000, - .flags = tfMPTCanLock | kMptDexFlags, - .mutableFlags = tmfMPTCanEnableRequireAuth | tmfMPTCanEnableCanTrade | - tmfMPTCanEnableCanTransfer}); + .flags = tfMPTCanLock | kMptDexFlags}); MPTTester eth( {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 1'000, - .flags = tfMPTCanLock | kMptDexFlags, - .mutableFlags = tmfMPTCanEnableCanTransfer}); + .flags = tfMPTCanLock | kMptDexFlags}); MPTTester const usd( {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 1'000, - .flags = kMptDexFlags | tfMPTCanLock, - .mutableFlags = tmfMPTCanEnableCanTransfer}); + .flags = kMptDexFlags | tfMPTCanLock}); MPTTester const cad( {.env = env, .issuer = gw, .holders = {alice, carol, bob}, .pay = 1'000, - .flags = kMptDexFlags | tfMPTCanLock, - .mutableFlags = tmfMPTCanEnableCanTransfer}); + .flags = kMptDexFlags | tfMPTCanLock}); env(offer(bob, eth(1'000), btc(1'000)), Txflags(tfPassive)); env.close(); @@ -4694,7 +4711,7 @@ class MPToken_test : public beast::unit_test::Suite env(pay(gw, ed, eth(100))); env(pay(gw, ed, btc(100))); env.close(); - btc.set({.mutableFlags = tmfMPTSetRequireAuth}); + btc.set({.flags = tfMPTSetRequireAuth}); // authorize bob to enable the offers trading btc.authorize({.account = gw, .holder = bob}); env.close(); @@ -4932,8 +4949,7 @@ class MPToken_test : public beast::unit_test::Suite .issuer = gw, .holders = {alice, carol, bob}, .pay = 1'000, - .flags = tfMPTCanTransfer, - .mutableFlags = tmfMPTCanEnableCanTrade}); + .flags = tfMPTCanTransfer}); MPTTester const eth( {.env = env, .issuer = gw, @@ -4952,7 +4968,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); // Enable MPTCanTrade so BTC can be crossed through offers. - btc.set({.mutableFlags = tmfMPTSetCanTrade}); + btc.set({.flags = tfMPTSetCanTrade}); env(offer(bob, XRP(1), btc(1))); env(offer(bob, btc(1), eth(1))); env(offer(bob, eth(1), usd(1))); @@ -6753,11 +6769,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); MPTTester mpt( - {.env = env, - .issuer = gw, - .holders = {alice, carol}, - .flags = tfMPTCanTrade, - .mutableFlags = tmfMPTCanEnableCanTransfer}); + {.env = env, .issuer = gw, .holders = {alice, carol}, .flags = tfMPTCanTrade}); // src is issuer uint256 checkId{keylet::check(gw, env.seq(gw)).key}; @@ -6793,7 +6805,7 @@ class MPToken_test : public beast::unit_test::Suite env.close(); // can create now - mpt.set({.account = gw, .mutableFlags = tmfMPTSetCanTransfer}); + mpt.set({.account = gw, .flags = tfMPTSetCanTransfer}); checkId = keylet::check(alice, env.seq(alice)).key; env(check::create(alice, carol, mpt(100))); env.close(); @@ -7223,37 +7235,26 @@ class MPToken_test : public beast::unit_test::Suite auto const txfee = Fee(drops(increment)); auto const badMPT = MPT(gw, 1'000); - auto const makeMPT = [&](std::uint32_t const flags, - Holders holders = {}, - std::uint64_t const pay = 0, - std::optional const mutableFlags = - std::nullopt) { - return MPTTester( - {.env = env, - .issuer = gw, - .holders = holders, - .pay = pay ? std::optional{pay} : std::nullopt, - .flags = flags, - .mutableFlags = mutableFlags}); - }; + auto const makeMPT = + [&](std::uint32_t const flags, Holders holders = {}, std::uint64_t const pay = 0) { + return MPTTester( + {.env = env, + .issuer = gw, + .holders = holders, + .pay = pay ? std::optional{pay} : std::nullopt, + .flags = flags}); + }; auto const makeDexMPT = [&](Holders holders = {}, std::uint64_t const pay = 0) { - return makeMPT( - tfMPTCanLock | kMptDexFlags, - holders, - pay, - tmfMPTCanEnableRequireAuth | tmfMPTCanEnableCanTransfer | - tmfMPTCanEnableCanTrade); + return makeMPT(tfMPTCanLock | kMptDexFlags, holders, pay); }; auto const makeNoTransferMPT = [&](Holders holders = {}, std::uint64_t const pay = 0) { - return makeMPT( - tfMPTCanLock | tfMPTCanTrade, holders, pay, tmfMPTCanEnableCanTransfer); + return makeMPT(tfMPTCanLock | tfMPTCanTrade, holders, pay); }; auto const makeNoTradeMPT = [&](Holders holders = {}, std::uint64_t const pay = 0) { - return makeMPT( - tfMPTCanLock | tfMPTCanTransfer, holders, pay, tmfMPTCanEnableCanTrade); + return makeMPT(tfMPTCanLock | tfMPTCanTransfer, holders, pay); }; // AMMCreate @@ -7299,7 +7300,7 @@ class MPToken_test : public beast::unit_test::Suite // MPTRequireAuth is set // alice is not authorized usd.set({.flags = tfMPTUnlock}); - usd.set({.mutableFlags = tmfMPTSetRequireAuth}); + usd.set({.flags = tfMPTSetRequireAuth}); createFail(usd, alice, tecNO_AUTH); // issuer can create createDeleteAMM(usd, gw); @@ -7316,7 +7317,7 @@ class MPToken_test : public beast::unit_test::Suite createFail(usd2, alice, tecNO_AUTH); // issuer can create createDeleteAMM(usd2, gw); - usd2.set({.mutableFlags = tmfMPTSetCanTransfer}); + usd2.set({.flags = tfMPTSetCanTransfer}); // alice can create createDeleteAMM(usd2, alice); } @@ -7328,7 +7329,7 @@ class MPToken_test : public beast::unit_test::Suite // alice and issuer can't create createFail(usd3, alice, tecNO_PERMISSION); createFail(usd3, gw, tecNO_PERMISSION); - usd3.set({.mutableFlags = tmfMPTSetCanTrade}); + usd3.set({.flags = tfMPTSetCanTrade}); // alice can create createDeleteAMM(usd3, alice); } @@ -7383,7 +7384,7 @@ class MPToken_test : public beast::unit_test::Suite // MPTRequireAuth is set // carol is not authorized by the issuer - usd.set({.mutableFlags = tmfMPTSetRequireAuth}); + usd.set({.flags = tfMPTSetRequireAuth}); env.close(); amm.deposit( {.account = carol, @@ -7429,7 +7430,7 @@ class MPToken_test : public beast::unit_test::Suite .err = Ter(tecNO_AUTH)}); // issuer can deposit amm2.deposit({.account = gw, .tokens = 1'000}); - usd2.set({.mutableFlags = tmfMPTSetCanTransfer}); + usd2.set({.flags = tfMPTSetCanTransfer}); // carol can deposit amm2.deposit({.account = carol, .tokens = 1'000}); } @@ -7499,7 +7500,7 @@ class MPToken_test : public beast::unit_test::Suite usd.set({.flags = tfMPTUnlock}); // MPTRequireAuth is set - usd.set({.mutableFlags = tmfMPTSetRequireAuth}); + usd.set({.flags = tfMPTSetRequireAuth}); usd.authorize({.account = gw, .holder = carol, .flags = tfMPTUnauthorize}); // carol can't withdraw amm.withdraw( @@ -7543,7 +7544,7 @@ class MPToken_test : public beast::unit_test::Suite usd2.authorize({.account = bob, .flags = tfMPTUnauthorize}); // Can redeem env(pay(carol, gw, usd2(1))); - usd2.set({.mutableFlags = tmfMPTSetCanTransfer}); + usd2.set({.flags = tfMPTSetCanTransfer}); // carol can withdraw amm2.withdraw({.account = carol, .asset1Out = usd2(1), .asset2Out = eur(1)}); } @@ -7746,6 +7747,7 @@ class MPToken_test : public beast::unit_test::Suite testMutateCanTransfer(all); testMutateCanTransfer(all - featureMPTokensV2); testMutateCanClawback(all); + testMutateImmutableFlags(all); // Test offer crossing testOfferCrossing(all); diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 617820c89c2..62cf25f4955 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -1602,8 +1602,7 @@ class Vault_test : public beast::unit_test::Suite mptt.create( {.flags = tfMPTCanTransfer | tfMPTCanLock | (args.enableClawback ? tfMPTCanClawback : kNone) | - (args.requireAuth ? tfMPTRequireAuth : kNone), - .mutableFlags = tmfMPTCanEnableCanTransfer}); + (args.requireAuth ? tfMPTRequireAuth : kNone)}); PrettyAsset const asset = mptt.issuanceID(); mptt.authorize({.account = owner}); mptt.authorize({.account = depositor}); @@ -2206,9 +2205,7 @@ class Vault_test : public beast::unit_test::Suite Vault const vault{env}; MPTTester mptt{env, issuer, kMptInitNoFund}; - mptt.create( - {.flags = tfMPTCanTransfer | tfMPTCanLock, - .mutableFlags = tmfMPTCanEnableCanTrade}); + mptt.create({.flags = tfMPTCanTransfer | tfMPTCanLock}); PrettyAsset const asset = mptt.issuanceID(); mptt.authorize({.account = owner}); mptt.authorize({.account = alice}); @@ -2252,7 +2249,7 @@ class Vault_test : public beast::unit_test::Suite env.close(); // Enable CanTrade on the underlying. - mptt.set({.mutableFlags = tmfMPTSetCanTrade}); + mptt.set({.flags = tfMPTSetCanTrade}); env.close(); env(offer(alice, XRP(1), asset(10))); diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index dddfc88c7fd..ae8449fb8f0 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -101,12 +101,12 @@ struct MPTSetFlagMapping }; static constexpr std::array mptSetFlagMappings = {{ - {.setFlag = tmfMPTSetCanLock, .ledgerFlag = lsfMPTCanLock}, - {.setFlag = tmfMPTSetRequireAuth, .ledgerFlag = lsfMPTRequireAuth}, - {.setFlag = tmfMPTSetCanEscrow, .ledgerFlag = lsfMPTCanEscrow}, - {.setFlag = tmfMPTSetCanClawback, .ledgerFlag = lsfMPTCanClawback}, - {.setFlag = tmfMPTSetCanTrade, .ledgerFlag = lsfMPTCanTrade}, - {.setFlag = tmfMPTSetCanTransfer, .ledgerFlag = lsfMPTCanTransfer}, + {.setFlag = tfMPTSetCanLock, .ledgerFlag = lsfMPTCanLock}, + {.setFlag = tfMPTSetRequireAuth, .ledgerFlag = lsfMPTRequireAuth}, + {.setFlag = tfMPTSetCanEscrow, .ledgerFlag = lsfMPTCanEscrow}, + {.setFlag = tfMPTSetCanClawback, .ledgerFlag = lsfMPTCanClawback}, + {.setFlag = tfMPTSetCanTrade, .ledgerFlag = lsfMPTCanTrade}, + {.setFlag = tfMPTSetCanTransfer, .ledgerFlag = lsfMPTCanTransfer}, }}; void @@ -195,7 +195,7 @@ makeMPTCreate(MPTInitDef const& arg) .transferFee = arg.transferFee, .pay = {{arg.holders, *arg.pay}}, .flags = arg.flags, - .mutableFlags = arg.mutableFlags, + .immutableFlags = arg.immutableFlags, .authHolder = arg.authHolder}; } return { @@ -203,7 +203,7 @@ makeMPTCreate(MPTInitDef const& arg) .transferFee = arg.transferFee, .authorize = arg.holders, .flags = arg.flags, - .mutableFlags = arg.mutableFlags, + .immutableFlags = arg.immutableFlags, .authHolder = arg.authHolder}; } @@ -245,8 +245,8 @@ MPTTester::createJV(MPTCreate const& arg) jv[sfMaximumAmount] = std::to_string(*arg.maxAmt); if (arg.domainID) jv[sfDomainID] = to_string(*arg.domainID); - if (arg.mutableFlags) - jv[sfMutableFlags] = *arg.mutableFlags; + if (arg.immutableFlags) + jv[sfImmutableFlags] = *arg.immutableFlags; jv[sfTransactionType] = jss::MPTokenIssuanceCreate; return jv; @@ -264,7 +264,7 @@ MPTTester::create(MPTCreate const& arg) .assetScale = arg.assetScale, .transferFee = arg.transferFee, .metadata = arg.metadata, - .mutableFlags = arg.mutableFlags, + .immutableFlags = arg.immutableFlags, .domainID = arg.domainID}); if (!isTesSuccess(submit(arg, jv))) { @@ -463,8 +463,8 @@ MPTTester::setJV(MPTSet const& arg) jv[sfDelegate] = arg.delegate->human(); if (arg.domainID) jv[sfDomainID] = to_string(*arg.domainID); - if (arg.mutableFlags) - jv[sfMutableFlags] = *arg.mutableFlags; + if (arg.immutableFlags) + jv[sfImmutableFlags] = *arg.immutableFlags; if (arg.transferFee) jv[sfTransferFee] = *arg.transferFee; if (arg.metadata) @@ -487,95 +487,88 @@ MPTTester::set(MPTSet const& arg) {.account = arg.account ? arg.account : issuer_, .holder = arg.holder, .id = arg.id ? arg.id : id_, - .mutableFlags = arg.mutableFlags, + .immutableFlags = arg.immutableFlags, .transferFee = arg.transferFee, .metadata = arg.metadata, .delegate = arg.delegate, .domainID = arg.domainID, .issuerPubKey = arg.issuerPubKey, .auditorPubKey = arg.auditorPubKey}); - if (submit(arg, jv) == tesSUCCESS && ((arg.flags.value_or(0) != 0u) || arg.mutableFlags)) + if (submit(arg, jv) == tesSUCCESS && arg.flags.value_or(0) != 0u) { - if (((arg.flags.value_or(0) != 0u) || arg.mutableFlags)) - { - auto require = [&](std::optional const& holder, bool unchanged) { - auto flags = getFlags(holder); - if (!unchanged) + auto require = [&](std::optional const& holder, bool unchanged) { + auto flags = getFlags(holder); + if (!unchanged) + { + if (arg.flags) { - if (arg.flags) + if (*arg.flags & tfMPTLock) { - if (*arg.flags & tfMPTLock) - { - flags |= lsfMPTLocked; - } - else if (*arg.flags & tfMPTUnlock) - { - flags &= ~lsfMPTLocked; - } + flags |= lsfMPTLocked; + } + else if (*arg.flags & tfMPTUnlock) + { + flags &= ~lsfMPTLocked; } - if (arg.mutableFlags) + for (auto const& [setFlag, ledgerFlag] : mptSetFlagMappings) { - for (auto const& [setFlag, ledgerFlag] : mptSetFlagMappings) + if ((*arg.flags & setFlag) != 0u) { - if ((*arg.mutableFlags & setFlag) != 0u) - { - flags |= ledgerFlag; - } + flags |= ledgerFlag; } - - if (*arg.mutableFlags & tmfMPTSetCanHoldConfidentialBalance) - flags |= tfMPTCanHoldConfidentialBalance; } - } - env_.require(MptFlags(*this, flags, holder)); - }; - if (arg.account) - require(std::nullopt, arg.holder.has_value()); - if (auto const account = (arg.holder ? std::get_if(&(*arg.holder)) : nullptr)) - require(*account, false); - - if (arg.issuerPubKey) - { - env_.require(RequireAny([&]() -> bool { - return forObject([&](SLEP const& sle) -> bool { - if (sle) - { - auto const issuerPubKey = getPubKey(issuer_); - if (!issuerPubKey) - { - Throw( - "MPTTester::set: issuer's pubkey is not set"); - } - return strHex((*sle)[sfIssuerEncryptionKey]) == strHex(*issuerPubKey); - } - return false; - }); - })); + if (*arg.flags & tfMPTSetCanHoldConfidentialBalance) + flags |= tfMPTCanHoldConfidentialBalance; + } } - if (arg.auditorPubKey) - { - env_.require(RequireAny([&]() -> bool { - return forObject([&](SLEP const& sle) -> bool { - if (sle) + env_.require(MptFlags(*this, flags, holder)); + }; + if (arg.account) + require(std::nullopt, arg.holder.has_value()); + if (auto const account = (arg.holder ? std::get_if(&(*arg.holder)) : nullptr)) + require(*account, false); + + if (arg.issuerPubKey) + { + env_.require(RequireAny([&]() -> bool { + return forObject([&](SLEP const& sle) -> bool { + if (sle) + { + auto const issuerPubKey = getPubKey(issuer_); + if (!issuerPubKey) { - if (!auditor_.has_value()) - Throw("MPTTester::set: auditor is not set"); + Throw("MPTTester::set: issuer's pubkey is not set"); + } - auto const auditorPubKey = getPubKey(*auditor_); - if (!auditorPubKey) - { - Throw( - "MPTTester::set: auditor's pubkey is not set"); - } + return strHex((*sle)[sfIssuerEncryptionKey]) == strHex(*issuerPubKey); + } + return false; + }); + })); + } + if (arg.auditorPubKey) + { + env_.require(RequireAny([&]() -> bool { + return forObject([&](SLEP const& sle) -> bool { + if (sle) + { + if (!auditor_.has_value()) + Throw("MPTTester::set: auditor is not set"); - return strHex((*sle)[sfAuditorEncryptionKey]) == strHex(*auditorPubKey); + auto const auditorPubKey = getPubKey(*auditor_); + if (!auditorPubKey) + { + Throw( + "MPTTester::set: auditor's pubkey is not set"); } - return false; - }); - })); - } + + return strHex((*sle)[sfAuditorEncryptionKey]) == strHex(*auditorPubKey); + } + return false; + }); + })); } } } @@ -664,6 +657,15 @@ MPTTester::isTransferFeePresent() const return forObject([&](SLEP const& sle) -> bool { return sle->isFieldPresent(sfTransferFee); }); } +[[nodiscard]] bool +MPTTester::checkImmutableFlags(std::uint32_t expectedFlags) const +{ + // sfImmutableFlags is soeDEFAULT, defaulting to 0 if not present. + return forObject([&](SLEP const& sle) -> bool { + return sle->getFieldU32(sfImmutableFlags) == expectedFlags; + }); +} + void MPTTester::pay( Account const& src, diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index c6532ab14a0..35ab7264bdc 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -147,7 +147,7 @@ struct MPTCreate // if empty vector then pay to either authorize or all holders. std::optional, std::uint64_t>> pay = std::nullopt; std::optional flags = {0}; - std::optional mutableFlags = std::nullopt; + std::optional immutableFlags = std::nullopt; bool authHolder = false; std::optional domainID = std::nullopt; std::optional err = std::nullopt; @@ -183,7 +183,7 @@ struct MPTInitDef std::uint16_t transferFee = 0; std::optional pay = std::nullopt; std::uint32_t flags = kMptDexFlags; - std::optional mutableFlags = std::nullopt; + std::optional immutableFlags = std::nullopt; bool authHolder = false; bool fund = false; bool close = true; @@ -229,7 +229,7 @@ struct MPTSet std::optional ownerCount = std::nullopt; std::optional holderCount = std::nullopt; std::optional flags = std::nullopt; - std::optional mutableFlags = std::nullopt; + std::optional immutableFlags = std::nullopt; std::optional transferFee = std::nullopt; std::optional metadata = std::nullopt; std::optional delegate = std::nullopt; @@ -609,6 +609,9 @@ class MPTTester [[nodiscard]] bool isTransferFeePresent() const; + [[nodiscard]] bool + checkImmutableFlags(std::uint32_t expectedFlags) const; + [[nodiscard]] Account const& issuer() const { diff --git a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp index 8dc5960ee0d..974d0e81d75 100644 --- a/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/ledger_entries/MPTokenIssuanceTests.cpp @@ -32,7 +32,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); auto const domainIDValue = canonical_UINT256(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); auto const referenceHoldingValue = canonical_UINT256(); auto const issuerEncryptionKeyValue = canonical_VL(); auto const auditorEncryptionKeyValue = canonical_VL(); @@ -53,7 +53,7 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) builder.setLockedAmount(lockedAmountValue); builder.setMPTokenMetadata(mPTokenMetadataValue); builder.setDomainID(domainIDValue); - builder.setMutableFlags(mutableFlagsValue); + builder.setImmutableFlags(immutableFlagsValue); builder.setReferenceHolding(referenceHoldingValue); builder.setIssuerEncryptionKey(issuerEncryptionKeyValue); builder.setAuditorEncryptionKey(auditorEncryptionKeyValue); @@ -153,11 +153,11 @@ TEST(MPTokenIssuanceTests, BuilderSettersRoundTrip) } { - auto const& expected = mutableFlagsValue; - auto const actualOpt = entry.getMutableFlags(); + auto const& expected = immutableFlagsValue; + auto const actualOpt = entry.getImmutableFlags(); ASSERT_TRUE(actualOpt.has_value()); - expectEqualField(expected, *actualOpt, "sfMutableFlags"); - EXPECT_TRUE(entry.hasMutableFlags()); + expectEqualField(expected, *actualOpt, "sfImmutableFlags"); + EXPECT_TRUE(entry.hasImmutableFlags()); } { @@ -217,7 +217,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) auto const previousTxnIDValue = canonical_UINT256(); auto const previousTxnLgrSeqValue = canonical_UINT32(); auto const domainIDValue = canonical_UINT256(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); auto const referenceHoldingValue = canonical_UINT256(); auto const issuerEncryptionKeyValue = canonical_VL(); auto const auditorEncryptionKeyValue = canonical_VL(); @@ -237,7 +237,7 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) sle->at(sfPreviousTxnID) = previousTxnIDValue; sle->at(sfPreviousTxnLgrSeq) = previousTxnLgrSeqValue; sle->at(sfDomainID) = domainIDValue; - sle->at(sfMutableFlags) = mutableFlagsValue; + sle->at(sfImmutableFlags) = immutableFlagsValue; sle->at(sfReferenceHolding) = referenceHoldingValue; sle->at(sfIssuerEncryptionKey) = issuerEncryptionKeyValue; sle->at(sfAuditorEncryptionKey) = auditorEncryptionKeyValue; @@ -391,16 +391,16 @@ TEST(MPTokenIssuanceTests, BuilderFromSleRoundTrip) } { - auto const& expected = mutableFlagsValue; + auto const& expected = immutableFlagsValue; - auto const fromSleOpt = entryFromSle.getMutableFlags(); - auto const fromBuilderOpt = entryFromBuilder.getMutableFlags(); + auto const fromSleOpt = entryFromSle.getImmutableFlags(); + auto const fromBuilderOpt = entryFromBuilder.getImmutableFlags(); ASSERT_TRUE(fromSleOpt.has_value()); ASSERT_TRUE(fromBuilderOpt.has_value()); - expectEqualField(expected, *fromSleOpt, "sfMutableFlags"); - expectEqualField(expected, *fromBuilderOpt, "sfMutableFlags"); + expectEqualField(expected, *fromSleOpt, "sfImmutableFlags"); + expectEqualField(expected, *fromBuilderOpt, "sfImmutableFlags"); } { @@ -531,8 +531,8 @@ TEST(MPTokenIssuanceTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(entry.getMPTokenMetadata().has_value()); EXPECT_FALSE(entry.hasDomainID()); EXPECT_FALSE(entry.getDomainID().has_value()); - EXPECT_FALSE(entry.hasMutableFlags()); - EXPECT_FALSE(entry.getMutableFlags().has_value()); + EXPECT_FALSE(entry.hasImmutableFlags()); + EXPECT_FALSE(entry.getImmutableFlags().has_value()); EXPECT_FALSE(entry.hasReferenceHolding()); EXPECT_FALSE(entry.getReferenceHolding().has_value()); EXPECT_FALSE(entry.hasIssuerEncryptionKey()); diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp index f7151fc749b..82281889503 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceCreateTests.cpp @@ -34,7 +34,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip) auto const maximumAmountValue = canonical_UINT64(); auto const mPTokenMetadataValue = canonical_VL(); auto const domainIDValue = canonical_UINT256(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); MPTokenIssuanceCreateBuilder builder{ accountValue, @@ -48,7 +48,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip) builder.setMaximumAmount(maximumAmountValue); builder.setMPTokenMetadata(mPTokenMetadataValue); builder.setDomainID(domainIDValue); - builder.setMutableFlags(mutableFlagsValue); + builder.setImmutableFlags(immutableFlagsValue); auto tx = builder.build(publicKey, secretKey); @@ -107,11 +107,11 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderSettersRoundTrip) } { - auto const& expected = mutableFlagsValue; - auto const actualOpt = tx.getMutableFlags(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; - expectEqualField(expected, *actualOpt, "sfMutableFlags"); - EXPECT_TRUE(tx.hasMutableFlags()); + auto const& expected = immutableFlagsValue; + auto const actualOpt = tx.getImmutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfImmutableFlags"); + EXPECT_TRUE(tx.hasImmutableFlags()); } } @@ -135,7 +135,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip) auto const maximumAmountValue = canonical_UINT64(); auto const mPTokenMetadataValue = canonical_VL(); auto const domainIDValue = canonical_UINT256(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); // Build an initial transaction MPTokenIssuanceCreateBuilder initialBuilder{ @@ -149,7 +149,7 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip) initialBuilder.setMaximumAmount(maximumAmountValue); initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); initialBuilder.setDomainID(domainIDValue); - initialBuilder.setMutableFlags(mutableFlagsValue); + initialBuilder.setImmutableFlags(immutableFlagsValue); auto initialTx = initialBuilder.build(publicKey, secretKey); @@ -204,10 +204,10 @@ TEST(TransactionsMPTokenIssuanceCreateTests, BuilderFromStTxRoundTrip) } { - auto const& expected = mutableFlagsValue; - auto const actualOpt = rebuiltTx.getMutableFlags(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; - expectEqualField(expected, *actualOpt, "sfMutableFlags"); + auto const& expected = immutableFlagsValue; + auto const actualOpt = rebuiltTx.getImmutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfImmutableFlags"); } } @@ -275,8 +275,8 @@ TEST(TransactionsMPTokenIssuanceCreateTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); EXPECT_FALSE(tx.hasDomainID()); EXPECT_FALSE(tx.getDomainID().has_value()); - EXPECT_FALSE(tx.hasMutableFlags()); - EXPECT_FALSE(tx.getMutableFlags().has_value()); + EXPECT_FALSE(tx.hasImmutableFlags()); + EXPECT_FALSE(tx.getImmutableFlags().has_value()); } } diff --git a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp index e7b34590b2f..af696ce47b4 100644 --- a/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp +++ b/src/tests/libxrpl/protocol_autogen/transactions/MPTokenIssuanceSetTests.cpp @@ -34,7 +34,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) auto const domainIDValue = canonical_UINT256(); auto const mPTokenMetadataValue = canonical_VL(); auto const transferFeeValue = canonical_UINT16(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); auto const issuerEncryptionKeyValue = canonical_VL(); auto const auditorEncryptionKeyValue = canonical_VL(); @@ -50,7 +50,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) builder.setDomainID(domainIDValue); builder.setMPTokenMetadata(mPTokenMetadataValue); builder.setTransferFee(transferFeeValue); - builder.setMutableFlags(mutableFlagsValue); + builder.setImmutableFlags(immutableFlagsValue); builder.setIssuerEncryptionKey(issuerEncryptionKeyValue); builder.setAuditorEncryptionKey(auditorEncryptionKeyValue); @@ -109,11 +109,11 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderSettersRoundTrip) } { - auto const& expected = mutableFlagsValue; - auto const actualOpt = tx.getMutableFlags(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; - expectEqualField(expected, *actualOpt, "sfMutableFlags"); - EXPECT_TRUE(tx.hasMutableFlags()); + auto const& expected = immutableFlagsValue; + auto const actualOpt = tx.getImmutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfImmutableFlags"); + EXPECT_TRUE(tx.hasImmutableFlags()); } { @@ -153,7 +153,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) auto const domainIDValue = canonical_UINT256(); auto const mPTokenMetadataValue = canonical_VL(); auto const transferFeeValue = canonical_UINT16(); - auto const mutableFlagsValue = canonical_UINT32(); + auto const immutableFlagsValue = canonical_UINT32(); auto const issuerEncryptionKeyValue = canonical_VL(); auto const auditorEncryptionKeyValue = canonical_VL(); @@ -169,7 +169,7 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) initialBuilder.setDomainID(domainIDValue); initialBuilder.setMPTokenMetadata(mPTokenMetadataValue); initialBuilder.setTransferFee(transferFeeValue); - initialBuilder.setMutableFlags(mutableFlagsValue); + initialBuilder.setImmutableFlags(immutableFlagsValue); initialBuilder.setIssuerEncryptionKey(issuerEncryptionKeyValue); initialBuilder.setAuditorEncryptionKey(auditorEncryptionKeyValue); @@ -225,10 +225,10 @@ TEST(TransactionsMPTokenIssuanceSetTests, BuilderFromStTxRoundTrip) } { - auto const& expected = mutableFlagsValue; - auto const actualOpt = rebuiltTx.getMutableFlags(); - ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfMutableFlags should be present"; - expectEqualField(expected, *actualOpt, "sfMutableFlags"); + auto const& expected = immutableFlagsValue; + auto const actualOpt = rebuiltTx.getImmutableFlags(); + ASSERT_TRUE(actualOpt.has_value()) << "Optional field sfImmutableFlags should be present"; + expectEqualField(expected, *actualOpt, "sfImmutableFlags"); } { @@ -310,8 +310,8 @@ TEST(TransactionsMPTokenIssuanceSetTests, OptionalFieldsReturnNullopt) EXPECT_FALSE(tx.getMPTokenMetadata().has_value()); EXPECT_FALSE(tx.hasTransferFee()); EXPECT_FALSE(tx.getTransferFee().has_value()); - EXPECT_FALSE(tx.hasMutableFlags()); - EXPECT_FALSE(tx.getMutableFlags().has_value()); + EXPECT_FALSE(tx.hasImmutableFlags()); + EXPECT_FALSE(tx.getImmutableFlags().has_value()); EXPECT_FALSE(tx.hasIssuerEncryptionKey()); EXPECT_FALSE(tx.getIssuerEncryptionKey().has_value()); EXPECT_FALSE(tx.hasAuditorEncryptionKey());