From 0290b73a9e21459da4ba5026cecb15b449698f74 Mon Sep 17 00:00:00 2001 From: tequ Date: Tue, 23 Sep 2025 14:28:28 +0900 Subject: [PATCH] hook Hook APIs --- src/ripple/app/hook/HookAPI.h | 32 +++- src/ripple/app/hook/impl/HookAPI.cpp | 172 ++++++++++++++++++++- src/ripple/app/hook/impl/applyHook.cpp | 204 ++++++------------------- 3 files changed, 243 insertions(+), 165 deletions(-) diff --git a/src/ripple/app/hook/HookAPI.h b/src/ripple/app/hook/HookAPI.h index 6d2b6190f..eefe35b8f 100644 --- a/src/ripple/app/hook/HookAPI.h +++ b/src/ripple/app/hook/HookAPI.h @@ -46,7 +46,7 @@ public: /// etxn APIs Expected, HookReturnCode> - emit(Slice txBlob); + emit(Slice txBlob) const; Expected etxn_burden() const; @@ -139,13 +139,29 @@ public: otxn_param(Bytes param_name) const; /// hook APIs - // hook_account - // hook_hash - // hook_again - // hook_param - // hook_param_set - // hook_skip - // hook_pos + AccountID + hook_account() const; + + Expected + hook_hash(int32_t hook_no) const; + + Expected + hook_again() const; + + Expected + hook_param(Bytes const& paramName) const; + + Expected + hook_param_set( + uint256 const& hash, + Bytes const& paramName, + Bytes const& paramValue) const; + + Expected + hook_skip(uint256 const& hash, uint32_t flags) const; + + uint8_t + hook_pos() const; /// ledger APIs // fee_base diff --git a/src/ripple/app/hook/impl/HookAPI.cpp b/src/ripple/app/hook/impl/HookAPI.cpp index 8af5bc20d..5d955152f 100644 --- a/src/ripple/app/hook/impl/HookAPI.cpp +++ b/src/ripple/app/hook/impl/HookAPI.cpp @@ -396,8 +396,178 @@ HookAPI::otxn_param(Bytes param_name) const return Unexpected(DOESNT_EXIST); } +AccountID +HookAPI::hook_account() const +{ + return hookCtx.result.account; +} + +Expected +HookAPI::hook_hash(int32_t hook_no) const +{ + if (hook_no == -1) + return hookCtx.result.hookHash; + + std::shared_ptr hookSLE = + hookCtx.applyCtx.view().peek(hookCtx.result.hookKeylet); + if (!hookSLE || !hookSLE->isFieldPresent(sfHooks)) + return Unexpected(INTERNAL_ERROR); + + ripple::STArray const& hooks = hookSLE->getFieldArray(sfHooks); + if (hook_no >= hooks.size()) + return Unexpected(DOESNT_EXIST); + + auto const& hook = hooks[hook_no]; + if (!hook.isFieldPresent(sfHookHash)) + return Unexpected(DOESNT_EXIST); + + return hook.getFieldH256(sfHookHash); +} + +Expected +HookAPI::hook_again() const +{ + if (hookCtx.result.executeAgainAsWeak) + return ALREADY_SET; + + if (hookCtx.result.isStrong) + { + hookCtx.result.executeAgainAsWeak = true; + return 1; + } + + return PREREQUISITE_NOT_MET; +} + +Expected +HookAPI::hook_param(Bytes const& paramName) const +{ + if (paramName.size() < 1) + return Unexpected(TOO_SMALL); + + if (paramName.size() > 32) + return Unexpected(TOO_BIG); + + // first check for overrides set by prior hooks in the chain + auto const& overrides = hookCtx.result.hookParamOverrides; + if (overrides.find(hookCtx.result.hookHash) != overrides.end()) + { + auto const& params = overrides.at(hookCtx.result.hookHash); + if (params.find(paramName) != params.end()) + { + auto const& param = params.at(paramName); + if (param.size() == 0) + // allow overrides to "delete" parameters + return Unexpected(DOESNT_EXIST); + + return param; + } + } + + // next check if there's a param set on this hook + auto const& params = hookCtx.result.hookParams; + if (params.find(paramName) != params.end()) + { + auto const& param = params.at(paramName); + if (param.size() == 0) + return Unexpected(DOESNT_EXIST); + + return param; + } + + return Unexpected(DOESNT_EXIST); +} + +Expected +HookAPI::hook_param_set( + uint256 const& hash, + Bytes const& paramName, + Bytes const& paramValue) const +{ + if (paramName.size() < 1) + return Unexpected(TOO_SMALL); + + if (paramName.size() > hook::maxHookParameterKeySize()) + return Unexpected(TOO_BIG); + + if (paramValue.size() > hook::maxHookParameterValueSize()) + return Unexpected(TOO_BIG); + + if (hookCtx.result.overrideCount >= hook_api::max_params) + return Unexpected(TOO_MANY_PARAMS); + + hookCtx.result.overrideCount++; + + auto& overrides = hookCtx.result.hookParamOverrides; + if (overrides.find(hash) == overrides.end()) + { + overrides[hash] = std::map{ + {std::move(paramName), std::move(paramValue)}}; + } + else + overrides[hash][std::move(paramName)] = std::move(paramValue); + + return paramValue.size(); +} + +Expected +HookAPI::hook_skip(uint256 const& hash, uint32_t flags) const +{ + if (flags != 0 && flags != 1) + return INVALID_ARGUMENT; + + auto& skips = hookCtx.result.hookSkips; + + if (flags == 1) + { + // delete flag + if (skips.find(hash) == skips.end()) + return Unexpected(DOESNT_EXIST); + skips.erase(hash); + return 1; + } + + // first check if it's already in the skips set + if (skips.find(hash) != skips.end()) + return 1; + + // next check if it's even in this chain + std::shared_ptr hookSLE = + hookCtx.applyCtx.view().peek(hookCtx.result.hookKeylet); + + if (!hookSLE || !hookSLE->isFieldPresent(sfHooks)) + return Unexpected(INTERNAL_ERROR); + + ripple::STArray const& hooks = hookSLE->getFieldArray(sfHooks); + bool found = false; + for (auto const& hookObj : hooks) + { + if (hookObj.isFieldPresent(sfHookHash)) + { + if (hookObj.getFieldH256(sfHookHash) == hash) + { + found = true; + break; + } + } + } + + if (!found) + return Unexpected(DOESNT_EXIST); + + // finally add it to the skips list + hookCtx.result.hookSkips.emplace(hash); + return 1; +} + +uint8_t +HookAPI::hook_pos() const +{ + return hookCtx.result.hookChainPosition; +} + Expected, HookReturnCode> -HookAPI::emit(Slice txBlob) +HookAPI::emit(Slice txBlob) const { auto& applyCtx = hookCtx.applyCtx; auto j = applyCtx.app.journal("View"); diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 188030713..9d0680ff4 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -3256,31 +3256,11 @@ DEFINE_HOOK_FUNCTION( if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) return OUT_OF_BOUNDS; - if (hook_no == -1) - { - WRITE_WASM_MEMORY_AND_RETURN( - write_ptr, - write_len, - hookCtx.result.hookHash.data(), - 32, - memory, - memory_length); - } - - std::shared_ptr hookSLE = - applyCtx.view().peek(hookCtx.result.hookKeylet); - if (!hookSLE || !hookSLE->isFieldPresent(sfHooks)) - return INTERNAL_ERROR; - - ripple::STArray const& hooks = hookSLE->getFieldArray(sfHooks); - if (hook_no >= hooks.size()) - return DOESNT_EXIST; - - auto const& hook = hooks[hook_no]; - if (!hook.isFieldPresent(sfHookHash)) - return DOESNT_EXIST; - - ripple::uint256 const& hash = hook.getFieldH256(sfHookHash); + hook::HookAPI api(hookCtx); + auto const result = api.hook_hash(hook_no); + if (!result) + return result.error(); + auto const& hash = result.value(); WRITE_WASM_MEMORY_AND_RETURN( write_ptr, write_len, hash.data(), hash.size(), memory, memory_length); @@ -3304,13 +3284,11 @@ DEFINE_HOOK_FUNCTION( if (ptr_len < 20) return TOO_SMALL; + hook::HookAPI api(hookCtx); + auto const result = api.hook_account(); + WRITE_WASM_MEMORY_AND_RETURN( - write_ptr, - 20, - hookCtx.result.account.data(), - 20, - memory, - memory_length); + write_ptr, 20, result.data(), 20, memory, memory_length); HOOK_TEARDOWN(); } @@ -4970,54 +4948,18 @@ DEFINE_HOOK_FUNCTION( if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) return OUT_OF_BOUNDS; - if (read_len < 1) - return TOO_SMALL; + Bytes paramName{read_ptr + memory, read_ptr + read_len + memory}; - if (read_len > 32) - return TOO_BIG; + hook::HookAPI api(hookCtx); + auto const result = api.hook_param(paramName); - std::vector paramName{ - read_ptr + memory, read_ptr + read_len + memory}; + if (!result) + return result.error(); - // first check for overrides set by prior hooks in the chain - auto const& overrides = hookCtx.result.hookParamOverrides; - if (overrides.find(hookCtx.result.hookHash) != overrides.end()) - { - auto const& params = overrides.at(hookCtx.result.hookHash); - if (params.find(paramName) != params.end()) - { - auto const& param = params.at(paramName); - if (param.size() == 0) - return DOESNT_EXIST; // allow overrides to "delete" parameters + auto const& val = result.value(); - WRITE_WASM_MEMORY_AND_RETURN( - write_ptr, - write_len, - param.data(), - param.size(), - memory, - memory_length); - } - } - - // next check if there's a param set on this hook - auto const& params = hookCtx.result.hookParams; - if (params.find(paramName) != params.end()) - { - auto const& param = params.at(paramName); - if (param.size() == 0) - return DOESNT_EXIST; - - WRITE_WASM_MEMORY_AND_RETURN( - write_ptr, - write_len, - param.data(), - param.size(), - memory, - memory_length); - } - - return DOESNT_EXIST; + WRITE_WASM_MEMORY_AND_RETURN( + write_ptr, write_len, val.data(), val.size(), memory, memory_length); HOOK_TEARDOWN(); } @@ -5040,40 +4982,31 @@ DEFINE_HOOK_FUNCTION( NOT_IN_BOUNDS(hread_ptr, hread_len, memory_length)) return OUT_OF_BOUNDS; - if (kread_len < 1) - return TOO_SMALL; + { + // those checks are also done in the HookAPI + // but we need to check them here too for backwards compatibility + if (kread_len < 1) + return TOO_SMALL; - if (kread_len > hook::maxHookParameterKeySize()) - return TOO_BIG; + if (kread_len > hook::maxHookParameterKeySize()) + return TOO_BIG; - if (hread_len != 32) - return INVALID_ARGUMENT; + if (hread_len != 32) + return INVALID_ARGUMENT; - if (read_len > hook::maxHookParameterValueSize()) - return TOO_BIG; - - std::vector paramName{ - kread_ptr + memory, kread_ptr + kread_len + memory}; - std::vector paramValue{ - read_ptr + memory, read_ptr + read_len + memory}; + if (read_len > hook::maxHookParameterValueSize()) + return TOO_BIG; + } + Bytes paramName{kread_ptr + memory, kread_ptr + kread_len + memory}; + Bytes paramValue{read_ptr + memory, read_ptr + read_len + memory}; ripple::uint256 hash = ripple::uint256::fromVoid(memory + hread_ptr); - if (hookCtx.result.overrideCount >= hook_api::max_params) - return TOO_MANY_PARAMS; - - hookCtx.result.overrideCount++; - - auto& overrides = hookCtx.result.hookParamOverrides; - if (overrides.find(hash) == overrides.end()) - { - overrides[hash] = std::map, std::vector>{ - {std::move(paramName), std::move(paramValue)}}; - } - else - overrides[hash][std::move(paramName)] = std::move(paramValue); - - return read_len; + hook::HookAPI api(hookCtx); + auto const result = api.hook_param_set(hash, paramName, paramValue); + if (!result) + return result.error(); + return result.value(); HOOK_TEARDOWN(); } @@ -5094,75 +5027,34 @@ DEFINE_HOOK_FUNCTION( if (read_len != 32) return INVALID_ARGUMENT; - if (flags != 0 && flags != 1) - return INVALID_ARGUMENT; - - auto& skips = hookCtx.result.hookSkips; ripple::uint256 hash = ripple::uint256::fromVoid(memory + read_ptr); - if (flags == 1) - { - // delete flag - if (skips.find(hash) == skips.end()) - return DOESNT_EXIST; - skips.erase(hash); - return 1; - } - - // first check if it's already in the skips set - if (skips.find(hash) != skips.end()) - return 1; - - // next check if it's even in this chain - std::shared_ptr hookSLE = - applyCtx.view().peek(hookCtx.result.hookKeylet); - - if (!hookSLE || !hookSLE->isFieldPresent(sfHooks)) - return INTERNAL_ERROR; - - ripple::STArray const& hooks = hookSLE->getFieldArray(sfHooks); - bool found = false; - for (auto const& hookObj : hooks) - { - if (hookObj.isFieldPresent(sfHookHash)) - { - if (hookObj.getFieldH256(sfHookHash) == hash) - { - found = true; - break; - } - } - } - - if (!found) - return DOESNT_EXIST; - - // finally add it to the skips list - hookCtx.result.hookSkips.emplace(hash); - return 1; + hook::HookAPI api(hookCtx); + auto const result = api.hook_skip(hash, flags); + if (!result) + return result.error(); + return result.value(); HOOK_TEARDOWN(); } DEFINE_HOOK_FUNCNARG(int64_t, hook_pos) { - return hookCtx.result.hookChainPosition; + hook::HookAPI api(hookCtx); + return api.hook_pos(); } DEFINE_HOOK_FUNCNARG(int64_t, hook_again) { HOOK_SETUP(); - if (hookCtx.result.executeAgainAsWeak) - return ALREADY_SET; + hook::HookAPI api(hookCtx); + auto const result = api.hook_again(); - if (hookCtx.result.isStrong) - { - hookCtx.result.executeAgainAsWeak = true; - return 1; - } + if (!result) + return result.error(); - return PREREQUISITE_NOT_MET; + return result.value(); HOOK_TEARDOWN(); }