hook Hook APIs

This commit is contained in:
tequ
2025-09-23 14:28:28 +09:00
parent cf9eef03e5
commit 0290b73a9e
3 changed files with 243 additions and 165 deletions

View File

@@ -46,7 +46,7 @@ public:
/// etxn APIs /// etxn APIs
Expected<std::shared_ptr<Transaction>, HookReturnCode> Expected<std::shared_ptr<Transaction>, HookReturnCode>
emit(Slice txBlob); emit(Slice txBlob) const;
Expected<uint64_t, HookReturnCode> Expected<uint64_t, HookReturnCode>
etxn_burden() const; etxn_burden() const;
@@ -139,13 +139,29 @@ public:
otxn_param(Bytes param_name) const; otxn_param(Bytes param_name) const;
/// hook APIs /// hook APIs
// hook_account AccountID
// hook_hash hook_account() const;
// hook_again
// hook_param Expected<ripple::uint256, HookReturnCode>
// hook_param_set hook_hash(int32_t hook_no) const;
// hook_skip
// hook_pos Expected<int64_t, HookReturnCode>
hook_again() const;
Expected<Blob, HookReturnCode>
hook_param(Bytes const& paramName) const;
Expected<uint64_t, HookReturnCode>
hook_param_set(
uint256 const& hash,
Bytes const& paramName,
Bytes const& paramValue) const;
Expected<uint64_t, HookReturnCode>
hook_skip(uint256 const& hash, uint32_t flags) const;
uint8_t
hook_pos() const;
/// ledger APIs /// ledger APIs
// fee_base // fee_base

View File

