From ae20ba2b6326f4adab96e21c8f283d22797df9d5 Mon Sep 17 00:00:00 2001 From: Thibault Martinez Date: Wed, 1 Nov 2023 14:18:28 +0100 Subject: [PATCH] More changes --- sdk/src/wallet/operations/output_finder.rs | 285 ------------------ sdk/src/wallet/operations/syncing/outputs.rs | 12 +- .../wallet/operations/syncing/transactions.rs | 20 +- sdk/tests/wallet/balance.rs | 8 +- 4 files changed, 19 insertions(+), 306 deletions(-) delete mode 100644 sdk/src/wallet/operations/output_finder.rs diff --git a/sdk/src/wallet/operations/output_finder.rs b/sdk/src/wallet/operations/output_finder.rs deleted file mode 100644 index 39f8b44817..0000000000 --- a/sdk/src/wallet/operations/output_finder.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2021 IOTA Stiftung -// SPDX-License-Identifier: Apache-2.0 - -use std::cmp; - -use crate::{ - client::secret::{GenerateAddressOptions, SecretManage}, - wallet::account::{operations::syncing::SyncOptions, types::AddressWithUnspentOutputs, Account}, -}; - -impl Account -where - crate::wallet::Error: From, - crate::client::Error: From, -{ - /// Search addresses with unspent outputs - /// `address_gap_limit`: The number of addresses to search for, after the last address with unspent outputs - /// Addresses that got crated during this operation and have a higher key_index than the latest one with outputs, - /// will be removed again, to keep the wallet size smaller - pub(crate) async fn search_addresses_with_outputs( - &self, - mut address_gap_limit: u32, - sync_options: Option, - ) -> crate::wallet::Result { - log::debug!("[search_addresses_with_outputs]"); - let mut sync_options = match sync_options { - Some(opt) => opt, - None => self.default_sync_options().await.clone(), - }; - - // store the current index, so we can remove new addresses with higher indexes later again, if they don't have - // outputs - let (highest_public_address_index, highest_internal_address_index) = { - let account_details = self.details().await; - ( - account_details - .public_addresses - .last() - .map(|a| a.key_index) - .expect("account needs to have a public address"), - account_details.internal_addresses.last().map(|a| a.key_index), - ) - }; - - // public addresses - if sync_options.address_start_index != 0 { - let mut address_amount_to_generate = - sync_options.address_start_index.abs_diff(highest_public_address_index); - // -1 if it's larger than 0, to get the correct amount, because the address with the actual start index - // gets generated later - address_amount_to_generate = address_amount_to_generate.saturating_sub(1); - log::debug!( - "[search_addresses_with_outputs] generate {address_amount_to_generate} public addresses below the start index" - ); - self.generate_ed25519_addresses(address_amount_to_generate, None) - .await?; - } - // internal addresses - if sync_options.address_start_index_internal != 0 { - let mut address_amount_to_generate = sync_options - .address_start_index_internal - .abs_diff(highest_internal_address_index.unwrap_or(0)); - // -1 if it's larger than 0, to get the correct amount, because the address with the actual start index - // gets generated later - if address_amount_to_generate > 0 && highest_internal_address_index.is_some() { - address_amount_to_generate -= 1; - } - log::debug!( - "[search_addresses_with_outputs] generate {address_amount_to_generate} internal addresses below the start index" - ); - self.generate_ed25519_addresses(address_amount_to_generate, Some(GenerateAddressOptions::internal())) - .await?; - } - - let mut address_gap_limit_internal = address_gap_limit; - - let mut latest_outputs_count = 0; - loop { - // Also needs to be in the loop so it gets updated every round for internal use without modifying the values - // outside - let (highest_public_address_index, highest_internal_address_index) = { - let account_details = self.details().await; - ( - account_details - .public_addresses - .last() - .map(|a| a.key_index) - .expect("account needs to have a public address"), - account_details.internal_addresses.last().map(|a| a.key_index), - ) - }; - log::debug!( - "[search_addresses_with_outputs] address_gap_limit: {address_gap_limit}, address_gap_limit_internal: {address_gap_limit_internal}" - ); - // generate public and internal addresses - let addresses = self.generate_ed25519_addresses(address_gap_limit, None).await?; - let internal_addresses = self - .generate_ed25519_addresses(address_gap_limit_internal, Some(GenerateAddressOptions::internal())) - .await?; - - let address_start_index = addresses - .first() - .map(|a| { - // If the index is 1, then we only have the single address before we got during account creation - // To also sync that, we set the index to 0 - if a.key_index == 1 { 0 } else { a.key_index } - }) - // +1, because we don't want to sync the latest address again - .unwrap_or(highest_public_address_index + 1); - - let address_start_index_internal = internal_addresses - .first() - .map(|a| a.key_index) - // +1, because we don't want to sync the latest address again - .unwrap_or_else(|| highest_internal_address_index.unwrap_or(0) + 1); - - sync_options.force_syncing = true; - sync_options.address_start_index = address_start_index; - sync_options.address_start_index_internal = address_start_index_internal; - self.sync(Some(sync_options.clone())).await?; - - let output_count = self.details().await.unspent_outputs.len(); - - // break if we didn't find more outputs with the new addresses - if output_count <= latest_outputs_count { - break; - } - - latest_outputs_count = output_count; - - // Update address_gap_limit to only generate the amount of addresses we need to have `address_gap_limit` - // amount of empty addresses after the latest one with outputs - - let account_details = self.details().await; - - let highest_address_index = account_details - .public_addresses - .iter() - .max_by_key(|a| *a.key_index()) - .map(|a| *a.key_index()) - .expect("account needs to have at least one public address"); - - let highest_address_index_internal = account_details - .internal_addresses - .iter() - .max_by_key(|a| *a.key_index()) - .map(|a| *a.key_index()) - .unwrap_or(0); - - drop(account_details); - - let addresses_with_unspent_outputs = self.addresses_with_unspent_outputs().await?; - - let (addresses_with_outputs_internal, address_with_outputs): ( - Vec<&AddressWithUnspentOutputs>, - Vec<&AddressWithUnspentOutputs>, - ) = addresses_with_unspent_outputs.iter().partition(|a| a.internal); - - let latest_address_index_with_outputs = address_with_outputs - .iter() - .max_by_key(|a| *a.key_index()) - .map(|a| *a.key_index() as i64) - // -1 as default, because we will subtract this value and want to have the amount of empty addresses in - // a row and not the address index - .unwrap_or(-1); - - let latest_address_index_with_outputs_internal = addresses_with_outputs_internal - .iter() - .max_by_key(|a| *a.key_index()) - .map(|a| *a.key_index() as i64) - // -1 as default, because we will subtract this value and want to have the amount of empty addresses in - // a row and not the address index - .unwrap_or(-1); - - log::debug!( - "new highest_address_index: {highest_address_index}, internal: {highest_address_index_internal}" - ); - log::debug!( - "new latest_address_index_with_outputs: {latest_address_index_with_outputs:?}, internal: {latest_address_index_with_outputs_internal:?}" - ); - - let empty_addresses_in_row = (highest_address_index as i64 - latest_address_index_with_outputs) as u32; - - let empty_addresses_in_row_internal = - (highest_address_index_internal as i64 - latest_address_index_with_outputs_internal) as u32; - - log::debug!( - "new empty_addresses_in_row: {empty_addresses_in_row}, internal: {empty_addresses_in_row_internal}" - ); - - if empty_addresses_in_row > address_gap_limit { - log::debug!("empty_addresses_in_row: {empty_addresses_in_row}, setting address_gap_limit to 0"); - address_gap_limit = 0; - } else { - address_gap_limit -= empty_addresses_in_row; - } - if empty_addresses_in_row_internal > address_gap_limit_internal { - log::debug!( - "empty_addresses_in_row_internal: {empty_addresses_in_row_internal}, setting address_gap_limit_internal to 0" - ); - address_gap_limit_internal = 0; - } else { - address_gap_limit_internal -= empty_addresses_in_row_internal; - } - - log::debug!("new address_gap_limit: {address_gap_limit}, internal: {address_gap_limit_internal}"); - - if address_gap_limit == 0 && address_gap_limit_internal == 0 { - break; - } - } - - self.clean_account_after_recovery(highest_public_address_index, highest_internal_address_index) - .await; - - #[cfg(feature = "storage")] - { - log::debug!( - "[search_addresses_with_outputs] storing account {} with new synced data", - self.alias().await - ); - self.save(None).await?; - } - - Ok(latest_outputs_count) - } - - /// During search_addresses_with_outputs we created new addresses that don't have funds, so we remove them again. - // `old_highest_public_address_index` is not optional, because we need to have at least one public address in the - // account - async fn clean_account_after_recovery( - &self, - old_highest_public_address_index: u32, - old_highest_internal_address_index: Option, - ) { - let mut account_details = self.details_mut().await; - - let (internal_addresses_with_unspent_outputs, public_addresses_with_spent_outputs): ( - Vec<&AddressWithUnspentOutputs>, - Vec<&AddressWithUnspentOutputs>, - ) = account_details - .addresses_with_unspent_outputs() - .iter() - .partition(|address| address.internal); - - let highest_public_index_with_outputs = public_addresses_with_spent_outputs - .iter() - .map(|a| a.key_index) - .max() - // We want to have at least one public address - .unwrap_or(0); - - let highest_internal_index_with_outputs = internal_addresses_with_unspent_outputs - .iter() - .map(|a| a.key_index) - .max(); - - // The new highest index should be either the old one before we searched for funds or if we found addresses with - // funds the highest index from an address with outputs - let new_latest_public_index = cmp::max(highest_public_index_with_outputs, old_highest_public_address_index); - account_details.public_addresses = account_details - .public_addresses - .clone() - .into_iter() - .filter(|a| a.key_index <= new_latest_public_index) - .collect(); - - account_details.internal_addresses = - if old_highest_internal_address_index.is_none() && highest_internal_index_with_outputs.is_none() { - // For internal addresses we don't leave an empty address, that's only required for the public address - Vec::new() - } else { - let new_latest_internal_index = cmp::max( - highest_internal_index_with_outputs.unwrap_or(0), - old_highest_internal_address_index.unwrap_or(0), - ); - account_details - .internal_addresses - .clone() - .into_iter() - .filter(|a| a.key_index <= new_latest_internal_index) - .collect() - }; - } -} diff --git a/sdk/src/wallet/operations/syncing/outputs.rs b/sdk/src/wallet/operations/syncing/outputs.rs index cfcb5f4163..ce4e3c3928 100644 --- a/sdk/src/wallet/operations/syncing/outputs.rs +++ b/sdk/src/wallet/operations/syncing/outputs.rs @@ -128,15 +128,13 @@ where ) -> crate::wallet::Result<()> { log::debug!("[SYNC] request_incoming_transaction_data"); - let account_details = self.data().await; + let wallet_data = self.data().await; transaction_ids.retain(|transaction_id| { - !(account_details.transactions.contains_key(transaction_id) - || account_details.incoming_transactions.contains_key(transaction_id) - || account_details - .inaccessible_incoming_transactions - .contains(transaction_id)) + !(wallet_data.transactions.contains_key(transaction_id) + || wallet_data.incoming_transactions.contains_key(transaction_id) + || wallet_data.inaccessible_incoming_transactions.contains(transaction_id)) }); - drop(account_details); + drop(wallet_data); // Limit parallel requests to 100, to avoid timeouts let results = diff --git a/sdk/src/wallet/operations/syncing/transactions.rs b/sdk/src/wallet/operations/syncing/transactions.rs index 791927c21f..15febb3bfb 100644 --- a/sdk/src/wallet/operations/syncing/transactions.rs +++ b/sdk/src/wallet/operations/syncing/transactions.rs @@ -30,13 +30,13 @@ where /// be synced again pub(crate) async fn sync_pending_transactions(&self) -> crate::wallet::Result { log::debug!("[SYNC] sync pending transactions"); - let account_details = self.data().await; + let wallet_data = self.data().await; // only set to true if a transaction got confirmed for which we don't have an output // (transaction_output.is_none()) let mut confirmed_unknown_output = false; - if account_details.pending_transactions.is_empty() { + if wallet_data.pending_transactions.is_empty() { return Ok(confirmed_unknown_output); } @@ -49,9 +49,9 @@ where let mut output_ids_to_unlock = Vec::new(); let mut transactions_to_reissue = Vec::new(); - for transaction_id in &account_details.pending_transactions { + for transaction_id in &wallet_data.pending_transactions { log::debug!("[SYNC] sync pending transaction {transaction_id}"); - let transaction = account_details + let transaction = wallet_data .transactions .get(transaction_id) // panic during development to easier detect if something is wrong, should be handled different later @@ -65,14 +65,14 @@ where // check if we have an output (remainder, if not sending to an own address) that got created by this // transaction, if that's the case, then the transaction got confirmed - let transaction_output = account_details + let transaction_output = wallet_data .outputs .keys() .find(|o| o.transaction_id() == transaction_id); if let Some(transaction_output) = transaction_output { // Save to unwrap, we just got the output - let confirmed_output_data = account_details.outputs.get(transaction_output).expect("output exists"); + let confirmed_output_data = wallet_data.outputs.get(transaction_output).expect("output exists"); log::debug!( "[SYNC] confirmed transaction {transaction_id} in block {}", confirmed_output_data.metadata.block_id() @@ -91,7 +91,7 @@ where let mut input_got_spent = false; for input in transaction.payload.transaction().inputs() { let Input::Utxo(input) = input; - if let Some(input) = account_details.outputs.get(input.output_id()) { + if let Some(input) = wallet_data.outputs.get(input.output_id()) { if input.is_spent { input_got_spent = true; } @@ -153,7 +153,7 @@ where // no need to reissue if one input got spent if input_got_spent { process_transaction_with_unknown_state( - &account_details, + &wallet_data, transaction, &mut updated_transactions, &mut output_ids_to_unlock, @@ -172,7 +172,7 @@ where // no need to reissue if one input got spent if input_got_spent { process_transaction_with_unknown_state( - &account_details, + &wallet_data, transaction, &mut updated_transactions, &mut output_ids_to_unlock, @@ -198,7 +198,7 @@ where } } } - drop(account_details); + drop(wallet_data); for mut transaction in transactions_to_reissue { log::debug!("[SYNC] reissue transaction"); diff --git a/sdk/tests/wallet/balance.rs b/sdk/tests/wallet/balance.rs index b193f1c6e6..a1e80c01f9 100644 --- a/sdk/tests/wallet/balance.rs +++ b/sdk/tests/wallet/balance.rs @@ -248,8 +248,8 @@ async fn balance_voting_power() -> Result<()> { let balance = wallet.sync(None).await?; assert_eq!(balance.base_coin().total(), faucet_amount); assert_eq!(balance.base_coin().available(), faucet_amount - voting_power); - let account_voting_power = wallet.get_voting_power().await?; - assert_eq!(account_voting_power, voting_power); + let wallet_voting_power = wallet.get_voting_power().await?; + assert_eq!(wallet_voting_power, voting_power); // Increase voting power to total amount let tx = wallet.increase_voting_power(faucet_amount - voting_power).await?; @@ -259,8 +259,8 @@ async fn balance_voting_power() -> Result<()> { let balance = wallet.sync(None).await?; assert_eq!(balance.base_coin().total(), faucet_amount); assert_eq!(balance.base_coin().available(), 0); - let account_voting_power = wallet.get_voting_power().await?; - assert_eq!(account_voting_power, faucet_amount); + let wallet_voting_power = wallet.get_voting_power().await?; + assert_eq!(wallet_voting_power, faucet_amount); tear_down(storage_path)?; Ok(())