Skip to content

Commit

Permalink
Have wallet-balance show hashlocked coins too
Browse files Browse the repository at this point in the history
Also have recover-from-incomplete-coinswap broadcast the hashlock
contract transactions as well.
  • Loading branch information
chris-belcher committed Jan 17, 2022
1 parent c3cdb93 commit d15536b
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::wallet_sync::{
pub const REFUND_LOCKTIME: u16 = 30; //in blocks
pub const REFUND_LOCKTIME_STEP: u16 = 5; //in blocks

//like the WalletSwapCoin struct but no privkey or signature information
//like the Incoming/OutgoingSwapCoin structs but no privkey or signature information
//used by the taker to monitor coinswaps between two makers
#[derive(Debug, Clone)]
pub struct WatchOnlySwapCoin {
Expand Down
63 changes: 54 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use bitcoin_wallet::mnemonic;
use bitcoincore_rpc::{Auth, Client, Error, RpcApi};

pub mod wallet_sync;
use wallet_sync::{Wallet, WalletSyncAddressAmount};
use wallet_sync::{Wallet, WalletSwapCoin, WalletSyncAddressAmount};

pub mod wallet_direct_send;
use wallet_direct_send::{CoinToSpend, Destination, SendAmount};
Expand Down Expand Up @@ -250,32 +250,67 @@ pub fn display_wallet_balance(wallet_file_name: &PathBuf, long_form: Option<bool
}
}

let (_incoming_contract_utxos, mut outgoing_contract_utxos) =
let (mut incoming_contract_utxos, mut outgoing_contract_utxos) =
wallet.find_live_contract_unspents(&rpc).unwrap();
if !outgoing_contract_utxos.is_empty() {
outgoing_contract_utxos.sort_by(|a, b| b.1.confirmations.cmp(&a.1.confirmations));
println!("= live timelocked contracts =");
println!(
"{:16} {:8} {:<7} {:<8} {:6}",
"coin", "timelock", "conf", "locked?", "value"
"{:16} {:10} {:8} {:<7} {:<8} {:6}",
"coin", "hashvalue", "timelock", "conf", "locked?", "value"
);
for (outgoing_swapcoin, utxo) in outgoing_contract_utxos {
let txid = utxo.txid.to_hex();
let timelock =
read_locktime_from_contract(&outgoing_swapcoin.contract_redeemscript).unwrap();
let hashvalue = outgoing_swapcoin.get_hashvalue().to_hex();
#[rustfmt::skip]
println!("{}{}{}:{} {:<8} {:<7} {:<8} {}",
println!("{}{}{}:{} {}{} {:<8} {:<7} {:<8} {}",
if long_form { &txid } else {&txid[0..6] },
if long_form { "" } else { ".." },
if long_form { &"" } else { &txid[58..64] },
utxo.vout,
if long_form { &hashvalue } else { &hashvalue[..8] },
if long_form { "" } else { ".." },
timelock,
utxo.confirmations,
if utxo.confirmations >= timelock.into() { "unlocked" } else { "locked" },
utxo.amount
);
}
}

//ordinary users shouldnt be spending via the hashlock branch
//maybe makers since they're a bit more expertly, and they dont start with the hash preimage
//but takers should basically never use the hash preimage
let expert_mode = true;
if expert_mode && !incoming_contract_utxos.is_empty() {
incoming_contract_utxos.sort_by(|a, b| b.1.confirmations.cmp(&a.1.confirmations));
println!("= live hashlocked contracts =");
println!(
"{:16} {:10} {:8} {:<7} {:8} {:6}",
"coin", "hashvalue", "timelock", "conf", "preimage", "value"
);
for (incoming_swapcoin, utxo) in incoming_contract_utxos {
let txid = utxo.txid.to_hex();
let timelock =
read_locktime_from_contract(&incoming_swapcoin.contract_redeemscript).unwrap();
let hashvalue = incoming_swapcoin.get_hashvalue().to_hex();
#[rustfmt::skip]
println!("{}{}{}:{} {}{} {:<8} {:<7} {:8} {}",
if long_form { &txid } else {&txid[0..6] },
if long_form { "" } else { ".." },
if long_form { &"" } else { &txid[58..64] },
utxo.vout,
if long_form { &hashvalue } else { &hashvalue[..8] },
if long_form { "" } else { ".." },
timelock,
utxo.confirmations,
if incoming_swapcoin.is_hash_preimage_known() { "known" } else { "unknown" },
utxo.amount
);
}
}
}

pub fn display_wallet_keys(wallet_file_name: &PathBuf) {
Expand Down Expand Up @@ -406,17 +441,27 @@ pub fn recover_from_incomplete_coinswap(
return;
}
let incomplete_coinswap = incomplete_coinswap.unwrap();

for (ii, outgoing_swapcoin) in incomplete_coinswap.1.iter().enumerate() {
for (ii, swapcoin) in incomplete_coinswap
.0
.iter()
.map(|(l, i)| (l, (*i as &dyn WalletSwapCoin)))
.chain(
incomplete_coinswap
.1
.iter()
.map(|(l, o)| (l, (*o as &dyn WalletSwapCoin))),
)
.enumerate()
{
wallet
.import_redeemscript(
&rpc,
&outgoing_swapcoin.1.contract_redeemscript,
&swapcoin.1.get_contract_redeemscript(),
wallet_sync::CoreAddressLabelType::Wallet,
)
.unwrap();

let signed_contract_tx = outgoing_swapcoin.1.get_fully_signed_contract_tx();
let signed_contract_tx = swapcoin.1.get_fully_signed_contract_tx();
if dont_broadcast {
let txhex = bitcoin::consensus::encode::serialize_hex(&signed_contract_tx);
println!("contract_tx_{} = \n{}", ii, txhex);
Expand Down
104 changes: 51 additions & 53 deletions src/wallet_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,37 +240,6 @@ impl OutgoingSwapCoin {
}
}

pub fn get_fully_signed_contract_tx(&self) -> Transaction {
if self.others_contract_sig.is_none() {
panic!("invalid state: others_contract_sig not known");
}
let my_pubkey = self.get_my_pubkey();
let multisig_redeemscript = create_multisig_redeemscript(&my_pubkey, &self.other_pubkey);
let index = 0;
let secp = Secp256k1::new();
let sighash = secp256k1::Message::from_slice(
&SigHashCache::new(&self.contract_tx).signature_hash(
index,
&multisig_redeemscript,
self.funding_amount,
SigHashType::All,
)[..],
)
.unwrap();
let sig_mine = secp.sign(&sighash, &self.my_privkey);

let mut signed_contract_tx = self.contract_tx.clone();
apply_two_signatures_to_2of2_multisig_spend(
&my_pubkey,
&self.other_pubkey,
&sig_mine,
&self.others_contract_sig.unwrap(),
&mut signed_contract_tx.input[index],
&multisig_redeemscript,
);
signed_contract_tx
}

fn sign_timelocked_transaction_input(
&self,
index: usize,
Expand All @@ -297,37 +266,66 @@ impl OutgoingSwapCoin {
}
}

pub trait WalletSwapCoin {
pub trait WalletSwapCoin: SwapCoin {
fn get_my_pubkey(&self) -> PublicKey;
fn get_other_pubkey(&self) -> &PublicKey;
fn get_fully_signed_contract_tx(&self) -> Transaction;
}

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),
macro_rules! add_walletswapcoin_functions {
() => {
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 get_other_pubkey(&self) -> &PublicKey {
&self.other_pubkey
}

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_fully_signed_contract_tx(&self) -> Transaction {
if self.others_contract_sig.is_none() {
panic!("invalid state: others_contract_sig not known");
}
let my_pubkey = self.get_my_pubkey();
let multisig_redeemscript =
create_multisig_redeemscript(&my_pubkey, &self.other_pubkey);
let index = 0;
let secp = Secp256k1::new();
let sighash = secp256k1::Message::from_slice(
&SigHashCache::new(&self.contract_tx).signature_hash(
index,
&multisig_redeemscript,
self.funding_amount,
SigHashType::All,
)[..],
)
.unwrap();
let sig_mine = secp.sign(&sighash, &self.my_privkey);

let mut signed_contract_tx = self.contract_tx.clone();
apply_two_signatures_to_2of2_multisig_spend(
&my_pubkey,
&self.other_pubkey,
&sig_mine,
&self.others_contract_sig.unwrap(),
&mut signed_contract_tx.input[index],
&multisig_redeemscript,
);
signed_contract_tx
}
}
};
}

fn get_other_pubkey(&self) -> &PublicKey {
&self.other_pubkey
}
impl WalletSwapCoin for IncomingSwapCoin {
add_walletswapcoin_functions!();
}

impl WalletSwapCoin for OutgoingSwapCoin {
add_walletswapcoin_functions!();
}

impl Wallet {
Expand Down

0 comments on commit d15536b

Please sign in to comment.