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 (submit(arg, jv) != tesSUCCESS)
106 {
107 // Verify issuance doesn't exist
108 env_.require(requireAny([&]() -> bool {
109 return env_.le(keylet::mptIssuance(*id_)) == nullptr;
110 }));
111
112 id_.reset();
113 }
114 else
115 env_.require(mptflags(*this, arg.flags.value_or(0)));
116}
117
118void
120{
121 Json::Value jv;
122 if (arg.issuer)
123 jv[sfAccount] = arg.issuer->human();
124 else
125 jv[sfAccount] = issuer_.human();
126 if (arg.id)
127 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
128 else
129 {
130 if (!id_)
131 Throw<std::runtime_error>("MPT has not been created");
132 jv[sfMPTokenIssuanceID] = to_string(*id_);
133 }
134 jv[sfTransactionType] = jss::MPTokenIssuanceDestroy;
135 submit(arg, jv);
136}
137
138Account const&
139MPTTester::holder(std::string const& holder_) const
140{
141 auto const& it = holders_.find(holder_);
142 if (it == holders_.cend())
143 Throw<std::runtime_error>("Holder is not found");
144 return it->second;
145}
146
147void
149{
150 Json::Value jv;
151 if (arg.account)
152 jv[sfAccount] = arg.account->human();
153 else
154 jv[sfAccount] = issuer_.human();
155 jv[sfTransactionType] = jss::MPTokenAuthorize;
156 if (arg.id)
157 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
158 else
159 {
160 if (!id_)
161 Throw<std::runtime_error>("MPT has not been created");
162 jv[sfMPTokenIssuanceID] = to_string(*id_);
163 }
164 if (arg.holder)
165 jv[sfHolder] = arg.holder->human();
166 if (auto const result = submit(arg, jv); result == tesSUCCESS)
167 {
168 // Issuer authorizes
169 if (!arg.account || *arg.account == issuer_)
170 {
171 auto const flags = getFlags(arg.holder);
172 // issuer un-authorizes the holder
173 if (arg.flags.value_or(0) == tfMPTUnauthorize)
174 env_.require(mptflags(*this, flags, arg.holder));
175 // issuer authorizes the holder
176 else
178 mptflags(*this, flags | lsfMPTAuthorized, arg.holder));
179 }
180 // Holder authorizes
181 else if (arg.flags.value_or(0) != tfMPTUnauthorize)
182 {
183 auto const flags = getFlags(arg.account);
184 // holder creates a token
185 env_.require(mptflags(*this, flags, arg.account));
186 env_.require(mptbalance(*this, *arg.account, 0));
187 }
188 else
189 {
190 // Verify that the MPToken doesn't exist.
191 forObject(
192 [&](SLEP const& sle) { return env_.test.BEAST_EXPECT(!sle); },
193 arg.account);
194 }
195 }
196 else if (
197 arg.account && *arg.account != issuer_ &&
198 arg.flags.value_or(0) != tfMPTUnauthorize && id_)
199 {
200 if (result == tecDUPLICATE)
201 {
202 // Verify that MPToken already exists
203 env_.require(requireAny([&]() -> bool {
204 return env_.le(keylet::mptoken(*id_, arg.account->id())) !=
205 nullptr;
206 }));
207 }
208 else
209 {
210 // Verify MPToken doesn't exist if holder failed authorizing(unless
211 // it already exists)
212 env_.require(requireAny([&]() -> bool {
213 return env_.le(keylet::mptoken(*id_, arg.account->id())) ==
214 nullptr;
215 }));
216 }
217 }
218}
219
220void
222{
223 Json::Value jv;
224 if (arg.account)
225 jv[sfAccount] = arg.account->human();
226 else
227 jv[sfAccount] = issuer_.human();
228 jv[sfTransactionType] = jss::MPTokenIssuanceSet;
229 if (arg.id)
230 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
231 else
232 {
233 if (!id_)
234 Throw<std::runtime_error>("MPT has not been created");
235 jv[sfMPTokenIssuanceID] = to_string(*id_);
236 }
237 if (arg.holder)
238 jv[sfHolder] = arg.holder->human();
239 if (arg.delegate)
240 jv[sfDelegate] = arg.delegate->human();
241 if (arg.domainID)
242 jv[sfDomainID] = to_string(*arg.domainID);
243 if (submit(arg, jv) == tesSUCCESS && arg.flags.value_or(0))
244 {
245 auto require = [&](std::optional<Account> const& holder,
246 bool unchanged) {
247 auto flags = getFlags(holder);
248 if (!unchanged)
249 {
250 if (*arg.flags & tfMPTLock)
252 else if (*arg.flags & tfMPTUnlock)
253 flags &= ~lsfMPTLocked;
254 else
255 Throw<std::runtime_error>("Invalid flags");
256 }
257 env_.require(mptflags(*this, flags, holder));
258 };
259 if (arg.account)
260 require(std::nullopt, arg.holder.has_value());
261 if (arg.holder)
262 require(*arg.holder, false);
263 }
264}
265
266bool
268 std::function<bool(SLEP const& sle)> const& cb,
269 std::optional<Account> const& holder_) const
270{
271 if (!id_)
272 Throw<std::runtime_error>("MPT has not been created");
273 auto const key = holder_ ? keylet::mptoken(*id_, holder_->id())
275 if (auto const sle = env_.le(key))
276 return cb(sle);
277 return false;
278}
279
280[[nodiscard]] bool
282{
283 return forObject([&](SLEP const& sle) -> bool {
284 if (sle->isFieldPresent(sfDomainID))
285 return expected == sle->getFieldH256(sfDomainID);
286 return (!expected.has_value());
287 });
288}
289
290[[nodiscard]] bool
292 Account const& holder_,
293 std::int64_t expectedAmount) const
294{
295 return forObject(
296 [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; },
297 holder_);
298}
299
300[[nodiscard]] bool
302{
303 return forObject([&](SLEP const& sle) {
304 return expectedAmount == (*sle)[sfOutstandingAmount];
305 });
306}
307
308[[nodiscard]] bool
310 uint32_t const expectedFlags,
311 std::optional<Account> const& holder) const
312{
313 return expectedFlags == getFlags(holder);
314}
315
316void
318 Account const& src,
319 Account const& dest,
320 std::int64_t amount,
323{
324 if (!id_)
325 Throw<std::runtime_error>("MPT has not been created");
326 auto const srcAmt = getBalance(src);
327 auto const destAmt = getBalance(dest);
328 auto const outstnAmt = getBalance(issuer_);
329
330 if (credentials)
331 env_(
332 jtx::pay(src, dest, mpt(amount)),
333 ter(err.value_or(tesSUCCESS)),
334 credentials::ids(*credentials));
335 else
336 env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));
337
338 if (env_.ter() != tesSUCCESS)
339 amount = 0;
340 if (close_)
341 env_.close();
342 if (src == issuer_)
343 {
344 env_.require(mptbalance(*this, src, srcAmt + amount));
345 env_.require(mptbalance(*this, dest, destAmt + amount));
346 }
347 else if (dest == issuer_)
348 {
349 env_.require(mptbalance(*this, src, srcAmt - amount));
350 env_.require(mptbalance(*this, dest, destAmt - amount));
351 }
352 else
353 {
354 STAmount const saAmount = {*id_, amount};
355 auto const actual =
356 multiply(saAmount, transferRate(*env_.current(), *id_))
357 .mpt()
358 .value();
359 // Sender pays the transfer fee if any
360 env_.require(mptbalance(*this, src, srcAmt - actual));
361 env_.require(mptbalance(*this, dest, destAmt + amount));
362 // Outstanding amount is reduced by the transfer fee if any
363 env_.require(mptbalance(*this, issuer_, outstnAmt - (actual - amount)));
364 }
365}
366
367void
369 Account const& issuer,
370 Account const& holder,
371 std::int64_t amount,
373{
374 if (!id_)
375 Throw<std::runtime_error>("MPT has not been created");
376 auto const issuerAmt = getBalance(issuer);
377 auto const holderAmt = getBalance(holder);
379 if (env_.ter() != tesSUCCESS)
380 amount = 0;
381 if (close_)
382 env_.close();
383
385 mptbalance(*this, issuer, issuerAmt - std::min(holderAmt, amount)));
387 mptbalance(*this, holder, holderAmt - std::min(holderAmt, amount)));
388}
389
392{
393 if (!id_)
394 Throw<std::runtime_error>("MPT has not been created");
395 return ripple::test::jtx::MPT(issuer_.name(), *id_)(amount);
396}
397
399MPTTester::getBalance(Account const& account) const
400{
401 if (!id_)
402 Throw<std::runtime_error>("MPT has not been created");
403 if (account == issuer_)
404 {
405 if (auto const sle = env_.le(keylet::mptIssuance(*id_)))
406 return sle->getFieldU64(sfOutstandingAmount);
407 }
408 else
409 {
410 if (auto const sle = env_.le(keylet::mptoken(*id_, account.id())))
411 return sle->getFieldU64(sfMPTAmount);
412 }
413 return 0;
414}
415
418{
420 if (!forObject(
421 [&](SLEP const& sle) {
422 flags = sle->getFlags();
423 return true;
424 },
425 holder))
426 Throw<std::runtime_error>("Failed to get the flags");
427 return flags;
428}
429
430MPT
432{
433 return MPT(name, issuanceID());
434}
435
436} // namespace jtx
437} // namespace test
438} // 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:107
std::string const & name() const
Return the name.
Definition Account.h:83
std::string const & human() const
Returns the human readable public key.
Definition Account.h:114
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:258
void require(Args const &... args)
Check a set of requirements.
Definition Env.h:544
TER ter() const
Return the TER for the last JTx.
Definition Env.h:595
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:279
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition Env.cpp:267
bool checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const
Definition mpt.cpp:301
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition mpt.cpp:148
void set(MPTSet const &set={})
Definition mpt.cpp:221
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:317
static std::unordered_map< std::string, Account > makeHolders(std::vector< Account > const &holders)
Definition mpt.cpp:48
Account const & issuer_
Definition mpt.h:150
MPTID const & issuanceID() const
Definition mpt.h:211
TER submit(A const &arg, Json::Value const &jv)
Definition mpt.h:233
void claw(Account const &issuer, Account const &holder, std::int64_t amount, std::optional< TER > err=std::nullopt)
Definition mpt.cpp:368
MPTTester(Env &env, Account const &issuer, MPTInit const &constr={})
Definition mpt.cpp:60
PrettyAmount mpt(std::int64_t amount) const
Definition mpt.cpp:391
bool checkFlags(uint32_t const expectedFlags, std::optional< Account > const &holder=std::nullopt) const
Definition mpt.cpp:309
Account const & holder(std::string const &h) const
Definition mpt.cpp:139
MPT operator[](std::string const &name)
Definition mpt.cpp:431
Account const & issuer() const
Definition mpt.h:186
bool forObject(std::function< bool(SLEP const &sle)> const &cb, std::optional< Account > const &holder=std::nullopt) const
Definition mpt.cpp:267
std::optional< MPTID > id_
Definition mpt.h:152
std::int64_t getBalance(Account const &account) const
Definition mpt.cpp:399
std::unordered_map< std::string, Account > const holders_
Definition mpt.h:151
bool checkDomainID(std::optional< uint256 > expected) const
Definition mpt.cpp:281
std::uint32_t getFlags(std::optional< Account > const &holder) const
Definition mpt.cpp:417
bool checkMPTokenAmount(Account const &holder, std::int64_t expectedAmount) const
Definition mpt.cpp:291
void create(MPTCreate const &arg=MPTCreate{})
Definition mpt.cpp:87
void destroy(MPTDestroy const &arg=MPTDestroy{})
Definition mpt.cpp:119
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:65
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
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition View.cpp:760
constexpr std::uint32_t const tfMPTUnlock
Definition TxFlags.h:160
@ lsfMPTAuthorized
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition TxFlags.h:155
@ tecDUPLICATE
Definition TER.h:315
constexpr std::uint32_t const tfMPTLock
Definition TxFlags.h:159
@ 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
T has_value(T... args)
std::optional< std::uint32_t > flags
Definition mpt.h:130
std::optional< Account > holder
Definition mpt.h:126
std::optional< Account > account
Definition mpt.h:125
std::optional< MPTID > id
Definition mpt.h:127
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: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:115
std::optional< MPTID > id
Definition mpt.h:116
PrettyAmount const xrpHolders
Definition mpt.h:93
PrettyAmount const xrp
Definition mpt.h:92
std::optional< std::uint32_t > flags
Definition mpt.h:141
std::optional< Account > account
Definition mpt.h:136
std::optional< uint256 > domainID
Definition mpt.h:143
std::optional< MPTID > id
Definition mpt.h:138
std::optional< Account > holder
Definition mpt.h:137
std::optional< Account > delegate
Definition mpt.h:142
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)