diff --git a/.clang-format b/.clang-format index 7b0fda27c9..660a60c4a1 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ --- -Language: Cpp +Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false @@ -19,52 +19,52 @@ AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BraceWrapping: - AfterClass: true + AfterClass: true AfterControlStatement: true - AfterEnum: false - AfterFunction: true - AfterNamespace: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false BreakBeforeBinaryOperators: false BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: true -ColumnLimit: 80 -CommentPragmas: '^ IWYU pragma:' +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false ExperimentalAutoDetectBinPacking: false -ForEachMacros: [ Q_FOREACH, BOOST_FOREACH ] -IncludeBlocks: Regroup +ForEachMacros: [Q_FOREACH, BOOST_FOREACH] +IncludeBlocks: Regroup IncludeCategories: - - Regex: '^<(test)/' - Priority: 0 - - Regex: '^<(xrpld)/' - Priority: 1 - - Regex: '^<(xrpl)/' - Priority: 2 - - Regex: '^<(boost)/' - Priority: 3 - - Regex: '^.*/' - Priority: 4 - - Regex: '^.*\.h' - Priority: 5 - - Regex: '.*' - Priority: 6 -IncludeIsMainRegex: '$' + - Regex: "^<(test)/" + Priority: 0 + - Regex: "^<(xrpld)/" + Priority: 1 + - Regex: "^<(xrpl)/" + Priority: 2 + - Regex: "^<(boost)/" + Priority: 3 + - Regex: "^.*/" + Priority: 4 + - Regex: '^.*\.h' + Priority: 5 + - Regex: ".*" + Priority: 6 +IncludeIsMainRegex: "$" IndentCaseLabels: true IndentFunctionDeclarationAfterType: false IndentRequiresClause: true -IndentWidth: 4 +IndentWidth: 4 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 1 @@ -78,20 +78,20 @@ PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left -ReflowComments: true +ReflowComments: true RequiresClausePosition: OwnLine -SortIncludes: true +SortIncludes: true SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 -SpacesInAngles: false +SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never -QualifierAlignment: Right \ No newline at end of file +Standard: Cpp11 +TabWidth: 8 +UseTab: Never +QualifierAlignment: Right diff --git a/.github/actions/dependencies/action.yml b/.github/actions/dependencies/action.yml index afce1557d3..c251daa936 100644 --- a/.github/actions/dependencies/action.yml +++ b/.github/actions/dependencies/action.yml @@ -17,6 +17,7 @@ runs: conan export external/rocksdb rocksdb/9.7.3@ conan export external/soci soci/4.0.3@ conan export external/nudb nudb/2.0.8@ + conan export -k external/wamr wamr/2.2.0@ - name: add Ripple Conan remote shell: bash run: | diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index de59e07761..76597a68e0 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -408,6 +408,7 @@ jobs: conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default conan export external/snappy snappy/1.1.10@ conan export external/soci soci/4.0.3@ + conan export -k external/wamr wamr/2.2.0@ - name: build dependencies run: | diff --git a/BUILD.md b/BUILD.md index 1ba767cd88..d4824fe1a0 100644 --- a/BUILD.md +++ b/BUILD.md @@ -204,6 +204,17 @@ It fixes some source files to add missing `#include`s. conan export --version 2.0.8 external/nudb ``` +Export our [Conan recipe for WAMR](./external/wamr). +It add metering and expose some internal structures. + + + ``` + # Conan 1.x + conan export external/wamr wamr/2.2.0@ + # Conan 2.x + conan export --version 2.2.0 external/wamr + ``` + ### Build and Test 1. Create a build directory and move into it. diff --git a/CMakeLists.txt b/CMakeLists.txt index a9f063db57..7d170c7bae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,7 @@ endif() find_package(nudb REQUIRED) find_package(date REQUIRED) find_package(xxHash REQUIRED) +find_package(wamr REQUIRED) target_link_libraries(ripple_libs INTERFACE ed25519::ed25519 diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 8fb7d00875..bd2b044c4c 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -1249,6 +1249,39 @@ # Example: # owner_reserve = 2000000 # 2 XRP # +# extension_compute_limit = +# +# The extension compute limit is the maximum amount of gas that can be +# consumed by a single transaction. The gas limit is used to prevent +# transactions from consuming too many resources. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# extension_compute_limit = 1000000 # 1 million gas +# +# extension_size_limit = +# +# The extension size limit is the maximum size of a WASM extension in +# bytes. The size limit is used to prevent extensions from consuming +# too many resources. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# extension_size_limit = 100000 # 100 kb +# +# gas_price = +# +# The gas price is the conversion between WASM gas and its price in drops. +# +# If this parameter is unspecified, rippled will use an internal +# default. Don't change this without understanding the consequences. +# +# Example: +# gas_price = 1000000 # 1 drop per gas #------------------------------------------------------------------------------- # # 9. Misc Settings diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index 1ef5a4ad68..b3a6a77bcb 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -65,6 +65,7 @@ target_link_libraries(xrpl.imports.main xrpl.libpb xxHash::xxhash $<$:antithesis-sdk-cpp> + wamr::wamr ) include(add_module) diff --git a/conanfile.py b/conanfile.py index a42c116ca2..a0b1602daa 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,6 +2,7 @@ from conan import ConanFile from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout import re + class Xrpl(ConanFile): name = 'xrpl' @@ -32,6 +33,7 @@ class Xrpl(ConanFile): 'soci/4.0.3', 'xxhash/0.8.2', 'zlib/1.3.1', + 'wamr/2.2.0', ] tool_requires = [ @@ -125,6 +127,7 @@ class Xrpl(ConanFile): self.folders.generators = 'build/generators' generators = 'CMakeDeps' + def generate(self): tc = CMakeToolchain(self) tc.variables['tests'] = self.options.tests diff --git a/external/wamr/conandata.yml b/external/wamr/conandata.yml new file mode 100644 index 0000000000..6c49946eca --- /dev/null +++ b/external/wamr/conandata.yml @@ -0,0 +1,6 @@ +patches: + 2.2.0: + - patch_description: add metering to iwasm interpreter + patch_file: patches/ripp_metering.patch + patch_type: conan + diff --git a/external/wamr/conanfile.py b/external/wamr/conanfile.py new file mode 100644 index 0000000000..ae83e8c13c --- /dev/null +++ b/external/wamr/conanfile.py @@ -0,0 +1,91 @@ +from conans import ConanFile, tools +from conan.tools.cmake import CMake, CMakeToolchain, CMakeDeps, cmake_layout +from conan.tools.files import ( + apply_conandata_patches, + export_conandata_patches, + # get, +) + +# import os + +required_conan_version = ">=1.55.0" + + +class WamrConan(ConanFile): + name = "wamr" + version = "2.2.0" + license = "Apache License v2.0" + url = "https://github.com/bytecodealliance/wasm-micro-runtime.git" + description = "Webassembly micro runtime" + package_type = "library" + settings = "os", "compiler", "build_type", "arch" + options = {"shared": [True, False], "fPIC": [True, False]} + default_options = {"shared": False, "fPIC": True} + generators = "CMakeToolchain", "CMakeDeps" + # requires = [("llvm/20.1.1@")] + + def export_sources(self): + export_conandata_patches(self) + pass + + # def build_requirements(self): + # self.tool_requires("llvm/20.1.1") + + def config_options(self): + if self.settings.os == "Windows": + del self.options.fPIC + + def layout(self): + cmake_layout(self, src_folder="src") + + def source(self): + git = tools.Git() + git.clone( + "https://github.com/bytecodealliance/wasm-micro-runtime.git", + "c883fafead005e87ad3122b05409886f507c1cb0", + shallow=True, + ) + # get(self, **self.conan_data["sources"][self.version], strip_root=True) + + def generate(self): + tc = CMakeToolchain(self) + + tc.variables["WAMR_BUILD_INTERP"] = 1 + tc.variables["WAMR_BUILD_FAST_INTERP"] = 1 + tc.variables["WAMR_BUILD_INSTRUCTION_METERING"] = 1 + tc.variables["WAMR_BUILD_AOT"] = 0 + tc.variables["WAMR_BUILD_JIT"] = 0 + tc.variables["WAMR_BUILD_FAST_JIT"] = 0 + tc.variables["WAMR_DISABLE_HW_BOUND_CHECK"] = 1 + tc.variables["WAMR_DISABLE_STACK_HW_BOUND_CHECK"] = 1 + tc.variables["WAMR_BH_LOG"] = "wamr_log_to_rippled" + # tc.variables["WAMR_BUILD_FAST_JIT"] = 0 if self.settings.os == "Windows" else 1 + # ll_dep = self.dependencies["llvm"] + # self.output.info(f"-----------package_folder: {type(ll_dep.__dict__)}") + # tc.variables["LLVM_DIR"] = os.path.join(ll_dep.package_folder, "lib", "cmake", "llvm") + tc.generate() + + # This generates "foo-config.cmake" and "bar-config.cmake" in self.generators_folder + deps = CMakeDeps(self) + deps.generate() + + def build(self): + apply_conandata_patches(self) + cmake = CMake(self) + cmake.verbose = True + cmake.configure() + cmake.build() + # self.run(f'echo {self.source_folder}') + # Explicit way: + # self.run('cmake %s/hello %s' % (self.source_folder, cmake.command_line)) + # self.run("cmake --build . %s" % cmake.build_config) + + def package(self): + cmake = CMake(self) + cmake.verbose = True + cmake.install() + + def package_info(self): + self.cpp_info.libs = ["iwasm"] + self.cpp_info.names["cmake_find_package"] = "wamr" + self.cpp_info.names["cmake_find_package_multi"] = "wamr" diff --git a/external/wamr/patches/ripp_metering.patch b/external/wamr/patches/ripp_metering.patch new file mode 100644 index 0000000000..a2abf37a57 --- /dev/null +++ b/external/wamr/patches/ripp_metering.patch @@ -0,0 +1,470 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 551991f8..5f48a0b8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,7 +1,7 @@ + # Copyright (C) 2019 Intel Corporation. All rights reserved. + # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +-cmake_minimum_required (VERSION 3.14) ++cmake_minimum_required (VERSION 3.20) + + option(BUILD_SHARED_LIBS "Build using shared libraries" OFF) + +diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake +index 1cb50235..bd103022 100644 +--- a/build-scripts/config_common.cmake ++++ b/build-scripts/config_common.cmake +@@ -669,6 +669,10 @@ if (WAMR_BUILD_AOT_VALIDATOR EQUAL 1) + message (" AOT validator enabled") + add_definitions (-DWASM_ENABLE_AOT_VALIDATOR=1) + endif () ++if (WAMR_BUILD_INSTRUCTION_METERING EQUAL 1) ++ message (" Instruction metering enabled") ++ add_definitions (-DWASM_ENABLE_INSTRUCTION_METERING=1) ++endif () + + ######################################## + # Show Phase4 Wasm proposals status. +diff --git a/core/config.h b/core/config.h +index cb1189c9..a4e1499e 100644 +--- a/core/config.h ++++ b/core/config.h +@@ -716,4 +716,8 @@ unless used elsewhere */ + #define WASM_ENABLE_AOT_VALIDATOR 0 + #endif + ++#ifndef WASM_ENABLE_INSTRUCTION_METERING ++#define WASM_ENABLE_INSTRUCTION_METERING 0 ++#endif ++ + #endif /* end of _CONFIG_H_ */ +diff --git a/core/iwasm/common/wasm_c_api.c b/core/iwasm/common/wasm_c_api.c +index 269ec577..bc6fd01b 100644 +--- a/core/iwasm/common/wasm_c_api.c ++++ b/core/iwasm/common/wasm_c_api.c +@@ -5389,3 +5389,8 @@ wasm_instance_get_wasm_func_exec_time(const wasm_instance_t *instance, + return -1.0; + #endif + } ++ ++wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*instance) ++{ ++ return wasm_runtime_get_exec_env_singleton(instance->inst_comm_rt); ++} +diff --git a/core/iwasm/common/wasm_exec_env.c b/core/iwasm/common/wasm_exec_env.c +index e33fd9f3..d1ff9c41 100644 +--- a/core/iwasm/common/wasm_exec_env.c ++++ b/core/iwasm/common/wasm_exec_env.c +@@ -85,6 +85,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst, + wasm_runtime_dump_exec_env_mem_consumption(exec_env); + #endif + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ exec_env->instructions_to_execute = -1; ++ for(int i = 0; i < 256; ++i) ++ exec_env->instructions_schedule[i] = 1; ++#endif ++ + return exec_env; + + #ifdef OS_ENABLE_HW_BOUND_CHECK +diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h +index ce0c1fa7..2713a092 100644 +--- a/core/iwasm/common/wasm_exec_env.h ++++ b/core/iwasm/common/wasm_exec_env.h +@@ -87,6 +87,12 @@ typedef struct WASMExecEnv { + uint8 *bottom; + } wasm_stack; + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ /* instructions to execute */ ++ int64 instructions_to_execute; ++ int64 instructions_schedule[256]; ++#endif ++ + #if WASM_ENABLE_FAST_JIT != 0 + /** + * Cache for +diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c +index d33c0272..21bfdf29 100644 +--- a/core/iwasm/common/wasm_runtime_common.c ++++ b/core/iwasm/common/wasm_runtime_common.c +@@ -2285,6 +2285,31 @@ wasm_runtime_access_exce_check_guard_page() + } + #endif + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ ++void ++wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, ++ int64 instructions_to_execute) ++{ ++ exec_env->instructions_to_execute = instructions_to_execute; ++} ++ ++int64 ++wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env) ++{ ++ return exec_env->instructions_to_execute; ++} ++ ++void ++wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env, ++ int64 const *instructions_schedule) ++{ ++ for(int i = 0; i < 256; ++i) ++ exec_env->instructions_schedule[i] = instructions_schedule[i]; ++} ++ ++#endif ++ + WASMFuncType * + wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type) +@@ -7792,13 +7817,14 @@ wasm_runtime_get_module_name(wasm_module_t module) + bool + wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) + { ++#if WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 + uint8 *boundary = exec_env->native_stack_boundary; + RECORD_STACK_USAGE(exec_env, (uint8 *)&boundary); + if (boundary == NULL) { + /* the platform doesn't support os_thread_get_stack_boundary */ + return true; + } +-#if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0 ++#if defined(OS_ENABLE_HW_BOUND_CHECK) + uint32 page_size = os_getpagesize(); + uint32 guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT; + boundary = boundary + page_size * guard_page_count; +@@ -7808,6 +7834,7 @@ wasm_runtime_detect_native_stack_overflow(WASMExecEnv *exec_env) + "native stack overflow"); + return false; + } ++#endif + return true; + } + +@@ -7830,7 +7857,7 @@ wasm_runtime_detect_native_stack_overflow_size(WASMExecEnv *exec_env, + boundary = boundary - WASM_STACK_GUARD_SIZE + requested_size; + if ((uint8 *)&boundary < boundary) { + wasm_runtime_set_exception(wasm_runtime_get_module_inst(exec_env), +- "native stack overflow"); ++ "native s stack overflow"); + return false; + } + return true; +diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h +index 8ac032bf..5ca5d489 100644 +--- a/core/iwasm/common/wasm_runtime_common.h ++++ b/core/iwasm/common/wasm_runtime_common.h +@@ -791,9 +791,25 @@ WASM_RUNTIME_API_EXTERN void + wasm_runtime_set_native_stack_boundary(WASMExecEnv *exec_env, + uint8 *native_stack_boundary); + +-#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ + /* See wasm_export.h for description */ + WASM_RUNTIME_API_EXTERN void ++wasm_runtime_set_instruction_count_limit(WASMExecEnv *exec_env, ++ int64 instructions_to_execute); ++WASM_RUNTIME_API_EXTERN int64 ++wasm_runtime_get_instruction_count_limit(WASMExecEnv *exec_env); ++ ++WASM_RUNTIME_API_EXTERN void ++wasm_runtime_set_instruction_schedule(WASMExecEnv *exec_env, ++ int64 const *instructions_schedule); ++ ++#endif ++ ++#if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 ++/* See wasm_export.h for description */ ++WASM_RUNTIME_API_EXTERN ++void + wasm_runtime_set_bounds_checks(WASMModuleInstanceCommon *module_inst, + bool enable); + +diff --git a/core/iwasm/include/wasm_c_api.h b/core/iwasm/include/wasm_c_api.h +index 241a0eec..9eb0dde1 100644 +--- a/core/iwasm/include/wasm_c_api.h ++++ b/core/iwasm/include/wasm_c_api.h +@@ -19,8 +19,10 @@ + #if defined(_MSC_BUILD) + #if defined(COMPILING_WASM_RUNTIME_API) + #define WASM_API_EXTERN __declspec(dllexport) +-#else ++#elif defined(_DLL) + #define WASM_API_EXTERN __declspec(dllimport) ++#else ++#define WASM_API_EXTERN + #endif + #else + #define WASM_API_EXTERN +@@ -701,6 +703,11 @@ WASM_API_EXTERN double wasm_instance_sum_wasm_exec_time(const wasm_instance_t*); + // func_name. If the function is not found, return 0. + WASM_API_EXTERN double wasm_instance_get_wasm_func_exec_time(const wasm_instance_t*, const char *); + ++struct WASMExecEnv; ++typedef struct WASMExecEnv *wasm_exec_env_t; ++ ++WASM_API_EXTERN wasm_exec_env_t wasm_instance_exec_env(const wasm_instance_t*); ++ + /////////////////////////////////////////////////////////////////////////////// + // Convenience + +diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h +index b73a0364..3fd0949f 100644 +--- a/core/iwasm/include/wasm_export.h ++++ b/core/iwasm/include/wasm_export.h +@@ -20,8 +20,10 @@ + #if defined(_MSC_BUILD) + #if defined(COMPILING_WASM_RUNTIME_API) + #define WASM_RUNTIME_API_EXTERN __declspec(dllexport) +-#else ++#elif defined(_DLL) + #define WASM_RUNTIME_API_EXTERN __declspec(dllimport) ++#else ++#define WASM_RUNTIME_API_EXTERN + #endif + #elif defined(__GNUC__) || defined(__clang__) + #define WASM_RUNTIME_API_EXTERN __attribute__((visibility("default"))) +@@ -1821,6 +1823,27 @@ WASM_RUNTIME_API_EXTERN void + wasm_runtime_set_native_stack_boundary(wasm_exec_env_t exec_env, + uint8_t *native_stack_boundary); + ++/** ++ * Set the instruction count limit to the execution environment. ++ * By default the instruction count limit is -1, which means no limit. ++ * However, if the instruction count limit is set to a positive value, ++ * the execution will be terminated when the instruction count reaches ++ * the limit. ++ * ++ * @param exec_env the execution environment ++ * @param instruction_count the instruction count limit ++ */ ++WASM_RUNTIME_API_EXTERN void ++wasm_runtime_set_instruction_count_limit(wasm_exec_env_t exec_env, ++ int64_t instruction_count); ++ ++WASM_RUNTIME_API_EXTERN int64_t ++wasm_runtime_get_instruction_count_limit(wasm_exec_env_t exec_env); ++ ++WASM_RUNTIME_API_EXTERN void ++wasm_runtime_set_instruction_schedule(wasm_exec_env_t exec_env, ++ int64_t const *instructions_schedule); ++ + /** + * Dump runtime memory consumption, including: + * Exec env memory consumption +diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c +index 41ac4c72..bd8f714a 100644 +--- a/core/iwasm/interpreter/wasm_interp_classic.c ++++ b/core/iwasm/interpreter/wasm_interp_classic.c +@@ -1516,10 +1516,13 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, + } \ + os_mutex_unlock(&exec_env->wait_lock); \ + } \ ++ CHECK_INSTRUCTION_LIMIT(); \ + goto *handle_table[*frame_ip++]; \ + } while (0) + #else +-#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() ++#define HANDLE_OP_END() \ ++ CHECK_INSTRUCTION_LIMIT(); \ ++ FETCH_OPCODE_AND_DISPATCH() + #endif + + #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +@@ -1542,9 +1545,12 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst, + } \ + os_mutex_unlock(&exec_env->wait_lock); \ + } \ ++ CHECK_INSTRUCTION_LIMIT(); \ + continue; + #else +-#define HANDLE_OP_END() continue ++#define HANDLE_OP_END() \ ++ CHECK_INSTRUCTION_LIMIT(); \ ++ continue; + #endif + + #endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ +@@ -1562,6 +1568,20 @@ get_global_addr(uint8 *global_data, WASMGlobalInstance *global) + #endif + } + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++#define CHECK_INSTRUCTION_LIMIT() \ ++ if (instructions_to_execute >= 0) \ ++ { \ ++ instructions_to_execute -= instructions_schedule[opcode];\ ++ if (instructions_to_execute < 0) { \ ++ wasm_set_exception(module, "instruction limit exceeded"); \ ++ goto got_exception; \ ++ } \ ++ } ++#else ++#define CHECK_INSTRUCTION_LIMIT() (void)0 ++#endif ++ + static void + wasm_interp_call_func_bytecode(WASMModuleInstance *module, + WASMExecEnv *exec_env, +@@ -1605,6 +1625,17 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + uint32 local_idx, local_offset, global_idx; + uint8 local_type, *global_addr; + uint32 cache_index, type_index, param_cell_num, cell_num; ++ ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ int64 instructions_to_execute = -1; ++ int64 const *instructions_schedule = NULL; ++ if(exec_env) ++ { ++ instructions_to_execute = exec_env->instructions_to_execute; ++ instructions_schedule = exec_env->instructions_schedule; ++ } ++#endif ++ + #if WASM_ENABLE_EXCE_HANDLING != 0 + int32_t exception_tag_index; + #endif +@@ -6859,6 +6890,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, prev_frame); + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ if(exec_env) ++ exec_env->instructions_to_execute = instructions_to_execute; ++#endif ++ + if (!prev_frame->ip) { + /* Called from native. */ + return; +@@ -6899,6 +6935,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + } + #endif + SYNC_ALL_TO_FRAME(); ++ ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ if(exec_env) ++ exec_env->instructions_to_execute = instructions_to_execute; ++#endif ++ + return; + + #if WASM_ENABLE_LABELS_AS_VALUES == 0 +diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c +index f33ad60e..9cbf2010 100644 +--- a/core/iwasm/interpreter/wasm_interp_fast.c ++++ b/core/iwasm/interpreter/wasm_interp_fast.c +@@ -105,6 +105,19 @@ typedef float64 CellType_F64; + goto unaligned_atomic; \ + } while (0) + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++#define CHECK_INSTRUCTION_LIMIT() \ ++ if (instructions_to_execute >= 0) { \ ++ instructions_to_execute -= instructions_schedule[opcode]; \ ++ if (instructions_to_execute < 0) { \ ++ wasm_set_exception(module, "instruction limit exceeded"); \ ++ goto got_exception; \ ++ } \ ++ } ++#else ++#define CHECK_INSTRUCTION_LIMIT() (void)0 ++#endif ++ + static inline uint32 + rotl32(uint32 n, uint32 c) + { +@@ -1466,12 +1479,14 @@ wasm_interp_dump_op_count() + } while (0) + #endif + #endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +-#define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH() ++#define HANDLE_OP_END() CHECK_INSTRUCTION_LIMIT(); FETCH_OPCODE_AND_DISPATCH() + + #else /* else of WASM_ENABLE_LABELS_AS_VALUES */ + + #define HANDLE_OP(opcode) case opcode: +-#define HANDLE_OP_END() continue ++#define HANDLE_OP_END() \ ++ CHECK_INSTRUCTION_LIMIT(); \ ++ continue + + #endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + +@@ -1538,6 +1553,16 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + uint8 *maddr = NULL; + uint32 local_idx, local_offset, global_idx; + uint8 opcode = 0, local_type, *global_addr; ++ ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ int64 instructions_to_execute = -1; ++ int64 const *instructions_schedule = NULL; ++ if (exec_env) { ++ instructions_to_execute = exec_env->instructions_to_execute; ++ instructions_schedule = exec_env->instructions_schedule; ++ } ++#endif ++ + #if !defined(OS_ENABLE_HW_BOUND_CHECK) \ + || WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0 + #if WASM_CONFIGURABLE_BOUNDS_CHECKS != 0 +@@ -7761,6 +7786,11 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame *)prev_frame); + ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ if (exec_env) ++ exec_env->instructions_to_execute = instructions_to_execute; ++#endif ++ + if (!prev_frame->ip) + /* Called from native. */ + return; +@@ -7789,6 +7819,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, + + got_exception: + SYNC_ALL_TO_FRAME(); ++#if WASM_ENABLE_INSTRUCTION_METERING != 0 ++ if (exec_env) ++ exec_env->instructions_to_execute = instructions_to_execute; ++#endif + return; + + #if WASM_ENABLE_LABELS_AS_VALUES == 0 +diff --git a/core/shared/platform/include/platform_wasi_types.h b/core/shared/platform/include/platform_wasi_types.h +index ac1a95ea..e23b500e 100644 +--- a/core/shared/platform/include/platform_wasi_types.h ++++ b/core/shared/platform/include/platform_wasi_types.h +@@ -36,7 +36,11 @@ extern "C" { + #if WASM_ENABLE_UVWASI != 0 || WASM_ENABLE_LIBC_WASI == 0 + #define assert_wasi_layout(expr, message) /* nothing */ + #else +-#define assert_wasi_layout(expr, message) _Static_assert(expr, message) ++ #ifndef _MSC_VER ++ #define assert_wasi_layout(expr, message) _Static_assert(expr, message) ++ #else ++ #define assert_wasi_layout(expr, message) static_assert(expr, message) ++ #endif + #endif + + assert_wasi_layout(_Alignof(int8_t) == 1, "non-wasi data layout"); +diff --git a/doc/build_wamr.md b/doc/build_wamr.md +index 6425450b..94dd9628 100644 +--- a/doc/build_wamr.md ++++ b/doc/build_wamr.md +@@ -327,6 +327,10 @@ And the wasm app can calls below APIs to allocate/free memory from/to the shared + - **WAMR_BUILD_SHRUNK_MEMORY**=1/0, default to enable if not set + > Note: When enabled, this feature will reduce memory usage by decreasing the size of the linear memory, particularly when the `memory.grow` opcode is not used and memory usage is somewhat predictable. + ++## **Instruction metering** ++- **WAMR_BUILD_INSTRUCTION_METERING**=1/0, default to disable if not set ++> Note: Enabling this feature allows limiting the number of instructions a wasm module instance can execute. Use the `wasm_runtime_set_instruction_count_limit(...)` API before calling `wasm_runtime_call_*(...)` APIs to enforce this limit. ++ + ## **Combination of configurations:** + + We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: diff --git a/include/xrpl/protocol/FeeUnits.h b/include/xrpl/protocol/FeeUnits.h index c6949a434c..3ba546920a 100644 --- a/include/xrpl/protocol/FeeUnits.h +++ b/include/xrpl/protocol/FeeUnits.h @@ -451,8 +451,8 @@ mulDivU(Source1 value, Dest mul, Source2 div) } using namespace boost::multiprecision; - - uint128_t product; + using uint128 = boost::multiprecision::uint128_t; + uint128 product; product = multiply( product, static_cast(value.value()), diff --git a/include/xrpl/protocol/Fees.h b/include/xrpl/protocol/Fees.h index 4393f1a1d9..1a1ecacb36 100644 --- a/include/xrpl/protocol/Fees.h +++ b/include/xrpl/protocol/Fees.h @@ -24,6 +24,8 @@ namespace ripple { +constexpr std::uint32_t MICRO_DROPS_PER_DROP{1'000'000}; + /** Reflects the fee settings for a particular ledger. The fees are always the same for any transactions applied @@ -34,6 +36,10 @@ struct Fees XRPAmount base{0}; // Reference tx cost (drops) XRPAmount reserve{0}; // Reserve base (drops) XRPAmount increment{0}; // Reserve increment (drops) + std::uint32_t extensionComputeLimit{ + 0}; // Extension compute limit (instructions) + std::uint32_t extensionSizeLimit{0}; // Extension size limit (bytes) + std::uint32_t gasPrice{0}; // price of WASM gas (micro-drops) explicit Fees() = default; Fees(Fees const&) = default; diff --git a/include/xrpl/protocol/Indexes.h b/include/xrpl/protocol/Indexes.h index 57c8727ae6..09fa07efef 100644 --- a/include/xrpl/protocol/Indexes.h +++ b/include/xrpl/protocol/Indexes.h @@ -230,6 +230,12 @@ page(Keylet const& root, std::uint64_t index = 0) noexcept Keylet escrow(AccountID const& src, std::uint32_t seq) noexcept; +inline Keylet +escrow(uint256 const& key) noexcept +{ + return {ltESCROW, key}; +} + /** A PaymentChannel */ Keylet payChan(AccountID const& src, AccountID const& dst, std::uint32_t seq) noexcept; diff --git a/include/xrpl/protocol/MPTAmount.h b/include/xrpl/protocol/MPTAmount.h index 419450eeb9..7ece7cc3b5 100644 --- a/include/xrpl/protocol/MPTAmount.h +++ b/include/xrpl/protocol/MPTAmount.h @@ -149,11 +149,12 @@ mulRatio( bool roundUp) { using namespace boost::multiprecision; + using int128 = boost::multiprecision::int128_t; if (!den) Throw("division by zero"); - int128_t const amt128(amt.value()); + int128 const amt128(amt.value()); auto const neg = amt.value() < 0; auto const m = amt128 * num; auto r = m / den; diff --git a/include/xrpl/protocol/TER.h b/include/xrpl/protocol/TER.h index 4483d6251a..c443b022b5 100644 --- a/include/xrpl/protocol/TER.h +++ b/include/xrpl/protocol/TER.h @@ -185,6 +185,8 @@ enum TEFcodes : TERUnderlyingType { tefNO_TICKET, tefNFTOKEN_IS_NOT_TRANSFERABLE, tefINVALID_LEDGER_FIX_TYPE, + tefNO_WASM, + tefWASM_FIELD_NOT_INCLUDED, }; //------------------------------------------------------------------------------ @@ -360,6 +362,7 @@ enum TECcodes : TERUnderlyingType { tecWRONG_ASSET = 194, tecLIMIT_EXCEEDED = 195, tecPSEUDO_ACCOUNT = 196, + tecWASM_REJECTED = 197, }; //------------------------------------------------------------------------------ diff --git a/include/xrpl/protocol/XRPAmount.h b/include/xrpl/protocol/XRPAmount.h index 332735dc6f..b11b5c0e06 100644 --- a/include/xrpl/protocol/XRPAmount.h +++ b/include/xrpl/protocol/XRPAmount.h @@ -286,11 +286,12 @@ mulRatio( bool roundUp) { using namespace boost::multiprecision; + using int128 = boost::multiprecision::int128_t; if (!den) Throw("division by zero"); - int128_t const amt128(amt.drops()); + int128 const amt128(amt.drops()); auto const neg = amt.drops() < 0; auto const m = amt128 * num; auto r = m / den; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index e61d3a8005..424b4a4008 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,11 +32,11 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FEATURE(SmartEscrow, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(Batch, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(SingleAssetVault, Supported::no, VoteBehavior::DefaultNo) XRPL_FEATURE(PermissionDelegation, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (PayChanCancelAfter, Supported::yes, VoteBehavior::DefaultNo) -// Check flags in Credential transactions XRPL_FIX (InvalidTxFlags, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (FrozenLPTokenTransfer, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(DeepFreeze, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index a902b32026..11157e601c 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -316,6 +316,12 @@ LEDGER_ENTRY(ltFEE_SETTINGS, 0x0073, FeeSettings, fee, ({ {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + + // New fields + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, + {sfPreviousTxnID, soeOPTIONAL}, {sfPreviousTxnLgrSeq, soeOPTIONAL}, })) @@ -345,6 +351,8 @@ LEDGER_ENTRY(ltESCROW, 0x0075, Escrow, escrow, ({ {sfCondition, soeOPTIONAL}, {sfCancelAfter, soeOPTIONAL}, {sfFinishAfter, soeOPTIONAL}, + {sfFinishFunction, soeOPTIONAL}, + {sfData, soeOPTIONAL}, {sfSourceTag, soeOPTIONAL}, {sfDestinationTag, soeOPTIONAL}, {sfOwnerNode, soeREQUIRED}, diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index dbef597ea0..faeff4ebb2 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -114,6 +114,10 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48) TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) +TYPED_SFIELD(sfExtensionComputeLimit, UINT32, 53) +TYPED_SFIELD(sfExtensionSizeLimit, UINT32, 54) +TYPED_SFIELD(sfGasPrice, UINT32, 55) +TYPED_SFIELD(sfComputationAllowance, UINT32, 56) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) @@ -232,7 +236,7 @@ TYPED_SFIELD(sfBaseFeeDrops, AMOUNT, 22) TYPED_SFIELD(sfReserveBaseDrops, AMOUNT, 23) TYPED_SFIELD(sfReserveIncrementDrops, AMOUNT, 24) -// currency amount (AMM) +// currency amount (more) TYPED_SFIELD(sfLPTokenOut, AMOUNT, 25) TYPED_SFIELD(sfLPTokenIn, AMOUNT, 26) TYPED_SFIELD(sfEPrice, AMOUNT, 27) @@ -274,6 +278,7 @@ TYPED_SFIELD(sfAssetClass, VL, 28) TYPED_SFIELD(sfProvider, VL, 29) TYPED_SFIELD(sfMPTokenMetadata, VL, 30) TYPED_SFIELD(sfCredentialType, VL, 31) +TYPED_SFIELD(sfFinishFunction, VL, 32) // account (common) TYPED_SFIELD(sfAccount, ACCOUNT, 1) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 5d5faae505..e48c5eca52 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -44,10 +44,12 @@ TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({ {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED}, + {sfDestinationTag, soeOPTIONAL}, {sfCondition, soeOPTIONAL}, {sfCancelAfter, soeOPTIONAL}, {sfFinishAfter, soeOPTIONAL}, - {sfDestinationTag, soeOPTIONAL}, + {sfFinishFunction, soeOPTIONAL}, + {sfData, soeOPTIONAL}, })) /** This transaction type completes an existing escrow. */ @@ -57,6 +59,7 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({ {sfFulfillment, soeOPTIONAL}, {sfCondition, soeOPTIONAL}, {sfCredentialIDs, soeOPTIONAL}, + {sfComputationAllowance, soeOPTIONAL}, })) @@ -543,6 +546,9 @@ TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({ {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, })) /** This system-generated transaction type is used to update the network's negative UNL diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 9dff4cc4f3..064b009299 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -268,6 +268,9 @@ JSS(expected_date_UTC); // out: any (warnings) JSS(expected_ledger_size); // out: TxQ JSS(expiration); // out: AccountOffers, AccountChannels, // ValidatorList, amm_info +JSS(extension_compute); // out: NetworkOps +JSS(extension_size); // out: NetworkOps +JSS(gas_price); // out: NetworkOps JSS(fail_hard); // in: Sign, Submit JSS(failed); // out: InboundLedger JSS(feature); // in: Feature diff --git a/src/libxrpl/protocol/STParsedJSON.cpp b/src/libxrpl/protocol/STParsedJSON.cpp index bc9aad0a13..a5f163ba65 100644 --- a/src/libxrpl/protocol/STParsedJSON.cpp +++ b/src/libxrpl/protocol/STParsedJSON.cpp @@ -1052,8 +1052,7 @@ parseArray( Json::Value const objectFields(json[i][objectName]); std::stringstream ss; - ss << json_name << "." - << "[" << i << "]." << objectName; + ss << json_name << "." << "[" << i << "]." << objectName; auto ret = parseObject( ss.str(), objectFields, nameField, depth + 1, error); diff --git a/src/libxrpl/protocol/STValidation.cpp b/src/libxrpl/protocol/STValidation.cpp index 51246a1830..3a9d16445e 100644 --- a/src/libxrpl/protocol/STValidation.cpp +++ b/src/libxrpl/protocol/STValidation.cpp @@ -78,6 +78,10 @@ STValidation::validationFormat() {sfBaseFeeDrops, soeOPTIONAL}, {sfReserveBaseDrops, soeOPTIONAL}, {sfReserveIncrementDrops, soeOPTIONAL}, + // featureSmartEscrow + {sfExtensionComputeLimit, soeOPTIONAL}, + {sfExtensionSizeLimit, soeOPTIONAL}, + {sfGasPrice, soeOPTIONAL}, }; // clang-format on diff --git a/src/libxrpl/protocol/TER.cpp b/src/libxrpl/protocol/TER.cpp index 68125fab83..9e7a76e5c1 100644 --- a/src/libxrpl/protocol/TER.cpp +++ b/src/libxrpl/protocol/TER.cpp @@ -126,6 +126,7 @@ transResults() MAKE_ERROR(tecWRONG_ASSET, "Wrong asset given."), MAKE_ERROR(tecLIMIT_EXCEEDED, "Limit exceeded."), MAKE_ERROR(tecPSEUDO_ACCOUNT, "This operation is not allowed against a pseudo-account."), + MAKE_ERROR(tecWASM_REJECTED, "The custom WASM code that was run rejected your transaction."), MAKE_ERROR(tefALREADY, "The exact transaction was already in this ledger."), MAKE_ERROR(tefBAD_ADD_AUTH, "Not authorized to add account."), @@ -149,6 +150,8 @@ transResults() MAKE_ERROR(tefNO_TICKET, "Ticket is not in ledger."), MAKE_ERROR(tefNFTOKEN_IS_NOT_TRANSFERABLE, "The specified NFToken is not transferable."), MAKE_ERROR(tefINVALID_LEDGER_FIX_TYPE, "The LedgerFixType field has an invalid value."), + MAKE_ERROR(tefNO_WASM, "There is no WASM code to run, but a WASM-specific field was included."), + MAKE_ERROR(tefWASM_FIELD_NOT_INCLUDED, "WASM code requires a field to be included that was not included."), MAKE_ERROR(telLOCAL_ERROR, "Local failure."), MAKE_ERROR(telBAD_DOMAIN, "Domain too long."), diff --git a/src/test/app/Escrow_test.cpp b/src/test/app/Escrow_test.cpp index 1129019aab..c326f8c996 100644 --- a/src/test/app/Escrow_test.cpp +++ b/src/test/app/Escrow_test.cpp @@ -17,8 +17,10 @@ */ //============================================================================== +#include #include +#include #include #include @@ -28,6 +30,7 @@ #include #include +#include #include namespace ripple { @@ -1548,7 +1551,7 @@ struct Escrow_test : public beast::unit_test::suite Account const alice{"alice"}; Account const bob{"bob"}; Account const carol{"carol"}; - Account const dillon{"dillon "}; + Account const dillon{"dillon"}; Account const zelda{"zelda"}; char const credType[] = "abcde"; @@ -1685,6 +1688,581 @@ struct Escrow_test : public beast::unit_test::suite } } + void + testCreateFinishFunctionPreflight() + { + testcase("Test preflight checks involving FinishFunction"); + + using namespace jtx; + using namespace std::chrono; + + Account const alice{"alice"}; + Account const carol{"carol"}; + + // Tests whether the ledger index is >= 5 + // #[no_mangle] + // pub fn finish() -> bool { + // unsafe { host_lib::getLedgerSqn() >= 5} + // } + static auto wasmHex = ledgerSqnHex; + + { + // featureSmartEscrow disabled + Env env(*this, supported_amendments() - featureSmartEscrow); + env.fund(XRP(5000), alice, carol); + XRPAmount const txnFees = env.current()->fees().base + 1000; + auto escrowCreate = escrow(alice, carol, XRP(1000)); + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + fee(txnFees), + ter(temDISABLED)); + env.close(); + } + + { + // FinishFunction > max length + Env env(*this, envconfig([](std::unique_ptr cfg) { + cfg->FEES.extension_size_limit = 10; // 10 bytes + return cfg; + })); + XRPAmount const txnFees = env.current()->fees().base + 1000; + // create escrow + env.fund(XRP(5000), alice, carol); + + auto escrowCreate = escrow(alice, carol, XRP(500)); + + // 11-byte string + std::string longWasmHex = "00112233445566778899AA"; + env(escrowCreate, + finish_function(longWasmHex), + cancel_time(env.now() + 100s), + fee(txnFees), + ter(temMALFORMED)); + env.close(); + } + + Env env(*this, envconfig([](std::unique_ptr cfg) { + cfg->START_UP = Config::FRESH; + return cfg; + })); + XRPAmount const txnFees = env.current()->fees().base * 10 + 1000; + // create escrow + env.fund(XRP(5000), alice, carol); + + auto escrowCreate = escrow(alice, carol, XRP(500)); + + // Success situations + { + // FinishFunction + CancelAfter + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + fee(txnFees)); + env.close(); + } + { + // FinishFunction + Condition + CancelAfter + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + condition(cb1), + fee(txnFees)); + env.close(); + } + { + // FinishFunction + FinishAfter + CancelAfter + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + finish_time(env.now() + 2s), + fee(txnFees)); + env.close(); + } + { + // FinishFunction + FinishAfter + Condition + CancelAfter + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + condition(cb1), + finish_time(env.now() + 2s), + fee(txnFees)); + env.close(); + } + + // Failure situations (i.e. all other combinations) + { + // only FinishFunction + env(escrowCreate, + finish_function(wasmHex), + fee(txnFees), + ter(temBAD_EXPIRATION)); + env.close(); + } + { + // FinishFunction + FinishAfter + env(escrowCreate, + finish_function(wasmHex), + finish_time(env.now() + 2s), + fee(txnFees), + ter(temBAD_EXPIRATION)); + env.close(); + } + { + // FinishFunction + Condition + env(escrowCreate, + finish_function(wasmHex), + condition(cb1), + fee(txnFees), + ter(temBAD_EXPIRATION)); + env.close(); + } + { + // FinishFunction + FinishAfter + Condition + env(escrowCreate, + finish_function(wasmHex), + condition(cb1), + finish_time(env.now() + 2s), + fee(txnFees), + ter(temBAD_EXPIRATION)); + env.close(); + } + { + // FinishFunction 0 length + env(escrowCreate, + finish_function(""), + cancel_time(env.now() + 100s), + fee(txnFees), + ter(temMALFORMED)); + env.close(); + } + } + + void + testFinishWasmFailures() + { + testcase("EscrowFinish Smart Escrow failures"); + + using namespace jtx; + using namespace std::chrono; + + Account const alice{"alice"}; + Account const carol{"carol"}; + + // Tests whether the ledger index is >= 5 + // #[no_mangle] + // pub fn finish() -> bool { + // unsafe { host_lib::getLedgerSqn() >= 5} + // } + static auto wasmHex = ledgerSqnHex; + + { + // featureSmartEscrow disabled + Env env(*this, supported_amendments() - featureSmartEscrow); + env.fund(XRP(5000), alice, carol); + XRPAmount const txnFees = env.current()->fees().base + 1000; + env(finish(carol, alice, 1), + fee(txnFees), + comp_allowance(110), + ter(temDISABLED)); + env.close(); + } + + { + // ComputationAllowance > max compute limit + Env env(*this, envconfig([](std::unique_ptr cfg) { + cfg->FEES.extension_compute_limit = 1'000; // in gas + return cfg; + })); + env.fund(XRP(5000), alice, carol); + // Run past the flag ledger so that a Fee change vote occurs and + // updates FeeSettings. (It also activates all supported + // amendments.) + for (auto i = env.current()->seq(); i <= 257; ++i) + env.close(); + + auto const allowance = 1'001; + env(finish(carol, alice, 1), + fee(env.current()->fees().base + allowance), + comp_allowance(allowance), + ter(temBAD_LIMIT)); + } + + Env env(*this); + + // Run past the flag ledger so that a Fee change vote occurs and + // updates FeeSettings. (It also activates all supported + // amendments.) + for (auto i = env.current()->seq(); i <= 257; ++i) + env.close(); + + XRPAmount const txnFees = env.current()->fees().base + 1000; + env.fund(XRP(5000), alice, carol); + + // create escrow + auto const seq = env.seq(alice); + env(escrow(alice, carol, XRP(500)), + finish_function(wasmHex), + cancel_time(env.now() + 100s), + fee(txnFees)); + env.close(); + + { + // no ComputationAllowance field + env(finish(carol, alice, seq), ter(tefWASM_FIELD_NOT_INCLUDED)); + } + + { + // not enough fees + // This function takes 110 gas + // In testing, 1 gas costs 1 drop + auto const finishFee = env.current()->fees().base + 109; + env(finish(carol, alice, seq), + fee(finishFee), + comp_allowance(110), + ter(telINSUF_FEE_P)); + } + + { + // not enough gas + // This function takes 4 gas + // In testing, 1 gas costs 1 drop + auto const finishFee = env.current()->fees().base + 4; + env(finish(carol, alice, seq), + fee(finishFee), + comp_allowance(2), + ter(tecFAILED_PROCESSING)); + } + + { + // ComputationAllowance field included w/no FinishFunction on + // escrow + auto const seq2 = env.seq(alice); + env(escrow(alice, carol, XRP(500)), + finish_time(env.now() + 10s), + cancel_time(env.now() + 100s)); + env.close(); + + auto const allowance = 100; + env(finish(carol, alice, seq2), + fee(env.current()->fees().base + allowance), + comp_allowance(allowance), + ter(tefNO_WASM)); + } + } + + void + testFinishFunction() + { + testcase("Example escrow function"); + + using namespace jtx; + using namespace std::chrono; + + Account const alice{"alice"}; + Account const carol{"carol"}; + + // Tests whether the ledger index is >= 5 + // #[no_mangle] + // pub fn finish() -> bool { + // unsafe { host_lib::getLedgerSqn() >= 5} + // } + static auto wasmHex = ledgerSqnHex; + + { + // basic FinishFunction situation + Env env(*this); + // create escrow + env.fund(XRP(5000), alice, carol); + auto const seq = env.seq(alice); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + auto escrowCreate = escrow(alice, carol, XRP(1000)); + XRPAmount txnFees = env.current()->fees().base + 1000; + env(escrowCreate, + finish_function(wasmHex), + cancel_time(env.now() + 100s), + fee(txnFees)); + env.close(); + + if (BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 2)) + { + env.require(balance(alice, XRP(4000) - txnFees)); + env.require(balance(carol, XRP(5000))); + + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env(finish(alice, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env(finish(alice, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env.close(); + env(finish(alice, alice, seq), + fee(txnFees), + comp_allowance(110), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + } + } + + { + // FinishFunction + Condition + Env env(*this); + env.fund(XRP(5000), alice, carol); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + auto const seq = env.seq(alice); + // create escrow + auto escrowCreate = escrow(alice, carol, XRP(1000)); + XRPAmount const createFee = env.current()->fees().base + 1000; + env(escrowCreate, + finish_function(wasmHex), + condition(cb1), + cancel_time(env.now() + 100s), + fee(createFee)); + env.close(); + + if (BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 2)) + { + env.require(balance(alice, XRP(4000) - createFee)); + env.require(balance(carol, XRP(5000))); + + XRPAmount const txnFees = + env.current()->fees().base * 34 + 1000; + + // no fulfillment provided, function fails + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecCRYPTOCONDITION_ERROR)); + // fulfillment provided, function fails + env(finish(carol, alice, seq), + condition(cb1), + fulfillment(fb1), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env.close(); + // no fulfillment provided, function succeeds + env(finish(alice, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecCRYPTOCONDITION_ERROR)); + // wrong fulfillment provided, function succeeds + env(finish(alice, alice, seq), + condition(cb1), + fulfillment(fb2), + comp_allowance(110), + fee(txnFees), + ter(tecCRYPTOCONDITION_ERROR)); + // fulfillment provided, function succeeds, tx succeeds + env(finish(alice, alice, seq), + condition(cb1), + fulfillment(fb1), + comp_allowance(110), + fee(txnFees), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + } + } + + { + // FinishFunction + FinishAfter + Env env(*this); + // create escrow + env.fund(XRP(5000), alice, carol); + auto const seq = env.seq(alice); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + auto escrowCreate = escrow(alice, carol, XRP(1000)); + XRPAmount txnFees = env.current()->fees().base + 1000; + auto const ts = env.now() + 97s; + env(escrowCreate, + finish_function(wasmHex), + finish_time(ts), + cancel_time(env.now() + 1000s), + fee(txnFees)); + env.close(); + + if (BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 2)) + { + env.require(balance(alice, XRP(4000) - txnFees)); + env.require(balance(carol, XRP(5000))); + + // finish time hasn't passed, function fails + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees + 1), + ter(tecNO_PERMISSION)); + env.close(); + // finish time hasn't passed, function succeeds + for (; env.now() < ts; env.close()) + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees + 2), + ter(tecNO_PERMISSION)); + + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees + 1), + ter(tesSUCCESS)); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + } + } + + { + // FinishFunction + FinishAfter #2 + Env env(*this); + // create escrow + env.fund(XRP(5000), alice, carol); + auto const seq = env.seq(alice); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + auto escrowCreate = escrow(alice, carol, XRP(1000)); + XRPAmount txnFees = env.current()->fees().base + 1000; + env(escrowCreate, + finish_function(wasmHex), + finish_time(env.now() + 2s), + cancel_time(env.now() + 100s), + fee(txnFees)); + // Don't close the ledger here + + if (BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 2)) + { + env.require(balance(alice, XRP(4000) - txnFees)); + env.require(balance(carol, XRP(5000))); + + // finish time hasn't passed, function fails + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecNO_PERMISSION)); + env.close(); + + // finish time has passed, function fails + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tecWASM_REJECTED)); + env.close(); + // finish time has passed, function succeeds, tx succeeds + env(finish(carol, alice, seq), + comp_allowance(110), + fee(txnFees), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + } + } + } + + void + testAllHostFunctions() + { + testcase("Test all host functions"); + + using namespace jtx; + using namespace std::chrono; + + // TODO: figure out how to make this a fixture in a separate file + static auto wasmHex = allHostFunctionsHex; + // let sender = get_tx_account_id(); + // let owner = get_current_escrow_account_id(); + // let dest = get_current_escrow_destination(); + // let dest_balance = get_account_balance(dest); + // let escrow_data = get_current_escrow_data(); + // let ed_str = String::from_utf8(escrow_data).unwrap(); + // let threshold_balance = ed_str.parse::().unwrap(); + // let pl_time = host_lib::getParentLedgerTime(); + // let e_time = get_current_escrow_finish_after(); + // sender == owner && dest_balance <= threshold_balance && + // pl_time >= e_time + + Account const alice{"alice"}; + Account const carol{"carol"}; + + { + // basic FinishFunction situation + Env env(*this); + // create escrow + env.fund(XRP(5000), alice, carol); + auto const seq = env.seq(alice); + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + auto escrowCreate = escrow(alice, carol, XRP(1000)); + XRPAmount txnFees = env.current()->fees().base + 1000; + env(escrowCreate, + finish_function(wasmHex), + finish_time(env.now() + 11s), + cancel_time(env.now() + 100s), + data("1000000000"), // 1000 XRP in drops + fee(txnFees)); + env.close(); + + if (BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 2)) + { + env.require(balance(alice, XRP(4000) - txnFees)); + env.require(balance(carol, XRP(5000))); + + auto const allowance = 40'000; + + // FinishAfter time hasn't passed + env(finish(carol, alice, seq), + comp_allowance(allowance), + fee(txnFees), + ter(tecNO_PERMISSION)); + env.close(); + + // tx sender not escrow creator (alice) + env(finish(carol, alice, seq), + comp_allowance(allowance), + fee(txnFees), + ter(tecWASM_REJECTED)); + + // destination balance is too high + env(finish(carol, alice, seq), + comp_allowance(allowance), + fee(txnFees), + ter(tecWASM_REJECTED)); + + env.close(); + + // reduce the destination balance + env(pay(carol, alice, XRP(4500))); + env.close(); + + // tx sender not escrow creator (alice) + env(finish(carol, alice, seq), + comp_allowance(allowance), + fee(txnFees), + ter(tecWASM_REJECTED)); + env.close(); + + env(finish(alice, alice, seq), + comp_allowance(allowance), + fee(txnFees), + ter(tesSUCCESS)); + env.close(); + + BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0); + } + } + } + void run() override { @@ -1700,6 +2278,10 @@ struct Escrow_test : public beast::unit_test::suite testConsequences(); testEscrowWithTickets(); testCredentials(); + testCreateFinishFunctionPreflight(); + testFinishWasmFailures(); + testFinishFunction(); + testAllHostFunctions(); } }; diff --git a/src/test/app/FeeVote_test.cpp b/src/test/app/FeeVote_test.cpp index 1cf2e67f83..43bad71af5 100644 --- a/src/test/app/FeeVote_test.cpp +++ b/src/test/app/FeeVote_test.cpp @@ -37,36 +37,62 @@ class FeeVote_test : public beast::unit_test::suite BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } { Section config; config.append( {"reference_fee = 50", "account_reserve = 1234567", - "owner_reserve = 1234"}); + "owner_reserve = 1234", + "extension_compute_limit = 100", + "extension_size_limit = 200", + " gas_price = 300"}); auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == 50); BEAST_EXPECT(setup.account_reserve == 1234567); BEAST_EXPECT(setup.owner_reserve == 1234); + BEAST_EXPECT(setup.extension_compute_limit == 100); + BEAST_EXPECT(setup.extension_size_limit == 200); + BEAST_EXPECT(setup.gas_price == 300); } { Section config; config.append( {"reference_fee = blah", "account_reserve = yada", - "owner_reserve = foo"}); + "owner_reserve = foo", + "extension_compute_limit = bar", + "extension_size_limit = baz", + "gas_price = qux"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } { Section config; config.append( {"reference_fee = -50", "account_reserve = -1234567", - "owner_reserve = -1234"}); + "owner_reserve = -1234", + "extension_compute_limit = -100", + "extension_size_limit = -200", + "gas_price = -300"}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); @@ -74,6 +100,12 @@ class FeeVote_test : public beast::unit_test::suite setup.account_reserve == static_cast(-1234567)); BEAST_EXPECT( setup.owner_reserve == static_cast(-1234)); + BEAST_EXPECT( + setup.extension_compute_limit == + static_cast(-100)); + BEAST_EXPECT( + setup.extension_size_limit == static_cast(-200)); + BEAST_EXPECT(setup.gas_price == static_cast(-300)); } { auto const big64 = std::to_string( @@ -84,12 +116,22 @@ class FeeVote_test : public beast::unit_test::suite config.append( {"reference_fee = " + big64, "account_reserve = " + big64, - "owner_reserve = " + big64}); + "owner_reserve = " + big64, + "extension_compute_limit = " + big64, + "extension_size_limit = " + big64, + "gas_price = " + big64}); // Illegal values are ignored, and the defaults left unchanged auto setup = setup_FeeVote(config); BEAST_EXPECT(setup.reference_fee == defaultSetup.reference_fee); BEAST_EXPECT(setup.account_reserve == defaultSetup.account_reserve); BEAST_EXPECT(setup.owner_reserve == defaultSetup.owner_reserve); + BEAST_EXPECT( + setup.extension_compute_limit == + defaultSetup.extension_compute_limit); + BEAST_EXPECT( + setup.extension_size_limit == + defaultSetup.extension_size_limit); + BEAST_EXPECT(setup.gas_price == defaultSetup.gas_price); } } diff --git a/src/test/app/PseudoTx_test.cpp b/src/test/app/PseudoTx_test.cpp index d96828a50b..0ba7b0e5ba 100644 --- a/src/test/app/PseudoTx_test.cpp +++ b/src/test/app/PseudoTx_test.cpp @@ -50,6 +50,12 @@ struct PseudoTx_test : public beast::unit_test::suite obj[sfReserveIncrement] = 0; obj[sfReferenceFeeUnits] = 0; } + if (rules.enabled(featureSmartEscrow)) + { + obj[sfExtensionComputeLimit] = 0; + obj[sfExtensionSizeLimit] = 0; + obj[sfGasPrice] = 0; + } })); res.emplace_back(STTx(ttAMENDMENT, [&](auto& obj) { @@ -116,9 +122,10 @@ struct PseudoTx_test : public beast::unit_test::suite { using namespace test::jtx; FeatureBitset const all{supported_amendments()}; - FeatureBitset const xrpFees{featureXRPFees}; + testPrevented(all - featureXRPFees - featureSmartEscrow); testPrevented(all - featureXRPFees); + testPrevented(all - featureSmartEscrow); testPrevented(all); testAllowed(); } diff --git a/src/test/app/Wasm_test.cpp b/src/test/app/Wasm_test.cpp new file mode 100644 index 0000000000..16e7f67d99 --- /dev/null +++ b/src/test/app/Wasm_test.cpp @@ -0,0 +1,337 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 +#include + +#include + +#include + +#include + +namespace ripple { +namespace test { + +/* Host function body definition. */ +using Add_proto = int32_t(int32_t, int32_t); +wasm_trap_t* +Add(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + int32_t Val1 = params->data[0].of.i32; + int32_t Val2 = params->data[1].of.i32; + // printf("Host function \"Add\": %d + %d\n", Val1, Val2); + results->data[0] = WASM_I32_VAL(Val1 + Val2); + return nullptr; +} + +struct Wasm_test : public beast::unit_test::suite +{ + void + testWasmtimeLib() + { + testcase("wasmtime lib test"); + // clang-format off + /* The WASM module buffer. */ + wbytes const wasm = {/* WASM header */ + 0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, + /* Type section */ + 0x01, 0x07, 0x01, + /* function type {i32, i32} -> {i32} */ + 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, + /* Import section */ + 0x02, 0x13, 0x01, + /* module name: "extern" */ + 0x06, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6E, + /* extern name: "func-add" */ + 0x08, 0x66, 0x75, 0x6E, 0x63, 0x2D, 0x61, 0x64, 0x64, + /* import desc: func 0 */ + 0x00, 0x00, + /* Function section */ + 0x03, 0x02, 0x01, 0x00, + /* Export section */ + 0x07, 0x0A, 0x01, + /* export name: "addTwo" */ + 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6F, + /* export desc: func 0 */ + 0x00, 0x01, + /* Code section */ + 0x0A, 0x0A, 0x01, + /* code body */ + 0x08, 0x00, 0x20, 0x00, 0x20, 0x01, 0x10, 0x00, 0x0B}; + // clang-format on + auto& vm = WasmEngine::instance(); + + std::vector imports; + WasmImpFunc( + imports, "func-add", reinterpret_cast(&Add)); + + auto res = vm.run(wasm, "addTwo", imports, wasmParams(1234, 5678)); + + // if (res) printf("invokeAdd get the result: %d\n", res.value()); + + BEAST_EXPECT(res.has_value() && res.value() == 6912); + } + + void + testBadWasm() + { + testcase("bad wasm test"); + + HostFunctions hfs; + auto wasmHex = "00000000"; + auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + std::string funcName("mock_escrow"); + auto re = runEscrowWasm(wasm, funcName, &hfs, 15); + BEAST_EXPECT(re.error()); + } + + void + testEscrowWasmDN1() + { + testcase("escrow wasm devnet 1 test"); + auto wasmHex = allHostFunctionsHex; + + auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + + // let sender = get_tx_account_id(); + // let owner = get_current_escrow_account_id(); + // let dest = get_current_escrow_destination(); + // let dest_balance = get_account_balance(dest); + // let escrow_data = get_current_escrow_data(); + // let ed_str = String::from_utf8(escrow_data).unwrap(); + // let threshold_balance = ed_str.parse::().unwrap(); + // let pl_time = host_lib::getParentLedgerTime(); + // let e_time = get_current_escrow_finish_after(); + // sender == owner && dest_balance <= threshold_balance && + // pl_time >= e_time + + using namespace test::jtx; + struct TestHostFunctions : public HostFunctions + { + Env* env_; + Bytes accountID_; + Bytes data_; + int clock_drift_ = 0; + test::StreamSink sink_; + beast::Journal jlog_; + + public: + explicit TestHostFunctions(Env* env, int cd = 0) + : env_(env) + , clock_drift_(cd) + , sink_(beast::severities::kTrace) + , jlog_(sink_) + { + std::string s = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + accountID_ = Bytes{s.begin(), s.end()}; + std::string t = "10000"; + data_ = Bytes{t.begin(), t.end()}; + } + + test::StreamSink& + getSink() + { + return sink_; + } + + beast::Journal + getJournal() override + { + return jlog_; + } + + int32_t + getLedgerSqn() override + { + return (int32_t)env_->current()->seq(); + } + + int32_t + getParentLedgerTime() override + { + return env_->current() + ->parentCloseTime() + .time_since_epoch() + .count() + + clock_drift_; + } + + std::optional + getTxField(std::string const& fname) override + { + return accountID_; + } + + std::optional + getLedgerEntryField( + int32_t type, + Bytes const& kdata, + std::string const& fname) override + { + return data_; + } + + std::optional + getCurrentLedgerEntryField(std::string const& fname) override + { + if (fname == "Destination" || fname == "Account") + return accountID_; + else if (fname == "Data") + return data_; + else if (fname == "FinishAfter") + { + auto t = env_->current() + ->parentCloseTime() + .time_since_epoch() + .count(); + std::string s = std::to_string(t); + return Bytes{s.begin(), s.end()}; + } + + return std::nullopt; + } + }; + + Env env{*this}; + + { + TestHostFunctions nfs(&env, 0); + std::string funcName("finish"); + auto re = runEscrowWasm(wasm, funcName, &nfs, 100000); + if (BEAST_EXPECT(re.has_value())) + { + BEAST_EXPECT(re.value().result); + // std::cout << "good case result " << re.value().result + // << " cost: " << re.value().cost << std::endl; + } + } + + env.close(); + env.close(); + env.close(); + env.close(); + + { // fail because current time < escrow_finish_after time + TestHostFunctions nfs(&env, -1); + std::string funcName("finish"); + auto re = runEscrowWasm(wasm, funcName, &nfs, 100000); + if (BEAST_EXPECT(re.has_value())) + { + BEAST_EXPECT(!re.value().result); + // std::cout << "bad case (current time < escrow_finish_after " + // "time) result " + // << re.value().result << " cost: " << + // re.value().cost + // << std::endl; + } + } + + { // fail because trying to access nonexistent field + struct BadTestHostFunctions : public TestHostFunctions + { + explicit BadTestHostFunctions(Env* env) : TestHostFunctions(env) + { + } + std::optional + getTxField(std::string const& fname) override + { + return std::nullopt; + } + }; + BadTestHostFunctions nfs(&env); + std::string funcName("finish"); + auto re = runEscrowWasm(wasm, funcName, &nfs, 100000); + BEAST_EXPECT(re.error()); + // std::cout << "bad case (access nonexistent field) result " + // << re.error() << std::endl; + } + + { // fail because trying to allocate more than MAX_PAGES memory + struct BadTestHostFunctions : public TestHostFunctions + { + explicit BadTestHostFunctions(Env* env) : TestHostFunctions(env) + { + } + std::optional + getTxField(std::string const& fname) override + { + return Bytes((MAX_PAGES + 1) * 64 * 1024, 1); + } + }; + BadTestHostFunctions nfs(&env); + std::string funcName("finish"); + auto re = runEscrowWasm(wasm, funcName, &nfs, 100000); + BEAST_EXPECT(!re); + // std::cout << "bad case (more than MAX_PAGES) result " + // << re.error() << std::endl; + } + + { // fail because recursion too deep + auto wasmHex = deepRecursionHex; + auto wasmStr = boost::algorithm::unhex(std::string(wasmHex)); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + + TestHostFunctions nfs(&env); + std::string funcName("recursive"); + auto re = runEscrowWasm(wasm, funcName, &nfs, 1000'000'000); + BEAST_EXPECT(re.error()); + // std::cout << "bad case (deep recursion) result " << re.error() + // << std::endl; + + auto const& sink = nfs.getSink(); + auto countSubstr = [](std::string const& str, + std::string const& substr) { + std::size_t pos = 0; + int occurrences = 0; + while ((pos = str.find(substr, pos)) != std::string::npos) + { + occurrences++; + pos += substr.length(); + } + return occurrences; + }; + + BEAST_EXPECT( + countSubstr( + sink.messages().str(), "WAMR error: failed to call func") == + 1); + BEAST_EXPECT( + countSubstr( + sink.messages().str(), + "WAMR trap: Exception: wasm operand stack overflow") == 1); + } + } + + void + run() override + { + using namespace test::jtx; + testWasmtimeLib(); + testBadWasm(); + testEscrowWasmDN1(); + } +}; + +BEAST_DEFINE_TESTSUITE(Wasm, app, ripple); + +} // namespace test +} // namespace ripple diff --git a/src/test/app/wasm_fixtures/fixtures.cpp b/src/test/app/wasm_fixtures/fixtures.cpp new file mode 100644 index 0000000000..1ed246f252 --- /dev/null +++ b/src/test/app/wasm_fixtures/fixtures.cpp @@ -0,0 +1,1571 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 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. +*/ +//============================================================================== + +// TODO: consider moving these to separate files (and figure out the build) + +#include + +std::string const ledgerSqnHex = + "0061736d010000000105016000017f02190108686f73745f6c69620c6765" + "744c656467657253716e00000302010005030100100611027f00418080c0" + "000b7f00418080c0000b072e04066d656d6f727902000666696e69736800" + "010a5f5f646174615f656e6403000b5f5f686561705f6261736503010a09" + "010700100041044a0b004d0970726f64756365727302086c616e67756167" + "65010452757374000c70726f6365737365642d6279010572757374631d31" + "2e38352e31202834656231363132353020323032352d30332d3135290049" + "0f7461726765745f6665617475726573042b0f6d757461626c652d676c6f" + "62616c732b087369676e2d6578742b0f7265666572656e63652d74797065" + "732b0a6d756c746976616c7565"; + +std::string const allHostFunctionsHex = + "0061736d0100000001540c60037f7f7f017f60027f7f017f60027f7f0060" + "017f0060057f7f7f7f7f017f6000017f60017f017f60037f7f7f0060047f" + "7f7f7f00600b7f7f7f7f7f7f7f7f7f7f7f017f60067f7f7f7f7f7f017f60" + "027f7e00027b0408686f73745f6c69620a67657454784669656c64000108" + "686f73745f6c69621a67657443757272656e744c6564676572456e747279" + "4669656c64000108686f73745f6c6962136765744c6564676572456e7472" + "794669656c64000408686f73745f6c696213676574506172656e744c6564" + "67657254696d65000503363501010101090a000302010101010506020207" + "020201040001010100010400010101020701080101030800010103020202" + "060102000b04050170011c1c05030100110619037f01418080c0000b7f00" + "41b2a0c0000b7f0041c0a0c0000b074606066d656d6f727902000666696e" + "697368001108616c6c6f6361746500340a6465616c6c6f6361746500360a" + "5f5f646174615f656e6403010b5f5f686561705f62617365030209210100" + "41010b1b07040e06101b1c181d1f242a292531350b0d0f212223272b2d2e" + "2f0ac99e0135d70201057f230041406a220224000240200028020022042d" + "0000450440200128021c41d480c0004104200128022028020c1100002100" + "0c010b41012100200128021c220341d880c0004104200128022022062802" + "0c22051100000d00200441016a2104024020012d00144104714504402003" + "418d83c000410120051100000d022004200110050d02200128021c210320" + "0128022028020c21050c010b2003418e83c000410220051100000d012002" + "41013a0017200241206a200141086a290200370300200241286a20014110" + "6a290200370300200241306a200141186a2802003602002002200636020c" + "20022003360208200241e482c00036023820022001290200370318200220" + "0241176a3602102002200241086a3602342004200241186a10050d012002" + "280234418883c0004102200228023828020c1100000d010b2003419891c0" + "004101200511000021000b200241406b240020000bff0201037f23004180" + "016b22032400027f0240200128021422024110714504402002412071450d" + "0120002d0000210241810121000340200020036a41026b2002410f712204" + "413072200441376a2004410a491b3a000020022204410476210220004101" + "6b21002004410f4b0d000b20014101419183c0004102200020036a41016b" + "41810120006b10090c020b20002d0000210241810121000340200020036a" + "41026b2002410f712204413072200441d7006a2004410a491b3a00002002" + "22044104762102200041016b21002004410f4b0d000b20014101419183c0" + "004102200020036a41016b41810120006b10090c010b0240024002402000" + "2d0000220241e4004f044020032002200241e4006e2202419c7f6c6a41ff" + "0171410174419383c0006a2f00003b0001410021000c010b410221002002" + "410a4f0d010b200020036a20024130723a00000c010b4101210020032002" + "410174419383c0006a2f00003b00010b2001410141014100200020036a20" + "0041037310090b20034180016a24000b4701017f230041106b2202240020" + "022000280200220041046a36020c200141a080c000410941a980c000410b" + "2000410141b480c00041092002410c6a41021008200241106a24000bff01" + "01037f23004180016b22042400027f024020012802142202411071450440" + "20024120710d01200028020041012001100a0c020b200028020021004181" + "0121020340200220046a41026b2000410f712203413072200341d7006a20" + "03410a491b3a0000200241016b21022000410f4b200041047621000d000b" + "20014101419183c0004102200220046a41016b41810120026b10090c010b" + "2000280200210041810121020340200220046a41026b2000410f71220341" + "3072200341376a2003410a491b3a0000200241016b21022000410f4b2000" + "41047621000d000b20014101419183c0004102200220046a41016b418101" + "20026b10090b20044180016a24000bc50101017f230041106b220b240020" + "0028021c20012002200028022028020c1100002101200b41003a000d200b" + "20013a000c200b2000360208200b41086a20032004200520061020200720" + "082009200a10202101200b2d000d2202200b2d000c220372210002402003" + "4101712002410147720d00200128020022002d0014410471450440200028" + "021c418b83c0004102200028022028020c11000021000c010b200028021c" + "418a83c0004101200028022028020c11000021000b200b41106a24002000" + "4101710bef0401077f027f200145044020002802142106412d2109200541" + "016a0c010b412b418080c4002000280214220641017122011b2109200120" + "056a0b210702402006410471450440410021020c010b024020034504400c" + "010b2003410371220a450d00200221010340200820012c000041bf7f4a6a" + "2108200141016a2101200a41016b220a0d000b0b200720086a21070b2000" + "280200450440200028021c22012000280220220020092002200310190440" + "41010f0b200120042005200028020c1100000f0b02400240024020072000" + "28020422084f0440200028021c2201200028022022002009200220031019" + "450d0141010f0b2006410871450d012000280210210b2000413036021020" + "002d0018210c41012101200041013a0018200028021c2206200028022022" + "0a20092002200310190d02200820076b41016a210102400340200141016b" + "2201450d0120064130200a280210110100450d000b41010f0b2006200420" + "05200a28020c110000044041010f0b2000200c3a00182000200b36021041" + "000f0b200120042005200028020c11000021010c010b200820076b210602" + "4002400240410120002d0018220120014103461b220141016b0e02000102" + "0b20062101410021060c010b20064101762101200641016a41017621060b" + "200141016a21012000280210210820002802202107200028021c21000240" + "0340200141016b2201450d01200020082007280210110100450d000b4101" + "0f0b410121012000200720092002200310190d0020002004200520072802" + "0c1100000d004100210103402001200646044041000f0b200141016a2101" + "200020082007280210110100450d000b200141016b2006490f0b20010bbd" + "0201067f230041106b22042400410a2103024020004190ce004904402000" + "21050c010b0340200441066a20036a220641046b20004190ce006e220541" + "f0b1036c20006a220741ffff037141e4006e2208410174419383c0006a2f" + "00003b0000200641026b2008419c7f6c20076a41ffff0371410174419383" + "c0006a2f00003b0000200341046b2103200041ffc1d72f4b200521000d00" + "0b0b0240200541e3004d0440200521000c010b200341026b220320044106" + "6a6a200541ffff037141e4006e2200419c7f6c20056a41ffff0371410174" + "419383c0006a2f00003b00000b02402000410a4f0440200341026b220320" + "0441066a6a2000410174419383c0006a2f00003b00000c010b200341016b" + "2203200441066a6a20004130723a00000b2002200141014100200441066a" + "20036a410a20036b1009200441106a24000b1701017f2000280200220104" + "4020002802042001100c0b0bce0601047f0240200041046b280200220422" + "02417871220341044108200241037122021b20016a4f0440200241002003" + "200141276a4b1b0d01200041086b22012004220341787122006a21020240" + "024020034101710d002003410271450d012001280200220320006a210020" + "0120036b22014190a0c00028020046044020022802044103714103470d01" + "4188a0c000200036020020022002280204417e7136020420012000410172" + "360204200220003602000c020b2001200310330b02400240024002400240" + "2002280204220341027145044020024194a0c000280200460d0220024190" + "a0c000280200460d0320022003417871220210332001200020026a220041" + "0172360204200020016a200036020020014190a0c000280200470d014188" + "a0c00020003602000c060b20022003417e71360204200120004101723602" + "04200020016a20003602000b2000418002490d0220012000103241002101" + "41a8a0c00041a8a0c00028020041016b220036020020000d0441f09dc000" + "280200220004400340200141016a2101200028020822000d000b0b41a8a0" + "c00041ff1f2001200141ff1f4d1b3602000c040b4194a0c0002001360200" + "418ca0c000418ca0c00028020020006a2200360200200120004101723602" + "044190a0c00028020020014604404188a0c00041003602004190a0c00041" + "003602000b200041a0a0c00028020022034d0d034194a0c0002802002202" + "450d0341002100418ca0c00028020022044129490d0241e89dc000210103" + "402002200128020022054f04402002200520012802046a490d040b200128" + "020821010c000b000b4190a0c00020013602004188a0c0004188a0c00028" + "020020006a220036020020012000410172360204200020016a2000360200" + "0c020b200041f8017141f89dc0006a2102027f4180a0c000280200220341" + "012000410376742200714504404180a0c000200020037236020020020c01" + "0b20022802080b2100200220013602082000200136020c2001200236020c" + "200120003602080c010b41f09dc000280200220104400340200041016a21" + "00200128020822010d000b0b41a8a0c00041ff1f2000200041ff1f4d1b36" + "0200200320044f0d0041a0a0c000417f3602000b0f0b41dd88c000418c89" + "c0001016000b419c89c00041cc89c0001016000b4201017f230041106b22" + "02240020022000410c6a36020c200141bd80c000410d41ca80c000410520" + "00410341cf80c00041052002410c6a41041008200241106a24000b8e0301" + "087f230041406a2202240020002802082105200028020421034101210620" + "0128021c41cc81c0004101200128022028020c1100002100200504400340" + "20072108410121072000410171210441012100024020040d00024020012d" + "00144104714504402008410171450d01200128021c418383c00041022001" + "28022028020c110000450d010c020b20012802202104200128021c210920" + "08410171450440200941d59ac0004101200428020c1100000d020b200241" + "013a0017200241206a200141086a290200370300200241286a200141106a" + "290200370300200241306a200141186a2802003602002002200436020c20" + "022009360208200241e482c0003602382002200129020037031820022002" + "41176a3602102002200241086a3602342003200241186a10054504402002" + "280234418883c0004102200228023828020c11000021000c020b0c010b20" + "032001100521000b200341016a2103200541016b22050d000b0b20004504" + "40200128021c419083c0004101200128022028020c11000021060b200241" + "406b240020060bdb0101047f230041106b220224002002200036020c2300" + "41106b22002400200128021c41f899c000410d200128022028020c110000" + "2103200041003a000d200020033a000c20002001360208200041086a4185" + "9ac00041042002410c6a41051020210320002d000d220420002d000c2205" + "722101024020054101712004410147720d00200328020022012d00144104" + "71450440200128021c418b83c0004102200128022028020c11000021010c" + "010b200128021c418a83c0004101200128022028020c11000021010b2000" + "41106a24002001410171200241106a24000b3300200128021c2000280200" + "2d00004102742200418c9cc0006a280200200041f89bc0006a2802002001" + "28022028020c1100000bf813020e7f037e230041f0006b2203240041b1a0" + "c0002d00001a024002400240024002404107101222000440200041036a41" + "879bc000280000360000200041849bc000280000360000200341286a2000" + "41071000101320004107100c41b1a0c0002d00001a410710122200450d05" + "200041036a41879bc000280000360000200041849bc00028000036000020" + "0341346a200041071001101320004107100c41b1a0c0002d00001a410b10" + "122200450d05200041076a41929bc0002800003600002000418b9bc00029" + "0000370000200341406b2000410b100110132000410b100c41b1a0c0002d" + "00001a200328024821012003280244210b410710122204450d0541002100" + "200441036a41b79bc000280000360000200441b49bc00028000036000020" + "0341d8006a41e100200b20012004410710021013200328025c2105024002" + "400240200328026022020e020700010b41012100200522012d0000412b6b" + "0e03060106010b20052d0000412b460440200241016b2100200541016a21" + "0120024112490d010c030b200521012002220041114f0d020b034020012d" + "000041306b220241094b0d04200141016a21012002ad2010420a7e7c2110" + "200041016b22000d000b0c020b0c040b2000210203402002450d01200341" + "186a2010103820012d000041306b220941094b0d02410221002003290320" + "4200520d03200141016a2101200241016b21022003290318220e2009ad7c" + "2210200e5a0d000b0c020b20032802582200044020052000100c0b200441" + "07100c41b1a0c0002d00001a024002404104101222000440200041c4c2d1" + "8b06360000200341cc006a200041041001101320004104100c027f024020" + "03280254220541004e047f2003280250210c20054504404101210141000c" + "030b41b1a0c0002d00001a2005101222010d0141010541000b41e899c000" + "1014000b20050b21092001200c2005103721002005450d01200541076b22" + "014100200120054d1b2106200041036a417c7120006b2108410021010340" + "024002400240200020016a2d00002202c0220741004e0440200820016b41" + "03710d01200120064f0d020340200020016a220228020420022802007241" + "8081828478710d03200141086a22012006490d000b0c020b428080808080" + "20210e428080808010210f024002400240027e0240024002400240024002" + "4002400240200241a485c0006a2d000041026b0e030003010b0b20014101" + "6a22022005490d014200210e0c090b4200210e200141016a22042005490d" + "020c080b200020026a2c000041bf7f4a0d080c060b4200210e200141016a" + "220420054f0d06200020046a2c0000210402400240200241e00147044020" + "0241ed01460d012007411f6a41ff0171410c490d022007417e71416e470d" + "0420044140480d050c040b200441607141a07f460d040c030b2004419f7f" + "4a0d020c030b20044140480d020c010b200020046a2c0000210402400240" + "02400240200241f0016b0e050100000002000b2007410f6a41ff01714102" + "4b200441404e720d030c020b200441f0006a41ff017141304f0d020c010b" + "2004418f7f4a0d010b200141026a220220054f0d05200020026a2c000041" + "bf7f4a0d024200210f200141036a220220054f0d06200020026a2c000041" + "bf7f4c0d04428080808080e0000c030b428080808080200c020b4200210f" + "200141026a220220054f0d04200020026a2c000041bf7f4c0d020b428080" + "808080c0000b210e428080808010210f0c020b200241016a21010c040b42" + "00210f0b2001ad200e200f8484210e200941808080807846044020002109" + "0c070b2003200e3702642003200936025820032000ad2005ad4220868437" + "025c200341d8006a418080c00041e880c0001015000b200141016a21010c" + "010b200120054f0d000340200020016a2c00004100480d01200520014101" + "6a2201470d000b0c030b20012005490d000b0c010b0c040b2005ad210e20" + "0021050b410021000240024002400240024002400240200ea722020e0206" + "00010b41012100200522012d0000412b6b0e03050105010b20052d000041" + "2b460440200241016b2100200541016a210120024112490d010c020b2005" + "21012002220041114f0d010b4200210e034020012d000041306b22024109" + "4b0d03200141016a21012002ad200e420a7e7c210e200041016b22000d00" + "0b0c010b4200210e2000210203402002450d01200341086a200e10382001" + "2d000041306b220441094b0d024102210020032903104200520d03200141" + "016a2101200241016b21022003290308220f2004ad7c220e200f5a0d000b" + "0c020b410021071003210d41b1a0c0002d00001a02400240024002400240" + "024002400240410b101222080440200841076a419d9bc000280000360000" + "200841969bc000290000370000200341d8006a2008410b10011013200328" + "025c2104024002400240200328026022020e020a00010b4101210720042d" + "00002201412b6b0e03090109010b20042d000021010b0240024002400240" + "200141ff0171412b6b0e03000201020b200241016b2101200441016a2100" + "20024109490d02200121020c080b200241016b2106200441016a21002002" + "4109490d044100210103402006450d0b20002d000041306b220241094b0d" + "09410321072001ac420a7e220f422088a7200fa7220a411f75470d0a2000" + "41016a2100200641016b2106200a200a20026b22014a200241004a73450d" + "000b0c090b20042100200241074b0d060c020b20012202450d030c010b0c" + "0c0b4100210141012107034020002d000041306b220641094b0d06200041" + "016a210020062001410a6c6a2101200241016b22020d000b0c060b20060d" + "010b410021010c040b4100210141012107034020002d000041306b220241" + "094b0d03200041016a21002001410a6c20026b2101200641016b22060d00" + "0b0c030b4100210103402002450d0320002d000041306b220641094b0d01" + "410221072001ac420a7e220f422088a7200fa7220a411f75470d02200041" + "016a2100200241016b21022006410048200a2006200a6a22014a73450d00" + "0b0c010b410121070b200320073a006f200341ef006a41c098c00041a49b" + "c0001015000b20032802582200044020042000100c0b2008410b100c4100" + "210020032802302202200328023c460440200328022c2104200328023821" + "0741002106024020022200450d00034020042d0000220220072d00002208" + "460440200441016a2104200741016a2107200041016b22000d010c020b0b" + "200220086b21060b20064521000b2009044020052009100c0b200328024c" + "22020440200c2002100c0b200328024022020440200b2002100c0b200328" + "02342202044020032802382002100c0b200328022822020440200328022c" + "2002100c0b200341f0006a2400200e20105a2000712001200d4c710f0b41" + "0121000b200320003a0058200341d8006a419080c00041f880c000101500" + "0b410121000b200320003a004c200341cc006a41c098c00041bc9bc00010" + "15000b410141e899c0001014000bee2202087f017e024002400240024002" + "40024002400240200041f4014d04404180a0c00028020022024110200041" + "0b6a41f803712000410b491b220541037622007622014103710d01200541" + "88a0c0002802004d0d0720010d024184a0c00028020022000d030c070b20" + "00410b6a220141787121054184a0c0002802002208450d06411f21074100" + "20056b2103200041f4ffff074d04402005410620014108766722006b7641" + "017120004101746b413e6a21070b200741027441e89cc0006a2802002202" + "45044041002100410021010c040b410021002005411920074101766b4100" + "2007411f471b742104410021010340024020022802044178712206200549" + "0d00200620056b220620034f0d0020022101200622030d00410021032001" + "21000c060b200228021422062000200620022004411d764104716a41106a" + "2802002202471b200020061b21002004410174210420020d000b0c030b02" + "402001417f7341017120006a2206410374220041f89dc0006a2203200041" + "809ec0006a280200220128020822044704402004200336020c2003200436" + "02080c010b4180a0c0002002417e200677713602000b2001200041037236" + "0204200020016a220020002802044101723602040c060b02404102200074" + "2203410020036b72200120007471682206410374220041f89dc0006a2203" + "200041809ec0006a280200220128020822044704402004200336020c2003" + "20043602080c010b4180a0c0002002417e200677713602000b2001200541" + "0372360204200120056a2206200020056b2204410172360204200020016a" + "20043602004188a0c00028020022020440200241787141f89dc0006a2100" + "4190a0c0002802002103027f4180a0c00028020022054101200241037674" + "2202714504404180a0c000200220057236020020000c010b20002802080b" + "2102200020033602082002200336020c2003200036020c20032002360208" + "0b4190a0c00020063602004188a0c00020043602000c050b200068410274" + "41e89cc0006a280200220128020441787120056b21032001210202400340" + "02400240200128021022000d00200128021422000d002002280218210702" + "4002402002200228020c2200460440200241144110200228021422001b6a" + "28020022010d01410021000c020b20022802082201200036020c20002001" + "3602080c010b200241146a200241106a20001b2104034020042106200122" + "0041146a200041106a200028021422011b210420004114411020011b6a28" + "020022010d000b200641003602000b2007450d032002200228021c410274" + "41e89cc0006a220128020047044020074110411420072802102002461b6a" + "20003602002000450d040c020b2001200036020020000d014184a0c00041" + "84a0c000280200417e200228021c77713602000c030b2000280204417871" + "20056b22012003200120034922011b21032000200220011b210220002101" + "0c010b0b2000200736021820022802102201044020002001360210200120" + "003602180b20022802142201450d0020002001360214200120003602180b" + "02400240200341104f044020022005410372360204200220056a22062003" + "410172360204200320066a20033602004188a0c0002802002204450d0120" + "0441787141f89dc0006a21004190a0c0002802002101027f4180a0c00028" + "0200220541012004410376742204714504404180a0c00020042005723602" + "0020000c010b20002802080b2104200020013602082004200136020c2001" + "200036020c200120043602080c010b2002200320056a2200410372360204" + "200020026a220020002802044101723602040c010b4190a0c00020063602" + "004188a0c00020033602000b200241086a0f0b2000200172450440410021" + "0141022007742200410020006b722008712200450d0320006841027441e8" + "9cc0006a28020021000b2000450d010b0340200020012000280204417871" + "220420056b220620034922071b2108200028021022024504402000280214" + "21020b20012008200420054922001b210120032006200320071b20001b21" + "03200222000d000b0b2001450d0020054188a0c00028020022004d200320" + "0020056b4f710d0020012802182107024002402001200128020c22004604" + "40200141144110200128021422001b6a28020022020d01410021000c020b" + "20012802082202200036020c200020023602080c010b200141146a200141" + "106a20001b21040340200421062002220041146a200041106a2000280214" + "22021b210420004114411020021b6a28020022020d000b20064100360200" + "0b02402007450d0002402001200128021c41027441e89cc0006a22022802" + "0047044020074110411420072802102001461b6a20003602002000450d02" + "0c010b2002200036020020000d004184a0c0004184a0c000280200417e20" + "0128021c77713602000c010b200020073602182001280210220204402000" + "2002360210200220003602180b20012802142202450d0020002002360214" + "200220003602180b0240200341104f044020012005410372360204200120" + "056a22022003410172360204200220036a200336020020034180024f0440" + "2002200310320c020b200341f8017141f89dc0006a2100027f4180a0c000" + "280200220441012003410376742203714504404180a0c000200320047236" + "020020000c010b20002802080b2103200020023602082003200236020c20" + "02200036020c200220033602080c010b2001200320056a22004103723602" + "04200020016a220020002802044101723602040b0c010b02400240024002" + "400240024020054188a0c00028020022014b04402005418ca0c000280200" + "22004f044041002100200541af80046a220141107640002202417f462203" + "0d0720024110742202450d074198a0c000410020014180807c7120031b22" + "044198a0c0002802006a2200360200419ca0c000419ca0c0002802002201" + "200020002001491b360200024002404194a0c0002802002203044041e89d" + "c0002100034020002802002201200028020422066a2002460d0220002802" + "0822000d000b0c020b41a4a0c00028020022004100200020024d1b450440" + "41a4a0c00020023602000b41a8a0c00041ff1f36020041ec9dc000200436" + "020041e89dc000200236020041849ec00041f89dc000360200418c9ec000" + "41809ec00036020041809ec00041f89dc00036020041949ec00041889ec0" + "0036020041889ec00041809ec000360200419c9ec00041909ec000360200" + "41909ec00041889ec00036020041a49ec00041989ec00036020041989ec0" + "0041909ec00036020041ac9ec00041a09ec00036020041a09ec00041989e" + "c00036020041b49ec00041a89ec00036020041a89ec00041a09ec0003602" + "0041bc9ec00041b09ec00036020041b09ec00041a89ec00036020041f49d" + "c000410036020041c49ec00041b89ec00036020041b89ec00041b09ec000" + "36020041c09ec00041b89ec00036020041cc9ec00041c09ec00036020041" + "c89ec00041c09ec00036020041d49ec00041c89ec00036020041d09ec000" + "41c89ec00036020041dc9ec00041d09ec00036020041d89ec00041d09ec0" + "0036020041e49ec00041d89ec00036020041e09ec00041d89ec000360200" + "41ec9ec00041e09ec00036020041e89ec00041e09ec00036020041f49ec0" + "0041e89ec00036020041f09ec00041e89ec00036020041fc9ec00041f09e" + "c00036020041f89ec00041f09ec00036020041849fc00041f89ec0003602" + "00418c9fc00041809fc00036020041809fc00041f89ec00036020041949f" + "c00041889fc00036020041889fc00041809fc000360200419c9fc0004190" + "9fc00036020041909fc00041889fc00036020041a49fc00041989fc00036" + "020041989fc00041909fc00036020041ac9fc00041a09fc00036020041a0" + "9fc00041989fc00036020041b49fc00041a89fc00036020041a89fc00041" + "a09fc00036020041bc9fc00041b09fc00036020041b09fc00041a89fc000" + "36020041c49fc00041b89fc00036020041b89fc00041b09fc00036020041" + "cc9fc00041c09fc00036020041c09fc00041b89fc00036020041d49fc000" + "41c89fc00036020041c89fc00041c09fc00036020041dc9fc00041d09fc0" + "0036020041d09fc00041c89fc00036020041e49fc00041d89fc000360200" + "41d89fc00041d09fc00036020041ec9fc00041e09fc00036020041e09fc0" + "0041d89fc00036020041f49fc00041e89fc00036020041e89fc00041e09f" + "c00036020041fc9fc00041f09fc00036020041f09fc00041e89fc0003602" + "004194a0c000200236020041f89fc00041f09fc000360200418ca0c00020" + "0441286b220036020020022000410172360204200020026a412836020441" + "a0a0c00041808080013602000c080b200220034d200120034b720d002000" + "28020c450d030b41a4a0c00041a4a0c0002802002200200220002002491b" + "360200200220046a210141e89dc000210002400240034020012000280200" + "2206470440200028020822000d010c020b0b200028020c450d010b41e89d" + "c0002100034002402003200028020022014f04402003200120002802046a" + "2206490d010b200028020821000c010b0b4194a0c0002002360200418ca0" + "c000200441286b220036020020022000410172360204200020026a412836" + "020441a0a0c00041808080013602002003200641206b41787141086b2200" + "2000200341106a491b2201411b36020441e89dc000290200210920014110" + "6a41f09dc0002902003702002001200937020841ec9dc000200436020041" + "e89dc000200236020041f09dc000200141086a36020041f49dc000410036" + "02002001411c6a2100034020004107360200200041046a22002006490d00" + "0b20012003460d0720012001280204417e713602042003200120036b2200" + "4101723602042001200036020020004180024f04402003200010320c080b" + "200041f8017141f89dc0006a2101027f4180a0c000280200220241012000" + "410376742200714504404180a0c000200020027236020020010c010b2001" + "2802080b2100200120033602082000200336020c2003200136020c200320" + "003602080c070b200020023602002000200028020420046a360204200220" + "054103723602042006410f6a41787141086b2201200220056a22046b2103" + "20014194a0c000280200460d0320014190a0c000280200460d0420012802" + "04220541037141014604402001200541787122001033200020016a220128" + "02042105200020036a21030b20012005417e713602042004200341017236" + "0204200320046a200336020020034180024f04402004200310320c060b20" + "0341f8017141f89dc0006a2100027f4180a0c00028020022014101200341" + "0376742203714504404180a0c000200120037236020020000c010b200028" + "02080b2103200020043602082003200436020c2004200036020c20042003" + "3602080c050b418ca0c000200020056b22013602004194a0c0004194a0c0" + "00280200220020056a220236020020022001410172360204200020054103" + "72360204200041086a21000c060b4190a0c0002802002100024020012005" + "6b2202410f4d04404190a0c00041003602004188a0c00041003602002000" + "2001410372360204200020016a220120012802044101723602040c010b41" + "88a0c00020023602004190a0c000200020056a2203360200200320024101" + "72360204200020016a2002360200200020054103723602040b200041086a" + "0f0b2000200420066a3602044194a0c0004194a0c0002802002200410f6a" + "417871220141086b2202360200418ca0c000418ca0c00028020020046a22" + "03200020016b6a41086a220136020020022001410172360204200020036a" + "412836020441a0a0c00041808080013602000c030b4194a0c00020043602" + "00418ca0c000418ca0c00028020020036a22003602002004200041017236" + "02040c010b4190a0c00020043602004188a0c0004188a0c0002802002003" + "6a220036020020042000410172360204200020046a20003602000b200241" + "086a0f0b41002100418ca0c000280200220120054d0d00418ca0c0002001" + "20056b22013602004194a0c0004194a0c000280200220020056a22023602" + "002002200141017236020420002005410372360204200041086a0f0b2000" + "0f0b200141086a0b2701017f200020012800042202360208200020012800" + "003602042000200236020020014108100c0b3b002000450440230041206b" + "22002400200041003602182000410136020c2000419c81c0003602082000" + "4204370210200041086a20011017000b000b7e01017f230041406a220324" + "002003412b36020c200341d098c000360208200320013602142003200036" + "02102003410236021c200341d482c0003602182003420237022420032003" + "41106aad4280808080e000843703382003200341086aad4280808080f000" + "843703302003200341306a360220200341186a20021017000b4101017f23" + "0041206b2202240020024100360210200241013602042002420437020820" + "02412e36021c200220003602182002200241186a36020020022001101700" + "0bf40102027f017e230041106b22022400200241013b010c200220013602" + "0820022000360204230041106b22012400200241046a2200290200210420" + "01200036020c20012004370204230041106b22002400200141046a220128" + "0200220228020c2103024002400240024020022802040e020001020b2003" + "0d0141012102410021030c020b20030d0020022802002202280204210320" + "0228020021020c010b20004180808080783602002000200136020c200128" + "020822012d0008210220012d00091a2000410e20021026000b2000200336" + "020420002002360200200128020822012d0008210220012d00091a200041" + "0f20021026000b0d00200028020041012001100a0b380002402002418080" + "c400460d00200020022001280210110100450d0041010f0b200345044041" + "000f0b200020032004200128020c1100000b900a010a7f02400240024020" + "0028020022052000280208220372044002402003410171450d0020012002" + "6a21060240200028020c2209450440200121040c010b2001210403402004" + "22032006460d02027f200341016a20032c0000220441004e0d001a200341" + "026a20044160490d001a200341036a20044170490d001a200341046a0b22" + "0420036b20076a21072009200841016a2208470d000b0b20042006460d00" + "20042c00001a20072002027f02402007450d00200220074d044020022007" + "460d0141000c020b200120076a2c000041404e0d0041000c010b20010b22" + "031b21022003200120031b21010b2005450d032000280204210b20024110" + "4f044020022001200141036a417c7122076b22086a220a41037121094100" + "210541002103200120074704402008417c4d044041002106034020032001" + "20066a22042c000041bf7f4a6a200441016a2c000041bf7f4a6a20044102" + "6a2c000041bf7f4a6a200441036a2c000041bf7f4a6a2103200641046a22" + "060d000b0b200121040340200320042c000041bf7f4a6a2103200441016a" + "2104200841016a22080d000b0b02402009450d002007200a417c716a2204" + "2c000041bf7f4a210520094101460d00200520042c000141bf7f4a6a2105" + "20094102460d00200520042c000241bf7f4a6a21050b200a410276210620" + "0320056a21050340200721082006450d0441c0012006200641c0014f1b22" + "09410371210a2009410274210741002104200641044f04402008200741f0" + "07716a210c200821030340200420032802002204417f7341077620044106" + "76724181828408716a20032802042204417f734107762004410676724181" + "828408716a20032802082204417f73410776200441067672418182840871" + "6a200328020c2204417f734107762004410676724181828408716a210420" + "0341106a2203200c470d000b0b200620096b2106200720086a2107200441" + "087641ff81fc0771200441ff81fc07716a418180046c41107620056a2105" + "200a450d000b2008200941fc01714102746a22042802002203417f734107" + "762003410676724181828408712103200a4101460d022003200428020422" + "03417f734107762003410676724181828408716a2103200a4102460d0220" + "0320042802082203417f734107762003410676724181828408716a21030c" + "020b2002450440410021050c030b20024103712104024020024104490440" + "41002105410021080c010b41002105200121032002410c71220821070340" + "200520032c000041bf7f4a6a200341016a2c000041bf7f4a6a200341026a" + "2c000041bf7f4a6a200341036a2c000041bf7f4a6a2105200341046a2103" + "200741046b22070d000b0b2004450d02200120086a21030340200520032c" + "000041bf7f4a6a2105200341016a2103200441016b22040d000b0c020b0c" + "020b200341087641ff811c71200341ff81fc07716a418180046c41107620" + "056a21050b02402005200b490440200b20056b210602400240024020002d" + "00182203410020034103471b220341016b0e020001020b20062103410021" + "060c010b20064101762103200641016a41017621060b200341016a210320" + "00280210210820002802202104200028021c21000340200341016b220345" + "0d02200020082004280210110100450d000b41010f0b0c010b2000200120" + "02200428020c110000044041010f0b410021030340200320064604404100" + "0f0b200341016a2103200020082004280210110100450d000b200341016b" + "2006490f0b200028021c20012002200028022028020c1100000b14002000" + "2802002001200028020428020c1101000b10002001200028020020002802" + "04101a0b1000200128021c20012802202000101e0bea04010a7f23004130" + "6b220324002003200136022c20032000360228200341033a002420034220" + "37021c200341003602142003410036020c027f0240024002402002280210" + "220a450440200228020c2200450d012002280208220120004103746a2104" + "200041016b41ffffffff017141016a210720022802002100034020004104" + "6a28020022050440200328022820002802002005200328022c28020c1100" + "000d040b20012802002003410c6a200141046a2802001101000d03200041" + "086a2100200141086a22012004470d000b0c010b20022802142200450d00" + "2000410574210b200041016b41ffffff3f7141016a210720022802082105" + "200228020021000340200041046a28020022010440200328022820002802" + "002001200328022c28020c1100000d030b20032008200a6a220141106a28" + "020036021c20032001411c6a2d00003a00242003200141186a2802003602" + "202001410c6a28020021044100210941002106024002400240200141086a" + "28020041016b0e020002010b200441037420056a220c2802000d01200c28" + "020421040b410121060b200320043602102003200636020c200141046a28" + "02002104024002400240200128020041016b0e020002010b200441037420" + "056a22062802000d01200628020421040b410121090b2003200436021820" + "0320093602142005200141146a2802004103746a22012802002003410c6a" + "200141046a2802001101000d02200041086a2100200b200841206a220847" + "0d000b0b200720022802044f0d012003280228200228020020074103746a" + "22002802002000280204200328022c28020c110000450d010b41010c010b" + "41000b200341306a24000b1900200128021c41a481c000410e2001280220" + "28020c1100000b820302047f017e230041406a2206240041012107024020" + "002d00040d0020002d00052108200028020022052d001441047145044020" + "0528021c418383c000418083c000200841017122081b4102410320081b20" + "0528022028020c1100000d01200528021c20012002200528022028020c11" + "00000d01200528021c41f391c0004102200528022028020c1100000d0120" + "032005200411010021070c010b2008410171450440200528021c418583c0" + "004103200528022028020c1100000d010b200641013a0017200641206a20" + "0541086a290200370300200641286a200541106a29020037030020064130" + "6a200541186a2802003602002006200529021c3702082005290200210920" + "0641e482c000360238200620093703182006200641176a36021020062006" + "41086a220536023420052001200210210d00200541f391c000410210210d" + "002003200641186a20041101000d002006280234418883c0004102200628" + "023828020c11000021070b200041013a0005200020073a0004200641406b" + "240020000bab04010c7f200141016b210e2000280204210a200028020021" + "0b2000280208210c0240034020050d01027f024020022003490d00034020" + "0120036a2105024002400240200220036b220741074d044020022003470d" + "01200221030c050b0240200541036a417c71220620056b22040440410021" + "000340200020056a2d0000410a460d052004200041016a2200470d000b20" + "04200741086b22004d0d010c030b200741086b21000b0340418082840820" + "062802002209418a94a8d000736b2009724180828408200641046a280200" + "2209418a94a8d000736b2009727141808182847871418081828478470d02" + "200641086a2106200441086a220420004d0d000b0c010b41002100034020" + "0020056a2d0000410a460d022007200041016a2200470d000b200221030c" + "030b20042007460440200221030c030b200420056a2106200220046b2003" + "6b21074100210002400340200020066a2d0000410a460d01200720004101" + "6a2200470d000b200221030c030b200020046a21000b200020036a220441" + "016a21030240200220044d0d00200020056a2d0000410a470d0041002105" + "200322040c030b200220034f0d000b0b20022008460d0241012105200821" + "0420020b21000240200c2d00000440200b41fc82c0004104200a28020c11" + "00000d010b41002106200020084704402000200e6a2d0000410a4621060b" + "200020086b2100200120086a2107200c20063a000020042108200b200720" + "00200a28020c110000450d010b0b4101210d0b200d0b4f01027f20002802" + "042102200028020021030240200028020822002d0000450d00200341fc82" + "c0004104200228020c110000450d0041010f0b20002001410a463a000020" + "03200120022802101101000b0d00200041e482c0002001101e0b2201017f" + "200028020022002000411f7522027320026b2000417f73411f762001100a" + "0b0900200041003602000b7b01027f230041106b2203240041d49cc00041" + "d49cc000280200220441016a360200024020044100480d00024041b0a0c0" + "002d000045044041aca0c00041aca0c00028020041016a36020041d09cc0" + "0028020041004e0d010c020b200341086a20002001110200000b41b0a0c0" + "0041003a00002002450d00000b000b250020002802002d00004504402001" + "41db84c0004105101a0f0b200141e084c0004104101a0b6701027f200128" + "02082204452001280200220520046b20034f724504402001410036020820" + "0141003a000c410021040b20032005490440200128020420046a20022003" + "10371a200041043a00002001200320046a3602080f0b2000420437020020" + "0141003a000c0bac0301017f230041406a22022400024002400240024002" + "40024020002d000041016b0e03010203000b2002200028020436020441b1" + "a0c0002d00001a411410122200450d04200041106a41ac95c00028000036" + "0000200041086a41a495c0002900003700002000419c95c0002900003700" + "00200241143602102002200036020c200241143602082002410336022c20" + "02419c91c000360228200242023702342002200241046aad4280808080b0" + "01843703202002200241086aad4280808080c00184370318200220024118" + "6a360230200128021c2001280220200241286a101e210020022802082201" + "450d03200228020c2001100c0c030b20002d000121002002410136022c20" + "0241c88ac000360228200242013702342002200241186aad4280808080f0" + "008437030820022000410274220041f095c0006a28020036021c20022000" + "419897c0006a2802003602182002200241086a360230200128021c200128" + "0220200241286a101e21000c020b20012000280204220028020020002802" + "04101a21000c010b20002802042200280200200120002802042802101101" + "0021000b200241406b240020000f0b000b10002001200028020420002802" + "08101a0b5501037f2000280204210120002d0000220041044d2000410347" + "7145044020012802002100200141046a2802002202280200220304402000" + "20031103000b20022802042202044020002002100c0b2001410c100c0b0b" + "c709010a7f230041206b2208240002400240024002402001280210450440" + "2001417f36021020032003200241036a417c7120026b220a6b4107714100" + "2003200a4f1b22046b210920032004490d0102402004450d00027f200441" + "016b200220036a220541016b22062d0000410a460d001a2006200220096a" + "2207460d01200441026b200541026b22062d0000410a460d001a20062007" + "460d01200441036b200541036b22062d0000410a460d001a20062007460d" + "01200441046b200541046b22062d0000410a460d001a20062007460d0120" + "0441056b200541056b22062d0000410a460d001a20062007460d01200441" + "066b200541066b22062d0000410a460d001a20062007460d01200441076b" + "200541076b22052d0000410a460d001a20052007460d0120044178720b20" + "096a41016a21040c040b200a20032003200a4b1b210c410020046b210a20" + "0241046b210d2004417f7320026a21060340024020062105200a21042009" + "2207200c4d0d00200441086b210a200541086b2106418082840820022007" + "41086b22096a280200220b418a94a8d000736b200b724180828408200720" + "0d6a280200220b418a94a8d000736b200b72714180818284787141808182" + "8478460d010b0b20032007490d0202400340200320046a450d0120044101" + "6b2104200320056a200541016b21052d0000410a470d000b200320046a41" + "016a21040c040b02402001411c6a2802002204450440410021040c010b20" + "0141186a28020020046a41016b2d0000410a470d0041002104200141003a" + "00202001411c6a41003602000b2003200128021420046b4f044020002001" + "41146a2002200310280c050b200128021820046a2002200310371a200041" + "043a00002001411c6a200320046a3602000c040b230041306b2200240020" + "00410136020c200041c481c0003602082000420137021420002000412f6a" + "ad4280808080a001843703202000200041206a360210200041086a41d091" + "c0001017000b230041306b22002400200020033602042000200936020020" + "00410236020c200041d887c000360208200042023702142000200041046a" + "ad428080808080018437032820002000ad42808080808001843703202000" + "200041206a360210200041086a418485c0001017000b230041306b220024" + "0020002003360204200020073602002000410236020c200041f887c00036" + "0208200042023702142000200041046aad42808080808001843703282000" + "2000ad42808080808001843703202000200041206a360210200041086a41" + "9485c0001017000b20032004490440200841003602182008410136020c20" + "0841888bc00036020820084204370210200841086a41908bc0001017000b" + "02402001411c6a2802002205450d0002400240200128021420056b20044d" + "0440200841086a200141146a20022004102820082d00084104470d012001" + "411c6a28020021050c020b200141186a28020020056a2002200410371a20" + "01411c6a200420056a22053602000c010b200020082903083702000c020b" + "2005450d00200141003a00202001411c6a41003602000b200220046a2105" + "200320046b220220012802144f04402000200141146a2005200210280c01" + "0b200141186a2802002005200210371a200041043a00002001411c6a2002" + "3602000b2001200128021041016a360210200841206a24000b9a0102047f" + "017e230041106b22032400200341086a200028020828020020012002102c" + "20032d000822054104470440200028020421012003290308210720002d00" + "00220241044d20024103477145044020012802002102200141046a280200" + "220428020022060440200220061103000b20042802042204044020022004" + "100c0b2001410c100c0b200020073702000b200341106a24002005410447" + "0be10202057f017e230041106b2202240020024100360204027f20014180" + "014f044020014180104f04402001418080044f044020022001413f714180" + "01723a00072002200141127641f001723a000420022001410676413f7141" + "8001723a000620022001410c76413f71418001723a000541040c030b2002" + "2001413f71418001723a000620022001410c7641e001723a000420022001" + "410676413f71418001723a000541030c020b20022001413f71418001723a" + "00052002200141067641c001723a000441020c010b200220013a00044101" + "0b2101200241086a2000280208280200200241046a2001102c20022d0008" + "22054104470440200028020421012002290308210720002d000022034104" + "4d20034103477145044020012802002103200141046a2802002204280200" + "22060440200320061103000b20042802042204044020032004100c0b2001" + "410c100c0b200020073702000b200241106a240020054104470b0d002000" + "419c88c0002001101e0bba0a02047f037e230041406a2201240020014106" + "36020c2001419892c00036020841c89cc0002d0000410347044002402300" + "41206b22022400024002400240024041c89cc0002d000041026b0e020301" + "000b41c89cc00041023a000041b1a0c0002d00001a41800810122203450d" + "0141c89cc00041033a000041b89cc000200336020041b09cc00042808080" + "8080800137030041a09cc000420037030041c09cc00041003a000041bc9c" + "c000410036020041ac9cc00041003a000041a89cc00041003602000b2002" + "41206a24000c020b000b200241003602182002410136020c200241e895c0" + "0036020820024204370210200241086a41b094c0001017000b0b02400240" + "024041d89cc000290300220650044041e09cc00029030021050340200542" + "7f510d0241e09cc000200542017c220641e09cc000290300220720052007" + "5122021b370300200721052002450d000b41d89cc00020063703000b0240" + "024041a09cc000290300200652044041ac9cc0002d000021034101210241" + "ac9cc00041013a0000200120033a00182003450d01200142003702342001" + "4281808080c00037022c200141c893c000360228230041106b2202240020" + "02418888c00036020c2002200141186a360208230041f0006b2200240020" + "00418c88c00036020c2000200241086a3602082000418c88c00036021420" + "002002410c6a3602102000410236021c200041cd81c00036021802402001" + "41286a22012802004504402000410336025c2000418082c0003602582000" + "42033702642000200041106aad4280808080e00084370348200020004108" + "6aad4280808080e000843703400c010b200041306a200141106a29020037" + "0300200041286a200141086a290200370300200020012902003703202000" + "410436025c200041b482c000360258200042043702642000200041106aad" + "4280808080e000843703502000200041086aad4280808080e00084370348" + "2000200041206aad42808080809001843703400b2000200041186aad4280" + "808080f000843703382000200041386a360260200041d8006a41fc93c000" + "1017000b41a89cc0002802002202417f470440200241016a21020c020b23" + "0041306b220024002000412636020c200041c094c0003602082000410136" + "0214200041c88ac0003602102000420137021c2000200041086aad428080" + "8080f000843703282000200041286a360218200041106a418c95c0001017" + "000b41a09cc00020063703000b41a89cc0002002360200200141a09cc000" + "36021041042102200141043a00182001200141106a360220200141186a41" + "9c88c0002000101e210320012d0018210002402003450440420021054117" + "2000764101710d01200128021c22002802002102200041046a2802002203" + "28020022040440200220041103000b20032802042203044020022003100c" + "0b2000410c100c410421020c010b20004104460d02200129031822074280" + "7e8321052007a721020b20012802102200200028020841016b2203360208" + "2003450440200041003a000c200042003703000b200241ff01714104470d" + "02200141406b24000f0b230041206b220024002000410036021820004101" + "36020c200041b08ac00036020820004204370210200041086a41b88ac000" + "1017000b200141003602382001410136022c2001419093c0003602282001" + "4204370230200141286a419893c0001017000b200120052002ad42ff0183" + "843703102001410236022c200141f891c000360228200142023702342001" + "200141106aad4280808080d001843703202001200141086aad4280808080" + "f000843703182001200141186a360230200141286a418892c0001017000b" + "0c00200020012902003703000bba0201047f411f21022000420037021020" + "0141ffffff074d04402001410620014108766722036b7641017120034101" + "746b413e6a21020b2000200236021c200241027441e89cc0006a21044101" + "20027422034184a0c0002802007145044020042000360200200020043602" + "182000200036020c200020003602084184a0c0004184a0c0002802002003" + "723602000f0b024002402001200428020022032802044178714604402003" + "21020c010b2001411920024101766b41002002411f471b74210503402003" + "2005411d764104716a41106a22042802002202450d022005410174210520" + "02210320022802044178712001470d000b0b20022802082201200036020c" + "20022000360208200041003602182000200236020c200020013602080f0b" + "20042000360200200020033602182000200036020c200020003602080bf1" + "0201047f200028020c21020240024020014180024f044020002802182103" + "0240024020002002460440200041144110200028021422021b6a28020022" + "010d01410021020c020b20002802082201200236020c200220013602080c" + "010b200041146a200041106a20021b21040340200421052001220241146a" + "200241106a200228021422011b210420024114411020011b6a2802002201" + "0d000b200541003602000b2003450d022000200028021c41027441e89cc0" + "006a220128020047044020034110411420032802102000461b6a20023602" + "002002450d030c020b2001200236020020020d014184a0c0004184a0c000" + "280200417e200028021c77713602000c020b200028020822002002470440" + "2000200236020c200220003602080f0b4180a0c0004180a0c00028020041" + "7e200141037677713602000f0b2002200336021820002802102201044020" + "022001360210200120023602180b20002802142200450d00200220003602" + "14200020023602180b0b8f0101027f230041306b22012400024020004100" + "4e047f2000450440410121020c020b41b1a0c0002d00001a200010122202" + "0d0141010541000b41bc9ac0001014000b2001200236020c200141023602" + "14200141d89ac0003602102001420137021c2001411036022c2001200141" + "286a36021820012001410c6a360228200141106a1030200128020c200141" + "306a24000bcf0101067f23004180016b2204240020012802042107200128" + "020021062000280200210020012802142205210202402005410471450d00" + "2005410872210220060d0020014281808080a0013702000b200120024104" + "7236021441810121020340200220046a41026b2000410f71220341307220" + "0341d7006a2003410a491b3a0000200241016b2102200041104920004104" + "762100450d000b20014101419183c0004102200220046a41016b41810120" + "026b10092001200536021420012007360204200120063602002004418001" + "6a24000b6301017f230041306b220224002002200036020c200241023602" + "14200241f49ac0003602102002420137021c2002411036022c2002200241" + "286a36021820022002410c6a360228200241106a10302001044020022802" + "0c2001100c0b200241306a24000b8c0501087f0240200241104904402000" + "21030c010b02402000410020006b41037122066a220520004d0d00200021" + "032001210420060440200621070340200320042d00003a0000200441016a" + "2104200341016a2103200741016b22070d000b0b200641016b4107490d00" + "0340200320042d00003a0000200341016a200441016a2d00003a00002003" + "41026a200441026a2d00003a0000200341036a200441036a2d00003a0000" + "200341046a200441046a2d00003a0000200341056a200441056a2d00003a" + "0000200341066a200441066a2d00003a0000200341076a200441076a2d00" + "003a0000200441086a2104200341086a22032005470d000b0b2005200220" + "066b2207417c7122086a21030240200120066a2204410371450440200320" + "054d0d0120042101034020052001280200360200200141046a2101200541" + "046a22052003490d000b0c010b200320054d0d0020044103742202411871" + "21062004417c71220941046a2101410020026b411871210a200928020021" + "0203402005200220067620012802002202200a7472360200200141046a21" + "01200541046a22052003490d000b0b20074103712102200420086a21010b" + "02402003200220036a22064f0d002002410771220404400340200320012d" + "00003a0000200141016a2101200341016a2103200441016b22040d000b0b" + "200241016b4107490d000340200320012d00003a0000200341016a200141" + "016a2d00003a0000200341026a200141026a2d00003a0000200341036a20" + "0141036a2d00003a0000200341046a200141046a2d00003a000020034105" + "6a200141056a2d00003a0000200341066a200141066a2d00003a00002003" + "41076a200141076a2d00003a0000200141086a2101200341086a22032006" + "470d000b0b20000b4901037e2000200142ffffffff0f832202420a7e2203" + "420022022001422088420a7e7c22014220867c2204370300200020032004" + "56ad2001200254ad4220862001422088847c3703080b0bea1b0400418080" + "c0000ba40611000000140000000400000012000000000000000100000001" + "00000013000000557466384572726f7276616c69645f75705f746f657272" + "6f725f6c656e46726f6d557466384572726f7262797465736572726f724e" + "6f6e65536f6d657372632f6c69622e727300005c0010000a000000150000" + "003d0000005c0010000a0000001600000037000000636170616369747920" + "6f766572666c6f770000008800100011000000426f72726f774d75744572" + "726f72616c726561647920626f72726f7765643a20b2001000120000005b" + "3d3d617373657274696f6e20606c6566742020726967687460206661696c" + "65640a20206c6566743a200a2072696768743a2000cf00100010000000df" + "00100017000000f60010000900000020726967687460206661696c65643a" + "200a20206c6566743a20000000cf00100010000000180110001000000028" + "01100009000000f6001000090000000100000000000000f3081000020000" + "00000000000c000000040000001400000015000000160000002020202020" + "7b202c20207b0a2c0a7d207d28280a5d3078303030313032303330343035" + "303630373038303931303131313231333134313531363137313831393230" + "323132323233323432353236323732383239333033313332333333343335" + "333633373338333934303431343234333434343534363437343834393530" + "353135323533353435353536353735383539363036313632363336343635" + "363636373638363937303731373237333734373537363737373837393830" + "383138323833383438353836383738383839393039313932393339343935" + "393639373938393966616c7365747275656c6962726172792f636f72652f" + "7372632f736c6963652f6d656d6368722e72736402100020000000850000" + "001e0000006402100020000000a100000009000000010101010101010101" + "010101010101010101010101010101010101010101010101010101010101" + "010101010101010101010101010101010101010101010101010101010101" + "010101010101010101010101010101010101010101010101010101010101" + "010101010101010101010101010101010101010101010101010101010100" + "41e686c0000b330202020202020202020202020202020202020202020202" + "020202020202020303030303030303030303030303030304040404040041" + "a487c0000b6172616e676520737461727420696e64657820206f7574206f" + "662072616e676520666f7220736c696365206f66206c656e67746820a403" + "100012000000b60310002200000072616e676520656e6420696e64657820" + "e803100010000000b60310002200419088c0000b8f140400000004000000" + "17000000180000000c00000004000000190000001a0000001b0000002f72" + "7573742f646570732f646c6d616c6c6f632d302e322e372f7372632f646c" + "6d616c6c6f632e7273617373657274696f6e206661696c65643a20707369" + "7a65203e3d2073697a65202b206d696e5f6f766572686561640034041000" + "29000000a804000009000000617373657274696f6e206661696c65643a20" + "7073697a65203c3d2073697a65202b206d61785f6f766572686561640000" + "3404100029000000ae0400000d0000006c6962726172792f7374642f7372" + "632f7468726561642f6d6f642e72736661696c656420746f2067656e6572" + "61746520756e69717565207468726561642049443a206269747370616365" + "20657868617573746564f904100037000000dc0410001d000000ae040000" + "0d00000001000000000000006c6962726172792f7374642f7372632f696f" + "2f62756666657265642f6c696e657772697465727368696d2e72736d6964" + "203e206c656e00007d05100009000000500510002d000000160100002900" + "0000656e74697479206e6f7420666f756e647065726d697373696f6e2064" + "656e696564636f6e6e656374696f6e2072656675736564636f6e6e656374" + "696f6e207265736574686f737420756e726561636861626c656e6574776f" + "726b20756e726561636861626c65636f6e6e656374696f6e2061626f7274" + "65646e6f7420636f6e6e65637465646164647265737320696e2075736561" + "646472657373206e6f7420617661696c61626c656e6574776f726b20646f" + "776e62726f6b656e2070697065656e7469747920616c7265616479206578" + "697374736f7065726174696f6e20776f756c6420626c6f636b6e6f742061" + "206469726563746f727969732061206469726563746f7279646972656374" + "6f7279206e6f7420656d707479726561642d6f6e6c792066696c65737973" + "74656d206f722073746f72616765206d656469756d66696c657379737465" + "6d206c6f6f70206f7220696e646972656374696f6e206c696d6974202865" + "2e672e2073796d6c696e6b206c6f6f70297374616c65206e6574776f726b" + "2066696c652068616e646c65696e76616c696420696e7075742070617261" + "6d65746572696e76616c6964206461746174696d6564206f757477726974" + "65207a65726f6e6f2073746f726167652073706163657365656b206f6e20" + "756e7365656b61626c652066696c6571756f746120657863656564656466" + "696c6520746f6f206c617267657265736f75726365206275737965786563" + "757461626c652066696c652062757379646561646c6f636b63726f73732d" + "646576696365206c696e6b206f722072656e616d65746f6f206d616e7920" + "6c696e6b73696e76616c69642066696c656e616d65617267756d656e7420" + "6c69737420746f6f206c6f6e676f7065726174696f6e20696e7465727275" + "70746564756e737570706f72746564756e657870656374656420656e6420" + "6f662066696c656f7574206f66206d656d6f7279696e2070726f67726573" + "736f74686572206572726f72756e63617465676f72697a6564206572726f" + "7220286f73206572726f72202900000001000000000000008d0810000b00" + "000098081000010000006c6962726172792f7374642f7372632f696f2f73" + "7464696f2e727300b40810001b00000032030000140000006661696c6564" + "207072696e74696e6720746f203a20000000e008100013000000f3081000" + "02000000b40810001b00000063040000090000007374646f75746c696272" + "6172792f7374642f7372632f696f2f6d6f642e72736120666f726d617474" + "696e6720747261697420696d706c656d656e746174696f6e207265747572" + "6e656420616e206572726f72207768656e2074686520756e6465726c7969" + "6e672073747265616d20646964206e6f7400000037091000560000001e09" + "100019000000680700001500000063616e6e6f7420726563757273697665" + "6c792061637175697265206d75746578a8091000200000006c6962726172" + "792f7374642f7372632f7379732f73796e632f6d757465782f6e6f5f7468" + "72656164732e7273d00910002c00000013000000090000006c6962726172" + "792f7374642f7372632f73796e632f706f69736f6e2f6f6e63652e727300" + "0c0a100023000000d9000000140000006c6f636b20636f756e74206f7665" + "72666c6f7720696e207265656e7472616e74206d757465786c6962726172" + "792f7374642f7372632f73796e632f7265656e7472616e745f6c6f636b2e" + "7273660a100026000000220100002d0000006f7065726174696f6e207375" + "636365737366756c6f6e652d74696d6520696e697469616c697a6174696f" + "6e206d6179206e6f7420626520706572666f726d65642072656375727369" + "76656c79b00a100038000000100000001100000012000000100000001000" + "000013000000120000000d0000000e000000150000000c0000000b000000" + "15000000150000000f0000000e0000001300000026000000380000001900" + "0000170000000c000000090000000a00000010000000170000000e000000" + "0e0000000d00000014000000080000001b0000000e000000100000001600" + "0000150000000b000000160000000d0000000b0000000b00000013000000" + "a0051000b0051000c1051000d3051000e3051000f3051000060610001806" + "1000250610003306100048061000540610005f0610007406100089061000" + "98061000a6061000b9061000df0610001707100030071000470710005307" + "10005c07100066071000760710008d0710009b071000a9071000b6071000" + "ca071000d2071000ed071000fb0710000b08100021081000360810004108" + "100057081000640810006f0810007a081000000000000100000001000000" + "1300000063616c6c65642060526573756c743a3a756e7772617028296020" + "6f6e20616e2060457272602076616c75652f55736572732f6d7661646172" + "692f2e7275737475702f746f6f6c636861696e732f737461626c652d6161" + "72636836342d6170706c652d64617277696e2f6c69622f727573746c6962" + "2f7372632f727573742f6c6962726172792f616c6c6f632f7372632f736c" + "6963652e72737b0c10006d000000a1000000190000005061727365496e74" + "4572726f726b696e642f55736572732f6d7661646172692f446f63756d65" + "6e74732f63726166742f7872706c2d7374642f7372632f6c69622e727300" + "090d1000320000000500000016000000616c6c6f63617465200a00004c0d" + "100009000000550d1000010000006465616c6c6f636174652000680d1000" + "0b000000550d1000010000004163636f756e7444657374696e6174696f6e" + "46696e6973684166746572000000090d1000320000008000000016000000" + "42616c616e636500090d1000320000009b00000016000000456d70747949" + "6e76616c69644469676974506f734f766572666c6f774e65674f76657266" + "6c6f775a65726f00050000000c0000000b0000000b00000004000000cc0d" + "1000d10d1000dd0d1000e80d1000f30d10004d0970726f64756365727302" + "086c616e6775616765010452757374000c70726f6365737365642d627901" + "0572757374631d312e38352e31202834656231363132353020323032352d" + "30332d31352900490f7461726765745f6665617475726573042b0f6d7574" + "61626c652d676c6f62616c732b087369676e2d6578742b0f726566657265" + "6e63652d74797065732b0a6d756c746976616c7565"; + +std::string const deepRecursionHex = + "0061736d01000000013f0b60017f0060037f7f7f017f60027f7f017f60027f" + "7f0060000060037f7f7f006000017f60037e7f7f017f60047f7f7f7f017f60" + "017f017f60047f7f7f7f00032f2e0302040400030501030302020600030302" + "0700080301050202020404090202000a0a0102020403030300000a03010405" + "017001101005030100110619037f01418080c0000b7f0041b49ac0000b7f00" + "41c09ac0000b073905066d656d6f7279020005726563757200000972656375" + "7273697665000c0a5f5f646174615f656e6403010b5f5f686561705f626173" + "6503020915010041010b0f010a0b1e10171819262c1d1f2223240a927c2ea7" + "0704017f017e027f017e23808080800041e0006b2202248080808000200220" + "003602040240200041004c0d002001200128020041016a3602002000417f6a" + "20011080808080000b2002410236020c2002418480c0800036020820024201" + "37021420024181808080003602242002200241206a3602102002200241046a" + "3602202002410636022c2002419490c08000360228024041002d00c096c080" + "004103460d001082808080000b0240024002400240024041002903a89ac080" + "0022034200520d00024041002802b09ac0800022000d001083808080004100" + "2802b09ac0800021000b20002000280200220141016a3602002001417f4c0d" + "012000450d02200020002802002201417f6a36020020002903082103200141" + "01470d0020001084808080000b0240024002402003410029039896c0800051" + "0d0041002d00a496c08000210141012100410041013a00a496c08000200220" + "013a00382001450d012002420037025420024281808080c00037024c200241" + "bc91c08000360248200241386a200241c8006a108580808000000b02404100" + "2802a096c080002200417f460d00200041016a21000c020b419c92c0800041" + "2641e092c08000108680808000000b4100200337039896c080000b41002000" + "3602a096c080002002419896c0800036023041042100200241043a00382002" + "200241306a360240200241386a41c085c08000200241086a10878080800021" + "0120022d003821040240024020010d00420021034117200441ff0171764101" + "710d01200228023c220028020021010240200041046a280200220428020022" + "05450d002001200511808080800080808080000b024020042802042204450d" + "00200120041088808080000b2000410c108880808000410421000c010b2004" + "41ff01714104460d032002290338220642807e8321032006a721000b200228" + "023022012001280208417f6a2204360208024020040d00200141003a000c20" + "0142003703000b200041ff01714104470d03200241e0006a2480808080000f" + "0b000b418087c0800041de0041f487c08000108680808000000b2002410036" + "02582002410136024c2002418491c0800036024820024204370250200241c8" + "006a418c91c08000108980808000000b200220032000ad42ff018384370330" + "2002410236024c200241f48fc0800036024820024202370254200241828080" + "8000ad422086200241306aad843703402002418380808000ad422086200241" + "286aad843703382002200241386a360250200241c8006a418490c080001089" + "80808000000b2701017f200028020022002000411f7522027320026bad2000" + "417f73411f7620011091808080000bf10101027f23808080800041206b2200" + "248080808000024002400240024041002d00c096c080000e0400000301000b" + "410041023a00c096c0800041002d009096c080001a418008109c8080800022" + "01450d01410041033a00c096c08000410020013602b096c080004100428080" + "80808080013703a896c080004100420037039896c08000410041003a00b896" + "c08000410041003602b496c08000410041003a00a496c08000410041003602" + "a096c080000b200041206a2480808080000f0b000b20004100360218200041" + "0136020c200041bc93c0800036020820004204370210200041086a418c92c0" + "8000108980808000000bf90103027f037e017f23808080800041206b220024" + "808080800041002d009096c080001a0240024002404120109c808080002201" + "450d0020014102360210200142818080801037030041002903d096c0800021" + "0203402002427f510d024100200242017c220341002903d096c08000220420" + "0420025122051b3703d096c08000200421022005450d000b410020033703a8" + "9ac080002001200337030841002802b09ac08000450d022000410036021820" + "00410136020c200041c484c0800036020820004204370210200041086a419c" + "85c080001089808080000b000b109b80808000000b410020013602b09ac080" + "00200041206a2480808080000b5b01027f024020002802104101470d002000" + "280214220141003a000020002802182202450d00200120021088808080000b" + "02402000417f460d00200020002802042201417f6a36020420014101470d00" + "200041201088808080000b0b3a01017f23808080800041106b220224808080" + "8000200241ac85c0800036020c20022000360208200241086a2002410c6a20" + "01109680808000000b6a01017f23808080800041306b220324808080800020" + "03200136020c2003200036020820034101360214200341d488c08000360210" + "2003420137021c2003418380808000ad422086200341086aad843703282003" + "200341286a360218200341106a2002108980808000000bbf05010a7f238080" + "80800041306b2203248080808000200341033a002c2003412036021c410021" + "04200341003602282003200136022420032000360220200341003602142003" + "410036020c02400240024002400240200228021022050d00200228020c2200" + "450d0120022802082101200041037421062000417f6a41ffffffff01714101" + "6a21042002280200210003400240200041046a2802002207450d0020032802" + "2020002802002007200328022428020c11818080800080808080000d040b20" + "012802002003410c6a200128020411828080800080808080000d0320014108" + "6a2101200041086a2100200641786a22060d000c020b0b2002280214220145" + "0d00200141057421082001417f6a41ffffff3f7141016a2104200228020821" + "09200228020021004100210603400240200041046a2802002201450d002003" + "28022020002802002001200328022428020c11818080800080808080000d03" + "0b2003200520066a220141106a28020036021c20032001411c6a2d00003a00" + "2c2003200141186a2802003602282001410c6a28020021074100210a410021" + "0b024002400240200141086a2802000e03010002010b2007410374210c4100" + "210b2009200c6a220c2802040d01200c28020021070b4101210b0b20032007" + "3602102003200b36020c200141046a28020021070240024002402001280200" + "0e03010002010b2007410374210b2009200b6a220b2802040d01200b280200" + "21070b4101210a0b200320073602182003200a3602142009200141146a2802" + "004103746a22012802002003410c6a20012802041182808080008080808000" + "0d02200041086a21002008200641206a2206470d000b0b200420022802044f" + "0d012003280220200228020020044103746a22012802002001280204200328" + "022428020c1181808080008080808000450d010b410121010c010b41002101" + "0b200341306a24808080800020010b6c01027f024002402000417c6a280200" + "2202417871220341044108200241037122021b20016a490d0002402002450d" + "002003200141276a4b0d020b2000108d808080000f0b418186c0800041b086" + "c08000108e80808000000b41c086c0800041f086c08000108e80808000000b" + "5601017f23808080800041206b2202248080808000200241106a200041106a" + "290200370300200241086a200041086a290200370300200241013b011c2002" + "2001360218200220002902003703002002109280808000000be50301017f23" + "808080800041c0006b22022480808080000240024002400240024002402000" + "2d00000e0400010203000b2002200028020436020441002d009096c080001a" + "4114109c808080002200450d04200041106a410028008093c0800036000020" + "0041086a41002900f892c08000370000200041002900f092c0800037000020" + "0241143602102002200036020c200241143602082002410336022c200241a0" + "8fc08000360228200242023702342002418180808000ad422086200241046a" + "ad843703202002418480808000ad422086200241086aad8437031820022002" + "41186a36023020012802142001280218200241286a10878080800021002002" + "2802082201450d03200228020c20011088808080000c030b20002d00012100" + "2002410136022c200241d488c0800036022820024201370234200241838080" + "8000ad422086200241186aad8437030820022000410274220041c493c08000" + "6a28020036021c2002200041e894c080006a2802003602182002200241086a" + "36023020012802142001280218200241286a10878080800021000c020b2001" + "20002802042200280200200028020410958080800021000c010b2000280204" + "220028020020012000280204280210118280808000808080800021000b2002" + "41c0006a24808080800020000f0b000b140020012000280200200028020410" + "95808080000b3f01027f23808080800041106b220024808080800020004100" + "36020c41a08d062000410c6a108080808000200028020c2101200041106a24" + "808080800020010bbe0601057f200041786a22012000417c6a280200220241" + "787122006a21030240024020024101710d002002410271450d012001280200" + "220220006a21000240200120026b220141002802809ac08000470d00200328" + "02044103714103470d01410020003602f899c0800020032003280204417e71" + "36020420012000410172360204200320003602000f0b2001200210a8808080" + "000b024002400240024002400240200328020422024102710d002003410028" + "02849ac08000460d02200341002802809ac08000460d032003200241787122" + "0210a8808080002001200220006a2200410172360204200120006a20003602" + "00200141002802809ac08000470d01410020003602f899c080000f0b200320" + "02417e7136020420012000410172360204200120006a20003602000b200041" + "8002490d022001200010a78080800041002101410041002802989ac0800041" + "7f6a22003602989ac0800020000d04024041002802e097c080002200450d00" + "410021010340200141016a2101200028020822000d000b0b4100200141ff1f" + "200141ff1f4b1b3602989ac080000f0b410020013602849ac0800041004100" + "2802fc99c0800020006a22003602fc99c08000200120004101723602040240" + "200141002802809ac08000470d00410041003602f899c08000410041003602" + "809ac080000b200041002802909ac0800022044d0d0341002802849ac08000" + "2200450d034100210241002802fc99c0800022054129490d0241d897c08000" + "2101034002402001280200220320004b0d002000200320012802046a490d04" + "0b200128020821010c000b0b410020013602809ac08000410041002802f899" + "c0800020006a22003602f899c0800020012000410172360204200120006a20" + "003602000f0b200041f8017141e897c080006a21030240024041002802f099" + "c08000220241012000410376742200710d00410020022000723602f099c080" + "00200321000c010b200328020821000b200320013602082000200136020c20" + "01200336020c200120003602080f0b024041002802e097c080002201450d00" + "410021020340200241016a2102200128020822010d000b0b4100200241ff1f" + "200241ff1f4b1b3602989ac08000200520044d0d004100417f3602909ac080" + "000b0b4d01017f23808080800041206b220224808080800020024100360210" + "20024101360204200242043702082002412e36021c20022000360218200220" + "0241186a36020020022001108980808000000b7d02017f017e238080808000" + "41306b22022480808080002002200036020020022001360204200241023602" + "0c2002418484c08000360208200242023702142002418580808000ad422086" + "2203200241046aad84370328200220032002ad843703202002200241206a36" + "0210200241086a41b083c08000108980808000000b11002000350200410120" + "011091808080000bdf0703027f017e097f23808080800041306b2203248080" + "808000412721040240024020004290ce005a0d00200021050c010b41272104" + "0340200341096a20046a2206417c6a20004290ce0080220542f0b1037e2000" + "7ca7220741ffff037141e4006e220841017441c481c080006a2f00003b0000" + "2006417e6a2008419c7f6c20076a41ffff037141017441c481c080006a2f00" + "003b00002004417c6a2104200042ffc1d72f5621062005210020060d000b0b" + "02400240200542e300560d002005a721060c010b200341096a2004417e6a22" + "046a2005a7220741ffff037141e4006e2206419c7f6c20076a41ffff037141" + "017441c481c080006a2f00003b00000b024002402006410a490d0020034109" + "6a2004417e6a22046a200641017441c481c080006a2f00003b00000c010b20" + "0341096a2004417f6a22046a20064130723a00000b412720046b2109024002" + "4020010d00412820046b2107200228021c2106412d21010c010b412b418080" + "c400200228021c220641017122071b2101200720096a21070b200341096a20" + "046a210a2006410471410276210b0240024020022802000d00024020022802" + "142204200228021822062001200b109380808000450d00410121040c020b20" + "04200a2009200628020c118180808000808080800021040c010b0240024002" + "402002280204220c20074b0d0020022802142204200228021822062001200b" + "109380808000450d01410121040c030b2006410871450d012002280210210d" + "2002413036021020022d0020210e41012104200241013a0020200228021422" + "06200228021822082001200b1093808080000d02200c20076b41016a210402" + "4003402004417f6a2204450d01200641302008280210118280808000808080" + "8000450d000b410121040c030b02402006200a2009200828020c1181808080" + "008080808000450d00410121040c030b2002200e3a00202002200d36021041" + "0021040c020b2004200a2009200628020c118180808000808080800021040c" + "010b200c20076b210c02400240024020022d002022040e0402000100020b20" + "0c21044100210c0c010b200c4101762104200c41016a410176210c0b200441" + "016a2104200228021021082002280218210620022802142107024003402004" + "417f6a2204450d012007200820062802101182808080008080808000450d00" + "0b410121040c010b41012104200720062001200b1093808080000d00200720" + "0a2009200628020c11818080800080808080000d004100210403400240200c" + "2004470d00200c200c4921040c020b200441016a2104200720082006280210" + "1182808080008080808000450d000b2004417f6a200c4921040b200341306a" + "24808080800020040b5d01027f23808080800041206b220124808080800020" + "002802182102200141106a200041106a290200370300200141086a20004108" + "6a2902003703002001200036021c2001200236021820012000290200370300" + "200110a980808000000b490002402002418080c400460d0020002002200128" + "02101182808080008080808000450d0041010f0b024020030d0041000f0b20" + "0020034100200128020c11818080800080808080000b7d02017f017e238080" + "80800041306b22022480808080002002200036020020022001360204200241" + "0236020c200241a484c08000360208200242023702142002418580808000ad" + "4220862203200241046aad84370328200220032002ad843703202002200241" + "206a360210200241086a41c083c08000108980808000000bc20b010b7f2000" + "28020821030240024002400240200028020022040d002003410171450d010b" + "02402003410171450d00200120026a210502400240200028020c22060d0041" + "002107200121080c010b410021074100210920012108034020082203200546" + "0d020240024020032c00002208417f4c0d00200341016a21080c010b024020" + "0841604f0d00200341026a21080c010b0240200841704f0d00200341036a21" + "080c010b200341046a21080b200820036b20076a21072006200941016a2209" + "470d000b0b20082005460d00024020082c00002203417f4a0d002003416049" + "1a0b024002402007450d000240200720024f0d00200120076a2c000041bf7f" + "4a0d01410021030c020b20072002460d00410021030c010b200121030b2007" + "200220031b21022003200120031b21010b024020040d002000280214200120" + "02200028021828020c11818080800080808080000f0b2000280204210a0240" + "20024110490d0020022001200141036a417c7122076b22096a220b41037121" + "044100210641002103024020012007460d004100210302402009417c4b0d00" + "410021034100210503402003200120056a22082c000041bf7f4a6a20084101" + "6a2c000041bf7f4a6a200841026a2c000041bf7f4a6a200841036a2c000041" + "bf7f4a6a2103200541046a22050d000b0b200121080340200320082c000041" + "bf7f4a6a2103200841016a2108200941016a22090d000b0b02402004450d00" + "2007200b417c716a22082c000041bf7f4a210620044101460d00200620082c" + "000141bf7f4a6a210620044102460d00200620082c000241bf7f4a6a21060b" + "200b4102762105200620036a21060340200721042005450d04200541c00120" + "0541c001491b220b410371210c200b410274210d4100210802402005410449" + "0d002004200d41f007716a210941002108200421030340200328020c220741" + "7f7341077620074106767241818284087120032802082207417f7341077620" + "074106767241818284087120032802042207417f7341077620074106767241" + "818284087120032802002207417f7341077620074106767241818284087120" + "086a6a6a6a2108200341106a22032009470d000b0b2005200b6b2105200420" + "0d6a2107200841087641ff81fc0771200841ff81fc07716a418180046c4110" + "7620066a2106200c450d000b2004200b41fc01714102746a22082802002203" + "417f734107762003410676724181828408712103200c4101460d0220082802" + "042207417f7341077620074106767241818284087120036a2103200c410246" + "0d0220082802082208417f7341077620084106767241818284087120036a21" + "030c020b024020020d00410021060c030b2002410371210802400240200241" + "044f0d0041002106410021090c010b41002106200121032002410c71220921" + "070340200620032c000041bf7f4a6a200341016a2c000041bf7f4a6a200341" + "026a2c000041bf7f4a6a200341036a2c000041bf7f4a6a2106200341046a21" + "032007417c6a22070d000b0b2008450d02200120096a21030340200620032c" + "000041bf7f4a6a2106200341016a21032008417f6a22080d000c030b0b2000" + "28021420012002200028021828020c11818080800080808080000f0b200341" + "087641ff811c71200341ff81fc07716a418180046c41107620066a21060b02" + "400240200a20064d0d00200a20066b21054100210302400240024020002d00" + "200e0402000102020b20052103410021050c010b2005410176210320054101" + "6a41017621050b200341016a21032000280210210920002802182108200028" + "0214210703402003417f6a2203450d02200720092008280210118280808000" + "8080808000450d000b41010f0b200028021420012002200028021828020c11" + "818080800080808080000f0b0240200720012002200828020c118180808000" + "8080808000450d0041010f0b410021030340024020052003470d0020052005" + "490f0b200341016a2103200720092008280210118280808000808080800045" + "0d000b2003417f6a2005490b820302017f017e23808080800041f0006b2203" + "248080808000200341b085c0800036020c20032000360208200341b085c080" + "00360214200320013602102003410236021c200341bc80c080003602180240" + "20022802000d002003410336025c200341f080c08000360258200342033702" + "642003418680808000ad4220862204200341106aad84370348200320042003" + "41086aad843703402003418380808000ad422086200341186aad8437033820" + "03200341386a360260200341d8006a41e891c08000108980808000000b2003" + "41206a41106a200241106a290200370300200341206a41086a200241086a29" + "0200370300200320022902003703202003410436025c200341a481c0800036" + "0258200342043702642003418680808000ad4220862204200341106aad8437" + "035020032004200341086aad843703482003418780808000ad422086200341" + "206aad843703402003418380808000ad422086200341186aad843703382003" + "200341386a360260200341d8006a41e891c08000108980808000000b1c0020" + "002802002001200028020428020c11828080800080808080000b1400200128" + "0214200128021820001087808080000b22002001280214419480c08000410e" + "200128021828020c11818080800080808080000b6001017f23808080800041" + "306b22002480808080002000410136020c200041b480c08000360208200042" + "013702142000418880808000ad4220862000412f6aad843703202000200041" + "206a360210200041086a41cc8fc08000108980808000000b4701017f238080" + "80800041206b2200248080808000200041003602182000410136020c200041" + "bc88c0800036020820004204370210200041086a41c488c080001089808080" + "00000bcb2502087f017e02400240024002400240024002400240200041f501" + "490d0041002101200041cdff7b4f0d052000410b6a22014178712102410028" + "02f499c080002203450d04411f21040240200041f4ffff074b0d0020024106" + "20014108766722006b7641017120004101746b413e6a21040b410020026b21" + "010240200441027441d896c080006a28020022050d0041002100410021060c" + "020b4100210020024100411920044101766b2004411f461b74210741002106" + "034002402005220528020441787122082002490d00200820026b220820014f" + "0d00200821012005210620080d004100210120052106200521000c040b2005" + "28021422082000200820052007411d764104716a41106a2802002205471b20" + "0020081b2100200741017421072005450d020c000b0b024041002802f099c0" + "8000220541102000410b6a41f803712000410b491b22024103762201762200" + "410371450d00024002402000417f7341017120016a2207410374220041e897" + "c080006a2201200041f097c080006a28020022022802082206460d00200620" + "0136020c200120063602080c010b41002005417e200777713602f099c08000" + "0b20022000410372360204200220006a220020002802044101723602042002" + "41086a0f0b200241002802f899c080004d0d0302400240024020000d004100" + "2802f499c080002200450d0620006841027441d896c080006a280200220628" + "020441787120026b21012006210503400240200628021022000d0020062802" + "1422000d0020052802182104024002400240200528020c22002005470d0020" + "0541144110200528021422001b6a28020022060d01410021000c020b200528" + "02082206200036020c200020063602080c010b200541146a200541106a2000" + "1b21070340200721082006220041146a200041106a200028021422061b2107" + "20004114411020061b6a28020022060d000b200841003602000b2004450d04" + "0240200528021c41027441d896c080006a22062802002005460d0020044110" + "411420042802102005461b6a20003602002000450d050c040b200620003602" + "0020000d03410041002802f499c08000417e200528021c77713602f499c080" + "000c040b200028020441787120026b22062001200620014922061b21012000" + "200520061b2105200021060c000b0b02400240200020017441022001742200" + "410020006b7271682208410374220141e897c080006a2206200141f097c080" + "006a28020022002802082207460d002007200636020c200620073602080c01" + "0b41002005417e200877713602f099c080000b200020024103723602042000" + "20026a2207200120026b2206410172360204200020016a2006360200024041" + "002802f899c080002205450d00200541787141e897c080006a210141002802" + "809ac0800021020240024041002802f099c080002208410120054103767422" + "05710d00410020082005723602f099c08000200121050c010b200128020821" + "050b200120023602082005200236020c2002200136020c200220053602080b" + "410020073602809ac08000410020063602f899c08000200041086a0f0b2000" + "2004360218024020052802102206450d002000200636021020062000360218" + "0b20052802142206450d0020002006360214200620003602180b0240024002" + "4020014110490d0020052002410372360204200520026a2202200141017236" + "0204200220016a200136020041002802f899c080002207450d012007417871" + "41e897c080006a210641002802809ac0800021000240024041002802f099c0" + "8000220841012007410376742207710d00410020082007723602f099c08000" + "200621070c010b200628020821070b200620003602082007200036020c2000" + "200636020c200020073602080c010b2005200120026a220041037236020420" + "0520006a220020002802044101723602040c010b410020023602809ac08000" + "410020013602f899c080000b200541086a0f0b024020002006720d00410021" + "0641022004742200410020006b722003712200450d0320006841027441d896" + "c080006a28020021000b2000450d010b034020002006200028020441787122" + "0520026b220820014922041b2103200520024921072008200120041b210802" + "40200028021022050d00200028021421050b2006200320071b210620012008" + "20071b21012005210020050d000b0b2006450d00024041002802f899c08000" + "22002002490d002001200020026b4f0d010b20062802182104024002400240" + "200628020c22002006470d00200641144110200628021422001b6a28020022" + "050d01410021000c020b20062802082205200036020c200020053602080c01" + "0b200641146a200641106a20001b21070340200721082005220041146a2000" + "41106a200028021422051b210720004114411020051b6a28020022050d000b" + "200841003602000b2004450d030240200628021c41027441d896c080006a22" + "052802002006460d0020044110411420042802102006461b6a200036020020" + "00450d040c030b2005200036020020000d02410041002802f499c08000417e" + "200628021c77713602f499c080000c030b0240024002400240024002404100" + "2802f899c08000220020024f0d00024041002802fc99c08000220020024b0d" + "0041002101200241af80046a220641107640002200417f4622070d07200041" + "10742205450d07410041002802889ac08000410020064180807c7120071b22" + "086a22003602889ac080004100410028028c9ac0800022012000200120004b" + "1b36028c9ac0800002400240024041002802849ac080002201450d0041d897" + "c080002100034020002802002206200028020422076a2005460d0220002802" + "0822000d000c030b0b0240024041002802949ac080002200450d0020002005" + "4d0d010b410020053602949ac080000b410041ff1f3602989ac08000410020" + "083602dc97c08000410020053602d897c08000410041e897c080003602f497" + "c08000410041f097c080003602fc97c08000410041e897c080003602f097c0" + "8000410041f897c0800036028498c08000410041f097c080003602f897c080" + "004100418098c0800036028c98c08000410041f897c0800036028098c08000" + "4100418898c0800036029498c080004100418098c0800036028898c0800041" + "00419098c0800036029c98c080004100418898c0800036029098c080004100" + "419898c080003602a498c080004100419098c0800036029898c08000410041" + "a098c080003602ac98c080004100419898c080003602a098c0800041004100" + "3602e497c08000410041a898c080003602b498c08000410041a098c0800036" + "02a898c08000410041a898c080003602b098c08000410041b098c080003602" + "bc98c08000410041b098c080003602b898c08000410041b898c080003602c4" + "98c08000410041b898c080003602c098c08000410041c098c080003602cc98" + "c08000410041c098c080003602c898c08000410041c898c080003602d498c0" + "8000410041c898c080003602d098c08000410041d098c080003602dc98c080" + "00410041d098c080003602d898c08000410041d898c080003602e498c08000" + "410041d898c080003602e098c08000410041e098c080003602ec98c0800041" + "0041e098c080003602e898c08000410041e898c080003602f498c080004100" + "41f098c080003602fc98c08000410041e898c080003602f098c08000410041" + "f898c0800036028499c08000410041f098c080003602f898c0800041004180" + "99c0800036028c99c08000410041f898c0800036028099c080004100418899" + "c0800036029499c080004100418099c0800036028899c080004100419099c0" + "800036029c99c080004100418899c0800036029099c080004100419899c080" + "003602a499c080004100419099c0800036029899c08000410041a099c08000" + "3602ac99c080004100419899c080003602a099c08000410041a899c0800036" + "02b499c08000410041a099c080003602a899c08000410041b099c080003602" + "bc99c08000410041a899c080003602b099c08000410041b899c080003602c4" + "99c08000410041b099c080003602b899c08000410041c099c080003602cc99" + "c08000410041b899c080003602c099c08000410041c899c080003602d499c0" + "8000410041c099c080003602c899c08000410041d099c080003602dc99c080" + "00410041c899c080003602d099c08000410041d899c080003602e499c08000" + "410041d099c080003602d899c08000410041e099c080003602ec99c0800041" + "0041d899c080003602e099c08000410020053602849ac08000410041e099c0" + "80003602e899c080004100200841586a22003602fc99c08000200520004101" + "72360204200520006a4128360204410041808080013602909ac080000c080b" + "200120054f0d00200620014b0d00200028020c450d030b410041002802949a" + "c080002200200520002005491b3602949ac08000200520086a210641d897c0" + "800021000240024002400340200028020022072006460d0120002802082200" + "0d000c020b0b200028020c450d010b41d897c0800021000240034002402000" + "280200220620014b0d002001200620002802046a2206490d020b2000280208" + "21000c000b0b410020053602849ac080004100200841586a22003602fc99c0" + "800020052000410172360204200520006a4128360204410041808080013602" + "909ac080002001200641606a41787141786a22002000200141106a491b2207" + "411b36020441002902d897c080002109200741106a41002902e097c0800037" + "020020072009370208410020083602dc97c08000410020053602d897c08000" + "4100200741086a3602e097c08000410041003602e497c080002007411c6a21" + "00034020004107360200200041046a22002006490d000b20072001460d0720" + "072007280204417e713602042001200720016b220041017236020420072000" + "36020002402000418002490d002001200010a7808080000c080b200041f801" + "7141e897c080006a21060240024041002802f099c080002205410120004103" + "76742200710d00410020052000723602f099c08000200621000c010b200628" + "020821000b200620013602082000200136020c2001200636020c2001200036" + "02080c070b200020053602002000200028020420086a360204200520024103" + "723602042007410f6a41787141786a2201200520026a22006b210220014100" + "2802849ac08000460d03200141002802809ac08000460d0402402001280204" + "22064103714101470d0020012006417871220610a880808000200620026a21" + "02200120066a220128020421060b20012006417e7136020420002002410172" + "360204200020026a200236020002402002418002490d002000200210a78080" + "80000c060b200241f8017141e897c080006a21010240024041002802f099c0" + "8000220641012002410376742202710d00410020062002723602f099c08000" + "200121020c010b200128020821020b200120003602082002200036020c2000" + "200136020c200020023602080c050b4100200020026b22013602fc99c08000" + "410041002802849ac08000220020026a22063602849ac08000200620014101" + "7236020420002002410372360204200041086a21010c060b41002802809ac0" + "8000210102400240200020026b2206410f4b0d00410041003602809ac08000" + "410041003602f899c0800020012000410372360204200120006a2200200028" + "02044101723602040c010b410020063602f899c080004100200120026a2205" + "3602809ac0800020052006410172360204200120006a200636020020012002" + "4103723602040b200141086a0f0b2000200720086a36020441004100280284" + "9ac080002200410f6a417871220141786a22063602849ac080004100200020" + "016b41002802fc99c0800020086a22016a41086a22053602fc99c080002006" + "2005410172360204200020016a4128360204410041808080013602909ac080" + "000c030b410020003602849ac08000410041002802fc99c0800020026a2202" + "3602fc99c08000200020024101723602040c010b410020003602809ac08000" + "410041002802f899c0800020026a22023602f899c080002000200241017236" + "0204200020026a20023602000b200541086a0f0b4100210141002802fc99c0" + "8000220020024d0d004100200020026b22013602fc99c08000410041002802" + "849ac08000220020026a22063602849ac08000200620014101723602042000" + "2002410372360204200041086a0f0b20010f0b200020043602180240200628" + "02102205450d0020002005360210200520003602180b20062802142205450d" + "0020002005360214200520003602180b0240024020014110490d0020062002" + "410372360204200620026a22002001410172360204200020016a2001360200" + "02402001418002490d002000200110a7808080000c020b200141f8017141e8" + "97c080006a21020240024041002802f099c080002205410120014103767422" + "01710d00410020052001723602f099c08000200221010c010b200228020821" + "010b200220003602082001200036020c2000200236020c200020013602080c" + "010b2006200120026a2200410372360204200620006a220020002802044101" + "723602040b200641086a0b3000024020002802002d00000d002001418c83c0" + "800041051095808080000f0b2001419183c0800041041095808080000b1400" + "2001200028020420002802081095808080000b7001037f2000280204210102" + "40024020002d0000220041044b0d0020004103470d010b2001280200210002" + "40200141046a28020022022802002203450d00200020031180808080008080" + "8080000b024020022802042202450d00200020021088808080000b2001410c" + "1088808080000b0bab08010a7f23808080800041206b220424808080800002" + "40024002400240024020012802100d002001417f3602102003410020032002" + "41036a417c7120026b22056b41077120032005491b22066b21072003200649" + "0d0102402006450d0002400240200220036a2208417f6a22092d0000410a47" + "0d002006417f6a21060c010b200220076a220a2009460d0102402008417e6a" + "22092d0000410a470d002006417e6a21060c010b200a2009460d0102402008" + "417d6a22092d0000410a470d002006417d6a21060c010b200a2009460d0102" + "402008417c6a22092d0000410a470d002006417c6a21060c010b200a200946" + "0d0102402008417b6a22092d0000410a470d002006417b6a21060c010b200a" + "2009460d0102402008417a6a22092d0000410a470d002006417a6a21060c01" + "0b200a2009460d010240200841796a22092d0000410a470d00200641796a21" + "060c010b200a2009460d01200641787221060b200620076a41016a21060c04" + "0b20052003200320054b1b210b410020066b21082002417c6a210c2006417f" + "7320026a210a02400340200a21052008210620072209200b4d0d0120064178" + "6a2108200541786a210a41808284082002200941786a22076a280200220d41" + "8a94a8d000736b200d724180828408200c20096a280200220d418a94a8d000" + "736b200d727141808182847871418081828478460d000b0b200920034b0d02" + "02400340200320066a450d012006417f6a2106200520036a21092005417f6a" + "210520092d0000410a470d000b200320066a41016a21060c040b0240024020" + "01411c6a28020022060d00410021060c010b2006200141186a2802006a417f" + "6a2d0000410a470d0041002106200141003a00202001411c6a41003602000b" + "0240200128021420066b20034b0d002000200141146a2002200310a1808080" + "000c050b200128021820066a2002200310ad808080001a200041043a000020" + "01411c6a200620036a3602000c040b109a80808000000b20072003108f8080" + "8000000b20092003109480808000000b0240200320064f0d00200441003602" + "182004410136020c2004418c89c0800036020820044204370210200441086a" + "419489c08000108980808000000b02402001411c6a2802002205450d000240" + "0240200128021420056b20064d0d00200141186a28020020056a2002200610" + "ad808080001a2001411c6a200520066a22053602000c010b200441086a2001" + "41146a2002200610a180808000024020042d00084104460d00200020042903" + "083702000c030b2001411c6a28020021050b2005450d00200141003a002020" + "01411c6a41003602000b200220066a210502402001280214200320066b2206" + "4b0d002000200141146a2005200610a1808080000c010b200141186a280200" + "2005200610ad808080001a200041043a00002001411c6a20063602000b2001" + "200128021041016a360210200441206a2480808080000b7101027f20012802" + "002104024020012802082205450d00200420056b20034f0d00410021052001" + "4100360208200141003a000c0b0240200420034d0d00200128020420056a20" + "02200310ad808080001a200041043a00002001200520036a3602080f0b2000" + "4204370200200141003a000c0bc90103027f017e027f23808080800041106b" + "2203248080808000200341086a20002802082802002001200210a080808000" + "024020032d000822024104460d002000280204210420032903082105024002" + "4020002d0000220141044b0d0020014103470d010b20042802002101024020" + "0441046a28020022062802002207450d002001200711808080800080808080" + "000b024020062802042206450d00200120061088808080000b2004410c1088" + "808080000b200020053702000b200341106a24808080800020024104470b9c" + "0303027f017e037f23808080800041106b2202248080808000200241003602" + "0402400240024002402001418001490d002001418010490d01200141808004" + "4f0d0220022001413f71418001723a000620022001410c7641e001723a0004" + "20022001410676413f71418001723a0005410321010c030b200220013a0004" + "410121010c020b20022001413f71418001723a00052002200141067641c001" + "723a0004410221010c010b20022001413f71418001723a0007200220014112" + "7641f001723a000420022001410676413f71418001723a000620022001410c" + "76413f71418001723a0005410421010b200241086a20002802082802002002" + "41046a200110a080808000024020022d000822014104460d00200028020421" + "03200229030821040240024020002d0000220541044b0d0020054103470d01" + "0b200328020021050240200341046a28020022062802002207450d00200520" + "0711808080800080808080000b024020062802042206450d00200520061088" + "808080000b2003410c1088808080000b200020043702000b200241106a2480" + "8080800020014104470b1200200041c085c0800020011087808080000b0300" + "000b0900200041003602000bc30201047f411f21020240200141ffffff074b" + "0d002001410620014108766722026b7641017120024101746b413e6a21020b" + "200042003702102000200236021c200241027441d896c080006a2103024041" + "002802f499c0800041012002742204710d0020032000360200200020033602" + "182000200036020c20002000360208410041002802f499c080002004723602" + "f499c080000f0b024002400240200328020022042802044178712001470d00" + "200421020c010b20014100411920024101766b2002411f461b742103034020" + "042003411d764104716a41106a22052802002202450d022003410174210320" + "02210420022802044178712001470d000b0b20022802082203200036020c20" + "022000360208200041003602182000200236020c200020033602080f0b2005" + "2000360200200020043602182000200036020c200020003602080b82030104" + "7f200028020c21020240024002402001418002490d00200028021821030240" + "0240024020022000470d00200041144110200028021422021b6a2802002201" + "0d01410021020c020b20002802082201200236020c200220013602080c010b" + "200041146a200041106a20021b21040340200421052001220241146a200241" + "106a200228021422011b210420024114411020011b6a28020022010d000b20" + "0541003602000b2003450d020240200028021c41027441d896c080006a2201" + "2802002000460d0020034110411420032802102000461b6a20023602002002" + "450d030c020b2001200236020020020d01410041002802f499c08000417e20" + "0028021c77713602f499c080000c020b0240200220002802082204460d0020" + "04200236020c200220043602080f0b410041002802f099c08000417e200141" + "037677713602f099c080000f0b20022003360218024020002802102201450d" + "0020022001360210200120023602180b20002802142201450d002002200136" + "0214200120023602180f0b0b0b00200010aa80808000000bb50101037f2380" + "8080800041106b2201248080808000200028020c2102024002400240024020" + "002802040e020001020b20020d0141012102410021030c020b20020d002000" + "28020022022802042103200228020021020c010b2001418080808078360200" + "2001200036020c2001418980808000200028021c22002d001c20002d001d10" + "ab80808000000b20012003360204200120023602002001418a808080002000" + "28021c22002d001c20002d001d10ab80808000000b990101027f2380808080" + "0041106b2204248080808000410041002802cc96c08000220541016a3602cc" + "96c08000024020054100480d000240024041002d00a09ac080000d00410041" + "0028029c9ac0800041016a36029c9ac0800041002802c896c08000417f4a0d" + "010c020b200441086a200020011183808080008080808000000b410041003a" + "00a09ac080002002450d0010a580808000000b000b0c002000200129020037" + "03000bc10201087f02400240200241104f0d00200021030c010b2000410020" + "006b41037122046a210502402004450d002000210320012106034020032006" + "2d00003a0000200641016a2106200341016a22032005490d000b0b20052002" + "20046b2207417c7122086a210302400240200120046a2209410371450d0020" + "084101480d012009410374220641187121022009417c71220a41046a210141" + "0020066b4118712104200a2802002106034020052006200276200128020022" + "0620047472360200200141046a2101200541046a22052003490d000c020b0b" + "20084101480d0020092101034020052001280200360200200141046a210120" + "0541046a22052003490d000b0b20074103712102200920086a21010b024020" + "02450d00200320026a21050340200320012d00003a0000200141016a210120" + "0341016a22032005490d000b0b20000b0b96160100418080c0000b8c160a00" + "000001000000000000000000100001000000426f72726f774d75744572726f" + "72616c726561647920626f72726f7765643a2022001000120000003d3d6173" + "73657274696f6e20606c6566742020726967687460206661696c65640a2020" + "6c6566743a200a2072696768743a2000003e001000100000004e0010001700" + "0000650010000900000020726967687460206661696c65643a200a20206c65" + "66743a200000003e0010001000000088001000100000009800100009000000" + "65001000090000003030303130323033303430353036303730383039313031" + "31313231333134313531363137313831393230323132323233323432353236" + "32373238323933303331333233333334333533363337333833393430343134" + "32343334343435343634373438343935303531353235333534353535363537" + "35383539363036313632363336343635363636373638363937303731373237" + "33373437353736373737383739383038313832383338343835383638373838" + "3839393039313932393339343935393639373938393966616c736574727565" + "636f72652f7372632f736c6963652f6d656d6368722e727300000095011000" + "18000000830000001e00000095011000180000009f0000000900000072616e" + "676520737461727420696e64657820206f7574206f662072616e676520666f" + "7220736c696365206f66206c656e67746820d001100012000000e201100022" + "00000072616e676520656e6420696e646578201402100010000000e2011000" + "220000007265656e7472616e7420696e69740000340210000e0000002f7275" + "7374632f633266373463336639323861656235303366313562346539656635" + "373738653737663330353862382f6c6962726172792f636f72652f7372632f" + "63656c6c2f6f6e63652e72730000004c0210004d0000002301000042000000" + "000000000000000004000000040000000b0000000c0000000c000000040000" + "000d0000000e0000000f0000002f727573742f646570732f646c6d616c6c6f" + "632d302e322e362f7372632f646c6d616c6c6f632e7273617373657274696f" + "6e206661696c65643a207073697a65203e3d2073697a65202b206d696e5f6f" + "7665726865616400d802100029000000a80400000900000061737365727469" + "6f6e206661696c65643a207073697a65203c3d2073697a65202b206d61785f" + "6f766572686561640000d802100029000000ae0400000d000000757365206f" + "66207374643a3a7468726561643a3a63757272656e742829206973206e6f74" + "20706f737369626c6520616674657220746865207468726561642773206c6f" + "63616c206461746120686173206265656e2064657374726f7965647374642f" + "7372632f7468726561642f6d6f642e727300de03100015000000f102000013" + "0000006661696c656420746f2067656e657261746520756e69717565207468" + "726561642049443a2062697473706163652065786861757374656400040410" + "0037000000de03100015000000c40400000d00000001000000000000007374" + "642f7372632f696f2f62756666657265642f6c696e65777269746572736869" + "6d2e72736d6964203e206c656e000081041000090000005c04100025000000" + "0f01000029000000656e74697479206e6f7420666f756e647065726d697373" + "696f6e2064656e696564636f6e6e656374696f6e2072656675736564636f6e" + "6e656374696f6e207265736574686f737420756e726561636861626c656e65" + "74776f726b20756e726561636861626c65636f6e6e656374696f6e2061626f" + "727465646e6f7420636f6e6e65637465646164647265737320696e20757365" + "61646472657373206e6f7420617661696c61626c656e6574776f726b20646f" + "776e62726f6b656e2070697065656e7469747920616c726561647920657869" + "7374736f7065726174696f6e20776f756c6420626c6f636b6e6f7420612064" + "69726563746f727969732061206469726563746f72796469726563746f7279" + "206e6f7420656d707479726561642d6f6e6c792066696c6573797374656d20" + "6f722073746f72616765206d656469756d66696c6573797374656d206c6f6f" + "70206f7220696e646972656374696f6e206c696d69742028652e672e207379" + "6d6c696e6b206c6f6f70297374616c65206e6574776f726b2066696c652068" + "616e646c65696e76616c696420696e70757420706172616d65746572696e76" + "616c6964206461746174696d6564206f75747772697465207a65726f6e6f20" + "73746f726167652073706163657365656b206f6e20756e7365656b61626c65" + "2066696c6566696c6573797374656d2071756f746120657863656564656466" + "696c6520746f6f206c617267657265736f7572636520627573796578656375" + "7461626c652066696c652062757379646561646c6f636b63726f73732d6465" + "76696365206c696e6b206f722072656e616d65746f6f206d616e79206c696e" + "6b73696e76616c69642066696c656e616d65617267756d656e74206c697374" + "20746f6f206c6f6e676f7065726174696f6e20696e74657272757074656475" + "6e737570706f72746564756e657870656374656420656e64206f662066696c" + "656f7574206f66206d656d6f72796f74686572206572726f72756e63617465" + "676f72697a6564206572726f7220286f73206572726f722029000000010000" + "0000000000910710000b0000009c071000010000007374642f7372632f696f" + "2f737464696f2e727300b8071000130000002c030000140000006661696c65" + "64207072696e74696e6720746f203a20000000dc07100013000000ef071000" + "02000000b8071000130000005d040000090000007374646f75747374642f73" + "72632f696f2f6d6f642e72736120666f726d617474696e6720747261697420" + "696d706c656d656e746174696f6e2072657475726e656420616e206572726f" + "72207768656e2074686520756e6465726c79696e672073747265616d206469" + "64206e6f740000002b081000560000001a0810001100000028070000150000" + "0063616e6e6f74207265637572736976656c792061637175697265206d7574" + "65789c081000200000007374642f7372632f7379732f73796e632f6d757465" + "782f6e6f5f746872656164732e7273c4081000240000001400000009000000" + "7374642f7372632f73796e632f6f6e63652e7273f808100014000000d90000" + "00140000006c6f636b20636f756e74206f766572666c6f7720696e20726565" + "6e7472616e74206d757465787374642f7372632f73796e632f7265656e7472" + "616e745f6c6f636b2e7273420910001e000000220100002d0000006f706572" + "6174696f6e207375636365737366756c6f6e652d74696d6520696e69746961" + "6c697a6174696f6e206d6179206e6f7420626520706572666f726d65642072" + "65637572736976656c79840910003800000010000000110000001200000010" + "0000001000000013000000120000000d0000000e000000150000000c000000" + "0b00000015000000150000000f0000000e0000001300000026000000380000" + "0019000000170000000c000000090000000a00000010000000170000001900" + "00000e0000000d00000014000000080000001b0000000e0000001000000016" + "000000150000000b000000160000000d0000000b00000013000000a4041000" + "b4041000c5041000d7041000e7041000f70410000a0510001c051000290510" + "00370510004c0510005805100063051000780510008d0510009c051000aa05" + "1000bd051000e30510001b061000340610004b06100057061000600610006a" + "0610007a06100091061000aa061000b8061000c5061000d9061000e1061000" + "fc0610000a0710001a07100030071000450710005007100066071000730710" + "007e071000009118046e616d65000e0d7761736d5f6c69622e7761736d01d9" + "172e0005726563757201625f5a4e34636f726533666d74336e756d33696d70" + "35325f244c5424696d706c2475323024636f72652e2e666d742e2e44697370" + "6c61792475323024666f7224753230246933322447542433666d7431376863" + "6564393063376136333963303164644502495f5a4e337374643473796e6339" + "6f6e63655f6c6f636b31374f6e63654c6f636b244c54245424475424313069" + "6e697469616c697a6531376837663563353038646139653162303962450342" + "5f5a4e34636f72653463656c6c346f6e636531374f6e636543656c6c244c54" + "245424475424387472795f696e697431376863653633626632323835313931" + "65373145043e5f5a4e35616c6c6f633473796e633136417263244c54245424" + "432441244754243964726f705f736c6f773137686565396163636361643963" + "63313036394505355f5a4e34636f72653970616e69636b696e673133617373" + "6572745f6661696c6564313768323332363266326333633738623661624506" + "325f5a4e34636f7265366f7074696f6e31336578706563745f6661696c6564" + "313768663038613939653264373333366336614507265f5a4e34636f726533" + "666d743577726974653137683933353534653462653731663263376145080e" + "5f5f727573745f6465616c6c6f6309305f5a4e34636f72653970616e69636b" + "696e673970616e69635f666d74313768363534306363623264356664633361" + "62450a595f5a4e36305f244c54247374642e2e696f2e2e6572726f722e2e45" + "72726f72247532302461732475323024636f72652e2e666d742e2e44697370" + "6c61792447542433666d743137683930323731633762326136636538333945" + "0b495f5a4e34345f244c54242452462454247532302461732475323024636f" + "72652e2e666d742e2e446973706c61792447542433666d7431376837666634" + "643062383630396332343732450c097265637572736976650d415f5a4e3864" + "6c6d616c6c6f6338646c6d616c6c6f633137446c6d616c6c6f63244c542441" + "24475424346672656531376833393833346161616165336538393436450e2c" + "5f5a4e34636f72653970616e69636b696e673570616e696331376830346565" + "623931376464393363323239450f445f5a4e34636f726535736c6963653569" + "6e6465783236736c6963655f73746172745f696e6465785f6c656e5f666169" + "6c313768663931613361666538376231643434334510625f5a4e34636f7265" + "33666d74336e756d33696d7035325f244c5424696d706c2475323024636f72" + "652e2e666d742e2e446973706c61792475323024666f722475323024753332" + "2447542433666d74313768626633653032323834383365333735614511305f" + "5a4e34636f726533666d74336e756d33696d7037666d745f75363431376864" + "353231666136656636613036373261451211727573745f626567696e5f756e" + "77696e6413465f5a4e34636f726533666d7439466f726d6174746572313270" + "61645f696e74656772616c313277726974655f707265666978313768613961" + "343332383062363030366431324514425f5a4e34636f726535736c69636535" + "696e6465783234736c6963655f656e645f696e6465785f6c656e5f6661696c" + "3137683038386235366532393962656161616645152e5f5a4e34636f726533" + "666d7439466f726d6174746572337061643137683437363961653338393337" + "346363353145163b5f5a4e34636f72653970616e69636b696e673139617373" + "6572745f6661696c65645f696e6e6572313768366637653332353764383461" + "353034324517475f5a4e34325f244c54242452462454247532302461732475" + "323024636f72652e2e666d742e2e44656275672447542433666d7431376833" + "6136626161316262343761643230344518585f5a4e35395f244c5424636f72" + "652e2e666d742e2e417267756d656e7473247532302461732475323024636f" + "72652e2e666d742e2e446973706c61792447542433666d7431376836386133" + "65386535303963616663363445195c5f5a4e36335f244c5424636f72652e2e" + "63656c6c2e2e426f72726f774d75744572726f722475323024617324753230" + "24636f72652e2e666d742e2e44656275672447542433666d74313768313564" + "33643334333462646463636338451a395f5a4e34636f72653463656c6c3232" + "70616e69635f616c72656164795f626f72726f776564313768333134623532" + "61316263343662666534451b395f5a4e337374643674687265616438546872" + "6561644964336e657739657868617573746564313768333336626637613134" + "38383034346338451c435f5a4e38646c6d616c6c6f6338646c6d616c6c6f63" + "3137446c6d616c6c6f63244c54244124475424366d616c6c6f633137686536" + "3539333961346338393763633135451d475f5a4e34325f244c542424524624" + "54247532302461732475323024636f72652e2e666d742e2e44656275672447" + "542433666d7431376865313837343338386530376266653235451e595f5a4e" + "36305f244c5424616c6c6f632e2e737472696e672e2e537472696e67247532" + "302461732475323024636f72652e2e666d742e2e446973706c617924475424" + "33666d7431376863653432323661613166373236633163451f7a5f5a4e3463" + "6f726533707472383864726f705f696e5f706c616365244c54247374642e2e" + "696f2e2e57726974652e2e77726974655f666d742e2e41646170746572244c" + "5424616c6c6f632e2e7665632e2e566563244c542475382447542424475424" + "24475424313768313636646336316162303333346331654520605f5a4e3631" + "5f244c54247374642e2e696f2e2e737464696f2e2e5374646f75744c6f636b" + "2475323024617324753230247374642e2e696f2e2e57726974652447542439" + "77726974655f616c6c31376832346238323631303436316432353666452155" + "5f5a4e3373746432696f386275666665726564396275667772697465723138" + "427566577269746572244c54245724475424313477726974655f616c6c5f63" + "6f6c64313768353834626462626165623066623162624522735f5a4e38305f" + "244c54247374642e2e696f2e2e57726974652e2e77726974655f666d742e2e" + "41646170746572244c54245424475424247532302461732475323024636f72" + "652e2e666d742e2e5772697465244754243977726974655f73747231376837" + "6661636635626330656663643830384523325f5a4e34636f726533666d7435" + "5772697465313077726974655f636861723137686630623362653165633139" + "64653565374524305f5a4e34636f726533666d743557726974653977726974" + "655f666d743137686638383038663064663065343531336445250a72757374" + "5f70616e696326375f5a4e34636f72653570616e6963313250616e69635061" + "796c6f61643661735f73747231376836313439663134326439613265303265" + "4527505f5a4e38646c6d616c6c6f6338646c6d616c6c6f633137446c6d616c" + "6c6f63244c542441244754243138696e736572745f6c617267655f6368756e" + "6b3137686566653835316132373538326461376245284a5f5a4e38646c6d61" + "6c6c6f6338646c6d616c6c6f633137446c6d616c6c6f63244c542441244754" + "243132756e6c696e6b5f6368756e6b31376839333465336463333833626235" + "3861334529455f5a4e3373746433737973396261636b747261636532365f5f" + "727573745f656e645f73686f72745f6261636b747261636531376834646333" + "646534376432323032316239452a585f5a4e337374643970616e69636b696e" + "673139626567696e5f70616e69635f68616e646c657232385f247537622424" + "75376224636c6f737572652475376424247537642431376865313761333937" + "376638396331313738452b3b5f5a4e337374643970616e69636b696e673230" + "727573745f70616e69635f776974685f686f6f6b3137683737366537396339" + "6636353931626535452c83015f5a4e39395f244c54247374642e2e70616e69" + "636b696e672e2e626567696e5f70616e69635f68616e646c65722e2e537461" + "7469635374725061796c6f6164247532302461732475323024636f72652e2e" + "70616e69632e2e50616e69635061796c6f6164244754243661735f73747231" + "376865623366373232643232346534326638452d066d656d63707907120100" + "0f5f5f737461636b5f706f696e746572090a0100072e726f64617461005509" + "70726f64756365727302086c616e6775616765010452757374000c70726f63" + "65737365642d62790105727573746325312e38332e302d6e696768746c7920" + "2863326637346333663920323032342d30392d30392900490f746172676574" + "5f6665617475726573042b0a6d756c746976616c75652b0f6d757461626c65" + "2d676c6f62616c732b0f7265666572656e63652d74797065732b087369676e" + "2d657874"; diff --git a/src/test/app/wasm_fixtures/fixtures.h b/src/test/app/wasm_fixtures/fixtures.h new file mode 100644 index 0000000000..e7bada74a5 --- /dev/null +++ b/src/test/app/wasm_fixtures/fixtures.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 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. +*/ +//============================================================================== + +#pragma once + +// TODO: consider moving these to separate files (and figure out the build) + +#include + +extern std::string const ledgerSqnHex; + +extern std::string const allHostFunctionsHex; + +extern std::string const deepRecursionHex; diff --git a/src/test/consensus/ByzantineFailureSim_test.cpp b/src/test/consensus/ByzantineFailureSim_test.cpp index 887a060a5b..b979943477 100644 --- a/src/test/consensus/ByzantineFailureSim_test.cpp +++ b/src/test/consensus/ByzantineFailureSim_test.cpp @@ -68,8 +68,8 @@ class ByzantineFailureSim_test : public beast::unit_test::suite for (TrustGraph::ForkInfo const& fi : sim.trustGraph.forkablePairs(0.8)) { - std::cout << "Can fork " << PeerGroup{fi.unlA} << " " - << " " << PeerGroup{fi.unlB} << " overlap " << fi.overlap + std::cout << "Can fork " << PeerGroup{fi.unlA} << " " << " " + << PeerGroup{fi.unlB} << " overlap " << fi.overlap << " required " << fi.required << "\n"; }; diff --git a/src/test/jtx/TestHelpers.h b/src/test/jtx/TestHelpers.h index ae46ea4fe3..200fc614fb 100644 --- a/src/test/jtx/TestHelpers.h +++ b/src/test/jtx/TestHelpers.h @@ -354,6 +354,76 @@ public: } }; +struct finish_function +{ +private: + std::string value_; + +public: + explicit finish_function(std::string func) : value_(func) + { + } + + explicit finish_function(Slice const& func) : value_(strHex(func)) + { + } + + template + explicit finish_function(std::array const& f) + : finish_function(makeSlice(f)) + { + } + + void + operator()(Env&, JTx& jt) const + { + jt.jv[sfFinishFunction.jsonName] = value_; + } +}; + +struct data +{ +private: + std::string value_; + +public: + explicit data(std::string func) : value_(func) + { + } + + explicit data(Slice const& func) : value_(strHex(func)) + { + } + + template + explicit data(std::array const& f) : data(makeSlice(f)) + { + } + + void + operator()(Env&, JTx& jt) const + { + jt.jv[sfData.jsonName] = value_; + } +}; + +struct comp_allowance +{ +private: + std::uint32_t value_; + +public: + explicit comp_allowance(std::uint32_t const& value) : value_(value) + { + } + + void + operator()(Env&, JTx& jt) const + { + jt.jv[sfComputationAllowance.jsonName] = value_; + } +}; + /* Payment Channel */ /******************************************************************************/ diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 54538032f5..6138673484 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -220,9 +220,8 @@ public: getList_ = [blob = blob, sig, manifest, version](int interval) { // Build the contents of a version 1 format UNL file std::stringstream l; - l << "{\"blob\":\"" << blob << "\"" - << ",\"signature\":\"" << sig << "\"" - << ",\"manifest\":\"" << manifest << "\"" + l << "{\"blob\":\"" << blob << "\"" << ",\"signature\":\"" << sig + << "\"" << ",\"manifest\":\"" << manifest << "\"" << ",\"refresh_interval\": " << interval << ",\"version\":" << version << '}'; return l.str(); @@ -257,15 +256,14 @@ public: std::stringstream l; for (auto const& info : blobInfo) { - l << "{\"blob\":\"" << info.blob << "\"" - << ",\"signature\":\"" << info.signature << "\"},"; + l << "{\"blob\":\"" << info.blob << "\"" << ",\"signature\":\"" + << info.signature << "\"},"; } std::string blobs = l.str(); blobs.pop_back(); l.str(std::string()); l << "{\"blobs_v2\": [ " << blobs << "],\"manifest\":\"" << manifest - << "\"" - << ",\"refresh_interval\": " << interval + << "\"" << ",\"refresh_interval\": " << interval << ",\"version\":" << (version + 1) << '}'; return l.str(); }; diff --git a/src/test/jtx/impl/envconfig.cpp b/src/test/jtx/impl/envconfig.cpp index dd9c735465..9838de80bb 100644 --- a/src/test/jtx/impl/envconfig.cpp +++ b/src/test/jtx/impl/envconfig.cpp @@ -33,9 +33,14 @@ setupConfigForUnitTests(Config& cfg) using namespace jtx; // Default fees to old values, so tests don't have to worry about changes in // Config.h + // NOTE: For new `FEES` fields, you need to wait for the first flag ledger + // to close for the values to be activated. cfg.FEES.reference_fee = UNIT_TEST_REFERENCE_FEE; cfg.FEES.account_reserve = XRP(200).value().xrp().drops(); cfg.FEES.owner_reserve = XRP(50).value().xrp().drops(); + cfg.FEES.extension_compute_limit = 1'000'000; + cfg.FEES.extension_size_limit = 100'000; + cfg.FEES.gas_price = 1'000'000; // 1 drop = 1,000,000 micro-drops // The Beta API (currently v2) is always available to tests cfg.BETA_RPC_API = true; diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index 18aebbe194..acd7ca8185 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -893,9 +893,8 @@ protected: printPeers(std::string const& msg, std::uint16_t validator = 0) { auto peers = network_.overlay().getPeers(network_.validator(validator)); - std::cout << msg << " " - << "num peers " << (int)network_.overlay().getNumPeers() - << std::endl; + std::cout << msg << " " << "num peers " + << (int)network_.overlay().getNumPeers() << std::endl; for (auto& [k, v] : peers) std::cout << k << ":" << (int)std::get(v) << " "; diff --git a/src/test/rpc/Subscribe_test.cpp b/src/test/rpc/Subscribe_test.cpp index 3d1b425422..3c5bcc9d94 100644 --- a/src/test/rpc/Subscribe_test.cpp +++ b/src/test/rpc/Subscribe_test.cpp @@ -522,6 +522,22 @@ public: if (jv.isMember(jss::reserve_inc) != isFlagLedger) return false; + if (env.closed()->rules().enabled(featureSmartEscrow)) + { + if (jv.isMember(jss::extension_compute) != isFlagLedger) + return false; + + if (jv.isMember(jss::extension_size) != isFlagLedger) + return false; + } + else + { + if (jv.isMember(jss::extension_compute)) + return false; + + if (jv.isMember(jss::extension_size)) + return false; + } return true; }; @@ -1305,14 +1321,14 @@ public: { using namespace test::jtx; FeatureBitset const all{supported_amendments()}; - FeatureBitset const xrpFees{featureXRPFees}; testServer(); testLedger(); testTransactions_APIv1(); testTransactions_APIv2(); testManifests(); - testValidations(all - xrpFees); + testValidations(all - featureXRPFees - featureSmartEscrow); + testValidations(all - featureSmartEscrow); testValidations(all); testSubErrors(true); testSubErrors(false); diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index 3cdf0ab1a7..9f2bc57de3 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -221,6 +221,15 @@ Ledger::Ledger( sle->at(sfReserveIncrement) = *f; sle->at(sfReferenceFeeUnits) = Config::FEE_UNITS_DEPRECATED; } + if (std::find( + amendments.begin(), amendments.end(), featureSmartEscrow) != + amendments.end()) + { + sle->at(sfExtensionComputeLimit) = + config.FEES.extension_compute_limit; + sle->at(sfExtensionSizeLimit) = config.FEES.extension_size_limit; + sle->at(sfGasPrice) = config.FEES.gas_price; + } rawInsert(sle); } @@ -612,6 +621,7 @@ Ledger::setup() { bool oldFees = false; bool newFees = false; + bool extensionFees = false; { auto const baseFee = sle->at(~sfBaseFee); auto const reserveBase = sle->at(~sfReserveBase); @@ -629,6 +639,7 @@ Ledger::setup() auto const reserveBaseXRP = sle->at(~sfReserveBaseDrops); auto const reserveIncrementXRP = sle->at(~sfReserveIncrementDrops); + auto assign = [&ret]( XRPAmount& dest, std::optional const& src) { @@ -645,12 +656,35 @@ Ledger::setup() assign(fees_.increment, reserveIncrementXRP); newFees = baseFeeXRP || reserveBaseXRP || reserveIncrementXRP; } + { + auto const extensionComputeLimit = + sle->at(~sfExtensionComputeLimit); + auto const extensionSizeLimit = sle->at(~sfExtensionSizeLimit); + auto const gasPrice = sle->at(~sfGasPrice); + + auto assign = [](std::uint32_t& dest, + std::optional const& src) { + if (src) + { + dest = src.value(); + } + }; + assign(fees_.extensionComputeLimit, extensionComputeLimit); + assign(fees_.extensionSizeLimit, extensionSizeLimit); + assign(fees_.gasPrice, gasPrice); + extensionFees = + extensionComputeLimit || extensionSizeLimit || gasPrice; + } if (oldFees && newFees) // Should be all of one or the other, but not both ret = false; if (!rules_.enabled(featureXRPFees) && newFees) // Can't populate the new fees before the amendment is enabled ret = false; + if (!rules_.enabled(featureSmartEscrow) && extensionFees) + // Can't populate the extension fees before the amendment is + // enabled + ret = false; } } catch (SHAMapMissingNode const&) @@ -669,15 +703,20 @@ Ledger::setup() void Ledger::defaultFees(Config const& config) { - XRPL_ASSERT( - fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0, - "ripple::Ledger::defaultFees : zero fees"); + assert( + fees_.base == 0 && fees_.reserve == 0 && fees_.increment == 0 && + fees_.extensionComputeLimit == 0 && fees_.extensionSizeLimit == 0 && + fees_.gasPrice == 0); if (fees_.base == 0) fees_.base = config.FEES.reference_fee; if (fees_.reserve == 0) fees_.reserve = config.FEES.account_reserve; - if (fees_.increment == 0) - fees_.increment = config.FEES.owner_reserve; + if (fees_.extensionComputeLimit == 0) + fees_.extensionComputeLimit = config.FEES.extension_compute_limit; + if (fees_.extensionSizeLimit == 0) + fees_.extensionSizeLimit = config.FEES.extension_size_limit; + if (fees_.gasPrice == 0) + fees_.gasPrice = config.FEES.gas_price; } std::shared_ptr diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index ccec209bd4..94a1a6ad80 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -225,30 +225,30 @@ log_metadata_difference( { JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different result and index!"; - JLOG(j.debug()) << " Built:" - << " Result: " << builtMetaData->getResult() - << " Index: " << builtMetaData->getIndex(); - JLOG(j.debug()) << " Valid:" - << " Result: " << validMetaData->getResult() - << " Index: " << validMetaData->getIndex(); + JLOG(j.debug()) + << " Built:" << " Result: " << builtMetaData->getResult() + << " Index: " << builtMetaData->getIndex(); + JLOG(j.debug()) + << " Valid:" << " Result: " << validMetaData->getResult() + << " Index: " << validMetaData->getIndex(); } else if (result_diff) { JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different result!"; - JLOG(j.debug()) << " Built:" - << " Result: " << builtMetaData->getResult(); - JLOG(j.debug()) << " Valid:" - << " Result: " << validMetaData->getResult(); + JLOG(j.debug()) + << " Built:" << " Result: " << builtMetaData->getResult(); + JLOG(j.debug()) + << " Valid:" << " Result: " << validMetaData->getResult(); } else if (index_diff) { JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different index!"; - JLOG(j.debug()) << " Built:" - << " Index: " << builtMetaData->getIndex(); - JLOG(j.debug()) << " Valid:" - << " Index: " << validMetaData->getIndex(); + JLOG(j.debug()) + << " Built:" << " Index: " << builtMetaData->getIndex(); + JLOG(j.debug()) + << " Valid:" << " Index: " << validMetaData->getIndex(); } } else @@ -267,12 +267,12 @@ log_metadata_difference( JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different result and nodes!"; JLOG(j.debug()) - << " Built:" - << " Result: " << builtMetaData->getResult() << " Nodes:\n" + << " Built:" << " Result: " << builtMetaData->getResult() + << " Nodes:\n" << builtNodes.getJson(JsonOptions::none); JLOG(j.debug()) - << " Valid:" - << " Result: " << validMetaData->getResult() << " Nodes:\n" + << " Valid:" << " Result: " << validMetaData->getResult() + << " Nodes:\n" << validNodes.getJson(JsonOptions::none); } else if (index_diff) @@ -280,23 +280,21 @@ log_metadata_difference( JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different index and nodes!"; JLOG(j.debug()) - << " Built:" - << " Index: " << builtMetaData->getIndex() << " Nodes:\n" + << " Built:" << " Index: " << builtMetaData->getIndex() + << " Nodes:\n" << builtNodes.getJson(JsonOptions::none); JLOG(j.debug()) - << " Valid:" - << " Index: " << validMetaData->getIndex() << " Nodes:\n" + << " Valid:" << " Index: " << validMetaData->getIndex() + << " Nodes:\n" << validNodes.getJson(JsonOptions::none); } else // nodes_diff { JLOG(j.debug()) << "MISMATCH on TX " << tx << ": Different nodes!"; - JLOG(j.debug()) << " Built:" - << " Nodes:\n" + JLOG(j.debug()) << " Built:" << " Nodes:\n" << builtNodes.getJson(JsonOptions::none); - JLOG(j.debug()) << " Valid:" - << " Nodes:\n" + JLOG(j.debug()) << " Valid:" << " Nodes:\n" << validNodes.getJson(JsonOptions::none); } } @@ -353,10 +351,10 @@ LedgerHistory::handleMismatch( if (!builtLedger || !validLedger) { - JLOG(j_.error()) << "MISMATCH cannot be analyzed:" - << " builtLedger: " << to_string(built) << " -> " - << builtLedger << " validLedger: " << to_string(valid) - << " -> " << validLedger; + JLOG(j_.error()) << "MISMATCH cannot be analyzed:" << " builtLedger: " + << to_string(built) << " -> " << builtLedger + << " validLedger: " << to_string(valid) << " -> " + << validLedger; return; } diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp index e81ec6574d..0961488691 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp @@ -100,8 +100,8 @@ TimeoutCounter::invokeOnTimer() if (!progress_) { ++timeouts_; - JLOG(journal_.debug()) << "Timeout(" << timeouts_ << ") " - << " acquiring " << hash_; + JLOG(journal_.debug()) + << "Timeout(" << timeouts_ << ") " << " acquiring " << hash_; onTimer(false, sl); } else diff --git a/src/xrpld/app/main/GRPCServer.cpp b/src/xrpld/app/main/GRPCServer.cpp index a4bbcda0a5..2ee811dc19 100644 --- a/src/xrpld/app/main/GRPCServer.cpp +++ b/src/xrpld/app/main/GRPCServer.cpp @@ -447,8 +447,8 @@ GRPCServerImpl::handleRpcs() if (!ok) { - JLOG(journal_.debug()) << "Request listener cancelled. " - << "Destroying object"; + JLOG(journal_.debug()) + << "Request listener cancelled. " << "Destroying object"; erase(ptr); } else diff --git a/src/xrpld/app/misc/AMMHelpers.h b/src/xrpld/app/misc/AMMHelpers.h index 97554b7e15..f27d542e32 100644 --- a/src/xrpld/app/misc/AMMHelpers.h +++ b/src/xrpld/app/misc/AMMHelpers.h @@ -382,9 +382,9 @@ changeSpotPriceQuality( { JLOG(j.error()) << "changeSpotPriceQuality failed: " << to_string(pool.in) - << " " << to_string(pool.out) << " " - << " " << quality << " " << tfee << " " - << to_string(amounts.in) << " " << to_string(amounts.out); + << " " << to_string(pool.out) << " " << " " << quality + << " " << tfee << " " << to_string(amounts.in) << " " + << to_string(amounts.out); Throw("changeSpotPriceQuality failed"); } else diff --git a/src/xrpld/app/misc/FeeVoteImpl.cpp b/src/xrpld/app/misc/FeeVoteImpl.cpp index 85b5791d67..ba8b1d5d5c 100644 --- a/src/xrpld/app/misc/FeeVoteImpl.cpp +++ b/src/xrpld/app/misc/FeeVoteImpl.cpp @@ -28,10 +28,10 @@ namespace ripple { namespace detail { +template class VotableValue { private: - using value_type = XRPAmount; value_type const current_; // The current setting value_type const target_; // The setting we want std::map voteMap_; @@ -66,8 +66,9 @@ public: getVotes() const; }; -auto -VotableValue::getVotes() const -> std::pair +template +std::pair +VotableValue::getVotes() const { value_type ourVote = current_; int weight = 0; @@ -190,6 +191,33 @@ FeeVoteImpl::doValidation( "reserve increment", sfReserveIncrement); } + if (rules.enabled(featureSmartEscrow)) + { + auto vote = [&v, this]( + auto const current, + std::uint32_t target, + char const* name, + auto const& sfield) { + if (current != target) + { + JLOG(journal_.info()) + << "Voting for " << name << " of " << target; + + v[sfield] = target; + } + }; + vote( + lastFees.extensionComputeLimit, + target_.extension_compute_limit, + "extension compute limit", + sfExtensionComputeLimit); + vote( + lastFees.extensionSizeLimit, + target_.extension_size_limit, + "extension size limit", + sfExtensionSizeLimit); + vote(lastFees.gasPrice, target_.gas_price, "gas price", sfGasPrice); + } } void @@ -212,11 +240,22 @@ FeeVoteImpl::doVoting( detail::VotableValue incReserveVote( lastClosedLedger->fees().increment, target_.owner_reserve); + detail::VotableValue extensionComputeVote( + lastClosedLedger->fees().extensionComputeLimit, + target_.extension_compute_limit); + + detail::VotableValue extensionSizeVote( + lastClosedLedger->fees().extensionSizeLimit, + target_.extension_size_limit); + + detail::VotableValue gasPriceVote( + lastClosedLedger->fees().gasPrice, target_.gas_price); + auto const& rules = lastClosedLedger->rules(); if (rules.enabled(featureXRPFees)) { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, SF_AMOUNT const& xrpField) { if (auto const field = ~val->at(~xrpField); field && field->native()) @@ -245,7 +284,7 @@ FeeVoteImpl::doVoting( else { auto doVote = [](std::shared_ptr const& val, - detail::VotableValue& value, + detail::VotableValue& value, auto const& valueField) { if (auto const field = val->at(~valueField)) { @@ -276,6 +315,30 @@ FeeVoteImpl::doVoting( doVote(val, incReserveVote, sfReserveIncrement); } } + if (rules.enabled(featureSmartEscrow)) + { + auto doVote = [](std::shared_ptr const& val, + detail::VotableValue& value, + SF_UINT32 const& extensionField) { + if (auto const field = ~val->at(~extensionField); field) + { + value.addVote(field.value()); + } + else + { + value.noVote(); + } + }; + + for (auto const& val : set) + { + if (!val->isTrusted()) + continue; + doVote(val, extensionComputeVote, sfExtensionComputeLimit); + doVote(val, extensionSizeVote, sfExtensionSizeLimit); + doVote(val, gasPriceVote, sfGasPrice); + } + } // choose our positions // TODO: Use structured binding once LLVM 16 is the minimum supported @@ -284,11 +347,15 @@ FeeVoteImpl::doVoting( auto const baseFee = baseFeeVote.getVotes(); auto const baseReserve = baseReserveVote.getVotes(); auto const incReserve = incReserveVote.getVotes(); + auto const extensionCompute = extensionComputeVote.getVotes(); + auto const extensionSize = extensionSizeVote.getVotes(); + auto const gasPrice = gasPriceVote.getVotes(); auto const seq = lastClosedLedger->info().seq + 1; // add transactions to our position - if (baseFee.second || baseReserve.second || incReserve.second) + if (baseFee.second || baseReserve.second || incReserve.second || + extensionCompute.second || extensionSize.second || gasPrice.second) { JLOG(journal_.warn()) << "We are voting for a fee change: " << baseFee.first << "/" @@ -316,6 +383,12 @@ FeeVoteImpl::doVoting( incReserveVote.current()); obj[sfReferenceFeeUnits] = Config::FEE_UNITS_DEPRECATED; } + if (rules.enabled(featureSmartEscrow)) + { + obj[sfExtensionComputeLimit] = extensionCompute.first; + obj[sfExtensionSizeLimit] = extensionSize.first; + obj[sfGasPrice] = gasPrice.first; + } }); uint256 txID = feeTx.getTransactionID(); diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index c8197b2219..1a41baf488 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -2466,6 +2466,18 @@ NetworkOPsImp::pubValidation(std::shared_ptr const& val) reserveIncXRP && reserveIncXRP->native()) jvObj[jss::reserve_inc] = reserveIncXRP->xrp().jsonClipped(); + if (auto const extensionComputeLimit = + ~val->at(~sfExtensionComputeLimit); + extensionComputeLimit) + jvObj[jss::extension_compute] = *extensionComputeLimit; + + if (auto const extensionSizeLimit = ~val->at(~sfExtensionSizeLimit); + extensionSizeLimit) + jvObj[jss::extension_size] = *extensionSizeLimit; + + if (auto const gasPrice = ~val->at(~sfGasPrice); gasPrice) + jvObj[jss::gas_price] = *gasPrice; + // NOTE Use MultiApiJson to publish two slightly different JSON objects // for consumers supporting different API versions MultiApiJson multiObj{jvObj}; @@ -2915,12 +2927,22 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::seq] = Json::UInt(lpClosed->info().seq); l[jss::hash] = to_string(lpClosed->info().hash); + bool const smartEscrowEnabled = + m_ledgerMaster.getValidatedLedger()->rules().enabled( + featureSmartEscrow); if (!human) { l[jss::base_fee] = baseFee.jsonClipped(); l[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); l[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); + if (smartEscrowEnabled) + { + l[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + l[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + l[jss::gas_price] = lpClosed->fees().gasPrice; + } l[jss::close_time] = Json::Value::UInt( lpClosed->info().closeTime.time_since_epoch().count()); } @@ -2930,6 +2952,13 @@ NetworkOPsImp::getServerInfo(bool human, bool admin, bool counters) l[jss::reserve_base_xrp] = lpClosed->fees().accountReserve(0).decimalXRP(); l[jss::reserve_inc_xrp] = lpClosed->fees().increment.decimalXRP(); + if (smartEscrowEnabled) + { + l[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + l[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + l[jss::gas_price] = lpClosed->fees().gasPrice; + } if (auto const closeOffset = app_.timeKeeper().closeOffset(); std::abs(closeOffset.count()) >= 60) @@ -3120,6 +3149,14 @@ NetworkOPsImp::pubLedger(std::shared_ptr const& lpAccepted) lpAccepted->fees().accountReserve(0).jsonClipped(); jvObj[jss::reserve_inc] = lpAccepted->fees().increment.jsonClipped(); + if (lpAccepted->rules().enabled(featureSmartEscrow)) + { + jvObj[jss::extension_compute] = + lpAccepted->fees().extensionComputeLimit; + jvObj[jss::extension_size] = + lpAccepted->fees().extensionSizeLimit; + jvObj[jss::gas_price] = lpAccepted->fees().gasPrice; + } jvObj[jss::txn_count] = Json::UInt(alpAccepted->size()); @@ -3489,8 +3526,8 @@ NetworkOPsImp::pubAccountTransaction( } JLOG(m_journal.trace()) - << "pubAccountTransaction: " - << "proposed=" << iProposed << ", accepted=" << iAccepted; + << "pubAccountTransaction: " << "proposed=" << iProposed + << ", accepted=" << iAccepted; if (!notify.empty() || !accountHistoryNotify.empty()) { @@ -4170,6 +4207,13 @@ NetworkOPsImp::subLedger(InfoSub::ref isrListener, Json::Value& jvResult) jvResult[jss::reserve_base] = lpClosed->fees().accountReserve(0).jsonClipped(); jvResult[jss::reserve_inc] = lpClosed->fees().increment.jsonClipped(); + if (lpClosed->rules().enabled(featureSmartEscrow)) + { + jvResult[jss::extension_compute] = + lpClosed->fees().extensionComputeLimit; + jvResult[jss::extension_size] = lpClosed->fees().extensionSizeLimit; + jvResult[jss::gas_price] = lpClosed->fees().gasPrice; + } } if ((mMode >= OperatingMode::SYNCING) && !isNeedNetworkLedger()) diff --git a/src/xrpld/app/misc/WamrVM.cpp b/src/xrpld/app/misc/WamrVM.cpp new file mode 100644 index 0000000000..3cc18fdea8 --- /dev/null +++ b/src/xrpld/app/misc/WamrVM.cpp @@ -0,0 +1,840 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2020 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 + +#include + +#include + +namespace ripple { + +////////////////////////////////////////////////////////////////////////////////////////// + +namespace { + +static log_level_t +getLogLevel(beast::severities::Severity severity) +{ + using namespace beast::severities; + switch (severity) + { + case kTrace: + return WASM_LOG_LEVEL_VERBOSE; + case kDebug: + return WASM_LOG_LEVEL_DEBUG; + case kInfo: + case kWarning: + return WASM_LOG_LEVEL_WARNING; + case kError: + return WASM_LOG_LEVEL_ERROR; + default: + UNREACHABLE("invalid severity"); + [[fallthrough]]; + case kFatal: + case kNone: + break; + } + + return WASM_LOG_LEVEL_FATAL; +} + +static beast::severities::Severity +getLogLevel(uint32_t severity) +{ + using namespace beast::severities; + switch (severity) + { + case WASM_LOG_LEVEL_VERBOSE: + return kTrace; + case WASM_LOG_LEVEL_DEBUG: + return kDebug; + case WASM_LOG_LEVEL_WARNING: + return kWarning; + case WASM_LOG_LEVEL_ERROR: + return kError; + default: + UNREACHABLE("invalid severity"); + [[fallthrough]]; + case WASM_LOG_LEVEL_FATAL: + break; + } + + return kFatal; +} + +// This function is called from WAMR to log messages. +extern "C" void +wamr_log_to_rippled( + uint32_t logLevel, + char const* file, + int line, + char const* fmt, + ...) +{ + beast::Journal j = debugLog(); + + // Format the variadic args + char const* safeFile = file ? file : ""; + + std::ostringstream oss; + oss << "WAMR LOG (" << safeFile << ":" << line << "): "; + + va_list args; + va_start(args, fmt); + + char formatted[1024]; + vsnprintf(formatted, sizeof(formatted), fmt, args); + formatted[sizeof(formatted) - 1] = '\0'; + + va_end(args); + + oss << formatted; + + j.stream(getLogLevel(logLevel)) << oss.str(); +} + +static void +print_wasm_error(char const* message, wasm_trap_t* trap, beast::Journal j) +{ + j.debug() << "WAMR error: " << message; + + if (trap) + { + wasm_byte_vec_t error_message; + + wasm_trap_message(trap, &error_message); + wasm_trap_delete(trap); + j.debug() << "WAMR trap: " << error_message.data; + wasm_byte_vec_delete(&error_message); + } +} + +} // namespace + +InstancePtr +InstanceWrapper::init( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t* expt, + wasm_extern_vec_t const& imports) +{ + wasm_trap_t* trap = nullptr; + InstantiationArgs inst_args{ + 128 * 1024, + 256 * 1024, + static_cast(maxPages > 0 ? maxPages : 0)}; + + InstancePtr mi = InstancePtr( + wasm_instance_new_with_args_ex(s, m, &imports, &trap, &inst_args), + &wasm_instance_delete); + + if (!mi || trap) + { + print_wasm_error( + "can't create instance", + trap, + beast::Journal(beast::Journal::getNullSink())); + throw std::runtime_error("WAMR: can't create instance"); + } + wasm_instance_exports(mi.get(), expt); + return mi; +} + +InstanceWrapper::InstanceWrapper() + : exports{0, nullptr, 0, 0, nullptr} + , mod_inst(nullptr, &wasm_instance_delete) +{ +} + +InstanceWrapper::InstanceWrapper(InstanceWrapper&& o) + : exports{0, nullptr, 0, 0, nullptr} + , mod_inst(nullptr, &wasm_instance_delete) +{ + *this = std::move(o); +} + +InstanceWrapper& +InstanceWrapper::operator=(InstanceWrapper&& o) +{ + if (this == &o) + return *this; + + if (exports.size) + wasm_extern_vec_delete(&exports); + exports = o.exports; + o.exports = {0, nullptr, 0, 0, nullptr}; + + mod_inst = std::move(o.mod_inst); + + return *this; +} + +InstanceWrapper::InstanceWrapper( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t const& imports) + : exports WASM_EMPTY_VEC, mod_inst(init(s, m, maxPages, &exports, imports)) +{ +} + +InstanceWrapper::~InstanceWrapper() +{ + if (exports.size) + wasm_extern_vec_delete(&exports); +} + +InstanceWrapper::operator bool() const +{ + return static_cast(mod_inst); +} + +wasm_func_t* +InstanceWrapper::getFunc( + std::string_view funcName, + wasm_exporttype_vec_t const& export_types) const +{ + wasm_func_t* f = nullptr; + + if (!export_types.size) + throw std::runtime_error("WAMR: no export"); + if (export_types.size != exports.size) + throw std::runtime_error("WAMR: invalid export"); + + for (unsigned i = 0; i < export_types.size; ++i) + { + auto const* exp_type(export_types.data[i]); + + wasm_name_t const* name = wasm_exporttype_name(exp_type); + wasm_externtype_t const* exn_type = wasm_exporttype_type(exp_type); + if (wasm_externtype_kind(exn_type) == WASM_EXTERN_FUNC) + { + if (funcName == std::string_view(name->data, name->size - 1)) + { + auto* exn(exports.data[i]); + if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC) + throw std::runtime_error("WAMR: invalid export"); + + f = wasm_extern_as_func(exn); + break; + } + } + } + + if (!f) + throw std::runtime_error( + "WAMR: can't find function " + std::string(funcName)); + + return f; +} + +wmem +InstanceWrapper::getMem() const +{ + wasm_memory_t* mem = nullptr; + for (unsigned i = 0; i < exports.size; ++i) + { + auto* e(exports.data[i]); + if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY) + { + mem = wasm_extern_as_memory(e); + break; + } + } + + if (!mem) + throw std::runtime_error("WAMR: no memory exported"); + + return { + reinterpret_cast(wasm_memory_data(mem)), + wasm_memory_data_size(mem)}; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ModulePtr +ModuleWrapper::init(wasm_store_t* s, wbytes const& wasmBin) +{ + wasm_byte_vec_t const code{ + wasmBin.size(), + (char*)(wasmBin.data()), + wasmBin.size(), + sizeof(std::remove_reference_t::value_type), + nullptr}; + ModulePtr m = ModulePtr(wasm_module_new(s, &code), &wasm_module_delete); + return m; +} + +ModuleWrapper::ModuleWrapper() + : module(nullptr, &wasm_module_delete) + , export_types{0, nullptr, 0, 0, nullptr} +{ +} + +ModuleWrapper::ModuleWrapper(ModuleWrapper&& o) + : module(nullptr, &wasm_module_delete) + , export_types{0, nullptr, 0, 0, nullptr} +{ + *this = std::move(o); +} + +ModuleWrapper& +ModuleWrapper::operator=(ModuleWrapper&& o) +{ + if (this == &o) + return *this; + + module = std::move(o.module); + mod_inst = std::move(o.mod_inst); + if (export_types.size) + wasm_exporttype_vec_delete(&export_types); + export_types = o.export_types; + o.export_types = {0, nullptr, 0, 0, nullptr}; + exec_env = o.exec_env; + o.exec_env = nullptr; + return *this; +} + +ModuleWrapper::ModuleWrapper( + wasm_store_t* s, + wbytes const& wasmBin, + bool instantiate, + int32_t maxPages, + std::vector const& imports) + : module(init(s, wasmBin)), export_types{0, nullptr, 0, 0, nullptr} +{ + if (!module) + throw std::runtime_error("WAMR: can't create module"); + + wasm_module_exports(module.get(), &export_types); + if (instantiate) + { + auto wimports = buildImports(s, imports); + addInstance(s, maxPages, wimports); + } +} + +ModuleWrapper::~ModuleWrapper() +{ + if (export_types.size) + wasm_exporttype_vec_delete(&export_types); +} + +ModuleWrapper::operator bool() const +{ + return mod_inst; +} + +void +ModuleWrapper::makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp) +{ + auto const paramSize = imp.params.size(); + + if (paramSize) + { + wasm_valtype_vec_new(&v, paramSize, nullptr); + v.num_elems = paramSize; + } + else + v = WASM_EMPTY_VEC; + for (unsigned i = 0; i < paramSize; ++i) + { + auto const vt = imp.params[i]; + switch (vt) + { + case WT_I32: + v.data[i] = wasm_valtype_new_i32(); + break; + case WT_I64: + v.data[i] = wasm_valtype_new_i64(); + break; + case WT_F32: + v.data[i] = wasm_valtype_new_f32(); + break; + case WT_F64: + v.data[i] = wasm_valtype_new_f64(); + break; + default: + throw std::runtime_error("Invalid import type"); + } + } +} + +void +ModuleWrapper::makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp) +{ + if (imp.result) + { + wasm_valtype_vec_new(&v, 1, nullptr); + v.num_elems = 1; + switch (*imp.result) + { + case WT_I32: + v.data[0] = wasm_valtype_new_i32(); + break; + case WT_I64: + v.data[0] = wasm_valtype_new_i64(); + break; + case WT_F32: + v.data[0] = wasm_valtype_new_f32(); + break; + case WT_F64: + v.data[0] = wasm_valtype_new_f64(); + break; + default: + throw std::runtime_error("Invalid return type"); + } + } + else + v = WASM_EMPTY_VEC; +} + +wasm_extern_vec_t +ModuleWrapper::buildImports( + wasm_store_t* s, + std::vector const& imports) +{ + wasm_importtype_vec_t importTypes = WASM_EMPTY_VEC; + wasm_module_imports(module.get(), &importTypes); + std:: + unique_ptr + itDeleter(&importTypes, &wasm_importtype_vec_delete); + + wasm_extern_vec_t wimports = WASM_EMPTY_VEC; + if (!importTypes.num_elems) + return wimports; + + wasm_extern_vec_new(&wimports, importTypes.size, nullptr); + wimports.num_elems = importTypes.num_elems; + + for (unsigned i = 0; i < importTypes.num_elems; ++i) + { + wasm_importtype_t const* importtype = importTypes.data[i]; + if (wasm_importtype_is_linked(importtype)) + { + // create a placeholder + wimports.data[i] = wasm_extern_new_empty( + s, wasm_externtype_kind(wasm_importtype_type(importtype))); + continue; + } + + // wasm_name_t const* mn = wasm_importtype_module(importtype); + // auto modName = std::string_view(mn->data, mn->num_elems - 1); + wasm_name_t const* fn = wasm_importtype_name(importtype); + auto fieldName = std::string_view(fn->data, fn->num_elems - 1); + + // for multi-module support + // if ((W_ENV != modName) && (W_HOST_LIB != modName)) + // continue; + + for (auto const& imp : imports) + { + if (imp.name != fieldName) + continue; + + wasm_valtype_vec_t params, results; + makeImpReturn(results, imp); + makeImpParams(params, imp); + + using ftype_ptr = std:: + unique_ptr; + ftype_ptr ftype( + wasm_functype_new(¶ms, &results), &wasm_functype_delete); + wasm_func_t* func = wasm_func_new_with_env( + s, + ftype.get(), + reinterpret_cast(imp.wrap), + imp.udata, + nullptr); + + wimports.data[i] = wasm_func_as_extern(func); + break; + } + } + + return wimports; +} + +wasm_func_t* +ModuleWrapper::getFunc(std::string_view funcName) const +{ + return mod_inst.getFunc(funcName, export_types); +} + +wmem +ModuleWrapper::getMem() const +{ + return mod_inst.getMem(); +} + +int +ModuleWrapper::addInstance( + wasm_store_t* s, + int32_t maxPages, + wasm_extern_vec_t const& imports) +{ + mod_inst = {s, module.get(), maxPages, imports}; + exec_env = wasm_instance_exec_env(mod_inst.mod_inst.get()); + + return 0; +} + +// int +// my_module_t::delInstance(int i) +// { +// if (i >= mod_inst.size()) +// return -1; +// if (!mod_inst[i]) +// mod_inst[i] = my_mod_inst_t(); +// return i; +// } + +std::int64_t +ModuleWrapper::setGas(std::int64_t gas) +{ + if (exec_env) + { + wasm_runtime_set_instruction_count_limit(exec_env, gas); + return gas; + } + return 0; +} + +std::int64_t +ModuleWrapper::getGas() +{ + return exec_env ? wasm_runtime_get_instruction_count_limit(exec_env) : 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// void +// WamrEngine::clearModules() +// { +// modules.clear(); +// store.reset(); // to free the memory before creating new store +// store = {wasm_store_new(engine.get()), &wasm_store_delete}; +// } + +WamrEngine::WamrEngine() + : engine(wasm_engine_new(), &wasm_engine_delete) + , store(nullptr, &wasm_store_delete) +{ + wasm_runtime_set_default_running_mode(Mode_Interp); + wasm_runtime_set_log_level(WASM_LOG_LEVEL_FATAL); + // wasm_runtime_set_log_level(WASM_LOG_LEVEL_VERBOSE); +} + +int +WamrEngine::addModule( + wbytes const& wasmCode, + bool instantiate, + std::vector const& imports) +{ + module.reset(); + store.reset(); // to free the memory before creating new store + store = {wasm_store_new(engine.get()), &wasm_store_delete}; + module = std::make_unique( + store.get(), wasmCode, instantiate, defMaxPages, imports); + setGas(defGas); + return module ? 0 : -1; +} + +int +WamrEngine::addInstance() +{ + return module->addInstance(store.get(), defMaxPages); +} + +wasm_func_t* +WamrEngine::getFunc(std::string_view funcName) +{ + return module->getFunc(funcName); +} + +std::vector +WamrEngine::convertParams(std::vector const& params) +{ + std::vector v; + v.reserve(params.size()); + for (auto const& p : params) + { + switch (p.type) + { + case WT_I32: + v.push_back(WASM_I32_VAL(p.of.i32)); + break; + case WT_I64: + v.push_back(WASM_I64_VAL(p.of.i64)); + break; + case WT_F32: + v.push_back(WASM_F32_VAL(p.of.f32)); + break; + case WT_F64: + v.push_back(WASM_F64_VAL(p.of.f64)); + break; + default: + break; + } + } + + return v; +} + +void +WamrEngine::add_param(std::vector& in, int32_t p) +{ + in.emplace_back(); + auto& el(in.back()); + memset(&el, 0, sizeof(el)); + el = WASM_I32_VAL(p); // WASM_I32; +} + +void +WamrEngine::add_param(std::vector& in, int64_t p) +{ + in.emplace_back(); + auto& el(in.back()); + el = WASM_I64_VAL(p); +} + +template +WamrResult +WamrEngine::call(std::string_view func, Types... args) +{ + // Lookup our export function + auto* f = getFunc(func); + return call(f, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call(wasm_func_t* func, Types... args) +{ + std::vector in; + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call(wasm_func_t* func, std::vector& in) +{ + // wasm_val_t rs[1] = {WASM_I32_VAL(0)}; + WamrResult ret(NR); + // if (NR) { wasm_val_vec_new_uninitialized(&ret, NR); // + // wasm_val_vec_new(&ret, NR, &rs[0]); // ret = WASM_ARRAY_VEC(rs); } + + wasm_val_vec_t const inv = in.empty() + ? wasm_val_vec_t WASM_EMPTY_VEC + : wasm_val_vec_t{ + in.size(), + in.data(), + in.size(), + sizeof(std::remove_reference_t::value_type), + nullptr}; + trap = wasm_func_call(func, &inv, &ret.r); + if (trap) + print_wasm_error("failed to call func", trap, j_); + + // assert(results[0].kind == WASM_I32); + // if (NR) printf("Result P5: %d\n", ret[0].of.i32); + + return ret; +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + std::int32_t p, + Types... args) +{ + add_param(in, p); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + + std::vector& in, + std::int64_t p, + Types... args) +{ + add_param(in, p); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + uint8_t const* d, + std::size_t sz, + Types... args) +{ + auto res = call<1>(W_ALLOC, static_cast(sz)); + + if (trap || (res.r.data[0].kind != WASM_I32)) + return {}; + auto const ptr = res.r.data[0].of.i32; + if (!ptr) + throw std::runtime_error( + "WAMR: can't allocate memory, " + std::to_string(sz) + " bytes"); + + auto mem = getMem(); + memcpy(mem.p + ptr, d, sz); + + add_param(in, ptr); + add_param(in, static_cast(sz)); + return call(func, in, std::forward(args)...); +} + +template +WamrResult +WamrEngine::call( + wasm_func_t* func, + std::vector& in, + wbytes const& p, + Types... args) +{ + return call(func, in, p.data(), p.size(), std::forward(args)...); +} + +Expected +WamrEngine::run( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params, + beast::Journal j) +{ + try + { + wasm_runtime_set_log_level(getLogLevel(j.sink().threshold())); + j_ = j; + return runHlp(wasmCode, funcName, imports, params); + } + catch (std::exception const&) + { + } + catch (...) + { + } + return Unexpected(tecFAILED_PROCESSING); +} + +Expected +WamrEngine::runHlp( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params) +{ + // Create and instantiate the module. + if (!wasmCode.empty()) + { + int const m = addModule(wasmCode, true, imports); + if (m < 0) + return Unexpected(tecFAILED_PROCESSING); + } + + if (!module) + return Unexpected(tecFAILED_PROCESSING); + + // Call main + auto* f = getFunc(!funcName.empty() ? funcName : "_start"); + auto p = convertParams(params); + auto res = call<1>(f, p); + if (!res.r.size || trap) + return Unexpected(tecFAILED_PROCESSING); + + assert(res.r.data[0].kind == WASM_I32); + // printf("Result: %d\n", results[0].of.i32); + // return res.r.data[0].of.i32 != 0; + return res.r.data[0].of.i32; +} + +std::int64_t +WamrEngine::initGas(std::int64_t def) +{ + defGas = def; + return def; +} + +std::int32_t +WamrEngine::initMaxPages(std::int32_t def) +{ + defMaxPages = def; + return def; +} + +std::int64_t +WamrEngine::setGas(std::int64_t gas) +{ + if (module) + { + module->setGas(gas); + return gas; + } + return 0; +} + +std::int64_t +WamrEngine::getGas() +{ + return module ? module->getGas() : 0; +} + +wmem +WamrEngine::getMem() const +{ + return module ? module->getMem() : wmem(); +} + +int32_t +WamrEngine::allocate(int32_t sz) +{ + auto res = call<1>(W_ALLOC, static_cast(sz)); + if (trap || (res.r.data[0].kind != WASM_I32)) + return {}; + auto const ptr = res.r.data[0].of.i32; + if (!ptr) + throw std::runtime_error( + "WAMR: can't allocate memory, " + std::to_string(sz) + " bytes"); + return ptr; +} + +wasm_trap_t* +WamrEngine::newTrap(std::string_view txt) +{ + wasm_message_t msg = WASM_EMPTY_VEC; + + if (!txt.empty()) + wasm_name_new(&msg, txt.size(), txt.data()); + + return wasm_trap_new(store.get(), &msg); +} + +} // namespace ripple diff --git a/src/xrpld/app/misc/WamrVM.h b/src/xrpld/app/misc/WamrVM.h new file mode 100644 index 0000000000..a5b8051cb1 --- /dev/null +++ b/src/xrpld/app/misc/WamrVM.h @@ -0,0 +1,268 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2023 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. +*/ +//============================================================================== +#pragma once + +#include + +#include + +#include +#include + +namespace ripple { + +// clang-format off + +struct WamrResult +{ + wasm_val_vec_t r; + WamrResult(unsigned N = 0):r{0, nullptr, 0, 0, nullptr} {if (N) wasm_val_vec_new_uninitialized(&r, N);} + ~WamrResult() { if (r.size) wasm_val_vec_delete(&r); } + WamrResult(WamrResult const &) = delete; + WamrResult& operator=(WamrResult const &) = delete; + + WamrResult(WamrResult &&o) {*this = std::move(o);} + WamrResult& operator=(WamrResult &&o){r = o.r; o.r = {0, nullptr, 0, 0, nullptr}; return *this;} + //operator wasm_val_vec_t &() {return r;} +}; + +using ModulePtr = std::unique_ptr; +using InstancePtr = std::unique_ptr; + +// clang-format on + +struct InstanceWrapper +{ + wasm_extern_vec_t exports; + InstancePtr mod_inst; + +private: + static InstancePtr + init( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t* expt, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + +public: + InstanceWrapper(); + + InstanceWrapper(InstanceWrapper&& o); + + InstanceWrapper& + operator=(InstanceWrapper&& o); + + InstanceWrapper( + wasm_store_t* s, + wasm_module_t* m, + int32_t maxPages, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + ~InstanceWrapper(); + + operator bool() const; + + wasm_func_t* + getFunc( + std::string_view funcName, + wasm_exporttype_vec_t const& export_types) const; + + wmem + getMem() const; +}; + +struct ModuleWrapper +{ + ModulePtr module; + wasm_exec_env_t exec_env = nullptr; + InstanceWrapper mod_inst; + wasm_exporttype_vec_t export_types; + +private: + static ModulePtr + init(wasm_store_t* s, wbytes const& wasmBin); + +public: + ModuleWrapper(); + ModuleWrapper(ModuleWrapper&& o); + ModuleWrapper& + operator=(ModuleWrapper&& o); + ModuleWrapper( + wasm_store_t* s, + wbytes const& wasmBin, + bool instantiate, + int32_t maxPages, + std::vector const& imports = {}); + ~ModuleWrapper(); + + operator bool() const; + + wasm_func_t* + getFunc(std::string_view funcName) const; + wmem + getMem() const; + + int + addInstance( + wasm_store_t* s, + int32_t maxPages, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + std::int64_t + setGas(std::int64_t gas); + + std::int64_t + getGas(); + +private: + static void + makeImpParams(wasm_valtype_vec_t& v, WasmImportFunc const& imp); + static void + makeImpReturn(wasm_valtype_vec_t& v, WasmImportFunc const& imp); + wasm_extern_vec_t + buildImports(wasm_store_t* s, std::vector const& imports); +}; + +class WamrEngine +{ + std::unique_ptr engine; + std::unique_ptr store; + std::unique_ptr module; + wasm_trap_t* trap = nullptr; + std::int64_t defGas = -1; + std::int32_t defMaxPages = -1; + beast::Journal j_ = beast::Journal(beast::Journal::getNullSink()); + +public: + WamrEngine(); + ~WamrEngine() = default; + + Expected + run(wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params, + beast::Journal j); + + std::int64_t + initGas(std::int64_t def); + + std::int64_t + setGas(std::int64_t gas); + + std::int32_t + initMaxPages(std::int32_t def); + + std::int64_t + getGas(); + + // Host functions helper functionality + wmem + getMem() const; + + int32_t + allocate(int32_t size); + + wasm_trap_t* + newTrap(std::string_view msg); + +private: + Expected + runHlp( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params); + + int + addModule( + wbytes const& wasmCode, + bool instantiate, + std::vector const& imports); + void + clearModules(); + int + addInstance(); + int32_t + runFunc(std::string_view const funcName, int32_t p); + + int32_t + makeModule( + wbytes const& wasmCode, + wasm_extern_vec_t const& imports = WASM_EMPTY_VEC); + + wasm_func_t* + getFunc(std::string_view funcName); + + std::vector + convertParams(std::vector const& params); + + void + add_param(std::vector& in, int32_t p); + void + add_param(std::vector& in, int64_t p); + + template + inline WamrResult + call(std::string_view func, Types... args); + + template + inline WamrResult + call(wasm_func_t* func, Types... args); + + template + inline WamrResult + call(wasm_func_t* f, std::vector& in); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + std::int32_t p, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + std::int64_t p, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + uint8_t const* d, + std::size_t sz, + Types... args); + + template + inline WamrResult + call( + wasm_func_t* func, + std::vector& in, + wbytes const& p, + Types... args); +}; + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncImpl.cpp b/src/xrpld/app/misc/WasmHostFuncImpl.cpp new file mode 100644 index 0000000000..6450647809 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncImpl.cpp @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 +#include + +#include + +namespace ripple { + +int32_t +WasmHostFunctionsImpl::getLedgerSqn() +{ + return ctx.view().seq(); +} + +int32_t +WasmHostFunctionsImpl::getParentLedgerTime() +{ + return ctx.view().parentCloseTime().time_since_epoch().count(); // TODO try +} + +// TODO remove json code after deciding encoding scheme + +std::optional +WasmHostFunctionsImpl::getTxField(std::string const& fname) +{ + auto js = ctx.tx.getJson(JsonOptions::none); + if (js.isMember(fname)) + { + auto s = js.get(fname, Json::Value::null).asString(); + return Bytes{s.begin(), s.end()}; + } + else + return std::nullopt; +} + +std::optional +WasmHostFunctionsImpl::getLedgerEntryField( + int32_t type, + Bytes const& kdata, + std::string const& fname) +{ + auto kl = [&]() -> std::optional { + if (type == ltACCOUNT_ROOT) + { + std::string s(kdata.begin(), kdata.end()); + auto const account = parseBase58(s); + if (account) + { + return keylet::account(account.value()); + } + } + return std::nullopt; + }(); + + if (!kl || !ctx.view().exists(kl.value())) + return std::nullopt; + + auto js = ctx.view().read(kl.value())->getJson(JsonOptions::none); + if (js.isMember(fname)) + { + auto s = js.get(fname, Json::Value::null).asString(); + return Bytes{s.begin(), s.end()}; + } + else + return std::nullopt; +} + +std::optional +WasmHostFunctionsImpl::getCurrentLedgerEntryField(std::string const& fname) +{ + if (!ctx.view().exists(leKey)) + return std::nullopt; + + auto js = ctx.view().read(leKey)->getJson(JsonOptions::none); + if (js.isMember(fname)) + { + auto s = js.get(fname, Json::Value::null).asString(); + return Bytes{s.begin(), s.end()}; + } + else + return std::nullopt; +} + +std::optional +WasmHostFunctionsImpl::getNFT( + std::string const& account, + std::string const& nftId) +{ + auto const accountId = parseBase58(account); + if (!accountId || accountId->isZero()) + { + return std::nullopt; + } + + uint256 nftHash; + if (!nftHash.parseHex(nftId)) + { + return std::nullopt; + } + + auto jv = nft::findToken(ctx.view(), accountId.value(), nftHash); + if (!jv) + { + return std::nullopt; + } + + Slice const s = (*jv)[sfURI]; + return Bytes{s.begin(), s.end()}; +} + +bool +WasmHostFunctionsImpl::updateData(Bytes const& data) +{ + if (!ctx.view().exists(leKey)) + return false; + auto sle = ctx.view().peek(leKey); + sle->setFieldVL(sfData, data); + ctx.view().update(sle); + return true; +} + +Hash +WasmHostFunctionsImpl::computeSha512HalfHash(Bytes const& data) +{ + auto const hash = sha512Half(data); + return uint256::fromVoid(hash.data()); +} + +std::optional +WasmHostFunctionsImpl::accountKeylet(std::string const& account) +{ + auto const accountId = parseBase58(account); + if (!accountId || accountId->isZero()) + { + return std::nullopt; + } + + auto keylet = keylet::account(*accountId).key; + if (!keylet) + { + return std::nullopt; + } + + return Bytes{keylet.begin(), keylet.end()}; +} + +std::optional +WasmHostFunctionsImpl::credentialKeylet( + std::string const& subject, + std::string const& issuer, + std::string const& credentialType) +{ + auto const subjectId = parseBase58(subject); + if (!subjectId || subjectId->isZero()) + { + return std::nullopt; + } + + auto const issuerId = parseBase58(issuer); + if (!issuerId || issuerId->isZero()) + { + return std::nullopt; + } + + auto keylet = + keylet::credential(*subjectId, *issuerId, makeSlice(credentialType)) + .key; + if (!keylet) + { + return std::nullopt; + } + + return Bytes{keylet.begin(), keylet.end()}; +} + +std::optional +WasmHostFunctionsImpl::escrowKeylet( + std::string const& account, + std::uint32_t const& seq) +{ + auto const accountId = parseBase58(account); + if (!accountId || accountId->isZero()) + { + return std::nullopt; + } + + auto keylet = keylet::escrow(*accountId, seq).key; + if (!keylet) + { + return std::nullopt; + } + + return Bytes{keylet.begin(), keylet.end()}; +} + +std::optional +WasmHostFunctionsImpl::oracleKeylet( + std::string const& account, + std::uint32_t const& documentId) +{ + auto const accountId = parseBase58(account); + if (!accountId || accountId->isZero()) + { + return std::nullopt; + } + + auto keylet = keylet::oracle(*accountId, documentId).key; + if (!keylet) + { + return std::nullopt; + } + + return Bytes{keylet.begin(), keylet.end()}; +} +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncImpl.h b/src/xrpld/app/misc/WasmHostFuncImpl.h new file mode 100644 index 0000000000..b87c23a4a8 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncImpl.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2023 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. +*/ +//============================================================================== + +#pragma once + +#include +#include + +#include +#include +#include + +namespace ripple { +class WasmHostFunctionsImpl : public HostFunctions +{ +public: + WasmHostFunctionsImpl(ApplyContext& ctx, Keylet leKey) + : ctx(ctx), leKey(leKey) + { + } + + beast::Journal + getJournal() override + { + return ctx.journal; + } + + int32_t + getLedgerSqn() override; + + int32_t + getParentLedgerTime() override; + + std::optional + getTxField(std::string const& fname) override; + + std::optional + getLedgerEntryField( + int32_t type, + Bytes const& kdata, + std::string const& fname) override; + + std::optional + getCurrentLedgerEntryField(std::string const& fname) override; + + std::optional + getNFT(std::string const& account, std::string const& nftId) override; + + bool + updateData(Bytes const& data) override; + + Hash + computeSha512HalfHash(Bytes const& data) override; + + std::optional + accountKeylet(std::string const& account) override; + + std::optional + credentialKeylet( + std::string const& subject, + std::string const& issuer, + std::string const& credentialType) override; + + std::optional + escrowKeylet(std::string const& account, std::uint32_t const& seq) override; + + std::optional + oracleKeylet(std::string const& account, std::uint32_t const& documentId) + override; + +private: + ApplyContext& ctx; + Keylet leKey; +}; + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncWrapper.cpp b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp new file mode 100644 index 0000000000..543350a7c1 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncWrapper.cpp @@ -0,0 +1,392 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 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 +#include + +#include + +namespace ripple { + +wasm_trap_t* +getLedgerSqn_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto* hf = reinterpret_cast(env); + int32_t const sqn = hf->getLedgerSqn(); + results->data[0] = WASM_I32_VAL(sqn); + + return nullptr; +} + +wasm_trap_t* +getParentLedgerTime_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto* hf = reinterpret_cast(env); + int32_t const ltime = hf->getParentLedgerTime(); + results->data[0] = WASM_I32_VAL(ltime); + return nullptr; +} + +static Expected +getParameterData(wasm_val_vec_t const* params, size_t index) +{ + auto& vm = WasmEngine::instance(); + auto fnameOffset = params->data[index].of.i32; + auto fnameLen = params->data[index + 1].of.i32; + auto mem = vm.getMem(); + if (!mem.s) + return Unexpected("No memory exported"); + + if (mem.s <= fnameOffset + fnameLen) + return Unexpected("Memory access failed"); + Bytes fname(mem.p + fnameOffset, mem.p + fnameOffset + fnameLen); + return fname; +} + +static Expected +getFieldName(wasm_val_vec_t const* params, size_t index) +{ + auto const dataRes = getParameterData(params, index); + if (dataRes) + { + return std::string(dataRes->begin(), dataRes->end()); + } + else + { + auto& vm = WasmEngine::instance(); + return Unexpected( + reinterpret_cast(vm.newTrap(dataRes.error()))); + } +} + +static Expected +setData(Bytes const& data) +{ + auto& vm = WasmEngine::instance(); + auto mem = vm.getMem(); + if (!mem.s) + return Unexpected("No memory exported"); + + int32_t const dataLen = static_cast(data.size()); + int32_t const dataPtr = vm.allocate(dataLen); + if (!dataPtr) + return Unexpected("Allocation error"); + memcpy(mem.p + dataPtr, data.data(), dataLen); + + auto retPtr = vm.allocate(8); + if (!retPtr) + return Unexpected("Allocation error"); + int32_t* retData = reinterpret_cast(mem.p + retPtr); + retData[0] = dataPtr; + retData[1] = dataLen; + + return retPtr; +} + +wasm_trap_t* +getTxField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getFieldName(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = hf->getTxField(fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap("Field not found")); + + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getLedgerEntryField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + int32_t const type = params->data[0].of.i32; + auto lkData = getParameterData(params, 1); + if (!lkData) + return reinterpret_cast(vm.newTrap()); + + auto fname = getFieldName(params, 3); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = + hf->getLedgerEntryField(type, lkData.value(), fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap()); + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getCurrentLedgerEntryField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getFieldName(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto fieldData = hf->getCurrentLedgerEntryField(fname.value()); + if (!fieldData) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(fieldData.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)fieldData.value().size()); + return nullptr; +} + +wasm_trap_t* +getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto nftId = getFieldName(params, 2); + if (!nftId) + return reinterpret_cast(vm.newTrap()); + + auto nftURI = hf->getNFT(account.value(), nftId.value()); + if (!nftURI) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(nftURI.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +accountKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto keylet = hf->accountKeylet(account.value()); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +credentialKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto subject = getFieldName(params, 0); + if (!subject) + return reinterpret_cast(vm.newTrap()); + + auto issuer = getFieldName(params, 2); + if (!issuer) + return reinterpret_cast(vm.newTrap()); + + auto credentialType = getFieldName(params, 4); + if (!credentialType) + return reinterpret_cast(vm.newTrap()); + + auto keylet = hf->credentialKeylet( + subject.value(), issuer.value(), credentialType.value()); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +escrowKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + int32_t const sequence = params->data[2].of.i32; + + auto keylet = hf->escrowKeylet(account.value(), sequence); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +oracleKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto account = getFieldName(params, 0); + if (!account) + return reinterpret_cast(vm.newTrap()); + + auto documentId = params->data[2].of.i32; + + auto keylet = hf->escrowKeylet(account.value(), documentId); + if (!keylet) + return reinterpret_cast(vm.newTrap()); + + auto pointer = setData(keylet.value()); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32((int)nftURI.value().size()); + return nullptr; +} + +wasm_trap_t* +updateData_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getParameterData(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + if (!hf->updateData(fname.value())) + return reinterpret_cast(vm.newTrap()); + + return nullptr; +} + +wasm_trap_t* +computeSha512HalfHash_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + auto* hf = reinterpret_cast(env); + + auto fname = getParameterData(params, 0); + if (!fname) + return reinterpret_cast(vm.newTrap()); + + auto hres = hf->computeSha512HalfHash(fname.value()); + Bytes digest{hres.begin(), hres.end()}; + auto pointer = setData(digest); + if (!pointer) + return reinterpret_cast(vm.newTrap()); + + results->data[0] = WASM_I32_VAL(pointer.value()); + // out[1] = WasmEdge_ValueGenI32(32); + return nullptr; +} + +wasm_trap_t* +print_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) +{ + auto& vm = WasmEngine::instance(); + // auto* hf = reinterpret_cast(env); + + auto f = getParameterData(params, 0); + if (!f) + return reinterpret_cast(vm.newTrap()); + std::string s(f->begin(), f->end()); + if (s.size() < 4096) + std::cout << s << std::endl; + return nullptr; +} + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmHostFuncWrapper.h b/src/xrpld/app/misc/WasmHostFuncWrapper.h new file mode 100644 index 0000000000..d63db3d505 --- /dev/null +++ b/src/xrpld/app/misc/WasmHostFuncWrapper.h @@ -0,0 +1,113 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 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. +*/ +//============================================================================== + +#pragma once + +#include + +namespace ripple { + +using getLedgerSqn_proto = int32_t(); +wasm_trap_t* +getLedgerSqn_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getParentLedgerTime_proto = int32_t(); +wasm_trap_t* +getParentLedgerTime_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getTxField_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +getTxField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getLedgerEntryField_proto = + uint32_t*(int32_t, uint8_t const*, int32_t, char const*, int32_t); +wasm_trap_t* +getLedgerEntryField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getCurrentLedgerEntryField_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +getCurrentLedgerEntryField_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using getNFT_proto = uint32_t*(char const*, int32_t, char const*, int32_t); +wasm_trap_t* +getNFT_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +using accountKeylet_proto = uint32_t*(char const*, int32_t); +wasm_trap_t* +accountKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using credentialKeylet_proto = + uint32_t*(char const*, int32_t, char const*, int32_t, char const*, int32_t); +wasm_trap_t* +credentialKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using escrowKeylet_proto = uint32_t*(char const*, int32_t, int32_t); +wasm_trap_t* +escrowKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using oracleKeylet_proto = uint32_t*(char const*, int32_t, int32_t); +wasm_trap_t* +oracleKeylet_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using updateData_proto = void(uint8_t const*, int32_t); +wasm_trap_t* +updateData_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using computeSha512HalfHash_proto = uint32_t*(uint8_t const*, int32_t); +wasm_trap_t* +computeSha512HalfHash_wrap( + void* env, + wasm_val_vec_t const* params, + wasm_val_vec_t* results); + +using print_proto = void(char const*, int32_t); +wasm_trap_t* +print_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmVM.cpp b/src/xrpld/app/misc/WasmVM.cpp new file mode 100644 index 0000000000..8e77bc027b --- /dev/null +++ b/src/xrpld/app/misc/WasmVM.cpp @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 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 +#include + +#include +#include + +#include + +namespace ripple { + +Expected +runEscrowWasm( + Bytes const& wasmCode, + std::string_view funcName, + HostFunctions* hfs, + uint64_t gasLimit) +{ + // create VM and set cost limit + auto& vm = WasmEngine::instance(); + vm.initGas(gasLimit); + vm.initMaxPages(MAX_PAGES); + + std::vector imports; + + WASM_IMPORT_FUNC(imports, getLedgerSqn, hfs) + WASM_IMPORT_FUNC(imports, getParentLedgerTime, hfs) + WASM_IMPORT_FUNC(imports, getTxField, hfs) + WASM_IMPORT_FUNC(imports, getLedgerEntryField, hfs) + WASM_IMPORT_FUNC(imports, getCurrentLedgerEntryField, hfs) + WASM_IMPORT_FUNC(imports, getNFT, hfs) + WASM_IMPORT_FUNC(imports, accountKeylet, hfs) + WASM_IMPORT_FUNC(imports, credentialKeylet, hfs) + WASM_IMPORT_FUNC(imports, escrowKeylet, hfs) + WASM_IMPORT_FUNC(imports, oracleKeylet, hfs) + WASM_IMPORT_FUNC(imports, updateData, hfs) + WASM_IMPORT_FUNC(imports, computeSha512HalfHash, hfs) + WASM_IMPORT_FUNC(imports, print, hfs) + + std::int64_t const sgas = gasLimit; // vm.getGas(); + auto ret = vm.run(wasmCode, funcName, imports, {}, hfs->getJournal()); + + // std::cout << "runEscrowWasm, mod size: " << wasmCode.size() + // << ", gasLimit: " << gasLimit << ", funcName: " << funcName; + + if (!ret.has_value()) + { + // std::cout << ", error: " << ret.error() << std::endl; + return Unexpected(ret.error()); + } + std::int64_t const egas = vm.getGas(); + std::uint64_t const spent = static_cast(sgas - egas); + + // std::cout << ", ret: " << ret.value() << ", gas spent: " << spent + // << std::endl; + return EscrowResult{static_cast(ret.value()), spent}; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +WasmEngine::WasmEngine() : impl(std::make_unique()) +{ +} + +WasmEngine& +WasmEngine::instance() +{ + static WasmEngine e; + return e; +} + +Expected +WasmEngine::run( + wbytes const& wasmCode, + std::string_view funcName, + std::vector const& imports, + std::vector const& params, + beast::Journal j) +{ + return impl->run(wasmCode, funcName, imports, params, j); +} + +std::int64_t +WasmEngine::initGas(std::int64_t def) +{ + return impl->initGas(def); +} + +std::int32_t +WasmEngine::initMaxPages(std::int32_t def) +{ + return impl->initMaxPages(def); +} + +// gas = 1'000'000'000LL +std::int64_t +WasmEngine::setGas(std::int64_t gas) +{ + return impl->setGas(gas); +} + +std::int64_t +WasmEngine::getGas() +{ + return impl->getGas(); +} + +wmem +WasmEngine::getMem() const +{ + return impl->getMem(); +} + +int32_t +WasmEngine::allocate(int32_t size) +{ + return impl->allocate(size); +} + +void* +WasmEngine::newTrap(std::string_view msg) +{ + return impl->newTrap(msg); +} + +} // namespace ripple diff --git a/src/xrpld/app/misc/WasmVM.h b/src/xrpld/app/misc/WasmVM.h new file mode 100644 index 0000000000..74e700883d --- /dev/null +++ b/src/xrpld/app/misc/WasmVM.h @@ -0,0 +1,375 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2025 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. +*/ +//============================================================================== +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +// #include + +#include + +namespace bft = boost::function_types; + +namespace ripple { + +static std::string_view const W_ENV = "env"; +static std::string_view const W_HOST_LIB = "host_lib"; +static std::string_view const W_MEM = "memory"; +static std::string_view const W_STORE = "store"; +static std::string_view const W_LOAD = "load"; +static std::string_view const W_SIZE = "size"; +static std::string_view const W_ALLOC = "allocate"; +static std::string_view const W_DEALLOC = "deallocate"; +static std::string_view const W_PROC_EXIT = "proc_exit"; + +using wbytes = std::vector; +struct wmem +{ + std::uint8_t* p = nullptr; + std::size_t s = 0; +}; + +uint32_t const MAX_PAGES = 128; // 8MB = 64KB*128 + +typedef std::vector Bytes; +typedef ripple::uint256 Hash; + +template +struct WasmResult +{ + T result; + uint64_t cost; +}; +typedef WasmResult EscrowResult; + +struct HostFunctions +{ + virtual beast::Journal + getJournal() + { + return beast::Journal{beast::Journal::getNullSink()}; + } + + virtual int32_t + getLedgerSqn() + { + return 1; + } + + virtual int32_t + getParentLedgerTime() + { + return 1; + } + + virtual std::optional + getTxField(std::string const& fname) + { + return Bytes{}; + } + + virtual std::optional + getLedgerEntryField( + int32_t type, + Bytes const& kdata, + std::string const& fname) + { + return Bytes{}; + } + + virtual std::optional + getCurrentLedgerEntryField(std::string const& fname) + { + return Bytes{}; + } + + virtual std::optional + getNFT(std::string const& account, std::string const& nftId) + { + return Bytes{}; + } + + virtual bool + updateData(Bytes const& data) + { + return true; + } + + virtual Hash + computeSha512HalfHash(Bytes const& data) + { + return Hash{}; + } + + virtual std::optional + accountKeylet(std::string const& account) + { + return Bytes{}; + } + + virtual std::optional + credentialKeylet( + std::string const& subject, + std::string const& issuer, + std::string const& credentialType) + { + return Bytes{}; + } + + virtual std::optional + escrowKeylet(std::string const& account, std::uint32_t const& seq) + { + return Bytes{}; + } + + virtual std::optional + oracleKeylet(std::string const& account, std::uint32_t const& docId) + { + return Bytes{}; + } + + virtual ~HostFunctions() = default; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum WasmTypes { WT_I32, WT_I64, WT_F32, WT_F64 }; + +struct WasmImportFunc +{ + std::string name; + std::optional result; + std::vector params; + void* udata = nullptr; + // wasm_func_callback_with_env_t + void* wrap = nullptr; +}; + +#define WASM_IMPORT_FUNC(v, f, ...) \ + WasmImpFunc( \ + v, #f, reinterpret_cast(&f##_wrap), ##__VA_ARGS__); + +template +void +WasmImpArgs(WasmImportFunc& e) +{ + if constexpr (N < C) + { + using at = typename boost::mpl::at_c::type; + if constexpr (std::is_pointer_v) + e.params.push_back(WT_I32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_I32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_I64); + else if constexpr (std::is_same_v) + e.params.push_back(WT_F32); + else if constexpr (std::is_same_v) + e.params.push_back(WT_F64); + else + static_assert(std::is_pointer_v, "Unsupported argument type"); + + return WasmImpArgs(e); + } + return; +} + +template +void +WasmImpRet(WasmImportFunc& e) +{ + if constexpr (std::is_pointer_v) + e.result = WT_I32; + else if constexpr (std::is_same_v) + e.result = WT_I32; + else if constexpr (std::is_same_v) + e.result = WT_I64; + else if constexpr (std::is_same_v) + e.result = WT_F32; + else if constexpr (std::is_same_v) + e.result = WT_F64; + else if constexpr (std::is_void_v) + e.result.reset(); +#if (defined(__GNUC__) && (__GNUC__ >= 14)) || \ + ((defined(__clang_major__)) && (__clang_major__ >= 18)) + else + static_assert(false, "Unsupported return type"); +#endif +} + +template +void +WasmImpFuncHelper(WasmImportFunc& e) +{ + using rt = typename bft::result_type::type; + using pt = typename bft::parameter_types::type; + // typename boost::mpl::at_c::type + + WasmImpRet(e); + WasmImpArgs<0, bft::function_arity::value, pt>(e); + // WasmImpWrap(e, std::forward(f)); +} + +template +void +WasmImpFunc( + std::vector& v, + std::string_view imp_name, + void* f_wrap, + void* data = nullptr) +{ + WasmImportFunc e; + e.name = imp_name; + e.udata = data; + e.wrap = f_wrap; + WasmImpFuncHelper(e); + v.push_back(std::move(e)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct WasmParam +{ + WasmTypes type = WT_I32; + union + { + std::int32_t i32; + std::int64_t i64 = 0; + float f32; + double f64; + } of; +}; + +template +inline std::vector +wasmParams(Types... args) +{ + std::vector v; + v.reserve(sizeof...(args)); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, std::int32_t p, Types... args) +{ + v.push_back({.type = WT_I32, .of = {.i32 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, std::int64_t p, Types... args) +{ + v.push_back({.type = WT_I64, .of = {.i64 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, float p, Types... args) +{ + v.push_back({.type = WT_F32, .of = {.f32 = p}}); + return wasmParams(v, std::forward(args)...); +} + +template +inline std::vector +wasmParams(std::vector& v, double p, Types... args) +{ + v.push_back({.type = WT_F64, .of = {.f64 = p}}); + return wasmParams(v, std::forward(args)...); +} + +inline std::vector +wasmParams(std::vector& v) +{ + return v; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class WamrEngine; +class WasmEngine +{ + std::unique_ptr const impl; + + WasmEngine(); + + WasmEngine(WasmEngine const&) = delete; + WasmEngine(WasmEngine&&) = delete; + WasmEngine& + operator=(WasmEngine const&) = delete; + WasmEngine& + operator=(WasmEngine&&) = delete; + +public: + ~WasmEngine() = default; + + static WasmEngine& + instance(); + + Expected + run(wbytes const& wasmCode, + std::string_view funcName = {}, + std::vector const& imports = {}, + std::vector const& params = {}, + beast::Journal j = beast::Journal{beast::Journal::getNullSink()}); + + std::int64_t + initGas(std::int64_t def = 1'000'000'000'000LL); + + std::int32_t + initMaxPages(std::int32_t def); + + std::int64_t + setGas(std::int64_t gas); + + std::int64_t + getGas(); + + // for host functions usage + wmem + getMem() const; + + int32_t + allocate(int32_t size); + + void* + newTrap(std::string_view msg = {}); +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +Expected +runEscrowWasm( + Bytes const& wasmCode, + std::string_view funcName, + HostFunctions* hfs, + uint64_t gasLimit); + +} // namespace ripple diff --git a/src/xrpld/app/misc/detail/AMMUtils.cpp b/src/xrpld/app/misc/detail/AMMUtils.cpp index ba4c741300..056b466ea1 100644 --- a/src/xrpld/app/misc/detail/AMMUtils.cpp +++ b/src/xrpld/app/misc/detail/AMMUtils.cpp @@ -150,8 +150,8 @@ ammLPHolds( } amount.setIssuer(ammAccount); - JLOG(j.trace()) << "ammLPHolds:" - << " lpAccount=" << to_string(lpAccount) + JLOG(j.trace()) << "ammLPHolds:" << " lpAccount=" + << to_string(lpAccount) << " amount=" << amount.getFullText(); } diff --git a/src/xrpld/app/misc/detail/TxQ.cpp b/src/xrpld/app/misc/detail/TxQ.cpp index 6924dae6c8..caa4cba8be 100644 --- a/src/xrpld/app/misc/detail/TxQ.cpp +++ b/src/xrpld/app/misc/detail/TxQ.cpp @@ -1504,11 +1504,11 @@ TxQ::accept(Application& app, OpenView& view) } else { - JLOG(j_.debug()) << "Queued transaction " << candidateIter->txID - << " failed with " << transToken(txnResult) - << ". Leave in queue." - << " Applied: " << didApply - << ". Flags: " << candidateIter->flags; + JLOG(j_.debug()) + << "Queued transaction " << candidateIter->txID + << " failed with " << transToken(txnResult) + << ". Leave in queue." << " Applied: " << didApply + << ". Flags: " << candidateIter->flags; if (account.retryPenalty && candidateIter->retriesRemaining > 2) candidateIter->retriesRemaining = 1; else diff --git a/src/xrpld/app/paths/Pathfinder.cpp b/src/xrpld/app/paths/Pathfinder.cpp index e02c3ed089..c9905b0156 100644 --- a/src/xrpld/app/paths/Pathfinder.cpp +++ b/src/xrpld/app/paths/Pathfinder.cpp @@ -236,8 +236,7 @@ Pathfinder::findPaths( mSource = STPathElement(account, mSrcCurrency, issuer); auto issuerString = mSrcIssuer ? to_string(*mSrcIssuer) : std::string("none"); - JLOG(j_.trace()) << "findPaths>" - << " mSrcAccount=" << mSrcAccount + JLOG(j_.trace()) << "findPaths>" << " mSrcAccount=" << mSrcAccount << " mDstAccount=" << mDstAccount << " mDstAmount=" << mDstAmount.getFullText() << " mSrcCurrency=" << mSrcCurrency diff --git a/src/xrpld/app/paths/detail/BookStep.cpp b/src/xrpld/app/paths/detail/BookStep.cpp index 4024ca190d..5e650230fe 100644 --- a/src/xrpld/app/paths/detail/BookStep.cpp +++ b/src/xrpld/app/paths/detail/BookStep.cpp @@ -190,8 +190,7 @@ protected: logStringImpl(char const* name) const { std::ostringstream ostr; - ostr << name << ": " - << "\ninIss: " << book_.in.account + ostr << name << ": " << "\ninIss: " << book_.in.account << "\noutIss: " << book_.out.account << "\ninCur: " << book_.in.currency << "\noutCur: " << book_.out.currency; diff --git a/src/xrpld/app/paths/detail/DirectStep.cpp b/src/xrpld/app/paths/detail/DirectStep.cpp index 4dc9cbf20d..4e5ccea3f1 100644 --- a/src/xrpld/app/paths/detail/DirectStep.cpp +++ b/src/xrpld/app/paths/detail/DirectStep.cpp @@ -205,8 +205,7 @@ protected: logStringImpl(char const* name) const { std::ostringstream ostr; - ostr << name << ": " - << "\nSrc: " << src_ << "\nDst: " << dst_; + ostr << name << ": " << "\nSrc: " << src_ << "\nDst: " << dst_; return ostr.str(); } diff --git a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp b/src/xrpld/app/paths/detail/XRPEndpointStep.cpp index 7fdfb3749d..4f38a7b422 100644 --- a/src/xrpld/app/paths/detail/XRPEndpointStep.cpp +++ b/src/xrpld/app/paths/detail/XRPEndpointStep.cpp @@ -132,8 +132,7 @@ protected: logStringImpl(char const* name) const { std::ostringstream ostr; - ostr << name << ": " - << "\nAcc: " << acc_; + ostr << name << ": " << "\nAcc: " << acc_; return ostr.str(); } diff --git a/src/xrpld/app/rdb/RelationalDatabase.h b/src/xrpld/app/rdb/RelationalDatabase.h index 25b16f04a1..927b08d385 100644 --- a/src/xrpld/app/rdb/RelationalDatabase.h +++ b/src/xrpld/app/rdb/RelationalDatabase.h @@ -238,8 +238,8 @@ rangeCheckedCast(C c) /* This should never happen */ UNREACHABLE("ripple::rangeCheckedCast : domain error"); JLOG(debugLog().error()) - << "rangeCheckedCast domain error:" - << " value = " << c << " min = " << std::numeric_limits::lowest() + << "rangeCheckedCast domain error:" << " value = " << c + << " min = " << std::numeric_limits::lowest() << " max: " << std::numeric_limits::max(); } diff --git a/src/xrpld/app/tx/detail/Batch.cpp b/src/xrpld/app/tx/detail/Batch.cpp index dcac889a5a..eca5fd6605 100644 --- a/src/xrpld/app/tx/detail/Batch.cpp +++ b/src/xrpld/app/tx/detail/Batch.cpp @@ -192,8 +192,8 @@ Batch::preflight(PreflightContext const& ctx) if (flags & tfBatchMask) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" - << "invalid flags."; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId << "]:" << "invalid flags."; return temINVALID_FLAG; } @@ -201,23 +201,24 @@ Batch::preflight(PreflightContext const& ctx) flags & (tfAllOrNothing | tfOnlyOne | tfUntilFailure | tfIndependent)) != 1) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" - << "too many flags."; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId << "]:" << "too many flags."; return temINVALID_FLAG; } auto const& rawTxns = ctx.tx.getFieldArray(sfRawTransactions); if (rawTxns.size() <= 1) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" - << "txns array must have at least 2 entries."; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]:" << "txns array must have at least 2 entries."; return temARRAY_EMPTY; } if (rawTxns.size() > maxBatchTxCount) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]:" - << "txns array exceeds 8 entries."; + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId + << "]:" << "txns array exceeds 8 entries."; return temARRAY_TOO_LARGE; } @@ -232,50 +233,53 @@ Batch::preflight(PreflightContext const& ctx) auto const hash = stx.getTransactionID(); if (!uniqueHashes.emplace(hash).second) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "duplicate Txn found. " - << "txID: " << hash; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "duplicate Txn found. " << "txID: " << hash; return temREDUNDANT; } if (stx.getFieldU16(sfTransactionType) == ttBATCH) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "batch cannot have an inner batch txn. " - << "txID: " << hash; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "batch cannot have an inner batch txn. " + << "txID: " << hash; return temINVALID; } if (!(stx.getFlags() & tfInnerBatchTxn)) { JLOG(ctx.j.debug()) - << "BatchTrace[" << parentBatchId << "]: " - << "inner txn must have the tfInnerBatchTxn flag. " + << "BatchTrace[" << parentBatchId + << "]: " << "inner txn must have the tfInnerBatchTxn flag. " << "txID: " << hash; return temINVALID_FLAG; } if (stx.isFieldPresent(sfTxnSignature)) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn cannot include TxnSignature. " - << "txID: " << hash; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "inner txn cannot include TxnSignature. " + << "txID: " << hash; return temBAD_SIGNATURE; } if (stx.isFieldPresent(sfSigners)) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn cannot include Signers. " + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId + << "]: " << "inner txn cannot include Signers. " << "txID: " << hash; return temBAD_SIGNER; } if (!stx.getSigningPubKey().empty()) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn SigningPubKey must be empty. " - << "txID: " << hash; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "inner txn SigningPubKey must be empty. " + << "txID: " << hash; return temBAD_REGKEY; } @@ -284,10 +288,10 @@ Batch::preflight(PreflightContext const& ctx) ctx.app, ctx.rules, parentBatchId, stx, tapBATCH, ctx.j); preflightResult.ter != tesSUCCESS) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn preflight failed: " - << transHuman(preflightResult.ter) << " " - << "txID: " << hash; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "inner txn preflight failed: " + << transHuman(preflightResult.ter) << " " << "txID: " << hash; return temINVALID_INNER_BATCH; } @@ -295,8 +299,8 @@ Batch::preflight(PreflightContext const& ctx) if (auto const fee = stx.getFieldAmount(sfFee); !fee.native() || fee.xrp() != beast::zero) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "inner txn must have a fee of 0. " + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId + << "]: " << "inner txn must have a fee of 0. " << "txID: " << hash; return temBAD_FEE; } @@ -333,8 +337,7 @@ Batch::preflight(PreflightContext const& ctx) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "duplicate sequence found: " - << "txID: " << hash; + << "duplicate sequence found: " << "txID: " << hash; return temREDUNDANT; } } @@ -346,8 +349,7 @@ Batch::preflight(PreflightContext const& ctx) { JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "duplicate ticket found: " - << "txID: " << hash; + << "duplicate ticket found: " << "txID: " << hash; return temREDUNDANT; } } @@ -373,8 +375,8 @@ Batch::preflight(PreflightContext const& ctx) // Check that the batch signers array is not too large. if (signers.size() > maxBatchTxCount) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "signers array exceeds 8 entries."; + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId + << "]: " << "signers array exceeds 8 entries."; return temARRAY_TOO_LARGE; } @@ -397,8 +399,8 @@ Batch::preflight(PreflightContext const& ctx) if (!batchSigners.insert(signerAccount).second) { JLOG(ctx.j.debug()) - << "BatchTrace[" << parentBatchId << "]: " - << "duplicate signer found: " << signerAccount; + << "BatchTrace[" << parentBatchId + << "]: " << "duplicate signer found: " << signerAccount; return temREDUNDANT; } @@ -406,8 +408,9 @@ Batch::preflight(PreflightContext const& ctx) // Remove it if it does, as it can be crossed off the list. if (requiredSigners.erase(signerAccount) == 0) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "no account signature for inner txn."; + JLOG(ctx.j.debug()) + << "BatchTrace[" << parentBatchId + << "]: " << "no account signature for inner txn."; return temBAD_SIGNER; } } @@ -427,8 +430,8 @@ Batch::preflight(PreflightContext const& ctx) if (!requiredSigners.empty()) { - JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId << "]: " - << "invalid batch signers."; + JLOG(ctx.j.debug()) << "BatchTrace[" << parentBatchId + << "]: " << "invalid batch signers."; return temBAD_SIGNER; } return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/CancelOffer.cpp b/src/xrpld/app/tx/detail/CancelOffer.cpp index 004ae1e8b9..6d8c077a62 100644 --- a/src/xrpld/app/tx/detail/CancelOffer.cpp +++ b/src/xrpld/app/tx/detail/CancelOffer.cpp @@ -35,8 +35,8 @@ CancelOffer::preflight(PreflightContext const& ctx) if (uTxFlags & tfUniversalMask) { - JLOG(ctx.j.trace()) << "Malformed transaction: " - << "Invalid flags set."; + JLOG(ctx.j.trace()) + << "Malformed transaction: " << "Invalid flags set."; return temINVALID_FLAG; } @@ -63,8 +63,8 @@ CancelOffer::preclaim(PreclaimContext const& ctx) if ((*sle)[sfSequence] <= offerSequence) { - JLOG(ctx.j.trace()) << "Malformed transaction: " - << "Sequence " << offerSequence << " is invalid."; + JLOG(ctx.j.trace()) << "Malformed transaction: " << "Sequence " + << offerSequence << " is invalid."; return temBAD_SEQUENCE; } diff --git a/src/xrpld/app/tx/detail/Change.cpp b/src/xrpld/app/tx/detail/Change.cpp index 1392d84c08..ea4399ae9c 100644 --- a/src/xrpld/app/tx/detail/Change.cpp +++ b/src/xrpld/app/tx/detail/Change.cpp @@ -130,6 +130,20 @@ Change::preclaim(PreclaimContext const& ctx) ctx.tx.isFieldPresent(sfReserveIncrementDrops)) return temDISABLED; } + if (ctx.view.rules().enabled(featureSmartEscrow)) + { + if (!ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + !ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + !ctx.tx.isFieldPresent(sfGasPrice)) + return temMALFORMED; + } + else + { + if (ctx.tx.isFieldPresent(sfExtensionComputeLimit) || + ctx.tx.isFieldPresent(sfExtensionSizeLimit) || + ctx.tx.isFieldPresent(sfGasPrice)) + return temDISABLED; + } return tesSUCCESS; case ttAMENDMENT: case ttUNL_MODIFY: @@ -377,6 +391,12 @@ Change::applyFee() set(feeObject, ctx_.tx, sfReserveBase); set(feeObject, ctx_.tx, sfReserveIncrement); } + if (view().rules().enabled(featureSmartEscrow)) + { + set(feeObject, ctx_.tx, sfExtensionComputeLimit); + set(feeObject, ctx_.tx, sfExtensionSizeLimit); + set(feeObject, ctx_.tx, sfGasPrice); + } view().update(feeObject); diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index 0b58957fcf..375a77022c 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -32,6 +34,8 @@ #include #include +#include + // During an EscrowFinish, the transaction must specify both // a condition and a fulfillment. We track whether that // fulfillment matches and validates the condition. @@ -82,9 +86,28 @@ EscrowCreate::makeTxConsequences(PreflightContext const& ctx) return TxConsequences{ctx.tx, ctx.tx[sfAmount].xrp()}; } +XRPAmount +EscrowCreate::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + XRPAmount txnFees{Transactor::calculateBaseFee(view, tx)}; + if (tx.isFieldPresent(sfFinishFunction)) + { + // TODO: make this fee increase based on the extra compute run + txnFees += 1000; + } + return txnFees; +} + NotTEC EscrowCreate::preflight(PreflightContext const& ctx) { + if (ctx.tx.isFieldPresent(sfFinishFunction) && + !ctx.rules.enabled(featureSmartEscrow)) + { + JLOG(ctx.j.debug()) << "SmartEscrow not enabled"; + return temDISABLED; + } + if (ctx.rules.enabled(fix1543) && ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; @@ -107,14 +130,23 @@ EscrowCreate::preflight(PreflightContext const& ctx) ctx.tx[sfCancelAfter] <= ctx.tx[sfFinishAfter]) return temBAD_EXPIRATION; + if (ctx.tx.isFieldPresent(sfFinishFunction) && + !ctx.tx.isFieldPresent(sfCancelAfter)) + return temBAD_EXPIRATION; + if (ctx.rules.enabled(fix1571)) { // In the absence of a FinishAfter, the escrow can be finished // immediately, which can be confusing. When creating an escrow, // we want to ensure that either a FinishAfter time is explicitly // specified or a completion condition is attached. - if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition]) + if (!ctx.tx[~sfFinishAfter] && !ctx.tx[~sfCondition] && + !ctx.tx[~sfFinishFunction]) + { + JLOG(ctx.j.debug()) << "Must have at least one of FinishAfter, " + "Condition, or FinishFunction."; return temMALFORMED; + } } if (auto const cb = ctx.tx[~sfCondition]) @@ -139,6 +171,19 @@ EscrowCreate::preflight(PreflightContext const& ctx) return temDISABLED; } + if (ctx.tx.isFieldPresent(sfFinishFunction)) + { + auto const code = ctx.tx.getFieldVL(sfFinishFunction); + if (code.size() == 0 || + code.size() > ctx.app.config().FEES.extension_size_limit) + { + JLOG(ctx.j.debug()) + << "EscrowCreate.FinishFunction bad size " << code.size(); + return temMALFORMED; + } + // TODO: add check to ensure this is valid WASM code + } + return preflight2(ctx); } @@ -243,6 +288,8 @@ EscrowCreate::doApply() (*slep)[~sfCancelAfter] = ctx_.tx[~sfCancelAfter]; (*slep)[~sfFinishAfter] = ctx_.tx[~sfFinishAfter]; (*slep)[~sfDestinationTag] = ctx_.tx[~sfDestinationTag]; + (*slep)[~sfFinishFunction] = ctx_.tx[~sfFinishFunction]; + (*slep)[~sfData] = ctx_.tx[~sfData]; ctx_.view().insert(slep); @@ -266,8 +313,13 @@ EscrowCreate::doApply() } // Deduct owner's balance, increment owner count + // TODO: determine actual reserve based on FinishFunction size (*sle)[sfBalance] = (*sle)[sfBalance] - ctx_.tx[sfAmount]; - adjustOwnerCount(ctx_.view(), sle, 1, ctx_.journal); + adjustOwnerCount( + ctx_.view(), + sle, + ctx_.tx.isFieldPresent(sfFinishFunction) ? 2 : 1, + ctx_.journal); ctx_.view().update(sle); return tesSUCCESS; @@ -303,6 +355,13 @@ EscrowFinish::preflight(PreflightContext const& ctx) !ctx.rules.enabled(featureCredentials)) return temDISABLED; + if (ctx.tx.isFieldPresent(sfComputationAllowance) && + !ctx.rules.enabled(featureSmartEscrow)) + { + JLOG(ctx.j.debug()) << "SmartEscrow not enabled"; + return temDISABLED; + } + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; @@ -312,7 +371,10 @@ EscrowFinish::preflight(PreflightContext const& ctx) // If you specify a condition, then you must also specify // a fulfillment. if (static_cast(cb) != static_cast(fb)) + { + JLOG(ctx.j.debug()) << "Condition != Fulfillment"; return temMALFORMED; + } // Verify the transaction signature. If it doesn't work // then don't do any more work. @@ -341,6 +403,16 @@ EscrowFinish::preflight(PreflightContext const& ctx) } } + if (auto const allowance = ctx.tx[~sfComputationAllowance]; allowance) + { + if (*allowance > ctx.app.config().FEES.extension_compute_limit) + { + JLOG(ctx.j.debug()) + << "ComputationAllowance too large: " << *allowance; + return temBAD_LIMIT; + } + } + if (auto const err = credentials::checkFields(ctx); !isTesSuccess(err)) return err; @@ -356,7 +428,10 @@ EscrowFinish::calculateBaseFee(ReadView const& view, STTx const& tx) { extraFee += view.fees().base * (32 + (fb->size() / 16)); } - + if (auto const allowance = tx[~sfComputationAllowance]; allowance) + { + extraFee += (*allowance) * view.fees().gasPrice / MICRO_DROPS_PER_DROP; + } return Transactor::calculateBaseFee(view, tx) + extraFee; } @@ -366,6 +441,34 @@ EscrowFinish::preclaim(PreclaimContext const& ctx) if (!ctx.view.rules().enabled(featureCredentials)) return Transactor::preclaim(ctx); + if (ctx.view.rules().enabled(featureSmartEscrow)) + { + // this check is done in doApply before this amendment is enabled + auto const k = keylet::escrow(ctx.tx[sfOwner], ctx.tx[sfOfferSequence]); + auto const slep = ctx.view.read(k); + if (!slep) + return tecNO_TARGET; + + if (slep->isFieldPresent(sfFinishFunction)) + { + if (!ctx.tx.isFieldPresent(sfComputationAllowance)) + { + JLOG(ctx.j.debug()) + << "FinishFunction requires ComputationAllowance"; + return tefWASM_FIELD_NOT_INCLUDED; + } + } + else + { + if (ctx.tx.isFieldPresent(sfComputationAllowance)) + { + JLOG(ctx.j.debug()) << "FinishFunction not present, " + "ComputationAllowance present"; + return tefNO_WASM; + } + } + } + if (auto const err = credentials::valid(ctx, ctx.tx[sfAccount]); !isTesSuccess(err)) return err; @@ -379,7 +482,15 @@ EscrowFinish::doApply() auto const k = keylet::escrow(ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]); auto const slep = ctx_.view().peek(k); if (!slep) - return tecNO_TARGET; + return ctx_.view().rules().enabled(featureSmartEscrow) ? tecINTERNAL + : tecNO_TARGET; + + // Order of processing the release conditions (in order of performance): + // FinishAfter/CancelAfter + // Destination validity (after SmartEscrow is enabled) + // Condition/Fulfillment + // Destination validity (before SmartEscrow is enabled) + // FinishFunction // If a cancel time is present, a finish operation should only succeed prior // to that time. fix1571 corrects a logic error in the check that would make @@ -390,11 +501,17 @@ EscrowFinish::doApply() // Too soon: can't execute before the finish time if ((*slep)[~sfFinishAfter] && !after(now, (*slep)[sfFinishAfter])) + { + JLOG(j_.debug()) << "Too soon"; return tecNO_PERMISSION; + } // Too late: can't execute after the cancel time if ((*slep)[~sfCancelAfter] && after(now, (*slep)[sfCancelAfter])) + { + JLOG(j_.debug()) << "Too late"; return tecNO_PERMISSION; + } } else { @@ -402,13 +519,35 @@ EscrowFinish::doApply() if ((*slep)[~sfFinishAfter] && ctx_.view().info().parentCloseTime.time_since_epoch().count() <= (*slep)[sfFinishAfter]) + { + JLOG(j_.debug()) << "Too soon?"; return tecNO_PERMISSION; + } // Too late? if ((*slep)[~sfCancelAfter] && ctx_.view().info().parentCloseTime.time_since_epoch().count() <= (*slep)[sfCancelAfter]) + { + JLOG(j_.debug()) << "Too late?"; return tecNO_PERMISSION; + } + } + + AccountID const destID = (*slep)[sfDestination]; + auto const sled = ctx_.view().peek(keylet::account(destID)); + if (ctx_.view().rules().enabled(featureSmartEscrow)) + { + // NOTE: Escrow payments cannot be used to fund accounts. + if (!sled) + return tecNO_DST; + + if (ctx_.view().rules().enabled(featureDepositAuth)) + { + if (auto err = verifyDepositPreauth(ctx_, account_, destID, sled); + !isTesSuccess(err)) + return err; + } } // Check cryptocondition fulfillment @@ -458,17 +597,56 @@ EscrowFinish::doApply() return tecCRYPTOCONDITION_ERROR; } - // NOTE: Escrow payments cannot be used to fund accounts. - AccountID const destID = (*slep)[sfDestination]; - auto const sled = ctx_.view().peek(keylet::account(destID)); - if (!sled) - return tecNO_DST; - - if (ctx_.view().rules().enabled(featureDepositAuth)) + if (!ctx_.view().rules().enabled(featureSmartEscrow)) { - if (auto err = verifyDepositPreauth(ctx_, account_, destID, sled); - !isTesSuccess(err)) - return err; + // NOTE: Escrow payments cannot be used to fund accounts. + if (!sled) + return tecNO_DST; + + if (ctx_.view().rules().enabled(featureDepositAuth)) + { + if (auto err = verifyDepositPreauth(ctx_, account_, destID, sled); + !isTesSuccess(err)) + return err; + } + } + + // Execute custom release function + if ((*slep)[~sfFinishFunction]) + { + JLOG(j_.trace()) + << "The escrow has a finish function, running WASM code..."; + // WASM execution + auto const wasmStr = slep->getFieldVL(sfFinishFunction); + std::vector wasm(wasmStr.begin(), wasmStr.end()); + std::string funcName("finish"); + + WasmHostFunctionsImpl ledgerDataProvider(ctx_, k); + + if (!ctx_.tx.isFieldPresent(sfComputationAllowance)) + { + // already checked above, this check is just in case + return tecINTERNAL; + } + std::uint32_t allowance = ctx_.tx[sfComputationAllowance]; + auto re = runEscrowWasm(wasm, funcName, &ledgerDataProvider, allowance); + JLOG(j_.trace()) << "Escrow WASM ran"; + if (re.has_value()) + { + auto reValue = re.value().result; + JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue) + << ", cost: " << re.value().cost; + if (!reValue) + { + // ctx_.view().update(slep); + return tecWASM_REJECTED; + } + } + else + { + JLOG(j_.debug()) << "WASM Failure: " + transHuman(re.error()); + return re.error(); + } } AccountID const account = (*slep)[sfAccount]; @@ -501,7 +679,11 @@ EscrowFinish::doApply() // Adjust source owner count auto const sle = ctx_.view().peek(keylet::account(account)); - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + adjustOwnerCount( + ctx_.view(), + sle, + slep->isFieldPresent(sfFinishFunction) ? -2 : -1, + ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger @@ -583,7 +765,11 @@ EscrowCancel::doApply() // Transfer amount back to owner, decrement owner count auto const sle = ctx_.view().peek(keylet::account(account)); (*sle)[sfBalance] = (*sle)[sfBalance] + (*slep)[sfAmount]; - adjustOwnerCount(ctx_.view(), sle, -1, ctx_.journal); + adjustOwnerCount( + ctx_.view(), + sle, + slep->isFieldPresent(sfFinishFunction) ? -2 : -1, + ctx_.journal); ctx_.view().update(sle); // Remove escrow from ledger diff --git a/src/xrpld/app/tx/detail/Escrow.h b/src/xrpld/app/tx/detail/Escrow.h index 78acdbee00..0a74f31b3a 100644 --- a/src/xrpld/app/tx/detail/Escrow.h +++ b/src/xrpld/app/tx/detail/Escrow.h @@ -36,6 +36,9 @@ public: static TxConsequences makeTxConsequences(PreflightContext const& ctx); + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + static NotTEC preflight(PreflightContext const& ctx); diff --git a/src/xrpld/app/tx/detail/Transactor.cpp b/src/xrpld/app/tx/detail/Transactor.cpp index cc82f7c3ca..f392a53e7a 100644 --- a/src/xrpld/app/tx/detail/Transactor.cpp +++ b/src/xrpld/app/tx/detail/Transactor.cpp @@ -297,9 +297,9 @@ Transactor::checkFee(PreclaimContext const& ctx, XRPAmount baseFee) if (balance < feePaid) { - JLOG(ctx.j.trace()) << "Insufficient balance:" - << " balance=" << to_string(balance) - << " paid=" << to_string(feePaid); + JLOG(ctx.j.trace()) + << "Insufficient balance:" << " balance=" << to_string(balance) + << " paid=" << to_string(feePaid); if ((balance > beast::zero) && !ctx.view.open()) { @@ -952,6 +952,22 @@ removeExpiredCredentials( } } +static void +modifyWasmDataFields( + ApplyView& view, + std::vector> const& wasmObjects, + beast::Journal viewJ) +{ + for (auto const& [index, data] : wasmObjects) + { + if (auto const sle = view.peek(keylet::escrow(index))) + { + sle->setFieldVL(sfData, data); + view.update(sle); + } + } +} + static void removeDeletedTrustLines( ApplyView& view, @@ -1109,6 +1125,7 @@ Transactor::operator()() else if ( (result == tecOVERSIZE) || (result == tecKILLED) || (result == tecINCOMPLETE) || (result == tecEXPIRED) || + (result == tecWASM_REJECTED) || (isTecClaimHardFail(result, view().flags()))) { JLOG(j_.trace()) << "reapplying because of " << transToken(result); @@ -1121,13 +1138,16 @@ Transactor::operator()() std::vector removedTrustLines; std::vector expiredNFTokenOffers; std::vector expiredCredentials; + std::vector> modifiedWasmObjects; bool const doOffers = ((result == tecOVERSIZE) || (result == tecKILLED)); bool const doLines = (result == tecINCOMPLETE); bool const doNFTokenOffers = (result == tecEXPIRED); bool const doCredentials = (result == tecEXPIRED); - if (doOffers || doLines || doNFTokenOffers || doCredentials) + bool const doWasmData = (result == tecWASM_REJECTED); + if (doOffers || doLines || doNFTokenOffers || doCredentials || + doWasmData) { ctx_.visit([doOffers, &removedOffers, @@ -1136,7 +1156,9 @@ Transactor::operator()() doNFTokenOffers, &expiredNFTokenOffers, doCredentials, - &expiredCredentials]( + &expiredCredentials, + doWasmData, + &modifiedWasmObjects]( uint256 const& index, bool isDelete, std::shared_ptr const& before, @@ -1171,6 +1193,13 @@ Transactor::operator()() (before->getType() == ltCREDENTIAL)) expiredCredentials.push_back(index); } + + if (doWasmData && before && after && + (before->getType() == ltESCROW)) + { + modifiedWasmObjects.push_back( + std::make_pair(index, after->getFieldVL(sfData))); + } }); } @@ -1200,6 +1229,10 @@ Transactor::operator()() removeExpiredCredentials( view(), expiredCredentials, ctx_.app.journal("View")); + if (result == tecWASM_REJECTED) + modifyWasmDataFields( + view(), modifiedWasmObjects, ctx_.app.journal("View")); + applied = isTecClaim(result); } diff --git a/src/xrpld/consensus/Consensus.h b/src/xrpld/consensus/Consensus.h index f3265cf381..7e48ff0e50 100644 --- a/src/xrpld/consensus/Consensus.h +++ b/src/xrpld/consensus/Consensus.h @@ -1121,8 +1121,8 @@ Consensus::checkLedger(std::unique_ptr const& clog) auto netLgr = adaptor_.getPrevLedger(prevLedgerID_, previousLedger_, mode_.get()); - CLOG(clog) << "network ledgerid " << netLgr << ", " - << "previous ledger " << prevLedgerID_ << ". "; + CLOG(clog) << "network ledgerid " << netLgr << ", " << "previous ledger " + << prevLedgerID_ << ". "; if (netLgr != prevLedgerID_) { @@ -1213,8 +1213,7 @@ Consensus::phaseOpen(std::unique_ptr const& clog) adaptor_.parms().ledgerIDLE_INTERVAL, 2 * previousLedger_.closeTimeResolution()); CLOG(clog) << "idle interval set to " << idleInterval.count() - << "ms based on " - << "ledgerIDLE_INTERVAL: " + << "ms based on " << "ledgerIDLE_INTERVAL: " << adaptor_.parms().ledgerIDLE_INTERVAL.count() << ", previous ledger close time resolution: " << previousLedger_.closeTimeResolution().count() << "ms. "; @@ -1262,8 +1261,7 @@ Consensus::shouldPause( << "roundTime: " << result_->roundTime.read().count() << ", " << "max consensus time: " << parms.ledgerMAX_CONSENSUS.count() << ", " << "validators: " << totalValidators << ", " - << "laggards: " << laggards << ", " - << "offline: " << offline << ", " + << "laggards: " << laggards << ", " << "offline: " << offline << ", " << "quorum: " << quorum << ")"; if (!ahead || !laggards || !totalValidators || !adaptor_.validator() || @@ -1624,8 +1622,8 @@ Consensus::updateOurPositions( if (!haveCloseTimeConsensus_) { JLOG(j_.debug()) - << "No CT consensus:" - << " Proposers:" << currPeerPositions_.size() + << "No CT consensus:" << " Proposers:" + << currPeerPositions_.size() << " Mode:" << to_string(mode_.get()) << " Thresh:" << threshConsensus << " Pos:" << consensusCloseTime.time_since_epoch().count(); diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 4fdce92c8a..c1b143b799 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -73,6 +73,15 @@ struct FeeSetup /** The per-owned item reserve requirement in drops. */ XRPAmount owner_reserve{2 * DROPS_PER_XRP}; + /** The compute limit for Feature Extensions. */ + std::uint32_t extension_compute_limit{1'000'000}; + + /** The WASM size limit for Feature Extensions. */ + std::uint32_t extension_size_limit{100'000}; + + /** The price of 1 WASM gas, in micro-drops. */ + std::uint32_t gas_price{1'000'000}; + /* (Remember to update the example cfg files when changing any of these * values.) */ }; diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index b132987d08..2d92c5360e 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -1104,6 +1104,12 @@ setup_FeeVote(Section const& section) setup.account_reserve = temp; if (set(temp, "owner_reserve", section)) setup.owner_reserve = temp; + if (set(temp, "extension_compute_limit", section)) + setup.extension_compute_limit = temp; + if (set(temp, "extension_size_limit", section)) + setup.extension_size_limit = temp; + if (set(temp, "gas_price", section)) + setup.gas_price = temp; } return setup; } diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index aa6e2dda8f..7bd665bbfa 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -459,8 +459,7 @@ accountHolds( amount.clear(Issue{currency, issuer}); } - JLOG(j.trace()) << "accountHolds:" - << " account=" << to_string(account) + JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(account) << " amount=" << amount.getFullText(); return view.balanceHook(account, issuer, amount); @@ -641,8 +640,7 @@ xrpLiquid( STAmount const amount = (balance < reserve) ? STAmount{0} : balance - reserve; - JLOG(j.trace()) << "accountHolds:" - << " account=" << to_string(id) + JLOG(j.trace()) << "accountHolds:" << " account=" << to_string(id) << " amount=" << amount.getFullText() << " fullBalance=" << fullBalance.getFullText() << " balance=" << balance.getFullText() diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index cb3a7a69f5..9d13aab3a8 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -1271,8 +1271,8 @@ PeerImp::handleTransaction( { // If we've never been in synch, there's nothing we can do // with a transaction - JLOG(p_journal_.debug()) << "Ignoring incoming transaction: " - << "Need network ledger"; + JLOG(p_journal_.debug()) + << "Ignoring incoming transaction: " << "Need network ledger"; return; } diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index e23bbc29e1..b3922f63b3 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -1132,9 +1132,9 @@ public: } else { - JLOG(m_journal.error()) << beast::leftw(18) << "Logic failed " - << "'" << source->name() << "' fetch, " - << results.error.message(); + JLOG(m_journal.error()) + << beast::leftw(18) << "Logic failed " << "'" << source->name() + << "' fetch, " << results.error.message(); } }