1#include <test/app/lending/LoanTestBase.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/envconfig.h>
7#include <test/jtx/fee.h>
8#include <test/jtx/flags.h>
9#include <test/jtx/jtx_json.h>
10#include <test/jtx/mpt.h>
11#include <test/jtx/pay.h>
12#include <test/jtx/sponsor.h>
13#include <test/jtx/ter.h>
14#include <test/jtx/trust.h>
15#include <test/jtx/txflags.h>
17#include <xrpl/basics/Number.h>
18#include <xrpl/basics/base_uint.h>
19#include <xrpl/basics/strHex.h>
20#include <xrpl/beast/unit_test/suite.h>
21#include <xrpl/beast/utility/Journal.h>
22#include <xrpl/beast/utility/Zero.h>
23#include <xrpl/json/json_value.h>
24#include <xrpl/json/to_string.h>
25#include <xrpl/ledger/helpers/AccountRootHelpers.h>
26#include <xrpl/protocol/Feature.h>
27#include <xrpl/protocol/Indexes.h>
28#include <xrpl/protocol/Issue.h>
29#include <xrpl/protocol/Protocol.h>
30#include <xrpl/protocol/SField.h>
31#include <xrpl/protocol/STTx.h>
32#include <xrpl/protocol/SeqProxy.h>
33#include <xrpl/protocol/TER.h>
34#include <xrpl/protocol/TxFlags.h>
35#include <xrpl/protocol/jss.h>
36#include <xrpl/tx/transactors/lending/LoanSet.h>
56 Env env(*
this, features);
60 env.
fund(
XRP(10000), alice, bob);
64 using namespace std::chrono_literals;
83 env(manage(alice, loanKeylet.key, tfLoanImpair),
Ter(
temDISABLED));
87 failAll(
all_ - featureMPTokensV1);
88 failAll(
all_ - featureSingleAssetVault - featureLendingProtocol);
89 failAll(
all_ - featureSingleAssetVault);
90 failAll(
all_ - featureLendingProtocol);
100 using namespace loan;
101 Account const lender{
"lender"};
102 Account const issuer{
"issuer"};
103 Account const borrower{
"borrower"};
105 auto const iou = issuer[
"IOU"];
107 auto testWrapper = [&](
auto&&
test) {
110 env(
trust(lender, iou(10'000'000)));
111 env(
pay(issuer, lender, iou(5'000'000)));
115 auto const loanSetFee =
Fee(env.
current()->fees().base * 2);
116 Number const debtMaximumRequest = brokerInfo.
asset(1'000).value();
117 test(env, brokerInfo, loanSetFee, debtMaximumRequest);
121 testWrapper([&](
Env& env,
124 Number const& debtMaximumRequest) {
127 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
129 Sig(sfCounterpartySignature, lender),
138 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
139 Sig(sfCounterpartySignature, lender),
145 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
146 Sig(sfCounterpartySignature, lender),
152 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
153 Sig(sfCounterpartySignature, lender),
154 kPaymentInterval(120),
161 auto jv =
set(borrower,
uint256{}, debtMaximumRequest);
165 jv[sfLoanBrokerID] = id;
167 Sig(sfCounterpartySignature, lender),
184 JTx const tx = env.
jt(
185 set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
186 Sig(sfCounterpartySignature, lender),
189 auto counterpartySig = local.
getFieldObject(sfCounterpartySignature);
190 auto badPubKey = counterpartySig.
getFieldVL(sfSigningPubKey);
191 badPubKey[20] ^= 0xAA;
192 counterpartySig.setFieldVL(sfSigningPubKey, badPubKey);
196 auto res = env.
rpc(
"json",
"submit",
to_string(jvResult))[
"result"];
198 res[jss::error] ==
"invalidTransaction" &&
199 res[jss::error_exception] ==
200 "fails local checks: Counterparty: Invalid signature.");
204 testWrapper([&](
Env& env,
207 Number const& debtMaximumRequest) {
211 env(
fclear(issuer, asfDefaultRipple));
213 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
214 Sig(sfCounterpartySignature, lender),
220 testWrapper([&](
Env& env,
223 Number const& debtMaximumRequest) {
226 env(
pay(borrower, issuer, amt));
229 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
230 Sig(sfCounterpartySignature, lender),
235 env(
pay(issuer, borrower, amt));
236 env(
fset(issuer, asfGlobalFreeze));
239 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
240 Sig(sfCounterpartySignature, lender),
251 using namespace loan;
268 using namespace loan;
285 using namespace loan;
286 Account const lender{
"lender"};
287 Account const issuer{
"issuer"};
288 Account const borrower{
"borrower"};
289 auto const iou = issuer[
"IOU"];
293 env.
fund(
XRP(1'000), lender, issuer, borrower);
294 env(
trust(lender, iou(10'000'000)));
295 env(
pay(issuer, lender, iou(5'000'000)));
298 auto const loanSetFee =
Fee(env.
current()->fees().base * 2);
299 STAmount const debtMaximumRequest = brokerInfo.
asset(1'000).value();
301 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
302 Sig(sfCounterpartySignature, lender),
308 auto const loanKeylet =
311 env(
fset(issuer, asfGlobalFreeze));
315 env(
pay(borrower, loanKeylet.key, debtMaximumRequest),
Ter(
tecFROZEN));
318 env(
fclear(issuer, asfGlobalFreeze));
323 BEAST_EXPECT(brokerSle))
325 return Account{
"pseudo", brokerSle->at(sfAccount)};
334 env(
trust(issuer, lender[
"IOU"](1'000), lender, tfSetFreeze | tfSetDeepFreeze));
336 issuer, (*pseudoBroker)[
"IOU"](1'000), *pseudoBroker, tfSetFreeze | tfSetDeepFreeze));
340 env(
pay(borrower, loanKeylet.key, debtMaximumRequest),
Ter(
tecFROZEN));
344 env(
trust(issuer, lender[
"IOU"](1'000), tfClearFreeze | tfClearDeepFreeze));
350 env(
pay(borrower, loanKeylet.key, debtMaximumRequest, tfLoanLatePayment));
356 env(
pay(borrower, loanKeylet.key, debtMaximumRequest),
Ter(
tecKILLED));
362 testcase(
"Require Auth - Implicit Pseudo-account authorization");
364 using namespace loan;
365 Account const lender{
"lender"};
366 Account const issuer{
"issuer"};
367 Account const borrower{
"borrower"};
370 env.
fund(
XRP(100'000), issuer, lender, borrower);
376 .holders = {lender, borrower},
377 .flags =
kMptDexFlags | tfMPTRequireAuth | tfMPTCanClawback | tfMPTCanLock,
381 env(
pay(issuer, lender, asset(5'000'000)));
384 auto const loanSetFee =
Fee(env.
current()->fees().base * 2);
385 STAmount const debtMaximumRequest = brokerInfo.
asset(1'000).value();
387 auto forUnauthAuth = [&](
auto&& doTx) {
388 for (
auto const flag : {tfMPTUnauthorize, 0u})
390 asset.authorize({.account = issuer, .holder = borrower, .flags = flag});
400 env(
set(borrower, brokerInfo.
brokerID, debtMaximumRequest),
401 Sig(sfCounterpartySignature, lender),
407 auto const loanKeylet =
413 env(
pay(borrower, loanKeylet.key, debtMaximumRequest), err);
420 testcase(
"RIPD-4125 - overpayment");
424 Account const issuer(
"issuer");
425 Account const lender(
"lender");
426 Account const borrower(
"borrower");
429 .vaultDeposit = 100'000,
437 .principalRequest =
Number{200000, -6},
442 .flags = tfLoanOverpayment,
450 createLoan(env, assetType, brokerParams, loanParams, issuer, lender, borrower);
452 if (BEAST_EXPECT(loanResult); !loanResult.has_value())
455 auto broker = std::get<BrokerInfo>(*loanResult);
456 auto loanKeylet = std::get<Keylet>(*loanResult);
457 auto pseudoAcct = std::get<Account>(*loanResult);
459 VerifyLoanStatus const verifyLoanStatus(env, broker, pseudoAcct, loanKeylet);
466 STAmount{broker.asset, state.periodicPayment * 3 / 2 + 1},
471 .showStepBalances =
false,
472 .validateBalances =
true,
491 testcase <<
"Wrong Max Debt Behavior";
494 using namespace std::chrono_literals;
495 Env env(*
this, features);
497 Account const issuer{
"issuer"};
498 Account const lender{
"lender"};
509 BEAST_EXPECT(brokerSle))
511 BEAST_EXPECT(brokerSle->at(sfDebtMaximum) == 0);
514 using namespace loan;
516 auto const loanSetFee =
Fee(env.
current()->fees().base * 2);
517 Number const principalRequest{1, 3};
519 auto createJson = env.
json(
set(lender, broker.
brokerID, principalRequest),
Fee(loanSetFee));
522 counterpartyJson[sfTxnSignature] = createJson[sfTxnSignature];
523 counterpartyJson[sfSigningPubKey] = createJson[sfSigningPubKey];
524 if (!BEAST_EXPECT(!createJson.isMember(jss::Signers)))
525 counterpartyJson[sfSigners] = createJson[sfSigners];
527 createJson = env.
json(createJson,
Json(sfCounterpartySignature, counterpartyJson));
559 {fixCleanup3_1_3, fixCleanup3_2_0, featureMPTokensV2},
all_))
TestcaseT testcase
Memberspace for declaring test cases.
static constexpr std::uint32_t kDefaultGracePeriod
Number is a floating point type that can represent a wide range of values.
Blob getFieldVL(SField const &field) const
Serializer getSerializer() const
void setFieldObject(SField const &field, STObject const &v)
STObject getFieldObject(SField const &field) const
static constexpr SeqProxy rawSequence(std::uint32_t v)
Factory function to return a sequence-based SeqProxy.
Slice slice() const noexcept
BrokerInfo createVaultAndBroker(jtx::Env &env, jtx::PrettyAsset const &asset, jtx::Account const &lender, BrokerParameters const ¶ms=BrokerParameters::defaults())
LoanState getCurrentState(jtx::Env const &env, BrokerInfo const &broker, Keylet const &loanKeylet)
Get the state without checking anything.
void makeLoanPayments(jtx::Env &env, BrokerInfo const &broker, LoanParameters const &loanParams, Keylet const &loanKeylet, VerifyLoanStatus const &verifyLoanStatus, jtx::Account const &issuer, jtx::Account const &lender, jtx::Account const &borrower, PaymentParameters const &paymentParams=PaymentParameters::defaults())
std::optional< std::tuple< BrokerInfo, Keylet, jtx::Account > > createLoan(jtx::Env &env, AssetType assetType, BrokerParameters const &brokerParams, LoanParameters const &loanParams, jtx::Account const &issuer, jtx::Account const &lender, jtx::Account const &borrower)
void testInvalidLoanPay()
void testInvalidLoanDelete()
void runAmendmentSensitive(FeatureBitset features)
void run() override
Runs the suite.
void testWrongMaxDebtBehavior(FeatureBitset features)
void testInvalidLoanManage()
void testInvalidLoanSet(VaultKind vaultKind)
void runAmendmentIndependent()
Immutable cryptographic account descriptor.
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.
json::Value json(JsonValue &&jv, FN const &... fN)
Create JSON from parameters.
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.
JTx jt(JsonValue &&jv, FN const &... fN)
Create a JTx from parameters.
PrettyAmount balance(Account const &account) const
Returns the XRP balance on an account.
beast::Journal const journal
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Test helper for creating, mutating, and asserting MPT and confidential MPT ledger state.
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.
@ Object
object value (collection of name/value pairs).
Keylet computation functions.
Keylet loan(uint256 const &loanBrokerID, SeqProxy const &loanSeq) noexcept
Keylet loanBroker(AccountID const &owner, SeqProxy const &seq) noexcept
json::Value pay(AccountID const &account, uint256 const &loanID, STAmount const &amount, std::uint32_t flags)
json::Value pay(AccountID const &account, AccountID const &to, AnyAmount amount)
Create a payment.
std::vector< FeatureBitset > amendmentCombinations(std::initializer_list< uint256 > features, FeatureBitset seed)
Returns all 2^N permutations of a seed FeatureBitset with each subset of the given features excluded.
XrpT const XRP
Converts to XRP Issue or STAmount.
std::unique_ptr< Config > makeConfig(std::map< std::string, std::string > extraTxQ={}, std::map< std::string, std::string > extraVoting={})
json::Value fclear(Account const &account, std::uint32_t off)
Remove account flag.
std::array< Account, 1+sizeof...(Args)> noripple(Account const &account, Args const &... args)
Designate accounts as no-ripple in Env::fund.
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.
static STAmount accountReserve(jtx::Env &env, std::uint32_t count=1)
BEAST_DEFINE_TESTSUITE(AMMClawback, app, xrpl)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
constexpr FlagValue spfSponsorFee
bool set(T &target, std::string const &name, Section const §ion)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Issue const & xrpIssue()
Returns an asset specifier that represents XRP.
std::string strHex(FwdIt begin, FwdIt end)
static bool authorized(Port const &port, std::map< std::string, std::string > const &h)
TenthBips< std::uint32_t > TenthBips32
TenthBips< std::uint16_t > TenthBips16
std::string to_string(BaseUInt< Bits, Tag > const &a)
@ tecINSUFFICIENT_RESERVE
constexpr FlagValue spfSponsorReserve
constexpr FlagValue tfFullyCanonicalSig
Helper class to compare the expected state of a loan and loan broker against the data in the ledger.
Execution context for applying a JSON transaction.
std::shared_ptr< STTx const > stx