1use crate::offchain::{DbExternalities, OffchainStorage, StorageKind, STORAGE_PREFIX};
21use std::{
22 collections::hash_map::{Entry, HashMap},
23 iter::Iterator,
24};
25
26const LOG_TARGET: &str = "offchain-worker::storage";
27
28#[derive(Debug, Clone, Default)]
30pub struct InMemOffchainStorage {
31 storage: HashMap<Vec<u8>, Vec<u8>>,
32}
33
34impl InMemOffchainStorage {
35 pub fn into_iter(self) -> impl Iterator<Item = (Vec<u8>, Vec<u8>)> {
37 self.storage.into_iter()
38 }
39
40 pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &Vec<u8>)> {
42 self.storage.iter()
43 }
44
45 pub fn remove(&mut self, prefix: &[u8], key: &[u8]) {
47 let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
48 self.storage.remove(&key);
49 }
50}
51
52impl OffchainStorage for InMemOffchainStorage {
53 fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) {
54 let key = prefix.iter().chain(key).cloned().collect();
55 self.storage.insert(key, value.to_vec());
56 }
57
58 fn remove(&mut self, prefix: &[u8], key: &[u8]) {
59 let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
60 self.storage.remove(&key);
61 }
62
63 fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>> {
64 let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
65 self.storage.get(&key).cloned()
66 }
67
68 fn compare_and_set(
69 &mut self,
70 prefix: &[u8],
71 key: &[u8],
72 old_value: Option<&[u8]>,
73 new_value: &[u8],
74 ) -> bool {
75 let key = prefix.iter().chain(key).cloned().collect();
76
77 match self.storage.entry(key) {
78 Entry::Vacant(entry) => {
79 if old_value.is_none() {
80 entry.insert(new_value.to_vec());
81 true
82 } else {
83 false
84 }
85 },
86 Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => {
87 entry.insert(new_value.to_vec());
88 true
89 },
90 _ => false,
91 }
92 }
93}
94
95fn unavailable_yet<R: Default>(name: &str) -> R {
96 tracing::error!(
97 target: LOG_TARGET,
98 "The {:?} API is not available for offchain workers yet. Follow \
99 https://github.com/pezkuwichain/pezkuwi-sdk/issues/332 for details",
100 name
101 );
102 Default::default()
103}
104
105const LOCAL_DB: &str = "LOCAL (fork-aware) DB";
106
107#[derive(Debug, Clone)]
109pub struct OffchainDb<Storage> {
110 persistent: Storage,
112}
113
114impl<Storage> OffchainDb<Storage> {
115 pub fn new(persistent: Storage) -> Self {
117 Self { persistent }
118 }
119}
120
121impl<Storage: OffchainStorage> DbExternalities for OffchainDb<Storage> {
122 fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
123 tracing::debug!(
124 target: LOG_TARGET,
125 ?kind,
126 key = ?array_bytes::bytes2hex("", key),
127 value = ?array_bytes::bytes2hex("", value),
128 "Write",
129 );
130 match kind {
131 StorageKind::PERSISTENT => self.persistent.set(STORAGE_PREFIX, key, value),
132 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
133 }
134 }
135
136 fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
137 tracing::debug!(
138 target: LOG_TARGET,
139 ?kind,
140 key = ?array_bytes::bytes2hex("", key),
141 "Clear",
142 );
143 match kind {
144 StorageKind::PERSISTENT => self.persistent.remove(STORAGE_PREFIX, key),
145 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
146 }
147 }
148
149 fn local_storage_compare_and_set(
150 &mut self,
151 kind: StorageKind,
152 key: &[u8],
153 old_value: Option<&[u8]>,
154 new_value: &[u8],
155 ) -> bool {
156 tracing::debug!(
157 target: LOG_TARGET,
158 ?kind,
159 key = ?array_bytes::bytes2hex("", key),
160 new_value = ?array_bytes::bytes2hex("", new_value),
161 old_value = ?old_value.as_ref().map(|s| array_bytes::bytes2hex("", s)),
162 "CAS",
163 );
164 match kind {
165 StorageKind::PERSISTENT => {
166 self.persistent.compare_and_set(STORAGE_PREFIX, key, old_value, new_value)
167 },
168 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
169 }
170 }
171
172 fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
173 let result = match kind {
174 StorageKind::PERSISTENT => self.persistent.get(STORAGE_PREFIX, key),
175 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
176 };
177 tracing::debug!(
178 target: LOG_TARGET,
179 ?kind,
180 key = ?array_bytes::bytes2hex("", key),
181 result = ?result.as_ref().map(|s| array_bytes::bytes2hex("", s)),
182 "Read",
183 );
184 result
185 }
186}