mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
523 lines
14 KiB
C++
523 lines
14 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2023 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <test/jtx/TestHelpers.h>
|
|
#include <test/jtx/offer.h>
|
|
#include <test/jtx/owners.h>
|
|
|
|
#include <xrpl/protocol/TxFlags.h>
|
|
|
|
namespace ripple {
|
|
namespace test {
|
|
namespace jtx {
|
|
|
|
// Functions used in debugging
|
|
Json::Value
|
|
getAccountOffers(Env& env, AccountID const& acct, bool current)
|
|
{
|
|
Json::Value jv;
|
|
jv[jss::account] = to_string(acct);
|
|
return env.rpc("json", "account_offers", to_string(jv))[jss::result];
|
|
}
|
|
|
|
Json::Value
|
|
getAccountLines(Env& env, AccountID const& acctId)
|
|
{
|
|
Json::Value jv;
|
|
jv[jss::account] = to_string(acctId);
|
|
return env.rpc("json", "account_lines", to_string(jv))[jss::result];
|
|
}
|
|
|
|
bool
|
|
checkArraySize(Json::Value const& val, unsigned int size)
|
|
{
|
|
return val.isArray() && val.size() == size;
|
|
}
|
|
|
|
std::uint32_t
|
|
ownerCount(Env const& env, Account const& account)
|
|
{
|
|
return env.ownerCount(account);
|
|
}
|
|
|
|
/* Path finding */
|
|
/******************************************************************************/
|
|
void
|
|
stpath_append_one(STPath& st, Account const& account)
|
|
{
|
|
st.push_back(STPathElement({account.id(), std::nullopt, std::nullopt}));
|
|
}
|
|
|
|
void
|
|
stpath_append_one(STPath& st, STPathElement const& pe)
|
|
{
|
|
st.push_back(pe);
|
|
}
|
|
|
|
bool
|
|
equal(STAmount const& sa1, STAmount const& sa2)
|
|
{
|
|
return sa1 == sa2 && sa1.issue().account == sa2.issue().account;
|
|
}
|
|
|
|
// Issue path element
|
|
STPathElement
|
|
IPE(Issue const& iss)
|
|
{
|
|
return STPathElement(
|
|
STPathElement::typeCurrency | STPathElement::typeIssuer,
|
|
xrpAccount(),
|
|
iss.currency,
|
|
iss.account);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
XRPAmount
|
|
txfee(Env const& env, std::uint16_t n)
|
|
{
|
|
return env.current()->fees().base * n;
|
|
}
|
|
|
|
PrettyAmount
|
|
xrpMinusFee(Env const& env, std::int64_t xrpAmount)
|
|
{
|
|
auto feeDrops = env.current()->fees().base;
|
|
return drops(dropsPerXRP * xrpAmount - feeDrops);
|
|
};
|
|
|
|
[[nodiscard]] bool
|
|
expectHolding(
|
|
Env& env,
|
|
AccountID const& account,
|
|
STAmount const& value,
|
|
bool defaultLimits)
|
|
{
|
|
if (auto const sle = env.le(keylet::line(account, value.issue())))
|
|
{
|
|
Issue const issue = value.issue();
|
|
bool const accountLow = account < issue.account;
|
|
|
|
bool expectDefaultTrustLine = true;
|
|
if (defaultLimits)
|
|
{
|
|
STAmount low{issue};
|
|
STAmount high{issue};
|
|
|
|
low.setIssuer(accountLow ? account : issue.account);
|
|
high.setIssuer(accountLow ? issue.account : account);
|
|
|
|
expectDefaultTrustLine = sle->getFieldAmount(sfLowLimit) == low &&
|
|
sle->getFieldAmount(sfHighLimit) == high;
|
|
}
|
|
|
|
auto amount = sle->getFieldAmount(sfBalance);
|
|
amount.setIssuer(value.issue().account);
|
|
if (!accountLow)
|
|
amount.negate();
|
|
return amount == value && expectDefaultTrustLine;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
expectHolding(
|
|
Env& env,
|
|
AccountID const& account,
|
|
None const&,
|
|
Issue const& issue)
|
|
{
|
|
return !env.le(keylet::line(account, issue));
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
expectHolding(
|
|
Env& env,
|
|
AccountID const& account,
|
|
None const&,
|
|
MPTIssue const& mptIssue)
|
|
{
|
|
return !env.le(keylet::mptoken(mptIssue.getMptID(), account));
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
expectHolding(Env& env, AccountID const& account, None const& value)
|
|
{
|
|
return std::visit(
|
|
[&](auto const& issue) {
|
|
return expectHolding(env, account, value, issue);
|
|
},
|
|
value.asset.value());
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
expectOffers(
|
|
Env& env,
|
|
AccountID const& account,
|
|
std::uint16_t size,
|
|
std::vector<Amounts> const& toMatch)
|
|
{
|
|
std::uint16_t cnt = 0;
|
|
std::uint16_t matched = 0;
|
|
forEachItem(
|
|
*env.current(), account, [&](std::shared_ptr<SLE const> const& sle) {
|
|
if (!sle)
|
|
return false;
|
|
if (sle->getType() == ltOFFER)
|
|
{
|
|
++cnt;
|
|
if (std::find_if(
|
|
toMatch.begin(), toMatch.end(), [&](auto const& a) {
|
|
return a.in == sle->getFieldAmount(sfTakerPays) &&
|
|
a.out == sle->getFieldAmount(sfTakerGets);
|
|
}) != toMatch.end())
|
|
++matched;
|
|
}
|
|
return true;
|
|
});
|
|
return size == cnt && matched == toMatch.size();
|
|
}
|
|
|
|
Json::Value
|
|
ledgerEntryRoot(Env& env, Account const& acct)
|
|
{
|
|
Json::Value jvParams;
|
|
jvParams[jss::ledger_index] = "current";
|
|
jvParams[jss::account_root] = acct.human();
|
|
return env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
|
}
|
|
|
|
Json::Value
|
|
ledgerEntryState(
|
|
Env& env,
|
|
Account const& acct_a,
|
|
Account const& acct_b,
|
|
std::string const& currency)
|
|
{
|
|
Json::Value jvParams;
|
|
jvParams[jss::ledger_index] = "current";
|
|
jvParams[jss::ripple_state][jss::currency] = currency;
|
|
jvParams[jss::ripple_state][jss::accounts] = Json::arrayValue;
|
|
jvParams[jss::ripple_state][jss::accounts].append(acct_a.human());
|
|
jvParams[jss::ripple_state][jss::accounts].append(acct_b.human());
|
|
return env.rpc("json", "ledger_entry", to_string(jvParams))[jss::result];
|
|
}
|
|
|
|
Json::Value
|
|
accountBalance(Env& env, Account const& acct)
|
|
{
|
|
auto const jrr = ledgerEntryRoot(env, acct);
|
|
return jrr[jss::node][sfBalance.fieldName];
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
expectLedgerEntryRoot(
|
|
Env& env,
|
|
Account const& acct,
|
|
STAmount const& expectedValue)
|
|
{
|
|
return accountBalance(env, acct) == to_string(expectedValue.xrp());
|
|
}
|
|
|
|
/* Payment Channel */
|
|
/******************************************************************************/
|
|
Json::Value
|
|
create(
|
|
AccountID const& account,
|
|
AccountID const& to,
|
|
STAmount const& amount,
|
|
NetClock::duration const& settleDelay,
|
|
PublicKey const& pk,
|
|
std::optional<NetClock::time_point> const& cancelAfter,
|
|
std::optional<std::uint32_t> const& dstTag)
|
|
{
|
|
Json::Value jv;
|
|
jv[jss::TransactionType] = jss::PaymentChannelCreate;
|
|
jv[jss::Account] = to_string(account);
|
|
jv[jss::Destination] = to_string(to);
|
|
jv[jss::Amount] = amount.getJson(JsonOptions::none);
|
|
jv[jss::SettleDelay] = settleDelay.count();
|
|
jv[sfPublicKey.fieldName] = strHex(pk.slice());
|
|
if (cancelAfter)
|
|
jv[sfCancelAfter.fieldName] = cancelAfter->time_since_epoch().count();
|
|
if (dstTag)
|
|
jv[sfDestinationTag.fieldName] = *dstTag;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
fund(
|
|
AccountID const& account,
|
|
uint256 const& channel,
|
|
STAmount const& amount,
|
|
std::optional<NetClock::time_point> const& expiration)
|
|
{
|
|
Json::Value jv;
|
|
jv[jss::TransactionType] = jss::PaymentChannelFund;
|
|
jv[jss::Account] = to_string(account);
|
|
jv[sfChannel.fieldName] = to_string(channel);
|
|
jv[jss::Amount] = amount.getJson(JsonOptions::none);
|
|
if (expiration)
|
|
jv[sfExpiration.fieldName] = expiration->time_since_epoch().count();
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
claim(
|
|
AccountID const& account,
|
|
uint256 const& channel,
|
|
std::optional<STAmount> const& balance,
|
|
std::optional<STAmount> const& amount,
|
|
std::optional<Slice> const& signature,
|
|
std::optional<PublicKey> const& pk)
|
|
{
|
|
Json::Value jv;
|
|
jv[jss::TransactionType] = jss::PaymentChannelClaim;
|
|
jv[jss::Account] = to_string(account);
|
|
jv["Channel"] = to_string(channel);
|
|
if (amount)
|
|
jv[jss::Amount] = amount->getJson(JsonOptions::none);
|
|
if (balance)
|
|
jv["Balance"] = balance->getJson(JsonOptions::none);
|
|
if (signature)
|
|
jv["Signature"] = strHex(*signature);
|
|
if (pk)
|
|
jv["PublicKey"] = strHex(pk->slice());
|
|
return jv;
|
|
}
|
|
|
|
uint256
|
|
channel(
|
|
AccountID const& account,
|
|
AccountID const& dst,
|
|
std::uint32_t seqProxyValue)
|
|
{
|
|
auto const k = keylet::payChan(account, dst, seqProxyValue);
|
|
return k.key;
|
|
}
|
|
|
|
STAmount
|
|
channelBalance(ReadView const& view, uint256 const& chan)
|
|
{
|
|
auto const slep = view.read({ltPAYCHAN, chan});
|
|
if (!slep)
|
|
return XRPAmount{-1};
|
|
return (*slep)[sfBalance];
|
|
}
|
|
|
|
bool
|
|
channelExists(ReadView const& view, uint256 const& chan)
|
|
{
|
|
auto const slep = view.read({ltPAYCHAN, chan});
|
|
return bool(slep);
|
|
}
|
|
|
|
/* Crossing Limits */
|
|
/******************************************************************************/
|
|
|
|
void
|
|
n_offers(
|
|
Env& env,
|
|
std::size_t n,
|
|
Account const& account,
|
|
STAmount const& in,
|
|
STAmount const& out)
|
|
{
|
|
auto const ownerCount = env.le(account)->getFieldU32(sfOwnerCount);
|
|
for (std::size_t i = 0; i < n; i++)
|
|
{
|
|
env(offer(account, in, out));
|
|
env.close();
|
|
}
|
|
env.require(owners(account, ownerCount + n));
|
|
}
|
|
|
|
/* Pay Strand */
|
|
/***************************************************************/
|
|
|
|
// Currency path element
|
|
STPathElement
|
|
cpe(Currency const& c)
|
|
{
|
|
return STPathElement(
|
|
STPathElement::typeCurrency, xrpAccount(), c, xrpAccount());
|
|
};
|
|
|
|
// All path element
|
|
STPathElement
|
|
allpe(AccountID const& a, Issue const& iss)
|
|
{
|
|
return STPathElement(
|
|
STPathElement::typeAccount | STPathElement::typeCurrency |
|
|
STPathElement::typeIssuer,
|
|
a,
|
|
iss.currency,
|
|
iss.account);
|
|
};
|
|
|
|
/* LoanBroker */
|
|
/******************************************************************************/
|
|
|
|
namespace loanBroker {
|
|
|
|
Json::Value
|
|
set(AccountID const& account, uint256 const& vaultId, uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanBrokerSet;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfVaultID] = to_string(vaultId);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
del(AccountID const& account, uint256 const& loanBrokerID, uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanBrokerDelete;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanBrokerID] = to_string(loanBrokerID);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
coverDeposit(
|
|
AccountID const& account,
|
|
uint256 const& loanBrokerID,
|
|
STAmount const& amount,
|
|
uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanBrokerCoverDeposit;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanBrokerID] = to_string(loanBrokerID);
|
|
jv[sfAmount] = amount.getJson(JsonOptions::none);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
coverWithdraw(
|
|
AccountID const& account,
|
|
uint256 const& loanBrokerID,
|
|
STAmount const& amount,
|
|
uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanBrokerCoverWithdraw;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanBrokerID] = to_string(loanBrokerID);
|
|
jv[sfAmount] = amount.getJson(JsonOptions::none);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
coverClawback(AccountID const& account, std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanBrokerCoverClawback;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
} // namespace loanBroker
|
|
|
|
/* Loan */
|
|
/******************************************************************************/
|
|
namespace loan {
|
|
|
|
Json::Value
|
|
set(AccountID const& account,
|
|
uint256 const& loanBrokerID,
|
|
Number principalRequested,
|
|
NetClock::time_point const& startDate,
|
|
std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanSet;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanBrokerID] = to_string(loanBrokerID);
|
|
jv[sfPrincipalRequested] = to_string(principalRequested);
|
|
jv[sfFlags] = flags;
|
|
jv[sfStartDate] = startDate.time_since_epoch().count();
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
manage(AccountID const& account, uint256 const& loanID, std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanManage;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanID] = to_string(loanID);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
del(AccountID const& account, uint256 const& loanID, std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanDelete;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanID] = to_string(loanID);
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
draw(
|
|
AccountID const& account,
|
|
uint256 const& loanID,
|
|
STAmount const& amount,
|
|
std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanDraw;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanID] = to_string(loanID);
|
|
jv[sfAmount] = amount.getJson();
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
Json::Value
|
|
pay(AccountID const& account,
|
|
uint256 const& loanID,
|
|
STAmount const& amount,
|
|
std::uint32_t flags)
|
|
{
|
|
Json::Value jv;
|
|
jv[sfTransactionType] = jss::LoanPay;
|
|
jv[sfAccount] = to_string(account);
|
|
jv[sfLoanID] = to_string(loanID);
|
|
jv[sfAmount] = amount.getJson();
|
|
jv[sfFlags] = flags;
|
|
return jv;
|
|
}
|
|
|
|
} // namespace loan
|
|
} // namespace jtx
|
|
} // namespace test
|
|
} // namespace ripple
|