mirror of
https://github.com/jwinterm/grin-wallet.git
synced 2026-10-30 12:38:32 +01:00
Slatepack address index on send (#821)
* Store address_index into tx log entry to get address and decrypt slatepack message, increment over all values * Provide address index on send and issue_invoice command * Keep 0 index for receive * Use sender address index at payment proof Rust API users need to update to SlatepackAddressIndex and the Slatepack methods Keep a wallet database backup for pending transactions and saved address indices. Seed recovery alone does not restore these, and the address counter restarts at 1.
This commit is contained in:
@@ -185,6 +185,11 @@ subcommands:
|
||||
- amount_includes_fee:
|
||||
help: Transaction amount includes transaction fee. Recipient will receive (amount - fee).
|
||||
long: amount_includes_fee
|
||||
- address_index:
|
||||
help: Slatepack address derivation path index
|
||||
short: a
|
||||
long: address_index
|
||||
takes_value: true
|
||||
- unpack:
|
||||
about: Unpack and display an armored Slatepack Message, decrypting if possible
|
||||
args:
|
||||
@@ -264,6 +269,11 @@ subcommands:
|
||||
help: Show slatepack data as QR code
|
||||
short: q
|
||||
long: slatepack_qr
|
||||
- address_index:
|
||||
help: Slatepack address derivation path index
|
||||
short: a
|
||||
long: address_index
|
||||
takes_value: true
|
||||
- pay:
|
||||
about: Spend coins to pay the provided invoice transaction
|
||||
args:
|
||||
|
||||
+67
-65
@@ -20,7 +20,6 @@ use crate::util::file::get_first_line;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{Mutex, ZeroingString};
|
||||
|
||||
use clap::ArgMatches;
|
||||
use grin_core as core;
|
||||
use grin_core::core::amount_to_hr_string;
|
||||
use grin_keychain as keychain;
|
||||
@@ -29,8 +28,11 @@ use grin_wallet_config::{GlobalWalletConfig, TorConfig, WalletConfig};
|
||||
use grin_wallet_controller::command::GlobalArgs;
|
||||
use grin_wallet_controller::{command, Error};
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
use grin_wallet_libwallet::slatepack::SlatepackAddressIndex;
|
||||
use grin_wallet_libwallet::{self, Slate, SlatepackAddress, SlatepackArmor};
|
||||
use grin_wallet_libwallet::{IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider};
|
||||
|
||||
use clap::ArgMatches;
|
||||
use linefeed::terminal::Signal;
|
||||
use linefeed::{Interface, ReadResult};
|
||||
use rpassword;
|
||||
@@ -38,6 +40,7 @@ use std::convert::TryFrom;
|
||||
use std::io::{IsTerminal, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
// define what to do on argument error
|
||||
macro_rules! arg_parse {
|
||||
@@ -526,6 +529,8 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
||||
|
||||
let slatepack_qr = args.is_present("slatepack_qr");
|
||||
|
||||
let address_index = parse_address_index_arg(args)?;
|
||||
|
||||
Ok(command::SendArgs {
|
||||
amount,
|
||||
amount_includes_fee,
|
||||
@@ -545,23 +550,32 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
||||
skip_tor,
|
||||
bridge,
|
||||
slatepack_qr,
|
||||
address_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_receive_args(args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
// input file
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", file);
|
||||
fn parse_address_index_arg(args: &ArgMatches) -> Result<Option<SlatepackAddressIndex>, ParseError> {
|
||||
let address = if let Some(i) = parse_optional(args, "address_index")? {
|
||||
let val = i.parse::<u32>();
|
||||
match val {
|
||||
Ok(v) => Some(
|
||||
SlatepackAddressIndex(v)
|
||||
.check()
|
||||
.map_err(|e| ParseError::ArgumentError(e.to_string()))?,
|
||||
),
|
||||
Err(e) => {
|
||||
let msg = format!("Could not parse address_index as a whole number. e={}", e);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
pub fn parse_receive_args(args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
let input_file = parse_input_file_arg(args)?;
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
@@ -590,8 +604,7 @@ pub fn parse_receive_args(args: &ArgMatches) -> Result<command::ReceiveArgs, Par
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_unpack_args(args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
// input file
|
||||
fn parse_input_file_arg(args: &ArgMatches) -> Result<Option<String>, ParseError> {
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
@@ -604,6 +617,11 @@ pub fn parse_unpack_args(args: &ArgMatches) -> Result<command::ReceiveArgs, Pars
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
Ok(input_file)
|
||||
}
|
||||
|
||||
pub fn parse_unpack_args(args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
let input_file = parse_input_file_arg(args)?;
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
@@ -636,18 +654,7 @@ pub fn parse_finalize_args(args: &ArgMatches) -> Result<command::FinalizeArgs, P
|
||||
let fluff = args.is_present("fluff");
|
||||
let nopost = args.is_present("nopost");
|
||||
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", file);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
let input_file = parse_input_file_arg(args)?;
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
@@ -702,12 +709,15 @@ pub fn parse_issue_invoice_args(
|
||||
|
||||
let slatepack_qr = args.is_present("slatepack_qr");
|
||||
|
||||
let address_index = parse_address_index_arg(args)?;
|
||||
|
||||
Ok(command::IssueInvoiceArgs {
|
||||
dest,
|
||||
issue_args: IssueInvoiceTxArgs {
|
||||
dest_acct_name: None,
|
||||
amount,
|
||||
target_slate_version,
|
||||
address_index,
|
||||
},
|
||||
outfile,
|
||||
slatepack_qr,
|
||||
@@ -757,7 +767,7 @@ pub fn parse_process_invoice_args(
|
||||
args: &ArgMatches,
|
||||
prompt: bool,
|
||||
slate: Slate,
|
||||
ret_address: Option<SlatepackAddress>,
|
||||
mut ret_address: Option<SlatepackAddress>,
|
||||
) -> Result<command::ProcessInvoiceArgs, ParseError> {
|
||||
// minimum_confirmations
|
||||
let min_c = parse_required(args, "minimum_confirmations")?;
|
||||
@@ -776,9 +786,18 @@ pub fn parse_process_invoice_args(
|
||||
let max_outputs = 500;
|
||||
|
||||
if prompt {
|
||||
let dest = match ret_address.clone() {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
let dest = parse_optional(args, "dest")?;
|
||||
let dest = match dest {
|
||||
Some(dest) => {
|
||||
ret_address = Some(SlatepackAddress::try_from(dest.as_str()).map_err(|e| {
|
||||
ParseError::ArgumentError(format!("Can not parse destination address {}", e))
|
||||
})?);
|
||||
dest
|
||||
}
|
||||
None => match ret_address.clone() {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
},
|
||||
};
|
||||
// Now we need to prompt the user whether they want to do this,
|
||||
prompt_pay_invoice(&slate, &dest)?;
|
||||
@@ -842,16 +861,7 @@ pub fn parse_txs_args(args: &ArgMatches) -> Result<command::TxsArgs, ParseError>
|
||||
None => None,
|
||||
Some(tx) => Some(parse_u64(tx, "id")? as u32),
|
||||
};
|
||||
let tx_slate_id = match args.value_of("txid") {
|
||||
None => None,
|
||||
Some(tx) => match tx.parse() {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
let msg = format!("Could not parse txid parameter. e={}", e);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
},
|
||||
};
|
||||
let tx_slate_id = parse_tx_slate_id_arg(args)?;
|
||||
if tx_id.is_some() && tx_slate_id.is_some() {
|
||||
let msg = "At most one of 'id' (-i) or 'txid' (-t) may be provided.".to_string();
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
@@ -867,22 +877,24 @@ pub fn parse_txs_args(args: &ArgMatches) -> Result<command::TxsArgs, ParseError>
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_tx_slate_id_arg(args: &ArgMatches) -> Result<Option<Uuid>, ParseError> {
|
||||
let tx_slate_id = match args.value_of("txid") {
|
||||
None => None,
|
||||
Some(tx) => match tx.parse() {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
let msg = format!("Could not parse txid parameter. e={}", e);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(tx_slate_id)
|
||||
}
|
||||
|
||||
pub fn parse_post_args(args: &ArgMatches) -> Result<command::PostArgs, ParseError> {
|
||||
let fluff = args.is_present("fluff");
|
||||
|
||||
// input file
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", file);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
let input_file = parse_input_file_arg(args)?;
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
@@ -948,16 +960,7 @@ pub fn parse_export_proof_args(args: &ArgMatches) -> Result<command::ProofExport
|
||||
None => None,
|
||||
Some(tx) => Some(parse_u64(tx, "id")? as u32),
|
||||
};
|
||||
let tx_slate_id = match args.value_of("txid") {
|
||||
None => None,
|
||||
Some(tx) => match tx.parse() {
|
||||
Ok(t) => Some(t),
|
||||
Err(e) => {
|
||||
let msg = format!("Could not parse txid parameter. e={}", e);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
},
|
||||
};
|
||||
let tx_slate_id = parse_tx_slate_id_arg(args)?;
|
||||
if tx_id.is_some() && tx_slate_id.is_some() {
|
||||
let msg = "At most one of 'id' (-i) or 'txid' (-t) may be provided.".to_string();
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
@@ -1055,8 +1058,7 @@ where
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut owner_api = Owner::new(wallet.clone(), None, config.config_file_path.clone());
|
||||
command::unpack(&mut owner_api, mask.as_ref(), slatepack)?;
|
||||
command::unpack(wallet.clone(), mask.as_ref(), slatepack)?;
|
||||
return Ok("unpack".to_string());
|
||||
}
|
||||
("owner_api", _) => {
|
||||
@@ -1237,7 +1239,7 @@ where
|
||||
("unpack", Some(args)) => {
|
||||
let a = arg_parse!(parse_unpack_args(args));
|
||||
let slatepack = command::read_slatepack(a)?;
|
||||
command::unpack(owner_api, km, slatepack)
|
||||
command::unpack(owner_api.wallet_inst.clone(), km, slatepack)
|
||||
}
|
||||
("finalize", Some(args)) => {
|
||||
let a = arg_parse!(parse_finalize_args(args));
|
||||
|
||||
Reference in New Issue
Block a user