From 9da1619c1d830ee78556ba87205237c5ecfa877a Mon Sep 17 00:00:00 2001 From: benthecarman Date: Mon, 11 Dec 2023 21:11:17 -0600 Subject: [PATCH] Create ContractSignerProvider --- bitcoin-rpc-provider/src/lib.rs | 155 +++++++------ dlc-manager/src/channel_updater.rs | 231 +++++++++++-------- dlc-manager/src/contract/contract_info.rs | 18 +- dlc-manager/src/contract/contract_input.rs | 5 + dlc-manager/src/contract/offered_contract.rs | 3 +- dlc-manager/src/contract_updater.rs | 119 ++++++---- dlc-manager/src/lib.rs | 67 +++++- dlc-manager/src/manager.rs | 143 +++++++++--- dlc-manager/src/utils.rs | 30 +-- dlc-manager/tests/channel_execution_tests.rs | 8 +- dlc-manager/tests/manager_execution_tests.rs | 4 + mocks/src/memory_storage_provider.rs | 10 +- mocks/src/mock_wallet.rs | 32 +-- simple-wallet/src/lib.rs | 147 +++++++----- 14 files changed, 629 insertions(+), 343 deletions(-) diff --git a/bitcoin-rpc-provider/src/lib.rs b/bitcoin-rpc-provider/src/lib.rs index bbee24c9..6aee1cbc 100644 --- a/bitcoin-rpc-provider/src/lib.rs +++ b/bitcoin-rpc-provider/src/lib.rs @@ -18,7 +18,7 @@ use bitcoin::{Address, OutPoint, TxOut}; use bitcoincore_rpc::{json, Auth, Client, RpcApi}; use bitcoincore_rpc_json::AddressType; use dlc_manager::error::Error as ManagerError; -use dlc_manager::{Blockchain, Signer, Utxo, Wallet}; +use dlc_manager::{Blockchain, ContractSignerProvider, SimpleSigner, Utxo, Wallet}; use json::EstimateMode; use lightning::chain::chaininterface::{ConfirmationTarget, FeeEstimator}; use log::error; @@ -168,7 +168,20 @@ fn enc_err_to_manager_err(_e: EncodeError) -> ManagerError { Error::BitcoinError.into() } -impl Signer for BitcoinCoreProvider { +impl ContractSignerProvider for BitcoinCoreProvider { + type Signer = SimpleSigner; + + fn derive_contract_signer( + &self, + _: bool, + _: u64, + _: [u8; 32], + ) -> Result { + // fixme needs to be idempotent + let secret_key = self.get_new_secret_key()?; + Ok(SimpleSigner::new(secret_key)) + } + fn get_secret_key_for_pubkey(&self, pubkey: &PublicKey) -> Result { let b_pubkey = bitcoin::PublicKey { compressed: true, @@ -186,75 +199,6 @@ impl Signer for BitcoinCoreProvider { Ok(pk.inner) } - fn sign_psbt_input( - &self, - psbt: &mut PartiallySignedTransaction, - input_index: usize, - ) -> Result<(), ManagerError> { - let outpoint = &psbt.unsigned_tx.input[input_index].previous_output; - let tx_out = if let Some(input) = psbt.inputs.get(input_index) { - if let Some(wit_utxo) = &input.witness_utxo { - Ok(wit_utxo.clone()) - } else if let Some(in_tx) = &input.non_witness_utxo { - Ok( - in_tx.output[psbt.unsigned_tx.input[input_index].previous_output.vout as usize] - .clone(), - ) - } else { - Err(ManagerError::InvalidParameters( - "No TxOut for PSBT inout".to_string(), - )) - } - } else { - Err(ManagerError::InvalidParameters( - "No TxOut for PSBT inout".to_string(), - )) - }?; - - let redeem_script = psbt - .inputs - .get(input_index) - .and_then(|i| i.redeem_script.clone()); - - let input = json::SignRawTransactionInput { - txid: outpoint.txid, - vout: outpoint.vout, - script_pub_key: tx_out.script_pubkey.clone(), - redeem_script, - amount: Some(Amount::from_sat(tx_out.value)), - }; - - let sign_result = self - .client - .lock() - .unwrap() - .sign_raw_transaction_with_wallet(&psbt.unsigned_tx, Some(&[input]), None) - .map_err(rpc_err_to_manager_err)?; - let signed_tx = Transaction::consensus_decode(&mut sign_result.hex.as_slice()) - .map_err(enc_err_to_manager_err)?; - - psbt.inputs[input_index].final_script_sig = - Some(signed_tx.input[input_index].script_sig.clone()); - psbt.inputs[input_index].final_script_witness = - Some(signed_tx.input[input_index].witness.clone()); - - Ok(()) - } -} - -impl Wallet for BitcoinCoreProvider { - fn get_new_address(&self) -> Result { - self.client - .lock() - .unwrap() - .get_new_address(None, Some(AddressType::Bech32)) - .map_err(rpc_err_to_manager_err) - } - - fn get_new_change_address(&self) -> Result { - self.get_new_address() - } - fn get_new_secret_key(&self) -> Result { let sk = SecretKey::new(&mut thread_rng()); let network = self.get_network()?; @@ -274,6 +218,20 @@ impl Wallet for BitcoinCoreProvider { Ok(sk) } +} + +impl Wallet for BitcoinCoreProvider { + fn get_new_address(&self) -> Result { + self.client + .lock() + .unwrap() + .get_new_address(None, Some(AddressType::Bech32)) + .map_err(rpc_err_to_manager_err) + } + + fn get_new_change_address(&self) -> Result { + self.get_new_address() + } fn get_utxos_for_amount( &self, @@ -324,6 +282,61 @@ impl Wallet for BitcoinCoreProvider { .import_address(address, None, Some(false)) .map_err(rpc_err_to_manager_err) } + + fn sign_psbt_input( + &self, + psbt: &mut PartiallySignedTransaction, + input_index: usize, + ) -> Result<(), ManagerError> { + let outpoint = &psbt.unsigned_tx.input[input_index].previous_output; + let tx_out = if let Some(input) = psbt.inputs.get(input_index) { + if let Some(wit_utxo) = &input.witness_utxo { + Ok(wit_utxo.clone()) + } else if let Some(in_tx) = &input.non_witness_utxo { + Ok( + in_tx.output[psbt.unsigned_tx.input[input_index].previous_output.vout as usize] + .clone(), + ) + } else { + Err(ManagerError::InvalidParameters( + "No TxOut for PSBT inout".to_string(), + )) + } + } else { + Err(ManagerError::InvalidParameters( + "No TxOut for PSBT inout".to_string(), + )) + }?; + + let redeem_script = psbt + .inputs + .get(input_index) + .and_then(|i| i.redeem_script.clone()); + + let input = json::SignRawTransactionInput { + txid: outpoint.txid, + vout: outpoint.vout, + script_pub_key: tx_out.script_pubkey.clone(), + redeem_script, + amount: Some(Amount::from_sat(tx_out.value)), + }; + + let sign_result = self + .client + .lock() + .unwrap() + .sign_raw_transaction_with_wallet(&psbt.unsigned_tx, Some(&[input]), None) + .map_err(rpc_err_to_manager_err)?; + let signed_tx = Transaction::consensus_decode(&mut sign_result.hex.as_slice()) + .map_err(enc_err_to_manager_err)?; + + psbt.inputs[input_index].final_script_sig = + Some(signed_tx.input[input_index].script_sig.clone()); + psbt.inputs[input_index].final_script_witness = + Some(signed_tx.input[input_index].witness.clone()); + + Ok(()) + } } impl Blockchain for BitcoinCoreProvider { diff --git a/dlc-manager/src/channel_updater.rs b/dlc-manager/src/channel_updater.rs index dfd9cf74..e643b995 100644 --- a/dlc-manager/src/channel_updater.rs +++ b/dlc-manager/src/channel_updater.rs @@ -20,7 +20,7 @@ use crate::{ }, error::Error, utils::get_new_temporary_id, - Blockchain, Signer, Time, Wallet, + Blockchain, ContractSigner, ContractSignerProvider, Time, Wallet, }; use bitcoin::{OutPoint, Script, Sequence, Transaction, TxIn, Witness}; use dlc::{ @@ -66,7 +66,15 @@ pub(crate) use get_signed_channel_state; /// Creates an [`OfferedChannel`] and an associated [`OfferedContract`] using /// the given parameter. -pub fn offer_channel( +pub fn offer_channel< + C: Signing, + W: Deref, + SP: Deref, + B: Deref, + T: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, +>( secp: &Secp256k1, contract: &ContractInput, counter_party: &PublicKey, @@ -74,24 +82,30 @@ pub fn offer_channel( cet_nsequence: u32, refund_delay: u32, wallet: &W, + signer_provider: &SP, blockchain: &B, time: &T, + get_signer: XFn, ) -> Result<(OfferedChannel, OfferedContract), Error> where W::Target: Wallet, + SP::Target: ContractSignerProvider, B::Target: Blockchain, T::Target: Time, { - let (offer_params, _, funding_inputs_info) = crate::utils::get_party_params( - secp, + let id = get_new_temporary_id(); + let signer = get_signer(true, contract.offer_collateral, id)?; + let (offer_params, funding_inputs_info) = crate::utils::get_party_params( contract.offer_collateral, contract.fee_rate, wallet, + &signer, blockchain, )?; - let party_points = crate::utils::get_party_base_points(secp, wallet)?; + let party_points = crate::utils::get_party_base_points(secp, signer_provider)?; let offered_contract = OfferedContract::new( + id, contract, oracle_announcements.to_vec(), &offer_params, @@ -103,7 +117,7 @@ where let temporary_channel_id = get_new_temporary_id(); - let per_update_seed = wallet.get_new_secret_key()?; + let per_update_seed = signer_provider.get_new_secret_key()?; let first_per_update_point = PublicKey::from_secret_key( secp, @@ -131,30 +145,40 @@ where /// Move the given [`OfferedChannel`] and [`OfferedContract`] to an [`AcceptedChannel`] /// and [`AcceptedContract`], returning them as well as the [`AcceptChannel`] /// message to be sent to the counter party. -pub fn accept_channel_offer( +pub fn accept_channel_offer< + W: Deref, + SP: Deref, + B: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, +>( secp: &Secp256k1, offered_channel: &OfferedChannel, offered_contract: &OfferedContract, wallet: &W, + signer_provider: &SP, blockchain: &B, + get_signer: XFn, ) -> Result<(AcceptedChannel, AcceptedContract, AcceptChannel), Error> where W::Target: Wallet, + SP::Target: ContractSignerProvider, B::Target: Blockchain, { assert_eq!(offered_channel.offered_contract_id, offered_contract.id); let total_collateral = offered_contract.total_collateral; - let (accept_params, _, funding_inputs) = crate::utils::get_party_params( - secp, + let signer = get_signer(false, total_collateral, offered_contract.id)?; + let (accept_params, funding_inputs) = crate::utils::get_party_params( total_collateral - offered_contract.offer_params.collateral, offered_contract.fee_rate_per_vb, wallet, + &signer, blockchain, )?; - let per_update_seed = wallet.get_new_secret_key()?; + let per_update_seed = signer_provider.get_new_secret_key()?; let first_per_update_point = PublicKey::from_secret_key( secp, @@ -165,7 +189,7 @@ where .expect("to have generated a valid secret key."), ); - let accept_points = crate::utils::get_party_base_points(secp, wallet)?; + let accept_points = crate::utils::get_party_base_points(secp, signer_provider)?; let accept_revoke_params = accept_points.get_revokable_params( secp, @@ -199,7 +223,8 @@ where Sequence(offered_channel.cet_nsequence), )?; - let own_base_secret_key = wallet.get_secret_key_for_pubkey(&accept_points.own_basepoint)?; + let own_base_secret_key = + signer_provider.get_secret_key_for_pubkey(&accept_points.own_basepoint)?; let own_secret_key = derive_private_key(secp, &first_per_update_point, &own_base_secret_key); @@ -209,7 +234,7 @@ where &offered_channel.temporary_channel_id, ); - let own_fund_sk = wallet.get_secret_key_for_pubkey(&accept_params.fund_pubkey)?; + let own_fund_sk = signer_provider.get_secret_key_for_pubkey(&accept_params.fund_pubkey)?; let buffer_adaptor_signature = get_tx_adaptor_signature( secp, @@ -259,16 +284,24 @@ where /// to the given [`OfferedChannel`] and [`OfferedContract`], transforming them /// to a [`SignedChannel`] and [`SignedContract`], returning them as well as the /// [`SignChannel`] to be sent to the counter party. -pub fn verify_and_sign_accepted_channel( +pub fn verify_and_sign_accepted_channel< + W: Deref, + SP: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, +>( secp: &Secp256k1, offered_channel: &OfferedChannel, offered_contract: &OfferedContract, accept_channel: &AcceptChannel, cet_nsequence: u32, - signer: &S, + wallet: &W, + signer_provider: &SP, + get_signer: XFn, ) -> Result<(SignedChannel, SignedContract, SignChannel), Error> where - S::Target: Signer, + W::Target: Wallet, + SP::Target: ContractSignerProvider, { let (tx_input_infos, input_amount) = crate::conversion_utils::get_tx_input_infos(&accept_channel.funding_inputs)?; @@ -290,17 +323,8 @@ where publish_basepoint: accept_channel.publish_basepoint, }; - let offer_own_base_secret = - signer.get_secret_key_for_pubkey(&offered_channel.party_points.own_basepoint)?; - - let offer_own_sk = derive_private_key( - secp, - &offered_channel.per_update_point, - &offer_own_base_secret, - ); - let offer_fund_sk = - signer.get_secret_key_for_pubkey(&offered_contract.offer_params.fund_pubkey)?; + signer_provider.get_secret_key_for_pubkey(&offered_contract.offer_params.fund_pubkey)?; let offer_revoke_params = offered_channel.party_points.get_revokable_params( secp, @@ -341,6 +365,11 @@ where let accept_cet_adaptor_signatures: Vec<_> = (&accept_channel.cet_adaptor_signatures).into(); + let signer = get_signer( + offered_contract.is_offer_party, + offered_contract.total_collateral, + offered_contract.id, + )?; let (signed_contract, cet_adaptor_signatures) = verify_accepted_and_sign_contract_internal( secp, offered_contract, @@ -353,8 +382,8 @@ where &accept_channel.refund_signature, &accept_cet_adaptor_signatures, buffer_transaction.output[0].value, - &offer_own_sk, - signer, + wallet, + &signer, Some(buffer_script_pubkey), Some(accept_revoke_params.own_pk.inner), &dlc_transactions, @@ -429,15 +458,15 @@ where /// Verify that the given [`SignChannel`] message is valid with respect to the /// given [`AcceptedChannel`] and [`AcceptedContract`], transforming them /// to a [`SignedChannel`] and [`SignedContract`], and returning them. -pub fn verify_signed_channel( +pub fn verify_signed_channel( secp: &Secp256k1, accepted_channel: &AcceptedChannel, accepted_contract: &AcceptedContract, sign_channel: &SignChannel, - signer: &S, + wallet: &W, ) -> Result<(SignedChannel, SignedContract), Error> where - S::Target: Signer, + W::Target: Wallet, { let own_publish_pk = accepted_channel .accept_base_points @@ -467,7 +496,7 @@ where accepted_channel.buffer_transaction.output[0].value, Some(accepted_channel.buffer_script_pubkey.clone()), Some(counter_own_pk), - signer, + wallet, Some(accepted_channel.channel_id), )?; @@ -513,16 +542,16 @@ where /// Creates a [`SettleOffer`] message from the given [`SignedChannel`] and parameters, /// updating the state of the channel at the same time. Expects the /// channel to be in [`SignedChannelState::Established`] state. -pub fn settle_channel_offer( +pub fn settle_channel_offer( secp: &Secp256k1, channel: &mut SignedChannel, counter_payout: u64, peer_timeout: u64, - signer: &S, + signer_provider: &SP, time: &T, ) -> Result where - S::Target: Signer, + SP::Target: ContractSignerProvider, T::Target: Time, { if let SignedChannelState::Established { .. } = channel.state { @@ -533,7 +562,7 @@ where } let per_update_seed_pk = channel.own_per_update_seed; - let per_update_seed = signer.get_secret_key_for_pubkey(&per_update_seed_pk)?; + let per_update_seed = signer_provider.get_secret_key_for_pubkey(&per_update_seed_pk)?; let per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), @@ -589,17 +618,17 @@ pub fn on_settle_offer( /// Creates a [`SettleAccept`] message from the given [`SignedChannel`] and other /// parameters, updating the state of the channel at the same time. Expects the /// channel to be in [`SignedChannelState::SettledReceived`] state. -pub fn settle_channel_accept( +pub fn settle_channel_accept( secp: &Secp256k1, channel: &mut SignedChannel, csv_timelock: u32, lock_time: u32, peer_timeout: u64, - signer: &S, + signer_provider: &SP, time: &T, ) -> Result where - S::Target: Signer, + SP::Target: ContractSignerProvider, T::Target: Time, { let (own_payout, counter_next_per_update_point) = if let SignedChannelState::SettledReceived { @@ -615,7 +644,7 @@ where }; let per_update_seed_pk = channel.own_per_update_seed; - let per_update_seed = signer.get_secret_key_for_pubkey(&per_update_seed_pk)?; + let per_update_seed = signer_provider.get_secret_key_for_pubkey(&per_update_seed_pk)?; let per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), channel.update_idx - 1, @@ -634,7 +663,7 @@ where let fund_vout = channel.fund_output_index; let funding_script_pubkey = &channel.fund_script_pubkey; - let own_fund_sk = signer.get_secret_key_for_pubkey(&channel.own_params.fund_pubkey)?; + let own_fund_sk = signer_provider.get_secret_key_for_pubkey(&channel.own_params.fund_pubkey)?; let (settle_tx, settle_adaptor_signature) = get_settle_tx_and_adaptor_sig( secp, @@ -676,19 +705,19 @@ where /// [`SettleAccept`] message, verifying the content of the message and updating /// the state of the channel at the same time. Expects the channel to be in /// [`SignedChannelState::SettledOffered`] state. -pub fn settle_channel_confirm( +pub fn settle_channel_confirm( secp: &Secp256k1, channel: &mut SignedChannel, settle_channel_accept: &SettleAccept, csv_timelock: u32, lock_time: u32, peer_timeout: u64, - signer: &S, + signer_provider: &SP, time: &T, ) -> Result where + SP::Target: ContractSignerProvider, T::Target: Time, - S::Target: Signer, { let (counter_payout, next_per_update_point) = match channel.state { SignedChannelState::SettledOffered { @@ -713,7 +742,7 @@ where let fund_vout = channel.fund_output_index; let funding_script_pubkey = &channel.fund_script_pubkey; - let own_fund_sk = signer.get_secret_key_for_pubkey(&channel.own_params.fund_pubkey)?; + let own_fund_sk = signer_provider.get_secret_key_for_pubkey(&channel.own_params.fund_pubkey)?; let (settle_tx, settle_adaptor_signature) = get_settle_tx_and_adaptor_sig( secp, @@ -737,7 +766,7 @@ where )?; let per_update_seed_pk = channel.own_per_update_seed; - let per_update_seed = signer.get_secret_key_for_pubkey(&per_update_seed_pk)?; + let per_update_seed = signer_provider.get_secret_key_for_pubkey(&per_update_seed_pk)?; let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), @@ -769,14 +798,14 @@ where /// [`SettleConfirm`] message, validating the message and updating the state of /// the channel at the same time. Expects the channel to be in /// [`SignedChannelState::SettledAccepted`] state. -pub fn settle_channel_finalize( +pub fn settle_channel_finalize( secp: &Secp256k1, channel: &mut SignedChannel, settle_channel_confirm: &SettleConfirm, - signer: &S, + signer_provider: &SP, ) -> Result where - S::Target: Signer, + SP::Target: ContractSignerProvider, { let ( own_next_per_update_point, @@ -804,7 +833,7 @@ where }; let per_update_seed_pk = channel.own_per_update_seed; - let per_update_seed = signer.get_secret_key_for_pubkey(&per_update_seed_pk)?; + let per_update_seed = signer_provider.get_secret_key_for_pubkey(&per_update_seed_pk)?; let accept_revoke_params = channel.own_points.get_revokable_params( secp, @@ -951,7 +980,7 @@ pub fn reject_settle_offer(signed_channel: &mut SignedChannel) -> Result( +pub fn renew_offer( secp: &Secp256k1, signed_channel: &mut SignedChannel, contract_input: &ContractInput, @@ -960,14 +989,16 @@ pub fn renew_offer( refund_delay: u32, peer_timeout: u64, cet_nsequence: u32, - signer: &S, + signer_provider: &SP, time: &T, ) -> Result<(RenewOffer, OfferedContract), Error> where - S::Target: Signer, + SP::Target: ContractSignerProvider, T::Target: Time, { + let id = get_new_temporary_id(); let mut offered_contract = OfferedContract::new( + id, contract_input, oracle_announcements, &signed_channel.own_params, @@ -981,7 +1012,8 @@ where offered_contract.fee_rate_per_vb = signed_channel.fee_rate_per_vb; - let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; + let per_update_seed = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; let per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), @@ -1078,7 +1110,7 @@ pub fn accept_channel_renewal( time: &T, ) -> Result<(AcceptedContract, RenewAccept), Error> where - S::Target: Signer, + S::Target: ContractSignerProvider, T::Target: Time, { let (offer_next_per_update_point, own_payout) = match signed_channel.state { @@ -1188,26 +1220,34 @@ where /// [`RenewAccept`] message, verifying the message and updating the state of the /// channel and associated contract the same time. Expects the channel to be in /// [`SignedChannelState::RenewOffered`] state. -pub fn verify_renew_accept_and_confirm( +pub fn verify_renew_accept_and_confirm< + W: Deref, + SP: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, + T: Deref, +>( secp: &Secp256k1, renew_accept: &RenewAccept, signed_channel: &mut SignedChannel, offered_contract: &OfferedContract, cet_nsequence: u32, peer_timeout: u64, - signer: &S, + wallet: &W, + signer_provider: &SP, + get_signer: XFn, time: &T, ) -> Result<(SignedContract, RenewConfirm), Error> where - S::Target: Signer, + W::Target: Wallet, + SP::Target: ContractSignerProvider, T::Target: Time, { - let own_fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let own_fund_sk = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; - let own_base_secret_key = - signer.get_secret_key_for_pubkey(&signed_channel.own_points.own_basepoint)?; - - let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; + let per_update_seed = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), @@ -1251,9 +1291,13 @@ where Sequence(cet_nsequence), )?; - let offer_own_sk = derive_private_key(secp, &offer_per_update_point, &own_base_secret_key); let cet_adaptor_signatures: Vec<_> = (&renew_accept.cet_adaptor_signatures).into(); + let signer = get_signer( + offered_contract.is_offer_party, + offered_contract.total_collateral, + offered_contract.id, + )?; let (signed_contract, cet_adaptor_signatures) = verify_accepted_and_sign_contract_internal( secp, offered_contract, @@ -1262,8 +1306,8 @@ where &renew_accept.refund_signature, &cet_adaptor_signatures, buffer_transaction.output[0].value, - &offer_own_sk, - signer, + wallet, + &signer, Some(buffer_script_pubkey.clone()), Some(accept_revoke_params.own_pk.inner), &dlc_transactions, @@ -1318,15 +1362,17 @@ where /// [`RenewAccept`] message, verifying the message and updating the state of the /// channel and associated contract the same time. Expects the channel to be in /// [`SignedChannelState::RenewAccepted`] state. -pub fn verify_renew_confirm_and_finalize( +pub fn verify_renew_confirm_and_finalize( secp: &Secp256k1, signed_channel: &mut SignedChannel, accepted_contract: &AcceptedContract, renew_confirm: &RenewConfirm, - signer: &S, + wallet: &W, + signer_provider: &SP, ) -> Result<(SignedContract, RenewFinalize), Error> where - S::Target: Signer, + W::Target: Wallet, + SP::Target: ContractSignerProvider, { let ( offer_per_update_point, @@ -1372,7 +1418,7 @@ where buffer_transaction.output[0].value, Some(buffer_script_pubkey.clone()), Some(counter_own_pk), - signer, + wallet, Some(signed_channel.channel_id), )?; @@ -1397,7 +1443,8 @@ where signed_channel.counter_per_update_point = offer_per_update_point; signed_channel.own_per_update_point = accept_per_update_point; - let per_update_seed = signer.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; + let per_update_seed = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_per_update_seed)?; let prev_per_update_secret = SecretKey::from_slice(&build_commitment_secret( per_update_seed.as_ref(), @@ -1484,15 +1531,15 @@ pub fn reject_renew_offer(signed_channel: &mut SignedChannel) -> Result( +pub fn offer_collaborative_close( secp: &Secp256k1, signed_channel: &mut SignedChannel, counter_payout: u64, - signer: &S, + signer_provider: &SP, time: &T, ) -> Result<(CollaborativeCloseOffer, Transaction), Error> where - S::Target: Signer, + SP::Target: ContractSignerProvider, T::Target: Time, { if counter_payout @@ -1520,7 +1567,8 @@ where fund_output_value, ); - let own_fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let own_fund_sk = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; let close_signature = dlc::util::get_raw_sig_for_tx_input( secp, @@ -1604,13 +1652,13 @@ where /// Accept an offer to collaboratively close the channel, signing the /// closing transaction and returning it. -pub fn accept_collaborative_close_offer( +pub fn accept_collaborative_close_offer( secp: &Secp256k1, signed_channel: &mut SignedChannel, - signer: &S, + signer_provider: &SP, ) -> Result where - S::Target: Signer, + SP::Target: ContractSignerProvider, { let (offer_signature, close_tx) = get_signed_channel_state!( signed_channel, @@ -1620,7 +1668,8 @@ where let fund_out_amount = signed_channel.fund_tx.output[signed_channel.fund_output_index].value; - let own_fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let own_fund_sk = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; let mut close_tx = close_tx.clone(); @@ -1760,17 +1809,17 @@ pub fn on_reject(signed_channel: &mut SignedChannel) -> Result<(), Error> { } /// Sign the buffer transaction and closing CET and update the state of the channel. -pub fn initiate_unilateral_close_established_channel( +pub fn initiate_unilateral_close_established_channel( secp: &Secp256k1, signed_channel: &mut SignedChannel, confirmed_contract: &SignedContract, contract_info: &ContractInfo, attestations: &[(usize, OracleAttestation)], adaptor_info: &AdaptorInfo, - signer: &S, + signer_provider: &SP, ) -> Result<(), Error> where - S::Target: Signer, + SP::Target: ContractSignerProvider, { let (buffer_adaptor_signature, buffer_transaction) = get_signed_channel_state!( signed_channel, @@ -1781,7 +1830,7 @@ where let mut buffer_transaction = buffer_transaction.clone(); let publish_base_secret = - signer.get_secret_key_for_pubkey(&signed_channel.own_points.publish_basepoint)?; + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_points.publish_basepoint)?; let publish_sk = derive_private_key( secp, @@ -1791,7 +1840,8 @@ where let counter_buffer_signature = buffer_adaptor_signature.decrypt(&publish_sk)?; - let fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let fund_sk = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; dlc::util::sign_multi_sig_input( secp, @@ -1867,7 +1917,7 @@ where ) }; - let base_secret = signer.get_secret_key_for_pubkey(own_basepoint)?; + let base_secret = signer_provider.get_secret_key_for_pubkey(own_basepoint)?; let own_sk = derive_private_key(secp, own_per_update_point, &base_secret); dlc::channel::sign_cet( @@ -1893,13 +1943,13 @@ where } /// Sign the settlement transaction and update the state of the channel. -pub fn close_settled_channel( +pub fn close_settled_channel( secp: &Secp256k1, signed_channel: &mut SignedChannel, - signer: &S, + signer_provider: &SP, ) -> Result where - S::Target: Signer, + SP::Target: ContractSignerProvider, { let (counter_settle_adaptor_signature, settle_tx) = get_signed_channel_state!( signed_channel, @@ -1910,7 +1960,7 @@ where let mut settle_tx = settle_tx.clone(); let publish_base_secret = - signer.get_secret_key_for_pubkey(&signed_channel.own_points.publish_basepoint)?; + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_points.publish_basepoint)?; let publish_sk = derive_private_key( secp, @@ -1920,7 +1970,8 @@ where let counter_settle_signature = counter_settle_adaptor_signature.decrypt(&publish_sk)?; - let fund_sk = signer.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; + let fund_sk = + signer_provider.get_secret_key_for_pubkey(&signed_channel.own_params.fund_pubkey)?; dlc::util::sign_multi_sig_input( secp, diff --git a/dlc-manager/src/contract/contract_info.rs b/dlc-manager/src/contract/contract_info.rs index 5d11e0e7..bda783a0 100644 --- a/dlc-manager/src/contract/contract_info.rs +++ b/dlc-manager/src/contract/contract_info.rs @@ -3,6 +3,7 @@ use super::AdaptorInfo; use super::ContractDescriptor; use crate::error::Error; +use crate::ContractSigner; use bitcoin::{Script, Transaction}; use dlc::{OracleInfo, Payout}; use dlc_messages::oracle_msgs::{EventDescriptor, OracleAnnouncement}; @@ -11,6 +12,7 @@ use secp256k1_zkp::{ hashes::sha256, All, EcdsaAdaptorSignature, Message, PublicKey, Secp256k1, SecretKey, Verification, }; +use std::ops::Deref; pub(super) type OracleIndexAndPrefixLength = Vec<(usize, usize)>; @@ -61,15 +63,19 @@ impl ContractInfo { /// Uses the provided AdaptorInfo and SecretKey to generate the set of /// adaptor signatures for the contract. - pub fn get_adaptor_signatures( + pub fn get_adaptor_signatures( &self, secp: &Secp256k1, adaptor_info: &AdaptorInfo, - fund_privkey: &SecretKey, + signer: &S, funding_script_pubkey: &Script, fund_output_value: u64, cets: &[Transaction], - ) -> Result, Error> { + ) -> Result, Error> + where + S::Target: ContractSigner, + { + let fund_privkey = signer.get_secret_key()?; match adaptor_info { AdaptorInfo::Enum => match &self.contract_descriptor { ContractDescriptor::Enum(e) => e.get_adaptor_signatures( @@ -77,7 +83,7 @@ impl ContractInfo { &self.get_oracle_infos(), self.threshold, cets, - fund_privkey, + &fund_privkey, funding_script_pubkey, fund_output_value, ), @@ -85,7 +91,7 @@ impl ContractInfo { }, AdaptorInfo::Numerical(trie) => Ok(trie.sign( secp, - fund_privkey, + &fund_privkey, funding_script_pubkey, fund_output_value, cets, @@ -93,7 +99,7 @@ impl ContractInfo { )?), AdaptorInfo::NumericalWithDifference(trie) => Ok(trie.sign( secp, - fund_privkey, + &fund_privkey, funding_script_pubkey, fund_output_value, cets, diff --git a/dlc-manager/src/contract/contract_input.rs b/dlc-manager/src/contract/contract_input.rs index 91ca2c9a..467f343b 100644 --- a/dlc-manager/src/contract/contract_input.rs +++ b/dlc-manager/src/contract/contract_input.rs @@ -99,6 +99,11 @@ impl ContractInput { dlc::util::validate_fee_rate(self.fee_rate) .map_err(|_| Error::InvalidParameters("Fee rate too high.".to_string())) } + + /// Returns the total collateral for the DLC. + pub fn total_collateral(&self) -> u64 { + self.offer_collateral + self.accept_collateral + } } #[cfg(test)] diff --git a/dlc-manager/src/contract/offered_contract.rs b/dlc-manager/src/contract/offered_contract.rs index aa793b65..e76087aa 100644 --- a/dlc-manager/src/contract/offered_contract.rs +++ b/dlc-manager/src/contract/offered_contract.rs @@ -75,6 +75,7 @@ impl OfferedContract { /// Creates a new [`OfferedContract`] from the given parameters. pub fn new( + id: [u8; 32], contract: &ContractInput, oracle_announcements: Vec>, offer_params: &PartyParams, @@ -102,7 +103,7 @@ impl OfferedContract { }) .collect::>(); OfferedContract { - id: crate::utils::get_new_temporary_id(), + id, is_offer_party: true, contract_info, offer_params: offer_params.clone(), diff --git a/dlc-manager/src/contract_updater.rs b/dlc-manager/src/contract_updater.rs index 9987f8dd..c8b553f8 100644 --- a/dlc-manager/src/contract_updater.rs +++ b/dlc-manager/src/contract_updater.rs @@ -21,13 +21,18 @@ use crate::{ }, conversion_utils::get_tx_input_infos, error::Error, - Blockchain, ChannelId, Signer, Time, Wallet, + Blockchain, ChannelId, ContractSigner, Time, Wallet, }; /// Creates an [`OfferedContract`] and [`OfferDlc`] message from the provided /// contract and oracle information. -pub fn offer_contract( - secp: &Secp256k1, +pub fn offer_contract< + W: Deref, + B: Deref, + T: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, +>( contract_input: &ContractInput, oracle_announcements: Vec>, refund_delay: u32, @@ -35,6 +40,7 @@ pub fn offer_contract( wallet: &W, blockchain: &B, time: &T, + get_signer: XFn, ) -> Result<(OfferedContract, OfferDlc), Error> where W::Target: Wallet, @@ -43,15 +49,18 @@ where { contract_input.validate()?; - let (party_params, _, funding_inputs_info) = crate::utils::get_party_params( - secp, + let id = crate::utils::get_new_temporary_id(); + let signer = get_signer(true, contract_input.total_collateral(), id)?; + let (party_params, funding_inputs_info) = crate::utils::get_party_params( contract_input.offer_collateral, contract_input.fee_rate, wallet, + &signer, blockchain, )?; let offered_contract = OfferedContract::new( + id, contract_input, oracle_announcements, &party_params, @@ -68,10 +77,16 @@ where /// Creates an [`AcceptedContract`] and produces /// the accepting party's cet adaptor signatures. -pub fn accept_contract( +pub fn accept_contract< + W: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, + B: Deref, +>( secp: &Secp256k1, offered_contract: &OfferedContract, wallet: &W, + get_signer: XFn, blockchain: &B, ) -> Result<(AcceptedContract, AcceptDlc), crate::Error> where @@ -80,11 +95,16 @@ where { let total_collateral = offered_contract.total_collateral; - let (accept_params, fund_secret_key, funding_inputs) = crate::utils::get_party_params( - secp, + let signer = get_signer( + false, + offered_contract.total_collateral, + offered_contract.id, + )?; + let (accept_params, funding_inputs) = crate::utils::get_party_params( total_collateral - offered_contract.offer_params.collateral, offered_contract.fee_rate_per_vb, wallet, + &signer, blockchain, )?; @@ -106,7 +126,7 @@ where offered_contract, &accept_params, &funding_inputs, - &fund_secret_key, + &signer.get_secret_key()?, fund_output_value, None, &dlc_transactions, @@ -216,14 +236,19 @@ pub(crate) fn accept_contract_internal( /// Verifies the information of the accepting party [`Accept` message](dlc_messages::AcceptDlc), /// creates a [`SignedContract`], and generates the offering party CET adaptor signatures. -pub fn verify_accepted_and_sign_contract( +pub fn verify_accepted_and_sign_contract< + W: Deref, + X: ContractSigner, + XFn: Fn(bool, u64, [u8; 32]) -> Result, +>( secp: &Secp256k1, offered_contract: &OfferedContract, accept_msg: &AcceptDlc, - signer: &S, + wallet: &W, + get_signer: XFn, ) -> Result<(SignedContract, SignDlc), Error> where - S::Target: Signer, + W::Target: Wallet, { let (tx_input_infos, input_amount) = get_tx_input_infos(&accept_msg.funding_inputs)?; @@ -258,8 +283,12 @@ where offered_contract.fund_output_serial_id, )?; let fund_output_value = dlc_transactions.get_fund_output().value; - let fund_privkey = - signer.get_secret_key_for_pubkey(&offered_contract.offer_params.fund_pubkey)?; + + let signer = get_signer( + offered_contract.is_offer_party, + offered_contract.total_collateral, + offered_contract.id, + )?; let (signed_contract, adaptor_sigs) = verify_accepted_and_sign_contract_internal( secp, offered_contract, @@ -272,8 +301,8 @@ where &accept_msg.refund_signature, &cet_adaptor_signatures, fund_output_value, - &fund_privkey, - signer, + wallet, + &signer, None, None, &dlc_transactions, @@ -310,7 +339,7 @@ fn populate_psbt( Ok(()) } -pub(crate) fn verify_accepted_and_sign_contract_internal( +pub(crate) fn verify_accepted_and_sign_contract_internal( secp: &Secp256k1, offered_contract: &OfferedContract, accept_params: &PartyParams, @@ -318,15 +347,15 @@ pub(crate) fn verify_accepted_and_sign_contract_internal( refund_signature: &Signature, cet_adaptor_signatures: &[EcdsaAdaptorSignature], input_value: u64, - adaptor_secret: &SecretKey, - signer: &S, + wallet: &W, + signer: &X, input_script_pubkey: Option