|
| 1 | +# WASM Module for Programmable Escrows |
| 2 | + |
| 3 | +This module provides WebAssembly (WASM) execution capabilities for programmable |
| 4 | +escrows on the XRP Ledger. When an escrow is finished, the WASM code runs to |
| 5 | +determine whether the escrow conditions are met, enabling custom programmable |
| 6 | +logic for escrow release conditions. |
| 7 | + |
| 8 | +For the full specification, see |
| 9 | +[XLS-0102: WASM VM](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html). |
| 10 | + |
| 11 | +## Architecture |
| 12 | + |
| 13 | +The module follows a layered architecture: |
| 14 | + |
| 15 | +``` |
| 16 | +┌─────────────────────────────────────────────────────────────┐ |
| 17 | +│ WasmEngine (WasmVM.h) │ |
| 18 | +│ runEscrowWasm(), preflightEscrowWasm() │ |
| 19 | +│ Host function registration │ |
| 20 | +├─────────────────────────────────────────────────────────────┤ |
| 21 | +│ WasmiEngine (WasmiVM.h) │ |
| 22 | +│ Low-level wasmi interpreter integration │ |
| 23 | +├─────────────────────────────────────────────────────────────┤ |
| 24 | +│ HostFuncWrapper │ HostFuncImpl │ |
| 25 | +│ C-style WASM bridges │ C++ implementations │ |
| 26 | +├─────────────────────────────────────────────────────────────┤ |
| 27 | +│ HostFunc (Interface) │ |
| 28 | +│ Abstract base class for host functions │ |
| 29 | +└─────────────────────────────────────────────────────────────┘ |
| 30 | +``` |
| 31 | + |
| 32 | +### Key Components |
| 33 | + |
| 34 | +- **`WasmVM.h` / `detail/WasmVM.cpp`** - High-level facade providing: |
| 35 | + - `WasmEngine` singleton that wraps the underlying WASM interpreter |
| 36 | + - `runEscrowWasm()` - Execute WASM code for escrow finish |
| 37 | + - `preflightEscrowWasm()` - Validate WASM code during preflight |
| 38 | + - `createWasmImport()` - Register all host functions |
| 39 | + |
| 40 | +- **`WasmiVM.h` / `detail/WasmiVM.cpp`** - Low-level integration with the |
| 41 | + [wasmi](https://github.com/wasmi-labs/wasmi) WebAssembly interpreter: |
| 42 | + - `WasmiEngine` - Manages WASM modules, instances, and execution |
| 43 | + - Memory management and gas metering |
| 44 | + - Function invocation and result handling |
| 45 | + |
| 46 | +- **`HostFunc.h`** - Abstract `HostFunctions` base class defining the interface |
| 47 | + for all callable host functions. Each method returns |
| 48 | + `Expected<T, HostFunctionError>`. |
| 49 | + |
| 50 | +- **`HostFuncImpl.h` / `detail/HostFuncImpl*.cpp`** - Concrete |
| 51 | + `WasmHostFunctionsImpl` class that implements host functions with access to |
| 52 | + `ApplyContext` for ledger state queries. Implementation split across files: |
| 53 | + - `HostFuncImpl.cpp` - Core utilities (updateData, checkSignature, etc.) |
| 54 | + - `HostFuncImplFloat.cpp` - Float/number arithmetic operations |
| 55 | + - `HostFuncImplGetter.cpp` - Field access (transaction, ledger objects) |
| 56 | + - `HostFuncImplKeylet.cpp` - Keylet construction functions |
| 57 | + - `HostFuncImplLedgerHeader.cpp` - Ledger header info access |
| 58 | + - `HostFuncImplNFT.cpp` - NFT-related queries |
| 59 | + - `HostFuncImplTrace.cpp` - Debugging/tracing functions |
| 60 | + |
| 61 | +- **`HostFuncWrapper.h` / `detail/HostFuncWrapper.cpp`** - C-style wrapper |
| 62 | + functions that bridge WASM calls to C++ `HostFunctions` methods. Each host |
| 63 | + function has: |
| 64 | + - A `_proto` type alias defining the function signature |
| 65 | + - A `_wrap` function that extracts parameters and calls the implementation |
| 66 | + |
| 67 | +- **`ParamsHelper.h`** - Utilities for WASM parameter handling: |
| 68 | + - `WASM_IMPORT_FUNC` / `WASM_IMPORT_FUNC2` macros for registration |
| 69 | + - `wasmParams()` helper for building parameter vectors |
| 70 | + - Type conversion between WASM and C++ types |
| 71 | + |
| 72 | +## Host Functions |
| 73 | + |
| 74 | +Host functions allow WASM code to interact with the XRP Ledger. They are |
| 75 | +organized into categories: |
| 76 | + |
| 77 | +- **Ledger Information** - Access ledger sequence, timestamps, hashes, fees |
| 78 | +- **Transaction & Ledger Object Access** - Read fields from the transaction |
| 79 | + and ledger objects (including the current escrow object) |
| 80 | +- **Keylet Construction** - Build keylets to look up various ledger object types |
| 81 | +- **Cryptography** - Signature verification and hashing |
| 82 | +- **Float Arithmetic** - Mathematical operations for amount calculations |
| 83 | +- **NFT Operations** - Query NFT properties |
| 84 | +- **Tracing/Debugging** - Log messages for debugging |
| 85 | + |
| 86 | +For the complete list of available host functions, their WASM names, and gas |
| 87 | +costs, see the [XLS-0102 specification](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html) |
| 88 | +or `detail/WasmVM.cpp` where they are registered via `WASM_IMPORT_FUNC2` macros. |
| 89 | +For method signatures, see `HostFunc.h`. |
| 90 | + |
| 91 | +## Gas Model |
| 92 | + |
| 93 | +Each host function has an associated gas cost. The gas cost is specified when |
| 94 | +registering the function in `detail/WasmVM.cpp`: |
| 95 | + |
| 96 | +```cpp |
| 97 | +WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60); |
| 98 | +// ^^ gas cost |
| 99 | +``` |
| 100 | +
|
| 101 | +WASM execution is metered, and if the gas limit is exceeded, execution fails. |
| 102 | +
|
| 103 | +## Entry Point |
| 104 | +
|
| 105 | +The WASM module must export a function with the name defined by |
| 106 | +`ESCROW_FUNCTION_NAME` (currently `"finish"`). This function: |
| 107 | +
|
| 108 | +- Takes no parameters (or parameters passed via host function calls) |
| 109 | +- Returns an `int32_t`: |
| 110 | + - `1` (or positive): Escrow conditions are met, allow finish |
| 111 | + - `0` (or negative): Escrow conditions are not met, reject finish |
| 112 | +
|
| 113 | +## Adding a New Host Function |
| 114 | +
|
| 115 | +To add a new host function, follow these steps: |
| 116 | +
|
| 117 | +### 1. Add to HostFunc.h (Base Class) |
| 118 | +
|
| 119 | +Add a virtual method declaration with a default implementation that returns an |
| 120 | +error: |
| 121 | +
|
| 122 | +```cpp |
| 123 | +virtual Expected<ReturnType, HostFunctionError> |
| 124 | +myNewFunction(ParamType1 param1, ParamType2 param2) |
| 125 | +{ |
| 126 | + return Unexpected(HostFunctionError::INTERNAL); |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +### 2. Add to HostFuncImpl.h (Declaration) |
| 131 | + |
| 132 | +Add the method override declaration in `WasmHostFunctionsImpl`: |
| 133 | + |
| 134 | +```cpp |
| 135 | +Expected<ReturnType, HostFunctionError> |
| 136 | +myNewFunction(ParamType1 param1, ParamType2 param2) override; |
| 137 | +``` |
| 138 | +
|
| 139 | +### 3. Implement in detail/HostFuncImpl\*.cpp |
| 140 | +
|
| 141 | +Add the implementation in the appropriate file: |
| 142 | +
|
| 143 | +```cpp |
| 144 | +Expected<ReturnType, HostFunctionError> |
| 145 | +WasmHostFunctionsImpl::myNewFunction(ParamType1 param1, ParamType2 param2) |
| 146 | +{ |
| 147 | + // Implementation using ctx (ApplyContext) for ledger access |
| 148 | + return result; |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +### 4. Add Wrapper to HostFuncWrapper.h |
| 153 | + |
| 154 | +Add the prototype and wrapper declaration: |
| 155 | + |
| 156 | +```cpp |
| 157 | +using myNewFunction_proto = int32_t(uint8_t const*, int32_t, ...); |
| 158 | +wasm_trap_t* |
| 159 | +myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results); |
| 160 | +``` |
| 161 | +
|
| 162 | +### 5. Implement Wrapper in detail/HostFuncWrapper.cpp |
| 163 | +
|
| 164 | +Implement the C-style wrapper that bridges WASM to C++: |
| 165 | +
|
| 166 | +```cpp |
| 167 | +wasm_trap_t* |
| 168 | +myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results) |
| 169 | +{ |
| 170 | + // Extract parameters from params |
| 171 | + // Call hfs->myNewFunction(...) |
| 172 | + // Set results and return |
| 173 | +} |
| 174 | +``` |
| 175 | + |
| 176 | +### 6. Register in WasmVM.cpp |
| 177 | + |
| 178 | +Add the function registration in `setCommonHostFunctions()` or |
| 179 | +`createWasmImport()`: |
| 180 | + |
| 181 | +```cpp |
| 182 | +WASM_IMPORT_FUNC2(i, myNewFunction, "my_new_function", hfs, 100); |
| 183 | +// ^^ WASM name ^^ gas cost |
| 184 | +``` |
| 185 | +
|
| 186 | +> [!IMPORTANT] |
| 187 | +> New host functions MUST be amendment-gated in `WasmVM.cpp`. |
| 188 | +> Wrap the registration in an amendment check to ensure the function is only |
| 189 | +> available after the corresponding amendment is enabled on the network. |
0 commit comments