From 09778f2fecde43d27335a484f07a1561e176d1b5 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Thu, 16 Apr 2026 17:51:31 -0400 Subject: [PATCH] Support compact AND-composed sigma proof (#6859) --- conan.lock | 2 +- conanfile.py | 2 +- include/xrpl/protocol/ConfidentialTransfer.h | 81 +---- include/xrpl/protocol/Protocol.h | 21 +- .../transactors/token/ConfidentialMPTSend.h | 3 - src/libxrpl/protocol/ConfidentialTransfer.cpp | 16 +- .../token/ConfidentialMPTClawback.cpp | 4 +- .../token/ConfidentialMPTConvertBack.cpp | 4 +- .../transactors/token/ConfidentialMPTSend.cpp | 8 +- src/test/app/ConfidentialTransfer_test.cpp | 290 ++++++++---------- src/test/jtx/impl/mpt.cpp | 157 ++-------- src/test/jtx/mpt.h | 20 -- 12 files changed, 180 insertions(+), 428 deletions(-) diff --git a/conan.lock b/conan.lock index 3616e4a8ec..073ff0ffc3 100644 --- a/conan.lock +++ b/conan.lock @@ -12,7 +12,7 @@ "protobuf/6.33.5#d96d52ba5baaaa532f47bda866ad87a5%1774467363.12", "openssl/3.6.1#e6399de266349245a4542fc5f6c71552%1774458290.139", "nudb/2.0.9#11149c73f8f2baff9a0198fe25971fc7%1774883011.384", - "mpt-crypto/0.2.0-rc1#ed3f241f69d8b9ebf80069d1923d93a8%1773853481.755", + "mpt-crypto/0.3.0-rc1#468344c6855d4aeaa8bd31fb2c403f89%1776358155.918", "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914", "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492", "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1765842973.03", diff --git a/conanfile.py b/conanfile.py index 9809c753b3..eea13ecabe 100644 --- a/conanfile.py +++ b/conanfile.py @@ -31,7 +31,7 @@ class Xrpl(ConanFile): "ed25519/2015.03", "grpc/1.78.1", "libarchive/3.8.1", - "mpt-crypto/0.2.0-rc1", + "mpt-crypto/0.3.0-rc1", "nudb/2.0.9", "openssl/3.6.1", "secp256k1/0.7.1", diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h index 59be56911e..451b56f547 100644 --- a/include/xrpl/protocol/ConfidentialTransfer.h +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -296,37 +296,22 @@ getConfidentialRecipientCount(bool hasAuditor) } /** - * @brief Returns the size of a multi-ciphertext equality proof. - * - * Computes the byte size required for a zero-knowledge proof that demonstrates - * multiple ciphertexts encrypt the same plaintext value. The size depends on - * the number of recipients. - * - * @param nRecipients The number of recipients (typically 3 or 4). - * @return The proof size in bytes. - */ -inline std::size_t -getEqualityProofSize(std::size_t nRecipients) -{ - return secp256k1_mpt_proof_equality_shared_r_size(nRecipients); -} - -/** - * @brief Verifies a clawback equality proof. + * @brief Verifies a compact sigma clawback proof. * * Proves that the issuer knows the exact amount encrypted in the holder's * balance ciphertext. Used in ConfidentialMPTClawback to verify the issuer * can decrypt the balance using their private key. * * @param amount The revealed plaintext amount. - * @param proof The zero-knowledge proof bytes. - * @param pubKeySlice The issuer's ElGamal public key (64 bytes). - * @param ciphertext The issuer's encrypted balance on the holder's account (66 bytes). + * @param proof The zero-knowledge proof bytes (ecClawbackProofLength). + * @param pubKeySlice The issuer's ElGamal public key (ecPubKeyLength bytes). + * @param ciphertext The issuer's encrypted balance on the holder's account + * (ecGamalEncryptedTotalLength bytes). * @param contextHash The 256-bit context hash binding the proof. * @return tesSUCCESS if the proof is valid, or an error code otherwise. */ TER -verifyClawbackEqualityProof( +verifyClawbackProof( uint64_t const amount, Slice const& proof, Slice const& pubKeySlice, @@ -403,58 +388,4 @@ verifyConvertBackProof( uint64_t amount, uint256 const& contextHash); -/** - * @brief Sequential reader for extracting proof components from a ZKProof blob. - * - * Encapsulates the offset-based arithmetic for slicing a concatenated proof - * blob into its individual components (equality proofs, Pedersen linkage - * proofs, bulletproofs, etc.). Performs bounds checking on every read and - * tracks whether the entire blob has been consumed. - * - * Usage: - * @code - * ProofReader reader(tx[sfZKProof]); - * auto equalityProof = reader.read(sizeEquality); - * auto pedersenProof = reader.read(ecPedersenProofLength); - * if (!equalityProof || !pedersenProof || !reader.done()) - * return tecINTERNAL; - * @endcode - */ -class ProofReader -{ - Slice data_; - std::size_t offset_ = 0; - -public: - explicit ProofReader(Slice data) : data_(data) - { - } - - /** - * @brief Read the next @p length bytes from the proof blob. - * - * @param length Number of bytes to read. - * @return A Slice of the requested bytes, or std::nullopt if there are - * not enough remaining bytes. - */ - [[nodiscard]] std::optional - read(std::size_t length) - { - if (offset_ + length > data_.size()) - return std::nullopt; - auto result = data_.substr(offset_, length); - offset_ += length; - return result; - } - - /** - * @brief Returns true when every byte has been consumed. - */ - [[nodiscard]] bool - done() const - { - return offset_ == data_.size(); - } -}; - } // namespace xrpl diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index 4cab26301b..05891d76f5 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -313,9 +313,6 @@ std::size_t constexpr ecGamalEncryptedLength = 33; /** EC ElGamal ciphertext length: two 33-byte components concatenated */ std::size_t constexpr ecGamalEncryptedTotalLength = ecGamalEncryptedLength * 2; -/** Length of equality ZKProof in bytes */ -std::size_t constexpr ecEqualityProofLength = 98; - /** Length of EC point (compressed) */ std::size_t constexpr compressedECPointLength = 33; @@ -328,11 +325,8 @@ std::size_t constexpr ecPrivKeyLength = 32; /** Length of the EC blinding factor in bytes */ std::size_t constexpr ecBlindingFactorLength = 32; -/** Length of Schnorr ZKProof for public key registration in bytes */ -std::size_t constexpr ecSchnorrProofLength = 65; - -/** Length of ElGamal Pedersen linkage proof in bytes */ -std::size_t constexpr ecPedersenProofLength = 195; +/** Length of Schnorr ZKProof for public key registration (compact form) in bytes */ +std::size_t constexpr ecSchnorrProofLength = 64; /** Length of Pedersen Commitment (compressed) */ std::size_t constexpr ecPedersenCommitmentLength = compressedECPointLength; @@ -343,6 +337,17 @@ std::size_t constexpr ecSingleBulletproofLength = 688; /** Length of double bulletproof (range proof for 2 commitments) in bytes */ std::size_t constexpr ecDoubleBulletproofLength = 754; +/** Length of the ZKProof for ConfidentialMPTSend. + * 192 bytes compact sigma proof + 754 bytes double bulletproof. */ +std::size_t constexpr ecSendProofLength = 946; + +/** Length of the ZKProof for ConfidentialMPTConvertBack. + * 128 bytes compact sigma proof + 688 bytes single bulletproof. */ +std::size_t constexpr ecConvertBackProofLength = 816; + +/** Length of the ZKProof for ConfidentialMPTClawback. */ +std::size_t constexpr ecClawbackProofLength = 64; + /** Compressed EC point prefix for even y-coordinate */ std::uint8_t constexpr ecCompressedPrefixEvenY = 0x02; diff --git a/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h b/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h index 0d844a3ea5..8dca8716d1 100644 --- a/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h +++ b/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h @@ -32,9 +32,6 @@ namespace xrpl { */ class ConfidentialMPTSend : public Transactor { - /// Size of two Pedersen linkage proofs (amount + balance) - static constexpr std::size_t doublePedersenProofLength = 2 * ecPedersenProofLength; - public: static constexpr ConsequencesFactoryType ConsequencesFactory{Normal}; diff --git a/src/libxrpl/protocol/ConfidentialTransfer.cpp b/src/libxrpl/protocol/ConfidentialTransfer.cpp index 473af293ea..10906394f4 100644 --- a/src/libxrpl/protocol/ConfidentialTransfer.cpp +++ b/src/libxrpl/protocol/ConfidentialTransfer.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace xrpl { /** @@ -277,7 +279,6 @@ verifyRevealedAmount( auto const holderP = toParticipant(holder); auto const issuerP = toParticipant(issuer); - mpt_confidential_participant auditorP; mpt_confidential_participant const* auditorPtr = nullptr; if (auditor) @@ -337,7 +338,7 @@ verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 co } TER -verifyClawbackEqualityProof( +verifyClawbackProof( uint64_t const amount, Slice const& proof, Slice const& pubKeySlice, @@ -345,7 +346,7 @@ verifyClawbackEqualityProof( uint256 const& contextHash) { if (ciphertext.size() != ecGamalEncryptedTotalLength || pubKeySlice.size() != ecPubKeyLength || - proof.size() != ecEqualityProofLength) + proof.size() != ecClawbackProofLength) return tecINTERNAL; // LCOV_EXCL_LINE if (mpt_verify_clawback_proof( @@ -368,10 +369,7 @@ verifySendProof( uint256 const& contextHash) { auto const recipientCount = getConfidentialRecipientCount(auditor.has_value()); - auto const expectedProofSize = getEqualityProofSize(recipientCount) + - 2 * ecPedersenProofLength + ecDoubleBulletproofLength; - - if (proof.size() != expectedProofSize || sender.publicKey.size() != ecPubKeyLength || + if (proof.size() != ecSendProofLength || sender.publicKey.size() != ecPubKeyLength || sender.encryptedAmount.size() != ecGamalEncryptedTotalLength || destination.publicKey.size() != ecPubKeyLength || destination.encryptedAmount.size() != ecGamalEncryptedTotalLength || @@ -403,7 +401,6 @@ verifySendProof( if (mpt_verify_send_proof( proof.data(), - proof.size(), participants.data(), static_cast(recipientCount), spendingBalance.data(), @@ -424,8 +421,7 @@ verifyConvertBackProof( uint64_t amount, uint256 const& contextHash) { - if (proof.size() != ecPedersenProofLength + ecSingleBulletproofLength || - pubKeySlice.size() != ecPubKeyLength || + if (proof.size() != ecConvertBackProofLength || pubKeySlice.size() != ecPubKeyLength || spendingBalance.size() != ecGamalEncryptedTotalLength || balanceCommitment.size() != ecPedersenCommitmentLength) return tecINTERNAL; // LCOV_EXCL_LINE diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp index 4afcb5d29a..795d205885 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp @@ -31,7 +31,7 @@ ConfidentialMPTClawback::preflight(PreflightContext const& ctx) return temBAD_AMOUNT; // Verify proof length - if (ctx.tx[sfZKProof].length() != ecEqualityProofLength) + if (ctx.tx[sfZKProof].length() != ecClawbackProofLength) return temMALFORMED; return tesSUCCESS; @@ -95,7 +95,7 @@ ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx) // Verify the revealed confidential amount by the issuer matches the exact // confidential balance of the holder. - return verifyClawbackEqualityProof( + return verifyClawbackProof( amount, ctx.tx[sfZKProof], (*sleIssuance)[sfIssuerEncryptionKey], diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp index 43c32516d6..5cccead427 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp @@ -33,8 +33,8 @@ ConfidentialMPTConvertBack::preflight(PreflightContext const& ctx) if (auto const res = checkEncryptedAmountFormat(ctx.tx); !isTesSuccess(res)) return res; - // ConvertBack proof = pedersen linkage proof + single bulletproof - if (ctx.tx[sfZKProof].size() != ecPedersenProofLength + ecSingleBulletproofLength) + // ConvertBack proof = compact sigma proof (128 bytes) + single bulletproof (688 bytes) + if (ctx.tx[sfZKProof].size() != ecConvertBackProofLength) return temMALFORMED; return tesSUCCESS; diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp index e5e5df47a2..ae03db2ccf 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp @@ -39,12 +39,8 @@ ConfidentialMPTSend::preflight(PreflightContext const& ctx) if (hasAuditor && ctx.tx[sfAuditorEncryptedAmount].length() != ecGamalEncryptedTotalLength) return temBAD_CIPHERTEXT; - // Check the length of the ZKProof - auto const recipientCount = getConfidentialRecipientCount(hasAuditor); - auto const sizeEquality = getEqualityProofSize(recipientCount); - - if (ctx.tx[sfZKProof].length() != - sizeEquality + doublePedersenProofLength + ecDoubleBulletproofLength) + // Check the length of the ZKProof (fixed size regardless of recipient count) + if (ctx.tx[sfZKProof].length() != ecSendProofLength) return temMALFORMED; // Check the Pedersen commitments are valid diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index c47b2fc487..597e1e613f 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -71,19 +71,15 @@ class ConfidentialTransfer_test : public beast::unit_test::suite } std::string - getTrivialSendProofHex(size_t nRecipients) + getTrivialSendProofHex() { - size_t const sizeEquality = getEqualityProofSize(nRecipients); - size_t const totalSize = - sizeEquality + (2 * ecPedersenProofLength) + ecDoubleBulletproofLength; + Buffer buf(ecSendProofLength); + std::memset(buf.data(), 0, ecSendProofLength); - Buffer buf(totalSize); - std::memset(buf.data(), 0, totalSize); - - for (std::size_t i = 0; i < totalSize; i += ecGamalEncryptedLength) + for (std::size_t i = 0; i < ecSendProofLength; i += ecGamalEncryptedLength) { buf.data()[i] = ecCompressedPrefixEvenY; - if (i + ecGamalEncryptedLength - 1 < totalSize) + if (i + ecGamalEncryptedLength - 1 < ecSendProofLength) buf.data()[i + ecGamalEncryptedLength - 1] = 0x01; } @@ -2113,7 +2109,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = makeZeroBuffer(ecGamalEncryptedTotalLength), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2125,7 +2121,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .destEncryptedAmt = makeZeroBuffer(ecGamalEncryptedTotalLength), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2137,7 +2133,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .issuerEncryptedAmt = makeZeroBuffer(ecGamalEncryptedTotalLength), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2160,7 +2156,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = makeZeroBuffer(100), .balanceCommitment = getTrivialCommitment(), .err = temMALFORMED, @@ -2171,7 +2167,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = makeZeroBuffer(100), .err = temMALFORMED, @@ -2182,7 +2178,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = makeZeroBuffer(ecPedersenCommitmentLength), .balanceCommitment = getTrivialCommitment(), .err = temMALFORMED, @@ -2193,7 +2189,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = makeZeroBuffer(ecPedersenCommitmentLength), .err = temMALFORMED, @@ -2255,7 +2251,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(4), + .proof = getTrivialSendProofHex(), .auditorEncryptedAmt = makeZeroBuffer(10), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2267,7 +2263,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(4), + .proof = getTrivialSendProofHex(), .auditorEncryptedAmt = getBadCiphertext(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2382,7 +2378,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite jv[sfIssuerEncryptedAmount] = strHex(getTrivialCiphertext()); jv[sfAmountCommitment] = strHex(getTrivialCommitment()); jv[sfBalanceCommitment] = strHex(getTrivialCommitment()); - jv[sfZKProof] = getTrivialSendProofHex(3); + jv[sfZKProof] = getTrivialSendProofHex(); env(jv, ter(tecOBJECT_NOT_FOUND)); } @@ -2394,7 +2390,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = unknown, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), @@ -2410,7 +2406,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = dave, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), @@ -2422,7 +2418,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = dave, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), @@ -2438,7 +2434,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = eve, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = getTrivialCiphertext(), .destEncryptedAmt = getTrivialCiphertext(), .issuerEncryptedAmt = getTrivialCiphertext(), @@ -2702,7 +2698,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .err = tecBAD_PROOF, }); } @@ -2713,7 +2709,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(4), + .proof = getTrivialSendProofHex(), .auditorEncryptedAmt = getTrivialCiphertext(), .err = tecNO_PERMISSION, }); @@ -2773,7 +2769,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(4), + .proof = getTrivialSendProofHex(), .auditorEncryptedAmt = getTrivialCiphertext(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -2987,7 +2983,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 0, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = mptAlice.encryptAmount(bob, 0, bf), .destEncryptedAmt = mptAlice.encryptAmount(carol, 0, bf), .issuerEncryptedAmt = mptAlice.encryptAmount(alice, 0, bf), @@ -3005,7 +3001,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 0, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = mptAlice.encryptAmount(bob, 0, bf2), .destEncryptedAmt = mptAlice.encryptAmount(carol, 0, bf2), .issuerEncryptedAmt = mptAlice.encryptAmount(alice, 0, bf2), @@ -4976,7 +4972,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite jv[sfHolder] = bob.human(); jv[jss::TransactionType] = jss::ConfidentialMPTClawback; jv[sfMPTAmount] = std::to_string(10); - std::string const dummyProof(196, '0'); + std::string const dummyProof(ecClawbackProofLength * 2, '0'); jv[sfZKProof] = dummyProof; jv[sfMPTokenIssuanceID] = to_string(mptAlice.issuanceID()); @@ -5562,25 +5558,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor); auto const version = mptAlice.getMPTokenVersion(bob); - // These tests verify that the pedersen linkage proof validation + // These tests verify that the compact ConvertBack proof validation // correctly rejects proofs generated with incorrect parameters. - // The pedersen linkage proof proves that the balance commitment - // PC = balance*G + rho*H is derived from the holder's encrypted - // spending balance. - - // Helper to combine pedersen proof and bulletproof - auto const combineProofs = [](Buffer const& pedersenProof, Buffer const& bulletproof) { - Buffer combinedProof(pedersenProof.size() + bulletproof.size()); - std::memcpy(combinedProof.data(), pedersenProof.data(), pedersenProof.size()); - std::memcpy( - combinedProof.data() + pedersenProof.size(), - bulletproof.data(), - bulletproof.size()); - return combinedProof; - }; - - auto const holderPubKey = mptAlice.getPubKey(bob); - BEAST_EXPECT(holderPubKey.has_value()); + // The compact proof simultaneously verifies balance ownership, + // commitment linkage, and that remaining balance is non-negative. // Test 1: Proof generated with wrong pedersen commitment value. // The proof uses PC(1, rho) but the transaction submits PC(balance, rho). @@ -5710,13 +5691,12 @@ class ConfidentialTransfer_test : public beast::unit_test::suite // sequence, issuanceID, amount, version). Using a different context hash // makes the proof invalid for this transaction, preventing replay attacks. { - uint256 const contextHash = - getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); uint256 const badContextHash{1}; - Buffer const pedersenProof = mptAlice.getBalanceLinkageProof( + + Buffer const proof = mptAlice.getConvertBackProof( bob, + amt, badContextHash, // wrong context hash - *holderPubKey, { .pedersenCommitment = pedersenCommitment, .amt = *spendingBalance, @@ -5724,12 +5704,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .blindingFactor = pcBlindingFactor, }); - // Bulletproof uses correct context hash so only pedersen proof fails - Buffer const bulletproof = - mptAlice.getBulletproof({*spendingBalance - amt}, {pcBlindingFactor}, contextHash); - - Buffer const proof = combineProofs(pedersenProof, bulletproof); - mptAlice.convertBack({ .account = bob, .amt = amt, @@ -5828,110 +5802,88 @@ class ConfidentialTransfer_test : public beast::unit_test::suite Buffer const bobCiphertext = mptAlice.encryptAmount(bob, amt, blindingFactor); auto const version = mptAlice.getMPTokenVersion(bob); - // These tests verify that the bulletproof (range proof) validation + // These tests verify that the compact ConvertBack proof (sigma + bulletproof) // correctly rejects proofs generated with incorrect parameters. - // The bulletproof proves that the remaining balance (balance - amount) - // is non-negative, i.e., in the range [0, 2^64-1]. This prevents - // overdrafts where a user tries to convert back more than they have. + // The compact proof simultaneously verifies balance ownership, commitment + // linkage, and that the remaining balance is non-negative. - // Helper to combine pedersen proof and bulletproof - auto const combineProofs = [](Buffer const& pedersenProof, Buffer const& bulletproof) { - Buffer combinedProof(pedersenProof.size() + bulletproof.size()); - std::memcpy(combinedProof.data(), pedersenProof.data(), pedersenProof.size()); - std::memcpy( - combinedProof.data() + pedersenProof.size(), - bulletproof.data(), - bulletproof.size()); - return combinedProof; - }; + // Test 1: Proof generated with wrong balance value. + // The sigma proof claims balance=1 but the spending balance contains the + // actual balance. The compact proof's balance-linkage check fails. + { + uint256 const contextHash = + getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); - auto const holderPubKey = mptAlice.getPubKey(bob); - BEAST_EXPECT(holderPubKey.has_value()); - - // Helper to generate pedersen proof with correct parameters. - // The pedersen proof links the encrypted balance to the pedersen commitment. - auto const getPedersenProof = [&](uint256 const& contextHash) { - return mptAlice.getBalanceLinkageProof( + Buffer const proof = mptAlice.getConvertBackProof( bob, + amt, contextHash, - *holderPubKey, + { + .pedersenCommitment = pedersenCommitment, + .amt = 1, // wrong balance (actual balance is ~40) + .encryptedAmt = *encryptedSpendingBalance, + .blindingFactor = pcBlindingFactor, + }); + + mptAlice.convertBack({ + .account = bob, + .amt = amt, + .proof = proof, + .holderEncryptedAmt = bobCiphertext, + .issuerEncryptedAmt = issuerCiphertext, + .blindingFactor = blindingFactor, + .pedersenCommitment = pedersenCommitment, + .err = tecBAD_PROOF, + }); + } + + // Test 2: Proof generated with wrong blinding factor (rho). + // The compact sigma proof must use the same blinding factor (rho) as the + // Pedersen commitment PC = balance*G + rho*H. Using a different rho + // creates an inconsistency the verifier detects. + { + uint256 const contextHash = + getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); + + Buffer const proof = mptAlice.getConvertBackProof( + bob, + amt, + contextHash, + { + .pedersenCommitment = pedersenCommitment, + .amt = *spendingBalance, + .encryptedAmt = *encryptedSpendingBalance, + .blindingFactor = generateBlindingFactor(), // wrong blinding factor + }); + + mptAlice.convertBack({ + .account = bob, + .amt = amt, + .proof = proof, + .holderEncryptedAmt = bobCiphertext, + .issuerEncryptedAmt = issuerCiphertext, + .blindingFactor = blindingFactor, + .pedersenCommitment = pedersenCommitment, + .err = tecBAD_PROOF, + }); + } + + // Test 3: Proof generated with wrong context hash. + // The context hash binds the proof to a specific transaction (account, + // sequence, issuanceID, amount, version). Using a different context hash + // makes the proof invalid for this transaction, preventing replay attacks. + { + uint256 const badContextHash{1}; + Buffer const proof = mptAlice.getConvertBackProof( + bob, + amt, + badContextHash, // wrong context hash { .pedersenCommitment = pedersenCommitment, .amt = *spendingBalance, .encryptedAmt = *encryptedSpendingBalance, .blindingFactor = pcBlindingFactor, }); - }; - - // Test 1: Bulletproof generated with wrong remaining balance. - // The bulletproof claims remaining balance is 1, but the pedersen - // commitment was created with (balance - amount). The verifier computes - // PC_rem = PC - amount*G and checks if the bulletproof matches, which fails. - { - uint256 const contextHash = - getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); - - Buffer const bulletproof = mptAlice.getBulletproof( - {1}, // wrong remaining balance - {pcBlindingFactor}, - contextHash); - - Buffer const proof = combineProofs(getPedersenProof(contextHash), bulletproof); - - mptAlice.convertBack({ - .account = bob, - .amt = amt, - .proof = proof, - .holderEncryptedAmt = bobCiphertext, - .issuerEncryptedAmt = issuerCiphertext, - .blindingFactor = blindingFactor, - .pedersenCommitment = pedersenCommitment, - .err = tecBAD_PROOF, - }); - } - - // Test 2: Bulletproof generated with wrong blinding factor. - // The bulletproof must use the same blinding factor (rho) as the pedersen - // commitment PC = (balance - amount)*G + rho*H. Using a different rho - // creates a commitment mismatch and verification fails. - { - uint256 const contextHash = - getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); - - Buffer const bulletproof = mptAlice.getBulletproof( - {*spendingBalance - amt}, - {generateBlindingFactor()}, // wrong blinding factor - contextHash); - - Buffer const proof = combineProofs(getPedersenProof(contextHash), bulletproof); - - mptAlice.convertBack({ - .account = bob, - .amt = amt, - .proof = proof, - .holderEncryptedAmt = bobCiphertext, - .issuerEncryptedAmt = issuerCiphertext, - .blindingFactor = blindingFactor, - .pedersenCommitment = pedersenCommitment, - .err = tecBAD_PROOF, - }); - } - - // Test 3: Bulletproof generated with wrong context hash. - // The context hash binds the proof to a specific transaction (account, - // sequence, issuanceID, amount, version). Using a different context hash - // makes the proof invalid for this transaction, preventing replay attacks. - { - uint256 const contextHash = - getConvertBackContextHash(bob, mptAlice.issuanceID(), env.seq(bob), version); - - uint256 const badContextHash{1}; - Buffer const bulletproof = mptAlice.getBulletproof( - {*spendingBalance - amt}, - {pcBlindingFactor}, - badContextHash); // wrong context hash - - Buffer const proof = combineProofs(getPedersenProof(contextHash), bulletproof); mptAlice.convertBack({ .account = bob, @@ -6627,7 +6579,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = getBadCiphertext(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6639,7 +6591,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .destEncryptedAmt = getBadCiphertext(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6651,7 +6603,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .issuerEncryptedAmt = getBadCiphertext(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6670,7 +6622,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = badCommitment, .balanceCommitment = getTrivialCommitment(), .err = temMALFORMED, @@ -6680,7 +6632,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = badCommitment, .err = temMALFORMED, @@ -6714,10 +6666,8 @@ class ConfidentialTransfer_test : public beast::unit_test::suite {.account = carol, .amt = 30, .holderPubKey = mptAlice.getPubKey(carol)}); mptAlice.mergeInbox({.account = carol}); - size_t const proofSize = - getEqualityProofSize(3) + 2 * ecPedersenProofLength + ecDoubleBulletproofLength; - Buffer badProof(proofSize); - std::memset(badProof.data(), 0xFF, proofSize); + Buffer badProof(ecSendProofLength); + std::memset(badProof.data(), 0xFF, ecSendProofLength); badProof.data()[0] = ecCompressedPrefixEvenY; mptAlice.send({ @@ -6777,7 +6727,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = badC1goodC2, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6789,7 +6739,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = goodC1badC2, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6801,7 +6751,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .destEncryptedAmt = badC1goodC2, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6813,7 +6763,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .destEncryptedAmt = goodC1badC2, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6891,7 +6841,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .senderEncryptedAmt = wrongGroupCt, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6903,7 +6853,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .destEncryptedAmt = wrongGroupCt, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6915,7 +6865,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .issuerEncryptedAmt = wrongGroupCt, .amountCommitment = getTrivialCommitment(), .balanceCommitment = getTrivialCommitment(), @@ -6927,7 +6877,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = wrongGroupCommitment, .balanceCommitment = getTrivialCommitment(), .err = tecBAD_PROOF, @@ -6938,7 +6888,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .account = bob, .dest = carol, .amt = 10, - .proof = getTrivialSendProofHex(3), + .proof = getTrivialSendProofHex(), .amountCommitment = getTrivialCommitment(), .balanceCommitment = wrongGroupCommitment, .err = tecBAD_PROOF, @@ -8405,7 +8355,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite jv[sfMPTokenIssuanceID] = to_string(mptAlice.issuanceID()); jv[sfHolder] = bob.human(); jv[sfMPTAmount.jsonName] = "50"; - jv[sfZKProof.jsonName] = std::string(ecEqualityProofLength * 2, '0'); + jv[sfZKProof.jsonName] = std::string(ecClawbackProofLength * 2, '0'); env(jv, delegate::as(dave), ter(temMALFORMED)); } @@ -8418,7 +8368,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite jv[sfMPTokenIssuanceID] = to_string(mptAlice.issuanceID()); jv[sfHolder] = carol.human(); jv[sfMPTAmount.jsonName] = "100"; - jv[sfZKProof.jsonName] = std::string(ecEqualityProofLength * 2, '0'); + jv[sfZKProof.jsonName] = std::string(ecClawbackProofLength * 2, '0'); env(jv, delegate::as(dave), ter(temMALFORMED)); } } diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 32e1d3c275..a70829b6de 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -792,7 +792,7 @@ MPTTester::getClawbackProof( if (pubKeyBlob.size() != ecPubKeyLength) return std::nullopt; - Buffer proof(ecEqualityProofLength); + Buffer proof(ecClawbackProofLength); if (mpt_get_clawback_proof( privateKey.data(), @@ -838,7 +838,6 @@ MPTTester::getConfidentialSendProof( PedersenProofParams const& amountParams, PedersenProofParams const& balanceParams) const { - auto const pedersenAmountParams = makePedersenParams(amountParams); auto const pedersenBalanceParams = makePedersenParams(balanceParams); if (recipients.size() != nRecipients) return std::nullopt; @@ -850,6 +849,13 @@ MPTTester::getConfidentialSendProof( if (!senderPrivKey) return std::nullopt; + auto const senderPubKey = getPubKey(sender); + if (!senderPubKey || senderPubKey->size() != ecPubKeyLength) + return std::nullopt; + + if (amountParams.pedersenCommitment.size() != ecPedersenCommitmentLength) + return std::nullopt; + // Build mpt_confidential_participant array std::vector participants(nRecipients); for (size_t i = 0; i < nRecipients; ++i) @@ -862,17 +868,18 @@ MPTTester::getConfidentialSendProof( std::memcpy(participants[i].ciphertext, r.encryptedAmount.data(), kMPT_ELGAMAL_TOTAL_SIZE); } - size_t proofLen = get_confidential_send_proof_size(nRecipients); + size_t proofLen = ecSendProofLength; Buffer proof(proofLen); if (mpt_get_confidential_send_proof( senderPrivKey->data(), + senderPubKey->data(), amount, participants.data(), nRecipients, blindingFactor.data(), contextHash.data(), - &pedersenAmountParams, + amountParams.pedersenCommitment.data(), &pedersenBalanceParams, proof.data(), &proofLen) != 0) @@ -914,8 +921,8 @@ MPTTester::getConvertBackProof( uint256 const& contextHash, PedersenProofParams const& pcParams) const { - // Expected total proof length: pedersen proof + single bulletproof - std::size_t constexpr expectedProofLength = ecPedersenProofLength + ecSingleBulletproofLength; + // Expected total proof length: compact sigma proof (128 bytes) + single bulletproof (688 bytes) + std::size_t constexpr expectedProofLength = ecConvertBackProofLength; auto const sleMptoken = env_.le(keylet::mptoken(*id_, holder.id())); if (!sleMptoken || !sleMptoken->isFieldPresent(sfConfidentialBalanceSpending)) @@ -1317,12 +1324,14 @@ MPTTester::send(MPTConfidentialSend const& arg) } // Fill in the commitment if not provided + // The amount commitment must use the same blinding factor as the ElGamal + // encryption. The sigma proof links the two, so using different randomness + // for each would cause proof verification to fail. Buffer amountCommitment, balanceCommitment; - auto const amountBlindingFactor = generateBlindingFactor(); if (arg.amountCommitment) amountCommitment = *arg.amountCommitment; else - amountCommitment = getPedersenCommitment(*arg.amt, amountBlindingFactor); + amountCommitment = getPedersenCommitment(*arg.amt, blindingFactor); jv[sfAmountCommitment] = strHex(amountCommitment); @@ -1390,7 +1399,7 @@ MPTTester::send(MPTConfidentialSend const& arg) blindingFactor, nRecipients, ctxHash, - {amountCommitment, *arg.amt, senderAmt, amountBlindingFactor}, + {amountCommitment, *arg.amt, senderAmt, blindingFactor}, {balanceCommitment, *prevSenderSpending, *prevEncryptedSenderSpending, @@ -1401,11 +1410,7 @@ MPTTester::send(MPTConfidentialSend const& arg) jv[sfZKProof.jsonName] = strHex(*proof); else { - size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients); - size_t const dummySize = - sizeEquality + 2 * ecPedersenProofLength + ecDoubleBulletproofLength; - - jv[sfZKProof.jsonName] = strHex(makeZeroBuffer(dummySize)); + jv[sfZKProof.jsonName] = strHex(makeZeroBuffer(ecSendProofLength)); } } @@ -1581,12 +1586,13 @@ MPTTester::sendJV( version = getMPTokenVersion(*arg.account); } + // The amount commitment must use the same blinding factor as the tx ElGamal + // encryption blinding factor. Buffer amountCommitment, balanceCommitment; - auto const amountBlindingFactor = generateBlindingFactor(); if (arg.amountCommitment) amountCommitment = *arg.amountCommitment; else - amountCommitment = getPedersenCommitment(*arg.amt, amountBlindingFactor); + amountCommitment = getPedersenCommitment(*arg.amt, blindingFactor); jv[sfAmountCommitment] = strHex(amountCommitment); @@ -1641,7 +1647,7 @@ MPTTester::sendJV( blindingFactor, nRecipients, ctxHash, - {amountCommitment, *arg.amt, senderAmt, amountBlindingFactor}, + {amountCommitment, *arg.amt, senderAmt, blindingFactor}, {balanceCommitment, prevSenderSpending, *prevEncryptedSenderSpending, @@ -1651,12 +1657,7 @@ MPTTester::sendJV( if (proof) jv[sfZKProof.jsonName] = strHex(*proof); else - { - size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients); - size_t const dummySize = - sizeEquality + 2 * ecPedersenProofLength + ecDoubleBulletproofLength; - jv[sfZKProof.jsonName] = strHex(makeZeroBuffer(dummySize)); - } + jv[sfZKProof.jsonName] = strHex(makeZeroBuffer(ecSendProofLength)); } return jv; @@ -1751,7 +1752,7 @@ MPTTester::confidentialClaw(MPTConfidentialClawback const& arg) if (proof) jv[sfZKProof] = strHex(*proof); else - jv[sfZKProof] = strHex(makeZeroBuffer(ecEqualityProofLength)); + jv[sfZKProof] = strHex(makeZeroBuffer(ecClawbackProofLength)); } auto const holderPubAmt = getBalance(*arg.holder); @@ -2066,7 +2067,7 @@ MPTTester::convertBack(MPTConvertBack const& arg) // generate a dummy proof if no encrypted amount field, so that other // preflight/preclaim are checked if (!prevEncryptedSpendingBalance) - proof = makeZeroBuffer(ecPedersenProofLength + ecSingleBulletproofLength); + proof = makeZeroBuffer(ecConvertBackProofLength); else { proof = getConvertBackProof( @@ -2198,7 +2199,7 @@ MPTTester::convertBackJV(MPTConvertBack const& arg, std::uint32_t seq) Buffer proof; if (!prevEncSpending) - proof = makeZeroBuffer(ecPedersenProofLength + ecSingleBulletproofLength); + proof = makeZeroBuffer(ecConvertBackProofLength); else proof = getConvertBackProof( *arg.account, @@ -2217,110 +2218,6 @@ MPTTester::convertBackJV(MPTConvertBack const& arg, std::uint32_t seq) return jv; } -Buffer -MPTTester::getAmountLinkageProof( - Buffer const& pubKey, - Buffer const& blindingFactor, - uint256 const& contextHash, - PedersenProofParams const& params) const -{ - if (pubKey.size() != ecPubKeyLength || blindingFactor.size() != ecBlindingFactorLength) - return makeZeroBuffer(ecPedersenProofLength); - - auto const pedersenParams = makePedersenParams(params); - Buffer proof(ecPedersenProofLength); - - if (mpt_get_amount_linkage_proof( - pubKey.data(), - blindingFactor.data(), - contextHash.data(), - &pedersenParams, - proof.data()) != 0) - { - Throw("Amount Linkage Proof generation failed"); - } - - return proof; -} - -Buffer -MPTTester::getBalanceLinkageProof( - Account const& account, - uint256 const& contextHash, - Buffer const& pubKey, - PedersenProofParams const& params) const -{ - if (pubKey.size() != ecPubKeyLength) - return makeZeroBuffer(ecPedersenProofLength); - - auto const privKey = getPrivKey(account); - if (!privKey || privKey->size() != ecPrivKeyLength) - Throw("Failed to get Pedersen proof private key"); - - auto const pedersenParams = makePedersenParams(params); - Buffer proof(ecPedersenProofLength); - - if (mpt_get_balance_linkage_proof( - privKey->data(), pubKey.data(), contextHash.data(), &pedersenParams, proof.data()) != 0) - Throw("Pedersen proof generation failed"); - - return proof; -} - -Buffer -MPTTester::getBulletproof( - std::vector const& values, - std::vector const& blindingFactors, - uint256 const& contextHash) const -{ - std::size_t const m = values.size(); - - if (m == 0 || m > 2 || m != blindingFactors.size()) - Throw("getBulletproof: invalid input parameters"); - - for (auto const& bf : blindingFactors) - { - if (bf.size() != ecBlindingFactorLength) - Throw("Invalid blinding factor length"); - } - - // Flatten blinding factors into contiguous memory (m * 32 bytes) - std::vector blindingsFlat(m * ecBlindingFactorLength); - for (std::size_t i = 0; i < m; ++i) - std::memcpy( - blindingsFlat.data() + i * ecBlindingFactorLength, - blindingFactors[i].data(), - ecBlindingFactorLength); - - secp256k1_pubkey pk_base; - if (secp256k1_mpt_get_h_generator(secp256k1Context(), &pk_base) != 1) - Throw("Failed to get H generator"); - - // Proof size scales with m; use safe upper bound - Buffer bulletproof(4096); - std::size_t proofLen = 4096; - - if (secp256k1_bulletproof_prove_agg( - secp256k1Context(), - bulletproof.data(), - &proofLen, - values.data(), - blindingsFlat.data(), - m, - &pk_base, - contextHash.data()) != 1) - { - Throw("Bulletproof generation failed"); - } - - std::size_t const expectedLen = - (m == 1) ? ecSingleBulletproofLength : ecDoubleBulletproofLength; - if (proofLen != expectedLen) - Throw("Unexpected bulletproof length"); - - return Buffer(bulletproof.data(), proofLen); -} - } // namespace jtx } // namespace test } // namespace xrpl diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index ac409d5e36..b6f1a21a20 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -582,26 +582,6 @@ public: std::uint32_t getMPTokenVersion(Account const account) const; - Buffer - getAmountLinkageProof( - Buffer const& pubKey, - Buffer const& blindingFactor, - uint256 const& contextHash, - PedersenProofParams const& params) const; - - Buffer - getBalanceLinkageProof( - Account const& account, - uint256 const& contextHash, - Buffer const& pubKey, - PedersenProofParams const& params) const; - - Buffer - getBulletproof( - std::vector const& values, - std::vector const& blindingFactors, - uint256 const& contextHash) const; - Buffer getPedersenCommitment(std::uint64_t const amount, Buffer const& pedersenBlindingFactor);