This commit is contained in:
Sergey Kuznetsov
2026-08-04 16:35:55 +01:00
parent eb946d23af
commit 6c02c45cbe
39 changed files with 162 additions and 7618 deletions

View File

@@ -302,8 +302,6 @@ if(xrpld)
"${CMAKE_CURRENT_SOURCE_DIR}/src/test/*.cpp"
)
target_sources(xrpld PRIVATE ${sources})
target_link_libraries(xrpld rs_hello_world_cxxbridge)
endif()
target_link_libraries(xrpld Xrpl::boost Xrpl::opts Xrpl::libs xrpl.libxrpl)

View File

@@ -42,7 +42,6 @@ function(add_xrpl_crate name)
add_dependencies(xrpl_crates ${name}_cxxbridge)
endfunction()
add_xrpl_crate(rs_hello_world CRATE rs_hello_world FILES lib.rs)
add_xrpl_crate(xrpl_wasm_vm_ffi CRATE xrpl_wasm_vm_ffi FILES lib.rs)
# Test-only, and deliberately not part of xrpl_wasm_vm_ffi: it carries the `wat` assembler,

7
crates/Cargo.lock generated
View File

@@ -223,13 +223,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rs-hello_world"
version = "0.1.0"
dependencies = [
"cxx",
]
[[package]]
name = "scratch"
version = "1.0.9"

View File

@@ -1,5 +1,5 @@
[workspace]
members = ["hello_world", "xrpl-wasm-vm-ffi", "xrpl-wasm-vm", "xrpl-wasm-testkit", "xrpl-host-functions", "xrpl-host-functions-macros"]
members = ["xrpl-wasm-vm-ffi", "xrpl-wasm-vm", "xrpl-wasm-testkit", "xrpl-host-functions", "xrpl-host-functions-macros"]
resolver = "3"
[workspace.dependencies]

View File

@@ -1,10 +0,0 @@
[package]
name = "rs-hello_world"
version = "0.1.0"
edition.workspace = true
[lib]
crate-type = ["staticlib"]
[dependencies]
cxx.workspace = true

View File

@@ -1,10 +0,0 @@
#[cxx::bridge(namespace = "rs::hello_world")]
mod ffi {
extern "Rust" {
fn hello_world() -> String;
}
}
pub fn hello_world() -> String {
"hello_world".to_string()
}

View File

@@ -1,122 +0,0 @@
[← Rust WASM VM docs](index.md)
# The ABI: one declaration, three outputs
`crates/xrpl-host-functions/` is the one declaration. C compatibility adds a **third
output** beside the trait and the spec enum — a *generated, checked-in* C header with a CI
diff — not a second input. "Explicit vs hidden" is the wrong axis; "derivable and emitted"
is the right one, because C authors read a header, not a macro.
**Adding a host function** is one `host_functions!` entry plus one arm in
`register_host_functions`'s exhaustive `match` (a new `HostFunctionSpec` variant will not
compile until it is registered), one `MOCK_METHOD` in `MockHostFunctions`, and one method on
`HostContext` if C++ is to serve it.
## The lowering table
The DSL already implied this; it was never written down, and that was the whole gap.
```
params, in declared order:
&self -> nothing (receiver, not part of the ABI)
i32, bool -> i32 (bool: nonzero = true)
i64 -> i64
&[u8], &str -> i32 ptr, i32 len const uint8_t*, int32_t
&mut [u8] -> i32 ptr, i32 len uint8_t*, int32_t (an output region)
returns, always HostResult<T>; Err(e) -> negative code, or a trap when host-fatal:
HostResult<usize> -> i32 = bytes written into the output region
HostResult<i32>, <bool> -> i32 = the value
HostResult<()> -> i32 = 0
```
Total, unambiguous and **positional**: every wasm parameter is a declared parameter in
order, so the C prototype is a direct reading of the declaration. **The macro must reject
any type not in this table** — that is C++'s `WasmImpArgs` `static_assert` restored, and it
is what keeps the C API always surfaceable.
All five current declarations lower to exactly the deleted C++ `_proto` aliases (verified
2026-07-29; `&self` dropped below since it contributes no C parameter):
| Declaration | Derived C |
|---|---|
| `get_ledger_sqn(out: &mut [u8]) -> HostResult<usize>` | `int32_t(uint8_t*, int32_t)` |
| `get_current_ledger_obj_field(field: i32, out: &mut [u8]) -> HostResult<usize>` | `int32_t(int32_t, uint8_t*, int32_t)` |
| `sha512_half(data: &[u8], out: &mut [u8]) -> HostResult<usize>` | `int32_t(const uint8_t*, int32_t, uint8_t*, int32_t)` |
| `trace(msg: &str, data: &[u8], as_hex: bool) -> HostResult<()>` | `int32_t(const uint8_t*, int32_t, const uint8_t*, int32_t, int32_t)` |
| `trace_num(msg: &str, number: i64) -> HostResult<()>` | `int32_t(const uint8_t*, int32_t, int64_t)` |
**Discipline the table requires**: a byte output is an explicit `out: &mut [u8]` plus
`HostResult<usize>`, never a returned value. `get_ledger_sqn` writes 4 LE bytes and returns
4 — it does not return the sequence number; by the same rule `float_to_int` takes an out
region rather than returning `i64`. A scalar `HostResult<T>` means value-in-the-return.
**The out-region contract, which the engine relies on: write only if the whole value fits,
and return its true length either way.** So a host never needs to know the guest's buffer
size — the engine turns `n > cap` into `BufferTooSmall`.
## Closing the drift gap to `register.rs`
**wasmi 1.1 cannot introspect a registered host function's signature.** `Linker::get`
returns `None` for `func_wrap`'d functions — they land in `Definition::HostFunc`
(`wasmi-1.1.0/src/linker.rs:147`), and `Definition::ty()` exists at `:171` but `Definition`
and `get_definition` are private. So "assert `Func::ty()` equals the spec" is unavailable.
| Approach | `register.rs` | Guarantee |
|---|---|---|
| Generate closures wholesale | disappears | by construction |
| **Generate `link_*` shims, hand-write bodies** | **stays, readable** | **compile-time** |
| Hand-write everything + probe-module test | stays | test-time |
**Preferred: the middle row** — the macro emits the *type* without the *body*:
```rust
pub type Sha512HalfFn =
fn(Caller<'_, VmState<'_>>, i32, i32, i32, i32) -> Result<i32, wasmi::Error>;
pub fn link_sha512_half(l: &mut Linker<VmState<'_>>, f: Sha512HalfFn)
-> Result<(), LinkerError> { l.func_wrap(MODULE, HostFunctionSpec::Sha512Half.wasm_name(), f) }
```
Wrong arity, scalar type or return then becomes a compile error, and the same lowering
table emits both the alias and the C prototype so they cannot drift. *Constraint*: `fn`
pointers accept only non-capturing closures; every arm today is non-capturing, and one that
needs to capture can take `impl Fn(..) + Send + Sync + 'static` instead. Cheap extra worth
having: a **probe-module test** that synthesises a WAT module importing every function with
its declared type and instantiates it — the only check that also catches module-name and
missing-import mistakes, from the guest's side.
Deferred together: the shims, the generated header, the probe test.
## The ABI crate is a library both sides link
It is consumed as an ordinary dependency — by `xrpl-wasm-vm` today, the guest stdlib next.
Neither invokes `host_functions!`; consumers get the generated code, not the generator.
That makes four properties load-bearing:
| Property | Why | Status |
|---|---|---|
| `#![no_std]`, no allocator | the guest stdlib is strictly `no_std` | ✓ `Vec` left when byte outputs became `out: &mut [u8]` |
| zero runtime dependencies | anything else must also build for the guest | ✓ `cargo tree` is the proc-macro crate alone |
| builds for `wasm32-unknown-unknown` | it links into the guest | ✓ verified |
| implementable by **both** sides | one declaration, two implementors | ✓ the out-param shape is what buys this |
A host impl writes into `out` and returns the length; a guest impl forwards to the import
and decodes the `i32` through `HostError::from_code`, which range-checks — unlike the SDK's
bare transmute, which is "new error codes are UB in the guest" in
[open-questions.md](open-questions.md). One trait serves both *because the declaration is now
the wire shape*.
**Known gap.** The `#[link(wasm_import_module = "…")] unsafe extern "C" { … }` block is not
generated; the PoC's macro did generate it plus a `GuestHost` impl. If the stdlib
hand-writes it, that is precisely the drift a single source of truth exists to prevent. One
wrinkle to decide first: a generated guest impl needs `HostError::from_code`, a name no
declaration mentions, so it would be the first vocabulary dependency inside an otherwise
closed expansion.
**Convention: the expansion is closed.** Every name in it is generated or written in the
declarations; `names_no_crate_of_its_own` enforces it. The macro owns `HostFunctions`,
`HostFunctionSpec`, `ALL`, `wasm_name()`, `gas()` and the private `HostFnSpec` row type.
The facade hand-writes only the vocabulary declarations are written in — `HostError`,
`HostResult`, `HASH_LEN` — which resolve at the call site like `&[u8]` does. `HostFnSpec`
and `spec()` are private; read the table through `wasm_name()` / `gas()`.

View File

@@ -1,153 +0,0 @@
[← Rust WASM VM docs](index.md)
# The cxx bridge
`crates/xrpl-wasm-vm-ffi/src/lib.rs` is the whole of the Rust half; `HostContext.{h,cpp}` and
`WasmVM.{h,cpp}` are the C++ half. Two decisions carry the design.
Three crossings, not two: `run_escrow` in, the host calls back out, and `check_escrow`
in. The third goes one way only — screening a module needs no host — so it takes no
`HostContext`, has no C++-exception half to contain, and is the one bridge function the
crate's own tests can call outright.
**The result is total, not `Result<T>`.** cxx's `Result` sugar throws a `rust::Error` into
C++; a status is the better interface for a condition the caller has to turn into a TER
anyway. `RunResult { status, result, gas_used, detail }` flattens the engine's
`Result<RunOutcome, RunFailure>` because a cxx enum carries no payload, and `RunStatus` is
1:1 with `RunError` plus `Ok` and `Panic`. Both directions of that map are compile-enforced:
`status_of`'s `match` is exhaustive over `RunError`, and C++'s `switch` over the generated
enum has no `default`, so an outcome added to the engine fails to build until it has been
given a status *and* a TER.
**Neither side may unwind into the other, and the two halves are not symmetric.**
- A **C++ exception** is stopped in C++. Every `HostContext` method is `noexcept` and every
body goes through one `guarded()` that catches `std::exception` and `...`, journals, and
returns -1. Nothing relies on cxx's own `trycatch`, which only catches `std::exception`
and only for `Result` returns.
- A **Rust panic** is caught in Rust, by `guarded()` in the bridge. `[profile.release]`
turns overflow checks on, so this is a live path; `#[cfg(panic = "abort")]
compile_error!` keeps a profile change from silently defeating it.
- The asymmetry is what makes each half sufficient: because the C++ shims never unwind,
every frame between a panic and `catch_unwind` is Rust.
Both halves are named `guarded`, and each is one function that every crossing goes through:
Rust's takes the panic arm as an argument (`ffi::RunResult::panicked`), C++'s takes the value
to answer with if the call throws. Anything C++ catches there is xrpld's own — a bad
allocation, or a `funcName` that is not valid UTF-8 and so cannot become a `rust::Str`
never a wasm outcome, since those arrive as statuses.
`HostContext` holds a `HostFunctions&` and lowers its typed `std::expected` onto the wire.
The `&self`-vs-non-const worry was a non-issue: a `const` member function holding a
non-const reference can still call `cacheLedgerObj`/`updateData`. `cxx_name` on each method
keeps ABI names on the Rust side and rippled's camelBack on the C++ side. `guarded` names the
failing call through a defaulted `std::source_location` rather than a string per call site;
`__func__` would expand to `operator()` inside the lambda.
The five current functions needed **no change to `HostFunctions`** — the shim absorbs the
`uint32 → bytes` (via `adjustWasmEndianess`, which is where the wasm boundary's byte order
is decided for the whole system), `i32 → SField` and `Hash → 32 bytes` lowerings. Where that
will stop being true: `float_to_mant_exp` (two output regions), the `FieldLocator` entries,
and `updateData`.
## The TER map
`runEscrowWasm` owns it. `tecINTERNAL` reports no cost by convention: it says the fault is the
node's, and charging a transaction for a node's defect would write that defect into the ledger.
| `RunStatus` | TER | cost |
|---|---|---|
| `Ok` | — | `gas_used` |
| `OutOfGas` | `tecOUT_OF_GAS` | `gas_used` |
| `Trap`, `NoMemory`, `Instantiate` | `tecFAILED_PROCESSING` | `gas_used` |
| `Compile`, `EntryPoint` | `tecINTERNAL` | none |
| `Internal`, `Panic` | `tecINTERNAL` | none |
`Compile` and `EntryPoint` are `tecINTERNAL` because preflight decides both from the same
bytes and the same engine, so agreement is not a matter of degree: reaching apply means the
screening did not happen. `Instantiate` is **not** in that row, and the reason is the point of
the whole arrangement — see below. `NoMemory` had no old TER to match (it used to reach the
guest as code -14); `tecFAILED_PROCESSING` treats it as the contract fault it is.
One thing to settle before a long-lived escrow exists: `Compile` is only a node fault while
the engine's configuration never changes. A contract created under one feature set and
finished under another could legitimately fail to compile at apply, so either the config is
amendment-gated or `Compile` joins the charged row.
`gas <= 0` is refused as `temBAD_AMOUNT` before the engine is called, restoring what
`WasmiEngine::run` did — see [open-questions.md](open-questions.md).
## The preflight map
`preflightEscrowWasm` owns it, and it is deliberately flat: every fault in the module is
one answer, because a caller's only decision is whether the transaction may proceed.
| `CheckStatus` | `NotTEC` |
|---|---|
| `Ok` | `tesSUCCESS` |
| `Compile`, `Import`, `EntryPoint`, `Memory` | `temBAD_WASM` |
| `Panic` | `telFAILED_PROCESSING` |
The statuses stay distinct anyway: the *detail* is what a contract author needs, and one
status per stage keeps the map's arms reviewable and lets it grow without inventing
distinctions later.
`Panic` is not `temBAD_WASM`. A defect in the engine teaches nothing about the module, and
`tem` would record our bug as the transaction's malformation; `tel` is the preflight
analogue of `tecINTERNAL`'s "the fault is the node's" — local, not forwarded, no fee. Two
things follow that are worth stating: divergence between nodes is not what the code choice
fixes (a panic in deterministic code is not node-local, and if it were, no TER would
reconcile the two), and this arm has no test on the C++ side, because there is no reliable
way to make the engine panic from a fixture.
**The signature the C++ front does not have is the point**: `(Bytes, beast::Journal,
std::string_view) -> NotTEC`, with no `HostFunctions&`. The deleted `preflightEscrowWasm`
took one and could therefore never have been called from a real `preflight()`
`PreflightContext` has no view to build a host over.
## Why `Instantiate` is the contract's fault
The map must not depend on preflight being exhaustive, because it cannot be. `check` closes
compile, imports, the entry point and an exported memory over the page cap — but a memory a
module *keeps to itself* is absent from its exports, so such a module passes screening and
then fails to instantiate ([engine.md](engine.md)). That is a deterministic property of the
code, identical on every node, and nothing this node did; charging it as
`tecFAILED_PROCESSING` says so, where `tecINTERNAL` would blame the node and forgive the gas.
The other half is in the engine rather than the map: `vm::instantiation_failure` reports a
failure carrying a trap code as `RunError::Trap`, because a start section that traps is guest
code trapping, and a trap is the guest's fault wherever it happens. What is left for
`Instantiate` is a module the linker or the store would not accept at all —
`vm_limits::instantiation_failure_is_a_module_the_engine_will_not_accept` pins both shapes.
So `tecINTERNAL` now means what it says: `Internal` and `Panic`, the node's own defects, plus
the two stages preflight decides exactly.
## The one copy left on the byte path, and why it needs `HostFunctions` to change
The engine's side of the byte path is copy-free by construction — `write_into` hands the host
guest memory directly, `write_buffered` copies once after every rule has passed
([engine.md](engine.md)). **The C++ side then puts a copy back**, because `HostFunctions`
returns its answer *by value*:
```cpp
std::expected<Bytes, HostFunctionError> getCurrentLedgerObjField(SField const&) const;
```
`Bytes` is a `std::vector<std::uint8_t>`, so serving one field allocates, fills, gets copied
into `out` by `HostContext::answer`, and is freed — a heap round trip per host call, on a
consensus path, for a value the caller already has a buffer for. **49 of the 66 virtuals
return `Bytes` this way**; only the two `Hash` ones are inline.
The fix is an out-param form on `HostFunctions` itself — `(…, std::span<std::uint8_t> out)
-> std::expected<std::size_t, HostFunctionError>`, returning the value's true length on the
same "write only if it all fits" contract the ABI already uses end to end. That makes the
convention identical on both sides of the bridge and leaves `HostContext` with no copy to
make. Note the two shapes are not equivalent for every function: one that cannot know its
length without building the value still allocates internally, so the win is real for field
and keylet reads and smaller for the float ops.
Deliberately **not** done with the bridge: it touches 49 signatures plus
`WasmHostFunctionsImpl`, `HostFuncImpl*.cpp` and the test hosts, which is a mechanical sweep
that would bury the bridge in review. Sequence it after a caller exists, so the sweep can be
measured against something that runs.

View File

@@ -1,24 +0,0 @@
[← Rust WASM VM docs](index.md)
# Conventions
**Comments.** Terse. A comment should say something the compiler cannot check and the code
cannot show; everything else is a candidate for deletion. Keep: why an apparent redundancy is
not one (the `MAX_FIELD_BYTES` check beside the clamp; `is_fatal`/`host_fatal` as two lists;
`MUST_TRAP` not deriving from `is_fatal` — each of these has been "simplified" wrongly in a
mutation test at least once); load-bearing invariants; hidden contracts a signature cannot
state; wasmi facts that decide a design. Cut: prose restating the next line; the same
rationale on a field and on its reader; retellings of these docs.
**No references to C++ that will not survive the merge.** They read as evidence but point at
deleted files. The crate has none, in `src/` or `tests/`. Two live exceptions stand:
`Protocol.h`'s `kMaxWasmDataLength` and `kWasmTransferLimit`, which are where those numbers
are defined for the rest of the system. The parity evidence itself lives in
[history.md](history.md) instead, which is commit-pinned and therefore stays resolvable.
**No historical comments** in code — describe the present, not how it differs from a previous
state.
**C++ naming.** rippled's camelBack for methods, `k`-prefixed CamelCase for constants; the
bridge keeps ABI names on the Rust side and camelBack on the C++ side via `cxx_name`. Test
names are subject-first with no leading article ([testing.md](testing.md)).

View File

@@ -1,171 +0,0 @@
[← Rust WASM VM docs](index.md)
# How the engine works
Contracts worth knowing before changing anything, and the reasons that are not visible in
the code.
**Two channels for a result.** A value or a guest-actionable error is the `i32` the wasm
function returns (`>= 0` value, `< 0` a `HostError` code). A **host-fatal** error —
`OutOfGas`, `Internal`, `NoMemExported` — traps instead, carrying `FatalHostError(HostError)`
as the payload so `run` can name the condition with `downcast_ref` rather than
string-comparing a message. XLS-0102 requires immediate halting on gas exhaustion, and a
guest handed `OutOfGas` as a code would run to the end of its current basic block — a
stopping point wasmi's `ConsumeFuel` placement decides rather than the protocol.
`is_fatal` spells the set variant by variant so a new `HostError`'s channel is chosen, not
inherited from its number. **`OutOfTransferLimit` is soft**: the one budget a contract can
be expected to handle.
`is_fatal` and `vm::host_fatal` are two lists that must agree. One direction is
compiler-enforced (`host_fatal` is exhaustive, so a new variant fails to build);
`every_fatal_error_has_an_outcome_of_its_own` covers the other. `HostError::ALL` and
`HostFunctionSpec::ALL` exist because an exhaustive `match` forces you to *write an arm* but
cannot *enumerate* variants, and every const-assertion scheme over `ALL` is beaten by "add
the variant, give its arm a value, leave `ALL` alone". The airtight mechanism is a single
declaration site: a `host_errors!` macro emits the enum, `ALL` and `from_code` from one list.
**Every failure carries its cost.** `run` returns `Result<RunOutcome, RunFailure>` where
`RunFailure` is `{ error: RunError, fuel_used }`, so gas is on both paths by construction. A
cost that cannot be read becomes `RunError::Internal` rather than a number — `0` would
forgive a run its whole cost and `gas` would charge an untouched one for everything.
`guest_halted` asks "did the guest halt?" at *every* stage from instantiation on, so a start
section that burns the limit is `OutOfGas`, not `Instantiate`: the stage a run stopped at is
not what the caller maps. `instantiation_failure` finishes the thought — a failure carrying a
trap code is `Trap`, since a start section that traps is guest code trapping — leaving
`Instantiate` to mean a module the linker or the store would not accept.
**Two ways a byte answer reaches the guest**, both taking a `Region`:
- `write_into` — the host writes straight into the guest's output region. Used by calls with
no byte input. Zero copy.
- `write_buffered` — the host fills `VmState::out_buffer` and the engine copies it to the
guest once every rule has passed. Used by calls that also *read* guest memory, because a
`&mut` view of that memory admits no simultaneous `&` view. `Memory::data_and_store_mut`
(`memory/mod.rs:165`) returns `(&mut [u8], &mut T)` — guest bytes and store data in one
split borrow — which is what lets any number of inputs stay borrowed while the answer is
written. No copying inputs out, no `unsafe`.
`write_buffered` never tells the host the guest's capacity: it offers the whole buffer and
takes the value's true length, so nothing reaches guest memory until the length, bounds, fit
and budget have all passed — **a refused value reaches it in no part**, which `write_into`
can only bound rather than prevent. The output is judged *after* the inputs, so a call with
both malformed reports the input's verdict; `NoMemExported` precedes both, because there is
no memory to validate a region against. `MAX_FIELD_BYTES` is checked beside the clamp on
purpose: the clamp bounds the **bytes**, the check bounds the **status**, since a host
reports a true length that can exceed the region it was offered.
Why this shape: of the ~65 ABI entries, **38 have a byte input *and* a byte output**, 9 are
output-only, 18 are input-only or scalar. Of the 38, **22 have more than one region** — every
two-argument keylet, `nft_uri`, all four float arithmetic ops — which a one-input helper
cannot express at all. The 9 output-only ones are exactly the row a buffer makes worse, and
they keep `write_into`.
**`Region`** (`region.rs`) is the wire's `(ptr, len)` as one type. It cannot catch a swapped
pair — `Region::new(len, ptr)` compiles, and no type can do better where the values arrive
as indistinguishable `i32`s in positional order; that is a job for a reader or for the shim
generator. What it enforces is that the pair cannot be *used* unchecked: `range()` is the
only way to indices, and it is where `InvalidParams` (the conversion to `usize` is the
negativity check) and the end-overflow guard live. It sits in its own module because Rust
privacy is module-level — inside `abi.rs` the helpers could still read `.ptr` and skip the
check. Verified: an attempted bypass is `error[E0616]: field ptr of struct Region is
private`. Construction is **infallible on purpose**; validating in `new` would hoist the
output region's verdict above the host call and break the input-first order that
`a_read_write_checks_its_input_before_its_output` pins.
`Region::read` is then ordinary safe slicing, because a guest pointer is an *index*: wasm
linear memory is a byte array in the store, `mem.data(caller)` is a `&[u8]` over it, and
`data.get(start..end)` does the bounds check and returns a slice **aliasing** guest memory.
`get` rather than `[..]` because indexing panics, and a panic on a consensus path is a node
crash. Elision ties the returned slice's lifetime to `data`, so a host cannot stash an input
past the call.
**Two budgets.** Gas is charged per host call from the spec table, before the body runs
(`charged` is the one path, so it cannot be forgotten); exhaustion spends what is left. The
transfer budget counts only bytes *copied* across — `charge_transfer` has one call site per
write path. A borrowed read copies nothing and is not charged; what bounds how many reads a
run makes is gas. Typed reads that materialise a host object will charge; this ABI has none
yet, and the alignment-copy charge for unaligned field reads has nothing to attach to until a
`FieldLocator` function exists.
**Guest memory is resolved once per run, by kind.** `run` takes
`instance.exports(&store).find_map(Export::into_memory)` after `instantiate_and_start` and
keeps the handle in `VmState::memory`, so no call pays for an export lookup. By *kind*, never
by name: nothing in the wasm spec attaches meaning to `"memory"`. Caching is sound because a
`Memory` is an arena index, not a pointer — it survives `memory.grow`. Two consequences:
the field assumes **one module, one instance, one store per `run`** (module linking would
have to resolve per instance, or serve a call against the wrong memory), and **a start
section cannot make a host call needing memory** — `Module::instantiate` is `pub(crate)`, so
instantiation cannot be split from the start section. `a_start_section_cannot_make_a_host_call`
pins it.
**Engine config is consensus-fixed**: fuel on, floats off, every post-MVP proposal off, one
process-wide `Engine` behind a `LazyLock` (an `Engine` is internally `Arc`ed and `Send +
Sync`). Notably `wasmi = { default-features = false, features = ["std"] }` — wasmi's `wat`
feature is **on by default** and makes `Module::new` accept text as readily as binary, which
would put a text assembler in the consensus path and make a transaction's validity a build
flag. `the_vm_refuses_a_text_format_module` catches that coming back, and
`WasmVMTest.TextFormatModuleIsRejected` catches it from the guest's side.
**A start section cannot be rejected outright.** wasmi 1.1 exposes no
`InstancePre`/`ensure_no_start` and `ModuleHeader::start` is private, so only a byte-level
section scan would do it. It is metered and memory-capped regardless, since `run` installs
the fuel and the limiter before `instantiate_and_start`.
## Screening without running: `check`
`preflight.rs`'s `check` decides whether `run` would refuse a module before the guest's first
instruction — compile, imports, entry point — from **the compiled module alone**: no host, no
store, no gas, no execution. That is not economy, it is a requirement; the caller is a
transaction's preflight, which has no ledger to serve a host call from.
Three things keep it from becoming a second opinion. Both stages compile through `vm::compile`,
so the configuration that decides validity cannot differ. The import set is
`HostFunctionSpec::ALL`, which is also what `register_host_functions` iterates, so adding a
host function extends the check and the linker at once. And the entry point's three faults are
described by one `entry_point_fault`, called from `run` with wasmi's error appended.
The rules themselves are pure functions over what a module *declares*`check_import` takes
`(namespace, name, ExternType)`, `entry_point_fault` takes an `Option<ExternType>` — so the
unit tests state each rule, its precedence and its wording on inputs built directly, and
`tests/preflight.rs` is left to run real modules. Precedence is a decision, not an accident:
an import breaking two rules reports the namespace, which is what explains the module's other
imports too.
A fourth stage screens what a module *declares*: an exported linear memory whose initial size
is past the page cap is refused, since the store's limiter would refuse it anyway. The
**minimum** only — a declared maximum past the cap is legal and simply unreachable. Only the
exported memory is visible, which is enough for every contract the guest SDK produces, since
a contract needs an exported memory to make a host call at all.
What stays invisible is one module: a memory the module keeps to itself, over the cap, which
passes the check and then fails instantiation — the reason a run's refusal at that stage is
charged to the contract rather than blamed on the node ([bridge.md](bridge.md)). A start
section is invisible too, but no longer matters: a trap in one is reported as
`RunError::Trap` and charged like any other trap.
`what_static_screening_cannot_see` is that one module, and `screening_and_a_run_agree` pins
the equivalence everywhere else — in both directions, so a rule that refused a contract the
engine would have served fails too.
Import **signatures** are the deliberate gap: `check` compares names and kinds, not types, so
a mistyped import still parts a module from the engine at instantiation. Closing it needs the
expected `FuncType` per function, and the only non-duplicating source is the closure
`register.rs` registers — `wasmi::IntoFunc::into_func()` returns `(FuncType, _)` and
`Linker::func_wrap` is a thin wrapper over it, so making `register_host_functions` generic
over a sink would give the linker and a type table from one declaration site.
**A dead end, recorded so nobody retries it.** Host-function parameters cannot be newtypes.
`wasmi::WasmTy` looks implementable — public, no sealing supertrait — but its bound names
`UntypedVal`, which wasmi re-exports only through a **private** `mod core`
(`wasmi-1.1.0/src/lib.rs:109-137`). Probed: `error[E0603]: module core is private`. The
escape hatch is a direct `wasmi_core` dependency pinned in lockstep with wasmi's own, plus a
`#[doc(hidden)]` method — not worth it on a consensus path. So the wire stays `i32` and pairs
are formed on the first line of each arm.
## Known gap: `OutOfGas` does not always report the whole limit
A contract that loops until the meter empties reports the full budget, but a budget too small
to reach the first charge reports `0`: wasmi leaves the remaining fuel in place on its own
`OutOfFuel` trap, and only `abi::charge` forces it to zero. The deleted C++ path did this
deliberately (`iw.setGas(0)` on out-of-gas, so the cost was always the full limit). Closing
the gap is a one-line change in `vm::run`, but it is consensus-visible metadata, so it is
called out rather than slipped in.

View File

