@@ -40,6 +40,7 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
4040use bitcoin:: { secp256k1, Sequence , Witness } ;
4141
4242use crate :: io;
43+ use core:: borrow:: Borrow ;
4344use core:: cmp;
4445use crate :: util:: transaction_utils:: sort_outputs;
4546use crate :: ln:: channel:: { INITIAL_COMMITMENT_NUMBER , ANCHOR_OUTPUT_VALUE_SATOSHI } ;
@@ -1480,7 +1481,7 @@ impl CommitmentTransaction {
14801481 let keys = TxCreationKeys :: from_channel_static_keys ( per_commitment_point, channel_parameters. broadcaster_pubkeys ( ) , channel_parameters. countersignatory_pubkeys ( ) , secp_ctx) ;
14811482
14821483 // Sort outputs and populate output indices
1483- let ( outputs, nondust_htlcs) = Self :: internal_build_outputs ( & keys, to_broadcaster_value_sat, to_countersignatory_value_sat, nondust_htlcs, channel_parameters) . unwrap ( ) ;
1484+ let ( outputs, nondust_htlcs) = Self :: build_outputs_and_htlcs ( & keys, to_broadcaster_value_sat, to_countersignatory_value_sat, nondust_htlcs, channel_parameters) . unwrap ( ) ;
14841485
14851486 let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( commitment_number, channel_parameters) ;
14861487 let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
@@ -1509,11 +1510,17 @@ impl CommitmentTransaction {
15091510 self
15101511 }
15111512
1513+ // This function
1514+ // - Builds the inputs of the commitment transaction.
1515+ // - Calls `internal_build_outputs` to get a `Vec<(TxOut, Option<&HTLCOutputInCommitment>)>` sorted according to the LN specification,
1516+ // and as they'd appear on the commitment transaction.
1517+ // - Maps this vector to a `Vec<TxOut>`, keeping the original order of the outputs.
1518+ // - Then uses the vectors of inputs and outputs to build the commitment transaction.
15121519 fn internal_rebuild_transaction ( & self , keys : & TxCreationKeys , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < BuiltCommitmentTransaction , ( ) > {
15131520 let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( self . commitment_number , channel_parameters) ;
15141521
1515- let mut nondust_htlcs = self . htlcs . clone ( ) ;
1516- let ( outputs, _ ) = Self :: internal_build_outputs ( keys , self . to_broadcaster_value_sat , self . to_countersignatory_value_sat , nondust_htlcs . iter_mut ( ) . collect ( ) , channel_parameters ) ? ;
1522+ let txout_htlc_pairs = Self :: internal_build_outputs ( keys , self . to_broadcaster_value_sat , self . to_countersignatory_value_sat , self . htlcs ( ) . iter ( ) , channel_parameters ) ? ;
1523+ let outputs = txout_htlc_pairs . into_iter ( ) . map ( | ( output , _ ) | output ) . collect ( ) ;
15171524
15181525 let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
15191526 let txid = transaction. compute_txid ( ) ;
@@ -1533,17 +1540,58 @@ impl CommitmentTransaction {
15331540 }
15341541 }
15351542
1536- // This is used in two cases:
1537- // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
1538- // caller needs to have sorted together with the HTLCs so it can keep track of the output index
1539- // - building of a bitcoin transaction during a verify() call, in which case T is just ()
1540- fn internal_build_outputs ( keys : & TxCreationKeys , to_broadcaster_value_sat : Amount , to_countersignatory_value_sat : Amount , nondust_htlcs : Vec < & mut HTLCOutputInCommitment > , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < ( Vec < TxOut > , Vec < HTLCOutputInCommitment > ) , ( ) > {
1543+ // This function
1544+ // - Calls `internal_build_outputs` to get a `Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)>` sorted according to the LN specification,
1545+ // and as they'd appear on the commitment transaction.
1546+ // - Then iterates over this vector of tuples:
1547+ // - populates *all* the output indices of the exclusive references in `nondust_htlcs`,
1548+ // - creates a vector of `HTLCOutputInCommitment` sorted by their output index,
1549+ // - creates a vector of `TxOut`, sorted according to the LN Spec, see `sort_outputs`.
1550+ // - Returns the two vectors: the vector of `TxOut`, and the vector of `HTLCOutputInCommitment`:
1551+ // - `Vec<TxOut>` gets moved to the `Transaction` cached by `CommitmentTransaction`,
1552+ // - `Vec<HTLCOutputInCommitment>` gets moved to and cached by `CommitmentTransaction`.
1553+ fn build_outputs_and_htlcs ( keys : & TxCreationKeys , to_broadcaster_value_sat : Amount , to_countersignatory_value_sat : Amount , nondust_htlcs : Vec < & mut HTLCOutputInCommitment > , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < ( Vec < TxOut > , Vec < HTLCOutputInCommitment > ) , ( ) > {
1554+ let mut htlcs: Vec < HTLCOutputInCommitment > = Vec :: with_capacity ( nondust_htlcs. len ( ) ) ;
1555+ let mut txout_htlc_pairs: Vec < ( TxOut , Option < & mut HTLCOutputInCommitment > ) > = Self :: internal_build_outputs ( keys, to_broadcaster_value_sat, to_countersignatory_value_sat, nondust_htlcs. into_iter ( ) , channel_parameters) ?;
1556+ let mut outputs: Vec < TxOut > = Vec :: with_capacity ( txout_htlc_pairs. len ( ) ) ;
1557+ for ( idx, out) in txout_htlc_pairs. drain ( ..) . enumerate ( ) {
1558+ if let Some ( htlc) = out. 1 {
1559+ // Remember `htlc` is a `&mut HTLCOutputInCommitment`, so this populates the HTLC exclusive references passed to `CommitmentTransaction::new`
1560+ htlc. transaction_output_index = Some ( idx as u32 ) ;
1561+ // We also clone the htlcs to cache them in `CommitmentTransaction`
1562+ htlcs. push ( htlc. clone ( ) ) ;
1563+ }
1564+ outputs. push ( out. 0 ) ;
1565+ }
1566+ Ok ( ( outputs, htlcs) )
1567+ }
1568+
1569+ // This function
1570+ // - Takes an iterator that yields objects of type `T` that can yield a `&HTLCOutputInCommitment`; `T: Borrow<HTLCOutputInCommitment>`.
1571+ // For our use-cases, these are `&mut HTLCOutputInCommitment` (used by `CommitmentTransaction::new`), and `&HTLCOutputInCommitment` (used by `CommitmentTransaction::internal_rebuild_transaction`).
1572+ // - Builds a `Vec<(TxOut, Option<T>)>`, containing all the outputs of the commitment transaction.
1573+ // For the `Option` in the tuple, an HTLC output sets the option to `Some(T)`, and all the other outputs set the option to `None`.
1574+ // - Passes this vector to `sort_outputs` to sort all the tuples in the vector according to the LN specification.
1575+ // - Then returns this sorted vector to the caller.
1576+ fn internal_build_outputs < T : Borrow < HTLCOutputInCommitment > > ( keys : & TxCreationKeys , to_broadcaster_value_sat : Amount , to_countersignatory_value_sat : Amount , nondust_htlcs : impl Iterator < Item = T > , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < Vec < ( TxOut , Option < T > ) > , ( ) > {
15411577 let countersignatory_pubkeys = channel_parameters. countersignatory_pubkeys ( ) ;
15421578 let broadcaster_funding_key = & channel_parameters. broadcaster_pubkeys ( ) . funding_pubkey ;
15431579 let countersignatory_funding_key = & countersignatory_pubkeys. funding_pubkey ;
15441580 let contest_delay = channel_parameters. contest_delay ( ) ;
15451581
1546- let mut txouts: Vec < ( TxOut , Option < & mut HTLCOutputInCommitment > ) > = Vec :: new ( ) ;
1582+ // Assumes the max number of outputs on the transaction is equal to n_htlcs + to_local + to_remote + local_anchor + remote_anchor
1583+ let mut txouts: Vec < ( TxOut , Option < T > ) > = Vec :: with_capacity ( nondust_htlcs. size_hint ( ) . 0 + 4 ) ;
1584+
1585+ for htlc in nondust_htlcs {
1586+ let script = get_htlc_redeemscript ( htlc. borrow ( ) , & channel_parameters. channel_type_features ( ) , & keys) ;
1587+ let txout = TxOut {
1588+ script_pubkey : script. to_p2wsh ( ) ,
1589+ value : htlc. borrow ( ) . to_bitcoin_amount ( ) ,
1590+ } ;
1591+ txouts. push ( ( txout, Some ( htlc) ) ) ;
1592+ }
1593+ // We avoid relying on `ExactSizeIterator` here
1594+ let nondust_htlcs_is_empty = txouts. is_empty ( ) ;
15471595
15481596 if to_countersignatory_value_sat > Amount :: ZERO {
15491597 let script = if channel_parameters. channel_type_features ( ) . supports_anchors_zero_fee_htlc_tx ( ) {
@@ -1576,7 +1624,7 @@ impl CommitmentTransaction {
15761624 }
15771625
15781626 if channel_parameters. channel_type_features ( ) . supports_anchors_zero_fee_htlc_tx ( ) {
1579- if to_broadcaster_value_sat > Amount :: ZERO || !nondust_htlcs . is_empty ( ) {
1627+ if to_broadcaster_value_sat > Amount :: ZERO || !nondust_htlcs_is_empty {
15801628 let anchor_script = get_anchor_redeemscript ( broadcaster_funding_key) ;
15811629 txouts. push ( (
15821630 TxOut {
@@ -1587,7 +1635,7 @@ impl CommitmentTransaction {
15871635 ) ) ;
15881636 }
15891637
1590- if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs . is_empty ( ) {
1638+ if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs_is_empty {
15911639 let anchor_script = get_anchor_redeemscript ( countersignatory_funding_key) ;
15921640 txouts. push ( (
15931641 TxOut {
@@ -1599,41 +1647,23 @@ impl CommitmentTransaction {
15991647 }
16001648 }
16011649
1602- let mut htlcs = Vec :: with_capacity ( nondust_htlcs. len ( ) ) ;
1603- for htlc in nondust_htlcs {
1604- let script = get_htlc_redeemscript ( & htlc, & channel_parameters. channel_type_features ( ) , & keys) ;
1605- let txout = TxOut {
1606- script_pubkey : script. to_p2wsh ( ) ,
1607- value : htlc. to_bitcoin_amount ( ) ,
1608- } ;
1609- txouts. push ( ( txout, Some ( htlc) ) ) ;
1610- }
1611-
16121650 // Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC
16131651 // CLTV expiration height.
16141652 sort_outputs ( & mut txouts, |a, b| {
1615- if let & Some ( ref a_htlcout) = a {
1616- if let & Some ( ref b_htlcout) = b {
1617- a_htlcout. cltv_expiry . cmp ( & b_htlcout. cltv_expiry )
1653+ if let Some ( a_htlcout) = a {
1654+ if let Some ( b_htlcout) = b {
1655+ a_htlcout. borrow ( ) . cltv_expiry . cmp ( & b_htlcout. borrow ( ) . cltv_expiry )
16181656 // Note that due to hash collisions, we have to have a fallback comparison
16191657 // here for fuzzing mode (otherwise at least chanmon_fail_consistency
16201658 // may fail)!
1621- . then ( a_htlcout. payment_hash . 0 . cmp ( & b_htlcout. payment_hash . 0 ) )
1659+ . then ( a_htlcout. borrow ( ) . payment_hash . 0 . cmp ( & b_htlcout. borrow ( ) . payment_hash . 0 ) )
16221660 // For non-HTLC outputs, if they're copying our SPK we don't really care if we
16231661 // close the channel due to mismatches - they're doing something dumb:
16241662 } else { cmp:: Ordering :: Equal }
16251663 } else { cmp:: Ordering :: Equal }
16261664 } ) ;
16271665
1628- let mut outputs = Vec :: with_capacity ( txouts. len ( ) ) ;
1629- for ( idx, out) in txouts. drain ( ..) . enumerate ( ) {
1630- if let Some ( htlc) = out. 1 {
1631- htlc. transaction_output_index = Some ( idx as u32 ) ;
1632- htlcs. push ( htlc. clone ( ) ) ;
1633- }
1634- outputs. push ( out. 0 ) ;
1635- }
1636- Ok ( ( outputs, htlcs) )
1666+ Ok ( txouts)
16371667 }
16381668
16391669 fn internal_build_inputs ( commitment_number : u64 , channel_parameters : & DirectedChannelTransactionParameters ) -> ( u64 , Vec < TxIn > ) {
0 commit comments