From 1917b9c121358c41929a5957162d6f60d9b03d31 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Fri, 18 Aug 2023 08:31:11 +0400 Subject: [PATCH] feat: utility to create and read batch transactions --- Cargo.lock | 23 +++--- Cargo.toml | 2 +- .../implementation/manager.rs | 10 +-- dan_layer/template_lib/src/crypto.rs | 6 ++ utilities/transaction_generator/Cargo.toml | 20 +++++ utilities/transaction_generator/src/cli.rs | 41 ++++++++++ utilities/transaction_generator/src/lib.rs | 8 ++ utilities/transaction_generator/src/main.rs | 66 +++++++++++++++ .../src/transaction_reader.rs | 31 +++++++ .../src/transaction_writer.rs | 81 +++++++++++++++++++ 10 files changed, 266 insertions(+), 22 deletions(-) create mode 100644 utilities/transaction_generator/Cargo.toml create mode 100644 utilities/transaction_generator/src/cli.rs create mode 100644 utilities/transaction_generator/src/lib.rs create mode 100644 utilities/transaction_generator/src/main.rs create mode 100644 utilities/transaction_generator/src/transaction_reader.rs create mode 100644 utilities/transaction_generator/src/transaction_writer.rs diff --git a/Cargo.lock b/Cargo.lock index ba5fb48b62..7dad345060 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,9 +253,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arc-swap" @@ -1469,24 +1469,23 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.3" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" +checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" dependencies = [ "clap_builder", - "clap_derive 4.3.2", + "clap_derive 4.3.12", "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.3" +version = "4.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" +checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" dependencies = [ "anstream", "anstyle", - "bitflags 1.3.2", "clap_lex 0.5.0", "strsim 0.10.0", "terminal_size", @@ -1507,9 +1506,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck 0.4.1", "proc-macro2", @@ -2050,7 +2049,7 @@ dependencies = [ "anyhow", "async-trait", "atty", - "clap 4.3.3", + "clap 4.3.21", "console", "cucumber-codegen", "cucumber-expressions", @@ -8100,7 +8099,7 @@ dependencies = [ "axum", "axum-jrpc", "chrono", - "clap 4.3.3", + "clap 4.3.21", "dirs", "jsonwebtoken", "log", diff --git a/Cargo.toml b/Cargo.toml index 840f0c1725..cf238ece12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ members = [ "dan_layer/validator_node_rpc", "dan_layer/wallet/sdk", "dan_layer/wallet/storage_sqlite", - "integration_tests" + "integration_tests", ] # resolver = "2" diff --git a/applications/tari_dan_app_utilities/src/template_manager/implementation/manager.rs b/applications/tari_dan_app_utilities/src/template_manager/implementation/manager.rs index 18270e4124..a8aa695167 100644 --- a/applications/tari_dan_app_utilities/src/template_manager/implementation/manager.rs +++ b/applications/tari_dan_app_utilities/src/template_manager/implementation/manager.rs @@ -27,7 +27,6 @@ use std::{ }; use chrono::Utc; -use lazy_static::lazy_static; use log::*; use tari_core::transactions::transaction_components::TemplateType; use tari_dan_common_types::{optional::Optional, services::template_provider::TemplateProvider}; @@ -40,7 +39,7 @@ use tari_dan_engine::{ use tari_dan_storage::global::{DbTemplate, DbTemplateType, DbTemplateUpdate, GlobalDb, TemplateStatus}; use tari_dan_storage_sqlite::global::SqliteGlobalDbAdapter; use tari_engine_types::calculate_template_binary_hash; -use tari_template_builtin::get_template_builtin; +use tari_template_builtin::{get_template_builtin, ACCOUNT_NFT_TEMPLATE_ADDRESS, ACCOUNT_TEMPLATE_ADDRESS}; use tari_template_lib::models::TemplateAddress; use super::TemplateConfig; @@ -54,13 +53,6 @@ use crate::template_manager::interface::{ const LOG_TARGET: &str = "tari::validator_node::template_manager"; -lazy_static! { - pub static ref ACCOUNT_TEMPLATE_ADDRESS: TemplateAddress = TemplateAddress::from_array([0; 32]); - pub static ref ACCOUNT_NFT_TEMPLATE_ADDRESS: TemplateAddress = TemplateAddress::from_array([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 - ]); -} - #[derive(Debug, Clone)] pub struct TemplateManager { global_db: GlobalDb, diff --git a/dan_layer/template_lib/src/crypto.rs b/dan_layer/template_lib/src/crypto.rs index 015755f84a..877058245a 100644 --- a/dan_layer/template_lib/src/crypto.rs +++ b/dan_layer/template_lib/src/crypto.rs @@ -63,6 +63,12 @@ impl AsRef<[u8]> for RistrettoPublicKeyBytes { } } +impl From<[u8; 32]> for RistrettoPublicKeyBytes { + fn from(bytes: [u8; 32]) -> Self { + Self(bytes) + } +} + #[derive(Debug, PartialEq, Eq)] pub struct InvalidByteLengthError { size: usize, diff --git a/utilities/transaction_generator/Cargo.toml b/utilities/transaction_generator/Cargo.toml new file mode 100644 index 0000000000..d0619b0852 --- /dev/null +++ b/utilities/transaction_generator/Cargo.toml @@ -0,0 +1,20 @@ +# Exclude deps from workspace +[workspace] + +[package] +name = "transaction_generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +tari_template_lib = { path = "../../dan_layer/template_lib" } +tari_transaction = { path = "../../dan_layer/transaction" } +tari_engine_types = { path = "../../dan_layer/engine_types" } +tari_template_builtin = { path = "../../dan_layer/template_builtin" } +tari_crypto = "0.17" + +anyhow = "1.0.72" +bincode = { version = "2.0.0-rc.3", features = ["serde"] } +bytes = "0.4" +clap = { version = "4.3.21", features = ["derive"] } +rayon = "1.7.0" \ No newline at end of file diff --git a/utilities/transaction_generator/src/cli.rs b/utilities/transaction_generator/src/cli.rs new file mode 100644 index 0000000000..1aec5e798c --- /dev/null +++ b/utilities/transaction_generator/src/cli.rs @@ -0,0 +1,41 @@ +// Copyright 2023 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::path::PathBuf; + +use clap::{Args, Parser, Subcommand}; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +pub struct Cli { + #[clap(subcommand)] + pub sub_command: SubCommand, +} + +impl Cli { + pub fn init() -> Self { + Self::parse() + } +} + +#[derive(Subcommand, Debug)] +pub enum SubCommand { + Write(WriteArgs), + Read(ReadArgs), +} + +#[derive(Args, Debug)] +pub struct WriteArgs { + #[clap(long, short = 'n')] + pub num_transactions: u64, + #[clap(long, short = 'o')] + pub output_file: PathBuf, + #[clap(long)] + pub overwrite: bool, +} +#[derive(Args, Debug)] +pub struct ReadArgs { + #[clap(long, short = 'f')] + pub input_file: PathBuf, +} diff --git a/utilities/transaction_generator/src/lib.rs b/utilities/transaction_generator/src/lib.rs new file mode 100644 index 0000000000..bf152a44b3 --- /dev/null +++ b/utilities/transaction_generator/src/lib.rs @@ -0,0 +1,8 @@ +// Copyright 2023 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +mod transaction_reader; +mod transaction_writer; + +pub use transaction_reader::*; +pub use transaction_writer::*; diff --git a/utilities/transaction_generator/src/main.rs b/utilities/transaction_generator/src/main.rs new file mode 100644 index 0000000000..19a7d65309 --- /dev/null +++ b/utilities/transaction_generator/src/main.rs @@ -0,0 +1,66 @@ +// Copyright 2023 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +mod cli; +mod transaction_writer; + +use std::io::{stdout, Seek, SeekFrom, Write}; + +use cli::Cli; +use tari_crypto::ristretto::RistrettoSecretKey; +use tari_template_lib::models::Amount; +use transaction_generator::{read_number_of_transactions, read_transactions}; + +use crate::{cli::SubCommand, transaction_writer::write_transactions}; + +fn main() -> anyhow::Result<()> { + let cli = Cli::init(); + match cli.sub_command { + SubCommand::Write(args) => { + let fee_amount = Amount(1000); + let signer_private_key = RistrettoSecretKey::default(); + + if !args.overwrite && args.output_file.exists() { + anyhow::bail!("Output file {} already exists", args.output_file.display()); + } + + let timer = std::time::Instant::now(); + println!("Generating and writing {} transactions", args.num_transactions,); + + let mut file = std::fs::File::create(&args.output_file)?; + write_transactions( + args.num_transactions, + signer_private_key, + fee_amount, + &|_| { + print!("."); + stdout().flush().unwrap() + }, + &mut file, + )?; + println!(); + let size = file.metadata()?.len() / 1024 / 1024; + println!( + "Wrote {} transactions to {} ({} MiB) in {:.2?}", + args.num_transactions, + args.output_file.display(), + size, + timer.elapsed() + ); + }, + SubCommand::Read(args) => { + let mut file = std::fs::File::open(args.input_file)?; + + let num_transactions = read_number_of_transactions(&mut file)?; + println!("Number of transactions: {}", num_transactions); + file.seek(SeekFrom::Start(0))?; + let receiver = read_transactions(file)?; + + while let Ok(transaction) = receiver.recv() { + println!("Read transaction: {}", transaction.id()); + } + }, + } + + Ok(()) +} diff --git a/utilities/transaction_generator/src/transaction_reader.rs b/utilities/transaction_generator/src/transaction_reader.rs new file mode 100644 index 0000000000..d8f918377f --- /dev/null +++ b/utilities/transaction_generator/src/transaction_reader.rs @@ -0,0 +1,31 @@ +// Copyright 2023 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{io::Read, sync::mpsc, thread}; + +use tari_transaction::Transaction; + +pub fn read_transactions(mut reader: R) -> anyhow::Result> { + let (sender, receiver) = mpsc::sync_channel(1000); + thread::spawn(move || { + let mut remaining = read_number_of_transactions(&mut reader).unwrap(); + + while remaining > 0 { + let mut len_bytes = [0u8; 4]; + reader.read_exact(&mut len_bytes).unwrap(); + let len = u32::from_le_bytes(len_bytes) as u64; + let mut limited_reader = (&mut reader).take(len); + let transaction: Transaction = + bincode::serde::decode_from_std_read(&mut limited_reader, bincode::config::standard()).unwrap(); + sender.send(transaction).unwrap(); + remaining -= 1; + } + }); + Ok(receiver) +} + +pub fn read_number_of_transactions(reader: &mut R) -> anyhow::Result { + let mut len_bytes = [0u8; 8]; + reader.read_exact(&mut len_bytes).unwrap(); + Ok(u64::from_le_bytes(len_bytes)) +} diff --git a/utilities/transaction_generator/src/transaction_writer.rs b/utilities/transaction_generator/src/transaction_writer.rs new file mode 100644 index 0000000000..5f3638d188 --- /dev/null +++ b/utilities/transaction_generator/src/transaction_writer.rs @@ -0,0 +1,81 @@ +// Copyright 2023 The Tari Project +// SPDX-License-Identifier: BSD-3-Clause + +use std::{io::Write, sync::mpsc, thread}; + +use bytes::{BufMut, Bytes, BytesMut}; +use rayon::iter::{ParallelBridge, ParallelIterator}; +use tari_crypto::ristretto::RistrettoSecretKey; +use tari_engine_types::{component::new_component_address_from_parts, instruction::Instruction}; +use tari_template_builtin::ACCOUNT_TEMPLATE_ADDRESS; +use tari_template_lib::{ + args, + models::{Amount, NonFungibleAddress}, +}; +use tari_transaction::Transaction; + +pub fn write_transactions( + num_transactions: u64, + signer_private_key: RistrettoSecretKey, + fee_amount: Amount, + on_progress: &dyn Fn(usize), + writer: &mut W, +) -> anyhow::Result<()> { + let (sender, receiver) = mpsc::sync_channel(1000); + + thread::spawn(move || { + (0..num_transactions).par_bridge().for_each_with(sender, |sender, n| { + let mut owner_pk = [0u8; 32]; + owner_pk[24..].copy_from_slice(&n.to_le_bytes()); + let owner_token = NonFungibleAddress::from_public_key(owner_pk.into()); + + let transaction = Transaction::builder() + .with_fee_instructions(vec![ + Instruction::CreateFreeTestCoins { + revealed_amount: Amount::new(1000), + output: None, + }, + Instruction::PutLastInstructionOutputOnWorkspace { + key: b"free_coins".to_vec(), + }, + Instruction::CallFunction { + template_address: *ACCOUNT_TEMPLATE_ADDRESS, + function: "create_with_bucket".to_string(), + args: args![owner_token, Workspace("free_coins")], + }, + Instruction::CallMethod { + component_address: new_component_address_from_parts( + &ACCOUNT_TEMPLATE_ADDRESS, + &owner_pk.into(), + ), + method: "pay_fee".to_string(), + args: args![fee_amount], + }, + ]) + .sign(&signer_private_key) + .build(); + + let buf = bincode::serde::encode_to_vec(&transaction, bincode::config::standard()).unwrap(); + let buf = Bytes::from(buf); + let output = BytesMut::with_capacity(buf.len() + 4); + let len = (u32::try_from(buf.len()).unwrap()).to_le_bytes(); + let mut writer = output.writer(); + writer.write_all(&len).unwrap(); + writer.write_all(&buf).unwrap(); + sender.send(writer.into_inner().freeze()).unwrap(); + }); + }); + + let len_bytes = num_transactions.to_le_bytes(); + bincode::serde::encode_into_std_write(len_bytes, writer, bincode::config::standard()).unwrap(); + let mut count = 0; + while let Ok(buf) = receiver.recv() { + writer.write_all(&buf)?; + count += 1; + if count % 10000 == 0 { + on_progress(count); + } + } + + Ok(()) +}