From e6197d15c66ccb2dd6ef5fa2f9419a24d89fae35 Mon Sep 17 00:00:00 2001 From: Tibo-lg Date: Tue, 15 Aug 2023 10:45:14 +0900 Subject: [PATCH] Handle auto close from LDK --- Cargo.toml | 6 +- dlc-manager/ClosingSubChannel | Bin 3387 -> 0 bytes dlc-manager/ConfirmedSubChannel | Bin 3634 -> 0 bytes dlc-manager/CounterOnChainClosedSubChannel | Bin 2424 -> 0 bytes dlc-manager/FinalizedSubChannel | Bin 3385 -> 0 bytes dlc-manager/OnChainClosedSubChannel | Bin 2424 -> 0 bytes dlc-manager/src/sub_channel_manager.rs | 510 +++++++++++------- dlc-manager/src/subchannel/mod.rs | 133 +++-- dlc-manager/src/subchannel/ser.rs | 3 +- dlc-manager/tests/custom_signer.rs | 53 ++ .../tests/ln_dlc_channel_execution_tests.rs | 376 +++++++++++-- dlc-sled-storage-provider/test_files/Accepted | Bin 3430 -> 3430 bytes .../test_files/AcceptedChannel | Bin 908 -> 908 bytes .../test_files/AcceptedSubChannel | Bin 3343 -> 3375 bytes dlc-sled-storage-provider/test_files/Closed | Bin 1109 -> 1109 bytes .../test_files/Confirmed | Bin 5983 -> 5983 bytes .../test_files/Confirmed1 | Bin 5983 -> 5983 bytes dlc-sled-storage-provider/test_files/Offered | Bin 1107 -> 1107 bytes .../test_files/OfferedChannel | Bin 235 -> 235 bytes .../test_files/OfferedSubChannel | Bin 2424 -> 2456 bytes .../test_files/OfferedSubChannel1 | Bin 2424 -> 2456 bytes .../test_files/PreClosed | Bin 6984 -> 6874 bytes dlc-sled-storage-provider/test_files/Signed | Bin 5873 -> 5873 bytes dlc-sled-storage-provider/test_files/Signed1 | Bin 5873 -> 5873 bytes .../test_files/SignedChannelEstablished | Bin 3663 -> 3663 bytes .../test_files/SignedChannelSettled | Bin 3681 -> 3681 bytes .../test_files/SignedSubChannel | Bin 3385 -> 3417 bytes dlc/src/util.rs | 48 +- mocks/src/mock_blockchain.rs | 8 +- 29 files changed, 862 insertions(+), 275 deletions(-) delete mode 100644 dlc-manager/ClosingSubChannel delete mode 100644 dlc-manager/ConfirmedSubChannel delete mode 100644 dlc-manager/CounterOnChainClosedSubChannel delete mode 100644 dlc-manager/FinalizedSubChannel delete mode 100644 dlc-manager/OnChainClosedSubChannel diff --git a/Cargo.toml b/Cargo.toml index 1c0523a5..62b6e2ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,6 @@ members = [ ] [patch.crates-io] -lightning = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "420d961d" } -lightning-net-tokio = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "420d961d" } -lightning-persister = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "420d961d" } +lightning = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "47c8479" } +lightning-net-tokio = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "47c8479" } +lightning-persister = { git = "https://github.com/p2pderivatives/rust-lightning/", rev = "47c8479" } diff --git a/dlc-manager/ClosingSubChannel b/dlc-manager/ClosingSubChannel deleted file mode 100644 index 5a4c4282d64a513d33176c7ab828be2afcdc2d9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3387 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 Ab^rhX diff --git a/dlc-manager/FinalizedSubChannel b/dlc-manager/FinalizedSubChannel deleted file mode 100644 index 5dc6b7dbfac06cb17a7a03370750bf7760e17b11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3385 zcmd%ol=N6oyh`Qh#*JL_;-hjnT8>?OVcV54`6AQbJ?|SH$-Q~; zvR(7<*SvpgW4?R{Xim{i*`ee0@J~cD6VtJDj@CeqQlF?$TlLdEU%9$gUOP1J^M^;? z;r~;;^cS@oIS3hQO^%Es{)@;}(?k|O$~fe}-co5AYHhaC zY2KxKc^QYpp7ySl=VP8=VYOp_*tEqS=lMiJu3o>i&HZ?Pv%~yT*EkM5NuM)ECvMeB zpAD~N4>32D?OV6wjCtFkt)Fs*O-f(fd6RretTIGoo}|j8*_Z3g=AX+Kd$Ono{`_-QQFiKyvTV9*(W#tauu=jKB zl{Y`szV+a$pZ^v)ThXx=C?UJ^)=kF4 zHtDlNH%SQn{{DN#!y`UmiQ0UF14W5%`%{@`bS`$tUA(_%VpB&tP{*#wPpdQYHtd|B z)KV6BEAgPHwL~sh$DOmjHxvY1?lNteQzZ5$)%5zAHLPHXUXR-^kFl{o%}&ja<7t#iTjTnO| zW8ay#eVVg7_Rg%4n=0@u^KtP5Uj>fHaIGiDW*r3wg#s|-80`^3y?K_l^&L- zoVAz#&b6@KDd2i%vfIYD={;L^$o^jX;?d)uoYzF&rY|?U@aOQh`(TTi_A7vB)>y9w z!fAq$xlq~u;!3{xk65~IpAl6MmOCjh-(an`^m)~P<-6ImUt9p23G@onF8x>EKMMTU z$?Xb%VIrG+F5seO_jJ;();nAyj=hsN4xCisi=VH}ZaW~i9udZN@5s9(xLUE_J4KGjcZzCse8@))ff0W!rOsy#op&_aH?k zW3EY?!<66Hb~jpTt}m`~ld(ML)lmK4Yw}g245KAlhZLEQ7+V$DRW9WJ+U{&H&otuj z=X3LxQ;>FcX5HDA6IGBJPm zlv5$ef9CA)sXQ;b$*1N?!N&az4FAD^lacx0{;0!k`aZl47d~$J;%K>T)r{r8H~()_ zGP@AGYtg=C(F_p4$iz2eb-}~RmZP)Ig-5UAonIrF;vUR5pNmyv#ob(Yzsh1JS(T$7 zAM!LYZ>yd#dE?rB3tA>U*PUJbbMD)IiEtjR!wZ;XqFr)iw;8Jldh99axzw4`&B)y> zoKUHrSXUs}mTk|)$h1rU)%TAA|8;V^!e5xkCZCHqu;)K(@QNtIJ9Y7T>%As3dl`Q{ z_OD_?lcB@INz;}uRAy;hldIDw*DoWiw59d2@l&Qr8dXyzF4j-^)VlOv)z_0Nik30Y zDavv5yS*y8^ZUxHrC{GM?N?xM4^m_@=9;uQO!CSNtm zFj}H@NRj!7u~m^>R!v!7`D2(A2E&!)d B)p`H` diff --git a/dlc-manager/src/sub_channel_manager.rs b/dlc-manager/src/sub_channel_manager.rs index ec8a22b6..2cd191ec 100644 --- a/dlc-manager/src/sub_channel_manager.rs +++ b/dlc-manager/src/sub_channel_manager.rs @@ -1,13 +1,10 @@ //! # Module containing a manager enabling set up and update of DLC channels embedded within //! Lightning Network channels. -use std::{marker::PhantomData, ops::Deref, sync::Mutex}; +use std::{collections::HashMap, marker::PhantomData, ops::Deref, sync::Mutex}; use bitcoin::{OutPoint, PackedLockTime, Script, Sequence, Transaction}; -use dlc::{ - channel::{get_tx_adaptor_signature, sub_channel::LN_GLUE_TX_WEIGHT}, - PartyParams, -}; +use dlc::{channel::sub_channel::LN_GLUE_TX_WEIGHT, PartyParams}; use dlc_messages::{ channel::{AcceptChannel, OfferChannel}, oracle_msgs::OracleAnnouncement, @@ -28,14 +25,17 @@ use lightning::{ channelmanager::ChannelDetails, msgs::{ChannelMessageHandler, DecodeError, RevokeAndACK}, }, - util::ser::{Readable, Writeable, Writer}, util::{errors::APIError, events::MessageSendEventsProvider}, + util::{ + events::ClosureReason, + ser::{Readable, Writeable, Writer}, + }, }; use log::{error, info, trace, warn}; use secp256k1_zkp::{ecdsa::Signature, PublicKey, SecretKey}; use crate::{ - chain_monitor::{ChannelInfo, RevokedTxType, TxType}, + chain_monitor::{ChainMonitor, ChannelInfo, RevokedTxType, TxType}, channel::{ generate_temporary_contract_id, offered_channel::OfferedChannel, party_points::PartyBasePoints, Channel, ClosedChannel, @@ -49,8 +49,8 @@ use crate::{ subchannel::{ self, generate_temporary_channel_id, AcceptedSubChannel, CloseAcceptedSubChannel, CloseConfirmedSubChannel, CloseOfferedSubChannel, ClosingSubChannel, ConfirmedSubChannel, - LNChannelManager, OfferedSubChannel, ReestablishFlag, SignedSubChannel, SubChannel, - SubChannelState, + LNChainMonitor, LNChannelManager, LnDlcChannelSigner, LnDlcSignerProvider, + OfferedSubChannel, ReestablishFlag, SignedSubChannel, SubChannel, SubChannelState, }, Blockchain, ChannelId, ContractId, Oracle, Signer, Storage, Time, Wallet, }; @@ -157,56 +157,77 @@ impl_dlc_writeable_enum!(Action, pub struct SubChannelManager< W: Deref, M: Deref, + C: Deref, S: Deref, B: Deref, O: Deref, T: Deref, F: Deref, D: Deref>, - SP: ChannelSigner, + CS: ChannelSigner, + SP: Deref, + LCS: LnDlcChannelSigner, > where W::Target: Wallet, - M::Target: LNChannelManager, + M::Target: LNChannelManager, + C::Target: LNChainMonitor, S::Target: Storage, B::Target: Blockchain, O::Target: Oracle, T::Target: Time, F::Target: FeeEstimator, + SP::Target: LnDlcSignerProvider, { ln_channel_manager: M, dlc_channel_manager: D, actions: Mutex>, - phantom: std::marker::PhantomData, + ln_chain_monitor: C, + phantom: std::marker::PhantomData, + signer_provider: SP, + ln_channel_signers: Mutex>, } impl< W: Deref, M: Deref, + C: Deref, S: Deref, B: Deref, O: Deref, T: Deref, F: Deref, D: Deref>, - SP: ChannelSigner, - > SubChannelManager + CS: ChannelSigner, + SP: Deref, + LCS: LnDlcChannelSigner, + > SubChannelManager where W::Target: Wallet, - M::Target: LNChannelManager, + M::Target: LNChannelManager, + C::Target: LNChainMonitor, S::Target: Storage, B::Target: Blockchain, O::Target: Oracle, T::Target: Time, F::Target: FeeEstimator, + SP::Target: LnDlcSignerProvider, { /// Creates a new [`SubChannelManager`]. - pub fn new(ln_channel_manager: M, dlc_channel_manager: D) -> Result { + pub fn new( + ln_channel_manager: M, + dlc_channel_manager: D, + ln_chain_monitor: C, + signer_provider: SP, + ) -> Result { let actions = dlc_channel_manager.get_store().get_sub_channel_actions()?; Ok(Self { ln_channel_manager, dlc_channel_manager, actions: Mutex::new(actions), phantom: PhantomData, + ln_chain_monitor, + signer_provider, + ln_channel_signers: Mutex::new(HashMap::new()), }) } @@ -386,6 +407,7 @@ where counter_fund_pk: channel_details.counter_funding_pubkey.ok_or_else(|| { Error::InvalidState("Counter funding PK is missing".to_string()) })?, + channel_keys_id: channel_details.channel_keys_id, } } }; @@ -586,8 +608,8 @@ where ); let commitment_transactions = self - .ln_channel_manager - .get_latest_holder_commitment_txn(channel_lock)?; + .ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?; let commitment_signed = self .ln_channel_manager @@ -697,7 +719,7 @@ where /// Start force closing the sub channel with given [`ChannelId`]. pub fn force_close_sub_channel(&self, channel_id: &ChannelId) -> Result<(), Error> { - let mut sub_channel = self + let sub_channel = self .dlc_channel_manager .get_store() .get_sub_channel(*channel_id)? @@ -705,23 +727,39 @@ where "Unknown sub channel {:?}", channel_id )))?; + let mut chain_monitor = self.dlc_channel_manager.get_chain_monitor().lock().unwrap(); + self.force_close_sub_channel_internal(sub_channel, &mut chain_monitor) + } + fn force_close_sub_channel_internal( + &self, + mut sub_channel: SubChannel, + chain_monitor: &mut ChainMonitor, + ) -> Result<(), Error> { match sub_channel.state { // Force close by simply asking LDK to force close as the channel funding outpoint has // not yet been updated (also updated sub-channel/channel/contract state). - SubChannelState::Offered(_) => self.force_close_with_ldk(sub_channel)?, + SubChannelState::Offered(_) => self.force_close_with_ldk(sub_channel, chain_monitor)?, // Force close by using the saved LN commitment transactions from before the spliting of the // channel. SubChannelState::Accepted(ref a) => { let commitment_transactions = a.commitment_transactions.clone(); - self.force_close_with_saved_commitment(sub_channel, &commitment_transactions)?; + self.force_close_with_saved_commitment( + sub_channel, + &commitment_transactions, + chain_monitor, + )?; } // Force close by using the saved LN commitment transactions from before the spliting of the // channel. SubChannelState::Confirmed(ref c) => { let commitment_transactions = c.commitment_transactions.clone(); - self.force_close_with_saved_commitment(sub_channel, &commitment_transactions)?; + self.force_close_with_saved_commitment( + sub_channel, + &commitment_transactions, + chain_monitor, + )?; } // Force close by broadcasting the split transaction and marking the sub-channel as // closing, which will trigger the periodic check to watch when the split transaction @@ -741,7 +779,7 @@ where // reacts properly during the closing process. SubChannelState::CloseAccepted(c) => { self.ln_channel_manager.with_channel_lock_no_check( - channel_id, + &sub_channel.channel_id, &sub_channel.counter_party, |channel_lock| { self.ln_channel_manager.set_funding_outpoint( @@ -762,7 +800,7 @@ where // Once we are in `CloseConfirmed`, we can simply use LDK to close the Lightning // channel. SubChannelState::CloseConfirmed(_) => { - self.force_close_with_ldk(sub_channel)?; + self.force_close_with_ldk(sub_channel, chain_monitor)?; } // In these states, either the channel is already closed, or it should be force closed // through LDK directly. @@ -782,7 +820,11 @@ where Ok(()) } - fn force_close_with_ldk(&self, mut sub_channel: SubChannel) -> Result<(), Error> { + fn force_close_with_ldk( + &self, + mut sub_channel: SubChannel, + chain_monitor: &mut ChainMonitor, + ) -> Result<(), Error> { let dlc_channel_id = sub_channel .get_dlc_channel_id(0) .expect("to have a channel id in offered state"); @@ -799,18 +841,18 @@ where .upsert_channel(closed_channel, Some(closed_contract))?; //TODO(tibo): this is actually unsafe, we shouldn't clean up the chain monitor before //having the commitment transaction confirmed on chain. - 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) + .persist_chain_monitor(chain_monitor) } fn force_close_with_saved_commitment( &self, mut sub_channel: SubChannel, commitment_transactions: &Vec, + chain_monitor: &mut ChainMonitor, ) -> Result<(), Error> { // The Lightning commitment transaction is always first in the vector so this is safe. for tx in commitment_transactions { @@ -831,12 +873,12 @@ where 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) + .persist_chain_monitor(chain_monitor)?; + Ok(()) } fn force_close_signed_channel( @@ -847,78 +889,63 @@ where 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; - 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 publish_base_secret = self + .dlc_channel_manager + .get_wallet() + .get_secret_key_for_pubkey(&sub_channel.own_base_points.publish_basepoint)?; - let publish_sk = derive_private_key( - self.dlc_channel_manager.get_secp(), - &state.own_per_split_point, - &publish_base_secret, - ); + let publish_sk = derive_private_key( + self.dlc_channel_manager.get_secp(), + &state.own_per_split_point, + &publish_base_secret, + ); - let counter_split_signature = state - .counter_split_adaptor_signature - .decrypt(&publish_sk) - .map_err(|e| APIError::ExternalError { err: e.to_string() })?; + let counter_split_signature = state + .counter_split_adaptor_signature + .decrypt(&publish_sk) + .map_err(|e| APIError::ExternalError { err: e.to_string() })?; - let mut split_tx = state.split_tx.transaction.clone(); + let mut split_tx = state.split_tx.transaction.clone(); - let mut own_sig = None; + let mut signers = self.ln_channel_signers.lock().unwrap(); - 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(), - &mut split_tx, - &counter_split_signature, - &sub_channel.counter_fund_pk, - fund_sk, - &sub_channel.original_funding_redeemscript, - sub_channel.fund_value_satoshis, - 0, - ) - .unwrap(); - }); - self.dlc_channel_manager - .get_blockchain() - .send_transaction(&split_tx)?; + let signer = signers.entry(sub_channel.channel_id).or_insert( + self.signer_provider.derive_ln_dlc_channel_signer( + sub_channel.fund_value_satoshis, + sub_channel.channel_keys_id, + ), + ); + let own_split_sig = signer.get_holder_split_tx_signature( + self.dlc_channel_manager.get_secp(), + &split_tx, + &sub_channel.original_funding_redeemscript, + sub_channel.fund_value_satoshis, + )?; + dlc::util::finalize_multi_sign_input_transaction( + &mut split_tx, + vec![ + (sub_channel.own_fund_pk, own_split_sig), + (sub_channel.counter_fund_pk, counter_split_signature), + ], + &sub_channel.original_funding_redeemscript, + 0, + ); - let closing_sub_channel = ClosingSubChannel { - signed_sub_channel: state, - is_initiator: true, - commitment_transactions, - }; + self.dlc_channel_manager + .get_blockchain() + .send_transaction(&split_tx)?; - sub_channel.state = SubChannelState::Closing(closing_sub_channel); + let closing_sub_channel = ClosingSubChannel { + signed_sub_channel: state, + is_initiator: true, + commitment_transactions, + }; - self.dlc_channel_manager - .get_store() - .upsert_sub_channel(&sub_channel)?; + sub_channel.state = SubChannelState::Closing(closing_sub_channel); - Ok(()) - }, - )?; + self.dlc_channel_manager + .get_store() + .upsert_sub_channel(&sub_channel)?; } else { unreachable!("Should not call this method if not in Signed or Finalized state"); } @@ -1067,7 +1094,11 @@ where /// Notify that LDK has decided to close the channel with given id. This MUST be called within /// the event handler provided to LDK in reaction to the `ChannelClosed` event. - pub fn notify_ln_channel_closed(&self, channel_id: ChannelId) -> Result<(), Error> { + pub fn notify_ln_channel_closed( + &self, + channel_id: ChannelId, + closure_reason: &ClosureReason, + ) -> Result<(), Error> { let mut sub_channel = self .dlc_channel_manager .get_store() @@ -1079,50 +1110,139 @@ where 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::CloseAccepted(_) - | SubChannelState::CloseConfirmed(_) - | 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::OffChainClosed => { - chain_monitor.cleanup_channel(sub_channel.channel_id); - sub_channel.state = SubChannelState::CounterOnChainClosed; - (None, None) - } - SubChannelState::Signed(_) - | SubChannelState::Closing(_) - | SubChannelState::CloseOffered(_) => { - error!("Got close notification from LDK in a state that we don't handle yet"); - return Ok(()); - } - SubChannelState::OnChainClosed | SubChannelState::CounterOnChainClosed => { - info!( - "Channel close notification received for channel: {:?}", - sub_channel.channel_id - ); - return Ok(()); - } - SubChannelState::ClosedPunished(_) => { - warn!("Got close notification while in ClosedPunished."); - return Ok(()); - } - SubChannelState::Rejected => { - info!("Counterparty closed channel in rejected state, marking as counter closed"); - sub_channel.state = SubChannelState::CounterOnChainClosed; - (None, None) + let (updated_channel, updated_contract) = match closure_reason { + ClosureReason::CounterpartyForceClosed { .. } + | ClosureReason::CommitmentTxConfirmed => match sub_channel.state { + SubChannelState::Offered(_) + | SubChannelState::Accepted(_) + | SubChannelState::Confirmed(_) + | SubChannelState::CloseAccepted(_) + | SubChannelState::CloseConfirmed(_) + | 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::OffChainClosed => { + chain_monitor.cleanup_channel(sub_channel.channel_id); + sub_channel.state = SubChannelState::CounterOnChainClosed; + (None, None) + } + SubChannelState::Signed(_) + | SubChannelState::Closing(_) + | SubChannelState::CloseOffered(_) => { + error!("Got notification of LN channel closure by counter party in a state where we do not expect it."); + return Ok(()); + } + SubChannelState::OnChainClosed | SubChannelState::CounterOnChainClosed => { + info!( + "Channel close notification received for channel: {:?}", + sub_channel.channel_id + ); + return Ok(()); + } + SubChannelState::ClosedPunished(_) => { + warn!("Got close notification while in ClosedPunished."); + return Ok(()); + } + SubChannelState::Rejected => { + info!( + "Counterparty closed channel in rejected state, marking as counter closed" + ); + sub_channel.state = SubChannelState::CounterOnChainClosed; + (None, None) + } + }, + ClosureReason::HolderForceClosed | ClosureReason::ProcessingError { .. } => { + match sub_channel.state { + SubChannelState::Offered(_) | SubChannelState::CloseConfirmed(_) => { + 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, false)?; + sub_channel.state = SubChannelState::OnChainClosed; + chain_monitor.cleanup_channel(sub_channel.channel_id); + chain_monitor.cleanup_channel(dlc_channel_id); + (Some(closed_channel), Some(closed_contract)) + } + SubChannelState::OffChainClosed => { + chain_monitor.cleanup_channel(sub_channel.channel_id); + sub_channel.state = SubChannelState::OnChainClosed; + (None, None) + } + SubChannelState::Accepted(_) + | SubChannelState::Confirmed(_) + | SubChannelState::Finalized(_) => { + return self + .force_close_sub_channel_internal(sub_channel, &mut chain_monitor); + } + SubChannelState::CloseAccepted(ref c) => { + let split_input = &c.signed_subchannel.split_tx.transaction.input[0]; + let funding_txo = lightning::chain::transaction::OutPoint { + txid: split_input.previous_output.txid, + index: split_input.previous_output.vout as u16, + }; + self.ln_chain_monitor.update_channel_funding_txo( + &funding_txo, + &c.ln_rollback.funding_outpoint, + c.ln_rollback.channel_value_satoshis, + )?; + let commitment_transactions = Some( + self.ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?, + ); + let mut signed_subchannel = sub_channel.clone(); + signed_subchannel.state = + SubChannelState::Signed(c.signed_subchannel.clone()); + self.force_close_signed_channel( + signed_subchannel, + commitment_transactions, + )?; + return Ok(()); + } + SubChannelState::CloseOffered(ref c) => { + let split_input = &c.signed_subchannel.split_tx.transaction.input[0]; + let funding_txo = lightning::chain::transaction::OutPoint { + txid: split_input.previous_output.txid, + index: split_input.previous_output.vout as u16, + }; + let commitment_transactions = Some( + self.ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?, + ); + let mut signed_subchannel = sub_channel.clone(); + signed_subchannel.state = + SubChannelState::Signed(c.signed_subchannel.clone()); + self.force_close_signed_channel( + signed_subchannel, + commitment_transactions, + )?; + return Ok(()); + } + SubChannelState::Signed(ref s) => { + let split_input = &s.split_tx.transaction.input[0]; + let funding_txo = lightning::chain::transaction::OutPoint { + txid: split_input.previous_output.txid, + index: split_input.previous_output.vout as u16, + }; + let commitment_transactions = Some( + self.ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?, + ); + self.force_close_signed_channel(sub_channel, commitment_transactions)?; + return Ok(()); + } + _ => return Ok(()), + } } + _ => return Ok(()), }; if let Some(channel) = updated_channel { @@ -1133,11 +1253,10 @@ where self.dlc_channel_manager .get_store() - .upsert_sub_channel(&sub_channel)?; - + .persist_chain_monitor(&chain_monitor)?; self.dlc_channel_manager .get_store() - .persist_chain_monitor(&chain_monitor)?; + .upsert_sub_channel(&sub_channel)?; Ok(()) } @@ -1261,9 +1380,17 @@ where Signed, None:: )?; + let funding_txo = lightning::chain::transaction::OutPoint { + txid: state.signed_subchannel.split_tx.transaction.input[0] + .previous_output + .txid, + index: state.signed_subchannel.split_tx.transaction.input[0] + .previous_output + .vout as u16, + }; let commitment_transactions = self - .ln_channel_manager - .get_latest_holder_commitment_txn(channel_lock)?; + .ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?; let total_collateral = dlc_channel.own_params.collateral + dlc_channel.counter_params.collateral; @@ -1477,6 +1604,7 @@ where counter_fund_pk: channel_details.counter_funding_pubkey.ok_or_else(|| { Error::InvalidState("Counter funding PK is missing".to_string()) })?, + channel_keys_id: channel_details.channel_keys_id, }, }; @@ -1655,27 +1783,26 @@ where 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| { - split_tx_adaptor_signature = Some( - get_tx_adaptor_signature( - self.dlc_channel_manager.get_secp(), - &split_tx.transaction, - channel_details.channel_value_satoshis, - &funding_redeemscript, - sk, - &accept_revoke_params.publish_pk.inner, - ) - .unwrap(), - ); - }); + let mut signers = self.ln_channel_signers.lock().unwrap(); - let split_tx_adaptor_signature = split_tx_adaptor_signature.unwrap(); + let signer = signers.entry(*channel_id).or_insert( + self.signer_provider.derive_ln_dlc_channel_signer( + channel_details.channel_value_satoshis, + channel_details.channel_keys_id, + ), + ); + + let split_tx_adaptor_signature = signer.get_holder_split_tx_adaptor_signature( + self.dlc_channel_manager.get_secp(), + &split_tx.transaction, + channel_details.channel_value_satoshis, + &funding_redeemscript, + &accept_revoke_params.publish_pk.inner, + )?; let commitment_transactions = self - .ln_channel_manager - .get_latest_holder_commitment_txn(channel_lock)?; + .ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?; let commitment_signed = self .ln_channel_manager @@ -1941,23 +2068,22 @@ where self.dlc_channel_manager.get_chain_monitor(), )?; - let mut split_tx_adaptor_signature = None; - self.ln_channel_manager - .sign_with_fund_key_cb(channel_lock, &mut |sk| { - split_tx_adaptor_signature = Some( - get_tx_adaptor_signature( - self.dlc_channel_manager.get_secp(), - &state.split_tx.transaction, - accepted_sub_channel.fund_value_satoshis, - funding_redeemscript, - sk, - &offer_revoke_params.publish_pk.inner, - ) - .unwrap(), - ); - }); + let mut signers = self.ln_channel_signers.lock().unwrap(); - let split_adaptor_signature = split_tx_adaptor_signature.unwrap(); + let signer = signers.entry(sub_channel_confirm.channel_id).or_insert( + self.signer_provider.derive_ln_dlc_channel_signer( + channel_details.channel_value_satoshis, + channel_details.channel_keys_id, + ), + ); + + let split_adaptor_signature = signer.get_holder_split_tx_adaptor_signature( + self.dlc_channel_manager.get_secp(), + &state.split_tx.transaction, + accepted_sub_channel.fund_value_satoshis, + funding_redeemscript, + &offer_revoke_params.publish_pk.inner, + )?; let signed_sub_channel = SignedSubChannel { own_per_split_point: state.accept_per_split_point, @@ -2309,9 +2435,17 @@ where next_per_commitment_point: raa.next_per_commitment_point, }; + let funding_txo = lightning::chain::transaction::OutPoint { + txid: state.signed_subchannel.split_tx.transaction.input[0] + .previous_output + .txid, + index: state.signed_subchannel.split_tx.transaction.input[0] + .previous_output + .vout as u16, + }; let commitment_transactions = self - .ln_channel_manager - .get_latest_holder_commitment_txn(channel_lock)?; + .ln_chain_monitor + .get_latest_holder_commitment_txn(&funding_txo)?; let updated_channel = CloseConfirmedSubChannel { signed_subchannel: state.signed_subchannel, @@ -3532,22 +3666,27 @@ where impl< W: Deref, M: Deref, + C: Deref, S: Deref, B: Deref, O: Deref, T: Deref, F: Deref, D: Deref>, - SP: ChannelSigner, - > ChannelMessageHandler for SubChannelManager + CS: ChannelSigner, + SP: Deref, + LCS: LnDlcChannelSigner, + > ChannelMessageHandler for SubChannelManager where W::Target: Wallet, - M::Target: LNChannelManager, + M::Target: LNChannelManager, + C::Target: LNChainMonitor, S::Target: Storage, B::Target: Blockchain, O::Target: Oracle, T::Target: Time, F::Target: FeeEstimator, + SP::Target: LnDlcSignerProvider, { fn handle_open_channel( &self, @@ -3731,22 +3870,27 @@ where impl< W: Deref, M: Deref, + C: Deref, S: Deref, B: Deref, O: Deref, T: Deref, F: Deref, D: Deref>, - SP: ChannelSigner, - > MessageSendEventsProvider for SubChannelManager + CS: ChannelSigner, + SP: Deref, + LCS: LnDlcChannelSigner, + > MessageSendEventsProvider for SubChannelManager where W::Target: Wallet, - M::Target: LNChannelManager, + M::Target: LNChannelManager, + C::Target: LNChainMonitor, S::Target: Storage, B::Target: Blockchain, O::Target: Oracle, T::Target: Time, F::Target: FeeEstimator, + SP::Target: LnDlcSignerProvider, { fn get_and_clear_pending_msg_events(&self) -> Vec { let mut msg_events = self.ln_channel_manager.get_and_clear_pending_msg_events(); diff --git a/dlc-manager/src/subchannel/mod.rs b/dlc-manager/src/subchannel/mod.rs index 906f6304..70c2da8d 100644 --- a/dlc-manager/src/subchannel/mod.rs +++ b/dlc-manager/src/subchannel/mod.rs @@ -8,7 +8,9 @@ use dlc::channel::sub_channel::SplitTx; use lightning::{ chain::{ chaininterface::{BroadcasterInterface, FeeEstimator}, - keysinterface::{EntropySource, NodeSigner, SignerProvider}, + chainmonitor::ChainMonitor, + keysinterface::{EntropySource, NodeSigner, SignerProvider, WriteableEcdsaChannelSigner}, + Watch, }, ln::{ chan_utils::CounterpartyCommitmentSecrets, @@ -56,6 +58,8 @@ pub struct SubChannel { pub counter_fund_pk: PublicKey, /// The revocation secrets from the remote party for already revoked split transactions. pub counter_party_secrets: CounterpartyCommitmentSecrets, + /// The id used to derive the keys for the Lightning channel. + pub channel_keys_id: [u8; 32], } impl std::fmt::Debug for SubChannel { @@ -422,11 +426,6 @@ where revoke_and_ack: &RevokeAndACK, ) -> Result<(), APIError>; - /// Gives the ability to access the funding secret key within the provided callback. - fn sign_with_fund_key_cb(&self, channel_lock: &mut ChannelLock, cb: &mut F) - where - F: FnMut(&SecretKey); - /// Force close the channel with given `channel_id` and `counter_party_node_id`. fn force_close_channel( &self, @@ -443,13 +442,25 @@ where channel_value_satoshis: u64, value_to_self_msat: u64, ); +} +/// +pub trait LNChainMonitor { /// Gets the latest commitment transactions and HTLC transactions that can be used to close the /// channel. fn get_latest_holder_commitment_txn( &self, - channel_lock: &ChannelLock, + funding_txo: &lightning::chain::transaction::OutPoint, ) -> Result, Error>; + + /// Updates the funding transaction output of the channel monitor associated with the channel + /// specified by `old_funding_txo`. + fn update_channel_funding_txo( + &self, + old_funding_txo: &lightning::chain::transaction::OutPoint, + new_funding_txo: &lightning::chain::transaction::OutPoint, + channel_value_satoshis: u64, + ) -> Result<(), Error>; } impl @@ -508,16 +519,6 @@ where self.revoke_and_ack_commitment(channel_lock, revoke_and_ack) } - fn sign_with_fund_key_cb( - &self, - channel_lock: &mut ChannelLock<::Signer>, - cb: &mut SF, - ) where - SF: FnMut(&SecretKey), - { - self.sign_with_fund_key_callback(channel_lock, cb); - } - fn force_close_channel( &self, channel_id: &[u8; 32], @@ -542,19 +543,6 @@ where ); } - fn get_latest_holder_commitment_txn( - &self, - channel_lock: &ChannelLock<::Signer>, - ) -> Result, Error> { - self.get_latest_holder_commitment_txn(channel_lock) - .map_err(|e| { - Error::InvalidState(format!( - "Could not retrieve latest commitment transactions {:?}", - e - )) - }) - } - fn with_useable_channel_lock( &self, channel_id: &ChannelId, @@ -584,6 +572,52 @@ where } } +impl< + ChannelSigner: WriteableEcdsaChannelSigner, + C: Deref, + T: Deref, + F: Deref, + L: Deref, + P: Deref, + > LNChainMonitor for ChainMonitor +where + C::Target: lightning::chain::Filter, + T::Target: BroadcasterInterface, + F::Target: FeeEstimator, + L::Target: Logger, + P::Target: lightning::chain::chainmonitor::Persist, +{ + fn get_latest_holder_commitment_txn( + &self, + funding_txo: &lightning::chain::transaction::OutPoint, + ) -> Result, Error> { + self.get_latest_holder_commitment_txn(funding_txo) + .map_err(|e| { + Error::InvalidParameters(format!("Could not get channel monitor: {:?}", e)) + }) + } + + fn update_channel_funding_txo( + &self, + old_funding_txo: &lightning::chain::transaction::OutPoint, + new_funding_txo: &lightning::chain::transaction::OutPoint, + channel_value_satoshis: u64, + ) -> Result<(), Error> { + match Watch::update_channel_funding_txo( + self, + *old_funding_txo, + *new_funding_txo, + channel_value_satoshis, + ) { + lightning::chain::ChannelMonitorUpdateStatus::Completed => Ok(()), + s => Err(Error::InvalidState(format!( + "Unexpected channel monitor status {:?}", + s + ))), + } + } +} + /// Generate a temporary channel id for a DLC channel based on the LN channel id, the update index of the /// split transaction and the index of the DLC channel within the sub channel. pub fn generate_temporary_channel_id( @@ -597,3 +631,42 @@ pub fn generate_temporary_channel_id( data.extend_from_slice(&channel_index.to_be_bytes()); bitcoin::hashes::sha256::Hash::hash(&data).into_inner() } + +/// Trait to be implemented by a signing component providing the ability to sign LN/DLC split +/// transactions. It is required that the signer's keys are identical to the ones used for the +/// original Lightning channel. +pub trait LnDlcChannelSigner { + /// Get the signature for the split transaction using the LN channel holder funding secret key. + fn get_holder_split_tx_signature( + &self, + secp: &secp256k1_zkp::Secp256k1, + split_tx: &Transaction, + original_funding_redeemscript: &Script, + original_channel_value_satoshis: u64, + ) -> Result; + + /// Get an adaptor signature for the split transaction using the LN channel holder funding + /// secret key as the signing key, and the remote party publish public key as adaptor key. + fn get_holder_split_tx_adaptor_signature( + &self, + secp: &secp256k1_zkp::Secp256k1, + split_tx: &Transaction, + original_channel_value_satoshis: u64, + original_funding_redeemscript: &Script, + other_publish_key: &PublicKey, + ) -> Result; +} + +/// Generates `Signer` that are able to sign split transaction for LN/DLC channels. +pub trait LnDlcSignerProvider { + /// Derives the private key material backing a `Signer`. + /// + /// To derive a new `Signer`, the same `channel_keys_id` and `channel_value_satoshis` parameter + /// that were provided to generate the LDK `ChannelSigner` shoud be passed, and the + /// implementation should derive the same key values. + fn derive_ln_dlc_channel_signer( + &self, + channel_value_satoshis: u64, + channel_keys_id: [u8; 32], + ) -> Signer; +} diff --git a/dlc-manager/src/subchannel/ser.rs b/dlc-manager/src/subchannel/ser.rs index f2f687a9..0dece5c0 100644 --- a/dlc-manager/src/subchannel/ser.rs +++ b/dlc-manager/src/subchannel/ser.rs @@ -24,7 +24,8 @@ impl_dlc_writeable!(SubChannel, { (is_offer, writeable), (own_fund_pk, writeable), (counter_fund_pk, writeable), - (counter_party_secrets, writeable) + (counter_party_secrets, writeable), + (channel_keys_id, writeable) }); impl_dlc_writeable_enum!(SubChannelState, diff --git a/dlc-manager/tests/custom_signer.rs b/dlc-manager/tests/custom_signer.rs index e5eab9e4..cc70094e 100644 --- a/dlc-manager/tests/custom_signer.rs +++ b/dlc-manager/tests/custom_signer.rs @@ -1,6 +1,10 @@ use std::sync::{Arc, Mutex}; use bitcoin::{Script, Transaction, TxOut}; +use dlc_manager::{ + error::Error, + subchannel::{LnDlcChannelSigner, LnDlcSignerProvider}, +}; use lightning::{ chain::keysinterface::{ ChannelSigner, EcdsaChannelSigner, EntropySource, ExtraSign, InMemorySigner, KeysManager, @@ -252,6 +256,45 @@ impl Readable for CustomSigner { } } +impl LnDlcChannelSigner for CustomSigner { + fn get_holder_split_tx_signature( + &self, + secp: &Secp256k1, + split_tx: &Transaction, + original_funding_redeemscript: &Script, + original_channel_value_satoshis: u64, + ) -> Result { + dlc::util::get_raw_sig_for_tx_input( + secp, + &split_tx, + 0, + original_funding_redeemscript, + original_channel_value_satoshis, + &self.in_memory_signer.lock().unwrap().funding_key, + ) + .map_err(|e| e.into()) + } + + fn get_holder_split_tx_adaptor_signature( + &self, + secp: &Secp256k1, + split_tx: &Transaction, + original_channel_value_satoshis: u64, + original_funding_redeemscript: &Script, + other_publish_key: &secp256k1_zkp::PublicKey, + ) -> Result { + dlc::channel::get_tx_adaptor_signature( + secp, + &split_tx, + original_channel_value_satoshis, + original_funding_redeemscript, + &self.in_memory_signer.lock().unwrap().funding_key, + other_publish_key, + ) + .map_err(|e| e.into()) + } +} + impl WriteableEcdsaChannelSigner for CustomSigner {} pub struct CustomKeysManager { @@ -325,6 +368,16 @@ impl SignerProvider for CustomKeysManager { } } +impl LnDlcSignerProvider for CustomKeysManager { + fn derive_ln_dlc_channel_signer( + &self, + channel_value_satoshis: u64, + channel_keys_id: [u8; 32], + ) -> CustomSigner { + self.derive_channel_signer(channel_value_satoshis, channel_keys_id) + } +} + impl EntropySource for CustomKeysManager { fn get_secure_random_bytes(&self) -> [u8; 32] { self.keys_manager.get_secure_random_bytes() diff --git a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs index 152b820d..a4c9e204 100644 --- a/dlc-manager/tests/ln_dlc_channel_execution_tests.rs +++ b/dlc-manager/tests/ln_dlc_channel_execution_tests.rs @@ -35,7 +35,7 @@ use lightning::{ chain::{ chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}, keysinterface::{EntropySource, KeysManager}, - BestBlock, Confirm, Watch, + BestBlock, Confirm, }, ln::{ channelmanager::{ChainParameters, PaymentId}, @@ -72,7 +72,7 @@ type ChainMonitor = lightning::chain::chainmonitor::ChainMonitor< CustomSigner, Arc>>, Arc>>, - Arc, + Arc>>, Arc, Arc, >; @@ -83,7 +83,7 @@ pub(crate) type ChannelManager = lightning::ln::channelmanager::ChannelManager< Arc, Arc, Arc, - Arc, + Arc>>, Arc< DefaultRouter< Arc>>, @@ -116,6 +116,7 @@ type DlcChannelManager = Manager< type DlcSubChannelManager = SubChannelManager< Arc, Arc>>, Arc, + Arc, Arc, Arc, Arc, @@ -123,6 +124,8 @@ type DlcSubChannelManager = SubChannelManager< Arc, Arc, CustomSigner, + Arc, + CustomSigner, >; struct LnDlcParty { @@ -161,8 +164,8 @@ impl LnDlcParty { } fn process_events(&self) { - self.peer_manager.process_events(); self.channel_manager.process_pending_events(self); + self.peer_manager.process_events(); self.channel_manager.timer_tick_occurred(); self.chain_monitor.process_pending_events(self); } @@ -367,10 +370,12 @@ impl EventHandler for LnDlcParty { .unwrap(); self.blockchain.broadcast_transaction(&spending_tx); } - Event::ChannelClosed { channel_id, .. } => { + Event::ChannelClosed { + channel_id, reason, .. + } => { if let Err(error) = self .sub_channel_manager - .notify_ln_channel_closed(channel_id) + .notify_ln_channel_closed(channel_id, &reason) { error!( "Error notifying sub channel manager of LN channel closing: {}", @@ -415,7 +420,7 @@ fn create_ln_node( Some(tx_sync.clone()), mock_blockchain.clone(), logger.clone(), - blockchain_provider.clone(), + mock_blockchain.clone(), persister.clone(), )); @@ -447,7 +452,7 @@ fn create_ln_node( }; Arc::new(ChannelManager::new( - blockchain_provider.clone(), + mock_blockchain.clone(), chain_monitor.clone(), mock_blockchain.clone(), router, @@ -498,8 +503,15 @@ fn create_ln_node( .unwrap(), ); - let sub_channel_manager = - Arc::new(SubChannelManager::new(channel_manager.clone(), dlc_manager.clone()).unwrap()); + let sub_channel_manager = Arc::new( + SubChannelManager::new( + channel_manager.clone(), + dlc_manager.clone(), + chain_monitor.clone(), + consistent_keys_manager.clone(), + ) + .unwrap(), + ); let lightning_msg_handler = MessageHandler { chan_handler: sub_channel_manager.clone(), @@ -1318,7 +1330,7 @@ fn ln_dlc_close_offered_force_close() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::OfferSent); + go_to_off_chain_close_state(&test_params, TargetState::OfferSent, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1351,7 +1363,7 @@ fn ln_dlc_close_offered_force_close2() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::OfferSent); + go_to_off_chain_close_state(&test_params, TargetState::OfferSent, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1384,7 +1396,7 @@ fn ln_dlc_close_offered_force_close3() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::OfferReceived); + go_to_off_chain_close_state(&test_params, TargetState::OfferReceived, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1416,7 +1428,7 @@ fn ln_dlc_close_offered_force_close4() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::OfferReceived); + go_to_off_chain_close_state(&test_params, TargetState::OfferReceived, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1449,7 +1461,7 @@ fn ln_dlc_close_accepted_force_close() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::Accepted); + go_to_off_chain_close_state(&test_params, TargetState::Accepted, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1482,7 +1494,7 @@ fn ln_dlc_close_accepted_force_close2() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::Accepted); + go_to_off_chain_close_state(&test_params, TargetState::Accepted, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1515,7 +1527,7 @@ fn ln_dlc_close_confirmed_force_close() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::Confirmed); + go_to_off_chain_close_state(&test_params, TargetState::Confirmed, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1548,7 +1560,7 @@ fn ln_dlc_close_confirmed_force_close2() { open_sub_channel(&test_params); - go_to_off_chain_close_state(&test_params, TargetState::Confirmed); + go_to_off_chain_close_state(&test_params, TargetState::Confirmed, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1584,7 +1596,7 @@ fn ln_dlc_close_finalized_force_close() { mocks::mock_time::set_time(EVENT_MATURITY as u64); - go_to_off_chain_close_state(&test_params, TargetState::Finalized); + go_to_off_chain_close_state(&test_params, TargetState::Finalized, false); assert_sub_channel_state!( test_params.alice_node.sub_channel_manager, @@ -1604,6 +1616,190 @@ fn ln_dlc_close_finalized_force_close() { force_close_mid_protocol(&mut test_params, false, &commit_tx); } +#[test] +#[ignore] +fn ln_dlc_ldk_auto_close_established_test() { + let mut test_params = test_init(); + + open_sub_channel(&test_params); + + let commit_tx = get_commit_tx_from_node(&test_params.bob_node, &test_params.funding_txo); + + mocks::mock_time::set_time(EVENT_MATURITY as u64); + + ldk_auto_close(&mut test_params, &commit_tx[0]); +} + +#[test] +#[ignore] +fn ln_dlc_ldk_auto_close_offered_test() { + let mut test_params = test_init(); + + go_to_established_target_state(&test_params, TargetState::OfferReceived); + + assert_sub_channel_state!( + test_params.alice_node.sub_channel_manager, + &test_params.channel_id, + Offered + ); + + assert_sub_channel_state!( + test_params.bob_node.sub_channel_manager, + &test_params.channel_id, + Offered + ); + + let commit_tx = get_commit_tx_from_node(&test_params.bob_node, &test_params.funding_txo); + ldk_auto_close(&mut test_params, &commit_tx[0]); +} + +#[test] +#[ignore] +fn ln_dlc_ldk_auto_close_accepted_test() { + let mut test_params = test_init(); + + go_to_established_target_state(&test_params, TargetState::Accepted); + + assert_sub_channel_state!( + test_params.alice_node.sub_channel_manager, + &test_params.channel_id, + Offered + ); + + assert_sub_channel_state!( + test_params.bob_node.sub_channel_manager, + &test_params.channel_id, + Accepted + ); + + let sub_channel = test_params + .bob_node + .dlc_manager + .get_store() + .get_sub_channel(test_params.channel_id) + .unwrap() + .unwrap(); + let commit_tx = if let SubChannelState::Accepted(a) = &sub_channel.state { + a.commitment_transactions[0].clone() + } else { + unreachable!(); + }; + + ldk_auto_close(&mut test_params, &commit_tx); +} + +//TODO(tibo): Find a way to trigger an ldk auto force close in this state. Changing the fee does +//not work because Alice is awaiting a revoke from Bob and doesn't send the fee update message, +//sending a bad commitment signature also doesn't really fit with the state. +// fn ln_dlc_ldk_auto_close_confirmed_test() { +// } +// #[test] +// #[ignore] +// fn ln_dlc_ldk_auto_close_finalized_test() { +// } + +#[test] +#[ignore] +fn ln_dlc_ldk_auto_close_close_offered_test() { + let mut test_params = test_init(); + + make_ln_payment(&test_params.alice_node, &test_params.bob_node, 900000); + + open_sub_channel(&test_params); + + go_to_off_chain_close_state(&test_params, TargetState::OfferReceived, false); + + assert_sub_channel_state!( + test_params.alice_node.sub_channel_manager, + &test_params.channel_id, + CloseOffered + ); + + assert_sub_channel_state!( + test_params.bob_node.sub_channel_manager, + &test_params.channel_id, + CloseOffered + ); + + mocks::mock_time::set_time(EVENT_MATURITY as u64); + + let commit_tx = + get_commit_tx_from_node(&test_params.bob_node, &test_params.funding_txo).remove(0); + + ldk_auto_close(&mut test_params, &commit_tx); +} + +#[test] +#[ignore] +fn ln_dlc_ldk_auto_close_close_accepted_test() { + let mut test_params = test_init(); + + make_ln_payment(&test_params.alice_node, &test_params.bob_node, 900000); + + open_sub_channel(&test_params); + + go_to_off_chain_close_state(&test_params, TargetState::Accepted, false); + + assert_sub_channel_state!( + test_params.alice_node.sub_channel_manager, + &test_params.channel_id, + CloseOffered + ); + + assert_sub_channel_state!( + test_params.bob_node.sub_channel_manager, + &test_params.channel_id, + CloseAccepted + ); + + mocks::mock_time::set_time(EVENT_MATURITY as u64); + + let commit_tx = + get_commit_tx_from_node(&test_params.bob_node, &test_params.funding_txo).remove(0); + + ldk_auto_close(&mut test_params, &commit_tx); +} + +//TODO(tibo): find a way to test this state. +// #[test] +// #[ignore] +// fn ln_dlc_ldk_auto_close_close_confirmed_test() { +// } + +#[test] +#[ignore] +/// Force close triggered by the party who offered to force close the channel, after their +/// counter party processed the close confirm message, but before they processed the close +/// finalize message. +fn ln_dlc_ldk_auto_close_close_finalized_test() { + let mut test_params = test_init(); + + make_ln_payment(&test_params.alice_node, &test_params.bob_node, 900000); + + open_sub_channel(&test_params); + + mocks::mock_time::set_time(EVENT_MATURITY as u64); + + go_to_off_chain_close_state(&test_params, TargetState::Finalized, true); + + assert_sub_channel_state!( + test_params.bob_node.sub_channel_manager, + &test_params.channel_id, + CloseConfirmed + ); + + assert_sub_channel_state!( + test_params.alice_node.sub_channel_manager, + &test_params.channel_id; + OffChainClosed + ); + + let commit_tx = + get_commit_tx_from_node(&test_params.bob_node, &test_params.funding_txo).remove(0); + + ldk_auto_close(&mut test_params, &commit_tx); +} + fn test_init() -> LnDlcTestParams { env_logger::init(); let (_, _, sink_rpc) = init_clients(); @@ -1843,7 +2039,7 @@ fn get_commit_tx_from_node( funding_txo: &lightning::chain::transaction::OutPoint, ) -> Vec { node.chain_monitor - .get_latest_holder_commitment_txn(*funding_txo) + .get_latest_holder_commitment_txn(funding_txo) .expect("to be able to get latest holder commitment transaction") } @@ -3049,9 +3245,17 @@ fn go_to_established_target_state(test_params: &LnDlcTestParams, target_state: T .unwrap(); } -fn go_to_off_chain_close_state(test_params: &LnDlcTestParams, target_state: TargetState) { - let (close_offer, _) = test_params - .alice_node +fn go_to_off_chain_close_state( + test_params: &LnDlcTestParams, + target_state: TargetState, + reverse_closer: bool, +) { + let (closer, closee) = if reverse_closer { + (&test_params.bob_node, &test_params.alice_node) + } else { + (&test_params.alice_node, &test_params.bob_node) + }; + let (close_offer, _) = closer .sub_channel_manager .offer_subchannel_close( &test_params.channel_id, @@ -3063,12 +3267,11 @@ fn go_to_off_chain_close_state(test_params: &LnDlcTestParams, target_state: Targ return; } - test_params - .bob_node + closee .sub_channel_manager .on_sub_channel_message( &SubChannelMessage::CloseOffer(close_offer), - &test_params.alice_node.channel_manager.get_our_node_id(), + &closer.channel_manager.get_our_node_id(), ) .unwrap(); @@ -3076,8 +3279,7 @@ fn go_to_off_chain_close_state(test_params: &LnDlcTestParams, target_state: Targ return; } - let (accept, _) = test_params - .bob_node + let (accept, _) = closee .sub_channel_manager .accept_subchannel_close_offer(&test_params.channel_id) .unwrap(); @@ -3086,12 +3288,11 @@ fn go_to_off_chain_close_state(test_params: &LnDlcTestParams, target_state: Targ return; } - let confirm = test_params - .alice_node + let confirm = closer .sub_channel_manager .on_sub_channel_message( &SubChannelMessage::CloseAccept(accept), - &test_params.bob_node.channel_manager.get_our_node_id(), + &closee.channel_manager.get_our_node_id(), ) .unwrap() .unwrap(); @@ -3100,12 +3301,113 @@ fn go_to_off_chain_close_state(test_params: &LnDlcTestParams, target_state: Targ return; } - test_params - .bob_node + closee .sub_channel_manager - .on_sub_channel_message( - &confirm, - &test_params.alice_node.channel_manager.get_our_node_id(), - ) + .on_sub_channel_message(&confirm, &closer.channel_manager.get_our_node_id()) + .unwrap(); +} + +fn ldk_auto_close(test_params: &mut LnDlcTestParams, commit_tx: &Transaction) { + let sub_channel = test_params + .alice_node + .dlc_manager + .get_store() + .get_sub_channel(test_params.channel_id) + .unwrap() + .unwrap(); + let dlc_channel_id_alice = sub_channel.get_dlc_channel_id(0); + + let sub_channel = test_params + .bob_node + .dlc_manager + .get_store() + .get_sub_channel(sub_channel.channel_id) + .unwrap() .unwrap(); + let dlc_channel_id_bob = sub_channel.get_dlc_channel_id(0); + + let channel_id = sub_channel.channel_id; + + test_params.alice_node.mock_blockchain.set_est_fee(10000); + test_params.alice_node.process_events(); + test_params.bob_node.process_events(); + test_params.alice_node.process_events(); + test_params.bob_node.process_events(); + test_params.alice_node.process_events(); + test_params.bob_node.process_events(); + test_params.alice_node.process_events(); + test_params.bob_node.process_events(); + + test_params.generate_blocks(500); + test_params.bob_node.update_to_chain_tip(); + test_params.bob_node.process_events(); + + assert_sub_channel_state!(test_params.bob_node.sub_channel_manager, &channel_id; OnChainClosed); + + test_params.generate_blocks(3); + + test_params.bob_node.update_to_chain_tip(); + test_params.alice_node.update_to_chain_tip(); + test_params.alice_node.process_events(); + + assert_sub_channel_state!(test_params.alice_node.sub_channel_manager, &channel_id; CounterOnChainClosed); + + test_params.generate_blocks(500); + + test_params.bob_node.update_to_chain_tip(); + test_params.bob_node.process_events(); + test_params.alice_node.update_to_chain_tip(); + test_params.alice_node.process_events(); + + test_params.generate_blocks(2); + + test_params.bob_node.update_to_chain_tip(); + test_params.bob_node.process_events(); + test_params.alice_node.update_to_chain_tip(); + test_params.alice_node.process_events(); + + if let Some(dlc_channel_id_alice) = dlc_channel_id_alice { + assert_channel_state_unlocked!( + test_params.alice_node.dlc_manager, + dlc_channel_id_alice, + CounterClosed + ); + } + if let Some(dlc_channel_id_bob) = dlc_channel_id_bob { + assert_channel_state_unlocked!( + test_params.bob_node.dlc_manager, + dlc_channel_id_bob, + Closed + ); + } + + assert!(test_params + .bob_node + .dlc_manager + .get_chain_monitor() + .lock() + .unwrap() + .is_empty()); + assert!(test_params + .alice_node + .dlc_manager + .get_chain_monitor() + .lock() + .unwrap() + .is_empty()); + + let all_spent = test_params + .electrs + .get_outspends(&commit_tx.txid()) + .unwrap() + .into_iter() + .all(|x| { + if let OutSpendResp::Spent(_) = x { + true + } else { + false + } + }); + + assert!(all_spent); } diff --git a/dlc-sled-storage-provider/test_files/Accepted b/dlc-sled-storage-provider/test_files/Accepted index f78338042d4277193be5a987c921f24e9ba5aab5..e2f44eebb8846d1dfb6deeb744b1ce43b6a591c9 100644 GIT binary patch literal 3430 zcmWGwKE7W2&G7^0?zz6Q@m>Bis7iPCYuRU+d0qFD^EQc?F)%VRF)%aW0$>@|Sg!`S z0#*=Mzwc0GPokZS7m_lDA6#zY1#%XDMG;E4|QhxfC^ z9@6Y-V6~mUS9asdQ-b^a|9UjOKly*Vnefw{o4VdkPGRTQ*z&b){pzpJT^zGI7N^$F zC==(J_2bdubqrz6Cs(+BELgh4qk5glywB55#FlJ+v?EqRY0;kwE1}7Lar3?2^Y(?+ z`@b};za7+QuQEfs;1Ija#OZqu&+D^Pov`7IgiqZm>AZG+sm~90uh@P$F>tcA-N6@| z-%V}VV=7fHulxS^k8fIsCOj#A+skw3{I0N7O2w5L8V1kPl0VnRIaVBUd$@+DZ|{QM|l6AFY4^uGwITslWc`C*NP%1i-+=r`Af0yotL1aDX~dEdi(P<<6_H$ zU%s2!ziK`-V~3Y{aLASyT~8vmv|n^n%=(rvjYA5G#c%FH4*ym248%j+ZA4`5 z7h3J+`^?DV^nC7*l?nT~H~hW9B?OFlj^f0UV*TROWN^%Kv4o@+mp~(#kx5h5=6S=T zBaaR8kDf7?|L~K^eOvyfP`xQ{tXd7~>&iSC#27?$os7blWnS#q^whlS+riEyq%W*DGgin4-_R?hCBwUh)v^W(G!}qyIAi!-V0vT=;k8(#s$w2rvTUc74JLHv=Y#mhi@J zhDZ8krgJ_rn8a4g)?9C}iswRz*NOIj6YpM3W4drRqIq+ZRIZBf6e0UWzCupt>w+$j{L`nC^1NT) zn#Mcz%-Tbb&EFn+9dEXE>N!p3v<24~^{eLe8>^Uv9^bz!@Re%LWli2WkD?~eda-J0 z?VVe)$+tny1v{DnZ==RA_-42bqjPGvpm-q|(iCgsGYuzT=L;{JB3`|%3vj{zB{pIKjh*8E?z zGU$x;KP@H&qw?*=;VYc3c0bSkuzh;<_mv^rW+ki@iCX?TtIfG}=3z!frYCoEou)-i zShPrwYkT)5tAkxC+p@QCUom2Bpa0DCtkxv-SY-r-EjBkXGcrgs!U7E%c8E{}vS4y> z8qPpc#t1L7;Ss^g;DI)yQGa>Jys>lA@Wi(=^Hfk|Q4 zDF%}k$I!_OlLFC**pPq;OTrjZpr``^Sh9tQBO;K2p=RpUkIKCKH}B1`m2iJxePUY; z|AP{ig%^LWvPew|s_+$mn>2gh-3FrrPl8s=m-~0bJ@$=y)r@T_;rniHv=wZA1Tve6 zP-zCX7*s41s?^{LnfYEg!>Sc*1+8CM9~;PPz_I{aEnB3*3YJ!4P{>!=c9U=Wy!4<~ zDm=?iO^qm;wUuMS=SBZ(kE}bm*bO){UNFkbgD*&D{`l_9Z{< zdIG+CKQb#gbG=9v9E_mClhHvLVmyvgem(d81ky}oI^YuzHXdjqwpKyIwWZ(^gnAY_ z2xx*!7918b2;Tw61@$a!@QMW&NH{D!aAP?rCU8}pWCUr$@85VWROh6wg~+J_TO5+( Z1&YGV!0ki=gL{x7!M3U*!S+tjIsj)pK(hb< literal 3430 zcmZ2~UF&u1>EMKL*%t*dAJ=)uANU?}b7rgh$6pKIyv`O(V_;-tVqj*#1;8?_v0e>u z1*{+z8>_MdkYWO{fdE+yEWrF9L_t(Bf<+JnoX-SfaWR@7J|dU#;jW50heD?&i=YI1 z;ZMufE4SI^=S;r;?c2hI+I7O+lWn3T)}Ff--|i$cY5nRwnWoQQCA@N2eLmrimAR*N zuGzJD$@bAv^Jmz6jJlk8%|fVeWAbd?-!B4;BjvySy`l7%F_D4mUPIpY5B84Ro%(k9 zmA_pw&+1Tg%E`-;Or?*zN*Us|sdgM$U$b{x+}Vw9sQp2 zzS>{C&Ui=WtBX@N&H1{>@7RflWmgM@Ch+CllJpQY@n+-xubr20NYn4@t&5gV)-djH zeyO~u{esjRz&Cazy}PF=F)AJ38%J+lv|>3`p_RC`K@xQZs{;`jBZpH#nI z8>v?tB6B`u_St9OLcdS+Sd$y7fBTVSiD>gx!fPaFU)L7wy8MS>%Kx3F=iMgs9(8Ba3H*ESlxj?@ zen*(i*_|1F>VG!)U(fK-aVl3^P~hwM@8C!Ao`dtQ-g%eWeLuD(Ox#P0X~_rE;y(Fk z<6Ecm)ReLs)_lresGhK&d&A!wTtdK@=O|7rDb_DeO$Ns-7fVQLaS1e%8JRjSN4*oS zc$)7auKPFSU(&)yXO`Va@L;On`|G;W{;W+)7{nMv^w0Oo%5wOtKlz!od<(Z_8UHKJ z`|Iasd7pXyW;#fIju+2_r_2ZPTPClMn|Su{+=fubg5rPAAKEk7n=rod*E34Iv#xX9 zO6T~*tLHwcPe0_%ySLzo$ofE*bviC95Xp@pn}H$9w0Qc@4G-6YM7dud0!zXYH7JoX z!W7&Cr)WmTEv%xdQyHS4rnPmLkLNnGZ5_+w0gNManR?j_)iLwuw6m0gKIP@&t z_t0O9@^XbYdC#VA|JQg)I_<(FiDu2dNEL5JMW*tjHGj8#`jA|^9~34Gn`9byTr<8s2`bJAjNA1IC)^B}BwE57zZo9s zmzmD_$Y2s%En9QF!782$Azmli|4qDmF^wr?L)Yup+skDbwl1DIz4_alH^X-W%LWU*5Xj-maJ zNkP!@wKP-E;uUHvB68awZ0KL~qxVthl3iz=_!G-3`Ajky6`4OQj`<(k`60t{*{TfQ z=jksq;*VM#nXFs*zN=zNyzG_9=&{NO3R`SWW@coNW`q^EV2^?bgaBL)CWI^i6Jub6 zm)UTwjBtgp+5n*j$pVBtumyV1X$|_7p-VT$%^2 z6I^EjRkOl$BH4-LPj;9zCx%XN5&+qcq!U=1!ZdKf7~B{-;f5mVM6#0urjr*#CtM1< zPCl4Uehi(QFe&Uh1z^&G7&_q|!LCyXrc)S0CpSzAyG{|9v?zv79+(t%onkO)aSWZj zFewm?hz$vtuq2Ej1&TT#fF)a)I3fZW804o2EZd*wCRkN>%%DK=2>Yogl8@T^-w15b znpwepE&O}HtKAZbm*N-4*VhYg>+9z`us{0~Tb4ijrH7FWGi=!Rfy`zis;&YR%S07> zAcf3)FPveu4z_~UudI&^AmcDaL%RnIKcxK)zr!_TPAMYscx!Old8J={#-mGv>ZsbEIa8NMLXe PQY6@#Q6$)!4O#~PW%MtA diff --git a/dlc-sled-storage-provider/test_files/AcceptedChannel b/dlc-sled-storage-provider/test_files/AcceptedChannel index d3225440829b85101c4b91b53e61bf95a2cc6753..742c297aa99aa821dfc546fab7ef8ea3695cd0c6 100644 GIT binary patch delta 904 zcmV;319$w42aE@QpE5BSEDDJmu&a(}+-++kpJJ_#z9*5HXM*T+zrm4<0;LmptpU(- z^#pAY2*2Xw)fCm`E-{`5)77)e;CtSvX#*V2%+meNUs!VArD|6b<`zogUv1(6uxq8h zF{qPtmjD8~PYDV^RJ?s602_5+SY$Oom*l}hoGUE9r|vm_dWa>y0wgGzc&R?DERac# z$1(gLr*%*@5h^=<16la8ii4^P!iZ>RAp->U=w9Ykg3LpPG18{1X|c0P(ixIy&;WYT zZL8}n@Ju~_wTPj}i3{Eq#+2%*)rb#ivd!o#=>x@I z2z}$mfU2lq$y=LB)+d3n+(}D$#RA*FeiSG)Rit(AK8A=lURfG|6d7_=KBw}i4vLZE z*bV~tGsn`pq4~u6Z*Qb~J16U0MMEUr_Q48z0lBbFnmRB-= zK7Wn$Y36=61F23S4AeDFmPYbpd{1Bk?lLAR!_$(8?tER@@o5&a+Zvl&y)*g|UgO}= zrl_M>$vs0}vmvF!t1bzl$VNlqa`HnmlGu@Pc#hId&p*k+iOHxb?5sk5wgc}Oh3uy_ eRNL1IHkB0B`~nz+SAq61#E%@83F0u5c>@!R+PfbB delta 904 zcmV;319$w42aE@QGlx6};&uQUg@726KD6mVAg2i*&`^ayB7abaAqlRT0?z@^jwl~j z`FOfzHefvu1V=-EUt?#(K)PLMX3N^I;{qqm!jw@WpB|A-?FYajXNBjKf|1aF-0#kO zKb8@*y)FVz0@fN1*y+v5#Ir*d?U?QpcP;3HAlM-{?yxw24mkaa0yU)qm4u7QR7u=3 z=^^>DY4yKRic`T5fW2PFNNa@vhXQDFGaGUuQv#!|)wzE$zQ9q*sp?9){H%~1lmo@8>!nB8AmeFij|~C< z00056g=A19Rv*Jyx4Xqd?4bg`sDK;a^6Q3A6v&ou9F|c400000{{R2~0dgb-1^@s6 z03rY&aFq@ODDs_UXDAj4ili8i8P3vQC|60g zX=e0s#A0ZysTUgvrguc z$nA3-mTpd(Vegy3HP?ksfiO;>*fp3)Z5%-o9Y9bua7}^M eqmZs8Q2_6YdIA`PSAq61#E%@83F0u5c>@!7;;#1q diff --git a/dlc-sled-storage-provider/test_files/AcceptedSubChannel b/dlc-sled-storage-provider/test_files/AcceptedSubChannel index fb1ffc85f649a0c219adee5a87b86196c4e578b6..eb8792dd01005c23de32d98df445a7695177af3b 100644 GIT binary patch literal 3375 zcmZorez_^{;`19`yiVNHni^g=t(Z|IxQ4rgX6#+Z9eQt z4)L#-IiBaS+wC~VqDIwG$t{P0;XfEKGMUJIF}U(>9?y~sS5IbJbL2ap`YV6ywN**r z`#8sw^QKxcB`}JrR(#rKeCga?_K#~O#@D>+pEf~r@8$DaJN|w4YTCfWz`(%RK)^vj zH4wn`%ajq!Qese$G*hh8{>r7(bpOOV$CM7eRjQdHny2S#vAo^tU0PJ!o7zFNQm~N^ z6q&dK#JbW&8}C)5><`y|)!WIOBD`7gc$fAP$CUGN!AtM1Nl7kSDboArzb}I>Yr?00 zTc*vs#=SF(y?RB*TA;+d(vyA(p|@7`9TZzq9>)DoX;yG9Sb|q*>XN{2v$B=OJbO|W zi5lWW=d$!AZoGHf^geFRV%GwZWO$4p zSbaw2se#k$S9W){PEE>rIcd$59AouBX}R_-bIP>u-C|*IH*jH6a9h!TIP&bw>+JTg zb}4<{m6Tql{p3N~WA|zSzwK4+=FgZED&(SjBE9BMwio{YPJ2sX9eb&{9H(D?b&=!d zpj`{HEE&P(h)muR^m)?S)}6J-lR0Dm`eyoSSzKRpch~PIwjwQZuk;T_VcEZUJACvrxVd$Jl&J)?UNF#YY^veDx};%ePvtG}GPp?Brf1OpGD zj04NHf)80}*~uPMWU7>0=&h|b_rs0v`{dTt(c4=YIIsxlh z3XIIm3BT4$CWmJ?a{vCSlJZ&HaaUyK-6=`CpTzxN)xpsWiDqEnGYQ0*Oj*XoCsMzD z+AO;Xr`uK}FE2U3XmsdZ+6vuev0F2lF5H=E!vF5+^To&RR^C14d|AZBVB* zzVht5SLViK9=Ghz=jKOO$~00_D+T5)_fo7o5>q%iU`v3w=>*oApKQ_gN; zSiOU3M$6mEqbE`iUx;sIzOhJBz)0{^J+nh!&e5obiQAH9>MvK8NZYfcW{sWB(r@=Z zzx(3Em2vJU^YjOd3RcW*4_$l{mOBL+xxBPX>5wyFY(3HIIeBl?JY!Ho0w+Fdg(3sN zxF8r6U^n3-Ac+;93W(gOI_zOI8ZM*Z0t$iAbTFC@M$-ZIG%y-2#D$Aqux0>AWy-Dl WXOm~%_2ZUXe&T#ZjP<-DRwV!c$WL_u literal 3343 zcmezLvu}Qz@mF59_$hC+D%y|d|0@XfWy`$Oslw%dF!##?=D#XgQ#(xmbpCZ$PrTX} zz%3ZltLk6zCW-ay`Q?*%0yP*I{(}J{vn8L8=Gn^1#h)~p6{gj9P0wPt-Q-!Kf?GGO3}a$oU|{@2z(GJY z5Ww`ylo8BQVo-S65IJ>SJQK6__4AWGc6e)EznHa)?QFQbVDRGeDjWVzPwgODDcHyd zicGG@LOK@S3}wi9rqXD%!KGvtgAC*EZJF|qU#y&4+bytpO-gdvN|Cb@x9wHAb*Wue zx$UWD_>PSm{uJeQtOZIuSrg6T)Dkr5%@6yF3J(tb&lmk#1eWj=O9*(~nY!ihzxcCBvx!3oXNmox4O3a?kzJF`A%KkFPzu7iyoiTM=}O(302`xQVG zYphoT;W%XkIf#LgxY+M`7vI2iGY%4^VhkdO&MJqBteDT0wPNp5<(GRWou4sd$Aoxz zj9<9KvaNx2NzuClk*6P5vbJo?`l_QHd)Zx;w>>Jk%GjBO!QH@xNx_pRbQ^=qDy=V; z_Gyi!eP`EB2wHiG{kh(hS%G>t4nDcUq!1yU>o+&7MM=r}M2h%%);GHgpS{X?#r~#> zVZ$|@#c|n;U~>$7?>OBSWKfRlJe`=udM5o-|MACicJU5eF&yhYmziv5Qb@75|L(=i zdHrV&eY?o_ZofxvcxiQIIi>V^>;rjH9`1$O%Tym*A+{cvzs>Fxa76XAtyTVh!?PvB zRdr>(Lx7BDQ5Lh?x3+ced#q#YMYg9Em+jX7&wNexP{-A0yF&kkv+3-A$H+WUYbs}D z%-Kt?C*PCe>^WY2dg715M|xqiKIJarz4KW#k*O%{-OO{w)1IpR{LDY4g3H!5J})6% zD!I`3z$?jUqpYXQ-QHD?V8!B(dUC0g)S3hdqo&!(}vFKp`-i4o1_#Xga{221dh$aJT>f9BX0B diff --git a/dlc-sled-storage-provider/test_files/Closed b/dlc-sled-storage-provider/test_files/Closed index 5787b24055e40ae88bec5f637ba9f014ffa9bf9a..9008a5e96da9a688885dd231abf0da9f407de6e5 100644 GIT binary patch delta 1094 zcmV-M1iAaw2-OIG0Rh60dyFSP{y9Rx)V`HAW(YgU|{ z)(@+I&YzY-l^5rU4a+jn(B6@5-(uPbBNKZ%8r_1(%O^yk&cw}onlp9S`#Z)lJSgJL zP=T1?oETl~F&|lUTq^RBu2T+8uBL-61$liaUJ#0~Bo@o%`n;s@lp+2k^P$@Xs+u%r zEJA{Z(jaMn>sug?Fzvhjf~Fdkl1-ID>ntwDE`@*>QSaVu=I1#ZrS^jN|BsM>C5e2L zmULpS?>kb3gk5WEV%Y>CXsDP^QqmiZM)+%4pBdzd&v-?{jN~ytzWpl&Ts%3`T@H0* z{l##-8SX$%Elepp6wlaY1If>f>yX~Kz1JP&{;hw1ar?Ag6;Din>sT63IsvZr!z!QV zTQ|le?z)F9;i=49jI&)ZR-HM)aL|fZzQk6t8-4Tm!8mNWbBRJ95w$1bIFw)&CSm%6 z)`Zr~Y#x<&Q!G%hrgX?ByeV>I0S>V-lM_xsv#Q538hYHKyT4GZ(|FH|6k&KKdr!7PL%p zO{S%ZYLOCzM5fkg3@`3F695VUFaa?EFaa*2R!d>p`o3#wmSMhfh| z=&fhPyDkDCe18L6Zsq^V{9lmB*}Y_Zcp%;Aolm!E$qU${B@F780Z1@K0wDm@ubu|} zrBdBtZK3uY1Tq+v5qgu0Yx|@To#>)}sO}}u0w5Xf$bY*~1t(Lo=TH>a572*G&;`9c zGIK4mm+rRPUC;qXQXvD;%`?PkHe_ZWuY@UgjHs4Nw1Y(ykYEDka;Y1gtO$@H1MKg6 zMQz3Z?JMI7Ly;?Kvk4}uT$9(2r;(Wjx+&tU`%|M>qBcK>*Kk;@1EQtQT(y zALUDEyj*ur57CPo^4eIy<=8Zd5Le6OtGdrt3pLgS!6SAJtXA$bE6S8K!Uw9d)!fpf zCuJ3wDi_J9M#|Fenuf%{0tF~{WkEw>B=~r7pl`?gj1NG62(C$XE$uqQP`iyRTTebl zB_z@-?pYD)H^+t}ZA~Xz>O@qVmA#~>7U8zrrc?fBzXGy=C^wHJJ4pyt>7KmTeK{DV zT(^flHBff2Z_X4)#a&CVL5^`bNzR_Df{RB~t04}Rmww%I%e}E6$J3r%8h1A>NuWJy z{=zv8p1OU1J&cU)(*vht6V*qR797ij=U2H?UnS1Ak(lnh&q>STM<+0!!MXu?teKc{ zJ4QIxeC6F{!r0lC6XK!A7^5Sj*fn;~ZiKWh6FedT7RpdIvcAo*p{WAz^5_uS<#ghr z{AP>`$``&yQp+EnXhOCBjnXYWcd&brBW%jT=SV<*XrC;V`DDVFQe6~cXSTWF-h&zV zWhhMEMii+`xa^h7e$tXh;xjgT1@@DZuQtxq)-zPoifP&HgVgClRAS#+$?EXXz4ftY z-voltDZxNFAmqoH=~`C)ULrG`n%5a2#8$C}NjJ=M?z~#vdd`Ie|CB^FIe4aA3J*k? z<)y!W9WfM`^+)sO-(q=1nBsbVam{I^-2U@KnLHUrK!MBJEw_9n#OyD~ghvmfih5-f zrqwLl>nQjhl-KNwY4kUmub`z5p0P)L5dr-+)1V$AB!d7-2voZLEwt7rYTx0=k;){A z5qI-sqwm&#AR{)E?%urZ@~C+p1KO&loY487FN9%~ zC0MFOGk9zXll8pdQNjEMLI4T@FaaCm~J~17ZH$XnMiJN+?xKv`{l;!EjU%j~quv#%pRh%8z0Z1@K0wDm}hz(VI zE^CAD#oi2@g8(z?^*kW~b{%NDThEDq_aa_^0w8uCKlRDMRuf3lrM?VURd|#-Fz8GO zk?cg;$!R*H`ZobbQXv9JplylTuySNo&#vi8<3D0`J_s zvi2c?l@DU-dTzR`Mr|<{8|NmGgKzEh~HUIzs diff --git a/dlc-sled-storage-provider/test_files/Confirmed b/dlc-sled-storage-provider/test_files/Confirmed index a43b731f61883ad556cf7be8a91ea89a13e9c81c..4d290999d3efcfbc59bbebe64c5c9a941e5417ce 100644 GIT binary patch literal 5983 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 H^PT=Lyg>O@ literal 5983 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 QrJ{)Vu+4k`0FxvHFWth2Qvd(} delta 890 zcmV-=1BLw42-654n_Jzoxzl5fQd>b!SnQ**3L|efwevjP!pX|bvk|-6e){p$ zyp8w8djVt)Ib>;t!9Fk4RR+q4vRwz(a=MlPk+GLV_xvkrn3dUH{n#S?0bu|N9ii_Y z=k9&o$gl`$N@dAIMEBS=m>Cg&9B$bjmmGDYge8Je`^EkK5wJ4~eJqOE-!t_^L4TCJ zAH`Bd)PmV;Rn>)4LR*qLC1BkRJ3oiVm#@B)p^u*@^UPqeF$O)cdKE3nnUZJF5w23Q zYkmIK>0X`mhbY2sLPQ{ICC$A!9DVhLd#B(aTWijq7Q$zKH^|6>hzx*#rxiA&Iu~#{ zI7ZiqiSel`(P9IxAjicg+|N>-7(AMvnnA&nGznlFJ2Fk2^e<6=>FO2o`F3;ixI(Qe zJg@=U&MTr9c>p(&Ax6`9cM8NitoJq*LaL{x_Y%TTReuAwZL>NfwZha`AE&a>#Nn`) z8ro3?&@j{=n3hFI&8%H3uWTK)W=voS4EP)31$wQ#*`G75#|TkSoCGWR*h7g!{QYkS zezVsA0-#2dP68)?0|5t{*e10_7=3k5Gv254l0KEll9hi_rB+pwsy*_ty#N*f6cl`K z$t*c+^JI0C!FR;`wqv|TFHR=4gOlzuJOCB|6oZa%FZ_Y%xn54JR|m|CHVcL`A5^(p zP?HlIX#q!ANmWHv< z7{65ledr;4)9nEO0002~|Ns92_*X6x0RR917626JN1xmz`zg6xmh!6koB!*EOu0b- z!s`nF0000M02KCC5OB;FQ0@zME*h9QSuLUZCn)h=0PW(^6gm zAp?p)-O*Mu@5ZA?o9fEq8y9U3(nu{tOt>@WYz+z>8xUv!0000000IC1|Nj7M0002= Qcit9G`mXc<0FxvHFZ+3x7XSbN diff --git a/dlc-sled-storage-provider/test_files/OfferedChannel b/dlc-sled-storage-provider/test_files/OfferedChannel index 45af6630c387ffc1a651c6cd187f7ae14c121fe9..e86a903170b727975171b75a48b76abd4336e556 100644 GIT binary patch delta 205 zcmV;;05bpU0qX&O%{@A*f$X46vYE3U^6c0xt5EB&VG${9@92yTy!#f2@y({rg*&Va&@# z_J^@g@&lze7`1H>Z_!@(Z33ru*_kLi2`G?NEyhyN&d75`L7C73ZpJJ%^(h)b3kJDX zZw~y*;20NFc=Ne~)}8^;c^(Y<0`ev@^i$ME=S(`Edh)9uA@oA~6zl1Pc*6s$fbMNb H9+AW+PIO|J delta 205 zcmV;;05bpU0qX&OtZ1??EfTx81A=C6GXkcUW$y~Jv~%T{v6}a=e&YE`*d`8g zs8gr#4+G@=qK;>fp{M~^3jEd{(+BM1spWsek(&sAV)xxe6f`meqSeB-6up?9zvWxa zPAKK01KQFD*9R!ssS6^%3+Sr`0>kCQoeVp)MY>p~F~AQK)tyaO?evj5;y=(Zj z;$w=$pL0_gpn#F7X3v_vJNK{2XHKoi&XQ%W-TwX6I=v@u7qSc9dfxwd&6(+Eirf7U z9Z%g3W!&~EUR`YYdwqyY>?W;KPc*%l{Z#Z4nRfTAJ-ton>!E*Z9K>e%pHqJ0-fOnb zM8u_E_qXVd5Xmw|Cb!#m=ViXVxsdxz?;96S(!AWvoyYe$l?FN<6w+S#tcZ{4yXl>!O=}m2qjo6cQ=IB39qZON1l{bVmF)}$uh9vH9GCNcGp8L_pf4{nw%-((e z;BfWP=LIj4yq&BsfxW}DUxC3rNRjD|)HIpvg0nnMt?fB^(D|uJ!OrzMpSB#A3YR|r z_qyid1Vv_pZ45u#rtxnrpDQsVz)<1Pe(RDg&C~f7KRC#`#nF{JJ!l;RK{W)efrT+n znh~cAs>G-|)bN1uM#BXb5~%cOxS)myj5iuCu#iBdN5cg*JYc-haDjycDm@&-Mak{k d0+26LZrx8m{mWlMdbMq;%E}q~UnV390RXT{=ZF9R literal 2424 zcmWI9`E{CM-232dw#VWY1s-W+d9f$ul(6&h+F5reTx^|qhk4S}9c?}7D-LX5xI1tE z=jaTsdxzqp9BhyZB{>H5|b>j(NFEx ziPf9A<@bMbV}JriCfylpryW{hUL#m}{iZ2b+V+6MYyTjz~_0D{A zopZRnn?r`ggGqsm%+u8ipWM7Yx#XCe*K`DMR#qV^tsr1`&Y+qZqqXT_kKl($0p`Ef-h96_eIRvAi~a&@N#o!^rpUu z;{xIn*#&YJMenkf1bc^RzXF4MkRsCzE4vNTnb;3S$;ybwmG5d0-n)Lu0~h-MLbFU_gBG12PeEI(n9H(rHuyE{#m+s}P9J!l;RK~o4C0t;iD zG$T$KREbe_sNn(QjfM*>Bv9$ma6t_Z7;iLOU?G7@kA@3sc))n0;Q|W@RC>6D3jnkZ B(qI4p diff --git a/dlc-sled-storage-provider/test_files/OfferedSubChannel1 b/dlc-sled-storage-provider/test_files/OfferedSubChannel1 index add2b164ba41e50cf420246fa4b6d24949a33d59..6defd78b2712adc16ef27d1e02d91578d2b91c55 100644 GIT binary patch literal 2456 zcmaFF)HvHfUw*mB^LLG|@{7-DRg`IEZJXx2yYZ)v(w-yln6F-4CF(W(S%Kc=J={NQ zmu?X6JaxUMc9!Fp#rHnz6`$~AVE7LP3`{>2z7^exN?5)>Xi1Wxa*E2-$9_jT_D;T@ zzV7~Jl}EZ$7@&ZWd2ZR{AFY38eduzl@z@}y_wAV0oJrXirXJUMndut0J~o-@s;heW zi%{v!_f%D#Yj^3bG)!Go6ZYu%awGF?h3BQxwV2KAqj#M7%9^;s$>_?hKl5g__wrN} zFS1ko=%={F{eJ*IBh!Bg!x#IX9@Tx*zFgzIMD`U?!&z@$?K`sTzsT-;Q{AI9ncX@w z4CA%dT*&6i-u>%x*zB{1`<_Zz&fmVN<(_NaRF}ETXaD5f=TvxBx81Dg`{N@mJ~r<6 z`Qo3>a?{pld8xH+buQRDO#2lW+=CREELNO9D*m+O;Qg|*8AmrSR~KUFj(oJC`%r12 zsmRqkQ}-$|ZRDLhBVYRej>~r^t&#nbT9kE2bcLsa_pa`o)VuXj|Coc;F%UF`pdqj@ z#z`~cltGmkRfifLFy3gmz(N9*9t{`N@PP3~!vz)+sPt&KpoRyGHySRmkU*t}W4Kst e;{E~hWy-DlvnCwbZgD)Rll8)C{Ry1W$GHIebng-X literal 2424 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 L|JHfy{7C;l4|v3Y literal 6984 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 bK14B{f;L(4jO=jV7mY^1e``rP+4cSd=@Gjp diff --git a/dlc-sled-storage-provider/test_files/Signed b/dlc-sled-storage-provider/test_files/Signed index daba85be53530a21f9127939176d8f6e448f9225..13fc561e9fb1abe1d1e3abf59e2f049eaf9d9ef1 100644 GIT binary patch literal 5873 zcmb_gbzD^4);?zjh8jTW5TwJ4GL&@Z5E6nQjkJ_>h=hbVG}ZtjNH?gYfPm5^CEdQ1 z5+W}nN_~Lupuel{ckg$^KRchF6iQNv$;ak^8ptHv?Z8kRWdC^LG-a7j;C;*`CVEO~K!} z)YC-+oHH<=wdPHalx-Qw8@*PM*;^uV+egU5C{+0z7mTElz@zv~h=h&6%V%2x^gB`2 zG3I+CEPJ2@Kc+1)dGC#JQ5Rx!lNsUKTmZuzCOQ%KdPc01@iZPsyiCSQD zqU|2~)MgmX{b+l6r!k+x9wW)EtD8WW@Wjg!cg6OP%B3M(XVU8*122?OcbgLEkokZB z3Q6hJhjC%4dSmgBE+8@j&4J;?ykt?+Wn){F_hOC6$|PlxEcKgWmo?#^7z=0KG_b!T z%&)_jR^~Q;*CRcQo*%iuck|j~^LnDvy%9pP!^q>iTX?whx?5WwN0tyr*V^6VuONev zN2QB7X=XlBRFf(*A2L#Xbp{OR$M+zVsRqJS>3^bH04jiD!hSt;l{s2`Cnm%>AyUl# zb(}jIXUy|{%Fy)U5$-7RXo*xWqFU0I?i{CF;xd=Nr2)IOTfp5<0^>~E+gM$MI@cJXs(c9sK z-+-j_mp@;kl85u>L@{NA$vN>nmDtdw{R_^V3Hi{QW1_VeIkWnB!%4ES$M|%Q1A=ZR@gk57kn%-dp4`W+m-ll|r(1hv-WB8h4&paZSLnOd%!9oHN`S4y{ z)wVnP@soWvP=1c0T5jo6tBP;hxo7D%yJ&L);TS2Q`EZo@#gg@dqNC0D7#jY%Yqy!> zfv@aF1-Y5FgruraT4nCS&`smWBRqE%ZDmrnZD118h%c+k1a7o4NyW$L_&-_4{ zTf-q>XTVv{?$`o0&t4rBCD+?;_o0N7DkcqPgsZ0CXMFrTTuJesHBVQH{qg#+(FJlw z8bB<7jp_~d=haZQ`57$}Q{r(`YWE6lYdqnyRXZM}`idMk3g>vhe~#+xZ58eugvMNU z#GScW2RUVwxY6mKqT1|Mr@_|+sy34*$S^CrziD`MRrq^UIVuq9!_y-P33C@KBC4)E z6+}rvlc^cG6o7{xzvVkgL6{7T;lk!}vs##FJi5T842Ar~>qs{*>Tflxx+Y)ie04x( zC_G4jLQR}&Ub=9Ya<0un;6)dLS81FnkH+(6S*ao_^`D~(9_{V#rVIrEdJtQ1PxkMJ z;snF0epX<00C-Zf0jv%@G5jmRfFCx8S%r1=gLwkOPMFy9=WvqW+hKnq{$QTKxF^i- zF#`Y`tob4TMEt=#f#E02<1_2f8W(GR*ki??)`^GJ5&Xi0VWFRxho>o4ONbSSeqo*r z`V;dfpZHiN@h{91==V$#ER*yXCIJ@uJ@XV6KK%>xWFfz2l3|(Tzc7ig(C?WPSeWt` zCL9a>o=Js;sefS-W1*w|M>J@#YFey7_dV54E-_gBj{pJy+}$xuMNzb-r>TDGHYjY1 z$JmlEkdXYgzPsq$!a2_ob)!mM)~42c*|B(;= zzjam}mF0h{>>U|GFL}klj;4S3vH$KEbz{RlhFJin#d=lw$}6rM?sEvQQ0X~PVz8rp zYy+S7BH|1UfBE%(k|lZU`SrV!edGjov55M3ELNmg{^-Jl@~kk zyS(v^=F6Sq?K>)-pbYDG?|)O}ONi>t|8FBg8r02>i_77Nzq=I#4P_lykpG@TnN!@y zmE~_70^}3N%l+>;3{o{YE=Ygluwf+UC=!2naQ+8d8nm56#aZWpe7 zs+~3l3WdqNYMd{gdKay%p!3vN`wKp&8P0~W!({3Fc220w28Ff zn?Vk-ke}uzm640B;=cp)^Sqbj`q^aJOW5L%q5A?ok=iZOiBr!4pRWghlkcKkNBYb% zQSN58?ly|XdMhL*EaKQb@n)D!e<${;1yc4nUkd7@bjA2(8ml66m2R0HXN*bcrkGO{ z`-?xl=q%I?)RX-nt^o#*WtMH6M{>b?D5G#c7sg5#jh)(;Mrm_Vy4$H^Qv=pDee$qm zqT$9B8FYY=iAxE~BzQtyO!xeceQ11Jq8iGkG1^jLbj~-Esun7%EZc ztjGAY<@U%3ngkQY+e=f&mnCsS7y;)bd1C7t;eb0F%d5x36PPvM)>lhJl30MERp zmLu^q40J9F>4vcGn{Fj#?uCx*b39ob)l@Yq`DFx?m-P!4+AE#%}Ok z9dsuBxlDI(T9zw*R&kA)65+t6a`Sm-p5zTHW`K&u##6aiR0kJT2!o z|GY&$j!nz5Su`;P-&|;@HU7d7K0v~ zx|A+F)_>V#B-WR|aO5@A^zF61BIOOtH?maYr^^yUW8sJ>T)3%#cw9GoSL(Vg_wxe1>3Fj}Jc}bO`70U(pctWxu z*Cu0DwLSNC}{;I}GCo}WS)w2@##Z+r4rU7!ARmd6Dx4M(6s-7$r%}>72 zSGNRmxeCMEWV9H%>4kjx)ApF_!7CrtZlY~PZ&5lIVa&(F9%g4?`aa;LitO(9>$AcL zMLYxUmnd9)S?eqZe}s}vd*|E!ynimkqb$jaUVdo}8W&yncq@-rh%(6T%KL3+PLoDA z=io%?(r}ej;nnt%49cA65FqscW^fc-Ac3f*ar_Qe>+YkFB0T;fhX-bov zHVP{+-)y=E(wffan94SOD@tQbwB;@ojDNcxTnDZD>NCdXP4Z&2lo2MVuox!i+#s4q zAnfUuu0iNdNL(SEtrbT_IEPTL@U_lkRQ{^^(%&fSTz!u)*YF!?S^JhzbZ!+RFx5Sx z%>x^rnAo$Kr|MwW#9dA?eKU#=@#!EjH_=^FxS_@uqdF2wA8FjTK3Fa7MBs@zNWLZF zRopQxEU};1bRMRsCJOga28M`@ScgOKQu3;<=vCDaES=$*r_`?>S&sSKpFCtGr^)pX zGdY=idZFS6=x5cb3_{kdqR!9`$qq8+-z}<7C{A@u&g@NlMq;sDMO!dSY0q3 zm6SdxRc&nbSNha%Wko!7k9)j}+!=Yd4iJN2JZs437G8#?DM?RR$%T9Kn8HH(a#qMs zSL4q@Oz@)UYdQ2~vzt@`?{3XWuW@qct=+1k#Vox?lUzh-6Lnk|2#PJ#O`I&1EicYh zC1Y#Ujh5@^$Em)$kywDVWaJ}OI^*#s!pydy$x+%@#@kIBY*vw!F-*Vyfr6wt7ryox zT6uSS)Q?ajdv?4b=Sl@fs1y;)X{x%#_gk%tPr0^l=R-Wm1pDYty>aSdo8k^He7VNz z&3f~8Z4L@BY0%Q>Q3GtJBpmz)AEyhE31$gt5+oN<3`&f&61YUSgrXHBGVhG1S2IQo z>O=Aj>)z*1^bsoPTlI7sgfZbrU~qkr-QI#*3+wN#uZn@q-f#UB_0VbpkvpqBXw57_ ztl+g`Uua^L^gL3bywN7v4O6a z`V25!+eG>6dl@75td!i%)|1em_#+3^ z4aHURt6+jC(y0+ux8eU);01nZT3jhT5CMMwAwq@AF7K5wxEQ@<*f)5YDkUgv-cJMP zeAj66E2N|?#s$LU6?NIDQQ19n^U@h>SZPu|IWf=*5=e|@2x(V)BCHufW(bbJgzFVl fQeRd!!2)yV{g$)kmVykt{gI;>y3IyVQ~>xNZ~m#6 literal 5873 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 literal 5873 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 diff --git a/dlc-sled-storage-provider/test_files/SignedChannelEstablished b/dlc-sled-storage-provider/test_files/SignedChannelEstablished index b25d4738b4e8c2ed05106b95d852ea96c17f8ff1..9e1b641e87c2bbabc4b3fc8acda0eda8b50b6ccc 100644 GIT binary patch literal 3663 zcmeH{XH-*37=~{l96Bg08(7d#qI4mwgpLpm5D@7_K(A5)8g&H(6mda{$|5R)^rn8}U8-9B(I4m<#;dpaop$NrAT8A#(RqDH)Z_ zT3&5Y0b+N=+Hs~SXVZiFHi({KN$SYNCmb-Wi8qxUZ;OCid6vy#n*1Y^mhMPHK6svD zNSM@1-da3!Y`bJiO}QC^e=$N6Z%{6@7c_kl=m6#RDe;no+%2s3yJA-*FcWJU!par2 zwwMq*lE+SW<|H5ri0Cx>5u{$-^5i#dPQTen)2B_!2UfSZ*)A{$n^}HZ!P%hcr}r88 zrE@ff_KE@7D(_YpooO(akW>f0xby1Zn0lVrVqKG=`EpDcDs)`FvIvKA4u9Z2Jk>M! z`LOHXqX3Y_99@kg=nk=FdBjjX>n^sB6E(QFC@s`YQIR zI{CkJ>5{+urn8eCP_m*x5p#rN(OqW==T6k+2H>+x9w9{6;0&XND={N?!|AjIENjQX`X1^;zPu7As1q=bJR_yIY+;QGnSh@;+2trsnMkc6K>gj3c{!o ztM@aAwh+p*M3xX1tZ>CCeZJ&_=S+f{E>>{$du5BvF#80vri=?crCENioIoee@jYKi zNgsj|8SpACowy~6PMdb~pBe|vb@b@*{T3EQDZaE;?^m4~(X(Zl`yGh_-gg|{p>xiE z;`Om#hnbKbv}Vz&WW^ePhKX_|g#d^g7!aBMU=_CSddE-U&)mYCxWt)JIWL``M0^z5 zi-8m+8ah)`cl+zZnWgB#Vhzji7;JVrF?&W|dz0^4_9ZxFJn(SA1B8KXG{>6-FN#&f z-+O#eExB)ppwVyXr`0>Bi|~~;Px8n6){eR^kV__&eBog?b30k|eaW`t9j}Hku zF!oGceue8M$qoWVYG3ft1s^8)k)OGd$!9q8s(7;;I;M7O;X;RGQYI9h9Ju0f@`g2t z)1m|&y;?rq0nYhv-QlUK-BbPAInYGDrasA0{mj-gxj9u5;Yl}AHjjwh@rMaD4`hx&^PW}0un1UlDF{ihKjteXK= z*VfiRA6XP9Yak6^RK$JpIU@%QXp5T~XCmN5#rEEg&DgbN~o_*AK#q|E_1r3WP(j|LttrqlQE^Xvh6Dxx%wa6qTr=r(gTNU;6KT zjXafk+tZAIcLZM6KE6;hcAbj(c*ZZh0?1f(H-xbt2?6&&r1S888jsH(X#loKuhZE%eZ=oR7-owg7Pdo69 zjTfrPZ3N{aS_NTrjN+(4R4i6{m}*v*W738>a)ENZe~)Tox<&}#x7Tw$6+@UKt$tg9 zcYSUEBh}QY#t`qtf6QNb)bw17)P=bHg6-24F;Rs;KBxjO>S^ugTW>@xUj)s WlN(&vV6e#zE^IK^b(=aP?XM literal 3663 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 F0RVKcl2`x$ literal 3385 zcmd%ol=N6oyh`Qh#*JL_;-hjnT8>?OVcV54`6AQbJ?|SH$-Q~; zvR(7<*SvpgW4?R{Xim{i*`ee0@J~cD6VtJDj@CeqQlF?$TlLdEU%9$gUOP1J^M^;? z;r~;;^cS@oIS3hQO^%Es{)@;}(?k|O$~fe}-co5AYHhaC zY2KxKc^QYpp7ySl=VP8=VYOp_*tEqS=lMiJu3o>i&HZ?Pv%~yT*EkM5NuM)ECvMeB zpAD~N4>32D?OV6wjCtFkt)Fs*O-f(fd6RretTIGoo}|j8*_Z3g=AX+Kd$Ono{`_-QQFiKyvTV9*(W#tauu=jKB zl{Y`szV+a$pZ^v)ThXx=C?UJ^)=kF4 zHtDlNH%SQn{{DN#!y`UmiQ0UF14W5%`%{@`bS`$tUA(_%VpB&tP{*#wPpdQYHtd|B z)KV6BEAgPHwL~sh$DOmjHxvY1?lNteQzZ5$)%5zAHLPHXUXR-^kFl{o%}&ja<7t#iTjTnO| zW8ay#eVVg7_Rg%4n=0@u^KtP5Uj>fHaIGiDW*r3wg#s|-80`^3y?K_l^&L- zoVAz#&b6@KDd2i%vfIYD={;L^$o^jX;?d)uoYzF&rY|?U@aOQh`(TTi_A7vB)>y9w z!fAq$xlq~u;!3{xk65~IpAl6MmOCjh-(an`^m)~P<-6ImUt9p23G@onF8x>EKMMTU z$?Xb%VIrG+F5seO_jJ;();nAyj=hsN4xCisi=VH}ZaW~i9udZN@5s9(xLUE_J4KGjcZzCse8@))ff0W!rOsy#op&_aH?k zW3EY?!<66Hb~jpTt}m`~ld(ML)lmK4Yw}g245KAlhZLEQ7+V$DRW9WJ+U{&H&otuj z=X3LxQ;( input_value: u64, input_index: usize, ) -> Result<(), Error> { - let own_sig = get_sig_for_tx_input( + let own_sig = get_raw_sig_for_tx_input( secp, transaction, input_index, script_pubkey, input_value, - EcdsaSighashType::All, sk, )?; - let own_pk = &PublicKey::from_secret_key(secp, sk); - - let other_finalized_sig = finalize_sig(other_sig, EcdsaSighashType::All); + let own_pk = PublicKey::from_secret_key(secp, sk); - transaction.input[input_index].witness = if own_pk < other_pk { - Witness::from_vec(vec![ - Vec::new(), - own_sig, - other_finalized_sig, - script_pubkey.to_bytes(), - ]) - } else { - Witness::from_vec(vec![ - Vec::new(), - other_finalized_sig, - own_sig, - script_pubkey.to_bytes(), - ]) - }; + finalize_multi_sign_input_transaction( + transaction, + vec![(*other_pk, *other_sig), (own_pk, own_sig)], + script_pubkey, + input_index, + ); Ok(()) } +/// Sorts signatures based on the lexicographical order of associated public keys, appends +/// `EcdsaSighashType::All` to each signature, and insert them in the transaction witness for the +/// procided input index, together with the given script pubkey. +pub fn finalize_multi_sign_input_transaction( + transaction: &mut Transaction, + mut signature_pubkey_pairs: Vec<(PublicKey, Signature)>, + script_pubkey: &Script, + input_index: usize, +) { + signature_pubkey_pairs.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + let mut signatures = signature_pubkey_pairs + .into_iter() + .map(|(_, s)| finalize_sig(&s, EcdsaSighashType::All)) + .collect(); + let mut witness = vec![Vec::new()]; + witness.append(&mut signatures); + witness.push(script_pubkey.to_bytes()); + transaction.input[input_index].witness = Witness::from_vec(witness); +} + /// Transforms a redeem script for a p2sh-p2w* output to a script signature. pub(crate) fn redeem_script_to_script_sig(redeem: &Script) -> Script { match redeem.len() { diff --git a/mocks/src/mock_blockchain.rs b/mocks/src/mock_blockchain.rs index 4a2fb644..70a17421 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,7 @@ where &self, _confirmation_target: lightning::chain::chaininterface::ConfirmationTarget, ) -> u32 { - unimplemented!() + *self.est_fee.lock().unwrap() } }