1#include <xrpl/protocol/ConfidentialTransfer.h>
3#include <xrpl/basics/Buffer.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/basics/base_uint.h>
6#include <xrpl/basics/contract.h>
7#include <xrpl/beast/utility/instrumentation.h>
8#include <xrpl/protocol/AccountID.h>
9#include <xrpl/protocol/LedgerFormats.h>
10#include <xrpl/protocol/Protocol.h>
11#include <xrpl/protocol/SField.h>
12#include <xrpl/protocol/STBlob.h>
13#include <xrpl/protocol/STLedgerEntry.h>
14#include <xrpl/protocol/STObject.h>
15#include <xrpl/protocol/TER.h>
16#include <xrpl/protocol/UintTypes.h>
18#include <openssl/rand.h>
19#include <utility/mpt_utility.h>
21#include <mpt_protocol.h>
23#include <secp256k1_mpt.h>
44toIssuanceId(
UInt192 const& issuance)
47 std::memcpy(res.bytes, issuance.data(), kMPT_ISSUANCE_ID_SIZE);
57mpt_confidential_participant
60 mpt_confidential_participant p{};
77 mpt_get_send_context_hash(
79 toIssuanceId(issuanceID),
81 toAccountId(destination),
95 mpt_get_clawback_context_hash(
97 toIssuanceId(issuanceID),
108 mpt_get_convert_context_hash(
109 toAccountId(account), toIssuanceId(issuanceID), sequence, result.
data());
121 mpt_get_convert_back_context_hash(
122 toAccountId(account), toIssuanceId(issuanceID), sequence, version, result.
data());
132 UNREACHABLE(
"xrpl::makeEcPair : callers must pre-validate ciphertext length");
137 auto parsePubKey = [](
Slice const& slice, secp256k1_pubkey& out) {
145 if (parsePubKey(s1, pair.
c1) != 1 || parsePubKey(s2, pair.
c2) != 1)
154 auto serializePubKey = [](secp256k1_pubkey
const& pub,
unsigned char* out) {
156 auto const ret = secp256k1_ec_pubkey_serialize(
162 auto const ptr = buffer.
data();
163 bool const res1 = serializePubKey(pair.
c1, ptr);
188 secp256k1_pubkey point;
201 if (!pairA || !pairB)
205 if (
auto res = secp256k1_elgamal_add(
224 if (!pairA || !pairB)
228 if (
auto const res = secp256k1_elgamal_subtract(
267 if (mpt_encrypt_amount(amt, pubKeySlice.
data(), blindingFactor.
data(), out.
data()) != 0)
280 "xrpl::encryptCanonicalZeroAmount : callers must pre-validate public key length");
286 secp256k1_pubkey pubKey;
287 if (
auto res = secp256k1_ec_pubkey_parse(
293 "xrpl::encryptCanonicalZeroAmount : public key read from the ledger must already be "
299 if (
auto res = generate_canonical_encrypted_zero(
305 "xrpl::encryptCanonicalZeroAmount : canonical zero generation cannot fail for a "
316 uint64_t
const amount,
317 Slice const& blindingFactor,
330 "xrpl::verifyRevealedAmount : callers must pre-validate holder/issuer field lengths");
335 auto const holderP = toParticipant(holder);
336 auto const issuerP = toParticipant(issuer);
337 mpt_confidential_participant auditorP{};
338 mpt_confidential_participant
const* auditorPtr =
nullptr;
346 "xrpl::verifyRevealedAmount : callers must pre-validate auditor field lengths");
350 auditorP = toParticipant(*auditor);
351 auditorPtr = &auditorP;
354 if (mpt_verify_revealed_amount(amount, blindingFactor.
data(), &holderP, &issuerP, auditorPtr) !=
369 if (!
object.isFieldPresent(sfHolderEncryptedAmount) ||
370 !
object.isFieldPresent(sfIssuerEncryptedAmount))
374 "xrpl::checkEncryptedAmountFormat : callers already enforce that these fields are "
386 bool const hasAuditor =
object.isFieldPresent(sfAuditorEncryptedAmount);
406 issuance.
getType() == ltMPTOKEN_ISSUANCE,
407 "xrpl::isIssuerMirrorCurrent : issuance MPTokenIssuance object");
409 mptoken.getType() == ltMPTOKEN,
"xrpl::isIssuerMirrorCurrent : mptoken MPToken object");
411 return mptoken.isFieldPresent(sfIssuerEncryptedBalance) &&
412 mptoken[~sfIssuerKeyMirrorEpoch].value_or(0) == issuance[~sfIssuerKeyEpoch].value_or(0);
419 issuance.
getType() == ltMPTOKEN_ISSUANCE,
420 "xrpl::isAuditorMirrorCurrent : issuance MPTokenIssuance object");
422 mptoken.getType() == ltMPTOKEN,
"xrpl::isAuditorMirrorCurrent : mptoken MPToken object");
427 return mptoken.isFieldPresent(sfAuditorEncryptedBalance) &&
428 mptoken[~sfAuditorKeyMirrorEpoch].value_or(0) == issuance[~sfAuditorKeyEpoch].value_or(0);
441 issuance.
getType() == ltMPTOKEN_ISSUANCE,
442 "xrpl::setIssuerMirrorEpoch : issuance MPTokenIssuance object");
444 mptoken.getType() == ltMPTOKEN,
"xrpl::setIssuerMirrorEpoch : mptoken MPToken object");
448 if (
auto const epoch = issuance[~sfIssuerKeyEpoch].value_or(0); epoch != 0)
449 mptoken[sfIssuerKeyMirrorEpoch] = epoch;
456 issuance.
getType() == ltMPTOKEN_ISSUANCE,
457 "xrpl::setAuditorMirrorEpoch : issuance MPTokenIssuance object");
459 mptoken.getType() == ltMPTOKEN,
"xrpl::setAuditorMirrorEpoch : mptoken MPToken object");
461 if (!mptoken.isFieldPresent(sfAuditorEncryptedBalance))
464 if (
auto const epoch = issuance[~sfAuditorKeyEpoch].value_or(0); epoch != 0)
465 mptoken[sfAuditorKeyMirrorEpoch] = epoch;
481 UNREACHABLE(
"xrpl::verifySchnorrProof : callers must pre-validate proof/public key length");
486 if (mpt_verify_convert_proof(proofSlice.
data(), pubKeySlice.
data(), contextHash.
data()) != 0)
494 uint64_t
const amount,
496 Slice const& pubKeySlice,
497 Slice const& ciphertext,
505 "xrpl::verifyClawbackProof : callers must pre-validate ciphertext/public "
511 if (mpt_verify_clawback_proof(
512 proof.
data(), amount, pubKeySlice.
data(), ciphertext.
data(), contextHash.
data()) != 0)
527 Slice const& spendingBalance,
528 Slice const& amountCommitment,
529 Slice const& balanceCommitment,
545 "xrpl::verifySendProof : callers must pre-validate proof/participant/commitment "
552 participants.
reserve(recipientCount);
553 participants.
push_back(toParticipant(sender));
554 participants.
push_back(toParticipant(destination));
555 participants.
push_back(toParticipant(issuer));
562 UNREACHABLE(
"xrpl::verifySendProof : callers must pre-validate auditor field lengths");
566 participants.
push_back(toParticipant(*auditor));
568 if (participants.
size() != recipientCount)
572 "xrpl::verifySendProof : participant count must match the requested recipient "
578 if (mpt_verify_send_proof(
582 spendingBalance.
data(),
583 amountCommitment.
data(),
584 balanceCommitment.
data(),
585 contextHash.
data()) != 0)
596 Slice const& pubKeySlice,
597 Slice const& spendingBalance,
598 Slice const& balanceCommitment,
608 "xrpl::verifyConvertBackProof : callers must pre-validate proof/public "
609 "key/balance/commitment lengths");
614 if (mpt_verify_convert_back_proof(
617 spendingBalance.
data(),
618 balanceCommitment.
data(),
620 contextHash.
data()) != 0)
Like std::vector<char> but better.
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
LedgerEntryType getType() const
bool isFieldPresent(SField const &field) const
An immutable linear range of bytes.
std::size_t length() const noexcept
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Keylet account(AccountID const &id) noexcept
AccountID root.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool areMirrorsCurrent(SLE const &issuance, SLE const &mptoken)
Checks whether each mirror a holder is required to have is encrypted under the issuance's currently r...
constexpr std::size_t kEcPubKeyLength
Length of EC public key (compressed).
constexpr std::uint8_t kEcCompressedPrefixEvenY
Compressed EC point prefix for even y-coordinate.
NotTEC checkEncryptedAmountFormat(STObject const &object)
Validates the format of encrypted amount fields in a transaction.
void setMirrorEpochs(SLE const &issuance, SLE &mptoken)
Set the holder's MPToken mirror epochs to match the issuance's current key epochs.
static auto sum(TCollection const &col)
std::optional< Buffer > rerandomizeCiphertext(Slice const &ciphertext, Slice const &pubKeySlice, Slice const &randomness)
Re-randomizes an ElGamal ciphertext without changing its plaintext.
constexpr std::size_t kEcBlindingFactorLength
Length of the EC blinding factor in bytes.
std::optional< Buffer > encryptCanonicalZeroAmount(Slice const &pubKeySlice, AccountID const &account, MPTID const &mptId)
Generates the canonical zero encryption for a specific MPToken.
UInt256 getConvertContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence)
Generates the context hash for ConfidentialMPTConvert transactions.
constexpr std::size_t kCompressedEcPointLength
Length of EC point (compressed).
UInt256 getSendContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, AccountID const &destination, std::uint32_t version)
Generates the context hash for ConfidentialMPTSend transactions.
bool isIssuerMirrorCurrent(SLE const &issuance, SLE const &mptoken)
Checks whether a holder's issuer mirror is encrypted under the issuance's currently registered issuer...
UInt256 getConvertBackContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, std::uint32_t version)
Generates the context hash for ConfidentialMPTConvertBack transactions.
constexpr std::size_t kEcClawbackProofLength
Length of the ZKProof for ConfidentialMPTClawback.
std::optional< Buffer > encryptAmount(uint64_t const amt, Slice const &pubKeySlice, Slice const &blindingFactor)
Encrypts an amount using ElGamal encryption.
constexpr std::uint8_t kEcCompressedPrefixOddY
Compressed EC point prefix for odd y-coordinate.
bool isValidCompressedECPoint(Slice const &buffer)
Verifies that a buffer contains a valid, parsable compressed EC point.
constexpr std::size_t kEcSchnorrProofLength
Length of Schnorr ZKProof for public key registration (compact form) in bytes.
bool isAuditorMirrorCurrent(SLE const &issuance, SLE const &mptoken)
Checks whether a holder's auditor mirror is encrypted under the issuance's currently registered audit...
constexpr uint8_t getConfidentialRecipientCount(bool hasAuditor)
Returns the number of recipients in a confidential transfer.
std::optional< EcPair > makeEcPair(Slice const &buffer)
Parses an ElGamal ciphertext into two secp256k1 public key components.
TER verifySendProof(Slice const &proof, ConfidentialRecipient const &sender, ConfidentialRecipient const &destination, ConfidentialRecipient const &issuer, std::optional< ConfidentialRecipient > const &auditor, Slice const &spendingBalance, Slice const &amountCommitment, Slice const &balanceCommitment, UInt256 const &contextHash)
Verifies all zero-knowledge proofs for a ConfidentialMPTSend transaction.
constexpr std::size_t kEcGamalEncryptedTotalLength
EC ElGamal ciphertext length: two compressed EC points concatenated.
TER verifyClawbackProof(uint64_t const amount, Slice const &proof, Slice const &pubKeySlice, Slice const &ciphertext, UInt256 const &contextHash)
Verifies a compact sigma clawback proof.
std::optional< Buffer > serializeEcPair(EcPair const &pair)
Serializes an EcPair into compressed form.
TER verifyRevealedAmount(uint64_t const amount, Slice const &blindingFactor, ConfidentialRecipient const &holder, ConfidentialRecipient const &issuer, std::optional< ConfidentialRecipient > const &auditor)
Verifies revealed amount encryptions for all recipients.
constexpr std::size_t kEcConvertBackProofLength
128 bytes compact sigma proof + 688 bytes single bulletproof.
bool isValidCiphertext(Slice const &buffer)
Verifies that a buffer contains two valid, parsable EC public keys.
TER verifyConvertBackProof(Slice const &proof, Slice const &pubKeySlice, Slice const &spendingBalance, Slice const &balanceCommitment, uint64_t amount, UInt256 const &contextHash)
Verifies all zero-knowledge proofs for a ConfidentialMPTConvertBack transaction.
TERSubset< CanCvtToNotTEC > NotTEC
constexpr std::size_t kEcPedersenCommitmentLength
Length of Pedersen Commitment (compressed).
constexpr std::size_t kEcCiphertextComponentLength
Length of one compressed EC point component in an EC ElGamal ciphertext.
std::optional< Buffer > homomorphicSubtract(Slice const &a, Slice const &b)
Homomorphically subtracts two ElGamal ciphertexts.
BaseUInt< 192 > MPTID
MPTID is a 192-bit value representing MPT Issuance ID, which is a concatenation of a 32-bit sequence ...
void setAuditorMirrorEpoch(SLE const &issuance, SLE &mptoken)
Set the holder's auditor mirror epoch to match the issuance's current auditor key epoch.
void setIssuerMirrorEpoch(SLE const &issuance, SLE &mptoken)
Set the holder's issuer mirror epoch to match the issuance's current issuer key epoch.
secp256k1_context const * secp256k1Context()
UInt256 getClawbackContextHash(AccountID const &account, UInt192 const &issuanceID, std::uint32_t sequence, AccountID const &holder)
Generates the context hash for ConfidentialMPTClawback transactions.
Buffer generateBlindingFactor()
Generates a cryptographically secure blinding factor (size=xrpl::kEcBlindingFactorLength).
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
constexpr std::size_t kEcSendProofLength
192 bytes compact sigma proof + 754 bytes double bulletproof.
TERSubset< CanCvtToTER > TER
TER verifySchnorrProof(Slice const &pubKeySlice, Slice const &proofSlice, UInt256 const &contextHash)
Verifies a Schnorr proof of knowledge of an ElGamal private key.
std::optional< Buffer > homomorphicAdd(Slice const &a, Slice const &b)
Homomorphically adds two ElGamal ciphertexts.
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Bundles an ElGamal public key with its associated encrypted amount.
Slice encryptedAmount
The encrypted amount ciphertext (size=xrpl::kEcGamalEncryptedTotalLength).
Slice publicKey
The recipient's ElGamal public key (size=xrpl::kEcPubKeyLength).
Holds two secp256k1 public key components representing an ElGamal ciphertext (C1, C2).
secp256k1_pubkey c2
Second ElGamal ciphertext component.
secp256k1_pubkey c1
First ElGamal ciphertext component.