Don't use tapENABLE_TESTING for TxQ.

* Enable FeeEscalation feature in TxQ tests.
* Elapsed time for simulated consensus.
This commit is contained in:
Edward Hennis
2015-12-15 14:06:50 -05:00
committed by Vinnie Falco
parent a5583de6e6
commit 1bce85d7b6
10 changed files with 136 additions and 193 deletions

View File

@@ -70,7 +70,8 @@ public:
server in standalone mode and SHOULD NOT be used during the normal
consensus process.
*/
virtual void simulate () = 0;
virtual void simulate (
boost::optional<std::chrono::milliseconds> consensusDelay) = 0;
};
//------------------------------------------------------------------------------

View File

@@ -902,13 +902,14 @@ bool LedgerConsensusImp::peerPosition (LedgerProposal::ref newPosition)
return true;
}
void LedgerConsensusImp::simulate ()
void LedgerConsensusImp::simulate (
boost::optional<std::chrono::milliseconds> consensusDelay)
{
std::lock_guard<std::recursive_mutex> _(lock_);
JLOG (j_.info) << "Simulating consensus";
closeLedger ();
mCurrentMSeconds = 100ms;
mCurrentMSeconds = consensusDelay.value_or(100ms);
beginAccept (true);
JLOG (j_.info) << "Simulation complete";
}

View File

@@ -150,7 +150,8 @@ public:
*/
bool peerPosition (LedgerProposal::ref newPosition) override;
void simulate () override;
void simulate(
boost::optional<std::chrono::milliseconds> consensusDelay) override;
private:
/**

View File

@@ -350,7 +350,8 @@ public:
Json::Value getServerInfo (bool human, bool admin) override;
void clearLedgerFetch () override;
Json::Value getLedgerFetchInfo () override;
std::uint32_t acceptLedger () override;
std::uint32_t acceptLedger (
boost::optional<std::chrono::milliseconds> consensusDelay) override;
uint256 getConsensusLCL () override;
void reportFeeChange () override;
@@ -2465,7 +2466,8 @@ bool NetworkOPsImp::unsubBook (std::uint64_t uSeq, Book const& book)
return true;
}
std::uint32_t NetworkOPsImp::acceptLedger ()
std::uint32_t NetworkOPsImp::acceptLedger (
boost::optional<std::chrono::milliseconds> consensusDelay)
{
// This code-path is exclusively used when the server is in standalone
// mode via `ledger_accept`
@@ -2477,7 +2479,7 @@ std::uint32_t NetworkOPsImp::acceptLedger ()
// FIXME Could we improve on this and remove the need for a specialized
// API in LedgerConsensus?
beginConsensus (m_ledgerMaster.getClosedLedger ()->getHash ());
mLedgerConsensus->simulate ();
mLedgerConsensus->simulate (consensusDelay);
return m_ledgerMaster.getCurrentLedger ()->info().seq;
}

View File

@@ -190,7 +190,8 @@ public:
performs a virtual consensus round, with all the transactions we are
proposing being accepted.
*/
virtual std::uint32_t acceptLedger () = 0;
virtual std::uint32_t acceptLedger (
boost::optional<std::chrono::milliseconds> consensusDelay = boost::none) = 0;
virtual uint256 getConsensusLCL () = 0;

View File

@@ -214,8 +214,7 @@ public:
@return Whether any txs were added to the view.
*/
bool
accept(Application& app, OpenView& view,
ApplyFlags flags = tapNONE);
accept(Application& app, OpenView& view);
/**
We have a new last validated ledger, update and clean up the
@@ -233,8 +232,7 @@ public:
*/
void
processValidatedLedger(Application& app,
OpenView const& view, bool timeLeap,
ApplyFlags flags = tapNONE);
OpenView const& view, bool timeLeap);
/** Used by tests only.
*/

View File

