mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +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)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</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">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
|
||||
@@ -2436,6 +2436,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\app\tests\AmendmentTable.test.cpp">
|
||||
<Filter>ripple\app\tests</Filter>
|
||||
</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">
|
||||
<Filter>ripple\app\tests</Filter>
|
||||
</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.");
|
||||
|
||||
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,
|
||||
Book (taker.issue_in (), xrpIssue ()), when, j_);
|
||||
Book (taker.issue_in (), xrpIssue ()),
|
||||
when, stepCounter_, j_);
|
||||
|
||||
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;
|
||||
|
||||
@@ -397,7 +400,7 @@ CreateOffer::direct_cross (
|
||||
OfferStream offers (
|
||||
view, view_cancel,
|
||||
Book (taker.issue_in (), taker.issue_out ()),
|
||||
when, j_);
|
||||
when, stepCounter_, j_);
|
||||
|
||||
TER cross_result (tesSUCCESS);
|
||||
int count = 0;
|
||||
|
||||
@@ -41,6 +41,7 @@ class CreateOffer
|
||||
public:
|
||||
CreateOffer (ApplyContext& ctx)
|
||||
: Transactor(ctx)
|
||||
, stepCounter_ (1000, j_)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -120,6 +121,9 @@ private:
|
||||
// What kind of offer we are placing
|
||||
CrossType cross_type_;
|
||||
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,
|
||||
BookRef book, Clock::time_point when,
|
||||
beast::Journal journal)
|
||||
StepCounter& counter, beast::Journal journal)
|
||||
: j_ (journal)
|
||||
, view_ (view)
|
||||
, cancelView_ (cancelView)
|
||||
, book_ (book)
|
||||
, expire_ (when)
|
||||
, tip_ (view, book_)
|
||||
, counter_ (counter)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -89,6 +90,10 @@ OfferStream::step ()
|
||||
|
||||
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
|
||||
if (! entry)
|
||||
{
|
||||
|
||||
@@ -48,6 +48,35 @@ namespace ripple {
|
||||
*/
|
||||
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:
|
||||
beast::Journal j_;
|
||||
ApplyView& view_;
|
||||
@@ -56,6 +85,7 @@ private:
|
||||
Clock::time_point const expire_;
|
||||
BookTip tip_;
|
||||
Offer offer_;
|
||||
StepCounter& counter_;
|
||||
|
||||
void
|
||||
erase (ApplyView& view);
|
||||
@@ -63,7 +93,7 @@ private:
|
||||
public:
|
||||
OfferStream (ApplyView& view, ApplyView& cancelView,
|
||||
BookRef book, Clock::time_point when,
|
||||
beast::Journal journal);
|
||||
StepCounter& counter, beast::Journal journal);
|
||||
|
||||
/** Returns the offer at the tip of the order book.
|
||||
Offers are always presented in decreasing quality.
|
||||
|
||||
@@ -19,14 +19,15 @@
|
||||
|
||||
#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/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/MultiSign.test.cpp>
|
||||
#include <ripple/app/tests/OfferStream.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/Taker.test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user