@@ -40,6 +40,7 @@ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
40
40
use bitcoin:: { secp256k1, Sequence , Witness } ;
41
41
42
42
use crate :: io;
43
+ use core:: borrow:: Borrow ;
43
44
use core:: cmp;
44
45
use crate :: util:: transaction_utils:: sort_outputs;
45
46
use crate :: ln:: channel:: { INITIAL_COMMITMENT_NUMBER , ANCHOR_OUTPUT_VALUE_SATOSHI } ;
@@ -1463,7 +1464,7 @@ impl CommitmentTransaction {
1463
1464
let keys = TxCreationKeys :: from_channel_static_keys ( per_commitment_point, channel_parameters. broadcaster_pubkeys ( ) , channel_parameters. countersignatory_pubkeys ( ) , secp_ctx) ;
1464
1465
1465
1466
// 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 ( ) ;
1467
1468
1468
1469
let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( commitment_number, channel_parameters) ;
1469
1470
let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
@@ -1492,11 +1493,17 @@ impl CommitmentTransaction {
1492
1493
self
1493
1494
}
1494
1495
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.
1495
1502
fn internal_rebuild_transaction ( & self , keys : & TxCreationKeys , channel_parameters : & DirectedChannelTransactionParameters ) -> Result < BuiltCommitmentTransaction , ( ) > {
1496
1503
let ( obscured_commitment_transaction_number, txins) = Self :: internal_build_inputs ( self . commitment_number , channel_parameters) ;
1497
1504
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 ( ) ;
1500
1507
1501
1508
let transaction = Self :: make_transaction ( obscured_commitment_transaction_number, txins, outputs) ;
1502
1509
let txid = transaction. compute_txid ( ) ;
@@ -1516,17 +1523,58 @@ impl CommitmentTransaction {
1516
1523
}
1517
1524
}
1518
1525
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 > ) > , ( ) > {
1524
1560
let countersignatory_pubkeys = channel_parameters. countersignatory_pubkeys ( ) ;
1525
1561
let broadcaster_funding_key = & channel_parameters. broadcaster_pubkeys ( ) . funding_pubkey ;
1526
1562
let countersignatory_funding_key = & countersignatory_pubkeys. funding_pubkey ;
1527
1563
let contest_delay = channel_parameters. contest_delay ( ) ;
1528
1564
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 ( ) ;
1530
1578
1531
1579
if to_countersignatory_value_sat > Amount :: ZERO {
1532
1580
let script = if channel_parameters. channel_type_features ( ) . supports_anchors_zero_fee_htlc_tx ( ) {
@@ -1559,7 +1607,7 @@ impl CommitmentTransaction {
1559
1607
}
1560
1608
1561
1609
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 {
1563
1611
let anchor_script = get_anchor_redeemscript ( broadcaster_funding_key) ;
1564
1612
txouts. push ( (
1565
1613
TxOut {
@@ -1570,7 +1618,7 @@ impl CommitmentTransaction {
1570
1618
) ) ;
1571
1619
}
1572
1620
1573
- if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs . is_empty ( ) {
1621
+ if to_countersignatory_value_sat > Amount :: ZERO || !nondust_htlcs_is_empty {
1574
1622
let anchor_script = get_anchor_redeemscript ( countersignatory_funding_key) ;
1575
1623
txouts. push ( (
1576
1624
TxOut {
@@ -1582,41 +1630,23 @@ impl CommitmentTransaction {
1582
1630
}
1583
1631
}
1584
1632
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
-
1595
1633
// Sort output in BIP-69 order (amount, scriptPubkey). Tie-breaks based on HTLC
1596
1634
// CLTV expiration height.
1597
1635
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 )
1601
1639
// Note that due to hash collisions, we have to have a fallback comparison
1602
1640
// here for fuzzing mode (otherwise at least chanmon_fail_consistency
1603
1641
// 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 ) )
1605
1643
// For non-HTLC outputs, if they're copying our SPK we don't really care if we
1606
1644
// close the channel due to mismatches - they're doing something dumb:
1607
1645
} else { cmp:: Ordering :: Equal }
1608
1646
} else { cmp:: Ordering :: Equal }
1609
1647
} ) ;
1610
1648
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)
1620
1650
}
1621
1651
1622
1652
fn internal_build_inputs ( commitment_number : u64 , channel_parameters : & DirectedChannelTransactionParameters ) -> ( u64 , Vec < TxIn > ) {
0 commit comments