mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
increase size of hook state to 256 bytes, parameters too, change the reserve requirement to 1:1 for hook state, allow weak execution on rollback if hook_again was specified, add further state_set tests
This commit is contained in:
@@ -136,12 +136,6 @@
|
||||
WasmEdge_String hook_api::WasmFunctionName##F = WasmEdge_StringCreateByCString(#F);\
|
||||
R hook_api::F(hook::HookContext& hookCtx, WasmEdge_MemoryInstanceContext& memoryCtx)
|
||||
|
||||
|
||||
|
||||
|
||||
#define COMPUTE_HOOK_DATA_OWNER_COUNT(state_count)\
|
||||
(std::ceil( (double)state_count/(double)5.0 ))
|
||||
|
||||
#define HOOK_SETUP()\
|
||||
[[maybe_unused]] ApplyContext& applyCtx = hookCtx.applyCtx;\
|
||||
[[maybe_unused]] auto& view = applyCtx.view();\
|
||||
|
||||
@@ -262,6 +262,9 @@ namespace hook
|
||||
|
||||
uint32_t maxHookChainLength(void);
|
||||
|
||||
uint32_t computeHookStateOwnerCount(uint32_t hookStateCount);
|
||||
|
||||
|
||||
int64_t computeExecutionFee(uint64_t instructionCount);
|
||||
int64_t computeCreationFee(uint64_t byteCount);
|
||||
|
||||
|
||||
@@ -556,6 +556,13 @@ get_free_slot(hook::HookContext& hookCtx)
|
||||
return slot_into;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hook::
|
||||
computeHookStateOwnerCount(uint32_t hookStateCount)
|
||||
{
|
||||
return hookStateCount;
|
||||
}
|
||||
|
||||
inline int64_t
|
||||
serialize_keylet(
|
||||
ripple::Keylet& kl,
|
||||
@@ -590,7 +597,7 @@ unserialize_keylet(uint8_t* ptr, uint32_t len)
|
||||
// RH TODO: this is used by sethook to determine the value stored in ltHOOK
|
||||
// replace this with votable value
|
||||
uint32_t hook::maxHookStateDataSize(void) {
|
||||
return 128;
|
||||
return 256U;
|
||||
}
|
||||
|
||||
uint32_t hook::maxHookWasmSize(void)
|
||||
@@ -604,7 +611,7 @@ uint32_t hook::maxHookParameterKeySize(void)
|
||||
}
|
||||
uint32_t hook::maxHookParameterValueSize(void)
|
||||
{
|
||||
return 128;
|
||||
return 256;
|
||||
}
|
||||
|
||||
bool hook::isEmittedTxn(ripple::STTx const& tx)
|
||||
@@ -775,7 +782,7 @@ hook::setHookState(
|
||||
auto hookStateDirKeylet = ripple::keylet::hookStateDir(acc, ns);
|
||||
|
||||
uint32_t stateCount = sleAccount->getFieldU32(sfHookStateCount);
|
||||
uint32_t oldStateReserve = COMPUTE_HOOK_DATA_OWNER_COUNT(stateCount);
|
||||
uint32_t oldStateReserve = computeHookStateOwnerCount(stateCount);
|
||||
|
||||
auto hookState = view.peek(hookStateKeylet);
|
||||
|
||||
@@ -806,7 +813,7 @@ hook::setHookState(
|
||||
--stateCount; // guard this because in the "impossible" event it is already 0 we'll wrap back to int_max
|
||||
|
||||
// if removing this state entry would destroy the allotment then reduce the owner count
|
||||
if (COMPUTE_HOOK_DATA_OWNER_COUNT(stateCount) < oldStateReserve)
|
||||
if (computeHookStateOwnerCount(stateCount) < oldStateReserve)
|
||||
adjustOwnerCount(view, sleAccount, -1, j);
|
||||
|
||||
sleAccount->setFieldU32(sfHookStateCount, stateCount);
|
||||
@@ -838,7 +845,7 @@ hook::setHookState(
|
||||
|
||||
++stateCount;
|
||||
|
||||
if (COMPUTE_HOOK_DATA_OWNER_COUNT(stateCount) > oldStateReserve)
|
||||
if (computeHookStateOwnerCount(stateCount) > oldStateReserve)
|
||||
{
|
||||
// the hook used its allocated allotment of state entries for its previous ownercount
|
||||
// increment ownercount and give it another allotment
|
||||
|
||||
@@ -1538,7 +1538,7 @@ Transactor::operator()()
|
||||
for (auto& hookResult: hookResults)
|
||||
{
|
||||
hook::finalizeHookResult(hookResult, ctx_, isTesSuccess(result));
|
||||
if (isTesSuccess(result) && hookResult.executeAgainAsWeak)
|
||||
if (hookResult.executeAgainAsWeak)
|
||||
{
|
||||
if (aawMap.find(hookResult.account) == aawMap.end())
|
||||
aawMap[hookResult.account] = {hookResult.hookHash};
|
||||
|
||||
@@ -300,11 +300,11 @@ public:
|
||||
params[0U][jss::HookParameter][jss::HookParameterName] =
|
||||
strHex(std::string(32, 'a'));
|
||||
params[0U][jss::HookParameter][jss::HookParameterValue] =
|
||||
strHex(std::string(129, 'a'));
|
||||
strHex(std::string(257, 'a'));
|
||||
iv[jss::HookParameters] = params;
|
||||
jv[jss::Hooks][0U][jss::Hook] = iv;
|
||||
env(jv,
|
||||
M("HSO must must not contain parameter values longer than 128 "
|
||||
M("HSO must must not contain parameter values longer than 256 "
|
||||
"bytes"),
|
||||
HSFEE,
|
||||
ter(temMALFORMED));
|
||||
@@ -5227,8 +5227,18 @@ public:
|
||||
|
||||
auto const bob = Account{"bob"};
|
||||
auto const alice = Account{"alice"};
|
||||
auto const cho = Account{"cho"};
|
||||
env.fund(XRP(10000), alice);
|
||||
env.fund(XRP(10000), bob);
|
||||
env.fund(XRP(10000), cho);
|
||||
|
||||
|
||||
|
||||
// install a rollback hook on cho
|
||||
env(ripple::test::jtx::hook(cho, {{hso(rollback_wasm, overrideFlag)}}, 0),
|
||||
M("set state_set rollback"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
auto const aliceid = Account("alice").id();
|
||||
|
||||
@@ -5241,6 +5251,8 @@ public:
|
||||
|
||||
auto const state1 = env.le(ripple::keylet::hookState(aliceid, beast::zero, beast::zero));
|
||||
BEAST_REQUIRE(!state1);
|
||||
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
|
||||
}
|
||||
|
||||
// first hook will set two state objects with different keys and data on alice
|
||||
@@ -5300,7 +5312,7 @@ public:
|
||||
ASSERT(state_set(0,1000000, 0, 32) == OUT_OF_BOUNDS);
|
||||
ASSERT(state_set(1000000, 0, 0, 32) == OUT_OF_BOUNDS);
|
||||
|
||||
ASSERT(state_set(0, 129, 0, 32) == TOO_BIG);
|
||||
ASSERT(state_set(0, 257, 0, 32) == TOO_BIG);
|
||||
}
|
||||
|
||||
|
||||
@@ -5345,8 +5357,21 @@ public:
|
||||
M("set state_set 1"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
|
||||
|
||||
// invoke the hook with cho (rollback after alice's hooks have executed)
|
||||
env(pay(alice, cho, XRP(1)), M("test state_set 1 rollback"), fee(XRP(1)), ter(tecHOOK_REJECTED));
|
||||
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 1);
|
||||
|
||||
// invoke the hook
|
||||
auto const nsdir = env.le(nsdirkl);
|
||||
BEAST_EXPECT(!nsdir);
|
||||
|
||||
auto const state1 = env.le(ripple::keylet::hookState(aliceid, beast::zero, beast::zero));
|
||||
BEAST_EXPECT(!state1);
|
||||
|
||||
// invoke the hook from bob to alice, this will work
|
||||
env(pay(bob, alice, XRP(1)), M("test state_set 1"), fee(XRP(1)));
|
||||
env.close();
|
||||
}
|
||||
@@ -5354,6 +5379,9 @@ public:
|
||||
// check that the state object and namespace exists
|
||||
{
|
||||
|
||||
// owner count should be 1 hook + 2 state objects == 3
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
||||
|
||||
auto const nsdir = env.le(nsdirkl);
|
||||
BEAST_REQUIRE(!!nsdir);
|
||||
|
||||
@@ -5517,6 +5545,8 @@ public:
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
// two hooks + two state objects = 4
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 4);
|
||||
|
||||
// this hook will be installed on bob, and it will verify the newly updated state
|
||||
// is also available on his side. caution must be taken because bob's hooks will execute
|
||||
@@ -5587,6 +5617,11 @@ public:
|
||||
M("set state_set 3"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
// invoke the hook with cho (rollback after alice's hooks have executed)
|
||||
env(pay(alice, cho, XRP(1)), M("test state_set 3 rollback"), fee(XRP(1)), ter(tecHOOK_REJECTED));
|
||||
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 4);
|
||||
|
||||
// invoke the hook
|
||||
env(pay(alice, bob, XRP(1)), M("test state_set 3"), fee(XRP(1)));
|
||||
@@ -5597,6 +5632,10 @@ public:
|
||||
// check that the updates have been made
|
||||
{
|
||||
|
||||
// two hooks + one state == 3
|
||||
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
||||
BEAST_EXPECT((*env.le("bob"))[sfOwnerCount] == 1);
|
||||
|
||||
auto const nsdir = env.le(nsdirkl);
|
||||
BEAST_REQUIRE(!!nsdir);
|
||||
|
||||
@@ -5633,16 +5672,92 @@ public:
|
||||
}
|
||||
|
||||
|
||||
// create a hook state inside the weak side of an execution, while the strong side is rolled back
|
||||
{
|
||||
TestHook hook = wasm[R"[test.hook](
|
||||
#include <stdint.h>
|
||||
extern int32_t _g (uint32_t id, uint32_t maxiter);
|
||||
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
||||
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
||||
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
||||
extern int64_t state_set (
|
||||
uint32_t read_ptr,
|
||||
uint32_t read_len,
|
||||
uint32_t kread_ptr,
|
||||
uint32_t kread_len
|
||||
);
|
||||
extern int64_t hook_again(void);
|
||||
|
||||
#define ASSERT(x)\
|
||||
if (!(x))\
|
||||
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
||||
|
||||
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
||||
int64_t hook(uint32_t reserved )
|
||||
{
|
||||
_g(1,1);
|
||||
|
||||
hook_again();
|
||||
|
||||
// create state
|
||||
{
|
||||
|
||||
uint8_t data[16] =
|
||||
{
|
||||
0xFFU,1,1,1,1,1,1,1,
|
||||
1,1,1,1,1,1,1,2
|
||||
};
|
||||
|
||||
|
||||
uint8_t ff = 0xFFU;
|
||||
|
||||
ASSERT(state_set(SBUF(data), &ff, 1) == sizeof(data));
|
||||
}
|
||||
|
||||
accept(0,0,0);
|
||||
|
||||
}
|
||||
)[test.hook]"];
|
||||
|
||||
|
||||
// install the hook on alice, deleting the other hook
|
||||
env(ripple::test::jtx::hook(alice, {{
|
||||
{hso(hook, overrideFlag)},
|
||||
{},
|
||||
{},
|
||||
{hso_delete()}}}, 0),
|
||||
M("set state_set 4"),
|
||||
HSFEE);
|
||||
env.close();
|
||||
|
||||
// invoke from alice to cho, this will cause a rollback, however the hook state should still be updated
|
||||
// because the hook specified hook_again, and in the second weak execution the hook is allowed to
|
||||
// set state
|
||||
env(pay(alice, cho, XRP(1)), M("test state_set 4 rollback"), fee(XRP(1)), ter(tecHOOK_REJECTED));
|
||||
|
||||
|
||||
uint8_t key[32] =
|
||||
{
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0xFFU
|
||||
};
|
||||
|
||||
auto const state = env.le(ripple::keylet::hookState(aliceid, uint256::fromVoid(key), beast::zero));
|
||||
|
||||
BEAST_EXPECT(state);
|
||||
|
||||
// one hook + two state objects == 3
|
||||
// BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
||||
|
||||
}
|
||||
// todo:
|
||||
// check state not set after unsuccessful chain execution
|
||||
// check state can be set on emit callback
|
||||
// check state can be set on weak execution
|
||||
// check state can be set on weak execution after strong execution
|
||||
// check namespacing provides for non-collision of same key
|
||||
// check state persistance between hook install and uninstall
|
||||
// check reserve - cant make new state object if reserve insufficient
|
||||
// try creating many new state objects
|
||||
// check namespacing provides for non-collision of same key
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@ hook(Account const& account, std::optional<std::vector<Json::Value>> hooks, std:
|
||||
Json::Value
|
||||
hso(std::vector<uint8_t> const& wasmBytes, void (*f)(Json::Value& jv) = 0);
|
||||
|
||||
Json::Value
|
||||
hso_delete(void (*f)(Json::Value& jv) = 0);
|
||||
|
||||
std::string uint64_hex(uint64_t x);
|
||||
|
||||
} // namespace jtx
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <stdexcept>
|
||||
#include <test/jtx/hook.h>
|
||||
#include <ripple/app/hook/Enum.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
@@ -65,9 +66,23 @@ std::string uint64_hex(uint64_t x)
|
||||
nibble(x >> 4U) + nibble(x >> 0U);
|
||||
}
|
||||
|
||||
Json::Value
|
||||
hso_delete(void (*f)(Json::Value& jv))
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::CreateCode] = "";
|
||||
jv[jss::Flags] = hsfOVERRIDE;
|
||||
|
||||
if (f)
|
||||
f(jv);
|
||||
|
||||
return jv;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
hso(std::vector<uint8_t> const& wasmBytes, void (*f)(Json::Value& jv))
|
||||
{
|
||||
|
||||
if (wasmBytes.size() == 0)
|
||||
throw std::runtime_error("empty hook wasm passed to hso()");
|
||||
|
||||
@@ -78,12 +93,12 @@ hso(std::vector<uint8_t> const& wasmBytes, void (*f)(Json::Value& jv))
|
||||
jv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
||||
jv[jss::HookApiVersion] = Json::Value{0};
|
||||
}
|
||||
|
||||
|
||||
if (f)
|
||||
if (f)
|
||||
f(jv);
|
||||
|
||||
return jv;
|
||||
|
||||
}
|
||||
|
||||
} // namespace jtx
|
||||
|
||||
Reference in New Issue
Block a user