From a634318e8d790066e1d2f69dc6d47d97f1df1eb6 Mon Sep 17 00:00:00 2001 From: Richard Holland Date: Tue, 21 May 2024 00:16:12 +1000 Subject: [PATCH] js sto_validate sto_subfield sto_subarray sto_emplace sto_erase, compiling not tested --- src/ripple/app/hook/applyHook.h | 31 +- src/ripple/app/hook/impl/applyHook.cpp | 386 +++++++++++++++++-------- 2 files changed, 299 insertions(+), 118 deletions(-) diff --git a/src/ripple/app/hook/applyHook.h b/src/ripple/app/hook/applyHook.h index 42b5f5d47..3906dd2e6 100644 --- a/src/ripple/app/hook/applyHook.h +++ b/src/ripple/app/hook/applyHook.h @@ -139,18 +139,32 @@ DECLARE_WASM_FUNCTION( sto_validate, uint32_t tread_ptr, uint32_t tread_len); +DECLARE_JS_FUNCTION( + JSValue, + sto_validate, + JSValue sto); DECLARE_WASM_FUNCTION( int64_t, sto_subfield, uint32_t read_ptr, uint32_t read_len, uint32_t field_id); +DECLARE_JS_FUNCTION( + JSValue, + sto_subfield, + JSValue sto, + JSValue field_id); DECLARE_WASM_FUNCTION( int64_t, sto_subarray, uint32_t read_ptr, uint32_t read_len, uint32_t array_id); +DECLARE_JS_FUNCTION( + JSValue, + sto_subarray, + JSValue sto, + JSValue array_id); DECLARE_WASM_FUNCTION( int64_t, sto_emplace, @@ -161,6 +175,12 @@ DECLARE_WASM_FUNCTION( uint32_t fread_ptr, uint32_t fread_len, uint32_t field_id); +DECLARE_JS_FUNCTION( + JSValue, + sto_emplace, + JSValue sto, + JSValue field_bytes, + JSValue field_id); DECLARE_WASM_FUNCTION( int64_t, sto_erase, @@ -169,6 +189,11 @@ DECLARE_WASM_FUNCTION( uint32_t read_ptr, uint32_t read_len, uint32_t field_id); +DECLARE_JS_FUNCTION( + JSValue, + sto_erase, + JSValue sto, + JSValue field_id); DECLARE_WASM_FUNCTION( int64_t, @@ -476,7 +501,7 @@ DECLARE_JS_FUNCTION( JSValue, slot_type, JSValue slot_no, - JSValeu flags); + JSValue flags); DECLARE_WASM_FUNCTION(int64_t, slot_float, uint32_t slot_no); DECLARE_JS_FUNCTION( JSValue, @@ -1179,13 +1204,13 @@ public: ADD_JS_FUNCTION(util_sha512h, ctx); ADD_JS_FUNCTION(util_keylet, ctx); - /* + ADD_JS_FUNCTION(sto_validate, ctx); ADD_JS_FUNCTION(sto_subfield, ctx); ADD_JS_FUNCTION(sto_subarray, ctx); ADD_JS_FUNCTION(sto_emplace, ctx); ADD_JS_FUNCTION(sto_erase, ctx); - + /* ADD_JS_FUNCTION(emit, ctx); */ ADD_JS_FUNCTION(etxn_burden, ctx); diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 86a72766a..9916e5761 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -5570,29 +5570,17 @@ get_stobject_length( return pe_unknown_type_late; } -// Given an serialized object in memory locate and return the offset and length -// of the payload of a subfield of that object. Arrays are returned fully -// formed. If successful returns offset and length joined as int64_t. Use -// SUB_OFFSET and SUB_LENGTH to extract. -DEFINE_WASM_FUNCTION( - int64_t, - sto_subfield, - uint32_t read_ptr, - uint32_t read_len, - uint32_t field_id) + +inline +int64_t __sto_subfield( + hook::HookContext& hookCtx, ApplyContext& applyCtx, beast::Journal& j, + uint8_t* start, size_t read_len, uint32_t field_id) { - WASM_HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, - // hookCtx on current stack - - if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) - return OUT_OF_BOUNDS; - if (read_len < 2) return TOO_SMALL; - unsigned char* start = (unsigned char*)(memory + read_ptr); - unsigned char* upto = start; - unsigned char* end = start + read_len; + uint8_t* upto = start; + uint8_t* end = start + read_len; DBG_PRINTF( "sto_subfield called, looking for field %u type %u\n", @@ -5639,17 +5627,17 @@ DEFINE_WASM_FUNCTION( return PARSE_ERROR; return DOESNT_EXIST; - - WASM_HOOK_TEARDOWN(); } - -// Same as subfield but indexes into a serialized array +// Given an serialized object in memory locate and return the offset and length +// of the payload of a subfield of that object. Arrays are returned fully +// formed. If successful returns offset and length joined as int64_t. Use +// SUB_OFFSET and SUB_LENGTH to extract. DEFINE_WASM_FUNCTION( int64_t, - sto_subarray, + sto_subfield, uint32_t read_ptr, uint32_t read_len, - uint32_t index_id) + uint32_t field_id) { WASM_HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, // hookCtx on current stack @@ -5657,10 +5645,44 @@ DEFINE_WASM_FUNCTION( if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) return OUT_OF_BOUNDS; + unsigned char* start = (unsigned char*)(memory + read_ptr); + + return __sto_subfield(hookCtx, applyCtx, j, start, read_len, field_id); + + WASM_HOOK_TEARDOWN(); +} + +DEFINE_JS_FUNCTION( + JSValue, + sto_subfield, + JSValue raw_sto, + JSValue raw_field_id) +{ + JS_HOOK_SETUP(); + + auto sto_in = FromJSIntArrayOrHexString(ctx, raw_sto, 1024); + auto field_id = FromJSInt(ctx, raw_field_id); + + if (!sto_in.has_value() || !field_id.has_value()) + returnJS(INVALID_ARGUMENT); + + if (*field_id > 0xFFFFFFFFULL || *field_id < 0) + returnJS(INVALID_ARGUMENT); + + returnJS(__sto_subfield(hookCtx, applyCtx, j, sto_in->data(), sto_in->size(), (uint32_t)(*field_id))); + + JS_HOOK_TEARDOWN(); +} + +inline +int64_t __sto_subarray( + hook::HookContext& hookCtx, ApplyContext& applyCtx, beast::Journal& j, + uint8_t* start, size_t read_len, uint32_t index_id) +{ + if (read_len < 2) return TOO_SMALL; - unsigned char* start = (unsigned char*)(memory + read_ptr); unsigned char* upto = start; unsigned char* end = start + read_len; @@ -5707,14 +5729,55 @@ DEFINE_WASM_FUNCTION( return PARSE_ERROR; return DOESNT_EXIST; + +} + +// Same as subfield but indexes into a serialized array +DEFINE_WASM_FUNCTION( + int64_t, + sto_subarray, + uint32_t read_ptr, + uint32_t read_len, + uint32_t index_id) +{ + WASM_HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, + // hookCtx on current stack + + if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) + return OUT_OF_BOUNDS; + + unsigned char* start = (unsigned char*)(memory + read_ptr); + + return __sto_subarray(hookCtx, applyCtx, j, start, read_len, index_id); WASM_HOOK_TEARDOWN(); } +DEFINE_JS_FUNCTION( + JSValue, + sto_subarray, + JSValue raw_sto, + JSValue raw_index_id) +{ + JS_HOOK_SETUP(); + + auto sto_in = FromJSIntArrayOrHexString(ctx, raw_sto, 1024); + auto index_id = FromJSInt(ctx, raw_index_id); + + if (!sto_in.has_value() || !index_id.has_value()) + returnJS(INVALID_ARGUMENT); + + if (*index_id > 0xFFFFFFFFULL || *index_id < 0) + returnJS(INVALID_ARGUMENT); + + returnJS(__sto_subfield(hookCtx, applyCtx, j, sto_in->data(), sto_in->size(), (uint32_t)(*index_id))); + + JS_HOOK_TEARDOWN(); +} + // Convert an account ID into a base58-check encoded r-address DEFINE_WASM_FUNCTION( - - int64_t, + int64_t, util_raddr, uint32_t write_ptr, uint32_t write_len, @@ -5893,36 +5956,13 @@ overlapping_memory(std::vector regions) return false; } -/** - * Inject a field into an sto if there is sufficient space - * Field must be fully formed and wrapped (NOT JUST PAYLOAD) - * sread - source object - * fread - field to inject - */ -DEFINE_WASM_FUNCTION( - 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) +inline +std::variant> +__sto_emplace( + hook::HookContext& hookCtx, ApplyContext& applyCtx, beast::Journal& j, + uint8_t* sread_ptr, size_t sread_len, + uint8_t* fread_ptr, size_t fread_len, uint32_t field_id) { - WASM_HOOK_SETUP(); - - if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) - return OUT_OF_BOUNDS; - - if (NOT_IN_BOUNDS(sread_ptr, sread_len, memory_length)) - return OUT_OF_BOUNDS; - - if (NOT_IN_BOUNDS(fread_ptr, fread_len, memory_length)) - return OUT_OF_BOUNDS; - - if (write_len < sread_len + fread_len) - return TOO_SMALL; // RH TODO: put these constants somewhere (votable?) if (sread_len > 1024 * 16) @@ -5934,12 +5974,6 @@ DEFINE_WASM_FUNCTION( if (fread_len == 0 && fread_ptr == 0) { // this is a delete operation - if (overlapping_memory( - {write_ptr, - write_ptr + write_len, - sread_ptr, - sread_ptr + sread_len})) - return MEM_OVERLAP; } else { @@ -5948,25 +5982,21 @@ DEFINE_WASM_FUNCTION( if (fread_len < 2) return TOO_SMALL; - - // check for buffer overlaps - if (overlapping_memory( - {write_ptr, - write_ptr + write_len, - sread_ptr, - sread_ptr + sread_len, - fread_ptr, - fread_ptr + fread_len})) - return MEM_OVERLAP; } + std::vector out; + out.reserve(sread_len + fread_len); + std::fill(out.begin(), out.end(), 0); + + uint8_t* write_ptr = out.data(); + // we must inject the field at the canonical location.... // so find that location - unsigned char* start = (unsigned char*)(memory + sread_ptr); - unsigned char* upto = start; - unsigned char* end = start + sread_len; - unsigned char* inject_start = end; - unsigned char* inject_end = end; + uint8_t* start = sread_ptr; + uint8_t* upto = start; + uint8_t* end = start + sread_len; + uint8_t* inject_start = end; + uint8_t* inject_end = end; DBG_PRINTF( "sto_emplace called, looking for field %u type %u\n", @@ -6010,44 +6040,122 @@ DEFINE_WASM_FUNCTION( // part 1 if (inject_start - start > 0) { - WRITE_WASM_MEMORY( - bytes_written, - write_ptr, - write_len, - start, - (inject_start - start), - memory, - memory_length); + size_t len = inject_start - start; + memcpy(write_ptr, start, len); + bytes_written += len; } if (fread_len > 0) { // write the field (or don't if it's a delete operation) - WRITE_WASM_MEMORY( - bytes_written, - (write_ptr + bytes_written), - (write_len - bytes_written), - memory + fread_ptr, - fread_len, - memory, - memory_length); + memcpy(write_ptr + bytes_written, fread_ptr, fread_len); + bytes_written += fread_len; } // part 2 if (end - inject_end > 0) { - WRITE_WASM_MEMORY( - bytes_written, - (write_ptr + bytes_written), - (write_len - bytes_written), - inject_end, - (end - inject_end), - memory, - memory_length); + size_t len = end - inject_end; + memcpy(write_ptr + bytes_written, inject_end, len); } - return bytes_written; - WASM_HOOK_TEARDOWN(); + out.resize(bytes_written); + return out; +} +/** + * Inject a field into an sto if there is sufficient space + * Field must be fully formed and wrapped (NOT JUST PAYLOAD) + * sread - source object + * fread - field to inject + */ +DEFINE_WASM_FUNCTION( + 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) +{ + WASM_HOOK_SETUP(); + + if (NOT_IN_BOUNDS(write_ptr, write_len, memory_length)) + return OUT_OF_BOUNDS; + + if (NOT_IN_BOUNDS(sread_ptr, sread_len, memory_length)) + return OUT_OF_BOUNDS; + + if (NOT_IN_BOUNDS(fread_ptr, fread_len, memory_length)) + return OUT_OF_BOUNDS; + + auto out = __sto_emplace(hookCtx, applyCtx, j, + sread_ptr + memory, sread_len, fread_ptr + memory, fread_len, field_id); + + if (std::holds_alternative(out)) + return std::get(out); + + // otherwise it's returned a vec + + std::vector const& vec = std::get>(out); + + if (vec.size() > write_len) + return INTERNAL_ERROR; + + WRITE_WASM_MEMORY_AND_RETURN(write_ptr, write_len, vec.data(), vec.size(), memory, memory_length); + + WASM_HOOK_TEARDOWN(); +} + + +DEFINE_JS_FUNCTION( + JSValue, + sto_emplace, + JSValue raw_sto, + JSValue raw_field, + JSValue raw_field_id) +{ + JS_HOOK_SETUP(); + + auto sto = FromJSIntArrayOrHexString(ctx, raw_sto, 16*1024); + auto field = FromJSIntArrayOrHexString(ctx, raw_field, 4*1024); + auto field_id = FromJSInt(ctx, raw_field_id); + + if (!sto.has_value() || !field_id.has_value()) + returnJS(INVALID_ARGUMENT); + + if (*field_id > 0xFFFFFFFFULL || *field_id < 0) + returnJS(INVALID_ARGUMENT); + + if (!field.has_value() && !JS_IsUndefined(raw_field)) + returnJS(INVALID_ARGUMENT); + + bool const isErase = !field.has_value(); + + auto ret = __sto_emplace(hookCtx, applyCtx, j, + sto->data(), sto->size(), + isErase ? 0 : field->data(), + isErase ? 0 : field->size(), + *field_id); + + if (std::holds_alternative(ret)) + returnJS(std::get(ret)); + + // otherwise it's returned a vec + + std::vector const& vec = std::get>(ret); + + if (vec.size() > sto->size() + (isErase ? 0 : field->size())) + returnJS(INTERNAL_ERROR); + + auto out = ToJSIntArray(ctx, vec); + if (!out.has_value()) + returnJS(INTERNAL_ERROR); + + return *out; + + JS_HOOK_TEARDOWN(); } /** @@ -6080,24 +6188,35 @@ DEFINE_WASM_FUNCTION( return ret; } -DEFINE_WASM_FUNCTION( - int64_t, - sto_validate, - uint32_t read_ptr, - uint32_t read_len) +DEFINE_JS_FUNCTION( + JSValue, + sto_erase, + JSValue raw_sto, + JSValue raw_field) { - WASM_HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, - // hookCtx on current stack + JS_HOOK_SETUP(); - // RH TODO: see if an internal ripple function/class would do this better + JSValueConst argv2[] = { + argv[0], + JS_UNDEFINED, + argv[1] + }; + + return FORWARD_JS_FUNCTION_CALL(sto_emplace, 3, argv2); - if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) - return OUT_OF_BOUNDS; + JS_HOOK_TEARDOWN(); +} +inline +int64_t +__sto_validate( + hook::HookContext& hookCtx, ApplyContext& applyCtx, beast::Journal& j, + uint8_t* read_ptr, size_t read_len) +{ if (read_len < 2) return TOO_SMALL; - unsigned char* start = (unsigned char*)(memory + read_ptr); + unsigned char* start = read_ptr; unsigned char* upto = start; unsigned char* end = start + read_len; @@ -6113,9 +6232,44 @@ DEFINE_WASM_FUNCTION( return upto == end ? 1 : 0; +} + +DEFINE_WASM_FUNCTION( + int64_t, + sto_validate, + uint32_t read_ptr, + uint32_t read_len) +{ + WASM_HOOK_SETUP(); // populates memory_ctx, memory, memory_length, applyCtx, + // hookCtx on current stack + + // RH TODO: see if an internal ripple function/class would do this better + + if (NOT_IN_BOUNDS(read_ptr, read_len, memory_length)) + return OUT_OF_BOUNDS; + + return __sto_validate(hookCtx, applyCtx, j, read_ptr + memory, read_len); + WASM_HOOK_TEARDOWN(); } +DEFINE_JS_FUNCTION( + JSValue, + sto_validate, + JSValue raw_sto) +{ + JS_HOOK_SETUP(); + + auto sto = FromJSIntArrayOrHexString(ctx, raw_sto, 16*1024); + + if (!sto.has_value()) + returnJS(INVALID_ARGUMENT); + + returnJS(__sto_validate(hookCtx, applyCtx, j, sto->data(), sto->size())); + + JS_HOOK_TEARDOWN(); +} + // Validate either an secp256k1 signature or an ed25519 signature, using the // XRPLD convention for identifying the key type. Pointer prefixes: d = data, s // = signature, k = public key. @@ -7440,6 +7594,8 @@ DEFINE_JS_FUNCTION( return *out; } + returnJS(DOESNT_EXIST); + JS_HOOK_TEARDOWN(); }