diff --git a/Cargo.lock b/Cargo.lock index aa58140c4..cb4bcdb20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2332,6 +2332,7 @@ dependencies = [ "sc-consensus", "sc-consensus-aura", "sc-executor", + "sc-finality-grandpa", "sc-network", "sc-service", "sc-telemetry 4.0.0-dev", @@ -2340,6 +2341,7 @@ dependencies = [ "sp-application-crypto", "sp-consensus", "sp-consensus-aura", + "sp-finality-grandpa", "sp-keystore", "sp-runtime", "sp-timestamp", @@ -2383,6 +2385,7 @@ dependencies = [ "pallet-aura", "pallet-balances", "pallet-bioauth", + "pallet-grandpa", "pallet-randomness-collective-flip", "pallet-sudo", "pallet-timestamp", @@ -4295,6 +4298,20 @@ dependencies = [ "sp-std 4.0.0-dev", ] +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/humanode-network/substrate?branch=master#b7409a2257c7c231b485ee827daa73d05d303047" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-authorship", + "sp-runtime", + "sp-std 4.0.0-dev", +] + [[package]] name = "pallet-balances" version = "4.0.0-dev" @@ -4325,6 +4342,28 @@ dependencies = [ "sp-std 4.0.0-dev", ] +[[package]] +name = "pallet-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/humanode-network/substrate?branch=master#b7409a2257c7c231b485ee827daa73d05d303047" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "sp-application-crypto", + "sp-core", + "sp-finality-grandpa", + "sp-io", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 4.0.0-dev", +] + [[package]] name = "pallet-randomness-collective-flip" version = "4.0.0-dev" @@ -4355,6 +4394,7 @@ dependencies = [ "sp-session", "sp-staking", "sp-std 4.0.0-dev", + "sp-trie", ] [[package]] @@ -5943,6 +5983,47 @@ dependencies = [ "wasmi", ] +[[package]] +name = "sc-finality-grandpa" +version = "0.10.0-dev" +source = "git+https://github.com/humanode-network/substrate?branch=master#b7409a2257c7c231b485ee827daa73d05d303047" +dependencies = [ + "async-trait", + "derive_more", + "dyn-clone", + "finality-grandpa", + "fork-tree", + "futures 0.3.16", + "futures-timer 3.0.2", + "linked-hash-map", + "log", + "parity-scale-codec", + "parking_lot 0.11.1", + "pin-project 1.0.8", + "rand 0.8.4", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-keystore", + "sc-network", + "sc-network-gossip", + "sc-telemetry 4.0.0-dev", + "serde_json", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-finality-grandpa", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-utils 4.0.0-dev", + "substrate-prometheus-endpoint", + "wasm-timer", +] + [[package]] name = "sc-informant" version = "0.10.0-dev" @@ -6055,6 +6136,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "sc-network-gossip" +version = "0.10.0-dev" +source = "git+https://github.com/humanode-network/substrate?branch=master#b7409a2257c7c231b485ee827daa73d05d303047" +dependencies = [ + "futures 0.3.16", + "futures-timer 3.0.2", + "libp2p 0.37.1", + "log", + "lru", + "sc-network", + "sp-runtime", + "substrate-prometheus-endpoint", + "tracing", + "wasm-timer", +] + [[package]] name = "sc-offchain" version = "4.0.0-dev" @@ -6935,6 +7033,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "sp-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/humanode-network/substrate?branch=master#b7409a2257c7c231b485ee827daa73d05d303047" +dependencies = [ + "async-trait", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std 4.0.0-dev", +] + [[package]] name = "sp-block-builder" version = "4.0.0-dev" diff --git a/crates/bioauth-consensus/src/lib.rs b/crates/bioauth-consensus/src/lib.rs index f509eb228..3ec88372a 100644 --- a/crates/bioauth-consensus/src/lib.rs +++ b/crates/bioauth-consensus/src/lib.rs @@ -8,7 +8,7 @@ use futures::future; use futures::future::FutureExt; -use sc_client_api::{backend::Backend, Finalizer}; +use sc_client_api::backend::Backend; use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; use sp_api::{HeaderT, ProvideRuntimeApi, TransactionFor}; use sp_blockchain::{well_known_cache_keys, HeaderBackend}; @@ -32,9 +32,11 @@ mod traits; pub use traits::*; /// A block-import handler for Bioauth. -pub struct BioauthBlockImport { +pub struct BioauthBlockImport { /// The client to interact with the chain. - inner: Arc, + client: Arc, + /// The inner block import to wrap. + inner: BI, /// The block author extractor. block_author_extractor: BAX, /// The bioauth auhtrization verifier. @@ -63,13 +65,19 @@ where AuthorizationVerifier(AV), } -impl BioauthBlockImport { +impl BioauthBlockImport { /// Simple constructor. - pub fn new(inner: Arc, block_author_extractor: BAX, authorization_verifier: AV) -> Self + pub fn new( + client: Arc, + inner: BI, + block_author_extractor: BAX, + authorization_verifier: AV, + ) -> Self where BE: Backend + 'static, { Self { + client, inner, block_author_extractor, authorization_verifier, @@ -79,14 +87,17 @@ impl BioauthBlockImport Clone for BioauthBlockImport +impl Clone + for BioauthBlockImport where + BI: Clone, BAX: Clone, AV: Clone, { fn clone(&self) -> Self { Self { - inner: Arc::clone(&self.inner), + client: Arc::clone(&self.client), + inner: self.inner.clone(), block_author_extractor: self.block_author_extractor.clone(), authorization_verifier: self.authorization_verifier.clone(), _phantom_back_end: PhantomData, @@ -96,13 +107,14 @@ where } #[async_trait::async_trait] -impl BlockImport - for BioauthBlockImport +impl BlockImport + for BioauthBlockImport where - Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + Finalizer, - for<'a> &'a Client: - BlockImport>, + Client: HeaderBackend + ProvideRuntimeApi + Send + Sync, TransactionFor: 'static, + BI: BlockImport> + + Send + + Sync, BAX: BlockAuthorExtractor + Send, AV: AuthorizationVerifier + Send, ::PublicKeyType: Send + Sync, @@ -130,7 +142,7 @@ where cache: HashMap>, ) -> Result { // Extract a number of the last imported block. - let at = sp_api::BlockId::Hash(self.inner.info().best_hash); + let at = sp_api::BlockId::Hash(self.client.info().best_hash); let mkerr = |err: BioauthBlockImportError| -> ConsensusError { ConsensusError::Other(Box::new(err)) @@ -150,7 +162,7 @@ where return Err(mkerr(BioauthBlockImportError::NotBioauthAuthorized)); } - // Import a new block. + // Import a new block and apply finality with Grandpa. self.inner.import_block(block, cache).await } } diff --git a/crates/bioauth-consensus/src/tests.rs b/crates/bioauth-consensus/src/tests.rs index af09b2f0a..b144b2169 100644 --- a/crates/bioauth-consensus/src/tests.rs +++ b/crates/bioauth-consensus/src/tests.rs @@ -2,7 +2,6 @@ use mockall::predicate::*; use mockall::*; use node_primitives::{Block, BlockNumber, Hash, Header}; use pallet_bioauth::{self, BioauthApi, StoredAuthTicket}; -use sc_client_api::Finalizer; use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult}; use sp_api::{ApiError, ApiRef, NativeOrEncoded, ProvideRuntimeApi, TransactionFor}; use sp_application_crypto::Pair; @@ -41,18 +40,7 @@ struct MockWrapperRuntimeApi(Arc); mock! { #[derive(Debug)] - Client { - async fn check_block( - &self, - block: BlockCheckParams, - ) -> Result; - - async fn import_block( - &self, - block: BlockImportParams>, - cache: HashMap>, - ) -> Result; - } + Client {} impl ProvideRuntimeApi for Client { type Api = MockWrapperRuntimeApi; @@ -67,47 +55,26 @@ mock! { fn number(&self, _hash: Hash) -> sc_service::Result, sp_blockchain::Error>; fn hash(&self, _number: sp_api::NumberFor) -> sp_blockchain::Result>; } - - impl Finalizer> for Client { - fn apply_finality( - &self, - _operation: &mut sc_client_api::ClientImportOperation< - Block, - sc_service::TFullBackend, - >, - _id: sp_api::BlockId, - _justification: Option, - _notify: bool, - ) -> sp_blockchain::Result<()>; - fn finalize_block( - &self, - _id: sp_api::BlockId, - _justification: Option, - _notify: bool, - ) -> sp_blockchain::Result<()>; - } } -// mockall doesn't allow implement trait for references inside mock -#[async_trait::async_trait] -impl<'a> BlockImport for &'a MockClient { - type Error = ConsensusError; +mock! { + BlockImportWrapper {} - type Transaction = TransactionFor; + #[async_trait::async_trait] + impl BlockImport for BlockImportWrapper { + type Error = ConsensusError; + type Transaction = TransactionFor; - async fn check_block( - &mut self, - block: BlockCheckParams, - ) -> Result { - (**self).check_block(block).await - } + async fn check_block( + &mut self, + block: BlockCheckParams, + ) -> Result; - async fn import_block( - &mut self, - block: BlockImportParams>, - cache: HashMap>, - ) -> Result { - (**self).import_block(block, cache).await + async fn import_block( + &mut self, + block: BlockImportParams>, + cache: HashMap>, + ) -> Result; } } @@ -233,14 +200,23 @@ async fn it_denies_block_import_with_error_extract_authorities() { let client = Arc::new(mock_client); + let mut mock_block_import = MockBlockImportWrapper::new(); + mock_block_import + .expect_import_block() + .returning(|_, _| Ok(ImportResult::Imported(Default::default()))); + + let block_import = mock_block_import; + let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, + MockBlockImportWrapper, _, _, > = BioauthBlockImport::new( Arc::clone(&client), + block_import, crate::aura::BlockAuthorExtractor::new(Arc::clone(&client)), crate::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); @@ -282,14 +258,23 @@ async fn it_denies_block_import_with_invalid_slot_number() { let client = Arc::new(mock_client); + let mut mock_block_import = MockBlockImportWrapper::new(); + mock_block_import + .expect_import_block() + .returning(|_, _| Ok(ImportResult::Imported(Default::default()))); + + let block_import = mock_block_import; + let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, + MockBlockImportWrapper, _, _, > = BioauthBlockImport::new( Arc::clone(&client), + block_import, crate::aura::BlockAuthorExtractor::new(Arc::clone(&client)), crate::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); @@ -335,14 +320,23 @@ async fn it_denies_block_import_with_error_extract_stored_auth_ticket() { let client = Arc::new(mock_client); + let mut mock_block_import = MockBlockImportWrapper::new(); + mock_block_import + .expect_import_block() + .returning(|_, _| Ok(ImportResult::Imported(Default::default()))); + + let block_import = mock_block_import; + let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, + MockBlockImportWrapper, _, _, > = BioauthBlockImport::new( Arc::clone(&client), + block_import, crate::aura::BlockAuthorExtractor::new(Arc::clone(&client)), crate::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); @@ -398,14 +392,23 @@ async fn it_denies_block_import_with_not_bioauth_authorized() { let client = Arc::new(mock_client); + let mut mock_block_import = MockBlockImportWrapper::new(); + mock_block_import + .expect_import_block() + .returning(|_, _| Ok(ImportResult::Imported(Default::default()))); + + let block_import = mock_block_import; + let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, + MockBlockImportWrapper, _, _, > = BioauthBlockImport::new( Arc::clone(&client), + block_import, crate::aura::BlockAuthorExtractor::new(Arc::clone(&client)), crate::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); @@ -453,28 +456,29 @@ async fn it_permits_block_import_with_valid_data() { let runtime_api = MockWrapperRuntimeApi(Arc::new(mock_runtime_api)); - mock_client - .expect_finalize_block() - .returning(|_, _, _| Ok(())); - - mock_client - .expect_import_block() - .returning(|_, _| Ok(ImportResult::imported(Default::default()))); - mock_client .expect_runtime_api() .returning(move || runtime_api.clone().into()); let client = Arc::new(mock_client); + let mut mock_block_import = MockBlockImportWrapper::new(); + mock_block_import + .expect_import_block() + .returning(|_, _| Ok(ImportResult::Imported(Default::default()))); + + let block_import = mock_block_import; + let mut bioauth_block_import: BioauthBlockImport< sc_service::TFullBackend, _, MockClient, + MockBlockImportWrapper, _, _, > = BioauthBlockImport::new( Arc::clone(&client), + block_import, crate::aura::BlockAuthorExtractor::new(Arc::clone(&client)), crate::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); diff --git a/crates/humanode-peer/Cargo.toml b/crates/humanode-peer/Cargo.toml index 1ac8f7dd2..69a03f62c 100644 --- a/crates/humanode-peer/Cargo.toml +++ b/crates/humanode-peer/Cargo.toml @@ -32,6 +32,7 @@ sc-client-api = { git = "https://github.com/humanode-network/substrate", branch 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" } +sc-finality-grandpa = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-network = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-service = { git = "https://github.com/humanode-network/substrate", branch = "master" } sc-telemetry = { git = "https://github.com/humanode-network/substrate", branch = "master" } @@ -40,6 +41,7 @@ sc-transaction-pool = { git = "https://github.com/humanode-network/substrate", b sp-application-crypto = { 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" } +sp-finality-grandpa = { 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" } sp-timestamp = { git = "https://github.com/humanode-network/substrate", branch = "master" } diff --git a/crates/humanode-peer/src/chain_spec.rs b/crates/humanode-peer/src/chain_spec.rs index c68c5ac5a..9b16406b9 100644 --- a/crates/humanode-peer/src/chain_spec.rs +++ b/crates/humanode-peer/src/chain_spec.rs @@ -2,12 +2,13 @@ use hex_literal::hex; use humanode_runtime::{ - AccountId, AuraConfig, BalancesConfig, BioauthConfig, GenesisConfig, RobonodePublicKeyWrapper, - Signature, SudoConfig, SystemConfig, WASM_BINARY, + AccountId, AuraConfig, BalancesConfig, BioauthConfig, GenesisConfig, GrandpaConfig, + RobonodePublicKeyWrapper, Signature, SudoConfig, SystemConfig, WASM_BINARY, }; use pallet_bioauth::StoredAuthTicket; use sc_service::ChainType; use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::{ app_crypto::{sr25519, Pair, Public}, traits::{IdentifyAccount, Verify}, @@ -35,8 +36,8 @@ where } /// Generate an Aura authority key. -pub fn authority_keys_from_seed(s: &str) -> AuraId { - get_from_seed::(s) +pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { + (get_from_seed::(s), get_from_seed::(s)) } /// A configuration for local testnet. @@ -81,7 +82,7 @@ pub fn local_testnet_config() -> Result { get_account_id_from_seed::("Ferdie//stash"), ], vec![pallet_bioauth::StoredAuthTicket { - public_key: authority_keys_from_seed("Alice"), + public_key: authority_keys_from_seed("Alice").0, nonce: "1".as_bytes().to_vec(), }], robonode_public_key, @@ -130,7 +131,7 @@ pub fn development_config() -> Result { get_account_id_from_seed::("Bob//stash"), ], vec![pallet_bioauth::StoredAuthTicket { - public_key: authority_keys_from_seed("Alice"), + public_key: authority_keys_from_seed("Alice").0, nonce: "1".as_bytes().to_vec(), }], robonode_public_key, @@ -152,7 +153,7 @@ pub fn development_config() -> Result { /// Configure initial storage state for FRAME modules. fn testnet_genesis( wasm_binary: &[u8], - initial_authorities: Vec, + initial_authorities: Vec<(AuraId, GrandpaId)>, root_key: AccountId, endowed_accounts: Vec, stored_auth_tickets: Vec>, @@ -173,7 +174,13 @@ fn testnet_genesis( .collect(), }, aura: AuraConfig { - authorities: initial_authorities, + authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), + }, + grandpa: GrandpaConfig { + authorities: initial_authorities + .iter() + .map(|x| (x.1.clone(), 1)) + .collect(), }, sudo: SudoConfig { // Assign network admin rights. diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index 9bd895a36..17d8234a4 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -8,6 +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_finality_grandpa::SharedVoterState; use sc_service::{Error as ServiceError, KeystoreContainer, PartialComponents, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_consensus::SlotData; @@ -30,6 +31,9 @@ type FullClient = sc_service::TFullClient; type FullBackend = sc_service::TFullBackend; /// Full node select chain type. type FullSelectChain = sc_consensus::LongestChain; +/// Full node Grandpa type. +type FullGrandpa = + sc_finality_grandpa::GrandpaBlockImport; /// Construct a bare keystore from the configuration. pub fn keystore_container( @@ -51,10 +55,13 @@ pub fn new_partial( sc_consensus::DefaultImportQueue, sc_transaction_pool::FullPool, ( + FullGrandpa, + sc_finality_grandpa::LinkHalf, bioauth_consensus::BioauthBlockImport< FullBackend, Block, FullClient, + FullGrandpa, bioauth_consensus::aura::BlockAuthorExtractor, bioauth_consensus::bioauth::AuthorizationVerifier, >, @@ -101,14 +108,24 @@ pub fn new_partial( ); let select_chain = sc_consensus::LongestChain::new(Arc::clone(&backend)); + + let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( + Arc::clone(&client), + &(Arc::clone(&client) as Arc<_>), + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + let bioauth_consensus_block_import: bioauth_consensus::BioauthBlockImport< FullBackend, _, _, _, _, + _, > = bioauth_consensus::BioauthBlockImport::new( Arc::clone(&client), + grandpa_block_import.clone(), bioauth_consensus::aura::BlockAuthorExtractor::new(Arc::clone(&client)), bioauth_consensus::bioauth::AuthorizationVerifier::new(Arc::clone(&client)), ); @@ -119,7 +136,7 @@ pub fn new_partial( let import_queue = sc_consensus_aura::import_queue::(ImportQueueParams { block_import: bioauth_consensus_block_import.clone(), - justification_import: None, + justification_import: Some(Box::new(grandpa_block_import.clone())), client: Arc::clone(&client), create_inherent_data_providers: move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); @@ -150,6 +167,8 @@ pub fn new_partial( select_chain, transaction_pool, other: ( + grandpa_block_import, + grandpa_link, bioauth_consensus_block_import, slot_duration, raw_slot_duration, @@ -169,20 +188,43 @@ pub async fn new_full(config: Configuration) -> Result = None; + let prometheus_registry = config.prometheus_registry().cloned(); let proposer_factory = sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), @@ -211,7 +253,7 @@ pub async fn new_full(config: Configuration) -> Result Result Result>::Proof; + + type KeyOwnerIdentification = >::IdentificationTuple; + + type HandleEquivocation = (); + + type WeightInfo = (); +} + parameter_types! { pub const MinimumPeriod: u64 = SLOT_DURATION / 2; } @@ -356,6 +381,7 @@ construct_runtime!( TransactionPayment: pallet_transaction_payment::{Pallet, Storage}, Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, Bioauth: pallet_bioauth::{Pallet, Config, Call, Storage, Event, ValidateUnsigned}, + Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config, Event}, } ); @@ -474,6 +500,36 @@ impl_runtime_apis! { } } + impl fg_primitives::GrandpaApi for Runtime { + fn grandpa_authorities() -> GrandpaAuthorityList { + Grandpa::grandpa_authorities() + } + + fn current_set_id() -> fg_primitives::SetId { + Grandpa::current_set_id() + } + + fn submit_report_equivocation_unsigned_extrinsic( + _equivocation_proof: fg_primitives::EquivocationProof< + ::Hash, + NumberFor, + >, + _key_owner_proof: fg_primitives::OpaqueKeyOwnershipProof, + ) -> Option<()> { + None + } + + fn generate_key_ownership_proof( + _set_id: fg_primitives::SetId, + _authority_id: GrandpaId, + ) -> Option { + // NOTE: this is the only implementation possible since we've + // defined our key owner proof type as a bottom type (i.e. a type + // with no values). + None + } + } + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account)