Merge pull request #8114 from XRPLF/timothybanks/port-wat-based-tests

feat: Port wat based wasm tests to new wasm design
This commit is contained in:
Timothy Banks
2026-09-08 09:31:01 -04:00
committed by GitHub
249 changed files with 4858 additions and 5357 deletions

View File

@@ -7,7 +7,6 @@ ignorePaths:
- cmake/**
- LICENSE.md
- .clang-tidy
- src/test/app/wasm_fixtures/*.c
- nix/check-tools/*.txt # generated, and full of Nix store hashes
language: en
allowCompoundWords: true # TODO (#6334)

View File

@@ -1,6 +1,9 @@
benchmarks.libxrpl > xrpl.basics
benchmarks.libxrpl > xrpl.config
benchmarks.libxrpl > xrpl.nodestore
benchmarks.libxrpl > xrpl.protocol
benchmarks.libxrpl > xrpl.protocol_autogen
benchmarks.libxrpl > xrpl.tx
libxrpl.basics > xrpl.basics
libxrpl.conditions > xrpl.basics
libxrpl.conditions > xrpl.conditions

View File

@@ -29,6 +29,14 @@ function(xrpl_add_benchmark name)
# XrplCore.cmake. Each file compiles fine on its own.
set_target_properties(${target} PROPERTIES UNITY_BUILD OFF)
# Land next to `xrpl_tests` in the build root rather than buried under
# `src/benchmarks/libxrpl/`. A benchmark is something a person runs by hand,
# repeatedly, and comparing two of them should not mean typing two long paths.
set_target_properties(
${target}
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
)
isolate_headers(
${target}
"${CMAKE_SOURCE_DIR}/src"

1
crates/Cargo.lock generated
View File

@@ -476,6 +476,7 @@ version = "0.1.0"
dependencies = [
"cxx",
"wat",
"xrpl-host-functions",
]
[[package]]

View File

@@ -9,3 +9,4 @@ crate-type = ["staticlib", "rlib"]
[dependencies]
cxx.workspace = true
wat = "1"
xrpl-host-functions = { path = "../xrpl-host-functions" }

View File

@@ -19,6 +19,18 @@ mod ffi {
/// Throws `rust::Error` on invalid input, which is what a test wants: a typo in a
/// fixture should fail the test that holds it, at the line that holds it.
fn compile_wat(wat: &str) -> Result<Vec<u8>>;
/// The gas a host function is charged before it runs, by its guest import name.
///
/// For the C++ gas benchmarks, which measure what a host call actually costs and
/// report it against what the table says it costs. Reading the declaration through
/// here rather than copying the numbers into C++ is the point: 61 transcribed
/// constants would drift from `lib.rs` the first time a price changed, and drift
/// silently, because a benchmark has nothing to fail.
///
/// Throws `rust::Error` on an unknown name — a typo should fail loudly rather than
/// quietly compare against zero.
fn host_function_gas(wasm_name: &str) -> Result<u64>;
}
}
@@ -26,9 +38,28 @@ fn compile_wat(wat: &str) -> Result<Vec<u8>, wat::Error> {
wat::parse_str(wat)
}
fn host_function_gas(wasm_name: &str) -> Result<u64, UnknownHostFunction> {
xrpl_host_functions::HostFunctionSpec::ALL
.iter()
.find(|op| op.wasm_name() == wasm_name)
.map(|op| op.gas())
.ok_or_else(|| UnknownHostFunction(wasm_name.to_owned()))
}
#[derive(Debug)]
struct UnknownHostFunction(String);
impl std::fmt::Display for UnknownHostFunction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "no host function is imported as `{}`", self.0)
}
}
impl std::error::Error for UnknownHostFunction {}
#[cfg(test)]
mod tests {
use super::compile_wat;
use super::*;
#[test]
fn a_module_assembles_to_something_beginning_with_the_wasm_magic() {
@@ -37,6 +68,33 @@ mod tests {
assert_eq!(&wasm[..4], b"\0asm");
}
#[test]
fn a_host_function_reports_the_gas_its_declaration_gives_it() {
// `trace` is the cheapest declaration in the table; the point is not the number but
// that the lookup reaches the same constant the engine charges from.
assert_eq!(
host_function_gas("trace").expect("trace is a host function"),
xrpl_host_functions::HostFunctionSpec::Trace.gas()
);
}
#[test]
fn every_host_function_is_reachable_by_its_import_name() {
for op in xrpl_host_functions::HostFunctionSpec::ALL {
assert_eq!(
host_function_gas(op.wasm_name()).expect("declared"),
op.gas(),
"{} must be reachable by name",
op.wasm_name()
);
}
}
#[test]
fn an_unknown_name_is_an_error_rather_than_zero_gas() {
host_function_gas("not_a_host_function").expect_err("must not resolve");
}
#[test]
fn a_typo_is_an_error_rather_than_a_module() {
let error = compile_wat("(module (func (export").expect_err("must not assemble");

View File

@@ -1247,3 +1247,142 @@ fn the_outcome_carries_whatever_the_guest_returned() {
assert_eq!(outcome.result, value);
}
}
/// The remaining binary operators marshal like `float_add`: both operands and the mode reach
/// the host, tagged by operator.
#[test]
fn float_sub_reads_both_operands_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_SUB, ONE_PAGE],
"(call $float_sub (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_binary_ops_asked.borrow(),
vec![("sub", vec![0u8; 8], vec![0u8; 8], 2)]
);
}
#[test]
fn float_mult_reads_both_operands_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_MULT, ONE_PAGE],
"(call $float_mult (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_binary_ops_asked.borrow(),
vec![("mult", vec![0u8; 8], vec![0u8; 8], 2)]
);
}
#[test]
fn float_div_reads_both_operands_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_DIV, ONE_PAGE],
"(call $float_div (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_binary_ops_asked.borrow(),
vec![("div", vec![0u8; 8], vec![0u8; 8], 2)]
);
}
#[test]
fn float_pow_reads_the_float_the_degree_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_POW, ONE_PAGE],
"(call $float_pow (i32.const 0) (i32.const 8) (i32.const 3) (i32.const 64) (i32.const 8) (i32.const 1))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_unary_ops_asked.borrow(),
vec![("pow", vec![0u8; 8], 3, 1)]
);
}
/// The `ST*`-in operators read one serialized region and a mode, and write the float.
#[test]
fn float_from_stamount_reads_the_amount_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_FROM_STAMOUNT, ONE_PAGE],
"(call $float_from_stamount (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_from_stamount_asked.borrow(),
vec![(vec![0u8; 8], 2)]
);
}
#[test]
fn float_from_stnumber_reads_the_number_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_FROM_STNUMBER, ONE_PAGE],
"(call $float_from_stnumber (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_from_stnumber_asked.borrow(),
vec![(vec![0u8; 8], 2)]
);
}
/// `float_to_int` reads a float and a mode, writing the integer.
#[test]
fn float_to_int_reads_the_float_and_the_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_TO_INT, ONE_PAGE],
"(call $float_to_int (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 2))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(*host.float_to_int_asked.borrow(), vec![(vec![0u8; 8], 2)]);
}
/// `float_from_mant_exp` passes two scalars (an i64 mantissa and an i32 exponent) and a mode.
#[test]
fn float_from_mant_exp_passes_the_mantissa_exponent_and_mode() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let wat = module(
&[import::FLOAT_FROM_MANT_EXP, ONE_PAGE],
"(call $float_from_mant_exp (i64.const 42) (i32.const 3) (i32.const 64) (i32.const 8) (i32.const 1))",
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(*host.float_from_mant_exp_asked.borrow(), vec![(42, 3, 1)]);
}
/// A host input read from an unaligned pointer arrives intact: the host reads a byte slice, not
/// an aligned word, so a contract that hands it an odd offset is served the right bytes. (Ports
/// the old `bad_align` fixture, which exercised unaligned reads in the C-ABI wrapper.)
#[test]
fn an_unaligned_input_is_read_intact() {
let host = FakeHost::new().answering_float(support::Answer::filler(8));
let stores: String = (0..8u8)
.map(|i| {
format!(
"(i32.store8 (i32.const {}) (i32.const {}))",
i + 1,
0xa0 + i
)
})
.collect();
let wat = module(
&[import::FLOAT_FROM_STAMOUNT, ONE_PAGE],
&format!(
"{stores} (call $float_from_stamount (i32.const 1) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 0))"
),
);
assert_eq!(status(&wat, &host), 8);
assert_eq!(
*host.float_from_stamount_asked.borrow(),
vec![((0xa0u8..0xa8u8).collect::<Vec<u8>>(), 0)]
);
}

View File

@@ -593,3 +593,61 @@ fn a_memory64_memory_is_refused_by_screening() {
"{refusal}"
);
}
/// The corruption fixtures below are written as hex strings, which is how the old Beast suite
/// carried them — the bytes are deliberately malformed, so there is nothing to assemble them
/// from.
fn hex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
/// Malformed modules crafted to abuse the parser rather than merely be invalid — a vector
/// length that lies about its size, a section that overruns its payload, a locals-count bomb,
/// and a non-terminating LEB128 — are refused at compile like any other garbage. These guard
/// the parser against resource-exhaustion shapes (ported from the old Beast section-corruption
/// fixtures); the plainer "bad magic / wrong version" shapes are covered by `garbage_does_not_pass`.
#[test]
fn parser_abuse_shapes_are_refused() {
let cases = [
("vector length lies", "0061736d010000000105ffffffff0f"),
("section overruns its payload", "0061736d01000000010a0160"),
(
"locals-count bomb",
"0061736d01000000010401600000030201000a0f010d01ffffffff0f7f0b",
),
(
"non-terminating LEB128",
"0061736d0100000001058080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080808080",
),
];
for (label, h) in cases {
let refusal = xrpl_wasm_vm::check(&hex(h), ENTRY).expect_err(label);
assert_stage!(refusal, CheckError::Compile(_));
}
}
/// The plain structurally-malformed modules from the old section-corruption fixtures — a
/// corrupt magic, a wrong version, a lying section length, sections out of order, junk after
/// the last section, an unknown section id — are all refused at compile. Belt-and-suspenders
/// alongside `garbage_does_not_pass`: guards against a wasmi upgrade loosening the validator.
#[test]
fn structurally_malformed_modules_are_refused() {
let cases = [
("corrupt magic number", "0161736d01000000"),
("wrong version", "0061736d02000000"),
("lying section length", "0061736d01000000018080808008"),
("sections out of order", "0061736d010000000a02000b03020000"),
(
"junk after last section",
"0061736d01000000010a01600000000000000000",
),
("unknown section id", "0061736d01000000ff0100"),
];
for (label, h) in cases {
let refusal = xrpl_wasm_vm::check(&hex(h), ENTRY).expect_err(label);
assert_stage!(refusal, CheckError::Compile(_));
}
}

View File

@@ -598,3 +598,114 @@ fn a_memory64_module_is_rejected_at_compile() {
"rejected before instantiation, so nothing is charged: {failure}"
);
}
/// A function declaring more parameters than wasm allows (1000) is refused at compile, so a
/// contract cannot smuggle an unbounded signature past screening.
#[test]
fn a_function_with_too_many_params_is_refused() {
let host = FakeHost::new();
let params = " i32".repeat(1001);
let wat = format!(
"(module {ONE_PAGE} (func (param{params}) (result i32) (i32.const 0)) \
(func (export \"finish\") (result i32) (i32.const 0)))"
);
assert_stage!(failure(&wat, &host), RunError::Compile(_));
}
/// A function declaring more locals than wasm allows (50 000) is refused at compile.
#[test]
fn a_function_with_too_many_locals_is_refused() {
let host = FakeHost::new();
let locals = format!("(local{})", " i32".repeat(50_001));
let wat = module(&[ONE_PAGE], &format!("{locals} (i32.const 0)"));
assert_stage!(failure(&wat, &host), RunError::Compile(_));
}
/// Below the compile cap but past the engine's register frame, a locals-heavy function is
/// refused when the frame is built rather than at compile — still refused, just later.
#[test]
fn a_function_past_the_register_frame_is_refused() {
let host = FakeHost::new();
let locals = format!("(local{})", " i32".repeat(40_000));
let wat = module(&[ONE_PAGE], &format!("{locals} (i32.const 0)"));
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}
/// Unbounded recursion is stopped by the engine's call-stack limit — it traps rather than
/// running the host's native stack off the end (the portable dispatcher makes loops safe;
/// this pins that guest *calls* are bounded too).
#[test]
fn unbounded_recursion_is_stopped_by_the_call_stack_limit() {
let host = FakeHost::new();
let wat = format!(
"(module {ONE_PAGE} \
(func $rec (param i32) (result i32) \
(if (result i32) (i32.eqz (local.get 0)) (then (i32.const 0)) \
(else (call $rec (i32.sub (local.get 0) (i32.const 1)))))) \
(func (export \"finish\") (result i32) (call $rec (i32.const 1000000))))"
);
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}
/// A module with many functions currently compiles and runs: wasmi's only cap is its
/// 1,000,000 hard limit, so the ticket's ~24k-function module — a CodeMap-growth DoS, since
/// every validation appends to the engine's append-only CodeMap — is not refused here.
/// Enforcing a tighter bound (a function-count / average-bytes-per-function limit) belongs in
/// a future preflight pass that parses the module before the engine sees it. Ignored until
/// then, so this documents the gap without asserting it is acceptable.
#[test]
#[ignore = "CodeMap-DoS unmitigated; a function-count limit is deferred to preflight parsing"]
fn many_functions_currently_run_unbounded() {
let host = FakeHost::new();
let funcs: String = (0..24_000)
.map(|i| format!("(func $f{i} (result i32) (i32.const {}))", i % 7))
.collect();
let wat =
format!("(module {ONE_PAGE} {funcs} (func (export \"finish\") (result i32) (call $f0)))");
assert!(
run(&wat, &host).is_ok(),
"a large-function module currently compiles and runs"
);
}
/// The trap *kinds* wasmi distinguishes all reach the caller identically — a guest trap
/// charged as the contract's fault — so the `unreachable` representative pins the mapping.
/// These pin the individual kinds too, guarding against a wasmi upgrade reclassifying any of
/// them as something other than a trap.
#[test]
fn a_division_by_zero_traps() {
let host = FakeHost::new();
let wat = module(&[ONE_PAGE], "(i32.div_s (i32.const 1) (i32.const 0))");
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}
#[test]
fn a_signed_integer_overflow_traps() {
let host = FakeHost::new();
let wat = module(
&[ONE_PAGE],
"(i32.div_s (i32.const 0x80000000) (i32.const -1))",
);
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}
#[test]
fn an_indirect_call_to_a_null_table_entry_traps() {
let host = FakeHost::new();
let wat = format!(
"(module {ONE_PAGE} (type $t (func (result i32))) (table 1 funcref) \
(func (export \"finish\") (result i32) (call_indirect (type $t) (i32.const 0))))"
);
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}
#[test]
fn an_indirect_call_with_a_mismatched_signature_traps() {
let host = FakeHost::new();
let wat = format!(
"(module {ONE_PAGE} (type $void (func)) (type $i32 (func (result i32))) \
(table 1 funcref) (elem (i32.const 0) $f) (func $f (type $void)) \
(func (export \"finish\") (result i32) (call_indirect (type $i32) (i32.const 0))))"
);
assert_stage!(failure(&wat, &host), RunError::Trap(_));
}

View File

@@ -20,3 +20,20 @@ target_link_libraries(
xrpl_add_benchmark(nodestore)
target_link_libraries(xrpl.bench.nodestore PRIVATE xrpl.imports.bench)
add_dependencies(xrpl.benchmarks xrpl.bench.nodestore)
# Gas calibration for the wasm host functions. The ledger and real host come from
# `xrpl.testkit.wasm` (built with the tests, but framework-free), so this target links
# no GTest and no GMock.
if(TARGET xrpl.testkit.wasm)
xrpl_add_benchmark(wasm)
target_link_libraries(
xrpl.bench.wasm
PRIVATE xrpl.imports.bench xrpl.testkit.wasm
)
add_dependencies(xrpl.benchmarks xrpl.bench.wasm)
else()
message(
STATUS
"xrpl.testkit.wasm not built (tests disabled); skipping xrpl.bench.wasm."
)
endif()

View File

@@ -0,0 +1,175 @@
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
#include <xrpl/protocol/STObject.h>
#include <xrpl/protocol/SeqProxy.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol_autogen/transactions/EscrowCreate.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <helpers/Account.h>
#include <helpers/TxTest.h>
#include <tx/wasm/fixtures/FloatConstants.h>
#include <tx/wasm/fixtures/NftSetup.h>
#include <tx/wasm/fixtures/WasmLedger.h>
#include <string>
#include <string_view>
#include <utility>
namespace xrpl::test::bench {
Fixtures::Fixtures()
: alice_{ledger_.fund("benchAlice")}
, bob_{ledger_.fund("benchBob")}
, signerListOwner_{ledger_.fund("benchSigners")}
, escrow_{keylet::account(AccountID{})}
, signedMessage_{signMessage("the quick brown fox jumps over the lazy dog")}
, nftId_{NftIds::makeNftId(alice_.id())}
{
ledger_.makeSignerList(signerListOwner_, 2, {{alice_, 1}, {bob_, 1}});
// The escrow has to be submitted after the accounts exist, which is why it is built here
// rather than in the initializer list: its keylet depends on the owner's sequence number at
// submission time.
auto const ownerSeq = ledger_.ledger.getAccountRoot(alice_.id()).getSequence();
auto const created = ledger_.ledger.submit(
transactions::EscrowCreateBuilder{alice_.id(), bob_.id(), XRP(100)}.setFinishAfter(
900'000'000),
alice_);
if (created.ter != tesSUCCESS)
{
fixtureFailed(std::string{"creating the escrow: "} + transToken(created.ter));
}
ledger_.ledger.close();
escrow_ = keylet::escrow(alice_.id(), SeqProxy::rawSequence(ownerSeq));
}
Account const&
Fixtures::alice() const
{
return alice_;
}
Account const&
Fixtures::bob() const
{
return bob_;
}
TxAssembler
Fixtures::memoTx()
{
auto assembler = escrowFinishTx(ledger_.ledger, alice_);
assembler.build = [inner = std::move(assembler.build)](STObject& obj) {
inner(obj);
auto memos = STArray{};
memos.push_back(makeMemo(WasmLedger::toBytes("hello")));
memos.push_back(makeMemo(WasmLedger::toBytes("world")));
obj.setFieldArray(sfMemos, memos);
};
return assembler;
}
FieldLocator
Fixtures::memoLocator()
{
return FieldLocator{{sfMemos.getCode(), 0, sfMemoData.getCode()}};
}
WasmHost
Fixtures::host()
{
auto assembler = memoTx();
return ledger_.makeHost(
keylet::account(alice_.id()), assembler.type, std::move(assembler.build));
}
WasmHost
Fixtures::cachedHost()
{
auto wasmHost = host();
if (!wasmHost->cacheLedgerObj(keylet::account(alice_.id()).key, 1).has_value())
{
fixtureFailed("caching the account root into slot 1");
}
return wasmHost;
}
WasmHost
Fixtures::signerListHost()
{
auto assembler = bareTx();
return ledger_.makeHost(
keylet::signerList(signerListOwner_.id()), assembler.type, std::move(assembler.build));
}
WasmHost
Fixtures::cachedSignerListHost()
{
auto assembler = bareTx();
auto wasmHost =
ledger_.makeHost(keylet::account(AccountID{}), assembler.type, std::move(assembler.build));
if (!wasmHost->cacheLedgerObj(keylet::signerList(signerListOwner_.id()).key, 1).has_value())
{
fixtureFailed("caching the signer list into slot 1");
}
return wasmHost;
}
WasmHost
Fixtures::tracingHost()
{
return ledger_.makeTracingHost();
}
Keylet const&
Fixtures::escrow() const
{
return escrow_;
}
WasmHost
Fixtures::escrowHost()
{
return ledger_.makeHost(escrow_);
}
Slice
Fixtures::floatX()
{
return FloatConstants::slice(FloatConstants::kPi);
}
Slice
Fixtures::floatY()
{
return FloatConstants::slice(FloatConstants::kTwo);
}
SignedMessage const&
Fixtures::signedMessage() const
{
return signedMessage_;
}
uint256 const&
Fixtures::nftId() const
{
return nftId_;
}
Fixtures&
Fixtures::instance()
{
static Fixtures kValue;
return kValue;
}
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,111 @@
#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <helpers/Account.h>
#include <tx/wasm/fixtures/WasmLedger.h>
#include <cstdint>
namespace xrpl::test::bench {
// The ledger and canned inputs every `*.bench.cpp` measures against.
class Fixtures
{
public:
// The one set of fixtures every benchmark shares, built on first use.
static Fixtures&
instance();
// A sequence number for the keylets that take one. Arbitrary — a keylet hashes whatever it
// is given, so the value cannot change the cost.
static constexpr std::uint32_t kSeq = 42;
// Rounding mode 0 throughout the float family: modes select a tie-breaking rule, not a
// different algorithm, so they do not move the cost, and pinning one keeps the fourteen
// comparable.
static constexpr std::int32_t kRoundingMode = 0;
// Two funded accounts, enough for every keylet shape and every object below.
[[nodiscard]] Account const&
alice() const;
[[nodiscard]] Account const&
bob() const;
// The default host: its transaction carries a two-element memo array (something for the
// nested getters to walk to and the array-length getters to count) and its current object is
// Alice's account root.
[[nodiscard]] WasmHost
host();
// The same, with Alice's account root pinned to slot 1, for the `le_*` getters that read
// through a cache slot rather than the current object.
[[nodiscard]] WasmHost
cachedHost();
// An account root has no arrays, so the array-length getters that read a *ledger object*
// need a different one. A signer list has `sfSignerEntries`; without it those calls would
// answer `FieldNotFound` and the benchmark would time the rejection instead of the work.
[[nodiscard]] WasmHost
signerListHost();
[[nodiscard]] WasmHost
cachedSignerListHost();
// A host whose `trace` output is captured rather than dropped, so the log-enabled path can
// be measured against the log-disabled one that `host()` gives.
[[nodiscard]] WasmHost
tracingHost();
// A real escrow, created through the real transactor — the current object for
// `home_le_field`, the one getter whose cost depends on the object it reads rather than on
// its arguments.
[[nodiscard]] Keylet const&
escrow() const;
[[nodiscard]] WasmHost
escrowHost();
// `sfMemos[0].sfMemoData` — a two-step locator path, the shape the nested getters are priced
// for.
[[nodiscard]] static FieldLocator
memoLocator();
// Canonical float operands. Zeroed bytes decode as a non-canonical float and would be
// refused before any arithmetic ran, so the whole family shares these two known-good values.
[[nodiscard]] static Slice
floatX();
[[nodiscard]] static Slice
floatY();
// A signed message for `check_sig`. Signing is far more expensive than the verification
// being measured, so it happens once here rather than inside a timed loop.
[[nodiscard]] SignedMessage const&
signedMessage() const;
// A well-formed NFToken id with the fixture's known taxon, flags, fee and sequence baked in,
// so the id-extractor getters have real fields to pull out rather than zeros.
[[nodiscard]] uint256 const&
nftId() const;
private:
// Order matters, and that is the reason this is a constructor rather than a pile of lazy
// statics: the accounts have to be funded before the signer list and the escrow can be built
// on them.
Fixtures();
// The transaction the default host runs, carrying the memo array.
[[nodiscard]] TxAssembler
memoTx();
WasmLedger ledger_;
Account alice_;
Account bob_;
Account signerListOwner_;
Keylet escrow_;
SignedMessage signedMessage_;
uint256 nftId_;
};
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,44 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
// The harness checking itself.
//
// This file holds no host function — it belongs to the wasm directory rather than
// `host_functions/` because it measures the two reference points every per-function number is read
// against, and neither is a host call.
//
// `GuestInstruction` runs a contract whose "host call" is a couple of guest instructions. Its
// `implied_gas` and `charged_gas` are then two independent measurements of the same quantity — one
// from wall time via `secondsPerGas`, one from the engine's own fuel meter — and they should agree
// closely. When they diverge, `secondsPerGas` has measured something other than a guest
// instruction and no other number in the run is trustworthy. Read this first.
//
// The crossing floor is the other reference point, and it lives in `host_functions/LedgerSqn`:
// `ldgr_index` takes no input and answers from a header already in hand, so its impl is as close
// to nothing as a host function gets, and whatever its `ThroughVm` case costs above its `Impl`
// case is the price of leaving the guest — paid by every one of the 61 functions before any of
// them does any work.
//
// So the reading order across the suite is: this file, then `LedgerSqn`'s pair for the floor, then
// a function's own `Impl` number. Those three should account for its `ThroughVm` number; where
// they do not, the gap is size-dependent copying, which the swept cases (`Sha512Half`,
// `UpdateData`) expose.
void
guestInstruction(benchmark::State& state)
{
static constexpr std::string_view kBody = "(i32.add (local.get $r) (i32.const 1))";
// Empty import name: this case prices no host function, so there is nothing to look a
// declaration up for and it reports no `suggested_gas`.
benchmarkThroughVm(state, "", "", "", kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(guestInstruction)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,181 @@
# WASM host functions — gas calibration
These are **not tests**: nothing asserts, and a number moving is not a build failure. They answer
the question the tests cannot — whether each `#[gas = N]` in
`crates/xrpl-host-functions/src/lib.rs` matches what the function actually costs.
```bash
cmake --build build --target xrpl.bench.wasm
./build/xrpl.bench.wasm # everything (~2 min)
./build/xrpl.bench.wasm --benchmark_filter=sha512Half
./build/xrpl.bench.wasm --benchmark_repetitions=25 --benchmark_report_aggregates_only=true
```
**Build Release.** Debug inflates the crossing far more than the impls: `Impl`-to-`Impl` ratios
survive it, `suggested_gas` does not.
## Reading the output
| Counter | Meaning |
| ------------------- | ---------------------------------------------------------------------------------- |
| `suggested_gas` | **the answer** — what this function should be priced at |
| `host_function_gas` | what `lib.rs` says today, read through the `wasm_testkit` bridge so it can't drift |
| `price_ratio` | `host_function_gas / suggested_gas`. **1.0 is correct; below 1 is underpriced** |
| `rel_error` | relative uncertainty of `suggested_gas`. **Quote this one** — ~1.2% when quiet |
| `unreliable` | `1` means do not act on this row |
| `implied_gas` | the raw measurement, before the crossing is added back |
| `charged_gas` | what the engine actually billed; confirms the right call was measured |
| `ns_per_call` | raw wall time, for debugging a suspicious ratio |
Sort by `price_ratio`. **Below 1 is the direction that matters** — an underpriced call is one a
contract can buy too cheaply, a denial-of-service vector rather than a rounding error:
```bash
./build/xrpl.bench.wasm --benchmark_format=json |
jq -r '.benchmarks[] | select(.price_ratio) | [.price_ratio, .name] | @tsv' | sort -n
```
`unreliable=1` when `rel_error` exceeds 25%, or when `suggested_gas` falls below the crossing floor
— a call whose own cost is small next to the crossing is read off the difference of two nearly
equal numbers.
### With `--benchmark_repetitions`
Adds `_mean` / `_median` / `_stddev` / `_cv` rows. One trap worth knowing:
**`cv` on `suggested_gas` for an `Impl` case is not an error bar.** The crossing floor is measured
once per process, so repetitions never resample it — and it is most of a cheap `Impl` case's value.
Over 25 repetitions on an idle machine, `Impl` `cv` reads **1.3%** against `ThroughVm`'s 2.3%, while
`rel_error` is 1.2% for both. The lowest number in the output is the least trustworthy one.
Use repetitions to confirm the machine is quiet and to get a median; quote `rel_error`. Under load
the `Impl` cases inflate first (3.0% against 1.8% at load ~10), which makes the gap between the two
families a usable load detector.
## How `suggested_gas` is measured
Gas is wasmi fuel — `set_fuel(gas)` meters guest instructions and host charges from one pool — so
one gas is about one guest instruction and the question becomes a ratio. Every step is a
subtraction, so fixed costs cancel:
```
secondsPerGas = (time_busy − time_idle) / (fuel_busy − fuel_idle) # pure-wasm loop, N vs 0
implied_gas = secondsPerCall / secondsPerGas
crossing_floor = (ldgr_index ThroughVm − ldgr_index Impl) / secondsPerGas
suggested_gas = implied_gas # ThroughVm — the guest already paid the crossing
suggested_gas = implied_gas + crossing_floor # Impl — a guest cannot call without paying it
price_ratio = host_function_gas / suggested_gas
rel_error = sqrt( ( sqrt((implied·caseErr)² + (floor·floorErr)²) / suggested )² + perGasErr² )
```
`secondsPerCall` is itself a subtraction: a `ThroughVm` case runs a contract making N host calls
against a **byte-identical** one making none, so compilation, instantiation and the guest's own loop
cancel. An `Impl` case times `kCallsPerRun` direct calls and divides.
`secondsPerGas` is measured **once** and shared by every case, deliberately — two routes to the same
price must divide by the same constant, and per-case calibration was tried and swung 6x between
runs. Its uncertainty is propagated arithmetically instead. See the comments in `WasmBench.cpp` for
why the three terms in `rel_error` do not all combine in quadrature.
**`guestInstruction` is the self-test — read it first.** It runs the calibration's own loop body, so
its `implied_gas` (wall time) and `charged_gas` (the fuel meter) are two independent measurements of
one quantity. Quiet Release machine: **≈13.7 against 13.007, ~5% high.** A persistent gap much
beyond that means every other number in the run shares it. It has already caught an estimator
mismatch worth +40%, and the memory leak below.
**`suggested_gas` for an `Impl`-only case is a lower bound** — the crossing floor is measured on a
call with no input, so a function that moves bytes pays more; `Sha512Half` and `UpdateData` sweep
that per-byte term. `ThroughVm` cases are deliberately one per crossing _shape_, not one per
function.
## VM overhead (`Vm.cpp`)
Everything above prices what a contract _asks for_. `Vm.cpp` prices getting it running at all. Of
the seven stages `runEscrowWasm` performs — compile, store, linker, instantiate, entry-point lookup,
call, fuel read — **only the call is metered**; the rest is wall time no transaction pays for.
These cases report `ns_per_op` and `gas_equivalent` (that time over the same `secondsPerGas`, so an
unpriced stage reads on the same axis as a priced one), plus `module_bytes` and `gas_per_byte` on
size sweeps.
| case | measures |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `preflightMinimal` | compile plus the import/export walk. The narrowest view of compilation reachable from C++: no host, no instance |
| `preflightRejects` | the same on a module refused at the import walk. Against `preflightMinimal`, what refusing costs versus accepting |
| `runMinimal` | a whole run of a do-nothing contract — every stage. Minus `preflightMinimal`, the stages that are not compilation |
| `compileScaling/N` | compilation against module size: `N` unreachable filler functions, preflight only |
| `runScaling/N` | the same modules through a whole run. Paired with `compileScaling` per size, separates size-dependent stages from fixed ones |
| `instantiateScaling/pages` | declared memory at constant module size, so what moves is the host allocating and zeroing pages |
Two of those pairings are the point of the file. `runMinimal − preflightMinimal` gives the fixed
cost of everything that is not compilation; `runScaling` against `compileScaling` shows compilation
appearing a second time, because the transactor validates and executes with no module cache between
them. The size sweeps matter more than the floor: a fixed cost is only a griefing concern if it is
large, but a slope against attacker-chosen module size is one at any height.
Two caveats when reading a sweep. `gas_per_byte` is an **average** carrying the case's fixed cost,
not a marginal rate — it overestimates, and falls toward the true slope as the module grows, so read
the convergence rather than any single row. And the `/4096` points get few iterations and go noisy
first; compare `rel_error` across the sweep before quoting the largest one.
The sweep stops at 4096 functions for want of a real cap to stop at — no maximum contract size is
enforced anywhere yet, the transactor not being wired.
The linker rebuild and the fuel-metering overhead are **not** separable from here — C++ sees only
`runEscrowWasm` and `preflightEscrowWasm`. Both need benchmarks inside `xrpl-wasm-vm`, where
`compile` and `wasm_engine` are `pub(crate)`.
### Compiling leaks — pin your iteration counts
`wasm_engine()` is a process-global `LazyLock<Engine>`, and what `Module::new` adds to it is never
released. Repeatedly preflighting one **60-byte** module:
| `--benchmark_repetitions` | peak RSS |
| ------------------------- | -------- |
| 1 | 0.41 GB |
| 5 | 1.46 GB |
| 15 | 4.20 GB |
Linear, at roughly **800 bytes per compile**. Within the suite this is why every `Vm.cpp` case pins
`->Iterations(...)`: automatic sizing ran `preflightMinimal` ~348k times per repetition, reaching
7.9 GB at 25 repetitions, after which every later case in the binary failed to compile — 720 errored
rows, all blaming cases that were innocent.
**Outside the suite it is worth a look.** A validator compiles twice per programmable-escrow
transaction against that same static engine. Whether that is unbounded growth in production depends
on wasmi internals not checked here — this is the C++-visible symptom, not a diagnosis. wasmi is
pinned at `2.0.0-beta.10`, a beta, so try a version bump first.
## Gotchas, each of which has already cost someone an afternoon
- **The wasm ABI is not the trait's argument order.** `float_add(x, y, mode, out)` in Rust is
`(x_ptr, x_len, y_ptr, y_len, out_ptr, out_len, mode)` on the wire — scalars move _after_ the
output region. Check `register.rs`, not `lib.rs`, when writing WAT.
- **A soft host error still "succeeds".** The run completes and gas is charged _before_ the body, so
a wrong-argument case reports a plausible, confidently wrong number. The harness requires the
contract's result to be `>= 0`; the tell is a `ThroughVm` case coming out _faster_ than its `Impl`.
- **A host serves exactly one run** (`checkSelf` in `WasmVM.cpp`), so a benchmark builds a fresh host
per run and cannot pre-cache a slot.
- **`MAX_FIELD_BYTES` is 1024** — nothing crosses the boundary above 1 KiB, so size sweeps stop there.
- **Do not compare timings across builds at the cheap end.** Two builds whose timed regions were
byte-identical measured 2.18 ns and 2.52 ns for the same case — ~15% apart, from code layout alone.
Use `price_ratio` within one run, and reason from the code when judging whether a change costs
anything.
- **Every case pins `->Iterations(...)`, for two different reasons.** Host-function cases must
because with `UseManualTime` automatic sizing reads only the tiny reported residue and would ask
for millions of iterations; `Vm.cpp` cases must because compiling leaks.
## Layout
One `.cpp` per host function under `host_functions/`, mirroring
`src/tests/libxrpl/tx/wasm/host_functions/`, so adding a host function is a two-file checklist
rather than a judgement call. `Crossing.cpp` holds the harness's own reference points, `Vm.cpp` the
per-run overhead around them, `WasmBench.*` the measurement machinery, `BenchFixtures.*` the shared
ledger (one ledger, funded once, for the whole binary).
The ledger and real host come from `xrpl.testkit.wasm` — a framework-free library built alongside
the tests — so this target links **no GTest and no GMock**. See
`src/tests/libxrpl/tx/wasm/README.md` for how that library is split, and why its setup steps throw
rather than using `EXPECT_`.

View File

@@ -0,0 +1,149 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/WasmRun.h>
#include <cstddef>
#include <format>
#include <string>
namespace xrpl::test::bench {
namespace {
// What a run costs *around* the contract, rather than what its host calls cost. Only the guest's
// own execution is metered, so every stage measured here is wall time no transaction pays for.
// ../README.md has what each case measures and how to read `gas_equivalent`.
//
// **Every case must pin `->Iterations(...)`.** Compiling a module allocates against the
// process-global engine and is never reclaimed — roughly 800 bytes per compile — so Google
// Benchmark's automatic sizing, which targets a wall-clock budget rather than a compile count,
// reaches gigabytes resident and the whole binary stops being able to compile anything.
// The smallest module the engine accepts. Everything a run does to it is overhead by construction.
std::string
minimalWat()
{
return R"wat((module
(memory (export "memory") 1)
(func (export "escrow_finish") (result i32)
(i32.const 1)))
)wat";
}
// `count` unreachable functions on top of the minimal module: bigger without doing more.
//
// Each body is seeded with its own index so no two are identical and none folds to a constant the
// translator can drop — either would break the link between function count and byte count.
// Unreachable is fine: validation and translation visit every function a module declares, and that
// visit is exactly the cost being swept.
std::string
fillerWat(size_t count)
{
auto out = std::string{"(module\n (memory (export \"memory\") 1)\n"};
for (auto i = 0uz; i < count; ++i)
{
out += std::format(
" (func $f{0} (param i32) (result i32)\n"
" (i32.add (i32.mul (local.get 0) (i32.const {0})) (i32.const {0})))\n",
i);
}
out += " (func (export \"escrow_finish\") (result i32)\n (i32.const 1)))\n";
return out;
}
// The minimal module asking for `pages` of initial memory. `MAX_MEMORY_PAGES` is 128 (8 MiB), and a
// contract declares this for free — the host allocates and zeroes it before the first instruction.
std::string
pagesWat(size_t pages)
{
return std::format(
"(module\n"
" (memory (export \"memory\") {})\n"
" (func (export \"escrow_finish\") (result i32)\n"
" (i32.const 1)))\n",
pages);
}
// Compiles cleanly and is then refused: `env::malloc` is not a namespace the engine serves, so
// `check_imports` stops at it.
std::string
rejectedWat()
{
return R"wat((module
(import "env" "malloc" (func $malloc (param i32) (result i32)))
(memory (export "memory") 1)
(func (export "escrow_finish") (result i32)
(i32.const 1)))
)wat";
}
void
preflightMinimal(benchmark::State& state)
{
static auto const kWasm = assembleWat(minimalWat());
benchmarkPreflight(state, kWasm);
}
BENCHMARK(preflightMinimal)->UseManualTime()->Iterations(kBenchIterations);
void
preflightRejects(benchmark::State& state)
{
static auto const kWasm = assembleWat(rejectedWat());
benchmarkPreflight(state, kWasm, /*expectAccepted*/ false);
}
BENCHMARK(preflightRejects)->UseManualTime()->Iterations(kBenchIterations);
void
runMinimal(benchmark::State& state)
{
static auto const kWasm = assembleWat(minimalWat());
benchmarkRun(state, kWasm, [] { return Fixtures::instance().host(); });
}
BENCHMARK(runMinimal)->UseManualTime()->Iterations(kBenchIterations);
void
compileScaling(benchmark::State& state)
{
auto const wasm = assembleWat(fillerWat(static_cast<size_t>(state.range(0))));
benchmarkPreflight(state, wasm, /*expectAccepted*/ true, /*sizeSweep*/ true);
}
BENCHMARK(compileScaling)
->UseManualTime()
->Iterations(kBenchIterations)
->Arg(1)
->Arg(8)
->Arg(64)
->Arg(512)
->Arg(4096);
void
runScaling(benchmark::State& state)
{
auto const wasm = assembleWat(fillerWat(static_cast<size_t>(state.range(0))));
benchmarkRun(state, wasm, [] { return Fixtures::instance().host(); }, /*sizeSweep*/ true);
}
BENCHMARK(runScaling)
->UseManualTime()
->Iterations(kBenchIterations)
->Arg(1)
->Arg(8)
->Arg(64)
->Arg(512)
->Arg(4096);
void
instantiateScaling(benchmark::State& state)
{
auto const wasm = assembleWat(pagesWat(static_cast<size_t>(state.range(0))));
benchmarkRun(state, wasm, [] { return Fixtures::instance().host(); });
}
BENCHMARK(instantiateScaling)
->UseManualTime()
->Iterations(kBenchIterations)
->Arg(1)
->Arg(8)
->Arg(32)
->Arg(128);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,397 @@
#include <benchmarks/libxrpl/wasm/WasmBench.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/WasmVM.h>
#include <benchmark/benchmark.h>
#include <rust/cxx.h>
#include <tx/wasm/fixtures/WasmLedger.h>
#include <tx/wasm/fixtures/WasmRun.h>
#include <xrpl_wasm_testkit_cxxbridge/lib.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <format>
#include <span>
#include <string>
#include <string_view>
namespace xrpl::test::bench {
int
callsWithinTransferBudget(std::int64_t bytesWrittenPerCall)
{
// Writes nothing back to the guest, so the budget does not apply.
if (bytesWrittenPerCall <= 0)
{
return kCallsPerRun;
}
auto const affordable = kTransferLimitBytes / bytesWrittenPerCall;
if (affordable < 1)
{
fixtureFailed("a single call would exceed the run's transfer budget");
}
return static_cast<int>(std::min<std::int64_t>(affordable, kCallsPerRun));
}
std::string
dataSegment(int offset, std::span<std::uint8_t const> bytes)
{
return std::format(" (data (i32.const {}) \"{}\")\n", offset, watEscaped(bytes));
}
std::string
dataSegment(int offset, Bytes const& bytes)
{
return dataSegment(offset, std::span<std::uint8_t const>{bytes.data(), bytes.size()});
}
std::string
makeLoopWat(std::string_view imports, std::string_view data, std::string_view body, int count)
{
static constexpr auto kTemplate = R"wat((module
{}
(memory (export "memory") 1)
{}
(func (export "escrow_finish") (result i32)
(local $i i32)
(local $r i32)
(local.set $i (i32.const {}))
(block $done
(loop $again
(br_if $done (i32.eqz (local.get $i)))
(local.set $r {})
(local.set $i (i32.sub (local.get $i) (i32.const 1)))
(br $again)))
(local.get $r)))
)wat";
return std::format(kTemplate, imports, data, count, body);
}
Timing
timeRun(HostFunctions& host, Bytes const& wasm)
{
auto const start = std::chrono::steady_clock::now();
auto outcome = runEscrowWasm(wasm, host, kBenchGas);
auto const elapsed = std::chrono::steady_clock::now() - start;
benchmark::DoNotOptimize(outcome);
return {
.seconds = std::chrono::duration<double>(elapsed).count(),
.gas = outcome.has_value() ? outcome->cost : std::int64_t{0}};
}
StageTimer::StageTimer(benchmark::State& state, std::int64_t moduleBytes)
: state_{state}, moduleBytes_{moduleBytes}
{
}
void
StageTimer::add(double seconds)
{
state_.SetIterationTime(seconds);
total_ += seconds;
sumSquares_ += seconds * seconds;
++rounds_;
}
void
StageTimer::report()
{
if (rounds_ == 0)
{
return;
}
auto const count = static_cast<double>(rounds_);
auto const mean = total_ / count;
auto const variance = std::max(0.0, (sumSquares_ / count) - (mean * mean));
auto const spread = mean > 0.0 ? std::sqrt(variance) / mean : 0.0;
auto const& calibration = Calibration::instance();
auto const perGas = calibration.secondsPerGas();
auto const equivalent = perGas > 0.0 ? mean / perGas : 0.0;
state_.counters["ns_per_op"] = mean * 1e9;
// What this stage would cost if it were charged at the rate the guest inside it is charged at.
// It is not charged, which is the point: this puts an unpriced stage in the host-function
// table's units.
state_.counters["gas_equivalent"] = equivalent;
if (moduleBytes_ > 0)
{
state_.counters["module_bytes"] = static_cast<double>(moduleBytes_);
// An average over the whole operation, not the marginal rate: it carries the case's fixed
// cost, so it overestimates and falls toward the true slope as the sweep grows.
state_.counters["gas_per_byte"] = equivalent / static_cast<double>(moduleBytes_);
}
// `gas_equivalent` divides by `secondsPerGas`, so the divisor's uncertainty is in every number
// here too.
auto const caseStdErr = spread / std::sqrt(count);
auto const perGasErr = calibration.secondsPerGasRelStdErr();
auto const totalErr = std::sqrt((caseStdErr * caseStdErr) + (perGasErr * perGasErr));
state_.counters["rel_error"] = totalErr;
state_.counters["unreliable"] = totalErr > kMaxRelativeSpread ? 1 : 0;
}
void
benchmarkPreflight(benchmark::State& state, Bytes const& wasm, bool expectAccepted, bool sizeSweep)
{
(void)Calibration::instance();
// Discarded: the reject case refuses on every iteration, and a real sink would put string
// formatting and I/O inside the measurement.
auto const journal = beast::Journal{beast::Journal::getNullSink()};
if (isTesSuccess(preflightEscrowWasm(wasm, journal)) != expectAccepted)
{
state.SkipWithError(
expectAccepted
? "the module was refused; the case would be measuring the reject path"
: "the module was accepted; the case would be measuring the accept path");
return;
}
StageTimer timer{state, sizeSweep ? static_cast<std::int64_t>(wasm.size()) : 0};
for (auto _ : state)
{
auto const start = std::chrono::steady_clock::now();
auto verdict = preflightEscrowWasm(wasm, journal);
auto const elapsed = std::chrono::steady_clock::now() - start;
benchmark::DoNotOptimize(verdict);
timer.add(std::chrono::duration<double>(elapsed).count());
}
timer.report();
}
namespace {
// Seconds of wall time one unit of gas buys on this machine.
//
// The estimator must be the *same* one the cases use — a mean, with the same clamp at zero. Since
// `implied_gas = secondsPerCall / secondsPerGas`, any difference between how divisor and dividend
// are estimated lands in every reported number: calibrating with a best-of while measuring with a
// mean once biased the whole report +40%.
//
// `guestInstruction` in Crossing.cpp is the check that this holds — it runs this exact loop body.
double
measureSecondsPerGas(double& relativeStandardError)
{
// A couple of guest instructions, no memory traffic, nothing the engine can fold away.
static constexpr auto kBody = std::string_view{"(i32.add (local.get $r) (i32.const 1))"};
auto const busy = assembleWat(makeLoopWat("", "", kBody, kCallsPerRun));
auto const idle = assembleWat(makeLoopWat("", "", kBody, 0));
auto fixture = WasmLedger{};
// Warm up, so the first-run penalty does not land on one side of the subtraction.
for (auto i = 0U; i < 8; ++i)
{
timeRun(*fixture.makeHost(), busy);
timeRun(*fixture.makeHost(), idle);
}
auto total = 0.0;
auto sumSquares = 0.0;
// Fuel is exact and deterministic, so any pair gives the same delta.
auto gasDelta = std::int64_t{1};
for (auto i = 0; i < kCalibrationPairs; ++i)
{
auto hotHost = fixture.makeHost();
auto const hot = timeRun(*hotHost, busy);
auto coldHost = fixture.makeHost();
auto const cold = timeRun(*coldHost, idle);
auto const delta = std::max(0.0, hot.seconds - cold.seconds);
total += delta;
sumSquares += delta * delta;
gasDelta = std::max(std::int64_t{1}, hot.gas - cold.gas);
}
auto const mean = total / kCalibrationPairs;
auto const variance = std::max(0.0, (sumSquares / kCalibrationPairs) - (mean * mean));
relativeStandardError = mean > 0.0 ? std::sqrt(variance / kCalibrationPairs) / mean : 0.0;
return mean / static_cast<double>(gasDelta);
}
// The crossing, in gas: `ldgr_index` through the VM minus `ldgr_index` called directly.
//
// Both halves are means, for the reason above: the VM half has to match `benchmarkThroughVm`'s
// estimator and the impl half `benchmarkImpl`'s. `secondsPerGas` is passed in rather than
// re-measured so it comes from the same snapshot.
double
measureCrossingFloorGas(double secondsPerGas, double& relativeStandardError)
{
static constexpr std::string_view kImport =
R"( (import "host_lib" "ldgr_index" (func $ldgr_index (param i32 i32) (result i32)))
)";
static constexpr std::string_view kBody = "(call $ldgr_index (i32.const 0) (i32.const 4))";
auto const loaded = assembleWat(makeLoopWat(kImport, "", kBody, kCallsPerRun));
auto const baseline = assembleWat(makeLoopWat(kImport, "", kBody, 0));
auto fixture = WasmLedger{};
auto vmTotal = 0.0;
auto vmSquares = 0.0;
auto guestOverheadGas = 0.0;
for (auto i = 0; i < kBenchIterations; ++i)
{
auto hotHost = fixture.makeHost();
auto const hot = timeRun(*hotHost, loaded);
auto coldHost = fixture.makeHost();
auto const cold = timeRun(*coldHost, baseline);
auto const perCall = std::max(0.0, hot.seconds - cold.seconds) / kCallsPerRun;
vmTotal += perCall;
vmSquares += perCall * perCall;
// Exact, from the fuel meter: what the guest burned per call beyond the call itself.
guestOverheadGas =
(static_cast<double>(hot.gas - cold.gas) / kCallsPerRun) - declaredGas("ldgr_index");
}
auto const vmSeconds = vmTotal / kBenchIterations;
// The impl side is the same call without the VM. Subtracting it leaves the crossing.
auto implTotal = 0.0;
auto implSquares = 0.0;
auto host = fixture.makeHost();
for (auto i = 0; i < kBenchIterations; ++i)
{
auto const start = std::chrono::steady_clock::now();
for (auto c = 0U; c < kCallsPerRun; ++c)
{
auto result = host->getLedgerSqn();
benchmark::DoNotOptimize(result);
}
auto const elapsed = std::chrono::steady_clock::now() - start;
auto const perCall = std::chrono::duration<double>(elapsed).count() / kCallsPerRun;
implTotal += perCall;
implSquares += perCall * perCall;
}
auto const implSeconds = implTotal / kBenchIterations;
if (secondsPerGas <= 0.0)
{
return 0.0;
}
// Take the guest's loop bookkeeping off here too: `report` removes it from every `ThroughVm`
// number, so leaving it in would make the two routes to one price disagree by that amount.
auto const crossing = std::max(0.0, vmSeconds - implSeconds) / secondsPerGas;
auto const floor = std::max(0.0, crossing - std::max(0.0, guestOverheadGas));
// Relative to the *difference*, not to either half: both contribute their error, and the
// denominator is what survives the subtraction. Not divided by `secondsPerGas` — that error is
// common-mode with the rest of `suggested_gas` and is applied once, to the sum, in `report`.
auto const vmVariance = std::max(0.0, (vmSquares / kBenchIterations) - (vmSeconds * vmSeconds));
auto const implVariance =
std::max(0.0, (implSquares / kBenchIterations) - (implSeconds * implSeconds));
auto const vmStdErr = std::sqrt(vmVariance / kBenchIterations);
auto const implStdErr = std::sqrt(implVariance / kBenchIterations);
auto const crossingSeconds = vmSeconds - implSeconds;
auto const crossingStdErr = std::sqrt((vmStdErr * vmStdErr) + (implStdErr * implStdErr));
relativeStandardError = crossingSeconds > 0.0 ? crossingStdErr / crossingSeconds : 0.0;
return floor;
}
} // namespace
Calibration const&
Calibration::instance()
{
static Calibration const kValue;
return kValue;
}
Calibration::Calibration()
: secondsPerGas_{measureSecondsPerGas(secondsPerGasRelStdErr_)}
, crossingFloorGas_{measureCrossingFloorGas(secondsPerGas_, crossingFloorRelStdErr_)}
{
}
double
declaredGas(std::string_view wasmName)
{
return static_cast<double>(
rs::wasm_testkit::host_function_gas(rust::Str{wasmName.data(), wasmName.size()}));
}
void
report(
benchmark::State& state,
double secondsPerCall,
double chargedGas,
double guestOverheadGas,
double relativeSpread,
std::int64_t rounds,
std::string_view wasmName,
bool throughVm)
{
auto const& calibration = Calibration::instance();
auto const perGas = calibration.secondsPerGas();
auto const measured = perGas > 0.0 ? secondsPerCall / perGas : 0.0;
// The timed number covers the host call *and* whatever the guest ran around it.
// `guestOverheadGas` is exact, so taking it off removes a bias rather than trading estimates.
auto const implied = std::max(0.0, measured - guestOverheadGas);
auto const suggested = throughVm ? implied : implied + calibration.crossingFloorGas();
state.counters["implied_gas"] = implied;
state.counters["ns_per_call"] = secondsPerCall * 1e9;
state.counters["charged_gas"] = chargedGas;
if (wasmName.empty())
{
return;
}
auto const declared = declaredGas(wasmName);
state.counters["host_function_gas"] = declared;
state.counters["suggested_gas"] = suggested;
// Below 1 is the direction that matters: an underpriced call is one a contract buys too
// cheaply.
state.counters["price_ratio"] = suggested > 0.0 ? declared / suggested : 0.0;
// Uncertainty **of `suggested_gas`**, not of `implied_gas` — different numbers once the floor
// is added. `implied` and the floor are independent timings, so their absolute errors add in
// quadrature over the sum; but both divide by `secondsPerGas`, so that error is common-mode and
// applies once to the total. Adding it per-term would count it twice.
//
// For `ThroughVm` the floor term is zero and `suggested == implied`, so this reduces exactly to
// the plain `sqrt(caseErr² + perGasErr²)`. Only `Impl` cases move — and for a cheap one the
// floor is most of `suggested_gas`, so an error bar describing `implied` alone described
// little.
auto const caseStdErr =
rounds > 0 ? relativeSpread / std::sqrt(static_cast<double>(rounds)) : relativeSpread;
auto const impliedErr = implied * caseStdErr;
auto const floorErr =
throughVm ? 0.0 : calibration.crossingFloorGas() * calibration.crossingFloorRelStdErr();
auto const independentErr = suggested > 0.0
? std::sqrt((impliedErr * impliedErr) + (floorErr * floorErr)) / suggested
: caseStdErr;
auto const perGasErr = calibration.secondsPerGasRelStdErr();
auto const totalErr = std::sqrt((independentErr * independentErr) + (perGasErr * perGasErr));
state.counters["rel_error"] = totalErr;
// A call whose own cost is small next to the crossing is read off the difference of two nearly
// equal numbers, so its `suggested_gas` is scatter rather than signal.
auto const floor = calibration.crossingFloorGas();
state.counters["unreliable"] =
(totalErr > kMaxRelativeSpread || (floor > 0.0 && suggested < floor)) ? 1 : 0;
}
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,369 @@
#pragma once
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <benchmark/benchmark.h>
#include <tx/wasm/fixtures/WasmRun.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
// The gas-calibration harness. What the numbers mean and how to read a report are in ../README.md.
namespace xrpl::test::bench {
// Enough that a benchmark loop never ends early; running out would measure something shorter.
inline constexpr std::int64_t kBenchGas = 2'000'000'000;
// Host calls per run: enough that the per-call cost dominates the baseline subtraction's residue.
inline constexpr std::int32_t kCallsPerRun = 1000;
// Timed iterations per case. Pinned because automatic sizing cannot work here: a case reports a
// residue of nanoseconds while the iteration producing it ran two contracts and cost milliseconds,
// so sizing would ask for millions.
inline constexpr std::int32_t kBenchIterations = 50;
// Pairs the one-off calibration averages. Higher than `kBenchIterations` because `secondsPerGas`
// divides every reported number, and a bare wasm loop is cheap to repeat.
inline constexpr std::int32_t kCalibrationPairs = 400;
// Above this relative uncertainty, `suggested_gas` is reported as unreliable.
inline constexpr double kMaxRelativeSpread = 0.25;
// `TRANSFER_LIMIT_BYTES` in crates/xrpl-wasm-vm/src/vm.rs: what a run may write into guest memory
// before `charge_transfer` starts refusing calls.
inline constexpr std::int64_t kTransferLimitBytes = 1 << 20;
// How many calls a run can afford, given the bytes each has the host **write into guest memory**.
// Output direction only — what the guest passes in is borrowed, and costs nothing against the
// budget. Never raises the count to meet a floor; a case that cannot afford one call fails loudly.
int
callsWithinTransferBudget(std::int64_t bytesWrittenPerCall);
// One run of a contract: how long it took, and what the engine charged it.
struct Timing
{
double seconds{};
std::int64_t gas{};
};
// A `(data ...)` segment placing `bytes` at `offset` in guest memory, so the timed loop measures
// the host call rather than the guest arranging its arguments. `watEscaped` in WasmRun.h says why
// zeroed memory will not do.
std::string
dataSegment(int offset, std::span<std::uint8_t const> bytes);
std::string
dataSegment(int offset, Bytes const& bytes);
// A contract that runs `body` `count` times and returns the last result.
std::string
makeLoopWat(std::string_view imports, std::string_view data, std::string_view body, int count);
// Run pre-assembled `wasm` once through the real VM, reporting wall time and gas.
Timing
timeRun(HostFunctions& host, Bytes const& wasm);
// What this machine costs, measured once and shared by every case: two cases pricing one function
// two ways (`Impl` + crossing floor, versus `ThroughVm`) must divide by the *same* `secondsPerGas`
// or they disagree for reasons unrelated to the function.
class Calibration
{
public:
static Calibration const&
instance();
// Seconds of wall time one unit of gas buys here.
[[nodiscard]] double
secondsPerGas() const
{
return secondsPerGas_;
}
// The gas a host call costs before it does anything: region decode, bounds checks, the cxx hop.
[[nodiscard]] double
crossingFloorGas() const
{
return crossingFloorGas_;
}
// Propagated into every case's `rel_error`, which is what lets one snapshot stay shared:
// `--benchmark_repetitions` resamples per-case timings but never this divisor.
[[nodiscard]] double
secondsPerGasRelStdErr() const
{
return secondsPerGasRelStdErr_;
}
// An additive term in every `Impl` case's `suggested_gas`, and most of the cheap ones.
[[nodiscard]] double
crossingFloorRelStdErr() const
{
return crossingFloorRelStdErr_;
}
private:
Calibration();
double secondsPerGasRelStdErr_{};
double crossingFloorRelStdErr_{};
double secondsPerGas_{};
double crossingFloorGas_{};
};
// What the gas table declares for a host function, by guest import name, read through the
// `wasm_testkit` bridge so it cannot drift.
double
declaredGas(std::string_view wasmName);
// Attach the calibration counters to a finished case.
//
// `guestOverheadGas` is fuel the guest burned around the call — loop bookkeeping and the
// `i32.const`s pushing arguments. From the fuel meter, so it is exact. Zero for `Impl` cases.
void
report(
benchmark::State& state,
double secondsPerCall,
double chargedGas,
double guestOverheadGas,
double relativeSpread,
std::int64_t rounds,
std::string_view wasmName,
bool throughVm);
// Accumulates one whole-operation case and turns it into counters.
//
// The two harnesses below subtract setup away, amortizing over `kCallsPerRun` calls against a
// baseline. Here that is inverted: setup is the subject, timed whole, nothing amortized.
class StageTimer
{
public:
// Pass zero for `moduleBytes` unless the case belongs to a sweep that *varies* module size —
// the per-byte counter it enables is an average over the whole operation, meaningless where the
// module is constant.
StageTimer(benchmark::State& state, std::int64_t moduleBytes);
void
add(double seconds);
// Attach the counters. Call once, after the loop.
void
report();
private:
benchmark::State& state_;
std::int64_t moduleBytes_{};
double total_{};
double sumSquares_{};
std::int64_t rounds_{};
};
// Measure `preflightEscrowWasm`: compile, then the walk over the module's imports and exports.
//
// `expectAccepted` is what the module *should* do. A refused module stops at the first fault and is
// far cheaper, so a case that silently flipped verdict would report a confident number for an
// operation it never performed.
void
benchmarkPreflight(
benchmark::State& state,
Bytes const& wasm,
bool expectAccepted = true,
bool sizeSweep = false);
// Measure a whole `runEscrowWasm` — every stage, unamortized.
template <class SetUp>
void
benchmarkRun(benchmark::State& state, Bytes const& wasm, SetUp&& setUp, bool sizeSweep = false)
{
// Force the calibration and the engine's lazy construction before the clock starts, so the
// first case does not absorb them into its own first iteration.
[[maybe_unused]] auto const& calibration = Calibration::instance();
auto probe = setUp();
if (auto const check = runEscrowWasm(wasm, *probe, kBenchGas); !check.has_value())
{
state.SkipWithError("the benchmarked contract did not run to completion");
return;
}
auto timer = StageTimer{state, sizeSweep ? static_cast<std::int64_t>(wasm.size()) : 0};
for (auto _ : state)
{
auto host = setUp();
timer.add(timeRun(*host, wasm).seconds);
}
timer.report();
}
// Measure a host function through the whole stack — guest, VM, marshalling, real impl, real ledger
// — with everything but the host calls subtracted away.
template <class SetUp>
void
benchmarkThroughVm(
benchmark::State& state,
std::string_view wasmName,
std::string_view imports,
std::string_view data,
std::string_view body,
SetUp&& setUp,
int calls = kCallsPerRun)
{
auto const loaded = assembleWat(makeLoopWat(imports, data, body, calls));
auto const baseline = assembleWat(makeLoopWat(imports, data, body, 0));
// A host serves exactly one run — `runEscrowWasm` asserts it was handed a clean one
// (`checkSelf` in WasmVM.cpp). Hence `setUp` being a factory rather than a host.
auto probe = setUp();
// A soft host error still completes the run, so require both that it completed and that the
// last host call returned a non-negative result, which every body leaves in `$r`.
auto const check = runEscrowWasm(loaded, *probe, kBenchGas);
if (!check.has_value())
{
state.SkipWithError("the benchmarked contract did not run to completion");
return;
}
if (check->result < 0)
{
state.SkipWithError(
"the benchmarked host call returned error code " + std::to_string(check->result) +
"; the case would be measuring the rejection path, not the work");
return;
}
auto totalSeconds = 0.0;
auto sumSquares = 0.0;
auto totalGas = 0.0;
auto rounds = std::int64_t{0};
for (auto _ : state)
{
auto hotHost = setUp();
auto const hot = timeRun(*hotHost, loaded);
auto coldHost = setUp();
auto const cold = timeRun(*coldHost, baseline);
// Clamped: on a noisy machine a pair can invert, and a negative iteration time would make
// Google Benchmark's statistics meaningless.
auto const perCall = std::max(0.0, hot.seconds - cold.seconds) / calls;
state.SetIterationTime(perCall);
totalSeconds += perCall;
sumSquares += perCall * perCall;
totalGas += static_cast<double>(hot.gas - cold.gas) / calls;
++rounds;
}
if (rounds > 0)
{
auto const meanSeconds = totalSeconds / rounds;
auto const variance = std::max(0.0, (sumSquares / rounds) - (meanSeconds * meanSeconds));
auto const spread = meanSeconds > 0.0 ? std::sqrt(variance) / meanSeconds : 0.0;
// Zero for the empty-name case — `guestInstruction`, which prices no host function. There
// is no host call to separate scaffolding *from*, and subtracting the full charge would
// leave `implied_gas = measured - charged`, ~0 by construction: it would turn the harness's
// one self-test into a tautology that cannot fail.
auto const chargedPerCall = totalGas / rounds;
auto const overhead = wasmName.empty() ? 0.0 : chargedPerCall - declaredGas(wasmName);
report(
state,
meanSeconds,
chargedPerCall,
std::max(0.0, overhead),
spread,
rounds,
wasmName,
true);
}
}
// Measure a host function's impl alone — no guest, no VM, no marshalling. Against the `ThroughVm`
// case for the same function, the difference is what crossing the boundary costs.
template <class SetUp, class Call>
void
benchmarkImpl(benchmark::State& state, std::string_view wasmName, SetUp&& setUp, Call&& call)
{
auto host = setUp();
// A call that errors returns early and is far cheaper than one that works, so a subtly wrong
// argument yields a confident and always *too low* price. Probed either side of the loop rather
// than inside it, where the branch would land in the measurement: *before* catches wrong
// arguments, *after* catches a call that stopped working once the loop exhausted something.
//
// The `requires` skips host functions that answer nothing (`trace`) or answer a bare value.
auto const checkSucceeds = [&](char const* when) {
if constexpr (requires { call(*host).has_value(); })
{
if (auto const probe = call(*host); !probe.has_value())
{
state.SkipWithError(
std::string{"the benchmarked host call returned error code "} +
std::to_string(static_cast<int>(probe.error())) + " " + when +
" the timed loop; the case would be measuring the rejection path");
return false;
}
}
return true;
};
if (!checkSucceeds("before"))
{
return;
}
auto totalSeconds = 0.0;
auto sumSquares = 0.0;
auto rounds = std::int64_t{0};
for (auto _ : state)
{
auto const start = std::chrono::steady_clock::now();
for (int i = 0; i < kCallsPerRun; ++i)
{
// `trace` answers nothing, so `ClobberMemory` stands in for `DoNotOptimize`.
if constexpr (std::is_void_v<decltype(call(*host))>)
{
call(*host);
benchmark::ClobberMemory();
}
else
{
auto result = call(*host);
benchmark::DoNotOptimize(result);
}
}
auto const elapsed = std::chrono::steady_clock::now() - start;
auto const perCall = std::chrono::duration<double>(elapsed).count() / kCallsPerRun;
state.SetIterationTime(perCall);
totalSeconds += perCall;
sumSquares += perCall * perCall;
++rounds;
}
if (!checkSucceeds("after"))
{
return;
}
if (rounds > 0)
{
auto const meanSeconds = totalSeconds / rounds;
auto const variance = std::max(0.0, (sumSquares / rounds) - (meanSeconds * meanSeconds));
auto const spread = meanSeconds > 0.0 ? std::sqrt(variance) / meanSeconds : 0.0;
// Nothing charged and no guest scaffolding; `report` adds the crossing back in.
report(state, meanSeconds, 0.0, 0.0, spread, rounds, wasmName, false);
}
}
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
accountKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"accountroot_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.accountKeylet(Fixtures::instance().alice().id()); });
}
BENCHMARK(accountKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,31 @@
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/UintTypes.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ammKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"amm_id"};
auto const usd = Asset{Issue{toCurrency("USD"), Fixtures::instance().alice().id()}};
auto const xrp = Asset{xrpIssue()};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&usd, &xrp](auto& host) { return host.ammKeylet(usd, xrp); });
}
BENCHMARK(ammKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
baseFeeImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"base_fee"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getBaseFee(); });
}
BENCHMARK(baseFeeImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,28 @@
#include <xrpl/protocol/Indexes.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
cacheLedgerObjImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"cache_le"};
auto const key = keylet::account(Fixtures::instance().alice().id()).key;
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&key](auto& host) { return host.cacheLedgerObj(key, 1); });
}
BENCHMARK(cacheLedgerObjImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
checkKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"check_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.checkKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(checkKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,63 @@
#include <xrpl/basics/Slice.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <cstdint>
#include <format>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "check_sig";
constexpr std::string_view kImport =
R"( (import "host_lib" "check_sig" (func $check_sig (param i32 i32 i32 i32 i32 i32) (result i32)))
)";
constexpr std::int32_t kMessageOffset = 0;
constexpr std::int32_t kSignatureOffset = 256;
constexpr std::int32_t kPubkeyOffset = 512;
void
checkSignatureThroughVm(benchmark::State& state)
{
auto const& m = Fixtures::instance().signedMessage();
static auto const kData = dataSegment(kMessageOffset, m.message) +
dataSegment(kSignatureOffset, m.signature) + dataSegment(kPubkeyOffset, m.publicKey);
static auto const kBody = std::format(
"(call $check_sig (i32.const {}) (i32.const {}) (i32.const {}) (i32.const {}) "
"(i32.const {}) (i32.const {}))",
kMessageOffset,
m.message.size(),
kSignatureOffset,
m.signature.size(),
kPubkeyOffset,
m.publicKey.size());
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(checkSignatureThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
checkSignatureImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
auto const& m = Fixtures::instance().signedMessage();
return host.checkSignature(
Slice{m.message.data(), m.message.size()},
Slice{m.signature.data(), m.signature.size()},
Slice{m.publicKey.data(), m.publicKey.size()});
});
}
BENCHMARK(checkSignatureImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,32 @@
#include <xrpl/basics/Slice.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
credentialKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"credential_id"};
static constexpr auto kType = std::string_view{"termsandconditions"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.credentialKeylet(
Fixtures::instance().alice().id(),
Fixtures::instance().bob().id(),
Slice{kType.data(), kType.size()});
});
}
BENCHMARK(credentialKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
currentLedgerObjArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"home_le_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().signerListHost(); },
[](auto& host) { return host.getCurrentLedgerObjArrayLen(sfSignerEntries); });
}
BENCHMARK(currentLedgerObjArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,41 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <format>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "home_le_field";
constexpr std::string_view kImport =
R"( (import "host_lib" "home_le_field" (func $home_le_field (param i32 i32 i32) (result i32)))
)";
void
currentLedgerObjFieldThroughVm(benchmark::State& state)
{
static auto const kBody = std::format(
"(call $home_le_field (i32.const {}) (i32.const 0) (i32.const 32))", sfAccount.getCode());
benchmarkThroughVm(
state, kWasmName, kImport, "", kBody, [] { return Fixtures::instance().escrowHost(); });
}
BENCHMARK(currentLedgerObjFieldThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
currentLedgerObjFieldImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().escrowHost(); },
[](auto& host) { return host.getCurrentLedgerObjField(sfAccount); });
}
BENCHMARK(currentLedgerObjFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,30 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
currentLedgerObjNestedArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"home_le_inner_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().signerListHost(); },
[](auto& host) {
return host.getCurrentLedgerObjNestedArrayLen(
FieldLocator{{sfSignerEntries.getCode()}});
});
}
BENCHMARK(currentLedgerObjNestedArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,29 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
currentLedgerObjNestedFieldImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"home_le_inner"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.getCurrentLedgerObjNestedField(FieldLocator{{sfAccount.getCode()}});
});
}
BENCHMARK(currentLedgerObjNestedFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
delegateKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"delegate_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.delegateKeylet(
Fixtures::instance().alice().id(), Fixtures::instance().bob().id());
});
}
BENCHMARK(delegateKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
depositPreauthKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"deposit_preauth_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.depositPreauthKeylet(
Fixtures::instance().alice().id(), Fixtures::instance().bob().id());
});
}
BENCHMARK(depositPreauthKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
didKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"did_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.didKeylet(Fixtures::instance().alice().id()); });
}
BENCHMARK(didKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,55 @@
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/WasmLedger.h>
#include <cstdint>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "escrow_id";
constexpr std::string_view kImport =
R"( (import "host_lib" "escrow_id" (func $escrow_id (param i32 i32 i32 i32 i32 i32) (result i32)))
)";
constexpr std::string_view kBody =
"(call $escrow_id (i32.const 0) (i32.const 20) (i32.const 32) (i32.const 4) "
"(i32.const 64) (i32.const 32))";
void
escrowKeyletThroughVm(benchmark::State& state)
{
static auto const kData = [] {
auto seq = Bytes(4);
for (auto i = 0U; i < 4; ++i)
{
seq[i] = static_cast<std::uint8_t>((Fixtures::kSeq >> (8 * i)) & 0xFF);
}
return dataSegment(0, WasmLedger::toBytes(Fixtures::instance().alice().id())) +
dataSegment(32, seq);
}();
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(escrowKeyletThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
escrowKeyletImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.escrowKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(escrowKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,45 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/FloatConstants.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "float_add";
constexpr std::string_view kImport =
R"( (import "host_lib" "float_add" (func $float_add (param i32 i32 i32 i32 i32 i32 i32) (result i32)))
)";
constexpr std::string_view kBody =
"(call $float_add (i32.const 0) (i32.const 12) (i32.const 16) (i32.const 12) "
"(i32.const 64) (i32.const 12) (i32.const 0))";
void
floatAddThroughVm(benchmark::State& state)
{
static auto const kData =
dataSegment(0, FloatConstants::kPi) + dataSegment(16, FloatConstants::kTwo);
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(floatAddThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
floatAddImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.floatAdd(Fixtures::floatX(), Fixtures::floatY(), Fixtures::kRoundingMode);
});
}
BENCHMARK(floatAddImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatCompareImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_cmp"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatCompare(Fixtures::floatX(), Fixtures::floatY()); });
}
BENCHMARK(floatCompareImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatDivideImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_div"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.floatDivide(
Fixtures::floatX(), Fixtures::floatY(), Fixtures::kRoundingMode);
});
}
BENCHMARK(floatDivideImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatFromIntImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_from_int"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatFromInt(3141592653589793, Fixtures::kRoundingMode); });
}
BENCHMARK(floatFromIntImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatFromMantExpImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_from_mant_exp"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.floatFromMantExp(3141592653589793, -15, Fixtures::kRoundingMode);
});
}
BENCHMARK(floatFromMantExpImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,31 @@
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/UintTypes.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatFromStAmountImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_from_stamount"};
auto const amount =
STAmount{Issue{toCurrency("USD"), Fixtures::instance().alice().id()}, 1234567, -3};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&amount](auto& host) { return host.floatFromSTAmount(amount, Fixtures::kRoundingMode); });
}
BENCHMARK(floatFromStAmountImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,30 @@
#include <xrpl/basics/Number.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STNumber.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatFromStNumberImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_from_stnumber"};
auto const number = STNumber{sfNumber, Number(3141592653589793, -15)};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&number](auto& host) { return host.floatFromSTNumber(number, Fixtures::kRoundingMode); });
}
BENCHMARK(floatFromStNumberImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatFromUintImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_from_uint"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatFromUint(3141592653589793u, Fixtures::kRoundingMode); });
}
BENCHMARK(floatFromUintImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatMultiplyImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_mult"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.floatMultiply(
Fixtures::floatX(), Fixtures::floatY(), Fixtures::kRoundingMode);
});
}
BENCHMARK(floatMultiplyImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,42 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/FloatConstants.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "float_pow";
constexpr std::string_view kImport =
R"( (import "host_lib" "float_pow" (func $float_pow (param i32 i32 i32 i32 i32 i32) (result i32)))
)";
constexpr std::string_view kBody =
"(call $float_pow (i32.const 0) (i32.const 12) (i32.const 7) "
"(i32.const 64) (i32.const 12) (i32.const 0))";
void
floatPowerThroughVm(benchmark::State& state)
{
static auto const kData = dataSegment(0, FloatConstants::kPi);
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(floatPowerThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
floatPowerImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatPower(Fixtures::floatX(), 7, Fixtures::kRoundingMode); });
}
BENCHMARK(floatPowerImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatSubtractImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_sub"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.floatSubtract(
Fixtures::floatX(), Fixtures::floatY(), Fixtures::kRoundingMode);
});
}
BENCHMARK(floatSubtractImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
floatToIntImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"float_to_int"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatToInt(Fixtures::floatX(), Fixtures::kRoundingMode); });
}
BENCHMARK(floatToIntImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,41 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/FloatConstants.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "float_to_mant_exp";
constexpr std::string_view kImport =
R"( (import "host_lib" "float_to_mant_exp" (func $split (param i32 i32 i32 i32 i32 i32) (result i32)))
)";
constexpr std::string_view kBody =
"(call $split (i32.const 0) (i32.const 12) (i32.const 64) (i32.const 8) "
"(i32.const 128) (i32.const 4))";
void
floatToMantExpThroughVm(benchmark::State& state)
{
static auto const kData = dataSegment(0, FloatConstants::kPi);
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(floatToMantExpThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
floatToMantExpImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.floatToMantExp(Fixtures::floatX()); });
}
BENCHMARK(floatToMantExpImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,34 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <tx/wasm/fixtures/NftSetup.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
getNFTImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_uri"};
// A really minted token, so the lookup walks a real page rather than failing fast — a
// not-found answer would measure the rejection instead of the work.
static constexpr auto kUri = std::string_view{"ipfs://benchmark"};
// Its own ledger rather than the shared `Fixtures`: minting mutates state, and the shared
// one is deliberately read-only after construction.
static auto nft = WasmLedger{};
static auto const kOwner = nft.fund("benchNftOwner");
static auto const kMinted = mintNft(nft, kOwner, kUri);
benchmarkImpl(
state,
kWasmName,
[] { return nft.makeHost(); },
[](auto& host) { return host.getNFT(kOwner.id(), kMinted); });
}
BENCHMARK(getNFTImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,54 @@
#include <xrpl/protocol/Feature.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "amendment_enabled";
std::string const&
benchAmendment()
{
static auto const kValue = std::string{"TokenEscrow"};
return kValue;
}
void
isAmendmentEnabledByIdImpl(benchmark::State& state)
{
auto const feature = getRegisteredFeature(benchAmendment());
if (!feature.has_value())
{
state.SkipWithError("the benchmarked amendment is not registered");
return;
}
auto const id = *feature;
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&id](auto& host) { return host.isAmendmentEnabled(id); });
}
BENCHMARK(isAmendmentEnabledByIdImpl)->UseManualTime()->Iterations(kBenchIterations);
void
isAmendmentEnabledByNameImpl(benchmark::State& state)
{
// The gap over the id form is precisely what the shared price of 100 asserts does not exist.
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.isAmendmentEnabled(std::string_view{benchAmendment()}); });
}
BENCHMARK(isAmendmentEnabledByNameImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ledgerObjArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"le_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().cachedSignerListHost(); },
[](auto& host) { return host.getLedgerObjArrayLen(1, sfSignerEntries); });
}
BENCHMARK(ledgerObjArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ledgerObjFieldImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"le_field"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().cachedHost(); },
[](auto& host) { return host.getLedgerObjField(1, sfAccount); });
}
BENCHMARK(ledgerObjFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,29 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ledgerObjNestedArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"le_inner_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().cachedSignerListHost(); },
[](auto& host) {
return host.getLedgerObjNestedArrayLen(1, FieldLocator{{sfSignerEntries.getCode()}});
});
}
BENCHMARK(ledgerObjNestedArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,29 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ledgerObjNestedFieldImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"le_inner"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().cachedHost(); },
[](auto& host) {
return host.getLedgerObjNestedField(1, FieldLocator{{sfAccount.getCode()}});
});
}
BENCHMARK(ledgerObjNestedFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,38 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "ldgr_index";
constexpr std::string_view kImport =
R"( (import "host_lib" "ldgr_index" (func $ldgr_index (param i32 i32) (result i32)))
)";
void
ledgerSqnThroughVm(benchmark::State& state)
{
benchmarkThroughVm(
state, kWasmName, kImport, "", "(call $ldgr_index (i32.const 0) (i32.const 4))", [] {
return Fixtures::instance().host();
});
}
BENCHMARK(ledgerSqnThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
ledgerSqnImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getLedgerSqn(); });
}
BENCHMARK(ledgerSqnImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
mptokenIssuanceKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"mpt_issuance_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.mptokenIssuanceKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(mptokenIssuanceKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,31 @@
#include <xrpl/protocol/Indexes.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
mptokenKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"mptoken_id"};
auto const mptid = makeMptID(1, Fixtures::instance().alice().id());
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&mptid](auto& host) {
return host.mptokenKeylet(mptid, Fixtures::instance().bob().id());
});
}
BENCHMARK(mptokenKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftFlagsImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_flags"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getNFTFlags(Fixtures::instance().nftId()); });
}
BENCHMARK(nftFlagsImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftIssuerImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_issuer"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getNFTIssuer(Fixtures::instance().nftId()); });
}
BENCHMARK(nftIssuerImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftSequenceImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_serial"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getNFTSequence(Fixtures::instance().nftId()); });
}
BENCHMARK(nftSequenceImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftTaxonImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_taxon"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getNFTTaxon(Fixtures::instance().nftId()); });
}
BENCHMARK(nftTaxonImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftTransferFeeImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_xfer_fee"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getNFTTransferFee(Fixtures::instance().nftId()); });
}
BENCHMARK(nftTransferFeeImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
nftokenOfferKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"nft_offer_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.nftokenOfferKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(nftokenOfferKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
offerKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"offer_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.offerKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(offerKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
oracleKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"oracle_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.oracleKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(oracleKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
parentLedgerHashImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"parent_ldgr_hash"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getParentLedgerHash(); });
}
BENCHMARK(parentLedgerHashImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
parentLedgerTimeImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"parent_ldgr_time"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getParentLedgerTime(); });
}
BENCHMARK(parentLedgerTimeImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
paychannelKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"paychan_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.paychannelKeylet(
Fixtures::instance().alice().id(), Fixtures::instance().bob().id(), Fixtures::kSeq);
});
}
BENCHMARK(paychannelKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
permissionedDomainKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"permissioned_domain_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.permissionedDomainKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(permissionedDomainKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,69 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <cstddef>
#include <format>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "sha512_half";
constexpr std::string_view kImport =
R"( (import "host_lib" "sha512_half" (func $sha512_half (param i32 i32 i32 i32) (result i32)))
)";
void
sha512HalfThroughVm(benchmark::State& state)
{
// Zeroed guest memory is a perfectly good hash input: `sha512_half` validates nothing about
// its bytes, so there is no data segment to seed.
auto const body = std::format(
"(call $sha512_half (i32.const 0) (i32.const {}) (i32.const 8192) (i32.const 32))",
state.range(0));
// A hash is 32 bytes back to the guest whatever the input length, and the transfer budget
// counts only what the host writes — so the input sweep does not shrink the call count.
benchmarkThroughVm(
state,
kWasmName,
kImport,
"",
body,
[] { return Fixtures::instance().host(); },
callsWithinTransferBudget(32));
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK(sha512HalfThroughVm)
->RangeMultiplier(8)
->Range(8, xrpl::kMaxWasmDataLength)
->UseManualTime()
->Iterations(kBenchIterations);
void
sha512HalfImpl(benchmark::State& state)
{
auto const data = Bytes(static_cast<std::size_t>(state.range(0)), 0x42);
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&data](auto& host) {
return host.computeSha512HalfHash(Slice{data.data(), data.size()});
});
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK(sha512HalfImpl)
->RangeMultiplier(8)
->Range(8, xrpl::kMaxWasmDataLength)
->UseManualTime()
->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,24 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
signerListKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"signers_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.signerListKeylet(Fixtures::instance().alice().id()); });
}
BENCHMARK(signerListKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
ticketKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"ticket_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.ticketKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(ticketKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,40 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "trace";
constexpr std::string_view kMessage = "benchmark trace message";
constexpr std::string_view kData = "0123456789abcdef";
// The path a validator actually runs: journal pointed at a null sink.
void
traceDisabledImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.trace(kMessage, kData); });
}
BENCHMARK(traceDisabledImpl)->UseManualTime()->Iterations(kBenchIterations);
// The same call against a host whose sink records what it is given. The gap over the case above
// is the cost the flat 30 does not cover.
void
traceEnabledImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().tracingHost(); },
[](auto& host) { return host.trace(kMessage, kData); });
}
BENCHMARK(traceEnabledImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,31 @@
#include <xrpl/protocol/UintTypes.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
trustLineKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"trustline_id"};
auto const currency = toCurrency("USD");
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&currency](auto& host) {
return host.trustLineKeylet(
Fixtures::instance().alice().id(), Fixtures::instance().bob().id(), currency);
});
}
BENCHMARK(trustLineKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
txArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"tx_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getTxArrayLen(sfMemos); });
}
BENCHMARK(txArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <xrpl/protocol/SField.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
txFieldImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"tx_field"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getTxField(sfAccount); });
}
BENCHMARK(txFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,27 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
txNestedArrayLenImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"tx_inner_arr_len"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getTxNestedArrayLen(FieldLocator{{sfMemos.getCode()}}); });
}
BENCHMARK(txNestedArrayLenImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,55 @@
#include <xrpl/protocol/SField.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <cstdint>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "tx_inner";
constexpr std::string_view kImport =
R"( (import "host_lib" "tx_inner" (func $tx_inner (param i32 i32 i32 i32) (result i32)))
)";
constexpr std::string_view kBody =
"(call $tx_inner (i32.const 0) (i32.const 12) (i32.const 64) (i32.const 64))";
void
txNestedFieldThroughVm(benchmark::State& state)
{
// The locator bytes are seeded rather than stored by the guest, so the loop measures the host
// call and not three `i32.store`s.
static auto const kData = dataSegment(0, [] {
auto bytes = Bytes{};
for (auto const step : {sfMemos.getCode(), 0, sfMemoData.getCode()})
{
for (auto i = 0U; i < 4; ++i)
{
bytes.push_back(static_cast<std::uint8_t>((step >> (8 * i)) & 0xFF));
}
}
return bytes;
}());
benchmarkThroughVm(
state, kWasmName, kImport, kData, kBody, [] { return Fixtures::instance().host(); });
}
BENCHMARK(txNestedFieldThroughVm)->UseManualTime()->Iterations(kBenchIterations);
void
txNestedFieldImpl(benchmark::State& state)
{
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) { return host.getTxNestedField(Fixtures::memoLocator()); });
}
BENCHMARK(txNestedFieldImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,62 @@
#include <xrpl/basics/Slice.h>
#include <xrpl/protocol/Protocol.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <cstddef>
#include <format>
#include <string_view>
namespace xrpl::test::bench {
namespace {
constexpr std::string_view kWasmName = "set_data";
constexpr std::string_view kImport =
R"( (import "host_lib" "set_data" (func $set_data (param i32 i32) (result i32)))
)";
void
updateDataThroughVm(benchmark::State& state)
{
auto const body = std::format("(call $set_data (i32.const 0) (i32.const {}))", state.range(0));
benchmarkThroughVm(
state,
kWasmName,
kImport,
"",
body,
[] { return Fixtures::instance().host(); },
// `set_data` answers a scalar and writes nothing into guest memory, so the
// transfer budget does not constrain it however large the input gets.
callsWithinTransferBudget(0));
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK(updateDataThroughVm)
->RangeMultiplier(4)
->Range(8, kMaxWasmDataLength)
->UseManualTime()
->Iterations(kBenchIterations);
void
updateDataImpl(benchmark::State& state)
{
auto const data = Bytes(static_cast<std::size_t>(state.range(0)), 0x42);
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[&data](auto& host) { return host.updateData(Slice{data.data(), data.size()}); });
state.SetBytesProcessed(state.iterations() * state.range(0));
}
BENCHMARK(updateDataImpl)
->RangeMultiplier(4)
->Range(8, kMaxWasmDataLength)
->UseManualTime()
->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -0,0 +1,26 @@
#include <benchmark/benchmark.h>
#include <benchmarks/libxrpl/wasm/BenchFixtures.h>
#include <benchmarks/libxrpl/wasm/WasmBench.h>
#include <string_view>
namespace xrpl::test::bench {
namespace {
void
vaultKeyletImpl(benchmark::State& state)
{
static constexpr auto kWasmName = std::string_view{"vault_id"};
benchmarkImpl(
state,
kWasmName,
[] { return Fixtures::instance().host(); },
[](auto& host) {
return host.vaultKeylet(Fixtures::instance().alice().id(), Fixtures::kSeq);
});
}
BENCHMARK(vaultKeyletImpl)->UseManualTime()->Iterations(kBenchIterations);
} // namespace
} // namespace xrpl::test::bench

View File

@@ -1,488 +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/protocol/SeqProxy.h>
#include <xrpl/tx/wasm/HostFunc.h>
#include <xrpl/tx/wasm/WasmCommon.h>
#include <cstdint>
#include <expected>
#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()};
}
// FieldNotFound is a guest-returnable code (the contract handles a negative result);
// Unimplemented now maps to a fatal Fault::Internal (tecINTERNAL) that stops the run.
return std::unexpected(HostFunctionError::FieldNotFound);
}
[[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, SeqProxy::rawSequence(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, SeqProxy::rawSequence(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
}
void
trace(std::string_view const& msg, std::string_view const& data) const override
{
log(msg, [&data] { return data; });
}
[[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>
floatPower(Slice const& x, int32_t n, int32_t mode) const override
{
return wasm_float::floatPowerImpl(x, n, mode);
}
};
class TestHostFunctionsSink : public TestHostFunctions
{
test::StreamSink sink_;
public:
explicit TestHostFunctionsSink(test::jtx::Env& env)
: TestHostFunctions(env), sink_(beast::Severity::Debug)
{
j_ = beast::Journal(sink_);
}
test::StreamSink&
getSink()
{
return sink_;
}
};
} // namespace xrpl::test

View File

@@ -1,243 +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/WasmCommon.h>
#include <xrpl/tx/wasm/WasmVM.h>
#include <boost/algorithm/hex.hpp>
#include <cstdint>
#include <limits>
#include <source_location>
#include <string>
#include <vector>
namespace xrpl::test {
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
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, env.journal, 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, env.journal, escrowFunctionName);
BEAST_EXPECT(!isTesSuccess(re));
}
}
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, 50'207);
}
{
// max<int64_t>() gas
TestHostFunctions hfs(env);
auto re = runEscrowWasm(
allHFWasm, hfs, std::numeric_limits<int64_t>::max(), escrowFunctionName);
checkResult(re, 1, 50'207);
}
{ // 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, 28'901);
}
{ // 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, 28'901);
}
}
void
testCodecovWasm()
{
testcase("Codecov wasm test");
using namespace test::jtx;
Env env{*this};
auto const codecovWasm = hexToBytes(kCodecovTestsWasmHex);
TestHostFunctions hfs(env);
auto const allowance = 124'173;
auto re = runEscrowWasm(codecovWasm, hfs, allowance, escrowFunctionName);
checkResult(re, 1, allowance);
}
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
{
testBadWasm();
testEscrowWasmDN();
testCodecovWasm();
testSwapBytes();
}
};
BEAST_DEFINE_TESTSUITE(Wasm, app, xrpl);
} // namespace xrpl::test

