use crate::runtime::StoreData;
use wasmtime::{Caller, Linker};
pub fn register_host_functions(linker: &mut Linker<StoreData>) -> Result<(), wasmtime::Error> {
linker.func_wrap("env", "host_log", host_log)?;
linker.func_wrap("env", "host_has_path", host_has_path)?;
linker.func_wrap("env", "host_get_hops", host_get_hops)?;
linker.func_wrap("env", "host_get_next_hop", host_get_next_hop)?;
linker.func_wrap("env", "host_is_blackholed", host_is_blackholed)?;
linker.func_wrap("env", "host_get_interface_name", host_get_interface_name)?;
linker.func_wrap("env", "host_get_interface_mode", host_get_interface_mode)?;
linker.func_wrap(
"env",
"host_get_transport_identity",
host_get_transport_identity,
)?;
linker.func_wrap("env", "host_get_announce_rate", host_get_announce_rate)?;
linker.func_wrap("env", "host_get_link_state", host_get_link_state)?;
linker.func_wrap("env", "host_inject_action", host_inject_action)?;
linker.func_wrap("env", "host_emit_event", host_emit_event)?;
Ok(())
}
fn get_memory(caller: &mut Caller<'_, StoreData>) -> Option<wasmtime::Memory> {
caller.get_export("memory").and_then(|e| e.into_memory())
}
fn read_bytes<'a>(data: &'a [u8], ptr: usize, len: usize) -> Option<&'a [u8]> {
let end = ptr.checked_add(len)?;
data.get(ptr..end)
}
fn read_16(data: &[u8], ptr: usize) -> Option<[u8; 16]> {
read_bytes(data, ptr, 16).map(|s| {
let mut arr = [0u8; 16];
arr.copy_from_slice(s);
arr
})
}
fn host_log(mut caller: Caller<'_, StoreData>, ptr: i32, len: i32) {
if ptr < 0 || len < 0 {
return;
}
let Some(memory) = get_memory(&mut caller) else {
return;
};
let data = memory.data(&caller);
if let Some(bytes) = read_bytes(data, ptr as usize, len as usize) {
let msg = String::from_utf8_lossy(bytes).to_string();
log::debug!("[wasm-hook] {}", msg);
caller.data_mut().log_messages.push(msg);
}
}
fn host_has_path(mut caller: Caller<'_, StoreData>, dest_ptr: i32) -> i32 {
let Some(memory) = get_memory(&mut caller) else {
return 0;
};
let data = memory.data(&caller);
let Some(dest) = read_16(data, dest_ptr as usize) else {
return 0;
};
let result = unsafe { caller.data().engine().has_path(&dest) };
result as i32
}
fn host_get_hops(mut caller: Caller<'_, StoreData>, dest_ptr: i32) -> i32 {
let Some(memory) = get_memory(&mut caller) else {
return -1;
};
let data = memory.data(&caller);
let Some(dest) = read_16(data, dest_ptr as usize) else {
return -1;
};
match unsafe { caller.data().engine().hops_to(&dest) } {
Some(h) => h as i32,
None => -1,
}
}
fn host_get_next_hop(mut caller: Caller<'_, StoreData>, dest_ptr: i32, out_ptr: i32) -> i32 {
let Some(memory) = get_memory(&mut caller) else {
return 0;
};
let data = memory.data(&caller);
let Some(dest) = read_16(data, dest_ptr as usize) else {
return 0;
};
match unsafe { caller.data().engine().next_hop(&dest) } {
Some(hop) => {
let data = memory.data_mut(&mut caller);
let out = out_ptr as usize;
if out + 16 > data.len() {
return 0;
}
data[out..out + 16].copy_from_slice(&hop);
1
}
None => 0,
}
}
fn host_is_blackholed(mut caller: Caller<'_, StoreData>, identity_ptr: i32) -> i32 {
let Some(memory) = get_memory(&mut caller) else {
return 0;
};
let data = memory.data(&caller);
let Some(identity) = read_16(data, identity_ptr as usize) else {
return 0;
};
let result = unsafe { caller.data().engine().is_blackholed(&identity) };
result as i32
}
fn host_get_interface_name(
mut caller: Caller<'_, StoreData>,
id: i64,
out_ptr: i32,
out_len: i32,
) -> i32 {
if out_ptr < 0 || out_len < 0 {
return -1;
}
let name = unsafe { caller.data().engine().interface_name(id as u64) };
let Some(name) = name else { return -1 };
let Some(memory) = get_memory(&mut caller) else {
return -1;
};
let bytes = name.as_bytes();
let write_len = bytes.len().min(out_len as usize);
let data = memory.data_mut(&mut caller);
let out = out_ptr as usize;
if out + write_len > data.len() {
return -1;
}
data[out..out + write_len].copy_from_slice(&bytes[..write_len]);
write_len as i32
}
fn host_get_interface_mode(caller: Caller<'_, StoreData>, id: i64) -> i32 {
match unsafe { caller.data().engine().interface_mode(id as u64) } {
Some(m) => m as i32,
None => -1,
}
}
fn host_get_transport_identity(mut caller: Caller<'_, StoreData>, out_ptr: i32) -> i32 {
let hash = unsafe { caller.data().engine().identity_hash() };
let Some(hash) = hash else { return 0 };
let Some(memory) = get_memory(&mut caller) else {
return 0;
};
let data = memory.data_mut(&mut caller);
let out = out_ptr as usize;
if out + 16 > data.len() {
return 0;
}
data[out..out + 16].copy_from_slice(&hash);
1
}
fn host_get_announce_rate(caller: Caller<'_, StoreData>, id: i64) -> i32 {
match unsafe { caller.data().engine().announce_rate(id as u64) } {
Some(rate) => rate,
None => -1,
}
}
fn host_get_link_state(mut caller: Caller<'_, StoreData>, link_hash_ptr: i32) -> i32 {
let Some(memory) = get_memory(&mut caller) else {
return -1;
};
let data = memory.data(&caller);
let Some(hash) = read_16(data, link_hash_ptr as usize) else {
return -1;
};
match unsafe { caller.data().engine().link_state(&hash) } {
Some(state) => state as i32,
None => -1,
}
}
fn host_inject_action(mut caller: Caller<'_, StoreData>, action_ptr: i32, action_len: i32) -> i32 {
if action_ptr < 0 || action_len <= 0 {
return -1;
}
let Some(memory) = get_memory(&mut caller) else {
return -1;
};
let data = memory.data(&caller);
match crate::arena::read_action_wire(data, action_ptr as usize, action_len as usize) {
Some(action) => {
caller.data_mut().injected_actions.push(action);
0
}
None => -1,
}
}
fn host_emit_event(
mut caller: Caller<'_, StoreData>,
type_ptr: i32,
type_len: i32,
payload_ptr: i32,
payload_len: i32,
) -> i32 {
if type_ptr < 0 || type_len < 0 || payload_ptr < 0 || payload_len < 0 {
return -1;
}
if !caller.data().provider_events_enabled {
return -2;
}
let Some(memory) = get_memory(&mut caller) else {
return -1;
};
let data = memory.data(&caller);
let Some(payload_type_bytes) = read_bytes(data, type_ptr as usize, type_len as usize) else {
return -1;
};
let Some(payload) = read_bytes(data, payload_ptr as usize, payload_len as usize) else {
return -1;
};
let payload_type = String::from_utf8_lossy(payload_type_bytes).to_string();
let payload = payload.to_vec();
caller
.data_mut()
.provider_events
.push(crate::runtime::RawProviderEvent {
payload_type,
payload,
});
0
}