diff --git a/src/ffi.rs b/src/ffi.rs index 57605d1..ec5fe04 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -423,6 +423,11 @@ extern "C" { cx: *const Context, pk: *mut PublicKey, commit: *const c_uchar) -> c_int; + // Get a pedersen commitment from a pubkey + pub fn secp256k1_pubkey_to_pedersen_commitment( + cx: *const Context, commit: *mut c_uchar, + pk: *const PublicKey) -> c_int; + // Takes a list of n pointers to 32 byte blinding values, the first negs // of which are treated with positive sign and the rest negative, then // calculates an additional blinding value that adds to zero. diff --git a/src/lib.rs b/src/lib.rs index 5b1482e..1215ba7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -460,6 +460,8 @@ pub enum Error { InvalidMessage, /// Bad public key InvalidPublicKey, + /// Bad commit + InvalidCommit, /// Bad signature InvalidSignature, /// Bad secret key @@ -490,6 +492,7 @@ impl error::Error for Error { Error::IncorrectSignature => "secp: signature failed verification", Error::InvalidMessage => "secp: message was not 32 bytes (do you need to hash?)", Error::InvalidPublicKey => "secp: malformed public key", + Error::InvalidCommit => "secp: malformed commit", Error::InvalidSignature => "secp: malformed signature", Error::InvalidSecretKey => "secp: malformed or out-of-range secret key", Error::InvalidRecoveryId => "secp: bad recovery id", diff --git a/src/pedersen.rs b/src/pedersen.rs index 05137e2..f5df67a 100644 --- a/src/pedersen.rs +++ b/src/pedersen.rs @@ -24,7 +24,7 @@ use std::ptr; use std::u64; use crate::ContextFlag; -use crate::Error::{self, InvalidPublicKey}; +use crate::Error::{self, InvalidPublicKey, InvalidCommit}; use crate::Secp256k1; use super::{Message, Signature}; @@ -95,6 +95,18 @@ impl Commitment { mem::uninitialized() } + /// Creates from a pubkey + pub fn from_pubkey(secp: &Secp256k1, pk: &key::PublicKey) -> Result { + unsafe { + let mut commit = Self::blank(); + if ffi::secp256k1_pubkey_to_pedersen_commitment(secp.ctx, commit.as_mut_ptr(), &pk.0) == 1 { + Ok(commit) + } else { + Err(InvalidCommit) + } + } + } + /// Converts a commitment to a public key pub fn to_pubkey(&self, secp: &Secp256k1) -> Result { let mut pk = unsafe { ffi::PublicKey::blank() }; @@ -107,6 +119,7 @@ impl Commitment { } } } + } /// A range proof. Typically much larger in memory that the above (~5k). @@ -1276,6 +1289,40 @@ mod tests { } } + #[test] + fn test_from_pubkey() { + for _ in 0..100 { + let secp = Secp256k1::with_caps(ContextFlag::Commit); + let blinding = SecretKey::new(&secp, &mut thread_rng()); + let commit = secp.commit(1, blinding).unwrap(); + let pubkey = commit.to_pubkey(&secp); + let p = match pubkey { + Ok(p) => { + // this is good + p + } + Err(e) => { + panic!("Creating pubkey: {}", e); + } + }; + //println!("Pre Commit is: {:?}", commit); + //println!("Pre Pubkey is: {:?}", p); + let new_commit = Commitment::from_pubkey(&secp, &p); + let commit2 = match new_commit { + Ok(c) => { + // this is good + c + } + Err(e) => { + panic!("Creating commit from Pubkey: {}", e); + } + }; + //println!("Post Commit is: {:?}", commit2); + //println!("Post Pubkey is: {:?}", p); + assert_eq!(commit, commit2); + } + } + #[test] fn test_sign_with_pubkey_from_commitment() { let secp = Secp256k1::with_caps(ContextFlag::Commit);