mirror of
https://github.com/XRPLF/rippled.git
synced 2026-10-01 17:28:13 +00:00
Support Pedersen-ElGamal linkage for ConfidentialSend (#6289)
* support Pedersen Amount commitment for ConfidentialSend * support Pedersen Balance commitment for ConfidentialSend
This commit is contained in:
@@ -163,8 +163,49 @@ verifyClawbackEqualityProof(
|
||||
Buffer
|
||||
generateBlindingFactor();
|
||||
|
||||
/**
|
||||
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
|
||||
* Pedersen Commitment for a transaction Amount.
|
||||
*
|
||||
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `m`
|
||||
* as the Pedersen Commitment `pcmSlice`, using the randomness `r`.
|
||||
* Proves Enc(m) <-> Pcm(m)
|
||||
*
|
||||
* @param proof The Zero Knowledge Proof bytes.
|
||||
* @param encAmt The ElGamal ciphertext of the amount (C1, C2).
|
||||
* @param pubKeySlice The sender's public key.
|
||||
* @param pcmSlice The Pedersen Commitment to the amount.
|
||||
* @param contextHash The unique context hash for this transaction.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyPedersenLinkage(
|
||||
verifyAmountPcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& pcmSlice,
|
||||
uint256 const& contextHash);
|
||||
|
||||
/**
|
||||
* @brief Verifies the cryptographic link between an ElGamal Ciphertext and a
|
||||
* Pedersen Commitment for an account Balance.
|
||||
*
|
||||
* It proves that the ElGamal ciphertext `encAmt` encrypts the same value `b`
|
||||
* as the Pedersen Commitment `pcmSlice`, using the secret key `s`.
|
||||
* Proves Enc(b) <-> Pcm(b)
|
||||
*
|
||||
* Note: Swaps arguments (Pk <-> C1) to accommodate the different algebraic
|
||||
* structure.
|
||||
*
|
||||
* @param proof The Zero Knowledge Proof bytes.
|
||||
* @param encAmt The ElGamal ciphertext of the balance (C1, C2).
|
||||
* @param pubKeySlice The sender's public key.
|
||||
* @param pcmSlice The Pedersen Commitment to the balance.
|
||||
* @param contextHash The unique context hash for this transaction.
|
||||
* @return tesSUCCESS if the proof is valid, or an error code otherwise.
|
||||
*/
|
||||
TER
|
||||
verifyBalancePcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
|
||||
@@ -313,7 +313,8 @@ TYPED_SFIELD(sfAuditorEncryptedBalance, VL, 42)
|
||||
TYPED_SFIELD(sfAuditorEncryptedAmount, VL, 43)
|
||||
TYPED_SFIELD(sfAuditorElGamalPublicKey, VL, 44)
|
||||
TYPED_SFIELD(sfBlindingFactor, VL, 45)
|
||||
TYPED_SFIELD(sfPedersenCommitment, VL, 46)
|
||||
TYPED_SFIELD(sfAmountCommitment, VL, 46)
|
||||
TYPED_SFIELD(sfBalanceCommitment, VL, 47)
|
||||
|
||||
// account (common)
|
||||
TYPED_SFIELD(sfAccount, ACCOUNT, 1)
|
||||
|
||||
@@ -1107,7 +1107,7 @@ TRANSACTION(ttCONFIDENTIAL_CONVERT_BACK, 87, ConfidentialConvertBack,
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfBlindingFactor, soeREQUIRED},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
{sfPedersenCommitment, soeREQUIRED}
|
||||
{sfBalanceCommitment, soeREQUIRED},
|
||||
}))
|
||||
|
||||
#if TRANSACTION_INCLUDE
|
||||
@@ -1123,9 +1123,11 @@ TRANSACTION(ttCONFIDENTIAL_SEND, 88, ConfidentialSend,
|
||||
{sfSenderEncryptedAmount, soeREQUIRED},
|
||||
{sfDestinationEncryptedAmount, soeREQUIRED},
|
||||
{sfIssuerEncryptedAmount, soeREQUIRED},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
{sfCredentialIDs, soeOPTIONAL},
|
||||
{sfAuditorEncryptedAmount, soeOPTIONAL},
|
||||
{sfZKProof, soeREQUIRED},
|
||||
{sfAmountCommitment, soeREQUIRED},
|
||||
{sfBalanceCommitment, soeREQUIRED},
|
||||
{sfCredentialIDs, soeOPTIONAL},
|
||||
}))
|
||||
|
||||
#if TRANSACTION_INCLUDE
|
||||
|
||||
@@ -479,7 +479,48 @@ checkEncryptedAmountFormat(STObject const& object)
|
||||
}
|
||||
|
||||
TER
|
||||
verifyPedersenLinkage(
|
||||
verifyAmountPcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
Slice const& pcmSlice,
|
||||
uint256 const& contextHash)
|
||||
{
|
||||
if (proof.length() != ecPedersenProofLength)
|
||||
return tecINTERNAL;
|
||||
|
||||
secp256k1_pubkey c1, c2;
|
||||
if (!makeEcPair(encAmt, c1, c2))
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
secp256k1_pubkey pubKey;
|
||||
if (pubKeySlice.size() != ecPubKeyLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
secp256k1_pubkey pcm;
|
||||
if (pcmSlice.size() != ecPedersenCommitmentLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::memcpy(pubKey.data, pubKeySlice.data(), ecPubKeyLength);
|
||||
std::memcpy(pcm.data, pcmSlice.data(), ecPedersenCommitmentLength);
|
||||
|
||||
if (secp256k1_elgamal_pedersen_link_verify(
|
||||
secp256k1Context(),
|
||||
proof.data(),
|
||||
&c1,
|
||||
&c2,
|
||||
&pubKey,
|
||||
&pcm,
|
||||
contextHash.data()) != 1)
|
||||
{
|
||||
return tecBAD_PROOF;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifyBalancePcmLinkage(
|
||||
Slice const& proof,
|
||||
Slice const& encAmt,
|
||||
Slice const& pubKeySlice,
|
||||
@@ -493,12 +534,17 @@ verifyPedersenLinkage(
|
||||
secp256k1_pubkey c2;
|
||||
|
||||
if (!makeEcPair(encAmt, c1, c2))
|
||||
return tecINTERNAL;
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
secp256k1_pubkey pubKey;
|
||||
std::memcpy(pubKey.data, pubKeySlice.data(), ecPubKeyLength);
|
||||
if (pubKeySlice.size() != ecPubKeyLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
secp256k1_pubkey pcm;
|
||||
if (pcmSlice.size() != ecPedersenCommitmentLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
std::memcpy(pubKey.data, pubKeySlice.data(), ecPubKeyLength);
|
||||
std::memcpy(pcm.data, pcmSlice.data(), ecPubKeyLength);
|
||||
|
||||
if (secp256k1_elgamal_pedersen_link_verify(
|
||||
@@ -509,7 +555,9 @@ verifyPedersenLinkage(
|
||||
&c1,
|
||||
&pcm,
|
||||
contextHash.data()) != 1)
|
||||
{
|
||||
return tecBAD_PROOF;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,30 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
return trivialCiphertext;
|
||||
}
|
||||
|
||||
static std::string const&
|
||||
getTrivialSendProofHex(size_t nRecipients)
|
||||
{
|
||||
static std::string const trivialProofHex = [nRecipients]() {
|
||||
size_t const sizeEquality =
|
||||
getMultiCiphertextEqualityProofSize(nRecipients);
|
||||
size_t const totalSize = sizeEquality + (2 * ecPedersenProofLength);
|
||||
|
||||
Buffer buf(totalSize);
|
||||
std::memset(buf.data(), 0, totalSize);
|
||||
|
||||
for (std::size_t i = 0; i < totalSize; i += ecGamalEncryptedLength)
|
||||
{
|
||||
buf.data()[i] = 0x02;
|
||||
if (i + ecGamalEncryptedLength - 1 < totalSize)
|
||||
buf.data()[i + ecGamalEncryptedLength - 1] = 0x01;
|
||||
}
|
||||
|
||||
return strHex(buf);
|
||||
}();
|
||||
|
||||
return trivialProofHex;
|
||||
}
|
||||
|
||||
void
|
||||
testConvert(FeatureBitset features)
|
||||
{
|
||||
@@ -1026,12 +1050,9 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.senderEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.err = temDISABLED});
|
||||
}
|
||||
|
||||
@@ -1073,26 +1094,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = alice,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.err = temMALFORMED});
|
||||
|
||||
// can not send to self
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = bob,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.err = temMALFORMED});
|
||||
{.account = bob, .dest = bob, .amt = 10, .err = temMALFORMED});
|
||||
|
||||
// sender encrypted amount wrong length
|
||||
mptAlice.send(
|
||||
@@ -1100,59 +1106,51 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt = Buffer(10),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// dest encrypted amount wrong length
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt = Buffer(10),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// issuer encrypted amount wrong length
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt = Buffer(10),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
// auto const ciphertextHex = generatePlaceholderCiphertext();
|
||||
|
||||
// sender encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.senderEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// dest encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.destEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
// issuer encrypted amount malformed
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.issuerEncryptedAmt = Buffer(ecGamalEncryptedTotalLength),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = temBAD_CIPHERTEXT});
|
||||
|
||||
// invalid proof length
|
||||
@@ -1160,13 +1158,29 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.destEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.issuerEncryptedAmt =
|
||||
Buffer(ripple::ecGamalEncryptedTotalLength),
|
||||
.proof = std::string(10, 'A'),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = temMALFORMED});
|
||||
|
||||
// invalid amount Pedersen commitment length
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(100),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = temMALFORMED});
|
||||
|
||||
// invalid balance Pedersen commitment length
|
||||
mptAlice.send(
|
||||
{.account = bob,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(100),
|
||||
.err = temMALFORMED});
|
||||
}
|
||||
}
|
||||
@@ -1253,10 +1267,10 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
jv[sfSenderEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
jv[sfDestinationEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
jv[sfIssuerEncryptedAmount] = strHex(getTrivialCiphertext());
|
||||
|
||||
auto const dummyProofSize =
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(3);
|
||||
jv[sfZKProof.jsonName] = strHex(Buffer(dummyProofSize));
|
||||
jv[sfAmountCommitment] = strHex(Buffer(ecPedersenCommitmentLength));
|
||||
jv[sfBalanceCommitment] =
|
||||
strHex(Buffer(ecPedersenCommitmentLength));
|
||||
jv[sfZKProof] = getTrivialSendProofHex(3);
|
||||
|
||||
env(jv, ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
@@ -1268,8 +1282,12 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = unknown,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt = getTrivialCiphertext(),
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.issuerEncryptedAmt = getTrivialCiphertext(),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = tecNO_TARGET});
|
||||
}
|
||||
|
||||
@@ -1279,11 +1297,23 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = dave,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt = getTrivialCiphertext(),
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.issuerEncryptedAmt = getTrivialCiphertext(),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = tecNO_PERMISSION});
|
||||
mptAlice.send(
|
||||
{.account = dave,
|
||||
.dest = carol,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt = getTrivialCiphertext(),
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.issuerEncryptedAmt = getTrivialCiphertext(),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = tecNO_PERMISSION});
|
||||
}
|
||||
|
||||
@@ -1293,7 +1323,12 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
{.account = bob,
|
||||
.dest = eve,
|
||||
.amt = 10,
|
||||
.senderEncryptedAmt = getTrivialCiphertext(),
|
||||
.destEncryptedAmt = getTrivialCiphertext(),
|
||||
.issuerEncryptedAmt = getTrivialCiphertext(),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.amountCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.balanceCommitment = Buffer(ecPedersenCommitmentLength),
|
||||
.err = tecOBJECT_NOT_FOUND});
|
||||
}
|
||||
|
||||
@@ -1468,14 +1503,11 @@ class ConfidentialTransfer_test : public beast::unit_test::suite
|
||||
.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)),
|
||||
.proof = getTrivialSendProofHex(3),
|
||||
.err = tecBAD_PROOF});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,11 +819,14 @@ MPTTester::getSchnorrProof(Account const& account, uint256 const& ctxHash) const
|
||||
|
||||
std::optional<Buffer>
|
||||
MPTTester::getConfidentialSendProof(
|
||||
Account const& sender,
|
||||
std::uint64_t const amount,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
Slice const& blindingFactor,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash) const
|
||||
uint256 const& contextHash,
|
||||
PedersenProofParams const& amountParams,
|
||||
PedersenProofParams const& balanceParams) const
|
||||
{
|
||||
if (recipients.size() != nRecipients)
|
||||
return std::nullopt;
|
||||
@@ -831,6 +834,10 @@ MPTTester::getConfidentialSendProof(
|
||||
if (blindingFactor.size() != ecBlindingFactorLength)
|
||||
return std::nullopt;
|
||||
|
||||
auto const senderPubKey = getPubKey(sender);
|
||||
if (!senderPubKey)
|
||||
return std::nullopt;
|
||||
|
||||
auto const ctx = secp256k1Context();
|
||||
|
||||
std::vector<secp256k1_pubkey> r(nRecipients);
|
||||
@@ -874,14 +881,15 @@ MPTTester::getConfidentialSendProof(
|
||||
blindingFactor.data() + ecBlindingFactorLength);
|
||||
}
|
||||
|
||||
size_t proofLen =
|
||||
size_t sizeEquality =
|
||||
secp256k1_mpt_prove_same_plaintext_multi_size(nRecipients);
|
||||
Buffer proof(proofLen);
|
||||
Buffer equalityProof(sizeEquality);
|
||||
|
||||
// Get the multi-ciphertext equality proof
|
||||
if (secp256k1_mpt_prove_same_plaintext_multi(
|
||||
ctx,
|
||||
proof.data(),
|
||||
&proofLen,
|
||||
equalityProof.data(),
|
||||
&sizeEquality,
|
||||
amount,
|
||||
nRecipients,
|
||||
r.data(),
|
||||
@@ -893,6 +901,31 @@ MPTTester::getConfidentialSendProof(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto const amountLinkageProof = getAmountLinkageProof(
|
||||
*senderPubKey,
|
||||
Buffer(blindingFactor.data(), ecBlindingFactorLength),
|
||||
contextHash,
|
||||
amountParams);
|
||||
|
||||
auto const balanceLinkageProof = getBalanceLinkageProof(
|
||||
sender, contextHash, *senderPubKey, balanceParams);
|
||||
|
||||
auto const sizeAmountLinkage = amountLinkageProof.size();
|
||||
auto const sizeBalanceLinkage = balanceLinkageProof.size();
|
||||
|
||||
size_t const proofSize =
|
||||
sizeEquality + sizeAmountLinkage + sizeBalanceLinkage;
|
||||
Buffer proof(proofSize);
|
||||
|
||||
auto ptr = proof.data();
|
||||
std::memcpy(ptr, equalityProof.data(), sizeEquality);
|
||||
ptr += sizeEquality;
|
||||
|
||||
std::memcpy(ptr, amountLinkageProof.data(), sizeAmountLinkage);
|
||||
ptr += sizeAmountLinkage;
|
||||
|
||||
std::memcpy(ptr, balanceLinkageProof.data(), sizeBalanceLinkage);
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
@@ -941,7 +974,7 @@ MPTTester::getConvertBackProof(
|
||||
auto const holderPubKey = getPubKey(holder);
|
||||
if (holderPubKey)
|
||||
{
|
||||
Buffer const pedersenProof = generatePedersenLinkageProof(
|
||||
Buffer const pedersenProof = getBalanceLinkageProof(
|
||||
holder, contextHash, *holderPubKey, pcParams);
|
||||
|
||||
// todo: incoporate range proof
|
||||
@@ -1264,7 +1297,72 @@ MPTTester::send(MPTConfidentialSend const& arg)
|
||||
if (auditorAmt)
|
||||
jv[sfAuditorEncryptedAmount] = strHex(*auditorAmt);
|
||||
|
||||
// fill in the proof if not provided
|
||||
if (arg.credentials)
|
||||
{
|
||||
auto& arr(jv[sfCredentialIDs.jsonName] = Json::arrayValue);
|
||||
for (auto const& hash : *arg.credentials)
|
||||
arr.append(hash);
|
||||
}
|
||||
|
||||
// Sender's previous confidential state
|
||||
auto const prevSenderInbox =
|
||||
getDecryptedBalance(*arg.account, HOLDER_ENCRYPTED_INBOX);
|
||||
auto const prevSenderSpending =
|
||||
getDecryptedBalance(*arg.account, HOLDER_ENCRYPTED_SPENDING);
|
||||
auto const prevSenderIssuer =
|
||||
getDecryptedBalance(*arg.account, ISSUER_ENCRYPTED_BALANCE);
|
||||
if (!prevSenderInbox || !prevSenderSpending || !prevSenderIssuer)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
|
||||
std::optional<uint64_t> prevSenderAuditor;
|
||||
if (arg.auditorEncryptedAmt || auditor_)
|
||||
{
|
||||
prevSenderAuditor =
|
||||
getDecryptedBalance(*arg.account, AUDITOR_ENCRYPTED_BALANCE);
|
||||
if (!prevSenderAuditor)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
}
|
||||
|
||||
// Destination's previous confidential state
|
||||
auto const prevDestInbox =
|
||||
getDecryptedBalance(*arg.dest, HOLDER_ENCRYPTED_INBOX);
|
||||
auto const prevDestSpending =
|
||||
getDecryptedBalance(*arg.dest, HOLDER_ENCRYPTED_SPENDING);
|
||||
auto const prevDestIssuer =
|
||||
getDecryptedBalance(*arg.dest, ISSUER_ENCRYPTED_BALANCE);
|
||||
if (!prevDestInbox || !prevDestSpending || !prevDestIssuer)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
|
||||
std::optional<uint64_t> prevDestAuditor;
|
||||
if (arg.auditorEncryptedAmt || auditor_)
|
||||
{
|
||||
prevDestAuditor =
|
||||
getDecryptedBalance(*arg.dest, AUDITOR_ENCRYPTED_BALANCE);
|
||||
if (!prevDestAuditor)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
}
|
||||
|
||||
// Fill in the commitment if not provided
|
||||
Buffer amountCommitment, balanceCommitment;
|
||||
auto const amountBlindingFactor = generateBlindingFactor();
|
||||
if (arg.amountCommitment)
|
||||
amountCommitment = *arg.amountCommitment;
|
||||
else
|
||||
amountCommitment =
|
||||
getPedersenCommitment(*arg.amt, amountBlindingFactor);
|
||||
|
||||
jv[sfAmountCommitment] = strHex(amountCommitment);
|
||||
|
||||
auto const balanceBlindingFactor = generateBlindingFactor();
|
||||
if (arg.balanceCommitment)
|
||||
balanceCommitment = *arg.balanceCommitment;
|
||||
else
|
||||
balanceCommitment =
|
||||
getPedersenCommitment(*prevSenderSpending, balanceBlindingFactor);
|
||||
|
||||
jv[sfBalanceCommitment] = strHex(balanceCommitment);
|
||||
|
||||
// Fill in the proof if not provided
|
||||
if (arg.proof)
|
||||
jv[sfZKProof] = *arg.proof;
|
||||
else
|
||||
@@ -1306,8 +1404,33 @@ MPTTester::send(MPTConfidentialSend const& arg)
|
||||
recipients.push_back({Slice(*auditorPubKey), *auditorAmt});
|
||||
}
|
||||
|
||||
auto const proof = getConfidentialSendProof(
|
||||
*arg.amt, recipients, blindingFactor, nRecipients, ctxHash);
|
||||
auto const prevEncryptedSenderSpending =
|
||||
getEncryptedBalance(*arg.account, HOLDER_ENCRYPTED_SPENDING);
|
||||
|
||||
std::optional<Buffer> proof;
|
||||
|
||||
// Skip proof generation if encrypted balance is missing (e.g.,
|
||||
// feature disabled), or when the sender and destination are the
|
||||
// same (malformed case causing pcm to be zero). This prevents a
|
||||
// crash and allows certain error cases to be tested.
|
||||
if (arg.account != arg.dest && prevEncryptedSenderSpending)
|
||||
{
|
||||
proof = getConfidentialSendProof(
|
||||
*arg.account,
|
||||
*arg.amt,
|
||||
recipients,
|
||||
blindingFactor,
|
||||
nRecipients,
|
||||
ctxHash,
|
||||
{.pedersenCommitment = amountCommitment,
|
||||
.amt = *arg.amt,
|
||||
.encryptedAmt = senderAmt,
|
||||
.blindingFactor = amountBlindingFactor},
|
||||
{.pedersenCommitment = balanceCommitment,
|
||||
.amt = *prevSenderSpending,
|
||||
.encryptedAmt = *prevEncryptedSenderSpending,
|
||||
.blindingFactor = balanceBlindingFactor});
|
||||
}
|
||||
|
||||
if (proof)
|
||||
jv[sfZKProof.jsonName] = strHex(*proof);
|
||||
@@ -1320,55 +1443,11 @@ MPTTester::send(MPTConfidentialSend const& arg)
|
||||
}
|
||||
}
|
||||
|
||||
if (arg.credentials)
|
||||
{
|
||||
auto& arr(jv[sfCredentialIDs.jsonName] = Json::arrayValue);
|
||||
for (auto const& hash : *arg.credentials)
|
||||
arr.append(hash);
|
||||
}
|
||||
|
||||
auto const senderPubAmt = getBalance(*arg.account);
|
||||
auto const destPubAmt = getBalance(*arg.dest);
|
||||
auto const prevCOA = getIssuanceConfidentialBalance();
|
||||
auto const prevOA = getIssuanceOutstandingBalance();
|
||||
|
||||
// Sender's previous confidential state
|
||||
auto const prevSenderInbox =
|
||||
getDecryptedBalance(*arg.account, HOLDER_ENCRYPTED_INBOX);
|
||||
auto const prevSenderSpending =
|
||||
getDecryptedBalance(*arg.account, HOLDER_ENCRYPTED_SPENDING);
|
||||
auto const prevSenderIssuer =
|
||||
getDecryptedBalance(*arg.account, ISSUER_ENCRYPTED_BALANCE);
|
||||
if (!prevSenderInbox || !prevSenderSpending || !prevSenderIssuer)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
|
||||
std::optional<uint64_t> prevSenderAuditor;
|
||||
if (arg.auditorEncryptedAmt || auditor_)
|
||||
{
|
||||
prevSenderAuditor =
|
||||
getDecryptedBalance(*arg.account, AUDITOR_ENCRYPTED_BALANCE);
|
||||
if (!prevSenderAuditor)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
}
|
||||
// Destination's previous confidential state
|
||||
auto const prevDestInbox =
|
||||
getDecryptedBalance(*arg.dest, HOLDER_ENCRYPTED_INBOX);
|
||||
auto const prevDestSpending =
|
||||
getDecryptedBalance(*arg.dest, HOLDER_ENCRYPTED_SPENDING);
|
||||
auto const prevDestIssuer =
|
||||
getDecryptedBalance(*arg.dest, ISSUER_ENCRYPTED_BALANCE);
|
||||
if (!prevDestInbox || !prevDestSpending || !prevDestIssuer)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
|
||||
std::optional<uint64_t> prevDestAuditor;
|
||||
if (arg.auditorEncryptedAmt || auditor_)
|
||||
{
|
||||
prevDestAuditor =
|
||||
getDecryptedBalance(*arg.dest, AUDITOR_ENCRYPTED_BALANCE);
|
||||
if (!prevDestAuditor)
|
||||
Throw<std::runtime_error>("Failed to get Pre-send balance");
|
||||
}
|
||||
|
||||
if (submit(arg, jv) == tesSUCCESS)
|
||||
{
|
||||
auto const postCOA = getIssuanceConfidentialBalance();
|
||||
@@ -1797,7 +1876,7 @@ MPTTester::convertBack(MPTConvertBack const& arg)
|
||||
pedersenCommitment =
|
||||
getPedersenCommitment(*prevSpendingBalance, pcBlindingFactor);
|
||||
|
||||
jv[sfPedersenCommitment] = strHex(pedersenCommitment);
|
||||
jv[sfBalanceCommitment] = strHex(pedersenCommitment);
|
||||
|
||||
if (arg.proof)
|
||||
jv[sfZKProof.jsonName] = strHex(*arg.proof);
|
||||
@@ -1908,7 +1987,60 @@ MPTTester::convertBack(MPTConvertBack const& arg)
|
||||
}
|
||||
|
||||
Buffer
|
||||
MPTTester::generatePedersenLinkageProof(
|
||||
MPTTester::getAmountLinkageProof(
|
||||
Buffer const& pubKey,
|
||||
Buffer const& blindingFactor,
|
||||
uint256 const& contextHash,
|
||||
PedersenProofParams const& params) const
|
||||
{
|
||||
if (params.blindingFactor.size() != ecBlindingFactorLength ||
|
||||
params.pedersenCommitment.size() != ecPedersenCommitmentLength ||
|
||||
pubKey.size() != ecPubKeyLength ||
|
||||
params.encryptedAmt.size() != ecGamalEncryptedTotalLength ||
|
||||
blindingFactor.size() != ecBlindingFactorLength)
|
||||
return Buffer(ecPedersenProofLength);
|
||||
|
||||
secp256k1_pubkey c1, c2;
|
||||
auto const ctx = secp256k1Context();
|
||||
if (!secp256k1_ec_pubkey_parse(
|
||||
ctx, &c1, params.encryptedAmt.data(), ecGamalEncryptedLength) ||
|
||||
!secp256k1_ec_pubkey_parse(
|
||||
ctx,
|
||||
&c2,
|
||||
params.encryptedAmt.data() + ecGamalEncryptedLength,
|
||||
ecGamalEncryptedLength))
|
||||
{
|
||||
return Buffer();
|
||||
}
|
||||
|
||||
secp256k1_pubkey pk;
|
||||
std::memcpy(pk.data, pubKey.data(), ecPubKeyLength);
|
||||
|
||||
secp256k1_pubkey pcm;
|
||||
std::memcpy(
|
||||
pcm.data, params.pedersenCommitment.data(), ecPedersenCommitmentLength);
|
||||
|
||||
Buffer proof(ecPedersenProofLength);
|
||||
if (secp256k1_elgamal_pedersen_link_prove(
|
||||
ctx,
|
||||
proof.data(),
|
||||
&c1,
|
||||
&c2,
|
||||
&pk,
|
||||
&pcm,
|
||||
params.amt,
|
||||
blindingFactor.data(),
|
||||
params.blindingFactor.data(),
|
||||
contextHash.data()) != 1)
|
||||
{
|
||||
Throw<std::runtime_error>("Amount Linkage Proof generation failed");
|
||||
}
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
Buffer
|
||||
MPTTester::getBalanceLinkageProof(
|
||||
Account const& account,
|
||||
uint256 const& contextHash,
|
||||
Buffer const& pubKey,
|
||||
|
||||
@@ -215,6 +215,8 @@ struct MPTConfidentialSend
|
||||
std::optional<std::vector<std::string>> credentials = std::nullopt;
|
||||
// not an txn param, only used for autofilling
|
||||
std::optional<Buffer> blindingFactor = std::nullopt;
|
||||
std::optional<Buffer> amountCommitment = std::nullopt;
|
||||
std::optional<Buffer> balanceCommitment = std::nullopt;
|
||||
std::optional<std::uint32_t> ownerCount = std::nullopt;
|
||||
std::optional<std::uint32_t> holderCount = std::nullopt;
|
||||
std::optional<std::uint32_t> flags = std::nullopt;
|
||||
@@ -465,11 +467,14 @@ public:
|
||||
|
||||
std::optional<Buffer>
|
||||
getConfidentialSendProof(
|
||||
Account const& sender,
|
||||
std::uint64_t const amount,
|
||||
std::vector<ConfidentialRecipient> const& recipients,
|
||||
Slice const& blindingFactor,
|
||||
std::size_t const nRecipients,
|
||||
uint256 const& contextHash) const;
|
||||
uint256 const& contextHash,
|
||||
PedersenProofParams const& amountParams,
|
||||
PedersenProofParams const& balanceParams) const;
|
||||
|
||||
Buffer
|
||||
getConvertBackProof(
|
||||
@@ -486,7 +491,14 @@ public:
|
||||
getMPTokenVersion(Account const account) const;
|
||||
|
||||
Buffer
|
||||
generatePedersenLinkageProof(
|
||||
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,
|
||||
|
||||
@@ -28,7 +28,7 @@ ConfidentialConvertBack::preflight(PreflightContext const& ctx)
|
||||
if (ctx.tx[sfBlindingFactor].size() != ecBlindingFactorLength)
|
||||
return temMALFORMED;
|
||||
|
||||
if (ctx.tx[sfPedersenCommitment].size() != ecPedersenCommitmentLength)
|
||||
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength)
|
||||
return temMALFORMED;
|
||||
|
||||
// check encrypted amount format after the above basic checks
|
||||
@@ -90,11 +90,11 @@ verifyProofs(
|
||||
// verify el gamal pedersen linkage
|
||||
{
|
||||
Buffer const pedersen{ptr, ecPedersenProofLength};
|
||||
if (auto const ter = verifyPedersenLinkage(
|
||||
if (auto const ter = verifyBalancePcmLinkage(
|
||||
pedersen,
|
||||
(*mptoken)[sfConfidentialBalanceSpending],
|
||||
holderPubKey,
|
||||
tx[sfPedersenCommitment],
|
||||
tx[sfBalanceCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
|
||||
@@ -45,8 +45,16 @@ ConfidentialSend::preflight(PreflightContext const& ctx)
|
||||
|
||||
// Check the length of the ZKProof
|
||||
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
|
||||
if (ctx.tx[sfZKProof].length() !=
|
||||
getMultiCiphertextEqualityProofSize(recipientCount))
|
||||
auto const sizeEquality =
|
||||
getMultiCiphertextEqualityProofSize(recipientCount);
|
||||
auto const sizePedersenLinkage = 2 * ecPedersenProofLength;
|
||||
|
||||
if (ctx.tx[sfZKProof].length() != sizeEquality + sizePedersenLinkage)
|
||||
return temMALFORMED;
|
||||
|
||||
// Check the length of Pedersen commitments
|
||||
if (ctx.tx[sfBalanceCommitment].size() != ecPedersenCommitmentLength ||
|
||||
ctx.tx[sfAmountCommitment].size() != ecPedersenCommitmentLength)
|
||||
return temMALFORMED;
|
||||
|
||||
// Check the encrypted amount formats, this is more expensive so put it at
|
||||
@@ -62,6 +70,125 @@ ConfidentialSend::preflight(PreflightContext const& ctx)
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
verifySendProofs(
|
||||
PreclaimContext const& ctx,
|
||||
std::shared_ptr<SLE const> const& sleSenderMPToken,
|
||||
std::shared_ptr<SLE const> const& sleDestinationMPToken,
|
||||
std::shared_ptr<SLE const> const& sleIssuance)
|
||||
{
|
||||
// Sanity check
|
||||
if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
|
||||
auto const recipientCount = getConfidentialRecipientCount(hasAuditor);
|
||||
auto const proof = ctx.tx[sfZKProof];
|
||||
size_t remainingLength = proof.size();
|
||||
size_t currentOffset = 0;
|
||||
|
||||
// Extract equality proof
|
||||
auto const sizeEquality =
|
||||
getMultiCiphertextEqualityProofSize(recipientCount);
|
||||
if (remainingLength < sizeEquality)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const equalityProof = proof.substr(currentOffset, sizeEquality);
|
||||
currentOffset += sizeEquality;
|
||||
remainingLength -= sizeEquality;
|
||||
|
||||
// Extract Pedersen linkage proof for amount commitment
|
||||
if (remainingLength < ecPedersenProofLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const amountLinkageProof =
|
||||
proof.substr(currentOffset, ecPedersenProofLength);
|
||||
currentOffset += ecPedersenProofLength;
|
||||
remainingLength -= ecPedersenProofLength;
|
||||
|
||||
// Extract Pedersen linkage proof for balance commitment
|
||||
if (remainingLength < ecPedersenProofLength)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
auto const balanceLinkageProof =
|
||||
proof.substr(currentOffset, ecPedersenProofLength);
|
||||
currentOffset += ecPedersenProofLength;
|
||||
remainingLength -= ecPedersenProofLength;
|
||||
|
||||
// todo: Extract range proof once the lib is ready
|
||||
if (remainingLength != 0)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
|
||||
// Prepare receipient list
|
||||
std::vector<ConfidentialRecipient> recipients;
|
||||
recipients.reserve(recipientCount);
|
||||
|
||||
recipients.push_back(
|
||||
{(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfSenderEncryptedAmount]});
|
||||
|
||||
recipients.push_back(
|
||||
{(*sleDestinationMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfDestinationEncryptedAmount]});
|
||||
|
||||
recipients.push_back(
|
||||
{(*sleIssuance)[sfIssuerElGamalPublicKey],
|
||||
ctx.tx[sfIssuerEncryptedAmount]});
|
||||
|
||||
if (hasAuditor)
|
||||
{
|
||||
recipients.push_back(
|
||||
{(*sleIssuance)[sfAuditorElGamalPublicKey],
|
||||
ctx.tx[sfAuditorEncryptedAmount]});
|
||||
}
|
||||
|
||||
// Prepare the context hash
|
||||
auto const contextHash = getSendContextHash(
|
||||
ctx.tx[sfAccount],
|
||||
ctx.tx[sfSequence],
|
||||
ctx.tx[sfMPTokenIssuanceID],
|
||||
ctx.tx[sfDestination],
|
||||
(*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
|
||||
|
||||
// Verify the multi-ciphertext equality proof
|
||||
if (auto const ter = verifyMultiCiphertextEqualityProof(
|
||||
equalityProof, recipients, recipientCount, contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "ConfidentialSend: Equality proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Verify amount linkage
|
||||
if (auto const ter = verifyAmountPcmLinkage(
|
||||
amountLinkageProof,
|
||||
ctx.tx[sfSenderEncryptedAmount],
|
||||
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfAmountCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace()) << "ConfidentialSend: Amount linkage proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
// Verify balance linkage
|
||||
if (auto const ter = verifyBalancePcmLinkage(
|
||||
balanceLinkageProof,
|
||||
(*sleSenderMPToken)[sfConfidentialBalanceSpending],
|
||||
(*sleSenderMPToken)[sfHolderElGamalPublicKey],
|
||||
ctx.tx[sfBalanceCommitment],
|
||||
contextHash);
|
||||
!isTesSuccess(ter))
|
||||
{
|
||||
JLOG(ctx.j.trace())
|
||||
<< "ConfidentialSend: Balance linkage proof failed.";
|
||||
return ter;
|
||||
}
|
||||
|
||||
return tesSUCCESS;
|
||||
}
|
||||
|
||||
TER
|
||||
ConfidentialSend::preclaim(PreclaimContext const& ctx)
|
||||
{
|
||||
@@ -155,52 +282,8 @@ 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 =
|
||||
getConfidentialRecipientCount(requiresAuditor);
|
||||
|
||||
// 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;
|
||||
return verifySendProofs(
|
||||
ctx, sleSenderMPToken, sleDestinationMPToken, sleIssuance);
|
||||
}
|
||||
|
||||
TER
|
||||
@@ -209,13 +292,14 @@ ConfidentialSend::doApply()
|
||||
auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
|
||||
auto const destination = ctx_.tx[sfDestination];
|
||||
|
||||
auto sleSender = view().peek(keylet::mptoken(mptIssuanceID, account_));
|
||||
auto sleDestination =
|
||||
auto sleSenderMPToken =
|
||||
view().peek(keylet::mptoken(mptIssuanceID, account_));
|
||||
auto sleDestinationMPToken =
|
||||
view().peek(keylet::mptoken(mptIssuanceID, destination));
|
||||
|
||||
auto sleDestAcct = view().peek(keylet::account(destination));
|
||||
|
||||
if (!sleSender || !sleDestination || !sleDestAcct)
|
||||
if (!sleSenderMPToken || !sleDestinationMPToken || !sleDestAcct)
|
||||
return tecINTERNAL;
|
||||
|
||||
if (auto err = verifyDepositPreauth(
|
||||
@@ -236,7 +320,8 @@ ConfidentialSend::doApply()
|
||||
|
||||
// Subtract from sender's spending balance
|
||||
{
|
||||
Slice const curSpending = (*sleSender)[sfConfidentialBalanceSpending];
|
||||
Slice const curSpending =
|
||||
(*sleSenderMPToken)[sfConfidentialBalanceSpending];
|
||||
Buffer newSpending(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter =
|
||||
@@ -244,12 +329,13 @@ ConfidentialSend::doApply()
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSender)[sfConfidentialBalanceSpending] = newSpending;
|
||||
(*sleSenderMPToken)[sfConfidentialBalanceSpending] = newSpending;
|
||||
}
|
||||
|
||||
// Subtract from issuer's balance
|
||||
{
|
||||
Slice const curIssuerEnc = (*sleSender)[sfIssuerEncryptedBalance];
|
||||
Slice const curIssuerEnc =
|
||||
(*sleSenderMPToken)[sfIssuerEncryptedBalance];
|
||||
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter =
|
||||
@@ -257,13 +343,14 @@ ConfidentialSend::doApply()
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSender)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
(*sleSenderMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
}
|
||||
|
||||
// Subtract from auditor's balance if present
|
||||
if (auditorEc)
|
||||
{
|
||||
Slice const curAuditorEnc = (*sleSender)[sfAuditorEncryptedBalance];
|
||||
Slice const curAuditorEnc =
|
||||
(*sleSenderMPToken)[sfAuditorEncryptedBalance];
|
||||
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter =
|
||||
@@ -271,24 +358,26 @@ ConfidentialSend::doApply()
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleSender)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
(*sleSenderMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// Add to destination's inbox balance
|
||||
{
|
||||
Slice const curInbox = (*sleDestination)[sfConfidentialBalanceInbox];
|
||||
Slice const curInbox =
|
||||
(*sleDestinationMPToken)[sfConfidentialBalanceInbox];
|
||||
Buffer newInbox(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter = homomorphicAdd(curInbox, destEc, newInbox);
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestination)[sfConfidentialBalanceInbox] = newInbox;
|
||||
(*sleDestinationMPToken)[sfConfidentialBalanceInbox] = newInbox;
|
||||
}
|
||||
|
||||
// Add to issuer's balance
|
||||
{
|
||||
Slice const curIssuerEnc = (*sleDestination)[sfIssuerEncryptedBalance];
|
||||
Slice const curIssuerEnc =
|
||||
(*sleDestinationMPToken)[sfIssuerEncryptedBalance];
|
||||
Buffer newIssuerEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter =
|
||||
@@ -296,14 +385,14 @@ ConfidentialSend::doApply()
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestination)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
(*sleDestinationMPToken)[sfIssuerEncryptedBalance] = newIssuerEnc;
|
||||
}
|
||||
|
||||
// Add to auditor's balance if present
|
||||
if (auditorEc)
|
||||
{
|
||||
Slice const curAuditorEnc =
|
||||
(*sleDestination)[sfAuditorEncryptedBalance];
|
||||
(*sleDestinationMPToken)[sfAuditorEncryptedBalance];
|
||||
Buffer newAuditorEnc(ecGamalEncryptedTotalLength);
|
||||
|
||||
if (TER const ter =
|
||||
@@ -311,15 +400,15 @@ ConfidentialSend::doApply()
|
||||
!isTesSuccess(ter))
|
||||
return tecINTERNAL;
|
||||
|
||||
(*sleDestination)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
(*sleDestinationMPToken)[sfAuditorEncryptedBalance] = newAuditorEnc;
|
||||
}
|
||||
|
||||
// increment version
|
||||
incrementConfidentialVersion(*sleSender);
|
||||
incrementConfidentialVersion(*sleDestination);
|
||||
incrementConfidentialVersion(*sleSenderMPToken);
|
||||
incrementConfidentialVersion(*sleDestinationMPToken);
|
||||
|
||||
view().update(sleSender);
|
||||
view().update(sleDestination);
|
||||
view().update(sleSenderMPToken);
|
||||
view().update(sleDestinationMPToken);
|
||||
return tesSUCCESS;
|
||||
}
|
||||
} // namespace ripple
|
||||
|
||||
Reference in New Issue
Block a user