Fix HTLC-output-in-commitment sorting for duplicate-HTLCs
[rust-lightning] / src / util / transaction_utils.rs
index 92bc209abb1978aa29cb75edecff7bc094204c6e..b81d3d9d00c06409e4b9e8a84e745256580a0aca 100644 (file)
@@ -1,36 +1,36 @@
 use bitcoin::blockdata::transaction::{TxIn, TxOut};
+use bitcoin_hashes::sha256d::Hash as Sha256dHash;
 
 use std::cmp::Ordering;
 
-pub fn sort_outputs<T>(outputs: &mut Vec<(TxOut, T)>) {
+pub fn sort_outputs<T, C : Fn(&T, &T) -> Ordering>(outputs: &mut Vec<(TxOut, T)>, tie_breaker: C) {
        outputs.sort_unstable_by(|a, b| {
-               if a.0.value < b.0.value {
-                       Ordering::Less
-               } else if b.0.value < a.0.value {
-                       Ordering::Greater
-               } else if a.0.script_pubkey[..] < b.0.script_pubkey[..] {
-                       Ordering::Less
-               } else if b.0.script_pubkey[..] < a.0.script_pubkey[..] {
-                       Ordering::Greater
-               } else {
-                       Ordering::Equal
-               }
+               a.0.value.cmp(&b.0.value).then_with(|| {
+                       a.0.script_pubkey[..].cmp(&b.0.script_pubkey[..]).then_with(|| {
+                               tie_breaker(&a.1, &b.1)
+                       })
+               })
        });
 }
 
+fn cmp(a: &Sha256dHash, b: &Sha256dHash) -> Ordering {
+       use bitcoin_hashes::Hash;
+
+       let av = a.into_inner();
+       let bv = b.into_inner();
+       for i in (0..32).rev() {
+               let cmp = av[i].cmp(&bv[i]);
+               if cmp != Ordering::Equal {
+                       return cmp;
+               }
+       }
+       Ordering::Equal
+}
+
 pub fn sort_inputs<T>(inputs: &mut Vec<(TxIn, T)>) {
        inputs.sort_unstable_by(|a, b| {
-               if a.0.prev_hash.into_le() < b.0.prev_hash.into_le() {
-               Ordering::Less
-               } else if b.0.prev_hash.into_le() < a.0.prev_hash.into_le() {
-                       Ordering::Greater
-               } else if a.0.prev_index < b.0.prev_index {
-                       Ordering::Less
-               } else if b.0.prev_index < a.0.prev_index {
-                       Ordering::Greater
-               } else {
-                       Ordering::Equal
-               }
+               cmp( &a.0.previous_output.txid, &b.0.previous_output.txid).then(
+               a.0.previous_output.vout.cmp(&b.0.previous_output.vout))
        });
 }
 
@@ -39,8 +39,9 @@ mod tests {
        use super::*;
 
        use bitcoin::blockdata::script::{Script, Builder};
-       use bitcoin::blockdata::transaction::TxOut;
-       use bitcoin::util::hash::Sha256dHash;
+       use bitcoin::blockdata::transaction::{TxOut, OutPoint};
+       use bitcoin_hashes::sha256d::Hash as Sha256dHash;
+       use bitcoin_hashes::hex::FromHex;
 
        use hex::decode;
 
@@ -59,7 +60,7 @@ mod tests {
                let txout2_ = txout2.clone();
 
                let mut outputs = vec![(txout1, "ignore"), (txout2, "ignore")];
-               sort_outputs(&mut outputs);
+               sort_outputs(&mut outputs, |_, _| { unreachable!(); });
 
                assert_eq!(
                        &outputs,
@@ -82,7 +83,7 @@ mod tests {
                let txout2_ = txout2.clone();
 
                let mut outputs = vec![(txout1, "ignore"), (txout2, "ignore")];
-               sort_outputs(&mut outputs);
+               sort_outputs(&mut outputs, |_, _| { unreachable!(); });
 
                assert_eq!(
                        &outputs,
@@ -106,7 +107,7 @@ mod tests {
                let txout2_ = txout2.clone();
 
                let mut outputs = vec![(txout1, "ignore"), (txout2, "ignore")];
-               sort_outputs(&mut outputs);
+               sort_outputs(&mut outputs, |_, _| { unreachable!(); });
 
                assert_eq!(&outputs, &vec![(txout1_, "ignore"), (txout2_, "ignore")]);
        }
@@ -132,7 +133,7 @@ mod tests {
                                        outputs.reverse(); // prep it
 
                                        // actually do the work!
-                                       sort_outputs(&mut outputs);
+                                       sort_outputs(&mut outputs, |_, _| { unreachable!(); });
 
                                        assert_eq!(outputs, expected);
                                }
@@ -161,8 +162,10 @@ mod tests {
                                        let expected_raw: Vec<(&str, u32)> = $value;
                                        let expected: Vec<(TxIn, &str)> = expected_raw.iter().map(
                                                |txin_raw| TxIn {
-                                                       prev_hash: Sha256dHash::from_hex(txin_raw.0).unwrap(),
-                                                       prev_index: txin_raw.1,
+                                                       previous_output: OutPoint {
+                                                               txid: Sha256dHash::from_hex(txin_raw.0).unwrap(),
+                                                               vout: txin_raw.1,
+                                                       },
                                                        script_sig: Script::new(),
                                                        sequence: 0,
                                                        witness: vec![]