Merge pull request #1956 from TheBlueMatt/2023-01-ser-cleanups
[rust-lightning] / lightning / src / chain / onchaintx.rs
index 8dcfea347040abf5a065217378ea2fcfbabc48c7..f526cc8aaa4aa85b2b720dd5f8de3b74c00ae06e 100644 (file)
@@ -21,17 +21,17 @@ use bitcoin::hash_types::{Txid, BlockHash};
 use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
 use bitcoin::secp256k1;
 
-use crate::chain::keysinterface::BaseSign;
+use crate::chain::keysinterface::{BaseSign, EntropySource, SignerProvider};
 use crate::ln::msgs::DecodeError;
 use crate::ln::PaymentPreimage;
 #[cfg(anchors)]
-use crate::ln::chan_utils;
+use crate::ln::chan_utils::{self, HTLCOutputInCommitment};
 use crate::ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
 #[cfg(anchors)]
 use crate::chain::chaininterface::ConfirmationTarget;
 use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
 use crate::chain::channelmonitor::{ANTI_REORG_DELAY, CLTV_SHARED_CLAIM_BUFFER};
-use crate::chain::keysinterface::{Sign, KeysInterface};
+use crate::chain::keysinterface::Sign;
 #[cfg(anchors)]
 use crate::chain::package::PackageSolvingData;
 use crate::chain::package::PackageTemplate;
@@ -79,7 +79,7 @@ enum OnchainEvent {
        /// Outpoint under claim process by our own tx, once this one get enough confirmations, we remove it from
        /// bump-txn candidate buffer.
        Claim {
-               claim_request: Txid,
+               package_id: PackageID,
        },
        /// Claim tx aggregate multiple claimable outpoints. One of the outpoint may be claimed by a counterparty party tx.
        /// In this case, we need to drop the outpoint and regenerate a new claim tx. By safety, we keep tracking
@@ -123,7 +123,7 @@ impl MaybeReadable for OnchainEventEntry {
 
 impl_writeable_tlv_based_enum_upgradable!(OnchainEvent,
        (0, Claim) => {
-               (0, claim_request, required),
+               (0, package_id, required),
        },
        (1, ContentiousOutpoint) => {
                (0, package, required),
@@ -174,6 +174,16 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
        }
 }
 
+#[cfg(anchors)]
+/// The claim commonly referred to as the pre-signed second-stage HTLC transaction.
+pub(crate) struct ExternalHTLCClaim {
+       pub(crate) commitment_txid: Txid,
+       pub(crate) per_commitment_number: u64,
+       pub(crate) htlc: HTLCOutputInCommitment,
+       pub(crate) preimage: Option<PaymentPreimage>,
+       pub(crate) counterparty_sig: Signature,
+}
+
 // Represents the different types of claims for which events are yielded externally to satisfy said
 // claims.
 #[cfg(anchors)]
@@ -185,6 +195,12 @@ pub(crate) enum ClaimEvent {
                commitment_tx: Transaction,
                anchor_output_idx: u32,
        },
+       /// Event yielded to signal that the commitment transaction has confirmed and its HTLCs must be
+       /// resolved by broadcasting a transaction with sufficient fee to claim them.
+       BumpHTLC {
+               target_feerate_sat_per_1000_weight: u32,
+               htlcs: Vec<ExternalHTLCClaim>,
+       },
 }
 
 /// Represents the different ways an output can be claimed (i.e., spent to an address under our
@@ -306,11 +322,12 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
        }
 }
 
