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 Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => {
86 entry.insert(new_value.to_vec());
87 true
88 },
89 _ => false,
90 }
91 }
92}
93
94fn unavailable_yet<R: Default>(name: &str) -> R {
95 tracing::error!(
96 target: LOG_TARGET,
97 "The {:?} API is not available for offchain workers yet. Follow \
98 https://github.com/paritytech/substrate/issues/1458 for details",
99 name
100 );
101 Default::default()
102}
103
104const LOCAL_DB: &str = "LOCAL (fork-aware) DB";
105
106#[derive(Debug, Clone)]
108pub struct OffchainDb<Storage> {
109 persistent: Storage,
111}
112
113impl<Storage> OffchainDb<Storage> {
114 pub fn new(persistent: Storage) -> Self {
116 Self { persistent }
117 }
118}
119
120impl<Storage: OffchainStorage> DbExternalities for OffchainDb<Storage> {
121 fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
122 tracing::debug!(
123 target: LOG_TARGET,
124 ?kind,
125 key = ?array_bytes::bytes2hex("", key),
126 value = ?array_bytes::bytes2hex("", value),
127 "Write",
128 );
129 match kind {
130 StorageKind::PERSISTENT => self.persistent.set(STORAGE_PREFIX, key, value),
131 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
132 }
133 }
134
135 fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
136 tracing::debug!(
137 target: LOG_TARGET,
138 ?kind,
139 key = ?array_bytes::bytes2hex("", key),
140 "Clear",
141 );
142 match kind {
143 StorageKind::PERSISTENT => self.persistent.remove(STORAGE_PREFIX, key),
144 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
145 }
146 }
147
148 fn local_storage_compare_and_set(
149 &mut self,
150 kind: StorageKind,
151 key: &[u8],
152 old_value: Option<&[u8]>,
153 new_value: &[u8],
154 ) -> bool {
155 tracing::debug!(
156 target: LOG_TARGET,
157 ?kind,
158 key = ?array_bytes::bytes2hex("", key),
159 new_value = ?array_bytes::bytes2hex("", new_value),
160 old_value = ?old_value.as_ref().map(|s| array_bytes::bytes2hex("", s)),
161 "CAS",
162 );
163 match kind {
164 StorageKind::PERSISTENT =>
165 self.persistent.compare_and_set(STORAGE_PREFIX, key, old_value, new_value),
166 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
167 }
168 }
169
170 fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
171 let result = match kind {
172 StorageKind::PERSISTENT => self.persistent.get(STORAGE_PREFIX, key),
173 StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
174 };
175 tracing::debug!(
176 target: LOG_TARGET,
177 ?kind,
178 key = ?array_bytes::bytes2hex("", key),
179 result = ?result.as_ref().map(|s| array_bytes::bytes2hex("", s)),
180 "Read",
181 );
182 result
183 }
184}