Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ use codec::{Encode, Decode};
use hash_db::{Hasher, Prefix};
use primitives::{
Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash,
NeverNativeValue, ExecutionContext,
storage::{StorageKey, StorageData, well_known_keys}, NativeOrEncoded
NeverNativeValue, ExecutionContext, NativeOrEncoded,
storage::{StorageKey, StorageData, well_known_keys},
offchain,
};
use substrate_telemetry::{telemetry, SUBSTRATE_INFO};
use sr_primitives::{
Expand Down Expand Up @@ -1471,25 +1472,24 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
context: ExecutionContext,
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
) -> error::Result<NativeOrEncoded<R>> {
let enable_keystore = context.enable_keystore();

let manager = match context {
ExecutionContext::BlockConstruction =>
self.execution_strategies.block_construction.get_manager(),
ExecutionContext::Syncing =>
self.execution_strategies.syncing.get_manager(),
ExecutionContext::Importing =>
self.execution_strategies.importing.get_manager(),
ExecutionContext::OffchainWorker(_) =>
ExecutionContext::OffchainCall(Some((_, capabilities))) if capabilities.has_all() =>
self.execution_strategies.offchain_worker.get_manager(),
ExecutionContext::Other =>
ExecutionContext::OffchainCall(_) =>
self.execution_strategies.other.get_manager(),
};

let capabilities = context.capabilities();
let mut offchain_extensions = match context {
ExecutionContext::OffchainWorker(ext) => Some(ext),
ExecutionContext::OffchainCall(ext) => ext.map(|x| x.0),
_ => None,
};
}.map(|ext| offchain::LimitedExternalities::new(capabilities, ext));

self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>(
|| core_api.initialize_block(at, &self.prepare_environment_block(at)?),
Expand All @@ -1502,7 +1502,7 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
native_call,
offchain_extensions.as_mut(),
recorder,
enable_keystore,
capabilities.has(offchain::Capability::Keystore),
)
}

Expand Down
4 changes: 2 additions & 2 deletions core/offchain/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use client::runtime_api::ApiExt;
use futures::future::Future;
use log::{debug, warn};
use network::NetworkStateInfo;
use primitives::ExecutionContext;
use primitives::{offchain, ExecutionContext};
use sr_primitives::{generic::BlockId, traits::{self, ProvideRuntimeApi}};
use transaction_pool::txpool::{Pool, ChainApi};

