@@ -1864,6 +1864,8 @@ impl FundingScope {
18641864#[cfg(splicing)]
18651865struct PendingSplice {
18661866 pub our_funding_contribution: i64,
1867+ sent_funding_txid: Option<Txid>,
1868+ received_funding_txid: Option<Txid>,
18671869}
18681870
18691871/// Contains everything about the channel including state, and various flags.
@@ -4865,6 +4867,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
48654867 self.get_initial_counterparty_commitment_signature(funding, logger)
48664868 }
48674869
4870+ #[cfg(splicing)]
4871+ fn check_get_splice_locked<L: Deref>(
4872+ &mut self, pending_splice: &PendingSplice, funding: &mut FundingScope, height: u32,
4873+ logger: &L,
4874+ ) -> Option<msgs::SpliceLocked>
4875+ where
4876+ L::Target: Logger,
4877+ {
4878+ if !self.check_funding_confirmations(funding, height) {
4879+ return None;
4880+ }
4881+
4882+ let confirmed_funding_txid = match funding.get_funding_txo().map(|txo| txo.txid) {
4883+ Some(funding_txid) => funding_txid,
4884+ None => {
4885+ debug_assert!(false);
4886+ return None;
4887+ },
4888+ };
4889+
4890+ match pending_splice.sent_funding_txid {
4891+ Some(sent_funding_txid) if confirmed_funding_txid == sent_funding_txid => {
4892+ debug_assert!(false);
4893+ None
4894+ },
4895+ _ => {
4896+ Some(msgs::SpliceLocked {
4897+ channel_id: self.channel_id(),
4898+ splice_txid: confirmed_funding_txid,
4899+ })
4900+ },
4901+ }
4902+ }
4903+
48684904 fn check_funding_confirmations(&self, funding: &mut FundingScope, height: u32) -> bool {
48694905 if funding.funding_tx_confirmation_height == 0 && funding.minimum_depth != Some(0) {
48704906 return false;
@@ -4881,6 +4917,78 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
48814917
48824918 return true;
48834919 }
4920+
4921+ fn check_for_funding_tx<'a, L: Deref>(
4922+ &mut self, funding: &mut FundingScope, block_hash: &BlockHash, height: u32,
4923+ txdata: &'a TransactionData, logger: &L,
4924+ ) -> Result<Option<&'a Transaction>, ClosureReason>
4925+ where
4926+ L::Target: Logger
4927+ {
4928+ let funding_txo = match funding.get_funding_txo() {
4929+ Some(funding_txo) => funding_txo,
4930+ None => {
4931+ debug_assert!(false);
4932+ return Ok(None);
4933+ },
4934+ };
4935+
4936+ let mut confirmed_funding_tx = None;
4937+ for &(index_in_block, tx) in txdata.iter() {
4938+ // Check if the transaction is the expected funding transaction, and if it is,
4939+ // check that it pays the right amount to the right script.
4940+ if funding.funding_tx_confirmation_height == 0 {
4941+ if tx.compute_txid() == funding_txo.txid {
4942+ let txo_idx = funding_txo.index as usize;
4943+ if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() ||
4944+ tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() {
4945+ if funding.is_outbound() {
4946+ // If we generated the funding transaction and it doesn't match what it
4947+ // should, the client is really broken and we should just panic and
4948+ // tell them off. That said, because hash collisions happen with high
4949+ // probability in fuzzing mode, if we're fuzzing we just close the
4950+ // channel and move on.
4951+ #[cfg(not(fuzzing))]
4952+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4953+ }
4954+ self.update_time_counter += 1;
4955+ let err_reason = "funding tx had wrong script/value or output index";
4956+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
4957+ } else {
4958+ if funding.is_outbound() {
4959+ if !tx.is_coinbase() {
4960+ for input in tx.input.iter() {
4961+ if input.witness.is_empty() {
4962+ // We generated a malleable funding transaction, implying we've
4963+ // just exposed ourselves to funds loss to our counterparty.
4964+ #[cfg(not(fuzzing))]
4965+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4966+ }
4967+ }
4968+ }
4969+ }
4970+
4971+ funding.funding_tx_confirmation_height = height;
4972+ funding.funding_tx_confirmed_in = Some(*block_hash);
4973+ funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
4974+ Ok(scid) => Some(scid),
4975+ Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
4976+ };
4977+ }
4978+
4979+ confirmed_funding_tx = Some(tx);
4980+ }
4981+ }
4982+ for inp in tx.input.iter() {
4983+ if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
4984+ log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.compute_txid(), inp.previous_output.txid, inp.previous_output.vout, &self.channel_id());
4985+ return Err(ClosureReason::CommitmentTxConfirmed);
4986+ }
4987+ }
4988+ }
4989+
4990+ Ok(confirmed_funding_tx)
4991+ }
48844992}
48854993
48864994// Internal utility functions for channels
@@ -5061,6 +5169,16 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
50615169 pending_splice: Option<PendingSplice>,
50625170}
50635171
5172+ #[cfg(splicing)]
5173+ macro_rules! promote_splice_funding {
5174+ ($self: expr, $funding: expr) => {
5175+ core::mem::swap(&mut $self.funding, $funding);
5176+ $self.pending_splice = None;
5177+ $self.pending_funding.clear();
5178+ $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent;
5179+ }
5180+ }
5181+
50645182#[cfg(any(test, fuzzing))]
50655183struct CommitmentTxInfoCached {
50665184 fee: u64,
@@ -8191,75 +8309,69 @@ impl<SP: Deref> FundedChannel<SP> where
81918309 NS::Target: NodeSigner,
81928310 L::Target: Logger
81938311 {
8194- let mut msgs = (None, None);
8195- if let Some(funding_txo) = self.funding.get_funding_txo() {
8196- for &(index_in_block, tx) in txdata.iter() {
8197- // Check if the transaction is the expected funding transaction, and if it is,
8198- // check that it pays the right amount to the right script.
8199- if self.funding.funding_tx_confirmation_height == 0 {
8200- if tx.compute_txid() == funding_txo.txid {
8201- let txo_idx = funding_txo.index as usize;
8202- if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.funding.get_funding_redeemscript().to_p2wsh() ||
8203- tx.output[txo_idx].value.to_sat() != self.funding.get_value_satoshis() {
8204- if self.funding.is_outbound() {
8205- // If we generated the funding transaction and it doesn't match what it
8206- // should, the client is really broken and we should just panic and
8207- // tell them off. That said, because hash collisions happen with high
8208- // probability in fuzzing mode, if we're fuzzing we just close the
8209- // channel and move on.
8210- #[cfg(not(fuzzing))]
8211- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8212- }
8213- self.context.update_time_counter += 1;
8214- let err_reason = "funding tx had wrong script/value or output index";
8215- return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8216- } else {
8217- if self.funding.is_outbound() {
8218- if !tx.is_coinbase() {
8219- for input in tx.input.iter() {
8220- if input.witness.is_empty() {
8221- // We generated a malleable funding transaction, implying we've
8222- // just exposed ourselves to funds loss to our counterparty.
8223- #[cfg(not(fuzzing))]
8224- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8225- }
8226- }
8227- }
8228- }
8312+ // If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here
8313+ // and send it immediately instead of waiting for a best_block_updated call (which may have
8314+ // already happened for this block).
8315+ let confirmed_funding_tx = self.context.check_for_funding_tx(
8316+ &mut self.funding, block_hash, height, txdata, logger,
8317+ )?;
82298318
8230- self.funding.funding_tx_confirmation_height = height;
8231- self.funding.funding_tx_confirmed_in = Some(*block_hash);
8232- self.funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
8233- Ok(scid) => Some(scid),
8234- Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
8235- }
8236- }
8237- // If this is a coinbase transaction and not a 0-conf channel
8238- // we should update our min_depth to 100 to handle coinbase maturity
8239- if tx.is_coinbase() &&
8240- self.funding.minimum_depth.unwrap_or(0) > 0 &&
8241- self.funding.minimum_depth.unwrap_or(0) < COINBASE_MATURITY {
8242- self.funding.minimum_depth = Some(COINBASE_MATURITY);
8243- }
8244- }
8245- // If we allow 1-conf funding, we may need to check for channel_ready here and
8246- // send it immediately instead of waiting for a best_block_updated call (which
8247- // may have already happened for this block).
8248- if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8249- log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8250- let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8251- msgs = (Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs);
8252- }
8319+ if let Some(funding_tx) = confirmed_funding_tx {
8320+ // If this is a coinbase transaction and not a 0-conf channel
8321+ // we should update our min_depth to 100 to handle coinbase maturity
8322+ if funding_tx.is_coinbase() &&
8323+ self.funding.minimum_depth.unwrap_or(0) > 0 &&
8324+ self.funding.minimum_depth.unwrap_or(0) < COINBASE_MATURITY {
8325+ self.funding.minimum_depth = Some(COINBASE_MATURITY);
8326+ }
8327+
8328+ if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8329+ log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8330+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8331+ return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
8332+ }
8333+ }
8334+
8335+ #[cfg(splicing)]
8336+ let mut confirmed_funding = None;
8337+ #[cfg(splicing)]
8338+ for funding in self.pending_funding.iter_mut() {
8339+ if self.context.check_for_funding_tx(funding, block_hash, height, txdata, logger)?.is_some() {
8340+ if confirmed_funding.is_some() {
8341+ let err_reason = "splice tx of another pending funding already confirmed";
8342+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
82538343 }
8254- for inp in tx.input.iter() {
8255- if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
8256- log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.compute_txid(), inp.previous_output.txid, inp.previous_output.vout, &self.context.channel_id());
8257- return Err(ClosureReason::CommitmentTxConfirmed);
8258- }
8344+
8345+ confirmed_funding = Some(funding);
8346+ }
8347+ }
8348+
8349+ #[cfg(splicing)]
8350+ if let Some(funding) = confirmed_funding {
8351+ let pending_splice = match self.pending_splice.as_mut() {
8352+ Some(pending_splice) => pending_splice,
8353+ None => {
8354+ // TODO: Move pending_funding into pending_splice?
8355+ debug_assert!(false);
8356+ // TODO: Error instead?
8357+ return Ok((None, None));
8358+ },
8359+ };
8360+
8361+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8362+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8363+
8364+ let mut announcement_sigs = None;
8365+ if Some(splice_locked.splice_txid) == pending_splice.received_funding_txid {
8366+ promote_splice_funding!(self, funding);
8367+ announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
82598368 }
8369+
8370+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
82608371 }
82618372 }
8262- Ok(msgs)
8373+
8374+ Ok((None, None))
82638375 }
82648376
82658377 /// When a new block is connected, we check the height of the block against outbound holding
@@ -8352,6 +8464,43 @@ impl<SP: Deref> FundedChannel<SP> where
83528464 return Err(ClosureReason::FundingTimedOut);
83538465 }
83548466
8467+ #[cfg(splicing)]
8468+ let mut confirmed_funding = None;
8469+ #[cfg(splicing)]
8470+ for funding in self.pending_funding.iter_mut() {
8471+ if confirmed_funding.is_some() {
8472+ let err_reason = "splice tx of another pending funding already confirmed";
8473+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8474+ }
8475+
8476+ confirmed_funding = Some(funding);
8477+ }
8478+
8479+ #[cfg(splicing)]
8480+ if let Some(funding) = confirmed_funding {
8481+ let pending_splice = match self.pending_splice.as_mut() {
8482+ Some(pending_splice) => pending_splice,
8483+ None => {
8484+ // TODO: Move pending_funding into pending_splice?
8485+ debug_assert!(false);
8486+ // TODO: Error instead?
8487+ return Ok((None, timed_out_htlcs, None));
8488+ },
8489+ };
8490+
8491+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8492+ let mut announcement_sigs = None;
8493+ if Some(splice_locked.splice_txid) == pending_splice.received_funding_txid {
8494+ promote_splice_funding!(self, funding);
8495+ if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
8496+ announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8497+ }
8498+ }
8499+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8500+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), timed_out_htlcs, announcement_sigs));
8501+ }
8502+ }
8503+
83558504 let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
83568505 self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
83578506 } else { None };
@@ -8683,6 +8832,8 @@ impl<SP: Deref> FundedChannel<SP> where
86838832
86848833 self.pending_splice = Some(PendingSplice {
86858834 our_funding_contribution: our_funding_contribution_satoshis,
8835+ sent_funding_txid: None,
8836+ received_funding_txid: None,
86868837 });
86878838
86888839 let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime);
0 commit comments