From a43cf94ff785de9a6fc482b3810cb4e03be35746 Mon Sep 17 00:00:00 2001 From: Shawn Xie <35279399+shawnxie999@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:04:03 -0400 Subject: [PATCH] update `sfBlindingFactor` to type `uint256` and refactor helper functions to return `std::optional` --- include/xrpl/protocol/ConfidentialTransfer.h | 64 ++++++++++++------- include/xrpl/protocol/detail/sfields.macro | 6 +- .../transactors/token/ConfidentialMPTSend.h | 3 + src/libxrpl/protocol/ConfidentialTransfer.cpp | 42 ++++++------ .../token/ConfidentialMPTClawback.cpp | 4 +- .../token/ConfidentialMPTConvert.cpp | 18 ++---- .../token/ConfidentialMPTConvertBack.cpp | 25 ++++---- .../transactors/token/ConfidentialMPTSend.cpp | 36 +++++------ src/test/app/ConfidentialTransfer_test.cpp | 19 +----- src/test/jtx/impl/mpt.cpp | 2 +- 10 files changed, 108 insertions(+), 111 deletions(-) diff --git a/include/xrpl/protocol/ConfidentialTransfer.h b/include/xrpl/protocol/ConfidentialTransfer.h index 8ab7eadc32..bd86e7aacf 100644 --- a/include/xrpl/protocol/ConfidentialTransfer.h +++ b/include/xrpl/protocol/ConfidentialTransfer.h @@ -25,8 +25,9 @@ namespace xrpl { */ struct ConfidentialRecipient { - Slice publicKey; ///< The recipient's ElGamal public key (64 bytes). - Slice encryptedAmount; ///< The encrypted amount ciphertext (128 bytes). + Slice publicKey; ///< The recipient's ElGamal public key (size=xrpl::ecPubKeyLength). + Slice encryptedAmount; ///< The encrypted amount ciphertext + ///< (size=xrpl::ecGamalEncryptedTotalLength). }; /// Holds two secp256k1 public key components representing an ElGamal ciphertext (C1, C2). @@ -73,8 +74,8 @@ addCommonZKPFields( Serializer& s, std::uint16_t txType, AccountID const& account, - std::uint32_t sequence, - uint192 const& issuanceID); + uint192 const& issuanceID, + std::uint32_t sequence); /** * @brief Generates the context hash for ConfidentialMPTSend transactions. @@ -227,9 +228,10 @@ homomorphicSubtract(Slice const& a, Slice const& b); * using the provided blinding factor r. * * @param amt The plaintext amount to encrypt. - * @param pubKeySlice The recipient's ElGamal public key (64 bytes). - * @param blindingFactor The 32-byte randomness used as blinding factor r. - * @return The 66-byte ciphertext, or std::nullopt on failure. + * @param pubKeySlice The recipient's ElGamal public key (size=xrpl::ecPubKeyLength). + * @param blindingFactor The randomness used as blinding factor r + * (size=xrpl::ecBlindingFactorLength). + * @return The ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt on failure. */ std::optional encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindingFactor); @@ -240,10 +242,11 @@ encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindin * Creates a deterministic encryption of zero that is unique to the account * and MPT issuance. Used to initialize confidential balance fields. * - * @param pubKeySlice The holder's ElGamal public key (64 bytes). + * @param pubKeySlice The holder's ElGamal public key (size=xrpl::ecPubKeyLength). * @param account The account ID of the token holder. * @param mptId The MPToken Issuance ID. - * @return The 66-byte canonical zero ciphertext, or std::nullopt on failure. + * @return The canonical zero ciphertext (size=xrpl::ecGamalEncryptedTotalLength), or std::nullopt + * on failure. */ std::optional encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, MPTID const& mptId); @@ -254,8 +257,8 @@ encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, M * Proves that the submitter knows the secret key corresponding to the * provided public key, without revealing the secret key itself. * - * @param pubKeySlice The ElGamal public key (64 bytes). - * @param proofSlice The Schnorr proof (65 bytes). + * @param pubKeySlice The ElGamal public key (size=xrpl::ecPubKeyLength). + * @param proofSlice The Schnorr proof (size=xrpl::ecSchnorrProofLength). * @param contextHash The 256-bit context hash binding the proof. * @return tesSUCCESS if valid, or an error code otherwise. */ @@ -269,9 +272,9 @@ verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 co * ciphertext was correctly constructed using ElGamal encryption. * * @param amount The revealed plaintext amount. - * @param blindingFactor The 32-byte blinding factor used in encryption. - * @param pubKeySlice The recipient's ElGamal public key (64 bytes). - * @param ciphertext The ciphertext to verify (66 bytes). + * @param blindingFactor The blinding factor used in encryption (size=xrpl::ecBlindingFactorLength). + * @param pubKeySlice The recipient's ElGamal public key (size=xrpl::ecPubKeyLength). + * @param ciphertext The ciphertext to verify (size=xrpl::ecGamalEncryptedTotalLength). * @return tesSUCCESS if the encryption is valid, or an error code otherwise. */ TER @@ -302,7 +305,8 @@ checkEncryptedAmountFormat(STObject const& object); * issuer, and optionally the auditor using their respective public keys. * * @param amount The revealed plaintext amount. - * @param blindingFactor The 32-byte blinding factor used in all encryptions. + * @param blindingFactor The blinding factor used in all encryptions + * (size=xrpl::ecBlindingFactorLength). * @param holder The holder's public key and encrypted amount. * @param issuer The issuer's public key and encrypted amount. * @param auditor Optional auditor's public key and encrypted amount. @@ -331,6 +335,22 @@ getConfidentialRecipientCount(bool hasAuditor) return hasAuditor ? 4 : 3; } +/** + * @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 multi-ciphertext equality proof. * @@ -462,11 +482,10 @@ verifyAggregatedBulletproof( * * @param balanceCommitment The compressed Pedersen commitment to the balance (33 bytes). * @param amountCommitment The compressed Pedersen commitment to the amount (33 bytes). - * @param out Output buffer for the resulting remainder commitment (33 bytes). - * @return tesSUCCESS on success, tecINTERNAL on failure. + * @return The remainder commitment (33 bytes), or std::nullopt on failure. */ -TER -computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitment, Buffer& out); +std::optional +computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitment); /** * @brief Computes the remainder commitment for ConvertBack. @@ -476,9 +495,8 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme * * @param commitment The compressed Pedersen commitment (33 bytes). * @param amount The amount to subtract (must be non-zero). - * @param out Output buffer for the resulting commitment (33 bytes). - * @return tesSUCCESS on success, tecINTERNAL on failure or if amount is 0. + * @return The remainder commitment (33 bytes), or std::nullopt on failure or if amount is 0. */ -TER -computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& out); +std::optional +computeConvertBackRemainder(Slice const& commitment, uint64_t amount); } // namespace xrpl diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 7a18f27ff3..b1a32fa7df 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -206,6 +206,7 @@ TYPED_SFIELD(sfParentBatchID, UINT256, 36) TYPED_SFIELD(sfLoanBrokerID, UINT256, 37, SField::sMD_PseudoAccount | SField::sMD_Default) TYPED_SFIELD(sfLoanID, UINT256, 38) +TYPED_SFIELD(sfBlindingFactor, UINT256, 39) // number (common) TYPED_SFIELD(sfNumber, NUMBER, 1) @@ -312,9 +313,8 @@ TYPED_SFIELD(sfDestinationEncryptedAmount, VL, 41) TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42) TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43) TYPED_SFIELD(sfAuditorEncryptionKey, VL, 44) -TYPED_SFIELD(sfBlindingFactor, VL, 45) -TYPED_SFIELD(sfAmountCommitment, VL, 46) -TYPED_SFIELD(sfBalanceCommitment, VL, 47) +TYPED_SFIELD(sfAmountCommitment, VL, 45) +TYPED_SFIELD(sfBalanceCommitment, VL, 46) // account (common) TYPED_SFIELD(sfAccount, ACCOUNT, 1) diff --git a/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h b/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h index 8dca8716d1..0d844a3ea5 100644 --- a/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h +++ b/include/xrpl/tx/transactors/token/ConfidentialMPTSend.h @@ -32,6 +32,9 @@ 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 9545dfc8b0..d0e5c1d20a 100644 --- a/src/libxrpl/protocol/ConfidentialTransfer.cpp +++ b/src/libxrpl/protocol/ConfidentialTransfer.cpp @@ -371,7 +371,7 @@ verifyMultiCiphertextEqualityProof( if (recipients.size() != nRecipients) return tecINTERNAL; // LCOV_EXCL_LINE - if (proof.size() != secp256k1_mpt_proof_equality_shared_r_size(nRecipients)) + if (proof.size() != getEqualityProofSize(nRecipients)) return tecINTERNAL; // LCOV_EXCL_LINE auto const ctx = secp256k1Context(); @@ -700,12 +700,12 @@ verifyAggregatedBulletproof( return tesSUCCESS; } -TER -computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitment, Buffer& out) +std::optional +computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitment) { if (balanceCommitment.size() != ecPedersenCommitmentLength || amountCommitment.size() != ecPedersenCommitmentLength) - return tecINTERNAL; + return std::nullopt; auto const ctx = secp256k1Context(); @@ -714,7 +714,7 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme ctx, &pcBalance, balanceCommitment.data(), ecPedersenCommitmentLength); res != 1) { - return tecINTERNAL; + return std::nullopt; } secp256k1_pubkey pcAmount; @@ -722,13 +722,13 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme ctx, &pcAmount, amountCommitment.data(), ecPedersenCommitmentLength); res != 1) { - return tecINTERNAL; + return std::nullopt; } // Negate PC_amount point to get -PC_amount if (auto res = secp256k1_ec_pubkey_negate(ctx, &pcAmount); res != 1) { - return tecINTERNAL; + return std::nullopt; } // Compute pcRem = pcBalance + (-pcAmount) @@ -736,27 +736,28 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme secp256k1_pubkey pcRem; if (auto res = secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2); res != 1) { - return tecINTERNAL; + return std::nullopt; } // Serialize result to compressed format + Buffer out; out.alloc(ecPedersenCommitmentLength); size_t outLen = ecPedersenCommitmentLength; if (auto res = secp256k1_ec_pubkey_serialize( ctx, out.data(), &outLen, &pcRem, SECP256K1_EC_COMPRESSED); - res != 1) + res != 1 || outLen != ecPedersenCommitmentLength) { - return tecINTERNAL; + return std::nullopt; } - return tesSUCCESS; + return out; } -TER -computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& out) +std::optional +computeConvertBackRemainder(Slice const& commitment, uint64_t amount) { if (commitment.size() != ecPedersenCommitmentLength || amount == 0) - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE auto const ctx = secp256k1Context(); @@ -766,7 +767,7 @@ computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& ou ctx, &pcBalance, commitment.data(), ecPedersenCommitmentLength); res != 1) { - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE } // Convert amount to 32-byte big-endian scalar @@ -778,13 +779,13 @@ computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& ou secp256k1_pubkey mG; if (auto res = secp256k1_ec_pubkey_create(ctx, &mG, mScalar); res != 1) { - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE } // Negate mG to get -mG if (auto res = secp256k1_ec_pubkey_negate(ctx, &mG); res != 1) { - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE } // Compute pcRem = pcBalance + (-mG) @@ -792,19 +793,20 @@ computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& ou secp256k1_pubkey pcRem; if (auto res = secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2); res != 1) { - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE } // Serialize result to compressed format + Buffer out; out.alloc(ecPedersenCommitmentLength); size_t outLen = ecPedersenCommitmentLength; if (auto res = secp256k1_ec_pubkey_serialize( ctx, out.data(), &outLen, &pcRem, SECP256K1_EC_COMPRESSED); res != 1 || outLen != ecPedersenCommitmentLength) { - return tecINTERNAL; // LCOV_EXCL_LINE + return std::nullopt; // LCOV_EXCL_LINE } - return tesSUCCESS; + return out; } } // namespace xrpl diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp index 8acefb9cfd..ad3273220f 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTClawback.cpp @@ -56,7 +56,7 @@ ConfidentialMPTClawback::preclaim(PreclaimContext const& ctx) if (!sleIssuance) return tecOBJECT_NOT_FOUND; - // Sanity check: issuer must be the same as account + // Sanity check: account must be the same as issuer if (sleIssuance->getAccountID(sfIssuer) != account) return tefINTERNAL; // LCOV_EXCL_LINE @@ -123,7 +123,7 @@ ConfidentialMPTClawback::doApply() // Set holder's confidential balances to encrypted zero (*sleHolderMPToken)[sfConfidentialBalanceInbox] = *encZeroForHolder; - (*sleHolderMPToken)[sfConfidentialBalanceSpending] = *encZeroForHolder; + (*sleHolderMPToken)[sfConfidentialBalanceSpending] = std::move(*encZeroForHolder); (*sleHolderMPToken)[sfIssuerEncryptedBalance] = std::move(*encZeroForIssuer); incrementConfidentialVersion(*sleHolderMPToken); diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp index 61960a5326..6ba8cffafe 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvert.cpp @@ -22,9 +22,6 @@ ConfidentialMPTConvert::preflight(PreflightContext const& ctx) if (ctx.tx[sfMPTAmount] > maxMPTokenAmount) return temBAD_AMOUNT; - if (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength) - return temMALFORMED; - if (ctx.tx.isFieldPresent(sfHolderEncryptionKey)) { if (!isValidCompressedECPoint(ctx.tx[sfHolderEncryptionKey])) @@ -35,13 +32,14 @@ ConfidentialMPTConvert::preflight(PreflightContext const& ctx) if (!ctx.tx.isFieldPresent(sfZKProof)) return temMALFORMED; - // verify schnorr proof length when registerring holder ec public key + // verify schnorr proof length when registering holder ec public key if (ctx.tx[sfZKProof].size() != ecSchnorrProofLength) return temMALFORMED; } else { - // zkp should not be present if public key was already set + // Either both sfHolderEncryptionKey and sfZKProof should be present, or both should be + // absent. if (ctx.tx.isFieldPresent(sfZKProof)) return temMALFORMED; } @@ -66,7 +64,8 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx) if (!sleIssuance) return tecOBJECT_NOT_FOUND; - if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount)) + if (!sleIssuance->isFlag(lsfMPTCanConfidentialAmount) || + !sleIssuance->isFieldPresent(sfIssuerEncryptionKey)) return tecNO_PERMISSION; // already checked in preflight, but should also check that issuer on the @@ -74,10 +73,6 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx) if (sleIssuance->getAccountID(sfIssuer) == account) return tefINTERNAL; // LCOV_EXCL_LINE - // issuer has not uploaded their pub key yet - if (!sleIssuance->isFieldPresent(sfIssuerEncryptionKey)) - return tecNO_PERMISSION; - bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount); bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey); @@ -142,9 +137,10 @@ ConfidentialMPTConvert::preclaim(PreclaimContext const& ctx) (*sleIssuance)[sfAuditorEncryptionKey], ctx.tx[sfAuditorEncryptedAmount]}); } + auto const blindingFactor = ctx.tx[sfBlindingFactor]; return verifyRevealedAmount( amount, - ctx.tx[sfBlindingFactor], + Slice(blindingFactor.data(), blindingFactor.size()), {holderPubKey, ctx.tx[sfHolderEncryptedAmount]}, {(*sleIssuance)[sfIssuerEncryptionKey], ctx.tx[sfIssuerEncryptedAmount]}, auditor); diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp index 38b2dab6d1..93b27b2ea4 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTConvertBack.cpp @@ -24,9 +24,6 @@ ConfidentialMPTConvertBack::preflight(PreflightContext const& ctx) if (ctx.tx[sfMPTAmount] == 0 || ctx.tx[sfMPTAmount] > maxMPTokenAmount) return temBAD_AMOUNT; - if (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength) - return temMALFORMED; - if (!isValidCompressedECPoint(ctx.tx[sfBalanceCommitment])) return temMALFORMED; @@ -93,7 +90,7 @@ verifyProofs( // verify revealed amount if (auto const ter = verifyRevealedAmount( amount, - blindingFactor, + Slice(blindingFactor.data(), blindingFactor.size()), {holderPubKey, tx[sfHolderEncryptedAmount]}, {(*issuance)[sfIssuerEncryptionKey], tx[sfIssuerEncryptedAmount]}, auditor); @@ -141,18 +138,18 @@ verifyProofs( // verify bullet proof { // Compute PC_rem = PC_balance - mG (the commitment to the remaining balance) - Buffer pcRem; - if (auto const ter = computeConvertBackRemainder(tx[sfBalanceCommitment], amount, pcRem); - !isTesSuccess(ter)) + if (auto pcRem = computeConvertBackRemainder(tx[sfBalanceCommitment], amount)) { - valid = false; + // The bulletproof verifies that the remaining balance is non-negative + std::vector commitments{Slice(pcRem->data(), pcRem->size())}; + + if (auto const ter = verifyAggregatedBulletproof(bulletproof, commitments, contextHash); + !isTesSuccess(ter)) + { + valid = false; + } } - - // The bulletproof verifies that the remaining balance is non-negative - std::vector commitments{Slice(pcRem.data(), pcRem.size())}; - - if (auto const ter = verifyAggregatedBulletproof(bulletproof, commitments, contextHash); - !isTesSuccess(ter)) + else { valid = false; } diff --git a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp index 07617f99af..68e45aa2b4 100644 --- a/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp +++ b/src/libxrpl/tx/transactors/token/ConfidentialMPTSend.cpp @@ -40,11 +40,10 @@ ConfidentialMPTSend::preflight(PreflightContext const& ctx) // Check the length of the ZKProof auto const recipientCount = getConfidentialRecipientCount(hasAuditor); - auto const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(recipientCount); - auto const sizePedersenLinkage = 2 * ecPedersenProofLength; + auto const sizeEquality = getEqualityProofSize(recipientCount); if (ctx.tx[sfZKProof].length() != - sizeEquality + sizePedersenLinkage + ecDoubleBulletproofLength) + sizeEquality + doublePedersenProofLength + ecDoubleBulletproofLength) return temMALFORMED; // Check the Pedersen commitments are valid @@ -86,7 +85,7 @@ verifySendProofs( size_t currentOffset = 0; // Extract equality proof - auto const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(recipientCount); + auto const sizeEquality = getEqualityProofSize(recipientCount); if (remainingLength < sizeEquality) return tecINTERNAL; // LCOV_EXCL_LINE @@ -183,22 +182,15 @@ verifySendProofs( // Verify Range Proof { - Buffer pcRem; - // Derive PC_rem = PC_balance - PC_amount - if (auto const ter = computeSendRemainder( - ctx.tx[sfBalanceCommitment], ctx.tx[sfAmountCommitment], pcRem); - !isTesSuccess(ter)) - { - valid = false; - } - else + if (auto pcRem = + computeSendRemainder(ctx.tx[sfBalanceCommitment], ctx.tx[sfAmountCommitment])) { // Aggregated commitments: [PC_amount, PC_rem] // Prove that both the transfer amount and the remaining balance are in range std::vector commitments; commitments.push_back(ctx.tx[sfAmountCommitment]); - commitments.push_back(Slice{pcRem.data(), pcRem.size()}); + commitments.push_back(Slice{pcRem->data(), pcRem->size()}); if (auto const ter = verifyAggregatedBulletproof(rangeProof, commitments, contextHash); !isTesSuccess(ter)) @@ -206,6 +198,10 @@ verifySendProofs( valid = false; } } + else + { + valid = false; + } } if (!valid) @@ -271,11 +267,6 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx) !sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance)) return tecNO_PERMISSION; - // Sanity check: MPToken's auditor field must be present if auditing is - // enabled - if (requiresAuditor && !sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance)) - return tefINTERNAL; - // Check destination's MPToken existence auto const sleDestinationMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, destination)); if (!sleDestinationMPToken) @@ -287,6 +278,13 @@ ConfidentialMPTSend::preclaim(PreclaimContext const& ctx) !sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance)) return tecNO_PERMISSION; + // Sanity check: Both MPTokens' auditor fields must be present if auditing + // is enabled + if (requiresAuditor && + (!sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance) || + !sleDestinationMPToken->isFieldPresent(sfAuditorEncryptedBalance))) + return tefINTERNAL; // LCOV_EXCL_LINE + // Check lock MPTIssue const mptIssue(mptIssuanceID); if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter)) diff --git a/src/test/app/ConfidentialTransfer_test.cpp b/src/test/app/ConfidentialTransfer_test.cpp index ad467484a3..092dff7674 100644 --- a/src/test/app/ConfidentialTransfer_test.cpp +++ b/src/test/app/ConfidentialTransfer_test.cpp @@ -71,7 +71,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite std::string getTrivialSendProofHex(size_t nRecipients) { - size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients); + size_t const sizeEquality = getEqualityProofSize(nRecipients); size_t const totalSize = sizeEquality + (2 * ecPedersenProofLength) + ecDoubleBulletproofLength; @@ -365,15 +365,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .err = temMALFORMED, }); - // blinding factor length is invalid - mptAlice.convert({ - .account = alice, - .amt = 10, - .holderPubKey = mptAlice.getPubKey(bob), - .blindingFactor = makeZeroBuffer(10), - .err = temMALFORMED, - }); - // Holder encrypted amount is empty (length 0) mptAlice.convert({ .account = bob, @@ -2952,14 +2943,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite .err = temBAD_AMOUNT, }); - // invalid blinding factor length - mptAlice.convertBack({ - .account = alice, - .amt = 30, - .blindingFactor = Buffer{}, - .err = temMALFORMED, - }); - // Balance commitment has correct length but invalid EC point data mptAlice.convertBack({ .account = bob, diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 5fd7ba4a02..29503ab1f0 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -827,7 +827,7 @@ MPTTester::getConfidentialSendProof( return std::nullopt; } - size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients); + size_t const sizeEquality = getEqualityProofSize(nRecipients); Buffer equalityProof(sizeEquality); if (secp256k1_mpt_prove_equality_shared_r(