#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { ApplyContext::ApplyContext( ServiceRegistry& registry, OpenView& base, std::optional 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_, flags_); } void ApplyContext::discard() { view_.emplace(&base_, flags_); } std::optional ApplyContext::apply(TER ter) { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) view_ emplaced in constructor return view_->apply(base_, tx, ter, parentBatchId_, (flags_ & TapDryRun) != 0u, journal); } std::size_t ApplyContext::size() { return view_->size(); // NOLINT(bugprone-unchecked-optional-access) } void ApplyContext::visit( std::function const& func) { view_->visit(base_, func); // NOLINT(bugprone-unchecked-optional-access) } 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 TER ApplyContext::checkInvariantsHelper( TER const result, XRPAmount const fee, std::index_sequence) { try { auto checkers = getInvariantChecks(); // call each check's per-entry method visit( [&checkers]( uint256 const& index, bool isDelete, SLE::const_ref before, SLE::const_ref after) { (..., std::get(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 const finalizers{{std::get(checkers).finalize( tx, result, fee, *view_, journal)...}}; // NOLINT(bugprone-unchecked-optional-access) // 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 global invariants: " << to_string(tx.getJson(JsonOptions::Values::None)); return failInvariantCheck(result); } } catch (std::exception const& ex) { JLOG(journal.fatal()) << "Transaction caused an exception in a global invariant" << ", ex: " << ex.what() << ", tx: " << to_string(tx.getJson(JsonOptions::Values::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>{}); } } // namespace xrpl