add more hook unit tests, and test wasm checker

This commit is contained in:
Richard Holland
2022-10-07 11:41:33 +00:00
parent eff650c922
commit 105cd06d7e
5 changed files with 172 additions and 47 deletions

View File

@@ -31,6 +31,107 @@ using TestHook = std::vector<uint8_t> const&;
class SetHook_test : public beast::unit_test::suite
{
public:
void
testHooksDisabled()
{
testcase("Check for disabled amendment");
using namespace jtx;
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));
}
//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()
{
testcase("Checks malformed transactions");
using namespace jtx;
Env env{*this, supported_amendments()};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
// Must have a "Hooks" field
env(ripple::test::jtx::hook(alice, {}, 0), 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, {{
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm),
hso(accept_wasm)}}, 0), ter(temMALFORMED));
// If createcode present must be less than 64kib
{
std::vector<uint8_t> longbin(0x10000U, 0xFFU);
env(ripple::test::jtx::hook(alice, {{hso(longbin)}}, 0), ter(temMALFORMED));
}
}
/*
Json::Value jv;
jv[jss::Account] = alice.human();
jv[jss::TransactionType] = jss::SetHook;
jv[jss::Flags] = 0;
jv[jss::Hooks] =
Json::Value{Json::arrayValue};
Json::Value iv;
iv[jss::CreateCode] = std::string(65536, 'F');
iv[jss::HookOn] = "0000000000000000";
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
iv[jss::HookApiVersion] = Json::Value{0};
jv[jss::Hooks][i][jss::Hook] = iv;
env(jv, ter(temMALFORMED));
*/
void
testMalformedWasm()
{
testcase("Checks malformed hook binaries");
using namespace jtx;
Env env{*this, supported_amendments()};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
// 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));
}
// Trivial single hook
//env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0));
// RH TODO
void
run() override
{
//testTicketSetHook(); // RH TODO
testHooksDisabled();
testMalformedTxStructure();
testMalformedWasm();
}
private:
TestHook
accept_wasm =
@@ -41,55 +142,64 @@ private:
extern int64_t accept (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
int64_t hook(uint32_t reserved )
{
accept(0,0,0);
return accept(0,0,0);
_g(1,1);
}
)[test.hook]"
];
public:
TestHook
noguard_wasm =
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 )
{
return accept(0,0,0);
}
)[test.hook]"
];
TestHook
illegalfunc_wasm =
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 )
{
return accept(0,0,0);
}
void otherfunc()
{
_g(1,1);
}
)[test.hook]"
];
TestHook
long_wasm =
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)
#define M_REPEAT_10(X) X X X X X X X X X X
#define M_REPEAT_100(X) M_REPEAT_10(M_REPEAT_10(X))
#define M_REPEAT_1000(X) M_REPEAT_100(M_REPEAT_10(X))
int64_t hook(uint32_t reserved )
{
char ret[] = M_REPEAT_1000("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz01234567890123");
return accept(SBUF(ret), 0);
}
)[test.hook]"
];
void
testHooksDisabled()
{
testcase("SetHook checks for disabled amendment");
using namespace jtx;
Env env{*this, supported_amendments() - featureHooks};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
env(ripple::test::jtx::hook(alice, {}, 0), ter(temDISABLED));
}
void
testMalformedTransaction()
{
testcase("SetHook checks for malformed transactions");
using namespace jtx;
Env env{*this, supported_amendments()};
auto const alice = Account{"alice"};
env.fund(XRP(10000), alice);
// Must have a "Hooks" field
env(ripple::test::jtx::hook(alice, {}, 0), ter(temMALFORMED));
// Must have at least one non-empty subfield
env(ripple::test::jtx::hook(alice, {{}}, 0), ter(temMALFORMED));
// Trivial single hook
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0));
// RH TODO
}
void
run() override
{
//testTicketSetHook(); // RH TODO
testHooksDisabled();
testMalformedTransaction();
}
};
BEAST_DEFINE_TESTSUITE(SetHook, tx, ripple);
} // namespace test

View File

@@ -14,12 +14,12 @@ std::map<std::string, std::vector<uint8_t>> wasm = {' > SetHook_wasm.h
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]*//g' |
sed -E 's/\)\[test\.hook\]"[\f \t]*/\/*end*\//g' |
while read -r line
do
echo -n '{ R"[test.hook](' >> SetHook_wasm.h
# tr '\f' '\n' <<< $line >> SetHook_wasm.h
cat <<< $line | tr -d '\n' | tr '\f' '\n' >> 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
echo "{" >> SetHook_wasm.h
wasmcc -x c /dev/stdin -o /dev/stdout -O2 -Wl,--allow-undefined <<< `tr '\f' '\n' <<< $line` |

View File

@@ -0,0 +1,12 @@
#!/bin/bash
ERRORS="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
xxd -r -p <<< $line | wasm-objdump -d -
if [ "$?" -gt "0" ]
then
ERRORS=`echo $ERRORS + 1 | bc`
fi
done
echo "Errors decompiling: $ERRORS"

View File

@@ -32,7 +32,7 @@ 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);
hso(std::vector<uint8_t> const& wasmBytes, uint64_t hookOn = 0, uint256 ns = beast::zero, uint8_t apiversion = 0);
} // namespace jtx
} // namespace test

View File

@@ -65,8 +65,11 @@ inline std::string uint64_hex(uint64_t x)
}
Json::Value
hso(std::vector<uint8_t> wasmBytes, uint64_t hookOn, uint256 ns, uint8_t apiversion)
hso(std::vector<uint8_t> const& wasmBytes, uint64_t hookOn, uint256 ns, uint8_t apiversion)
{
if (wasmBytes.size() == 0)
throw std::runtime_error("empty hook wasm passed to hso()");
Json::Value jv;
jv[jss::CreateCode] = strHex(wasmBytes);
jv[jss::HookOn] = uint64_hex(hookOn);