From 02455f9bfc465a8070103fc11d19d6f9380e5346 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 3 Apr 2026 10:30:01 -0400 Subject: [PATCH] move to helper function --- include/xrpl/tx/Transactor.h | 6 ++ src/libxrpl/tx/Transactor.cpp | 192 ++++++++++++++++++---------------- 2 files changed, 105 insertions(+), 93 deletions(-) diff --git a/include/xrpl/tx/Transactor.h b/include/xrpl/tx/Transactor.h index 287f785cd7..0b5cfc1222 100644 --- a/include/xrpl/tx/Transactor.h +++ b/include/xrpl/tx/Transactor.h @@ -299,8 +299,13 @@ private: TER consumeSeqProxy(SLE::pointer const& sleAccount); + TER payFee(); + + void + processPersistentChanges(TER result, XRPAmount& fee, bool& applied); + static NotTEC checkSingleSign( ReadView const& view, @@ -308,6 +313,7 @@ private: AccountID const& idAccount, std::shared_ptr sleAccount, beast::Journal const j); + static NotTEC checkMultiSign( ReadView const& view, diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index ba70dde144..33ac150a56 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -1071,6 +1071,104 @@ Transactor::trapTransaction(uint256 txHash) const JLOG(j_.debug()) << "Transaction trapped: " << txHash; } +void +Transactor::processPersistentChanges(TER result, XRPAmount& fee, bool& applied) +{ + JLOG(j_.trace()) << "reapplying because of " << transToken(result); + + // FIXME: This mechanism for doing work while returning a `tec` is + // awkward and very limiting. A more general purpose approach + // should be used, making it possible to do more useful work + // when transactions fail with a `tec` code. + + // Build a list of ledger entry types to collect, based on the + // result code. Only deleted objects of these types will be + // re-applied after the context is reset. + std::vector typesToCollect; + if ((result == tecOVERSIZE) || (result == tecKILLED)) + typesToCollect.push_back(ltOFFER); + if (result == tecINCOMPLETE) + typesToCollect.push_back(ltRIPPLE_STATE); + if (result == tecEXPIRED) + { + typesToCollect.push_back(ltNFTOKEN_OFFER); + typesToCollect.push_back(ltCREDENTIAL); + } + + std::map> deletedObjects; + if (!typesToCollect.empty()) + { + ctx_.visit([&typesToCollect, &deletedObjects]( + uint256 const& index, + bool isDelete, + std::shared_ptr const& before, + std::shared_ptr const& after) { + if (isDelete) + { + XRPL_ASSERT( + before && after, + "xrpl::Transactor::operator()::visit : non-null SLE " + "inputs"); + if (before && after) + { + auto const type = before->getType(); + if (std::ranges::find(typesToCollect, type) != typesToCollect.end()) + { + // For offers, only collect unfunded removals + // (where TakerPays is unchanged) + if (type == ltOFFER && + before->getFieldAmount(sfTakerPays) != + after->getFieldAmount(sfTakerPays)) + return; + + deletedObjects[type].push_back(index); + } + } + } + }); + } + + // Reset the context, potentially adjusting the fee. + { + auto const resetResult = reset(fee); + if (!isTesSuccess(resetResult.first)) + result = resetResult.first; + + fee = resetResult.second; + } + + // Re-apply the collected deletions + auto const viewJ = ctx_.registry.get().getJournal("View"); + for (auto const& [type, ids] : deletedObjects) + { + if (ids.empty()) + continue; + + switch (type) + { + case ltOFFER: + removeUnfundedOffers(view(), ids, viewJ); + break; + case ltNFTOKEN_OFFER: + removeExpiredNFTokenOffers(view(), ids, viewJ); + break; + case ltRIPPLE_STATE: + removeDeletedTrustLines(view(), ids, viewJ); + break; + case ltCREDENTIAL: + removeExpiredCredentials(view(), ids, viewJ); + break; + // LCOV_EXCL_START + default: + UNREACHABLE("xrpl::Transactor::operator() : unexpected type"); + break; + // LCOV_EXCL_STOP + } + } + + applied = isTecClaim(result); +} + //------------------------------------------------------------------------------ ApplyResult Transactor::operator()() @@ -1139,99 +1237,7 @@ Transactor::operator()() (result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) || (result == tecEXPIRED) || (isTecClaimHardFail(result, view().flags()))) { - JLOG(j_.trace()) << "reapplying because of " << transToken(result); - - // FIXME: This mechanism for doing work while returning a `tec` is - // awkward and very limiting. A more general purpose approach - // should be used, making it possible to do more useful work - // when transactions fail with a `tec` code. - - // Build a list of ledger entry types to collect, based on the - // result code. Only deleted objects of these types will be - // re-applied after the context is reset. - std::vector typesToCollect; - if ((result == tecOVERSIZE) || (result == tecKILLED)) - typesToCollect.push_back(ltOFFER); - if (result == tecINCOMPLETE) - typesToCollect.push_back(ltRIPPLE_STATE); - if (result == tecEXPIRED) - { - typesToCollect.push_back(ltNFTOKEN_OFFER); - typesToCollect.push_back(ltCREDENTIAL); - } - - std::map> deletedObjects; - if (!typesToCollect.empty()) - { - ctx_.visit([&typesToCollect, &deletedObjects]( - uint256 const& index, - bool isDelete, - std::shared_ptr const& before, - std::shared_ptr const& after) { - if (isDelete) - { - XRPL_ASSERT( - before && after, - "xrpl::Transactor::operator()::visit : non-null SLE " - "inputs"); - if (before && after) - { - auto const type = before->getType(); - if (std::ranges::find(typesToCollect, type) != typesToCollect.end()) - { - // For offers, only collect unfunded removals - // (where TakerPays is unchanged) - if (type == ltOFFER && - before->getFieldAmount(sfTakerPays) != - after->getFieldAmount(sfTakerPays)) - return; - - deletedObjects[type].push_back(index); - } - } - } - }); - } - - // Reset the context, potentially adjusting the fee. - { - auto const resetResult = reset(fee); - if (!isTesSuccess(resetResult.first)) - result = resetResult.first; - - fee = resetResult.second; - } - - // Re-apply the collected deletions - auto const viewJ = ctx_.registry.get().getJournal("View"); - for (auto const& [type, ids] : deletedObjects) - { - if (ids.empty()) - continue; - - switch (type) - { - case ltOFFER: - removeUnfundedOffers(view(), ids, viewJ); - break; - case ltNFTOKEN_OFFER: - removeExpiredNFTokenOffers(view(), ids, viewJ); - break; - case ltRIPPLE_STATE: - removeDeletedTrustLines(view(), ids, viewJ); - break; - case ltCREDENTIAL: - removeExpiredCredentials(view(), ids, viewJ); - break; - // LCOV_EXCL_START - default: - UNREACHABLE("xrpl::Transactor::operator() : unexpected type"); - break; - // LCOV_EXCL_STOP - } - } - - applied = isTecClaim(result); + processPersistentChanges(result, fee, applied); } if (applied)