Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions include/xrpl/tx/transactors/smart_escrow/EscrowWasm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#pragma once
Copy link
Collaborator

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


#include <xrpl/tx/transactors/smart_escrow/HostFunc.h>

#include <string_view>
#include <vector>

namespace xrpl {

// Escrow-specific WASM constants
Copy link
Collaborator

Choose a reason for hiding this comment

The 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
@@ -1,7 +1,5 @@
#pragma once

#include <xrpld/app/wasm/ParamsHelper.h>

#include <xrpl/basics/Expected.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/beast/utility/Journal.h>
Expand All @@ -10,6 +8,7 @@
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/tx/transactors/smart_escrow/ParamsHelper.h>

namespace xrpl {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#pragma once

#include <xrpld/app/wasm/HostFunc.h>

#include <xrpl/tx/ApplyContext.h>
#include <xrpl/tx/transactors/smart_escrow/HostFunc.h>

namespace xrpl {
class WasmHostFunctionsImpl : public HostFunctions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <xrpld/app/wasm/WasmiVM.h>
#include <xrpl/tx/transactors/smart_escrow/wasm/WasmiRuntime.h>

namespace xrpl {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ The module follows a layered architecture:

```
┌─────────────────────────────────────────────────────────────┐
WasmEngine (WasmVM.h)
EscrowWasm.h
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 │
Expand All @@ -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

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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()`:
Expand All @@ -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>
Copy link
Collaborator

Choose a reason for hiding this comment

The 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 <xrpl/tx/transactors/wasm/HostFunc.h> is the better location?

Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Collaborator Author

@a1q123456 a1q123456 Feb 26, 2026

Choose a reason for hiding this comment

The 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 xrpl.wasm and then we implement other things for that feature specifically.

HostFunc is something tightly coupled with smart escrow and I don't think we'll reuse it.

Copy link
Collaborator

@sappenin sappenin Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HostFunc is something tightly coupled with smart escrow and I don't think we'll reuse it.

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
Copy link
Collaborator

@oleks-rip oleks-rip Feb 26, 2026

Choose a reason for hiding this comment

The 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";
Expand All @@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The 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.
Copy link
Collaborator

Choose a reason for hiding this comment

The 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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about renaming.


WasmEngine();

Expand Down Expand Up @@ -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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same about renaming


#include <wasm.h>
#include <wasmi.h>
Expand Down Expand Up @@ -178,7 +178,7 @@ struct ModuleWrapper
buildImports(StorePtr& s, std::shared_ptr<ImportVec> const& imports);
};

class WasmiEngine
class WasmiRuntime
{
EnginePtr engine_;
StorePtr store_;
Expand All @@ -192,8 +192,8 @@ class WasmiEngine
std::shared_ptr<HostFunctions> hfs_;

public:
WasmiEngine();
~WasmiEngine() = default;
WasmiRuntime();
~WasmiRuntime() = default;

static EnginePtr
init();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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)
{
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <xrpld/app/wasm/HostFuncImpl.h>

#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/tx/transactors/smart_escrow/HostFuncImpl.h>

namespace xrpl {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <xrpld/app/wasm/HostFuncImpl.h>

#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/tx/transactors/smart_escrow/HostFunc.h>

#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <xrpld/app/wasm/HostFuncImpl.h>

#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STBitString.h>
#include <xrpl/protocol/digest.h>
#include <xrpl/tx/transactors/smart_escrow/HostFuncImpl.h>

namespace xrpl {

Expand Down
Loading