From 7beab407c7e4dfd39c5e526e16ba9f4d30670f93 Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Tue, 24 Aug 2021 14:53:50 +0400 Subject: [PATCH 1/6] Implement BioauthCanAuthorWith logic to disable block production if the peer isn't bioauth-unauthorized --- Cargo.lock | 1 + crates/bioauth-consensus/Cargo.toml | 1 + crates/bioauth-consensus/src/lib.rs | 93 ++++++++++++++++++++++++++++- crates/humanode-peer/src/service.rs | 32 ++++++++-- 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b93193d6a..079022613 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,6 +478,7 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-aura", + "sp-keystore", "sp-runtime", "thiserror", "tokio 1.9.0", diff --git a/crates/bioauth-consensus/Cargo.toml b/crates/bioauth-consensus/Cargo.toml index 8e0659183..036d643b6 100644 --- a/crates/bioauth-consensus/Cargo.toml +++ b/crates/bioauth-consensus/Cargo.toml @@ -16,6 +16,7 @@ sp-application-crypto = { git = "https://github.com/humanode-network/substrate", sp-blockchain = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", branch = "master", optional = true } +sp-keystore = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "master" } thiserror = "1" diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index 000249cff..7e65c2027 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -9,8 +9,10 @@ use sc_client_api::{backend::Backend, Finalizer}; use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; use sp_api::{ProvideRuntimeApi, TransactionFor}; +use sp_application_crypto::Public; use sp_blockchain::{well_known_cache_keys, HeaderBackend}; -use sp_consensus::Error as ConsensusError; +use sp_consensus::{CanAuthorWith, Error as ConsensusError}; +use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::Block as BlockT; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use thiserror::Error; @@ -158,3 +160,92 @@ where self.inner.import_block(block, cache).await } } + +/// A can-author-with handler for Bioauth. +pub struct BioauthCanAuthorWith { + /// Native can-author-with handler. + base_caw: CAW, + /// Keystore to extract validator public key. + keystore: SyncCryptoStorePtr, + /// The bioauth auhtrization verifier. + authorization_verifier: AV, + /// A phantom data for Block. + _phantom_block: PhantomData, +} + +/// BioauthCanAuthorWith Error Type. +#[derive(Error, Debug, Eq, PartialEq)] +pub enum BioauthCanAuthorWithError +where + AV: std::error::Error, +{ + /// The block author isn't Bioauth authorized. + #[error("the block author isn't bioauth-authorized")] + NotBioauthAuthorized, + /// Authorization verification error. + #[error("unable verify the authorization: {0}")] + AuthorizationVerifier(AV), +} + +impl BioauthCanAuthorWith { + /// Simple constructor. + pub fn new(base_caw: CAW, keystore: SyncCryptoStorePtr, authorization_verifier: AV) -> Self { + BioauthCanAuthorWith { + base_caw, + keystore, + authorization_verifier, + _phantom_block: PhantomData, + } + } +} + +impl Clone for BioauthCanAuthorWith +where + AV: Clone, + CAW: Clone, +{ + fn clone(&self) -> Self { + Self { + base_caw: self.base_caw.clone(), + keystore: Arc::clone(&self.keystore), + authorization_verifier: self.authorization_verifier.clone(), + _phantom_block: PhantomData, + } + } +} + +impl CanAuthorWith for BioauthCanAuthorWith +where + AV: AuthorizationVerifier + Send, + ::Error: std::error::Error + Send + Sync + 'static, + CAW: CanAuthorWith + Send + Sync + 'static, +{ + fn can_author_with(&self, at: &sp_api::BlockId) -> Result<(), String> { + self.base_caw.can_author_with(at)?; + + let mkerr = |err: BioauthCanAuthorWithError| -> String { err.to_string() }; + + let aura_public_keys = sp_keystore::SyncCryptoStore::sr25519_public_keys( + self.keystore.as_ref(), + sp_application_crypto::key_types::AURA, + ); + + assert!( + aura_public_keys.len() == 1, + "The list of aura public keys should contain only 1 key; please report this" + ); + + let aura_public_key = aura_public_keys[0]; + + let is_authorized = self + .authorization_verifier + .is_authorized(at, &aura_public_key.to_raw_vec()) + .map_err(|err| mkerr(BioauthCanAuthorWithError::AuthorizationVerifier(err)))?; + + if !is_authorized { + return Err(mkerr(BioauthCanAuthorWithError::NotBioauthAuthorized)); + } + + Ok(()) + } +} diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index e83d9f042..551715123 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -8,7 +8,9 @@ use sc_client_api::ExecutorProvider; use sc_consensus_aura::{ImportQueueParams, SlotDuration, SlotProportion, StartAuraParams}; use sc_executor::native_executor_instance; pub use sc_executor::NativeExecutor; -use sc_service::{Error as ServiceError, KeystoreContainer, PartialComponents, TaskManager}; +use sc_service::{ + Error as ServiceError, KeystoreContainer, LocalCallExecutor, PartialComponents, TaskManager, +}; use sp_consensus::SlotData; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use tracing::*; @@ -57,6 +59,13 @@ pub fn new_partial( bioauth_consensus::aura::BlockAuthorExtractor, bioauth_consensus::bioauth::AuthorizationVerifier, >, + bioauth_consensus::BioauthCanAuthorWith< + Block, + bioauth_consensus::bioauth::AuthorizationVerifier, + sp_consensus::CanAuthorWithNativeVersion< + LocalCallExecutor>, + >, + >, SlotDuration, Duration, ), @@ -91,6 +100,17 @@ pub fn new_partial( bioauth_consensus::aura::BlockAuthorExtractor::new(Arc::clone(&client)), bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); + let bioauth_can_author_with: bioauth_consensus::BioauthCanAuthorWith< + Block, + bioauth_consensus::bioauth::AuthorizationVerifier, + sp_consensus::CanAuthorWithNativeVersion< + LocalCallExecutor>, + >, + > = bioauth_consensus::BioauthCanAuthorWith::new( + sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()), + keystore_container.sync_keystore(), + bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), + ); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; let raw_slot_duration = slot_duration.slot_duration(); @@ -112,9 +132,7 @@ pub fn new_partial( Ok((timestamp, slot)) }, spawner: &task_manager.spawn_essential_handle(), - can_author_with: sp_consensus::CanAuthorWithNativeVersion::new( - client.executor().clone(), - ), + can_author_with: bioauth_can_author_with.clone(), registry: config.prometheus_registry(), check_for_equivocation: Default::default(), telemetry: None, @@ -130,6 +148,7 @@ pub fn new_partial( transaction_pool, other: ( bioauth_consensus_block_import, + bioauth_can_author_with, slot_duration, raw_slot_duration, ), @@ -147,7 +166,8 @@ pub async fn new_full(config: Configuration) -> Result Result = None; From 6bc7dd1718f7b016934d7b55a39e5410d7c5875e Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Thu, 2 Sep 2021 17:04:10 +0300 Subject: [PATCH 2/6] Change using CanAuthorWith by ProposerFactory approach --- Cargo.lock | 1 + crates/bioauth-consensus/Cargo.toml | 1 + crates/bioauth-consensus/src/lib.rs | 83 ++++++++++++++++------------- crates/humanode-peer/src/service.rs | 49 ++++++++--------- 4 files changed, 69 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 98f5c70e2..ff1f69f3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -467,6 +467,7 @@ name = "bioauth-consensus" version = "0.1.0" dependencies = [ "async-trait", + "futures 0.3.16", "mockall", "node-primitives", "pallet-bioauth", diff --git a/crates/bioauth-consensus/Cargo.toml b/crates/bioauth-consensus/Cargo.toml index 036d643b6..7bc4ac38c 100644 --- a/crates/bioauth-consensus/Cargo.toml +++ b/crates/bioauth-consensus/Cargo.toml @@ -9,6 +9,7 @@ publish = false pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", optional = true } async-trait = "0.1.42" +futures = "0.3" sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-api = { git = "https://github.com/humanode-network/substrate", branch = "master" } diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index 7e65c2027..542f2fc65 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -6,12 +6,14 @@ clippy::clone_on_ref_ptr )] +use futures::future; +use futures::future::FutureExt; use sc_client_api::{backend::Backend, Finalizer}; use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; -use sp_api::{ProvideRuntimeApi, TransactionFor}; +use sp_api::{HeaderT, ProvideRuntimeApi, TransactionFor}; use sp_application_crypto::Public; use sp_blockchain::{well_known_cache_keys, HeaderBackend}; -use sp_consensus::{CanAuthorWith, Error as ConsensusError}; +use sp_consensus::{Environment, Error as ConsensusError}; use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::Block as BlockT; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; @@ -161,10 +163,10 @@ where } } -/// A can-author-with handler for Bioauth. -pub struct BioauthCanAuthorWith { - /// Native can-author-with handler. - base_caw: CAW, +/// A Proposer handler for Bioauth. +pub struct BioauthProposer { + /// A basic authorship proposer. + base_proposer: BAP, /// Keystore to extract validator public key. keystore: SyncCryptoStorePtr, /// The bioauth auhtrization verifier. @@ -173,9 +175,9 @@ pub struct BioauthCanAuthorWith { _phantom_block: PhantomData, } -/// BioauthCanAuthorWith Error Type. +/// BioauthProposer Error Type. #[derive(Error, Debug, Eq, PartialEq)] -pub enum BioauthCanAuthorWithError +pub enum BioauthProposerError where AV: std::error::Error, { @@ -187,11 +189,15 @@ where AuthorizationVerifier(AV), } -impl BioauthCanAuthorWith { +impl BioauthProposer { /// Simple constructor. - pub fn new(base_caw: CAW, keystore: SyncCryptoStorePtr, authorization_verifier: AV) -> Self { - BioauthCanAuthorWith { - base_caw, + pub fn new( + base_proposer: BAP, + keystore: SyncCryptoStorePtr, + authorization_verifier: AV, + ) -> Self { + BioauthProposer { + base_proposer, keystore, authorization_verifier, _phantom_block: PhantomData, @@ -199,31 +205,26 @@ impl BioauthCanAuthorWith { } } -impl Clone for BioauthCanAuthorWith -where - AV: Clone, - CAW: Clone, -{ - fn clone(&self) -> Self { - Self { - base_caw: self.base_caw.clone(), - keystore: Arc::clone(&self.keystore), - authorization_verifier: self.authorization_verifier.clone(), - _phantom_block: PhantomData, - } - } -} - -impl CanAuthorWith for BioauthCanAuthorWith +impl Environment for BioauthProposer where AV: AuthorizationVerifier + Send, ::Error: std::error::Error + Send + Sync + 'static, - CAW: CanAuthorWith + Send + Sync + 'static, + BAP: Environment + Send + Sync + 'static, + BAP::Error: Send, + BAP::Proposer: Send, { - fn can_author_with(&self, at: &sp_api::BlockId) -> Result<(), String> { - self.base_caw.can_author_with(at)?; + type Proposer = BAP::Proposer; + + type CreateProposer = future::BoxFuture<'static, Result>; + + type Error = BAP::Error; - let mkerr = |err: BioauthCanAuthorWithError| -> String { err.to_string() }; + fn init(&mut self, parent_header: &Block::Header) -> Self::CreateProposer { + let mkerr = |err: BioauthProposerError| -> Self::CreateProposer { + Box::pin(future::err(Self::Error::from(sp_consensus::Error::Other( + Box::new(err), + )))) + }; let aura_public_keys = sp_keystore::SyncCryptoStore::sr25519_public_keys( self.keystore.as_ref(), @@ -232,20 +233,26 @@ where assert!( aura_public_keys.len() == 1, - "The list of aura public keys should contain only 1 key; please report this" + "The list of aura public keys should contain only 1 key, please report this" ); let aura_public_key = aura_public_keys[0]; - let is_authorized = self + let parent_hash = parent_header.hash(); + let at = sp_api::BlockId::hash(parent_hash); + + let is_authorized = match self .authorization_verifier - .is_authorized(at, &aura_public_key.to_raw_vec()) - .map_err(|err| mkerr(BioauthCanAuthorWithError::AuthorizationVerifier(err)))?; + .is_authorized(&at, &aura_public_key.to_raw_vec()) + { + Ok(v) => v, + Err(err) => return mkerr(BioauthProposerError::AuthorizationVerifier(err)), + }; if !is_authorized { - return Err(mkerr(BioauthCanAuthorWithError::NotBioauthAuthorized)); + return mkerr(BioauthProposerError::NotBioauthAuthorized); } - Ok(()) + self.base_proposer.init(parent_header).boxed() } } diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index 551715123..b30eff3f6 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -8,9 +8,7 @@ use sc_client_api::ExecutorProvider; use sc_consensus_aura::{ImportQueueParams, SlotDuration, SlotProportion, StartAuraParams}; use sc_executor::native_executor_instance; pub use sc_executor::NativeExecutor; -use sc_service::{ - Error as ServiceError, KeystoreContainer, LocalCallExecutor, PartialComponents, TaskManager, -}; +use sc_service::{Error as ServiceError, KeystoreContainer, PartialComponents, TaskManager}; use sp_consensus::SlotData; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use tracing::*; @@ -59,13 +57,6 @@ pub fn new_partial( bioauth_consensus::aura::BlockAuthorExtractor, bioauth_consensus::bioauth::AuthorizationVerifier, >, - bioauth_consensus::BioauthCanAuthorWith< - Block, - bioauth_consensus::bioauth::AuthorizationVerifier, - sp_consensus::CanAuthorWithNativeVersion< - LocalCallExecutor>, - >, - >, SlotDuration, Duration, ), @@ -100,17 +91,6 @@ pub fn new_partial( bioauth_consensus::aura::BlockAuthorExtractor::new(Arc::clone(&client)), bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); - let bioauth_can_author_with: bioauth_consensus::BioauthCanAuthorWith< - Block, - bioauth_consensus::bioauth::AuthorizationVerifier, - sp_consensus::CanAuthorWithNativeVersion< - LocalCallExecutor>, - >, - > = bioauth_consensus::BioauthCanAuthorWith::new( - sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()), - keystore_container.sync_keystore(), - bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), - ); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; let raw_slot_duration = slot_duration.slot_duration(); @@ -132,7 +112,9 @@ pub fn new_partial( Ok((timestamp, slot)) }, spawner: &task_manager.spawn_essential_handle(), - can_author_with: bioauth_can_author_with.clone(), + can_author_with: sp_consensus::CanAuthorWithNativeVersion::new( + client.executor().clone(), + ), registry: config.prometheus_registry(), check_for_equivocation: Default::default(), telemetry: None, @@ -148,7 +130,6 @@ pub fn new_partial( transaction_pool, other: ( bioauth_consensus_block_import, - bioauth_can_author_with, slot_duration, raw_slot_duration, ), @@ -166,8 +147,7 @@ pub async fn new_full(config: Configuration) -> Result Result = None; @@ -190,6 +170,21 @@ pub async fn new_full(config: Configuration) -> Result, + sc_basic_authorship::ProposerFactory< + sc_transaction_pool::FullPool, + FullBackend, + FullClient, + sp_consensus::DisableProofRecording, + >, + > = bioauth_consensus::BioauthProposer::new( + proposer_factory, + keystore_container.sync_keystore(), + bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), + ); + let (network, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, @@ -248,7 +243,7 @@ pub async fn new_full(config: Configuration) -> Result Date: Fri, 10 Sep 2021 10:26:57 +0300 Subject: [PATCH 3/6] Remove redundant type definition --- crates/humanode-peer/src/service.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index b30eff3f6..fc7c6951d 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -173,12 +173,7 @@ pub async fn new_full(config: Configuration) -> Result, - sc_basic_authorship::ProposerFactory< - sc_transaction_pool::FullPool, - FullBackend, - FullClient, - sp_consensus::DisableProofRecording, - >, + _, > = bioauth_consensus::BioauthProposer::new( proposer_factory, keystore_container.sync_keystore(), From 26091935c7afa990c981ddf066b64f88f4f26fdd Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Fri, 10 Sep 2021 17:07:21 +0300 Subject: [PATCH 4/6] Use block_in_plase for SyncCryptoStore --- crates/bioauth-consensus/Cargo.toml | 2 +- crates/bioauth-consensus/src/lib.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/bioauth-consensus/Cargo.toml b/crates/bioauth-consensus/Cargo.toml index 7bc4ac38c..9d7c8f5cb 100644 --- a/crates/bioauth-consensus/Cargo.toml +++ b/crates/bioauth-consensus/Cargo.toml @@ -20,12 +20,12 @@ sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", bra sp-keystore = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "master" } thiserror = "1" +tokio = { version = "1", features = ["full"] } [dev-dependencies] mockall = "0.10" node-primitives = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-service = { git = "https://github.com/humanode-network/substrate", branch = "master" } -tokio = { version = "1", features = ["full"] } [features] default = [] diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index 542f2fc65..7ea181915 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -226,10 +226,14 @@ where )))) }; - let aura_public_keys = sp_keystore::SyncCryptoStore::sr25519_public_keys( - self.keystore.as_ref(), - sp_application_crypto::key_types::AURA, - ); + let keystore_ref = self.keystore.as_ref(); + + let aura_public_keys = tokio::task::block_in_place(move || { + sp_keystore::SyncCryptoStore::sr25519_public_keys( + keystore_ref, + sp_application_crypto::key_types::AURA, + ) + }); assert!( aura_public_keys.len() == 1, From 13003c611cfe486ff7fa602e2a3f75dec8ec9782 Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Fri, 10 Sep 2021 18:07:29 +0300 Subject: [PATCH 5/6] Update Cargo.toml --- crates/bioauth-consensus/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bioauth-consensus/Cargo.toml b/crates/bioauth-consensus/Cargo.toml index e77b02ace..0bc19a05c 100644 --- a/crates/bioauth-consensus/Cargo.toml +++ b/crates/bioauth-consensus/Cargo.toml @@ -9,8 +9,8 @@ publish = false pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", optional = true } async-trait = "0.1.42" -futures = "0.3" codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] } +futures = "0.3" sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-api = { git = "https://github.com/humanode-network/substrate", branch = "master" } @@ -21,12 +21,13 @@ sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", bra sp-keystore = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "master" } thiserror = "1" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1", features = ["rt-multi-thread"] } [dev-dependencies] mockall = "0.10" node-primitives = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-service = { git = "https://github.com/humanode-network/substrate", branch = "master" } +tokio = { version = "1", features = ["full"] } [features] default = [] From ef20b490ba10231abad5c0e6fc9bfa77abfafc5d Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Fri, 10 Sep 2021 18:21:56 +0300 Subject: [PATCH 6/6] Change bioauth_proposer to proposer_factory --- crates/humanode-peer/src/service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index 976b67daa..a560822a3 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -170,7 +170,7 @@ pub async fn new_full(config: Configuration) -> Result, _, @@ -238,7 +238,7 @@ pub async fn new_full(config: Configuration) -> Result