@@ -1852,6 +1852,32 @@ impl FundingScope {
18521852#[cfg(splicing)]
18531853struct PendingSplice {
18541854 pub our_funding_contribution: i64,
1855+ sent_funding_txid: Option<Txid>,
1856+ received_funding_txid: Option<Txid>,
1857+ }
1858+
1859+ /// Wrapper around a [`Transaction`] useful for caching the result of [`Transaction::compute_txid`].
1860+ struct ConfirmedTransaction<'a> {
1861+ tx: &'a Transaction,
1862+ txid: Option<Txid>,
1863+ }
1864+
1865+ impl<'a> ConfirmedTransaction<'a> {
1866+ /// Returns the underlying [`Transaction`].
1867+ pub fn tx(&self) -> &'a Transaction {
1868+ self.tx
1869+ }
1870+
1871+ /// Returns the [`Txid`], computing and caching it if necessary.
1872+ pub fn txid(&mut self) -> Txid {
1873+ *self.txid.get_or_insert_with(|| self.tx.compute_txid())
1874+ }
1875+ }
1876+
1877+ impl<'a> From<&'a Transaction> for ConfirmedTransaction<'a> {
1878+ fn from(tx: &'a Transaction) -> Self {
1879+ ConfirmedTransaction { tx, txid: None }
1880+ }
18551881}
18561882
18571883/// Contains everything about the channel including state, and various flags.
@@ -4854,6 +4880,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
48544880 self.get_initial_counterparty_commitment_signature(funding, logger)
48554881 }
48564882
4883+ #[cfg(splicing)]
4884+ fn check_get_splice_locked<L: Deref>(
4885+ &mut self, pending_splice: &PendingSplice, funding: &mut FundingScope, height: u32,
4886+ logger: &L,
4887+ ) -> Option<msgs::SpliceLocked>
4888+ where
4889+ L::Target: Logger,
4890+ {
4891+ if !self.check_funding_confirmations(funding, height) {
4892+ return None;
4893+ }
4894+
4895+ let confirmed_funding_txid = match funding.get_funding_txid() {
4896+ Some(funding_txid) => funding_txid,
4897+ None => {
4898+ debug_assert!(false);
4899+ return None;
4900+ },
4901+ };
4902+
4903+ match pending_splice.sent_funding_txid {
4904+ Some(sent_funding_txid) if confirmed_funding_txid == sent_funding_txid => {
4905+ debug_assert!(false);
4906+ None
4907+ },
4908+ _ => {
4909+ Some(msgs::SpliceLocked {
4910+ channel_id: self.channel_id(),
4911+ splice_txid: confirmed_funding_txid,
4912+ })
4913+ },
4914+ }
4915+ }
4916+
48574917 fn check_funding_confirmations(&self, funding: &mut FundingScope, height: u32) -> bool {
48584918 let is_coinbase = funding
48594919 .funding_transaction
@@ -4887,6 +4947,107 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
48874947
48884948 return true;
48894949 }
4950+
4951+ fn check_for_funding_tx_confirmed<L: Deref>(
4952+ &mut self, funding: &mut FundingScope, block_hash: &BlockHash, height: u32,
4953+ index_in_block: usize, tx: &mut ConfirmedTransaction, logger: &L,
4954+ ) -> Result<bool, ClosureReason>
4955+ where
4956+ L::Target: Logger
4957+ {
4958+ let funding_txo = match funding.get_funding_txo() {
4959+ Some(funding_txo) => funding_txo,
4960+ None => {
4961+ debug_assert!(false);
4962+ return Ok(false);
4963+ },
4964+ };
4965+
4966+ let mut is_funding_tx_confirmed = false;
4967+
4968+ // Check if the transaction is the expected funding transaction, and if it is,
4969+ // check that it pays the right amount to the right script.
4970+ if funding.funding_tx_confirmation_height == 0 {
4971+ if tx.txid() == funding_txo.txid {
4972+ let tx = tx.tx();
4973+ let txo_idx = funding_txo.index as usize;
4974+ if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() ||
4975+ tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() {
4976+ if funding.is_outbound() {
4977+ // If we generated the funding transaction and it doesn't match what it
4978+ // should, the client is really broken and we should just panic and
4979+ // tell them off. That said, because hash collisions happen with high
4980+ // probability in fuzzing mode, if we're fuzzing we just close the
4981+ // channel and move on.
4982+ #[cfg(not(fuzzing))]
4983+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4984+ }
4985+ self.update_time_counter += 1;
4986+ let err_reason = "funding tx had wrong script/value or output index";
4987+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
4988+ } else {
4989+ if funding.is_outbound() {
4990+ if !tx.is_coinbase() {
4991+ for input in tx.input.iter() {
4992+ if input.witness.is_empty() {
4993+ // We generated a malleable funding transaction, implying we've
4994+ // just exposed ourselves to funds loss to our counterparty.
4995+ #[cfg(not(fuzzing))]
4996+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4997+ }
4998+ }
4999+ }
5000+ }
5001+
5002+ // The acceptor of v1-established channels doesn't have the funding
5003+ // transaction until it is seen on chain. Set it so that minimum_depth
5004+ // checks can tell if the coinbase transaction was used.
5005+ if funding.funding_transaction.is_none() {
5006+ funding.funding_transaction = Some(tx.clone());
5007+ }
5008+
5009+ funding.funding_tx_confirmation_height = height;
5010+ funding.funding_tx_confirmed_in = Some(*block_hash);
5011+ funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
5012+ Ok(scid) => Some(scid),
5013+ Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
5014+ };
5015+ }
5016+
5017+ is_funding_tx_confirmed = true;
5018+ }
5019+ }
5020+
5021+ Ok(is_funding_tx_confirmed)
5022+ }
5023+
5024+ fn check_for_funding_tx_spent<L: Deref>(
5025+ &mut self, funding: &FundingScope, tx: &Transaction, logger: &L,
5026+ ) -> Result<(), ClosureReason>
5027+ where
5028+ L::Target: Logger
5029+ {
5030+ let funding_txo = match funding.get_funding_txo() {
5031+ Some(funding_txo) => funding_txo,
5032+ None => {
5033+ debug_assert!(false);
5034+ return Ok(());
5035+ },
5036+ };
5037+
5038+ for input in tx.input.iter() {
5039+ if input.previous_output == funding_txo.into_bitcoin_outpoint() {
5040+ log_info!(
5041+ logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}",
5042+ tx.compute_txid(), input.previous_output.txid, input.previous_output.vout,
5043+ &self.channel_id(),
5044+ );
5045+ return Err(ClosureReason::CommitmentTxConfirmed);
5046+ }
5047+ }
5048+
5049+ Ok(())
5050+ }
48905051}
48915052
48925053// Internal utility functions for channels
@@ -5067,6 +5228,16 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
50675228 pending_splice: Option<PendingSplice>,
50685229}
50695230
5231+ #[cfg(splicing)]
5232+ macro_rules! promote_splice_funding {
5233+ ($self: expr, $funding: expr) => {
5234+ core::mem::swap(&mut $self.funding, $funding);
5235+ $self.pending_splice = None;
5236+ $self.pending_funding.clear();
5237+ $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent;
5238+ }
5239+ }
5240+
50705241#[cfg(any(test, fuzzing))]
50715242struct CommitmentTxInfoCached {
50725243 fee: u64,
@@ -8197,75 +8368,80 @@ impl<SP: Deref> FundedChannel<SP> where
81978368 NS::Target: NodeSigner,
81988369 L::Target: Logger
81998370 {
8200- let mut msgs = (None, None);
8201- if let Some(funding_txo) = self.funding.get_funding_txo() {
8202- for &(index_in_block, tx) in txdata.iter() {
8203- // Check if the transaction is the expected funding transaction, and if it is,
8204- // check that it pays the right amount to the right script.
8205- if self.funding.funding_tx_confirmation_height == 0 {
8206- if tx.compute_txid() == funding_txo.txid {
8207- let txo_idx = funding_txo.index as usize;
8208- if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.funding.get_funding_redeemscript().to_p2wsh() ||
8209- tx.output[txo_idx].value.to_sat() != self.funding.get_value_satoshis() {
8210- if self.funding.is_outbound() {
8211- // If we generated the funding transaction and it doesn't match what it
8212- // should, the client is really broken and we should just panic and
8213- // tell them off. That said, because hash collisions happen with high
8214- // probability in fuzzing mode, if we're fuzzing we just close the
8215- // channel and move on.
8216- #[cfg(not(fuzzing))]
8217- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8218- }
8219- self.context.update_time_counter += 1;
8220- let err_reason = "funding tx had wrong script/value or output index";
8221- return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8222- } else {
8223- if self.funding.is_outbound() {
8224- if !tx.is_coinbase() {
8225- for input in tx.input.iter() {
8226- if input.witness.is_empty() {
8227- // We generated a malleable funding transaction, implying we've
8228- // just exposed ourselves to funds loss to our counterparty.
8229- #[cfg(not(fuzzing))]
8230- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8231- }
8232- }
8233- }
8234- }
8371+ for &(index_in_block, tx) in txdata.iter() {
8372+ let mut confirmed_tx = ConfirmedTransaction::from(tx);
8373+
8374+ // If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here
8375+ // and send it immediately instead of waiting for a best_block_updated call (which may have
8376+ // already happened for this block).
8377+ let is_funding_tx_confirmed = self.context.check_for_funding_tx_confirmed(
8378+ &mut self.funding, block_hash, height, index_in_block, &mut confirmed_tx, logger,
8379+ )?;
82358380
8236- // The acceptor of v1-established channels doesn't have the funding
8237- // transaction until it is seen on chain. Set it so that minimum_depth
8238- // checks can tell if the coinbase transaction was used.
8239- if self.funding.funding_transaction.is_none() {
8240- self.funding.funding_transaction = Some(tx.clone());
8241- }
8381+ if is_funding_tx_confirmed {
8382+ for &(_, tx) in txdata.iter() {
8383+ self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?;
8384+ }
82428385
8243- self.funding.funding_tx_confirmation_height = height;
8244- self.funding.funding_tx_confirmed_in = Some(*block_hash);
8245- self.funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
8246- Ok(scid) => Some(scid),
8247- Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
8248- }
8249- }
8250- }
8251- // If we allow 1-conf funding, we may need to check for channel_ready here and
8252- // send it immediately instead of waiting for a best_block_updated call (which
8253- // may have already happened for this block).
8254- if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8255- log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8256- let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8257- msgs = (Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs);
8386+ if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8387+ log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8388+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8389+ return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
8390+ }
8391+ }
8392+
8393+ #[cfg(splicing)]
8394+ let mut confirmed_funding = None;
8395+ #[cfg(splicing)]
8396+ for funding in self.pending_funding.iter_mut() {
8397+ if self.context.check_for_funding_tx_confirmed(
8398+ funding, block_hash, height, index_in_block, &mut confirmed_tx, logger,
8399+ )? {
8400+ if confirmed_funding.is_some() {
8401+ let err_reason = "splice tx of another pending funding already confirmed";
8402+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
82588403 }
8404+
8405+ confirmed_funding = Some(funding);
8406+ }
8407+ }
8408+
8409+ #[cfg(splicing)]
8410+ if let Some(funding) = confirmed_funding {
8411+ let pending_splice = match self.pending_splice.as_mut() {
8412+ Some(pending_splice) => pending_splice,
8413+ None => {
8414+ // TODO: Move pending_funding into pending_splice?
8415+ debug_assert!(false);
8416+ // TODO: Error instead?
8417+ return Ok((None, None));
8418+ },
8419+ };
8420+
8421+ for &(_, tx) in txdata.iter() {
8422+ self.context.check_for_funding_tx_spent(funding, tx, logger)?;
82598423 }
8260- for inp in tx.input.iter() {
8261- if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
8262- 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());
8263- return Err(ClosureReason::CommitmentTxConfirmed);
8424+
8425+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8426+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8427+
8428+ pending_splice.sent_funding_txid = Some(splice_locked.splice_txid);
8429+ if pending_splice.sent_funding_txid == pending_splice.received_funding_txid {
8430+ promote_splice_funding!(self, funding);
8431+
8432+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8433+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
82648434 }
8435+
8436+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), None));
82658437 }
82668438 }
8439+
8440+ self.context.check_for_funding_tx_spent(&self.funding, tx, logger)?;
8441+
82678442 }
8268- Ok(msgs)
8443+
8444+ Ok((None, None))
82698445 }
82708446
82718447 /// When a new block is connected, we check the height of the block against outbound holding
@@ -8358,6 +8534,49 @@ impl<SP: Deref> FundedChannel<SP> where
83588534 return Err(ClosureReason::FundingTimedOut);
83598535 }
83608536
8537+ #[cfg(splicing)]
8538+ let mut confirmed_funding = None;
8539+ #[cfg(splicing)]
8540+ for funding in self.pending_funding.iter_mut() {
8541+ if confirmed_funding.is_some() {
8542+ let err_reason = "splice tx of another pending funding already confirmed";
8543+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8544+ }
8545+
8546+ confirmed_funding = Some(funding);
8547+ }
8548+
8549+ #[cfg(splicing)]
8550+ if let Some(funding) = confirmed_funding {
8551+ let pending_splice = match self.pending_splice.as_mut() {
8552+ Some(pending_splice) => pending_splice,
8553+ None => {
8554+ // TODO: Move pending_funding into pending_splice?
8555+ debug_assert!(false);
8556+ // TODO: Error instead?
8557+ return Ok((None, timed_out_htlcs, None));
8558+ },
8559+ };
8560+
8561+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8562+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8563+
8564+ pending_splice.sent_funding_txid = Some(splice_locked.splice_txid);
8565+ if pending_splice.sent_funding_txid == pending_splice.received_funding_txid {
8566+ promote_splice_funding!(self, funding);
8567+
8568+ let mut announcement_sigs = None;
8569+ if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
8570+ announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8571+ }
8572+
8573+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), timed_out_htlcs, announcement_sigs));
8574+ }
8575+
8576+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), timed_out_htlcs, None));
8577+ }
8578+ }
8579+
83618580 let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
83628581 self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
83638582 } else { None };
@@ -8689,6 +8908,8 @@ impl<SP: Deref> FundedChannel<SP> where
86898908
86908909 self.pending_splice = Some(PendingSplice {
86918910 our_funding_contribution: our_funding_contribution_satoshis,
8911+ sent_funding_txid: None,
8912+ received_funding_txid: None,
86928913 });
86938914
86948915 let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime);
0 commit comments