mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 09:17:53 +00:00
refactor, guard_check, compiling but crash on validateGuards stream output (probably need a proxy class)
This commit is contained in:
@@ -432,8 +432,8 @@ target_sources (rippled PRIVATE
|
|||||||
src/ripple/app/tx/impl/Taker.cpp
|
src/ripple/app/tx/impl/Taker.cpp
|
||||||
src/ripple/app/tx/impl/Transactor.cpp
|
src/ripple/app/tx/impl/Transactor.cpp
|
||||||
src/ripple/app/tx/impl/apply.cpp
|
src/ripple/app/tx/impl/apply.cpp
|
||||||
src/ripple/app/tx/impl/applyHook.cpp
|
|
||||||
src/ripple/app/tx/impl/applySteps.cpp
|
src/ripple/app/tx/impl/applySteps.cpp
|
||||||
|
src/ripple/app/hook/impl/applyHook.cpp
|
||||||
#[===============================[
|
#[===============================[
|
||||||
main sources:
|
main sources:
|
||||||
subdir: basics (partial)
|
subdir: basics (partial)
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ if (process.argv.length < 3)
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//const server = 'ws://localhost:6005'
|
||||||
|
const server = 'ws://tn4:6005'
|
||||||
|
|
||||||
require('../utils-tests.js').TestRig('ws://localhost:6005').then(t=>
|
require('../utils-tests.js').TestRig(server).then(t=>
|
||||||
{
|
{
|
||||||
let wasm = t.findWasm();
|
let wasm = t.findWasm();
|
||||||
if (wasm.length != 1)
|
if (wasm.length != 1)
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ int out_len = 0;\
|
|||||||
out_len += i;\
|
out_len += i;\
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TRACEVAR(v) trace_num((uint32_t)(#v), (uint32_t)(sizeof(#v)), (int64_t)v);
|
#define TRACEVAR(v) trace_num((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (int64_t)v);
|
||||||
#define TRACEHEX(v) trace((uint32_t)(#v), (uint32_t)(sizeof(#v)), (uint32_t)(v), (uint32_t)(sizeof(v)), 1);
|
#define TRACEHEX(v) trace((uint32_t)(#v), (uint32_t)(sizeof(#v)), (uint32_t)(v), (uint32_t)(sizeof(v)), 1);
|
||||||
#define TRACEXFL(v) trace_float((uint32_t)(#v), (uint32_t)(sizeof(#v)), (int64_t)v);
|
#define TRACEXFL(v) trace_float((uint32_t)(#v), (uint32_t)(sizeof(#v)), (int64_t)v);
|
||||||
#define TRACESTR(v) trace((uint32_t)(#v), (uint32_t)(sizeof(#v)), (uint32_t)(v), sizeof(v), 0);
|
#define TRACESTR(v) trace((uint32_t)(#v), (uint32_t)(sizeof(#v)), (uint32_t)(v), sizeof(v), 0);
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ module.exports = {
|
|||||||
|
|
||||||
const wasm = (x) =>
|
const wasm = (x) =>
|
||||||
{
|
{
|
||||||
return fs.readFileSync('wasm/' + x).toString('hex').toUpperCase();
|
if (x.slice(0,1) != '/')
|
||||||
|
x = 'wasm/' + x;
|
||||||
|
return fs.readFileSync( x).toString('hex').toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
const genesisseed = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
const genesisseed = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
all: accept.wasm makestate.wasm checkstate.wasm
|
all: accept.wasm makestate.wasm makestate2.wasm checkstate.wasm
|
||||||
accept.wasm: accept.c
|
accept.wasm: accept.c
|
||||||
wasmcc accept.c -o accept.wasm -O0 -Wl,--allow-undefined -I../
|
wasmcc accept.c -o accept.wasm -O0 -Wl,--allow-undefined -I../
|
||||||
makestate.wasm: makestate.c
|
makestate.wasm: makestate.c
|
||||||
wasmcc makestate.c -o makestate.wasm -O0 -Wl,--allow-undefined -I../
|
wasmcc makestate.c -o makestate.wasm -O0 -Wl,--allow-undefined -I../
|
||||||
|
makestate2.wasm: makestate2.c
|
||||||
|
wasmcc makestate2.c -o makestate2.wasm -O0 -Wl,--allow-undefined -I../
|
||||||
checkstate.wasm: checkstate.c
|
checkstate.wasm: checkstate.c
|
||||||
wasmcc checkstate.c -o checkstate.wasm -O0 -Wl,--allow-undefined -I../
|
wasmcc checkstate.c -o checkstate.wasm -O0 -Wl,--allow-undefined -I../
|
||||||
|
|
||||||
|
|||||||
287
src/ripple/app/hook/Enum.h
Normal file
287
src/ripple/app/hook/Enum.h
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#ifndef HOOKENUM_INCLUDED
|
||||||
|
#define HOOKENUM_INCLUDED 1
|
||||||
|
namespace hook
|
||||||
|
{
|
||||||
|
enum TSHFlags : uint8_t
|
||||||
|
{
|
||||||
|
tshNONE = 0b000,
|
||||||
|
tshROLLBACK = 0b001,
|
||||||
|
tshCOLLECT = 0b010,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace log
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Hook log-codes are not necessarily errors. Each type of Hook log line contains a code in
|
||||||
|
* round parens like so:
|
||||||
|
* HookSet(5)[rAcc...]: message
|
||||||
|
* The log-code gives an external tool an easy way to handle and report the status of a hook
|
||||||
|
* to end users and developers.
|
||||||
|
*/
|
||||||
|
enum hook_log_code : uint16_t
|
||||||
|
{
|
||||||
|
/* HookSet log-codes */
|
||||||
|
AMENDMENT_DISABLED = 1, // attempt to HookSet when amendment is not yet enabled.
|
||||||
|
API_ILLEGAL = 2, // HookSet object contained HookApiVersion for existing HookDefinition
|
||||||
|
API_INVALID = 3, // HookSet object contained HookApiVersion for unrecognised hook API
|
||||||
|
API_MISSING = 4, // HookSet object did not contain HookApiVersion but should have
|
||||||
|
BLOCK_ILLEGAL = 5, // a block end instruction moves execution below depth 0 {{}}`}` <= like this
|
||||||
|
CALL_ILLEGAL = 6, // wasm tries to call a non-whitelisted function
|
||||||
|
CALL_INDIRECT = 7, // wasm used call indirect instruction which is disallowed
|
||||||
|
CREATE_FLAG = 8, // create operation requires hsfOVERRIDE flag
|
||||||
|
DELETE_FIELD = 9, //
|
||||||
|
DELETE_FLAG = 10, // delete operation requires hsfOVERRIDE flag
|
||||||
|
DELETE_NOTHING = 11, // delete operation would delete nothing
|
||||||
|
EXPORTS_MISSING = 12, // hook did not export *any* functions (should be cbak, hook)
|
||||||
|
EXPORT_CBAK_FUNC = 13, // hook did not export correct func def int64_t cbak(uint32_t)
|
||||||
|
EXPORT_HOOK_FUNC = 14, // hook did not export correct func def int64_t hook(uint32_t)
|
||||||
|
EXPORT_MISSING = 15, // distinct from export*S*_missing, either hook or cbak is missing
|
||||||
|
FLAGS_INVALID = 16, // HookSet flags were invalid for specified operation
|
||||||
|
FUNCS_MISSING = 17, // hook did not include function code for any functions
|
||||||
|
FUNC_PARAM_INVALID = 18, // parameter types may only be i32 i64 u32 u64
|
||||||
|
FUNC_RETURN_COUNT = 19, // a function type is defined in the wasm which returns > 1 return value
|
||||||
|
FUNC_RETURN_INVALID = 20, // a function type does not return i32 i64 u32 or u64
|
||||||
|
FUNC_TYPELESS = 21, // hook defined hook/cbak but their type is not defined in wasm
|
||||||
|
FUNC_TYPE_INVALID = 22, // malformed and illegal wasm in the func type section
|
||||||
|
GRANTS_EMPTY = 23, // HookSet object contained an empty grants array (you should remove it)
|
||||||
|
GRANTS_EXCESS = 24, // HookSet object cotnained a grants array with too many grants
|
||||||
|
GRANTS_FIELD = 25, // HookSet object contained a grant without Authorize or HookHash
|
||||||
|
GRANTS_ILLEGAL = 26, // Hookset object contained grants array which contained a non Grant object
|
||||||
|
GUARD_IMPORT = 27, // guard import is missing
|
||||||
|
GUARD_MISSING = 28, // guard call missing at top of loop
|
||||||
|
GUARD_PARAMETERS = 29, // guard called but did not use constant expressions for params
|
||||||
|
HASH_OR_CODE = 30, // HookSet object can contain only one of CreateCode and HookHash
|
||||||
|
HOOKON_MISSING = 31, // HookSet object did not contain HookOn but should have
|
||||||
|
HOOKS_ARRAY_BAD = 32, // attempt to HookSet with a Hooks array containing a non-Hook obj
|
||||||
|
HOOKS_ARRAY_BLANK = 33, // all hook set objs were blank
|
||||||
|
HOOKS_ARRAY_EMPTY = 34, // attempt to HookSet with an empty Hooks array
|
||||||
|
HOOKS_ARRAY_MISSING = 35, // attempt to HookSet without a Hooks array
|
||||||
|
HOOKS_ARRAY_TOO_BIG = 36, // attempt to HookSet with a Hooks array beyond the chain size limit
|
||||||
|
HOOK_ADD = 37, // Informational: adding ltHook to directory
|
||||||
|
HOOK_DEF_MISSING = 38, // attempt to reference a hook definition (by hash) that is not on ledger
|
||||||
|
HOOK_DELETE = 39, // unable to delete ltHook from owner
|
||||||
|
HOOK_INVALID_FIELD = 40, // HookSetObj contained an illegal/unexpected field
|
||||||
|
HOOK_PARAMS_COUNT = 41, // hookset obj would create too many hook parameters
|
||||||
|
HOOK_PARAM_SIZE = 42, // hookset obj sets a parameter or value that exceeds max allowable size
|
||||||
|
IMPORTS_MISSING = 43, // hook must import guard, and accept/rollback
|
||||||
|
IMPORT_ILLEGAL = 44, // attempted import of a non-whitelisted function
|
||||||
|
IMPORT_MODULE_BAD = 45, // hook attempted to specify no or a bad import module
|
||||||
|
IMPORT_MODULE_ENV = 46, // hook attempted to specify import module not named env
|
||||||
|
IMPORT_NAME_BAD = 47, // import name was too short or too long
|
||||||
|
INSTALL_FLAG = 48, // install operation requires hsoOVERRIDE
|
||||||
|
INSTALL_MISSING = 49, // install operation specifies hookhash which doesn't exist on the ledger
|
||||||
|
INSTRUCTION_COUNT = 50, // worst case execution instruction count as computed by HookSet
|
||||||
|
INSTRUCTION_EXCESS = 51, // worst case execution instruction count was too large
|
||||||
|
MEMORY_GROW = 52, // memory.grow instruction is present but disallowed
|
||||||
|
NAMESPACE_MISSING = 53, // HookSet object lacked HookNamespace
|
||||||
|
NSDELETE = 54, // Informational: a namespace is being deleted
|
||||||
|
NSDELETE_ACCOUNT = 55, // nsdelete tried to delete ns from a non-existing account
|
||||||
|
NSDELETE_COUNT = 56, // namespace state count less than 0 / overflow
|
||||||
|
NSDELETE_DIR = 57, // could not delete directory node in ledger
|
||||||
|
NSDELETE_DIRECTORY = 58, // nsdelete operation failed to delete ns directory
|
||||||
|
NSDELETE_DIR_ENTRY = 59, // nsdelete operation failed due to bad entry in ns directory
|
||||||
|
NSDELETE_ENTRY = 60, // nsdelete operation failed due to missing hook state entry
|
||||||
|
NSDELETE_FIELD = 61,
|
||||||
|
NSDELETE_FLAGS = 62,
|
||||||
|
NSDELETE_NONSTATE = 63, // nsdelete operation failed due to the presence of a non-hookstate obj
|
||||||
|
NSDELETE_NOTHING = 64, // hsfNSDELETE provided but nothing to delete
|
||||||
|
OPERATION_INVALID = 65, // could not deduce an operation from the provided hookset obj
|
||||||
|
OVERRIDE_MISSING = 66, // HookSet object was trying to update or delete a hook but lacked hsfOVERRIDE
|
||||||
|
PARAMETERS_FIELD = 67, // HookParameters contained a HookParameter with an invalid key in it
|
||||||
|
PARAMETERS_ILLEGAL = 68, // HookParameters contained something other than a HookParameter
|
||||||
|
PARAMETERS_NAME = 69, // HookParameters contained a HookParameter which lacked ParameterName field
|
||||||
|
PARAM_HOOK_CBAK = 70, // hook and cbak must take exactly one u32 parameter
|
||||||
|
RETURN_HOOK_CBAK = 71, // hook and cbak must retunr i64
|
||||||
|
SHORT_HOOK = 72, // web assembly byte code ended abruptly
|
||||||
|
TYPE_INVALID = 73, // malformed and illegal wasm specifying an illegal local var type
|
||||||
|
WASM_BAD_MAGIC = 74, // wasm magic number missing or not wasm
|
||||||
|
WASM_INVALID = 75, // set hook operation would set invalid wasm
|
||||||
|
WASM_PARSE_LOOP = 76, // wasm section parsing resulted in an infinite loop
|
||||||
|
WASM_SMOKE_TEST = 77, // Informational: first attempt to load wasm into wasm runtime
|
||||||
|
WASM_TEST_FAILURE = 78, // the smoke test failed
|
||||||
|
WASM_TOO_BIG = 79, // set hook would exceed maximum hook size
|
||||||
|
WASM_TOO_SMALL = 80,
|
||||||
|
WASM_VALIDATION = 81, // a generic error while parsing wasm, usually leb128 overflow
|
||||||
|
// RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines should get a code?
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace hook_api
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace keylet_code
|
||||||
|
{
|
||||||
|
enum keylet_code : uint32_t
|
||||||
|
{
|
||||||
|
HOOK = 1,
|
||||||
|
HOOK_STATE = 2,
|
||||||
|
ACCOUNT = 3,
|
||||||
|
AMENDMENTS = 4,
|
||||||
|
CHILD = 5,
|
||||||
|
SKIP = 6,
|
||||||
|
FEES = 7,
|
||||||
|
NEGATIVE_UNL = 8,
|
||||||
|
LINE = 9,
|
||||||
|
OFFER = 10,
|
||||||
|
QUALITY = 11,
|
||||||
|
EMITTED_DIR = 12,
|
||||||
|
TICKET = 13,
|
||||||
|
SIGNERS = 14,
|
||||||
|
CHECK = 15,
|
||||||
|
DEPOSIT_PREAUTH = 16,
|
||||||
|
UNCHECKED = 17,
|
||||||
|
OWNER_DIR = 18,
|
||||||
|
PAGE = 19,
|
||||||
|
ESCROW = 20,
|
||||||
|
PAYCHAN = 21,
|
||||||
|
EMITTED = 22
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace compare_mode {
|
||||||
|
enum compare_mode : uint32_t
|
||||||
|
{
|
||||||
|
EQUAL = 1,
|
||||||
|
LESS = 2,
|
||||||
|
GREATER = 4
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum hook_return_code : int64_t
|
||||||
|
{
|
||||||
|
SUCCESS = 0, // return codes > 0 are reserved for hook apis to return "success"
|
||||||
|
OUT_OF_BOUNDS = -1, // could not read or write to a pointer to provided by hook
|
||||||
|
INTERNAL_ERROR = -2, // eg directory is corrupt
|
||||||
|
TOO_BIG = -3, // something you tried to store was too big
|
||||||
|
TOO_SMALL = -4, // something you tried to store or provide was too small
|
||||||
|
DOESNT_EXIST = -5, // something you requested wasn't found
|
||||||
|
NO_FREE_SLOTS = -6, // when trying to load an object there is a maximum of 255 slots
|
||||||
|
INVALID_ARGUMENT = -7, // self explanatory
|
||||||
|
ALREADY_SET = -8, // returned when a one-time parameter was already set by the hook
|
||||||
|
PREREQUISITE_NOT_MET = -9, // returned if a required param wasn't set, before calling
|
||||||
|
FEE_TOO_LARGE = -10, // returned if the attempted operation would result in an absurd fee
|
||||||
|
EMISSION_FAILURE = -11, // returned if an emitted tx was not accepted by rippled
|
||||||
|
TOO_MANY_NONCES = -12, // a hook has a maximum of 256 nonces
|
||||||
|
TOO_MANY_EMITTED_TXN = -13, // a hook has emitted more than its stated number of emitted txn
|
||||||
|
NOT_IMPLEMENTED = -14, // an api was called that is reserved for a future version
|
||||||
|
INVALID_ACCOUNT = -15, // an api expected an account id but got something else
|
||||||
|
GUARD_VIOLATION = -16, // a guarded loop or function iterated over its maximum
|
||||||
|
INVALID_FIELD = -17, // the field requested is returning sfInvalid
|
||||||
|
PARSE_ERROR = -18, // hook asked hookapi to parse something the contents of which was invalid
|
||||||
|
RC_ROLLBACK = -19, // hook should terminate due to a rollback() call
|
||||||
|
RC_ACCEPT = -20, // hook should temrinate due to an accept() call
|
||||||
|
NO_SUCH_KEYLET = -21, // invalid keylet or keylet type
|
||||||
|
NOT_AN_ARRAY = -22, // if a count of an sle is requested but its not STI_ARRAY
|
||||||
|
NOT_AN_OBJECT = -23, // if a subfield is requested from something that isn't an object
|
||||||
|
INVALID_FLOAT = -10024, // specially selected value that will never be a valid exponent
|
||||||
|
DIVISION_BY_ZERO = -25,
|
||||||
|
MANTISSA_OVERSIZED = -26,
|
||||||
|
MANTISSA_UNDERSIZED = -27,
|
||||||
|
EXPONENT_OVERSIZED = -28,
|
||||||
|
EXPONENT_UNDERSIZED = -29,
|
||||||
|
OVERFLOW = -30, // if an operation with a float results in an overflow
|
||||||
|
NOT_IOU_AMOUNT = -31,
|
||||||
|
NOT_AN_AMOUNT = -32,
|
||||||
|
CANT_RETURN_NEGATIVE = -33,
|
||||||
|
NOT_AUTHORIZED = -34,
|
||||||
|
PREVIOUS_FAILURE_PREVENTS_RETRY = -35,
|
||||||
|
TOO_MANY_PARAMS = -36
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ExitType : uint8_t
|
||||||
|
{
|
||||||
|
UNSET = 0,
|
||||||
|
WASM_ERROR = 1,
|
||||||
|
ROLLBACK = 2,
|
||||||
|
ACCEPT = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const int etxn_details_size = 105;
|
||||||
|
const int max_slots = 255;
|
||||||
|
const int max_nonce = 255;
|
||||||
|
const int max_emit = 255;
|
||||||
|
const int max_params = 16;
|
||||||
|
const int drops_per_byte = 31250; //RH TODO make these votable config option
|
||||||
|
const double fee_base_multiplier = 1.1f;
|
||||||
|
|
||||||
|
|
||||||
|
// RH NOTE: Find descriptions of api functions in ./impl/applyHook.cpp and hookapi.h (include for hooks)
|
||||||
|
static const std::set<std::string> import_whitelist
|
||||||
|
{
|
||||||
|
"accept",
|
||||||
|
"emit",
|
||||||
|
"etxn_burden",
|
||||||
|
"etxn_details",
|
||||||
|
"etxn_fee_base",
|
||||||
|
"etxn_generation",
|
||||||
|
"etxn_reserve",
|
||||||
|
"float_compare",
|
||||||
|
"float_divide",
|
||||||
|
"float_exponent",
|
||||||
|
"float_exponent_set",
|
||||||
|
"float_invert",
|
||||||
|
"float_mantissa",
|
||||||
|
"float_mantissa_set",
|
||||||
|
"float_mulratio",
|
||||||
|
"float_multiply",
|
||||||
|
"float_int",
|
||||||
|
"float_negate",
|
||||||
|
"float_one",
|
||||||
|
"float_set",
|
||||||
|
"float_sign",
|
||||||
|
"float_sign_set",
|
||||||
|
"float_sto",
|
||||||
|
"float_sto_set",
|
||||||
|
"float_sum",
|
||||||
|
"fee_base",
|
||||||
|
"_g",
|
||||||
|
"hook_account",
|
||||||
|
"hook_hash",
|
||||||
|
"ledger_seq",
|
||||||
|
"ledger_last_hash",
|
||||||
|
"nonce",
|
||||||
|
"otxn_burden",
|
||||||
|
"otxn_field",
|
||||||
|
"otxn_slot",
|
||||||
|
"otxn_generation",
|
||||||
|
"otxn_id",
|
||||||
|
"otxn_type",
|
||||||
|
"rollback",
|
||||||
|
"slot",
|
||||||
|
"slot_clear",
|
||||||
|
"slot_count",
|
||||||
|
"slot_id",
|
||||||
|
"slot_set",
|
||||||
|
"slot_size",
|
||||||
|
"slot_subarray",
|
||||||
|
"slot_subfield",
|
||||||
|
"slot_type",
|
||||||
|
"slot_float",
|
||||||
|
"state",
|
||||||
|
"state_foreign",
|
||||||
|
"state_set",
|
||||||
|
"state_foreign_set",
|
||||||
|
"trace",
|
||||||
|
"trace_num",
|
||||||
|
"trace_float",
|
||||||
|
"trace_slot",
|
||||||
|
"util_accid",
|
||||||
|
"util_raddr",
|
||||||
|
"util_sha512h",
|
||||||
|
"util_verify",
|
||||||
|
"sto_subarray",
|
||||||
|
"sto_subfield",
|
||||||
|
"sto_validate",
|
||||||
|
"sto_emplace",
|
||||||
|
"sto_erase",
|
||||||
|
"util_keylet",
|
||||||
|
"hook_pos",
|
||||||
|
"hook_param",
|
||||||
|
"hook_param_set",
|
||||||
|
"hook_skip"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
#endif
|
||||||
972
src/ripple/app/hook/Guard.h
Normal file
972
src/ripple/app/hook/Guard.h
Normal file
@@ -0,0 +1,972 @@
|
|||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stack>
|
||||||
|
#include "Enum.h"
|
||||||
|
|
||||||
|
// Rather than pass around heavy capturing lambdas for something as simple as logging, use this
|
||||||
|
// somewhat convoluted convention:
|
||||||
|
// std::basic_ostream<char> myLogFunc(uint8_t code, std::string_view accountID, void* otherData)
|
||||||
|
// {
|
||||||
|
// return std::cout GUARDLOG(code << accountID << "]: ";
|
||||||
|
// }
|
||||||
|
// This is designed to allow use outside of rippled, simply pass "" for accountID
|
||||||
|
using GuardLogFuncPtr =
|
||||||
|
std::basic_ostream<char>&(*) // 0
|
||||||
|
(
|
||||||
|
uint16_t, // roundBraceCode may be 0
|
||||||
|
std::string_view, // square brace text may be ""
|
||||||
|
void* // arbitrary data
|
||||||
|
);
|
||||||
|
|
||||||
|
using GuardLog =
|
||||||
|
std::optional<
|
||||||
|
std::tuple<
|
||||||
|
GuardLogFuncPtr,
|
||||||
|
std::string_view,
|
||||||
|
void*
|
||||||
|
>>;
|
||||||
|
|
||||||
|
#define DEBUG_GUARD 0
|
||||||
|
#define GUARDLOG(logCode)\
|
||||||
|
if (!guardLog)\
|
||||||
|
{\
|
||||||
|
}\
|
||||||
|
else\
|
||||||
|
(std::get<0>(*guardLog))(logCode, std::get<1>(*guardLog), std::get<2>(*guardLog))
|
||||||
|
|
||||||
|
// RH TODO test overflow on leb128 detection
|
||||||
|
// web assembly contains a lot of run length encoding in LEB128 format
|
||||||
|
inline uint64_t
|
||||||
|
parseLeb128(
|
||||||
|
std::vector<unsigned char> const& buf,
|
||||||
|
int start_offset,
|
||||||
|
int* end_offset)
|
||||||
|
{
|
||||||
|
uint64_t val = 0, shift = 0, i = start_offset;
|
||||||
|
while (i < buf.size())
|
||||||
|
{
|
||||||
|
int b = (int)(buf[i]);
|
||||||
|
uint64_t last = val;
|
||||||
|
val += (b & 0x7F) << shift;
|
||||||
|
if (val < last)
|
||||||
|
{
|
||||||
|
// overflow
|
||||||
|
throw std::overflow_error { "leb128 overflow" };
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
if (b & 0x80)
|
||||||
|
{
|
||||||
|
shift += 7;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*end_offset = i;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// this macro will return temMALFORMED if i ever exceeds the end of the hook
|
||||||
|
#define CHECK_SHORT_HOOK()\
|
||||||
|
{\
|
||||||
|
if (i >= hook.size())\
|
||||||
|
{\
|
||||||
|
\
|
||||||
|
GUARDLOG(hook::log::SHORT_HOOK) \
|
||||||
|
<< "Malformed transaction: Hook truncated or otherwise invalid. "\
|
||||||
|
<< "SetHook.cpp:" << __LINE__;\
|
||||||
|
return {};\
|
||||||
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks the WASM binary for the appropriate required _g guard calls and rejects it if they are not found
|
||||||
|
// start_offset is where the codesection or expr under analysis begins and end_offset is where it ends
|
||||||
|
// returns {worst case instruction count} if valid or {} if invalid
|
||||||
|
// may throw overflow_error
|
||||||
|
inline
|
||||||
|
std::optional<uint64_t>
|
||||||
|
check_guard(
|
||||||
|
std::vector<uint8_t> const& hook,
|
||||||
|
int codesec,
|
||||||
|
int start_offset,
|
||||||
|
int end_offset,
|
||||||
|
int guard_func_idx,
|
||||||
|
int last_import_idx,
|
||||||
|
GuardLog guardLog)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("\ncheck_guard called with "
|
||||||
|
"codesec=%d start_offset=%d end_offset=%d guard_func_idx=%d last_import_idx=%d\n",
|
||||||
|
codesec, start_offset, end_offset, guard_func_idx, last_import_idx);
|
||||||
|
|
||||||
|
if (end_offset <= 0) end_offset = hook.size();
|
||||||
|
int block_depth = 0;
|
||||||
|
int mode = 1; // controls the state machine for searching for guards
|
||||||
|
// 0 = looking for guard from a trigger point (loop or function start)
|
||||||
|
// 1 = looking for a new trigger point (loop);
|
||||||
|
// currently always starts at 1 no-top-of-func check, see above block comment
|
||||||
|
|
||||||
|
std::stack<uint64_t> stack; // we track the stack in mode 0 to work out if constants end up in the guard function
|
||||||
|
std::map<uint32_t, uint64_t> local_map; // map of local variables since the trigger point
|
||||||
|
std::map<uint32_t, uint64_t> global_map; // map of global variables since the trigger point
|
||||||
|
|
||||||
|
// block depth level -> { largest guard, rolling instruction count }
|
||||||
|
std::map<int, std::pair<uint32_t, uint64_t>> instruction_count;
|
||||||
|
|
||||||
|
// largest guard // instr ccount
|
||||||
|
instruction_count[0] = {1, 0};
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("\n\n\nstart of guard analysis for codesec %d\n", codesec);
|
||||||
|
|
||||||
|
for (int i = start_offset; i < end_offset; )
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
{
|
||||||
|
printf("->");
|
||||||
|
for (int z = i; z < 16 + i && z < end_offset; ++z)
|
||||||
|
printf("%02X", hook[z]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_SHORT_HOOK();
|
||||||
|
int instr = hook[i++];
|
||||||
|
|
||||||
|
instruction_count[block_depth].second++;
|
||||||
|
|
||||||
|
if (instr == 0x10) // call instr
|
||||||
|
{
|
||||||
|
int callee_idx = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - call instruction at %d -- call funcid: %d\n", mode, i, callee_idx);
|
||||||
|
|
||||||
|
// disallow calling of user defined functions inside a hook
|
||||||
|
if (callee_idx > last_import_idx)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::CALL_ILLEGAL)
|
||||||
|
<< "GuardCheck "
|
||||||
|
<< "Hook calls a function outside of the whitelisted imports "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i;
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callee_idx == guard_func_idx)
|
||||||
|
{
|
||||||
|
// found!
|
||||||
|
if (mode == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (stack.size() < 2)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::GUARD_PARAMETERS)
|
||||||
|
<< "GuardCheck "
|
||||||
|
<< "_g() called but could not detect constant parameters "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i << "\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t a = stack.top();
|
||||||
|
stack.pop();
|
||||||
|
uint64_t b = stack.top();
|
||||||
|
stack.pop();
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("FOUND: GUARD(%lu, %lu), codesec: %d offset %d\n", a, b, codesec, i);
|
||||||
|
|
||||||
|
if (b <= 0)
|
||||||
|
{
|
||||||
|
// 0 has a special meaning, generally it's not a constant value
|
||||||
|
// < 0 is a constant but negative, either way this is a reject condition
|
||||||
|
GUARDLOG(hook::log::GUARD_PARAMETERS)
|
||||||
|
<< "GuardCheck "
|
||||||
|
<< "_g() called but could not detect constant parameters "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i << "\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the instruction count for this block depth to the largest possible guard
|
||||||
|
if (instruction_count[block_depth].first < a)
|
||||||
|
{
|
||||||
|
instruction_count[block_depth].first = a;
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
{
|
||||||
|
std::cout
|
||||||
|
<< "HookDebug GuardCheck "
|
||||||
|
<< "Depth " << block_depth << " guard: " << a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear stack and maps
|
||||||
|
while (stack.size() > 0)
|
||||||
|
stack.pop();
|
||||||
|
local_map.clear();
|
||||||
|
global_map.clear();
|
||||||
|
mode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr == 0x11) // call indirect [ we don't allow guard to be called this way ]
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::CALL_INDIRECT) << "GuardCheck "
|
||||||
|
<< "Call indirect detected and is disallowed in hooks "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i;
|
||||||
|
return {};
|
||||||
|
/*
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - call_indirect instruction at %d\n", mode, i);
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
++i; CHECK_SHORT_HOOK(); //absorb 0x00 trailing
|
||||||
|
continue;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// unreachable and nop instructions
|
||||||
|
if (instr == 0x00 || instr == 0x01)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - unreachable/nop instruction at %d\n", mode, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// branch loop block instructions
|
||||||
|
if ((instr >= 0x02 && instr <= 0x0F) || instr == 0x11)
|
||||||
|
{
|
||||||
|
if (mode == 0 && instr >= 0x03)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::GUARD_MISSING)
|
||||||
|
<< "GuardCheck "
|
||||||
|
<< "_g() did not occur at start of loop statement "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// block instruction
|
||||||
|
// RH NOTE: block instructions *are* allowed between a loop and a guard
|
||||||
|
if (instr == 0x02)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - block instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
++i; CHECK_SHORT_HOOK();
|
||||||
|
block_depth++;
|
||||||
|
instruction_count[block_depth] = {1, 0};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execution to here means we are in 'search mode' for loop instructions
|
||||||
|
|
||||||
|
// loop instruction
|
||||||
|
if (instr == 0x03)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - loop instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
++i; CHECK_SHORT_HOOK();
|
||||||
|
mode = 0; // we now search for a guard()
|
||||||
|
block_depth++;
|
||||||
|
instruction_count[block_depth] = {1, 0};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if instr
|
||||||
|
if (instr == 0x04)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - if instruction at %d\n", mode, i);
|
||||||
|
++i; CHECK_SHORT_HOOK();
|
||||||
|
block_depth++;
|
||||||
|
instruction_count[block_depth] = {1, 0};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else instr
|
||||||
|
if (instr == 0x05)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - else instruction at %d\n", mode, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// branch instruction
|
||||||
|
if (instr == 0x0C || instr == 0x0D)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - br instruction at %d\n", mode, i);
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// branch table instr
|
||||||
|
if (instr == 0x0E)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - br_table instruction at %d\n", mode, i);
|
||||||
|
int vec_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
for (int v = 0; v < vec_count; ++v)
|
||||||
|
{
|
||||||
|
parseLeb128(hook, i, &i);
|
||||||
|
CHECK_SHORT_HOOK();
|
||||||
|
}
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// parametric instructions | no operands
|
||||||
|
if (instr == 0x1A || instr == 0x1B)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - parametric instruction at %d\n", mode, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// variable instructions
|
||||||
|
if (instr >= 0x20 && instr <= 0x24)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - variable local/global instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
int idx = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
if (mode == 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// we need to do stack and map manipualtion to track any possible constants before guard call
|
||||||
|
if (instr == 0x20 || instr == 0x23) // local.get idx or global.get idx
|
||||||
|
{
|
||||||
|
auto& map = ( instr == 0x20 ? local_map : global_map );
|
||||||
|
if (map.find(idx) == map.end())
|
||||||
|
stack.push(0); // we always put a 0 in place of a local or global we don't know the value of
|
||||||
|
else
|
||||||
|
stack.push(map[idx]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr == 0x21 || instr == 0x22 || instr == 0x24) // local.set idx or global.set idx
|
||||||
|
{
|
||||||
|
auto& map = ( instr == 0x21 || instr == 0x22 ? local_map : global_map );
|
||||||
|
|
||||||
|
uint64_t to_store = (stack.size() == 0 ? 0 : stack.top());
|
||||||
|
map[idx] = to_store;
|
||||||
|
if (instr != 0x22)
|
||||||
|
stack.pop();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RH TODO support guard consts being passed through memory functions (maybe)
|
||||||
|
|
||||||
|
//memory instructions
|
||||||
|
if (instr >= 0x28 && instr <= 0x3E)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - variable memory instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// more memory instructions
|
||||||
|
if (instr == 0x3F || instr == 0x40)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - memory instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
++i; CHECK_SHORT_HOOK();
|
||||||
|
if (instr == 0x40) // disallow memory.grow
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::MEMORY_GROW)
|
||||||
|
<< "GuardCheck "
|
||||||
|
<< "Memory.grow instruction not allowed at "
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i << "\n";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// int const instrs
|
||||||
|
// numeric instructions with immediates
|
||||||
|
if (instr == 0x41 || instr == 0x42)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - const instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
uint64_t immediate = parseLeb128(hook, i, &i);
|
||||||
|
CHECK_SHORT_HOOK(); // RH TODO enforce i32 i64 size limit
|
||||||
|
|
||||||
|
// in mode 0 we should be stacking our constants and tracking their movement in
|
||||||
|
// and out of locals and globals
|
||||||
|
stack.push(immediate);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const instr
|
||||||
|
// more numerics with immediates
|
||||||
|
if (instr == 0x43 || instr == 0x44)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - const float instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
i += ( instr == 0x43 ? 4 : 8 );
|
||||||
|
CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// numerics no immediates
|
||||||
|
if (instr >= 0x45 && instr <= 0xC4)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - numeric instruction at %d\n", mode, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncation instructions
|
||||||
|
if (instr == 0xFC)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - truncation instruction at %d\n", mode, i);
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instr == 0x0B)
|
||||||
|
{
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("%d - block end instruction at %d\n", mode, i);
|
||||||
|
|
||||||
|
// end of expression
|
||||||
|
if (block_depth == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
block_depth--;
|
||||||
|
|
||||||
|
if (block_depth < 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
GUARDLOG(hook::log::BLOCK_ILLEGAL) << "GuardCheck "
|
||||||
|
<< "Unexpected 0x0B instruction, malformed"
|
||||||
|
<< "codesec: " << codesec << " hook byte offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform the instruction count * guard accounting
|
||||||
|
instruction_count[block_depth].second +=
|
||||||
|
instruction_count[block_depth+1].second * instruction_count[block_depth+1].first;
|
||||||
|
instruction_count.erase(block_depth+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GUARDLOG(hook::log::INSTRUCTION_COUNT) << "GuardCheck "
|
||||||
|
<< "Total worse-case execution count: " << instruction_count[0].second;
|
||||||
|
|
||||||
|
// RH TODO: don't hardcode this
|
||||||
|
if (instruction_count[0].second > 0xFFFFF)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::INSTRUCTION_EXCESS) << "GuardCheck "
|
||||||
|
<< "Maximum possible instructions exceed 1048575, please make your hook smaller "
|
||||||
|
<< "or check your guards!";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we reach the end of the code looking for another trigger the guards are installed correctly
|
||||||
|
if (mode == 1)
|
||||||
|
return instruction_count[0].second;
|
||||||
|
|
||||||
|
GUARDLOG(hook::log::GUARD_MISSING) << "GuardCheck "
|
||||||
|
<< "Guard did not occur before end of loop / function. "
|
||||||
|
<< "Codesec: " << codesec;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// may throw overflow_error
|
||||||
|
inline
|
||||||
|
std::optional< // unpopulated means invalid
|
||||||
|
std::pair<
|
||||||
|
uint64_t, // max instruction count for hook()
|
||||||
|
uint64_t // max instruction count for cbak()
|
||||||
|
>>
|
||||||
|
validateGuards(
|
||||||
|
std::vector<uint8_t> const& hook,
|
||||||
|
bool strict,
|
||||||
|
GuardLog guardLog)
|
||||||
|
{
|
||||||
|
uint64_t byteCount = hook.size();
|
||||||
|
|
||||||
|
// RH TODO compute actual smallest possible hook and update this value
|
||||||
|
if (byteCount < 10)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::WASM_TOO_SMALL)
|
||||||
|
<< "Malformed transaction: Hook was not valid webassembly binary. Too small.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// check header, magic number
|
||||||
|
unsigned char header[8] = { 0x00U, 0x61U, 0x73U, 0x6DU, 0x01U, 0x00U, 0x00U, 0x00U };
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
if (hook[i] != header[i])
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::WASM_BAD_MAGIC)
|
||||||
|
<< "Malformed transaction: Hook was not valid webassembly binary. "
|
||||||
|
<< "Missing magic number or version.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// these will store the function type indicies of hook and cbak if
|
||||||
|
// hook and cbak are found in the export section
|
||||||
|
std::optional<int> hook_func_idx;
|
||||||
|
std::optional<int> cbak_func_idx;
|
||||||
|
|
||||||
|
// this maps function ids to type ids, used for looking up the type of cbak and hook
|
||||||
|
// as established inside the wasm binary.
|
||||||
|
std::map<int, int> func_type_map;
|
||||||
|
|
||||||
|
|
||||||
|
// now we check for guards... first check if _g is imported
|
||||||
|
int guard_import_number = -1;
|
||||||
|
int last_import_number = -1;
|
||||||
|
int import_count = 0;
|
||||||
|
for (int i = 8, j = 0; i < hook.size();)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (j == i)
|
||||||
|
{
|
||||||
|
// if the loop iterates twice with the same value for i then
|
||||||
|
// it's an infinite loop edge case
|
||||||
|
GUARDLOG(hook::log::WASM_PARSE_LOOP)
|
||||||
|
<< "Malformed transaction: Hook is invalid WASM binary.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
j = i;
|
||||||
|
|
||||||
|
// each web assembly section begins with a single byte section type followed by an leb128 length
|
||||||
|
int section_type = hook[i++];
|
||||||
|
int section_length = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
//int section_start = i;
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("WASM binary analysis -- upto %d: section %d with length %d\n",
|
||||||
|
i, section_type, section_length);
|
||||||
|
|
||||||
|
int next_section = i + section_length;
|
||||||
|
|
||||||
|
// we are interested in the import section... we need to know if _g is imported and which import# it is
|
||||||
|
if (section_type == 2) // import section
|
||||||
|
{
|
||||||
|
import_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (import_count <= 0)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::IMPORTS_MISSING)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not import any functions... "
|
||||||
|
<< "required at least guard(uint32_t, uint32_t) and accept or rollback";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// process each import one by one
|
||||||
|
int func_upto = 0; // not all imports are functions so we need an indep counter for these
|
||||||
|
for (int j = 0; j < import_count; ++j)
|
||||||
|
{
|
||||||
|
// first check module name
|
||||||
|
int mod_length = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (mod_length < 1 || mod_length > (hook.size() - i))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::IMPORT_MODULE_BAD)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook attempted to specify nil or invalid import module";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::string_view( (const char*)(hook.data() + i), (size_t)mod_length ) != "env")
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::IMPORT_MODULE_ENV)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook attempted to specify import module other than 'env'";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
i += mod_length; CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
// next get import name
|
||||||
|
int name_length = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (name_length < 1 || name_length > (hook.size() - i))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::IMPORT_NAME_BAD)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook attempted to specify nil or invalid import name";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string import_name { (const char*)(hook.data() + i), (size_t)name_length };
|
||||||
|
|
||||||
|
i += name_length; CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
// next get import type
|
||||||
|
if (hook[i] > 0x00)
|
||||||
|
{
|
||||||
|
// not a function import
|
||||||
|
// RH TODO check these other imports for weird stuff
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execution to here means it's a function import
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
/*int type_idx = */
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
// RH TODO: validate that the parameters of the imported functions are correct
|
||||||
|
if (import_name == "_g")
|
||||||
|
{
|
||||||
|
guard_import_number = func_upto;
|
||||||
|
} else if (hook_api::import_whitelist.find(import_name) == hook_api::import_whitelist.end())
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::IMPORT_ILLEGAL)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook attempted to import a function that does not "
|
||||||
|
<< "appear in the hook_api function set: `" << import_name << "`";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
func_upto++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guard_import_number == -1)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::GUARD_IMPORT)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not import _g (guard) function";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
last_import_number = func_upto - 1;
|
||||||
|
|
||||||
|
// we have an imported guard function, so now we need to enforce the guard rule:
|
||||||
|
// all loops must start with a guard call before any branching
|
||||||
|
// to enforce these rules we must do a second pass of the wasm in case the function
|
||||||
|
// section was placed in this wasm binary before the import section
|
||||||
|
|
||||||
|
} else
|
||||||
|
if (section_type == 7) // export section
|
||||||
|
{
|
||||||
|
int export_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (export_count <= 0)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::EXPORTS_MISSING)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not export any functions... "
|
||||||
|
<< "required hook(int64_t), callback(int64_t).";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < export_count; ++j)
|
||||||
|
{
|
||||||
|
int name_len = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (name_len == 4)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (hook[i] == 'h' && hook[i+1] == 'o' && hook[i+2] == 'o' && hook[i+3] == 'k')
|
||||||
|
{
|
||||||
|
i += name_len; CHECK_SHORT_HOOK();
|
||||||
|
if (hook[i] != 0)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::EXPORT_HOOK_FUNC)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not export: A valid int64_t hook(uint32_t)";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
hook_func_idx = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hook[i] == 'c' && hook[i+1] == 'b' && hook[i+2] == 'a' && hook[i+3] == 'k')
|
||||||
|
{
|
||||||
|
i += name_len; CHECK_SHORT_HOOK();
|
||||||
|
if (hook[i] != 0)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::EXPORT_CBAK_FUNC)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not export: A valid int64_t cbak(uint32_t)";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
cbak_func_idx = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += name_len + 1;
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
// execution to here means export section was parsed
|
||||||
|
if (!hook_func_idx)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::EXPORT_MISSING)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not export: "
|
||||||
|
<< ( !hook_func_idx ? "int64_t hook(uint32_t); " : "" );
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (section_type == 3) // function section
|
||||||
|
{
|
||||||
|
int function_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (function_count <= 0)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNCS_MISSING)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook did not establish any functions... "
|
||||||
|
<< "required hook(int64_t), callback(int64_t).";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < function_count; ++j)
|
||||||
|
{
|
||||||
|
int type_idx = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
printf("Function map: func %d -> type %d\n", j, type_idx);
|
||||||
|
func_type_map[j] = type_idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = next_section;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we must subtract import_count from the hook and cbak function in order to be able to
|
||||||
|
// look them up in the functions section. this is a rule of the webassembly spec
|
||||||
|
// note that at this point in execution we are guarenteed these are populated
|
||||||
|
*hook_func_idx -= import_count;
|
||||||
|
|
||||||
|
if (cbak_func_idx)
|
||||||
|
*cbak_func_idx -= import_count;
|
||||||
|
|
||||||
|
if (func_type_map.find(*hook_func_idx) == func_type_map.end() ||
|
||||||
|
(cbak_func_idx && func_type_map.find(*cbak_func_idx) == func_type_map.end()))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNC_TYPELESS)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "hook or cbak functions did not have a corresponding type in WASM binary.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int hook_type_idx = func_type_map[*hook_func_idx];
|
||||||
|
|
||||||
|
// cbak function is optional so if it exists it has a type otherwise it is skipped in checks
|
||||||
|
std::optional<int> cbak_type_idx;
|
||||||
|
if (cbak_func_idx)
|
||||||
|
cbak_type_idx = func_type_map[*cbak_func_idx];
|
||||||
|
|
||||||
|
int64_t maxInstrCountHook = 0;
|
||||||
|
int64_t maxInstrCountCbak = 0;
|
||||||
|
|
||||||
|
// second pass... where we check all the guard function calls follow the guard rules
|
||||||
|
// minimal other validation in this pass because first pass caught most of it
|
||||||
|
for (int i = 8; i < hook.size();)
|
||||||
|
{
|
||||||
|
|
||||||
|
int section_type = hook[i++];
|
||||||
|
int section_length = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
//int section_start = i;
|
||||||
|
int next_section = i + section_length;
|
||||||
|
|
||||||
|
if (section_type == 1) // type section
|
||||||
|
{
|
||||||
|
int type_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
for (int j = 0; j < type_count; ++j)
|
||||||
|
{
|
||||||
|
if (hook[i++] != 0x60)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNC_TYPE_INVALID)
|
||||||
|
<< "Invalid function type. "
|
||||||
|
<< "Codesec: " << section_type << " "
|
||||||
|
<< "Local: " << j << " "
|
||||||
|
<< "Offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
int param_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
for (int k = 0; k < param_count; ++k)
|
||||||
|
{
|
||||||
|
int param_type = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (param_type == 0x7F || param_type == 0x7E ||
|
||||||
|
param_type == 0x7D || param_type == 0x7C)
|
||||||
|
{
|
||||||
|
// pass, this is fine
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNC_PARAM_INVALID)
|
||||||
|
<< "Invalid parameter type in function type. "
|
||||||
|
<< "Codesec: " << section_type << " "
|
||||||
|
<< "Local: " << j << " "
|
||||||
|
<< "Offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("Function type idx: %d, hook_func_idx: %d, cbak_func_idx: %d "
|
||||||
|
"param_count: %d param_type: %x\n",
|
||||||
|
j, *hook_func_idx, *cbak_func_idx, param_count, param_type);
|
||||||
|
|
||||||
|
// hook and cbak parameter check here
|
||||||
|
if ((j == hook_type_idx || (cbak_type_idx && j == cbak_type_idx)) &&
|
||||||
|
(param_count != 1 || param_type != 0x7F /* i32 */ ))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::PARAM_HOOK_CBAK)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "hook and cbak function definition must have exactly one uint32_t parameter.";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int result_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
// RH TODO: enable this for production
|
||||||
|
// this needs a reliable hook cleaner otherwise it will catch most compilers out
|
||||||
|
if (strict && result_count != 1)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNC_RETURN_COUNT)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< "Hook declares a function type that returns fewer or more than one value. ";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// this can only ever be 1 in production, but in testing it may also be 0 or >1
|
||||||
|
// so for completeness this loop is here but can be taken out in prod
|
||||||
|
for (int k = 0; k < result_count; ++k)
|
||||||
|
{
|
||||||
|
int result_type = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (result_type == 0x7F || result_type == 0x7E ||
|
||||||
|
result_type == 0x7D || result_type == 0x7C)
|
||||||
|
{
|
||||||
|
// pass, this is fine
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::FUNC_RETURN_INVALID)
|
||||||
|
<< "Invalid return type in function type. "
|
||||||
|
<< "Codesec: " << section_type << " "
|
||||||
|
<< "Local: " << j << " "
|
||||||
|
<< "Offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_GUARD)
|
||||||
|
printf("Function type idx: %d, hook_func_idx: %d, cbak_func_idx: %d "
|
||||||
|
"result_count: %d result_type: %x\n",
|
||||||
|
j, *hook_func_idx, *cbak_func_idx, result_count, result_type);
|
||||||
|
|
||||||
|
// hook and cbak return type check here
|
||||||
|
if ((j == hook_type_idx || (cbak_type_idx && j == cbak_type_idx)) &&
|
||||||
|
(result_count != 1 || result_type != 0x7E /* i64 */ ))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::RETURN_HOOK_CBAK)
|
||||||
|
<< "Malformed transaction. "
|
||||||
|
<< (j == hook_type_idx ? "hook" : "cbak") << " j=" << j << " "
|
||||||
|
<< " function definition must have exactly one int64_t return type. "
|
||||||
|
<< "resultcount=" << result_count << ", resulttype=" << result_type << ", "
|
||||||
|
<< "paramcount=" << param_count;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (section_type == 10) // code section
|
||||||
|
{
|
||||||
|
// RH TODO: parse anywhere else an expr is allowed in wasm and enforce rules there too
|
||||||
|
// these are the functions
|
||||||
|
int func_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
|
||||||
|
for (int j = 0; j < func_count; ++j)
|
||||||
|
{
|
||||||
|
// parse locals
|
||||||
|
int code_size = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
int code_end = i + code_size;
|
||||||
|
int local_count = parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
for (int k = 0; k < local_count; ++k)
|
||||||
|
{
|
||||||
|
/*int array_size = */
|
||||||
|
parseLeb128(hook, i, &i); CHECK_SHORT_HOOK();
|
||||||
|
if (!(hook[i] >= 0x7C && hook[i] <= 0x7F))
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::TYPE_INVALID)
|
||||||
|
<< "Invalid local type. "
|
||||||
|
<< "Codesec: " << j << " "
|
||||||
|
<< "Local: " << k << " "
|
||||||
|
<< "Offset: " << i;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
i++; CHECK_SHORT_HOOK();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == code_end)
|
||||||
|
continue; // allow empty functions
|
||||||
|
|
||||||
|
// execution to here means we are up to the actual expr for the codesec/function
|
||||||
|
|
||||||
|
auto valid =
|
||||||
|
check_guard(hook, j, i, code_end, guard_import_number, last_import_number, guardLog);
|
||||||
|
|
||||||
|
if (!valid)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (hook_func_idx && *hook_func_idx == j)
|
||||||
|
maxInstrCountHook = *valid;
|
||||||
|
else if (cbak_func_idx && *cbak_func_idx == j)
|
||||||
|
maxInstrCountCbak = *valid;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("code section: %d not hook_func_idx: %d or cbak_func_idx: %d\n",
|
||||||
|
j, *hook_func_idx, (cbak_func_idx ? *cbak_func_idx : -1));
|
||||||
|
// assert(false);
|
||||||
|
}
|
||||||
|
i = code_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = next_section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execution to here means guards are installed correctly
|
||||||
|
|
||||||
|
return std::pair<uint64_t, uint64_t>{maxInstrCountHook, maxInstrCountCbak};
|
||||||
|
|
||||||
|
/*
|
||||||
|
GUARDLOG(hook::log::WASM_SMOKE_TEST)
|
||||||
|
<< "Trying to wasm instantiate proposed hook "
|
||||||
|
<< "size = " << hook.size();
|
||||||
|
|
||||||
|
std::optional<std::string> result =
|
||||||
|
hook::HookExecutor::validateWasm(hook.data(), (size_t)hook.size());
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
GUARDLOG(hook::log::WASM_TEST_FAILURE)
|
||||||
|
<< "Tried to set a hook with invalid code. VM error: "
|
||||||
|
<< *result;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return std::pair<uint64_t, uint64_t>{maxInstrCountHook, maxInstrCountCbak};
|
||||||
|
}
|
||||||
@@ -12,25 +12,9 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <ripple/protocol/digest.h>
|
#include <ripple/protocol/digest.h>
|
||||||
#include <ripple/app/tx/applyHookMacro.h>
|
|
||||||
#include <wasmedge/wasmedge.h>
|
#include <wasmedge/wasmedge.h>
|
||||||
|
#include <ripple/app/hook/Macro.h>
|
||||||
enum HookSetFlags : uint8_t
|
#include <ripple/app/hook/Enum.h>
|
||||||
{
|
|
||||||
hsfOVERRIDE = (1U << 0U), // override or delete hook
|
|
||||||
hsfNSDELETE = (1U << 1U), // delete namespace
|
|
||||||
};
|
|
||||||
|
|
||||||
enum HookSetOperation : int8_t
|
|
||||||
{
|
|
||||||
hsoINVALID = -1,
|
|
||||||
hsoNOOP = 0,
|
|
||||||
hsoCREATE = 1,
|
|
||||||
hsoINSTALL = 2,
|
|
||||||
hsoDELETE = 3,
|
|
||||||
hsoNSDELETE = 4,
|
|
||||||
hsoUPDATE = 5
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace hook
|
namespace hook
|
||||||
{
|
{
|
||||||
@@ -51,13 +35,7 @@ namespace hook
|
|||||||
bool, // is modified from ledger value
|
bool, // is modified from ledger value
|
||||||
ripple::Blob>>>>; // the value
|
ripple::Blob>>>>; // the value
|
||||||
|
|
||||||
enum TSHFlags : uint8_t
|
|
||||||
{
|
|
||||||
tshNONE = 0b000,
|
|
||||||
tshROLLBACK = 0b001,
|
|
||||||
tshCOLLECT = 0b010,
|
|
||||||
};
|
|
||||||
|
|
||||||
using namespace ripple;
|
using namespace ripple;
|
||||||
static const std::map<uint16_t, uint8_t> TSHAllowances =
|
static const std::map<uint16_t, uint8_t> TSHAllowances =
|
||||||
{
|
{
|
||||||
@@ -87,104 +65,6 @@ namespace hook
|
|||||||
|
|
||||||
std::vector<std::pair<AccountID, bool>>
|
std::vector<std::pair<AccountID, bool>>
|
||||||
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv);
|
getTransactionalStakeHolders(STTx const& tx, ReadView const& rv);
|
||||||
|
|
||||||
namespace log
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Hook log-codes are not necessarily errors. Each type of Hook log line contains a code in
|
|
||||||
* round parens like so:
|
|
||||||
* HookSet(5)[rAcc...]: message
|
|
||||||
* The log-code gives an external tool an easy way to handle and report the status of a hook
|
|
||||||
* to end users and developers.
|
|
||||||
*/
|
|
||||||
enum hook_log_code : uint16_t
|
|
||||||
{
|
|
||||||
/* HookSet log-codes */
|
|
||||||
AMENDMENT_DISABLED = 1, // attempt to HookSet when amendment is not yet enabled.
|
|
||||||
API_ILLEGAL = 2, // HookSet object contained HookApiVersion for existing HookDefinition
|
|
||||||
API_INVALID = 3, // HookSet object contained HookApiVersion for unrecognised hook API
|
|
||||||
API_MISSING = 4, // HookSet object did not contain HookApiVersion but should have
|
|
||||||
BLOCK_ILLEGAL = 5, // a block end instruction moves execution below depth 0 {{}}`}` <= like this
|
|
||||||
CALL_ILLEGAL = 6, // wasm tries to call a non-whitelisted function
|
|
||||||
CALL_INDIRECT = 7, // wasm used call indirect instruction which is disallowed
|
|
||||||
CREATE_FLAG = 8, // create operation requires hsfOVERRIDE flag
|
|
||||||
DELETE_FIELD = 9, //
|
|
||||||
DELETE_FLAG = 10, // delete operation requires hsfOVERRIDE flag
|
|
||||||
DELETE_NOTHING = 11, // delete operation would delete nothing
|
|
||||||
EXPORTS_MISSING = 12, // hook did not export *any* functions (should be cbak, hook)
|
|
||||||
EXPORT_CBAK_FUNC = 13, // hook did not export correct func def int64_t cbak(uint32_t)
|
|
||||||
EXPORT_HOOK_FUNC = 14, // hook did not export correct func def int64_t hook(uint32_t)
|
|
||||||
EXPORT_MISSING = 15, // distinct from export*S*_missing, either hook or cbak is missing
|
|
||||||
FLAGS_INVALID = 16, // HookSet flags were invalid for specified operation
|
|
||||||
FUNCS_MISSING = 17, // hook did not include function code for any functions
|
|
||||||
FUNC_PARAM_INVALID = 18, // parameter types may only be i32 i64 u32 u64
|
|
||||||
FUNC_RETURN_COUNT = 19, // a function type is defined in the wasm which returns > 1 return value
|
|
||||||
FUNC_RETURN_INVALID = 20, // a function type does not return i32 i64 u32 or u64
|
|
||||||
FUNC_TYPELESS = 21, // hook defined hook/cbak but their type is not defined in wasm
|
|
||||||
FUNC_TYPE_INVALID = 22, // malformed and illegal wasm in the func type section
|
|
||||||
GRANTS_EMPTY = 23, // HookSet object contained an empty grants array (you should remove it)
|
|
||||||
GRANTS_EXCESS = 24, // HookSet object cotnained a grants array with too many grants
|
|
||||||
GRANTS_FIELD = 25, // HookSet object contained a grant without Authorize or HookHash
|
|
||||||
GRANTS_ILLEGAL = 26, // Hookset object contained grants array which contained a non Grant object
|
|
||||||
GUARD_IMPORT = 27, // guard import is missing
|
|
||||||
GUARD_MISSING = 28, // guard call missing at top of loop
|
|
||||||
GUARD_PARAMETERS = 29, // guard called but did not use constant expressions for params
|
|
||||||
HASH_OR_CODE = 30, // HookSet object can contain only one of CreateCode and HookHash
|
|
||||||
HOOKON_MISSING = 31, // HookSet object did not contain HookOn but should have
|
|
||||||
HOOKS_ARRAY_BAD = 32, // attempt to HookSet with a Hooks array containing a non-Hook obj
|
|
||||||
HOOKS_ARRAY_BLANK = 33, // all hook set objs were blank
|
|
||||||
HOOKS_ARRAY_EMPTY = 34, // attempt to HookSet with an empty Hooks array
|
|
||||||
HOOKS_ARRAY_MISSING = 35, // attempt to HookSet without a Hooks array
|
|
||||||
HOOKS_ARRAY_TOO_BIG = 36, // attempt to HookSet with a Hooks array beyond the chain size limit
|
|
||||||
HOOK_ADD = 37, // Informational: adding ltHook to directory
|
|
||||||
HOOK_DEF_MISSING = 38, // attempt to reference a hook definition (by hash) that is not on ledger
|
|
||||||
HOOK_DELETE = 39, // unable to delete ltHook from owner
|
|
||||||
HOOK_INVALID_FIELD = 40, // HookSetObj contained an illegal/unexpected field
|
|
||||||
HOOK_PARAMS_COUNT = 41, // hookset obj would create too many hook parameters
|
|
||||||
HOOK_PARAM_SIZE = 42, // hookset obj sets a parameter or value that exceeds max allowable size
|
|
||||||
IMPORTS_MISSING = 43, // hook must import guard, and accept/rollback
|
|
||||||
IMPORT_ILLEGAL = 44, // attempted import of a non-whitelisted function
|
|
||||||
IMPORT_MODULE_BAD = 45, // hook attempted to specify no or a bad import module
|
|
||||||
IMPORT_MODULE_ENV = 46, // hook attempted to specify import module not named env
|
|
||||||
IMPORT_NAME_BAD = 47, // import name was too short or too long
|
|
||||||
INSTALL_FLAG = 48, // install operation requires hsoOVERRIDE
|
|
||||||
INSTALL_MISSING = 49, // install operation specifies hookhash which doesn't exist on the ledger
|
|
||||||
INSTRUCTION_COUNT = 50, // worst case execution instruction count as computed by HookSet
|
|
||||||
INSTRUCTION_EXCESS = 51, // worst case execution instruction count was too large
|
|
||||||
MEMORY_GROW = 52, // memory.grow instruction is present but disallowed
|
|
||||||
NAMESPACE_MISSING = 53, // HookSet object lacked HookNamespace
|
|
||||||
NSDELETE = 54, // Informational: a namespace is being deleted
|
|
||||||
NSDELETE_ACCOUNT = 55, // nsdelete tried to delete ns from a non-existing account
|
|
||||||
NSDELETE_COUNT = 56, // namespace state count less than 0 / overflow
|
|
||||||
NSDELETE_DIR = 57, // could not delete directory node in ledger
|
|
||||||
NSDELETE_DIRECTORY = 58, // nsdelete operation failed to delete ns directory
|
|
||||||
NSDELETE_DIR_ENTRY = 59, // nsdelete operation failed due to bad entry in ns directory
|
|
||||||
NSDELETE_ENTRY = 60, // nsdelete operation failed due to missing hook state entry
|
|
||||||
NSDELETE_FIELD = 61,
|
|
||||||
NSDELETE_FLAGS = 62,
|
|
||||||
NSDELETE_NONSTATE = 63, // nsdelete operation failed due to the presence of a non-hookstate obj
|
|
||||||
NSDELETE_NOTHING = 64, // hsfNSDELETE provided but nothing to delete
|
|
||||||
OPERATION_INVALID = 65, // could not deduce an operation from the provided hookset obj
|
|
||||||
OVERRIDE_MISSING = 66, // HookSet object was trying to update or delete a hook but lacked hsfOVERRIDE
|
|
||||||
PARAMETERS_FIELD = 67, // HookParameters contained a HookParameter with an invalid key in it
|
|
||||||
PARAMETERS_ILLEGAL = 68, // HookParameters contained something other than a HookParameter
|
|
||||||
PARAMETERS_NAME = 69, // HookParameters contained a HookParameter which lacked ParameterName field
|
|
||||||
PARAM_HOOK_CBAK = 70, // hook and cbak must take exactly one u32 parameter
|
|
||||||
RETURN_HOOK_CBAK = 71, // hook and cbak must retunr i64
|
|
||||||
SHORT_HOOK = 72, // web assembly byte code ended abruptly
|
|
||||||
TYPE_INVALID = 73, // malformed and illegal wasm specifying an illegal local var type
|
|
||||||
WASM_BAD_MAGIC = 74, // wasm magic number missing or not wasm
|
|
||||||
WASM_INVALID = 75, // set hook operation would set invalid wasm
|
|
||||||
WASM_PARSE_LOOP = 76, // wasm section parsing resulted in an infinite loop
|
|
||||||
WASM_SMOKE_TEST = 77, // Informational: first attempt to load wasm into wasm runtime
|
|
||||||
WASM_TEST_FAILURE = 78, // the smoke test failed
|
|
||||||
WASM_TOO_BIG = 79, // set hook would exceed maximum hook size
|
|
||||||
WASM_TOO_SMALL = 80,
|
|
||||||
WASM_VALIDATION = 81, // a generic error while parsing wasm, usually leb128 overflow
|
|
||||||
// RH NOTE: only HookSet msgs got log codes, possibly all Hook log lines should get a code?
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace hook_api
|
namespace hook_api
|
||||||
@@ -193,186 +73,11 @@ namespace hook_api
|
|||||||
#define TER_TO_HOOK_RETURN_CODE(x)\
|
#define TER_TO_HOOK_RETURN_CODE(x)\
|
||||||
(((TERtoInt(x)) << 16)*-1)
|
(((TERtoInt(x)) << 16)*-1)
|
||||||
|
|
||||||
|
|
||||||
// for debugging if you want a lot of output change these to if (1)
|
// for debugging if you want a lot of output change these to if (1)
|
||||||
#define HOOK_DBG 1
|
#define HOOK_DBG 1
|
||||||
#define DBG_PRINTF if (HOOK_DBG) printf
|
#define DBG_PRINTF if (HOOK_DBG) printf
|
||||||
#define DBG_FPRINTF if (HOOK_DBG) fprintf
|
#define DBG_FPRINTF if (HOOK_DBG) fprintf
|
||||||
|
|
||||||
namespace keylet_code
|
|
||||||
{
|
|
||||||
enum keylet_code : uint32_t
|
|
||||||
{
|
|
||||||
HOOK = 1,
|
|
||||||
HOOK_STATE = 2,
|
|
||||||
ACCOUNT = 3,
|
|
||||||
AMENDMENTS = 4,
|
|
||||||
CHILD = 5,
|
|
||||||
SKIP = 6,
|
|
||||||
FEES = 7,
|
|
||||||
NEGATIVE_UNL = 8,
|
|
||||||
LINE = 9,
|
|
||||||
OFFER = 10,
|
|
||||||
QUALITY = 11,
|
|
||||||
EMITTED_DIR = 12,
|
|
||||||
TICKET = 13,
|
|
||||||
SIGNERS = 14,
|
|
||||||
CHECK = 15,
|
|
||||||
DEPOSIT_PREAUTH = 16,
|
|
||||||
UNCHECKED = 17,
|
|
||||||
OWNER_DIR = 18,
|
|
||||||
PAGE = 19,
|
|
||||||
ESCROW = 20,
|
|
||||||
PAYCHAN = 21,
|
|
||||||
EMITTED = 22
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace compare_mode {
|
|
||||||
enum compare_mode : uint32_t
|
|
||||||
{
|
|
||||||
EQUAL = 1,
|
|
||||||
LESS = 2,
|
|
||||||
GREATER = 4
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
enum hook_return_code : int64_t
|
|
||||||
{
|
|
||||||
SUCCESS = 0, // return codes > 0 are reserved for hook apis to return "success"
|
|
||||||
OUT_OF_BOUNDS = -1, // could not read or write to a pointer to provided by hook
|
|
||||||
INTERNAL_ERROR = -2, // eg directory is corrupt
|
|
||||||
TOO_BIG = -3, // something you tried to store was too big
|
|
||||||
TOO_SMALL = -4, // something you tried to store or provide was too small
|
|
||||||
DOESNT_EXIST = -5, // something you requested wasn't found
|
|
||||||
NO_FREE_SLOTS = -6, // when trying to load an object there is a maximum of 255 slots
|
|
||||||
INVALID_ARGUMENT = -7, // self explanatory
|
|
||||||
ALREADY_SET = -8, // returned when a one-time parameter was already set by the hook
|
|
||||||
PREREQUISITE_NOT_MET = -9, // returned if a required param wasn't set, before calling
|
|
||||||
FEE_TOO_LARGE = -10, // returned if the attempted operation would result in an absurd fee
|
|
||||||
EMISSION_FAILURE = -11, // returned if an emitted tx was not accepted by rippled
|
|
||||||
TOO_MANY_NONCES = -12, // a hook has a maximum of 256 nonces
|
|
||||||
TOO_MANY_EMITTED_TXN = -13, // a hook has emitted more than its stated number of emitted txn
|
|
||||||
NOT_IMPLEMENTED = -14, // an api was called that is reserved for a future version
|
|
||||||
INVALID_ACCOUNT = -15, // an api expected an account id but got something else
|
|
||||||
GUARD_VIOLATION = -16, // a guarded loop or function iterated over its maximum
|
|
||||||
INVALID_FIELD = -17, // the field requested is returning sfInvalid
|
|
||||||
PARSE_ERROR = -18, // hook asked hookapi to parse something the contents of which was invalid
|
|
||||||
RC_ROLLBACK = -19, // hook should terminate due to a rollback() call
|
|
||||||
RC_ACCEPT = -20, // hook should temrinate due to an accept() call
|
|
||||||
NO_SUCH_KEYLET = -21, // invalid keylet or keylet type
|
|
||||||
NOT_AN_ARRAY = -22, // if a count of an sle is requested but its not STI_ARRAY
|
|
||||||
NOT_AN_OBJECT = -23, // if a subfield is requested from something that isn't an object
|
|
||||||
INVALID_FLOAT = -10024, // specially selected value that will never be a valid exponent
|
|
||||||
DIVISION_BY_ZERO = -25,
|
|
||||||
MANTISSA_OVERSIZED = -26,
|
|
||||||
MANTISSA_UNDERSIZED = -27,
|
|
||||||
EXPONENT_OVERSIZED = -28,
|
|
||||||
EXPONENT_UNDERSIZED = -29,
|
|
||||||
OVERFLOW = -30, // if an operation with a float results in an overflow
|
|
||||||
NOT_IOU_AMOUNT = -31,
|
|
||||||
NOT_AN_AMOUNT = -32,
|
|
||||||
CANT_RETURN_NEGATIVE = -33,
|
|
||||||
NOT_AUTHORIZED = -34,
|
|
||||||
PREVIOUS_FAILURE_PREVENTS_RETRY = -35,
|
|
||||||
TOO_MANY_PARAMS = -36
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ExitType : uint8_t
|
|
||||||
{
|
|
||||||
UNSET = 0,
|
|
||||||
WASM_ERROR = 1,
|
|
||||||
ROLLBACK = 2,
|
|
||||||
ACCEPT = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
const int etxn_details_size = 105;
|
|
||||||
const int max_slots = 255;
|
|
||||||
const int max_nonce = 255;
|
|
||||||
const int max_emit = 255;
|
|
||||||
const int max_params = 16;
|
|
||||||
const int drops_per_byte = 31250; //RH TODO make these votable config option
|
|
||||||
const double fee_base_multiplier = 1.1f;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// RH NOTE: Find descriptions of api functions in ./impl/applyHook.cpp and hookapi.h (include for hooks)
|
|
||||||
|
|
||||||
static const std::set<std::string> import_whitelist
|
|
||||||
{
|
|
||||||
"accept",
|
|
||||||
"emit",
|
|
||||||
"etxn_burden",
|
|
||||||
"etxn_details",
|
|
||||||
"etxn_fee_base",
|
|
||||||
"etxn_generation",
|
|
||||||
"etxn_reserve",
|
|
||||||
"float_compare",
|
|
||||||
"float_divide",
|
|
||||||
"float_exponent",
|
|
||||||
"float_exponent_set",
|
|
||||||
"float_invert",
|
|
||||||
"float_mantissa",
|
|
||||||
"float_mantissa_set",
|
|
||||||
"float_mulratio",
|
|
||||||
"float_multiply",
|
|
||||||
"float_int",
|
|
||||||
"float_negate",
|
|
||||||
"float_one",
|
|
||||||
"float_set",
|
|
||||||
"float_sign",
|
|
||||||
"float_sign_set",
|
|
||||||
"float_sto",
|
|
||||||
"float_sto_set",
|
|
||||||
"float_sum",
|
|
||||||
"fee_base",
|
|
||||||
"_g",
|
|
||||||
"hook_account",
|
|
||||||
"hook_hash",
|
|
||||||
"ledger_seq",
|
|
||||||
"ledger_last_hash",
|
|
||||||
"nonce",
|
|
||||||
"otxn_burden",
|
|
||||||
"otxn_field",
|
|
||||||
"otxn_slot",
|
|
||||||
"otxn_generation",
|
|
||||||
"otxn_id",
|
|
||||||
"otxn_type",
|
|
||||||
"rollback",
|
|
||||||
"slot",
|
|
||||||
"slot_clear",
|
|
||||||
"slot_count",
|
|
||||||
"slot_id",
|
|
||||||
"slot_set",
|
|
||||||
"slot_size",
|
|
||||||
"slot_subarray",
|
|
||||||
"slot_subfield",
|
|
||||||
"slot_type",
|
|
||||||
"slot_float",
|
|
||||||
"state",
|
|
||||||
"state_foreign",
|
|
||||||
"state_set",
|
|
||||||
"state_foreign_set",
|
|
||||||
"trace",
|
|
||||||
"trace_num",
|
|
||||||
"trace_float",
|
|
||||||
"trace_slot",
|
|
||||||
"util_accid",
|
|
||||||
"util_raddr",
|
|
||||||
"util_sha512h",
|
|
||||||
"util_verify",
|
|
||||||
"sto_subarray",
|
|
||||||
"sto_subfield",
|
|
||||||
"sto_validate",
|
|
||||||
"sto_emplace",
|
|
||||||
"sto_erase",
|
|
||||||
"util_keylet",
|
|
||||||
"hook_pos",
|
|
||||||
"hook_param",
|
|
||||||
"hook_param_set",
|
|
||||||
"hook_skip"
|
|
||||||
};
|
|
||||||
|
|
||||||
DECLARE_HOOK_FUNCTION(int32_t, _g, uint32_t guard_id, uint32_t maxiter );
|
DECLARE_HOOK_FUNCTION(int32_t, _g, uint32_t guard_id, uint32_t maxiter );
|
||||||
|
|
||||||
DECLARE_HOOK_FUNCTION(int64_t, accept, uint32_t read_ptr, uint32_t read_len, int64_t error_code );
|
DECLARE_HOOK_FUNCTION(int64_t, accept, uint32_t read_ptr, uint32_t read_len, int64_t error_code );
|
||||||
67
src/ripple/app/hook/guard_checker.cpp
Normal file
67
src/ripple/app/hook/guard_checker.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string_view>
|
||||||
|
#include <optional>
|
||||||
|
#include "Guard.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
std::basic_ostream<char>&
|
||||||
|
guard_logger(
|
||||||
|
uint16_t code,
|
||||||
|
std::string_view accID,
|
||||||
|
void* )
|
||||||
|
{
|
||||||
|
return (std::cout << "\nSetHook(" << code << ")[]: ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
return fprintf(stderr, "Guard Checker\n\tUsage: %s somefile.wasm\n", argv[0]);
|
||||||
|
|
||||||
|
|
||||||
|
int fd = open(argv[1], O_RDONLY);
|
||||||
|
if (fd < 0)
|
||||||
|
return fprintf(stderr, "Could not open file for reading:`%s`\n", argv[1]);
|
||||||
|
|
||||||
|
size_t len = lseek(fd, 0, SEEK_END);
|
||||||
|
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
std::vector<uint8_t> hook(len);
|
||||||
|
|
||||||
|
uint8_t* ptr = hook.data();
|
||||||
|
|
||||||
|
size_t upto = 0;
|
||||||
|
while (upto < len)
|
||||||
|
{
|
||||||
|
size_t bytes_read = read(fd, ptr + upto, len - upto);
|
||||||
|
if (bytes_read < 0)
|
||||||
|
return fprintf(stderr, "Error reading file `%s`, only %ld bytes could be read\n", argv[1], upto);
|
||||||
|
upto += bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Read %ld bytes from `%s` successfully...\n", upto, argv[1]);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
auto logger = std::tuple<GuardLogFuncPtr, std::string_view, void*>{guard_logger, {}, 0};
|
||||||
|
auto result =
|
||||||
|
validateGuards(hook, true, logger);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
printf("Hook validation failed.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\nHook validation successful!\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/basics/Log.h>
|
#include <ripple/basics/Log.h>
|
||||||
#include <ripple/basics/Slice.h>
|
#include <ripple/basics/Slice.h>
|
||||||
#include <ripple/app/misc/Transaction.h>
|
#include <ripple/app/misc/Transaction.h>
|
||||||
2
src/ripple/app/hook/makefile
Normal file
2
src/ripple/app/hook/makefile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
guard_checker: guard_checker.cpp Guard.h Enum.h
|
||||||
|
g++ -o guard_checker guard_checker.cpp --std=c++17
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
#include <ripple/app/rdb/backend/RelationalDBInterfaceSqlite.h>
|
#include <ripple/app/rdb/backend/RelationalDBInterfaceSqlite.h>
|
||||||
#include <ripple/app/reporting/ReportingETL.h>
|
#include <ripple/app/reporting/ReportingETL.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/basics/PerfLog.h>
|
#include <ripple/basics/PerfLog.h>
|
||||||
#include <ripple/basics/UptimeClock.h>
|
#include <ripple/basics/UptimeClock.h>
|
||||||
#include <ripple/basics/mulDiv.h>
|
#include <ripple/basics/mulDiv.h>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -45,6 +45,24 @@ struct SetHookCtx
|
|||||||
Application& app;
|
Application& app;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum HookSetOperation : int8_t
|
||||||
|
{
|
||||||
|
hsoINVALID = -1,
|
||||||
|
hsoNOOP = 0,
|
||||||
|
hsoCREATE = 1,
|
||||||
|
hsoINSTALL = 2,
|
||||||
|
hsoDELETE = 3,
|
||||||
|
hsoNSDELETE = 4,
|
||||||
|
hsoUPDATE = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HookSetFlags : uint8_t
|
||||||
|
{
|
||||||
|
hsfOVERRIDE = (1U << 0U), // override or delete hook
|
||||||
|
hsfNSDELETE = (1U << 1U), // delete namespace
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class SetHook : public Transactor
|
class SetHook : public Transactor
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||||
#include <ripple/app/tx/apply.h>
|
#include <ripple/app/tx/apply.h>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#ifndef RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
|
#ifndef RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
|
||||||
#define RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
|
#define RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
|
||||||
|
|
||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/app/tx/applySteps.h>
|
#include <ripple/app/tx/applySteps.h>
|
||||||
#include <ripple/app/tx/impl/ApplyContext.h>
|
#include <ripple/app/tx/impl/ApplyContext.h>
|
||||||
#include <ripple/basics/XRPAmount.h>
|
#include <ripple/basics/XRPAmount.h>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#include <ripple/protocol/Feature.h>
|
#include <ripple/protocol/Feature.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/GRPCHandlers.h>
|
#include <ripple/rpc/GRPCHandlers.h>
|
||||||
#include <ripple/app/tx/applyHook.h>
|
#include <ripple/app/hook/applyHook.h>
|
||||||
#include <ripple/app/tx/impl/Transactor.h>
|
#include <ripple/app/tx/impl/Transactor.h>
|
||||||
#include <ripple/basics/FeeUnits.h>
|
#include <ripple/basics/FeeUnits.h>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user