use codec::{Encode, Decode};
use sp_std::{prelude::{Vec, Box}, convert::TryFrom};
use crate::{OpaquePeerId, RuntimeDebug};
use sp_runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum};
pub use crate::crypto::KeyTypeId;
#[cfg(feature = "std")]
pub mod storage;
#[cfg(feature = "std")]
pub mod testing;
pub const STORAGE_PREFIX : &'static [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 = "std", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum StorageKind {
PERSISTENT = 1,
LOCAL = 2,
}
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,
IoError = 2,
Invalid = 3,
}
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)]
#[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)]
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)]
pub struct Timestamp(u64);
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, RuntimeDebug, PassByInner, Encode, Decode)]
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
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum Capability {
TransactionPool = 1,
Http = 2,
Keystore = 4,
Randomness = 8,
NetworkState = 16,
OffchainWorkerDbRead = 32,
OffchainWorkerDbWrite = 64,
NodeAuthorization = 128,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Capabilities(u8);
impl Capabilities {
pub fn none() -> Self {
Self(0)
}
pub fn all() -> Self {
Self(u8::max_value())
}
pub fn rich_offchain_call() -> Self {
[
Capability::TransactionPool,
Capability::Keystore,
Capability::OffchainWorkerDbRead,
][..].into()
}
pub fn has(&self, capability: Capability) -> bool {
self.0 & capability as u8 != 0
}
pub fn has_all(&self) -> bool {
self == &Capabilities::all()
}
}
impl<'a> From<&'a [Capability]> for Capabilities {
fn from(list: &'a [Capability]) -> Self {
Capabilities(list.iter().fold(0_u8, |a, b| a | *b as u8))
}
}
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 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>>;
fn http_request_start(
&mut self,
method: &str,
uri: &str,
meta: &[u8]
) -> Result<HttpRequestId, ()>;
fn http_request_add_header(
&mut self,
request_id: HttpRequestId,
name: &str,
value: &str
) -> Result<(), ()>;
fn http_request_write_body(
&mut self,
request_id: HttpRequestId,
chunk: &[u8],
deadline: Option<Timestamp>
) -> Result<(), HttpError>;
fn http_response_wait(
&mut self,
ids: &[HttpRequestId],
deadline: Option<Timestamp>
) -> Vec<HttpRequestStatus>;
fn http_response_headers(
&mut self,
request_id: HttpRequestId
) -> Vec<(Vec<u8>, Vec<u8>)>;
fn http_response_read_body(
&mut self,
request_id: HttpRequestId,
buffer: &mut [u8],
deadline: Option<Timestamp>
) -> Result<usize, HttpError>;
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 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)
}
fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
(&mut **self).http_request_start(method, uri, meta)
}
fn http_request_add_header(&mut self, request_id: HttpRequestId, name: &str, value: &str) -> Result<(), ()> {
(&mut **self).http_request_add_header(request_id, name, value)
}
fn http_request_write_body(
&mut self,
request_id: HttpRequestId,
chunk: &[u8],
deadline: Option<Timestamp>
) -> Result<(), HttpError> {
(&mut **self).http_request_write_body(request_id, chunk, deadline)
}
fn http_response_wait(&mut self, ids: &[HttpRequestId], deadline: Option<Timestamp>) -> Vec<HttpRequestStatus> {
(&mut **self).http_response_wait(ids, deadline)
}
fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
(&mut **self).http_response_headers(request_id)
}
fn http_response_read_body(
&mut self,
request_id: HttpRequestId,
buffer: &mut [u8],
deadline: Option<Timestamp>
) -> Result<usize, HttpError> {
(&mut **self).http_response_read_body(request_id, buffer, deadline)
}
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: Capability, name: &'static str) {
if !self.capabilities.has(capability) {
panic!("Accessing a forbidden API: {}. No: {:?} capability.", name, capability);
}
}
}
impl<T: Externalities> Externalities for LimitedExternalities<T> {
fn is_validator(&self) -> bool {
self.check(Capability::Keystore, "is_validator");
self.externalities.is_validator()
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
self.check(Capability::NetworkState, "network_state");
self.externalities.network_state()
}
fn timestamp(&mut self) -> Timestamp {
self.check(Capability::Http, "timestamp");
self.externalities.timestamp()
}
fn sleep_until(&mut self, deadline: Timestamp) {
self.check(Capability::Http, "sleep_until");
self.externalities.sleep_until(deadline)
}
fn random_seed(&mut self) -> [u8; 32] {
self.check(Capability::Randomness, "random_seed");
self.externalities.random_seed()
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.check(Capability::OffchainWorkerDbWrite, "local_storage_set");
self.externalities.local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.check(Capability::OffchainWorkerDbWrite, "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(Capability::OffchainWorkerDbWrite, "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(Capability::OffchainWorkerDbRead, "local_storage_get");
self.externalities.local_storage_get(kind, key)
}
fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
self.check(Capability::Http, "http_request_start");
self.externalities.http_request_start(method, uri, meta)
}
fn http_request_add_header(&mut self, request_id: HttpRequestId, name: &str, value: &str) -> Result<(), ()> {
self.check(Capability::Http, "http_request_add_header");
self.externalities.http_request_add_header(request_id, name, value)
}
fn http_request_write_body(
&mut self,
request_id: HttpRequestId,
chunk: &[u8],
deadline: Option<Timestamp>
) -> Result<(), HttpError> {
self.check(Capability::Http, "http_request_write_body");
self.externalities.http_request_write_body(request_id, chunk, deadline)
}
fn http_response_wait(&mut self, ids: &[HttpRequestId], deadline: Option<Timestamp>) -> Vec<HttpRequestStatus> {
self.check(Capability::Http, "http_response_wait");
self.externalities.http_response_wait(ids, deadline)
}
fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
self.check(Capability::Http, "http_response_headers");
self.externalities.http_response_headers(request_id)
}
fn http_response_read_body(
&mut self,
request_id: HttpRequestId,
buffer: &mut [u8],
deadline: Option<Timestamp>
) -> Result<usize, HttpError> {
self.check(Capability::Http, "http_response_read_body");
self.externalities.http_response_read_body(request_id, buffer, deadline)
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.check(Capability::NodeAuthorization, "set_authorized_nodes");
self.externalities.set_authorized_nodes(nodes, authorized_only)
}
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
pub struct OffchainExt(Box<dyn Externalities>);
}
#[cfg(feature = "std")]
impl OffchainExt {
pub fn new<O: Externalities + '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))
}
}
#[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::none();
let all = Capabilities::all();
let some = Capabilities::from(&[Capability::Keystore, Capability::Randomness][..]);
assert!(!none.has(Capability::Keystore));
assert!(all.has(Capability::Keystore));
assert!(some.has(Capability::Keystore));
assert!(!none.has(Capability::TransactionPool));
assert!(all.has(Capability::TransactionPool));
assert!(!some.has(Capability::TransactionPool));
}
}