mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
API for improved Unit Testing (RIPD-432):
* Added new test APIs allowing easy ways to create ledgers, apply transactions to them, close and advance them. * Moved Ledger tests from Ledger.cpp to Ledger.test.cpp. * Changed several TransactionEngine log priorities from lsINFO to lsDEBUG to reduce unnecessary verbosity in the log messages while running these tests. * Moved LedgerConsensus:applyTransactions from a private member function to a free function so that it could be accessed externally, and without having to reference a LedgerConsensus object. This was done to facilitate the new testing API.
This commit is contained in:
committed by
Nik Bougalis
parent
c62ccf4870
commit
eafa6f960f
@@ -1452,174 +1452,6 @@ private:
|
||||
msg, protocol::mtHAVE_SET)));
|
||||
}
|
||||
|
||||
/** Apply a set of transactions to a ledger
|
||||
|
||||
@param set The set of transactions to apply
|
||||
@param applyLedger The ledger to which the transactions should
|
||||
be applied.
|
||||
@param checkLedger A reference ledger for determining error
|
||||
messages (typically new last closed ledger).
|
||||
@param retriableTransactions collect failed transactions in this set
|
||||
@param openLgr true if applyLedger is open, else false.
|
||||
*/
|
||||
void applyTransactions (SHAMap::ref set, Ledger::ref applyLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions,
|
||||
bool openLgr)
|
||||
{
|
||||
TransactionEngine engine (applyLedger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
for (SHAMapItem::pointer item = set->peekFirstItem (); !!item;
|
||||
item = set->peekNextItem (item->getTag ()))
|
||||
{
|
||||
// If the checkLedger doesn't have the transaction
|
||||
if (!checkLedger->hasTransaction (item->getTag ()))
|
||||
{
|
||||
// Then try to apply the transaction to applyLedger
|
||||
WriteLog (lsINFO, LedgerConsensus) <<
|
||||
"Processing candidate transaction: " << item->getTag ();
|
||||
try
|
||||
{
|
||||
SerializerIterator sit (item->peekSerializer ());
|
||||
STTx::pointer txn
|
||||
= std::make_shared<STTx>(sit);
|
||||
if (applyTransaction (engine, txn,
|
||||
openLgr, true) == resultRetry)
|
||||
{
|
||||
// On failure, stash the failed transaction for
|
||||
// later retry.
|
||||
retriableTransactions.push_back (txn);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus) << " Throws";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int changes;
|
||||
bool certainRetry = true;
|
||||
// Attempt to apply all of the retriable transactions
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: " << pass << " Txns: "
|
||||
<< retriableTransactions.size ()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
changes = 0;
|
||||
|
||||
auto it = retriableTransactions.begin ();
|
||||
|
||||
while (it != retriableTransactions.end ())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (applyTransaction (engine, it->second,
|
||||
openLgr, certainRetry))
|
||||
{
|
||||
case resultSuccess:
|
||||
it = retriableTransactions.erase (it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case resultFail:
|
||||
it = retriableTransactions.erase (it);
|
||||
break;
|
||||
|
||||
case resultRetry:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus)
|
||||
<< "Transaction throws";
|
||||
it = retriableTransactions.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: "
|
||||
<< pass << " finished " << changes << " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return;
|
||||
|
||||
// Stop retriable passes
|
||||
if ((!changes) || (pass >= LEDGER_RETRY_PASSES))
|
||||
certainRetry = false;
|
||||
}
|
||||
|
||||
// If there are any transactions left, we must have
|
||||
// tried them in at least one final pass
|
||||
assert (retriableTransactions.empty() || !certainRetry);
|
||||
}
|
||||
|
||||
/** Apply a transaction to a ledger
|
||||
|
||||
@param engine The transaction engine containing the ledger.
|
||||
@param txn The transaction to be applied to ledger.
|
||||
@param openLedger true if ledger is open
|
||||
@param retryAssured true if the transaction should be retried on failure.
|
||||
@return One of resultSuccess, resultFail or resultRetry.
|
||||
*/
|
||||
int applyTransaction (TransactionEngine& engine
|
||||
, STTx::ref txn, bool openLedger, bool retryAssured)
|
||||
{
|
||||
// Returns false if the transaction has need not be retried.
|
||||
TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE;
|
||||
|
||||
if (retryAssured)
|
||||
{
|
||||
parms = static_cast<TransactionEngineParams> (parms | tapRETRY);
|
||||
}
|
||||
|
||||
if (getApp().getHashRouter ().setFlag (txn->getTransactionID ()
|
||||
, SF_SIGGOOD))
|
||||
{
|
||||
parms = static_cast<TransactionEngineParams>
|
||||
(parms | tapNO_CHECK_SIGN);
|
||||
}
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "TXN "
|
||||
<< txn->getTransactionID ()
|
||||
<< (openLedger ? " open" : " closed")
|
||||
<< (retryAssured ? "/retry" : "/final");
|
||||
WriteLog (lsTRACE, LedgerConsensus) << txn->getJson (0);
|
||||
|
||||
try
|
||||
{
|
||||
bool didApply;
|
||||
TER result = engine.applyTransaction (*txn, parms, didApply);
|
||||
|
||||
if (didApply)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction success: " << transHuman (result);
|
||||
return resultSuccess;
|
||||
}
|
||||
|
||||
if (isTefFailure (result) || isTemMalformed (result) ||
|
||||
isTelLocal (result))
|
||||
{
|
||||
// failure
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction failure: " << transHuman (result);
|
||||
return resultFail;
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction retry: " << transHuman (result);
|
||||
return resultRetry;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus) << "Throws";
|
||||
return resultFail;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Round the close time to the close time resolution.
|
||||
|
||||
@@ -2164,4 +1996,173 @@ make_LedgerConsensus (LedgerConsensus::clock_type& clock, LocalTxs& localtx,
|
||||
prevLCLHash, previousLedger, closeTime, feeVote);
|
||||
}
|
||||
|
||||
/** Apply a transaction to a ledger
|
||||
|
||||
@param engine The transaction engine containing the ledger.
|
||||
@param txn The transaction to be applied to ledger.
|
||||
@param openLedger true if ledger is open
|
||||
@param retryAssured true if the transaction should be retried on failure.
|
||||
@return One of resultSuccess, resultFail or resultRetry.
|
||||
*/
|
||||
static
|
||||
int applyTransaction (TransactionEngine& engine
|
||||
, STTx::ref txn, bool openLedger, bool retryAssured)
|
||||
{
|
||||
// Returns false if the transaction has need not be retried.
|
||||
TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE;
|
||||
|
||||
if (retryAssured)
|
||||
{
|
||||
parms = static_cast<TransactionEngineParams> (parms | tapRETRY);
|
||||
}
|
||||
|
||||
if (getApp().getHashRouter ().setFlag (txn->getTransactionID ()
|
||||
, SF_SIGGOOD))
|
||||
{
|
||||
parms = static_cast<TransactionEngineParams>
|
||||
(parms | tapNO_CHECK_SIGN);
|
||||
}
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "TXN "
|
||||
<< txn->getTransactionID ()
|
||||
<< (openLedger ? " open" : " closed")
|
||||
<< (retryAssured ? "/retry" : "/final");
|
||||
WriteLog (lsTRACE, LedgerConsensus) << txn->getJson (0);
|
||||
|
||||
try
|
||||
{
|
||||
bool didApply;
|
||||
TER result = engine.applyTransaction (*txn, parms, didApply);
|
||||
|
||||
if (didApply)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction success: " << transHuman (result);
|
||||
return LedgerConsensusImp::resultSuccess;
|
||||
}
|
||||
|
||||
if (isTefFailure (result) || isTemMalformed (result) ||
|
||||
isTelLocal (result))
|
||||
{
|
||||
// failure
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction failure: " << transHuman (result);
|
||||
return LedgerConsensusImp::resultFail;
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, LedgerConsensus)
|
||||
<< "Transaction retry: " << transHuman (result);
|
||||
return LedgerConsensusImp::resultRetry;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus) << "Throws";
|
||||
return LedgerConsensusImp::resultFail;
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply a set of transactions to a ledger
|
||||
|
||||
@param set The set of transactions to apply
|
||||
@param applyLedger The ledger to which the transactions should
|
||||
be applied.
|
||||
@param checkLedger A reference ledger for determining error
|
||||
messages (typically new last closed ledger).
|
||||
@param retriableTransactions collect failed transactions in this set
|
||||
@param openLgr true if applyLedger is open, else false.
|
||||
*/
|
||||
void applyTransactions (SHAMap::ref set, Ledger::ref applyLedger,
|
||||
Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions,
|
||||
bool openLgr)
|
||||
{
|
||||
TransactionEngine engine (applyLedger);
|
||||
|
||||
if (set)
|
||||
{
|
||||
for (SHAMapItem::pointer item = set->peekFirstItem (); !!item;
|
||||
item = set->peekNextItem (item->getTag ()))
|
||||
{
|
||||
// If the checkLedger doesn't have the transaction
|
||||
if (!checkLedger->hasTransaction (item->getTag ()))
|
||||
{
|
||||
// Then try to apply the transaction to applyLedger
|
||||
WriteLog (lsINFO, LedgerConsensus) <<
|
||||
"Processing candidate transaction: " << item->getTag ();
|
||||
try
|
||||
{
|
||||
SerializerIterator sit (item->peekSerializer ());
|
||||
STTx::pointer txn
|
||||
= std::make_shared<STTx>(sit);
|
||||
if (applyTransaction (engine, txn,
|
||||
openLgr, true) == LedgerConsensusImp::resultRetry)
|
||||
{
|
||||
// On failure, stash the failed transaction for
|
||||
// later retry.
|
||||
retriableTransactions.push_back (txn);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus) << " Throws";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int changes;
|
||||
bool certainRetry = true;
|
||||
// Attempt to apply all of the retriable transactions
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: " << pass << " Txns: "
|
||||
<< retriableTransactions.size ()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
changes = 0;
|
||||
|
||||
auto it = retriableTransactions.begin ();
|
||||
|
||||
while (it != retriableTransactions.end ())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (applyTransaction (engine, it->second,
|
||||
openLgr, certainRetry))
|
||||
{
|
||||
case LedgerConsensusImp::resultSuccess:
|
||||
it = retriableTransactions.erase (it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case LedgerConsensusImp::resultFail:
|
||||
it = retriableTransactions.erase (it);
|
||||
break;
|
||||
|
||||
case LedgerConsensusImp::resultRetry:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
WriteLog (lsWARNING, LedgerConsensus)
|
||||
<< "Transaction throws";
|
||||
it = retriableTransactions.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, LedgerConsensus) << "Pass: "
|
||||
<< pass << " finished " << changes << " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return;
|
||||
|
||||
// Stop retriable passes
|
||||
if ((!changes) || (pass >= LEDGER_RETRY_PASSES))
|
||||
certainRetry = false;
|
||||
}
|
||||
|
||||
// If there are any transactions left, we must have
|
||||
// tried them in at least one final pass
|
||||
assert (retriableTransactions.empty() || !certainRetry);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
Reference in New Issue
Block a user