// Copyright 2019-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate 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. // Substrate 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 Substrate. If not, see . //! Simple transaction factory which distributes tokens from a master //! account to a specified number of newly created accounts. //! //! The factory currently only works on an empty database! use std::collections::HashMap; use std::sync::Arc; use std::cmp::PartialOrd; use std::fmt::Display; use log::info; use sc_client::Client; use sp_block_builder::BlockBuilder; use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi, ApiExt}; use sp_consensus::{ BlockOrigin, BlockImportParams, InherentData, ForkChoiceStrategy, SelectChain }; use sp_consensus::block_import::BlockImport; use codec::{Decode, Encode}; use sp_runtime::generic::BlockId; use sp_runtime::traits::{ Block as BlockT, Header as HeaderT, AtLeast32Bit, One, Zero, }; pub trait RuntimeAdapter { type AccountId: Display; type Balance: Display + AtLeast32Bit + From; type Block: BlockT; type Index: Copy; type Number: Display + PartialOrd + AtLeast32Bit + Zero + One; type Phase: Copy; type Secret; fn new(blocks: u32, transactions: u32) -> Self; fn blocks(&self) -> u32; fn transactions(&self) -> u32; fn block_number(&self) -> u32; fn set_block_number(&mut self, value: u32); fn transfer_extrinsic( &mut self, sender: &Self::AccountId, key: &Self::Secret, destination: &Self::AccountId, amount: &Self::Balance, version: u32, genesis_hash: &::Hash, prior_block_hash: &::Hash, ) -> ::Extrinsic; fn inherent_extrinsics(&self) -> InherentData; fn minimum_balance() -> Self::Balance; fn master_account_id() -> Self::AccountId; fn master_account_secret() -> Self::Secret; fn gen_random_account_id(seed: u32) -> Self::AccountId; fn gen_random_account_secret(seed: u32) -> Self::Secret; } /// Manufactures transactions. The exact amount depends on `num` and `rounds`. pub fn factory( mut factory_state: RA, client: &Arc>, select_chain: &Sc, ) -> sc_cli::Result<()> where Block: BlockT, Exec: sc_client::CallExecutor + Send + Sync + Clone, Backend: sc_client_api::backend::Backend + Send, Client: ProvideRuntimeApi, as ProvideRuntimeApi>::Api: BlockBuilder + ApiExt, RtApi: ConstructRuntimeApi> + Send + Sync, Sc: SelectChain, RA: RuntimeAdapter, Block::Hash: From, { let best_header: Result<::Header, sc_cli::Error> = select_chain.best_chain().map_err(|e| format!("{:?}", e).into()); let mut best_hash = best_header?.hash(); let mut best_block_id = BlockId::::hash(best_hash); let version = client.runtime_version_at(&best_block_id)?.spec_version; let genesis_hash = client.block_hash(Zero::zero())? .expect("Genesis block always exists; qed").into(); while factory_state.block_number() < factory_state.blocks() { let from = (RA::master_account_id(), RA::master_account_secret()); let amount = RA::minimum_balance(); let inherents = RA::inherent_extrinsics(&factory_state); let inherents = client.runtime_api().inherent_extrinsics(&best_block_id, inherents) .expect("Failed to create inherent extrinsics"); let tx_per_block = factory_state.transactions(); let mut block = client.new_block(Default::default()).expect("Failed to create new block"); for tx_num in 0..tx_per_block { let seed = tx_num * (factory_state.block_number() + 1); let to = RA::gen_random_account_id(seed); let transfer = factory_state.transfer_extrinsic( &from.0, &from.1, &to, &amount, version, &genesis_hash, &best_hash, ); info!("Pushing transfer {}/{} to {} into block.", tx_num + 1, tx_per_block, to); block.push( Decode::decode(&mut &transfer.encode()[..]) .expect("Failed to decode transfer extrinsic") ).expect("Failed to push transfer extrinsic into block"); } for inherent in inherents { block.push(inherent).expect("Failed ..."); } let block = block.build().expect("Failed to bake block").block; factory_state.set_block_number(factory_state.block_number() + 1); info!( "Created block {} with hash {}.", factory_state.block_number(), best_hash, ); best_hash = block.header().hash(); best_block_id = BlockId::::hash(best_hash); let mut import = BlockImportParams::new(BlockOrigin::File, block.header().clone()); import.body = Some(block.extrinsics().to_vec()); import.fork_choice = Some(ForkChoiceStrategy::LongestChain); client.clone().import_block(import, HashMap::new()).expect("Failed to import block"); info!("Imported block at {}", factory_state.block_number()); } Ok(()) }