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 xrpl {
9
10bool
12{
13 return !ctx.tx.isFieldPresent(sfDomainID) ||
14 (ctx.rules.enabled(featurePermissionedDomains) && ctx.rules.enabled(featureSingleAssetVault));
15}
16
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
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) && (txFlags & tfMPTUnlock))
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))
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 || (*mutableFlags & tmfMPTokenIssuanceSetMutableMask))
92 return temINVALID_FLAG;
93
94 // Can not set and clear the same flag
95 if (std::any_of(mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags](auto const& f) {
96 return (*mutableFlags & f.setFlag) && (*mutableFlags & f.clearFlag);
97 }))
98 return temINVALID_FLAG;
99
100 // Trying to set a non-zero TransferFee and clear MPTCanTransfer
101 // in the same transaction is not allowed.
102 if (transferFee.value_or(0) && (*mutableFlags & tmfMPTClearCanTransfer))
103 return temMALFORMED;
104 }
105 }
106
107 return tesSUCCESS;
108}
109
110NotTEC
112{
113 auto const delegate = tx[~sfDelegate];
114 if (!delegate)
115 return tesSUCCESS;
116
117 auto const delegateKey = keylet::delegate(tx[sfAccount], *delegate);
118 auto const sle = view.read(delegateKey);
119
120 if (!sle)
122
123 if (checkTxPermission(sle, tx) == tesSUCCESS)
124 return tesSUCCESS;
125
126 auto const txFlags = tx.getFlags();
127
128 // this is added in case more flags will be added for MPTokenIssuanceSet
129 // in the future. Currently unreachable.
131 return terNO_DELEGATE_PERMISSION; // LCOV_EXCL_LINE
132
134 loadGranularPermission(sle, ttMPTOKEN_ISSUANCE_SET, granularPermissions);
135
136 if (txFlags & tfMPTLock && !granularPermissions.contains(MPTokenIssuanceLock))
138
139 if (txFlags & tfMPTUnlock && !granularPermissions.contains(MPTokenIssuanceUnlock))
141
142 return tesSUCCESS;
143}
144
145TER
147{
148 // ensure that issuance exists
149 auto const sleMptIssuance = ctx.view.read(keylet::mptIssuance(ctx.tx[sfMPTokenIssuanceID]));
150 if (!sleMptIssuance)
151 return tecOBJECT_NOT_FOUND;
152
153 if (!sleMptIssuance->isFlag(lsfMPTCanLock))
154 {
155 // For readability two separate `if` rather than `||` of two conditions
156 if (!ctx.view.rules().enabled(featureSingleAssetVault) && !ctx.view.rules().enabled(featureDynamicMPT))
157 return tecNO_PERMISSION;
158 else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock))
159 return tecNO_PERMISSION;
160 }
161
162 // ensure it is issued by the tx submitter
163 if ((*sleMptIssuance)[sfIssuer] != ctx.tx[sfAccount])
164 return tecNO_PERMISSION;
165
166 if (auto const holderID = ctx.tx[~sfHolder])
167 {
168 // make sure holder account exists
169 if (!ctx.view.exists(keylet::account(*holderID)))
170 return tecNO_DST;
171
172 // the mptoken must exist
173 if (!ctx.view.exists(keylet::mptoken(ctx.tx[sfMPTokenIssuanceID], *holderID)))
174 return tecOBJECT_NOT_FOUND;
175 }
176
177 if (auto const domain = ctx.tx[~sfDomainID])
178 {
179 if (not sleMptIssuance->isFlag(lsfMPTRequireAuth))
180 return tecNO_PERMISSION;
181
182 if (*domain != beast::zero)
183 {
184 auto const sleDomain = ctx.view.read(keylet::permissionedDomain(*domain));
185 if (!sleDomain)
186 return tecOBJECT_NOT_FOUND;
187 }
188 }
189
190 // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on
191 // the ledger.
192 auto const currentMutableFlags = sleMptIssuance->getFieldU32(sfMutableFlags);
193
194 auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { return currentMutableFlags & mutableFlag; };
195
196 if (auto const mutableFlags = ctx.tx[~sfMutableFlags])
197 {
198 if (std::any_of(
199 mptMutabilityFlags.begin(), mptMutabilityFlags.end(), [mutableFlags, &isMutableFlag](auto const& f) {
200 return !isMutableFlag(f.canMutateFlag) && ((*mutableFlags & (f.setFlag | f.clearFlag)));
201 }))
202 return tecNO_PERMISSION;
203 }
204
205 if (!isMutableFlag(lsmfMPTCanMutateMetadata) && ctx.tx.isFieldPresent(sfMPTokenMetadata))
206 return tecNO_PERMISSION;
207
208 if (auto const fee = ctx.tx[~sfTransferFee])
209 {
210 // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag
211 // was previously enabled (at issuance or via a prior mutation). Setting
212 // it by tmfMPTSetCanTransfer in the current transaction does not meet
213 // this requirement.
214 if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer))
215 return tecNO_PERMISSION;
216
217 if (!isMutableFlag(lsmfMPTCanMutateTransferFee))
218 return tecNO_PERMISSION;
219 }
220
221 return tesSUCCESS;
222}
223
224TER
226{
227 auto const mptIssuanceID = ctx_.tx[sfMPTokenIssuanceID];
228 auto const txFlags = ctx_.tx.getFlags();
229 auto const holderID = ctx_.tx[~sfHolder];
230 auto const domainID = ctx_.tx[~sfDomainID];
232
233 if (holderID)
234 sle = view().peek(keylet::mptoken(mptIssuanceID, *holderID));
235 else
236 sle = view().peek(keylet::mptIssuance(mptIssuanceID));
237
238 if (!sle)
239 return tecINTERNAL; // LCOV_EXCL_LINE
240
241 std::uint32_t const flagsIn = sle->getFieldU32(sfFlags);
242 std::uint32_t flagsOut = flagsIn;
243
244 if (txFlags & tfMPTLock)
245 flagsOut |= lsfMPTLocked;
246 else if (txFlags & tfMPTUnlock)
247 flagsOut &= ~lsfMPTLocked;
248
249 if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0))
250 {
251 for (auto const& f : mptMutabilityFlags)
252 {
253 if (mutableFlags & f.setFlag)
254 flagsOut |= f.canMutateFlag;
255 else if (mutableFlags & f.clearFlag)
256 flagsOut &= ~f.canMutateFlag;
257 }
258
259 if (mutableFlags & tmfMPTClearCanTransfer)
260 {
261 // If the lsfMPTCanTransfer flag is being cleared, then also clear
262 // the TransferFee field.
263 sle->makeFieldAbsent(sfTransferFee);
264 }
265 }
266
267 if (flagsIn != flagsOut)
268 sle->setFieldU32(sfFlags, flagsOut);
269
270 if (auto const transferFee = ctx_.tx[~sfTransferFee])
271 {
272 // TransferFee uses soeDEFAULT style:
273 // - If the field is absent, it is interpreted as 0.
274 // - If the field is present, it must be non-zero.
275 // Therefore, when TransferFee is 0, the field should be removed.
276 if (transferFee == 0)
277 sle->makeFieldAbsent(sfTransferFee);
278 else
279 sle->setFieldU16(sfTransferFee, *transferFee);
280 }
281
282 if (auto const metadata = ctx_.tx[~sfMPTokenMetadata])
283 {
284 if (metadata->empty())
285 sle->makeFieldAbsent(sfMPTokenMetadata);
286 else
287 sle->setFieldVL(sfMPTokenMetadata, *metadata);
288 }
289
290 if (domainID)
291 {
292 // This is enforced in preflight.
293 XRPL_ASSERT(sle->getType() == ltMPTOKEN_ISSUANCE, "MPTokenIssuanceSet::doApply : modifying MPTokenIssuance");
294
295 if (*domainID != beast::zero)
296 {
297 sle->setFieldH256(sfDomainID, *domainID);
298 }
299 else
300 {
301 if (sle->isFieldPresent(sfDomainID))
302 sle->makeFieldAbsent(sfDomainID);
303 }
304 }
305
306 view().update(sle);
307
308 return tesSUCCESS;
309}
310
311} // 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:118
bool isFlag(std::uint32_t) const
Definition STObject.cpp:486
bool isFieldPresent(SField const &field) const
Definition STObject.cpp:439
std::uint32_t getFlags() const
Definition STObject.cpp:492
ApplyView & view()
Definition Transactor.h:128
ApplyContext & ctx_
Definition Transactor.h:108
T contains(T... args)
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:462
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:474
Keylet delegate(AccountID const &account, AccountID const &authorizedAccount) noexcept
A keylet for Delegate object.
Definition Indexes.cpp:406
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:160
Keylet permissionedDomain(AccountID const &account, std::uint32_t seq) noexcept
Definition Indexes.cpp:510
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 std::uint32_t const tmfMPTClearCanTransfer
Definition TxFlags.h:172
constexpr std::uint32_t const tmfMPTClearCanLock
Definition TxFlags.h:164
constexpr std::uint32_t const tmfMPTClearCanClawback
Definition TxFlags.h:174
static constexpr std::array< MPTMutabilityFlags, 6 > mptMutabilityFlags
constexpr std::uint32_t const tmfMPTClearCanEscrow
Definition TxFlags.h:168
constexpr std::uint32_t const tmfMPTSetRequireAuth
Definition TxFlags.h:165
constexpr std::uint32_t const tmfMPTClearRequireAuth
Definition TxFlags.h:166
std::size_t constexpr maxMPTokenMetadataLength
The maximum length of MPTokenMetadata.
Definition Protocol.h:230
constexpr std::uint32_t const tmfMPTokenIssuanceSetMutableMask
Definition TxFlags.h:175
constexpr std::uint32_t const tfMPTUnlock
Definition TxFlags.h:157
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 tmfMPTSetCanLock
Definition TxFlags.h:163
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:156
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 tmfMPTClearCanTrade
Definition TxFlags.h:170
constexpr std::uint32_t const tmfMPTSetCanTrade
Definition TxFlags.h:169
constexpr std::uint32_t const tmfMPTSetCanClawback
Definition TxFlags.h:173
@ temINVALID_FLAG
Definition TER.h:91
@ temMALFORMED
Definition TER.h:67
@ temDISABLED
Definition TER.h:94
@ temBAD_TRANSFER_FEE
Definition TER.h:122
constexpr std::uint32_t const tmfMPTSetCanEscrow
Definition TxFlags.h:167
@ 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
@ lsfMPTCanLock
@ lsmfMPTCanMutateCanEscrow
@ lsmfMPTCanMutateCanLock
@ lsmfMPTCanMutateCanTransfer
@ lsfMPTRequireAuth
@ lsfMPTLocked
@ lsmfMPTCanMutateCanTrade
@ lsfMPTCanTransfer
@ lsmfMPTCanMutateTransferFee
@ lsmfMPTCanMutateMetadata
@ lsmfMPTCanMutateRequireAuth
@ lsmfMPTCanMutateCanClawback
constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask
Definition TxFlags.h:159
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:580
constexpr std::uint32_t const tfMPTokenIssuanceSetMask
Definition TxFlags.h:158
@ tesSUCCESS
Definition TER.h:225
constexpr std::uint32_t tfUniversalMask
Definition TxFlags.h:43
constexpr std::uint32_t const tmfMPTSetCanTransfer
Definition TxFlags.h:171
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:53
ReadView const & view
Definition Transactor.h:56
State information when preflighting a tx.
Definition Transactor.h:15