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/core/ServiceRegistry.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/ledger/helpers/TokenHelpers.h>
9#include <xrpl/protocol/ConfidentialTransfer.h>
10#include <xrpl/protocol/Feature.h>
11#include <xrpl/protocol/Indexes.h>
12#include <xrpl/protocol/LedgerFormats.h>
13#include <xrpl/protocol/MPTIssue.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>
78 auto const account = ctx.
tx[sfAccount];
79 auto const issuanceID = ctx.
tx[sfMPTokenIssuanceID];
80 auto const amount = ctx.
tx[sfMPTAmount];
87 if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) ||
88 !sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
95 if (sleIssuance->getAccountID(sfIssuer) == account)
99 bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
103 if (requiresAuditor != hasAuditor)
110 auto const mptIssue =
MPTIssue{issuanceID};
126 auto const mptAmount =
139 auto const hasHolderKeyOnLedger = sleMptoken->isFieldPresent(sfHolderEncryptionKey);
143 if (!hasHolderKeyOnLedger && !hasHolderKeyInTx)
147 if (hasHolderKeyOnLedger && hasHolderKeyInTx)
155 if (hasHolderKeyInTx)
157 holderPubKey = ctx.
tx[sfHolderEncryptionKey];
159 auto const contextHash =
170 holderPubKey = (*sleMptoken)[sfHolderEncryptionKey];
178 .publicKey = (*sleIssuance)[sfAuditorEncryptionKey],
179 .encryptedAmount = ctx.
tx[sfAuditorEncryptedAmount],
183 auto const blindingFactor = ctx.
tx[sfBlindingFactor];
186 Slice(blindingFactor.data(), blindingFactor.size()),
188 .publicKey = holderPubKey,
189 .encryptedAmount = ctx.tx[sfHolderEncryptedAmount],
192 .publicKey = (*sleIssuance)[sfIssuerEncryptionKey],
193 .encryptedAmount = ctx.tx[sfIssuerEncryptedAmount],
210 auto const mptIssuanceID =
ctx_.tx[sfMPTokenIssuanceID];
220 auto const amtToConvert =
ctx_.tx[sfMPTAmount];
221 auto const amt = (*sleMptoken)[~sfMPTAmount].valueOr(0);
223 if (
ctx_.tx.isFieldPresent(sfHolderEncryptionKey))
224 (*sleMptoken)[sfHolderEncryptionKey] =
ctx_.tx[sfHolderEncryptionKey];
228 auto const currentCOA = (*sleIssuance)[~sfConfidentialOutstandingAmount].valueOr(0);
232 (*sleMptoken)[sfMPTAmount] = amt - amtToConvert;
233 (*sleIssuance)[sfConfidentialOutstandingAmount] = currentCOA + amtToConvert;
235 auto const holderEc =
ctx_.tx[sfHolderEncryptedAmount];
236 auto const issuerEc =
ctx_.tx[sfIssuerEncryptedAmount];
237 auto const auditorEc =
ctx_.tx[~sfAuditorEncryptedAmount];
242 if (sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
243 sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
244 sleMptoken->isFieldPresent(sfConfidentialBalanceSpending))
252 JLOG(
ctx_.journal.error())
253 <<
"ConfidentialMPTConvert failed homomorphic add for holder inbox.";
258 (*sleMptoken)[sfConfidentialBalanceInbox] = std::move(*
sum);
267 JLOG(
ctx_.journal.error())
268 <<
"ConfidentialMPTConvert failed homomorphic add for issuer balance.";
273 (*sleMptoken)[sfIssuerEncryptedBalance] = std::move(*
sum);
279 if (!sleMptoken->isFieldPresent(sfAuditorEncryptedBalance))
286 JLOG(
ctx_.journal.error())
287 <<
"ConfidentialMPTConvert failed homomorphic add for auditor balance.";
292 (*sleMptoken)[sfAuditorEncryptedBalance] = std::move(*
sum);
296 !sleMptoken->isFieldPresent(sfIssuerEncryptedBalance) &&
297 !sleMptoken->isFieldPresent(sfConfidentialBalanceInbox) &&
298 !sleMptoken->isFieldPresent(sfConfidentialBalanceSpending) &&
299 !sleMptoken->isFieldPresent(sfAuditorEncryptedBalance))
302 (*sleMptoken)[sfConfidentialBalanceInbox] = holderEc;
303 (*sleMptoken)[sfIssuerEncryptedBalance] = issuerEc;
304 (*sleMptoken)[sfConfidentialBalanceVersion] = 0;
307 (*sleMptoken)[sfAuditorEncryptedBalance] = *auditorEc;
312 (*sleMptoken)[sfHolderEncryptionKey],
accountID_, mptIssuanceID);
317 (*sleMptoken)[sfConfidentialBalanceSpending] = std::move(*zeroBalance);
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 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 mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
NotTEC checkEncryptedAmountFormat(STObject const &object)
Validates the format of encrypted amount fields in a transaction.
static auto sum(TCollection const &col)
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 > encryptCanonicalZeroAmount(Slice const &pubKeySlice, AccountID const &account, MPTID const &mptId)
Generates the canonical zero encryption for a specific MPToken.
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
uint256 getConvertContextHash(AccountID const &account, uint192 const &issuanceID, std::uint32_t sequence)
Generates the context hash for ConfidentialMPTConvert transactions.
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.
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.