use std::collections::HashMap;
use solana_sdk::pubkey::Pubkey;
use super::{
err::CatscopeWasmError,
header::{AccountHeader, AccountId},
wasmstore::{GuestBlob, Store},
};
#[derive(Default)]
pub struct HostImport {
store: Store,
m_pubkey: HashMap<Pubkey, AccountId>,
pub m_tx: HashMap<u64, TransactionState>,
}
pub struct TransactionState {
status: u8,
slot: u64,
}
const MAX_PUBKEY_HASHMAP_SIZE: usize = 1000;
impl HostImport {
pub fn store(&self) -> &Store {
&self.store
}
pub fn store_mut(&mut self) -> &mut Store {
&mut self.store
}
pub fn init_args(&mut self) -> Result<Option<GuestBlob>, CatscopeWasmError> {
let size = unsafe { hf_init_args_size() };
let mut o_blob = self.store.allocate2(size as usize);
if o_blob.is_none() {
return Ok(None);
}
let blob = o_blob.take().unwrap();
let ptr = blob.pointer();
let result = unsafe { hf_init_args(ptr) };
if result < 0 {
Err(CatscopeWasmError::InsufficientBuffer)
} else {
Ok(Some(blob))
}
}
pub fn pubkey_lookup(
&mut self,
list: &[Pubkey],
) -> Result<Option<GuestBlob>, CatscopeWasmError> {
if list.is_empty() {
return Ok(None);
}
if list.len() == 1 {
if self.m_pubkey.contains_key(&list[0]) {
return Ok(None);
}
}
let node_len = std::mem::size_of::<AccountId>();
let store = self.store_mut();
let blob = store.allocate2(std::mem::size_of_val(list)).unwrap();
let req_ptr = blob.pointer();
let req_len = std::mem::size_of_val(list) as u32;
let resp_blob = store.allocate2(node_len * list.len()).unwrap();
let resp_callback_id = resp_blob.pointer();
let result = unsafe { hf_pubkey_lookup(req_ptr, req_len, resp_callback_id) };
if result < 0 {
return Err(CatscopeWasmError::InsufficientBuffer);
}
let resp_slice =
unsafe { std::slice::from_raw_parts(resp_callback_id as *const AccountId, list.len()) };
if MAX_PUBKEY_HASHMAP_SIZE < self.m_pubkey.len() {
self.m_pubkey.clear();
}
for i in 0..list.len() {
self.m_pubkey.insert(list[i], resp_slice[i]);
}
Ok(Some(resp_blob))
}
pub fn header(
&mut self,
node_id: AccountId,
o_slot: Option<u64>,
) -> Result<Option<GuestBlob>, CatscopeWasmError> {
let header_size = std::mem::size_of::<AccountHeader>();
let store = self.store_mut();
let blob = store.allocate2(header_size).unwrap();
let header_ptr = blob.pointer();
let s = o_slot.unwrap_or_default();
let header_result = unsafe { hf_account_header(header_ptr, node_id, s) };
if header_result < 0 {
Err(CatscopeWasmError::InsufficientBuffer)
} else if header_result == 0 {
Ok(None)
} else {
Ok(Some(blob))
}
}
pub fn body(
&mut self,
header_blob: &GuestBlob,
) -> Result<Option<GuestBlob>, CatscopeWasmError> {
let header: &AccountHeader = header_blob.payload();
let size = header.data_size as usize;
if size == 0 {
return Ok(None);
}
let list = vec![header.pubkey];
self.pubkey_lookup(&list)?;
let node_id = *self.m_pubkey.get(&header.pubkey).unwrap();
let store = self.store_mut();
let body_blob = store.allocate2(size).unwrap();
let req_callback_id = body_blob.pointer();
let result = unsafe { hf_account_body(req_callback_id, node_id, header.slot) };
if result < 0 {
Err(CatscopeWasmError::InsufficientBuffer)
} else if result == 0 {
Ok(None)
} else {
Ok(Some(body_blob))
}
}
pub fn tx_send(&mut self, tx: &[u8]) -> Result<u64, CatscopeWasmError> {
if tx.is_empty() {
return Err(CatscopeWasmError::InsufficientBuffer);
}
let mut blob = match self.store.allocate2(tx.len()) {
Some(x) => x,
None => return Err(CatscopeWasmError::InsufficientBuffer),
};
{
let slice = blob.slice_mut();
slice.copy_from_slice(tx);
}
let req_ptr = blob.pointer();
let req_len = tx.len() as u32;
let result = unsafe { hf_tx_send(req_ptr, req_len) };
if result < 0 {
Err(CatscopeWasmError::NotImplemented)
} else {
self.m_tx
.insert(req_ptr, TransactionState { status: 0, slot: 0 });
Ok(req_ptr)
}
}
pub fn tx_response(&mut self, callback_id: u64, slot: u64, status: u8) {
if let Some(ts) = self.m_tx.get_mut(&callback_id) {
ts.slot = slot;
ts.status = status;
}
}
pub fn log(str: String) {
let ptr: *const u8 = str.as_ptr();
unsafe { hf_simple_log(ptr as u64, str.len() as u32) };
}
}
pub trait CatscopeBot {
fn on_slot(&mut self, slot: u64, status: u8) -> std::io::Result<()>;
}
pub struct Bot<B: CatscopeBot> {
hook: B,
pub store: Store,
}
impl<B: CatscopeBot> Bot<B> {
pub fn new(hook: B) -> Result<Self, CatscopeWasmError> {
let store = Store::default();
Ok(Self { hook, store })
}
}
#[link(wasm_import_module = "")]
extern "C" {
fn hf_simple_log(ptr: u64, size: u32);
fn hf_init_args_size() -> u32;
fn hf_init_args(req_callback_id: u64) -> i32;
fn hf_pubkey_lookup(req_ptr: u64, req_len: u32, resp_callback_id: u64) -> i32;
fn hf_account_header(req_callback_id: u64, node_id: u64, o_slot: u64) -> i32;
fn hf_account_body(req_callback_id: u64, node_id: u64, o_slot: u64) -> i32;
fn hf_tx_send(req_ptr: u64, req_len: u32) -> i32;
}