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)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </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"> <ClCompile Include="..\..\src\test\app\Regression_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|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"> <ClCompile Include="..\..\src\test\app\PayChan_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\test\app\PayStrand_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Regression_test.cpp"> <ClCompile Include="..\..\src\test\app\Regression_test.cpp">
<Filter>test\app</Filter> <Filter>test\app</Filter>
</ClCompile> </ClCompile>

View File

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

View File

@@ -27,6 +27,7 @@
#include <ripple/ledger/Directory.h> #include <ripple/ledger/Directory.h>
#include <ripple/ledger/PaymentSandbox.h> #include <ripple/ledger/PaymentSandbox.h>
#include <ripple/protocol/Book.h> #include <ripple/protocol/Book.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/IOUAmount.h> #include <ripple/protocol/IOUAmount.h>
#include <ripple/protocol/Quality.h> #include <ripple/protocol/Quality.h>
#include <ripple/protocol/XRPAmount.h> #include <ripple/protocol/XRPAmount.h>
@@ -684,6 +685,13 @@ BookStep<TIn, TOut>::check(StrandContext const& ctx) const
return temBAD_PATH_LOOP; 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 (amendmentRIPD1443(ctx.view.info().parentCloseTime))
{ {
if (ctx.prevStep) if (ctx.prevStep)

View File

@@ -124,6 +124,12 @@ class DirectStepI : public StepImp<IOUAmount, IOUAmount, DirectStepI>
return src_; return src_;
} }
boost::optional<std::pair<AccountID,AccountID>>
directStepAccts () const override
{
return std::make_pair(src_, dst_);
}
bool bool
redeems (ReadView const& sb, bool fwd) const override; redeems (ReadView const& sb, bool fwd) const override;

View File

