mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-25 15:21:10 +00:00
95d73994c1
* feat/offchain/storage: add remove interface method * feat/offchain/storeage: add remote to StorageValueRef * feat/offchain/storage: add storage lock * fix/review: Apply suggestions from code review Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com> * refactor/offchain/storage/lock: introduce `Lockable` trait part 1 of 2 * chore/offchain/rename: _remove -> clean * feat/offchain/storage/lock: add TimeAndBlock based part 2 of 2 * fix/offchain/storage/lock: block and time expiry must be && not || * chore/offchain/storage: minor fmt doc comments * doc/comment: prefer markdown emphasis over CAPS * doc/comment: rewrap multiline module level docs * doc/comment: rephrase * impl sleep_until and use the actual time for the test env * feat/test: add more tests, ignore some sample impl doctests * fix/review: Apply suggestions from code review Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * doc/comment: better description * fix/review: Apply suggestions from code review Co-authored-by: Nikolay Volf <nikvolf@gmail.com> * chore/storage: lifetime cleanup * fix/cleanup: trait bounds, cargo-spellcheck + extra explanations * fix/doc: periods +- * fix/review: Apply suggestions from code review Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> * cleanup: remove explicit lifetime bound, copy -> clone * fix/review: make trait Lockable contain only static, try_lock should not return Err(Option<L>), * chore/lifetimes: remove a couple of lifetime bounds which the compiler can figure out * refactor: migrate to an instant based * fix/feedback: fix, reduce, rename, docs update pending * docs/reword: adjust to changed code * fix/offchain/testing: timestamp and sleep_until shall not block * chore/lines: lines must < 100 chars * fix/docs: add missing pub field doc comments * refactor/x: try_lock does not need to return an Option<_> * refactor/simplify: a better way of waiting for a lock to resolve * docs: consistency * fix/line: < 100 * fix/doctest/use: avoid crate:: * fix/doctest: * * fix/review: remove unused trait bound * fix/review: pretty by const fn * fix/review: reduce default timeout to 20s * docs: grammar * fix/review: add with_block_deadline * doc: revamp BlockNumberProvider documentation to be less frame centric * chore: fmt * docs: add missing doc comment Co-authored-by: Bernhard Schuster <bernhard@parity.io> Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com> Co-authored-by: Peter Goodspeed-Niklaus <coriolinus@users.noreply.github.com> Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
164 lines
4.6 KiB
Rust
164 lines
4.6 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) 2020 Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! A set of storage helpers for offchain workers.
|
|
|
|
use sp_core::offchain::StorageKind;
|
|
|
|
/// A storage value with a static key.
|
|
pub type StorageValue = StorageValueRef<'static>;
|
|
|
|
/// An abstraction over local storage value.
|
|
pub struct StorageValueRef<'a> {
|
|
key: &'a [u8],
|
|
kind: StorageKind,
|
|
}
|
|
|
|
impl<'a> StorageValueRef<'a> {
|
|
/// Create a new reference to a value in the persistent local storage.
|
|
pub fn persistent(key: &'a [u8]) -> Self {
|
|
Self { key, kind: StorageKind::PERSISTENT }
|
|
}
|
|
|
|
/// Create a new reference to a value in the fork-aware local storage.
|
|
pub fn local(key: &'a [u8]) -> Self {
|
|
Self { key, kind: StorageKind::LOCAL }
|
|
}
|
|
|
|
/// Set the value of the storage to encoding of given parameter.
|
|
///
|
|
/// Note that the storage may be accessed by workers running concurrently,
|
|
/// if you happen to write a `get-check-set` pattern you should most likely
|
|
/// be using `mutate` instead.
|
|
pub fn set(&self, value: &impl codec::Encode) {
|
|
value.using_encoded(|val| {
|
|
sp_io::offchain::local_storage_set(self.kind, self.key, val)
|
|
})
|
|
}
|
|
|
|
/// Remove the associated value from the storage.
|
|
pub fn clear(&mut self) {
|
|
sp_io::offchain::local_storage_clear(self.kind, self.key)
|
|
}
|
|
|
|
/// Retrieve & decode the value from storage.
|
|
///
|
|
/// Note that if you want to do some checks based on the value
|
|
/// and write changes after that you should rather be using `mutate`.
|
|
///
|
|
/// The function returns `None` if the value was not found in storage,
|
|
/// otherwise a decoding of the value to requested type.
|
|
pub fn get<T: codec::Decode>(&self) -> Option<Option<T>> {
|
|
sp_io::offchain::local_storage_get(self.kind, self.key)
|
|
.map(|val| T::decode(&mut &*val).ok())
|
|
}
|
|
|
|
/// Retrieve & decode the value and set it to a new one atomically.
|
|
///
|
|
/// Function `f` should return a new value that we should attempt to write to storage.
|
|
/// This function returns:
|
|
/// 1. `Ok(Ok(T))` in case the value has been successfully set.
|
|
/// 2. `Ok(Err(T))` in case the value was calculated by the passed closure `f`,
|
|
/// but it could not be stored.
|
|
/// 3. `Err(_)` in case `f` returns an error.
|
|
pub fn mutate<T, E, F>(&self, f: F) -> Result<Result<T, T>, E> where
|
|
T: codec::Codec,
|
|
F: FnOnce(Option<Option<T>>) -> Result<T, E>
|
|
{
|
|
let value = sp_io::offchain::local_storage_get(self.kind, self.key);
|
|
let decoded = value.as_deref().map(|mut v| T::decode(&mut v).ok());
|
|
let val = f(decoded)?;
|
|
let set = val.using_encoded(|new_val| {
|
|
sp_io::offchain::local_storage_compare_and_set(
|
|
self.kind,
|
|
self.key,
|
|
value,
|
|
new_val,
|
|
)
|
|
});
|
|
|
|
if set {
|
|
Ok(Ok(val))
|
|
} else {
|
|
Ok(Err(val))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use sp_io::TestExternalities;
|
|
use sp_core::offchain::{
|
|
OffchainExt,
|
|
OffchainStorage,
|
|
testing,
|
|
};
|
|
|
|
#[test]
|
|
fn should_set_and_get() {
|
|
let (offchain, state) = testing::TestOffchainExt::new();
|
|
let mut t = TestExternalities::default();
|
|
t.register_extension(OffchainExt::new(offchain));
|
|
|
|
t.execute_with(|| {
|
|
let val = StorageValue::persistent(b"testval");
|
|
|
|
assert_eq!(val.get::<u32>(), None);
|
|
|
|
val.set(&15_u32);
|
|
|
|
assert_eq!(val.get::<u32>(), Some(Some(15_u32)));
|
|
assert_eq!(val.get::<Vec<u8>>(), Some(None));
|
|
assert_eq!(
|
|
state.read().persistent_storage.get(b"", b"testval"),
|
|
Some(vec![15_u8, 0, 0, 0])
|
|
);
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn should_mutate() {
|
|
let (offchain, state) = testing::TestOffchainExt::new();
|
|
let mut t = TestExternalities::default();
|
|
t.register_extension(OffchainExt::new(offchain));
|
|
|
|
t.execute_with(|| {
|
|
let val = StorageValue::persistent(b"testval");
|
|
|
|
let result = val.mutate::<u32, (), _>(|val| {
|
|
assert_eq!(val, None);
|
|
|
|
Ok(16_u32)
|
|
});
|
|
assert_eq!(result, Ok(Ok(16_u32)));
|
|
assert_eq!(val.get::<u32>(), Some(Some(16_u32)));
|
|
assert_eq!(
|
|
state.read().persistent_storage.get(b"", b"testval"),
|
|
Some(vec![16_u8, 0, 0, 0])
|
|
);
|
|
|
|
// mutate again, but this time early-exit.
|
|
let res = val.mutate::<u32, (), _>(|val| {
|
|
assert_eq!(val, Some(Some(16_u32)));
|
|
Err(())
|
|
});
|
|
assert_eq!(res, Err(()));
|
|
})
|
|
}
|
|
}
|