diff --git a/Cargo.toml b/Cargo.toml index 7c28419a..90d061c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,6 @@ members = [ ] [patch.crates-io] -lightning = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "74690beb" } -lightning-net-tokio = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "74690beb" } -lightning-persister = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "74690beb" } +lightning = { path = "../../rust-lightning/lightning" } +lightning-net-tokio = {path = "../../rust-lightning/lightning-net-tokio" } +lightning-persister = { path = "../../rust-lightning/lightning-persister" } diff --git a/dlc-manager/src/chain_monitor.rs b/dlc-manager/src/chain_monitor.rs index 135f87ac..4a178a62 100644 --- a/dlc-manager/src/chain_monitor.rs +++ b/dlc-manager/src/chain_monitor.rs @@ -84,6 +84,7 @@ impl ChainMonitor { } pub(crate) fn add_tx(&mut self, txid: Txid, channel_info: ChannelInfo) { + println!("ADDING: {}", txid); self.watched_tx.insert(txid, channel_info); } diff --git a/dlc-manager/src/channel/mod.rs b/dlc-manager/src/channel/mod.rs index 8e8d0b70..4cb7a275 100644 --- a/dlc-manager/src/channel/mod.rs +++ b/dlc-manager/src/channel/mod.rs @@ -1,6 +1,6 @@ //! # Module containing structures and methods for working with DLC channels. -use bitcoin::hashes::Hash; +use bitcoin::{hashes::Hash, Transaction, Txid}; use dlc_messages::channel::{AcceptChannel, SignChannel}; use secp256k1_zkp::PublicKey; @@ -27,6 +27,24 @@ pub enum Channel { Accepted(AcceptedChannel), /// A channel whose fund outputs have been signed by the offer party. Signed(SignedChannel), + /// A [`Channel`] is in `Closing` state when the local party + /// has broadcast a buffer transaction and is waiting to finalize the + /// closing of a the channel by broadcasting a CET. + Closing(ClosingChannel), + /// A [`Channel`] is in `Closed` state when it was force closed by + /// the local party. + Closed(ClosedChannel), + /// A [`Channel`] is in `CounterClosed` state when it was force + /// closed by the counter party. + CounterClosed(ClosedChannel), + /// A [`Channel`] is in `ClosedPublished` state when the local + /// party broadcast a punishment transaction in response to the counter + /// party broadcasting a settle or buffer transaction for a revoked channel + /// state. + ClosedPunished(ClosedPunishedChannel), + /// A [`SignedChannel`] is in `CollaborativelyClosed` state when it was + /// collaboratively closed. + CollaborativelyClosed(ClosedChannel), /// A channel that failed when validating an /// [`dlc_messages::channel::AcceptChannel`] message. FailedAccept(FailedAccept), @@ -43,8 +61,13 @@ impl std::fmt::Debug for Channel { Channel::Signed(_) => "signed", Channel::FailedAccept(_) => "failed accept", Channel::FailedSign(_) => "failed sign", + Channel::Closing(_) => "closing", + Channel::Closed(_) => "closed", + Channel::CounterClosed(_) => "counter closed", + Channel::ClosedPunished(_) => "closed punished", + Channel::CollaborativelyClosed(_) => "collaboratively closed", }; - f.debug_struct("Contract").field("state", &state).finish() + f.debug_struct("Channel").field("state", &state).finish() } } @@ -57,6 +80,11 @@ impl Channel { Channel::Signed(s) => s.counter_party, Channel::FailedAccept(f) => f.counter_party, Channel::FailedSign(f) => f.counter_party, + Channel::Closing(c) => c.counter_party, + Channel::Closed(c) | Channel::CounterClosed(c) | Channel::CollaborativelyClosed(c) => { + c.counter_party + } + Channel::ClosedPunished(c) => c.counter_party, } } } @@ -91,6 +119,52 @@ pub struct FailedSign { pub sign_message: SignChannel, } +#[derive(Clone)] +/// A channel is closing when its buffer transaction was broadcast or detected on chain. +pub struct ClosingChannel { + /// The [`secp256k1_zkp::PublicKey`] of the counter party. + pub counter_party: PublicKey, + /// The temporary [`crate::ChannelId`] of the channel. + pub temporary_channel_id: ChannelId, + /// The [`crate::ChannelId`] for the channel. + pub channel_id: ChannelId, + /// The previous state the channel was before being closed, if that state was the `Signed` one, + /// otherwise is `None`. + pub rollback_state: Option, + /// The buffer transaction that was broadcast. + pub buffer_transaction: Transaction, + /// The [`crate::ContractId`] of the contract that was used to close + /// the channel. + pub contract_id: ContractId, + /// Whether the local party initiated the closing of the channel. + pub is_initiator: bool, +} + +#[derive(Clone)] +/// A channel is closed when its buffer transaction has been spent. +pub struct ClosedChannel { + /// The [`secp256k1_zkp::PublicKey`] of the counter party. + pub counter_party: PublicKey, + /// The temporary [`crate::ChannelId`] of the channel. + pub temporary_channel_id: ChannelId, + /// The [`crate::ChannelId`] for the channel. + pub channel_id: ChannelId, +} + +#[derive(Clone)] +/// A channel is closed punished when the counter party broadcast a revoked transaction triggering +/// the broadcast of a punishment transaction by the local party. +pub struct ClosedPunishedChannel { + /// The [`secp256k1_zkp::PublicKey`] of the counter party. + pub counter_party: PublicKey, + /// The temporary [`crate::ChannelId`] of the channel. + pub temporary_channel_id: ChannelId, + /// The [`crate::ChannelId`] for the channel. + pub channel_id: ChannelId, + /// The transaction id of the punishment transaction that was broadcast. + pub punish_txid: Txid, +} + impl Channel { /// Returns the temporary [`crate::ChannelId`] for the channel. pub fn get_temporary_id(&self) -> ChannelId { @@ -99,6 +173,10 @@ impl Channel { Channel::Accepted(a) => a.temporary_channel_id, Channel::Signed(s) => s.temporary_channel_id, Channel::FailedAccept(f) => f.temporary_channel_id, + Channel::Closed(c) | Channel::CounterClosed(c) | Channel::CollaborativelyClosed(c) => { + c.temporary_channel_id + } + Channel::ClosedPunished(c) => c.temporary_channel_id, _ => unimplemented!(), } } @@ -111,6 +189,11 @@ impl Channel { Channel::Signed(s) => s.channel_id, Channel::FailedAccept(f) => f.temporary_channel_id, Channel::FailedSign(f) => f.channel_id, + Channel::Closing(c) => c.channel_id, + Channel::Closed(c) | Channel::CounterClosed(c) | Channel::CollaborativelyClosed(c) => { + c.channel_id + } + Channel::ClosedPunished(c) => c.channel_id, } } @@ -122,6 +205,7 @@ impl Channel { Channel::Signed(s) => s.get_contract_id(), Channel::FailedAccept(_) => None, Channel::FailedSign(_) => None, + _ => None, } } } diff --git a/dlc-manager/src/channel/ser.rs b/dlc-manager/src/channel/ser.rs index c3efabc9..feb706bd 100644 --- a/dlc-manager/src/channel/ser.rs +++ b/dlc-manager/src/channel/ser.rs @@ -3,7 +3,7 @@ use super::accepted_channel::AcceptedChannel; use super::offered_channel::OfferedChannel; use super::party_points::PartyBasePoints; use super::signed_channel::{SignedChannel, SignedChannelState}; -use super::{FailedAccept, FailedSign}; +use super::{ClosedChannel, ClosedPunishedChannel, ClosingChannel, FailedAccept, FailedSign}; use dlc_messages::ser_impls::{ read_ecdsa_adaptor_signature, read_string, write_ecdsa_adaptor_signature, write_string, @@ -60,12 +60,24 @@ impl_dlc_writeable_enum!( (6, RenewOffered, {(offered_contract_id, writeable), (counter_payout, writeable), (is_offer, writeable), (offer_next_per_update_point, writeable), (timeout, writeable)}), (7, RenewAccepted, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (timeout, writeable), (own_payout, writeable)}), (8, RenewConfirmed, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), - (15, RenewFinalized, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), - (9, Closing, {(buffer_transaction, writeable), (contract_id, writeable), (is_initiator, writeable)}), - (10, ClosedPunished, { (punishment_txid, writeable) }), + (9, RenewFinalized, {(contract_id, writeable), (offer_per_update_point, writeable), (accept_per_update_point, writeable), (buffer_transaction, writeable), (buffer_script_pubkey, writeable), (offer_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (accept_buffer_adaptor_signature, {cb_writeable, write_ecdsa_adaptor_signature, read_ecdsa_adaptor_signature}), (timeout, writeable), (own_payout, writeable), (total_collateral, writeable)}), + (10, Closing, {(buffer_transaction, writeable), (contract_id, writeable), (is_initiator, writeable)}), (11, CollaborativeCloseOffered, { (counter_payout, writeable), (offer_signature, writeable), (close_tx, writeable), (timeout, writeable) }) - ;;(12, Closed), (13, CounterClosed), (14, CollaborativelyClosed) + ;; ); impl_dlc_writeable!(FailedAccept, {(temporary_channel_id, writeable), (error_message, {cb_writeable, write_string, read_string}), (accept_message, writeable), (counter_party, writeable)}); impl_dlc_writeable!(FailedSign, {(channel_id, writeable), (error_message, {cb_writeable, write_string, read_string}), (sign_message, writeable), (counter_party, writeable)}); + +impl_dlc_writeable!(ClosingChannel, { + (channel_id, writeable), + (counter_party, writeable), + (temporary_channel_id, writeable), + (rollback_state, option), + (buffer_transaction, writeable), + (contract_id, writeable), + (is_initiator, writeable) + +}); +impl_dlc_writeable!(ClosedChannel, {(channel_id, writeable), (counter_party, writeable), (temporary_channel_id, writeable)}); +impl_dlc_writeable!(ClosedPunishedChannel, {(channel_id, writeable), (counter_party, writeable), (temporary_channel_id, writeable), (punish_txid, writeable)}); diff --git a/dlc-manager/src/channel/signed_channel.rs b/dlc-manager/src/channel/signed_channel.rs index 1146ca99..0951c080 100644 --- a/dlc-manager/src/channel/signed_channel.rs +++ b/dlc-manager/src/channel/signed_channel.rs @@ -2,7 +2,7 @@ //! transaction inputs. This module contains the model for a signed channel, //! the possible states in which it can be as well as methods to work with it. -use bitcoin::{Script, Transaction, Txid}; +use bitcoin::{Script, Transaction}; use dlc::PartyParams; use lightning::ln::chan_utils::CounterpartyCommitmentSecrets; use secp256k1_zkp::{ecdsa::Signature, EcdsaAdaptorSignature, PublicKey}; @@ -300,20 +300,6 @@ typed_enum!( /// Whether the local party initiated the closing of the channel. is_initiator: bool, }, - /// A [`SignedChannel`] is in `Closed` state when it was force closed by - /// the local party. - Closed, - /// A [`SignedChannel`] is in `CounterClosed` state when it was force - /// closed by the counter party. - CounterClosed, - /// A [`SignedChannel`] is in `ClosedPublished` state when the local - /// party broadcast a punishment transaction in response to the counter - /// party broadcasting a settle or buffer transaction for a revoked channel - /// state. - ClosedPunished { - /// The transaction id of the punishment transaction that was broadcast. - punishment_txid: Txid, - }, /// A [`SignedChannel`] is in `CollaborativeCloseOffered` state when the local party /// has sent a [`dlc_messages::channel::CollaborativeCloseOffer`] message. CollaborativeCloseOffered { @@ -327,9 +313,6 @@ typed_enum!( /// unresponsive and the channel will be forced closed. timeout: u64, }, - /// A [`SignedChannel`] is in `CollaborativelyClosed` state when it was - /// collaboratively closed. - CollaborativelyClosed, }, /// Enum automatically generated associating a number to each signed channel /// state. diff --git a/dlc-manager/src/channel_updater.rs b/dlc-manager/src/channel_updater.rs index c2c5bec7..e8eed253 100644 --- a/dlc-manager/src/channel_updater.rs +++ b/dlc-manager/src/channel_updater.rs @@ -9,6 +9,7 @@ use crate::{ offered_channel::OfferedChannel, party_points::PartyBasePoints, signed_channel::{SignedChannel, SignedChannelState}, + Channel, ClosedChannel, }, contract::{ accepted_contract::AcceptedContract, contract_info::ContractInfo, @@ -2265,9 +2266,9 @@ where /// closing transaction and returning it. pub fn accept_collaborative_close_offer( secp: &Secp256k1, - signed_channel: &mut SignedChannel, + signed_channel: &SignedChannel, signer: &S, -) -> Result +) -> Result<(Transaction, Channel), Error> where S::Target: Signer, { @@ -2295,8 +2296,12 @@ where )?; // TODO(tibo): should only transition to close after confirmation. - signed_channel.state = SignedChannelState::CollaborativelyClosed; - Ok(close_tx) + let channel = Channel::CollaborativelyClosed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }); + Ok((close_tx, channel)) } fn get_settle_tx_and_adaptor_sig( @@ -2528,13 +2533,14 @@ where /// Extract the CET and computes the signature for it, and marks the channel as closed. pub fn finalize_unilateral_close_settled_channel( secp: &Secp256k1, - signed_channel: &mut SignedChannel, + signed_channel: &SignedChannel, confirmed_contract: &SignedContract, contract_info: &ContractInfo, attestations: &[(usize, OracleAttestation)], adaptor_info: &AdaptorInfo, signer: &S, -) -> Result + is_initiator: bool, +) -> Result<(Transaction, Channel), Error> where S::Target: Signer, { @@ -2618,10 +2624,18 @@ where &adaptor_sigs[range_info.adaptor_index], &oracle_sigs, )?; + let closed_channel = ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }; + let channel = if is_initiator { + Channel::Closed(closed_channel) + } else { + Channel::CounterClosed(closed_channel) + }; - signed_channel.state = SignedChannelState::Closed; - - Ok(cet) + Ok((cet, channel)) } /// Sign the settlement transaction and update the state of the channel. @@ -2630,7 +2644,7 @@ pub fn close_settled_channel( signed_channel: &mut SignedChannel, signer: &S, is_initiator: bool, -) -> Result +) -> Result<(Transaction, Channel), Error> where S::Target: Signer, { @@ -2639,11 +2653,11 @@ where pub(crate) fn close_settled_channel_internal( secp: &Secp256k1, - signed_channel: &mut SignedChannel, + signed_channel: &SignedChannel, signer: &S, sub_channel: Option<(SubChannel, &ClosingSubChannel)>, is_initiator: bool, -) -> Result +) -> Result<(Transaction, Channel), Error> where S::Target: Signer, { @@ -2744,10 +2758,18 @@ where )?; } - signed_channel.state = if is_initiator { - SignedChannelState::Closed + let channel = if is_initiator { + Channel::Closed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }) } else { - SignedChannelState::CounterClosed + Channel::Closed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }) }; - Ok(settle_tx) + Ok((settle_tx, channel)) } diff --git a/dlc-manager/src/manager.rs b/dlc-manager/src/manager.rs index 393d08f7..d064fcf2 100644 --- a/dlc-manager/src/manager.rs +++ b/dlc-manager/src/manager.rs @@ -4,7 +4,7 @@ use super::{Blockchain, Oracle, Storage, Time, Wallet}; use crate::chain_monitor::{ChainMonitor, ChannelInfo, RevokedTxType, TxType}; use crate::channel::offered_channel::OfferedChannel; use crate::channel::signed_channel::{SignedChannel, SignedChannelState, SignedChannelStateType}; -use crate::channel::Channel; +use crate::channel::{Channel, ClosedChannel, ClosedPunishedChannel}; use crate::channel_updater::verify_signed_channel; use crate::channel_updater::{self, get_signed_channel_state}; use crate::contract::{ @@ -1092,7 +1092,7 @@ where None }; - let close_tx = crate::channel_updater::accept_collaborative_close_offer( + let (close_tx, closed_channel) = crate::channel_updater::accept_collaborative_close_offer( &self.secp, &mut signed_channel, &self.wallet, @@ -1100,8 +1100,7 @@ where self.blockchain.send_transaction(&close_tx)?; - self.store - .upsert_channel(Channel::Signed(signed_channel), None)?; + self.store.upsert_channel(closed_channel, None)?; if let Some(closed_contract) = closed_contract { self.store @@ -1123,11 +1122,13 @@ where is_initiator )?; + println!("AJA"); if self .blockchain .get_transaction_confirmations(&buffer_tx.txid())? > CET_NSEQUENCE { + println!("OK"); let confirmed_contract = get_contract_in_state!(self, contract_id, Confirmed, None as Option)?; @@ -1137,15 +1138,18 @@ where Error::InvalidState("Could not get information to close contract".to_string()) })?; - let signed_cet = channel_updater::finalize_unilateral_close_settled_channel( - &self.secp, - &mut signed_channel, - &confirmed_contract, - contract_info, - &attestations, - adaptor_info, - &self.wallet, - )?; + let (signed_cet, closed_channel) = + channel_updater::finalize_unilateral_close_settled_channel( + &self.secp, + &mut signed_channel, + &confirmed_contract, + contract_info, + &attestations, + adaptor_info, + &self.wallet, + is_initiator, + )?; + println!("OK"); let closed_contract = self.close_contract( &confirmed_contract, @@ -1153,19 +1157,13 @@ where attestations.iter().map(|x| &x.1).cloned().collect(), )?; - signed_channel.state = if is_initiator { - SignedChannelState::Closed - } else { - SignedChannelState::CounterClosed - }; - self.chain_monitor .lock() .unwrap() .cleanup_channel(signed_channel.channel_id); self.store - .upsert_channel(Channel::Signed(signed_channel), Some(closed_contract))?; + .upsert_channel(closed_channel, Some(closed_contract))?; } Ok(()) @@ -1960,7 +1958,7 @@ where _ => false, }; - let contract = if is_buffer_tx { + if is_buffer_tx { let contract_id = signed_channel .get_contract_id() .expect("to have a contract id"); @@ -1972,21 +1970,34 @@ where std::mem::swap(&mut signed_channel.state, &mut state); signed_channel.roll_back_state = Some(state); - None + self.store + .upsert_channel(Channel::Signed(signed_channel), None)?; } else { let contract_id = signed_channel.get_contract_id(); - signed_channel.state = { + let closed_channel = { match &signed_channel.state { SignedChannelState::Closing { is_initiator, .. } => { if *is_initiator { - SignedChannelState::Closed + Channel::Closed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }) } else { - SignedChannelState::CounterClosed + Channel::CounterClosed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }) } } _ => { error!("Saw spending of buffer transaction without being in closing state"); - SignedChannelState::Closed + Channel::Closed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }) } } }; @@ -1994,7 +2005,7 @@ where .lock() .unwrap() .cleanup_channel(signed_channel.channel_id); - if let Some(contract_id) = contract_id { + let contract = if let Some(contract_id) = contract_id { let contract_opt = self.store.get_contract(&contract_id)?; if let Some(contract) = contract_opt { match contract { @@ -2012,11 +2023,10 @@ where } } else { None - } + }; + self.store.upsert_channel(closed_channel, contract)?; }; - self.store - .upsert_channel(Channel::Signed(signed_channel), contract)?; !is_buffer_tx } else if let TxType::Revoked { update_idx, @@ -2157,9 +2167,12 @@ where self.blockchain.send_transaction(&signed_tx)?; - signed_channel.state = SignedChannelState::ClosedPunished { - punishment_txid: signed_tx.txid(), - }; + let closed_channel = Channel::ClosedPunished(ClosedPunishedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + punish_txid: signed_tx.txid(), + }); //TODO(tibo): should probably make sure the tx is confirmed somewhere before //stop watching the cheating tx. @@ -2167,8 +2180,7 @@ where .lock() .unwrap() .cleanup_channel(signed_channel.channel_id); - self.store - .upsert_channel(Channel::Signed(signed_channel), None)?; + self.store.upsert_channel(closed_channel, None)?; true } else if let TxType::CollaborativeClose = channel_info.tx_type { if let Some(SignedChannelState::Established { @@ -2188,22 +2200,28 @@ where self.store .update_contract(&Contract::Closed(closed_contract))?; } - signed_channel.state = SignedChannelState::CollaborativelyClosed; + let closed_channel = Channel::CollaborativelyClosed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }); self.chain_monitor .lock() .unwrap() .cleanup_channel(signed_channel.channel_id); - self.store - .upsert_channel(Channel::Signed(signed_channel), None)?; + self.store.upsert_channel(closed_channel, None)?; true } else if let TxType::SettleTx = channel_info.tx_type { - signed_channel.state = SignedChannelState::CounterClosed; + let closed_channel = Channel::CounterClosed(ClosedChannel { + counter_party: signed_channel.counter_party, + temporary_channel_id: signed_channel.temporary_channel_id, + channel_id: signed_channel.channel_id, + }); self.chain_monitor .lock() .unwrap() .cleanup_channel(signed_channel.channel_id); - self.store - .upsert_channel(Channel::Signed(signed_channel), None)?; + self.store.upsert_channel(closed_channel, None)?; true } else { false @@ -2316,12 +2334,6 @@ where SignedChannelState::Closing { .. } => Err(Error::InvalidState( "Channel is already closing.".to_string(), )), - SignedChannelState::Closed - | SignedChannelState::CounterClosed - | SignedChannelState::CollaborativelyClosed - | SignedChannelState::ClosedPunished { .. } => { - Err(Error::InvalidState("Channel already closed.".to_string())) - } } } @@ -2377,7 +2389,7 @@ where sub_channel: Option<(SubChannel, &ClosingSubChannel)>, is_initiator: bool, ) -> Result<(), Error> { - let settle_tx = crate::channel_updater::close_settled_channel_internal( + let (settle_tx, closed_channel) = crate::channel_updater::close_settled_channel_internal( &self.secp, &mut signed_channel, &self.wallet, @@ -2399,8 +2411,7 @@ where .unwrap() .cleanup_channel(signed_channel.channel_id); - self.store - .upsert_channel(Channel::Signed(signed_channel), None)?; + self.store.upsert_channel(closed_channel, None)?; Ok(()) } @@ -2411,8 +2422,8 @@ where &self, channel_id: ChannelId, own_balance: u64, - ) -> Result<(SignedChannel, Option), Error> { - let mut channel = get_channel_in_state!(self, &channel_id, Signed, None::)?; + ) -> Result<(Channel, Option), Error> { + let channel = get_channel_in_state!(self, &channel_id, Signed, None::)?; let contract = if let Some(contract_id) = channel.get_contract_id() { Some(Contract::Closed(self.get_collaboratively_closed_contract( @@ -2424,9 +2435,13 @@ where None }; - channel.state = SignedChannelState::CollaborativelyClosed; + let closed_channel = Channel::Closed(ClosedChannel { + counter_party: channel.counter_party, + temporary_channel_id: channel.temporary_channel_id, + channel_id, + }); - Ok((channel, contract)) + Ok((closed_channel, contract)) } fn get_collaboratively_closed_contract( diff --git a/dlc-manager/src/sub_channel_manager.rs b/dlc-manager/src/sub_channel_manager.rs index 128fe87c..a7115956 100644 --- a/dlc-manager/src/sub_channel_manager.rs +++ b/dlc-manager/src/sub_channel_manager.rs @@ -3,7 +3,7 @@ use std::{marker::PhantomData, ops::Deref, sync::Mutex}; -use bitcoin::{OutPoint, PackedLockTime, Script, Sequence}; +use bitcoin::{OutPoint, PackedLockTime, Script, Sequence, Transaction}; use dlc::{ channel::{get_tx_adaptor_signature, sub_channel::LN_GLUE_TX_WEIGHT}, PartyParams, @@ -38,12 +38,12 @@ use crate::{ chain_monitor::{ChannelInfo, RevokedTxType, TxType}, channel::{ generate_temporary_contract_id, offered_channel::OfferedChannel, - party_points::PartyBasePoints, Channel, + party_points::PartyBasePoints, Channel, ClosedChannel, }, channel_updater::{ self, FundingInfo, SubChannelSignInfo, SubChannelSignVerifyInfo, SubChannelVerifyInfo, }, - contract::{contract_input::ContractInput, Contract, FundingInputInfo}, + contract::{contract_input::ContractInput, ClosedContract, Contract, FundingInputInfo}, error::Error, manager::{get_channel_in_state, get_contract_in_state, Manager, CET_NSEQUENCE}, subchannel::{ @@ -583,6 +583,10 @@ where glue_tx_output_value, ); + let commitment_transactions = self + .ln_channel_manager + .get_latest_holder_commitment_txn(channel_lock); + let commitment_signed = self .ln_channel_manager .get_updated_funding_outpoint_commitment_signed( @@ -667,6 +671,7 @@ where split_tx, ln_glue_transaction: ln_glue_tx, ln_rollback: (&channel_details).into(), + commitment_transactions, }; offered_sub_channel.state = SubChannelState::Accepted(accepted_sub_channel); @@ -690,84 +695,186 @@ where /// Start force closing the sub channel with given [`ChannelId`]. pub fn force_close_sub_channel(&self, channel_id: &ChannelId) -> Result<(), Error> { - let (mut signed, state) = get_sub_channel_in_state!( - self.dlc_channel_manager, - *channel_id, - Signed, - None:: + let sub_channel = self + .dlc_channel_manager + .get_store() + .get_sub_channel(*channel_id)? + .ok_or(Error::InvalidParameters(format!( + "Unknown sub channel {:?}", + channel_id + )))?; + + match sub_channel.state { + SubChannelState::Offered(_) => self.force_close_offered_channel(sub_channel)?, + SubChannelState::Accepted(ref a) => { + let commitment_transactions = a.commitment_transactions.clone(); + + println!("IOIO"); + self.force_close_with_saved_commitment(sub_channel, &commitment_transactions)?; + } + SubChannelState::Signed(_) | SubChannelState::Finalized(_) => { + self.force_close_signed_channel(sub_channel)? + } + SubChannelState::Confirmed(ref c) => { + let commitment_transactions = c.commitment_transactions.clone(); + self.force_close_with_saved_commitment(sub_channel, &commitment_transactions)?; + } + SubChannelState::Closing(_) => todo!(), + SubChannelState::CloseOffered(_) => todo!(), + SubChannelState::CloseAccepted(_) => todo!(), + SubChannelState::CloseConfirmed(_) => todo!(), + _ => { + error!( + "Tried to force close channel with {:?} state", + sub_channel.state + ); + return Err(Error::InvalidParameters( + "Channel is not in a state to be force closed".to_string(), + )); + } + }; + + Ok(()) + } + + fn force_close_offered_channel(&self, mut sub_channel: SubChannel) -> Result<(), Error> { + println!("B"); + let (closed_channel, closed_contract) = self.get_closed_dlc_channel_and_contract( + sub_channel + .get_dlc_channel_id(0) + .expect("to have a channel id in offered state"), + false, )?; - let counter_party = signed.counter_party; - let channel_details = self - .ln_channel_manager - .get_channel_details(channel_id) - .unwrap(); - self.ln_channel_manager.with_channel_lock_no_check( - channel_id, - &counter_party, - |channel_lock| { - let publish_base_secret = self - .dlc_channel_manager - .get_wallet() - .get_secret_key_for_pubkey(&signed.own_base_points.publish_basepoint)?; + sub_channel.state = SubChannelState::OnChainClosed; + self.ln_channel_manager + .force_close_channel(&sub_channel.channel_id, &sub_channel.counter_party)?; + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&sub_channel)?; + self.dlc_channel_manager + .get_store() + .upsert_channel(closed_channel, Some(closed_contract))?; + let mut chain_monitor = self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + chain_monitor.cleanup_channel(sub_channel.channel_id); + self.dlc_channel_manager + .get_store() + .persist_chain_monitor(&chain_monitor) + } - let publish_sk = derive_private_key( - self.dlc_channel_manager.get_secp(), - &state.own_per_split_point, - &publish_base_secret, - ); + fn force_close_with_saved_commitment( + &self, + mut sub_channel: SubChannel, + commitment_transactions: &Vec, + ) -> Result<(), Error> { + for tx in commitment_transactions { + self.dlc_channel_manager + .get_blockchain() + .send_transaction(tx)?; + } + + let dlc_channel_id = sub_channel + .get_dlc_channel_id(0) + .expect("to have a channel id in offered state"); + println!("A"); + let (closed_channel, closed_contract) = + self.get_closed_dlc_channel_and_contract(dlc_channel_id, false)?; + sub_channel.state = SubChannelState::OnChainClosed; + self.dlc_channel_manager + .get_store() + .upsert_channel(closed_channel, Some(closed_contract))?; + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&sub_channel)?; + let mut chain_monitor = self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + chain_monitor.cleanup_channel(sub_channel.channel_id); + chain_monitor.cleanup_channel(dlc_channel_id); + self.dlc_channel_manager + .get_store() + .persist_chain_monitor(&chain_monitor) + } - let counter_split_signature = state - .counter_split_adaptor_signature - .decrypt(&publish_sk) - .map_err(|e| APIError::ExternalError { err: e.to_string() })?; + fn force_close_signed_channel(&self, mut sub_channel: SubChannel) -> Result<(), Error> { + if let SubChannelState::Signed(state) | SubChannelState::Finalized(state) = + sub_channel.state.clone() + { + let channel_id = sub_channel.channel_id; + let counter_party = sub_channel.counter_party; + let channel_details = self + .ln_channel_manager + .get_channel_details(&channel_id) + .unwrap(); + self.ln_channel_manager.with_channel_lock_no_check( + &channel_id, + &counter_party, + |channel_lock| { + let publish_base_secret = self + .dlc_channel_manager + .get_wallet() + .get_secret_key_for_pubkey( + &sub_channel.own_base_points.publish_basepoint, + )?; - let mut split_tx = state.split_tx.transaction.clone(); + let publish_sk = derive_private_key( + self.dlc_channel_manager.get_secp(), + &state.own_per_split_point, + &publish_base_secret, + ); - let mut own_sig = None; + let counter_split_signature = state + .counter_split_adaptor_signature + .decrypt(&publish_sk) + .map_err(|e| APIError::ExternalError { err: e.to_string() })?; - self.ln_channel_manager - .sign_with_fund_key_cb(channel_lock, &mut |fund_sk| { - own_sig = Some( - dlc::util::get_raw_sig_for_tx_input( + let mut split_tx = state.split_tx.transaction.clone(); + + let mut own_sig = None; + + self.ln_channel_manager + .sign_with_fund_key_cb(channel_lock, &mut |fund_sk| { + own_sig = Some( + dlc::util::get_raw_sig_for_tx_input( + self.dlc_channel_manager.get_secp(), + &split_tx, + 0, + &sub_channel.original_funding_redeemscript, + sub_channel.fund_value_satoshis, + fund_sk, + ) + .unwrap(), + ); + dlc::util::sign_multi_sig_input( self.dlc_channel_manager.get_secp(), - &split_tx, - 0, - &signed.original_funding_redeemscript, - signed.fund_value_satoshis, + &mut split_tx, + &counter_split_signature, + &channel_details.counter_funding_pubkey, fund_sk, + &sub_channel.original_funding_redeemscript, + sub_channel.fund_value_satoshis, + 0, ) - .unwrap(), - ); - dlc::util::sign_multi_sig_input( - self.dlc_channel_manager.get_secp(), - &mut split_tx, - &counter_split_signature, - &channel_details.counter_funding_pubkey, - fund_sk, - &signed.original_funding_redeemscript, - signed.fund_value_satoshis, - 0, - ) - .unwrap(); - }); - self.dlc_channel_manager - .get_blockchain() - .send_transaction(&split_tx)?; + .unwrap(); + }); + self.dlc_channel_manager + .get_blockchain() + .send_transaction(&split_tx)?; - let closing_sub_channel = ClosingSubChannel { - signed_sub_channel: state, - is_initiator: true, - }; + let closing_sub_channel = ClosingSubChannel { + signed_sub_channel: state, + is_initiator: true, + }; - signed.state = SubChannelState::Closing(closing_sub_channel); + sub_channel.state = SubChannelState::Closing(closing_sub_channel); - self.dlc_channel_manager - .get_store() - .upsert_sub_channel(&signed)?; + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&sub_channel)?; - Ok(()) - }, - )?; + Ok(()) + }, + )?; + } else { + unreachable!("Should not call this method if not in Signed state"); + } Ok(()) } @@ -885,6 +992,7 @@ where }; let mut chain_monitor = self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + println!("CLEANING UP CHANNEL"); chain_monitor.cleanup_channel(closing.channel_id); self.dlc_channel_manager .get_store() @@ -897,6 +1005,73 @@ where Ok(()) } + /// Notify that LDK has decided to close the channel with given id. + pub fn notify_ln_channel_closed(&self, channel_id: ChannelId) -> Result<(), Error> { + let mut sub_channel = self + .dlc_channel_manager + .get_store() + .get_sub_channel(channel_id)? + .ok_or(Error::InvalidParameters(format!( + "No channel with id {:?} found", + channel_id + )))?; + + let mut chain_monitor = self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + + let (updated_channel, updated_contract) = match sub_channel.state { + SubChannelState::Offered(_) + | SubChannelState::Accepted(_) + | SubChannelState::Confirmed(_) + | SubChannelState::Finalized(_) => { + let dlc_channel_id = sub_channel + .get_dlc_channel_id(0) + .expect("to have a channel id"); + let (closed_channel, closed_contract) = + self.get_closed_dlc_channel_and_contract(dlc_channel_id, true)?; + sub_channel.state = SubChannelState::CounterOnChainClosed; + chain_monitor.cleanup_channel(sub_channel.channel_id); + chain_monitor.cleanup_channel(dlc_channel_id); + (Some(closed_channel), Some(closed_contract)) + } + SubChannelState::CloseConfirmed(_) | SubChannelState::OffChainClosed => { + println!("SETTING TO COUNTER CLOSED"); + sub_channel.state = SubChannelState::CounterOnChainClosed; + (None, None) + } + SubChannelState::Signed(_) => todo!(), + SubChannelState::Closing(_) => { + todo!() + } + SubChannelState::OnChainClosed => (None, None), + SubChannelState::CounterOnChainClosed => todo!(), + SubChannelState::CloseOffered(_) => todo!(), + SubChannelState::CloseAccepted(_) => todo!(), + SubChannelState::ClosedPunished(_) => todo!(), + SubChannelState::Rejected => { + info!("Counterparty closed channel in rejected state, marking as counter closed"); + sub_channel.state = SubChannelState::CounterOnChainClosed; + (None, None) + } + }; + + if let Some(channel) = updated_channel { + println!("SAVING CHANNEL"); + self.dlc_channel_manager + .get_store() + .upsert_channel(channel, updated_contract)?; + } + + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&sub_channel)?; + + self.dlc_channel_manager + .get_store() + .persist_chain_monitor(&chain_monitor)?; + + Ok(()) + } + /// Generates an offer to collaboratively close a sub channel off chain, updating its state. pub fn offer_subchannel_close( &self, @@ -1397,9 +1572,15 @@ where glue_tx_output_value, ); - let (split_tx_adaptor_signature, commitment_signed, revoke_and_ack) = self - .ln_channel_manager - .with_useable_channel_lock(channel_id, counter_party, |channel_lock| { + let ( + split_tx_adaptor_signature, + commitment_signed, + revoke_and_ack, + commitment_transactions, + ) = self.ln_channel_manager.with_useable_channel_lock( + channel_id, + counter_party, + |channel_lock| { let mut split_tx_adaptor_signature = None; self.ln_channel_manager .sign_with_fund_key_cb(channel_lock, &mut |sk| { @@ -1418,6 +1599,10 @@ where let split_tx_adaptor_signature = split_tx_adaptor_signature.unwrap(); + let commitment_transactions = self + .ln_channel_manager + .get_latest_holder_commitment_txn(channel_lock); + let commitment_signed = self .ln_channel_manager .get_updated_funding_outpoint_commitment_signed( @@ -1439,8 +1624,10 @@ where split_tx_adaptor_signature, commitment_signed, revoke_and_ack, + commitment_transactions, )) - })?; + }, + )?; let accept_channel = AcceptChannel { temporary_channel_id: offered_channel.temporary_channel_id, @@ -1539,6 +1726,7 @@ where prev_commitment_secret: SecretKey::from_slice(&revoke_and_ack.per_commitment_secret) .expect("a valid secret key"), next_per_commitment_point: revoke_and_ack.next_per_commitment_point, + commitment_transactions, }; offered_sub_channel.counter_base_points = Some(accept_points); @@ -2191,13 +2379,21 @@ where sub_channel.state = SubChannelState::OffChainClosed; + let mut chain_monitor = + self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + chain_monitor.cleanup_channel(dlc_channel_id); + self.dlc_channel_manager .get_store() - .upsert_channel(Channel::Signed(dlc_channel), contract)?; + .upsert_channel(dlc_channel, contract)?; self.dlc_channel_manager .get_store() .upsert_sub_channel(&sub_channel)?; + + self.dlc_channel_manager + .get_store() + .persist_chain_monitor(&chain_monitor)?; Ok(finalize) }, )?; @@ -2266,13 +2462,21 @@ where sub_channel.state = SubChannelState::OffChainClosed; + let mut chain_monitor = + self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + chain_monitor.cleanup_channel(dlc_channel_id); + self.dlc_channel_manager .get_store() - .upsert_channel(Channel::Signed(dlc_channel), contract)?; + .upsert_channel(dlc_channel, contract)?; self.dlc_channel_manager .get_store() .upsert_sub_channel(&sub_channel)?; + + self.dlc_channel_manager + .get_store() + .persist_chain_monitor(&chain_monitor)?; Ok(()) }, )?; @@ -2428,6 +2632,7 @@ where }); for c in closing_sub_channels { + println!("HJ"); if let Err(e) = self.finalize_force_close_sub_channels(&c.channel_id) { warn!( "Could not finalize force closing of sub channel {:?}: {}", @@ -2501,11 +2706,12 @@ where continue; } _ => { - log::error!("Unexpected channel state"); + log::error!("Unexpected channel state {:?}", sub_channel.state); continue; } }; + log::info!("Spotted split transaction, marking sub channel as closing"); let closing_sub_channel = ClosingSubChannel { signed_sub_channel: state.clone(), is_initiator: false, @@ -3141,6 +3347,57 @@ where Ok(()) } + + fn get_closed_dlc_channel_and_contract( + &self, + channel_id: [u8; 32], + counter_closed: bool, + ) -> Result<(Channel, Contract), Error> { + println!("CHANNEL ID: {:?}", channel_id); + let channel = self + .dlc_channel_manager + .get_store() + .get_channel(&channel_id)? + .ok_or(Error::InvalidParameters(format!( + "No such channel {:?}", + channel_id + )))?; + println!("CHANNEL ID: {:?}", channel.get_id()); + let closed_channel_data = ClosedChannel { + counter_party: channel.get_counter_party_id(), + temporary_channel_id: channel.get_temporary_id(), + channel_id: channel.get_id(), + }; + let closed_channel = if counter_closed { + Channel::CounterClosed(closed_channel_data) + } else { + println!("Setting to closed"); + Channel::Closed(closed_channel_data) + }; + let contract_id = channel + .get_contract_id() + .ok_or(Error::InvalidParameters(format!( + "Channel {:?} does not have a contract associated", + channel_id + )))?; + let contract = self + .dlc_channel_manager + .get_store() + .get_contract(&contract_id)? + .ok_or(Error::InvalidParameters(format!( + "No such contract {:?}", + contract_id + )))?; + let closed_contract = Contract::Closed(ClosedContract { + attestations: None, + signed_cet: None, + contract_id, + temporary_contract_id: contract.get_id(), + counter_party_id: contract.get_counter_party_id(), + pnl: 0, + }); + Ok((closed_channel, closed_contract)) + } } impl< diff --git a/dlc-manager/src/subchannel/mod.rs b/dlc-manager/src/subchannel/mod.rs index 647ec268..fb7273e7 100644 --- a/dlc-manager/src/subchannel/mod.rs +++ b/dlc-manager/src/subchannel/mod.rs @@ -190,6 +190,8 @@ pub struct AcceptedSubChannel { pub ln_glue_transaction: Transaction, /// Information used to facilitate the rollback of a channel split. pub ln_rollback: LnRollBackInfo, + /// Commitment transactions to broadcast in order to force close the channel + pub commitment_transactions: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -247,6 +249,8 @@ pub struct ConfirmedSubChannel { pub next_per_commitment_point: PublicKey, /// Information used to facilitate the rollback of a channel split. pub ln_rollback: LnRollBackInfo, + /// Commitment transactions to broadcast in order to force close the channel + pub commitment_transactions: Vec, } impl ConfirmedSubChannel { @@ -414,6 +418,9 @@ where channel_value_satoshis: u64, value_to_self_msat: u64, ); + + /// + fn get_latest_holder_commitment_txn(&self, channel_lock: &ChannelLock) -> Vec; } impl @@ -506,6 +513,13 @@ where ); } + fn get_latest_holder_commitment_txn( + &self, + channel_lock: &ChannelLock<::Signer>, + ) -> Vec { + self.get_latest_holder_commitment_txn(channel_lock) + } + fn with_useable_channel_lock( &self, channel_id: &ChannelId, diff --git a/dlc-manager/src/subchannel/ser.rs b/dlc-manager/src/subchannel/ser.rs index 99519d71..e5179d2c 100644 --- a/dlc-manager/src/subchannel/ser.rs +++ b/dlc-manager/src/subchannel/ser.rs @@ -56,7 +56,8 @@ impl_dlc_writeable!(AcceptedSubChannel, { (accept_per_split_point, writeable), (split_tx, {cb_writeable, split_tx::write, split_tx::read}), (ln_glue_transaction, writeable), - (ln_rollback, writeable) + (ln_rollback, writeable), + (commitment_transactions, vec) }); impl_dlc_writeable!(ConfirmedSubChannel, { @@ -68,7 +69,8 @@ impl_dlc_writeable!(ConfirmedSubChannel, { (counter_glue_signature, writeable), (ln_rollback, writeable), (prev_commitment_secret, writeable), - (next_per_commitment_point, writeable) + (next_per_commitment_point, writeable), + (commitment_transactions, vec) }); impl_dlc_writeable!(SignedSubChannel, { diff --git a/dlc-manager/tests/channel_execution_tests.rs b/dlc-manager/tests/channel_execution_tests.rs index 4e1e6a11..4a88e85e 100644 --- a/dlc-manager/tests/channel_execution_tests.rs +++ b/dlc-manager/tests/channel_execution_tests.rs @@ -820,7 +820,7 @@ fn close_established_channel( .periodic_check() .expect("to be able to do the periodic check"); - assert_channel_state!(first, channel_id, Signed, Closed); + assert_channel_state!(first, channel_id, Closed); assert_contract_state!(first, contract_id, PreClosed); @@ -832,7 +832,7 @@ fn close_established_channel( .periodic_check() .expect("to be able to do the periodic check"); - assert_channel_state!(second, channel_id, Signed, CounterClosed); + assert_channel_state!(second, channel_id, CounterClosed); assert_contract_state!(second, contract_id, PreClosed); generate_blocks(5); @@ -875,7 +875,7 @@ fn cheat_punish( .periodic_check() .expect("the check to succeed"); - assert_channel_state!(second, channel_id, Signed, ClosedPunished); + assert_channel_state!(second, channel_id, ClosedPunished); } fn settle_channel( @@ -1227,7 +1227,7 @@ fn collaborative_close( .accept_collaborative_close(&channel_id) .expect("to be able to accept a collaborative close"); - assert_channel_state!(second, channel_id, Signed, CollaborativelyClosed); + assert_channel_state!(second, channel_id, CollaborativelyClosed); assert_contract_state!(second, contract_id, Closed); generate_blocks(2); @@ -1238,7 +1238,7 @@ fn collaborative_close( .periodic_check() .expect("the check to succeed"); - assert_channel_state!(first, channel_id, Signed, CollaborativelyClosed); + assert_channel_state!(first, channel_id, CollaborativelyClosed); assert_contract_state!(first, contract_id, Closed); } @@ -1279,7 +1279,7 @@ fn renew_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(first, channel_id, Signed, Closed); + assert_channel_state!(first, channel_id, Closed); } else { let (renew_accept, _) = second .lock() @@ -1306,7 +1306,7 @@ fn renew_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(second, channel_id, Signed, Closed); + assert_channel_state!(second, channel_id, Closed); } else if let TestPath::RenewConfirmTimeout = path { // Process Confirm second_receive.recv().expect("Error synchronizing"); @@ -1319,7 +1319,7 @@ fn renew_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(first, channel_id, Signed, Closed); + assert_channel_state!(first, channel_id, Closed); } else if let TestPath::RenewFinalizeTimeout = path { //Process confirm second_receive.recv().expect("Error synchronizing"); @@ -1340,7 +1340,7 @@ fn renew_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(second, channel_id, Signed, Closed); + assert_channel_state!(second, channel_id, Closed); } } } @@ -1407,7 +1407,7 @@ fn settle_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(second, channel_id, Signed, Closing); + assert_channel_state!(second, channel_id, Closing); } else if let TestPath::SettleConfirmTimeout = path { // Process Confirm second_receive.recv().expect("Error synchronizing"); @@ -1420,7 +1420,7 @@ fn settle_timeout( .periodic_check() .expect("not to error"); - assert_channel_state!(first, channel_id, Signed, Closing); + assert_channel_state!(first, channel_id, Closing); } } } diff --git a/dlc-manager/tests/console_logger.rs b/dlc-manager/tests/console_logger.rs index 52b185b3..eb622ab7 100644 --- a/dlc-manager/tests/console_logger.rs +++ b/dlc-manager/tests/console_logger.rs @@ -1,6 +1,7 @@ use chrono::Utc; use lightning::util::logger::{Logger, Record}; +#[derive(Debug)] pub(crate) struct ConsoleLogger { pub name: String, } diff --git a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs index 46ad01ed..edd9b7e1 100644 --- a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs +++ b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs @@ -54,6 +54,7 @@ use lightning::{ }; use lightning_persister::FilesystemPersister; use lightning_transaction_sync::EsploraSyncClient; +use log::error; use mocks::{ memory_storage_provider::MemoryStorage, mock_blockchain::MockBlockchain, @@ -71,7 +72,7 @@ type ChainMonitor = lightning::chain::chainmonitor::ChainMonitor< CustomSigner, Arc>>, Arc>>, - Arc, + Arc>>, Arc, Arc, >; @@ -82,7 +83,7 @@ pub(crate) type ChannelManager = lightning::ln::channelmanager::ChannelManager< Arc, Arc, Arc, - Arc, + Arc>>, Arc< DefaultRouter< Arc>>, @@ -162,6 +163,14 @@ enum TestPath { Reconnect, ReconnectReOfferAfterClose, DisconnectedForceClose, + OfferedForceClose, + OfferedForceClose2, + AcceptedForceClose, + AcceptedForceClose2, + ConfirmedForceClose, + ConfirmedForceClose2, + FinalizedForceClose, + FinalizedForceClose2, } impl LnDlcParty { @@ -179,6 +188,7 @@ impl LnDlcParty { fn process_events(&self) { self.peer_manager.process_events(); self.channel_manager.process_pending_events(self); + self.channel_manager.timer_tick_occurred(); self.chain_monitor.process_pending_events(self); } } @@ -351,6 +361,17 @@ impl EventHandler for LnDlcParty { .unwrap(); self.blockchain.broadcast_transaction(&spending_tx); } + Event::ChannelClosed { channel_id, .. } => { + if let Err(error) = self + .sub_channel_manager + .notify_ln_channel_closed(channel_id) + { + error!( + "Error notifying sub channel manager of LN channel closing: {}", + error + ); + } + } _ => { //Ignore } @@ -388,7 +409,7 @@ fn create_ln_node( Some(tx_sync.clone()), mock_blockchain.clone(), logger.clone(), - blockchain_provider.clone(), + mock_blockchain.clone(), persister.clone(), )); @@ -420,7 +441,7 @@ fn create_ln_node( }; Arc::new(ChannelManager::new( - blockchain_provider.clone(), + mock_blockchain.clone(), chain_monitor.clone(), mock_blockchain.clone(), router, @@ -589,6 +610,54 @@ fn ln_dlc_disconnected_force_close() { ln_dlc_test(TestPath::DisconnectedForceClose); } +#[test] +#[ignore] +fn ln_dlc_offered_force_close() { + ln_dlc_test(TestPath::OfferedForceClose); +} + +#[test] +#[ignore] +fn ln_dlc_offered_force_close2() { + ln_dlc_test(TestPath::OfferedForceClose2); +} + +#[test] +#[ignore] +fn ln_dlc_accepted_force_close() { + ln_dlc_test(TestPath::AcceptedForceClose); +} + +#[test] +#[ignore] +fn ln_dlc_accepted_force_close2() { + ln_dlc_test(TestPath::AcceptedForceClose2); +} + +#[test] +#[ignore] +fn ln_dlc_confirmed_force_close() { + ln_dlc_test(TestPath::ConfirmedForceClose); +} + +#[test] +#[ignore] +fn ln_dlc_confirmed_force_close2() { + ln_dlc_test(TestPath::ConfirmedForceClose2); +} + +#[test] +#[ignore] +fn ln_dlc_finalized_force_close() { + ln_dlc_test(TestPath::FinalizedForceClose); +} + +#[test] +#[ignore] +fn ln_dlc_finalized_force_close2() { + ln_dlc_test(TestPath::FinalizedForceClose2); +} + // #[derive(Debug)] // pub struct TestParams { // pub oracles: Vec, @@ -788,14 +857,11 @@ fn ln_dlc_test(test_path: TestPath) { let get_commit_tx_from_node = |node: &LnDlcParty| { let mut res = node .persister - .read_channelmonitors( - alice_node.keys_manager.clone(), - alice_node.keys_manager.clone(), - ) + .read_channelmonitors(node.keys_manager.clone(), node.keys_manager.clone()) .unwrap(); assert!(res.len() == 1); let (_, channel_monitor) = res.remove(0); - channel_monitor.get_latest_holder_commitment_txn(&alice_node.logger) + channel_monitor.get_latest_holder_commitment_txn(&node.logger) }; let pre_split_commit_tx = if let TestPath::CheatPreSplitCommit = test_path { @@ -822,6 +888,10 @@ fn ln_dlc_test(test_path: TestPath) { bob_descriptor.clone(), ); + // println!("Setting fee XXXX"); + // alice_node.mock_blockchain.set_est_fee(10000); + // alice_node.process_events(); + if let TestPath::CheatPreSplitCommit = test_path { let revoked_tx = pre_split_commit_tx.unwrap(); @@ -1097,6 +1167,191 @@ fn ln_dlc_test(test_path: TestPath) { return; } + if let TestPath::OfferedForceClose + | TestPath::OfferedForceClose2 + | TestPath::AcceptedForceClose + | TestPath::AcceptedForceClose2 + | TestPath::ConfirmedForceClose + | TestPath::ConfirmedForceClose2 + | TestPath::FinalizedForceClose + | TestPath::FinalizedForceClose2 = test_path + { + off_chain_close_offer( + &test_path, + &test_params, + &alice_node, + &bob_node, + channel_id, + alice_descriptor.clone(), + bob_descriptor.clone(), + ); + off_chain_close_finalize( + &test_path, + &alice_node, + &bob_node, + channel_id, + alice_descriptor.clone(), + bob_descriptor.clone(), + &test_params, + ); + + let offer = offer_common(&test_params, &alice_node, &channel_id); + bob_node + .sub_channel_manager + .on_sub_channel_message( + &SubChannelMessage::Offer(offer), + &alice_node.channel_manager.get_our_node_id(), + ) + .unwrap(); + if let TestPath::AcceptedForceClose + | TestPath::AcceptedForceClose2 + | TestPath::ConfirmedForceClose + | TestPath::ConfirmedForceClose2 + | TestPath::FinalizedForceClose + | TestPath::FinalizedForceClose2 = test_path + { + let (_, accept) = bob_node + .sub_channel_manager + .accept_sub_channel(&channel_id) + .unwrap(); + if let TestPath::ConfirmedForceClose + | TestPath::ConfirmedForceClose2 + | TestPath::FinalizedForceClose + | TestPath::FinalizedForceClose2 = test_path + { + let confirm = alice_node + .sub_channel_manager + .on_sub_channel_message( + &SubChannelMessage::Accept(accept), + &bob_node.channel_manager.get_our_node_id(), + ) + .unwrap() + .unwrap(); + if let TestPath::FinalizedForceClose | TestPath::FinalizedForceClose2 = test_path { + bob_node + .sub_channel_manager + .on_sub_channel_message( + &confirm, + &alice_node.channel_manager.get_our_node_id(), + ) + .unwrap(); + } + } + } + + let (mut closer, mut closee) = if let TestPath::OfferedForceClose + | TestPath::AcceptedForceClose + | TestPath::ConfirmedForceClose + | TestPath::FinalizedForceClose = test_path + { + (alice_node, bob_node) + } else { + (bob_node, alice_node) + }; + + let sub_channel = closer + .dlc_manager + .get_store() + .get_sub_channel(channel_id) + .unwrap() + .unwrap(); + + let commit_tx = if let TestPath::OfferedForceClose + | TestPath::OfferedForceClose2 + | TestPath::AcceptedForceClose = test_path + { + get_commit_tx_from_node(&closer).remove(0) + } else if let TestPath::AcceptedForceClose2 | TestPath::ConfirmedForceClose2 = test_path { + if let SubChannelState::Accepted(a) = &sub_channel.state { + a.commitment_transactions[0].clone() + } else { + unreachable!(); + } + } else if let TestPath::ConfirmedForceClose | TestPath::FinalizedForceClose = test_path { + if let SubChannelState::Confirmed(c) = &sub_channel.state { + c.commitment_transactions[0].clone() + } else { + unreachable!(); + } + } else { + get_commit_tx_from_node(&closer).remove(0) + }; + + let dlc_channel_id_closer = sub_channel.get_dlc_channel_id(0).unwrap(); + + let sub_channel = closee + .dlc_manager + .get_store() + .get_sub_channel(channel_id) + .unwrap() + .unwrap(); + let dlc_channel_id_closee = sub_channel.get_dlc_channel_id(0).unwrap(); + + closer + .sub_channel_manager + .force_close_sub_channel(&channel_id) + .expect("To be able to force close offered channel"); + + if let TestPath::ConfirmedForceClose + | TestPath::FinalizedForceClose + | TestPath::FinalizedForceClose2 = test_path + { + // assert_sub_channel_state!(closer.sub_channel_manager, &channel_id, Closing); + generate_blocks(500); + closer.update_to_chain_tip(); + closer.process_events(); + } + + assert_sub_channel_state!(closer.sub_channel_manager, &channel_id; OnChainClosed); + + generate_blocks(3); + + closer.update_to_chain_tip(); + closee.update_to_chain_tip(); + closee.process_events(); + + assert_sub_channel_state!(closee.sub_channel_manager, &channel_id; CounterOnChainClosed); + + generate_blocks(500); + + closer.update_to_chain_tip(); + closer.process_events(); + closee.update_to_chain_tip(); + closee.process_events(); + + assert_channel_state_unlocked!(closer.dlc_manager, dlc_channel_id_closer, Closed); + assert_channel_state_unlocked!(closee.dlc_manager, dlc_channel_id_closee, CounterClosed); + + assert!(closer + .dlc_manager + .get_chain_monitor() + .lock() + .unwrap() + .is_empty()); + assert!(closee + .dlc_manager + .get_chain_monitor() + .lock() + .unwrap() + .is_empty()); + + println!("Commit tx id: {}", commit_tx.txid()); + let all_spent = electrs + .get_outspends(&commit_tx.txid()) + .unwrap() + .into_iter() + .all(|x| { + if let OutSpendResp::Spent(_) = x { + true + } else { + false + } + }); + + assert!(all_spent); + return; + } + let commit_tx = get_commit_tx_from_node(&alice_node).remove(0); if let TestPath::CheatPostSplitCommit = test_path { @@ -1171,8 +1426,8 @@ fn ln_dlc_test(test_path: TestPath) { generate_blocks(1); bob_node.update_to_chain_tip(); - assert_channel_state_unlocked!(alice_node.dlc_manager, dlc_channel_id, Signed, Closed); - assert_channel_state_unlocked!(bob_node.dlc_manager, dlc_channel_id, Signed, CounterClosed); + assert_channel_state_unlocked!(alice_node.dlc_manager, dlc_channel_id, Closed); + assert_channel_state_unlocked!(bob_node.dlc_manager, dlc_channel_id, CounterClosed); assert_contract_state_unlocked!(alice_node.dlc_manager, contract_id, PreClosed); assert_contract_state_unlocked!(bob_node.dlc_manager, contract_id, PreClosed); @@ -1188,8 +1443,8 @@ fn ln_dlc_test(test_path: TestPath) { assert_contract_state_unlocked!(alice_node.dlc_manager, contract_id, Closed); assert_contract_state_unlocked!(bob_node.dlc_manager, contract_id, Closed); } else { - assert_channel_state_unlocked!(alice_node.dlc_manager, dlc_channel_id, Signed, Closed); - assert_channel_state_unlocked!(bob_node.dlc_manager, dlc_channel_id, Signed, CounterClosed); + assert_channel_state_unlocked!(alice_node.dlc_manager, dlc_channel_id, Closed); + assert_channel_state_unlocked!(bob_node.dlc_manager, dlc_channel_id, CounterClosed); } generate_blocks(500); @@ -1957,15 +2212,9 @@ fn off_chain_close_finalize( assert_channel_state_unlocked!( alice_node.dlc_manager, dlc_channel_id, - Signed, - CollaborativelyClosed - ); - assert_channel_state_unlocked!( - bob_node.dlc_manager, - dlc_channel_id, - Signed, CollaborativelyClosed ); + assert_channel_state_unlocked!(bob_node.dlc_manager, dlc_channel_id, CollaborativelyClosed); assert_sub_channel_state!(alice_node.sub_channel_manager, &channel_id; OffChainClosed); assert_sub_channel_state!(bob_node.sub_channel_manager, &channel_id; OffChainClosed); diff --git a/dlc-manager/tests/test_utils.rs b/dlc-manager/tests/test_utils.rs index e5b2c8ae..89fc48c5 100644 --- a/dlc-manager/tests/test_utils.rs +++ b/dlc-manager/tests/test_utils.rs @@ -192,7 +192,7 @@ macro_rules! assert_channel_state_unlocked { let res = $d .get_store() .get_channel(&$id) - .expect("Could not retrieve contract"); + .expect("Could not retrieve channel"); if let Some(Channel::$p(c)) = res { $(if let dlc_manager::channel::signed_channel::SignedChannelState::$s { .. } = c.state { } else { @@ -203,15 +203,7 @@ macro_rules! assert_channel_state_unlocked { write_channel!(channel, $p); } } else { - let state = match res { - Some(Channel::Offered(_)) => "offered", - Some(Channel::Accepted(_)) => "accepted", - Some(Channel::Signed(_)) => "signed", - Some(Channel::FailedAccept(_)) => "failed accept", - Some(Channel::FailedSign(_)) => "failed sign", - None => "none", - }; - panic!("Unexpected channel state {}", state); + panic!("Could not find requested channel"); } }}; } diff --git a/dlc-sled-storage-provider/src/lib.rs b/dlc-sled-storage-provider/src/lib.rs index 90e82cf5..570a4757 100644 --- a/dlc-sled-storage-provider/src/lib.rs +++ b/dlc-sled-storage-provider/src/lib.rs @@ -20,7 +20,9 @@ use dlc_manager::chain_monitor::ChainMonitor; use dlc_manager::channel::accepted_channel::AcceptedChannel; use dlc_manager::channel::offered_channel::OfferedChannel; use dlc_manager::channel::signed_channel::{SignedChannel, SignedChannelStateType}; -use dlc_manager::channel::{Channel, FailedAccept, FailedSign}; +use dlc_manager::channel::{ + Channel, ClosedChannel, ClosedPunishedChannel, ClosingChannel, FailedAccept, FailedSign, +}; use dlc_manager::contract::accepted_contract::AcceptedContract; use dlc_manager::contract::offered_contract::OfferedContract; use dlc_manager::contract::ser::Serializable; @@ -122,6 +124,11 @@ convertible_enum!( Offered = 1, Accepted, Signed; SignedChannelPrefix, state, + Closing, + Closed, + CounterClosed, + ClosedPunished, + CollaborativelyClosed, FailedAccept, FailedSign,; }, @@ -137,11 +144,7 @@ convertible_enum!( SettledConfirmed, Settled, Closing, - Closed, - CounterClosed, - ClosedPunished, CollaborativeCloseOffered, - CollaborativelyClosed, RenewAccepted, RenewOffered, RenewConfirmed, @@ -708,6 +711,11 @@ fn serialize_channel(channel: &Channel) -> Result, ::std::io::Error> { Channel::Signed(s) => s.serialize(), Channel::FailedAccept(f) => f.serialize(), Channel::FailedSign(f) => f.serialize(), + Channel::Closing(c) => c.serialize(), + Channel::Closed(c) | Channel::CounterClosed(c) | Channel::CollaborativelyClosed(c) => { + c.serialize() + } + Channel::ClosedPunished(c) => c.serialize(), }; let mut serialized = serialized?; let mut res = Vec::with_capacity(serialized.len() + 1); @@ -742,6 +750,21 @@ fn deserialize_channel(buff: &sled::IVec) -> Result { ChannelPrefix::FailedSign => { Channel::FailedSign(FailedSign::deserialize(&mut cursor).map_err(to_storage_error)?) } + ChannelPrefix::Closing => { + Channel::Closing(ClosingChannel::deserialize(&mut cursor).map_err(to_storage_error)?) + } + ChannelPrefix::Closed => { + Channel::Closed(ClosedChannel::deserialize(&mut cursor).map_err(to_storage_error)?) + } + ChannelPrefix::CollaborativelyClosed => Channel::CollaborativelyClosed( + ClosedChannel::deserialize(&mut cursor).map_err(to_storage_error)?, + ), + ChannelPrefix::CounterClosed => Channel::CounterClosed( + ClosedChannel::deserialize(&mut cursor).map_err(to_storage_error)?, + ), + ChannelPrefix::ClosedPunished => Channel::ClosedPunished( + ClosedPunishedChannel::deserialize(&mut cursor).map_err(to_storage_error)?, + ), }; Ok(channel) } diff --git a/electrs-blockchain-provider/src/lib.rs b/electrs-blockchain-provider/src/lib.rs index 41c09f26..c2607b9a 100644 --- a/electrs-blockchain-provider/src/lib.rs +++ b/electrs-blockchain-provider/src/lib.rs @@ -106,6 +106,7 @@ impl ElectrsBlockchainProvider { impl Blockchain for ElectrsBlockchainProvider { fn send_transaction(&self, transaction: &Transaction) -> Result<(), dlc_manager::error::Error> { + println!("Sending tx: {}", transaction.txid()); let res = self .client .post(format!("{}tx", self.host)) diff --git a/mocks/src/mock_blockchain.rs b/mocks/src/mock_blockchain.rs index 4a2fb644..76a7b833 100644 --- a/mocks/src/mock_blockchain.rs +++ b/mocks/src/mock_blockchain.rs @@ -12,6 +12,7 @@ where inner: T, discard: Mutex, discard_ids: Mutex>, + est_fee: Mutex, } impl MockBlockchain @@ -23,6 +24,7 @@ where inner, discard: Mutex::new(false), discard_ids: Mutex::new(Vec::new()), + est_fee: Mutex::new(500), } } @@ -33,6 +35,10 @@ where pub fn discard_id(&self, txid: Txid) { self.discard_ids.lock().unwrap().push(txid); } + + pub fn set_est_fee(&self, est_fee: u32) { + *self.est_fee.lock().unwrap() = est_fee; + } } impl BroadcasterInterface for MockBlockchain @@ -83,7 +89,8 @@ where &self, _confirmation_target: lightning::chain::chaininterface::ConfirmationTarget, ) -> u32 { - unimplemented!() + println!("Getting estimate of {}", *self.est_fee.lock().unwrap()); + *self.est_fee.lock().unwrap() } }