// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. #![allow(dead_code)] #![allow(unused_variables)] use bizinikiwi_txtesttool::{ block_monitor::BlockMonitor, cli::{Cli, CliCommand}, execution_log::{journal::Journal, make_stats, STAT_TARGET}, init_logger, runner::DefaultTxTask, scenario::{AccountsDescription, ChainType, ScenarioBuilder, ScenarioType}, subxt_transaction::{ self, generate_ecdsa_keypair, generate_sr25519_keypair, BizinikiwTransaction, BizinikiwTransactionsSink, EthRuntimeConfig, EthTransaction, EthTransactionsSink, SENDER_SEED, }, }; use clap::Parser; use codec::Compact; use pezkuwi_subxt::{ext::frame_metadata::RuntimeMetadataPrefixed, PezkuwiConfig}; use std::{fs, fs::File, io::BufReader, time::Duration}; use tracing::info; macro_rules! populate_scenario_builder { ($scenario_builder:expr, $scenario_type:expr) => {{ match $scenario_type { ScenarioType::OneShot { account, nonce } => { $scenario_builder.with_account_id(account.clone()).with_nonce_from(*nonce) }, ScenarioType::FromSingleAccount { account, from, count } => $scenario_builder .with_account_id(account.clone()) .with_nonce_from(*from) .with_txs_count(*count), ScenarioType::FromManyAccounts { start_id, last_id, from, count } => $scenario_builder .with_start_id(*start_id) .with_last_id(*last_id) .with_nonce_from(*from) .with_txs_count(*count), } }}; } #[tokio::main] async fn main() -> Result<(), Box> { init_logger(); let cli = Cli::parse(); match &cli.command { CliCommand::Tx { chain, ws, unwatched, block_monitor, mortal, log_file, scenario, send_threshold, remark, tip, use_legacy_backend, } => match chain { ChainType::Fake => { unimplemented!() }, ChainType::Eth => { let mut scenario_builder = ScenarioBuilder::new() .with_rpc_uri(ws.to_string()) .with_chain_type(chain.clone()) .with_send_threshold(*send_threshold as usize) .with_block_monitoring(*block_monitor) .with_watched_txs(!unwatched) .with_installed_ctrlc_stop_hook(true) .with_legacy_backend(*use_legacy_backend) .with_tip(*tip); scenario_builder = populate_scenario_builder!(scenario_builder, scenario); if let Some(mortality) = mortal { scenario_builder = scenario_builder.with_mortality(*mortality); } if let Some(inner) = remark { scenario_builder = scenario_builder.with_remark_recipe(*inner); } let scenario_executor = scenario_builder.build().await; let _ = scenario_executor.execute().await; }, ChainType::Sub => { let mut scenario_builder = ScenarioBuilder::new() .with_rpc_uri(ws.to_string()) .with_chain_type(chain.clone()) .with_send_threshold(*send_threshold as usize) .with_block_monitoring(*block_monitor) .with_watched_txs(!unwatched) .with_installed_ctrlc_stop_hook(true) .with_legacy_backend(*use_legacy_backend) .with_tip(*tip); scenario_builder = populate_scenario_builder!(scenario_builder, scenario); if let Some(mortality) = mortal { scenario_builder = scenario_builder.with_mortality(*mortality); } if let Some(inner) = remark { scenario_builder = scenario_builder.with_remark_recipe(*inner); } let scenario_executor = scenario_builder.build().await; let _ = scenario_executor.execute().await; }, }, CliCommand::CheckNonce { chain, ws, account } => { match chain { ChainType::Fake => { panic!("check nonce not supported for fake chain"); }, ChainType::Eth => { let desc = if let Ok(id) = account.parse::() { AccountsDescription::Derived(id..id + 1) } else { AccountsDescription::Keyring(account.clone()) }; let sink = EthTransactionsSink::new_with_uri_with_accounts_description( ws, desc, generate_ecdsa_keypair, None, false, ) .await; let account = sink.get_from_account_id(account).ok_or("account shall be correct")?; let nonce = sink.check_account_nonce(account).await?; info!(target:STAT_TARGET, "{nonce:?}"); }, ChainType::Sub => { let desc = if let Ok(id) = account.parse::() { AccountsDescription::Derived(id..id + 1) } else { AccountsDescription::Keyring(account.clone()) }; let sink = BizinikiwTransactionsSink::new_with_uri_with_accounts_description( ws, desc, generate_sr25519_keypair, None, false, ) .await; let account = sink.get_from_account_id(account).ok_or("account shall be correct")?; let nonce = sink.check_account_nonce(account).await?; info!(target:STAT_TARGET, "{nonce:?}"); }, }; }, CliCommand::Metadata { ws } => { // Handle metadata command use codec::Decode; let api = pezkuwi_subxt::OnlineClient::::from_insecure_url(ws).await?; let runtime_apis = api.runtime_api().at_latest().await?; let raw_bytes: Vec = runtime_apis.call_raw("Metadata_metadata", None).await?; let (_, meta): (Compact, RuntimeMetadataPrefixed) = Decode::decode(&mut &raw_bytes[..]).map_err(|e| format!("Decode error: {e}"))?; println!("{meta:#?}"); }, CliCommand::BlockMonitor { chain, ws, display } => { match chain { ChainType::Sub => { let block_monitor = BlockMonitor::::new_with_options(ws, *display).await; async { loop { tokio::time::sleep(Duration::from_secs(10)).await } } .await; }, ChainType::Eth => { let block_monitor = BlockMonitor::::new_with_options(ws, *display).await; async { loop { tokio::time::sleep(Duration::from_secs(10)).await } } .await; }, ChainType::Fake => { unimplemented!() }, }; }, CliCommand::LoadLog { chain, log_file, show_graphs, out_csv_filename, .. } => match chain { ChainType::Sub => { let logs = Journal::>::load_logs( log_file, out_csv_filename, ); make_stats(logs.values().cloned(), *show_graphs); }, ChainType::Eth => { let logs = Journal::>::load_logs(log_file, out_csv_filename); make_stats(logs.values().cloned(), *show_graphs); }, ChainType::Fake => { unimplemented!() }, }, CliCommand::GenerateEndowedAccounts { chain, start_id, last_id, balance, out_file_name, chain_spec, } => { let accounts_description = AccountsDescription::Derived(*start_id..last_id + 1); let funded_accounts = match chain { ChainType::Sub => { let accounts = subxt_transaction::derive_accounts::( accounts_description.clone(), SENDER_SEED, generate_sr25519_keypair, ); accounts .values() .map(|keypair| { serde_json::json!(( ::AccountId::from( keypair.0.clone().public_key() ), balance, )) }) .collect::>() }, ChainType::Eth => { let accounts = subxt_transaction::derive_accounts::( accounts_description.clone(), SENDER_SEED, generate_ecdsa_keypair, ); accounts .values() .map(|keypair| { serde_json::json!(( "0x".to_string() + &hex::encode(keypair.0.clone().public_key()), balance, )) }) .collect::>() }, ChainType::Fake => Default::default(), }; if let Some(chain_spec) = chain_spec { let file = File::open(chain_spec)?; let reader = BufReader::new(file); let mut chain_spec: serde_json::Value = serde_json::from_reader(reader)?; if let Some(balances) = chain_spec["genesis"]["runtimeGenesis"]["patch"]["balances"] ["balances"] .as_array_mut() { balances.extend(funded_accounts); } else { return Err("Balances array not found in provided chain-spec".into()); } fs::write( out_file_name, serde_json::to_string_pretty(&chain_spec) .map_err(|e| format!("to pretty failed: {e}"))?, ) .map_err(|err| err.to_string())?; } else { let json_object = serde_json::json!({"balances":{"balances":funded_accounts}}); fs::write(out_file_name, serde_json::to_string_pretty(&json_object)?.as_bytes())?; } }, }; Ok(()) }