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:
seelabs
2017-02-08 13:24:36 -05:00
committed by Scott Schurr
parent 80d9b0464a
commit 846723d771
27 changed files with 2172 additions and 646 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -51,7 +51,8 @@ supportedAmendments ()
{ "532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize" },
{ "E2E6F2866106419B88C50045ACE96368558C345566AC8F2BDF5A5B5587F0E6FA fix1368" },
{ "07D43DCE529B15A10827E5E04943B496762F9A88E3268269D69C44BE49E21104 Escrow" },
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" }
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" },
{ "48C4451D6C6A138453F056EB6793AFF4B5C57457A37BA63EF3541FF8CE873DC2 ToStrandV2"}
};
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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,

View File

@@ -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
*/

View File

@@ -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
{

View File

@@ -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

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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);
}
};

View File

@@ -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});
}
};

View File

@@ -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});
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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;

View File

@@ -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,

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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});
}
};

View File

@@ -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>