mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Limit the total number of offers processed while crossing
This commit is contained in:
@@ -1689,6 +1689,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\ripple\app\tests\CrossingLimits_test.cpp">
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||||
|
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tests\DeliverMin.test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\DeliverMin.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>
|
||||||
|
|||||||
@@ -2436,6 +2436,9 @@
|
|||||||
<ClCompile Include="..\..\src\ripple\app\tests\AmendmentTable.test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\AmendmentTable.test.cpp">
|
||||||
<Filter>ripple\app\tests</Filter>
|
<Filter>ripple\app\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\ripple\app\tests\CrossingLimits_test.cpp">
|
||||||
|
<Filter>ripple\app\tests</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\src\ripple\app\tests\DeliverMin.test.cpp">
|
<ClCompile Include="..\..\src\ripple\app\tests\DeliverMin.test.cpp">
|
||||||
<Filter>ripple\app\tests</Filter>
|
<Filter>ripple\app\tests</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
162
src/ripple/app/tests/CrossingLimits_test.cpp
Normal file
162
src/ripple/app/tests/CrossingLimits_test.cpp
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of rippled: https://github.com/ripple/rippled
|
||||||
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <BeastConfig.h>
|
||||||
|
#include <ripple/test/jtx.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
|
||||||
|
namespace ripple {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class CrossingLimits_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
n_offers (
|
||||||
|
jtx::Env& env,
|
||||||
|
std::size_t n,
|
||||||
|
jtx::Account const& account,
|
||||||
|
STAmount const& in,
|
||||||
|
STAmount const& out)
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
auto const ownerCount = env.le(account)->getFieldU32(sfOwnerCount);
|
||||||
|
for (std::size_t i = 0; i < n; i++)
|
||||||
|
env(offer(account, in, out));
|
||||||
|
env.require (owners (account, ownerCount + n));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
testStepLimit()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Env env(*this);
|
||||||
|
auto const xrpMax = XRP(100000000000);
|
||||||
|
auto const gw = Account("gateway");
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
|
||||||
|
env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan");
|
||||||
|
env.trust(USD(1), "bob");
|
||||||
|
env(pay(gw, "bob", USD(1)));
|
||||||
|
env.trust(USD(1), "dan");
|
||||||
|
env(pay(gw, "dan", USD(1)));
|
||||||
|
n_offers (env, 2000, "bob", XRP(1), USD(1));
|
||||||
|
n_offers (env, 1, "dan", XRP(1), USD(1));
|
||||||
|
|
||||||
|
// Alice offers to buy 1000 XRP for 1000 USD. She takes Bob's first
|
||||||
|
// offer, and removes 999 more as unfunded and hits the step limit.
|
||||||
|
env(offer("alice", USD(1000), XRP(1000)),
|
||||||
|
require (
|
||||||
|
balance("alice", USD(1)), owners("alice", 2),
|
||||||
|
balance("bob", USD(0)), owners("bob", 1001),
|
||||||
|
balance("dan", USD(1)), owners("dan", 2)));
|
||||||
|
|
||||||
|
// Carol offers to buy 1000 XRP for 1000 USD. She removes Bob's next
|
||||||
|
// 1000 offers as unfunded and hits the step limit.
|
||||||
|
env(offer("carol", USD(1000), XRP(1000)),
|
||||||
|
require (
|
||||||
|
balance("carol", USD(none)), owners("carol", 1),
|
||||||
|
balance("bob", USD(0)), owners("bob", 1),
|
||||||
|
balance("dan", USD(1)), owners("dan", 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testCrossingLimit()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Env env(*this);
|
||||||
|
auto const xrpMax = XRP(100000000000);
|
||||||
|
auto const gw = Account("gateway");
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
|
||||||
|
env.fund(XRP(100000000), gw, "alice", "bob", "carol");
|
||||||
|
env.trust(USD(1000), "bob");
|
||||||
|
env(pay(gw, "bob", USD(1000)));
|
||||||
|
n_offers (env, 1000, "bob", XRP(1), USD(1));
|
||||||
|
|
||||||
|
// Alice offers to buy 1000 XRP for 1000 USD. She takes the first
|
||||||
|
// 850 offers, hitting the crossing limit.
|
||||||
|
env(offer("alice", USD(1000), XRP(1000)),
|
||||||
|
require (
|
||||||
|
balance("alice", USD(850)),
|
||||||
|
balance("bob", USD(150)), owners ("bob", 151)));
|
||||||
|
|
||||||
|
// Carol offers to buy 1000 XRP for 1000 USD. She takes the remaining
|
||||||
|
// 150 offers without hitting a limit.
|
||||||
|
env(offer("carol", USD(1000), XRP(1000)),
|
||||||
|
require (
|
||||||
|
balance("carol", USD(150)),
|
||||||
|
balance("bob", USD(0)), owners ("bob", 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testStepAndCrossingLimit()
|
||||||
|
{
|
||||||
|
using namespace jtx;
|
||||||
|
Env env(*this);
|
||||||
|
auto const xrpMax = XRP(100000000000);
|
||||||
|
auto const gw = Account("gateway");
|
||||||
|
auto const USD = gw["USD"];
|
||||||
|
|
||||||
|
env.fund(XRP(100000000), gw, "alice", "bob", "carol", "dan", "evita");
|
||||||
|
|
||||||
|
env.trust(USD(1000), "alice");
|
||||||
|
env(pay(gw, "alice", USD(1000)));
|
||||||
|
env.trust(USD(1000), "carol");
|
||||||
|
env(pay(gw, "carol", USD(1)));
|
||||||
|
env.trust(USD(1000), "evita");
|
||||||
|
env(pay(gw, "evita", USD(1000)));
|
||||||
|
|
||||||
|
n_offers (env, 400, "alice", XRP(1), USD(1));
|
||||||
|
n_offers (env, 700, "carol", XRP(1), USD(1));
|
||||||
|
n_offers (env, 999, "evita", XRP(1), USD(1));
|
||||||
|
|
||||||
|
// Bob offers to buy 1000 XRP for 1000 USD. He takes all 400 USD from
|
||||||
|
// Alice's offers, 1 USD from Carol's and then removes 599 of Carol's
|
||||||
|
// offers as unfunded, before hitting the step limit.
|
||||||
|
env(offer("bob", USD(1000), XRP(1000)),
|
||||||
|
require (
|
||||||
|
balance("bob", USD(401)),
|
||||||
|
balance("alice", USD(600)), owners("alice", 1),
|
||||||
|
balance("carol", USD(0)), owners("carol", 101),
|
||||||
|
balance("evita", USD(1000)), owners("evita", 1000)));
|
||||||
|
|
||||||
|
// Dan offers to buy 900 XRP for 900 USD. He removes all 100 of Carol's
|
||||||
|
// offers as unfunded, then takes 850 USD from Evita's, hitting the
|
||||||
|
// crossing limit.
|
||||||
|
env(offer("dan", USD(900), XRP(900)),
|
||||||
|
require (
|
||||||
|
balance("dan", USD(850)),
|
||||||
|
balance("alice", USD(600)), owners("alice", 1),
|
||||||
|
balance("carol", USD(0)), owners("carol", 1),
|
||||||
|
balance("evita", USD(150)), owners("evita", 150)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
testStepLimit();
|
||||||
|
testCrossingLimit();
|
||||||
|
testStepAndCrossingLimit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE_MANUAL(CrossingLimits,tx,ripple);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // ripple
|
||||||
@@ -251,13 +251,16 @@ CreateOffer::bridged_cross (
|
|||||||
throw std::logic_error ("Bridging with XRP and an endpoint.");
|
throw std::logic_error ("Bridging with XRP and an endpoint.");
|
||||||
|
|
||||||
OfferStream offers_direct (view, view_cancel,
|
OfferStream offers_direct (view, view_cancel,
|
||||||
Book (taker.issue_in (), taker.issue_out ()), when, j_);
|
Book (taker.issue_in (), taker.issue_out ()),
|
||||||
|
when, stepCounter_, j_);
|
||||||
|
|
||||||
OfferStream offers_leg1 (view, view_cancel,
|
OfferStream offers_leg1 (view, view_cancel,
|
||||||
Book (taker.issue_in (), xrpIssue ()), when, j_);
|
Book (taker.issue_in (), xrpIssue ()),
|
||||||
|
when, stepCounter_, j_);
|
||||||
|
|
||||||
OfferStream offers_leg2 (view, view_cancel,
|
OfferStream offers_leg2 (view, view_cancel,
|
||||||
Book (xrpIssue (), taker.issue_out ()), when, j_);
|
Book (xrpIssue (), taker.issue_out ()),
|
||||||
|
when, stepCounter_, j_);
|
||||||
|
|
||||||
TER cross_result = tesSUCCESS;
|
TER cross_result = tesSUCCESS;
|
||||||
|
|
||||||
@@ -397,7 +400,7 @@ CreateOffer::direct_cross (
|
|||||||
OfferStream offers (
|
OfferStream offers (
|
||||||
view, view_cancel,
|
view, view_cancel,
|
||||||
Book (taker.issue_in (), taker.issue_out ()),
|
Book (taker.issue_in (), taker.issue_out ()),
|
||||||
when, j_);
|
when, stepCounter_, j_);
|
||||||
|
|
||||||
TER cross_result (tesSUCCESS);
|
TER cross_result (tesSUCCESS);
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class CreateOffer
|
|||||||
public:
|
public:
|
||||||
CreateOffer (ApplyContext& ctx)
|
CreateOffer (ApplyContext& ctx)
|
||||||
: Transactor(ctx)
|
: Transactor(ctx)
|
||||||
|
, stepCounter_ (1000, j_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +121,9 @@ private:
|
|||||||
// What kind of offer we are placing
|
// What kind of offer we are placing
|
||||||
CrossType cross_type_;
|
CrossType cross_type_;
|
||||||
std::uint32_t deprecatedWrongOwnerCount_;
|
std::uint32_t deprecatedWrongOwnerCount_;
|
||||||
|
|
||||||
|
// The number of steps to take through order books while crossing
|
||||||
|
OfferStream::StepCounter stepCounter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ namespace ripple {
|
|||||||
|
|
||||||
OfferStream::OfferStream (ApplyView& view, ApplyView& cancelView,
|
OfferStream::OfferStream (ApplyView& view, ApplyView& cancelView,
|
||||||
BookRef book, Clock::time_point when,
|
BookRef book, Clock::time_point when,
|
||||||
beast::Journal journal)
|
StepCounter& counter, beast::Journal journal)
|
||||||
: j_ (journal)
|
: j_ (journal)
|
||||||
, view_ (view)
|
, view_ (view)
|
||||||
, cancelView_ (cancelView)
|
, cancelView_ (cancelView)
|
||||||
, book_ (book)
|
, book_ (book)
|
||||||
, expire_ (when)
|
, expire_ (when)
|
||||||
, tip_ (view, book_)
|
, tip_ (view, book_)
|
||||||
|
, counter_ (counter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +90,10 @@ OfferStream::step ()
|
|||||||
|
|
||||||
std::shared_ptr<SLE> entry = tip_.entry();
|
std::shared_ptr<SLE> entry = tip_.entry();
|
||||||
|
|
||||||
|
// If we exceed the maximum number of allowed steps, we're done.
|
||||||
|
if (!counter_.step ())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Remove if missing
|
// Remove if missing
|
||||||
if (! entry)
|
if (! entry)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,6 +48,35 @@ namespace ripple {
|
|||||||
*/
|
*/
|
||||||
class OfferStream
|
class OfferStream
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
class StepCounter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::uint32_t const limit_;
|
||||||
|
std::uint32_t count_;
|
||||||
|
beast::Journal j_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StepCounter (std::uint32_t limit, beast::Journal j)
|
||||||
|
: limit_ (limit)
|
||||||
|
, count_ (0)
|
||||||
|
, j_ (j)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
step ()
|
||||||
|
{
|
||||||
|
if (count_ >= limit_)
|
||||||
|
{
|
||||||
|
j_.debug << "Exceeded " << limit_ << " step limit.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
count_++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
beast::Journal j_;
|
beast::Journal j_;
|
||||||
ApplyView& view_;
|
ApplyView& view_;
|
||||||
@@ -56,6 +85,7 @@ private:
|
|||||||
Clock::time_point const expire_;
|
Clock::time_point const expire_;
|
||||||
BookTip tip_;
|
BookTip tip_;
|
||||||
Offer offer_;
|
Offer offer_;
|
||||||
|
StepCounter& counter_;
|
||||||
|
|
||||||
void
|
void
|
||||||
erase (ApplyView& view);
|
erase (ApplyView& view);
|
||||||
@@ -63,7 +93,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
OfferStream (ApplyView& view, ApplyView& cancelView,
|
OfferStream (ApplyView& view, ApplyView& cancelView,
|
||||||
BookRef book, Clock::time_point when,
|
BookRef book, Clock::time_point when,
|
||||||
beast::Journal journal);
|
StepCounter& counter, beast::Journal journal);
|
||||||
|
|
||||||
/** Returns the offer at the tip of the order book.
|
/** Returns the offer at the tip of the order book.
|
||||||
Offers are always presented in decreasing quality.
|
Offers are always presented in decreasing quality.
|
||||||
|
|||||||
@@ -19,14 +19,15 @@
|
|||||||
|
|
||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
|
|
||||||
#include <ripple/app/tests/SusPay_test.cpp>
|
|
||||||
#include <ripple/app/tests/Regression_test.cpp>
|
|
||||||
#include <ripple/app/tests/AccountTxPaging.test.cpp>
|
#include <ripple/app/tests/AccountTxPaging.test.cpp>
|
||||||
#include <ripple/app/tests/AmendmentTable.test.cpp>
|
#include <ripple/app/tests/AmendmentTable.test.cpp>
|
||||||
#include <ripple/app/tests/Path_test.cpp>
|
#include <ripple/app/tests/CrossingLimits_test.cpp>
|
||||||
#include <ripple/app/tests/DeliverMin.test.cpp>
|
#include <ripple/app/tests/DeliverMin.test.cpp>
|
||||||
#include <ripple/app/tests/MultiSign.test.cpp>
|
#include <ripple/app/tests/MultiSign.test.cpp>
|
||||||
#include <ripple/app/tests/OfferStream.test.cpp>
|
#include <ripple/app/tests/OfferStream.test.cpp>
|
||||||
#include <ripple/app/tests/Offer.test.cpp>
|
#include <ripple/app/tests/Offer.test.cpp>
|
||||||
#include <ripple/app/tests/Taker.test.cpp>
|
#include <ripple/app/tests/Path_test.cpp>
|
||||||
|
#include <ripple/app/tests/Regression_test.cpp>
|
||||||
|
#include <ripple/app/tests/SusPay_test.cpp>
|
||||||
#include <ripple/app/tests/SetAuth_test.cpp>
|
#include <ripple/app/tests/SetAuth_test.cpp>
|
||||||
|
#include <ripple/app/tests/Taker.test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user