oasis_runtime_sdk_contracts/
code.rs

1//! Code caching and storage.
2use std::{
3    io::{Read, Write},
4    num::NonZeroUsize,
5    sync::Mutex,
6};
7
8use once_cell::sync::Lazy;
9
10use oasis_runtime_sdk::{
11    core::common::crypto::hash::Hash,
12    state::CurrentState,
13    storage::{self, Store},
14};
15
16use crate::{state, types, Config, Error, Module, MODULE_NAME};
17
18/// A global in-memory LRU cache of code instances.
19static CODE_CACHE: Lazy<Mutex<lru::LruCache<Hash, Vec<u8>>>> =
20    Lazy::new(|| Mutex::new(lru::LruCache::new(NonZeroUsize::new(128).unwrap())));
21
22impl<Cfg: Config> Module<Cfg> {
23    /// Loads code with the specified code identifier.
24    pub fn load_code(code_info: &types::Code) -> Result<Vec<u8>, Error> {
25        let mut cache = CODE_CACHE.lock().unwrap();
26        if let Some(code) = cache.get(&code_info.hash) {
27            return Ok(code.clone());
28        }
29
30        // TODO: Support local untrusted cache to avoid storage queries.
31        let code = CurrentState::with_store(|store| {
32            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
33            let code_store = storage::PrefixStore::new(&mut store, &state::CODE);
34            code_store
35                .get(&code_info.id.to_storage_key())
36                .ok_or_else(|| Error::CodeNotFound(code_info.id.as_u64()))
37        })?;
38
39        // Decompress code.
40        let mut output = Vec::with_capacity(code.len());
41        let mut decoder = snap::read::FrameDecoder::new(code.as_slice());
42        decoder.read_to_end(&mut output).unwrap();
43
44        // Cache uncompressed code for later use.
45        cache.put(code_info.hash, output.clone());
46
47        Ok(output)
48    }
49
50    /// Stores code with the specified code identifier.
51    pub fn store_code(code_info: &types::Code, code: &[u8]) -> Result<(), Error> {
52        // If the code is currently cached replace it, otherwise don't do anything.
53        let mut cache = CODE_CACHE.lock().unwrap();
54        if cache.contains(&code_info.hash) {
55            cache.put(code_info.hash, code.to_vec());
56        }
57
58        // Compress code before storing it in storage.
59        let mut output = Vec::with_capacity(code.len() << 3);
60        let mut encoder = snap::write::FrameEncoder::new(&mut output);
61        encoder.write_all(code).unwrap();
62        drop(encoder); // Make sure data is flushed.
63
64        CurrentState::with_store(|store| {
65            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
66            let mut code_store = storage::PrefixStore::new(&mut store, &state::CODE);
67            code_store.insert(&code_info.id.to_storage_key(), &output);
68        });
69
70        Ok(())
71    }
72}