mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 18:15:50 +00:00
mock txmeta and pass to weakly executed hooks, add meta_slot to access this data
This commit is contained in:
@@ -284,7 +284,7 @@ namespace hook_api
|
||||
"hook_param_set",
|
||||
"hook_skip",
|
||||
"hook_after",
|
||||
"hook_weak"
|
||||
"meta_slot"
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -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<STObject const> 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<STObject const> 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);
|
||||
|
||||
@@ -683,7 +683,8 @@ hook::apply(
|
||||
bool isCallback,
|
||||
bool isStrong,
|
||||
uint32_t wasmParam,
|
||||
int32_t hookChainPosition)
|
||||
int32_t hookChainPosition,
|
||||
std::shared_ptr<STObject const> 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<int, hook::SlotEntry> { 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -901,7 +901,8 @@ executeHookChain(
|
||||
hook::HookStateMap& stateMap,
|
||||
std::vector<hook::HookResult>& results,
|
||||
ripple::AccountID const& account,
|
||||
bool strong)
|
||||
bool strong,
|
||||
std::shared_ptr<STObject const> const& provisionalMeta)
|
||||
{
|
||||
std::set<uint256> 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<STObject const> const& provisionalMeta)
|
||||
{
|
||||
// Finally check if there is a callback
|
||||
if (!ctx_.tx.isFieldPresent(sfEmitDetails))
|
||||
@@ -1117,7 +1119,8 @@ Transactor::doHookCallback()
|
||||
false,
|
||||
safe_cast<TxType>(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<hook::HookResult>& results)
|
||||
std::vector<hook::HookResult>& results,
|
||||
std::shared_ptr<STObject const> 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<uint256> const& hookHashes,
|
||||
hook::HookStateMap& stateMap,
|
||||
std::vector<hook::HookResult>& results,
|
||||
std::shared_ptr<STObject const> 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<STObject const*>(&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<uint8_t>, std::vector<uint8_t>> 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 << "-" <<ctx_.tx.getAccountID(sfAccount) << "]: "
|
||||
<< " aaw Hook ExitCode = "
|
||||
<< aawResult.exitCode;
|
||||
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
JLOG(j_.fatal())
|
||||
<< "HookError[" << hookAccountID << "-" <<ctx_.tx.getAccountID(sfAccount) << "]: "
|
||||
<< "]: aaw failure " << e.what();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
std::pair<TER, bool>
|
||||
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<AccountID, std::set<uint256>> 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<hook::HookResult> orgResults;
|
||||
std::vector<hook::HookResult> tshResults;
|
||||
std::vector<hook::HookResult> 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<hook::HookResult> 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<STObject const>
|
||||
proMeta = std::make_shared<STObject const>(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<hook::HookResult> 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)
|
||||
{
|
||||
|
||||
@@ -191,13 +191,27 @@ public:
|
||||
protected:
|
||||
|
||||
void
|
||||
doHookCallback();
|
||||
doHookCallback(
|
||||
std::shared_ptr<STObject const> const& provisionalMeta);
|
||||
|
||||
TER
|
||||
doTSH(
|
||||
bool strong, // only do strong TSH iff true, otheriwse only weak
|
||||
hook::HookStateMap& stateMap,
|
||||
std::vector<hook::HookResult>& results);
|
||||
std::vector<hook::HookResult>& result,
|
||||
std::shared_ptr<STObject const> 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<uint256> const& hookHashes,
|
||||
hook::HookStateMap& stateMap,
|
||||
std::vector<hook::HookResult>& results,
|
||||
std::shared_ptr<STObject const> const& provisionalMeta);
|
||||
|
||||
TER
|
||||
executeHookChain(
|
||||
@@ -205,8 +219,10 @@ protected:
|
||||
hook::HookStateMap& stateMap,
|
||||
std::vector<hook::HookResult>& results,
|
||||
ripple::AccountID const& account,
|
||||
bool strong);
|
||||
|
||||
bool strong,
|
||||
std::shared_ptr<STObject const> const& provisionalMeta);
|
||||
|
||||
|
||||
void
|
||||
addWeakTSHFromSandbox(detail::ApplyViewBase const& pv);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/protocol/TER.h>
|
||||
#include <ripple/protocol/TxMeta.h>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace ripple {
|
||||
namespace detail {
|
||||
|
||||
@@ -37,6 +39,7 @@ class ApplyStateTable
|
||||
{
|
||||
public:
|
||||
using key_type = ReadView::key_type;
|
||||
using Mods = hash_map<key_type, std::shared_ptr<SLE>>;
|
||||
|
||||
private:
|
||||
enum class Action {
|
||||
@@ -64,6 +67,15 @@ public:
|
||||
void
|
||||
apply(RawView& to) const;
|
||||
|
||||
|
||||
std::pair<TxMeta, Mods>
|
||||
generateTxMeta(
|
||||
OpenView const& to,
|
||||
STTx const& tx,
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::vector<STObject> const& hookExecution,
|
||||
beast::Journal j);
|
||||
|
||||
void
|
||||
apply(
|
||||
OpenView& to,
|
||||
@@ -126,7 +138,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
using Mods = hash_map<key_type, std::shared_ptr<SLE>>;
|
||||
|
||||
static void
|
||||
threadItem(TxMeta& meta, std::shared_ptr<SLE> const& to);
|
||||
|
||||
@@ -109,6 +109,147 @@ ApplyStateTable::visit(
|
||||
}
|
||||
}
|
||||
|
||||
// return a txmeta and a set of created nodes
|
||||
// does not modify ledger
|
||||
std::pair<TxMeta, ApplyStateTable::Mods>
|
||||
ApplyStateTable::generateTxMeta(
|
||||
OpenView const& to,
|
||||
STTx const& tx,
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::vector<STObject> 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<Serializer> 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)
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user