use crate::OpaquePeerId;
use alloc::{boxed::Box, vec::Vec};
use codec::{Decode, Encode};
use scale_info::TypeInfo;
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, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum StorageKind {
PERSISTENT = 0_isize,
LOCAL = 1_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 u32
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Hash))]
pub struct HttpRequestId(pub u16);
impl From<u16> for HttpRequestId {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<HttpRequestId> for u16 {
fn from(c: HttpRequestId) -> Self {
c.0
}
}
impl From<HttpRequestId> for u32 {
fn from(c: HttpRequestId) -> Self {
c.0 as u32
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Encode, Decode)]
#[repr(C)]
pub enum HttpError {
#[codec(index = 1)]
DeadlineReached = 0_isize,
#[codec(index = 2)]
IoError = 1_isize,
#[codec(index = 3)]
Invalid = 2_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, Debug, Encode, Decode)]
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, Debug, 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, Debug, 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, Debug, Encode, Decode)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Timestamp(u64);
impl From<u64> for Timestamp {
fn from(value: u64) -> Self {
Self(value)
}
}
impl From<Timestamp> for u64 {
fn from(value: Timestamp) -> u64 {
value.0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default, Debug, 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 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 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: 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 http_request_start(
&mut self,
method: &str,
uri: &str,
meta: &[u8],
) -> Result<HttpRequestId, ()> {
self.check(Capabilities::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(Capabilities::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(Capabilities::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(Capabilities::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(Capabilities::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(Capabilities::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(Capabilities::NODE_AUTHORIZATION, "set_authorized_nodes");
self.externalities.set_authorized_nodes(nodes, authorized_only)
}
}
#[cfg(not(substrate_runtime))]
sp_externalities::decl_extension! {
pub struct OffchainWorkerExt(Box<dyn Externalities>);
}
#[cfg(not(substrate_runtime))]
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(not(substrate_runtime))]
sp_externalities::decl_extension! {
pub struct OffchainDbExt(Box<dyn DbExternalities>);
}
#[cfg(not(substrate_runtime))]
impl OffchainDbExt {
pub fn new<O: DbExternalities + 'static>(offchain: O) -> Self {
Self(Box::new(offchain))
}
}
#[cfg(not(substrate_runtime))]
pub trait TransactionPool {
fn submit_transaction(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
}
#[cfg(not(substrate_runtime))]
sp_externalities::decl_extension! {
pub struct TransactionPoolExt(Box<dyn TransactionPool + Send>);
}
#[cfg(not(substrate_runtime))]
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));
}
}