more hook unit test tooling

This commit is contained in:
Richard Holland
2022-10-10 12:48:52 +00:00
parent f5a0c951fe
commit 2a57c89176
4 changed files with 118 additions and 57 deletions

View File

@@ -26,6 +26,7 @@ namespace ripple {
namespace test {
#define DEBUG_TESTS 1
using TestHook = std::vector<uint8_t> const&;
@@ -33,6 +34,10 @@ class SetHook_test : public beast::unit_test::suite
{
public:
// This is a large fee, large enough that we can set most small test hooks without running into fee issues
// we only want to test fee code specifically in fee unit tests, the rest of the time we want to ignore it.
#define HSFEE fee(1'000'000)
#define M(m) memo(m, "", "")
void
testHooksDisabled()
{
@@ -41,16 +46,13 @@ public:
Env env{*this, supported_amendments() - featureHooks};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
// RH TODO: does it matter that passing malformed txn here gives back temMALFORMED (and not disabled)?
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0), ter(temDISABLED));
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0),
M("Hooks Disabled"),
HSFEE, ter(temDISABLED));
}
//Json::Value
//hook(Account const& account, std::optional<std::vector<Json::Value>> hooks, std::uint32_t flags);
//Json::Value
//hso(std::vector<uint8_t> wasmBytes, uint64_t hookOn = 0, uint256 ns = beast::zero, uint8_t apiversion = 0);
void
testMalformedTxStructure()
{
@@ -60,32 +62,42 @@ public:
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env.close();
// Must have a "Hooks" field
env(ripple::test::jtx::hook(alice, {}, 0), ter(temMALFORMED));
env(ripple::test::jtx::hook(alice, {}, 0),
M("Must have a hooks field"),
HSFEE, ter(temMALFORMED));
// Must have at least one non-empty subfield
env(ripple::test::jtx::hook(alice, {{}}, 0), ter(temMALFORMED));
// Must have fewer than 5 entries
env(ripple::test::jtx::hook(alice, {{}}, 0),
M("Must have a non-empty hooks field"),
HSFEE, ter(temMALFORMED));
env(ripple::test::jtx::hook(alice, {{
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm)}}, 0), ter(temMALFORMED));
hso(accept_wasm)}}, 0),
M("Must have fewer than 5 entries"),
HSFEE, ter(temMALFORMED));
// Cannot have both CreateCode and HookHash
{
Json::Value jv =
ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0);
Json::Value iv = jv[jss::Hooks][0U];
iv[jss::Hook][jss::HookHash] = to_string(uint256{beast::zero});
env(jv, ter(temMALFORMED));
jv[jss::Hooks][0U] = iv;
env(jv,
M("Cannot have both CreateCode and HookHash"),
HSFEE, ter(temMALFORMED));
env.close();
}
// If createcode present must be less than 64kib
env(ripple::test::jtx::hook(alice, {{hso(long_wasm)}}, 0), ter(temMALFORMED));
env(ripple::test::jtx::hook(alice, {{hso(long_wasm)}}, 0),
M("If CreateCode is present, then it must be less than 64kib"),
HSFEE, ter(temMALFORMED));
env.close();
}
@@ -98,12 +110,14 @@ public:
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env(ripple::test::jtx::hook(alice, {{hso(noguard_wasm)}}, 0),
M("Must import guard"),
HSFEE, ter(temMALFORMED));
// Must import guard
env(ripple::test::jtx::hook(alice, {{hso(noguard_wasm)}}, 0), ter(temMALFORMED));
// Must only contain hook and cbak
env(ripple::test::jtx::hook(alice, {{hso(illegalfunc_wasm)}}, 0), ter(temMALFORMED));
env(ripple::test::jtx::hook(alice, {{hso(illegalfunc_wasm)}}, 0),
M("Must only contain hook and cbak"),
HSFEE, ter(temMALFORMED));
}
void
@@ -114,9 +128,20 @@ public:
Env env{*this, supported_amendments()};
auto const alice = Account{"alice"};
auto const bob = Account{"bob"};
env.fund(XRP(10000), alice);
env.fund(XRP(10000), bob);
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0),
M("Install Accept Hook"),
HSFEE);
env.close();
env(pay(bob, alice, XRP(1)),
M("Test Accept Hook"),
fee(XRP(1)));
env.close();
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0), ter(tesSUCCESS));
}
void
@@ -126,30 +151,37 @@ public:
using namespace jtx;
Env env{*this, supported_amendments()};
auto const bob = Account{"bob"};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env.fund(XRP(10000), bob);
env(ripple::test::jtx::hook(alice, {{hso(rollback_wasm)}}, 0), ter(tecHOOK_REJECTED));
env(ripple::test::jtx::hook(alice, {{hso(rollback_wasm)}}, 0),
M("Install Rollback Hook"),
HSFEE);
env.close();
env(pay(bob, alice, XRP(1)),
M("Test Rollback Hook"),
fee(XRP(1)), ter(tecHOOK_REJECTED));
env.close();
}
// Trivial single hook
//env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0));
// RH TODO
void
run() override
{
//testTicketSetHook(); // RH TODO
testHooksDisabled();
testAccept();
testRollback();
testMalformedTxStructure();
testMalformedWasm();
testAccept();
testRollback();
}
private:
TestHook
accept_wasm =
accept_wasm = // WASM: 0
wasm[
R"[test.hook](
#include <stdint.h>
@@ -164,7 +196,7 @@ private:
];
TestHook
rollback_wasm =
rollback_wasm = // WASM: 1
wasm[
R"[test.hook](
#include <stdint.h>
@@ -180,42 +212,63 @@ private:
];
TestHook
noguard_wasm =
noguard_wasm = // WASM: 2
wasm[
R"[test.hook](
#include <stdint.h>
extern int32_t _g (uint32_t id, uint32_t maxiter);
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
#define SBUF(x) (uint32_t)(x),sizeof(x)
int64_t hook(uint32_t reserved )
{
_g(1,1);
return accept(SBUF("Hook Accepted"),0);
}
(module
(type (;0;) (func (param i32 i32 i64) (result i64)))
(type (;1;) (func (param i32) (result i64)))
(import "env" "accept" (func (;0;) (type 0)))
(func (;1;) (type 1) (param i32) (result i64)
i32.const 0
i32.const 0
i64.const 0
call 0)
(memory (;0;) 2)
(export "memory" (memory 0))
(export "hook" (func 1)))
)[test.hook]"
];
TestHook
illegalfunc_wasm =
illegalfunc_wasm = // WASM: 3
wasm[
R"[test.hook](
#include <stdint.h>
extern int32_t _g (uint32_t id, uint32_t maxiter);
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
int64_t hook(uint32_t reserved )
{
_g(1,1);
return accept(0,0,0);
}
void otherfunc()
{
_g(1,1);
}
(module
(type (;0;) (func (param i32 i32) (result i32)))
(type (;1;) (func (param i32 i32 i64) (result i64)))
(type (;2;) (func))
(type (;3;) (func (param i32) (result i64)))
(import "env" "_g" (func (;0;) (type 0)))
(import "env" "accept" (func (;1;) (type 1)))
(func (;2;) (type 3) (param i32) (result i64)
i32.const 1
i32.const 1
call 0
drop
i32.const 0
i32.const 0
i64.const 0
call 1)
(func (;3;) (type 2)
i32.const 1
i32.const 1
call 0
drop)
(memory (;0;) 2)
(global (;0;) (mut i32) (i32.const 66560))
(global (;1;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1024))
(global (;3;) i32 (i32.const 66560))
(global (;4;) i32 (i32.const 1024))
(export "memory" (memory 0))
(export "hook" (func 2))
(export "bad_func" (func 3)))
)[test.hook]"
];
TestHook
long_wasm =
long_wasm = // WASM: 4
wasm[
R"[test.hook](
#include <stdint.h>
@@ -227,6 +280,7 @@ private:
#define M_REPEAT_1000(X) M_REPEAT_100(M_REPEAT_10(X))
int64_t hook(uint32_t reserved )
{
_g(1,1);
char ret[] = M_REPEAT_1000("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz01234567890123");
return accept(SBUF(ret), 0);
}

View File

@@ -10,13 +10,14 @@ echo '
namespace ripple {
namespace test {
std::map<std::string, std::vector<uint8_t>> wasm = {' > SetHook_wasm.h
COUNTER="0"
cat SetHook_test.cpp | tr '\n' '\f' |
grep -Po 'R"\[test\.hook\](.*?)\[test\.hook\]"' |
sed -E 's/R"\[test\.hook\]\(//g' |
sed -E 's/\)\[test\.hook\]"[\f \t]*/\/*end*\//g' |
while read -r line
do
echo "/* ==== WASM: $COUNTER ==== */" >> SetHook_wasm.h
echo -n '{ R"[test.hook](' >> SetHook_wasm.h
cat <<< $line | sed -E 's/.{7}$//g' | tr -d '\n' | tr '\f' '\n' >> SetHook_wasm.h
echo ')[test.hook]",' >> SetHook_wasm.h
@@ -35,6 +36,7 @@ cat SetHook_test.cpp | tr '\n' '\f' |
fi
echo '}},' >> SetHook_wasm.h
echo >> SetHook_wasm.h
COUNTER=`echo $COUNTER + 1 | bc`
done
echo '};
}

View File

@@ -1,12 +1,16 @@
#!/bin/bash
ERRORS="0"
COUNTER="0"
cat SetHook_wasm.h | tr -d '\n' | grep -Po '{[0-9A-FUx, ]*}' | tr -d ' ,{}U' | sed -E 's/0x//g' |
while read -r line
do
echo ""
echo "======== WASM: $COUNTER ========="
xxd -r -p <<< $line | wasm-objdump -d -
if [ "$?" -gt "0" ]
then
ERRORS=`echo $ERRORS + 1 | bc`
fi
COUNTER=`echo $COUNTER+1|bc`
done
echo "Errors decompiling: $ERRORS"

View File

@@ -33,6 +33,7 @@ hook(Account const& account, std::optional<std::vector<Json::Value>> hooks, std:
jv[jss::Account] = account.human();
jv[jss::TransactionType] = jss::SetHook;
jv[jss::Flags] = flags;
if (hooks)
{
jv[jss::Hooks] =