1use crate::errors::ContractPrecompilatonResult;
2use crate::logic::errors::{CacheError, CompilationError};
3use crate::logic::{CompiledContract, CompiledContractCache, Config};
4use crate::runner::VMKindExt;
5use crate::ContractCode;
6use borsh::BorshSerialize;
7use std::collections::HashMap;
8use std::fmt;
9use std::sync::{Arc, Mutex};
10use unc_parameters::vm::VMKind;
11use unc_primitives_core::hash::CryptoHash;
12
13#[derive(Debug, Clone, BorshSerialize)]
14enum ContractCacheKey {
15 _Version1,
16 _Version2,
17 _Version3,
18 Version4 {
19 code_hash: CryptoHash,
20 vm_config_non_crypto_hash: u64,
21 vm_kind: VMKind,
22 vm_hash: u64,
23 },
24}
25
26fn vm_hash(vm_kind: VMKind) -> u64 {
27 match vm_kind {
28 #[cfg(all(feature = "wasmer0_vm", target_arch = "x86_64"))]
29 VMKind::Wasmer0 => crate::wasmer_runner::wasmer0_vm_hash(),
30 #[cfg(not(all(feature = "wasmer0_vm", target_arch = "x86_64")))]
31 VMKind::Wasmer0 => panic!("Wasmer0 is not enabled"),
32 #[cfg(all(feature = "wasmer2_vm", target_arch = "x86_64"))]
33 VMKind::Wasmer2 => crate::wasmer2_runner::wasmer2_vm_hash(),
34 #[cfg(not(all(feature = "wasmer2_vm", target_arch = "x86_64")))]
35 VMKind::Wasmer2 => panic!("Wasmer2 is not enabled"),
36 #[cfg(feature = "wasmtime_vm")]
37 VMKind::Wasmtime => crate::wasmtime_runner::wasmtime_vm_hash(),
38 #[cfg(not(feature = "wasmtime_vm"))]
39 VMKind::Wasmtime => panic!("Wasmtime is not enabled"),
40 #[cfg(all(feature = "unc_vm", target_arch = "x86_64"))]
41 VMKind::UncVm => crate::unc_vm_runner::unc_vm_vm_hash(),
42 #[cfg(not(all(feature = "unc_vm", target_arch = "x86_64")))]
43 VMKind::UncVm => panic!("UncVM is not enabled"),
44 }
45}
46
47pub fn get_contract_cache_key(code: &ContractCode, config: &Config) -> CryptoHash {
48 let _span = tracing::debug_span!(target: "vm", "get_key").entered();
49 let key = ContractCacheKey::Version4 {
50 code_hash: *code.hash(),
51 vm_config_non_crypto_hash: config.non_crypto_hash(),
52 vm_kind: config.vm_kind,
53 vm_hash: vm_hash(config.vm_kind),
54 };
55 CryptoHash::hash_borsh(key)
56}
57
58#[derive(Default)]
59pub struct MockCompiledContractCache {
60 store: Arc<Mutex<HashMap<CryptoHash, CompiledContract>>>,
61}
62
63impl MockCompiledContractCache {
64 pub fn len(&self) -> usize {
65 self.store.lock().unwrap().len()
66 }
67}
68
69impl CompiledContractCache for MockCompiledContractCache {
70 fn put(&self, key: &CryptoHash, value: CompiledContract) -> std::io::Result<()> {
71 self.store.lock().unwrap().insert(*key, value);
72 Ok(())
73 }
74
75 fn get(&self, key: &CryptoHash) -> std::io::Result<Option<CompiledContract>> {
76 Ok(self.store.lock().unwrap().get(key).map(Clone::clone))
77 }
78}
79
80impl fmt::Debug for MockCompiledContractCache {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 let guard = self.store.lock().unwrap();
83 let hm: &HashMap<_, _> = &*guard;
84 fmt::Debug::fmt(hm, f)
85 }
86}
87
88pub fn precompile_contract(
92 code: &ContractCode,
93 config: &Config,
94 cache: Option<&dyn CompiledContractCache>,
95) -> Result<Result<ContractPrecompilatonResult, CompilationError>, CacheError> {
96 let _span = tracing::debug_span!(target: "vm", "precompile_contract").entered();
97 let vm_kind = config.vm_kind;
98 let runtime = vm_kind
99 .runtime(config.clone())
100 .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time"));
101 let cache = match cache {
102 Some(it) => it,
103 None => return Ok(Ok(ContractPrecompilatonResult::CacheNotAvailable)),
104 };
105 let key = get_contract_cache_key(code, config);
106 if cache.has(&key).map_err(CacheError::ReadError)? {
108 return Ok(Ok(ContractPrecompilatonResult::ContractAlreadyInCache));
109 }
110 runtime.precompile(code, cache)
111}