fix(hooks): preserve finalization semantics

Keep hook result/state finalization non-fatal while enforcing the hook-export backlog cap through the transaction-level ApplyContext guard. This avoids resetting non-success tec metadata and preserves hook_again weak execution behavior.
This commit is contained in:
Nicholas Dudfield
2026-04-28 07:47:20 +07:00
parent c58da3da58
commit e729aa11eb
2 changed files with 7 additions and 33 deletions

View File

@@ -5,7 +5,6 @@
#include <xrpld/app/misc/NetworkOPs.h>
#include <xrpld/app/misc/Transaction.h>
#include <xrpld/app/misc/TxQ.h>
#include <xrpld/app/tx/detail/ExportLedgerOps.h>
#include <xrpld/app/tx/detail/Import.h>
#include <xrpld/app/tx/detail/NFTokenUtils.h>
#include <xrpld/ledger/View.h>
@@ -1662,8 +1661,6 @@ hook::finalizeHookResult(
std::vector<std::pair<uint256 /* txnid */, uint256 /* emit nonce */>>
emission_txnid;
std::vector<uint256 /* txnid */> exported_txnid;
std::size_t pendingExportEmissions =
ExportLedgerOps::pendingExportEmissionCount(applyCtx.view());
if (doEmit)
{
@@ -1678,29 +1675,12 @@ hook::finalizeHookResult(
std::shared_ptr<const ripple::STTx> ptr =
tpTrans->getSTransaction();
bool const isExportEmission =
ExportLedgerOps::isPendingExportWorkTxn(*ptr);
auto emittedId = keylet::emittedTxn(id);
auto sleEmitted = applyCtx.view().peek(emittedId);
if (!sleEmitted)
{
if (isExportEmission)
{
if (pendingExportEmissions >=
ExportLimits::maxPendingExports)
{
JLOG(j.warn())
<< "HookExport[" << HR_ACC()
<< "]: export emission limit reached pending="
<< pendingExportEmissions
<< " max=" << +ExportLimits::maxPendingExports;
return tecDIR_FULL;
}
++pendingExportEmissions;
}
auto const& emitDetails = const_cast<ripple::STTx&>(*ptr)
.getField(sfEmitDetails)
.downcast<STObject>();
@@ -1740,7 +1720,9 @@ hook::finalizeHookResult(
// Exported txns now flow through the emitted txn path above
// (xport() pushes a ttEXPORT wrapper onto emittedTxn).
// No separate processing needed here.
// The export backlog cap is enforced after hook finalization by
// ApplyContext::checkExportEmissionLimit(), so strong and weak hook
// emissions use the same fee-only reset path.
}
bool const fixV2 = applyCtx.view().rules().enabled(fixXahauV2);

View File

@@ -1972,12 +1972,7 @@ Transactor::operator()()
// write state if all chains executed successfully
if (isTesSuccess(result))
{
if (auto const ter = hook::finalizeHookState(
stateMap, ctx_, ctx_.tx.getTransactionID());
!isTesSuccess(ter))
result = ter;
}
hook::finalizeHookState(stateMap, ctx_, ctx_.tx.getTransactionID());
// write hook results
// this happens irrespective of whether final result was a tesSUCCESS
@@ -1986,12 +1981,9 @@ Transactor::operator()()
for (auto& hookResult : hookResults)
{
if (auto const ter = hook::finalizeHookResult(
hookResult, ctx_, isTesSuccess(result));
isTesSuccess(result) && !isTesSuccess(ter))
result = ter;
hook::finalizeHookResult(hookResult, ctx_, isTesSuccess(result));
if (isTesSuccess(result) && hookResult.executeAgainAsWeak)
if (hookResult.executeAgainAsWeak)
{
if (aawMap.find(hookResult.account) == aawMap.end())
aawMap[hookResult.account] = {hookResult.hookHash};
@@ -2287,7 +2279,7 @@ Transactor::operator()()
result = tecOVERSIZE;
}
if (applied)
if (applied && isTesSuccess(result))
{
auto const limitResult = ctx_.checkExportEmissionLimit(result);
if (!isTesSuccess(limitResult))