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

Bugfix-2512 | rusk-wallet: Remove all panics in the codebase #3005

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
7 changes: 4 additions & 3 deletions rusk-wallet/src/bin/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use rusk_wallet::{
Gas, DEFAULT_LIMIT_CALL, DEFAULT_LIMIT_DEPLOYMENT,
DEFAULT_LIMIT_TRANSFER, DEFAULT_PRICE, MIN_PRICE_DEPLOYMENT,
},
Address, Error, Profile, Wallet, EPOCH, MAX_PROFILES,
get_max_profiles, Address, Error, Profile, Wallet, EPOCH,
};
use wallet_core::BalanceInfo;

Expand Down Expand Up @@ -328,10 +328,11 @@ impl Command {
}
}
Command::Profiles { new } => {
let max_profiles = get_max_profiles()?;
if new {
if wallet.profiles().len() >= MAX_PROFILES {
if wallet.profiles().len() >= max_profiles {
println!(
"Cannot create more profiles, this wallet only supports up to {MAX_PROFILES} profiles. You have {} profiles already.", wallet.profiles().len()
"Cannot create more profiles, this wallet only supports up to {max_profiles} profiles. You have {} profiles already.", wallet.profiles().len()
);
std::process::exit(0);
}
Expand Down
3 changes: 2 additions & 1 deletion rusk-wallet/src/bin/command/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ pub(crate) async fn transaction_from_notes(
) -> anyhow::Result<Vec<TransactionHistory>> {
notes.sort_by(|a, b| a.note.pos().cmp(b.note.pos()));
let mut ret: Vec<TransactionHistory> = vec![];
let gql = GraphQL::new(settings.state.to_string(), io::status::interactive);
let gql =
GraphQL::new(settings.state.to_string(), io::status::interactive)?;

let nullifiers = notes
.iter()
Expand Down
33 changes: 21 additions & 12 deletions rusk-wallet/src/bin/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use std::collections::HashMap;
use std::path::Path;
use url::Url;

use crate::Error;

#[derive(Debug, Deserialize, Clone)]
#[allow(dead_code)]
pub(crate) struct Network {
Expand Down Expand Up @@ -38,22 +40,29 @@ fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<Option<String>> {

impl Config {
/// Attempt to load configuration from file
pub fn load(profile: &Path) -> anyhow::Result<Config> {
pub fn load(profile: &Path) -> Result<Config, Error> {
let profile = profile.join("config.toml");

let mut global_config = dirs::home_dir().expect("OS not supported");
global_config.push(".config");
global_config.push(env!("CARGO_BIN_NAME"));
global_config.push("config.toml");
let mut global_config = dirs::home_dir();

match global_config {
Some(ref mut path) => {
path.push(".config");
path.push(env!("CARGO_BIN_NAME"));
path.push("config.toml");

let contents = read_to_string(profile)?
.or(read_to_string(&global_config)?)
.unwrap_or_else(|| {
include_str!("../../default.config.toml").to_string()
});
let contents = read_to_string(profile)?
.or(read_to_string(&path)?)
.unwrap_or_else(|| {
include_str!("../../default.config.toml").to_string()
});

let network: Network = toml::from_str(&contents)?;
let network: Network = toml::from_str(&contents)
.map_err(|_| Error::NetworkNotFound)?;

Ok(Config { network })
Ok(Config { network })
}
None => Err(Error::OsNotSupported),
}
}
}
29 changes: 15 additions & 14 deletions rusk-wallet/src/bin/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use inquire::{InquireError, Select};
use rusk_wallet::{
currency::Dusk,
dat::{DatFileVersion, LATEST_VERSION},
Address, Error, Profile, Wallet, WalletPath, MAX_PROFILES,
get_max_profiles, Address, Error, Profile, Wallet, WalletPath,
};

use crate::{
Expand Down Expand Up @@ -106,7 +106,7 @@ pub(crate) async fn run_loop(
let gql = GraphQL::new(
settings.state.to_string(),
io::status::interactive,
);
)?;
gql.wait_for(&tx_id).await?;

if let Some(explorer) = &settings.explorer {
Expand Down Expand Up @@ -148,9 +148,10 @@ async fn profile_idx(
match menu_profile(wallet)? {
ProfileSelect::Index(index, _) => Ok(index),
ProfileSelect::New => {
if wallet.profiles().len() >= MAX_PROFILES {
let max_profiles = get_max_profiles()?;
if wallet.profiles().len() >= max_profiles {
println!(
"Cannot create more profiles, this wallet only supports up to {MAX_PROFILES} profiles"
"Cannot create more profiles, this wallet only supports up to {max_profiles} profiles"
);

return Err(InquireError::OperationCanceled.into());
Expand All @@ -169,6 +170,8 @@ async fn profile_idx(
DatFileVersion::RuskBinaryFileFormat(LATEST_VERSION),
)?;

// UNWRAP: we can safely unwrap here because we know the file is
// not None since we've checked the file version
wallet.save_to(WalletFile {
path: wallet.file().clone().unwrap().path,
pwd,
Expand All @@ -194,8 +197,10 @@ fn menu_profile(wallet: &Wallet<WalletFile>) -> anyhow::Result<ProfileSelect> {
menu_items.push(ProfileSelect::Index(index as u8, profile));
}

let max_profiles = get_max_profiles()?;

let remaining_profiles =
MAX_PROFILES.saturating_sub(wallet.profiles().len());
max_profiles.saturating_sub(wallet.profiles().len());

// only show the option to create a new profile if we don't already have
// `MAX_PROFILES`
Expand Down Expand Up @@ -364,7 +369,7 @@ fn confirm(cmd: &Command, wallet: &Wallet<WalletFile>) -> anyhow::Result<bool> {
gas_price,
memo,
} => {
let sender = sender.as_ref().expect("sender to be a valid address");
let sender = sender.as_ref().ok_or(Error::BadAddress)?;
sender.same_transaction_model(rcvr)?;
let max_fee = gas_limit * gas_price;
println!(" > Pay with {}", sender.preview());
Expand All @@ -385,8 +390,7 @@ fn confirm(cmd: &Command, wallet: &Wallet<WalletFile>) -> anyhow::Result<bool> {
gas_limit,
gas_price,
} => {
let sender =
address.as_ref().expect("address to be a valid address");
let sender = address.as_ref().ok_or(Error::BadAddress)?;
let max_fee = gas_limit * gas_price;
let stake_to = wallet.public_address(wallet.find_index(sender)?)?;
println!(" > Pay with {}", sender.preview());
Expand All @@ -403,8 +407,7 @@ fn confirm(cmd: &Command, wallet: &Wallet<WalletFile>) -> anyhow::Result<bool> {
gas_limit,
gas_price,
} => {
let sender =
address.as_ref().expect("address to be a valid address");
let sender = address.as_ref().ok_or(Error::BadAddress)?;
let unstake_from =
wallet.public_address(wallet.find_index(sender)?)?;
let max_fee = gas_limit * gas_price;
Expand All @@ -424,8 +427,7 @@ fn confirm(cmd: &Command, wallet: &Wallet<WalletFile>) -> anyhow::Result<bool> {
gas_limit,
gas_price,
} => {
let sender =
address.as_ref().expect("address to be a valid address");
let sender = address.as_ref().ok_or(Error::BadAddress)?;
let max_fee = gas_limit * gas_price;
let withdraw_from =
wallet.public_address(wallet.find_index(sender)?)?;
Expand All @@ -447,8 +449,7 @@ fn confirm(cmd: &Command, wallet: &Wallet<WalletFile>) -> anyhow::Result<bool> {
gas_limit,
gas_price,
} => {
let sender =
address.as_ref().expect("address to be a valid address");
let sender = address.as_ref().ok_or(Error::BadAddress)?;
let code_len = code.metadata()?.len();
let max_fee = gas_limit * gas_price;

Expand Down
37 changes: 20 additions & 17 deletions rusk-wallet/src/bin/io/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub(crate) fn ask_pwd(msg: &str) -> Result<String, InquireError> {
let pwd = Password::new(msg)
.with_display_toggle_enabled()
.without_confirmation()
.with_display_mode(PasswordDisplayMode::Hidden)
.with_display_mode(PasswordDisplayMode::Masked)
.prompt();

pwd
Expand Down Expand Up @@ -157,12 +157,14 @@ pub(crate) fn request_dir(
}
};

let q = Text::new(
format!("Please enter a directory to {}:", what_for).as_str(),
)
.with_default(profile.to_str().unwrap())
.with_validator(validator)
.prompt()?;
let msg = format!("Please enter a directory to {}:", what_for);
let q = match profile.to_str() {
Some(p) => Text::new(msg.as_str())
.with_default(p)
.with_validator(validator)
.prompt(),
None => Text::new(msg.as_str()).with_validator(validator).prompt(),
}?;

let p = PathBuf::from(q);

Expand Down Expand Up @@ -205,13 +207,13 @@ fn request_token(
min: Dusk,
balance: Dusk,
default: Option<f64>,
) -> anyhow::Result<Dusk> {
) -> Result<Dusk, Error> {
// Checks if the value is larger than the given min and smaller than the
// min of the balance and `MAX_CONVERTIBLE`.
let validator = move |value: &f64| {
let max = std::cmp::min(balance, MAX_CONVERTIBLE);

match (min..=max).contains(&Dusk::from(*value)) {
match (min..=max).contains(&Dusk::try_from(*value)?) {
true => Ok(Validation::Valid),
false => Ok(Validation::Invalid(
format!("The amount has to be between {} and {}", min, max)
Expand Down Expand Up @@ -240,34 +242,34 @@ fn request_token(
render_config: RenderConfig::default(),
};

Ok(amount_prompt.prompt()?.into())
amount_prompt.prompt()?.try_into()
}

/// Request a positive amount of tokens
pub(crate) fn request_token_amt(
action: &str,
balance: Dusk,
) -> anyhow::Result<Dusk> {
) -> Result<Dusk, Error> {
let min = MIN_CONVERTIBLE;

request_token(action, min, balance, None)
request_token(action, min, balance, None).map_err(Error::from)
}

/// Request amount of tokens that can be 0
pub(crate) fn request_optional_token_amt(
action: &str,
balance: Dusk,
) -> anyhow::Result<Dusk> {
) -> Result<Dusk, Error> {
let min = Dusk::from(0);

request_token(action, min, balance, None)
request_token(action, min, balance, None).map_err(Error::from)
}

/// Request amount of tokens that can't be lower than MINIMUM_STAKE
pub(crate) fn request_stake_token_amt(balance: Dusk) -> anyhow::Result<Dusk> {
pub(crate) fn request_stake_token_amt(balance: Dusk) -> Result<Dusk, Error> {
let min: Dusk = MINIMUM_STAKE.into();

request_token("stake", min, balance, None)
request_token("stake", min, balance, None).map_err(Error::from)
}

/// Request gas limit
Expand All @@ -290,7 +292,7 @@ pub(crate) fn request_gas_limit(default_gas_limit: u64) -> anyhow::Result<u64> {
pub(crate) fn request_gas_price(
min_gas_price: Lux,
mempool_gas_prices: MempoolGasPrices,
) -> anyhow::Result<Lux> {
) -> Result<Lux, Error> {
let default_gas_price = if mempool_gas_prices.average > min_gas_price {
mempool_gas_prices.average
} else {
Expand All @@ -304,6 +306,7 @@ pub(crate) fn request_gas_price(
Some(default_gas_price as f64),
)
.map(|dusk| *dusk)
.map_err(Error::from)
}

pub(crate) fn request_str(
Expand Down
4 changes: 2 additions & 2 deletions rusk-wallet/src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ async fn exec() -> anyhow::Result<()> {
let cmd = args.command.clone();

// Get the initial settings from the args
let settings_builder = Settings::args(args);
let settings_builder = Settings::args(args)?;

// Obtain the wallet dir from the settings
let wallet_dir = settings_builder.wallet_dir().clone();
Expand Down Expand Up @@ -357,7 +357,7 @@ async fn exec() -> anyhow::Result<()> {
let tx_id = hex::encode(hash.to_bytes());

// Wait for transaction confirmation from network
let gql = GraphQL::new(settings.state, status::headless);
let gql = GraphQL::new(settings.state, status::headless)?;
gql.wait_for(&tx_id).await?;

println!("{tx_id}");
Expand Down
16 changes: 9 additions & 7 deletions rusk-wallet/src/bin/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,29 +123,31 @@ impl SettingsBuilder {
}

impl Settings {
pub fn args(args: WalletArgs) -> SettingsBuilder {
pub fn args(args: WalletArgs) -> Result<SettingsBuilder, Error> {
let wallet_dir = if let Some(path) = &args.wallet_dir {
path.clone()
} else {
let mut path = dirs::home_dir().expect("OS not supported");
let mut path = dirs::home_dir().ok_or(Error::OsNotSupported)?;
path.push(".dusk");
path.push(env!("CARGO_BIN_NAME"));
path
};

SettingsBuilder { wallet_dir, args }
Ok(SettingsBuilder { wallet_dir, args })
}

pub async fn check_state_con(&self) -> Result<(), reqwest::Error> {
RuesHttpClient::new(self.state.as_ref())
pub async fn check_state_con(&self) -> Result<(), Error> {
RuesHttpClient::new(self.state.as_ref())?
.check_connection()
.await
.map_err(Error::from)
}

pub async fn check_prover_con(&self) -> Result<(), reqwest::Error> {
RuesHttpClient::new(self.prover.as_ref())
pub async fn check_prover_con(&self) -> Result<(), Error> {
RuesHttpClient::new(self.prover.as_ref())?
.check_connection()
.await
.map_err(Error::from)
}
}

Expand Down
16 changes: 11 additions & 5 deletions rusk-wallet/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl Cache {
let to_move = self
.db
.get_cf(&cf, key)?
.expect("Note must exists to be moved");
.ok_or(Error::CacheDatabaseCorrupted)?;
self.db.put_cf(&spent_cf, key, to_move)?;
self.db.delete_cf(&cf, n.to_bytes())?;
}
Expand All @@ -148,11 +148,17 @@ impl Cache {
/// Returns the last position of inserted notes. If no note has ever been
/// inserted it returns None.
pub(crate) fn last_pos(&self) -> Result<Option<u64>, Error> {
Ok(self.db.get(b"last_pos")?.map(|x| {
let buff: [u8; 8] = x.try_into().expect("Invalid u64 in cache db");
let last_pos = self.db.get(b"last_pos")?;

u64::from_be_bytes(buff)
}))
match last_pos {
Some(x) => {
let buff =
x.try_into().map_err(|_| Error::CacheDatabaseCorrupted)?;

Ok(Some(u64::from_be_bytes(buff)))
}
None => Ok(None),
}
}

/// Returns an iterator over all unspent notes nullifier for the given pk.
Expand Down
Loading
Loading