1#include <test/jtx/AMM.h>
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/delegate.h>
7#include <test/jtx/deposit.h>
8#include <test/jtx/fee.h>
9#include <test/jtx/flags.h>
10#include <test/jtx/multisign.h>
11#include <test/jtx/noop.h>
12#include <test/jtx/offer.h>
13#include <test/jtx/pay.h>
14#include <test/jtx/proposal.h>
15#include <test/jtx/sig.h>
16#include <test/jtx/sponsor.h>
17#include <test/jtx/ter.h>
18#include <test/jtx/ticket.h>
19#include <test/jtx/token.h>
20#include <test/jtx/trust.h>
22#include <xrpl/basics/strHex.h>
23#include <xrpl/beast/unit_test/suite.h>
24#include <xrpl/beast/utility/Journal.h>
25#include <xrpl/json/json_value.h>
26#include <xrpl/ledger/OpenView.h>
27#include <xrpl/protocol/AccountID.h>
28#include <xrpl/protocol/Feature.h>
29#include <xrpl/protocol/Indexes.h>
30#include <xrpl/protocol/Keylet.h>
31#include <xrpl/protocol/SField.h>
32#include <xrpl/protocol/STAmount.h>
33#include <xrpl/protocol/STArray.h>
34#include <xrpl/protocol/STLedgerEntry.h>
35#include <xrpl/protocol/STObject.h>
36#include <xrpl/protocol/SeqProxy.h>
37#include <xrpl/protocol/TER.h>
38#include <xrpl/protocol/TxFlags.h>
39#include <xrpl/protocol/jss.h>
72 using namespace std::chrono_literals;
74 Env env{*
this, features - featureCosign};
78 env.
fund(
XRP(10000), target, bob);
103 testcase(
"reject payload that must not be stored");
106 using namespace std::chrono_literals;
108 Env env{*
this, features};
110 Account const target{
"target"};
112 env.
fund(
XRP(10000), target, bob);
120 auto payload = [&]() {
139 tx[jss::TransactionType] = 65535;
149 tx[jss::TransactionType] = jss::EnableAmendment;
169 tx[jss::TransactionType] =
"TransactionProposalCreate";
199 tx[jss::Sequence] = 1;
233 tx[jss::Amount] =
"0";
243 tx[sfMemos.jsonName][0u][sfMemo.jsonName][sfMemoData.jsonName] =
280 testcase(
"reject payload carrying a signature");
283 using namespace std::chrono_literals;
285 Env env{*
this, features};
287 Account const target{
"target"};
289 env.
fund(
XRP(10000), target, bob);
305 auto payment = [&]() {
308 auto sponsoredPayment = [&]() {
313 tx[sfSponsor.getJsonName()] = bob.
human();
317 auto loanSet = [&]() {
319 tx[sfCounterparty.getJsonName()] = bob.
human();
322 auto batchTx = [&]() {
354 {.apply = [&](
json::Value& o) { o[jss::SigningPubKey] = key; }, .signs =
false},
355 {.apply = [&](
json::Value& o) { o[sfTxnSignature.getJsonName()] = sig; },
359 o[jss::SigningPubKey] =
"";
360 o[sfTxnSignature.getJsonName()] = sig;
367 o[jss::SigningPubKey] = key;
368 o[sfTxnSignature.getJsonName()] = sig;
377 o[jss::SigningPubKey] =
"";
378 auto& signer = o[sfSigners.getJsonName()][0u][sfSigner.getJsonName()];
379 signer[jss::Account] = bob.
human();
380 signer[jss::SigningPubKey] = key;
381 signer[sfTxnSignature.getJsonName()] = sig;
386 o[jss::SigningPubKey] =
"";
387 auto& signer = o[sfSigners.getJsonName()][0u][sfSigner.getJsonName()];
388 signer[jss::Account] = bob.
human();
389 signer[jss::SigningPubKey] =
"";
390 signer[sfTxnSignature.getJsonName()] = sig;
408 bool checksSignatureContent;
414 .checksSignatureContent =
true},
417 auto& o = tx[sfCounterpartySignature.getJsonName()];
421 .checksSignatureContent =
true},
422 {.payload = sponsoredPayment,
424 auto& o = tx[sfSponsorSignature.getJsonName()];
428 .checksSignatureContent =
false},
434 auto& o = tx[sfBatchSigners.getJsonName()][0u][sfBatchSigner.getJsonName()];
435 o[jss::Account] = bob.
human();
438 .checksSignatureContent =
false},
441 for (
auto const& place : places)
443 for (
auto const& fill : fills)
446 fill.apply(place.at(tx));
478 tx[sfTxnSignature.getJsonName()] = sig;
488 testcase(
"reject proposal that cannot be completed");
491 using namespace std::chrono_literals;
493 Env env{*
this, features};
495 Account const target{
"target"};
498 env.
fund(
XRP(10000), target, bob);
526 tx[sfLastLedgerSequence.getJsonName()] = env.
current()->seq() - 1;
540 tx[sfLastLedgerSequence.getJsonName()] = env.
current()->seq();
605 testcase(
"reject proposal from an unauthorized proposer");
608 using namespace std::chrono_literals;
610 Env env{*
this, features};
612 Account const target{
"target"};
613 Account const signer{
"signer"};
614 Account const stranger{
"stranger"};
616 env.
fund(
XRP(10000), target, signer, stranger, bob);
619 env(
signers(target, 1, {{signer, 1}}));
682 env.
fund(
XRP(10000), s1, s2, s3, s4, s5);
685 env(
signers(target, 3, {{s1, 1}, {s2, 1}, {s3, 1}, {s4, 1}, {s5, 1}}));
688 for (
Account const& s : {s1, s2, s3, s4, s5})
734 testcase(
"unparseable on-ledger SignerList is tefBAD_LEDGER");
737 using namespace std::chrono_literals;
740 env.
fund(
XRP(10000), target, signer);
742 env(
signers(target, 1, {{signer, 1}}));
749 auto proposeAsSigner =
761 Env env{*
this, features};
762 Account const target{
"targetMissing"};
763 Account const signer{
"signerMissing"};
768 auto const sle = view.read(signerListKeylet);
771 auto replacement = std::make_shared<SLE>(*sle);
772 if (!replacement->delField(sfSignerEntries))
774 view.rawReplace(replacement);
777 BEAST_EXPECT(env.
le(signerListKeylet));
779 proposeAsSigner(env, target, signer, ticketSeq);
783 Env env{*
this, features};
784 Account const target{
"targetBadEntry"};
785 Account const signer{
"signerBadEntry"};
790 auto const sle = view.read(signerListKeylet);
793 auto replacement = std::make_shared<SLE>(*sle);
795 badEntries.pushBack(STObject{sfSigner});
796 replacement->setFieldArray(sfSignerEntries, badEntries);
800 BEAST_EXPECT(env.
le(signerListKeylet));
802 proposeAsSigner(env, target, signer, ticketSeq);
806 Env env{*
this, features};
807 Account const target{
"targetMissingAccount"};
808 Account const signer{
"signerMissingAccount"};
809 std::uint32_t
const ticketSeq = setup(env, target, signer);
813 auto const sle = view.read(signerListKeylet);
816 auto replacement = std::make_shared<SLE>(*sle);
821 badEntries.pushBack(STObject{sfSignerEntry});
822 replacement->setFieldArray(sfSignerEntries, badEntries);
823 view.rawReplace(replacement);
826 BEAST_EXPECT(env.
le(signerListKeylet));
828 proposeAsSigner(env, target, signer, ticketSeq);
842 testcase(
"proposer authorized through a delegated proposed txn");
845 using namespace std::chrono_literals;
847 Env env{*
this, features};
849 Account const target{
"target"};
850 Account const delegateAcct{
"delegateAcct"};
853 Account const stranger{
"stranger"};
855 env.
fund(
XRP(10000), target, delegateAcct, ds1, ds2, stranger, bob);
860 tx[sfDelegate.jsonName] = delegateAcct.
human();
892 env(
signers(delegateAcct, 1, {{ds1, 1}, {ds2, 1}}));
922 testcase(
"proposer authorized through granular delegate permission");
925 using namespace std::chrono_literals;
927 Env env{*
this, features};
932 env.
fund(
XRP(10000), gw, alice, bob);
933 env(
fset(gw, asfRequireAuth));
936 env(
trust(alice, gw[
"USD"](50)));
943 tx[sfDelegate.jsonName] = bob.
human();
964 delegatedTrustSet(ticketSeq, tfSetFreeze),
977 tx[sfDelegate.jsonName] = bob.
human();
978 tx[sfQualityOut.jsonName] = 100;
999 testcase(
"unparseable delegate SignerList is tefBAD_LEDGER");
1001 using namespace jtx;
1002 using namespace std::chrono_literals;
1004 Env env{*
this, features};
1006 Account const target{
"target"};
1007 Account const delegateAcct{
"delegateAcct"};
1010 env.
fund(
XRP(10000), target, delegateAcct, ds1, bob);
1019 env(
signers(delegateAcct, 1, {{ds1, 1}}));
1030 auto const sle = view.read(delegateSignerListKeylet);
1033 auto replacement = std::make_shared<SLE>(*sle);
1038 badEntries.pushBack(STObject{sfSignerEntry});
1039 replacement->setFieldArray(sfSignerEntries, badEntries);
1043 BEAST_EXPECT(env.le(delegateSignerListKeylet));
1046 tx[sfDelegate.jsonName] = delegateAcct.human();
1062 testcase(
"reject proposal targeting a pseudo-account");
1064 using namespace jtx;
1065 using namespace std::chrono_literals;
1067 Env env{*
this, features};
1069 Account const proposer{
"proposer"};
1074 auto const USD = gw[
"USD"];
1075 env.
fund(
XRP(10000), proposer, alice, gw, bob);
1077 env.
trust(USD(1'000'000), alice);
1079 env(
pay(gw, alice, USD(10'000)));
1099 testcase(
"create proposal object");
1101 using namespace jtx;
1102 using namespace std::chrono_literals;
1104 Env env{*
this, features};
1106 Account const target{
"target"};
1108 env.
fund(
XRP(10000), target, bob);
1125 if (!BEAST_EXPECT(sle))
1128 BEAST_EXPECT(sle->getAccountID(sfOwner) == target.
id());
1129 BEAST_EXPECT(sle->getFieldU32(sfExpiration) == expiration);
1131 auto const stored = sle->getFieldObject(sfProposedTransaction);
1132 BEAST_EXPECT(stored.getAccountID(sfAccount) == target.
id());
1133 BEAST_EXPECT(stored.getFieldU32(sfSequence) == 0);
1134 BEAST_EXPECT(stored.getFieldU32(sfTicketSequence) == targetTicketSeq);
1135 BEAST_EXPECT(stored.getFieldVL(sfSigningPubKey).empty());
1151 testcase(
"proposals for other transaction types");
1153 using namespace jtx;
1154 using namespace std::chrono_literals;
1156 Env env{*
this, features};
1158 Account const target{
"target"};
1162 auto const USD = gw[
"USD"];
1163 env.
fund(
XRP(10000), target, bob, gw);
1171 trust(target, USD(1000)),
1172 signers(target, 1, {{bob, 1}}),
1206 testcase(
"proposal for a transaction type with an auxiliary co-signature");
1208 using namespace jtx;
1209 using namespace std::chrono_literals;
1211 Env env{*
this, features};
1213 Account const borrower{
"borrower"};
1216 env.
fund(
XRP(10000), borrower, bob);
1239 tx[sfSponsor.getJsonName()] = bob.
human();
1255 using namespace jtx;
1256 using namespace std::chrono_literals;
1258 Env env{*
this, features};
1261 Account const target{
"target"};
1263 env.
fund(
XRP(10000), target, bob);
1284 env(
pay(bob, alice,
XRP(10)));
1298 testcase(
"proposer reserve sponsored");
1300 using namespace jtx;
1301 using namespace std::chrono_literals;
1308 Env env{*
this, features - featureSponsor};
1311 Account const target{
"target"};
1313 Account const backer{
"backer"};
1314 env.
fund(
XRP(10000), alice, target, bob, backer);
1324 Sig(sfSponsorSignature, backer),
1331 Env env{*
this, features};
1334 Account const target{
"target"};
1336 Account const backer{
"backer"};
1338 env.
fund(
XRP(10000), alice, target, bob, backer);
1348 Sig(sfSponsorSignature, backer),
1353 if (!BEAST_EXPECT(sle))
1356 BEAST_EXPECT(sle->isFieldPresent(sfSponsor));
1357 BEAST_EXPECT(sle->getAccountID(sfSponsor) == backer.
id());
1375 testcase(
"proposer reserve sponsorship transferred");
1377 using namespace jtx;
1378 using namespace std::chrono_literals;
1380 Env env{*
this, features};
1383 Account const target{
"target"};
1385 Account const backer1{
"backer1"};
1386 Account const backer2{
"backer2"};
1388 env.
fund(
XRP(10000), alice, target, bob, backer1, backer2);
1398 Sig(sfSponsorSignature, backer1),
1409 Sig(sfSponsorSignature, backer2));
1413 if (!BEAST_EXPECT(sle))
1416 BEAST_EXPECT(sle->isFieldPresent(sfSponsor));
1417 BEAST_EXPECT(sle->getAccountID(sfSponsor) == backer2.
id());
1434 testcase(
"proposal creation fee sponsored");
1436 using namespace jtx;
1437 using namespace std::chrono_literals;
1439 Env env{*
this, features};
1441 Account const target{
"target"};
1443 Account const backer{
"backer"};
1445 env.
fund(
XRP(10000), target, bob, backer);
1452 auto const targetBalance = env.
balance(target);
1453 auto const backerBalance = env.
balance(backer);
1463 Sig(sfSponsorSignature, backer),
1468 BEAST_EXPECT(env.
balance(target) == targetBalance);
1469 BEAST_EXPECT(env.
balance(backer) == backerBalance - feeAmt);
1477 testcase(
"proposed batch reserve");
1479 using namespace jtx;
1480 using namespace std::chrono_literals;
1482 Env env{*
this, features};
1484 Account const target{
"target"};
1486 env.
fund(
XRP(10000), target, bob);
1517 testcase(
"proposed multi-account batch");
1519 using namespace jtx;
1520 using namespace std::chrono_literals;
1522 Env env{*
this, features};
1524 Account const target{
"target"};
1526 env.
fund(
XRP(10000), target, bob);
1546 if (!BEAST_EXPECT(sle))
1551 auto const stored = sle->getFieldObject(sfProposedTransaction);
1552 BEAST_EXPECT(!stored.isFieldPresent(sfBatchSigners));
1565 testcase(
"proposed batch inner account-reserve SponsorshipTransfer");
1567 using namespace jtx;
1568 using namespace std::chrono_literals;
1570 Env env{*
this, features};
1572 Account const target{
"target"};
1574 env.
fund(
XRP(10000), target, bob);
1577 auto unsignedInnerSponsorship = [&](
Account const& account) {
1579 tx[sfSponsor.getJsonName()] = bob.
human();
1589 auto const seq = env.
seq(target);
1609 auto const seq = env.
seq(target);
1629 using namespace jtx;
A generic endpoint for log messages.
TestcaseT testcase
Memberspace for declaring test cases.
Value removeMember(char const *key)
Remove and return the named member.
bool modify(ModifyType const &f)
Modify the open ledger.
Writable ledger view that accumulates state and tx changes.
void rawReplace(SLE::Ref sle) override
Unconditionally replace a state item.
Slice slice() const noexcept
static constexpr SeqProxy rawTicket(std::uint32_t v)
Factory function to return a ticket-based SeqProxy.
virtual OpenLedger & getOpenLedger()=0
Convenience class to test AMM functionality.
Immutable cryptographic account descriptor.
std::string const & human() const
Returns the human readable public key.
PublicKey const & pk() const
Return the public key.
AccountID id() const
Returns the Account ID.
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.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
void trust(STAmount const &amount, Account const &account)
Establish trust lines.
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
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.
@ Object
object value (collection of name/value pairs).
Keylet signerList(AccountID const &account) noexcept
A SignerList.
Keylet txProposal(AccountID const &target, std::uint32_t ticketSequence) noexcept
A TransactionProposal.
Keylet ticket(AccountID const &id, SeqProxy const &ticketSeq)
A ticket belonging to an account.
json::Value set(jtx::Account const &account, jtx::Account const &authorize, std::vector< std::string > const &permissions)
json::Value auth(Account const &account, Account const &auth)
Preauthorize for deposit.
json::Value set(AccountID const &account, UInt256 const &loanBrokerID, Number principalRequested, std::uint32_t flags)
Create create()
Check the ledger effects a TransactionProposalCreate must have, whatever its outcome: on tesSUCCESS a...
json::Value innerTx(json::Value tx, std::uint32_t seq)
Put a transaction into the form an inner transaction of a proposed Batch takes, as batch::Inner does ...
std::uint32_t createTicket(Env &env, Account const &account, std::uint32_t count)
Create tickets for a proposal to be built against, and close the ledger.
json::Value unsignedPayload(Env const &env, json::Value tx, std::uint32_t ticketSeq)
Put a transaction of any type into the form a proposal stores it in: unsigned and ticket-based,...
constexpr std::uint32_t kBatchProposalOwnerCount
Owner-reserve increments held by a proposal of a Batch transaction.
constexpr std::uint32_t kProposalOwnerCount
Owner-reserve increments held by a proposal of an ordinary transaction.
SLE::const_pointer entry(Env const &env, AccountID const &target, std::uint32_t ticketSeq)
The proposal stored against a target account's ticket.
json::Value create(Account const &proposer, json::Value const &proposedTx, std::uint32_t expiration)
Build a TransactionProposalCreate carrying an unsigned proposed transaction.
void authorizeProposer(Env &env, Account const &target, Account const &proposer)
Give proposer a place on target's SignerList, so it may create proposals against target.
json::Value unsignedBatch(Env const &env, Account const &target, std::uint32_t ticketSeq, std::uint32_t flags, std::vector< json::Value > const &inners, std::optional< std::uint32_t > numSigners)
An unsigned outer Batch payload holding inners.
std::uint32_t expiration(Env &env, NetClock::duration delta)
An absolute expiration delta past the environment's current time.
json::Value mint(jtx::Account const &account, std::uint32_t nfTokenTaxon)
Mint an NFToken.
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)
std::uint32_t sponsoringOwnerCount(Env const &env, Account const &account)
FeatureBitset testableAmendments()
json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
std::uint32_t sponsoredOwnerCount(Env const &env, Account const &account)
json::Value trust(Account const &account, STAmount const &amount, std::uint32_t flags)
Modify a trust line.
PrettyAmount drops(Integer i)
Returns an XRP PrettyAmount, which is trivially convertible to STAmount.
json::Value signers(Account const &account, std::uint32_t quorum, std::vector< Signer > const &v)
json::Value fset(Account const &account, std::uint32_t on, std::uint32_t off=0)
Add and/or remove flag.
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr FlagValue spfSponsorFee
constexpr FlagValue tfInnerBatchTxn
ApplyResult apply(ServiceRegistry ®istry, OpenView &view, STTx const &tx, ApplyFlags flags, beast::Journal journal)
Apply a transaction to an OpenView.
std::string strHex(FwdIt begin, FwdIt end)
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
TERSubset< CanCvtToTER > TER
@ tecINSUFFICIENT_RESERVE
constexpr FlagValue spfSponsorReserve
A pair of SHAMap key and LedgerEntryType.
void testProposerAuthorization(FeatureBitset features)
void testPseudoTarget(FeatureBitset features)
void testCorruptDelegateSignerList(FeatureBitset features)
void testProposedBatchInnerSponsorship(FeatureBitset features)
void testRejectedSignatureFields(FeatureBitset features)
void testSponsorshipTransfer(FeatureBitset features)
void testReserve(FeatureBitset features)
void testCorruptSignerList(FeatureBitset features)
void testMultiAccountBatch(FeatureBitset features)
void testPreclaim(FeatureBitset features)
void testRejectedPayload(FeatureBitset features)
void testFeeSponsored(FeatureBitset features)
void testSponsoredReserve(FeatureBitset features)
void testDelegatedProposedTx(FeatureBitset features)
void testDelegatedGranularProposedTx(FeatureBitset features)
void testAuxiliaryCoSignatureTypes(FeatureBitset features)
void testCreate(FeatureBitset features)
void testDisabled(FeatureBitset features)
void run() override
Runs the suite.
void testOtherTransactionTypes(FeatureBitset features)
void testBatchReserve(FeatureBitset features)