use crate::{Accrete, ForksHandler, base::{Elastic, Portable, Probe, RuntimeEnum, RuntimeError, Time}};
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::{
prelude::{
format,
string::{String, ToString},
},
TypeInfo,
};
use core::marker::PhantomData;
use frame_system::{pallet_prelude::BlockNumberFor};
use sp_core::blake2_256;
use sp_runtime::{
offchain::storage::{MutateStorageError, StorageValueRef},
traits::{Debug, One, Saturating, Zero},
DispatchError,
};
use sp_std::{collections::{btree_map::BTreeMap, btree_set::BTreeSet}, vec::Vec};
pub type LogFormatter<TS, L> = fn(timestamp: TS, level: &L, target: &str, message: &str) -> String;
pub trait Logging<Timestamp>
where
Timestamp: Time,
{
type Logger: Elastic + RuntimeEnum;
type Level: RuntimeEnum + From<&'static str>;
const FALLBACK_TARGET: &'static str;
fn log(
level: Self::Level,
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<LogFormatter<Timestamp, Self::Level>>,
) -> Self::Logger;
#[inline]
fn info(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<LogFormatter<Timestamp, Self::Level>>,
) -> Self::Logger
where
Self: Sized,
{
Self::log(Self::Level::from("info"), err, timestamp, target, fmt)
}
#[inline]
fn warn(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<LogFormatter<Timestamp, Self::Level>>,
) -> Self::Logger
where
Self: Sized,
{
Self::log(Self::Level::from("warn"), err, timestamp, target, fmt)
}
#[inline]
fn error(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<LogFormatter<Timestamp, Self::Level>>,
) -> Self::Logger
where
Self: Sized,
{
Self::log(Self::Level::from("error"), err, timestamp, target, fmt)
}
#[inline]
fn debug(
err: &Self::Logger,
timestamp: Timestamp,
target: Option<&str>,
fmt: Option<LogFormatter<Timestamp, Self::Level>>,
) -> Self::Logger
where
Self: Sized,
{
Self::log(Self::Level::from("debug"), err, timestamp, target, fmt)
}
}
impl<T, Time> Logging<Time> for T
where
Time: crate::Time,
{
type Logger = DispatchError;
type Level = LogLevel;
const FALLBACK_TARGET: &str = "routine";
fn log(
level: Self::Level,
err: &Self::Logger,
timestamp: Time,
target: Option<&str>,
fmt: Option<LogFormatter<Time, Self::Level>>,
) -> Self::Logger {
use log::{debug, error, info, warn};
let actual_target = target.unwrap_or(<Self as Logging<Time>>::FALLBACK_TARGET);
let message = match err {
DispatchError::Other(str) => str.to_string(),
DispatchError::Module(module_error) => {
if let Some(msg) = module_error.message {
format!("Module({}): {}", module_error.index, msg)
} else {
format!(
"Module({}) raw error: {:?}",
module_error.index, module_error.error
)
}
}
DispatchError::CannotLookup => "CannotLookup".to_string(),
DispatchError::BadOrigin => "BadOrigin".to_string(),
DispatchError::ConsumerRemaining => "ConsumerRemaining".to_string(),
DispatchError::NoProviders => "NoProviders".to_string(),
DispatchError::TooManyConsumers => "TooManyConsumers".to_string(),
DispatchError::Token(token_error) => match token_error {
sp_runtime::TokenError::FundsUnavailable => {
"TokenError: FundsUnavailable".to_string()
}
sp_runtime::TokenError::OnlyProvider => "TokenError: OnlyProvider".to_string(),
sp_runtime::TokenError::BelowMinimum => "TokenError: BelowMinimum".to_string(),
sp_runtime::TokenError::CannotCreate => "TokenError: CannotCreate".to_string(),
sp_runtime::TokenError::UnknownAsset => "TokenError: UnknownAsset".to_string(),
sp_runtime::TokenError::Frozen => "TokenError: Frozen".to_string(),
sp_runtime::TokenError::Unsupported => "TokenError: Unsupported".to_string(),
sp_runtime::TokenError::CannotCreateHold => {
"TokenError: CannotCreateHold".to_string()
}
sp_runtime::TokenError::NotExpendable => "TokenError: NotExpendable".to_string(),
sp_runtime::TokenError::Blocked => "TokenError: Blocked".to_string(),
},
DispatchError::Arithmetic(arithmetic_error) => match arithmetic_error {
sp_runtime::ArithmeticError::Underflow => "ArithmeticError: Underflow".to_string(),
sp_runtime::ArithmeticError::Overflow => "ArithmeticError: Overflow".to_string(),
sp_runtime::ArithmeticError::DivisionByZero => {
"ArithmeticError: DivisionByZero".to_string()
}
},
DispatchError::Transactional(transactional_error) => match transactional_error {
sp_runtime::TransactionalError::LimitReached => {
"TransactionalError: LimitReached".to_string()
}
sp_runtime::TransactionalError::NoLayer => {
"TransactionalError: NoLayer".to_string()
}
},
DispatchError::Exhausted => "Exhausted".to_string(),
DispatchError::Corruption => "Corruption".to_string(),
DispatchError::Unavailable => "Unavailable".to_string(),
DispatchError::RootNotAllowed => "RootNotAllowed".to_string(),
DispatchError::Trie(trie_error) => match trie_error {
frame_support::traits::TrieError::InvalidStateRoot => {
"TrieError: InvalidStateRoot".to_string()
}
frame_support::traits::TrieError::IncompleteDatabase => {
"TrieError: IncompleteDatabase".to_string()
}
frame_support::traits::TrieError::ValueAtIncompleteKey => {
"TrieError: ValueAtIncompleteKey".to_string()
}
frame_support::traits::TrieError::DecoderError => {
"TrieError: DecoderError".to_string()
}
frame_support::traits::TrieError::InvalidHash => {
"TrieError: InvalidHash".to_string()
}
frame_support::traits::TrieError::DuplicateKey => {
"TrieError: DuplicateKey".to_string()
}
frame_support::traits::TrieError::ExtraneousNode => {
"TrieError: ExtraneousNode".to_string()
}
frame_support::traits::TrieError::ExtraneousValue => {
"TrieError: ExtraneousValue".to_string()
}
frame_support::traits::TrieError::ExtraneousHashReference => {
"TrieError: ExtraneousHashReference".to_string()
}
frame_support::traits::TrieError::InvalidChildReference => {
"TrieError: InvalidChildReference".to_string()
}
frame_support::traits::TrieError::ValueMismatch => {
"TrieError: ValueMismatch".to_string()
}
frame_support::traits::TrieError::IncompleteProof => {
"TrieError: IncompleteProof".to_string()
}
frame_support::traits::TrieError::RootMismatch => {
"TrieError: RootMismatch".to_string()
}
frame_support::traits::TrieError::DecodeError => {
"TrieError: DecodeError".to_string()
}
},
};
let log_line = if let Some(f) = fmt {
f(timestamp, &level, actual_target, &message)
} else {
format!(
"[{:?}][{}][{}] {}",
timestamp,
level.as_str(),
actual_target,
message
)
};
match level {
LogLevel::Error => error!(target: actual_target, "{}", log_line),
LogLevel::Warn => warn!(target: actual_target, "{}", log_line),
LogLevel::Debug => debug!(target: actual_target, "{}", log_line),
LogLevel::Info => info!(target: actual_target, "{}", log_line),
}
*err
}
}
#[derive(Clone, Copy, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)]
pub enum LogLevel {
Info,
Warn,
Error,
Debug,
}
impl From<&'static str> for LogLevel {
fn from(s: &'static str) -> Self {
match s {
"warn" => LogLevel::Warn,
"error" => LogLevel::Error,
"debug" => LogLevel::Debug,
"info" => LogLevel::Info,
_ => LogLevel::Debug,
}
}
}
impl LogLevel {
pub fn as_str(&self) -> &'static str {
match self {
LogLevel::Info => "INFO",
LogLevel::Warn => "WARN",
LogLevel::Error => "ERROR",
LogLevel::Debug => "DEBUG",
}
}
}
pub trait KeyValueStore<Value, TimeStamp>: Logging<TimeStamp>
where
TimeStamp: Time,
{
type Key: Probe + ?Sized;
type Value: Portable;
fn insert(
key: &Self::Key,
value: &Value,
target: Option<&str>,
fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
) -> Result<(), Self::Logger>;
fn get(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
) -> Result<Option<Self::Value>, Self::Logger>;
fn remove(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
) -> Result<Option<Value>, Self::Logger>;
fn mutate<F>(
key: &Self::Key,
f: F,
target: Option<&str>,
fmt: Option<LogFormatter<TimeStamp, Self::Level>>,
) -> Result<(), Self::Logger>
where
F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>;
}
pub trait SubstrateOffchainStorage {}
pub trait OffchainStorageError<Kind>
where
Kind: SubstrateOffchainStorage,
{
type Error: RuntimeError;
fn decode_failed() -> Self::Error;
fn concurrent_mutation() -> Self::Error;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Persistent<Context, Value, Routine>(PhantomData<(Value, Context, Routine)>)
where
Context: frame_system::Config,
Value: Portable,
Routine: Routines<BlockNumberFor<Context>>;
impl<Context, Value, Routine> SubstrateOffchainStorage for Persistent<Context, Value, Routine>
where
Context: frame_system::Config,
Value: Portable,
Routine: Routines<BlockNumberFor<Context>>,
{
}
impl<T, Value, Routine> KeyValueStore<Value, BlockNumberFor<T>> for Persistent<T, Value, Routine>
where
T: frame_system::Config,
Value: Portable,
Routine: OffchainStorageError<Self> + Routines<BlockNumberFor<T>>,
{
type Key = [u8];
type Value = Value;
fn insert(
key: &Self::Key,
value: &Value,
_target: Option<&str>,
_fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger> {
let storage_ref = StorageValueRef::persistent(key);
storage_ref.set(value);
Ok(())
}
fn get(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Value>, Self::Logger> {
let storage_ref = StorageValueRef::persistent(key);
let block = frame_system::Pallet::<T>::block_number();
let Ok(value) = storage_ref.get() else {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
));
};
Ok(value)
}
fn remove(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Value>, Self::Logger> {
let storage_ref = StorageValueRef::persistent(key);
let block = frame_system::Pallet::<T>::block_number();
let Ok(existing) = storage_ref.get::<Value>() else {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
));
};
let mut storage_ref = storage_ref;
storage_ref.clear();
Ok(existing)
}
fn mutate<F>(
key: &Self::Key,
f: F,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger>
where
F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
{
let storage_ref = StorageValueRef::persistent(key);
let block = frame_system::Pallet::<T>::block_number();
let res = storage_ref.mutate::<Value, Self::Logger, _>(|current| {
let current = match current {
Ok(v) => Ok(v), Err(_) => Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
)),
};
f(current)
});
match res {
Ok(_) => Ok(()),
Err(MutateStorageError::ConcurrentModification(_)) => {
let logged = <Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::concurrent_mutation().into(),
block,
target,
fmt,
);
Err(logged)
}
Err(MutateStorageError::ValueFunctionFailed(logged)) => {
Err(<Self as Logging<BlockNumberFor<T>>>::error(
&logged, block, target, fmt,
))
}
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ForkAware<Context, Value, Routine, Handler>(PhantomData<(Value, Context, Routine, Handler)>)
where
Context: frame_system::Config,
Value: Portable,
Routine: Routines<BlockNumberFor<Context>>,
Handler: ForksHandler<Context, ForkLocalDepot>;
impl<Context, Value, Routine, Handler> SubstrateOffchainStorage for ForkAware<Context, Value, Routine, Handler>
where
Context: frame_system::Config,
Value: Portable,
Routine: Routines<BlockNumberFor<Context>>,
Handler: ForksHandler<Context, ForkLocalDepot>,
{
}
impl<T, Value, Routine, Handler> KeyValueStore<Value, BlockNumberFor<T>> for ForkAware<T, Value, Routine, Handler>
where
T: frame_system::Config,
Value: Portable,
Routine: OffchainStorageError<Self> + Routines<BlockNumberFor<T>>,
Handler: ForksHandler<T, ForkLocalDepot> + Logging<BlockNumberFor<T>, Level = LogLevel, Logger = DispatchError>,
{
type Key = [u8];
type Value = Value;
fn insert(
key: &Self::Key,
value: &Value,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger> {
let key = key.to_vec();
let scope_key = Handler::add_to_scope(key, target, fmt)?;
let storage_ref = StorageValueRef::persistent(&scope_key);
storage_ref.set(value);
Ok(())
}
fn get(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Value>, Self::Logger> {
let scope_key = Handler::gen_scope_item_key(&key.to_vec());
if !Handler::scope_item_exists(&scope_key, target, fmt)? {
return Ok(None)
};
let storage_ref = StorageValueRef::persistent(&scope_key);
let block = frame_system::Pallet::<T>::block_number();
let Ok(value) = storage_ref.get() else {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
));
};
Ok(value)
}
fn remove(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Value>, Self::Logger> {
let scope_key = Handler::gen_scope_item_key(&key.to_vec());
if !Handler::scope_item_exists(&scope_key, target, fmt)? {
return Ok(None)
}
let storage_ref = StorageValueRef::persistent(&scope_key);
let block = frame_system::Pallet::<T>::block_number();
let Ok(existing) = storage_ref.get::<Value>() else {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
));
};
let mut storage_ref = storage_ref;
storage_ref.clear();
Handler::remove_from_scope(&scope_key, target, fmt)?;
Ok(existing)
}
fn mutate<F>(
key: &Self::Key,
f: F,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger>
where
F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
{
let scope_key = Handler::gen_scope_item_key(&key.to_vec());
if !Handler::scope_item_exists(&scope_key, target, fmt)? {
let value = f(Ok(None))?;
Self::insert(key, &value, target, fmt)?;
return Ok(())
}
let storage_ref = StorageValueRef::persistent(&scope_key);
let block = frame_system::Pallet::<T>::block_number();
let res = storage_ref.mutate::<Value, Self::Logger, _>(|current| {
let current = match current {
Ok(opt) => Ok(opt),
Err(_) => Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::decode_failed().into(),
block,
target,
fmt,
)),
};
f(current)
});
match res {
Ok(_) => Ok(()),
Err(MutateStorageError::ConcurrentModification(_)) => {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as OffchainStorageError<Self>>::concurrent_mutation().into(),
block,
target,
fmt,
));
}
Err(MutateStorageError::ValueFunctionFailed(logged)) => {
Err(<Self as Logging<BlockNumberFor<T>>>::error(
&logged, block, target, fmt,
))
}
}
}
}
pub trait FinalizedPolicy<Context>
where
Context: pallet_timestamp::Config,
{
fn finality_after() -> <Context as pallet_timestamp::Config>::Moment;
fn finality_ticks() -> BlockNumberFor<Context>;
}
pub trait FinalizedOffchainStorageError<Context, Value>
where
Context: frame_system::Config,
{
type Error: RuntimeError;
fn hanging_hash() -> Self::Error;
fn hanging_value() -> Self::Error;
}
#[derive(Clone, Copy, Debug)]
pub struct Finalized<Context, Value, Routine, Handler>(PhantomData<(Value, Context, Routine, Handler)>)
where
Context: frame_system::Config,
Value: Portable,
Routine: Routines<BlockNumberFor<Context>>,
Handler: ForksHandler<Context, ForkLocalDepot>;
#[derive(Encode, Decode, Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ValueHash(pub [u8; 32]);
impl ValueHash {
pub fn new(hash: [u8; 32]) -> Self {
ValueHash(hash)
}
}
#[derive(Encode, Decode, Debug, Clone)]
pub struct Observation<Context, Value>
where
Context: pallet_timestamp::Config,
{
pub first_seen: Moment<Context>,
pub last_seen: Moment<Context>,
pub blocks_seen: BlockNumberFor<Context>,
pub value: Value,
}
#[derive(Encode, Decode, Debug, Clone)]
pub struct Ledger<Context, Value>(pub ConfidenceMap<Context, Value>)
where
Context: pallet_timestamp::Config,
Value: Encode + Decode + Clone;
type ConfidenceMap<Context, Value> = BTreeMap<ValueHash, Observation<Context, Value>>;
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone)]
pub enum Confidence<Value>
where
Value: Portable,
{
Safe(Value),
Risky(Value),
Unsafe(Value),
}
pub type Moment<T> = <T as pallet_timestamp::Config>::Moment;
impl<T, Value, Routine, Handler> KeyValueStore<Value, BlockNumberFor<T>> for Finalized<T, Value, Routine, Handler>
where
T: pallet_timestamp::Config,
Value: Portable,
Routine: FinalizedOffchainStorageError<T, Value>
+ FinalizedPolicy<T>
+ OffchainStorageError<Persistent<T, Ledger<T, Value>, Routine>>
+ OffchainStorageError<ForkAware<T, ValueHash, Routine, Handler>>
+ Routines<BlockNumberFor<T>>,
Handler: ForksHandler<T, ForkLocalDepot> + Logging<BlockNumberFor<T>, Level = LogLevel, Logger = DispatchError>,
{
type Key = [u8];
type Value = Confidence<Value>;
fn insert(
key: &Self::Key,
value: &Value,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger> {
let hash = ValueHash(blake2_256(&value.encode()));
let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
ForkAware::<T, ValueHash, Routine, Handler>::insert(key, &hash, target, fmt)?;
Persistent::<T, Ledger<T, Value>, Routine>::mutate(
key,
|current| {
let mut ledger = match current {
Ok(Some(existing)) => existing,
Ok(None) => Ledger(ConfidenceMap::new()),
Err(logged) => return Err(logged),
};
ledger.0.insert(
hash,
Observation {
first_seen: now,
last_seen: now,
blocks_seen: Zero::zero(),
value: value.clone(),
},
);
Ok(ledger)
},
target,
fmt,
)?;
Ok(())
}
fn get(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Confidence<Value>>, Self::Logger> {
let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
let block = frame_system::Pallet::<T>::block_number();
let hash = match ForkAware::<T, ValueHash, Routine, Handler>::get(key, target, fmt)? {
Some(h) => h,
None => return Ok(None),
};
let mut result: Option<Confidence<Value>> = None;
Persistent::<T, Ledger<T, Value>, Routine>::mutate(
key,
|current| {
let mut ledger = match current {
Ok(Some(l)) => l,
Ok(None) => {
ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
.into(),
block,
target,
fmt,
));
}
Err(logged) => return Err(logged),
};
let obs = match ledger.0.get_mut(&hash) {
Some(o) => o,
None => {
ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_hash()
.into(),
block,
target,
fmt,
));
}
};
let first_seen = obs.first_seen;
let last_seen = obs.last_seen;
let obs_count = obs.blocks_seen;
let value = obs.value.clone();
let after = <Routine as FinalizedPolicy<T>>::finality_after();
let ticks = <Routine as FinalizedPolicy<T>>::finality_ticks();
obs.last_seen = now;
let confidence = match first_seen.saturating_add(after) > last_seen {
true => Confidence::Unsafe(value),
false => match obs_count < ticks {
true => {
if last_seen < now {
obs.blocks_seen += One::one();
}
Confidence::Risky(value)
}
false => Confidence::Safe(value),
},
};
result = Some(confidence);
Ok(ledger)
},
target,
fmt,
)?;
Ok(result)
}
fn remove(
key: &Self::Key,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<Option<Value>, Self::Logger> {
let block = frame_system::Pallet::<T>::block_number();
let hash = match ForkAware::<T, ValueHash, Routine, Handler>::get(key, target, fmt)? {
Some(h) => h,
None => return Ok(None), };
ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
let mut removed: Option<Value> = None;
let mut is_empty = false;
Persistent::<T, Ledger<T, Value>, Routine>::mutate(
key,
|current| {
let mut ledger = match current {
Ok(Some(l)) => l,
Ok(None) => {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value()
.into(),
block,
target,
fmt,
));
}
Err(logged) => return Err(logged),
};
if let Some(obs) = ledger.0.remove(&hash) {
removed = Some(obs.value);
}
if ledger.0.is_empty() {
is_empty = true;
}
Ok(ledger)
},
target,
fmt,
)?;
if is_empty {
Persistent::<T, Ledger<T, Value>, Routine>::remove(key, target, fmt)?;
}
Ok(removed)
}
fn mutate<F>(
key: &Self::Key,
f: F,
target: Option<&str>,
fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
) -> Result<(), Self::Logger>
where
F: FnOnce(Result<Option<Value>, Self::Logger>) -> Result<Value, Self::Logger>,
{
let now: Moment<T> = pallet_timestamp::Pallet::<T>::get();
let block = frame_system::Pallet::<T>::block_number();
let result = ForkAware::<T, ValueHash, Routine, Handler>::mutate(
key,
|current_hash| {
let current_value = match current_hash {
Err(logged) => {
return Err(logged);
}
Ok(None) => None,
Ok(Some(hash)) => {
let ledger =
Persistent::<T, Ledger<T, Value>, Routine>::get(key, target, fmt)?;
let ledger = match ledger {
Some(l) => l,
None => {
return Err(<Self as Logging<BlockNumberFor<T>>>::warn(
&<Routine as FinalizedOffchainStorageError<
T,
Value,
>>::hanging_value()
.into(),
block,
target,
fmt,
));
}
};
match ledger.0.get(&hash) {
Some(obs) => Some(obs.value.clone()),
None => None,
}
}
};
let new_value = f(Ok(current_value))?;
let new_hash = ValueHash(blake2_256(&new_value.encode()));
Persistent::<T, Ledger<T, Value>, Routine>::mutate(
key,
|ledger_result| {
let mut ledger = match ledger_result {
Ok(Some(l)) => l,
Ok(None) => Ledger(ConfidenceMap::new()),
Err(logged) => return Err(logged),
};
if let Ok(Some(old_hash)) = current_hash {
ledger.0.remove(&old_hash);
}
ledger.0.insert(
new_hash,
Observation {
first_seen: now,
last_seen: now,
blocks_seen: Zero::zero(),
value: new_value,
},
);
Ok(ledger)
},
target,
fmt,
)?;
Ok(new_hash)
},
target,
fmt,
);
match result {
Ok(_) => Ok(()),
Err(logged) => {
if logged
== <Routine as FinalizedOffchainStorageError<T, Value>>::hanging_value().into()
{
ForkAware::<T, ValueHash, Routine, Handler>::remove(key, target, fmt)?;
}
Err(logged)
}
}
}
}
#[derive(Encode, Decode, Clone, Debug, Default)]
pub struct ForkLocalDepot {
pub inherited_keys: BTreeSet<[u8; 32]>,
pub local_keys: BTreeSet<[u8; 32]>,
}
impl Accrete for ForkLocalDepot {
type Item = Vec<u8>;
fn accrete(&self) -> Self {
let mut inherited = self.inherited_keys.clone();
inherited.extend(self.local_keys.iter().copied());
Self {
inherited_keys: inherited,
local_keys: BTreeSet::new(),
}
}
fn inherited(&self) -> Vec<[u8; 32]> {
self.inherited_keys
.iter()
.copied()
.collect()
}
fn local(&self) -> Vec<[u8; 32]> {
self.local_keys
.iter()
.copied()
.collect()
}
fn add_to_local(
&mut self,
item: Self::Item,
) -> [u8; 32] {
let key = Self::make_key(&item);
self.local_keys.insert(key);
key
}
fn exists_in_local(
&self,
key: &[u8; 32],
) -> bool {
self.local_keys.contains(key)
}
fn exists_in_inherited(
&self,
key: &[u8; 32],
) -> bool {
self.inherited_keys.contains(key)
}
fn remove_from_local(
&mut self,
key: &[u8; 32],
) {
self.local_keys.remove(key);
}
fn remove_from_inherited(
&mut self,
key: &[u8; 32],
) {
self.inherited_keys.remove(key);
}
}
pub trait RoutineOf<Identifier, TimeStamp>: Logging<TimeStamp> + Routines<TimeStamp>
where
TimeStamp: Time,
Identifier: Portable,
{
fn who(at: &TimeStamp) -> Result<Identifier, Self::Logger>;
}
pub trait Routines<TimeStamp>: Logging<TimeStamp>
where
TimeStamp: Time,
{
fn can_run(&self) -> Result<(), Self::Logger>;
fn run_service(&self) -> Result<(), Self::Logger>;
fn on_ran_service(&self) {}
}