mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 06:10:58 +00:00
Clean up
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
7
crates/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "rs-hello_world"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
cxx.workspace = true
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()`.
|
||||
@@ -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.
|
||||
@@ -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)).
|
||||
@@ -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.
|
||||
@@ -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"`).
|
||||
@@ -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).
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
126
include/xrpl/nodestore/detail/Varint.h
Normal file
126
include/xrpl/nodestore/detail/Varint.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
@@ -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
|
||||
@@ -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
|
||||
*/
|
||||
3
src/test/app/wasm_fixtures/.gitignore
vendored
3
src/test/app/wasm_fixtures/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
**/target
|
||||
**/debug
|
||||
*.wasm
|
||||
171
src/test/app/wasm_fixtures/all_host_functions/Cargo.lock
generated
171
src/test/app/wasm_fixtures/all_host_functions/Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
@@ -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:", ¤t_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:",
|
||||
¤t_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
|
||||
}
|
||||
171
src/test/app/wasm_fixtures/all_keylets/Cargo.lock
generated
171
src/test/app/wasm_fixtures/all_keylets/Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
@@ -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"
|
||||
@@ -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, ¤cy);
|
||||
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.
|
||||
}
|
||||
@@ -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(); }
|
||||
171
src/test/app/wasm_fixtures/codecov_tests/Cargo.lock
generated
171
src/test/app/wasm_fixtures/codecov_tests/Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
@@ -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" }
|
||||
@@ -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
@@ -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.")
|
||||
@@ -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";
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user