sp_core/offchain/
storage.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
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			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/// Offchain DB that implements [`DbExternalities`] for [`OffchainStorage`].
107#[derive(Debug, Clone)]
108pub struct OffchainDb<Storage> {
109	/// Persistent storage database.
110	persistent: Storage,
111}
112
113impl<Storage> OffchainDb<Storage> {
114	/// Create new instance of Offchain DB.
115	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}