xrpld
Loading...
Searching...
No Matches
MPTokenIssuanceSet.cpp
1#include <xrpl/tx/transactors/token/MPTokenIssuanceSet.h>
2
3#include <xrpl/beast/utility/Journal.h>
4#include <xrpl/beast/utility/Zero.h>
5#include <xrpl/beast/utility/instrumentation.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/ReadView.h>
8#include <xrpl/protocol/ConfidentialTransfer.h>
9#include <xrpl/protocol/Feature.h>
10#include <xrpl/protocol/Indexes.h>
11#include <xrpl/protocol/LedgerFormats.h>
12#include <xrpl/protocol/Permissions.h>
13#include <xrpl/protocol/Protocol.h>
14#include <xrpl/protocol/SField.h>
15#include <xrpl/protocol/STLedgerEntry.h>
16#include <xrpl/protocol/STTx.h>
17#include <xrpl/protocol/TER.h>
18#include <xrpl/protocol/TxFlags.h>
19#include <xrpl/protocol/XRPAmount.h>
20#include <xrpl/tx/Transactor.h>
21
22#include <algorithm>
23#include <cstdint>
24
25namespace xrpl {
26
27bool
29{
30 return !ctx.tx.isFieldPresent(sfDomainID) ||
31 (ctx.rules.enabled(featurePermissionedDomains) &&
32 ctx.rules.enabled(featureSingleAssetVault));
33}
34
37{
38 return tfMPTokenIssuanceSetMask;
39}
40
43{
44 auto const txFlags = ctx.tx.getFlags();
45 auto const enableFlags = txFlags & tfMPTokenIssuanceSetEnableFlagMask;
46 auto const metadata = ctx.tx[~sfMPTokenMetadata];
47 auto const transferFee = ctx.tx[~sfTransferFee];
48 auto const immutableFlags = ctx.tx[~sfImmutableFlags];
49 auto const isMutate = (enableFlags != 0u) || metadata || transferFee || immutableFlags;
50 auto const hasIssuerElGamalKey = ctx.tx.isFieldPresent(sfIssuerEncryptionKey);
51 auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorEncryptionKey);
52
53 bool const enablePrivacy = (enableFlags & tfMPTSetCanHoldConfidentialBalance) != 0u;
54 auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID);
55 auto const hasHolder = ctx.tx.isFieldPresent(sfHolder);
56
57 if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
58 return temDISABLED;
59
60 bool const setConfidentialBalanceImmutable =
61 immutableFlags && (*immutableFlags & tifMPTCanHoldConfidentialBalance) != 0u;
62 if ((hasIssuerElGamalKey || hasAuditorElGamalKey || enablePrivacy ||
63 setConfidentialBalanceImmutable) &&
64 !ctx.rules.enabled(featureConfidentialTransfer))
65 return temDISABLED;
66
67 if (hasDomain && hasHolder)
68 return temMALFORMED;
69
70 if (enablePrivacy && hasHolder)
71 return temMALFORMED;
72
73 // fails if both flags are set
74 if (ctx.tx.isFlag(tfMPTLock) && ctx.tx.isFlag(tfMPTUnlock))
75 return temINVALID_FLAG;
76
77 auto const accountID = ctx.tx[sfAccount];
78 auto const holderID = ctx.tx[~sfHolder];
79 if (holderID && accountID == holderID)
80 return temMALFORMED;
81
82 if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT) ||
83 ctx.rules.enabled(featureConfidentialTransfer))
84 {
85 // Is this transaction actually changing anything ?
86 if (txFlags == 0 && !hasDomain && !hasIssuerElGamalKey && !hasAuditorElGamalKey &&
87 !isMutate)
88 return temMALFORMED;
89 }
90
91 if (ctx.rules.enabled(featureDynamicMPT))
92 {
93 // Holder field is not allowed when mutating MPTokenIssuance
94 if (isMutate && holderID)
95 return temMALFORMED;
96
97 // A single transaction may either lock/unlock or mutate capability
98 // flags, but not both.
99 if (isMutate && (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)))
100 return temMALFORMED;
101
102 if (transferFee && *transferFee > kMaxTransferFee)
103 return temBAD_TRANSFER_FEE;
104
105 if (transferFee && *transferFee > 0u && enablePrivacy)
106 return temBAD_TRANSFER_FEE;
107
108 if (metadata && metadata->length() > kMaxMpTokenMetadataLength)
109 return temMALFORMED;
110
111 // If the immutable flags field is included, at least one flag must be
112 // specified, and undefined flags must not be specified.
113 if (immutableFlags &&
114 ((*immutableFlags == 0u) ||
115 ((*immutableFlags & tifMPTokenIssuanceImmutableMask) != 0u)))
116 return temINVALID_FLAG;
117 }
118
119 if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey))
120 return temMALFORMED;
121
122 // Pre-ConfidentialMPTKeyRotation amendment, the auditor key could not be
123 // registered independently of the issuer key. The issuer could either:
124 // - Register only the issuer key (in which case an auditor key could not be added later), or
125 // - Register both the issuer and auditor keys simultaneously.
126 //
127 // Post-ConfidentialMPTKeyRotation amendment, the auditor key can be
128 // registered after the issuer key has already been registered.
129 if (hasAuditorElGamalKey && !hasIssuerElGamalKey &&
130 !ctx.rules.enabled(featureConfidentialMPTKeyRotation))
131 return temMALFORMED;
132
133 if (hasIssuerElGamalKey && !isValidCompressedECPoint(ctx.tx[sfIssuerEncryptionKey]))
134 return temMALFORMED;
135
136 if (hasAuditorElGamalKey && !isValidCompressedECPoint(ctx.tx[sfAuditorEncryptionKey]))
137 return temMALFORMED;
138
139 return tesSUCCESS;
140}
141
142TER
144{
145 // ensure that issuance exists
146 auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
147 if (!sleMptIssuance)
148 return tecOBJECT_NOT_FOUND;
149
150 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
151 {
152 // For readability two separate `if` rather than `||` of two conditions
153 if (!ctx.view.rules().enabled(featureSingleAssetVault) &&
154 !ctx.view.rules().enabled(featureDynamicMPT))
155 {
156 return tecNO_PERMISSION;
157 }
158 if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
159 {
160 return tecNO_PERMISSION;
161 }
162 }
163
164 // ensure it is issued by the tx submitter
165 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
166 return tecNO_PERMISSION;
167
168 if (auto const holderID = ctx.tx[~sfHolder])
169 {
170 // make sure holder account exists
171 if (!ctx.view.exists(keylet::account(*holderID)))
172 return tecNO_DST;
173
174 // the mptoken must exist
175 if (!ctx.view.exists(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
176 return tecOBJECT_NOT_FOUND;
177 }
178
179 if (auto const domain = ctx.tx[~sfDomainID])
180 {
181 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
182 return tecNO_PERMISSION;
183
184 if (*domain != beast::kZero)
185 {
186 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
187 if (!sleDomain)
188 return tecOBJECT_NOT_FOUND;
189 }
190 }
191
192 // sfImmutableFlags is soeDEFAULT, defaulting to 0 if not specified on
193 // the ledger.
194 auto const currentImmutableFlags = sleMptIssuance->getFieldU32(sfImmutableFlags);
195
196 auto isImmutable = [&](std::uint32_t flag) -> bool { return currentImmutableFlags & flag; };
197
198 auto const enableFlags = ctx.tx.getFlags() & tfMPTokenIssuanceSetEnableFlagMask;
199 if (enableFlags != 0u)
200 {
201 // If any of the flags to be set is immutable, return tecNO_PERMISSION.
202 if (std::ranges::any_of(flagMapping, [&](auto const& f) {
203 return isImmutable(f.immutableFlag) && ctx.tx.isFlag(f.setFlag);
204 }))
205 return tecNO_PERMISSION;
206 }
207
208 if (isImmutable(lsifMPTMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
209 return tecNO_PERMISSION;
210
211 if (auto const fee = ctx.tx[~sfTransferFee])
212 {
213 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
214 // is already set on the ledger object, or is being enabled by this
215 // same transaction. The Immutability of lsfMPTCanTransfer is checked above.
216 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer) &&
217 (enableFlags & tfMPTSetCanTransfer) == 0u)
218 return tecNO_PERMISSION;
219
220 // Cannot set a non-zero TransferFee on an issuance that has confidential
221 // transfer enabled
222 if (fee > 0u && sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
223 return tecNO_PERMISSION;
224
225 // Cannot set TransferFee if it is immutable
226 if (isImmutable(lsifMPTTransferFee))
227 return tecNO_PERMISSION;
228 }
229
230 // Updating an existing encryption key requires the
231 // ConfidentialMPTKeyRotation amendment.
232 bool const canRotateKey = ctx.view.rules().enabled(featureConfidentialMPTKeyRotation);
233
234 bool const txHasIssuerKey = ctx.tx.isFieldPresent(sfIssuerEncryptionKey);
235 bool const txHasAuditorKey = ctx.tx.isFieldPresent(sfAuditorEncryptionKey);
236 bool const sleHasIssuerKey = sleMptIssuance->isFieldPresent(sfIssuerEncryptionKey);
237 bool const sleHasAuditorKey = sleMptIssuance->isFieldPresent(sfAuditorEncryptionKey);
238
239 if (canRotateKey)
240 {
241 // Post-ConfidentialMPTKeyRotation amendment, the encryption keys can be updated.
242 // A first-time auditor key registration requires an issuer key,
243 // either already on the issuance or set by the same transaction.
244 bool const registersAuditorKey = txHasAuditorKey && !sleHasAuditorKey;
245 bool const issuerKeyExists = sleHasIssuerKey || txHasIssuerKey;
246 if (registersAuditorKey && !issuerKeyExists)
247 return tecNO_PERMISSION;
248
249 // Rotating a key to its current value is not permitted: a key epoch
250 // increment must always correspond to an actual key change.
251 if (txHasIssuerKey && sleHasIssuerKey &&
252 ctx.tx[sfIssuerEncryptionKey] == (*sleMptIssuance)[sfIssuerEncryptionKey])
253 return tecDUPLICATE;
254
255 if (txHasAuditorKey && sleHasAuditorKey &&
256 ctx.tx[sfAuditorEncryptionKey] == (*sleMptIssuance)[sfAuditorEncryptionKey])
257 return tecDUPLICATE;
258
259 // Key epochs must never wrap. Epoch 0 serves as the sentinel for "never
260 // rotated." Holders' mirror epochs are checked against it for equality,
261 // so a wrap would cause stale mirror ciphertexts to appear valid instead
262 // of failing loudly.
263 if (txHasIssuerKey && sleHasIssuerKey &&
264 (*sleMptIssuance)[~sfIssuerKeyEpoch].value_or(0) == kMaxKeyEpoch)
265 return tecNO_PERMISSION;
266
267 if (txHasAuditorKey && sleHasAuditorKey &&
268 (*sleMptIssuance)[~sfAuditorKeyEpoch].value_or(0) == kMaxKeyEpoch)
269 return tecNO_PERMISSION;
270 }
271 else
272 {
273 // Pre-ConfidentialMPTKeyRotation amendment, the encryption keys can not be updated.
274 // cannot update issuer public key
275 if (txHasIssuerKey && sleHasIssuerKey)
276 return tecNO_PERMISSION;
277
278 // cannot update auditor public key
279 if (txHasAuditorKey && sleHasAuditorKey)
280 return tecNO_PERMISSION; // LCOV_EXCL_LINE
281 }
282
283 auto const enablesConfidentialBalance =
284 (enableFlags & tfMPTSetCanHoldConfidentialBalance) != 0u;
285 if (enablesConfidentialBalance && sleMptIssuance->isFieldPresent(sfTransferFee) &&
286 (*sleMptIssuance)[sfTransferFee] > 0u)
287 return tecNO_PERMISSION;
288
289 // Encryption keys can only be set if confidential amounts are already
290 // enabled on the issuance OR if the transaction is enabling it
291 if (txHasIssuerKey && !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) &&
292 !enablesConfidentialBalance)
293 {
294 return tecNO_PERMISSION;
295 }
296
297 if (txHasAuditorKey && !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) &&
298 !enablesConfidentialBalance)
299 {
300 return tecNO_PERMISSION;
301 }
302
303 bool const hasConfidentialOA =
304 (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0;
305
306 // Pre-ConfidentialMPTKeyRotation amendment, keys cannot be uploaded while
307 // COA > 0. Post-amendment they can be uploaded even if COA > 0.
308 if (!canRotateKey && (txHasIssuerKey || txHasAuditorKey) && hasConfidentialOA)
309 return tecNO_PERMISSION; // LCOV_EXCL_LINE
310
311 // Enabling confidential balances when COA > 0 is not permitted, regardless of
312 // ConfidentialMPTKeyRotation.
313 if (enablesConfidentialBalance && hasConfidentialOA)
314 return tecNO_PERMISSION;
315
316 return tesSUCCESS;
317}
318
319TER
321{
322 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
323 auto const holderID = ctx_.tx[~sfHolder];
324 auto const domainID = ctx_.tx[~sfDomainID];
325 SLE::pointer sle;
326
327 if (holderID)
328 {
329 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
330 }
331 else
332 {
333 sle = view().peek(keylet::mptokenIssuance(mptIssuanceID));
334 }
335
336 if (!sle)
337 return tecINTERNAL; // LCOV_EXCL_LINE
338
339 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
340 std::uint32_t flagsOut = flagsIn;
341
342 if (ctx_.tx.isFlag(tfMPTLock))
343 {
344 flagsOut |= lsfMPTLocked;
345 }
346 else if (ctx_.tx.isFlag(tfMPTUnlock))
347 {
348 flagsOut &= ~lsfMPTLocked;
349 }
350
351 if (auto const enableFlags = (ctx_.tx.getFlags() & tfMPTokenIssuanceSetEnableFlagMask);
352 enableFlags != 0u)
353 {
354 for (auto const& f : flagMapping)
355 {
356 if (ctx_.tx.isFlag(f.setFlag))
357 {
358 flagsOut |= f.ledgerFlag;
359 }
360 }
361 }
362
363 if (flagsIn != flagsOut)
364 sle->setFieldU32(sfFlags, flagsOut);
365
366 if (auto const immutableFlags = ctx_.tx[~sfImmutableFlags])
367 {
368 // sle is guaranteed to be an ltMPTOKEN_ISSUANCE rather than an ltMPTOKEN.
369 // Preflight verification ensures that sfHolder and sfImmutableFlags can
370 // never both be present in the same transaction. Therefore, if
371 // sfImmutableFlags is present, sfHolder must be absent.
372 //
373 // In doApply, the absence of sfHolder causes the MPTokenIssuance keylet
374 // to be peeked. The runtime check below is a defensive fallback in case
375 // this invariant is ever broken by a future change.
376 XRPL_ASSERT(
377 sle->getType() == ltMPTOKEN_ISSUANCE,
378 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
379
380 if (sle->getType() != ltMPTOKEN_ISSUANCE)
381 return tecINTERNAL; // LCOV_EXCL_LINE
382
383 (*sle)[sfImmutableFlags] = (*sle)[sfImmutableFlags] | *immutableFlags;
384 }
385
386 if (auto const transferFee = ctx_.tx[~sfTransferFee])
387 {
388 // TransferFee uses soeDEFAULT style:
389 // - If the field is absent, it is interpreted as 0.
390 // - If the field is present, it must be non-zero.
391 // Therefore, when TransferFee is 0, the field should be removed.
392 if (transferFee == 0)
393 {
394 sle->makeFieldAbsent(sfTransferFee);
395 }
396 else
397 {
398 sle->setFieldU16(sfTransferFee, *transferFee);
399 }
400 }
401
402 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
403 {
404 if (metadata->empty())
405 {
406 sle->makeFieldAbsent(sfMPTokenMetadata);
407 }
408 else
409 {
410 sle->setFieldVL(sfMPTokenMetadata, *metadata);
411 }
412 }
413
414 if (domainID)
415 {
416 // This is enforced in preflight.
417 XRPL_ASSERT(
418 sle->getType() == ltMPTOKEN_ISSUANCE,
419 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
420
421 if (*domainID != beast::kZero)
422 {
423 sle->setFieldH256(sfDomainID, *domainID);
424 }
425 else
426 {
427 if (sle->isFieldPresent(sfDomainID))
428 sle->makeFieldAbsent(sfDomainID);
429 }
430 }
431
432 // Sets an encryption key on the issuance. Overwriting an existing key
433 // (a rotation) increments the corresponding key epoch; a first-time
434 // registration leaves the epoch absent (epoch 0), matching issuances
435 // whose keys were registered before the ConfidentialMPTKeyRotation
436 // amendment.
437 bool const canRotateKey = view().rules().enabled(featureConfidentialMPTKeyRotation);
438 auto const setEncryptionKey = [&](SF_VL const& keyField, SF_UINT32 const& epochField) -> TER {
439 auto const pubKey = ctx_.tx[~keyField];
440 if (!pubKey)
441 return tesSUCCESS;
442
443 // This is enforced in preflight, which rejects a transaction carrying
444 // both sfHolder and an encryption key.
445 XRPL_ASSERT(
446 sle->getType() == ltMPTOKEN_ISSUANCE,
447 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
448
449 // Add sanity check under the amendment ConfidentialMPTKeyRotation.
450 // Pre-confidentialMPTKeyRotation did not return tecINTERNAL so
451 // this should be under the amendment guard.
452 if (canRotateKey && sle->getType() != ltMPTOKEN_ISSUANCE)
453 return tecINTERNAL; // LCOV_EXCL_LINE
454
455 // NOTE: presence must be checked before the key is overwritten below.
456 bool const isRotation = sle->isFieldPresent(keyField);
457 sle->setFieldVL(keyField, *pubKey);
458
459 if (isRotation)
460 {
461 // Preclaim rejects overwriting an existing key unless the amendment is
462 // enabled.
463 if (!canRotateKey)
464 {
465 // LCOV_EXCL_START
466 UNREACHABLE("xrpl::MPTokenIssuanceSet::doApply : rotation without amendment");
467 return tecINTERNAL;
468 // LCOV_EXCL_STOP
469 }
470
471 auto const epoch = (*sle)[~epochField].valueOr(0);
472
473 // Preclaim rejects a rotation that would wrap the epoch. So this should never happen.
474 if (epoch >= kMaxKeyEpoch)
475 {
476 // LCOV_EXCL_START
477 UNREACHABLE("xrpl::MPTokenIssuanceSet::doApply : key epoch overflow");
478 return tecINTERNAL;
479 // LCOV_EXCL_STOP
480 }
481
482 (*sle)[epochField] = epoch + 1;
483 }
484
485 return tesSUCCESS;
486 };
487
488 if (auto const ter = setEncryptionKey(sfIssuerEncryptionKey, sfIssuerKeyEpoch);
489 !isTesSuccess(ter))
490 return ter; // LCOV_EXCL_LINE
491
492 if (auto const ter = setEncryptionKey(sfAuditorEncryptionKey, sfAuditorKeyEpoch);
493 !isTesSuccess(ter))
494 return ter; // LCOV_EXCL_LINE
495
496 view().update(sle);
497
498 return tesSUCCESS;
499}
500
501void
503{
504 // No transaction-specific invariants yet (future work).
505}
506
507bool
509 STTx const&,
510 TER,
511 XRPAmount,
512 ReadView const&,
513 beast::Journal const&)
514{
515 // No transaction-specific invariants yet (future work).
516 return true;
517}
518
519} // namespace xrpl
T any_of(T... args)
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.
static NotTEC preflight(PreflightContext const &ctx)
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
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 TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static constexpr std::array< FlagMapping, 7 > flagMapping
A view into a ledger.
Definition ReadView.h:41
virtual Rules const & rules() const =0
Returns the tx processing rules.
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:182
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & ConstRef
bool isFlag(std::uint32_t) const
Definition STObject.cpp:511
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:464
std::uint32_t getFlags() const
Definition STObject.cpp:517
ApplyView & view()
Definition Transactor.h:184
ApplyContext & ctx_
Definition Transactor.h:162
constexpr Zero kZero
Definition Zero.h:30
Keylet permissionedDomain(AccountID const &account, SeqProxy const &seq) noexcept
Definition Indexes.cpp:609
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:573
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:220
Keylet mptokenIssuance(MPTID const &issuanceID) noexcept
Definition Indexes.cpp:567
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
bool isValidCompressedECPoint(Slice const &buffer)
Verifies that a buffer contains a valid, parsable compressed EC point.
TypedField< STInteger< std::uint32_t > > SF_UINT32
Definition SField.h:344
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:614
constexpr FlagValue tfMPTokenIssuanceSetEnableFlagMask
Definition TxFlags.h:389
constexpr std::size_t kMaxMpTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:292
TypedField< STBlob > SF_VL
Definition SField.h:362
constexpr FlagValue tifMPTokenIssuanceImmutableMask
Definition TxFlags.h:383
constexpr std::uint32_t kMaxKeyEpoch
Maximum value a confidential MPT key epoch may reach.
Definition Protocol.h:556
constexpr std::uint16_t kMaxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:97
constexpr FlagValue tifMPTCanHoldConfidentialBalance
Definition TxFlags.h:382
constexpr std::uint32_t lsifMPTMetadata
@ temINVALID_FLAG
Definition TER.h:99
@ temMALFORMED
Definition TER.h:75
@ temDISABLED
Definition TER.h:102
@ temBAD_TRANSFER_FEE
Definition TER.h:130
bool isTesSuccess(TER x) noexcept
Definition TER.h:683
TERSubset< CanCvtToTER > TER
Definition TER.h:654
@ tecOBJECT_NOT_FOUND
Definition TER.h:334
@ tecINTERNAL
Definition TER.h:318
@ tecNO_PERMISSION
Definition TER.h:313
@ tecDUPLICATE
Definition TER.h:323
@ tecNO_DST
Definition TER.h:298
@ tesSUCCESS
Definition TER.h:250
constexpr std::uint32_t lsifMPTTransferFee
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:92
ReadView const & view
Definition Transactor.h:95
State information when preflighting a tx.
Definition Transactor.h:39