rippled
Loading...
Searching...
No Matches
mpt.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 <test/jtx.h>
21
22#include <xrpl/protocol/SField.h>
23#include <xrpl/protocol/jss.h>
24
25namespace ripple {
26namespace test {
27namespace jtx {
28
29void
34
35void
40
41void
43{
44 env.test.expect(cb_());
45}
46
49{
51 for (auto const& h : holders)
52 {
53 if (accounts.find(h.human()) != accounts.cend())
54 Throw<std::runtime_error>("Duplicate holder");
55 accounts.emplace(h.human(), h);
56 }
57 return accounts;
58}
59
60MPTTester::MPTTester(Env& env, Account const& issuer, MPTInit const& arg)
61 : env_(env)
62 , issuer_(issuer)
63 , holders_(makeHolders(arg.holders))
64 , close_(arg.close)
65{
66 if (arg.fund)
67 {
68 env_.fund(arg.xrp, issuer_);
69 for (auto it : holders_)
70 env_.fund(arg.xrpHolders, it.second);
71 }
72 if (close_)
73 env.close();
74 if (arg.fund)
75 {
77 for (auto it : holders_)
78 {
79 if (issuer_.id() == it.second.id())
80 Throw<std::runtime_error>("Issuer can't be holder");
81 env_.require(owners(it.second, 0));
82 }
83 }
84}
85
86void
88{
89 if (id_)
90 Throw<std::runtime_error>("MPT can't be reused");
92 Json::Value jv;
93 jv[sfAccount] = issuer_.human();
94 jv[sfTransactionType] = jss::MPTokenIssuanceCreate;
95 if (arg.assetScale)
96 jv[sfAssetScale] = *arg.assetScale;
97 if (arg.transferFee)
98 jv[sfTransferFee] = *arg.transferFee;
99 if (arg.metadata)
100 jv[sfMPTokenMetadata] = strHex(*arg.metadata);
101 if (arg.maxAmt)
102 jv[sfMaximumAmount] = std::to_string(*arg.maxAmt);
103 if (arg.domainID)
104 jv[sfDomainID] = to_string(*arg.domainID);
105 if (arg.mutableFlags)
106 jv[sfMutableFlags] = *arg.mutableFlags;
107 if (submit(arg, jv) != tesSUCCESS)
108 {
109 // Verify issuance doesn't exist
110 env_.require(requireAny([&]() -> bool {
111 return env_.le(keylet::mptIssuance(*id_)) == nullptr;
112 }));
113
114 id_.reset();
115 }
116 else
117 env_.require(mptflags(*this, arg.flags.value_or(0)));
118}
119
120void
122{
123 Json::Value jv;
124 if (arg.issuer)
125 jv[sfAccount] = arg.issuer->human();
126 else
127 jv[sfAccount] = issuer_.human();
128 if (arg.id)
129 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
130 else
131 {
132 if (!id_)
133 Throw<std::runtime_error>("MPT has not been created");
134 jv[sfMPTokenIssuanceID] = to_string(*id_);
135 }
136 jv[sfTransactionType] = jss::MPTokenIssuanceDestroy;
137 submit(arg, jv);
138}
139
140Account const&
141MPTTester::holder(std::string const& holder_) const
142{
143 auto const& it = holders_.find(holder_);
144 if (it == holders_.cend())
145 Throw<std::runtime_error>("Holder is not found");
146 return it->second;
147}
148
149void
151{
152 Json::Value jv;
153 if (arg.account)
154 jv[sfAccount] = arg.account->human();
155 else
156 jv[sfAccount] = issuer_.human();
157 jv[sfTransactionType] = jss::MPTokenAuthorize;
158 if (arg.id)
159 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
160 else
161 {
162 if (!id_)
163 Throw<std::runtime_error>("MPT has not been created");
164 jv[sfMPTokenIssuanceID] = to_string(*id_);
165 }
166 if (arg.holder)
167 jv[sfHolder] = arg.holder->human();
168 if (auto const result = submit(arg, jv); result == tesSUCCESS)
169 {
170 // Issuer authorizes
171 if (!arg.account || *arg.account == issuer_)
172 {
173 auto const flags = getFlags(arg.holder);
174 // issuer un-authorizes the holder
175 if (arg.flags.value_or(0) == tfMPTUnauthorize)
176 env_.require(mptflags(*this, flags, arg.holder));
177 // issuer authorizes the holder
178 else
180 mptflags(*this, flags | lsfMPTAuthorized, arg.holder));
181 }
182 // Holder authorizes
183 else if (arg.flags.value_or(0) != tfMPTUnauthorize)
184 {
185 auto const flags = getFlags(arg.account);
186 // holder creates a token
187 env_.require(mptflags(*this, flags, arg.account));
188 env_.require(mptbalance(*this, *arg.account, 0));
189 }
190 else
191 {
192 // Verify that the MPToken doesn't exist.
193 forObject(
194 [&](SLEP const& sle) { return env_.test.BEAST_EXPECT(!sle); },
195 arg.account);
196 }
197 }
198 else if (
199 arg.account && *arg.account != issuer_ &&
200 arg.flags.value_or(0) != tfMPTUnauthorize && id_)
201 {
202 if (result == tecDUPLICATE)
203 {
204 // Verify that MPToken already exists
205 env_.require(requireAny([&]() -> bool {
206 return env_.le(keylet::mptoken(*id_, arg.account->id())) !=
207 nullptr;
208 }));
209 }
210 else
211 {
212 // Verify MPToken doesn't exist if holder failed authorizing(unless
213 // it already exists)
214 env_.require(requireAny([&]() -> bool {
215 return env_.le(keylet::mptoken(*id_, arg.account->id())) ==
216 nullptr;
217 }));
218 }
219 }
220}
221
222void
224{
225 Json::Value jv;
226 if (arg.account)
227 jv[sfAccount] = arg.account->human();
228 else
229 jv[sfAccount] = issuer_.human();
230 jv[sfTransactionType] = jss::MPTokenIssuanceSet;
231 if (arg.id)
232 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
233 else
234 {
235 if (!id_)
236 Throw<std::runtime_error>("MPT has not been created");
237 jv[sfMPTokenIssuanceID] = to_string(*id_);
238 }
239 if (arg.holder)
240 jv[sfHolder] = arg.holder->human();
241 if (arg.delegate)
242 jv[sfDelegate] = arg.delegate->human();
243 if (arg.domainID)
244 jv[sfDomainID] = to_string(*arg.domainID);
245 if (arg.mutableFlags)
246 jv[sfMutableFlags] = *arg.mutableFlags;
247 if (arg.transferFee)
248 jv[sfTransferFee] = *arg.transferFee;
249 if (arg.metadata)
250 jv[sfMPTokenMetadata] = strHex(*arg.metadata);
251 if (submit(arg, jv) == tesSUCCESS && (arg.flags || arg.mutableFlags))
252 {
253 auto require = [&](std::optional<Account> const& holder,
254 bool unchanged) {
255 auto flags = getFlags(holder);
256 if (!unchanged)
257 {
258 if (arg.flags)
259 {
260 if (*arg.flags & tfMPTLock)
262 else if (*arg.flags & tfMPTUnlock)
263 flags &= ~lsfMPTLocked;
264 }
265
266 if (arg.mutableFlags)
267 {
270 else if (*arg.mutableFlags & tmfMPTClearCanLock)
271 flags &= ~lsfMPTCanLock;
272
275 else if (*arg.mutableFlags & tmfMPTClearRequireAuth)
276 flags &= ~lsfMPTRequireAuth;
277
280 else if (*arg.mutableFlags & tmfMPTClearCanEscrow)
281 flags &= ~lsfMPTCanEscrow;
282
285 else if (*arg.mutableFlags & tmfMPTClearCanClawback)
286 flags &= ~lsfMPTCanClawback;
287
290 else if (*arg.mutableFlags & tmfMPTClearCanTrade)
291 flags &= ~lsfMPTCanTrade;
292
295 else if (*arg.mutableFlags & tmfMPTClearCanTransfer)
296 flags &= ~lsfMPTCanTransfer;
297 }
298 }
299 env_.require(mptflags(*this, flags, holder));
300 };
301 if (arg.account)
302 require(std::nullopt, arg.holder.has_value());
303 if (arg.holder)
304 require(*arg.holder, false);
305 }
306}
307
308bool
310 std::function<bool(SLEP const& sle)> const& cb,
311 std::optional<Account> const& holder_) const
312{
313 if (!id_)
314 Throw<std::runtime_error>("MPT has not been created");
315 auto const key = holder_ ? keylet::mptoken(*id_, holder_->id())
317 if (auto const sle = env_.le(key))
318 return cb(sle);
319 return false;
320}
321
322[[nodiscard]] bool
324{
325 return forObject([&](SLEP const& sle) -> bool {
326 if (sle->isFieldPresent(sfDomainID))
327 return expected == sle->getFieldH256(sfDomainID);
328 return (!expected.has_value());
329 });
330}
331
332[[nodiscard]] bool
334 Account const& holder_,
335 std::int64_t expectedAmount) const
336{
337 return forObject(
338 [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; },
339 holder_);
340}
341
342[[nodiscard]] bool
344{
345 return forObject([&](SLEP const& sle) {
346 return expectedAmount == (*sle)[sfOutstandingAmount];
347 });
348}
349
350[[nodiscard]] bool
352 uint32_t const expectedFlags,
353 std::optional<Account> const& holder) const
354{
355 return expectedFlags == getFlags(holder);
356}
357
358[[nodiscard]] bool
360{
361 return forObject([&](SLEP const& sle) -> bool {
362 if (sle->isFieldPresent(sfMPTokenMetadata))
363 return strHex(sle->getFieldVL(sfMPTokenMetadata)) ==
364 strHex(metadata);
365 return false;
366 });
367}
368
369[[nodiscard]] bool
371{
372 return forObject([&](SLEP const& sle) -> bool {
373 return sle->isFieldPresent(sfMPTokenMetadata);
374 });
375}
376
377[[nodiscard]] bool
379{
380 return forObject([&](SLEP const& sle) -> bool {
381 if (sle->isFieldPresent(sfTransferFee))
382 return sle->getFieldU16(sfTransferFee) == transferFee;
383 return false;
384 });
385}
386
387[[nodiscard]] bool
389{
390 return forObject([&](SLEP const& sle) -> bool {
391 return sle->isFieldPresent(sfTransferFee);
392 });
393}
394
395void
397 Account const& src,
398 Account const& dest,
399 std::int64_t amount,
402{
403 if (!id_)
404 Throw<std::runtime_error>("MPT has not been created");
405 auto const srcAmt = getBalance(src);
406 auto const destAmt = getBalance(dest);
407 auto const outstnAmt = getBalance(issuer_);
408
409 if (credentials)
410 env_(
411 jtx::pay(src, dest, mpt(amount)),
412 ter(err.value_or(tesSUCCESS)),
413 credentials::ids(*credentials));
414 else
415 env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));
416
417 if (env_.ter() != tesSUCCESS)
418 amount = 0;
419 if (close_)
420 env_.close();
421 if (src == issuer_)
422 {
423 env_.require(mptbalance(*this, src, srcAmt + amount));
424 env_.require(mptbalance(*this, dest, destAmt + amount));
425 }
426 else if (dest == issuer_)
427 {
428 env_.require(mptbalance(*this, src, srcAmt - amount));
429 env_.require(mptbalance(*this, dest, destAmt - amount));
430 }
431 else
432 {
433 STAmount const saAmount = {*id_, amount};
434 auto const actual =
435 multiply(saAmount, transferRate(*env_.current(), *id_))
436 .mpt()
437 .value();
438 // Sender pays the transfer fee if any
439 env_.require(mptbalance(*this, src, srcAmt - actual));
440 env_.require(mptbalance(*this, dest, destAmt + amount));
441 // Outstanding amount is reduced by the transfer fee if any
442 env_.require(mptbalance(*this, issuer_, outstnAmt - (actual - amount)));
443 }
444}
445
446void
448 Account const& issuer,
449 Account const& holder,
450 std::int64_t amount,
452{
453 if (!id_)
454 Throw<std::runtime_error>("MPT has not been created");
455 auto const issuerAmt = getBalance(issuer);
456 auto const holderAmt = getBalance(holder);
458 if (env_.ter() != tesSUCCESS)
459 amount = 0;
460 if (close_)
461 env_.close();
462
464 mptbalance(*this, issuer, issuerAmt - std::min(holderAmt, amount)));
466 mptbalance(*this, holder, holderAmt - std::min(holderAmt, amount)));
467}
468
471{
472 if (!id_)
473 Throw<std::runtime_error>("MPT has not been created");
474 return ripple::test::jtx::MPT(issuer_.name(), *id_)(amount);
475}
476
478MPTTester::getBalance(Account const& account) const
479{
480 if (!id_)
481 Throw<std::runtime_error>("MPT has not been created");
482 if (account == issuer_)
483 {
484 if (auto const sle = env_.le(keylet::mptIssuance(*id_)))
485 return sle->getFieldU64(sfOutstandingAmount);
486 }
487 else
488 {
489 if (auto const sle = env_.le(keylet::mptoken(*id_, account.id())))
490 return sle->getFieldU64(sfMPTAmount);
491 }
492 return 0;
493}
494
497{
499 if (!forObject(
500 [&](SLEP const& sle) {
501 flags = sle->getFlags();
502 return true;
503 },
504 holder))
505 Throw<std::runtime_error>("Failed to get the flags");
506 return flags;
507}
508
509MPT
511{
512 return MPT(name, issuanceID());
513}
514
515} // namespace jtx
516} // namespace test
517} // namespace ripple
Represents a JSON value.
Definition json_value.h:149
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition suite.h:229
constexpr value_type value() const
Returns the underlying value.
Definition MPTAmount.h:133
MPTAmount mpt() const
Definition STAmount.cpp:337
Immutable cryptographic account descriptor.
Definition Account.h:39
AccountID id() const
Returns the Account ID.
Definition Account.h:111
std::string const & name() const
Return the name.
Definition Account.h:87
std::string const & human() const
Returns the human readable public key.
Definition Account.h:118
A transaction testing environment.
Definition Env.h:121
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition Env.cpp:268
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:547
TER ter() const
Return the TER for the last JTx.
Definition Env.h:598
beast::unit_test::suite & test
Definition Env.h:123
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:331
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition Env.cpp:121
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition Env.cpp:289
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:277
bool checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const
Definition mpt.cpp:343
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:150
void set(MPTSet const &set={})
Definition mpt.cpp:223
void pay(Account const &src, Account const &dest, std::int64_t amount, std::optional< TER > err=std::nullopt, std::optional< std::vector< std::string > > credentials=std::nullopt)
Definition mpt.cpp:396
static std::unordered_map< std::string, Account > makeHolders(std::vector< Account > const &holders)
Definition mpt.cpp:48
Account const & issuer_
Definition mpt.h:154
bool isMetadataPresent() const
Definition mpt.cpp:370
bool checkTransferFee(std::uint16_t transferFee) const
Definition mpt.cpp:378
MPTID const & issuanceID() const
Definition mpt.h:227
TER submit(A const &arg, Json::Value const &jv)
Definition mpt.h:249
void claw(Account const &issuer, Account const &holder, std::int64_t amount, std::optional< TER > err=std::nullopt)
Definition mpt.cpp:447
MPTTester(Env &env, Account const &issuer, MPTInit const &constr={})
Definition mpt.cpp:60
PrettyAmount mpt(std::int64_t amount) const
Definition mpt.cpp:470
bool checkFlags(uint32_t const expectedFlags, std::optional< Account > const &holder=std::nullopt) const
Definition mpt.cpp:351
Account const & holder(std::string const &h) const
Definition mpt.cpp:141
MPT operator[](std::string const &name)
Definition mpt.cpp:510
Account const & issuer() const
Definition mpt.h:202
bool checkMetadata(std::string const &metadata) const
Definition mpt.cpp:359
bool forObject(std::function< bool(SLEP const &sle)> const &cb, std::optional< Account > const &holder=std::nullopt) const
Definition mpt.cpp:309
bool isTransferFeePresent() const
Definition mpt.cpp:388
std::optional< MPTID > id_
Definition mpt.h:156
std::int64_t getBalance(Account const &account) const
Definition mpt.cpp:478
std::unordered_map< std::string, Account > const holders_
Definition mpt.h:155
bool checkDomainID(std::optional< uint256 > expected) const
Definition mpt.cpp:323
std::uint32_t getFlags(std::optional< Account > const &holder) const
Definition mpt.cpp:496
bool checkMPTokenAmount(Account const &holder, std::int64_t expectedAmount) const
Definition mpt.cpp:333
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:87
void destroy(MPTDestroy const &arg=MPTDestroy{})
Definition mpt.cpp:121
Converts to MPT Issue or STAmount.
Match set account flags.
Definition flags.h:128
void operator()(Env &env) const
Definition mpt.cpp:36
Account const & account_
Definition mpt.h:62
std::int64_t const amount_
Definition mpt.h:63
MPTTester const & tester_
Definition mpt.h:61
std::uint32_t flags_
Definition mpt.h:41
MPTTester & tester_
Definition mpt.h:40
void operator()(Env &env) const
Definition mpt.cpp:30
std::optional< Account > holder_
Definition mpt.h:42
Match the number of items in the account's owner directory.
Definition owners.h:73
void operator()(Env &env) const
Definition mpt.cpp:42
std::function< bool()> cb_
Definition mpt.h:78
Check a set of conditions.
Definition require.h:66
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition ter.h:35
T emplace(T... args)
T cend(T... args)
T find(T... args)
T is_same_v
T min(T... args)
Keylet mptoken(MPTID const &issuanceID, AccountID const &holder) noexcept
Definition Indexes.cpp:540
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition Indexes.cpp:526
Json::Value claw(Account const &account, STAmount const &amount, std::optional< Account > const &mptHolder)
Definition trust.cpp:69
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition pay.cpp:30
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr std::uint32_t const tmfMPTClearCanClawback
Definition TxFlags.h:194
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
@ lsfMPTCanEscrow
@ lsfMPTCanClawback
@ lsfMPTRequireAuth
@ lsfMPTAuthorized
constexpr std::uint32_t const tmfMPTClearRequireAuth
Definition TxFlags.h:186
constexpr std::uint32_t const tmfMPTSetCanLock
Definition TxFlags.h:183
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
constexpr std::uint32_t const tmfMPTSetCanTrade
Definition TxFlags.h:189
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:172
constexpr std::uint32_t const tmfMPTSetCanEscrow
Definition TxFlags.h:187
constexpr std::uint32_t const tmfMPTClearCanLock
Definition TxFlags.h:184
constexpr std::uint32_t const tmfMPTSetCanTransfer
Definition TxFlags.h:191
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:759
@ tecDUPLICATE
Definition TER.h:315
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:176
@ tesSUCCESS
Definition TER.h:244
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition Indexes.cpp:170
constexpr std::uint32_t const tmfMPTClearCanTransfer
Definition TxFlags.h:192
T has_value(T... args)
std::optional< std::uint32_t > flags
Definition mpt.h:131
std::optional< Account > holder
Definition mpt.h:127
std::optional< Account > account
Definition mpt.h:126
std::optional< MPTID > id
Definition mpt.h:128
std::optional< std::uint8_t > assetScale
Definition mpt.h:102
std::optional< std::uint16_t > transferFee
Definition mpt.h:103
std::optional< uint256 > domainID
Definition mpt.h:110
std::optional< std::uint32_t > mutableFlags
Definition mpt.h:109
std::optional< std::uint32_t > flags
Definition mpt.h:108
std::optional< std::string > metadata
Definition mpt.h:104
std::optional< std::uint64_t > maxAmt
Definition mpt.h:101
std::optional< Account > issuer
Definition mpt.h:116
std::optional< MPTID > id
Definition mpt.h:117
PrettyAmount const xrpHolders
Definition mpt.h:93
PrettyAmount const xrp
Definition mpt.h:92
std::optional< std::uint32_t > flags
Definition mpt.h:142
std::optional< std::uint16_t > transferFee
Definition mpt.h:144
std::optional< Account > account
Definition mpt.h:137
std::optional< std::string > metadata
Definition mpt.h:145
std::optional< uint256 > domainID
Definition mpt.h:147
std::optional< std::uint32_t > mutableFlags
Definition mpt.h:143
std::optional< MPTID > id
Definition mpt.h:139
std::optional< Account > holder
Definition mpt.h:138
std::optional< Account > delegate
Definition mpt.h:146
Represents an XRP or IOU quantity This customizes the string conversion and supports XRP conversions ...
T to_string(T... args)
T value_or(T... args)