mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 09:17:57 +00:00
New rules for payment paths:
* Sanity check on newly created strands * Better loop detection * Better tests (test every combination of path element pairs) * Disallow any root issuer (even for xrp) * Disallow compount element typs in path * Issue was not reset when currency was XRP * Add amendment
This commit is contained in:
@@ -4229,6 +4229,10 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\test\app\PayStrand_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\test\app\Regression_test.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
|
||||
@@ -5010,6 +5010,9 @@
|
||||
<ClCompile Include="..\..\src\test\app\PayChan_test.cpp">
|
||||
<Filter>test\app</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\test\app\PayStrand_test.cpp">
|
||||
<Filter>test\app</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\test\app\Regression_test.cpp">
|
||||
<Filter>test\app</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -51,7 +51,8 @@ supportedAmendments ()
|
||||
{ "532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize" },
|
||||
{ "E2E6F2866106419B88C50045ACE96368558C345566AC8F2BDF5A5B5587F0E6FA fix1368" },
|
||||
{ "07D43DCE529B15A10827E5E04943B496762F9A88E3268269D69C44BE49E21104 Escrow" },
|
||||
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" }
|
||||
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" },
|
||||
{ "48C4451D6C6A138453F056EB6793AFF4B5C57457A37BA63EF3541FF8CE873DC2 ToStrandV2"}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <ripple/ledger/Directory.h>
|
||||
#include <ripple/ledger/PaymentSandbox.h>
|
||||
#include <ripple/protocol/Book.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/IOUAmount.h>
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <ripple/protocol/XRPAmount.h>
|
||||
@@ -684,6 +685,13 @@ BookStep<TIn, TOut>::check(StrandContext const& ctx) const
|
||||
return temBAD_PATH_LOOP;
|
||||
}
|
||||
|
||||
if (ctx.view.rules().enabled(featureToStrandV2) &&
|
||||
ctx.seenDirectIssues[1].count(book_.out))
|
||||
{
|
||||
JLOG(j_.debug()) << "BookStep: loop detected: " << *this;
|
||||
return temBAD_PATH_LOOP;
|
||||
}
|
||||
|
||||
if (amendmentRIPD1443(ctx.view.info().parentCloseTime))
|
||||
{
|
||||
if (ctx.prevStep)
|
||||
|
||||
@@ -124,6 +124,12 @@ class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI>
|
||||
return src_;
|
||||
}
|
||||
|
||||
boost::optional<std::pair<AccountID,AccountID>>
|
||||
directStepAccts () const override
|
||||
{
|
||||
return std::make_pair(src_, dst_);
|
||||
}
|
||||
|
||||
bool
|
||||
redeems (ReadView const& sb, bool fwd) const override;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <ripple/app/paths/impl/Steps.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/json/json_writer.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/IOUAmount.h>
|
||||
#include <ripple/protocol/XRPAmount.h>
|
||||
|
||||
@@ -102,6 +104,8 @@ toStep (
|
||||
JLOG (j.warn())
|
||||
<< "Found offer/account payment step. Aborting payment strand.";
|
||||
assert (0);
|
||||
if (ctx.view.rules().enabled(featureToStrandV2))
|
||||
return {temBAD_PATH, std::unique_ptr<Step>{}};
|
||||
Throw<FlowException> (tefEXCEPTION, "Found offer/account payment step.");
|
||||
}
|
||||
|
||||
@@ -132,7 +136,7 @@ toStep (
|
||||
}
|
||||
|
||||
std::pair<TER, Strand>
|
||||
toStrand (
|
||||
toStrandV1 (
|
||||
ReadView const& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
@@ -370,6 +374,308 @@ toStrand (
|
||||
return {tesSUCCESS, std::move (result)};
|
||||
}
|
||||
|
||||
|
||||
std::pair<TER, Strand>
|
||||
toStrandV2 (
|
||||
ReadView const& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
boost::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path,
|
||||
bool ownerPaysTransferFee,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (isXRP(src) || isXRP(dst) ||
|
||||
!isConsistent(deliver) || (sendMaxIssue && !isConsistent(*sendMaxIssue)))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
for (auto const& pe : path)
|
||||
{
|
||||
auto const t = pe.getNodeType();
|
||||
|
||||
if ((t & ~STPathElement::typeAll) || !t)
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
bool const hasAccount = t & STPathElement::typeAccount;
|
||||
bool const hasIssuer = t & STPathElement::typeIssuer;
|
||||
bool const hasCurrency = t & STPathElement::typeCurrency;
|
||||
|
||||
if (hasAccount && (hasIssuer || hasCurrency))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
if (hasIssuer && isXRP(pe.getIssuerID()))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
if (hasAccount && isXRP(pe.getAccountID()))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
|
||||
if (hasCurrency && hasIssuer &&
|
||||
isXRP(pe.getCurrency()) != isXRP(pe.getIssuerID()))
|
||||
return {temBAD_PATH, Strand{}};
|
||||
}
|
||||
|
||||
Issue curIssue = [&]
|
||||
{
|
||||
auto const& currency =
|
||||
sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
|
||||
if (isXRP (currency))
|
||||
return xrpIssue ();
|
||||
return Issue{currency, src};
|
||||
}();
|
||||
|
||||
auto hasCurrency = [](STPathElement const pe)
|
||||
{
|
||||
return pe.getNodeType () & STPathElement::typeCurrency;
|
||||
};
|
||||
|
||||
std::vector<STPathElement> normPath;
|
||||
// reserve enough for the path, the implied source, destination,
|
||||
// sendmax and deliver.
|
||||
normPath.reserve(4 + path.size());
|
||||
{
|
||||
normPath.emplace_back(
|
||||
STPathElement::typeAll, src, curIssue.currency, curIssue.account);
|
||||
|
||||
if (sendMaxIssue && sendMaxIssue->account != src &&
|
||||
(path.empty() || !path[0].isAccount() ||
|
||||
path[0].getAccountID() != sendMaxIssue->account))
|
||||
{
|
||||
normPath.emplace_back(sendMaxIssue->account, boost::none, boost::none);
|
||||
}
|
||||
|
||||
for (auto const& i : path)
|
||||
normPath.push_back(i);
|
||||
|
||||
auto const lastCurrency =
|
||||
(*boost::find_if(boost::adaptors::reverse(normPath), hasCurrency))
|
||||
.getCurrency();
|
||||
if (lastCurrency != deliver.currency)
|
||||
normPath.emplace_back(
|
||||
boost::none, deliver.currency, deliver.account);
|
||||
|
||||
if (!((normPath.back().isAccount() &&
|
||||
normPath.back().getAccountID() == deliver.account) ||
|
||||
(dst == deliver.account)))
|
||||
{
|
||||
normPath.emplace_back(deliver.account, boost::none, boost::none);
|
||||
}
|
||||
|
||||
if (!normPath.back().isAccount() ||
|
||||
normPath.back().getAccountID() != dst)
|
||||
{
|
||||
normPath.emplace_back(dst, boost::none, boost::none);
|
||||
}
|
||||
}
|
||||
|
||||
auto const strandSrc = normPath.front().getAccountID ();
|
||||
auto const strandDst = normPath.back().getAccountID ();
|
||||
|
||||
Strand result;
|
||||
result.reserve (2 * normPath.size ());
|
||||
|
||||
/* A strand may not include the same account node more than once
|
||||
in the same currency. In a direct step, an account will show up
|
||||
at most twice: once as a src and once as a dst (hence the two element array).
|
||||
The strandSrc and strandDst will only show up once each.
|
||||
*/
|
||||
std::array<boost::container::flat_set<Issue>, 2> seenDirectIssues;
|
||||
// A strand may not include the same offer book more than once
|
||||
boost::container::flat_set<Issue> seenBookOuts;
|
||||
seenDirectIssues[0].reserve (normPath.size());
|
||||
seenDirectIssues[1].reserve (normPath.size());
|
||||
seenBookOuts.reserve (normPath.size());
|
||||
auto ctx = [&](bool isLast = false)
|
||||
{
|
||||
return StrandContext{view, result, strandSrc, strandDst, isLast,
|
||||
ownerPaysTransferFee, seenDirectIssues, seenBookOuts, j};
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < normPath.size () - 1; ++i)
|
||||
{
|
||||
/* Iterate through the path elements considering them in pairs.
|
||||
The first element of the pair is `cur` and the second element is
|
||||
`next`. When an offer is one of the pairs, the step created will be for
|
||||
`next`. This means when `cur` is an offer and `next` is an
|
||||
account then no step is created, as a step has already been created for
|
||||
that offer.
|
||||
*/
|
||||
boost::optional<STPathElement> impliedPE;
|
||||
auto cur = &normPath[i];
|
||||
auto const next = &normPath[i + 1];
|
||||
|
||||
if (cur->isAccount())
|
||||
curIssue.account = cur->getAccountID ();
|
||||
else if (cur->hasIssuer())
|
||||
curIssue.account = cur->getIssuerID ();
|
||||
|
||||
if (cur->hasCurrency())
|
||||
{
|
||||
curIssue.currency = cur->getCurrency ();
|
||||
if (isXRP(curIssue.currency))
|
||||
curIssue.account = xrpAccount();
|
||||
}
|
||||
|
||||
if (cur->isAccount() && next->isAccount())
|
||||
{
|
||||
if (!isXRP (curIssue.currency) &&
|
||||
curIssue.account != cur->getAccountID () &&
|
||||
curIssue.account != next->getAccountID ())
|
||||
{
|
||||
JLOG (j.trace()) << "Inserting implied account";
|
||||
auto msr = make_DirectStepI (ctx(), cur->getAccountID (),
|
||||
curIssue.account, curIssue.currency);
|
||||
if (msr.first != tesSUCCESS)
|
||||
return {msr.first, Strand{}};
|
||||
result.push_back (std::move (msr.second));
|
||||
impliedPE.emplace(STPathElement::typeAccount,
|
||||
curIssue.account, xrpCurrency(), xrpAccount());
|
||||
cur = &*impliedPE;
|
||||
}
|
||||
}
|
||||
else if (cur->isAccount() && next->isOffer())
|
||||
{
|
||||
if (curIssue.account != cur->getAccountID ())
|
||||
{
|
||||
JLOG (j.trace()) << "Inserting implied account before offer";
|
||||
auto msr = make_DirectStepI (ctx(), cur->getAccountID (),
|
||||
curIssue.account, curIssue.currency);
|
||||
if (msr.first != tesSUCCESS)
|
||||
return {msr.first, Strand{}};
|
||||
result.push_back (std::move (msr.second));
|
||||
impliedPE.emplace(STPathElement::typeAccount,
|
||||
curIssue.account, xrpCurrency(), xrpAccount());
|
||||
cur = &*impliedPE;
|
||||
}
|
||||
}
|
||||
else if (cur->isOffer() && next->isAccount())
|
||||
{
|
||||
if (curIssue.account != next->getAccountID () &&
|
||||
!isXRP (next->getAccountID ()))
|
||||
{
|
||||
if (isXRP(curIssue))
|
||||
{
|
||||
if (i != normPath.size() - 2)
|
||||
return {temBAD_PATH, Strand{}};
|
||||
else
|
||||
{
|
||||
// Last step. insert xrp endpoint step
|
||||
auto msr = make_XRPEndpointStep (ctx(), next->getAccountID());
|
||||
if (msr.first != tesSUCCESS)
|
||||
return {msr.first, Strand{}};
|
||||
result.push_back(std::move(msr.second));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j.trace()) << "Inserting implied account after offer";
|
||||
auto msr = make_DirectStepI(ctx(),
|
||||
curIssue.account, next->getAccountID(), curIssue.currency);
|
||||
if (msr.first != tesSUCCESS)
|
||||
return {msr.first, Strand{}};
|
||||
result.push_back(std::move(msr.second));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!next->isOffer() &&
|
||||
next->hasCurrency() && next->getCurrency () != curIssue.currency)
|
||||
{
|
||||
// Should never happen
|
||||
assert(0);
|
||||
return {temBAD_PATH, Strand{}};
|
||||
}
|
||||
|
||||
auto s =
|
||||
toStep (ctx (/*isLast*/ i == normPath.size () - 2), cur, next, curIssue);
|
||||
if (s.first == tesSUCCESS)
|
||||
result.emplace_back (std::move (s.second));
|
||||
else
|
||||
{
|
||||
JLOG (j.debug()) << "toStep failed: " << s.first;
|
||||
return {s.first, Strand{}};
|
||||
}
|
||||
}
|
||||
|
||||
auto checkStrand = [&]() -> bool {
|
||||
auto stepAccts = [](Step const& s) -> std::pair<AccountID, AccountID> {
|
||||
if (auto r = s.directStepAccts())
|
||||
return *r;
|
||||
if (auto const r = s.bookStepBook())
|
||||
return std::make_pair(r->in.account, r->out.account);
|
||||
Throw<FlowException>(
|
||||
tefEXCEPTION, "Step should be either a direct or book step");
|
||||
return std::make_pair(xrpAccount(), xrpAccount());
|
||||
};
|
||||
|
||||
auto curAccount = src;
|
||||
auto curIssue = [&] {
|
||||
auto& currency =
|
||||
sendMaxIssue ? sendMaxIssue->currency : deliver.currency;
|
||||
if (isXRP(currency))
|
||||
return xrpIssue();
|
||||
return Issue{currency, src};
|
||||
}();
|
||||
|
||||
for (auto const& s : result)
|
||||
{
|
||||
auto const accts = stepAccts(*s);
|
||||
if (accts.first != curAccount)
|
||||
return false;
|
||||
|
||||
if (auto const b = s->bookStepBook())
|
||||
{
|
||||
if (curIssue != b->in)
|
||||
return false;
|
||||
curIssue = b->out;
|
||||
}
|
||||
else
|
||||
{
|
||||
curIssue.account = accts.second;
|
||||
}
|
||||
|
||||
curAccount = accts.second;
|
||||
}
|
||||
if (curAccount != dst)
|
||||
return false;
|
||||
if (curIssue.currency != deliver.currency)
|
||||
return false;
|
||||
if (curIssue.account != deliver.account &&
|
||||
curIssue.account != dst)
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!checkStrand())
|
||||
{
|
||||
JLOG (j.warn()) << "Flow check strand failed";
|
||||
assert(0);
|
||||
return {temBAD_PATH, Strand{}};
|
||||
}
|
||||
|
||||
return {tesSUCCESS, std::move (result)};
|
||||
}
|
||||
|
||||
std::pair<TER, Strand>
|
||||
toStrand (
|
||||
ReadView const& view,
|
||||
AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
boost::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path,
|
||||
bool ownerPaysTransferFee,
|
||||
beast::Journal j)
|
||||
{
|
||||
if (view.rules().enabled(featureToStrandV2))
|
||||
return toStrandV2(
|
||||
view, src, dst, deliver, sendMaxIssue, path, ownerPaysTransferFee, j);
|
||||
else
|
||||
return toStrandV1(
|
||||
view, src, dst, deliver, sendMaxIssue, path, ownerPaysTransferFee, j);
|
||||
}
|
||||
|
||||
std::pair<TER, std::vector<Strand>>
|
||||
toStrands (
|
||||
ReadView const& view,
|
||||
|
||||
@@ -121,6 +121,14 @@ public:
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// for debugging. Return the src and dst accounts for a direct step
|
||||
// For XRP endpoints, one of src or dst will be the root account
|
||||
virtual boost::optional<std::pair<AccountID,AccountID>>
|
||||
directStepAccts () const
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
/**
|
||||
If this step is a DirectStepI and the src redeems to the dst, return true,
|
||||
otherwise return false.
|
||||
@@ -223,6 +231,26 @@ bool operator==(Strand const& lhs, Strand const& rhs)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Normalize a path by inserting implied accounts and offers
|
||||
|
||||
@param src Account that is sending assets
|
||||
@param dst Account that is receiving assets
|
||||
@param deliver Asset the dst account will receive
|
||||
(if issuer of deliver == dst, then accept any issuer)
|
||||
@param sendMax Optional asset to send.
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@return error code and normalized path
|
||||
*/
|
||||
std::pair<TER, STPath>
|
||||
normalizePath(AccountID const& src,
|
||||
AccountID const& dst,
|
||||
Issue const& deliver,
|
||||
boost::optional<Issue> const& sendMaxIssue,
|
||||
STPath const& path);
|
||||
|
||||
/*
|
||||
Create a strand for the specified path
|
||||
|
||||
@@ -235,7 +263,6 @@ bool operator==(Strand const& lhs, Strand const& rhs)
|
||||
@param path Liquidity sources to use for this strand of the payment. The path
|
||||
contains an ordered collection of the offer books to use and
|
||||
accounts to ripple through.
|
||||
@param addDefaultPath Determines if the default path should be considered
|
||||
@param l logs to write journal messages to
|
||||
@return error code and collection of strands
|
||||
*/
|
||||
|
||||
@@ -68,6 +68,14 @@ class XRPEndpointStep : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep>
|
||||
return acc_;
|
||||
};
|
||||
|
||||
boost::optional<std::pair<AccountID,AccountID>>
|
||||
directStepAccts () const override
|
||||
{
|
||||
if (isLast_)
|
||||
return std::make_pair(xrpAccount(), acc_);
|
||||
return std::make_pair(acc_, xrpAccount());
|
||||
}
|
||||
|
||||
boost::optional<EitherAmount>
|
||||
cachedIn () const override
|
||||
{
|
||||
|
||||
@@ -48,6 +48,7 @@ extern uint256 const featureTickSize;
|
||||
extern uint256 const fix1368;
|
||||
extern uint256 const featureEscrow;
|
||||
extern uint256 const featureCryptoConditionsSuite;
|
||||
extern uint256 const featureToStrandV2;
|
||||
|
||||
} // ripple
|
||||
|
||||
|
||||
@@ -117,6 +117,8 @@ public:
|
||||
hash_value_ = get_hash (*this);
|
||||
}
|
||||
|
||||
STPathElement(STPathElement const&) = default;
|
||||
|
||||
int
|
||||
getNodeType () const
|
||||
{
|
||||
@@ -203,8 +205,8 @@ class STPath
|
||||
public:
|
||||
STPath () = default;
|
||||
|
||||
STPath (std::vector<STPathElement> const& p)
|
||||
: mPath (p)
|
||||
STPath (std::vector<STPathElement> p)
|
||||
: mPath (std::move(p))
|
||||
{ }
|
||||
|
||||
std::vector<STPathElement>::size_type
|
||||
@@ -279,6 +281,10 @@ public:
|
||||
return mPath[i];
|
||||
}
|
||||
|
||||
void reserve(size_t s)
|
||||
{
|
||||
mPath.reserve(s);
|
||||
}
|
||||
private:
|
||||
std::vector<STPathElement> mPath;
|
||||
};
|
||||
|
||||
@@ -59,5 +59,6 @@ uint256 const featureTickSize = feature("TickSize");
|
||||
uint256 const fix1368 = feature("fix1368");
|
||||
uint256 const featureEscrow = feature("Escrow");
|
||||
uint256 const featureCryptoConditionsSuite = feature("CryptoConditionsSuite");
|
||||
uint256 const featureToStrandV2 = feature("ToStrandV2");
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <BeastConfig.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -41,11 +42,12 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
void
|
||||
testStepLimit()
|
||||
testStepLimit(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
auto const xrpMax = XRP(100000000000);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
@@ -76,10 +78,10 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCrossingLimit()
|
||||
testCrossingLimit(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
auto const xrpMax = XRP(100000000000);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
@@ -105,10 +107,10 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testStepAndCrossingLimit()
|
||||
testStepAndCrossingLimit(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
auto const xrpMax = XRP(100000000000);
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
@@ -150,9 +152,14 @@ public:
|
||||
void
|
||||
run()
|
||||
{
|
||||
testStepLimit();
|
||||
testCrossingLimit();
|
||||
testStepAndCrossingLimit();
|
||||
auto testAll = [this](std::initializer_list<uint256> fs) {
|
||||
testStepLimit(fs);
|
||||
testCrossingLimit(fs);
|
||||
testStepAndCrossingLimit(fs);
|
||||
};
|
||||
testAll({});
|
||||
testAll({featureFlow});
|
||||
testAll({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <BeastConfig.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -28,7 +29,7 @@ class DeliverMin_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
test_convert_all_of_an_asset()
|
||||
test_convert_all_of_an_asset(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("Convert all of an asset using DeliverMin");
|
||||
|
||||
@@ -37,7 +38,7 @@ public:
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
env.trust(USD(100), "alice", "bob", "carol");
|
||||
env(pay("alice", "bob", USD(10)), delivermin(USD(10)), ter(temBAD_AMOUNT));
|
||||
@@ -60,7 +61,7 @@ public:
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), "alice", "bob", gw);
|
||||
env.trust(USD(1000), "alice", "bob");
|
||||
env(pay(gw, "bob", USD(100)));
|
||||
@@ -72,7 +73,7 @@ public:
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", gw);
|
||||
env.trust(USD(1000), "bob", "carol");
|
||||
env(pay(gw, "bob", USD(200)));
|
||||
@@ -90,7 +91,7 @@ public:
|
||||
}
|
||||
|
||||
{
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), "alice", "bob", "carol", "dan", gw);
|
||||
env.trust(USD(1000), "bob", "carol", "dan");
|
||||
env(pay(gw, "bob", USD(100)));
|
||||
@@ -110,7 +111,9 @@ public:
|
||||
void
|
||||
run()
|
||||
{
|
||||
test_convert_all_of_an_asset();
|
||||
test_convert_all_of_an_asset({});
|
||||
test_convert_all_of_an_asset({featureFlow});
|
||||
test_convert_all_of_an_asset({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -36,11 +37,11 @@ class Discrepancy_test : public beast::unit_test::suite
|
||||
// A payment with path and sendmax is made and the transaction is queried
|
||||
// to verify that the net of balance changes match the fee charged.
|
||||
void
|
||||
testXRPDiscrepancy ()
|
||||
testXRPDiscrepancy (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Discrepancy test : XRP Discrepancy");
|
||||
using namespace test::jtx;
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
|
||||
Account A1 {"A1"};
|
||||
Account A2 {"A2"};
|
||||
@@ -143,7 +144,9 @@ class Discrepancy_test : public beast::unit_test::suite
|
||||
public:
|
||||
void run ()
|
||||
{
|
||||
testXRPDiscrepancy ();
|
||||
testXRPDiscrepancy ({});
|
||||
testXRPDiscrepancy ({featureFlow});
|
||||
testXRPDiscrepancy ({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -21,61 +21,16 @@
|
||||
#include <ripple/app/paths/impl/Steps.h>
|
||||
#include <ripple/basics/contract.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/ledger/ApplyViewImpl.h>
|
||||
#include <ripple/ledger/PaymentSandbox.h>
|
||||
#include <ripple/ledger/Sandbox.h>
|
||||
#include <test/jtx/PathSet.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct Flow_test;
|
||||
|
||||
struct DirectStepInfo
|
||||
{
|
||||
AccountID src;
|
||||
AccountID dst;
|
||||
Currency currency;
|
||||
};
|
||||
|
||||
struct XRPEndpointStepInfo
|
||||
{
|
||||
AccountID acc;
|
||||
};
|
||||
|
||||
enum class TrustFlag {freeze, auth};
|
||||
|
||||
/*constexpr*/ std::uint32_t trustFlag (TrustFlag f, bool useHigh)
|
||||
{
|
||||
switch(f)
|
||||
{
|
||||
case TrustFlag::freeze:
|
||||
if (useHigh)
|
||||
return lsfHighFreeze;
|
||||
return lsfLowFreeze;
|
||||
case TrustFlag::auth:
|
||||
if (useHigh)
|
||||
return lsfHighAuth;
|
||||
return lsfLowAuth;
|
||||
}
|
||||
return 0; // Silence warning about end of non-void function
|
||||
}
|
||||
|
||||
bool getTrustFlag (jtx::Env const& env,
|
||||
jtx::Account const& src,
|
||||
jtx::Account const& dst,
|
||||
Currency const& cur,
|
||||
TrustFlag flag)
|
||||
{
|
||||
if (auto sle = env.le (keylet::line (src, dst, cur)))
|
||||
{
|
||||
auto const useHigh = src.id() > dst.id();
|
||||
return sle->isFlag (trustFlag (flag, useHigh));
|
||||
}
|
||||
Throw<std::runtime_error> ("No line in getTrustFlag");
|
||||
return false; // silence warning
|
||||
}
|
||||
|
||||
jtx::PrettyAmount
|
||||
xrpMinusFee (jtx::Env const& env, std::int64_t xrpAmount)
|
||||
{
|
||||
@@ -85,311 +40,17 @@ xrpMinusFee (jtx::Env const& env, std::int64_t xrpAmount)
|
||||
dropsPerXRP<std::int64_t>::value * xrpAmount - feeDrops);
|
||||
};
|
||||
|
||||
bool equal (std::unique_ptr<Step> const& s1,
|
||||
DirectStepInfo const& dsi)
|
||||
{
|
||||
if (!s1)
|
||||
return false;
|
||||
return test::directStepEqual (*s1, dsi.src, dsi.dst, dsi.currency);
|
||||
}
|
||||
|
||||
bool equal (std::unique_ptr<Step> const& s1,
|
||||
XRPEndpointStepInfo const& xrpsi)
|
||||
{
|
||||
if (!s1)
|
||||
return false;
|
||||
return test::xrpEndpointStepEqual (*s1, xrpsi.acc);
|
||||
}
|
||||
|
||||
bool equal (std::unique_ptr<Step> const& s1, ripple::Book const& bsi)
|
||||
{
|
||||
if (!s1)
|
||||
return false;
|
||||
return bookStepEqual (*s1, bsi);
|
||||
}
|
||||
|
||||
template <class Iter>
|
||||
bool strandEqualHelper (Iter i)
|
||||
{
|
||||
// base case. all args processed and found equal.
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Iter, class StepInfo, class... Args>
|
||||
bool strandEqualHelper (Iter i, StepInfo&& si, Args&&... args)
|
||||
{
|
||||
if (!equal (*i, std::forward<StepInfo> (si)))
|
||||
return false;
|
||||
return strandEqualHelper (++i, std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
bool equal (Strand const& strand, Args&&... args)
|
||||
{
|
||||
if (strand.size () != sizeof...(Args))
|
||||
return false;
|
||||
if (strand.empty ())
|
||||
return true;
|
||||
return strandEqualHelper (strand.begin (), std::forward<Args> (args)...);
|
||||
}
|
||||
|
||||
struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// Account path element
|
||||
static auto APE(AccountID const& a)
|
||||
static bool hasFeature(uint256 const& feat, std::initializer_list<uint256> args)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeAccount, a, xrpCurrency (), xrpAccount ());
|
||||
};
|
||||
|
||||
// Issue path element
|
||||
static auto IPE(Issue const& iss)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
xrpAccount (), iss.currency, iss.account);
|
||||
};
|
||||
|
||||
// Issuer path element
|
||||
static auto IAPE(AccountID const& account)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeIssuer,
|
||||
xrpAccount (), xrpCurrency (), account);
|
||||
};
|
||||
|
||||
// Currency path element
|
||||
static auto CPE(Currency const& c)
|
||||
{
|
||||
return STPathElement (
|
||||
STPathElement::typeCurrency, xrpAccount (), c, xrpAccount ());
|
||||
};
|
||||
|
||||
void testToStrand ()
|
||||
{
|
||||
testcase ("To Strand");
|
||||
|
||||
using namespace jtx;
|
||||
auto const alice = Account ("alice");
|
||||
auto const bob = Account ("bob");
|
||||
auto const carol = Account ("carol");
|
||||
auto const gw = Account ("gw");
|
||||
|
||||
auto const USD = gw["USD"];
|
||||
auto const EUR = gw["EUR"];
|
||||
|
||||
auto const eurC = EUR.currency;
|
||||
auto const usdC = USD.currency;
|
||||
|
||||
using D = DirectStepInfo;
|
||||
using B = ripple::Book;
|
||||
using XRPS = XRPEndpointStepInfo;
|
||||
|
||||
auto test = [&, this](jtx::Env& env, Issue const& deliver,
|
||||
boost::optional<Issue> const& sendMaxIssue, STPath const& path,
|
||||
TER expTer, auto&&... expSteps)
|
||||
{
|
||||
auto r = toStrand (*env.current (), alice, bob,
|
||||
deliver, sendMaxIssue, path, true, env.app ().logs ().journal ("Flow"));
|
||||
BEAST_EXPECT(r.first == expTer);
|
||||
if (sizeof...(expSteps))
|
||||
BEAST_EXPECT(equal (
|
||||
r.second, std::forward<decltype (expSteps)> (expSteps)...));
|
||||
};
|
||||
|
||||
{
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
|
||||
test (env, USD, boost::none, STPath(), terNO_LINE);
|
||||
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
test (env, USD, boost::none, STPath(), tecPATH_DRY);
|
||||
|
||||
env (pay (gw, alice, USD (100)));
|
||||
env (pay (gw, carol, USD (100)));
|
||||
|
||||
// Insert implied account
|
||||
test (env, USD, boost::none, STPath(), tesSUCCESS,
|
||||
D{alice, gw, usdC}, D{gw, bob, usdC});
|
||||
env.trust (EUR (1000), alice, bob);
|
||||
|
||||
// Insert implied offer
|
||||
test (env, EUR, USD.issue (), STPath(), tesSUCCESS,
|
||||
D{alice, gw, usdC}, B{USD, EUR}, D{gw, bob, eurC});
|
||||
|
||||
// Path with explicit offer
|
||||
test (env, EUR, USD.issue (), STPath ({IPE (EUR)}),
|
||||
tesSUCCESS, D{alice, gw, usdC}, B{USD, EUR}, D{gw, bob, eurC});
|
||||
|
||||
// Path with offer that changes issuer only
|
||||
env.trust (carol["USD"] (1000), bob);
|
||||
test (env, carol["USD"], USD.issue (), STPath ({IAPE (carol)}),
|
||||
tesSUCCESS,
|
||||
D{alice, gw, usdC}, B{USD, carol["USD"]}, D{carol, bob, usdC});
|
||||
|
||||
// Path with XRP src currency
|
||||
test (env, USD, xrpIssue (), STPath ({IPE (USD)}), tesSUCCESS,
|
||||
XRPS{alice}, B{XRP, USD}, D{gw, bob, usdC});
|
||||
|
||||
// Path with XRP dst currency
|
||||
test (env, xrpIssue(), USD.issue (), STPath ({IPE (XRP)}),
|
||||
tesSUCCESS, D{alice, gw, usdC}, B{USD, XRP}, XRPS{bob});
|
||||
|
||||
// Path with XRP cross currency bridged payment
|
||||
test (env, EUR, USD.issue (), STPath ({CPE (xrpCurrency ())}),
|
||||
tesSUCCESS,
|
||||
D{alice, gw, usdC}, B{USD, XRP}, B{XRP, EUR}, D{gw, bob, eurC});
|
||||
|
||||
// XRP -> XRP transaction can't include a path
|
||||
test (env, XRP, boost::none, STPath ({APE (carol)}), temBAD_PATH);
|
||||
|
||||
{
|
||||
// The root account can't be the src or dst
|
||||
auto flowJournal = env.app ().logs ().journal ("Flow");
|
||||
{
|
||||
// The root account can't be the dst
|
||||
auto r = toStrand (*env.current (), alice,
|
||||
xrpAccount (), XRP, USD.issue (), STPath (), true, flowJournal);
|
||||
BEAST_EXPECT(r.first == temBAD_PATH);
|
||||
}
|
||||
{
|
||||
// The root account can't be the src
|
||||
auto r =
|
||||
toStrand (*env.current (), xrpAccount (),
|
||||
alice, XRP, boost::none, STPath (), true, flowJournal);
|
||||
BEAST_EXPECT(r.first == temBAD_PATH);
|
||||
}
|
||||
{
|
||||
// The root account can't be the src
|
||||
auto r = toStrand (*env.current (),
|
||||
noAccount (), bob, USD, boost::none, STPath (), true, flowJournal);
|
||||
BEAST_EXPECT(r.first == terNO_ACCOUNT);
|
||||
}
|
||||
for(auto const& f : args)
|
||||
if (f == feat)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create an offer with the same in/out issue
|
||||
test (env, EUR, USD.issue (), STPath ({IPE (USD), IPE (EUR)}),
|
||||
temBAD_PATH);
|
||||
|
||||
// Path element with type zero
|
||||
test (env, USD, boost::none,
|
||||
STPath ({STPathElement (
|
||||
0, xrpAccount (), xrpCurrency (), xrpAccount ())}),
|
||||
temBAD_PATH);
|
||||
|
||||
// The same account can't appear more than once on a path
|
||||
// `gw` will be used from alice->carol and implied between carol
|
||||
// and bob
|
||||
test (env, USD, boost::none, STPath ({APE (gw), APE (carol)}),
|
||||
temBAD_PATH_LOOP);
|
||||
|
||||
// The same offer can't appear more than once on a path
|
||||
test (env, EUR, USD.issue (), STPath ({IPE (EUR), IPE (USD), IPE (EUR)}),
|
||||
temBAD_PATH_LOOP);
|
||||
}
|
||||
|
||||
{
|
||||
// cannot have more than one offer with the same output issue
|
||||
|
||||
using namespace jtx;
|
||||
Env env (*this, features (featureFlow), features(featureOwnerPaysFee));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (10000), alice, bob, carol);
|
||||
env.trust (EUR (10000), alice, bob, carol);
|
||||
|
||||
env (pay (gw, bob, USD (100)));
|
||||
env (pay (gw, bob, EUR (100)));
|
||||
|
||||
env (offer (bob, XRP (100), USD (100)));
|
||||
env (offer (bob, USD (100), EUR (100)), txflags (tfPassive));
|
||||
env (offer (bob, EUR (100), USD (100)), txflags (tfPassive));
|
||||
|
||||
// payment path: XRP -> XRP/USD -> USD/EUR -> EUR/USD
|
||||
env (pay (alice, carol, USD (100)), path (~USD, ~EUR, ~USD),
|
||||
sendmax (XRP (200)), txflags (tfNoRippleDirect),
|
||||
ter (temBAD_PATH_LOOP));
|
||||
}
|
||||
|
||||
{
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, noripple (gw));
|
||||
env.trust (USD (1000), alice, bob);
|
||||
env (pay (gw, alice, USD (100)));
|
||||
test (env, USD, boost::none, STPath (), terNO_RIPPLE);
|
||||
}
|
||||
|
||||
{
|
||||
// check global freeze
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env.trust (USD (1000), alice, bob);
|
||||
env (pay (gw, alice, USD (100)));
|
||||
|
||||
// Account can still issue payments
|
||||
env(fset(alice, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), tesSUCCESS);
|
||||
env(fclear(alice, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), tesSUCCESS);
|
||||
|
||||
// Account can not issue funds
|
||||
env(fset(gw, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), terNO_LINE);
|
||||
env(fclear(gw, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), tesSUCCESS);
|
||||
|
||||
// Account can not receive funds
|
||||
env(fset(bob, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), terNO_LINE);
|
||||
env(fclear(bob, asfGlobalFreeze));
|
||||
test (env, USD, boost::none, STPath (), tesSUCCESS);
|
||||
}
|
||||
{
|
||||
// Freeze between gw and alice
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env.trust (USD (1000), alice, bob);
|
||||
env (pay (gw, alice, USD (100)));
|
||||
test (env, USD, boost::none, STPath (), tesSUCCESS);
|
||||
env (trust (gw, alice["USD"] (0), tfSetFreeze));
|
||||
BEAST_EXPECT(getTrustFlag (env, gw, alice, usdC, TrustFlag::freeze));
|
||||
test (env, USD, boost::none, STPath (), terNO_LINE);
|
||||
}
|
||||
{
|
||||
// check no auth
|
||||
// An account may require authorization to receive IOUs from an
|
||||
// issuer
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env (fset (gw, asfRequireAuth));
|
||||
env.trust (USD (1000), alice, bob);
|
||||
// Authorize alice but not bob
|
||||
env (trust (gw, alice ["USD"] (1000), tfSetfAuth));
|
||||
BEAST_EXPECT(getTrustFlag (env, gw, alice, usdC, TrustFlag::auth));
|
||||
env (pay (gw, alice, USD (100)));
|
||||
env.require (balance (alice, USD (100)));
|
||||
test (env, USD, boost::none, STPath (), terNO_AUTH);
|
||||
|
||||
// Check pure issue redeem still works
|
||||
auto r = toStrand (*env.current (), alice, gw, USD,
|
||||
boost::none, STPath (), true, env.app ().logs ().journal ("Flow"));
|
||||
BEAST_EXPECT(r.first == tesSUCCESS);
|
||||
BEAST_EXPECT(equal (r.second, D{alice, gw, usdC}));
|
||||
}
|
||||
{
|
||||
// Check path with sendMax and node with correct sendMax already set
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env.trust (USD (1000), alice, bob);
|
||||
env.trust (EUR (1000), alice, bob);
|
||||
env (pay (gw, alice, EUR (100)));
|
||||
auto const path = STPath ({STPathElement (STPathElement::typeAll,
|
||||
EUR.account, EUR.currency, EUR.account)});
|
||||
test (env, USD, EUR.issue(), path, tesSUCCESS);
|
||||
}
|
||||
}
|
||||
void testDirectStep ()
|
||||
void testDirectStep (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Direct Step");
|
||||
|
||||
@@ -407,7 +68,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
auto const USD = gw["USD"];
|
||||
{
|
||||
// Pay USD, trivial path
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env.trust (USD (1000), alice, bob);
|
||||
@@ -417,7 +78,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// XRP transfer
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob);
|
||||
env (pay (alice, bob, XRP (100)));
|
||||
@@ -426,7 +87,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// Partial payments
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env.trust (USD (1000), alice, bob);
|
||||
@@ -440,7 +101,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// Pay by rippling through accounts, use path finder
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, dan);
|
||||
env.trust (USDA (10), bob);
|
||||
@@ -455,7 +116,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// Pay by rippling through accounts, specify path
|
||||
// and charge a transfer fee
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, dan);
|
||||
env.trust (USDA (10), bob);
|
||||
@@ -473,7 +134,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// Pay by rippling through accounts, specify path and transfer fee
|
||||
// Test that the transfer fee is not charged when alice issues
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, dan);
|
||||
env.trust (USDA (10), bob);
|
||||
@@ -489,7 +150,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// test best quality path is taken
|
||||
// Paths: A->B->D->E ; A->C->D->E
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, dan, erin);
|
||||
env.trust (USDA (10), bob, carol);
|
||||
@@ -510,7 +171,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// Limit quality
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol);
|
||||
env.trust (USDA (10), bob);
|
||||
@@ -526,7 +187,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void testLineQuality ()
|
||||
void testLineQuality (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Line Quality");
|
||||
|
||||
@@ -543,66 +204,63 @@ struct Flow_test : public beast::unit_test::suite
|
||||
// Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut
|
||||
for (auto bobDanQIn : {80, 100, 120})
|
||||
for (auto bobAliceQOut : {80, 100, 120})
|
||||
for (auto const& f : {feature ("nullFeature"), featureFlow})
|
||||
{
|
||||
if (f != featureFlow && bobDanQIn < 100 && bobAliceQOut < 100)
|
||||
if (!hasFeature(featureFlow, fs) && bobDanQIn < 100 &&
|
||||
bobAliceQOut < 100)
|
||||
continue; // Bug in flow v1
|
||||
Env env (*this, features (f));
|
||||
env.fund (XRP (10000), alice, bob, carol, dan);
|
||||
env (trust (bob, USDD (100)), qualityInPercent (bobDanQIn));
|
||||
env (trust (bob, USDA (100)),
|
||||
qualityOutPercent (bobAliceQOut));
|
||||
env (trust (carol, USDA (100)));
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), alice, bob, carol, dan);
|
||||
env(trust(bob, USDD(100)), qualityInPercent(bobDanQIn));
|
||||
env(trust(bob, USDA(100)), qualityOutPercent(bobAliceQOut));
|
||||
env(trust(carol, USDA(100)));
|
||||
|
||||
env (pay (alice, bob, USDA (100)));
|
||||
env.require (balance (bob, USDA (100)));
|
||||
env (pay (dan, carol, USDA (10)), path (bob),
|
||||
sendmax (USDD (100)), txflags (tfNoRippleDirect));
|
||||
env.require (balance (bob, USDA (90)));
|
||||
env(pay(alice, bob, USDA(100)));
|
||||
env.require(balance(bob, USDA(100)));
|
||||
env(pay(dan, carol, USDA(10)),
|
||||
path(bob), sendmax(USDD(100)), txflags(tfNoRippleDirect));
|
||||
env.require(balance(bob, USDA(90)));
|
||||
if (bobAliceQOut > bobDanQIn)
|
||||
env.require (
|
||||
balance (bob, USDD (10.0 * double(bobAliceQOut) /
|
||||
double(bobDanQIn))));
|
||||
env.require(balance(
|
||||
bob,
|
||||
USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn))));
|
||||
else
|
||||
env.require (balance (bob, USDD (10)));
|
||||
env.require (balance (carol, USDA (10)));
|
||||
env.require(balance(bob, USDD(10)));
|
||||
env.require(balance(carol, USDA(10)));
|
||||
}
|
||||
|
||||
// bob -> alice -> carol; vary carolAliceQIn
|
||||
for (auto carolAliceQIn : {80, 100, 120})
|
||||
for (auto const& f : {feature ("nullFeature"), featureFlow})
|
||||
{
|
||||
Env env (*this, features (f));
|
||||
env.fund (XRP (10000), alice, bob, carol);
|
||||
env (trust (bob, USDA (10)));
|
||||
env (trust (carol, USDA (10)), qualityInPercent (carolAliceQIn));
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
env(trust(bob, USDA(10)));
|
||||
env(trust(carol, USDA(10)), qualityInPercent(carolAliceQIn));
|
||||
|
||||
env (pay (alice, bob, USDA (10)));
|
||||
env.require (balance (bob, USDA (10)));
|
||||
env (pay (bob, carol, USDA (5)), sendmax (USDA (10)));
|
||||
env(pay(alice, bob, USDA(10)));
|
||||
env.require(balance(bob, USDA(10)));
|
||||
env(pay(bob, carol, USDA(5)), sendmax(USDA(10)));
|
||||
auto const effectiveQ =
|
||||
carolAliceQIn > 100 ? 1.0 : carolAliceQIn / 100.0;
|
||||
env.require (balance (bob, USDA (10.0 - 5.0 / effectiveQ)));
|
||||
env.require(balance(bob, USDA(10.0 - 5.0 / effectiveQ)));
|
||||
}
|
||||
|
||||
// bob -> alice -> carol; bobAliceQOut varies.
|
||||
for (auto bobAliceQOut : {80, 100, 120})
|
||||
for (auto const& f : {feature ("nullFeature"), featureFlow})
|
||||
{
|
||||
Env env (*this, features (f));
|
||||
env.fund (XRP (10000), alice, bob, carol);
|
||||
env (trust (bob, USDA (10)), qualityOutPercent (bobAliceQOut));
|
||||
env (trust (carol, USDA (10)));
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(10000), alice, bob, carol);
|
||||
env(trust(bob, USDA(10)), qualityOutPercent(bobAliceQOut));
|
||||
env(trust(carol, USDA(10)));
|
||||
|
||||
env (pay (alice, bob, USDA (10)));
|
||||
env.require (balance (bob, USDA (10)));
|
||||
env (pay (bob, carol, USDA (5)), sendmax (USDA (5)));
|
||||
env.require (balance (carol, USDA (5)));
|
||||
env.require (balance (bob, USDA (10-5)));
|
||||
env(pay(alice, bob, USDA(10)));
|
||||
env.require(balance(bob, USDA(10)));
|
||||
env(pay(bob, carol, USDA(5)), sendmax(USDA(5)));
|
||||
env.require(balance(carol, USDA(5)));
|
||||
env.require(balance(bob, USDA(10 - 5)));
|
||||
}
|
||||
}
|
||||
|
||||
void testBookStep ()
|
||||
void testBookStep (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Book Step");
|
||||
|
||||
@@ -618,7 +276,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// simple IOU/IOU offer
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -639,7 +297,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// simple IOU/XRP XRP/IOU offer
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -663,7 +321,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// simple XRP -> USD through offer and sendmax
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -684,7 +342,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// simple USD -> XRP through offer and sendmax
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -705,7 +363,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// test unfunded offers are removed when payment succeeds
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -751,7 +409,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
// offer. When the payment fails `flow` should return the unfunded
|
||||
// offer. This test is intentionally similar to the one that removes
|
||||
// unfunded offers when the payment succeeds.
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -778,7 +436,15 @@ struct Flow_test : public beast::unit_test::suite
|
||||
STAmount smax (BTC (61));
|
||||
PaymentSandbox sb (env.current ().get (), tapNONE);
|
||||
STPathSet paths;
|
||||
auto IPE = [](Issue const& iss) {
|
||||
return STPathElement(
|
||||
STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
xrpAccount(),
|
||||
iss.currency,
|
||||
iss.account);
|
||||
};
|
||||
{
|
||||
|
||||
// BTC -> USD
|
||||
STPath p1 ({IPE (USD.issue ())});
|
||||
paths.push_back (p1);
|
||||
@@ -818,8 +484,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
// Without limits, the 0.4 USD would produce 1000 EUR in the forward
|
||||
// pass. This test checks that the payment produces 1 EUR, as expected.
|
||||
|
||||
Env env (*this, features (featureFlow),
|
||||
features (featureOwnerPaysFee));
|
||||
Env env (*this, features (fs));
|
||||
|
||||
auto const closeTime = STAmountSO::soTime2 +
|
||||
100 * env.closed ()->info ().closeTimeResolution;
|
||||
@@ -844,7 +509,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void testTransferRate ()
|
||||
void testTransferRate (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Transfer Rate");
|
||||
|
||||
@@ -862,7 +527,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// Simple payment through a gateway with a
|
||||
// transfer rate
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -874,7 +539,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// transfer rate is not charged when issuer is src or dst
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -886,7 +551,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// transfer fee on an offer
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -904,7 +569,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// Transfer fee two consecutive offers
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -927,7 +592,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, no offers
|
||||
// limiting step is not an endpoint
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
|
||||
@@ -947,7 +612,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
{
|
||||
// First pass through a strand redeems, second pass issues, through an offer
|
||||
// limiting step is not an endpoint
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
auto const USDA = alice["USD"];
|
||||
auto const USDB = bob["USD"];
|
||||
Account const dan ("dan");
|
||||
@@ -974,7 +639,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
|
||||
{
|
||||
// Offer where the owner is also the issuer, owner pays fee
|
||||
Env env (*this, features(featureFlow), features(featureOwnerPaysFee));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -986,9 +651,10 @@ struct Flow_test : public beast::unit_test::suite
|
||||
balance (alice, xrpMinusFee(env, 10000-100)),
|
||||
balance (bob, USD (100)));
|
||||
}
|
||||
if (!hasFeature(featureOwnerPaysFee, fs))
|
||||
{
|
||||
// Offer where the owner is also the issuer, sender pays fee
|
||||
Env env (*this, features(featureFlow));
|
||||
Env env (*this, features(fs));
|
||||
|
||||
env.fund (XRP (10000), alice, bob, gw);
|
||||
env(rate(gw, 1.25));
|
||||
@@ -1003,8 +669,10 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testFalseDryHelper (jtx::Env& env)
|
||||
testFalseDry(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("falseDryChanges");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
auto const gw = Account ("gateway");
|
||||
@@ -1014,6 +682,8 @@ struct Flow_test : public beast::unit_test::suite
|
||||
Account const bob ("bob");
|
||||
Account const carol ("carol");
|
||||
|
||||
Env env (*this, features (fs));
|
||||
|
||||
auto const closeTime = amendmentRIPD1141SoTime() +
|
||||
100 * env.closed ()->info ().closeTimeResolution;
|
||||
env.close (closeTime);
|
||||
@@ -1045,21 +715,6 @@ struct Flow_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(carolUSD > USD (0) && carolUSD < USD (50));
|
||||
}
|
||||
|
||||
void
|
||||
testFalseDry ()
|
||||
{
|
||||
testcase ("falseDryChanges");
|
||||
using namespace jtx;
|
||||
{
|
||||
Env env (*this, features (featureFlow));
|
||||
testFalseDryHelper (env);
|
||||
}
|
||||
{
|
||||
Env env (*this);
|
||||
testFalseDryHelper (env);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testLimitQuality ()
|
||||
{
|
||||
@@ -1123,7 +778,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testSelfPayment1()
|
||||
testSelfPayment1(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Self-payment 1");
|
||||
|
||||
@@ -1140,7 +795,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
auto const USD = gw1["USD"];
|
||||
auto const EUR = gw2["EUR"];
|
||||
|
||||
Env env (*this, features (featureFlow));
|
||||
Env env (*this, features (fs));
|
||||
|
||||
auto const closeTime =
|
||||
amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution;
|
||||
@@ -1199,7 +854,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testSelfPayment2()
|
||||
testSelfPayment2(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Self-payment 2");
|
||||
|
||||
@@ -1214,7 +869,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
auto const USD = gw1["USD"];
|
||||
auto const EUR = gw2["EUR"];
|
||||
|
||||
Env env (*this, features (featureFlow));
|
||||
Env env (*this, features (fs));
|
||||
|
||||
auto const closeTime =
|
||||
amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution;
|
||||
@@ -1271,7 +926,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(offer[sfTakerPays] == USD (495));
|
||||
}
|
||||
}
|
||||
void testSelfFundedXRPEndpoint (bool consumeOffer)
|
||||
void testSelfFundedXRPEndpoint (bool consumeOffer, std::initializer_list<uint256> fs)
|
||||
{
|
||||
// Test that the deferred credit table is not bypassed for
|
||||
// XRPEndpointSteps. If the account in the first step is sending XRP and
|
||||
@@ -1282,7 +937,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this, features(featureFlow));
|
||||
Env env(*this, features(fs));
|
||||
|
||||
// Need new behavior from `accountHolds`
|
||||
auto const closeTime = amendmentRIPD1141SoTime() +
|
||||
@@ -1305,7 +960,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
txflags(tfPartialPayment | tfNoRippleDirect));
|
||||
}
|
||||
|
||||
void testUnfundedOffer (bool withFix)
|
||||
void testUnfundedOffer (bool withFix, std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase(std::string("Unfunded Offer ") +
|
||||
(withFix ? "with fix" : "without fix"));
|
||||
@@ -1313,7 +968,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
using namespace jtx;
|
||||
{
|
||||
// Test reverse
|
||||
Env env(*this, features(featureFlow));
|
||||
Env env(*this, features(fs));
|
||||
auto closeTime = amendmentRIPD1298SoTime();
|
||||
if (withFix)
|
||||
closeTime += env.closed()->info().closeTimeResolution;
|
||||
@@ -1345,7 +1000,7 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
{
|
||||
// Test forward
|
||||
Env env(*this, features(featureFlow));
|
||||
Env env(*this, features(fs));
|
||||
auto closeTime = amendmentRIPD1298SoTime();
|
||||
if (withFix)
|
||||
closeTime += env.closed()->info().closeTimeResolution;
|
||||
@@ -1379,14 +1034,13 @@ struct Flow_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Features>
|
||||
void
|
||||
testReexecuteDirectStep(Features&&... fs)
|
||||
testReexecuteDirectStep(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("ReexecuteDirectStep");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this, features(fs)...);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
@@ -1488,22 +1142,32 @@ struct Flow_test : public beast::unit_test::suite
|
||||
|
||||
void run() override
|
||||
{
|
||||
testDirectStep ();
|
||||
testLineQuality();
|
||||
testBookStep ();
|
||||
testTransferRate ();
|
||||
testToStrand ();
|
||||
testFalseDry();
|
||||
testLimitQuality();
|
||||
testSelfPayment1();
|
||||
testSelfPayment2();
|
||||
testSelfFundedXRPEndpoint(false);
|
||||
testSelfFundedXRPEndpoint(true);
|
||||
testUnfundedOffer(true);
|
||||
testUnfundedOffer(false);
|
||||
testReexecuteDirectStep(featureFlow, fix1368);
|
||||
testRIPD1443(true);
|
||||
testRIPD1443(false);
|
||||
|
||||
auto testWithFeats = [this](auto&&... fs)
|
||||
{
|
||||
testLineQuality({fs...});
|
||||
testFalseDry({fs...});
|
||||
if (!sizeof...(fs))
|
||||
return;
|
||||
testDirectStep({fs...});
|
||||
testBookStep({fs...});
|
||||
testDirectStep({featureOwnerPaysFee, fs...});
|
||||
testBookStep({featureOwnerPaysFee, fs...});
|
||||
testTransferRate({featureOwnerPaysFee, fs...});
|
||||
testSelfPayment1({fs...});
|
||||
testSelfPayment2({fs...});
|
||||
testSelfFundedXRPEndpoint(false, {fs...});
|
||||
testSelfFundedXRPEndpoint(true, {fs...});
|
||||
testUnfundedOffer(true, {fs...});
|
||||
testUnfundedOffer(false, {fs...});
|
||||
testReexecuteDirectStep({fix1368, fs...});
|
||||
};
|
||||
testWithFeats();
|
||||
testWithFeats(featureFlow);
|
||||
testWithFeats(featureFlow, featureToStrandV2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
@@ -52,12 +53,12 @@ class Freeze_test : public beast::unit_test::suite
|
||||
return val.isArray() && val.size() == size;
|
||||
}
|
||||
|
||||
void testRippleState()
|
||||
void testRippleState(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("RippleState Freeze");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
Account G1 {"G1"};
|
||||
Account alice {"alice"};
|
||||
@@ -206,12 +207,12 @@ class Freeze_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testGlobalFreeze()
|
||||
testGlobalFreeze(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("Global Freeze");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
Account G1 {"G1"};
|
||||
Account A1 {"A1"};
|
||||
@@ -364,12 +365,12 @@ class Freeze_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testNoFreeze()
|
||||
testNoFreeze(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("No Freeze");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
Account G1 {"G1"};
|
||||
Account A1 {"A1"};
|
||||
@@ -418,12 +419,12 @@ class Freeze_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testOffersWhenFrozen()
|
||||
testOffersWhenFrozen(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("Offers for Frozen Trust Lines");
|
||||
|
||||
using namespace test::jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
Account G1 {"G1"};
|
||||
Account A2 {"A2"};
|
||||
@@ -522,10 +523,16 @@ public:
|
||||
|
||||
void run()
|
||||
{
|
||||
testRippleState();
|
||||
testGlobalFreeze();
|
||||
testNoFreeze();
|
||||
testOffersWhenFrozen();
|
||||
auto testAll = [this](std::initializer_list<uint256> fs)
|
||||
{
|
||||
testRippleState(fs);
|
||||
testGlobalFreeze(fs);
|
||||
testNoFreeze(fs);
|
||||
testOffersWhenFrozen(fs);
|
||||
};
|
||||
testAll({});
|
||||
testAll({featureFlow});
|
||||
testAll({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ class Offer_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
public:
|
||||
void testRmFundedOffer ()
|
||||
void testRmFundedOffer (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Incorrect Removal of Funded Offers");
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
// still funded and not used for the payment.
|
||||
|
||||
using namespace jtx;
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
|
||||
// ledger close times have a dynamic resolution depending on network
|
||||
// conditions it appears the resolution in test is 10 seconds
|
||||
@@ -151,12 +151,12 @@ public:
|
||||
isOffer (env, carol, BTC (49), XRP (49)));
|
||||
}
|
||||
|
||||
void testCanceledOffer ()
|
||||
void testCanceledOffer (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Removing Canceled Offers");
|
||||
|
||||
using namespace jtx;
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const USD = gw["USD"];
|
||||
@@ -206,7 +206,7 @@ public:
|
||||
BEAST_EXPECT(!isOffer (env, alice, XRP (222), USD (111)));
|
||||
}
|
||||
|
||||
void testTinyPayment ()
|
||||
void testTinyPayment (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Tiny payments");
|
||||
|
||||
@@ -221,7 +221,7 @@ public:
|
||||
auto const USD = gw["USD"];
|
||||
auto const EUR = gw["EUR"];
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
|
||||
env.fund (XRP (10000), alice, bob, carol, gw);
|
||||
env.trust (USD (1000), alice, bob, carol);
|
||||
@@ -248,7 +248,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testXRPTinyPayment ()
|
||||
void testXRPTinyPayment (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("XRP Tiny payments");
|
||||
|
||||
@@ -279,7 +279,10 @@ public:
|
||||
|
||||
for (auto withFix : {false, true})
|
||||
{
|
||||
Env env {*this};
|
||||
if (!withFix && fs.size())
|
||||
continue;
|
||||
|
||||
Env env {*this, features(fs)};
|
||||
|
||||
auto closeTime = [&]
|
||||
{
|
||||
@@ -352,7 +355,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void testEnforceNoRipple ()
|
||||
void testEnforceNoRipple (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Enforce No Ripple");
|
||||
|
||||
@@ -369,7 +372,7 @@ public:
|
||||
|
||||
{
|
||||
// No ripple with an implied account step after an offer
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw1 = Account {"gw1"};
|
||||
auto const USD1 = gw1["USD"];
|
||||
auto const gw2 = Account {"gw2"};
|
||||
@@ -419,7 +422,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testInsufficientReserve ()
|
||||
testInsufficientReserve (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Insufficient Reserve");
|
||||
|
||||
@@ -442,7 +445,7 @@ public:
|
||||
|
||||
// No crossing:
|
||||
{
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (XRP (1000000), gw);
|
||||
|
||||
auto const f = env.current ()->fees ().base;
|
||||
@@ -490,7 +493,7 @@ public:
|
||||
// if an offer were added. Attempt to sell IOUs to
|
||||
// buy XRP. If it fully crosses, we succeed.
|
||||
{
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (XRP (1000000), gw);
|
||||
|
||||
auto const f = env.current ()->fees ().base;
|
||||
@@ -522,7 +525,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testFillModes ()
|
||||
testFillModes (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Fill Modes");
|
||||
|
||||
@@ -537,7 +540,7 @@ public:
|
||||
// Fill or Kill - unless we fully cross, just charge
|
||||
// a fee and not place the offer on the books:
|
||||
{
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (startBalance, gw);
|
||||
|
||||
auto const f = env.current ()->fees ().base;
|
||||
@@ -579,7 +582,7 @@ public:
|
||||
// Immediate or Cancel - cross as much as possible
|
||||
// and add nothing on the books:
|
||||
{
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (startBalance, gw);
|
||||
|
||||
auto const f = env.current ()->fees ().base;
|
||||
@@ -632,7 +635,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testMalformed()
|
||||
testMalformed(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Malformed Detection");
|
||||
|
||||
@@ -643,7 +646,7 @@ public:
|
||||
auto const alice = Account {"alice"};
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (startBalance, gw);
|
||||
|
||||
env.fund (startBalance, alice);
|
||||
@@ -726,7 +729,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testExpiration()
|
||||
testExpiration(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer Expiration");
|
||||
|
||||
@@ -799,7 +802,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testUnfundedCross()
|
||||
testUnfundedCross(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Unfunded Crossing");
|
||||
|
||||
@@ -860,7 +863,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testSelfCross(bool use_partner)
|
||||
testSelfCross(bool use_partner, std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase (std::string("Self-crossing") +
|
||||
(use_partner ? ", with partner account" : ""));
|
||||
@@ -872,7 +875,7 @@ public:
|
||||
auto const USD = gw["USD"];
|
||||
auto const BTC = gw["BTC"];
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env.fund (XRP (10000), gw);
|
||||
if (use_partner)
|
||||
{
|
||||
@@ -964,7 +967,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testNegativeBalance()
|
||||
testNegativeBalance(std::initializer_list<uint256> fs)
|
||||
{
|
||||
// This test creates an offer test for negative balance
|
||||
// with transfer fees and miniscule funds.
|
||||
@@ -972,7 +975,7 @@ public:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1041,7 +1044,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferCrossWithXRP(bool reverse_order)
|
||||
testOfferCrossWithXRP(bool reverse_order, std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase (std::string("Offer Crossing with XRP, ") +
|
||||
(reverse_order ? "Reverse" : "Normal") +
|
||||
@@ -1049,7 +1052,7 @@ public:
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1098,13 +1101,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferCrossWithLimitOverride()
|
||||
testOfferCrossWithLimitOverride(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer Crossing with Limit Override");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1143,13 +1146,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferAcceptThenCancel()
|
||||
testOfferAcceptThenCancel(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer Accept then Cancel.");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const USD = env.master["USD"];
|
||||
|
||||
auto const nextOfferSeq = env.seq (env.master);
|
||||
@@ -1170,14 +1173,14 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferCancelPastAndFuture()
|
||||
testOfferCancelPastAndFuture(std::initializer_list<uint256> fs)
|
||||
{
|
||||
|
||||
testcase ("Offer Cancel Past and Future Sequence.");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const alice = Account {"alice"};
|
||||
|
||||
auto const nextOfferSeq = env.seq (env.master);
|
||||
@@ -1200,13 +1203,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCurrencyConversionEntire()
|
||||
testCurrencyConversionEntire(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Currency Conversion: Entire Offer");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1260,13 +1263,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCurrencyConversionIntoDebt()
|
||||
testCurrencyConversionIntoDebt(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Currency Conversion: Offerer Into Debt");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
auto const carol = Account {"carol"};
|
||||
@@ -1288,13 +1291,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCurrencyConversionInParts()
|
||||
testCurrencyConversionInParts(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Currency Conversion: In Parts");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1375,13 +1378,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCrossCurrencyStartXRP()
|
||||
testCrossCurrencyStartXRP(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Cross Currency Payment: Start with XRP");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1414,13 +1417,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCrossCurrencyEndXRP()
|
||||
testCrossCurrencyEndXRP(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Cross Currency Payment: End with XRP");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1461,13 +1464,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testCrossCurrencyBridged()
|
||||
testCrossCurrencyBridged(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Cross Currency Payment: Bridged");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw1 = Account {"gateway_1"};
|
||||
auto const gw2 = Account {"gateway_2"};
|
||||
auto const alice = Account {"alice"};
|
||||
@@ -1525,13 +1528,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferFeesConsumeFunds()
|
||||
testOfferFeesConsumeFunds(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer Fees Consume Funds");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw1 = Account {"gateway_1"};
|
||||
auto const gw2 = Account {"gateway_2"};
|
||||
auto const gw3 = Account {"gateway_3"};
|
||||
@@ -1578,13 +1581,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testOfferCreateThenCross()
|
||||
testOfferCreateThenCross(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer Create, then Cross");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1613,13 +1616,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testSellFlagBasic()
|
||||
testSellFlagBasic(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer tfSell: Basic Sell");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1654,13 +1657,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testSellFlagExceedLimit()
|
||||
testSellFlagExceedLimit(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Offer tfSell: 2x Sell Exceed Limit");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1697,13 +1700,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
testGatewayCrossCurrency()
|
||||
testGatewayCrossCurrency(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Client Issue #535: Gateway Cross Currency");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const bob = Account {"bob"};
|
||||
@@ -1764,7 +1767,7 @@ public:
|
||||
BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
|
||||
}
|
||||
|
||||
void testTickSize ()
|
||||
void testTickSize (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Tick Size");
|
||||
|
||||
@@ -1772,7 +1775,7 @@ public:
|
||||
|
||||
// Try to set tick size without enabling feature
|
||||
{
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto const gw = Account {"gateway"};
|
||||
env.fund (XRP(10000), gw);
|
||||
|
||||
@@ -1783,7 +1786,7 @@ public:
|
||||
|
||||
// Try to set tick size out of range
|
||||
{
|
||||
Env env {*this, features (featureTickSize)};
|
||||
Env env {*this, features(fs), features (featureTickSize)};
|
||||
auto const gw = Account {"gateway"};
|
||||
env.fund (XRP(10000), gw);
|
||||
|
||||
@@ -1816,7 +1819,7 @@ public:
|
||||
BEAST_EXPECT (! env.le(gw)->isFieldPresent (sfTickSize));
|
||||
}
|
||||
|
||||
Env env {*this, features (featureTickSize)};
|
||||
Env env {*this, features(fs), features (featureTickSize)};
|
||||
auto const gw = Account {"gateway"};
|
||||
auto const alice = Account {"alice"};
|
||||
auto const XTS = gw["XTS"];
|
||||
@@ -1886,36 +1889,41 @@ public:
|
||||
|
||||
void run ()
|
||||
{
|
||||
testCanceledOffer ();
|
||||
testRmFundedOffer ();
|
||||
testTinyPayment ();
|
||||
testXRPTinyPayment ();
|
||||
testEnforceNoRipple ();
|
||||
testInsufficientReserve ();
|
||||
testFillModes ();
|
||||
testMalformed ();
|
||||
testExpiration ();
|
||||
testUnfundedCross ();
|
||||
testSelfCross (false);
|
||||
testSelfCross (true);
|
||||
testNegativeBalance ();
|
||||
testOfferCrossWithXRP (true);
|
||||
testOfferCrossWithXRP (false);
|
||||
testOfferCrossWithLimitOverride ();
|
||||
testOfferAcceptThenCancel ();
|
||||
testOfferCancelPastAndFuture ();
|
||||
testCurrencyConversionEntire ();
|
||||
testCurrencyConversionIntoDebt ();
|
||||
testCurrencyConversionInParts ();
|
||||
testCrossCurrencyStartXRP ();
|
||||
testCrossCurrencyEndXRP ();
|
||||
testCrossCurrencyBridged ();
|
||||
testOfferFeesConsumeFunds ();
|
||||
testOfferCreateThenCross ();
|
||||
testSellFlagBasic ();
|
||||
testSellFlagExceedLimit ();
|
||||
testGatewayCrossCurrency ();
|
||||
testTickSize ();
|
||||
auto testAll = [this](std::initializer_list<uint256> fs) {
|
||||
testCanceledOffer(fs);
|
||||
testRmFundedOffer(fs);
|
||||
testTinyPayment(fs);
|
||||
testXRPTinyPayment(fs);
|
||||
testEnforceNoRipple(fs);
|
||||
testInsufficientReserve(fs);
|
||||
testFillModes(fs);
|
||||
testMalformed(fs);
|
||||
testExpiration(fs);
|
||||
testUnfundedCross(fs);
|
||||
testSelfCross(false, fs);
|
||||
testSelfCross(true, fs);
|
||||
testNegativeBalance(fs);
|
||||
testOfferCrossWithXRP(true, fs);
|
||||
testOfferCrossWithXRP(false, fs);
|
||||
testOfferCrossWithLimitOverride(fs);
|
||||
testOfferAcceptThenCancel(fs);
|
||||
testOfferCancelPastAndFuture(fs);
|
||||
testCurrencyConversionEntire(fs);
|
||||
testCurrencyConversionIntoDebt(fs);
|
||||
testCurrencyConversionInParts(fs);
|
||||
testCrossCurrencyStartXRP(fs);
|
||||
testCrossCurrencyEndXRP(fs);
|
||||
testCrossCurrencyBridged(fs);
|
||||
testOfferFeesConsumeFunds(fs);
|
||||
testOfferCreateThenCross(fs);
|
||||
testSellFlagBasic(fs);
|
||||
testSellFlagExceedLimit(fs);
|
||||
testGatewayCrossCurrency(fs);
|
||||
testTickSize(fs);
|
||||
};
|
||||
testAll({});
|
||||
testAll({featureFlow});
|
||||
testAll({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1416
src/test/app/PayStrand_test.cpp
Normal file
1416
src/test/app/PayStrand_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -46,13 +46,13 @@ struct SetAuth_test : public beast::unit_test::suite
|
||||
return jv;
|
||||
}
|
||||
|
||||
void testAuth()
|
||||
void testAuth(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace jtx;
|
||||
auto const gw = Account("gw");
|
||||
auto const USD = gw["USD"];
|
||||
{
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
env.fund(XRP(100000), "alice", gw);
|
||||
env(fset(gw, asfRequireAuth));
|
||||
env(auth(gw, "alice", "USD"), ter(tecNO_LINE_REDUNDANT));
|
||||
@@ -75,7 +75,9 @@ struct SetAuth_test : public beast::unit_test::suite
|
||||
|
||||
void run() override
|
||||
{
|
||||
testAuth();
|
||||
testAuth({});
|
||||
testAuth({featureFlow});
|
||||
testAuth({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <BeastConfig.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/protocol/SField.h>
|
||||
#include <test/jtx/WSClient.h>
|
||||
@@ -45,12 +46,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
};
|
||||
|
||||
void
|
||||
testPayNonexistent ()
|
||||
testPayNonexistent (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Payment to Nonexistent Account");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
env (pay (env.master, "alice", XRP(1)), ter(tecNO_DST_INSUF_XRP));
|
||||
env.close();
|
||||
}
|
||||
@@ -161,12 +162,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testDirectRipple ()
|
||||
testDirectRipple (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Direct Payment, Ripple");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
Account alice {"alice"};
|
||||
Account bob {"bob"};
|
||||
|
||||
@@ -202,14 +203,14 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testWithTransferFee (bool subscribe, bool with_rate)
|
||||
testWithTransferFee (bool subscribe, bool with_rate, std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase(std::string("Direct Payment: ") +
|
||||
(with_rate ? "With " : "Without ") + " Xfer Fee, " +
|
||||
(subscribe ? "With " : "Without ") + " Subscribe");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
auto wsc = test::makeWSClient(env.app().config());
|
||||
Account gw {"gateway"};
|
||||
Account alice {"alice"};
|
||||
@@ -282,12 +283,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testWithPath ()
|
||||
testWithPath (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Payments With Paths and Fees");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
Account gw {"gateway"};
|
||||
Account alice {"alice"};
|
||||
Account bob {"bob"};
|
||||
@@ -330,12 +331,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testIndirect ()
|
||||
testIndirect (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Indirect Payment");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
Account gw {"gateway"};
|
||||
Account alice {"alice"};
|
||||
Account bob {"bob"};
|
||||
@@ -371,13 +372,13 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testIndirectMultiPath (bool with_rate)
|
||||
testIndirectMultiPath (bool with_rate, std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase (std::string("Indirect Payment, Multi Path, ") +
|
||||
(with_rate ? "With " : "Without ") + " Xfer Fee, ");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
Account gw {"gateway"};
|
||||
Account amazon {"amazon"};
|
||||
Account alice {"alice"};
|
||||
@@ -437,12 +438,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
}
|
||||
|
||||
void
|
||||
testInvoiceID ()
|
||||
testInvoiceID (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Set Invoice ID on Payment");
|
||||
using namespace test::jtx;
|
||||
|
||||
Env env {*this};
|
||||
Env env {*this, features(fs)};
|
||||
Account alice {"alice"};
|
||||
auto wsc = test::makeWSClient(env.app().config());
|
||||
|
||||
@@ -489,19 +490,25 @@ class TrustAndBalance_test : public beast::unit_test::suite
|
||||
public:
|
||||
void run ()
|
||||
{
|
||||
testPayNonexistent ();
|
||||
testTrustNonexistent ();
|
||||
testCreditLimit ();
|
||||
testDirectRipple ();
|
||||
testWithTransferFee (false, false);
|
||||
testWithTransferFee (false, true);
|
||||
testWithTransferFee (true, false);
|
||||
testWithTransferFee (true, true);
|
||||
testWithPath ();
|
||||
testIndirect ();
|
||||
testIndirectMultiPath (true);
|
||||
testIndirectMultiPath (false);
|
||||
testInvoiceID ();
|
||||
|
||||
auto testWithFeatures = [this](std::initializer_list<uint256> fs) {
|
||||
testPayNonexistent(fs);
|
||||
testDirectRipple(fs);
|
||||
testWithTransferFee(false, false, fs);
|
||||
testWithTransferFee(false, true, fs);
|
||||
testWithTransferFee(true, false, fs);
|
||||
testWithTransferFee(true, true, fs);
|
||||
testWithPath(fs);
|
||||
testIndirect(fs);
|
||||
testIndirectMultiPath(true, fs);
|
||||
testIndirectMultiPath(false, fs);
|
||||
testInvoiceID(fs);
|
||||
};
|
||||
testWithFeatures({});
|
||||
testWithFeatures({featureFlow});
|
||||
testWithFeatures({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -73,6 +73,14 @@ features (uint256 const& key, Args const&... args)
|
||||
return {{key, args...}};
|
||||
}
|
||||
|
||||
/** Activate features in the Env ctor */
|
||||
inline
|
||||
auto
|
||||
features (std::initializer_list<uint256> keys)
|
||||
{
|
||||
return keys;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A transaction testing environment. */
|
||||
@@ -125,6 +133,14 @@ private:
|
||||
app().config().features.insert(key);
|
||||
}
|
||||
|
||||
void
|
||||
construct_arg (
|
||||
std::initializer_list<uint256> list)
|
||||
{
|
||||
for(auto const& key : list)
|
||||
app().config().features.insert(key);
|
||||
}
|
||||
|
||||
public:
|
||||
Env() = delete;
|
||||
Env (Env const&) = delete;
|
||||
|
||||
@@ -66,6 +66,7 @@ public:
|
||||
}
|
||||
Path& push_back (Issue const& iss);
|
||||
Path& push_back (jtx::Account const& acc);
|
||||
Path& push_back (STPathElement const& pe);
|
||||
Json::Value json () const;
|
||||
private:
|
||||
Path& addHelper (){return *this;};
|
||||
@@ -73,6 +74,12 @@ public:
|
||||
Path& addHelper (First&& first, Rest&&... rest);
|
||||
};
|
||||
|
||||
inline Path& Path::push_back (STPathElement const& pe)
|
||||
{
|
||||
path.emplace_back (pe);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Path& Path::push_back (Issue const& iss)
|
||||
{
|
||||
path.emplace_back (STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
|
||||
@@ -18,16 +18,17 @@
|
||||
#include <BeastConfig.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/ledger/BookDirs.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct BookDirs_test : public beast::unit_test::suite
|
||||
{
|
||||
void test_bookdir()
|
||||
void test_bookdir(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
auto gw = Account("gw");
|
||||
auto USD = gw["USD"];
|
||||
env.fund(XRP(1000000), "alice", "bob", "gw");
|
||||
@@ -93,7 +94,8 @@ struct BookDirs_test : public beast::unit_test::suite
|
||||
|
||||
void run() override
|
||||
{
|
||||
test_bookdir();
|
||||
test_bookdir({});
|
||||
test_bookdir({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <test/jtx/PathSet.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/protocol/AmountConversions.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -54,12 +55,12 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
2) New code: Path is dry because sender does not have any
|
||||
GW1 to spend until the end of the transaction.
|
||||
*/
|
||||
void testSelfFunding ()
|
||||
void testSelfFunding (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("selfFunding");
|
||||
|
||||
using namespace jtx;
|
||||
Env env (*this);
|
||||
Env env (*this, features(fs));
|
||||
Account const gw1 ("gw1");
|
||||
Account const gw2 ("gw2");
|
||||
Account const snd ("snd");
|
||||
@@ -95,12 +96,12 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
env.require (balance ("rcv", USD_gw2 (2)));
|
||||
}
|
||||
|
||||
void testSubtractCredits ()
|
||||
void testSubtractCredits (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("subtractCredits");
|
||||
|
||||
using namespace jtx;
|
||||
Env env (*this);
|
||||
Env env (*this, features(fs));
|
||||
Account const gw1 ("gw1");
|
||||
Account const gw2 ("gw2");
|
||||
Account const alice ("alice");
|
||||
@@ -255,7 +256,7 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void testTinyBalance ()
|
||||
void testTinyBalance (std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Tiny balance");
|
||||
|
||||
@@ -265,7 +266,7 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
Env env (*this);
|
||||
Env env (*this, features(fs));
|
||||
|
||||
Account const gw ("gw");
|
||||
Account const alice ("alice");
|
||||
@@ -291,7 +292,8 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(pv.balanceHook (alice, gw, hugeAmt) != tinyAmt);
|
||||
}
|
||||
}
|
||||
void testReserve()
|
||||
|
||||
void testReserve(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase ("Reserve");
|
||||
using namespace jtx;
|
||||
@@ -310,7 +312,7 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
return env.current ()->fees ().accountReserve (count);
|
||||
};
|
||||
|
||||
Env env (*this);
|
||||
Env env (*this, features(fs));
|
||||
|
||||
Account const alice ("alice");
|
||||
env.fund (reserve(env, 1), alice);
|
||||
@@ -335,10 +337,14 @@ class PaymentSandbox_test : public beast::unit_test::suite
|
||||
public:
|
||||
void run ()
|
||||
{
|
||||
testSelfFunding ();
|
||||
testSubtractCredits ();
|
||||
testTinyBalance ();
|
||||
testReserve();
|
||||
auto testAll = [this](std::initializer_list<uint256> fs) {
|
||||
testSelfFunding(fs);
|
||||
testSubtractCredits(fs);
|
||||
testTinyBalance(fs);
|
||||
testReserve(fs);
|
||||
};
|
||||
testAll({});
|
||||
testAll({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <test/jtx/WSClient.h>
|
||||
#include <test/jtx.h>
|
||||
@@ -29,11 +30,11 @@ class GatewayBalances_test : public beast::unit_test::suite
|
||||
public:
|
||||
|
||||
void
|
||||
testGWB()
|
||||
testGWB(std::initializer_list<uint256> fs)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
// Gateway account and assets
|
||||
Account const alice {"alice"};
|
||||
@@ -152,7 +153,8 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testGWB();
|
||||
testGWB({});
|
||||
testGWB({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
@@ -66,13 +67,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testNegativeBalance()
|
||||
void testNegativeBalance(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("Set noripple on a line with negative balance");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
auto const gw = Account("gateway");
|
||||
auto const alice = Account("alice");
|
||||
@@ -113,13 +113,12 @@ public:
|
||||
BEAST_EXPECT(!lines[0u].isMember(jss::no_ripple));
|
||||
}
|
||||
|
||||
void
|
||||
testPairwise()
|
||||
void testPairwise(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("pairwise NoRipple");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
@@ -151,13 +150,12 @@ public:
|
||||
env(pay(alice, carol, bob["USD"](50)), ter(tecPATH_DRY));
|
||||
}
|
||||
|
||||
void
|
||||
testDefaultRipple()
|
||||
void testDefaultRipple(std::initializer_list<uint256> fs)
|
||||
{
|
||||
testcase("Set default ripple on an account and check new trustlines");
|
||||
|
||||
using namespace jtx;
|
||||
Env env(*this);
|
||||
Env env(*this, features(fs));
|
||||
|
||||
auto const gw = Account("gateway");
|
||||
auto const alice = Account("alice");
|
||||
@@ -213,9 +211,15 @@ public:
|
||||
void run ()
|
||||
{
|
||||
testSetAndClear();
|
||||
testNegativeBalance();
|
||||
testPairwise();
|
||||
testDefaultRipple();
|
||||
|
||||
auto withFeatsTests = [this](std::initializer_list<uint256> fs) {
|
||||
testNegativeBalance(fs);
|
||||
testPairwise(fs);
|
||||
testDefaultRipple(fs);
|
||||
};
|
||||
withFeatsTests({});
|
||||
withFeatsTests({featureFlow});
|
||||
withFeatsTests({featureFlow, featureToStrandV2});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <test/app/OversizeMeta_test.cpp>
|
||||
#include <test/app/Path_test.cpp>
|
||||
#include <test/app/PayChan_test.cpp>
|
||||
#include <test/app/PayStrand_test.cpp>
|
||||
#include <test/app/Regression_test.cpp>
|
||||
#include <test/app/SetAuth_test.cpp>
|
||||
#include <test/app/SetRegularKey_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user