@@ -396,8 +396,178 @@ HookAPI::otxn_param(Bytes param_name) const
return Unexpected(DOESNT_EXIST); return Unexpected(DOESNT_EXIST);
} }
AccountID
HookAPI::hook_account() const
{
return hookCtx.result.account;
}
Expected<ripple::uint256, HookReturnCode>
HookAPI::hook_hash(int32_t hook_no) const
{
if (hook_no == -1)
return hookCtx.result.hookHash;
std::shared_ptr<SLE> 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<int64_t, HookReturnCode>
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<Blob, HookReturnCode>
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<uint64_t, HookReturnCode>
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<Bytes, Bytes>{
{std::move(paramName), std::move(paramValue)}};
}
else
overrides[hash][std::move(paramName)] = std::move(paramValue);
return paramValue.size();
}
Expected<uint64_t, HookReturnCode>
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<SLE> 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<std::shared_ptr<Transaction>, HookReturnCode> Expected<std::shared_ptr<Transaction>, HookReturnCode>
HookAPI::emit(Slice txBlob) HookAPI::emit(Slice txBlob) const
{ {
auto& applyCtx = hookCtx.applyCtx; auto& applyCtx = hookCtx.applyCtx;
auto j = applyCtx.app.journal("View"); auto j = applyCtx.app.journal("View");

View File

@@ -3256,31 +3256,11 @@ DEFINE_HOOK_FUNCTION(
if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length))
return OUT_OF_BOUNDS; return OUT_OF_BOUNDS;
if (hook_no == -1) hook::HookAPI api(hookCtx);
{ auto const result = api.hook_hash(hook_no);
WRITE_WASM_MEMORY_AND_RETURN( if (!result)
write_ptr, return result.error();
write_len, auto const& hash = result.value();
hookCtx.result.hookHash.data(),
32,
memory,
memory_length);
}
std::shared_ptr<SLE> 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);
WRITE_WASM_MEMORY_AND_RETURN( WRITE_WASM_MEMORY_AND_RETURN(
write_ptr, write_len, hash.data(), hash.size(), memory, memory_length); write_ptr, write_len, hash.data(), hash.size(), memory, memory_length);
@@ -3304,13 +3284,11 @@ DEFINE_HOOK_FUNCTION(
if (ptr_len < 20) if (ptr_len < 20)
return TOO_SMALL; return TOO_SMALL;
hook::HookAPI api(hookCtx);
auto const result = api.hook_account();
WRITE_WASM_MEMORY_AND_RETURN( WRITE_WASM_MEMORY_AND_RETURN(
write_ptr, write_ptr, 20, result.data(), 20, memory, memory_length);
20,
hookCtx.result.account.data(),
20,
memory,
memory_length);
HOOK_TEARDOWN(); HOOK_TEARDOWN();
} }
@@ -4970,54 +4948,18 @@ DEFINE_HOOK_FUNCTION(
if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length))
return OUT_OF_BOUNDS; return OUT_OF_BOUNDS;
if (read_len < 1) Bytes paramName{read_ptr + memory, read_ptr + read_len + memory};
return TOO_SMALL;
if (read_len > 32) hook::HookAPI api(hookCtx);
return TOO_BIG; auto const result = api.hook_param(paramName);
std::vector<uint8_t> paramName{ if (!result)
read_ptr + memory, read_ptr + read_len + memory}; return result.error();
// first check for overrides set by prior hooks in the chain auto const& val = result.value();
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
WRITE_WASM_MEMORY_AND_RETURN( WRITE_WASM_MEMORY_AND_RETURN(
write_ptr, write_ptr, write_len, val.data(), val.size(), memory, memory_length);
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;
HOOK_TEARDOWN(); HOOK_TEARDOWN();
} }
@@ -5040,6 +4982,9 @@ DEFINE_HOOK_FUNCTION(
NOT_IN_BOUNDS(hread_ptr, hread_len, memory_length)) NOT_IN_BOUNDS(hread_ptr, hread_len, memory_length))
return OUT_OF_BOUNDS; return OUT_OF_BOUNDS;
{
// those checks are also done in the HookAPI
// but we need to check them here too for backwards compatibility
if (kread_len < 1) if (kread_len < 1)
return TOO_SMALL; return TOO_SMALL;
@@ -5051,29 +4996,17 @@ DEFINE_HOOK_FUNCTION(
if (read_len > hook::maxHookParameterValueSize()) if (read_len > hook::maxHookParameterValueSize())
return TOO_BIG; return TOO_BIG;
}
std::vector<uint8_t> paramName{ Bytes paramName{kread_ptr + memory, kread_ptr + kread_len + memory};
kread_ptr + memory, kread_ptr + kread_len + memory}; Bytes paramValue{read_ptr + memory, read_ptr + read_len + memory};
std::vector<uint8_t> paramValue{
read_ptr + memory, read_ptr + read_len + memory};
ripple::uint256 hash = ripple::uint256::fromVoid(memory + hread_ptr); ripple::uint256 hash = ripple::uint256::fromVoid(memory + hread_ptr);
if (hookCtx.result.overrideCount >= hook_api::max_params) hook::HookAPI api(hookCtx);
return TOO_MANY_PARAMS; auto const result = api.hook_param_set(hash, paramName, paramValue);
if (!result)
hookCtx.result.overrideCount++; return result.error();
return result.value();
auto& overrides = hookCtx.result.hookParamOverrides;
if (overrides.find(hash) == overrides.end())
{
overrides[hash] = std::map<std::vector<uint8_t>, std::vector<uint8_t>>{
{std::move(paramName), std::move(paramValue)}};
}
else
overrides[hash][std::move(paramName)] = std::move(paramValue);
return read_len;
HOOK_TEARDOWN(); HOOK_TEARDOWN();
} }
@@ -5094,75 +5027,34 @@ DEFINE_HOOK_FUNCTION(
if (read_len != 32) if (read_len != 32)
return INVALID_ARGUMENT; 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); ripple::uint256 hash = ripple::uint256::fromVoid(memory + read_ptr);
if (flags == 1) hook::HookAPI api(hookCtx);
{ auto const result = api.hook_skip(hash, flags);
// delete flag if (!result)
if (skips.find(hash) == skips.end()) return result.error();
return DOESNT_EXIST; return result.value();
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<SLE> 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_TEARDOWN(); HOOK_TEARDOWN();
} }
DEFINE_HOOK_FUNCNARG(int64_t, hook_pos) 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) DEFINE_HOOK_FUNCNARG(int64_t, hook_again)
{ {
HOOK_SETUP(); HOOK_SETUP();
if (hookCtx.result.executeAgainAsWeak) hook::HookAPI api(hookCtx);
return ALREADY_SET; auto const result = api.hook_again();
if (hookCtx.result.isStrong) if (!result)
{ return result.error();
hookCtx.result.executeAgainAsWeak = true;
return 1;
}
return PREREQUISITE_NOT_MET; return result.value();
HOOK_TEARDOWN(); HOOK_TEARDOWN();
} }