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/jss.h>
23
24namespace ripple {
25namespace test {
26namespace jtx {
27
28void
30{
32}
33
34void
36{
38}
39
40void
42{
43 env.test.expect(cb_());
44}
45
48{
50 for (auto const& h : holders)
51 {
52 if (accounts.find(h.human()) != accounts.cend())
53 Throw<std::runtime_error>("Duplicate holder");
54 accounts.emplace(h.human(), h);
55 }
56 return accounts;
57}
58
59MPTTester::MPTTester(Env& env, Account const& issuer, MPTInit const& arg)
60 : env_(env)
61 , issuer_(issuer)
62 , holders_(makeHolders(arg.holders))
63 , close_(arg.close)
64{
65 if (arg.fund)
66 {
67 env_.fund(arg.xrp, issuer_);
68 for (auto it : holders_)
69 env_.fund(arg.xrpHolders, it.second);
70 }
71 if (close_)
72 env.close();
73 if (arg.fund)
74 {
76 for (auto it : holders_)
77 {
78 if (issuer_.id() == it.second.id())
79 Throw<std::runtime_error>("Issuer can't be holder");
80 env_.require(owners(it.second, 0));
81 }
82 }
83}
84
85void
87{
88 if (id_)
89 Throw<std::runtime_error>("MPT can't be reused");
91 Json::Value jv;
92 jv[sfAccount] = issuer_.human();
93 jv[sfTransactionType] = jss::MPTokenIssuanceCreate;
94 if (arg.assetScale)
95 jv[sfAssetScale] = *arg.assetScale;
96 if (arg.transferFee)
97 jv[sfTransferFee] = *arg.transferFee;
98 if (arg.metadata)
99 jv[sfMPTokenMetadata] = strHex(*arg.metadata);
100 if (arg.maxAmt)
101 jv[sfMaximumAmount] = std::to_string(*arg.maxAmt);
102 if (submit(arg, jv) != tesSUCCESS)
103 {
104 // Verify issuance doesn't exist
105 env_.require(requireAny([&]() -> bool {
106 return env_.le(keylet::mptIssuance(*id_)) == nullptr;
107 }));
108
109 id_.reset();
110 }
111 else
112 env_.require(mptflags(*this, arg.flags.value_or(0)));
113}
114
115void
117{
118 Json::Value jv;
119 if (arg.issuer)
120 jv[sfAccount] = arg.issuer->human();
121 else
122 jv[sfAccount] = issuer_.human();
123 if (arg.id)
124 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
125 else
126 {
127 if (!id_)
128 Throw<std::runtime_error>("MPT has not been created");
129 jv[sfMPTokenIssuanceID] = to_string(*id_);
130 }
131 jv[sfTransactionType] = jss::MPTokenIssuanceDestroy;
132 submit(arg, jv);
133}
134
135Account const&
136MPTTester::holder(std::string const& holder_) const
137{
138 auto const& it = holders_.find(holder_);
139 if (it == holders_.cend())
140 Throw<std::runtime_error>("Holder is not found");
141 return it->second;
142}
143
144void
146{
147 Json::Value jv;
148 if (arg.account)
149 jv[sfAccount] = arg.account->human();
150 else
151 jv[sfAccount] = issuer_.human();
152 jv[sfTransactionType] = jss::MPTokenAuthorize;
153 if (arg.id)
154 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
155 else
156 {
157 if (!id_)
158 Throw<std::runtime_error>("MPT has not been created");
159 jv[sfMPTokenIssuanceID] = to_string(*id_);
160 }
161 if (arg.holder)
162 jv[sfHolder] = arg.holder->human();
163 if (auto const result = submit(arg, jv); result == tesSUCCESS)
164 {
165 // Issuer authorizes
166 if (!arg.account || *arg.account == issuer_)
167 {
168 auto const flags = getFlags(arg.holder);
169 // issuer un-authorizes the holder
170 if (arg.flags.value_or(0) == tfMPTUnauthorize)
171 env_.require(mptflags(*this, flags, arg.holder));
172 // issuer authorizes the holder
173 else
175 mptflags(*this, flags | lsfMPTAuthorized, arg.holder));
176 }
177 // Holder authorizes
178 else if (arg.flags.value_or(0) != tfMPTUnauthorize)
179 {
180 auto const flags = getFlags(arg.account);
181 // holder creates a token
182 env_.require(mptflags(*this, flags, arg.account));
183 env_.require(mptbalance(*this, *arg.account, 0));
184 }
185 else
186 {
187 // Verify that the MPToken doesn't exist.
188 forObject(
189 [&](SLEP const& sle) { return env_.test.BEAST_EXPECT(!sle); },
190 arg.account);
191 }
192 }
193 else if (
194 arg.account && *arg.account != issuer_ &&
195 arg.flags.value_or(0) != tfMPTUnauthorize && id_)
196 {
197 if (result == tecDUPLICATE)
198 {
199 // Verify that MPToken already exists
200 env_.require(requireAny([&]() -> bool {
201 return env_.le(keylet::mptoken(*id_, arg.account->id())) !=
202 nullptr;
203 }));
204 }
205 else
206 {
207 // Verify MPToken doesn't exist if holder failed authorizing(unless
208 // it already exists)
209 env_.require(requireAny([&]() -> bool {
210 return env_.le(keylet::mptoken(*id_, arg.account->id())) ==
211 nullptr;
212 }));
213 }
214 }
215}
216
217void
219{
220 Json::Value jv;
221 if (arg.account)
222 jv[sfAccount] = arg.account->human();
223 else
224 jv[sfAccount] = issuer_.human();
225 jv[sfTransactionType] = jss::MPTokenIssuanceSet;
226 if (arg.id)
227 jv[sfMPTokenIssuanceID] = to_string(*arg.id);
228 else
229 {
230 if (!id_)
231 Throw<std::runtime_error>("MPT has not been created");
232 jv[sfMPTokenIssuanceID] = to_string(*id_);
233 }
234 if (arg.holder)
235 jv[sfHolder] = arg.holder->human();
236 if (arg.delegate)
237 jv[sfDelegate] = arg.delegate->human();
238 if (submit(arg, jv) == tesSUCCESS && arg.flags.value_or(0))
239 {
240 auto require = [&](std::optional<Account> const& holder,
241 bool unchanged) {
242 auto flags = getFlags(holder);
243 if (!unchanged)
244 {
245 if (*arg.flags & tfMPTLock)
247 else if (*arg.flags & tfMPTUnlock)
248 flags &= ~lsfMPTLocked;
249 else
250 Throw<std::runtime_error>("Invalid flags");
251 }
252 env_.require(mptflags(*this, flags, holder));
253 };
254 if (arg.account)
255 require(std::nullopt, arg.holder.has_value());
256 if (arg.holder)
257 require(*arg.holder, false);
258 }
259}
260
261bool
263 std::function<bool(SLEP const& sle)> const& cb,
264 std::optional<Account> const& holder_) const
265{
266 if (!id_)
267 Throw<std::runtime_error>("MPT has not been created");
268 auto const key = holder_ ? keylet::mptoken(*id_, holder_->id())
270 if (auto const sle = env_.le(key))
271 return cb(sle);
272 return false;
273}
274
275[[nodiscard]] bool
277 Account const& holder_,
278 std::int64_t expectedAmount) const
279{
280 return forObject(
281 [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; },
282 holder_);
283}
284
285[[nodiscard]] bool
287{
288 return forObject([&](SLEP const& sle) {
289 return expectedAmount == (*sle)[sfOutstandingAmount];
290 });
291}
292
293[[nodiscard]] bool
295 uint32_t const expectedFlags,
296 std::optional<Account> const& holder) const
297{
298 return expectedFlags == getFlags(holder);
299}
300
301void
303 Account const& src,
304 Account const& dest,
305 std::int64_t amount,
308{
309 if (!id_)
310 Throw<std::runtime_error>("MPT has not been created");
311 auto const srcAmt = getBalance(src);
312 auto const destAmt = getBalance(dest);
313 auto const outstnAmt = getBalance(issuer_);
314
315 if (credentials)
316 env_(
317 jtx::pay(src, dest, mpt(amount)),
318 ter(err.value_or(tesSUCCESS)),
319 credentials::ids(*credentials));
320 else
321 env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));
322
323 if (env_.ter() != tesSUCCESS)
324 amount = 0;
325 if (close_)
326 env_.close();
327 if (src == issuer_)
328 {
329 env_.require(mptbalance(*this, src, srcAmt + amount));
330 env_.require(mptbalance(*this, dest, destAmt + amount));
331 }
332 else if (dest == issuer_)
333 {
334 env_.require(mptbalance(*this, src, srcAmt - amount));
335 env_.require(mptbalance(*this, dest, destAmt - amount));
336 }
337 else
338 {
339 STAmount const saAmount = {*id_, amount};
340 auto const actual =
341 multiply(saAmount, transferRate(*env_.current(), *id_))
342 .mpt()
343 .value();
344 // Sender pays the transfer fee if any
345 env_.require(mptbalance(*this, src, srcAmt - actual));
346 env_.require(mptbalance(*this, dest, destAmt + amount));
347 // Outstanding amount is reduced by the transfer fee if any
348 env_.require(mptbalance(*this, issuer_, outstnAmt - (actual - amount)));
349 }
350}
351
352void
354 Account const& issuer,
355 Account const& holder,
356 std::int64_t amount,
358{
359 if (!id_)
360 Throw<std::runtime_error>("MPT has not been created");
361 auto const issuerAmt = getBalance(issuer);
362 auto const holderAmt = getBalance(holder);
364 if (env_.ter() != tesSUCCESS)
365 amount = 0;
366 if (close_)
367 env_.close();
368
370 mptbalance(*this, issuer, issuerAmt - std::min(holderAmt, amount)));
372 mptbalance(*this, holder, holderAmt - std::min(holderAmt, amount)));
373}
374
377{
378 if (!id_)
379 Throw<std::runtime_error>("MPT has not been created");
380 return ripple::test::jtx::MPT(issuer_.name(), *id_)(amount);
381}
382
384MPTTester::getBalance(Account const& account) const
385{
386 if (!id_)
387 Throw<std::runtime_error>("MPT has not been created");
388 if (account == issuer_)
389 {
390 if (auto const sle = env_.le(keylet::mptIssuance(*id_)))
391 return sle->getFieldU64(sfOutstandingAmount);
392 }
393 else
394 {
395 if (auto const sle = env_.le(keylet::mptoken(*id_, account.id())))
396 return sle->getFieldU64(sfMPTAmount);
397 }
398 return 0;
399}
400
403{
405 if (!forObject(
406 [&](SLEP const& sle) {
407 flags = sle->getFlags();
408 return true;
409 },
410 holder))
411 Throw<std::runtime_error>("Failed to get the flags");
412 return flags;
413}
414
415MPT
417{
418 return MPT(name, issuanceID());
419}
420
421} // namespace jtx
422} // namespace test
423} // 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:286
void authorize(MPTAuthorize const &arg=MPTAuthorize{})
Definition: mpt.cpp:145
void set(MPTSet const &set={})
Definition: mpt.cpp:218
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:302
static std::unordered_map< std::string, Account > makeHolders(std::vector< Account > const &holders)
Definition: mpt.cpp:47
Account const & issuer_
Definition: mpt.h:148
MPTID const & issuanceID() const
Definition: mpt.h:206
TER submit(A const &arg, Json::Value const &jv)
Definition: mpt.h:228
void claw(Account const &issuer, Account const &holder, std::int64_t amount, std::optional< TER > err=std::nullopt)
Definition: mpt.cpp:353
MPTTester(Env &env, Account const &issuer, MPTInit const &constr={})
Definition: mpt.cpp:59
PrettyAmount mpt(std::int64_t amount) const
Definition: mpt.cpp:376
bool checkFlags(uint32_t const expectedFlags, std::optional< Account > const &holder=std::nullopt) const
Definition: mpt.cpp:294
Account const & holder(std::string const &h) const
Definition: mpt.cpp:136
MPT operator[](std::string const &name)
Definition: mpt.cpp:416
Account const & issuer() const
Definition: mpt.h:181
bool forObject(std::function< bool(SLEP const &sle)> const &cb, std::optional< Account > const &holder=std::nullopt) const
Definition: mpt.cpp:262
std::optional< MPTID > id_
Definition: mpt.h:150
std::int64_t getBalance(Account const &account) const
Definition: mpt.cpp:384
std::unordered_map< std::string, Account > const holders_
Definition: mpt.h:149
std::uint32_t getFlags(std::optional< Account > const &holder) const
Definition: mpt.cpp:402
bool checkMPTokenAmount(Account const &holder, std::int64_t expectedAmount) const
Definition: mpt.cpp:276
void create(MPTCreate const &arg=MPTCreate{})
Definition: mpt.cpp:86
void destroy(MPTDestroy const &arg=MPTDestroy{})
Definition: mpt.cpp:116
Converts to MPT Issue or STAmount.
Match set account flags.
Definition: flags.h:128
void operator()(Env &env) const
Definition: mpt.cpp:35
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:29
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:41
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:761
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
std::optional< std::uint32_t > flags
Definition: mpt.h:129
std::optional< Account > holder
Definition: mpt.h:125
std::optional< Account > account
Definition: mpt.h:124
std::optional< MPTID > id
Definition: mpt.h:126
std::optional< std::uint8_t > assetScale
Definition: mpt.h:102
std::optional< std::uint16_t > transferFee
Definition: mpt.h:103
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:114
std::optional< MPTID > id
Definition: mpt.h:115
PrettyAmount const xrpHolders
Definition: mpt.h:93
PrettyAmount const xrp
Definition: mpt.h:92
std::optional< std::uint32_t > flags
Definition: mpt.h:140
std::optional< Account > account
Definition: mpt.h:135
std::optional< MPTID > id
Definition: mpt.h:137
std::optional< Account > holder
Definition: mpt.h:136
std::optional< Account > delegate
Definition: mpt.h:141
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)