From e1505375387907c980c37ee5fc860a43013bb6ff Mon Sep 17 00:00:00 2001 From: GeneFerneau Date: Fri, 30 Jul 2021 06:03:58 +0000 Subject: [PATCH] WalletSwapCoin: Split into two classes + add trait Convert WalletSwapCoin into Incoming/OutgoingSwapCoin, and add a WalletSwapCoin trait for common functionality --- src/contracts.rs | 127 ++++++++++++--- src/main.rs | 54 +++---- src/maker_protocol.rs | 48 ++++-- src/messages.rs | 4 +- src/taker_protocol.rs | 27 ++-- src/wallet_sync.rs | 364 +++++++++++++++++++++++++++++++++--------- 6 files changed, 464 insertions(+), 160 deletions(-) diff --git a/src/contracts.rs b/src/contracts.rs index dc3eb077..ddc1348e 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -23,7 +23,9 @@ use rand::RngCore; use crate::error::Error; use crate::messages::ConfirmedCoinSwapTxInfo; -use crate::wallet_sync::{create_multisig_redeemscript, Wallet, WalletSwapCoin, NETWORK}; +use crate::wallet_sync::{ + create_multisig_redeemscript, IncomingSwapCoin, OutgoingSwapCoin, Wallet, NETWORK, +}; //TODO should be configurable somehow //relatively low value for now so that its easier to test on regtest @@ -49,6 +51,8 @@ pub trait SwapCoin { fn verify_contract_tx_receiver_sig(&self, sig: &Signature) -> bool; fn verify_contract_tx_sender_sig(&self, sig: &Signature) -> bool; fn apply_privkey(&mut self, privkey: SecretKey) -> Result<(), Error>; + fn contract_type(&self) -> &str; + fn is_known(&self) -> bool; } pub fn calculate_maker_pubkey_from_nonce( @@ -457,7 +461,7 @@ fn verify_contract_tx_sig( secp.verify(&sighash, sig, &pubkey.key).is_ok() } -impl SwapCoin for WalletSwapCoin { +impl SwapCoin for IncomingSwapCoin { fn get_multisig_redeemscript(&self) -> Script { let secp = Secp256k1::new(); create_multisig_redeemscript( @@ -501,35 +505,104 @@ impl SwapCoin for WalletSwapCoin { self.other_privkey = Some(privkey); Ok(()) } -} -impl WalletSwapCoin { - //"_with_my_privkey" as opposed to with other_privkey - pub fn sign_contract_tx_with_my_privkey( - &self, - contract_tx: &Transaction, - ) -> Result { - let multisig_redeemscript = self.get_multisig_redeemscript(); - Ok(sign_contract_tx( - contract_tx, - &multisig_redeemscript, - self.funding_amount, - &self.my_privkey, - ) - .map_err(|_| Error::Protocol("error with signing contract tx"))?) + fn contract_type(&self) -> &str { + self.type_string() } - pub fn verify_contract_tx_sig(&self, sig: &Signature) -> bool { - verify_contract_tx_sig( - &self.contract_tx, - &self.get_multisig_redeemscript(), - self.funding_amount, + fn is_known(&self) -> bool { + self.contract_privkey_is_known() + } +} + +impl SwapCoin for OutgoingSwapCoin { + fn get_multisig_redeemscript(&self) -> Script { + let secp = Secp256k1::new(); + create_multisig_redeemscript( &self.other_pubkey, - sig, + &PublicKey { + compressed: true, + key: secp256k1::PublicKey::from_secret_key(&secp, &self.my_privkey), + }, ) } + + fn get_contract_tx(&self) -> Transaction { + self.contract_tx.clone() + } + + fn get_contract_redeemscript(&self) -> Script { + self.contract_redeemscript.clone() + } + + fn get_funding_amount(&self) -> u64 { + self.funding_amount + } + + fn verify_contract_tx_receiver_sig(&self, sig: &Signature) -> bool { + self.verify_contract_tx_sig(sig) + } + + fn verify_contract_tx_sender_sig(&self, sig: &Signature) -> bool { + self.verify_contract_tx_sig(sig) + } + + fn apply_privkey(&mut self, privkey: SecretKey) -> Result<(), Error> { + let secp = Secp256k1::new(); + let pubkey = PublicKey { + compressed: true, + key: secp256k1::PublicKey::from_secret_key(&secp, &privkey), + }; + if pubkey == self.other_pubkey { + Ok(()) + } else { + Err(Error::Protocol("not correct privkey")) + } + } + + fn contract_type(&self) -> &str { + self.type_string() + } + + fn is_known(&self) -> bool { + self.contract_privkey_is_known() + } } +macro_rules! sign_and_verify_contract { + ($coin:ident) => { + impl $coin { + //"_with_my_privkey" as opposed to with other_privkey + pub fn sign_contract_tx_with_my_privkey( + &self, + contract_tx: &Transaction, + ) -> Result { + let multisig_redeemscript = self.get_multisig_redeemscript(); + Ok(sign_contract_tx( + contract_tx, + &multisig_redeemscript, + self.funding_amount, + &self.my_privkey, + ) + .map_err(|_| Error::Protocol("error with signing contract tx"))?) + } + + pub fn verify_contract_tx_sig(&self, sig: &Signature) -> bool { + verify_contract_tx_sig( + &self.contract_tx, + &self.get_multisig_redeemscript(), + self.funding_amount, + &self.other_pubkey, + sig, + ) + } + } + }; +} + +sign_and_verify_contract!(IncomingSwapCoin); +sign_and_verify_contract!(OutgoingSwapCoin); + impl SwapCoin for WatchOnlySwapCoin { fn get_multisig_redeemscript(&self) -> Script { create_multisig_redeemscript(&self.sender_pubkey, &self.receiver_pubkey) @@ -582,6 +655,14 @@ impl SwapCoin for WatchOnlySwapCoin { Err(Error::Protocol("not correct privkey")) } } + + fn contract_type(&self) -> &str { + "watchonly" + } + + fn is_known(&self) -> bool { + false + } } impl WatchOnlySwapCoin { diff --git a/src/main.rs b/src/main.rs index 9833056e..e01af8f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,6 @@ use std::path::PathBuf; use std::sync::{Arc, RwLock}; use bitcoin::hashes::hex::ToHex; -use bitcoin::secp256k1::Secp256k1; -use bitcoin::util::key::PublicKey; use bitcoin::Amount; use bitcoin_wallet::mnemonic; use bitcoincore_rpc::{Auth, Client, Error, RpcApi}; @@ -20,10 +18,7 @@ mod wallet_sync; use wallet_sync::Wallet; mod contracts; -use contracts::{ - read_hashlock_pubkey_from_contract, read_locktime_from_contract, - read_timelock_pubkey_from_contract, -}; +use contracts::{read_locktime_from_contract, SwapCoin}; mod error; mod maker_protocol; @@ -173,39 +168,34 @@ fn display_wallet_balance(wallet_file_name: &PathBuf, long_form: Option) { println!("coin count = {}", utxo_count); println!("total balance = {}", balance); - let secp = Secp256k1::new(); let incomplete_coinswaps = wallet.find_incomplete_coinswaps(&rpc).unwrap(); if incomplete_coinswaps.len() > 0 { println!("= incomplete coinswaps ="); - for (hashvalue, utxo_swapcoins) in incomplete_coinswaps { - let balance: Amount = utxo_swapcoins + for (hashvalue, (utxo_incoming_swapcoins, utxo_outgoing_swapcoins)) in incomplete_coinswaps + { + let balance: Amount = utxo_incoming_swapcoins .iter() + .map(|(l, i)| (l, (*i as &dyn SwapCoin))) + .chain( + utxo_outgoing_swapcoins + .iter() + .map(|(l, o)| (l, (*o as &dyn SwapCoin))), + ) .fold(Amount::ZERO, |acc, us| acc + us.0.amount); println!( "{:16} {:8} {:8} {:<15} {:<7} value", "outpoint", "type", "preimage", "locktime/blocks", "conf", ); - for (utxo, swapcoin) in utxo_swapcoins { + for (utxo, swapcoin) in utxo_incoming_swapcoins + .iter() + .map(|(l, i)| (l, (*i as &dyn SwapCoin))) + .chain( + utxo_outgoing_swapcoins + .iter() + .map(|(l, o)| (l, (*o as &dyn SwapCoin))), + ) + { let txid = utxo.txid.to_hex(); - let contract_pubkey = PublicKey { - compressed: true, - key: bitcoin::secp256k1::PublicKey::from_secret_key( - &secp, - &swapcoin.contract_privkey, - ), - }; - let type_string = if contract_pubkey - == read_hashlock_pubkey_from_contract(&swapcoin.contract_redeemscript).unwrap() - { - "hashlock" - } else { - assert_eq!( - contract_pubkey, - read_timelock_pubkey_from_contract(&swapcoin.contract_redeemscript) - .unwrap() - ); - "timelock" - }; #[rustfmt::skip] println!("{}{}{}:{} {:8} {:8} {:^15} {:<7} {}", @@ -213,9 +203,9 @@ fn display_wallet_balance(wallet_file_name: &PathBuf, long_form: Option) { if long_form { "" } else { ".." }, if long_form { &"" } else { &txid[58..64] }, utxo.vout, - type_string, - if swapcoin.hash_preimage.is_some() { "known" } else { "unknown" }, - read_locktime_from_contract(&swapcoin.contract_redeemscript) + swapcoin.contract_type(), + if swapcoin.is_known() { "known" } else { "unknown" }, + read_locktime_from_contract(&swapcoin.get_contract_redeemscript()) .expect("unable to read locktime from contract"), utxo.confirmations, utxo.amount diff --git a/src/maker_protocol.rs b/src/maker_protocol.rs index 37a99f1b..3e304aa8 100644 --- a/src/maker_protocol.rs +++ b/src/maker_protocol.rs @@ -27,7 +27,7 @@ use crate::messages::{ SendersContractSig, SignReceiversContractTx, SignSendersAndReceiversContractTxes, SignSendersContractTx, SwapCoinPrivateKey, TakerToMakerMessage, }; -use crate::wallet_sync::{CoreAddressLabelType, Wallet, WalletSwapCoin}; +use crate::wallet_sync::{CoreAddressLabelType, IncomingSwapCoin, OutgoingSwapCoin, Wallet}; // A structure denoting expectation of type of taker message. // Used in the [ConnectionState] structure. @@ -56,8 +56,8 @@ pub async fn start_maker(rpc: Arc, wallet: Arc>, port: u1 struct ConnectionState { allowed_message: ExpectedMessage, - incoming_swapcoins: Option>, - outgoing_swapcoins: Option>, + incoming_swapcoins: Option>, + outgoing_swapcoins: Option>, pending_funding_txes: Option>, } @@ -399,7 +399,7 @@ fn handle_proof_of_funding( log::trace!(target: "maker", "proof of funding valid, creating own funding txes"); - connection_state.incoming_swapcoins = Some(Vec::::new()); + connection_state.incoming_swapcoins = Some(Vec::::new()); for (funding_info, &funding_output_index, &funding_output, &incoming_swapcoin_keys) in izip!( proof.confirmed_funding_txes.iter(), funding_output_indexes.iter(), @@ -434,7 +434,7 @@ fn handle_proof_of_funding( .incoming_swapcoins .as_mut() .unwrap() - .push(WalletSwapCoin::new( + .push(IncomingSwapCoin::new( coin_privkey, coin_other_pubkey, my_receivers_contract_tx.clone(), @@ -553,10 +553,10 @@ fn handle_senders_and_receivers_contract_sigs( let mut w = wallet.write().unwrap(); incoming_swapcoins .iter() - .for_each(|incoming_swapcoin| w.add_swapcoin(incoming_swapcoin.clone())); + .for_each(|incoming_swapcoin| w.add_incoming_swapcoin(incoming_swapcoin.clone())); outgoing_swapcoins .iter() - .for_each(|outgoing_swapcoin| w.add_swapcoin(outgoing_swapcoin.clone())); + .for_each(|outgoing_swapcoin| w.add_outgoing_swapcoin(outgoing_swapcoin.clone())); w.update_swap_coins_list()?; //TODO add coin to watchtowers @@ -586,12 +586,20 @@ fn handle_sign_receivers_contract_tx( let mut sigs = Vec::::new(); for receivers_contract_tx_info in message.txes { sigs.push( - wallet + if let Some(c) = wallet .read() .unwrap() - .find_swapcoin(&receivers_contract_tx_info.multisig_redeemscript) - .ok_or(Error::Protocol("multisig_redeemscript not found"))? - .sign_contract_tx_with_my_privkey(&receivers_contract_tx_info.contract_tx)?, + .find_incoming_swapcoin(&receivers_contract_tx_info.multisig_redeemscript) + { + c.sign_contract_tx_with_my_privkey(&receivers_contract_tx_info.contract_tx)? + } else { + wallet + .read() + .unwrap() + .find_outgoing_swapcoin(&receivers_contract_tx_info.multisig_redeemscript) + .ok_or(Error::Protocol("multisig_redeemscript not found"))? + .sign_contract_tx_with_my_privkey(&receivers_contract_tx_info.contract_tx)? + }, ); } Ok(Some(MakerToTakerMessage::ReceiversContractSig( @@ -608,7 +616,7 @@ fn handle_hash_preimage( let mut wallet_mref = wallet.write().unwrap(); for multisig_redeemscript in message.senders_multisig_redeemscripts { let mut incoming_swapcoin = wallet_mref - .find_swapcoin_mut(&multisig_redeemscript) + .find_incoming_swapcoin_mut(&multisig_redeemscript) .ok_or(Error::Protocol("senders multisig_redeemscript not found"))?; if read_hashvalue_from_contract(&incoming_swapcoin.contract_redeemscript) .map_err(|_| Error::Protocol("unable to read hashvalue from contract"))? @@ -624,7 +632,7 @@ fn handle_hash_preimage( let mut swapcoin_private_keys = Vec::::new(); for multisig_redeemscript in message.receivers_multisig_redeemscripts { let outgoing_swapcoin = wallet_ref - .find_swapcoin(&multisig_redeemscript) + .find_outgoing_swapcoin(&multisig_redeemscript) .ok_or(Error::Protocol("receivers multisig_redeemscript not found"))?; if read_hashvalue_from_contract(&outgoing_swapcoin.contract_redeemscript) .map_err(|_| Error::Protocol("unable to read hashvalue from contract"))? @@ -652,10 +660,16 @@ fn handle_private_key_handover( ) -> Result, Error> { let mut wallet_ref = wallet.write().unwrap(); for swapcoin_private_key in message.swapcoin_private_keys { - wallet_ref - .find_swapcoin_mut(&swapcoin_private_key.multisig_redeemscript) - .ok_or(Error::Protocol("multisig_redeemscript not found"))? - .apply_privkey(swapcoin_private_key.key)? + if let Some(c) = + wallet_ref.find_incoming_swapcoin_mut(&swapcoin_private_key.multisig_redeemscript) + { + c.apply_privkey(swapcoin_private_key.key)? + } else { + wallet_ref + .find_outgoing_swapcoin_mut(&swapcoin_private_key.multisig_redeemscript) + .ok_or(Error::Protocol("multisig_redeemscript not found"))? + .apply_privkey(swapcoin_private_key.key)? + } } wallet_ref.update_swap_coins_list()?; println!("successfully completed coinswap"); diff --git a/src/messages.rs b/src/messages.rs index 9880ea9c..5b3df3dc 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; -use bitcoin::hashes::hash160::Hash as Hash160; +use bitcoin::hashes::{hash160::Hash as Hash160, sha256::Hash as Hash256}; use bitcoin::secp256k1::{SecretKey, Signature}; use bitcoin::util::key::PublicKey; use bitcoin::{Script, Transaction}; @@ -86,7 +86,7 @@ pub struct SignReceiversContractTx { pub struct HashPreimage { pub senders_multisig_redeemscripts: Vec