Skip to content

Commit

Permalink
Merge #29: WalletSwapCoin: Split into two classes + add trait
Browse files Browse the repository at this point in the history
e150537 WalletSwapCoin: Split into two classes + add trait (GeneFerneau)

Tree-SHA512: 8470334207c88565f2a56d00989af2b3c1ecc59ea5ea0f0a1815148e86163d8a6658f20097663ee3935ffa59b0ce919450451cf6dc5c6eb30425a68b1c4982d7
  • Loading branch information
chris-belcher committed Aug 25, 2021
2 parents 1d9b34b + e150537 commit 5a4bf1c
Show file tree
Hide file tree
Showing 6 changed files with 464 additions and 160 deletions.
127 changes: 104 additions & 23 deletions src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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<Signature, Error> {
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<Signature, Error> {
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)
Expand Down Expand Up @@ -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 {
Expand Down
54 changes: 22 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand Down Expand Up @@ -173,49 +168,44 @@ fn display_wallet_balance(wallet_file_name: &PathBuf, long_form: Option<bool>) {
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} {}",
if long_form { &txid } else {&txid[0..6] },
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
Expand Down
48 changes: 31 additions & 17 deletions src/maker_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -56,8 +56,8 @@ pub async fn start_maker(rpc: Arc<Client>, wallet: Arc<RwLock<Wallet>>, port: u1

struct ConnectionState {
allowed_message: ExpectedMessage,
incoming_swapcoins: Option<Vec<WalletSwapCoin>>,
outgoing_swapcoins: Option<Vec<WalletSwapCoin>>,
incoming_swapcoins: Option<Vec<IncomingSwapCoin>>,
outgoing_swapcoins: Option<Vec<OutgoingSwapCoin>>,
pending_funding_txes: Option<Vec<Transaction>>,
}

Expand Down Expand Up @@ -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::<WalletSwapCoin>::new());
connection_state.incoming_swapcoins = Some(Vec::<IncomingSwapCoin>::new());
for (funding_info, &funding_output_index, &funding_output, &incoming_swapcoin_keys) in izip!(
proof.confirmed_funding_txes.iter(),
funding_output_indexes.iter(),
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -586,12 +586,20 @@ fn handle_sign_receivers_contract_tx(
let mut sigs = Vec::<Signature>::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(
Expand All @@ -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"))?
Expand All @@ -624,7 +632,7 @@ fn handle_hash_preimage(
let mut swapcoin_private_keys = Vec::<SwapCoinPrivateKey>::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"))?
Expand Down Expand Up @@ -652,10 +660,16 @@ fn handle_private_key_handover(
) -> Result<Option<MakerToTakerMessage>, 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");
Expand Down
4 changes: 2 additions & 2 deletions src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -86,7 +86,7 @@ pub struct SignReceiversContractTx {
pub struct HashPreimage {
pub senders_multisig_redeemscripts: Vec<Script>,
pub receivers_multisig_redeemscripts: Vec<Script>,
pub preimage: [u8; 32],
pub preimage: Hash256,
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down
Loading

0 comments on commit 5a4bf1c

Please sign in to comment.