2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/amount.h>
5#include <test/jtx/balance.h>
6#include <test/jtx/flags.h>
7#include <test/jtx/owners.h>
8#include <test/jtx/pay.h>
9#include <test/jtx/ter.h>
10#include <test/jtx/token.h>
11#include <test/jtx/trust.h>
12#include <test/jtx/txflags.h>
14#include <xrpl/basics/base_uint.h>
15#include <xrpl/beast/unit_test/suite.h>
16#include <xrpl/beast/utility/Journal.h>
17#include <xrpl/core/ServiceRegistry.h>
18#include <xrpl/ledger/OpenView.h>
19#include <xrpl/protocol/Feature.h>
20#include <xrpl/protocol/Indexes.h>
21#include <xrpl/protocol/SField.h>
22#include <xrpl/protocol/SeqProxy.h>
23#include <xrpl/protocol/TER.h>
24#include <xrpl/protocol/TxFlags.h>
43 auto const nftID{token::getNextID(env, account, 0u, tfTransferable, xfee)};
44 env(token::mint(account, 0), token::XferFee(xfee), Txflags(tfTransferable));
49 env(token::createOffer(account, nftID, currency), Txflags(tfSellNFToken));
59 testcase(
"Unauthorized seller tries to accept buy offer");
62 Env env(*
this, features);
66 auto const usd{g1[
"USD"]};
68 env.fund(XRP(10000), g1, a1, a2);
69 env(fset(g1, asfRequireAuth));
72 auto const limit = usd(10000);
74 env(trust(a1, limit));
75 env(trust(g1, limit, a1, tfSetfAuth));
76 env(pay(g1, a1, usd(1000)));
83 env(token::createOffer(a1, nftID, usd(10)), token::Owner(a2));
85 if (features[fixEnforceNFTokenTrustlineV2])
88 env(token::acceptBuyOffer(a2, buyIdx), Ter(
tecNO_LINE));
92 env(trust(a2, limit));
95 env(token::acceptBuyOffer(a2, buyIdx), Ter(
tecNO_AUTH));
102 env(token::acceptBuyOffer(a2, buyIdx));
105 BEAST_EXPECT(env.balance(a2, usd) == usd(10));
112 testcase(
"Unauthorized buyer tries to create buy offer");
115 Env env(*
this, features);
119 auto const usd{g1[
"USD"]};
121 env.fund(XRP(10000), g1, a1, a2);
122 env(fset(g1, asfRequireAuth));
128 env(token::createOffer(a1, nftID, usd(10)), token::Owner(a2), Ter(
tecUNFUNDED_OFFER));
136 sleA1->setFieldAmount(sfBalance, a1[
"USD"](-1000));
140 env.app().getOpenLedger().modify(unauthTrustline);
142 if (features[fixEnforceNFTokenTrustlineV2])
145 env(token::createOffer(a1, nftID, usd(10)), token::Owner(a2), Ter(
tecNO_AUTH));
151 env(token::createOffer(a1, nftID, usd(10)), token::Owner(a2));
158 testcase(
"Seller tries to accept buy offer from unauth buyer");
161 Env env(*
this, features);
165 auto const usd{g1[
"USD"]};
167 env.fund(XRP(10000), g1, a1, a2);
168 env(fset(g1, asfRequireAuth));
171 auto const limit = usd(10000);
176 env(trust(a1, limit));
177 env(trust(g1, limit, a1, tfSetfAuth));
178 env(pay(g1, a1, usd(10)));
179 env(trust(a2, limit));
180 env(trust(g1, limit, a2, tfSetfAuth));
181 env(pay(g1, a2, usd(10)));
185 env(token::createOffer(a1, nftID, usd(10)), token::Owner(a2));
188 env(pay(a1, g1, usd(10)));
189 env(trust(a1, usd(0)));
190 env(trust(g1, a1[
"USD"](0)));
199 sleA1->setFieldAmount(sfBalance, a1[
"USD"](-1000));
203 env.app().getOpenLedger().modify(unauthTrustline);
204 if (features[fixEnforceNFTokenTrustlineV2])
207 env(token::acceptBuyOffer(a2, buyIdx), Ter(
tecNO_AUTH));
215 "Authorized buyer tries to accept sell offer from unauthorized "
219 Env env(*
this, features);
223 auto const usd{g1[
"USD"]};
225 env.fund(XRP(10000), g1, a1, a2);
226 env(fset(g1, asfRequireAuth));
229 auto const limit = usd(10000);
231 env(trust(a1, limit));
232 env(trust(g1, limit, a1, tfSetfAuth));
233 env(pay(g1, a1, usd(1000)));
236 if (features[fixEnforceNFTokenTrustlineV2])
240 env(token::createOffer(a2, nftID, usd(10)), Txflags(tfSellNFToken), Ter(
tecNO_LINE));
242 env(trust(a2, limit));
244 env(token::createOffer(a2, nftID, usd(10)), Txflags(tfSellNFToken), Ter(
tecNO_AUTH));
247 env(trust(g1, usd(0), a2, tfSetfAuth));
250 env(token::createOffer(a2, nftID, usd(10)), Txflags(tfSellNFToken));
256 env(trust(a2, usd(0)));
260 env(token::acceptSellOffer(a1, sellIdx), Ter(
tecNO_LINE));
264 env(trust(a2, limit));
268 env(token::acceptSellOffer(a1, sellIdx), Ter(
tecNO_AUTH));
276 env(token::createOffer(a2, nftID, usd(10)), Txflags(tfSellNFToken));
281 env(token::acceptSellOffer(a1, sellIdx));
284 BEAST_EXPECT(env.balance(a2, usd) == usd(10));
291 testcase(
"Unauthorized buyer tries to accept sell offer");
294 Env env(*
this, features);
298 auto const usd{g1[
"USD"]};
300 env.fund(XRP(10000), g1, a1, a2);
301 env(fset(g1, asfRequireAuth));
304 auto const limit = usd(10000);
306 env(trust(a2, limit));
307 env(trust(g1, limit, a2, tfSetfAuth));
319 sleA1->setFieldAmount(sfBalance, a1[
"USD"](-1000));
323 env.app().getOpenLedger().modify(unauthTrustline);
324 if (features[fixEnforceNFTokenTrustlineV2])
326 env(token::acceptSellOffer(a1, sellIdx), Ter(
tecNO_AUTH));
333 testcase(
"Unauthorized broker bridges authorized buyer and seller.");
336 Env env(*
this, features);
340 Account const broker{
"broker"};
341 auto const usd{g1[
"USD"]};
343 env.fund(XRP(10000), g1, a1, a2, broker);
344 env(fset(g1, asfRequireAuth));
347 auto const limit = usd(10000);
349 env(trust(a1, limit));
350 env(trust(g1, limit, a1, tfSetfAuth));
351 env(pay(g1, a1, usd(1000)));
352 env(trust(a2, limit));
353 env(trust(g1, limit, a2, tfSetfAuth));
354 env(pay(g1, a2, usd(1000)));
359 env(token::createOffer(a1, nftID, usd(11)), token::Owner(a2));
362 if (features[fixEnforceNFTokenTrustlineV2])
365 env(token::brokerOffers(broker, buyIdx, sellIdx),
366 token::BrokerFee(usd(1)),
371 env(trust(broker, limit));
375 env(token::brokerOffers(broker, buyIdx, sellIdx),
376 token::BrokerFee(usd(1)),
381 env(token::brokerOffers(broker, buyIdx, sellIdx));
387 env(token::brokerOffers(broker, buyIdx, sellIdx), token::BrokerFee(usd(1)));
390 BEAST_EXPECT(env.balance(broker, usd) == usd(1));
398 "Authorized broker tries to bridge offers from unauthorized "
402 Env env(*
this, features);
406 Account const broker{
"broker"};
407 auto const usd{g1[
"USD"]};
409 env.fund(XRP(10000), g1, a1, a2, broker);
410 env(fset(g1, asfRequireAuth));
413 auto const limit = usd(10000);
415 env(trust(a1, limit));
416 env(trust(g1, usd(0), a1, tfSetfAuth));
417 env(pay(g1, a1, usd(1000)));
418 env(trust(a2, limit));
419 env(trust(g1, usd(0), a2, tfSetfAuth));
420 env(pay(g1, a2, usd(1000)));
421 env(trust(broker, limit));
422 env(trust(g1, usd(0), broker, tfSetfAuth));
423 env(pay(g1, broker, usd(1000)));
428 env(token::createOffer(a1, nftID, usd(11)), token::Owner(a2));
432 env(pay(a1, g1, usd(1000)));
433 env(trust(a1, usd(0)));
438 sleA1->setFieldAmount(sfBalance, a1[
"USD"](-1000));
442 env.app().getOpenLedger().modify(unauthTrustline);
444 if (features[fixEnforceNFTokenTrustlineV2])
447 env(token::brokerOffers(broker, buyIdx, sellIdx),
448 token::BrokerFee(usd(1)),
458 "Authorized broker tries to bridge offers from unauthorized "
462 Env env(*
this, features);
466 Account const broker{
"broker"};
467 auto const usd{g1[
"USD"]};
469 env.fund(XRP(10000), g1, a1, a2, broker);
470 env(fset(g1, asfRequireAuth));
473 auto const limit = usd(10000);
475 env(trust(a1, limit));
476 env(trust(g1, limit, a1, tfSetfAuth));
477 env(pay(g1, a1, usd(1000)));
478 env(trust(broker, limit));
479 env(trust(g1, limit, broker, tfSetfAuth));
480 env(pay(g1, broker, usd(1000)));
484 env(trust(g1, usd(0), a2, tfSetfAuth));
489 env(token::createOffer(a1, nftID, usd(11)), token::Owner(a2));
494 env(trust(a2, usd(0)));
497 if (features[fixEnforceNFTokenTrustlineV2])
500 env(token::brokerOffers(broker, buyIdx, sellIdx),
501 token::BrokerFee(usd(1)),
506 env(trust(a2, limit));
510 env(token::brokerOffers(broker, buyIdx, sellIdx),
511 token::BrokerFee(usd(1)),
516 env(token::brokerOffers(broker, buyIdx, sellIdx), Ter(
tecNO_AUTH));
522 env(token::brokerOffers(broker, buyIdx, sellIdx), token::BrokerFee(usd(1)));
525 BEAST_EXPECT(env.balance(a2, usd) == usd(10));
533 testcase(
"Unauthorized minter receives transfer fee.");
536 Env env(*
this, features);
538 Account const minter{
"minter"};
541 auto const usd{g1[
"USD"]};
543 env.fund(XRP(10000), g1, minter, a1, a2);
544 env(fset(g1, asfRequireAuth));
547 auto const limit = usd(10000);
549 env(trust(a1, limit));
550 env(trust(g1, limit, a1, tfSetfAuth));
551 env(pay(g1, a1, usd(1000)));
552 env(trust(a2, limit));
553 env(trust(g1, limit, a2, tfSetfAuth));
554 env(pay(g1, a2, usd(1000)));
556 env(trust(minter, limit));
561 auto const [nftID, minterSellIdx] =
mintAndOfferNFT(env, minter, drops(1), 1);
562 env(token::acceptSellOffer(a1, minterSellIdx));
565 env(token::createOffer(a1, nftID, usd(100)), Txflags(tfSellNFToken));
567 if (features[fixEnforceNFTokenTrustlineV2])
570 env(token::acceptSellOffer(a2, sellIdx), Ter(
tecNO_AUTH));
576 env(token::acceptSellOffer(a2, sellIdx));
579 BEAST_EXPECT(env.balance(minter, usd) == usd(0.001));
589 static std::array const kFeatures = {kAll - fixEnforceNFTokenTrustlineV2, kAll};
591 for (
auto const feature : kFeatures)
A generic endpoint for log messages.
TestcaseT testcase
Memberspace for declaring test cases.
void testBrokeredAcceptOfferUnauthorizedBuyer(FeatureBitset features)
void testAcceptBuyOfferUnauthorizedBuyer(FeatureBitset features)
void testBuyOfferUnauthorizedSeller(FeatureBitset features)
void testBrokeredAcceptOfferUnauthorizedSeller(FeatureBitset features)
void testSellOfferUnauthorizedBuyer(FeatureBitset features)
void testCreateBuyOfferUnauthorizedBuyer(FeatureBitset features)
void testTransferFeeUnauthorizedMinter(FeatureBitset features)
static auto mintAndOfferNFT(test::jtx::Env &env, test::jtx::Account const &account, test::jtx::PrettyAmount const ¤cy, uint32_t xfee=0u)
void run() override
Runs the suite.
void testBrokeredAcceptOfferUnauthorizedBroker(FeatureBitset features)
void testSellOfferUnauthorizedSeller(FeatureBitset features)
Writable ledger view that accumulates state and tx changes.
void rawInsert(SLE::ref sle) override
Unconditionally insert a state item.
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Immutable cryptographic account descriptor.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
Keylet nftokenOffer(AccountID const &owner, SeqProxy const &seq)
An offer from an account to buy or sell an NFT.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
BEAST_DEFINE_TESTSUITE_PRIO(AccountSet, app, xrpl, 1)
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...