use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use freenet_stdlib::prelude::*;
use super::mock_state_storage::MockStateStorage;
#[allow(dead_code)]
#[derive(Clone, Default)]
pub struct InMemoryContractStore {
inner: Arc<Mutex<InMemoryContractStoreInner>>,
}
#[allow(dead_code)]
#[derive(Default)]
struct InMemoryContractStoreInner {
code_by_hash: HashMap<CodeHash, Arc<ContractCode<'static>>>,
instance_to_code: HashMap<ContractInstanceId, (CodeHash, Parameters<'static>)>,
}
#[allow(dead_code)]
impl InMemoryContractStore {
pub fn new() -> Self {
Self::default()
}
pub fn fetch_contract(
&self,
key: &ContractKey,
params: &Parameters<'_>,
) -> Option<ContractContainer> {
let inner = self.inner.lock().unwrap();
let code_hash = key.code_hash();
let code = inner.code_by_hash.get(code_hash)?;
Some(ContractContainer::Wasm(ContractWasmAPIVersion::V1(
WrappedContract::new(code.clone(), params.clone().into_owned()),
)))
}
pub fn store_contract(&self, contract: ContractContainer) -> Result<(), anyhow::Error> {
let (key, code, params) = match &contract {
ContractContainer::Wasm(ContractWasmAPIVersion::V1(contract_v1)) => {
let key = *contract_v1.key();
let code_data = contract_v1.code().data().to_vec();
let code = ContractCode::from(code_data);
let params = contract_v1.params().clone().into_owned();
(key, code, params)
}
ContractContainer::Wasm(_) | _ => {
return Err(anyhow::anyhow!("unsupported contract type"));
}
};
let code_hash = *key.code_hash();
let mut inner = self.inner.lock().unwrap();
inner.code_by_hash.insert(code_hash, Arc::new(code));
inner
.instance_to_code
.insert(*key.id(), (code_hash, params));
Ok(())
}
pub fn code_hash_from_id(&self, id: &ContractInstanceId) -> Option<CodeHash> {
let inner = self.inner.lock().unwrap();
inner.instance_to_code.get(id).map(|(hash, _)| *hash)
}
pub fn contract_count(&self) -> usize {
let inner = self.inner.lock().unwrap();
inner.code_by_hash.len()
}
pub fn instance_count(&self) -> usize {
let inner = self.inner.lock().unwrap();
inner.instance_to_code.len()
}
pub fn remove_contract(&self, key: &ContractKey) -> Result<(), anyhow::Error> {
let mut inner = self.inner.lock().unwrap();
inner.instance_to_code.remove(key.id());
let code_hash = *key.code_hash();
let still_referenced = inner
.instance_to_code
.values()
.any(|(hash, _)| *hash == code_hash);
if !still_referenced {
inner.code_by_hash.remove(&code_hash);
}
Ok(())
}
pub fn ensure_key_indexed(&self, key: &ContractKey) -> Result<(), anyhow::Error> {
let mut inner = self.inner.lock().unwrap();
if !inner.instance_to_code.contains_key(key.id()) {
let code_hash = *key.code_hash();
inner
.instance_to_code
.insert(*key.id(), (code_hash, Parameters::from(Vec::<u8>::new())));
}
Ok(())
}
pub fn clear(&self) {
let mut inner = self.inner.lock().unwrap();
inner.code_by_hash.clear();
inner.instance_to_code.clear();
}
}
#[allow(dead_code)]
#[derive(Clone)]
pub struct SimulationStores {
pub contract_store: InMemoryContractStore,
pub state_storage: MockStateStorage,
}
impl Default for SimulationStores {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
impl SimulationStores {
pub fn new() -> Self {
Self {
contract_store: InMemoryContractStore::new(),
state_storage: MockStateStorage::new(),
}
}
pub fn summary(&self) -> SimulationStoresSummary {
SimulationStoresSummary {
contract_count: self.contract_store.contract_count(),
instance_count: self.contract_store.instance_count(),
state_operation_count: self.state_storage.operation_count(),
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SimulationStoresSummary {
pub contract_count: usize,
pub instance_count: usize,
pub state_operation_count: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_in_memory_contract_store_basic() {
let store = InMemoryContractStore::new();
let code = ContractCode::from(vec![0x00, 0x61, 0x73, 0x6d]); let params = Parameters::from(vec![1, 2, 3]);
let contract = WrappedContract::new(Arc::new(code), params.clone());
let container = ContractContainer::Wasm(ContractWasmAPIVersion::V1(contract.clone()));
store.store_contract(container).unwrap();
let key = contract.key();
let fetched = store.fetch_contract(key, ¶ms);
assert!(fetched.is_some());
assert_eq!(store.contract_count(), 1);
assert_eq!(store.instance_count(), 1);
}
#[test]
fn test_in_memory_contract_store_sharing() {
let store1 = InMemoryContractStore::new();
let code = ContractCode::from(vec![0x00, 0x61, 0x73, 0x6d]);
let params = Parameters::from(vec![1, 2, 3]);
let contract = WrappedContract::new(Arc::new(code), params.clone());
let container = ContractContainer::Wasm(ContractWasmAPIVersion::V1(contract.clone()));
store1.store_contract(container).unwrap();
let store2 = store1.clone();
let key = contract.key();
let fetched = store2.fetch_contract(key, ¶ms);
assert!(fetched.is_some(), "Cloned store should see the same data");
}
#[test]
fn test_simulation_stores_sharing() {
let stores1 = SimulationStores::new();
let code = ContractCode::from(vec![0x00, 0x61, 0x73, 0x6d]);
let params = Parameters::from(vec![1, 2, 3]);
let contract = WrappedContract::new(Arc::new(code), params.clone());
let container = ContractContainer::Wasm(ContractWasmAPIVersion::V1(contract));
stores1.contract_store.store_contract(container).unwrap();
let stores2 = stores1.clone();
assert_eq!(
stores1.contract_store.contract_count(),
stores2.contract_store.contract_count()
);
}
}