use pchain_types::cryptography::PublicAddress;
use crate::{
contract::{blank, Importable},
wasmer::cache::ModuleMetadata,
Cache as SmartContractCache,
};
use super::{
instance::{ContractValidateError, Instance},
CONTRACT_METHOD,
};
pub(crate) struct Module(pub wasmer::Module, pub ModuleMetadata);
impl Module {
pub fn from_cache(
address: PublicAddress,
cache: &SmartContractCache,
wasmer_store: &wasmer::Store,
) -> Option<Module> {
match cache.load(address, wasmer_store) {
Ok((m, d)) => Some(Module(m, d)),
Err(_) => None,
}
}
pub fn cache_to(&self, address: PublicAddress, cache: &mut SmartContractCache) {
let _ = cache.store(address, &self.0, self.1.bytes_length);
}
pub fn from_wasm_bytecode(
cbi_version: u32,
bytecode: &Vec<u8>,
wasmer_store: &wasmer::Store,
) -> Result<Module, ModuleBuildError> {
let wasmer_module = match wasmer::Module::from_binary(wasmer_store, bytecode) {
Ok(m) => m,
Err(e) => {
if e.to_string().contains("OpcodeError") {
return Err(ModuleBuildError::DisallowedOpcodePresent);
}
return Err(ModuleBuildError::Else);
}
};
Ok(Module(
wasmer_module,
ModuleMetadata {
cbi_version,
bytes_length: bytecode.len(),
},
))
}
pub fn from_wasm_bytecode_unchecked(
cbi_version: u32,
bytecode: &Vec<u8>,
wasmer_store: &wasmer::Store,
) -> Result<Module, ModuleBuildError> {
let wasmer_module =
match unsafe { wasmer::Module::from_binary_unchecked(wasmer_store, bytecode) } {
Ok(m) => m,
Err(e) => {
if e.to_string().contains("OpcodeError") {
return Err(ModuleBuildError::DisallowedOpcodePresent);
}
return Err(ModuleBuildError::Else);
}
};
Ok(Module(
wasmer_module,
ModuleMetadata {
cbi_version,
bytes_length: bytecode.len(),
},
))
}
pub fn bytes_length(&self) -> usize {
self.1.bytes_length
}
pub fn instantiate(
&self,
importable: &Importable,
gas_limit: u64,
) -> Result<Instance, wasmer::InstantiationError> {
let wasmer_instance = wasmer::Instance::new(&self.0, &importable.0)?;
wasmer_middlewares::metering::set_remaining_points(&wasmer_instance, gas_limit);
Ok(Instance(wasmer_instance))
}
pub fn validate_contract(
&self,
wasmer_store: &wasmer::Store,
) -> Result<(), ContractValidateError> {
if !self
.0
.exports()
.functions()
.any(|f| f.name() == CONTRACT_METHOD)
{
return Err(ContractValidateError::MethodNotFound);
}
let imports_object = blank::imports(wasmer_store);
if let Ok(instance) = wasmer::Instance::new(&self.0, &imports_object) {
if instance
.exports
.get_native_function::<(), ()>(CONTRACT_METHOD)
.is_ok()
{
return Ok(());
}
}
Err(ContractValidateError::InstantiateError)
}
}
#[derive(Debug)]
pub(crate) enum ModuleBuildError {
DisallowedOpcodePresent,
Else,
}