rippled
Loading...
Searching...
No Matches
MPTokenIssuanceSet.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2024 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/DelegateUtils.h>
21#include <xrpld/app/tx/detail/MPTokenIssuanceSet.h>
22
23#include <xrpl/protocol/Feature.h>
24#include <xrpl/protocol/LedgerFormats.h>
25#include <xrpl/protocol/TxFlags.h>
26
27namespace ripple {
28
29bool
31{
32 return !ctx.tx.isFieldPresent(sfDomainID) ||
33 (ctx.rules.enabled(featurePermissionedDomains) &&
34 ctx.rules.enabled(featureSingleAssetVault));
35}
36
42
43// Maps set/clear mutable flags in an MPTokenIssuanceSet transaction to the
44// corresponding ledger mutable flags that control whether the change is
45// allowed.
52
66
69{
70 auto const mutableFlags = ctx.tx[~sfMutableFlags];
71 auto const metadata = ctx.tx[~sfMPTokenMetadata];
72 auto const transferFee = ctx.tx[~sfTransferFee];
73 auto const isMutate = mutableFlags || metadata || transferFee;
74
75 if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
76 return temDISABLED;
77
78 if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
79 return temMALFORMED;
80
81 auto const txFlags = ctx.tx.getFlags();
82
83 // fails if both flags are set
84 if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock))
85 return temINVALID_FLAG;
86
87 auto const accountID = ctx.tx[sfAccount];
88 auto const holderID = ctx.tx[~sfHolder];
89 if (holderID && accountID == holderID)
90 return temMALFORMED;
91
92 if (ctx.rules.enabled(featureSingleAssetVault) ||
93 ctx.rules.enabled(featureDynamicMPT))
94 {
95 // Is this transaction actually changing anything ?
96 if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
97 return temMALFORMED;
98 }
99
100 if (ctx.rules.enabled(featureDynamicMPT))
101 {
102 // Holder field is not allowed when mutating MPTokenIssuance
103 if (isMutate && holderID)
104 return temMALFORMED;
105
106 // Can not set flags when mutating MPTokenIssuance
107 if (isMutate && (txFlags & tfUniversalMask))
108 return temMALFORMED;
109
110 if (transferFee && *transferFee > maxTransferFee)
111 return temBAD_TRANSFER_FEE;
112
113 if (metadata && metadata->length() > maxMPTokenMetadataLength)
114 return temMALFORMED;
115
116 if (mutableFlags)
117 {
118 if (!*mutableFlags ||
119 (*mutableFlags & tmfMPTokenIssuanceSetMutableMask))
120 return temINVALID_FLAG;
121
122 // Can not set and clear the same flag
123 if (std::any_of(
124 mptMutabilityFlags.begin(),
125 mptMutabilityFlags.end(),
126 [mutableFlags](auto const& f) {
127 return (*mutableFlags & f.setFlag) &&
128 (*mutableFlags & f.clearFlag);
129 }))
130 return temINVALID_FLAG;
131
132 // Trying to set a non-zero TransferFee and clear MPTCanTransfer
133 // in the same transaction is not allowed.
134 if (transferFee.value_or(0) &&
135 (*mutableFlags & tmfMPTClearCanTransfer))
136 return temMALFORMED;
137 }
138 }
139
140 return tesSUCCESS;
141}
142
143NotTEC
145{
146 auto const delegate = tx[~sfDelegate];
147 if (!delegate)
148 return tesSUCCESS;
149
150 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
151 auto const sle = view.read(delegateKey);
152
153 if (!sle)
155
156 if (checkTxPermission(sle, tx) == tesSUCCESS)
157 return tesSUCCESS;
158
159 auto const txFlags = tx.getFlags();
160
161 // this is added in case more flags will be added for MPTokenIssuanceSet
162 // in the future. Currently unreachable.
164 return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
165
167 loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
168
169 if (txFlags & tfMPTLock &&
170 !granularPermissions.contains(MPTokenIssuanceLock))
172
173 if (txFlags & tfMPTUnlock &&
174 !granularPermissions.contains(MPTokenIssuanceUnlock))
176
177 return tesSUCCESS;
178}
179
180TER
182{
183 // ensure that issuance exists
184 auto const sleMptIssuance =
185 ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
186 if (!sleMptIssuance)
187 return tecOBJECT_NOT_FOUND;
188
189 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
190 {
191 // For readability two separate `if` rather than `||` of two conditions
192 if (!ctx.view.rules().enabled(featureSingleAssetVault) &&
193 !ctx.view.rules().enabled(featureDynamicMPT))
194 return tecNO_PERMISSION;
195 else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
196 return tecNO_PERMISSION;
197 }
198
199 // ensure it is issued by the tx submitter
200 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
201 return tecNO_PERMISSION;
202
203 if (auto const holderID = ctx.tx[~sfHolder])
204 {
205 // make sure holder account exists
206 if (!ctx.view.exists(keylet::account(*holderID)))
207 return tecNO_DST;
208
209 // the mptoken must exist
210 if (!ctx.view.exists(
211 keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
212 return tecOBJECT_NOT_FOUND;
213 }
214
215 if (auto const domain = ctx.tx[~sfDomainID])
216 {
217 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
218 return tecNO_PERMISSION;
219
220 if (*domain != beast::zero)
221 {
222 auto const sleDomain =
224 if (!sleDomain)
225 return tecOBJECT_NOT_FOUND;
226 }
227 }
228
229 // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
230 // the ledger.
231 auto const currentMutableFlags =
232 sleMptIssuance->getFieldU32(sfMutableFlags);
233
234 auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool {
235 return currentMutableFlags & mutableFlag;
236 };
237
238 if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
239 {
240 if (std::any_of(
241 mptMutabilityFlags.begin(),
242 mptMutabilityFlags.end(),
243 [mutableFlags, &isMutableFlag](auto const& f) {
244 return !isMutableFlag(f.canMutateFlag) &&
245 ((*mutableFlags & (f.setFlag | f.clearFlag)));
246 }))
247 return tecNO_PERMISSION;
248 }
249
250 if (!isMutableFlag(lsmfMPTCanMutateMetadata) &&
251 ctx.tx.isFieldPresent(sfMPTokenMetadata))
252 return tecNO_PERMISSION;
253
254 if (auto const fee = ctx.tx[~sfTransferFee])
255 {
256 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
257 // was previously enabled (at issuance or via a prior mutation). Setting
258 // it by tmfMPTSetCanTransfer in the current transaction does not meet
259 // this requirement.
260 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
261 return tecNO_PERMISSION;
262
263 if (!isMutableFlag(lsmfMPTCanMutateTransferFee))
264 return tecNO_PERMISSION;
265 }
266
267 return tesSUCCESS;
268}
269
270TER
272{
273 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
274 auto const txFlags = ctx_.tx.getFlags();
275 auto const holderID = ctx_.tx[~sfHolder];
276 auto const domainID = ctx_.tx[~sfDomainID];
278
279 if (holderID)
280 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
281 else
282 sle = view().peek(keylet::mptIssuance(mptIssuanceID));
283
284 if (!sle)
285 return tecINTERNAL; // LCOV_EXCL_LINE
286
287 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
288 std::uint32_t flagsOut = flagsIn;
289
290 if (txFlags & tfMPTLock)
291 flagsOut |= lsfMPTLocked;
292 else if (txFlags & tfMPTUnlock)
293 flagsOut &= ~lsfMPTLocked;
294
295 if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0))
296 {
297 for (auto const& f : mptMutabilityFlags)
298 {
299 if (mutableFlags & f.setFlag)
300 flagsOut |= f.canMutateFlag;
301 else if (mutableFlags & f.clearFlag)
302 flagsOut &= ~f.canMutateFlag;
303 }
304
305 if (mutableFlags & tmfMPTClearCanTransfer)
306 {
307 // If the lsfMPTCanTransfer flag is being cleared, then also clear
308 // the TransferFee field.
309 sle->makeFieldAbsent(sfTransferFee);
310 }
311 }
312
313 if (flagsIn != flagsOut)
314 sle->setFieldU32(sfFlags, flagsOut);
315
316 if (auto const transferFee = ctx_.tx[~sfTransferFee])
317 {
318 // TransferFee uses soeDEFAULT style:
319 // - If the field is absent, it is interpreted as 0.
320 // - If the field is present, it must be non-zero.
321 // Therefore, when TransferFee is 0, the field should be removed.
322 if (transferFee == 0)
323 sle->makeFieldAbsent(sfTransferFee);
324 else
325 sle->setFieldU16(sfTransferFee, *transferFee);
326 }
327
328 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
329 {
330 if (metadata->empty())
331 sle->makeFieldAbsent(sfMPTokenMetadata);
332 else
333 sle->setFieldVL(sfMPTokenMetadata, *metadata);
334 }
335
336 if (domainID)
337 {
338 // This is enforced in preflight.
339 XRPL_ASSERT(
340 sle->getType() == ltMPTOKEN_ISSUANCE,
341 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
342
343 if (*domainID != beast::zero)
344 {
345 sle->setFieldH256(sfDomainID, *domainID);
346 }
347 else
348 {
349 if (sle->isFieldPresent(sfDomainID))
350 sle->makeFieldAbsent(sfDomainID);
351 }
352 }
353
354 view().update(sle);
355
356 return tesSUCCESS;
357}
358
359} // namespace ripple
T any_of(T... args)
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
static TER preclaim(PreclaimContext const &ctx)
static NotTEC preflight(PreflightContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
A view into a ledger.
Definition ReadView.h:51
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual bool exists(Keylet const &k) const =0
Determine if a state item exists.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
bool isFlag(std::uint32_t) const
Definition STObject.cpp:531
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:484
std::uint32_t getFlags() const
Definition STObject.cpp:537
ApplyView & view()
Definition Transactor.h:163
ApplyContext & ctx_
Definition Transactor.h:143
T contains(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:465
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:570
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t const tfMPTokenIssuanceSetMask
Definition TxFlags.h:178
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask
Definition TxFlags.h:179
constexpr std::uint32_t const tmfMPTClearCanClawback
Definition TxFlags.h:194
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask
Definition TxFlags.h:195
constexpr std::uint32_t const tmfMPTClearCanEscrow
Definition TxFlags.h:188
constexpr std::uint32_t const tmfMPTSetCanClawback
Definition TxFlags.h:193
constexpr std::uint32_t const tmfMPTSetRequireAuth
Definition TxFlags.h:185
constexpr std::uint32_t const tmfMPTClearCanTrade
Definition TxFlags.h:190
constexpr std::uint32_t const tfMPTUnlock
Definition TxFlags.h:177
@ lsmfMPTCanMutateRequireAuth
@ lsfMPTCanTransfer
@ lsmfMPTCanMutateCanTransfer
@ lsmfMPTCanMutateCanTrade
@ lsmfMPTCanMutateCanEscrow
@ lsmfMPTCanMutateCanClawback
@ lsmfMPTCanMutateMetadata
@ lsmfMPTCanMutateCanLock
@ lsfMPTRequireAuth
@ lsmfMPTCanMutateTransferFee
constexpr std::uint32_t const tmfMPTClearRequireAuth
Definition TxFlags.h:186
std::uint16_t constexpr maxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:85
constexpr std::uint32_t const tmfMPTSetCanLock
Definition TxFlags.h:183
constexpr std::uint32_t const tmfMPTSetCanTrade
Definition TxFlags.h:189
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:116
void loadGranularPermission(std::shared_ptr< SLE const > const &delegate, TxType const &type, std::unordered_set< GranularPermissionType > &granularPermissions)
Load the granular permissions granted to the delegate account for the specified transaction type.
constexpr std::uint32_t const tmfMPTSetCanEscrow
Definition TxFlags.h:187
constexpr std::uint32_t const tmfMPTClearCanLock
Definition TxFlags.h:184
constexpr std::uint32_t const tmfMPTSetCanTransfer
Definition TxFlags.h:191
@ tecNO_DST
Definition TER.h:291
@ tecOBJECT_NOT_FOUND
Definition TER.h:327
@ tecINTERNAL
Definition TER.h:311
@ tecNO_PERMISSION
Definition TER.h:306
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:176
@ tesSUCCESS
Definition TER.h:245
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
@ terNO_DELEGATE_PERMISSION
Definition TER.h:230
constexpr std::uint32_t const tmfMPTClearCanTransfer
Definition TxFlags.h:192
static constexpr std::array< MPTMutabilityFlags, 6 > mptMutabilityFlags
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:609
@ temBAD_TRANSFER_FEE
Definition TER.h:142
@ temMALFORMED
Definition TER.h:87
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
State information when preflighting a tx.
Definition Transactor.h:35