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