Add auth checks for convert (#5937)

This commit is contained in:
Shawn Xie
2025-10-24 11:42:43 -04:00
committed by GitHub
parent e4a8ba51f9
commit 3899e3f36c
5 changed files with 111 additions and 19 deletions

View File

@@ -501,7 +501,8 @@ accountHolds(
// Only if auth check is needed, as it needs to do an additional read // Only if auth check is needed, as it needs to do an additional read
// operation. Note featureSingleAssetVault will affect error codes. // operation. Note featureSingleAssetVault will affect error codes.
if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED &&
view.rules().enabled(featureSingleAssetVault)) (view.rules().enabled(featureSingleAssetVault) ||
view.rules().enabled(featureConfidentialTransfer)))
{ {
if (auto const err = if (auto const err =
requireAuth(view, mptIssue, account, AuthType::StrongAuth); requireAuth(view, mptIssue, account, AuthType::StrongAuth);

View File

@@ -60,9 +60,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
.proof = "123", .proof = "123",
.holderPubKey = mptAlice.getPubKey(bob), .holderPubKey = mptAlice.getPubKey(bob),
}); });
env.close();
mptAlice.printMPT(bob);
mptAlice.convert({ mptAlice.convert({
.account = bob, .account = bob,
@@ -70,16 +67,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
.proof = "123", .proof = "123",
}); });
mptAlice.printMPT(bob);
mptAlice.convert({ mptAlice.convert({
.account = bob, .account = bob,
.amt = 40, .amt = 40,
.proof = "123", .proof = "123",
}); });
env.close();
mptAlice.printMPT(bob);
} }
void void
@@ -437,6 +429,100 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
.err = tecDUPLICATE}); .err = tecDUPLICATE});
} }
// cannot convert if locked
{
Env env{*this, features};
Account const alice("alice");
Account const bob("bob");
MPTTester mptAlice(env, alice, {.holders = {bob}});
mptAlice.create(
{.ownerCount = 1,
.holderCount = 0,
.flags = tfMPTCanTransfer | tfMPTCanLock});
mptAlice.authorize({.account = bob});
env.close();
mptAlice.pay(alice, bob, 100);
env.close();
mptAlice.generateKeyPair(alice);
mptAlice.set(
{.account = alice, .pubKey = mptAlice.getPubKey(alice)});
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
mptAlice.generateKeyPair(bob);
mptAlice.convert(
{.account = bob,
.amt = 10,
.proof = "123",
.holderPubKey = mptAlice.getPubKey(bob),
.err = tecINSUFFICIENT_FUNDS});
mptAlice.set(
{.account = alice, .holder = bob, .flags = tfMPTUnlock});
mptAlice.convert({
.account = bob,
.amt = 10,
.proof = "123",
.holderPubKey = mptAlice.getPubKey(bob),
});
}
// cannot convert if unauth
{
Env env{*this, features};
Account const alice("alice");
Account const bob("bob");
MPTTester mptAlice(env, alice, {.holders = {bob}});
mptAlice.create(
{.ownerCount = 1,
.holderCount = 0,
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTRequireAuth});
mptAlice.authorize({.account = bob});
mptAlice.authorize({.account = alice, .holder = bob});
env.close();
mptAlice.pay(alice, bob, 100);
env.close();
mptAlice.generateKeyPair(alice);
mptAlice.set(
{.account = alice, .pubKey = mptAlice.getPubKey(alice)});
mptAlice.generateKeyPair(bob);
// Unauthorize bob
mptAlice.authorize(
{.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
mptAlice.convert(
{.account = bob,
.amt = 10,
.proof = "123",
.holderPubKey = mptAlice.getPubKey(bob),
.err = tecINSUFFICIENT_FUNDS});
// auth bob
mptAlice.authorize({
.account = alice,
.holder = bob,
});
mptAlice.convert({
.account = bob,
.amt = 10,
.proof = "123",
.holderPubKey = mptAlice.getPubKey(bob),
});
}
// todo: test well formed proof // todo: test well formed proof
} }
@@ -473,15 +559,9 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
.holderPubKey = mptAlice.getPubKey(bob), .holderPubKey = mptAlice.getPubKey(bob),
}); });
env.close();
mptAlice.printMPT(bob);
mptAlice.mergeInbox({ mptAlice.mergeInbox({
.account = bob, .account = bob,
}); });
env.close();
mptAlice.printMPT(bob);
} }
void void

View File

@@ -573,8 +573,6 @@ MPTTester::getEncryptedBalance(
return Buffer( return Buffer(
(*sle)[sfIssuerEncryptedBalance].data(), (*sle)[sfIssuerEncryptedBalance].data(),
(*sle)[sfIssuerEncryptedBalance].size()); (*sle)[sfIssuerEncryptedBalance].size());
return {};
} }
return {}; return {};

View File

@@ -20,6 +20,7 @@
#include <xrpld/app/misc/DelegateUtils.h> #include <xrpld/app/misc/DelegateUtils.h>
#include <xrpld/app/tx/detail/ConfidentialConvert.h> #include <xrpld/app/tx/detail/ConfidentialConvert.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h> #include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>
@@ -78,9 +79,20 @@ ConfidentialConvert::preclaim(PreclaimContext const& ctx)
if (!sleMptoken) if (!sleMptoken)
return tecOBJECT_NOT_FOUND; return tecOBJECT_NOT_FOUND;
// we still allow conversion of zero amount auto const mptIssue = MPTIssue{ctx.tx[sfMPTokenIssuanceID]};
if ((*sleMptoken)[~sfMPTAmount].value_or(0) < ctx.tx[sfMPTAmount]) STAmount const mptAmount = STAmount(
MPTAmount{static_cast<MPTAmount::value_type>(ctx.tx[sfMPTAmount])},
mptIssue);
if (accountHolds(
ctx.view,
ctx.tx[sfAccount],
mptIssue,
FreezeHandling::fhZERO_IF_FROZEN,
AuthHandling::ahZERO_IF_UNAUTHORIZED,
ctx.j) < mptAmount)
{
return tecINSUFFICIENT_FUNDS; return tecINSUFFICIENT_FUNDS;
}
// must have pk to convert // must have pk to convert
if (!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey) && if (!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey) &&

View File

@@ -20,6 +20,7 @@
#include <xrpld/app/misc/DelegateUtils.h> #include <xrpld/app/misc/DelegateUtils.h>
#include <xrpld/app/tx/detail/ConfidentialSend.h> #include <xrpld/app/tx/detail/ConfidentialSend.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/ConfidentialTransfer.h> #include <xrpl/protocol/ConfidentialTransfer.h>
#include <xrpl/protocol/Feature.h> #include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h> #include <xrpl/protocol/Indexes.h>