1#include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/CredentialHelpers.h>
9#include <xrpl/ledger/helpers/TokenHelpers.h>
10#include <xrpl/protocol/ConfidentialTransfer.h>
11#include <xrpl/protocol/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/LedgerFormats.h>
14#include <xrpl/protocol/Protocol.h>
15#include <xrpl/protocol/SField.h>
16#include <xrpl/protocol/TER.h>
17#include <xrpl/protocol/XRPAmount.h>
18#include <xrpl/tx/Transactor.h>
35 auto const account = ctx.
tx[sfAccount];
40 if (account == issuer)
44 if (account == ctx.
tx[sfDestination])
48 if (ctx.
tx[sfDestination] == issuer)
108 if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
112 "xrpl::detail::verifySendProofs : caller must pre-validate sender/destination/"
113 "issuance existence");
125 .publicKey = (*sleIssuance)[sfAuditorEncryptionKey],
126 .encryptedAmount = ctx.
tx[sfAuditorEncryptedAmount],
132 ctx.
tx[sfMPTokenIssuanceID],
134 ctx.
tx[sfDestination],
135 (*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
140 .publicKey = (*sleSenderMPToken)[sfHolderEncryptionKey],
141 .encryptedAmount = ctx.tx[sfSenderEncryptedAmount],
144 .publicKey = (*sleDestinationMPToken)[sfHolderEncryptionKey],
145 .encryptedAmount = ctx.tx[sfDestinationEncryptedAmount],
148 .publicKey = (*sleIssuance)[sfIssuerEncryptionKey],
149 .encryptedAmount = ctx.tx[sfIssuerEncryptedAmount],
152 (*sleSenderMPToken)[sfConfidentialBalanceSpending],
153 ctx.
tx[sfAmountCommitment],
154 ctx.
tx[sfBalanceCommitment],
164 auto const account = ctx.
tx[sfAccount];
169 auto const destination = ctx.
tx[sfDestination];
175 if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) &&
182 auto const mptIssuanceID = ctx.
tx[sfMPTokenIssuanceID];
188 if (!sleIssuance->isFlag(lsfMPTCanTransfer))
192 if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
198 if ((*sleIssuance)[~sfTransferFee].value_or(0) > 0)
202 if (!sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
206 bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
210 if (requiresAuditor != hasAuditor)
214 if (sleIssuance->getAccountID(sfIssuer) == ctx.
tx[sfAccount])
218 "xrpl::ConfidentialMPTSend::preclaim : issuer derived from the MPT ID must match "
219 "the ledger's stored issuer");
226 if (!sleSenderMPToken)
230 if (!sleSenderMPToken->isFieldPresent(sfHolderEncryptionKey) ||
231 !sleSenderMPToken->isFieldPresent(sfConfidentialBalanceSpending) ||
232 !sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
239 if (!sleDestinationMPToken)
243 if (!sleDestinationMPToken->isFieldPresent(sfHolderEncryptionKey) ||
244 !sleDestinationMPToken->isFieldPresent(sfConfidentialBalanceInbox) ||
245 !sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance))
252 if (requiresAuditor &&
253 (!sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance) ||
254 !sleDestinationMPToken->isFieldPresent(sfAuditorEncryptedBalance)))
258 "xrpl::ConfidentialMPTSend::preclaim : issuance-level auditing implies both "
259 "MPTokens already carry an auditor balance");
265 MPTIssue const mptIssue(mptIssuanceID);
285 auto const preauthErr =
296 auto const mptIssuanceID =
ctx_.tx[sfMPTokenIssuanceID];
297 auto const destination =
ctx_.tx[sfDestination];
305 if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance || !sleDestAcct)
309 "xrpl::ConfidentialMPTSend::doApply : preclaim already validated these objects "
321 auto const senderEc =
ctx_.tx[sfSenderEncryptedAmount];
322 auto const destEc =
ctx_.tx[sfDestinationEncryptedAmount];
323 auto const issuerEc =
ctx_.tx[sfIssuerEncryptedAmount];
324 auto const proof =
ctx_.tx[sfZKProof];
327 auto const auditorEc =
ctx_.tx[~sfAuditorEncryptedAmount];
331 auto const curSpending = (*sleSenderMPToken)[sfConfidentialBalanceSpending];
336 JLOG(
ctx_.journal.error())
337 <<
"ConfidentialMPTSend failed homomorphic subtract for sender spending balance.";
342 (*sleSenderMPToken)[sfConfidentialBalanceSpending] = std::move(*newSpending);
347 auto const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
352 JLOG(
ctx_.journal.error())
353 <<
"ConfidentialMPTSend failed homomorphic subtract for sender issuer balance.";
358 (*sleSenderMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
364 auto const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
369 JLOG(
ctx_.journal.error())
370 <<
"ConfidentialMPTSend failed homomorphic subtract for sender auditor balance.";
375 (*sleSenderMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
381 destEc, (*sleDestinationMPToken)[sfHolderEncryptionKey], sendChallenge);
382 if (!rerandomizedDestEc)
385 JLOG(
ctx_.journal.error())
386 <<
"ConfidentialMPTSend failed to rerandomize destination inbox ciphertext.";
391 auto const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
396 JLOG(
ctx_.journal.error())
397 <<
"ConfidentialMPTSend failed homomorphic add for destination inbox.";
402 (*sleDestinationMPToken)[sfConfidentialBalanceInbox] = std::move(*newInbox);
407 auto rerandomizedIssuerEc =
409 if (!rerandomizedIssuerEc)
412 JLOG(
ctx_.journal.error())
413 <<
"ConfidentialMPTSend failed to rerandomize destination issuer ciphertext.";
418 auto const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
419 auto newIssuerEnc =
homomorphicAdd(curIssuerEnc, *rerandomizedIssuerEc);
423 JLOG(
ctx_.journal.error())
424 <<
"ConfidentialMPTSend failed homomorphic add for destination issuer balance.";
429 (*sleDestinationMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
436 *auditorEc, (*sleIssuance)[sfAuditorEncryptionKey], sendChallenge);
437 if (!rerandomizedAuditorEc)
440 JLOG(
ctx_.journal.error())
441 <<
"ConfidentialMPTSend failed to rerandomize destination auditor ciphertext.";
446 auto const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
447 auto newAuditorEnc =
homomorphicAdd(curAuditorEnc, *rerandomizedAuditorEc);
451 JLOG(
ctx_.journal.error())
452 <<
"ConfidentialMPTSend failed homomorphic add for destination auditor balance.";
457 (*sleDestinationMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
A generic endpoint for log messages.
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void update(SLE::ref sle)=0
Indicate changes to a peeked SLE.
void visitInvariantEntry(bool isDelete, std::shared_ptr< SLE const > const &before, std::shared_ptr< SLE const > const &after) override
static TER preclaim(PreclaimContext const &ctx)
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
static bool checkExtraFeatures(PreflightContext const &ctx)
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
static NotTEC preflight(PreflightContext const &ctx)
AccountID const & getIssuer() const
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual SLE::const_pointer read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
bool isFieldPresent(SField const &field) const
SeqProxy getSeqProxy() const
constexpr std::uint32_t value() const
An immutable linear range of bytes.
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const accountID_
NotTEC checkFields(STTx const &tx, Rules const &rules, beast::Journal j)
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
static 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)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
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.
constexpr std::uint32_t kConfidentialFeeMultiplier
Extra base fee multiplier charged to confidential MPT transactions.
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
bool isValidCompressedECPoint(Slice const &buffer)
Verifies that a buffer contains a valid, parsable compressed EC point.
constexpr std::size_t kEcGamalEncryptedTotalLength
EC ElGamal ciphertext length: two compressed EC points concatenated.
bool isValidCiphertext(Slice const &buffer)
Verifies that a buffer contains two valid, parsable EC public keys.
TERSubset< CanCvtToNotTEC > NotTEC
std::optional< Buffer > homomorphicSubtract(Slice const &a, Slice const &b)
Homomorphically subtracts two ElGamal ciphertexts.
TER cleanupExpiredCredentials(STTx const &tx, ApplyView &view, beast::Journal j)
Remove expired credentials referenced by the transaction.
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.
constexpr std::size_t kEcSendProofLength
192 bytes compact sigma proof + 754 bytes double bulletproof.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
TER requireAuth(ReadView const &view, MPTIssue const &mptIssue, AccountID const &account, AuthType authType=AuthType::Legacy, std::uint8_t depth=0)
Check if the account lacks required authorization for MPT.
void incrementConfidentialVersion(STObject &mptoken)
Increments the confidential balance version counter on an MPToken.
std::optional< Buffer > homomorphicAdd(Slice const &a, Slice const &b)
Homomorphically adds two ElGamal ciphertexts.
TER checkDepositPreauth(STTx const &tx, ReadView const &view, AccountID const &src, AccountID const &dst, std::shared_ptr< SLE const > const &sleDst, beast::Journal j)
Check whether src is authorized to deposit to dst.
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.
Bundles an ElGamal public key with its associated encrypted amount.
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.