@@ -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 } ;
@@ -1463,7 +1464,7 @@ impl CommitmentTransaction {
14631464 let keys = TxCreationKeys :: from_channel_static_keys ( per_commitment_point, channel_parameters. broadcaster_pubkeys ( ) , channel_parameters. countersignatory_pubkeys ( ) , secp_ctx) ;
14641465
14651466 // Sort outputs and populate output indices
1466- let ( outputs, nondust_htlcs) = Self :: internal_build_outputs ( & keys, to_broadcaster_value_sat, to_countersignatory_value_sat, nondust_htlcs, channel_parameters) . unwrap ( ) ;
1467+ let ( outputs, nondust_htlcs) = Self :: build_outputs_and_htlcs ( & keys, to_broadcaster_value_sat, to_countersignatory_value_sat, nondust_htlcs, channel_parameters) . unwrap ( ) ;
14671468
14681469 let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( commitment_number, channel_parameters) ;
14691470 let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
@@ -1492,11 +1493,17 @@ impl CommitmentTransaction {
14921493 self
14931494 }
14941495
1496+ // This function
1497+ // - Builds the inputs of the commitment transaction.
1498+ // - Calls `internal_build_outputs` to get a `Vec<(TxOut, Option<&HTLCOutputInCommitment>)>` sorted according to the LN specification,
1499+ // and as they'd appear on the commitment transaction.
1500+ // - Maps this vector to a `Vec<TxOut>`, keeping the original order of the outputs.
1501+ // - Then uses the vectors of inputs and outputs to build the commitment transaction.
14951502 fn internal_rebuild_transaction ( & self , keys : & TxCreationKeys , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < BuiltCommitmentTransaction , ( ) > {
14961503 let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( self . commitment_number , channel_parameters) ;
14971504
1498- let mut nondust_htlcs = self . htlcs . clone ( ) ;
1499- let ( outputs, _ ) = Self :: internal_build_outputs ( keys , self . to_broadcaster_value_sat , self . to_countersignatory_value_sat , nondust_htlcs . iter_mut ( ) . collect ( ) , channel_parameters ) ? ;
1505+ let txout_htlc_pairs = Self :: internal_build_outputs ( keys , self . to_broadcaster_value_sat , self . to_countersignatory_value_sat , self . htlcs ( ) . iter ( ) , channel_parameters ) ? ;
1506+ let outputs = txout_htlc_pairs . into_iter ( ) . map ( | ( output , _ ) | output ) . collect ( ) ;
15001507
15011508 let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
15021509 let txid = transaction. compute_txid ( ) ;
@@ -1516,17 +1523,58 @@ impl CommitmentTransaction {
15161523 }
15171524 }
15181525
1519- // This is used in two cases:
1520- // - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
1521- // caller needs to have sorted together with the HTLCs so it can keep track of the output index
1522- // - building of a bitcoin transaction during a verify() call, in which case T is just ()
1523- 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 > ) , ( ) > {
1526+ // This function
1527+ // - Calls `internal_build_outputs` to get a `Vec<(TxOut, Option<&mut HTLCOutputInCommitment>)>` sorted according to the LN specification,
1528+ // and as they'd appear on the commitment transaction.
1529+ // - Then iterates over this vector of tuples:
1530+ // - populates *all* the output indices of the exclusive references in `nondust_htlcs`,
1531+ // - creates a vector of `HTLCOutputInCommitment` sorted by their output index,
1532+ // - creates a vector of `TxOut`, sorted according to the LN Spec, see `sort_outputs`.
1533+ // - Returns the two vectors: the vector of `TxOut`, and the vector of `HTLCOutputInCommitment`:
1534+ // - `Vec<TxOut>` gets moved to the `Transaction` cached by `CommitmentTransaction`,
1535+ // - `Vec<HTLCOutputInCommitment>` gets moved to and cached by `CommitmentTransaction`.
1536+ 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 > ) , ( ) > {
1537+ let mut htlcs: Vec < HTLCOutputInCommitment > = Vec :: with_capacity ( nondust_htlcs. len ( ) ) ;
1538+ 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) ?;
1539+ let mut outputs: Vec < TxOut > = Vec :: with_capacity ( txout_htlc_pairs. len ( ) ) ;
1540+ for ( idx, out) in txout_htlc_pairs. drain ( ..) . enumerate ( ) {
1541+ if let Some ( htlc) = out. 1 {
1542+ // Remember `htlc` is a `&mut HTLCOutputInCommitment`, so this populates the HTLC exclusive references passed to `CommitmentTransaction::new`
1543+ htlc. transaction_output_index = Some ( idx as u32 ) ;
1544+ // We also clone the htlcs to cache them in `CommitmentTransaction`
1545+ htlcs. push ( htlc. clone ( ) ) ;
1546+ }
1547+ outputs. push ( out. 0 ) ;
1548+ }
1549+ Ok ( ( outputs, htlcs) )
1550+ }
1551+
1552+ // This function
1553+ // - Takes an iterator that yields objects of type `T` that can yield a `&HTLCOutputInCommitment`; `T: Borrow<HTLCOutputInCommitment>`.
1554+ // For our use-cases, these are `&mut HTLCOutputInCommitment` (used by `CommitmentTransaction::new`), and `&HTLCOutputInCommitment` (used by `CommitmentTransaction::internal_rebuild_transaction`).
1555+ // - Builds a `Vec<(TxOut, Option<T>)>`, containing all the outputs of the commitment transaction.
1556+ // 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`.
1557+ // - Passes this vector to `sort_outputs` to sort all the tuples in the vector according to the LN specification.
1558+ // - Then returns this sorted vector to the caller.
1559+ 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 > ) > , ( ) > {
15241560 let countersignatory_pubkeys = channel_parameters. countersignatory_pubkeys ( ) ;
15251561 let broadcaster_funding_key = & channel_parameters. broadcaster_pubkeys ( ) . funding_pubkey ;
15261562 let countersignatory_funding_key = & countersignatory_pubkeys. funding_pubkey ;
15271563 let contest_delay = channel_parameters. contest_delay ( ) ;
15281564
1529- let mut txouts: Vec < ( TxOut , Option < & mut HTLCOutputInCommitment > ) > = Vec :: new ( ) ;
1565+ // Assumes the max number of outputs on the transaction is equal to n_htlcs + to_local + to_remote + local_anchor + remote_anchor
1566+ let mut txouts: Vec < ( TxOut , Option < T > ) > = Vec :: with_capacity ( nondust_htlcs. size_hint ( ) . 0 + 4 ) ;
1567+
1568+ for htlc in nondust_htlcs {
1569+ let script = get_htlc_redeemscript ( htlc. borrow ( ) , & channel_parameters. channel_type_features ( ) , & keys) ;
1570+ let txout = TxOut {
1571+ script_pubkey : script. to_p2wsh ( ) ,
1572+ value : htlc. borrow ( ) . to_bitcoin_amount ( ) ,
1573+ } ;
1574+ txouts. push ( ( txout, Some ( htlc) ) ) ;
1575+ }
1576+ // We avoid relying on `ExactSizeIterator` here
1577+ let nondust_htlcs_is_empty = txouts. is_empty ( ) ;
15301578
15311579 if to_countersignatory_value_sat > Amount :: ZERO {
15321580 let script = if channel_parameters. channel_type_features ( ) . supports_anchors_zero_fee_htlc_tx ( ) {
@@ -1559,7 +1607,7 @@ impl CommitmentTransaction {
15591607 }
15601608
15611609 if channel_parameters. channel_type_features ( ) . supports_anchors_zero_fee_htlc_tx ( ) {
1562- if to_broadcaster_value_sat > Amount :: ZERO || !nondust_htlcs . is_empty ( ) {
1610+ if to_broadcaster_value_sat > Amount :: ZERO || !nondust_htlcs_is_empty {
15631611 let anchor_script = get_anchor_redeemscript ( broadcaster_funding_key) ;
15641612 txouts. push ( (
15651613 TxOut {
@@ -1570,7 +1618,7 @@ impl CommitmentTransaction {
15701618 ) ) ;
15711619 }
15721620
1573- if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs . is_empty ( ) {
1621+ if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs_is_empty {
15741622 let anchor_script = get_anchor_redeemscript ( countersignatory_funding_key) ;
15751623 txouts. push ( (
15761624 TxOut {
@@ -1582,41 +1630,23 @@ impl CommitmentTransaction {
15821630 }
15831631 }
15841632
1585- let mut htlcs = Vec :: with_capacity ( nondust_htlcs. len ( ) ) ;
1586- for htlc in nondust_htlcs {
1587- let script = get_htlc_redeemscript ( & htlc, & channel_parameters. channel_type_features ( ) , & keys) ;
1588- let txout = TxOut {
1589- script_pubkey : script. to_p2wsh ( ) ,
1590- value : htlc. to_bitcoin_amount ( ) ,
1591- } ;
1592- txouts. push ( ( txout, Some ( htlc) ) ) ;
1593- }
1594-
15951633 // Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC
15961634 // CLTV expiration height.
15971635 sort_outputs ( & mut txouts, |a, b| {
1598- if let & Some ( ref a_htlcout) = a {
1599- if let & Some ( ref b_htlcout) = b {
1600- a_htlcout. cltv_expiry . cmp ( & b_htlcout. cltv_expiry )
1636+ if let Some ( a_htlcout) = a {
1637+ if let Some ( b_htlcout) = b {
1638+ a_htlcout. borrow ( ) . cltv_expiry . cmp ( & b_htlcout. borrow ( ) . cltv_expiry )
16011639 // Note that due to hash collisions, we have to have a fallback comparison
16021640 // here for fuzzing mode (otherwise at least chanmon_fail_consistency
16031641 // may fail)!
1604- . then ( a_htlcout. payment_hash . 0 . cmp ( & b_htlcout. payment_hash . 0 ) )
1642+ . then ( a_htlcout. borrow ( ) . payment_hash . 0 . cmp ( & b_htlcout. borrow ( ) . payment_hash . 0 ) )
16051643 // For non-HTLC outputs, if they're copying our SPK we don't really care if we
16061644 // close the channel due to mismatches - they're doing something dumb:
16071645 } else { cmp:: Ordering :: Equal }
16081646 } else { cmp:: Ordering :: Equal }
16091647 } ) ;
16101648
1611- let mut outputs = Vec :: with_capacity ( txouts. len ( ) ) ;
1612- for ( idx, out) in txouts. drain ( ..) . enumerate ( ) {
1613- if let Some ( htlc) = out. 1 {
1614- htlc. transaction_output_index = Some ( idx as u32 ) ;
1615- htlcs. push ( htlc. clone ( ) ) ;
1616- }
1617- outputs. push ( out. 0 ) ;
1618- }
1619- Ok ( ( outputs, htlcs) )
1649+ Ok ( txouts)
16201650 }
16211651
16221652 fn internal_build_inputs ( commitment_number : u64 , channel_parameters : & DirectedChannelTransactionParameters ) -> ( u64 , Vec < TxIn > ) {
0 commit comments