fixed token name validation
This commit is contained in:
parent
bdca5ed1ff
commit
e044f15e76
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::burn::{BurnTransaction, UnsignedBurnTransaction};
|
use blockchain::blocks::burn::{BurnTransaction, UnsignedBurnTransaction};
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
|
|
||||||
|
|
@ -9,13 +10,6 @@ use blockchain::File;
|
||||||
use blockchain::Utc;
|
use blockchain::Utc;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
use blockchain::{create_dir_all, AsyncWriteExt};
|
||||||
|
|
||||||
// Pad the asset name so it matches the 15-byte on-chain asset format.
|
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
|
||||||
let mut result = String::with_capacity(width);
|
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_fee(value: u64) -> f64 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
// Fees are stored as atomic units and displayed as whole-coin decimals for the CLI.
|
// Fees are stored as atomic units and displayed as whole-coin decimals for the CLI.
|
||||||
value as f64 / 100_000_000.0
|
value as f64 / 100_000_000.0
|
||||||
|
|
@ -29,7 +23,10 @@ async fn main() {
|
||||||
|
|
||||||
// Burnable assets are tokens or NFTs, never the base coin for this transaction type.
|
// Burnable assets are tokens or NFTs, never the base coin for this transaction type.
|
||||||
let coin_name = prompt_visible("Please enter the token or NFT name you wish to burn: ").await;
|
let coin_name = prompt_visible("Please enter the token or NFT name you wish to burn: ").await;
|
||||||
let coin = pad_to_width(&coin_name.trim().to_lowercase(), 15);
|
let Some(coin) = padded_normalized_asset_input(coin_name.trim()) else {
|
||||||
|
println!("Burn asset must normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if coin.trim().to_lowercase() == block_extension_and_paths().1.trim().to_lowercase() {
|
if coin.trim().to_lowercase() == block_extension_and_paths().1.trim().to_lowercase() {
|
||||||
println!("Base coin cannot be burned with this transaction type.");
|
println!("Base coin cannot be burned with this transaction type.");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::issue_token::{IssueTokenTransaction, UnsignedIssueTokenTransaction};
|
use blockchain::blocks::issue_token::{IssueTokenTransaction, UnsignedIssueTokenTransaction};
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
use blockchain::common::types::ISSUE_TOKEN_FEE;
|
use blockchain::common::types::ISSUE_TOKEN_FEE;
|
||||||
|
|
||||||
|
|
@ -8,13 +9,6 @@ use blockchain::File;
|
||||||
use blockchain::Utc;
|
use blockchain::Utc;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
use blockchain::{create_dir_all, AsyncWriteExt};
|
||||||
|
|
||||||
// Pad the ticker so it matches the fixed-width 15-byte on-chain format.
|
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
|
||||||
let mut result = String::with_capacity(width);
|
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn display_fee(value: u64) -> f64 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
// Fees are stored as atomic units and displayed as whole-coin decimals for the CLI.
|
// Fees are stored as atomic units and displayed as whole-coin decimals for the CLI.
|
||||||
value as f64 / 100_000_000.0
|
value as f64 / 100_000_000.0
|
||||||
|
|
@ -29,7 +23,10 @@ async fn main() {
|
||||||
// The ticker is stored as a fixed-width 15-byte asset field.
|
// The ticker is stored as a fixed-width 15-byte asset field.
|
||||||
let ticker_name =
|
let ticker_name =
|
||||||
prompt_visible("Please enter the ticker / token name you wish to issue more of: ").await;
|
prompt_visible("Please enter the ticker / token name you wish to issue more of: ").await;
|
||||||
let ticker = pad_to_width(&ticker_name.trim().to_lowercase(), 15);
|
let Some(ticker) = padded_normalized_asset_input(ticker_name.trim()) else {
|
||||||
|
println!("Ticker must normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// Token amounts are entered as whole tokens and stored as atomic units.
|
// Token amounts are entered as whole tokens and stored as atomic units.
|
||||||
let number_string =
|
let number_string =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use blockchain::blocks::loans::UnsignedLoanContractTransaction;
|
use blockchain::blocks::loans::UnsignedLoanContractTransaction;
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
|
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
use blockchain::common::types::LENDER_FEE;
|
use blockchain::common::types::LENDER_FEE;
|
||||||
|
|
||||||
use blockchain::json;
|
use blockchain::json;
|
||||||
|
|
@ -9,11 +11,13 @@ use blockchain::File;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
use blockchain::{create_dir_all, AsyncWriteExt};
|
||||||
use blockchain::{Local, LocalResult, NaiveDate, NaiveTime, TimeZone};
|
use blockchain::{Local, LocalResult, NaiveDate, NaiveTime, TimeZone};
|
||||||
|
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
fn normalize_asset_or_base(input: &str) -> Option<String> {
|
||||||
// Asset names are fixed-width fields in the loan transaction bytes.
|
let base_coin = block_extension_and_paths().1;
|
||||||
let mut result = String::with_capacity(width);
|
if input.trim().eq_ignore_ascii_case(base_coin.trim()) {
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
Some(base_coin)
|
||||||
result
|
} else {
|
||||||
|
padded_normalized_asset_input(input.trim())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_fee(value: u64) -> f64 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
|
|
@ -63,8 +67,10 @@ async fn main() {
|
||||||
|
|
||||||
// Coin/token and collateral names are padded to fixed-width transaction fields.
|
// Coin/token and collateral names are padded to fixed-width transaction fields.
|
||||||
let loan_coin_name = prompt_visible("What coin or token will you be lending out? ").await;
|
let loan_coin_name = prompt_visible("What coin or token will you be lending out? ").await;
|
||||||
let loan_coin_name = loan_coin_name.trim().to_lowercase();
|
let Some(loan_coin) = normalize_asset_or_base(&loan_coin_name) else {
|
||||||
let loan_coin = pad_to_width(&loan_coin_name, 15);
|
println!("Loan asset must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let loan_amount_string = prompt_visible("How many coins or tokens will you be lending? ").await;
|
let loan_amount_string = prompt_visible("How many coins or tokens will you be lending? ").await;
|
||||||
let loan_amount_f64: f64 = loan_amount_string
|
let loan_amount_f64: f64 = loan_amount_string
|
||||||
|
|
@ -123,8 +129,10 @@ async fn main() {
|
||||||
let collateral_name =
|
let collateral_name =
|
||||||
prompt_visible("What collateral coin, token, or NFT will you be accepting for this loan? ")
|
prompt_visible("What collateral coin, token, or NFT will you be accepting for this loan? ")
|
||||||
.await;
|
.await;
|
||||||
let collateral_name = collateral_name.trim().to_lowercase();
|
let Some(collateral) = padded_normalized_asset_input(collateral_name.trim()) else {
|
||||||
let collateral = pad_to_width(&collateral_name, 15);
|
println!("Collateral asset must normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let collateral_amount_string = prompt_visible("How many collateral coins or tokens will you be accepting for this loan (enter 1 for NFT collateral)? ").await;
|
let collateral_amount_string = prompt_visible("How many collateral coins or tokens will you be accepting for this loan (enter 1 for NFT collateral)? ").await;
|
||||||
let collateral_amount_f64: f64 = collateral_amount_string
|
let collateral_amount_f64: f64 = collateral_amount_string
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::nft::{CreateNftTransaction, UnsignedCreateNftTransaction};
|
use blockchain::blocks::nft::{CreateNftTransaction, UnsignedCreateNftTransaction};
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
use blockchain::common::types::CREATE_NFT_FEE;
|
use blockchain::common::types::CREATE_NFT_FEE;
|
||||||
|
|
||||||
|
|
@ -31,7 +32,10 @@ async fn main() {
|
||||||
|
|
||||||
// get user input ticker
|
// get user input ticker
|
||||||
let nft_ticker_name = prompt_visible("Please enter the name for your NFT. This can be 3 to 15 characters and must be 100% unique. First come first serve: ").await;
|
let nft_ticker_name = prompt_visible("Please enter the name for your NFT. This can be 3 to 15 characters and must be 100% unique. First come first serve: ").await;
|
||||||
let nft_name = pad_to_width(nft_ticker_name.trim(), 15);
|
let Some(nft_name) = padded_normalized_asset_input(nft_ticker_name.trim()) else {
|
||||||
|
println!("NFT name must normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// get the padded CID string
|
// get the padded CID string
|
||||||
let ipfs_hash = prompt_visible("Enter your IPFS CID for your NFT. This will be stored in a fixed 100 character field, so shorter CIDs are padded with spaces: ").await;
|
let ipfs_hash = prompt_visible("Enter your IPFS CID for your NFT. This will be stored in a fixed 100 character field, so shorter CIDs are padded with spaces: ").await;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use blockchain::blocks::swap::UnsignedSwapTransaction;
|
use blockchain::blocks::swap::UnsignedSwapTransaction;
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
|
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
use blockchain::common::types::SWAP_FEE;
|
use blockchain::common::types::SWAP_FEE;
|
||||||
|
|
||||||
use blockchain::json;
|
use blockchain::json;
|
||||||
|
|
@ -10,11 +12,13 @@ use blockchain::File;
|
||||||
use blockchain::Utc;
|
use blockchain::Utc;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
use blockchain::{create_dir_all, AsyncWriteExt};
|
||||||
|
|
||||||
// pad the coin to ensure 15 characters
|
fn normalize_asset_or_base(input: &str) -> Option<String> {
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
let base_coin = block_extension_and_paths().1;
|
||||||
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
|
if input.trim().eq_ignore_ascii_case(base_coin.trim()) {
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
Some(base_coin)
|
||||||
result
|
} else {
|
||||||
|
padded_normalized_asset_input(input.trim())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_fee(value: u64) -> f64 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
|
|
@ -30,8 +34,10 @@ async fn main() {
|
||||||
// get user input tocken1
|
// get user input tocken1
|
||||||
let ticker1_name =
|
let ticker1_name =
|
||||||
prompt_visible("Please enter the coin or token you wish to send during the swap: ").await;
|
prompt_visible("Please enter the coin or token you wish to send during the swap: ").await;
|
||||||
let ticker1_name = ticker1_name.trim().to_lowercase();
|
let Some(ticker1) = normalize_asset_or_base(&ticker1_name) else {
|
||||||
let ticker1 = pad_to_width(&ticker1_name, 15);
|
println!("Swap asset must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let nft_series1_string = prompt_visible("Please enter the NFT series number for the asset you are sending, or 0 for coins/tokens/1of1 NFTs: ").await;
|
let nft_series1_string = prompt_visible("Please enter the NFT series number for the asset you are sending, or 0 for coins/tokens/1of1 NFTs: ").await;
|
||||||
let nft_series1: u32 = nft_series1_string
|
let nft_series1: u32 = nft_series1_string
|
||||||
|
|
@ -52,8 +58,10 @@ async fn main() {
|
||||||
let ticker2_name =
|
let ticker2_name =
|
||||||
prompt_visible("Please enter the coin or token you wish to receive during the swap: ")
|
prompt_visible("Please enter the coin or token you wish to receive during the swap: ")
|
||||||
.await;
|
.await;
|
||||||
let ticker2_name = ticker2_name.trim().to_lowercase();
|
let Some(ticker2) = normalize_asset_or_base(&ticker2_name) else {
|
||||||
let ticker2 = pad_to_width(&ticker2_name, 15);
|
println!("Swap asset must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let nft_series2_string = prompt_visible("Please enter the NFT series number for the asset you expect to receive, or 0 for coins/tokens/1of1 NFTs: ").await;
|
let nft_series2_string = prompt_visible("Please enter the NFT series number for the asset you expect to receive, or 0 for coins/tokens/1of1 NFTs: ").await;
|
||||||
let nft_series2: u32 = nft_series2_string
|
let nft_series2: u32 = nft_series2_string
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::token::{CreateTokenTransaction, UnsignedCreateTokenTransaction};
|
use blockchain::blocks::token::{CreateTokenTransaction, UnsignedCreateTokenTransaction};
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
use blockchain::common::types::CREATE_TOKEN_FEE;
|
use blockchain::common::types::CREATE_TOKEN_FEE;
|
||||||
|
|
||||||
|
|
@ -8,13 +9,6 @@ use blockchain::File;
|
||||||
use blockchain::Utc;
|
use blockchain::Utc;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
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 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
value as f64 / 100_000_000.0
|
value as f64 / 100_000_000.0
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +21,10 @@ async fn main() {
|
||||||
|
|
||||||
// get user input ticker
|
// get user input ticker
|
||||||
let ticker_name = prompt_visible("Please enter the ticker / token name you wish to create. This can be 3 to 15 characters and must be 100% unique. First come first serve: ").await;
|
let ticker_name = prompt_visible("Please enter the ticker / token name you wish to create. This can be 3 to 15 characters and must be 100% unique. First come first serve: ").await;
|
||||||
let ticker = pad_to_width(ticker_name.trim(), 15);
|
let Some(ticker) = padded_normalized_asset_input(ticker_name.trim()) else {
|
||||||
|
println!("Ticker must normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// get user input value
|
// get user input value
|
||||||
let number_string = prompt_visible(
|
let number_string = prompt_visible(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::transfer::{TransferTransaction, UnsignedTransferTransaction};
|
use blockchain::blocks::transfer::{TransferTransaction, UnsignedTransferTransaction};
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
||||||
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
use blockchain::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
|
|
||||||
|
|
@ -11,11 +12,13 @@ use blockchain::File;
|
||||||
use blockchain::Utc;
|
use blockchain::Utc;
|
||||||
use blockchain::{create_dir_all, AsyncWriteExt};
|
use blockchain::{create_dir_all, AsyncWriteExt};
|
||||||
|
|
||||||
// pad the coin to ensure 15 characters
|
fn normalize_asset_or_base(input: &str) -> Option<String> {
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
let base_coin = block_extension_and_paths().1;
|
||||||
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
|
if input.trim().eq_ignore_ascii_case(base_coin.trim()) {
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
Some(base_coin)
|
||||||
result
|
} else {
|
||||||
|
padded_normalized_asset_input(input.trim())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_fee(value: u64) -> f64 {
|
fn display_fee(value: u64) -> f64 {
|
||||||
|
|
@ -42,9 +45,11 @@ async fn main() {
|
||||||
} else {
|
} else {
|
||||||
prompt_visible("Please enter the coin or token you wish to transfer: ").await
|
prompt_visible("Please enter the coin or token you wish to transfer: ").await
|
||||||
};
|
};
|
||||||
let coin = pad_to_width(&coin_name, 15);
|
let Some(coin) = normalize_asset_or_base(&coin_name) else {
|
||||||
let is_base_coin =
|
println!("Transfer asset must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
coin.trim().to_lowercase() == block_extension_and_paths().1.trim().to_lowercase();
|
return;
|
||||||
|
};
|
||||||
|
let is_base_coin = coin.trim().eq_ignore_ascii_case(block_extension_and_paths().1.trim());
|
||||||
|
|
||||||
let value_string = if args.len() > 2 {
|
let value_string = if args.len() > 2 {
|
||||||
args[2].clone()
|
args[2].clone()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use blockchain::blocks::swap::UnsignedSwapTransaction;
|
use blockchain::blocks::swap::UnsignedSwapTransaction;
|
||||||
|
use blockchain::common::asset_names::padded_normalized_asset_input;
|
||||||
use blockchain::common::cli_prompts::{
|
use blockchain::common::cli_prompts::{
|
||||||
ask_yes_no_question, prompt_hidden_nonempty, prompt_wallet_path,
|
ask_yes_no_question, prompt_hidden_nonempty, prompt_wallet_path,
|
||||||
};
|
};
|
||||||
|
|
@ -12,11 +13,12 @@ use blockchain::standalone_tools::vanity_resolver::resolve_wallet_address_input;
|
||||||
use blockchain::wallets::structures::Wallet;
|
use blockchain::wallets::structures::Wallet;
|
||||||
use blockchain::Value;
|
use blockchain::Value;
|
||||||
|
|
||||||
// padd the coin to ensure 15 characters
|
fn normalize_asset_or_base(input: &str, base_coin: &str) -> Option<String> {
|
||||||
fn pad_to_width(input: &str, width: usize) -> String {
|
if input.trim().eq_ignore_ascii_case(base_coin.trim()) {
|
||||||
let mut result = String::with_capacity(width); // Pre-allocate string with capacity
|
Some(base_coin.to_string())
|
||||||
let _ = std::fmt::write(&mut result, format_args!("{input:<width$}"));
|
} else {
|
||||||
result
|
padded_normalized_asset_input(input.trim())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
@ -162,8 +164,14 @@ async fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let padded_token_name1 = pad_to_width(&token_name1, 15);
|
let Some(padded_token_name1) = normalize_asset_or_base(&token_name1, &network_coin) else {
|
||||||
let padded_token_name2 = pad_to_width(&token_name2, 15);
|
println!("Ticker1 must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(padded_token_name2) = normalize_asset_or_base(&token_name2, &network_coin) else {
|
||||||
|
println!("Ticker2 must be the base coin or normalize to 3-15 alphanumeric characters.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let unsigned_swap = UnsignedSwapTransaction::new(
|
let unsigned_swap = UnsignedSwapTransaction::new(
|
||||||
txtype,
|
txtype,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
use crate::common::types::COIN_LENGTH;
|
||||||
|
|
||||||
|
pub fn normalize_asset_identifier(input: &str) -> String {
|
||||||
|
input
|
||||||
|
.chars()
|
||||||
|
.filter(|character| character.is_ascii_alphanumeric())
|
||||||
|
.flat_map(char::to_lowercase)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn padded_normalized_asset_identifier(input: &str) -> Option<String> {
|
||||||
|
let normalized = normalize_asset_identifier(input);
|
||||||
|
if normalized.len() < 3 || normalized.len() > COIN_LENGTH {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(format!("{normalized:<COIN_LENGTH$}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn padded_normalized_asset_input(input: &str) -> Option<String> {
|
||||||
|
if !input
|
||||||
|
.chars()
|
||||||
|
.all(|character| character.is_ascii_alphanumeric() || character.is_ascii_whitespace())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
padded_normalized_asset_identifier(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_canonical_padded_asset_identifier(input: &str) -> bool {
|
||||||
|
padded_normalized_asset_identifier(input).is_some_and(|canonical| canonical == input)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_canonical_padded_asset_or_base(input: &str, base_coin: &str) -> bool {
|
||||||
|
input.trim().eq_ignore_ascii_case(base_coin.trim())
|
||||||
|
|| is_canonical_padded_asset_identifier(input)
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod binary_conversions;
|
pub mod binary_conversions;
|
||||||
|
pub mod asset_names;
|
||||||
pub mod check_genesis;
|
pub mod check_genesis;
|
||||||
pub mod cli_prompts;
|
pub mod cli_prompts;
|
||||||
pub mod network_paths_and_settings;
|
pub mod network_paths_and_settings;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::blocks::burn::BurnTransaction;
|
use crate::blocks::burn::BurnTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_identifier;
|
||||||
use crate::common::nft_assets::nft_asset_name;
|
use crate::common::nft_assets::nft_asset_name;
|
||||||
use crate::common::types::{BURN_FEE, COIN_LENGTH};
|
use crate::common::types::{BURN_FEE, COIN_LENGTH};
|
||||||
use crate::encode;
|
use crate::encode;
|
||||||
|
|
@ -51,6 +52,9 @@ impl BurnTransaction {
|
||||||
if self.unsigned_burn.coin.len() != COIN_LENGTH {
|
if self.unsigned_burn.coin.len() != COIN_LENGTH {
|
||||||
return Err("Coin length is invalid.".to_string());
|
return Err("Coin length is invalid.".to_string());
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_identifier(&self.unsigned_burn.coin) {
|
||||||
|
return Err("Burn asset must be a canonical alphanumeric asset id.".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// The base coin can never be destroyed through the burn path.
|
// The base coin can never be destroyed through the burn path.
|
||||||
if self.unsigned_burn.coin.trim().to_lowercase() == BASECOIN.trim().to_lowercase() {
|
if self.unsigned_burn.coin.trim().to_lowercase() == BASECOIN.trim().to_lowercase() {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::blocks::nft::CreateNftTransaction;
|
use crate::blocks::nft::CreateNftTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_identifier;
|
||||||
use crate::common::nft_assets::nft_asset_name;
|
use crate::common::nft_assets::nft_asset_name;
|
||||||
use crate::common::types::{COIN_LENGTH, CREATE_NFT_FEE};
|
use crate::common::types::{COIN_LENGTH, CREATE_NFT_FEE};
|
||||||
use crate::encode;
|
use crate::encode;
|
||||||
|
|
@ -27,6 +28,11 @@ impl CreateNftTransaction {
|
||||||
)
|
)
|
||||||
.map_err(|s| s.to_string())?;
|
.map_err(|s| s.to_string())?;
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_identifier(&self.unsigned_create_nft.nft_name) {
|
||||||
|
return Err(
|
||||||
|
"NFT name must normalize to 3-15 lowercase alphanumeric characters.".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if is_reserved_base_coin_ticker(&self.unsigned_create_nft.nft_name) {
|
if is_reserved_base_coin_ticker(&self.unsigned_create_nft.nft_name) {
|
||||||
return Err("NFT name is reserved for the base coin.".to_string());
|
return Err("NFT name is reserved for the base coin.".to_string());
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::blocks::token::CreateTokenTransaction;
|
use crate::blocks::token::CreateTokenTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_identifier;
|
||||||
use crate::common::types::{COIN_LENGTH, CREATE_TOKEN_FEE};
|
use crate::common::types::{COIN_LENGTH, CREATE_TOKEN_FEE};
|
||||||
use crate::encode;
|
use crate::encode;
|
||||||
use crate::records::memory::mempool::BASECOIN;
|
use crate::records::memory::mempool::BASECOIN;
|
||||||
|
|
@ -27,6 +28,11 @@ impl CreateTokenTransaction {
|
||||||
return Err("Ticker length must be 15 characters. Consider padding with empty spaces")
|
return Err("Ticker length must be 15 characters. Consider padding with empty spaces")
|
||||||
.map_err(|s| s.to_string())?;
|
.map_err(|s| s.to_string())?;
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_identifier(&self.unsigned_create_token.ticker) {
|
||||||
|
return Err(
|
||||||
|
"Ticker must normalize to 3-15 lowercase alphanumeric characters.".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if is_reserved_base_coin_ticker(&self.unsigned_create_token.ticker) {
|
if is_reserved_base_coin_ticker(&self.unsigned_create_token.ticker) {
|
||||||
return Err("Ticker is reserved for the base coin.".to_string());
|
return Err("Ticker is reserved for the base coin.".to_string());
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::blocks::issue_token::IssueTokenTransaction;
|
use crate::blocks::issue_token::IssueTokenTransaction;
|
||||||
use crate::blocks::token::CreateTokenTransaction;
|
use crate::blocks::token::CreateTokenTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_identifier;
|
||||||
use crate::common::network_paths_and_settings::block_extension_and_paths;
|
use crate::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
use crate::common::types::{COIN_LENGTH, ISSUE_TOKEN_FEE};
|
use crate::common::types::{COIN_LENGTH, ISSUE_TOKEN_FEE};
|
||||||
use crate::records::memory::mempool::BASECOIN;
|
use crate::records::memory::mempool::BASECOIN;
|
||||||
|
|
@ -88,6 +89,11 @@ impl IssueTokenTransaction {
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_identifier(ticker) {
|
||||||
|
return Err(
|
||||||
|
"Ticker must normalize to 3-15 lowercase alphanumeric characters.".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Check reserved keywords not used
|
// Check reserved keywords not used
|
||||||
if reserved_base_coins()
|
if reserved_base_coins()
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
use crate::blocks::loans::LoanContractTransaction;
|
use crate::blocks::loans::LoanContractTransaction;
|
||||||
|
use crate::common::asset_names::{
|
||||||
|
is_canonical_padded_asset_identifier, is_canonical_padded_asset_or_base,
|
||||||
|
};
|
||||||
use crate::common::types::{COIN_LENGTH, LENDER_FEE};
|
use crate::common::types::{COIN_LENGTH, LENDER_FEE};
|
||||||
use crate::encode;
|
use crate::encode;
|
||||||
use crate::records::memory::mempool::BASECOIN;
|
use crate::records::memory::mempool::BASECOIN;
|
||||||
|
|
@ -91,6 +94,14 @@ impl LoanContractTransaction {
|
||||||
)
|
)
|
||||||
.map_err(|s| s.to_string())?;
|
.map_err(|s| s.to_string())?;
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_or_base(&loan_coin, &BASECOIN)
|
||||||
|
|| !is_canonical_padded_asset_identifier(&collateral)
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"Loan assets must use the base coin or canonical alphanumeric asset ids."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Loaned assets must already exist as either the network base coin
|
// Loaned assets must already exist as either the network base coin
|
||||||
// or a previously created token.
|
// or a previously created token.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::blocks::swap::SwapTransaction;
|
use crate::blocks::swap::SwapTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_or_base;
|
||||||
use crate::common::network_paths_and_settings::block_extension_and_paths;
|
use crate::common::network_paths_and_settings::block_extension_and_paths;
|
||||||
use crate::common::nft_assets::nft_asset_name;
|
use crate::common::nft_assets::nft_asset_name;
|
||||||
use crate::common::types::{COIN_LENGTH, SWAP_FEE};
|
use crate::common::types::{COIN_LENGTH, SWAP_FEE};
|
||||||
|
|
@ -75,6 +76,18 @@ impl SwapTransaction {
|
||||||
"Ticker lengths are invalid. Consider padding with empty spaces".to_string(),
|
"Ticker lengths are invalid. Consider padding with empty spaces".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
let network_base_coin_padded = block_extension_and_paths().1;
|
||||||
|
if !is_canonical_padded_asset_or_base(&self.unsigned_swap.ticker1, &network_base_coin_padded)
|
||||||
|
|| !is_canonical_padded_asset_or_base(
|
||||||
|
&self.unsigned_swap.ticker2,
|
||||||
|
&network_base_coin_padded,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return Err(
|
||||||
|
"Swap assets must be the base coin or canonical alphanumeric asset ids."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Build concrete asset names for NFT series swaps before checking
|
// Build concrete asset names for NFT series swaps before checking
|
||||||
// balances and registry existence.
|
// balances and registry existence.
|
||||||
|
|
@ -83,7 +96,7 @@ impl SwapTransaction {
|
||||||
|
|
||||||
// The base coin name comes from the active network settings so
|
// The base coin name comes from the active network settings so
|
||||||
// mainnet and testnet validate against their own ticker.
|
// mainnet and testnet validate against their own ticker.
|
||||||
let network_base_coin = block_extension_and_paths().1.trim().to_lowercase();
|
let network_base_coin = network_base_coin_padded.trim().to_lowercase();
|
||||||
|
|
||||||
// Numbered NFT swaps are single-item transfers, while non-series
|
// Numbered NFT swaps are single-item transfers, while non-series
|
||||||
// assets must already exist as a token, NFT, or base coin.
|
// assets must already exist as a token, NFT, or base coin.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::blocks::transfer::TransferTransaction;
|
use crate::blocks::transfer::TransferTransaction;
|
||||||
|
use crate::common::asset_names::is_canonical_padded_asset_or_base;
|
||||||
use crate::common::nft_assets::nft_asset_name;
|
use crate::common::nft_assets::nft_asset_name;
|
||||||
use crate::common::types::{minimum_transfer_fee, COIN_LENGTH};
|
use crate::common::types::{minimum_transfer_fee, COIN_LENGTH};
|
||||||
use crate::encode;
|
use crate::encode;
|
||||||
|
|
@ -138,6 +139,12 @@ impl TransferTransaction {
|
||||||
if self.unsigned_transfer.coin.len() != COIN_LENGTH {
|
if self.unsigned_transfer.coin.len() != COIN_LENGTH {
|
||||||
return Err("Coin length is invalid.".to_string());
|
return Err("Coin length is invalid.".to_string());
|
||||||
}
|
}
|
||||||
|
if !is_canonical_padded_asset_or_base(&self.unsigned_transfer.coin, &BASECOIN) {
|
||||||
|
return Err(
|
||||||
|
"Transfer asset must be the base coin or a canonical alphanumeric asset id."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Saved-chain duplicates are rejected by txid even if the mempool
|
// Saved-chain duplicates are rejected by txid even if the mempool
|
||||||
// did not already contain the transaction.
|
// did not already contain the transaction.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue