Files
rippled/src/xrpld/app/ledger/OpenLedger.h
2026-07-13 10:40:40 +00:00

285 lines
7.6 KiB
C++

#pragma once
#include <xrpld/core/Config.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/PerfLog.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/CachedSLEs.h>
#include <xrpl/ledger/CanonicalTXSet.h>
#include <xrpl/ledger/Ledger.h>
#include <xrpl/ledger/OpenView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/shamap/SHAMap.h>
#include <exception>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
namespace xrpl {
// How many total extra passes we make
// We must ensure we make at least one non-retriable pass
#define LEDGER_TOTAL_PASSES 3
// How many extra retry passes we
// make if the previous retry pass made changes
#define LEDGER_RETRY_PASSES 1
using OrderedTxs = CanonicalTXSet;
//------------------------------------------------------------------------------
/**
* Represents the open ledger.
*/
class OpenLedger
{
private:
beast::Journal const j_;
CachedSLEs& cache_;
std::mutex mutable modifyMutex_;
std::mutex mutable currentMutex_;
std::shared_ptr<OpenView const> current_;
public:
/**
* Signature for modification functions.
*
* The modification function is called during
* apply and modify with an OpenView to accumulate
* changes and the Journal to use for logging.
*
* A return value of `true` informs OpenLedger
* that changes were made. Always returning
* `true` won't cause harm, but it may be
* sub-optimal.
*/
using modify_type = std::function<bool(OpenView&, beast::Journal)>;
OpenLedger() = delete;
OpenLedger(OpenLedger const&) = delete;
OpenLedger&
operator=(OpenLedger const&) = delete;
/**
* Create a new open ledger object.
*
* @param ledger A closed ledger
*/
explicit OpenLedger(
std::shared_ptr<Ledger const> const& ledger,
CachedSLEs& cache,
beast::Journal journal);
/**
* Returns `true` if there are no transactions.
*
* The behavior of ledger closing can be different
* depending on whether or not transactions exist
* in the open ledger.
*
* @note The value returned is only meaningful for
* that specific instant in time. An open,
* empty ledger can become non empty from
* subsequent modifications. Caller is
* responsible for synchronizing the meaning of
* the return value.
*/
bool
empty() const;
/**
* Returns a view to the current open ledger.
*
* Thread safety:
* Can be called concurrently from any thread.
*
* Effects:
* The caller is given ownership of a
* non-modifiable snapshot of the open ledger
* at the time of the call.
*/
std::shared_ptr<OpenView const>
current() const;
/**
* Modify the open ledger
*
* Thread safety:
* Can be called concurrently from any thread.
*
* If `f` returns `true`, the changes made in the
* OpenView will be published to the open ledger.
*
* @return `true` if the open view was changed
*/
bool
modify(modify_type const& f);
/**
* Accept a new ledger.
*
* Thread safety:
* Can be called concurrently from any thread.
*
* Effects:
*
* A new open view based on the accepted ledger
* is created, and the list of retriable
* transactions is optionally applied first
* depending on the value of `retriesFirst`.
*
* The transactions in the current open view
* are applied to the new open view.
*
* The list of local transactions are applied
* to the new open view.
*
* The optional modify function f is called
* to perform further modifications to the
* open view, atomically. Changes made in
* the modify function are not visible to
* callers until accept() returns.
*
* Any failed, retriable transactions are left
* in `retries` for the caller.
*
* The current view is atomically set to the
* new open view.
*
* @param rules The rules for the open ledger
* @param ledger A new closed ledger
*/
void
accept(
Application& app,
Rules const& rules,
std::shared_ptr<Ledger const> const& ledger,
OrderedTxs const& locals,
bool retriesFirst,
OrderedTxs& retries,
ApplyFlags flags,
std::string_view suffix = "",
modify_type const& f = {});
private:
/**
* Algorithm for applying transactions.
*
* This has the retry logic and ordering semantics
* used for consensus and building the open ledger.
*/
template <class FwdRange>
static void
apply(
Application& app,
OpenView& view,
ReadView const& check,
FwdRange const& txs,
OrderedTxs& retries,
ApplyFlags flags,
beast::Journal j);
enum class Result { Success, Failure, Retry };
std::shared_ptr<OpenView>
create(Rules const& rules, std::shared_ptr<Ledger const> const& ledger);
static Result
applyOne(
Application& app,
OpenView& view,
std::shared_ptr<STTx const> const& tx,
bool retry,
ApplyFlags flags,
beast::Journal j);
};
//------------------------------------------------------------------------------
template <class FwdRange>
void
OpenLedger::apply(
Application& app,
OpenView& view,
ReadView const& check,
FwdRange const& txs,
OrderedTxs& retries,
ApplyFlags flags,
beast::Journal j)
{
for (auto iter = txs.begin(); iter != txs.end(); ++iter)
{
try
{
// Dereferencing the iterator can throw since it may be transformed.
auto const tx = *iter;
auto const txId = tx->getTransactionID();
if (check.txExists(txId))
continue;
auto const result = applyOne(app, view, tx, true, flags, j);
if (result == Result::Retry)
retries.insert(tx);
}
catch (std::exception const& e)
{
JLOG(j.error()) << "OpenLedger::apply: Caught exception: " << e.what();
}
}
bool retry = true;
for (int pass = 0; pass < LEDGER_TOTAL_PASSES; ++pass)
{
int changes = 0;
auto iter = retries.begin();
while (iter != retries.end())
{
switch (applyOne(app, view, iter->second, retry, flags, j))
{
case Result::Success:
++changes;
[[fallthrough]];
case Result::Failure:
iter = retries.erase(iter);
break;
case Result::Retry:
++iter;
}
}
// A non-retry pass made no changes
if (!changes && !retry)
return;
// Stop retriable passes
if (!changes || (pass >= LEDGER_RETRY_PASSES))
retry = false;
}
// If there are any transactions left, we must have
// tried them in at least one final pass
XRPL_ASSERT(retries.empty() || !retry, "xrpl::OpenLedger::apply : valid retries");
}
//------------------------------------------------------------------------------
// For debug logging
std::string
debugTxstr(std::shared_ptr<STTx const> const& tx);
std::string
debugTostr(OrderedTxs const& set);
std::string
debugTostr(SHAMap const& set);
std::string
debugTostr(std::shared_ptr<ReadView const> const& view);
} // namespace xrpl