refactor: return optional buffers for helper functions (#6520)

This commit is contained in:
Shawn Xie
2026-03-11 12:02:27 -04:00
committed by GitHub
parent 84cc8599af
commit eeb0d15ea9
8 changed files with 330 additions and 271 deletions

View File

@@ -25,8 +25,15 @@ namespace xrpl {
*/
struct ConfidentialRecipient
{
Slice const publicKey; ///< The recipient's ElGamal public key (64 bytes).
Slice const encryptedAmount; ///< The encrypted amount ciphertext (128 bytes).
Slice publicKey; ///< The recipient's ElGamal public key (64 bytes).
Slice encryptedAmount; ///< The encrypted amount ciphertext (128 bytes).
};
/// Holds two secp256k1 public key components representing an ElGamal ciphertext (C1, C2).
struct EcPair
{
secp256k1_pubkey c1;
secp256k1_pubkey c2;
};
/**
@@ -146,29 +153,25 @@ getConvertBackContextHash(
* @brief Parses an ElGamal ciphertext into two secp256k1 public key components.
*
* Breaks a 66-byte encrypted amount (two 33-byte compressed EC points) into
* two secp256k1_pubkey structures (C1, C2) for use in cryptographic operations.
* a pair containing (C1, C2) for use in cryptographic operations.
*
* @param buffer The 66-byte buffer containing the compressed ciphertext.
* @param out1 Output: The C1 component of the ElGamal ciphertext.
* @param out2 Output: The C2 component of the ElGamal ciphertext.
* @return true if parsing succeeds, false if the buffer is invalid.
* @return The parsed pair (c1, c2) if successful, std::nullopt if the buffer is invalid.
*/
bool
makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2);
std::optional<EcPair>
makeEcPair(Slice const& buffer);
/**
* @brief Serializes two secp256k1 public key components into compressed form.
* @brief Serializes an EcPair into compressed form.
*
* Converts two secp256k1_pubkey structures (C1, C2) back into a 66-byte
* buffer containing two 33-byte compressed EC points.
* Converts an EcPair (C1, C2) back into a 66-byte buffer containing
* two 33-byte compressed EC points.
*
* @param in1 The C1 component to serialize.
* @param in2 The C2 component to serialize.
* @param buffer Output: The 66-byte buffer to write the compressed ciphertext.
* @return true if serialization succeeds, false otherwise.
* @param pair The EcPair to serialize.
* @return The 66-byte buffer, or std::nullopt if serialization fails.
*/
bool
serializeEcPair(secp256k1_pubkey const& in1, secp256k1_pubkey const& in2, Buffer& buffer);
std::optional<Buffer>
serializeEcPair(EcPair const& pair);
/**
* @brief Verifies that a buffer contains two valid, parsable EC public keys.
@@ -197,13 +200,12 @@ isValidCompressedECPoint(Slice const& buffer);
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a + b) from Enc(a) and Enc(b) without decryption.
*
* @param a The first ciphertext (66 bytes).
* @param b The second ciphertext (66 bytes).
* @param out Output: The resulting ciphertext Enc(a + b).
* @return tesSUCCESS on success, or an error code if parsing fails.
* @param a The first ciphertext (66 bytes).
* @param b The second ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a + b), or std::nullopt on failure.
*/
TER
homomorphicAdd(Slice const& a, Slice const& b, Buffer& out);
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b);
/**
* @brief Homomorphically subtracts two ElGamal ciphertexts.
@@ -211,13 +213,12 @@ homomorphicAdd(Slice const& a, Slice const& b, Buffer& out);
* Uses the additive homomorphic property of ElGamal encryption to compute
* Enc(a - b) from Enc(a) and Enc(b) without decryption.
*
* @param a The minuend ciphertext (66 bytes).
* @param b The subtrahend ciphertext (66 bytes).
* @param out Output: The resulting ciphertext Enc(a - b).
* @return tesSUCCESS on success, or an error code if parsing fails.
* @param a The minuend ciphertext (66 bytes).
* @param b The subtrahend ciphertext (66 bytes).
* @return The resulting ciphertext Enc(a - b), or std::nullopt on failure.
*/
TER
homomorphicSubtract(Slice const& a, Slice const& b, Buffer& out);
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b);
/**
* @brief Encrypts an amount using ElGamal encryption.
@@ -275,7 +276,7 @@ verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 co
*/
TER
verifyElGamalEncryption(
std::uint64_t const amount,
uint64_t const amount,
Slice const& blindingFactor,
Slice const& pubKeySlice,
Slice const& ciphertext);
@@ -309,7 +310,7 @@ checkEncryptedAmountFormat(STObject const& object);
*/
TER
verifyRevealedAmount(
std::uint64_t const amount,
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
@@ -479,5 +480,5 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme
* @return tesSUCCESS on success, tecINTERNAL on failure or if amount is 0.
*/
TER
computeConvertBackRemainder(Slice const& commitment, std::uint64_t amount, Buffer& out);
computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& out);
} // namespace xrpl

