diff --git a/src/libxrpl/ledger/View.cpp b/src/libxrpl/ledger/View.cpp index 10b7e81b4f..438827c5dc 100644 --- a/src/libxrpl/ledger/View.cpp +++ b/src/libxrpl/ledger/View.cpp @@ -501,7 +501,8 @@ accountHolds( // Only if auth check is needed, as it needs to do an additional read // operation. Note featureSingleAssetVault will affect error codes. if (zeroIfUnauthorized == ahZERO_IF_UNAUTHORIZED && - view.rules().enabled(featureSingleAssetVault)) + (view.rules().enabled(featureSingleAssetVault) || + view.rules().enabled(featureConfidentialTransfer))) { if (auto const err = requireAuth(view, mptIssue, account, AuthType::StrongAuth); diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index 243aa5f0d5..8698e24c15 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -60,9 +60,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .proof = "123", .holderPubKey = mptAlice.getPubKey(bob), }); - env.close(); - - mptAlice.printMPT(bob); mptAlice.convert({ .account = bob, @@ -70,16 +67,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .proof = "123", }); - mptAlice.printMPT(bob); - mptAlice.convert({ .account = bob, .amt = 40, .proof = "123", }); - - env.close(); - mptAlice.printMPT(bob); } void @@ -437,6 +429,100 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .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 } @@ -473,15 +559,9 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .holderPubKey = mptAlice.getPubKey(bob), }); - env.close(); - mptAlice.printMPT(bob); - mptAlice.mergeInbox({ .account = bob, }); - - env.close(); - mptAlice.printMPT(bob); } void diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 50a2256ba3..05143121b2 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -573,8 +573,6 @@ MPTTester::getEncryptedBalance( return Buffer( (*sle)[sfIssuerEncryptedBalance].data(), (*sle)[sfIssuerEncryptedBalance].size()); - - return {}; } return {}; diff --git a/src/xrpld/app/tx/detail/ConfidentialConvert.cpp b/src/xrpld/app/tx/detail/ConfidentialConvert.cpp index 9cc233ce84..7c04021551 100644 --- a/src/xrpld/app/tx/detail/ConfidentialConvert.cpp +++ b/src/xrpld/app/tx/detail/ConfidentialConvert.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -78,9 +79,20 @@ ConfidentialConvert::preclaim(PreclaimContext const& ctx) if (!sleMptoken) return tecOBJECT_NOT_FOUND; - // we still allow conversion of zero amount - if ((*sleMptoken)[~sfMPTAmount].value_or(0) < ctx.tx[sfMPTAmount]) + auto const mptIssue = MPTIssue{ctx.tx[sfMPTokenIssuanceID]}; + STAmount const mptAmount = STAmount( + MPTAmount{static_cast(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; + } // must have pk to convert if (!sleMptoken->isFieldPresent(sfHolderElGamalPublicKey) && diff --git a/src/xrpld/app/tx/detail/ConfidentialSend.cpp b/src/xrpld/app/tx/detail/ConfidentialSend.cpp index 64519f3b15..e599558dca 100644 --- a/src/xrpld/app/tx/detail/ConfidentialSend.cpp +++ b/src/xrpld/app/tx/detail/ConfidentialSend.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include