diff --git a/Cargo.toml b/Cargo.toml index 016947d..de6f874 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "secp256k1" -version = "0.8.0" +version = "0.8.1" authors = [ "Dawid Ciężarkiewicz ", "Andrew Poelstra " ] license = "CC0-1.0" diff --git a/src/key.rs b/src/key.rs index f572d41..07aff81 100644 --- a/src/key.rs +++ b/src/key.rs @@ -20,6 +20,8 @@ #[cfg(any(test, feature = "rustc-serialize"))] use serialize::{Decoder, Decodable, Encoder, Encodable}; #[cfg(any(test, feature = "serde"))] use serde::{Serialize, Deserialize, Serializer, Deserializer}; +use std::mem; + use super::{Secp256k1, ContextFlag}; use super::Error::{self, IncapableContext, InvalidPublicKey, InvalidSecretKey}; use constants; @@ -252,6 +254,21 @@ impl PublicKey { } } } + + /// Adds a second key to this one, returning the sum. Returns an error if + /// the result would be the point at infinity, i.e. we are adding this point + /// to its own negation + pub fn combine(&self, secp: &Secp256k1, other: &PublicKey) -> Result { + unsafe { + let mut ret = mem::uninitialized(); + let ptrs = [self.as_ptr(), other.as_ptr()]; + if ffi::secp256k1_ec_pubkey_combine(secp.ctx, &mut ret, ptrs.as_ptr(), 2) == 1 { + Ok(PublicKey(ret)) + } else { + Err(InvalidPublicKey) + } + } + } } #[cfg(any(test, feature = "rustc-serialize"))] @@ -377,8 +394,11 @@ mod test { use super::{PublicKey, SecretKey}; use super::super::constants; + use serialize::hex::FromHex; use rand::{Rng, thread_rng}; + macro_rules! hex (($hex:expr) => (FromHex::from_hex($hex).unwrap())); + #[test] fn skey_from_slice() { let s = Secp256k1::new(); @@ -734,6 +754,30 @@ mod test { }).count(); assert_eq!(count, COUNT); } + + #[test] + fn pubkey_combine() { + let s = Secp256k1::with_caps(ContextFlag::None); + let compressed1 = PublicKey::from_slice( + &s, + &hex!("0241cc121c419921942add6db6482fb36243faf83317c866d2a28d8c6d7089f7ba"), + ).unwrap(); + let compressed2 = PublicKey::from_slice( + &s, + &hex!("02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443"), + ).unwrap(); + let exp_sum = PublicKey::from_slice( + &s, + &hex!("0384526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07"), + ).unwrap(); + + let sum1 = compressed1.combine(&s, &compressed2); + assert!(sum1.is_ok()); + let sum2 = compressed2.combine(&s, &compressed1); + assert!(sum2.is_ok()); + assert_eq!(sum1, sum2); + assert_eq!(sum1.unwrap(), exp_sum); + } }