backend: do not panic on iter and directory creation

This commit is contained in:
ardocrat
2026-06-04 16:46:36 +03:00
parent 6e2062c143
commit ff1238c3c4
6 changed files with 56 additions and 61 deletions
+7 -2
View File
@@ -845,7 +845,7 @@ where
{
let mut uuid = None;
if let Some(i) = tx_id {
let tx = w.tx_log_iter().find(|t| t.id == i);
let tx = w.tx_log_iter()?.find(|t| t.id == i);
if let Some(t) = tx {
uuid = t.tx_slate_id;
}
@@ -1010,7 +1010,12 @@ where
}),
Err(_) => {
let outputs = retrieve_outputs(wallet_inst, keychain_mask, &None, true, false, None)?;
let height = outputs.1.iter().map(|m| m.output.height).max().unwrap_or_else(|| 0);
let height = outputs
.1
.iter()
.map(|m| m.output.height)
.max()
.unwrap_or_else(|| 0);
Ok(NodeHeightResult {
height,
header_hash: "".to_owned(),
+22 -32
View File
@@ -122,11 +122,10 @@ where
/// Create new wallet backend.
pub fn new(data_file_dir: &str, n_client: C) -> Result<Self, Error> {
let db_path = Path::new(data_file_dir).join(DB_DIR);
fs::create_dir_all(&db_path).expect("Couldn't create wallet backend directory!");
fs::create_dir_all(&db_path)?;
let stored_tx_path = Path::new(data_file_dir).join(TX_SAVE_DIR);
fs::create_dir_all(&stored_tx_path)
.expect("Couldn't create wallet backend tx storage directory!");
fs::create_dir_all(&stored_tx_path)?;
let store = Store::new(
db_path.to_str().unwrap(),
@@ -279,7 +278,7 @@ where
/// Set parent key id by stored account name.
pub fn set_parent_key_id_by_name(&mut self, label: &str) -> Result<(), Error> {
let label = label.to_owned();
let res = self.acct_path_iter().find(|l| l.label == label);
let res = self.acct_path_iter()?.find(|l| l.label == label);
if let Some(a) = res {
self.set_parent_key_id(a.path);
Ok(())
@@ -312,7 +311,7 @@ where
}
/// Iterate over all output data stored by the backend.
pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = OutputData> + 'a> {
pub fn iter(&self) -> Result<impl Iterator<Item = OutputData>, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(OUTPUT_PREFIX), move |_, mut v| {
ser::deserialize(
@@ -322,12 +321,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
Ok(iter)
}
/// Get an (Optional) tx log entry by uuid.
@@ -338,7 +336,7 @@ where
}
/// Iterate over all tx log data stored by the backend.
pub fn tx_log_iter<'a>(&'a self) -> Box<dyn Iterator<Item = TxLogEntry> + 'a> {
pub fn tx_log_iter(&self) -> Result<impl Iterator<Item = TxLogEntry>, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(TX_LOG_ENTRY_PREFIX), move |_, mut v| {
ser::deserialize(
@@ -348,12 +346,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
Ok(iter)
}
/// Retrieve the private context associated with a given slate id.
@@ -381,7 +378,7 @@ where
}
/// Iterate over all stored account paths.
pub fn acct_path_iter<'a>(&'a self) -> Box<dyn Iterator<Item = AcctPathMapping> + 'a> {
pub fn acct_path_iter(&self) -> Result<impl Iterator<Item = AcctPathMapping>, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self
.db
@@ -393,12 +390,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
Box::new(iter)
Ok(iter)
}
/// Gets an account path for a given label.
@@ -475,10 +471,7 @@ where
}
/// Next child ID when we want to create a new output, based on current parent.
pub fn next_child(
&mut self,
keychain_mask: Option<&SecretKey>,
) -> Result<Identifier, Error> {
pub fn next_child(&mut self, keychain_mask: Option<&SecretKey>) -> Result<Identifier, Error> {
let parent_key_id = self.parent_key_id.clone();
let mut deriv_idx = {
let batch = self.db.batch()?;
@@ -581,7 +574,7 @@ where
}
/// Iterate over all output data stored by the backend.
pub fn iter(&'a self) -> impl Iterator<Item = OutputData> + 'a {
pub fn iter(&'a self) -> Result<impl Iterator<Item = OutputData> + 'a, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(OUTPUT_PREFIX), move |_, mut v| {
ser::deserialize(
@@ -591,12 +584,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
Ok(iter)
}
/// Delete data about an output from the backend.
@@ -666,7 +658,7 @@ where
}
/// Iterate over transactions data stored by the backend.
pub fn tx_log_iter(&'a self) -> impl Iterator<Item = TxLogEntry> + 'a {
pub fn tx_log_iter(&'a self) -> Result<impl Iterator<Item = TxLogEntry> + 'a, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self.db.iter(Some(TX_LOG_ENTRY_PREFIX), move |_, mut v| {
ser::deserialize(
@@ -676,12 +668,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
Ok(iter)
}
/// Save a transaction log entry.
@@ -714,7 +705,7 @@ where
}
/// Iterate over account names stored in backend.
pub fn acct_path_iter(&'a self) -> impl Iterator<Item = AcctPathMapping> + 'a {
pub fn acct_path_iter(&'a self) -> Result<impl Iterator<Item = AcctPathMapping> + 'a, Error> {
let protocol_version = self.db.protocol_version();
let prefix_iter = self
.db
@@ -726,12 +717,11 @@ where
)
.map_err(From::from)
});
let iter = prefix_iter
.expect("deserialize")
let iter = prefix_iter?
.into_iter()
.filter(|x| x.is_ok())
.map(|x| x.unwrap());
iter
Ok(iter)
}
/// Save an output as locked in the backend.
+3 -3
View File
@@ -54,7 +54,7 @@ where
C: NodeClient,
K: Keychain,
{
Ok(wallet.acct_path_iter().collect())
Ok(wallet.acct_path_iter()?.collect())
}
/// Adds a new parent account path with a given label
@@ -68,7 +68,7 @@ where
K: Keychain,
{
let label = label.to_owned();
if wallet.acct_path_iter().any(|l| l.label == label) {
if wallet.acct_path_iter()?.any(|l| l.label == label) {
return Err(Error::AccountLabelAlreadyExists(label));
}
@@ -76,7 +76,7 @@ where
// so find the highest of those, then increment (to conform with external/internal
// derivation chains in BIP32 spec)
let highest_entry = wallet.acct_path_iter().max_by(|a, b| {
let highest_entry = wallet.acct_path_iter()?.max_by(|a, b| {
<u32>::from(a.path.to_path().path[0]).cmp(&<u32>::from(b.path.to_path().path[0]))
});
+1 -1
View File
@@ -645,7 +645,7 @@ where
// restore labels, account paths and child derivation indices
wallet_lock!(wallet_inst, w);
let label_base = "account";
let accounts: Vec<Identifier> = w.acct_path_iter().map(|m| m.path).collect();
let accounts: Vec<Identifier> = w.acct_path_iter()?.map(|m| m.path).collect();
let mut acct_index = accounts.len();
for (path, max_child_index) in found_parents.iter() {
// Only restore paths that don't exist
+11 -11
View File
@@ -399,7 +399,7 @@ where
max_outputs,
selection_strategy_is_use_all,
parent_key_id,
);
)?;
// sender is responsible for setting the fee on the partial tx
// recipient should double-check the fee calculation and not blindly trust the
@@ -464,7 +464,7 @@ where
max_outputs,
selection_strategy_is_use_all,
parent_key_id,
)
)?
.1;
fee = tx_fee(coins.len(), num_outputs, 1);
total = coins.iter().map(|c| c.value).sum();
@@ -571,7 +571,7 @@ pub fn select_coins<C, K>(
max_outputs: usize,
select_all: bool,
parent_key_id: &Identifier,
) -> (usize, Vec<OutputData>)
) -> Result<(usize, Vec<OutputData>), Error>
// max_outputs_available, Outputs
where
C: NodeClient,
@@ -579,7 +579,7 @@ where
{
// first find all eligible outputs based on number of confirmations
let mut eligible = wallet
.iter()
.iter()?
.filter(|out| {
out.root_key_id == *parent_key_id
&& out.eligible_to_spend(current_height, minimum_confirmations)
@@ -603,7 +603,7 @@ where
for window in eligible.windows(max_outputs) {
let windowed_eligibles = window.to_vec();
if let Some(outputs) = select_from(amount, select_all, windowed_eligibles) {
return (max_available, outputs);
return Ok((max_available, outputs));
}
}
// Not exist in any window of which total amount >= amount.
@@ -614,20 +614,20 @@ where
"Extending maximum number of outputs. {} outputs selected.",
outputs.len()
);
return (max_available, outputs);
return Ok((max_available, outputs));
}
} else if let Some(outputs) = select_from(amount, select_all, eligible.clone()) {
return (max_available, outputs);
return Ok((max_available, outputs));
}
// we failed to find a suitable set of outputs to spend,
// so return the largest amount we can so we can provide guidance on what is
// possible
eligible.reverse();
(
Ok((
max_available,
eligible.iter().take(max_outputs).cloned().collect(),
)
))
}
fn select_from(amount: u64, select_all: bool, outputs: Vec<OutputData>) -> Option<Vec<OutputData>> {
@@ -684,7 +684,7 @@ where
let mut parts = vec![];
for (id, _, value) in &context.get_inputs() {
let input = wallet.iter().find(|out| out.key_id == *id);
let input = wallet.iter()?.find(|out| out.key_id == *id);
if let Some(i) = input {
if i.is_coinbase {
parts.push(build::coinbase_input(*value, i.key_id.clone()));
@@ -694,7 +694,7 @@ where
}
}
for (id, _, value) in &context.get_outputs() {
let output = wallet.iter().find(|out| out.key_id == *id);
let output = wallet.iter()?.find(|out| out.key_id == *id);
if let Some(i) = output {
parts.push(build::output(*value, i.key_id.clone()));
}
+12 -12
View File
@@ -52,7 +52,7 @@ where
{
// just read the wallet here, no need for a write lock
let mut outputs = wallet
.iter()
.iter()?
.filter(|out| show_spent || out.status != OutputStatus::Spent)
.collect::<Vec<_>>();
@@ -95,7 +95,7 @@ pub fn apply_advanced_tx_list_filtering<C, K>(
wallet: &mut WalletBackend<C, K>,
parent_key_id: Option<&Identifier>,
query_args: &RetrieveTxQueryArgs,
) -> Vec<TxLogEntry>
) -> Result<Vec<TxLogEntry>, Error>
where
C: NodeClient,
K: Keychain,
@@ -103,7 +103,7 @@ where
// Apply simple bool, GTE or LTE fields
let txs_iter: Box<dyn Iterator<Item = TxLogEntry>> = Box::new(
wallet
.tx_log_iter()
.tx_log_iter()?
.filter(|tx_entry| match parent_key_id {
Some(k) => tx_entry.parent_key_id == *k,
None => true,
@@ -322,7 +322,7 @@ where
return_txs = return_txs.into_iter().take(l as usize).collect()
}
return_txs
Ok(return_txs)
}
/// Retrieve all the transaction entries, or a particular entry
@@ -343,10 +343,10 @@ where
// Adding in new transaction list query logic. If `tx_id` or `tx_slate_id`
// is provided, then `query_args` is ignored and old logic is followed.
if query_args.is_some() && tx_id.is_none() && tx_slate_id.is_none() {
txs = apply_advanced_tx_list_filtering(wallet, parent_key_id, &query_args.unwrap())
txs = apply_advanced_tx_list_filtering(wallet, parent_key_id, &query_args.unwrap())?
} else {
txs = wallet
.tx_log_iter()
.tx_log_iter()?
.filter(|tx_entry| {
let f_pk = match parent_key_id {
Some(k) => tx_entry.parent_key_id == *k,
@@ -409,7 +409,7 @@ where
let mut wallet_outputs = HashMap::new();
let keychain = wallet.keychain(keychain_mask)?;
let unspents: Vec<OutputData> = wallet
.iter()
.iter()?
.filter(|x| x.root_key_id == *parent_key_id && x.status != OutputStatus::Spent)
.collect();
@@ -547,7 +547,7 @@ where
&& (output.status == OutputStatus::Unconfirmed
|| output.status == OutputStatus::Reverted)
{
let tx = batch.tx_log_iter().find(|t| {
let tx = batch.tx_log_iter()?.find(|t| {
Some(t.id) == output.tx_log_entry
&& t.parent_key_id == *parent_key_id
});
@@ -582,7 +582,7 @@ where
}
let mut txs_to_save = vec![];
for mut tx in batch.tx_log_iter() {
for mut tx in batch.tx_log_iter()? {
if reverted_kernels.contains(&tx.id) && tx.parent_key_id == *parent_key_id {
tx.tx_type = TxLogEntryType::TxReverted;
tx.reverted_after = tx.confirmation_ts.clone().and_then(|t| {
@@ -672,7 +672,7 @@ where
// Get corresponding kernels
let kernels = wallet
.tx_log_iter()
.tx_log_iter()?
.filter(|t| {
ids.contains(&t.id)
&& t.parent_key_id == *parent_key_id
@@ -707,7 +707,7 @@ where
return Ok(());
}
let mut ids_to_del = vec![];
for out in wallet.iter() {
for out in wallet.iter()? {
if out.status == OutputStatus::Unconfirmed
&& out.height > 0
&& out.height < height - 50
@@ -737,7 +737,7 @@ where
{
let current_height = wallet.last_confirmed_height()?;
let outputs = wallet
.iter()
.iter()?
.filter(|out| out.root_key_id == *parent_key_id);
let mut unspent_total = 0;