Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alias infloop fix #690

Merged
merged 2 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/src/bin/anoma-wallet/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ fn address_find(ctx: Context, args: args::AddressFind) {
/// Add an address to the wallet.
fn address_add(ctx: Context, args: args::AddressAdd) {
let mut wallet = ctx.wallet;
if !wallet.add_address(args.alias.clone(), args.address) {
if wallet
.add_address(args.alias.clone(), args.address)
.is_none()
{
eprintln!("Address not added");
cli::safe_exit(1);
}
Expand Down
75 changes: 32 additions & 43 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,25 +274,25 @@ pub async fn submit_init_validator(
} else {
validator_address_alias
};
if ctx.wallet.add_address(
if let Some(new_alias) = ctx.wallet.add_address(
validator_address_alias.clone(),
validator_address.clone(),
) {
println!(
"Added alias {} for address {}.",
validator_address_alias,
new_alias,
validator_address.encode()
);
}
let rewards_address_alias =
format!("{}-rewards", validator_address_alias);
if ctx.wallet.add_address(
if let Some(new_alias) = ctx.wallet.add_address(
rewards_address_alias.clone(),
rewards_address.clone(),
) {
println!(
"Added alias {} for address {}.",
rewards_address_alias,
new_alias,
rewards_address.encode()
);
}
Expand Down Expand Up @@ -717,49 +717,38 @@ async fn save_initialized_accounts(
let wallet = &mut ctx.wallet;
for (ix, address) in initialized_accounts.iter().enumerate() {
let encoded = address.encode();
let mut added = false;
while !added {
let alias: Cow<str> = match &args.initialized_account_alias {
Some(initialized_account_alias) => {
if len == 1 {
// If there's only one account, use the
// alias as is
initialized_account_alias.into()
} else {
// If there're multiple accounts, use
// the alias as prefix, followed by
// index number
format!("{}{}", initialized_account_alias, ix)
.into()
}
let alias: Cow<str> = match &args.initialized_account_alias {
Some(initialized_account_alias) => {
if len == 1 {
// If there's only one account, use the
// alias as is
initialized_account_alias.into()
} else {
// If there're multiple accounts, use
// the alias as prefix, followed by
// index number
format!("{}{}", initialized_account_alias, ix).into()
}
None => {
print!("Choose an alias for {}: ", encoded);
io::stdout().flush().await.unwrap();
let mut alias = String::new();
io::stdin().read_line(&mut alias).await.unwrap();
alias.trim().to_owned().into()
}
};
added = if alias.is_empty() {
}
None => {
print!("Choose an alias for {}: ", encoded);
io::stdout().flush().await.unwrap();
let mut alias = String::new();
io::stdin().read_line(&mut alias).await.unwrap();
alias.trim().to_owned().into()
}
};
let alias = alias.into_owned();
let added = wallet.add_address(alias.clone(), address.clone());
match added {
Some(new_alias) if new_alias != encoded => {
println!(
"Empty alias given, using {} as the alias.",
encoded
"Added alias {} for address {}.",
new_alias, encoded
);
wallet.add_address(encoded.clone(), address.clone())
} else {
let alias = alias.into_owned();
let added =
wallet.add_address(alias.clone(), address.clone());
if added {
println!(
"Added alias {} for address {}.",
alias, encoded
);
}
added
}
}
_ => println!("No alias added for address {}.", encoded),
};
}
if !args.dry_run {
wallet.save().unwrap_or_else(|err| eprintln!("{}", err));
Expand Down
6 changes: 4 additions & 2 deletions apps/src/lib/client/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,17 +641,19 @@ fn init_genesis_validator_aux(
let validator_address =
address::gen_established_address("genesis validator address");
let validator_address_alias = alias.clone();
if !wallet
if wallet
.add_address(validator_address_alias.clone(), validator_address.clone())
.is_none()
{
cli::safe_exit(1)
}
// Generate staking reward address
let rewards_address =
address::gen_established_address("genesis validator reward address");
let rewards_address_alias = format!("{}-rewards", alias);
if !wallet
if wallet
.add_address(rewards_address_alias.clone(), rewards_address.clone())
.is_none()
{
cli::safe_exit(1)
}
Expand Down
14 changes: 10 additions & 4 deletions apps/src/lib/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,15 @@ impl Wallet {
}

/// Add a new address with the given alias. If the alias is already used,
/// will prompt for overwrite confirmation, which when declined, the address
/// won't be added. Return `true` if the address has been added.
pub fn add_address(&mut self, alias: String, address: Address) -> bool {
/// will ask whether the existing alias should be replaced, a different
/// alias is desired, or the alias creation should be cancelled. Return
/// the chosen alias if the address has been added, otherwise return
/// nothing.
pub fn add_address(
&mut self,
alias: Alias,
address: Address,
) -> Option<Alias> {
self.store.insert_address(alias, address)
}

Expand All @@ -240,7 +246,7 @@ impl Wallet {
alias: Alias,
keypair: StoredKeypair,
pkh: PublicKeyHash,
) -> bool {
) -> Option<Alias> {
self.store.insert_keypair(alias, keypair, pkh)
}
}
Expand Down
102 changes: 74 additions & 28 deletions apps/src/lib/wallet/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,48 +240,76 @@ impl Store {
StoredKeypair::new(keypair, password);
let address = Address::Implicit(ImplicitAddress::Ed25519(pkh.clone()));
let alias = alias.unwrap_or_else(|| pkh.clone().into());
if !self.insert_keypair(alias.clone(), keypair_to_store, pkh) {
if self
.insert_keypair(alias.clone(), keypair_to_store, pkh)
.is_none()
{
eprintln!("Action cancelled, no changes persisted.");
cli::safe_exit(1);
}
if !self.insert_address(alias.clone(), address) {
if self.insert_address(alias.clone(), address).is_none() {
eprintln!("Action cancelled, no changes persisted.");
cli::safe_exit(1);
}
(alias, raw_keypair)
}

/// Insert a new key with the given alias. If the alias is already used,
/// will prompt for overwrite confirmation.
/// will prompt for overwrite/reselection confirmation. If declined, then
/// keypair is not inserted and nothing is returned, otherwise selected
/// alias is returned.
pub(super) fn insert_keypair(
&mut self,
alias: Alias,
keypair: StoredKeypair,
pkh: PublicKeyHash,
) -> bool {
) -> Option<Alias> {
if alias.is_empty() {
println!(
"Empty alias given, defaulting to {}.",
alias = Into::<Alias>::into(pkh.clone())
);
}
if self.keys.contains_key(&alias) {
match show_overwrite_confirmation(&alias, "a key") {
ConfirmationResponse::Overwrite => {}
ConfirmationResponse::Cancel => return false,
ConfirmationResponse::Replace => {}
ConfirmationResponse::Reselect(new_alias) => {
return self.insert_keypair(new_alias, keypair, pkh);
}
ConfirmationResponse::Skip => return None,
}
}
self.keys.insert(alias.clone(), keypair);
self.pkhs.insert(pkh, alias);
true
self.pkhs.insert(pkh, alias.clone());
Some(alias)
}

/// Insert a new address with the given alias. If the alias is already used,
/// will prompt for overwrite confirmation, which when declined, the address
/// won't be added. Return `true` if the address has been added.
pub fn insert_address(&mut self, alias: Alias, address: Address) -> bool {
/// will prompt for overwrite/reselection confirmation, which when declined,
/// the address won't be added. Return the selected alias if the address has
/// been added.
pub fn insert_address(
&mut self,
alias: Alias,
address: Address,
) -> Option<Alias> {
if alias.is_empty() {
println!(
"Empty alias given, defaulting to {}.",
alias = address.encode()
);
}
if self.addresses.contains_key(&alias) {
match show_overwrite_confirmation(&alias, "an address") {
ConfirmationResponse::Overwrite => {}
ConfirmationResponse::Cancel => return false,
ConfirmationResponse::Replace => {}
ConfirmationResponse::Reselect(new_alias) => {
return self.insert_address(new_alias, address);
}
ConfirmationResponse::Skip => return None,
}
}
self.addresses.insert(alias, address);
true
self.addresses.insert(alias.clone(), address);
Some(alias)
}

fn decode(data: Vec<u8>) -> Result<Self, toml::de::Error> {
Expand All @@ -294,38 +322,56 @@ impl Store {
}

enum ConfirmationResponse {
Overwrite,
Cancel,
Replace,
Reselect(Alias),
Skip,
}

/// The given alias has been selected but conflicts with another alias in
/// the store. Offer the user to either replace existing mapping, alter the
/// chosen alias to a name of their chosing, or cancel the aliasing.

fn show_overwrite_confirmation(
alias: &str,
alias_for: &str,
) -> ConfirmationResponse {
println!(
print!(
"You're trying to create an alias \"{}\" that already exists for {} \
in your store.",
inyour store.\nWould you like to replace it? \
s(k)ip/re(p)lace/re(s)elect: ",
alias, alias_for
);
print!("Would you like to replace it? [y/N]: ");

io::stdout().flush().unwrap();

let mut buffer = String::new();
// Get the user to select between 3 choices
match io::stdin().read_line(&mut buffer) {
Ok(size) if size > 0 => {
// Isolate the single character representing the choice
let byte = buffer.chars().next().unwrap();
buffer.clear();
match byte {
'y' | 'Y' => ConfirmationResponse::Overwrite,
'n' | 'N' | '\n' => ConfirmationResponse::Cancel,
_ => {
println!("Invalid option, try again.");
show_overwrite_confirmation(alias, alias_for)
'p' | 'P' => return ConfirmationResponse::Replace,
's' | 'S' => {
// In the case of reselection, elicit new alias
print!("Please enter a different alias: ");
io::stdout().flush().unwrap();
if io::stdin().read_line(&mut buffer).is_ok() {
return ConfirmationResponse::Reselect(
buffer.trim().to_string(),
);
}
}
}
'k' | 'K' => return ConfirmationResponse::Skip,
// Input is senseless fall through to repeat prompt
_ => {}
};
}
_ => ConfirmationResponse::Cancel,
_ => {}
}
// Input is senseless fall through to repeat prompt
println!("Invalid option, try again.");
show_overwrite_confirmation(alias, alias_for)
}

/// Wallet file name
Expand Down