1#include <xrpl/tx/transactors/token/ConfidentialMPTConvert.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/beast/utility/Journal.h>
6#include <xrpl/beast/utility/instrumentation.h>
7#include <xrpl/core/ServiceRegistry.h>
8#include <xrpl/ledger/ReadView.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/MPTIssue.h>
15#include <xrpl/protocol/Protocol.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/TER.h>
18#include <xrpl/protocol/XRPAmount.h>
19#include <xrpl/tx/Transactor.h>
76 auto const account = ctx.
tx[sfAccount];
77 auto const issuanceID = ctx.
tx[sfMPTokenIssuanceID];
78 auto const amount = ctx.
tx[sfMPTAmount];
85 if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) ||
86 !sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
93 if (sleIssuance->getAccountID(sfIssuer) == account)
97 "xrpl::ConfidentialMPTConvert::preclaim : issuer derived from the MPT ID must "
98 "match the ledger's stored issuer");
104 bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
108 if (requiresAuditor != hasAuditor)
120 sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
126 auto const mptIssue =
MPTIssue{issuanceID};
142 auto const mptAmount =
155 auto const hasHolderKeyOnLedger = sleMptoken->isFieldPresent(sfHolderEncryptionKey);
159 if (!hasHolderKeyOnLedger && !hasHolderKeyInTx)
163 if (hasHolderKeyOnLedger && hasHolderKeyInTx)
171 if (hasHolderKeyInTx)
173 holderPubKey = ctx.
tx[sfHolderEncryptionKey];
175 auto const contextHash =
186 holderPubKey = (*sleMptoken)[sfHolderEncryptionKey];
194 .publicKey = (*sleIssuance)[sfAuditorEncryptionKey],
195 .encryptedAmount = ctx.
tx[sfAuditorEncryptedAmount],
199 auto const blindingFactor = ctx.
tx[sfBlindingFactor];
202 Slice(blindingFactor.data(), blindingFactor.size()),
204 .publicKey = holderPubKey,
205 .encryptedAmount = ctx.tx[sfHolderEncryptedAmount],
208 .publicKey = (*sleIssuance)[sfIssuerEncryptionKey],
209 .encryptedAmount = ctx.tx[sfIssuerEncryptedAmount],
226 auto const mptIssuanceID =
ctx_.tx[sfMPTokenIssuanceID];
233 "xrpl::ConfidentialMPTConvert::doApply : preclaim already validated the MPToken "
244 "xrpl::ConfidentialMPTConvert::doApply : preclaim already validated the issuance "
250 auto const amtToConvert =
ctx_.tx[sfMPTAmount];
251 auto const amt = (*sleMptoken)[~sfMPTAmount].valueOr(0);
253 if (
ctx_.tx.isFieldPresent(sfHolderEncryptionKey))
254 (*sleMptoken)[sfHolderEncryptionKey] =
ctx_.tx[sfHolderEncryptionKey];
258 auto const currentCOA = (*sleIssuance)[~sfConfidentialOutstandingAmount].valueOr(0);
262 (*sleMptoken)[sfMPTAmount] = amt - amtToConvert;
263 (*sleIssuance)[sfConfidentialOutstandingAmount] = currentCOA + amtToConvert;
265 auto const holderEc =
ctx_.tx[sfHolderEncryptedAmount];
266 auto const issuerEc =
ctx_.tx[sfIssuerEncryptedAmount];
267 auto const auditorEc =
ctx_.tx[~sfAuditorEncryptedAmount];
272 if (sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
273 sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
274 sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
282 JLOG(
ctx_.journal.error())
283 <<
"ConfidentialMPTConvert failed homomorphic add for holder inbox.";
288 (*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*
sum);
297 JLOG(
ctx_.journal.error())
298 <<
"ConfidentialMPTConvert failed homomorphic add for issuer balance.";
303 (*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*
sum);
309 if (!sleMptoken->isFieldPresent(sfAuditorEncryptedBalance))
313 "xrpl::ConfidentialMPTConvert::doApply : issuance-level auditing implies "
314 "the MPToken already carries an auditor balance");
323 JLOG(
ctx_.journal.error())
324 <<
"ConfidentialMPTConvert failed homomorphic add for auditor balance.";
329 (*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*
sum);
333 !sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
334 !sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
335 !sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) &&
336 !sleMptoken->isFieldPresent(sfAuditorEncryptedBalance))
339 (*sleMptoken)[sfConfidentialBalanceInbox] = holderEc;
340 (*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
341 (*sleMptoken)[sfConfidentialBalanceVersion] = 0;
344 (*sleMptoken)[sfAuditorEncryptedBalance] = *auditorEc;
347 if (
view().rules().enabled(featureConfidentialMPTKeyRotation))
353 (*sleMptoken)[sfHolderEncryptionKey],
accountID_, mptIssuanceID);
359 "xrpl::ConfidentialMPTConvert::doApply : canonical zero encryption cannot fail "
360 "for an already-valid holder public key");
365 (*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*zeroBalance);
373 "xrpl::ConfidentialMPTConvert::doApply : confidential balance fields must be all "
374 "present or all absent");
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 NotTEC preflight(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 XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const & getIssuer() const
virtual Rules const & rules() const =0
Returns the tx processing rules.
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_
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
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...
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 > 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::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 kEcSchnorrProofLength
Length of Schnorr ZKProof for public key registration (compact form) in bytes.
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.
TERSubset< CanCvtToNotTEC > NotTEC
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.
TER verifySchnorrProof(Slice const &pubKeySlice, Slice const &proofSlice, UInt256 const &contextHash)
Verifies a Schnorr proof of knowledge of an ElGamal private key.
constexpr std::uint64_t kMaxMpTokenAmount
The maximum amount of MPTokenIssuance.
std::optional< Buffer > homomorphicAdd(Slice const &a, Slice const &b)
Homomorphically adds two ElGamal ciphertexts.
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const ¤cy, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j, SpendableHandling includeFullBalance=SpendableHandling::SimpleBalance)
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.