mirror of
https://github.com/XRPLF/rippled.git
synced 2026-06-02 16:26:48 +00:00
Support compact AND-composed sigma proof (#6859)
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<Slice>
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <openssl/rand.h>
|
||||
#include <utility/mpt_utility.h>
|
||||
|
||||
#include <secp256k1_mpt.h>
|
||||
|
||||
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<uint8_t>(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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<mpt_confidential_participant> 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<std::runtime_error>("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<std::runtime_error>("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<std::runtime_error>("Pedersen proof generation failed");
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
Buffer
|
||||
MPTTester::getBulletproof(
|
||||
std::vector<std::uint64_t> const& values,
|
||||
std::vector<Buffer> const& blindingFactors,
|
||||
uint256 const& contextHash) const
|
||||
{
|
||||
std::size_t const m = values.size();
|
||||
|
||||
if (m == 0 || m > 2 || m != blindingFactors.size())
|
||||
Throw<std::runtime_error>("getBulletproof: invalid input parameters");
|
||||
|
||||
for (auto const& bf : blindingFactors)
|
||||
{
|
||||
if (bf.size() != ecBlindingFactorLength)
|
||||
Throw<std::runtime_error>("Invalid blinding factor length");
|
||||
}
|
||||
|
||||
// Flatten blinding factors into contiguous memory (m * 32 bytes)
|
||||
std::vector<unsigned char> 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<std::runtime_error>("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<std::runtime_error>("Bulletproof generation failed");
|
||||
}
|
||||
|
||||
std::size_t const expectedLen =
|
||||
(m == 1) ? ecSingleBulletproofLength : ecDoubleBulletproofLength;
|
||||
if (proofLen != expectedLen)
|
||||
Throw<std::runtime_error>("Unexpected bulletproof length");
|
||||
|
||||
return Buffer(bulletproof.data(), proofLen);
|
||||
}
|
||||
|
||||
} // namespace jtx
|
||||
} // namespace test
|
||||
} // namespace xrpl
|
||||
|
||||
@@ -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<std::uint64_t> const& values,
|
||||
std::vector<Buffer> const& blindingFactors,
|
||||
uint256 const& contextHash) const;
|
||||
|
||||
Buffer
|
||||
getPedersenCommitment(std::uint64_t const amount, Buffer const& pedersenBlindingFactor);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user