View File

@@ -87,11 +87,11 @@ getConvertBackContextHash(
return s.getSHA512Half();
}
bool
makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2)
std::optional<EcPair>
makeEcPair(Slice const& buffer)
{
if (buffer.length() != 2 * ecGamalEncryptedLength)
return false; // LCOV_EXCL_LINE
return std::nullopt; // LCOV_EXCL_LINE
auto parsePubKey = [](Slice const& slice, secp256k1_pubkey& out) {
return secp256k1_ec_pubkey_parse(
@@ -104,14 +104,15 @@ makeEcPair(Slice const& buffer, secp256k1_pubkey& out1, secp256k1_pubkey& out2)
Slice s1{buffer.data(), ecGamalEncryptedLength};
Slice s2{buffer.data() + ecGamalEncryptedLength, ecGamalEncryptedLength};
int const ret1 = parsePubKey(s1, out1);
int const ret2 = parsePubKey(s2, out2);
EcPair pair;
if (parsePubKey(s1, pair.c1) != 1 || parsePubKey(s2, pair.c2) != 1)
return std::nullopt;
return ret1 == 1 && ret2 == 1;
return pair;
}
bool
serializeEcPair(secp256k1_pubkey const& in1, secp256k1_pubkey const& in2, Buffer& buffer)
std::optional<Buffer>
serializeEcPair(EcPair const& pair)
{
auto serializePubKey = [](secp256k1_pubkey const& pub, unsigned char* out) {
size_t outLen = ecGamalEncryptedLength; // 33 bytes
@@ -120,19 +121,21 @@ serializeEcPair(secp256k1_pubkey const& in1, secp256k1_pubkey const& in2, Buffer
return ret == 1 && outLen == ecGamalEncryptedLength;
};
Buffer buffer(ecGamalEncryptedTotalLength);
unsigned char* ptr = buffer.data();
bool const res1 = serializePubKey(in1, ptr);
bool const res2 = serializePubKey(in2, ptr + ecGamalEncryptedLength);
bool const res1 = serializePubKey(pair.c1, ptr);
bool const res2 = serializePubKey(pair.c2, ptr + ecGamalEncryptedLength);
return res1 && res2;
if (!res1 || !res2)
return std::nullopt;
return buffer;
}
bool
isValidCiphertext(Slice const& buffer)
{
secp256k1_pubkey key1;
secp256k1_pubkey key2;
return makeEcPair(buffer, key1, key2);
return makeEcPair(buffer).has_value();
}
bool
@@ -149,57 +152,50 @@ isValidCompressedECPoint(Slice const& buffer)
return secp256k1_ec_pubkey_parse(secp256k1Context(), &point, buffer.data(), buffer.size()) == 1;
}
TER
homomorphicAdd(Slice const& a, Slice const& b, Buffer& out)
std::optional<Buffer>
homomorphicAdd(Slice const& a, Slice const& b)
{
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
return tecINTERNAL;
return std::nullopt;
secp256k1_pubkey aC1;
secp256k1_pubkey aC2;
secp256k1_pubkey bC1;
secp256k1_pubkey bC2;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!makeEcPair(a, aC1, aC2) || !makeEcPair(b, bC1, bC2))
return tecINTERNAL;
if (!pairA || !pairB)
return std::nullopt;
secp256k1_pubkey sumC1;
secp256k1_pubkey sumC2;
EcPair sum;
if (auto res = secp256k1_elgamal_add(
secp256k1Context(), &sum.c1, &sum.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
if (secp256k1_elgamal_add(secp256k1Context(), &sumC1, &sumC2, &aC1, &aC2, &bC1, &bC2) != 1)
return tecINTERNAL;
if (!serializeEcPair(sumC1, sumC2, out))
return tecINTERNAL;
return tesSUCCESS;
return serializeEcPair(sum);
}
TER
homomorphicSubtract(Slice const& a, Slice const& b, Buffer& out)
std::optional<Buffer>
homomorphicSubtract(Slice const& a, Slice const& b)
{
if (a.length() != ecGamalEncryptedTotalLength || b.length() != ecGamalEncryptedTotalLength)
return tecINTERNAL;
return std::nullopt;
secp256k1_pubkey aC1;
secp256k1_pubkey aC2;
secp256k1_pubkey bC1;
secp256k1_pubkey bC2;
auto const pairA = makeEcPair(a);
auto const pairB = makeEcPair(b);
if (!makeEcPair(a, aC1, aC2) || !makeEcPair(b, bC1, bC2))
return tecINTERNAL;
if (!pairA || !pairB)
return std::nullopt;
secp256k1_pubkey diffC1;
secp256k1_pubkey diffC2;
EcPair diff;
if (auto res = secp256k1_elgamal_subtract(
secp256k1Context(), &diff.c1, &diff.c2, &pairA->c1, &pairA->c2, &pairB->c1, &pairB->c2);
res != 1)
{
return std::nullopt;
}
if (secp256k1_elgamal_subtract(secp256k1Context(), &diffC1, &diffC2, &aC1, &aC2, &bC1, &bC2) !=
1)
return tecINTERNAL;
if (!serializeEcPair(diffC1, diffC2, out))
return tecINTERNAL;
return tesSUCCESS;
return serializeEcPair(diff);
}
Buffer
@@ -223,20 +219,23 @@ encryptAmount(uint64_t const amt, Slice const& pubKeySlice, Slice const& blindin
if (pubKeySlice.size() != ecPubKeyLength)
return std::nullopt;
secp256k1_pubkey c1, c2, pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
EcPair pair;
secp256k1_pubkey pubKey;
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return std::nullopt;
}
if (!secp256k1_elgamal_encrypt(
secp256k1Context(), &c1, &c2, &pubKey, amt, blindingFactor.data()))
if (auto res = secp256k1_elgamal_encrypt(
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, amt, blindingFactor.data());
res != 1)
{
return std::nullopt;
}
Buffer buf(ecGamalEncryptedTotalLength);
if (!serializeEcPair(c1, c2, buf))
return std::nullopt;
return buf;
return serializeEcPair(pair);
}
std::optional<Buffer>
@@ -245,20 +244,23 @@ encryptCanonicalZeroAmount(Slice const& pubKeySlice, AccountID const& account, M
if (pubKeySlice.size() != ecPubKeyLength)
return std::nullopt; // LCOV_EXCL_LINE
secp256k1_pubkey c1, c2, pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
EcPair pair;
secp256k1_pubkey pubKey;
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
if (!generate_canonical_encrypted_zero(
secp256k1Context(), &c1, &c2, &pubKey, account.data(), mptId.data()))
if (auto res = generate_canonical_encrypted_zero(
secp256k1Context(), &pair.c1, &pair.c2, &pubKey, account.data(), mptId.data());
res != 1)
{
return std::nullopt; // LCOV_EXCL_LINE
}
Buffer buf(ecGamalEncryptedTotalLength);
if (!serializeEcPair(c1, c2, buf))
return std::nullopt; // LCOV_EXCL_LINE
return buf;
return serializeEcPair(pair);
}
TER
@@ -271,20 +273,26 @@ verifySchnorrProof(Slice const& pubKeySlice, Slice const& proofSlice, uint256 co
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (secp256k1_mpt_pok_sk_verify(
secp256k1Context(), proofSlice.data(), &pubKey, contextHash.data()) != 1)
if (auto res = secp256k1_mpt_pok_sk_verify(
secp256k1Context(), proofSlice.data(), &pubKey, contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifyElGamalEncryption(
std::uint64_t const amount,
uint64_t const amount,
Slice const& blindingFactor,
Slice const& pubKeySlice,
Slice const& ciphertext)
@@ -294,24 +302,30 @@ verifyElGamalEncryption(
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
auto const pair = makeEcPair(ciphertext);
if (!pair)
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey c1, c2;
if (!makeEcPair(ciphertext, c1, c2))
return tecINTERNAL; // LCOV_EXCL_LINE
if (secp256k1_elgamal_verify_encryption(
secp256k1Context(), &c1, &c2, &pubKey, amount, blindingFactor.data()) != 1)
if (auto res = secp256k1_elgamal_verify_encryption(
secp256k1Context(), &pair->c1, &pair->c2, &pubKey, amount, blindingFactor.data());
res != 1)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
TER
verifyRevealedAmount(
std::uint64_t const amount,
uint64_t const amount,
Slice const& blindingFactor,
ConfidentialRecipient const& holder,
ConfidentialRecipient const& issuer,
@@ -376,9 +390,12 @@ verifyMultiCiphertextEqualityProof(
// Parse Shared C1 from the first recipient only
if (i == 0)
{
if (!secp256k1_ec_pubkey_parse(
ctx, &c1, recipient.encryptedAmount.data(), ecGamalEncryptedLength))
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &c1, recipient.encryptedAmount.data(), ecGamalEncryptedLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
}
else
{
@@ -392,25 +409,30 @@ verifyMultiCiphertextEqualityProof(
}
}
if (secp256k1_ec_pubkey_parse(
if (auto res = secp256k1_ec_pubkey_parse(
ctx,
&c2_vec[i],
recipient.encryptedAmount.data() + ecGamalEncryptedLength,
ecGamalEncryptedLength) != 1)
ecGamalEncryptedLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (secp256k1_ec_pubkey_parse(
ctx, &pk_vec[i], recipient.publicKey.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &pk_vec[i], recipient.publicKey.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
}
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)
if (auto res = secp256k1_mpt_verify_equality_shared_r(
ctx, proof.data(), nRecipients, &c1, c2_vec.data(), pk_vec.data(), contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
@@ -427,20 +449,30 @@ verifyClawbackEqualityProof(
proof.size() != ecEqualityProofLength)
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey c1, c2;
if (!makeEcPair(ciphertext, c1, c2))
auto const pair = makeEcPair(ciphertext);
if (!pair)
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Note: c2, c1 order - the proof is generated with c2 first (the encrypted
// message component) because the equality proof structure expects the
// message-containing term before the blinding term.
if (secp256k1_equality_plaintext_verify(
secp256k1Context(), proof.data(), &pubKey, &c2, &c1, amount, contextHash.data()) != 1)
if (auto res = secp256k1_equality_plaintext_verify(
secp256k1Context(),
proof.data(),
&pubKey,
&pair->c2,
&pair->c1,
amount,
contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
@@ -487,8 +519,8 @@ verifyAmountPcmLinkage(
if (proof.length() != ecPedersenProofLength)
return tecINTERNAL;
secp256k1_pubkey c1, c2;
if (!makeEcPair(encAmt, c1, c2))
auto const pair = makeEcPair(encAmt);
if (!pair)
return tecINTERNAL; // LCOV_EXCL_LINE
if (pubKeySlice.size() != ecPubKeyLength)
@@ -498,17 +530,30 @@ verifyAmountPcmLinkage(
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
secp256k1_pubkey pcm;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pcm, pcmSlice.data(), ecPedersenCommitmentLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pcm, pcmSlice.data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (secp256k1_elgamal_pedersen_link_verify(
secp256k1Context(), proof.data(), &c1, &c2, &pubKey, &pcm, contextHash.data()) != 1)
if (auto res = secp256k1_elgamal_pedersen_link_verify(
secp256k1Context(),
proof.data(),
&pair->c1,
&pair->c2,
&pubKey,
&pcm,
contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
@@ -527,10 +572,8 @@ verifyBalancePcmLinkage(
if (proof.length() != ecPedersenProofLength)
return tecINTERNAL;
secp256k1_pubkey c1;
secp256k1_pubkey c2;
if (!makeEcPair(encAmt, c1, c2))
auto const pair = makeEcPair(encAmt);
if (!pair)
return tecINTERNAL; // LCOV_EXCL_LINE
if (pubKeySlice.size() != ecPubKeyLength)
@@ -540,19 +583,32 @@ verifyBalancePcmLinkage(
return tecINTERNAL; // LCOV_EXCL_LINE
secp256k1_pubkey pubKey;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pubKey, pubKeySlice.data(), ecPubKeyLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
secp256k1_pubkey pcm;
if (secp256k1_ec_pubkey_parse(
secp256k1Context(), &pcm, pcmSlice.data(), ecPedersenCommitmentLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
secp256k1Context(), &pcm, pcmSlice.data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Note: c2, c1 order - the linkage proof expects the message-containing
// component (c2 = m*G + r*Pk) before the blinding component (c1 = r*G).
if (secp256k1_elgamal_pedersen_link_verify(
secp256k1Context(), proof.data(), &pubKey, &c2, &c1, &pcm, contextHash.data()) != 1)
if (auto res = secp256k1_elgamal_pedersen_link_verify(
secp256k1Context(),
proof.data(),
&pubKey,
&pair->c2,
&pair->c1,
&pcm,
contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
@@ -586,12 +642,12 @@ verifyAggregatedBulletproof(
if (compressedCommitments[i].size() != ecPedersenCommitmentLength)
return tecINTERNAL; // LCOV_EXCL_LINE
if (secp256k1_ec_pubkey_parse(
ctx,
&commitments[i],
compressedCommitments[i].data(),
ecPedersenCommitmentLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &commitments[i], compressedCommitments[i].data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
}
// 3. Prepare Generator Vectors (G_vec, H_vec)
@@ -601,37 +657,42 @@ verifyAggregatedBulletproof(
std::vector<secp256k1_pubkey> H_vec(n);
// Retrieve deterministic generators "G" and "H"
if (secp256k1_mpt_get_generator_vector(ctx, G_vec.data(), n, (unsigned char const*)"G", 1) != 1)
if (auto res =
secp256k1_mpt_get_generator_vector(ctx, G_vec.data(), n, (unsigned char const*)"G", 1);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
if (secp256k1_mpt_get_generator_vector(ctx, H_vec.data(), n, (unsigned char const*)"H", 1) != 1)
if (auto res =
secp256k1_mpt_get_generator_vector(ctx, H_vec.data(), n, (unsigned char const*)"H", 1);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// 4. Prepare Base Generator (pk_base / H)
secp256k1_pubkey pk_base;
if (secp256k1_mpt_get_h_generator(ctx, &pk_base) != 1)
if (auto res = secp256k1_mpt_get_h_generator(ctx, &pk_base); res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// 5. Verify the Proof
int const result = secp256k1_bulletproof_verify_agg(
ctx,
G_vec.data(),
H_vec.data(),
reinterpret_cast<unsigned char const*>(proof.data()),
proof.size(),
commitments.data(),
m,
&pk_base,
contextHash.data());
if (result != 1)
if (auto res = secp256k1_bulletproof_verify_agg(
ctx,
G_vec.data(),
H_vec.data(),
reinterpret_cast<unsigned char const*>(proof.data()),
proof.size(),
commitments.data(),
m,
&pk_base,
contextHash.data());
res != 1)
{
return tecBAD_PROOF;
}
return tesSUCCESS;
}
@@ -646,37 +707,50 @@ computeSendRemainder(Slice const& balanceCommitment, Slice const& amountCommitme
auto const ctx = secp256k1Context();
secp256k1_pubkey pcBalance;
if (secp256k1_ec_pubkey_parse(
ctx, &pcBalance, balanceCommitment.data(), ecPedersenCommitmentLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &pcBalance, balanceCommitment.data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL;
}
secp256k1_pubkey pcAmount;
if (secp256k1_ec_pubkey_parse(
ctx, &pcAmount, amountCommitment.data(), ecPedersenCommitmentLength) != 1)
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &pcAmount, amountCommitment.data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL;
}
// Negate PC_amount point to get -PC_amount
if (!secp256k1_ec_pubkey_negate(ctx, &pcAmount))
if (auto res = secp256k1_ec_pubkey_negate(ctx, &pcAmount); res != 1)
{
return tecINTERNAL;
}
// Compute pcRem = pcBalance + (-pcAmount)
secp256k1_pubkey const* summands[2] = {&pcBalance, &pcAmount};
secp256k1_pubkey pcRem;
if (!secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2))
if (auto res = secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2); res != 1)
{
return tecINTERNAL;
}
// Serialize result to compressed format
out.alloc(ecPedersenCommitmentLength);
size_t outLen = ecPedersenCommitmentLength;
if (secp256k1_ec_pubkey_serialize(ctx, out.data(), &outLen, &pcRem, SECP256K1_EC_COMPRESSED) !=
1)
if (auto res = secp256k1_ec_pubkey_serialize(
ctx, out.data(), &outLen, &pcRem, SECP256K1_EC_COMPRESSED);
res != 1)
{
return tecINTERNAL;
}
return tesSUCCESS;
}
TER
computeConvertBackRemainder(Slice const& commitment, std::uint64_t amount, Buffer& out)
computeConvertBackRemainder(Slice const& commitment, uint64_t amount, Buffer& out)
{
if (commitment.size() != ecPedersenCommitmentLength || amount == 0)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -685,37 +759,48 @@ computeConvertBackRemainder(Slice const& commitment, std::uint64_t amount, Buffe
// Parse commitment from compressed format
secp256k1_pubkey pcBalance;
if (secp256k1_ec_pubkey_parse(ctx, &pcBalance, commitment.data(), ecPedersenCommitmentLength) !=
1)
if (auto res = secp256k1_ec_pubkey_parse(
ctx, &pcBalance, commitment.data(), ecPedersenCommitmentLength);
res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Convert amount to 32-byte big-endian scalar
unsigned char mScalar[32] = {0};
std::uint64_t amountBigEndian = boost::endian::native_to_big(amount);
uint64_t amountBigEndian = boost::endian::native_to_big(amount);
std::memcpy(&mScalar[24], &amountBigEndian, sizeof(amountBigEndian));
// Compute mG = amount * G
secp256k1_pubkey mG;
if (!secp256k1_ec_pubkey_create(ctx, &mG, mScalar))
if (auto res = secp256k1_ec_pubkey_create(ctx, &mG, mScalar); res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Negate mG to get -mG
if (!secp256k1_ec_pubkey_negate(ctx, &mG))
if (auto res = secp256k1_ec_pubkey_negate(ctx, &mG); res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Compute pcRem = pcBalance + (-mG)
secp256k1_pubkey const* summands[2] = {&pcBalance, &mG};
secp256k1_pubkey pcRem;
if (!secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2))
if (auto res = secp256k1_ec_pubkey_combine(ctx, &pcRem, summands, 2); res != 1)
{
return tecINTERNAL; // LCOV_EXCL_LINE
}
// Serialize result to compressed format
out.alloc(ecPedersenCommitmentLength);
size_t outLen = ecPedersenCommitmentLength;
if (secp256k1_ec_pubkey_serialize(ctx, out.data(), &outLen, &pcRem, SECP256K1_EC_COMPRESSED) !=
1 ||
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 tesSUCCESS;
}

View File

@@ -117,14 +117,14 @@ ConfidentialMPTClawback::doApply()
if (!encZeroForHolder)
return tecINTERNAL; // LCOV_EXCL_LINE
auto const encZeroForIssuer = encryptCanonicalZeroAmount(issuerPubKey, holder, mptIssuanceID);
auto encZeroForIssuer = encryptCanonicalZeroAmount(issuerPubKey, holder, mptIssuanceID);
if (!encZeroForIssuer)
return tecINTERNAL; // LCOV_EXCL_LINE
// Set holder's confidential balances to encrypted zero
(*sleHolderMPToken)[sfConfidentialBalanceInbox] = *encZeroForHolder;
(*sleHolderMPToken)[sfConfidentialBalanceSpending] = *encZeroForHolder;
(*sleHolderMPToken)[sfIssuerEncryptedBalance] = *encZeroForIssuer;
(*sleHolderMPToken)[sfIssuerEncryptedBalance] = std::move(*encZeroForIssuer);
incrementConfidentialVersion(*sleHolderMPToken);
if (sleHolderMPToken->isFieldPresent(sfAuditorEncryptedBalance))
@@ -136,13 +136,12 @@ ConfidentialMPTClawback::doApply()
Slice const auditorPubKey = (*sleIssuance)[sfAuditorElGamalPublicKey];
auto const encZeroForAuditor =
encryptCanonicalZeroAmount(auditorPubKey, holder, mptIssuanceID);
auto encZeroForAuditor = encryptCanonicalZeroAmount(auditorPubKey, holder, mptIssuanceID);
if (!encZeroForAuditor)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleHolderMPToken)[sfAuditorEncryptedBalance] = *encZeroForAuditor;
(*sleHolderMPToken)[sfAuditorEncryptedBalance] = std::move(*encZeroForAuditor);
}
// Decrease Global Confidential Outstanding Amount

View File

@@ -189,36 +189,30 @@ ConfidentialMPTConvert::doApply()
{
// Case 1: Add to existing inbox balance (holder will merge later)
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter =
homomorphicAdd(holderEc, (*sleMptoken)[sfConfidentialBalanceInbox], sum);
!isTesSuccess(ter))
auto sum = homomorphicAdd(holderEc, (*sleMptoken)[sfConfidentialBalanceInbox]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceInbox] = sum;
(*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*sum);
}
// homomorphically add issuer's encrypted balance
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter =
homomorphicAdd(issuerEc, (*sleMptoken)[sfIssuerEncryptedBalance], sum);
!isTesSuccess(ter))
auto sum = homomorphicAdd(issuerEc, (*sleMptoken)[sfIssuerEncryptedBalance]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfIssuerEncryptedBalance] = sum;
(*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*sum);
}
// homomorphically add auditor's encrypted balance
if (auditorEc)
{
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter =
homomorphicAdd(*auditorEc, (*sleMptoken)[sfAuditorEncryptedBalance], sum);
!isTesSuccess(ter))
auto sum = homomorphicAdd(*auditorEc, (*sleMptoken)[sfAuditorEncryptedBalance]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfAuditorEncryptedBalance] = sum;
(*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*sum);
}
}
else if (
@@ -236,13 +230,13 @@ ConfidentialMPTConvert::doApply()
// Spending balance starts at zero. Must use canonical zero encryption
// (deterministic ciphertext) so the ledger state is reproducible.
auto const zeroBalance = encryptCanonicalZeroAmount(
auto zeroBalance = encryptCanonicalZeroAmount(
(*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
if (!zeroBalance)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = *zeroBalance;
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*zeroBalance);
}
else
{

View File

@@ -256,37 +256,32 @@ ConfidentialMPTConvertBack::doApply()
// homomorphically subtract holder's encrypted balance
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(
(*sleMptoken)[sfConfidentialBalanceSpending],
ctx_.tx[sfHolderEncryptedAmount],
res);
!isTesSuccess(ter))
auto res = homomorphicSubtract(
(*sleMptoken)[sfConfidentialBalanceSpending], ctx_.tx[sfHolderEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = res;
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*res);
}
// homomorphically subtract issuer's encrypted balance
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(
(*sleMptoken)[sfIssuerEncryptedBalance], ctx_.tx[sfIssuerEncryptedAmount], res);
!isTesSuccess(ter))
auto res = homomorphicSubtract(
(*sleMptoken)[sfIssuerEncryptedBalance], ctx_.tx[sfIssuerEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfIssuerEncryptedBalance] = res;
(*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*res);
}
if (auditorEc)
{
Buffer res(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(
(*sleMptoken)[sfAuditorEncryptedBalance], ctx_.tx[sfAuditorEncryptedAmount], res);
!isTesSuccess(ter))
auto res = homomorphicSubtract(
(*sleMptoken)[sfAuditorEncryptedBalance], ctx_.tx[sfAuditorEncryptedAmount]);
if (!res)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfAuditorEncryptedBalance] = res;
(*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*res);
}
incrementConfidentialVersion(*sleMptoken);

View File

@@ -68,25 +68,22 @@ ConfidentialMPTMergeInbox::doApply()
// Merge inbox into spending: spending = spending + inbox
// This allows holder to use received funds. Without merging, incoming
// transfers sit in inbox and cannot be spent or converted back.
Buffer sum(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(
(*sleMptoken)[sfConfidentialBalanceSpending],
(*sleMptoken)[sfConfidentialBalanceInbox],
sum);
!isTesSuccess(ter))
auto sum = homomorphicAdd(
(*sleMptoken)[sfConfidentialBalanceSpending], (*sleMptoken)[sfConfidentialBalanceInbox]);
if (!sum)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceSpending] = sum;
(*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*sum);
// Reset inbox to encrypted zero. Must use canonical zero encryption
// (deterministic ciphertext) so the ledger state is reproducible.
auto const zeroEncryption = encryptCanonicalZeroAmount(
auto zeroEncryption = encryptCanonicalZeroAmount(
(*sleMptoken)[sfHolderElGamalPublicKey], account_, mptIssuanceID);
if (!zeroEncryption)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleMptoken)[sfConfidentialBalanceInbox] = *zeroEncryption;
(*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*zeroEncryption);
incrementConfidentialVersion(*sleMptoken);

View File

@@ -338,74 +338,63 @@ ConfidentialMPTSend::doApply()
// Subtract from sender's spending balance
{
Slice const curSpending = (*sleSenderMPToken)[sfConfidentialBalanceSpending];
Buffer newSpending(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curSpending, senderEc, newSpending);
!isTesSuccess(ter))
auto newSpending = homomorphicSubtract(curSpending, senderEc);
if (!newSpending)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = newSpending;
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = std::move(*newSpending);
}
// Subtract from issuer's balance
{
Slice const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curIssuerEnc, issuerEc, newIssuerEnc);
!isTesSuccess(ter))
auto newIssuerEnc = homomorphicSubtract(curIssuerEnc, issuerEc);
if (!newIssuerEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
}
// Subtract from auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicSubtract(curAuditorEnc, *auditorEc, newAuditorEnc);
!isTesSuccess(ter))
auto newAuditorEnc = homomorphicSubtract(curAuditorEnc, *auditorEc);
if (!newAuditorEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
}
// Add to destination's inbox balance
{
Slice const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
Buffer newInbox(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curInbox, destEc, newInbox); !isTesSuccess(ter))
auto newInbox = homomorphicAdd(curInbox, destEc);
if (!newInbox)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = newInbox;
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = std::move(*newInbox);
}
// Add to issuer's balance
{
Slice const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curIssuerEnc, issuerEc, newIssuerEnc);
!isTesSuccess(ter))
auto newIssuerEnc = homomorphicAdd(curIssuerEnc, issuerEc);
if (!newIssuerEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
}
// Add to auditor's balance if present
if (auditorEc)
{
Slice const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
if (TER const ter = homomorphicAdd(curAuditorEnc, *auditorEc, newAuditorEnc);
!isTesSuccess(ter))
auto newAuditorEnc = homomorphicAdd(curAuditorEnc, *auditorEc);
if (!newAuditorEnc)
return tecINTERNAL; // LCOV_EXCL_LINE
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
}
// increment sender version only; receiver version is not modified by incoming sends

View File

@@ -1629,10 +1629,8 @@ MPTTester::decryptAmount(Account const& account, Buffer const& amt) const
if (amt.size() != ecGamalEncryptedTotalLength)
return std::nullopt;
secp256k1_pubkey c1;
secp256k1_pubkey c2;
if (!makeEcPair(amt, c1, c2))
auto const pair = makeEcPair(amt);
if (!pair)
return std::nullopt;
auto const privKey = getPrivKey(account);
@@ -1640,7 +1638,8 @@ MPTTester::decryptAmount(Account const& account, Buffer const& amt) const
return std::nullopt;
uint64_t decryptedAmt;
if (!secp256k1_elgamal_decrypt(secp256k1Context(), &decryptedAmt, &c1, &c2, privKey->data()))
if (!secp256k1_elgamal_decrypt(
secp256k1Context(), &decryptedAmt, &pair->c1, &pair->c2, privKey->data()))
{
return std::nullopt;
}