use blockchain::blocks::marketing::{MarketingTransaction, UnsignedMarketingTransaction}; use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path}; use blockchain::common::types::MARKETING_FEE; use blockchain::json; use blockchain::wallets::structures::Wallet; use blockchain::File; use blockchain::Utc; use blockchain::{create_dir_all, AsyncWriteExt}; // pad the coin to ensure 15 characters fn pad_to_width(input: &str, width: usize) -> String { let mut result = String::with_capacity(width); // Pre-allocate string with capacity let _ = std::fmt::write(&mut result, format_args!("{input: f64 { value as f64 / 100_000_000.0 } #[tokio::main] async fn main() { let campaign_data = prompt_visible("What is the id number of your campaign? ").await; let campaign: u64 = campaign_data .trim() .parse() .expect("Please enter a valid number"); let ad_type_input = prompt_visible("What type of campaign is this? banner, social, or text: ").await; let ad_type = pad_to_width(ad_type_input.trim(), 6); let keyword_input = prompt_visible("Enter a keyword, can be up to 40 characters: ").await; let keyword = pad_to_width(keyword_input.trim(), 40); let displayed_input = prompt_visible("Enter the location the ad was displayed, can be up to 100 characters: ") .await; let displayed = pad_to_width(displayed_input.trim(), 100); let impression_input = prompt_visible("How many times was the ad viewed? (0 to 255): ").await; let impression: u8 = impression_input .trim() .parse() .expect("Please enter a valid number"); let click_input = prompt_visible("How many times was the ad clicked? (0 to 255): ").await; let click: u8 = click_input .trim() .parse() .expect("Please enter a valid number"); let impression_value_data = prompt_visible("What is the value of each impression? (1.25): ").await; let impression_value: f32 = impression_value_data .trim() .parse() .expect("Please enter a valid number"); let impression_value = (impression_value * 100.0) as u16; let click_value_data = prompt_visible("What is the value of each click? (1.25): ").await; let click_value: f32 = click_value_data .trim() .parse() .expect("Please enter a valid number"); let click_value = (click_value * 100.0) as u16; let txfee_prompt = format!( "Please enter the amount for the fee: (e.g., 0.001, minimum fee {:.8})", display_fee(MARKETING_FEE) ); let txfee_string = prompt_visible(&format!("{txfee_prompt}: ")).await; let txfee_f32: f32 = txfee_string .trim() .parse() .expect("Please enter a valid value."); let txfee = ((txfee_f32 as f64) * (100000000_f64)).round() as u64; let wallet_path = prompt_wallet_path().await; let decryption_key = prompt_hidden_nonempty( "What is your wallet decryption key? ", "Wallet key cannot be empty. Please try again.", ) .await; let wallet = match Wallet::try_obtain_wallet(decryption_key, Some(&wallet_path)).await { Ok(wallet) => wallet, Err(err) => { eprintln!("Wallet decryption failed: {err}"); return; } }; let private_key = &wallet.saved.private_key; let address = &wallet.saved.short_address; // Set type and timestamp let txtype = 5; let timestamp = Utc::now().timestamp() as u32; // Validate wallets if !Wallet::short_address_validation(address.trim_matches('"')) { println!("creator wallet invalid: {}", &address); return; } let unsigned_marketing = UnsignedMarketingTransaction::new( txtype, timestamp, campaign, &ad_type, &keyword, &displayed, impression, click, impression_value, click_value, address.trim_matches('"'), txfee, ) .await; let marketing = match MarketingTransaction::new(unsigned_marketing, private_key).await { Ok(tx) => tx, Err(err) => { eprintln!("Failed to sign marketing transaction: {err}"); return; } }; let hashed_data = marketing.unsigned_marketing.hash().await; let signature = marketing.signature.clone(); // Add the signature and hash let output = json!({ "txtype": txtype, "timestamp": timestamp, "campaign": campaign, "ad_type": ad_type, "keyword": keyword, "displayed": displayed, "impression": impression, "click": click, "impression_value": impression_value, "click_value": click_value, "advertiser": address.trim_matches('"'), "txfee": txfee, "hash": hashed_data, "signature": signature }); let output_str = serde_json::to_string_pretty(&output).expect("Failed to serialize JSON"); // Define the directory path let dir_path = "./transactions"; // Create the directory if it doesn't exist if let Err(e) = create_dir_all(&dir_path).await { eprintln!("Failed to create directory: {e}"); return; } // Define the file path let file_path = format!("{dir_path}/{hashed_data}.json"); // Open the file for writing asynchronously let mut file = File::create(&file_path) .await .expect("Failed to create file"); // Write the JSON string to the file asynchronously if let Err(e) = file.write_all(output_str.as_bytes()).await { eprintln!("Failed to write file: {e}"); return; } println!("{output_str}"); }