]> git.bitcoin.ninja Git - rust-lightning/commitdiff
demo user passing outpoint 2024-06-3024-outpoint
authorMatt Corallo <git@bluematt.me>
Mon, 17 Jun 2024 17:50:32 +0000 (17:50 +0000)
committerMatt Corallo <git@bluematt.me>
Mon, 17 Jun 2024 17:50:32 +0000 (17:50 +0000)
lightning/src/ln/channel.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs

index ac316d450d69b54e9cffb534e87ae62cb4d4b837..3c9b361c96cd7a5feb097c4fbb7a81635be677fb 100644 (file)
@@ -1325,6 +1325,8 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
        counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
 
        pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
+       /// The transaction which funds this channel. Note that for manually-funded channels this may
+       /// be a dummy empty transaction.
        funding_transaction: Option<Transaction>,
        is_batch_funding: Option<()>,
 
index 36b17cead0d31818145a5c68c8af318aa99c7164..4641942fa76b6640af461fc66e82bf42ee55506a 100644 (file)
@@ -698,6 +698,32 @@ struct ClaimablePayment {
        htlcs: Vec<ClaimableHTLC>,
 }
 
+enum FundingType {
+       Unchecked(OutPoint),
+       Checked(Transaction),
+}
+
+impl FundingType {
+       fn txid(&self) -> Txid {
+               match self {
+                       FundingType::Unchecked(outp) => outp.txid,
+                       FundingType::Checked(tx) => tx.txid(),
+               }
+       }
+
+       fn transaction_or_dummy(&self) -> Transaction {
+               match self {
+                       FundingType::Unchecked(_) => Transaction {
+                               version: bitcoin::transaction::Version::TWO,
+                               lock_time: bitcoin::absolute::LockTime::ZERO,
+                               input: Vec::new(),
+                               output: Vec::new(),
+                       },
+                       FundingType::Checked(tx) => tx.clone(),
+               }
+       }
+}
+
 /// Information about claimable or being-claimed payments
 struct ClaimablePayments {
        /// Map from payment hash to the payment data and any HTLCs which are to us and can be
@@ -4200,7 +4226,7 @@ where
 
        /// Handles the generation of a funding transaction, optionally (for tests) with a function
        /// which checks the correctness of the funding transaction given the associated channel.
-       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, &'static str>>(
+       fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>) -> Result<OutPoint, &'static str>>(
                &self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
                mut find_funding_output: FundingOutput, is_manual_broadcast: bool,
        ) -> Result<(), APIError> {
@@ -4228,7 +4254,7 @@ where
                                        let _: Result<(), _> = handle_error!(self, Err(err), counterparty);
                                        Err($api_err)
                                } } }
-                               match find_funding_output(&chan, &funding_transaction) {
+                               match find_funding_output(&chan) {
                                        Ok(found_funding_txo) => funding_txo = found_funding_txo,
                                        Err(err) => {
                                                let chan_err = ChannelError::Close(err.to_owned());
@@ -4299,8 +4325,9 @@ where
 
        #[cfg(test)]
        pub(crate) fn funding_transaction_generated_unchecked(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, output_index: u16) -> Result<(), APIError> {
-               self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_, tx| {
-                       Ok(OutPoint { txid: tx.txid(), index: output_index })
+               let txid = funding_transaction.txid();
+               self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_| {
+                       Ok(OutPoint { txid, index: output_index })
                }, false)
        }
 
@@ -4370,11 +4397,11 @@ where
        /// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
        /// [`Event::ChannelClosed`]: crate::events::Event::ChannelClosed
        /// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
-       pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction) -> Result<(), APIError> {
+       pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding: OutPoint) -> Result<(), APIError> {
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
 
                let temporary_channels = &[(temporary_channel_id, counterparty_node_id)];
-               return self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, true);
+               return self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Unchecked(funding));
 
        }
 
@@ -4401,33 +4428,35 @@ where
                                }
                        }
                }
-               result.and(self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, false))
+               result.and(self.batch_funding_transaction_generated_intern(temporary_channels, FundingType::Checked(funding_transaction)))
        }
 
