pezsp_core/offchain/
storage.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! In-memory implementation of offchain workers database.
19
20use 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/// In-memory storage for offchain workers.
29#[derive(Debug, Clone, Default)]
30pub struct InMemOffchainStorage {
31	storage: HashMap<Vec<u8>, Vec<u8>>,
32}
33
34impl InMemOffchainStorage {
35	/// Consume the offchain storage and iterate over all key value pairs.
36	pub fn into_iter(self) -> impl Iterator<Item = (Vec<u8>, Vec<u8>)> {
37		self.storage.into_iter()
38	}
39
40	/// Iterate over all key value pairs by reference.
41	pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &Vec<u8>)> {
42		self.storage.iter()
43	}
44
45	/// Remove a key and its associated value from the offchain database.
46	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/// Offchain DB that implements [`DbExternalities`] for [`OffchainStorage`].
108#[derive(Debug, Clone)]
109pub struct OffchainDb<Storage> {
110	/// Persistent storage database.
111	persistent: Storage,
112}
113
114impl<Storage> OffchainDb<Storage> {
115	/// Create new instance of Offchain DB.
116	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}