xrpld
Loading...
Searching...
No Matches
ConfidentialMPTSend.cpp
1#include <xrpl/tx/transactors/token/ConfidentialMPTSend.h>
2
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>
19
20#include <memory>
21#include <optional>
22#include <utility>
23
24namespace xrpl {
25
26bool
28{
29 return !ctx.tx.isFieldPresent(sfCredentialIDs) || ctx.rules.enabled(featureCredentials);
30}
31
34{
35 auto const account = ctx.tx[sfAccount];
36 auto const issuer = MPTIssue(ctx.tx[sfMPTokenIssuanceID]).getIssuer();
37
38 // ConfidentialMPTSend only allows holder to holder, holder to second account,
39 // and second account to holder transfers. So issuer cannot be the sender.
40 if (account == issuer)
41 return temMALFORMED;
42
43 // Can not send to self
44 if (account == ctx.tx[sfDestination])
45 return temMALFORMED;
46
47 // Issuer cannot be the destination
48 if (ctx.tx[sfDestination] == issuer)
49 return temMALFORMED;
50
51 // Check the length of the encrypted amounts
52 if (ctx.tx[sfSenderEncryptedAmount].length() != kEcGamalEncryptedTotalLength ||
53 ctx.tx[sfDestinationEncryptedAmount].length() != kEcGamalEncryptedTotalLength ||
54 ctx.tx[sfIssuerEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
55 {
56 return temBAD_CIPHERTEXT;
57 }
58
59 bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
60 if (hasAuditor && ctx.tx[sfAuditorEncryptedAmount].length() != kEcGamalEncryptedTotalLength)
61 return temBAD_CIPHERTEXT;
62
63 // Check the length of the ZKProof (fixed size regardless of recipient count)
64 if (ctx.tx[sfZKProof].length() != kEcSendProofLength)
65 return temMALFORMED;
66
67 // Check the Pedersen commitments are valid
68 if (!isValidCompressedECPoint(ctx.tx[sfBalanceCommitment]) ||
69 !isValidCompressedECPoint(ctx.tx[sfAmountCommitment]))
70 {
71 return temMALFORMED;
72 }
73
74 // Check the encrypted amount formats, this is more expensive so put it at
75 // the end
76 if (!isValidCiphertext(ctx.tx[sfSenderEncryptedAmount]) ||
77 !isValidCiphertext(ctx.tx[sfDestinationEncryptedAmount]) ||
78 !isValidCiphertext(ctx.tx[sfIssuerEncryptedAmount]))
79 {
80 return temBAD_CIPHERTEXT;
81 }
82
83 if (hasAuditor && !isValidCiphertext(ctx.tx[sfAuditorEncryptedAmount]))
84 return temBAD_CIPHERTEXT;
85
86 if (auto const err = credentials::checkFields(ctx.tx, ctx.rules, ctx.j); !isTesSuccess(err))
87 return err;
88
89 return tesSUCCESS;
90}
91
97
98namespace detail {
99
100static TER
102 PreclaimContext const& ctx,
103 std::shared_ptr<SLE const> const& sleSenderMPToken,
104 std::shared_ptr<SLE const> const& sleDestinationMPToken,
105 std::shared_ptr<SLE const> const& sleIssuance)
106{
107 // Sanity check
108 if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance)
109 {
110 // LCOV_EXCL_START
111 UNREACHABLE(
112 "xrpl::detail::verifySendProofs : caller must pre-validate sender/destination/"
113 "issuance existence");
114 return tecINTERNAL;
115 // LCOV_EXCL_STOP
116 }
117
118 auto const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
119
121 if (hasAuditor)
122 {
123 auditor.emplace(
125 .publicKey = (*sleIssuance)[sfAuditorEncryptionKey],
126 .encryptedAmount = ctx.tx[sfAuditorEncryptedAmount],
127 });
128 }
129
130 auto const contextHash = getSendContextHash(
131 ctx.tx[sfAccount],
132 ctx.tx[sfMPTokenIssuanceID],
133 ctx.tx.getSeqProxy().value(),
134 ctx.tx[sfDestination],
135 (*sleSenderMPToken)[~sfConfidentialBalanceVersion].value_or(0));
136
137 return verifySendProof(
138 ctx.tx[sfZKProof],
139 {
140 .publicKey = (*sleSenderMPToken)[sfHolderEncryptionKey],
141 .encryptedAmount = ctx.tx[sfSenderEncryptedAmount],
142 },
143 {
144 .publicKey = (*sleDestinationMPToken)[sfHolderEncryptionKey],
145 .encryptedAmount = ctx.tx[sfDestinationEncryptedAmount],
146 },
147 {
148 .publicKey = (*sleIssuance)[sfIssuerEncryptionKey],
149 .encryptedAmount = ctx.tx[sfIssuerEncryptedAmount],
150 },
151 auditor,
152 (*sleSenderMPToken)[sfConfidentialBalanceSpending],
153 ctx.tx[sfAmountCommitment],
154 ctx.tx[sfBalanceCommitment],
155 contextHash);
156}
157
158} // namespace detail
159
160TER
162{
163 // Check if sender account exists
164 auto const account = ctx.tx[sfAccount];
165 if (!ctx.view.exists(keylet::account(account)))
166 return terNO_ACCOUNT;
167
168 // Check if destination account exists
169 auto const destination = ctx.tx[sfDestination];
170 auto const sleDst = ctx.view.read(keylet::account(destination));
171 if (!sleDst)
172 return tecNO_TARGET;
173
174 // Check destination tag
175 if (((sleDst->getFlags() & lsfRequireDestTag) != 0u) &&
176 !ctx.tx.isFieldPresent(sfDestinationTag))
177 {
178 return tecDST_TAG_NEEDED;
179 }
180
181 // Check if MPT issuance exists
182 auto const mptIssuanceID = ctx.tx[sfMPTokenIssuanceID];
183 auto const sleIssuance = ctx.view.read(keylet::mptokenIssuance(mptIssuanceID));
184 if (!sleIssuance)
185 return tecOBJECT_NOT_FOUND;
186
187 // Check if the issuance allows transfer
188 if (!sleIssuance->isFlag(lsfMPTCanTransfer))
189 return tecNO_AUTH;
190
191 // Check if issuance allows confidential transfer
192 if (!sleIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
193 return tecNO_PERMISSION;
194
195 // Sanity check: transfer fee must be 0 for confidential MPTs. This should
196 // be unreachable in valid ledger state because MPTokenIssuanceCreate and
197 // MPTokenIssuanceSet enforce it.
198 if ((*sleIssuance)[~sfTransferFee].value_or(0) > 0)
199 return tecNO_PERMISSION;
200
201 // Check if issuance has issuer ElGamal public key
202 if (!sleIssuance->isFieldPresent(sfIssuerEncryptionKey))
203 return tecNO_PERMISSION;
204
205 bool const hasAuditor = ctx.tx.isFieldPresent(sfAuditorEncryptedAmount);
206 bool const requiresAuditor = sleIssuance->isFieldPresent(sfAuditorEncryptionKey);
207
208 // Tx must include auditor ciphertext if the issuance has enabled
209 // auditing, and must not include it if auditing is not enabled
210 if (requiresAuditor != hasAuditor)
211 return tecNO_PERMISSION;
212
213 // Sanity check: issuer isn't the sender
214 if (sleIssuance->getAccountID(sfIssuer) == ctx.tx[sfAccount])
215 {
216 // LCOV_EXCL_START
217 UNREACHABLE(
218 "xrpl::ConfidentialMPTSend::preclaim : issuer derived from the MPT ID must match "
219 "the ledger's stored issuer");
220 return tefINTERNAL;
221 // LCOV_EXCL_STOP
222 }
223
224 // Check sender's MPToken existence
225 auto const sleSenderMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, account));
226 if (!sleSenderMPToken)
227 return tecOBJECT_NOT_FOUND;
228
229 // Check sender's MPToken has necessary fields for confidential send
230 if (!sleSenderMPToken->isFieldPresent(sfHolderEncryptionKey) ||
231 !sleSenderMPToken->isFieldPresent(sfConfidentialBalanceSpending) ||
232 !sleSenderMPToken->isFieldPresent(sfIssuerEncryptedBalance))
233 {
234 return tecNO_PERMISSION;
235 }
236
237 // Check destination's MPToken existence
238 auto const sleDestinationMPToken = ctx.view.read(keylet::mptoken(mptIssuanceID, destination));
239 if (!sleDestinationMPToken)
240 return tecOBJECT_NOT_FOUND;
241
242 // Check destination's MPToken has necessary fields for confidential send
243 if (!sleDestinationMPToken->isFieldPresent(sfHolderEncryptionKey) ||
244 !sleDestinationMPToken->isFieldPresent(sfConfidentialBalanceInbox) ||
245 !sleDestinationMPToken->isFieldPresent(sfIssuerEncryptedBalance))
246 {
247 return tecNO_PERMISSION;
248 }
249
250 // Sanity check: Both MPTokens' auditor fields must be present if auditing
251 // is enabled
252 if (requiresAuditor &&
253 (!sleSenderMPToken->isFieldPresent(sfAuditorEncryptedBalance) ||
254 !sleDestinationMPToken->isFieldPresent(sfAuditorEncryptedBalance)))
255 {
256 // LCOV_EXCL_START
257 UNREACHABLE(
258 "xrpl::ConfidentialMPTSend::preclaim : issuance-level auditing implies both "
259 "MPTokens already carry an auditor balance");
260 return tefINTERNAL;
261 // LCOV_EXCL_STOP
262 }
263
264 // Check lock
265 MPTIssue const mptIssue(mptIssuanceID);
266 if (auto const ter = checkFrozen(ctx.view, account, mptIssue); !isTesSuccess(ter))
267 return ter;
268
269 if (auto const ter = checkFrozen(ctx.view, destination, mptIssue); !isTesSuccess(ter))
270 return ter;
271
272 // Check auth
273 if (auto const ter = requireAuth(ctx.view, mptIssue, account); !isTesSuccess(ter))
274 return ter;
275
276 if (auto const ter = requireAuth(ctx.view, mptIssue, destination); !isTesSuccess(ter))
277 return ter;
278
279 if (auto const err = credentials::valid(ctx.tx, ctx.view, ctx.tx[sfAccount], ctx.j);
280 !isTesSuccess(err))
281 return err;
282
283 // Check deposit preauth before the expensive ZK proof verification.
284 // Uses read-only view.
285 auto const preauthErr =
286 checkDepositPreauth(ctx.tx, ctx.view, account, destination, sleDst, ctx.j);
287 if (!isTesSuccess(preauthErr))
288 return preauthErr;
289
290 return detail::verifySendProofs(ctx, sleSenderMPToken, sleDestinationMPToken, sleIssuance);
291}
292
293TER
295{
296 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
297 auto const destination = ctx_.tx[sfDestination];
298
299 auto sleSenderMPToken = view().peek(keylet::mptoken(mptIssuanceID, accountID_));
300 auto sleDestinationMPToken = view().peek(keylet::mptoken(mptIssuanceID, destination));
301 auto const sleIssuance = view().read(keylet::mptokenIssuance(mptIssuanceID));
302
303 auto const sleDestAcct = view().read(keylet::account(destination));
304
305 if (!sleSenderMPToken || !sleDestinationMPToken || !sleIssuance || !sleDestAcct)
306 {
307 // LCOV_EXCL_START
308 UNREACHABLE(
309 "xrpl::ConfidentialMPTSend::doApply : preclaim already validated these objects "
310 "exist");
311 return tecINTERNAL;
312 // LCOV_EXCL_STOP
313 }
314
315 // Deposit preauth authorization was already verified in preclaim.
316 // Remove any expired credentials.
317 if (auto err = cleanupExpiredCredentials(ctx_.tx, ctx_.view(), ctx_.journal);
318 !isTesSuccess(err))
319 return err;
320
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];
325 Slice const sendChallenge{proof.data(), kEcBlindingFactorLength};
326
327 auto const auditorEc = ctx_.tx[~sfAuditorEncryptedAmount];
328
329 // Subtract from sender's spending balance
330 {
331 auto const curSpending = (*sleSenderMPToken)[sfConfidentialBalanceSpending];
332 auto newSpending = homomorphicSubtract(curSpending, senderEc);
333 if (!newSpending)
334 {
335 // LCOV_EXCL_START
336 JLOG(ctx_.journal.error())
337 << "ConfidentialMPTSend failed homomorphic subtract for sender spending balance.";
338 return tecINTERNAL;
339 // LCOV_EXCL_STOP
340 }
341
342 (*sleSenderMPToken)[sfConfidentialBalanceSpending] = std::move(*newSpending);
343 }
344
345 // Subtract from issuer's balance
346 {
347 auto const curIssuerEnc = (*sleSenderMPToken)[sfIssuerEncryptedBalance];
348 auto newIssuerEnc = homomorphicSubtract(curIssuerEnc, issuerEc);
349 if (!newIssuerEnc)
350 {
351 // LCOV_EXCL_START
352 JLOG(ctx_.journal.error())
353 << "ConfidentialMPTSend failed homomorphic subtract for sender issuer balance.";
354 return tecINTERNAL;
355 // LCOV_EXCL_STOP
356 }
357
358 (*sleSenderMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
359 }
360
361 // Subtract from auditor's balance if present
362 if (auditorEc)
363 {
364 auto const curAuditorEnc = (*sleSenderMPToken)[sfAuditorEncryptedBalance];
365 auto newAuditorEnc = homomorphicSubtract(curAuditorEnc, *auditorEc);
366 if (!newAuditorEnc)
367 {
368 // LCOV_EXCL_START
369 JLOG(ctx_.journal.error())
370 << "ConfidentialMPTSend failed homomorphic subtract for sender auditor balance.";
371 return tecINTERNAL;
372 // LCOV_EXCL_STOP
373 }
374
375 (*sleSenderMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
376 }
377
378 // Add to destination's inbox balance
379 {
380 auto rerandomizedDestEc = rerandomizeCiphertext(
381 destEc, (*sleDestinationMPToken)[sfHolderEncryptionKey], sendChallenge);
382 if (!rerandomizedDestEc)
383 {
384 // LCOV_EXCL_START
385 JLOG(ctx_.journal.error())
386 << "ConfidentialMPTSend failed to rerandomize destination inbox ciphertext.";
387 return tecINTERNAL;
388 // LCOV_EXCL_STOP
389 }
390
391 auto const curInbox = (*sleDestinationMPToken)[sfConfidentialBalanceInbox];
392 auto newInbox = homomorphicAdd(curInbox, *rerandomizedDestEc);
393 if (!newInbox)
394 {
395 // LCOV_EXCL_START
396 JLOG(ctx_.journal.error())
397 << "ConfidentialMPTSend failed homomorphic add for destination inbox.";
398 return tecINTERNAL;
399 // LCOV_EXCL_STOP
400 }
401
402 (*sleDestinationMPToken)[sfConfidentialBalanceInbox] = std::move(*newInbox);
403 }
404
405 // Add to issuer's balance
406 {
407 auto rerandomizedIssuerEc =
408 rerandomizeCiphertext(issuerEc, (*sleIssuance)[sfIssuerEncryptionKey], sendChallenge);
409 if (!rerandomizedIssuerEc)
410 {
411 // LCOV_EXCL_START
412 JLOG(ctx_.journal.error())
413 << "ConfidentialMPTSend failed to rerandomize destination issuer ciphertext.";
414 return tecINTERNAL;
415 // LCOV_EXCL_STOP
416 }
417
418 auto const curIssuerEnc = (*sleDestinationMPToken)[sfIssuerEncryptedBalance];
419 auto newIssuerEnc = homomorphicAdd(curIssuerEnc, *rerandomizedIssuerEc);
420 if (!newIssuerEnc)
421 {
422 // LCOV_EXCL_START
423 JLOG(ctx_.journal.error())
424 << "ConfidentialMPTSend failed homomorphic add for destination issuer balance.";
425 return tecINTERNAL;
426 // LCOV_EXCL_STOP
427 }
428
429 (*sleDestinationMPToken)[sfIssuerEncryptedBalance] = std::move(*newIssuerEnc);
430 }
431
432 // Add to auditor's balance if present
433 if (auditorEc)
434 {
435 auto rerandomizedAuditorEc = rerandomizeCiphertext(
436 *auditorEc, (*sleIssuance)[sfAuditorEncryptionKey], sendChallenge);
437 if (!rerandomizedAuditorEc)
438 {
439 // LCOV_EXCL_START
440 JLOG(ctx_.journal.error())
441 << "ConfidentialMPTSend failed to rerandomize destination auditor ciphertext.";
442 return tecINTERNAL;
443 // LCOV_EXCL_STOP
444 }
445
446 auto const curAuditorEnc = (*sleDestinationMPToken)[sfAuditorEncryptedBalance];
447 auto newAuditorEnc = homomorphicAdd(curAuditorEnc, *rerandomizedAuditorEc);
448 if (!newAuditorEnc)
449 {
450 // LCOV_EXCL_START
451 JLOG(ctx_.journal.error())
452 << "ConfidentialMPTSend failed homomorphic add for destination auditor balance.";
453 return tecINTERNAL;
454 // LCOV_EXCL_STOP
455 }
456
457 (*sleDestinationMPToken)[sfAuditorEncryptedBalance] = std::move(*newAuditorEnc);
458 }
459
460 // increment sender version only; receiver version is not modified by incoming sends
461 incrementConfidentialVersion(*sleSenderMPToken);
462
463 view().update(sleSenderMPToken);
464 view().update(sleDestinationMPToken);
465 return tesSUCCESS;
466}
467
468void
475
476bool
478 STTx const&,
479 TER,
480 XRPAmount,
481 ReadView const&,
482 beast::Journal const&)
483{
484 return true;
485}
486
487} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
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
Definition MPTIssue.cpp:29
A view into a ledger.
Definition ReadView.h:41
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.
Definition Rules.cpp:180
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
SeqProxy getSeqProxy() const
Definition STTx.cpp:199
constexpr std::uint32_t value() const
Definition SeqProxy.h:80
An immutable linear range of bytes.
Definition Slice.h:28
ApplyView & view()
Definition Transactor.h:175
static XRPAmount calculateBaseFee(ReadView const &view, STTx const &tx)
AccountID const accountID_
Definition Transactor.h:157
ApplyContext & ctx_
Definition Transactor.h:153
T emplace(T... args)
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
Definition Indexes.cpp:543
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:537
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_ACCOUNT
Definition TER.h:213
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.
Definition Protocol.h:483
constexpr std::uint32_t kConfidentialFeeMultiplier
Extra base fee multiplier charged to confidential MPT transactions.
Definition Protocol.h:534
TER checkFrozen(ReadView const &view, AccountID const &account, Issue const &issue)
@ tefINTERNAL
Definition TER.h:165
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.
Definition Protocol.h:468
bool isValidCiphertext(Slice const &buffer)
Verifies that a buffer contains two valid, parsable EC public keys.
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
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.
Definition Protocol.h:513
@ temBAD_CIPHERTEXT
Definition TER.h:133
@ temMALFORMED
Definition TER.h:75
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
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.
@ tecNO_TARGET
Definition TER.h:307
@ tecOBJECT_NOT_FOUND
Definition TER.h:329
@ tecNO_AUTH
Definition TER.h:303
@ tecINTERNAL
Definition TER.h:313
@ tecNO_PERMISSION
Definition TER.h:308
@ tecDST_TAG_NEEDED
Definition TER.h:312
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.
@ tesSUCCESS
Definition TER.h:245
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.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38
beast::Journal const j
Definition Transactor.h:45