-       fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding_transaction: Transaction, is_manual_broadcast: bool) -> Result<(), APIError> {
+       fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding: FundingType) -> Result<(), APIError> {
                let mut result = Ok(());
-               if funding_transaction.output.len() > u16::max_value() as usize {
-                       result = result.and(Err(APIError::APIMisuseError {
-                               err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
-                       }));
-               }
-               {
-                       let height = self.best_block.read().unwrap().height;
-                       // Transactions are evaluated as final by network mempools if their locktime is strictly
-                       // lower than the next block height. However, the modules constituting our Lightning
-                       // node might not have perfect sync about their blockchain views. Thus, if the wallet
-                       // module is ahead of LDK, only allow one more block of headroom.
-                       if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) &&
-                               funding_transaction.lock_time.is_block_height() &&
-                               funding_transaction.lock_time.to_consensus_u32() > height + 1
-                       {
+               if let FundingType::Checked(funding_transaction) = &funding {
+                       if funding_transaction.output.len() > u16::max_value() as usize {
                                result = result.and(Err(APIError::APIMisuseError {
-                                       err: "Funding transaction absolute timelock is non-final".to_owned()
+                                       err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
                                }));
                        }
+                       {
+                               let height = self.best_block.read().unwrap().height;
+                               // Transactions are evaluated as final by network mempools if their locktime is strictly
+                               // lower than the next block height. However, the modules constituting our Lightning
+                               // node might not have perfect sync about their blockchain views. Thus, if the wallet
+                               // module is ahead of LDK, only allow one more block of headroom.
+                               if !funding_transaction.input.iter().all(|input| input.sequence == Sequence::MAX) &&
+                                       funding_transaction.lock_time.is_block_height() &&
+                                       funding_transaction.lock_time.to_consensus_u32() > height + 1
+                               {
+                                       result = result.and(Err(APIError::APIMisuseError {
+                                               err: "Funding transaction absolute timelock is non-final".to_owned()
+                                       }));
+                               }
+                       }
                }
 
-               let txid = funding_transaction.txid();
+               let txid = funding.txid();
                let is_batch_funding = temporary_channels.len() > 1;
                let mut funding_batch_states = if is_batch_funding {
                        Some(self.funding_batch_states.lock().unwrap())
@@ -4445,27 +4474,36 @@ where
                                btree_map::Entry::Vacant(vacant) => Some(vacant.insert(Vec::new())),
                        }
                });
+               let is_manual_broadcast = match &funding {
+                       FundingType::Checked(_) => false,
+                       FundingType::Unchecked(_) => true,
+               };
                for &(temporary_channel_id, counterparty_node_id) in temporary_channels {
                        result = result.and_then(|_| self.funding_transaction_generated_intern(
                                temporary_channel_id,
                                counterparty_node_id,
-                               funding_transaction.clone(),
+                               funding.transaction_or_dummy(),
                                is_batch_funding,
-                               |chan, tx| {
+                               |chan| {
                                        let mut output_index = None;
                                        let expected_spk = chan.context.get_funding_redeemscript().to_p2wsh();
-                                       for (idx, outp) in tx.output.iter().enumerate() {
-                                               if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
-                                                       if output_index.is_some() {
-                                                               return Err("Multiple outputs matched the expected script and value");
+                                       let outpoint = match &funding {
+                                               FundingType::Checked(tx) => {
+                                                       for (idx, outp) in tx.output.iter().enumerate() {
+                                                               if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
+                                                                       if output_index.is_some() {
+                                                                               return Err("Multiple outputs matched the expected script and value");
+                                                                       }
+                                                                       output_index = Some(idx as u16);
+                                                               }
                                                        }
-                                                       output_index = Some(idx as u16);
-                                               }
-                                       }
-                                       if output_index.is_none() {
-                                               return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
-                                       }
-                                       let outpoint = OutPoint { txid: tx.txid(), index: output_index.unwrap() };
+                                                       if output_index.is_none() {
+                                                               return Err("No output matched the script_pubkey and value in the FundingGenerationReady event");
+                                                       }
+                                                       OutPoint { txid, index: output_index.unwrap() }
+                                               },
+                                               FundingType::Unchecked(outpoint) => outpoint.clone(),
+                                       };
                                        if let Some(funding_batch_state) = funding_batch_state.as_mut() {
                                                // TODO(dual_funding): We only do batch funding for V1 channels at the moment, but we'll probably
                                                // need to fix this somehow to not rely on using the outpoint for the channel ID if we
index 9b1bf83bf511e773f61b6fe22f7dbf4d9e7cde1f..69a59520b18e8acd46208d82c20e11df7d10150e 100644 (file)
@@ -3796,10 +3796,10 @@ fn test_unsafe_manual_funding_transaction_generated() {
        let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
        nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
 
-       let (temporary_channel_id, tx, _funding_output) = create_funding_tx_without_witness_data(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+       let (temporary_channel_id, _, funding_outpoint) = create_funding_tx_without_witness_data(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
        assert_eq!(temporary_channel_id, expected_temporary_channel_id);
 
-       assert!(nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).is_ok());
+       assert!(nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), funding_outpoint).is_ok());
        let node_0_msg_events = nodes[0].node.get_and_clear_pending_msg_events();
        match node_0_msg_events[0] {
                MessageSendEvent::SendFundingCreated { ref node_id, .. } => {
@@ -11242,8 +11242,8 @@ fn test_funding_signed_event() {
        let accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
 
        nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_channel);
-       let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
-       nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
+       let (temporary_channel_id, tx, funding_outpoint) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100_000, 42);
+       nodes[0].node.unsafe_manual_funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), funding_outpoint).unwrap();
        check_added_monitors!(nodes[0], 0);
 
        let funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());