-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Modularise the wasm module #6441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: ripple/wasmi-host-functions
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| #pragma once | ||
|
|
||
| #include <xrpl/tx/transactors/smart_escrow/HostFunc.h> | ||
|
|
||
| #include <string_view> | ||
| #include <vector> | ||
|
|
||
| namespace xrpl { | ||
|
|
||
| // Escrow-specific WASM constants | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is too obvious, it is not needed here |
||
| static std::string_view const ESCROW_FUNCTION_NAME = "finish"; | ||
|
|
||
| /** | ||
| * Creates WASM imports for smart escrow execution. | ||
| * | ||
| * Binds all host functions (ledger access, crypto, float operations, etc.) | ||
| * to the WASM import table with their associated gas costs. | ||
| * | ||
| * @param hfs The host functions implementation to bind | ||
| * @return Shared pointer to the import vector | ||
| */ | ||
| std::shared_ptr<ImportVec> | ||
| createWasmImport(HostFunctions& hfs); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't belong to "Escrow", it is more general. |
||
|
|
||
| /** | ||
| * Runs smart escrow WASM code. | ||
| * | ||
| * Executes the specified function in the WASM module with the given parameters | ||
| * and gas limit. | ||
| * | ||
| * @param wasmCode The compiled WASM bytecode | ||
| * @param hfs Host functions implementation | ||
| * @param funcName Function to execute (default: "finish") | ||
| * @param params Parameters to pass to the function | ||
| * @param gasLimit Maximum gas to consume (-1 for unlimited) | ||
| * @return EscrowResult on success, TER error code on failure | ||
| */ | ||
| Expected<EscrowResult, TER> | ||
| runEscrowWasm( | ||
| Bytes const& wasmCode, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| std::string_view funcName = ESCROW_FUNCTION_NAME, | ||
| std::vector<WasmParam> const& params = {}, | ||
| int64_t gasLimit = -1); | ||
|
|
||
| /** | ||
| * Validates smart escrow WASM code during preflight. | ||
| * | ||
| * Checks that the WASM module is valid and contains the expected function | ||
| * signature without actually executing the code. | ||
| * | ||
| * @param wasmCode The compiled WASM bytecode | ||
| * @param hfs Host functions implementation | ||
| * @param funcName Function to validate (default: "finish") | ||
| * @param params Parameters to validate against | ||
| * @return tesSUCCESS if valid, error code otherwise | ||
| */ | ||
| NotTEC | ||
| preflightEscrowWasm( | ||
| Bytes const& wasmCode, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| std::string_view funcName = ESCROW_FUNCTION_NAME, | ||
| std::vector<WasmParam> const& params = {}); | ||
|
|
||
| } // namespace xrpl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,11 +14,14 @@ The module follows a layered architecture: | |
|
|
||
| ``` | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ WasmEngine (WasmVM.h) │ | ||
| │ EscrowWasm.h │ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is incorrect. The WasmEngine is not specific to Escrow (it's used by Escrow, but will be used by more in the future). |
||
| │ runEscrowWasm(), preflightEscrowWasm() │ | ||
| │ Host function registration │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ WasmiEngine (WasmiVM.h) │ | ||
| │ wasm/WasmEngine.h │ | ||
| │ Singleton facade for WASM runtime execution │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ wasm/WasmiRuntime.h │ | ||
| │ Low-level wasmi interpreter integration │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ HostFuncWrapper │ HostFuncImpl │ | ||
|
|
@@ -31,15 +34,18 @@ The module follows a layered architecture: | |
|
|
||
| ### Key Components | ||
|
|
||
| - **`WasmVM.h` / `detail/WasmVM.cpp`** - High-level facade providing: | ||
| - `WasmEngine` singleton that wraps the underlying WASM interpreter | ||
| - **`EscrowWasm.h` / `EscrowWasm.cpp`** - Escrow-specific WASM functions: | ||
| - `runEscrowWasm()` - Execute WASM code for escrow finish | ||
| - `preflightEscrowWasm()` - Validate WASM code during preflight | ||
| - `createWasmImport()` - Register all host functions | ||
|
|
||
| - **`WasmiVM.h` / `detail/WasmiVM.cpp`** - Low-level integration with the | ||
| [wasmi](https://github.com/wasmi-labs/wasmi) WebAssembly interpreter: | ||
| - `WasmiEngine` - Manages WASM modules, instances, and execution | ||
| - **`wasm/WasmEngine.h` / `wasm/WasmEngine.cpp`** - Generic WASM engine facade: | ||
| - `WasmEngine` singleton that wraps the underlying WASM interpreter | ||
| - WASM constants (`W_ENV`, `W_MEM`, `MAX_PAGES`, etc.) | ||
|
|
||
| - **`wasm/WasmiRuntime.h` / `wasm/WasmiRuntime.cpp`** - Low-level integration | ||
| with the [wasmi](https://github.com/wasmi-labs/wasmi) WebAssembly interpreter: | ||
| - `WasmiRuntime` - Manages WASM modules, instances, and execution | ||
| - Memory management and gas metering | ||
| - Function invocation and result handling | ||
|
|
||
|
|
@@ -85,13 +91,13 @@ organized into categories: | |
|
|
||
| For the complete list of available host functions, their WASM names, and gas | ||
| costs, see the [XLS-0102 specification](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html) | ||
| or `detail/WasmVM.cpp` where they are registered via `WASM_IMPORT_FUNC2` macros. | ||
| or `EscrowWasm.cpp` where they are registered via `WASM_IMPORT_FUNC2` macros. | ||
| For method signatures, see `HostFunc.h`. | ||
|
|
||
| ## Gas Model | ||
|
|
||
| Each host function has an associated gas cost. The gas cost is specified when | ||
| registering the function in `detail/WasmVM.cpp`: | ||
| registering the function in `EscrowWasm.cpp`: | ||
|
|
||
| ```cpp | ||
| WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60); | ||
|
|
@@ -173,7 +179,7 @@ myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* resu | |
| } | ||
| ``` | ||
|
|
||
| ### 6. Register in WasmVM.cpp | ||
| ### 6. Register in EscrowWasm.cpp | ||
|
|
||
| Add the function registration in `setCommonHostFunctions()` or | ||
| `createWasmImport()`: | ||
|
|
@@ -184,6 +190,6 @@ WASM_IMPORT_FUNC2(i, myNewFunction, "my_new_function", hfs, 100); | |
| ``` | ||
| > [!IMPORTANT] | ||
| > New host functions MUST be amendment-gated in `WasmVM.cpp`. | ||
| > New host functions MUST be amendment-gated in `EscrowWasm.cpp`. | ||
| > Wrap the registration in an amendment check to ensure the function is only | ||
| > available after the corresponding amendment is enabled on the network. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,12 @@ | ||
| #pragma once | ||
|
|
||
| #include <xrpld/app/wasm/HostFunc.h> | ||
| #include <xrpl/tx/transactors/smart_escrow/HostFunc.h> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Smart Escrow is just one feature, wheras the WASM VM will power multiple "Smart Features." Because of that, I think may
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we want it to be in the transactors module or a separate module entirely?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea was, once we want to use wasm for something else, we take the smart_escrow/wasm folder and make it a top level module like HostFunc is something tightly coupled with smart escrow and I don't think we'll reuse it.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Actually, HostFunctions are coupled to the broader category of Smart Features (with more coming soon), not just Smart Escrow. Smart Escrow serves as a proving ground for generalized WASM functionality in xrpld. Future WASM-based features—like Smart Features, Smart Contracts, potentially even Native Transactors—will reuse this layer. Ultimately, WASM should be viewed as a cross-cutting architectural feature that may even extend beyond transactor processing. |
||
|
|
||
| #include <string_view> | ||
|
|
||
| namespace xrpl { | ||
|
|
||
| // Generic WASM constants | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment explain / clarify nothing, I don't think we need to add obvious comments. |
||
| 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"; | ||
|
|
@@ -16,15 +17,19 @@ 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"; | ||
|
|
||
| static std::string_view const ESCROW_FUNCTION_NAME = "finish"; | ||
|
|
||
| uint32_t const MAX_PAGES = 128; // 8MB = 64KB*128 | ||
|
|
||
| class WasmiEngine; | ||
| class WasmiRuntime; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know choosing naming is tough process, so it need to be done in separate PR with proper discussion. For example "Runtime" is more suitable for javascript engine, wasm is more docker like. Renaming it here just complicate reviewing modularization. |
||
|
|
||
| /** | ||
| * WasmEngine - Singleton facade for WASM runtime execution. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about renaming. |
||
| * | ||
| * This class provides a high-level interface to the underlying Wasmi runtime, | ||
| * hiding the implementation details and providing a simple API for WASM execution. | ||
| */ | ||
| class WasmEngine | ||
| { | ||
| std::unique_ptr<WasmiEngine> const impl; | ||
| std::unique_ptr<WasmiRuntime> const impl; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about renaming. |
||
|
|
||
| WasmEngine(); | ||
|
|
||
|
|
@@ -67,24 +72,4 @@ class WasmEngine | |
| getJournal() const; | ||
| }; | ||
|
|
||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| std::shared_ptr<ImportVec> | ||
| createWasmImport(HostFunctions& hfs); | ||
|
|
||
| Expected<EscrowResult, TER> | ||
| runEscrowWasm( | ||
| Bytes const& wasmCode, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| std::string_view funcName = ESCROW_FUNCTION_NAME, | ||
| std::vector<WasmParam> const& params = {}, | ||
| int64_t gasLimit = -1); | ||
|
|
||
| NotTEC | ||
| preflightEscrowWasm( | ||
| Bytes const& wasmCode, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| std::string_view funcName = ESCROW_FUNCTION_NAME, | ||
| std::vector<WasmParam> const& params = {}); | ||
|
|
||
| } // namespace xrpl | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| #pragma once | ||
|
|
||
| #include <xrpld/app/wasm/WasmVM.h> | ||
| #include <xrpl/tx/transactors/smart_escrow/wasm/WasmEngine.h> | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same about renaming |
||
|
|
||
| #include <wasm.h> | ||
| #include <wasmi.h> | ||
|
|
@@ -178,7 +178,7 @@ struct ModuleWrapper | |
| buildImports(StorePtr& s, std::shared_ptr<ImportVec> const& imports); | ||
| }; | ||
|
|
||
| class WasmiEngine | ||
| class WasmiRuntime | ||
| { | ||
| EnginePtr engine_; | ||
| StorePtr store_; | ||
|
|
@@ -192,8 +192,8 @@ class WasmiEngine | |
| std::shared_ptr<HostFunctions> hfs_; | ||
|
|
||
| public: | ||
| WasmiEngine(); | ||
| ~WasmiEngine() = default; | ||
| WasmiRuntime(); | ||
| ~WasmiRuntime() = default; | ||
|
|
||
| static EnginePtr | ||
| init(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,16 +2,9 @@ | |
| // #define DEBUG_OUTPUT 1 | ||
| #endif | ||
|
|
||
| #include <xrpld/app/wasm/HostFunc.h> | ||
| #include <xrpld/app/wasm/HostFuncWrapper.h> | ||
| #include <xrpld/app/wasm/WasmiVM.h> | ||
|
|
||
| #include <xrpl/basics/Log.h> | ||
| #include <xrpl/protocol/AccountID.h> | ||
| #include <xrpl/protocol/Feature.h> | ||
| #include <xrpl/protocol/LedgerFormats.h> | ||
|
|
||
| #include <memory> | ||
| #include <xrpl/tx/transactors/smart_escrow/EscrowWasm.h> | ||
| #include <xrpl/tx/transactors/smart_escrow/HostFuncWrapper.h> | ||
| #include <xrpl/tx/transactors/smart_escrow/wasm/WasmEngine.h> | ||
|
|
||
| namespace xrpl { | ||
|
|
||
|
|
@@ -110,12 +103,13 @@ runEscrowWasm( | |
| { | ||
| // create VM and set cost limit | ||
| auto& vm = WasmEngine::instance(); | ||
| // vm.initMaxPages(MAX_PAGES); | ||
|
|
||
| auto const ret = vm.run(wasmCode, funcName, params, createWasmImport(*hfs), hfs, gasLimit, hfs->getJournal()); | ||
|
|
||
| // std::cout << "runEscrowWasm, mod size: " << wasmCode.size() | ||
| // << ", gasLimit: " << gasLimit << ", funcName: " << funcName; | ||
| #ifdef DEBUG_OUTPUT | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use another definition, as it not always needed during debugging. |
||
| std::cout << "runEscrowWasm, mod size: " << wasmCode.size() << ", gasLimit: " << gasLimit | ||
| << ", funcName: " << funcName; | ||
| #endif | ||
|
|
||
| if (!ret) | ||
| { | ||
|
|
@@ -140,63 +134,10 @@ preflightEscrowWasm( | |
| { | ||
| // create VM and set cost limit | ||
| auto& vm = WasmEngine::instance(); | ||
| // vm.initMaxPages(MAX_PAGES); | ||
|
|
||
| auto const ret = vm.check(wasmCode, funcName, params, createWasmImport(*hfs), hfs, hfs->getJournal()); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| WasmEngine::WasmEngine() : impl(std::make_unique<WasmiEngine>()) | ||
| { | ||
| } | ||
|
|
||
| WasmEngine& | ||
| WasmEngine::instance() | ||
| { | ||
| static WasmEngine e; | ||
| return e; | ||
| } | ||
|
|
||
| Expected<WasmResult<int32_t>, TER> | ||
| WasmEngine::run( | ||
| Bytes const& wasmCode, | ||
| std::string_view funcName, | ||
| std::vector<WasmParam> const& params, | ||
| std::shared_ptr<ImportVec> const& imports, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| int64_t gasLimit, | ||
| beast::Journal j) | ||
| { | ||
| return impl->run(wasmCode, funcName, params, imports, hfs, gasLimit, j); | ||
| } | ||
|
|
||
| NotTEC | ||
| WasmEngine::check( | ||
| Bytes const& wasmCode, | ||
| std::string_view funcName, | ||
| std::vector<WasmParam> const& params, | ||
| std::shared_ptr<ImportVec> const& imports, | ||
| std::shared_ptr<HostFunctions> const& hfs, | ||
| beast::Journal j) | ||
| { | ||
| return impl->check(wasmCode, funcName, params, imports, hfs, j); | ||
| } | ||
|
|
||
| void* | ||
| WasmEngine::newTrap(std::string const& msg) | ||
| { | ||
| return impl->newTrap(msg); | ||
| } | ||
|
|
||
| // LCOV_EXCL_START | ||
| beast::Journal | ||
| WasmEngine::getJournal() const | ||
| { | ||
| return impl->getJournal(); | ||
| } | ||
| // LCOV_EXCL_STOP | ||
|
|
||
| } // namespace xrpl | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name EscrowWasm.h is more like some ledger object / transaction style. EscrowWasmHelpers.h more suitable