Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WalletSwapCoin: Split into two classes + add trait #29

Merged
merged 1 commit into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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