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 <array>
24#include <cstdint>
25
26namespace xrpl {
27
28bool
30{
31 return !ctx.tx.isFieldPresent(sfDomainID) ||
32 (ctx.rules.enabled(featurePermissionedDomains) &&
33 ctx.rules.enabled(featureSingleAssetVault));
34}
35
38{
39 return tfMPTokenIssuanceSetMask;
40}
41
42// Maps each MPTokenIssuanceSet MutableFlags to the corresponding mutable
43// flag and the target ledger flag to mutate.
50
52 {{.setFlag = tmfMPTSetCanLock,
53 .canEnableFlag = lsmfMPTCanEnableCanLock,
54 .ledgerFlag = lsfMPTCanLock},
55 {.setFlag = tmfMPTSetRequireAuth,
56 .canEnableFlag = lsmfMPTCanEnableRequireAuth,
57 .ledgerFlag = lsfMPTRequireAuth},
58 {.setFlag = tmfMPTSetCanEscrow,
59 .canEnableFlag = lsmfMPTCanEnableCanEscrow,
60 .ledgerFlag = lsfMPTCanEscrow},
61 {.setFlag = tmfMPTSetCanTrade,
62 .canEnableFlag = lsmfMPTCanEnableCanTrade,
63 .ledgerFlag = lsfMPTCanTrade},
64 {.setFlag = tmfMPTSetCanTransfer,
65 .canEnableFlag = lsmfMPTCanEnableCanTransfer,
66 .ledgerFlag = lsfMPTCanTransfer},
67 {.setFlag = tmfMPTSetCanClawback,
68 .canEnableFlag = lsmfMPTCanEnableCanClawback,
69 .ledgerFlag = lsfMPTCanClawback}}};
70
73{
74 auto const mutableFlags = ctx.tx[~sfMutableFlags];
75 auto const metadata = ctx.tx[~sfMPTokenMetadata];
76 auto const transferFee = ctx.tx[~sfTransferFee];
77 auto const isMutate = mutableFlags || metadata || transferFee;
78 auto const hasIssuerElGamalKey = ctx.tx.isFieldPresent(sfIssuerEncryptionKey);
79 auto const hasAuditorElGamalKey = ctx.tx.isFieldPresent(sfAuditorEncryptionKey);
80 auto const txFlags = ctx.tx.getFlags();
81
82 bool const enablePrivacy =
83 mutableFlags && (*mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u;
84
85 auto const hasDomain = ctx.tx.isFieldPresent(sfDomainID);
86 auto const hasHolder = ctx.tx.isFieldPresent(sfHolder);
87
88 if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
89 return temDISABLED;
90
91 if ((hasIssuerElGamalKey || hasAuditorElGamalKey || enablePrivacy) &&
92 !ctx.rules.enabled(featureConfidentialTransfer))
93 return temDISABLED;
94
95 if (hasDomain && hasHolder)
96 return temMALFORMED;
97
98 if (enablePrivacy && hasHolder)
99 return temMALFORMED;
100
101 // fails if both flags are set
102 if (ctx.tx.isFlag(tfMPTLock) && ctx.tx.isFlag(tfMPTUnlock))
103 return temINVALID_FLAG;
104
105 auto const accountID = ctx.tx[sfAccount];
106 auto const holderID = ctx.tx[~sfHolder];
107 if (holderID && accountID == holderID)
108 return temMALFORMED;
109
110 if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT) ||
111 ctx.rules.enabled(featureConfidentialTransfer))
112 {
113 // Is this transaction actually changing anything ?
114 if (txFlags == 0 && !hasDomain && !hasIssuerElGamalKey && !hasAuditorElGamalKey &&
115 !isMutate)
116 return temMALFORMED;
117 }
118
119 if (ctx.rules.enabled(featureDynamicMPT))
120 {
121 // Holder field is not allowed when mutating MPTokenIssuance
122 if (isMutate && holderID)
123 return temMALFORMED;
124
125 // Can not set flags when mutating MPTokenIssuance
126 if (isMutate && ((ctx.tx.getFlags() & tfUniversalMask) != 0u))
127 return temMALFORMED;
128
129 if (transferFee && *transferFee > kMaxTransferFee)
130 return temBAD_TRANSFER_FEE;
131
132 if (transferFee && *transferFee > 0u && enablePrivacy)
133 return temBAD_TRANSFER_FEE;
134
135 if (metadata && metadata->length() > kMaxMpTokenMetadataLength)
136 return temMALFORMED;
137
138 if (mutableFlags)
139 {
140 if ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceSetMutableMask) != 0u))
141 return temINVALID_FLAG;
142 }
143 }
144
145 if (hasHolder && (hasIssuerElGamalKey || hasAuditorElGamalKey))
146 return temMALFORMED;
147
148 if (hasAuditorElGamalKey && !hasIssuerElGamalKey)
149 return temMALFORMED;
150
151 if (hasIssuerElGamalKey && !isValidCompressedECPoint(ctx.tx[sfIssuerEncryptionKey]))
152 return temMALFORMED;
153
154 if (hasAuditorElGamalKey && !isValidCompressedECPoint(ctx.tx[sfAuditorEncryptionKey]))
155 return temMALFORMED;
156
157 return tesSUCCESS;
158}
159
160TER
162{
163 // ensure that issuance exists
164 auto const sleMptIssuance = ctx.view.read(keylet::mptokenIssuance(ctx.tx[sfMPTokenIssuanceID]));
165 if (!sleMptIssuance)
166 return tecOBJECT_NOT_FOUND;
167
168 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
169 {
170 // For readability two separate `if` rather than `||` of two conditions
171 if (!ctx.view.rules().enabled(featureSingleAssetVault) &&
172 !ctx.view.rules().enabled(featureDynamicMPT))
173 {
174 return tecNO_PERMISSION;
175 }
176 if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
177 {
178 return tecNO_PERMISSION;
179 }
180 }
181
182 // ensure it is issued by the tx submitter
183 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
184 return tecNO_PERMISSION;
185
186 if (auto const holderID = ctx.tx[~sfHolder])
187 {
188 // make sure holder account exists
189 if (!ctx.view.exists(keylet::account(*holderID)))
190 return tecNO_DST;
191
192 // the mptoken must exist
193 if (!ctx.view.exists(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
194 return tecOBJECT_NOT_FOUND;
195 }
196
197 if (auto const domain = ctx.tx[~sfDomainID])
198 {
199 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
200 return tecNO_PERMISSION;
201
202 if (*domain != beast::kZero)
203 {
204 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
205 if (!sleDomain)
206 return tecOBJECT_NOT_FOUND;
207 }
208 }
209
210 // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
211 // the ledger.
212 auto const currentMutableFlags = sleMptIssuance->getFieldU32(sfMutableFlags);
213
214 auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool {
215 return currentMutableFlags & mutableFlag;
216 };
217
218 auto const mutableFlags = ctx.tx[~sfMutableFlags];
219 // Whether the transaction is enabling confidential amounts.
220 bool const enablesConfidentialAmount =
221 mutableFlags && (*mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u;
222 if (mutableFlags)
223 {
224 if (std::ranges::any_of(kMptMutabilityFlags, [mutableFlags, &isMutableFlag](auto const& f) {
225 return !isMutableFlag(f.canEnableFlag) && ((*mutableFlags & f.setFlag) != 0u);
226 }))
227 return tecNO_PERMISSION;
228
229 if (enablesConfidentialAmount &&
230 isMutableFlag(lsmfMPTCannotEnableCanHoldConfidentialBalance))
231 return tecNO_PERMISSION;
232 }
233
234 if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
235 return tecNO_PERMISSION;
236
237 if (auto const fee = ctx.tx[~sfTransferFee])
238 {
239 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
240 // was previously enabled (at issuance or via a prior mutation). Setting
241 // it by tmfMPTSetCanTransfer in the current transaction does not meet
242 // this requirement.
243 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
244 return tecNO_PERMISSION;
245
246 // Cannot set a non-zero TransferFee on an issuance that has confidential
247 // transfer enabled
248 if (fee > 0u && sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance))
249 return tecNO_PERMISSION;
250
251 if (!isMutableFlag(lsmfMPTCanMutateTransferFee))
252 return tecNO_PERMISSION;
253 }
254
255 // cannot update issuer public key
256 if (ctx.tx.isFieldPresent(sfIssuerEncryptionKey) &&
257 sleMptIssuance->isFieldPresent(sfIssuerEncryptionKey))
258 {
259 return tecNO_PERMISSION;
260 }
261
262 // cannot update auditor public key
263 if (ctx.tx.isFieldPresent(sfAuditorEncryptionKey) &&
264 sleMptIssuance->isFieldPresent(sfAuditorEncryptionKey))
265 {
266 return tecNO_PERMISSION; // LCOV_EXCL_LINE
267 }
268
269 if (enablesConfidentialAmount && sleMptIssuance->isFieldPresent(sfTransferFee) &&
270 (*sleMptIssuance)[sfTransferFee] > 0u)
271 return tecNO_PERMISSION;
272
273 // Encryption keys can only be set if confidential amounts are already
274 // enabled on the issuance OR if the transaction is enabling it
275 if (ctx.tx.isFieldPresent(sfIssuerEncryptionKey) &&
276 !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialAmount)
277 {
278 return tecNO_PERMISSION;
279 }
280
281 if (ctx.tx.isFieldPresent(sfAuditorEncryptionKey) &&
282 !sleMptIssuance->isFlag(lsfMPTCanHoldConfidentialBalance) && !enablesConfidentialAmount)
283 {
284 return tecNO_PERMISSION;
285 }
286
287 // cannot upload key if there's circulating supply of COA
288 if ((ctx.tx.isFieldPresent(sfIssuerEncryptionKey) ||
289 ctx.tx.isFieldPresent(sfAuditorEncryptionKey) || enablesConfidentialAmount) &&
290 (*sleMptIssuance)[~sfConfidentialOutstandingAmount].value_or(0) > 0)
291 {
292 return tecNO_PERMISSION; // LCOV_EXCL_LINE
293 }
294
295 return tesSUCCESS;
296}
297
298TER
300{
301 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
302 auto const holderID = ctx_.tx[~sfHolder];
303 auto const domainID = ctx_.tx[~sfDomainID];
304 SLE::pointer sle;
305
306 if (holderID)
307 {
308 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
309 }
310 else
311 {
312 sle = view().peek(keylet::mptokenIssuance(mptIssuanceID));
313 }
314
315 if (!sle)
316 return tecINTERNAL; // LCOV_EXCL_LINE
317
318 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
319 std::uint32_t flagsOut = flagsIn;
320
321 if (ctx_.tx.isFlag(tfMPTLock))
322 {
323 flagsOut |= lsfMPTLocked;
324 }
325 else if (ctx_.tx.isFlag(tfMPTUnlock))
326 {
327 flagsOut &= ~lsfMPTLocked;
328 }
329
330 if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0))
331 {
332 for (auto const& f : kMptMutabilityFlags)
333 {
334 if ((mutableFlags & f.setFlag) != 0u)
335 {
336 flagsOut |= f.ledgerFlag;
337 }
338 }
339
340 if ((mutableFlags & tmfMPTSetCanHoldConfidentialBalance) != 0u)
341 flagsOut |= lsfMPTCanHoldConfidentialBalance;
342 }
343
344 if (flagsIn != flagsOut)
345 sle->setFieldU32(sfFlags, flagsOut);
346
347 if (auto const transferFee = ctx_.tx[~sfTransferFee])
348 {
349 // TransferFee uses soeDEFAULT style:
350 // - If the field is absent, it is interpreted as 0.
351 // - If the field is present, it must be non-zero.
352 // Therefore, when TransferFee is 0, the field should be removed.
353 if (transferFee == 0)
354 {
355 sle->makeFieldAbsent(sfTransferFee);
356 }
357 else
358 {
359 sle->setFieldU16(sfTransferFee, *transferFee);
360 }
361 }
362
363 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
364 {
365 if (metadata->empty())
366 {
367 sle->makeFieldAbsent(sfMPTokenMetadata);
368 }
369 else
370 {
371 sle->setFieldVL(sfMPTokenMetadata, *metadata);
372 }
373 }
374
375 if (domainID)
376 {
377 // This is enforced in preflight.
378 XRPL_ASSERT(
379 sle->getType() == ltMPTOKEN_ISSUANCE,
380 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
381
382 if (*domainID != beast::kZero)
383 {
384 sle->setFieldH256(sfDomainID, *domainID);
385 }
386 else
387 {
388 if (sle->isFieldPresent(sfDomainID))
389 sle->makeFieldAbsent(sfDomainID);
390 }
391 }
392
393 if (auto const pubKey = ctx_.tx[~sfIssuerEncryptionKey])
394 {
395 // This is enforced in preflight.
396 XRPL_ASSERT(
397 sle->getType() == ltMPTOKEN_ISSUANCE,
398 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
399
400 sle->setFieldVL(sfIssuerEncryptionKey, *pubKey);
401 }
402
403 if (auto const pubKey = ctx_.tx[~sfAuditorEncryptionKey])
404 {
405 // This is enforced in preflight.
406 XRPL_ASSERT(
407 sle->getType() == ltMPTOKEN_ISSUANCE,
408 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
409
410 sle->setFieldVL(sfAuditorEncryptionKey, *pubKey);
411 }
412
413 view().update(sle);
414
415 return tesSUCCESS;
416}
417
418void
420{
421 // No transaction-specific invariants yet (future work).
422}
423
424bool
426 STTx const&,
427 TER,
428 XRPAmount,
429 ReadView const&,
430 beast::Journal const&)
431{
432 // No transaction-specific invariants yet (future work).
433 return true;
434}
435
436} // namespace xrpl
T any_of(T... args)
A generic endpoint for log messages.
Definition Journal.h:38
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)
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.
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
static TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
A view into a ledger.
Definition ReadView.h:31
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:171
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
bool isFlag(std::uint32_t) const
Definition STObject.cpp:501
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:454
std::uint32_t getFlags() const
Definition STObject.cpp:507
ApplyView & view()
Definition Transactor.h:136
ApplyContext & ctx_
Definition Transactor.h:116
Keylet mptokenIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:521
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:533
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:186
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:569
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static constexpr std::array< MPTMutabilityFlags, 6 > kMptMutabilityFlags
constexpr FlagValue tmfMPTSetCanLock
Definition TxFlags.h:365
constexpr FlagValue tmfMPTSetCanClawback
Definition TxFlags.h:370
constexpr FlagValue tmfMPTSetCanHoldConfidentialBalance
Definition TxFlags.h:371
bool isValidCompressedECPoint(Slice const &buffer)
Verifies that a buffer contains a valid, parsable compressed EC point.
constexpr FlagValue tmfMPTSetCanTrade
Definition TxFlags.h:368
constexpr FlagValue tmfMPTSetCanTransfer
Definition TxFlags.h:369
constexpr FlagValue tmfMPTSetCanEscrow
Definition TxFlags.h:367
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:594
constexpr std::size_t kMaxMpTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:235
constexpr FlagValue tmfMPTokenIssuanceSetMutableMask
Definition TxFlags.h:372
constexpr std::uint16_t kMaxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:70
@ temINVALID_FLAG
Definition TER.h:97
@ temMALFORMED
Definition TER.h:73
@ temDISABLED
Definition TER.h:100
@ temBAD_TRANSFER_FEE
Definition TER.h:128
TERSubset< CanCvtToTER > TER
Definition TER.h:634
@ tecOBJECT_NOT_FOUND
Definition TER.h:324
@ tecINTERNAL
Definition TER.h:308
@ tecNO_PERMISSION
Definition TER.h:303
@ tecNO_DST
Definition TER.h:288
constexpr FlagValue tfUniversalMask
Definition TxFlags.h:45
@ tesSUCCESS
Definition TER.h:240
constexpr FlagValue tmfMPTSetRequireAuth
Definition TxFlags.h:366
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:61
ReadView const & view
Definition Transactor.h:64
State information when preflighting a tx.
Definition Transactor.h:18