From 58aa158331be110b2c5e8d7b4e2b60bff7a339b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 21 Aug 2019 16:19:21 +0200 Subject: [PATCH 1/8] Introduce capabilities filtering for calls. --- Cargo.lock | 1 + core/client/src/client.rs | 20 +- core/primitives/Cargo.toml | 2 + core/primitives/src/lib.rs | 31 ++- core/primitives/src/offchain.rs | 200 ++++++++++++++++++++ core/sr-api-macros/src/decl_runtime_apis.rs | 2 +- 6 files changed, 240 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 592b39e08ce8c..a8c94fa7f6fe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4925,6 +4925,7 @@ dependencies = [ "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/core/client/src/client.rs b/core/client/src/client.rs index c6234dc8d1cca..5d18c0b54ab51 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -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::{ @@ -1471,7 +1472,7 @@ impl CallRuntimeAt for Client where context: ExecutionContext, recorder: &Option>>>, ) -> error::Result> { - let enable_keystore = context.enable_keystore(); + let capabilities = context.capabilities(); let manager = match context { ExecutionContext::BlockConstruction => @@ -1480,16 +1481,19 @@ impl CallRuntimeAt for Client where self.execution_strategies.syncing.get_manager(), ExecutionContext::Importing => self.execution_strategies.importing.get_manager(), + ExecutionContext::OffchainCall => + self.execution_strategies.other.get_manager(), + ExecutionContext::RichOffchainCall(_) => + self.execution_strategies.other.get_manager(), ExecutionContext::OffchainWorker(_) => self.execution_strategies.offchain_worker.get_manager(), - ExecutionContext::Other => - self.execution_strategies.other.get_manager(), }; let mut offchain_extensions = match context { - ExecutionContext::OffchainWorker(ext) => Some(ext), + ExecutionContext::OffchainWorker(ext) + | ExecutionContext::RichOffchainCall(ext) => Some(ext), _ => None, - }; + }.map(|ext| offchain::LimitedExternalities::new(capabilities, ext)); self.executor.contextual_call::<_, _, fn(_,_) -> _,_,_>( || core_api.initialize_block(at, &self.prepare_environment_block(at)?), @@ -1502,7 +1506,7 @@ impl CallRuntimeAt for Client where native_call, offchain_extensions.as_mut(), recorder, - enable_keystore, + capabilities.has(offchain::Capability::Keystore), ) } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index b9bb141ad7616..f05907e3f205e 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -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 } @@ -48,6 +49,7 @@ bench = false [features] default = ["std"] std = [ + "log", "wasmi", "lazy_static", "parking_lot", diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 21e7c878082ad..07657cbd1e4e1 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -88,19 +88,36 @@ pub enum ExecutionContext { Syncing, /// Context used for block construction. BlockConstruction, - /// Offchain worker context. + /// Context used for offchain calls. + /// + /// Includes keystore capabilities. + OffchainCall, + /// Context used for rich offchain calls. + /// + /// This includes access to offchain worker db (read / write) + /// and transaction pool. + RichOffchainCall(Box), + /// A context used for offchain workers. + /// + /// This should include all capabilities of `offchain::Externalities`. OffchainWorker(Box), - /// Context used for other calls. - Other, } 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::*; + use crate::offchain::Capability::*; + use crate::offchain::Capabilities; match self { - Importing | Syncing | BlockConstruction => false, - OffchainWorker(_) | Other => true, + Importing | Syncing | BlockConstruction => Capabilities::none(), + OffchainCall => [Keystore][..].into(), + RichOffchainCall(_) => [ + TransactionPool, + Keystore, + OffchainWorkerDb, + ][..].into(), + OffchainWorker(_) => Capabilities::all(), } } } diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index 52dbf5fbee356..b6e468ab3696d 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -230,6 +230,51 @@ impl Timestamp { } } +/// Execution context extra capabilities. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[repr(C)] +pub enum Capability { + /// Access to transaction pool. + TransactionPool = 1, + /// Access to offchain worker DB. + OffchainWorkerDb = 2, + /// External http calls. + Http = 4, + /// Keystore access. + Keystore = 8, + /// Randomness source. + Randomness = 16, + /// Access to opaque network state. + NetworkState = 32, +} + +/// 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()) + } + + /// Check if particular capability is enabled. + pub fn has(&self, capability: Capability) -> bool { + self.0 & capability as u8 != 0 + } +} + +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. @@ -430,6 +475,147 @@ impl Externalities for Box { (&mut **self).http_response_read_body(request_id, buffer, deadline) } } +/// An `OffchainExternalities` implementation with limited capabilities. +pub struct LimitedExternalities { + capabilities: Capabilities, + externalities: T, +} + +impl LimitedExternalities { + /// Create new externalities limited to given `capabilities`. + pub fn new(capabilities: Capabilities, externalities: T) -> Self { + Self { + capabilities, + externalities, + } + } + + /// Check if given capability is allowed. + /// + /// Returns `Ok` in case it is, `Err` otherwise. + fn check(&self, capability: Capability) -> Result<(), ()> { + if self.capabilities.has(capability) { + Ok(()) + } else { + #[cfg(feature = "log")] + log::warn!("Accessing a forbidden API. No: {:?} capability.", capability); + Err(()) + } + } +} + +impl Externalities for LimitedExternalities { + fn is_validator(&self) -> bool { + if self.check(Capability::Keystore).is_ok() { + self.externalities.is_validator() + } else { + false + } + } + + fn submit_transaction(&mut self, ex: Vec) -> Result<(), ()> { + self.check(Capability::TransactionPool)?; + self.externalities.submit_transaction(ex) + } + + fn network_state(&self) -> Result { + self.check(Capability::NetworkState)?; + self.externalities.network_state() + } + + fn timestamp(&mut self) -> Timestamp { + if self.check(Capability::Http).is_ok() { + self.externalities.timestamp() + } else { + Timestamp::from_unix_millis(0) + } + } + + fn sleep_until(&mut self, deadline: Timestamp) { + if self.check(Capability::Http).is_ok() { + self.externalities.sleep_until(deadline) + } + } + + fn random_seed(&mut self) -> [u8; 32] { + if self.check(Capability::Randomness).is_ok() { + self.externalities.random_seed() + } else { + [0_u8; 32] + } + } + + fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { + if self.check(Capability::OffchainWorkerDb).is_ok() { + 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 { + if self.check(Capability::OffchainWorkerDb).is_ok() { + self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value) + } else { + true + } + } + + fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { + self.check(Capability::OffchainWorkerDb).ok()?; + self.externalities.local_storage_get(kind, key) + } + + fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result { + self.check(Capability::Http)?; + 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)?; + self.externalities.http_request_add_header(request_id, name, value) + } + + fn http_request_write_body( + &mut self, + request_id: HttpRequestId, + chunk: &[u8], + deadline: Option + ) -> Result<(), HttpError> { + self.check(Capability::Http).map_err(|_| HttpError::IoError)?; + self.externalities.http_request_write_body(request_id, chunk, deadline) + } + + fn http_response_wait(&mut self, ids: &[HttpRequestId], deadline: Option) -> Vec { + if self.check(Capability::Http).is_ok() { + self.externalities.http_response_wait(ids, deadline) + } else { + ids.iter().map(|_| HttpRequestStatus::Unknown).collect() + } + } + + fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec, Vec)> { + if self.check(Capability::Http).is_ok() { + self.externalities.http_response_headers(request_id) + } else { + Default::default() + } + } + + fn http_response_read_body( + &mut self, + request_id: HttpRequestId, + buffer: &mut [u8], + deadline: Option + ) -> Result { + self.check(Capability::Http).map_err(|_| HttpError::IoError)?; + self.externalities.http_response_read_body(request_id, buffer, deadline) + } +} #[cfg(test)] @@ -443,4 +629,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)); + } } diff --git a/core/sr-api-macros/src/decl_runtime_apis.rs b/core/sr-api-macros/src/decl_runtime_apis.rs index 27f102740b83b..9b866e8a81ef5 100644 --- a/core/sr-api-macros/src/decl_runtime_apis.rs +++ b/core/sr-api-macros/src/decl_runtime_apis.rs @@ -552,7 +552,7 @@ impl<'a> ToClientSideDecl<'a> { fn fold_trait_item_method(&mut self, method: TraitItemMethod) -> (TraitItemMethod, Option, TraitItemMethod) { let crate_ = self.crate_; - let context_other = quote!( #crate_::runtime_api::ExecutionContext::Other ); + let context_other = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall ); let fn_impl = self.create_method_runtime_api_impl(method.clone()); let fn_decl = self.create_method_decl(method.clone(), context_other); let fn_decl_ctx = self.create_method_decl_with_context(method); From f44d1ad5ff2f39d8ffb9bce2907ebb63bee9a1c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 21 Aug 2019 17:43:21 +0200 Subject: [PATCH 2/8] Bump impl version. --- node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index e17e0085f0365..0163f1ef4815e 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 148, - impl_version: 148, + impl_version: 149, apis: RUNTIME_API_VERSIONS, }; From f1a9a653e18c2f34070461373ed09f9c2131f12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 22 Aug 2019 10:59:14 +0200 Subject: [PATCH 3/8] Allow RichOffchainCall to only read offchain db. --- core/primitives/src/lib.rs | 2 +- core/primitives/src/offchain.rs | 14 ++++++++------ core/sr-api-macros/src/decl_runtime_apis.rs | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 07657cbd1e4e1..622a7799284ac 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -115,7 +115,7 @@ impl ExecutionContext { RichOffchainCall(_) => [ TransactionPool, Keystore, - OffchainWorkerDb, + OffchainWorkerDbRead, ][..].into(), OffchainWorker(_) => Capabilities::all(), } diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index b6e468ab3696d..a53b7cae6b37b 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -236,16 +236,18 @@ impl Timestamp { pub enum Capability { /// Access to transaction pool. TransactionPool = 1, - /// Access to offchain worker DB. - OffchainWorkerDb = 2, /// External http calls. - Http = 4, + Http = 2, /// Keystore access. - Keystore = 8, + Keystore = 4, /// Randomness source. - Randomness = 16, + Randomness = 8, /// Access to opaque network state. - NetworkState = 32, + NetworkState = 16, + /// Access to offchain worker DB (read only). + OffchainWorkerDbRead = 32, + /// Access to offchain worker DB (writes). + OffchainWorkerDbWrite = 64, } /// A set of capabilities diff --git a/core/sr-api-macros/src/decl_runtime_apis.rs b/core/sr-api-macros/src/decl_runtime_apis.rs index 9b866e8a81ef5..9a036bae27e9a 100644 --- a/core/sr-api-macros/src/decl_runtime_apis.rs +++ b/core/sr-api-macros/src/decl_runtime_apis.rs @@ -552,9 +552,9 @@ impl<'a> ToClientSideDecl<'a> { fn fold_trait_item_method(&mut self, method: TraitItemMethod) -> (TraitItemMethod, Option, TraitItemMethod) { let crate_ = self.crate_; - let context_other = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall ); + let context = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall ); let fn_impl = self.create_method_runtime_api_impl(method.clone()); - 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) From 1e3fe0c50cb8191391070dbed753315991acb3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 22 Aug 2019 11:07:09 +0200 Subject: [PATCH 4/8] Fix code. --- core/primitives/src/offchain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index a53b7cae6b37b..ea0cba00f5e7b 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -548,7 +548,7 @@ impl Externalities for LimitedExternalities { } fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { - if self.check(Capability::OffchainWorkerDb).is_ok() { + if self.check(Capability::OffchainWorkerDbWrite).is_ok() { self.externalities.local_storage_set(kind, key, value) } } @@ -560,7 +560,7 @@ impl Externalities for LimitedExternalities { old_value: Option<&[u8]>, new_value: &[u8], ) -> bool { - if self.check(Capability::OffchainWorkerDb).is_ok() { + if self.check(Capability::OffchainWorkerDbWrite).is_ok() { self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value) } else { true @@ -568,7 +568,7 @@ impl Externalities for LimitedExternalities { } fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option> { - self.check(Capability::OffchainWorkerDb).ok()?; + self.check(Capability::OffchainWorkerDbRead).ok()?; self.externalities.local_storage_get(kind, key) } From 393b2ade59d2cb74839702d8df0b4ed179d15d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 22 Aug 2019 11:31:10 +0200 Subject: [PATCH 5/8] Panic on invalid calls. --- core/primitives/src/offchain.rs | 78 ++++++++++++--------------------- 1 file changed, 27 insertions(+), 51 deletions(-) diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index ea0cba00f5e7b..ac00505db0f25 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -494,63 +494,48 @@ impl LimitedExternalities { /// Check if given capability is allowed. /// - /// Returns `Ok` in case it is, `Err` otherwise. - fn check(&self, capability: Capability) -> Result<(), ()> { - if self.capabilities.has(capability) { - Ok(()) - } else { - #[cfg(feature = "log")] - log::warn!("Accessing a forbidden API. No: {:?} capability.", capability); - Err(()) + /// 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 Externalities for LimitedExternalities { fn is_validator(&self) -> bool { - if self.check(Capability::Keystore).is_ok() { - self.externalities.is_validator() - } else { - false - } + self.check(Capability::Keystore, "is_validator"); + self.externalities.is_validator() } fn submit_transaction(&mut self, ex: Vec) -> Result<(), ()> { - self.check(Capability::TransactionPool)?; + self.check(Capability::TransactionPool, "submit_transaction"); self.externalities.submit_transaction(ex) } fn network_state(&self) -> Result { - self.check(Capability::NetworkState)?; + self.check(Capability::NetworkState, "network_state"); self.externalities.network_state() } fn timestamp(&mut self) -> Timestamp { - if self.check(Capability::Http).is_ok() { - self.externalities.timestamp() - } else { - Timestamp::from_unix_millis(0) - } + self.check(Capability::Http, "timestamp"); + self.externalities.timestamp() } fn sleep_until(&mut self, deadline: Timestamp) { - if self.check(Capability::Http).is_ok() { - self.externalities.sleep_until(deadline) - } + self.check(Capability::Http, "sleep_until"); + self.externalities.sleep_until(deadline) } fn random_seed(&mut self) -> [u8; 32] { - if self.check(Capability::Randomness).is_ok() { - self.externalities.random_seed() - } else { - [0_u8; 32] - } + self.check(Capability::Randomness, "random_seed"); + self.externalities.random_seed() } fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) { - if self.check(Capability::OffchainWorkerDbWrite).is_ok() { - self.externalities.local_storage_set(kind, key, value) - } + self.check(Capability::OffchainWorkerDbWrite, "local_storage_set"); + self.externalities.local_storage_set(kind, key, value) } fn local_storage_compare_and_set( @@ -560,25 +545,22 @@ impl Externalities for LimitedExternalities { old_value: Option<&[u8]>, new_value: &[u8], ) -> bool { - if self.check(Capability::OffchainWorkerDbWrite).is_ok() { - self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value) - } else { - true - } + 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> { - self.check(Capability::OffchainWorkerDbRead).ok()?; + 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 { - self.check(Capability::Http)?; + 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)?; + self.check(Capability::Http, "http_request_add_header"); self.externalities.http_request_add_header(request_id, name, value) } @@ -588,24 +570,18 @@ impl Externalities for LimitedExternalities { chunk: &[u8], deadline: Option ) -> Result<(), HttpError> { - self.check(Capability::Http).map_err(|_| HttpError::IoError)?; + 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) -> Vec { - if self.check(Capability::Http).is_ok() { - self.externalities.http_response_wait(ids, deadline) - } else { - ids.iter().map(|_| HttpRequestStatus::Unknown).collect() - } + 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, Vec)> { - if self.check(Capability::Http).is_ok() { - self.externalities.http_response_headers(request_id) - } else { - Default::default() - } + self.check(Capability::Http, "http_response_headers"); + self.externalities.http_response_headers(request_id) } fn http_response_read_body( @@ -614,7 +590,7 @@ impl Externalities for LimitedExternalities { buffer: &mut [u8], deadline: Option ) -> Result { - self.check(Capability::Http).map_err(|_| HttpError::IoError)?; + self.check(Capability::Http, "http_response_read_body"); self.externalities.http_response_read_body(request_id, buffer, deadline) } } From af191090b54f5836689a0dee730ef25f18920fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 22 Aug 2019 11:42:23 +0200 Subject: [PATCH 6/8] Merge execution contexts and expose capabilities. --- core/client/src/client.rs | 14 ++++------- core/offchain/src/lib.rs | 4 +-- core/primitives/src/lib.rs | 27 +++++---------------- core/primitives/src/offchain.rs | 17 +++++++++++++ core/sr-api-macros/src/decl_runtime_apis.rs | 2 +- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/core/client/src/client.rs b/core/client/src/client.rs index 5d18c0b54ab51..a57584575cc23 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -1472,8 +1472,6 @@ impl CallRuntimeAt for Client where context: ExecutionContext, recorder: &Option>>>, ) -> error::Result> { - let capabilities = context.capabilities(); - let manager = match context { ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(), @@ -1481,17 +1479,15 @@ impl CallRuntimeAt for Client where self.execution_strategies.syncing.get_manager(), ExecutionContext::Importing => self.execution_strategies.importing.get_manager(), - ExecutionContext::OffchainCall => - self.execution_strategies.other.get_manager(), - ExecutionContext::RichOffchainCall(_) => - self.execution_strategies.other.get_manager(), - ExecutionContext::OffchainWorker(_) => + ExecutionContext::OffchainCall(Some((_, capabilities))) if capabilities.has_all() => self.execution_strategies.offchain_worker.get_manager(), + ExecutionContext::OffchainCall(_) => + self.execution_strategies.other.get_manager(), }; + let capabilities = context.capabilities(); let mut offchain_extensions = match context { - ExecutionContext::OffchainWorker(ext) - | ExecutionContext::RichOffchainCall(ext) => Some(ext), + ExecutionContext::OffchainCall(ext) => ext.map(|x| x.0), _ => None, }.map(|ext| offchain::LimitedExternalities::new(capabilities, ext)); diff --git a/core/offchain/src/lib.rs b/core/offchain/src/lib.rs index b38b202c62e08..9b785ec8bada1 100644 --- a/core/offchain/src/lib.rs +++ b/core/offchain/src/lib.rs @@ -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}; @@ -122,7 +122,7 @@ impl 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 { diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 622a7799284ac..258d982ecb340 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -90,34 +90,19 @@ pub enum ExecutionContext { BlockConstruction, /// Context used for offchain calls. /// - /// Includes keystore capabilities. - OffchainCall, - /// Context used for rich offchain calls. - /// - /// This includes access to offchain worker db (read / write) - /// and transaction pool. - RichOffchainCall(Box), - /// A context used for offchain workers. - /// - /// This should include all capabilities of `offchain::Externalities`. - OffchainWorker(Box), + /// This allows passing offchain extension and customizing available capabilities. + OffchainCall(Option<(Box, offchain::Capabilities)>), } impl ExecutionContext { /// Returns the capabilities of particular context. pub fn capabilities(&self) -> offchain::Capabilities { use ExecutionContext::*; - use crate::offchain::Capability::*; - use crate::offchain::Capabilities; + match self { - Importing | Syncing | BlockConstruction => Capabilities::none(), - OffchainCall => [Keystore][..].into(), - RichOffchainCall(_) => [ - TransactionPool, - Keystore, - OffchainWorkerDbRead, - ][..].into(), - OffchainWorker(_) => Capabilities::all(), + Importing | Syncing | BlockConstruction | OffchainCall(None) => + offchain::Capabilities::none(), + OffchainCall(Some((_, capabilities))) => *capabilities, } } } diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index ac00505db0f25..a7ae31502aab5 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -265,10 +265,27 @@ impl Capabilities { 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 { diff --git a/core/sr-api-macros/src/decl_runtime_apis.rs b/core/sr-api-macros/src/decl_runtime_apis.rs index 9a036bae27e9a..0e69c2b76d701 100644 --- a/core/sr-api-macros/src/decl_runtime_apis.rs +++ b/core/sr-api-macros/src/decl_runtime_apis.rs @@ -552,7 +552,7 @@ impl<'a> ToClientSideDecl<'a> { fn fold_trait_item_method(&mut self, method: TraitItemMethod) -> (TraitItemMethod, Option, TraitItemMethod) { let crate_ = self.crate_; - let context = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall ); + let context = quote!( #crate_::runtime_api::ExecutionContext::OffchainCall(None) ); let fn_impl = self.create_method_runtime_api_impl(method.clone()); let fn_decl = self.create_method_decl(method.clone(), context); let fn_decl_ctx = self.create_method_decl_with_context(method); From 429d097d3d7d332e7efc899b49d06dd719adba55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 26 Aug 2019 10:45:41 +0200 Subject: [PATCH 7/8] Fix repr --- core/primitives/src/offchain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index a7ae31502aab5..4239c73f132ed 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -232,7 +232,7 @@ impl Timestamp { /// Execution context extra capabilities. #[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[repr(C)] +#[repr(u8)] pub enum Capability { /// Access to transaction pool. TransactionPool = 1, From de9e8e5cc045bb3d6641620bcee14363330eafb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 26 Aug 2019 10:51:33 +0200 Subject: [PATCH 8/8] Re-enable keystore for offchain calls. --- core/primitives/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 258d982ecb340..5c918e4964e37 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -100,8 +100,10 @@ impl ExecutionContext { use ExecutionContext::*; match self { - Importing | Syncing | BlockConstruction | OffchainCall(None) => + 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, } }