use crate::{OpaquePeerId, RuntimeDebug};
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime_interface::pass_by::{PassByCodec, PassByEnum, PassByInner};
use sp_std::prelude::{Box, Vec};
pub use crate::crypto::KeyTypeId;
#[cfg(feature = "std")]
pub mod storage;
#[cfg(feature = "std")]
pub mod testing;
pub const STORAGE_PREFIX: &[u8] = b"storage";
pub trait OffchainStorage: Clone + Send + Sync {
fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]);
fn remove(&mut self, prefix: &[u8], key: &[u8]);
fn get(&self, prefix: &[u8], key: &[u8]) -> Option<Vec<u8>>;
fn compare_and_set(
&mut self,
prefix: &[u8],
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool;
}
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum StorageKind {
PERSISTENT = 1_isize,
LOCAL = 2_isize,
}
impl TryFrom<u32> for StorageKind {
type Error = ();
fn try_from(kind: u32) -> Result<Self, Self::Error> {
match kind {
e if e == u32::from(StorageKind::PERSISTENT as u8) => Ok(StorageKind::PERSISTENT),
e if e == u32::from(StorageKind::LOCAL as u8) => Ok(StorageKind::LOCAL),
_ => Err(()),
}
}
}
impl From<StorageKind> for u32 {
fn from(c: StorageKind) -> Self {
c as u8 as u32
}
}
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, Encode, Decode, PassByInner,
)]
#[cfg_attr(feature = "std", derive(Hash))]
pub struct HttpRequestId(pub u16);
impl From<HttpRequestId> for u32 {
fn from(c: HttpRequestId) -> Self {
c.0 as u32
}
}
#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByEnum)]
#[repr(C)]
pub enum HttpError {
DeadlineReached = 1_isize,
IoError = 2_isize,
Invalid = 3_isize,
}
impl TryFrom<u32> for HttpError {
type Error = ();
fn try_from(error: u32) -> Result<Self, Self::Error> {
match error {
e if e == HttpError::DeadlineReached as u8 as u32 => Ok(HttpError::DeadlineReached),
e if e == HttpError::IoError as u8 as u32 => Ok(HttpError::IoError),
e if e == HttpError::Invalid as u8 as u32 => Ok(HttpError::Invalid),
_ => Err(()),
}
}
}
impl From<HttpError> for u32 {
fn from(c: HttpError) -> Self {
c as u8 as u32
}
}
#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug, Encode, Decode, PassByCodec)]
pub enum HttpRequestStatus {
DeadlineReached,
IoError,
Invalid,
Finished(u16),
}
impl From<HttpRequestStatus> for u32 {
fn from(status: HttpRequestStatus) -> Self {
match status {
HttpRequestStatus::Invalid => 0,
HttpRequestStatus::DeadlineReached => 10,
HttpRequestStatus::IoError => 20,
HttpRequestStatus::Finished(code) => u32::from(code),
}
}
}
impl TryFrom<u32> for HttpRequestStatus {
type Error = ();
fn try_from(status: u32) -> Result<Self, Self::Error> {
match status {
0 => Ok(HttpRequestStatus::Invalid),
10 => Ok(HttpRequestStatus::DeadlineReached),
20 => Ok(HttpRequestStatus::IoError),
100..=999 => u16::try_from(status).map(HttpRequestStatus::Finished).map_err(|_| ()),
_ => Err(()),
}
}
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec, TypeInfo)]
#[cfg_attr(feature = "std", derive(Default))]
pub struct OpaqueNetworkState {
pub peer_id: OpaquePeerId,
pub external_addresses: Vec<OpaqueMultiaddr>,
}
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner, TypeInfo)]
pub struct OpaqueMultiaddr(pub Vec<u8>);
impl OpaqueMultiaddr {
pub fn new(vec: Vec<u8>) -> Self {
OpaqueMultiaddr(vec)
}
}
#[derive(
Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Timestamp(u64);
#[derive(
Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Duration(u64);
impl Duration {
pub const fn from_millis(millis: u64) -> Self {
Duration(millis)
}
pub fn millis(&self) -> u64 {
self.0
}
}
impl Timestamp {
pub fn from_unix_millis(millis: u64) -> Self {
Timestamp(millis)
}
pub fn add(&self, duration: Duration) -> Timestamp {
Timestamp(self.0.saturating_add(duration.0))
}
pub fn sub(&self, duration: Duration) -> Timestamp {
Timestamp(self.0.saturating_sub(duration.0))
}
pub fn diff(&self, other: &Self) -> Duration {
Duration(self.0.saturating_sub(other.0))
}
pub fn unix_millis(&self) -> u64 {
self.0
}
}
bitflags::bitflags! {
pub struct Capabilities: u32 {
const HTTP = 1 << 0;
const KEYSTORE = 1 << 2;
const RANDOMNESS = 1 << 3;
const NETWORK_STATE = 1 << 4;
const OFFCHAIN_DB_READ = 1 << 5;
const OFFCHAIN_DB_WRITE = 1 << 6;
const NODE_AUTHORIZATION = 1 << 7;
const TIME = 1 << 8;
}
}
pub trait Externalities: Send {
fn is_validator(&self) -> bool;
fn network_state(&self) -> Result<OpaqueNetworkState, ()>;
fn timestamp(&mut self) -> Timestamp;
fn sleep_until(&mut self, deadline: Timestamp);
fn random_seed(&mut self) -> [u8; 32];
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool);
}
impl<T: Externalities + ?Sized> Externalities for Box<T> {
fn is_validator(&self) -> bool {
(&**self).is_validator()
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
(&**self).network_state()
}
fn timestamp(&mut self) -> Timestamp {
(&mut **self).timestamp()
}
fn sleep_until(&mut self, deadline: Timestamp) {
(&mut **self).sleep_until(deadline)
}
fn random_seed(&mut self) -> [u8; 32] {
(&mut **self).random_seed()
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
(&mut **self).set_authorized_nodes(nodes, authorized_only)
}
}
pub struct LimitedExternalities<T> {
capabilities: Capabilities,
externalities: T,
}
impl<T> LimitedExternalities<T> {
pub fn new(capabilities: Capabilities, externalities: T) -> Self {
Self { capabilities, externalities }
}
fn check(&self, capability: Capabilities, name: &'static str) {
if !self.capabilities.contains(capability) {
panic!("Accessing a forbidden API: {}. No: {:?} capability.", name, capability);
}
}
}
impl<T: Externalities> Externalities for LimitedExternalities<T> {
fn is_validator(&self) -> bool {
self.check(Capabilities::KEYSTORE, "is_validator");
self.externalities.is_validator()
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
self.check(Capabilities::NETWORK_STATE, "network_state");
self.externalities.network_state()
}
fn timestamp(&mut self) -> Timestamp {
self.check(Capabilities::TIME, "timestamp");
self.externalities.timestamp()
}
fn sleep_until(&mut self, deadline: Timestamp) {
self.check(Capabilities::TIME, "sleep_until");
self.externalities.sleep_until(deadline)
}
fn random_seed(&mut self) -> [u8; 32] {
self.check(Capabilities::RANDOMNESS, "random_seed");
self.externalities.random_seed()
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.check(Capabilities::NODE_AUTHORIZATION, "set_authorized_nodes");
self.externalities.set_authorized_nodes(nodes, authorized_only)
}
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
pub struct OffchainWorkerExt(Box<dyn Externalities>);
}
#[cfg(feature = "std")]
impl OffchainWorkerExt {
pub fn new<O: Externalities + 'static>(offchain: O) -> Self {
Self(Box::new(offchain))
}
}
pub trait DbExternalities: Send {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]);
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]);
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool;
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>>;
}
impl<T: DbExternalities + ?Sized> DbExternalities for Box<T> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
(&mut **self).local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
(&mut **self).local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
(&mut **self).local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
(&mut **self).local_storage_get(kind, key)
}
}
impl<T: DbExternalities> DbExternalities for LimitedExternalities<T> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.check(Capabilities::OFFCHAIN_DB_WRITE, "local_storage_set");
self.externalities.local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.check(Capabilities::OFFCHAIN_DB_WRITE, "local_storage_clear");
self.externalities.local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
self.check(Capabilities::OFFCHAIN_DB_WRITE, "local_storage_compare_and_set");
self.externalities
.local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
self.check(Capabilities::OFFCHAIN_DB_READ, "local_storage_get");
self.externalities.local_storage_get(kind, key)
}
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
pub struct OffchainDbExt(Box<dyn DbExternalities>);
}
#[cfg(feature = "std")]
impl OffchainDbExt {
pub fn new<O: DbExternalities + 'static>(offchain: O) -> Self {
Self(Box::new(offchain))
}
}
#[cfg(feature = "std")]
pub trait TransactionPool {
fn submit_transaction(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
pub struct TransactionPoolExt(Box<dyn TransactionPool + Send>);
}
#[cfg(feature = "std")]
impl TransactionPoolExt {
pub fn new<O: TransactionPool + Send + 'static>(pool: O) -> Self {
Self(Box::new(pool))
}
}
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum OffchainOverlayedChange {
Remove,
SetValue(Vec<u8>),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timestamp_ops() {
let t = Timestamp(5);
assert_eq!(t.add(Duration::from_millis(10)), Timestamp(15));
assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0));
assert_eq!(t.diff(&Timestamp(3)), Duration(2));
}
#[test]
fn capabilities() {
let none = Capabilities::empty();
let all = Capabilities::all();
let some = Capabilities::KEYSTORE | Capabilities::RANDOMNESS;
assert!(!none.contains(Capabilities::KEYSTORE));
assert!(all.contains(Capabilities::KEYSTORE));
assert!(some.contains(Capabilities::KEYSTORE));
assert!(!none.contains(Capabilities::RANDOMNESS));
assert!(all.contains(Capabilities::RANDOMNESS));
assert!(!some.contains(Capabilities::TIME));
}
}