@@ -172,6 +172,72 @@ impl PublicKey {
172
172
buf
173
173
}
174
174
175
+ /// Serialize the public key into a `SortKey`.
176
+ ///
177
+ /// `SortKey` is not too useful by itself, but it can be used to sort a
178
+ /// `[PublicKey]` slice using `sort_unstable_by_key`, `sort_by_cached_key`,
179
+ /// `sort_by_key`, or any of the other `*_by_key` methods on slice.
180
+ /// Pass the method into the sort method directly. (ie. `PublicKey::to_sort_key`)
181
+ ///
182
+ /// This method of sorting is in line with Bitcoin Core's implementation of
183
+ /// sorting keys for output descriptors such as `sortedmulti()`.
184
+ ///
185
+ /// If every `PublicKey` in the slice is `compressed == true` then this will sort
186
+ /// the keys in a
187
+ /// [BIP67](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki)
188
+ /// compliant way.
189
+ ///
190
+ /// # Example: Using with `sort_unstable_by_key`
191
+ ///
192
+ /// ```rust
193
+ /// use std::str::FromStr;
194
+ /// use bitcoin::PublicKey;
195
+ ///
196
+ /// let pk = |s| PublicKey::from_str(s).unwrap();
197
+ ///
198
+ /// let mut unsorted = [
199
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
200
+ /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
201
+ /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
202
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
203
+ /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
204
+ /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
205
+ /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
206
+ /// ];
207
+ /// let sorted = [
208
+ /// // These first 4 keys are in a BIP67 compatible sorted order
209
+ /// // (since they are compressed)
210
+ /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
211
+ /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
212
+ /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
213
+ /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
214
+ /// // Uncompressed keys are not BIP67 compliant, but are sorted
215
+ /// // after compressed keys in Bitcoin Core using `sortedmulti()`
216
+ /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
217
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
218
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
219
+ /// ];
220
+ ///
221
+ /// unsorted.sort_unstable_by_key(PublicKey::to_sort_key);
222
+ ///
223
+ /// assert_eq!(unsorted, sorted);
224
+ /// ```
225
+ pub fn to_sort_key ( & self ) -> SortKey {
226
+ if self . compressed {
227
+ let bytes = self . inner . serialize ( ) ;
228
+ let mut res = [ 0 ; 32 ] ;
229
+ res[ ..] . copy_from_slice ( & bytes[ 1 ..33 ] ) ;
230
+ SortKey ( bytes[ 0 ] , res, [ 0 ; 32 ] )
231
+ } else {
232
+ let bytes = self . inner . serialize_uncompressed ( ) ;
233
+ let mut res_left = [ 0 ; 32 ] ;
234
+ let mut res_right = [ 0 ; 32 ] ;
235
+ res_left[ ..] . copy_from_slice ( & bytes[ 1 ..33 ] ) ;
236
+ res_right[ ..] . copy_from_slice ( & bytes[ 33 ..65 ] ) ;
237
+ SortKey ( bytes[ 0 ] , res_left, res_right)
238
+ }
239
+ }
240
+
175
241
/// Deserialize a public key from a slice
176
242
pub fn from_slice ( data : & [ u8 ] ) -> Result < PublicKey , Error > {
177
243
let compressed = match data. len ( ) {
@@ -198,6 +264,10 @@ impl PublicKey {
198
264
}
199
265
}
200
266
267
+ /// An opaque return type for PublicKey::to_sort_key
268
+ #[ derive( Debug , Hash , PartialEq , Eq , PartialOrd , Ord , Clone , Copy ) ]
269
+ pub struct SortKey ( u8 , [ u8 ; 32 ] , [ u8 ; 32 ] ) ;
270
+
201
271
impl fmt:: Display for PublicKey {
202
272
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
203
273
if self . compressed {
@@ -475,10 +545,10 @@ impl<'de> ::serde::Deserialize<'de> for PublicKey {
475
545
#[ cfg( test) ]
476
546
mod tests {
477
547
use crate :: io;
478
- use super :: { PrivateKey , PublicKey } ;
548
+ use super :: { PrivateKey , PublicKey , SortKey } ;
479
549
use secp256k1:: Secp256k1 ;
480
550
use std:: str:: FromStr ;
481
- use crate :: hashes:: hex:: ToHex ;
551
+ use crate :: hashes:: hex:: { FromHex , ToHex } ;
482
552
use crate :: network:: constants:: Network :: Testnet ;
483
553
use crate :: network:: constants:: Network :: Bitcoin ;
484
554
use crate :: util:: address:: Address ;
@@ -629,4 +699,141 @@ mod tests {
629
699
assert ! ( PublicKey :: read_from( io:: Cursor :: new( & [ 0 ; 65 ] [ ..] ) ) . is_err( ) ) ;
630
700
assert ! ( PublicKey :: read_from( io:: Cursor :: new( & [ 4 ; 64 ] [ ..] ) ) . is_err( ) ) ;
631
701
}
702
+
703
+ #[ test]
704
+ fn pubkey_to_sort_key ( ) {
705
+ let key1 = PublicKey :: from_str ( "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8" ) . unwrap ( ) ;
706
+ let key2 = PublicKey {
707
+ inner : key1. inner ,
708
+ compressed : false ,
709
+ } ;
710
+ let expected1 = SortKey (
711
+ 2 ,
712
+ <[ u8 ; 32 ] >:: from_hex (
713
+ "ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8" ,
714
+ ) . unwrap ( ) ,
715
+ [ 0_u8 ; 32 ] ,
716
+ ) ;
717
+ let expected2 = SortKey (
718
+ 4 ,
719
+ <[ u8 ; 32 ] >:: from_hex (
720
+ "ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8" ,
721
+ ) . unwrap ( ) ,
722
+ <[ u8 ; 32 ] >:: from_hex (
723
+ "1794e7f3d5e420641a3bc690067df5541470c966cbca8c694bf39aa16d836918" ,
724
+ ) . unwrap ( ) ,
725
+ ) ;
726
+ assert_eq ! ( key1. to_sort_key( ) , expected1) ;
727
+ assert_eq ! ( key2. to_sort_key( ) , expected2) ;
728
+ }
729
+
730
+ #[ test]
731
+ fn pubkey_sort ( ) {
732
+ struct Vector {
733
+ input : Vec < PublicKey > ,
734
+ expect : Vec < PublicKey > ,
735
+ }
736
+ let fmt = |v : Vec < _ > | v. into_iter ( )
737
+ . map ( |s| PublicKey :: from_str ( s) . unwrap ( ) )
738
+ . collect :: < Vec < _ > > ( ) ;
739
+ let vectors = vec ! [
740
+ // Start BIP67 vectors
741
+ // Vector 1
742
+ Vector {
743
+ input: fmt( vec![
744
+ "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8" ,
745
+ "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f" ,
746
+ ] ) ,
747
+ expect: fmt( vec![
748
+ "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f" ,
749
+ "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8" ,
750
+ ] ) ,
751
+ } ,
752
+ // Vector 2 (Already sorted, no action required)
753
+ Vector {
754
+ input: fmt( vec![
755
+ "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0" ,
756
+ "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77" ,
757
+ "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404" ,
758
+ ] ) ,
759
+ expect: fmt( vec![
760
+ "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0" ,
761
+ "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77" ,
762
+ "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404" ,
763
+ ] ) ,
764
+ } ,
765
+ // Vector 3
766
+ Vector {
767
+ input: fmt( vec![
768
+ "030000000000000000000000000000000000004141414141414141414141414141" ,
769
+ "020000000000000000000000000000000000004141414141414141414141414141" ,
770
+ "020000000000000000000000000000000000004141414141414141414141414140" ,
771
+ "030000000000000000000000000000000000004141414141414141414141414140" ,
772
+ ] ) ,
773
+ expect: fmt( vec![
774
+ "020000000000000000000000000000000000004141414141414141414141414140" ,
775
+ "020000000000000000000000000000000000004141414141414141414141414141" ,
776
+ "030000000000000000000000000000000000004141414141414141414141414140" ,
777
+ "030000000000000000000000000000000000004141414141414141414141414141" ,
778
+ ] ) ,
779
+ } ,
780
+ // Vector 4: (from bitcore)
781
+ Vector {
782
+ input: fmt( vec![
783
+ "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da" ,
784
+ "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9" ,
785
+ "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18" ,
786
+ ] ) ,
787
+ expect: fmt( vec![
788
+ "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18" ,
789
+ "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da" ,
790
+ "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9" ,
791
+ ] ) ,
792
+ } ,
793
+ // Non-BIP67 vectors
794
+ Vector {
795
+ input: fmt( vec![
796
+ "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88" ,
797
+ "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68" ,
798
+ "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46" ,
799
+ "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b" ,
800
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa" ,
801
+ "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7" ,
802
+ "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8" ,
803
+ "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b" ,
804
+ "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8" ,
805
+ "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5" ,
806
+ "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354" ,
807
+ "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438" ,
808
+ "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1" ,
809
+ "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f" ,
810
+ "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa" ,
811
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35" ,
812
+ ] ) ,
813
+ expect: fmt( vec![
814
+ "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68" ,
815
+ "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa" ,
816
+ "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f" ,
817
+ "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7" ,
818
+ "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88" ,
819
+ "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b" ,
820
+ "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b" ,
821
+ "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354" ,
822
+ "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1" ,
823
+ "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46" ,
824
+ "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5" ,
825
+ "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8" ,
826
+ // These two pubkeys are mirrored. This helps verify the sort past the x value.
827
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa" ,
828
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35" ,
829
+ "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438" ,
830
+ "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8" ,
831
+ ] ) ,
832
+ } ,
833
+ ] ;
834
+ for mut vector in vectors {
835
+ vector. input . sort_by_cached_key ( PublicKey :: to_sort_key) ;
836
+ assert_eq ! ( vector. input, vector. expect) ;
837
+ }
838
+ }
632
839
}
0 commit comments