Files
pezkuwi-sdk/bizinikiwi/utils/txtesttool/bin/main.rs
T
pezkuwichain b6d35f6faf chore: add Dijital Kurdistan Tech Institute to copyright headers
Updated 4763 files with dual copyright:
- Parity Technologies (UK) Ltd.
- Dijital Kurdistan Tech Institute
2025-12-27 21:28:36 +03:00

294 lines
8.4 KiB
Rust

// 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<dyn std::error::Error>> {
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::<u32>() {
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::<u32>() {
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::<EthRuntimeConfig>::from_insecure_url(ws).await?;
let runtime_apis = api.runtime_api().at_latest().await?;
let raw_bytes: Vec<u8> = runtime_apis.call_raw("Metadata_metadata", None).await?;
let (_, meta): (Compact<u32>, 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::<PezkuwiConfig>::new_with_options(ws, *display).await;
async {
loop {
tokio::time::sleep(Duration::from_secs(10)).await
}
}
.await;
},
ChainType::Eth => {
let block_monitor =
BlockMonitor::<EthRuntimeConfig>::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::<DefaultTxTask<BizinikiwTransaction>>::load_logs(
log_file,
out_csv_filename,
);
make_stats(logs.values().cloned(), *show_graphs);
},
ChainType::Eth => {
let logs =
Journal::<DefaultTxTask<EthTransaction>>::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::<PezkuwiConfig, _, _>(
accounts_description.clone(),
SENDER_SEED,
generate_sr25519_keypair,
);
accounts
.values()
.map(|keypair| {
serde_json::json!((
<PezkuwiConfig as pezkuwi_subxt::Config>::AccountId::from(
keypair.0.clone().public_key()
),
balance,
))
})
.collect::<Vec<_>>()
},
ChainType::Eth => {
let accounts = subxt_transaction::derive_accounts::<EthRuntimeConfig, _, _>(
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::<Vec<_>>()
},
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(())
}