Skip to main content

ownable_std/
storage.rs

1use cosmwasm_std::{
2    Api, Addr, CanonicalAddr, Empty, Order, OwnedDeps, Querier, RecoverPubkeyError, StdError,
3    StdResult, Storage, VerificationError,
4};
5use serde::{Deserialize, Serialize};
6use serde_with::serde_as;
7use std::collections::{BTreeMap, HashMap};
8use std::iter;
9use std::marker::PhantomData;
10use std::ops::{Bound, RangeBounds};
11
12const CANONICAL_LENGTH: usize = 54;
13
14#[derive(Default)]
15/// In-memory [`Storage`] implementation used for tests and host-side execution.
16pub struct MemoryStorage {
17    data: BTreeMap<Vec<u8>, Vec<u8>>,
18}
19
20impl MemoryStorage {
21    /// Creates an empty in-memory storage backend.
22    pub fn new() -> Self {
23        Self::default()
24    }
25}
26
27impl Storage for MemoryStorage {
28    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
29        self.data.get(key).cloned()
30    }
31
32    fn set(&mut self, key: &[u8], value: &[u8]) {
33        if value.is_empty() {
34            panic!(
35                "TL;DR: Value must not be empty in Storage::set but in most cases you can use Storage::remove instead. Long story: Getting empty values from storage is not well supported at the moment. Some of our internal interfaces cannot differentiate between a non-existent key and an empty value. Right now, you cannot rely on the behaviour of empty values. To protect you from trouble later on, we stop here. Sorry for the inconvenience! We highly welcome you to contribute to CosmWasm, making this more solid one way or the other."
36            );
37        }
38
39        self.data.insert(key.to_vec(), value.to_vec());
40    }
41
42    fn remove(&mut self, key: &[u8]) {
43        self.data.remove(key);
44    }
45
46    fn range<'a>(
47        &'a self,
48        start: Option<&[u8]>,
49        end: Option<&[u8]>,
50        order: Order,
51    ) -> Box<dyn Iterator<Item = (Vec<u8>, Vec<u8>)> + 'a> {
52        let bounds = range_bounds(start, end);
53
54        match (bounds.start_bound(), bounds.end_bound()) {
55            (Bound::Included(start), Bound::Excluded(end)) if start > end => {
56                return Box::new(iter::empty());
57            }
58            _ => {}
59        }
60
61        let iter = self.data.range(bounds);
62        match order {
63            Order::Ascending => Box::new(iter.map(clone_item)),
64            Order::Descending => Box::new(iter.rev().map(clone_item)),
65        }
66    }
67}
68
69fn range_bounds(start: Option<&[u8]>, end: Option<&[u8]>) -> impl RangeBounds<Vec<u8>> {
70    (
71        start.map_or(Bound::Unbounded, |x| Bound::Included(x.to_vec())),
72        end.map_or(Bound::Unbounded, |x| Bound::Excluded(x.to_vec())),
73    )
74}
75
76type BTreeMapRecordRef<'a> = (&'a Vec<u8>, &'a Vec<u8>);
77
78fn clone_item(item_ref: BTreeMapRecordRef) -> (Vec<u8>, Vec<u8>) {
79    let (key, value) = item_ref;
80    (key.clone(), value.clone())
81}
82
83/// Builds in-memory dependencies for contract execution, optionally preloaded from IndexedDB dump data.
84pub fn load_owned_deps(
85    state_dump: Option<IdbStateDump>,
86) -> OwnedDeps<MemoryStorage, EmptyApi, EmptyQuerier, Empty> {
87    match state_dump {
88        None => OwnedDeps {
89            storage: MemoryStorage::default(),
90            api: EmptyApi::default(),
91            querier: EmptyQuerier::default(),
92            custom_query_type: PhantomData,
93        },
94        Some(dump) => {
95            let idb_storage = IdbStorage::load(dump);
96            OwnedDeps {
97                storage: idb_storage.storage,
98                api: EmptyApi::default(),
99                querier: EmptyQuerier::default(),
100                custom_query_type: PhantomData,
101            }
102        }
103    }
104}
105
106/// Wrapper around [`MemoryStorage`] with helpers to load state from browser IndexedDB dumps.
107pub struct IdbStorage {
108    pub storage: MemoryStorage,
109}
110
111impl IdbStorage {
112    /// Creates a new [`IdbStorage`] and populates it from a serialized state dump.
113    pub fn load(idb: IdbStateDump) -> Self {
114        let mut store = IdbStorage {
115            storage: MemoryStorage::new(),
116        };
117        store.load_to_mem_storage(idb);
118        store
119    }
120
121    /// takes a IdbStateDump and loads the values into MemoryStorage
122    pub fn load_to_mem_storage(&mut self, idb_state: IdbStateDump) {
123        for (k, v) in idb_state.state_dump {
124            self.storage.set(&k, &v);
125        }
126    }
127}
128
129#[serde_as]
130#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
131/// Serialized contract storage dump used to move state between JS and Rust.
132pub struct IdbStateDump {
133    #[serde_as(as = "Vec<(serde_with::Bytes, serde_with::Bytes)>")]
134    pub state_dump: HashMap<Vec<u8>, Vec<u8>>,
135}
136
137impl IdbStateDump {
138    /// generates a state dump from all key-value pairs in MemoryStorage
139    pub fn from(store: MemoryStorage) -> IdbStateDump {
140        let mut state: HashMap<Vec<u8>, Vec<u8>> = HashMap::new();
141
142        for (key, value) in store.range(None, None, Order::Ascending) {
143            state.insert(key, value);
144        }
145        IdbStateDump { state_dump: state }
146    }
147}
148
149// EmptyApi that is meant to conform the traits by the cosmwasm standard contract syntax. The functions of this implementation are not meant to be used or produce any sensible results.
150#[derive(Copy, Clone)]
151pub struct EmptyApi {
152    canonical_length: usize,
153}
154
155impl Default for EmptyApi {
156    fn default() -> Self {
157        EmptyApi {
158            canonical_length: CANONICAL_LENGTH,
159        }
160    }
161}
162
163impl Api for EmptyApi {
164    fn addr_validate(&self, human: &str) -> StdResult<Addr> {
165        self.addr_canonicalize(human).map(|_canonical| ())?;
166        Ok(Addr::unchecked(human))
167    }
168
169    fn addr_canonicalize(&self, human: &str) -> StdResult<CanonicalAddr> {
170        if human.len() < 3 {
171            return Err(StdError::msg("Invalid input: human address too short"));
172        }
173        if human.len() > self.canonical_length {
174            return Err(StdError::msg("Invalid input: human address too long"));
175        }
176
177        let mut out = Vec::from(human);
178        out.resize(self.canonical_length, 0x00);
179        Ok(out.into())
180    }
181
182    fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
183        if canonical.len() != self.canonical_length {
184            return Err(StdError::msg(
185                "Invalid input: canonical address length not correct",
186            ));
187        }
188
189        let tmp: Vec<u8> = canonical.clone().into();
190        let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect();
191        let human = String::from_utf8(trimmed)?;
192        Ok(Addr::unchecked(human))
193    }
194
195    fn secp256k1_verify(
196        &self,
197        _message_hash: &[u8],
198        _signature: &[u8],
199        _public_key: &[u8],
200    ) -> Result<bool, VerificationError> {
201        Err(VerificationError::unknown_err(0))
202    }
203
204    fn secp256k1_recover_pubkey(
205        &self,
206        _message_hash: &[u8],
207        _signature: &[u8],
208        _recovery_param: u8,
209    ) -> Result<Vec<u8>, RecoverPubkeyError> {
210        Err(RecoverPubkeyError::unknown_err(0))
211    }
212
213    fn ed25519_verify(
214        &self,
215        _message: &[u8],
216        _signature: &[u8],
217        _public_key: &[u8],
218    ) -> Result<bool, VerificationError> {
219        Ok(true)
220    }
221
222    fn ed25519_batch_verify(
223        &self,
224        _messages: &[&[u8]],
225        _signatures: &[&[u8]],
226        _public_keys: &[&[u8]],
227    ) -> Result<bool, VerificationError> {
228        Ok(true)
229    }
230
231    fn debug(&self, message: &str) {
232        println!("{message}");
233    }
234}
235
236/// Empty Querier that is meant to conform the traits expected by the cosmwasm standard contract syntax. It should not be used whatsoever
237#[derive(Default)]
238pub struct EmptyQuerier {}
239
240impl Querier for EmptyQuerier {
241    fn raw_query(&self, _bin_request: &[u8]) -> cosmwasm_std::QuerierResult {
242        todo!()
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn idb_state_dump_round_trips_through_storage() {
252        let mut storage = MemoryStorage::new();
253        storage.set(b"key1", b"value1");
254        storage.set(b"key2", b"value2");
255
256        let dump = IdbStateDump::from(storage);
257        assert_eq!(
258            dump.state_dump.get(b"key1".as_ref()),
259            Some(&b"value1".to_vec())
260        );
261        assert_eq!(
262            dump.state_dump.get(b"key2".as_ref()),
263            Some(&b"value2".to_vec())
264        );
265    }
266
267    #[test]
268    fn idb_storage_load_restores_all_keys() {
269        let mut storage = MemoryStorage::new();
270        storage.set(b"foo", b"bar");
271        storage.set(b"baz", b"qux");
272
273        let dump = IdbStateDump::from(storage);
274        let loaded = IdbStorage::load(dump);
275
276        assert_eq!(loaded.storage.get(b"foo"), Some(b"bar".to_vec()));
277        assert_eq!(loaded.storage.get(b"baz"), Some(b"qux".to_vec()));
278    }
279
280    #[test]
281    fn idb_state_dump_empty_storage_produces_empty_map() {
282        let storage = MemoryStorage::new();
283        let dump = IdbStateDump::from(storage);
284        assert!(dump.state_dump.is_empty());
285    }
286}