mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 19:25:51 +00:00
Refactor ledger replay logic (RIPD-1547):
Also switch to use ReadView for TxQ updates.
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <ripple/app/consensus/RCLConsensus.h>
|
||||
#include <ripple/app/consensus/RCLValidations.h>
|
||||
#include <ripple/app/ledger/BuildLedger.h>
|
||||
#include <ripple/app/ledger/InboundLedgers.h>
|
||||
#include <ripple/app/ledger/InboundTransactions.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
@@ -31,7 +32,6 @@
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/app/misc/ValidatorKeys.h>
|
||||
#include <ripple/app/misc/ValidatorList.h>
|
||||
#include <ripple/app/tx/apply.h>
|
||||
#include <ripple/basics/make_lock.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/consensus/LedgerTiming.h>
|
||||
@@ -441,9 +441,7 @@ RCLConsensus::Adaptor::doAccept(
|
||||
|
||||
if (validating_)
|
||||
validating_ = ledgerMaster_.isCompatible(
|
||||
*sharedLCL.ledger_,
|
||||
app_.journal("LedgerConsensus").warn(),
|
||||
"Not validating");
|
||||
*sharedLCL.ledger_, j_.warn(), "Not validating");
|
||||
|
||||
if (validating_ && !consensusFail &&
|
||||
app_.getValidations().canValidateSeq(sharedLCL.seq()))
|
||||
@@ -631,103 +629,6 @@ RCLConsensus::Adaptor::notify(
|
||||
JLOG (j_.trace()) << "send status change to peer";
|
||||
}
|
||||
|
||||
/** Apply a set of transactions to a ledger.
|
||||
|
||||
Typically the txFilter is used to reject transactions
|
||||
that already accepted in the prior ledger.
|
||||
|
||||
@param txns set of transactions to apply
|
||||
@param view ledger to apply to
|
||||
@param txFilter callback, return false to reject txn
|
||||
@return retriable transactions
|
||||
*/
|
||||
|
||||
CanonicalTXSet
|
||||
applyTransactions(
|
||||
Application& app,
|
||||
RCLTxSet const& txns,
|
||||
OpenView& view,
|
||||
std::function<bool(uint256 const&)> txFilter)
|
||||
{
|
||||
auto j = app.journal("LedgerConsensus");
|
||||
|
||||
auto& set = *(txns.map_);
|
||||
CanonicalTXSet retriableTxs(set.getHash().as_uint256());
|
||||
|
||||
for (auto const& item : set)
|
||||
{
|
||||
if (!txFilter(item.key()))
|
||||
continue;
|
||||
|
||||
// The transaction wan't filtered
|
||||
// Add it to the set to be tried in canonical order
|
||||
JLOG(j.debug()) << "Processing candidate transaction: " << item.key();
|
||||
try
|
||||
{
|
||||
retriableTxs.insert(
|
||||
std::make_shared<STTx const>(SerialIter{item.slice()}));
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG(j.warn()) << "Txn " << item.key() << " throws";
|
||||
}
|
||||
}
|
||||
|
||||
bool certainRetry = true;
|
||||
// Attempt to apply all of the retriable transactions
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
JLOG(j.debug()) << "Pass: " << pass << " Txns: " << retriableTxs.size()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
int changes = 0;
|
||||
|
||||
auto it = retriableTxs.begin();
|
||||
|
||||
while (it != retriableTxs.end())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (applyTransaction(
|
||||
app, view, *it->second, certainRetry, tapNO_CHECK_SIGN, j))
|
||||
{
|
||||
case ApplyResult::Success:
|
||||
it = retriableTxs.erase(it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case ApplyResult::Fail:
|
||||
it = retriableTxs.erase(it);
|
||||
break;
|
||||
|
||||
case ApplyResult::Retry:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG(j.warn()) << "Transaction throws";
|
||||
it = retriableTxs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(j.debug()) << "Pass: " << pass << " finished " << changes
|
||||
<< " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return retriableTxs;
|
||||
|
||||
// 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(retriableTxs.empty() || !certainRetry);
|
||||
return retriableTxs;
|
||||
}
|
||||
|
||||
RCLCxLedger
|
||||
RCLConsensus::Adaptor::buildLCL(
|
||||
RCLCxLedger const& previousLedger,
|
||||
@@ -738,81 +639,26 @@ RCLConsensus::Adaptor::buildLCL(
|
||||
std::chrono::milliseconds roundTime,
|
||||
CanonicalTXSet& retriableTxs)
|
||||
{
|
||||
auto replay = ledgerMaster_.releaseReplay();
|
||||
if (replay)
|
||||
{
|
||||
// replaying, use the time the ledger we're replaying closed
|
||||
closeTime = replay->closeTime_;
|
||||
closeTimeCorrect = ((replay->closeFlags_ & sLCF_NoConsensusTime) == 0);
|
||||
}
|
||||
|
||||
JLOG(j_.debug()) << "Report: TxSt = " << txns.id() << ", close "
|
||||
<< closeTime.time_since_epoch().count()
|
||||
<< (closeTimeCorrect ? "" : " (incorrect)");
|
||||
|
||||
// Build the new last closed ledger
|
||||
auto buildLCL =
|
||||
std::make_shared<Ledger>(*previousLedger.ledger_, closeTime);
|
||||
|
||||
auto const v2_enabled = buildLCL->rules().enabled(featureSHAMapV2);
|
||||
|
||||
auto v2_transition = false;
|
||||
if (v2_enabled && !buildLCL->stateMap().is_v2())
|
||||
{
|
||||
buildLCL->make_v2();
|
||||
v2_transition = true;
|
||||
}
|
||||
|
||||
// Set up to write SHAMap changes to our database,
|
||||
// perform updates, extract changes
|
||||
JLOG(j_.debug()) << "Applying consensus txns transactions to the"
|
||||
<< " last closed ledger";
|
||||
|
||||
{
|
||||
OpenView accum(&*buildLCL);
|
||||
assert(!accum.open());
|
||||
if (replay)
|
||||
std::shared_ptr<Ledger> buildLCL = [&]() {
|
||||
auto const replayData = ledgerMaster_.releaseReplay();
|
||||
if (replayData)
|
||||
{
|
||||
// Special case, we are replaying a ledger close
|
||||
for (auto& tx : replay->txns_)
|
||||
applyTransaction(
|
||||
app_, accum, *tx.second, false, tapNO_CHECK_SIGN, j_);
|
||||
assert(replayData->parent()->info().hash == previousLedger.id());
|
||||
return buildLedger(*replayData, tapNO_CHECK_SIGN, app_, j_);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal case, we are not replaying a ledger close
|
||||
retriableTxs = applyTransactions(
|
||||
app_, txns, accum, [&buildLCL](uint256 const& txID) {
|
||||
return !buildLCL->txExists(txID);
|
||||
});
|
||||
}
|
||||
// Update fee computations.
|
||||
app_.getTxQ().processClosedLedger(app_, accum, roundTime > 5s);
|
||||
accum.apply(*buildLCL);
|
||||
}
|
||||
return buildLedger(
|
||||
previousLedger.ledger_,
|
||||
closeTime,
|
||||
closeTimeCorrect,
|
||||
closeResolution,
|
||||
*txns.map_,
|
||||
app_,
|
||||
retriableTxs,
|
||||
j_);
|
||||
}();
|
||||
|
||||
// retriableTxs will include any transactions that
|
||||
// made it into the consensus set but failed during application
|
||||
// to the ledger.
|
||||
|
||||
buildLCL->updateSkipList();
|
||||
|
||||
{
|
||||
// Write the final version of all modified SHAMap
|
||||
// nodes to the node store to preserve the new LCL
|
||||
|
||||
int asf = buildLCL->stateMap().flushDirty(
|
||||
hotACCOUNT_NODE, buildLCL->info().seq);
|
||||
int tmf = buildLCL->txMap().flushDirty(
|
||||
hotTRANSACTION_NODE, buildLCL->info().seq);
|
||||
JLOG(j_.debug()) << "Flushed " << asf << " accounts and " << tmf
|
||||
<< " transaction nodes";
|
||||
}
|
||||
buildLCL->unshare();
|
||||
|
||||
// Accept ledger
|
||||
buildLCL->setAccepted(
|
||||
closeTime, closeResolution, closeTimeCorrect, app_.config());
|
||||
// Update fee computations based on accepted txs
|
||||
app_.getTxQ().processClosedLedger(app_, *buildLCL, roundTime > 5s);
|
||||
|
||||
// And stash the ledger in the ledger master
|
||||
if (ledgerMaster_.storeLedger(buildLCL))
|
||||
|
||||
@@ -332,7 +332,7 @@ class RCLConsensus
|
||||
|
||||
@param previousLedger Prior ledger building upon
|
||||
@param txns The set of transactions to apply to the ledger
|
||||
@param closeTime The the ledger closed
|
||||
@param closeTime The time the ledger closed
|
||||
@param closeTimeCorrect Whether consensus agreed on close time
|
||||
@param closeResolution Resolution used to determine consensus close
|
||||
time
|
||||
|
||||
82
src/ripple/app/ledger/BuildLedger.h
Normal file
82
src/ripple/app/ledger/BuildLedger.h
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_BUILD_LEDGER_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_BUILD_LEDGER_H_INCLUDED
|
||||
|
||||
#include <ripple/ledger/ApplyView.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/beast/utility/Journal.h>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Application;
|
||||
class CanonicalTXSet;
|
||||
class Ledger;
|
||||
class LedgerReplay;
|
||||
class SHAMap;
|
||||
|
||||
|
||||
/** Build a new ledger by applying consensus transactions
|
||||
|
||||
Build a new ledger by applying a set of transactions accepted as part of
|
||||
consensus.
|
||||
|
||||
@param parent The ledger to apply transactions to
|
||||
@param closeTime The time the ledger closed
|
||||
@param closeTimeCorrect Whether consensus agreed on close time
|
||||
@param closeResolution Resolution used to determine consensus close time
|
||||
@param txs The consensus transactions to attempt to apply
|
||||
@param app Handle to application instance
|
||||
@param retriableTxs Populate with transactions to retry in next round
|
||||
@param j Journal to use for logging
|
||||
@return The newly built ledger
|
||||
*/
|
||||
std::shared_ptr<Ledger>
|
||||
buildLedger(
|
||||
std::shared_ptr<Ledger const> const& parent,
|
||||
NetClock::time_point closeTime,
|
||||
const bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
SHAMap const& txs,
|
||||
Application& app,
|
||||
CanonicalTXSet& retriableTxs,
|
||||
beast::Journal j);
|
||||
|
||||
/** Build a new ledger by replaying transactions
|
||||
|
||||
Build a new ledger by replaying transactions accepted into a prior ledger.
|
||||
|
||||
@param replayData Data of the ledger to replay
|
||||
@param applyFlags Flags to use when applying transactions
|
||||
@param app Handle to application instance
|
||||
@param j Journal to use for logging
|
||||
@return The newly built ledger
|
||||
*/
|
||||
std::shared_ptr<Ledger>
|
||||
buildLedger(
|
||||
LedgerReplay const& replayData,
|
||||
ApplyFlags applyFlags,
|
||||
Application& app,
|
||||
beast::Journal j);
|
||||
|
||||
} // namespace ripple
|
||||
#endif
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <ripple/app/ledger/LedgerCleaner.h>
|
||||
#include <ripple/app/ledger/LedgerHistory.h>
|
||||
#include <ripple/app/ledger/LedgerHolder.h>
|
||||
#include <ripple/app/ledger/LedgerReplay.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
#include <ripple/basics/chrono.h>
|
||||
#include <ripple/basics/RangeSet.h>
|
||||
@@ -47,13 +48,7 @@ namespace ripple {
|
||||
class Peer;
|
||||
class Transaction;
|
||||
|
||||
struct LedgerReplay
|
||||
{
|
||||
std::map< int, std::shared_ptr<STTx const> > txns_;
|
||||
NetClock::time_point closeTime_;
|
||||
int closeFlags_;
|
||||
std::shared_ptr<Ledger const> prevLedger_;
|
||||
};
|
||||
|
||||
|
||||
// Tracks the current ledger and any ledgers in the process of closing
|
||||
// Tracks ledger history
|
||||
|
||||
70
src/ripple/app/ledger/LedgerReplay.h
Normal file
70
src/ripple/app/ledger/LedgerReplay.h
Normal file
@@ -0,0 +1,70 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
||||
#define RIPPLE_APP_LEDGER_LEDGERREPLAY_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class Ledger;
|
||||
class STTx;
|
||||
|
||||
class LedgerReplay
|
||||
{
|
||||
std::shared_ptr<Ledger const> parent_;
|
||||
std::shared_ptr<Ledger const> replay_;
|
||||
std::map<std::uint32_t, std::shared_ptr<STTx const>> orderedTxns_;
|
||||
|
||||
public:
|
||||
LedgerReplay(
|
||||
std::shared_ptr<Ledger const> parent,
|
||||
std::shared_ptr<Ledger const> replay);
|
||||
|
||||
/** @return The parent of the ledger to replay
|
||||
*/
|
||||
std::shared_ptr<Ledger const> const&
|
||||
parent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
/** @return The ledger to replay
|
||||
*/
|
||||
std::shared_ptr<Ledger const> const&
|
||||
replay() const
|
||||
{
|
||||
return replay_;
|
||||
}
|
||||
|
||||
/** @return Transactions in the order they should be replayed
|
||||
*/
|
||||
std::map<std::uint32_t, std::shared_ptr<STTx const>> const&
|
||||
orderedTxns() const
|
||||
{
|
||||
return orderedTxns_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
235
src/ripple/app/ledger/impl/BuildLedger.cpp
Normal file
235
src/ripple/app/ledger/impl/BuildLedger.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 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 <ripple/app/ledger/BuildLedger.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/ledger/LedgerReplay.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||
#include <ripple/app/tx/apply.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/* Generic buildLedgerImpl that dispatches to ApplyTxs invocable with signature
|
||||
void(OpenView&, std::shared_ptr<Ledger> const&)
|
||||
It is responsible for adding transactions to the open view to generate the
|
||||
new ledger. It is generic since the mechanics differ for consensus
|
||||
generated ledgers versus replayed ledgers.
|
||||
*/
|
||||
template <class ApplyTxs>
|
||||
std::shared_ptr<Ledger>
|
||||
buildLedgerImpl(
|
||||
std::shared_ptr<Ledger const> const& parent,
|
||||
NetClock::time_point closeTime,
|
||||
const bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
Application& app,
|
||||
beast::Journal j,
|
||||
ApplyTxs&& applyTxs)
|
||||
{
|
||||
auto buildLCL = std::make_shared<Ledger>(*parent, closeTime);
|
||||
|
||||
if (buildLCL->rules().enabled(featureSHAMapV2) &&
|
||||
!buildLCL->stateMap().is_v2())
|
||||
{
|
||||
buildLCL->make_v2();
|
||||
}
|
||||
|
||||
// Set up to write SHAMap changes to our database,
|
||||
// perform updates, extract changes
|
||||
|
||||
{
|
||||
OpenView accum(&*buildLCL);
|
||||
assert(!accum.open());
|
||||
applyTxs(accum, buildLCL);
|
||||
accum.apply(*buildLCL);
|
||||
}
|
||||
|
||||
buildLCL->updateSkipList();
|
||||
|
||||
{
|
||||
// Write the final version of all modified SHAMap
|
||||
// nodes to the node store to preserve the new LCL
|
||||
|
||||
int const asf = buildLCL->stateMap().flushDirty(
|
||||
hotACCOUNT_NODE, buildLCL->info().seq);
|
||||
int const tmf = buildLCL->txMap().flushDirty(
|
||||
hotTRANSACTION_NODE, buildLCL->info().seq);
|
||||
JLOG(j.debug()) << "Flushed " << asf << " accounts and " << tmf
|
||||
<< " transaction nodes";
|
||||
}
|
||||
buildLCL->unshare();
|
||||
|
||||
// Accept ledger
|
||||
buildLCL->setAccepted(
|
||||
closeTime, closeResolution, closeTimeCorrect, app.config());
|
||||
|
||||
return buildLCL;
|
||||
}
|
||||
|
||||
/** Apply a set of consensus transactions to a ledger.
|
||||
|
||||
@param app Handle to application
|
||||
@param txns Consensus transactions to apply
|
||||
@param view Ledger to apply to
|
||||
@param buildLCL Ledger to check if transaction already exists
|
||||
@param j Journal for logging
|
||||
@return Any retriable transactions
|
||||
*/
|
||||
|
||||
CanonicalTXSet
|
||||
applyTransactions(
|
||||
Application& app,
|
||||
SHAMap const& txns,
|
||||
OpenView& view,
|
||||
std::shared_ptr<Ledger> const& buildLCL,
|
||||
beast::Journal j)
|
||||
{
|
||||
CanonicalTXSet retriableTxs(txns.getHash().as_uint256());
|
||||
|
||||
for (auto const& item : txns)
|
||||
{
|
||||
if (buildLCL->txExists(item.key()))
|
||||
continue;
|
||||
|
||||
// The transaction wasn't filtered
|
||||
// Add it to the set to be tried in canonical order
|
||||
JLOG(j.debug()) << "Processing candidate transaction: " << item.key();
|
||||
try
|
||||
{
|
||||
retriableTxs.insert(
|
||||
std::make_shared<STTx const>(SerialIter{item.slice()}));
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG(j.warn()) << "Txn " << item.key() << " throws";
|
||||
}
|
||||
}
|
||||
|
||||
bool certainRetry = true;
|
||||
// Attempt to apply all of the retriable transactions
|
||||
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
|
||||
{
|
||||
JLOG(j.debug()) << "Pass: " << pass << " Txns: " << retriableTxs.size()
|
||||
<< (certainRetry ? " retriable" : " final");
|
||||
int changes = 0;
|
||||
|
||||
auto it = retriableTxs.begin();
|
||||
|
||||
while (it != retriableTxs.end())
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (applyTransaction(
|
||||
app, view, *it->second, certainRetry, tapNO_CHECK_SIGN, j))
|
||||
{
|
||||
case ApplyResult::Success:
|
||||
it = retriableTxs.erase(it);
|
||||
++changes;
|
||||
break;
|
||||
|
||||
case ApplyResult::Fail:
|
||||
it = retriableTxs.erase(it);
|
||||
break;
|
||||
|
||||
case ApplyResult::Retry:
|
||||
++it;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
JLOG(j.warn()) << "Transaction throws";
|
||||
it = retriableTxs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
JLOG(j.debug()) << "Pass: " << pass << " finished " << changes
|
||||
<< " changes";
|
||||
|
||||
// A non-retry pass made no changes
|
||||
if (!changes && !certainRetry)
|
||||
return retriableTxs;
|
||||
|
||||
// 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(retriableTxs.empty() || !certainRetry);
|
||||
return retriableTxs;
|
||||
}
|
||||
|
||||
// Build a ledger from consensus transactions
|
||||
std::shared_ptr<Ledger>
|
||||
buildLedger(
|
||||
std::shared_ptr<Ledger const> const& parent,
|
||||
NetClock::time_point closeTime,
|
||||
const bool closeTimeCorrect,
|
||||
NetClock::duration closeResolution,
|
||||
SHAMap const& txs,
|
||||
Application& app,
|
||||
CanonicalTXSet& retriableTxs,
|
||||
beast::Journal j)
|
||||
{
|
||||
JLOG(j.debug()) << "Report: TxSt = " << txs.getHash().as_uint256()
|
||||
<< ", close " << closeTime.time_since_epoch().count()
|
||||
<< (closeTimeCorrect ? "" : " (incorrect)");
|
||||
|
||||
return buildLedgerImpl(
|
||||
parent,
|
||||
closeTime,
|
||||
closeTimeCorrect,
|
||||
closeResolution,
|
||||
app,
|
||||
j,
|
||||
[&](OpenView& accum, std::shared_ptr<Ledger> const& buildLCL) {
|
||||
retriableTxs = applyTransactions(app, txs, accum, buildLCL, j);
|
||||
});
|
||||
}
|
||||
|
||||
// Build a ledger by replaying
|
||||
std::shared_ptr<Ledger>
|
||||
buildLedger(
|
||||
LedgerReplay const& replayData,
|
||||
ApplyFlags applyFlags,
|
||||
Application& app,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto const& replayLedger = replayData.replay();
|
||||
|
||||
JLOG(j.debug()) << "Report: Replay Ledger " << replayLedger->info().hash;
|
||||
|
||||
return buildLedgerImpl(
|
||||
replayData.parent(),
|
||||
replayLedger->info().closeTime,
|
||||
((replayLedger->info().closeFlags & sLCF_NoConsensusTime) == 0),
|
||||
replayLedger->info().closeTimeResolution,
|
||||
app,
|
||||
j,
|
||||
[&](OpenView& accum, std::shared_ptr<Ledger> const& buildLCL) {
|
||||
for (auto& tx : replayData.orderedTxns())
|
||||
applyTransaction(app, accum, *tx.second, false, applyFlags, j);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
38
src/ripple/app/ledger/impl/LedgerReplay.cpp
Normal file
38
src/ripple/app/ledger/impl/LedgerReplay.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 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 <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/ledger/LedgerReplay.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
LedgerReplay::LedgerReplay(
|
||||
std::shared_ptr<Ledger const> parent,
|
||||
std::shared_ptr<Ledger const> replay)
|
||||
: parent_{std::move(parent)}, replay_{std::move(replay)}
|
||||
{
|
||||
for (auto const& item : replay_->txMap())
|
||||
{
|
||||
auto const txPair = replay_->txRead(item.key());
|
||||
auto const txIndex = (*txPair.second)[sfTransactionIndex];
|
||||
orderedTxns_.emplace(txIndex, std::move(txPair.first));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
@@ -1793,27 +1793,20 @@ bool ApplicationImp::loadOldLedger (
|
||||
{
|
||||
// inject transaction(s) from the replayLedger into our open ledger
|
||||
// and build replay structure
|
||||
auto const& txns = replayLedger->txMap();
|
||||
auto replayData = std::make_unique <LedgerReplay> ();
|
||||
auto replayData =
|
||||
std::make_unique<LedgerReplay>(loadLedger, replayLedger);
|
||||
|
||||
replayData->prevLedger_ = replayLedger;
|
||||
replayData->closeTime_ = replayLedger->info().closeTime;
|
||||
replayData->closeFlags_ = replayLedger->info().closeFlags;
|
||||
|
||||
for (auto const& item : txns)
|
||||
for (auto const& it : replayData->orderedTxns())
|
||||
{
|
||||
auto txID = item.key();
|
||||
auto txPair = replayLedger->txRead (txID);
|
||||
auto txIndex = (*txPair.second)[sfTransactionIndex];
|
||||
std::shared_ptr<STTx const> const& tx = it.second;
|
||||
auto txID = tx->getTransactionID();
|
||||
|
||||
auto s = std::make_shared <Serializer> ();
|
||||
txPair.first->add(*s);
|
||||
tx->add(*s);
|
||||
|
||||
forceValidity(getHashRouter(),
|
||||
txID, Validity::SigGoodOnly);
|
||||
|
||||
replayData->txns_.emplace (txIndex, txPair.first);
|
||||
|
||||
openLedger_->modify(
|
||||
[&txID, &s](OpenView& view, beast::Journal j)
|
||||
{
|
||||
|
||||
@@ -1365,8 +1365,7 @@ void NetworkOPsImp::switchLastClosedLedger (
|
||||
clearNeedNetworkLedger ();
|
||||
|
||||
// Update fee computations.
|
||||
// TODO: Needs an open ledger
|
||||
//app_.getTxQ().processClosedLedger(app_, *newLCL, true);
|
||||
app_.getTxQ().processClosedLedger(app_, *newLCL, true);
|
||||
|
||||
// Caller must own master lock
|
||||
{
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
*/
|
||||
void
|
||||
processClosedLedger(Application& app,
|
||||
OpenView const& view, bool timeLeap);
|
||||
ReadView const& view, bool timeLeap);
|
||||
|
||||
/** Returns fee metrics in reference fee level units.
|
||||
|
||||
|
||||
@@ -1146,7 +1146,7 @@ TxQ::apply(Application& app, OpenView& view,
|
||||
*/
|
||||
void
|
||||
TxQ::processClosedLedger(Application& app,
|
||||
OpenView const& view, bool timeLeap)
|
||||
ReadView const& view, bool timeLeap)
|
||||
{
|
||||
auto const allowEscalation =
|
||||
(view.rules().enabled(featureFeeEscalation));
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
//==============================================================================
|
||||
|
||||
|
||||
#include <ripple/app/ledger/impl/BuildLedger.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundLedger.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundLedgers.cpp>
|
||||
#include <ripple/app/ledger/impl/InboundTransactions.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerCleaner.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerMaster.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerReplay.cpp>
|
||||
#include <ripple/app/ledger/impl/LocalTxs.cpp>
|
||||
#include <ripple/app/ledger/impl/OpenLedger.cpp>
|
||||
#include <ripple/app/ledger/impl/LedgerToJson.cpp>
|
||||
|
||||
62
src/test/app/LedgerReplay_test.cpp
Normal file
62
src/test/app/LedgerReplay_test.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2018 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 <test/jtx.h>
|
||||
#include <ripple/app/ledger/BuildLedger.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/LedgerReplay.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
struct LedgerReplay_test : public beast::unit_test::suite
|
||||
{
|
||||
void run() override
|
||||
{
|
||||
testcase("Replay ledger");
|
||||
|
||||
using namespace jtx;
|
||||
|
||||
// Build a ledger normally
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
|
||||
Env env(*this);
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
env.close();
|
||||
|
||||
LedgerMaster& ledgerMaster = env.app().getLedgerMaster();
|
||||
auto const lastClosed = ledgerMaster.getClosedLedger();
|
||||
auto const lastClosedParent =
|
||||
ledgerMaster.getLedgerByHash(lastClosed->info().parentHash);
|
||||
|
||||
auto const replayed = buildLedger(
|
||||
LedgerReplay(lastClosedParent,lastClosed),
|
||||
tapNO_CHECK_SIGN,
|
||||
env.app(),
|
||||
env.journal);
|
||||
|
||||
BEAST_EXPECT(replayed->info().hash == lastClosed->info().hash);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(LedgerReplay,app,ripple);
|
||||
|
||||
} // test
|
||||
} // ripple
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <test/app/HashRouter_test.cpp>
|
||||
#include <test/app/LedgerHistory_test.cpp>
|
||||
#include <test/app/LedgerLoad_test.cpp>
|
||||
#include <test/app/LedgerReplay_test.cpp>
|
||||
#include <test/app/LoadFeeTrack_test.cpp>
|
||||
#include <test/app/Manifest_test.cpp>
|
||||
#include <test/app/MultiSign_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user