mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 22:30:57 +00:00
Improve docs
This commit is contained in:
@@ -176,8 +176,7 @@ fn generate(functions: &[ParsedHostFunction]) -> TokenStream {
|
||||
/// handing over a region clamped to the field cap — but it cannot take
|
||||
/// back what a method already put there. A host that wrote a truncated
|
||||
/// prefix and then reported the larger length would leave those bytes in
|
||||
/// guest memory behind a refusal the guest is told to ignore. C++'s
|
||||
/// `setData` is the reference point: it wrote only on a value that fit.
|
||||
/// guest memory behind a refusal the guest is told to ignore.
|
||||
pub trait HostFunctions {
|
||||
#(#trait_methods)*
|
||||
}
|
||||
|
||||
@@ -90,16 +90,15 @@ host_functions! {
|
||||
#[wasm_name = "base_fee"]
|
||||
fn get_base_fee(&self, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// Whether an amendment is enabled. The input is either its 32-byte id or its
|
||||
/// name; the answer is `1` if enabled and `0` if not. Unlike the getters, this
|
||||
/// reads an input region and returns the flag directly rather than writing bytes.
|
||||
/// Whether an amendment is enabled. The input is either its 32-byte id or its name;
|
||||
/// the answer is `1` if enabled and `0` if not.
|
||||
#[gas = 100]
|
||||
#[wasm_name = "amendment_enabled"]
|
||||
fn is_amendment_enabled(&self, amendment: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// Load the ledger object with the given 32-byte id into a cache slot, so later
|
||||
/// calls can read its fields. `cache_idx` selects the slot (1-based); `0` asks the
|
||||
/// host to assign a free one. Returns the slot used, or a negative error.
|
||||
/// host to assign a free one. Answers the slot used.
|
||||
#[gas = 5000]
|
||||
#[wasm_name = "cache_le"]
|
||||
fn cache_ledger_obj(&self, obj_id: &[u8], cache_idx: i32) -> HostResult<i32>;
|
||||
@@ -122,8 +121,8 @@ host_functions! {
|
||||
fn get_ledger_obj_field(&self, cache_idx: i32, field: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of a nested field of the transaction, reached by a
|
||||
/// `locator`: a path of little-endian `i32` steps (so its byte length is a
|
||||
/// non-zero multiple of 4). Reads the locator region and writes the field bytes.
|
||||
/// `locator`: a path of little-endian `i32` steps (so its byte length is a non-zero
|
||||
/// multiple of 4).
|
||||
#[gas = 110]
|
||||
#[wasm_name = "tx_inner"]
|
||||
fn get_tx_nested_field(&self, locator: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
@@ -150,8 +149,7 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The number of elements in an array field of the transaction, selected by its
|
||||
/// `SField` code. Answers the count directly, or a negative error (`NoArray` if
|
||||
/// the field is not an array). Reads and writes no memory.
|
||||
/// `SField` code. Answers the count directly; `NoArray` if the field is not an array.
|
||||
#[gas = 40]
|
||||
#[wasm_name = "tx_arr_len"]
|
||||
fn get_tx_array_len(&self, field: i32) -> HostResult<i32>;
|
||||
@@ -169,7 +167,7 @@ host_functions! {
|
||||
fn get_ledger_obj_array_len(&self, cache_idx: i32, field: i32) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in a nested array field of the transaction, reached by a
|
||||
/// `locator`. Reads the locator region and answers the count directly.
|
||||
/// `locator`.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "tx_inner_arr_len"]
|
||||
fn get_tx_nested_array_len(&self, locator: &[u8]) -> HostResult<i32>;
|
||||
@@ -186,8 +184,8 @@ host_functions! {
|
||||
#[wasm_name = "le_inner_arr_len"]
|
||||
fn get_ledger_obj_nested_array_len(&self, cache_idx: i32, locator: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// Verify `signature` over `message` under `pubkey`. Reads the three regions and
|
||||
/// answers `1` if the signature is valid, `0` if not, or a negative error.
|
||||
/// Verify `signature` over `message` under `pubkey`. Answers `1` if the signature
|
||||
/// is valid, `0` if not, or a negative error.
|
||||
#[gas = 300]
|
||||
#[wasm_name = "check_sig"]
|
||||
fn check_signature(
|
||||
@@ -198,28 +196,26 @@ host_functions! {
|
||||
) -> HostResult<i32>;
|
||||
|
||||
/// The 32-byte ledger key (keylet) of an account's `AccountRoot`, computed from a
|
||||
/// 20-byte account id. Reads the account region and writes the keylet.
|
||||
/// 20-byte account id.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "accountroot_id"]
|
||||
fn account_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an AMM, computed from its two assets. Each asset is a
|
||||
/// byte slice whose length selects its kind (24 = MPT, 20 = XRP, 40 = issued
|
||||
/// currency + issuer). Reads both asset regions and writes the keylet.
|
||||
/// The 32-byte keylet of an AMM, computed from its two assets. Each asset is a byte
|
||||
/// slice whose length selects its kind (24 = MPT, 20 = XRP, 40 = issued currency +
|
||||
/// issuer).
|
||||
#[gas = 450]
|
||||
#[wasm_name = "amm_id"]
|
||||
fn amm_keylet(&self, asset1: &[u8], asset2: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `Check`, computed from a 20-byte account id and its
|
||||
/// sequence number. `seq` is the guest's `u32` carried as its `i32` bit pattern.
|
||||
/// Reads the account region and writes the keylet.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "check_id"]
|
||||
fn check_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `Credential`, computed from the 20-byte subject and
|
||||
/// issuer account ids and a credential-type byte string. Reads all three regions
|
||||
/// and writes the keylet.
|
||||
/// issuer account ids and a credential-type byte string.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "credential_id"]
|
||||
fn credential_keylet(
|
||||
@@ -230,8 +226,8 @@ host_functions! {
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `Delegate` object, computed from the 20-byte account
|
||||
/// and the account it authorizes. Reads both account regions and writes the keylet.
|
||||
/// The 32-byte keylet of a `Delegate` object, computed from the 20-byte account and
|
||||
/// the account it authorizes.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "delegate_id"]
|
||||
fn delegate_keylet(
|
||||
@@ -242,8 +238,7 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `DepositPreauth`, computed from the 20-byte account and
|
||||
/// the account it authorizes to deposit. Reads both account regions and writes the
|
||||
/// keylet.
|
||||
/// the account it authorizes to deposit.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "deposit_preauth_id"]
|
||||
fn deposit_preauth_keylet(
|
||||
@@ -254,21 +249,19 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an account's `DID`, computed from its 20-byte account id.
|
||||
/// Reads the account region and writes the keylet.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "did_id"]
|
||||
fn did_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an `Escrow`, computed from the 20-byte owner account and
|
||||
/// its sequence number. `seq` is the guest's `u32` carried as its `i32` bit
|
||||
/// pattern. Reads the account region and writes the keylet.
|
||||
/// pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "escrow_id"]
|
||||
fn escrow_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `RippleState` (trust line), computed from two 20-byte
|
||||
/// account ids and a 20-byte currency. Reads all three regions and writes the
|
||||
/// keylet.
|
||||
/// account ids and a 20-byte currency.
|
||||
#[gas = 400]
|
||||
#[wasm_name = "trustline_id"]
|
||||
fn trust_line_keylet(
|
||||
@@ -280,8 +273,8 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an `MPTokenIssuance`, computed from the 20-byte issuer
|
||||
/// account and its sequence number. `seq` is the guest's `u32` carried as its
|
||||
/// `i32` bit pattern. Reads the account region and writes the keylet.
|
||||
/// account and its sequence number. `seq` is the guest's `u32` carried as its `i32`
|
||||
/// bit pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "mpt_issuance_id"]
|
||||
fn mptoken_issuance_keylet(
|
||||
@@ -292,14 +285,14 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an `MPToken`, computed from a 24-byte MPT issuance id and
|
||||
/// the 20-byte holder account. Reads both regions and writes the keylet.
|
||||
/// the 20-byte holder account.
|
||||
#[gas = 500]
|
||||
#[wasm_name = "mptoken_id"]
|
||||
fn mptoken_keylet(&self, mptid: &[u8], holder: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an `NFTokenOffer`, computed from the 20-byte owner account
|
||||
/// and its sequence number. `seq` is the guest's `u32` carried as its `i32` bit
|
||||
/// pattern. Reads the account region and writes the keylet.
|
||||
/// pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "nft_offer_id"]
|
||||
fn nftoken_offer_keylet(
|
||||
@@ -311,22 +304,20 @@ host_functions! {
|
||||
|
||||
/// The 32-byte keylet of an `Offer`, computed from the 20-byte owner account and
|
||||
/// its sequence number. `seq` is the guest's `u32` carried as its `i32` bit
|
||||
/// pattern. Reads the account region and writes the keylet.
|
||||
/// pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "offer_id"]
|
||||
fn offer_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of an `Oracle`, computed from the 20-byte owner account and
|
||||
/// its document id. `doc_id` is the guest's `u32` carried as its `i32` bit pattern.
|
||||
/// Reads the account region and writes the keylet.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "oracle_id"]
|
||||
fn oracle_keylet(&self, account: &[u8], doc_id: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `PayChannel`, computed from the 20-byte source account,
|
||||
/// the 20-byte destination account, and the channel's sequence number. `seq` is the
|
||||
/// guest's `u32` carried as its `i32` bit pattern. Reads both account regions and
|
||||
/// writes the keylet.
|
||||
/// guest's `u32` carried as its `i32` bit pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "paychan_id"]
|
||||
fn paychannel_keylet(
|
||||
@@ -339,7 +330,7 @@ host_functions! {
|
||||
|
||||
/// The 32-byte keylet of a `PermissionedDomain`, computed from the 20-byte owner
|
||||
/// account and its sequence number. `seq` is the guest's `u32` carried as its `i32`
|
||||
/// bit pattern. Reads the account region and writes the keylet.
|
||||
/// bit pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "permissioned_domain_id"]
|
||||
fn permissioned_domain_keylet(
|
||||
@@ -350,21 +341,19 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `SignerList`, computed from its 20-byte owner account.
|
||||
/// Reads the account region and writes the keylet.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "signers_id"]
|
||||
fn signer_list_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `Ticket`, computed from the 20-byte owner account and its
|
||||
/// ticket sequence number. `seq` is the guest's `u32` carried as its `i32` bit
|
||||
/// pattern. Reads the account region and writes the keylet.
|
||||
/// The 32-byte keylet of a `Ticket`, computed from the 20-byte owner account and
|
||||
/// its ticket sequence number. `seq` is the guest's `u32` carried as its `i32` bit
|
||||
/// pattern.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "ticket_id"]
|
||||
fn ticket_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 32-byte keylet of a `Vault`, computed from the 20-byte owner account and its
|
||||
/// sequence number. `seq` is the guest's `u32` carried as its `i32` bit pattern.
|
||||
/// Reads the account region and writes the keylet.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "vault_id"]
|
||||
fn vault_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
@@ -382,51 +371,48 @@ host_functions! {
|
||||
///
|
||||
/// It is also the one declaration that is **not** the wasm parameter order.
|
||||
/// `data_type` is the third wasm parameter, between the two regions, because that
|
||||
/// is where xrpld's `trace_proto` and the guest stdlib put it; `register.rs` takes
|
||||
/// the arguments in wasm order and calls this in declaration order.
|
||||
/// is where the guest stdlib declares it; `register.rs` takes the arguments in wasm
|
||||
/// order and calls this in declaration order.
|
||||
#[gas = 30]
|
||||
#[wasm_name = "trace"]
|
||||
fn trace(&self, msg: &str, data: &[u8], data_type: TraceDataType) -> HostResult<()>;
|
||||
|
||||
/// Stores `data` as the current object's data field, replacing whatever was there,
|
||||
/// and returns the number of bytes stored. Reads the data region; `DataFieldTooLarge`
|
||||
/// if it exceeds the host's limit.
|
||||
/// and returns the number of bytes stored; `DataFieldTooLarge` if it exceeds the
|
||||
/// host's limit.
|
||||
#[gas = 1000]
|
||||
#[wasm_name = "set_data"]
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The URI of the `NFToken` with id `nft_id` (32 bytes) held by the 20-byte
|
||||
/// `account`. Reads both regions and writes the URI bytes.
|
||||
/// `account`.
|
||||
#[gas = 5000]
|
||||
#[wasm_name = "nft_uri"]
|
||||
fn get_nft(&self, account: &[u8], nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The 20-byte issuer account encoded in the `NFToken` id `nft_id` (32 bytes).
|
||||
/// Reads the id region and writes the issuer bytes.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "nft_issuer"]
|
||||
fn get_nft_issuer(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The taxon encoded in the `NFToken` id `nft_id` (32 bytes). Reads the id region
|
||||
/// and writes the taxon as its four little-endian bytes.
|
||||
/// The taxon encoded in the `NFToken` id `nft_id` (32 bytes), as four little-endian
|
||||
/// bytes.
|
||||
#[gas = 60]
|
||||
#[wasm_name = "nft_taxon"]
|
||||
fn get_nft_taxon(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The flags encoded in the `NFToken` id `nft_id` (32 bytes). Reads the id region
|
||||
/// and returns the flags as the call's scalar result.
|
||||
/// The flags encoded in the `NFToken` id `nft_id` (32 bytes).
|
||||
#[gas = 60]
|
||||
#[wasm_name = "nft_flags"]
|
||||
fn get_nft_flags(&self, nft_id: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The transfer fee encoded in the `NFToken` id `nft_id` (32 bytes). Reads the id
|
||||
/// region and returns the fee as the call's scalar result.
|
||||
/// The transfer fee encoded in the `NFToken` id `nft_id` (32 bytes).
|
||||
#[gas = 60]
|
||||
#[wasm_name = "nft_xfer_fee"]
|
||||
fn get_nft_transfer_fee(&self, nft_id: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The sequence number encoded in the `NFToken` id `nft_id` (32 bytes). Reads the
|
||||
/// id region and writes the sequence as its four little-endian bytes.
|
||||
/// The sequence number encoded in the `NFToken` id `nft_id` (32 bytes), as four
|
||||
/// little-endian bytes.
|
||||
#[gas = 60]
|
||||
#[wasm_name = "nft_serial"]
|
||||
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
@@ -435,39 +421,35 @@ host_functions! {
|
||||
// holds opaquely and hands back to these functions. Inputs and outputs that are
|
||||
// floats are byte regions; `mode` is the rounding mode, a scalar the guest chooses.
|
||||
|
||||
/// A float built from the signed integer `x` under rounding `mode`. Writes the
|
||||
/// float bytes; no input region.
|
||||
/// A float built from the signed integer `x` under rounding `mode`.
|
||||
#[gas = 100]
|
||||
#[wasm_name = "float_from_int"]
|
||||
fn float_from_int(&self, x: i64, mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// A float built from the unsigned integer in the 8-byte region `x` under rounding
|
||||
/// `mode`. Reads the integer region and writes the float bytes.
|
||||
/// `mode`.
|
||||
#[gas = 130]
|
||||
#[wasm_name = "float_from_uint"]
|
||||
fn float_from_uint(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// A float built from the serialized `STAmount` in `amount` under rounding `mode`.
|
||||
/// Reads the amount region and writes the float bytes.
|
||||
#[gas = 150]
|
||||
#[wasm_name = "float_from_stamount"]
|
||||
fn float_from_stamount(&self, amount: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// A float built from the serialized `STNumber` in `number` under rounding `mode`.
|
||||
/// Reads the number region and writes the float bytes.
|
||||
#[gas = 150]
|
||||
#[wasm_name = "float_from_stnumber"]
|
||||
fn float_from_stnumber(&self, number: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float `x` rounded to a signed integer under rounding `mode`. Reads the float
|
||||
/// region and writes the integer as its eight little-endian bytes.
|
||||
/// The float `x` rounded to a signed integer under rounding `mode`, as eight
|
||||
/// little-endian bytes.
|
||||
#[gas = 130]
|
||||
#[wasm_name = "float_to_int"]
|
||||
fn float_to_int(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float `x` split into its mantissa and exponent. Reads the float region and
|
||||
/// writes the mantissa (eight little-endian bytes) and the exponent (four little-
|
||||
/// endian bytes) to two separate output regions.
|
||||
/// The float `x` split into its mantissa (eight little-endian bytes) and its exponent
|
||||
/// (four little-endian bytes), each written to its own output region.
|
||||
#[gas = 130]
|
||||
#[wasm_name = "float_to_mant_exp"]
|
||||
fn float_to_mant_exp(
|
||||
@@ -477,8 +459,7 @@ host_functions! {
|
||||
exponent_out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// A float built from `mantissa` and `exponent` under rounding `mode`. Writes the
|
||||
/// float bytes; no input region.
|
||||
/// A float built from `mantissa` and `exponent` under rounding `mode`.
|
||||
#[gas = 100]
|
||||
#[wasm_name = "float_from_mant_exp"]
|
||||
fn float_from_mant_exp(
|
||||
@@ -490,43 +471,37 @@ host_functions! {
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// Compares floats `x` and `y`, returning a negative, zero, or positive scalar as
|
||||
/// `x` is less than, equal to, or greater than `y`. Reads both float regions.
|
||||
/// `x` is less than, equal to, or greater than `y`.
|
||||
#[gas = 80]
|
||||
#[wasm_name = "float_cmp"]
|
||||
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The float sum `x + y` under rounding `mode`. Reads both float regions and writes
|
||||
/// the result bytes.
|
||||
/// The float sum `x + y` under rounding `mode`.
|
||||
#[gas = 160]
|
||||
#[wasm_name = "float_add"]
|
||||
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float difference `x - y` under rounding `mode`. Reads both float regions and
|
||||
/// writes the result bytes.
|
||||
/// The float difference `x - y` under rounding `mode`.
|
||||
#[gas = 160]
|
||||
#[wasm_name = "float_sub"]
|
||||
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float product `x * y` under rounding `mode`. Reads both float regions and
|
||||
/// writes the result bytes.
|
||||
/// The float product `x * y` under rounding `mode`.
|
||||
#[gas = 300]
|
||||
#[wasm_name = "float_mult"]
|
||||
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float quotient `x / y` under rounding `mode`. Reads both float regions and
|
||||
/// writes the result bytes.
|
||||
/// The float quotient `x / y` under rounding `mode`.
|
||||
#[gas = 300]
|
||||
#[wasm_name = "float_div"]
|
||||
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The `n`-th root of the float `x` under rounding `mode`. Reads the float region
|
||||
/// and writes the result bytes.
|
||||
/// The `n`-th root of the float `x` under rounding `mode`.
|
||||
#[gas = 5500]
|
||||
#[wasm_name = "float_root"]
|
||||
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The float `x` raised to the power `n` under rounding `mode`. Reads the float
|
||||
/// region and writes the result bytes.
|
||||
/// The float `x` raised to the power `n` under rounding `mode`.
|
||||
#[gas = 5500]
|
||||
#[wasm_name = "float_pow"]
|
||||
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! point. The engine pins `wasmi = { default-features = false }` precisely so a text
|
||||
//! assembler cannot reach the consensus path — wasmi's `wat` feature is on by default and
|
||||
//! makes `Module::new` accept text as readily as binary, which would make a transaction's
|
||||
//! validity a build flag (review finding A5). Putting `compile_wat` on the production bridge
|
||||
//! would link `wat` into xrpld even if nothing called it.
|
||||
//! validity a build flag. Putting `compile_wat` on the production bridge would link `wat`
|
||||
//! into xrpld even if nothing called it.
|
||||
//!
|
||||
//! Linked only into `xrpl_tests`, never into `libxrpl` or `xrpld`, so "no assembler in the
|
||||
//! shipped node" is a property of the link graph rather than a flag someone can flip.
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
//! The cxx bridge between the escrow wasm engine and xrpld.
|
||||
//!
|
||||
//! Three crossings. C++ calls `run_escrow` once per escrow finish; the engine's host
|
||||
//! calls come back out through the C++ `HostContext`, which `CxxHost` presents to the
|
||||
//! engine as an ordinary [`HostFunctions`] implementor. The ABI those calls speak is
|
||||
//! declared once, in `xrpl-host-functions`, so neither side of this file gets to
|
||||
//! restate a signature.
|
||||
//! Three crossings:
|
||||
//!
|
||||
//! `check_escrow` is the third, and it crosses in one direction only: screening a
|
||||
//! module needs no host, so nothing comes back out.
|
||||
//! - **In:** C++ calls `run_escrow`, once per escrow finish.
|
||||
//! - **Back out:** that run's host calls leave through the C++ `HostContext`, which
|
||||
//! `CxxHost` presents to the engine as an ordinary [`HostFunctions`] implementor.
|
||||
//! - **In only:** C++ screens a module with `check_escrow`. Screening needs no host,
|
||||
//! so nothing comes back out.
|
||||
//!
|
||||
//! **Neither direction may unwind into the other**, and the two halves of that are
|
||||
//! The ABI the host calls speak is declared once, in `xrpl-host-functions`, so neither
|
||||
//! side of this file gets to restate a signature.
|
||||
//!
|
||||
//! **Neither language may unwind into the other**, and the two halves of that are
|
||||
//! not symmetric:
|
||||
//!
|
||||
//! - A **Rust panic** is caught here, by `guarded`. Letting one reach C++ is
|
||||
//! undefined behaviour; `[profile.release]` turns overflow checks on, so this is a
|
||||
//! live path and not a formality.
|
||||
//! - A **C++ exception** is stopped on the C++ side: every `HostContext` method is
|
||||
//! `noexcept` and reports failure as a negative `HostError` code. That is what
|
||||
//! makes `guarded` sufficient — see its documentation.
|
||||
//! `noexcept` and catches its own. That is what makes `guarded` sufficient — see
|
||||
//! its documentation.
|
||||
//!
|
||||
//! Everything hand-written here is private, so the names above are code spans rather
|
||||
//! than links, and `cargo doc` needs `--document-private-items` to show any of it.
|
||||
@@ -168,7 +170,7 @@ mod ffi {
|
||||
/// The C++ side of the ABI: one method per host function, forwarding to
|
||||
/// `xrpl::HostFunctions`.
|
||||
///
|
||||
/// Every method is `noexcept` and answers with a code, so a host call cannot
|
||||
/// Every method is `noexcept` and catches everything, so a host call cannot
|
||||
/// unwind into the engine.
|
||||
///
|
||||
/// `cxx_name` on each method below is not cosmetic: the declarations keep the
|
||||
@@ -941,7 +943,7 @@ impl ffi::CheckResult {
|
||||
/// **Why catching here is enough.** An unwind can only be caught where every frame
|
||||
/// between the panic and the catch is Rust, and every frame here is: the engine and
|
||||
/// wasmi are Rust, and a host call cannot start a C++ unwind because each
|
||||
/// `HostContext` method is `noexcept` and answers with a code. So the only unwind
|
||||
/// `HostContext` method is `noexcept` and catches everything. So the only unwind
|
||||
/// that can reach this frame started in Rust, and this stops it.
|
||||
///
|
||||
/// [`AssertUnwindSafe`] is sound because nothing survives to be observed in a torn
|
||||
|
||||
@@ -166,7 +166,7 @@ pub(crate) fn read_borrowed<'a>(
|
||||
///
|
||||
/// The ABI transports these as a 4-byte region rather than a wasm scalar (the guest
|
||||
/// SDK passes `seq.to_le_bytes()`), so the region must be exactly four bytes;
|
||||
/// `InvalidParams` otherwise, matching the C-ABI wrapper's `getDataUInt32`.
|
||||
/// `InvalidParams` otherwise.
|
||||
pub(crate) fn read_u32_arg(bytes: &[u8]) -> HostResult<i32> {
|
||||
let arr: [u8; 4] = bytes.try_into().map_err(|_| HostError::InvalidParams)?;
|
||||
Ok(i32::from_le_bytes(arr))
|
||||
|
||||
@@ -126,6 +126,13 @@ fn is_entry_point(ty: &FuncType) -> bool {
|
||||
/// engine grants. One pass over the exports, since both rules read the same list and
|
||||
/// the export table is the only place either is visible.
|
||||
///
|
||||
/// **A memory or table the module keeps to itself is therefore not screened**: it is
|
||||
/// absent from the exports, and the store's limiter is what refuses it, at
|
||||
/// instantiation. That gap is wide for tables — Rust exports
|
||||
/// `__indirect_function_table` only under `--export-table`, so unexported is the
|
||||
/// normal shape — and narrow for memories, since a contract needs an exported one to
|
||||
/// make any host call at all.
|
||||
///
|
||||
/// A module faulting on both is reported by whichever it declares first. Neither
|
||||
/// fault explains the other, so there is no precedence to preserve — only the need
|
||||
/// for every node to reach the same verdict, which export order already gives.
|
||||
|
||||
@@ -566,10 +566,10 @@ fn what_static_screening_cannot_see() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A start section is guest code, so screening cannot see whether it traps — but it
|
||||
/// no longer has to. A trap is the guest's fault wherever it happens, so the run
|
||||
/// charges the contract for what it burned instead of reporting a module the node
|
||||
/// should have screened.
|
||||
/// A start section is guest code, so screening cannot see whether it traps — and does
|
||||
/// not have to. A trap is the guest's fault wherever it happens, so the run charges the
|
||||
/// contract for what it burned instead of reporting a module the node should have
|
||||
/// screened.
|
||||
#[test]
|
||||
fn a_start_section_screening_cannot_see_is_charged_as_a_trap() {
|
||||
let host = FakeHost::new();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! What the engine refuses outright: modules it will not compile, will not
|
||||
//! instantiate, or cannot find an entry point in — plus the linear-memory cap.
|
||||
//! instantiate, or cannot find an entry point in — plus the memory and table caps.
|
||||
//!
|
||||
//! These are the sandbox's outer wall. Everything here fails the run rather than
|
||||
//! returning a code to the guest, so each test reads the failure's message.
|
||||
@@ -262,7 +262,7 @@ fn disabled_features() -> Vec<(&'static str, Vec<&'static str>, &'static str, &'
|
||||
///
|
||||
/// `wasm_custom_page_sizes` and `wasm_wide_arithmetic` are off by default in wasmi
|
||||
/// 1.1 (`engine/config.rs:72,74`), so their rows guard against wasmi changing that
|
||||
/// default rather than against our own config.
|
||||
/// default rather than against this engine's own config.
|
||||
#[test]
|
||||
fn every_disabled_feature_is_refused_by_name() {
|
||||
let host = FakeHost::new();
|
||||
@@ -279,9 +279,9 @@ fn every_disabled_feature_is_refused_by_name() {
|
||||
}
|
||||
|
||||
/// The three knobs [`every_disabled_feature_is_refused_by_name`] cannot cover. The
|
||||
/// engine is a process-wide `LazyLock`, so a test observes the one configuration we
|
||||
/// build: a knob masked by another, or with no caller-visible effect, has no
|
||||
/// distinguishing module.
|
||||
/// engine is a process-wide `LazyLock`, so a test observes the one configuration
|
||||
/// `build_wasm_engine` makes: a knob masked by another, or with no caller-visible
|
||||
/// effect, has no distinguishing module.
|
||||
#[test]
|
||||
fn the_knobs_without_a_module_of_their_own() {
|
||||
let host = FakeHost::new();
|
||||
|
||||
@@ -27,12 +27,11 @@ enum class TraceDataType : std::int32_t;
|
||||
// access - and lowering its typed `std::expected` result onto the ABI's wire form.
|
||||
//
|
||||
// Every method is `noexcept`, and every body catches everything: a C++ exception
|
||||
// unwinding into the Rust frames that called it would be undefined behaviour, so a
|
||||
// failure leaves here as -1, which the engine reads as a fatal error and reports as
|
||||
// `tecINTERNAL`.
|
||||
// unwinding into the Rust frames that called it would be undefined behaviour, so a caught
|
||||
// one leaves here as `HostFunctionError::InternalFatal`, which the engine reads as a fatal
|
||||
// error and reports as `tecINTERNAL`.
|
||||
//
|
||||
// Not an owner: it borrows `hf` for the length of one run. Declared `struct` because the
|
||||
// Rust side only ever sees an opaque pointer.
|
||||
// Not an owner: it borrows the `HostFunctions` it is built over for the length of one run.
|
||||
class HostContext
|
||||
{
|
||||
// Non-const so a host function that mutates (`cacheLedgerObj`, `updateData`) can be
|
||||
|
||||
@@ -18,11 +18,12 @@ bridge.
|
||||
`ApplyContext&`. Bodies are split across `HostFuncImpl*.cpp` by category.
|
||||
- **`HostContext.h`** — the bridge's C++ half: an ABI-shaped, `noexcept` view of
|
||||
`HostFunctions` that the engine calls back into. Nothing may unwind into Rust, so every
|
||||
method routes through `guarded()`.
|
||||
method catches everything — through `guarded()`, except `trace`, which answers the guest
|
||||
nothing and so has its own catch that only logs.
|
||||
- **`WasmCommon.h`** — the shared vocabulary: `HostFunctionError` (the codes a contract
|
||||
sees), `Bytes`, `FieldLocator`, `WasmTER`, `adjustWasmEndianess`, which is where the
|
||||
boundary's byte order is decided, and `guarded()`, the one catch every crossing of the
|
||||
bridge's C++ half goes through.
|
||||
boundary's byte order is decided, and `guarded()`, the catch that turns a throwing host
|
||||
body into a code the engine can read.
|
||||
|
||||
## Host functions
|
||||
|
||||
|
||||
@@ -50,9 +50,9 @@ enum class HostFunctionError : int32_t {
|
||||
// tecINTERNAL rather than the contract being handed a code to interpret. `guarded`
|
||||
// answers it for a host body that throws.
|
||||
//
|
||||
// Outside the -1 ..= -20 range that a contract reads, and the only entry that is: it
|
||||
// needs no number in that range, and INT32_MIN cannot collide with a code appended
|
||||
// above. Negative so that a reader treating it as an ordinary failure is still right.
|
||||
// The only entry outside the -1 ..= -20 range a contract reads: it needs no number
|
||||
// there, and INT32_MIN cannot collide with a code appended above. Negative so that a
|
||||
// reader treating it as an ordinary failure is still right.
|
||||
InternalFatal = std::numeric_limits<int32_t>::min(),
|
||||
};
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <xrpl/tx/wasm/WasmCommon.h>
|
||||
|
||||
#include <rust/cxx.h>
|
||||
// For `TraceDataType`, which the bridge declares and this header defines.
|
||||
// For `TraceDataType`: declared in the cxx bridge, defined in the header it generates.
|
||||
#include <xrpl_wasm_vm_ffi_cxxbridge/lib.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -76,8 +76,7 @@ answerScalar(rust::Slice<std::uint8_t> out, T value)
|
||||
|
||||
// Decode an asset from its wire bytes, whose length selects the kind: an MPT id, a
|
||||
// bare currency (which must be XRP), or a currency followed by an issuer (which must
|
||||
// not be XRP). Any other length is malformed. This mirrors `getDataAsset` in the
|
||||
// C-ABI wrapper the wasm engine replaces.
|
||||
// not be XRP). Any other length is malformed.
|
||||
std::expected<Asset, HostFunctionError>
|
||||
parseAsset(rust::Slice<std::uint8_t const> bytes)
|
||||
{
|
||||
@@ -111,7 +110,7 @@ parseAsset(rust::Slice<std::uint8_t const> bytes)
|
||||
}
|
||||
|
||||
// Decode a `uint64` from its eight wire bytes, in the wire's byte order. The region
|
||||
// must be exactly eight bytes, mirroring `getDataUnsigned` in the C-ABI wrapper.
|
||||
// must be exactly eight bytes, else `InvalidParams`.
|
||||
std::expected<std::uint64_t, HostFunctionError>
|
||||
parseUint64(rust::Slice<std::uint8_t const> bytes)
|
||||
{
|
||||
@@ -126,7 +125,7 @@ parseUint64(rust::Slice<std::uint8_t const> bytes)
|
||||
}
|
||||
|
||||
// Deserialize an `ST` object from its wire bytes; `InvalidParams` if the bytes are not
|
||||
// a well-formed one. Mirrors the try/catch around `SerialIter` in the C-ABI wrapper.
|
||||
// a well-formed one, which `SerialIter` reports by throwing.
|
||||
template <class T>
|
||||
std::expected<T, HostFunctionError>
|
||||
parseST(rust::Slice<std::uint8_t const> bytes)
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace xrpl::test {
|
||||
|
||||
// A mock of the host the wasm engine calls back into.
|
||||
//
|
||||
// Only the methods the ABI currently declares are mocked, and that is deliberate: the ~60
|
||||
// others keep `HostFunctions`' own `std::unexpected(Unimplemented)`, so a contract reaching
|
||||
// for something the ABI has not declared yet fails the way production would. Add a
|
||||
// `MOCK_METHOD` here when the matching entry is added to `host_functions!`.
|
||||
// Only the methods the tests beside it exercise are mocked, and that is deliberate: the
|
||||
// rest keep `HostFunctions`' own `std::unexpected(Unimplemented)`, so a contract reaching
|
||||
// for one fails the way production would. Add a `MOCK_METHOD` here when a test needs to
|
||||
// say what that host function answers.
|
||||
struct MockHostFunctions : HostFunctions
|
||||
{
|
||||
explicit MockHostFunctions(beast::Journal journal) : HostFunctions(journal)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <tx/wasm/WasmFixture.h>
|
||||
// For `TraceDataType`, which the bridge declares and this header defines.
|
||||
// For `TraceDataType`: declared in the cxx bridge, defined in the header it generates.
|
||||
#include <xrpl_wasm_vm_ffi_cxxbridge/lib.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -45,8 +45,8 @@ serialized(STAmount const& amount)
|
||||
|
||||
} // namespace
|
||||
|
||||
// trace — a message, a data type, and a buffer holding what that type says. One import for
|
||||
// what were five, so what a test varies is the type rather than the function.
|
||||
// trace — a message, a data type, and a buffer holding what that type says. One import
|
||||
// covers every rendering, so what a test varies is the type rather than the function.
|
||||
//
|
||||
// The buffer arrives as bytes and leaves as text: `HostContext` renders it, and the host is
|
||||
// handed the finished line. So a test says which renderer the type selected.
|
||||
|
||||
Reference in New Issue
Block a user