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>
25#include <xrpl/basics/strHex.h>
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/json/json_value.h>
28#include <xrpl/json/to_string.h>
29#include <xrpl/protocol/AccountID.h>
30#include <xrpl/protocol/Feature.h>
31#include <xrpl/protocol/LedgerFormats.h>
32#include <xrpl/protocol/SField.h>
33#include <xrpl/protocol/STAmount.h>
34#include <xrpl/protocol/TER.h>
35#include <xrpl/protocol/TxFlags.h>
36#include <xrpl/protocol/XRPAmount.h>
37#include <xrpl/protocol/jss.h>
54 return env.
current()->fees().accountReserve(count);
61 return env.
le(acct)->isFlag(lsfDepositAuth);
78 env(
fset(alice, asfDepositAuth));
82 env(
fclear(alice, asfDepositAuth));
100 IOU const usd = gw[
"USD"];
104 env.
fund(
XRP(10000), alice, bob, carol, gw);
106 env.
trust(usd(1000), alice, bob);
109 env(
pay(gw, alice, usd(150)));
110 env(
offer(carol, usd(100),
XRP(100)));
114 env(
pay(alice, bob, usd(50)));
122 auto failedIouPayments = [
this, &env, &alice, &bob, &usd]() {
137 BEAST_EXPECT(bobXrpBalance == env.
balance(bob,
XRP));
138 BEAST_EXPECT(bobUsdBalance == env.
balance(bob, usd));
146 env(
pay(bob, alice, usd(25)));
152 env(
pay(bob, alice, bobPaysXRP),
Fee(bobPaysFee));
158 BEAST_EXPECT(env.
balance(bob, usd) == usd(25));
172 env(
fclear(bob, asfDepositAuth));
175 env(
pay(alice, bob, usd(50)));
194 auto const baseFee = env.
current()->fees().base;
196 env.
fund(
XRP(10000), alice, bob);
213 env(
pay(bob, alice, bobPaysXRP),
Fee(bobPaysFee));
267 env(
pay(alice, bob,
drops(baseFee - 1)));
297 IOU const usD1(gw1[
"USD"]);
298 IOU const usD2(gw2[
"USD"]);
303 bool withDepositAuth) {
304 Env env(*
this, features);
306 env.
fund(
XRP(10000), gw1, alice, bob);
308 env(
trust(gw1, alice[
"USD"](10), noRipplePrev ? tfSetNoRipple : 0));
309 env(
trust(gw1, bob[
"USD"](10), noRippleNext ? tfSetNoRipple : 0));
310 env.
trust(usD1(10), alice, bob);
312 env(
pay(gw1, alice, usD1(10)));
315 env(
fset(gw1, asfDepositAuth));
318 env(
pay(alice, bob, usD1(10)),
Path(gw1),
Ter(result));
324 bool withDepositAuth) {
325 Env env(*
this, features);
327 env.
fund(
XRP(10000), gw1, gw2, alice);
329 env(
trust(alice, usD1(10), noRipplePrev ? tfSetNoRipple : 0));
330 env(
trust(alice, usD2(10), noRippleNext ? tfSetNoRipple : 0));
331 env(
pay(gw2, alice, usD2(10)));
334 env(
fset(alice, asfDepositAuth));
341 for (
int i = 0; i < 8; ++i)
343 auto const noRipplePrev = i & 0x1;
344 auto const noRippleNext = i & 0x2;
345 auto const withDepositAuth = i & 0x4;
371 jvParams[jss::ledger_index] = jss::validated;
372 jvParams[jss::deposit_preauth][jss::owner] = acc.
human();
374 auto& arr(jvParams[jss::deposit_preauth][jss::authorized_credentials]);
375 for (
auto const& o : auth)
377 arr.append(o.toLEJson());
379 return env.
rpc(
"json",
"ledger_entry",
to_string(jvParams));
397 env.
fund(
XRP(10000), alice, becky);
416 env.
fund(
XRP(10000), alice, becky);
433 BEAST_EXPECT(env.
seq(alice) == aliceSeq);
441 BEAST_EXPECT(env.
seq(alice) == aliceSeq);
466 env.
fund(
XRP(10000), alice, becky);
487 tx[sfUnauthorize.jsonName] = becky.
human();
573 IOU const usd(gw[
"USD"]);
579 Env env(*
this, features);
580 env.
fund(
XRP(5000), alice, becky, gw);
583 env.
trust(usd(1000), alice);
584 env.
trust(usd(1000), becky);
587 env(
pay(gw, alice, usd(500)));
599 env(
fset(becky, asfDepositAuth));
608 char const credType[] =
"abcde";
613 bool const supportsCredentials = features[featureCredentials];
629 ? jv[jss::result][jss::index].asString()
630 :
"48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6"
642 Env env(*
this, features);
643 env.
fund(
XRP(5000), alice, becky, carol, gw);
646 env.
trust(usd(1000), alice);
647 env.
trust(usd(1000), becky);
648 env.
trust(usd(1000), carol);
651 env(
pay(gw, alice, usd(1000)));
655 env(
pay(alice, becky,
XRP(100)));
656 env(
pay(alice, becky, usd(100)));
660 env(
fset(becky, asfDepositAuth));
683 env(
pay(alice, becky,
XRP(100)));
684 env(
pay(alice, becky, usd(100)));
688 env(
fset(alice, asfDepositAuth));
701 env(
pay(alice, becky,
XRP(100)));
702 env(
pay(alice, becky, usd(100)));
715 env(
fclear(becky, asfDepositAuth));
718 env(
pay(alice, becky,
XRP(100)));
719 env(
pay(alice, becky, usd(100)));
728 char const credType[] =
"abcde";
729 Account const issuer{
"issuer"};
736 testcase(
"Payment failure with disabled credentials rule.");
740 env.
fund(
XRP(5000), issuer, bob, alice);
744 env(
fset(bob, asfDepositAuth));
759 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
766 testcase(
"Payment with credentials.");
770 env.
fund(
XRP(5000), issuer, alice, bob, john);
779 std::string const credIdx = jv[jss::result][jss::index].asString();
782 env(
fset(bob, asfDepositAuth));
793 jDP.isObject() && jDP.isMember(jss::result) &&
794 !jDP[jss::result].isMember(jss::error) && jDP[jss::result].isMember(jss::node) &&
795 jDP[jss::result][jss::node].isMember(
"LedgerEntryType") &&
796 jDP[jss::result][jss::node][
"LedgerEntryType"] == jss::DepositPreauth);
800 auto jv =
pay(alice, bob,
XRP(100));
824 env(
fset(john, asfDepositAuth));
831 testcase(
"Payment failure with invalid credentials.");
835 env.
fund(
XRP(10000), issuer, alice, bob, maria);
846 std::string const credIdx = jv[jss::result][jss::index].asString();
855 env(
fset(bob, asfDepositAuth));
866 {{.issuer = issuer, .credType = credType},
867 {.issuer = issuer, .credType = credType}}),
876 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
879 env(
pay(alice, bob,
XRP(100)),
886 env(
pay(maria, bob,
XRP(100)),
893 char const credType2[] =
"fghij";
899 std::string const credIdx2 = jv[jss::result][jss::index].asString();
902 env(
pay(alice, bob,
XRP(100)),
921 char const credType[] =
"abcde";
922 Account const issuer{
"issuer"};
928 testcase(
"Creating / deleting with credentials.");
932 env.
fund(
XRP(5000), issuer, alice, bob);
945 jv[sfUnauthorize.jsonName] = issuer.
human();
952 jv[sfAuthorize.jsonName] = issuer.
human();
960 jv[sfUnauthorize.jsonName] = issuer.
human();
968 jv[sfAuthorize.jsonName] = issuer.
human();
981 auto& arr(jv[sfAuthorizeCredentials.jsonName]);
986 credParent[jss::Credential] = cred;
987 arr.
append(std::move(credParent));
1000 Account const a(
"a"), b(
"b"), c(
"c"), d(
"d"), e(
"e"), f(
"f"), g(
"g"), h(
"h"),
1002 auto const& z = credType;
1005 {{.issuer = a, .credType = z},
1006 {.issuer = b, .credType = z},
1007 {.issuer = c, .credType = z},
1008 {.issuer = d, .credType = z},
1009 {.issuer = e, .credType = z},
1010 {.issuer = f, .credType = z},
1011 {.issuer = g, .credType = z},
1012 {.issuer = h, .credType = z},
1013 {.issuer = i, .credType = z}});
1028 env.
fund(env.
current()->fees().accountReserve(0), john);
1049 jDP.isObject() && jDP.isMember(jss::result) &&
1050 !jDP[jss::result].isMember(jss::error) &&
1051 jDP[jss::result].isMember(jss::node) &&
1052 jDP[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1053 jDP[jss::result][jss::node][
"LedgerEntryType"] == jss::DepositPreauth);
1056 BEAST_EXPECT(jDP[jss::result][jss::node][jss::Account] == bob.
human());
1057 auto const&
credentials(jDP[jss::result][jss::node][
"AuthorizeCredentials"]);
1061 auto const& c(o[jss::Credential]);
1062 BEAST_EXPECT(c[jss::Issuer].asString() == issuer.
human());
1079 jDP.isObject() && jDP.isMember(jss::result) &&
1080 jDP[jss::result].isMember(jss::error) &&
1081 jDP[jss::result][jss::error] ==
"entryNotFound");
1089 using namespace jtx;
1090 char const credType[] =
"abcde";
1091 char const credType2[] =
"fghijkl";
1092 Account const issuer{
"issuer"};
1096 IOU const usd = gw[
"USD"];
1100 testcase(
"Payment failure with expired credentials.");
1104 env.
fund(
XRP(10000), issuer, alice, bob, gw);
1112 env.
current()->header().parentCloseTime.time_since_epoch().count() + 60;
1113 jv[sfExpiration.jsonName] = t;
1124 env.
current()->header().parentCloseTime.time_since_epoch().count() + 1000;
1125 jv[sfExpiration.jsonName] = t2;
1136 std::string const credIdx = jv[jss::result][jss::index].asString();
1138 std::string const credIdx2 = jv[jss::result][jss::index].asString();
1141 env(
fset(bob, asfDepositAuth));
1146 {{.issuer = issuer, .credType = credType},
1147 {.issuer = issuer, .credType = credType2}}));
1157 env(
pay(alice, bob,
XRP(100)),
1166 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1167 jDelCred[jss::result].isMember(jss::error) &&
1168 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1175 jle.isObject() && jle.isMember(jss::result) &&
1176 !jle[jss::result].isMember(jss::error) &&
1177 jle[jss::result].isMember(jss::node) &&
1178 jle[jss::result][jss::node].isMember(
"LedgerEntryType") &&
1179 jle[jss::result][jss::node][
"LedgerEntryType"] == jss::Credential &&
1180 jle[jss::result][jss::node][jss::Issuer] == issuer.
human() &&
1181 jle[jss::result][jss::node][jss::Subject] == alice.
human() &&
1182 jle[jss::result][jss::node][
"CredentialType"] ==
1193 env.
current()->header().parentCloseTime.time_since_epoch().count() + 40;
1194 jv[sfExpiration.jsonName] = t;
1201 std::string const credIdx = jv[jss::result][jss::index].asString();
1217 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1218 jDelCred[jss::result].isMember(jss::error) &&
1219 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1229 testcase(
"Escrow failure with expired credentials.");
1233 env.
fund(
XRP(5000), issuer, alice, bob, zelda);
1239 env.
current()->header().parentCloseTime.time_since_epoch().count() + 50;
1240 jv[sfExpiration.jsonName] = t;
1250 std::string const credIdx = jv[jss::result][jss::index].asString();
1253 env(
fset(bob, asfDepositAuth));
1259 auto const seq = env.
seq(alice);
1272 "0E0B04ED60588A758B67E21FBBE95AC5A63598BA951761DC0EC9C08D7E"
1292 jDelCred.isObject() && jDelCred.isMember(jss::result) &&
1293 jDelCred[jss::result].isMember(jss::error) &&
1294 jDelCred[jss::result][jss::error] ==
"entryNotFound");
1301 using namespace jtx;
1311 env.
fund(
XRP(5000), stock, alice, bob);
1314 {.issuer =
"a", .credType =
"a"},
1315 {.issuer =
"b", .credType =
"b"},
1316 {.issuer =
"c", .credType =
"c"},
1317 {.issuer =
"d", .credType =
"d"},
1318 {.issuer =
"e", .credType =
"e"},
1319 {.issuer =
"f", .credType =
"f"},
1320 {.issuer =
"g", .credType =
"g"},
1321 {.issuer =
"h", .credType =
"h"}};
1324 env.
fund(
XRP(5000), c.issuer);
1333 pubKey2Acc.
emplace(c.issuer.human(), c.issuer);
1336 for (
int i = 0; i < 10; ++i)
1343 auto const& authCred(dp[jss::result][jss::node][
"AuthorizeCredentials"]);
1344 BEAST_EXPECT(authCred.isArray() && authCred.size() ==
credentials.size());
1346 for (
auto const& o : authCred)
1348 auto const& c(o[jss::Credential]);
1349 auto issuer = c[jss::Issuer].asString();
1351 if (BEAST_EXPECT(pubKey2Acc.
contains(issuer)))
1354 pubKey2Acc.
at(issuer), c[
"CredentialType"].asString());
1371 for (
int i = 0; i < 10; ++i)
1378 testcase(
"Check duplicate credentials.");
1385 for (
auto const& c : copyCredentials)
1387 auto credentials2 = copyCredentials;
1403 env, alice, c.issuer, c.credType)[jss::result][jss::index]
1408 for (
auto const& h : credentialIDs)
1410 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).
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.
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 testSortingCredentials()
void testCredentialsCreation()
void testPayment(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.