rippled
Loading...
Searching...
No Matches
MPTokenIssuanceSet.cpp
1#include <xrpl/protocol/Feature.h>
2#include <xrpl/protocol/LedgerFormats.h>
3#include <xrpl/protocol/TxFlags.h>
4#include <xrpl/tx/transactors/delegate/DelegateUtils.h>
5#include <xrpl/tx/transactors/token/MPTokenIssuanceSet.h>
6
7namespace xrpl {
8
9bool
11{
12 return !ctx.tx.isFieldPresent(sfDomainID) ||
13 (ctx.rules.enabled(featurePermissionedDomains) &&
14 ctx.rules.enabled(featureSingleAssetVault));
15}
16
19{
20 return tfMPTokenIssuanceSetMask;
21}
22
23// Maps set/clear mutable flags in an MPTokenIssuanceSet transaction to the
24// corresponding ledger mutable flags that control whether the change is
25// allowed.
32
34 {{tmfMPTSetCanLock, tmfMPTClearCanLock, lsmfMPTCanMutateCanLock},
35 {tmfMPTSetRequireAuth, tmfMPTClearRequireAuth, lsmfMPTCanMutateRequireAuth},
36 {tmfMPTSetCanEscrow, tmfMPTClearCanEscrow, lsmfMPTCanMutateCanEscrow},
37 {tmfMPTSetCanTrade, tmfMPTClearCanTrade, lsmfMPTCanMutateCanTrade},
38 {tmfMPTSetCanTransfer, tmfMPTClearCanTransfer, lsmfMPTCanMutateCanTransfer},
39 {tmfMPTSetCanClawback, tmfMPTClearCanClawback, lsmfMPTCanMutateCanClawback}}};
40
43{
44 auto const mutableFlags = ctx.tx[~sfMutableFlags];
45 auto const metadata = ctx.tx[~sfMPTokenMetadata];
46 auto const transferFee = ctx.tx[~sfTransferFee];
47 auto const isMutate = mutableFlags || metadata || transferFee;
48
49 if (isMutate && !ctx.rules.enabled(featureDynamicMPT))
50 return temDISABLED;
51
52 if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder))
53 return temMALFORMED;
54
55 auto const txFlags = ctx.tx.getFlags();
56
57 // fails if both flags are set
58 if (((txFlags & tfMPTLock) != 0u) && ((txFlags & tfMPTUnlock) != 0u))
59 return temINVALID_FLAG;
60
61 auto const accountID = ctx.tx[sfAccount];
62 auto const holderID = ctx.tx[~sfHolder];
63 if (holderID && accountID == holderID)
64 return temMALFORMED;
65
66 if (ctx.rules.enabled(featureSingleAssetVault) || ctx.rules.enabled(featureDynamicMPT))
67 {
68 // Is this transaction actually changing anything ?
69 if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate)
70 return temMALFORMED;
71 }
72
73 if (ctx.rules.enabled(featureDynamicMPT))
74 {
75 // Holder field is not allowed when mutating MPTokenIssuance
76 if (isMutate && holderID)
77 return temMALFORMED;
78
79 // Can not set flags when mutating MPTokenIssuance
80 if (isMutate && ((txFlags & tfUniversalMask) != 0u))
81 return temMALFORMED;
82
83 if (transferFee && *transferFee > maxTransferFee)
85
86 if (metadata && metadata->length() > maxMPTokenMetadataLength)
87 return temMALFORMED;
88
89 if (mutableFlags)
90 {
91 if ((*mutableFlags == 0u) || ((*mutableFlags & tmfMPTokenIssuanceSetMutableMask) != 0u))
92 return temINVALID_FLAG;
93
94 // Can not set and clear the same flag
95 if (std::any_of(
96 mptMutabilityFlags.begin(),
98 [mutableFlags](auto const& f) {
99 return (*mutableFlags & f.setFlag) && (*mutableFlags & f.clearFlag);
100 }))
101 return temINVALID_FLAG;
102
103 // Trying to set a non-zero TransferFee and clear MPTCanTransfer
104 // in the same transaction is not allowed.
105 if ((transferFee.value_or(0) != 0u) && ((*mutableFlags & tmfMPTClearCanTransfer) != 0u))
106 return temMALFORMED;
107 }
108 }
109
110 return tesSUCCESS;
111}
112
113NotTEC
115{
116 auto const delegate = tx[~sfDelegate];
117 if (!delegate)
118 return tesSUCCESS;
119
120 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
121 auto const sle = view.read(delegateKey);
122
123 if (!sle)
125
126 if (isTesSuccess(checkTxPermission(sle, tx)))
127 return tesSUCCESS;
128
129 auto const txFlags = tx.getFlags();
130
131 // this is added in case more flags will be added for MPTokenIssuanceSet
132 // in the future. Currently unreachable.
133 if ((txFlags & tfMPTokenIssuanceSetMask) != 0u)
134 return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
135
137 loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
138
139 if (((txFlags & tfMPTLock) != 0u) && !granularPermissions.contains(MPTokenIssuanceLock))
141
142 if (((txFlags & tfMPTUnlock) != 0u) && !granularPermissions.contains(MPTokenIssuanceUnlock))
144
145 return tesSUCCESS;
146}
147
148TER
150{
151 // ensure that issuance exists
152 auto const sleMptIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
153 if (!sleMptIssuance)
154 return tecOBJECT_NOT_FOUND;
155
156 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
157 {
158 // For readability two separate `if` rather than `||` of two conditions
159 if (!ctx.view.rules().enabled(featureSingleAssetVault) &&
160 !ctx.view.rules().enabled(featureDynamicMPT))
161 {
162 return tecNO_PERMISSION;
163 }
164 if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
165 {
166 return tecNO_PERMISSION;
167 }
168 }
169
170 // ensure it is issued by the tx submitter
171 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
172 return tecNO_PERMISSION;
173
174 if (auto const holderID = ctx.tx[~sfHolder])
175 {
176 // make sure holder account exists
177 if (!ctx.view.exists(keylet::account(*holderID)))
178 return tecNO_DST;
179
180 // the mptoken must exist
181 if (!ctx.view.exists(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
182 return tecOBJECT_NOT_FOUND;
183 }
184
185 if (auto const domain = ctx.tx[~sfDomainID])
186 {
187 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
188 return tecNO_PERMISSION;
189
190 if (*domain != beast::zero)
191 {
192 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
193 if (!sleDomain)
194 return tecOBJECT_NOT_FOUND;
195 }
196 }
197
198 // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
199 // the ledger.
200 auto const currentMutableFlags = sleMptIssuance->getFieldU32(sfMutableFlags);
201
202 auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool {
203 return currentMutableFlags & mutableFlag;
204 };
205
206 if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
207 {
208 if (std::any_of(
209 mptMutabilityFlags.begin(),
210 mptMutabilityFlags.end(),
211 [mutableFlags, &isMutableFlag](auto const& f) {
212 return !isMutableFlag(f.canMutateFlag) &&
213 ((*mutableFlags & (f.setFlag | f.clearFlag)));
214 }))
215 return tecNO_PERMISSION;
216 }
217
218 if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
219 return tecNO_PERMISSION;
220
221 if (auto const fee = ctx.tx[~sfTransferFee])
222 {
223 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
224 // was previously enabled (at issuance or via a prior mutation). Setting
225 // it by tmfMPTSetCanTransfer in the current transaction does not meet
226 // this requirement.
227 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
228 return tecNO_PERMISSION;
229
230 if (!isMutableFlag(lsmfMPTCanMutateTransferFee))
231 return tecNO_PERMISSION;
232 }
233
234 return tesSUCCESS;
235}
236
237TER
239{
240 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
241 auto const txFlags = ctx_.tx.getFlags();
242 auto const holderID = ctx_.tx[~sfHolder];
243 auto const domainID = ctx_.tx[~sfDomainID];
245
246 if (holderID)
247 {
248 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
249 }
250 else
251 {
252 sle = view().peek(keylet::mptIssuance(mptIssuanceID));
253 }
254
255 if (!sle)
256 return tecINTERNAL; // LCOV_EXCL_LINE
257
258 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
259 std::uint32_t flagsOut = flagsIn;
260
261 if ((txFlags & tfMPTLock) != 0u)
262 {
263 flagsOut |= lsfMPTLocked;
264 }
265 else if ((txFlags & tfMPTUnlock) != 0u)
266 {
267 flagsOut &= ~lsfMPTLocked;
268 }
269
270 if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0))
271 {
272 for (auto const& f : mptMutabilityFlags)
273 {
274 if ((mutableFlags & f.setFlag) != 0u)
275 {
276 flagsOut |= f.canMutateFlag;
277 }
278 else if ((mutableFlags & f.clearFlag) != 0u)
279 {
280 flagsOut &= ~f.canMutateFlag;
281 }
282 }
283
284 if ((mutableFlags & tmfMPTClearCanTransfer) != 0u)
285 {
286 // If the lsfMPTCanTransfer flag is being cleared, then also clear
287 // the TransferFee field.
288 sle->makeFieldAbsent(sfTransferFee);
289 }
290 }
291
292 if (flagsIn != flagsOut)
293 sle->setFieldU32(sfFlags, flagsOut);
294
295 if (auto const transferFee = ctx_.tx[~sfTransferFee])
296 {
297 // TransferFee uses soeDEFAULT style:
298 // - If the field is absent, it is interpreted as 0.
299 // - If the field is present, it must be non-zero.
300 // Therefore, when TransferFee is 0, the field should be removed.
301 if (transferFee == 0)
302 {
303 sle->makeFieldAbsent(sfTransferFee);
304 }
305 else
306 {
307 sle->setFieldU16(sfTransferFee, *transferFee);
308 }
309 }
310
311 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
312 {
313 if (metadata->empty())
314 {
315 sle->makeFieldAbsent(sfMPTokenMetadata);
316 }
317 else
318 {
319 sle->setFieldVL(sfMPTokenMetadata, *metadata);
320 }
321 }
322
323 if (domainID)
324 {
325 // This is enforced in preflight.
326 XRPL_ASSERT(
327 sle->getType() == ltMPTOKEN_ISSUANCE,
328 "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
329
330 if (*domainID != beast::zero)
331 {
332 sle->setFieldH256(sfDomainID, *domainID);
333 }
334 else
335 {
336 if (sle->isFieldPresent(sfDomainID))
337 sle->makeFieldAbsent(sfDomainID);
338 }
339 }
340
341 view().update(sle);
342
343 return tesSUCCESS;
344}
345
346} // namespace xrpl
T any_of(T... args)
STTx const & tx
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 NotTEC preflight(PreflightContext const &ctx)
static bool checkExtraFeatures(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
static std::uint32_t getFlagsMask(PreflightContext const &ctx)
static NotTEC checkPermission(ReadView const &view, STTx const &tx)
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 std::shared_ptr< SLE const > 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:120
bool isFlag(std::uint32_t) const
Definition STObject.cpp:503
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:456
std::uint32_t getFlags() const
Definition STObject.cpp:509
ApplyView & view()
Definition Transactor.h:132
ApplyContext & ctx_
Definition Transactor.h:112
T contains(T... args)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:474
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:486
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:418
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:165
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:522
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
@ terNO_DELEGATE_PERMISSION
Definition TER.h:210
constexpr FlagValue tmfMPTSetCanLock
Definition TxFlags.h:358
constexpr FlagValue tmfMPTSetCanClawback
Definition TxFlags.h:368
static constexpr std::array< MPTMutabilityFlags, 6 > mptMutabilityFlags
constexpr FlagValue tmfMPTClearCanTrade
Definition TxFlags.h:365
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:231
constexpr FlagValue tmfMPTSetCanTrade
Definition TxFlags.h:364
constexpr FlagValue tmfMPTClearCanLock
Definition TxFlags.h:359
constexpr FlagValue tmfMPTClearCanEscrow
Definition TxFlags.h:363
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 FlagValue tmfMPTSetCanTransfer
Definition TxFlags.h:366
constexpr FlagValue tmfMPTSetCanEscrow
Definition TxFlags.h:362
constexpr FlagValue tmfMPTClearCanTransfer
Definition TxFlags.h:367
NotTEC checkTxPermission(std::shared_ptr< SLE const > const &delegate, STTx const &tx)
Check if the delegate account has permission to execute the transaction.
constexpr FlagValue tmfMPTokenIssuanceSetMutableMask
Definition TxFlags.h:370
@ temINVALID_FLAG
Definition TER.h:91
@ temMALFORMED
Definition TER.h:67
@ temDISABLED
Definition TER.h:94
@ temBAD_TRANSFER_FEE
Definition TER.h:122
bool isTesSuccess(TER x) noexcept
Definition TER.h:651
constexpr FlagValue tmfMPTClearRequireAuth
Definition TxFlags.h:361
@ tecOBJECT_NOT_FOUND
Definition TER.h:307
@ tecINTERNAL
Definition TER.h:291
@ tecNO_PERMISSION
Definition TER.h:286
@ tecNO_DST
Definition TER.h:271
std::uint16_t constexpr maxTransferFee
The maximum token transfer fee allowed.
Definition Protocol.h:66
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:582
constexpr FlagValue tmfMPTClearCanClawback
Definition TxFlags.h:369
constexpr FlagValue tfUniversalMask
Definition TxFlags.h:43
@ tesSUCCESS
Definition TER.h:225
constexpr FlagValue tmfMPTSetRequireAuth
Definition TxFlags.h:360
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:57
ReadView const & view
Definition Transactor.h:60
State information when preflighting a tx.
Definition Transactor.h:14