mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-19 10:05:48 +00:00
11749 lines
484 KiB
C++
11749 lines
484 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012-2016 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
#include <ripple/app/hook/Enum.h>
|
|
#include <ripple/app/ledger/LedgerMaster.h>
|
|
#include <ripple/app/tx/impl/SetHook.h>
|
|
#include <ripple/protocol/TxFlags.h>
|
|
#include <ripple/protocol/jss.h>
|
|
#include <test/app/SetHook_wasm.h>
|
|
#include <test/jtx.h>
|
|
#include <test/jtx/hook.h>
|
|
#include <unordered_map>
|
|
|
|
// RH TODO: test collect calls / weak tsh
|
|
|
|
namespace ripple {
|
|
|
|
namespace test {
|
|
|
|
#define DEBUG_TESTS 1
|
|
|
|
using TestHook = std::vector<uint8_t> const&;
|
|
|
|
class JSSHasher
|
|
{
|
|
public:
|
|
size_t
|
|
operator()(const Json::StaticString& n) const
|
|
{
|
|
return std::hash<std::string_view>{}(n.c_str());
|
|
}
|
|
};
|
|
|
|
class JSSEq
|
|
{
|
|
public:
|
|
bool
|
|
operator()(const Json::StaticString& a, const Json::StaticString& b) const
|
|
{
|
|
return a == b;
|
|
}
|
|
};
|
|
|
|
using JSSMap =
|
|
std::unordered_map<Json::StaticString, Json::Value, JSSHasher, JSSEq>;
|
|
|
|
// Identical to BEAST_EXPECT except it returns from the function
|
|
// if the condition isn't met (and would otherwise therefore cause a crash)
|
|
#define BEAST_REQUIRE(x) \
|
|
{ \
|
|
BEAST_EXPECT(!!(x)); \
|
|
if (!(x)) \
|
|
return; \
|
|
}
|
|
|
|
#define HASH_WASM(x) \
|
|
uint256 const x##_hash = \
|
|
ripple::sha512Half_s(ripple::Slice(x##_wasm.data(), x##_wasm.size())); \
|
|
std::string const x##_hash_str = to_string(x##_hash); \
|
|
Keylet const x##_keylet = keylet::hookDefinition(x##_hash);
|
|
|
|
class SetHook_test : public beast::unit_test::suite
|
|
{
|
|
private:
|
|
// helper
|
|
void static overrideFlag(Json::Value& jv)
|
|
{
|
|
jv[jss::Flags] = hsfOVERRIDE;
|
|
}
|
|
|
|
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(100'000'000)
|
|
#define M(m) memo(m, "", "")
|
|
void
|
|
testHooksOwnerDir()
|
|
{
|
|
testcase("Test owner directory");
|
|
|
|
using namespace jtx;
|
|
|
|
for (bool const withXahauV1 : {true, false})
|
|
{
|
|
auto const amend = withXahauV1
|
|
? supported_amendments()
|
|
: supported_amendments() - fixXahauV1;
|
|
|
|
Env env{*this, amend};
|
|
|
|
auto const alice = Account{"alice"};
|
|
auto const gw = Account{"gateway"};
|
|
auto const USD = gw["USD"];
|
|
env.fund(XRP(10000), alice, gw);
|
|
env.close();
|
|
env.trust(USD(100000), alice);
|
|
env.close();
|
|
env(pay(gw, alice, USD(10000)));
|
|
|
|
for (int i = 1; i < 34; ++i)
|
|
{
|
|
std::string const uri(i, '?');
|
|
env(uritoken::mint(alice, uri));
|
|
}
|
|
env.close();
|
|
|
|
env(ripple::test::jtx::hook(
|
|
alice, {{hso(accept_wasm, overrideFlag)}}, 0),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
env(ripple::test::jtx::hook(
|
|
alice, {{hso(accept_wasm, overrideFlag)}}, 0),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// delete hook
|
|
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::Flags] = 1;
|
|
iv[jss::CreateCode] = "";
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
auto const txResult =
|
|
withXahauV1 ? ter(tesSUCCESS) : ter(tefBAD_LEDGER);
|
|
env(jv, HSFEE, txResult);
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
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, overrideFlag)}}, 0),
|
|
M("Hooks Disabled"),
|
|
HSFEE,
|
|
ter(temDISABLED));
|
|
}
|
|
|
|
void
|
|
testTxStructure()
|
|
{
|
|
testcase("Checks malformed transactions");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
env.close();
|
|
|
|
// Test outer structure
|
|
|
|
env(ripple::test::jtx::hook(alice, {}, 0),
|
|
M("Must have a hooks field"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
|
|
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),
|
|
hso(accept_wasm),
|
|
hso(accept_wasm),
|
|
hso(accept_wasm),
|
|
hso(accept_wasm),
|
|
hso(accept_wasm),
|
|
hso(accept_wasm)}},
|
|
0),
|
|
M("Must have fewer than 11 entries"),
|
|
HSFEE,
|
|
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::MemoData] = "DEADBEEF";
|
|
iv[jss::MemoFormat] = "";
|
|
iv[jss::MemoType] = "";
|
|
jv[jss::Hooks][0U][jss::Memo] = iv;
|
|
env(jv,
|
|
M("Hooks Array must contain Hook objects"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
testGrants()
|
|
{
|
|
testcase("Checks malformed grants on install operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// check too many grants
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value grants{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 9; ++i)
|
|
{
|
|
Json::Value pv;
|
|
Json::Value piv;
|
|
piv[jss::HookHash] = to_string(uint256{i});
|
|
pv[jss::HookGrant] = piv;
|
|
grants[i] = pv;
|
|
}
|
|
iv[jss::HookGrants] = grants;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO must not include more than 8 grants"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// check wrong inner type
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U] = Json::Value{};
|
|
grants[0U][jss::Memo] = Json::Value{};
|
|
grants[0U][jss::Memo][jss::MemoFormat] =
|
|
strHex(std::string(12, 'a'));
|
|
grants[0U][jss::Memo][jss::MemoData] = strHex(std::string(12, 'a'));
|
|
iv[jss::HookGrants] = grants;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO grant array can only contain HookGrant objects"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
testParams()
|
|
{
|
|
testcase("Checks malformed params on install operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// check too many parameters
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 17; ++i)
|
|
{
|
|
Json::Value pv;
|
|
Json::Value piv;
|
|
piv[jss::HookParameterName] =
|
|
strHex("param" + std::to_string(i));
|
|
piv[jss::HookParameterValue] =
|
|
strHex("value" + std::to_string(i));
|
|
pv[jss::HookParameter] = piv;
|
|
params[i] = pv;
|
|
}
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO must not include more than 16 parameters"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// check repeat parameters
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 2; ++i)
|
|
{
|
|
params[i] = Json::Value{};
|
|
params[i][jss::HookParameter] = Json::Value{};
|
|
params[i][jss::HookParameter][jss::HookParameterName] =
|
|
strHex(std::string{"param"});
|
|
}
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO must not repeat parameter names"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// check too long parameter name
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U] = Json::Value{};
|
|
params[0U][jss::HookParameter] = Json::Value{};
|
|
params[0U][jss::HookParameter][jss::HookParameterName] =
|
|
strHex(std::string(33, 'a'));
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO must must not contain parameter names longer than 32 "
|
|
"bytes"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// check too long parameter value
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U] = Json::Value{};
|
|
params[0U][jss::HookParameter] = Json::Value{};
|
|
params[0U][jss::HookParameter][jss::HookParameterName] =
|
|
strHex(std::string(32, 'a'));
|
|
params[0U][jss::HookParameter][jss::HookParameterValue] =
|
|
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 256 "
|
|
"bytes"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// wrong object type
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U] = Json::Value{};
|
|
params[0U][jss::Memo] = Json::Value{};
|
|
params[0U][jss::Memo][jss::MemoFormat] =
|
|
strHex(std::string(12, 'a'));
|
|
params[0U][jss::Memo][jss::MemoData] = strHex(std::string(12, 'a'));
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("HSO parameter array can only contain HookParameter objects"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
testInstall()
|
|
{
|
|
testcase("Checks malformed install operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), bob);
|
|
|
|
// create a hook that we can then install
|
|
{
|
|
env(ripple::test::jtx::hook(
|
|
bob, {{hso(accept_wasm), hso(rollback_wasm)}}, 0),
|
|
M("First set = tesSUCCESS"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
}
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// can't set api version
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = accept_hash_str;
|
|
iv[jss::HookApiVersion] = 0U;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook Install operation cannot set apiversion"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// can't set non-existent hook
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] =
|
|
"DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBE"
|
|
"EF";
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook Install operation cannot set non existent hook hash"),
|
|
HSFEE,
|
|
ter(terNO_HOOK));
|
|
env.close();
|
|
}
|
|
|
|
// can set extant hook
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = accept_hash_str;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook Install operation can set extant hook hash"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
// can't set extant hook over other hook without override flag
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = rollback_hash_str;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook Install operation can set extant hook hash"),
|
|
HSFEE,
|
|
ter(tecREQUIRES_FLAG));
|
|
env.close();
|
|
}
|
|
|
|
// can set extant hook over other hook with override flag
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookHash] = rollback_hash_str;
|
|
iv[jss::Flags] = hsfOVERRIDE;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook Install operation can set extant hook hash"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
testDelete()
|
|
{
|
|
testcase("Checks malformed delete operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// flag required
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook DELETE operation must include hsfOVERRIDE flag"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// invalid flags
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
iv[jss::Flags] = "2147483648";
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook DELETE operation must include hsfOVERRIDE flag"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// grants, parameters, hookon, hookapiversion, hooknamespace keys must
|
|
// be absent
|
|
for (auto const& [key, value] : JSSMap{
|
|
{jss::HookGrants, Json::arrayValue},
|
|
{jss::HookParameters, Json::arrayValue},
|
|
{jss::HookOn,
|
|
"000000000000000000000000000000000000000000000000000000000000"
|
|
"0000"},
|
|
{jss::HookApiVersion, "0"},
|
|
{jss::HookNamespace, to_string(uint256{beast::zero})}})
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
iv[key] = value;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook DELETE operation cannot include: grants, params, "
|
|
"hookon, apiversion, namespace"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// create and delete single hook
|
|
{
|
|
{
|
|
Json::Value jv =
|
|
ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0);
|
|
env(jv, M("Normal accept create"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
BEAST_REQUIRE(env.le(accept_keylet));
|
|
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
iv[jss::Flags] = hsfOVERRIDE;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv, M("Normal hook DELETE"), HSFEE);
|
|
env.close();
|
|
|
|
// check to ensure definition is deleted and hooks object too
|
|
auto const def = env.le(accept_keylet);
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
|
|
BEAST_EXPECT(!def);
|
|
BEAST_EXPECT(!hook);
|
|
}
|
|
|
|
// create four hooks then delete the second last one
|
|
{
|
|
// create
|
|
{
|
|
Json::Value jv = ripple::test::jtx::hook(
|
|
alice,
|
|
{{hso(accept_wasm),
|
|
hso(makestate_wasm),
|
|
hso(rollback_wasm),
|
|
hso(accept2_wasm)}},
|
|
0);
|
|
env(jv, M("Create four"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
// delete third and check
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
iv[jss::Flags] = hsfOVERRIDE;
|
|
for (uint8_t i = 0; i < 4; ++i)
|
|
jv[jss::Hooks][i][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][2U][jss::Hook] = iv;
|
|
|
|
env(jv, M("Normal hooki DELETE (third pos)"), HSFEE);
|
|
env.close();
|
|
|
|
// check the hook definitions are consistent with reference
|
|
// count dropping to zero on the third
|
|
auto const accept_def = env.le(accept_keylet);
|
|
auto const rollback_def = env.le(rollback_keylet);
|
|
auto const makestate_def = env.le(makestate_keylet);
|
|
auto const accept2_def = env.le(accept2_keylet);
|
|
|
|
BEAST_REQUIRE(accept_def);
|
|
BEAST_EXPECT(!rollback_def);
|
|
BEAST_REQUIRE(makestate_def);
|
|
BEAST_REQUIRE(accept2_def);
|
|
|
|
// check the hooks array is correct
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_REQUIRE(hooks.size() == 4);
|
|
|
|
// make sure only the third is deleted
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_REQUIRE(hooks[1].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(!hooks[2].isFieldPresent(sfHookHash));
|
|
BEAST_REQUIRE(hooks[3].isFieldPresent(sfHookHash));
|
|
|
|
// check hashes on the three remaining
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
BEAST_EXPECT(
|
|
hooks[1].getFieldH256(sfHookHash) == makestate_hash);
|
|
BEAST_EXPECT(hooks[3].getFieldH256(sfHookHash) == accept2_hash);
|
|
}
|
|
|
|
// delete rest and check
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = "";
|
|
iv[jss::Flags] = hsfOVERRIDE;
|
|
for (uint8_t i = 0; i < 4; ++i)
|
|
{
|
|
if (i != 2U)
|
|
jv[jss::Hooks][i][jss::Hook] = iv;
|
|
else
|
|
jv[jss::Hooks][i][jss::Hook] = Json::Value{};
|
|
}
|
|
|
|
env(jv,
|
|
M("Normal hook DELETE (first, second, fourth pos)"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// check the hook definitions are consistent with reference
|
|
// count dropping to zero on the third
|
|
auto const accept_def = env.le(accept_keylet);
|
|
auto const rollback_def = env.le(rollback_keylet);
|
|
auto const makestate_def = env.le(makestate_keylet);
|
|
auto const accept2_def = env.le(accept2_keylet);
|
|
|
|
BEAST_EXPECT(!accept_def);
|
|
BEAST_EXPECT(!rollback_def);
|
|
BEAST_EXPECT(!makestate_def);
|
|
BEAST_EXPECT(!accept2_def);
|
|
|
|
// check the hooks object is gone
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_EXPECT(!hook);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
testNSDelete()
|
|
{
|
|
testcase("Checks malformed nsdelete operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), bob);
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
for (auto const& [key, value] : JSSMap{
|
|
{jss::HookGrants, Json::arrayValue},
|
|
{jss::HookParameters, Json::arrayValue},
|
|
{jss::HookOn,
|
|
"000000000000000000000000000000000000000000000000000000000000"
|
|
"0000"},
|
|
{jss::HookApiVersion, "0"},
|
|
})
|
|
{
|
|
Json::Value iv;
|
|
iv[key] = value;
|
|
iv[jss::Flags] = hsfNSDELETE;
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Hook NSDELETE operation cannot include: grants, params, "
|
|
"hookon, apiversion"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
auto const key = uint256::fromVoid(
|
|
(std::array<uint8_t, 32>{
|
|
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
|
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
|
0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,
|
|
0x00U, 0x00U, 0x00U, 0x00U, 'k', 'e', 'y', 0x00U})
|
|
.data());
|
|
|
|
auto const ns = uint256::fromVoid(
|
|
(std::array<uint8_t, 32>{
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU})
|
|
.data());
|
|
|
|
auto const stateKeylet =
|
|
keylet::hookState(Account("alice").id(), key, ns);
|
|
|
|
// create a namespace
|
|
std::string ns_str =
|
|
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE";
|
|
{
|
|
// create hook
|
|
Json::Value jv =
|
|
ripple::test::jtx::hook(alice, {{hso(makestate_wasm)}}, 0);
|
|
|
|
jv[jss::Hooks][0U][jss::Hook][jss::HookNamespace] = ns_str;
|
|
env(jv, M("Create makestate hook"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// run hook
|
|
env(pay(bob, alice, XRP(1)),
|
|
M("Run create state hook"),
|
|
fee(XRP(1)));
|
|
env.close();
|
|
|
|
// check if the hookstate object was created
|
|
auto const hookstate = env.le(stateKeylet);
|
|
BEAST_EXPECT(!!hookstate);
|
|
|
|
// check if the value was set correctly
|
|
auto const& data = hookstate->getFieldVL(sfHookStateData);
|
|
BEAST_REQUIRE(data.size() == 6);
|
|
BEAST_EXPECT(
|
|
data[0] == 'v' && data[1] == 'a' && data[2] == 'l' &&
|
|
data[3] == 'u' && data[4] == 'e' && data[5] == '\0');
|
|
}
|
|
|
|
// delete the namespace
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::Flags] = hsfNSDELETE;
|
|
iv[jss::HookNamespace] = ns_str;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv, M("Normal NSDELETE operation"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// ensure the hook is still installed
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() > 0);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == makestate_hash);
|
|
|
|
// ensure the directory is gone
|
|
auto const dirKeylet =
|
|
keylet::hookStateDir(Account("alice").id(), ns);
|
|
BEAST_EXPECT(!env.le(dirKeylet));
|
|
|
|
// ensure the state object is gone
|
|
BEAST_EXPECT(!env.le(stateKeylet));
|
|
}
|
|
}
|
|
|
|
void
|
|
testCreate()
|
|
{
|
|
testcase("Checks malformed create operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), bob);
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
// test normal create and missing override flag
|
|
{
|
|
env(ripple::test::jtx::hook(bob, {{hso(accept_wasm)}}, 0),
|
|
M("First set = tesSUCCESS"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
|
|
env(ripple::test::jtx::hook(bob, {{hso(accept_wasm)}}, 0),
|
|
M("Second set = tecREQUIRES_FLAG"),
|
|
HSFEE,
|
|
ter(tecREQUIRES_FLAG));
|
|
env.close();
|
|
}
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// payload too large
|
|
{
|
|
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();
|
|
}
|
|
|
|
// namespace missing
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = strHex(accept_wasm);
|
|
iv[jss::HookApiVersion] = 0U;
|
|
iv[jss::HookOn] =
|
|
"00000000000000000000000000000000000000000000000000000000000000"
|
|
"00";
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("HSO Create operation must contain namespace"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// api version missing
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = strHex(accept_wasm);
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
iv[jss::HookOn] =
|
|
"00000000000000000000000000000000000000000000000000000000000000"
|
|
"00";
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("HSO Create operation must contain api version"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// api version wrong
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = strHex(accept_wasm);
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
iv[jss::HookApiVersion] = 1U;
|
|
iv[jss::HookOn] =
|
|
"00000000000000000000000000000000000000000000000000000000000000"
|
|
"00";
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("HSO Create operation must contain valid api version"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// hookon missing
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = strHex(accept_wasm);
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
iv[jss::HookApiVersion] = 0U;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("HSO Create operation must contain hookon"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// hook hash present
|
|
{
|
|
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});
|
|
jv[jss::Hooks][0U] = iv;
|
|
env(jv,
|
|
M("Cannot have both CreateCode and HookHash"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// correctly formed
|
|
{
|
|
Json::Value jv =
|
|
ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0);
|
|
env(jv, M("Normal accept"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
auto const def = env.le(accept_keylet);
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
|
|
// check if the hook definition exists
|
|
BEAST_EXPECT(!!def);
|
|
|
|
// check if the user account has a hooks object
|
|
BEAST_EXPECT(!!hook);
|
|
|
|
// check if the hook is correctly set at position 1
|
|
BEAST_EXPECT(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() > 0);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check if the wasm binary was correctly set
|
|
BEAST_EXPECT(def->isFieldPresent(sfCreateCode));
|
|
auto const& wasm = def->getFieldVL(sfCreateCode);
|
|
auto const wasm_hash =
|
|
sha512Half_s(ripple::Slice(wasm.data(), wasm.size()));
|
|
BEAST_EXPECT(wasm_hash == accept_hash);
|
|
}
|
|
|
|
// add a second hook
|
|
{
|
|
Json::Value jv =
|
|
ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0);
|
|
Json::Value iv = jv[jss::Hooks][0U];
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][1U] = iv;
|
|
env(jv,
|
|
M("Normal accept, second position"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
auto const def = env.le(accept_keylet);
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
|
|
// check if the hook definition exists
|
|
BEAST_EXPECT(!!def);
|
|
|
|
// check if the user account has a hooks object
|
|
BEAST_EXPECT(!!hook);
|
|
|
|
// check if the hook is correctly set at position 2
|
|
BEAST_EXPECT(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() > 1);
|
|
BEAST_EXPECT(hooks[1].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[1].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check if the reference count was correctly incremented
|
|
BEAST_EXPECT(def->isFieldPresent(sfReferenceCount));
|
|
// two references from alice, one from bob (first test above)
|
|
BEAST_EXPECT(def->getFieldU64(sfReferenceCount) == 3ULL);
|
|
}
|
|
|
|
auto const rollback_hash = ripple::sha512Half_s(
|
|
ripple::Slice(rollback_wasm.data(), rollback_wasm.size()));
|
|
|
|
// test override
|
|
{
|
|
Json::Value jv =
|
|
ripple::test::jtx::hook(alice, {{hso(rollback_wasm)}}, 0);
|
|
jv[jss::Hooks][0U][jss::Hook][jss::Flags] = hsfOVERRIDE;
|
|
env(jv, M("Rollback override"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
auto const rollback_def = env.le(rollback_keylet);
|
|
auto const accept_def = env.le(accept_keylet);
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
|
|
// check if the hook definition exists
|
|
BEAST_EXPECT(rollback_def);
|
|
BEAST_EXPECT(accept_def);
|
|
|
|
// check if the user account has a hooks object
|
|
BEAST_EXPECT(hook);
|
|
|
|
// check if the hook is correctly set at position 1
|
|
BEAST_EXPECT(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() > 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == rollback_hash);
|
|
BEAST_EXPECT(hooks[1].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[1].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check if the wasm binary was correctly set
|
|
BEAST_EXPECT(rollback_def->isFieldPresent(sfCreateCode));
|
|
auto const& wasm = rollback_def->getFieldVL(sfCreateCode);
|
|
auto const wasm_hash =
|
|
sha512Half_s(ripple::Slice(wasm.data(), wasm.size()));
|
|
BEAST_EXPECT(wasm_hash == rollback_hash);
|
|
|
|
// check if the reference count was correctly incremented
|
|
BEAST_EXPECT(rollback_def->isFieldPresent(sfReferenceCount));
|
|
BEAST_EXPECT(rollback_def->getFieldU64(sfReferenceCount) == 1ULL);
|
|
|
|
// check if the reference count was correctly decremented
|
|
BEAST_EXPECT(accept_def->isFieldPresent(sfReferenceCount));
|
|
BEAST_EXPECT(accept_def->getFieldU64(sfReferenceCount) == 2ULL);
|
|
}
|
|
}
|
|
|
|
void
|
|
testUpdate()
|
|
{
|
|
testcase("Checks malformed update operation");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), bob);
|
|
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::SetHook;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Hooks] = Json::Value{Json::arrayValue};
|
|
|
|
// first create the hook
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::CreateCode] = strHex(accept_wasm);
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
iv[jss::HookApiVersion] = 0U;
|
|
iv[jss::HookOn] =
|
|
"00000000000000000000000000000000000000000000000000000000000000"
|
|
"00";
|
|
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
|
iv[jss::HookParameters][0U] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterName] = "AAAAAAAAAAAA";
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "BBBBBB";
|
|
|
|
iv[jss::HookParameters][1U] = Json::Value{};
|
|
iv[jss::HookParameters][1U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][1U][jss::HookParameter]
|
|
[jss::HookParameterName] = "CAFE";
|
|
iv[jss::HookParameters][1U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "FACADE";
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv, M("Create accept"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
// all alice operations below are then updates
|
|
|
|
// must not specify override flag
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::Flags] = hsfOVERRIDE;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("Override flag not allowed on update"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// must not specify NSDELETE unless also Namespace
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::Flags] = hsfNSDELETE;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("NSDELETE flag not allowed on update unless HookNamespace "
|
|
"also present"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// api version not allowed in update
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookApiVersion] = 0U;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("ApiVersion not allowed in update"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// try individually updating the various allowed fields
|
|
{
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U][jss::HookParameter] = Json::Value{};
|
|
params[0U][jss::HookParameter][jss::HookParameterName] = "CAFE";
|
|
params[0U][jss::HookParameter][jss::HookParameterValue] = "BABE";
|
|
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = accept_hash_str;
|
|
|
|
for (auto const& [key, value] : JSSMap{
|
|
{jss::HookOn,
|
|
"00000000000000000000000000000000000000000000000000000000"
|
|
"00000001"},
|
|
{jss::HookNamespace,
|
|
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"
|
|
"CAFECAFE"},
|
|
{jss::HookParameters, params},
|
|
{jss::HookGrants, grants}})
|
|
{
|
|
Json::Value iv;
|
|
iv[key] = value;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv, M("Normal update"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check all fields were updated to correct values
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookOn));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookOn) == UINT256_BIT[0]);
|
|
|
|
auto const ns = uint256::fromVoid(
|
|
(std::array<uint8_t, 32>{
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU,
|
|
0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU, 0xCAU, 0xFEU})
|
|
.data());
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookNamespace));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookNamespace) == ns);
|
|
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookParameters));
|
|
const auto& p = hooks[0].getFieldArray(sfHookParameters);
|
|
BEAST_REQUIRE(p.size() == 1);
|
|
BEAST_REQUIRE(p[0].isFieldPresent(sfHookParameterName));
|
|
BEAST_REQUIRE(p[0].isFieldPresent(sfHookParameterValue));
|
|
|
|
const auto pn = p[0].getFieldVL(sfHookParameterName);
|
|
BEAST_REQUIRE(pn.size() == 2);
|
|
BEAST_REQUIRE(pn[0] == 0xCAU && pn[1] == 0xFEU);
|
|
|
|
const auto pv = p[0].getFieldVL(sfHookParameterValue);
|
|
BEAST_REQUIRE(pv.size() == 2);
|
|
BEAST_REQUIRE(pv[0] == 0xBAU && pv[1] == 0xBEU);
|
|
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookGrants));
|
|
const auto& g = hooks[0].getFieldArray(sfHookGrants);
|
|
BEAST_REQUIRE(g.size() == 1);
|
|
BEAST_REQUIRE(g[0].isFieldPresent(sfHookHash));
|
|
BEAST_REQUIRE(g[0].getFieldH256(sfHookHash) == accept_hash);
|
|
}
|
|
|
|
// reset hookon and namespace to defaults
|
|
{
|
|
for (auto const& [key, value] : JSSMap{
|
|
{jss::HookOn,
|
|
"00000000000000000000000000000000000000000000000000000000"
|
|
"00000000"},
|
|
{jss::HookNamespace, to_string(uint256{beast::zero})}})
|
|
{
|
|
Json::Value iv;
|
|
iv[key] = value;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
env(jv, M("Reset to default"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// ensure the two fields are now absent (because they were reset to
|
|
// the defaults on the hook def)
|
|
BEAST_EXPECT(!hooks[0].isFieldPresent(sfHookOn));
|
|
BEAST_EXPECT(!hooks[0].isFieldPresent(sfHookNamespace));
|
|
}
|
|
|
|
// add three additional parameters
|
|
std::map<ripple::Blob, ripple::Blob> params{
|
|
{{0xFEU, 0xEDU, 0xFAU, 0xCEU}, {0xF0U, 0x0DU}},
|
|
{{0xA0U}, {0xB0U}},
|
|
{{0xCAU, 0xFEU}, {0xBAU, 0xBEU}},
|
|
{{0xAAU}, {0xBBU, 0xCCU}}};
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
|
iv[jss::HookParameters][0U] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterName] = "FEEDFACE";
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "F00D";
|
|
|
|
iv[jss::HookParameters][1U] = Json::Value{};
|
|
iv[jss::HookParameters][1U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][1U][jss::HookParameter]
|
|
[jss::HookParameterName] = "A0";
|
|
iv[jss::HookParameters][1U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "B0";
|
|
|
|
iv[jss::HookParameters][2U] = Json::Value{};
|
|
iv[jss::HookParameters][2U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][2U][jss::HookParameter]
|
|
[jss::HookParameterName] = "AA";
|
|
iv[jss::HookParameters][2U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "BBCC";
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv, M("Add three parameters"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check all the previous parameters plus the new ones
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookParameters));
|
|
const auto& p = hooks[0].getFieldArray(sfHookParameters);
|
|
BEAST_REQUIRE(p.size() == params.size());
|
|
|
|
std::set<ripple::Blob> already;
|
|
|
|
for (uint8_t i = 0; i < params.size(); ++i)
|
|
{
|
|
const auto pn = p[i].getFieldVL(sfHookParameterName);
|
|
const auto pv = p[i].getFieldVL(sfHookParameterValue);
|
|
|
|
// make sure it's not a duplicate entry
|
|
BEAST_EXPECT(already.find(pn) == already.end());
|
|
|
|
// make sure it exists
|
|
BEAST_EXPECT(params.find(pn) != params.end());
|
|
|
|
// make sure the value matches
|
|
BEAST_EXPECT(params[pn] == pv);
|
|
already.emplace(pn);
|
|
}
|
|
}
|
|
|
|
// try to reset CAFE parameter to default
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
|
iv[jss::HookParameters][0U] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterName] = "CAFE";
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Reset cafe param to default using Absent Value"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
params.erase({0xCAU, 0xFEU});
|
|
|
|
// check there right number of parameters exist
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookParameters));
|
|
const auto& p = hooks[0].getFieldArray(sfHookParameters);
|
|
BEAST_REQUIRE(p.size() == params.size());
|
|
|
|
// and that they still have the expected values and that there are
|
|
// no duplicates
|
|
std::set<ripple::Blob> already;
|
|
for (uint8_t i = 0; i < params.size(); ++i)
|
|
{
|
|
const auto pn = p[i].getFieldVL(sfHookParameterName);
|
|
const auto pv = p[i].getFieldVL(sfHookParameterValue);
|
|
|
|
// make sure it's not a duplicate entry
|
|
BEAST_EXPECT(already.find(pn) == already.end());
|
|
|
|
// make sure it exists
|
|
BEAST_EXPECT(params.find(pn) != params.end());
|
|
|
|
// make sure the value matches
|
|
BEAST_EXPECT(params[pn] == pv);
|
|
already.emplace(pn);
|
|
}
|
|
}
|
|
|
|
// now re-add CAFE parameter but this time as an explicit blank (Empty
|
|
// value)
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
|
iv[jss::HookParameters][0U] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter] = Json::Value{};
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterName] = "CAFE";
|
|
iv[jss::HookParameters][0U][jss::HookParameter]
|
|
[jss::HookParameterValue] = "";
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv,
|
|
M("Set cafe param to blank using Empty Value"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
params[Blob{0xCAU, 0xFEU}] = Blob{};
|
|
|
|
// check there right number of parameters exist
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookParameters));
|
|
const auto& p = hooks[0].getFieldArray(sfHookParameters);
|
|
BEAST_REQUIRE(p.size() == params.size());
|
|
|
|
// and that they still have the expected values and that there are
|
|
// no duplicates
|
|
std::set<ripple::Blob> already;
|
|
for (uint8_t i = 0; i < params.size(); ++i)
|
|
{
|
|
const auto pn = p[i].getFieldVL(sfHookParameterName);
|
|
const auto pv = p[i].getFieldVL(sfHookParameterValue);
|
|
|
|
// make sure it's not a duplicate entry
|
|
BEAST_EXPECT(already.find(pn) == already.end());
|
|
|
|
// make sure it exists
|
|
BEAST_EXPECT(params.find(pn) != params.end());
|
|
|
|
// make sure the value matches
|
|
BEAST_EXPECT(params[pn] == pv);
|
|
already.emplace(pn);
|
|
}
|
|
}
|
|
|
|
// try to delete all parameters (reset to defaults) using EMA (Empty
|
|
// Parameters Array)
|
|
{
|
|
Json::Value iv;
|
|
iv[jss::HookParameters] = Json::Value{Json::arrayValue};
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv, M("Unset all params on hook"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check there right number of parameters exist
|
|
BEAST_REQUIRE(!hooks[0].isFieldPresent(sfHookParameters));
|
|
}
|
|
|
|
// try to set each type of field on a non existent hook
|
|
{
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U][jss::HookParameter] = Json::Value{};
|
|
params[0U][jss::HookParameter][jss::HookParameterName] = "CAFE";
|
|
params[0U][jss::HookParameter][jss::HookParameterValue] = "BABE";
|
|
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = accept_hash_str;
|
|
|
|
for (auto const& [key, value] : JSSMap{
|
|
{jss::HookOn,
|
|
"00000000000000000000000000000000000000000000000000000000"
|
|
"00000001"},
|
|
{jss::HookNamespace,
|
|
"CAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFECAFE"
|
|
"CAFECAFE"},
|
|
{jss::HookParameters, params},
|
|
{jss::HookGrants, grants}})
|
|
{
|
|
Json::Value iv;
|
|
iv[key] = value;
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][1U] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook] = iv;
|
|
|
|
env(jv,
|
|
M("Invalid update on non existent hook"),
|
|
HSFEE,
|
|
ter(tecNO_ENTRY));
|
|
env.close();
|
|
}
|
|
|
|
// ensure hook still exists and that there was no created new entry
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 1);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
}
|
|
|
|
// test adding multiple grants
|
|
{
|
|
{
|
|
// add a second hook
|
|
env(ripple::test::jtx::hook(alice, {{{}, hso(accept_wasm)}}, 0),
|
|
M("Add second hook"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
}
|
|
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = rollback_hash_str;
|
|
grants[0U][jss::HookGrant][jss::Authorize] = bob.human();
|
|
|
|
grants[1U][jss::HookGrant] = Json::Value{};
|
|
grants[1U][jss::HookGrant][jss::HookHash] = accept_hash_str;
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = Json::objectValue;
|
|
jv[jss::Hooks][1U] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook][jss::HookGrants] = grants;
|
|
|
|
env(jv, M("Add grants"), HSFEE);
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 2);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check there right number of grants exist
|
|
// hook 0 should have 1 grant
|
|
BEAST_REQUIRE(hooks[0].isFieldPresent(sfHookGrants));
|
|
BEAST_REQUIRE(hooks[0].getFieldArray(sfHookGrants).size() == 1);
|
|
// hook 1 should have 2 grants
|
|
{
|
|
BEAST_REQUIRE(hooks[1].isFieldPresent(sfHookGrants));
|
|
auto const& grants = hooks[1].getFieldArray(sfHookGrants);
|
|
BEAST_REQUIRE(grants.size() == 2);
|
|
|
|
BEAST_REQUIRE(grants[0].isFieldPresent(sfHookHash));
|
|
BEAST_REQUIRE(grants[0].isFieldPresent(sfAuthorize));
|
|
BEAST_REQUIRE(grants[1].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(!grants[1].isFieldPresent(sfAuthorize));
|
|
|
|
BEAST_EXPECT(
|
|
grants[0].getFieldH256(sfHookHash) == rollback_hash);
|
|
BEAST_EXPECT(grants[0].getAccountID(sfAuthorize) == bob.id());
|
|
|
|
BEAST_EXPECT(grants[1].getFieldH256(sfHookHash) == accept_hash);
|
|
}
|
|
}
|
|
|
|
// update grants
|
|
{
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = makestate_hash_str;
|
|
|
|
jv[jss::Hooks][0U] = Json::Value{};
|
|
jv[jss::Hooks][0U][jss::Hook] = Json::objectValue;
|
|
jv[jss::Hooks][1U] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook][jss::HookGrants] = grants;
|
|
|
|
env(jv, M("update grants"), HSFEE);
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 2);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check there right number of grants exist
|
|
// hook 1 should have 1 grant
|
|
{
|
|
BEAST_REQUIRE(hooks[1].isFieldPresent(sfHookGrants));
|
|
auto const& grants = hooks[1].getFieldArray(sfHookGrants);
|
|
BEAST_REQUIRE(grants.size() == 1);
|
|
BEAST_REQUIRE(grants[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(
|
|
grants[0].getFieldH256(sfHookHash) == makestate_hash);
|
|
}
|
|
}
|
|
|
|
// use an empty grants array to reset the grants
|
|
{
|
|
jv[jss::Hooks][0U] = Json::objectValue;
|
|
jv[jss::Hooks][0U][jss::Hook] = Json::objectValue;
|
|
jv[jss::Hooks][1U] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook] = Json::Value{};
|
|
jv[jss::Hooks][1U][jss::Hook][jss::HookGrants] = Json::arrayValue;
|
|
|
|
env(jv, M("clear grants"), HSFEE);
|
|
env.close();
|
|
|
|
// ensure hook still exists
|
|
auto const hook = env.le(keylet::hook(Account("alice").id()));
|
|
BEAST_REQUIRE(hook);
|
|
|
|
BEAST_REQUIRE(hook->isFieldPresent(sfHooks));
|
|
auto const& hooks = hook->getFieldArray(sfHooks);
|
|
BEAST_EXPECT(hooks.size() == 2);
|
|
BEAST_EXPECT(hooks[0].isFieldPresent(sfHookHash));
|
|
BEAST_EXPECT(hooks[0].getFieldH256(sfHookHash) == accept_hash);
|
|
|
|
// check there right number of grants exist
|
|
// hook 1 should have 0 grants
|
|
BEAST_REQUIRE(!hooks[1].isFieldPresent(sfHookGrants));
|
|
}
|
|
}
|
|
|
|
void
|
|
testWithTickets()
|
|
{
|
|
testcase("with tickets");
|
|
using namespace jtx;
|
|
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const alice = Account{"alice"};
|
|
env.fund(XRP(10000), alice);
|
|
|
|
std::uint32_t aliceTicketSeq{env.seq(alice) + 1};
|
|
env(ticket::create(alice, 10));
|
|
std::uint32_t const aliceSeq{env.seq(alice)};
|
|
env.require(owners(alice, 10));
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(accept_wasm)}}, 0),
|
|
HSFEE,
|
|
ticket::use(aliceTicketSeq++),
|
|
ter(tesSUCCESS));
|
|
|
|
env.require(tickets(alice, env.seq(alice) - aliceTicketSeq));
|
|
BEAST_EXPECT(env.seq(alice) == aliceSeq);
|
|
env.require(owners(alice, 9 + 1));
|
|
}
|
|
|
|
void
|
|
testInferHookSetOperation()
|
|
{
|
|
testcase("Test operation inference");
|
|
|
|
// hsoNOOP
|
|
{
|
|
STObject hso{sfHook};
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNOOP);
|
|
}
|
|
|
|
// hsoCREATE
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoCREATE);
|
|
}
|
|
|
|
// hsoDELETE
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldVL(sfCreateCode, ripple::Blob{}); // empty create code
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoDELETE);
|
|
}
|
|
|
|
// hsoINSTALL
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldH256(
|
|
sfHookHash, uint256{beast::zero}); // all zeros hook hash
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINSTALL);
|
|
}
|
|
|
|
// hsoNSDELETE
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldH256(
|
|
sfHookNamespace, uint256{beast::zero}); // all zeros hook hash
|
|
hso.setFieldU32(sfFlags, hsfNSDELETE);
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoNSDELETE);
|
|
}
|
|
|
|
// hsoUPDATE
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldH256(sfHookOn, UINT256_BIT[0]);
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoUPDATE);
|
|
}
|
|
|
|
// hsoINVALID
|
|
{
|
|
STObject hso{sfHook};
|
|
hso.setFieldVL(sfCreateCode, {1}); // non-empty create code
|
|
hso.setFieldH256(
|
|
sfHookHash, uint256{beast::zero}); // all zeros hook hash
|
|
BEAST_EXPECT(SetHook::inferOperation(hso) == hsoINVALID);
|
|
}
|
|
}
|
|
|
|
void
|
|
testWasm()
|
|
{
|
|
testcase("Checks malformed hook binaries");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
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));
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(illegalfunc_wasm)}}, 0),
|
|
M("Must only contain hook and cbak"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
}
|
|
|
|
void
|
|
test_accept()
|
|
{
|
|
testcase("Test accept() hookapi");
|
|
using namespace jtx;
|
|
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();
|
|
}
|
|
|
|
void
|
|
test_rollback()
|
|
{
|
|
testcase("Test rollback() hookapi");
|
|
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),
|
|
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();
|
|
}
|
|
|
|
void
|
|
testGuards()
|
|
{
|
|
testcase("Test guards");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
// test a simple loop without a guard call
|
|
{
|
|
TestHook hook = wasm[R"[test.hook](
|
|
(module
|
|
(type (;0;) (func (param i32 i32) (result i64)))
|
|
(type (;1;) (func (param i32 i32) (result i32)))
|
|
(type (;2;) (func (param i32) (result i64)))
|
|
(import "env" "hook_account" (func (;0;) (type 0)))
|
|
(import "env" "_g" (func (;1;) (type 1)))
|
|
(func (;2;) (type 2) (param i32) (result i64)
|
|
(local i32)
|
|
global.get 0
|
|
i32.const 32
|
|
i32.sub
|
|
local.tee 1
|
|
global.set 0
|
|
loop (result i64) ;; label = @1
|
|
local.get 1
|
|
i32.const 20
|
|
call 0
|
|
drop
|
|
br 0 (;@1;)
|
|
end)
|
|
(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)))
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook)}}, 0),
|
|
M("Loop 1 no guards"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
|
|
// same loop again but with a guard call
|
|
{
|
|
TestHook hook = wasm[R"[test.hook](
|
|
(module
|
|
(type (;0;) (func (param i32 i32) (result i64)))
|
|
(type (;1;) (func (param i32 i32) (result i32)))
|
|
(type (;2;) (func (param i32) (result i64)))
|
|
(import "env" "hook_account" (func (;0;) (type 0)))
|
|
(import "env" "_g" (func (;1;) (type 1)))
|
|
(func (;2;) (type 2) (param i32) (result i64)
|
|
(local i32)
|
|
global.get 0
|
|
i32.const 32
|
|
i32.sub
|
|
local.tee 1
|
|
global.set 0
|
|
loop (result i64) ;; label = @1
|
|
i32.const 1
|
|
i32.const 1
|
|
call 1
|
|
drop
|
|
local.get 1
|
|
i32.const 20
|
|
call 0
|
|
drop
|
|
br 0 (;@1;)
|
|
end)
|
|
(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)))
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook)}}, 0),
|
|
M("Loop 1 with guards"),
|
|
HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// simple looping, c
|
|
{
|
|
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 hook_account (uint32_t, uint32_t);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
uint8_t acc[20];
|
|
for (int i = 0; GUARD(10), i < 10; ++i)
|
|
hook_account(acc, 20);
|
|
|
|
return accept(0,0,2);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("Loop 2 in C"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("Test Loop 2"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
// complex looping, c
|
|
{
|
|
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 hook_account (uint32_t, uint32_t);
|
|
int64_t hook(uint32_t reserved)
|
|
{
|
|
uint8_t acc[20];
|
|
// guards should be computed by:
|
|
// (this loop iterations + 1) * (each parent loop's iteration's + 0)
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
_g(1, 11);
|
|
for (int j = 0; j < 2; ++j)
|
|
{
|
|
_g(2, 30);
|
|
for (int k = 0; k < 5; ++k)
|
|
{
|
|
_g(3, 120);
|
|
hook_account(acc, 20);
|
|
}
|
|
for (int k = 0; k < 5; ++k)
|
|
{
|
|
_g(4, 120);
|
|
hook_account(acc, 20);
|
|
}
|
|
}
|
|
}
|
|
return accept(0,0,2);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("Loop 3 in C"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("Test Loop 3"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
// complex looping missing a guard
|
|
{
|
|
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 hook_account (uint32_t, uint32_t);
|
|
int64_t hook(uint32_t reserved)
|
|
{
|
|
uint8_t acc[20];
|
|
// guards should be computed by:
|
|
// (this loop iterations + 1) * (each parent loop's iteration's + 0)
|
|
for (int i = 0; i < acc[0]; ++i)
|
|
{
|
|
_g(1, 11);
|
|
for (int j = 0; j < acc[1]; ++j)
|
|
{
|
|
// guard missing here
|
|
hook_account(acc, 20);
|
|
for (int k = 0; k < acc[2]; ++k)
|
|
{
|
|
_g(3, 120);
|
|
hook_account(acc, 20);
|
|
}
|
|
for (int k = 0; k < acc[3]; ++k)
|
|
{
|
|
_g(4, 120);
|
|
hook_account(acc, 20);
|
|
}
|
|
}
|
|
}
|
|
return accept(0,0,2);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("Loop 4 in C"),
|
|
HSFEE,
|
|
ter(temMALFORMED));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_emit()
|
|
{
|
|
testcase("Test float_emit");
|
|
using namespace jtx;
|
|
Env env{
|
|
*this,
|
|
envconfig(),
|
|
supported_amendments(),
|
|
nullptr,
|
|
beast::severities::kWarning
|
|
// beast::severities::kTrace
|
|
};
|
|
|
|
auto const alice = Account{"alice"};
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = wasm[R"[test.hook](
|
|
#include <stdint.h>
|
|
extern int32_t _g(uint32_t, uint32_t);
|
|
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 emit (uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t etxn_reserve(uint32_t);
|
|
extern int64_t otxn_param(uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t hook_account(uint32_t, uint32_t);
|
|
extern int64_t otxn_field (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t field_id
|
|
);
|
|
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define ttPAYMENT 0
|
|
#define tfCANONICAL 0x80000000UL
|
|
#define amAMOUNT 1U
|
|
#define amFEE 8U
|
|
#define atACCOUNT 1U
|
|
#define DOESNT_EXIST (-5)
|
|
#define atDESTINATION 3U
|
|
#define SBUF(x) (uint32_t)x,sizeof(x)
|
|
|
|
#define PREREQUISITE_NOT_MET -9
|
|
#define ENCODE_DROPS_SIZE 9
|
|
#define ENCODE_DROPS(buf_out, drops, amount_type ) \
|
|
{\
|
|
uint8_t uat = amount_type; \
|
|
uint64_t udrops = drops; \
|
|
buf_out[0] = 0x60U +(uat & 0x0FU ); \
|
|
buf_out[1] = 0b01000000 + (( udrops >> 56 ) & 0b00111111 ); \
|
|
buf_out[2] = (udrops >> 48) & 0xFFU; \
|
|
buf_out[3] = (udrops >> 40) & 0xFFU; \
|
|
buf_out[4] = (udrops >> 32) & 0xFFU; \
|
|
buf_out[5] = (udrops >> 24) & 0xFFU; \
|
|
buf_out[6] = (udrops >> 16) & 0xFFU; \
|
|
buf_out[7] = (udrops >> 8) & 0xFFU; \
|
|
buf_out[8] = (udrops >> 0) & 0xFFU; \
|
|
buf_out += ENCODE_DROPS_SIZE; \
|
|
}
|
|
|
|
#define _06_XX_ENCODE_DROPS(buf_out, drops, amount_type )\
|
|
ENCODE_DROPS(buf_out, drops, amount_type );
|
|
|
|
#define ENCODE_DROPS_AMOUNT(buf_out, drops )\
|
|
ENCODE_DROPS(buf_out, drops, amAMOUNT );
|
|
#define _06_01_ENCODE_DROPS_AMOUNT(buf_out, drops )\
|
|
ENCODE_DROPS_AMOUNT(buf_out, drops );
|
|
|
|
#define ENCODE_DROPS_FEE(buf_out, drops )\
|
|
ENCODE_DROPS(buf_out, drops, amFEE );
|
|
#define _06_08_ENCODE_DROPS_FEE(buf_out, drops )\
|
|
ENCODE_DROPS_FEE(buf_out, drops );
|
|
|
|
#define ENCODE_TT_SIZE 3
|
|
#define ENCODE_TT(buf_out, tt )\
|
|
{\
|
|
uint8_t utt = tt;\
|
|
buf_out[0] = 0x12U;\
|
|
buf_out[1] =(utt >> 8 ) & 0xFFU;\
|
|
buf_out[2] =(utt >> 0 ) & 0xFFU;\
|
|
buf_out += ENCODE_TT_SIZE; \
|
|
}
|
|
#define _01_02_ENCODE_TT(buf_out, tt)\
|
|
ENCODE_TT(buf_out, tt);
|
|
|
|
|
|
#define ENCODE_ACCOUNT_SIZE 22
|
|
#define ENCODE_ACCOUNT(buf_out, account_id, account_type)\
|
|
{\
|
|
uint8_t uat = account_type;\
|
|
buf_out[0] = 0x80U + uat;\
|
|
buf_out[1] = 0x14U;\
|
|
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(account_id + 0);\
|
|
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(account_id + 8);\
|
|
*(uint32_t*)(buf_out + 18) = *(uint32_t*)(account_id + 16);\
|
|
buf_out += ENCODE_ACCOUNT_SIZE;\
|
|
}
|
|
#define _08_XX_ENCODE_ACCOUNT(buf_out, account_id, account_type)\
|
|
ENCODE_ACCOUNT(buf_out, account_id, account_type);
|
|
|
|
#define ENCODE_ACCOUNT_SRC_SIZE 22
|
|
#define ENCODE_ACCOUNT_SRC(buf_out, account_id)\
|
|
ENCODE_ACCOUNT(buf_out, account_id, atACCOUNT);
|
|
#define _08_01_ENCODE_ACCOUNT_SRC(buf_out, account_id)\
|
|
ENCODE_ACCOUNT_SRC(buf_out, account_id);
|
|
|
|
#define ENCODE_ACCOUNT_DST_SIZE 22
|
|
#define ENCODE_ACCOUNT_DST(buf_out, account_id)\
|
|
ENCODE_ACCOUNT(buf_out, account_id, atDESTINATION);
|
|
#define _08_03_ENCODE_ACCOUNT_DST(buf_out, account_id)\
|
|
ENCODE_ACCOUNT_DST(buf_out, account_id);
|
|
|
|
#define ENCODE_ACCOUNT_OWNER_SIZE 22
|
|
#define ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
|
|
ENCODE_ACCOUNT(buf_out, account_id, atOWNER);
|
|
#define _08_02_ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
|
|
ENCODE_ACCOUNT_OWNER(buf_out, account_id);
|
|
|
|
#define ENCODE_UINT32_COMMON_SIZE 5U
|
|
#define ENCODE_UINT32_COMMON(buf_out, i, field)\
|
|
{\
|
|
uint32_t ui = i; \
|
|
uint8_t uf = field; \
|
|
buf_out[0] = 0x20U +(uf & 0x0FU); \
|
|
buf_out[1] =(ui >> 24 ) & 0xFFU; \
|
|
buf_out[2] =(ui >> 16 ) & 0xFFU; \
|
|
buf_out[3] =(ui >> 8 ) & 0xFFU; \
|
|
buf_out[4] =(ui >> 0 ) & 0xFFU; \
|
|
buf_out += ENCODE_UINT32_COMMON_SIZE; \
|
|
}
|
|
#define _02_XX_ENCODE_UINT32_COMMON(buf_out, i, field)\
|
|
ENCODE_UINT32_COMMON(buf_out, i, field)\
|
|
|
|
#define ENCODE_UINT32_UNCOMMON_SIZE 6U
|
|
#define ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
|
|
{\
|
|
uint32_t ui = i; \
|
|
uint8_t uf = field; \
|
|
buf_out[0] = 0x20U; \
|
|
buf_out[1] = uf; \
|
|
buf_out[2] =(ui >> 24 ) & 0xFFU; \
|
|
buf_out[3] =(ui >> 16 ) & 0xFFU; \
|
|
buf_out[4] =(ui >> 8 ) & 0xFFU; \
|
|
buf_out[5] =(ui >> 0 ) & 0xFFU; \
|
|
buf_out += ENCODE_UINT32_UNCOMMON_SIZE; \
|
|
}
|
|
#define _02_XX_ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
|
|
ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
|
|
|
|
#define ENCODE_LLS_SIZE 6U
|
|
#define ENCODE_LLS(buf_out, lls )\
|
|
ENCODE_UINT32_UNCOMMON(buf_out, lls, 0x1B );
|
|
#define _02_27_ENCODE_LLS(buf_out, lls )\
|
|
ENCODE_LLS(buf_out, lls );
|
|
|
|
#define ENCODE_FLS_SIZE 6U
|
|
#define ENCODE_FLS(buf_out, fls )\
|
|
ENCODE_UINT32_UNCOMMON(buf_out, fls, 0x1A );
|
|
#define _02_26_ENCODE_FLS(buf_out, fls )\
|
|
ENCODE_FLS(buf_out, fls );
|
|
|
|
#define ENCODE_TAG_SRC_SIZE 5
|
|
#define ENCODE_TAG_SRC(buf_out, tag )\
|
|
ENCODE_UINT32_COMMON(buf_out, tag, 0x3U );
|
|
#define _02_03_ENCODE_TAG_SRC(buf_out, tag )\
|
|
ENCODE_TAG_SRC(buf_out, tag );
|
|
|
|
#define ENCODE_TAG_DST_SIZE 5
|
|
#define ENCODE_TAG_DST(buf_out, tag )\
|
|
ENCODE_UINT32_COMMON(buf_out, tag, 0xEU );
|
|
#define _02_14_ENCODE_TAG_DST(buf_out, tag )\
|
|
ENCODE_TAG_DST(buf_out, tag );
|
|
|
|
#define ENCODE_SEQUENCE_SIZE 5
|
|
#define ENCODE_SEQUENCE(buf_out, sequence )\
|
|
ENCODE_UINT32_COMMON(buf_out, sequence, 0x4U );
|
|
#define _02_04_ENCODE_SEQUENCE(buf_out, sequence )\
|
|
ENCODE_SEQUENCE(buf_out, sequence );
|
|
|
|
#define ENCODE_FLAGS_SIZE 5
|
|
#define ENCODE_FLAGS(buf_out, tag )\
|
|
ENCODE_UINT32_COMMON(buf_out, tag, 0x2U );
|
|
#define _02_02_ENCODE_FLAGS(buf_out, tag )\
|
|
ENCODE_FLAGS(buf_out, tag );
|
|
|
|
#define ENCODE_SIGNING_PUBKEY_SIZE 35
|
|
#define ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
|
|
{\
|
|
buf_out[0] = 0x73U;\
|
|
buf_out[1] = 0x21U;\
|
|
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(pkey + 0);\
|
|
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(pkey + 8);\
|
|
*(uint64_t*)(buf_out + 18) = *(uint64_t*)(pkey + 16);\
|
|
*(uint64_t*)(buf_out + 26) = *(uint64_t*)(pkey + 24);\
|
|
buf[34] = pkey[32];\
|
|
buf_out += ENCODE_SIGNING_PUBKEY_SIZE;\
|
|
}
|
|
|
|
#define _07_03_ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
|
|
ENCODE_SIGNING_PUBKEY(buf_out, pkey );
|
|
|
|
#define ENCODE_SIGNING_PUBKEY_NULL_SIZE 35
|
|
#define ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
|
|
{\
|
|
buf_out[0] = 0x73U;\
|
|
buf_out[1] = 0x21U;\
|
|
*(uint64_t*)(buf_out+2) = 0;\
|
|
*(uint64_t*)(buf_out+10) = 0;\
|
|
*(uint64_t*)(buf_out+18) = 0;\
|
|
*(uint64_t*)(buf_out+25) = 0;\
|
|
buf_out += ENCODE_SIGNING_PUBKEY_NULL_SIZE;\
|
|
}
|
|
|
|
#define _07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
|
|
ENCODE_SIGNING_PUBKEY_NULL(buf_out );
|
|
|
|
extern int64_t etxn_fee_base (
|
|
uint32_t read_ptr,
|
|
uint32_t read_len
|
|
);
|
|
extern int64_t etxn_details (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len
|
|
);
|
|
extern int64_t ledger_seq (void);
|
|
|
|
#define PREPARE_PAYMENT_SIMPLE_SIZE 270U
|
|
#define PREPARE_PAYMENT_SIMPLE(buf_out_master, drops_amount_raw, to_address, dest_tag_raw, src_tag_raw)\
|
|
{\
|
|
uint8_t* buf_out = buf_out_master;\
|
|
uint8_t acc[20];\
|
|
uint64_t drops_amount = (drops_amount_raw);\
|
|
uint32_t dest_tag = (dest_tag_raw);\
|
|
uint32_t src_tag = (src_tag_raw);\
|
|
uint32_t cls = (uint32_t)ledger_seq();\
|
|
hook_account(SBUF(acc));\
|
|
_01_02_ENCODE_TT (buf_out, ttPAYMENT ); /* uint16 | size 3 */ \
|
|
_02_02_ENCODE_FLAGS (buf_out, tfCANONICAL ); /* uint32 | size 5 */ \
|
|
_02_03_ENCODE_TAG_SRC (buf_out, src_tag ); /* uint32 | size 5 */ \
|
|
_02_04_ENCODE_SEQUENCE (buf_out, 0 ); /* uint32 | size 5 */ \
|
|
_02_14_ENCODE_TAG_DST (buf_out, dest_tag ); /* uint32 | size 5 */ \
|
|
_02_26_ENCODE_FLS (buf_out, cls + 1 ); /* uint32 | size 6 */ \
|
|
_02_27_ENCODE_LLS (buf_out, cls + 5 ); /* uint32 | size 6 */ \
|
|
_06_01_ENCODE_DROPS_AMOUNT (buf_out, drops_amount ); /* amount | size 9 */ \
|
|
uint8_t* fee_ptr = buf_out;\
|
|
_06_08_ENCODE_DROPS_FEE (buf_out, 0 ); /* amount | size 9 */ \
|
|
_07_03_ENCODE_SIGNING_PUBKEY_NULL (buf_out ); /* pk | size 35 */ \
|
|
_08_01_ENCODE_ACCOUNT_SRC (buf_out, acc ); /* account | size 22 */ \
|
|
_08_03_ENCODE_ACCOUNT_DST (buf_out, to_address ); /* account | size 22 */ \
|
|
int64_t edlen = etxn_details((uint32_t)buf_out, PREPARE_PAYMENT_SIMPLE_SIZE); /* emitdet | size 1?? */ \
|
|
int64_t fee = etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_SIZE); \
|
|
_06_08_ENCODE_DROPS_FEE (fee_ptr, fee ); \
|
|
}
|
|
|
|
#define UINT16_FROM_BUF(buf)\
|
|
(((uint64_t)((buf)[0]) << 8U) +\
|
|
((uint64_t)((buf)[1]) << 0U))
|
|
|
|
#define BUFFER_EQUAL_32(buf1, buf2)\
|
|
(\
|
|
*(((uint64_t*)(buf1)) + 0) == *(((uint64_t*)(buf2)) + 0) &&\
|
|
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
|
|
*(((uint64_t*)(buf1)) + 2) == *(((uint64_t*)(buf2)) + 2) &&\
|
|
*(((uint64_t*)(buf1)) + 3) == *(((uint64_t*)(buf2)) + 3) &&\
|
|
*(((uint64_t*)(buf1)) + 4) == *(((uint64_t*)(buf2)) + 4) &&\
|
|
*(((uint64_t*)(buf1)) + 5) == *(((uint64_t*)(buf2)) + 5) &&\
|
|
*(((uint64_t*)(buf1)) + 6) == *(((uint64_t*)(buf2)) + 6) &&\
|
|
*(((uint64_t*)(buf1)) + 7) == *(((uint64_t*)(buf2)) + 7))
|
|
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x,sizeof(#x),__LINE__)
|
|
|
|
#define sfDestination ((8U << 16U) + 3U)
|
|
|
|
extern int64_t etxn_generation(void);
|
|
extern int64_t otxn_generation(void);
|
|
extern int64_t otxn_burden(void);
|
|
extern int64_t etxn_burden(void);
|
|
|
|
int64_t cbak(uint32_t r)
|
|
{
|
|
// on callback we emit 2 more txns
|
|
uint8_t bob[20];
|
|
ASSERT(otxn_field(SBUF(bob), sfDestination) == 20);
|
|
|
|
ASSERT(otxn_generation() + 1 == etxn_generation());
|
|
|
|
ASSERT(etxn_burden() == PREREQUISITE_NOT_MET);
|
|
|
|
ASSERT(etxn_reserve(2) == 2);
|
|
|
|
ASSERT(otxn_burden() > 0);
|
|
ASSERT(etxn_burden() == otxn_burden() * 2);
|
|
|
|
uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE];
|
|
PREPARE_PAYMENT_SIMPLE(tx, 1000, bob, 0, 0);
|
|
|
|
uint8_t hash1[32];
|
|
ASSERT(emit(SBUF(hash1), SBUF(tx)) == 32);
|
|
|
|
ASSERT(etxn_details(tx + 132, 138) == 138);
|
|
uint8_t hash2[32];
|
|
ASSERT(emit(SBUF(hash2), SBUF(tx)) == 32);
|
|
|
|
ASSERT(!BUFFER_EQUAL_32(hash1, hash2));
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
|
|
int64_t hook(uint32_t r)
|
|
{
|
|
_g(1,1);
|
|
|
|
etxn_reserve(1);
|
|
|
|
// bounds checks
|
|
ASSERT(emit(1000000, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(emit(0,1000000, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(emit(0,32, 1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(emit(0,32, 0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(otxn_generation() == 0);
|
|
ASSERT(otxn_burden == 1);
|
|
|
|
uint8_t bob[20];
|
|
ASSERT(otxn_param(SBUF(bob), "bob", 3) == 20);
|
|
|
|
uint8_t tx[PREPARE_PAYMENT_SIMPLE_SIZE];
|
|
PREPARE_PAYMENT_SIMPLE(tx, 1000, bob, 0, 0);
|
|
|
|
uint8_t hash[32];
|
|
ASSERT(emit(SBUF(hash), SBUF(tx)) == 32);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set emit"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
Json::Value invoke;
|
|
invoke[jss::TransactionType] = "Invoke";
|
|
invoke[jss::Account] = alice.human();
|
|
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U][jss::HookParameter][jss::HookParameterName] =
|
|
strHex(std::string("bob"));
|
|
params[0U][jss::HookParameter][jss::HookParameterValue] =
|
|
strHex(bob.id());
|
|
|
|
invoke[jss::HookParameters] = params;
|
|
|
|
env(invoke, M("test emit"), fee(XRP(1)));
|
|
|
|
std::optional<uint256> emithash;
|
|
{
|
|
auto meta = env.meta(); // meta can close
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// ensure there was one emitted txn
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU16(sfHookEmitCount) == 1);
|
|
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfAffectedNodes));
|
|
|
|
BEAST_REQUIRE(meta->getFieldArray(sfAffectedNodes).size() == 3);
|
|
|
|
for (auto const& node : meta->getFieldArray(sfAffectedNodes))
|
|
{
|
|
SField const& metaType = node.getFName();
|
|
uint16_t nodeType = node.getFieldU16(sfLedgerEntryType);
|
|
if (metaType == sfCreatedNode && nodeType == ltEMITTED_TXN)
|
|
{
|
|
BEAST_REQUIRE(node.isFieldPresent(sfNewFields));
|
|
|
|
auto const& nf = const_cast<ripple::STObject&>(node)
|
|
.getField(sfNewFields)
|
|
.downcast<STObject>();
|
|
|
|
auto const& et = const_cast<ripple::STObject&>(nf)
|
|
.getField(sfEmittedTxn)
|
|
.downcast<STObject>();
|
|
|
|
auto const& em = const_cast<ripple::STObject&>(et)
|
|
.getField(sfEmitDetails)
|
|
.downcast<STObject>();
|
|
|
|
BEAST_EXPECT(em.getFieldU32(sfEmitGeneration) == 1);
|
|
BEAST_EXPECT(em.getFieldU64(sfEmitBurden) == 1);
|
|
|
|
Blob txBlob = et.getSerializer().getData();
|
|
auto const tx = std::make_unique<STTx>(
|
|
Slice{txBlob.data(), txBlob.size()});
|
|
emithash = tx->getTransactionID();
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
BEAST_REQUIRE(emithash);
|
|
}
|
|
|
|
{
|
|
auto balbefore = env.balance(bob).value().xrp().drops();
|
|
|
|
env.close();
|
|
|
|
auto const ledger = env.closed();
|
|
|
|
int txcount = 0;
|
|
for (auto& i : ledger->txs)
|
|
{
|
|
auto const& hash = i.first->getTransactionID();
|
|
txcount++;
|
|
BEAST_EXPECT(hash == *emithash);
|
|
}
|
|
|
|
BEAST_EXPECT(txcount == 1);
|
|
|
|
auto balafter = env.balance(bob).value().xrp().drops();
|
|
|
|
BEAST_EXPECT(balafter - balbefore == 1000);
|
|
|
|
env.close();
|
|
}
|
|
|
|
uint64_t burden_expected = 2;
|
|
for (int j = 0; j < 7; ++j)
|
|
{
|
|
auto const ledger = env.closed();
|
|
for (auto& i : ledger->txs)
|
|
{
|
|
auto const& em = const_cast<ripple::STTx&>(*(i.first))
|
|
.getField(sfEmitDetails)
|
|
.downcast<STObject>();
|
|
BEAST_EXPECT(em.getFieldU64(sfEmitBurden) == burden_expected);
|
|
BEAST_EXPECT(em.getFieldU32(sfEmitGeneration) == j + 2);
|
|
BEAST_REQUIRE(i.second->isFieldPresent(sfHookExecutions));
|
|
auto const hookExecutions =
|
|
i.second->getFieldArray(sfHookExecutions);
|
|
BEAST_EXPECT(hookExecutions.size() == 1);
|
|
BEAST_EXPECT(
|
|
hookExecutions[0].getFieldU64(sfHookReturnCode) == 0);
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU8(sfHookResult) == 3);
|
|
BEAST_EXPECT(
|
|
hookExecutions[0].getFieldU16(sfHookEmitCount) == 2);
|
|
}
|
|
env.close();
|
|
burden_expected *= 2U;
|
|
}
|
|
|
|
{
|
|
auto const ledger = env.closed();
|
|
int txcount = 0;
|
|
for (auto& i : ledger->txs)
|
|
{
|
|
txcount++;
|
|
auto const& em = const_cast<ripple::STTx&>(*(i.first))
|
|
.getField(sfEmitDetails)
|
|
.downcast<STObject>();
|
|
BEAST_EXPECT(em.getFieldU64(sfEmitBurden) == 256);
|
|
BEAST_EXPECT(em.getFieldU32(sfEmitGeneration) == 9);
|
|
BEAST_REQUIRE(i.second->isFieldPresent(sfHookExecutions));
|
|
auto const hookExecutions =
|
|
i.second->getFieldArray(sfHookExecutions);
|
|
BEAST_EXPECT(hookExecutions.size() == 1);
|
|
BEAST_EXPECT(
|
|
hookExecutions[0].getFieldU64(sfHookReturnCode) ==
|
|
283); // emission failure on first emit
|
|
}
|
|
BEAST_EXPECT(txcount == 256);
|
|
}
|
|
|
|
// next close will lead to zero transactions
|
|
env.close();
|
|
{
|
|
auto const ledger = env.closed();
|
|
int txcount = 0;
|
|
for ([[maybe_unused]] auto& i : ledger->txs)
|
|
txcount++;
|
|
BEAST_EXPECT(txcount == 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
test_etxn_details()
|
|
{
|
|
// mainly tested in test_emit
|
|
testcase("Test etxn_details");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 etxn_details (uint32_t, uint32_t);
|
|
extern int64_t etxn_reserve(uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define PREREQUISITE_NOT_MET -9
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t det[116];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(etxn_details(1000000, 116) == OUT_OF_BOUNDS);
|
|
ASSERT(etxn_details(0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(etxn_details((uint32_t)det, 115) == TOO_SMALL);
|
|
|
|
ASSERT(etxn_details((uint32_t)det, 116) == PREREQUISITE_NOT_MET);
|
|
|
|
etxn_reserve(1);
|
|
ASSERT(etxn_details((uint32_t)det, 116) == 116);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set etxn_details"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test etxn_details"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_etxn_fee_base()
|
|
{
|
|
// mainly tested in test_emit
|
|
testcase("Test etxn_fee_base");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 etxn_fee_base (uint32_t, uint32_t);
|
|
extern int64_t etxn_reserve(uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define PREREQUISITE_NOT_MET -9
|
|
#define INVALID_TXN -37
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
uint8_t det[116];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(etxn_fee_base(1000000, 116) == OUT_OF_BOUNDS);
|
|
ASSERT(etxn_fee_base(0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(etxn_fee_base((uint32_t)det, 116) == PREREQUISITE_NOT_MET);
|
|
|
|
etxn_reserve(1);
|
|
ASSERT(etxn_fee_base((uint32_t)det, 116) == INVALID_TXN);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set etxn_fee_base"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test etxn_fee_base"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_etxn_nonce()
|
|
{
|
|
// mainly tested in test_emit
|
|
testcase("Test etxn_nonce");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 etxn_nonce (uint32_t, uint32_t);
|
|
extern int64_t etxn_reserve(uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define TOO_MANY_NONCES -12
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
uint8_t nonce[64];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(etxn_nonce(1000000, 116) == OUT_OF_BOUNDS);
|
|
ASSERT(etxn_nonce(0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(etxn_nonce((uint32_t)nonce, 31) == TOO_SMALL);
|
|
|
|
uint64_t* n1 = (uint64_t*)nonce;
|
|
uint64_t* n2 = (uint64_t*)(((uint8_t*)nonce) + 32);
|
|
|
|
for (int i = 0; GUARD(256), i < 256; ++i)
|
|
{
|
|
ASSERT(etxn_nonce((uint32_t)nonce + ((i % 2) * 32), 32) == 32);
|
|
ASSERT(!(*(n1 + 0) == *(n2 + 0) && *(n1 + 1) == *(n2 + 1)));
|
|
}
|
|
|
|
ASSERT(etxn_nonce((uint32_t)nonce, 116) == TOO_MANY_NONCES);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set etxn_nonce"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test etxn_nonce"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_etxn_reserve()
|
|
{
|
|
// mainly tested in test_emit
|
|
testcase("Test etxn_reserve");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 etxn_reserve(uint32_t);
|
|
#define TOO_BIG -3
|
|
#define TOO_SMALL -4
|
|
#define ALREADY_SET -8
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
ASSERT(etxn_reserve(0) == TOO_SMALL);
|
|
ASSERT(etxn_reserve(256) == TOO_BIG);
|
|
ASSERT(etxn_reserve(255) == 255);
|
|
ASSERT(etxn_reserve(255) == ALREADY_SET);
|
|
ASSERT(etxn_reserve(1) == ALREADY_SET);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set etxn_reserve"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test etxn_reserve"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_fee_base()
|
|
{
|
|
testcase("Test fee_base");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 fee_base(void);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
ASSERT(fee_base() == 10);
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set fee_base"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test fee_base"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_float_compare()
|
|
{
|
|
testcase("Test float_compare");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
#define EQ 0b001U
|
|
#define LT 0b010U
|
|
#define GT 0b100U
|
|
#define LTE 0b011U
|
|
#define GTE 0b101U
|
|
#define NEQ 0b110U
|
|
#define ASSERT(x)\
|
|
if ((x) != 1)\
|
|
rollback(0,0,__LINE__)
|
|
#define INVALID_ARGUMENT -7
|
|
#define INVALID_FLOAT -10024
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
int64_t result = 0;
|
|
|
|
// test invalid floats
|
|
{
|
|
ASSERT(float_compare(-1,-2, EQ) == INVALID_FLOAT);
|
|
ASSERT(float_compare( 0,-2, EQ) == INVALID_FLOAT);
|
|
ASSERT(float_compare(-1, 0, EQ) == INVALID_FLOAT);
|
|
}
|
|
|
|
// test invalid flags
|
|
{
|
|
// flag 8 doesnt exist
|
|
ASSERT(float_compare(0,0, 0b1000U) == INVALID_ARGUMENT);
|
|
// flag 16 doesnt exist
|
|
ASSERT(float_compare(0,0, 0b10000U) == INVALID_ARGUMENT);
|
|
// every flag except the valid ones
|
|
ASSERT(float_compare(0,0, ~0b111UL) == INVALID_ARGUMENT);
|
|
// all valid flags combined is invalid too
|
|
ASSERT(float_compare(0,0, 0b111UL) == INVALID_ARGUMENT);
|
|
// no flags is also invalid
|
|
ASSERT(float_compare(0,0, 0) == INVALID_ARGUMENT);
|
|
}
|
|
|
|
// test logic
|
|
{
|
|
ASSERT(float_compare(0,0,EQ));
|
|
ASSERT(float_compare(0, float_one(), LT));
|
|
ASSERT(float_compare(0, float_one(), GT) == 0);
|
|
ASSERT(float_compare(0, float_one(), GTE) == 0);
|
|
ASSERT(float_compare(0, float_one(), LTE));
|
|
ASSERT(float_compare(0, float_one(), NEQ));
|
|
|
|
int64_t large_negative = 1622844335003378560LL; /* -154846915 */
|
|
int64_t small_negative = 1352229899321148800LL; /* -1.15001111e-7 */
|
|
int64_t small_positive = 5713898440837102138LL; /* 3.33411333131321e-21 */
|
|
int64_t large_positive = 7749425685711506120LL; /* 3.234326634253e+92 */
|
|
|
|
// large negative < small negative
|
|
ASSERT(float_compare(large_negative, small_negative, LT));
|
|
ASSERT(float_compare(large_negative, small_negative, LTE));
|
|
ASSERT(float_compare(large_negative, small_negative, NEQ));
|
|
ASSERT(float_compare(large_negative, small_negative, GT) == 0);
|
|
ASSERT(float_compare(large_negative, small_negative, GTE) == 0);
|
|
ASSERT(float_compare(large_negative, small_negative, EQ) == 0);
|
|
|
|
// large_negative < large positive
|
|
ASSERT(float_compare(large_negative, large_positive, LT));
|
|
ASSERT(float_compare(large_negative, large_positive, LTE));
|
|
ASSERT(float_compare(large_negative, large_positive, NEQ));
|
|
ASSERT(float_compare(large_negative, large_positive, GT) == 0);
|
|
ASSERT(float_compare(large_negative, large_positive, GTE) == 0);
|
|
ASSERT(float_compare(large_negative, large_positive, EQ) == 0);
|
|
|
|
// small_negative < small_positive
|
|
ASSERT(float_compare(small_negative, small_positive, LT));
|
|
ASSERT(float_compare(small_negative, small_positive, LTE));
|
|
ASSERT(float_compare(small_negative, small_positive, NEQ));
|
|
ASSERT(float_compare(small_negative, small_positive, GT) == 0);
|
|
ASSERT(float_compare(small_negative, small_positive, GTE) == 0);
|
|
ASSERT(float_compare(small_negative, small_positive, EQ) == 0);
|
|
|
|
// small positive < large positive
|
|
ASSERT(float_compare(small_positive, large_positive, LT));
|
|
ASSERT(float_compare(small_positive, large_positive, LTE));
|
|
ASSERT(float_compare(small_positive, large_positive, NEQ));
|
|
ASSERT(float_compare(small_positive, large_positive, GT) == 0);
|
|
ASSERT(float_compare(small_positive, large_positive, GTE) == 0);
|
|
ASSERT(float_compare(small_positive, large_positive, EQ) == 0);
|
|
|
|
// small negative < 0
|
|
ASSERT(float_compare(small_negative, 0, LT));
|
|
|
|
// large negative < 0
|
|
ASSERT(float_compare(large_negative, 0, LT));
|
|
|
|
// small positive > 0
|
|
ASSERT(float_compare(small_positive, 0, GT));
|
|
|
|
// large positive > 0
|
|
ASSERT(float_compare(large_positive, 0, GT));
|
|
}
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_compare"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_compare"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_divide()
|
|
{
|
|
testcase("Test float_divide");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_divide (int64_t, int64_t);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_FLOAT -10024
|
|
#define DIVISION_BY_ZERO -25
|
|
#define XFL_OVERFLOW -30
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// ensure invalid xfl are not accepted
|
|
ASSERT(float_divide(-1, float_one()) == INVALID_FLOAT);
|
|
|
|
// divide by 0
|
|
ASSERT(float_divide(float_one(), 0) == DIVISION_BY_ZERO);
|
|
ASSERT(float_divide(0, float_one()) == 0);
|
|
|
|
// check 1
|
|
ASSERT(float_divide(float_one(), float_one()) == float_one());
|
|
ASSERT(float_divide(float_one(), float_negate(float_one())) == float_negate(float_one()));
|
|
ASSERT(float_divide(float_negate(float_one()), float_one()) == float_negate(float_one()));
|
|
ASSERT(float_divide(float_negate(float_one()), float_negate(float_one())) == float_one());
|
|
|
|
// 1 / 10 = 0.1
|
|
ASSERT_EQUAL(float_divide(float_one(), 6107881094714392576LL), 6071852297695428608LL);
|
|
|
|
// 123456789 / 1623 = 76067.0295749
|
|
ASSERT_EQUAL(float_divide(6234216452170766464LL, 6144532891733356544LL), 6168530993200328528LL);
|
|
|
|
// -1.245678451111 / 1.3546984132111e+42 = -9.195245517106014e-43
|
|
ASSERT_EQUAL(float_divide(1478426356228633688LL, 6846826132016365020LL), 711756787386903390LL);
|
|
|
|
// 9.134546514878452e-81 / 1
|
|
ASSERT(float_divide(4638834963451748340LL, float_one()) == 4638834963451748340LL);
|
|
|
|
// 9.134546514878452e-81 / 1.41649684651e+75 = (underflow 0)
|
|
ASSERT(float_divide(4638834963451748340LL, 7441363081262569392LL) == 0);
|
|
|
|
// 1.3546984132111e+42 / 9.134546514878452e-81 = XFL_OVERFLOW
|
|
ASSERT(float_divide(6846826132016365020LL, 4638834963451748340LL) == XFL_OVERFLOW);
|
|
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
3121244226425810900LL /* -4.753284285427668e+91 */,
|
|
2135203055881892282LL /* -9.50403176301817e+36 */),
|
|
7066645550312560102LL /* 5.001334595622374e+54 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
2473507938381460320LL /* -5.535342582428512e+55 */,
|
|
6365869885731270068LL /* 6787211884129716 */),
|
|
2187897766692155363LL /* -8.155547044835299e+39 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
1716271542690607496LL /* -49036842898190.16 */,
|
|
3137794549622534856LL /* -3.28920897266964e+92 */),
|
|
4667220053951274769LL /* 1.490839995440913e-79 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
1588045991926420391LL /* -2778923.092005799 */,
|
|
5933338827267685794LL /* 6.601717648113058e-9 */),
|
|
1733591650950017206LL /* -420939403974674.2 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
5880783758174228306LL /* 8.089844083101523e-12 */,
|
|
1396720886139976383LL /* -0.00009612200909863615 */),
|
|
1341481714205255877LL /* -8.416224503589061e-8 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
5567703563029955929LL /* 1.254423600022873e-29 */,
|
|
2184969513100691140LL /* -5.227293453371076e+39 */),
|
|
236586937995245543LL /* -2.399757371979751e-69 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
7333313065548121054LL /* 1.452872188953566e+69 */,
|
|
1755926008837497886LL /* -8529353417745438 */),
|
|
2433647177826281173LL /* -1.703379046213333e+53 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
1172441975040622050LL /* -1.50607192429309e-17 */,
|
|
6692015311011173216LL /* 8.673463993357152e+33 */),
|
|
560182767210134346LL /* -1.736413416192842e-51 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
577964843368607493LL /* -1.504091065184005e-50 */,
|
|
6422931182144699580LL /* 9805312769113276000 */),
|
|
235721135837751035LL /* -1.533955214485243e-69 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
6039815413139899240LL /* 0.0049919124634346 */,
|
|
2117655488444284242LL /* -9.970862834892113e+35 */),
|
|
779625635892827768LL /* -5.006499985102456e-39 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
1353563835098586141LL /* -2.483946887437341e-7 */,
|
|
6450909070545770298LL /* 175440415122002600000 */),
|
|
992207753070525611LL /* -1.415835049016491e-27 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
6382158843584616121LL /* 50617712279937850 */,
|
|
5373794957212741595LL /* 5.504201387110363e-40 */),
|
|
7088854809772330055LL /* 9.196195545910343e+55 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
2056891719200540975LL /* -3.250289119594799e+32 */,
|
|
1754532627802542730LL /* -7135972382790282 */),
|
|
6381651867337939070LL /* 45547949813167340 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
5730152450208688630LL /* 1.573724193417718e-20 */,
|
|
1663581695074866883LL /* -62570322025.24355 */),
|
|
921249452789827075LL /* -2.515128806245891e-31 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
6234301156018475310LL /* 131927173.7708846 */,
|
|
2868710604383082256LL /* -4.4212413754468e+77 */),
|
|
219156721749007916LL /* -2.983939635224108e-70 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
2691125731495874243LL /* -6.980353583058627e+67 */,
|
|
7394070851520237320LL /* 8.16746263262388e+72 */),
|
|
1377640825464715759LL /* -0.000008546538744084975 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
5141867696142208039LL /* 7.764120939842599e-53 */,
|
|
5369434678231981897LL /* 1.143922406350665e-40 */),
|
|
5861466794943198400LL /* 6.7872793615536e-13 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
638296190872832492LL /* -7.792243040963052e-47 */,
|
|
5161669734904371378LL /* 9.551761192523954e-52 */),
|
|
1557396184145861422LL /* -81579.12330410798 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
2000727145906286285LL /* -1.128911353786061e+29 */,
|
|
2096625200460673392LL /* -6.954973360763248e+34 */),
|
|
5982403476503576795LL /* 0.000001623171355558107 */);
|
|
ASSERT_EQUAL(
|
|
float_divide(
|
|
640472838055334326LL /* -9.968890223464885e-47 */,
|
|
5189754252349396763LL /* 1.607481618585371e-50 */),
|
|
1537425431139169736LL /* -6201.557833201096 */);
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_divide"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_divide"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_int()
|
|
{
|
|
testcase("Test float_int");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_int (int64_t, uint32_t, uint32_t);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_FLOAT -10024
|
|
#define INVALID_ARGUMENT -7
|
|
#define CANT_RETURN_NEGATIVE -33
|
|
#define TOO_BIG -3
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// ensure invalid xfl are not accepted
|
|
ASSERT(float_int(-1,0,0) == INVALID_FLOAT);
|
|
|
|
// check 1
|
|
ASSERT(float_int(float_one(), 0, 0) == 1LL);
|
|
|
|
// check 1.23e-20 always returns 0 (too small to display)
|
|
ASSERT(float_int(5729808726015270912LL,0,0) == 0);
|
|
ASSERT(float_int(5729808726015270912LL,15,0) == 0);
|
|
ASSERT(float_int(5729808726015270912LL,16,0) == INVALID_ARGUMENT);
|
|
|
|
|
|
ASSERT(float_int(float_one(), 15, 0) == 1000000000000000LL);
|
|
ASSERT(float_int(float_one(), 14, 0) == 100000000000000LL);
|
|
ASSERT(float_int(float_one(), 13, 0) == 10000000000000LL);
|
|
ASSERT(float_int(float_one(), 12, 0) == 1000000000000LL);
|
|
ASSERT(float_int(float_one(), 11, 0) == 100000000000LL);
|
|
ASSERT(float_int(float_one(), 10, 0) == 10000000000LL);
|
|
ASSERT(float_int(float_one(), 9, 0) == 1000000000LL);
|
|
ASSERT(float_int(float_one(), 8, 0) == 100000000LL);
|
|
ASSERT(float_int(float_one(), 7, 0) == 10000000LL);
|
|
ASSERT(float_int(float_one(), 6, 0) == 1000000LL);
|
|
ASSERT(float_int(float_one(), 5, 0) == 100000LL);
|
|
ASSERT(float_int(float_one(), 4, 0) == 10000LL);
|
|
ASSERT(float_int(float_one(), 3, 0) == 1000LL);
|
|
ASSERT(float_int(float_one(), 2, 0) == 100LL);
|
|
ASSERT(float_int(float_one(), 1, 0) == 10LL);
|
|
ASSERT(float_int(float_one(), 0, 0) == 1LL);
|
|
|
|
// normal upper limit on exponent
|
|
ASSERT(float_int(6360317241828374919LL, 0, 0) == 1234567981234567LL);
|
|
|
|
// ask for one decimal above limit
|
|
ASSERT(float_int(6360317241828374919LL, 1, 0) == TOO_BIG);
|
|
|
|
// ask for 15 decimals above limit
|
|
ASSERT(float_int(6360317241828374919LL, 15, 0) == TOO_BIG);
|
|
|
|
// every combination for 1.234567981234567
|
|
ASSERT(float_int(6090101264186145159LL, 0, 0) == 1LL);
|
|
ASSERT(float_int(6090101264186145159LL, 1, 0) == 12LL);
|
|
ASSERT(float_int(6090101264186145159LL, 2, 0) == 123LL);
|
|
ASSERT(float_int(6090101264186145159LL, 3, 0) == 1234LL);
|
|
ASSERT(float_int(6090101264186145159LL, 4, 0) == 12345LL);
|
|
ASSERT(float_int(6090101264186145159LL, 5, 0) == 123456LL);
|
|
ASSERT(float_int(6090101264186145159LL, 6, 0) == 1234567LL);
|
|
ASSERT(float_int(6090101264186145159LL, 7, 0) == 12345679LL);
|
|
ASSERT(float_int(6090101264186145159LL, 8, 0) == 123456798LL);
|
|
ASSERT(float_int(6090101264186145159LL, 9, 0) == 1234567981LL);
|
|
ASSERT(float_int(6090101264186145159LL, 10, 0) == 12345679812LL);
|
|
ASSERT(float_int(6090101264186145159LL, 11, 0) == 123456798123LL);
|
|
ASSERT(float_int(6090101264186145159LL, 12, 0) == 1234567981234LL);
|
|
ASSERT(float_int(6090101264186145159LL, 13, 0) == 12345679812345LL);
|
|
ASSERT(float_int(6090101264186145159LL, 14, 0) == 123456798123456LL);
|
|
ASSERT(float_int(6090101264186145159LL, 15, 0) == 1234567981234567LL);
|
|
|
|
// same with absolute parameter
|
|
ASSERT(float_int(1478415245758757255LL, 0, 1) == 1LL);
|
|
ASSERT(float_int(1478415245758757255LL, 1, 1) == 12LL);
|
|
ASSERT(float_int(1478415245758757255LL, 2, 1) == 123LL);
|
|
ASSERT(float_int(1478415245758757255LL, 3, 1) == 1234LL);
|
|
ASSERT(float_int(1478415245758757255LL, 4, 1) == 12345LL);
|
|
ASSERT(float_int(1478415245758757255LL, 5, 1) == 123456LL);
|
|
ASSERT(float_int(1478415245758757255LL, 6, 1) == 1234567LL);
|
|
ASSERT(float_int(1478415245758757255LL, 7, 1) == 12345679LL);
|
|
ASSERT(float_int(1478415245758757255LL, 8, 1) == 123456798LL);
|
|
ASSERT(float_int(1478415245758757255LL, 9, 1) == 1234567981LL);
|
|
ASSERT(float_int(1478415245758757255LL, 10, 1) == 12345679812LL);
|
|
ASSERT(float_int(1478415245758757255LL, 11, 1) == 123456798123LL);
|
|
ASSERT(float_int(1478415245758757255LL, 12, 1) == 1234567981234LL);
|
|
ASSERT(float_int(1478415245758757255LL, 13, 1) == 12345679812345LL);
|
|
ASSERT(float_int(1478415245758757255LL, 14, 1) == 123456798123456LL);
|
|
ASSERT(float_int(1478415245758757255LL, 15, 1) == 1234567981234567LL);
|
|
|
|
// neg xfl sans absolute parameter
|
|
ASSERT(float_int(1478415245758757255LL, 15, 0) == CANT_RETURN_NEGATIVE);
|
|
|
|
// 1.234567981234567e-16
|
|
ASSERT(float_int(5819885286543915399LL, 15, 0) == 1LL);
|
|
for (uint32_t i = 1; GUARD(15), i < 15; ++i)
|
|
ASSERT(float_int(5819885286543915399LL, i, 0) == 0);
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_int"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_int"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_invert()
|
|
{
|
|
testcase("Test float_invert");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_invert (int64_t);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_FLOAT -10024
|
|
#define DIVISION_BY_ZERO -25
|
|
#define TOO_BIG -3
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// divide by 0
|
|
ASSERT(float_invert(0) == DIVISION_BY_ZERO);
|
|
|
|
// ensure invalid xfl are not accepted
|
|
ASSERT(float_invert(-1) == INVALID_FLOAT);
|
|
|
|
// check 1
|
|
ASSERT(float_invert(float_one()) == float_one());
|
|
|
|
// 1 / 10 = 0.1
|
|
ASSERT_EQUAL(float_invert(6107881094714392576LL), 6071852297695428608LL);
|
|
|
|
// 1 / 123 = 0.008130081300813009
|
|
ASSERT_EQUAL(float_invert(6126125493223874560LL), 6042953581977277649LL);
|
|
|
|
// 1 / 1234567899999999 = 8.100000008100007e-16
|
|
ASSERT_EQUAL(float_invert(6360317241747140351LL), 5808736320061298855LL);
|
|
|
|
// 1/ 1*10^-81 = 10**81
|
|
ASSERT_EQUAL(float_invert(4630700416936869888LL), 7540018576963469311LL);
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_invert"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_invert"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_log()
|
|
{
|
|
testcase("Test float_log");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_log (int64_t float1);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_ARGUMENT -7
|
|
#define COMPLEX_NOT_SUPPORTED -39
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// check 0 is not allowed
|
|
if (float_log(0) != INVALID_ARGUMENT)
|
|
rollback(0,0,__LINE__);
|
|
|
|
// log10( 846513684968451 ) = 14.92763398342338
|
|
ASSERT_EQUAL(float_log(6349533412187342878LL), 6108373858112734914LL);
|
|
|
|
// log10 ( -1000 ) = invalid (complex not supported)
|
|
if (float_log(1532223873305968640LL) != COMPLEX_NOT_SUPPORTED)
|
|
rollback(0,0,__LINE__);
|
|
|
|
// log10 (1000) == 3
|
|
ASSERT_EQUAL(float_log(6143909891733356544LL), 6091866696204910592LL);
|
|
|
|
// log10 (0.112381) == -0.949307107740766
|
|
ASSERT_EQUAL(float_log(6071976107695428608LL), 1468659350345448364LL);
|
|
|
|
// log10 (0.00000000000000001123) = -16.94962024373854221
|
|
ASSERT_EQUAL(float_log(5783744921543716864LL), 1496890038311378526LL);
|
|
|
|
// log10 (100000000000000000000000000000000000000000000000000000000000000) = 62
|
|
ASSERT_EQUAL(float_log(7206759403792793600LL), 6113081094714392576LL);
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_log"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_log"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_mantissa()
|
|
{
|
|
testcase("Test float_mantissa");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
extern int64_t float_mantissa(int64_t);
|
|
extern int64_t float_negate(int64_t);
|
|
#define ASSERT_EQUAL(x,y)\
|
|
if ((x) != (y))\
|
|
rollback(0,0,__LINE__);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
#define INVALID_FLOAT -10024
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
int64_t result = 0;
|
|
|
|
// test invalid floats
|
|
{
|
|
ASSERT(float_mantissa(-1) == INVALID_FLOAT);
|
|
ASSERT(float_mantissa(-11010191919LL) == INVALID_FLOAT);
|
|
}
|
|
|
|
// test canonical zero
|
|
ASSERT(float_mantissa(0) == 0);
|
|
|
|
// test one, negative one
|
|
{
|
|
ASSERT(float_mantissa(float_one()) == 1000000000000000LL);
|
|
ASSERT(float_mantissa(float_negate(float_one())) == 1000000000000000LL);
|
|
}
|
|
|
|
// test random numbers
|
|
{
|
|
ASSERT_EQUAL(
|
|
float_mantissa(4763370308433150973LL /* 7.569101929907197e-74 */),
|
|
7569101929907197LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(668909658849475214LL /* -2.376913998641806e-45 */),
|
|
2376913998641806LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(962271544155031248LL /* -7.508423152486096e-29 */),
|
|
7508423152486096LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(7335644976228470276LL /* 3.784782869302788e+69 */),
|
|
3784782869302788LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(2837780149340315954LL /* -9.519583351644467e+75 */),
|
|
9519583351644466LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(2614004940018599738LL /* -1.917156143712058e+63 */),
|
|
1917156143712058LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(4812250541755005603LL /* 2.406139723315875e-71 */),
|
|
2406139723315875LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(5140304866732560580LL /* 6.20129153019514e-53 */),
|
|
6201291530195140LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(1124677839589482624LL /* -7.785132001599617e-20 */),
|
|
7785132001599616LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(5269336076015865585LL /* 9.131711247126257e-46 */),
|
|
9131711247126257LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(2296179634826760368LL /* -8.3510241225484e+45 */),
|
|
8351024122548400LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(1104028240398536470LL /* -5.149931320135446e-21 */),
|
|
5149931320135446LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(2691222059222981864LL /* -7.076681310166248e+67 */),
|
|
7076681310166248LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(6113256168823855946LL /* 63.7507410946337 */),
|
|
6375074109463370LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(311682216630003626LL /* -5.437441968809898e-65 */),
|
|
5437441968809898LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(794955605753965262LL /* -2.322071336757966e-38 */),
|
|
2322071336757966LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(204540636400815950LL /* -6.382252796514126e-71 */),
|
|
6382252796514126LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(5497195278343034975LL /* 2.803732951029855e-33 */),
|
|
2803732951029855LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(1450265914369875626LL /* -0.09114033611316906 */),
|
|
9114033611316906LL);
|
|
ASSERT_EQUAL(
|
|
float_mantissa(7481064015089962668LL /* 5.088633654939308e+77 */),
|
|
5088633654939308LL);
|
|
}
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_mantissa"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_mantissa"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_mulratio()
|
|
{
|
|
testcase("Test float_mulratio");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_mulratio (int64_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_FLOAT -10024
|
|
#define DIVISION_BY_ZERO -25
|
|
#define XFL_OVERFLOW -30
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// ensure invalid xfl are not accepted
|
|
ASSERT(float_mulratio(-1, 0, 1, 1) == INVALID_FLOAT);
|
|
|
|
// multiply by 0
|
|
ASSERT(float_mulratio(float_one(), 0, 0, 1) == 0);
|
|
ASSERT(float_mulratio(0, 0, 1, 1) == 0);
|
|
|
|
// check 1
|
|
ASSERT(float_mulratio(float_one(), 0, 1, 1) == float_one());
|
|
ASSERT(float_mulratio(float_negate(float_one()), 0, 1, 1) ==
|
|
float_negate(float_one()));
|
|
|
|
// check overflow
|
|
// 1e+95 * 1e+95
|
|
ASSERT(float_mulratio(7801234554605699072LL, 0, 0xFFFFFFFFUL, 1) == XFL_OVERFLOW);
|
|
// 1e+95 * 10
|
|
ASSERT(float_mulratio(7801234554605699072LL, 0, 10, 1) == XFL_OVERFLOW);
|
|
// -1e+95 * 10
|
|
ASSERT(float_mulratio(3189548536178311168LL, 0, 10, 1) == XFL_OVERFLOW);
|
|
|
|
// identity
|
|
ASSERT_EQUAL(float_mulratio(3189548536178311168LL, 0, 1, 1), 3189548536178311168LL);
|
|
|
|
|
|
// random mulratios
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2296131684119423544LL, 0U, 2210828011U, 2814367554U),
|
|
2294351094683836182LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(565488225163275031LL, 0U, 2373474507U, 4203973264U),
|
|
562422045628095449LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2292703263479286183LL, 0U, 3170020147U, 773892643U),
|
|
2307839765178024100LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(758435948837102675LL, 0U, 3802740780U, 1954123588U),
|
|
760168290112163547LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(3063742137774439410LL, 0U, 2888815591U, 4122448592U),
|
|
3053503824756415637LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(974014561126802184LL, 0U, 689168634U, 3222648522U),
|
|
957408554638995792LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2978333847445611553LL, 0U, 1718558513U, 2767410870U),
|
|
2976075722223325259LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(6577058837932757648LL, 0U, 1423256719U, 1338068927U),
|
|
6577173649752398013LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2668681541248816636LL, 0U, 345215754U, 4259223936U),
|
|
2650183845127530219LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(651803640367065917LL, 0U, 327563234U, 1191613855U),
|
|
639534906402789368LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(3154958130393015979LL, 0U, 1304112625U, 3024066701U),
|
|
3153571282364880740LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(1713286099776800976LL, 0U, 1902151138U, 2927030061U),
|
|
1712614441093927706LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2333142120591277120LL, 0U, 914099656U, 108514965U),
|
|
2349692988167140475LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(995968561418010814LL, 0U, 1334462574U, 846156977U),
|
|
998955931389416094LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(6276035843030312442LL, 0U, 2660687613U, 236740983U),
|
|
6294920527635363073LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(7333118474702086419LL, 0U, 46947714U, 2479204760U),
|
|
7298214153648998535LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(2873297486994296492LL, 0U, 880591893U, 436034100U),
|
|
2884122995598532757LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(1935815261812737573LL, 0U, 3123665800U, 3786746543U),
|
|
1934366328810191207LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(7249556282125616118LL, 0U, 2378803159U, 2248850590U),
|
|
7250005170160875417LL);
|
|
ASSERT_EQUAL(
|
|
float_mulratio(311005347529659996LL, 0U, 992915590U, 2433548552U),
|
|
308187142737041830LL);
|
|
|
|
// today: round up test
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_mulratio"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_mulratio"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_multiply()
|
|
{
|
|
testcase("Test float_multiply");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_multiply (int64_t, int64_t);
|
|
extern int64_t float_one (void);
|
|
#define INVALID_FLOAT -10024
|
|
#define DIVISION_BY_ZERO -25
|
|
#define XFL_OVERFLOW -30
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback(0,0,__LINE__);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// ensure invalid xfl are not accepted
|
|
ASSERT(float_multiply(-1, float_one()) == INVALID_FLOAT);
|
|
|
|
// multiply by 0
|
|
ASSERT(float_multiply(float_one(), 0) == 0);
|
|
ASSERT(float_multiply(0, float_one()) == 0);
|
|
|
|
// check 1
|
|
ASSERT(float_multiply(float_one(), float_one()) == float_one());
|
|
ASSERT(float_multiply(float_one(), float_negate(float_one())) == float_negate(float_one()));
|
|
ASSERT(float_multiply(float_negate(float_one()), float_one()) == float_negate(float_one()));
|
|
ASSERT(float_multiply(float_negate(float_one()), float_negate(float_one())) == float_one());
|
|
|
|
// check overflow
|
|
// 1e+95 * 1e+95
|
|
ASSERT(float_multiply(7801234554605699072LL, 7801234554605699072LL) == XFL_OVERFLOW);
|
|
// 1e+95 * 10
|
|
ASSERT(float_multiply(7801234554605699072LL, 6107881094714392576LL) == XFL_OVERFLOW);
|
|
ASSERT(float_multiply(6107881094714392576LL, 7801234554605699072LL) == XFL_OVERFLOW);
|
|
// -1e+95 * 10
|
|
ASSERT(float_multiply(3189548536178311168LL, 6107881094714392576LL) == XFL_OVERFLOW);
|
|
|
|
// identity
|
|
ASSERT_EQUAL(float_multiply(3189548536178311168LL, float_one()), 3189548536178311168LL);
|
|
ASSERT_EQUAL(float_multiply(float_one(), 3189548536178311168LL), 3189548536178311168LL);
|
|
|
|
|
|
// random multiplications
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
7791757438262485039LL /* 9.537282166267951e+94 */,
|
|
4759088999670263908LL /* 3.287793167020132e-74 */),
|
|
6470304726017852129LL /* 3.135661113819873e+21 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
7534790022873909775LL /* 4.771445910440463e+80 */,
|
|
1017891960669847079LL /* -9.085644138855975e-26 */),
|
|
2472307761756037979LL /* -4.335165957006171e+55 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2813999069907898454LL /* -3.75290242870895e+74 */,
|
|
4962524721184225460LL /* 8.56513107667986e-63 */),
|
|
1696567870013294731LL /* -3214410121988.235 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2151742066453140308LL /* -8.028643824784212e+37 */,
|
|
437647738130579252LL /* -5.302173903011636e-58 */),
|
|
5732835652591705549LL /* 4.256926576434637e-20 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5445302332922546340LL /* 4.953983058987172e-36 */,
|
|
7770966530708354172LL /* 6.760773121619068e+93 */),
|
|
7137051085305881332LL /* 3.349275551015668e+58 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2542989542826132533LL /* -2.959352989172789e+59 */,
|
|
6308418769944702613LL /* 3379291626008.213 */),
|
|
2775217422137696934LL /* -1.000051677471398e+72 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5017652318929433511LL /* 9.649533293441959e-60 */,
|
|
6601401767766764916LL /* 8.131913296358772e+28 */),
|
|
5538267259220228820LL /* 7.846916809259732e-31 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
892430323307269235LL /* -9.724796342652019e-33 */,
|
|
1444078017997143500LL /* -0.0292613723858478 */),
|
|
5479222755754111850LL /* 2.845608871588714e-34 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
7030632722283214253LL /* 5.017303585240493e+52 */,
|
|
297400838197636668LL /* -9.170462045924924e-66 */),
|
|
1247594596364389994LL /* -4.601099210133098e-13 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1321751204165279730LL /* -6.700112973094898e-9 */,
|
|
2451801790748530375LL /* -1.843593458980551e+54 */),
|
|
6918764256086244704LL /* 1.235228445162848e+46 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2055496484261758590LL /* -1.855054180812414e+32 */,
|
|
2079877890137711361LL /* -8.222061547283201e+33 */),
|
|
7279342234795540005LL /* 1.525236964818469e+66 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2439875962311968674LL /* -7.932163531900834e+53 */,
|
|
4707485682591872793LL /* 5.727671617074969e-77 */),
|
|
1067392794851803610LL /* -4.543282792366554e-23 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6348574818322812800LL /* 750654298515443.2 */,
|
|
6474046245013515838LL /* 6.877180109483582e+21 */),
|
|
6742547427357110773LL /* 5.162384810848757e+36 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1156137305783593424LL /* -3.215801176746448e-18 */,
|
|
351790564990861307LL /* -9.516993310703611e-63 */),
|
|
4650775291275116747LL /* 3.060475828764875e-80 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5786888485280994123LL /* 4.266563737277259e-17 */,
|
|
6252137323085080394LL /* 1141040294.831946 */),
|
|
5949619829273756852LL /* 4.868321144702132e-8 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2078182880999439640LL /* -6.52705240901148e+33 */,
|
|
1662438186251269392LL /* -51135233789.26864 */),
|
|
6884837854131013998LL /* 3.33762350889611e+44 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1823781083140711248LL /* -43268336830308640000 */,
|
|
1120252241608199010LL /* -3.359534020316002e-20 */),
|
|
6090320310700749729LL /* 1.453614495839137 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6617782604883935174LL /* 6.498351904047046e+29 */,
|
|
6185835042802056262LL /* 689635.404973575 */),
|
|
6723852137583788319LL /* 4.481493547008287e+35 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
333952667495151166LL /* -9.693494324475454e-64 */,
|
|
1556040883317758614LL /* -68026.1150230799 */),
|
|
5032611291744396930LL /* 6.594107598923394e-59 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2326968399632616779LL /* -3.110991909440843e+47 */,
|
|
707513695207834635LL /* -4.952153338037259e-43 */),
|
|
6180479299649214949LL /* 154061.0896894437 */);
|
|
|
|
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1271003508324696477LL /* -9.995612660957597e-12 */,
|
|
5321949753651889765LL /* 7.702193354704484e-43 */),
|
|
512101972406838314LL /* -7.698814141342762e-54 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1928646740923345323LL /* -1.106100408773035e+25 */,
|
|
4639329980209973352LL /* 9.629563273103463e-81 */),
|
|
487453886143282122LL /* -1.065126387268554e-55 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6023906813956669432LL /* 0.0007097711789686777 */,
|
|
944348444470060009LL /* -7.599721976996842e-30 */),
|
|
888099590592064434LL /* -5.394063627447218e-33 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6580290597764062787LL /* 5.035141803138627e+27 */,
|
|
6164319297265300034LL /* 33950.07022461506 */),
|
|
6667036882686408593LL /* 1.709434178074513e+32 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2523439530503240484LL /* -1.423739175762724e+58 */,
|
|
5864448766677980801LL /* 9.769251096336e-13 */),
|
|
2307233895764065602LL /* -1.39088655037165e+46 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6760707453987140465LL /* 5.308012931396465e+37 */,
|
|
5951641080643457645LL /* 6.889572514402925e-8 */),
|
|
6632955645489194550LL /* 3.656993999824438e+30 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6494270716308443375LL /* 9.087252894929135e+22 */,
|
|
564752637895553836LL /* -6.306284101612332e-51 */),
|
|
978508199357889360LL /* -5.730679845862224e-28 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6759145618427534062LL /* 3.746177371790062e+37 */,
|
|
4721897842483633304LL /* 2.125432999353496e-76 */),
|
|
5394267403342547165LL /* 7.962249007433949e-39 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1232673571201806425LL /* -7.694472557031513e-14 */,
|
|
6884256144221925318LL /* 2.75591359980743e+44 */),
|
|
2037747561727791012LL /* -2.12053015632682e+31 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1427694775835421031LL /* -0.004557293586344295 */,
|
|
4883952867277976402LL /* 2.050871208358738e-67 */),
|
|
225519204318055258LL /* -9.34642220427145e-70 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5843509949864662087LL /* 6.84483279249927e-14 */,
|
|
5264483986612843822LL /* 4.279621844104494e-46 */),
|
|
5028946513739275800LL /* 2.929329593802264e-59 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
6038444022009738988LL /* 0.003620521333274348 */,
|
|
7447499078040748850LL /* 7.552493624689458e+75 */),
|
|
7406652183825856093LL /* 2.734396428760669e+73 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
939565473697468970LL /* -2.816751204405802e-30 */,
|
|
1100284903077087966LL /* -1.406593998686942e-21 */),
|
|
5174094397561240825LL /* 3.962025339911417e-51 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5694071830210473617LL /* 1.521901214166673e-22 */,
|
|
5536709154363579683LL /* 6.288811952610595e-31 */),
|
|
5143674525748709391LL /* 9.570950546343951e-53 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
600729862341871819LL /* -6.254711528966347e-49 */,
|
|
6330630279715378440LL /* 75764028872020.56 */),
|
|
851415551394320910LL /* -4.738821448667662e-35 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1876763139233864902LL /* -3.265694247738566e+22 */,
|
|
4849561230315278754LL /* 3.688031264625058e-69 */),
|
|
649722744589988028LL /* -1.204398248636604e-46 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
3011947542126279863LL /* -3.542991042788535e+85 */,
|
|
1557732559110376235LL /* -84942.87294925611 */),
|
|
7713172080438368541LL /* 3.009518380079389e+90 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
5391579936313268788LL /* 5.274781978155572e-39 */,
|
|
1018647290024655822LL /* -9.840973493664718e-26 */),
|
|
329450072133864644LL /* -5.190898963188932e-64 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
2815029221608845312LL /* -4.783054129655808e+74 */,
|
|
4943518985822088837LL /* 7.57379422402522e-64 */),
|
|
1678961648155863225LL /* -362258677403.8713 */);
|
|
ASSERT_EQUAL(
|
|
float_multiply(
|
|
1377509900308195934LL /* -0.00000841561358756515 */,
|
|
7702104197062186199LL /* 9.95603351337903e+89 */),
|
|
2998768765665354000LL /* -8.378613091344656e+84 */);
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_multiply"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_multiply"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_negate()
|
|
{
|
|
testcase("Test float_negate");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
extern int64_t float_negate(int64_t);
|
|
#define ASSERT(x)\
|
|
if ((x) != 1)\
|
|
rollback(0,0,__LINE__)
|
|
#define INVALID_FLOAT -10024
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
int64_t result = 0;
|
|
|
|
// test invalid floats
|
|
{
|
|
ASSERT(float_negate(-1) == INVALID_FLOAT);
|
|
ASSERT(float_negate(-11010191919LL) == INVALID_FLOAT);
|
|
}
|
|
|
|
// test canonical zero
|
|
ASSERT(float_negate(0) == 0);
|
|
|
|
// test double negation
|
|
{
|
|
ASSERT(float_negate(float_one()) != float_one());
|
|
ASSERT(float_negate(float_negate(float_one())) == float_one());
|
|
}
|
|
|
|
// test random numbers
|
|
{
|
|
// +/- 3.463476342523e+22
|
|
ASSERT(float_negate(6488646939756037240LL) == 1876960921328649336LL);
|
|
|
|
ASSERT(float_negate(float_one()) == 1478180677777522688LL);
|
|
|
|
ASSERT(float_negate(1838620299498162368LL) == 6450306317925550272LL);
|
|
}
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_negate"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_negate"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_one()
|
|
{
|
|
testcase("Test float_one");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
int64_t f = float_one();
|
|
return
|
|
f == 6089866696204910592ULL
|
|
? accept(0,0,2)
|
|
: rollback(0,0,1);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_one"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_one"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_root()
|
|
{
|
|
testcase("Test float_root");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_root (int64_t float1, uint32_t n);
|
|
extern int64_t float_one (void);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT_EQUAL(float_root(float_one(), 2), float_one());
|
|
|
|
// sqrt 9 is 3
|
|
ASSERT_EQUAL(float_root(6097866696204910592LL, 2), 6091866696204910592LL);
|
|
|
|
// cube root of 1000 is 10
|
|
ASSERT_EQUAL(float_root(6143909891733356544LL, 3), 6107881094714392576LL);
|
|
|
|
// sqrt of negative is "complex not supported error"
|
|
if (float_root(1478180677777522688LL, 2) != -39)
|
|
rollback(0,0,__LINE__);
|
|
|
|
// tenth root of 0 is 0
|
|
if (float_root(0, 10) != 0)
|
|
rollback(0,0,__LINE__);
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_root"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_root"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_set()
|
|
{
|
|
testcase("Test float_set");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t float_set (int32_t, int64_t);
|
|
#define INVALID_FLOAT -10024
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
// zero mantissa should return canonical zero
|
|
ASSERT(float_set(-5, 0) == 0);
|
|
ASSERT(float_set(50, 0) == 0);
|
|
ASSERT(float_set(-50, 0) == 0);
|
|
ASSERT(float_set(0, 0) == 0);
|
|
|
|
// an exponent lower than -96 should produce an invalid float error
|
|
ASSERT(float_set(-97, 1) == INVALID_FLOAT);
|
|
|
|
// an exponent larger than +96 should produce an invalid float error
|
|
ASSERT(float_set(+97, 1) == INVALID_FLOAT);
|
|
|
|
ASSERT(float_set(-5,6541432897943971LL) == 6275552114197674403LL);
|
|
ASSERT(float_set(-83,7906202688397446LL) == 4871793800248533126LL);
|
|
ASSERT(float_set(76,4760131426754533LL) == 7732937091994525669LL);
|
|
ASSERT(float_set(37,-8019384286534438LL) == 2421948784557120294LL);
|
|
ASSERT(float_set(50,5145342538007840LL) == 7264947941859247392LL);
|
|
ASSERT(float_set(-70,4387341302202416LL) == 5102462119485603888LL);
|
|
ASSERT(float_set(-26,-1754544005819476LL) == 1280776838179040340LL);
|
|
ASSERT(float_set(36,8261761545780560LL) == 7015862781734272336LL);
|
|
ASSERT(float_set(35,7975622850695472LL) == 6997562244529705264LL);
|
|
ASSERT(float_set(17,-4478222822793996LL) == 2058119652903740172LL);
|
|
ASSERT(float_set(-53,5506604247857835LL) == 5409826157092453035LL);
|
|
ASSERT(float_set(-60,5120164869507050LL) == 5283338928147728362LL);
|
|
ASSERT(float_set(41,5176113875683063LL) == 7102849126611584759LL);
|
|
ASSERT(float_set(-54,-3477931844992923LL) == 778097067752718235LL);
|
|
ASSERT(float_set(21,6345031894305479LL) == 6743730074440567495LL);
|
|
ASSERT(float_set(-23,5091583691147091LL) == 5949843091820201811LL);
|
|
ASSERT(float_set(-33,7509684078851678LL) == 5772117207113086558LL);
|
|
ASSERT(float_set(-72,-1847771838890268LL) == 452207734575939868LL);
|
|
ASSERT(float_set(71,-9138413713437220LL) == 3035557363306410532LL);
|
|
ASSERT(float_set(28,4933894067102586LL) == 6868419726179738490LL);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_set"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_set"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_sign()
|
|
{
|
|
testcase("Test float_sign");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
extern int64_t float_sign(int64_t);
|
|
extern int64_t float_negate(int64_t);
|
|
#define ASSERT(x)\
|
|
if ((x) != 1)\
|
|
rollback(0,0,__LINE__)
|
|
#define ASSERT_EQUAL(x,y) ASSERT((x) == (y))
|
|
#define INVALID_FLOAT -10024
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
int64_t result = 0;
|
|
|
|
// test invalid floats
|
|
{
|
|
ASSERT(float_sign(-1) == INVALID_FLOAT);
|
|
ASSERT(float_sign(-11010191919LL) == INVALID_FLOAT);
|
|
}
|
|
|
|
// test canonical zero
|
|
ASSERT(float_sign(0) == 0);
|
|
|
|
// test one
|
|
ASSERT(float_sign(float_one()) == 0);
|
|
ASSERT(float_sign(float_negate(float_one())) == 1);
|
|
|
|
// test random numbers
|
|
ASSERT_EQUAL(
|
|
float_sign(7248434512952957686LL /* 6.646312141200119e+64 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(889927818394811978LL /* -7.222291430194763e-33 */),
|
|
1LL);
|
|
ASSERT_EQUAL(float_sign(5945816149233111421LL /* 1.064641104056701e-8 */), 0LL);
|
|
ASSERT_EQUAL(float_sign(6239200145838704863LL /* 621826155.7938399 */), 0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(6992780785042190360LL /* 3.194163363180568e+50 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(6883099933108789087LL /* 1.599702486671199e+44 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(890203738162163464LL /* -7.498211197546248e-33 */),
|
|
1LL);
|
|
ASSERT_EQUAL(float_sign(4884803073052080964LL /* 2.9010769824633e-67 */), 0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(2688292350356944394LL /* -4.146972444128778e+67 */),
|
|
1LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(4830109852288093280LL /* 2.251051746921568e-70 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(294175951907940320LL /* -5.945575756228576e-66 */),
|
|
1LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(7612037404955382316LL /* 9.961233953985069e+84 */),
|
|
0LL);
|
|
ASSERT_EQUAL(float_sign(7520840929603658997LL /* 8.83675114967167e+79 */), 0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(4798982086157926282LL /* 7.152082635718538e-72 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(689790136568817905LL /* -5.242993208502513e-44 */),
|
|
1LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(5521738045011558042LL /* 9.332101110070938e-32 */),
|
|
0LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(728760820583452906LL /* -8.184880204173546e-42 */),
|
|
1LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(2272937984362856794LL /* -3.12377216812681e+44 */),
|
|
1LL);
|
|
ASSERT_EQUAL(float_sign(1445723661896317830LL /* -0.0457178113775911 */), 1LL);
|
|
ASSERT_EQUAL(
|
|
float_sign(5035721527359772724LL /* 9.704343214299189e-59 */),
|
|
0LL);
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_sign"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_sign"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_sto()
|
|
{
|
|
testcase("Test float_sto");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 hook_account (uint32_t, uint32_t);
|
|
extern int64_t float_sto (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t cread_ptr,
|
|
uint32_t cread_len,
|
|
uint32_t iread_ptr,
|
|
uint32_t iread_len,
|
|
int64_t float1,
|
|
uint32_t field_code
|
|
);
|
|
extern int64_t float_sto_set (
|
|
uint32_t read_ptr,
|
|
uint32_t read_len
|
|
);
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define INVALID_FLOAT (-10024)
|
|
#define INVALID_ARGUMENT (-7)
|
|
#define TOO_SMALL (-4)
|
|
#define XFL_OVERFLOW (-30)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define SBUF(x) x, sizeof(x)
|
|
#define sfAmount ((6U << 16U) + 1U)
|
|
#define sfDeliveredAmount ((6U << 16U) + 18U)
|
|
uint8_t cur1[3] = {'U','S','D'};
|
|
uint8_t cur1full[20] = {
|
|
0,0,0,0,0,0,0,0,0,0,0,0,
|
|
'U', 'S', 'D',
|
|
0,0,0,0,0
|
|
};
|
|
|
|
#define BUFFER_EQUAL_20(buf1, buf2)\
|
|
(\
|
|
*(((uint64_t*)(buf1)) + 0)) == *(((uint64_t*)(buf2)) + 0) &&\
|
|
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
|
|
*(((uint32_t*)(buf1)) + 4) == *(((uint32_t*)(buf2)) + 4)
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
int64_t y;
|
|
uint8_t cur2[20];
|
|
for (int i =0; GUARD(20), i < 20; ++i)
|
|
cur2[i] = i;
|
|
|
|
uint8_t iss[20];
|
|
ASSERT(hook_account(SBUF(iss)) == 20);
|
|
|
|
|
|
uint8_t buf[50];
|
|
|
|
// the three buffers must be bounds checked
|
|
ASSERT((y=float_sto(1000000, 50, 0,0,0,0,0,0)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto(0, 1000000, 0,0,0,0,0,0)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto(SBUF(buf), 1000000, 20, 1,20,0,0)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto(SBUF(buf), 1, 1000000, 1,20,0,0)) == INVALID_ARGUMENT);
|
|
ASSERT((y=float_sto(SBUF(buf), 1,20, 1000000, 20, 0,0)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto(SBUF(buf), 1,20, 1, 1000000, 0,0)) == INVALID_ARGUMENT);
|
|
|
|
// zero issuer/currency pointers must be accompanied by 0 length
|
|
ASSERT((y=float_sto(SBUF(buf), 0, 1, 0,0, 0,0)) == INVALID_ARGUMENT);
|
|
ASSERT((y=float_sto(SBUF(buf), 0, 0, 0,1, 0,0)) == INVALID_ARGUMENT);
|
|
|
|
// zero issuer/currency lengths mus tbe accompanied by 0 pointers
|
|
ASSERT((y=float_sto(SBUF(buf), 1, 0, 0,0, 0,0)) == INVALID_ARGUMENT);
|
|
ASSERT((y=float_sto(SBUF(buf), 0, 0, 1,0, 0,0)) == INVALID_ARGUMENT);
|
|
|
|
// issuer without currency is invalid
|
|
ASSERT((y=float_sto(SBUF(buf), 0,0, SBUF(iss), 0, sfAmount)) == INVALID_ARGUMENT);
|
|
|
|
// currency without issuer is invalid
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur1), 0,0, 0, sfAmount)) == INVALID_ARGUMENT);
|
|
|
|
// currency and issuer with field code 0 = XRP is invalid
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur1), SBUF(iss), 0, 0)) == INVALID_ARGUMENT);
|
|
|
|
// invalid XFL
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur2), SBUF(iss), -1, sfAmount)) == INVALID_FLOAT);
|
|
|
|
// valid XFL, currency and issuer
|
|
{
|
|
// currency and issuer with field code not XRP is valid (XFL = 1234567.0)
|
|
// try with a three letter currency
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur1), SBUF(iss), 6198187654261802496ULL, sfAmount)) == 49);
|
|
|
|
// check the output contains the correct currency code
|
|
ASSERT(BUFFER_EQUAL_20(buf + 9, cur1full));
|
|
|
|
// again with a 20 byte currency
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur2), SBUF(iss), 6198187654261802496ULL, sfAmount)) == 49);
|
|
|
|
// check the output contains the correct currency code
|
|
ASSERT(BUFFER_EQUAL_20(buf + 9, cur2));
|
|
|
|
// check the output contains the correct issuer
|
|
ASSERT(BUFFER_EQUAL_20(buf + 29, iss));
|
|
|
|
// check the field code is correct
|
|
ASSERT(buf[0] == 0x61U); // sfAmount
|
|
|
|
// reverse the operation and check the XFL amount is correct
|
|
ASSERT((y=float_sto_set(buf, 49)) == 6198187654261802496ULL);
|
|
|
|
// test 0
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur2), SBUF(iss), 0, sfAmount)) == 49);
|
|
ASSERT((y=float_sto_set(buf, 49)) == 0);
|
|
|
|
}
|
|
|
|
// the same again but with a field-uncommon fieldcode
|
|
{
|
|
ASSERT((y=float_sto(SBUF(buf), SBUF(cur2), SBUF(iss), 6198187654261802496ULL, sfDeliveredAmount)) == 50);
|
|
|
|
// check the first 2 bytes
|
|
ASSERT(buf[0] == 0x60U && buf[1] == 0x12U);
|
|
|
|
// same checks as above moved along one
|
|
|
|
// check the output contains the correct currency code
|
|
ASSERT(BUFFER_EQUAL_20(buf + 10, cur2));
|
|
|
|
// check the output contains the correct issuer
|
|
ASSERT(BUFFER_EQUAL_20(buf + 30, iss));
|
|
|
|
// reverse the operation and check the XFL amount is correct
|
|
ASSERT((y=float_sto_set(SBUF(buf))) == 6198187654261802496ULL);
|
|
}
|
|
|
|
// and the same again except use -1 as field code to supress field type bytes
|
|
{
|
|
// zero the serialized amount bytes
|
|
*((uint64_t*)(buf + 2)) = 0;
|
|
|
|
// request fieldcode -1 = only serialize the number
|
|
ASSERT((y=float_sto(buf + 2, 8, 0,0,0,0, 6198187654261802496ULL, 0xFFFFFFFFUL)) == 8);
|
|
|
|
// reverse the operation and check the XFL amount is correct
|
|
ASSERT((y=float_sto_set(SBUF(buf))) == 6198187654261802496ULL);
|
|
|
|
// try again with some different xfls
|
|
ASSERT((y=float_sto(buf + 2, 8, 0,0,0,0, 1244912689067196128ULL, 0xFFFFFFFFUL)) == 8);
|
|
ASSERT((y=float_sto_set(SBUF(buf))) == 1244912689067196128ULL);
|
|
|
|
|
|
// test 0
|
|
ASSERT((y=float_sto(buf + 2, 8, 0,0,0,0, 0, 0xFFFFFFFFUL)) == 8);
|
|
ASSERT((y=float_sto_set(SBUF(buf))) == 0);
|
|
}
|
|
|
|
// finally test xrp
|
|
{
|
|
// zero the serialized amount bytes
|
|
*((uint64_t*)(buf)) = 0;
|
|
|
|
// request fieldcode 0 = xrp amount serialized
|
|
ASSERT((y=float_sto(buf + 1, 8, 0,0,0,0, 6198187654261802496ULL, 0)) == 8);
|
|
|
|
buf[0] = 0x61U;
|
|
|
|
ASSERT((y=float_sto_set(buf, 9)) == 6198187654261802496ULL);
|
|
|
|
// test 0
|
|
ASSERT((y=float_sto(buf + 1, 8, 0,0,0,0, 0, 0)) == 8);
|
|
ASSERT((y=float_sto_set(buf, 9)) == 0);
|
|
|
|
//6198187654373024496
|
|
}
|
|
|
|
return accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_sto"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_sto"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_sto_set()
|
|
{
|
|
testcase("Test float_sto_set");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 hook_account (uint32_t, uint32_t);
|
|
extern int64_t float_sto_set (
|
|
uint32_t read_ptr,
|
|
uint32_t read_len
|
|
);
|
|
#define NOT_AN_OBJECT (-23)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define SBUF(x) x, sizeof(x)
|
|
|
|
#define BUFFER_EQUAL_20(buf1, buf2)\
|
|
(\
|
|
*(((uint64_t*)(buf1)) + 0)) == *(((uint64_t*)(buf2)) + 0) &&\
|
|
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
|
|
*(((uint32_t*)(buf1)) + 4) == *(((uint32_t*)(buf2)) + 4)
|
|
|
|
|
|
// 1234567000000000 * 10**-9, currency USD, issuer 7C4C8D5B2FDA1D16E9A4F5BB579AC2926C146235 (alice)
|
|
uint8_t iou[] = //6198187654261802496
|
|
{
|
|
0x61U,0xD6U,0x04U,0x62U,0xD5U,0x07U,0x7CU,0x86U,0x00U,0x00U,
|
|
0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x55U,0x53U,0x44U,0x00U,0x00U,0x00U,0x00U,0x00U,0x7CU,
|
|
0x4CU,0x8DU,0x5BU,0x2FU,0xDAU,0x1DU,0x16U,0xE9U,0xA4U,0xF5U,
|
|
0xBBU,0x57U,0x9AU,0xC2U,0x92U,0x6CU,0x14U,0x62U,0x35U
|
|
};
|
|
|
|
// as above but value is negative
|
|
uint8_t iou_neg[] = //1586501635834414592
|
|
{
|
|
0x61U,0x96U,0x04U,0x62U,0xD5U,0x07U,0x7CU,0x86U,0x00U,0x00U,
|
|
0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x55U,0x53U,0x44U,0x00U,0x00U,0x00U,0x00U,0x00U,0x7CU,
|
|
0x4CU,0x8DU,0x5BU,0x2FU,0xDAU,0x1DU,0x16U,0xE9U,0xA4U,0xF5U,
|
|
0xBBU,0x57U,0x9AU,0xC2U,0x92U,0x6CU,0x14U,0x62U,0x35U
|
|
};
|
|
|
|
// as above but value is 0
|
|
uint8_t iou_zero[] = // 0
|
|
{
|
|
0x61U,0x80U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x55U,0x53U,0x44U,0x00U,0x00U,0x00U,0x00U,0x00U,0x7CU,
|
|
0x4CU,0x8DU,0x5BU,0x2FU,0xDAU,0x1DU,0x16U,0xE9U,0xA4U,0xF5U,
|
|
0xBBU,0x57U,0x9AU,0xC2U,0x92U,0x6CU,0x14U,0x62U,0x35U
|
|
};
|
|
|
|
// XRP short code 1234567 drops
|
|
uint8_t xrp_short[] = //6198187654261802496
|
|
{
|
|
0x61U,0x40U,0x00U,0x00U,0x00U,0x00U,0x12U,0xD6U,0x87U
|
|
};
|
|
|
|
// XRP long code 755898701447 drops
|
|
uint8_t xrp_long[] = //6294584066823682416
|
|
{
|
|
0x60U,0x11U,0x40U,0x00U,0x00U,0xAFU,0xFFU,0x12U,0xD6U,0x87U
|
|
};
|
|
|
|
// XRP negative 1234567 drops
|
|
uint8_t xrp_neg[] = //1586501635834414592
|
|
{
|
|
0x61U,0x00U,0x00U,0x00U,0x00U,0x00U,0x12U,0xD6U,0x87U
|
|
};
|
|
|
|
// XRP negative zero
|
|
uint8_t xrp_neg_zero[] = // 0
|
|
{
|
|
0x61U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U
|
|
};
|
|
|
|
// XRP positive zero
|
|
uint8_t xrp_pos_zero[] = // 0
|
|
{
|
|
0x61U,0x40U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U
|
|
};
|
|
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
int64_t y;
|
|
|
|
// bounds check
|
|
ASSERT((y=float_sto_set(1000000, 50)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto_set(0, 1000000)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=float_sto_set(1000000, 1000000)) == OUT_OF_BOUNDS);
|
|
|
|
// too small check
|
|
ASSERT((y=float_sto_set(0, 7)) == NOT_AN_OBJECT);
|
|
|
|
// garbage check
|
|
ASSERT((y=float_sto_set(0, 9)) == NOT_AN_OBJECT);
|
|
ASSERT((y=float_sto_set(0, 8)) == 0);
|
|
|
|
|
|
ASSERT((y=float_sto_set(SBUF(iou))) == 6198187654261802496ULL);
|
|
ASSERT((y=float_sto_set(SBUF(xrp_short))) == 6198187654261802496ULL);
|
|
ASSERT((y=float_sto_set(SBUF(iou_neg))) == 1586501635834414592ULL);
|
|
ASSERT((y=float_sto_set(SBUF(xrp_neg))) == 1586501635834414592ULL);
|
|
ASSERT((y=float_sto_set(SBUF(xrp_pos_zero))) == 0);
|
|
ASSERT((y=float_sto_set(SBUF(xrp_neg_zero))) == 0);
|
|
ASSERT((y=float_sto_set(SBUF(iou_zero))) == 0);
|
|
ASSERT((y=float_sto_set(SBUF(xrp_long))) == 6294584066823682416ULL);
|
|
|
|
return accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_sto_set"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_sto_set"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_float_sum()
|
|
{
|
|
testcase("Test float_sum");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
{
|
|
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 float_one (void);
|
|
extern int64_t float_compare(int64_t, int64_t, uint32_t);
|
|
extern int64_t float_negate(int64_t);
|
|
extern int64_t float_sum(int64_t, int64_t);
|
|
extern int64_t float_mantissa(int64_t);
|
|
#define float_exponent(f) (((int32_t)(((f) >> 54U) & 0xFFU)) - 97)
|
|
#define ASSERT_EQUAL(x, y)\
|
|
{\
|
|
int64_t px = (x);\
|
|
int64_t py = (y);\
|
|
int64_t mx = float_mantissa(px);\
|
|
int64_t my = float_mantissa(py);\
|
|
int32_t diffexp = float_exponent(px) - float_exponent(py);\
|
|
if (diffexp == 1)\
|
|
mx *= 10LL;\
|
|
if (diffexp == -1)\
|
|
my *= 10LL;\
|
|
int64_t diffman = mx - my;\
|
|
if (diffman < 0) diffman *= -1LL;\
|
|
if (diffexp < 0) diffexp *= -1;\
|
|
if (diffexp > 1 || diffman > 5000000 || mx < 0 || my < 0)\
|
|
rollback((uint32_t) #x, sizeof(#x), __LINE__);\
|
|
}
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// 1 + 1 = 2
|
|
ASSERT_EQUAL(6090866696204910592LL,
|
|
float_sum(float_one(), float_one()));
|
|
|
|
// 1 - 1 = 0
|
|
ASSERT_EQUAL(0,
|
|
float_sum(float_one(), float_negate(float_one())));
|
|
|
|
// 45678 + 0.345678 = 45678.345678
|
|
ASSERT_EQUAL(6165492124810638528LL,
|
|
float_sum(6165492090242838528LL, 6074309077695428608LL));
|
|
|
|
// -151864512641 + 100000000000000000 = 99999848135487359
|
|
ASSERT_EQUAL(
|
|
6387097057170171072LL,
|
|
float_sum(1676857706508234512LL, 6396111470866104320LL));
|
|
|
|
// auto generated random sums
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
95785354843184473 /* -5.713362295774553e-77 */,
|
|
7607324992379065667 /* 5.248821377668419e+84 */),
|
|
7607324992379065667 /* 5.248821377668419e+84 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
1011203427860697296 /* -2.397111329706192e-26 */,
|
|
7715811566197737722 /* 5.64900413944857e+90 */),
|
|
7715811566197737722 /* 5.64900413944857e+90 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
6507979072644559603 /* 4.781210721563379e+23 */,
|
|
422214339164556094 /* -7.883173446470462e-59 */),
|
|
6507979072644559603 /* 4.781210721563379e+23 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
129493221419941559 /* -3.392431853567671e-75 */,
|
|
6742079437952459317 /* 4.694395406197301e+36 */),
|
|
6742079437952459317 /* 4.694395406197301e+36 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
5172806703808250354 /* 2.674331586920946e-51 */,
|
|
3070396690523275533 /* -7.948943911338253e+88 */),
|
|
3070396690523275533 /* -7.948943911338253e+88 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
2440992231195047997 /* -9.048432414980156e+53 */,
|
|
4937813945440933271 /* 1.868753842869655e-64 */),
|
|
2440992231195047996 /* -9.048432414980156e+53 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
7351918685453062372 /* 2.0440935844129e+70 */,
|
|
6489541496844182832 /* 4.358033430668592e+22 */),
|
|
7351918685453062372 /* 2.0440935844129e+70 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
4960621423606196948 /* 6.661833498651348e-63 */,
|
|
6036716382996689576 /* 0.001892882320224936 */),
|
|
6036716382996689576 /* 0.001892882320224936 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
1342689232407435206 /* -9.62374270576839e-8 */,
|
|
5629833007898276923 /* 9.340672939897915e-26 */),
|
|
1342689232407435206 /* -9.62374270576839e-8 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
7557687707019793516 /* 9.65473154684222e+81 */,
|
|
528084028396448719 /* -5.666471621471183e-53 */),
|
|
7557687707019793516 /* 9.65473154684222e+81 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
130151633377050812 /* -4.050843810676924e-75 */,
|
|
2525286695563827336 /* -3.270904236349576e+58 */),
|
|
2525286695563827336 /* -3.270904236349576e+58 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
5051914485221832639 /* 7.88290256687712e-58 */,
|
|
7518727241611221951 /* 6.723063157234623e+79 */),
|
|
7518727241611221951 /* 6.723063157234623e+79 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
3014788764095798870 /* -6.384213012307542e+85 */,
|
|
7425019819707800346 /* 3.087633801222938e+74 */),
|
|
3014788764095767995 /* -6.384213012276667e+85 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
4918950856932792129 /* 1.020063844210497e-65 */,
|
|
7173510242188034581 /* 3.779635414204949e+60 */),
|
|
7173510242188034581 /* 3.779635414204949e+60 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
20028000442705357 /* -2.013601933223373e-81 */,
|
|
95248745393457140 /* -5.17675284604722e-77 */),
|
|
95248946753650462 /* -5.176954206240542e-77 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
5516870225060928024 /* 4.46428115944092e-32 */,
|
|
7357202055584617194 /* 7.327463715967722e+70 */),
|
|
7357202055584617194 /* 7.327463715967722e+70 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
2326103538819088036 /* -2.2461310959121e+47 */,
|
|
1749360946246242122 /* -1964290826489674 */),
|
|
2326103538819088036 /* -2.2461310959121e+47 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
1738010758208819410 /* -862850129854894.6 */,
|
|
2224610859005732191 /* -8.83984233944816e+41 */),
|
|
2224610859005732192 /* -8.83984233944816e+41 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
4869534730307487904 /* 5.647132747352224e-68 */,
|
|
2166841923565712115 /* -5.114102427874035e+38 */),
|
|
2166841923565712115 /* -5.114102427874035e+38 */);
|
|
ASSERT_EQUAL(
|
|
float_sum(
|
|
1054339559322014937 /* -9.504445772059864e-24 */,
|
|
1389511416678371338 /* -0.0000240273144825857 */),
|
|
1389511416678371338 /* -0.0000240273144825857 */);
|
|
|
|
return
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set float_sum"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test float_sum"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
}
|
|
|
|
void
|
|
test_hook_account()
|
|
{
|
|
testcase("Test hook_account");
|
|
using namespace jtx;
|
|
|
|
auto const test = [&](Account alice) -> void {
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 hook_account (uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t acc[20];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(hook_account(1000000, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_account(0, 1000000) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_account((uint32_t)acc, 19) == TOO_SMALL);
|
|
ASSERT(hook_account((uint32_t)acc, 20) == 20);
|
|
|
|
// return the accid as the return string
|
|
accept((uint32_t)acc, 20, 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set hook_account"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test hook_account"), fee(XRP(1)));
|
|
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr =
|
|
hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the account id
|
|
BEAST_EXPECT(retStr.size() == 20);
|
|
auto const a = alice.id();
|
|
BEAST_EXPECT(memcmp(retStr.data(), a.data(), 20) == 0);
|
|
}
|
|
|
|
// install the same hook bob
|
|
env(ripple::test::jtx::hook(bob, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set hook_account 2"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test hook_account 2"), fee(XRP(1)));
|
|
|
|
// there should be two hook executions, the first should be bob's
|
|
// address the second should be alice's
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there were two hook executions
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 2);
|
|
|
|
{
|
|
// get the data in the return string of the extention
|
|
auto const retStr =
|
|
hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the account id
|
|
BEAST_EXPECT(retStr.size() == 20);
|
|
auto const b = bob.id();
|
|
BEAST_EXPECT(memcmp(retStr.data(), b.data(), 20) == 0);
|
|
}
|
|
|
|
{
|
|
// get the data in the return string of the extention
|
|
auto const retStr =
|
|
hookExecutions[1].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the account id
|
|
BEAST_EXPECT(retStr.size() == 20);
|
|
auto const a = alice.id();
|
|
BEAST_EXPECT(memcmp(retStr.data(), a.data(), 20) == 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
test(Account{"alice"});
|
|
test(Account{"cho"});
|
|
}
|
|
|
|
void
|
|
test_hook_again()
|
|
{
|
|
testcase("Test hook_again");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t hook_again (void);
|
|
#define PREREQUISITE_NOT_MET (-9)
|
|
#define ALREADY_SET (-8)
|
|
int64_t hook(uint32_t r)
|
|
{
|
|
_g(1,1);
|
|
|
|
if (r > 0)
|
|
{
|
|
if (hook_again() != PREREQUISITE_NOT_MET)
|
|
return rollback(0,0,253);
|
|
|
|
return accept(0,0,1);
|
|
}
|
|
|
|
if (hook_again() != 1)
|
|
return rollback(0,0,254);
|
|
|
|
if (hook_again() != ALREADY_SET)
|
|
return rollback(0,0,255);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set hook_again"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test hook_again"), fee(XRP(1)));
|
|
env.close();
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there were two executions
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 2);
|
|
|
|
// get the data in the return code of the execution
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU64(sfHookReturnCode) == 0);
|
|
BEAST_EXPECT(hookExecutions[1].getFieldU64(sfHookReturnCode) == 1);
|
|
|
|
// RH TODO: test hook_again on a weak execution not following a strong
|
|
// execution to make sure it fails
|
|
}
|
|
|
|
void
|
|
test_hook_hash()
|
|
{
|
|
testcase("Test hook_hash");
|
|
using namespace jtx;
|
|
|
|
auto const test = [&](Account alice) -> void {
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const bob = Account{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 hook_hash (uint32_t, uint32_t, int32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(hook_hash(1000000, 32, -1) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_hash((uint32_t)hash, 31, -1) == TOO_SMALL);
|
|
ASSERT(hook_hash((uint32_t)hash, 32, -1) == 32);
|
|
|
|
// return the hash as the return string
|
|
accept((uint32_t)hash, 32, 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set hook_hash"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test hook_hash"), fee(XRP(1)));
|
|
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr =
|
|
hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the hook hash
|
|
BEAST_EXPECT(retStr.size() == 32);
|
|
|
|
auto const hash = hookExecutions[0].getFieldH256(sfHookHash);
|
|
BEAST_EXPECT(memcmp(hash.data(), retStr.data(), 32) == 0);
|
|
}
|
|
|
|
TestHook hook2 = 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 hook_hash (uint32_t, uint32_t, int32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,2);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(hook_hash(1000000, 32, -1) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_hash((uint32_t)hash, 31, -1) == TOO_SMALL);
|
|
ASSERT(hook_hash((uint32_t)hash, 32, -1) == 32);
|
|
|
|
// return the hash as the return string
|
|
accept((uint32_t)hash, 32, 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install a slightly different hook on bob
|
|
env(ripple::test::jtx::hook(bob, {{hso(hook2, overrideFlag)}}, 0),
|
|
M("set hook_hash 2"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test hook_hash 2"), fee(XRP(1)));
|
|
|
|
// there should be two hook executions, the first should have bob's
|
|
// hook hash the second should have alice's hook hash
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 2);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr1 =
|
|
hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the hook hash
|
|
BEAST_EXPECT(retStr1.size() == 32);
|
|
|
|
auto const hash1 = hookExecutions[0].getFieldH256(sfHookHash);
|
|
BEAST_EXPECT(memcmp(hash1.data(), retStr1.data(), 32) == 0);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr2 =
|
|
hookExecutions[1].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the hook hash
|
|
BEAST_EXPECT(retStr2.size() == 32);
|
|
|
|
auto const hash2 = hookExecutions[1].getFieldH256(sfHookHash);
|
|
BEAST_EXPECT(memcmp(hash2.data(), retStr2.data(), 32) == 0);
|
|
|
|
// make sure they're not the same
|
|
BEAST_EXPECT(memcmp(hash1.data(), hash2.data(), 32) != 0);
|
|
|
|
// compute the hashes
|
|
auto computedHash2 = ripple::sha512Half_s(
|
|
ripple::Slice(hook.data(), hook.size()));
|
|
|
|
auto computedHash1 = ripple::sha512Half_s(
|
|
ripple::Slice(hook2.data(), hook2.size()));
|
|
|
|
// ensure the computed hashes match
|
|
BEAST_EXPECT(computedHash1 == hash1);
|
|
BEAST_EXPECT(computedHash2 == hash2);
|
|
}
|
|
};
|
|
|
|
test(Account{"alice"});
|
|
}
|
|
|
|
void
|
|
test_hook_param()
|
|
{
|
|
testcase("Test hook_param");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t hook_param(uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define DOESNT_EXIST (-5)
|
|
uint8_t* names[] =
|
|
{
|
|
"param0",
|
|
"param1",
|
|
"param2",
|
|
"param3",
|
|
"param4",
|
|
"param5",
|
|
"param6",
|
|
"param7",
|
|
"param8",
|
|
"param9",
|
|
"param10",
|
|
"param11",
|
|
"param12",
|
|
"param13",
|
|
"param14",
|
|
"param15"
|
|
};
|
|
uint8_t* values[] =
|
|
{
|
|
"value0",
|
|
"value1",
|
|
"value2",
|
|
"value3",
|
|
"value4",
|
|
"value5",
|
|
"value6",
|
|
"value7",
|
|
"value8",
|
|
"value9",
|
|
"value10",
|
|
"value11",
|
|
"value12",
|
|
"value13",
|
|
"value14",
|
|
"value15"
|
|
};
|
|
#define SBUF(x) x,sizeof(x)
|
|
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(hook_param(0, 1000000, 0,32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param(1000000, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param(0, 32, 1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param(0, 32, 0, 1000000) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param(0, 32, 0, 33) == TOO_BIG);
|
|
ASSERT(hook_param(0, 32, 0, 0) == TOO_SMALL);
|
|
ASSERT(hook_param(0, 32, 0, 32) == DOESNT_EXIST);
|
|
|
|
uint8_t buf[32];
|
|
|
|
for (int i = 0; GUARD(16), i < 16; ++i)
|
|
{
|
|
int s = 6 + (i < 10 ? 0 : 1);
|
|
int64_t v = hook_param(SBUF(buf), names[i], s);
|
|
ASSERT(v == s);
|
|
|
|
ASSERT(buf[0] == 'v' && buf[1] == 'a' && buf[2] == 'l' && buf[3] == 'u' && buf[4] == 'e');
|
|
ASSERT(*(buf + v - 1) == *(values[i] + v - 1));
|
|
ASSERT(*(buf + v - 2) == *(values[i] + v - 2));
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
|
|
)[test.hook]"];
|
|
|
|
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] = strHex(hook);
|
|
iv[jss::HookOn] =
|
|
"0000000000000000000000000000000000000000000000000000000000000000";
|
|
iv[jss::HookApiVersion] = 0U;
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
Json::Value params{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 16; ++i)
|
|
{
|
|
Json::Value pv;
|
|
Json::Value piv;
|
|
piv[jss::HookParameterName] = strHex("param" + std::to_string(i));
|
|
piv[jss::HookParameterValue] = strHex("value" + std::to_string(i));
|
|
pv[jss::HookParameter] = piv;
|
|
params[i] = pv;
|
|
}
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
env(jv, M("set hook_param"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// invoke
|
|
env(pay(bob, alice, XRP(1)), M("test hook_param"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
void
|
|
test_hook_param_set()
|
|
{
|
|
testcase("Test hook_param_set");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook checker_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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
|
|
extern int64_t hook_param(uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define DOESNT_EXIST (-5)
|
|
#define INVALID_ARGUMENT (-7)
|
|
uint8_t* names[] =
|
|
{
|
|
"param0",
|
|
"param1",
|
|
"param2",
|
|
"param3",
|
|
};
|
|
#define SBUF(x) x,sizeof(x)
|
|
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// this entry should be deleted by the setter
|
|
uint8_t checker_hash[32];
|
|
ASSERT(hook_param(SBUF(checker_hash), "checker", 7) == DOESNT_EXIST);
|
|
|
|
|
|
uint8_t buf[32];
|
|
|
|
// this entry should havebeen added by the setter
|
|
ASSERT(hook_param(SBUF(buf), "hello", 5) == 5);
|
|
ASSERT(buf[0] == 'w' && buf[1] == 'o' && buf[2] == 'r' && buf[3] == 'l' && buf[4] == 'd');
|
|
|
|
// these pre-existing entries should be modified by the setter
|
|
for (int i = 0; GUARD(4), i < 4; ++i)
|
|
{
|
|
ASSERT(hook_param(SBUF(buf), names[i], 6) == 6);
|
|
|
|
ASSERT(buf[0] == 'v' && buf[1] == 'a' && buf[2] == 'l' &&
|
|
buf[3] == 'u' && buf[4] == 'e' && buf[5] == '0' + i);
|
|
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
|
|
)[test.hook]"];
|
|
|
|
TestHook setter_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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t hook_param (uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t hook_param_set (
|
|
uint32_t read_ptr,
|
|
uint32_t read_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len,
|
|
uint32_t hread_ptr,
|
|
uint32_t hread_len
|
|
);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define DOESNT_EXIST (-5)
|
|
#define INVALID_ARGUMENT (-7)
|
|
uint8_t* names[] =
|
|
{
|
|
"param0",
|
|
"param1",
|
|
"param2",
|
|
"param3",
|
|
};
|
|
uint8_t* values[] =
|
|
{
|
|
"value0",
|
|
"value1",
|
|
"value2",
|
|
"value3",
|
|
};
|
|
#define SBUF(x) x,sizeof(x)
|
|
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
|
|
ASSERT(hook_param_set(1000000, 32, 0, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 1000000, 0, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 32, 1000000, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 32, 0, 1000000, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 32, 0, 32, 1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 32, 0, 32, 0, 1000000) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_param_set(0, 32, 0, 0, 0, 32) == TOO_SMALL);
|
|
ASSERT(hook_param_set(0, 32, 0, 33, 0, 32) == TOO_BIG);
|
|
ASSERT(hook_param_set(0, 32, 0, 32, 0, 33) == INVALID_ARGUMENT);
|
|
ASSERT(hook_param_set(0, 32, 0, 32, 0, 31) == INVALID_ARGUMENT);
|
|
ASSERT(hook_param_set(0, 32, 0, 32, 0, 0) == INVALID_ARGUMENT);
|
|
ASSERT(hook_param_set(0, 257, 0, 32, 0, 32) == TOO_BIG);
|
|
|
|
|
|
uint8_t checker_hash[32];
|
|
ASSERT(hook_param(SBUF(checker_hash), "checker", 7) == 32);
|
|
|
|
for (int i = 0; GUARD(4), i < 4; ++i)
|
|
{
|
|
ASSERT(hook_param_set(values[i], 6, names[i], 6, SBUF(checker_hash)) == 6);
|
|
}
|
|
|
|
// "delete" the checker entry" for when the checker runs
|
|
ASSERT(hook_param_set(0,0,"checker", 7, SBUF(checker_hash)) == 0);
|
|
|
|
// add a parameter that did not previously exist
|
|
ASSERT(hook_param_set("world", 5,"hello", 5, SBUF(checker_hash)) == 5);
|
|
|
|
// ensure this hook's parameters did not change
|
|
uint8_t buf[32];
|
|
for (int i = 0; GUARD(4), i < 4; ++i)
|
|
{
|
|
ASSERT(hook_param(SBUF(buf), names[i], 6) == 6);
|
|
|
|
ASSERT(buf[0] == 'v' && buf[1] == 'a' && buf[2] == 'l' &&
|
|
buf[3] == 'u' && buf[4] == 'e' && buf[5] == '0');
|
|
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(checker);
|
|
|
|
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] = strHex(setter_wasm);
|
|
iv[jss::HookOn] =
|
|
"0000000000000000000000000000000000000000000000000000000000000000";
|
|
iv[jss::HookApiVersion] = 0U;
|
|
iv[jss::HookNamespace] = to_string(uint256{beast::zero});
|
|
|
|
Json::Value checkerpv;
|
|
{
|
|
Json::Value piv;
|
|
piv[jss::HookParameterName] = strHex(std::string("checker"));
|
|
piv[jss::HookParameterValue] = checker_hash_str;
|
|
checkerpv[jss::HookParameter] = piv;
|
|
}
|
|
|
|
Json::Value params{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 4; ++i)
|
|
{
|
|
Json::Value pv;
|
|
Json::Value piv;
|
|
piv[jss::HookParameterName] = strHex("param" + std::to_string(i));
|
|
piv[jss::HookParameterValue] = strHex(std::string("value0"));
|
|
pv[jss::HookParameter] = piv;
|
|
params[i] = pv;
|
|
}
|
|
params[4U] = checkerpv;
|
|
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][0U][jss::Hook] = iv;
|
|
|
|
{
|
|
iv[jss::CreateCode] = strHex(checker_wasm);
|
|
Json::Value params{Json::arrayValue};
|
|
params[0U] = checkerpv;
|
|
iv[jss::HookParameters] = params;
|
|
jv[jss::Hooks][3U][jss::Hook] = iv;
|
|
jv[jss::Hooks][1U][jss::Hook] = Json::objectValue;
|
|
jv[jss::Hooks][2U][jss::Hook] = Json::objectValue;
|
|
}
|
|
|
|
env(jv, M("set hook_param_set"), HSFEE, ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// invoke
|
|
env(pay(bob, alice, XRP(1)), M("test hook_param_set"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
void
|
|
test_hook_pos()
|
|
{
|
|
testcase("Test hook_pos");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t hook_pos (void);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
accept(0,0,hook_pos());
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice in all four spots
|
|
env(ripple::test::jtx::hook(
|
|
alice, {{hso(hook), hso(hook), hso(hook), hso(hook)}}, 0),
|
|
M("set hook_pos"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// invoke the hooks
|
|
env(pay(bob, alice, XRP(1)), M("test hook_pos"), fee(XRP(1)));
|
|
env.close();
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was four hook executions
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 4);
|
|
|
|
// get the data in the return code of the execution
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU64(sfHookReturnCode) == 0);
|
|
BEAST_EXPECT(hookExecutions[1].getFieldU64(sfHookReturnCode) == 1);
|
|
BEAST_EXPECT(hookExecutions[2].getFieldU64(sfHookReturnCode) == 2);
|
|
BEAST_EXPECT(hookExecutions[3].getFieldU64(sfHookReturnCode) == 3);
|
|
}
|
|
|
|
void
|
|
test_hook_skip()
|
|
{
|
|
testcase("Test hook_skip");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook skip_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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t hook_skip (uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_field (uint32_t, uint32_t, uint32_t);
|
|
extern int64_t hook_hash (uint32_t, uint32_t, int32_t);
|
|
extern int64_t hook_pos(void);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__ << 8U);
|
|
#define sfInvoiceID ((5U << 16U) + 17U)
|
|
#define SBUF(x) x,sizeof(x)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define DOESNT_EXIST (-5)
|
|
#define INVALID_ARGUMENT (-7)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// bounds checks
|
|
ASSERT(hook_skip(0, 1000000, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(1000000, 32, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(1000000, 100000, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(0, 33, 0) == INVALID_ARGUMENT);
|
|
ASSERT(hook_skip(0, 1000000, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(1000000, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(1000000, 100000, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(hook_skip(0, 33, 1) == INVALID_ARGUMENT);
|
|
|
|
// garbage check
|
|
ASSERT(hook_skip(0, 32, 0) == DOESNT_EXIST);
|
|
ASSERT(hook_skip(0, 32, 1) == DOESNT_EXIST);
|
|
ASSERT(hook_skip(0, 32, 2) == INVALID_ARGUMENT);
|
|
|
|
// the hook to skip is passed in by invoice id
|
|
uint8_t skip[32];
|
|
ASSERT(otxn_field(SBUF(skip), sfInvoiceID) == 32);
|
|
|
|
// get this hook's hash
|
|
uint8_t hash[32];
|
|
ASSERT(hook_hash(SBUF(hash), (uint32_t)hook_pos()) == 32);
|
|
|
|
// to test if the "remove" function works in the api we will add this hook hash itself and then
|
|
// remove it again. Therefore if the hook is placed at positions 0 and 3, the one at 3 should still
|
|
// run
|
|
ASSERT(hook_skip(SBUF(hash), 1) == DOESNT_EXIST);
|
|
ASSERT(hook_skip(SBUF(hash), 0) == 1);
|
|
ASSERT(hook_skip(SBUF(hash), 1) == 1);
|
|
|
|
// finally skip the hook hash indicated by invoice id
|
|
ASSERT(hook_skip(SBUF(skip), 0));
|
|
|
|
accept(0,0,hook_pos());
|
|
}
|
|
|
|
)[test.hook]"];
|
|
|
|
TestHook pos_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);
|
|
extern int64_t hook_pos (void);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
accept(0,0,255);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(pos);
|
|
|
|
// install the hook on alice in one places
|
|
env(ripple::test::jtx::hook(
|
|
alice,
|
|
{{hso(skip_wasm),
|
|
hso(pos_wasm),
|
|
hso(pos_wasm),
|
|
hso(skip_wasm)}},
|
|
0),
|
|
M("set hook_skip"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
|
|
// invoke the hooks
|
|
{
|
|
Json::Value json = pay(bob, alice, XRP(1));
|
|
json[jss::InvoiceID] = pos_hash_str;
|
|
env(json, fee(XRP(1)), M("test hook_skip"), ter(tesSUCCESS));
|
|
env.close();
|
|
}
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was four hook executions
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 2);
|
|
|
|
// get the data in the return code of the execution
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU64(sfHookReturnCode) == 0);
|
|
BEAST_EXPECT(hookExecutions[1].getFieldU64(sfHookReturnCode) == 3);
|
|
}
|
|
|
|
void
|
|
test_ledger_keylet()
|
|
{
|
|
testcase("Test ledger_keylet");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t ledger_keylet(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
{\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);\
|
|
}
|
|
#define SBUF(x) x, sizeof(x)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define TOO_SMALL (-4)
|
|
#define TOO_BIG (-3)
|
|
#define INVALID_ARGUMENT (-7)
|
|
#define DOESNT_EXIST (-5)
|
|
#define DOES_NOT_MATCH (-40)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(ledger_keylet(1000000, 34, 0, 34, 0, 34) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_keylet(0, 1000000, 0, 34, 0, 34) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_keylet(0, 34, 1000000, 34, 0, 34) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_keylet(0, 34, 0, 1000000, 0, 34) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_keylet(0, 34, 0, 34, 1000000, 34) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_keylet(0, 34, 0, 34, 0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(ledger_keylet(0, 33, 0, 34, 0, 34) == TOO_SMALL);
|
|
ASSERT(ledger_keylet(0, 34, 0, 33, 0, 34) == TOO_SMALL);
|
|
ASSERT(ledger_keylet(0, 34, 0, 34, 0, 33) == TOO_SMALL);
|
|
|
|
ASSERT(ledger_keylet(0, 35, 0, 34, 0, 34) == TOO_BIG);
|
|
ASSERT(ledger_keylet(0, 34, 0, 35, 0, 34) == TOO_BIG);
|
|
ASSERT(ledger_keylet(0, 34, 0, 34, 0, 35) == TOO_BIG);
|
|
|
|
uint8_t trash[34] = {
|
|
1,2,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9
|
|
};
|
|
|
|
uint8_t trash2[34] = {
|
|
1,2,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
1, 2, 3, 4, 5, 6, 7, 8, 10
|
|
};
|
|
|
|
|
|
ASSERT(ledger_keylet(0, 34, SBUF(trash2), SBUF(trash)) == DOESNT_EXIST);
|
|
ASSERT(ledger_keylet(0, 34, SBUF(trash), SBUF(trash2)) == DOESNT_EXIST);
|
|
|
|
|
|
uint8_t first[34];
|
|
uint8_t last[34] = {
|
|
0x00U, 0x01U,
|
|
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
|
|
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
|
|
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU,
|
|
0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFEU
|
|
};
|
|
|
|
uint8_t out[34];
|
|
ASSERT(ledger_keylet(SBUF(out), SBUF(first), SBUF(last)) == DOES_NOT_MATCH);
|
|
last[1] = 0;
|
|
ASSERT(ledger_keylet(SBUF(out), SBUF(first), SBUF(last)) == 34);
|
|
|
|
ASSERT(slot_set(SBUF(out), 1) == 1);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set ledger_keylet"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test ledger_keylet"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
void
|
|
test_ledger_last_hash()
|
|
{
|
|
testcase("Test ledger_last_hash");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t ledger_last_hash (uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(ledger_last_hash(1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_last_hash((uint32_t)hash, 31) == TOO_SMALL);
|
|
ASSERT(ledger_last_hash((uint32_t)hash, 32) == 32);
|
|
|
|
// return the hash
|
|
accept((uint32_t)hash, 32, 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set ledger_last_hash"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
for (uint32_t i = 0; i < 3; ++i)
|
|
{
|
|
auto const llh =
|
|
env.app().getLedgerMaster().getClosedLedger()->info().hash;
|
|
|
|
env(pay(bob, alice, XRP(1)),
|
|
M("test ledger_last_hash"),
|
|
fee(XRP(1)));
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr =
|
|
hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the expected size (32 bytes)
|
|
BEAST_EXPECT(retStr.size() == 32);
|
|
|
|
BEAST_EXPECT(llh == uint256::fromVoid(retStr.data()));
|
|
}
|
|
}
|
|
|
|
void
|
|
test_ledger_last_time()
|
|
{
|
|
testcase("Test ledger_last_time");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t ledger_last_time (void);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
accept(0,0,ledger_last_time());
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set ledger_last_time"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook a few times
|
|
for (uint32_t i = 0; i < 3; ++i)
|
|
{
|
|
int64_t llc = std::chrono::duration_cast<std::chrono::seconds>(
|
|
env.app()
|
|
.getLedgerMaster()
|
|
.getCurrentLedger()
|
|
->info()
|
|
.parentCloseTime.time_since_epoch())
|
|
.count();
|
|
|
|
env(pay(bob, alice, XRP(1)),
|
|
M("test ledger_last_time"),
|
|
fee(XRP(1)));
|
|
env.close();
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return code of the execution
|
|
auto const rc = hookExecutions[0].getFieldU64(sfHookReturnCode);
|
|
|
|
// check that it matches the last ledger seq number
|
|
BEAST_EXPECT(llc == rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
test_ledger_nonce()
|
|
{
|
|
testcase("Test ledger_nonce");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 ledger_nonce (uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t nonce[64];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(ledger_nonce(1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(ledger_nonce((uint32_t)nonce, 31) == TOO_SMALL);
|
|
ASSERT(ledger_nonce((uint32_t)nonce, 32) == 32);
|
|
ASSERT(ledger_nonce((uint32_t)(nonce + 32), 32) == 32);
|
|
|
|
// return the two nonces as the return string
|
|
accept((uint32_t)nonce, 64, 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set ledger_nonce"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
auto const seq =
|
|
env.app().getLedgerMaster().getCurrentLedger()->info().seq;
|
|
auto const llc = env.app()
|
|
.getLedgerMaster()
|
|
.getCurrentLedger()
|
|
->info()
|
|
.parentCloseTime.time_since_epoch()
|
|
.count();
|
|
auto const llh =
|
|
env.app().getLedgerMaster().getCurrentLedger()->info().hash;
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test ledger_nonce"), fee(XRP(1)));
|
|
auto const txid = env.txid();
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return string of the extention
|
|
auto const retStr = hookExecutions[0].getFieldVL(sfHookReturnString);
|
|
|
|
// check that it matches the expected size (two nonces = 64 bytes)
|
|
BEAST_EXPECT(retStr.size() == 64);
|
|
|
|
auto const computed_hash_1 = ripple::sha512Half(
|
|
ripple::HashPrefix::hookNonce,
|
|
seq,
|
|
llc,
|
|
llh,
|
|
txid,
|
|
(uint16_t)0UL,
|
|
alice.id());
|
|
auto const computed_hash_2 = ripple::sha512Half(
|
|
ripple::HashPrefix::hookNonce,
|
|
seq,
|
|
llc,
|
|
llh,
|
|
txid,
|
|
(uint16_t)1UL, // second nonce
|
|
alice.id());
|
|
|
|
BEAST_EXPECT(computed_hash_1 == uint256::fromVoid(retStr.data()));
|
|
BEAST_EXPECT(computed_hash_2 == uint256::fromVoid(retStr.data() + 32));
|
|
}
|
|
|
|
void
|
|
test_ledger_seq()
|
|
{
|
|
testcase("Test ledger_seq");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t ledger_seq (void);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
accept(0,0,ledger_seq());
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set ledger_seq"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook a few times
|
|
for (uint32_t i = 0; i < 3; ++i)
|
|
{
|
|
env(pay(bob, alice, XRP(1)), M("test ledger_seq"), fee(XRP(1)));
|
|
env.close();
|
|
{
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there was only one hook execution
|
|
auto const hookExecutions =
|
|
meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 1);
|
|
|
|
// get the data in the return code of the execution
|
|
auto const rc = hookExecutions[0].getFieldU64(sfHookReturnCode);
|
|
|
|
// check that it matches the last ledger seq number
|
|
BEAST_EXPECT(
|
|
env.app().getLedgerMaster().getClosedLedger()->info().seq ==
|
|
rc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
test_meta_slot()
|
|
{
|
|
testcase("Test meta_slot");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t hook_again (void);
|
|
extern int64_t slot(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t trace(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t meta_slot(uint32_t);
|
|
extern int64_t slot_subfield (
|
|
uint32_t parent_slot,
|
|
uint32_t field_id,
|
|
uint32_t new_slot
|
|
);
|
|
#define PREREQUISITE_NOT_MET (-9)
|
|
#define ALREADY_SET (-8)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define sfHookExecutions ((15U << 16U) + 18U)
|
|
#define sfTransactionResult ((16U << 16U) + 3U)
|
|
#define sfAffectedNodes ((15U << 16U) + 8U)
|
|
#define sfTransactionIndex ((2U << 16U) + 28U)
|
|
int64_t hook(uint32_t r)
|
|
{
|
|
_g(1,1);
|
|
|
|
if (r > 0)
|
|
{
|
|
ASSERT(meta_slot(1) == 1);
|
|
|
|
uint8_t buf[1024];
|
|
ASSERT(slot(buf, 1024, 1) > 200);
|
|
|
|
ASSERT(slot_subfield(1, sfTransactionIndex, 2) == 2);
|
|
ASSERT(slot_subfield(1, sfAffectedNodes, 3) == 3);
|
|
ASSERT(slot_subfield(1, sfHookExecutions, 4) == 4);
|
|
ASSERT(slot_subfield(1, sfTransactionResult, 5) == 5);
|
|
|
|
return accept(0,0,1);
|
|
}
|
|
|
|
if (hook_again() != 1)
|
|
return rollback(0,0,254);
|
|
|
|
ASSERT(meta_slot(1) == PREREQUISITE_NOT_MET);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set meta_slot"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
env(pay(bob, alice, XRP(1)), M("test meta_slot"), fee(XRP(1)));
|
|
env.close();
|
|
|
|
auto meta = env.meta();
|
|
|
|
// ensure hook execution occured
|
|
BEAST_REQUIRE(meta);
|
|
BEAST_REQUIRE(meta->isFieldPresent(sfHookExecutions));
|
|
|
|
// ensure there were two executions
|
|
auto const hookExecutions = meta->getFieldArray(sfHookExecutions);
|
|
BEAST_REQUIRE(hookExecutions.size() == 2);
|
|
|
|
// get the data in the return code of the execution
|
|
BEAST_EXPECT(hookExecutions[0].getFieldU64(sfHookReturnCode) == 0);
|
|
BEAST_EXPECT(hookExecutions[1].getFieldU64(sfHookReturnCode) == 1);
|
|
}
|
|
|
|
void
|
|
test_otxn_field()
|
|
{
|
|
testcase("Test otxn_field");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 otxn_field (uint32_t write_ptr, uint32_t write_len, uint32_t sfcode);
|
|
extern int64_t hook_account(uint32_t, uint32_t);
|
|
#define OUT_OF_BOUNDS -1
|
|
#define TOO_BIG -3
|
|
#define TOO_SMALL -4
|
|
#define INVALID_ARGUMENT -7
|
|
#define INVALID_FIELD -17
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define sfAccount ((8U << 16U) + 1U)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// bounds check
|
|
ASSERT(otxn_field(1, 1000000, sfAccount) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_field(1000000, 20, sfAccount) == OUT_OF_BOUNDS);
|
|
|
|
// sanity check
|
|
ASSERT(otxn_field(0, 1, sfAccount) == INVALID_ARGUMENT);
|
|
|
|
// size check
|
|
ASSERT(otxn_field(0, 0, sfAccount) == TOO_BIG);
|
|
|
|
uint8_t acc[20];
|
|
ASSERT(otxn_field(acc, 19, sfAccount) == TOO_SMALL);
|
|
|
|
ASSERT(otxn_field(acc, 20, sfAccount) == 20);
|
|
|
|
ASSERT(otxn_field(acc, 20, 1) == INVALID_FIELD);
|
|
uint8_t acc2[20];
|
|
ASSERT(hook_account(acc2, 20) == 20);
|
|
|
|
for (int i = 0; GUARD(20), i < 20; ++i)
|
|
ASSERT(acc[i] == acc2[i]);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set otxn_field"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(alice, bob, XRP(1)), M("test otxn_field"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_otxn_id()
|
|
{
|
|
testcase("Test otxn_id");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t otxn_id (uint32_t write_ptr, uint32_t write_len, uint32_t flags);
|
|
extern int64_t util_sha512h(uint32_t write_ptr, uint32_t write_len,
|
|
uint32_t read_ptr, uint32_t read_len );
|
|
extern int64_t otxn_slot (
|
|
uint32_t slot_no
|
|
);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#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);
|
|
|
|
// bounds check
|
|
ASSERT(otxn_id(1, 1000000, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_id(1000000, 1024, 0) == OUT_OF_BOUNDS);
|
|
|
|
// size check
|
|
ASSERT(otxn_id(1, 0, 0) == TOO_SMALL);
|
|
ASSERT(otxn_id(1, 31, 0) == TOO_SMALL);
|
|
|
|
uint8_t id[32];
|
|
ASSERT(otxn_id(SBUF(id), 0) == 32);
|
|
|
|
// slot the otxn then generate a canonical hash over it
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
uint8_t buf[1024];
|
|
int64_t size = slot(buf + 4, sizeof(buf) - 4, 1);
|
|
|
|
|
|
ASSERT(size > 0);
|
|
|
|
buf[0] = 'T';
|
|
buf[1] = 'X';
|
|
buf[2] = 'N';
|
|
buf[3] = 0;
|
|
|
|
uint8_t hash[32];
|
|
ASSERT(util_sha512h(SBUF(hash), buf, size+4) == 32);
|
|
|
|
for (int i = 0; GUARD(32), i < 32; ++i)
|
|
ASSERT(hash[i] == id[i]);
|
|
|
|
// RH TODO: test the flags = 1 on emitted txn
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set otxn_id"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test otxn_id"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_otxn_slot()
|
|
{
|
|
testcase("Test otxn_slot");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t otxn_id (uint32_t write_ptr, uint32_t write_len, uint32_t flags);
|
|
extern int64_t util_sha512h(uint32_t write_ptr, uint32_t write_len,
|
|
uint32_t read_ptr, uint32_t read_len );
|
|
extern int64_t otxn_slot (
|
|
uint32_t slot_no
|
|
);
|
|
#define INVALID_ARGUMENT -7
|
|
#define NO_FREE_SLOTS -6
|
|
#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);
|
|
|
|
ASSERT(otxn_slot(256) == INVALID_ARGUMENT);
|
|
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
uint8_t id[32];
|
|
ASSERT(otxn_id(SBUF(id), 0) == 32);
|
|
|
|
// slot the otxn then generate a canonical hash over it
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
uint8_t buf[1024];
|
|
int64_t size = slot(buf + 4, sizeof(buf) - 4, 1);
|
|
|
|
ASSERT(size > 0);
|
|
|
|
buf[0] = 'T';
|
|
buf[1] = 'X';
|
|
buf[2] = 'N';
|
|
buf[3] = 0;
|
|
|
|
uint8_t hash[32];
|
|
ASSERT(util_sha512h(SBUF(hash), buf, size+4) == 32);
|
|
|
|
for (int i = 0; GUARD(32), i < 32; ++i)
|
|
ASSERT(hash[i] == id[i]);
|
|
|
|
// slot exhaustion
|
|
for (int i = 0; GUARD(255), i < 254; ++i)
|
|
ASSERT(otxn_slot(0) > 0);
|
|
|
|
ASSERT(otxn_slot(0) == NO_FREE_SLOTS);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set otxn_slot"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test otxn_slot"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_otxn_type()
|
|
{
|
|
testcase("Test otxn_type");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t otxn_slot (
|
|
uint32_t slot_no
|
|
);
|
|
extern int64_t slot_subfield(
|
|
uint32_t parent_slot,
|
|
uint32_t field_id,
|
|
uint32_t new_slot
|
|
);
|
|
extern int64_t otxn_type(void);
|
|
#define sfTransactionType ((1U << 16U) + 2U)
|
|
#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);
|
|
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
ASSERT(slot_subfield(1, sfTransactionType, 2) == 2);
|
|
|
|
int64_t tt = slot(0,0,2);
|
|
|
|
ASSERT(tt == otxn_type());
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set otxn_type"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test otxn_type"), fee(XRP(1)));
|
|
|
|
// invoke it another way
|
|
Json::Value jv;
|
|
jv[jss::Account] = alice.human();
|
|
jv[jss::TransactionType] = jss::AccountSet;
|
|
jv[jss::Flags] = 0;
|
|
|
|
// invoke the hook
|
|
env(jv, M("test otxn_type 2"), fee(XRP(1)));
|
|
|
|
// RH TODO: test behaviour on emit failure
|
|
}
|
|
|
|
void
|
|
test_otxn_param()
|
|
{
|
|
testcase("Test otxn_param");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
TestHook hook = 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);
|
|
extern int64_t rollback (uint32_t read_ptr, uint32_t read_len, int64_t error_code);
|
|
extern int64_t otxn_param(uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define DOESNT_EXIST (-5)
|
|
uint8_t* names[] =
|
|
{
|
|
"param0",
|
|
"param1",
|
|
"param2",
|
|
"param3",
|
|
"param4",
|
|
"param5",
|
|
"param6",
|
|
"param7",
|
|
"param8",
|
|
"param9",
|
|
"param10",
|
|
"param11",
|
|
"param12",
|
|
"param13",
|
|
"param14",
|
|
"param15"
|
|
};
|
|
uint8_t* values[] =
|
|
{
|
|
"value0",
|
|
"value1",
|
|
"value2",
|
|
"value3",
|
|
"value4",
|
|
"value5",
|
|
"value6",
|
|
"value7",
|
|
"value8",
|
|
"value9",
|
|
"value10",
|
|
"value11",
|
|
"value12",
|
|
"value13",
|
|
"value14",
|
|
"value15"
|
|
};
|
|
#define SBUF(x) x,sizeof(x)
|
|
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(otxn_param(0, 1000000, 0,32) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_param(1000000, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_param(0, 32, 1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_param(0, 32, 0, 1000000) == OUT_OF_BOUNDS);
|
|
ASSERT(otxn_param(0, 32, 0, 33) == TOO_BIG);
|
|
ASSERT(otxn_param(0, 32, 0, 0) == TOO_SMALL);
|
|
ASSERT(otxn_param(0, 32, 0, 32) == DOESNT_EXIST);
|
|
|
|
uint8_t buf[32];
|
|
|
|
for (int i = 0; GUARD(16), i < 16; ++i)
|
|
{
|
|
int s = 6 + (i < 10 ? 0 : 1);
|
|
int64_t v = otxn_param(SBUF(buf), names[i], s);
|
|
ASSERT(v == s);
|
|
|
|
ASSERT(buf[0] == 'v' && buf[1] == 'a' && buf[2] == 'l' && buf[3] == 'u' && buf[4] == 'e');
|
|
ASSERT(*(buf + v - 1) == *(values[i] + v - 1));
|
|
ASSERT(*(buf + v - 2) == *(values[i] + v - 2));
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set otxn_param"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke
|
|
Json::Value invoke;
|
|
invoke[jss::TransactionType] = "Invoke";
|
|
invoke[jss::Account] = bob.human();
|
|
invoke[jss::Destination] = alice.human();
|
|
|
|
Json::Value params{Json::arrayValue};
|
|
for (uint32_t i = 0; i < 16; ++i)
|
|
{
|
|
Json::Value pv;
|
|
Json::Value piv;
|
|
piv[jss::HookParameterName] = strHex("param" + std::to_string(i));
|
|
piv[jss::HookParameterValue] = strHex("value" + std::to_string(i));
|
|
pv[jss::HookParameter] = piv;
|
|
params[i] = pv;
|
|
}
|
|
invoke[jss::HookParameters] = params;
|
|
|
|
env(invoke, M("test otxn_param"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
void
|
|
test_slot()
|
|
{
|
|
testcase("Test slot");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t slot_subfield (
|
|
uint32_t parent_slot,
|
|
uint32_t field_id,
|
|
uint32_t new_slot
|
|
);
|
|
extern int64_t util_keylet (
|
|
uint32_t write_ptr, uint32_t write_len, uint32_t keylet_type,
|
|
uint32_t a, uint32_t b, uint32_t c,
|
|
uint32_t d, uint32_t e, uint32_t f
|
|
);
|
|
extern int64_t sto_subfield(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t sto_validate(uint32_t, uint32_t);
|
|
extern int64_t hook_account(uint32_t, uint32_t);
|
|
extern int64_t slot_size(uint32_t);
|
|
#define sfBalance ((6U << 16U) + 2U)
|
|
#define sfFlags ((2U << 16U) + 2U)
|
|
#define DOESNT_EXIST -5
|
|
#define TOO_SMALL -4
|
|
#define TOO_BIG -3
|
|
#define OUT_OF_BOUNDS -1
|
|
#define INVALID_ARGUMENT -7
|
|
#define KEYLET_ACCOUNT 3
|
|
#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);
|
|
|
|
// bounds check
|
|
ASSERT(slot(1, 1000000, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(slot(1000000, 1024, 0) == OUT_OF_BOUNDS);
|
|
|
|
// this function can return data as an int64_t,
|
|
// but requires 0,0 as arguments
|
|
ASSERT(slot(0,1, 0) == INVALID_ARGUMENT);
|
|
|
|
// slot 0 hasn't been set yet so
|
|
ASSERT(slot(0,0,0) == DOESNT_EXIST);
|
|
|
|
// grab the hook account
|
|
uint8_t acc[20];
|
|
ASSERT(20 == hook_account(SBUF(acc)));
|
|
|
|
// turn it into account root keylet
|
|
uint8_t kl[34];
|
|
ASSERT(34 == util_keylet(SBUF(kl), KEYLET_ACCOUNT, SBUF(acc), 0,0,0,0));
|
|
|
|
// slot the account root into a new slot
|
|
int64_t slot_no = 0;
|
|
ASSERT((slot_no = slot_set(SBUF(kl), 0)) > 0);
|
|
|
|
int64_t size = 0;
|
|
ASSERT((size = slot_size(slot_no)) > 0);
|
|
|
|
// the slotted item is too large for return as int64
|
|
ASSERT(slot(0,0,slot_no) == TOO_BIG);
|
|
|
|
// big buffer, large enough to hold the account_root
|
|
uint8_t buf[1024];
|
|
|
|
// the slot call should return the bytes written which should exactly
|
|
// match the size of the slotted object
|
|
ASSERT(slot(SBUF(buf), slot_no) == size);
|
|
|
|
// do a quick sanity check on the object using sto api
|
|
ASSERT(sto_validate(buf, size) == 1);
|
|
|
|
// grab a field
|
|
ASSERT(sto_subfield(buf, size, sfBalance) > 0);
|
|
|
|
// subslot a subfield we can return as an int64_t
|
|
ASSERT(slot_subfield(slot_no, sfBalance, 200) == 200);
|
|
|
|
// retrieve the slotted object as an int64_t
|
|
ASSERT(slot(0,0,200) > 0);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_clear()
|
|
{
|
|
testcase("Test slot_clear");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot_clear(uint32_t slot_no);
|
|
extern int64_t otxn_slot (uint32_t slot_no);
|
|
extern int64_t slot_size(uint32_t);
|
|
#define sfBalance ((6U << 16U) + 2U)
|
|
#define sfFlags ((2U << 16U) + 2U)
|
|
#define DOESNT_EXIST -5
|
|
#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);
|
|
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_clear(1) == 1);
|
|
|
|
ASSERT(slot_size(1) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_clear(1) == DOESNT_EXIST);
|
|
ASSERT(slot_clear(10) == DOESNT_EXIST);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_clear"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_clear"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_count()
|
|
{
|
|
testcase("Test slot_count");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 otxn_slot (uint32_t slot_no);
|
|
extern int64_t slot_size(uint32_t);
|
|
extern int64_t slot_count(uint32_t);
|
|
extern int64_t slot_subfield (
|
|
uint32_t parent_slot,
|
|
uint32_t field_id,
|
|
uint32_t new_slot);
|
|
#define NOT_AN_ARRAY -22
|
|
#define DOESNT_EXIST -5
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define sfMemos ((15U << 16U) + 9U)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_count(1) == NOT_AN_ARRAY);
|
|
|
|
ASSERT(slot_count(0) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_subfield(1, sfMemos, 1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_count(1) == 1);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_count"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_count"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_float()
|
|
{
|
|
testcase("Test slot_float");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 otxn_slot (uint32_t slot_no);
|
|
extern int64_t slot_size(uint32_t);
|
|
extern int64_t slot_count(uint32_t);
|
|
extern int64_t slot_float(uint32_t);
|
|
extern int64_t float_int (
|
|
int64_t float1,
|
|
uint32_t decimal_places,
|
|
uint32_t absolute
|
|
);
|
|
extern int64_t slot_subfield (
|
|
uint32_t parent_slot,
|
|
uint32_t field_id,
|
|
uint32_t new_slot);
|
|
#define NOT_AN_ARRAY -22
|
|
#define DOESNT_EXIST -5
|
|
#define NOT_AN_AMOUNT -32
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define sfFee ((6U << 16U) + 8U)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(otxn_slot(1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_subfield(1, sfFee, 2) == 2);
|
|
|
|
ASSERT(slot_size(2) > 0);
|
|
|
|
ASSERT(slot_float(0) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_float(1) == NOT_AN_AMOUNT);
|
|
|
|
int64_t xfl = slot_float(2);
|
|
ASSERT(xfl > 0);
|
|
|
|
ASSERT(float_int(xfl, 6, 0) == 1000000LL);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_float"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_float"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_set()
|
|
{
|
|
testcase("Test slot_set");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 otxn_id(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_size(uint32_t);
|
|
#define sfBalance ((6U << 16U) + 2U)
|
|
#define sfFlags ((2U << 16U) + 2U)
|
|
#define DOESNT_EXIST -5
|
|
#define OUT_OF_BOUNDS -1
|
|
#define INVALID_ARGUMENT -7
|
|
#define NO_FREE_SLOTS -6
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
|
|
// skip keylet
|
|
uint8_t kl_sk[] =
|
|
{
|
|
0x00U, 0x68U,
|
|
0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,0xD5U,0xC3U,
|
|
0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,0xDAU,0x87U,
|
|
0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,0x2AU,0xB0U,
|
|
0xD6U,0x6BU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// bounds check
|
|
ASSERT(slot_set(1, 1000000, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(slot_set(1000000, 1024, 0) == OUT_OF_BOUNDS);
|
|
|
|
// read len is only allowed to be 32 (txn id) or 34 (keylet)
|
|
uint8_t kl_zero[34];
|
|
ASSERT(slot_set((uint32_t)kl_zero, 0, 0) == INVALID_ARGUMENT);
|
|
ASSERT(slot_set((uint32_t)kl_zero, 31, 0) == INVALID_ARGUMENT);
|
|
ASSERT(slot_set((uint32_t)kl_zero, 33, 0) == INVALID_ARGUMENT);
|
|
ASSERT(slot_set((uint32_t)kl_zero, 35, 0) == INVALID_ARGUMENT);
|
|
ASSERT(slot_set((uint32_t)kl_zero, 34, 256) == INVALID_ARGUMENT);
|
|
|
|
// request an invalid keylet
|
|
ASSERT(slot_set(SBUF(kl_zero), 0) == DOESNT_EXIST);
|
|
kl_zero[0] = 1;
|
|
ASSERT(slot_set(SBUF(kl_zero), 0) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_size(1) == DOESNT_EXIST);
|
|
// request a valid keylet
|
|
ASSERT(slot_set(SBUF(kl_sk), 0) > 0);
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
// fill up all slots
|
|
for (uint32_t i = 1; GUARD(257), i < 255; ++i)
|
|
ASSERT(slot_set(SBUF(kl_sk), 0) > 0);
|
|
|
|
// request a final slot that should fail
|
|
ASSERT(slot_set(SBUF(kl_sk), 0));
|
|
|
|
// overwrite an existing slot, should work
|
|
ASSERT(slot_set(SBUF(kl_sk), 10) == 10);
|
|
|
|
// check all the slots contain an object, the same object
|
|
uint32_t s = slot_size(1);
|
|
|
|
for (uint32_t i = 2; GUARD(257), i < 256; ++i)
|
|
ASSERT(s == slot_size(i));
|
|
|
|
// slot a txn
|
|
|
|
uint8_t txn[32];
|
|
ASSERT(otxn_id(SBUF(txn), 0) == 32);
|
|
ASSERT(slot_set(SBUF(txn), 1) == 1);
|
|
|
|
uint32_t s2 = slot_size(1);
|
|
|
|
// ensure it's not the same object that was there before
|
|
ASSERT(s != s2 && s2 > 0);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_set"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_set"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_size()
|
|
{
|
|
testcase("Test slot_size");
|
|
using namespace jtx;
|
|
Env env{*this, supported_amendments()};
|
|
|
|
Account const alice{"alice"};
|
|
Account const bob{"bob"};
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
|
|
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 slot_set(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_size(uint32_t);
|
|
extern int64_t slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t sto_validate(uint32_t, uint32_t);
|
|
#define sfBalance ((6U << 16U) + 2U)
|
|
#define sfFlags ((2U << 16U) + 2U)
|
|
#define DOESNT_EXIST -5
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
|
|
// skip keylet
|
|
uint8_t kl_sk[] =
|
|
{
|
|
0x00U, 0x68U,
|
|
0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,0xD5U,0xC3U,
|
|
0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,0xDAU,0x87U,
|
|
0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,0x2AU,0xB0U,
|
|
0xD6U,0x6BU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(slot_size(1) == DOESNT_EXIST);
|
|
|
|
// request a valid keylet, twice
|
|
ASSERT(slot_set(SBUF(kl_sk), 1) == 1);
|
|
ASSERT(slot_set(SBUF(kl_sk), 255) == 255);
|
|
|
|
// check the sizes are equal
|
|
ASSERT(slot_size(1) == slot_size(255));
|
|
|
|
// check the sizes are > 0
|
|
int64_t s = slot_size(1);
|
|
ASSERT(s > 0);
|
|
|
|
// pull the object out into a buffer, check the number of bytes written is correct
|
|
uint8_t buf[4096];
|
|
ASSERT(slot(SBUF(buf), 1) == s);
|
|
|
|
// check the object is valid
|
|
ASSERT(sto_validate(buf, s) == 1);
|
|
|
|
// done!
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_size"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_size"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_subarray()
|
|
{
|
|
testcase("Test slot_subarray");
|
|
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);
|
|
|
|
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 slot_size(uint32_t);
|
|
extern int64_t slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t slot_subarray(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_slot(uint32_t);
|
|
extern int64_t slot_subfield(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_count(uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
#define sfMemos ((15U << 16U) + 9U)
|
|
#define sfMemoData ((7U << 16U) + 13U)
|
|
|
|
#define DOESNT_EXIST -5
|
|
#define NO_FREE_SLOTS -6
|
|
#define NOT_AN_ARRAY -22
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
// skip keylet
|
|
uint8_t kl_sk[] =
|
|
{
|
|
0x00U, 0x68U,
|
|
0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,0xD5U,0xC3U,
|
|
0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,0xDAU,0x87U,
|
|
0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,0x2AU,0xB0U,
|
|
0xD6U,0x6BU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(slot_subarray(1, 1, 1) == DOESNT_EXIST);
|
|
|
|
// request a valid keylet that doesn't contain an array
|
|
ASSERT(slot_set(SBUF(kl_sk), 1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_subarray(1,1,1) == NOT_AN_ARRAY);
|
|
|
|
// now request an object that contains an array (this txn)
|
|
ASSERT(otxn_slot(2) == 2);
|
|
|
|
// slot the array
|
|
ASSERT(slot_subfield(2, sfMemos, 3) == 3);
|
|
|
|
// it should contain 9 entries
|
|
ASSERT(slot_count(3) == 9);
|
|
|
|
// now index into the array
|
|
ASSERT(slot_subarray(3, 0, 0) > 0);
|
|
|
|
// take element at index 5 and place it in slot 100
|
|
ASSERT(slot_subarray(3, 5, 100) == 100);
|
|
|
|
// override it and replace with element 6
|
|
ASSERT(slot_subarray(3, 6, 100) == 100);
|
|
|
|
// check the value is correct
|
|
ASSERT(slot_subfield(100, sfMemoData, 100) == 100);
|
|
|
|
uint8_t buf[16];
|
|
|
|
ASSERT(6 == slot(SBUF(buf), 100));
|
|
|
|
ASSERT(
|
|
buf[0] == 0x05U &&
|
|
buf[1] == 0xC0U && buf[2] == 0x01U && buf[3] == 0xCAU && buf[4] == 0xFEU && buf[5] == 0x06U);
|
|
|
|
// override it and replace with element 0
|
|
ASSERT(slot_subarray(3, 0, 100) == 100);
|
|
|
|
// check the value is correct
|
|
ASSERT(slot_subfield(100, sfMemoData, 100) == 100);
|
|
|
|
ASSERT(slot(SBUF(buf), 100) == 6);
|
|
|
|
ASSERT(
|
|
buf[0] == 0x05U &&
|
|
buf[1] == 0xC0U && buf[2] == 0x01U && buf[3] == 0xCAU && buf[4] == 0xFEU && buf[5] == 0x00U);
|
|
|
|
// test slot exhaustion
|
|
for (int i = 0; GUARD(255), i < 250; ++i)
|
|
ASSERT(slot_subarray(3, 0, 0) > 0);
|
|
|
|
ASSERT(slot_subarray(3, 0, 0) == NO_FREE_SLOTS);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_subarray"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// generate an array of memos to attach
|
|
Json::Value jv;
|
|
jv[jss::Account] = bob.human();
|
|
jv[jss::TransactionType] = jss::Payment;
|
|
jv[jss::Flags] = 0;
|
|
jv[jss::Amount] = "1";
|
|
jv[jss::Memos] = Json::Value{Json::arrayValue};
|
|
jv[jss::Destination] = alice.human();
|
|
Json::Value iv;
|
|
for (uint32_t i = 0; i < 8; ++i)
|
|
{
|
|
std::string v = "C001CAFE00";
|
|
v.data()[9] = '0' + i;
|
|
iv[jss::MemoData] = v.c_str();
|
|
iv[jss::MemoFormat] = "";
|
|
iv[jss::MemoType] = "";
|
|
jv[jss::Memos][i][jss::Memo] = iv;
|
|
}
|
|
|
|
// invoke the hook
|
|
env(jv, M("test slot_subarray"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_subfield()
|
|
{
|
|
testcase("Test slot_subfield");
|
|
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);
|
|
|
|
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 slot_size(uint32_t);
|
|
extern int64_t slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t otxn_slot(uint32_t);
|
|
extern int64_t slot_subfield(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_count(uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
#define sfMemos ((15U << 16U) + 9U)
|
|
#define sfMemoData ((7U << 16U) + 13U)
|
|
#define sfLastLedgerSequence ((2U << 16U) + 0x1BU)
|
|
#define sfHashes ((19U << 16U) + 2U)
|
|
#define NOT_AN_OBJECT -23
|
|
#define DOESNT_EXIST -5
|
|
#define NO_FREE_SLOTS -6
|
|
#define INVALID_FIELD -17
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
// skip keylet
|
|
uint8_t kl_sk[] =
|
|
{
|
|
0x00U, 0x68U,
|
|
0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,0xD5U,0xC3U,
|
|
0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,0xDAU,0x87U,
|
|
0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,0x2AU,0xB0U,
|
|
0xD6U,0x6BU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(slot_subfield(1, 1, 1) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_set(SBUF(kl_sk), 1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_subfield(1, sfLastLedgerSequence, 0) == 2);
|
|
|
|
ASSERT(slot_size(2) >0);
|
|
ASSERT(slot_size(1) > slot_size(2));
|
|
|
|
ASSERT(slot_subfield(1, sfHashes, 0) == 3);
|
|
|
|
ASSERT(slot_size(3) > 0);
|
|
ASSERT(slot_size(1) > slot_size(3));
|
|
|
|
// request a field that is invalid
|
|
ASSERT(slot_subfield(1, 0xFFFFFFFFUL, 0) == INVALID_FIELD);
|
|
|
|
// request a field that isn't present
|
|
ASSERT(slot_subfield(1, sfMemos, 0) == DOESNT_EXIST);
|
|
|
|
// request a subfield from something that's not an object
|
|
ASSERT(slot_subfield(3, sfMemoData, 0) == NOT_AN_OBJECT);
|
|
|
|
// overwrite an existing slot
|
|
ASSERT(slot_subfield(1, sfLastLedgerSequence, 3) == 3);
|
|
ASSERT(slot_size(2) == slot_size(3));
|
|
|
|
// test slot exhaustion
|
|
for (int i = 0; GUARD(255), i < 252; ++i)
|
|
ASSERT(slot_subfield(1, sfLastLedgerSequence, 0) > 0);
|
|
|
|
ASSERT(slot_subfield(1, sfLastLedgerSequence, 0) == NO_FREE_SLOTS);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_subfield"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_subfield"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_slot_type()
|
|
{
|
|
testcase("Test slot_type");
|
|
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);
|
|
|
|
// set up a trustline which we can retrieve later
|
|
env(trust(alice, bob["USD"](600)));
|
|
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 slot_size(uint32_t);
|
|
extern int64_t slot (uint32_t write_ptr, uint32_t write_len, uint32_t slot_no);
|
|
extern int64_t otxn_slot(uint32_t);
|
|
extern int64_t slot_subfield(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t slot_count(uint32_t);
|
|
extern int64_t slot_type(uint32_t, uint32_t);
|
|
extern int64_t slot_set(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_field (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t field_id
|
|
);
|
|
extern int64_t util_keylet (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t keylet_type,
|
|
uint32_t a,
|
|
uint32_t b,
|
|
uint32_t c,
|
|
uint32_t d,
|
|
uint32_t e,
|
|
uint32_t f
|
|
);
|
|
extern int64_t hook_account(uint32_t, uint32_t);
|
|
#define sfMemos ((15U << 16U) + 9U)
|
|
#define sfMemoData ((7U << 16U) + 13U)
|
|
#define sfLastLedgerSequence ((2U << 16U) + 0x1BU)
|
|
#define sfHashes ((19U << 16U) + 2U)
|
|
#define NOT_AN_OBJECT -23
|
|
#define DOESNT_EXIST -5
|
|
#define NO_FREE_SLOTS -6
|
|
#define INVALID_FIELD -17
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
// skip keylet
|
|
uint8_t kl_sk[] =
|
|
{
|
|
0x00U, 0x68U,
|
|
0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,0xD5U,0xC3U,
|
|
0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,0xDAU,0x87U,
|
|
0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,0x2AU,0xB0U,
|
|
0xD6U,0x6BU
|
|
};
|
|
#define sfLedgerEntry ((10002U << 16U) + 257U)
|
|
#define sfTransaction ((10001U << 16U) + 257U)
|
|
#define sfAmount ((6U << 16U) + 1U)
|
|
#define sfHighLimit ((6U << 16U) + 7U)
|
|
#define sfAccount ((8U << 16U) + 1U)
|
|
#define NOT_AN_AMOUNT -32
|
|
#define KEYLET_LINE 9
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
ASSERT(slot_type(1, 0) == DOESNT_EXIST);
|
|
|
|
ASSERT(slot_set(SBUF(kl_sk), 1) == 1);
|
|
|
|
ASSERT(slot_size(1) > 0);
|
|
|
|
ASSERT(slot_type(1, 0) == sfLedgerEntry);
|
|
|
|
ASSERT(slot_subfield(1, sfLastLedgerSequence, 0) == 2);
|
|
|
|
ASSERT(slot_size(2) >0);
|
|
|
|
ASSERT(slot_size(1) > slot_size(2));
|
|
|
|
ASSERT(slot_type(2, 0) == sfLastLedgerSequence);
|
|
|
|
ASSERT(otxn_slot(3) == 3);
|
|
|
|
ASSERT(slot_type(3, 0) == sfTransaction);
|
|
|
|
ASSERT(slot_subfield(3, sfAmount, 4) == 4);
|
|
|
|
// this will determine if the amount is native by returning 1 if it is
|
|
ASSERT(slot_type(4, 1) == 1);
|
|
|
|
ASSERT(slot_type(3, 1) == NOT_AN_AMOUNT);
|
|
|
|
// there's a trustline between alice and bob
|
|
// we can find alice and bob's addresses from otxn
|
|
uint8_t addra[20];
|
|
uint8_t addrb[20];
|
|
ASSERT(hook_account(SBUF(addra)) == 20);
|
|
ASSERT(otxn_field(SBUF(addrb), sfAccount) == 20);
|
|
|
|
// build the keylet for the tl
|
|
uint8_t kl_tr[34];
|
|
ASSERT(util_keylet(SBUF(kl_tr), KEYLET_LINE, SBUF(addra), SBUF(addrb), "USD", 3) == 34);
|
|
|
|
// slot the ripplestate object
|
|
ASSERT(slot_set(SBUF(kl_tr), 5) == 5);
|
|
|
|
// subfield into the high limit
|
|
ASSERT(slot_subfield(5, sfHighLimit, 6) == 6);
|
|
|
|
// this is a non-native balance so we should get 0 back when testing the amount type
|
|
ASSERT(slot_type(6, 1) == 0);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set slot_subfield"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test slot_type"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_state()
|
|
{
|
|
testcase("Test state");
|
|
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);
|
|
|
|
{
|
|
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 (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len
|
|
);
|
|
extern int64_t state_set(uint32_t,uint32_t,uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// set a state object
|
|
ASSERT(state_set(SBUF("content"), SBUF("key")) == sizeof("content"));
|
|
ASSERT(state_set(SBUF("content2"), SBUF("key2")) == sizeof("content2"));
|
|
|
|
// Test out of bounds check
|
|
ASSERT(state(1000000, 32, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(state(0, 1000000, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(state(0, 32, 1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(state(0, 32, 0, 1000000) == TOO_BIG);
|
|
ASSERT(state(0,0,0,0) == TOO_SMALL);
|
|
ASSERT(state(0,0,0,33) == TOO_BIG);
|
|
|
|
// read state back
|
|
uint8_t buf1[32];
|
|
uint8_t buf2[32];
|
|
|
|
int64_t bytes1 = state(SBUF(buf1), SBUF("key"));
|
|
ASSERT(bytes1 == sizeof("content"));
|
|
|
|
int64_t bytes2 = state(SBUF(buf2), SBUF("key2"));
|
|
ASSERT(bytes2 == sizeof("content2"));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes1; ++i)
|
|
ASSERT(buf1[i] == *((uint8_t*)"content" + i));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes2; ++i)
|
|
ASSERT(buf2[i] == *((uint8_t*)"content2" + i));
|
|
|
|
// RH TODO:
|
|
// - read small state back as int64
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set state"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test state"), fee(XRP(1)));
|
|
env.close();
|
|
}
|
|
|
|
// override hook with a second version that just reads those state
|
|
// objects
|
|
{
|
|
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 (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len
|
|
);
|
|
extern int64_t state_set(uint32_t,uint32_t,uint32_t, uint32_t);
|
|
#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);
|
|
|
|
// read state back
|
|
uint8_t buf1[32];
|
|
uint8_t buf2[32];
|
|
|
|
int64_t bytes1 = state(SBUF(buf1), SBUF("key"));
|
|
ASSERT(bytes1 == sizeof("content"));
|
|
|
|
int64_t bytes2 = state(SBUF(buf2), SBUF("key2"));
|
|
ASSERT(bytes2 == sizeof("content2"));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes1; ++i)
|
|
ASSERT(buf1[i] == *((uint8_t*)"content" + i));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes2; ++i)
|
|
ASSERT(buf2[i] == *((uint8_t*)"content2" + i));
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set state 2"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test state 2"), fee(XRP(1)));
|
|
}
|
|
}
|
|
|
|
void
|
|
test_state_foreign()
|
|
{
|
|
testcase("Test state_foreign");
|
|
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);
|
|
|
|
{
|
|
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 (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len
|
|
);
|
|
extern int64_t state_foreign_set(
|
|
uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
extern int64_t hook_account(uint32_t, uint32_t);
|
|
extern int64_t state_set(uint32_t,uint32_t,uint32_t, uint32_t);
|
|
#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);
|
|
|
|
// set a state object
|
|
ASSERT(state_set(SBUF("content"), SBUF("key")) == sizeof("content"));
|
|
|
|
// put the second state object on a different ns
|
|
|
|
uint8_t ns[32];
|
|
ns[9] = 0xABU;
|
|
|
|
uint8_t acc[20];
|
|
ASSERT(hook_account(SBUF(acc)) == 20);
|
|
ASSERT(state_foreign_set(SBUF("content2"), SBUF("key2"), SBUF(ns), SBUF(acc)) == sizeof("content2"));
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set state_foreign"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test state_foreign"), fee(XRP(1)));
|
|
}
|
|
|
|
// set a second hook on bob that will read the state objects from alice
|
|
{
|
|
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 (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len
|
|
);
|
|
extern int64_t state_foreign (
|
|
uint32_t, uint32_t, uint32_t, uint32_t,
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
extern int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
|
|
extern int64_t state_set(uint32_t,uint32_t,uint32_t, uint32_t);
|
|
extern int64_t otxn_field(uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define DOESNT_EXIST (-5)
|
|
#define INVALID_ARGUMENT (-7)
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define sfAccount ((8U << 16U) + 1U)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
|
|
// Test out of bounds check
|
|
int64_t y;
|
|
ASSERT((y=state_foreign(1111111, 32, 1, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign(1, 1111111, 1, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign(1, 32, 1111111, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign(1, 32, 1, 1111111, 1, 32, 1, 20)) == TOO_BIG);
|
|
ASSERT((y=state_foreign(1, 32, 1, 32, 1111111, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign(1, 32, 1, 32, 1, 1111111, 1, 20)) == INVALID_ARGUMENT);
|
|
ASSERT((y=state_foreign(1, 32, 1, 32, 1, 32, 1111111, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign(1, 32, 1, 32, 1, 32, 1, 1111111)) == INVALID_ARGUMENT);
|
|
|
|
|
|
// alice's address is the sender
|
|
uint8_t acc[20];
|
|
ASSERT(otxn_field(SBUF(acc), sfAccount) == 20);
|
|
|
|
// read state back
|
|
uint8_t buf1[32];
|
|
uint8_t buf2[32];
|
|
|
|
// the namespace of the first obj is all zeros
|
|
uint8_t ns[32];
|
|
int64_t bytes1 = state_foreign(SBUF(buf1), SBUF("key"), SBUF(ns), SBUF(acc));
|
|
ASSERT(bytes1 == sizeof("content"));
|
|
|
|
// the namespace of the second obj is all zeros except position 9 which is 0xAB
|
|
// ensure the namespacing is working by requesting against the wrong namespace first
|
|
int64_t bytes2 = state_foreign(SBUF(buf2), SBUF("key2"), SBUF(ns), SBUF(acc));
|
|
ASSERT(bytes2 == DOESNT_EXIST);
|
|
ns[9] = 0xABU;
|
|
bytes2 = state_foreign(SBUF(buf2), SBUF("key2"), SBUF(ns), SBUF(acc));
|
|
ASSERT(bytes2 == sizeof("content2"));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes1; ++i)
|
|
ASSERT(buf1[i] == *((uint8_t*)"content" + i));
|
|
|
|
for (int i = 32; GUARD(32), i < bytes2; ++i)
|
|
ASSERT(buf2[i] == *((uint8_t*)"content2" + i));
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on bob
|
|
env(ripple::test::jtx::hook(bob, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set state_foreign 2"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
|
|
env(pay(alice, bob, XRP(1)),
|
|
M("test state_foreign 2"),
|
|
fee(XRP(1)));
|
|
}
|
|
}
|
|
|
|
void
|
|
test_state_foreign_set()
|
|
{
|
|
testcase("Test state_foreign_set");
|
|
using namespace jtx;
|
|
|
|
Env env{*this, supported_amendments()};
|
|
// Env env{*this, envconfig(), supported_amendments(), nullptr,
|
|
// beast::severities::kWarning
|
|
// beast::severities::kTrace
|
|
// };
|
|
|
|
auto const david = Account("david"); // grantee generic
|
|
auto const cho = Account{"cho"}; // invoker
|
|
auto const bob = Account{"bob"}; // grantee specific
|
|
auto const alice = Account{"alice"}; // grantor
|
|
auto const eve = Account{"eve"}; // grantor with small balance
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
env.fund(XRP(10000), cho);
|
|
env.fund(XRP(10000), david);
|
|
env.fund(XRP(2600), eve);
|
|
|
|
TestHook grantee_wasm = wasm[R"[test.hook](
|
|
#include <stdint.h>
|
|
#define sfInvoiceID ((5U << 16U) + 17U)
|
|
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_foreign (
|
|
uint32_t, uint32_t, uint32_t, uint32_t,
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
extern int64_t state_foreign_set (
|
|
uint32_t, uint32_t, uint32_t, uint32_t,
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
extern int64_t otxn_id(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_field(uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define TOO_BIG (-3)
|
|
#define TOO_SMALL (-4)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
#define DOESNT_EXIST (-5)
|
|
#define INVALID_ARGUMENT (-7)
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// bounds tests
|
|
int64_t y;
|
|
ASSERT((y=state_foreign_set(1111111, 32, 1, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign_set(1, 1111111, 1, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign_set(1, 32, 1111111, 32, 1, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign_set(1, 32, 1, 1111111, 1, 32, 1, 20)) == TOO_BIG);
|
|
ASSERT((y=state_foreign_set(1, 32, 1, 32, 1111111, 32, 1, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign_set(1, 32, 1, 32, 1, 1111111, 1, 20)) == INVALID_ARGUMENT);
|
|
ASSERT((y=state_foreign_set(1, 32, 1, 32, 1, 32, 1111111, 20)) == OUT_OF_BOUNDS);
|
|
ASSERT((y=state_foreign_set(1, 32, 1, 32, 1, 32, 1, 1111111)) == INVALID_ARGUMENT);
|
|
|
|
// get this transaction id
|
|
uint8_t txn[32];
|
|
ASSERT(otxn_id(SBUF(txn), 0) == 32);
|
|
|
|
// get the invoice id, which contains the grantor account
|
|
uint8_t grantor[32];
|
|
ASSERT(otxn_field(SBUF(grantor), sfInvoiceID) == 32);
|
|
|
|
// set the current txn id on the grantor's state under key 1, namespace 0
|
|
uint8_t one[32]; one[31] = 1U;
|
|
uint8_t zero[32];
|
|
ASSERT(state_foreign_set(SBUF(txn), SBUF(one), SBUF(zero), grantor + 12, 20) == 32);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(grantee);
|
|
|
|
// this is the grantor
|
|
TestHook grantor_wasm = 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 otxn_field (uint32_t, uint32_t, uint32_t);
|
|
extern int64_t hook_account(uint32_t write_ptr, uint32_t write_len);
|
|
extern int64_t state_foreign_set (
|
|
uint32_t, uint32_t, uint32_t, uint32_t,
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
#define DOESNT_EXIST (-5)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
#define BUFFER_EQUAL_20(buf1, buf2)\
|
|
(\
|
|
*(((uint64_t*)(buf1)) + 0) == *(((uint64_t*)(buf2)) + 0) &&\
|
|
*(((uint64_t*)(buf1)) + 1) == *(((uint64_t*)(buf2)) + 1) &&\
|
|
*(((uint32_t*)(buf1)) + 4) == *(((uint32_t*)(buf2)) + 4))
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
|
|
#define sfAccount ((8U << 16U) + 1U)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t otxnacc[20];
|
|
ASSERT(otxn_field(SBUF(otxnacc), sfAccount) == 20);
|
|
|
|
uint8_t hookacc[20];
|
|
ASSERT(hook_account(SBUF(hookacc)) == 20);
|
|
|
|
if (BUFFER_EQUAL_20(otxnacc, hookacc))
|
|
{
|
|
// outgoin txn, delete the state
|
|
uint8_t one[32]; one[31] = 1U;
|
|
uint8_t zero[32];
|
|
// we can use state_foreign_set to do the deletion, a concise way to test this functionality too
|
|
int64_t y = state_foreign_set(0,0, SBUF(one), SBUF(zero), SBUF(hookacc));
|
|
ASSERT(y == 0 || y == DOESNT_EXIST);
|
|
}
|
|
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(grantor);
|
|
|
|
// install the grantor hook on alice
|
|
{
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = grantee_hash_str;
|
|
grants[0U][jss::HookGrant][jss::Authorize] = bob.human();
|
|
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
alice, {{hso(grantor_wasm, overrideFlag)}}, 0);
|
|
json[jss::Hooks][0U][jss::Hook][jss::HookGrants] = grants;
|
|
|
|
env(json, M("set state_foreign_set"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// install the grantee hook on bob
|
|
{
|
|
// invoice ID contains the grantor account
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
bob, {{hso(grantee_wasm, overrideFlag)}}, 0);
|
|
env(json, M("set state_foreign_set 2"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
auto const aliceid = Account("alice").id();
|
|
auto const nsdirkl = keylet::hookStateDir(aliceid, beast::zero);
|
|
|
|
std::string const invid = std::string(24, '0') + strHex(alice.id());
|
|
|
|
auto const one = ripple::uint256(
|
|
"0000000000000000000000000000000000000000000000000000000000000001");
|
|
|
|
// ensure there's no way the state or directory exist before we start
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!nsdir);
|
|
|
|
auto const state1 =
|
|
env.le(ripple::keylet::hookState(aliceid, one, beast::zero));
|
|
BEAST_REQUIRE(!state1);
|
|
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 2);
|
|
}
|
|
|
|
// inovke the grantee hook but supply an account to foreign_set (through
|
|
// invoiceid) should definitely fail
|
|
{
|
|
Json::Value json = pay(cho, bob, XRP(1));
|
|
json[jss::InvoiceID] =
|
|
"00000000000000000000000000000000000000000000000000000000000000"
|
|
"01";
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 6a"),
|
|
ter(tecHOOK_REJECTED));
|
|
}
|
|
|
|
// invoke the grantee hook but supply a valid account for which no
|
|
// grants exist
|
|
{
|
|
Json::Value json = pay(cho, bob, XRP(1));
|
|
json[jss::InvoiceID] = std::string(24, '0') + strHex(david.id());
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 6b"),
|
|
ter(tecHOOK_REJECTED));
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!nsdir);
|
|
|
|
auto const state1 = env.le(
|
|
ripple::keylet::hookState(david.id(), one, beast::zero));
|
|
BEAST_REQUIRE(!state1);
|
|
|
|
BEAST_EXPECT((*env.le("david"))[sfOwnerCount] == 0);
|
|
}
|
|
}
|
|
|
|
// invoke the grantee hook, this will create the state on the grantor
|
|
{
|
|
Json::Value json = pay(cho, bob, XRP(1));
|
|
json[jss::InvoiceID] = invid;
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 6"),
|
|
ter(tesSUCCESS));
|
|
}
|
|
|
|
// check state
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!!nsdir);
|
|
|
|
auto const state1 =
|
|
env.le(ripple::keylet::hookState(aliceid, one, beast::zero));
|
|
BEAST_REQUIRE(!!state1);
|
|
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
|
|
|
BEAST_EXPECT(state1->getFieldH256(sfHookStateKey) == one);
|
|
|
|
auto const data1 = state1->getFieldVL(sfHookStateData);
|
|
BEAST_EXPECT(data1.size() == 32);
|
|
BEAST_EXPECT(uint256::fromVoid(data1.data()) == env.txid());
|
|
}
|
|
|
|
// invoke the grantor hook, this will delete the state
|
|
env(pay(alice, cho, XRP(1)),
|
|
M("test state_foreign_set 4"),
|
|
fee(XRP(1)));
|
|
|
|
// check state was removed
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!nsdir);
|
|
|
|
auto const state1 =
|
|
env.le(ripple::keylet::hookState(aliceid, one, beast::zero));
|
|
BEAST_REQUIRE(!state1);
|
|
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 2);
|
|
}
|
|
|
|
// install grantee hook on david
|
|
{
|
|
// invoice ID contains the grantor account
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
david, {{hso(grantee_wasm, overrideFlag)}}, 0);
|
|
env(json, M("set state_foreign_set 5"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// invoke daivd, expect failure
|
|
{
|
|
Json::Value json = pay(cho, david, XRP(1));
|
|
json[jss::InvoiceID] = invid;
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 6"),
|
|
ter(tecHOOK_REJECTED));
|
|
}
|
|
|
|
// remove sfAuthorize from alice grants
|
|
{
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = grantee_hash_str;
|
|
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
alice, {{hso(grantor_wasm, overrideFlag)}}, 0);
|
|
json[jss::Hooks][0U][jss::Hook][jss::HookGrants] = grants;
|
|
|
|
env(json, M("set state_foreign_set 7"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// invoke david again, expect success
|
|
{
|
|
Json::Value json = pay(cho, david, XRP(1));
|
|
json[jss::InvoiceID] = invid;
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 8"),
|
|
ter(tesSUCCESS));
|
|
}
|
|
|
|
// check state
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!!nsdir);
|
|
|
|
auto const state1 =
|
|
env.le(ripple::keylet::hookState(aliceid, one, beast::zero));
|
|
BEAST_REQUIRE(!!state1);
|
|
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
|
|
|
BEAST_EXPECT(state1->getFieldH256(sfHookStateKey) == one);
|
|
|
|
auto const data1 = state1->getFieldVL(sfHookStateData);
|
|
BEAST_EXPECT(data1.size() == 32);
|
|
BEAST_EXPECT(uint256::fromVoid(data1.data()) == env.txid());
|
|
}
|
|
|
|
// change alice's namespace
|
|
{
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
alice, {{hso(grantor_wasm, overrideFlag)}}, 0);
|
|
json[jss::Hooks][0U][jss::Hook][jss::HookNamespace] =
|
|
"77777777777777777777777777777777777777777777777777777777777777"
|
|
"77";
|
|
env(json, M("set state_foreign_set 9"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// invoke david again, expect failure
|
|
{
|
|
Json::Value json = pay(cho, david, XRP(1));
|
|
json[jss::InvoiceID] = invid;
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 10"),
|
|
ter(tecHOOK_REJECTED));
|
|
}
|
|
|
|
// check reserve exhaustion
|
|
TestHook exhaustion_wasm = wasm[R"[test.hook](
|
|
#include <stdint.h>
|
|
#define sfInvoiceID ((5U << 16U) + 17U)
|
|
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 otxn_field (uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_id(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t state_foreign_set (
|
|
uint32_t, uint32_t, uint32_t, uint32_t,
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
extern int64_t trace_num(uint32_t, uint32_t, int64_t);
|
|
extern int64_t trace(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), y);
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
uint8_t i;
|
|
int64_t y;
|
|
// get this transaction id
|
|
uint8_t txn[32];
|
|
ASSERT(otxn_id(SBUF(txn), 0) == 32);
|
|
|
|
trace(SBUF("txnid"), SBUF(txn), 1);
|
|
|
|
// get the invoice id, which contains the grantor account
|
|
uint8_t grantor[32];
|
|
ASSERT(otxn_field(SBUF(grantor), sfInvoiceID) == 32);
|
|
|
|
|
|
|
|
uint8_t iterations = grantor[0];
|
|
trace_num(SBUF("iterations"), iterations);
|
|
|
|
// set the current txn id on the grantor's state under key 1, namespace 0
|
|
uint8_t zero[32];
|
|
for (i = 0; GUARD(255), i < iterations; ++i)
|
|
{
|
|
txn[0] = i;
|
|
trace_num(SBUF("exhaustion i:"), i);
|
|
ASSERT((y=state_foreign_set(SBUF(txn), SBUF(txn), SBUF(zero), grantor + 12, 20)) == 32);
|
|
}
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(exhaustion);
|
|
|
|
// install the grantor hook on eve
|
|
{
|
|
Json::Value grants{Json::arrayValue};
|
|
grants[0U][jss::HookGrant] = Json::Value{};
|
|
grants[0U][jss::HookGrant][jss::HookHash] = exhaustion_hash_str;
|
|
grants[0U][jss::HookGrant][jss::Authorize] = bob.human();
|
|
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
eve, {{hso(grantor_wasm, overrideFlag)}}, 0);
|
|
json[jss::Hooks][0U][jss::Hook][jss::HookGrants] = grants;
|
|
|
|
env(json, M("set state_foreign_set 11"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// install exhaustion grantee on bob
|
|
{
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
bob, {{hso(exhaustion_wasm, overrideFlag)}}, 0);
|
|
env(json, M("set state_foreign_set 12"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// now invoke repeatedly until exhaustion is reached
|
|
{
|
|
Json::Value json = pay(cho, bob, XRP(1));
|
|
json[jss::InvoiceID] =
|
|
"01" + std::string(22, '0') + strHex(eve.id());
|
|
|
|
// 2500 xrp less 1 account reserve (200) divided by 50xrp per object
|
|
// reserve = 46 objects of these we already have: 1 hook, 1
|
|
// sfAuthorize, so 44 objects can be allocated
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 13"),
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 3);
|
|
|
|
// now we have allocated 1 state object, so 43 more can be allocated
|
|
|
|
// try to set 44 state entries, this will fail
|
|
json[jss::InvoiceID] =
|
|
"2C" + std::string(22, '0') + strHex(eve.id());
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 14"),
|
|
ter(tecHOOK_REJECTED));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 3);
|
|
|
|
// try to set 43 state objects, this will succeed
|
|
json[jss::InvoiceID] =
|
|
"2B" + std::string(22, '0') + strHex(eve.id());
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 15"),
|
|
ter(tesSUCCESS));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 46);
|
|
|
|
// try to set one state object, this will fail
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_foreign_set 16"),
|
|
ter(tecHOOK_REJECTED));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 46);
|
|
}
|
|
}
|
|
|
|
void
|
|
test_state_set()
|
|
{
|
|
testcase("Test state_set");
|
|
using namespace jtx;
|
|
|
|
Env env{*this, supported_amendments()};
|
|
|
|
auto const bob = Account{"bob"};
|
|
auto const alice = Account{"alice"};
|
|
auto const cho = Account{"cho"};
|
|
auto const david = Account{"david"};
|
|
auto const eve = Account{"eve"}; // small balance
|
|
auto const frank = Account{"frank"}; // big balance
|
|
env.fund(XRP(10000), alice);
|
|
env.fund(XRP(10000), bob);
|
|
env.fund(XRP(10000), cho);
|
|
env.fund(XRP(1000000), david);
|
|
env.fund(XRP(2600), eve);
|
|
env.fund(XRP(1000000000), frank);
|
|
|
|
// 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();
|
|
|
|
auto const nsdirkl = keylet::hookStateDir(aliceid, beast::zero);
|
|
|
|
// ensure there's no way the state or directory exist before we start
|
|
{
|
|
auto const nsdir = env.le(nsdirkl);
|
|
BEAST_REQUIRE(!nsdir);
|
|
|
|
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
|
|
{
|
|
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
|
|
);
|
|
|
|
uint8_t data2[128] =
|
|
{
|
|
0x23U,0x13U,0x96U,0x68U,0x78U,0xDCU,0xABU,0xC4U,0x40U,0x26U,
|
|
0x07U,0x2BU,0xA3U,0xD2U,0x0CU,0x69U,0x40U,0xDDU,0xCDU,0xE7U,
|
|
0x38U,0x9BU,0x0BU,0xA9U,0x6CU,0x3CU,0xB3U,0x87U,0x37U,0x02U,
|
|
0x81U,0xE8U,0x2BU,0xDDU,0x5DU,0xBBU,0x40U,0xD9U,0x66U,0x96U,
|
|
0x6FU,0xC1U,0x6BU,0xE8U,0xD4U,0x7CU,0x7BU,0x62U,0x14U,0x4CU,
|
|
0xD1U,0x4BU,0xAAU,0x99U,0x36U,0x75U,0xE9U,0x22U,0xADU,0x0FU,
|
|
0x5FU,0x94U,0x1DU,0x86U,0xEBU,0xA8U,0x13U,0x99U,0xF9U,0x98U,
|
|
0xFFU,0xCAU,0x5BU,0x86U,0x2FU,0xDFU,0x67U,0x8FU,0xE2U,0xE3U,
|
|
0xC3U,0x37U,0xCCU,0x47U,0x0FU,0x33U,0x88U,0xB0U,0x33U,0x3BU,
|
|
0x02U,0x55U,0x67U,0x16U,0xA4U,0xFBU,0x8EU,0x85U,0x6FU,0xD8U,
|
|
0x84U,0x16U,0xA3U,0x54U,0x18U,0x34U,0x06U,0x0EU,0xF6U,0x65U,
|
|
0x34U,0x05U,0x26U,0x7EU,0x05U,0x74U,0xDAU,0x09U,0xBFU,0x55U,
|
|
0x8CU,0x75U,0x92U,0xACU,0x33U,0xFBU,0x01U,0x8DU
|
|
};
|
|
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define TOO_SMALL (-4)
|
|
#define TOO_BIG (-3)
|
|
#define OUT_OF_BOUNDS (-1)
|
|
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
|
|
// bounds and buffer size checks
|
|
{
|
|
// RH NOTE: readptr/len 0/0 = delete entry
|
|
|
|
ASSERT(state_set(0,0,0,0) == TOO_SMALL);
|
|
ASSERT(state_set(0,0,0,33) == TOO_BIG);
|
|
ASSERT(state_set(0,0,0,1000000) == TOO_BIG);
|
|
ASSERT(state_set(0,0,1000000,1) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(state_set(0,1000000, 0, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(state_set(1000000, 0, 0, 32) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(state_set(0, 257, 0, 32) == TOO_BIG);
|
|
}
|
|
|
|
|
|
// create state 1
|
|
{
|
|
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,0
|
|
};
|
|
|
|
uint8_t data[4] =
|
|
{
|
|
0xCAU,0xFEU,0xBAU,0xBEU
|
|
};
|
|
|
|
|
|
ASSERT(state_set(SBUF(data), SBUF(key)) == sizeof(data));
|
|
}
|
|
|
|
// create state 2
|
|
{
|
|
uint8_t key[3] =
|
|
{
|
|
1,2,3
|
|
};
|
|
|
|
|
|
ASSERT(state_set(SBUF(data2), SBUF(key)) == sizeof(data2));
|
|
}
|
|
|
|
|
|
accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
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);
|
|
|
|
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();
|
|
}
|
|
|
|
// 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);
|
|
|
|
BEAST_EXPECT(nsdir->getFieldV256(sfIndexes).size() == 2);
|
|
|
|
auto const state1 = env.le(
|
|
ripple::keylet::hookState(aliceid, beast::zero, beast::zero));
|
|
BEAST_REQUIRE(!!state1);
|
|
|
|
BEAST_EXPECT(state1->getFieldH256(sfHookStateKey) == beast::zero);
|
|
|
|
auto const data1 = state1->getFieldVL(sfHookStateData);
|
|
BEAST_EXPECT(data1.size() == 4);
|
|
BEAST_EXPECT(
|
|
data1[0] == 0xCAU && data1[1] == 0xFEU && data1[2] == 0xBAU &&
|
|
data1[3] == 0xBEU);
|
|
|
|
uint8_t key2[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, 1, 2, 3};
|
|
|
|
auto const state2 = env.le(ripple::keylet::hookState(
|
|
aliceid, uint256::fromVoid(key2), beast::zero));
|
|
|
|
BEAST_REQUIRE(!!state2);
|
|
|
|
auto const lekey2 = state2->getFieldH256(sfHookStateKey);
|
|
|
|
BEAST_EXPECT(lekey2 == uint256::fromVoid(key2));
|
|
|
|
uint8_t data2[128] = {
|
|
0x23U, 0x13U, 0x96U, 0x68U, 0x78U, 0xDCU, 0xABU, 0xC4U, 0x40U,
|
|
0x26U, 0x07U, 0x2BU, 0xA3U, 0xD2U, 0x0CU, 0x69U, 0x40U, 0xDDU,
|
|
0xCDU, 0xE7U, 0x38U, 0x9BU, 0x0BU, 0xA9U, 0x6CU, 0x3CU, 0xB3U,
|
|
0x87U, 0x37U, 0x02U, 0x81U, 0xE8U, 0x2BU, 0xDDU, 0x5DU, 0xBBU,
|
|
0x40U, 0xD9U, 0x66U, 0x96U, 0x6FU, 0xC1U, 0x6BU, 0xE8U, 0xD4U,
|
|
0x7CU, 0x7BU, 0x62U, 0x14U, 0x4CU, 0xD1U, 0x4BU, 0xAAU, 0x99U,
|
|
0x36U, 0x75U, 0xE9U, 0x22U, 0xADU, 0x0FU, 0x5FU, 0x94U, 0x1DU,
|
|
0x86U, 0xEBU, 0xA8U, 0x13U, 0x99U, 0xF9U, 0x98U, 0xFFU, 0xCAU,
|
|
0x5BU, 0x86U, 0x2FU, 0xDFU, 0x67U, 0x8FU, 0xE2U, 0xE3U, 0xC3U,
|
|
0x37U, 0xCCU, 0x47U, 0x0FU, 0x33U, 0x88U, 0xB0U, 0x33U, 0x3BU,
|
|
0x02U, 0x55U, 0x67U, 0x16U, 0xA4U, 0xFBU, 0x8EU, 0x85U, 0x6FU,
|
|
0xD8U, 0x84U, 0x16U, 0xA3U, 0x54U, 0x18U, 0x34U, 0x06U, 0x0EU,
|
|
0xF6U, 0x65U, 0x34U, 0x05U, 0x26U, 0x7EU, 0x05U, 0x74U, 0xDAU,
|
|
0x09U, 0xBFU, 0x55U, 0x8CU, 0x75U, 0x92U, 0xACU, 0x33U, 0xFBU,
|
|
0x01U, 0x8DU};
|
|
|
|
auto const ledata2 = state2->getFieldVL(sfHookStateData);
|
|
BEAST_REQUIRE(ledata2.size() == sizeof(data2));
|
|
|
|
for (uint32_t i = 0; i < sizeof(data2); ++i)
|
|
BEAST_EXPECT(data2[i] == ledata2[i]);
|
|
}
|
|
|
|
// make amother hook to override an existing state and delete an
|
|
// existing state
|
|
{
|
|
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
|
|
);
|
|
|
|
#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);
|
|
|
|
|
|
// override state 1
|
|
{
|
|
|
|
uint8_t data[16] =
|
|
{
|
|
1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,2
|
|
};
|
|
|
|
|
|
uint8_t zero = 0;
|
|
|
|
ASSERT(state_set(SBUF(data), &zero, 1) == sizeof(data));
|
|
}
|
|
|
|
// delete state 2
|
|
{
|
|
uint8_t key2[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,1,2,3
|
|
};
|
|
uint8_t zero[1] = {0};
|
|
ASSERT(state_set(0,0, key2, 32) == 0);
|
|
}
|
|
|
|
accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
|
|
TestHook hook2 = 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 (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len
|
|
);
|
|
|
|
#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);
|
|
|
|
// verify updated state
|
|
{
|
|
|
|
uint8_t data[16] =
|
|
{
|
|
1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,2
|
|
};
|
|
|
|
uint8_t data_read[16];
|
|
uint8_t zero = 0;
|
|
|
|
ASSERT(state(SBUF(data_read), &zero, 1) == sizeof(data));
|
|
|
|
for (uint32_t i = 0; GUARD(16), i < 16; ++i)
|
|
ASSERT(data[i] == data_read[i]);
|
|
}
|
|
|
|
accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(
|
|
alice,
|
|
{{{hso(hook, overrideFlag)}, {}, {}, {hso(hook2, 0)}}},
|
|
0),
|
|
M("set state_set 2"),
|
|
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 first if bob's is the
|
|
// otxn. therefore we will flip to a payment from alice to bob here
|
|
TestHook hook3 = 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 hook_again(void);
|
|
extern int64_t state_foreign (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t kread_ptr,
|
|
uint32_t kread_len,
|
|
uint32_t nread_ptr,
|
|
uint32_t nread_len,
|
|
uint32_t aread_ptr,
|
|
uint32_t aread_len
|
|
);
|
|
extern int64_t otxn_field(uint32_t, uint32_t, uint32_t);
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define sfAccount ((8U << 16U) + 1U)
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
hook_again(); // we're going to check in weak execution too
|
|
|
|
uint8_t alice[20];
|
|
ASSERT(otxn_field(SBUF(alice), sfAccount) == 20);
|
|
|
|
// verify updated state
|
|
{
|
|
|
|
uint8_t data[16] =
|
|
{
|
|
1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,2
|
|
};
|
|
|
|
uint8_t data_read[16];
|
|
uint8_t zero[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,0
|
|
};
|
|
|
|
ASSERT(state_foreign(SBUF(data_read), SBUF(zero), SBUF(zero), SBUF(alice)) == sizeof(data));
|
|
|
|
for (uint32_t i = 0; GUARD(16), i < 16; ++i)
|
|
ASSERT(data[i] == data_read[i]);
|
|
}
|
|
|
|
accept(0,0,0);
|
|
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on bob
|
|
env(ripple::test::jtx::hook(bob, {{hso(hook3, overrideFlag)}}, 0),
|
|
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)));
|
|
env.close();
|
|
}
|
|
|
|
// 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);
|
|
|
|
BEAST_EXPECT(nsdir->getFieldV256(sfIndexes).size() == 1);
|
|
|
|
auto const state1 = env.le(
|
|
ripple::keylet::hookState(aliceid, beast::zero, beast::zero));
|
|
BEAST_REQUIRE(!!state1);
|
|
|
|
BEAST_EXPECT(state1->getFieldH256(sfHookStateKey) == beast::zero);
|
|
|
|
auto const ledata1 = state1->getFieldVL(sfHookStateData);
|
|
BEAST_EXPECT(ledata1.size() == 16);
|
|
uint8_t data1[16] = {
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2};
|
|
|
|
for (uint32_t i = 0; i < sizeof(data1); ++i)
|
|
BEAST_EXPECT(data1[i] == ledata1[i]);
|
|
|
|
uint8_t key2[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, 1, 2, 3};
|
|
|
|
auto const state2 = env.le(ripple::keylet::hookState(
|
|
aliceid, uint256::fromVoid(key2), beast::zero));
|
|
|
|
BEAST_REQUIRE(!state2);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// delete alice's hook
|
|
env(ripple::test::jtx::hook(
|
|
alice, {{{hso_delete()}, {}, {}, {}}}, 0),
|
|
M("set state_set 5 delete"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// check the state is still present
|
|
{
|
|
auto const state = env.le(ripple::keylet::hookState(
|
|
aliceid, uint256::fromVoid(key), beast::zero));
|
|
BEAST_EXPECT(state);
|
|
}
|
|
|
|
// zero hooks + two state objects == 2
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 2);
|
|
|
|
// put on a different hook
|
|
|
|
env(ripple::test::jtx::hook(
|
|
alice, {{hso(rollback_wasm, overrideFlag)}}, 0),
|
|
M("set state_set rollback2"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// check the state is still present
|
|
{
|
|
auto const state = env.le(ripple::keylet::hookState(
|
|
aliceid, uint256::fromVoid(key), beast::zero));
|
|
BEAST_EXPECT(state);
|
|
}
|
|
|
|
// one hooks + two state objects == 3
|
|
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 3);
|
|
}
|
|
|
|
// check reserve exhaustion
|
|
TestHook exhaustion_wasm = wasm[R"[test.hook](
|
|
#include <stdint.h>
|
|
#define sfInvoiceID ((5U << 16U) + 17U)
|
|
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 otxn_field (uint32_t, uint32_t, uint32_t);
|
|
extern int64_t otxn_id(uint32_t, uint32_t, uint32_t);
|
|
extern int64_t state_set (
|
|
uint32_t, uint32_t, uint32_t, uint32_t
|
|
);
|
|
extern int64_t hook_pos(void);
|
|
#define SBUF(x) (uint32_t)(x), sizeof(x)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// get this transaction id
|
|
uint8_t txn[32];
|
|
ASSERT(otxn_id(SBUF(txn), 0) == 32);
|
|
|
|
// get the invoice id, which contains the grantor account
|
|
uint8_t grantor[32];
|
|
ASSERT(otxn_field(SBUF(grantor), sfInvoiceID) == 32);
|
|
|
|
uint16_t iterations = (((uint16_t)grantor[0]) << 8U) + ((uint16_t)grantor[1]);
|
|
|
|
uint8_t p = hook_pos();
|
|
for (uint16_t i = 0; GUARD(1500), i < iterations; ++i)
|
|
{
|
|
*((uint16_t*)txn) = i;
|
|
txn[2] = (uint8_t)p;
|
|
ASSERT(state_set(SBUF(txn), SBUF(txn)) == 32);
|
|
}
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(exhaustion);
|
|
|
|
// install the exhaustion hook on eve
|
|
{
|
|
Json::Value json = ripple::test::jtx::hook(
|
|
eve, {{hso(exhaustion_wasm, overrideFlag)}}, 0);
|
|
|
|
env(json, M("set state_set 6"), HSFEE);
|
|
env.close();
|
|
}
|
|
|
|
// now invoke repeatedly until exhaustion is reached
|
|
{
|
|
Json::Value json = pay(david, eve, XRP(1));
|
|
json[jss::InvoiceID] = "0001" + std::string(60, '0');
|
|
|
|
// 2500 xrp less 1 account reserve (200) divided by 50xrp per object
|
|
// reserve = 46 objects of these we already have: 1 hook, so 45
|
|
// objects can be allocated
|
|
env(json, fee(XRP(1)), M("test state_set 7"), ter(tesSUCCESS));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 2);
|
|
|
|
// now we have allocated 1 state object, so 44 more can be allocated
|
|
|
|
// try to set 45 state entries, this will fail
|
|
json[jss::InvoiceID] = "002D" + std::string(60, '0');
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_set 8"),
|
|
ter(tecHOOK_REJECTED));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 2);
|
|
|
|
// try to set 44 state objects, this will succeed
|
|
json[jss::InvoiceID] = "002C" + std::string(60, '0');
|
|
env(json, fee(XRP(1)), M("test state_set 9"), ter(tesSUCCESS));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 46);
|
|
|
|
// try to set one state object, this will fail
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_set 10"),
|
|
ter(tecHOOK_REJECTED));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("eve"))[sfOwnerCount] == 46);
|
|
}
|
|
|
|
// test maximum state modification
|
|
{
|
|
// install the hook into every position on frank
|
|
env(ripple::test::jtx::hook(
|
|
frank,
|
|
{{hso(exhaustion_wasm),
|
|
hso(exhaustion_wasm),
|
|
hso(exhaustion_wasm),
|
|
hso(exhaustion_wasm)}},
|
|
0),
|
|
M("set state_set 11"),
|
|
HSFEE,
|
|
ter(tesSUCCESS));
|
|
|
|
Json::Value json = pay(david, frank, XRP(1));
|
|
|
|
// we can modify 256 entries at a time with the hook, but first we
|
|
// want to test too many modifications so we will do 65 which times
|
|
// 4 executions is 260
|
|
json[jss::InvoiceID] = "0041" + std::string(60, '0');
|
|
env(json,
|
|
fee(XRP(1)),
|
|
M("test state_set 12"),
|
|
ter(tecHOOK_REJECTED));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("frank"))[sfOwnerCount] == 4);
|
|
|
|
// now we will do 64 which is exactly 256, which should be accepted
|
|
json[jss::InvoiceID] = "0040" + std::string(60, '0');
|
|
env(json, fee(XRP(1)), M("test state_set 13"), ter(tesSUCCESS));
|
|
env.close();
|
|
BEAST_EXPECT((*env.le("frank"))[sfOwnerCount] == 260);
|
|
}
|
|
|
|
// RH TODO:
|
|
// check state can be set on emit callback
|
|
// check namespacing provides for non-collision of same key
|
|
}
|
|
|
|
void
|
|
test_sto_emplace()
|
|
{
|
|
testcase("Test sto_emplace");
|
|
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);
|
|
|
|
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 sto_emplace (
|
|
uint32_t write_ptr, uint32_t write_len,
|
|
uint32_t sread_ptr, uint32_t sread_len,
|
|
uint32_t fread_ptr, uint32_t fread_len, uint32_t field_id );
|
|
extern int64_t trace_num(uint32_t, uint32_t, int64_t);
|
|
#define TOO_SMALL -4
|
|
#define TOO_BIG -3
|
|
#define OUT_OF_BOUNDS -1
|
|
#define MEM_OVERLAP -43
|
|
#define PARSE_ERROR -18
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
uint8_t sto[] =
|
|
{
|
|
0x11U,0x00U,0x61U,0x22U,0x00U,0x00U,0x00U,0x00U,0x24U,0x04U,0x1FU,0x94U,0xD9U,0x25U,0x04U,0x5EU,
|
|
0x84U,0xB7U,0x2DU,0x00U,0x00U,0x00U,0x00U,0x55U,0x13U,0x40U,0xB3U,0x25U,0x86U,0x31U,0x96U,0xB5U,
|
|
0x6FU,0x41U,0xF5U,0x89U,0xEBU,0x7DU,0x2FU,0xD9U,0x4CU,0x0DU,0x7DU,0xB8U,0x0EU,0x4BU,0x2CU,0x67U,
|
|
0xA7U,0x78U,0x2AU,0xD6U,0xC2U,0xB0U,0x77U,0x50U,0x62U,0x40U,0x00U,0x00U,0x00U,0x00U,0xA4U,0x79U,
|
|
0x94U,0x81U,0x14U,0x37U,0xDFU,0x44U,0x07U,0xE7U,0xAAU,0x07U,0xF1U,0xD5U,0xC9U,0x91U,0xF2U,0xD3U,
|
|
0x6FU,0x9EU,0xB8U,0xC7U,0x34U,0xAFU,0x6CU
|
|
};
|
|
|
|
uint8_t ins[] =
|
|
{
|
|
0x56U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U
|
|
};
|
|
|
|
uint8_t ans[] = {
|
|
0x11U,0x00U,0x61U,0x22U,0x00U,0x00U,0x00U,0x00U,0x24U,0x04U,0x1FU,0x94U,0xD9U,0x25U,0x04U,
|
|
0x5EU,0x84U,0xB7U,0x2DU,0x00U,0x00U,0x00U,0x00U,0x55U,0x13U,0x40U,0xB3U,0x25U,0x86U,0x31U,
|
|
0x96U,0xB5U,0x6FU,0x41U,0xF5U,0x89U,0xEBU,0x7DU,0x2FU,0xD9U,0x4CU,0x0DU,0x7DU,0xB8U,0x0EU,
|
|
0x4BU,0x2CU,0x67U,0xA7U,0x78U,0x2AU,0xD6U,0xC2U,0xB0U,0x77U,0x50U,0x56U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x62U,
|
|
0x40U,0x00U,0x00U,0x00U,0x00U,0xA4U,0x79U,0x94U,0x81U,0x14U,0x37U,0xDFU,0x44U,0x07U,0xE7U,
|
|
0xAAU,0x07U,0xF1U,0xD5U,0xC9U,0x91U,0xF2U,0xD3U,0x6FU,0x9EU,0xB8U,0xC7U,0x34U,0xAFU,0x6CU
|
|
};
|
|
|
|
uint8_t ans2[] =
|
|
{
|
|
0x11U,0x00U,0x61U,0x22U,0x00U,0x00U,0x00U,0x00U,0x24U,0x04U,0x1FU,0x94U,0xD9U,0x25U,0x04U,
|
|
0x5EU,0x84U,0xB7U,0x2DU,0x00U,0x00U,0x00U,0x00U,0x54U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,
|
|
0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x11U,0x55U,0x13U,0x40U,0xB3U,
|
|
0x25U,0x86U,0x31U,0x96U,0xB5U,0x6FU,0x41U,0xF5U,0x89U,0xEBU,0x7DU,0x2FU,0xD9U,0x4CU,0x0DU,
|
|
0x7DU,0xB8U,0x0EU,0x4BU,0x2CU,0x67U,0xA7U,0x78U,0x2AU,0xD6U,0xC2U,0xB0U,0x77U,0x50U,0x62U,
|
|
0x40U,0x00U,0x00U,0x00U,0x00U,0xA4U,0x79U,0x94U,0x81U,0x14U,0x37U,0xDFU,0x44U,0x07U,0xE7U,
|
|
0xAAU,0x07U,0xF1U,0xD5U,0xC9U,0x91U,0xF2U,0xD3U,0x6FU,0x9EU,0xB8U,0xC7U,0x34U,0xAFU,0x6CU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(sto_emplace(1000000, 32, 0, 32, 32, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_emplace(0, 1000000, 0, 32, 32, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_emplace(0, 32, 1000000, 32, 32, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_emplace(0, 32, 64, 1000000, 32, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_emplace(0, 32, 64, 32, 1000000, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_emplace(0, 32, 64, 32, 0, 1000000, 1) == OUT_OF_BOUNDS);
|
|
|
|
|
|
// Test size check
|
|
{
|
|
// write buffer too small
|
|
ASSERT(sto_emplace(0,1, 0,2, 0,2, 1) == TOO_SMALL);
|
|
// src buffer too small
|
|
ASSERT(sto_emplace(0,3, 0,1, 0,2, 1) == TOO_SMALL);
|
|
// field buffer too small
|
|
ASSERT(sto_emplace(0,3, 0,2, 0,1, 1) == TOO_SMALL);
|
|
// field buffer too big
|
|
ASSERT(sto_emplace(6000, 32000, 0, 5, 5, 6000, 1) == TOO_BIG);
|
|
// src buffer too big
|
|
ASSERT(sto_emplace(0, 32000, 32000, 17000, 49000, 4000, 1) == TOO_BIG);
|
|
}
|
|
|
|
|
|
uint8_t buf[1024];
|
|
|
|
// Test overlapping memory
|
|
ASSERT(sto_emplace(buf, 1024, buf+1, 512, 0, 32, 1) == MEM_OVERLAP);
|
|
ASSERT(sto_emplace(buf+1, 1024, buf, 512, 0, 32, 1) == MEM_OVERLAP);
|
|
ASSERT(sto_emplace(0, 700, buf, 512, buf+1, 32, 1) == MEM_OVERLAP);
|
|
|
|
// insert ledger index 561111111111111111111111111111111111111111111111111111111111111111
|
|
|
|
{
|
|
|
|
ASSERT(sto_emplace(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto),
|
|
ins, sizeof(ins), 0x50006U) ==
|
|
sizeof(sto) + sizeof(ins));
|
|
|
|
for (int i = 0; GUARD(200), i < sizeof(ans); ++i)
|
|
ASSERT(ans[i] == buf[i]);
|
|
|
|
// flip it to 54 and check it is installed before
|
|
ins[0] = 0x54U;
|
|
ASSERT(sto_emplace(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto),
|
|
ins, sizeof(ins), 0x50004U) ==
|
|
sizeof(sto) + sizeof(ins));
|
|
|
|
|
|
for (int i = 0; GUARD(200), i < sizeof(ans2); ++i)
|
|
ASSERT(ans2[i] == buf[i]);
|
|
|
|
}
|
|
|
|
// test front insertion
|
|
{
|
|
uint8_t sto[] = {0x22U,0x00U,0x00U,0x00U,0x00U};
|
|
uint8_t ins[] = {0x11U,0x11U,0x11U};
|
|
|
|
ASSERT(sto_emplace(buf, sizeof(buf), sto, sizeof(sto), ins, sizeof(ins), 0x10001U) ==
|
|
sizeof(sto) + sizeof(ins));
|
|
uint8_t ans[] = {0x11U,0x11U,0x11U,0x22U,0x00U,0x00U,0x00U,0x00U};
|
|
for (int i = 0; GUARD(10), i < sizeof(ans); ++i)
|
|
ASSERT(ans[i] == buf[i]);
|
|
}
|
|
|
|
// test back insertion
|
|
{
|
|
uint8_t sto[] = {0x22U,0x00U,0x00U,0x00U,0x00U};
|
|
uint8_t ins[] = {0x31U,0x11U,0x11U,0x11U,0x11U,0x12U,0x22U,0x22U,0x22U};
|
|
|
|
ASSERT(sto_emplace(buf, sizeof(buf), sto, sizeof(sto), ins, sizeof(ins), 0x30001U) ==
|
|
sizeof(sto) + sizeof(ins));
|
|
uint8_t ans[] = {0x22U,0x00U,0x00U,0x00U,0x00U,0x31U,0x11U,0x11U,0x11U,0x11U,0x12U,0x22U,0x22U,
|
|
0x22U};
|
|
for (int i = 0; GUARD(20), i < sizeof(ans); ++i)
|
|
ASSERT(ans[i] == buf[i]);
|
|
|
|
}
|
|
// test replacement
|
|
{
|
|
uint8_t rep[] = {0x22U,0x10U,0x20U,0x30U,0x40U};
|
|
ASSERT(sto_emplace(buf, sizeof(buf), sto, sizeof(sto), rep, sizeof(rep), 0x20002U) ==
|
|
sizeof(sto));
|
|
|
|
// check start
|
|
ASSERT(buf[0] == sto[0] && buf[1] == sto[1] && buf[2] == sto[2]);
|
|
|
|
// check replaced part
|
|
for (int i = 3; GUARD(sizeof(rep)+1), i < sizeof(rep)+3; ++i)
|
|
ASSERT(buf[i] == rep[i-3]);
|
|
|
|
// check end
|
|
for (int i = sizeof(rep)+3; GUARD(sizeof(sto)), i < sizeof(sto); ++i)
|
|
ASSERT(sto[i] == buf[i]);
|
|
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set sto_emplace"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test sto_emplace"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_sto_erase()
|
|
{
|
|
testcase("Test sto_erase");
|
|
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);
|
|
|
|
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 sto_erase (
|
|
uint32_t write_ptr, uint32_t write_len,
|
|
uint32_t sread_ptr, uint32_t sread_len,
|
|
uint32_t field_id );
|
|
#define TOO_SMALL -4
|
|
#define TOO_BIG -3
|
|
#define OUT_OF_BOUNDS -1
|
|
#define MEM_OVERLAP -43
|
|
#define PARSE_ERROR -18
|
|
#define DOESNT_EXIST -5
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
uint8_t sto[] =
|
|
{
|
|
0x11U,0x00U,0x61U,0x22U,0x00U,0x00U,0x00U,0x00U,0x24U,0x04U,0x1FU,0x94U,0xD9U,0x25U,0x04U,0x5EU,
|
|
0x84U,0xB7U,0x2DU,0x00U,0x00U,0x00U,0x00U,0x55U,0x13U,0x40U,0xB3U,0x25U,0x86U,0x31U,0x96U,0xB5U,
|
|
0x6FU,0x41U,0xF5U,0x89U,0xEBU,0x7DU,0x2FU,0xD9U,0x4CU,0x0DU,0x7DU,0xB8U,0x0EU,0x4BU,0x2CU,0x67U,
|
|
0xA7U,0x78U,0x2AU,0xD6U,0xC2U,0xB0U,0x77U,0x50U,0x62U,0x40U,0x00U,0x00U,0x00U,0x00U,0xA4U,0x79U,
|
|
0x94U,0x81U,0x14U,0x37U,0xDFU,0x44U,0x07U,0xE7U,0xAAU,0x07U,0xF1U,0xD5U,0xC9U,0x91U,0xF2U,0xD3U,
|
|
0x6FU,0x9EU,0xB8U,0xC7U,0x34U,0xAFU,0x6CU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(sto_erase(1000000, 32, 0, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_erase(0, 1000000, 0, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_erase(0, 32, 1000000, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_erase(0, 32, 64, 1000000, 1) == OUT_OF_BOUNDS);
|
|
|
|
|
|
// Test size check
|
|
{
|
|
// write buffer too small
|
|
ASSERT(sto_erase(0,1, 0,2, 1) == TOO_SMALL);
|
|
ASSERT(sto_erase(0, 32000, 0, 17000, 1) == TOO_BIG);
|
|
}
|
|
|
|
|
|
uint8_t buf[1024];
|
|
|
|
// Test overlapping memory
|
|
ASSERT(sto_erase(buf, 1024, buf+1, 512, 1) == MEM_OVERLAP);
|
|
ASSERT(sto_erase(buf+1, 1024, buf, 512, 1) == MEM_OVERLAP);
|
|
|
|
|
|
// erase field 22
|
|
{
|
|
ASSERT(sto_erase(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto), 0x20002U) ==
|
|
sizeof(sto) - 5);
|
|
|
|
ASSERT(buf[0] == sto[0] && buf[1] == sto[1] && buf[2] == sto[2]);
|
|
for (int i = 3; GUARD(sizeof(sto) + 1), i < sizeof(sto) - 5; ++i)
|
|
ASSERT(sto[i+5] == buf[i]);
|
|
}
|
|
|
|
// test front erasure
|
|
{
|
|
ASSERT(sto_erase(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto), 0x10001U) ==
|
|
sizeof(sto) - 3);
|
|
|
|
for (int i = 3; GUARD(sizeof(sto) + 1), i < sizeof(sto) - 3; ++i)
|
|
ASSERT(sto[i] == buf[i-3]);
|
|
}
|
|
|
|
// test back erasure
|
|
{
|
|
ASSERT(sto_erase(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto), 0x80001U) ==
|
|
sizeof(sto) - 22);
|
|
|
|
for (int i = 0; GUARD(sizeof(sto) - 21), i < sizeof(sto)-22; ++i)
|
|
ASSERT(sto[i] == buf[i]);
|
|
}
|
|
|
|
// test not found
|
|
{
|
|
ASSERT(sto_erase(
|
|
buf, sizeof(buf),
|
|
sto, sizeof(sto), 0x80002U) ==
|
|
DOESNT_EXIST);
|
|
|
|
for (int i = 0; GUARD(sizeof(sto) +1), i < sizeof(sto); ++i)
|
|
ASSERT(sto[i] == buf[i]);
|
|
}
|
|
|
|
// test total erasure
|
|
{
|
|
uint8_t rep[] = {0x22U,0x10U,0x20U,0x30U,0x40U};
|
|
ASSERT(sto_erase(buf, sizeof(buf), rep, sizeof(rep), 0x20002U) ==
|
|
0);
|
|
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set sto_erase"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test sto_erase"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_sto_subarray()
|
|
{
|
|
testcase("Test sto_subarray");
|
|
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);
|
|
|
|
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 sto_subarray(
|
|
uint32_t read_ptr, uint32_t read_len, uint32_t field_id);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define DOESNT_EXIST -5
|
|
#define PARSE_ERROR -18
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
uint8_t sto[] =
|
|
{
|
|
0xF4U,0xEBU,0x13U,0x00U,0x01U,0x81U,0x14U,0x20U,0x42U,0x88U,0xD2U,0xE4U,0x7FU,0x8EU,0xF6U,0xC9U,
|
|
0x9BU,0xCCU,0x45U,0x79U,0x66U,0x32U,0x0DU,0x12U,0x40U,0x97U,0x11U,0xE1U,0xEBU,0x13U,0x00U,0x01U,
|
|
0x81U,0x14U,0x3EU,0x9DU,0x4AU,0x2BU,0x8AU,0xA0U,0x78U,0x0FU,0x68U,0x2DU,0x13U,0x6FU,0x7AU,0x56U,
|
|
0xD6U,0x72U,0x4EU,0xF5U,0x37U,0x54U,0xE1U,0xF1U
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(sto_subarray(1000000, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_subarray(0, 1000000, 1) == OUT_OF_BOUNDS);
|
|
|
|
// Test size check
|
|
ASSERT(sto_subarray(0,1, 1) == TOO_SMALL);
|
|
|
|
// Test index 0, should be position 1 length 27
|
|
ASSERT(sto_subarray(sto, sizeof(sto), 0) ==
|
|
(1ULL << 32ULL) + 27ULL);
|
|
|
|
// Test index 1, should be position 28 length 27
|
|
ASSERT(sto_subarray(sto, sizeof(sto), 1) ==
|
|
(28ULL << 32ULL) + 27ULL);
|
|
|
|
// Test index2, doesn't exist
|
|
ASSERT(sto_subarray(sto, sizeof(sto), 2) == DOESNT_EXIST);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set sto_subarray"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test sto_subarray"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_sto_subfield()
|
|
{
|
|
testcase("Test sto_subfield");
|
|
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);
|
|
|
|
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 sto_subfield(
|
|
uint32_t read_ptr, uint32_t read_len, uint32_t field_id);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define DOESNT_EXIST -5
|
|
#define PARSE_ERROR -18
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
uint8_t sto[] =
|
|
{
|
|
0x11U,0x00U,0x53U,0x22U,0x00U,0x00U,0x00U,0x00U,0x25U,0x01U,0x52U,0x70U,0x1AU,0x20U,0x23U,0x00U,
|
|
0x00U,0x00U,0x02U,0x20U,0x26U,0x00U,0x00U,0x00U,0x00U,0x34U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x00U,0x55U,0x09U,0xA9U,0xC8U,0x6BU,0xF2U,0x06U,0x95U,0x73U,0x5AU,0xB0U,0x36U,0x20U,0xEBU,
|
|
0x1CU,0x32U,0x60U,0x66U,0x35U,0xACU,0x3DU,0xA0U,0xB7U,0x02U,0x82U,0xF3U,0x7CU,0x67U,0x4FU,0xC8U,
|
|
0x89U,0xEFU,0xE7U
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(sto_subfield(1000000, 32, 1) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_subfield(0, 1000000, 1) == OUT_OF_BOUNDS);
|
|
|
|
// Test size check
|
|
ASSERT(sto_subfield(0,1, 1) == TOO_SMALL);
|
|
|
|
// Test subfield 0x11, should be position 0 length 3, payload pos 1, len 2
|
|
ASSERT(sto_subfield(sto, sizeof(sto),
|
|
0x10001U) == (1ULL << 32ULL) + 2ULL);
|
|
|
|
// Test subfield 0x22, should be position 3 length 5, payload pos 4, len 4
|
|
ASSERT(sto_subfield(sto, sizeof(sto),
|
|
0x20002U) == (4ULL << 32ULL) + 4ULL);
|
|
|
|
// Test subfield 0x34, should be at position 25, length = 9, payload pos 26, len 8
|
|
ASSERT(sto_subfield(sto, sizeof(sto),
|
|
0x30004U) == (26ULL << 32ULL) + 8ULL);
|
|
|
|
// Test final subfield, position 34, length 33, payload pos 35, len 32
|
|
ASSERT(sto_subfield(sto, sizeof(sto),
|
|
0x50005U) == (35ULL << 32ULL) + 32ULL);
|
|
|
|
// Test not found
|
|
ASSERT(sto_subfield(sto, sizeof(sto),
|
|
0x90009U) == DOESNT_EXIST);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set sto_subfield"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test sto_subfield"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_sto_validate()
|
|
{
|
|
testcase("Test sto_validate");
|
|
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);
|
|
|
|
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 sto_validate (
|
|
uint32_t read_ptr, uint32_t read_len);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
uint8_t sto[] =
|
|
{
|
|
0x11U,0x00U,0x61U,0x22U,0x00U,0x00U,0x00U,0x00U,0x24U,0x04U,0x1FU,0x94U,0xD9U,0x25U,0x04U,0x5EU,
|
|
0x84U,0xB7U,0x2DU,0x00U,0x00U,0x00U,0x00U,0x55U,0x13U,0x40U,0xB3U,0x25U,0x86U,0x31U,0x96U,0xB5U,
|
|
0x6FU,0x41U,0xF5U,0x89U,0xEBU,0x7DU,0x2FU,0xD9U,0x4CU,0x0DU,0x7DU,0xB8U,0x0EU,0x4BU,0x2CU,0x67U,
|
|
0xA7U,0x78U,0x2AU,0xD6U,0xC2U,0xB0U,0x77U,0x50U,0x62U,0x40U,0x00U,0x00U,0x00U,0x00U,0xA4U,0x79U,
|
|
0x94U,0x81U,0x14U,0x37U,0xDFU,0x44U,0x07U,0xE7U,0xAAU,0x07U,0xF1U,0xD5U,0xC9U,0x91U,0xF2U,0xD3U,
|
|
0x6FU,0x9EU,0xB8U,0xC7U,0x34U,0xAFU,0x6CU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t hash[32];
|
|
|
|
// Test out of bounds check
|
|
ASSERT(sto_validate(1000000, 32) == OUT_OF_BOUNDS);
|
|
ASSERT(sto_validate(0, 1000000) == OUT_OF_BOUNDS);
|
|
|
|
// Test size check
|
|
ASSERT(sto_validate(0,1) == TOO_SMALL);
|
|
|
|
// Test validation
|
|
ASSERT(sto_validate(sto, sizeof(sto)) == 1);
|
|
|
|
// Invalidate
|
|
sto[0] = 0x22U;
|
|
ASSERT(sto_validate(sto, sizeof(sto)) == 0);
|
|
|
|
// Fix
|
|
sto[0] = 0x11U;
|
|
|
|
// Invalidate somewhere else
|
|
sto[3] = 0x40U;
|
|
ASSERT(sto_validate(sto, sizeof(sto)) == 0);
|
|
|
|
// test small validation
|
|
{
|
|
uint8_t sto[] = {0x22U,0x00U,0x00U,0x00U,0x00U};
|
|
ASSERT(sto_validate(sto, sizeof(sto)) == 1);
|
|
}
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set sto_validate"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test sto_validate"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_trace()
|
|
{
|
|
testcase("Test trace");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 trace (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
// Test out of bounds check
|
|
ASSERT(trace(1000000, 10, 0, 10, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(trace(0, 1000000, 0, 10, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(trace(0, 10, 1000000, 10, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(trace(0, 10, 0, 1000000, 0) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(trace(0,0,0,0,0) == 0);
|
|
ASSERT(trace(0,1,2,3,1) == 0);
|
|
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set trace"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test trace"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_trace_float()
|
|
{
|
|
testcase("Test trace_float");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 trace_float (uint32_t, uint32_t, int64_t);
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reservmaed )
|
|
{
|
|
_g(1,1);
|
|
// Test out of bounds check
|
|
ASSERT(trace_float(1000000, 10, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(trace_float(0, 1000000, 0) == OUT_OF_BOUNDS);
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set trace_float"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test trace_float"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_trace_num()
|
|
{
|
|
testcase("Test trace_num");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 trace_num (uint32_t, uint32_t, int64_t);
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t r )
|
|
{
|
|
_g(1,1);
|
|
// Test out of bounds check
|
|
ASSERT(trace_num(1000000, 10, 0) == OUT_OF_BOUNDS);
|
|
ASSERT(trace_num(0, 1000000, 0) == OUT_OF_BOUNDS);
|
|
return accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set trace_num"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test trace_num"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_util_accid()
|
|
{
|
|
using namespace jtx;
|
|
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);
|
|
|
|
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 util_accid (uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
uint8_t b[20];
|
|
{
|
|
const char addr[] = "rMEGJtK2SttrtAfoKaqKUpCrDCi9saNuLg";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xDEU && b[ 1] == 0x15U && b[ 2] == 0x1EU && b[ 3] == 0x2FU &&
|
|
b[ 4] == 0xB2U && b[ 5] == 0xAAU && b[ 6] == 0xBDU && b[ 7] == 0x1AU &&
|
|
b[ 8] == 0x5BU && b[ 9] == 0xD0U && b[10] == 0x2FU && b[11] == 0x63U &&
|
|
b[12] == 0x68U && b[13] == 0x26U && b[14] == 0xDFU && b[15] == 0x43U &&
|
|
b[16] == 0x50U && b[17] == 0xC0U && b[18] == 0x40U && b[19] == 0xDEU);
|
|
}
|
|
{
|
|
const char addr[] = "rNo8xzUAauXENpvsMVJ9Q9w5LtVxCVFN4p";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x97U && b[ 1] == 0x73U && b[ 2] == 0x23U && b[ 3] == 0xAAU &&
|
|
b[ 4] == 0x33U && b[ 5] == 0x7CU && b[ 6] == 0xB6U && b[ 7] == 0x82U &&
|
|
b[ 8] == 0x37U && b[ 9] == 0x83U && b[10] == 0x58U && b[11] == 0x3AU &&
|
|
b[12] == 0x7AU && b[13] == 0xDFU && b[14] == 0x4EU && b[15] == 0xD8U &&
|
|
b[16] == 0x52U && b[17] == 0x2CU && b[18] == 0xA8U && b[19] == 0xF0U);
|
|
}
|
|
{
|
|
const char addr[] = "rUpwuJR1xLH18aHLP5nEm4Hw215tmkq6V7";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x78U && b[ 1] == 0xE2U && b[ 2] == 0x10U && b[ 3] == 0xACU &&
|
|
b[ 4] == 0x98U && b[ 5] == 0x38U && b[ 6] == 0xF2U && b[ 7] == 0x5AU &&
|
|
b[ 8] == 0x3BU && b[ 9] == 0x7EU && b[10] == 0xDEU && b[11] == 0x51U &&
|
|
b[12] == 0x37U && b[13] == 0x13U && b[14] == 0x94U && b[15] == 0xEDU &&
|
|
b[16] == 0x80U && b[17] == 0x77U && b[18] == 0x89U && b[19] == 0x48U);
|
|
}
|
|
{
|
|
const char addr[] = "ravUPmVUQ65qeuNSFiN6W2U88smjJYHBJm";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x40U && b[ 1] == 0xE8U && b[ 2] == 0x2FU && b[ 3] == 0x55U &&
|
|
b[ 4] == 0xC7U && b[ 5] == 0x3AU && b[ 6] == 0xEBU && b[ 7] == 0xCFU &&
|
|
b[ 8] == 0xC9U && b[ 9] == 0x1DU && b[10] == 0x3BU && b[11] == 0xF4U &&
|
|
b[12] == 0x77U && b[13] == 0x76U && b[14] == 0x50U && b[15] == 0x2BU &&
|
|
b[16] == 0x49U && b[17] == 0x7BU && b[18] == 0x12U && b[19] == 0x2CU);
|
|
}
|
|
{
|
|
const char addr[] = "rPXQ8PW1C382oewiEyJrAWtDQBNsQhAtWA";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xF7U && b[ 1] == 0x13U && b[ 2] == 0x19U && b[ 3] == 0x49U &&
|
|
b[ 4] == 0x3FU && b[ 5] == 0xA6U && b[ 6] == 0xA3U && b[ 7] == 0xDBU &&
|
|
b[ 8] == 0x62U && b[ 9] == 0xAEU && b[10] == 0x12U && b[11] == 0x1BU &&
|
|
b[12] == 0x12U && b[13] == 0x6CU && b[14] == 0xFEU && b[15] == 0x81U &&
|
|
b[16] == 0x49U && b[17] == 0x5AU && b[18] == 0x49U && b[19] == 0x16U);
|
|
}
|
|
{
|
|
const char addr[] = "rnZbUT8tpm48KEdfELCxRjJJhNV1JNYcg5";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x32U && b[ 1] == 0x0AU && b[ 2] == 0x5CU && b[ 3] == 0x53U &&
|
|
b[ 4] == 0x61U && b[ 5] == 0x5BU && b[ 6] == 0x4BU && b[ 7] == 0x57U &&
|
|
b[ 8] == 0x1DU && b[ 9] == 0xC4U && b[10] == 0x6FU && b[11] == 0x13U &&
|
|
b[12] == 0xBDU && b[13] == 0x4FU && b[14] == 0x31U && b[15] == 0x70U &&
|
|
b[16] == 0x84U && b[17] == 0xD1U && b[18] == 0xB1U && b[19] == 0x68U);
|
|
}
|
|
{
|
|
const char addr[] = "rPghxri3jhBaxBfWGAHrVC4KANoRBe6dcM";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xF8U && b[ 1] == 0xB6U && b[ 2] == 0x49U && b[ 3] == 0x2BU &&
|
|
b[ 4] == 0x5BU && b[ 5] == 0x21U && b[ 6] == 0xC8U && b[ 7] == 0xDAU &&
|
|
b[ 8] == 0xBDU && b[ 9] == 0x0FU && b[10] == 0x1DU && b[11] == 0x2FU &&
|
|
b[12] == 0xD9U && b[13] == 0xF4U && b[14] == 0x5BU && b[15] == 0xDEU &&
|
|
b[16] == 0xCCU && b[17] == 0x6AU && b[18] == 0xEBU && b[19] == 0x91U);
|
|
}
|
|
{
|
|
const char addr[] = "r4Tck2QJcfcwBuTgVJXYb4QbrKP6mT1acM";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xEBU && b[ 1] == 0x63U && b[ 2] == 0x4CU && b[ 3] == 0xD6U &&
|
|
b[ 4] == 0xF9U && b[ 5] == 0xBFU && b[ 6] == 0x50U && b[ 7] == 0xC1U &&
|
|
b[ 8] == 0xD9U && b[ 9] == 0x79U && b[10] == 0x30U && b[11] == 0x84U &&
|
|
b[12] == 0x1BU && b[13] == 0xFCU && b[14] == 0x35U && b[15] == 0x32U &&
|
|
b[16] == 0xBDU && b[17] == 0x6DU && b[18] == 0xC0U && b[19] == 0x75U);
|
|
}
|
|
{
|
|
const char addr[] = "rETHUL5T1SzM6AMotnsK5V3J5XMwJ9UhZ2";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x9EU && b[ 1] == 0x8AU && b[ 2] == 0x18U && b[ 3] == 0x66U &&
|
|
b[ 4] == 0x92U && b[ 5] == 0x0EU && b[ 6] == 0xE5U && b[ 7] == 0xEDU &&
|
|
b[ 8] == 0xFAU && b[ 9] == 0xE3U && b[10] == 0x23U && b[11] == 0x15U &&
|
|
b[12] == 0xCBU && b[13] == 0x83U && b[14] == 0xEFU && b[15] == 0x73U &&
|
|
b[16] == 0xE4U && b[17] == 0x91U && b[18] == 0x0BU && b[19] == 0xCAU);
|
|
}
|
|
{
|
|
const char addr[] = "rh9CggaWiY6QdD55ZkbbnrFpHJkKSauLfC";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x22U && b[ 1] == 0x8BU && b[ 2] == 0xFFU && b[ 3] == 0x31U &&
|
|
b[ 4] == 0xB4U && b[ 5] == 0x93U && b[ 6] == 0xF6U && b[ 7] == 0xC1U &&
|
|
b[ 8] == 0x12U && b[ 9] == 0xEAU && b[10] == 0xD6U && b[11] == 0xDFU &&
|
|
b[12] == 0xC4U && b[13] == 0x05U && b[14] == 0xB3U && b[15] == 0x7DU &&
|
|
b[16] == 0xC0U && b[17] == 0x65U && b[18] == 0x21U && b[19] == 0x34U);
|
|
}
|
|
{
|
|
const char addr[] = "r9sYGdPCGuJauy8QVG4CHnvp5U4eu3yY2B";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x58U && b[ 1] == 0x3BU && b[ 2] == 0xF0U && b[ 3] == 0xCBU &&
|
|
b[ 4] == 0x95U && b[ 5] == 0x80U && b[ 6] == 0xDEU && b[ 7] == 0xA0U &&
|
|
b[ 8] == 0xB3U && b[ 9] == 0x71U && b[10] == 0xD0U && b[11] == 0x18U &&
|
|
b[12] == 0x17U && b[13] == 0x1AU && b[14] == 0xBBU && b[15] == 0x98U &&
|
|
b[16] == 0x1FU && b[17] == 0xCCU && b[18] == 0x7CU && b[19] == 0x68U);
|
|
}
|
|
{
|
|
const char addr[] = "r4yJX9eU65WHfmKz6xXmSRf9CZN6bXfpWb";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xF1U && b[ 1] == 0x00U && b[ 2] == 0x8FU && b[ 3] == 0x64U &&
|
|
b[ 4] == 0x0FU && b[ 5] == 0x99U && b[ 6] == 0x19U && b[ 7] == 0xDAU &&
|
|
b[ 8] == 0xCFU && b[ 9] == 0x48U && b[10] == 0x18U && b[11] == 0x1CU &&
|
|
b[12] == 0x35U && b[13] == 0x2EU && b[14] == 0xE4U && b[15] == 0x3EU &&
|
|
b[16] == 0x37U && b[17] == 0x7CU && b[18] == 0x01U && b[19] == 0xF6U);
|
|
}
|
|
{
|
|
const char addr[] = "rBkXoWoXPHuZy2nHbE7L1zJfqAvb4jHRrK";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x75U && b[ 1] == 0xECU && b[ 2] == 0xDBU && b[ 3] == 0x3BU &&
|
|
b[ 4] == 0x9AU && b[ 5] == 0x71U && b[ 6] == 0xD9U && b[ 7] == 0xEFU &&
|
|
b[ 8] == 0xD6U && b[ 9] == 0x55U && b[10] == 0x15U && b[11] == 0xDDU &&
|
|
b[12] == 0xEAU && b[13] == 0xD2U && b[14] == 0x36U && b[15] == 0x7AU &&
|
|
b[16] == 0x05U && b[17] == 0x6FU && b[18] == 0x4EU && b[19] == 0x5FU);
|
|
}
|
|
{
|
|
const char addr[] = "rnaUBeEBNuyv57Jk127DsApEQoR8JqWpie";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x2CU && b[ 1] == 0xDBU && b[ 2] == 0xEBU && b[ 3] == 0x1FU &&
|
|
b[ 4] == 0x5EU && b[ 5] == 0xC5U && b[ 6] == 0xD7U && b[ 7] == 0x5FU &&
|
|
b[ 8] == 0xACU && b[ 9] == 0xBDU && b[10] == 0x19U && b[11] == 0xC8U &&
|
|
b[12] == 0x3FU && b[13] == 0x45U && b[14] == 0x3BU && b[15] == 0xA8U &&
|
|
b[16] == 0xA0U && b[17] == 0x1CU && b[18] == 0xDBU && b[19] == 0x0FU);
|
|
}
|
|
{
|
|
const char addr[] = "rJHmUPMQ6qYdaqMizDZY8FKcCqCJxYYnb3";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xBDU && b[ 1] == 0xA5U && b[ 2] == 0xAFU && b[ 3] == 0xDAU &&
|
|
b[ 4] == 0x5FU && b[ 5] == 0x04U && b[ 6] == 0xE7U && b[ 7] == 0xEFU &&
|
|
b[ 8] == 0x16U && b[ 9] == 0x7AU && b[10] == 0x35U && b[11] == 0x94U &&
|
|
b[12] == 0x6EU && b[13] == 0xEFU && b[14] == 0x19U && b[15] == 0xFAU &&
|
|
b[16] == 0x12U && b[17] == 0xF3U && b[18] == 0x1CU && b[19] == 0x64U);
|
|
}
|
|
{
|
|
const char addr[] = "rpJtt64FNNtaEBgqbJcrrunucUWJSdKJa2";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x0EU && b[ 1] == 0x5AU && b[ 2] == 0x83U && b[ 3] == 0x89U &&
|
|
b[ 4] == 0xC0U && b[ 5] == 0x5EU && b[ 6] == 0x56U && b[ 7] == 0xD1U &&
|
|
b[ 8] == 0x50U && b[ 9] == 0xBCU && b[10] == 0x45U && b[11] == 0x7BU &&
|
|
b[12] == 0x86U && b[13] == 0x46U && b[14] == 0xF1U && b[15] == 0xCFU &&
|
|
b[16] == 0xB7U && b[17] == 0xD0U && b[18] == 0xBFU && b[19] == 0xD4U);
|
|
}
|
|
{
|
|
const char addr[] = "rUC2XjZURBYQ8r6i5sqWnhtDmFFdJFobb9";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x7FU && b[ 1] == 0xF5U && b[ 2] == 0x2DU && b[ 3] == 0xF4U &&
|
|
b[ 4] == 0x98U && b[ 5] == 0x2BU && b[ 6] == 0x7CU && b[ 7] == 0x14U &&
|
|
b[ 8] == 0x7EU && b[ 9] == 0x9AU && b[10] == 0x8BU && b[11] == 0xEBU &&
|
|
b[12] == 0x1AU && b[13] == 0x53U && b[14] == 0x60U && b[15] == 0x34U &&
|
|
b[16] == 0x95U && b[17] == 0x42U && b[18] == 0x4AU && b[19] == 0x44U);
|
|
}
|
|
{
|
|
const char addr[] = "rKEsw1ExpKaukXyyPCxeZdAF5V68kPSAVZ";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0xC8U && b[ 1] == 0x19U && b[ 2] == 0xE6U && b[ 3] == 0x2AU &&
|
|
b[ 4] == 0xDDU && b[ 5] == 0x42U && b[ 6] == 0x48U && b[ 7] == 0xD6U &&
|
|
b[ 8] == 0x7DU && b[ 9] == 0xA5U && b[10] == 0x56U && b[11] == 0x66U &&
|
|
b[12] == 0x55U && b[13] == 0xB4U && b[14] == 0xBFU && b[15] == 0xDEU &&
|
|
b[16] == 0x99U && b[17] == 0xCFU && b[18] == 0xEDU && b[19] == 0x96U);
|
|
}
|
|
{
|
|
const char addr[] = "rEXhVGVWdte28r1DUzfgKLjNiHi1Tn6R7X";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x9FU && b[ 1] == 0x41U && b[ 2] == 0x26U && b[ 3] == 0xA3U &&
|
|
b[ 4] == 0x6DU && b[ 5] == 0x56U && b[ 6] == 0x01U && b[ 7] == 0xC8U &&
|
|
b[ 8] == 0x09U && b[ 9] == 0x63U && b[10] == 0x76U && b[11] == 0xEDU &&
|
|
b[12] == 0x4CU && b[13] == 0x45U && b[14] == 0x66U && b[15] == 0x63U &&
|
|
b[16] == 0x16U && b[17] == 0xC9U && b[18] == 0x5CU && b[19] == 0x80U);
|
|
}
|
|
{
|
|
const char addr[] = "r3TcfPNEvidJ2LkNoFojffcCd7RgT53Thg";
|
|
ASSERT(20 ==
|
|
util_accid((uint32_t)b, 20, (uint32_t)addr, sizeof(addr)));
|
|
ASSERT(
|
|
b[ 0] == 0x51U && b[ 1] == 0xD1U && b[ 2] == 0x00U && b[ 3] == 0xFFU &&
|
|
b[ 4] == 0x0DU && b[ 5] == 0x92U && b[ 6] == 0x18U && b[ 7] == 0x73U &&
|
|
b[ 8] == 0x80U && b[ 9] == 0x30U && b[10] == 0xC5U && b[11] == 0x1AU &&
|
|
b[12] == 0xF2U && b[13] == 0x9FU && b[14] == 0x52U && b[15] == 0x8EU &&
|
|
b[16] == 0xB8U && b[17] == 0x63U && b[18] == 0x08U && b[19] == 0x7CU);
|
|
}
|
|
|
|
// Test out of bounds check
|
|
ASSERT(util_accid(1000000, 20, 0, 35) == OUT_OF_BOUNDS);
|
|
ASSERT(util_accid(0, 35, 10000000, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(util_accid(0, 19, 0, 0) == TOO_SMALL);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set util_accid"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test util_accid"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_util_keylet()
|
|
{
|
|
testcase("Test util_keylet");
|
|
using namespace jtx;
|
|
|
|
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);
|
|
|
|
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 util_keylet (
|
|
uint32_t write_ptr,
|
|
uint32_t write_len,
|
|
uint32_t keylet_type,
|
|
uint32_t a,
|
|
uint32_t b,
|
|
uint32_t c,
|
|
uint32_t d,
|
|
uint32_t e,
|
|
uint32_t f
|
|
);
|
|
#define OUT_OF_BOUNDS -1
|
|
#define INVALID_ARGUMENT -7
|
|
#define TOO_SMALL -4
|
|
#define KEYLET_HOOK 1
|
|
#define KEYLET_HOOK_STATE 2
|
|
#define KEYLET_ACCOUNT 3
|
|
#define KEYLET_AMENDMENTS 4
|
|
#define KEYLET_CHILD 5
|
|
#define KEYLET_SKIP 6
|
|
#define KEYLET_FEES 7
|
|
#define KEYLET_NEGATIVE_UNL 8
|
|
#define KEYLET_LINE 9
|
|
#define KEYLET_OFFER 10
|
|
#define KEYLET_QUALITY 11
|
|
#define KEYLET_EMITTED_DIR 12
|
|
#define KEYLET_SIGNERS 14
|
|
#define KEYLET_CHECK 15
|
|
#define KEYLET_DEPOSIT_PREAUTH 16
|
|
#define KEYLET_UNCHECKED 17
|
|
#define KEYLET_OWNER_DIR 18
|
|
#define KEYLET_PAGE 19
|
|
#define KEYLET_ESCROW 20
|
|
#define KEYLET_PAYCHAN 21
|
|
#define KEYLET_EMITTED_TXN 22
|
|
#define KEYLET_NFT_OFFER 23
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
#define ASSERT_KL_EQ(b)\
|
|
{\
|
|
uint64_t* n = (uint64_t*)(b);\
|
|
uint64_t* m = (uint64_t*)(buf);\
|
|
ASSERT(n[0] == m[0] && n[1] == m[1] && n[2] == m[2] && n[3] == m[3]);\
|
|
}
|
|
#define SBUF(x) x,sizeof(x)
|
|
//C5D0F34B0A1905BC3B29AA1BE139FE04D60C8694D3950A8D80251D10B563A822
|
|
uint8_t ns[] =
|
|
{
|
|
0xC5U,0xD0U,0xF3U,0x4BU,0x0AU,0x19U,0x05U,0xBCU,0x3BU,0x29U,0xAAU,0x1BU,0xE1U,
|
|
0x39U,0xFEU,0x04U,0xD6U,0x0CU,0x86U,0x94U,0xD3U,0x95U,0x0AU,0x8DU,0x80U,0x25U,
|
|
0x1DU,0x10U,0xB5U,0x63U,0xA8U,0x22U
|
|
};
|
|
|
|
//2D0CB3CD60DA33B5AA7FEA321F111663EAED32481C6B700E484550F45AD96223
|
|
uint8_t klkey[] =
|
|
{
|
|
0x00U, 0x00U,
|
|
0x2DU,0x0CU,0xB3U,0xCDU,0x60U,0xDAU,0x33U,0xB5U,0xAAU,0x7FU,0xEAU,0x32U,0x1FU,
|
|
0x11U,0x16U,0x63U,0xEAU,0xEDU,0x32U,0x48U,0x1CU,0x6BU,0x70U,0x0EU,0x48U,0x45U,
|
|
0x50U,0xF4U,0x5AU,0xD9U,0x62U,0x23U
|
|
};
|
|
|
|
uint8_t cur[] =
|
|
{
|
|
0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x00U,0x55U,0x53U,0x44U,0x00U,0x00U,0x00U,0x00U,0x00U
|
|
|
|
};
|
|
|
|
uint8_t* key = klkey + 2;
|
|
|
|
uint8_t a[] = //rB6v18pQ765Z9DH5RQsTFevoQPFmRtBqhT
|
|
{
|
|
0x75U,0x6EU,0xDEU,0x88U,0xA9U,0x07U,0xD4U,0xCCU,0xF3U,0x8DU,0x6AU,0xDBU,
|
|
0x9FU,0xC7U,0x94U,0x64U,0x19U,0xF0U,0xC4U,0x1DU
|
|
};
|
|
|
|
uint8_t b[] = //raKM1bZkGmASBqN5v2swrf2uAPJ32Cd8GV
|
|
{
|
|
0x3AU,0x51U,0x8AU,0x22U,0x53U,0x81U,0x60U,0x84U,0x1CU,0x14U,0x32U,0xFEU,
|
|
0x6FU,0x3EU,0x6DU,0x6EU,0x76U,0x29U,0xFBU,0xBAU
|
|
};
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t buf[34];
|
|
int64_t e = 0;
|
|
|
|
|
|
// Test out of bounds check
|
|
ASSERT(util_keylet(1000000, 34, KEYLET_SKIP, 0,0,0,0,0,0) == OUT_OF_BOUNDS);
|
|
ASSERT(util_keylet((uint32_t)buf, 1000000, KEYLET_SKIP, 0,0,0,0,0,0) == OUT_OF_BOUNDS);
|
|
|
|
// Test min size
|
|
ASSERT(util_keylet((uint32_t)buf, 33, KEYLET_SKIP, 0,0,0,0,0,0) == TOO_SMALL);
|
|
|
|
|
|
// Test one of each type
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_HOOK,
|
|
SBUF(a),
|
|
0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x48U,0x6CU,0x4BU,0x29U,0xC6U,0x0FU,0x40U,0x5DU,0xB7U,0x6EU,0x87U,
|
|
0x65U,0x4AU,0x2FU,0x15U,0x4BU,0xABU,0x99U,0xC7U,0x62U,0x29U,0x80U,0x10U,
|
|
0xA1U,0x89U,0x78U,0x52U,0x90U,0x80U,0x2FU,0x78U,0xBDU,0xCCU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_HOOK_STATE,
|
|
SBUF(b), key, 32, SBUF(ns)
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x76U,0x28U,0xAFU,0xCCU,0x25U,0x0AU,0x64U,0x41U,0x8EU,0xB7U,0x83U,
|
|
0x68U,0xEBU,0x4EU,0xC5U,0x52U,0x4AU,0xEBU,0x97U,0x54U,0xABU,0xC1U,0x0BU,
|
|
0x13U,0x06U,0x7FU,0xFBU,0x9FU,0x4BU,0xD8U,0x38U,0x62U,0xF2U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_ACCOUNT,
|
|
SBUF(b),
|
|
0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x61U,0xC6U,0x55U,0xDDU,0x8DU,0x8EU,0xD3U,0xBAU,0xB4U,0xA0U,0xF1U,
|
|
0xECU,0x2DU,0xA9U,0x99U,0xF4U,0x1BU,0xA6U,0x82U,0xC6U,0x84U,0xF9U,0x99U,
|
|
0x66U,0xB9U,0x3CU,0x9AU,0xC3U,0xE3U,0x5CU,0x9AU,0x81U,0x6DU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_AMENDMENTS,
|
|
0,0,0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x66U,0x7DU,0xB0U,0x78U,0x8CU,0x02U,0x0FU,0x02U,0x78U,0x0AU,0x67U,
|
|
0x3DU,0xC7U,0x47U,0x57U,0xF2U,0x38U,0x23U,0xFAU,0x30U,0x14U,0xC1U,0x86U,
|
|
0x6EU,0x72U,0xCCU,0x4CU,0xD8U,0xB2U,0x26U,0xCDU,0x6EU,0xF4U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_CHILD,
|
|
key, 32,
|
|
0,0,0,0
|
|
)));
|
|
|
|
{
|
|
klkey[0] = 0x1CU;
|
|
klkey[1] = 0xD2U;
|
|
ASSERT_KL_EQ(klkey);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_SKIP,
|
|
0,0,0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x68U,0xB4U,0x97U,0x9AU,0x36U,0xCDU,0xC7U,0xF3U,0xD3U,
|
|
0xD5U,0xC3U,0x1AU,0x4EU,0xAEU,0x2AU,0xC7U,0xD7U,0x20U,0x9DU,
|
|
0xDAU,0x87U,0x75U,0x88U,0xB9U,0xAFU,0xC6U,0x67U,0x99U,0x69U,
|
|
0x2AU,0xB0U,0xD6U,0x6BU
|
|
};
|
|
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_FEES,
|
|
0,0,0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x73U,0x4BU,0xC5U,0x0CU,0x9BU,0x0DU,0x85U,0x15U,0xD3U,
|
|
0xEAU,0xAEU,0x1EU,0x74U,0xB2U,0x9AU,0x95U,0x80U,0x43U,0x46U,
|
|
0xC4U,0x91U,0xEEU,0x1AU,0x95U,0xBFU,0x25U,0xE4U,0xAAU,0xB8U,
|
|
0x54U,0xA6U,0xA6U,0x51U
|
|
};
|
|
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_NEGATIVE_UNL,
|
|
0,0,0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x4EU,0x2EU,0x8AU,0x59U,0xAAU,0x9DU,0x3BU,0x5BU,0x18U,
|
|
0x6BU,0x0BU,0x9EU,0x0FU,0x62U,0xE6U,0xC0U,0x25U,0x87U,0xCAU,
|
|
0x74U,0xA4U,0xD7U,0x78U,0x93U,0x8EU,0x95U,0x7BU,0x63U,0x57U,
|
|
0xD3U,0x64U,0xB2U,0x44U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_LINE,
|
|
SBUF(a),
|
|
SBUF(b),
|
|
SBUF(cur)
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x72U,0x0EU,0xB8U,0x2AU,0xDDU,0x5EU,0x15U,0x59U,0x1BU,
|
|
0xF6U,0xE3U,0x6DU,0xBCU,0x3CU,0x12U,0xD3U,0x07U,0x6DU,0x43U,
|
|
0xA8U,0x53U,0xF8U,0xF9U,0xE8U,0xA7U,0xD8U,0x4FU,0xE1U,0xE9U,
|
|
0x7AU,0x2AU,0xC7U,0x3DU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
// test 3 byte code
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_LINE,
|
|
SBUF(a),
|
|
SBUF(b),
|
|
(uint32_t)"USD", 3
|
|
)));
|
|
|
|
{
|
|
// same answer
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x72U,0x0EU,0xB8U,0x2AU,0xDDU,0x5EU,0x15U,0x59U,0x1BU,
|
|
0xF6U,0xE3U,0x6DU,0xBCU,0x3CU,0x12U,0xD3U,0x07U,0x6DU,0x43U,
|
|
0xA8U,0x53U,0xF8U,0xF9U,0xE8U,0xA7U,0xD8U,0x4FU,0xE1U,0xE9U,
|
|
0x7AU,0x2AU,0xC7U,0x3DU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
// test invalid 3 byte code
|
|
ASSERT(INVALID_ARGUMENT ==
|
|
util_keylet(buf, 34, KEYLET_LINE, SBUF(a), SBUF(b), (uint32_t)"`SD", 3));
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_OFFER,
|
|
SBUF(a),
|
|
1,
|
|
0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x6FU,0x60U,0x14U,0x48U,0x80U,0x97U,0x5FU,0x76U,0x6AU,
|
|
0xB2U,0x2CU,0x32U,0x2FU,0x10U,0x8EU,0x03U,0x43U,0x51U,0xDEU,
|
|
0x89U,0x6CU,0xF4U,0x9FU,0x6BU,0x4AU,0xC7U,0x2CU,0x54U,0xF7U,
|
|
0x27U,0x29U,0x9BU,0xE8U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
// again with a uint256
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_OFFER,
|
|
SBUF(a),
|
|
SBUF(ns),
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x6FU,0x23U,0x61U,0x7FU,0x44U,0x91U,0x1CU,0xBAU,0x3BU,
|
|
0x5CU,0xBEU,0xE9U,0x42U,0x22U,0xACU,0xA4U,0x29U,0xF4U,0xD6U,
|
|
0x60U,0x01U,0xA8U,0xABU,0x9BU,0x98U,0x5EU,0xB8U,0xB8U,0x42U,
|
|
0x9FU,0x1EU,0x91U,0x4BU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
// verify that quality returns invalid argument when passed
|
|
// something that isn't a dir keylet
|
|
klkey[0] = 0;
|
|
klkey[1] = 0x65U;
|
|
ASSERT(INVALID_ARGUMENT == (e=util_keylet(buf, 34, KEYLET_QUALITY,
|
|
SBUF(klkey),
|
|
0,1,
|
|
0,0
|
|
)));
|
|
|
|
// now change it to a dir
|
|
klkey[1] = 0x64U;
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_QUALITY,
|
|
SBUF(klkey),
|
|
0,1,
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x64U,0x2DU,0x0CU,0xB3U,0xCDU,0x60U,0xDAU,0x33U,0xB5U,
|
|
0xAAU,0x7FU,0xEAU,0x32U,0x1FU,0x11U,0x16U,0x63U,0xEAU,0xEDU,
|
|
0x32U,0x48U,0x1CU,0x6BU,0x70U,0x0EU,0x00U,0x00U,0x00U,0x00U,
|
|
0x00U,0x00U,0x00U,0x01U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_EMITTED_DIR,
|
|
0,0,0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x64U,0xB4U,0xDEU,0x82U,0x30U,0x55U,0xD0U,0x0BU,0xC1U,
|
|
0x2CU,0xD7U,0x8FU,0xE1U,0xAAU,0xF7U,0x4EU,0xE6U,0x06U,0x21U,
|
|
0x95U,0xB2U,0x62U,0x9FU,0x49U,0xA2U,0x59U,0x15U,0xA3U,0x9CU,
|
|
0x64U,0xBEU,0x19U,0x00U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_SIGNERS,
|
|
SBUF(a),
|
|
0,0,0,0
|
|
)));
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x53U,0xDFU,0x8FU,0xF0U,0xCEU,0x41U,0x1AU,0x3BU,0x8FU,
|
|
0x1BU,0xB5U,0xBBU,0x32U,0x78U,0x17U,0x15U,0xD6U,0x77U,0x42U,
|
|
0xF5U,0xB5U,0x63U,0xB8U,0x77U,0xB3U,0x3BU,0x07U,0x76U,0xF6U,
|
|
0xF7U,0xBCU,0xDAU,0x1DU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_CHECK,
|
|
SBUF(a), 1, 0,
|
|
0,0
|
|
)));
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x43U,0x08U,0x1FU,0x26U,0xFFU,0x79U,0x1AU,0xF7U,0x54U,
|
|
0x26U,0xEDU,0xF9U,0xEBU,0x08U,0x44U,0x85U,0x28U,0x58U,0x2CU,
|
|
0xB1U,0xA4U,0xEFU,0x4FU,0xD0U,0xB4U,0x49U,0x9BU,0x76U,0x82U,
|
|
0xE7U,0x69U,0xA6U,0xB5U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
// ans again with uint256
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_CHECK,
|
|
SBUF(a), SBUF(ns),
|
|
0,0
|
|
)));
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x43U,0x94U,0xE3U,0x6FU,0x0DU,0xD3U,0xEDU,0xC0U,0x2CU,
|
|
0x49U,0xA5U,0xAAU,0x0EU,0xCCU,0x49U,0x18U,0x39U,0x92U,0xABU,
|
|
0x57U,0xC3U,0x2DU,0x9EU,0x45U,0x51U,0x04U,0x78U,0x49U,0x49U,
|
|
0xD1U,0xE6U,0xD2U,0x01U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_DEPOSIT_PREAUTH,
|
|
SBUF(a), SBUF(b),
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x70U,0x88U,0x90U,0x0FU,0x27U,0x66U,0x57U,0xBCU,0xC0U,
|
|
0x5DU,0xA1U,0x67U,0x40U,0xABU,0x9DU,0x33U,0x01U,0x8EU,0x45U,
|
|
0x71U,0x7BU,0x0EU,0xC4U,0x2EU,0x4DU,0x11U,0xBDU,0x6DU,0xBDU,
|
|
0x94U,0x03U,0x48U,0xE0U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
klkey[0] = 0;
|
|
klkey[1] = 0;
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_UNCHECKED,
|
|
key, 32,
|
|
0,0,0,0
|
|
)));
|
|
|
|
ASSERT_KL_EQ(klkey);
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_OWNER_DIR,
|
|
SBUF(a),
|
|
0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x64U,0xC8U,0x5EU,0x01U,0x29U,0x06U,0x7BU,0x75U,0xADU,
|
|
0x30U,0xB0U,0xAAU,0x1CU,0xC2U,0x5BU,0x0AU,0x82U,0xC7U,0xF9U,
|
|
0xAAU,0xBDU,0xEEU,0x05U,0xFFU,0x01U,0x66U,0x69U,0xEFU,0x9DU,
|
|
0x82U,0xDCU,0xECU,0x30U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_PAGE,
|
|
SBUF(ns), 0, 1,
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x64U,0x61U,0xE6U,0x05U,0x1AU,0xB0U,0x49U,0x89U,0x2EU,
|
|
0x75U,0xC9U,0x3DU,0x67U,0xFBU,0x7AU,0x63U,0xF1U,0xEFU,0x56U,
|
|
0xDDU,0xAFU,0x3EU,0x6BU,0x43U,0x6FU,0x57U,0x6EU,0x8CU,0x01U,
|
|
0x81U,0x98U,0x2EU,0x48U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_ESCROW,
|
|
SBUF(a), 1, 0,
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x75U,0x13U,0xEFU,0x04U,0xCDU,0x33U,0x6AU,0xADU,0xF6U,
|
|
0x3DU,0x0CU,0x7EU,0x05U,0x6CU,0x84U,0x9AU,0x7CU,0xF6U,0x72U,
|
|
0x5EU,0x99U,0xBCU,0x93U,0x80U,0x1EU,0xF5U,0x78U,0xA0U,0x32U,
|
|
0x72U,0x5BU,0x84U,0xFEU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
// again with a uint256
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_ESCROW,
|
|
SBUF(a),
|
|
SBUF(ns),
|
|
0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x75U,0xC1U,0xC6U,0xC5U,0x23U,0x74U,0x87U,0x12U,0x56U,
|
|
0xAAU,0x7AU,0x1FU,0xB3U,0x29U,0x7AU,0x0AU,0x55U,0x88U,0x7DU,
|
|
0x16U,0x6AU,0xCFU,0x85U,0x28U,0x59U,0x88U,0xC2U,0xDAU,0x81U,
|
|
0x7FU,0x03U,0x90U,0x43U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_PAYCHAN,
|
|
SBUF(a), SBUF(b), 1, 0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x78U,0xEDU,0x04U,0xCEU,0x27U,0x20U,0x21U,0x55U,0x2BU,
|
|
0xBFU,0xA1U,0xE5U,0xFFU,0xBBU,0x53U,0xB6U,0x45U,0xA2U,0xFFU,
|
|
0x8AU,0x44U,0x66U,0xD5U,0x76U,0x24U,0xB5U,0x71U,0xE6U,0x44U,
|
|
0x9EU,0xEBU,0xFCU,0x5AU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
|
|
// again with uint256
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_PAYCHAN,
|
|
SBUF(a), SBUF(b), SBUF(ns)
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x78U,0x7DU,0xE1U,0x01U,0xF6U,0x2BU,0xB0U,0x55U,0x80U,
|
|
0xB9U,0xD6U,0xB0U,0x3FU,0x3BU,0xB0U,0x01U,0xBDU,0xE6U,0x9BU,
|
|
0x89U,0x0FU,0x8AU,0xCDU,0xBEU,0x71U,0x73U,0x5EU,0xC3U,0x63U,
|
|
0xF8U,0xC5U,0x4BU,0x9BU
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_EMITTED_TXN,
|
|
ns, 32,
|
|
0,0,0,0
|
|
)));
|
|
|
|
{
|
|
uint8_t ans[] =
|
|
{
|
|
0x00U,0x45U,0xF3U,0x51U,0x2DU,0x1CU,0x80U,0xA3U,0xC0U,0xB1U,
|
|
0x46U,0x04U,0xE1U,0xADU,0xDBU,0x90U,0x1CU,0x66U,0x32U,0x10U,
|
|
0x08U,0xCCU,0xD0U,0xABU,0xD2U,0xDBU,0xBEU,0xC4U,0x08U,0xA6U,
|
|
0x0FU,0x6AU,0x62U,0xE9U
|
|
};
|
|
ASSERT_KL_EQ(ans);
|
|
}
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_NFT_OFFER,
|
|
SBUF(a), 1, 0,
|
|
0,0
|
|
)));
|
|
|
|
|
|
ASSERT(34 == (e=util_keylet(buf, 34, KEYLET_NFT_OFFER,
|
|
SBUF(a), SBUF(ns),
|
|
0,0
|
|
)));
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set util_keylet"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test util_keylet"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_util_raddr()
|
|
{
|
|
testcase("Test util_raddr");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
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 util_raddr (uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x6BU, 0x30U, 0xE2U, 0x94U, 0xF3U, 0x40U, 0x3FU, 0xF8U,
|
|
0x7CU, 0xEFU, 0x9EU, 0x72U, 0x21U, 0x7FU, 0xF7U, 0xEBU,
|
|
0x4AU, 0x6AU, 0x43U, 0xF4U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x77U && addr[ 2] == 0x6DU && addr[ 3] == 0x6DU &&
|
|
addr[ 4] == 0x31U && addr[ 5] == 0x33U && addr[ 6] == 0x70U && addr[ 7] == 0x37U &&
|
|
addr[ 8] == 0x56U && addr[ 9] == 0x67U && addr[10] == 0x36U && addr[11] == 0x4BU &&
|
|
addr[12] == 0x6DU && addr[13] == 0x6EU && addr[14] == 0x71U && addr[15] == 0x4BU &&
|
|
addr[16] == 0x52U && addr[17] == 0x77U && addr[18] == 0x44U && addr[19] == 0x7AU &&
|
|
addr[20] == 0x78U && addr[21] == 0x76U && addr[22] == 0x69U && addr[23] == 0x35U &&
|
|
addr[24] == 0x58U && addr[25] == 0x70U && addr[26] == 0x36U && addr[27] == 0x77U &&
|
|
addr[28] == 0x6EU && addr[29] == 0x48U && addr[30] == 0x4DU && addr[31] == 0x44U &&
|
|
addr[32] == 0x44U && addr[33] == 0x68U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xE4U, 0x0FU, 0xA3U, 0x4EU, 0x3EU, 0x66U, 0x15U, 0x36U,
|
|
0x64U, 0x89U, 0x4FU, 0xCBU, 0xFBU, 0xFCU, 0xFEU, 0x2DU,
|
|
0x2DU, 0x19U, 0x0DU, 0x69U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4DU && addr[ 2] == 0x38U && addr[ 3] == 0x31U &&
|
|
addr[ 4] == 0x6FU && addr[ 5] == 0x48U && addr[ 6] == 0x77U && addr[ 7] == 0x68U &&
|
|
addr[ 8] == 0x37U && addr[ 9] == 0x35U && addr[10] == 0x39U && addr[11] == 0x34U &&
|
|
addr[12] == 0x6AU && addr[13] == 0x48U && addr[14] == 0x38U && addr[15] == 0x70U &&
|
|
addr[16] == 0x36U && addr[17] == 0x31U && addr[18] == 0x57U && addr[19] == 0x65U &&
|
|
addr[20] == 0x31U && addr[21] == 0x73U && addr[22] == 0x64U && addr[23] == 0x58U &&
|
|
addr[24] == 0x46U && addr[25] == 0x42U && addr[26] == 0x35U && addr[27] == 0x48U &&
|
|
addr[28] == 0x52U && addr[29] == 0x52U && addr[30] == 0x79U && addr[31] == 0x4BU &&
|
|
addr[32] == 0x76U && addr[33] == 0x4AU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x0CU, 0x90U, 0x4BU, 0x4FU, 0xA5U, 0x59U, 0xBFU, 0x10U,
|
|
0x6AU, 0xAEU, 0xB5U, 0x28U, 0x6CU, 0x94U, 0xBAU, 0x34U,
|
|
0x18U, 0xFDU, 0xF3U, 0x53U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x70U && addr[ 2] == 0x39U && addr[ 3] == 0x52U &&
|
|
addr[ 4] == 0x79U && addr[ 5] == 0x73U && addr[ 6] == 0x42U && addr[ 7] == 0x63U &&
|
|
addr[ 8] == 0x55U && addr[ 9] == 0x42U && addr[10] == 0x59U && addr[11] == 0x63U &&
|
|
addr[12] == 0x76U && addr[13] == 0x4AU && addr[14] == 0x4AU && addr[15] == 0x4BU &&
|
|
addr[16] == 0x38U && addr[17] == 0x54U && addr[18] == 0x48U && addr[19] == 0x45U &&
|
|
addr[20] == 0x79U && addr[21] == 0x6FU && addr[22] == 0x79U && addr[23] == 0x74U &&
|
|
addr[24] == 0x74U && addr[25] == 0x6BU && addr[26] == 0x57U && addr[27] == 0x58U &&
|
|
addr[28] == 0x39U && addr[29] == 0x4BU && addr[30] == 0x52U && addr[31] == 0x62U &&
|
|
addr[32] == 0x39U && addr[33] == 0x4DU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x75U, 0x82U, 0xFBU, 0x27U, 0x10U, 0x8CU, 0x0FU, 0x9AU,
|
|
0xF2U, 0x67U, 0x35U, 0xCCU, 0x7BU, 0x22U, 0x6BU, 0xD2U,
|
|
0x2FU, 0xDFU, 0x4FU, 0x92U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x42U && addr[ 2] == 0x35U && addr[ 3] == 0x4CU &&
|
|
addr[ 4] == 0x79U && addr[ 5] == 0x77U && addr[ 6] == 0x6BU && addr[ 7] == 0x54U &&
|
|
addr[ 8] == 0x4CU && addr[ 9] == 0x31U && addr[10] == 0x34U && addr[11] == 0x51U &&
|
|
addr[12] == 0x64U && addr[13] == 0x55U && addr[14] == 0x64U && addr[15] == 0x77U &&
|
|
addr[16] == 0x43U && addr[17] == 0x78U && addr[18] == 0x70U && addr[19] == 0x6EU &&
|
|
addr[20] == 0x65U && addr[21] == 0x46U && addr[22] == 0x32U && addr[23] == 0x7AU &&
|
|
addr[24] == 0x63U && addr[25] == 0x7AU && addr[26] == 0x46U && addr[27] == 0x66U &&
|
|
addr[28] == 0x44U && addr[29] == 0x7AU && addr[30] == 0x57U && addr[31] == 0x46U &&
|
|
addr[32] == 0x38U && addr[33] == 0x50U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x6CU, 0xB6U, 0x51U, 0x1FU, 0x20U, 0xECU, 0xCAU, 0x1EU,
|
|
0x98U, 0x03U, 0xFCU, 0xFAU, 0x6FU, 0x3EU, 0x56U, 0x75U,
|
|
0x72U, 0x29U, 0x51U, 0x97U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x77U && addr[ 2] == 0x75U && addr[ 3] == 0x46U &&
|
|
addr[ 4] == 0x50U && addr[ 5] == 0x4BU && addr[ 6] == 0x34U && addr[ 7] == 0x48U &&
|
|
addr[ 8] == 0x51U && addr[ 9] == 0x4EU && addr[10] == 0x73U && addr[11] == 0x59U &&
|
|
addr[12] == 0x42U && addr[13] == 0x47U && addr[14] == 0x74U && addr[15] == 0x46U &&
|
|
addr[16] == 0x52U && addr[17] == 0x4BU && addr[18] == 0x77U && addr[19] == 0x45U &&
|
|
addr[20] == 0x6DU && addr[21] == 0x75U && addr[22] == 0x41U && addr[23] == 0x68U &&
|
|
addr[24] == 0x63U && addr[25] == 0x4BU && addr[26] == 0x63U && addr[27] == 0x48U &&
|
|
addr[28] == 0x39U && addr[29] == 0x5AU && addr[30] == 0x32U && addr[31] == 0x59U &&
|
|
addr[32] == 0x7AU && addr[33] == 0x58U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xA5U, 0x31U, 0x30U, 0x28U, 0xF9U, 0x62U, 0xE4U, 0x80U,
|
|
0x48U, 0x94U, 0x3BU, 0x1AU, 0x59U, 0xBBU, 0x5EU, 0x36U,
|
|
0x96U, 0xB3U, 0x44U, 0x35U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x47U && addr[ 2] == 0x68U && addr[ 3] == 0x54U &&
|
|
addr[ 4] == 0x52U && addr[ 5] == 0x4AU && addr[ 6] == 0x5AU && addr[ 7] == 0x31U &&
|
|
addr[ 8] == 0x56U && addr[ 9] == 0x4DU && addr[10] == 0x51U && addr[11] == 0x74U &&
|
|
addr[12] == 0x36U && addr[13] == 0x6AU && addr[14] == 0x44U && addr[15] == 0x72U &&
|
|
addr[16] == 0x66U && addr[17] == 0x4EU && addr[18] == 0x63U && addr[19] == 0x6FU &&
|
|
addr[20] == 0x4AU && addr[21] == 0x34U && addr[22] == 0x39U && addr[23] == 0x6AU &&
|
|
addr[24] == 0x34U && addr[25] == 0x43U && addr[26] == 0x67U && addr[27] == 0x71U &&
|
|
addr[28] == 0x4BU && addr[29] == 0x6DU && addr[30] == 0x52U && addr[31] == 0x32U &&
|
|
addr[32] == 0x6FU && addr[33] == 0x36U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xBFU, 0x04U, 0x6CU, 0x79U, 0xA0U, 0x96U, 0xDEU, 0x80U,
|
|
0x66U, 0xD3U, 0x74U, 0xC8U, 0xDFU, 0x94U, 0x5FU, 0x89U,
|
|
0xF2U, 0x3EU, 0x9AU, 0x27U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4AU && addr[ 2] == 0x52U && addr[ 3] == 0x72U &&
|
|
addr[ 4] == 0x34U && addr[ 5] == 0x72U && addr[ 6] == 0x4CU && addr[ 7] == 0x32U &&
|
|
addr[ 8] == 0x43U && addr[ 9] == 0x4AU && addr[10] == 0x48U && addr[11] == 0x67U &&
|
|
addr[12] == 0x46U && addr[13] == 0x47U && addr[14] == 0x56U && addr[15] == 0x67U &&
|
|
addr[16] == 0x31U && addr[17] == 0x6AU && addr[18] == 0x61U && addr[19] == 0x66U &&
|
|
addr[20] == 0x39U && addr[21] == 0x4AU && addr[22] == 0x48U && addr[23] == 0x51U &&
|
|
addr[24] == 0x70U && addr[25] == 0x56U && addr[26] == 0x6DU && addr[27] == 0x68U &&
|
|
addr[28] == 0x76U && addr[29] == 0x45U && addr[30] == 0x37U && addr[31] == 0x68U &&
|
|
addr[32] == 0x61U && addr[33] == 0x62U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xE2U, 0x07U, 0xABU, 0xD3U, 0x7DU, 0xC2U, 0xCDU, 0xD4U,
|
|
0x6DU, 0x15U, 0x7BU, 0x67U, 0x5AU, 0xC8U, 0x3EU, 0x0EU,
|
|
0x05U, 0x9BU, 0x08U, 0x62U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4DU && addr[ 2] == 0x63U && addr[ 3] == 0x33U &&
|
|
addr[ 4] == 0x75U && addr[ 5] == 0x4DU && addr[ 6] == 0x6BU && addr[ 7] == 0x4BU &&
|
|
addr[ 8] == 0x31U && addr[ 9] == 0x62U && addr[10] == 0x62U && addr[11] == 0x32U &&
|
|
addr[12] == 0x64U && addr[13] == 0x4BU && addr[14] == 0x7AU && addr[15] == 0x5AU &&
|
|
addr[16] == 0x64U && addr[17] == 0x56U && addr[18] == 0x71U && addr[19] == 0x35U &&
|
|
addr[20] == 0x75U && addr[21] == 0x59U && addr[22] == 0x54U && addr[23] == 0x55U &&
|
|
addr[24] == 0x37U && addr[25] == 0x5AU && addr[26] == 0x76U && addr[27] == 0x4EU &&
|
|
addr[28] == 0x45U && addr[29] == 0x41U && addr[30] == 0x32U && addr[31] == 0x33U &&
|
|
addr[32] == 0x67U && addr[33] == 0x44U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x2AU, 0x56U, 0x74U, 0x25U, 0x84U, 0x8DU, 0x41U, 0x6DU,
|
|
0xF1U, 0x06U, 0x01U, 0x6CU, 0x2AU, 0xB1U, 0x13U, 0xC3U,
|
|
0x1EU, 0x65U, 0x63U, 0x80U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x68U && addr[ 2] == 0x69U && addr[ 3] == 0x69U &&
|
|
addr[ 4] == 0x41U && addr[ 5] == 0x78U && addr[ 6] == 0x79U && addr[ 7] == 0x59U &&
|
|
addr[ 8] == 0x41U && addr[ 9] == 0x43U && addr[10] == 0x67U && addr[11] == 0x45U &&
|
|
addr[12] == 0x52U && addr[13] == 0x4BU && addr[14] == 0x47U && addr[15] == 0x51U &&
|
|
addr[16] == 0x4DU && addr[17] == 0x72U && addr[18] == 0x53U && addr[19] == 0x5AU &&
|
|
addr[20] == 0x57U && addr[21] == 0x43U && addr[22] == 0x74U && addr[23] == 0x6BU &&
|
|
addr[24] == 0x4DU && addr[25] == 0x6FU && addr[26] == 0x69U && addr[27] == 0x58U &&
|
|
addr[28] == 0x48U && addr[29] == 0x34U && addr[30] == 0x64U && addr[31] == 0x48U &&
|
|
addr[32] == 0x6EU && addr[33] == 0x6FU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x24U, 0xBBU, 0xA9U, 0xC3U, 0x95U, 0x74U, 0x9AU, 0x88U,
|
|
0x04U, 0x12U, 0xC0U, 0x91U, 0xE7U, 0x13U, 0x41U, 0x7FU,
|
|
0x9AU, 0xD5U, 0x74U, 0x43U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x68U && addr[ 2] == 0x4DU && addr[ 3] == 0x4EU &&
|
|
addr[ 4] == 0x33U && addr[ 5] == 0x79U && addr[ 6] == 0x4EU && addr[ 7] == 0x50U &&
|
|
addr[ 8] == 0x4EU && addr[ 9] == 0x74U && addr[10] == 0x4BU && addr[11] == 0x70U &&
|
|
addr[12] == 0x78U && addr[13] == 0x6BU && addr[14] == 0x71U && addr[15] == 0x4CU &&
|
|
addr[16] == 0x78U && addr[17] == 0x51U && addr[18] == 0x32U && addr[19] == 0x63U &&
|
|
addr[20] == 0x33U && addr[21] == 0x55U && addr[22] == 0x68U && addr[23] == 0x6FU &&
|
|
addr[24] == 0x41U && addr[25] == 0x7AU && addr[26] == 0x66U && addr[27] == 0x75U &&
|
|
addr[28] == 0x59U && addr[29] == 0x35U && addr[30] == 0x75U && addr[31] == 0x35U &&
|
|
addr[32] == 0x4AU && addr[33] == 0x7AU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x49U, 0x53U, 0x9EU, 0x65U, 0x21U, 0x8AU, 0xCFU, 0x37U,
|
|
0x85U, 0x2BU, 0xFFU, 0x87U, 0x14U, 0x76U, 0xDAU, 0x1AU,
|
|
0x62U, 0x3AU, 0xEAU, 0x80U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x66U && addr[ 2] == 0x67U && addr[ 3] == 0x35U &&
|
|
addr[ 4] == 0x56U && addr[ 5] == 0x41U && addr[ 6] == 0x44U && addr[ 7] == 0x41U &&
|
|
addr[ 8] == 0x4DU && addr[ 9] == 0x4DU && addr[10] == 0x42U && addr[11] == 0x78U &&
|
|
addr[12] == 0x46U && addr[13] == 0x51U && addr[14] == 0x76U && addr[15] == 0x44U &&
|
|
addr[16] == 0x78U && addr[17] == 0x5AU && addr[18] == 0x54U && addr[19] == 0x32U &&
|
|
addr[20] == 0x52U && addr[21] == 0x6AU && addr[22] == 0x55U && addr[23] == 0x64U &&
|
|
addr[24] == 0x47U && addr[25] == 0x69U && addr[26] == 0x64U && addr[27] == 0x59U &&
|
|
addr[28] == 0x61U && addr[29] == 0x35U && addr[30] == 0x76U && addr[31] == 0x69U &&
|
|
addr[32] == 0x37U && addr[33] == 0x5AU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xE7U, 0xD3U, 0x03U, 0xBCU, 0xAEU, 0xBDU, 0x62U, 0x20U,
|
|
0xAEU, 0xC2U, 0xE1U, 0x7EU, 0x0BU, 0xFFU, 0xDCU, 0x21U,
|
|
0x24U, 0x34U, 0x50U, 0x82U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x34U && addr[ 2] == 0x33U && addr[ 3] == 0x6DU &&
|
|
addr[ 4] == 0x31U && addr[ 5] == 0x31U && addr[ 6] == 0x66U && addr[ 7] == 0x74U &&
|
|
addr[ 8] == 0x36U && addr[ 9] == 0x79U && addr[10] == 0x6FU && addr[11] == 0x50U &&
|
|
addr[12] == 0x69U && addr[13] == 0x6DU && addr[14] == 0x36U && addr[15] == 0x56U &&
|
|
addr[16] == 0x44U && addr[17] == 0x78U && addr[18] == 0x64U && addr[19] == 0x55U &&
|
|
addr[20] == 0x76U && addr[21] == 0x63U && addr[22] == 0x46U && addr[23] == 0x77U &&
|
|
addr[24] == 0x36U && addr[25] == 0x57U && addr[26] == 0x38U && addr[27] == 0x41U &&
|
|
addr[28] == 0x77U && addr[29] == 0x78U && addr[30] == 0x78U && addr[31] == 0x4BU &&
|
|
addr[32] == 0x35U && addr[33] == 0x58U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xC3U, 0xE1U, 0x5FU, 0xABU, 0xC0U, 0x0AU, 0x79U, 0x73U,
|
|
0x71U, 0xD0U, 0x55U, 0xC0U, 0x80U, 0x79U, 0xAEU, 0x45U,
|
|
0x71U, 0x0FU, 0xA0U, 0x97U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4AU && addr[ 2] == 0x69U && addr[ 3] == 0x35U &&
|
|
addr[ 4] == 0x6BU && addr[ 5] == 0x65U && addr[ 6] == 0x58U && addr[ 7] == 0x79U &&
|
|
addr[ 8] == 0x33U && addr[ 9] == 0x31U && addr[10] == 0x4AU && addr[11] == 0x31U &&
|
|
addr[12] == 0x34U && addr[13] == 0x52U && addr[14] == 0x4BU && addr[15] == 0x73U &&
|
|
addr[16] == 0x4EU && addr[17] == 0x59U && addr[18] == 0x41U && addr[19] == 0x46U &&
|
|
addr[20] == 0x31U && addr[21] == 0x51U && addr[22] == 0x36U && addr[23] == 0x6AU &&
|
|
addr[24] == 0x4DU && addr[25] == 0x56U && addr[26] == 0x69U && addr[27] == 0x45U &&
|
|
addr[28] == 0x52U && addr[29] == 0x55U && addr[30] == 0x51U && addr[31] == 0x71U &&
|
|
addr[32] == 0x59U && addr[33] == 0x36U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x95U, 0x15U, 0x7FU, 0x2AU, 0xAFU, 0xE3U, 0x2FU, 0x7FU,
|
|
0x2EU, 0xF1U, 0xA0U, 0xF5U, 0xEAU, 0xC3U, 0x07U, 0x06U,
|
|
0xA1U, 0xD3U, 0xF5U, 0xD9U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4EU && addr[ 2] == 0x62U && addr[ 3] == 0x48U &&
|
|
addr[ 4] == 0x53U && addr[ 5] == 0x55U && addr[ 6] == 0x6DU && addr[ 7] == 0x66U &&
|
|
addr[ 8] == 0x4BU && addr[ 9] == 0x61U && addr[10] == 0x34U && addr[11] == 0x71U &&
|
|
addr[12] == 0x31U && addr[13] == 0x51U && addr[14] == 0x78U && addr[15] == 0x44U &&
|
|
addr[16] == 0x45U && addr[17] == 0x5AU && addr[18] == 0x4CU && addr[19] == 0x6EU &&
|
|
addr[20] == 0x54U && addr[21] == 0x67U && addr[22] == 0x46U && addr[23] == 0x56U &&
|
|
addr[24] == 0x45U && addr[25] == 0x4CU && addr[26] == 0x78U && addr[27] == 0x39U &&
|
|
addr[28] == 0x6DU && addr[29] == 0x57U && addr[30] == 0x45U && addr[31] == 0x43U &&
|
|
addr[32] == 0x6BU && addr[33] == 0x41U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xF0U, 0xECU, 0x0FU, 0x86U, 0x31U, 0xBBU, 0x2CU, 0xBFU,
|
|
0x8FU, 0xB7U, 0xE3U, 0x1CU, 0x82U, 0xA0U, 0xA3U, 0x50U,
|
|
0xD5U, 0xE0U, 0xFEU, 0x6BU
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x34U && addr[ 2] == 0x78U && addr[ 3] == 0x31U &&
|
|
addr[ 4] == 0x78U && addr[ 5] == 0x46U && addr[ 6] == 0x32U && addr[ 7] == 0x42U &&
|
|
addr[ 8] == 0x47U && addr[ 9] == 0x73U && addr[10] == 0x42U && addr[11] == 0x41U &&
|
|
addr[12] == 0x7AU && addr[13] == 0x77U && addr[14] == 0x77U && addr[15] == 0x61U &&
|
|
addr[16] == 0x4BU && addr[17] == 0x61U && addr[18] == 0x70U && addr[19] == 0x4BU &&
|
|
addr[20] == 0x6FU && addr[21] == 0x6FU && addr[22] == 0x35U && addr[23] == 0x57U &&
|
|
addr[24] == 0x65U && addr[25] == 0x31U && addr[26] == 0x59U && addr[27] == 0x53U &&
|
|
addr[28] == 0x6EU && addr[29] == 0x52U && addr[30] == 0x50U && addr[31] == 0x57U &&
|
|
addr[32] == 0x75U && addr[33] == 0x39U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x8DU, 0xA4U, 0x7DU, 0xABU, 0xD1U, 0x19U, 0xDCU, 0xC4U,
|
|
0x45U, 0x5FU, 0xAAU, 0xE2U, 0x1CU, 0x39U, 0xCAU, 0x19U,
|
|
0x34U, 0xF1U, 0x86U, 0x16U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x44U && addr[ 2] == 0x75U && addr[ 3] == 0x41U &&
|
|
addr[ 4] == 0x4CU && addr[ 5] == 0x6EU && addr[ 6] == 0x52U && addr[ 7] == 0x79U &&
|
|
addr[ 8] == 0x76U && addr[ 9] == 0x77U && addr[10] == 0x38U && addr[11] == 0x36U &&
|
|
addr[12] == 0x43U && addr[13] == 0x63U && addr[14] == 0x55U && addr[15] == 0x5AU &&
|
|
addr[16] == 0x39U && addr[17] == 0x74U && addr[18] == 0x52U && addr[19] == 0x55U &&
|
|
addr[20] == 0x45U && addr[21] == 0x6DU && addr[22] == 0x35U && addr[23] == 0x43U &&
|
|
addr[24] == 0x61U && addr[25] == 0x65U && addr[26] == 0x50U && addr[27] == 0x46U &&
|
|
addr[28] == 0x66U && addr[29] == 0x33U && addr[30] == 0x74U && addr[31] == 0x36U &&
|
|
addr[32] == 0x61U && addr[33] == 0x31U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xA9U, 0x94U, 0x5AU, 0xE3U, 0x5AU, 0x43U, 0xADU, 0xBEU,
|
|
0xBAU, 0xA4U, 0x13U, 0x94U, 0xF5U, 0xDCU, 0x8FU, 0x3BU,
|
|
0x01U, 0x14U, 0xFFU, 0xFEU
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x47U && addr[ 2] == 0x54U && addr[ 3] == 0x65U &&
|
|
addr[ 4] == 0x76U && addr[ 5] == 0x4AU && addr[ 6] == 0x71U && addr[ 7] == 0x76U &&
|
|
addr[ 8] == 0x6BU && addr[ 9] == 0x5AU && addr[10] == 0x76U && addr[11] == 0x48U &&
|
|
addr[12] == 0x73U && addr[13] == 0x58U && addr[14] == 0x5AU && addr[15] == 0x71U &&
|
|
addr[16] == 0x55U && addr[17] == 0x78U && addr[18] == 0x43U && addr[19] == 0x48U &&
|
|
addr[20] == 0x4CU && addr[21] == 0x68U && addr[22] == 0x73U && addr[23] == 0x43U &&
|
|
addr[24] == 0x53U && addr[25] == 0x38U && addr[26] == 0x57U && addr[27] == 0x68U &&
|
|
addr[28] == 0x79U && addr[29] == 0x4DU && addr[30] == 0x74U && addr[31] == 0x7AU &&
|
|
addr[32] == 0x6EU && addr[33] == 0x5AU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xC1U, 0xE6U, 0x7FU, 0x17U, 0xD3U, 0x00U, 0x9BU, 0x80U,
|
|
0x6CU, 0x85U, 0x74U, 0x9CU, 0x80U, 0x40U, 0xAFU, 0x64U,
|
|
0xCEU, 0x09U, 0x7EU, 0x2EU
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4AU && addr[ 2] == 0x67U && addr[ 3] == 0x45U &&
|
|
addr[ 4] == 0x59U && addr[ 5] == 0x55U && addr[ 6] == 0x37U && addr[ 7] == 0x36U &&
|
|
addr[ 8] == 0x45U && addr[ 9] == 0x55U && addr[10] == 0x34U && addr[11] == 0x59U &&
|
|
addr[12] == 0x41U && addr[13] == 0x5AU && addr[14] == 0x41U && addr[15] == 0x44U &&
|
|
addr[16] == 0x79U && addr[17] == 0x61U && addr[18] == 0x37U && addr[19] == 0x6BU &&
|
|
addr[20] == 0x37U && addr[21] == 0x62U && addr[22] == 0x71U && addr[23] == 0x38U &&
|
|
addr[24] == 0x4EU && addr[25] == 0x76U && addr[26] == 0x64U && addr[27] == 0x65U &&
|
|
addr[28] == 0x4BU && addr[29] == 0x41U && addr[30] == 0x48U && addr[31] == 0x69U &&
|
|
addr[32] == 0x32U && addr[33] == 0x50U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0xD8U, 0x74U, 0xCFU, 0x61U, 0x0DU, 0x97U, 0xE4U, 0xABU,
|
|
0x76U, 0xA0U, 0x70U, 0x60U, 0xB7U, 0xC5U, 0x9CU, 0x9AU,
|
|
0x88U, 0x86U, 0x62U, 0xAAU
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4CU && addr[ 2] == 0x6AU && addr[ 3] == 0x57U &&
|
|
addr[ 4] == 0x74U && addr[ 5] == 0x59U && addr[ 6] == 0x52U && addr[ 7] == 0x61U &&
|
|
addr[ 8] == 0x6EU && addr[ 9] == 0x61U && addr[10] == 0x6BU && addr[11] == 0x33U &&
|
|
addr[12] == 0x74U && addr[13] == 0x52U && addr[14] == 0x43U && addr[15] == 0x5AU &&
|
|
addr[16] == 0x42U && addr[17] == 0x69U && addr[18] == 0x61U && addr[19] == 0x38U &&
|
|
addr[20] == 0x64U && addr[21] == 0x33U && addr[22] == 0x70U && addr[23] == 0x7AU &&
|
|
addr[24] == 0x78U && addr[25] == 0x6BU && addr[26] == 0x6EU && addr[27] == 0x63U &&
|
|
addr[28] == 0x73U && addr[29] == 0x7AU && addr[30] == 0x6FU && addr[31] == 0x33U &&
|
|
addr[32] == 0x33U && addr[33] == 0x38U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x8EU, 0xADU, 0xB4U, 0xBBU, 0x71U, 0x2AU, 0x29U, 0x1BU,
|
|
0x53U, 0x43U, 0xE0U, 0x03U, 0x1FU, 0x97U, 0x6BU, 0x0DU,
|
|
0xA9U, 0xEDU, 0x39U, 0xC2U
|
|
};
|
|
uint8_t addr[50];
|
|
ASSERT(34 ==
|
|
util_raddr((uint32_t)addr, sizeof(addr), raw, 20));
|
|
ASSERT(
|
|
addr[ 0] == 0x72U && addr[ 1] == 0x4EU && addr[ 2] == 0x72U && addr[ 3] == 0x52U &&
|
|
addr[ 4] == 0x73U && addr[ 5] == 0x59U && addr[ 6] == 0x57U && addr[ 7] == 0x69U &&
|
|
addr[ 8] == 0x4AU && addr[ 9] == 0x53U && addr[10] == 0x64U && addr[11] == 0x39U &&
|
|
addr[12] == 0x47U && addr[13] == 0x4AU && addr[14] == 0x50U && addr[15] == 0x50U &&
|
|
addr[16] == 0x36U && addr[17] == 0x51U && addr[18] == 0x71U && addr[19] == 0x33U &&
|
|
addr[20] == 0x4AU && addr[21] == 0x61U && addr[22] == 0x44U && addr[23] == 0x43U &&
|
|
addr[24] == 0x37U && addr[25] == 0x53U && addr[26] == 0x48U && addr[27] == 0x61U &&
|
|
addr[28] == 0x57U && addr[29] == 0x66U && addr[30] == 0x68U && addr[31] == 0x32U &&
|
|
addr[32] == 0x33U && addr[33] == 0x4BU);
|
|
}
|
|
|
|
// Test out of bounds check
|
|
ASSERT(util_raddr(1000000, 50, 0, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(util_raddr(0, 50, 10000000, 20) == OUT_OF_BOUNDS);
|
|
uint8_t raw[20] = {
|
|
0x8EU, 0xADU, 0xB4U, 0xBBU, 0x71U, 0x2AU, 0x29U, 0x1BU,
|
|
0x53U, 0x43U, 0xE0U, 0x03U, 0x1FU, 0x97U, 0x6BU, 0x0DU,
|
|
0xA9U, 0xEDU, 0x39U, 0xC2U
|
|
};
|
|
ASSERT(util_raddr(0, 30, raw, 20) == TOO_SMALL);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set util_raddr"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test util_raddr"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_util_sha512h()
|
|
{
|
|
testcase("Test util_sha512h");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
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 util_sha512h (uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4EU, 0x36U, 0x53U, 0x59U, 0x77U, 0x72U, 0x32U,
|
|
0x64U, 0x54U, 0x56U, 0x43U, 0x7AU, 0x45U, 0x71U, 0x39U,
|
|
0x57U, 0x43U, 0x77U, 0x4AU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x42U && hash[ 1] == 0x5CU && hash[ 2] == 0x4CU && hash[ 3] == 0x01U &&
|
|
hash[ 4] == 0x84U && hash[ 5] == 0xA5U && hash[ 6] == 0x76U && hash[ 7] == 0x79U &&
|
|
hash[ 8] == 0xDCU && hash[ 9] == 0x6DU && hash[10] == 0xFFU && hash[11] == 0x40U &&
|
|
hash[12] == 0x8CU && hash[13] == 0x29U && hash[14] == 0x06U && hash[15] == 0x6BU &&
|
|
hash[16] == 0x0FU && hash[17] == 0xB9U && hash[18] == 0xEAU && hash[19] == 0x34U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4BU, 0x4BU, 0x75U, 0x52U, 0x36U, 0x36U, 0x46U,
|
|
0x62U, 0x38U, 0x33U, 0x76U, 0x35U, 0x71U, 0x79U, 0x41U,
|
|
0x34U, 0x48U, 0x67U, 0x6AU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x36U && hash[ 1] == 0x2CU && hash[ 2] == 0x32U && hash[ 3] == 0x1DU &&
|
|
hash[ 4] == 0x8DU && hash[ 5] == 0xDDU && hash[ 6] == 0xAFU && hash[ 7] == 0x2DU &&
|
|
hash[ 8] == 0x3CU && hash[ 9] == 0xE6U && hash[10] == 0x94U && hash[11] == 0x12U &&
|
|
hash[12] == 0x20U && hash[13] == 0xDAU && hash[14] == 0x62U && hash[15] == 0xA6U &&
|
|
hash[16] == 0x98U && hash[17] == 0x41U && hash[18] == 0x04U && hash[19] == 0x5EU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x42U, 0x54U, 0x33U, 0x58U, 0x57U, 0x43U, 0x76U,
|
|
0x61U, 0x38U, 0x48U, 0x55U, 0x4EU, 0x4EU, 0x5AU, 0x46U,
|
|
0x6AU, 0x5AU, 0x43U, 0x55U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xCFU && hash[ 1] == 0xFDU && hash[ 2] == 0x6FU && hash[ 3] == 0x01U &&
|
|
hash[ 4] == 0x95U && hash[ 5] == 0x76U && hash[ 6] == 0x7DU && hash[ 7] == 0xFBU &&
|
|
hash[ 8] == 0xCAU && hash[ 9] == 0x41U && hash[10] == 0xFDU && hash[11] == 0x24U &&
|
|
hash[12] == 0x23U && hash[13] == 0xD6U && hash[14] == 0x82U && hash[15] == 0x20U &&
|
|
hash[16] == 0x76U && hash[17] == 0xDDU && hash[18] == 0xC9U && hash[19] == 0xECU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4CU, 0x52U, 0x4CU, 0x41U, 0x6EU, 0x61U, 0x62U,
|
|
0x56U, 0x6FU, 0x46U, 0x62U, 0x37U, 0x47U, 0x68U, 0x79U,
|
|
0x58U, 0x75U, 0x42U, 0x53U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x02U && hash[ 1] == 0xEBU && hash[ 2] == 0x2FU && hash[ 3] == 0x30U &&
|
|
hash[ 4] == 0xFCU && hash[ 5] == 0x73U && hash[ 6] == 0x34U && hash[ 7] == 0xE7U &&
|
|
hash[ 8] == 0x89U && hash[ 9] == 0xA2U && hash[10] == 0x58U && hash[11] == 0xD6U &&
|
|
hash[12] == 0xB0U && hash[13] == 0x55U && hash[14] == 0x32U && hash[15] == 0x96U &&
|
|
hash[16] == 0xB5U && hash[17] == 0x2EU && hash[18] == 0x97U && hash[19] == 0x81U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4CU, 0x37U, 0x33U, 0x39U, 0x47U, 0x4BU, 0x35U,
|
|
0x75U, 0x36U, 0x79U, 0x78U, 0x76U, 0x43U, 0x73U, 0x6FU,
|
|
0x68U, 0x43U, 0x32U, 0x43U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x9FU && hash[ 1] == 0xD4U && hash[ 2] == 0x7CU && hash[ 3] == 0x25U &&
|
|
hash[ 4] == 0xDEU && hash[ 5] == 0x23U && hash[ 6] == 0x97U && hash[ 7] == 0x57U &&
|
|
hash[ 8] == 0xEDU && hash[ 9] == 0x25U && hash[10] == 0xD0U && hash[11] == 0x98U &&
|
|
hash[12] == 0xF7U && hash[13] == 0x83U && hash[14] == 0x70U && hash[15] == 0xF6U &&
|
|
hash[16] == 0x5FU && hash[17] == 0x3DU && hash[18] == 0xB5U && hash[19] == 0x43U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4DU, 0x4DU, 0x45U, 0x57U, 0x74U, 0x75U, 0x4BU,
|
|
0x43U, 0x77U, 0x54U, 0x43U, 0x36U, 0x31U, 0x78U, 0x41U,
|
|
0x78U, 0x35U, 0x55U, 0x46U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x77U && hash[ 1] == 0x59U && hash[ 2] == 0x43U && hash[ 3] == 0x6BU &&
|
|
hash[ 4] == 0x4DU && hash[ 5] == 0x11U && hash[ 6] == 0x6BU && hash[ 7] == 0xE5U &&
|
|
hash[ 8] == 0xF8U && hash[ 9] == 0x90U && hash[10] == 0x07U && hash[11] == 0x00U &&
|
|
hash[12] == 0xB3U && hash[13] == 0xB2U && hash[14] == 0x6BU && hash[15] == 0x8AU &&
|
|
hash[16] == 0xC8U && hash[17] == 0xF2U && hash[18] == 0x82U && hash[19] == 0xB7U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x66U, 0x48U, 0x6AU, 0x66U, 0x31U, 0x6BU, 0x70U,
|
|
0x4BU, 0x6AU, 0x39U, 0x66U, 0x6AU, 0x39U, 0x35U, 0x58U,
|
|
0x6AU, 0x59U, 0x69U, 0x51U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xBDU && hash[ 1] == 0x1BU && hash[ 2] == 0xDDU && hash[ 3] == 0x9DU &&
|
|
hash[ 4] == 0x10U && hash[ 5] == 0xDEU && hash[ 6] == 0x24U && hash[ 7] == 0xA1U &&
|
|
hash[ 8] == 0xB2U && hash[ 9] == 0x6CU && hash[10] == 0x24U && hash[11] == 0xBCU &&
|
|
hash[12] == 0xF9U && hash[13] == 0x97U && hash[14] == 0x50U && hash[15] == 0xDEU &&
|
|
hash[16] == 0x93U && hash[17] == 0x39U && hash[18] == 0x58U && hash[19] == 0x21U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x66U, 0x6EU, 0x75U, 0x57U, 0x38U, 0x77U, 0x6FU,
|
|
0x4BU, 0x62U, 0x6EU, 0x57U, 0x4BU, 0x6BU, 0x6BU, 0x75U,
|
|
0x39U, 0x6AU, 0x79U, 0x64U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x3BU && hash[ 1] == 0x89U && hash[ 2] == 0xEDU && hash[ 3] == 0x68U &&
|
|
hash[ 4] == 0x0DU && hash[ 5] == 0x13U && hash[ 6] == 0x3BU && hash[ 7] == 0x1DU &&
|
|
hash[ 8] == 0x43U && hash[ 9] == 0xFEU && hash[10] == 0xAEU && hash[11] == 0x3EU &&
|
|
hash[12] == 0xC3U && hash[13] == 0x90U && hash[14] == 0xE8U && hash[15] == 0x0EU &&
|
|
hash[16] == 0x17U && hash[17] == 0x14U && hash[18] == 0x23U && hash[19] == 0x71U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x70U, 0x79U, 0x64U, 0x52U, 0x39U, 0x55U, 0x32U,
|
|
0x67U, 0x66U, 0x75U, 0x6BU, 0x34U, 0x5AU, 0x72U, 0x53U,
|
|
0x66U, 0x48U, 0x61U, 0x71U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x2BU && hash[ 1] == 0x01U && hash[ 2] == 0x00U && hash[ 3] == 0x05U &&
|
|
hash[ 4] == 0xF1U && hash[ 5] == 0x60U && hash[ 6] == 0x71U && hash[ 7] == 0x62U &&
|
|
hash[ 8] == 0x7CU && hash[ 9] == 0x4AU && hash[10] == 0xCCU && hash[11] == 0x03U &&
|
|
hash[12] == 0x2AU && hash[13] == 0x89U && hash[14] == 0x40U && hash[15] == 0x5AU &&
|
|
hash[16] == 0x03U && hash[17] == 0xDCU && hash[18] == 0x83U && hash[19] == 0xC8U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x4CU, 0x4CU, 0x45U, 0x36U, 0x34U, 0x74U, 0x44U,
|
|
0x4CU, 0x78U, 0x59U, 0x37U, 0x47U, 0x6FU, 0x41U, 0x41U,
|
|
0x57U, 0x66U, 0x73U, 0x36U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xDFU && hash[ 1] == 0xE3U && hash[ 2] == 0x14U && hash[ 3] == 0xF0U &&
|
|
hash[ 4] == 0x5FU && hash[ 5] == 0x95U && hash[ 6] == 0x8CU && hash[ 7] == 0x57U &&
|
|
hash[ 8] == 0x2FU && hash[ 9] == 0x9DU && hash[10] == 0x45U && hash[11] == 0xDCU &&
|
|
hash[12] == 0x12U && hash[13] == 0x77U && hash[14] == 0x39U && hash[15] == 0xACU &&
|
|
hash[16] == 0xEAU && hash[17] == 0x4AU && hash[18] == 0xB0U && hash[19] == 0x8FU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x50U, 0x64U, 0x50U, 0x58U, 0x77U, 0x76U, 0x75U,
|
|
0x39U, 0x4EU, 0x4CU, 0x59U, 0x46U, 0x50U, 0x34U, 0x69U,
|
|
0x56U, 0x64U, 0x56U, 0x70U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xD1U && hash[ 1] == 0x9FU && hash[ 2] == 0x25U && hash[ 3] == 0x93U &&
|
|
hash[ 4] == 0xA3U && hash[ 5] == 0xCAU && hash[ 6] == 0xEAU && hash[ 7] == 0x10U &&
|
|
hash[ 8] == 0x06U && hash[ 9] == 0x78U && hash[10] == 0xFCU && hash[11] == 0x58U &&
|
|
hash[12] == 0xA4U && hash[13] == 0x99U && hash[14] == 0x3CU && hash[15] == 0x6EU &&
|
|
hash[16] == 0xC4U && hash[17] == 0x2DU && hash[18] == 0x6DU && hash[19] == 0x53U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x44U, 0x65U, 0x44U, 0x32U, 0x5AU, 0x71U, 0x53U,
|
|
0x48U, 0x35U, 0x44U, 0x70U, 0x51U, 0x4DU, 0x78U, 0x76U,
|
|
0x36U, 0x36U, 0x52U, 0x6BU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x4DU && hash[ 1] == 0x5BU && hash[ 2] == 0xDDU && hash[ 3] == 0x31U &&
|
|
hash[ 4] == 0xDEU && hash[ 5] == 0xB9U && hash[ 6] == 0xF5U && hash[ 7] == 0xB8U &&
|
|
hash[ 8] == 0xBDU && hash[ 9] == 0x17U && hash[10] == 0xE1U && hash[11] == 0x51U &&
|
|
hash[12] == 0xAAU && hash[13] == 0x51U && hash[14] == 0x9CU && hash[15] == 0x5BU &&
|
|
hash[16] == 0xE0U && hash[17] == 0x15U && hash[18] == 0x61U && hash[19] == 0x2CU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x55U, 0x34U, 0x78U, 0x54U, 0x52U, 0x75U, 0x6FU,
|
|
0x32U, 0x34U, 0x62U, 0x52U, 0x6FU, 0x65U, 0x41U, 0x48U,
|
|
0x33U, 0x53U, 0x55U, 0x66U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xBDU && hash[ 1] == 0xA1U && hash[ 2] == 0x62U && hash[ 3] == 0x1EU &&
|
|
hash[ 4] == 0x84U && hash[ 5] == 0x12U && hash[ 6] == 0xB3U && hash[ 7] == 0xCCU &&
|
|
hash[ 8] == 0x58U && hash[ 9] == 0x19U && hash[10] == 0x9AU && hash[11] == 0x22U &&
|
|
hash[12] == 0xCFU && hash[13] == 0x6AU && hash[14] == 0x0AU && hash[15] == 0x43U &&
|
|
hash[16] == 0xDEU && hash[17] == 0xB5U && hash[18] == 0xBAU && hash[19] == 0x50U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x6EU, 0x6DU, 0x6FU, 0x6AU, 0x57U, 0x46U, 0x6FU,
|
|
0x41U, 0x58U, 0x72U, 0x76U, 0x71U, 0x75U, 0x62U, 0x6FU,
|
|
0x45U, 0x77U, 0x4EU, 0x4EU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x5FU && hash[ 1] == 0x26U && hash[ 2] == 0xF9U && hash[ 3] == 0x0AU &&
|
|
hash[ 4] == 0xC7U && hash[ 5] == 0xD5U && hash[ 6] == 0x40U && hash[ 7] == 0x2DU &&
|
|
hash[ 8] == 0x1FU && hash[ 9] == 0x9EU && hash[10] == 0x46U && hash[11] == 0xAAU &&
|
|
hash[12] == 0x6DU && hash[13] == 0x9CU && hash[14] == 0x64U && hash[15] == 0x88U &&
|
|
hash[16] == 0x87U && hash[17] == 0xF3U && hash[18] == 0x29U && hash[19] == 0x72U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x61U, 0x33U, 0x57U, 0x65U, 0x64U, 0x69U, 0x71U,
|
|
0x58U, 0x37U, 0x34U, 0x79U, 0x42U, 0x42U, 0x68U, 0x48U,
|
|
0x4CU, 0x44U, 0x51U, 0x4DU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x25U && hash[ 1] == 0x70U && hash[ 2] == 0x5FU && hash[ 3] == 0x6DU &&
|
|
hash[ 4] == 0xA8U && hash[ 5] == 0x60U && hash[ 6] == 0x54U && hash[ 7] == 0xBAU &&
|
|
hash[ 8] == 0xD8U && hash[ 9] == 0x33U && hash[10] == 0x41U && hash[11] == 0x48U &&
|
|
hash[12] == 0x95U && hash[13] == 0x52U && hash[14] == 0xA6U && hash[15] == 0x22U &&
|
|
hash[16] == 0x9DU && hash[17] == 0x82U && hash[18] == 0xA0U && hash[19] == 0x87U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x45U, 0x47U, 0x57U, 0x33U, 0x6BU, 0x6FU, 0x34U,
|
|
0x41U, 0x31U, 0x69U, 0x50U, 0x43U, 0x5AU, 0x54U, 0x78U,
|
|
0x6DU, 0x77U, 0x6AU, 0x44U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xD4U && hash[ 1] == 0xDAU && hash[ 2] == 0xE0U && hash[ 3] == 0xC7U &&
|
|
hash[ 4] == 0x40U && hash[ 5] == 0xC4U && hash[ 6] == 0x28U && hash[ 7] == 0x59U &&
|
|
hash[ 8] == 0xA9U && hash[ 9] == 0x6DU && hash[10] == 0x91U && hash[11] == 0xDCU &&
|
|
hash[12] == 0x34U && hash[13] == 0x0DU && hash[14] == 0xB9U && hash[15] == 0xE6U &&
|
|
hash[16] == 0xE9U && hash[17] == 0x9DU && hash[18] == 0x04U && hash[19] == 0x0BU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x68U, 0x52U, 0x46U, 0x71U, 0x54U, 0x35U, 0x45U,
|
|
0x39U, 0x7AU, 0x63U, 0x69U, 0x70U, 0x68U, 0x4CU, 0x54U,
|
|
0x39U, 0x78U, 0x6AU, 0x52U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x61U && hash[ 1] == 0x5BU && hash[ 2] == 0xFEU && hash[ 3] == 0x17U &&
|
|
hash[ 4] == 0x6EU && hash[ 5] == 0x81U && hash[ 6] == 0x42U && hash[ 7] == 0xFFU &&
|
|
hash[ 8] == 0xEEU && hash[ 9] == 0xD7U && hash[10] == 0x1AU && hash[11] == 0x6DU &&
|
|
hash[12] == 0x14U && hash[13] == 0x5DU && hash[14] == 0x64U && hash[15] == 0xA8U &&
|
|
hash[16] == 0x20U && hash[17] == 0x1AU && hash[18] == 0x33U && hash[19] == 0xC3U);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x70U, 0x61U, 0x4AU, 0x69U, 0x34U, 0x4CU, 0x62U,
|
|
0x55U, 0x36U, 0x55U, 0x63U, 0x4AU, 0x45U, 0x78U, 0x62U,
|
|
0x38U, 0x39U, 0x35U, 0x5AU
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x01U && hash[ 1] == 0x61U && hash[ 2] == 0xA4U && hash[ 3] == 0x8EU &&
|
|
hash[ 4] == 0x6DU && hash[ 5] == 0x20U && hash[ 6] == 0xBAU && hash[ 7] == 0x20U &&
|
|
hash[ 8] == 0x72U && hash[ 9] == 0x72U && hash[10] == 0x8FU && hash[11] == 0x4FU &&
|
|
hash[12] == 0x3FU && hash[13] == 0xE1U && hash[14] == 0xE1U && hash[15] == 0xE7U &&
|
|
hash[16] == 0xEBU && hash[17] == 0x15U && hash[18] == 0xA8U && hash[19] == 0x4CU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x34U, 0x59U, 0x78U, 0x47U, 0x46U, 0x71U, 0x51U,
|
|
0x64U, 0x47U, 0x70U, 0x71U, 0x6EU, 0x4CU, 0x59U, 0x65U,
|
|
0x4DU, 0x38U, 0x56U, 0x52U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0x42U && hash[ 1] == 0xC5U && hash[ 2] == 0x2FU && hash[ 3] == 0x3BU &&
|
|
hash[ 4] == 0xB7U && hash[ 5] == 0xD4U && hash[ 6] == 0x54U && hash[ 7] == 0xB4U &&
|
|
hash[ 8] == 0x97U && hash[ 9] == 0xB4U && hash[10] == 0xFCU && hash[11] == 0xB0U &&
|
|
hash[12] == 0x46U && hash[13] == 0xBAU && hash[14] == 0xB6U && hash[15] == 0xADU &&
|
|
hash[16] == 0x93U && hash[17] == 0x8DU && hash[18] == 0xEBU && hash[19] == 0x7DU);
|
|
}
|
|
{
|
|
uint8_t raw[20] = {
|
|
0x72U, 0x33U, 0x76U, 0x71U, 0x75U, 0x79U, 0x72U, 0x45U,
|
|
0x39U, 0x55U, 0x53U, 0x70U, 0x68U, 0x62U, 0x43U, 0x55U,
|
|
0x6DU, 0x65U, 0x4BU, 0x55U
|
|
};
|
|
uint8_t hash[32];
|
|
ASSERT(32 ==
|
|
util_sha512h((uint32_t)hash, sizeof(hash), raw, 20));
|
|
ASSERT(
|
|
hash[ 0] == 0xD5U && hash[ 1] == 0x6BU && hash[ 2] == 0x6BU && hash[ 3] == 0x45U &&
|
|
hash[ 4] == 0x30U && hash[ 5] == 0xF0U && hash[ 6] == 0x34U && hash[ 7] == 0x76U &&
|
|
hash[ 8] == 0x31U && hash[ 9] == 0x56U && hash[10] == 0x8CU && hash[11] == 0x38U &&
|
|
hash[12] == 0x0CU && hash[13] == 0x1AU && hash[14] == 0xAFU && hash[15] == 0xABU &&
|
|
hash[16] == 0x42U && hash[17] == 0x16U && hash[18] == 0x21U && hash[19] == 0x42U);
|
|
}
|
|
|
|
// Test out of bounds check
|
|
ASSERT(util_sha512h(1000000, 50, 0, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(util_sha512h(0, 50, 10000000, 20) == OUT_OF_BOUNDS);
|
|
uint8_t raw[20] = {
|
|
0x8EU, 0xADU, 0xB4U, 0xBBU, 0x71U, 0x2AU, 0x29U, 0x1BU,
|
|
0x53U, 0x43U, 0xE0U, 0x03U, 0x1FU, 0x97U, 0x6BU, 0x0DU,
|
|
0xA9U, 0xEDU, 0x39U, 0xC2U
|
|
};
|
|
ASSERT(util_sha512h(0, 30, raw, 20) == TOO_SMALL);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set util_sha512h"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test util_sha512h"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
test_util_verify()
|
|
{
|
|
testcase("Test util_verify");
|
|
using namespace jtx;
|
|
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);
|
|
|
|
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 util_verify (uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
|
#define TOO_SMALL -4
|
|
#define OUT_OF_BOUNDS -1
|
|
#define INVALID_KEY -41
|
|
#define SBUF(x) ((uint32_t)(x)), sizeof(x)
|
|
#define ASSERT(x)\
|
|
if (!(x))\
|
|
rollback((uint32_t)#x, sizeof(#x), __LINE__);
|
|
|
|
// secp256k1
|
|
uint8_t pubkey_sec[] =
|
|
{
|
|
0x02U,0xC7U,0x38U,0x7FU,0xFCU,0x25U,0xC1U,0x56U,0xCAU,0x7FU,
|
|
0x8AU,0x6DU,0x76U,0x0CU,0x8DU,0x01U,0xEFU,0x64U,0x2CU,0xEEU,
|
|
0x9CU,0xE4U,0x68U,0x0CU,0x33U,0xFFU,0xB3U,0xFFU,0x39U,0xAFU,
|
|
0xECU,0xFEU,0x70U
|
|
};
|
|
|
|
uint8_t sig_sec[] =
|
|
{
|
|
0x30U,0x45U,0x02U,0x21U,0x00U,0x95U,0x6EU,0x7DU,0x1FU,0x01U,
|
|
0x16U,0xF1U,0x65U,0x00U,0xD2U,0xCCU,0xD8U,0x8DU,0x2AU,0x2FU,
|
|
0xEFU,0xF6U,0x52U,0x16U,0x85U,0x42U,0xF4U,0x4EU,0x43U,0xDBU,
|
|
0xE6U,0xF4U,0x53U,0xE8U,0x03U,0xB8U,0x4FU,0x02U,0x20U,0x0AU,
|
|
0xB6U,0xC3U,0x4BU,0x5FU,0x0CU,0xC6U,0x6BU,0x4FU,0x1FU,0x83U,
|
|
0xE9U,0x89U,0x74U,0xB8U,0x80U,0xA2U,0x2FU,0xAEU,0x52U,0x91U,
|
|
0x6BU,0xA2U,0xCEU,0x96U,0xA3U,0x61U,0x05U,0x3FU,0xFFU,0x81U,
|
|
0xE9U
|
|
};
|
|
|
|
// ed25519
|
|
uint8_t pubkey_ed[] =
|
|
{
|
|
0xEDU,0xD9U,0xB3U,0x59U,0x98U,0x02U,0xB2U,0x14U,0xA9U,0x9DU,
|
|
0x75U,0x77U,0x12U,0xD6U,0xABU,0xDFU,0x72U,0xF8U,0x3CU,0x63U,
|
|
0xBBU,0xD5U,0x38U,0x61U,0x41U,0x17U,0x90U,0xB1U,0x3DU,0x04U,
|
|
0xB2U,0xC5U,0xC9U
|
|
};
|
|
|
|
uint8_t sig_ed[] =
|
|
{
|
|
0x56U,0x68U,0x80U,0x76U,0x70U,0xFEU,0xCEU,0x60U,0x34U,0xAFU,
|
|
0xD6U,0xCDU,0x1BU,0xB4U,0xC6U,0x60U,0xAEU,0x08U,0x39U,0x6DU,
|
|
0x6DU,0x8BU,0x7DU,0x22U,0x71U,0x3BU,0xDAU,0x26U,0x43U,0xC1U,
|
|
0xE1U,0x91U,0xC4U,0xE4U,0x4DU,0x8EU,0x02U,0xE8U,0x57U,0x8BU,
|
|
0x20U,0x45U,0xDAU,0xD4U,0x8FU,0x97U,0xFCU,0x16U,0xF8U,0x92U,
|
|
0x5BU,0x6BU,0x51U,0xFBU,0x3BU,0xE5U,0x0FU,0xB0U,0x4BU,0x3AU,
|
|
0x20U,0x4CU,0x53U,0x04U
|
|
};
|
|
|
|
|
|
uint8_t msg[] =
|
|
{
|
|
0xDEU,0xADU,0xBEU,0xEFU
|
|
};
|
|
|
|
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
|
|
// Test out of bounds check
|
|
ASSERT(util_verify(1000000, 33, 0, 20, 0, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(util_verify(0, 33, 10000000, 20, 0, 20) == OUT_OF_BOUNDS);
|
|
ASSERT(util_verify(0, 33, 0, 20, 10000000, 20) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(util_verify(0, 1000000, 33, 1, 20, 30) == OUT_OF_BOUNDS);
|
|
ASSERT(util_verify(0, 33, 0, 10000000, 20, 30) == OUT_OF_BOUNDS);
|
|
ASSERT(util_verify(0, 33, 0, 2, 20, 10000000) == OUT_OF_BOUNDS);
|
|
|
|
ASSERT(util_verify(0, 30, 0, 1, 0, 30) == INVALID_KEY);
|
|
ASSERT(util_verify(0, 33, 0, 0, SBUF(pubkey_sec)) == TOO_SMALL);
|
|
ASSERT(util_verify(0, 0, 0, 100, SBUF(pubkey_sec)) == TOO_SMALL);
|
|
|
|
// test secp256k1 verification
|
|
ASSERT(util_verify(SBUF(msg), SBUF(sig_sec), SBUF(pubkey_sec)) == 1);
|
|
ASSERT(util_verify(msg + 1, sizeof(msg) - 1, SBUF(sig_sec), SBUF(pubkey_sec)) == 0);
|
|
|
|
// test ed25519 verification
|
|
ASSERT(util_verify(SBUF(msg), SBUF(sig_ed), SBUF(pubkey_ed)) == 1);
|
|
ASSERT(util_verify(msg + 1, sizeof(msg) - 1, SBUF(sig_ed), SBUF(pubkey_ed)) == 0);
|
|
|
|
accept(0,0,0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
// install the hook on alice
|
|
env(ripple::test::jtx::hook(alice, {{hso(hook, overrideFlag)}}, 0),
|
|
M("set util_verify"),
|
|
HSFEE);
|
|
env.close();
|
|
|
|
// invoke the hook
|
|
env(pay(bob, alice, XRP(1)), M("test util_verify"), fee(XRP(1)));
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
testHooksOwnerDir();
|
|
testHooksDisabled();
|
|
testTxStructure();
|
|
testInferHookSetOperation();
|
|
testParams();
|
|
testGrants();
|
|
|
|
testDelete();
|
|
testInstall();
|
|
testCreate();
|
|
testWithTickets();
|
|
|
|
testUpdate();
|
|
|
|
testNSDelete();
|
|
|
|
testWasm();
|
|
test_accept();
|
|
test_rollback();
|
|
|
|
testGuards();
|
|
|
|
test_emit(); //
|
|
// test_etxn_burden(); // tested above
|
|
// test_etxn_generation(); // tested above
|
|
// test_otxn_burden(); // tested above
|
|
// test_otxn_generation(); // tested above
|
|
test_etxn_details(); //
|
|
test_etxn_fee_base(); //
|
|
test_etxn_nonce(); //
|
|
test_etxn_reserve(); //
|
|
test_fee_base(); //
|
|
|
|
test_otxn_field(); //
|
|
|
|
test_ledger_keylet(); //
|
|
|
|
test_float_compare(); //
|
|
test_float_divide(); //
|
|
test_float_int(); //
|
|
test_float_invert(); //
|
|
test_float_log(); //
|
|
test_float_mantissa(); //
|
|
test_float_mulratio(); //
|
|
test_float_multiply(); //
|
|
test_float_negate(); //
|
|
test_float_one(); //
|
|
test_float_root(); //
|
|
test_float_set(); //
|
|
test_float_sign(); //
|
|
test_float_sto(); //
|
|
test_float_sto_set(); //
|
|
test_float_sum(); //
|
|
|
|
test_hook_account(); //
|
|
test_hook_again(); //
|
|
test_hook_hash(); //
|
|
test_hook_param(); //
|
|
test_hook_param_set(); //
|
|
test_hook_pos(); //
|
|
test_hook_skip(); //
|
|
|
|
test_ledger_last_hash(); //
|
|
test_ledger_last_time(); //
|
|
test_ledger_nonce(); //
|
|
test_ledger_seq(); //
|
|
|
|
test_meta_slot(); //
|
|
|
|
test_otxn_id(); //
|
|
test_otxn_slot(); //
|
|
test_otxn_type(); //
|
|
test_otxn_param(); //
|
|
|
|
test_slot(); //
|
|
test_slot_clear(); //
|
|
test_slot_count(); //
|
|
test_slot_float(); //
|
|
test_slot_set(); //
|
|
test_slot_size(); //
|
|
test_slot_subarray(); //
|
|
test_slot_subfield(); //
|
|
test_slot_type(); //
|
|
|
|
test_state(); //
|
|
test_state_foreign(); //
|
|
test_state_foreign_set(); //
|
|
test_state_set(); //
|
|
|
|
test_sto_emplace(); //
|
|
test_sto_erase(); //
|
|
test_sto_subarray(); //
|
|
test_sto_subfield(); //
|
|
test_sto_validate(); //
|
|
|
|
test_trace(); //
|
|
test_trace_float(); //
|
|
test_trace_num(); //
|
|
|
|
test_util_accid(); //
|
|
test_util_keylet(); //
|
|
test_util_raddr(); //
|
|
test_util_sha512h(); //
|
|
test_util_verify(); //
|
|
}
|
|
|
|
private:
|
|
TestHook accept_wasm = // WASM: 0
|
|
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);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(accept);
|
|
|
|
TestHook rollback_wasm = // WASM: 1
|
|
wasm[
|
|
R"[test.hook](
|
|
#include <stdint.h>
|
|
extern int32_t _g (uint32_t id, uint32_t maxiter);
|
|
extern int64_t rollback (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 rollback(SBUF("Hook Rejected"),0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(rollback);
|
|
|
|
TestHook noguard_wasm = // WASM: 2
|
|
wasm[
|
|
R"[test.hook](
|
|
(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 = // WASM: 3
|
|
wasm[
|
|
R"[test.hook](
|
|
(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 = // WASM: 4
|
|
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 )
|
|
{
|
|
_g(1,1);
|
|
char ret[] = M_REPEAT_1000("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz01234567890123");
|
|
return accept(SBUF(ret), 0);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
TestHook makestate_wasm = // WASM: 5
|
|
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);
|
|
extern int64_t state_set (uint32_t read_ptr, uint32_t read_len, uint32_t kread_ptr, uint32_t kread_len);
|
|
#define SBUF(x) x, sizeof(x)
|
|
int64_t hook(uint32_t reserved )
|
|
{
|
|
_g(1,1);
|
|
uint8_t test_key[] = "key";
|
|
uint8_t test_value[] = "value";
|
|
return accept(0,0, state_set(SBUF(test_value), SBUF(test_key)));
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(makestate);
|
|
|
|
// this is just used as a second small hook with a unique hash
|
|
TestHook accept2_wasm = // WASM: 6
|
|
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,2);
|
|
}
|
|
)[test.hook]"];
|
|
|
|
HASH_WASM(accept2);
|
|
};
|
|
BEAST_DEFINE_TESTSUITE(SetHook, app, ripple);
|
|
} // namespace test
|
|
} // namespace ripple
|
|
#undef M
|