From b6d1a8d62b3d920db10b647ccefe4021de2db304 Mon Sep 17 00:00:00 2001 From: Peter Chen <34582813+PeterChen13579@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:27:33 -0800 Subject: [PATCH] Test Auditor for Confidential Send and revealed R (#6320) --- cspell.config.yaml | 5 + include/xrpl/protocol/ConfidentialTransfer.h | 5 +- src/test/app/ConfidentialTransfer_test.cpp | 215 ++++++++++++++++-- src/test/jtx/impl/mpt.cpp | 2 +- src/test/jtx/mpt.h | 2 +- .../app/tx/detail/ConfidentialMPTClawback.h | 5 +- .../app/tx/detail/ConfidentialMPTConvert.h | 5 +- .../tx/detail/ConfidentialMPTConvertBack.h | 5 +- .../app/tx/detail/ConfidentialMPTMergeInbox.h | 5 +- .../app/tx/detail/ConfidentialMPTSend.cpp | 2 +- src/xrpld/app/tx/detail/ConfidentialMPTSend.h | 5 +- 11 files changed, 209 insertions(+), 47 deletions(-) diff --git a/cspell.config.yaml b/cspell.config.yaml index b2f4a33769..8e756ffc93 100644 --- a/cspell.config.yaml +++ b/cspell.config.yaml @@ -86,6 +86,7 @@ words: - daria - dcmake - dearmor + - decryptor - deleteme - demultiplexer - deserializaton @@ -95,6 +96,7 @@ words: - distro - doxyfile - dxrpl + - elgamal - endmacro - exceptioned - Falco @@ -103,6 +105,7 @@ words: - fmtdur - fsanitize - funclets + - Gamal - gcov - gcovr - ghead @@ -183,6 +186,7 @@ words: - partitioner - paychan - paychans + - Pedersen - permdex - perminute - permissioned @@ -218,6 +222,7 @@ words: - sahyadri - Satoshi - scons + - Schnorr - secp - sendq - seqit diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h index 623d889660..55a2da43fa 100644 --- a/include/xrpl/protocol/ConfidentialTransfer.h +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -1,5 +1,4 @@ -#ifndef XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED -#define XRPL_PROTOCOL_CONFIDENTIALTRANSFER_H_INCLUDED +#pragma once #include #include @@ -439,5 +438,3 @@ verifyBalancePcmLinkage( uint256 const& contextHash); } // namespace xrpl - -#endif diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index 2630d2c16b..1432beee2e 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -48,27 +48,23 @@ class ConfidentialTransfer_test : public beast::unit_test::suite return trivialCiphertext; } - static std::string const& + std::string getTrivialSendProofHex(size_t nRecipients) { - static std::string const trivialProofHex = [nRecipients]() { - size_t const sizeEquality = getMultiCiphertextEqualityProofSize(nRecipients); - size_t const totalSize = sizeEquality + (2 * ecPedersenProofLength); + size_t const sizeEquality = getMultiCiphertextEqualityProofSize(nRecipients); + size_t const totalSize = sizeEquality + (2 * ecPedersenProofLength); - Buffer buf(totalSize); - std::memset(buf.data(), 0, totalSize); + Buffer buf(totalSize); + std::memset(buf.data(), 0, totalSize); - for (std::size_t i = 0; i < totalSize; i += ecGamalEncryptedLength) - { - buf.data()[i] = 0x02; - if (i + ecGamalEncryptedLength - 1 < totalSize) - buf.data()[i + ecGamalEncryptedLength - 1] = 0x01; - } + for (std::size_t i = 0; i < totalSize; i += ecGamalEncryptedLength) + { + buf.data()[i] = 0x02; + if (i + ecGamalEncryptedLength - 1 < totalSize) + buf.data()[i + ecGamalEncryptedLength - 1] = 0x01; + } - return strHex(buf); - }(); - - return trivialProofHex; + return strHex(buf); } void @@ -517,6 +513,38 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = bob, .amt = 10, .holderPubKey = mptAlice.getPubKey(bob), .err = tecOBJECT_NOT_FOUND}); } + // Verification of Issuer and and holder ciphertexts + { + Env env{*this, features}; + Account const alice("alice"); + Account const bob("bob"); + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy}); + + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 100); + + mptAlice.generateKeyPair(alice); + mptAlice.generateKeyPair(bob); + + mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)}); + + mptAlice.convert( + {.account = bob, + .amt = 10, + .holderPubKey = mptAlice.getPubKey(bob), + .holderEncryptedAmt = getTrivialCiphertext(), + .err = tecBAD_PROOF}); + + mptAlice.convert( + {.account = bob, + .amt = 10, + .holderPubKey = mptAlice.getPubKey(bob), + .issuerEncryptedAmt = getTrivialCiphertext(), + .err = tecBAD_PROOF}); + } + // trying to convert more than what bob has { Env env{*this, features}; @@ -1161,6 +1189,66 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .balanceCommitment = Buffer(100), .err = temMALFORMED}); } + + // test bad ciphertext + { + Env env{*this, features}; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + Account const auditor("auditor"); + MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .auditor = auditor}); + + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanPrivacy}); + + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = carol}); + mptAlice.generateKeyPair(alice); + mptAlice.generateKeyPair(bob); + mptAlice.generateKeyPair(carol); + mptAlice.generateKeyPair(auditor); + + mptAlice.set( + {.account = alice, + .issuerPubKey = mptAlice.getPubKey(alice), + .auditorPubKey = mptAlice.getPubKey(auditor)}); + mptAlice.pay(alice, bob, 100); + mptAlice.pay(alice, carol, 50); + + mptAlice.convert({ + .account = bob, + .amt = 50, + .holderPubKey = mptAlice.getPubKey(bob), + }); + + mptAlice.convert({ + .account = carol, + .amt = 40, + .holderPubKey = mptAlice.getPubKey(carol), + }); + + // auditor encrypted amount wrong length + mptAlice.send( + {.account = bob, + .dest = carol, + .amt = 10, + .proof = getTrivialSendProofHex(4), + .auditorEncryptedAmt = Buffer(10), + .amountCommitment = Buffer(ecPedersenCommitmentLength), + .balanceCommitment = Buffer(ecPedersenCommitmentLength), + .err = temBAD_CIPHERTEXT}); + + // auditor encrypted amount (correct length, invalid data) + mptAlice.send( + {.account = bob, + .dest = carol, + .amt = 10, + .proof = getTrivialSendProofHex(4), + .auditorEncryptedAmt = getBadCiphertext(), + .amountCommitment = Buffer(ecPedersenCommitmentLength), + .balanceCommitment = Buffer(ecPedersenCommitmentLength), + .err = temBAD_CIPHERTEXT}); + } } void @@ -1247,10 +1335,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = bob, .dest = unknown, .amt = 10, + .proof = getTrivialSendProofHex(3), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), - .proof = getTrivialSendProofHex(3), .amountCommitment = Buffer(ecPedersenCommitmentLength), .balanceCommitment = Buffer(ecPedersenCommitmentLength), .err = tecNO_TARGET}); @@ -1262,10 +1350,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = bob, .dest = dave, .amt = 10, + .proof = getTrivialSendProofHex(3), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), - .proof = getTrivialSendProofHex(3), .amountCommitment = Buffer(ecPedersenCommitmentLength), .balanceCommitment = Buffer(ecPedersenCommitmentLength), .err = tecNO_PERMISSION}); @@ -1273,10 +1361,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = dave, .dest = carol, .amt = 10, + .proof = getTrivialSendProofHex(3), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), - .proof = getTrivialSendProofHex(3), .amountCommitment = Buffer(ecPedersenCommitmentLength), .balanceCommitment = Buffer(ecPedersenCommitmentLength), .err = tecNO_PERMISSION}); @@ -1288,10 +1376,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = bob, .dest = eve, .amt = 10, + .proof = getTrivialSendProofHex(3), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), - .proof = getTrivialSendProofHex(3), .amountCommitment = Buffer(ecPedersenCommitmentLength), .balanceCommitment = Buffer(ecPedersenCommitmentLength), .err = tecOBJECT_NOT_FOUND}); @@ -1442,6 +1530,65 @@ class ConfidentialTransfer_test : public beast::unit_test::suite mptAlice.send( {.account = bob, .dest = carol, .amt = 10, .proof = getTrivialSendProofHex(3), .err = tecBAD_PROOF}); } + + // No Auditor key set, but auditor encrypted amt provided + { + mptAlice.send( + {.account = bob, + .dest = carol, + .amt = 10, + .proof = getTrivialSendProofHex(4), + .auditorEncryptedAmt = getTrivialCiphertext(), + .err = tecNO_PERMISSION}); + } + + // Auditor CipherText is Valid, but does not match the Txn Amount + { + Env env{*this, features}; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + Account const auditor("auditor"); + MPTTester mptAlice(env, alice, {.holders = {bob, carol}, .auditor = auditor}); + + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanPrivacy}); + + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = carol}); + mptAlice.generateKeyPair(alice); + mptAlice.generateKeyPair(bob); + mptAlice.generateKeyPair(carol); + mptAlice.generateKeyPair(auditor); + + mptAlice.set( + {.account = alice, + .issuerPubKey = mptAlice.getPubKey(alice), + .auditorPubKey = mptAlice.getPubKey(auditor)}); + mptAlice.pay(alice, bob, 100); + mptAlice.pay(alice, carol, 50); + + mptAlice.convert({ + .account = bob, + .amt = 50, + .holderPubKey = mptAlice.getPubKey(bob), + }); + + mptAlice.convert({ + .account = carol, + .amt = 40, + .holderPubKey = mptAlice.getPubKey(carol), + }); + + mptAlice.send( + {.account = bob, + .dest = carol, + .amt = 10, + .proof = getTrivialSendProofHex(4), + .auditorEncryptedAmt = getTrivialCiphertext(), + .amountCommitment = Buffer(ecPedersenCommitmentLength), + .balanceCommitment = Buffer(ecPedersenCommitmentLength), + .err = tecBAD_PROOF}); + } } void @@ -1908,6 +2055,34 @@ class ConfidentialTransfer_test : public beast::unit_test::suite }); } + // Verification of holder and issuer ciphertexts during convertBack + { + Env env{*this, features}; + Account const alice("alice"); + Account const bob("bob"); + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create({.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy}); + mptAlice.authorize({.account = bob}); + mptAlice.pay(alice, bob, 100); + + mptAlice.generateKeyPair(alice); + mptAlice.generateKeyPair(bob); + + mptAlice.set({.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)}); + + mptAlice.convert({.account = bob, .amt = 50, .holderPubKey = mptAlice.getPubKey(bob)}); + mptAlice.mergeInbox({.account = bob}); + + // Holder encrypted amount is valid format but mathematically incorrect for this convertBack + mptAlice.convertBack( + {.account = bob, .amt = 10, .holderEncryptedAmt = getTrivialCiphertext(), .err = tecBAD_PROOF}); + + // Issuer encrypted amount is valid format but mathematically incorrect for this convertBack + mptAlice.convertBack( + {.account = bob, .amt = 10, .issuerEncryptedAmt = getTrivialCiphertext(), .err = tecBAD_PROOF}); + } + // Alice has NOT set an auditor key, but Bob provides // auditorEncryptedAmt { diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 49cd6c46ff..7c7dd194c8 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -875,7 +875,7 @@ MPTTester::getConvertBackProof( { Buffer const pedersenProof = getBalanceLinkageProof(holder, contextHash, *holderPubKey, pcParams); - // todo: incoporate range proof + // todo: incorporate range proof return pedersenProof; } diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index b74c1f89e4..f83e2392a1 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -251,7 +251,7 @@ struct MPTConfidentialClawback }; /** - * @brief Stores the parameterss that are exclusively used to generate a + * @brief Stores the parameters that are exclusively used to generate a * pedersen linkage proof */ struct PedersenProofParams diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTClawback.h b/src/xrpld/app/tx/detail/ConfidentialMPTClawback.h index dcea4bc4ac..43a1e5c616 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTClawback.h +++ b/src/xrpld/app/tx/detail/ConfidentialMPTClawback.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED -#define XRPL_TX_CONFIDENTIALCLAWSBACK_H_INCLUDED +#pragma once #include @@ -41,5 +40,3 @@ public: }; } // namespace xrpl - -#endif diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTConvert.h b/src/xrpld/app/tx/detail/ConfidentialMPTConvert.h index 8147f32e79..f0208b396a 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTConvert.h +++ b/src/xrpld/app/tx/detail/ConfidentialMPTConvert.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED -#define XRPL_TX_CONFIDENTIALCONVERT_H_INCLUDED +#pragma once #include @@ -43,5 +42,3 @@ public: }; } // namespace xrpl - -#endif diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h b/src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h index c07c494c57..48154aaa83 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h +++ b/src/xrpld/app/tx/detail/ConfidentialMPTConvertBack.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED -#define XRPL_TX_CONFIDENTIALCONVERTBACK_H_INCLUDED +#pragma once #include @@ -44,5 +43,3 @@ public: }; } // namespace xrpl - -#endif diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h b/src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h index f7de7d0005..c35e87584f 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h +++ b/src/xrpld/app/tx/detail/ConfidentialMPTMergeInbox.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED -#define XRPL_TX_CONFIDENTIALMERGEINBOX_H_INCLUDED +#pragma once #include @@ -45,5 +44,3 @@ public: }; } // namespace xrpl - -#endif diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp b/src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp index 323fb4d28e..f740cac1dc 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp +++ b/src/xrpld/app/tx/detail/ConfidentialMPTSend.cpp @@ -110,7 +110,7 @@ verifySendProofs( if (remainingLength != 0) return tecINTERNAL; // LCOV_EXCL_LINE - // Prepare receipient list + // Prepare recipient list std::vector recipients; recipients.reserve(recipientCount); diff --git a/src/xrpld/app/tx/detail/ConfidentialMPTSend.h b/src/xrpld/app/tx/detail/ConfidentialMPTSend.h index f4ed8c9e18..1eef54d646 100644 --- a/src/xrpld/app/tx/detail/ConfidentialMPTSend.h +++ b/src/xrpld/app/tx/detail/ConfidentialMPTSend.h @@ -1,5 +1,4 @@ -#ifndef XRPL_TX_CONFIDENTIALSEND_H_INCLUDED -#define XRPL_TX_CONFIDENTIALSEND_H_INCLUDED +#pragma once #include @@ -51,5 +50,3 @@ public: }; } // namespace xrpl - -#endif