Skip to content

Commit 0a152e0

Browse files
committed
Disallow mutation when COA not empty
1 parent b94c95b commit 0a152e0

File tree

3 files changed

+84
-6
lines changed

3 files changed

+84
-6
lines changed

src/test/app/ConfidentialTransfer_test.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,68 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
23972397
// todo: test zkp verification failure
23982398
}
23992399

2400+
void
2401+
testBlockingMutation(FeatureBitset features)
2402+
{
2403+
testcase("disallow mutation when there's confidential OA");
2404+
using namespace test::jtx;
2405+
2406+
Env env{*this, features};
2407+
Account const alice("alice");
2408+
Account const bob("bob");
2409+
MPTTester mptAlice(env, alice, {.holders = {bob}});
2410+
2411+
mptAlice.create(
2412+
{.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanClawback,
2413+
.mutableFlags = tmfMPTCanMutateMetadata |
2414+
tmfMPTCanMutateTransferFee | tmfMPTCanMutateCanLock});
2415+
2416+
mptAlice.authorize({.account = bob});
2417+
mptAlice.pay(alice, bob, 1000);
2418+
mptAlice.generateKeyPair(alice);
2419+
mptAlice.generateKeyPair(bob);
2420+
2421+
mptAlice.set({.account = alice, .pubKey = mptAlice.getPubKey(alice)});
2422+
2423+
// muatation is allowed because confidential outstanding amount is 0.
2424+
mptAlice.set({.account = alice, .metadata = "test"});
2425+
2426+
// bob convert 100
2427+
mptAlice.convert({
2428+
.account = bob,
2429+
.amt = 100,
2430+
.proof = "123",
2431+
.holderPubKey = mptAlice.getPubKey(bob),
2432+
});
2433+
BEAST_EXPECT(mptAlice.getIssuanceConfidentialBalance() == 100);
2434+
2435+
// can not mutate metadata
2436+
mptAlice.set(
2437+
{.account = alice, .metadata = "modify", .err = tecNO_PERMISSION});
2438+
2439+
// can not mutate transfer fee
2440+
mptAlice.set(
2441+
{.account = alice, .transferFee = 50, .err = tecNO_PERMISSION});
2442+
2443+
// can not mutate flags
2444+
mptAlice.set(
2445+
{.account = alice,
2446+
.mutableFlags = tmfMPTClearCanLock,
2447+
.err = tecNO_PERMISSION});
2448+
2449+
// clawback all confidential balance and make COA to 0
2450+
mptAlice.confidentialClaw({
2451+
.account = alice,
2452+
.holder = bob,
2453+
.amt = 100,
2454+
.proof = "123",
2455+
});
2456+
BEAST_EXPECT(mptAlice.getIssuanceConfidentialBalance() == 0);
2457+
2458+
// now we can mutate because COA is 0
2459+
mptAlice.set({.account = alice, .metadata = "modify"});
2460+
}
2461+
24002462
void
24012463
testWithFeats(FeatureBitset features)
24022464
{
@@ -2426,6 +2488,8 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
24262488
testConvertBack(features);
24272489
testConvertBackPreflight(features);
24282490
testConvertBackPreclaim(features);
2491+
2492+
testBlockingMutation(features);
24292493
}
24302494

24312495
public:

src/test/app/MPToken_test.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,8 @@ class MPToken_test : public beast::unit_test::suite
590590
.err = temINVALID_FLAG});
591591

592592
if (!features[featureSingleAssetVault] &&
593-
!features[featureDynamicMPT])
593+
!features[featureDynamicMPT] &&
594+
!features[featureConfidentialTransfer])
594595
{
595596
// test invalid flags - nothing is being changed
596597
mptAlice.set(

src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,20 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
240240
}
241241
}
242242

243+
auto const mutableFlags = ctx.tx[~sfMutableFlags];
244+
auto const metadata = ctx.tx[~sfMPTokenMetadata];
245+
auto const transferFee = ctx.tx[~sfTransferFee];
246+
auto const isMutate = mutableFlags || metadata || transferFee;
247+
248+
if (ctx.view.rules().enabled(featureConfidentialTransfer) && isMutate)
249+
{
250+
std::uint64_t const confidentialOA =
251+
(*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0);
252+
253+
if (confidentialOA > 0)
254+
return tecNO_PERMISSION;
255+
}
256+
243257
// sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
244258
// the ledger.
245259
auto const currentMutableFlags =
@@ -249,7 +263,7 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
249263
return currentMutableFlags & mutableFlag;
250264
};
251265

252-
if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
266+
if (mutableFlags)
253267
{
254268
if (std::any_of(
255269
mptMutabilityFlags.begin(),
@@ -261,17 +275,16 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx)
261275
return tecNO_PERMISSION;
262276
}
263277

264-
if (!isMutableFlag(lsmfMPTCanMutateMetadata) &&
265-
ctx.tx.isFieldPresent(sfMPTokenMetadata))
278+
if (!isMutableFlag(lsmfMPTCanMutateMetadata) && metadata)
266279
return tecNO_PERMISSION;
267280

268-
if (auto const fee = ctx.tx[~sfTransferFee])
281+
if (transferFee)
269282
{
270283
// A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
271284
// was previously enabled (at issuance or via a prior mutation). Setting
272285
// it by tmfMPTSetCanTransfer in the current transaction does not meet
273286
// this requirement.
274-
if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
287+
if (transferFee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
275288
return tecNO_PERMISSION;
276289

277290
if (!isMutableFlag(lsmfMPTCanMutateTransferFee))

0 commit comments

Comments
 (0)