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
29// Maps set/clear mutable flags in an MPTokenIssuanceSet transaction to the
30// corresponding ledger mutable flags that control whether the change is
31// allowed.
38
48
51{
52 if (!ctx.rules.enabled(featureMPTokensV1))
53 return temDISABLED;
54
55 if (ctx.tx.isFieldPresent(sfDomainID) &&
56 !(ctx.rules.enabled(featurePermissionedDomains) &&
57 ctx.rules.enabled(featureSingleAssetVault)))
58 return temDISABLED;
59
60 auto const mutableFlags = ctx.tx[~sfMutableFlags];
61 auto const metadata = ctx.tx[~sfMPTokenMetadata];
62 auto const transferFee = ctx.tx[~sfTransferFee];
63 auto const isMutate = mutableFlags || metadata || transferFee;
64
65 if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
66 return temDISABLED;
67
68 if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
69 return temMALFORMED;
70
71 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
72 return ret;
73
74 auto const txFlags = ctx.tx.getFlags();
75
76 // check flags
77 if (txFlags & tfMPTokenIssuanceSetMask)
78 return temINVALID_FLAG;
79 // fails if both flags are set
80 else if ((txFlags & tfMPTLock) && (txFlags & tfMPTUnlock))
81 return temINVALID_FLAG;
82
83 auto const accountID = ctx.tx[sfAccount];
84 auto const holderID = ctx.tx[~sfHolder];
85 if (holderID && accountID == holderID)
86 return temMALFORMED;
87
88 if (ctx.rules.enabled(featureSingleAssetVault) ||
89 ctx.rules.enabled(featureDynamicMPT))
90 {
91 // Is this transaction actually changing anything ?
92 if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
93 return temMALFORMED;
94 }
95
96 if (ctx.rules.enabled(featureDynamicMPT))
97 {
98 // Holder field is not allowed when mutating MPTokenIssuance
99 if (isMutate && holderID)
100 return temMALFORMED;
101
102 // Can not set flags when mutating MPTokenIssuance
103 if (isMutate && (txFlags & tfUniversalMask))
104 return temMALFORMED;
105
106 if (transferFee && *transferFee > maxTransferFee)
107 return temBAD_TRANSFER_FEE;
108
109 if (metadata && metadata->length() > maxMPTokenMetadataLength)
110 return temMALFORMED;
111
112 if (mutableFlags)
113 {
114 if (!*mutableFlags ||
115 (*mutableFlags & tmfMPTokenIssuanceSetMutableMask))
116 return temINVALID_FLAG;
117
118 // Can not set and clear the same flag
119 if (std::any_of(
120 mptMutabilityFlags.begin(),
121 mptMutabilityFlags.end(),
122 [mutableFlags](auto const& f) {
123 return (*mutableFlags & f.setFlag) &&
124 (*mutableFlags & f.clearFlag);
125 }))
126 return temINVALID_FLAG;
127
128 // Trying to set a non-zero TransferFee and clear MPTCanTransfer
129 // in the same transaction is not allowed.
130 if (transferFee.value_or(0) &&
131 (*mutableFlags & tmfMPTClearCanTransfer))
132 return temMALFORMED;
133 }
134 }
135
136 return preflight2(ctx);
137}
138
139TER
141{
142 auto const delegate = tx[~sfDelegate];
143 if (!delegate)
144 return tesSUCCESS;
145
146 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
147 auto const sle = view.read(delegateKey);
148
149 if (!sle)
151
152 if (checkTxPermission(sle, tx) == tesSUCCESS)
153 return tesSUCCESS;
154
155 auto const txFlags = tx.getFlags();
156
157 // this is added in case more flags will be added for MPTokenIssuanceSet
158 // in the future. Currently unreachable.
160 return tecNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
161
163 loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
164
165 if (txFlags & tfMPTLock &&
166 !granularPermissions.contains(MPTokenIssuanceLock))
168
169 if (txFlags & tfMPTUnlock &&
170 !granularPermissions.contains(MPTokenIssuanceUnlock))
172
173 return tesSUCCESS;
174}
175
176TER
178{
179 // ensure that issuance exists
180 auto const sleMptIssuance =
181 ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
182 if (!sleMptIssuance)
183 return tecOBJECT_NOT_FOUND;
184
185 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
186 {
187 // For readability two separate `if` rather than `||` of two conditions
188 if (!ctx.view.rules().enabled(featureSingleAssetVault) &&
189 !ctx.view.rules().enabled(featureDynamicMPT))
190 return tecNO_PERMISSION;
191 else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
192 return tecNO_PERMISSION;
193 }
194
195 // ensure it is issued by the tx submitter
196 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
197 return tecNO_PERMISSION;
198
199 if (auto const holderID = ctx.tx[~sfHolder])
200 {
201 // make sure holder account exists
202 if (!ctx.view.exists(keylet::account(*holderID)))
203 return tecNO_DST;
204
205 // the mptoken must exist
206 if (!ctx.view.exists(
207 keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
208 return tecOBJECT_NOT_FOUND;
209 }
210
211 if (auto const domain = ctx.tx[~sfDomainID])
212 {
213 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
214 return tecNO_PERMISSION;
215
216 if (*domain != beast::zero)
217 {
218 auto const sleDomain =
220 if (!sleDomain)
221 return tecOBJECT_NOT_FOUND;
222 }
223 }
224
225 // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
226 // the ledger.
227 auto const currentMutableFlags =
228 sleMptIssuance->getFieldU32(sfMutableFlags);
229
230 auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool {
231 return currentMutableFlags & mutableFlag;
232 };
233
234 if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
235 {
236 if (std::any_of(
237 mptMutabilityFlags.begin(),
238 mptMutabilityFlags.end(),
239 [mutableFlags, &isMutableFlag](auto const& f) {
240 return !isMutableFlag(f.canMutateFlag) &&
241 ((*mutableFlags & (f.setFlag | f.clearFlag)));
242 }))
243 return tecNO_PERMISSION;
244 }
245
246 if (!isMutableFlag(lmfMPTCanMutateMetadata) &&
247 ctx.tx.isFieldPresent(sfMPTokenMetadata))
248 return tecNO_PERMISSION;
249
250 if (auto const fee = ctx.tx[~sfTransferFee])
251 {
252 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
253 // was previously enabled (at issuance or via a prior mutation). Setting
254 // it by tmfMPTSetCanTransfer in the current transaction does not meet
255 // this requirement.
256 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
257 return tecNO_PERMISSION;
258
259 if (!isMutableFlag(lmfMPTCanMutateTransferFee))
260 return tecNO_PERMISSION;
261 }
262
263 return tesSUCCESS;
264}
265
266TER
268{
269 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
270 auto const txFlags = ctx_.tx.getFlags();
271 auto const holderID = ctx_.tx[~sfHolder];
272 auto const domainID = ctx_.tx[~sfDomainID];
274
275 if (holderID)
276 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
277 else
278 sle = view().peek(keylet::mptIssuance(mptIssuanceID));
279
280 if (!sle)
281 return tecINTERNAL;
282
283 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
284 std::uint32_t flagsOut = flagsIn;
285
286 if (txFlags & tfMPTLock)
287 flagsOut |= lsfMPTLocked;
288 else if (txFlags & tfMPTUnlock)
289 flagsOut &= ~lsfMPTLocked;
290
291 if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0))
292 {
293 for (auto const& f : mptMutabilityFlags)
294 {
295 if (mutableFlags & f.setFlag)
296 flagsOut |= f.canMutateFlag;
297 else if (mutableFlags & f.clearFlag)
298 flagsOut &= ~f.canMutateFlag;
299 }
300
301 if (mutableFlags & tmfMPTClearCanTransfer)
302 {
303 // If the lsfMPTCanTransfer flag is being cleared, then also clear
304 // the TransferFee field.
305 sle->makeFieldAbsent(sfTransferFee);
306 }
307 }
308
309 if (flagsIn != flagsOut)
310 sle->setFieldU32(sfFlags, flagsOut);
311
312 if (auto const transferFee = ctx_.tx[~sfTransferFee])
313 {
314 // TransferFee uses soeDEFAULT style:
315 // - If the field is absent, it is interpreted as 0.
316 // - If the field is present, it must be non-zero.
317 // Therefore, when TransferFee is 0, the field should be removed.
318 if (transferFee == 0)
319 sle->makeFieldAbsent(sfTransferFee);
320 else
321 sle->setFieldU16(sfTransferFee, *transferFee);
322 }
323
324 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
325 {
326 if (metadata->empty())
327 sle->makeFieldAbsent(sfMPTokenMetadata);
328 else
329 sle->setFieldVL(sfMPTokenMetadata, *metadata);
330 }
331
332 if (domainID)
333 {
334 // This is enforced in preflight.
335 XRPL_ASSERT(
336 sle->getType() == ltMPTOKEN_ISSUANCE,
337 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
338
339 if (*domainID != beast::zero)
340 {
341 sle->setFieldH256(sfDomainID, *domainID);
342 }
343 else
344 {
345 if (sle->isFieldPresent(sfDomainID))
346 sle->makeFieldAbsent(sfDomainID);
347 }
348 }
349
350 view().update(sle);
351
352 return tesSUCCESS;
353}
354
355} // 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 TER 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:161
ApplyContext & ctx_
Definition Transactor.h:141
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
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
@ lsfMPTCanTransfer
@ lmfMPTCanMutateMetadata
@ lmfMPTCanMutateCanTransfer
@ lmfMPTCanMutateTransferFee
@ lmfMPTCanMutateCanClawback
@ lmfMPTCanMutateRequireAuth
@ lsfMPTRequireAuth
@ lmfMPTCanMutateCanLock
@ lmfMPTCanMutateCanEscrow
@ lmfMPTCanMutateCanTrade
constexpr std::uint32_t const tmfMPTClearRequireAuth
Definition TxFlags.h:186
std::uint16_t constexpr maxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:82
constexpr std::uint32_t const tmfMPTSetCanLock
Definition TxFlags.h:183
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
constexpr std::uint32_t const tmfMPTSetCanTrade
Definition TxFlags.h:189
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:113
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
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
constexpr std::uint32_t const tmfMPTSetCanTransfer
Definition TxFlags.h:191
TER checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
@ tecNO_DELEGATE_PERMISSION
Definition TER.h:364
@ tecNO_DST
Definition TER.h:290
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecINTERNAL
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:305
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:176
@ tesSUCCESS
Definition TER.h:244
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:63
constexpr std::uint32_t const tmfMPTClearCanTransfer
Definition TxFlags.h:192
static constexpr std::array< MPTMutabilityFlags, 6 > mptMutabilityFlags
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ 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