diff --git a/src/key.rs b/src/key.rs index 2492f0436..7952ace5b 100644 --- a/src/key.rs +++ b/src/key.rs @@ -65,6 +65,7 @@ pub const ONE_KEY: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0, /// A Secp256k1 public key, used for verification of signatures #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[repr(transparent)] pub struct PublicKey(ffi::PublicKey); impl fmt::LowerHex for PublicKey { @@ -364,14 +365,26 @@ impl PublicKey { /// the result would be the point at infinity, i.e. we are adding this point /// to its own negation pub fn combine(&self, other: &PublicKey) -> Result { + PublicKey::combine_keys(&[self, other]) + } + + /// Adds the keys in the provided slice together, returning the sum. Returns + /// an error if the result would be the point at infinity, i.e. we are adding + /// a point to its own negation + pub fn combine_keys(keys: &[&PublicKey]) -> Result { + use core::mem::transmute; + use core::i32::MAX; + + debug_assert!(keys.len() < MAX as usize); unsafe { let mut ret = ffi::PublicKey::new(); - let ptrs = [self.as_c_ptr(), other.as_c_ptr()]; + let ptrs : &[*const ffi::PublicKey] = + transmute::<&[&PublicKey], &[*const ffi::PublicKey]>(keys); if ffi::secp256k1_ec_pubkey_combine( ffi::secp256k1_context_no_precomp, &mut ret, ptrs.as_c_ptr(), - 2 + keys.len() as i32 ) == 1 { Ok(PublicKey(ret)) @@ -794,6 +807,29 @@ mod test { assert_eq!(sum1.unwrap(), exp_sum); } + #[test] + fn pubkey_combine_keys() { + let compressed1 = PublicKey::from_slice( + &hex!("0241cc121c419921942add6db6482fb36243faf83317c866d2a28d8c6d7089f7ba"), + ).unwrap(); + let compressed2 = PublicKey::from_slice( + &hex!("02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443"), + ).unwrap(); + let compressed3 = PublicKey::from_slice( + &hex!("03e74897d8644eb3e5b391ca2ab257aec2080f4d1a95cad57e454e47f021168eb0") + ).unwrap(); + let exp_sum = PublicKey::from_slice( + &hex!("0252d73a47f66cf341e5651542f0348f452b7c793af62a6d8bff75ade703a451ad"), + ).unwrap(); + + let sum1 = PublicKey::combine_keys(&[&compressed1, &compressed2, &compressed3]); + assert!(sum1.is_ok()); + let sum2 = PublicKey::combine_keys(&[&compressed1, &compressed2, &compressed3]); + assert!(sum2.is_ok()); + assert_eq!(sum1, sum2); + assert_eq!(sum1.unwrap(), exp_sum); + } + #[test] fn pubkey_equal() { let pk1 = PublicKey::from_slice(