@@ -563,10 +563,18 @@ pub struct ClaimId(pub [u8; 32]);
563563
564564impl ClaimId {
565565 pub ( crate ) fn from_htlcs ( htlcs : & [ HTLCDescriptor ] ) -> ClaimId {
566+ let mut htlc_outpoints = htlcs
567+ . iter ( )
568+ . map ( |htlc| {
569+ ( htlc. commitment_txid . to_byte_array ( ) , htlc. htlc . transaction_output_index . unwrap ( ) )
570+ } )
571+ . collect :: < Vec < _ > > ( ) ;
572+ htlc_outpoints. sort_unstable ( ) ;
573+
566574 let mut engine = Sha256 :: engine ( ) ;
567- for htlc in htlcs {
568- engine. input ( & htlc . commitment_txid . to_byte_array ( ) ) ;
569- engine. input ( & htlc . htlc . transaction_output_index . unwrap ( ) . to_be_bytes ( ) ) ;
575+ for ( commitment_txid , transaction_output_index ) in htlc_outpoints {
576+ engine. input ( & commitment_txid) ;
577+ engine. input ( & transaction_output_index. to_be_bytes ( ) ) ;
570578 }
571579 ClaimId ( Sha256 :: from_engine ( engine) . to_byte_array ( ) )
572580 }
@@ -581,8 +589,45 @@ impl ClaimId {
581589#[ cfg( test) ]
582590mod tests {
583591 use super :: * ;
592+ use crate :: ln:: chan_utils:: {
593+ ChannelTransactionParameters , HTLCOutputInCommitment , HolderCommitmentTransaction ,
594+ } ;
595+ use crate :: sign:: ChannelDerivationParameters ;
596+ use crate :: types:: payment:: { PaymentHash , PaymentPreimage } ;
584597 use bitcoin:: hashes:: Hash ;
585598
599+ fn dummy_htlc_descriptor (
600+ commitment_txid : Txid , transaction_output_index : u32 ,
601+ ) -> HTLCDescriptor {
602+ let channel_parameters = ChannelTransactionParameters :: test_dummy ( 100_000 ) ;
603+ let htlc = HTLCOutputInCommitment {
604+ offered : true ,
605+ amount_msat : 1000 ,
606+ cltv_expiry : 100 ,
607+ payment_hash : PaymentHash :: from ( PaymentPreimage ( [ 1 ; 32 ] ) ) ,
608+ transaction_output_index : Some ( transaction_output_index) ,
609+ } ;
610+ let funding_outpoint = channel_parameters. funding_outpoint . unwrap ( ) ;
611+ let commitment_tx =
612+ HolderCommitmentTransaction :: dummy ( 100_000 , funding_outpoint, vec ! [ htlc. clone( ) ] ) ;
613+ let trusted_tx = commitment_tx. trust ( ) ;
614+
615+ HTLCDescriptor {
616+ channel_derivation_parameters : ChannelDerivationParameters {
617+ value_satoshis : channel_parameters. channel_value_satoshis ,
618+ keys_id : [ 1 ; 32 ] ,
619+ transaction_parameters : channel_parameters,
620+ } ,
621+ commitment_txid,
622+ per_commitment_number : trusted_tx. commitment_number ( ) ,
623+ per_commitment_point : trusted_tx. per_commitment_point ( ) ,
624+ feerate_per_kw : trusted_tx. negotiated_feerate_per_kw ( ) ,
625+ htlc,
626+ preimage : None ,
627+ counterparty_sig : commitment_tx. counterparty_htlc_sigs [ 0 ] ,
628+ }
629+ }
630+
586631 #[ test]
587632 fn test_best_block ( ) {
588633 let hash1 = BlockHash :: from_slice ( & [ 1 ; 32 ] ) . unwrap ( ) ;
@@ -618,4 +663,17 @@ mod tests {
618663 let chain_c = BlockLocator :: new ( hash_other, 200 ) ;
619664 assert_eq ! ( chain_a. find_common_ancestor( & chain_c) , None ) ;
620665 }
666+
667+ #[ test]
668+ fn test_htlc_claim_id_is_descriptor_order_independent ( ) {
669+ // Use opposite txid and vout ordering so the assertion would fail if
670+ // ClaimId still hashed descriptors in caller-provided order.
671+ let first = dummy_htlc_descriptor ( Txid :: from_slice ( & [ 1 ; 32 ] ) . unwrap ( ) , 2 ) ;
672+ let second = dummy_htlc_descriptor ( Txid :: from_slice ( & [ 2 ; 32 ] ) . unwrap ( ) , 1 ) ;
673+
674+ assert_eq ! (
675+ ClaimId :: from_htlcs( & [ first. clone( ) , second. clone( ) ] ) ,
676+ ClaimId :: from_htlcs( & [ second, first] )
677+ ) ;
678+ }
621679}
0 commit comments