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