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
31{
33}
34
35void
37{
39}
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:254
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:117
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:275
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:263
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 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
@ lsfMPTLocked
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)