From 099bbb044a5a9f71fc4878192cd634d7181f2209 Mon Sep 17 00:00:00 2001 From: benthecarman Date: Tue, 28 Nov 2023 00:10:26 -0600 Subject: [PATCH] Add ability to handle external closures of contracts --- dlc-manager/src/manager.rs | 55 +++++++++++++++++++++++++++++++++++++- dlc/src/lib.rs | 8 ++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/dlc-manager/src/manager.rs b/dlc-manager/src/manager.rs index 63fdd433..fed77e82 100644 --- a/dlc-manager/src/manager.rs +++ b/dlc-manager/src/manager.rs @@ -19,7 +19,7 @@ use crate::utils::same_nonces; use crate::Signer; use crate::{ChannelId, ContractId}; use bitcoin::Transaction; -use bitcoin::{locktime, Address, LockTime}; +use bitcoin::{locktime, Address, LockTime, OutPoint}; use dlc_messages::channel::{ AcceptChannel, CollaborativeCloseOffer, OfferChannel, Reject, RenewAccept, RenewConfirm, RenewFinalize, RenewOffer, SettleAccept, SettleConfirm, SettleFinalize, SettleOffer, @@ -361,6 +361,22 @@ where Ok(()) } + /// Outputs to watch for on the blockchain. This is used to detect when a contract is closed + /// by our counter party. + /// + /// If a contract is closed by our counter party, the [`on_counterparty_close`] method should + /// be called. + pub fn outputs_to_watch(&self) -> Result, Error> { + let contracts: Vec = self.store.get_confirmed_contracts()?; + + let outpoints = contracts + .into_iter() + .map(|c| (c.accepted_contract.dlc_transactions.get_fund_outpoint(), c)) + .collect(); + + Ok(outpoints) + } + fn on_offer_message( &mut self, offered_message: &OfferDlc, @@ -802,6 +818,43 @@ where Ok(()) } + + /// Function to call when we detect that a contract was closed by our counter party. + /// This will update the state of the contract and return the [`Contract`] object. + pub fn on_counterparty_close( + &mut self, + contract: &SignedContract, + closing_tx: Transaction, + confirmations: u32, + ) -> Result { + // check if it is the refund tx (easy case) + if contract.accepted_contract.dlc_transactions.refund.txid() == closing_tx.txid() { + let refunded = Contract::Refunded(contract.clone()); + self.store.update_contract(&refunded)?; + return Ok(refunded); + } + + let contract = if confirmations < NB_CONFIRMATIONS { + Contract::PreClosed(PreClosedContract { + signed_contract: contract.clone(), + attestations: None, // todo in some cases we can get the attestations from the closing tx + signed_cet: closing_tx, + }) + } else { + Contract::Closed(ClosedContract { + attestations: None, // todo in some cases we can get the attestations from the closing tx + pnl: contract.accepted_contract.compute_pnl(&closing_tx), + signed_cet: Some(closing_tx), + contract_id: contract.accepted_contract.get_contract_id(), + temporary_contract_id: contract.accepted_contract.offered_contract.id, + counter_party_id: contract.accepted_contract.offered_contract.counter_party, + }) + }; + + self.store.update_contract(&contract)?; + + Ok(contract) + } } impl Manager diff --git a/dlc/src/lib.rs b/dlc/src/lib.rs index 9ec63397..f6ea1aa9 100644 --- a/dlc/src/lib.rs +++ b/dlc/src/lib.rs @@ -144,6 +144,14 @@ impl DlcTransactions { .unwrap() .0 } + + /// Get the outpoint for the fund output in the fund transaction + pub fn get_fund_outpoint(&self) -> OutPoint { + OutPoint { + txid: self.fund.txid(), + vout: self.get_fund_output_index() as u32, + } + } } /// Contains info about a utxo used for funding a DLC contract