// This file is part of Substrate. // Copyright (C) 2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Basic example of end to end runtime tests. use test_runner::{Node, ChainInfo, SignatureVerificationOverride, default_config}; use grandpa::GrandpaBlockImport; use sc_service::{TFullBackend, TFullClient, Configuration, TaskManager, new_full_parts, TaskExecutor}; use std::sync::Arc; use sp_inherents::CreateInherentDataProviders; use sc_consensus_babe::BabeBlockImport; use sp_keystore::SyncCryptoStorePtr; use sp_keyring::sr25519::Keyring::Alice; use sp_consensus_babe::AuthorityId; use sc_consensus_manual_seal::{ ConsensusDataProvider, consensus::babe::{BabeConsensusDataProvider, SlotTimestampProvider}, }; use sp_runtime::{traits::IdentifyAccount, MultiSigner, generic::Era}; use node_cli::chain_spec::development_config; type BlockImport = BabeBlockImport>; sc_executor::native_executor_instance!( pub Executor, node_runtime::api::dispatch, node_runtime::native_version, ( frame_benchmarking::benchmarking::HostFunctions, SignatureVerificationOverride, ) ); /// ChainInfo implementation. struct NodeTemplateChainInfo; impl ChainInfo for NodeTemplateChainInfo { type Block = node_primitives::Block; type Executor = Executor; type Runtime = node_runtime::Runtime; type RuntimeApi = node_runtime::RuntimeApi; type SelectChain = sc_consensus::LongestChain, Self::Block>; type BlockImport = BlockImport< Self::Block, TFullBackend, TFullClient, Self::SelectChain, >; type SignedExtras = node_runtime::SignedExtra; type InherentDataProviders = (SlotTimestampProvider, sp_consensus_babe::inherents::InherentDataProvider); fn signed_extras(from: ::AccountId) -> Self::SignedExtras { ( frame_system::CheckSpecVersion::::new(), frame_system::CheckTxVersion::::new(), frame_system::CheckGenesis::::new(), frame_system::CheckMortality::::from(Era::Immortal), frame_system::CheckNonce::::from(frame_system::Pallet::::account_nonce(from)), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0), ) } fn config(task_executor: TaskExecutor) -> Configuration { default_config(task_executor, Box::new(development_config())) } fn create_client_parts( config: &Configuration, ) -> Result< ( Arc>, Arc>, SyncCryptoStorePtr, TaskManager, Box>, Option< Box< dyn ConsensusDataProvider< Self::Block, Transaction = sp_api::TransactionFor< TFullClient, Self::Block, >, >, >, >, Self::SelectChain, Self::BlockImport, ), sc_service::Error, > { let (client, backend, keystore, task_manager) = new_full_parts::(config, None)?; let client = Arc::new(client); let select_chain = sc_consensus::LongestChain::new(backend.clone()); let (grandpa_block_import, ..) = grandpa::block_import( client.clone(), &(client.clone() as Arc<_>), select_chain.clone(), None )?; let slot_duration = sc_consensus_babe::Config::get_or_compute(&*client)?; let (block_import, babe_link) = sc_consensus_babe::block_import( slot_duration.clone(), grandpa_block_import, client.clone(), )?; let consensus_data_provider = BabeConsensusDataProvider::new( client.clone(), keystore.sync_keystore(), babe_link.epoch_changes().clone(), vec![(AuthorityId::from(Alice.public()), 1000)], ) .expect("failed to create ConsensusDataProvider"); Ok(( client.clone(), backend, keystore.sync_keystore(), task_manager, Box::new(move |_, _| { let client = client.clone(); async move { let timestamp = SlotTimestampProvider::new(client.clone()).map_err(|err| format!("{:?}", err))?; let babe = sp_consensus_babe::inherents::InherentDataProvider::new(timestamp.slot().into()); Ok((timestamp, babe)) } }), Some(Box::new(consensus_data_provider)), select_chain, block_import, )) } fn dispatch_with_root(call: ::Call, node: &mut Node) { let alice = MultiSigner::from(Alice.public()).into_account(); let call = pallet_sudo::Call::sudo(Box::new(call)); node.submit_extrinsic(call, alice); node.seal_blocks(1); } } #[cfg(test)] mod tests { use super::*; use test_runner::NodeConfig; use log::LevelFilter; #[test] fn test_runner() { let config = NodeConfig { log_targets: vec![ ("yamux", LevelFilter::Off), ("multistream_select", LevelFilter::Off), ("libp2p", LevelFilter::Off), ("jsonrpc_client_transports", LevelFilter::Off), ("sc_network", LevelFilter::Off), ("tokio_reactor", LevelFilter::Off), ("parity-db", LevelFilter::Off), ("sub-libp2p", LevelFilter::Off), ("sync", LevelFilter::Off), ("peerset", LevelFilter::Off), ("ws", LevelFilter::Off), ("sc_network", LevelFilter::Off), ("sc_service", LevelFilter::Off), ("sc_basic_authorship", LevelFilter::Off), ("telemetry-logger", LevelFilter::Off), ("sc_peerset", LevelFilter::Off), ("rpc", LevelFilter::Off), ("runtime", LevelFilter::Trace), ("babe", LevelFilter::Debug) ], }; let mut node = Node::::new(config).unwrap(); // seals blocks node.seal_blocks(1); // submit extrinsics let alice = MultiSigner::from(Alice.public()).into_account(); node.submit_extrinsic(frame_system::Call::remark((b"hello world").to_vec()), alice); // look ma, I can read state. let _events = node.with_state(|| frame_system::Pallet::::events()); // get access to the underlying client. let _client = node.client(); } }