1use crate::Result;
3use inkpad_executor::Memory;
4use inkpad_runtime::Runtime;
5use inkpad_std::BTreeMap;
6use inkpad_support::{
7 traits::{self, Cache, Frame},
8 types::{Metadata, State},
9};
10use etc::{Etc, FileSystem, Meta};
11use parity_scale_codec::{Decode, Encode};
12use sled::{Db, Tree};
13use std::{cell::RefCell, fs, path::PathBuf, process, rc::Rc};
14
15const RUNTIME_CACHE: &str = "RUNTIME_CACHE";
16const PREVIOUS_STATE: &str = "PREVIOUS_STATE";
17type HostState = BTreeMap<[u8; 32], BTreeMap<Vec<u8>, Vec<u8>>>;
18
19#[derive(Clone)]
21pub struct Storage {
22 pub db: Db,
23 cache: Tree,
24 frame: Vec<Rc<RefCell<State<Memory>>>>,
25 state: HostState,
26}
27
28impl traits::Storage for Storage {
29 fn set(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<Vec<u8>> {
30 self.cache.insert(key, value).ok()?.map(|v| v.to_vec())
31 }
32
33 fn remove(&mut self, key: &[u8]) -> Option<Vec<u8>> {
34 self.cache.remove(key).ok()?.map(|v| v.to_vec())
35 }
36
37 fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
38 self.cache.get(key).ok()?.map(|v| v.to_vec())
39 }
40}
41
42impl Cache<Memory> for Storage {
43 fn frame(&self) -> &Vec<Rc<RefCell<State<Memory>>>> {
44 &self.frame
45 }
46
47 fn frame_mut(&mut self) -> &mut Vec<Rc<RefCell<State<Memory>>>> {
48 &mut self.frame
49 }
50
51 fn memory(&self) -> Option<Memory> {
52 Some(self.frame.last()?.borrow().memory.clone())
53 }
54
55 fn flush(&mut self) -> Option<()> {
57 for state in self.frame.iter() {
58 let state = state.borrow().clone();
59 self.state.insert(state.hash, state.state);
60 }
61
62 let mut data = if let Some(state) = self.db.get(PREVIOUS_STATE).ok()? {
63 HostState::decode(&mut state.as_ref()).ok()?
64 } else {
65 BTreeMap::new()
66 };
67
68 data.append(&mut self.state.clone());
69 self.db.insert(PREVIOUS_STATE, data.encode()).ok()?;
70 self.db.flush().ok()?;
71 Some(())
72 }
73}
74
75impl Frame<Memory> for Storage {}
76
77impl Storage {
78 fn quit() {
79 println!(
80 "The following required arguments were not provided: \n\t\t\
81 <*.contract | name | code-hash>"
82 );
83 process::exit(1);
84 }
85
86 pub fn new() -> crate::Result<Self> {
88 let etc = Etc::new(&dirs::home_dir().ok_or("Could not find home dir")?)?;
89 let db = sled::open(etc.open(".inkpad/contracts")?.real_path()?)?;
90 let cache = db.open_tree(RUNTIME_CACHE)?;
91
92 Ok(Self {
93 db,
94 cache,
95 frame: Vec::new(),
96 state: BTreeMap::new(),
97 })
98 }
99
100 fn load(&mut self, rt: &mut Runtime) -> Result<()> {
101 let previous = self.db.get(PREVIOUS_STATE)?;
102 if previous.is_none() {
103 return Ok(());
104 }
105
106 for (code_hash, map) in
107 HostState::decode(&mut previous.ok_or("Get previous data failed")?.as_ref())?
108 .into_iter()
109 {
110 log::debug!("load contract: 0x{}", hex::encode(code_hash));
111 rt.sandbox.prepare(code_hash)?;
112 rt.sandbox
113 .cache
114 .borrow_mut()
115 .frame_mut()
116 .last_mut()
117 .ok_or("Could not get last frame")?
118 .borrow_mut()
119 .state = map;
120 }
121
122 let mut cache = rt.sandbox.cache.borrow_mut();
123 let first = cache
124 .frame()
125 .first()
126 .ok_or("No frame in current runtime")?
127 .clone();
128 cache.frame_mut().push(first);
129 Ok(())
130 }
131
132 pub fn rt(&mut self, contract: &str) -> crate::Result<Runtime> {
138 let if_path = PathBuf::from(contract);
139 let cache = self.clone();
140 let mut runtime = if if_path.exists() {
141 let source = fs::read(contract)?;
143 let r = Runtime::from_contract(&source, cache, Some(inkpad_ri::Instance))?;
144 self.db.insert(
145 r.cache
146 .borrow()
147 .active()
148 .ok_or(inkpad_executor::Error::CodeNotFound)?,
149 r.metadata.encode(),
150 )?;
151 r
152 } else if let Ok(Some(contract)) = if contract.is_empty() {
153 let mut recent = None;
155 for c in self.db.iter() {
156 let (k, v) = c?;
157 if k.len() == 32 {
158 recent = Some(Ok(Some(v)));
159 break;
160 }
161 }
162
163 if let Some(r) = recent {
164 r
165 } else {
166 return Err(crate::Error::ParseContractFailed(
167 "Get recent contract failed".to_string(),
168 ));
169 }
170 } else {
171 self.db.get(contract.as_bytes())
172 } {
173 Runtime::from_metadata(
174 Metadata::decode(&mut contract.as_ref())?,
175 cache,
176 Some(inkpad_ri::Instance),
177 )?
178 } else {
179 Self::quit();
180
181 return Err(crate::Error::ParseContractFailed(contract.to_string()));
185 };
186
187 self.load(&mut runtime)?;
189
190 self.flush().ok_or("Flush data failed")?;
192
193 Ok(runtime)
195 }
196}