2#include <test/jtx/Account.h>
3#include <test/jtx/Env.h>
4#include <test/jtx/TestHelpers.h>
5#include <test/jtx/amount.h>
6#include <test/jtx/balance.h>
7#include <test/jtx/credentials.h>
8#include <test/jtx/deposit.h>
9#include <test/jtx/escrow.h>
10#include <test/jtx/fee.h>
11#include <test/jtx/flags.h>
12#include <test/jtx/noop.h>
13#include <test/jtx/offer.h>
14#include <test/jtx/owners.h>
15#include <test/jtx/paths.h>
16#include <test/jtx/pay.h>
17#include <test/jtx/require.h>
18#include <test/jtx/sendmax.h>
19#include <test/jtx/seq.h>
20#include <test/jtx/ter.h>
21#include <test/jtx/ticket.h>
22#include <test/jtx/trust.h>
23#include <test/jtx/txflags.h>
24#include <test/jtx/vault.h>
26#include <xrpl/basics/strHex.h>
27#include <xrpl/beast/unit_test/suite.h>
28#include <xrpl/json/json_value.h>
29#include <xrpl/json/to_string.h>
30#include <xrpl/protocol/AccountID.h>
31#include <xrpl/protocol/Feature.h>
32#include <xrpl/protocol/Issue.h>
33#include <xrpl/protocol/LedgerFormats.h>
34#include <xrpl/protocol/SField.h>
35#include <xrpl/protocol/STAmount.h>
36#include <xrpl/protocol/TER.h>
37#include <xrpl/protocol/TxFlags.h>
38#include <xrpl/protocol/XRPAmount.h>
39#include <xrpl/protocol/jss.h>
56 return env.
current()->fees().accountReserve(count, 1);
63 return env.
le(acct)->isFlag(lsfDepositAuth);
80 env(
fset(alice, asfDepositAuth));
84 env(
fclear(alice, asfDepositAuth));
102 IOU const usd = gw[
"USD"];
106 env.
fund(
XRP(10000), alice, bob, carol, gw);
108 env.
trust(usd(1000), alice, bob);
111 env(
pay(gw, alice, usd(150)));
112 env(
offer(carol, usd(100),
XRP(100)));
116 env(
pay(alice, bob, usd(50)));
124 auto failedIouPayments = [
this, &env, &alice, &bob, &usd]() {
139 BEAST_EXPECT(bobXrpBalance == env.
balance(bob,
XRP));
140 BEAST_EXPECT(bobUsdBalance == env.
balance(bob, usd));
148 env(
pay(bob, alice, usd(25)));
154 env(
pay(bob, alice, bobPaysXRP),
Fee(bobPaysFee));
160 BEAST_EXPECT(env.
balance(bob, usd) == usd(25));
174 env(
fclear(bob, asfDepositAuth));
177 env(
pay(alice, bob, usd(50)));
196 auto const baseFee = env.
current()->fees().base;
198 env.
fund(
XRP(10000), alice, bob);
215 env(
pay(bob, alice, bobPaysXRP),
Fee(bobPaysFee));
269 env(
pay(alice, bob,
drops(baseFee - 1)));
299 IOU const usD1(gw1[
"USD"]);
300 IOU const usD2(gw2[
"USD"]);
305 bool withDepositAuth) {
306 Env env(*
this, features);
308 env.
fund(
XRP(10000), gw1, alice, bob);
310 env(
trust(gw1, alice[
"USD"](10), noRipplePrev ? tfSetNoRipple : 0));
311 env(
trust(gw1, bob[
"USD"](10), noRippleNext ? tfSetNoRipple : 0));
312 env.
trust(usD1(10), alice, bob);
314 env(
pay(gw1, alice, usD1(10)));
317 env(
fset(gw1, asfDepositAuth));
320 env(
pay(alice, bob, usD1(10)),
Path(gw1),
Ter(result));
326 bool withDepositAuth) {
327 Env env(*
this, features);
329 env.
fund(
XRP(10000), gw1, gw2, alice);
331 env(
trust(alice, usD1(10), noRipplePrev ? tfSetNoRipple : 0));
332 env(
trust(alice, usD2(10), noRippleNext ? tfSetNoRipple : 0));
333 env(
pay(gw2, alice, usD2(10)));
336 env(
fset(alice, asfDepositAuth));
343 for (
int i = 0; i < 8; ++i)
345 auto const noRipplePrev = i & 0x1;
346 auto const noRippleNext = i & 0x2;
347 auto const withDepositAuth = i & 0x4;
373 jvParams[jss::ledger_index] = jss::validated;
374 jvParams[jss::deposit_preauth][jss::owner] = acc.
human();
376 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
377 for (
auto const& o : auth)
379 arr.append(o.toLEJson());
381 return env.
rpc(
"json",
"ledger_entry",
to_string(jvParams));
399 env.
fund(
XRP(10000), alice, becky);
418 env.
fund(
XRP(10000), alice, becky);
435 BEAST_EXPECT(env.
seq(alice) == aliceSeq);
443 BEAST_EXPECT(env.
seq(alice) == aliceSeq);
458 Env env(*
this, features);
468 env.
fund(
XRP(10000), alice, becky);
489 tx[sfUnauthorize.jsonName] = becky.
human();
567 Vault const vault{env};
568 auto [tx,
keylet] = vault.create({.owner = becky, .asset =
xrpIssue()});
572 auto const sleVault = env.
le(
keylet);
573 if (!BEAST_EXPECT(sleVault))
575 Account const vaultPseudo{
"vault", sleVault->at(sfAccount)};
577 auto const expectedResult =
581 env.
require(
Owners(alice, features[fixCleanup3_3_0] ? 0 : 1));
594 IOU const usd(gw[
"USD"]);
600 Env env(*
this, features);
601 env.
fund(
XRP(5000), alice, becky, gw);
604 env.
trust(usd(1000), alice);
605 env.
trust(usd(1000), becky);
608 env(
pay(gw, alice, usd(500)));
620 env(
fset(becky, asfDepositAuth));
629 char const credType[] =
"abcde";
634 bool const supportsCredentials = features[featureCredentials];
650 ? jv[jss::result][jss::index].asString()
651 :
"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6"
663 Env env(*
this, features);
664 env.
fund(
XRP(5000), alice, becky, carol, gw);
667 env.
trust(usd(1000), alice);
668 env.
trust(usd(1000), becky);
669 env.
trust(usd(1000), carol);
672 env(
pay(gw, alice, usd(1000)));
676 env(
pay(alice, becky,
XRP(100)));
677 env(
pay(alice, becky, usd(100)));
681 env(
fset(becky, asfDepositAuth));
704 env(
pay(alice, becky,
XRP(100)));
705 env(
pay(alice, becky, usd(100)));
709 env(
fset(alice, asfDepositAuth));
722 env(
pay(alice, becky,
XRP(100)));
723 env(
pay(alice, becky, usd(100)));
736 env(
fclear(becky, asfDepositAuth));
739 env(
pay(alice, becky,
XRP(100)));
740 env(
pay(alice, becky, usd(100)));
749 char const credType[] =
"abcde";
750 Account const issuer{
"issuer"};
757 testcase(
"Payment failure with disabled credentials rule.");
761 env.
fund(
XRP(5000), issuer, bob, alice);
765 env(
fset(bob, asfDepositAuth));
780 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
787 testcase(
"Payment with credentials.");
791 env.
fund(
XRP(5000), issuer, alice, bob, john);
800 std::string const credIdx = jv[jss::result][jss::index].asString();
803 env(
fset(bob, asfDepositAuth));
814 jDP.isObject() && jDP.isMember(jss::result) &&
815 !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) &&
816 jDP[jss::result][jss::node].isMember(
"LedgerEntryType") &&
817 jDP[jss::result][jss::node][
"LedgerEntryType"] == jss::DepositPreauth);
821 auto jv =
pay(alice, bob,
XRP(100));
845 env(
fset(john, asfDepositAuth));
852 testcase(
"Payment failure with invalid credentials.");
856 env.
fund(
XRP(10000), issuer, alice, bob, maria);
867 std::string const credIdx = jv[jss::result][jss::index].asString();
876 env(
fset(bob, asfDepositAuth));
887 {{.issuer = issuer, .credType = credType},
888 {.issuer = issuer, .credType = credType}}),
897 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
900 env(
pay(alice, bob,
XRP(100)),
907 env(
pay(maria, bob,
XRP(100)),
914 char const credType2[] =
"fghij";
920 std::string const credIdx2 = jv[jss::result][jss::index].asString();
923 env(
pay(alice, bob,
XRP(100)),
944 char const credType[] =
"abcde";
945 Account const issuer{
"issuer"};
949 Env env(*
this, features);
951 env.
fund(
XRP(5000), issuer, alice, bob);
960 std::string const credIdx = jv[jss::result][jss::index].asString();
982 char const credType[] =
"abcde";
983 Account const issuer{
"issuer"};
989 testcase(
"Creating / deleting with credentials.");
993 env.
fund(
XRP(5000), issuer, alice, bob);
1006 jv[sfUnauthorize.jsonName] = issuer.
human();
1013 jv[sfAuthorize.jsonName] = issuer.
human();
1021 jv[sfUnauthorize.jsonName] = issuer.
human();
1029 jv[sfAuthorize.jsonName] = issuer.
human();
1042 auto& arr(jv[sfAuthorizeCredentials.jsonName]);
1047 credParent[jss::Credential] = cred;
1048 arr.
append(std::move(credParent));
1061 Account const a(
"a"), b(
"b"), c(
"c"), d(
"d"), e(
"e"), f(
"f"), g(
"g"), h(
"h"),
1063 auto const& z = credType;
1066 {{.issuer = a, .credType = z},
1067 {.issuer = b, .credType = z},
1068 {.issuer = c, .credType = z},
1069 {.issuer = d, .credType = z},
1070 {.issuer = e, .credType = z},
1071 {.issuer = f, .credType = z},
1072 {.issuer = g, .credType = z},
1073 {.issuer = h, .credType = z},
1074 {.issuer = i, .credType = z}});
1089 env.
fund(env.
current()->fees().accountReserve(0, 1), john);
1110 jDP.isObject() && jDP.isMember(jss::result) &&
1111 !jDP[jss::result].isMember(jss::error) &&
1112 jDP[jss::result].isMember(jss::node) &&
1113 jDP[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1114 jDP[jss::result][jss::node][
"LedgerEntryType"] == jss::DepositPreauth);
1117 BEAST_EXPECT(jDP[jss::result][jss::node][jss::Account] == bob.
human());
1118 auto const&
credentials(jDP[jss::result][jss::node][
"AuthorizeCredentials"]);
1122 auto const& c(o[jss::Credential]);
1123 BEAST_EXPECT(c[jss::Issuer].asString() == issuer.
human());
1140 jDP.isObject() && jDP.isMember(jss::result) &&
1141 jDP[jss::result].isMember(jss::error) &&
1142 jDP[jss::result][jss::error] ==
"entryNotFound");
1150 using namespace jtx;
1151 char const credType[] =
"abcde";
1152 char const credType2[] =
"fghijkl";
1153 Account const issuer{
"issuer"};
1157 IOU const usd = gw[
"USD"];
1161 testcase(
"Payment failure with expired credentials.");
1165 env.
fund(
XRP(10000), issuer, alice, bob, gw);
1173 env.
current()->header().parentCloseTime.time_since_epoch().count() + 60;
1174 jv[sfExpiration.jsonName] = t;
1185 env.
current()->header().parentCloseTime.time_since_epoch().count() + 1000;
1186 jv[sfExpiration.jsonName] = t2;
1197 std::string const credIdx = jv[jss::result][jss::index].asString();
1199 std::string const credIdx2 = jv[jss::result][jss::index].asString();
1202 env(
fset(bob, asfDepositAuth));
1207 {{.issuer = issuer, .credType = credType},
1208 {.issuer = issuer, .credType = credType2}}));
1218 env(
pay(alice, bob,
XRP(100)),
1227 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1228 jDelCred[jss::result].isMember(jss::error) &&
1229 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1236 jle.isObject() && jle.isMember(jss::result) &&
1237 !jle[jss::result].isMember(jss::error) &&
1238 jle[jss::result].isMember(jss::node) &&
1239 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1240 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1241 jle[jss::result][jss::node][jss::Issuer] == issuer.
human() &&
1242 jle[jss::result][jss::node][jss::Subject] == alice.
human() &&
1243 jle[jss::result][jss::node][
"CredentialType"] ==
1254 env.
current()->header().parentCloseTime.time_since_epoch().count() + 40;
1255 jv[sfExpiration.jsonName] = t;
1262 std::string const credIdx = jv[jss::result][jss::index].asString();
1278 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1279 jDelCred[jss::result].isMember(jss::error) &&
1280 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1290 testcase(
"Escrow failure with expired credentials.");
1294 env.
fund(
XRP(5000), issuer, alice, bob, zelda);
1300 env.
current()->header().parentCloseTime.time_since_epoch().count() + 50;
1301 jv[sfExpiration.jsonName] = t;
1311 std::string const credIdx = jv[jss::result][jss::index].asString();
1314 env(
fset(bob, asfDepositAuth));
1320 auto const seq = env.
seq(alice);
1333 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
1353 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1354 jDelCred[jss::result].isMember(jss::error) &&
1355 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1362 using namespace jtx;
1372 env.
fund(
XRP(5000), stock, alice, bob);
1375 {.issuer =
"a", .credType =
"a"},
1376 {.issuer =
"b", .credType =
"b"},
1377 {.issuer =
"c", .credType =
"c"},
1378 {.issuer =
"d", .credType =
"d"},
1379 {.issuer =
"e", .credType =
"e"},
1380 {.issuer =
"f", .credType =
"f"},
1381 {.issuer =
"g", .credType =
"g"},
1382 {.issuer =
"h", .credType =
"h"}};
1385 env.
fund(
XRP(5000), c.issuer);
1394 pubKey2Acc.
emplace(c.issuer.human(), c.issuer);
1397 for (
int i = 0; i < 10; ++i)
1404 auto const& authCred(dp[jss::result][jss::node][
"AuthorizeCredentials"]);
1405 BEAST_EXPECT(authCred.isArray() && authCred.size() ==
credentials.size());
1407 for (
auto const& o : authCred)
1409 auto const& c(o[jss::Credential]);
1410 auto issuer = c[jss::Issuer].asString();
1412 if (BEAST_EXPECT(pubKey2Acc.
contains(issuer)))
1415 pubKey2Acc.
at(issuer), c[
"CredentialType"].asString());
1432 for (
int i = 0; i < 10; ++i)
1439 testcase(
"Check duplicate credentials.");
1446 for (
auto const& c : copyCredentials)
1448 auto credentials2 = copyCredentials;
1464 env, alice, c.issuer, c.credType)[jss::result][jss::index]
1469 for (
auto const& h : credentialIDs)
1471 auto credentialIDs2 = credentialIDs;
TestcaseT testcase
Memberspace for declaring test cases.
Value removeMember(char const *key)
Remove and return the named member.
Value & append(Value const &value)
Append value to array at the end.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
A transaction testing environment.
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
SLE::const_pointer le(Account const &account) const
Return an account root.
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
std::uint32_t seq(Account const &account) const
Returns the next sequence number on account.
json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
void memoize(Account const &account)
Associate AccountID with account.
void require(Args const &... args)
Check a set of requirements.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
NetClock::time_point now()
Returns the current network time.
Converts to IOU Issue or STAmount.
Match clear account flags.
Match the number of items in the account's owner directory.
Check a set of conditions.
Sets the SendMax 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.
T emplace_back(T... args)
@ Array
array value (ordered list)
@ Object
object value (collection of name/value pairs).
Keylet computation functions.
json::Value accept(jtx::Account const &subject, jtx::Account const &issuer, std::string_view credType)
json::Value create(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 unauthCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
json::Value authCredentials(jtx::Account const &account, std::vector< AuthorizeCredentials > const &auth)
json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
json::Value unauth(Account const &account, Account const &unauth)
Remove pre-authorization for deposit.
json::Value create(AccountID const &account, AccountID const &to, STAmount const &amount)
auto const kFinishTime
Set the "FinishAfter" time tag on a JTx.
json::Value finish(AccountID const &account, AccountID const &from, std::uint32_t seq)
json::Value create(Account const &account, std::uint32_t count)
Create one of more tickets.
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
XrpT const XRP
Converts to XRP Issue or STAmount.
json::Value noop(Account const &account)
The null transaction.
std::uint32_t ownerCount(Env const &env, Account const &account)
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
FeatureBitset testableAmendments()
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
OwnerCount< 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.
OwnerCount< ltTICKET > tickets
Match the number of tickets on the account.
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
static bool hasDepositAuth(jtx::Env const &env, jtx::Account const &acct)
static XRPAmount reserve(jtx::Env &env, std::uint32_t count)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
constexpr XRPAmount
Convert XRP to drops (integral types).
static json::Value ledgerEntryDepositPreauth(jtx::Env &env, jtx::Account const &acc, std::vector< jtx::deposit::AuthorizeCredentials > const &auth)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::string strHex(FwdIt begin, FwdIt end)
std::string to_string(BaseUInt< Bits, Tag > const &a)
TERSubset< CanCvtToTER > TER
AccountID const & xrpAccount()
Compute AccountID from public key.
@ tecINSUFFICIENT_RESERVE
void run() override
Runs the suite.
void testCredentialsPayment()
void run() override
Runs the suite.
void testInvalid(FeatureBitset features)
void testSortingCredentials()
void testCredentialsCreation()
void testPayment(FeatureBitset features)
void testZeroCredentialID(FeatureBitset features)
Represents an XRP, IOU, or MPT quantity This customizes the string conversion and supports XRP conver...
Set the sequence number on a JTx.