mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 06:10:58 +00:00
Merge pull request #5 from XRPLF/Wasm-vm-redesign-host-functions
This commit is contained in:
@@ -75,11 +75,306 @@ host_functions! {
|
||||
#[wasm_name = "ldgr_index"]
|
||||
fn get_ledger_sqn(&self, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The close time of the parent (last-closed) ledger, as 4 little-endian bytes.
|
||||
#[gas = 60]
|
||||
#[wasm_name = "parent_ldgr_time"]
|
||||
fn get_parent_ledger_time(&self, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The hash of the parent (last-closed) ledger, as 32 bytes.
|
||||
#[gas = 60]
|
||||
#[wasm_name = "parent_ldgr_hash"]
|
||||
fn get_parent_ledger_hash(&self, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The base fee of the ledger being built, in drops, as 4 little-endian bytes.
|
||||
#[gas = 60]
|
||||
#[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.
|
||||
#[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.
|
||||
#[gas = 5000]
|
||||
#[wasm_name = "cache_le"]
|
||||
fn cache_ledger_obj(&self, obj_id: &[u8], cache_idx: i32) -> HostResult<i32>;
|
||||
|
||||
/// The serialized bytes of one field of the transaction being executed, selected
|
||||
/// by its `SField` code.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "tx_field"]
|
||||
fn get_tx_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of one field of the current (escrow) ledger object.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "home_le_field"]
|
||||
fn get_current_ledger_obj_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of one field of a previously cached ledger object,
|
||||
/// selected by its cache slot and the field's `SField` code.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "le_field"]
|
||||
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.
|
||||
#[gas = 110]
|
||||
#[wasm_name = "tx_inner"]
|
||||
fn get_tx_nested_field(&self, locator: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of a nested field of the current (escrow) ledger object,
|
||||
/// reached by a `locator`, as with [`Self::get_tx_nested_field`].
|
||||
#[gas = 110]
|
||||
#[wasm_name = "home_le_inner"]
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
&self,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// The serialized bytes of a nested field of a previously cached ledger object,
|
||||
/// selected by its cache slot and reached by a `locator`.
|
||||
#[gas = 110]
|
||||
#[wasm_name = "le_inner"]
|
||||
fn get_ledger_obj_nested_field(
|
||||
&self,
|
||||
cache_idx: i32,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[gas = 40]
|
||||
#[wasm_name = "tx_arr_len"]
|
||||
fn get_tx_array_len(&self, field: i32) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in an array field of the current (escrow) ledger
|
||||
/// object, as with [`Self::get_tx_array_len`].
|
||||
#[gas = 40]
|
||||
#[wasm_name = "home_le_arr_len"]
|
||||
fn get_current_ledger_obj_array_len(&self, field: i32) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in an array field of a previously cached ledger object,
|
||||
/// selected by its cache slot and `SField` code.
|
||||
#[gas = 40]
|
||||
#[wasm_name = "le_arr_len"]
|
||||
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.
|
||||
#[gas = 70]
|
||||
#[wasm_name = "tx_inner_arr_len"]
|
||||
fn get_tx_nested_array_len(&self, locator: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in a nested array field of the current (escrow) ledger
|
||||
/// object, reached by a `locator`, as with [`Self::get_tx_nested_array_len`].
|
||||
#[gas = 70]
|
||||
#[wasm_name = "home_le_inner_arr_len"]
|
||||
fn get_current_ledger_obj_nested_array_len(&self, locator: &[u8]) -> HostResult<i32>;
|
||||
|
||||
/// The number of elements in a nested array field of a previously cached ledger
|
||||
/// object, selected by its cache slot and reached by a `locator`.
|
||||
#[gas = 70]
|
||||
#[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.
|
||||
///
|
||||
/// GAS DISCREPANCY: this 300 is the value the C-ABI fork registered
|
||||
/// (`rippled-wasm-host-functions`, WasmVM.cpp), which this port follows. The
|
||||
/// prior C++ integration in this tree charged 35000 for the same call — 100x
|
||||
/// more, and closer to the real cost of signature verification. The value is
|
||||
/// consensus-critical, so confirm which is intended before this ships.
|
||||
#[gas = 300]
|
||||
#[wasm_name = "check_sig"]
|
||||
fn check_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
pubkey: &[u8],
|
||||
) -> 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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "credential_id"]
|
||||
fn credential_keylet(
|
||||
&self,
|
||||
subject: &[u8],
|
||||
issuer: &[u8],
|
||||
credential_type: &[u8],
|
||||
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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "delegate_id"]
|
||||
fn delegate_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "deposit_preauth_id"]
|
||||
fn deposit_preauth_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[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.
|
||||
#[gas = 400]
|
||||
#[wasm_name = "trustline_id"]
|
||||
fn trust_line_keylet(
|
||||
&self,
|
||||
account1: &[u8],
|
||||
account2: &[u8],
|
||||
currency: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "mpt_issuance_id"]
|
||||
fn mptoken_issuance_keylet(
|
||||
&self,
|
||||
issuer: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "nft_offer_id"]
|
||||
fn nftoken_offer_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// 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.
|
||||
#[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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "paychan_id"]
|
||||
fn paychannel_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
destination: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// 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.
|
||||
#[gas = 350]
|
||||
#[wasm_name = "permissioned_domain_id"]
|
||||
fn permissioned_domain_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[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>;
|
||||
|
||||
/// The XRPL `sha512Half` of `data`: the first [`HASH_LEN`] bytes of its SHA-512.
|
||||
#[gas = 2000]
|
||||
#[wasm_name = "sha512_half"]
|
||||
@@ -98,4 +393,147 @@ host_functions! {
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[gas = 60]
|
||||
#[wasm_name = "nft_serial"]
|
||||
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize>;
|
||||
|
||||
// A "float" here is an XRPL `Number` in its serialized form: a byte blob the guest
|
||||
// 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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[gas = 130]
|
||||
#[wasm_name = "float_to_mant_exp"]
|
||||
fn float_to_mant_exp(
|
||||
&self,
|
||||
x: &[u8],
|
||||
mantissa_out: &mut [u8],
|
||||
exponent_out: &mut [u8],
|
||||
) -> HostResult<usize>;
|
||||
|
||||
/// A float built from `mantissa` and `exponent` under rounding `mode`. Writes the
|
||||
/// float bytes; no input region.
|
||||
#[gas = 100]
|
||||
#[wasm_name = "float_from_mant_exp"]
|
||||
fn float_from_mant_exp(
|
||||
&self,
|
||||
mantissa: i64,
|
||||
exponent: i32,
|
||||
mode: i32,
|
||||
out: &mut [u8],
|
||||
) -> 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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[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.
|
||||
#[gas = 5500]
|
||||
#[wasm_name = "float_pow"]
|
||||
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize>;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,36 @@ impl HostFunctions for FakeHost {
|
||||
put(out, &7u32.to_le_bytes())
|
||||
}
|
||||
|
||||
fn get_parent_ledger_time(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
put(out, &9u32.to_le_bytes())
|
||||
}
|
||||
|
||||
fn get_parent_ledger_hash(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
put(out, &[0xab; HASH_LEN])
|
||||
}
|
||||
|
||||
fn get_base_fee(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
put(out, &10u32.to_le_bytes())
|
||||
}
|
||||
|
||||
/// Returns a flag rather than bytes, and reads its input: enabled unless empty.
|
||||
fn is_amendment_enabled(&self, amendment: &[u8]) -> HostResult<i32> {
|
||||
Ok(i32::from(!amendment.is_empty()))
|
||||
}
|
||||
|
||||
/// Returns a slot: the requested one, or slot 1 when asked to pick.
|
||||
fn cache_ledger_obj(&self, _obj_id: &[u8], cache_idx: i32) -> HostResult<i32> {
|
||||
Ok(if cache_idx == 0 { 1 } else { cache_idx })
|
||||
}
|
||||
|
||||
/// A field getter over the transaction; fails on a negative selector.
|
||||
fn get_tx_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if field < 0 {
|
||||
return Err(HostError::FieldNotFound);
|
||||
}
|
||||
put(out, &[field as u8])
|
||||
}
|
||||
|
||||
/// Fails on a field it doesn't know, so the error channel is exercised too.
|
||||
fn get_current_ledger_obj_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if field < 0 {
|
||||
@@ -41,6 +71,321 @@ impl HostFunctions for FakeHost {
|
||||
put(out, &[field as u8])
|
||||
}
|
||||
|
||||
/// A field getter over a cached object, keyed by slot and selector.
|
||||
fn get_ledger_obj_field(
|
||||
&self,
|
||||
cache_idx: i32,
|
||||
field: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if cache_idx <= 0 || field < 0 {
|
||||
return Err(HostError::FieldNotFound);
|
||||
}
|
||||
put(out, &[cache_idx as u8, field as u8])
|
||||
}
|
||||
|
||||
/// A nested-field getter over the transaction, keyed by the locator bytes.
|
||||
fn get_tx_nested_field(&self, locator: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
put(out, &[locator[0], locator.len() as u8])
|
||||
}
|
||||
|
||||
/// The same, over the current ledger object.
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
&self,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
put(out, &[locator.len() as u8, locator[0]])
|
||||
}
|
||||
|
||||
/// The same, over a cached object keyed by slot.
|
||||
fn get_ledger_obj_nested_field(
|
||||
&self,
|
||||
cache_idx: i32,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if cache_idx <= 0 || locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
put(out, &[cache_idx as u8, locator[0]])
|
||||
}
|
||||
|
||||
/// A scalar-in, scalar-out count; `NoArray` on a negative selector.
|
||||
fn get_tx_array_len(&self, field: i32) -> HostResult<i32> {
|
||||
if field < 0 {
|
||||
return Err(HostError::NoArray);
|
||||
}
|
||||
Ok(field)
|
||||
}
|
||||
|
||||
/// The same, over the current ledger object.
|
||||
fn get_current_ledger_obj_array_len(&self, field: i32) -> HostResult<i32> {
|
||||
if field < 0 {
|
||||
return Err(HostError::NoArray);
|
||||
}
|
||||
Ok(field + 1)
|
||||
}
|
||||
|
||||
/// The same, over a cached object keyed by slot.
|
||||
fn get_ledger_obj_array_len(&self, cache_idx: i32, field: i32) -> HostResult<i32> {
|
||||
if cache_idx <= 0 || field < 0 {
|
||||
return Err(HostError::NoArray);
|
||||
}
|
||||
Ok(cache_idx + field)
|
||||
}
|
||||
|
||||
/// A nested array-length getter, keyed by the locator bytes.
|
||||
fn get_tx_nested_array_len(&self, locator: &[u8]) -> HostResult<i32> {
|
||||
if locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
Ok(locator.len() as i32)
|
||||
}
|
||||
|
||||
/// The same, over the current ledger object.
|
||||
fn get_current_ledger_obj_nested_array_len(&self, locator: &[u8]) -> HostResult<i32> {
|
||||
if locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
Ok(locator.len() as i32 + 1)
|
||||
}
|
||||
|
||||
/// The same, over a cached object keyed by slot.
|
||||
fn get_ledger_obj_nested_array_len(&self, cache_idx: i32, locator: &[u8]) -> HostResult<i32> {
|
||||
if cache_idx <= 0 || locator.is_empty() {
|
||||
return Err(HostError::LocatorMalformed);
|
||||
}
|
||||
Ok(cache_idx + locator.len() as i32)
|
||||
}
|
||||
|
||||
/// Reads three regions and returns a verdict: valid unless the signature is empty.
|
||||
fn check_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
signature: &[u8],
|
||||
_pubkey: &[u8],
|
||||
) -> HostResult<i32> {
|
||||
Ok(i32::from(!signature.is_empty()))
|
||||
}
|
||||
|
||||
/// A keylet getter: reads an account, writes a 32-byte keylet; `InvalidAccount`
|
||||
/// on an empty account.
|
||||
fn account_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A two-asset keylet getter; `InvalidParams` if the two assets are equal.
|
||||
fn amm_keylet(&self, asset1: &[u8], asset2: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if asset1 == asset2 {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[asset1.len() as u8; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A keylet from an account and a sequence; `InvalidAccount` on an empty account.
|
||||
fn check_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A keylet from subject, issuer, and credential type; `InvalidAccount` if either
|
||||
/// account is empty, `InvalidParams` if the type is empty.
|
||||
fn credential_keylet(
|
||||
&self,
|
||||
subject: &[u8],
|
||||
issuer: &[u8],
|
||||
credential_type: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if subject.is_empty() || issuer.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
if credential_type.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[subject[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A keylet from two accounts; `InvalidAccount` if either is empty, `InvalidParams`
|
||||
/// if they are equal.
|
||||
fn delegate_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if account.is_empty() || authorize.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
if account == authorize {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same two-account shape, for a `DepositPreauth`.
|
||||
fn deposit_preauth_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if account.is_empty() || authorize.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
if account == authorize {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[authorize[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A single-account keylet, for a `DID`.
|
||||
fn did_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The account-and-sequence shape, for an `Escrow`.
|
||||
fn escrow_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A keylet from two accounts and a currency; `InvalidAccount` if either account
|
||||
/// is empty, `InvalidParams` if they are equal or the currency is empty.
|
||||
fn trust_line_keylet(
|
||||
&self,
|
||||
account1: &[u8],
|
||||
account2: &[u8],
|
||||
currency: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if account1.is_empty() || account2.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
if account1 == account2 || currency.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[account1[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The issuer-and-sequence shape, for an `MPTokenIssuance`.
|
||||
fn mptoken_issuance_keylet(
|
||||
&self,
|
||||
issuer: &[u8],
|
||||
_seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if issuer.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[issuer[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A keylet from an MPT id and a holder; `InvalidParams` if the id is empty,
|
||||
/// `InvalidAccount` if the holder is empty.
|
||||
fn mptoken_keylet(&self, mptid: &[u8], holder: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if mptid.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
if holder.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[mptid[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The account-and-sequence shape, for an `NFTokenOffer`.
|
||||
fn nftoken_offer_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same account-and-sequence shape, for an `Offer`.
|
||||
fn offer_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same account-and-scalar shape, for an `Oracle` keyed by document id.
|
||||
fn oracle_keylet(&self, account: &[u8], _doc_id: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// A two-account-and-sequence shape, for a `PayChannel`; `InvalidAccount` if
|
||||
/// either account is empty.
|
||||
fn paychannel_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
destination: &[u8],
|
||||
_seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if account.is_empty() || destination.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same account-and-sequence shape, for a `PermissionedDomain`.
|
||||
fn permissioned_domain_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
_seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The account-only shape, for a `SignerList`.
|
||||
fn signer_list_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same account-and-sequence shape, for a `Ticket`.
|
||||
fn ticket_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same account-and-sequence shape, for a `Vault`.
|
||||
fn vault_keylet(&self, account: &[u8], _seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() {
|
||||
return Err(HostError::InvalidAccount);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
fn sha512_half(&self, data: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
let mut digest = [0; HASH_LEN];
|
||||
digest[0] = data.len() as u8;
|
||||
@@ -53,6 +398,180 @@ impl HostFunctions for FakeHost {
|
||||
.push(format!("{msg}/{data_type:?}/{}", data.len()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads a data blob and returns the count of bytes stored.
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32> {
|
||||
Ok(data.len() as i32)
|
||||
}
|
||||
|
||||
/// Reads an account and an nft id, writes a byte value; `InvalidParams` if either
|
||||
/// is empty.
|
||||
fn get_nft(&self, account: &[u8], nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if account.is_empty() || nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[account[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// Reads an nft id, writes a byte value; `InvalidParams` on an empty id.
|
||||
fn get_nft_issuer(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[nft_id[0]; HASH_LEN])
|
||||
}
|
||||
|
||||
/// The same, for the taxon.
|
||||
fn get_nft_taxon(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &nft_id[0].to_le_bytes())
|
||||
}
|
||||
|
||||
/// Reads an nft id and returns a scalar; `InvalidParams` on an empty id.
|
||||
fn get_nft_flags(&self, nft_id: &[u8]) -> HostResult<i32> {
|
||||
if nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
Ok(i32::from(nft_id[0]))
|
||||
}
|
||||
|
||||
/// The same, for the transfer fee.
|
||||
fn get_nft_transfer_fee(&self, nft_id: &[u8]) -> HostResult<i32> {
|
||||
if nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
Ok(i32::from(nft_id[0]))
|
||||
}
|
||||
|
||||
/// The same byte-output shape, for the sequence number.
|
||||
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
if nft_id.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &nft_id[0].to_le_bytes())
|
||||
}
|
||||
|
||||
/// A scalar-in float: writes the low byte of `x` as a stand-in float.
|
||||
fn float_from_int(&self, x: i64, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
put(out, &[x as u8])
|
||||
}
|
||||
|
||||
/// A byte-in float; `InvalidParams` on an empty region.
|
||||
fn float_from_uint(&self, x: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// The same, for a serialized amount.
|
||||
fn float_from_stamount(&self, amount: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if amount.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[amount[0]])
|
||||
}
|
||||
|
||||
/// The same, for a serialized number.
|
||||
fn float_from_stnumber(&self, number: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if number.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[number[0]])
|
||||
}
|
||||
|
||||
/// A float rounded to an integer, written as bytes.
|
||||
fn float_to_int(&self, x: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// Writes a mantissa (its first byte) and an exponent (its first byte) to two
|
||||
/// regions, returning their combined length.
|
||||
fn float_to_mant_exp(
|
||||
&self,
|
||||
x: &[u8],
|
||||
mantissa_out: &mut [u8],
|
||||
exponent_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
if x.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
let m = put(mantissa_out, &[x[0]])?;
|
||||
let e = put(exponent_out, &[x[0]])?;
|
||||
Ok(m + e)
|
||||
}
|
||||
|
||||
/// A two-scalar-in float.
|
||||
fn float_from_mant_exp(
|
||||
&self,
|
||||
mantissa: i64,
|
||||
_exponent: i32,
|
||||
_mode: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
put(out, &[mantissa as u8])
|
||||
}
|
||||
|
||||
/// Reads two floats and returns a scalar; `InvalidParams` if either is empty.
|
||||
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32> {
|
||||
if x.is_empty() || y.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
Ok(i32::from(x[0]) - i32::from(y[0]))
|
||||
}
|
||||
|
||||
/// A binary float operator; `InvalidParams` if either operand is empty.
|
||||
fn float_add(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() || y.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// The same shape, for subtraction.
|
||||
fn float_subtract(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() || y.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// The same shape, for multiplication.
|
||||
fn float_multiply(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() || y.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// The same shape, for division.
|
||||
fn float_divide(&self, x: &[u8], y: &[u8], _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() || y.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// A one-float-and-integer operator; `InvalidParams` on an empty operand.
|
||||
fn float_root(&self, x: &[u8], _n: i32, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
|
||||
/// The same shape, for exponentiation.
|
||||
fn float_power(&self, x: &[u8], _n: i32, _mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
if x.is_empty() {
|
||||
return Err(HostError::InvalidParams);
|
||||
}
|
||||
put(out, &[x[0]])
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -62,11 +581,248 @@ fn the_trait_is_implementable() {
|
||||
|
||||
assert_eq!(host.get_ledger_sqn(&mut out), Ok(4));
|
||||
assert_eq!(out[..4], [7, 0, 0, 0]);
|
||||
assert_eq!(host.get_parent_ledger_time(&mut out), Ok(4));
|
||||
assert_eq!(out[..4], [9, 0, 0, 0]);
|
||||
assert_eq!(host.get_parent_ledger_hash(&mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 0xab);
|
||||
assert_eq!(host.get_base_fee(&mut out), Ok(4));
|
||||
assert_eq!(out[..4], [10, 0, 0, 0]);
|
||||
assert_eq!(host.is_amendment_enabled(&[1; 32]), Ok(1));
|
||||
assert_eq!(host.is_amendment_enabled(&[]), Ok(0));
|
||||
assert_eq!(host.cache_ledger_obj(&[1; 32], 0), Ok(1));
|
||||
assert_eq!(host.cache_ledger_obj(&[1; 32], 5), Ok(5));
|
||||
assert_eq!(host.get_tx_field(5, &mut out), Ok(1));
|
||||
assert_eq!(out[0], 5);
|
||||
assert_eq!(host.get_current_ledger_obj_field(3, &mut out), Ok(1));
|
||||
assert_eq!(out[0], 3);
|
||||
assert_eq!(host.get_ledger_obj_field(2, 4, &mut out), Ok(2));
|
||||
assert_eq!(out[..2], [2, 4]);
|
||||
assert_eq!(host.get_tx_nested_field(&[9, 0, 0, 0], &mut out), Ok(2));
|
||||
assert_eq!(out[..2], [9, 4]);
|
||||
assert_eq!(
|
||||
host.get_current_ledger_obj_nested_field(&[9, 0, 0, 0], &mut out),
|
||||
Ok(2)
|
||||
);
|
||||
assert_eq!(out[..2], [4, 9]);
|
||||
assert_eq!(
|
||||
host.get_ledger_obj_nested_field(3, &[9, 0, 0, 0], &mut out),
|
||||
Ok(2)
|
||||
);
|
||||
assert_eq!(out[..2], [3, 9]);
|
||||
assert_eq!(host.get_tx_array_len(3), Ok(3));
|
||||
assert_eq!(host.get_tx_array_len(-1), Err(HostError::NoArray));
|
||||
assert_eq!(host.get_current_ledger_obj_array_len(3), Ok(4));
|
||||
assert_eq!(
|
||||
host.get_current_ledger_obj_array_len(-1),
|
||||
Err(HostError::NoArray)
|
||||
);
|
||||
assert_eq!(host.get_ledger_obj_array_len(2, 3), Ok(5));
|
||||
assert_eq!(host.get_ledger_obj_array_len(0, 3), Err(HostError::NoArray));
|
||||
assert_eq!(host.get_tx_nested_array_len(&[9, 0, 0, 0]), Ok(4));
|
||||
assert_eq!(
|
||||
host.get_tx_nested_array_len(&[]),
|
||||
Err(HostError::LocatorMalformed)
|
||||
);
|
||||
assert_eq!(
|
||||
host.get_current_ledger_obj_nested_array_len(&[9, 0, 0, 0]),
|
||||
Ok(5)
|
||||
);
|
||||
assert_eq!(
|
||||
host.get_current_ledger_obj_nested_array_len(&[]),
|
||||
Err(HostError::LocatorMalformed)
|
||||
);
|
||||
assert_eq!(
|
||||
host.get_ledger_obj_nested_array_len(2, &[9, 0, 0, 0]),
|
||||
Ok(6)
|
||||
);
|
||||
assert_eq!(
|
||||
host.get_ledger_obj_nested_array_len(0, &[9, 0, 0, 0]),
|
||||
Err(HostError::LocatorMalformed)
|
||||
);
|
||||
assert_eq!(host.check_signature(b"msg", b"sig", b"pk"), Ok(1));
|
||||
assert_eq!(host.check_signature(b"msg", b"", b"pk"), Ok(0));
|
||||
assert_eq!(host.account_keylet(&[7; 20], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.account_keylet(&[], &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.amm_keylet(&[1; 20], &[2; 40], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 20);
|
||||
assert_eq!(
|
||||
host.amm_keylet(&[1; 20], &[1; 20], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(host.check_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.check_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.credential_keylet(&[7; 20], &[8; 20], b"cred", &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.credential_keylet(&[], &[8; 20], b"cred", &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.delegate_keylet(&[7; 20], &[8; 20], &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.delegate_keylet(&[], &[8; 20], &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.deposit_preauth_keylet(&[7; 20], &[8; 20], &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 8);
|
||||
assert_eq!(
|
||||
host.deposit_preauth_keylet(&[7; 20], &[7; 20], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(host.did_keylet(&[7; 20], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.did_keylet(&[], &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.escrow_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.escrow_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.trust_line_keylet(&[7; 20], &[8; 20], &[1; 20], &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.trust_line_keylet(&[7; 20], &[7; 20], &[1; 20], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(
|
||||
host.mptoken_issuance_keylet(&[7; 20], 5, &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.mptoken_issuance_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.mptoken_keylet(&[9; 24], &[8; 20], &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 9);
|
||||
assert_eq!(
|
||||
host.mptoken_keylet(&[], &[8; 20], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(
|
||||
host.nftoken_offer_keylet(&[7; 20], 5, &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.nftoken_offer_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.offer_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.offer_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.oracle_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.oracle_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.paychannel_keylet(&[7; 20], &[8; 20], 5, &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.paychannel_keylet(&[7; 20], &[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(
|
||||
host.permissioned_domain_keylet(&[7; 20], 5, &mut out),
|
||||
Ok(HASH_LEN)
|
||||
);
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.permissioned_domain_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.signer_list_keylet(&[7; 20], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.signer_list_keylet(&[], &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.ticket_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.ticket_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.vault_keylet(&[7; 20], 5, &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.vault_keylet(&[], 5, &mut out),
|
||||
Err(HostError::InvalidAccount)
|
||||
);
|
||||
assert_eq!(host.sha512_half(b"abc", &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 3);
|
||||
assert_eq!(host.trace("hello", b"xy", TraceDataType::AsHex), Ok(()));
|
||||
assert_eq!(host.update_data(b"abcd"), Ok(4));
|
||||
assert_eq!(host.get_nft(&[7; 20], &[9; 32], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 7);
|
||||
assert_eq!(
|
||||
host.get_nft(&[], &[9; 32], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(host.get_nft_issuer(&[9; 32], &mut out), Ok(HASH_LEN));
|
||||
assert_eq!(out[0], 9);
|
||||
assert_eq!(
|
||||
host.get_nft_issuer(&[], &mut out),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(host.get_nft_taxon(&[9; 32], &mut out), Ok(1));
|
||||
assert_eq!(host.get_nft_flags(&[9; 32]), Ok(9));
|
||||
assert_eq!(host.get_nft_flags(&[]), Err(HostError::InvalidParams));
|
||||
assert_eq!(host.get_nft_transfer_fee(&[9; 32]), Ok(9));
|
||||
assert_eq!(host.get_nft_sequence(&[9; 32], &mut out), Ok(1));
|
||||
assert_eq!(host.float_from_int(5, 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_from_uint(&[3; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_from_stamount(&[3; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_from_stnumber(&[3; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_to_int(&[3; 8], 0, &mut out), Ok(1));
|
||||
let mut mant = [0u8; 8];
|
||||
let mut exp = [0u8; 4];
|
||||
assert_eq!(host.float_to_mant_exp(&[3; 8], &mut mant, &mut exp), Ok(2));
|
||||
assert_eq!(host.float_from_mant_exp(5, 0, 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_compare(&[9; 8], &[4; 8]), Ok(5));
|
||||
assert_eq!(
|
||||
host.float_compare(&[], &[4; 8]),
|
||||
Err(HostError::InvalidParams)
|
||||
);
|
||||
assert_eq!(host.float_add(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_subtract(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_multiply(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_divide(&[3; 8], &[4; 8], 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_root(&[3; 8], 2, 0, &mut out), Ok(1));
|
||||
assert_eq!(host.float_power(&[3; 8], 2, 0, &mut out), Ok(1));
|
||||
|
||||
assert_eq!(*host.traced.borrow(), ["hello/AsHex/2"]);
|
||||
}
|
||||
@@ -134,9 +890,66 @@ fn the_spec_table_matches_the_declarations() {
|
||||
table,
|
||||
[
|
||||
("ldgr_index", 60),
|
||||
("parent_ldgr_time", 60),
|
||||
("parent_ldgr_hash", 60),
|
||||
("base_fee", 60),
|
||||
("amendment_enabled", 100),
|
||||
("cache_le", 5000),
|
||||
("tx_field", 70),
|
||||
("home_le_field", 70),
|
||||
("le_field", 70),
|
||||
("tx_inner", 110),
|
||||
("home_le_inner", 110),
|
||||
("le_inner", 110),
|
||||
("tx_arr_len", 40),
|
||||
("home_le_arr_len", 40),
|
||||
("le_arr_len", 40),
|
||||
("tx_inner_arr_len", 70),
|
||||
("home_le_inner_arr_len", 70),
|
||||
("le_inner_arr_len", 70),
|
||||
("check_sig", 300),
|
||||
("accountroot_id", 350),
|
||||
("amm_id", 450),
|
||||
("check_id", 350),
|
||||
("credential_id", 350),
|
||||
("delegate_id", 350),
|
||||
("deposit_preauth_id", 350),
|
||||
("did_id", 350),
|
||||
("escrow_id", 350),
|
||||
("trustline_id", 400),
|
||||
("mpt_issuance_id", 350),
|
||||
("mptoken_id", 500),
|
||||
("nft_offer_id", 350),
|
||||
("offer_id", 350),
|
||||
("oracle_id", 350),
|
||||
("paychan_id", 350),
|
||||
("permissioned_domain_id", 350),
|
||||
("signers_id", 350),
|
||||
("ticket_id", 350),
|
||||
("vault_id", 350),
|
||||
("sha512_half", 2000),
|
||||
("trace", 30),
|
||||
("set_data", 1000),
|
||||
("nft_uri", 5000),
|
||||
("nft_issuer", 70),
|
||||
("nft_taxon", 60),
|
||||
("nft_flags", 60),
|
||||
("nft_xfer_fee", 60),
|
||||
("nft_serial", 60),
|
||||
("float_from_int", 100),
|
||||
("float_from_uint", 130),
|
||||
("float_from_stamount", 150),
|
||||
("float_from_stnumber", 150),
|
||||
("float_to_int", 130),
|
||||
("float_to_mant_exp", 130),
|
||||
("float_from_mant_exp", 100),
|
||||
("float_cmp", 80),
|
||||
("float_add", 160),
|
||||
("float_sub", 160),
|
||||
("float_mult", 300),
|
||||
("float_div", 300),
|
||||
("float_root", 5500),
|
||||
("float_pow", 5500),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -185,10 +185,226 @@ mod ffi {
|
||||
#[cxx_name = "getLedgerSqn"]
|
||||
fn get_ledger_sqn(self: &HostContext, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getParentLedgerTime"]
|
||||
fn get_parent_ledger_time(self: &HostContext, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getParentLedgerHash"]
|
||||
fn get_parent_ledger_hash(self: &HostContext, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getBaseFee"]
|
||||
fn get_base_fee(self: &HostContext, out: &mut [u8]) -> i32;
|
||||
|
||||
/// Reads the amendment (id or name) and answers `1`/`0`, or a negative
|
||||
/// `HostError` code.
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "isAmendmentEnabled"]
|
||||
fn is_amendment_enabled(self: &HostContext, amendment: &[u8]) -> i32;
|
||||
|
||||
/// Caches the object with `obj_id` in slot `cache_idx` (`0` = pick one) and
|
||||
/// answers the slot used, or a negative `HostError` code.
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "cacheLedgerObj"]
|
||||
fn cache_ledger_obj(self: &HostContext, obj_id: &[u8], cache_idx: i32) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getTxField"]
|
||||
fn get_tx_field(self: &HostContext, field: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getCurrentLedgerObjField"]
|
||||
fn get_current_ledger_obj_field(self: &HostContext, field: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getLedgerObjField"]
|
||||
fn get_ledger_obj_field(
|
||||
self: &HostContext,
|
||||
cache_idx: i32,
|
||||
field: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getTxNestedField"]
|
||||
fn get_tx_nested_field(self: &HostContext, locator: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getCurrentLedgerObjNestedField"]
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
self: &HostContext,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getLedgerObjNestedField"]
|
||||
fn get_ledger_obj_nested_field(
|
||||
self: &HostContext,
|
||||
cache_idx: i32,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
/// Answers the array's element count directly, or a negative `HostError` code.
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getTxArrayLen"]
|
||||
fn get_tx_array_len(self: &HostContext, field: i32) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getCurrentLedgerObjArrayLen"]
|
||||
fn get_current_ledger_obj_array_len(self: &HostContext, field: i32) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getLedgerObjArrayLen"]
|
||||
fn get_ledger_obj_array_len(self: &HostContext, cache_idx: i32, field: i32) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getTxNestedArrayLen"]
|
||||
fn get_tx_nested_array_len(self: &HostContext, locator: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getCurrentLedgerObjNestedArrayLen"]
|
||||
fn get_current_ledger_obj_nested_array_len(self: &HostContext, locator: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getLedgerObjNestedArrayLen"]
|
||||
fn get_ledger_obj_nested_array_len(
|
||||
self: &HostContext,
|
||||
cache_idx: i32,
|
||||
locator: &[u8],
|
||||
) -> i32;
|
||||
|
||||
/// Answers `1`/`0` for a valid/invalid signature, or a negative `HostError`.
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "checkSignature"]
|
||||
fn check_signature(
|
||||
self: &HostContext,
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
pubkey: &[u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "accountKeylet"]
|
||||
fn account_keylet(self: &HostContext, account: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "ammKeylet"]
|
||||
fn amm_keylet(self: &HostContext, asset1: &[u8], asset2: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "checkKeylet"]
|
||||
fn check_keylet(self: &HostContext, account: &[u8], seq: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "credentialKeylet"]
|
||||
fn credential_keylet(
|
||||
self: &HostContext,
|
||||
subject: &[u8],
|
||||
issuer: &[u8],
|
||||
credential_type: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "delegateKeylet"]
|
||||
fn delegate_keylet(
|
||||
self: &HostContext,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "depositPreauthKeylet"]
|
||||
fn deposit_preauth_keylet(
|
||||
self: &HostContext,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "didKeylet"]
|
||||
fn did_keylet(self: &HostContext, account: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "escrowKeylet"]
|
||||
fn escrow_keylet(self: &HostContext, account: &[u8], seq: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "trustLineKeylet"]
|
||||
fn trust_line_keylet(
|
||||
self: &HostContext,
|
||||
account1: &[u8],
|
||||
account2: &[u8],
|
||||
currency: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "mptokenIssuanceKeylet"]
|
||||
fn mptoken_issuance_keylet(
|
||||
self: &HostContext,
|
||||
issuer: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "mptokenKeylet"]
|
||||
fn mptoken_keylet(self: &HostContext, mptid: &[u8], holder: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "nftokenOfferKeylet"]
|
||||
fn nftoken_offer_keylet(
|
||||
self: &HostContext,
|
||||
account: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "offerKeylet"]
|
||||
fn offer_keylet(self: &HostContext, account: &[u8], seq: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "oracleKeylet"]
|
||||
fn oracle_keylet(self: &HostContext, account: &[u8], doc_id: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "paychannelKeylet"]
|
||||
fn paychannel_keylet(
|
||||
self: &HostContext,
|
||||
account: &[u8],
|
||||
destination: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "permissionedDomainKeylet"]
|
||||
fn permissioned_domain_keylet(
|
||||
self: &HostContext,
|
||||
account: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "signerListKeylet"]
|
||||
fn signer_list_keylet(self: &HostContext, account: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "ticketKeylet"]
|
||||
fn ticket_keylet(self: &HostContext, account: &[u8], seq: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "vaultKeylet"]
|
||||
fn vault_keylet(self: &HostContext, account: &[u8], seq: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "sha512Half"]
|
||||
fn sha512_half(self: &HostContext, data: &[u8], out: &mut [u8]) -> i32;
|
||||
@@ -202,6 +418,105 @@ mod ffi {
|
||||
/// here is always one of the variants.
|
||||
#[namespace = "xrpl"]
|
||||
fn trace(self: &HostContext, msg: &str, data: &[u8], data_type: TraceDataType);
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "updateData"]
|
||||
fn update_data(self: &HostContext, data: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFT"]
|
||||
fn get_nft(self: &HostContext, account: &[u8], nft_id: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFTIssuer"]
|
||||
fn get_nft_issuer(self: &HostContext, nft_id: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFTTaxon"]
|
||||
fn get_nft_taxon(self: &HostContext, nft_id: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFTFlags"]
|
||||
fn get_nft_flags(self: &HostContext, nft_id: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFTTransferFee"]
|
||||
fn get_nft_transfer_fee(self: &HostContext, nft_id: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "getNFTSequence"]
|
||||
fn get_nft_sequence(self: &HostContext, nft_id: &[u8], out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatFromInt"]
|
||||
fn float_from_int(self: &HostContext, x: i64, mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatFromUint"]
|
||||
fn float_from_uint(self: &HostContext, x: &[u8], mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatFromSTAmount"]
|
||||
fn float_from_stamount(self: &HostContext, amount: &[u8], mode: i32, out: &mut [u8])
|
||||
-> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatFromSTNumber"]
|
||||
fn float_from_stnumber(self: &HostContext, number: &[u8], mode: i32, out: &mut [u8])
|
||||
-> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatToInt"]
|
||||
fn float_to_int(self: &HostContext, x: &[u8], mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatToMantExp"]
|
||||
fn float_to_mant_exp(
|
||||
self: &HostContext,
|
||||
x: &[u8],
|
||||
mantissa_out: &mut [u8],
|
||||
exponent_out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatFromMantExp"]
|
||||
fn float_from_mant_exp(
|
||||
self: &HostContext,
|
||||
mantissa: i64,
|
||||
exponent: i32,
|
||||
mode: i32,
|
||||
out: &mut [u8],
|
||||
) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatCompare"]
|
||||
fn float_compare(self: &HostContext, x: &[u8], y: &[u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatAdd"]
|
||||
fn float_add(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatSubtract"]
|
||||
fn float_subtract(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8])
|
||||
-> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatMultiply"]
|
||||
fn float_multiply(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8])
|
||||
-> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatDivide"]
|
||||
fn float_divide(self: &HostContext, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatRoot"]
|
||||
fn float_root(self: &HostContext, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> i32;
|
||||
|
||||
#[namespace = "xrpl"]
|
||||
#[cxx_name = "floatPower"]
|
||||
fn float_power(self: &HostContext, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,15 +557,229 @@ fn crossed(data_type: TraceDataType) -> ffi::TraceDataType {
|
||||
}
|
||||
}
|
||||
|
||||
/// A call whose answer is a scalar the guest reads directly (a flag, a slot index):
|
||||
/// a non-negative value is that answer, a negative one its error code.
|
||||
fn scalar(n: i32) -> HostResult<i32> {
|
||||
if n < 0 {
|
||||
return Err(HostError::from_code(n));
|
||||
}
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
impl HostFunctions for CxxHost<'_> {
|
||||
fn get_ledger_sqn(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_ledger_sqn(out))
|
||||
}
|
||||
|
||||
fn get_parent_ledger_time(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_parent_ledger_time(out))
|
||||
}
|
||||
|
||||
fn get_parent_ledger_hash(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_parent_ledger_hash(out))
|
||||
}
|
||||
|
||||
fn get_base_fee(&self, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_base_fee(out))
|
||||
}
|
||||
|
||||
fn is_amendment_enabled(&self, amendment: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.is_amendment_enabled(amendment))
|
||||
}
|
||||
|
||||
fn cache_ledger_obj(&self, obj_id: &[u8], cache_idx: i32) -> HostResult<i32> {
|
||||
scalar(self.ctx.cache_ledger_obj(obj_id, cache_idx))
|
||||
}
|
||||
|
||||
fn get_tx_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_tx_field(field, out))
|
||||
}
|
||||
|
||||
fn get_current_ledger_obj_field(&self, field: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_current_ledger_obj_field(field, out))
|
||||
}
|
||||
|
||||
fn get_ledger_obj_field(
|
||||
&self,
|
||||
cache_idx: i32,
|
||||
field: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_ledger_obj_field(cache_idx, field, out))
|
||||
}
|
||||
|
||||
fn get_tx_nested_field(&self, locator: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_tx_nested_field(locator, out))
|
||||
}
|
||||
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
&self,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_current_ledger_obj_nested_field(locator, out))
|
||||
}
|
||||
|
||||
fn get_ledger_obj_nested_field(
|
||||
&self,
|
||||
cache_idx: i32,
|
||||
locator: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(
|
||||
self.ctx
|
||||
.get_ledger_obj_nested_field(cache_idx, locator, out),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_tx_array_len(&self, field: i32) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_tx_array_len(field))
|
||||
}
|
||||
|
||||
fn get_current_ledger_obj_array_len(&self, field: i32) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_current_ledger_obj_array_len(field))
|
||||
}
|
||||
|
||||
fn get_ledger_obj_array_len(&self, cache_idx: i32, field: i32) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_ledger_obj_array_len(cache_idx, field))
|
||||
}
|
||||
|
||||
fn get_tx_nested_array_len(&self, locator: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_tx_nested_array_len(locator))
|
||||
}
|
||||
|
||||
fn get_current_ledger_obj_nested_array_len(&self, locator: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_current_ledger_obj_nested_array_len(locator))
|
||||
}
|
||||
|
||||
fn get_ledger_obj_nested_array_len(&self, cache_idx: i32, locator: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_ledger_obj_nested_array_len(cache_idx, locator))
|
||||
}
|
||||
|
||||
fn check_signature(&self, message: &[u8], signature: &[u8], pubkey: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.check_signature(message, signature, pubkey))
|
||||
}
|
||||
|
||||
fn account_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.account_keylet(account, out))
|
||||
}
|
||||
|
||||
fn amm_keylet(&self, asset1: &[u8], asset2: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.amm_keylet(asset1, asset2, out))
|
||||
}
|
||||
|
||||
fn check_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.check_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn credential_keylet(
|
||||
&self,
|
||||
subject: &[u8],
|
||||
issuer: &[u8],
|
||||
credential_type: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(
|
||||
self.ctx
|
||||
.credential_keylet(subject, issuer, credential_type, out),
|
||||
)
|
||||
}
|
||||
|
||||
fn delegate_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.delegate_keylet(account, authorize, out))
|
||||
}
|
||||
|
||||
fn deposit_preauth_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
authorize: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.deposit_preauth_keylet(account, authorize, out))
|
||||
}
|
||||
|
||||
fn did_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.did_keylet(account, out))
|
||||
}
|
||||
|
||||
fn escrow_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.escrow_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn trust_line_keylet(
|
||||
&self,
|
||||
account1: &[u8],
|
||||
account2: &[u8],
|
||||
currency: &[u8],
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(
|
||||
self.ctx
|
||||
.trust_line_keylet(account1, account2, currency, out),
|
||||
)
|
||||
}
|
||||
|
||||
fn mptoken_issuance_keylet(
|
||||
&self,
|
||||
issuer: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.mptoken_issuance_keylet(issuer, seq, out))
|
||||
}
|
||||
|
||||
fn mptoken_keylet(&self, mptid: &[u8], holder: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.mptoken_keylet(mptid, holder, out))
|
||||
}
|
||||
|
||||
fn nftoken_offer_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.nftoken_offer_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn offer_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.offer_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn oracle_keylet(&self, account: &[u8], doc_id: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.oracle_keylet(account, doc_id, out))
|
||||
}
|
||||
|
||||
fn paychannel_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
destination: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.paychannel_keylet(account, destination, seq, out))
|
||||
}
|
||||
|
||||
fn permissioned_domain_keylet(
|
||||
&self,
|
||||
account: &[u8],
|
||||
seq: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.permissioned_domain_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn signer_list_keylet(&self, account: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.signer_list_keylet(account, out))
|
||||
}
|
||||
|
||||
fn ticket_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.ticket_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn vault_keylet(&self, account: &[u8], seq: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.vault_keylet(account, seq, out))
|
||||
}
|
||||
|
||||
fn sha512_half(&self, data: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.sha512_half(data, out))
|
||||
}
|
||||
@@ -259,6 +788,101 @@ impl HostFunctions for CxxHost<'_> {
|
||||
self.ctx.trace(msg, data, crossed(data_type));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_data(&self, data: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.update_data(data))
|
||||
}
|
||||
|
||||
fn get_nft(&self, account: &[u8], nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_nft(account, nft_id, out))
|
||||
}
|
||||
|
||||
fn get_nft_issuer(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_nft_issuer(nft_id, out))
|
||||
}
|
||||
|
||||
fn get_nft_taxon(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_nft_taxon(nft_id, out))
|
||||
}
|
||||
|
||||
fn get_nft_flags(&self, nft_id: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_nft_flags(nft_id))
|
||||
}
|
||||
|
||||
fn get_nft_transfer_fee(&self, nft_id: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.get_nft_transfer_fee(nft_id))
|
||||
}
|
||||
|
||||
fn get_nft_sequence(&self, nft_id: &[u8], out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.get_nft_sequence(nft_id, out))
|
||||
}
|
||||
|
||||
fn float_from_int(&self, x: i64, mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_from_int(x, mode, out))
|
||||
}
|
||||
|
||||
fn float_from_uint(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_from_uint(x, mode, out))
|
||||
}
|
||||
|
||||
fn float_from_stamount(&self, amount: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_from_stamount(amount, mode, out))
|
||||
}
|
||||
|
||||
fn float_from_stnumber(&self, number: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_from_stnumber(number, mode, out))
|
||||
}
|
||||
|
||||
fn float_to_int(&self, x: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_to_int(x, mode, out))
|
||||
}
|
||||
|
||||
fn float_to_mant_exp(
|
||||
&self,
|
||||
x: &[u8],
|
||||
mantissa_out: &mut [u8],
|
||||
exponent_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_to_mant_exp(x, mantissa_out, exponent_out))
|
||||
}
|
||||
|
||||
fn float_from_mant_exp(
|
||||
&self,
|
||||
mantissa: i64,
|
||||
exponent: i32,
|
||||
mode: i32,
|
||||
out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_from_mant_exp(mantissa, exponent, mode, out))
|
||||
}
|
||||
|
||||
fn float_compare(&self, x: &[u8], y: &[u8]) -> HostResult<i32> {
|
||||
scalar(self.ctx.float_compare(x, y))
|
||||
}
|
||||
|
||||
fn float_add(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_add(x, y, mode, out))
|
||||
}
|
||||
|
||||
fn float_subtract(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_subtract(x, y, mode, out))
|
||||
}
|
||||
|
||||
fn float_multiply(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_multiply(x, y, mode, out))
|
||||
}
|
||||
|
||||
fn float_divide(&self, x: &[u8], y: &[u8], mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_divide(x, y, mode, out))
|
||||
}
|
||||
|
||||
fn float_root(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_root(x, n, mode, out))
|
||||
}
|
||||
|
||||
fn float_power(&self, x: &[u8], n: i32, mode: i32, out: &mut [u8]) -> HostResult<usize> {
|
||||
bytes_written(self.ctx.float_power(x, n, mode, out))
|
||||
}
|
||||
}
|
||||
|
||||
fn run_escrow(
|
||||
@@ -559,16 +1183,11 @@ mod tests {
|
||||
assert_eq!(bytes_written(0), Ok(0));
|
||||
assert_eq!(bytes_written(-3), Err(HostError::BufferTooSmall));
|
||||
assert_eq!(bytes_written(-14), Err(HostError::NoMemExported));
|
||||
assert_eq!(scalar(1), Ok(1));
|
||||
assert_eq!(scalar(0), Ok(0));
|
||||
assert_eq!(scalar(-2), Err(HostError::FieldNotFound));
|
||||
}
|
||||
|
||||
/// An exception caught on the C++ side arrives as `InternalFatal`, the code
|
||||
/// `HostContext` answers with when a body throws. The engine stops the run on it and
|
||||
/// the transaction is `tecINTERNAL`, rather than the contract being handed a code to
|
||||
/// interpret.
|
||||
///
|
||||
/// It arrives through the sign test like any other code, which is the point of
|
||||
/// choosing a negative sentinel: `usize::try_from` rejects it, so this needs no case
|
||||
/// of its own here and a positive length cannot be mistaken for it.
|
||||
#[test]
|
||||
fn a_caught_cxx_exception_arrives_as_internal_fatal() {
|
||||
assert_eq!(bytes_written(i32::MIN), Err(HostError::InternalFatal));
|
||||
|
||||
@@ -160,6 +160,17 @@ pub(crate) fn read_borrowed<'a>(
|
||||
Ok(input.read(mem.data(caller))?)
|
||||
}
|
||||
|
||||
/// Decode a guest `u32` argument — a keylet's sequence number or document id — from
|
||||
/// its four little-endian bytes, carried on to the host as its `i32` bit pattern.
|
||||
///
|
||||
/// 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`.
|
||||
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))
|
||||
}
|
||||
|
||||
/// Service a call whose answer is bytes, written straight into the guest's output
|
||||
/// region.
|
||||
///
|
||||
@@ -256,6 +267,69 @@ pub(crate) fn write_buffered(
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
/// The mantissa and exponent widths `float_to_mant_exp` writes: an `i64` and an `i32`.
|
||||
/// Fixed by the ABI, not the guest, so the split is a constant rather than a reported
|
||||
/// length.
|
||||
const MANTISSA_BYTES: usize = 8;
|
||||
const EXPONENT_BYTES: usize = 4;
|
||||
|
||||
/// Service `float_to_mant_exp`, the one call that writes two output regions: the host
|
||||
/// fills the run's output buffer with the mantissa followed by the exponent, and each
|
||||
/// is copied to its own guest region once every rule has passed.
|
||||
///
|
||||
/// Like [`write_buffered`], the host reads its input from the guest's memory and writes
|
||||
/// to a scratch buffer, so the input stays borrowed rather than copied. The two output
|
||||
/// regions are judged after the input, and the mantissa's region before the exponent's,
|
||||
/// so the first fault reported is the leftmost.
|
||||
pub(crate) fn write_mant_exp(
|
||||
caller: &mut Caller<'_, VmState<'_>>,
|
||||
mantissa_out: Region,
|
||||
exponent_out: Region,
|
||||
call: impl FnOnce(&dyn HostFunctions, &[u8], &mut [u8], &mut [u8]) -> HostResult<usize>,
|
||||
) -> CallResult<i32> {
|
||||
let mem = memory(caller)?;
|
||||
let (data, state) = mem.data_and_store_mut(&mut *caller);
|
||||
let host: &dyn HostFunctions = state.host;
|
||||
|
||||
// The scratch buffer is split at the fixed mantissa width: the host fills the first
|
||||
// eight bytes with the mantissa and the next four with the exponent.
|
||||
let (mant_buf, exp_buf) = state.out_buffer.split_at_mut(MANTISSA_BYTES);
|
||||
let mant_buf = &mut mant_buf[..MANTISSA_BYTES];
|
||||
let exp_buf = &mut exp_buf[..EXPONENT_BYTES];
|
||||
|
||||
let total = call(host, data, mant_buf, exp_buf)?;
|
||||
|
||||
// Copy the mantissa, then the exponent, each only if its whole value fits its
|
||||
// region — a region too small is `BufferTooSmall`, with nothing written.
|
||||
let mant_range = mantissa_out.range()?;
|
||||
let mant_dst = data
|
||||
.get_mut(mant_range)
|
||||
.ok_or(HostError::PointerOutOfBounds)?;
|
||||
if mant_dst.len() < MANTISSA_BYTES {
|
||||
return Err(HostError::BufferTooSmall.into());
|
||||
}
|
||||
mant_dst[..MANTISSA_BYTES].copy_from_slice(&state.out_buffer[..MANTISSA_BYTES]);
|
||||
|
||||
let exp_range = exponent_out.range()?;
|
||||
let exp_dst = data
|
||||
.get_mut(exp_range)
|
||||
.ok_or(HostError::PointerOutOfBounds)?;
|
||||
if exp_dst.len() < EXPONENT_BYTES {
|
||||
return Err(HostError::BufferTooSmall.into());
|
||||
}
|
||||
exp_dst[..EXPONENT_BYTES]
|
||||
.copy_from_slice(&state.out_buffer[MANTISSA_BYTES..MANTISSA_BYTES + EXPONENT_BYTES]);
|
||||
|
||||
charge_transfer(state, MANTISSA_BYTES + EXPONENT_BYTES)?;
|
||||
#[expect(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
reason = "the total is 12, far inside i32"
|
||||
)]
|
||||
let total = total as i32;
|
||||
Ok(total)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -271,15 +345,313 @@ mod tests {
|
||||
fn get_ledger_sqn(&self, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_parent_ledger_time(&self, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_parent_ledger_hash(&self, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_base_fee(&self, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn is_amendment_enabled(&self, _amendment: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn cache_ledger_obj(&self, _obj_id: &[u8], _cache_idx: i32) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_tx_field(&self, _field: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_current_ledger_obj_field(&self, _field: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_ledger_obj_field(
|
||||
&self,
|
||||
_cache_idx: i32,
|
||||
_field: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_tx_nested_field(&self, _locator: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_current_ledger_obj_nested_field(
|
||||
&self,
|
||||
_locator: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_ledger_obj_nested_field(
|
||||
&self,
|
||||
_cache_idx: i32,
|
||||
_locator: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_tx_array_len(&self, _field: i32) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_current_ledger_obj_array_len(&self, _field: i32) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_ledger_obj_array_len(&self, _cache_idx: i32, _field: i32) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_tx_nested_array_len(&self, _locator: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_current_ledger_obj_nested_array_len(&self, _locator: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_ledger_obj_nested_array_len(
|
||||
&self,
|
||||
_cache_idx: i32,
|
||||
_locator: &[u8],
|
||||
) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn check_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_signature: &[u8],
|
||||
_pubkey: &[u8],
|
||||
) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn account_keylet(&self, _account: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn amm_keylet(&self, _asset1: &[u8], _asset2: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn check_keylet(&self, _account: &[u8], _seq: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn credential_keylet(
|
||||
&self,
|
||||
_subject: &[u8],
|
||||
_issuer: &[u8],
|
||||
_credential_type: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn delegate_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_authorize: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn deposit_preauth_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_authorize: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn did_keylet(&self, _account: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn escrow_keylet(&self, _account: &[u8], _seq: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn trust_line_keylet(
|
||||
&self,
|
||||
_account1: &[u8],
|
||||
_account2: &[u8],
|
||||
_currency: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn mptoken_issuance_keylet(
|
||||
&self,
|
||||
_issuer: &[u8],
|
||||
_seq: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn mptoken_keylet(
|
||||
&self,
|
||||
_mptid: &[u8],
|
||||
_holder: &[u8],
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn nftoken_offer_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_seq: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn offer_keylet(&self, _account: &[u8], _seq: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn oracle_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_doc_id: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn paychannel_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_destination: &[u8],
|
||||
_seq: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn permissioned_domain_keylet(
|
||||
&self,
|
||||
_account: &[u8],
|
||||
_seq: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn signer_list_keylet(&self, _account: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn ticket_keylet(&self, _account: &[u8], _seq: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn vault_keylet(&self, _account: &[u8], _seq: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn sha512_half(&self, _data: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn trace(&self, _msg: &str, _data: &[u8], _data_type: TraceDataType) -> HostResult<()> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn update_data(&self, _data: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft(&self, _account: &[u8], _nft_id: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft_issuer(&self, _nft_id: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft_taxon(&self, _nft_id: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft_flags(&self, _nft_id: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft_transfer_fee(&self, _nft_id: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn get_nft_sequence(&self, _nft_id: &[u8], _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_from_int(&self, _x: i64, _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_from_uint(&self, _x: &[u8], _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_from_stamount(
|
||||
&self,
|
||||
_amount: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_from_stnumber(
|
||||
&self,
|
||||
_number: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_to_int(&self, _x: &[u8], _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_to_mant_exp(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_mantissa_out: &mut [u8],
|
||||
_exponent_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_from_mant_exp(
|
||||
&self,
|
||||
_mantissa: i64,
|
||||
_exponent: i32,
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_compare(&self, _x: &[u8], _y: &[u8]) -> HostResult<i32> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_add(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_y: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_subtract(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_y: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_multiply(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_y: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_divide(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_y: &[u8],
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_root(&self, _x: &[u8], _n: i32, _mode: i32, _out: &mut [u8]) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
fn float_power(
|
||||
&self,
|
||||
_x: &[u8],
|
||||
_n: i32,
|
||||
_mode: i32,
|
||||
_out: &mut [u8],
|
||||
) -> HostResult<usize> {
|
||||
unreachable!("no unit test in this module calls the host")
|
||||
}
|
||||
}
|
||||
|
||||
fn state(budget: u64) -> VmState<'static> {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -94,11 +94,189 @@ fn call_for(op: HostFunctionSpec) -> Call {
|
||||
"(call $ldgr_index (i32.const 0) (i32.const 4))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetParentLedgerTime => (
|
||||
import::PARENT_LDGR_TIME,
|
||||
"(call $parent_ldgr_time (i32.const 0) (i32.const 4))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetParentLedgerHash => (
|
||||
import::PARENT_LDGR_HASH,
|
||||
"(call $parent_ldgr_hash (i32.const 0) (i32.const 32))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetBaseFee => (
|
||||
import::BASE_FEE,
|
||||
"(call $base_fee (i32.const 0) (i32.const 4))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::IsAmendmentEnabled => (
|
||||
import::AMENDMENT_ENABLED,
|
||||
"(call $amendment_enabled (i32.const 0) (i32.const 32))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::CacheLedgerObj => (
|
||||
import::CACHE_LE,
|
||||
"(call $cache_le (i32.const 0) (i32.const 32) (i32.const 0))",
|
||||
3,
|
||||
),
|
||||
HostFunctionSpec::GetTxField => (
|
||||
import::TX_FIELD,
|
||||
"(call $tx_field (i32.const 1) (i32.const 0) (i32.const 4))",
|
||||
3,
|
||||
),
|
||||
HostFunctionSpec::GetCurrentLedgerObjField => (
|
||||
import::HOME_LE_FIELD,
|
||||
"(call $home_le_field (i32.const 1) (i32.const 0) (i32.const 4))",
|
||||
3,
|
||||
),
|
||||
HostFunctionSpec::GetLedgerObjField => (
|
||||
import::LE_FIELD,
|
||||
"(call $le_field (i32.const 1) (i32.const 1) (i32.const 0) (i32.const 4))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::GetTxNestedField => (
|
||||
import::TX_INNER,
|
||||
"(call $tx_inner (i32.const 0) (i32.const 4) (i32.const 8) (i32.const 4))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::GetCurrentLedgerObjNestedField => (
|
||||
import::HOME_LE_INNER,
|
||||
"(call $home_le_inner (i32.const 0) (i32.const 4) (i32.const 8) (i32.const 4))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::GetLedgerObjNestedField => (
|
||||
import::LE_INNER,
|
||||
"(call $le_inner (i32.const 1) (i32.const 0) (i32.const 4) (i32.const 8) (i32.const 4))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::GetTxArrayLen => {
|
||||
(import::TX_ARR_LEN, "(call $tx_arr_len (i32.const 1))", 1)
|
||||
}
|
||||
HostFunctionSpec::GetCurrentLedgerObjArrayLen => (
|
||||
import::HOME_LE_ARR_LEN,
|
||||
"(call $home_le_arr_len (i32.const 1))",
|
||||
1,
|
||||
),
|
||||
HostFunctionSpec::GetLedgerObjArrayLen => (
|
||||
import::LE_ARR_LEN,
|
||||
"(call $le_arr_len (i32.const 1) (i32.const 1))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetTxNestedArrayLen => (
|
||||
import::TX_INNER_ARR_LEN,
|
||||
"(call $tx_inner_arr_len (i32.const 0) (i32.const 4))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetCurrentLedgerObjNestedArrayLen => (
|
||||
import::HOME_LE_INNER_ARR_LEN,
|
||||
"(call $home_le_inner_arr_len (i32.const 0) (i32.const 4))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetLedgerObjNestedArrayLen => (
|
||||
import::LE_INNER_ARR_LEN,
|
||||
"(call $le_inner_arr_len (i32.const 1) (i32.const 0) (i32.const 4))",
|
||||
3,
|
||||
),
|
||||
HostFunctionSpec::CheckSignature => (
|
||||
import::CHECK_SIG,
|
||||
"(call $check_sig (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0) (i32.const 0))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::AccountKeylet => (
|
||||
import::ACCOUNTROOT_ID,
|
||||
"(call $accountroot_id (i32.const 0) (i32.const 20) (i32.const 32) (i32.const 32))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::AmmKeylet => (
|
||||
import::AMM_ID,
|
||||
"(call $amm_id (i32.const 0) (i32.const 20) (i32.const 24) (i32.const 40) (i32.const 0) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::CheckKeylet => (
|
||||
import::CHECK_ID,
|
||||
"(call $check_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::CredentialKeylet => (
|
||||
import::CREDENTIAL_ID,
|
||||
"(call $credential_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 20) (i32.const 40) (i32.const 4) (i32.const 44) (i32.const 20))",
|
||||
8,
|
||||
),
|
||||
HostFunctionSpec::DelegateKeylet => (
|
||||
import::DELEGATE_ID,
|
||||
"(call $delegate_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 20) (i32.const 40) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::DepositPreauthKeylet => (
|
||||
import::DEPOSIT_PREAUTH_ID,
|
||||
"(call $deposit_preauth_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 20) (i32.const 40) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::DidKeylet => (
|
||||
import::DID_ID,
|
||||
"(call $did_id (i32.const 0) (i32.const 20) (i32.const 32) (i32.const 32))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::EscrowKeylet => (
|
||||
import::ESCROW_ID,
|
||||
"(call $escrow_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::TrustLineKeylet => (
|
||||
import::TRUSTLINE_ID,
|
||||
"(call $trustline_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 20) (i32.const 40) (i32.const 20) (i32.const 60) (i32.const 32))",
|
||||
8,
|
||||
),
|
||||
HostFunctionSpec::MptokenIssuanceKeylet => (
|
||||
import::MPT_ISSUANCE_ID,
|
||||
"(call $mpt_issuance_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::MptokenKeylet => (
|
||||
import::MPTOKEN_ID,
|
||||
"(call $mptoken_id (i32.const 0) (i32.const 24) (i32.const 24) (i32.const 20) (i32.const 44) (i32.const 20))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::NftokenOfferKeylet => (
|
||||
import::NFT_OFFER_ID,
|
||||
"(call $nft_offer_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::OfferKeylet => (
|
||||
import::OFFER_ID,
|
||||
"(call $offer_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::OracleKeylet => (
|
||||
import::ORACLE_ID,
|
||||
"(call $oracle_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::PaychannelKeylet => (
|
||||
import::PAYCHAN_ID,
|
||||
"(call $paychan_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 40) (i32.const 20))",
|
||||
8,
|
||||
),
|
||||
HostFunctionSpec::PermissionedDomainKeylet => (
|
||||
import::PERMISSIONED_DOMAIN_ID,
|
||||
"(call $permissioned_domain_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::SignerListKeylet => (
|
||||
import::SIGNERS_ID,
|
||||
"(call $signers_id (i32.const 0) (i32.const 20) (i32.const 32) (i32.const 32))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::TicketKeylet => (
|
||||
import::TICKET_ID,
|
||||
"(call $ticket_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::VaultKeylet => (
|
||||
import::VAULT_ID,
|
||||
"(call $vault_id (i32.const 0) (i32.const 20) (i32.const 0) (i32.const 4) (i32.const 32) (i32.const 32))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::Sha512Half => (
|
||||
import::SHA512_HALF,
|
||||
"(call $sha512_half (i32.const 0) (i32.const 4) (i32.const 0) (i32.const 32))",
|
||||
@@ -109,6 +287,111 @@ fn call_for(op: HostFunctionSpec) -> Call {
|
||||
"(call $trace (i32.const 0) (i32.const 0) (i32.const 1) (i32.const 0) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::UpdateData => (
|
||||
import::SET_DATA,
|
||||
"(call $set_data (i32.const 0) (i32.const 8))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetNft => (
|
||||
import::NFT_URI,
|
||||
"(call $nft_uri (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 32) (i32.const 52) (i32.const 12))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::GetNftIssuer => (
|
||||
import::NFT_ISSUER,
|
||||
"(call $nft_issuer (i32.const 0) (i32.const 32) (i32.const 32) (i32.const 20))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::GetNftTaxon => (
|
||||
import::NFT_TAXON,
|
||||
"(call $nft_taxon (i32.const 0) (i32.const 32) (i32.const 32) (i32.const 4))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::GetNftFlags => (
|
||||
import::NFT_FLAGS,
|
||||
"(call $nft_flags (i32.const 0) (i32.const 32))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetNftTransferFee => (
|
||||
import::NFT_XFER_FEE,
|
||||
"(call $nft_xfer_fee (i32.const 0) (i32.const 32))",
|
||||
2,
|
||||
),
|
||||
HostFunctionSpec::GetNftSequence => (
|
||||
import::NFT_SERIAL,
|
||||
"(call $nft_serial (i32.const 0) (i32.const 32) (i32.const 32) (i32.const 4))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::FloatFromInt => (
|
||||
import::FLOAT_FROM_INT,
|
||||
"(call $float_from_int (i64.const 0) (i32.const 0) (i32.const 8) (i32.const 0))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::FloatFromUint => (
|
||||
import::FLOAT_FROM_UINT,
|
||||
"(call $float_from_uint (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::FloatFromStamount => (
|
||||
import::FLOAT_FROM_STAMOUNT,
|
||||
"(call $float_from_stamount (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::FloatFromStnumber => (
|
||||
import::FLOAT_FROM_STNUMBER,
|
||||
"(call $float_from_stnumber (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::FloatToInt => (
|
||||
import::FLOAT_TO_INT,
|
||||
"(call $float_to_int (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::FloatToMantExp => (
|
||||
import::FLOAT_TO_MANT_EXP,
|
||||
"(call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 4))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::FloatFromMantExp => (
|
||||
import::FLOAT_FROM_MANT_EXP,
|
||||
"(call $float_from_mant_exp (i64.const 0) (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
5,
|
||||
),
|
||||
HostFunctionSpec::FloatCompare => (
|
||||
import::FLOAT_CMP,
|
||||
"(call $float_cmp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8))",
|
||||
4,
|
||||
),
|
||||
HostFunctionSpec::FloatAdd => (
|
||||
import::FLOAT_ADD,
|
||||
"(call $float_add (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
|
||||
7,
|
||||
),
|
||||
HostFunctionSpec::FloatSubtract => (
|
||||
import::FLOAT_SUB,
|
||||
"(call $float_sub (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
|
||||
7,
|
||||
),
|
||||
HostFunctionSpec::FloatMultiply => (
|
||||
import::FLOAT_MULT,
|
||||
"(call $float_mult (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
|
||||
7,
|
||||
),
|
||||
HostFunctionSpec::FloatDivide => (
|
||||
import::FLOAT_DIV,
|
||||
"(call $float_div (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8) (i32.const 16) (i32.const 8) (i32.const 0))",
|
||||
7,
|
||||
),
|
||||
HostFunctionSpec::FloatRoot => (
|
||||
import::FLOAT_ROOT,
|
||||
"(call $float_root (i32.const 0) (i32.const 8) (i32.const 2) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
6,
|
||||
),
|
||||
HostFunctionSpec::FloatPower => (
|
||||
import::FLOAT_POW,
|
||||
"(call $float_pow (i32.const 0) (i32.const 8) (i32.const 2) (i32.const 8) (i32.const 8) (i32.const 0))",
|
||||
6,
|
||||
),
|
||||
};
|
||||
Call {
|
||||
import,
|
||||
|
||||
@@ -31,6 +31,118 @@ fn ldgr_index_writes_the_sequence_number_where_the_guest_asked() {
|
||||
assert_eq!(status(&wat, &host), 4, "the byte count");
|
||||
}
|
||||
|
||||
/// A second scalar getter travels the same path: the value the host supplies lands
|
||||
/// where the guest asked, and the status is the byte count. The default parent
|
||||
/// ledger time is distinct from the sequence number, so this cannot pass by reading
|
||||
/// the wrong one.
|
||||
#[test]
|
||||
fn parent_ldgr_time_writes_the_close_time_where_the_guest_asked() {
|
||||
let host = FakeHost::new();
|
||||
|
||||
let wat = module(
|
||||
&[import::PARENT_LDGR_TIME, ONE_PAGE],
|
||||
"(drop (call $parent_ldgr_time (i32.const 64) (i32.const 4)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 9, "the 4 LE bytes the host wrote");
|
||||
|
||||
let wat = module(
|
||||
&[import::PARENT_LDGR_TIME, ONE_PAGE],
|
||||
"(call $parent_ldgr_time (i32.const 64) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 4, "the byte count");
|
||||
}
|
||||
|
||||
/// A 32-byte value (a ledger hash) travels the same getter path as the 4-byte
|
||||
/// scalars: every byte lands where the guest asked, and the status is the length.
|
||||
#[test]
|
||||
fn parent_ldgr_hash_writes_all_32_bytes_where_the_guest_asked() {
|
||||
let host = FakeHost::new();
|
||||
|
||||
let wat = module(
|
||||
&[import::PARENT_LDGR_HASH, ONE_PAGE],
|
||||
"(call $parent_ldgr_hash (i32.const 64) (i32.const 32))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the byte count");
|
||||
|
||||
// The default hash is 0, 1, 2, ..., so its first four bytes load as 0x03020100.
|
||||
let wat = module(
|
||||
&[import::PARENT_LDGR_HASH, ONE_PAGE],
|
||||
"(drop (call $parent_ldgr_hash (i32.const 64) (i32.const 32)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(
|
||||
status(&wat, &host),
|
||||
0x03020100,
|
||||
"the first four bytes the host wrote"
|
||||
);
|
||||
}
|
||||
|
||||
/// A third scalar getter, to pin the pattern rather than a single instance of it.
|
||||
#[test]
|
||||
fn base_fee_writes_the_fee_where_the_guest_asked() {
|
||||
let host = FakeHost::new();
|
||||
|
||||
let wat = module(
|
||||
&[import::BASE_FEE, ONE_PAGE],
|
||||
"(drop (call $base_fee (i32.const 64) (i32.const 4)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 10, "the 4 LE bytes the host wrote");
|
||||
|
||||
let wat = module(
|
||||
&[import::BASE_FEE, ONE_PAGE],
|
||||
"(call $base_fee (i32.const 64) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 4, "the byte count");
|
||||
}
|
||||
|
||||
/// A call that reads an input region and returns a scalar flag, rather than writing
|
||||
/// bytes to an output region: the amendment reaches the host, and its verdict comes
|
||||
/// back as the call's status.
|
||||
#[test]
|
||||
fn amendment_enabled_reads_the_input_and_returns_the_flag() {
|
||||
let host = FakeHost::new(); // enabled by default
|
||||
|
||||
let wat = module(
|
||||
&[import::AMENDMENT_ENABLED, ONE_PAGE],
|
||||
"(call $amendment_enabled (i32.const 64) (i32.const 32))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 1, "the enabled flag");
|
||||
assert_eq!(
|
||||
*host.amendments_asked.borrow(),
|
||||
[vec![0u8; 32]],
|
||||
"the 32-byte region reached the host"
|
||||
);
|
||||
|
||||
// A host that reports the amendment disabled answers 0 — a value, not an error.
|
||||
let host = FakeHost::new().answering_amendment_enabled(Ok(0));
|
||||
let wat = module(
|
||||
&[import::AMENDMENT_ENABLED, ONE_PAGE],
|
||||
"(call $amendment_enabled (i32.const 0) (i32.const 32))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 0, "the disabled flag");
|
||||
}
|
||||
|
||||
/// A call that reads an input region and takes a second scalar arg: both the object
|
||||
/// id and the requested slot reach the host, and the slot it chose comes back as the
|
||||
/// status.
|
||||
#[test]
|
||||
fn cache_le_passes_the_object_id_and_slot_through() {
|
||||
let host = FakeHost::new().answering_cache_slot(Ok(4));
|
||||
|
||||
let wat = module(
|
||||
&[import::CACHE_LE, ONE_PAGE],
|
||||
"(call $cache_le (i32.const 64) (i32.const 32) (i32.const 7))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 4, "the slot the host chose");
|
||||
assert_eq!(
|
||||
*host.cached.borrow(),
|
||||
[(vec![0u8; 32], 7)],
|
||||
"the id region and the requested slot reached the host"
|
||||
);
|
||||
}
|
||||
|
||||
/// The output region is wherever the guest points, not a fixed address.
|
||||
#[test]
|
||||
fn the_output_region_is_the_pointer_the_guest_gave() {
|
||||
@@ -48,6 +160,832 @@ fn the_output_region_is_the_pointer_the_guest_gave() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A field getter over the transaction: the selector reaches the host, and the bytes
|
||||
/// it answers land where the guest asked. It has its own answer set, distinct from
|
||||
/// the current-object field getter's.
|
||||
#[test]
|
||||
fn tx_field_passes_the_selector_and_writes_the_field() {
|
||||
let host = FakeHost::new().answering_tx_field(17, support::Answer::bytes([0xab, 0xcd]));
|
||||
|
||||
let wat = module(
|
||||
&[import::TX_FIELD, ONE_PAGE],
|
||||
"(call $tx_field (i32.const 17) (i32.const 0) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 2);
|
||||
assert_eq!(*host.tx_fields_asked.borrow(), vec![17]);
|
||||
}
|
||||
|
||||
/// A field getter over a cached object: both the slot and the selector reach the
|
||||
/// host, keyed together, and the answered bytes land where the guest asked.
|
||||
#[test]
|
||||
fn le_field_passes_the_slot_and_selector_through() {
|
||||
let host =
|
||||
FakeHost::new().answering_le_field(2, 17, support::Answer::bytes([0xab, 0xcd, 0xef]));
|
||||
|
||||
let wat = module(
|
||||
&[import::LE_FIELD, ONE_PAGE],
|
||||
"(call $le_field (i32.const 2) (i32.const 17) (i32.const 0) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 3);
|
||||
assert_eq!(*host.le_fields_asked.borrow(), vec![(2, 17)]);
|
||||
}
|
||||
|
||||
/// A nested-field getter: the locator is read from one region and the answer written
|
||||
/// to another — the read-input-write-output path. The guest lays the locator down in
|
||||
/// memory, and the bytes the host answers land where it asked.
|
||||
#[test]
|
||||
fn tx_inner_reads_the_locator_and_writes_the_field() {
|
||||
// An eight-byte, two-step locator, as it lands in little-endian guest memory.
|
||||
let locator = vec![17u8, 0, 0, 0, 2, 0, 0, 0];
|
||||
let host =
|
||||
FakeHost::new().answering_tx_nested(locator.clone(), support::Answer::bytes([0xaa, 0xbb]));
|
||||
|
||||
let wat = module(
|
||||
&[import::TX_INNER, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 17))
|
||||
(i32.store (i32.const 4) (i32.const 2))
|
||||
(call $tx_inner (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 2, "the field bytes the host wrote");
|
||||
assert_eq!(*host.tx_nested_asked.borrow(), vec![locator]);
|
||||
}
|
||||
|
||||
/// The same read-input-write-output path over the current object, with its own
|
||||
/// answer set distinct from the transaction's nested getter.
|
||||
#[test]
|
||||
fn home_le_inner_reads_the_locator_and_writes_the_field() {
|
||||
let locator = vec![5u8, 0, 0, 0];
|
||||
let host = FakeHost::new()
|
||||
.answering_home_le_nested(locator.clone(), support::Answer::bytes([0xcc, 0xdd, 0xee]));
|
||||
|
||||
let wat = module(
|
||||
&[import::HOME_LE_INNER, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 5))
|
||||
(call $home_le_inner (i32.const 0) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 3, "the field bytes the host wrote");
|
||||
assert_eq!(*host.home_le_nested_asked.borrow(), vec![locator]);
|
||||
}
|
||||
|
||||
/// The nested getter over a cached object: the slot leads, the locator is read from
|
||||
/// memory, and the two reach the host keyed together.
|
||||
#[test]
|
||||
fn le_inner_reads_the_slot_and_locator_and_writes_the_field() {
|
||||
let locator = vec![5u8, 0, 0, 0];
|
||||
let host = FakeHost::new().answering_le_nested(
|
||||
3,
|
||||
locator.clone(),
|
||||
support::Answer::bytes([0x11, 0x22]),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::LE_INNER, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 5))
|
||||
(call $le_inner (i32.const 3) (i32.const 0) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 2, "the field bytes the host wrote");
|
||||
assert_eq!(*host.le_nested_asked.borrow(), vec![(3, locator)]);
|
||||
}
|
||||
|
||||
/// A scalar-in, scalar-out call — no memory regions at all: the field selector
|
||||
/// reaches the host and the array length comes back as the status.
|
||||
#[test]
|
||||
fn tx_arr_len_passes_the_selector_and_returns_the_count() {
|
||||
let host = FakeHost::new().answering_tx_arr_len(17, 5);
|
||||
|
||||
let wat = module(
|
||||
&[import::TX_ARR_LEN, ONE_PAGE],
|
||||
"(call $tx_arr_len (i32.const 17))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 5, "the array length");
|
||||
assert_eq!(*host.tx_arr_lens_asked.borrow(), vec![17]);
|
||||
}
|
||||
|
||||
/// The same scalar-in, scalar-out count over the current object, with its own answer
|
||||
/// set distinct from the transaction's.
|
||||
#[test]
|
||||
fn home_le_arr_len_passes_the_selector_and_returns_the_count() {
|
||||
let host = FakeHost::new().answering_home_le_arr_len(17, 8);
|
||||
|
||||
let wat = module(
|
||||
&[import::HOME_LE_ARR_LEN, ONE_PAGE],
|
||||
"(call $home_le_arr_len (i32.const 17))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the array length");
|
||||
assert_eq!(*host.home_le_arr_lens_asked.borrow(), vec![17]);
|
||||
}
|
||||
|
||||
/// The scalar count over a cached object: the slot leads, and both it and the
|
||||
/// selector reach the host keyed together.
|
||||
#[test]
|
||||
fn le_arr_len_passes_the_slot_and_selector_and_returns_the_count() {
|
||||
let host = FakeHost::new().answering_le_arr_len(2, 17, 9);
|
||||
|
||||
let wat = module(
|
||||
&[import::LE_ARR_LEN, ONE_PAGE],
|
||||
"(call $le_arr_len (i32.const 2) (i32.const 17))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 9, "the array length");
|
||||
assert_eq!(*host.le_arr_lens_asked.borrow(), vec![(2, 17)]);
|
||||
}
|
||||
|
||||
/// A nested array-length getter: the locator is read from memory and the count comes
|
||||
/// back as the status — read-input, scalar-out, no output buffer.
|
||||
#[test]
|
||||
fn tx_inner_arr_len_reads_the_locator_and_returns_the_count() {
|
||||
let locator = vec![5u8, 0, 0, 0];
|
||||
let host = FakeHost::new().answering_tx_nested_arr_len(locator.clone(), 6);
|
||||
|
||||
let wat = module(
|
||||
&[import::TX_INNER_ARR_LEN, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 5))
|
||||
(call $tx_inner_arr_len (i32.const 0) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 6, "the array length");
|
||||
assert_eq!(*host.tx_nested_arr_lens_asked.borrow(), vec![locator]);
|
||||
}
|
||||
|
||||
/// The same read-input, scalar-out count over the current object, with its own answer
|
||||
/// set distinct from the transaction's.
|
||||
#[test]
|
||||
fn home_le_inner_arr_len_reads_the_locator_and_returns_the_count() {
|
||||
let locator = vec![5u8, 0, 0, 0];
|
||||
let host = FakeHost::new().answering_home_le_nested_arr_len(locator.clone(), 7);
|
||||
|
||||
let wat = module(
|
||||
&[import::HOME_LE_INNER_ARR_LEN, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 5))
|
||||
(call $home_le_inner_arr_len (i32.const 0) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 7, "the array length");
|
||||
assert_eq!(*host.home_le_nested_arr_lens_asked.borrow(), vec![locator]);
|
||||
}
|
||||
|
||||
/// The nested array-length getter over a cached object: the slot leads, the locator
|
||||
/// is read from memory, and the two reach the host keyed together.
|
||||
#[test]
|
||||
fn le_inner_arr_len_reads_the_slot_and_locator_and_returns_the_count() {
|
||||
let locator = vec![5u8, 0, 0, 0];
|
||||
let host = FakeHost::new().answering_le_nested_arr_len(3, locator.clone(), 8);
|
||||
|
||||
let wat = module(
|
||||
&[import::LE_INNER_ARR_LEN, ONE_PAGE],
|
||||
"(i32.store (i32.const 0) (i32.const 5))
|
||||
(call $le_inner_arr_len (i32.const 3) (i32.const 0) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the array length");
|
||||
assert_eq!(*host.le_nested_arr_lens_asked.borrow(), vec![(3, locator)]);
|
||||
}
|
||||
|
||||
/// A call that reads three input regions and returns a scalar verdict: the message,
|
||||
/// signature, and pubkey all reach the host, and the verdict comes back as the status.
|
||||
#[test]
|
||||
fn check_sig_reads_all_three_regions_and_returns_the_verdict() {
|
||||
let host = FakeHost::new(); // valid by default
|
||||
|
||||
// message @0 len 3, signature @8 len 4, pubkey @16 len 5 — memory is zeroed.
|
||||
let wat = module(
|
||||
&[import::CHECK_SIG, ONE_PAGE],
|
||||
"(call $check_sig
|
||||
(i32.const 0) (i32.const 3)
|
||||
(i32.const 8) (i32.const 4)
|
||||
(i32.const 16) (i32.const 5))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 1, "the valid verdict");
|
||||
assert_eq!(
|
||||
*host.sigs_checked.borrow(),
|
||||
[(vec![0u8; 3], vec![0u8; 4], vec![0u8; 5])],
|
||||
"the three regions reached the host at their declared lengths"
|
||||
);
|
||||
|
||||
// An invalid signature comes back as 0 — a value, not an error.
|
||||
let host = FakeHost::new().answering_check_sig(Ok(0));
|
||||
assert_eq!(status(&wat, &host), 0, "the invalid verdict");
|
||||
}
|
||||
|
||||
/// A keylet getter: reads an account region and writes a 32-byte keylet back — the
|
||||
/// read-input-write-output path. The account reaches the host and the keylet lands
|
||||
/// where the guest asked.
|
||||
#[test]
|
||||
fn accountroot_id_reads_the_account_and_writes_the_keylet() {
|
||||
// Guest memory is zeroed, so a 20-byte account read is all zeros.
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_account_keylet(account.clone(), support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::ACCOUNTROOT_ID, ONE_PAGE],
|
||||
"(call $accountroot_id (i32.const 0) (i32.const 20) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.account_keylets_asked.borrow(), vec![account]);
|
||||
|
||||
// The keylet bytes land at the output pointer: filler is 0, 1, 2, ..., so the
|
||||
// first four load as 0x03020100.
|
||||
let wat = module(
|
||||
&[import::ACCOUNTROOT_ID, ONE_PAGE],
|
||||
"(drop (call $accountroot_id (i32.const 0) (i32.const 20) (i32.const 64) (i32.const 64)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(
|
||||
status(&wat, &host),
|
||||
0x03020100,
|
||||
"the first four keylet bytes"
|
||||
);
|
||||
}
|
||||
|
||||
/// A keylet getter that reads two input regions: both assets reach the host as a
|
||||
/// pair, and the keylet lands where the guest asked.
|
||||
#[test]
|
||||
fn amm_id_reads_two_assets_and_writes_the_keylet() {
|
||||
// Two distinct all-zero assets of different lengths (20 and 40 bytes).
|
||||
let asset1 = vec![0u8; 20];
|
||||
let asset2 = vec![0u8; 40];
|
||||
let host = FakeHost::new().answering_amm_keylet(
|
||||
asset1.clone(),
|
||||
asset2.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::AMM_ID, ONE_PAGE],
|
||||
"(call $amm_id
|
||||
(i32.const 0) (i32.const 20)
|
||||
(i32.const 64) (i32.const 40)
|
||||
(i32.const 128) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.amm_keylets_asked.borrow(), vec![(asset1, asset2)]);
|
||||
}
|
||||
|
||||
/// A keylet getter that reads an account region and also takes a scalar seq: both
|
||||
/// reach the host keyed together, and the keylet lands where the guest asked.
|
||||
#[test]
|
||||
fn check_id_reads_the_account_and_seq_and_writes_the_keylet() {
|
||||
// Guest memory is zeroed, so a 20-byte account read is all zeros.
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_check_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::CHECK_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $check_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.check_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A keylet getter that reads three input regions — two accounts and a credential
|
||||
/// type: all three reach the host keyed together, and the keylet lands where asked.
|
||||
#[test]
|
||||
fn credential_id_reads_subject_issuer_and_type() {
|
||||
// Guest memory is zeroed, so the two 20-byte accounts and the 4-byte type read
|
||||
// as zeros of their declared lengths.
|
||||
let subject = vec![0u8; 20];
|
||||
let issuer = vec![0u8; 20];
|
||||
let cred_type = vec![0u8; 4];
|
||||
let host = FakeHost::new().answering_credential_keylet(
|
||||
subject.clone(),
|
||||
issuer.clone(),
|
||||
cred_type.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::CREDENTIAL_ID, ONE_PAGE],
|
||||
"(call $credential_id
|
||||
(i32.const 0) (i32.const 20)
|
||||
(i32.const 20) (i32.const 20)
|
||||
(i32.const 40) (i32.const 4)
|
||||
(i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(
|
||||
*host.credential_keylets_asked.borrow(),
|
||||
vec![(subject, issuer, cred_type)]
|
||||
);
|
||||
}
|
||||
|
||||
/// A two-account keylet getter: both accounts reach the host as a pair, and the
|
||||
/// keylet lands where the guest asked.
|
||||
#[test]
|
||||
fn delegate_id_reads_both_accounts_and_writes_the_keylet() {
|
||||
let account = vec![0u8; 20];
|
||||
let authorize = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_delegate_keylet(
|
||||
account.clone(),
|
||||
authorize.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::DELEGATE_ID, ONE_PAGE],
|
||||
"(call $delegate_id
|
||||
(i32.const 0) (i32.const 20)
|
||||
(i32.const 20) (i32.const 20)
|
||||
(i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(
|
||||
*host.delegate_keylets_asked.borrow(),
|
||||
vec![(account, authorize)]
|
||||
);
|
||||
}
|
||||
|
||||
/// The same two-account keylet shape as delegate, with its own answer set.
|
||||
#[test]
|
||||
fn deposit_preauth_id_reads_both_accounts_and_writes_the_keylet() {
|
||||
let account = vec![0u8; 20];
|
||||
let authorize = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_deposit_preauth_keylet(
|
||||
account.clone(),
|
||||
authorize.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::DEPOSIT_PREAUTH_ID, ONE_PAGE],
|
||||
"(call $deposit_preauth_id
|
||||
(i32.const 0) (i32.const 20)
|
||||
(i32.const 20) (i32.const 20)
|
||||
(i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(
|
||||
*host.deposit_preauth_keylets_asked.borrow(),
|
||||
vec![(account, authorize)]
|
||||
);
|
||||
}
|
||||
|
||||
/// A single-account keylet getter (like accountroot), with its own answer set.
|
||||
#[test]
|
||||
fn did_id_reads_the_account_and_writes_the_keylet() {
|
||||
let account = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_did_keylet(account.clone(), support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::DID_ID, ONE_PAGE],
|
||||
"(call $did_id (i32.const 0) (i32.const 20) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.did_keylets_asked.borrow(), vec![account]);
|
||||
}
|
||||
|
||||
/// The account-and-sequence keylet shape (like check), with its own answer set.
|
||||
#[test]
|
||||
fn escrow_id_reads_the_account_and_seq_and_writes_the_keylet() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_escrow_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::ESCROW_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $escrow_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.escrow_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A keylet getter reading three regions — two accounts and a currency: all three
|
||||
/// reach the host as a triple, and the keylet lands where the guest asked.
|
||||
#[test]
|
||||
fn trustline_id_reads_two_accounts_and_a_currency() {
|
||||
let account1 = vec![0u8; 20];
|
||||
let account2 = vec![0u8; 20];
|
||||
let currency = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_trust_line_keylet(
|
||||
account1.clone(),
|
||||
account2.clone(),
|
||||
currency.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::TRUSTLINE_ID, ONE_PAGE],
|
||||
"(call $trustline_id
|
||||
(i32.const 0) (i32.const 20)
|
||||
(i32.const 20) (i32.const 20)
|
||||
(i32.const 40) (i32.const 20)
|
||||
(i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(
|
||||
*host.trust_line_keylets_asked.borrow(),
|
||||
vec![(account1, account2, currency)]
|
||||
);
|
||||
}
|
||||
|
||||
/// The issuer-and-sequence keylet shape (like escrow), with its own answer set.
|
||||
#[test]
|
||||
fn mpt_issuance_id_reads_the_issuer_and_seq() {
|
||||
let issuer = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_mpt_issuance_keylet(
|
||||
issuer.clone(),
|
||||
5,
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::MPT_ISSUANCE_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $mpt_issuance_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.mpt_issuance_keylets_asked.borrow(), vec![(issuer, 5)]);
|
||||
}
|
||||
|
||||
/// A keylet from a 24-byte MPT id and a 20-byte holder: both reach the host as a
|
||||
/// pair, and the keylet lands where the guest asked.
|
||||
#[test]
|
||||
fn mptoken_id_reads_the_mptid_and_holder() {
|
||||
let mptid = vec![0u8; 24];
|
||||
let holder = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_mptoken_keylet(
|
||||
mptid.clone(),
|
||||
holder.clone(),
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::MPTOKEN_ID, ONE_PAGE],
|
||||
"(call $mptoken_id
|
||||
(i32.const 0) (i32.const 24)
|
||||
(i32.const 24) (i32.const 20)
|
||||
(i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.mptoken_keylets_asked.borrow(), vec![(mptid, holder)]);
|
||||
}
|
||||
|
||||
/// Another account-and-sequence keylet, with its own answer set.
|
||||
#[test]
|
||||
fn nft_offer_id_reads_the_account_and_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_nft_offer_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_OFFER_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $nft_offer_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.nft_offer_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A third account-and-sequence keylet, distinct from the NFT-offer set, to pin the
|
||||
/// pattern rather than a single instance of it.
|
||||
#[test]
|
||||
fn offer_id_reads_the_account_and_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_offer_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::OFFER_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $offer_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.offer_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// The account-and-scalar keylet, keyed on a document id rather than a sequence; its
|
||||
/// own answer set, to keep it distinct from the other account-and-scalar getters.
|
||||
#[test]
|
||||
fn oracle_id_reads_the_account_and_doc_id() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_oracle_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::ORACLE_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $oracle_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.oracle_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A keylet that reads two account regions and a scalar: both accounts and the
|
||||
/// sequence reach the host, keyed together, and the answered bytes land where asked.
|
||||
#[test]
|
||||
fn paychan_id_reads_both_accounts_and_the_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let destination = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_paychannel_keylet(
|
||||
account.clone(),
|
||||
destination.clone(),
|
||||
5,
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::PAYCHAN_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 24) (i32.const 5))
|
||||
(call $paychan_id (i32.const 0) (i32.const 20) (i32.const 32) (i32.const 20) (i32.const 24) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(
|
||||
*host.paychannel_keylets_asked.borrow(),
|
||||
vec![(account, destination, 5)]
|
||||
);
|
||||
}
|
||||
|
||||
/// Another account-and-sequence keylet, with its own answer set, for a permissioned
|
||||
/// domain.
|
||||
#[test]
|
||||
fn permissioned_domain_id_reads_the_account_and_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let host = FakeHost::new().answering_permissioned_domain_keylet(
|
||||
account.clone(),
|
||||
5,
|
||||
support::Answer::filler(32),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::PERMISSIONED_DOMAIN_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $permissioned_domain_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.domain_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// An account-only keylet: the account reaches the host and the answered bytes land
|
||||
/// where the guest asked, with no scalar in the shape.
|
||||
#[test]
|
||||
fn signers_id_reads_the_account() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_signer_list_keylet(account.clone(), support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::SIGNERS_ID, ONE_PAGE],
|
||||
"(call $signers_id (i32.const 0) (i32.const 20) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.signer_list_keylets_asked.borrow(), vec![account]);
|
||||
}
|
||||
|
||||
/// Another account-and-sequence keylet, with its own answer set, for a ticket.
|
||||
#[test]
|
||||
fn ticket_id_reads_the_account_and_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_ticket_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::TICKET_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $ticket_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.ticket_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// The last account-and-sequence keylet, with its own answer set, for a vault.
|
||||
#[test]
|
||||
fn vault_id_reads_the_account_and_seq() {
|
||||
let account = vec![0u8; 20];
|
||||
let host =
|
||||
FakeHost::new().answering_vault_keylet(account.clone(), 5, support::Answer::filler(32));
|
||||
|
||||
let wat = module(
|
||||
&[import::VAULT_ID, ONE_PAGE],
|
||||
"(i32.store (i32.const 20) (i32.const 5))
|
||||
(call $vault_id (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 4) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 32, "the 32-byte keylet length");
|
||||
assert_eq!(*host.vault_keylets_asked.borrow(), vec![(account, 5)]);
|
||||
}
|
||||
|
||||
/// A call that reads an input region and returns a scalar rather than writing bytes:
|
||||
/// the data blob reaches the host, and the byte count it reports comes back as the
|
||||
/// call's status.
|
||||
#[test]
|
||||
fn set_data_passes_the_data_through_and_returns_the_count() {
|
||||
let host = FakeHost::new().answering_update_data(Ok(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::SET_DATA, ONE_PAGE],
|
||||
"(call $set_data (i32.const 64) (i32.const 8))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the byte count the host reported");
|
||||
assert_eq!(
|
||||
*host.update_data_asked.borrow(),
|
||||
[vec![0u8; 8]],
|
||||
"the 8-byte region reached the host"
|
||||
);
|
||||
}
|
||||
|
||||
/// A getter that reads two input regions — an account and an nft id — and writes the
|
||||
/// answer to a third: both inputs reach the host, keyed together, and the bytes it
|
||||
/// answers land where the guest asked.
|
||||
#[test]
|
||||
fn nft_uri_reads_the_account_and_id_and_writes_the_uri() {
|
||||
let account = vec![0u8; 20];
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host = FakeHost::new().answering_get_nft(
|
||||
account.clone(),
|
||||
nft_id.clone(),
|
||||
support::Answer::bytes([0xab, 0xcd, 0xef]),
|
||||
);
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_URI, ONE_PAGE],
|
||||
"(call $nft_uri (i32.const 0) (i32.const 20) (i32.const 20) (i32.const 32) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 3, "the uri length");
|
||||
assert_eq!(*host.nfts_asked.borrow(), vec![(account, nft_id)]);
|
||||
}
|
||||
|
||||
/// A single-input byte getter: the nft id reaches the host and the issuer bytes it
|
||||
/// answers land where the guest asked.
|
||||
#[test]
|
||||
fn nft_issuer_reads_the_id_and_writes_the_issuer() {
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host = FakeHost::new().answering_nft_issuer(nft_id.clone(), support::Answer::filler(20));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_ISSUER, ONE_PAGE],
|
||||
"(call $nft_issuer (i32.const 0) (i32.const 32) (i32.const 64) (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 20, "the issuer length");
|
||||
assert_eq!(*host.nft_issuers_asked.borrow(), vec![nft_id]);
|
||||
}
|
||||
|
||||
/// A u32-valued getter whose four bytes the host writes to the output region: the id
|
||||
/// reaches the host, and the little-endian bytes land where the guest asked.
|
||||
#[test]
|
||||
fn nft_taxon_reads_the_id_and_writes_four_bytes() {
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host =
|
||||
FakeHost::new().answering_nft_taxon(nft_id.clone(), support::Answer::bytes([7, 0, 0, 0]));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_TAXON, ONE_PAGE],
|
||||
"(drop (call $nft_taxon (i32.const 0) (i32.const 32) (i32.const 64) (i32.const 4)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 7, "the taxon the host wrote");
|
||||
assert_eq!(*host.nft_taxons_asked.borrow(), vec![nft_id]);
|
||||
}
|
||||
|
||||
/// A single-input scalar getter: the nft id reaches the host and the flags it reports
|
||||
/// come back as the call's status, no output region involved.
|
||||
#[test]
|
||||
fn nft_flags_reads_the_id_and_returns_the_flags() {
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host = FakeHost::new().answering_nft_flags(Ok(11));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_FLAGS, ONE_PAGE],
|
||||
"(call $nft_flags (i32.const 0) (i32.const 32))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 11, "the flags the host reported");
|
||||
assert_eq!(*host.nft_flags_asked.borrow(), vec![nft_id]);
|
||||
}
|
||||
|
||||
/// A second scalar getter, to pin the pattern: the transfer fee comes back as the
|
||||
/// status.
|
||||
#[test]
|
||||
fn nft_xfer_fee_reads_the_id_and_returns_the_fee() {
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host = FakeHost::new().answering_nft_transfer_fee(Ok(314));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_XFER_FEE, ONE_PAGE],
|
||||
"(call $nft_xfer_fee (i32.const 0) (i32.const 32))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 314, "the fee the host reported");
|
||||
assert_eq!(*host.nft_fee_asked.borrow(), vec![nft_id]);
|
||||
}
|
||||
|
||||
/// The last NFT getter, a u32 sequence written to the output region.
|
||||
#[test]
|
||||
fn nft_serial_reads_the_id_and_writes_four_bytes() {
|
||||
let nft_id = vec![0u8; 32];
|
||||
let host = FakeHost::new()
|
||||
.answering_nft_sequence(nft_id.clone(), support::Answer::bytes([42, 0, 0, 0]));
|
||||
|
||||
let wat = module(
|
||||
&[import::NFT_SERIAL, ONE_PAGE],
|
||||
"(drop (call $nft_serial (i32.const 0) (i32.const 32) (i32.const 64) (i32.const 4)))
|
||||
(i32.load (i32.const 64))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 42, "the sequence the host wrote");
|
||||
assert_eq!(*host.nft_sequences_asked.borrow(), vec![nft_id]);
|
||||
}
|
||||
|
||||
/// A float built from an i64 scalar and no input region: the value and mode reach the
|
||||
/// host, and the float bytes it answers land where the guest asked. `float_from_int`
|
||||
/// carries a genuine `i64` parameter, so this pins that the wide scalar survives.
|
||||
#[test]
|
||||
fn float_from_int_passes_the_value_and_writes_the_float() {
|
||||
let host = FakeHost::new().answering_float(support::Answer::filler(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_FROM_INT, ONE_PAGE],
|
||||
"(call $float_from_int (i64.const 42) (i32.const 64) (i32.const 8) (i32.const 3))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the float length");
|
||||
assert_eq!(*host.float_from_int_asked.borrow(), vec![(42, 3)]);
|
||||
}
|
||||
|
||||
/// A float built from an 8-byte input region: the integer bytes and mode reach the
|
||||
/// host, and the float bytes it answers land where the guest asked.
|
||||
#[test]
|
||||
fn float_from_uint_reads_the_input_and_writes_the_float() {
|
||||
let host = FakeHost::new().answering_float(support::Answer::filler(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_FROM_UINT, ONE_PAGE],
|
||||
"(call $float_from_uint (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 1))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the float length");
|
||||
assert_eq!(
|
||||
*host.float_from_uint_asked.borrow(),
|
||||
vec![(vec![0u8; 8], 1)]
|
||||
);
|
||||
}
|
||||
|
||||
/// The one call that writes two output regions: the mantissa lands in the first, the
|
||||
/// exponent in the second, and the status is their combined length.
|
||||
#[test]
|
||||
fn float_to_mant_exp_writes_both_regions() {
|
||||
let host =
|
||||
FakeHost::new().answering_float_mant_exp(vec![1, 2, 3, 4, 5, 6, 7, 8], vec![9, 10, 11, 12]);
|
||||
|
||||
// Mantissa to offset 64, exponent to offset 80; read the first byte of each back.
|
||||
let wat = module(
|
||||
&[import::FLOAT_TO_MANT_EXP, ONE_PAGE],
|
||||
"(call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 80) (i32.const 4))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 12, "the mantissa and exponent lengths");
|
||||
assert_eq!(*host.float_to_mant_exp_asked.borrow(), vec![vec![0u8; 8]]);
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_TO_MANT_EXP, ONE_PAGE],
|
||||
"(drop (call $float_to_mant_exp (i32.const 0) (i32.const 8) (i32.const 64) (i32.const 8) (i32.const 80) (i32.const 4)))
|
||||
(i32.load8_u (i32.const 80))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 9, "the exponent's first byte");
|
||||
}
|
||||
|
||||
/// A comparison that reads two float regions and returns a scalar verdict, no output
|
||||
/// region involved.
|
||||
#[test]
|
||||
fn float_cmp_reads_both_and_returns_the_verdict() {
|
||||
let host = FakeHost::new().answering_float_compare(Ok(-1));
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_CMP, ONE_PAGE],
|
||||
"(call $float_cmp (i32.const 0) (i32.const 8) (i32.const 8) (i32.const 8))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), -1, "the comparison verdict");
|
||||
assert_eq!(
|
||||
*host.float_compare_asked.borrow(),
|
||||
vec![(vec![0u8; 8], vec![0u8; 8])]
|
||||
);
|
||||
}
|
||||
|
||||
/// A binary operator that reads two float regions and a mode, and writes the result:
|
||||
/// both operands and the mode reach the host, tagged by operator.
|
||||
#[test]
|
||||
fn float_add_reads_both_operands_and_the_mode() {
|
||||
let host = FakeHost::new().answering_float(support::Answer::filler(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_ADD, ONE_PAGE],
|
||||
"(call $float_add (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, "the result length");
|
||||
assert_eq!(
|
||||
*host.float_binops_asked.borrow(),
|
||||
vec![("add", vec![0u8; 8], vec![0u8; 8], 2)]
|
||||
);
|
||||
}
|
||||
|
||||
/// A unary operator that reads one float region, an integer, and a mode: all three
|
||||
/// reach the host, tagged by operator.
|
||||
#[test]
|
||||
fn float_root_reads_the_float_the_degree_and_the_mode() {
|
||||
let host = FakeHost::new().answering_float(support::Answer::filler(8));
|
||||
|
||||
let wat = module(
|
||||
&[import::FLOAT_ROOT, ONE_PAGE],
|
||||
"(call $float_root (i32.const 0) (i32.const 8) (i32.const 3) (i32.const 64) (i32.const 8) (i32.const 1))",
|
||||
);
|
||||
assert_eq!(status(&wat, &host), 8, "the result length");
|
||||
assert_eq!(
|
||||
*host.float_unops_asked.borrow(),
|
||||
vec![("root", vec![0u8; 8], 3, 1)]
|
||||
);
|
||||
}
|
||||
|
||||
/// A leading scalar parameter reaches the host as declared.
|
||||
#[test]
|
||||
fn home_le_field_passes_the_field_selector_through() {
|
||||
|
||||
@@ -98,11 +98,68 @@ fn a_disabled_feature_does_not_pass() {
|
||||
|
||||
/// Every host function the ABI declares, spelled as a guest imports it. The count
|
||||
/// is asserted against the ABI so a function added to it cannot be left out here.
|
||||
const ALL_IMPORTS: [&str; 4] = [
|
||||
const ALL_IMPORTS: [&str; 61] = [
|
||||
import::LDGR_INDEX,
|
||||
import::PARENT_LDGR_TIME,
|
||||
import::PARENT_LDGR_HASH,
|
||||
import::BASE_FEE,
|
||||
import::AMENDMENT_ENABLED,
|
||||
import::CACHE_LE,
|
||||
import::TX_FIELD,
|
||||
import::HOME_LE_FIELD,
|
||||
import::LE_FIELD,
|
||||
import::TX_INNER,
|
||||
import::HOME_LE_INNER,
|
||||
import::LE_INNER,
|
||||
import::TX_ARR_LEN,
|
||||
import::HOME_LE_ARR_LEN,
|
||||
import::LE_ARR_LEN,
|
||||
import::TX_INNER_ARR_LEN,
|
||||
import::HOME_LE_INNER_ARR_LEN,
|
||||
import::LE_INNER_ARR_LEN,
|
||||
import::CHECK_SIG,
|
||||
import::ACCOUNTROOT_ID,
|
||||
import::AMM_ID,
|
||||
import::CHECK_ID,
|
||||
import::CREDENTIAL_ID,
|
||||
import::DELEGATE_ID,
|
||||
import::DEPOSIT_PREAUTH_ID,
|
||||
import::DID_ID,
|
||||
import::ESCROW_ID,
|
||||
import::TRUSTLINE_ID,
|
||||
import::MPT_ISSUANCE_ID,
|
||||
import::MPTOKEN_ID,
|
||||
import::NFT_OFFER_ID,
|
||||
import::OFFER_ID,
|
||||
import::ORACLE_ID,
|
||||
import::PAYCHAN_ID,
|
||||
import::PERMISSIONED_DOMAIN_ID,
|
||||
import::SIGNERS_ID,
|
||||
import::TICKET_ID,
|
||||
import::VAULT_ID,
|
||||
import::SHA512_HALF,
|
||||
import::TRACE,
|
||||
import::SET_DATA,
|
||||
import::NFT_URI,
|
||||
import::NFT_ISSUER,
|
||||
import::NFT_TAXON,
|
||||
import::NFT_FLAGS,
|
||||
import::NFT_XFER_FEE,
|
||||
import::NFT_SERIAL,
|
||||
import::FLOAT_FROM_INT,
|
||||
import::FLOAT_FROM_UINT,
|
||||
import::FLOAT_FROM_STAMOUNT,
|
||||
import::FLOAT_FROM_STNUMBER,
|
||||
import::FLOAT_TO_INT,
|
||||
import::FLOAT_TO_MANT_EXP,
|
||||
import::FLOAT_FROM_MANT_EXP,
|
||||
import::FLOAT_CMP,
|
||||
import::FLOAT_ADD,
|
||||
import::FLOAT_SUB,
|
||||
import::FLOAT_MULT,
|
||||
import::FLOAT_DIV,
|
||||
import::FLOAT_ROOT,
|
||||
import::FLOAT_POW,
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -54,9 +54,226 @@ public:
|
||||
[[nodiscard]] std::int32_t
|
||||
getLedgerSqn(rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getParentLedgerTime(rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getParentLedgerHash(rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getBaseFee(rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The amendment is either a 32-byte id or a name; a 32-byte input is tried as an
|
||||
// id first and falls back to a name lookup. Answers 1 or 0, or a negative
|
||||
// `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
isAmendmentEnabled(rust::Slice<std::uint8_t const> amendment) const noexcept;
|
||||
|
||||
// The object id must be a 32-byte uint256, else `InvalidParams`. `cacheIdx` selects
|
||||
// the slot (0 = pick a free one). Answers the slot used, or a negative
|
||||
// `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
cacheLedgerObj(rust::Slice<std::uint8_t const> objId, std::int32_t cacheIdx) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getTxField(std::int32_t field, rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getCurrentLedgerObjField(std::int32_t field, rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getLedgerObjField(std::int32_t cacheIdx, std::int32_t field, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// The locator is a path of little-endian i32 steps, so its byte length must be a
|
||||
// non-zero multiple of 4, else `LocatorMalformed`.
|
||||
[[nodiscard]] std::int32_t
|
||||
getTxNestedField(rust::Slice<std::uint8_t const> locator, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getCurrentLedgerObjNestedField(
|
||||
rust::Slice<std::uint8_t const> locator,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getLedgerObjNestedField(
|
||||
std::int32_t cacheIdx,
|
||||
rust::Slice<std::uint8_t const> locator,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Answers the array's element count directly, or a negative `HostFunctionError`
|
||||
// code (`NoArray` if the field is not an array).
|
||||
[[nodiscard]] std::int32_t
|
||||
getTxArrayLen(std::int32_t field) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getCurrentLedgerObjArrayLen(std::int32_t field) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getLedgerObjArrayLen(std::int32_t cacheIdx, std::int32_t field) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getTxNestedArrayLen(rust::Slice<std::uint8_t const> locator) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getCurrentLedgerObjNestedArrayLen(rust::Slice<std::uint8_t const> locator) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
getLedgerObjNestedArrayLen(std::int32_t cacheIdx, rust::Slice<std::uint8_t const> locator)
|
||||
const noexcept;
|
||||
|
||||
// Answers 1/0 for a valid/invalid signature, or a negative `HostFunctionError`.
|
||||
[[nodiscard]] std::int32_t
|
||||
checkSignature(
|
||||
rust::Slice<std::uint8_t const> message,
|
||||
rust::Slice<std::uint8_t const> signature,
|
||||
rust::Slice<std::uint8_t const> pubkey) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
accountKeylet(rust::Slice<std::uint8_t const> account, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// Each asset is decoded by length (24 = MPT, 20 = XRP, 40 = issue), else
|
||||
// `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
ammKeylet(
|
||||
rust::Slice<std::uint8_t const> asset1,
|
||||
rust::Slice<std::uint8_t const> asset2,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
checkKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Subject and issuer must each be 20 bytes, else `InvalidParams`. Writes the
|
||||
// 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
credentialKeylet(
|
||||
rust::Slice<std::uint8_t const> subject,
|
||||
rust::Slice<std::uint8_t const> issuer,
|
||||
rust::Slice<std::uint8_t const> credentialType,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Both accounts must be 20 bytes, else `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
delegateKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
rust::Slice<std::uint8_t const> authorize,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Both accounts must be 20 bytes, else `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
depositPreauthKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
rust::Slice<std::uint8_t const> authorize,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
didKeylet(rust::Slice<std::uint8_t const> account, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
escrowKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Both accounts and the currency must each be 20 bytes, else `InvalidParams`.
|
||||
// Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
trustLineKeylet(
|
||||
rust::Slice<std::uint8_t const> account1,
|
||||
rust::Slice<std::uint8_t const> account2,
|
||||
rust::Slice<std::uint8_t const> currency,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The issuer id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
mptokenIssuanceKeylet(
|
||||
rust::Slice<std::uint8_t const> issuer,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The MPT id must be 24 bytes and the holder 20, else `InvalidParams`. Writes the
|
||||
// 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
mptokenKeylet(
|
||||
rust::Slice<std::uint8_t const> mptid,
|
||||
rust::Slice<std::uint8_t const> holder,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
nftokenOfferKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
offerKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `docId` carries the
|
||||
// guest's u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
oracleKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t docId,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Both account ids must be 20 bytes, else `InvalidParams`. `seq` carries the
|
||||
// guest's u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
paychannelKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
rust::Slice<std::uint8_t const> destination,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
permissionedDomainKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
signerListKeylet(rust::Slice<std::uint8_t const> account, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
ticketKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes, else `InvalidParams`. `seq` carries the guest's
|
||||
// u32 as its i32 bit pattern. Writes the 32-byte keylet.
|
||||
[[nodiscard]] std::int32_t
|
||||
vaultKeylet(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
std::int32_t seq,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
sha512Half(rust::Slice<std::uint8_t const> data, rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
@@ -69,6 +286,142 @@ public:
|
||||
void
|
||||
trace(rust::Str msg, rust::Slice<std::uint8_t const> data, TraceDataType dataType)
|
||||
const noexcept;
|
||||
|
||||
// Stores `data` as the current object's data field and returns the number of bytes
|
||||
// stored, or a negative `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
updateData(rust::Slice<std::uint8_t const> data) const noexcept;
|
||||
|
||||
// The account id must be 20 bytes and the nft id 32 bytes, else `InvalidParams`.
|
||||
// Writes the token's URI bytes.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFT(
|
||||
rust::Slice<std::uint8_t const> account,
|
||||
rust::Slice<std::uint8_t const> nftId,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The nft id must be 32 bytes, else `InvalidParams`. Writes the 20-byte issuer
|
||||
// account encoded in the id.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFTIssuer(rust::Slice<std::uint8_t const> nftId, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// The nft id must be 32 bytes, else `InvalidParams`. Writes the taxon as its four
|
||||
// little-endian bytes.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFTTaxon(rust::Slice<std::uint8_t const> nftId, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// The nft id must be 32 bytes, else `InvalidParams`. Returns the flags, or a
|
||||
// negative `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFTFlags(rust::Slice<std::uint8_t const> nftId) const noexcept;
|
||||
|
||||
// The nft id must be 32 bytes, else `InvalidParams`. Returns the transfer fee, or a
|
||||
// negative `HostFunctionError` code.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFTTransferFee(rust::Slice<std::uint8_t const> nftId) const noexcept;
|
||||
|
||||
// The nft id must be 32 bytes, else `InvalidParams`. Writes the sequence number as
|
||||
// its four little-endian bytes.
|
||||
[[nodiscard]] std::int32_t
|
||||
getNFTSequence(rust::Slice<std::uint8_t const> nftId, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// Float / number arithmetic. A float is an XRPL `Number` in serialized form;
|
||||
// `mode` is a rounding mode. Each writes the result float bytes unless noted.
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatFromInt(std::int64_t x, std::int32_t mode, rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// The integer region must be eight bytes, else `InvalidParams`.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatFromUint(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// `amount` must be a serialized `STAmount`, else `InvalidParams`.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatFromSTAmount(
|
||||
rust::Slice<std::uint8_t const> amount,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// `number` must be a serialized `STNumber`, else `InvalidParams`.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatFromSTNumber(
|
||||
rust::Slice<std::uint8_t const> number,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Rounds the float to an integer, written as its eight little-endian bytes.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatToInt(rust::Slice<std::uint8_t const> x, std::int32_t mode, rust::Slice<std::uint8_t> out)
|
||||
const noexcept;
|
||||
|
||||
// Writes the mantissa (eight little-endian bytes) and the exponent (four little-
|
||||
// endian bytes) to two output regions; returns their total size.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatToMantExp(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
rust::Slice<std::uint8_t> mantissaOut,
|
||||
rust::Slice<std::uint8_t> exponentOut) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatFromMantExp(
|
||||
std::int64_t mantissa,
|
||||
std::int32_t exponent,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
// Returns a negative, zero, or positive scalar as `x` is less than, equal to, or
|
||||
// greater than `y`, or a negative `HostFunctionError` code on failure.
|
||||
[[nodiscard]] std::int32_t
|
||||
floatCompare(rust::Slice<std::uint8_t const> x, rust::Slice<std::uint8_t const> y)
|
||||
const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatAdd(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
rust::Slice<std::uint8_t const> y,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatSubtract(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
rust::Slice<std::uint8_t const> y,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatMultiply(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
rust::Slice<std::uint8_t const> y,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatDivide(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
rust::Slice<std::uint8_t const> y,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatRoot(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
std::int32_t n,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
|
||||
[[nodiscard]] std::int32_t
|
||||
floatPower(
|
||||
rust::Slice<std::uint8_t const> x,
|
||||
std::int32_t n,
|
||||
std::int32_t mode,
|
||||
rust::Slice<std::uint8_t> out) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/protocol/STAmount.h>
|
||||
#include <xrpl/protocol/Serializer.h>
|
||||
#include <xrpl/tx/wasm/HostContext.h>
|
||||
#include <xrpl/tx/wasm/HostFunc.h>
|
||||
#include <xrpl/tx/wasm/WasmCommon.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user