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