21#include <xrpl/protocol/Feature.h>
22#include <xrpl/protocol/jss.h>
51 Json::Value const meta = env.
rpc(
"tx", txHash)[jss::result][jss::meta];
54 if (!BEAST_EXPECT(meta.
isMember(sfDeliveredAmount.jsonName)))
60 BEAST_EXPECT(meta[sfDeliveredAmount.jsonName] == jsonExpect);
61 BEAST_EXPECT(meta[jss::delivered_amount] == jsonExpect);
75 jv[jss::TransactionType] = jss::PaymentChannelCreate;
76 jv[jss::Account] = account.human();
77 jv[jss::Destination] = to.
human();
79 jv[sfSettleDelay.jsonName] = settleDelay.
count();
94 int const delta = [&]() ->
int {
99 BEAST_EXPECT(margin == 0 || delta >= 0);
100 for (
int i = 0; i < delta; ++i)
119 env.fund(
XRP(10000), alice, becky, carol, gw);
135 auto const acctDelFee{
drops(env.current()->fees().increment)};
147 env(
trust(becky, gw[
"USD"](1000)));
154 env(
offer(carol, gw[
"USD"](51),
XRP(51)));
157 env(
signers(carol, 1, {{alice, 1}, {becky, 1}}));
176 auto const aliceOldBalance{env.balance(alice)};
177 auto const beckyOldBalance{env.balance(becky)};
193 env.balance(becky) ==
194 aliceOldBalance + beckyOldBalance - acctDelFee);
215 for (
int i = 0; i < 8; ++i)
219 auto const beckyOldBalance{env.balance(becky)};
220 auto const carolOldBalance{env.balance(carol)};
226 BEAST_EXPECT(env.closed()->exists(
230 BEAST_EXPECT(env.closed()->exists(
243 BEAST_EXPECT(!env.closed()->exists(
245 BEAST_EXPECT(!env.closed()->exists(
247 BEAST_EXPECT(!env.closed()->exists(
253 env.balance(becky) ==
254 carolOldBalance + beckyOldBalance - acctDelFee);
276 env.fund(
XRP(10000), alice, gw);
280 for (
int i{0}; i < 45; ++i)
282 env(
offer(alice, gw[
"USD"](1),
XRP(1)));
285 env.require(
offers(alice, 45));
293 BEAST_EXPECT(env.closed()->exists(aliceRootKey));
294 BEAST_EXPECT(env.closed()->exists(alicePageKey));
297 auto const acctDelFee{
drops(env.current()->fees().increment)};
298 auto const aliceBalance{env.balance(alice)};
304 BEAST_EXPECT(!env.closed()->exists(aliceRootKey));
305 BEAST_EXPECT(!env.closed()->exists(alicePageKey));
325 env.fund(
XRP(100000), alice, becky, gw);
331 for (
int i{0}; i < 200; ++i)
333 env(
offer(alice, gw[
"USD"](1),
XRP(1)));
334 env(
offer(becky, gw[
"USD"](1),
XRP(1)));
337 env.require(
offers(alice, 200));
338 env.require(
offers(becky, 200));
352 auto const acctDelFee{
drops(env.current()->fees().increment)};
369 jv[jss::TransactionType] = jss::EscrowCreate;
371 jv[jss::Account] = account.human();
372 jv[jss::Destination] = to.human();
374 jv[sfFinishAfter.jsonName] =
375 cancelAfter.time_since_epoch().count() + 1;
376 jv[sfCancelAfter.jsonName] =
377 cancelAfter.time_since_epoch().count() + 2;
381 using namespace std::chrono_literals;
383 env(escrowCreate(alice, becky,
XRP(333), env.now() + 2s));
399 jv[jss::TransactionType] = jss::EscrowCancel;
401 jv[jss::Account] = account.human();
402 jv[sfOwner.jsonName] = from.human();
403 jv[sfOfferSequence.jsonName] =
seq;
406 env(escrowCancel(becky, alice, escrowSeq));
409 Keylet const alicePayChanKey{
413 alice, becky,
XRP(57), 4s, env.now() + 2s, alice.
pk()));
419 auto const beckyBalance{env.balance(becky)};
430 Keylet const& payChanKeylet,
433 jv[jss::TransactionType] = jss::PaymentChannelClaim;
435 jv[jss::Account] = account.human();
436 jv[sfChannel.jsonName] =
to_string(payChanKeylet.key);
437 jv[sfPublicKey.jsonName] =
strHex(pk.slice());
440 env(payChanClose(alice, alicePayChanKey, alice.
pk()));
445 env.enableFeature(fixPayChanRecipientOwnerDir);
462 env(payChanClose(alice, gwPayChanKey, alice.
pk()));
466 auto const aliceBalance{env.balance(alice)};
488 env.fund(
XRP(10000), alice, becky);
493 BEAST_EXPECT(env.closed()->exists(beckyAcctKey));
495 using namespace std::chrono_literals;
497 auto const payChanXRP =
XRP(37);
500 alice, becky, payChanXRP, 4s, env.now() + 1h, alice.
pk()));
502 BEAST_EXPECT(env.closed()->exists(payChanKey));
507 auto const beckyPreDelBalance{env.balance(becky)};
509 auto const acctDelFee{
drops(env.current()->fees().increment)};
515 BEAST_EXPECT(!env.closed()->exists(beckyAcctKey));
521 drops(env.current()->fees().accountReserve(0)) -
XRP(1)),
526 env(
pay(alice, becky,
XRP(10)));
530 BEAST_EXPECT(env.closed()->exists(beckyAcctKey));
531 BEAST_EXPECT(env.balance(becky) ==
XRP(10));
535 auto payChanClaim = [&]() {
537 jv[jss::TransactionType] = jss::PaymentChannelClaim;
539 jv[jss::Account] = alice.
human();
540 jv[sfChannel.jsonName] =
to_string(payChanKey.key);
541 jv[sfBalance.jsonName] =
548 BEAST_EXPECT(env.balance(becky) ==
XRP(10) + payChanXRP);
564 env.fund(
XRP(10000), alice, becky);
572 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
574 auto const alicePreDelBal{env.balance(alice)};
575 auto const beckyPreDelBal{env.balance(becky)};
577 auto const acctDelFee{
drops(env.current()->fees().increment)};
583 BEAST_EXPECT(env.current()->exists(aliceAcctKey));
584 BEAST_EXPECT(env.balance(alice) == alicePreDelBal);
585 BEAST_EXPECT(env.balance(becky) == beckyPreDelBal);
589 env.enableFeature(featureDeletableAccounts);
593 BEAST_EXPECT(env.closed()->exists(aliceAcctKey));
597 BEAST_EXPECT(!env.current()->exists(aliceAcctKey));
599 env.balance(becky) == alicePreDelBal + beckyPreDelBal - acctDelFee);
602 BEAST_EXPECT(!env.closed()->exists(aliceAcctKey));
618 env.fund(
XRP(10000000), alice, gw);
628 constexpr int offerCount{1001};
629 for (
int i{0}; i < offerCount; ++i)
631 env(
offer(alice, gw[currency](1),
XRP(1)));
636 if (currency[0] >
'Z')
641 if (currency[1] >
'Z')
646 if (currency[2] >
'Z')
662 BEAST_EXPECT(closed->exists(aliceOwnerDirKey));
666 BEAST_EXPECT(closed->exists(
keylet::page(aliceOwnerDirKey, i)));
676 auto const acctDelFee{
drops(env.current()->fees().increment)};
681 env.require(
offers(alice, offerCount));
684 env.require(
offers(alice, offerCount - 1));
687 auto const alicePreDelBal{env.balance(alice)};
697 BEAST_EXPECT(!closed->exists(aliceOwnerDirKey));
718 testcase(
"Implicitly created trust line");
723 auto const BUX{gw[
"BUX"]};
725 env.fund(
XRP(10000), alice, gw);
730 env(
offer(alice, BUX(30),
XRP(30)));
736 env.require(
balance(alice, BUX(30)));
743 auto const acctDelFee{
drops(env.current()->fees().increment)};
763 testcase(
"Balance too small for fee");
771 env.fund(env.current()->fees().accountReserve(0),
noripple(alice));
776 env(
noop(alice),
fee(env.balance(alice) -
XRP(1)));
779 auto const acctDelFee{
drops(env.current()->fees().increment)};
780 BEAST_EXPECT(acctDelFee > env.balance(alice));
786 auto const masterBalance{env.balance(env.master)};
794 BEAST_EXPECT(env.balance(env.master) == masterBalance);
799 BEAST_EXPECT(env.balance(alice) ==
XRP(1));
805 BEAST_EXPECT(env.balance(env.master) == masterBalance);
814 using namespace test::jtx;
820 env.
fund(
XRP(100000), alice, bob);
827 env.require(
owners(bob, 250));
844 auto const acctDelFee{
drops(env.current()->fees().increment)};
845 auto const bobOldBalance{env.balance(bob)};
863 testcase(
"Destination Constraints");
865 using namespace test::jtx;
873 env.
fund(
XRP(100000), alice, becky, carol);
890 auto const acctDelFee{
drops(env.current()->fees().increment)};
909 auto const beckyOldBalance{env.balance(becky)};
920 "Destination Constraints with DepositPreauth and Credentials");
922 using namespace test::jtx;
929 const char credType[] =
"abcd";
932 env.
fund(
XRP(100000), alice, becky, carol, daria);
942 std::string const credIdx = jv[jss::result][jss::index].asString();
947 auto const acctDelFee{
drops(env.current()->fees().increment)};
1007 {
"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6E"
1021 auto const jNoCred =
1024 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
1025 jNoCred[jss::result].isMember(jss::error) &&
1026 jNoCred[jss::result][jss::error] ==
"entryNotFound");
1029 testcase(
"Credentials that aren't required");
1037 env, daria, carol, credType)[jss::result][jss::index]
1048 auto const jNoCred =
1052 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
1053 jNoCred[jss::result].isMember(jss::error) &&
1054 jNoCred[jss::result][jss::error] ==
"entryNotFound");
1061 env.fund(
XRP(5000), eaton, fred);
1070 env, eaton, carol, credType)[jss::result][jss::index]
1081 auto const acctDelFee{
drops(env.current()->fees().increment)};
1091 auto const jNoCred =
1095 jNoCred.isObject() && jNoCred.isMember(jss::result) &&
1096 jNoCred[jss::result].isMember(jss::error) &&
1097 jNoCred[jss::result][jss::error] ==
"entryNotFound");
1104 env.fund(
XRP(10000), john);
1108 uint32_t
const t = env.current()
1110 .parentCloseTime.time_since_epoch()
1113 jv[sfExpiration.jsonName] = t;
1120 jv[jss::result][jss::index].asString();
1137 jv.isObject() && jv.isMember(jss::result) &&
1138 jv[jss::result].isMember(jss::error) &&
1139 jv[jss::result][jss::error] ==
"entryNotFound");
1145 testcase(
"Credentials feature disabled");
1146 using namespace test::jtx;
1153 env.fund(
XRP(100000), alice, becky, carol);
1165 auto const acctDelFee{
drops(env.current()->fees().increment)};
1168 "098B7F1B146470A1C5084DC7832C04A72939E3EBC58E68AB8B579BA072B0CE"
1187 testcase(
"Deleting Issuer deletes issued credentials");
1189 using namespace test::jtx;
1195 const char credType[] =
"abcd";
1198 env.
fund(
XRP(100000), alice, becky, carol);
1210 std::string const credIdx = jv[jss::result][jss::index].asString();
1215 auto const acctDelFee{
drops(env.current()->fees().increment)};
1220 BEAST_EXPECT(!env.le(credIdx));
1224 jv.isObject() && jv.isMember(jss::result) &&
1225 jv[jss::result].isMember(jss::error) &&
1226 jv[jss::result][jss::error] ==
"entryNotFound");
1231 testcase(
"Deleting Subject deletes issued credentials");
1233 using namespace test::jtx;
1239 const char credType[] =
"abcd";
1242 env.
fund(
XRP(100000), alice, becky, carol);
1254 std::string const credIdx = jv[jss::result][jss::index].asString();
1259 auto const acctDelFee{
drops(env.current()->fees().increment)};
1264 BEAST_EXPECT(!env.le(credIdx));
1268 jv.isObject() && jv.isMember(jss::result) &&
1269 jv[jss::result].isMember(jss::error) &&
1270 jv[jss::result][jss::error] ==
"entryNotFound");
std::string asString() const
Returns the unquoted string value.
bool isMember(const char *key) const
Return true if the object has a member named key.
testcase_t testcase
Memberspace for declaring test cases.
Slice slice() const noexcept
Json::Value getJson(JsonOptions) const override
void testBalanceTooSmallForFee()
void verifyDeliveredAmount(jtx::Env &env, STAmount const &amount)
void testDeleteCredentialsOwner()
void incLgrSeqForAccDel(jtx::Env &env, jtx::Account const &acc, std::uint32_t margin=0)
void run() override
Runs the suite.
void testAmendmentEnable()
void testDestinationDepositAuthCredentials()
std::uint32_t openLedgerSeq(jtx::Env &env)
static Json::Value payChanCreate(jtx::Account const &account, jtx::Account const &to, STAmount const &amount, NetClock::duration const &settleDelay, NetClock::time_point const &cancelAfter, PublicKey const &pk)
void testImplicitlyCreatedTrustline()
Immutable cryptographic account descriptor.
PublicKey const & pk() const
Return the public key.
AccountID id() const
Returns the Account ID.
static Account const master
The master account.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
std::shared_ptr< STTx const > tx() const
Return the tx data for the last JTx.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Set a multisignature on a JTx.
Match the number of items in the account's owner directory.
Set the regular signature on a JTx.
Set the expected result code for a JTx The test will fail if the code doesn't match.
Set a ticket sequence on a JTx.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Keylet ownerDir(AccountID const &id) noexcept
The root page of an account's directory.
Keylet signers(AccountID const &account) noexcept
A SignerList.
static ticket_t const ticket
Keylet check(AccountID const &id, std::uint32_t seq) noexcept
A Check.
Keylet offer(AccountID const &id, std::uint32_t seq) noexcept
An offer from an account.
Keylet depositPreauth(AccountID const &owner, AccountID const &preauthorized) noexcept
A DepositPreauth.
Keylet payChan(AccountID const &src, AccountID const &dst, std::uint32_t seq) noexcept
A PaymentChannel.
Json::Value create(A const &account, A const &dest, STAmount const &sendMax)
Create a check.
Json::Value cancel(jtx::Account const &dest, uint256 const &checkId)
Cancel a check.
Json::Value create(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Json::Value ledgerEntry(jtx::Env &env, jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
Json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
Json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
Json::Value setValid(jtx::Account const &account)
Json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
Json::Value regkey(Account const &account, disabled_t)
Disable the regular key.
Json::Value signers(Account const &account, std::uint32_t quorum, std::vector< signer > const &v)
owner_count< ltOFFER > offers
Match the number of offers in the account's owner directory.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
Json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
Json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
Json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
Json::Value noop(Account const &account)
The null transaction.
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
XRP_t const XRP
Converts to XRP Issue or STAmount.
FeatureBitset supported_amendments()
Json::Value offer_cancel(Account const &account, std::uint32_t offerSeq)
Cancel an offer.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr std::uint32_t asfDepositAuth
constexpr std::uint32_t asfRequireDest
constexpr std::uint32_t tfImmediateOrCancel
std::string strHex(FwdIt begin, FwdIt end)
constexpr std::uint32_t tfUniversal
std::string to_string(base_uint< Bits, Tag > const &a)
constexpr std::uint32_t tfClose
A pair of SHAMap key and LedgerEntryType.
Set the sequence number on a JTx.
T time_since_epoch(T... args)