use crate::offchain::{DbExternalities, OffchainStorage, StorageKind, STORAGE_PREFIX};
use std::{
collections::hash_map::{Entry, HashMap},
iter::Iterator,
};
const LOG_TARGET: &str = "offchain-worker::storage";
#[derive(Debug, Clone, Default)]
pub struct InMemOffchainStorage {
storage: HashMap<Vec<u8>, Vec<u8>>,
}
impl InMemOffchainStorage {
pub fn into_iter(self) -> impl Iterator<Item = (Vec<u8>, Vec<u8>)> {
self.storage.into_iter()
}
pub fn iter(&self) -> impl Iterator<Item = (&Vec<u8>, &Vec<u8>)> {
self.storage.iter()
}
pub fn remove(&mut self, prefix: &[u8], key: &[u8]) {
let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
self.storage.remove(&key);
}
}
impl OffchainStorage for InMemOffchainStorage {
fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]) {
let key = prefix.iter().chain(key).cloned().collect();
self.storage.insert(key, value.to_vec());
}
fn remove(&mut self, prefix: &[u8], key: &[u8]) {
let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
self.storage.remove(&key);
}
fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>> {
let key: Vec<u8> = prefix.iter().chain(key).cloned().collect();
self.storage.get(&key).cloned()
}
fn compare_and_set(
&mut self,
prefix: &[u8],
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
let key = prefix.iter().chain(key).cloned().collect();
match self.storage.entry(key) {
Entry::Vacant(entry) =>
if old_value.is_none() {
entry.insert(new_value.to_vec());
true
} else {
false
},
Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => {
entry.insert(new_value.to_vec());
true
},
_ => false,
}
}
}
fn unavailable_yet<R: Default>(name: &str) -> R {
tracing::error!(
target: LOG_TARGET,
"The {:?} API is not available for offchain workers yet. Follow \
https://github.com/paritytech/substrate/issues/1458 for details",
name
);
Default::default()
}
const LOCAL_DB: &str = "LOCAL (fork-aware) DB";
#[derive(Debug, Clone)]
pub struct OffchainDb<Storage> {
persistent: Storage,
}
impl<Storage> OffchainDb<Storage> {
pub fn new(persistent: Storage) -> Self {
Self { persistent }
}
}
impl<Storage: OffchainStorage> DbExternalities for OffchainDb<Storage> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
tracing::debug!(
target: LOG_TARGET,
?kind,
key = ?array_bytes::bytes2hex("", key),
value = ?array_bytes::bytes2hex("", value),
"Write",
);
match kind {
StorageKind::PERSISTENT => self.persistent.set(STORAGE_PREFIX, key, value),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
tracing::debug!(
target: LOG_TARGET,
?kind,
key = ?array_bytes::bytes2hex("", key),
"Clear",
);
match kind {
StorageKind::PERSISTENT => self.persistent.remove(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
tracing::debug!(
target: LOG_TARGET,
?kind,
key = ?array_bytes::bytes2hex("", key),
new_value = ?array_bytes::bytes2hex("", new_value),
old_value = ?old_value.as_ref().map(|s| array_bytes::bytes2hex("", s)),
"CAS",
);
match kind {
StorageKind::PERSISTENT =>
self.persistent.compare_and_set(STORAGE_PREFIX, key, old_value, new_value),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
let result = match kind {
StorageKind::PERSISTENT => self.persistent.get(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
};
tracing::debug!(
target: LOG_TARGET,
?kind,
key = ?array_bytes::bytes2hex("", key),
result = ?result.as_ref().map(|s| array_bytes::bytes2hex("", s)),
"Read",
);
result
}
}