drk: wallet command use subcommand structure instead of flags

This commit is contained in:
skoupidi
2025-06-19 16:57:51 +03:00
parent 590eec6f9c
commit 5236c4516d
12 changed files with 531 additions and 582 deletions

View File

@@ -128,38 +128,34 @@ pub fn generate_completions(shell: &str) -> Result<()> {
.arg(shell_arg);
// Wallet
let initialize =
Arg::with_name("initialize").long("initialize").help("Initialize wallet database");
let initialize = SubCommand::with_name("initialize").about("Initialize wallet database");
let keygen =
Arg::with_name("keygen").long("keygen").help("Generate a new keypair in the wallet");
let keygen = SubCommand::with_name("keygen").about("Generate a new keypair in the wallet");
let balance =
Arg::with_name("balance").long("balance").help("Query the wallet for known balances");
let balance = SubCommand::with_name("balance").about("Query the wallet for known balances");
let address =
Arg::with_name("address").long("address").help("Get the default address in the wallet");
let address = SubCommand::with_name("address").about("Get the default address in the wallet");
let addresses =
Arg::with_name("addresses").long("addresses").help("Print all the addresses in the wallet");
SubCommand::with_name("addresses").about("Print all the addresses in the wallet");
let default_address = Arg::with_name("default-address")
.long("default-address")
.takes_value(true)
.help("Set the default address in the wallet");
let index = Arg::with_name("index").help("Identifier of the address");
let default_address = SubCommand::with_name("default-address")
.about("Set the default address in the wallet")
.arg(index);
let secrets =
Arg::with_name("secrets").long("secrets").help("Print all the secret keys from the wallet");
SubCommand::with_name("secrets").about("Print all the secret keys from the wallet");
let import_secrets = Arg::with_name("import-secrets")
.long("import-secrets")
.help("Import secret keys from stdin into the wallet, separated by newlines");
let import_secrets = SubCommand::with_name("import-secrets")
.about("Import secret keys from stdin into the wallet, separated by newlines");
let tree = Arg::with_name("tree").long("tree").help("Print the Merkle tree in the wallet");
let tree = SubCommand::with_name("tree").about("Print the Merkle tree in the wallet");
let coins = Arg::with_name("coins").long("coins").help("Print all the coins in the wallet");
let coins = SubCommand::with_name("coins").about("Print all the coins in the wallet");
let wallet = SubCommand::with_name("wallet").about("Wallet operations").args(&vec![
let wallet = SubCommand::with_name("wallet").about("Wallet operations").subcommands(vec![
initialize,
keygen,
balance,

View File

@@ -111,16 +111,16 @@ fn completion(buf: &str, lc: &mut Vec<String>) {
if buf.starts_with("w") {
lc.push("wallet".to_string());
lc.push("wallet --initialize".to_string());
lc.push("wallet --keygen".to_string());
lc.push("wallet --balance".to_string());
lc.push("wallet --address".to_string());
lc.push("wallet --addresses".to_string());
lc.push("wallet --default-address".to_string());
lc.push("wallet --secrets".to_string());
lc.push("wallet --import-secrets".to_string());
lc.push("wallet --tree".to_string());
lc.push("wallet --coins".to_string());
lc.push("wallet initialize".to_string());
lc.push("wallet keygen".to_string());
lc.push("wallet balance".to_string());
lc.push("wallet address".to_string());
lc.push("wallet addresses".to_string());
lc.push("wallet default-address".to_string());
lc.push("wallet secrets".to_string());
lc.push("wallet import-secrets".to_string());
lc.push("wallet tree".to_string());
lc.push("wallet coins".to_string());
return
}
@@ -210,10 +210,8 @@ fn completion(buf: &str, lc: &mut Vec<String>) {
fn hints(buf: &str) -> Option<(String, i32, bool)> {
match buf {
"completions " => Some(("<shell>".to_string(), 35, false)), // 35 = magenta
"wallet " => Some(("--(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
"wallet -" => Some(("-(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
"wallet --" => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
"wallet --default-address " => Some(("<address_id>".to_string(), 35, false)),
"wallet " => Some(("(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)".to_string(), 35, false)),
"wallet default-address " => Some(("<index>".to_string(), 35, false)),
"unspend " => Some(("<coin>".to_string(), 35, false)),
"transfer " => Some(("[--half-split] <amount> <token> <recipient> [spend_hook] [user_data]".to_string(), 35, false)),
"otc " => Some(("(init|join|inspect|sign)".to_string(), 35, false)),
@@ -445,279 +443,284 @@ fn handle_completions(parts: &[&str]) {
/// Auxiliary function to define the wallet command handling.
async fn handle_wallet(drk: &DrkPtr, parts: &[&str]) {
// Check correct command structure
if parts.len() != 2 && parts.len() != 3 {
if parts.len() < 2 {
println!("Malformed `wallet` command");
println!("Usage: wallet --(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
return
}
// Handle command flag
if parts[1] == "--initialize" {
let lock = drk.read().await;
if let Err(e) = lock.initialize_wallet().await {
println!("Error initializing wallet: {e:?}");
// Handle subcommand
match parts[1] {
"initialize" => handle_wallet_initialize(drk).await,
"keygen" => handle_wallet_keygen(drk).await,
"balance" => handle_wallet_balance(drk).await,
"address" => handle_wallet_address(drk).await,
"addresses" => handle_wallet_addresses(drk).await,
"default-address" => handle_wallet_default_address(drk, parts).await,
"secrets" => handle_wallet_secrets(drk).await,
"import-secrets" => handle_wallet_import_secrets(drk).await,
"tree" => handle_wallet_tree(drk).await,
"coins" => handle_wallet_coins(drk).await,
_ => {
println!("Unreconized wallet subcommand: {}", parts[1]);
println!("Usage: wallet (initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
}
}
}
/// Auxiliary function to define the wallet initialize subcommand handling.
async fn handle_wallet_initialize(drk: &DrkPtr) {
let lock = drk.read().await;
if let Err(e) = lock.initialize_wallet().await {
println!("Error initializing wallet: {e:?}");
return
}
if let Err(e) = lock.initialize_money().await {
println!("Failed to initialize Money: {e:?}");
return
}
if let Err(e) = lock.initialize_dao().await {
println!("Failed to initialize DAO: {e:?}");
return
}
if let Err(e) = lock.initialize_deployooor() {
println!("Failed to initialize Deployooor: {e:?}");
}
}
/// Auxiliary function to define the wallet keygen subcommand handling.
async fn handle_wallet_keygen(drk: &DrkPtr) {
if let Err(e) = drk.read().await.money_keygen().await {
println!("Failed to generate keypair: {e:?}");
}
}
/// Auxiliary function to define the wallet balance subcommand handling.
async fn handle_wallet_balance(drk: &DrkPtr) {
let lock = drk.read().await;
let balmap = match lock.money_balance().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch balances map: {e:?}");
return
}
if let Err(e) = lock.initialize_money().await {
println!("Failed to initialize Money: {e:?}");
};
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
return
}
if let Err(e) = lock.initialize_dao().await {
println!("Failed to initialize DAO: {e:?}");
return
}
if let Err(e) = lock.initialize_deployooor() {
println!("Failed to initialize Deployooor: {e:?}");
}
return
}
};
if parts[1] == "--keygen" {
if let Err(e) = drk.read().await.money_keygen().await {
println!("Failed to generate keypair: {e:?}");
}
return
}
if parts[1] == "--balance" {
let lock = drk.read().await;
let balmap = match lock.money_balance().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch balances map: {e:?}");
return
}
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Token ID", "Aliases", "Balance"]);
for (token_id, balance) in balmap.iter() {
let aliases = match aliases_map.get(token_id) {
Some(a) => a,
None => "-",
};
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
return
}
};
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Token ID", "Aliases", "Balance"]);
for (token_id, balance) in balmap.iter() {
let aliases = match aliases_map.get(token_id) {
Some(a) => a,
None => "-",
};
table.add_row(row![
token_id,
aliases,
encode_base10(*balance, BALANCE_BASE10_DECIMALS)
]);
}
if table.is_empty() {
println!("No unspent balances found");
} else {
println!("{table}");
}
return
table.add_row(row![token_id, aliases, encode_base10(*balance, BALANCE_BASE10_DECIMALS)]);
}
if parts[1] == "--address" {
match drk.read().await.default_address().await {
Ok(address) => println!("{address}"),
Err(e) => println!("Failed to fetch default address: {e:?}"),
}
return
}
if parts[1] == "--addresses" {
let addresses = match drk.read().await.addresses().await {
Ok(a) => a,
Err(e) => {
println!("Failed to fetch addresses: {e:?}");
return
}
};
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
for (key_id, public_key, secret_key, is_default) in addresses {
let is_default = match is_default {
1 => "*",
_ => "",
};
table.add_row(row![key_id, public_key, secret_key, is_default]);
}
if table.is_empty() {
println!("No addresses found");
} else {
println!("{table}");
}
return
}
if parts[1] == "--default-address" {
if parts.len() != 3 {
println!("Malformed `wallet` command");
println!("Usage: wallet --default-address <address_id>");
return
}
let idx = match usize::from_str(parts[2]) {
Ok(i) => i,
Err(e) => {
println!("Invalid address id: {e:?}");
return
}
};
if let Err(e) = drk.read().await.set_default_address(idx) {
println!("Failed to set default address: {e:?}");
}
return
}
if parts[1] == "--secrets" {
match drk.read().await.get_money_secrets().await {
Ok(secrets) => {
for secret in secrets {
println!("{secret}");
}
}
Err(e) => println!("Failed to fetch secrets: {e:?}"),
}
return
}
if parts[1] == "--import-secrets" {
let mut secrets = vec![];
// TODO: read from a file here not stdin
let lines = stdin().lines();
for (i, line) in lines.enumerate() {
if let Ok(line) = line {
let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
println!("Warning: Failed to decode secret on line {i}");
continue
};
let Ok(secret) = deserialize_async(&bytes).await else {
println!("Warning: Failed to deserialize secret on line {i}");
continue
};
secrets.push(secret);
}
}
match drk.read().await.import_money_secrets(secrets).await {
Ok(pubkeys) => {
for key in pubkeys {
println!("{key}");
}
}
Err(e) => println!("Failed to import secrets: {e:?}"),
}
return
}
if parts[1] == "--tree" {
// TODO: write to a file here not stdout
match drk.read().await.get_money_tree().await {
Ok(tree) => println!("{tree:#?}"),
Err(e) => println!("Failed to fetch tree: {e:?}"),
}
return
}
if parts[1] == "--coins" {
let lock = drk.read().await;
let coins = match lock.get_coins(true).await {
Ok(c) => c,
Err(e) => {
println!("Failed to fetch coins: {e:?}");
return
}
};
if coins.is_empty() {
return
}
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
return
}
};
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row![
"Coin",
"Token ID",
"Aliases",
"Value",
"Spend Hook",
"User Data",
"Creation Height",
"Spent",
"Spent Height",
"Spent TX"
]);
for coin in coins {
let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
Some(a) => a,
None => "-",
};
let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
format!("{}", coin.0.note.spend_hook)
} else {
String::from("-")
};
let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
bs58::encode(&serialize_async(&coin.0.note.user_data).await)
.into_string()
.to_string()
} else {
String::from("-")
};
let spent_height = match coin.3 {
Some(spent_height) => spent_height.to_string(),
None => String::from("-"),
};
table.add_row(row![
bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
.into_string()
.to_string(),
coin.0.note.token_id,
aliases,
format!(
"{} ({})",
coin.0.note.value,
encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
),
spend_hook,
user_data,
coin.1,
coin.2,
spent_height,
coin.4,
]);
}
if table.is_empty() {
println!("No unspent balances found");
} else {
println!("{table}");
}
}
/// Auxiliary function to define the wallet address subcommand handling.
async fn handle_wallet_address(drk: &DrkPtr) {
match drk.read().await.default_address().await {
Ok(address) => println!("{address}"),
Err(e) => println!("Failed to fetch default address: {e:?}"),
}
}
/// Auxiliary function to define the wallet addresses subcommand handling.
async fn handle_wallet_addresses(drk: &DrkPtr) {
let addresses = match drk.read().await.addresses().await {
Ok(a) => a,
Err(e) => {
println!("Failed to fetch addresses: {e:?}");
return
}
};
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
for (key_id, public_key, secret_key, is_default) in addresses {
let is_default = match is_default {
1 => "*",
_ => "",
};
table.add_row(row![key_id, public_key, secret_key, is_default]);
}
if table.is_empty() {
println!("No addresses found");
} else {
println!("{table}");
}
}
/// Auxiliary function to define the wallet default address subcommand handling.
async fn handle_wallet_default_address(drk: &DrkPtr, parts: &[&str]) {
if parts.len() != 3 {
println!("Malformed `wallet default-address` subcommand");
println!("Usage: wallet default-address <index>");
return
}
println!("Malformed `wallet` command");
println!("Usage: wallet --(initialize|keygen|balance|address|addresses|default-address|secrets|import-secrets|tree|coins)");
let index = match usize::from_str(parts[2]) {
Ok(i) => i,
Err(e) => {
println!("Invalid address id: {e:?}");
return
}
};
if let Err(e) = drk.read().await.set_default_address(index) {
println!("Failed to set default address: {e:?}");
}
}
/// Auxiliary function to define the wallet secrets subcommand handling.
async fn handle_wallet_secrets(drk: &DrkPtr) {
match drk.read().await.get_money_secrets().await {
Ok(secrets) => {
for secret in secrets {
println!("{secret}");
}
}
Err(e) => println!("Failed to fetch secrets: {e:?}"),
}
}
/// Auxiliary function to define the wallet import secrets subcommand handling.
async fn handle_wallet_import_secrets(drk: &DrkPtr) {
let mut secrets = vec![];
// TODO: read from a file here not stdin
let lines = stdin().lines();
for (i, line) in lines.enumerate() {
if let Ok(line) = line {
let Ok(bytes) = bs58::decode(&line.trim()).into_vec() else {
println!("Warning: Failed to decode secret on line {i}");
continue
};
let Ok(secret) = deserialize_async(&bytes).await else {
println!("Warning: Failed to deserialize secret on line {i}");
continue
};
secrets.push(secret);
}
}
match drk.read().await.import_money_secrets(secrets).await {
Ok(pubkeys) => {
for key in pubkeys {
println!("{key}");
}
}
Err(e) => println!("Failed to import secrets: {e:?}"),
}
}
/// Auxiliary function to define the wallet tree subcommand handling.
async fn handle_wallet_tree(drk: &DrkPtr) {
// TODO: write to a file here not stdout
match drk.read().await.get_money_tree().await {
Ok(tree) => println!("{tree:#?}"),
Err(e) => println!("Failed to fetch tree: {e:?}"),
}
}
/// Auxiliary function to define the wallet coins subcommand handling.
async fn handle_wallet_coins(drk: &DrkPtr) {
let lock = drk.read().await;
let coins = match lock.get_coins(true).await {
Ok(c) => c,
Err(e) => {
println!("Failed to fetch coins: {e:?}");
return
}
};
if coins.is_empty() {
return
}
let aliases_map = match lock.get_aliases_mapped_by_token().await {
Ok(m) => m,
Err(e) => {
println!("Failed to fetch aliases map: {e:?}");
return
}
};
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row![
"Coin",
"Token ID",
"Aliases",
"Value",
"Spend Hook",
"User Data",
"Creation Height",
"Spent",
"Spent Height",
"Spent TX"
]);
for coin in coins {
let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
Some(a) => a,
None => "-",
};
let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
format!("{}", coin.0.note.spend_hook)
} else {
String::from("-")
};
let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
bs58::encode(&serialize_async(&coin.0.note.user_data).await).into_string().to_string()
} else {
String::from("-")
};
let spent_height = match coin.3 {
Some(spent_height) => spent_height.to_string(),
None => String::from("-"),
};
table.add_row(row![
bs58::encode(&serialize_async(&coin.0.coin.inner()).await).into_string().to_string(),
coin.0.note.token_id,
aliases,
format!(
"{} ({})",
coin.0.note.value,
encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
),
spend_hook,
user_data,
coin.1,
coin.2,
spent_height,
coin.4,
]);
}
println!("{table}");
}
/// Auxiliary function to define the spend command handling.

View File

@@ -119,45 +119,9 @@ enum Subcmd {
/// Wallet operations
Wallet {
#[structopt(long)]
/// Initialize wallet database
initialize: bool,
#[structopt(long)]
/// Generate a new keypair in the wallet
keygen: bool,
#[structopt(long)]
/// Query the wallet for known balances
balance: bool,
#[structopt(long)]
/// Get the default address in the wallet
address: bool,
#[structopt(long)]
/// Print all the addresses in the wallet
addresses: bool,
#[structopt(long)]
/// Set the default address in the wallet
default_address: Option<usize>,
#[structopt(long)]
/// Print all the secret keys from the wallet
secrets: bool,
#[structopt(long)]
/// Import secret keys from stdin into the wallet, separated by newlines
import_secrets: bool,
#[structopt(long)]
/// Print the Merkle tree in the wallet
tree: bool,
#[structopt(long)]
/// Print all the coins in the wallet
coins: bool,
#[structopt(subcommand)]
/// Sub command to execute
command: WalletSubcmd,
},
/// Read a transaction from stdin and mark its input coins as spent
@@ -250,6 +214,42 @@ enum Subcmd {
},
}
#[derive(Clone, Debug, Deserialize, StructOpt)]
enum WalletSubcmd {
/// Initialize wallet database
Initialize,
/// Generate a new keypair in the wallet
Keygen,
/// Query the wallet for known balances
Balance,
/// Get the default address in the wallet
Address,
/// Print all the addresses in the wallet
Addresses,
/// Set the default address in the wallet
DefaultAddress {
/// Identifier of the address
index: usize,
},
/// Print all the secret keys from the wallet
Secrets,
/// Import secret keys from stdin into the wallet, separated by newlines
ImportSecrets,
/// Print the Merkle tree in the wallet
Tree,
/// Print all the coins in the wallet
Coins,
}
#[derive(Clone, Debug, Deserialize, StructOpt)]
enum OtcSubcmd {
/// Initialize the first half of the atomic swap
@@ -693,34 +693,7 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
Subcmd::Completions { shell } => generate_completions(&shell),
Subcmd::Wallet {
initialize,
keygen,
balance,
address,
addresses,
default_address,
secrets,
import_secrets,
tree,
coins,
} => {
if !initialize &&
!keygen &&
!balance &&
!address &&
!addresses &&
default_address.is_none() &&
!secrets &&
!tree &&
!coins &&
!import_secrets
{
eprintln!("Error: You must use at least one flag for this subcommand");
eprintln!("Run with \"wallet -h\" to see the subcommand usage.");
exit(2);
}
Subcmd::Wallet { command } => {
let drk = new_wallet(
blockchain_config.cache_path,
blockchain_config.wallet_path,
@@ -731,232 +704,209 @@ async fn realmain(args: Args, ex: ExecutorPtr) -> Result<()> {
)
.await;
if initialize {
if let Err(e) = drk.initialize_wallet().await {
eprintln!("Error initializing wallet: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_money().await {
eprintln!("Failed to initialize Money: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_dao().await {
eprintln!("Failed to initialize DAO: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_deployooor() {
eprintln!("Failed to initialize Deployooor: {e:?}");
exit(2);
}
return Ok(())
}
if keygen {
if let Err(e) = drk.money_keygen().await {
eprintln!("Failed to generate keypair: {e:?}");
exit(2);
}
return Ok(())
}
if balance {
let balmap = drk.money_balance().await?;
let aliases_map = drk.get_aliases_mapped_by_token().await?;
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Token ID", "Aliases", "Balance"]);
for (token_id, balance) in balmap.iter() {
let aliases = match aliases_map.get(token_id) {
Some(a) => a,
None => "-",
};
table.add_row(row![
token_id,
aliases,
encode_base10(*balance, BALANCE_BASE10_DECIMALS)
]);
match command {
WalletSubcmd::Initialize => {
if let Err(e) = drk.initialize_wallet().await {
eprintln!("Error initializing wallet: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_money().await {
eprintln!("Failed to initialize Money: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_dao().await {
eprintln!("Failed to initialize DAO: {e:?}");
exit(2);
}
if let Err(e) = drk.initialize_deployooor() {
eprintln!("Failed to initialize Deployooor: {e:?}");
exit(2);
}
}
if table.is_empty() {
println!("No unspent balances found");
} else {
println!("{table}");
WalletSubcmd::Keygen => {
if let Err(e) = drk.money_keygen().await {
eprintln!("Failed to generate keypair: {e:?}");
exit(2);
}
}
return Ok(())
}
WalletSubcmd::Balance => {
let balmap = drk.money_balance().await?;
if address {
let address = match drk.default_address().await {
Ok(a) => a,
let aliases_map = drk.get_aliases_mapped_by_token().await?;
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Token ID", "Aliases", "Balance"]);
for (token_id, balance) in balmap.iter() {
let aliases = match aliases_map.get(token_id) {
Some(a) => a,
None => "-",
};
table.add_row(row![
token_id,
aliases,
encode_base10(*balance, BALANCE_BASE10_DECIMALS)
]);
}
if table.is_empty() {
println!("No unspent balances found");
} else {
println!("{table}");
}
}
WalletSubcmd::Address => match drk.default_address().await {
Ok(address) => println!("{address}"),
Err(e) => {
eprintln!("Failed to fetch default address: {e:?}");
exit(2);
}
};
},
println!("{address}");
WalletSubcmd::Addresses => {
let addresses = drk.addresses().await?;
return Ok(())
}
if addresses {
let addresses = drk.addresses().await?;
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
for (key_id, public_key, secret_key, is_default) in addresses {
let is_default = match is_default {
1 => "*",
_ => "",
};
table.add_row(row![key_id, public_key, secret_key, is_default]);
}
if table.is_empty() {
println!("No addresses found");
} else {
println!("{table}");
}
return Ok(())
}
if let Some(idx) = default_address {
if let Err(e) = drk.set_default_address(idx) {
eprintln!("Failed to set default address: {e:?}");
exit(2);
}
return Ok(())
}
if secrets {
let v = drk.get_money_secrets().await?;
for i in v {
println!("{i}");
}
return Ok(())
}
if import_secrets {
let mut secrets = vec![];
let lines = stdin().lines();
for (i, line) in lines.enumerate() {
if let Ok(line) = line {
let bytes = bs58::decode(&line.trim()).into_vec()?;
let Ok(secret) = deserialize_async(&bytes).await else {
println!("Warning: Failed to deserialize secret on line {i}");
continue
// Create a prettytable with the new data:
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row!["Key ID", "Public Key", "Secret Key", "Is Default"]);
for (key_id, public_key, secret_key, is_default) in addresses {
let is_default = match is_default {
1 => "*",
_ => "",
};
secrets.push(secret);
table.add_row(row![key_id, public_key, secret_key, is_default]);
}
if table.is_empty() {
println!("No addresses found");
} else {
println!("{table}");
}
}
let pubkeys = match drk.import_money_secrets(secrets).await {
Ok(p) => p,
Err(e) => {
eprintln!("Failed to import secret keys into wallet: {e:?}");
WalletSubcmd::DefaultAddress { index } => {
if let Err(e) = drk.set_default_address(index) {
eprintln!("Failed to set default address: {e:?}");
exit(2);
}
};
for key in pubkeys {
println!("{key}");
}
return Ok(())
}
if tree {
let tree = drk.get_money_tree().await?;
println!("{tree:#?}");
return Ok(())
}
if coins {
let coins = drk.get_coins(true).await?;
if coins.is_empty() {
return Ok(())
WalletSubcmd::Secrets => {
for secret in drk.get_money_secrets().await? {
println!("{secret}");
}
}
let aliases_map = drk.get_aliases_mapped_by_token().await?;
WalletSubcmd::ImportSecrets => {
let mut secrets = vec![];
let lines = stdin().lines();
for (i, line) in lines.enumerate() {
if let Ok(line) = line {
let bytes = bs58::decode(&line.trim()).into_vec()?;
let Ok(secret) = deserialize_async(&bytes).await else {
println!("Warning: Failed to deserialize secret on line {i}");
continue
};
secrets.push(secret);
}
}
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row![
"Coin",
"Token ID",
"Aliases",
"Value",
"Spend Hook",
"User Data",
"Creation Height",
"Spent",
"Spent Height",
"Spent TX"
]);
for coin in coins {
let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
Some(a) => a,
None => "-",
let pubkeys = match drk.import_money_secrets(secrets).await {
Ok(p) => p,
Err(e) => {
eprintln!("Failed to import secret keys into wallet: {e:?}");
exit(2);
}
};
let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
format!("{}", coin.0.note.spend_hook)
} else {
String::from("-")
};
for key in pubkeys {
println!("{key}");
}
}
let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
bs58::encode(&serialize_async(&coin.0.note.user_data).await)
.into_string()
.to_string()
} else {
String::from("-")
};
WalletSubcmd::Tree => {
println!("{:#?}", drk.get_money_tree().await?);
}
let spent_height = match coin.3 {
Some(spent_height) => spent_height.to_string(),
None => String::from("-"),
};
WalletSubcmd::Coins => {
let coins = drk.get_coins(true).await?;
table.add_row(row![
bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
.into_string()
.to_string(),
coin.0.note.token_id,
aliases,
format!(
"{} ({})",
coin.0.note.value,
encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
),
spend_hook,
user_data,
coin.1,
coin.2,
spent_height,
coin.4,
if coins.is_empty() {
return Ok(())
}
let aliases_map = drk.get_aliases_mapped_by_token().await?;
let mut table = Table::new();
table.set_format(*format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
table.set_titles(row![
"Coin",
"Token ID",
"Aliases",
"Value",
"Spend Hook",
"User Data",
"Creation Height",
"Spent",
"Spent Height",
"Spent TX"
]);
for coin in coins {
let aliases = match aliases_map.get(&coin.0.note.token_id.to_string()) {
Some(a) => a,
None => "-",
};
let spend_hook = if coin.0.note.spend_hook != FuncId::none() {
format!("{}", coin.0.note.spend_hook)
} else {
String::from("-")
};
let user_data = if coin.0.note.user_data != pallas::Base::ZERO {
bs58::encode(&serialize_async(&coin.0.note.user_data).await)
.into_string()
.to_string()
} else {
String::from("-")
};
let spent_height = match coin.3 {
Some(spent_height) => spent_height.to_string(),
None => String::from("-"),
};
table.add_row(row![
bs58::encode(&serialize_async(&coin.0.coin.inner()).await)
.into_string()
.to_string(),
coin.0.note.token_id,
aliases,
format!(
"{} ({})",
coin.0.note.value,
encode_base10(coin.0.note.value, BALANCE_BASE10_DECIMALS)
),
spend_hook,
user_data,
coin.1,
coin.2,
spent_height,
coin.4,
]);
}
println!("{table}");
}
println!("{table}");
return Ok(())
}
unreachable!()
Ok(())
}
Subcmd::Spend => {

View File

@@ -37,13 +37,13 @@ of the guide can be added for future regressions.
| # | Description | Command | Status |
|----|------------------------------|-----------------------------------------------------------------|--------|
| 0 | Initialization | wallet --initialize | Pass |
| 1 | Key generation | wallet --keygen | Pass |
| 2 | Set default wallet | wallet --default-address {ADDR_ID} | Pass |
| 3 | Default address retrieval | wallet --address | Pass |
| 0 | Initialization | wallet initialize | Pass |
| 1 | Key generation | wallet keygen | Pass |
| 2 | Set default wallet | wallet default-address {ADDR_ID} | Pass |
| 3 | Default address retrieval | wallet address | Pass |
| 4 | Block scanning | scan | Pass |
| 5 | Block subscribing | subscribe | Pass |
| 6 | Balance retrieval | wallet --balance | Pass |
| 6 | Balance retrieval | wallet balance | Pass |
| 7 | Aliases retrieval | alias show | Pass |
| 8 | Mint auth generation | token generate-mint | Pass |
| 9 | Mint auths retrieval | token list | Pass |
@@ -52,7 +52,7 @@ of the guide can be added for future regressions.
| 12 | Mint generation | token mint {ALIAS} {AMOUNT} {ADDR} | Pass |
| 13 | Token freeze | token freeze {ALIAS} | Pass |
| 14 | Transfer | transfer {AMOUNT} {ALIAS} {ADDR} | Pass |
| 15 | Coins retrieval | wallet --coins | Pass |
| 15 | Coins retrieval | wallet coins | Pass |
| 16 | OTC initialization | otc init -v {AMOUNT}:{AMOUNT} -t {ALIAS}:{ALIAS} | Pass |
| 17 | OTC join | otc join | Pass |
| 18 | OTC sign | otc sign | Pass |

View File

@@ -3,8 +3,8 @@
# Path to `drk` binary
DRK="../../../drk -c drk.toml"
$DRK wallet --initialize
$DRK wallet --keygen
$DRK wallet --default-address 1
wallet=$($DRK wallet --address)
$DRK wallet initialize
$DRK wallet keygen
$DRK wallet default-address 1
wallet=$($DRK wallet address)
sed -i -e "s|9vw6WznKk7xEFQwwXhJWMMdjUPi3cXL8NrFKQpKifG1U|$wallet|g" darkfid.toml

View File

@@ -28,7 +28,7 @@ mint_tokens() {
$DRK alias add WCKD "$TOKEN_ID1"
$DRK alias add MLDY "$TOKEN_ID2"
ADDR="$($DRK wallet --address)"
ADDR="$($DRK wallet address)"
$DRK token mint WCKD 42 "$ADDR" | tee /tmp/mint-wkcd.tx | $DRK broadcast
$DRK token mint MLDY 20 "$ADDR" | tee /tmp/mint-mldy.tx | $DRK broadcast
@@ -37,7 +37,7 @@ mint_tokens() {
}
token_balance() {
BALANCE="$($DRK wallet --balance 2>/dev/null)"
BALANCE="$($DRK wallet balance 2>/dev/null)"
# No tokens received at all yet
if echo "$BALANCE" | grep -q "No unspent balances found"; then
@@ -110,7 +110,7 @@ wait_dao_treasury() {
}
propose() {
MY_ADDR=$($DRK wallet --address)
MY_ADDR=$($DRK wallet address)
PROPOSAL="$($DRK dao propose-transfer MiladyMakerDAO 1 5 WCKD "$MY_ADDR" | cut -d' ' -f3)"
$DRK dao proposal "$PROPOSAL" --mint-proposal > /tmp/propose.tx
$DRK broadcast < /tmp/propose.tx

View File

@@ -11,4 +11,4 @@ while true; do
done
$DRK scan
$DRK wallet --balance
$DRK wallet balance

View File

@@ -14,7 +14,7 @@ with. Note that the coin overview might look very different depending
on your activity:
```shell
$ ./drk wallet --coins
$ ./drk wallet coins
Coin | Spent | Token ID | Aliases | Value | Spend Hook | User Data | Spent TX
-----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------
@@ -88,7 +88,7 @@ also be in the mempool, so you should wait again until it's confirmed.
After a while you should see the change in balances in your wallet:
```shell
$ ./drk wallet --balance
$ ./drk wallet balance
Token ID | Aliases | Balance
----------------------------------------------+---------+-------------
@@ -102,7 +102,7 @@ unchanged. We can confirm it actually happened successfully by checking
our coins:
```shell
$ ./drk wallet --coins
$ ./drk wallet coins
Coin | Spent | Token ID | Aliases | Value | Spend Hook | User Data | Spent TX
-----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------

View File

@@ -264,7 +264,7 @@ Now that the DAO has something in its treasury, we can generate a
transfer proposal to send it somewhere, that will be up to vote
for 1 block period, if we hold the DAO proposer key. Let's propose
to send 5 of the 10 tokens to our address (we can find that with
`drk wallet --address`):
`drk wallet address`):
```shell
$ ./drk dao propose-transfer AnonDAO 1 5 DAWN {YOUR_ADDRESS}
@@ -511,7 +511,7 @@ $ ./drk dao balance AnonDAO
```
```shell
$ ./drk wallet --balance
$ ./drk wallet balance
Token ID | Aliases | Balance
----------------------------------------------+---------+-------------

View File

@@ -108,7 +108,7 @@ Config file created in "~/.config/darkfi/darkfid_config.toml". Please review it
```
```shell
$ ./drk wallet --address
$ ./drk wallet address
Config file created in "~/.config/darkfi/drk_config.toml". Please review it and try again.
```
@@ -150,7 +150,7 @@ initialize a wallet, and create a keypair. The wallet address shown in
the outputs is examplatory and will different from the one you get.
```shell
$ ./drk wallet --initialize
$ ./drk wallet initialize
Initializing Money Merkle tree
Successfully initialized Merkle tree for the Money contract
@@ -160,7 +160,7 @@ Successfully initialized Merkle trees for the DAO contract
```
```shell
$ ./drk wallet --keygen
$ ./drk wallet keygen
Generating a new keypair
New address:
@@ -168,7 +168,7 @@ CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
```
```shell
$ ./drk wallet --default-address 1
$ ./drk wallet default-address 1
```
The second command will print out your new DarkFi address where you
@@ -176,7 +176,7 @@ can receive payments. Take note of it. Alternatively, you can always
retrieve your default address using:
```shell
$ ./drk wallet --address
$ ./drk wallet address
CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
```
@@ -252,7 +252,7 @@ like this:
```toml
# Put your `minerd` endpoint here (default for testnet is in this example)
minerd_endpoint = "tcp://127.0.0.1:28467"
# Put the address from `drk wallet --address` here
# Put the address from `drk wallet address` here
recipient = "YOUR_WALLET_ADDRESS_HERE"
```
@@ -263,7 +263,7 @@ is `~/.config/darkfi/minerd_config.toml`). Finally, replace the
you can retrieve as follows:
```shell
$ ./drk wallet --address
$ ./drk wallet address
CbaqFqGTgn86Zh9AjUeMw3DJyVCshaPSPFtmj6Cyd5yU
```

View File

@@ -32,7 +32,7 @@ will receive the tokens you've sent.
We can see the spent coin in our wallet.
```shell
$ ./drk wallet --coins
$ ./drk wallet coins
Coin | Spent | Token ID | Aliases | Value | Spend Hook | User Data | Spent TX
-----------------+-------+-----------------+---------+--------------------------+------------+-----------+-----------------
@@ -47,7 +47,7 @@ We have to wait until the next block to see our change balance reappear
in our wallet.
```shell
$ ./drk wallet --balance
$ ./drk wallet balance
Token ID | Aliases | Balance
----------------------------------------------+---------+-------------

View File

@@ -20,7 +20,7 @@ then after some time your new balance should be in your wallet.
You can check your wallet balance using `drk`:
```shell
$ ./drk wallet --balance
$ ./drk wallet balance
Token ID | Aliases | Balance
----------------------------------------------+---------+---------
@@ -130,7 +130,7 @@ Now let's mint some tokens for ourselves. First grab your wallet address,
and then create the token mint transaction, and finally - broadcast it:
```shell
$ ./drk wallet --address
$ ./drk wallet address
{YOUR_ADDRESS}
```
@@ -171,7 +171,7 @@ then when the transaction is confirmed, your wallet should have your
new tokens listed when you run:
```shell
$ ./drk wallet --balance
$ ./drk wallet balance
Token ID | Aliases | Balance
----------------------------------------------+---------+-------------