@@ -304,9 +304,8 @@ TxQ::apply(Application& app, OpenView& view,
ApplyFlags flags, beast::Journal j)
{
auto const allowEscalation =
(flags & tapENABLE_TESTING) ||
(view.rules().enabled(featureFeeEscalation,
app.config().features));
(view.rules().enabled(featureFeeEscalation,
app.config().features));
if (!allowEscalation)
{
return ripple::apply(app, view, *tx, flags, j);
@@ -502,11 +501,9 @@ TxQ::apply(Application& app, OpenView& view,
void
TxQ::processValidatedLedger(Application& app,
OpenView const& view, bool timeLeap,
ApplyFlags flags)
OpenView const& view, bool timeLeap)
{
auto const allowEscalation =
(flags & tapENABLE_TESTING) ||
(view.rules().enabled(featureFeeEscalation,
app.config().features));
if (!allowEscalation)
@@ -560,10 +557,9 @@ TxQ::processValidatedLedger(Application& app,
bool
TxQ::accept(Application& app,
OpenView& view, ApplyFlags flags)
OpenView& view)
{
auto const allowEscalation =
(flags & tapENABLE_TESTING) ||
(view.rules().enabled(featureFeeEscalation,
app.config().features));
if (!allowEscalation)

View File

@@ -20,9 +20,11 @@
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/app/ledger/LedgerConsensus.h>
#include <ripple/app/tx/apply.h>
#include <ripple/core/LoadFeeTrack.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/TestSuite.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/protocol/STTx.h>
#include <ripple/test/jtx.h>
@@ -58,41 +60,6 @@ class TxQ_test : public TestSuite
expect(metrics.expFeeLevel == expectedCurFeeLevel, "expFeeLevel");
}
void
close(jtx::Env& env, size_t expectedTxSetSize, bool timeLeap = false)
{
{
auto const view = env.current();
expect(view->txCount() == expectedTxSetSize, "TxSet size mismatch");
}
env.close();
}
void
submit(jtx::Env& env, jtx::JTx const& jt)
{
// Env checks this, but this test shouldn't
// generate any malformed txns.
expect(jt.stx);
bool didApply;
TER ter;
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j)
{
std::tie(ter, didApply) =
env.app().getTxQ().apply(env.app(),
view, jt.stx, tapENABLE_TESTING,
env.journal);
return didApply;
}
);
env.postconditions(jt, ter, didApply);
}
static
std::unique_ptr<Config>
makeConfig()
@@ -111,8 +78,9 @@ public:
void testQueue()
{
using namespace jtx;
using namespace std::chrono;
Env env(*this, makeConfig());
Env env(*this, makeConfig(), features(featureFeeEscalation));
auto& txq = env.app().getTxQ();
txq.setMinimumTx(3);
@@ -137,14 +105,12 @@ public:
checkMetrics(env, 0, boost::none, 4, 3, 256, 500);
// Alice - price starts exploding: held
submit(env,
env.jt(noop(alice), queued));
env(noop(alice), queued);
checkMetrics(env, 1, boost::none, 4, 3, 256, 500);
// Alice - Alice is already in the queue, so can't hold.
submit(env,
env.jt(noop(alice), seq(env.seq(alice) + 1),
ter(telINSUF_FEE_P)));
env(noop(alice), seq(env.seq(alice) + 1),
ter(telINSUF_FEE_P));
checkMetrics(env, 1, boost::none, 4, 3, 256, 500);
auto openLedgerFee =
@@ -154,22 +120,19 @@ public:
};
// Alice's next transaction -
// fails because the item in the TxQ hasn't applied.
submit(env,
env.jt(noop(alice), openLedgerFee(),
seq(env.seq(alice) + 1), ter(terPRE_SEQ)));
env(noop(alice), openLedgerFee(),
seq(env.seq(alice) + 1), ter(terPRE_SEQ));
checkMetrics(env, 1, boost::none, 4, 3, 256, 500);
// Bob with really high fee - applies
submit(env,
env.jt(noop(bob), openLedgerFee()));
env(noop(bob), openLedgerFee());
checkMetrics(env, 1, boost::none, 5, 3, 256, 500);
// Daria with low fee: hold
submit(env,
env.jt(noop(daria), fee(1000), queued));
env(noop(daria), fee(1000), queued);
checkMetrics(env, 2, boost::none, 5, 3, 256, 500);
close(env, 5);
env.close();
// Verify that the held transactions got applied
auto lastMedian = 500;
checkMetrics(env, 0, 10, 2, 5, 256, lastMedian);
@@ -181,27 +144,19 @@ public:
checkMetrics(env, 0, 10, 6, 5, 256, lastMedian);
// Now get a bunch of transactions held.
submit(env,
env.jt(noop(alice), fee(12), queued));
env(noop(alice), fee(12), queued);
checkMetrics(env, 1, 10, 6, 5, 256, lastMedian);
submit(env,
env.jt(noop(bob), fee(10), queued)); // won't clear the queue
submit(env,
env.jt(noop(charlie), fee(20), queued));
submit(env,
env.jt(noop(daria), fee(15), queued));
submit(env,
env.jt(noop(elmo), fee(11), queued));
submit(env,
env.jt(noop(fred), fee(19), queued));
submit(env,
env.jt(noop(gwen), fee(16), queued));
submit(env,
env.jt(noop(hank), fee(18), queued));
env(noop(bob), fee(10), queued); // won't clear the queue
env(noop(charlie), fee(20), queued);
env(noop(daria), fee(15), queued);
env(noop(elmo), fee(11), queued);
env(noop(fred), fee(19), queued);
env(noop(gwen), fee(16), queued);
env(noop(hank), fee(18), queued);
checkMetrics(env, 8, 10, 6, 5, 256, lastMedian);
close(env, 6);
env.close();
// Verify that the held transactions got applied
lastMedian = 500;
checkMetrics(env, 1, 12, 7, 6, 256, lastMedian);
@@ -211,40 +166,35 @@ public:
//////////////////////////////////////////////////////////////
// Hank sends another txn
submit(env,
env.jt(noop(hank), fee(10), queued));
env(noop(hank), fee(10), queued);
// But he's not going to leave it in the queue
checkMetrics(env, 2, 12, 7, 6, 256, lastMedian);
// Hank sees his txn got held and bumps the fee,
// but doesn't even bump it enough to requeue
submit(env,
env.jt(noop(hank), fee(11), ter(telINSUF_FEE_P)));
env(noop(hank), fee(11), ter(telINSUF_FEE_P));
checkMetrics(env, 2, 12, 7, 6, 256, lastMedian);
// Hank sees his txn got held and bumps the fee,
// enough to requeue, but doesn't bump it enough to
// apply to the ledger
submit(env,
env.jt(noop(hank), fee(6000), queued));
env(noop(hank), fee(6000), queued);
// But he's not going to leave it in the queue
checkMetrics(env, 2, 12, 7, 6, 256, lastMedian);
// Hank sees his txn got held and bumps the fee,
// high enough to get into the open ledger, because
// he doesn't want to wait.
submit(env,
env.jt(noop(hank), openLedgerFee()));
env(noop(hank), openLedgerFee());
checkMetrics(env, 1, 12, 8, 6, 256, lastMedian);
// Hank then sends another, less important txn
// (In addition to the metrics, this will verify that
// the original txn got removed.)
submit(env,
env.jt(noop(hank), fee(6000), queued));
env(noop(hank), fee(6000), queued);
checkMetrics(env, 2, 12, 8, 6, 256, lastMedian);
close(env, 8);
env.close();
// Verify that bob and hank's txns were applied
lastMedian = 500;
@@ -253,12 +203,12 @@ public:
// Close again with a simulated time leap to
// reset the escalation limit down to minimum
lastMedian = 76928;
close(env, 2, true);
env.close(env.now() + 5s, 10000ms);
checkMetrics(env, 0, 16, 0, 3, 256, lastMedian);
// Then close once more without the time leap
// to reset the queue maxsize down to minimum
lastMedian = 500;
close(env, 0);
env.close();
checkMetrics(env, 0, 6, 0, 3, 256, lastMedian);
//////////////////////////////////////////////////////////////
@@ -266,56 +216,44 @@ public:
// At this point, the queue should have a limit of 6.
// Stuff the ledger and queue so we can verify that
// stuff gets kicked out.
submit(env,
env.jt(noop(hank)));
submit(env,
env.jt(noop(gwen)));
submit(env,
env.jt(noop(fred)));
submit(env,
env.jt(noop(elmo)));
env(noop(hank));
env(noop(gwen));
env(noop(fred));
env(noop(elmo));
checkMetrics(env, 0, 6, 4, 3, 256, lastMedian);
// Use explicit fees so we can control which txn
// will get dropped
submit(env,
env.jt(noop(alice), fee(20), queued));
submit(env,
env.jt(noop(hank), fee(19), queued));
submit(env,
env.jt(noop(gwen), fee(18), queued));
submit(env,
env.jt(noop(fred), fee(17), queued));
submit(env,
env.jt(noop(elmo), fee(16), queued));
env(noop(alice), fee(20), queued);
env(noop(hank), fee(19), queued);
env(noop(gwen), fee(18), queued);
env(noop(fred), fee(17), queued);
env(noop(elmo), fee(16), queued);
// This one gets into the queue, but gets dropped when the
// higher fee one is added later.
submit(env,
env.jt(noop(daria), fee(15), queued));
env(noop(daria), fee(15), queued);
// Queue is full now.
checkMetrics(env, 6, 6, 4, 3, 385, lastMedian);
// Try to add another transaction with the default (low) fee,
// it should fail because the queue is full.
submit(env,
env.jt(noop(charlie), ter(telINSUF_FEE_P)));
env(noop(charlie), ter(telINSUF_FEE_P));
// Add another transaction, with a higher fee,
// Not high enough to get into the ledger, but high
// enough to get into the queue (and kick somebody out)
submit(env,
env.jt(noop(charlie), fee(100), queued));
env(noop(charlie), fee(100), queued);
// Queue is still full, of course, but the min fee has gone up
checkMetrics(env, 6, 6, 4, 3, 410, lastMedian);
close(env, 4);
env.close();
lastMedian = 500;
checkMetrics(env, 1, 8, 5, 4, 256, lastMedian);
lastMedian = 500;
close(env, 5);
env.close();
checkMetrics(env, 0, 10, 1, 5, 256, lastMedian);
//////////////////////////////////////////////////////////////
@@ -332,13 +270,11 @@ public:
// Stuff the ledger.
for (int i = 0; i <= txnsNeeded; ++i)
{
submit(env,
env.jt(noop(env.master)));
env(noop(env.master));
}
// Queue one straightforward transaction
submit(env,
env.jt(noop(env.master), fee(20), queued));
env(noop(env.master), fee(20), queued);
++metrics.txCount;
checkMetrics(env, metrics.txCount,
@@ -350,8 +286,9 @@ public:
void testLastLedgerSeq()
{
using namespace jtx;
using namespace std::chrono;
Env env(*this, makeConfig());
Env env(*this, makeConfig(), features(featureFeeEscalation));
auto& txq = env.app().getTxQ();
txq.setMinimumTx(2);
@@ -367,53 +304,43 @@ public:
checkMetrics(env, 0, boost::none, 0, 2, 256, 500);
// Fund these accounts and close the ledger without
// involving the queue, so that stats aren't affected.
env.fund(XRP(1000), noripple(alice, bob, charlie, daria, edgar, felicia));
env.close();
// Fund across several ledgers so the TxQ metrics stay restricted.
env.fund(XRP(1000), noripple(alice, bob));
env.close(env.now() + 5s, 10000ms);
env.fund(XRP(1000), noripple(charlie, daria));
env.close(env.now() + 5s, 10000ms);
env.fund(XRP(1000), noripple(edgar, felicia));
env.close(env.now() + 5s, 10000ms);
checkMetrics(env, 0, boost::none, 0, 2, 256, 500);
submit(env,
env.jt(noop(bob)));
submit(env,
env.jt(noop(charlie)));
submit(env,
env.jt(noop(daria)));
env(noop(bob));
env(noop(charlie));
env(noop(daria));
checkMetrics(env, 0, boost::none, 3, 2, 256, 500);
// Queue an item with a LastLedgerSeq.
submit(env,
env.jt(noop(alice), json(R"({"LastLedgerSequence":4})"),
queued));
env(noop(alice), json(R"({"LastLedgerSequence":7})"),
queued);
// Queue items with higher fees to force the previous
// txn to wait.
submit(env,
env.jt(noop(bob), fee(20), queued));
submit(env,
env.jt(noop(charlie), fee(20), queued));
submit(env,
env.jt(noop(daria), fee(20), queued));
submit(env,
env.jt(noop(edgar), fee(20), queued));
env(noop(bob), fee(20), queued);
env(noop(charlie), fee(20), queued);
env(noop(daria), fee(20), queued);
env(noop(edgar), fee(20), queued);
checkMetrics(env, 5, boost::none, 3, 2, 256, 500);
close(env, 3);
env.close();
checkMetrics(env, 1, 6, 4, 3, 256, 500);
// Keep alice's transaction waiting.
submit(env,
env.jt(noop(bob), fee(20), queued));
submit(env,
env.jt(noop(charlie), fee(20), queued));
submit(env,
env.jt(noop(daria), fee(20), queued));
submit(env,
env.jt(noop(edgar), fee(20), queued));
submit(env,
env.jt(noop(felicia), fee(20), queued));
env(noop(bob), fee(20), queued);
env(noop(charlie), fee(20), queued);
env(noop(daria), fee(20), queued);
env(noop(edgar), fee(20), queued);
env(noop(felicia), fee(20), queued);
checkMetrics(env, 6, 6, 4, 3, 257, 500);
close(env, 4);
env.close();
// alice's transaction expired without getting
// into the ledger, so the queue is now empty.
checkMetrics(env, 0, 8, 5, 4, 256, 512);
@@ -423,8 +350,9 @@ public:
void testZeroFeeTxn()
{
using namespace jtx;
using namespace std::chrono;
Env env(*this, makeConfig());
Env env(*this, makeConfig(), features(featureFeeEscalation));
auto& txq = env.app().getTxQ();
txq.setMinimumTx(2);
@@ -439,24 +367,22 @@ public:
// Fund these accounts and close the ledger without
// involving the queue, so that stats aren't affected.
env.fund(XRP(1000), noripple(alice, bob));
env.close();
env.close(env.now() + 5s, 10000ms);
// Fill the ledger
submit(env, env.jt(noop(alice)));
submit(env, env.jt(noop(alice)));
submit(env, env.jt(noop(alice)));
env(noop(alice));
env(noop(alice));
env(noop(alice));
checkMetrics(env, 0, boost::none, 3, 2, 256, 500);
submit(env,
env.jt(noop(bob), queued));
env(noop(bob), queued);
checkMetrics(env, 1, boost::none, 3, 2, 256, 500);
// Even though this transaction has a 0 fee,
// SetRegularKey::calculateBaseFee indicates this is
// a "free" transaction, so it has an "infinite" fee
// level and goes into the open ledger.
submit(env,
env.jt(regkey(alice, bob), fee(0)));
env(regkey(alice, bob), fee(0));
checkMetrics(env, 1, boost::none, 4, 2, 256, 500);
// This transaction also has an "infinite" fee level,
@@ -465,9 +391,8 @@ public:
// with terPRE_SEQ (notably, *not* telINSUF_FEE_P).
// This implicitly relies on preclaim succeeding and
// canBeHeld failing under the hood.
submit(env,
env.jt(regkey(bob, alice), fee(0),
seq(env.seq(bob) + 1), ter(terPRE_SEQ)));
env(regkey(bob, alice), fee(0),
seq(env.seq(bob) + 1), ter(terPRE_SEQ));
checkMetrics(env, 1, boost::none, 4, 2, 256, 500);
}
@@ -476,7 +401,7 @@ public:
{
using namespace jtx;
Env env(*this, makeConfig());
Env env(*this, makeConfig(), features(featureFeeEscalation));
auto alice = Account("alice");
auto bob = Account("bob");
@@ -488,21 +413,19 @@ public:
// expected.
// Fail in preflight
submit(env,
env.jt(pay(alice, bob, XRP(-1000)),
ter(temBAD_AMOUNT)));
env(pay(alice, bob, XRP(-1000)),
ter(temBAD_AMOUNT));
// Fail in preclaim
submit(env,
env.jt(noop(alice), fee(XRP(100000)),
ter(terINSUF_FEE_B)));
env(noop(alice), fee(XRP(100000)),
ter(terINSUF_FEE_B));
}
void testQueuedFailure()
{
using namespace jtx;
Env env(*this, makeConfig());
Env env(*this, makeConfig(), features(featureFeeEscalation));
auto& txq = env.app().getTxQ();
txq.setMinimumTx(2);
@@ -519,18 +442,36 @@ public:
checkMetrics(env, 0, boost::none, 2, 2, 256, 500);
// Fill the ledger
submit(env, env.jt(noop(alice)));
env(noop(alice));
checkMetrics(env, 0, boost::none, 3, 2, 256, 500);
// Put a transaction in the queue
submit(env, env.jt(noop(alice), queued));
env(noop(alice), queued);
checkMetrics(env, 1, boost::none, 3, 2, 256, 500);
// Now cheat, and bypass the queue.
env(noop(alice));
{
auto const& jt = env.jt(noop(alice));
expect(jt.stx);
bool didApply;
TER ter;
env.app().openLedger().modify(
[&](OpenView& view, beast::Journal j)
{
std::tie(ter, didApply) =
ripple::apply(env.app(),
view, *jt.stx, tapNONE,
env.journal);
return didApply;
}
);
env.postconditions(jt, ter, didApply);
}
checkMetrics(env, 1, boost::none, 4, 2, 256, 500);
close(env, 4);
env.close();
// Alice's queued transaction failed in TxQ::accept
// with tefPAST_SEQ
checkMetrics(env, 0, 8, 0, 4, 256, 500);

View File

@@ -224,7 +224,8 @@ public:
the close time of the resulting ledger.
*/
void
close (NetClock::time_point closeTime);
close (NetClock::time_point closeTime,
boost::optional<std::chrono::milliseconds> consensusDelay = boost::none);
/** Close and advance the ledger.

View File

@@ -28,10 +28,10 @@
#include <ripple/test/jtx/seq.h>
#include <ripple/test/jtx/sig.h>
#include <ripple/test/jtx/utility.h>
#include <ripple/app/tx/apply.h>
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/ledger/LedgerTiming.h>
#include <ripple/app/misc/NetworkOPs.h>
#include <ripple/app/misc/TxQ.h>
#include <ripple/basics/contract.h>
#include <ripple/basics/Slice.h>
#include <ripple/core/ConfigSections.h>
@@ -113,12 +113,13 @@ Env::closed()
}
void
Env::close(NetClock::time_point closeTime)
Env::close(NetClock::time_point closeTime,
boost::optional<std::chrono::milliseconds> consensusDelay)
{
// Round up to next distinguishable value
closeTime += closed()->info().closeTimeResolution - 1s;
bundle_.timeKeeper->set(closeTime);
app().getOPs().acceptLedger();
app().getOPs().acceptLedger(consensusDelay);
bundle_.timeKeeper->set(
closed()->info().closeTime);
}
@@ -260,8 +261,8 @@ Env::submit (JTx const& jt)
app().openLedger().modify(
[&](OpenView& view, beast::Journal j)
{
std::tie(ter_, didApply) = ripple::apply(
app(), view, *jt.stx, applyFlags(),
std::tie(ter_, didApply) = app().getTxQ().apply(
app(), view, jt.stx, applyFlags(),
beast::Journal{});
return didApply;
});