Support shared r for ConfidentialMPTSend equality proof (#6496)

* Support shared r for send equality proof
This commit is contained in:
yinyiqian1
2026-03-06 18:28:35 -05:00
committed by GitHub
parent fae34d0f36
commit 734b11c0e3
5 changed files with 52 additions and 54 deletions

View File

@@ -329,18 +329,6 @@ getConfidentialRecipientCount(bool hasAuditor)
return hasAuditor ? 4 : 3;
}
/**
* @brief Calculates the size of a multi-ciphertext equality proof.
*
* The proof size varies based on the number of recipients because each
* additional recipient requires additional proof components.
*
* @param nRecipients The number of recipients in the transfer.
* @return The size in bytes of the equality proof.
*/
std::size_t
getMultiCiphertextEqualityProofSize(std::size_t nRecipients);
/**
* @brief Verifies a multi-ciphertext equality proof.
*

View File

@@ -328,15 +328,6 @@ verifyRevealedAmount(
return tesSUCCESS;
}
std::size_t
getMultiCiphertextEqualityProofSize(std::size_t nRecipients)
{
// Points (33 bytes): T_m (1) + T_rG (nRecipients) + T_rP (nRecipients) = 1
// + 2nRecipients Scalars (32 bytes): s_m (1) + s_r (nRecipients) = 1 +
// nRecipients
return ((1 + (2 * nRecipients)) * 33) + ((1 + nRecipients) * 32);
}
TER
verifyMultiCiphertextEqualityProof(
Slice const& proof,
@@ -347,31 +338,55 @@ verifyMultiCiphertextEqualityProof(
if (recipients.size() != nRecipients)
return tecINTERNAL; // LCOV_EXCL_LINE
if (proof.size() != getMultiCiphertextEqualityProofSize(nRecipients))
if (proof.size() != secp256k1_mpt_proof_equality_shared_r_size(nRecipients))
return tecINTERNAL; // LCOV_EXCL_LINE
std::vector<secp256k1_pubkey> r(nRecipients);
std::vector<secp256k1_pubkey> s(nRecipients);
std::vector<secp256k1_pubkey> pk(nRecipients);
auto const ctx = secp256k1Context();
secp256k1_pubkey c1;
std::vector<secp256k1_pubkey> c2_vec(nRecipients);
std::vector<secp256k1_pubkey> pk_vec(nRecipients);
for (size_t i = 0; i < nRecipients; ++i)
{
auto const& recipient = recipients[i];
if (recipient.encryptedAmount.size() != ecGamalEncryptedTotalLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (!makeEcPair(recipient.encryptedAmount, r[i], s[i]))
if (recipient.encryptedAmount.size() != ecGamalEncryptedTotalLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (recipient.publicKey.size() != ecPubKeyLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (secp256k1_ec_pubkey_parse(secp256k1Context(), &pk[i], recipient.publicKey.data(), ecPubKeyLength) != 1)
// Parse Shared C1 from the first recipient only
if (i == 0)
{
if (!secp256k1_ec_pubkey_parse(ctx, &c1, recipient.encryptedAmount.data(), ecGamalEncryptedLength))
return tecINTERNAL; // LCOV_EXCL_LINE
}
else
{
// All C1 bytes must be the same
if (std::memcmp(
recipient.encryptedAmount.data(), recipients[0].encryptedAmount.data(), ecGamalEncryptedLength) !=
0)
{
return tecBAD_PROOF;
}
}
if (secp256k1_ec_pubkey_parse(
ctx, &c2_vec[i], recipient.encryptedAmount.data() + ecGamalEncryptedLength, ecGamalEncryptedLength) !=
1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (secp256k1_ec_pubkey_parse(ctx, &pk_vec[i], recipient.publicKey.data(), ecPubKeyLength) != 1)
return tecINTERNAL; // LCOV_EXCL_LINE
}
int const result = secp256k1_mpt_verify_same_plaintext_multi(
secp256k1Context(), proof.data(), proof.size(), nRecipients, r.data(), s.data(), pk.data(), contextHash.data());
int const result = secp256k1_mpt_verify_equality_shared_r(
ctx, proof.data(), nRecipients, &c1, c2_vec.data(), pk_vec.data(), contextHash.data());
if (result != 1)
return tecBAD_PROOF;

View File

@@ -71,7 +71,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
std::string
getTrivialSendProofHex(size_t nRecipients)
{
size_t const sizeEquality = getMultiCiphertextEqualityProofSize(nRecipients);
size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients);
size_t const totalSize = sizeEquality + (2 * ecPedersenProofLength) + ecDoubleBulletproofLength;
Buffer buf(totalSize);

View File

@@ -762,9 +762,9 @@ MPTTester::getConfidentialSendProof(
auto const ctx = secp256k1Context();
std::vector<secp256k1_pubkey> r(nRecipients);
std::vector<secp256k1_pubkey> s(nRecipients);
std::vector<secp256k1_pubkey> pk(nRecipients);
secp256k1_pubkey c1;
std::vector<secp256k1_pubkey> c2_vec(nRecipients);
std::vector<secp256k1_pubkey> pk_vec(nRecipients);
std::vector<unsigned char> sr;
sr.reserve(nRecipients * ecBlindingFactorLength);
@@ -780,36 +780,31 @@ MPTTester::getConfidentialSendProof(
if (recipient.publicKey.size() != ecPubKeyLength)
return std::nullopt;
if (!secp256k1_ec_pubkey_parse(ctx, &r[i], ctData, ecGamalEncryptedLength))
if (i == 0)
{
return std::nullopt;
if (!secp256k1_ec_pubkey_parse(ctx, &c1, ctData, ecGamalEncryptedLength))
return std::nullopt;
}
if (!secp256k1_ec_pubkey_parse(ctx, &s[i], ctData + ecGamalEncryptedLength, ecGamalEncryptedLength))
{
return std::nullopt;
}
if (!secp256k1_ec_pubkey_parse(ctx, &pk[i], recipient.publicKey.data(), ecPubKeyLength))
if (!secp256k1_ec_pubkey_parse(ctx, &c2_vec[i], ctData + ecGamalEncryptedLength, ecGamalEncryptedLength))
return std::nullopt;
sr.insert(sr.end(), blindingFactor.data(), blindingFactor.data() + ecBlindingFactorLength);
if (!secp256k1_ec_pubkey_parse(ctx, &pk_vec[i], recipient.publicKey.data(), ecPubKeyLength))
return std::nullopt;
}
size_t sizeEquality = secp256k1_mpt_prove_same_plaintext_multi_size(nRecipients);
size_t const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(nRecipients);
Buffer equalityProof(sizeEquality);
// Get the multi-ciphertext equality proof
if (secp256k1_mpt_prove_same_plaintext_multi(
if (secp256k1_mpt_prove_equality_shared_r(
ctx,
equalityProof.data(),
&sizeEquality,
amount,
blindingFactor.data(), // The shared 'r' witness
nRecipients,
r.data(),
s.data(),
pk.data(),
sr.data(),
&c1,
c2_vec.data(),
pk_vec.data(),
contextHash.data()) != 1)
{
return std::nullopt;

View File

@@ -41,7 +41,7 @@ ConfidentialMPTSend::preflight(PreflightContext const& ctx)
// Check the length of the ZKProof
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
auto const sizeEquality = getMultiCiphertextEqualityProofSize(recipientCount);
auto const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(recipientCount);
auto const sizePedersenLinkage = 2 * ecPedersenProofLength;
if (ctx.tx[sfZKProof].length() != sizeEquality + sizePedersenLinkage + ecDoubleBulletproofLength)
@@ -81,7 +81,7 @@ verifySendProofs(
size_t currentOffset = 0;
// Extract equality proof
auto const sizeEquality = getMultiCiphertextEqualityProofSize(recipientCount);
auto const sizeEquality = secp256k1_mpt_proof_equality_shared_r_size(recipientCount);
if (remainingLength < sizeEquality)
return tecINTERNAL; // LCOV_EXCL_LINE