View File

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

View File

@@ -1,180 +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-common-stdlib",
"xrpl-escrow-stdlib",
]
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-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 = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
]
[[package]]
name = "hybrid-array"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
"typenum",
]
[[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.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
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 = "xrpl-common-stdlib"
version = "0.8.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=error-and-trace#b008b097237ce0d1a2dffc72ba39dd9fc50020a9"
dependencies = [
"xrpl-macros",
]
[[package]]
name = "xrpl-escrow-stdlib"
version = "0.1.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=error-and-trace#b008b097237ce0d1a2dffc72ba39dd9fc50020a9"
dependencies = [
"xrpl-common-stdlib",
]
[[package]]
name = "xrpl-macros"
version = "0.1.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git?branch=error-and-trace#b008b097237ce0d1a2dffc72ba39dd9fc50020a9"
dependencies = [
"bs58",
"proc-macro2",
"quote",
"sha2",
"syn",
]

View File

@@ -1,22 +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-common-stdlib", branch = "error-and-trace" }
xrpl-escrow = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-escrow-stdlib", branch = "error-and-trace" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
opt-level = "z"
lto = true

View File

@@ -1,760 +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_escrow::current_tx::escrow_finish::EscrowFinish;
use xrpl_std::current_tx::traits::TransactionCommonFields;
use xrpl_std::host;
use xrpl_std::host::trace::TraceDataType;
use xrpl_std::host::trace::{trace, trace_acct_buf, trace_hex, trace_num};
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_hex("Parent ledger hash:", &hash_buffer);
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_acct_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_hex("Transaction Fee (serialized XRP amount):", &fee_buffer);
// 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_hex("Transaction Sequence:", &seq_buffer);
// 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_hex("Nested field:", &nested_buffer[..nested_result as usize]);
}
// 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_hex(
"Current object balance (serialized XRP amount):",
&balance_buffer,
);
} else {
let _ = trace_num(
"Current object balance length (non-XRP amount):",
balance_result as i64,
);
let _ = trace_hex(
"Current object balance:",
&balance_buffer[..balance_result as usize],
);
}
// 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_acct_buf("Current ledger object account:", &current_account_buffer);
}
// Test 3.2: get_current_ledger_obj_nested_field() - Nested field access
let locator = [
0x01_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8,
]; // Two int32s in little-endian: [1, 0]
let mut current_nested_buffer = [0u8; 32];
let current_nested_result = unsafe {
host::home_le_inner(
locator.as_ptr(),
locator.len(),
current_nested_buffer.as_mut_ptr(),
current_nested_buffer.len(),
)
};
if current_nested_result < 0 {
let _ = trace_num(
"INFO: get_current_ledger_obj_nested_field not applicable:",
current_nested_result as i64,
);
} else {
let _ = trace_num("Current nested field length:", current_nested_result as i64);
let _ = trace_hex(
"Current nested field:",
&current_nested_buffer[..current_nested_result as usize],
);
}
// 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_hex(
"Cached object balance (serialized XRP amount):",
&cached_balance_buffer,
);
} else {
let _ = trace_num(
"Cached object balance length (non-XRP amount):",
cached_balance_result as i64,
);
let _ = trace_hex(
"Cached object balance:",
&cached_balance_buffer[..cached_balance_result as usize],
);
}
// 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_hex(
"Cached nested field:",
&cached_nested_buffer[..cached_nested_result as usize],
);
}
// 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_hex("Account keylet:", &accountroot_id_buffer);
// 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_hex(
"Credential keylet:",
&credential_keylet_buffer[..credential_keylet_result as usize],
);
}
// 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_hex("Escrow keylet:", &escrow_keylet_buffer);
// 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_hex("Oracle keylet:", &oracle_keylet_buffer);
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_hex("Input data:", test_data);
let _ = trace_hex("SHA512 half hash:", &hash_output);
// 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_hex("NFT data:", &nft_buffer[..nft_result as usize]);
}
// Test 6.3: trace() - Debug logging with data
let trace_message = b"Test trace message";
let trace_data_payload = b"payload";
unsafe {
host::trace(
trace_message.as_ptr(),
trace_message.len(),
TraceDataType::AsHex as i32,
trace_data_payload.as_ptr(),
trace_data_payload.len(),
)
};
// Test 6.4: trace_num() - Debug logging with number
let test_number = 42i64;
trace_num("Test number trace", test_number);
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_hex("Successfully updated ledger entry with:", update_payload);
let _ = trace("SUCCESS: Data update functions");
0
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,180 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-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-common-stdlib",
"xrpl-escrow-stdlib",
]
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
]
[[package]]
name = "hybrid-array"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
"typenum",
]
[[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.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
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 = "xrpl-common-stdlib"
version = "0.9.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git#e88dc32a48fac9d0eb6e7239c8a38693221657f6"
dependencies = [
"xrpl-macros",
]
[[package]]
name = "xrpl-escrow-stdlib"
version = "0.9.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git#e88dc32a48fac9d0eb6e7239c8a38693221657f6"
dependencies = [
"xrpl-common-stdlib",
]
[[package]]
name = "xrpl-macros"
version = "0.9.0"
source = "git+https://github.com/ripple/xrpl-wasm-stdlib.git#e88dc32a48fac9d0eb6e7239c8a38693221657f6"
dependencies = [
"bs58",
"proc-macro2",
"quote",
"sha2",
"syn",
]

View File

@@ -1,19 +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-common-stdlib" }
xrpl-escrow = { git = "https://github.com/ripple/xrpl-wasm-stdlib.git", package = "xrpl-escrow-stdlib" }

View File

@@ -1,56 +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;
// Same wasm functype as the real binding, so this is not a second import of
// host_lib.trace. Loose i32 pointers exercise the out-of-bounds path.
#[link_name = "trace"]
pub fn trace_loose(
msg_read_ptr: i32,
msg_read_len: i32,
data_type: i32,
data_read_ptr: i32,
data_read_len: i32,
);
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,648 +0,0 @@
#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 =
"0061736d0100000001550c60027f7f017f60037f7f7f017f60047f7f7f7f017f60017f017f60067f7f7f7f7f7f017f"
"60037f7f7f0060057f7f7f7f7f0060087f7f7f7f7f7f7f7f017f60057f7f7f7f7f017f60017f0060027f7f00600001"
"7f02b1041808686f73745f6c69620874785f6669656c64000108686f73745f6c6962057472616365000608686f7374"
"5f6c69620a6c6467725f696e646578000008686f73745f6c696210706172656e745f6c6467725f74696d6500000868"
"6f73745f6c696210706172656e745f6c6467725f68617368000008686f73745f6c69620874785f696e6e6572000208"
"686f73745f6c69620a74785f6172725f6c656e000308686f73745f6c69621074785f696e6e65725f6172725f6c656e"
"000008686f73745f6c69620d686f6d655f6c655f6669656c64000108686f73745f6c69620d686f6d655f6c655f696e"
"6e6572000208686f73745f6c69620f686f6d655f6c655f6172725f6c656e000308686f73745f6c696215686f6d655f"
"6c655f696e6e65725f6172725f6c656e000008686f73745f6c69620863616368655f6c65000108686f73745f6c6962"
"0d63726564656e7469616c5f6964000708686f73745f6c696209657363726f775f6964000408686f73745f6c696209"
"6f7261636c655f6964000408686f73745f6c69620b7368613531325f68616c66000208686f73745f6c6962076e6674"
"5f757269000408686f73745f6c6962087365745f64617461000008686f73745f6c69620a6c655f6172725f6c656e00"
"0008686f73745f6c69620e6163636f756e74726f6f745f6964000208686f73745f6c6962106c655f696e6e65725f61"
"72725f6c656e000108686f73745f6c6962086c655f6669656c64000208686f73745f6c6962086c655f696e6e657200"
"08030b0a090a05050b000101030005030100110619037f01418080c0000b7f0041c698c0000b7f0041d098c0000b07"
"3504066d656d6f727902000d657363726f775f66696e697368001c0a5f5f646174615f656e6403010b5f5f68656170"
"5f6261736503020acf210a9d0101027f230041206b2201240020014200370310200142003703082001410036021820"
"00027f024041818020200141086a41141000220241004e0440200241144b0d01200241144704402000418180808078"
"36020441010c030b20002001280218360011200020012903103700092000200129030837000141000c020b20002002"
"36020441010c010b2000417336020441010b3a0000200141206a24000b5a01017f230041106b2202240020012d0000"
"41014604402002200134020437030841df97c000410b4101200241086a41081001000b200020012800113600102000"
"200129000937000820002001290001370000200241106a24000b1900200241214f0440000b20002002360204200020"
"013602000b1900200241094f0440000b20002002360204200020013602000bdd1e01077f230041c0036b2200240041"
"ea97c000411b4107410141001001418598c0004119410741014100100141b583c000412b4107410141001001200041"
"003602500240024002400240024002400240200041d0006a41041002220141004a044020002000280250220141ff81"
"fc0771410878200141187841ff81fc077172ad3703c00141e083c00041174101200041c0016a220241081001200041"
"003602800120004180016a41041003220141004c0d012000200028028001220141ff81fc0771410878200141187841"
"ff81fc077172ad3703c00141f783c00041134101200241081001200042003703d801200042003703d0012000420037"
"03c801200042003703c0012002412010042201412047044020002001ac3703a00141bd84c000412b4101200041a001"
"6a4108100141997f21030c080b418a84c00041134106200041c0016a220441201001419d84c0004120410741014100"
"100141aa85c000412e4107410141001001200041003602b001200042003703a801200042003703a001418180202000"
"41a0016a22024114100022014114470d0241d885c00041144104200241141001200042003703384188801820004138"
"6a22024108100022014108470d03200042083703c00141ec85c00041174101200441081001418386c0004128410620"
"02410810012000410036027841848008200041f8006a22024104100022014104470d0441ab86c00041154106200241"
"04100120004100360051200041013a005020004100360054200042003703d801200042003703d001200042003703c8"
"01200042003703c0010240200041d0006a4108200441201005220141004e044020002001ad3703800141c086c00041"
"14410120004180016a41081001200041306a20042001101a41d486c000410d41062000280230200028023410010c01"
"0b20002001ac3703800141e186c0004129410120004180016a410810010b20004183803c1006ac37038001418a87c0"
"004115410120004180016a22024108100120004189803c1006ac37038001419f87c000411341012002410810010240"
"200041d0006a41081007220141004e044020002001ad3703800141b287c000411441012002410810010c010b200020"
"01ac3703800141c687c000412d410120004180016a410810010b41f387c0004123410741014100100141e792c00041"
"3341074101410010012000420037033841828018200041386a220141081008220241004c0d05200241084604402000"
"42083703c001419a93c000412b4101200041c0016a4108100141c593c000412f41062001410810010c070b20002002"
"ad3703c00141f493c000412f4101200041c0016a41081001200041286a200041386a2002101b41a394c00041174106"
"2000280228200028022c10010c060b20002001ac3703c001418d85c000411d4101200041c0016a41081001419b7f21"
"030c060b20002001ac3703c00141e884c00041254101200041c0016a41081001419a7f21030c050b20002001ac3703"
"c001418289c000412a4101200041c0016a4108100141b77e21030c040b20002001ac3703c00141c188c00041c10041"
"01200041c0016a4108100141b67e21030c030b20002001ac3703c001419688c000412b4101200041c0016a41081001"
"41b57e21030c020b20002002ac3703c00141ba94c00041c5004101200041c0016a410810010b200041003602b00120"
"0042003703a801200042003703a001024041818020200041a0016a220241141008220141004a044041ff94c000411e"
"41042002411410010c010b20002001ac3703c001419d95c00041334101200041c0016a410810010b20004100360051"
"200041013a005020004100360054200042003703d801200042003703d001200042003703c801200042003703c00102"
"40200041d0006a4108200041c0016a220141201009220241004e044020002002ad3703800141d095c000411c410120"
"004180016a41081001200041206a20012002101a41ec95c000411541062000280220200028022410010c010b200020"
"02ac37038001418196c0004139410120004180016a410810010b20004183803c100aac3703800141ba96c000412441"
"0120004180016a2201410810010240200041d0006a4108100b220241004e044020002002ad3703800141de96c00041"
"1c41012001410810010c010b20002002ac3703800141fa96c000413d410120004180016a410810010b41b797c00041"
"28410741014100100141ac89c000412f4107410141001001200041c0016a2204101820004180016a22012004101920"
"0042003703b801200042003703b001200042003703a801200042003703a001024002400240024002402001200041a0"
"016a2202101d22014120460440200241204100100c220541004a044020002005ad3703c00141db89c0004123410120"
"0441081001200042003703782005200041f8006a22014108101e220241004c0d0220024108460440200042083703c0"
"0141fe89c000412a410120044108100141a88ac000412e41062001410810010c060b20002002ad3703c00141d68ac0"
"00412e4101200041c0016a41081001200041186a200041f8006a2002101b41848bc000411641062000280218200028"
"021c10010c050b20002005ac3703c00141bc8dc000413c4101200041c0016a220141081001200042003703d8012000"
"42003703d001200042003703c801200042003703c001410120014120101e22014100480d020c030b20002001ac3703"
"c001419090c000412e4101200041c0016a4108100141ef7c21030c050b20002002ac3703c001419a8bc000412b4101"
"200041c0016a410810010c020b20002001ac37035041f88dc00041c1004101200041d0006a410810010b2000410036"
"0039200041013a00382000410036003c4101200041386a200041c0016a101f2201410048044020002001ac37035041"
"b98ec00041354101200041d0006a410810010b410110202201410048044020002001ac37035041ee8ec00041324101"
"200041d0006a410810010b4101200041386a10212201410048044020002001ac37035041a08fc00041394101200041"
"d0006a410810010b41d98fc000413741074101410010010c010b20004100360039200041013a00382000410036003c"
"200042003703d801200042003703d001200042003703c801200042003703c00102402005200041386a200041c0016a"
"2201101f220241004e044020002002ad37035041c58bc000411b4101200041d0006a41081001200041106a20012002"
"101a41e08bc000411441062000280210200028021410010c010b20002002ac37035041f48bc00041314101200041d0"
"006a410810010b200020051020ac37035041a58cc00041234101200041d0006a22014108100102402005200041386a"
"1021220241004e044020002002ad37035041c88cc000411b41012001410810010c010b20002002ac37035041e38cc0"
"0041354101200041d0006a410810010b41988dc000412441074101410010010b41be90c000412f4107410141001001"
"200041c0016a22011018200041386a2204200110192000420037036820004200370360200042003703582000420037"
"0350024002402004200041d0006a2202101d2201412046044041ed90c000410f410620024120100120004200370398"
"012000420037039001200042003703880120004200370380010240200441142004411441fc90c00041092000418001"
"6a22014120100d220241004a0440200041086a20012002101a418491c000411241062000280208200028020c10010c"
"010b20002002ac3703c001419691c000413c4101200041c0016a410810010b200042003703b801200042003703b001"
"200042003703a801200042003703a00120004180808cc07e360270200041386a22044114200041f0006a4104200041"
"a0016a22024120100e22014120470d0141d291c000410e4106200241201001200042003703d801200042003703d001"
"200042003703c801200042003703c001200041808080d00236027420044114200041f4006a4104200041c0016a4120"
"100f2201412047044020002001ac370378419292c000411c4101200041f8006a4108100141887c21030c040b41e091"
"c000410e4106200041c0016a22044120100141ee91c00041244107410141001001418080c000412541074101410010"
"0120004200370398012000420037039001200042003703880120004200370380010240024041a580c0004117200041"
"80016a2202412010102201412046044041bc80c000410b410641a580c0004117100141c780c0004111410620024120"
"100120041018200041d0006a220620041019200042003703b801200042003703b001200042003703a8012000420037"
"03a00102404100200422036b410371220220036a220520034d0d0020020440200221010340200341003a0000200341"
"016a2103200141016b22010d000b0b200241016b4107490d000340200341003a0000200341076a41003a0000200341"
"066a41003a0000200341056a41003a0000200341046a41003a0000200341036a41003a0000200341026a41003a0000"
"200341016a41003a0000200341086a22032005470d000b0b200541800220026b2201417c716a220320054b04400340"
"20054100360200200541046a22052003490d000b0b024020032001410371220120036a22024f0d0020012205044003"
"40200341003a0000200341016a2103200541016b22050d000b0b200141016b4107490d000340200341003a00002003"
"41076a41003a0000200341066a41003a0000200341056a41003a0000200341046a41003a0000200341036a41003a00"
"00200341026a41003a0000200341016a41003a0000200341086a22032002470d000b0b20064114200041a0016a4120"
"20044180021011220141004c0d0120002001ad37033841d880c00041104101200041386a4108100120014181024f0d"
"0541e880c000410941062004200110010c020b20002001ac3703c00141e381c00041224101200041c0016a41081001"
"41a77b21030c050b20002001ac37033841f180c000412e4101200041386a410810010b419f81c0004112410641b181"
"c000410710012000422a3703384101210341b881c00041114101200041386a4108100141c981c000411a4107410141"
"001001418582c0004129410741014100100141ae82c000412810122201412847044020002001ac3703c001419b83c0"
"00411a4101200041c0016a4108100141c37a21030c040b41d682c0004127410641ae82c0004128100141fd82c00041"
"1e4107410141001001419e98c000412841074101410010010c030b20002001ac3703c00141ca92c000411d41012000"
"41c0016a41081001418b7c21030c020b20002001ac3703c00141ae92c000411c4101200041c0016a4108100141897c"
"21030c010b000b200041c0036a240020030b0c00200041142001412010140b0e002000418280182001200210160b0e"
"002000200141082002412010170b0a0020004183803c10130b0a0020002001410810150b0bd0180100418080c0000b"
"c6182d2d2d2043617465676f727920363a205574696c6974792046756e6374696f6e73202d2d2d48656c6c6f2c2058"
"52504c205741534d20776f726c6421496e70757420646174613a5348413531322068616c6620686173683a4e465420"
"64617461206c656e6774683a4e465420646174613a494e464f3a206765745f6e6674206661696c6564202865787065"
"63746564202d206e6f2073756368204e4654293a54657374207472616365206d6573736167657061796c6f61645465"
"7374206e756d626572207472616365535543434553533a205574696c6974792066756e6374696f6e734552524f523a"
"20636f6d707574655f7368613531325f68616c66206661696c65643a2d2d2d2043617465676f727920373a20446174"
"61205570646174652046756e6374696f6e73202d2d2d55706461746564206c656467657220656e7472792064617461"
"2066726f6d205741534d20746573745375636365737366756c6c792075706461746564206c656467657220656e7472"
"7920776974683a535543434553533a2044617461207570646174652066756e6374696f6e734552524f523a20757064"
"6174655f64617461206661696c65643a2d2d2d2043617465676f727920313a204c6564676572204865616465722046"
"756e6374696f6e73202d2d2d4c65646765722073657175656e6365206e756d6265723a506172656e74206c65646765"
"722074696d653a506172656e74206c656467657220686173683a535543434553533a204c6564676572206865616465"
"722066756e6374696f6e734552524f523a206765745f706172656e745f6c65646765725f686173682077726f6e6720"
"6c656e6774683a4552524f523a206765745f706172656e745f6c65646765725f74696d65206661696c65643a455252"
"4f523a206765745f6c65646765725f73716e206661696c65643a2d2d2d2043617465676f727920323a205472616e73"
"616374696f6e20446174612046756e6374696f6e73202d2d2d5472616e73616374696f6e204163636f756e743a5472"
"616e73616374696f6e20466565206c656e6774683a5472616e73616374696f6e20466565202873657269616c697a65"
"642058525020616d6f756e74293a5472616e73616374696f6e2053657175656e63653a4e6573746564206669656c64"
"206c656e6774683a4e6573746564206669656c643a494e464f3a206765745f74785f6e65737465645f6669656c6420"
"6e6f74206170706c696361626c653a5369676e657273206172726179206c656e6774683a4d656d6f73206172726179"
"206c656e6774683a4e6573746564206172726179206c656e6774683a494e464f3a206765745f74785f6e6573746564"
"5f61727261795f6c656e206e6f74206170706c696361626c653a535543434553533a205472616e73616374696f6e20"
"646174612066756e6374696f6e734552524f523a206765745f74785f6669656c642853657175656e6365292077726f"
"6e67206c656e6774683a4552524f523a206765745f74785f6669656c6428466565292077726f6e67206c656e677468"
"20286578706563746564203820627974657320666f7220585250293a4552524f523a206765745f74785f6669656c64"
"284163636f756e74292077726f6e67206c656e6774683a2d2d2d2043617465676f727920343a20416e79204c656467"
"6572204f626a6563742046756e6374696f6e73202d2d2d5375636365737366756c6c7920636163686564206f626a65"
"637420696e20736c6f743a436163686564206f626a6563742062616c616e6365206c656e677468202858525020616d"
"6f756e74293a436163686564206f626a6563742062616c616e6365202873657269616c697a65642058525020616d6f"
"756e74293a436163686564206f626a6563742062616c616e6365206c656e67746820286e6f6e2d58525020616d6f75"
"6e74293a436163686564206f626a6563742062616c616e63653a494e464f3a206765745f6c65646765725f6f626a5f"
"6669656c642842616c616e636529206661696c65643a436163686564206e6573746564206669656c64206c656e6774"
"683a436163686564206e6573746564206669656c643a494e464f3a206765745f6c65646765725f6f626a5f6e657374"
"65645f6669656c64206e6f74206170706c696361626c653a436163686564206f626a656374205369676e6572732061"
"72726179206c656e6774683a436163686564206e6573746564206172726179206c656e6774683a494e464f3a206765"
"745f6c65646765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c696361626c653a5355"
"43434553533a20416e79206c6564676572206f626a6563742066756e6374696f6e73494e464f3a2063616368655f6c"
"65646765725f6f626a206661696c65642028657870656374656420776974682074657374206669787475726573293a"
"494e464f3a206765745f6c65646765725f6f626a5f6669656c64206661696c65642061732065787065637465642028"
"6e6f20636163686564206f626a656374293a494e464f3a206765745f6c65646765725f6f626a5f6e65737465645f66"
"69656c64206661696c65642061732065787065637465643a494e464f3a206765745f6c65646765725f6f626a5f6172"
"7261795f6c656e206661696c65642061732065787065637465643a494e464f3a206765745f6c65646765725f6f626a"
"5f6e65737465645f61727261795f6c656e206661696c65642061732065787065637465643a535543434553533a2041"
"6e79206c6564676572206f626a6563742066756e6374696f6e732028696e7465726661636520746573746564294552"
"524f523a206163636f756e74726f6f745f6964206661696c656420666f722063616368696e6720746573743a2d2d2d"
"2043617465676f727920353a204b65796c65742047656e65726174696f6e2046756e6374696f6e73202d2d2d416363"
"6f756e74206b65796c65743a546573745479706543726564656e7469616c206b65796c65743a494e464f3a20637265"
"64656e7469616c5f6b65796c6574206661696c656420286578706563746564202d20696e7465726661636520697373"
"7565293a457363726f77206b65796c65743a4f7261636c65206b65796c65743a535543434553533a204b65796c6574"
"2067656e65726174696f6e2066756e6374696f6e734552524f523a206f7261636c655f6b65796c6574206661696c65"
"643a4552524f523a20657363726f775f6b65796c6574206661696c65643a4552524f523a206163636f756e74726f6f"
"745f6964206661696c65643a2d2d2d2043617465676f727920333a2043757272656e74204c6564676572204f626a65"
"63742046756e6374696f6e73202d2d2d43757272656e74206f626a6563742062616c616e6365206c656e6774682028"
"58525020616d6f756e74293a43757272656e74206f626a6563742062616c616e6365202873657269616c697a656420"
"58525020616d6f756e74293a43757272656e74206f626a6563742062616c616e6365206c656e67746820286e6f6e2d"
"58525020616d6f756e74293a43757272656e74206f626a6563742062616c616e63653a494e464f3a206765745f6375"
"7272656e745f6c65646765725f6f626a5f6669656c642842616c616e636529206661696c656420286d617920626520"
"6578706563746564293a43757272656e74206c6564676572206f626a656374206163636f756e743a494e464f3a2067"
"65745f63757272656e745f6c65646765725f6f626a5f6669656c64284163636f756e7429206661696c65643a437572"
"72656e74206e6573746564206669656c64206c656e6774683a43757272656e74206e6573746564206669656c643a49"
"4e464f3a206765745f63757272656e745f6c65646765725f6f626a5f6e65737465645f6669656c64206e6f74206170"
"706c696361626c653a43757272656e74206f626a656374205369676e657273206172726179206c656e6774683a4375"
"7272656e74206e6573746564206172726179206c656e6774683a494e464f3a206765745f63757272656e745f6c6564"
"6765725f6f626a5f6e65737465645f61727261795f6c656e206e6f74206170706c696361626c653a53554343455353"
"3a2043757272656e74206c6564676572206f626a6563742066756e6374696f6e736572726f725f636f64653d3d3d3d"
"20484f53542046554e4354494f4e532054455354203d3d3d54657374696e6720323620686f73742066756e6374696f"
"6e73535543434553533a20416c6c20686f73742066756e6374696f6e2074657374732070617373656421004d097072"
"6f64756365727302086c616e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e"
"39352e30202835393830373631366520323032362d30342d313429002c0f7461726765745f6665617475726573022b"
"0f6d757461626c652d676c6f62616c732b087369676e2d657874";
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 =
"0061736d01000000015c0c60067f7f7f7f7f7f017f60027f7f017f60047f7f7f7f017f60037f7f7f017f60077f7f7f"
"7f7f7f7f017f60087f7f7f7f7f7f7f7f017f60057f7f7f7f7f017f60017f017f60057f7f7f7f7f0060047f7f7f7f00"
"60017f006000017f02d8093608686f73745f6c6962057472616365000808686f73745f6c69620a6c6467725f696e64"
"6578000108686f73745f6c696210706172656e745f6c6467725f74696d65000108686f73745f6c696210706172656e"
"745f6c6467725f68617368000108686f73745f6c696208626173655f666565000108686f73745f6c696211616d656e"
"646d656e745f656e61626c6564000108686f73745f6c69620874785f6669656c64000308686f73745f6c69620e6163"
"636f756e74726f6f745f6964000208686f73745f6c69620863616368655f6c65000308686f73745f6c69620d686f6d"
"655f6c655f6669656c64000308686f73745f6c6962086c655f6669656c64000208686f73745f6c69620874785f696e"
"6e6572000208686f73745f6c69620d686f6d655f6c655f696e6e6572000208686f73745f6c6962086c655f696e6e65"
"72000608686f73745f6c69620a74785f6172725f6c656e000708686f73745f6c69620f686f6d655f6c655f6172725f"
"6c656e000708686f73745f6c69620a6c655f6172725f6c656e000108686f73745f6c69621074785f696e6e65725f61"
"72725f6c656e000108686f73745f6c696215686f6d655f6c655f696e6e65725f6172725f6c656e000108686f73745f"
"6c6962106c655f696e6e65725f6172725f6c656e000308686f73745f6c6962087365745f64617461000108686f7374"
"5f6c69620b7368613531325f68616c66000208686f73745f6c696209636865636b5f736967000008686f73745f6c69"
"62076e66745f757269000008686f73745f6c69620a6e66745f697373756572000208686f73745f6c6962096e66745f"
"7461786f6e000208686f73745f6c6962096e66745f666c616773000108686f73745f6c69620c6e66745f786665725f"
"666565000108686f73745f6c69620a6e66745f73657269616c000208686f73745f6c696208636865636b5f69640000"
"08686f73745f6c69620f666c6f61745f66726f6d5f75696e74000608686f73745f6c69620c74727573746c696e655f"
"6964000508686f73745f6c696206616d6d5f6964000008686f73745f6c69620d63726564656e7469616c5f69640005"
"08686f73745f6c69620a6d70746f6b656e5f6964000008686f73745f6c696209666c6f61745f636d70000208686f73"
"745f6c696209666c6f61745f616464000408686f73745f6c696209666c6f61745f737562000408686f73745f6c6962"
"0a666c6f61745f6d756c74000408686f73745f6c696209666c6f61745f646976000408686f73745f6c696209666c6f"
"61745f706f77000008686f73745f6c696209657363726f775f6964000008686f73745f6c69620f6d70745f69737375"
"616e63655f6964000008686f73745f6c69620c6e66745f6f666665725f6964000008686f73745f6c6962086f666665"
"725f6964000008686f73745f6c6962096f7261636c655f6964000008686f73745f6c69620a7061796368616e5f6964"
"000508686f73745f6c6962167065726d697373696f6e65645f646f6d61696e5f6964000008686f73745f6c69620974"
"69636b65745f6964000008686f73745f6c6962087661756c745f6964000008686f73745f6c69620b64656c65676174"
"655f6964000008686f73745f6c6962126465706f7369745f707265617574685f6964000008686f73745f6c69620664"
"69645f6964000208686f73745f6c69620a7369676e6572735f69640002030403090a0b05030100110619037f014180"
"80c0000b7f0041c698c0000b7f0041d098c0000b073504066d656d6f727902000d657363726f775f66696e69736800"
"380a5f5f646174615f656e6403010b5f5f686561705f6261736503020aa22e037201017f230041106b220424000240"
"02402000200147044020022003410741014100100020004100480d0120042000ad3703080c020b20042000ac370308"
"200220034101200441086a41081000200441106a24000f0b20042000ac3703080b418080c000410b4101200441086a"
"41081000000b2801017f230041106b2201240020012000ac37030841aa91c000410b4101200141086a41081000000b"
"832d02087f017e230041a0026b2200240041b591c0004123410741014100100020004100360260200041e0006a2201"
"41041001410441888ec000410a103620004100360260200141041002410441a683c000411010362000420037037820"
"0042003703702000420037036820004200370360200141201003412041a185c0004110103620004100360260200141"
"041004410441d888c000410810362000428182848890a0c080013703202000428182848890a0c08001370318200042"
"8182848890a0c080013703102000428182848890a0c0800137030841d891c000410e1005410141e691c00041111036"
"200041086a41201005410141e691c00041111036200041003602702000420037036820004200370360024002404181"
"802020014114100622014100480d00200141144b0440417321010c010b20014114460d0141808080807821010b2001"
"1037000b2000200029006c3700fd01200020002900673703f801200020002d00623a002e200020002f01603b012c20"
"00200028006336002f200020002903f801370033200020002900fd0137003820004200370378200042003703702000"
"42003703682000420037036002402000412c6a4114200041e0006a4120100722014120470440200141004e0d012001"
"1037000b200020002d00623a0042200020002f01603b01402000200029006f22083703800220002000280063360043"
"200020002900673700472000200837004f20002000290077370057200020002d007f3a005f200041406b4120410010"
"08410141f791c0004108103620004100360270200042003703682000420037036041818020200041e0006a22024114"
"1009411441a68fc000410d103620004100360270200042003703682000420037036041014181802020024114100a41"
"1441bf8cc0004108103602404100200041e4006a22046b410371220320046a220120044d0d00200304402003210503"
"40200441003a0000200441016a2104200541016b22050d000b0b200341016b4107490d000340200441003a00002004"
"41076a41003a0000200441066a41003a0000200441056a41003a0000200441046a41003a0000200441036a41003a00"
"00200441026a41003a0000200441016a41003a0000200441086a22042001470d000b0b2001413c20036b2203417c71"
"6a220420014b0440034020014100360200200141046a22012004490d000b0b024020042003410371220320046a2205"
"4f0d002003220104400340200441003a0000200441016a2104200141016b22010d000b0b200341016b4107490d0003"
"40200441003a0000200441076a41003a0000200441066a41003a0000200441056a41003a0000200441046a41003a00"
"00200441036a41003a0000200441026a41003a0000200441016a41003a0000200441086a22042005470d000b0b2000"
"41043602a00120004181802036026020004100360288022000420037038002200042003703f80120024104200041f8"
"016a22014114100b4114418388c0004108103620004100360288022000420037038002200042003703f80120022000"
"2802a00120014114100c411441e085c000410d103620004100360288022000420037038002200042003703f8014101"
"200220002802a00120014114100d4114418b86c000410810364189803c100e412041ff91c000410a10364189803c10"
"0f4120418992c000410f103641014189803c10104120419892c000410a1036200220002802a0011011412041a292c0"
"0041101036200220002802a0011012412041b292c000411510364101200220002802a0011013412041c792c0004110"
"10362000412c6a220341141014411441d792c000410810362000420037039002200042003703880220004200370380"
"02200042003703f801200220002802a001200141201015412041db82c000410b103641df92c000410c41eb92c00041"
"0b41f692c000410e10164101418493c00041091036200020002903203703c001200020002903183703b80120002000"
"2903103703b001200020002903083703a801200041003b0188022000420037038002200042003703f8012003411420"
"0041a8016a220541202001411210174112418c84c00041071036200041003602880220004200370380022000420037"
"03f801200541202001411410184114419386c000410a1036200041003602f80120054120200141041019410441c48a"
"c0004109103620054120101a4108418d93c0004109103620054120101b410a419693c000410c1036200041003602f8"
"012005412020014104101c410441818fc000410a103641a293c000410d410420034114100041a293c000410d410541"
"af93c0004108100041a293c000410d410541b793c00041081000417f41041003417141bf93c0004118103620004100"
"3602f8012001417f1003417141e686c00041181036200041003a00fa01200041003b01f801200141031003417d41f8"
"80c000411e1036200041003602f8012001418094ebdc0310034173419790c000411d10364102100e416f41d793c000"
"41191036417f20002802a0011011417141f093c000411810362002417f10114171418894c000411810362002418108"
"1011417441a094c00041191036200041e094ebdc036a220420002802a0011011417341b994c0004118103620004200"
"3703900220004200370388022000420037038002200042003703f801200341142004410820014120101d417341e283"
"c00041141036200042003703900220004200370388022000420037038002200042003703f801200341142003411420"
"014120101d417141eb84c00041161036200042003703900220004200370388022000420037038002200042003703f8"
"0120044108200141204100101e417341ea8ec000411710362000420037039002200042003703880220004200370380"
"02200042003703f801200220002802a001200141204100101e4171418185c00041201036200420002802a001410110"
"08417341d194c00041101036200220002802a00141011008417141e194c00041121036200042003703900220004200"
"370388022000420037038002200042003703f801200420002802a0012001412010074173418b8bc000411610362000"
"42003703900220004200370388022000420037038002200042003703f801200220002802a001200141201007417141"
"b683c00041181036200042003703900220004200370388022000420037038002200042003703f80120034114200341"
"14200420002802a00120014120101f417341c78cc000411d1036200042003703900220004200370388022000420037"
"038002200042003703f8012003411420034114200220002802a00120014120101f417141e489c000411f1036200042"
"003703900220004200370388022000420037038002200042003703f80141b298c0004114200420002802a001200141"
"201020417341a688c00041151036200042003703900220004200370388022000420037038002200042003703f80141"
"b298c0004114200220002802a001200141201020417141c787c000411b103620004200370390022000420037038802"
"2000420037038002200042003703f80141b298c000411441f394c0004114200141201020417141bb85c00041251036"
"200042003703900220004200370388022000420037038002200042003703f801418795c000412841b298c000411420"
"0141201020417141c98bc000412110362000200028013c3602dc01200020002901343702d4012000200029012c3702"
"cc01200041808080083602c801200041003b01f801200041c8016a2207411841b298c0004114200141021020417141"
"b185c000410a10362000422a3703e001200420002802a0014101200041e0016a41081000200041003b01f801410220"
"0141021006416f41af80c00041171036200041003b01f8014102200141021009416f419787c000411c103620004100"
"3b01f8014101410220014102100a416f41e682c000411710364102100e416f41d793c000411910364102100f416f41"
"af95c000411e1036410141021010416f41cd95c0004119103641d891c0004181081005417441e695c000411f103641"
"d891c00041c10010054174418596c000411a1036200041003b01f801200241810820014102100b417441f683c00041"
"161036200041003b01f801200241810820014102100c4174418b88c000411b1036200041003b01f801410120024181"
"0820014102100d4174419384c00041161036200241810810114174419f96c000411e103620024181081012417441bd"
"96c00041231036410120024181081013417441e096c000411e103620024181081014417441fe96c0004116103641a2"
"93c00041810841eb92c000410b41f692c000410e10164174418493c0004109103641a293c000410d41eb92c0004181"
"0841f692c000410e10164174418493c0004109103641a293c000410d41eb92c000410b41f692c00041810810164174"
"418493c00041091036200041003b01f8012002418108200141021015417441fe86c00041191036200041003b01f801"
"41b298c00041810841b298c0004114200141021020417441b58bc00041141036200041003b01f80120034114200341"
"142002418108200141021021417441c082c000411b1036200041003b01f80120074181082003411420014102102241"
"7441da80c000411e103641a293c000410d4107200420002802a0011000200042d487b6f4c7d4b1c0003700ec0141a2"
"93c000410d4103200041ec95ebdc036a22054108100041a293c000410d4105200420002802a0011000200541082000"
"41ec016a2204410810234173419497c0004114103620044108200541081023417341a897c00041141036200041003b"
"01f80120054108200441082001410241001024417341ac82c00041141036200041003b01f801200441082005410820"
"01410241001024417341ce83c00041141036200041003b01f80120054108200441082001410241001025417341a18b"
"c00041141036200041003b01f80120044108200541082001410241001025417341c680c00041141036200041003b01"
"f80120054108200441082001410241001026417341cd8ac00041151036200041003b01f80120044108200541082001"
"410241001026417341c781c00041151036200041003b01f80120054108200441082001410241001027417341b387c0"
"0041141036200041003b01f801200441082005410820014102410010274173419d86c00041141036200041003b01f8"
"0120054108410320014102410010284173419681c00041131036200042003703900220004200370388022000420037"
"038002200042003703f8012003411420034114200141201029417141ea8bc000411b10362000420037039002200042"
"00370388022000420037038002200042003703f801200341142003411420014120102a417141e287c0004121103620"
"0042003703900220004200370388022000420037038002200042003703f801200341142003411420014120102b4171"
"41a981c000411e1036200042003703900220004200370388022000420037038002200042003703f801200341142003"
"411420014120102c417141b48ec000411a103620004200370390022000420037038802200042003703800220004200"
"3703f801200341142003411420014120102d4171418b8fc000411b1036200042003703900220004200370388022000"
"420037038002200042003703f80120034114200341142003411420014120102e417141ce8ec000411c103620004200"
"3703900220004200370388022000420037038002200042003703f801200341142003411420014120102f417141e08d"
"c00041281036200042003703900220004200370388022000420037038002200042003703f801200341142003411420"
"0141201030417141b186c000411b1036200042003703900220004200370388022000420037038002200042003703f8"
"012003411420034114200141201031417141b490c000411a1036200220002802a00141001008417141bc97c000411b"
"1036200041003b01f80120034114200220002802a001200141021017417141cc86c000411a1036200041003b01f801"
"200220002802a001200141021018417141858cc000411d1036200041003b01f801200220002802a001200141021019"
"417141ce90c000411c1036200220002802a001101a417141d797c000411c1036200220002802a001101b417141f397"
"c000411f1036200041003602f801200220002802a00120014104101c417141a28cc000411d1036200041003b01f801"
"200220002802a0012001410210074171418b80c00041241036200041808080083602f401200041003b01f801200220"
"002802a001200041f4016a2205410420014102101d4171419f8dc000411e1036200041003b01f801200220002802a0"
"0122062003411420022006200141021021417141d28fc00041241036200041003b01f80120034114200220002802a0"
"01220620022006200141021021417141dc81c00041241036200041003b01f801200220002802a00120034114200141"
"021032417141838ac00041221036200041003b01f80120034114200220002802a001200141021032417141a984c000"
"41221036200041003b01f801200220002802a00120034114200141021033417141fd82c00041291036200041003b01"
"f80120034114200220002802a001200141021033417141e28ac00041291036200041003b01f801200220002802a001"
"2001410210344171418089c000411c1036200041003b01f801200220002802a00120054104200141021029417141b3"
"8fc000411f1036200041003b01f801200220002802a0012003411441f394c000411420014102101f417141c189c000"
"41231036200041003b01f80120034114200220002802a00141f394c000411420014102101f417141bd8dc000412310"
"36200041003b01f801200220002802a0012005410420014102102a4171419c89c00041251036200041003b01f80120"
"074118200220002802a001200141021022417141cb84c00041201036200041003b01f801200220002802a001200541"
"0420014102102b417141928ec00041221036200041003b01f801200220002802a0012005410420014102102c417141"
"ed85c000411e1036200041003b01f801200220002802a0012005410420014102102d417141a58ac000411f10362000"
"41003b01f801200220002802a001200341142005410420014102102e417141f68fc00041211036200041003b01f801"
"20034114200220002802a0012005410420014102102e417141ea90c00041211036200041003b01f801200220002802"
"a0012005410420014102102f4171418082c000412c1036200041003b01f801200220002802a0012001410210354171"
"41e088c00041201036200041003b01f801200220002802a001200541042001410210304171418b91c000411f103620"
"0041003b01f801200220002802a00120054104200141021031417141818dc000411e1036200041003b01f801200220"
"002802a001419298c0004120200141021017417141e48cc000411d103641a293c000410d4104200220002802a00110"
"0041a2a7abdd03410d4107419298c0004120100041a2a7abdd03410d410320044108100041a2a7abdd03410d410420"
"034114100041a2a7abdd03410d410541b793c00041081000200220002802a001410720024181081000200042013703"
"f8012002418108410120014108100041a293c000418108410320044108100041a293c0004181084104200341141000"
"41a293c000418108410541b793c0004108100041a293c000410d4105200220002802a0011000200041003b019e0220"
"0220002802a001200341142000419e026a41021022417141bb88c000411d103641a293c000410d41e3002002200028"
"02a0011000410141004104200341141000200041a0026a240041010f0b000b0b9d180200418080c0000b8715544553"
"54204641494c45446163636f756e74726f6f745f69645f77726f6e675f73697a655f6163636f756e745f696474785f"
"6669656c645f696e76616c69645f736669656c64666c6f61745f7375625f6f6f625f736c696365326d70746f6b656e"
"5f69645f746f6f5f6269675f736c6963655f6d70746964706172656e745f6c6467725f686173685f6275665f746f6f"
"5f736d616c6c666c6f61745f706f775f6f6f625f736c6963656e66745f6f666665725f69645f77726f6e675f73697a"
"655f75696e743332666c6f61745f6d756c745f6f6f625f736c6963653263726564656e7469616c5f69645f77726f6e"
"675f73697a655f6163636f756e745f6964327065726d697373696f6e65645f646f6d61696e5f69645f77726f6e675f"
"73697a655f6163636f756e745f6964666c6f61745f6164645f6f6f625f736c6963653163726564656e7469616c5f69"
"645f746f6f5f6269675f736c6963657368613531325f68616c666c655f6669656c645f696e76616c69645f73666965"
"6c646465706f7369745f707265617574685f69645f77726f6e675f73697a655f6163636f756e745f69643170617265"
"6e745f6c6467725f74696d656163636f756e74726f6f745f69645f77726f6e675f6c656e666c6f61745f6164645f6f"
"6f625f736c69636532636865636b5f69645f6f6f625f6c656e5f75333274785f696e6e65725f746f6f5f6269675f73"
"6c6963656e66745f7572696c655f696e6e65725f746f6f5f6269675f736c69636564656c65676174655f69645f7772"
"6f6e675f73697a655f6163636f756e745f6964326d70746f6b656e5f69645f77726f6e675f73697a655f6163636f75"
"6e745f6964636865636b5f69645f77726f6e675f6c656e5f753332666c6f61745f66726f6d5f75696e745f77726f6e"
"675f6c656e5f75696e743634706172656e745f6c6467725f68617368616d6d5f69645f6d7074616d6d5f69645f6c65"
"6e5f77726f6e675f6e6f6e5f7872705f63757272656e63795f6c656e686f6d655f6c655f696e6e65726f666665725f"
"69645f77726f6e675f73697a655f6163636f756e745f69646c655f696e6e65726e66745f697373756572666c6f6174"
"5f6469765f6f6f625f736c696365327469636b65745f69645f77726f6e675f73697a655f75696e7433326e66745f75"
"72695f77726f6e675f73697a655f75696e74323536706172656e745f6c6467725f686173685f6e65675f6c656e7368"
"613531325f68616c665f746f6f5f6269675f736c696365686f6d655f6c655f6669656c645f696e76616c69645f7366"
"69656c64666c6f61745f6469765f6f6f625f736c69636531616d6d5f69645f6c656e5f77726f6e675f6c656e5f6173"
"736574326d70745f69737375616e63655f69645f77726f6e675f73697a655f75696e74333274785f696e6e6572686f"
"6d655f6c655f696e6e65725f746f6f5f6269675f736c696365616d6d5f69645f6c656e5f6f6f625f6173736574326d"
"70746f6b656e5f69645f6d707469645f77726f6e675f6c656e677468626173655f6665657369676e6572735f69645f"
"77726f6e675f73697a655f6163636f756e745f69646469645f69645f77726f6e675f73697a655f6163636f756e745f"
"69646d70745f69737375616e63655f69645f77726f6e675f73697a655f6163636f756e745f696474727573746c696e"
"655f69645f77726f6e675f73697a655f6163636f756e745f69643174727573746c696e655f69645f77726f6e675f6c"
"656e5f63757272656e637964656c65676174655f69645f77726f6e675f73697a655f6163636f756e745f6964316f72"
"61636c655f69645f77726f6e675f73697a655f6163636f756e745f69646e66745f7461786f6e666c6f61745f6d756c"
"745f6f6f625f736c696365316465706f7369745f707265617574685f69645f77726f6e675f73697a655f6163636f75"
"6e745f6964326163636f756e74726f6f745f69645f6c656e5f6f6f62666c6f61745f7375625f6f6f625f736c696365"
"31616d6d5f69645f746f6f5f6269675f736c696365616d6d5f69645f6c656e5f77726f6e675f7872705f6375727265"
"6e63795f6c656e657363726f775f69645f77726f6e675f73697a655f75696e7433326e66745f6973737565725f7772"
"6f6e675f73697a655f75696e743235366e66745f73657269616c5f77726f6e675f73697a655f75696e743235366c65"
"5f6669656c6474727573746c696e655f69645f6c656e5f6f6f625f63757272656e63796e66745f7572695f77726f6e"
"675f73697a655f6163636f756e745f69647661756c745f69645f77726f6e675f73697a655f6163636f756e745f6964"
"636865636b5f69645f77726f6e675f73697a655f6163636f756e745f696474727573746c696e655f69645f77726f6e"
"675f73697a655f6163636f756e745f6964327065726d697373696f6e65645f646f6d61696e5f69645f77726f6e675f"
"73697a655f75696e7433326c6467725f696e6465786e66745f6f666665725f69645f77726f6e675f73697a655f6163"
"636f756e745f69646f666665725f69645f77726f6e675f73697a655f75696e7433327061796368616e5f69645f7772"
"6f6e675f73697a655f75696e743332666c6f61745f66726f6d5f75696e745f6c656e5f6f6f626e66745f7365726961"
"6c6f7261636c655f69645f77726f6e675f73697a655f75696e743332686f6d655f6c655f6669656c64657363726f77"
"5f69645f77726f6e675f73697a655f6163636f756e745f696463726564656e7469616c5f69645f77726f6e675f7369"
"7a655f6163636f756e745f6964317061796368616e5f69645f77726f6e675f73697a655f6163636f756e745f696431"
"706172656e745f6c6467725f686173685f6c656e5f746f6f5f6c6f6e677661756c745f69645f77726f6e675f73697a"
"655f75696e7433326e66745f7461786f6e5f77726f6e675f73697a655f75696e743235367061796368616e5f69645f"
"77726f6e675f73697a655f6163636f756e745f6964327469636b65745f69645f77726f6e675f73697a655f6163636f"
"756e745f69646572726f725f636f64653d2424242424205354415254494e47205741534d20455845435554494f4e20"
"2424242424746573745f616d656e646d656e74616d656e646d656e745f656e61626c656463616368655f6c6574785f"
"6172725f6c656e686f6d655f6c655f6172725f6c656e6c655f6172725f6c656e74785f696e6e65725f6172725f6c65"
"6e686f6d655f6c655f696e6e65725f6172725f6c656e6c655f696e6e65725f6172725f6c656e7365745f6461746174"
"657374206d65737361676574657374207075626b657974657374207369676e6174757265636865636b5f7369676e66"
"745f666c6167736e66745f786665725f66656574657374696e67207472616365400000000000005f40000000000000"
"00706172656e745f6c6467725f686173685f6e65675f70747274785f6172725f6c656e5f696e76616c69645f736669"
"656c6474785f696e6e65725f6172725f6c656e5f6e65675f70747274785f696e6e65725f6172725f6c656e5f6e6567"
"5f6c656e74785f696e6e65725f6172725f6c656e5f746f6f5f6c6f6e6774785f696e6e65725f6172725f6c656e5f70"
"74725f6f6f6263616368655f6c655f7074725f6f6f6263616368655f6c655f77726f6e675f6c656e55534430303030"
"303030303030303030303030300041af95c0000b8303686f6d655f6c655f6172725f6c656e5f696e76616c69645f73"
"6669656c646c655f6172725f6c656e5f696e76616c69645f736669656c64616d656e646d656e745f656e61626c6564"
"5f746f6f5f6269675f736c696365616d656e646d656e745f656e61626c65645f746f6f5f6c6f6e6774785f696e6e65"
"725f6172725f6c656e5f746f6f5f6269675f736c696365686f6d655f6c655f696e6e65725f6172725f6c656e5f746f"
"6f5f6269675f736c6963656c655f696e6e65725f6172725f6c656e5f746f6f5f6269675f736c6963657365745f6461"
"74615f746f6f5f6269675f736c696365666c6f61745f636d705f6f6f625f736c69636531666c6f61745f636d705f6f"
"6f625f736c6963653263616368655f6c655f77726f6e675f73697a655f75696e743235366e66745f666c6167735f77"
"726f6e675f73697a655f75696e743235366e66745f786665725f6665655f77726f6e675f73697a655f75696e743235"
"363030303030303030303030303030303030303030303030303030303030303031004d0970726f6475636572730208"
"6c616e6775616765010452757374000c70726f6365737365642d6279010572757374631d312e39352e302028353938"
"30373631366520323032362d30342d313429002c0f7461726765745f6665617475726573022b0f6d757461626c652d"
"676c6f62616c732b087369676e2d657874";
extern std::string const kBadAlignWasmHex =
"0061736d01000000011b046000017f60057f7f7f7f7f017f60067f7f7f7f7f7f017f60000002260203656e760f666c"
"6f61745f66726f6d5f75696e74000103656e7608636865636b5f6964000203050403000000050301000306470b7f00"
"4180080b7f00418088020b7f004180080b7f00418088040b7f00418088040b7f00418088080b7f004180080b7f0041"
"8088080b7f004180800c0b7f0041000b7f0041010b07cc0110066d656d6f72790200115f5f7761736d5f63616c6c5f"
"63746f72730002057465737431000307655f64617461310300057465737432000407655f6461746132030104746573"
"7400050c5f5f64736f5f68616e646c6503020a5f5f646174615f656e6403030b5f5f737461636b5f6c6f7703040c5f"
"5f737461636b5f6869676803050d5f5f676c6f62616c5f6261736503060b5f5f686561705f6261736503070a5f5f68"
"6561705f656e6403080d5f5f6d656d6f72795f6261736503090c5f5f7461626c655f62617365030a0a99020402000b"
"2801017f418108427f370000418108410841a308410c41001000220041a40828020020004100481b0b5f01017f419a"
"88024191a4cca00136010041928802428994ace0d0c1c38710370100418a88024281848ca0d0c0c183083701004181"
"8802417f360000418a8802411441818802410441a3880241201001220041a4880228020020004100481b0b8a010103"
"7f418108427f370000418108410841a308410c410010002100419a88024191a4cca00136010041928802428994ace0"
"d0c1c38710370100418a88024281848ca0d0c0c1830837010041818802417f36000041a4082802002101418a880241"
"1441818802410441a3880241201001220241a4880228020020024100481b2000200120004100481b6a0b007f097072"
"6f647563657273010c70726f6365737365642d62790105636c616e675f31392e312e352d776173692d73646b202868"
"747470733a2f2f6769746875622e636f6d2f6c6c766d2f6c6c766d2d70726f6a656374206162346235613264623538"
"32393538616631656533303861373930636664623432626432343732302900490f7461726765745f66656174757265"
"73042b0f6d757461626c652d676c6f62616c732b087369676e2d6578742b0f7265666572656e63652d74797065732b"
"0a6d756c746976616c7565";

View File

@@ -1,10 +0,0 @@
#pragma once
#include <string>
extern std::string const kLedgerSqnWasmHex;
extern std::string const kAllHostFunctionsWasmHex;
extern std::string const kAllKeyletsWasmHex;
extern std::string const kCodecovTestsWasmHex;
extern std::string const kBadAlignWasmHex;

View File

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

View File

@@ -5,15 +5,24 @@ include(verify_headers)
# Test requirements.
find_package(GTest REQUIRED)
# Single combined gtest binary built from the shared test helpers and all test
# modules below.
add_executable(
xrpl_tests
main.cpp
add_library(
xrpl.testkit.wasm
STATIC
helpers/Account.cpp
helpers/TestSink.cpp
helpers/TxTest.cpp
tx/wasm/fixtures/NftSetup.cpp
tx/wasm/fixtures/WasmLedger.cpp
tx/wasm/fixtures/WasmRun.cpp
)
target_include_directories(xrpl.testkit.wasm PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(
xrpl.testkit.wasm
PUBLIC xrpl.libxrpl xrpl_wasm_testkit_cxxbridge
)
add_dependencies(xrpl.testkit.wasm xrpl_crates)
add_executable(xrpl_tests main.cpp)
patch_nix_binary(xrpl_tests)
set_target_properties(
xrpl_tests
@@ -21,10 +30,10 @@ set_target_properties(
)
# Lets test sources include the shared helpers as <helpers/...>.
target_include_directories(xrpl_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(xrpl_tests PRIVATE GTest::gtest GTest::gmock xrpl.libxrpl)
target_link_libraries(xrpl_tests PRIVATE xrpl_wasm_testkit_cxxbridge)
add_dependencies(xrpl_tests xrpl_crates)
target_link_libraries(
xrpl_tests
PRIVATE GTest::gtest GTest::gmock xrpl.libxrpl xrpl.testkit.wasm
)
# One source subdirectory per module. Network unit tests are currently not
# supported on Windows.
@@ -55,6 +64,13 @@ foreach(module IN LISTS test_modules)
"${CMAKE_CURRENT_SOURCE_DIR}/${module}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${module}.cpp"
)
# The framework-free half of tx/wasm/fixtures/ is compiled into xrpl.testkit.wasm,
# which this binary links; the rest of that folder belongs here.
list(
FILTER sources
EXCLUDE
REGEX "/fixtures/(NftSetup|WasmLedger|WasmRun)\\.cpp$"
)
target_sources(xrpl_tests PRIVATE ${sources})
# Expose the module's private headers under their canonical include path.

View File

@@ -1,33 +0,0 @@
#pragma once
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/nft.h>
#include <helpers/Account.h>
#include <tx/wasm/RealHostFixture.h>
#include <cstdint>
#include <optional>
#include <string_view>
namespace xrpl::test {
struct NFTTest : RealHostFixture
{
static constexpr std::uint16_t kFlags = nft::kFlagTransferable | nft::kFlagBurnable;
static constexpr std::uint16_t kFee = 314;
static constexpr std::uint32_t kTaxon = 12345;
static constexpr std::uint32_t kSequence = 7;
static uint256
makeNftId(AccountID const& issuer);
// Mint a real NFToken owned by `issuer` (taxon 0) and return its id, read back from the
// owner's NFTokenPage. TxTest applies to the open ledger, which produces no metadata, so
// the id is recovered from ledger state rather than from the mint's metadata.
uint256
mintNFT(Account const& issuer, std::optional<std::string_view> uri = std::nullopt);
};
} // namespace xrpl::test

View File

@@ -6,8 +6,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <helpers/CaptureSink.h>
#include <tx/wasm/MockHostFunctions.h>
#include <tx/wasm/WasmFixture.h>
#include <tx/wasm/fixtures/MockHostFunctions.h>
#include <tx/wasm/fixtures/WasmRun.h>
#include <string>
#include <string_view>
@@ -29,7 +29,7 @@ constexpr std::string_view kRunnableWat = R"wat(
} // namespace
// `preflightEscrowWasm` takes no host, so this fixture holds none - which is the point of
// the signature, and what deriving from `WasmTest` would hide. Only a journal, to read the
// the signature, and what deriving from `MockVmTest` would hide. Only a journal, to read the
// refusal out of.
struct PreflightTest : testing::Test
{

View File

@@ -0,0 +1,90 @@
# WASM host-function tests — layering
These tests are deliberately **layered**: each layer isolates one thing, so a failure points at
one place instead of "somewhere in the stack." If a folder looks thin, the breadth it seems to be
missing lives in a sibling layer.
## The layers
| Layer | Location | host | VM | ledger | Answers |
| ------------------------------------- | --------------------------------------------------- | ---- | --- | ------ | ----------------------------------------------------------------------------------- |
| Engine / gas / limits / ABI | `crates/xrpl-wasm-vm`, `crates/xrpl-host-functions` | mock | ✓ | ✗ | gas, transfer budget, memory/field limits, preflight, VM limits, generated ABI |
| `host_context/` (`HostContextTest`) | `.../host_context` | mock | ✗ | ✗ | the `HostContext` marshalling shim alone (byte order, buffer sizing, `SField` xlat) |
| `host_calls/` (`HostCallTest`) | `.../host_calls` | mock | ✓ | ✗ | per-function **wire contract** — what the host was asked, what came back |
| `host_functions/` (`RealHostFixture`) | `.../host_functions` | real | ✗ | real | each function's **actual answer** vs. a real `TxTest` ledger |
| `e2e/` (`RealVmTest`) | `.../e2e` | real | ✓ | real | **full-stack integration** — VM + `HostContext` + real impl + real ledger |
Run the C++ side with:
```bash
./build/xrpl_tests --gtest_filter='*Impl.*:*Call.*:*E2e.*:WasmVMTest.*:WasmVMDeathTest.*:PreflightTest.*'
```
(707 tests, 136 suites.) The engine-level coverage is Rust: `cd crates && cargo test`.
## `fixtures/` — split by whether it needs a test framework
| | |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **No GTest** — the `xrpl.testkit.wasm` library | `WasmLedger` (real genesis ledger + the real host over it), `WasmRun` (WAT assembler), `NftSetup`, `FloatConstants` |
| **GTest** → `xrpl_tests` | `RealHostFixture` (`: testing::Test, WasmLedger` + `expectValue`/`expectError`/`expectKeyletMatches`), `FloatFixture`, `NFTFixture`, `MockHostFunctions`, `WasmFixture`, `RealVmTest`, `HostContextFixture` |
A benchmark wants a ledger and a host, not GTest's lifecycle. Both binaries link the library;
`xrpl.bench.wasm` links no GTest and no GMock at all.
Setup steps in `WasmLedger` and `NftSetup` **throw** (`fixtureFailed`) rather than using `EXPECT_`.
Not stylistic: an `EXPECT_` outside a running test is recorded and discarded, so a benchmark whose
escrow was never created would still run its host call, take the not-found path, and report a
cheap, plausible, completely wrong price. **If you add a setup step that can fail, throw.**
## Gas calibration
The benchmarks that price these host functions live in `src/benchmarks/libxrpl/wasm/`, mirroring
this tree one file per function, and have their own README. They link `xrpl.testkit.wasm` (above)
for the ledger and host, and no test framework.
## What `e2e/` covers — the rule
**`e2e/` covers every marshalling shape and cross-call convention exactly once. It does not cover
every function.** That is a completeness claim on the axis e2e uniquely tests, not a sample.
`host_calls` pins what the bridge _asks_ with a _canned_ answer; `host_functions` pins what the
real impl _answers_. The type system guarantees they agree on signatures. Nothing guarantees they
agree on **conventions** — units, endianness, buffer layout — because in neither test does a real
guest write bytes a real host reads. That is exactly the `seq`-as-little-endian-region bug: every
internal test passed, and it was caught by cross-checking the guest SDK.
Convention mismatch is a property of a call's **shape**, not of the function. All 19 keylets share
one shape, so a 19th keylet e2e proves nothing the 1st did. The inventory is meant to be exhaustive:
| Shape / convention | Covered by | Why it is its own row |
| ----------------------------------------- | -------------------------- | -------------------------------------------------------- |
| no-input scalar getter | `LedgerSqnE2e` | header read; the minimal call |
| field code in, bytes out (ledger object) | `CurrentLedgerObjFieldE2e` | `SField` translation over a real object |
| field code in, bytes out (transaction) | `TxFieldE2e` | a different source than a ledger object |
| region in, bytes out + `u32` region | `CacheLedgerObjE2e` | the 4-byte little-endian region convention |
| slot in, bytes out — **cross-call state** | `CacheLedgerObjE2e` | the slot table is the only host state outliving one call |
| locator (path of i32 steps) | `TxNestedFieldE2e` | a wire format the guest writes and the host walks |
| **two** output regions | `FloatToMantExpE2e` | two bounds checks, two writes, an ordering between them |
| write / mutation | `SetDataE2e` | the one thing a contract changes |
| **error** path from a real impl | `HostErrorE2e` | a soft code from a real failure, not a staged one |
| realistic multi-call contract | `HostFunctionTourE2e` | the old `all_host_functions` tour shape, as one test |
Adding a function needs no new e2e case unless it introduces a shape not in that table. Per-function
breadth lives in `host_functions/` and `host_calls/`, one case each.
## Out of scope
**The guest SDK** (`xrpl-std` / `xrpl-escrow`, external `xrpl-wasm-stdlib` repo) is not exercised
here — that is the SDK repo's own suite. These tests hand-write the ABI in WAT (raw imports,
literal field codes, hand-built byte layouts), deliberately bypassing all SDK code. Agreement is
verified _transitively_: the SDK repo tests the SDK against the ABI spec, this repo tests the host
against the same spec. That would not catch a drift where both diverge on an ambiguous point;
closing it needs a **cross-repo integration test** (compiled guests against a real host) in CI
where the Rust→wasm toolchain exists.
**Transactor-level (L5) tests** are deferred: the redesign does not yet wire `runEscrowWasm` into
the `EscrowFinish` transactor, so there is no caller under `src/xrpld`. When it is wired, these
need a home as C++ transactor tests over a real `Env` — `set_data` persistence (including on
`tecBYTECODE_REJECTED`), `sfGasUsed` / `sfVMReturnCode` in transaction metadata, and owner-reserve
accounting for a bytecode-bearing escrow. The layers here deliberately stop at the VM boundary.

View File

@@ -6,7 +6,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <tx/wasm/WasmFixture.h>
#include <tx/wasm/fixtures/WasmFixture.h>
#include <tx/wasm/fixtures/WasmRun.h>
#include <array>
#include <cstdint>
@@ -52,7 +53,7 @@ constexpr std::string_view kNoMemoryWat = R"wat(
} // namespace
class WasmVMTest : public WasmTest
class WasmVMTest : public MockVmTest
{
};

Some files were not shown because too many files have changed in this diff Show More