180 lines
5.6 KiB
Rust
180 lines
5.6 KiB
Rust
|
|
use blockchain::blocks::marketing::{MarketingTransaction, UnsignedMarketingTransaction};
|
||
|
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible};
|
||
|
|
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:<width$}"),
|
||
|
|
);
|
||
|
|
result
|
||
|
|
}
|
||
|
|
|
||
|
|
fn display_fee(value: u64) -> 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 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, None).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!("transaction: {output_str}");
|
||
|
|
}
|