//------------------------------------------------------------------------------ /* 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 #include #include #include 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 const& toMatch) { std::uint16_t cnt = 0; std::uint16_t matched = 0; forEachItem( *env.current(), account, [&](std::shared_ptr 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 const& cancelAfter, std::optional 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 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 const& balance, std::optional const& amount, std::optional const& signature, std::optional 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