Skip to content

Commit

Permalink
feat(cli): resend confirmed spends if necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
maqi committed Jun 24, 2024
1 parent 1f89121 commit f46d4d7
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -995,12 +995,13 @@ jobs:
df
du -sh /home/runner/
- name: Confirm the cash_notes files
- name: Confirm the wallet files: cash_notes, confirmed_spends
run: |
pwd
ls $CLIENT_DATA_PATH/ -l
ls $CLIENT_DATA_PATH/wallet -l
ls $CLIENT_DATA_PATH/wallet/cash_notes -l
ls $CLIENT_DATA_PATH/wallet/confirmed_spends -l
ls $CLIENT_DATA_PATH/logs -l
env:
CLIENT_DATA_PATH: /home/runner/.local/share/safe/client
Expand Down
49 changes: 47 additions & 2 deletions sn_client/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,17 @@ impl WalletClient {
}
}

/// Resend previous confirmed spend.
async fn resend_confirmed_spend(&mut self, spend_addr: &SpendAddress) {
if let Ok(Some(spend)) = self.wallet.get_confirmed_spend(*spend_addr) {
let spend_vec = vec![spend];
let _ = self.client.send_spends(spend_vec.iter(), true).await;
} else {
warn!("Cann't find confirmed spend of {spend_addr:?}");
println!("Cann't find confirmed spend of {spend_addr:?}");
}
}

/// This is a blocking loop in cas there is pending transaction.
/// It will keeps resending the unconfirmed spend infinitely but explictly.
/// Function will only return on success (all unconfirmed spend uploaded),
Expand All @@ -689,12 +700,23 @@ impl WalletClient {

// Before re-sending, take a peek of un-confirmed spends first
// Helping user having a better view of what's happening.
let unconfirmed_spends_addrs: Vec<_> = self
let spends_to_check: BTreeMap<SpendAddress, BTreeSet<SpendAddress>> = self
.wallet
.unconfirmed_spend_requests()
.iter()
.map(|s| SpendAddress::from_unique_pubkey(&s.spend.unique_pubkey))
.map(|s| {
let parent_spends: BTreeSet<_> = s
.spend
.parent_tx
.inputs
.iter()
.map(|i| SpendAddress::from_unique_pubkey(&i.unique_pubkey))
.collect();
(s.address(), parent_spends)
})
.collect();
let unconfirmed_spends_addrs: Vec<_> = spends_to_check.keys().copied().collect();

for addr in unconfirmed_spends_addrs {
match self.client.peek_a_spend(addr).await {
Ok(_) => {
Expand All @@ -710,6 +732,29 @@ impl WalletClient {
println!(
"Unconfirmed Spend {addr:?} has no copy in the network yet {err:?} !"
);
// For those that still not even have one copy in network yet
// Check it's parent's status in network
if let Some(parent_spends) = spends_to_check.get(&addr) {
for parent_addr in parent_spends.iter() {
match self.client.peek_a_spend(*parent_addr).await {
Ok(_) => {
info!("Parent {parent_addr:?} of unconfirmed Spend {addr:?} is find having at least one copy in the network !");
println!("Parent {parent_addr:?} of unconfirmed Spend {addr:?} is find having at least one copy in the network !");
}
Err(err) => {
warn!(
"Parent {parent_addr:?} of unconfirmed Spend {addr:?} has no copy in the network yet {err:?} !"
);
println!(
"Parent {parent_addr:?} of unconfirmed Spend {addr:?} has no copy in the network yet {err:?} !"
);
// In theory, it shall be traversed back to re-send all ancestors.
// However, in practical, only track back one generation is enough.
self.resend_confirmed_spend(parent_addr).await;
}
}
}
}
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions sn_transfers/src/wallet/hot_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use super::{
data_payments::{PaymentDetails, PaymentQuote},
keys::{get_main_key_from_disk, store_new_keypair},
wallet_file::{
get_unconfirmed_spend_requests, load_created_cash_note, remove_cash_notes,
remove_unconfirmed_spend_requests, store_created_cash_notes,
get_confirmed_spend, get_unconfirmed_spend_requests, load_created_cash_note,
remove_cash_notes, remove_unconfirmed_spend_requests, store_created_cash_notes,
store_unconfirmed_spend_requests,
},
watch_only::WatchOnlyWallet,
Expand All @@ -23,8 +23,8 @@ use crate::{
cashnotes::UnsignedTransfer,
transfers::{CashNotesAndSecretKey, OfflineTransfer},
CashNote, CashNoteRedemption, DerivationIndex, DerivedSecretKey, MainPubkey, MainSecretKey,
NanoTokens, SignedSpend, Spend, SpendReason, Transaction, Transfer, UniquePubkey, WalletError,
NETWORK_ROYALTIES_PK,
NanoTokens, SignedSpend, Spend, SpendAddress, SpendReason, Transaction, Transfer, UniquePubkey,
WalletError, NETWORK_ROYALTIES_PK,
};
use std::{
collections::{BTreeMap, BTreeSet, HashSet},
Expand Down Expand Up @@ -109,6 +109,11 @@ impl HotWallet {
)
}

/// Get confirmed spend from disk.
pub fn get_confirmed_spend(&mut self, spend_addr: SpendAddress) -> Result<Option<SignedSpend>> {
get_confirmed_spend(self.watchonly_wallet.wallet_dir(), spend_addr)
}

/// Remove unconfirmed_spend_requests from disk.
fn remove_unconfirmed_spend_requests(&mut self) -> Result<()> {
remove_unconfirmed_spend_requests(
Expand Down
18 changes: 18 additions & 0 deletions sn_transfers/src/wallet/wallet_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ pub(super) fn remove_unconfirmed_spend_requests(
Ok(())
}

/// Returns `Some(SignedSpend)` or None if spend doesn't exist.
pub(super) fn get_confirmed_spend(
wallet_dir: &Path,
spend_addr: SpendAddress,
) -> Result<Option<SignedSpend>> {
let spends_dir = wallet_dir.join(CONFIRMED_SPENDS_DIR_NAME);
let spend_hex_name = spend_addr.to_hex();
let spend_file_path = spends_dir.join(spend_hex_name);
if !spend_file_path.is_file() {
return Ok(None);
}

let file = fs::File::open(&spend_file_path)?;
let confirmed_spend = rmp_serde::from_read(&file)?;

Ok(Some(confirmed_spend))
}

/// Returns `Some(Vec<SpendRequest>)` or None if file doesn't exist.
pub(super) fn get_unconfirmed_spend_requests(
wallet_dir: &Path,
Expand Down

0 comments on commit f46d4d7

Please sign in to comment.