@@ -21,6 +21,8 @@
#include <ripple/app/paths/impl/Steps.h> #include <ripple/app/paths/impl/Steps.h>
#include <ripple/basics/contract.h> #include <ripple/basics/contract.h>
#include <ripple/json/json_writer.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/IOUAmount.h>
#include <ripple/protocol/XRPAmount.h> #include <ripple/protocol/XRPAmount.h>
@@ -102,6 +104,8 @@ toStep (
JLOG (j.warn()) JLOG (j.warn())
<< "Found offer/account payment step. Aborting payment strand."; << "Found offer/account payment step. Aborting payment strand.";
assert (0); assert (0);
if (ctx.view.rules().enabled(featureToStrandV2))
return {temBAD_PATH, std::unique_ptr<Step>{}};
Throw<FlowException> (tefEXCEPTION, "Found offer/account payment step."); Throw<FlowException> (tefEXCEPTION, "Found offer/account payment step.");
} }
@@ -132,7 +136,7 @@ toStep (
} }
std::pair<TER, Strand> std::pair<TER, Strand>
toStrand ( toStrandV1 (
ReadView const& view, ReadView const& view,
AccountID const& src, AccountID const& src,
AccountID const& dst, AccountID const& dst,
@@ -370,6 +374,308 @@ toStrand (
return {tesSUCCESS, std::move (result)}; 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>> std::pair<TER, std::vector<Strand>>
toStrands ( toStrands (
ReadView const& view, ReadView const& view,

View File

@@ -121,6 +121,14 @@ public:
return boost::none; 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, If this step is a DirectStepI and the src redeems to the dst, return true,
otherwise return false. otherwise return false.
@@ -223,6 +231,26 @@ bool operator==(Strand const& lhs, Strand const& rhs)
return true; 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 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 @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 contains an ordered collection of the offer books to use and
accounts to ripple through. accounts to ripple through.
@param addDefaultPath Determines if the default path should be considered
@param l logs to write journal messages to @param l logs to write journal messages to
@return error code and collection of strands @return error code and collection of strands
*/ */

View File

@@ -68,6 +68,14 @@ class XRPEndpointStep : public StepImp<XRPAmount, XRPAmount, XRPEndpointStep>
return acc_; 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> boost::optional<EitherAmount>
cachedIn () const override cachedIn () const override
{ {

View File

@@ -48,6 +48,7 @@ extern uint256 const featureTickSize;
extern uint256 const fix1368; extern uint256 const fix1368;
extern uint256 const featureEscrow; extern uint256 const featureEscrow;
extern uint256 const featureCryptoConditionsSuite; extern uint256 const featureCryptoConditionsSuite;
extern uint256 const featureToStrandV2;
} // ripple } // ripple

View File

@@ -117,6 +117,8 @@ public:
hash_value_ = get_hash (*this); hash_value_ = get_hash (*this);
} }
STPathElement(STPathElement const&) = default;
int int
getNodeType () const getNodeType () const
{ {
@@ -203,8 +205,8 @@ class STPath
public: public:
STPath () = default; STPath () = default;
STPath (std::vector<STPathElement> const& p) STPath (std::vector<STPathElement> p)
: mPath (p) : mPath (std::move(p))
{ } { }
std::vector<STPathElement>::size_type std::vector<STPathElement>::size_type
@@ -279,6 +281,10 @@ public:
return mPath[i]; return mPath[i];
} }
void reserve(size_t s)
{
mPath.reserve(s);
}
private: private:
std::vector<STPathElement> mPath; std::vector<STPathElement> mPath;
}; };

View File

@@ -59,5 +59,6 @@ uint256 const featureTickSize = feature("TickSize");
uint256 const fix1368 = feature("fix1368"); uint256 const fix1368 = feature("fix1368");
uint256 const featureEscrow = feature("Escrow"); uint256 const featureEscrow = feature("Escrow");
uint256 const featureCryptoConditionsSuite = feature("CryptoConditionsSuite"); uint256 const featureCryptoConditionsSuite = feature("CryptoConditionsSuite");
uint256 const featureToStrandV2 = feature("ToStrandV2");
} // ripple } // ripple

View File

@@ -18,6 +18,7 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <test/jtx.h> #include <test/jtx.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/protocol/Feature.h>
namespace ripple { namespace ripple {
namespace test { namespace test {
@@ -41,11 +42,12 @@ private:
} }
public: public:
void void
testStepLimit() testStepLimit(std::initializer_list<uint256> fs)
{ {
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const xrpMax = XRP(100000000000); auto const xrpMax = XRP(100000000000);
auto const gw = Account("gateway"); auto const gw = Account("gateway");
auto const USD = gw["USD"]; auto const USD = gw["USD"];
@@ -74,12 +76,12 @@ public:
balance("bob", USD(0)), owners("bob", 1), balance("bob", USD(0)), owners("bob", 1),
balance("dan", USD(1)), owners("dan", 2))); balance("dan", USD(1)), owners("dan", 2)));
} }
void void
testCrossingLimit() testCrossingLimit(std::initializer_list<uint256> fs)
{ {
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const xrpMax = XRP(100000000000); auto const xrpMax = XRP(100000000000);
auto const gw = Account("gateway"); auto const gw = Account("gateway");
auto const USD = gw["USD"]; auto const USD = gw["USD"];
@@ -105,10 +107,10 @@ public:
} }
void void
testStepAndCrossingLimit() testStepAndCrossingLimit(std::initializer_list<uint256> fs)
{ {
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const xrpMax = XRP(100000000000); auto const xrpMax = XRP(100000000000);
auto const gw = Account("gateway"); auto const gw = Account("gateway");
auto const USD = gw["USD"]; auto const USD = gw["USD"];
@@ -150,9 +152,14 @@ public:
void void
run() run()
{ {
testStepLimit(); auto testAll = [this](std::initializer_list<uint256> fs) {
testCrossingLimit(); testStepLimit(fs);
testStepAndCrossingLimit(); testCrossingLimit(fs);
testStepAndCrossingLimit(fs);
};
testAll({});
testAll({featureFlow});
testAll({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -20,6 +20,7 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <test/jtx.h> #include <test/jtx.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/protocol/Feature.h>
namespace ripple { namespace ripple {
namespace test { namespace test {
@@ -28,7 +29,7 @@ class DeliverMin_test : public beast::unit_test::suite
{ {
public: public:
void 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"); testcase("Convert all of an asset using DeliverMin");
@@ -37,7 +38,7 @@ public:
auto const USD = gw["USD"]; auto const USD = gw["USD"];
{ {
Env env(*this); Env env(*this, features(fs));
env.fund(XRP(10000), "alice", "bob", "carol", gw); env.fund(XRP(10000), "alice", "bob", "carol", gw);
env.trust(USD(100), "alice", "bob", "carol"); env.trust(USD(100), "alice", "bob", "carol");
env(pay("alice", "bob", USD(10)), delivermin(USD(10)), ter(temBAD_AMOUNT)); 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.fund(XRP(10000), "alice", "bob", gw);
env.trust(USD(1000), "alice", "bob"); env.trust(USD(1000), "alice", "bob");
env(pay(gw, "bob", USD(100))); 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.fund(XRP(10000), "alice", "bob", "carol", gw);
env.trust(USD(1000), "bob", "carol"); env.trust(USD(1000), "bob", "carol");
env(pay(gw, "bob", USD(200))); 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.fund(XRP(10000), "alice", "bob", "carol", "dan", gw);
env.trust(USD(1000), "bob", "carol", "dan"); env.trust(USD(1000), "bob", "carol", "dan");
env(pay(gw, "bob", USD(100))); env(pay(gw, "bob", USD(100)));
@@ -110,7 +111,9 @@ public:
void void
run() 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/unit_test.h>
#include <ripple/beast/core/LexicalCast.h> #include <ripple/beast/core/LexicalCast.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/SField.h> #include <ripple/protocol/SField.h>
namespace ripple { 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 // 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. // to verify that the net of balance changes match the fee charged.
void void
testXRPDiscrepancy () testXRPDiscrepancy (std::initializer_list<uint256> fs)
{ {
testcase ("Discrepancy test : XRP Discrepancy"); testcase ("Discrepancy test : XRP Discrepancy");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account A1 {"A1"}; Account A1 {"A1"};
Account A2 {"A2"}; Account A2 {"A2"};
@@ -143,7 +144,9 @@ class Discrepancy_test : public beast::unit_test::suite
public: public:
void run () void run ()
{ {
testXRPDiscrepancy (); testXRPDiscrepancy ({});
testXRPDiscrepancy ({featureFlow});
testXRPDiscrepancy ({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -21,61 +21,16 @@
#include <ripple/app/paths/impl/Steps.h> #include <ripple/app/paths/impl/Steps.h>
#include <ripple/basics/contract.h> #include <ripple/basics/contract.h>
#include <ripple/core/Config.h> #include <ripple/core/Config.h>
#include <ripple/ledger/ApplyViewImpl.h>
#include <ripple/ledger/PaymentSandbox.h> #include <ripple/ledger/PaymentSandbox.h>
#include <ripple/ledger/Sandbox.h> #include <ripple/ledger/Sandbox.h>
#include <test/jtx/PathSet.h> #include <test/jtx/PathSet.h>
#include <ripple/protocol/Feature.h> #include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
namespace ripple { namespace ripple {
namespace test { 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 jtx::PrettyAmount
xrpMinusFee (jtx::Env const& env, std::int64_t xrpAmount) 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); 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 struct Flow_test : public beast::unit_test::suite
{ {
// Account path element static bool hasFeature(uint256 const& feat, std::initializer_list<uint256> args)
static auto APE(AccountID const& a)
{ {
return STPathElement ( for(auto const& f : args)
STPathElement::typeAccount, a, xrpCurrency (), xrpAccount ()); if (f == feat)
}; return true;
return false;
// 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);
}
}
// 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"); testcase ("Direct Step");
@@ -407,7 +68,7 @@ struct Flow_test : public beast::unit_test::suite
auto const USD = gw["USD"]; auto const USD = gw["USD"];
{ {
// Pay USD, trivial path // Pay USD, trivial path
Env env (*this, features(featureFlow), features(featureOwnerPaysFee)); Env env (*this, features(fs));
env.fund (XRP (10000), alice, bob, gw); env.fund (XRP (10000), alice, bob, gw);
env.trust (USD (1000), alice, bob); env.trust (USD (1000), alice, bob);
@@ -417,7 +78,7 @@ struct Flow_test : public beast::unit_test::suite
} }
{ {
// XRP transfer // XRP transfer
Env env (*this, features(featureFlow), features(featureOwnerPaysFee)); Env env (*this, features(fs));
env.fund (XRP (10000), alice, bob); env.fund (XRP (10000), alice, bob);
env (pay (alice, bob, XRP (100))); env (pay (alice, bob, XRP (100)));
@@ -426,7 +87,7 @@ struct Flow_test : public beast::unit_test::suite
} }
{ {
// Partial payments // Partial payments
Env env (*this, features(featureFlow), features(featureOwnerPaysFee)); Env env (*this, features(fs));
env.fund (XRP (10000), alice, bob, gw); env.fund (XRP (10000), alice, bob, gw);
env.trust (USD (1000), alice, bob); 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 // 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.fund (XRP (10000), alice, bob, carol, dan);
env.trust (USDA (10), bob); env.trust (USDA (10), bob);
@@ -455,7 +116,7 @@ struct Flow_test : public beast::unit_test::suite
{ {
// Pay by rippling through accounts, specify path // Pay by rippling through accounts, specify path
// and charge a transfer fee // 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.fund (XRP (10000), alice, bob, carol, dan);
env.trust (USDA (10), bob); 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 // Pay by rippling through accounts, specify path and transfer fee
// Test that the transfer fee is not charged when alice issues // 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.fund (XRP (10000), alice, bob, carol, dan);
env.trust (USDA (10), bob); env.trust (USDA (10), bob);
@@ -489,7 +150,7 @@ struct Flow_test : public beast::unit_test::suite
{ {
// test best quality path is taken // test best quality path is taken
// Paths: A->B->D->E ; A->C->D->E // 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.fund (XRP (10000), alice, bob, carol, dan, erin);
env.trust (USDA (10), bob, carol); env.trust (USDA (10), bob, carol);
@@ -510,7 +171,7 @@ struct Flow_test : public beast::unit_test::suite
} }
{ {
// Limit quality // Limit quality
Env env (*this, features(featureFlow), features(featureOwnerPaysFee)); Env env (*this, features(fs));
env.fund (XRP (10000), alice, bob, carol); env.fund (XRP (10000), alice, bob, carol);
env.trust (USDA (10), bob); 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"); testcase ("Line Quality");
@@ -543,66 +204,63 @@ struct Flow_test : public beast::unit_test::suite
// Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut // Dan -> Bob -> Alice -> Carol; vary bobDanQIn and bobAliceQOut
for (auto bobDanQIn : {80, 100, 120}) for (auto bobDanQIn : {80, 100, 120})
for (auto bobAliceQOut : {80, 100, 120}) for (auto bobAliceQOut : {80, 100, 120})
for (auto const& f : {feature ("nullFeature"), featureFlow}) {
{ if (!hasFeature(featureFlow, fs) && bobDanQIn < 100 &&
if (f != featureFlow && bobDanQIn < 100 && bobAliceQOut < 100) bobAliceQOut < 100)
continue; // Bug in flow v1 continue; // Bug in flow v1
Env env (*this, features (f)); Env env(*this, features(fs));
env.fund (XRP (10000), alice, bob, carol, dan); env.fund(XRP(10000), alice, bob, carol, dan);
env (trust (bob, USDD (100)), qualityInPercent (bobDanQIn)); env(trust(bob, USDD(100)), qualityInPercent(bobDanQIn));
env (trust (bob, USDA (100)), env(trust(bob, USDA(100)), qualityOutPercent(bobAliceQOut));
qualityOutPercent (bobAliceQOut)); env(trust(carol, USDA(100)));
env (trust (carol, USDA (100)));
env (pay (alice, bob, USDA (100))); env(pay(alice, bob, USDA(100)));
env.require (balance (bob, USDA (100))); env.require(balance(bob, USDA(100)));
env (pay (dan, carol, USDA (10)), path (bob), env(pay(dan, carol, USDA(10)),
sendmax (USDD (100)), txflags (tfNoRippleDirect)); path(bob), sendmax(USDD(100)), txflags(tfNoRippleDirect));
env.require (balance (bob, USDA (90))); env.require(balance(bob, USDA(90)));
if (bobAliceQOut > bobDanQIn) if (bobAliceQOut > bobDanQIn)
env.require ( env.require(balance(
balance (bob, USDD (10.0 * double(bobAliceQOut) / bob,
double(bobDanQIn)))); USDD(10.0 * double(bobAliceQOut) / double(bobDanQIn))));
else else
env.require (balance (bob, USDD (10))); env.require(balance(bob, USDD(10)));
env.require (balance (carol, USDA (10))); env.require(balance(carol, USDA(10)));
} }
// bob -> alice -> carol; vary carolAliceQIn // bob -> alice -> carol; vary carolAliceQIn
for (auto carolAliceQIn : {80, 100, 120}) for (auto carolAliceQIn : {80, 100, 120})
for (auto const& f : {feature ("nullFeature"), featureFlow}) {
{ Env env(*this, features(fs));
Env env (*this, features (f)); env.fund(XRP(10000), alice, bob, carol);
env.fund (XRP (10000), alice, bob, carol); env(trust(bob, USDA(10)));
env (trust (bob, USDA (10))); env(trust(carol, USDA(10)), qualityInPercent(carolAliceQIn));
env (trust (carol, USDA (10)), qualityInPercent (carolAliceQIn));
env (pay (alice, bob, USDA (10))); env(pay(alice, bob, USDA(10)));
env.require (balance (bob, USDA (10))); env.require(balance(bob, USDA(10)));
env (pay (bob, carol, USDA (5)), sendmax (USDA (10))); env(pay(bob, carol, USDA(5)), sendmax(USDA(10)));
auto const effectiveQ = auto const effectiveQ =
carolAliceQIn > 100 ? 1.0 : carolAliceQIn / 100.0; 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. // bob -> alice -> carol; bobAliceQOut varies.
for (auto bobAliceQOut : {80, 100, 120}) for (auto bobAliceQOut : {80, 100, 120})
for (auto const& f : {feature ("nullFeature"), featureFlow}) {
{ Env env(*this, features(fs));
Env env (*this, features (f)); env.fund(XRP(10000), alice, bob, carol);
env.fund (XRP (10000), alice, bob, carol); env(trust(bob, USDA(10)), qualityOutPercent(bobAliceQOut));
env (trust (bob, USDA (10)), qualityOutPercent (bobAliceQOut)); env(trust(carol, USDA(10)));
env (trust (carol, USDA (10)));
env (pay (alice, bob, USDA (10))); env(pay(alice, bob, USDA(10)));
env.require (balance (bob, USDA (10))); env.require(balance(bob, USDA(10)));
env (pay (bob, carol, USDA (5)), sendmax (USDA (5))); env(pay(bob, carol, USDA(5)), sendmax(USDA(5)));
env.require (balance (carol, USDA (5))); env.require(balance(carol, USDA(5)));
env.require (balance (bob, USDA (10-5))); env.require(balance(bob, USDA(10 - 5)));
} }
} }
void testBookStep () void testBookStep (std::initializer_list<uint256> fs)
{ {
testcase ("Book Step"); testcase ("Book Step");
@@ -618,7 +276,7 @@ struct Flow_test : public beast::unit_test::suite
{ {
// simple IOU/IOU offer // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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 // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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 // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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 // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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 // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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. When the payment fails `flow` should return the unfunded
// offer. This test is intentionally similar to the one that removes // offer. This test is intentionally similar to the one that removes
// unfunded offers when the payment succeeds. // 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.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); env.trust (USD (1000), alice, bob, carol);
@@ -778,7 +436,15 @@ struct Flow_test : public beast::unit_test::suite
STAmount smax (BTC (61)); STAmount smax (BTC (61));
PaymentSandbox sb (env.current ().get (), tapNONE); PaymentSandbox sb (env.current ().get (), tapNONE);
STPathSet paths; STPathSet paths;
auto IPE = [](Issue const& iss) {
return STPathElement(
STPathElement::typeCurrency | STPathElement::typeIssuer,
xrpAccount(),
iss.currency,
iss.account);
};
{ {
// BTC -> USD // BTC -> USD
STPath p1 ({IPE (USD.issue ())}); STPath p1 ({IPE (USD.issue ())});
paths.push_back (p1); 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 // 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. // pass. This test checks that the payment produces 1 EUR, as expected.
Env env (*this, features (featureFlow), Env env (*this, features (fs));
features (featureOwnerPaysFee));
auto const closeTime = STAmountSO::soTime2 + auto const closeTime = STAmountSO::soTime2 +
100 * env.closed ()->info ().closeTimeResolution; 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"); testcase ("Transfer Rate");
@@ -862,7 +527,7 @@ struct Flow_test : public beast::unit_test::suite
{ {
// Simple payment through a gateway with a // Simple payment through a gateway with a
// transfer rate // transfer rate
Env env (*this, features(featureFlow), features(featureOwnerPaysFee)); Env env (*this, features(fs));
env.fund (XRP (10000), alice, bob, carol, gw); env.fund (XRP (10000), alice, bob, carol, gw);
env(rate(gw, 1.25)); 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 // 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.fund (XRP (10000), alice, bob, carol, gw);
env(rate(gw, 1.25)); env(rate(gw, 1.25));
@@ -886,7 +551,7 @@ struct Flow_test : public beast::unit_test::suite
} }
{ {
// transfer fee on an offer // 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.fund (XRP (10000), alice, bob, carol, gw);
env(rate(gw, 1.25)); env(rate(gw, 1.25));
@@ -904,7 +569,7 @@ struct Flow_test : public beast::unit_test::suite
{ {
// Transfer fee two consecutive offers // 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.fund (XRP (10000), alice, bob, carol, gw);
env(rate(gw, 1.25)); 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 // First pass through a strand redeems, second pass issues, no offers
// limiting step is not an endpoint // 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 USDA = alice["USD"];
auto const USDB = bob["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 // First pass through a strand redeems, second pass issues, through an offer
// limiting step is not an endpoint // 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 USDA = alice["USD"];
auto const USDB = bob["USD"]; auto const USDB = bob["USD"];
Account const dan ("dan"); 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 // 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.fund (XRP (10000), alice, bob, gw);
env(rate(gw, 1.25)); env(rate(gw, 1.25));
@@ -986,9 +651,10 @@ struct Flow_test : public beast::unit_test::suite
balance (alice, xrpMinusFee(env, 10000-100)), balance (alice, xrpMinusFee(env, 10000-100)),
balance (bob, USD (100))); balance (bob, USD (100)));
} }
if (!hasFeature(featureOwnerPaysFee, fs))
{ {
// Offer where the owner is also the issuer, sender pays fee // 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.fund (XRP (10000), alice, bob, gw);
env(rate(gw, 1.25)); env(rate(gw, 1.25));
@@ -1003,8 +669,10 @@ struct Flow_test : public beast::unit_test::suite
} }
void void
testFalseDryHelper (jtx::Env& env) testFalseDry(std::initializer_list<uint256> fs)
{ {
testcase ("falseDryChanges");
using namespace jtx; using namespace jtx;
auto const gw = Account ("gateway"); auto const gw = Account ("gateway");
@@ -1014,6 +682,8 @@ struct Flow_test : public beast::unit_test::suite
Account const bob ("bob"); Account const bob ("bob");
Account const carol ("carol"); Account const carol ("carol");
Env env (*this, features (fs));
auto const closeTime = amendmentRIPD1141SoTime() + auto const closeTime = amendmentRIPD1141SoTime() +
100 * env.closed ()->info ().closeTimeResolution; 100 * env.closed ()->info ().closeTimeResolution;
env.close (closeTime); env.close (closeTime);
@@ -1045,21 +715,6 @@ struct Flow_test : public beast::unit_test::suite
BEAST_EXPECT(carolUSD > USD (0) && carolUSD < USD (50)); 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 void
testLimitQuality () testLimitQuality ()
{ {
@@ -1123,7 +778,7 @@ struct Flow_test : public beast::unit_test::suite
} }
void void
testSelfPayment1() testSelfPayment1(std::initializer_list<uint256> fs)
{ {
testcase ("Self-payment 1"); testcase ("Self-payment 1");
@@ -1140,7 +795,7 @@ struct Flow_test : public beast::unit_test::suite
auto const USD = gw1["USD"]; auto const USD = gw1["USD"];
auto const EUR = gw2["EUR"]; auto const EUR = gw2["EUR"];
Env env (*this, features (featureFlow)); Env env (*this, features (fs));
auto const closeTime = auto const closeTime =
amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution; amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution;
@@ -1199,7 +854,7 @@ struct Flow_test : public beast::unit_test::suite
} }
void void
testSelfPayment2() testSelfPayment2(std::initializer_list<uint256> fs)
{ {
testcase ("Self-payment 2"); testcase ("Self-payment 2");
@@ -1214,7 +869,7 @@ struct Flow_test : public beast::unit_test::suite
auto const USD = gw1["USD"]; auto const USD = gw1["USD"];
auto const EUR = gw2["EUR"]; auto const EUR = gw2["EUR"];
Env env (*this, features (featureFlow)); Env env (*this, features (fs));
auto const closeTime = auto const closeTime =
amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution; amendmentRIPD1141SoTime () + 100 * env.closed ()->info ().closeTimeResolution;
@@ -1271,7 +926,7 @@ struct Flow_test : public beast::unit_test::suite
BEAST_EXPECT(offer[sfTakerPays] == USD (495)); 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 // Test that the deferred credit table is not bypassed for
// XRPEndpointSteps. If the account in the first step is sending XRP and // 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; using namespace jtx;
Env env(*this, features(featureFlow)); Env env(*this, features(fs));
// Need new behavior from `accountHolds` // Need new behavior from `accountHolds`
auto const closeTime = amendmentRIPD1141SoTime() + auto const closeTime = amendmentRIPD1141SoTime() +
@@ -1305,7 +960,7 @@ struct Flow_test : public beast::unit_test::suite
txflags(tfPartialPayment | tfNoRippleDirect)); txflags(tfPartialPayment | tfNoRippleDirect));
} }
void testUnfundedOffer (bool withFix) void testUnfundedOffer (bool withFix, std::initializer_list<uint256> fs)
{ {
testcase(std::string("Unfunded Offer ") + testcase(std::string("Unfunded Offer ") +
(withFix ? "with fix" : "without fix")); (withFix ? "with fix" : "without fix"));
@@ -1313,7 +968,7 @@ struct Flow_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
{ {
// Test reverse // Test reverse
Env env(*this, features(featureFlow)); Env env(*this, features(fs));
auto closeTime = amendmentRIPD1298SoTime(); auto closeTime = amendmentRIPD1298SoTime();
if (withFix) if (withFix)
closeTime += env.closed()->info().closeTimeResolution; closeTime += env.closed()->info().closeTimeResolution;
@@ -1345,7 +1000,7 @@ struct Flow_test : public beast::unit_test::suite
} }
{ {
// Test forward // Test forward
Env env(*this, features(featureFlow)); Env env(*this, features(fs));
auto closeTime = amendmentRIPD1298SoTime(); auto closeTime = amendmentRIPD1298SoTime();
if (withFix) if (withFix)
closeTime += env.closed()->info().closeTimeResolution; closeTime += env.closed()->info().closeTimeResolution;
@@ -1379,14 +1034,13 @@ struct Flow_test : public beast::unit_test::suite
} }
} }
template<class... Features>
void void
testReexecuteDirectStep(Features&&... fs) testReexecuteDirectStep(std::initializer_list<uint256> fs)
{ {
testcase("ReexecuteDirectStep"); testcase("ReexecuteDirectStep");
using namespace jtx; using namespace jtx;
Env env(*this, features(fs)...); Env env(*this, features(fs));
auto const alice = Account("alice"); auto const alice = Account("alice");
auto const bob = Account("bob"); auto const bob = Account("bob");
@@ -1488,22 +1142,32 @@ struct Flow_test : public beast::unit_test::suite
void run() override void run() override
{ {
testDirectStep ();
testLineQuality();
testBookStep ();
testTransferRate ();
testToStrand ();
testFalseDry();
testLimitQuality(); testLimitQuality();
testSelfPayment1();
testSelfPayment2();
testSelfFundedXRPEndpoint(false);
testSelfFundedXRPEndpoint(true);
testUnfundedOffer(true);
testUnfundedOffer(false);
testReexecuteDirectStep(featureFlow, fix1368);
testRIPD1443(true); testRIPD1443(true);
testRIPD1443(false); 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 <test/jtx.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/TxFlags.h> #include <ripple/protocol/TxFlags.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/SField.h> #include <ripple/protocol/SField.h>
@@ -52,12 +53,12 @@ class Freeze_test : public beast::unit_test::suite
return val.isArray() && val.size() == size; return val.isArray() && val.size() == size;
} }
void testRippleState() void testRippleState(std::initializer_list<uint256> fs)
{ {
testcase("RippleState Freeze"); testcase("RippleState Freeze");
using namespace test::jtx; using namespace test::jtx;
Env env(*this); Env env(*this, features(fs));
Account G1 {"G1"}; Account G1 {"G1"};
Account alice {"alice"}; Account alice {"alice"};
@@ -206,12 +207,12 @@ class Freeze_test : public beast::unit_test::suite
} }
void void
testGlobalFreeze() testGlobalFreeze(std::initializer_list<uint256> fs)
{ {
testcase("Global Freeze"); testcase("Global Freeze");
using namespace test::jtx; using namespace test::jtx;
Env env(*this); Env env(*this, features(fs));
Account G1 {"G1"}; Account G1 {"G1"};
Account A1 {"A1"}; Account A1 {"A1"};
@@ -364,12 +365,12 @@ class Freeze_test : public beast::unit_test::suite
} }
void void
testNoFreeze() testNoFreeze(std::initializer_list<uint256> fs)
{ {
testcase("No Freeze"); testcase("No Freeze");
using namespace test::jtx; using namespace test::jtx;
Env env(*this); Env env(*this, features(fs));
Account G1 {"G1"}; Account G1 {"G1"};
Account A1 {"A1"}; Account A1 {"A1"};
@@ -418,12 +419,12 @@ class Freeze_test : public beast::unit_test::suite
} }
void void
testOffersWhenFrozen() testOffersWhenFrozen(std::initializer_list<uint256> fs)
{ {
testcase("Offers for Frozen Trust Lines"); testcase("Offers for Frozen Trust Lines");
using namespace test::jtx; using namespace test::jtx;
Env env(*this); Env env(*this, features(fs));
Account G1 {"G1"}; Account G1 {"G1"};
Account A2 {"A2"}; Account A2 {"A2"};
@@ -522,10 +523,16 @@ public:
void run() void run()
{ {
testRippleState(); auto testAll = [this](std::initializer_list<uint256> fs)
testGlobalFreeze(); {
testNoFreeze(); testRippleState(fs);
testOffersWhenFrozen(); 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: public:
void testRmFundedOffer () void testRmFundedOffer (std::initializer_list<uint256> fs)
{ {
testcase ("Incorrect Removal of Funded Offers"); testcase ("Incorrect Removal of Funded Offers");
@@ -105,7 +105,7 @@ public:
// still funded and not used for the payment. // still funded and not used for the payment.
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
// ledger close times have a dynamic resolution depending on network // ledger close times have a dynamic resolution depending on network
// conditions it appears the resolution in test is 10 seconds // conditions it appears the resolution in test is 10 seconds
@@ -151,12 +151,12 @@ public:
isOffer (env, carol, BTC (49), XRP (49))); isOffer (env, carol, BTC (49), XRP (49)));
} }
void testCanceledOffer () void testCanceledOffer (std::initializer_list<uint256> fs)
{ {
testcase ("Removing Canceled Offers"); testcase ("Removing Canceled Offers");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const USD = gw["USD"]; auto const USD = gw["USD"];
@@ -206,7 +206,7 @@ public:
BEAST_EXPECT(!isOffer (env, alice, XRP (222), USD (111))); BEAST_EXPECT(!isOffer (env, alice, XRP (222), USD (111)));
} }
void testTinyPayment () void testTinyPayment (std::initializer_list<uint256> fs)
{ {
testcase ("Tiny payments"); testcase ("Tiny payments");
@@ -221,7 +221,7 @@ public:
auto const USD = gw["USD"]; auto const USD = gw["USD"];
auto const EUR = gw["EUR"]; auto const EUR = gw["EUR"];
Env env {*this}; Env env {*this, features(fs)};
env.fund (XRP (10000), alice, bob, carol, gw); env.fund (XRP (10000), alice, bob, carol, gw);
env.trust (USD (1000), alice, bob, carol); 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"); testcase ("XRP Tiny payments");
@@ -279,7 +279,10 @@ public:
for (auto withFix : {false, true}) for (auto withFix : {false, true})
{ {
Env env {*this}; if (!withFix && fs.size())
continue;
Env env {*this, features(fs)};
auto closeTime = [&] auto closeTime = [&]
{ {
@@ -352,7 +355,7 @@ public:
} }
} }
void testEnforceNoRipple () void testEnforceNoRipple (std::initializer_list<uint256> fs)
{ {
testcase ("Enforce No Ripple"); testcase ("Enforce No Ripple");
@@ -369,7 +372,7 @@ public:
{ {
// No ripple with an implied account step after an offer // 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 gw1 = Account {"gw1"};
auto const USD1 = gw1["USD"]; auto const USD1 = gw1["USD"];
auto const gw2 = Account {"gw2"}; auto const gw2 = Account {"gw2"};
@@ -419,7 +422,7 @@ public:
} }
void void
testInsufficientReserve () testInsufficientReserve (std::initializer_list<uint256> fs)
{ {
testcase ("Insufficient Reserve"); testcase ("Insufficient Reserve");
@@ -442,7 +445,7 @@ public:
// No crossing: // No crossing:
{ {
Env env {*this}; Env env {*this, features(fs)};
env.fund (XRP (1000000), gw); env.fund (XRP (1000000), gw);
auto const f = env.current ()->fees ().base; auto const f = env.current ()->fees ().base;
@@ -490,7 +493,7 @@ public:
// if an offer were added. Attempt to sell IOUs to // if an offer were added. Attempt to sell IOUs to
// buy XRP. If it fully crosses, we succeed. // buy XRP. If it fully crosses, we succeed.
{ {
Env env {*this}; Env env {*this, features(fs)};
env.fund (XRP (1000000), gw); env.fund (XRP (1000000), gw);
auto const f = env.current ()->fees ().base; auto const f = env.current ()->fees ().base;
@@ -522,7 +525,7 @@ public:
} }
void void
testFillModes () testFillModes (std::initializer_list<uint256> fs)
{ {
testcase ("Fill Modes"); testcase ("Fill Modes");
@@ -537,7 +540,7 @@ public:
// Fill or Kill - unless we fully cross, just charge // Fill or Kill - unless we fully cross, just charge
// a fee and not place the offer on the books: // a fee and not place the offer on the books:
{ {
Env env {*this}; Env env {*this, features(fs)};
env.fund (startBalance, gw); env.fund (startBalance, gw);
auto const f = env.current ()->fees ().base; auto const f = env.current ()->fees ().base;
@@ -579,7 +582,7 @@ public:
// Immediate or Cancel - cross as much as possible // Immediate or Cancel - cross as much as possible
// and add nothing on the books: // and add nothing on the books:
{ {
Env env {*this}; Env env {*this, features(fs)};
env.fund (startBalance, gw); env.fund (startBalance, gw);
auto const f = env.current ()->fees ().base; auto const f = env.current ()->fees ().base;
@@ -632,7 +635,7 @@ public:
} }
void void
testMalformed() testMalformed(std::initializer_list<uint256> fs)
{ {
testcase ("Malformed Detection"); testcase ("Malformed Detection");
@@ -643,7 +646,7 @@ public:
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const USD = gw["USD"]; auto const USD = gw["USD"];
Env env {*this}; Env env {*this, features(fs)};
env.fund (startBalance, gw); env.fund (startBalance, gw);
env.fund (startBalance, alice); env.fund (startBalance, alice);
@@ -726,7 +729,7 @@ public:
} }
void void
testExpiration() testExpiration(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Expiration"); testcase ("Offer Expiration");
@@ -799,7 +802,7 @@ public:
} }
void void
testUnfundedCross() testUnfundedCross(std::initializer_list<uint256> fs)
{ {
testcase ("Unfunded Crossing"); testcase ("Unfunded Crossing");
@@ -860,7 +863,7 @@ public:
} }
void void
testSelfCross(bool use_partner) testSelfCross(bool use_partner, std::initializer_list<uint256> fs)
{ {
testcase (std::string("Self-crossing") + testcase (std::string("Self-crossing") +
(use_partner ? ", with partner account" : "")); (use_partner ? ", with partner account" : ""));
@@ -872,7 +875,7 @@ public:
auto const USD = gw["USD"]; auto const USD = gw["USD"];
auto const BTC = gw["BTC"]; auto const BTC = gw["BTC"];
Env env {*this}; Env env {*this, features(fs)};
env.fund (XRP (10000), gw); env.fund (XRP (10000), gw);
if (use_partner) if (use_partner)
{ {
@@ -964,7 +967,7 @@ public:
} }
void void
testNegativeBalance() testNegativeBalance(std::initializer_list<uint256> fs)
{ {
// This test creates an offer test for negative balance // This test creates an offer test for negative balance
// with transfer fees and miniscule funds. // with transfer fees and miniscule funds.
@@ -972,7 +975,7 @@ public:
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1041,7 +1044,7 @@ public:
} }
void void
testOfferCrossWithXRP(bool reverse_order) testOfferCrossWithXRP(bool reverse_order, std::initializer_list<uint256> fs)
{ {
testcase (std::string("Offer Crossing with XRP, ") + testcase (std::string("Offer Crossing with XRP, ") +
(reverse_order ? "Reverse" : "Normal") + (reverse_order ? "Reverse" : "Normal") +
@@ -1049,7 +1052,7 @@ public:
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1098,13 +1101,13 @@ public:
} }
void void
testOfferCrossWithLimitOverride() testOfferCrossWithLimitOverride(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Crossing with Limit Override"); testcase ("Offer Crossing with Limit Override");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1143,13 +1146,13 @@ public:
} }
void void
testOfferAcceptThenCancel() testOfferAcceptThenCancel(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Accept then Cancel."); testcase ("Offer Accept then Cancel.");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const USD = env.master["USD"]; auto const USD = env.master["USD"];
auto const nextOfferSeq = env.seq (env.master); auto const nextOfferSeq = env.seq (env.master);
@@ -1170,14 +1173,14 @@ public:
} }
void void
testOfferCancelPastAndFuture() testOfferCancelPastAndFuture(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Cancel Past and Future Sequence."); testcase ("Offer Cancel Past and Future Sequence.");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const nextOfferSeq = env.seq (env.master); auto const nextOfferSeq = env.seq (env.master);
@@ -1200,13 +1203,13 @@ public:
} }
void void
testCurrencyConversionEntire() testCurrencyConversionEntire(std::initializer_list<uint256> fs)
{ {
testcase ("Currency Conversion: Entire Offer"); testcase ("Currency Conversion: Entire Offer");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1260,13 +1263,13 @@ public:
} }
void void
testCurrencyConversionIntoDebt() testCurrencyConversionIntoDebt(std::initializer_list<uint256> fs)
{ {
testcase ("Currency Conversion: Offerer Into Debt"); testcase ("Currency Conversion: Offerer Into Debt");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
auto const carol = Account {"carol"}; auto const carol = Account {"carol"};
@@ -1288,13 +1291,13 @@ public:
} }
void void
testCurrencyConversionInParts() testCurrencyConversionInParts(std::initializer_list<uint256> fs)
{ {
testcase ("Currency Conversion: In Parts"); testcase ("Currency Conversion: In Parts");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1375,13 +1378,13 @@ public:
} }
void void
testCrossCurrencyStartXRP() testCrossCurrencyStartXRP(std::initializer_list<uint256> fs)
{ {
testcase ("Cross Currency Payment: Start with XRP"); testcase ("Cross Currency Payment: Start with XRP");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1414,13 +1417,13 @@ public:
} }
void void
testCrossCurrencyEndXRP() testCrossCurrencyEndXRP(std::initializer_list<uint256> fs)
{ {
testcase ("Cross Currency Payment: End with XRP"); testcase ("Cross Currency Payment: End with XRP");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1461,13 +1464,13 @@ public:
} }
void void
testCrossCurrencyBridged() testCrossCurrencyBridged(std::initializer_list<uint256> fs)
{ {
testcase ("Cross Currency Payment: Bridged"); testcase ("Cross Currency Payment: Bridged");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw1 = Account {"gateway_1"}; auto const gw1 = Account {"gateway_1"};
auto const gw2 = Account {"gateway_2"}; auto const gw2 = Account {"gateway_2"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
@@ -1525,13 +1528,13 @@ public:
} }
void void
testOfferFeesConsumeFunds() testOfferFeesConsumeFunds(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Fees Consume Funds"); testcase ("Offer Fees Consume Funds");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw1 = Account {"gateway_1"}; auto const gw1 = Account {"gateway_1"};
auto const gw2 = Account {"gateway_2"}; auto const gw2 = Account {"gateway_2"};
auto const gw3 = Account {"gateway_3"}; auto const gw3 = Account {"gateway_3"};
@@ -1578,13 +1581,13 @@ public:
} }
void void
testOfferCreateThenCross() testOfferCreateThenCross(std::initializer_list<uint256> fs)
{ {
testcase ("Offer Create, then Cross"); testcase ("Offer Create, then Cross");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1613,13 +1616,13 @@ public:
} }
void void
testSellFlagBasic() testSellFlagBasic(std::initializer_list<uint256> fs)
{ {
testcase ("Offer tfSell: Basic Sell"); testcase ("Offer tfSell: Basic Sell");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1654,13 +1657,13 @@ public:
} }
void void
testSellFlagExceedLimit() testSellFlagExceedLimit(std::initializer_list<uint256> fs)
{ {
testcase ("Offer tfSell: 2x Sell Exceed Limit"); testcase ("Offer tfSell: 2x Sell Exceed Limit");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1697,13 +1700,13 @@ public:
} }
void void
testGatewayCrossCurrency() testGatewayCrossCurrency(std::initializer_list<uint256> fs)
{ {
testcase ("Client Issue #535: Gateway Cross Currency"); testcase ("Client Issue #535: Gateway Cross Currency");
using namespace jtx; using namespace jtx;
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const bob = Account {"bob"}; auto const bob = Account {"bob"};
@@ -1764,7 +1767,7 @@ public:
BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101"); BEAST_EXPECT(jrr[jss::node][sfBalance.fieldName][jss::value] == "-101");
} }
void testTickSize () void testTickSize (std::initializer_list<uint256> fs)
{ {
testcase ("Tick Size"); testcase ("Tick Size");
@@ -1772,7 +1775,7 @@ public:
// Try to set tick size without enabling feature // Try to set tick size without enabling feature
{ {
Env env {*this}; Env env {*this, features(fs)};
auto const gw = Account {"gateway"}; auto const gw = Account {"gateway"};
env.fund (XRP(10000), gw); env.fund (XRP(10000), gw);
@@ -1783,7 +1786,7 @@ public:
// Try to set tick size out of range // 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"}; auto const gw = Account {"gateway"};
env.fund (XRP(10000), gw); env.fund (XRP(10000), gw);
@@ -1816,7 +1819,7 @@ public:
BEAST_EXPECT (! env.le(gw)->isFieldPresent (sfTickSize)); 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 gw = Account {"gateway"};
auto const alice = Account {"alice"}; auto const alice = Account {"alice"};
auto const XTS = gw["XTS"]; auto const XTS = gw["XTS"];
@@ -1886,36 +1889,41 @@ public:
void run () void run ()
{ {
testCanceledOffer (); auto testAll = [this](std::initializer_list<uint256> fs) {
testRmFundedOffer (); testCanceledOffer(fs);
testTinyPayment (); testRmFundedOffer(fs);
testXRPTinyPayment (); testTinyPayment(fs);
testEnforceNoRipple (); testXRPTinyPayment(fs);
testInsufficientReserve (); testEnforceNoRipple(fs);
testFillModes (); testInsufficientReserve(fs);
testMalformed (); testFillModes(fs);
testExpiration (); testMalformed(fs);
testUnfundedCross (); testExpiration(fs);
testSelfCross (false); testUnfundedCross(fs);
testSelfCross (true); testSelfCross(false, fs);
testNegativeBalance (); testSelfCross(true, fs);
testOfferCrossWithXRP (true); testNegativeBalance(fs);
testOfferCrossWithXRP (false); testOfferCrossWithXRP(true, fs);
testOfferCrossWithLimitOverride (); testOfferCrossWithXRP(false, fs);
testOfferAcceptThenCancel (); testOfferCrossWithLimitOverride(fs);
testOfferCancelPastAndFuture (); testOfferAcceptThenCancel(fs);
testCurrencyConversionEntire (); testOfferCancelPastAndFuture(fs);
testCurrencyConversionIntoDebt (); testCurrencyConversionEntire(fs);
testCurrencyConversionInParts (); testCurrencyConversionIntoDebt(fs);
testCrossCurrencyStartXRP (); testCurrencyConversionInParts(fs);
testCrossCurrencyEndXRP (); testCrossCurrencyStartXRP(fs);
testCrossCurrencyBridged (); testCrossCurrencyEndXRP(fs);
testOfferFeesConsumeFunds (); testCrossCurrencyBridged(fs);
testOfferCreateThenCross (); testOfferFeesConsumeFunds(fs);
testSellFlagBasic (); testOfferCreateThenCross(fs);
testSellFlagExceedLimit (); testSellFlagBasic(fs);
testGatewayCrossCurrency (); testSellFlagExceedLimit(fs);
testTickSize (); 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; return jv;
} }
void testAuth() void testAuth(std::initializer_list<uint256> fs)
{ {
using namespace jtx; using namespace jtx;
auto const gw = Account("gw"); auto const gw = Account("gw");
auto const USD = gw["USD"]; auto const USD = gw["USD"];
{ {
Env env(*this); Env env(*this, features(fs));
env.fund(XRP(100000), "alice", gw); env.fund(XRP(100000), "alice", gw);
env(fset(gw, asfRequireAuth)); env(fset(gw, asfRequireAuth));
env(auth(gw, "alice", "USD"), ter(tecNO_LINE_REDUNDANT)); env(auth(gw, "alice", "USD"), ter(tecNO_LINE_REDUNDANT));
@@ -75,7 +75,9 @@ struct SetAuth_test : public beast::unit_test::suite
void run() override void run() override
{ {
testAuth(); testAuth({});
testAuth({featureFlow});
testAuth({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -20,6 +20,7 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <test/jtx.h> #include <test/jtx.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/SField.h> #include <ripple/protocol/SField.h>
#include <test/jtx/WSClient.h> #include <test/jtx/WSClient.h>
@@ -45,12 +46,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
}; };
void void
testPayNonexistent () testPayNonexistent (std::initializer_list<uint256> fs)
{ {
testcase ("Payment to Nonexistent Account"); testcase ("Payment to Nonexistent Account");
using namespace test::jtx; 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 (pay (env.master, "alice", XRP(1)), ter(tecNO_DST_INSUF_XRP));
env.close(); env.close();
} }
@@ -161,12 +162,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testDirectRipple () testDirectRipple (std::initializer_list<uint256> fs)
{ {
testcase ("Direct Payment, Ripple"); testcase ("Direct Payment, Ripple");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account alice {"alice"}; Account alice {"alice"};
Account bob {"bob"}; Account bob {"bob"};
@@ -202,14 +203,14 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testWithTransferFee (bool subscribe, bool with_rate) testWithTransferFee (bool subscribe, bool with_rate, std::initializer_list<uint256> fs)
{ {
testcase(std::string("Direct Payment: ") + testcase(std::string("Direct Payment: ") +
(with_rate ? "With " : "Without ") + " Xfer Fee, " + (with_rate ? "With " : "Without ") + " Xfer Fee, " +
(subscribe ? "With " : "Without ") + " Subscribe"); (subscribe ? "With " : "Without ") + " Subscribe");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
auto wsc = test::makeWSClient(env.app().config()); auto wsc = test::makeWSClient(env.app().config());
Account gw {"gateway"}; Account gw {"gateway"};
Account alice {"alice"}; Account alice {"alice"};
@@ -282,12 +283,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testWithPath () testWithPath (std::initializer_list<uint256> fs)
{ {
testcase ("Payments With Paths and Fees"); testcase ("Payments With Paths and Fees");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account gw {"gateway"}; Account gw {"gateway"};
Account alice {"alice"}; Account alice {"alice"};
Account bob {"bob"}; Account bob {"bob"};
@@ -330,12 +331,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testIndirect () testIndirect (std::initializer_list<uint256> fs)
{ {
testcase ("Indirect Payment"); testcase ("Indirect Payment");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account gw {"gateway"}; Account gw {"gateway"};
Account alice {"alice"}; Account alice {"alice"};
Account bob {"bob"}; Account bob {"bob"};
@@ -371,13 +372,13 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testIndirectMultiPath (bool with_rate) testIndirectMultiPath (bool with_rate, std::initializer_list<uint256> fs)
{ {
testcase (std::string("Indirect Payment, Multi Path, ") + testcase (std::string("Indirect Payment, Multi Path, ") +
(with_rate ? "With " : "Without ") + " Xfer Fee, "); (with_rate ? "With " : "Without ") + " Xfer Fee, ");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account gw {"gateway"}; Account gw {"gateway"};
Account amazon {"amazon"}; Account amazon {"amazon"};
Account alice {"alice"}; Account alice {"alice"};
@@ -437,12 +438,12 @@ class TrustAndBalance_test : public beast::unit_test::suite
} }
void void
testInvoiceID () testInvoiceID (std::initializer_list<uint256> fs)
{ {
testcase ("Set Invoice ID on Payment"); testcase ("Set Invoice ID on Payment");
using namespace test::jtx; using namespace test::jtx;
Env env {*this}; Env env {*this, features(fs)};
Account alice {"alice"}; Account alice {"alice"};
auto wsc = test::makeWSClient(env.app().config()); auto wsc = test::makeWSClient(env.app().config());
@@ -489,19 +490,25 @@ class TrustAndBalance_test : public beast::unit_test::suite
public: public:
void run () void run ()
{ {
testPayNonexistent ();
testTrustNonexistent (); testTrustNonexistent ();
testCreditLimit (); testCreditLimit ();
testDirectRipple ();
testWithTransferFee (false, false); auto testWithFeatures = [this](std::initializer_list<uint256> fs) {
testWithTransferFee (false, true); testPayNonexistent(fs);
testWithTransferFee (true, false); testDirectRipple(fs);
testWithTransferFee (true, true); testWithTransferFee(false, false, fs);
testWithPath (); testWithTransferFee(false, true, fs);
testIndirect (); testWithTransferFee(true, false, fs);
testIndirectMultiPath (true); testWithTransferFee(true, true, fs);
testIndirectMultiPath (false); testWithPath(fs);
testInvoiceID (); 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...}}; return {{key, args...}};
} }
/** Activate features in the Env ctor */
inline
auto
features (std::initializer_list<uint256> keys)
{
return keys;
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** A transaction testing environment. */ /** A transaction testing environment. */
@@ -125,6 +133,14 @@ private:
app().config().features.insert(key); app().config().features.insert(key);
} }
void
construct_arg (
std::initializer_list<uint256> list)
{
for(auto const& key : list)
app().config().features.insert(key);
}
public: public:
Env() = delete; Env() = delete;
Env (Env const&) = delete; Env (Env const&) = delete;

View File

@@ -66,6 +66,7 @@ public:
} }
Path& push_back (Issue const& iss); Path& push_back (Issue const& iss);
Path& push_back (jtx::Account const& acc); Path& push_back (jtx::Account const& acc);
Path& push_back (STPathElement const& pe);
Json::Value json () const; Json::Value json () const;
private: private:
Path& addHelper (){return *this;}; Path& addHelper (){return *this;};
@@ -73,6 +74,12 @@ public:
Path& addHelper (First&& first, Rest&&... rest); 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) inline Path& Path::push_back (Issue const& iss)
{ {
path.emplace_back (STPathElement::typeCurrency | STPathElement::typeIssuer, path.emplace_back (STPathElement::typeCurrency | STPathElement::typeIssuer,

View File

@@ -18,16 +18,17 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <test/jtx.h> #include <test/jtx.h>
#include <ripple/ledger/BookDirs.h> #include <ripple/ledger/BookDirs.h>
#include <ripple/protocol/Feature.h>
namespace ripple { namespace ripple {
namespace test { namespace test {
struct BookDirs_test : public beast::unit_test::suite struct BookDirs_test : public beast::unit_test::suite
{ {
void test_bookdir() void test_bookdir(std::initializer_list<uint256> fs)
{ {
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto gw = Account("gw"); auto gw = Account("gw");
auto USD = gw["USD"]; auto USD = gw["USD"];
env.fund(XRP(1000000), "alice", "bob", "gw"); env.fund(XRP(1000000), "alice", "bob", "gw");
@@ -93,7 +94,8 @@ struct BookDirs_test : public beast::unit_test::suite
void run() override void run() override
{ {
test_bookdir(); test_bookdir({});
test_bookdir({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -23,6 +23,7 @@
#include <test/jtx/PathSet.h> #include <test/jtx/PathSet.h>
#include <ripple/ledger/View.h> #include <ripple/ledger/View.h>
#include <ripple/protocol/AmountConversions.h> #include <ripple/protocol/AmountConversions.h>
#include <ripple/protocol/Feature.h>
namespace ripple { namespace ripple {
namespace test { 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 2) New code: Path is dry because sender does not have any
GW1 to spend until the end of the transaction. GW1 to spend until the end of the transaction.
*/ */
void testSelfFunding () void testSelfFunding (std::initializer_list<uint256> fs)
{ {
testcase ("selfFunding"); testcase ("selfFunding");
using namespace jtx; using namespace jtx;
Env env (*this); Env env (*this, features(fs));
Account const gw1 ("gw1"); Account const gw1 ("gw1");
Account const gw2 ("gw2"); Account const gw2 ("gw2");
Account const snd ("snd"); Account const snd ("snd");
@@ -95,12 +96,12 @@ class PaymentSandbox_test : public beast::unit_test::suite
env.require (balance ("rcv", USD_gw2 (2))); env.require (balance ("rcv", USD_gw2 (2)));
} }
void testSubtractCredits () void testSubtractCredits (std::initializer_list<uint256> fs)
{ {
testcase ("subtractCredits"); testcase ("subtractCredits");
using namespace jtx; using namespace jtx;
Env env (*this); Env env (*this, features(fs));
Account const gw1 ("gw1"); Account const gw1 ("gw1");
Account const gw2 ("gw2"); Account const gw2 ("gw2");
Account const alice ("alice"); 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"); testcase ("Tiny balance");
@@ -265,7 +266,7 @@ class PaymentSandbox_test : public beast::unit_test::suite
using namespace jtx; using namespace jtx;
Env env (*this); Env env (*this, features(fs));
Account const gw ("gw"); Account const gw ("gw");
Account const alice ("alice"); Account const alice ("alice");
@@ -291,7 +292,8 @@ class PaymentSandbox_test : public beast::unit_test::suite
BEAST_EXPECT(pv.balanceHook (alice, gw, hugeAmt) != tinyAmt); BEAST_EXPECT(pv.balanceHook (alice, gw, hugeAmt) != tinyAmt);
} }
} }
void testReserve()
void testReserve(std::initializer_list<uint256> fs)
{ {
testcase ("Reserve"); testcase ("Reserve");
using namespace jtx; using namespace jtx;
@@ -310,7 +312,7 @@ class PaymentSandbox_test : public beast::unit_test::suite
return env.current ()->fees ().accountReserve (count); return env.current ()->fees ().accountReserve (count);
}; };
Env env (*this); Env env (*this, features(fs));
Account const alice ("alice"); Account const alice ("alice");
env.fund (reserve(env, 1), alice); env.fund (reserve(env, 1), alice);
@@ -335,10 +337,14 @@ class PaymentSandbox_test : public beast::unit_test::suite
public: public:
void run () void run ()
{ {
testSelfFunding (); auto testAll = [this](std::initializer_list<uint256> fs) {
testSubtractCredits (); testSelfFunding(fs);
testTinyBalance (); testSubtractCredits(fs);
testReserve(); testTinyBalance(fs);
testReserve(fs);
};
testAll({});
testAll({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -16,6 +16,7 @@
//============================================================================== //==============================================================================
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <test/jtx/WSClient.h> #include <test/jtx/WSClient.h>
#include <test/jtx.h> #include <test/jtx.h>
@@ -29,11 +30,11 @@ class GatewayBalances_test : public beast::unit_test::suite
public: public:
void void
testGWB() testGWB(std::initializer_list<uint256> fs)
{ {
using namespace std::chrono_literals; using namespace std::chrono_literals;
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
// Gateway account and assets // Gateway account and assets
Account const alice {"alice"}; Account const alice {"alice"};
@@ -152,7 +153,8 @@ public:
void void
run() override run() override
{ {
testGWB(); testGWB({});
testGWB({featureFlow, featureToStrandV2});
} }
}; };

View File

@@ -17,6 +17,7 @@
*/ */
//============================================================================== //==============================================================================
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h> #include <ripple/protocol/JsonFields.h>
#include <test/jtx.h> #include <test/jtx.h>
@@ -66,13 +67,12 @@ public:
} }
} }
void void testNegativeBalance(std::initializer_list<uint256> fs)
testNegativeBalance()
{ {
testcase("Set noripple on a line with negative balance"); testcase("Set noripple on a line with negative balance");
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const gw = Account("gateway"); auto const gw = Account("gateway");
auto const alice = Account("alice"); auto const alice = Account("alice");
@@ -113,13 +113,12 @@ public:
BEAST_EXPECT(!lines[0u].isMember(jss::no_ripple)); BEAST_EXPECT(!lines[0u].isMember(jss::no_ripple));
} }
void void testPairwise(std::initializer_list<uint256> fs)
testPairwise()
{ {
testcase("pairwise NoRipple"); testcase("pairwise NoRipple");
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const alice = Account("alice"); auto const alice = Account("alice");
auto const bob = Account("bob"); auto const bob = Account("bob");
@@ -151,13 +150,12 @@ public:
env(pay(alice, carol, bob["USD"](50)), ter(tecPATH_DRY)); env(pay(alice, carol, bob["USD"](50)), ter(tecPATH_DRY));
} }
void void testDefaultRipple(std::initializer_list<uint256> fs)
testDefaultRipple()
{ {
testcase("Set default ripple on an account and check new trustlines"); testcase("Set default ripple on an account and check new trustlines");
using namespace jtx; using namespace jtx;
Env env(*this); Env env(*this, features(fs));
auto const gw = Account("gateway"); auto const gw = Account("gateway");
auto const alice = Account("alice"); auto const alice = Account("alice");
@@ -213,9 +211,15 @@ public:
void run () void run ()
{ {
testSetAndClear(); testSetAndClear();
testNegativeBalance();
testPairwise(); auto withFeatsTests = [this](std::initializer_list<uint256> fs) {
testDefaultRipple(); 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/OversizeMeta_test.cpp>
#include <test/app/Path_test.cpp> #include <test/app/Path_test.cpp>
#include <test/app/PayChan_test.cpp> #include <test/app/PayChan_test.cpp>
#include <test/app/PayStrand_test.cpp>
#include <test/app/Regression_test.cpp> #include <test/app/Regression_test.cpp>
#include <test/app/SetAuth_test.cpp> #include <test/app/SetAuth_test.cpp>
#include <test/app/SetRegularKey_test.cpp> #include <test/app/SetRegularKey_test.cpp>