-impl<'a, K: KeysInterface> ReadableArgs<(&'a K, u64, [u8; 32])> for OnchainTxHandler<K::Signer> {
-       fn read<R: io::Read>(reader: &mut R, args: (&'a K, u64, [u8; 32])) -> Result<Self, DecodeError> {
-               let keys_manager = args.0;
-               let channel_value_satoshis = args.1;
-               let channel_keys_id = args.2;
+impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP, u64, [u8; 32])> for OnchainTxHandler<SP::Signer> {
+       fn read<R: io::Read>(reader: &mut R, args: (&'a ES, &'b SP, u64, [u8; 32])) -> Result<Self, DecodeError> {
+               let entropy_source = args.0;
+               let signer_provider = args.1;
+               let channel_value_satoshis = args.2;
+               let channel_keys_id = args.3;
 
                let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
 
@@ -336,7 +353,7 @@ impl<'a, K: KeysInterface> ReadableArgs<(&'a K, u64, [u8; 32])> for OnchainTxHan
                        bytes_read += bytes_to_read;
                }
 
-               let mut signer = keys_manager.derive_channel_signer(channel_value_satoshis, channel_keys_id);
+               let mut signer = signer_provider.derive_channel_signer(channel_value_satoshis, channel_keys_id);
                signer.provide_channel_parameters(&channel_parameters);
 
                let pending_claim_requests_len: u64 = Readable::read(reader)?;
@@ -377,7 +394,7 @@ impl<'a, K: KeysInterface> ReadableArgs<(&'a K, u64, [u8; 32])> for OnchainTxHan
                read_tlv_fields!(reader, {});
 
                let mut secp_ctx = Secp256k1::new();
-               secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
+               secp_ctx.seeded_randomize(&entropy_source.get_secure_random_bytes());
 
                Ok(OnchainTxHandler {
                        destination_script,
@@ -459,13 +476,13 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                // remove it once it reaches the confirmation threshold, or to generate a new claim if the
                // transaction is reorged out.
                let mut all_inputs_have_confirmed_spend = true;
-               for outpoint in &request_outpoints {
-                       if let Some(first_claim_txid_height) = self.claimable_outpoints.get(outpoint) {
+               for outpoint in request_outpoints.iter() {
+                       if let Some(first_claim_txid_height) = self.claimable_outpoints.get(*outpoint) {
                                // We check for outpoint spends within claims individually rather than as a set
                                // since requests can have outpoints split off.
                                if !self.onchain_events_awaiting_threshold_conf.iter()
-                                       .any(|event_entry| if let OnchainEvent::Claim { claim_request } = event_entry.event {
-                                               first_claim_txid_height.0 == claim_request.into_inner()
+                                       .any(|event_entry| if let OnchainEvent::Claim { package_id } = event_entry.event {
+                                               first_claim_txid_height.0 == package_id
                                        } else {
                                                // The onchain event is not a claim, keep seeking until we find one.
                                                false
@@ -488,15 +505,36 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                // didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
                let new_timer = Some(cached_request.get_height_timer(cur_height));
                if cached_request.is_malleable() {
+                       #[cfg(anchors)]
+                       { // Attributes are not allowed on if expressions on our current MSRV of 1.41.
+                               if cached_request.requires_external_funding() {
+                                       let target_feerate_sat_per_1000_weight = cached_request
+                                               .compute_package_feerate(fee_estimator, ConfirmationTarget::HighPriority);
+                                       if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
+                                               return Some((
+                                                       new_timer,
+                                                       target_feerate_sat_per_1000_weight as u64,
+                                                       OnchainClaim::Event(ClaimEvent::BumpHTLC {
+                                                               target_feerate_sat_per_1000_weight,
+                                                               htlcs,
+                                                       }),
+                                               ));
+                                       } else {
+                                               return None;
+                                       }
+                               }
+                       }
+
                        let predicted_weight = cached_request.package_weight(&self.destination_script);
-                       if let Some((output_value, new_feerate)) =
-                                       cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger) {
+                       if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
+                               predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger,
+                       ) {
                                assert!(new_feerate != 0);
 
                                let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
                                log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
                                assert!(predicted_weight >= transaction.weight());
-                               return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
+                               return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
                        }
                } else {
                        // Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
@@ -552,7 +590,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                        debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
                                        None
                                },
-                       });
+                       })
                }
                None
        }
@@ -640,10 +678,20 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                        #[cfg(anchors)]
                                        OnchainClaim::Event(claim_event) => {
                                                log_info!(logger, "Yielding onchain event to spend inputs {:?}", req.outpoints());
-                                               let txid = match claim_event {
-                                                       ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
+                                               let package_id = match claim_event {
+                                                       ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid().into_inner(),
+                                                       ClaimEvent::BumpHTLC { ref htlcs, .. } => {
+                                                               // Use the same construction as a lightning channel id to generate
+                                                               // the package id for this request based on the first HTLC. It
+                                                               // doesn't matter what we use as long as it's unique per request.
+                                                               let mut package_id = [0; 32];
+                                                               package_id[..].copy_from_slice(&htlcs[0].commitment_txid[..]);
+                                                               let htlc_output_index = htlcs[0].htlc.transaction_output_index.unwrap();
+                                                               package_id[30] ^= ((htlc_output_index >> 8) & 0xff) as u8;
+                                                               package_id[31] ^= ((htlc_output_index >> 0) & 0xff) as u8;
+                                                               package_id
+                                                       },
                                                };
-                                               let package_id = txid.into_inner();
                                                self.pending_claim_events.insert(package_id, claim_event);
                                                package_id
                                        },
@@ -685,14 +733,13 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                                //... we need to verify equality between transaction outpoints and claim request
                                                // outpoints to know if transaction is the original claim or a bumped one issued
                                                // by us.
-                                               let mut set_equality = true;
-                                               if request.outpoints().len() != tx.input.len() {
-                                                       set_equality = false;
-                                               } else {
-                                                       for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
-                                                               if **claim_inp != tx_inp.previous_output {
-                                                                       set_equality = false;
-                                                               }
+                                               let mut are_sets_equal = true;
+                                               let mut tx_inputs = tx.input.iter().map(|input| &input.previous_output).collect::<Vec<_>>();
+                                               tx_inputs.sort_unstable();
+                                               for request_input in request.outpoints() {
+                                                       if tx_inputs.binary_search(&request_input).is_err() {
+                                                               are_sets_equal = false;
+                                                               break;
                                                        }
                                                }
 
@@ -702,7 +749,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                                                        txid: tx.txid(),
                                                                        height: conf_height,
                                                                        block_hash: Some(conf_hash),
-                                                                       event: OnchainEvent::Claim { claim_request: Txid::from_inner(first_claim_txid_height.0) }
+                                                                       event: OnchainEvent::Claim { package_id: first_claim_txid_height.0 }
                                                                };
                                                                if !self.onchain_events_awaiting_threshold_conf.contains(&entry) {
                                                                        self.onchain_events_awaiting_threshold_conf.push(entry);
@@ -713,7 +760,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                                // If this is our transaction (or our counterparty spent all the outputs
                                                // before we could anyway with same inputs order than us), wait for
                                                // ANTI_REORG_DELAY and clean the RBF tracking map.
-                                               if set_equality {
+                                               if are_sets_equal {
                                                        clean_claim_request_after_safety_delay!();
                                                } else { // If false, generate new claim request with update outpoint set
                                                        let mut at_least_one_drop = false;
@@ -757,14 +804,14 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                for entry in onchain_events_awaiting_threshold_conf {
                        if entry.has_reached_confirmation_threshold(cur_height) {
                                match entry.event {
-                                       OnchainEvent::Claim { claim_request } => {
-                                               let package_id = claim_request.into_inner();
+                                       OnchainEvent::Claim { package_id } => {
                                                // We may remove a whole set of claim outpoints here, as these one may have
                                                // been aggregated in a single tx and claimed so atomically
                                                if let Some(request) = self.pending_claim_requests.remove(&package_id) {
                                                        for outpoint in request.outpoints() {
-                                                               log_debug!(logger, "Removing claim tracking for {} due to maturation of claim tx {}.", outpoint, claim_request);
-                                                               self.claimable_outpoints.remove(&outpoint);
+                                                               log_debug!(logger, "Removing claim tracking for {} due to maturation of claim package {}.",
+                                                                       outpoint, log_bytes!(package_id));
+                                                               self.claimable_outpoints.remove(outpoint);
                                                                #[cfg(anchors)]
                                                                self.pending_claim_events.remove(&package_id);
                                                        }
@@ -773,7 +820,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                        OnchainEvent::ContentiousOutpoint { package } => {
                                                log_debug!(logger, "Removing claim tracking due to maturation of claim tx for outpoints:");
                                                log_debug!(logger, " {:?}", package.outpoints());
-                                               self.claimable_outpoints.remove(&package.outpoints()[0]);
+                                               self.claimable_outpoints.remove(package.outpoints()[0]);
                                        }
                                }
                        } else {
@@ -851,7 +898,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                                //- resurect outpoint back in its claimable set and regenerate tx
                                match entry.event {
                                        OnchainEvent::ContentiousOutpoint { package } => {
-                                               if let Some(ancestor_claimable_txid) = self.claimable_outpoints.get(&package.outpoints()[0]) {
+                                               if let Some(ancestor_claimable_txid) = self.claimable_outpoints.get(package.outpoints()[0]) {
                                                        if let Some(request) = self.pending_claim_requests.get_mut(&ancestor_claimable_txid.0) {
                                                                request.merge_package(package);
                                                                // Using a HashMap guarantee us than if we have multiple outpoints getting
@@ -999,6 +1046,37 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
                htlc_tx
        }
 
+       #[cfg(anchors)]
+       pub(crate) fn generate_external_htlc_claim(
+               &self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
+       ) -> Option<ExternalHTLCClaim> {
+               let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<ExternalHTLCClaim> {
+                       let trusted_tx = holder_commitment.trust();
+                       if outp.txid != trusted_tx.txid() {
+                               return None;
+                       }
+                       trusted_tx.htlcs().iter().enumerate()
+                               .find(|(_, htlc)| if let Some(output_index) = htlc.transaction_output_index {
+                                       output_index == outp.vout
+                               } else {
+                                       false
+                               })
+                               .map(|(htlc_idx, htlc)| {
+                                       let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
+                                       ExternalHTLCClaim {
+                                               commitment_txid: trusted_tx.txid(),
+                                               per_commitment_number: trusted_tx.commitment_number(),
+                                               htlc: htlc.clone(),
+                                               preimage: *preimage,
+                                               counterparty_sig: counterparty_htlc_sig,
+                                       }
+                               })
+               };
+               // Check if the HTLC spends from the current holder commitment or the previous one otherwise.
+               find_htlc(&self.holder_commitment)
+                       .or_else(|| self.prev_holder_commitment.as_ref().map(|c| find_htlc(c)).flatten())
+       }
+
        pub(crate) fn opt_anchors(&self) -> bool {
                self.channel_transaction_parameters.opt_anchors.is_some()
        }