mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
151 lines
4.2 KiB
C++
151 lines
4.2 KiB
C++
#include <xrpl/tx/ApplyContext.h>
|
|
//
|
|
#include <xrpl/basics/Log.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/json/to_string.h>
|
|
#include <xrpl/tx/invariants/InvariantCheck.h>
|
|
|
|
namespace xrpl {
|
|
|
|
ApplyContext::ApplyContext(
|
|
ServiceRegistry& registry_,
|
|
OpenView& base,
|
|
std::optional<uint256 const> const& parentBatchId,
|
|
STTx const& tx_,
|
|
TER preclaimResult_,
|
|
XRPAmount baseFee_,
|
|
ApplyFlags flags,
|
|
beast::Journal journal_)
|
|
: registry(registry_)
|
|
, tx(tx_)
|
|
, preclaimResult(preclaimResult_)
|
|
, baseFee(baseFee_)
|
|
, journal(journal_)
|
|
, base_(base)
|
|
, flags_(flags)
|
|
, parentBatchId_(parentBatchId)
|
|
{
|
|
XRPL_ASSERT(
|
|
parentBatchId.has_value() == ((flags_ & tapBATCH) == tapBATCH),
|
|
"Parent Batch ID should be set if batch apply flag is set");
|
|
view_.emplace(&base_.view(), flags_);
|
|
}
|
|
|
|
void
|
|
ApplyContext::discard()
|
|
{
|
|
base_.discard();
|
|
view_.emplace(&base_.view(), flags_);
|
|
}
|
|
|
|
void
|
|
ApplyContext::finalize()
|
|
{
|
|
base_.commit();
|
|
view_.emplace(&base_.view(), flags_);
|
|
}
|
|
|
|
std::optional<TxMeta>
|
|
ApplyContext::apply(TER ter)
|
|
{
|
|
if (wasmReturnCode_.has_value())
|
|
{
|
|
view_->setWasmReturnCode(*wasmReturnCode_);
|
|
}
|
|
view_->setGasUsed(gasUsed_);
|
|
return view_->apply(
|
|
base_.view(), tx, ter, parentBatchId_, (flags_ & tapDRY_RUN) != 0u, journal);
|
|
}
|
|
|
|
std::size_t
|
|
ApplyContext::size()
|
|
{
|
|
return view_->size();
|
|
}
|
|
|
|
void
|
|
ApplyContext::visit(
|
|
std::function<void(
|
|
uint256 const&,
|
|
bool,
|
|
std::shared_ptr<SLE const> const&,
|
|
std::shared_ptr<SLE const> const&)> const& func)
|
|
{
|
|
view_->visit(base_.view(), func);
|
|
}
|
|
|
|
TER
|
|
ApplyContext::failInvariantCheck(TER const result)
|
|
{
|
|
// If we already failed invariant checks before and we are now attempting to
|
|
// only charge a fee, and even that fails the invariant checks something is
|
|
// very wrong. We switch to tefINVARIANT_FAILED, which does NOT get included
|
|
// in a ledger.
|
|
|
|
return (result == tecINVARIANT_FAILED || result == tefINVARIANT_FAILED)
|
|
? TER{tefINVARIANT_FAILED}
|
|
: TER{tecINVARIANT_FAILED};
|
|
}
|
|
|
|
template <std::size_t... Is>
|
|
TER
|
|
ApplyContext::checkInvariantsHelper(
|
|
TER const result,
|
|
XRPAmount const fee,
|
|
std::index_sequence<Is...>)
|
|
{
|
|
try
|
|
{
|
|
auto checkers = getInvariantChecks();
|
|
|
|
// call each check's per-entry method
|
|
visit([&checkers](
|
|
uint256 const& index,
|
|
bool isDelete,
|
|
std::shared_ptr<SLE const> const& before,
|
|
std::shared_ptr<SLE const> const& after) {
|
|
(..., std::get<Is>(checkers).visitEntry(isDelete, before, after));
|
|
});
|
|
|
|
// Note: do not replace this logic with a `...&&` fold expression.
|
|
// The fold expression will only run until the first check fails (it
|
|
// short-circuits). While the logic is still correct, the log
|
|
// message won't be. Every failed invariant should write to the log,
|
|
// not just the first one.
|
|
std::array<bool, sizeof...(Is)> const finalizers{
|
|
{std::get<Is>(checkers).finalize(tx, result, fee, *view_, journal)...}};
|
|
|
|
// call each check's finalizer to see that it passes
|
|
if (!std::all_of(finalizers.cbegin(), finalizers.cend(), [](auto const& b) { return b; }))
|
|
{
|
|
JLOG(journal.fatal()) << "Transaction has failed one or more invariants: "
|
|
<< to_string(tx.getJson(JsonOptions::none));
|
|
|
|
return failInvariantCheck(result);
|
|
}
|
|
}
|
|
catch (std::exception const& ex)
|
|
{
|
|
JLOG(journal.fatal()) << "Transaction caused an exception in an invariant"
|
|
<< ", ex: " << ex.what()
|
|
<< ", tx: " << to_string(tx.getJson(JsonOptions::none));
|
|
|
|
return failInvariantCheck(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
TER
|
|
ApplyContext::checkInvariants(TER const result, XRPAmount const fee)
|
|
{
|
|
XRPL_ASSERT(
|
|
isTesSuccess(result) || isTecClaim(result),
|
|
"xrpl::ApplyContext::checkInvariants : is tesSUCCESS or tecCLAIM");
|
|
|
|
return checkInvariantsHelper(
|
|
result, fee, std::make_index_sequence<std::tuple_size<InvariantChecks>::value>{});
|
|
}
|
|
|
|
} // namespace xrpl
|