Refactor ledger replay logic (RIPD-1547):

Also switch to use ReadView for TxQ updates.
This commit is contained in:
Brad Chase
2018-04-09 15:54:37 -04:00
committed by seelabs
parent f31ca2860f
commit 681df58b61
14 changed files with 521 additions and 198 deletions

View File

@@ -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))

View File

@@ -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

View 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

View File

@@ -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

View 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

View 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

View 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

View File

@@ -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)
{

View File

@@ -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
{

View File

@@ -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.

View File

@@ -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));

View File

@@ -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>

View 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

View File

@@ -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>