mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-20 10:35:50 +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/RCLConsensus.h>
|
||||||
#include <ripple/app/consensus/RCLValidations.h>
|
#include <ripple/app/consensus/RCLValidations.h>
|
||||||
|
#include <ripple/app/ledger/BuildLedger.h>
|
||||||
#include <ripple/app/ledger/InboundLedgers.h>
|
#include <ripple/app/ledger/InboundLedgers.h>
|
||||||
#include <ripple/app/ledger/InboundTransactions.h>
|
#include <ripple/app/ledger/InboundTransactions.h>
|
||||||
#include <ripple/app/ledger/LedgerMaster.h>
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
#include <ripple/app/misc/TxQ.h>
|
#include <ripple/app/misc/TxQ.h>
|
||||||
#include <ripple/app/misc/ValidatorKeys.h>
|
#include <ripple/app/misc/ValidatorKeys.h>
|
||||||
#include <ripple/app/misc/ValidatorList.h>
|
#include <ripple/app/misc/ValidatorList.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
|
||||||
#include <ripple/basics/make_lock.h>
|
#include <ripple/basics/make_lock.h>
|
||||||
#include <ripple/beast/core/LexicalCast.h>
|
#include <ripple/beast/core/LexicalCast.h>
|
||||||
#include <ripple/consensus/LedgerTiming.h>
|
#include <ripple/consensus/LedgerTiming.h>
|
||||||
@@ -441,9 +441,7 @@ RCLConsensus::Adaptor::doAccept(
|
|||||||
|
|
||||||
if (validating_)
|
if (validating_)
|
||||||
validating_ = ledgerMaster_.isCompatible(
|
validating_ = ledgerMaster_.isCompatible(
|
||||||
*sharedLCL.ledger_,
|
*sharedLCL.ledger_, j_.warn(), "Not validating");
|
||||||
app_.journal("LedgerConsensus").warn(),
|
|
||||||
"Not validating");
|
|
||||||
|
|
||||||
if (validating_ && !consensusFail &&
|
if (validating_ && !consensusFail &&
|
||||||
app_.getValidations().canValidateSeq(sharedLCL.seq()))
|
app_.getValidations().canValidateSeq(sharedLCL.seq()))
|
||||||
@@ -631,103 +629,6 @@ RCLConsensus::Adaptor::notify(
|
|||||||
JLOG (j_.trace()) << "send status change to peer";
|
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
|
RCLCxLedger
|
||||||
RCLConsensus::Adaptor::buildLCL(
|
RCLConsensus::Adaptor::buildLCL(
|
||||||
RCLCxLedger const& previousLedger,
|
RCLCxLedger const& previousLedger,
|
||||||
@@ -738,81 +639,26 @@ RCLConsensus::Adaptor::buildLCL(
|
|||||||
std::chrono::milliseconds roundTime,
|
std::chrono::milliseconds roundTime,
|
||||||
CanonicalTXSet& retriableTxs)
|
CanonicalTXSet& retriableTxs)
|
||||||
{
|
{
|
||||||
auto replay = ledgerMaster_.releaseReplay();
|
std::shared_ptr<Ledger> buildLCL = [&]() {
|
||||||
if (replay)
|
auto const replayData = ledgerMaster_.releaseReplay();
|
||||||
{
|
if (replayData)
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
// Special case, we are replaying a ledger close
|
assert(replayData->parent()->info().hash == previousLedger.id());
|
||||||
for (auto& tx : replay->txns_)
|
return buildLedger(*replayData, tapNO_CHECK_SIGN, app_, j_);
|
||||||
applyTransaction(
|
|
||||||
app_, accum, *tx.second, false, tapNO_CHECK_SIGN, j_);
|
|
||||||
}
|
}
|
||||||
else
|
return buildLedger(
|
||||||
{
|
previousLedger.ledger_,
|
||||||
// Normal case, we are not replaying a ledger close
|
closeTime,
|
||||||
retriableTxs = applyTransactions(
|
closeTimeCorrect,
|
||||||
app_, txns, accum, [&buildLCL](uint256 const& txID) {
|
closeResolution,
|
||||||
return !buildLCL->txExists(txID);
|
*txns.map_,
|
||||||
});
|
app_,
|
||||||
}
|
retriableTxs,
|
||||||
// Update fee computations.
|
j_);
|
||||||
app_.getTxQ().processClosedLedger(app_, accum, roundTime > 5s);
|
}();
|
||||||
accum.apply(*buildLCL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// retriableTxs will include any transactions that
|
// Update fee computations based on accepted txs
|
||||||
// made it into the consensus set but failed during application
|
app_.getTxQ().processClosedLedger(app_, *buildLCL, roundTime > 5s);
|
||||||
// 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());
|
|
||||||
|
|
||||||
// And stash the ledger in the ledger master
|
// And stash the ledger in the ledger master
|
||||||
if (ledgerMaster_.storeLedger(buildLCL))
|
if (ledgerMaster_.storeLedger(buildLCL))
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ class RCLConsensus
|
|||||||
|
|
||||||
@param previousLedger Prior ledger building upon
|
@param previousLedger Prior ledger building upon
|
||||||
@param txns The set of transactions to apply to the ledger
|
@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 closeTimeCorrect Whether consensus agreed on close time
|
||||||
@param closeResolution Resolution used to determine consensus close
|
@param closeResolution Resolution used to determine consensus close
|
||||||
time
|
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/LedgerCleaner.h>
|
||||||
#include <ripple/app/ledger/LedgerHistory.h>
|
#include <ripple/app/ledger/LedgerHistory.h>
|
||||||
#include <ripple/app/ledger/LedgerHolder.h>
|
#include <ripple/app/ledger/LedgerHolder.h>
|
||||||
|
#include <ripple/app/ledger/LedgerReplay.h>
|
||||||
#include <ripple/app/misc/CanonicalTXSet.h>
|
#include <ripple/app/misc/CanonicalTXSet.h>
|
||||||
#include <ripple/basics/chrono.h>
|
#include <ripple/basics/chrono.h>
|
||||||
#include <ripple/basics/RangeSet.h>
|
#include <ripple/basics/RangeSet.h>
|
||||||
@@ -47,13 +48,7 @@ namespace ripple {
|
|||||||
class Peer;
|
class Peer;
|
||||||
class Transaction;
|
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 the current ledger and any ledgers in the process of closing
|
||||||
// Tracks ledger history
|
// 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
|
// inject transaction(s) from the replayLedger into our open ledger
|
||||||
// and build replay structure
|
// and build replay structure
|
||||||
auto const& txns = replayLedger->txMap();
|
auto replayData =
|
||||||
auto replayData = std::make_unique <LedgerReplay> ();
|
std::make_unique<LedgerReplay>(loadLedger, replayLedger);
|
||||||
|
|
||||||
replayData->prevLedger_ = replayLedger;
|
for (auto const& it : replayData->orderedTxns())
|
||||||
replayData->closeTime_ = replayLedger->info().closeTime;
|
|
||||||
replayData->closeFlags_ = replayLedger->info().closeFlags;
|
|
||||||
|
|
||||||
for (auto const& item : txns)
|
|
||||||
{
|
{
|
||||||
auto txID = item.key();
|
std::shared_ptr<STTx const> const& tx = it.second;
|
||||||
auto txPair = replayLedger->txRead (txID);
|
auto txID = tx->getTransactionID();
|
||||||
auto txIndex = (*txPair.second)[sfTransactionIndex];
|
|
||||||
|
|
||||||
auto s = std::make_shared <Serializer> ();
|
auto s = std::make_shared <Serializer> ();
|
||||||
txPair.first->add(*s);
|
tx->add(*s);
|
||||||
|
|
||||||
forceValidity(getHashRouter(),
|
forceValidity(getHashRouter(),
|
||||||
txID, Validity::SigGoodOnly);
|
txID, Validity::SigGoodOnly);
|
||||||
|
|
||||||
replayData->txns_.emplace (txIndex, txPair.first);
|
|
||||||
|
|
||||||
openLedger_->modify(
|
openLedger_->modify(
|
||||||
[&txID, &s](OpenView& view, beast::Journal j)
|
[&txID, &s](OpenView& view, beast::Journal j)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1365,8 +1365,7 @@ void NetworkOPsImp::switchLastClosedLedger (
|
|||||||
clearNeedNetworkLedger ();
|
clearNeedNetworkLedger ();
|
||||||
|
|
||||||
// Update fee computations.
|
// 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
|
// Caller must own master lock
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
processClosedLedger(Application& app,
|
processClosedLedger(Application& app,
|
||||||
OpenView const& view, bool timeLeap);
|
ReadView const& view, bool timeLeap);
|
||||||
|
|
||||||
/** Returns fee metrics in reference fee level units.
|
/** Returns fee metrics in reference fee level units.
|
||||||
|
|
||||||
|
|||||||
@@ -1146,7 +1146,7 @@ TxQ::apply(Application& app, OpenView& view,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TxQ::processClosedLedger(Application& app,
|
TxQ::processClosedLedger(Application& app,
|
||||||
OpenView const& view, bool timeLeap)
|
ReadView const& view, bool timeLeap)
|
||||||
{
|
{
|
||||||
auto const allowEscalation =
|
auto const allowEscalation =
|
||||||
(view.rules().enabled(featureFeeEscalation));
|
(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/InboundLedger.cpp>
|
||||||
#include <ripple/app/ledger/impl/InboundLedgers.cpp>
|
#include <ripple/app/ledger/impl/InboundLedgers.cpp>
|
||||||
#include <ripple/app/ledger/impl/InboundTransactions.cpp>
|
#include <ripple/app/ledger/impl/InboundTransactions.cpp>
|
||||||
#include <ripple/app/ledger/impl/LedgerCleaner.cpp>
|
#include <ripple/app/ledger/impl/LedgerCleaner.cpp>
|
||||||
#include <ripple/app/ledger/impl/LedgerMaster.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/LocalTxs.cpp>
|
||||||
#include <ripple/app/ledger/impl/OpenLedger.cpp>
|
#include <ripple/app/ledger/impl/OpenLedger.cpp>
|
||||||
#include <ripple/app/ledger/impl/LedgerToJson.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/HashRouter_test.cpp>
|
||||||
#include <test/app/LedgerHistory_test.cpp>
|
#include <test/app/LedgerHistory_test.cpp>
|
||||||
#include <test/app/LedgerLoad_test.cpp>
|
#include <test/app/LedgerLoad_test.cpp>
|
||||||
|
#include <test/app/LedgerReplay_test.cpp>
|
||||||
#include <test/app/LoadFeeTrack_test.cpp>
|
#include <test/app/LoadFeeTrack_test.cpp>
|
||||||
#include <test/app/Manifest_test.cpp>
|
#include <test/app/Manifest_test.cpp>
|
||||||
#include <test/app/MultiSign_test.cpp>
|
#include <test/app/MultiSign_test.cpp>
|
||||||
|
|||||||
Reference in New Issue
Block a user