use super::engine::{WasmEngine, WasmError};
use super::native_api::CONTRACT_IO;
use super::{ContractExecError, RuntimeResult};
use freenet_stdlib::prelude::{
CodeHash, ContractContainer, ContractInstanceId, ContractInterfaceResult, ContractKey,
Parameters, RelatedContracts, StateDelta, StateSummary, UpdateData, UpdateModification,
ValidateResult, WrappedState,
};
const STREAMING_BUF_CAP: usize = 64 * 1024;
pub(crate) trait ContractRuntimeInterface {
fn validate_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
related: &RelatedContracts<'_>,
) -> RuntimeResult<ValidateResult>;
fn update_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
update_data: &[UpdateData<'_>],
) -> RuntimeResult<UpdateModification<'static>>;
fn summarize_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
) -> RuntimeResult<StateSummary<'static>>;
fn get_state_delta(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
delta_to: &StateSummary<'_>,
) -> RuntimeResult<StateDelta<'static>>;
}
pub(crate) trait ContractStoreBridge {
fn code_hash_from_id(&self, id: &ContractInstanceId) -> Option<CodeHash>;
fn fetch_contract_code(
&self,
key: &ContractKey,
params: &Parameters<'_>,
) -> Option<ContractContainer>;
fn store_contract(&mut self, contract: ContractContainer) -> Result<(), anyhow::Error>;
fn remove_contract(&mut self, key: &ContractKey) -> Result<(), anyhow::Error>;
fn ensure_key_indexed(&mut self, key: &ContractKey) -> Result<(), anyhow::Error>;
}
pub(crate) trait ContractRuntimeBridge:
ContractRuntimeInterface + ContractStoreBridge + Send + 'static
{
}
struct ContractIoGuard {
instance_id: i64,
}
impl ContractIoGuard {
fn new(instance_id: i64) -> Self {
Self { instance_id }
}
}
impl Drop for ContractIoGuard {
fn drop(&mut self) {
CONTRACT_IO.retain(|(id, _), _| *id != self.instance_id);
}
}
impl ContractRuntimeInterface for super::Runtime {
fn validate_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
related: &RelatedContracts<'_>,
) -> RuntimeResult<ValidateResult> {
let req_bytes = parameters.size() + state.size();
let mut running = self.prepare_contract_call(key, parameters, req_bytes)?;
let _io_guard = ContractIoGuard::new(running.id);
let result = (|| -> RuntimeResult<ValidateResult> {
let linear_mem = self.linear_mem(&running.handle)?;
let param_buf_ptr =
self.write_contract_buf(&running, parameters.as_ref(), STREAMING_BUF_CAP)?;
let state_buf_ptr =
self.write_contract_buf(&running, state.as_ref(), STREAMING_BUF_CAP)?;
let related_buf_ptr =
self.write_contract_buf_serialized(&running, related, STREAMING_BUF_CAP)?;
let result = self.engine.call_3i64_blocking(
&running.handle,
"validate_state",
param_buf_ptr as i64,
state_buf_ptr as i64,
related_buf_ptr as i64,
);
let result = classify_result(result)?;
let is_valid = unsafe {
ContractInterfaceResult::from_raw(result, &linear_mem)
.unwrap_validate_state_res(linear_mem)
.map_err(Into::<ContractExecError>::into)?
};
Ok(is_valid)
})();
self.drop_running_instance(&mut running);
result
}
fn update_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
update_data: &[UpdateData<'_>],
) -> RuntimeResult<UpdateModification<'static>> {
let req_bytes =
parameters.size() + state.size() + update_data.iter().map(|e| e.size()).sum::<usize>();
let mut running = self.prepare_contract_call(key, parameters, req_bytes)?;
let _io_guard = ContractIoGuard::new(running.id);
let result = (|| -> RuntimeResult<UpdateModification<'static>> {
let linear_mem = self.linear_mem(&running.handle)?;
let param_buf_ptr =
self.write_contract_buf(&running, parameters.as_ref(), STREAMING_BUF_CAP)?;
let state_buf_ptr =
self.write_contract_buf(&running, state.as_ref(), STREAMING_BUF_CAP)?;
let update_data_buf_ptr =
self.write_contract_buf_serialized(&running, update_data, STREAMING_BUF_CAP)?;
let result = self.engine.call_3i64_blocking(
&running.handle,
"update_state",
param_buf_ptr as i64,
state_buf_ptr as i64,
update_data_buf_ptr as i64,
);
let result = classify_result(result)?;
let update_res = unsafe {
ContractInterfaceResult::from_raw(result, &linear_mem)
.unwrap_update_state(linear_mem)
.map_err(Into::<ContractExecError>::into)?
};
Ok(update_res)
})();
self.drop_running_instance(&mut running);
result
}
fn summarize_state(
&mut self,
key: &ContractKey,
parameters: &Parameters<'_>,
state: &WrappedState,
) -> RuntimeResult<StateSummary<'static>> {
let req_bytes = parameters.size() + state.size();
let mut running = self.prepare_contract_call(key, parameters, req_bytes)?;
let _io_guard = ContractIoGuard::new(running.id);
let result = (|| -> RuntimeResult<StateSummary<'static>> {
let linear_mem = self.linear_mem(&running.handle)?;
let param_buf_ptr =
self.write_contract_buf(&running, parameters.as_ref(), STREAMING_BUF_CAP)?;
let state_buf_ptr =
self.write_contract_buf(&running, state.as_ref(), STREAMING_BUF_CAP)?;
let result = self.engine.call_2i64_blocking(
&running.handle,
"summarize_state",
param_buf_ptr as i64,
state_buf_ptr as i64,
);
let result = classify_result(result)?;
let summary = unsafe {
ContractInterfaceResult::from_raw(result, &linear_mem)
.unwrap_summarize_state(linear_mem)
.map_err(Into::<ContractExecError>::into)?
};
Ok(summary)
})();
self.drop_running_instance(&mut running);
result
}
fn get_state_delta<'a>(
&mut self,
key: &ContractKey,
parameters: &Parameters<'a>,
state: &WrappedState,
summary: &StateSummary<'a>,
) -> RuntimeResult<StateDelta<'static>> {
let req_bytes = parameters.size() + state.size() + summary.size();
let mut running = self.prepare_contract_call(key, parameters, req_bytes)?;
let _io_guard = ContractIoGuard::new(running.id);
let result = (|| -> RuntimeResult<StateDelta<'static>> {
let linear_mem = self.linear_mem(&running.handle)?;
let param_buf_ptr =
self.write_contract_buf(&running, parameters.as_ref(), STREAMING_BUF_CAP)?;
let state_buf_ptr =
self.write_contract_buf(&running, state.as_ref(), STREAMING_BUF_CAP)?;
let summary_buf_ptr =
self.write_contract_buf(&running, summary.as_ref(), STREAMING_BUF_CAP)?;
let result = self.engine.call_3i64_blocking(
&running.handle,
"get_state_delta",
param_buf_ptr as i64,
state_buf_ptr as i64,
summary_buf_ptr as i64,
);
let result = classify_result(result)?;
let delta = unsafe {
ContractInterfaceResult::from_raw(result, &linear_mem)
.unwrap_get_state_delta(linear_mem)
.map_err(Into::<ContractExecError>::into)?
};
Ok(delta)
})();
self.drop_running_instance(&mut running);
result
}
}
fn classify_result(result: Result<i64, WasmError>) -> RuntimeResult<i64> {
match result {
Ok(value) => Ok(value),
Err(WasmError::OutOfGas) => Err(ContractExecError::OutOfGas.into()),
Err(WasmError::Timeout) => Err(ContractExecError::MaxComputeTimeExceeded.into()),
Err(e) => Err(e.into()),
}
}