@@ -1857,6 +1857,8 @@ impl FundingScope {
1857
1857
#[cfg(splicing)]
1858
1858
struct PendingSplice {
1859
1859
pub our_funding_contribution: i64,
1860
+ sent_funding_txid: Option<Txid>,
1861
+ received_funding_txid: Option<Txid>,
1860
1862
}
1861
1863
1862
1864
/// Contains everything about the channel including state, and various flags.
@@ -4859,6 +4861,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4859
4861
self.get_initial_counterparty_commitment_signature(funding, logger)
4860
4862
}
4861
4863
4864
+ #[cfg(splicing)]
4865
+ fn check_get_splice_locked<L: Deref>(
4866
+ &mut self, pending_splice: &PendingSplice, funding: &mut FundingScope, height: u32,
4867
+ logger: &L,
4868
+ ) -> Option<msgs::SpliceLocked>
4869
+ where
4870
+ L::Target: Logger,
4871
+ {
4872
+ if !self.check_funding_confirmations(funding, height) {
4873
+ return None;
4874
+ }
4875
+
4876
+ let confirmed_funding_txid = match funding.get_funding_txo().map(|txo| txo.txid) {
4877
+ Some(funding_txid) => funding_txid,
4878
+ None => {
4879
+ debug_assert!(false);
4880
+ return None;
4881
+ },
4882
+ };
4883
+
4884
+ match pending_splice.sent_funding_txid {
4885
+ Some(sent_funding_txid) if confirmed_funding_txid == sent_funding_txid => {
4886
+ debug_assert!(false);
4887
+ None
4888
+ },
4889
+ _ => {
4890
+ Some(msgs::SpliceLocked {
4891
+ channel_id: self.channel_id(),
4892
+ splice_txid: confirmed_funding_txid,
4893
+ })
4894
+ },
4895
+ }
4896
+ }
4897
+
4862
4898
fn check_funding_confirmations(&self, funding: &mut FundingScope, height: u32) -> bool {
4863
4899
let is_coinbase = funding
4864
4900
.funding_transaction
@@ -4892,6 +4928,85 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4892
4928
4893
4929
return true;
4894
4930
}
4931
+
4932
+ fn check_for_funding_tx<'a, L: Deref>(
4933
+ &mut self, funding: &mut FundingScope, block_hash: &BlockHash, height: u32,
4934
+ txdata: &'a TransactionData, logger: &L,
4935
+ ) -> Result<Option<&'a Transaction>, ClosureReason>
4936
+ where
4937
+ L::Target: Logger
4938
+ {
4939
+ let funding_txo = match funding.get_funding_txo() {
4940
+ Some(funding_txo) => funding_txo,
4941
+ None => {
4942
+ debug_assert!(false);
4943
+ return Ok(None);
4944
+ },
4945
+ };
4946
+
4947
+ let mut confirmed_funding_tx = None;
4948
+ for &(index_in_block, tx) in txdata.iter() {
4949
+ // Check if the transaction is the expected funding transaction, and if it is,
4950
+ // check that it pays the right amount to the right script.
4951
+ if funding.funding_tx_confirmation_height == 0 {
4952
+ if tx.compute_txid() == funding_txo.txid {
4953
+ let txo_idx = funding_txo.index as usize;
4954
+ if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != funding.get_funding_redeemscript().to_p2wsh() ||
4955
+ tx.output[txo_idx].value.to_sat() != funding.get_value_satoshis() {
4956
+ if funding.is_outbound() {
4957
+ // If we generated the funding transaction and it doesn't match what it
4958
+ // should, the client is really broken and we should just panic and
4959
+ // tell them off. That said, because hash collisions happen with high
4960
+ // probability in fuzzing mode, if we're fuzzing we just close the
4961
+ // channel and move on.
4962
+ #[cfg(not(fuzzing))]
4963
+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4964
+ }
4965
+ self.update_time_counter += 1;
4966
+ let err_reason = "funding tx had wrong script/value or output index";
4967
+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
4968
+ } else {
4969
+ if funding.is_outbound() {
4970
+ if !tx.is_coinbase() {
4971
+ for input in tx.input.iter() {
4972
+ if input.witness.is_empty() {
4973
+ // We generated a malleable funding transaction, implying we've
4974
+ // just exposed ourselves to funds loss to our counterparty.
4975
+ #[cfg(not(fuzzing))]
4976
+ panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
4977
+ }
4978
+ }
4979
+ }
4980
+ }
4981
+
4982
+ // The acceptor of v1-established channels doesn't have the funding
4983
+ // transaction until it is seen on chain. Set it so that minimum_depth
4984
+ // checks can tell if the coinbase transaction was used.
4985
+ if funding.funding_transaction.is_none() {
4986
+ funding.funding_transaction = Some(tx.clone());
4987
+ }
4988
+
4989
+ funding.funding_tx_confirmation_height = height;
4990
+ funding.funding_tx_confirmed_in = Some(*block_hash);
4991
+ funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
4992
+ Ok(scid) => Some(scid),
4993
+ Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
4994
+ };
4995
+ }
4996
+
4997
+ confirmed_funding_tx = Some(tx);
4998
+ }
4999
+ }
5000
+ for inp in tx.input.iter() {
5001
+ if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
5002
+ log_info!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.compute_txid(), inp.previous_output.txid, inp.previous_output.vout, &self.channel_id());
5003
+ return Err(ClosureReason::CommitmentTxConfirmed);
5004
+ }
5005
+ }
5006
+ }
5007
+
5008
+ Ok(confirmed_funding_tx)
5009
+ }
4895
5010
}
4896
5011
4897
5012
// Internal utility functions for channels
@@ -5072,6 +5187,16 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
5072
5187
pending_splice: Option<PendingSplice>,
5073
5188
}
5074
5189
5190
+ #[cfg(splicing)]
5191
+ macro_rules! promote_splice_funding {
5192
+ ($self: expr, $funding: expr) => {
5193
+ core::mem::swap(&mut $self.funding, $funding);
5194
+ $self.pending_splice = None;
5195
+ $self.pending_funding.clear();
5196
+ $self.context.announcement_sigs_state = AnnouncementSigsState::NotSent;
5197
+ }
5198
+ }
5199
+
5075
5200
#[cfg(any(test, fuzzing))]
5076
5201
struct CommitmentTxInfoCached {
5077
5202
fee: u64,
@@ -8202,75 +8327,61 @@ impl<SP: Deref> FundedChannel<SP> where
8202
8327
NS::Target: NodeSigner,
8203
8328
L::Target: Logger
8204
8329
{
8205
- let mut msgs = (None, None);
8206
- if let Some(funding_txo) = self.funding.get_funding_txo() {
8207
- for &(index_in_block, tx) in txdata.iter() {
8208
- // Check if the transaction is the expected funding transaction, and if it is,
8209
- // check that it pays the right amount to the right script.
8210
- if self.funding.funding_tx_confirmation_height == 0 {
8211
- if tx.compute_txid() == funding_txo.txid {
8212
- let txo_idx = funding_txo.index as usize;
8213
- if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.funding.get_funding_redeemscript().to_p2wsh() ||
8214
- tx.output[txo_idx].value.to_sat() != self.funding.get_value_satoshis() {
8215
- if self.funding.is_outbound() {
8216
- // If we generated the funding transaction and it doesn't match what it
8217
- // should, the client is really broken and we should just panic and
8218
- // tell them off. That said, because hash collisions happen with high
8219
- // probability in fuzzing mode, if we're fuzzing we just close the
8220
- // channel and move on.
8221
- #[cfg(not(fuzzing))]
8222
- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8223
- }
8224
- self.context.update_time_counter += 1;
8225
- let err_reason = "funding tx had wrong script/value or output index";
8226
- return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8227
- } else {
8228
- if self.funding.is_outbound() {
8229
- if !tx.is_coinbase() {
8230
- for input in tx.input.iter() {
8231
- if input.witness.is_empty() {
8232
- // We generated a malleable funding transaction, implying we've
8233
- // just exposed ourselves to funds loss to our counterparty.
8234
- #[cfg(not(fuzzing))]
8235
- panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
8236
- }
8237
- }
8238
- }
8239
- }
8330
+ // If we allow 1-conf funding, we may need to check for channel_ready or splice_locked here
8331
+ // and send it immediately instead of waiting for a best_block_updated call (which may have
8332
+ // already happened for this block).
8333
+ let confirmed_funding_tx = self.context.check_for_funding_tx(
8334
+ &mut self.funding, block_hash, height, txdata, logger,
8335
+ )?;
8240
8336
8241
- // The acceptor of v1-established channels doesn't have the funding
8242
- // transaction until it is seen on chain. Set it so that minimum_depth
8243
- // checks can tell if the coinbase transaction was used.
8244
- if self.funding.funding_transaction.is_none() {
8245
- self.funding.funding_transaction = Some(tx.clone());
8246
- }
8337
+ if let Some(funding_tx) = confirmed_funding_tx {
8338
+ if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8339
+ log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8340
+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8341
+ return Ok((Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs));
8342
+ }
8343
+ }
8247
8344
8248
- self.funding.funding_tx_confirmation_height = height;
8249
- self.funding.funding_tx_confirmed_in = Some(*block_hash);
8250
- self.funding.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
8251
- Ok(scid) => Some(scid),
8252
- Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
8253
- }
8254
- }
8255
- }
8256
- // If we allow 1-conf funding, we may need to check for channel_ready here and
8257
- // send it immediately instead of waiting for a best_block_updated call (which
8258
- // may have already happened for this block).
8259
- if let Some(channel_ready) = self.check_get_channel_ready(height, logger) {
8260
- log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
8261
- let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8262
- msgs = (Some(FundingConfirmedMessage::Establishment(channel_ready)), announcement_sigs);
8263
- }
8345
+ #[cfg(splicing)]
8346
+ let mut confirmed_funding = None;
8347
+ #[cfg(splicing)]
8348
+ for funding in self.pending_funding.iter_mut() {
8349
+ if self.context.check_for_funding_tx(funding, block_hash, height, txdata, logger)?.is_some() {
8350
+ if confirmed_funding.is_some() {
8351
+ let err_reason = "splice tx of another pending funding already confirmed";
8352
+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8264
8353
}
8265
- for inp in tx.input.iter() {
8266
- if inp.previous_output == funding_txo.into_bitcoin_outpoint() {
8267
- 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());
8268
- return Err(ClosureReason::CommitmentTxConfirmed);
8269
- }
8354
+
8355
+ confirmed_funding = Some(funding);
8356
+ }
8357
+ }
8358
+
8359
+ #[cfg(splicing)]
8360
+ if let Some(funding) = confirmed_funding {
8361
+ let pending_splice = match self.pending_splice.as_mut() {
8362
+ Some(pending_splice) => pending_splice,
8363
+ None => {
8364
+ // TODO: Move pending_funding into pending_splice?
8365
+ debug_assert!(false);
8366
+ // TODO: Error instead?
8367
+ return Ok((None, None));
8368
+ },
8369
+ };
8370
+
8371
+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8372
+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8373
+
8374
+ let mut announcement_sigs = None;
8375
+ if Some(splice_locked.splice_txid) == pending_splice.received_funding_txid {
8376
+ promote_splice_funding!(self, funding);
8377
+ announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8270
8378
}
8379
+
8380
+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
8271
8381
}
8272
8382
}
8273
- Ok(msgs)
8383
+
8384
+ Ok((None, None))
8274
8385
}
8275
8386
8276
8387
/// When a new block is connected, we check the height of the block against outbound holding
@@ -8363,6 +8474,43 @@ impl<SP: Deref> FundedChannel<SP> where
8363
8474
return Err(ClosureReason::FundingTimedOut);
8364
8475
}
8365
8476
8477
+ #[cfg(splicing)]
8478
+ let mut confirmed_funding = None;
8479
+ #[cfg(splicing)]
8480
+ for funding in self.pending_funding.iter_mut() {
8481
+ if confirmed_funding.is_some() {
8482
+ let err_reason = "splice tx of another pending funding already confirmed";
8483
+ return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() });
8484
+ }
8485
+
8486
+ confirmed_funding = Some(funding);
8487
+ }
8488
+
8489
+ #[cfg(splicing)]
8490
+ if let Some(funding) = confirmed_funding {
8491
+ let pending_splice = match self.pending_splice.as_mut() {
8492
+ Some(pending_splice) => pending_splice,
8493
+ None => {
8494
+ // TODO: Move pending_funding into pending_splice?
8495
+ debug_assert!(false);
8496
+ // TODO: Error instead?
8497
+ return Ok((None, timed_out_htlcs, None));
8498
+ },
8499
+ };
8500
+
8501
+ if let Some(splice_locked) = self.context.check_get_splice_locked(pending_splice, funding, height, logger) {
8502
+ let mut announcement_sigs = None;
8503
+ if Some(splice_locked.splice_txid) == pending_splice.received_funding_txid {
8504
+ promote_splice_funding!(self, funding);
8505
+ if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
8506
+ announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
8507
+ }
8508
+ }
8509
+ log_info!(logger, "Sending a splice_locked to our peer for channel {}", &self.context.channel_id);
8510
+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), timed_out_htlcs, announcement_sigs));
8511
+ }
8512
+ }
8513
+
8366
8514
let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
8367
8515
self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
8368
8516
} else { None };
@@ -8694,6 +8842,8 @@ impl<SP: Deref> FundedChannel<SP> where
8694
8842
8695
8843
self.pending_splice = Some(PendingSplice {
8696
8844
our_funding_contribution: our_funding_contribution_satoshis,
8845
+ sent_funding_txid: None,
8846
+ received_funding_txid: None,
8697
8847
});
8698
8848
8699
8849
let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime);
0 commit comments