mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-27 00:50:45 +00:00
Support ConfidentialSend equality proof (#6274)
* Support ConfidentialSend equality proof * resolve conflicts * Add version check in send
This commit is contained in:
@@ -16,15 +16,38 @@
|
||||
#include <secp256k1.h>
|
||||
|
||||
namespace ripple {
|
||||
// Helper struct to bundle the ElGamal Public Key and the associated Ciphertext
|
||||
struct ConfidentialRecipient
|
||||
{
|
||||
Slice const publicKey;
|
||||
Slice const encryptedAmount;
|
||||
};
|
||||
|
||||
inline void
|
||||
incrementConfidentialVersion(STObject& mptoken)
|
||||
{
|
||||
// Retrieve current version and increment.
|
||||
// Unsigned integer overflow is defined behavior in C++ (wraps to 0),
|
||||
// which is acceptable here.
|
||||
mptoken[sfConfidentialBalanceVersion] =
|
||||
mptoken[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
|
||||
}
|
||||
|
||||
void
|
||||
addCommonZKPFields(
|
||||
Serializer& s,
|
||||
std::uint16_t txType,
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID);
|
||||
|
||||
uint256
|
||||
getSendContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
std::uint64_t amount);
|
||||
AccountID const& destination,
|
||||
std::uint32_t version);
|
||||
|
||||
uint256
|
||||
getClawbackContextHash(
|
||||
@@ -101,6 +124,33 @@ verifyElGamalEncryption(
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& ciphertext);
|
||||
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object);
|
||||
|
||||
TER
|
||||
verifyRevealedAmount(
|
||||
std::uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
ConfidentialRecipient const& holder,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor);
|
||||
|
||||
constexpr std::size_t
|
||||
getConfidentialRecipientCount(bool hasAuditor)
|
||||
{
|
||||
return hasAuditor ? 4 : 3;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
getMultiCiphertextEqualityProofSize(std::size_t nRecipients);
|
||||
|
||||
TER
|
||||
verifyMultiCiphertextEqualityProof(
|
||||
Slice const& proof,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash);
|
||||
|
||||
TER
|
||||
verifyClawbackEqualityProof(
|
||||
uint64_t const amount,
|
||||
@@ -109,24 +159,6 @@ verifyClawbackEqualityProof(
|
||||
Slice const& ciphertext,
|
||||
uint256 const& contextHash);
|
||||
|
||||
NotTEC
|
||||
checkEncryptedAmountFormat(STObject const& object);
|
||||
|
||||
// Helper struct to bundle the ElGamal Public Key and the associated Ciphertext
|
||||
struct EncryptedAmountInfo
|
||||
{
|
||||
Slice const publicKey;
|
||||
Slice const encryptedAmount;
|
||||
};
|
||||
|
||||
TER
|
||||
verifyRevealedAmount(
|
||||
std::uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
EncryptedAmountInfo const& holder,
|
||||
EncryptedAmountInfo const& issuer,
|
||||
std::optional<EncryptedAmountInfo> const& auditor);
|
||||
|
||||
// generates a 32 byte randomness factor to be used in encryption and proofs
|
||||
Buffer
|
||||
generateBlindingFactor();
|
||||
@@ -392,6 +424,46 @@ secp256k1_mpt_pedersen_commit(
|
||||
uint64_t amount,
|
||||
unsigned char const* blinding_factor_rho /* 32 bytes */
|
||||
);
|
||||
|
||||
// Multi-proof for same plaintexts
|
||||
void
|
||||
build_hash_input(
|
||||
unsigned char* hash_out, // Output: 32-byte hash
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
secp256k1_pubkey const* T_m,
|
||||
secp256k1_pubkey const* T_rG,
|
||||
secp256k1_pubkey const* T_rP,
|
||||
unsigned char const* tx_id);
|
||||
|
||||
size_t
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(size_t n);
|
||||
|
||||
int
|
||||
secp256k1_mpt_prove_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof_out,
|
||||
size_t* proof_len,
|
||||
uint64_t amount_m,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* r_array,
|
||||
unsigned char const* tx_id);
|
||||
|
||||
int
|
||||
secp256k1_mpt_verify_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof,
|
||||
size_t proof_len,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* tx_id);
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -318,6 +318,9 @@ std::size_t constexpr ecBlindingFactorLength = 32;
|
||||
/** Length of Schnorr ZKProof for public key registration */
|
||||
std::size_t constexpr ecSchnorrProofLength = 65;
|
||||
|
||||
/** Length of ElGamal ciphertext equality proof */
|
||||
std::size_t constexpr ecCiphertextEqualityProofLength = 261;
|
||||
|
||||
/** Length of ElGamal Pedersen linkage proof */
|
||||
std::size_t constexpr ecPedersenProofLength = 195;
|
||||
|
||||
|
||||
@@ -11,14 +11,29 @@ addCommonZKPFields(
|
||||
std::uint16_t txType,
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
std::uint64_t amount)
|
||||
uint192 const& issuanceID)
|
||||
{
|
||||
s.add16(txType);
|
||||
s.addBitString(account);
|
||||
s.add32(sequence);
|
||||
s.addBitString(issuanceID);
|
||||
s.add64(amount);
|
||||
}
|
||||
|
||||
uint256
|
||||
getSendContextHash(
|
||||
AccountID const& account,
|
||||
std::uint32_t sequence,
|
||||
uint192 const& issuanceID,
|
||||
AccountID const& destination,
|
||||
std::uint32_t version)
|
||||
{
|
||||
Serializer s;
|
||||
addCommonZKPFields(s, ttCONFIDENTIAL_SEND, account, sequence, issuanceID);
|
||||
|
||||
s.addBitString(destination);
|
||||
s.addInteger(version);
|
||||
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
|
||||
uint256
|
||||
@@ -31,8 +46,9 @@ getClawbackContextHash(
|
||||
{
|
||||
Serializer s;
|
||||
addCommonZKPFields(
|
||||
s, ttCONFIDENTIAL_CLAWBACK, account, sequence, issuanceID, amount);
|
||||
s, ttCONFIDENTIAL_CLAWBACK, account, sequence, issuanceID);
|
||||
|
||||
s.add64(amount);
|
||||
s.addBitString(holder);
|
||||
|
||||
return s.getSHA512Half();
|
||||
@@ -47,7 +63,9 @@ getConvertContextHash(
|
||||
{
|
||||
Serializer s;
|
||||
addCommonZKPFields(
|
||||
s, ttCONFIDENTIAL_CONVERT, account, sequence, issuanceID, amount);
|
||||
s, ttCONFIDENTIAL_CONVERT, account, sequence, issuanceID);
|
||||
|
||||
s.add64(amount);
|
||||
|
||||
return s.getSHA512Half();
|
||||
}
|
||||
@@ -62,8 +80,9 @@ getConvertBackContextHash(
|
||||
{
|
||||
Serializer s;
|
||||
addCommonZKPFields(
|
||||
s, ttCONFIDENTIAL_CONVERT_BACK, account, sequence, issuanceID, amount);
|
||||
s, ttCONFIDENTIAL_CONVERT_BACK, account, sequence, issuanceID);
|
||||
|
||||
s.add64(amount);
|
||||
s.addInteger(version);
|
||||
|
||||
return s.getSHA512Half();
|
||||
@@ -314,9 +333,9 @@ TER
|
||||
verifyRevealedAmount(
|
||||
std::uint64_t const amount,
|
||||
Slice const& blindingFactor,
|
||||
EncryptedAmountInfo const& holder,
|
||||
EncryptedAmountInfo const& issuer,
|
||||
std::optional<EncryptedAmountInfo> const& auditor)
|
||||
ConfidentialRecipient const& holder,
|
||||
ConfidentialRecipient const& issuer,
|
||||
std::optional<ConfidentialRecipient> const& auditor)
|
||||
{
|
||||
if (auto const res = verifyElGamalEncryption(
|
||||
amount, blindingFactor, holder.publicKey, holder.encryptedAmount);
|
||||
@@ -348,6 +367,63 @@ 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,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash)
|
||||
{
|
||||
if (recipients.size() != nRecipients)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (proof.size() != getMultiCiphertextEqualityProofSize(nRecipients))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::vector<secp256k1_pubkey> r(nRecipients);
|
||||
std::vector<secp256k1_pubkey> s(nRecipients);
|
||||
std::vector<secp256k1_pubkey> pk(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]))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
if (recipient.publicKey.size() != ecPubKeyLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::memcpy(pk[i].data, recipient.publicKey.data(), ecPubKeyLength);
|
||||
}
|
||||
|
||||
int const result = secp256k1_mpt_verify_same_plaintext_multi(
|
||||
secp256k1Context(),
|
||||
proof.data(),
|
||||
proof.size(),
|
||||
nRecipients,
|
||||
r.data(),
|
||||
s.data(),
|
||||
pk.data(),
|
||||
contextHash.data());
|
||||
|
||||
if (result != 1)
|
||||
return tecBAD_PROOF;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifyClawbackEqualityProof(
|
||||
uint64_t const amount,
|
||||
@@ -1532,4 +1608,309 @@ secp256k1_mpt_pedersen_commit(
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Multi-proof for same plaintexts
|
||||
/**
|
||||
* Builds the challenge hash: Domain || PublicInputs || Commitments || TxID
|
||||
*/
|
||||
void
|
||||
build_hash_input(
|
||||
unsigned char* hash_out, // Output: 32-byte hash
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
secp256k1_pubkey const* T_m,
|
||||
secp256k1_pubkey const* T_rG,
|
||||
secp256k1_pubkey const* T_rP,
|
||||
unsigned char const* tx_id)
|
||||
{
|
||||
SHA256_CTX sha_ctx;
|
||||
char const* domain = "MPT_POK_SAME_PLAINTEXT_PROOF";
|
||||
unsigned char buf[33];
|
||||
size_t len = 33;
|
||||
size_t i;
|
||||
secp256k1_context* ser_ctx =
|
||||
secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
|
||||
SHA256_Init(&sha_ctx);
|
||||
SHA256_Update(&sha_ctx, domain, strlen(domain));
|
||||
|
||||
// Public Inputs (R, S, Pk for each ciphertext)
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, &R[i], SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, &S[i], SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, &Pk[i], SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
}
|
||||
|
||||
// Commitments
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, T_m, SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, &T_rG[i], SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ser_ctx, buf, &len, &T_rP[i], SECP256K1_EC_COMPRESSED);
|
||||
SHA256_Update(&sha_ctx, buf, 33);
|
||||
}
|
||||
|
||||
SHA256_Update(&sha_ctx, tx_id, 32);
|
||||
SHA256_Final(hash_out, &sha_ctx);
|
||||
secp256k1_context_destroy(ser_ctx);
|
||||
}
|
||||
|
||||
/* --- Public API --- */
|
||||
|
||||
size_t
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(size_t n)
|
||||
{
|
||||
// (1 point T_m + 2*N points T_r) * 33 + (1 scalar s_m + N scalars s_r) * 32
|
||||
return ((1 + 2 * n) * 33) + ((1 + n) * 32);
|
||||
}
|
||||
|
||||
int
|
||||
secp256k1_mpt_prove_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char* proof_out,
|
||||
size_t* proof_len,
|
||||
uint64_t amount_m,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* r_array,
|
||||
unsigned char const* tx_id)
|
||||
{
|
||||
size_t required_len = secp256k1_mpt_prove_same_plaintext_multi_size(n);
|
||||
if (*proof_len < required_len)
|
||||
{
|
||||
*proof_len = required_len;
|
||||
return 0;
|
||||
}
|
||||
*proof_len = required_len;
|
||||
|
||||
unsigned char k_m[32];
|
||||
unsigned char k_r[n][32];
|
||||
secp256k1_pubkey T_m;
|
||||
secp256k1_pubkey T_rG[n];
|
||||
secp256k1_pubkey T_rP[n];
|
||||
unsigned char e[32];
|
||||
unsigned char s_m[32];
|
||||
unsigned char s_r[n][32];
|
||||
|
||||
size_t i;
|
||||
int ok = 1;
|
||||
|
||||
/* 1. Generate Randomness & Commitments */
|
||||
if (!generate_random_scalar(ctx, k_m))
|
||||
return 0;
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &T_m, k_m))
|
||||
ok = 0;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (!generate_random_scalar(ctx, k_r[i]))
|
||||
ok = 0;
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &T_rG[i], k_r[i]))
|
||||
ok = 0;
|
||||
|
||||
T_rP[i] = Pk[i];
|
||||
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &T_rP[i], k_r[i]))
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
return 0;
|
||||
|
||||
/* 2. Compute Challenge e */
|
||||
build_hash_input(e, n, R, S, Pk, &T_m, T_rG, T_rP, tx_id);
|
||||
// Ensure e is valid
|
||||
if (!secp256k1_ec_seckey_verify(ctx, e))
|
||||
return 0;
|
||||
|
||||
/* 3. Compute Responses */
|
||||
/* s_m = k_m + e * m */
|
||||
unsigned char m_scalar[32] = {0};
|
||||
for (i = 0; i < 8; ++i)
|
||||
m_scalar[31 - i] = (amount_m >> (i * 8)) & 0xFF;
|
||||
|
||||
memcpy(s_m, k_m, 32);
|
||||
unsigned char term[32];
|
||||
memcpy(term, m_scalar, 32);
|
||||
if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e))
|
||||
return 0;
|
||||
if (!secp256k1_ec_seckey_tweak_add(ctx, s_m, term))
|
||||
return 0;
|
||||
|
||||
/* s_ri = k_ri + e * ri */
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
memcpy(s_r[i], k_r[i], 32);
|
||||
memcpy(term, &r_array[i * 32], 32); // Extract r_i from flat array
|
||||
if (!secp256k1_ec_seckey_tweak_mul(ctx, term, e))
|
||||
return 0;
|
||||
if (!secp256k1_ec_seckey_tweak_add(ctx, s_r[i], term))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 4. Serialize Proof */
|
||||
size_t offset = 0;
|
||||
size_t len = 33;
|
||||
|
||||
// Points
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, proof_out + offset, &len, &T_m, SECP256K1_EC_COMPRESSED);
|
||||
offset += 33;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, proof_out + offset, &len, &T_rG[i], SECP256K1_EC_COMPRESSED);
|
||||
offset += 33;
|
||||
}
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, proof_out + offset, &len, &T_rP[i], SECP256K1_EC_COMPRESSED);
|
||||
offset += 33;
|
||||
}
|
||||
|
||||
// Scalars
|
||||
memcpy(proof_out + offset, s_m, 32);
|
||||
offset += 32;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
memcpy(proof_out + offset, s_r[i], 32);
|
||||
offset += 32;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
secp256k1_mpt_verify_same_plaintext_multi(
|
||||
secp256k1_context const* ctx,
|
||||
unsigned char const* proof,
|
||||
size_t proof_len,
|
||||
size_t n,
|
||||
secp256k1_pubkey const* R,
|
||||
secp256k1_pubkey const* S,
|
||||
secp256k1_pubkey const* Pk,
|
||||
unsigned char const* tx_id)
|
||||
{
|
||||
if (proof_len != secp256k1_mpt_prove_same_plaintext_multi_size(n))
|
||||
return 0;
|
||||
|
||||
/* Deserialize */
|
||||
size_t offset = 0;
|
||||
secp256k1_pubkey T_m;
|
||||
secp256k1_pubkey T_rG[n];
|
||||
secp256k1_pubkey T_rP[n];
|
||||
unsigned char s_m[32];
|
||||
unsigned char s_r[n][32];
|
||||
size_t i;
|
||||
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &T_m, proof + offset, 33))
|
||||
return 0;
|
||||
offset += 33;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &T_rG[i], proof + offset, 33))
|
||||
return 0;
|
||||
offset += 33;
|
||||
}
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
if (!secp256k1_ec_pubkey_parse(ctx, &T_rP[i], proof + offset, 33))
|
||||
return 0;
|
||||
offset += 33;
|
||||
}
|
||||
|
||||
memcpy(s_m, proof + offset, 32);
|
||||
offset += 32;
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
memcpy(s_r[i], proof + offset, 32);
|
||||
offset += 32;
|
||||
}
|
||||
|
||||
/* Recompute Challenge */
|
||||
unsigned char e[32];
|
||||
build_hash_input(e, n, R, S, Pk, &T_m, T_rG, T_rP, tx_id);
|
||||
|
||||
/* Verify Equations */
|
||||
secp256k1_pubkey lhs, rhs, term, SmG;
|
||||
secp256k1_pubkey const* add_pt[3];
|
||||
unsigned char b1[33], b2[33];
|
||||
size_t len;
|
||||
|
||||
// Precompute s_m * G
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &SmG, s_m))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
{
|
||||
/* Check 1: s_ri * G == T_ri_G + e * R_i */
|
||||
if (!secp256k1_ec_pubkey_create(ctx, &lhs, s_r[i]))
|
||||
return 0;
|
||||
|
||||
term = R[i];
|
||||
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e))
|
||||
return 0;
|
||||
add_pt[0] = &T_rG[i];
|
||||
add_pt[1] = &term;
|
||||
if (!secp256k1_ec_pubkey_combine(ctx, &rhs, add_pt, 2))
|
||||
return 0;
|
||||
|
||||
len = 33;
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, b1, &len, &lhs, SECP256K1_EC_COMPRESSED);
|
||||
len = 33;
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, b2, &len, &rhs, SECP256K1_EC_COMPRESSED);
|
||||
if (memcmp(b1, b2, 33) != 0)
|
||||
return 0;
|
||||
|
||||
/* Check 2: s_m * G + s_ri * P_i == T_m + T_ri_P + e * S_i */
|
||||
/* LHS = SmG + s_r[i] * Pk[i] */
|
||||
term = Pk[i];
|
||||
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, s_r[i]))
|
||||
return 0;
|
||||
add_pt[0] = &SmG;
|
||||
add_pt[1] = &term;
|
||||
if (!secp256k1_ec_pubkey_combine(ctx, &lhs, add_pt, 2))
|
||||
return 0;
|
||||
|
||||
/* RHS = T_m + T_rP[i] + e * S[i] */
|
||||
term = S[i];
|
||||
if (!secp256k1_ec_pubkey_tweak_mul(ctx, &term, e))
|
||||
return 0;
|
||||
add_pt[0] = &T_m;
|
||||
add_pt[1] = &T_rP[i];
|
||||
add_pt[2] = &term;
|
||||
if (!secp256k1_ec_pubkey_combine(ctx, &rhs, add_pt, 3))
|
||||
return 0;
|
||||
|
||||
len = 33;
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, b1, &len, &lhs, SECP256K1_EC_COMPRESSED);
|
||||
len = 33;
|
||||
secp256k1_ec_pubkey_serialize(
|
||||
ctx, b2, &len, &rhs, SECP256K1_EC_COMPRESSED);
|
||||
if (memcmp(b1, b2, 33) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -9,14 +9,44 @@ namespace ripple {
|
||||
|
||||
class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{
|
||||
// A 66-byte array of random unsigned char values
|
||||
constexpr static unsigned char badCiphertext[ecGamalEncryptedTotalLength] =
|
||||
{0x3E, 0x9A, 0x0F, 0x7C, 0x51, 0xD8, 0x22, 0x8B, 0x6E, 0x14, 0xC9,
|
||||
0xF5, 0x4D, 0x6A, 0x03, 0x81, 0x77, 0x2B, 0xEE, 0x9F, 0x10, 0xC2,
|
||||
0x57, 0x3D, 0x88, 0x65, 0x0C, 0xAB, 0xF1, 0x4E, 0x19, 0x96, 0x2A,
|
||||
0x73, 0xDC, 0x44, 0xB8, 0x5F, 0x01, 0xEA, 0x87, 0x36, 0x60, 0xCE,
|
||||
0x92, 0x25, 0x7D, 0x5B, 0xC0, 0x1E, 0x48, 0xF9, 0x84, 0x33, 0x67,
|
||||
0xAD, 0x0B, 0xE3, 0x91, 0x50, 0xDA, 0x2F, 0x75, 0xC6, 0xBD, 0x42};
|
||||
// Get a bad ciphertext with valid structure but cryptographic invalid for
|
||||
// testing purposes. For preflight test purposes.
|
||||
static Buffer const&
|
||||
getBadCiphertext()
|
||||
{
|
||||
static Buffer const badCiphertext = []() {
|
||||
Buffer buf(ecGamalEncryptedTotalLength);
|
||||
std::memset(buf.data(), 0xFF, ecGamalEncryptedTotalLength);
|
||||
|
||||
buf.data()[0] = 0x02;
|
||||
buf.data()[ecGamalEncryptedLength] = 0x02;
|
||||
return buf;
|
||||
}();
|
||||
|
||||
return badCiphertext;
|
||||
}
|
||||
|
||||
// Get a trivial buffer that is structurally and mathematically valid, but
|
||||
// contains invalid data that does not match the ledger state. For preclaim
|
||||
// test purposes.
|
||||
static Buffer const&
|
||||
getTrivialCiphertext()
|
||||
{
|
||||
static Buffer const trivialCiphertext = []() {
|
||||
Buffer buf(ecGamalEncryptedTotalLength);
|
||||
std::memset(buf.data(), 0, ecGamalEncryptedTotalLength);
|
||||
|
||||
buf.data()[0] = 0x02;
|
||||
buf.data()[ecGamalEncryptedLength] = 0x02;
|
||||
|
||||
buf.data()[ecGamalEncryptedLength - 1] = 0x01;
|
||||
buf.data()[ecGamalEncryptedTotalLength - 1] = 0x01;
|
||||
|
||||
return buf;
|
||||
}();
|
||||
|
||||
return trivialCiphertext;
|
||||
}
|
||||
|
||||
void
|
||||
testConvert(FeatureBitset features)
|
||||
@@ -203,16 +233,14 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.amt = 1,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.holderEncryptedAmt =
|
||||
Buffer{badCiphertext, ecGamalEncryptedTotalLength},
|
||||
.holderEncryptedAmt = getBadCiphertext(),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
mptAlice.convert(
|
||||
{.account = bob,
|
||||
.amt = 1,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer{badCiphertext, ecGamalEncryptedTotalLength},
|
||||
.issuerEncryptedAmt = getBadCiphertext(),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
// invalid pub key
|
||||
@@ -845,7 +873,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
@@ -865,41 +892,18 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.convert(
|
||||
{.account = bob,
|
||||
.amt = 60,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
BEAST_EXPECT(mptAlice.getBalance(bob) == 40);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_INBOX) == 60);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_SPENDING) == 0);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::ISSUER_ENCRYPTED_BALANCE) == 60);
|
||||
.holderPubKey = mptAlice.getPubKey(bob)});
|
||||
|
||||
// bob merge inbox
|
||||
mptAlice.mergeInbox({
|
||||
.account = bob,
|
||||
});
|
||||
|
||||
// carol convert 20 to confidential
|
||||
mptAlice.convert(
|
||||
{.account = carol,
|
||||
.amt = 20,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
BEAST_EXPECT(mptAlice.getBalance(carol) == 30);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::HOLDER_ENCRYPTED_INBOX) == 20);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::HOLDER_ENCRYPTED_SPENDING) == 0);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::ISSUER_ENCRYPTED_BALANCE) == 20);
|
||||
.holderPubKey = mptAlice.getPubKey(carol)});
|
||||
|
||||
// carol merge inbox
|
||||
mptAlice.mergeInbox({
|
||||
@@ -907,32 +911,29 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
});
|
||||
|
||||
// bob sends 10 to carol
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({
|
||||
.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
});
|
||||
|
||||
// bob sends 1 to carol again
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 1,
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({
|
||||
.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 1,
|
||||
});
|
||||
|
||||
mptAlice.mergeInbox({
|
||||
.account = carol,
|
||||
});
|
||||
|
||||
// carol sends 15 backto bob
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 15,
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({
|
||||
.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 15,
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -950,7 +951,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
@@ -973,22 +973,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.convert(
|
||||
{.account = bob,
|
||||
.amt = 60,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
BEAST_EXPECT(mptAlice.getBalance(bob) == 40);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_INBOX) == 60);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_SPENDING) == 0);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::ISSUER_ENCRYPTED_BALANCE) == 60);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::AUDITOR_ENCRYPTED_BALANCE) == 60);
|
||||
.holderPubKey = mptAlice.getPubKey(bob)});
|
||||
|
||||
// bob merge inbox
|
||||
mptAlice.mergeInbox({
|
||||
@@ -998,22 +983,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.convert(
|
||||
{.account = carol,
|
||||
.amt = 20,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
BEAST_EXPECT(mptAlice.getBalance(carol) == 30);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::HOLDER_ENCRYPTED_INBOX) == 20);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::HOLDER_ENCRYPTED_SPENDING) == 0);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::ISSUER_ENCRYPTED_BALANCE) == 20);
|
||||
BEAST_EXPECT(
|
||||
mptAlice.getDecryptedBalance(
|
||||
carol, MPTTester::AUDITOR_ENCRYPTED_BALANCE) == 20);
|
||||
.holderPubKey = mptAlice.getPubKey(carol)});
|
||||
|
||||
// carol merge inbox
|
||||
mptAlice.mergeInbox({
|
||||
@@ -1021,32 +991,17 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
});
|
||||
|
||||
// bob sends 10 to carol
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 10});
|
||||
|
||||
// bob sends 1 to carol again
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 1,
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 1});
|
||||
|
||||
mptAlice.mergeInbox({
|
||||
.account = carol,
|
||||
});
|
||||
|
||||
// carol sends 15 backto bob
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 15,
|
||||
.proof = "123",
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.send({.account = carol, .dest = bob, .amt = 15});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1071,7 +1026,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
@@ -1090,9 +1044,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanTransfer | tfMPTCanPrivacy});
|
||||
{.ownerCount = 1, .flags = tfMPTCanTransfer | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
mptAlice.authorize({.account = carol});
|
||||
@@ -1104,12 +1056,23 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.pay(alice, bob, 100);
|
||||
mptAlice.pay(alice, carol, 50);
|
||||
|
||||
mptAlice.convert({
|
||||
.account = bob,
|
||||
.amt = 50,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
});
|
||||
|
||||
mptAlice.convert({
|
||||
.account = carol,
|
||||
.amt = 40,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
});
|
||||
|
||||
// issuer can not be the same as sender
|
||||
mptAlice.send(
|
||||
{.account = alice,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
@@ -1123,7 +1086,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = bob,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
@@ -1137,7 +1099,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt = Buffer(10),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
@@ -1149,7 +1110,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt = Buffer(10),
|
||||
@@ -1161,7 +1121,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
@@ -1169,43 +1128,46 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
.issuerEncryptedAmt = Buffer(10),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
auto const ciphertextHex = generatePlaceholderCiphertext();
|
||||
// auto const ciphertextHex = generatePlaceholderCiphertext();
|
||||
|
||||
// sender encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt = ciphertextHex,
|
||||
.issuerEncryptedAmt = ciphertextHex,
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// dest encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt = ciphertextHex,
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt = ciphertextHex,
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// issuer encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.senderEncryptedAmt = ciphertextHex,
|
||||
.destEncryptedAmt = ciphertextHex,
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
// todo: proof length check
|
||||
// invalid proof length
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.proof = std::string(10, 'A'),
|
||||
.err = temMALFORMED});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1265,8 +1227,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
.account = carol,
|
||||
});
|
||||
|
||||
auto const ciphertextHex = generatePlaceholderCiphertext();
|
||||
|
||||
// issuance not found
|
||||
{
|
||||
Env env{*this, features};
|
||||
@@ -1290,12 +1250,13 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
jv[jss::Destination] = carol.human();
|
||||
jv[jss::TransactionType] = jss::ConfidentialSend;
|
||||
jv[sfMPTokenIssuanceID] = to_string(mptAlice.issuanceID());
|
||||
jv[sfSenderEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
jv[sfDestinationEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
jv[sfIssuerEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
|
||||
auto const encryptedAmt = strHex(ciphertextHex);
|
||||
jv[sfSenderEncryptedAmount] = encryptedAmt;
|
||||
jv[sfDestinationEncryptedAmount] = encryptedAmt;
|
||||
jv[sfIssuerEncryptedAmount] = encryptedAmt;
|
||||
jv[sfZKProof] = "123";
|
||||
auto const dummyProofSize =
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(3);
|
||||
jv[sfZKProof.jsonName] = strHex(Buffer(dummyProofSize));
|
||||
|
||||
env(jv, ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
@@ -1307,9 +1268,8 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = unknown,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.destEncryptedAmt = ciphertextHex,
|
||||
.issuerEncryptedAmt = ciphertextHex,
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.issuerEncryptedAmt = getTrivialCiphertext(),
|
||||
.err = tecNO_TARGET});
|
||||
}
|
||||
|
||||
@@ -1319,13 +1279,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = dave,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecNO_PERMISSION});
|
||||
mptAlice.send(
|
||||
{.account = dave,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecNO_PERMISSION});
|
||||
}
|
||||
|
||||
@@ -1335,8 +1293,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = eve,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.destEncryptedAmt = ciphertextHex,
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.err = tecOBJECT_NOT_FOUND});
|
||||
}
|
||||
|
||||
@@ -1345,16 +1302,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
// lock issuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTLock});
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecLOCKED});
|
||||
{.account = bob, .dest = carol, .amt = 10, .err = tecLOCKED});
|
||||
// unlock issuance
|
||||
mptAlice.set({.account = alice, .flags = tfMPTUnlock});
|
||||
// now can send
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 1, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 1});
|
||||
}
|
||||
|
||||
// sender is locked
|
||||
@@ -1362,17 +1314,12 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
// lock bob
|
||||
mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock});
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecLOCKED});
|
||||
{.account = bob, .dest = carol, .amt = 10, .err = tecLOCKED});
|
||||
// unlock bob
|
||||
mptAlice.set(
|
||||
{.account = alice, .holder = bob, .flags = tfMPTUnlock});
|
||||
// now can send
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 2, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 2});
|
||||
}
|
||||
|
||||
// destination is locked
|
||||
@@ -1381,17 +1328,12 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.set(
|
||||
{.account = alice, .holder = carol, .flags = tfMPTLock});
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecLOCKED});
|
||||
{.account = bob, .dest = carol, .amt = 10, .err = tecLOCKED});
|
||||
// unlock carol
|
||||
mptAlice.set(
|
||||
{.account = alice, .holder = carol, .flags = tfMPTUnlock});
|
||||
// now can send
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 3, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 3});
|
||||
}
|
||||
|
||||
// sender not authorized
|
||||
@@ -1400,19 +1342,14 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.authorize(
|
||||
{.account = alice, .holder = bob, .flags = tfMPTUnauthorize});
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecNO_AUTH});
|
||||
{.account = bob, .dest = carol, .amt = 10, .err = tecNO_AUTH});
|
||||
// authorize bob again
|
||||
mptAlice.authorize({
|
||||
.account = alice,
|
||||
.holder = bob,
|
||||
});
|
||||
// now can send
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 4, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 4});
|
||||
}
|
||||
|
||||
// destination not authorized
|
||||
@@ -1421,19 +1358,14 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.authorize(
|
||||
{.account = alice, .holder = carol, .flags = tfMPTUnauthorize});
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = tecNO_AUTH});
|
||||
{.account = bob, .dest = carol, .amt = 10, .err = tecNO_AUTH});
|
||||
// authorize carol again
|
||||
mptAlice.authorize({
|
||||
.account = alice,
|
||||
.holder = carol,
|
||||
});
|
||||
// now can send
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 5, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 5});
|
||||
}
|
||||
|
||||
// cannot send when MPTCanTransfer is not set
|
||||
@@ -1445,9 +1377,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanLock | tfMPTCanPrivacy});
|
||||
{.ownerCount = 1, .flags = tfMPTCanLock | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
mptAlice.authorize({.account = carol});
|
||||
@@ -1490,9 +1420,64 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.err = tecNO_AUTH});
|
||||
}
|
||||
|
||||
// bad proof
|
||||
{
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
MPTTester mptAlice(env, alice, {.holders = {bob, carol}});
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.flags = tfMPTCanLock | tfMPTCanPrivacy | tfMPTCanTransfer});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
mptAlice.authorize({.account = carol});
|
||||
|
||||
mptAlice.pay(alice, bob, 100);
|
||||
mptAlice.pay(alice, carol, 50);
|
||||
|
||||
mptAlice.generateKeyPair(alice);
|
||||
mptAlice.generateKeyPair(bob);
|
||||
mptAlice.generateKeyPair(carol);
|
||||
|
||||
mptAlice.set(
|
||||
{.account = alice, .issuerPubKey = mptAlice.getPubKey(alice)});
|
||||
|
||||
mptAlice.convert(
|
||||
{.account = bob,
|
||||
.amt = 60,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
mptAlice.mergeInbox({
|
||||
.account = bob,
|
||||
});
|
||||
|
||||
mptAlice.convert(
|
||||
{.account = carol,
|
||||
.amt = 20,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
.err = tesSUCCESS});
|
||||
|
||||
mptAlice.mergeInbox({
|
||||
.account = carol,
|
||||
});
|
||||
|
||||
auto const dummyProofSize =
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(3);
|
||||
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = strHex(Buffer(dummyProofSize)),
|
||||
.err = tecBAD_PROOF});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1825,15 +1810,13 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.convertBack(
|
||||
{.account = bob,
|
||||
.amt = 30,
|
||||
.holderEncryptedAmt =
|
||||
Buffer{badCiphertext, ecGamalEncryptedTotalLength},
|
||||
.holderEncryptedAmt = getBadCiphertext(),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
mptAlice.convertBack(
|
||||
{.account = bob,
|
||||
.amt = 30,
|
||||
.issuerEncryptedAmt =
|
||||
Buffer{badCiphertext, ecGamalEncryptedTotalLength},
|
||||
.issuerEncryptedAmt = getBadCiphertext(),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
}
|
||||
}
|
||||
@@ -2054,10 +2037,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
Account const alice("alice"); // issuer
|
||||
Account const bob("bob"); // holder
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
Account const dpIssuer("dpIssuer"); // holder
|
||||
Account const dpIssuer("dpIssuer");
|
||||
|
||||
env.fund(XRP(50000), dpIssuer);
|
||||
env.close();
|
||||
@@ -2066,7 +2049,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
@@ -2086,16 +2068,16 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
env(fset(bob, asfDepositAuth));
|
||||
env.close();
|
||||
|
||||
mptAlice.convert(
|
||||
{.account = carol,
|
||||
.amt = 50,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.convert(
|
||||
{.account = bob,
|
||||
.amt = 50,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
.err = tesSUCCESS});
|
||||
mptAlice.convert({
|
||||
.account = carol,
|
||||
.amt = 50,
|
||||
.holderPubKey = mptAlice.getPubKey(carol),
|
||||
});
|
||||
mptAlice.convert({
|
||||
.account = bob,
|
||||
.amt = 50,
|
||||
.holderPubKey = mptAlice.getPubKey(bob),
|
||||
});
|
||||
|
||||
// carol merge inbox
|
||||
mptAlice.mergeInbox({
|
||||
@@ -2111,20 +2093,14 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
// Bob authorize alice
|
||||
env(deposit::auth(bob, carol));
|
||||
env.close();
|
||||
|
||||
mptAlice.send({
|
||||
.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
});
|
||||
mptAlice.send({.account = carol, .dest = bob, .amt = 10});
|
||||
|
||||
// Create credentials
|
||||
env(credentials::create(bob, dpIssuer, credType));
|
||||
@@ -2137,8 +2113,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.credentials = {{credIdx}}});
|
||||
|
||||
// Bob revoke authorization
|
||||
@@ -2148,15 +2123,13 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.credentials = {{credIdx}},
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
@@ -2167,15 +2140,13 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.send(
|
||||
{.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.err = tecNO_PERMISSION});
|
||||
|
||||
mptAlice.send({
|
||||
.account = carol,
|
||||
.dest = bob,
|
||||
.amt = 10, // will be encrypted internally
|
||||
.proof = "123",
|
||||
.amt = 10,
|
||||
.credentials = {{credIdx}},
|
||||
});
|
||||
}
|
||||
@@ -2252,8 +2223,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
// after send, bob's inbox balance is 50, spending balance
|
||||
// remains 60. carol's inbox balance remains 0, spending balance
|
||||
// drops to 70.
|
||||
mptAlice.send(
|
||||
{.account = carol, .dest = bob, .amt = 50, .proof = "123"});
|
||||
mptAlice.send({.account = carol, .dest = bob, .amt = 50});
|
||||
|
||||
// alice clawback all confidential balance from bob, 110 in total.
|
||||
// bob has balance in both inbox and spending. These balances should
|
||||
@@ -2350,8 +2320,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
// after send, bob's inbox balance is 50, spending balance
|
||||
// remains 60. carol's inbox balance remains 0, spending balance
|
||||
// drops to 70.
|
||||
mptAlice.send(
|
||||
{.account = carol, .dest = bob, .amt = 50, .proof = "123"});
|
||||
mptAlice.send({.account = carol, .dest = bob, .amt = 50});
|
||||
|
||||
// alice clawback all confidential balance from bob, 110 in total.
|
||||
// bob has balance in both inbox and spending. These balances should
|
||||
@@ -2406,7 +2375,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
|
||||
mptAlice.create(
|
||||
{.ownerCount = 1,
|
||||
.holderCount = 0,
|
||||
.flags = tfMPTCanTransfer | tfMPTCanLock | tfMPTCanPrivacy});
|
||||
|
||||
mptAlice.authorize({.account = bob});
|
||||
@@ -2424,7 +2392,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = carol,
|
||||
.holder = bob,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = temMALFORMED});
|
||||
|
||||
// invalid issuance ID, whose issuer is not alice
|
||||
@@ -2448,7 +2415,6 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.holder = alice,
|
||||
.amt = 10,
|
||||
.proof = "123",
|
||||
.err = temMALFORMED});
|
||||
|
||||
// invalid amount
|
||||
@@ -2456,10 +2422,9 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
.amt = 0,
|
||||
.proof = "123",
|
||||
.err = temBAD_AMOUNT});
|
||||
|
||||
// proof length invalid
|
||||
// invalid proof length
|
||||
mptAlice.confidentialClaw(
|
||||
{.account = alice,
|
||||
.holder = bob,
|
||||
@@ -2801,10 +2766,8 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
mptAlice.mergeInbox({
|
||||
.account = carol,
|
||||
});
|
||||
mptAlice.send(
|
||||
{.account = bob, .dest = carol, .amt = 100, .proof = "123"});
|
||||
mptAlice.send(
|
||||
{.account = carol, .dest = bob, .amt = 100, .proof = "123"});
|
||||
mptAlice.send({.account = bob, .dest = carol, .amt = 100});
|
||||
mptAlice.send({.account = carol, .dest = bob, .amt = 100});
|
||||
|
||||
// verify proof fails with invalid clawback amount
|
||||
// bob: 100 in inbox, 200 in spending
|
||||
@@ -3067,15 +3030,18 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
uint64_t const amt = 10;
|
||||
Buffer const blindingFactor = generateBlindingFactor();
|
||||
Buffer const pcBlindingFactor = generateBlindingFactor();
|
||||
uint64_t const spendingBalance = mptAlice.getDecryptedBalance(
|
||||
|
||||
auto const spendingBalance = mptAlice.getDecryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_SPENDING);
|
||||
BEAST_EXPECT(spendingBalance.has_value());
|
||||
auto const encryptedSpendingBalance = mptAlice.getEncryptedBalance(
|
||||
bob, MPTTester::HOLDER_ENCRYPTED_SPENDING);
|
||||
|
||||
BEAST_EXPECT(encryptedSpendingBalance);
|
||||
BEAST_EXPECT(
|
||||
encryptedSpendingBalance.has_value() &&
|
||||
!encryptedSpendingBalance->empty());
|
||||
|
||||
Buffer const pedersenCommitment =
|
||||
mptAlice.getPedersenCommitment(spendingBalance, pcBlindingFactor);
|
||||
mptAlice.getPedersenCommitment(*spendingBalance, pcBlindingFactor);
|
||||
Buffer const issuerCiphertext =
|
||||
mptAlice.encryptAmount(alice, amt, blindingFactor);
|
||||
Buffer const bobCiphertext =
|
||||
@@ -3099,7 +3065,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{
|
||||
.pedersenCommitment =
|
||||
badPedersenCommitment, // bad pedersen commitment
|
||||
.amt = spendingBalance,
|
||||
.amt = *spendingBalance,
|
||||
.encryptedAmt = *encryptedSpendingBalance,
|
||||
.blindingFactor = pcBlindingFactor,
|
||||
});
|
||||
@@ -3135,7 +3101,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
blindingFactor,
|
||||
{
|
||||
.pedersenCommitment = pedersenCommitment,
|
||||
.amt = spendingBalance,
|
||||
.amt = *spendingBalance,
|
||||
.encryptedAmt = *encryptedSpendingBalance,
|
||||
.blindingFactor = pcBlindingFactor,
|
||||
});
|
||||
@@ -3170,7 +3136,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
blindingFactor,
|
||||
{
|
||||
.pedersenCommitment = pedersenCommitment,
|
||||
.amt = spendingBalance,
|
||||
.amt = *spendingBalance,
|
||||
.encryptedAmt = *encryptedSpendingBalance,
|
||||
.blindingFactor =
|
||||
generateBlindingFactor(), // bad blinding factor
|
||||
@@ -3204,7 +3170,7 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
blindingFactor,
|
||||
{
|
||||
.pedersenCommitment = pedersenCommitment,
|
||||
.amt = spendingBalance,
|
||||
.amt = *spendingBalance,
|
||||
.encryptedAmt = *encryptedSpendingBalance,
|
||||
.blindingFactor = pcBlindingFactor,
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,6 @@ class MPTTester;
|
||||
|
||||
auto const MPTDEXFlags = tfMPTCanTrade | tfMPTCanTransfer;
|
||||
|
||||
// Generates a syntactically valid placeholder ciphertext
|
||||
ripple::Buffer
|
||||
generatePlaceholderCiphertext();
|
||||
|
||||
// Check flags settings on MPT create
|
||||
class mptflags
|
||||
{
|
||||
@@ -434,10 +430,10 @@ public:
|
||||
void
|
||||
generateKeyPair(Account const& account);
|
||||
|
||||
Buffer
|
||||
std::optional<Buffer>
|
||||
getPubKey(Account const& account) const;
|
||||
|
||||
Buffer
|
||||
std::optional<Buffer>
|
||||
getPrivKey(Account const& account) const;
|
||||
|
||||
Buffer
|
||||
@@ -446,10 +442,10 @@ public:
|
||||
uint64_t const amt,
|
||||
Buffer const& blindingFactor) const;
|
||||
|
||||
uint64_t
|
||||
std::optional<uint64_t>
|
||||
decryptAmount(Account const& account, Buffer const& amt) const;
|
||||
|
||||
uint64_t
|
||||
std::optional<uint64_t>
|
||||
getDecryptedBalance(
|
||||
Account const& account,
|
||||
EncryptedBalanceType balanceType) const;
|
||||
@@ -457,15 +453,23 @@ public:
|
||||
std::int64_t
|
||||
getIssuanceOutstandingBalance() const;
|
||||
|
||||
Buffer
|
||||
std::optional<Buffer>
|
||||
getClawbackProof(
|
||||
Account const& holder,
|
||||
std::uint64_t amount,
|
||||
Buffer const& privateKey,
|
||||
uint256 const& txHash) const;
|
||||
|
||||
Buffer
|
||||
getSchnorrProof(Account const& account, uint256 const& contextHash) const;
|
||||
std::optional<Buffer>
|
||||
getSchnorrProof(Account const& account, uint256 const& ctxHash) const;
|
||||
|
||||
std::optional<Buffer>
|
||||
getConfidentialSendProof(
|
||||
std::uint64_t const amount,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
Slice const& blindingFactor,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash) const;
|
||||
|
||||
Buffer
|
||||
getConvertBackProof(
|
||||
|
||||
@@ -139,11 +139,10 @@ ConfidentialConvert::preclaim(PreclaimContext const& ctx)
|
||||
holderPubKey = (*sleMptoken)[sfHolderElGamalPublicKey];
|
||||
}
|
||||
|
||||
// Prepare Auditor Info
|
||||
std::optional<EncryptedAmountInfo> auditor;
|
||||
std::optional<ConfidentialRecipient> auditor;
|
||||
if (hasAuditor)
|
||||
{
|
||||
auditor.emplace(EncryptedAmountInfo{
|
||||
auditor.emplace(ConfidentialRecipient{
|
||||
(*sleIssuance)[sfAuditorElGamalPublicKey],
|
||||
ctx.tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
@@ -62,14 +62,13 @@ verifyProofs(
|
||||
(*mptoken)[~sfConfidentialBalanceVersion].value_or(0));
|
||||
|
||||
// Prepare Auditor Info
|
||||
std::optional<EncryptedAmountInfo> auditor;
|
||||
std::optional<ConfidentialRecipient> auditor;
|
||||
bool const hasAuditor = issuance->isFieldPresent(sfAuditorElGamalPublicKey);
|
||||
if (hasAuditor)
|
||||
{
|
||||
auditor.emplace(
|
||||
EncryptedAmountInfo{
|
||||
(*issuance)[sfAuditorElGamalPublicKey],
|
||||
tx[sfAuditorEncryptedAmount]});
|
||||
auditor.emplace(ConfidentialRecipient{
|
||||
(*issuance)[sfAuditorElGamalPublicKey],
|
||||
tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
if (auto const ter = verifyRevealedAmount(
|
||||
@@ -200,10 +199,6 @@ ConfidentialConvertBack::doApply()
|
||||
(*sleIssuance)[sfConfidentialOutstandingAmount] =
|
||||
(*sleIssuance)[sfConfidentialOutstandingAmount] - amtToConvertBack;
|
||||
|
||||
// it's fine if it reaches max uint32, it just resets to 0
|
||||
(*sleMptoken)[sfConfidentialBalanceVersion] =
|
||||
(*sleMptoken)[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
|
||||
|
||||
std::optional<Slice> const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
|
||||
|
||||
// homomorphically subtract holder's encrypted balance
|
||||
@@ -245,6 +240,9 @@ ConfidentialConvertBack::doApply()
|
||||
(*sleMptoken)[sfAuditorEncryptedBalance] = res;
|
||||
}
|
||||
|
||||
// increment version
|
||||
incrementConfidentialVersion(*sleMptoken);
|
||||
|
||||
view().update(sleIssuance);
|
||||
view().update(sleMptoken);
|
||||
return tesSUCCESS;
|
||||
|
||||
@@ -43,11 +43,11 @@ ConfidentialSend::preflight(PreflightContext const& ctx)
|
||||
ecGamalEncryptedTotalLength)
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
if (hasAuditor && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
// if (ctx.tx[sfZKProof].length() != ecEqualityProofLength)
|
||||
// return temMALFORMED;
|
||||
// Check the length of the ZKProof
|
||||
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
|
||||
if (ctx.tx[sfZKProof].length() !=
|
||||
getMultiCiphertextEqualityProofSize(recipientCount))
|
||||
return temMALFORMED;
|
||||
|
||||
// Check the encrypted amount formats, this is more expensive so put it at
|
||||
// the end
|
||||
@@ -56,6 +56,9 @@ ConfidentialSend::preflight(PreflightContext const& ctx)
|
||||
!isValidCiphertext(ctx.tx[sfIssuerEncryptedAmount]))
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
if (hasAuditor && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
|
||||
return temBAD_CIPHERTEXT;
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -152,6 +155,50 @@ ConfidentialSend::preclaim(PreclaimContext const& ctx)
|
||||
!isTesSuccess(ter))
|
||||
return ter;
|
||||
|
||||
auto const contextHash = getSendContextHash(
|
||||
account,
|
||||
ctx.tx[sfSequence],
|
||||
mptIssuanceID,
|
||||
destination,
|
||||
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
|
||||
|
||||
auto const expectedRecipients = requiresAuditor ? 4 : 3;
|
||||
|
||||
// Prepare encrypted amount info for proof verification
|
||||
std::vector<ConfidentialRecipient> recipients;
|
||||
recipients.reserve(expectedRecipients);
|
||||
|
||||
// Add sender encrypted amount info
|
||||
recipients.push_back(
|
||||
{(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfSenderEncryptedAmount]});
|
||||
|
||||
// Add destination encrypted amount info
|
||||
recipients.push_back(
|
||||
{(*sleDestinationMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfDestinationEncryptedAmount]});
|
||||
|
||||
// Add issuer encrypted amount info
|
||||
recipients.push_back(
|
||||
{(*sleIssuance)[sfIssuerElGamalPublicKey],
|
||||
ctx.tx[sfIssuerEncryptedAmount]});
|
||||
|
||||
// Add auditor encrypted amount info if present
|
||||
if (requiresAuditor)
|
||||
{
|
||||
recipients.push_back(
|
||||
{(*sleIssuance)[sfAuditorElGamalPublicKey],
|
||||
ctx.tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
// Verify the multi-ciphertext equality proof
|
||||
if (auto const ter = verifyMultiCiphertextEqualityProof(
|
||||
ctx.tx[sfZKProof], recipients, expectedRecipients, contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
return ter;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -226,10 +273,6 @@ ConfidentialSend::doApply()
|
||||
(*sleSender)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// Increment version
|
||||
(*sleSender)[sfConfidentialBalanceVersion] =
|
||||
(*sleSender)[~sfConfidentialBalanceVersion].value_or(0u) + 1u;
|
||||
|
||||
// Add to destination's inbox balance
|
||||
{
|
||||
Slice const curInbox = (*sleDestination)[sfConfidentialBalanceInbox];
|
||||
@@ -270,6 +313,10 @@ ConfidentialSend::doApply()
|
||||
(*sleDestination)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// increment version
|
||||
incrementConfidentialVersion(*sleSender);
|
||||
incrementConfidentialVersion(*sleDestination);
|
||||
|
||||
view().update(sleSender);
|
||||
view().update(sleDestination);
|
||||
return tesSUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user