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/mpt.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 (submit(arg, jv) == tesSUCCESS && arg.flags.value_or(0))
237 {
238 auto require = [&](std::optional<Account> const& holder,
239 bool unchanged) {
240 auto flags = getFlags(holder);
241 if (!unchanged)
242 {
243 if (*arg.flags & tfMPTLock)
245 else if (*arg.flags & tfMPTUnlock)
246 flags &= ~lsfMPTLocked;
247 else
248 Throw<std::runtime_error>("Invalid flags");
249 }
250 env_.require(mptflags(*this, flags, holder));
251 };
252 if (arg.account)
253 require(std::nullopt, arg.holder.has_value());
254 if (arg.holder)
255 require(*arg.holder, false);
256 }
257}
258
259bool
261 std::function<bool(SLEP const& sle)> const& cb,
262 std::optional<Account> const& holder_) const
263{
264 if (!id_)
265 Throw<std::runtime_error>("MPT has not been created");
266 auto const key = holder_ ? keylet::mptoken(*id_, holder_->id())
268 if (auto const sle = env_.le(key))
269 return cb(sle);
270 return false;
271}
272
273[[nodiscard]] bool
275 Account const& holder_,
276 std::int64_t expectedAmount) const
277{
278 return forObject(
279 [&](SLEP const& sle) { return expectedAmount == (*sle)[sfMPTAmount]; },
280 holder_);
281}
282
283[[nodiscard]] bool
285{
286 return forObject([&](SLEP const& sle) {
287 return expectedAmount == (*sle)[sfOutstandingAmount];
288 });
289}
290
291[[nodiscard]] bool
293 uint32_t const expectedFlags,
294 std::optional<Account> const& holder) const
295{
296 return expectedFlags == getFlags(holder);
297}
298
299void
301 Account const& src,
302 Account const& dest,
303 std::int64_t amount,
306{
307 if (!id_)
308 Throw<std::runtime_error>("MPT has not been created");
309 auto const srcAmt = getBalance(src);
310 auto const destAmt = getBalance(dest);
311 auto const outstnAmt = getBalance(issuer_);
312
313 if (credentials)
314 env_(
315 jtx::pay(src, dest, mpt(amount)),
316 ter(err.value_or(tesSUCCESS)),
317 credentials::ids(*credentials));
318 else
319 env_(jtx::pay(src, dest, mpt(amount)), ter(err.value_or(tesSUCCESS)));
320
321 if (env_.ter() != tesSUCCESS)
322 amount = 0;
323 if (close_)
324 env_.close();
325 if (src == issuer_)
326 {
327 env_.require(mptbalance(*this, src, srcAmt + amount));
328 env_.require(mptbalance(*this, dest, destAmt + amount));
329 }
330 else if (dest == issuer_)
331 {
332 env_.require(mptbalance(*this, src, srcAmt - amount));
333 env_.require(mptbalance(*this, dest, destAmt - amount));
334 }
335 else
336 {
337 STAmount const saAmount = {*id_, amount};
338 auto const actual =
339 multiply(saAmount, transferRate(*env_.current(), *id_))
340 .mpt()
341 .value();
342 // Sender pays the transfer fee if any
343 env_.require(mptbalance(*this, src, srcAmt - actual));
344 env_.require(mptbalance(*this, dest, destAmt + amount));
345 // Outstanding amount is reduced by the transfer fee if any
346 env_.require(mptbalance(*this, issuer_, outstnAmt - (actual - amount)));
347 }
348}
349
350void
352 Account const& issuer,
353 Account const& holder,
354 std::int64_t amount,
356{
357 if (!id_)
358 Throw<std::runtime_error>("MPT has not been created");
359 auto const issuerAmt = getBalance(issuer);
360 auto const holderAmt = getBalance(holder);
362 if (env_.ter() != tesSUCCESS)
363 amount = 0;
364 if (close_)
365 env_.close();
366
368 mptbalance(*this, issuer, issuerAmt - std::min(holderAmt, amount)));
370 mptbalance(*this, holder, holderAmt - std::min(holderAmt, amount)));
371}
372
375{
376 if (!id_)
377 Throw<std::runtime_error>("MPT has not been created");
378 return ripple::test::jtx::MPT(issuer_.name(), *id_)(amount);
379}
380
382MPTTester::getBalance(Account const& account) const
383{
384 if (!id_)
385 Throw<std::runtime_error>("MPT has not been created");
386 if (account == issuer_)
387 {
388 if (auto const sle = env_.le(keylet::mptIssuance(*id_)))
389 return sle->getFieldU64(sfOutstandingAmount);
390 }
391 else
392 {
393 if (auto const sle = env_.le(keylet::mptoken(*id_, account.id())))
394 return sle->getFieldU64(sfMPTAmount);
395 }
396 return 0;
397}
398
401{
403 if (!forObject(
404 [&](SLEP const& sle) {
405 flags = sle->getFlags();
406 return true;
407 },
408 holder))
409 Throw<std::runtime_error>("Failed to get the flags");
410 return flags;
411}
412
413MPT
415{
416 return MPT(name, issuanceID());
417}
418
419} // namespace jtx
420} // namespace test
421} // namespace ripple
Represents a JSON value.
Definition: json_value.h:147
bool expect(Condition const &shouldBeTrue)
Evaluate a test condition.
Definition: suite.h:227
constexpr value_type value() const
Returns the underlying value.
Definition: MPTAmount.h:136
MPTAmount mpt() const
Definition: STAmount.cpp:303
Immutable cryptographic account descriptor.
Definition: Account.h:38
AccountID id() const
Returns the Account ID.
Definition: Account.h:106
std::string const & name() const
Return the name.
Definition: Account.h:82
std::string const & human() const
Returns the human readable public key.
Definition: Account.h:113
A transaction testing environment.
Definition: Env.h:117
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Definition: Env.cpp:216
void require(Args const &... args)
Check a set of requirements.
Definition: Env.h:529
TER ter() const
Return the TER for the last JTx.
Definition: Env.h:580
beast::unit_test::suite & test
Definition: Env.h:119
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition: Env.h:325
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:237
std::shared_ptr< SLE const > le(Account const &account) const
Return an account root.
Definition: Env.cpp:225
bool checkMPTokenOutstandingAmount(std::int64_t expectedAmount) const
Definition: mpt.cpp:284
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:300
static std::unordered_map< std::string, Account > makeHolders(std::vector< Account > const &holders)
Definition: mpt.cpp:47
Account const & issuer_
Definition: mpt.h:145
MPTID const & issuanceID() const
Definition: mpt.h:203
TER submit(A const &arg, Json::Value const &jv)
Definition: mpt.h:225
void claw(Account const &issuer, Account const &holder, std::int64_t amount, std::optional< TER > err=std::nullopt)
Definition: mpt.cpp:351
MPTTester(Env &env, Account const &issuer, MPTInit const &constr={})
Definition: mpt.cpp:59
PrettyAmount mpt(std::int64_t amount) const
Definition: mpt.cpp:374
bool checkFlags(uint32_t const expectedFlags, std::optional< Account > const &holder=std::nullopt) const
Definition: mpt.cpp:292
Account const & holder(std::string const &h) const
Definition: mpt.cpp:136
MPT operator[](std::string const &name)
Definition: mpt.cpp:414
Account const & issuer() const
Definition: mpt.h:178
bool forObject(std::function< bool(SLEP const &sle)> const &cb, std::optional< Account > const &holder=std::nullopt) const
Definition: mpt.cpp:260
std::optional< MPTID > id_
Definition: mpt.h:147
std::int64_t getBalance(Account const &account) const
Definition: mpt.cpp:382
std::unordered_map< std::string, Account > const holders_
Definition: mpt.h:146
std::uint32_t getFlags(std::optional< Account > const &holder) const
Definition: mpt.cpp:400
bool checkMPTokenAmount(Account const &holder, std::int64_t expectedAmount) const
Definition: mpt.cpp:274
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:112
void operator()(Env &env) const
Definition: mpt.cpp:35
Account const & account_
Definition: mpt.h:61
std::int64_t const amount_
Definition: mpt.h:62
MPTTester const & tester_
Definition: mpt.h:60
std::uint32_t flags_
Definition: mpt.h:40
MPTTester & tester_
Definition: mpt.h:39
void operator()(Env &env) const
Definition: mpt.cpp:29
std::optional< Account > holder_
Definition: mpt.h:41
Match the number of items in the account's owner directory.
Definition: owners.h:70
void operator()(Env &env) const
Definition: mpt.cpp:41
std::function< bool()> cb_
Definition: mpt.h:77
Check a set of conditions.
Definition: require.h:64
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition: ter.h:34
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:508
Keylet mptIssuance(std::uint32_t seq, AccountID const &issuer) noexcept
Definition: Indexes.cpp:494
Json::Value claw(Account const &account, STAmount const &amount, std::optional< Account > const &mptHolder)
Definition: trust.cpp:67
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Definition: pay.cpp:29
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
Rate transferRate(ReadView const &view, AccountID const &issuer)
Returns IOU issuer transfer fee as Rate.
Definition: View.cpp:650
constexpr std::uint32_t const tfMPTUnlock
Definition: TxFlags.h:156
@ lsfMPTAuthorized
@ lsfMPTLocked
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:47
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
constexpr std::uint32_t const tfMPTUnauthorize
Definition: TxFlags.h:151
@ tecDUPLICATE
Definition: TER.h:302
constexpr std::uint32_t const tfMPTLock
Definition: TxFlags.h:155
@ tesSUCCESS
Definition: TER.h:242
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
MPTID makeMptID(std::uint32_t sequence, AccountID const &account)
Definition: Indexes.cpp:146
std::optional< std::uint32_t > flags
Definition: mpt.h:127
std::optional< Account > holder
Definition: mpt.h:123
std::optional< Account > account
Definition: mpt.h:122
std::optional< MPTID > id
Definition: mpt.h:124
std::optional< std::uint8_t > assetScale
Definition: mpt.h:100
std::optional< std::uint16_t > transferFee
Definition: mpt.h:101
std::optional< std::uint32_t > flags
Definition: mpt.h:106
std::optional< std::string > metadata
Definition: mpt.h:102
std::optional< std::uint64_t > maxAmt
Definition: mpt.h:99
std::optional< Account > issuer
Definition: mpt.h:112
std::optional< MPTID > id
Definition: mpt.h:113
PrettyAmount const & xrp
Definition: mpt.h:91
PrettyAmount const & xrpHolders
Definition: mpt.h:92
std::optional< std::uint32_t > flags
Definition: mpt.h:138
std::optional< Account > account
Definition: mpt.h:133
std::optional< MPTID > id
Definition: mpt.h:135
std::optional< Account > holder
Definition: mpt.h:134
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)