@@ -1864,6 +1864,8 @@ impl FundingScope {
1864
1864
#[cfg(splicing)]
1865
1865
struct PendingSplice {
1866
1866
pub our_funding_contribution: i64,
1867
+ sent_funding_txid: Option<Txid>,
1868
+ received_funding_txid: Option<Txid>,
1867
1869
}
1868
1870
1869
1871
/// Contains everything about the channel including state, and various flags.
@@ -4865,6 +4867,40 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4865
4867
self.get_initial_counterparty_commitment_signature(funding, logger)
4866
4868
}
4867
4869
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
+
4868
4904
fn check_funding_confirmations(&self, funding: &mut FundingScope, height: u32) -> bool {
4869
4905
if funding.funding_tx_confirmation_height == 0 && funding.minimum_depth != Some(0) {
4870
4906
return false;
@@ -4881,6 +4917,78 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
4881
4917
4882
4918
return true;
4883
4919
}
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
+ }
4884
4992
}
4885
4993
4886
4994
// Internal utility functions for channels
@@ -5061,6 +5169,16 @@ pub(super) struct FundedChannel<SP: Deref> where SP::Target: SignerProvider {
5061
5169
pending_splice: Option<PendingSplice>,
5062
5170
}
5063
5171
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
+
5064
5182
#[cfg(any(test, fuzzing))]
5065
5183
struct CommitmentTxInfoCached {
5066
5184
fee: u64,
@@ -8191,75 +8309,69 @@ impl<SP: Deref> FundedChannel<SP> where
8191
8309
NS::Target: NodeSigner,
8192
8310
L::Target: Logger
8193
8311
{
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
+ )?;
8229
8318
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() });
8253
8343
}
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);
8259
8368
}
8369
+
8370
+ return Ok((Some(FundingConfirmedMessage::Splice(splice_locked)), announcement_sigs));
8260
8371
}
8261
8372
}
8262
- Ok(msgs)
8373
+
8374
+ Ok((None, None))
8263
8375
}
8264
8376
8265
8377
/// 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
8352
8464
return Err(ClosureReason::FundingTimedOut);
8353
8465
}
8354
8466
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
+
8355
8504
let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
8356
8505
self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
8357
8506
} else { None };
@@ -8683,6 +8832,8 @@ impl<SP: Deref> FundedChannel<SP> where
8683
8832
8684
8833
self.pending_splice = Some(PendingSplice {
8685
8834
our_funding_contribution: our_funding_contribution_satoshis,
8835
+ sent_funding_txid: None,
8836
+ received_funding_txid: None,
8686
8837
});
8687
8838
8688
8839
let msg = self.get_splice_init(our_funding_contribution_satoshis, funding_feerate_per_kw, locktime);
0 commit comments