[RFC][Tx Sort] Implement sorting of inputs
authorSavil Srivastava <savil@cs.stanford.edu>
Mon, 23 Jul 2018 00:03:13 +0000 (17:03 -0700)
committerSavil Srivastava <savil@cs.stanford.edu>
Tue, 24 Jul 2018 03:26:26 +0000 (20:26 -0700)
Follows BIP69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki

1. Implements sorting of transactoin inputs.
- BIP says to use "reversed byte-order" for the `prev_hash`. I interpreted this as: little-endian.
- TODO need to add tests

2. Re: improve sorting of TxOut's script_pubkey to use lexicographic ordering, and not length.
From the test-cases i've included it seems that the current code already does lexicographic ordering (and not length based). Am i missing something?

src/util/transaction_utils.rs

index 8ecd78acc482ccd5c35df234de9e4d00cab113ec..4f5018f60bb757a97f02fd0f2d16ce6f0280a76c 100644 (file)
@@ -1,19 +1,92 @@
-use bitcoin::blockdata::transaction::TxOut;
+use bitcoin::blockdata::transaction::{TxIn, TxOut};
 
 use std::cmp::Ordering;
 
-pub fn sort_outputs<T>(outputs: &mut Vec<(TxOut, T)>) { //TODO: Make static and put it in some utils somewhere (+inputs sorting)
+pub fn sort_outputs<T>(outputs: &mut Vec<(TxOut, T)>) {
        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[..] { //TODO: ordering of scripts shouldn't be len-based
+               } else if a.0.script_pubkey[..] < b.0.script_pubkey[..] {
                        Ordering::Less
-               } else if b.0.script_pubkey[..] < a.0.script_pubkey[..] { //TODO: ordering of scripts shouldn't be len-based
+               } else if b.0.script_pubkey[..] < a.0.script_pubkey[..] {
                        Ordering::Greater
                } else {
                        Ordering::Equal
                }
        });
 }
+
+// TODO savil. Add tests.
+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
+        }
+    });
+}
+
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use bitcoin::blockdata::opcodes;
+    use bitcoin::blockdata::script::Builder;
+    use bitcoin::blockdata::transaction::TxOut;
+
+    #[test]
+    fn sort_output_by_value() {
+        let txout1 = TxOut {
+            value:  100,
+            script_pubkey: Builder::new().push_int(0).into_script()
+        };
+        let txout1_ = txout1.clone();
+
+        let txout2 = TxOut {
+            value: 99,
+            script_pubkey: Builder::new().push_int(0).into_script()
+        };
+        let txout2_ = txout2.clone();
+
+        let mut outputs = vec![(txout1, "ignore"), (txout2, "ignore")];
+        sort_outputs(&mut outputs);
+
+        assert_eq!(
+            &outputs,
+            &vec![(txout2_, "ignore"), (txout1_, "ignore")]
+        );
+    }
+
+    #[test]
+    fn sort_output_by_script_pubkey() {
+        let txout1 = TxOut {
+            value:  100,
+            script_pubkey: Builder::new().push_int(3).into_script(),
+        };
+        let txout1_ = txout1.clone();
+
+        let txout2 = TxOut {
+            value: 100,
+            script_pubkey: Builder::new().push_int(1).push_int(2).into_script()
+        };
+        let txout2_ = txout2.clone();
+
+        let mut outputs = vec![(txout1, "ignore"), (txout2, "ignore")];
+        sort_outputs(&mut outputs);
+
+        assert_eq!(
+            &outputs,
+            &vec![(txout2_, "ignore"), (txout1_, "ignore")]
+        );
+    }
+}