Expand Down Expand Up @@ -122,7 +122,7 @@ impl<Client, Storage, Block> OffchainWorkers<
debug!("Running offchain workers at {:?}", at);
let run = runtime.offchain_worker_with_context(
&at,
ExecutionContext::OffchainWorker(api),
ExecutionContext::OffchainCall(Some((api, offchain::Capabilities::all()))),
number,
);
if let Err(e) = run {
Expand Down
2 changes: 2 additions & 0 deletions core/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ twox-hash = { version = "1.2.0", optional = true }
byteorder = { version = "1.3.1", default-features = false }
primitive-types = { version = "0.5.0", default-features = false, features = ["codec"] }
impl-serde = { version = "0.1", optional = true }
log = { version = "0.4", optional = true }
wasmi = { version = "0.5.0", optional = true }
hash-db = { version = "0.15.2", default-features = false }
hash256-std-hasher = { version = "0.15.2", default-features = false }
Expand Down Expand Up @@ -48,6 +49,7 @@ bench = false
[features]
default = ["std"]
std = [
"log",
"wasmi",
"lazy_static",
"parking_lot",
Expand Down
20 changes: 12 additions & 8 deletions core/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,23 @@ pub enum ExecutionContext {
Syncing,
/// Context used for block construction.
BlockConstruction,
/// Offchain worker context.
OffchainWorker(Box<dyn offchain::Externalities>),
/// Context used for other calls.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all of these exclusive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

Other,
/// Context used for offchain calls.
///
/// This allows passing offchain extension and customizing available capabilities.
OffchainCall(Option<(Box<dyn offchain::Externalities>, offchain::Capabilities)>),
}

impl ExecutionContext {
/// Returns if the keystore should be enabled for the current context.
pub fn enable_keystore(&self) -> bool {
/// Returns the capabilities of particular context.
pub fn capabilities(&self) -> offchain::Capabilities {
use ExecutionContext::*;

match self {
Importing | Syncing | BlockConstruction => false,
OffchainWorker(_) | Other => true,
Importing | Syncing | BlockConstruction =>
offchain::Capabilities::none(),
// Enable keystore by default for offchain calls. CC @bkchr
OffchainCall(None) => [offchain::Capability::Keystore][..].into(),
OffchainCall(Some((_, capabilities))) => *capabilities,
}
}
}
Expand Down
195 changes: 195 additions & 0 deletions core/primitives/src/offchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,70 @@ impl Timestamp {
}
}

/// Execution context extra capabilities.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum Capability {
/// Access to transaction pool.
TransactionPool = 1,
/// External http calls.
Http = 2,
/// Keystore access.
Keystore = 4,
/// Randomness source.
Randomness = 8,
/// Access to opaque network state.
NetworkState = 16,
/// Access to offchain worker DB (read only).
OffchainWorkerDbRead = 32,
/// Access to offchain worker DB (writes).
OffchainWorkerDbWrite = 64,
}

/// A set of capabilities
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Capabilities(u8);

impl Capabilities {
/// Return an object representing an empty set of capabilities.
pub fn none() -> Self {
Self(0)
}

/// Return an object representing all capabilities enabled.
pub fn all() -> Self {
Self(u8::max_value())
}

/// Return capabilities for rich offchain calls.
///
/// Those calls should be allowed to sign and submit transactions
/// and access offchain workers database (but read only!).
pub fn rich_offchain_call() -> Self {
[
Capability::TransactionPool,
Capability::Keystore,
Capability::OffchainWorkerDbRead,
][..].into()
}

/// Check if particular capability is enabled.
pub fn has(&self, capability: Capability) -> bool {
self.0 & capability as u8 != 0
}

/// Check if this capability object represents all capabilities.
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))
}
}

/// An extended externalities for offchain workers.
pub trait Externalities {
/// Returns if the local node is a potential validator.
Expand Down Expand Up @@ -481,6 +545,123 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).http_response_read_body(request_id, buffer, deadline)
}
}
/// An `OffchainExternalities` implementation with limited capabilities.
pub struct LimitedExternalities<T> {
capabilities: Capabilities,
externalities: T,
}

impl<T> LimitedExternalities<T> {
/// Create new externalities limited to given `capabilities`.
pub fn new(capabilities: Capabilities, externalities: T) -> Self {
Self {
capabilities,
externalities,
}
}

/// Check if given capability is allowed.
///
/// Panics in case it is not.
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 submit_transaction(&mut self, ex: Vec<u8>) -> Result<(), ()> {
self.check(Capability::TransactionPool, "submit_transaction");
self.externalities.submit_transaction(ex)
}

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_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)
}
}


#[cfg(test)]
Expand All @@ -494,4 +675,18 @@ mod tests {
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));
}
}
4 changes: 2 additions & 2 deletions core/sr-api-macros/src/decl_runtime_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,9 @@ impl<'a> ToClientSideDecl<'a> {
fn fold_trait_item_method(&mut self, method: TraitItemMethod)
-> (TraitItemMethod, Option<TraitItemMethod>, TraitItemMethod) {
let crate_ = self.crate_;
let context_other = quote!( #crate_::runtime_api::ExecutionContext::Other );
let context = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall(None) );
let fn_impl = self.create_method_runtime_api_impl(method.clone());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename context_other.

let fn_decl = self.create_method_decl(method.clone(), context_other);
let fn_decl = self.create_method_decl(method.clone(), context);
let fn_decl_ctx = self.create_method_decl_with_context(method);

(fn_decl, fn_impl, fn_decl_ctx)
Expand Down
2 changes: 1 addition & 1 deletion node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 151,
impl_version: 152,
impl_version: 153,
apis: RUNTIME_API_VERSIONS,
};

Expand Down