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