pub mod branch;
pub mod check_eq;
pub mod check_preimage;
pub mod check_preimage_value;
pub mod check_signature;
pub mod log;
pub mod push;
pub mod push_value;
use crate::{
error::ApiError,
types::{WasmPtr, WasmSize},
Context, Error,
};
use wasmtime::{AsContextMut, Caller, Engine, Extern, Linker, Val};
pub const WASM_TRUE: Val = Val::I32(1);
pub const WASM_FALSE: Val = Val::I32(0);
pub fn add_to_linker(engine: &Engine, linker: &mut Linker<Context>) -> Result<(), Error> {
branch::add_to_linker(engine, linker)?;
check_eq::add_to_linker(engine, linker)?;
check_preimage::add_to_linker(engine, linker)?;
check_preimage_value::add_to_linker(engine, linker)?;
check_signature::add_to_linker(engine, linker)?;
log::add_to_linker(engine, linker)?;
push::add_to_linker(engine, linker)?;
push_value::add_to_linker(engine, linker)?;
Ok(())
}
pub fn get_bytes(caller: &mut Caller<'_, Context>, params: &[Val]) -> Result<Vec<u8>, Error> {
let mem = match caller.get_export("memory") {
Some(Extern::Memory(mem)) => mem,
_ => {
return Err(ApiError::MissingExport {
export_name: "memory".to_string(),
context: "get_bytes requires linear memory export".to_string(),
}
.into())
}
};
if params.len() < 2 {
return Err(ApiError::IncorrectNumberOfParams {
expected: 2,
received: params.len(),
}
.into());
}
let ptr = match params[0].i32() {
Some(val) => WasmPtr::from(val),
_ => {
return Err(ApiError::InvalidParam {
param_index: 0,
reason: "expected i32 pointer parameter".to_string(),
}
.into())
}
};
let len = match params[1].i32() {
Some(val) => {
let size = WasmSize::from(val);
match WasmSize::new_checked(size.as_u32()) {
Some(s) => s,
None => {
return Err(ApiError::InvalidParam {
param_index: 1,
reason: format!(
"length {} exceeds maximum allowed size {}",
size.as_u32(),
WasmSize::MAX_SIZE
),
}
.into())
}
}
}
_ => {
return Err(ApiError::InvalidParam {
param_index: 1,
reason: "expected i32 length parameter".to_string(),
}
.into())
}
};
if ptr.would_overflow(len) {
return Err(ApiError::MemoryDecodeError {
offset: ptr.as_usize(),
length: len.as_usize(),
reason: "pointer + length would overflow".to_string(),
}
.into());
}
let bytes = {
let mut buf = vec![0u8; len.as_usize()];
mem.read(&caller, ptr.as_usize(), buf.as_mut_slice())
.map_err(|e| ApiError::MemoryDecodeError {
offset: ptr.as_usize(),
length: len.as_usize(),
reason: format!("failed to read from linear memory: {e}"),
})?;
buf
};
Ok(bytes)
}
pub fn get_string(caller: &mut Caller<'_, Context>, params: &[Val]) -> Result<String, Error> {
let mem = match caller.get_export("memory") {
Some(Extern::Memory(mem)) => mem,
_ => {
return Err(ApiError::MissingExport {
export_name: "memory".to_string(),
context: "get_string requires linear memory export".to_string(),
}
.into())
}
};
if params.len() < 2 {
return Err(ApiError::IncorrectNumberOfParams {
expected: 2,
received: params.len(),
}
.into());
}
let ptr = match params[0].i32() {
Some(val) => WasmPtr::from(val),
_ => {
return Err(ApiError::InvalidParam {
param_index: 0,
reason: "expected i32 pointer parameter".to_string(),
}
.into())
}
};
let len = match params[1].i32() {
Some(val) => {
let size = WasmSize::from(val);
match WasmSize::new_checked(size.as_u32()) {
Some(s) => s,
None => {
return Err(ApiError::InvalidParam {
param_index: 1,
reason: format!(
"length {} exceeds maximum allowed size {}",
size.as_u32(),
WasmSize::MAX_SIZE
),
}
.into())
}
}
}
_ => {
return Err(ApiError::InvalidParam {
param_index: 1,
reason: "expected i32 length parameter".to_string(),
}
.into())
}
};
if ptr.would_overflow(len) {
return Err(ApiError::MemoryDecodeError {
offset: ptr.as_usize(),
length: len.as_usize(),
reason: "pointer + length would overflow".to_string(),
}
.into());
}
let s = {
let mut buf = vec![0u8; len.as_usize()];
mem.read(&caller, ptr.as_usize(), buf.as_mut_slice())
.map_err(|e| ApiError::MemoryDecodeError {
offset: ptr.as_usize(),
length: len.as_usize(),
reason: format!("failed to read from linear memory: {e}"),
})?;
String::from_utf8(buf)?
};
Ok(s)
}
pub fn put_string(
caller: &mut Caller<'_, Context>,
s: &str,
results: &mut [Val],
) -> Result<(), Error> {
if results.len() < 2 {
return Err(ApiError::IncorrectNumberOfResults {
expected: 2,
received: results.len(),
}
.into());
}
let mem = match caller.get_export("memory") {
Some(Extern::Memory(mem)) => mem,
_ => {
return Err(ApiError::MissingExport {
export_name: "memory".to_string(),
context: "put_string requires linear memory export".to_string(),
}
.into())
}
};
let size = mem.data_size(&caller);
let write_idx = {
let mut ctx = caller.as_context_mut();
let context = ctx.data_mut();
context.write_idx += s.len();
size - context.write_idx - 1
};
let write_ptr = WasmPtr::new(write_idx as u32);
let write_size = WasmSize::new(s.len() as u32);
results[0] = Val::I32(write_ptr.as_u32() as i32);
results[1] = Val::I32(write_size.as_u32() as i32);
mem.write(caller, write_ptr.as_usize(), s.as_bytes())
.map_err(|e| ApiError::MemoryAccess {
offset: write_ptr.as_usize(),
reason: format!("failed to write to linear memory: {e}"),
})?;
Ok(())
}