diff --git a/Cargo.lock b/Cargo.lock index d71f2f5..d5b92e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -939,9 +939,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -1097,7 +1097,7 @@ dependencies = [ "rand", "serde", "serde_json", - "sp_backend", + "sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-backend?branch=sp_client)", "tsify", "uuid", "wasm-bindgen", @@ -1118,7 +1118,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sp_backend", + "sp_client 0.1.0 (git+https://github.com/Sosthene00/sp-client?branch=sp_client)", "tokio", "tokio-stream", "tokio-tungstenite", @@ -1168,18 +1168,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", @@ -1299,9 +1299,21 @@ dependencies = [ ] [[package]] -name = "sp_backend" +name = "sp_client" version = "0.1.0" -source = "git+https://github.com/Sosthene00/sp-backend?branch=sp_client#d09c92ac7718c34c9ce3ff55298140ebda0b2e1a" +source = "git+https://github.com/Sosthene00/sp-backend?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26" +dependencies = [ + "anyhow", + "bitcoin 0.31.2", + "serde", + "serde_json", + "silentpayments", +] + +[[package]] +name = "sp_client" +version = "0.1.0" +source = "git+https://github.com/Sosthene00/sp-client?branch=sp_client#4eaa51ed306fc939f487c0e14208bf36da8c8a26" dependencies = [ "anyhow", "bitcoin 0.31.2", @@ -1330,9 +1342,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", diff --git a/src/main.rs b/src/main.rs index 266cdb2..e115aaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,11 @@ use std::{ - any::Any, collections::HashMap, env, fmt::Debug, net::SocketAddr, str::FromStr, sync::{Arc, Mutex, MutexGuard} + any::Any, + collections::HashMap, + env, + fmt::Debug, + net::SocketAddr, + str::FromStr, + sync::{Arc, Mutex, MutexGuard}, }; use bitcoincore_rpc::json::{self as bitcoin_json}; @@ -20,8 +26,8 @@ use sp_client::bitcoin::{ use sp_client::{ bitcoin::secp256k1::{ rand::{thread_rng, Rng}, - Error as Secp256k1Error, Keypair, Message as Secp256k1Message, PublicKey, Scalar, - Secp256k1, SecretKey, ThirtyTwoByteHash, + Keypair, Message as Secp256k1Message, PublicKey, + Secp256k1, ThirtyTwoByteHash, }, spclient::SpWallet, }; @@ -86,6 +92,7 @@ impl SilentPaymentWallet { enum BroadcastType { Sender(SocketAddr), + #[allow(dead_code)] ExcludeSender(SocketAddr), #[allow(dead_code)] ToAll, @@ -169,55 +176,6 @@ fn spend_from_core( } } -fn find_owned_outputs( - tx: &Transaction, - ours: HashMap, HashMap>, -) -> Result> { - let mut res: HashMap = HashMap::new(); - for (label, map) in ours { - res.extend(tx.output.iter().enumerate().filter_map( - |(i, o)| match XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..]) { - Ok(key) => { - if let Some(scalar) = map.get(&key) { - match SecretKey::from_slice(&scalar.to_be_bytes()) { - Ok(tweak) => { - let outpoint = OutPoint { - txid: tx.txid(), - vout: i as u32, - }; - let label_str: Option; - if let Some(l) = &label { - label_str = - Some(l.as_inner().to_be_bytes().to_lower_hex_string()); - } else { - label_str = None; - } - return Some(( - outpoint, - OwnedOutput { - blockheight: 0, - tweak: tweak.secret_bytes().to_lower_hex_string(), - amount: o.value, - script: o.script_pubkey.as_bytes().to_lower_hex_string(), - label: label_str, - spend_status: OutputSpendStatus::Unspent, - }, - )); - } - Err(_) => { - return None; - } - } - } - None - } - Err(_) => None, - }, - )); - } - Ok(res) -} - fn faucet_send( sp_address: SilentPaymentAddress, sp_wallet: Arc, @@ -225,7 +183,6 @@ fn faucet_send( ) -> Result { let mut first_tx: Option = None; let final_tx: Transaction; - let mut new_outpoints: HashMap; // do we have a sp output available ? let available_outpoints = sp_wallet.get_wallet()?.get_outputs().to_spendable_list(); @@ -270,7 +227,8 @@ fn faucet_send( .create_new_psbt(inputs.clone(), vec![recipient], None)?; log::debug!("Created psbt: {}", new_psbt); SpClient::set_fees(&mut new_psbt, fee_estimate, sp_address.into())?; - wallet.get_client().fill_sp_outputs(&mut new_psbt)?; + let partial_secret = wallet.get_client().get_partial_secret_from_psbt(&new_psbt)?; + wallet.get_client().fill_sp_outputs(&mut new_psbt, partial_secret)?; log::debug!("Definitive psbt: {}", new_psbt); let mut aux_rand = [0u8; 32]; thread_rng().fill(&mut aux_rand); @@ -279,46 +237,6 @@ fn faucet_send( SpClient::finalize_psbt(&mut signed)?; final_tx = signed.extract_tx()?; - - // take all we need to register the new sp output - let outpoints: Vec<(String, u32)> = final_tx - .input - .iter() - .map(|i| (i.previous_output.txid.to_string(), i.previous_output.vout)) - .collect(); - - let our_sp_address: SilentPaymentAddress = wallet - .get_client() - .sp_receiver - .get_receiving_address() - .try_into()?; - let our_spend_pubkey = our_sp_address.get_spend_key(); - let secp = Secp256k1::verification_only(); - let input_pubkeys: Result, Secp256k1Error> = inputs - .iter() - .map(|(_, o)| { - let tweak = SecretKey::from_str(&o.tweak)?; - our_spend_pubkey.mul_tweak(&secp, &tweak.into()) - }) - .collect(); - let input_pubkeys = input_pubkeys?; - let input_pubkeys: Vec<&PublicKey> = input_pubkeys.iter().collect(); - let partial_tweak = calculate_tweak_data(&input_pubkeys, &outpoints)?; - let ecdh_shared_secret = - calculate_shared_secret(partial_tweak, wallet.get_client().get_scan_key())?; - - let outputs_to_check: Result, Secp256k1Error> = final_tx - .output - .iter() - .map(|o| XOnlyPublicKey::from_slice(&o.script_pubkey.as_bytes()[2..])) - .collect(); - - let ours = wallet - .get_client() - .sp_receiver - .scan_transaction(&ecdh_shared_secret, outputs_to_check?)?; - - new_outpoints = find_owned_outputs(&final_tx, ours)?; } else { // let's try to spend directly from the mining address let secp = Secp256k1::signing_only(); @@ -329,7 +247,7 @@ fn faucet_send( spend_from_core(keypair.x_only_public_key().0, shared_daemon.clone())?; // check that the first output of the transaction pays to the key we just created - assert!( + debug_assert!( core_tx.output[0].script_pubkey == ScriptBuf::new_p2tr_tweaked( keypair.x_only_public_key().0.dangerous_assume_tweaked() @@ -411,40 +329,12 @@ fn faucet_send( faucet_tx.input[0].witness.push(final_sig.to_vec()); - // take all we need to register the new sp output - let outpoints: Vec<(String, u32)> = vec![(core_tx.txid().to_string(), 0)]; - first_tx = Some(core_tx); - let client = sp_wallet.get_wallet()?; - - let input_pubkey = &keypair.public_key(); - - let input_pub_keys: Vec<&PublicKey> = vec![input_pubkey]; - let partial_tweak = calculate_tweak_data(&input_pub_keys, &outpoints)?; - let ecdh_shared_secret = - calculate_shared_secret(partial_tweak, client.get_client().get_scan_key())?; - - let p2tr_outs = vec![ext_output_key, change_output_key]; - - let ours = client - .get_client() - .sp_receiver - .scan_transaction(&ecdh_shared_secret, p2tr_outs)?; - final_tx = faucet_tx; - - new_outpoints = find_owned_outputs(&final_tx, ours)?; } if let Ok(daemon) = shared_daemon.lock() { - // get current blockheight - let blkheight: u32 = daemon.get_current_height()?.try_into()?; - // update the new outpoints - for o in new_outpoints.iter_mut() { - o.1.blockheight = blkheight; - } - // broadcast one or two transactions if first_tx.is_some() { daemon.broadcast(&first_tx.unwrap())?; @@ -454,17 +344,6 @@ fn faucet_send( return Err(Error::msg("Failed to lock daemon")); } - // update our sp_client with the change output(s) - sp_wallet - .get_wallet()? - .get_mut_outputs() - .extend_from(new_outpoints); - - debug!("{:?}", sp_wallet); - - // save to disk - sp_wallet.save()?; - Ok(final_tx) } @@ -476,9 +355,33 @@ fn handle_faucet_request( if let Ok(sp_address) = SilentPaymentAddress::try_from(msg) { debug!("Sending bootstrap coins to {}", sp_address); // send bootstrap coins to this sp_address - let tx = faucet_send(sp_address, sp_wallet, shared_daemon.clone())?; - let partial_tweak = - compute_partial_tweak_to_transaction(tx.clone(), shared_daemon.clone())?; + let tx = faucet_send(sp_address, sp_wallet.clone(), shared_daemon.clone())?; + + // get the tweak + let partial_tweak = compute_partial_tweak_to_transaction(&tx, shared_daemon.clone())?; + + debug!("Got the tweak"); + + // get current blockheight + let blkheight: u32 = shared_daemon + .lock_anyhow()? + .get_current_height()? + .try_into()?; + + // update our sp_client with the change output(s) + sp_wallet.get_wallet()?.update_wallet_with_transaction( + &tx, + blkheight, + partial_tweak, + )?; + + debug!("{:?}", sp_wallet.get_wallet()?.get_outputs()); + + debug!("updated the wallet"); + // save to disk + sp_wallet.save()?; + + debug!("saved the wallet"); Ok(NewTxMessage::new( serialize(&tx).to_lower_hex_string(), Some(partial_tweak.to_string()), @@ -594,19 +497,18 @@ async fn handle_connection( } fn compute_partial_tweak_to_transaction( - tx: Transaction, + tx: &Transaction, daemon: Arc>, ) -> Result { let mut outpoints: Vec<(String, u32)> = Vec::with_capacity(tx.input.len()); let mut pubkeys: Vec = Vec::with_capacity(tx.input.len()); - for input in tx.input { + for input in tx.input.iter() { outpoints.push(( input.previous_output.txid.to_string(), input.previous_output.vout, )); let prev_tx = daemon - .lock() - .map_err(|e| Error::msg(format!("Failed to lock the daemon: {}", e)))? + .lock_anyhow()? .get_transaction(&input.previous_output.txid, None) .map_err(|e| Error::msg(format!("Failed to find previous transaction: {}", e)))?; @@ -642,7 +544,7 @@ fn create_new_tx_message(transaction: Vec, daemon: Arc>) -> Re return Err(Error::msg("Can't process coinbase transaction")); } - let partial_tweak = compute_partial_tweak_to_transaction(tx, daemon)?; + let partial_tweak = compute_partial_tweak_to_transaction(&tx, daemon)?; Ok(NewTxMessage::new( transaction.to_lower_hex_string(), Some(partial_tweak.to_string()), @@ -666,35 +568,30 @@ async fn handle_zmq(peers: PeerMap, shared_daemon: SharedDaemon) { }; debug!("Received a message"); - let payload: String = - if let (Some(topic), Some(data)) = (core_msg.get(0), core_msg.get(1)) { - match std::str::from_utf8(&topic) { - Ok("rawtx") => { - match create_new_tx_message(data.to_vec(), shared_daemon.clone()) { - Ok(m) => serde_json::to_string(&m).expect("This shouldn't fail"), - Err(e) => { - error!("{}", e); - continue; - } - } - } - Ok("hashblock") => todo!(), - _ => { - error!("Unexpected message in zmq"); + let payload: String = if let (Some(topic), Some(data)) = (core_msg.get(0), core_msg.get(1)) + { + match std::str::from_utf8(&topic) { + Ok("rawtx") => match create_new_tx_message(data.to_vec(), shared_daemon.clone()) { + Ok(m) => serde_json::to_string(&m).expect("This shouldn't fail"), + Err(e) => { + error!("{}", e); continue; } + }, + Ok("hashblock") => todo!(), + _ => { + error!("Unexpected message in zmq"); + continue; } - } else { - error!("Empty message"); - continue; - }; + } + } else { + error!("Empty message"); + continue; + }; - if let Err(e) = broadcast_message( - peers.clone(), - AnkFlag::NewTx, - payload, - BroadcastType::ToAll, - ) { + if let Err(e) = + broadcast_message(peers.clone(), AnkFlag::NewTx, payload, BroadcastType::ToAll) + { log::error!("{}", e.to_string()); } }