mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 18:15:50 +00:00
Retried transactions that tec move from TxQ to open ledger:
* Unit test of tec code handling. * Extra TxQ debug logging
This commit is contained in:
committed by
Nik Bougalis
parent
7427cf7506
commit
16b9bbb517
@@ -654,7 +654,7 @@ RCLConsensus::Adaptor::buildLCL(
|
||||
if (replayData)
|
||||
{
|
||||
assert(replayData->parent()->info().hash == previousLedger.id());
|
||||
return buildLedger(*replayData, tapNO_CHECK_SIGN, app_, j_);
|
||||
return buildLedger(*replayData, tapNONE, app_, j_);
|
||||
}
|
||||
return buildLedger(
|
||||
previousLedger.ledger_,
|
||||
|
||||
@@ -139,7 +139,7 @@ applyTransactions(
|
||||
try
|
||||
{
|
||||
switch (applyTransaction(
|
||||
app, view, *it->second, certainRetry, tapNO_CHECK_SIGN, j))
|
||||
app, view, *it->second, certainRetry, tapNONE, j))
|
||||
{
|
||||
case ApplyResult::Success:
|
||||
it = retriableTxs.erase(it);
|
||||
|
||||
@@ -1028,8 +1028,8 @@ void NetworkOPsImp::apply (std::unique_lock<std::mutex>& batchLock)
|
||||
{
|
||||
for (TransactionStatus& e : transactions)
|
||||
{
|
||||
// we check before addingto the batch
|
||||
ApplyFlags flags = tapNO_CHECK_SIGN;
|
||||
// we check before adding to the batch
|
||||
ApplyFlags flags = tapNONE;
|
||||
if (e.admin)
|
||||
flags = flags | tapUNLIMITED;
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ private:
|
||||
PreflightResult const& pfresult);
|
||||
|
||||
std::pair<TER, bool>
|
||||
apply(Application& app, OpenView& view);
|
||||
apply(Application& app, OpenView& view, beast::Journal j);
|
||||
};
|
||||
|
||||
class GreaterFee
|
||||
|
||||
@@ -262,7 +262,7 @@ TxQ::MaybeTx::MaybeTx(
|
||||
}
|
||||
|
||||
std::pair<TER, bool>
|
||||
TxQ::MaybeTx::apply(Application& app, OpenView& view)
|
||||
TxQ::MaybeTx::apply(Application& app, OpenView& view, beast::Journal j)
|
||||
{
|
||||
boost::optional<STAmountSO> saved;
|
||||
if (view.rules().enabled(fix1513))
|
||||
@@ -272,6 +272,10 @@ TxQ::MaybeTx::apply(Application& app, OpenView& view)
|
||||
if (pfresult->rules != view.rules() ||
|
||||
pfresult->flags != flags)
|
||||
{
|
||||
JLOG(j.debug()) << "Queued transaction " <<
|
||||
txID << " rules or flags have changed. Flags from " <<
|
||||
pfresult->flags << " to " << flags ;
|
||||
|
||||
pfresult.emplace(
|
||||
preflight(app, view.rules(),
|
||||
pfresult->tx,
|
||||
@@ -504,7 +508,7 @@ TxQ::tryClearAccountQueue(Application& app, OpenView& view,
|
||||
// Attempt to apply the queued transactions.
|
||||
for (auto it = beginTxIter; it != endTxIter; ++it)
|
||||
{
|
||||
auto txResult = it->second.apply(app, view);
|
||||
auto txResult = it->second.apply(app, view, j);
|
||||
// Succeed or fail, use up a retry, because if the overall
|
||||
// process fails, we want the attempt to count. If it all
|
||||
// succeeds, the MaybeTx will be destructed, so it'll be
|
||||
@@ -1009,7 +1013,7 @@ TxQ::apply(Application& app, OpenView& view,
|
||||
|
||||
std::tie(txnResult, didApply) = doApply(pcresult, app, view);
|
||||
|
||||
JLOG(j_.trace()) << "Transaction " <<
|
||||
JLOG(j_.trace()) << "New transaction " <<
|
||||
transactionID <<
|
||||
(didApply ? " applied successfully with " :
|
||||
" failed with ") <<
|
||||
@@ -1111,6 +1115,17 @@ TxQ::apply(Application& app, OpenView& view,
|
||||
(void)created;
|
||||
assert(created);
|
||||
}
|
||||
// Modify the flags for use when coming out of the queue.
|
||||
// These changes _may_ cause an extra `preflight`, but as long as
|
||||
// the `HashRouter` still knows about the transaction, the signature
|
||||
// will not be checked again, so the cost should be minimal.
|
||||
|
||||
// Don't allow soft failures, which can lead to retries
|
||||
flags &= ~tapRETRY;
|
||||
|
||||
// Don't queue because we're already in the queue
|
||||
flags &= ~tapPREFER_QUEUE;
|
||||
|
||||
auto& candidate = accountIter->second.add(
|
||||
{ tx, transactionID, feeLevelPaid, flags, pfresult });
|
||||
/* Normally we defer figuring out the consequences until
|
||||
@@ -1122,8 +1137,10 @@ TxQ::apply(Application& app, OpenView& view,
|
||||
// Then index it into the byFee lookup.
|
||||
byFee_.insert(candidate);
|
||||
JLOG(j_.debug()) << "Added transaction " << candidate.txID <<
|
||||
" with result " << transToken(pfresult.ter) <<
|
||||
" from " << (accountExists ? "existing" : "new") <<
|
||||
" account " << candidate.account << " to queue.";
|
||||
" account " << candidate.account << " to queue." <<
|
||||
" Flags: " << flags;
|
||||
|
||||
return { terQUEUED, false };
|
||||
}
|
||||
@@ -1278,14 +1295,15 @@ TxQ::accept(Application& app,
|
||||
|
||||
TER txnResult;
|
||||
bool didApply;
|
||||
std::tie(txnResult, didApply) = candidateIter->apply(app, view);
|
||||
std::tie(txnResult, didApply) = candidateIter->apply(app, view, j_);
|
||||
|
||||
if (didApply)
|
||||
{
|
||||
// Remove the candidate from the queue
|
||||
JLOG(j_.debug()) << "Queued transaction " <<
|
||||
candidateIter->txID <<
|
||||
" applied successfully. Remove from queue.";
|
||||
" applied successfully with " <<
|
||||
transToken(txnResult) << ". Remove from queue.";
|
||||
|
||||
candidateIter = eraseAndAdvance(candidateIter);
|
||||
ledgerChanged = true;
|
||||
@@ -1304,9 +1322,12 @@ TxQ::accept(Application& app,
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG(j_.debug()) << "Transaction " <<
|
||||
JLOG(j_.debug()) << "Queued transaction " <<
|
||||
candidateIter->txID << " failed with " <<
|
||||
transToken(txnResult) << ". Leave in queue.";
|
||||
transToken(txnResult) << ". Leave in queue." <<
|
||||
" Applied: " << didApply <<
|
||||
". Flags: " <<
|
||||
candidateIter->flags;
|
||||
if (account.retryPenalty &&
|
||||
candidateIter->retriesRemaining > 2)
|
||||
candidateIter->retriesRemaining = 1;
|
||||
|
||||
@@ -28,6 +28,16 @@ namespace ripple {
|
||||
class Application;
|
||||
class STTx;
|
||||
|
||||
/** Return true if the transaction can claim a fee (tec),
|
||||
and the `ApplyFlags` do not allow soft failures.
|
||||
*/
|
||||
inline
|
||||
bool
|
||||
isTecClaimHardFail(TER ter, ApplyFlags flags)
|
||||
{
|
||||
return isTecClaim(ter) && !(flags & tapRETRY);
|
||||
}
|
||||
|
||||
/** Describes the results of the `preflight` check
|
||||
|
||||
@note All members are const to make it more difficult
|
||||
@@ -99,7 +109,7 @@ public:
|
||||
, j(ctx_.j)
|
||||
, ter(ter_)
|
||||
, likelyToClaimFee(ter == tesSUCCESS
|
||||
|| isTecClaim(ter))
|
||||
|| isTecClaimHardFail(ter, flags))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -268,4 +278,4 @@ doApply(PreclaimResult const& preclaimResult,
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -88,16 +88,13 @@ preflight1 (PreflightContext const& ctx)
|
||||
NotTEC
|
||||
preflight2 (PreflightContext const& ctx)
|
||||
{
|
||||
if(!( ctx.flags & tapNO_CHECK_SIGN))
|
||||
auto const sigValid = checkValidity(ctx.app.getHashRouter(),
|
||||
ctx.tx, ctx.rules, ctx.app.config());
|
||||
if (sigValid.first == Validity::SigBad)
|
||||
{
|
||||
auto const sigValid = checkValidity(ctx.app.getHashRouter(),
|
||||
ctx.tx, ctx.rules, ctx.app.config());
|
||||
if (sigValid.first == Validity::SigBad)
|
||||
{
|
||||
JLOG(ctx.j.debug()) <<
|
||||
"preflight2: bad signature. " << sigValid.second;
|
||||
return temINVALID;
|
||||
}
|
||||
JLOG(ctx.j.debug()) <<
|
||||
"preflight2: bad signature. " << sigValid.second;
|
||||
return temINVALID;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
}
|
||||
@@ -638,7 +635,7 @@ Transactor::operator()()
|
||||
result = tecOVERSIZE;
|
||||
|
||||
if ((result == tecOVERSIZE) ||
|
||||
(isTecClaim (result) && !(view().flags() & tapRETRY)))
|
||||
(isTecClaimHardFail (result, view().flags())))
|
||||
{
|
||||
JLOG(j_.trace()) << "reapplying because of " << transToken(result);
|
||||
|
||||
|
||||
@@ -30,9 +30,6 @@ enum ApplyFlags
|
||||
{
|
||||
tapNONE = 0x00,
|
||||
|
||||
// Signature already checked
|
||||
tapNO_CHECK_SIGN = 0x01,
|
||||
|
||||
// This is not the transaction's last pass
|
||||
// Transaction can be retried, soft failures allowed
|
||||
tapRETRY = 0x20,
|
||||
@@ -46,24 +43,63 @@ enum ApplyFlags
|
||||
tapUNLIMITED = 0x400,
|
||||
};
|
||||
|
||||
inline
|
||||
constexpr
|
||||
ApplyFlags
|
||||
operator|(ApplyFlags const& lhs,
|
||||
ApplyFlags const& rhs)
|
||||
{
|
||||
return static_cast<ApplyFlags>(
|
||||
static_cast<int>(lhs) |
|
||||
static_cast<int>(rhs));
|
||||
static_cast<std::underlying_type_t<ApplyFlags>>(lhs) |
|
||||
static_cast<std::underlying_type_t<ApplyFlags>>(rhs));
|
||||
}
|
||||
|
||||
inline
|
||||
static_assert((tapPREFER_QUEUE | tapRETRY) == static_cast<ApplyFlags>(0x60),
|
||||
"ApplyFlags operator |");
|
||||
static_assert((tapRETRY | tapPREFER_QUEUE) == static_cast<ApplyFlags>(0x60),
|
||||
"ApplyFlags operator |");
|
||||
|
||||
constexpr
|
||||
ApplyFlags
|
||||
operator&(ApplyFlags const& lhs,
|
||||
ApplyFlags const& rhs)
|
||||
{
|
||||
return static_cast<ApplyFlags>(
|
||||
static_cast<int>(lhs) &
|
||||
static_cast<int>(rhs));
|
||||
static_cast<std::underlying_type_t<ApplyFlags>>(lhs) &
|
||||
static_cast<std::underlying_type_t<ApplyFlags>>(rhs));
|
||||
}
|
||||
|
||||
static_assert((tapPREFER_QUEUE & tapRETRY) == tapNONE,
|
||||
"ApplyFlags operator &");
|
||||
static_assert((tapRETRY & tapPREFER_QUEUE) == tapNONE,
|
||||
"ApplyFlags operator &");
|
||||
|
||||
constexpr
|
||||
ApplyFlags
|
||||
operator~(ApplyFlags const& flags)
|
||||
{
|
||||
return static_cast<ApplyFlags>(
|
||||
~static_cast<std::underlying_type_t<ApplyFlags>>(flags));
|
||||
}
|
||||
|
||||
static_assert(~tapRETRY == static_cast<ApplyFlags>(~0x20),
|
||||
"ApplyFlags operator ~");
|
||||
|
||||
inline
|
||||
ApplyFlags
|
||||
operator|=(ApplyFlags & lhs,
|
||||
ApplyFlags const& rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline
|
||||
ApplyFlags
|
||||
operator&=(ApplyFlags& lhs,
|
||||
ApplyFlags const& rhs)
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -115,7 +115,7 @@ public:
|
||||
{
|
||||
OpenView accum(&*res);
|
||||
applyTransaction(
|
||||
env.app(), accum, *stx, false, tapNO_CHECK_SIGN, env.journal);
|
||||
env.app(), accum, *stx, false, tapNONE, env.journal);
|
||||
accum.apply(*res);
|
||||
}
|
||||
res->updateSkipList();
|
||||
|
||||
@@ -48,7 +48,7 @@ struct LedgerReplay_test : public beast::unit_test::suite
|
||||
|
||||
auto const replayed = buildLedger(
|
||||
LedgerReplay(lastClosedParent,lastClosed),
|
||||
tapNO_CHECK_SIGN,
|
||||
tapNONE,
|
||||
env.app(),
|
||||
env.journal);
|
||||
|
||||
|
||||
@@ -344,6 +344,41 @@ public:
|
||||
256);
|
||||
}
|
||||
|
||||
void testTecResult()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env(*this,
|
||||
makeConfig({ { "minimum_txn_in_ledger_standalone", "2" } }));
|
||||
|
||||
auto alice = Account("alice");
|
||||
auto gw = Account("gw");
|
||||
auto USD = gw["USD"];
|
||||
|
||||
checkMetrics(env, 0, boost::none, 0, 2, 256);
|
||||
|
||||
// Create accounts
|
||||
env.fund(XRP(50000), noripple(alice, gw));
|
||||
checkMetrics(env, 0, boost::none, 2, 2, 256);
|
||||
env.close();
|
||||
checkMetrics(env, 0, 4, 0, 2, 256);
|
||||
|
||||
// Alice creates an unfunded offer while the ledger is not full
|
||||
env(offer(alice, XRP(1000), USD(1000)), ter(tecUNFUNDED_OFFER));
|
||||
checkMetrics(env, 0, 4, 1, 2, 256);
|
||||
|
||||
fillQueue(env, alice);
|
||||
checkMetrics(env, 0, 4, 3, 2, 256);
|
||||
|
||||
// Alice creates an unfunded offer that goes in the queue
|
||||
env(offer(alice, XRP(1000), USD(1000)), ter(terQUEUED));
|
||||
checkMetrics(env, 1, 4, 3, 2, 256);
|
||||
|
||||
// The offer comes out of the queue
|
||||
env.close();
|
||||
checkMetrics(env, 0, 6, 1, 3, 256);
|
||||
}
|
||||
|
||||
void testLocalTxRetry()
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
@@ -363,25 +363,25 @@ class View_test
|
||||
BEAST_EXPECT(v1.parentCloseTime() ==
|
||||
v1.parentCloseTime());
|
||||
|
||||
ApplyViewImpl v2(&v1, tapNO_CHECK_SIGN);
|
||||
ApplyViewImpl v2(&v1, tapRETRY);
|
||||
BEAST_EXPECT(v2.parentCloseTime() ==
|
||||
v1.parentCloseTime());
|
||||
BEAST_EXPECT(v2.seq() == v1.seq());
|
||||
BEAST_EXPECT(v2.flags() == tapNO_CHECK_SIGN);
|
||||
BEAST_EXPECT(v2.flags() == tapRETRY);
|
||||
|
||||
Sandbox v3(&v2);
|
||||
BEAST_EXPECT(v3.seq() == v2.seq());
|
||||
BEAST_EXPECT(v3.parentCloseTime() ==
|
||||
v2.parentCloseTime());
|
||||
BEAST_EXPECT(v3.flags() == tapNO_CHECK_SIGN);
|
||||
BEAST_EXPECT(v3.flags() == tapRETRY);
|
||||
}
|
||||
{
|
||||
ApplyViewImpl v1(&v0, tapNO_CHECK_SIGN);
|
||||
ApplyViewImpl v1(&v0, tapRETRY);
|
||||
PaymentSandbox v2(&v1);
|
||||
BEAST_EXPECT(v2.seq() == v0.seq());
|
||||
BEAST_EXPECT(v2.parentCloseTime() ==
|
||||
v0.parentCloseTime());
|
||||
BEAST_EXPECT(v2.flags() == tapNO_CHECK_SIGN);
|
||||
BEAST_EXPECT(v2.flags() == tapRETRY);
|
||||
PaymentSandbox v3(&v2);
|
||||
BEAST_EXPECT(v3.seq() == v2.seq());
|
||||
BEAST_EXPECT(v3.parentCloseTime() ==
|
||||
|
||||
Reference in New Issue
Block a user