From 06b00bef78ec78dbe801e135659802204d5b89dc Mon Sep 17 00:00:00 2001 From: GeneFerneau Date: Mon, 2 Aug 2021 19:16:04 +0000 Subject: [PATCH] fixup: Restrict trait functions Restrict trait functions to enforce contextual rules for Incoming, Outgoing, and WatchOnly swapcoins types --- src/contracts.rs | 174 +++++++++++++++++++++++++++------------------ src/main.rs | 32 +-------- src/wallet_sync.rs | 174 ++++++++++++++++++++++++++++----------------- 3 files changed, 216 insertions(+), 164 deletions(-) diff --git a/src/contracts.rs b/src/contracts.rs index 1d6a084f..0cf94b67 100644 --- a/src/contracts.rs +++ b/src/contracts.rs @@ -46,13 +46,12 @@ pub trait SwapCoin { fn get_multisig_redeemscript(&self) -> Script; fn get_contract_tx(&self) -> Transaction; fn get_contract_redeemscript(&self) -> Script; - fn get_other_privkey(&self) -> Option<&SecretKey>; - fn get_contract_privkey(&self) -> Option<&SecretKey>; - fn get_hash_preimage(&self) -> Option<&[u8; 32]>; fn get_funding_amount(&self) -> u64; 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( @@ -459,70 +458,113 @@ fn verify_contract_tx_sig( secp.verify(&sighash, sig, &pubkey.key).is_ok() } -macro_rules! impl_swapcoin { - ($coin:ident) => { - impl SwapCoin for $coin { - fn get_multisig_redeemscript(&self) -> Script { - let secp = Secp256k1::new(); - create_multisig_redeemscript( - &self.other_pubkey, - &PublicKey { - compressed: true, - key: secp256k1::PublicKey::from_secret_key(&secp, &self.my_privkey), - }, - ) - } +impl SwapCoin for IncomingSwapCoin { + fn get_multisig_redeemscript(&self) -> Script { + let secp = Secp256k1::new(); + create_multisig_redeemscript( + &self.other_pubkey, + &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_tx(&self) -> Transaction { + self.contract_tx.clone() + } - fn get_contract_redeemscript(&self) -> Script { - self.contract_redeemscript.clone() - } + fn get_contract_redeemscript(&self) -> Script { + self.contract_redeemscript.clone() + } - fn get_other_privkey(&self) -> Option<&SecretKey> { - self.other_privkey.as_ref() - } + fn get_funding_amount(&self) -> u64 { + self.funding_amount + } - fn get_contract_privkey(&self) -> Option<&SecretKey> { - Some(self.get_lock_privkey()) - } + fn verify_contract_tx_receiver_sig(&self, sig: &Signature) -> bool { + self.verify_contract_tx_sig(sig) + } - fn get_hash_preimage(&self) -> Option<&[u8; 32]> { - self.hash_preimage.as_ref() - } + fn verify_contract_tx_sender_sig(&self, sig: &Signature) -> bool { + self.verify_contract_tx_sig(sig) + } - fn get_funding_amount(&self) -> u64 { - self.funding_amount - } + 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 { + return Err(Error::Protocol("not correct privkey")); + } + self.other_privkey = Some(privkey); + Ok(()) + } - fn verify_contract_tx_receiver_sig(&self, sig: &Signature) -> bool { - self.verify_contract_tx_sig(sig) - } + fn contract_type(&self) -> &str { + self.type_string() + } - fn verify_contract_tx_sender_sig(&self, sig: &Signature) -> bool { - self.verify_contract_tx_sig(sig) - } + fn is_known(&self) -> bool { + self.contract_privkey_is_known() + } +} - 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 { - return Err(Error::Protocol("not correct privkey")); - } - self.other_privkey = Some(privkey); - Ok(()) - } +impl SwapCoin for OutgoingSwapCoin { + fn get_multisig_redeemscript(&self) -> Script { + let secp = Secp256k1::new(); + create_multisig_redeemscript( + &self.other_pubkey, + &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")) } - }; -} + } -impl_swapcoin!(IncomingSwapCoin); -impl_swapcoin!(OutgoingSwapCoin); + 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) => { @@ -571,18 +613,6 @@ impl SwapCoin for WatchOnlySwapCoin { self.contract_redeemscript.clone() } - fn get_other_privkey(&self) -> Option<&SecretKey> { - None - } - - fn get_contract_privkey(&self) -> Option<&SecretKey> { - None - } - - fn get_hash_preimage(&self) -> Option<&[u8; 32]> { - None - } - fn get_funding_amount(&self) -> u64 { self.funding_amount } @@ -622,6 +652,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 714ff87a..656fb4c3 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; mod error; mod maker_protocol; @@ -173,7 +168,6 @@ 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 ="); @@ -187,26 +181,6 @@ fn display_wallet_balance(wallet_file_name: &PathBuf, long_form: Option) { ); for (utxo, swapcoin) in utxo_swapcoins { let txid = utxo.txid.to_hex(); - let contract_pubkey = PublicKey { - compressed: true, - key: bitcoin::secp256k1::PublicKey::from_secret_key( - &secp, - swapcoin.get_contract_privkey().unwrap(), - ), - }; - let type_string = if contract_pubkey - == read_hashlock_pubkey_from_contract(&swapcoin.get_contract_redeemscript()) - .unwrap() - { - "hashlock" - } else { - assert_eq!( - contract_pubkey, - read_timelock_pubkey_from_contract(&swapcoin.get_contract_redeemscript()) - .unwrap() - ); - "timelock" - }; #[rustfmt::skip] println!("{}{}{}:{} {:8} {:8} {:^15} {:<7} {}", @@ -214,8 +188,8 @@ 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.get_hash_preimage().is_some() { "known" } else { "unknown" }, + 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, diff --git a/src/wallet_sync.rs b/src/wallet_sync.rs index e02cbc4f..a6baa692 100644 --- a/src/wallet_sync.rs +++ b/src/wallet_sync.rs @@ -111,7 +111,6 @@ pub struct IncomingSwapCoin { pub struct OutgoingSwapCoin { pub my_privkey: SecretKey, pub other_pubkey: PublicKey, - pub other_privkey: Option, pub contract_tx: Transaction, pub contract_redeemscript: Script, pub timelock_privkey: SecretKey, @@ -151,8 +150,12 @@ impl IncomingSwapCoin { } } - pub fn get_lock_privkey(&self) -> &SecretKey { - &self.hashlock_privkey + pub fn type_string(&self) -> &str { + "hashlock" + } + + pub fn contract_privkey_is_known(&self) -> bool { + self.other_privkey.is_some() || self.hash_preimage.is_some() } } @@ -177,7 +180,6 @@ impl OutgoingSwapCoin { Self { my_privkey, other_pubkey, - other_privkey: None, contract_tx, contract_redeemscript, timelock_privkey, @@ -187,8 +189,12 @@ impl OutgoingSwapCoin { } } - pub fn get_lock_privkey(&self) -> &SecretKey { - &self.timelock_privkey + pub fn type_string(&self) -> &str { + "timelock" + } + + pub fn contract_privkey_is_known(&self) -> bool { + self.hash_preimage.is_some() } } @@ -204,68 +210,103 @@ pub trait WalletSwapCoin { ) -> Result<(), &'static str>; } -macro_rules! impl_wallet_swapcoin { - ($coin:ident) => { - impl WalletSwapCoin for $coin { - fn get_my_pubkey(&self) -> PublicKey { - let secp = Secp256k1::new(); - PublicKey { - compressed: true, - key: secp256k1::PublicKey::from_secret_key(&secp, &self.my_privkey), - } - } - - fn get_other_pubkey(&self) -> &PublicKey { - &self.other_pubkey - } +impl WalletSwapCoin for IncomingSwapCoin { + fn get_my_pubkey(&self) -> PublicKey { + let secp = Secp256k1::new(); + PublicKey { + compressed: true, + key: secp256k1::PublicKey::from_secret_key(&secp, &self.my_privkey), + } + } - fn sign_transaction_input( - &self, - index: usize, - tx: &Transaction, - input: &mut TxIn, - redeemscript: &Script, - ) -> Result<(), &'static str> { - if self.other_privkey.is_none() { - return Err("unable to sign: incomplete coinswap for this input"); - } - let secp = Secp256k1::new(); - let my_pubkey = self.get_my_pubkey(); - - let sighash = secp256k1::Message::from_slice( - &SigHashCache::new(tx).signature_hash( - index, - redeemscript, - self.funding_amount, - SigHashType::All, - )[..], - ) - .unwrap(); + fn get_other_pubkey(&self) -> &PublicKey { + &self.other_pubkey + } - let sig_mine = secp.sign(&sighash, &self.my_privkey); - let sig_other = secp.sign(&sighash, &self.other_privkey.unwrap()); - - let (sig_first, sig_second) = - if my_pubkey.serialize()[..] < self.other_pubkey.serialize()[..] { - (sig_mine, sig_other) - } else { - (sig_other, sig_mine) - }; - - input.witness.push(Vec::new()); //first is multisig dummy - input.witness.push(sig_first.serialize_der().to_vec()); - input.witness.push(sig_second.serialize_der().to_vec()); - input.witness[1].push(SigHashType::All as u8); - input.witness[2].push(SigHashType::All as u8); - input.witness.push(redeemscript.to_bytes()); - Ok(()) - } + fn sign_transaction_input( + &self, + index: usize, + tx: &Transaction, + input: &mut TxIn, + redeemscript: &Script, + ) -> Result<(), &'static str> { + if self.other_privkey.is_none() { + return Err("unable to sign: incomplete coinswap for this input"); } - }; + let secp = Secp256k1::new(); + let my_pubkey = self.get_my_pubkey(); + + let sighash = secp256k1::Message::from_slice( + &SigHashCache::new(tx).signature_hash( + index, + redeemscript, + self.funding_amount, + SigHashType::All, + )[..], + ) + .unwrap(); + + let sig_mine = secp.sign(&sighash, &self.my_privkey); + let sig_other = secp.sign(&sighash, &self.other_privkey.unwrap()); + + let (sig_first, sig_second) = + if my_pubkey.serialize()[..] < self.other_pubkey.serialize()[..] { + (sig_mine, sig_other) + } else { + (sig_other, sig_mine) + }; + + input.witness.push(Vec::new()); //first is multisig dummy + input.witness.push(sig_first.serialize_der().to_vec()); + input.witness.push(sig_second.serialize_der().to_vec()); + input.witness[1].push(SigHashType::All as u8); + input.witness[2].push(SigHashType::All as u8); + input.witness.push(redeemscript.to_bytes()); + Ok(()) + } } -impl_wallet_swapcoin!(IncomingSwapCoin); -impl_wallet_swapcoin!(OutgoingSwapCoin); +impl WalletSwapCoin for OutgoingSwapCoin { + fn get_my_pubkey(&self) -> PublicKey { + let secp = Secp256k1::new(); + PublicKey { + compressed: true, + key: secp256k1::PublicKey::from_secret_key(&secp, &self.my_privkey), + } + } + + fn get_other_pubkey(&self) -> &PublicKey { + &self.other_pubkey + } + + fn sign_transaction_input( + &self, + index: usize, + tx: &Transaction, + input: &mut TxIn, + redeemscript: &Script, + ) -> Result<(), &'static str> { + let secp = Secp256k1::new(); + + let sighash = secp256k1::Message::from_slice( + &SigHashCache::new(tx).signature_hash( + index, + redeemscript, + self.funding_amount, + SigHashType::All, + )[..], + ) + .unwrap(); + + let sig = secp.sign(&sighash, &self.my_privkey); + + input.witness.push(Vec::new()); //first is multisig dummy + input.witness.push(sig.serialize_der().to_vec()); + input.witness[1].push(SigHashType::All as u8); + input.witness.push(redeemscript.to_bytes()); + Ok(()) + } +} impl Wallet { pub fn print_wallet_key_data(&self) { @@ -293,7 +334,7 @@ impl Wallet { Address::p2wsh(script, NETWORK), contract_tx.input[0].previous_output.txid, contract_tx.input[0].previous_output.vout, - if coin.get_other_privkey().is_some() { + if coin.is_known() { " known" } else { "unknown" @@ -706,7 +747,7 @@ impl Wallet { .as_ref() .unwrap_or(&Script::from(Vec::from_hex("").unwrap())), ) - .map_or(false, |sc| sc.other_privkey.is_some()) + .map_or(false, |sc| sc.hash_preimage.is_some()) } } @@ -758,7 +799,6 @@ impl Wallet { completed_coinswap_hashvalues.extend( self.outgoing_swap_coins .values() - .filter(|sc| sc.other_privkey.is_some()) .map(|sc| read_hashvalue_from_contract(&sc.contract_redeemscript).unwrap()), ); //TODO make this read_hashvalue_from_contract() a struct function of WalletCoinSwap @@ -782,7 +822,7 @@ impl Wallet { } else { continue; }; - if swapcoin.get_other_privkey().is_some() { + if swapcoin.is_known() { continue; } let swapcoin_hashvalue =