From 1ee47686c94ea404fc368de64fe45489268875b8 Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Thu, 12 Aug 2021 16:41:22 +0300 Subject: [PATCH 1/3] Decouple bioauth consensus from aura and bioauth pallet --- .../src/author_validation.rs | 30 +++++ crates/bioauth-consensus/src/lib.rs | 119 ++++++++++++------ 2 files changed, 111 insertions(+), 38 deletions(-) create mode 100644 crates/bioauth-consensus/src/author_validation.rs diff --git a/crates/bioauth-consensus/src/author_validation.rs b/crates/bioauth-consensus/src/author_validation.rs new file mode 100644 index 000000000..a14d4437d --- /dev/null +++ b/crates/bioauth-consensus/src/author_validation.rs @@ -0,0 +1,30 @@ +//! Block author validation logic. + +/// BlockAuthorExtractor provides a functionality to extract +/// block author public key for a particular block. +pub trait BlockAuthorExtractor { + /// BlockAuthorExtractor error. + type Error; + /// BlockHeader type. + type BlockHeader; + /// Block author Public key type. + type PublicKeyType; + + /// Extract block author public key for a provided block header. + fn extract_block_author( + &self, + block_header: &Self::BlockHeader, + ) -> Result; +} + +/// BlockAuthorExtractor provides a functionality to verify +/// whether aparticular author is authorized to be a validator. +pub trait AuthorizationVerifier { + /// AuthorizationVerifier + type Error; + /// Public key type. + type PublicKeyType: ?Sized; + + /// Verify that a provided author is authorized by it's public key. + fn is_authorized(&self, author_public_key: &Self::PublicKeyType) -> Result; +} diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index 6cc09ed30..be2948e29 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -6,6 +6,7 @@ clippy::clone_on_ref_ptr )] +use author_validation::{AuthorizationVerifier, BlockAuthorExtractor}; use pallet_bioauth::BioauthApi; use sc_client_api::{backend::Backend, Finalizer}; use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; @@ -19,6 +20,8 @@ use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; use thiserror::Error; +mod author_validation; + #[cfg(test)] mod tests; @@ -49,16 +52,6 @@ pub enum BioauthBlockImportError { ErrorExtractAuthorities, } -impl Clone for BioauthBlockImport { - fn clone(&self) -> Self { - BioauthBlockImport { - inner: Arc::clone(&self.inner), - _phantom_back_end: PhantomData, - _phantom_block: PhantomData, - } - } -} - impl BioauthBlockImport { /// Simple constructor. pub fn new(inner: Arc) -> Self @@ -73,36 +66,30 @@ impl BioauthBlockImport { } } -#[async_trait::async_trait] -impl BlockImport for BioauthBlockImport +impl Clone for BioauthBlockImport { + fn clone(&self) -> Self { + BioauthBlockImport { + inner: Arc::clone(&self.inner), + _phantom_back_end: PhantomData, + _phantom_block: PhantomData, + } + } +} + +impl BlockAuthorExtractor + for BioauthBlockImport where - Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + Finalizer, - for<'a> &'a Client: - BlockImport>, - TransactionFor: 'static, + Client: HeaderBackend + ProvideRuntimeApi, Client::Api: AuraApi, - Client::Api: BioauthApi, - BE: Backend, { type Error = ConsensusError; + type BlockHeader = Block::Header; + type PublicKeyType = Vec; - type Transaction = TransactionFor; - - /// Check block preconditions. Only entire structure of a block. - async fn check_block( - &mut self, - block: BlockCheckParams, - ) -> Result { - self.inner.check_block(block).await - } - - /// Import a block. - /// Cached data can be accessed through the blockchain cache. - async fn import_block( - &mut self, - block: BlockImportParams, - cache: HashMap>, - ) -> Result { + fn extract_block_author( + &self, + block_header: &Self::BlockHeader, + ) -> Result, Self::Error> { // Extract a number of the last imported block. let at = &sp_api::BlockId::Hash(self.inner.info().best_hash); @@ -112,8 +99,7 @@ where })?; // Extract current slot of a new produced block. - let mut slot = block - .header + let mut slot = block_header .digest() .log(|l| l.try_as_raw(OpaqueDigestItemId::PreRuntime(b"aura"))) .ok_or_else(|| { @@ -136,7 +122,23 @@ where .ok_or_else(|| { sp_consensus::Error::Other(Box::new(BioauthBlockImportError::InvalidSlotNumber)) })?; - let author_public_key = author_public_key.as_slice(); + + Ok(author_public_key.to_raw_vec()) + } +} + +impl AuthorizationVerifier + for BioauthBlockImport +where + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: BioauthApi, +{ + type Error = ConsensusError; + type PublicKeyType = [u8]; + + fn is_authorized(&self, author_public_key: &Self::PublicKeyType) -> Result { + // Extract a number of the last imported block. + let at = &sp_api::BlockId::Hash(self.inner.info().best_hash); // Get current stored tickets. let stored_tickets = self @@ -153,6 +155,47 @@ where .iter() .any(|ticket| ticket.public_key == author_public_key); + Ok(is_authorized) + } +} + +#[async_trait::async_trait] +impl BlockImport for BioauthBlockImport +where + Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + Finalizer, + for<'a> &'a Client: + BlockImport>, + TransactionFor: 'static, + Client::Api: AuraApi, + Client::Api: BioauthApi, + BE: Backend, +{ + type Error = ConsensusError; + + type Transaction = TransactionFor; + + /// Check block preconditions. Only entire structure of a block. + async fn check_block( + &mut self, + block: BlockCheckParams, + ) -> Result { + self.inner.check_block(block).await + } + + /// Import a block. + /// Cached data can be accessed through the blockchain cache. + async fn import_block( + &mut self, + block: BlockImportParams, + cache: HashMap>, + ) -> Result { + // Extract a number of the last imported block. + let at = &sp_api::BlockId::Hash(self.inner.info().best_hash); + + let author_public_key = self.extract_block_author(&block.header)?; + + let is_authorized = self.is_authorized(author_public_key.as_slice())?; + if !is_authorized { return Err(sp_consensus::Error::Other(Box::new( BioauthBlockImportError::NotBioauthAuthorized, From eba189eac1674a377e1dba533e5715f7f7355eda Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Thu, 12 Aug 2021 19:35:44 +0300 Subject: [PATCH 2/3] Add support for --dev mode and allow reading chain spec from files --- crates/humanode-peer/src/chain_spec.rs | 161 ++++++++++++++++++------- crates/humanode-peer/src/command.rs | 15 ++- 2 files changed, 125 insertions(+), 51 deletions(-) diff --git a/crates/humanode-peer/src/chain_spec.rs b/crates/humanode-peer/src/chain_spec.rs index 9a23fb876..c44a5a9eb 100644 --- a/crates/humanode-peer/src/chain_spec.rs +++ b/crates/humanode-peer/src/chain_spec.rs @@ -5,6 +5,7 @@ use humanode_runtime::{ AccountId, AuraConfig, BalancesConfig, BioauthConfig, GenesisConfig, RobonodePublicKeyWrapper, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; +use pallet_bioauth::StoredAuthTicket; use sc_service::ChainType; use sp_consensus_aura::sr25519::AuthorityId as AuraId; use sp_runtime::{ @@ -55,49 +56,36 @@ pub fn local_testnet_config() -> Result { "local_testnet", ChainType::Local, move || { - GenesisConfig { - system: SystemConfig { - // Add Wasm runtime to storage. - code: wasm_binary.to_vec(), - changes_trie_config: Default::default(), - }, - balances: BalancesConfig { - // Configure endowed accounts with initial balance of 1 << 60. - balances: vec![ - ( - get_account_id_from_seed::("Alice"), - 1 << 60, - ), - ( - get_account_id_from_seed::("Alice//stash"), - 1 << 60, - ), - (get_account_id_from_seed::("Bob"), 1 << 60), - ( - get_account_id_from_seed::("Bob//stash"), - 1 << 60, - ), - ], - }, - aura: AuraConfig { - authorities: vec![ - authority_keys_from_seed("Alice"), - authority_keys_from_seed("Bob"), - ], - }, - sudo: SudoConfig { - // Assign network admin rights. - key: get_account_id_from_seed::("Alice"), - }, - bioauth: BioauthConfig { - // Add Alice AuraId to StoredAuthTickets for producing blocks - stored_auth_tickets: vec![pallet_bioauth::StoredAuthTicket { - public_key: authority_keys_from_seed("Alice").as_slice().to_vec(), - nonce: "1".as_bytes().to_vec(), - }], - robonode_public_key, - }, - } + testnet_genesis( + wasm_binary, + // Initial PoA authorities + vec![ + authority_keys_from_seed("Alice"), + authority_keys_from_seed("Bob"), + ], + // Sudo account + get_account_id_from_seed::("Alice"), + // Pre-funded accounts + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ], + vec![pallet_bioauth::StoredAuthTicket { + public_key: authority_keys_from_seed("Alice").as_slice().to_vec(), + nonce: "1".as_bytes().to_vec(), + }], + robonode_public_key, + ) }, // Bootnodes vec![], @@ -111,3 +99,90 @@ pub fn local_testnet_config() -> Result { None, )) } + +/// A configuration for dev. +pub fn development_config() -> Result { + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + + let robonode_public_key = RobonodePublicKeyWrapper::from_bytes( + &hex!("5dde03934419252d13336e5a5881f5b1ef9ea47084538eb229f86349e7f394ab")[..], + ) + .map_err(|err| format!("{:?}", err))?; + + Ok(ChainSpec::from_genesis( + // Name + "Development", + // ID + "dev", + ChainType::Development, + move || { + testnet_genesis( + wasm_binary, + // Initial PoA authorities + vec![authority_keys_from_seed("Alice")], + // Sudo account + get_account_id_from_seed::("Alice"), + // Pre-funded accounts + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + vec![pallet_bioauth::StoredAuthTicket { + public_key: authority_keys_from_seed("Alice").as_slice().to_vec(), + nonce: "1".as_bytes().to_vec(), + }], + robonode_public_key, + ) + }, + // Bootnodes + vec![], + // Telemetry + None, + // Protocol ID + None, + // Properties + None, + // Extensions + None, + )) +} + +/// Configure initial storage state for FRAME modules. +fn testnet_genesis( + wasm_binary: &[u8], + initial_authorities: Vec, + root_key: AccountId, + endowed_accounts: Vec, + stored_auth_tickets: Vec, + robonode_public_key: RobonodePublicKeyWrapper, +) -> GenesisConfig { + GenesisConfig { + system: SystemConfig { + // Add Wasm runtime to storage. + code: wasm_binary.to_vec(), + changes_trie_config: Default::default(), + }, + balances: BalancesConfig { + // Configure endowed accounts with initial balance of 1 << 60. + balances: endowed_accounts + .iter() + .cloned() + .map(|k| (k, 1 << 60)) + .collect(), + }, + aura: AuraConfig { + authorities: initial_authorities, + }, + sudo: SudoConfig { + // Assign network admin rights. + key: root_key, + }, + bioauth: BioauthConfig { + // Add Alice AuraId to StoredAuthTickets for producing blocks + stored_auth_tickets, + robonode_public_key, + }, + } +} diff --git a/crates/humanode-peer/src/command.rs b/crates/humanode-peer/src/command.rs index ada0c8cdf..46083146b 100644 --- a/crates/humanode-peer/src/command.rs +++ b/crates/humanode-peer/src/command.rs @@ -36,14 +36,13 @@ impl SubstrateCli for Cli { } fn load_spec(&self, id: &str) -> std::result::Result, String> { - if id != "local" && !id.is_empty() { - return Err(format!( - "chain {:?} is not supported, only {:?} is currently available", - id, "local" - )); - } - - Ok(Box::new(chain_spec::local_testnet_config()?)) + Ok(match id { + "dev" => Box::new(chain_spec::development_config()?), + "" | "local" => Box::new(chain_spec::local_testnet_config()?), + path => Box::new(chain_spec::ChainSpec::from_json_file( + std::path::PathBuf::from(path), + )?), + }) } fn native_runtime_version(_chain_spec: &Box) -> &'static RuntimeVersion { From 8563ab30328d4d17eb5c75c99f58233296e2724d Mon Sep 17 00:00:00 2001 From: dmitrylavrenov Date: Mon, 16 Aug 2021 16:17:48 +0300 Subject: [PATCH 3/3] Add implementation of CanAuthorWith trait --- Cargo.lock | 3 ++- crates/bioauth-consensus/Cargo.toml | 2 ++ crates/bioauth-consensus/src/lib.rs | 37 +++++++++++++++++++++++++-- crates/bioauth-consensus/src/tests.rs | 17 ++++++++---- crates/humanode-peer/Cargo.toml | 1 - crates/humanode-peer/src/service.rs | 12 ++++----- 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b6f1a585..699899d0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,6 +474,7 @@ dependencies = [ "pallet-bioauth", "sc-client-api", "sc-consensus", + "sc-keystore", "sc-service", "sc-tracing 4.0.0-dev", "sp-api", @@ -483,6 +484,7 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-slots", "sp-core", + "sp-keystore", "sp-runtime", "thiserror", "tokio 1.9.0", @@ -2330,7 +2332,6 @@ dependencies = [ "robonode-client", "sc-basic-authorship", "sc-cli", - "sc-client-api", "sc-consensus", "sc-consensus-aura", "sc-executor", diff --git a/crates/bioauth-consensus/Cargo.toml b/crates/bioauth-consensus/Cargo.toml index da37684a8..4e4a22df1 100644 --- a/crates/bioauth-consensus/Cargo.toml +++ b/crates/bioauth-consensus/Cargo.toml @@ -20,12 +20,14 @@ sp-blockchain = { git = "https://github.com/humanode-network/substrate", branch sp-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-consensus-slots = { git = "https://github.com/humanode-network/substrate", branch = "master" } +sp-keystore = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "master" } thiserror = "1" [dev-dependencies] mockall = "0.10" node-primitives = { git = "https://github.com/humanode-network/substrate", branch = "master" } +sc-keystore = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-service = { git = "https://github.com/humanode-network/substrate", branch = "master" } sp-core = { git = "https://github.com/humanode-network/substrate", branch = "master" } tokio = { version = "1", features = ["full"] } diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index be2948e29..fd07d6cc6 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -13,8 +13,9 @@ use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResul use sp_api::{Decode, 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_consensus_aura::{AuraApi, Slot}; +use sp_keystore::SyncCryptoStorePtr; use sp_runtime::generic::OpaqueDigestItemId; use sp_runtime::traits::{Block as BlockT, Header}; use std::{collections::HashMap, marker::PhantomData, sync::Arc}; @@ -29,6 +30,8 @@ mod tests; pub struct BioauthBlockImport { /// The client to interact with the chain. inner: Arc, + /// Keystore to extract validator public key. + keystore: SyncCryptoStorePtr, /// A phantom data for Backend. _phantom_back_end: PhantomData, /// A phantom data for Block. @@ -54,12 +57,13 @@ pub enum BioauthBlockImportError { impl BioauthBlockImport { /// Simple constructor. - pub fn new(inner: Arc) -> Self + pub fn new(inner: Arc, keystore: SyncCryptoStorePtr) -> Self where BE: Backend + 'static, { BioauthBlockImport { inner, + keystore, _phantom_back_end: PhantomData, _phantom_block: PhantomData, } @@ -70,6 +74,7 @@ impl Clone for BioauthBlockImport Self { BioauthBlockImport { inner: Arc::clone(&self.inner), + keystore: Arc::clone(&self.keystore), _phantom_back_end: PhantomData, _phantom_block: PhantomData, } @@ -211,3 +216,31 @@ where self.inner.import_block(block, cache).await } } + +impl CanAuthorWith for BioauthBlockImport +where + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: BioauthApi, +{ + fn can_author_with(&self, _at: &sp_api::BlockId) -> Result<(), String> { + let mut aura_public_keys = sp_keystore::SyncCryptoStore::sr25519_public_keys( + self.keystore.as_ref(), + sp_application_crypto::key_types::AURA, + ); + assert_eq!(aura_public_keys.len(), 1); + let aura_public_key = match aura_public_keys.drain(..).next() { + Some(v) => v, + _ => return Err("You aren't Aura validator.".to_string()), + }; + + let is_authorized = self + .is_authorized(aura_public_key.as_slice()) + .map_err(|e| e.to_string())?; + + if !is_authorized { + return Err("You aren't bioauth-authorized.".to_string()); + } + + Ok(()) + } +} diff --git a/crates/bioauth-consensus/src/tests.rs b/crates/bioauth-consensus/src/tests.rs index 6257425c2..87e20e296 100644 --- a/crates/bioauth-consensus/src/tests.rs +++ b/crates/bioauth-consensus/src/tests.rs @@ -3,6 +3,8 @@ use mockall::predicate::*; use mockall::*; use node_primitives::{Block, BlockNumber, Hash, Header}; use pallet_bioauth::StoredAuthTicket; +use sc_service::config::KeystoreConfig; +use sc_service::KeystoreContainer; use sp_api::{ApiError, ApiRef, NativeOrEncoded}; use sp_consensus::BlockOrigin; use sp_consensus_aura::digests::CompatibleDigestItem; @@ -202,12 +204,13 @@ async fn it_denies_block_import_with_error_extract_authorities() { .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let keystore_container = KeystoreContainer::new(&KeystoreConfig::InMemory).unwrap(); let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, - > = BioauthBlockImport::new(Arc::clone(&client)); + > = BioauthBlockImport::new(Arc::clone(&client), keystore_container.sync_keystore()); let res = bioauth_block_import .import_block( @@ -243,12 +246,13 @@ async fn it_denies_block_import_with_invalid_slot_number() { .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let keystore_container = KeystoreContainer::new(&KeystoreConfig::InMemory).unwrap(); let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, - > = BioauthBlockImport::new(Arc::clone(&client)); + > = BioauthBlockImport::new(Arc::clone(&client), keystore_container.sync_keystore()); let res = bioauth_block_import .import_block( @@ -287,12 +291,13 @@ async fn it_denies_block_import_with_error_extract_stored_auth_ticket() { .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let keystore_container = KeystoreContainer::new(&KeystoreConfig::InMemory).unwrap(); let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, - > = BioauthBlockImport::new(Arc::clone(&client)); + > = BioauthBlockImport::new(Arc::clone(&client), keystore_container.sync_keystore()); let res = bioauth_block_import .import_block( @@ -339,12 +344,13 @@ async fn it_denies_block_import_with_not_bioauth_authorized() { .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let keystore_container = KeystoreContainer::new(&KeystoreConfig::InMemory).unwrap(); let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, - > = BioauthBlockImport::new(Arc::clone(&client)); + > = BioauthBlockImport::new(Arc::clone(&client), keystore_container.sync_keystore()); let res = bioauth_block_import .import_block( @@ -405,12 +411,13 @@ async fn it_permits_block_import_with_valid_data() { .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let keystore_container = KeystoreContainer::new(&KeystoreConfig::InMemory).unwrap(); let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, - > = BioauthBlockImport::new(Arc::clone(&client)); + > = BioauthBlockImport::new(Arc::clone(&client), keystore_container.sync_keystore()); let res = bioauth_block_import .import_block( diff --git a/crates/humanode-peer/Cargo.toml b/crates/humanode-peer/Cargo.toml index eaff56ab9..3254712f0 100644 --- a/crates/humanode-peer/Cargo.toml +++ b/crates/humanode-peer/Cargo.toml @@ -22,7 +22,6 @@ qr2term = "0.2" reqwest = "0.11" sc-basic-authorship = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-cli = { git = "https://github.com/humanode-network/substrate", branch = "master" } -sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-consensus-aura = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-executor = { git = "https://github.com/humanode-network/substrate", branch = "master" } diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index fb5ebd767..eb57d38c5 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -4,7 +4,6 @@ use std::{marker::PhantomData, sync::Arc, time::Duration}; use humanode_runtime::{self, opaque::Block, RuntimeApi}; -use sc_client_api::ExecutorProvider; use sc_consensus_aura::{ImportQueueParams, SlotDuration, SlotProportion, StartAuraParams}; use sc_executor::native_executor_instance; pub use sc_executor::NativeExecutor; @@ -60,7 +59,10 @@ pub fn new_partial( let select_chain = sc_consensus::LongestChain::new(Arc::clone(&backend)); let bioauth_consensus_block_import: bioauth_consensus::BioauthBlockImport = - bioauth_consensus::BioauthBlockImport::new(Arc::clone(&client)); + bioauth_consensus::BioauthBlockImport::new( + Arc::clone(&client), + keystore_container.sync_keystore(), + ); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; let raw_slot_duration = slot_duration.slot_duration(); @@ -82,9 +84,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_consensus_block_import.clone(), registry: config.prometheus_registry(), check_for_equivocation: Default::default(), telemetry: None, @@ -120,7 +120,7 @@ pub async fn new_full(config: Configuration) -> Result = None;