From b01bbcae5c22e55070f60792113e4d3fbda8a9af Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Mon, 16 May 2022 10:18:01 +0000 Subject: [PATCH] mock txmeta and pass to weakly executed hooks, add meta_slot to access this data --- src/ripple/app/hook/Enum.h | 2 +- src/ripple/app/hook/applyHook.h | 22 +- src/ripple/app/hook/impl/applyHook.cpp | 51 +++- src/ripple/app/tx/impl/ApplyContext.h | 7 + src/ripple/app/tx/impl/Transactor.cpp | 233 +++++++++++++----- src/ripple/app/tx/impl/Transactor.h | 24 +- src/ripple/ledger/ApplyViewImpl.h | 2 + src/ripple/ledger/detail/ApplyStateTable.h | 13 +- src/ripple/ledger/impl/ApplyStateTable.cpp | 270 +++++++++++---------- src/ripple/ledger/impl/ApplyViewImpl.cpp | 10 + 10 files changed, 421 insertions(+), 213 deletions(-) diff --git a/src/ripple/app/hook/Enum.h b/src/ripple/app/hook/Enum.h index 992f1be18..90898a992 100644 --- a/src/ripple/app/hook/Enum.h +++ b/src/ripple/app/hook/Enum.h @@ -284,7 +284,7 @@ namespace hook_api "hook_param_set", "hook_skip", "hook_after", - "hook_weak" + "meta_slot" }; }; #endif diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index 547046802..8d1bdd874 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -152,8 +152,7 @@ namespace hook_api DECLARE_HOOK_FUNCTION(int64_t, hook_param, uint32_t write_ptr, uint32_t write_len, uint32_t read_ptr, uint32_t read_len); - DECLARE_HOOK_FUNCNARG(int64_t, hook_after); - DECLARE_HOOK_FUNCNARG(int64_t, hook_weak); + DECLARE_HOOK_FUNCNARG(int64_t, hook_again); DECLARE_HOOK_FUNCTION(int64_t, hook_skip, uint32_t read_ptr, uint32_t read_len, uint32_t flags); DECLARE_HOOK_FUNCNARG(int64_t, hook_pos); @@ -195,6 +194,7 @@ namespace hook_api DECLARE_HOOK_FUNCNARG(int64_t, otxn_type ); DECLARE_HOOK_FUNCTION(int64_t, otxn_slot, uint32_t slot_no ); + DECLARE_HOOK_FUNCTION(int64_t, meta_slot, uint32_t slot_no ); } /* end namespace hook_api */ @@ -225,10 +225,12 @@ namespace hook ripple::ApplyContext& applyCtx, ripple::AccountID const& account, /* the account the hook is INSTALLED ON not always the otxn account */ bool hasCallback, - bool isCallback = false, - bool isStrongTSH = false, - uint32_t wasmParam = 0, - int32_t hookChainPosition = -1 + bool isCallback, + bool isStrongTSH, + uint32_t wasmParam, + int32_t hookChainPosition, + // result of apply() if this is weak exec + std::shared_ptr const& provisionalMeta ); struct HookContext; @@ -280,8 +282,9 @@ namespace hook uint32_t overrideCount = 0; int32_t hookChainPosition = -1; bool foreignStateSetDisabled = false; - bool executeAgainAsWeak = false; // hook_after allows strong pre-apply to nominate + bool executeAgainAsWeak = false; // hook_again allows strong pre-apply to nominate // additional weak post-apply execution + std::shared_ptr provisionalMeta; }; class HookExecutor; @@ -554,8 +557,7 @@ namespace hook ADD_HOOK_FUNCTION(otxn_slot, ctx); ADD_HOOK_FUNCTION(hook_account, ctx); ADD_HOOK_FUNCTION(hook_hash, ctx); - ADD_HOOK_FUNCTION(hook_after, ctx); - ADD_HOOK_FUNCTION(hook_weak, ctx); + ADD_HOOK_FUNCTION(hook_again, ctx); ADD_HOOK_FUNCTION(fee_base, ctx); ADD_HOOK_FUNCTION(ledger_seq, ctx); ADD_HOOK_FUNCTION(ledger_last_hash, ctx); @@ -588,6 +590,8 @@ namespace hook ADD_HOOK_FUNCTION(trace_num, ctx); ADD_HOOK_FUNCTION(trace_float, ctx); + ADD_HOOK_FUNCTION(meta_slot, ctx); + WasmEdge_TableInstanceContext* hostTable = WasmEdge_TableInstanceCreate(tableType); WasmEdge_ImportObjectAddTable(importObj, tableName, hostTable); WasmEdge_MemoryInstanceContext* hostMem = WasmEdge_MemoryInstanceCreate(memType); diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 45523550b..3c8d828f8 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -683,7 +683,8 @@ hook::apply( bool isCallback, bool isStrong, uint32_t wasmParam, - int32_t hookChainPosition) + int32_t hookChainPosition, + std::shared_ptr const& provisionalMeta) { HookContext hookCtx = @@ -712,7 +713,8 @@ hook::apply( .isStrong = isStrong, .wasmParam = wasmParam, .hookChainPosition = hookChainPosition, - .foreignStateSetDisabled = false + .foreignStateSetDisabled = false, + .provisionalMeta = provisionalMeta }, .emitFailure = isCallback && wasmParam & 1 @@ -4493,15 +4495,7 @@ DEFINE_HOOK_FUNCNARG( DEFINE_HOOK_FUNCNARG( int64_t, - hook_weak) -{ - HOOK_SETUP(); - return (hookCtx.result.isStrong ? 0 : 1); -} - -DEFINE_HOOK_FUNCNARG( - int64_t, - hook_after) + hook_again) { HOOK_SETUP(); @@ -4516,3 +4510,38 @@ DEFINE_HOOK_FUNCNARG( return PREREQUISITE_NOT_MET; } + +DEFINE_HOOK_FUNCTION( + int64_t, + meta_slot, + uint32_t slot_into ) +{ + HOOK_SETUP(); + if (!hookCtx.result.provisionalMeta) + return PREREQUISITE_NOT_MET; + + if (slot_into > hook_api::max_slots) + return INVALID_ARGUMENT; + + // check if we can emplace the object to a slot + if (slot_into == 0 && no_free_slots(hookCtx)) + return NO_FREE_SLOTS; + + if (slot_into == 0) + slot_into = get_free_slot(hookCtx); + + hookCtx.slot.emplace( std::pair { slot_into, hook::SlotEntry { + .id = { + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, + 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, + }, + .storage = hookCtx.result.provisionalMeta, + .entry = 0 + }}); + hookCtx.slot[slot_into].entry = &(*hookCtx.slot[slot_into].storage); + + return slot_into; +} + diff --git a/src/ripple/app/tx/impl/ApplyContext.h b/src/ripple/app/tx/impl/ApplyContext.h index 7fdc887de..7c6aab3fb 100644 --- a/src/ripple/app/tx/impl/ApplyContext.h +++ b/src/ripple/app/tx/impl/ApplyContext.h @@ -101,6 +101,13 @@ public: view_->rawDestroyXRP(fee); } + TxMeta + generateProvisionalMeta() + { + return view_->generateProvisionalMeta(base_, tx, journal); + } + + /** Applies all invariant checkers one by one. @param result the result generated by processing this transaction. diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 37b8a7c0e..bc808f0c5 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -901,7 +901,8 @@ executeHookChain( hook::HookStateMap& stateMap, std::vector& results, ripple::AccountID const& account, - bool strong) + bool strong, + std::shared_ptr const& provisionalMeta) { std::set hookSkips; std::map< @@ -978,8 +979,9 @@ executeHookChain( hasCallback, false, strong, - 0, - hook_no)); + (strong ? 0 : 1UL), // 0 = strong, 1 = weak + hook_no, + provisionalMeta)); executedHookCount_++; @@ -1015,7 +1017,7 @@ executeHookChain( } void -Transactor::doHookCallback() +Transactor::doHookCallback(std::shared_ptr const& provisionalMeta) { // Finally check if there is a callback if (!ctx_.tx.isFieldPresent(sfEmitDetails)) @@ -1117,7 +1119,8 @@ Transactor::doHookCallback() false, safe_cast(ctx_.tx.getFieldU16(sfTransactionType)) == ttEMIT_FAILURE ? 1UL : 0UL, - hook_no - 1); + hook_no - 1, + provisionalMeta); bool success = callbackResult.exitType == hook_api::ExitType::ACCEPT; @@ -1185,7 +1188,8 @@ Transactor:: doTSH( bool strong, // only strong iff true, only weak iff false hook::HookStateMap& stateMap, - std::vector& results) + std::vector& results, + std::shared_ptr const& provisionalMeta) { auto& view = ctx_.view(); @@ -1298,7 +1302,8 @@ doTSH( stateMap, results, tshAccountID, - strong); + strong, + provisionalMeta); if (canRollback && (tshResult != tesSUCCESS)) return tshResult; @@ -1307,6 +1312,111 @@ doTSH( return tesSUCCESS; } +void +Transactor::doAaw( + AccountID const& hookAccountID, + std::set const& hookHashes, + hook::HookStateMap& stateMap, + std::vector& results, + std::shared_ptr const& provisionalMeta) +{ + + auto const& hooksArray = view().peek(keylet::hook(hookAccountID)); + if (!hooksArray) + { + JLOG(j_.warn()) + << "HookError[]: Hook missing on aaw"; + return; + } + + if (!hooksArray->isFieldPresent(sfHooks)) + { + JLOG(j_.warn()) + << "HookError[]: Hooks Array missing on aaw"; + return; + } + + auto const& hooks = hooksArray->getFieldArray(sfHooks); + int hook_no = 0; + for (auto const& hook : hooks) + { + hook_no++; + + STObject const* hookObj = dynamic_cast(&hook); + + if (!hookObj->isFieldPresent(sfHookHash)) // skip blanks + continue; + + uint256 const& hookHash = hookObj->getFieldH256(sfHookHash); + + if (hookHashes.find(hookObj->getFieldH256(sfHookHash)) == hookHashes.end()) + continue; + + auto const& hookDef = view().peek(keylet::hookDefinition(hookHash)); + if (!hookDef) + { + JLOG(j_.warn()) + << "HookError[]: Hook def missing on aaw, hash: " + << hookHash; + continue; + } + + // fetch the namespace either from the hook object of, if absent, the hook def + uint256 const& ns = + (hookObj->isFieldPresent(sfHookNamespace) + ? hookObj->getFieldH256(sfHookNamespace) + : hookDef->getFieldH256(sfHookNamespace)); + + executedHookCount_++; + + std::map, std::vector> parameters; + if (hook::gatherHookParameters(hookDef, hookObj, parameters, j_)) + { + JLOG(j_.warn()) + << "HookError[]: Failure: gatherHookParameters failed)"; + return; + } + + try + { + hook::HookResult aawResult = + hook::apply( + hookDef->getFieldH256(sfHookSetTxnID), + hookHash, + ns, + hookDef->getFieldVL(sfCreateCode), + parameters, + {}, + stateMap, + ctx_, + hookAccountID, + hookDef->isFieldPresent(sfHookCallbackFee), + false, + false, + 2UL, // param 2 = aaw + hook_no - 1, + provisionalMeta); + + + results.push_back(aawResult); + + JLOG(j_.trace()) + << "HookInfo[" << hookAccountID << "-" < Transactor::operator()() @@ -1337,12 +1447,9 @@ Transactor::operator()() bool const hooksEnabled = view().rules().enabled(featureHooks); - // This vector stores any "strong" hooks that requested a weak "after application" re-execution. - std::vector< - std::pair< - AccountID, // account of hook - uint256 // hash of hook - >> executeAgainAsWeak; + // AgainAsWeak map stores information about accounts whose strongly executed hooks + // request an additional weak execution after the otxn has finished application to the ledger + std::map> aawMap; // Pre-application (Strong TSH) Hooks are executed here // These TSH have the right to rollback. @@ -1355,8 +1462,7 @@ Transactor::operator()() hook::HookStateMap stateMap; auto const& accountID = ctx_.tx.getAccountID(sfAccount); - std::vector orgResults; - std::vector tshResults; + std::vector hookResults; auto const& hooksOriginator = view().read(keylet::hook(accountID)); @@ -1366,9 +1472,10 @@ Transactor::operator()() executeHookChain( hooksOriginator, stateMap, - orgResults, + hookResults, accountID, - true); + true, + {}); if (isTesSuccess(result)) { @@ -1376,7 +1483,7 @@ Transactor::operator()() // here. Note these are only strong TSH (who have the right to rollback the txn), // any weak TSH will be executed after doApply has been successful (callback as well) - result = doTSH(true, stateMap, tshResults); + result = doTSH(true, stateMap, hookResults, {}); } // write state if all chains executed successfully @@ -1387,20 +1494,18 @@ Transactor::operator()() // this happens irrespective of whether final result was a tesSUCCESS // because it contains error codes that any failed hooks would have // returned for meta - for (auto& orgResult: orgResults) - { - hook::finalizeHookResult(orgResult, ctx_, isTesSuccess(result)); - if (isTesSuccess(result) && orgResult.executeAgainAsWeak) - executeAgainAsWeak.emplace_back(orgResult.account, orgResult.hookHash); - } - for (auto& tshResult: tshResults) + for (auto& hookResult: hookResults) { - hook::finalizeHookResult(tshResult, ctx_, isTesSuccess(result)); - if (isTesSuccess(result) && tshResult.executeAgainAsWeak) - executeAgainAsWeak.emplace_back(tshResult.account, tshResult.hookHash); + hook::finalizeHookResult(hookResult, ctx_, isTesSuccess(result)); + if (isTesSuccess(result) && hookResult.executeAgainAsWeak) + { + if (aawMap.find(hookResult.account) == aawMap.end()) + aawMap[hookResult.account] = {hookResult.hookHash}; + else + aawMap[hookResult.account].emplace(hookResult.hookHash); + } } - } // fall through allows normal apply @@ -1416,38 +1521,6 @@ Transactor::operator()() bool applied = isTesSuccess(result); - // Post-application (Weak TSH) Hooks are executed here. - // These TSH do not have the ability to rollback. - // The callback, if any, is also executed here. - if (hooksEnabled && applied) - { - // perform callback logic if applicable - if (ctx_.tx.isFieldPresent(sfEmitDetails)) - doHookCallback(); - - // remove emission entry if this is an emitted transaction - hook::removeEmissionEntry(ctx_); - - // process weak TSH - hook::HookStateMap stateMap; - std::vector tshResults; - - doTSH(false, stateMap, tshResults); - - // execute any hooks that nominated for 'again as weak' - for (auto const& [accID, hash] : executeAgainAsWeak) - { - - // RH UPTO: execute individual hooks again here - } - - // write hook results - hook::finalizeHookState(stateMap, ctx_, ctx_.tx.getTransactionID()); - for (auto& tshResult: tshResults) - hook::finalizeHookResult(tshResult, ctx_, isTesSuccess(result)); - } - - auto fee = ctx_.tx.getFieldAmount(sfFee).xrp(); if (ctx_.size() > oversizeMetaDataCap) @@ -1533,6 +1606,44 @@ Transactor::operator()() if (!isTecClaim(result) && !isTesSuccess(result)) applied = false; } + + // Post-application (Weak TSH/AAW) Hooks are executed here. + // These TSH do not have the ability to rollback. + // The callback, if any, is also executed here. + if (applied && hooksEnabled) + { + // weakly executed hooks have access to a provisional TxMeta + // for this tx application. + std::shared_ptr + proMeta = std::make_shared(std::move(ctx_.generateProvisionalMeta().getAsObject())); + + // perform callback logic if applicable + if (ctx_.tx.isFieldPresent(sfEmitDetails)) + doHookCallback(proMeta); + + // remove emission entry if this is an emitted transaction + hook::removeEmissionEntry(ctx_); + + // process weak TSH + hook::HookStateMap stateMap; + std::vector weakResults; + + doTSH(false, stateMap, weakResults, proMeta); + + // execute any hooks that nominated for 'again as weak' + for (auto const& [accID, hookHashes] : aawMap) + doAaw(accID, hookHashes, stateMap, weakResults, proMeta); + + // write hook results + hook::finalizeHookState(stateMap, ctx_, ctx_.tx.getTransactionID()); + for (auto& weakResult: weakResults) + hook::finalizeHookResult(weakResult, ctx_, isTesSuccess(result)); + + if (ctx_.size() > oversizeMetaDataCap) + result = tecOVERSIZE; + } + + if (applied) { diff --git a/src/ripple/app/tx/impl/Transactor.h b/src/ripple/app/tx/impl/Transactor.h index 41cf0e6dc..2d6eee797 100644 --- a/src/ripple/app/tx/impl/Transactor.h +++ b/src/ripple/app/tx/impl/Transactor.h @@ -191,13 +191,27 @@ public: protected: void - doHookCallback(); + doHookCallback( + std::shared_ptr const& provisionalMeta); TER doTSH( bool strong, // only do strong TSH iff true, otheriwse only weak hook::HookStateMap& stateMap, - std::vector& results); + std::vector& result, + std::shared_ptr const& provisionalMeta); + + + // Execute a hook "Again As Weak" is a feature that allows + // a hook that which is being executed pre-application of the otxn + // to request an additional post-application execution. + void + doAaw( + AccountID const& hookAccountID, + std::set const& hookHashes, + hook::HookStateMap& stateMap, + std::vector& results, + std::shared_ptr const& provisionalMeta); TER executeHookChain( @@ -205,8 +219,10 @@ protected: hook::HookStateMap& stateMap, std::vector& results, ripple::AccountID const& account, - bool strong); - + bool strong, + std::shared_ptr const& provisionalMeta); + + void addWeakTSHFromSandbox(detail::ApplyViewBase const& pv); diff --git a/src/ripple/ledger/ApplyViewImpl.h b/src/ripple/ledger/ApplyViewImpl.h index 8530c4414..64de15d51 100644 --- a/src/ripple/ledger/ApplyViewImpl.h +++ b/src/ripple/ledger/ApplyViewImpl.h @@ -72,6 +72,8 @@ public: deliver_ = amount; } + TxMeta + generateProvisionalMeta(OpenView const& to, STTx const& tx, beast::Journal j); /* Set hook metadata for a hook execution * Takes ownership / use std::move diff --git a/src/ripple/ledger/detail/ApplyStateTable.h b/src/ripple/ledger/detail/ApplyStateTable.h index 9ab3dc14d..3c028fd48 100644 --- a/src/ripple/ledger/detail/ApplyStateTable.h +++ b/src/ripple/ledger/detail/ApplyStateTable.h @@ -27,8 +27,10 @@ #include #include #include +#include #include + namespace ripple { namespace detail { @@ -37,6 +39,7 @@ class ApplyStateTable { public: using key_type = ReadView::key_type; + using Mods = hash_map>; private: enum class Action { @@ -64,6 +67,15 @@ public: void apply(RawView& to) const; + + std::pair + generateTxMeta( + OpenView const& to, + STTx const& tx, + std::optional const& deliver, + std::vector const& hookExecution, + beast::Journal j); + void apply( OpenView& to, @@ -126,7 +138,6 @@ public: } private: - using Mods = hash_map>; static void threadItem(TxMeta& meta, std::shared_ptr const& to); diff --git a/src/ripple/ledger/impl/ApplyStateTable.cpp b/src/ripple/ledger/impl/ApplyStateTable.cpp index 8c592789f..dc83d731f 100644 --- a/src/ripple/ledger/impl/ApplyStateTable.cpp +++ b/src/ripple/ledger/impl/ApplyStateTable.cpp @@ -109,6 +109,147 @@ ApplyStateTable::visit( } } +// return a txmeta and a set of created nodes +// does not modify ledger +std::pair +ApplyStateTable::generateTxMeta( + OpenView const& to, + STTx const& tx, + std::optional const& deliver, + std::vector const& hookExecution, + beast::Journal j) +{ + TxMeta meta(tx.getTransactionID(), to.seq()); + if (deliver) + meta.setDeliveredAmount(*deliver); + + if (!hookExecution.empty()) + meta.setHookExecutions(STArray{hookExecution, sfHookExecutions}); + + Mods newMod; + for (auto& item : items_) + { + SField const* type; + switch (item.second.first) + { + default: + case Action::cache: + continue; + case Action::erase: + type = &sfDeletedNode; + break; + case Action::insert: + type = &sfCreatedNode; + break; + case Action::modify: + type = &sfModifiedNode; + break; + } + auto const origNode = to.read(keylet::unchecked(item.first)); + auto curNode = item.second.second; + if ((type == &sfModifiedNode) && (*curNode == *origNode)) + continue; + std::uint16_t nodeType = curNode + ? curNode->getFieldU16(sfLedgerEntryType) + : origNode->getFieldU16(sfLedgerEntryType); + meta.setAffectedNode(item.first, *type, nodeType); + if (type == &sfDeletedNode) + { + assert(origNode && curNode); + threadOwners(to, meta, origNode, newMod, j); + + STObject prevs(sfPreviousFields); + for (auto const& obj : *origNode) + { + // go through the original node for + // modified fields saved on modification + if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && + !curNode->hasMatchingEntry(obj)) + prevs.emplace_back(obj); + } + + if (!prevs.empty()) + meta.getAffectedNode(item.first) + .emplace_back(std::move(prevs)); + + STObject finals(sfFinalFields); + for (auto const& obj : *curNode) + { + // go through the final node for final fields + if (obj.getFName().shouldMeta( + SField::sMD_Always | SField::sMD_DeleteFinal)) + finals.emplace_back(obj); + } + + if (!finals.empty()) + meta.getAffectedNode(item.first) + .emplace_back(std::move(finals)); + } + else if (type == &sfModifiedNode) + { + assert(curNode && origNode); + + if (curNode->isThreadedType()) // thread transaction to node + // item modified + threadItem(meta, curNode); + + STObject prevs(sfPreviousFields); + for (auto const& obj : *origNode) + { + // search the original node for values saved on modify + if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && + !curNode->hasMatchingEntry(obj)) + prevs.emplace_back(obj); + } + + if (!prevs.empty()) + meta.getAffectedNode(item.first) + .emplace_back(std::move(prevs)); + + STObject finals(sfFinalFields); + for (auto const& obj : *curNode) + { + // search the final node for values saved always + if (obj.getFName().shouldMeta( + SField::sMD_Always | SField::sMD_ChangeNew)) + finals.emplace_back(obj); + } + + if (!finals.empty()) + meta.getAffectedNode(item.first) + .emplace_back(std::move(finals)); + } + else if (type == &sfCreatedNode) // if created, thread to owner(s) + { + assert(curNode && !origNode); + threadOwners(to, meta, curNode, newMod, j); + + if (curNode->isThreadedType()) // always thread to self + threadItem(meta, curNode); + + STObject news(sfNewFields); + for (auto const& obj : *curNode) + { + // save non-default values + if (!obj.isDefault() && + obj.getFName().shouldMeta( + SField::sMD_Create | SField::sMD_Always)) + news.emplace_back(obj); + } + + if (!news.empty()) + meta.getAffectedNode(item.first) + .emplace_back(std::move(news)); + } + else + { + assert(false); + } + } + + return {meta, newMod}; +} + void ApplyStateTable::apply( OpenView& to, @@ -124,133 +265,10 @@ ApplyStateTable::apply( std::shared_ptr sMeta; if (!to.open()) { - TxMeta meta(tx.getTransactionID(), to.seq()); - if (deliver) - meta.setDeliveredAmount(*deliver); - if (!hookExecution.empty()) - meta.setHookExecutions(STArray{hookExecution, sfHookExecutions}); - - Mods newMod; - for (auto& item : items_) - { - SField const* type; - switch (item.second.first) - { - default: - case Action::cache: - continue; - case Action::erase: - type = &sfDeletedNode; - break; - case Action::insert: - type = &sfCreatedNode; - break; - case Action::modify: - type = &sfModifiedNode; - break; - } - auto const origNode = to.read(keylet::unchecked(item.first)); - auto curNode = item.second.second; - if ((type == &sfModifiedNode) && (*curNode == *origNode)) - continue; - std::uint16_t nodeType = curNode - ? curNode->getFieldU16(sfLedgerEntryType) - : origNode->getFieldU16(sfLedgerEntryType); - meta.setAffectedNode(item.first, *type, nodeType); - if (type == &sfDeletedNode) - { - assert(origNode && curNode); - threadOwners(to, meta, origNode, newMod, j); - - STObject prevs(sfPreviousFields); - for (auto const& obj : *origNode) - { - // go through the original node for - // modified fields saved on modification - if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && - !curNode->hasMatchingEntry(obj)) - prevs.emplace_back(obj); - } - - if (!prevs.empty()) - meta.getAffectedNode(item.first) - .emplace_back(std::move(prevs)); - - STObject finals(sfFinalFields); - for (auto const& obj : *curNode) - { - // go through the final node for final fields - if (obj.getFName().shouldMeta( - SField::sMD_Always | SField::sMD_DeleteFinal)) - finals.emplace_back(obj); - } - - if (!finals.empty()) - meta.getAffectedNode(item.first) - .emplace_back(std::move(finals)); - } - else if (type == &sfModifiedNode) - { - assert(curNode && origNode); - - if (curNode->isThreadedType()) // thread transaction to node - // item modified - threadItem(meta, curNode); - - STObject prevs(sfPreviousFields); - for (auto const& obj : *origNode) - { - // search the original node for values saved on modify - if (obj.getFName().shouldMeta(SField::sMD_ChangeOrig) && - !curNode->hasMatchingEntry(obj)) - prevs.emplace_back(obj); - } - - if (!prevs.empty()) - meta.getAffectedNode(item.first) - .emplace_back(std::move(prevs)); - - STObject finals(sfFinalFields); - for (auto const& obj : *curNode) - { - // search the final node for values saved always - if (obj.getFName().shouldMeta( - SField::sMD_Always | SField::sMD_ChangeNew)) - finals.emplace_back(obj); - } - - if (!finals.empty()) - meta.getAffectedNode(item.first) - .emplace_back(std::move(finals)); - } - else if (type == &sfCreatedNode) // if created, thread to owner(s) - { - assert(curNode && !origNode); - threadOwners(to, meta, curNode, newMod, j); - - if (curNode->isThreadedType()) // always thread to self - threadItem(meta, curNode); - - STObject news(sfNewFields); - for (auto const& obj : *curNode) - { - // save non-default values - if (!obj.isDefault() && - obj.getFName().shouldMeta( - SField::sMD_Create | SField::sMD_Always)) - news.emplace_back(obj); - } - - if (!news.empty()) - meta.getAffectedNode(item.first) - .emplace_back(std::move(news)); - } - else - { - assert(false); - } - } + // generate meta + auto [meta, newMod] = + generateTxMeta(to, tx, deliver, hookExecution, j); // add any new modified nodes to the modification set for (auto& mod : newMod) diff --git a/src/ripple/ledger/impl/ApplyViewImpl.cpp b/src/ripple/ledger/impl/ApplyViewImpl.cpp index 11e0b7fe0..b76224bb5 100644 --- a/src/ripple/ledger/impl/ApplyViewImpl.cpp +++ b/src/ripple/ledger/impl/ApplyViewImpl.cpp @@ -34,6 +34,16 @@ ApplyViewImpl::apply(OpenView& to, STTx const& tx, TER ter, beast::Journal j) items_.apply(to, tx, ter, deliver_, hookExecution_, j); } +TxMeta +ApplyViewImpl:: +generateProvisionalMeta(OpenView const& to, STTx const& tx, beast::Journal j) +{ + auto [meta, _] = + items_.generateTxMeta(to, tx, deliver_, hookExecution_, j); + + return meta; +} + std::size_t ApplyViewImpl::size() {