@@ -1,77 +0,0 @@
[← Rust WASM VM docs](index.md)
# History: review findings, and the deleted C++ path
## Review findings (2026-07-29)
A read of `vm.rs`, `abi.rs` and `register.rs` against the vendored wasmi 1.1.0. **Seventeen
of the eighteen are closed, C12 the only one left** — earlier revisions of these docs said
"fourteen of seventeen", which never matched the table. The rationale that is still
load-bearing has moved into [engine.md](engine.md).
| # | Finding | Outcome |
|---|---|---|
| A1 | Out-of-gas returned a code instead of trapping, so how much guest code ran after exhaustion was wasmi's business | ✓ two-channel design, `FatalHostError` payload |
| A2 | `run` discarded gas accounting on every failure path, and its error was a `String` | ✓ `RunFailure { error, fuel_used }` over a typed `RunError` |
| A3 | `HOST_MODULE = "host"` matched no guest that exists | ✓ `host_lib`, pinned by test |
| A4 | Transfer budget charged for bytes never copied, and charged before validation | ✓ one call site per write path; reads are free |
| A5 | `Module::new` accepted WAT text — a behaviour the rewrite introduced by accident | ✓ `default-features = false` |
| A6 | The memory export's *name* was a rule the rewrite introduced | ✓ resolved by kind, as C++ did |
| B6 | `AbiRet` was vestigial | ✓ deleted |
| B7 | The `i64` pipeline was pointless and lossy (silent truncating cast) | ✓ `HostResult<i32>` end to end |
| B8 | `cxx` was an unused dependency of this crate | ✓ removed |
| B9 | Seven broken intra-doc links, plus historical comments | ✓ fixed, `deny` added |
| C10 | The `"memory"` export was a string hash lookup on every host call | ✓ resolved once per run, by kind |
| C11 | `read_write` memset 1 KiB of stack per call and did not generalize past one byte input | ✓ replaced by `write_buffered` + `Region` |
| C12 | `Linker` rebuilt per run; module compiled per run with no cache | **open — [open-questions.md](open-questions.md)** |
| D13 | The public surface was accidental (`RunOutcome` unnameable, limits unreachable) | ✓ exported; `MAX_FIELD_BYTES` renamed out of `abi.rs` |
| D14 | `abi.rs` *claimed* every access was a checked slice op | ✓ `forbid(unsafe_code)` + cast lints enforce it |
| D15 | Zero tests | ✓ 79 in `xrpl-wasm-vm` |
| D16 | `gas = 0` accepted silently; `get_fuel().unwrap_or(0)` reported the whole limit; entry-point diagnostic wrong for a wrong-signature export | ✓ closed — `gas <= 0` is `temBAD_AMOUNT` in `runEscrowWasm` |
| D17 | The start-section TODO reads like a hole | ✓ documented as not closeable with wasmi 1.1 |
Three of the closed findings were **behaviour changes nobody had chosen** — A3, A5, A6 — and
all three restored C++ behaviour the rewrite had altered by accident. That is the pattern
worth carrying forward: on this path, "tidier than C++" is usually "different from
C++". A fourth decision, C11's buffer, extends it rather than restoring it.
## Reference points from the deleted C++ path
Import names and gas costs are ABI. The rest is *evidence of prior behaviour* — useful for
comparison and for the gas assertions in `Wasm_test.cpp`, not gospel. All recoverable with
`git show b7059deb9f^:<path>` — note that `WasmVM.{h,cpp}` exist again at that path with
entirely different contents, so the revision in that command is doing real work.
Deleted later, with the dead `wasmi::wasmi` link that was the only thing supplying their
`<wasm.h>`: `include/xrpl/tx/wasm/HostFuncWrapper.h` (the `*_proto` aliases and `*_wrap`
declarations, whose `.cpp` went in `b7059deb9f`) and `WasmImportsHelper.h` (`ImportVec`,
`WasmImpArgs`'s `static_assert`). Every remaining reference to either was inside a
commented-out file. The `_proto` aliases are the C lowering the table in [abi.md](abi.md)
reproduces, so they are worth reading before extending it.
- **Import names + per-call gas**: `src/libxrpl/tx/wasm/WasmVM.cpp`
(`setCommonHostFunctions`, 64 entries plus `set_data` registered only in
`createWasmImport`; e.g. `ldgr_index` 60, `sha512_half` 2000, `set_data` 1000, `float_pow`
5'500).
- **Guest-visible error codes**: `HostFunctionError` in `include/xrpl/tx/wasm/WasmCommon.h`
(-1 `Unimplemented` … -20 `FloatComputationError`; note **-11 is `OutOfTransferLimit`**
there, `InvalidDecoding` in the SDK; see "the two error enums have already drifted" in
[open-questions.md](open-questions.md)).
- **Host-fatal conditions were traps**: out-of-gas and internal errors threw
`hfErrOutOfGas` / `hfErrInternal` → trap → `tecOUT_OF_GAS` / `tecINTERNAL`. Only the
transfer limit was a soft, guest-visible failure.
- **Limits**: `maxPages = 128` (8 MiB), `kMaxWasmDataLength = 1024`, `kWasmTransferLimit =
1 << 20`. The last two are still live in `include/xrpl/protocol/Protocol.h:328,333`.
- **Transfer limit** was charged for bytes actually copied: host→guest writes (`setData`) and
typed reads materialising a host object (uint256, AccountID, Currency, Asset), plus
unaligned `FieldLocator` copies (+`unalignedGas = 50`). Plain slice/string reads were not
charged.
- **Check order after a value existed** (`setData`): params → data-too-large → no-memory →
out-of-bounds → buffer-too-small → transfer → copy. Inputs (`getDataSlice`) were validated
before the call. `write_buffered` follows this; `write_into` cannot, since it must
bounds-check before handing over a slice.
- **Entry point** was `escrow_finish` (`escrowFunctionName`); gas `-1` meant unlimited,
`<= 0` meant `temBAD_AMOUNT`; on out-of-gas the reported cost was the full limit. Positive
return = conditions met; `0` or negative = reject.
- **wasmi's fuel table is consensus input** — pin the version deliberately (currently
`wasmi = "1.1.0"`).

View File

@@ -1,114 +0,0 @@
# Rust WASM VM — working docs
The Rust wasmi engine for programmable escrows on branch `Wasm-vm-redesign`, and the cxx
bridge that connects it to xrpld.
| Read | When |
|---|---|
| [bridge.md](bridge.md) | changing anything that crosses between C++ and Rust, or the TER map |
| [engine.md](engine.md) | changing `vm.rs`, `preflight.rs`, `abi.rs`, `region.rs` — the invariants, and the wasmi facts that decided them |
| [abi.md](abi.md) | adding or changing a host function |
| [testing.md](testing.md) | running the loop, or adding a test on either side |
| [conventions.md](conventions.md) | before writing code or comments in the crate |
| [open-questions.md](open-questions.md) | the undecided ABI questions, and the two performance items gated on a benchmark |
| [history.md](history.md) | recovering deleted C++ behaviour, or checking a review finding's outcome |
## What this branch is doing
`Wasm-vm-redesign`: replacing the C++ wasmi **C-API** integration with a Rust wasmi
wrapper, written as production code built on the PoC's ideas — not a cleanup pass over the
PoC.
`Rust_wasm_PoC` (and `Rust_wasm_PoC_benchmark`) are read-only reference branches. Their
crates are named differently — `host_functions`, `host_functions_macros`, `wasm_vm` (with
`imports.rs` where we have `register.rs`, plus `ffi.rs`), `stdlib`, `example_contract`
read with `git show Rust_wasm_PoC:crates/<path>`.
**One PoC difference explains a lot of this crate.** The PoC's `host_abi!` inserted
`&self`, wrapped returns in `HostResult<_>`, and for a `Vec<u8>`/`[u8; N]` return
**appended `out: &mut [u8]` and changed the return to `HostResult<usize>`**. Here the
declaration *is* the signature — nothing is appended behind the reader's back. That is what
"no magic" means throughout these docs, and it is why byte outputs are written as explicit
out-params.
The C-API path is gone: `b7059deb9f` ("Remove wasmi dependency") deleted
`WasmVM.{h,cpp}`, `WasmiVM.h`, `HostFuncWrapper.cpp` and dropped the conan `wasmi`
package. Old semantics are recoverable with `git show b7059deb9f^:<path>` — do that rather
than guessing. See [history.md](history.md) for what is worth recovering.
## Where the code lives
- `crates/` — cargo workspace (edition 2024, resolver 3), built into the C++ build via
corrosion (`crates/CMakeLists.txt`, which registers the cxxbridge targets).
- `xrpl-host-functions/``no_std` ABI declaration: `host_functions! { … }` generates the
`HostFunctions` trait and the `HostFunctionSpec` enum (import name + gas per function).
Also `HostError`. **The single source of truth for the ABI** — see [abi.md](abi.md).
- `xrpl-host-functions-macros/` — the proc macro. An implementation detail of the crate
above, deliberately not re-exported: the ABI has one declaration site.
- `xrpl-wasm-vm/` — the wasmi wrapper. `vm.rs` (engine, store, `run`), `preflight.rs`
(`check` — compile, imports, entry point, declared memory, with no host, store or
gas), `abi.rs` (gas,
transfer budget, guest-memory marshaling), `region.rs` (the `(ptr, len)` type),
`register.rs` (one `func_wrap` per host function). See [engine.md](engine.md).
- `xrpl-wasm-vm-ffi/` — the cxx bridge, all three crossings. `RunStatus`/`RunResult` and
`run_escrow`, `CheckStatus`/`CheckResult` and `check_escrow`, `CxxHost`, the panic
guard. See [bridge.md](bridge.md).
- `xrpl-wasm-testkit/`**test-only**: `compile_wat`, so the C++ tests write their modules
as WebAssembly text. A crate of its own so `wat` cannot reach the shipped node; see
[testing.md](testing.md).
- `include/xrpl/tx/wasm/`, `src/libxrpl/tx/wasm/` — C++ side: `HostFunc.h` (the ~60-method
`HostFunctions` interface), `HostFuncImpl*.cpp` (its implementations, over
`ApplyContext&`), `WasmCommon.h` (`HostFunctionError`, `Wmem`, `WasmTER`, `FieldLocator`).
The bridge's C++ half is `HostContext.{h,cpp}` (the ABI-shaped view of `HostFunctions`)
and `WasmVM.{h,cpp}` (`runEscrowWasm`, `preflightEscrowWasm`, gas validation, both TER
maps).
- `src/tests/libxrpl/tx/wasm/` — the C++ tests, in the `xrpl_tests` gtest binary.
- `include/xrpl/tx/wasm/README.md` is **stale**: it uses the long name `get_ledger_sqn`
where the code registers `ldgr_index`, and references `detail/WasmVM.cpp`,
`detail/HostFuncWrapper.cpp`, `HostFuncWrapper.h` and `ParamsHelper.h`, none of which
exist.
## Current state (2026-08-04)
**The whole workspace is green**: `cargo test --workspace`, `clippy --workspace
--all-targets`, `fmt`, and `cargo doc -p xrpl-wasm-vm --no-deps`. **173 tests** — 33 macro,
12 facade, 1 doctest, **110 in `xrpl-wasm-vm`** (20 unit; 90 integration — 13 `budgets`,
12 `host_calls`, 23 `memory_policy`, 20 `preflight`, 22 `vm_limits`), 15 in
`xrpl-wasm-vm-ffi`, 2 in `xrpl-wasm-testkit`. On the C++ side, **40 tests over the whole
loop** in seven fixtures: `./xrpl_tests
--gtest_filter='WasmVMTest.*:*Call.*:PreflightTest.*'`.
**All three crossings are wired and a real contract runs through them**: C++ calls
`runEscrowWasm`, the engine services `ldgr_index` by calling back into
`xrpl::HostFunctions`, and the guest reads the answer out of its own memory;
`preflightEscrowWasm` screens a module through the third, with no host at all. Five host
functions are registered (`ldgr_index`, `home_le_field`, `sha512_half`, `trace`,
`trace_num`) out of the ~65 the full ABI will carry.
**Seventeen of the eighteen review findings are closed**, C12 the only one left
([history.md](history.md)).
## Next
1. **A caller.** `EscrowFinish.cpp` still has no wasm reference, so `runEscrowWasm` and
`preflightEscrowWasm` are reached only from `src/tests/libxrpl/tx/wasm/`. Wiring it up is
what makes `WasmHostFunctionsImpl` (over a real `ApplyContext`) the host in production
rather than in principle. **Blocked on the protocol fields**: `FinishFunction` and
`ComputationAllowance` exist nowhere in this fork or upstream, and adding
`FinishFunction` also has to answer the **contract code-size cap** — there is none, and
preflight's cost is linear in the blob (a 249 KB module of duplicate imports measures
1.5 ms, mostly wasmi's own parse).
2. **Import signatures at preflight.** `check` compares an import's namespace, name and
kind, not its type, so a mistyped import still parts a module from the engine at
instantiation. Deferred to when `host_functions!` generates the wasm-level lowering,
which the C header and the typed `link_*` shims in [abi.md](abi.md) both want anyway.
Note the deleted C++ `check` did not compare signatures either, so this is inherited
rather than new.
3. **A gas parity oracle.** `Wasm_test.cpp` asserts exact gas numbers (e.g. 29'502) and is
the best oracle we have, but it is commented out and its fixtures cannot run on this
engine — see the `env` finding in [testing.md](testing.md).
4. **The `Bytes`-by-value copy in `HostFunctions`** — [bridge.md](bridge.md). A
49-signature sweep, so it wants a caller to measure against first.
Also open: the two performance items and the ABI questions in
[open-questions.md](open-questions.md).

View File

@@ -1,91 +0,0 @@
[← Rust WASM VM docs](index.md)
# Open questions and deferred work
## Resolved, recorded so they are not reopened
**`gas = 0` — refused in C++ as `temBAD_AMOUNT`.** The old code already decided this and an
earlier revision of these docs had it garbled: `WasmiEngine::run` rejected `gas <= 0` for
*every* value including `-1`, and `-1 = unlimited` applied only to preflight's `check`.
`runEscrowWasm` restores exactly that, which is why the engine's own budget stays a `u64` with
no invalid value to represent.
**Import module name — `host_lib`**, matching the SDK and this fork's Rust fixtures.
`the_import_module_name_must_match` rejects `host`, `env` and the empty name.
**`-1` collides semantically** — host `Unimplemented` vs Rust `Internal`. Now a decision
rather than an accident: the bridge treats them as one condition, "the host could not serve
this call, and the contract has no business interpreting why". Both are host-fatal, so both
stop the run and report `tecINTERNAL`, which is also what a C++ exception caught in
`HostContext::guarded` becomes. The guest-side half of the collision (its own `InternalError`)
is untouched.
## ABI / guest-SDK interop
Found auditing the guest SDK (`~/Documents/rust/xrpl-wasm-stdlib`, checkout `435a091f`)
against this fork. All are decisions rather than code.
1. **Import name lineage.** The fixtures pin the SDK at `branch = renames` and use **short**
wire names (`parent_ldgr_hash`, `cache_le`, `tx_inner_arr_len`, `accountroot_id`,
`trustline_id`), matching `ldgr_index` / `home_le_field` / `sha512_half`. The standalone
SDK checkout is the **long**-name lineage (`get_parent_ledger_hash`, `cache_ledger_obj`,
`compute_sha512_half`). Which is authoritative is undecided.
2. **New error codes are UB in the guest.** The SDK decodes with a bare transmute and no
range check (`xrpl-common-stdlib/src/host/mod.rs:325`), valid only for `-1..=-20`. Making
host-fatal errors trap (A1) removed `OutOfGas` from the guest's view, and the
soft/fatal question is settled — **`OutOfTransferLimit` stays soft**. The *encoding*
question is not: `OutOfTransferLimit = -23` still reaches a transmuting guest, and
`NoRuntime = -21` would if anything returned it. Closing it needs either a range check in
the SDK or a remap into `-1..=-20`.
3. **The two error enums have already drifted.** C++ `HostFunctionError` spells -11
`OutOfTransferLimit`; the Rust ABI spells it `Decoding`. `WasmVMTest.SoftHostErrorCodes-
CrossUnchanged` now walks every soft code so a further renumbering is caught, but the
divergence itself is unresolved — one of the two lists is wrong.
4. **`float_to_mant_exp` byte count.** The host returns **12** (8 mantissa + 4 exponent);
the guest doc says 8, and the guest's `match_result_code_with_expected_bytes` **panics**
on a non-negative mismatch. Note this function writes *two* output regions, a shape no
current helper serves.
5. **Return conventions are not uniform** — six of them: bytes-written; value-in-return
(`*_arr_len`, `nft_flags`); boolean 0/1 (`amendment_enabled`, `check_sig`); 1-based handle
(`cache_le`, always ≥ 1); status-0 (`trace*`, `set_data`); tri-state (`float_cmp``0`
equal, `1` first >, `2` second >).
6. **The SDK's drift checker is silently broken.** `tools/compareHostFunctions.js`
regex-parses `WasmVM.cpp` and `HostFuncWrapper.h`, both deleted. A generated C header
would give it a stable target again ([abi.md](abi.md)).
## Performance, gated on a benchmark
**Two optimisations are deferred pending measurement, not rejected.** Neither is worth
guessing at, and one benchmarking pass with the google-benchmark harness on a
host-call-heavy module settles both.
1. **Lazy output buffer.** `VmState::out_buffer` is an inline `[u8; MAX_FIELD_BYTES]`,
zero-filled once per run whether or not the contract makes a call that uses it. The lazy
form is `Option<[u8; N]>` with `get_or_insert_with` (not `OnceCell` — that is for init
behind a shared borrow; `write_buffered` holds `&mut VmState`). The case against it today
is a magnitude argument a measurement could overturn: it defers one ~1 KiB fill per run,
invisible beside the `Module::new` that starts every run, and pays for it with a
discriminant test on **every host call** — the direction C11 moved cost away from. Also
note `Option<[u8; N]>` does not shrink `VmState` (no niche in a byte array), and
`Option<Box<[u8; N]>>` does but then charges a malloc to the 38 functions that use this
path in order to save the ones that do not.
2. **C12: cached `Linker`, cached module.** Two independent halves.
- *Module compile cache* is the bigger win — a whole wasm translation per run versus
building a five-entry linker — and it is **not** blocked by the lifetime problem, since
a `Module` is engine-scoped. But it carries a question that is not the engine's to
answer: **who owns a compiled contract's lifetime?** An unbounded static cache inside a
library on a consensus path brings an eviction policy nobody asked for; the alternative
is handing `run` a pre-compiled module, which changes the signature the bridge now
consumes. Either way the answer comes from the caller side.
- *Per-run `Linker`* is blocked: `VmState<'h>`'s lifetime forces `Linker<VmState<'h>>` to
be per-run, so hoisting it means making the store data `'static` — not holding
`&'h dyn HostFunctions`. This was expected to fall out of the bridge; **it did not.**
`CxxHost<'a>` borrows the C++ `HostContext` for one run and coerces to
`&'h dyn HostFunctions` unchanged, so hoisting the linker is still its own piece of
work with no other reason to do it.
So: **measure first**, and treat the linker and the lazy buffer as whatever the numbers say.
The module cache's open question is unchanged by the bridge — `run(wasm, gas, host, name)` is
now a signature the C++ side consumes, so handing it a pre-compiled module is a change to a
live interface rather than a hypothetical one, and **who owns a compiled contract's lifetime**
is still the caller's question to answer.

View File

@@ -1,147 +0,0 @@
[← Rust WASM VM docs](index.md)
# Testing: the loop, and how the suites are built
## The build / test loop
- Fast: `cd crates && cargo check --workspace --all-targets`, `cargo test --workspace`,
`cargo clippy --workspace --all-targets`.
- **`cargo doc -p xrpl-wasm-vm --no-deps` is part of the loop, not a nicety.** `lib.rs`
carries `deny(rustdoc::broken_intra_doc_links)`, and neither `cargo test` nor `clippy`
checks doc links. **Caveat: it does not cover private modules**, which are not documented
by default — a dead link inside `abi.rs` passes silently (this is how a `VmState::scratch`
link survived the `out_buffer` rename). Add `--document-private-items` to check those, and
grep after renaming a field. `lib.rs` also carries `forbid(unsafe_code)`,
`deny(unreachable_pub)` and `deny` on four clippy cast lints, so an unargued cast fails the
build rather than warning.
- Full C++↔Rust: normal CMake build, then
`./xrpl_tests --gtest_filter='WasmVMTest.*:*Call.*'`.
- Guest-linkability (needs `rustup target add wasm32-unknown-unknown`):
`cargo check -p xrpl-host-functions --target wasm32-unknown-unknown`. Only the ABI crate —
`xrpl-wasm-vm` is host-side and pulls in wasmi, and `crates/hello_world` cannot be checked
for that target at all because it depends on `cxx``link-cplusplus`, which wants a C++
toolchain for the target.
- VCS is **jj** (`jj st`, `jj log`), not raw git, for local work.
### Two build gotchas that cost an afternoon each
- **A stale build directory fails to link with `duplicate symbol '_rust_eh_personality'`.**
The conan `wasmi` package ships a Rust `std`, and so does our staticlib. `b7059deb9f`
dropped the conan requirement but left `find_package(wasmi REQUIRED)` and `wasmi::wasmi` in
the CMake, both now removed; a build folder generated before that still has
`build/generators/wasmi-*.cmake`, so re-run `conan install .. --output-folder . --build
missing --settings build_type=Debug` and delete them. Note this was *two differently
compiled* `std`s — two staticlibs from this workspace are fine, and `xrpld` already links
`rs_hello_world` alongside `xrpl_wasm_vm_ffi`.
- **`cargo test` on the bridge crate links only because nothing in the tests reaches a C++
shim.** The `extern "C++"` symbols exist only in the CMake build, and the test binary links
because `-dead_strip` drops what no test path reaches. Verified: forcing a reference
(`let f: fn(&ffi::HostContext) -> _ = ...`) fails with `Undefined symbols:
_rs$wasm_vm$cxxbridge1$…`. So keep those tests on pure logic — the status map, the panic
guard, the wire conversions — and put anything that needs a host in the gtest.
**`check_escrow` is the exception**: it takes no `HostContext`, so its tests call the real
bridge function, hand-writing the two modules they need as bytes (the eight-byte header is
a valid module) rather than reaching for an assembler this crate does not have.
## The Rust tests
- **They come in two kinds, and the split is forced.** A wasmi `Caller` exists only during a
host call, so everything in `abi.rs` that takes one cannot be reached from a unit test.
Unit tests in `src/` cover what needs no live instance (wire conversions, budget
arithmetic, the limits); guest-memory policy lives in `tests/`, running real modules
against a configurable fake host.
- Those integration tests write modules as **WAT text** and assemble it themselves — `wat` is
a `[dev-dependencies]` entry and `support::assemble` its only caller, so the assembler
never enters the library. `run` takes binaries; there is no `run_wat`.
- `tests/support/mod.rs` holds the fake host and the import declarations. `Answer` separates
*what the host writes* from *what length it reports*, which is what makes the over-cap and
buffer-fit rules testable without values that large existing.
## How the C++ tests are built
`src/tests/libxrpl/tx/wasm/`, in the `xrpl_tests` gtest binary. Four decisions, each of which
had an obvious cheaper alternative that was worse.
**Modules are WebAssembly text, assembled at run time.** Checked-in hex blobs do not scale
past one module — every host function needs its own, with its own import signature — and they
are unreviewable. So `compile_wat` comes over cxx from **`crates/xrpl-wasm-testkit`**, a crate
of its own that nothing in `libxrpl` or `xrpld` links.
That separation is the whole point and is worth not undoing. The engine pins
`wasmi = { default-features = false }` because wasmi's `wat` feature makes `Module::new`
accept text as readily as binary, which would make a transaction's validity a build flag
(finding A5 in [history.md](history.md)). Putting `compile_wat` on the production bridge would
link an assembler into the shipped node even though nothing called it; a cargo feature would
make the test and production binaries differ. A separate crate makes "no assembler in the
node" a property of the link graph. `WasmVMTest.TextFormatModuleIsRejected` then feeds the
engine the very text the rest of the suite assembles, so the guest-side half of A5 is pinned
too.
**The host is a `StrictMock`.** `MockHostFunctions` mocks only the methods the ABI declares;
the ~60 others keep `HostFunctions`' `Unimplemented` default, so a contract reaching past the
ABI fails the way production would. What this buys over a hand-written fake is assertions on
*what the host was asked* — that a guest `i32` became the right `SField`, that two borrowed
regions and a flag all arrived, that an `i64` survived as `INT64_MIN`.
Strict rather than nice, because these modules import exactly what they mean to exercise: a
host call no test asked for means the engine reached for something on its own, which is worth
a failure rather than a warning. The cost is one line in the fixture —
`EXPECT_CALL(host_, checkSelf()).WillRepeatedly(Return(true))`, since `runEscrowWasm` asks
every run whether the host is clean. Verified by mutation: giving `escrow_finish` an
unstubbed host call fails the test under Strict and passes silently under `NiceMock`.
*One trap worth knowing even so*: gmock's default action for `std::expected<T, E>` is a
**successful** `T{}`, so a method with an `EXPECT_CALL` but no action would answer `0` and a
test could pass on an answer nobody chose. The mock's constructor therefore `ON_CALL`s every
method to the base class's `std::unexpected(Unimplemented)`.
**Two levels of fixture.** `WasmTest` holds the mock, a capturing journal sink and `run(wat,
gas, entryPoint)`. `HostCallTest` adds a `wat()` the derived fixture supplies and
`hostAnswer()`, so a per-function test says only what the host was asked and what came back.
Then one fixture per host function — `LedgerSqnCall`, `CurrentLedgerObjFieldCall`,
`Sha512HalfCall`, `TraceCall`, `TraceNumCall` — because the module *is* that function's shared
setup. `WasmVMTest` keeps what belongs to the engine rather than to any function.
**`PreflightTest` deliberately derives from `testing::Test`, not from `WasmTest`**, and holds
no mock: `preflightEscrowWasm` takes no host, and a fixture that supplied one would hide the
signature that is the point. That is why `assembleWat` is a free function in `WasmFixture.h`
rather than a `WasmTest` member. `PreflightTest.ScreeningAgreesWithARun` is the one test
there that does build a host — it puts the same modules through `runEscrowWasm` so the two
entry points do not have to be trusted to agree.
**The journal is captured, not sent to a null sink.**
`WasmVMTest.ThrowingHostFunctionBecomesInternal` asserts the exception text *and* that the log
names `getLedgerSqn`; without that, an exception silently swallowed with no log would pass, and
`HostContext::guarded`'s `source_location` would be untested.
Two properties are pinned from the guest's side rather than asserted about internals:
`LedgerSqnCall.BufferTooSmallIsRefusedWholeNotTruncated` has the contract report whether
*anything* reached its memory, which is "a refused value reaches it in no part" as a contract
can observe it; and `WasmVMTest.SoftHostErrorCodesCrossUnchanged` walks all 18 soft
`HostFunctionError` codes, because the C++ and Rust error enums are two hand-maintained lists
of the same numbers that **have already drifted once** — -11 is `OutOfTransferLimit` in C++ and
`Decoding` in the Rust ABI; see "the two error enums have already drifted" in
[open-questions.md](open-questions.md).
*Mutation-checked*: making a too-large value write a truncated prefix, and pointing the
sha512 input matcher at bytes the guest does not send, each fail exactly one test and nothing
else.
**Naming.** Subject-first, no leading article — `ContractReturnValueReachesCaller`, not
`AContractsReturnValueReachesTheCaller`. That is the house style in `src/tests/libxrpl`
(`BuilderThrowsOnWrongEntryType`, `OptionalFieldsReturnNullopt`).
## The old C++ suites, and why they are not the parity oracle yet
`src/test/app/Wasm_test.cpp` and `HostFuncImpl_test.cpp` are **entirely inside `/* */`** and
compile to nothing, as is `src/libxrpl/tx/wasm/WasmiVM.cpp`.
`Wasm_test.cpp` asserts exact gas numbers (e.g. 29'502), which makes it the best gas-parity
oracle available — but **its fixtures cannot run on this engine.** They import from module
`env`, not `host_lib` (`kLedgerSqnWasmHex` decodes to
`... 03 656e76 0a 6c6467725f696e646578 ...`), and their `target_features` include `sign-ext`,
`multivalue` and `reference-types`, which this engine disables. The deleted C++ engine ignored
the import module name entirely — `wasm_importtype_module` is commented out at its
`WasmiVM.cpp:428`. Reviving it as an oracle means recompiling those fixtures with
`-Wl,--import-module=host_lib` and the engine's feature set. The gtest carries its own
WAT-derived modules for that reason.

View File

@@ -0,0 +1,126 @@
#pragma once
#include <nudb/detail/stream.hpp>
#include <cstddef>
#include <cstdint>
#include <type_traits>
namespace xrpl::node_store {
// This is a variant of the base128 varint format from
// google protocol buffers:
// https://developers.google.com/protocol-buffers/docs/encoding#varints
// field tag
struct Varint;
// Metafunction to return largest
// possible size of T represented as varint.
// T must be unsigned
template <class T, bool = std::is_unsigned_v<T>>
struct VarintTraits;
template <class T>
struct VarintTraits<T, true>
{
explicit VarintTraits() = default;
static constexpr std::size_t kMax = ((8 * sizeof(T)) + 6) / 7;
};
// Returns: Number of bytes consumed or 0 on error,
// if the buffer was too small or t overflowed.
//
template <class = void>
std::size_t
readVarint(void const* buf, std::size_t buflen, std::size_t& t)
{
if (buflen == 0)
return 0;
t = 0;
auto const* p = reinterpret_cast<std::uint8_t const*>(buf);
std::size_t n = 0;
while (p[n] & 0x80)
{
if (++n >= buflen)
return 0;
}
if (++n > buflen)
return 0;
// Special case for 0
if (n == 1 && *p == 0)
{
t = 0;
return 1;
}
auto const used = n;
while (n > 0)
{
--n;
auto const d = p[n];
auto const t0 = t;
t *= 127;
t += d & 0x7f;
if (t <= t0)
return 0; // overflow
}
return used;
}
template <class T>
std::size_t
sizeVarint(T v)
requires(std::is_unsigned_v<T>)
{
std::size_t n = 0;
do
{
v /= 127;
++n;
} while (v != 0);
return n;
}
template <class = void>
std::size_t
writeVarint(void* p0, std::size_t v)
{
// NOLINTNEXTLINE(misc-const-correctness)
auto* p = reinterpret_cast<std::uint8_t*>(p0);
do
{
std::uint8_t d = v % 127;
v /= 127;
if (v != 0)
d |= 0x80;
*p++ = d;
} while (v != 0);
return p - reinterpret_cast<std::uint8_t*>(p0);
}
// input stream
template <class T>
void
read(nudb::detail::istream& is, std::size_t& u)
requires(std::is_same_v<T, Varint>)
{
auto p0 = is(1);
auto p1 = p0;
while (*p1++ & 0x80)
is(1);
readVarint(p0, p1 - p0, u);
}
// output stream
template <class T>
void
write(nudb::detail::ostream& os, std::size_t t)
requires(std::is_same_v<T, Varint>)
{
writeVarint(os.data(sizeVarint(t)), t);
}
} // namespace xrpl::node_store

View File

@@ -2,7 +2,6 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
@@ -12,9 +11,6 @@
#include <cstdint>
#include <expected>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
@@ -73,7 +69,6 @@ floatPowerImpl(Slice const& x, int32_t n, int32_t mode);
class HostFunctions
{
protected:
RTOptRef rt_;
beast::Journal j_;
public:
@@ -81,26 +76,6 @@ public:
{
}
void
setRT(WasmRuntimeWrapper& rt)
{
rt_ = rt;
}
void
resetRT()
{
rt_ = std::nullopt;
}
[[nodiscard]] WasmRuntimeWrapper&
getRT() const
{
if (!rt_)
Throw<std::logic_error>("Wasm runtime not set");
return rt_->get();
}
[[nodiscard]] beast::Journal
getJournal() const
{
@@ -518,6 +493,4 @@ public:
// LCOV_EXCL_STOP
};
using HFRef = std::reference_wrapper<HostFunctions>;
} // namespace xrpl

View File

@@ -1,189 +1,42 @@
# WASM Module for Programmable Escrows
This module provides WebAssembly (WASM) execution capabilities for programmable
escrows on the XRP Ledger. When an escrow is finished, the WASM code runs to
determine whether the escrow conditions are met, enabling custom programmable
logic for escrow release conditions.
For the full specification, see
WebAssembly execution for programmable escrows. When an escrow is finished, its contract
runs to decide whether the release conditions are met. Specification:
[XLS-0102: WASM VM](https://xls.xrpl.org/xls/XLS-0102-wasm-vm.html).
## Architecture
The engine itself is Rust (`crates/xrpl-wasm-vm`, over wasmi), reached through a cxx
bridge. The design docs live in [`docs/claude/wasm-vm/`](../../../../docs/claude/wasm-vm/index.md)
— read [`abi.md`](../../../../docs/claude/wasm-vm/abi.md) before adding a host function and
[`bridge.md`](../../../../docs/claude/wasm-vm/bridge.md) before changing anything that
crosses between the two languages.
The module follows a layered architecture:
## What is in this directory
```
┌─────────────────────────────────────────────────────────────┐
WasmEngine (WasmVM.h) │
│ runEscrowWasm(), preflightEscrowWasm() │
Host function registration │
├─────────────────────────────────────────────────────────────┤
WasmiEngine (WasmiVM.h) │
│ Low-level wasmi interpreter integration │
├─────────────────────────────────────────────────────────────┤
HostFuncWrapper │ HostFuncImpl │
│ C-style WASM bridges │ C++ implementations │
├─────────────────────────────────────────────────────────────┤
│ HostFunc (Interface) │
│ Abstract base class for host functions │
└─────────────────────────────────────────────────────────────┘
```
- **`WasmVM.h`** — the entry points xrpld calls: `runEscrowWasm` (execute a contract,
returning a result and its gas cost, or a `WasmTER`) and `preflightEscrowWasm` (screen a
module with no host and no execution). Both own their TER maps.
- **`HostFunc.h`** — the `HostFunctions` interface: one virtual per host function, each
defaulting to `Unimplemented`, returning `std::expected<T, HostFunctionError>`.
- **`HostFuncImpl.h`** — `WasmHostFunctionsImpl`, the implementation over an
`ApplyContext&`. Bodies are split across `HostFuncImpl*.cpp` by category.
- **`HostContext.h`** — the bridge's C++ half: an ABI-shaped, `noexcept` view of
`HostFunctions` that the engine calls back into. Nothing may unwind into Rust, so every
method routes through one `guarded()`.
- **`WasmCommon.h`** — the shared vocabulary: `HostFunctionError` (the codes a contract
sees), `Bytes`, `FieldLocator`, `WasmTER`, and `adjustWasmEndianess`, which is where the
boundary's byte order is decided.
### Key Components
## Host functions
- **`WasmVM.h` / `detail/WasmVM.cpp`** - High-level facade providing:
- `WasmEngine` singleton that wraps the underlying WASM interpreter
- `runEscrowWasm()` - Execute WASM code for escrow finish
- `preflightEscrowWasm()` - Validate WASM code during preflight
- `createWasmImport()` - Register all host functions
Grouped by what they reach: ledger information; transaction and ledger-object field access;
keylet construction; cryptography; float arithmetic; NFT queries; tracing.
- **`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
- Memory management and gas metering
- Function invocation and result handling
The wire names and per-call gas costs are declared in `crates/xrpl-host-functions`
one `host_functions!` block that generates the ABI trait and the spec table. That
declaration is the single source of truth; `HostFunc.h` is the C++ side of it.
- **`HostFunc.h`** - Abstract `HostFunctions` base class defining the interface
for all callable host functions. Each method returns
`std::expected<T, HostFunctionError>`.
## Entry point
- **`HostFuncImpl.h` / `detail/HostFuncImpl*.cpp`** - Concrete
`WasmHostFunctionsImpl` class that implements host functions with access to
`ApplyContext` for ledger state queries. Implementation split across files:
- `HostFuncImpl.cpp` - Core utilities (updateData, checkSignature, etc.)
- `HostFuncImplFloat.cpp` - Float/number arithmetic operations
- `HostFuncImplGetter.cpp` - Field access (transaction, ledger objects)
- `HostFuncImplKeylet.cpp` - Keylet construction functions
- `HostFuncImplLedgerHeader.cpp` - Ledger header info access
- `HostFuncImplNFT.cpp` - NFT-related queries
- `HostFuncImplTrace.cpp` - Debugging/tracing functions
- **`HostFuncWrapper.h` / `detail/HostFuncWrapper.cpp`** - C-style wrapper
functions that bridge WASM calls to C++ `HostFunctions` methods. Each host
function has:
- A `_proto` type alias defining the function signature
- A `_wrap` function that extracts parameters and calls the implementation
- **`ParamsHelper.h`** - Utilities for WASM parameter handling:
- `WASM_IMPORT_FUNC` / `WASM_IMPORT_FUNC2` macros for registration
- `wasmParams()` helper for building parameter vectors
- Type conversion between WASM and C++ types
## Host Functions
Host functions allow WASM code to interact with the XRP Ledger. They are
organized into categories:
- **Ledger Information** - Access ledger sequence, timestamps, hashes, fees
- **Transaction & Ledger Object Access** - Read fields from the transaction
and ledger objects (including the current escrow object)
- **Keylet Construction** - Build keylets to look up various ledger object types
- **Cryptography** - Signature verification and hashing
- **Float Arithmetic** - Mathematical operations for amount calculations
- **NFT Operations** - Query NFT properties
- **Tracing/Debugging** - Log messages for debugging
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.
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`:
```cpp
WASM_IMPORT_FUNC2(i, getLedgerSqn, "get_ledger_sqn", hfs, 60);
// ^^ gas cost
```
WASM execution is metered, and if the gas limit is exceeded, execution fails.
## Entry Point
The WASM module must export a function with the name defined by
`escrowFunctionName` (currently `"escrow_finish"`). This function:
- Takes no parameters (or parameters passed via host function calls)
- Returns an `int32_t`:
- `1` (or positive): Escrow conditions are met, allow finish
- `0` (or negative): Escrow conditions are not met, reject finish
## Adding a New Host Function
To add a new host function, follow these steps:
### 1. Add to HostFunc.h (Base Class)
Add a virtual method declaration with a default implementation that returns an
error:
```cpp
virtual std::expected<ReturnType, HostFunctionError>
myNewFunction(ParamType1 param1, ParamType2 param2)
{
return std::unexpected(HostFunctionError::INTERNAL);
}
```
### 2. Add to HostFuncImpl.h (Declaration)
Add the method override declaration in `WasmHostFunctionsImpl`:
```cpp
std::expected<ReturnType, HostFunctionError>
myNewFunction(ParamType1 param1, ParamType2 param2) override;
```
### 3. Implement in detail/HostFuncImpl\*.cpp
Add the implementation in the appropriate file:
```cpp
std::expected<ReturnType, HostFunctionError>
WasmHostFunctionsImpl::myNewFunction(ParamType1 param1, ParamType2 param2)
{
// Implementation using ctx (ApplyContext) for ledger access
return result;
}
```
### 4. Add Wrapper to HostFuncWrapper.h
Add the prototype and wrapper declaration:
```cpp
using myNewFunction_proto = int32_t(uint8_t const*, int32_t, ...);
wasm_trap_t*
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
```
### 5. Implement Wrapper in detail/HostFuncWrapper.cpp
Implement the C-style wrapper that bridges WASM to C++:
```cpp
wasm_trap_t*
myNewFunction_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
// Extract parameters from params
// Call hfs->myNewFunction(...)
// Set results and return
}
```
### 6. Register in WasmVM.cpp
Add the function registration in `setCommonHostFunctions()` or
`createWasmImport()`:
```cpp
WASM_IMPORT_FUNC2(i, myNewFunction, "my_new_function", hfs, 100);
// ^^ WASM name ^^ gas cost
```
> [!IMPORTANT]
> New host functions MUST be amendment-gated in `WasmVM.cpp`.
> Wrap the registration in an amendment check to ensure the function is only
> available after the corresponding amendment is enabled on the network.
A module must export `escrow_finish` (`escrowFunctionName`) taking no parameters and
returning `int32_t`: positive means the conditions are met, zero or negative rejects the
finish. Everything the contract needs it asks for through a host call.

View File

@@ -7,10 +7,8 @@
#include <bit>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <optional>
#include <stdexcept>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
@@ -21,18 +19,6 @@ using Bytes = std::vector<std::uint8_t>;
using Hash = xrpl::uint256;
using FloatPair = std::pair<int64_t, int32_t>;
// Error signals that cross the wasm boundary as trap messages (the C API has no
// trap code). WasmiEngine::call maps them to TER: hfErrInternal -> tecINTERNAL,
// hfErrOutOfGas / wasmi's OutOfFuel -> tecOUT_OF_GAS, anything else ->
// tecFAILED_PROCESSING.
//
// Matched as substrings, not by equality: the C API returns the Rust Debug form
// of the error, e.g. `Error { kind: Message("HfInternal") }` or
// `Error { kind: TrapCode(OutOfFuel) }`.
std::string_view inline constexpr hfErrInternal = "HfInternal";
std::string_view inline constexpr hfErrOutOfGas = "HfOutOfGas";
std::string_view inline constexpr wasmiTrapOutOfFuel = "OutOfFuel";
enum class HostFunctionError : int32_t {
Unimplemented = -1,
FieldNotFound = -2,
@@ -56,19 +42,6 @@ enum class HostFunctionError : int32_t {
FloatComputationError = -20,
};
enum class WasmTypes { WtI32, WtI64 };
struct Wmem
{
std::uint8_t* p = nullptr;
std::size_t s = 0;
Wmem() = default;
Wmem(void* ptr, std::size_t size) : p(reinterpret_cast<std::uint8_t*>(ptr)), s(size)
{
}
};
template <typename T>
struct WasmResult
{
@@ -136,71 +109,6 @@ public:
}
};
class WasmRuntimeWrapper
{
public:
virtual ~WasmRuntimeWrapper() = default;
virtual Wmem
getMem() = 0;
virtual std::int64_t
getGas() = 0;
virtual std::int64_t
setGas(std::int64_t gas) = 0;
virtual std::int64_t
getTransferLimit() = 0;
virtual std::int64_t
setTransferLimit(std::int64_t transferLimit) = 0;
};
using RTOptRef = std::optional<std::reference_wrapper<WasmRuntimeWrapper>>;
struct WasmParam
{
// We are not supporting float/double
WasmTypes type = WasmTypes::WtI32;
union
{
std::int32_t i32;
std::int64_t i64 = 0;
} of;
};
template <class... Types>
inline void
wasmParamsHlp(std::vector<WasmParam>& v, std::int32_t p, Types&&... args)
{
v.push_back({.type = WasmTypes::WtI32, .of = {.i32 = p}});
wasmParamsHlp(v, std::forward<Types>(args)...);
}
template <class... Types>
inline void
wasmParamsHlp(std::vector<WasmParam>& v, std::int64_t p, Types&&... args)
{
v.push_back({.type = WasmTypes::WtI64, .of = {.i64 = p}});
wasmParamsHlp(v, std::forward<Types>(args)...);
}
inline void
wasmParamsHlp(std::vector<WasmParam>& v)
{
}
template <class... Types>
inline std::vector<WasmParam>
wasmParams(Types&&... args)
{
std::vector<WasmParam> v;
v.reserve(sizeof...(args));
wasmParamsHlp(v, std::forward<Types>(args)...);
return v;
}
template <typename T, size_t Size = sizeof(T)>
constexpr T
adjustWasmEndianessHlp(T x)

View File

@@ -124,9 +124,10 @@ getAnyFieldData(FieldValue const& variantObj)
if (uint256 const* const* u = std::get_if<uint256 const*>(&variantObj))
return Bytes((*u)->begin(), (*u)->end());
// Unreachable: the variant only holds the two alternatives above. If not,
// it's an xrpld bug -> tecINTERNAL (thrown, caught by HostFuncMain_wrap).
Throw<std::runtime_error>(std::string(hfErrInternal)); // LCOV_EXCL_LINE
// Unreachable: the variant only holds the two alternatives above. If not, it is an
// xrpld bug, and `HostContext::guarded` turns the throw into the engine's fatal
// `Internal` -> tecINTERNAL.
Throw<std::runtime_error>("field value variant holds neither alternative"); // LCOV_EXCL_LINE
}
static inline bool

View File

@@ -1,960 +0,0 @@
/*
#include <xrpl/tx/wasm/WasmiVM.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <xrpl/tx/wasm/WasmImportsHelper.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <wasmi/config.h>
#include <wasmi/error.h>
#include <wasm.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <exception>
#include <expected>
#include <limits>
#include <memory>
#include <mutex>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
#endif
// #define SHOW_CALL_TIME 1
namespace xrpl {
wasm_trap_t*
HostFuncMain_wrap(void* env, wasm_val_vec_t const* params, wasm_val_vec_t* results);
namespace {
void
printWasmError(std::string_view msg, wasm_trap_t* trap, beast::Journal jlog)
{
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
auto j = jlog.warn();
if (jlog.active(beast::Severity::Warning))
#endif
{
wasm_byte_vec_t errorMessage WASM_EMPTY_VEC;
if (trap != nullptr)
wasm_trap_message(trap, &errorMessage);
if (errorMessage.size != 0u)
{
j << "WASMI Error: " << msg << ", "
<< std::string_view(errorMessage.data, errorMessage.size - 1);
}
else
{
j << "WASMI Error: " << msg;
}
if (errorMessage.size != 0u)
wasm_byte_vec_delete(&errorMessage);
}
if (trap != nullptr)
wasm_trap_delete(trap);
#ifdef DEBUG_OUTPUT
j << std::endl;
#endif
}
// LCOV_EXCL_STOP
// Extract a trap's message into a std::string (the only signal the C API gives
// for classification; see the trap-signal constants in WasmCommon.h). Does not
// take ownership of `trap`.
std::string
trapMessage(wasm_trap_t* trap)
{
if (trap == nullptr)
return {}; // LCOV_EXCL_LINE
wasm_byte_vec_t msg WASM_EMPTY_VEC;
wasm_trap_message(trap, &msg);
std::string out;
if (msg.size != 0u)
{
// wasm_trap_message NUL-terminates, so drop the trailing NUL.
out.assign(msg.data, msg.size - 1);
wasm_byte_vec_delete(&msg);
}
return out;
}
} // namespace
class WasmiRuntimeWrapper : public WasmRuntimeWrapper
{
InstanceWrapper& iw_;
public:
WasmiRuntimeWrapper(InstanceWrapper& iw) : iw_(iw)
{
}
Wmem
getMem() override
{
return iw_.getMem();
}
std::int64_t
getGas() override
{
return iw_.getGas();
}
std::int64_t
setGas(std::int64_t gas) override
{
return iw_.setGas(gas);
}
std::int64_t
getTransferLimit() override
{
return iw_.getTransferLimit();
}
std::int64_t
setTransferLimit(std::int64_t x) override
{
return iw_.setTransferLimit(x);
}
};
InstancePtr
InstanceWrapper::init(
StorePtr& s,
ModulePtr& m,
WasmExternVec& expt,
WasmExternVec const& imports,
beast::Journal j)
{
wasm_trap_t* trap = nullptr;
InstancePtr mi = InstancePtr(
wasm_instance_new(s.get(), m.get(), imports.get(), &trap), &wasm_instance_delete);
if (!mi || (trap != nullptr))
{
printWasmError("can't create instance", trap, j);
Throw<std::runtime_error>("can't create instance");
}
wasm_instance_exports(mi.get(), expt.get());
return mi;
}
InstanceWrapper&
InstanceWrapper::operator=(InstanceWrapper&& o)
{
if (this == &o)
return *this; // LCOV_EXCL_LINE
store_ = o.store_;
o.store_ = nullptr;
exports_ = std::move(o.exports_);
memIdx_ = o.memIdx_;
o.memIdx_ = -1;
instance_ = std::move(o.instance_);
j_ = o.j_;
return *this;
}
FuncInfo
InstanceWrapper::getFunc(std::string_view funcName, WasmExporttypeVec const& exportTypes) const
{
wasm_func_t const* f = nullptr;
wasm_functype_t const* ft = nullptr;
if (!instance_)
Throw<std::runtime_error>("no instance"); // LCOV_EXCL_LINE
if (exportTypes.empty())
Throw<std::runtime_error>("no export"); // LCOV_EXCL_LINE
if (exportTypes.size() != exports_.size())
Throw<std::runtime_error>("invalid export"); // LCOV_EXCL_LINE
for (unsigned i = 0; i < exportTypes.size(); ++i)
{
auto const* expType(exportTypes[i]);
wasm_name_t const* name = wasm_exporttype_name(expType);
wasm_externtype_t const* exnType = wasm_exporttype_type(expType);
if (wasm_externtype_kind(exnType) == WASM_EXTERN_FUNC)
{
if (funcName != std::string_view(name->data, name->size))
continue;
auto const* exn(exports_[i]);
if (wasm_extern_kind(exn) != WASM_EXTERN_FUNC)
Throw<std::runtime_error>("invalid export"); // LCOV_EXCL_LINE
ft = wasm_externtype_as_functype_const(exnType);
f = wasm_extern_as_func_const(exn);
break;
}
}
if ((f == nullptr) || (ft == nullptr))
Throw<std::runtime_error>("can't find function <" + std::string(funcName) + ">");
return {f, ft};
}
Wmem
InstanceWrapper::getMem() const
{
if (memIdx_ >= 0)
{
auto* e(exports_[memIdx_]);
wasm_memory_t* mem = wasm_extern_as_memory(e);
return Wmem(wasm_memory_data(mem), wasm_memory_data_size(mem));
}
wasm_memory_t* mem = nullptr;
for (int i = 0; i < exports_.size(); ++i)
{
auto* e(exports_[i]);
if (wasm_extern_kind(e) == WASM_EXTERN_MEMORY)
{
memIdx_ = i;
mem = wasm_extern_as_memory(e);
break;
}
}
if (mem == nullptr)
return {}; // LCOV_EXCL_LINE
return Wmem(wasm_memory_data(mem), wasm_memory_data_size(mem));
}
std::int64_t
InstanceWrapper::getGas() const
{
if (store_ == nullptr)
return -1; // LCOV_EXCL_LINE
std::uint64_t gas = 0;
wasm_store_get_fuel(store_, &gas);
return static_cast<std::int64_t>(gas);
}
std::int64_t
InstanceWrapper::setGas(std::int64_t gas) const
{
if (store_ == nullptr)
return -1; // LCOV_EXCL_LINE
if (gas < 0)
gas = std::numeric_limits<decltype(gas)>::max();
wasmi_error_t* err = wasm_store_set_fuel(store_, static_cast<std::uint64_t>(gas));
if (err != nullptr)
{
// LCOV_EXCL_START
printWasmError("Can't set instance gas", nullptr, j_);
wasmi_error_delete(err);
return -1;
// LCOV_EXCL_STOP
}
return gas;
}
std::int64_t
InstanceWrapper::getTransferLimit() const
{
if (store_ == nullptr)
return -1; // LCOV_EXCL_LINE
return transferLimit_;
}
std::int64_t
InstanceWrapper::setTransferLimit(std::int64_t x)
{
if (store_ == nullptr)
return -1; // LCOV_EXCL_LINE
if (x < 0)
{
transferLimit_ = std::numeric_limits<decltype(transferLimit_)>::max();
}
else
{
transferLimit_ = x;
}
return transferLimit_;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
ModulePtr
ModuleWrapper::init(StorePtr& s, Bytes const& wasmBin, beast::Journal j)
{
wasm_byte_vec_t const code{
.size = wasmBin.size(),
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
.data = const_cast<char*>(reinterpret_cast<char const*>(wasmBin.data()))};
ModulePtr m = ModulePtr(wasm_module_new(s.get(), &code), &wasm_module_delete);
if (!m)
throw std::runtime_error("can't create module");
return m;
}
ModuleWrapper::ModuleWrapper(
StorePtr& s,
Bytes const& wasmBin,
bool instantiate,
ImportVec const& imports,
beast::Journal j)
: module_(init(s, wasmBin, j)), j_(j)
{
wasm_module_exports(module_.get(), exportTypes_.get());
auto wimports = buildImports(s, imports);
if (instantiate)
{
addInstance(s, wimports);
}
}
// LCOV_EXCL_START
ModuleWrapper&
ModuleWrapper::operator=(ModuleWrapper&& o)
{
if (this == &o)
return *this;
module_ = std::move(o.module_);
instanceWrap_ = std::move(o.instanceWrap_);
exportTypes_ = std::move(o.exportTypes_);
j_ = o.j_;
return *this;
}
// LCOV_EXCL_STOP
static WasmValtypeVec
makeImpParams(WasmImportFunc const& imp)
{
auto const paramSize = imp.params.size();
if (paramSize == 0u)
return {};
WasmValtypeVec v(paramSize);
for (unsigned i = 0; i < paramSize; ++i)
{
auto const vt = imp.params[i];
switch (vt)
{
case WasmTypes::WtI32:
v[i] = wasm_valtype_new_i32();
break;
case WasmTypes::WtI64:
v[i] = wasm_valtype_new_i64();
break;
// LCOV_EXCL_START
default:
throw std::runtime_error("invalid import type");
// LCOV_EXCL_STOP
}
}
return v;
}
static WasmValtypeVec
makeImpReturn(WasmImportFunc const& imp)
{
if (!imp.result)
return {}; // LCOV_EXCL_LINE
WasmValtypeVec v(1);
switch (*imp.result)
{
case WasmTypes::WtI32:
v[0] = wasm_valtype_new_i32();
break;
// LCOV_EXCL_START
case WasmTypes::WtI64:
v[0] = wasm_valtype_new_i64();
break;
default:
throw std::runtime_error("invalid return type");
// LCOV_EXCL_STOP
}
return v;
}
WasmExternVec
ModuleWrapper::buildImports(StorePtr& s, ImportVec const& imports) const
{
WasmImporttypeVec importTypes;
wasm_module_imports(module_.get(), importTypes.get());
if (importTypes.empty())
return {};
if (imports.empty())
Throw<std::runtime_error>("Empty imports");
WasmExternVec wimports(importTypes.size());
unsigned impCnt = 0;
for (unsigned i = 0; i < importTypes.size(); ++i)
{
wasm_importtype_t const* importType = importTypes[i];
// wasm_name_t const* mn = wasm_importtype_module(importtype);
// auto modName = std::string_view(mn->data, mn->num_elems);
wasm_name_t const* fn = wasm_importtype_name(importType);
auto fieldName = std::string_view(fn->data, fn->size);
wasm_externkind_t const itype = wasm_externtype_kind(wasm_importtype_type(importType));
if (itype != WASM_EXTERN_FUNC)
{
Throw<std::runtime_error>(
"Invalid import type " + std::to_string(itype)); // LCOV_EXCL_LINE
}
// for multi-module support
// if ((W_ENV != modName) && (W_HOST_LIB != modName))
// continue;
auto const it = imports.find(fieldName);
if (it == imports.end())
{
printWasmError("Import not found: " + std::string(fieldName), nullptr, j_);
continue; // print all missed import
}
WasmUserData const& obj = it->second;
WasmImportFunc const& imp = obj.second;
WasmValtypeVec params(makeImpParams(imp));
WasmValtypeVec results(makeImpReturn(imp));
std::unique_ptr<wasm_functype_t, decltype(&wasm_functype_delete)> const ftype(
wasm_functype_new(params.get(), results.get()), &wasm_functype_delete);
params.release();
results.release();
wasm_func_t* func =
wasm_func_new_with_env(s.get(), ftype.get(), HostFuncMain_wrap, (void*)&obj, nullptr);
if (func == nullptr)
{
Throw<std::runtime_error>(
"can't create import function " + std::string(imp.name)); // LCOV_EXCL_LINE
}
wimports[i] = wasm_func_as_extern(func);
++impCnt;
}
if (impCnt != importTypes.size())
{
printWasmError(
std::string("Imports not finished: ") + std::to_string(impCnt) + "/" +
std::to_string(importTypes.size()),
nullptr,
j_);
Throw<std::runtime_error>("Missing imports");
}
return wimports;
}
wasm_functype_t const*
ModuleWrapper::getFuncType(std::string_view funcName) const
{
for (size_t i = 0; i < exportTypes_.size(); i++)
{
auto const* expType(exportTypes_[i]);
wasm_name_t const* name = wasm_exporttype_name(expType);
wasm_externtype_t const* exnType = wasm_exporttype_type(expType);
if (wasm_externtype_kind(exnType) == WASM_EXTERN_FUNC &&
funcName == std::string_view(name->data, name->size))
{
return wasm_externtype_as_functype_const(exnType);
}
}
throw std::runtime_error("can't find function <" + std::string(funcName) + ">");
}
// 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;
// }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// void
// WasmiEngine::clearModules()
// {
// modules.clear();
// store.reset(); // to free the memory before creating new store
// store = {wasm_store_new(engine.get()), &wasm_store_delete};
// }
std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>
WasmiEngine::init()
{
wasm_config_t* config = wasm_config_new();
if (config == nullptr)
{
return std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>{
nullptr, &wasm_engine_delete}; // LCOV_EXCL_LINE
}
wasmi_config_consume_fuel_set(config, true);
wasmi_config_ignore_custom_sections_set(config, true);
wasmi_config_wasm_mutable_globals_set(config, false);
wasmi_config_wasm_multi_value_set(config, false);
wasmi_config_wasm_sign_extension_set(config, false);
wasmi_config_wasm_saturating_float_to_int_set(config, false);
wasmi_config_wasm_bulk_memory_set(config, false);
wasmi_config_wasm_reference_types_set(config, false);
wasmi_config_wasm_tail_call_set(config, false);
wasmi_config_wasm_extended_const_set(config, false);
wasmi_config_floats_set(config, false);
wasmi_config_wasm_multi_memory_set(config, false);
wasmi_config_wasm_custom_page_sizes_set(config, false);
wasmi_config_wasm_memory64_set(config, false);
wasmi_config_wasm_wide_arithmetic_set(config, false);
return std::unique_ptr<wasm_engine_t, decltype(&wasm_engine_delete)>(
wasm_engine_new_with_config(config), &wasm_engine_delete);
}
int
WasmiEngine::addModule(
Bytes const& wasmCode,
bool instantiate,
ImportVec const& imports,
int64_t gas)
{
moduleWrap_.reset();
store_.reset(); // to free the memory before creating new store
store_ = {wasm_store_new_with_memory_max_pages(engine_.get(), maxPages), &wasm_store_delete};
if (gas < 0)
gas = std::numeric_limits<decltype(gas)>::max();
wasmi_error_t* err = wasm_store_set_fuel(store_.get(), static_cast<std::uint64_t>(gas));
if (err != nullptr)
{
// LCOV_EXCL_START
printWasmError("Error setting gas", nullptr, j_);
wasmi_error_delete(err);
throw std::runtime_error("can't set gas");
// LCOV_EXCL_STOP
}
moduleWrap_ = std::make_unique<ModuleWrapper>(store_, wasmCode, instantiate, imports, j_);
if (!moduleWrap_)
throw std::runtime_error("can't create module wrapper"); // LCOV_EXCL_LINE
return moduleWrap_ ? 0 : -1;
}
// int
// WasmiEngine::addInstance()
// {
// return module->addInstance(store.get());
// }
std::vector<wasm_val_t>
WasmiEngine::convertParams(std::vector<WasmParam> const& params)
{
std::vector<wasm_val_t> v;
v.reserve(params.size());
for (auto const& p : params)
{
switch (p.type)
{
case WasmTypes::WtI32:
v.push_back(WASM_I32_VAL(p.of.i32));
break;
// LCOV_EXCL_START
case WasmTypes::WtI64:
v.push_back(WASM_I64_VAL(p.of.i64));
break;
default:
throw std::runtime_error(
"unknown parameter type: " + std::to_string(static_cast<int>(p.type)));
break;
// LCOV_EXCL_STOP
}
}
return v;
}
int
WasmiEngine::compareParamTypes(wasm_valtype_vec_t const* ftp, std::vector<wasm_val_t> const& p)
{
if (ftp->size != p.size())
return std::min(ftp->size, p.size());
for (unsigned i = 0; i < ftp->size; ++i)
{
auto const t1 = wasm_valtype_kind(ftp->data[i]);
auto const t2 = p[i].kind;
if (t1 != t2)
return i;
}
return -1;
}
// LCOV_EXCL_START
void
WasmiEngine::addParam(std::vector<wasm_val_t>& in, int32_t p)
{
in.emplace_back();
auto& el(in.back());
memset(&el, 0, sizeof(el));
el = WASM_I32_VAL(p); // WASM_I32;
}
// LCOV_EXCL_STOP
void
WasmiEngine::addParam(std::vector<wasm_val_t>& in, int64_t p)
{
in.emplace_back();
auto& el(in.back());
el = WASM_I64_VAL(p);
}
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(std::string_view func, Types&&... args)
{
// Lookup our export function
auto f = getFunc(func);
return call<NR>(f, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(FuncInfo const& f, Types&&... args)
{
std::vector<wasm_val_t> in;
return call<NR>(f, in, std::forward<Types>(args)...);
}
#ifdef SHOW_CALL_TIME
static inline uint64_t
usecs()
{
uint64_t x = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return x;
}
#endif
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in)
{
WasmiResult ret(NR);
wasm_val_vec_t const inv = in.empty() ? wasm_val_vec_t WASM_EMPTY_VEC
: wasm_val_vec_t{.size = in.size(), .data = in.data()};
#ifdef SHOW_CALL_TIME
auto const start = usecs();
#endif
wasm_trap_t* trap = wasm_func_call(f.first, &inv, ret.r.get());
#ifdef SHOW_CALL_TIME
auto const finish = usecs();
auto const delta_ms = (finish - start) / 1000;
std::cout << "wasm_func_call: " << delta_ms << "ms" << std::endl;
#endif
if (trap)
{
// Classify the trap into a TER by matching tokens as substrings of the
// message (see the trap-signal constants in WasmCommon.h for why).
std::string const msg = trapMessage(trap);
auto const has = [&msg](std::string_view token) { return msg.contains(token); };
if (has(hfErrInternal))
{
ret.ter = tecINTERNAL;
}
else if (has(hfErrOutOfGas) || has(wasmiTrapOutOfFuel))
{
ret.ter = tecOUT_OF_GAS;
}
else
{
ret.ter = tecFAILED_PROCESSING;
}
printWasmError("failure to call func", trap, j_);
}
return ret;
}
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int32_t p, Types&&... args)
{
addParam(in, p);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in, std::int64_t p, Types&&... args)
{
addParam(in, p);
return call<NR>(f, in, std::forward<Types>(args)...);
}
template <int NR, class... Types>
WasmiResult
WasmiEngine::call(FuncInfo const& f, std::vector<wasm_val_t>& in, Bytes const& p, Types&&... args)
{
return call<NR>(f, in, p.data(), p.size(), std::forward<Types>(args)...);
}
static inline void
checkImports(ImportVec const& imports, HostFunctions* hfs)
{
for (auto const& obj : imports)
{
if (hfs != &obj.second.first.get())
Throw<std::runtime_error>("Imports hf unsync");
}
}
std::expected<WasmResult<int32_t>, WasmTER>
WasmiEngine::run(
Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gas,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j)
{
if (gas <= 0)
return std::unexpected(WasmTER{.ter = temBAD_AMOUNT, .cost = std::nullopt});
try
{
checkImports(imports, &hfs);
return runHlp(wasmCode, hfs, gas, funcName, params, imports, j);
}
catch (std::exception const& e)
{
printWasmError(std::string("exception: ") + e.what(), nullptr, j);
}
// LCOV_EXCL_START
catch (...)
{
printWasmError(std::string("exception: unknown"), nullptr, j);
}
// LCOV_EXCL_STOP
// An exception escaping the engine is an xrpld-side fault -> tecINTERNAL,
// no gas. Genuine wasm faults don't throw; they surface as traps in runHlp.
return std::unexpected(WasmTER{.ter = tecINTERNAL, .cost = std::nullopt});
}
std::expected<WasmResult<int32_t>, WasmTER>
WasmiEngine::runHlp(
Bytes const& wasmCode,
HostFunctions& hfs,
int64_t gas,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j)
{
// currently only 1 module support, possible parallel UT run
std::scoped_lock const lg(m_);
j_ = j;
if (wasmCode.empty())
throw std::runtime_error("empty module");
if (!hfs.checkSelf())
throw std::runtime_error("hfs isn't clean");
// Create and instantiate the module.
[[maybe_unused]] int const m = addModule(wasmCode, true, imports, gas);
if (!moduleWrap_ || !moduleWrap_->getInstance())
throw std::runtime_error("no instance"); // LCOV_EXCL_LINE
auto clearRT = [](HostFunctions* p) { p->resetRT(); };
std::unique_ptr<HostFunctions, decltype(clearRT)> const clearGuard(&hfs, clearRT);
WasmiRuntimeWrapper iw(getRT());
hfs.setRT(iw);
// Call main
auto const f = getFunc(!funcName.empty() ? funcName : "_start");
auto const* ftp = wasm_functype_params(f.second);
// not const because passed directly to VM function (which accept non
// const)
auto p = convertParams(params);
if (int const comp = compareParamTypes(ftp, p); comp >= 0)
throw std::runtime_error("invalid parameter type #" + std::to_string(comp));
auto const res = call<1>(f, p);
if (gas == -1)
gas = std::numeric_limits<decltype(gas)>::max();
if (res.ter.has_value())
{
// call() already classified the trap (see WasmiEngine::call).
// tecINTERNAL is an xrpld-side bug: report no gas.
if (*res.ter == tecINTERNAL)
return std::unexpected(WasmTER{.ter = tecINTERNAL, .cost = std::nullopt});
// Out-of-gas / wasm faults report gas (caller writes it to metadata).
// Force fuel to 0 on out-of-gas so cost is the full limit (wasmi leaves
// nonzero leftover fuel on its own out-of-fuel trap).
if (*res.ter == tecOUT_OF_GAS)
iw.setGas(0);
return std::unexpected(WasmTER{.ter = *res.ter, .cost = gas - moduleWrap_->getGas()});
}
if (res.r.empty())
{
Throw<std::runtime_error>(
"<" + std::string(funcName) + "> return nothing"); // LCOV_EXCL_LINE
}
if (res.r[0].kind != WASM_I32)
{
Throw<std::runtime_error>(
"<" + std::string(funcName) +
"> return type mismatch, ret: " + std::to_string(static_cast<int>(res.r[0].kind)));
}
WasmResult<int32_t> const ret{.result = res.r[0].of.i32, .cost = gas - moduleWrap_->getGas()};
// #ifdef DEBUG_OUTPUT
// auto& j = std::cerr;
// #else
// auto j = j_.debug();
// #endif
// j << "WASMI Res: " << ret.result << " cost: " << ret.cost << std::endl;
return ret;
}
NotTEC
WasmiEngine::check(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j)
{
try
{
checkImports(imports, &hfs);
return checkHlp(wasmCode, hfs, funcName, params, imports, j);
}
catch (std::exception const& e)
{
printWasmError(std::string("exception: ") + e.what(), nullptr, j);
}
// LCOV_EXCL_START
catch (...)
{
printWasmError(std::string("exception: unknown"), nullptr, j);
}
// LCOV_EXCL_STOP
return temBAD_WASM;
}
NotTEC
WasmiEngine::checkHlp(
Bytes const& wasmCode,
HostFunctions& hfs,
std::string_view funcName,
std::vector<WasmParam> const& params,
ImportVec const& imports,
beast::Journal j)
{
// currently only 1 module support, possible parallel UT run
std::scoped_lock const lg(m_);
j_ = j;
// Create and instantiate the module.
if (wasmCode.empty())
throw std::runtime_error("empty module");
int const m = addModule(wasmCode, false, imports, -1);
if ((m < 0) || !moduleWrap_)
throw std::runtime_error("no module"); // LCOV_EXCL_LINE
// Looking for a func and compare parameter types
auto const f = moduleWrap_->getFuncType(!funcName.empty() ? funcName : "_start");
auto const* ftp = wasm_functype_params(f);
auto const p = convertParams(params);
if (int const comp = compareParamTypes(ftp, p); comp >= 0)
throw std::runtime_error("invalid parameter type #" + std::to_string(comp));
return tesSUCCESS;
}
wasm_trap_t*
WasmiEngine::newTrap(std::string const& txt)
{
static char empty[1] = {0};
wasm_message_t msg = {.size = 1, .data = empty};
if (!txt.empty())
wasm_name_new(&msg, txt.size() + 1, txt.c_str()); // include 0
wasm_trap_t* trap = wasm_trap_new(store_.get(), &msg); // NOLINT
if (!txt.empty())
wasm_byte_vec_delete(&msg);
return trap;
}
} // namespace xrpl
*/

View File

@@ -1,538 +0,0 @@
#pragma once
#include <test/jtx/Env.h>
#include <test/unit_test/SuiteJournal.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <boost/algorithm/hex.hpp>
#include <cstdint>
#include <expected>
#include <iterator>
#include <string>
#include <string_view>
namespace xrpl::test {
class TestLedgerDataProvider : public HostFunctions
{
jtx::Env& env_;
public:
TestLedgerDataProvider(jtx::Env& env) : HostFunctions(env.journal), env_(env)
{
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getLedgerSqn() const override
{
return env_.current()->seq();
}
};
class TestHostFunctions : public HostFunctions
{
protected:
test::jtx::Env& env_;
AccountID accountID_;
Bytes data_;
public:
TestHostFunctions(test::jtx::Env& env) : HostFunctions(env.journal), env_(env)
{
accountID_ = env.master.id();
std::string t = "10000";
data_ = Bytes{t.begin(), t.end()};
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getLedgerSqn() const override
{
return 12345;
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getParentLedgerTime() const override
{
return 67890;
}
[[nodiscard]] std::expected<Hash, HostFunctionError>
getParentLedgerHash() const override
{
return env_.current()->header().parentHash;
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getBaseFee() const override
{
return 10;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
isAmendmentEnabled(uint256 const& amendmentId) const override
{
return 1;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
isAmendmentEnabled(std::string_view const& amendmentName) const override
{
return 1;
}
std::expected<int32_t, HostFunctionError>
cacheLedgerObj(uint256 const& objId, int32_t cacheIdx) override
{
return 1;
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getTxField(SField const& fname) const override
{
if (fname == sfAccount)
return Bytes(accountID_.begin(), accountID_.end());
if (fname == sfFee)
{
int64_t x = 235;
auto const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
if (fname == sfSequence)
{
auto const x = getLedgerSqn();
if (!x)
return std::unexpected(x.error());
std::uint32_t const data = x.value();
auto const* b = reinterpret_cast<uint8_t const*>(&data);
auto const* e = reinterpret_cast<uint8_t const*>(&data + 1);
return Bytes{b, e};
}
return Bytes();
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getCurrentLedgerObjField(SField const& fname) const override
{
auto const& sn = fname.getName();
if (sn == "Destination" || sn == "Account")
return Bytes(accountID_.begin(), accountID_.end());
if (sn == "Data")
return data_;
if (sn == "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::unexpected(HostFunctionError::Unimplemented);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getLedgerObjField(int32_t, SField const& fname) const override
{
if (fname == sfBalance)
{
int64_t x = 10'000;
auto const* p = reinterpret_cast<uint8_t const*>(&x);
return Bytes{p, p + sizeof(x)};
}
if (fname == sfAccount)
return Bytes(accountID_.begin(), accountID_.end());
return data_;
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getTxNestedField(FieldLocator const& locator) const override
{
if (locator.size() == 1)
{
int32_t const* l = locator.data();
int32_t const sfield = l[0];
if (sfield == sfAccount.getCode())
return Bytes(accountID_.begin(), accountID_.end());
}
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, 0xbf, 0x49, 0xd2,
0x45, 0x9f, 0xa4, 0xa0, 0x34, 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92,
0xfc, 0xee, 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getCurrentLedgerObjNestedField(FieldLocator const& locator) const override
{
if (locator.size() == 1)
{
int32_t const* l = locator.data();
int32_t const sfield = l[0];
if (sfield == sfAccount.getCode())
return Bytes(accountID_.begin(), accountID_.end());
}
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, 0xbf, 0x49, 0xd2,
0x45, 0x9f, 0xa4, 0xa0, 0x34, 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92,
0xfc, 0xee, 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const override
{
if (locator.size() == 1)
{
int32_t const* l = locator.data();
int32_t const sfield = l[0];
if (sfield == sfAccount.getCode())
return Bytes(accountID_.begin(), accountID_.end());
}
uint8_t const a[] = {0x2b, 0x6a, 0x23, 0x2a, 0xa4, 0xc4, 0xbe, 0x41, 0xbf, 0x49, 0xd2,
0x45, 0x9f, 0xa4, 0xa0, 0x34, 0x7e, 0x1b, 0x54, 0x3a, 0x4c, 0x92,
0xfc, 0xee, 0x08, 0x21, 0xc0, 0x20, 0x1e, 0x2e, 0x9a, 0x00};
return Bytes(&a[0], &a[sizeof(a)]);
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getTxArrayLen(SField const& fname) const override
{
return 32;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getCurrentLedgerObjArrayLen(SField const& fname) const override
{
return 32;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const override
{
return 32;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getTxNestedArrayLen(FieldLocator const& locator) const override
{
return 32;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const override
{
return 32;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator) const override
{
return 32;
}
std::expected<int32_t, HostFunctionError>
updateData(Slice const& data) override
{
return data.size();
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
checkSignature(Slice const& message, Slice const& signature, Slice const& pubkey) const override
{
return 1;
}
[[nodiscard]] std::expected<Hash, HostFunctionError>
computeSha512HalfHash(Slice const& data) const override
{
return env_.current()->header().parentHash;
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
accountKeylet(AccountID const& account) const override
{
if (!account)
return std::unexpected(HostFunctionError::InvalidAccount);
auto const keylet = keylet::account(account);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
ammKeylet(Asset const& issue1, Asset const& issue2) const override
{
if (issue1 == issue2)
return std::unexpected(HostFunctionError::InvalidParams);
if (issue1.holds<MPTIssue>() || issue2.holds<MPTIssue>())
return std::unexpected(HostFunctionError::InvalidParams);
auto const keylet = keylet::amm(issue1, issue2);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
checkKeylet(AccountID const& account, std::uint32_t seq) const override
{
if (!account)
return std::unexpected(HostFunctionError::InvalidAccount);
auto const keylet = keylet::check(account, seq);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
credentialKeylet(AccountID const& subject, AccountID const& issuer, Slice const& credentialType)
const override
{
if (!subject || !issuer || credentialType.empty() ||
credentialType.size() > kMaxCredentialTypeLength)
return std::unexpected(HostFunctionError::InvalidAccount);
auto const keylet = keylet::credential(subject, issuer, credentialType);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
escrowKeylet(AccountID const& account, std::uint32_t seq) const override
{
if (!account)
return std::unexpected(HostFunctionError::InvalidAccount);
auto const keylet = keylet::escrow(account, seq);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
oracleKeylet(AccountID const& account, std::uint32_t documentId) const override
{
if (!account)
return std::unexpected(HostFunctionError::InvalidAccount);
auto const keylet = keylet::oracle(account, documentId);
return Bytes{keylet.key.begin(), keylet.key.end()};
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getNFT(AccountID const& account, uint256 const& nftId) const override
{
if (!account || !nftId)
return std::unexpected(HostFunctionError::InvalidParams);
std::string s = "https://ripple.com";
return Bytes(s.begin(), s.end());
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getNFTIssuer(uint256 const& nftId) const override
{
return Bytes(accountID_.begin(), accountID_.end());
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getNFTTaxon(uint256 const& nftId) const override
{
return 4;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getNFTFlags(uint256 const& nftId) const override
{
return 8;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
getNFTTransferFee(uint256 const& nftId) const override
{
return 10;
}
[[nodiscard]] std::expected<std::uint32_t, HostFunctionError>
getNFTSequence(uint256 const& nftId) const override
{
return 4;
}
template <typename F>
void
log(std::string_view const& msg, F&& dataFn) const
{
#ifdef DEBUG_OUTPUT
auto& j = std::cerr;
#else
if (!getJournal().active(beast::Severity::Trace))
return;
auto j = getJournal().trace();
#endif
j << "WasmTrace: " << msg << " " << dataFn();
#ifdef DEBUG_OUTPUT
j << std::endl;
#endif
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
trace(std::string_view const& msg, Slice const& data, bool asHex) const override
{
if (!asHex)
{
log(msg, [&data] {
return std::string_view(reinterpret_cast<char const*>(data.data()), data.size());
});
}
else
{
log(msg, [&data] {
std::string hex;
hex.reserve(data.size() * 2);
boost::algorithm::hex(data.begin(), data.end(), std::back_inserter(hex));
return hex;
});
}
return 0;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
traceNum(std::string_view const& msg, int64_t data) const override
{
log(msg, [data] { return data; });
return 0;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
traceAccount(std::string_view const& msg, AccountID const& account) const override
{
log(msg, [&account] { return toBase58(account); });
return 0;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
traceFloat(std::string_view const& msg, Slice const& data) const override
{
log(msg, [&data] { return wasm_float::floatToString(data); });
return 0;
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
traceAmount(std::string_view const& msg, STAmount const& amount) const override
{
log(msg, [&amount] { return amount.getFullText(); });
return 0;
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatFromInt(int64_t x, int32_t mode) const override
{
return wasm_float::floatFromIntImpl(x, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatFromUint(uint64_t x, int32_t mode) const override
{
return wasm_float::floatFromUintImpl(x, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatFromSTAmount(STAmount const& x, int32_t mode) const override
{
return wasm_float::floatFromSTAmountImpl(x, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatFromSTNumber(STNumber const& x, int32_t mode) const override
{
return wasm_float::floatFromSTNumberImpl(x, mode);
}
[[nodiscard]] std::expected<int64_t, HostFunctionError>
floatToInt(Slice const& x, int32_t mode) const override
{
return wasm_float::floatToIntImpl(x, mode);
}
[[nodiscard]] std::expected<FloatPair, HostFunctionError>
floatToMantExp(Slice const& x) const override
{
return wasm_float::floatToMantExpImpl(x);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatFromMantExp(int64_t mantissa, int32_t exponent, int32_t mode) const override
{
return wasm_float::floatFromMantExpImpl(mantissa, exponent, mode);
}
[[nodiscard]] std::expected<int32_t, HostFunctionError>
floatCompare(Slice const& x, Slice const& y) const override
{
return wasm_float::floatCompareImpl(x, y);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatAdd(Slice const& x, Slice const& y, int32_t mode) const override
{
return wasm_float::floatAddImpl(x, y, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatSubtract(Slice const& x, Slice const& y, int32_t mode) const override
{
return wasm_float::floatSubtractImpl(x, y, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatMultiply(Slice const& x, Slice const& y, int32_t mode) const override
{
return wasm_float::floatMultiplyImpl(x, y, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatDivide(Slice const& x, Slice const& y, int32_t mode) const override
{
return wasm_float::floatDivideImpl(x, y, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatRoot(Slice const& x, int32_t n, int32_t mode) const override
{
return wasm_float::floatRootImpl(x, n, mode);
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
floatPower(Slice const& x, int32_t n, int32_t mode) const override
{
return wasm_float::floatPowerImpl(x, n, mode);
}
};
class TestHostFunctionsSink : public TestHostFunctions
{
test::StreamSink sink_;
public:
explicit TestHostFunctionsSink(test::jtx::Env& env)
: TestHostFunctions(env), sink_(beast::Severity::Debug)
{
j_ = beast::Journal(sink_);
}
test::StreamSink&
getSink()
{
return sink_;
}
};
} // namespace xrpl::test

View File

@@ -1,471 +0,0 @@
/*
#include <expected>
#ifdef _DEBUG
// #define DEBUG_OUTPUT 1
#endif
#include <test/app/TestHostFunctions.h>
#include <test/app/wasm_fixtures/fixtures.h>
#include <test/jtx/Env.h>
#include <xrpl/beast/unit_test/suite.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/HostFuncWrapper.h> // IWYU pragma: keep
#include <xrpl/tx/wasm/WasmCommon.h>
#include <xrpl/tx/wasm/WasmImportsHelper.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <boost/algorithm/hex.hpp>
#include <wasm.h>
#include <cstdint>
#include <limits>
#include <source_location>
#include <string>
#include <vector>
namespace xrpl::test {
bool
testGetDataIncrement();
using Add_proto = int32_t(int32_t, int32_t);
static wasm_trap_t*
add(HostFunctions&, wasm_val_vec_t const* params, wasm_val_vec_t* results)
{
int32_t const val1 = params->data[0].of.i32;
int32_t const 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;
}
std::vector<uint8_t>
hexToBytes(std::string const& hex)
{
auto const ws = boost::algorithm::unhex(hex);
return Bytes(ws.begin(), ws.end());
}
struct Wasm_test : public beast::unit_test::Suite
{
void
checkResult(
std::expected<WasmResult<int32_t>, WasmTER> re,
int32_t expectedResult,
int64_t expectedCost,
std::source_location const location = std::source_location::current())
{
auto const lineStr = " (" + std::to_string(location.line()) + ")";
if (BEAST_EXPECTS(re.has_value(), transToken(re.error().ter) + lineStr))
{
BEAST_EXPECTS(re->result == expectedResult, std::to_string(re->result) + lineStr);
BEAST_EXPECTS(re->cost == expectedCost, std::to_string(re->cost) + lineStr);
}
}
void
testGetDataHelperFunctions()
{
testcase("getData helper functions");
BEAST_EXPECT(testGetDataIncrement());
}
void
testWasmLib()
{
testcase("wasm lib test");
// clang-format off
// The WASM module buffer. //
Bytes 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();
HostFunctions hfs;
ImportVec imports;
WasmImpFunc<Add_proto>(imports, "func-add", add, hfs);
auto re = vm.run(wasm, hfs, 10'000'000, "addTwo", wasmParams(1234, 5678), imports);
// if (res) printf("invokeAdd get the result: %d\n", res.value());
checkResult(re, 6'912, 59);
}
void
testBadWasm()
{
testcase("bad wasm test");
using namespace test::jtx;
Env const env{*this};
HostFunctions hfs(env.journal);
{
auto wasm = hexToBytes("00000000");
std::string const funcName("mock_escrow");
auto re = runEscrowWasm(wasm, hfs, 15, funcName, {});
BEAST_EXPECT(!re);
}
{
auto wasm = hexToBytes("00112233445566778899AA");
std::string const funcName("mock_escrow");
auto const re = preflightEscrowWasm(wasm, hfs, funcName);
BEAST_EXPECT(!isTesSuccess(re));
}
{
// FinishFunction wrong function name
// pub fn bad() -> bool {
// unsafe { host_lib::getLedgerSqn() >= 5 }
// }
auto const badWasm = hexToBytes(
"0061736d010000000105016000017f02190108686f73745f6c69620c6765"
"744c656467657253716e00000302010005030100100611027f00418080c0"
"000b7f00418080c0000b072b04066d656d6f727902000362616400010a5f"
"5f646174615f656e6403000b5f5f686561705f6261736503010a09010700"
"100041044a0b004d0970726f64756365727302086c616e67756167650104"
"52757374000c70726f6365737365642d6279010572757374631d312e3835"
"2e31202834656231363132353020323032352d30332d31352900490f7461"
"726765745f6665617475726573042b0f6d757461626c652d676c6f62616c"
"732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a"
"6d756c746976616c7565");
auto const re = preflightEscrowWasm(badWasm, hfs, escrowFunctionName);
BEAST_EXPECT(!isTesSuccess(re));
}
}
void
testWasmLedgerSqn()
{
testcase("Wasm get ledger sequence");
auto ledgerSqnWasm = hexToBytes(kLedgerSqnWasmHex);
using namespace test::jtx;
Env env{*this};
TestLedgerDataProvider hfs(env);
ImportVec imports;
WASM_IMPORT_FUNC2(imports, getLedgerSqn, "ldgr_index", hfs, 33);
auto& engine = WasmEngine::instance();
auto re =
engine.run(ledgerSqnWasm, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal);
checkResult(re, 0, 440);
env.close();
env.close();
// empty module, throwing exception
re = engine.run({}, hfs, 1'000'000, escrowFunctionName, {}, imports, env.journal);
BEAST_EXPECT(!re);
env.close();
}
void
testHFCost()
{
testcase("wasm test host functions cost");
using namespace test::jtx;
Env env(*this);
{
auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex);
auto& engine = WasmEngine::instance();
TestHostFunctions hfs(env);
auto imp = createWasmImport(hfs);
for (auto& i : imp)
i.second.second.gas = 0;
auto re = engine.run(
allHostFuncWasm, hfs, 1'000'000, escrowFunctionName, {}, imp, env.journal);
checkResult(re, 1, 27'617);
env.close();
}
env.close();
env.close();
env.close();
env.close();
env.close();
{
auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex);
auto& engine = WasmEngine::instance();
TestHostFunctions hfs(env);
auto const imp = createWasmImport(hfs);
auto re = engine.run(
allHostFuncWasm, hfs, 1'000'000, escrowFunctionName, {}, imp, env.journal);
checkResult(re, 1, 70'877);
env.close();
}
// not enough gas
{
auto const allHostFuncWasm = hexToBytes(kAllHostFunctionsWasmHex);
auto& engine = WasmEngine::instance();
TestHostFunctions hfs(env);
auto const imp = createWasmImport(hfs);
auto re =
engine.run(allHostFuncWasm, hfs, 200, escrowFunctionName, {}, imp, env.journal);
if (BEAST_EXPECT(!re))
{
// Running out of gas now terminates with tecOUT_OF_GAS (was
// previously collapsed into tecFAILED_PROCESSING).
BEAST_EXPECTS(
re.error().ter == tecOUT_OF_GAS, std::to_string(TERtoInt(re.error().ter)));
}
env.close();
}
}
void
testEscrowWasmDN()
{
testcase("escrow wasm devnet test");
auto const allHFWasm = hexToBytes(kAllHostFunctionsWasmHex);
using namespace test::jtx;
Env env{*this};
{
TestHostFunctions hfs(env);
auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {});
checkResult(re, 1, 70'877);
}
{
// Invalid gas limit (0) should be rejected (boundary condition)
TestHostFunctions hfs(env);
auto re = runEscrowWasm(allHFWasm, hfs, -1, escrowFunctionName, {});
BEAST_EXPECT(!re.has_value());
BEAST_EXPECT(re.error().ter == temBAD_AMOUNT);
}
{
// Invalid gas limit (-1) should be rejected
TestHostFunctions hfs(env);
auto re = runEscrowWasm(allHFWasm, hfs, 0, escrowFunctionName, {});
BEAST_EXPECT(!re.has_value());
BEAST_EXPECT(re.error().ter == temBAD_AMOUNT);
}
{
// max<int64_t>() gas
TestHostFunctions hfs(env);
auto re = runEscrowWasm(
allHFWasm, hfs, std::numeric_limits<int64_t>::max(), escrowFunctionName, {});
checkResult(re, 1, 70'877);
}
{ // fail because trying to access nonexistent field
struct FieldNotFoundHostFunctions : public TestHostFunctions
{
explicit FieldNotFoundHostFunctions(Env& env) : TestHostFunctions(env)
{
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getTxField(SField const& fname) const override
{
return std::unexpected(HostFunctionError::FieldNotFound);
}
};
FieldNotFoundHostFunctions hfs(env);
auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {});
checkResult(re, -201, 29'502);
}
{ // fail because trying to allocate more than MAX_PAGES memory
struct OversizedFieldHostFunctions : public TestHostFunctions
{
explicit OversizedFieldHostFunctions(Env& env) : TestHostFunctions(env)
{
}
[[nodiscard]] std::expected<Bytes, HostFunctionError>
getTxField(SField const& fname) const override
{
return Bytes((128 + 1) * 64 * 1024, 1);
}
};
OversizedFieldHostFunctions hfs(env);
auto re = runEscrowWasm(allHFWasm, hfs, 100'000, escrowFunctionName, {});
checkResult(re, -201, 29'502);
}
}
void
testCodecovWasm()
{
testcase("Codecov wasm test");
using namespace test::jtx;
Env env{*this};
auto const codecovWasm = hexToBytes(kCodecovTestsWasmHex);
TestHostFunctions hfs(env);
auto const allowance = 204'624;
auto re = runEscrowWasm(codecovWasm, hfs, allowance, escrowFunctionName, {});
checkResult(re, 1, allowance);
}
void
testBadAlign()
{
testcase("Wasm Bad Align");
// bad_align.c
auto const badAlignWasm = hexToBytes(kBadAlignWasmHex);
using namespace test::jtx;
Env env{*this};
TestHostFunctions hfs(env);
auto imports = createWasmImport(hfs);
{ // Calls float_from_uint with bad alignment.
// Can be checked through codecov
auto& engine = WasmEngine::instance();
auto re = engine.run(badAlignWasm, hfs, 1'000'000, "test", {}, imports, env.journal);
if (BEAST_EXPECTS(re, transToken(re.error().ter)))
{
BEAST_EXPECTS(re->result == 0x47308594, std::to_string(re->result));
}
}
env.close();
}
void
testSwapBytes()
{
testcase("Wasm swap bytes");
uint64_t const swapDataU64 = 0x123456789abcdeffull;
uint64_t const reverseSwapDataU64 = 0xffdebc9a78563412ull;
int64_t const swapDataI64 = 0x123456789abcdeffll;
int64_t const reverseSwapDataI64 = 0xffdebc9a78563412ll;
uint32_t const swapDataU32 = 0x12789aff;
uint32_t const reverseSwapDataU32 = 0xff9a7812;
int32_t const swapDataI32 = 0x12789aff;
int32_t const reverseSwapDataI32 = 0xff9a7812;
uint16_t const swapDataU16 = 0x12ff;
uint16_t const reverseSwapDataU16 = 0xff12;
int16_t const swapDataI16 = 0x12ff;
int16_t const reverseSwapDataI16 = 0xff12;
uint64_t b1 = swapDataU64;
int64_t b2 = swapDataI64;
b1 = adjustWasmEndianessHlp(b1);
b2 = adjustWasmEndianessHlp(b2);
BEAST_EXPECT(b1 == reverseSwapDataU64);
BEAST_EXPECT(b2 == reverseSwapDataI64);
b1 = adjustWasmEndianessHlp(b1);
b2 = adjustWasmEndianessHlp(b2);
BEAST_EXPECT(b1 == swapDataU64);
BEAST_EXPECT(b2 == swapDataI64);
uint32_t b3 = swapDataU32;
int32_t b4 = swapDataI32;
b3 = adjustWasmEndianessHlp(b3);
b4 = adjustWasmEndianessHlp(b4);
BEAST_EXPECT(b3 == reverseSwapDataU32);
BEAST_EXPECT(b4 == reverseSwapDataI32);
b3 = adjustWasmEndianessHlp(b3);
b4 = adjustWasmEndianessHlp(b4);
BEAST_EXPECT(b3 == swapDataU32);
BEAST_EXPECT(b4 == swapDataI32);
uint16_t b5 = swapDataU16;
int16_t b6 = swapDataI16;
b5 = adjustWasmEndianessHlp(b5);
b6 = adjustWasmEndianessHlp(b6);
BEAST_EXPECT(b5 == reverseSwapDataU16);
BEAST_EXPECT(b6 == reverseSwapDataI16);
b5 = adjustWasmEndianessHlp(b5);
b6 = adjustWasmEndianessHlp(b6);
BEAST_EXPECT(b5 == swapDataU16);
BEAST_EXPECT(b6 == swapDataI16);
}
void
run() override
{
using namespace test::jtx;
testGetDataHelperFunctions();
testWasmLib();
testBadWasm();
testWasmLedgerSqn();
testHFCost();
testEscrowWasmDN();
testCodecovWasm();
testBadAlign();
testSwapBytes();
}
};
BEAST_DEFINE_TESTSUITE(Wasm, app, xrpl);
} // namespace xrpl::test
*/

View File

@@ -1,3 +0,0 @@
**/target
**/debug
*.wasm

View File

@@ -1,171 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "all_host_functions"
version = "0.1.0"
dependencies = [
"xrpl-wasm-stdlib",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "xrpl-macros"
version = "0.1.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#9822d645870908a79d87a57b0244caa6359cb9cf"
dependencies = [
"bs58",
"quote",
"sha2",
"syn",
]
[[package]]
name = "xrpl-wasm-stdlib"
version = "0.8.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#9822d645870908a79d87a57b0244caa6359cb9cf"
dependencies = [
"xrpl-macros",
]

View File

@@ -1,21 +0,0 @@
[package]
name = "all_host_functions"
version = "0.1.0"
edition = "2024"
# This empty workspace definition keeps this project independent of the parent workspace
[workspace]
[lib]
crate-type = ["cdylib"]
[dependencies]
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "renames" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
opt-level = "z"
lto = true

View File

@@ -1,799 +0,0 @@
#![cfg_attr(target_arch = "wasm32", no_std)]
#[cfg(not(target_arch = "wasm32"))]
extern crate std;
//
// Host Functions Test
// Tests 26 host functions (across 7 categories)
//
// With craft you can run this test with:
// craft test --project host_functions_test --test-case host_functions_test
//
// Amount Format Update:
// - XRP amounts now return as 8-byte serialized rippled objects
// - IOU and MPT amounts return in variable-length serialized format
// - Format details: https://xrpl.org/docs/references/protocol/binary-format#amount-fields
//
// Error Code Ranges:
// -100 to -199: Ledger Header Functions (3 functions)
// -200 to -299: Transaction Data Functions (5 functions)
// -300 to -399: Current Ledger Object Functions (4 functions)
// -400 to -499: Any Ledger Object Functions (5 functions)
// -500 to -599: Keylet Generation Functions (4 functions)
// -600 to -699: Utility Functions (4 functions)
// -700 to -799: Data Update Functions (1 function)
//
use xrpl_std::core::current_tx::escrow_finish::EscrowFinish;
use xrpl_std::core::current_tx::traits::TransactionCommonFields;
use xrpl_std::host;
use xrpl_std::host::trace::{trace, trace_account_buf, trace_data, trace_num, DataRepr};
use xrpl_std::sfield;
#[unsafe(no_mangle)]
pub extern "C" fn escrow_finish() -> i32 {
let _ = trace("=== HOST FUNCTIONS TEST ===");
let _ = trace("Testing 26 host functions");
// Category 1: Ledger Header Data Functions (3 functions)
// Error range: -100 to -199
match test_ledger_header_functions() {
0 => (),
err => return err,
}
// Category 2: Transaction Data Functions (5 functions)
// Error range: -200 to -299
match test_transaction_data_functions() {
0 => (),
err => return err,
}
// Category 3: Current Ledger Object Functions (4 functions)
// Error range: -300 to -399
match test_current_ledger_object_functions() {
0 => (),
err => return err,
}
// Category 4: Any Ledger Object Functions (5 functions)
// Error range: -400 to -499
match test_any_ledger_object_functions() {
0 => (),
err => return err,
}
// Category 5: Keylet Generation Functions (4 functions)
// Error range: -500 to -599
match test_keylet_generation_functions() {
0 => (),
err => return err,
}
// Category 6: Utility Functions (4 functions)
// Error range: -600 to -699
match test_utility_functions() {
0 => (),
err => return err,
}
// Category 7: Data Update Functions (1 function)
// Error range: -700 to -799
match test_data_update_functions() {
0 => (),
err => return err,
}
let _ = trace("SUCCESS: All host function tests passed!");
1 // Success return code for WASM finish function
}
/// Test Category 1: Ledger Header Data Functions (3 functions)
/// - get_ledger_sqn() - Get ledger sequence number
/// - get_parent_ledger_time() - Get parent ledger timestamp
/// - get_parent_ledger_hash() - Get parent ledger hash
fn test_ledger_header_functions() -> i32 {
let _ = trace("--- Category 1: Ledger Header Functions ---");
// Test 1.1: get_ledger_sqn() - should return current ledger sequence number
let mut sqn_buffer = [0u8; 4];
let sqn_result = unsafe { host::ldgr_index(sqn_buffer.as_mut_ptr(), sqn_buffer.len()) };
if sqn_result <= 0 {
let _ = trace_num("ERROR: get_ledger_sqn failed:", sqn_result as i64);
return -101; // Ledger sequence number test failed
}
let ledger_sqn = u32::from_be_bytes(sqn_buffer);
let _ = trace_num("Ledger sequence number:", ledger_sqn as i64);
// Test 1.2: get_parent_ledger_time() - should return parent ledger timestamp
let mut time_buffer = [0u8; 4];
let time_result =
unsafe { host::parent_ldgr_time(time_buffer.as_mut_ptr(), time_buffer.len()) };
if time_result <= 0 {
let _ = trace_num("ERROR: get_parent_ledger_time failed:", time_result as i64);
return -102; // Parent ledger time test failed
}
let parent_ledger_time = u32::from_be_bytes(time_buffer);
let _ = trace_num("Parent ledger time:", parent_ledger_time as i64);
// Test 1.3: get_parent_ledger_hash() - should return parent ledger hash (32 bytes)
let mut hash_buffer = [0u8; 32];
let hash_result =
unsafe { host::parent_ldgr_hash(hash_buffer.as_mut_ptr(), hash_buffer.len()) };
if hash_result != 32 {
let _ = trace_num(
"ERROR: get_parent_ledger_hash wrong length:",
hash_result as i64,
);
return -103; // Parent ledger hash test failed - should be exactly 32 bytes
}
let _ = trace_data("Parent ledger hash:", &hash_buffer, DataRepr::AsHex);
let _ = trace("SUCCESS: Ledger header functions");
0
}
/// Test Category 2: Transaction Data Functions (5 functions)
/// Tests all functions for accessing current transaction data
fn test_transaction_data_functions() -> i32 {
let _ = trace("--- Category 2: Transaction Data Functions ---");
// Test 2.1: get_tx_field() - Basic transaction field access
// Test with Account field (required, 20 bytes)
let mut account_buffer = [0u8; 20];
let account_len = unsafe {
host::tx_field(
sfield::Account.into(),
account_buffer.as_mut_ptr(),
account_buffer.len(),
)
};
if account_len != 20 {
let _ = trace_num(
"ERROR: get_tx_field(Account) wrong length:",
account_len as i64,
);
return -201; // Basic transaction field test failed
}
let _ = trace_account_buf("Transaction Account:", &account_buffer);
// Test with Fee field (XRP amount - 8 bytes in new serialized format)
// New format: XRP amounts are always 8 bytes (positive: value | cPositive flag, negative: just value)
let mut fee_buffer = [0u8; 8];
let fee_len = unsafe {
host::tx_field(
sfield::Fee.into(),
fee_buffer.as_mut_ptr(),
fee_buffer.len(),
)
};
if fee_len != 8 {
let _ = trace_num(
"ERROR: get_tx_field(Fee) wrong length (expected 8 bytes for XRP):",
fee_len as i64,
);
return -202; // Fee field test failed - XRP amounts should be exactly 8 bytes
}
let _ = trace_num("Transaction Fee length:", fee_len as i64);
let _ = trace_data(
"Transaction Fee (serialized XRP amount):",
&fee_buffer,
DataRepr::AsHex,
);
// Test with Sequence field (required, 4 bytes uint32)
let mut seq_buffer = [0u8; 4];
let seq_len = unsafe {
host::tx_field(
sfield::Sequence.into(),
seq_buffer.as_mut_ptr(),
seq_buffer.len(),
)
};
if seq_len != 4 {
let _ = trace_num(
"ERROR: get_tx_field(Sequence) wrong length:",
seq_len as i64,
);
return -203; // Sequence field test failed
}
let _ = trace_data("Transaction Sequence:", &seq_buffer, DataRepr::AsHex);
// NOTE: get_tx_field2() through get_tx_field6() have been deprecated.
// Use get_tx_field() with appropriate parameters for all transaction field access.
// Test 2.2: get_tx_nested_field() - Nested field access with locator
let locator = [
0x01_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8,
]; // Two int32s in little-endian: [1, 0]
let mut nested_buffer = [0u8; 32];
let nested_result = unsafe {
host::tx_inner(
locator.as_ptr(),
locator.len(),
nested_buffer.as_mut_ptr(),
nested_buffer.len(),
)
};
if nested_result < 0 {
let _ = trace_num(
"INFO: get_tx_nested_field not applicable:",
nested_result as i64,
);
// Expected - locator may not match transaction structure
} else {
let _ = trace_num("Nested field length:", nested_result as i64);
let _ = trace_data(
"Nested field:",
&nested_buffer[..nested_result as usize],
DataRepr::AsHex,
);
}
// Test 2.3: get_tx_array_len() - Get array length
let signers_len = unsafe { host::tx_arr_len(sfield::Signers.into()) };
let _ = trace_num("Signers array length:", signers_len as i64);
let memos_len = unsafe { host::tx_arr_len(sfield::Memos.into()) };
let _ = trace_num("Memos array length:", memos_len as i64);
// Test 2.4: get_tx_nested_array_len() - Get nested array length with locator
let nested_array_len = unsafe { host::tx_inner_arr_len(locator.as_ptr(), locator.len()) };
if nested_array_len < 0 {
let _ = trace_num(
"INFO: get_tx_nested_array_len not applicable:",
nested_array_len as i64,
);
} else {
let _ = trace_num("Nested array length:", nested_array_len as i64);
}
let _ = trace("SUCCESS: Transaction data functions");
0
}
/// Test Category 3: Current Ledger Object Functions (4 functions)
/// Tests functions that access the current ledger object being processed
fn test_current_ledger_object_functions() -> i32 {
let _ = trace("--- Category 3: Current Ledger Object Functions ---");
// Test 3.1: get_current_ledger_obj_field() - Access field from current ledger object
// Test with Balance field (XRP amount - 8 bytes in new serialized format)
let mut balance_buffer = [0u8; 8];
let balance_result = unsafe {
host::home_le_field(
sfield::Balance.into(),
balance_buffer.as_mut_ptr(),
balance_buffer.len(),
)
};
if balance_result <= 0 {
let _ = trace_num(
"INFO: get_current_ledger_obj_field(Balance) failed (may be expected):",
balance_result as i64,
);
// This might fail if current ledger object doesn't have balance field
} else if balance_result == 8 {
let _ = trace_num(
"Current object balance length (XRP amount):",
balance_result as i64,
);
let _ = trace_data(
"Current object balance (serialized XRP amount):",
&balance_buffer,
DataRepr::AsHex,
);
} else {
let _ = trace_num(
"Current object balance length (non-XRP amount):",
balance_result as i64,
);
let _ = trace_data(
"Current object balance:",
&balance_buffer[..balance_result as usize],
DataRepr::AsHex,
);
}
// Test with Account field
let mut current_account_buffer = [0u8; 20];
let current_account_result = unsafe {
host::home_le_field(
sfield::Account.into(),
current_account_buffer.as_mut_ptr(),
current_account_buffer.len(),
)
};
if current_account_result <= 0 {
let _ = trace_num(
"INFO: get_current_ledger_obj_field(Account) failed:",
current_account_result as i64,
);
} else {
let _ = trace_account_buf("Current ledger object account:", &current_account_buffer);
}
// Test 3.2: get_current_ledger_obj_nested_field() - Nested field access
let locator = [
0x01_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8,
]; // Two int32s in little-endian: [1, 0]
let mut current_nested_buffer = [0u8; 32];
let current_nested_result = unsafe {
host::home_le_inner(
locator.as_ptr(),
locator.len(),
current_nested_buffer.as_mut_ptr(),
current_nested_buffer.len(),
)
};
if current_nested_result < 0 {
let _ = trace_num(
"INFO: get_current_ledger_obj_nested_field not applicable:",
current_nested_result as i64,
);
} else {
let _ = trace_num("Current nested field length:", current_nested_result as i64);
let _ = trace_data(
"Current nested field:",
&current_nested_buffer[..current_nested_result as usize],
DataRepr::AsHex,
);
}
// Test 3.3: get_current_ledger_obj_array_len() - Array length in current object
let current_array_len = unsafe { host::home_le_arr_len(sfield::Signers.into()) };
let _ = trace_num(
"Current object Signers array length:",
current_array_len as i64,
);
// Test 3.4: get_current_ledger_obj_nested_array_len() - Nested array length
let current_nested_array_len =
unsafe { host::home_le_inner_arr_len(locator.as_ptr(), locator.len()) };
if current_nested_array_len < 0 {
let _ = trace_num(
"INFO: get_current_ledger_obj_nested_array_len not applicable:",
current_nested_array_len as i64,
);
} else {
let _ = trace_num(
"Current nested array length:",
current_nested_array_len as i64,
);
}
let _ = trace("SUCCESS: Current ledger object functions");
0
}
/// Test Category 4: Any Ledger Object Functions (5 functions)
/// Tests functions that work with cached ledger objects
fn test_any_ledger_object_functions() -> i32 {
let _ = trace("--- Category 4: Any Ledger Object Functions ---");
// First we need to cache a ledger object to test the other functions
// Get the account from transaction and generate its keylet
let escrow_finish = EscrowFinish;
let account_id = escrow_finish.get_account().unwrap();
// Test 4.1: cache_ledger_obj() - Cache a ledger object
let mut keylet_buffer = [0u8; 32];
let keylet_result = unsafe {
host::accountroot_id(
account_id.0.as_ptr(),
account_id.0.len(),
keylet_buffer.as_mut_ptr(),
keylet_buffer.len(),
)
};
if keylet_result != 32 {
let _ = trace_num(
"ERROR: accountroot_id failed for caching test:",
keylet_result as i64,
);
return -401; // Keylet generation failed for caching test
}
let cache_result = unsafe { host::cache_le(keylet_buffer.as_ptr(), keylet_result as usize, 0) };
if cache_result <= 0 {
let _ = trace_num(
"INFO: cache_ledger_obj failed (expected with test fixtures):",
cache_result as i64,
);
// Test fixtures may not contain the account object - this is expected
// We'll test the interface but expect failures
// Test 4.2-4.5 with invalid slot (should fail gracefully)
let mut test_buffer = [0u8; 32];
// Test get_ledger_obj_field with invalid slot
let field_result = unsafe {
host::le_field(
1,
sfield::Balance.into(),
test_buffer.as_mut_ptr(),
test_buffer.len(),
)
};
if field_result < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_field failed as expected (no cached object):",
field_result as i64,
);
}
// Test get_ledger_obj_nested_field with invalid slot
let locator = [
0x01_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8,
]; // Two int32s in little-endian: [1, 0]
let nested_result = unsafe {
host::le_inner(
1,
locator.as_ptr(),
locator.len(),
test_buffer.as_mut_ptr(),
test_buffer.len(),
)
};
if nested_result < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_nested_field failed as expected:",
nested_result as i64,
);
}
// Test get_ledger_obj_array_len with invalid slot
let array_result = unsafe { host::le_arr_len(1, sfield::Signers.into()) };
if array_result < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_array_len failed as expected:",
array_result as i64,
);
}
// Test get_ledger_obj_nested_array_len with invalid slot
let nested_array_result =
unsafe { host::le_inner_arr_len(1, locator.as_ptr(), locator.len()) };
if nested_array_result < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_nested_array_len failed as expected:",
nested_array_result as i64,
);
}
let _ = trace("SUCCESS: Any ledger object functions (interface tested)");
return 0;
}
// If we successfully cached an object, test the access functions
let slot = cache_result;
let _ = trace_num("Successfully cached object in slot:", slot as i64);
// Test 4.2: get_ledger_obj_field() - Access field from cached object
let mut cached_balance_buffer = [0u8; 8];
let cached_balance_result = unsafe {
host::le_field(
slot,
sfield::Balance.into(),
cached_balance_buffer.as_mut_ptr(),
cached_balance_buffer.len(),
)
};
if cached_balance_result <= 0 {
let _ = trace_num(
"INFO: get_ledger_obj_field(Balance) failed:",
cached_balance_result as i64,
);
} else if cached_balance_result == 8 {
let _ = trace_num(
"Cached object balance length (XRP amount):",
cached_balance_result as i64,
);
let _ = trace_data(
"Cached object balance (serialized XRP amount):",
&cached_balance_buffer,
DataRepr::AsHex,
);
} else {
let _ = trace_num(
"Cached object balance length (non-XRP amount):",
cached_balance_result as i64,
);
let _ = trace_data(
"Cached object balance:",
&cached_balance_buffer[..cached_balance_result as usize],
DataRepr::AsHex,
);
}
// Test 4.3: get_ledger_obj_nested_field() - Nested field from cached object
let locator = [
0x01_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8,
]; // Two int32s in little-endian: [1, 0]
let mut cached_nested_buffer = [0u8; 32];
let cached_nested_result = unsafe {
host::le_inner(
slot,
locator.as_ptr(),
locator.len(),
cached_nested_buffer.as_mut_ptr(),
cached_nested_buffer.len(),
)
};
if cached_nested_result < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_nested_field not applicable:",
cached_nested_result as i64,
);
} else {
let _ = trace_num("Cached nested field length:", cached_nested_result as i64);
let _ = trace_data(
"Cached nested field:",
&cached_nested_buffer[..cached_nested_result as usize],
DataRepr::AsHex,
);
}
// Test 4.4: get_ledger_obj_array_len() - Array length from cached object
let cached_array_len = unsafe { host::le_arr_len(slot, sfield::Signers.into()) };
let _ = trace_num(
"Cached object Signers array length:",
cached_array_len as i64,
);
// Test 4.5: get_ledger_obj_nested_array_len() - Nested array length from cached object
let cached_nested_array_len =
unsafe { host::le_inner_arr_len(slot, locator.as_ptr(), locator.len()) };
if cached_nested_array_len < 0 {
let _ = trace_num(
"INFO: get_ledger_obj_nested_array_len not applicable:",
cached_nested_array_len as i64,
);
} else {
let _ = trace_num(
"Cached nested array length:",
cached_nested_array_len as i64,
);
}
let _ = trace("SUCCESS: Any ledger object functions");
0
}
/// Test Category 5: Keylet Generation Functions (4 functions)
/// Tests keylet generation functions for different ledger entry types
fn test_keylet_generation_functions() -> i32 {
let _ = trace("--- Category 5: Keylet Generation Functions ---");
let escrow_finish = EscrowFinish;
let account_id = escrow_finish.get_account().unwrap();
// Test 5.1: accountroot_id() - Generate keylet for account
let mut accountroot_id_buffer = [0u8; 32];
let accountroot_id_result = unsafe {
host::accountroot_id(
account_id.0.as_ptr(),
account_id.0.len(),
accountroot_id_buffer.as_mut_ptr(),
accountroot_id_buffer.len(),
)
};
if accountroot_id_result != 32 {
let _ = trace_num(
"ERROR: accountroot_id failed:",
accountroot_id_result as i64,
);
return -501; // Account keylet generation failed
}
let _ = trace_data("Account keylet:", &accountroot_id_buffer, DataRepr::AsHex);
// Test 5.2: credential_keylet() - Generate keylet for credential
let mut credential_keylet_buffer = [0u8; 32];
let credential_keylet_result = unsafe {
host::credential_id(
account_id.0.as_ptr(), // Subject
account_id.0.len(),
account_id.0.as_ptr(), // Issuer - same account for test
account_id.0.len(),
b"TestType".as_ptr(), // Credential type
9usize, // Length of "TestType"
credential_keylet_buffer.as_mut_ptr(),
credential_keylet_buffer.len(),
)
};
if credential_keylet_result <= 0 {
let _ = trace_num(
"INFO: credential_keylet failed (expected - interface issue):",
credential_keylet_result as i64,
);
// This is expected to fail due to unusual parameter types
} else {
let _ = trace_data(
"Credential keylet:",
&credential_keylet_buffer[..credential_keylet_result as usize],
DataRepr::AsHex,
);
}
// Test 5.3: escrow_keylet() - Generate keylet for escrow
let mut escrow_keylet_buffer = [0u8; 32];
let sequence_number: i32 = 1000;
let sequence_number_bytes = sequence_number.to_be_bytes();
let escrow_keylet_result = unsafe {
host::escrow_id(
account_id.0.as_ptr(),
account_id.0.len(),
sequence_number_bytes.as_ptr(),
sequence_number_bytes.len(),
escrow_keylet_buffer.as_mut_ptr(),
escrow_keylet_buffer.len(),
)
};
if escrow_keylet_result != 32 {
let _ = trace_num("ERROR: escrow_keylet failed:", escrow_keylet_result as i64);
return -503; // Escrow keylet generation failed
}
let _ = trace_data("Escrow keylet:", &escrow_keylet_buffer, DataRepr::AsHex);
// Test 5.4: oracle_keylet() - Generate keylet for oracle
let mut oracle_keylet_buffer = [0u8; 32];
let document_id: i32 = 42;
let document_id_bytes = document_id.to_be_bytes();
let oracle_keylet_result = unsafe {
host::oracle_id(
account_id.0.as_ptr(),
account_id.0.len(),
document_id_bytes.as_ptr(),
document_id_bytes.len(),
oracle_keylet_buffer.as_mut_ptr(),
oracle_keylet_buffer.len(),
)
};
if oracle_keylet_result != 32 {
let _ = trace_num("ERROR: oracle_keylet failed:", oracle_keylet_result as i64);
return -504; // Oracle keylet generation failed
}
let _ = trace_data("Oracle keylet:", &oracle_keylet_buffer, DataRepr::AsHex);
let _ = trace("SUCCESS: Keylet generation functions");
0
}
/// Test Category 6: Utility Functions (4 functions)
/// Tests utility functions for hashing, NFT access, and tracing
fn test_utility_functions() -> i32 {
let _ = trace("--- Category 6: Utility Functions ---");
// Test 6.1: compute_sha512_half() - SHA512 hash computation (first 32 bytes)
let test_data = b"Hello, XRPL WASM world!";
let mut hash_output = [0u8; 32];
let hash_result = unsafe {
host::sha512_half(
test_data.as_ptr(),
test_data.len(),
hash_output.as_mut_ptr(),
hash_output.len(),
)
};
if hash_result != 32 {
let _ = trace_num("ERROR: compute_sha512_half failed:", hash_result as i64);
return -601; // SHA512 half computation failed
}
let _ = trace_data("Input data:", test_data, DataRepr::AsHex);
let _ = trace_data("SHA512 half hash:", &hash_output, DataRepr::AsHex);
// Test 6.2: get_nft() - NFT data retrieval
let escrow_finish = EscrowFinish;
let account_id = escrow_finish.get_account().unwrap();
let nft_id = [0u8; 32]; // Dummy NFT ID for testing
let mut nft_buffer = [0u8; 256];
let nft_result = unsafe {
host::nft_uri(
account_id.0.as_ptr(),
account_id.0.len(),
nft_id.as_ptr(),
nft_id.len(),
nft_buffer.as_mut_ptr(),
nft_buffer.len(),
)
};
if nft_result <= 0 {
let _ = trace_num(
"INFO: get_nft failed (expected - no such NFT):",
nft_result as i64,
);
// This is expected - test account likely doesn't own the dummy NFT
} else {
let _ = trace_num("NFT data length:", nft_result as i64);
let _ = trace_data(
"NFT data:",
&nft_buffer[..nft_result as usize],
DataRepr::AsHex,
);
}
// Test 6.3: trace() - Debug logging with data
let trace_message = b"Test trace message";
let trace_data_payload = b"payload";
let trace_result = unsafe {
host::trace(
trace_message.as_ptr(),
trace_message.len(),
trace_data_payload.as_ptr(),
trace_data_payload.len(),
1, // as_hex = true
)
};
if trace_result < 0 {
let _ = trace_num("ERROR: trace() failed:", trace_result as i64);
return -603; // Trace function failed
}
let _ = trace_num("Trace function bytes written:", trace_result as i64);
// Test 6.4: trace_num() - Debug logging with number
let test_number = 42i64;
let trace_num_result = trace_num("Test number trace", test_number);
use xrpl_std::host::Result;
match trace_num_result {
Result::Ok(_) => {
let _ = trace_num("Trace_num function succeeded", 0);
}
Result::Err(_) => {
let _ = trace_num("ERROR: trace_num() failed:", -604);
return -604; // Trace number function failed
}
}
let _ = trace("SUCCESS: Utility functions");
0
}
/// Test Category 7: Data Update Functions (1 function)
/// Tests the function for modifying the current ledger entry
fn test_data_update_functions() -> i32 {
let _ = trace("--- Category 7: Data Update Functions ---");
// Test 7.1: update_data() - Update current ledger entry data
let update_payload = b"Updated ledger entry data from WASM test";
let update_result = unsafe { host::set_data(update_payload.as_ptr(), update_payload.len()) };
if update_result != update_payload.len() as i32 {
let _ = trace_num("ERROR: update_data failed:", update_result as i64);
return -701; // Data update failed
}
let _ = trace_data(
"Successfully updated ledger entry with:",
update_payload,
DataRepr::AsHex,
);
let _ = trace("SUCCESS: Data update functions");
0
}

View File

@@ -1,171 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "all_keylets"
version = "0.0.1"
dependencies = [
"xrpl-wasm-stdlib",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "xrpl-macros"
version = "0.1.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#21c522f34a24b460297ebb6be1822680459bf37e"
dependencies = [
"bs58",
"quote",
"sha2",
"syn",
]
[[package]]
name = "xrpl-wasm-stdlib"
version = "0.8.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#21c522f34a24b460297ebb6be1822680459bf37e"
dependencies = [
"xrpl-macros",
]

View File

@@ -1,21 +0,0 @@
[package]
edition = "2024"
name = "all_keylets"
version = "0.0.1"
# This empty workspace definition keeps this project independent of the parent workspace
[workspace]
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
opt-level = 's'
panic = "abort"
[dependencies]
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "renames" }
[profile.dev]
panic = "abort"

View File

@@ -1,176 +0,0 @@
#![cfg_attr(target_arch = "wasm32", no_std)]
#[cfg(not(target_arch = "wasm32"))]
extern crate std;
use crate::host::{Error, Result, Result::Err, Result::Ok};
use xrpl_std::core::keylets;
use xrpl_std::core::ledger_objects::current_escrow::get_current_escrow;
use xrpl_std::core::ledger_objects::current_escrow::CurrentEscrow;
use xrpl_std::core::ledger_objects::ledger_object;
use xrpl_std::core::ledger_objects::traits::CurrentEscrowFields;
use xrpl_std::core::ledger_objects::LedgerObjectFieldGetter;
use xrpl_std::core::types::currency::Currency;
use xrpl_std::core::types::issue::{IouIssue, Issue, XrpIssue};
use xrpl_std::core::types::mpt_id::MptId;
use xrpl_std::host;
use xrpl_std::host::trace::{trace, trace_acct, trace_data, trace_num, DataRepr};
use xrpl_std::sfield;
pub fn object_exists<T: LedgerObjectFieldGetter, const CODE: i32>(
keylet_result: Result<keylets::KeyletBytes>,
keylet_type: &str,
sfield: sfield::SField<T, CODE>,
) -> Result<bool> {
let field = CODE;
match keylet_result {
Ok(keylet) => {
let _ = trace_data(keylet_type, &keylet, DataRepr::AsHex);
let slot = unsafe { host::cache_le(keylet.as_ptr(), keylet.len(), 0) };
if slot <= 0 {
let _ = trace_num("Error: ", slot.into());
return Err(Error::from_code(slot));
}
if field == 0 {
let new_field = sfield::PreviousTxnID;
let _ = trace_num("Getting field: ", new_field.clone().into());
match ledger_object::get_field(slot, new_field) {
Ok(data) => {
let _ = trace_data("Field data: ", &data.0, DataRepr::AsHex);
}
Err(result_code) => {
let _ = trace_num("Error getting field: ", result_code.into());
return Err(result_code);
}
}
} else {
let _ = trace_num("Getting field: ", field.into());
match ledger_object::get_field(slot, sfield) {
Ok(_data) => {
let _ = trace("Field data: retrieved");
}
Err(result_code) => {
let _ = trace_num("Error getting field: ", result_code.into());
return Err(result_code);
}
}
}
Ok(true)
}
Err(error) => {
let _ = trace_num("Error getting keylet: ", error.into());
Err(error)
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn escrow_finish() -> i32 {
let _ = trace("$$$$$ STARTING WASM EXECUTION $$$$$");
let escrow: CurrentEscrow = get_current_escrow();
let account = escrow.get_account().unwrap_or_panic();
let _ = trace_acct("Account:", &account);
let destination = escrow.get_destination().unwrap_or_panic();
let _ = trace_acct("Destination:", &destination);
let mut seq = 5;
macro_rules! check_object_exists {
($keylet:expr, $type:expr, $field:expr) => {
match object_exists($keylet, $type, $field) {
Ok(_exists) => {
// false isn't returned
let _ = trace(concat!(
$type,
" object exists, proceeding with escrow finish."
));
}
Err(error) => {
let _ = trace_num("Current seq value:", seq.try_into().unwrap());
return error.code();
}
}
};
}
let accountroot_id = keylets::accountroot_id(&account);
check_object_exists!(accountroot_id, "Account", sfield::Account);
let currency_code: &[u8; 3] = b"USD";
let currency: Currency = Currency::from(*currency_code);
let trustline_id = keylets::trustline_id(&account, &destination, &currency);
check_object_exists!(trustline_id, "Trustline", sfield::Generic);
seq += 1;
let asset1 = Issue::XRP(XrpIssue {});
let asset2 = Issue::IOU(IouIssue::new(destination, currency));
check_object_exists!(keylets::amm_id(&asset1, &asset2), "AMM", sfield::Account);
let check_id = keylets::check_id(&account, seq);
check_object_exists!(check_id, "Check", sfield::Account);
seq += 1;
let cred_type: &[u8] = b"termsandconditions";
let credential_id = keylets::credential_id(&account, &account, cred_type);
check_object_exists!(credential_id, "Credential", sfield::Subject);
seq += 1;
let delegate_id = keylets::delegate_id(&account, &destination);
check_object_exists!(delegate_id, "Delegate", sfield::Account);
seq += 1;
let deposit_preauth_id = keylets::deposit_preauth_id(&account, &destination);
check_object_exists!(deposit_preauth_id, "DepositPreauth", sfield::Account);
seq += 1;
let did_id = keylets::did_id(&account);
check_object_exists!(did_id, "DID", sfield::Account);
seq += 1;
let escrow_id = keylets::escrow_id(&account, seq);
check_object_exists!(escrow_id, "Escrow", sfield::Account);
seq += 1;
let mpt_issuance_id = keylets::mpt_issuance_id(&account, seq);
let mpt_id = MptId::new(seq.try_into().unwrap(), account);
check_object_exists!(mpt_issuance_id, "MPTIssuance", sfield::Issuer);
seq += 1;
let mptoken_id = keylets::mptoken_id(&mpt_id, &destination);
check_object_exists!(mptoken_id, "MPToken", sfield::Account);
let nft_offer_id = keylets::nft_offer_id(&destination, 6);
check_object_exists!(nft_offer_id, "NFTokenOffer", sfield::Owner);
let offer_id = keylets::offer_id(&account, seq);
check_object_exists!(offer_id, "Offer", sfield::Account);
seq += 1;
let paychan_id = keylets::paychan_id(&account, &destination, seq);
check_object_exists!(paychan_id, "PayChannel", sfield::Account);
seq += 1;
let pd_id = keylets::permissioned_domain_id(&account, seq);
check_object_exists!(pd_id, "PermissionedDomain", sfield::Owner);
seq += 1;
let signers_id = keylets::signers_id(&account);
check_object_exists!(signers_id, "SignerList", sfield::Generic);
seq += 1;
seq += 1; // ticket sequence number is one greater
let ticket_id = keylets::ticket_id(&account, seq);
check_object_exists!(ticket_id, "Ticket", sfield::Account);
seq += 1;
let vault_id = keylets::vault_id(&account, seq);
check_object_exists!(vault_id, "Vault", sfield::Account);
// seq += 1;
1 // All keylets exist, finish the escrow.
}

View File

@@ -1,42 +0,0 @@
#include <stdint.h>
int32_t float_from_uint(uint8_t const *, int32_t, uint8_t *, int32_t, int32_t);
int32_t check_id(uint8_t const *, int32_t, uint8_t const *, int32_t, uint8_t *,
int32_t);
uint8_t e_data1[32 * 1024];
uint8_t e_data2[32 * 1024];
int32_t test1()
{
e_data1[1] = 0xFF;
e_data1[2] = 0xFF;
e_data1[3] = 0xFF;
e_data1[4] = 0xFF;
e_data1[5] = 0xFF;
e_data1[6] = 0xFF;
e_data1[7] = 0xFF;
e_data1[8] = 0xFF;
int32_t result = float_from_uint(&e_data1[1], 8, &e_data1[35], 12, 0);
return result >= 0 ? *((int32_t *)(&e_data1[36])) : result;
}
int32_t test2()
{
// Set up misaligned uint32 (seq) at offset 1
e_data2[1] = 0xFF;
e_data2[2] = 0xFF;
e_data2[3] = 0xFF;
e_data2[4] = 0xFF;
// Set up valid non-zero AccountID (20 bytes) at offset 10
for (int i = 0; i < 20; i++)
e_data2[10 + i] = i + 1;
// Call check_id with misaligned uint32 at &e_data2[1] to hit line 72 in
// HostFuncWrapper.cpp
int32_t result = check_id(&e_data2[10], 20, &e_data2[1], 4, &e_data2[35], 32);
// Return the misaligned value directly to validate it was read correctly (-1
// if all 0xFF)
return result >= 0 ? *((int32_t *)(&e_data2[36])) : result;
}
int32_t test() { return test1() + test2(); }

View File

@@ -1,171 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
dependencies = [
"tinyvec",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "codecov_tests"
version = "0.0.1"
dependencies = [
"xrpl-wasm-stdlib",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "xrpl-macros"
version = "0.1.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#9822d645870908a79d87a57b0244caa6359cb9cf"
dependencies = [
"bs58",
"quote",
"sha2",
"syn",
]
[[package]]
name = "xrpl-wasm-stdlib"
version = "0.8.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=renames#9822d645870908a79d87a57b0244caa6359cb9cf"
dependencies = [
"xrpl-macros",
]

View File

@@ -1,18 +0,0 @@
[package]
edition = "2024"
name = "codecov_tests"
version = "0.0.1"
# This empty workspace definition keeps this project independent of the parent workspace
[workspace]
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
opt-level = 's'
panic = "abort"
[dependencies]
xrpl-std = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-wasm-stdlib", branch = "renames" }

View File

@@ -1,47 +0,0 @@
//TODO add docs after discussing the interface
//Note that Craft currently does not honor the rounding modes
#[allow(unused)]
pub const FLOAT_ROUNDING_MODES_TO_NEAREST: i32 = 0;
#[allow(unused)]
pub const FLOAT_ROUNDING_MODES_TOWARDS_ZERO: i32 = 1;
#[allow(unused)]
pub const FLOAT_ROUNDING_MODES_DOWNWARD: i32 = 2;
#[allow(unused)]
pub const FLOAT_ROUNDING_MODES_UPWARD: i32 = 3;
// pub enum RippledRoundingModes{
// ToNearest = 0,
// TowardsZero = 1,
// DOWNWARD = 2,
// UPWARD = 3
// }
#[allow(unused)]
#[link(wasm_import_module = "host_lib")]
unsafe extern "C" {
pub fn parent_ldgr_hash(out_buff_ptr: i32, out_buff_len: i32) -> i32;
pub fn cache_le(keylet_ptr: i32, keylet_len: i32, cache_num: i32) -> i32;
pub fn tx_inner_arr_len(locator_ptr: i32, locator_len: i32) -> i32;
pub fn accountroot_id(
account_ptr: i32,
account_len: i32,
out_buff_ptr: *mut u8,
out_buff_len: usize,
) -> i32;
pub fn trustline_id(
account1_ptr: *const u8,
account1_len: usize,
account2_ptr: *const u8,
account2_len: usize,
currency_ptr: i32,
currency_len: i32,
out_buff_ptr: *mut u8,
out_buff_len: usize,
) -> i32;
pub fn trace_num(msg_read_ptr: i32, msg_read_len: i32, number: i64) -> i32;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,287 +0,0 @@
# cspell: disable
import os
import re
import shlex
import subprocess
import sys
import tempfile
import zipfile
from difflib import get_close_matches
OPT = "-Oz"
BASE_PATH = os.path.abspath(os.path.dirname(__file__))
def pascal_case(name):
return "".join(word[:1].upper() + word[1:] for word in re.split(r"[_\W]+", name))
def normalize_name(name):
name = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name)
return re.sub(r"[^a-z0-9]", "", name.lower())
def fixture_key(name):
name = normalize_name(name).removeprefix("k")
return name.removesuffix("wasmhex").removesuffix("hex")
def declared_fixtures():
h_path = os.path.join(BASE_PATH, "fixtures.h")
with open(h_path, "r", encoding="utf8") as f:
return re.findall(
r"extern std::string const ([A-Za-z_][A-Za-z0-9_]*);", f.read()
)
def find_fixture_name(project_name, suffix):
default = re.sub(r"_([a-z])", lambda m: m.group(1).upper(), project_name) + suffix
k_default = f"k{pascal_case(project_name)}{suffix}"
declarations = declared_fixtures()
normalized = {normalize_name(name): name for name in declarations}
fixture_keys = {fixture_key(name): name for name in declarations}
for name in (default, k_default):
if normalize_name(name) in normalized:
return normalized[normalize_name(name)]
project_key = normalize_name(project_name)
matches = [
name
for key, name in fixture_keys.items()
if key.endswith(project_key)
or key.startswith(project_key)
or project_key.endswith(key)
or project_key.startswith(key)
]
if len(matches) == 1:
return matches[0]
close = get_close_matches(project_key, fixture_keys.keys(), n=1, cutoff=0.82)
if close:
return fixture_keys[close[0]]
return k_default
def fixture_cpp_path(fixture_name):
pattern = rf"extern std::string const {fixture_name} ="
for file_name in os.listdir(BASE_PATH):
if not file_name.endswith(".cpp"):
continue
cpp_path = os.path.join(BASE_PATH, file_name)
with open(cpp_path, "r", encoding="utf8") as f:
if re.search(pattern, f.read()):
return cpp_path
return os.path.join(BASE_PATH, "fixtures.cpp")
def update_fixture(project_name, wasm, suffix="WasmHex"):
fixture_name = find_fixture_name(project_name, suffix)
print(f"Updating fixture: {fixture_name}")
cpp_path = fixture_cpp_path(fixture_name)
h_path = os.path.join(BASE_PATH, "fixtures.h")
with open(cpp_path, "r", encoding="utf8") as f:
cpp_content = f.read()
pattern = rf'extern std::string const {fixture_name} =[ \n]+"[^;]*;'
if re.search(pattern, cpp_content, flags=re.MULTILINE):
updated_cpp_content = re.sub(
pattern,
f'extern std::string const {fixture_name} = "{wasm}";',
cpp_content,
flags=re.MULTILINE,
)
else:
with open(h_path, "r", encoding="utf8") as f:
h_content = f.read()
updated_h_content = (
h_content.rstrip() + f"\n\nextern std::string const {fixture_name};\n"
)
with open(h_path, "w", encoding="utf8") as f:
f.write(updated_h_content)
updated_cpp_content = (
cpp_content.rstrip()
+ f'\n\nextern std::string const {fixture_name} = "{wasm}";\n'
)
with open(cpp_path, "w", encoding="utf8") as f:
f.write(updated_cpp_content)
def read_wasm_hex(path):
with open(path, "rb") as f:
return f.read().hex()
def process_rust(project_name):
project_path = os.path.join(BASE_PATH, project_name)
wasm_location = os.path.join(
project_path, "target", "wasm32v1-none", "release", f"{project_name}.wasm"
)
try:
subprocess.run(
["cargo", "build", "--target", "wasm32v1-none", "--release"],
cwd=project_path,
check=True,
)
subprocess.run(
["wasm-opt", wasm_location, OPT, "-o", wasm_location], check=True
)
print(f"WASM file for {project_name} has been built and optimized.")
except FileNotFoundError as e:
print(f"exec error: {e.filename} is required to build Rust fixtures")
sys.exit(1)
except subprocess.CalledProcessError as e:
print(f"exec error: {e}")
sys.exit(1)
update_fixture(project_name, read_wasm_hex(wasm_location))
def process_c(project_name):
project_path = os.path.join(BASE_PATH, f"{project_name}.c")
wasm_path = os.path.join(BASE_PATH, f"{project_name}.wasm")
cc = os.environ.get("CC")
sysroot = os.environ.get("SYSROOT")
if not cc or not sysroot:
print("exec error: CC and SYSROOT are required to build C fixtures")
sys.exit(1)
build_cmd = [
*shlex.split(cc),
f"--sysroot={sysroot}",
"-O3",
"-ffast-math",
"--target=wasm32",
"-fno-exceptions",
"-fno-threadsafe-statics",
"-fvisibility=default",
"-Wl,--export-all",
"-Wl,--no-entry",
"-Wl,--allow-undefined",
"-DNDEBUG",
"--no-standard-libraries",
"-fno-builtin-memset",
"-o",
wasm_path,
project_path,
]
try:
subprocess.run(build_cmd, check=True)
subprocess.run(["wasm-opt", wasm_path, OPT, "-o", wasm_path], check=True)
print(
f"WASM file for {project_name} has been built with WASI support using clang."
)
except FileNotFoundError as e:
print(f"exec error: {e.filename} is required to build C fixtures")
sys.exit(1)
except subprocess.CalledProcessError as e:
print(f"exec error: {e}")
sys.exit(1)
update_fixture(project_name, read_wasm_hex(wasm_path))
def wat_to_wasm(wat_path, wasm_path):
build_cmd = ["wat2wasm", wat_path, "-o", wasm_path]
try:
subprocess.run(build_cmd, check=True)
print(f"WASM file for {os.path.basename(wat_path)} has been built.")
except FileNotFoundError:
print("exec error: wat2wasm is required to build WAT fixtures")
sys.exit(1)
except subprocess.CalledProcessError as e:
print(f"exec error: {e}")
sys.exit(1)
def process_wat_file(wat_path):
project_name = os.path.splitext(os.path.basename(wat_path))[0]
with open(wat_path, "r", encoding="utf8") as f:
if "(module" not in f.read():
print(f"Skipping WAT fixture without a module: {project_name}")
return
with tempfile.TemporaryDirectory() as tmpdir:
wasm_path = os.path.join(tmpdir, f"{project_name}.wasm")
wat_to_wasm(wat_path, wasm_path)
update_fixture(project_name, read_wasm_hex(wasm_path), "Hex")
def process_wat_zip(zip_path):
project_name = os.path.splitext(os.path.basename(zip_path))[0]
with tempfile.TemporaryDirectory() as tmpdir:
with zipfile.ZipFile(zip_path) as archive:
wat_names = [name for name in archive.namelist() if name.endswith(".wat")]
if len(wat_names) != 1:
print(f"exec error: expected one .wat file in {zip_path}")
sys.exit(1)
archive.extract(wat_names[0], tmpdir)
wasm_path = os.path.join(tmpdir, f"{project_name}.wasm")
wat_to_wasm(os.path.join(tmpdir, wat_names[0]), wasm_path)
update_fixture(project_name, read_wasm_hex(wasm_path), "Hex")
def process_wat(project_name):
candidates = [
os.path.join(BASE_PATH, f"{project_name}.wat"),
os.path.join(BASE_PATH, "wat", f"{project_name}.wat"),
os.path.join(BASE_PATH, "wat", f"{project_name}.zip"),
]
for path in candidates:
if os.path.isfile(path):
if path.endswith(".zip"):
process_wat_zip(path)
else:
process_wat_file(path)
return
print(f"exec error: fixture {project_name} not found")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) > 2:
print("Usage: python copyFixtures.py [<project_name>]")
sys.exit(1)
if len(sys.argv) == 2:
project_name = os.path.splitext(os.path.basename(sys.argv[1]))[0]
if os.path.isfile(os.path.join(BASE_PATH, project_name, "Cargo.toml")):
process_rust(project_name)
elif os.path.isfile(os.path.join(BASE_PATH, f"{project_name}.c")):
process_c(project_name)
else:
process_wat(project_name)
print("Fixture has been processed.")
else:
dirs = [
d
for d in os.listdir(BASE_PATH)
if os.path.isfile(os.path.join(BASE_PATH, d, "Cargo.toml"))
]
c_files = [f for f in os.listdir(BASE_PATH) if f.endswith(".c")]
wat_files = [f for f in os.listdir(BASE_PATH) if f.endswith(".wat")]
wat_path = os.path.join(BASE_PATH, "wat")
wat_fixture_files = [
f
for f in (os.listdir(wat_path) if os.path.isdir(wat_path) else [])
if f.endswith((".wat", ".zip"))
]
for d in sorted(dirs):
process_rust(d)
for c in sorted(c_files):
process_c(c[:-2])
for wat in sorted(wat_files):
process_wat_file(os.path.join(BASE_PATH, wat))
for wat_fixture in sorted(wat_fixture_files):
path = os.path.join(wat_path, wat_fixture)
if wat_fixture.endswith(".zip"):
process_wat_zip(path)
else:
process_wat_file(path)
print("All fixtures have been processed.")

View File

@@ -1,657 +0,0 @@
// TODO: consider moving these to separate files (and figure out the build)
#include <test/app/wasm_fixtures/fixtures.h>
#include <string>
extern std::string const kLedgerSqnWasmHex =
"0061736d01000000010e0360027f7f017f6000006000017f02120103656e760a6c6467725f696e6465780000030302"
"01020503010002063f0a7f01418088040b7f004180080b7f004180080b7f004180080b7f00418088040b7f00418008"
"0b7f00418088040b7f00418080080b7f0041000b7f0041010b07b1010c066d656d6f72790200115f5f7761736d5f63"
"616c6c5f63746f727300010d657363726f775f66696e69736800020c5f5f64736f5f68616e646c6503010a5f5f6461"
"74615f656e6403020b5f5f737461636b5f6c6f7703030c5f5f737461636b5f6869676803040d5f5f676c6f62616c5f"
"6261736503050b5f5f686561705f6261736503060a5f5f686561705f656e6403070d5f5f6d656d6f72795f62617365"
"03080c5f5f7461626c655f6261736503090a3d0202000b3801037f230041106b220024002000410c6a410410002101"
"200028020c2102200041106a2400200141054100200241054f1b20014100481b0b007f0970726f647563657273010c"
"70726f6365737365642d62790105636c616e675f31392e312e352d776173692d73646b202868747470733a2f2f6769"
"746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235613264623538323935386166316565"
"33303861373930636664623432626432343732302900490f7461726765745f6665617475726573042b0f6d75746162"
"6c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b0a6d756c746976616c"
"7565";
extern std::string const kAllHostFunctionsWasmHex =
"0061736d0100000001540c60027f7f017f60037f7f7f017f60047f7f7f7f017f60017f017f60067f7f7f7f7f7f017f"
"60037f7f7f0060057f7f7f7f7f017f60037f7f7e017f60087f7f7f7f7f7f7f7f017f60017f0060027f7f006000017f"
"02dc041a08686f73745f6c69620874785f6669656c64000108686f73745f6c69620974726163655f6e756d00070868"
"6f73745f6c6962057472616365000608686f73745f6c69620a6c6467725f696e646578000008686f73745f6c696210"
"706172656e745f6c6467725f74696d65000008686f73745f6c696210706172656e745f6c6467725f68617368000008"
"686f73745f6c69620874785f696e6e6572000208686f73745f6c69620a74785f6172725f6c656e000308686f73745f"
"6c69621074785f696e6e65725f6172725f6c656e000008686f73745f6c69620d686f6d655f6c655f6669656c640001"
"08686f73745f6c69620d686f6d655f6c655f696e6e6572000208686f73745f6c69620f686f6d655f6c655f6172725f"
"6c656e000308686f73745f6c696215686f6d655f6c655f696e6e65725f6172725f6c656e000008686f73745f6c6962"
"0863616368655f6c65000108686f73745f6c69620d63726564656e7469616c5f6964000808686f73745f6c69620965"
"7363726f775f6964000408686f73745f6c6962096f7261636c655f6964000408686f73745f6c69620b736861353132"
"5f68616c66000208686f73745f6c6962076e66745f757269000408686f73745f6c6962087365745f64617461000008"
"686f73745f6c6962086c655f6669656c64000208686f73745f6c6962086c655f696e6e6572000608686f73745f6c69"
"620a6c655f6172725f6c656e000008686f73745f6c6962106c655f696e6e65725f6172725f6c656e000108686f7374"
"5f6c69620e6163636f756e74726f6f745f6964000208686f73745f6c69620a74726163655f616363740002030c0b09"
"0a05050b05000101030005030100110619037f01418080c0000b7f0041af99c0000b7f0041b099c0000b073504066d"
"656d6f727902000d657363726f775f66696e697368001e0a5f5f646174615f656e6403010b5f5f686561705f626173"
"6503020a911e0b990101027f230041306b220124002000027f418180202001411c6a4114100022024114470440417f"
"20022002417f4e1b210241010c010b200020012f001c3b0001200041036a2001411e6a2d00003a0000200120012900"
"233703082001200141286a29000037000d200128001f21022000410d6a200129000d37000020002001290308370208"
"41000b3a000020002002360204200141306a24000b460020012d00004101460440418080c000410b20013402041001"
"000b20002001290001370000200041106a200141116a280000360000200041086a200141096a2900003700000b1900"
"200241094f0440000b20002002360204200020013602000b1900200241214f0440000b200020023602042000200136"
"02000ba91b01097f230041b0036b22002400418b80c000411b41014100410010021a41a680c0004119410141004100"
"10021a41e780c000412b41014100410010021a2000410036027002400240024002400240024002400240200041f000"
"6a220741041003220141004a0440419281c00041172000280270220141187420014180fe0371410874722001410876"
"4180fe037120014118767272ad10011a200041003602900120004190016a220341041004220141004c0d0141a981c0"
"004113200028029001220141187420014180fe03714108747220014108764180fe037120014118767272ad10011a20"
"0041c8016a22024200370300200041c0016a22054200370300200041b8016a22044200370300200042003703b00120"
"0041b0016a22064120100522014120470d0241bc81c000411320064120410110021a41cf81c0004120410141004100"
"10021a41dc82c000412e41014100410010021a200041a0016a410036020020004198016a4200370300200042003703"
"90014181802020034114100022014114470d03418a83c00041142003101f2000420037034841888018200041c8006a"
"22034108100022014108470d04419e83c0004117420810011a41b583c000412820034108410110021a200041003602"
"3041848008200041306a22034104100022014104470d0541dd83c000411520034104410110021a200041f4006a4100"
"36000020004100360071200041013a0070200242003703002005420037030020044200370300200042003703b00102"
"4020074108200641201006220141004e044041f283c00041142001ad10011a200041286a20062001101d418684c000"
"410d2000280228200028022c410110021a0c010b419384c00041292001ac10011a0b41bc84c00041154183803c1007"
"ac10011a41d184c00041134189803c1007ac10011a0240200041f0006a41081008220141004e044041e484c0004114"
"2001ad10011a0c010b41f884c000412d2001ac10011a0b41a585c000412341014100410010021a41de86c000413341"
"014100410010021a2000420037034841828018200041c8006a220141081009220341004c0d06200341084604404191"
"87c000412b420810011a41bc87c000412f20014108410110021a0c080b41eb87c000412f2003ad10011a200041206a"
"200041c8006a2003101c419a88c000411720002802202000280224410110021a0c070b41bf82c000411d2001ac1001"
"1a419b7f21020c070b419a82c00041252001ac10011a419a7f21020c060b41ef81c000412b2001ac10011a41997f21"
"020c050b41b486c000412a2001ac10011a41b77e21020c040b41f385c00041c1002001ac10011a41b67e21020c030b"
"41c885c000412b2001ac10011a41b57e21020c020b41b188c00041c5002003ac10011a0b200041a0016a4100360200"
"20004198016a4200370300200042003703900102404181802020004190016a220341141009220141004a044041f688"
"c000411e2003101f0c010b419489c00041332001ac10011a0b200041f4006a41003600002000410036007120004101"
"3a0070200041c8016a4200370300200041c0016a4200370300200041b8016a4200370300200042003703b001024020"
"0041f0006a4108200041b0016a22014120100a220341004e044041c789c000411c2003ad10011a200041186a200120"
"03101d41e389c00041152000280218200028021c410110021a0c010b41f889c00041392003ac10011a0b41b18ac000"
"41244183803c100bac10011a0240200041f0006a4108100c220141004e044041d58ac000411c2001ad10011a0c010b"
"41f18ac000413d2001ac10011a0b41ae8bc000412841014100410010021a41d68bc000412f41014100410010021a20"
"0041b0016a2203101a200041f0006a22012003101b200041a8016a4200370300200041a0016a420037030020004198"
"016a4200370300200042003703900102400240024002400240200120004190016a2203102022014120460440200341"
"204100100d220441004a044041858cc00041232004ad10011a200042003703302004200041306a2201410810212203"
"41004c0d022003410846044041a88cc000412a420810011a41d28cc000412e20014108410110021a0c060b41808dc0"
"00412e2003ad10011a200041106a200041306a2003101c41ae8dc000411620002802102000280214410110021a0c05"
"0b41e68fc000413c2004ac10011a200041c8016a4200370300200041c0016a4200370300200041b8016a4200370300"
"200042003703b0014101200041b0016a4120102122014100480d020c030b41ba92c000412e2001ac10011a41ef7c21"
"020c050b41c48dc000412b2003ac10011a0c020b41a290c00041c1002001ac10011a0b200041cc006a410036000020"
"004100360049200041013a00484101200041c8006a200041b0016a10222201410048044041e390c00041352001ac10"
"011a0b4101102322014100480440419891c00041322001ac10011a0b4101200041c8006a10242201410048044041ca"
"91c00041392001ac10011a0b418392c000413741014100410010021a0c010b200041cc006a41003600002000410036"
"0049200041013a0048200041c8016a4200370300200041c0016a4200370300200041b8016a42003703002000420037"
"03b00102402004200041c8006a200041b0016a22011022220341004e044041ef8dc000411b2003ad10011a20004108"
"6a20012003101d418a8ec00041142000280208200028020c410110021a0c010b419e8ec00041312003ac10011a0b41"
"cf8ec000412320041023ac10011a02402004200041c8006a1024220141004e044041f28ec000411b2001ad10011a0c"
"010b418d8fc00041352001ac10011a0b41c28fc000412441014100410010021a0b41e892c000412f41014100410010"
"021a200041b0016a2201101a200041306a22042001101b200041e0006a4200370300200041d8006a42003703002000"
"41d0006a420037030020004200370348024002400240024002402004200041c8006a22031020220141204604404197"
"93c000410f20034120410110021a20004188016a420037030020004180016a4200370300200041f8006a4200370300"
"200042003703700240200441142004411441a693c0004109200041f0006a22014120100e220341004a044020002001"
"2003101d41ae93c000411220002802002000280204410110021a0c010b41c093c000413c2003ac10011a0b200041a8"
"016a22064200370300200041a0016a2202420037030020004198016a22054200370300200042003703900120004180"
"808cc07e360268200041306a22034114200041e8006a410420004190016a22084120100f22014120470d0141fc93c0"
"00410e20084120410110021a200041c8016a4200370300200041c0016a4200370300200041b8016a42003703002000"
"42003703b001200041808080d00236026c20034114200041ec006a4104200041b0016a22044120101022014120470d"
"02418a94c000410e20044120410110021a419894c000412441014100410010021a419195c000412541014100410010"
"021a20004188016a420037030020004180016a4200370300200041f8006a42003703002000420037037041b695c000"
"4117200041f0006a22034120101122014120470d0341cd95c000410b41b695c0004117410110021a41d895c0004111"
"20034120410110021a2004101a200041c8006a22072004101b20064200370300200242003703002005420037030020"
"0042003703900102404100200422026b410371220320026a220520024d0d0020030440200321010340200241003a00"
"00200241016a2102200141016b22010d000b0b200341016b4107490d000340200241003a0000200241076a41003a00"
"00200241066a41003a0000200241056a41003a0000200241046a41003a0000200241036a41003a0000200241026a41"
"003a0000200241016a41003a0000200241086a22022005470d000b0b200541800220036b2201417c716a220220054b"
"0440034020054100360200200541046a22052002490d000b0b024020022001410371220120026a22034f0d00200122"
"0504400340200241003a0000200241016a2102200541016b22050d000b0b200141016b4107490d000340200241003a"
"0000200241076a41003a0000200241066a41003a0000200241056a41003a0000200241046a41003a0000200241036a"
"41003a0000200241026a41003a0000200241016a41003a0000200241086a22022003470d000b0b0240200741142008"
"412020044180021012220141004a044041e995c00041102001ad10011a20014181024f0d0641f995c0004109200420"
"01410110021a0c010b418296c000412e2001ac10011a0b41b096c000411241c296c00041074101100222014100480d"
"0541c996c000411d2001ad10011a41e696c0004111422a1001410048044041ad97c000411a42a47b10011a41a47b21"
"020c070b41f796c000411c420010011a41012102419397c000411a41014100410010021a41ff97c000412941014100"
"410010021a41a898c000412810132201412846044041d098c000412741a898c0004128410110021a41f798c000411e"
"41014100410010021a41bf80c000412841014100410010021a0c070b419599c000411a2001ac10011a41c37a21020c"
"060b41f494c000411d2001ac10011a418b7c21020c050b41d894c000411c2001ac10011a41897c21020c040b41bc94"
"c000411c2001ac10011a41887c21020c030b41dd97c00041222001ac10011a41a77b21020c020b000b41c797c00041"
"162001ac10011a41a57b21020b200041b0036a240020020b0d00200020012002411410191a0b0c0020004114200141"
"2010180b0e002000418280182001200210140b0e002000200141082002412010150b0a0020004183803c10160b0a00"
"20002001410810170b0bb9190100418080c0000baf196572726f725f636f64653d3d3d3d20484f53542046554e4354"
"494f4e532054455354203d3d3d54657374696e6720323620686f73742066756e6374696f6e73535543434553533a20"
"416c6c20686f73742066756e6374696f6e20746573747320706173736564212d2d2d2043617465676f727920313a20"
"4c6564676572204865616465722046756e6374696f6e73202d2d2d4c65646765722073657175656e6365206e756d62"
"65723a506172656e74206c65646765722074696d653a506172656e74206c656467657220686173683a535543434553"
"533a204c6564676572206865616465722066756e6374696f6e734552524f523a206765745f706172656e745f6c6564"
"6765725f686173682077726f6e67206c656e6774683a4552524f523a206765745f706172656e745f6c65646765725f"
"74696d65206661696c65643a4552524f523a206765745f6c65646765725f73716e206661696c65643a2d2d2d204361"
"7465676f727920323a205472616e73616374696f6e20446174612046756e6374696f6e73202d2d2d5472616e736163"
"74696f6e204163636f756e743a5472616e73616374696f6e20466565206c656e6774683a5472616e73616374696f6e"
"20466565202873657269616c697a65642058525020616d6f756e74293a5472616e73616374696f6e2053657175656e"
"63653a4e6573746564206669656c64206c656e6774683a4e6573746564206669656c643a494e464f3a206765745f74"
"785f6e65737465645f6669656c64206e6f74206170706c696361626c653a5369676e657273206172726179206c656e"
"6774683a4d656d6f73206172726179206c656e6774683a4e6573746564206172726179206c656e6774683a494e464f"
"3a206765745f74785f6e65737465645f61727261795f6c656e206e6f74206170706c696361626c653a535543434553"
"533a205472616e73616374696f6e20646174612066756e6374696f6e734552524f523a206765745f74785f6669656c"
"642853657175656e6365292077726f6e67206c656e6774683a4552524f523a206765745f74785f6669656c64284665"
"65292077726f6e67206c656e67746820286578706563746564203820627974657320666f7220585250293a4552524f"
"523a206765745f74785f6669656c64284163636f756e74292077726f6e67206c656e6774683a2d2d2d204361746567"
"6f727920333a2043757272656e74204c6564676572204f626a6563742046756e6374696f6e73202d2d2d4375727265"
"6e74206f626a6563742062616c616e6365206c656e677468202858525020616d6f756e74293a43757272656e74206f"
"626a6563742062616c616e6365202873657269616c697a65642058525020616d6f756e74293a43757272656e74206f"
"626a6563742062616c616e6365206c656e67746820286e6f6e2d58525020616d6f756e74293a43757272656e74206f"
"626a6563742062616c616e63653a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6669656c"
"642842616c616e636529206661696c656420286d6179206265206578706563746564293a43757272656e74206c6564"
"676572206f626a656374206163636f756e743a494e464f3a206765745f63757272656e745f6c65646765725f6f626a"
"5f6669656c64284163636f756e7429206661696c65643a43757272656e74206e6573746564206669656c64206c656e"
"6774683a43757272656e74206e6573746564206669656c643a494e464f3a206765745f63757272656e745f6c656467"
"65725f6f626a5f6e65737465645f6669656c64206e6f74206170706c696361626c653a43757272656e74206f626a65"
"6374205369676e657273206172726179206c656e6774683a43757272656e74206e6573746564206172726179206c65"
"6e6774683a494e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f61727261795f"
"6c656e206e6f74206170706c696361626c653a535543434553533a2043757272656e74206c6564676572206f626a65"
"63742066756e6374696f6e732d2d2d2043617465676f727920343a20416e79204c6564676572204f626a6563742046"
"756e6374696f6e73202d2d2d5375636365737366756c6c7920636163686564206f626a65637420696e20736c6f743a"
"436163686564206f626a6563742062616c616e6365206c656e677468202858525020616d6f756e74293a4361636865"
"64206f626a6563742062616c616e6365202873657269616c697a65642058525020616d6f756e74293a436163686564"
"206f626a6563742062616c616e6365206c656e67746820286e6f6e2d58525020616d6f756e74293a43616368656420"
"6f626a6563742062616c616e63653a494e464f3a206765745f6c65646765725f6f626a5f6669656c642842616c616e"
"636529206661696c65643a436163686564206e6573746564206669656c64206c656e6774683a436163686564206e65"
"73746564206669656c643a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206e6f"
"74206170706c696361626c653a436163686564206f626a656374205369676e657273206172726179206c656e677468"
"3a436163686564206e6573746564206172726179206c656e6774683a494e464f3a206765745f6c65646765725f6f62"
"6a5f6e65737465645f61727261795f6c656e206e6f74206170706c696361626c653a535543434553533a20416e7920"
"6c6564676572206f626a6563742066756e6374696f6e73494e464f3a2063616368655f6c65646765725f6f626a2066"
"61696c65642028657870656374656420776974682074657374206669787475726573293a494e464f3a206765745f6c"
"65646765725f6f626a5f6669656c64206661696c656420617320657870656374656420286e6f20636163686564206f"
"626a656374293a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f6669656c64206661696c6564"
"2061732065787065637465643a494e464f3a206765745f6c65646765725f6f626a5f61727261795f6c656e20666169"
"6c65642061732065787065637465643a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f617272"
"61795f6c656e206661696c65642061732065787065637465643a535543434553533a20416e79206c6564676572206f"
"626a6563742066756e6374696f6e732028696e7465726661636520746573746564294552524f523a206163636f756e"
"74726f6f745f6964206661696c656420666f722063616368696e6720746573743a2d2d2d2043617465676f72792035"
"3a204b65796c65742047656e65726174696f6e2046756e6374696f6e73202d2d2d4163636f756e74206b65796c6574"
"3a546573745479706543726564656e7469616c206b65796c65743a494e464f3a2063726564656e7469616c5f6b6579"
"6c6574206661696c656420286578706563746564202d20696e74657266616365206973737565293a457363726f7720"
"6b65796c65743a4f7261636c65206b65796c65743a535543434553533a204b65796c65742067656e65726174696f6e"
"2066756e6374696f6e734552524f523a206f7261636c655f6b65796c6574206661696c65643a4552524f523a206573"
"63726f775f6b65796c6574206661696c65643a4552524f523a206163636f756e74726f6f745f6964206661696c6564"
"3a2d2d2d2043617465676f727920363a205574696c6974792046756e6374696f6e73202d2d2d48656c6c6f2c205852"
"504c205741534d20776f726c6421496e70757420646174613a5348413531322068616c6620686173683a4e46542064"
"617461206c656e6774683a4e465420646174613a494e464f3a206765745f6e6674206661696c656420286578706563"
"746564202d206e6f2073756368204e4654293a54657374207472616365206d6573736167657061796c6f6164547261"
"63652066756e6374696f6e206279746573207772697474656e3a54657374206e756d62657220747261636554726163"
"655f6e756d2066756e6374696f6e20737563636565646564535543434553533a205574696c6974792066756e637469"
"6f6e734552524f523a2074726163655f6e756d2829206661696c65643a4552524f523a207472616365282920666169"
"6c65643a4552524f523a20636f6d707574655f7368613531325f68616c66206661696c65643a2d2d2d204361746567"
"6f727920373a2044617461205570646174652046756e6374696f6e73202d2d2d55706461746564206c656467657220"
"656e74727920646174612066726f6d205741534d20746573745375636365737366756c6c792075706461746564206c"
"656467657220656e74727920776974683a535543434553533a2044617461207570646174652066756e6374696f6e73"
"4552524f523a207570646174655f64617461206661696c65643a004d0970726f64756365727302086c616e67756167"
"65010452757374000c70726f6365737365642d6279010572757374631d312e38372e30202831373036376539616320"
"323032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c73"
"2b087369676e2d657874";
extern std::string const kAllKeyletsWasmHex =
"0061736d0100000001500a60067f7f7f7f7f7f017f60047f7f7f7f017f60087f7f7f7f7f7f7f7f017f60047f7f7f7f"
"0060037f7f7f017f60037f7f7e017f60057f7f7f7f7f017f6000017f60037f7f7f0060067f7f7f7f7f7e00029f0418"
"08686f73745f6c69620974726163655f6e756d000508686f73745f6c6962057472616365000608686f73745f6c6962"
"0863616368655f6c65000408686f73745f6c6962086c655f6669656c64000108686f73745f6c69620d686f6d655f6c"
"655f6669656c64000408686f73745f6c69620a74726163655f61636374000108686f73745f6c69620e6163636f756e"
"74726f6f745f6964000108686f73745f6c69620c74727573746c696e655f6964000208686f73745f6c696206616d6d"
"5f6964000008686f73745f6c696208636865636b5f6964000008686f73745f6c69620d63726564656e7469616c5f69"
"64000208686f73745f6c69620b64656c65676174655f6964000008686f73745f6c6962126465706f7369745f707265"
"617574685f6964000008686f73745f6c6962066469645f6964000108686f73745f6c696209657363726f775f696400"
"0008686f73745f6c69620f6d70745f69737375616e63655f6964000008686f73745f6c69620a6d70746f6b656e5f69"
"64000008686f73745f6c69620c6e66745f6f666665725f6964000008686f73745f6c6962086f666665725f69640000"
"08686f73745f6c69620a7061796368616e5f6964000208686f73745f6c6962167065726d697373696f6e65645f646f"
"6d61696e5f6964000008686f73745f6c69620a7369676e6572735f6964000108686f73745f6c6962097469636b6574"
"5f6964000008686f73745f6c6962087661756c745f6964000003070603030307080905030100110619037f01418080"
"c0000b7f0041c28ac0000b7f0041d08ac0000b073504066d656d6f727902000d657363726f775f66696e697368001b"
"0a5f5f646174615f656e6403010b5f5f686561705f6261736503020ae8370614002000200120022003418280204282"
"8020101d0b140020002001200220034181802042818020101d0bd10302017f017e230041a0016b2204240002402001"
"2d0000410146044041d780c000411620012802042201ac10001a200041013a0000200020013602040c010b20044118"
"6a200141196a290000370300200441106a200141116a290000370300200441086a200141096a290000370300200420"
"012900013703002002200320044120410110011a2004412041001002220141004c044041d080c00041072001ac1000"
"1a200041013a0000200020013602040c010b418b80c000410f4285801410001a20014185801420044180016a412010"
"032201412047044041af80c0004115417f20012001417f4e1b2201ac10001a200041013a0000200020013602040c01"
"0b200441c2006a20044182016a2d00003a0000200441f0006a20044197016a2900002205370300200441286a220120"
"04418f016a290000370300200441306a22022005370300200441386a22032004419f016a2d00003a0000200420042f"
"0080013b014020042004290087013703202004200428008301360043200441df006a20032d00003a0000200441d700"
"6a2002290300370000200441cf006a20012903003700002004200429032037004741c480c000410c200441406b4120"
"410110011a20004180023b01000b200441a0016a24000bd32c02097f027e23004180076b2200240041ed80c0004123"
"41014100410010011a02402000027f02404181802020004190016a220741141004220641144604402000410e6a2000"
"4192016a22032d00003a000020002000290097013703e80120002000419c016a22012900003700ed01200020002f00"
"90013b010c200020002903e8013703d806200020002900ed013700dd06200020002800930136000f200041186a2000"
"2900dd06370000200020002903d806370013419081c00041082000410c6a2204411410051a41838020200741141004"
"22064114470d03200041226a20032d00003a000020002000290097013703e801200020012900003700ed0120002000"
"2f0090013b0120200020002903e8013703d806200020002900ed013700dd0620002000280093013600232000412c6a"
"20002900dd06370000200020002903d806370027419881c000410c200041206a411410051a200041a8016a22034200"
"370300200041a0016a2201420037030020004198016a42003703002000420037039001200441142007412010062204"
"4120460d01024020044100480440200020043602380c010b2000417f3602380b41010c020b0c020b200041cd006a20"
"03290300370000200041c5006a20012903003700002000413d6a20004198016a290300370000200020002903900137"
"003541000b3a003420004190016a200041346a41a481c00041071019024020002d0090014101460440200028029401"
"2106419c8ac0004112420510001a0c010b4100210641ab81c000413541014100410010011a200041e6006a41c4003a"
"0000200041e0006a4100360200200041eb006a41003a0000200041d5a6013b01642000420037035820004100360067"
"200041a8016a22044200370300200041a0016a2203420037030020004198016a220142003703002000420037039001"
"02402000410c6a4114200041206a4114200041d8006a411420004190016a4120100722074120470440024020074100"
"480440200020073602700c010b2000417f3602700b410121060c010b20004185016a2004290300370000200041fd00"
"6a2003290300370000200041f5006a2001290300370000200020002903900137006d0b200020063a006c2000419001"
"6a200041ec006a41e081c0004109101a20002d00900141014604402000280294012106419c8ac0004112420510001a"
"0c010b4100210641e981c000413741014100410010011a200041f8016a200041306a2204280100360200200041f001"
"6a200041286a220329010037030020004184026a200041e0006a290300220a3702002000418c026a200041e8006a28"
"02002201360200200020002901203703e8012000200029035822093702fc01200041e8066a22052001360200200041"
"e0066a2207200a370300200020093703d806200041f4066a2003290100370200200041fc066a200428010036020020"
"0020002901203702ec0620004190026a200041d8066a22034128101c20004194016a200041e8016a41d000101c2000"
"410136029001200041f0066a220142003703002005420037030020074200370300200042003703d806024041ae8ac0"
"004114200041bc016a412820034120100822034120470440024020034100480440200020033602ec010c010b200041"
"7f3602ec010b410121060c010b20004181026a2001290300370000200041f9016a2005290300370000200041f1016a"
"2007290300370000200020002903d8063700e9010b200020063a00e801200041bc026a200041e8016a41a082c00041"
"03101920002d00bc02410146044020002802c0022106419c8ac0004112420610001a0c010b4100210641a382c00041"
"3141014100410010011a200041063602d80620004180026a22044200370300200041f8016a22034200370300200041"
"f0016a22014200370300200042003703e80102402000410c6a4114200041d8066a4104200041e8016a412010092207"
"4120470440024020074100480440200020073602c8020c010b2000417f3602c8020b410121060c010b200041dd026a"
"2004290300370000200041d5026a2003290300370000200041cd026a2001290300370000200020002903e8013700c5"
"020b200020063a00c402200041e8016a200041c4026a41d482c0004105101920002d00e801410146044020002802ec"
"012106419c8ac0004112420610001a0c010b41d982c000413341014100410010011a20004180026a42003703002000"
"41f8016a4200370300200041f0016a4200370300200042003703e801024002402000410c6a2201411420014114418c"
"83c0004112200041e8016a4120100a2201412047044041d780c0004116417f20012001417f4e1b2206ac10001a0c01"
"0b200041da066a20002d00ea013a0000200041f0026a200041f7016a290000220a370300200041f8026a200041ff01"
"6a290000220937030020004180036a20004187026a2d000022013a0000200041e7066a200a370000200041ef066a20"
"09370000200041f7066a20013a0000200020002f01e8013b01d806200020002900ef0122093703e802200020002800"
"eb013600db06200020093700df06419e83c000410a200041d8066a22014120410110011a2001412041001002220641"
"004c044041d080c00041072006ac10001a0c010b418b80c000410f4298802010001a200641988020200041e8016a41"
"14100322014114460d0141af80c0004115417f20012001417f4e1b2206ac10001a0b419c8ac0004112420710001a0c"
"010b419a80c000411541014100410010011a41a883c000413841014100410010011a230041206b2208240020084118"
"6a22074200370300200841106a22044200370300200841086a220342003703002008420037030020004184036a2201"
"027f2000410c6a22064114200041206a2202411420084120100b220541204704400240200541004804402001200536"
"02040c010b2001417f3602040b41010c010b20012008290300370001200141196a2007290300370000200141116a20"
"04290300370000200141096a200329030037000041000b3a0000200841206a2400200041e8016a2205200141e083c0"
"004108101920002d00e80145044041e883c000413641014100410010011a230041206b22082400200841186a220742"
"00370300200841106a22044200370300200841086a2203420037030020084200370300200041a8036a2201027f2006"
"41142002411420084120100c22024120470440024020024100480440200120023602040c010b2001417f3602040b41"
"010c010b20012008290300370001200141196a2007290300370000200141116a2004290300370000200141096a2003"
"29030037000041000b3a0000200841206a240020052001419e84c000410e101920002d00e801410146044020002802"
"ec012106419c8ac0004112420910001a0c020b41ac84c000413c41014100410010011a230041206b22022400200241"
"186a22074200370300200241106a22044200370300200241086a2203420037030020024200370300200041cc036a22"
"01027f2000410c6a411420024120100d22054120470440024020054100480440200120053602040c010b2001417f36"
"02040b41010c010b20012002290300370001200141196a2007290300370000200141116a2004290300370000200141"
"096a200329030037000041000b3a0000200241206a2400200041e8016a200141e884c0004103101920002d00e80141"
"0146044020002802ec012106419c8ac0004112420a10001a0c020b41eb84c000413141014100410010011a23004130"
"6b220224002002410b36020c200241286a22074200370300200241206a22044200370300200241186a220342003703"
"0020024200370310200041f0036a2201027f2000410c6a41142002410c6a4104200241106a4120100e220541204704"
"40024020054100480440200120053602040c010b2001417f3602040b41010c010b2001200229031037000120014119"
"6a2007290300370000200141116a2004290300370000200141096a200329030037000041000b3a0000200241306a24"
"00200041e8016a2001419c85c0004106101920002d00e801410146044020002802ec012106419c8ac0004112420b10"
"001a0c020b41a285c000413441014100410010011a230041306b220224002002410c36020c200241286a2207420037"
"0300200241206a22044200370300200241186a220342003703002002420037031020004194046a2201027f2000410c"
"6a41142002410c6a4104200241106a4120100f22054120470440024020054100480440200120053602040c010b2001"
"417f3602040b41010c010b20012002290310370001200141196a2007290300370000200141116a2004290300370000"
"200141096a200329030037000041000b3a0000200241306a2400200041fc016a2000411c6a280100360200200041f4"
"016a200041146a2901003702002000200029010c3702ec01200041808080e0003602e801200041d8066a2103230041"
"406a22042400024020012d0000410146044041d780c000411620012802042201ac10001a200341013a000020032001"
"3602040c010b200441206a200141196a290000370300200441186a200141116a290000370300200441106a20014109"
"6a2900003703002004200129000137030841d685c000410b200441086a22014120410110011a024002402001412041"
"001002220141004c044041d080c00041072001ac10001a0c010b418b80c000410f4284802010001a20014184802020"
"04412c6a4114100322014114460d0141af80c0004115417f20012001417f4e1b2201ac10001a0b200341013a000020"
"0320013602040c010b419a80c000411541014100410010011a20034180023b01000b200441406b240020002d00d806"
"410146044020002802dc062106419c8ac0004112420c10001a0c020b41e185c000413941014100410010011a230041"
"206b22022400200241186a22074200370300200241106a22044200370300200241086a220342003703002002420037"
"0300200041b8046a2201027f200041e8016a4118200041206a41142002412010102205412047044002402005410048"
"0440200120053602040c010b2001417f3602040b41010c010b20012002290300370001200141196a20072903003700"
"00200141116a2004290300370000200141096a200329030037000041000b3a0000200241206a2400200041d8066a20"
"01419a86c0004107101920002d00d806410146044020002802dc062106419c8ac0004112420d10001a0c020b41a186"
"c000413541014100410010011a230041306b220224002002410636020c200241286a22074200370300200241206a22"
"044200370300200241186a2203420037030020024200370310200041dc046a2201027f200041206a41142002410c6a"
"4104200241106a4120101122054120470440024020054100480440200120053602040c010b2001417f3602040b4101"
"0c010b20012002290310370001200141196a2007290300370000200141116a2004290300370000200141096a200329"
"030037000041000b3a0000200241306a2400200041d8066a200141d686c000410c101820002d00d806410146044020"
"002802dc062106419c8ac0004112420d10001a0c020b41e286c000413a41014100410010011a230041306b22022400"
"2002410d36020c200241286a22074200370300200241206a22044200370300200241186a2203420037030020024200"
"37031020004180056a2201027f2000410c6a41142002410c6a4104200241106a412010122205412047044002402005"
"4100480440200120053602040c010b2001417f3602040b41010c010b20012002290310370001200141196a20072903"
"00370000200141116a2004290300370000200141096a200329030037000041000b3a0000200241306a2400200041d8"
"066a2001419c87c0004105101920002d00d806410146044020002802dc062106419c8ac0004112420d10001a0c020b"
"41a187c000413341014100410010011a230041306b220224002002410e36020c200241286a22074200370300200241"
"206a22044200370300200241186a2203420037030020024200370310200041a4056a2201027f2000410c6a41142000"
"41206a41142002410c6a4104200241106a4120101322054120470440024020054100480440200120053602040c010b"
"2001417f3602040b41010c010b20012002290310370001200141196a2007290300370000200141116a200429030037"
"0000200141096a200329030037000041000b3a0000200241306a2400200041d8066a200141d487c000410a10192000"
"2d00d806410146044020002802dc062106419c8ac0004112420e10001a0c020b41de87c00041384101410041001001"
"1a230041306b220224002002410f36020c200241286a22074200370300200241206a22044200370300200241186a22"
"03420037030020024200370310200041c8056a2201027f2000410c6a41142002410c6a4104200241106a4120101422"
"054120470440024020054100480440200120053602040c010b2001417f3602040b41010c010b200120022903103700"
"01200141196a2007290300370000200141116a2004290300370000200141096a200329030037000041000b3a000020"
"0241306a2400200041d8066a2001419688c0004112101820002d00d806410146044020002802dc062106419c8ac000"
"4112420f10001a0c020b41a888c00041c00041014100410010011a230041206b22022400200241186a220742003703"
"00200241106a22044200370300200241086a2203420037030020024200370300200041ec056a2201027f2000410c6a"
"411420024120101522054120470440024020054100480440200120053602040c010b2001417f3602040b41010c010b"
"20012002290300370001200141196a2007290300370000200141116a2004290300370000200141096a200329030037"
"000041000b3a0000200241206a2400200041d8066a200141e888c000410a101a20002d00d806410146044020002802"
"dc062106419c8ac0004112421010001a0c020b41f288c000413841014100410010011a230041306b22022400200241"
"1236020c200241286a22074200370300200241206a22044200370300200241186a2203420037030020024200370310"
"20004190066a2201027f2000410c6a41142002410c6a4104200241106a412010162205412047044002402005410048"
"0440200120053602040c010b2001417f3602040b41010c010b20012002290310370001200141196a20072903003700"
"00200141116a2004290300370000200141096a200329030037000041000b3a0000200241306a2400200041d8066a20"
"0141aa89c0004106101920002d00d806410146044020002802dc062106419c8ac0004112421210001a0c020b410121"
"0641b089c000413441014100410010011a230041306b220224002002411336020c200241286a220742003703002002"
"41206a22044200370300200241186a2203420037030020024200370310200041b4066a2201027f2000410c6a411420"
"02410c6a4104200241106a4120101722054120470440024020054100480440200120053602040c010b2001417f3602"
"040b41010c010b20012002290310370001200141196a2007290300370000200141116a200429030037000020014109"
"6a200329030037000041000b3a0000200241306a2400200041d8066a200141e489c0004105101920002d00d8064101"
"46044020002802dc062106419c8ac0004112421310001a0c020b41e989c000413341014100410010011a0c010b2000"
"2802ec012106419c8ac0004112420810001a0b20004180076a240020060f0b418080c000410b417f20062006417f4e"
"1bac1000000bfd0401067f200241104f0440024020002000410020006b41037122056a22044f0d0020012103200504"
"40200521060340200020032d00003a0000200341016a2103200041016a2100200641016b22060d000b0b200541016b"
"4107490d000340200020032d00003a0000200041016a200341016a2d00003a0000200041026a200341026a2d00003a"
"0000200041036a200341036a2d00003a0000200041046a200341046a2d00003a0000200041056a200341056a2d0000"
"3a0000200041066a200341066a2d00003a0000200041076a200341076a2d00003a0000200341086a2103200041086a"
"22002004470d000b0b2004200220056b2207417c7122086a21000240200120056a2206410371450440200020044d0d"
"0120062101034020042001280200360200200141046a2101200441046a22042000490d000b0c010b200020044d0d00"
"2006410374220541187121032006417c71220241046a2101410020056b411871210520022802002102034020042002"
"2003762001280200220220057472360200200141046a2101200441046a22042000490d000b0b200741037121022006"
"20086a21010b02402000200020026a22064f0d002002410771220304400340200020012d00003a0000200141016a21"
"01200041016a2100200341016b22030d000b0b200241016b4107490d000340200020012d00003a0000200041016a20"
"0141016a2d00003a0000200041026a200141026a2d00003a0000200041036a200141036a2d00003a0000200041046a"
"200141046a2d00003a0000200041056a200141056a2d00003a0000200041066a200141066a2d00003a000020004107"
"6a200141076a2d00003a0000200141086a2101200041086a22002006470d000b0b0b940201017f230041406a220624"
"00024020012d0000410146044041d780c000411620012802042201ac10001a200041013a0000200020013602040c01"
"0b200641206a200141196a290000370300200641186a200141116a290000370300200641106a200141096a29000037"
"03002006200129000137030820022003200641086a22014120410110011a024002402001412041001002220141004c"
"044041d080c00041072001ac10001a0c010b418b80c000410f200510001a200120042006412c6a4114100322014114"
"460d0141af80c0004115417f20012001417f4e1b2201ac10001a0b200041013a0000200020013602040c010b419a80"
"c000411541014100410010011a20004180023b01000b200641406b24000b0bb80a0100418080c0000bae0a6572726f"
"725f636f64653d47657474696e67206669656c643a204669656c6420646174613a207265747269657665644572726f"
"722067657474696e67206669656c643a204669656c6420646174613a204572726f723a204572726f72206765747469"
"6e67206b65796c65743a202424242424205354415254494e47205741534d20455845435554494f4e20242424242441"
"63636f756e743a44657374696e6174696f6e3a4163636f756e744163636f756e74206f626a65637420657869737473"
"2c2070726f63656564696e67207769746820657363726f772066696e6973682e54727573746c696e6554727573746c"
"696e65206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973"
"682e414d4d414d4d206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f7720"
"66696e6973682e436865636b436865636b206f626a656374206578697374732c2070726f63656564696e6720776974"
"6820657363726f772066696e6973682e7465726d73616e64636f6e646974696f6e7343726564656e7469616c437265"
"64656e7469616c206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066"
"696e6973682e44656c656761746544656c6567617465206f626a656374206578697374732c2070726f63656564696e"
"67207769746820657363726f772066696e6973682e4465706f736974507265617574684465706f7369745072656175"
"7468206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e697368"
"2e444944444944206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066"
"696e6973682e457363726f77457363726f77206f626a656374206578697374732c2070726f63656564696e67207769"
"746820657363726f772066696e6973682e4d505449737375616e63654d505449737375616e6365206f626a65637420"
"6578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e4d50546f6b656e4d50"
"546f6b656e206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e"
"6973682e4e46546f6b656e4f666665724e46546f6b656e4f66666572206f626a656374206578697374732c2070726f"
"63656564696e67207769746820657363726f772066696e6973682e4f666665724f66666572206f626a656374206578"
"697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e5061794368616e6e656c50"
"61794368616e6e656c206f626a656374206578697374732c2070726f63656564696e67207769746820657363726f77"
"2066696e6973682e5065726d697373696f6e6564446f6d61696e5065726d697373696f6e6564446f6d61696e206f62"
"6a656374206578697374732c2070726f63656564696e67207769746820657363726f772066696e6973682e5369676e"
"65724c6973745369676e65724c697374206f626a656374206578697374732c2070726f63656564696e672077697468"
"20657363726f772066696e6973682e5469636b65745469636b6574206f626a656374206578697374732c2070726f63"
"656564696e67207769746820657363726f772066696e6973682e5661756c745661756c74206f626a65637420657869"
"7374732c2070726f63656564696e67207769746820657363726f772066696e6973682e43757272656e742073657120"
"76616c75653a004d0970726f64756365727302086c616e6775616765010452757374000c70726f6365737365642d62"
"79010572757374631d312e38372e30202831373036376539616320323032352d30352d303929002c0f746172676574"
"5f6665617475726573022b0f6d757461626c652d676c6f62616c732b087369676e2d657874";
extern std::string const kCodecovTestsWasmHex =
"0061736d0100000001570b60067f7f7f7f7f7f017f60047f7f7f7f017f60027f7f017f60037f7f7f017f60077f7f7f"
"7f7f7f7f017f60057f7f7f7f7f017f60087f7f7f7f7f7f7f7f017f60017f017f60037f7f7e017f60047f7f7f7f0060"
"00017f02c60a3b08686f73745f6c6962057472616365000508686f73745f6c69620974726163655f6e756d00080868"
"6f73745f6c69620a6c6467725f696e646578000208686f73745f6c696210706172656e745f6c6467725f74696d6500"
"0208686f73745f6c696210706172656e745f6c6467725f68617368000208686f73745f6c696208626173655f666565"
"000208686f73745f6c696211616d656e646d656e745f656e61626c6564000208686f73745f6c69620874785f666965"
"6c64000308686f73745f6c69620e6163636f756e74726f6f745f6964000108686f73745f6c69620863616368655f6c"
"65000308686f73745f6c69620d686f6d655f6c655f6669656c64000308686f73745f6c6962086c655f6669656c6400"
"0108686f73745f6c69620874785f696e6e6572000108686f73745f6c69620d686f6d655f6c655f696e6e6572000108"
"686f73745f6c6962086c655f696e6e6572000508686f73745f6c69620a74785f6172725f6c656e000708686f73745f"
"6c69620f686f6d655f6c655f6172725f6c656e000708686f73745f6c69620a6c655f6172725f6c656e000208686f73"
"745f6c69621074785f696e6e65725f6172725f6c656e000208686f73745f6c696215686f6d655f6c655f696e6e6572"
"5f6172725f6c656e000208686f73745f6c6962106c655f696e6e65725f6172725f6c656e000308686f73745f6c6962"
"087365745f64617461000208686f73745f6c69620b7368613531325f68616c66000108686f73745f6c696209636865"
"636b5f736967000008686f73745f6c6962076e66745f757269000008686f73745f6c69620a6e66745f697373756572"
"000108686f73745f6c6962096e66745f7461786f6e000108686f73745f6c6962096e66745f666c616773000208686f"
"73745f6c69620c6e66745f786665725f666565000208686f73745f6c69620a6e66745f73657269616c000108686f73"
"745f6c69620a74726163655f61636374000108686f73745f6c69620974726163655f616d74000108686f73745f6c69"
"6208636865636b5f6964000008686f73745f6c69620f666c6f61745f66726f6d5f75696e74000508686f73745f6c69"
"620c74727573746c696e655f6964000608686f73745f6c696206616d6d5f6964000008686f73745f6c69620d637265"
"64656e7469616c5f6964000608686f73745f6c69620a6d70746f6b656e5f6964000008686f73745f6c69620c747261"
"63655f78666c6f6174000108686f73745f6c696209666c6f61745f636d70000108686f73745f6c696209666c6f6174"
"5f616464000408686f73745f6c696209666c6f61745f737562000408686f73745f6c69620a666c6f61745f6d756c74"
"000408686f73745f6c696209666c6f61745f646976000408686f73745f6c69620a666c6f61745f726f6f7400000868"
"6f73745f6c696209666c6f61745f706f77000008686f73745f6c696209657363726f775f6964000008686f73745f6c"
"69620f6d70745f69737375616e63655f6964000008686f73745f6c69620c6e66745f6f666665725f6964000008686f"
"73745f6c6962086f666665725f6964000008686f73745f6c6962096f7261636c655f6964000008686f73745f6c6962"
"0a7061796368616e5f6964000608686f73745f6c6962167065726d697373696f6e65645f646f6d61696e5f69640000"
"08686f73745f6c6962097469636b65745f6964000008686f73745f6c6962087661756c745f6964000008686f73745f"
"6c69620b64656c65676174655f6964000008686f73745f6c6962126465706f7369745f707265617574685f69640000"
"08686f73745f6c6962066469645f6964000108686f73745f6c69620a7369676e6572735f69640001030302090a0503"
"0100110619037f01418080c0000b7f0041cf9bc0000b7f0041d09bc0000b073504066d656d6f727902000d65736372"
"6f775f66696e697368003c0a5f5f646174615f656e6403010b5f5f686561705f6261736503020a8c2f024600024020"
"0020014704402002200341014100410010001a20004100480d01418b80c000410b2000ad1001000b200220032000ac"
"10011a0f0b418b80c000410b2000ac1001000bc22e020b7f017e23004190026b22002400419680c000412341014100"
"410010001a20004100360260200041e0006a220241041002410441888ac000410a103b200041003602602002410410"
"03410441928ac0004110103b200041f8006a22054200370300200041f0006a22014200370300200041e8006a220642"
"0037030020004200370360200241201004412041a28ac0004110103b20004100360260200241041005410441b28ac0"
"004108103b200041106a2208428182848890a0c08001370300200041186a2209428182848890a0c080013703002000"
"41206a220a428182848890a0c080013703002000428182848890a0c0800137030841b980c000410e1006410141c780"
"c0004111103b200041086a41201006410141c780c0004111103b418180202002411410072203411446044002402000"
"412e6a200041e2006a2d00003a0000200020002900673703e8012000200041ec006a2900003700ed01200020002f00"
"603b012c200020002903e8013703a801200020002900ed013700ad012000200028006336002f200041386a20002900"
"ad01370000200020002903a80137003320054200370300200142003703002006420037030020004200370360200041"
"2c6a2205411420024120100822034120470d00200041c2006a20002d00623a0000200041f0016a2207200041ef006a"
"290000220b370300200041cf006a200b370000200041d7006a200041f7006a290000370000200041df006a200041ff"
"006a2d00003a0000200020002f01603b01402000200028006336004320002000290067370047200041406b41204100"
"1009410141d880c0004108103b2001410036020020064200370300200042003703604181802020024114100a411441"
"ba8ac000410d103b20014100360200200642003703002000420037036041014181802020024114100b411441c78ac0"
"004108103b02404100200041e4006a22046b410371220320046a220120044d0d002003044020032106034020044100"
"3a0000200441016a2104200641016b22060d000b0b200341016b4107490d000340200441003a0000200441076a4100"
"3a0000200441066a41003a0000200441056a41003a0000200441046a41003a0000200441036a41003a000020044102"
"6a41003a0000200441016a41003a0000200441086a22042001470d000b0b2001413c20036b2203417c716a22042001"
"4b0440034020014100360200200141046a22012004490d000b0b024020042003410371220320046a22064f0d002003"
"220104400340200441003a0000200441016a2104200141016b22010d000b0b200341016b4107490d00034020044100"
"3a0000200441076a41003a0000200441066a41003a0000200441056a41003a0000200441046a41003a000020044103"
"6a41003a0000200441026a41003a0000200441016a41003a0000200441086a22042006470d000b0b200041043602a0"
"01200041818020360260200041f8016a2203410036020020074200370300200042003703e80120024104200041e801"
"6a22014114100c411441cf8ac0004108103b2003410036020020074200370300200042003703e801200220002802a0"
"0120014114100d411441d78ac000410d103b2003410036020020074200370300200042003703e80141012002200028"
"02a00120014114100e411441e48ac0004108103b4189803c100f412041e080c000410a103b4189803c1010412041ea"
"80c000410f103b41014189803c1011412041f980c000410a103b200220002802a00110124120418381c0004110103b"
"200220002802a00110134120419381c0004115103b4101200220002802a0011014412041a881c0004110103b200541"
"141015411441b881c0004108103b20004180026a220642003703002003420037030020074200370300200042003703"
"e801200220002802a001200141201016412041ec8ac000410b103b41c081c000410c41cc81c000410b41d781c00041"
"0e1017410141e581c0004109103b200041c0016a200a290300370300200041b8016a2009290300370300200041b001"
"6a2008290300370300200020002903083703a801200341003b010020074200370300200042003703e8012005411420"
"0041a8016a22044120200141121018411241f78ac0004107103b2003410036020020074200370300200042003703e8"
"0120044120200141141019411441fe8ac000410a103b200041003602e8012004412020014104101a410441888bc000"
"4109103b20044120101b410841ee81c0004109103b20044120101c410a41f781c000410c103b200041003602e80120"
"04412020014104101d410441918bc000410a103b418382c000410d20054114101e4100419082c000410a103b418382"
"c000410d419a82c0004108101f410041a282c0004109103b418382c000410d41ab82c0004108101f410041b382c000"
"410e103b417f41041004417141c182c0004118103b200041003602e8012001417f10044171419b8bc0004118103b20"
"0041ea016a41003a0000200041003b01e801200141031004417d41b38bc000411e103b200041003602e80120014180"
"94ebdc031004417341d18bc000411d103b4102100f416f41d982c0004119103b417f20002802a0011012417141f282"
"c0004118103b2002417f10124171418a83c0004118103b20024181081012417441a283c0004119103b200041e094eb"
"dc036a220820002802a0011012417341bb83c0004118103b2006420037030020034200370300200742003703002000"
"42003703e8012005411420084108200141201020417341ee8bc0004114103b20064200370300200342003703002007"
"4200370300200042003703e8012005411420054114200141201020417141828cc0004116103b200642003703002003"
"420037030020074200370300200042003703e801200841082001412041001021417341988cc0004117103b20064200"
"3703002003420037030020074200370300200042003703e801200220002802a0012001412041001021417141af8cc0"
"004120103b200820002802a00141011009417341d383c0004110103b200220002802a00141011009417141e383c000"
"4112103b200642003703002003420037030020074200370300200042003703e801200820002802a001200141201008"
"417341cf8cc0004116103b200642003703002003420037030020074200370300200042003703e801200220002802a0"
"01200141201008417141e58cc0004118103b200642003703002003420037030020074200370300200042003703e801"
"2005411420054114200820002802a001200141201022417341fd8cc000411d103b2006420037030020034200370300"
"20074200370300200042003703e8012005411420054114200220002802a0012001412010224171419a8dc000411f10"
"3b200642003703002003420037030020074200370300200042003703e80141bb9bc0004114200820002802a0012001"
"41201023417341b98dc0004115103b200642003703002003420037030020074200370300200042003703e80141bb9b"
"c0004114200220002802a001200141201023417141ce8dc000411b103b200642003703002003420037030020074200"
"370300200042003703e80141bb9bc000411441f583c0004114200141201023417141e98dc0004125103b2006420037"
"03002003420037030020074200370300200042003703e801418984c000412841bb9bc0004114200141201023417141"
"8e8ec0004121103b200041dc016a2000413c6a280100360200200041d4016a200041346a2901003702002000200029"
"012c3702cc01200041808080083602c801200041003b01e801200041c8016a2209411841bb9bc00041142001410210"
"23417141af8ec000410a103b200820002802a001422a1001417341b184c0004111103b200041003b01e80141022001"
"41021007416f41b98ec0004117103b200041003b01e801410220014102100a416f41d08ec000411c103b200041003b"
"01e8014101410220014102100b416f41ec8ec0004117103b4102100f416f41d982c0004119103b41021010416f41c2"
"84c000411e103b410141021011416f41e084c0004119103b41b980c0004181081006417441f984c000411f103b41b9"
"80c00041c10010064174419885c000411a103b200041003b01e801200241810820014102100c417441838fc0004116"
"103b200041003b01e801200241810820014102100d417441998fc000411b103b200041003b01e80141012002418108"
"20014102100e417441b48fc0004116103b20024181081012417441b285c000411e103b20024181081013417441d085"
"c0004123103b410120024181081014417441f385c000411e103b200241812010154174419186c0004116103b418382"
"c00041810841cc81c000410b41d781c000410e1017417441e581c0004109103b418382c000410d41cc81c000418108"
"41d781c000410e1017417441e581c0004109103b418382c000410d41cc81c000410b41d781c0004181081017417441"
"e581c0004109103b200041003b01e8012002418108200141021016417441ca8fc0004119103b200041003b01e80141"
"bb9bc00041810841bb9bc0004114200141021023417441e38fc0004114103b200041003b01e8012005411420054114"
"2002418108200141021024417441f78fc000411b103b200041003b01e8012009418108200541142001410210254174"
"419290c000411e103b418382c000410d200820002802a00141001000417341a786c000410f103b200042d487b6f4c7"
"d4b1c0003700e001418382c000410d200041e095ebdc036a220441081026417341b686c0004116103b418382c00041"
"0d200820002802a001101f417341cc86c0004113103b20044108200041e0016a220841081027417341df86c0004114"
"103b20084108200441081027417341f386c0004114103b200041003b01e80120044108200841082001410241001028"
"417341b090c0004114103b200041003b01e80120084108200441082001410241001028417341c490c0004114103b20"
"0041003b01e80120044108200841082001410241001029417341d890c0004114103b200041003b01e8012008410820"
"0441082001410241001029417341ec90c0004114103b200041003b01e8012004410820084108200141024100102a41"
"73418091c0004115103b200041003b01e8012008410820044108200141024100102a4173419591c0004115103b2000"
"41003b01e8012004410820084108200141024100102b417341aa91c0004114103b200041003b01e801200841082004"
"4108200141024100102b417341be91c0004114103b200041003b01e801200441084103200141024100102c417341d2"
"91c0004114103b200041003b01e801200441084103200141024100102d417341e691c0004113103b20064200370300"
"2003420037030020074200370300200042003703e801200541142005411420014120102e417141f991c000411b103b"
"200642003703002003420037030020074200370300200042003703e801200541142005411420014120102f41714194"
"92c0004121103b200642003703002003420037030020074200370300200042003703e8012005411420054114200141"
"201030417141b592c000411e103b200642003703002003420037030020074200370300200042003703e80120054114"
"20054114200141201031417141d392c000411a103b2006420037030020034200370300200742003703002000420037"
"03e8012005411420054114200141201032417141ed92c000411b103b20064200370300200342003703002007420037"
"0300200042003703e8012005411420054114200541142001412010334171418893c000411c103b2006420037030020"
"03420037030020074200370300200042003703e8012005411420054114200141201034417141a493c0004128103b20"
"0642003703002003420037030020074200370300200042003703e8012005411420054114200141201035417141cc93"
"c000411b103b200642003703002003420037030020074200370300200042003703e801200541142005411420014120"
"1036417141e793c000411a103b200220002802a001410010094171418787c000411b103b200041003b01e801200541"
"14200220002802a0012001410210184171418194c000411a103b200041003b01e801200220002802a0012001410210"
"194171419b94c000411d103b200041003b01e801200220002802a00120014102101a417141b894c000411c103b2002"
"20002802a001101b417141a287c000411c103b200220002802a001101c417141be87c000411f103b200041003602e8"
"01200220002802a00120014104101d417141d494c000411d103b200041003b01e801200220002802a0012001410210"
"08417141f194c0004124103b200041808080083602e801200041003b018e02200220002802a001200141042000418e"
"026a2203410210204171419595c000411e103b200041003b018e02200220002802a001220620054114200220062003"
"41021024417141b395c0004124103b200041003b018e0220054114200220002802a001220620022006200341021024"
"417141d795c0004124103b200041003b018e02200220002802a00120054114200341021037417141fb95c000412210"
"3b200041003b018e0220054114200220002802a0012003410210374171419d96c0004122103b200041003b018e0220"
"0220002802a00120054114200341021038417141bf96c0004129103b200041003b018e0220054114200220002802a0"
"01200341021038417141e896c0004129103b200041003b018e02200220002802a0012003410210394171419197c000"
"411c103b200041003b018e02200220002802a0012001410420034102102e417141ad97c000411f103b200041003b01"
"8e02200220002802a0012005411441f583c0004114200341021022417141cc97c0004123103b200041003b018e0220"
"054114200220002802a00141f583c0004114200341021022417141ef97c0004123103b200041003b018e0220022000"
"2802a0012001410420034102102f4171419298c0004125103b200041003b018e0220094118200220002802a0012003"
"41021025417141b798c0004120103b200041003b018e02200220002802a00120014104200341021030417141d798c0"
"004122103b200041003b018e02200220002802a00120014104200341021031417141f998c000411e103b200041003b"
"018e02200220002802a001200141042003410210324171419799c000411f103b200041003b018e02200220002802a0"
"012005411420014104200341021033417141b699c0004121103b200041003b018e0220054114200220002802a00120"
"014104200341021033417141d799c0004121103b200041003b018e02200220002802a0012001410420034102103441"
"7141f899c000412c103b200041003b018e02200220002802a00120034102103a417141a49ac0004120103b20004100"
"3b018e02200220002802a00120014104200341021035417141c49ac000411f103b200041003b018e02200220002802"
"a00120014104200341021036417141e39ac000411e103b200041003b018e02200220002802a00141dd87c000412020"
"0341021018417141819bc000411d103b418382c000410d200220002802a001101e417141fd87c0004120103b418396"
"abdd03410d41dd87c0004120410010004173419d88c0004110103b418396abdd03410d200841081026417341ad88c0"
"004117103b418396abdd03410d20054114101e417341c488c0004115103b418396abdd03410d41ab82c0004108101f"
"417341d988c0004114103b200220002802a001200241810841001000417441ed88c000410e103b2002418108420110"
"01417441fb88c0004112103b418382c0004181082008410810264174418d89c0004115103b418382c0004181082005"
"4114101e417441a289c0004113103b418382c00041810841ab82c0004108101f417441b589c0004112103b418382c0"
"00410d200220002802a001101f417141c789c0004116103b200041003b018e02200220002802a00120054114200341"
"0210254171419e9bc000411d103b418382c000410d200220002802a00141021000417141dd89c0004114103b410141"
"0020054114101e410041f189c0004117103b20004190026a240041010f0b0b418080c000410b417f20032003417f4e"
"1bac1001000b0ba61b0200418080c0000b89046572726f725f636f64653d54455354204641494c4544242424242420"
"5354415254494e47205741534d20455845435554494f4e202424242424746573745f616d656e646d656e74616d656e"
"646d656e745f656e61626c656463616368655f6c6574785f6172725f6c656e686f6d655f6c655f6172725f6c656e6c"
"655f6172725f6c656e74785f696e6e65725f6172725f6c656e686f6d655f6c655f696e6e65725f6172725f6c656e6c"
"655f696e6e65725f6172725f6c656e7365745f6461746174657374206d65737361676574657374207075626b657974"
"657374207369676e6174757265636865636b5f7369676e66745f666c6167736e66745f786665725f66656574657374"
"696e6720747261636574726163655f61636374400000000000005f74726163655f616d744000000000000000747261"
"63655f616d745f7a65726f706172656e745f6c6467725f686173685f6e65675f70747274785f6172725f6c656e5f69"
"6e76616c69645f736669656c6474785f696e6e65725f6172725f6c656e5f6e65675f70747274785f696e6e65725f61"
"72725f6c656e5f6e65675f6c656e74785f696e6e65725f6172725f6c656e5f746f6f5f6c6f6e6774785f696e6e6572"
"5f6172725f6c656e5f7074725f6f6f6263616368655f6c655f7074725f6f6f6263616368655f6c655f77726f6e675f"
"6c656e55534430303030303030303030303030303030300041b184c0000b8a1774726163655f6e756d5f6f6f625f73"
"7472686f6d655f6c655f6172725f6c656e5f696e76616c69645f736669656c646c655f6172725f6c656e5f696e7661"
"6c69645f736669656c64616d656e646d656e745f656e61626c65645f746f6f5f6269675f736c696365616d656e646d"
"656e745f656e61626c65645f746f6f5f6c6f6e6774785f696e6e65725f6172725f6c656e5f746f6f5f6269675f736c"
"696365686f6d655f6c655f696e6e65725f6172725f6c656e5f746f6f5f6269675f736c6963656c655f696e6e65725f"
"6172725f6c656e5f746f6f5f6269675f736c6963657365745f646174615f746f6f5f6269675f736c69636574726163"
"655f6f6f625f736c69636574726163655f78666c6f61745f6f6f625f736c69636574726163655f616d745f6f6f625f"
"736c696365666c6f61745f636d705f6f6f625f736c69636531666c6f61745f636d705f6f6f625f736c696365326361"
"6368655f6c655f77726f6e675f73697a655f75696e743235366e66745f666c6167735f77726f6e675f73697a655f75"
"696e743235366e66745f786665725f6665655f77726f6e675f73697a655f75696e7432353630303030303030303030"
"3030303030303030303030303030303030303030303174726163655f616363745f77726f6e675f73697a655f616363"
"6f756e745f696474726163655f6f6f625f737472696e6774726163655f78666c6f61745f6f6f625f737472696e6774"
"726163655f616363745f6f6f625f737472696e6774726163655f616d745f6f6f625f737472696e6774726163655f74"
"6f6f5f6c6f6e6774726163655f6e756d5f746f6f5f6c6f6e6774726163655f78666c6f61745f746f6f5f6c6f6e6774"
"726163655f616363745f746f6f5f6c6f6e6774726163655f616d745f746f6f5f6c6f6e6774726163655f616d745f77"
"726f6e675f6c656e67746874726163655f696e76616c69645f61735f68657874726163655f616363745f636865636b"
"5f646573796e636c6467725f696e646578706172656e745f6c6467725f74696d65706172656e745f6c6467725f6861"
"7368626173655f666565686f6d655f6c655f6669656c646c655f6669656c6474785f696e6e6572686f6d655f6c655f"
"696e6e65726c655f696e6e65727368613531325f68616c666e66745f7572696e66745f6973737565726e66745f7461"
"786f6e6e66745f73657269616c706172656e745f6c6467725f686173685f6e65675f6c656e706172656e745f6c6467"
"725f686173685f6275665f746f6f5f736d616c6c706172656e745f6c6467725f686173685f6c656e5f746f6f5f6c6f"
"6e67636865636b5f69645f6f6f625f6c656e5f753332636865636b5f69645f77726f6e675f6c656e5f753332666c6f"
"61745f66726f6d5f75696e745f6c656e5f6f6f62666c6f61745f66726f6d5f75696e745f77726f6e675f6c656e5f75"
"696e7436346163636f756e74726f6f745f69645f6c656e5f6f6f626163636f756e74726f6f745f69645f77726f6e67"
"5f6c656e74727573746c696e655f69645f6c656e5f6f6f625f63757272656e637974727573746c696e655f69645f77"
"726f6e675f6c656e5f63757272656e6379616d6d5f69645f6c656e5f6f6f625f617373657432616d6d5f69645f6c65"
"6e5f77726f6e675f6c656e5f617373657432616d6d5f69645f6c656e5f77726f6e675f6e6f6e5f7872705f63757272"
"656e63795f6c656e616d6d5f69645f6c656e5f77726f6e675f7872705f63757272656e63795f6c656e616d6d5f6964"
"5f6d707474785f6669656c645f696e76616c69645f736669656c64686f6d655f6c655f6669656c645f696e76616c69"
"645f736669656c646c655f6669656c645f696e76616c69645f736669656c6474785f696e6e65725f746f6f5f626967"
"5f736c696365686f6d655f6c655f696e6e65725f746f6f5f6269675f736c6963656c655f696e6e65725f746f6f5f62"
"69675f736c6963657368613531325f68616c665f746f6f5f6269675f736c696365616d6d5f69645f746f6f5f626967"
"5f736c69636563726564656e7469616c5f69645f746f6f5f6269675f736c6963656d70746f6b656e5f69645f746f6f"
"5f6269675f736c6963655f6d70746964666c6f61745f6164645f6f6f625f736c69636531666c6f61745f6164645f6f"
"6f625f736c69636532666c6f61745f7375625f6f6f625f736c69636531666c6f61745f7375625f6f6f625f736c6963"
"6532666c6f61745f6d756c745f6f6f625f736c69636531666c6f61745f6d756c745f6f6f625f736c69636532666c6f"
"61745f6469765f6f6f625f736c69636531666c6f61745f6469765f6f6f625f736c69636532666c6f61745f726f6f74"
"5f6f6f625f736c696365666c6f61745f706f775f6f6f625f736c696365657363726f775f69645f77726f6e675f7369"
"7a655f75696e7433326d70745f69737375616e63655f69645f77726f6e675f73697a655f75696e7433326e66745f6f"
"666665725f69645f77726f6e675f73697a655f75696e7433326f666665725f69645f77726f6e675f73697a655f7569"
"6e7433326f7261636c655f69645f77726f6e675f73697a655f75696e7433327061796368616e5f69645f77726f6e67"
"5f73697a655f75696e7433327065726d697373696f6e65645f646f6d61696e5f69645f77726f6e675f73697a655f75"
"696e7433327469636b65745f69645f77726f6e675f73697a655f75696e7433327661756c745f69645f77726f6e675f"
"73697a655f75696e7433326e66745f7572695f77726f6e675f73697a655f75696e743235366e66745f697373756572"
"5f77726f6e675f73697a655f75696e743235366e66745f7461786f6e5f77726f6e675f73697a655f75696e74323536"
"6e66745f73657269616c5f77726f6e675f73697a655f75696e743235366163636f756e74726f6f745f69645f77726f"
"6e675f73697a655f6163636f756e745f6964636865636b5f69645f77726f6e675f73697a655f6163636f756e745f69"
"6463726564656e7469616c5f69645f77726f6e675f73697a655f6163636f756e745f69643163726564656e7469616c"
"5f69645f77726f6e675f73697a655f6163636f756e745f69643264656c65676174655f69645f77726f6e675f73697a"
"655f6163636f756e745f69643164656c65676174655f69645f77726f6e675f73697a655f6163636f756e745f696432"
"6465706f7369745f707265617574685f69645f77726f6e675f73697a655f6163636f756e745f6964316465706f7369"
"745f707265617574685f69645f77726f6e675f73697a655f6163636f756e745f6964326469645f69645f77726f6e67"
"5f73697a655f6163636f756e745f6964657363726f775f69645f77726f6e675f73697a655f6163636f756e745f6964"
"74727573746c696e655f69645f77726f6e675f73697a655f6163636f756e745f69643174727573746c696e655f6964"
"5f77726f6e675f73697a655f6163636f756e745f6964326d70745f69737375616e63655f69645f77726f6e675f7369"
"7a655f6163636f756e745f69646d70746f6b656e5f69645f77726f6e675f73697a655f6163636f756e745f69646e66"
"745f6f666665725f69645f77726f6e675f73697a655f6163636f756e745f69646f666665725f69645f77726f6e675f"
"73697a655f6163636f756e745f69646f7261636c655f69645f77726f6e675f73697a655f6163636f756e745f696470"
"61796368616e5f69645f77726f6e675f73697a655f6163636f756e745f6964317061796368616e5f69645f77726f6e"
"675f73697a655f6163636f756e745f6964327065726d697373696f6e65645f646f6d61696e5f69645f77726f6e675f"
"73697a655f6163636f756e745f69647369676e6572735f69645f77726f6e675f73697a655f6163636f756e745f6964"
"7469636b65745f69645f77726f6e675f73697a655f6163636f756e745f69647661756c745f69645f77726f6e675f73"
"697a655f6163636f756e745f69646e66745f7572695f77726f6e675f73697a655f6163636f756e745f69646d70746f"
"6b656e5f69645f6d707469645f77726f6e675f6c656e677468004d0970726f64756365727302086c616e6775616765"
"010452757374000c70726f6365737365642d6279010572757374631d312e38372e3020283137303637653961632032"
"3032352d30352d303929002c0f7461726765745f6665617475726573022b0f6d757461626c652d676c6f62616c732b"
"087369676e2d657874";
extern std::string const kBadAlignWasmHex =
"0061736d01000000011b046000017f60057f7f7f7f7f017f60067f7f7f7f7f7f017f60000002260203656e760f666c"
"6f61745f66726f6d5f75696e74000103656e7608636865636b5f6964000203050403000000050301000306470b7f00"
"4180080b7f00418088020b7f004180080b7f00418088040b7f00418088040b7f00418088080b7f004180080b7f0041"
"8088080b7f004180800c0b7f0041000b7f0041010b07cc0110066d656d6f72790200115f5f7761736d5f63616c6c5f"
"63746f72730002057465737431000307655f64617461310300057465737432000407655f6461746132030104746573"
"7400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f737461636b5f6c6f7703040c5f"
"5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f6261736503070a5f5f68"
"6561705f656e6403080d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a99020402000b"
"2801017f418108427f370000418108410841a308410c41001000220041a40828020020004100481b0b5f01017f419a"
"88024191a4cca00136010041928802428994ace0d0c1c38710370100418a88024281848ca0d0c0c183083701004181"
"8802417f360000418a8802411441818802410441a3880241201001220041a4880228020020004100481b0b8a010103"
"7f418108427f370000418108410841a308410c410010002100419a88024191a4cca00136010041928802428994ace0"
"d0c1c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a4082802002101418a880241"
"1441818802410441a3880241201001220241a4880228020020024100481b2000200120004100481b6a0b007f097072"
"6f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e352d776173692d73646b202868"
"747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235613264623538"
"32393538616631656533303861373930636664623432626432343732302900490f7461726765745f66656174757265"
"73042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b"
"0a6d756c746976616c7565";

View File

@@ -1,12 +0,0 @@
#pragma once
// TODO: consider moving these to separate files (and figure out the build)
#include <string>
extern std::string const kLedgerSqnWasmHex;
extern std::string const kAllHostFunctionsWasmHex;
extern std::string const kAllKeyletsWasmHex;
extern std::string const kCodecovTestsWasmHex;
extern std::string const kBadAlignWasmHex;

View File

@@ -1,14 +0,0 @@
#include <stdint.h>
int32_t ldgr_index(uint8_t *, int32_t);
int escrow_finish()
{
uint32_t sqn;
int32_t result = ldgr_index((uint8_t *)&sqn, sizeof(sqn));
if (result < 0)
return result;
return sqn >= 5 ? 5 : 0;
}

View File

@@ -1,27 +0,0 @@
#include <xrpl/beast/unit_test/suite.h>
#include <rs_hello_world_cxxbridge/lib.h>
namespace xrpl {
class RustInterop_test : public beast::unit_test::Suite
{
public:
void
testHelloWorld()
{
testcase("hello_world");
auto result = rs::hello_world::hello_world();
BEAST_EXPECT(result == "hello_world");
}
void
run() override
{
testHelloWorld();
}
};
BEAST_DEFINE_TESTSUITE(RustInterop, basics, xrpl);
} // namespace xrpl