diff --git a/src/faucet.rs b/src/faucet.rs index 056429d..42aa096 100644 --- a/src/faucet.rs +++ b/src/faucet.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, str::FromStr}; +use bitcoincore_rpc::bitcoin::secp256k1::PublicKey; use bitcoincore_rpc::json::{self as bitcoin_json}; use sdk_common::sp_client::bitcoin::secp256k1::{ rand::thread_rng, Keypair, Message as Secp256k1Message, Secp256k1, ThirtyTwoByteHash, @@ -29,6 +30,7 @@ use sdk_common::sp_client::spclient::Recipient; use anyhow::{Error, Result}; use crate::lock_freezed_utxos; +use crate::scan::check_transaction_alone; use crate::{scan::compute_partial_tweak_to_transaction, MutexExt, DAEMON, FAUCET_AMT, WALLET}; fn spend_from_core(dest: XOnlyPublicKey) -> Result<(Transaction, Amount)> { @@ -61,13 +63,10 @@ fn spend_from_core(dest: XOnlyPublicKey) -> Result<(Transaction, Amount)> { } } -fn faucet_send(sp_address: SilentPaymentAddress, commitment: &str) -> Result { - let mut first_tx: Option = None; - let final_tx: Transaction; - - let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?; +fn faucet_send(sp_address: SilentPaymentAddress, commitment: &str) -> Result<(Transaction, PublicKey)> { + let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?.get_wallet()?; // do we have a sp output available ? - let available_outpoints = sp_wallet.get_wallet()?.get_outputs().to_spendable_list(); + let available_outpoints = sp_wallet.get_outputs().to_spendable_list(); let available_amt = available_outpoints .iter() @@ -102,21 +101,33 @@ fn faucet_send(sp_address: SilentPaymentAddress, commitment: &str) -> Result Result Result Result { let sp_address = SilentPaymentAddress::try_from(msg.sp_address.as_str())?; log::debug!("Sending bootstrap coins to {}", sp_address); // send bootstrap coins to this sp_address - let tx = faucet_send(sp_address, &msg.commitment)?; + let (tx, partial_tweak) = faucet_send(sp_address, &msg.commitment)?; - // get the tweak - let partial_tweak = compute_partial_tweak_to_transaction(&tx)?; - - // get current blockheight - let blkheight: u32 = DAEMON - .get() - .unwrap() - .lock_anyhow()? - .get_current_height()? - .try_into()?; - - let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?; - - // update our sp_client with the change output(s) - sp_wallet - .get_wallet()? - .update_wallet_with_transaction(&tx, blkheight, partial_tweak)?; - - log::debug!("updated the wallet"); - // save to disk - sp_wallet.save()?; - - log::debug!("saved the wallet"); Ok(NewTxMessage::new( serialize(&tx).to_lower_hex_string(), Some(partial_tweak.to_string()), diff --git a/src/main.rs b/src/main.rs index 78c9f4f..7300827 100644 --- a/src/main.rs +++ b/src/main.rs @@ -250,11 +250,8 @@ fn create_new_tx_message(transaction: Vec) -> Result { let partial_tweak = compute_partial_tweak_to_transaction(&tx)?; - let found = check_transaction_alone(&tx, &partial_tweak)?; - - if found.len() > 0 { - debug!("Found {} modified outputs in {}", found.len(), tx.txid()); - } + let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?; + check_transaction_alone(sp_wallet.get_wallet()?, &tx, &partial_tweak)?; Ok(NewTxMessage::new( transaction.to_lower_hex_string(), diff --git a/src/scan.rs b/src/scan.rs index 37a1b91..12e889c 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::str::FromStr; +use std::sync::MutexGuard; use anyhow::{Error, Result}; use electrum_client::ElectrumApi; @@ -12,7 +13,7 @@ use sdk_common::sp_client::silentpayments::receiving::Receiver; use sdk_common::sp_client::silentpayments::utils::receiving::{ calculate_tweak_data, get_pubkey_from_input, }; -use sdk_common::sp_client::spclient::{OutputSpendStatus, OwnedOutput}; +use sdk_common::sp_client::spclient::{OutputSpendStatus, OwnedOutput, SpWallet}; use tokio::time::Instant; use crate::{electrumclient, MutexExt, DAEMON, WALLET}; @@ -21,6 +22,7 @@ pub fn compute_partial_tweak_to_transaction(tx: &Transaction) -> Result = Vec::with_capacity(tx.input.len()); let mut pubkeys: Vec = Vec::with_capacity(tx.input.len()); + // TODO we should cache transactions to prevent multiple rpc request when transaction spends multiple outputs from the same tx for input in tx.input.iter() { outpoints.push(( input.previous_output.txid.to_string(), @@ -82,9 +84,8 @@ fn get_script_to_secret_map( Ok(res) } -pub fn check_transaction_alone(tx: &Transaction, tweak_data: &PublicKey) -> Result> { - let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?; - let updates = match sp_wallet.get_wallet()?.update_wallet_with_transaction(tx, 0, *tweak_data) { +pub fn check_transaction_alone(mut wallet: MutexGuard, tx: &Transaction, tweak_data: &PublicKey) -> Result> { + let updates = match wallet.update_wallet_with_transaction(tx, 0, *tweak_data) { Ok(updates) => updates, Err(e) => { log::debug!("Error while checking transaction: {}", e); @@ -234,6 +235,7 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res let electrum_client = electrumclient::create_electrum_client(electrum_url)?; let sp_wallet = WALLET.get().ok_or(Error::msg("Wallet not initialized"))?; + let mut wallet = sp_wallet.get_wallet()?; let core = DAEMON .get() @@ -241,7 +243,7 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res .lock_anyhow()?; let secp = Secp256k1::new(); - let scan_height = sp_wallet.get_wallet()?.get_outputs().get_last_scan(); + let scan_height = wallet.get_outputs().get_last_scan(); let tip_height: u32 = core.get_current_height()?.try_into()?; // 0 means scan to tip @@ -268,9 +270,9 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res let mut tweak_data_map = electrum_client.sp_tweaks(start as usize)?; - let scan_sk = sp_wallet.get_wallet()?.get_client().get_scan_key(); + let scan_sk = wallet.get_client().get_scan_key(); - let sp_receiver = sp_wallet.get_wallet()?.get_client().sp_receiver.clone(); + let sp_receiver = wallet.get_client().sp_receiver.clone(); let start_time = Instant::now(); for (blkheight, blkhash, blkfilter) in filters { @@ -286,7 +288,7 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res // check if owned inputs are spent let our_outputs: HashMap = - sp_wallet.get_wallet()?.get_outputs().to_outpoints_list(); + wallet.get_outputs().to_outpoints_list(); let owned_spks: Result>> = our_outputs .iter() @@ -305,8 +307,7 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res let utxo_created_in_block = scan_block_outputs(&sp_receiver, &blk.txdata, blkheight.into(), spk2secret)?; if !utxo_created_in_block.is_empty() { - sp_wallet - .get_wallet()? + wallet .get_mut_outputs() .extend_from(utxo_created_in_block); } @@ -314,12 +315,11 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res // update the list of outputs just in case // utxos may be created and destroyed in the same block let updated_outputs: HashMap = - sp_wallet.get_wallet()?.get_outputs().to_outpoints_list(); + wallet.get_outputs().to_outpoints_list(); // search inputs and mark as mined let utxo_destroyed_in_block = scan_block_inputs(updated_outputs, blk.txdata)?; if !utxo_destroyed_in_block.is_empty() { - let mut wallet = sp_wallet.get_wallet()?; let outputs = wallet.get_mut_outputs(); for outpoint in utxo_destroyed_in_block { outputs.mark_mined(outpoint, blkhash)?; @@ -335,8 +335,7 @@ pub fn scan_blocks(mut n_blocks_to_scan: u32, electrum_url: &str) -> anyhow::Res ); // update last_scan height - sp_wallet - .get_wallet()? + wallet .get_mut_outputs() .update_last_scan(end); WALLET.get().unwrap().save(wallet)?;