diff --git a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp index 339a9ba894..8646d69302 100644 --- a/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp +++ b/src/libxrpl/tx/transactors/token/MPTokenIssuanceSet.cpp @@ -86,7 +86,10 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) 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; @@ -134,6 +137,8 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) if (metadata && metadata->length() > kMaxMpTokenMetadataLength) return temMALFORMED; + // 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))) diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 376eae06ad..44298e8d43 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -3963,6 +3963,19 @@ class MPToken_test : public beast::unit_test::Suite {.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);