diff --git a/Cargo.toml b/Cargo.toml index f9818b8..c6327f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cfd-rust" -version = "0.2.2" +version = "0.2.3" license = "MIT" readme = "README.md" keywords = ["build-dependencies"] diff --git a/cfd-sys/Cargo.toml b/cfd-sys/Cargo.toml index 2b5b664..9ef4f3d 100644 --- a/cfd-sys/Cargo.toml +++ b/cfd-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cfd_sys" -version = "0.2.2" +version = "0.2.3" license = "MIT" readme = "../README.md" keywords = ["build-dependencies"] diff --git a/cfd-sys/cfd-cmake/local_resource/external_project_local_setting.config b/cfd-sys/cfd-cmake/local_resource/external_project_local_setting.config index c1cc0e2..b8af309 100644 --- a/cfd-sys/cfd-cmake/local_resource/external_project_local_setting.config +++ b/cfd-sys/cfd-cmake/local_resource/external_project_local_setting.config @@ -1,6 +1,6 @@ -CFDRUST_VERSION=0.2.2 -CFD_TARGET_VERSION=refs/tags/v0.2.6 -CFDCORE_TARGET_VERSION=refs/tags/v0.2.5 -LIBWALLY_TARGET_VERSION=refs/tags/cfd-0.2.2 +CFDRUST_VERSION=0.2.3 +CFD_TARGET_VERSION=refs/tags/v0.2.7 +CFDCORE_TARGET_VERSION=refs/tags/v0.2.6 +LIBWALLY_TARGET_VERSION=refs/tags/cfd-0.2.3 CFD_TARGET_URL=cryptogarageinc/cfd.git CFDCORE_TARGET_URL=cryptogarageinc/cfd-core.git diff --git a/cfd-sys/src/lib.rs b/cfd-sys/src/lib.rs index 04373cb..f5e2d1b 100644 --- a/cfd-sys/src/lib.rs +++ b/cfd-sys/src/lib.rs @@ -372,6 +372,35 @@ fns! { handle: *const c_void, privkey: *const i8, pubkey: *mut *mut c_char, + parity: *mut bool, + ) -> c_int; + pub fn CfdGetSchnorrPubkeyFromPubkey( + handle: *const c_void, + pubkey: *const i8, + schnorr_pubkey: *mut *mut c_char, + parity: *mut bool, + ) -> c_int; + pub fn CfdSchnorrPubkeyTweakAdd( + handle: *const c_void, + pubkey: *const i8, + tweak: *const i8, + output: *mut *mut c_char, + parity: *mut bool, + ) -> c_int; + pub fn CfdSchnorrKeyPairTweakAdd( + handle: *const c_void, + privkey: *const i8, + tweak: *const i8, + tweaked_pubkey: *mut *mut c_char, + tweaked_parity: *mut bool, + tweaked_privkey: *mut *mut c_char, + ) -> c_int; + pub fn CfdCheckTweakAddFromSchnorrPubkey( + handle: *const c_void, + tweaked_pubkey: *const i8, + tweaked_parity: bool, + base_pubkey: *const i8, + tweak: *const i8, ) -> c_int; pub fn CfdSignSchnorr( handle: *const c_void, diff --git a/src/schnorr.rs b/src/schnorr.rs index e9316b7..04ebe2e 100644 --- a/src/schnorr.rs +++ b/src/schnorr.rs @@ -13,9 +13,10 @@ use std::result::Result::{Err, Ok}; use std::str::FromStr; use self::cfd_sys::{ - CfdAdaptEcdsaAdaptor, CfdComputeSchnorrSigPoint, CfdExtractEcdsaAdaptorSecret, - CfdSignEcdsaAdaptor, CfdSignSchnorr, CfdSignSchnorrWithNonce, CfdSplitSchnorrSignature, - CfdVerifyEcdsaAdaptor, CfdVerifySchnorr, CfdGetSchnorrPubkeyFromPrivkey, + CfdAdaptEcdsaAdaptor, CfdCheckTweakAddFromSchnorrPubkey, CfdComputeSchnorrSigPoint, + CfdExtractEcdsaAdaptorSecret, CfdGetSchnorrPubkeyFromPrivkey, CfdGetSchnorrPubkeyFromPubkey, + CfdSchnorrKeyPairTweakAdd, CfdSchnorrPubkeyTweakAdd, CfdSignEcdsaAdaptor, CfdSignSchnorr, + CfdSignSchnorrWithNonce, CfdSplitSchnorrSignature, CfdVerifyEcdsaAdaptor, CfdVerifySchnorr, }; /// adaptor signature size. @@ -590,23 +591,115 @@ impl SchnorrPubkey { /// use cfd_rust::{SchnorrPubkey, Privkey}; /// use std::str::FromStr; /// let key = Privkey::from_str("475697a71a74ff3f2a8f150534e9b67d4b0b6561fab86fcaa51f8c9d6c9db8c6").expect("Fail"); - /// let pubkey = SchnorrPubkey::from_privkey(&key).expect("Fail"); + /// let pubkey_ret = SchnorrPubkey::from_privkey(&key).expect("Fail"); + /// let (pubkey, parity) = pubkey_ret; /// ``` - pub fn from_privkey(key: &Privkey) -> Result { + pub fn from_privkey(key: &Privkey) -> Result<(SchnorrPubkey, bool), CfdError> { let key_hex = alloc_c_string(&key.to_hex())?; let handle = ErrorHandle::new()?; let mut pubkey_hex: *mut c_char = ptr::null_mut(); + let mut parity = false; let error_code = unsafe { CfdGetSchnorrPubkeyFromPrivkey( handle.as_handle(), key_hex.as_ptr(), &mut pubkey_hex, + &mut parity, ) }; let result = match error_code { 0 => { let pubkey = unsafe { collect_cstring_and_free(pubkey_hex) }?; - SchnorrPubkey::from_str(&pubkey) + let pubkey_obj = SchnorrPubkey::from_str(&pubkey)?; + Ok((pubkey_obj, parity)) + } + _ => Err(handle.get_error(error_code)), + }; + handle.free_handle(); + result + } + + /// Generate from pubkey. + /// + /// # Arguments + /// * `key` - A public key. + /// + /// # Example + /// + /// ``` + /// use cfd_rust::{SchnorrPubkey, Pubkey}; + /// use std::str::FromStr; + /// let key = Pubkey::from_str("03b33cc9edc096d0a83416964bd3c6247b8fecd256e4efa7870d2c854bdeb33390").expect("Fail"); + /// let pubkey_ret = SchnorrPubkey::from_pubkey(&key).expect("Fail"); + /// let (pubkey, parity) = pubkey_ret; + /// ``` + pub fn from_pubkey(key: &Pubkey) -> Result<(SchnorrPubkey, bool), CfdError> { + let key_hex = alloc_c_string(&key.to_hex())?; + let handle = ErrorHandle::new()?; + let mut pubkey_hex: *mut c_char = ptr::null_mut(); + let mut parity = false; + let error_code = unsafe { + CfdGetSchnorrPubkeyFromPubkey( + handle.as_handle(), + key_hex.as_ptr(), + &mut pubkey_hex, + &mut parity, + ) + }; + let result = match error_code { + 0 => { + let pubkey = unsafe { collect_cstring_and_free(pubkey_hex) }?; + let pubkey_obj = SchnorrPubkey::from_str(&pubkey)?; + Ok((pubkey_obj, parity)) + } + _ => Err(handle.get_error(error_code)), + }; + handle.free_handle(); + result + } + + /// Generate tweaked key pair from privkey. + /// + /// # Arguments + /// * `key` - A private key. + /// * `data` - A tweaked 32 byte buffer. + /// + /// # Example + /// + /// ``` + /// use cfd_rust::{SchnorrPubkey, Privkey, ByteData}; + /// use std::str::FromStr; + /// let key = Privkey::from_str("475697a71a74ff3f2a8f150534e9b67d4b0b6561fab86fcaa51f8c9d6c9db8c6").expect("Fail"); + /// let tweak = ByteData::from_str("e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614").expect("Fail"); + /// let pubkey_ret = SchnorrPubkey::get_tweak_add_from_privkey(&key, tweak.to_slice()).expect("Fail"); + /// let (pubkey, parity, privkey) = pubkey_ret; + /// ``` + pub fn get_tweak_add_from_privkey( + key: &Privkey, + data: &[u8], + ) -> Result<(SchnorrPubkey, bool, Privkey), CfdError> { + let key_hex = alloc_c_string(&key.to_hex())?; + let tweak_hex = alloc_c_string(&hex_from_bytes(data))?; + let handle = ErrorHandle::new()?; + let mut pubkey_hex: *mut c_char = ptr::null_mut(); + let mut privkey_hex: *mut c_char = ptr::null_mut(); + let mut parity = false; + let error_code = unsafe { + CfdSchnorrKeyPairTweakAdd( + handle.as_handle(), + key_hex.as_ptr(), + tweak_hex.as_ptr(), + &mut pubkey_hex, + &mut parity, + &mut privkey_hex, + ) + }; + let result = match error_code { + 0 => { + let str_list = unsafe { collect_multi_cstring_and_free(&[pubkey_hex, privkey_hex]) }?; + let pubkey_obj = SchnorrPubkey::from_str(&str_list[0])?; + let privkey_obj = Privkey::from_str(&str_list[1])?; + Ok((pubkey_obj, parity, privkey_obj)) } _ => Err(handle.get_error(error_code)), }; @@ -632,6 +725,91 @@ impl SchnorrPubkey { pub fn as_key(&self) -> Result { Privkey::from_slice(&self.data) } + + /// Generate tweaked schnorr pubkey. + /// + /// # Arguments + /// * `data` - A tweaked 32 byte buffer. + /// + /// # Example + /// + /// ``` + /// use cfd_rust::{SchnorrPubkey, ByteData}; + /// use std::str::FromStr; + /// let key = SchnorrPubkey::from_str("b33cc9edc096d0a83416964bd3c6247b8fecd256e4efa7870d2c854bdeb33390").expect("Fail"); + /// let tweak = ByteData::from_str("e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614").expect("Fail"); + /// let pubkey_ret = key.tweak_add(tweak.to_slice()).expect("Fail"); + /// let (pubkey, parity) = pubkey_ret; + /// ``` + pub fn tweak_add(&self, data: &[u8]) -> Result<(SchnorrPubkey, bool), CfdError> { + let key_hex = alloc_c_string(&self.to_hex())?; + let tweak_hex = alloc_c_string(&hex_from_bytes(data))?; + let handle = ErrorHandle::new()?; + let mut pubkey_hex: *mut c_char = ptr::null_mut(); + let mut parity = false; + let error_code = unsafe { + CfdSchnorrPubkeyTweakAdd( + handle.as_handle(), + key_hex.as_ptr(), + tweak_hex.as_ptr(), + &mut pubkey_hex, + &mut parity, + ) + }; + let result = match error_code { + 0 => { + let pubkey = unsafe { collect_cstring_and_free(pubkey_hex) }?; + let pubkey_obj = SchnorrPubkey::from_str(&pubkey)?; + Ok((pubkey_obj, parity)) + } + _ => Err(handle.get_error(error_code)), + }; + handle.free_handle(); + result + } + + /// Generate tweaked schnorr pubkey. + /// + /// # Arguments + /// * `data` - A tweaked 32 byte buffer. + /// + /// # Example + /// + /// ``` + /// use cfd_rust::{SchnorrPubkey, ByteData}; + /// use std::str::FromStr; + /// let key = SchnorrPubkey::from_str("b33cc9edc096d0a83416964bd3c6247b8fecd256e4efa7870d2c854bdeb33390").expect("Fail"); + /// let tweak = ByteData::from_str("e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614").expect("Fail"); + /// let tweaked_key = SchnorrPubkey::from_str("1fc8e882e34cc7942a15f39ffaebcbdf58a19239bcb17b7f5aa88e0eb808f906").expect("Fail"); + /// let is_valid = tweaked_key.is_tweaked(true, &key, tweak.to_slice()).expect("Fail"); + /// ``` + pub fn is_tweaked( + &self, + parity: bool, + base_pubkey: &SchnorrPubkey, + data: &[u8], + ) -> Result { + let key_hex = alloc_c_string(&self.to_hex())?; + let base_key_hex = alloc_c_string(&base_pubkey.to_hex())?; + let tweak_hex = alloc_c_string(&hex_from_bytes(data))?; + let handle = ErrorHandle::new()?; + let error_code = unsafe { + CfdCheckTweakAddFromSchnorrPubkey( + handle.as_handle(), + key_hex.as_ptr(), + parity, + base_key_hex.as_ptr(), + tweak_hex.as_ptr(), + ) + }; + let result = match error_code { + 0 => Ok(true), + 7 => Ok(false), + _ => Err(handle.get_error(error_code)), + }; + handle.free_handle(); + result + } } impl fmt::Display for SchnorrPubkey { diff --git a/tests/hdwallet_test.rs b/tests/hdwallet_test.rs index 60b8167..2eb2f03 100644 --- a/tests/hdwallet_test.rs +++ b/tests/hdwallet_test.rs @@ -150,7 +150,7 @@ mod tests { "0101010101010101010101010101010101010101010101010101010101010101", ByteData::from_slice(&entropy).to_hex() ); - + let wallet = HDWallet::from_mnemonic( "absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice comic", MnemonicLanguage::EN, diff --git a/tests/schnorr_test.rs b/tests/schnorr_test.rs index 22968ef..2f743cd 100644 --- a/tests/schnorr_test.rs +++ b/tests/schnorr_test.rs @@ -56,7 +56,7 @@ mod tests { } #[test] - fn schnorr_test() { + fn schnorr_util_test() { // default let msg = ByteData::from_str("e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614") @@ -79,9 +79,6 @@ mod tests { SchnorrSignature::from_str("6470fd1303dda4fda717b9837153c24a6eab377183fc438f939e0ed2b620e9ee5077c4a8b8dca28963d772a94f5f0ddf598e1c47c137f91933274c7c3edadce8").expect("Fail"); let obj = SchnorrUtil::new(); - let schnorr_pubkey = SchnorrPubkey::from_privkey(&sk).expect("Fail"); - assert_eq!(pubkey.to_hex(), schnorr_pubkey.to_hex()); - let sig1 = obj.sign(&msg, &sk, &aux_rand).expect("Fail"); assert_eq!(signature.to_hex(), sig1.to_hex()); @@ -106,4 +103,53 @@ mod tests { assert_eq!(expected_nonce, sig1.as_nonce().to_hex()); assert_eq!(expected_privkey, sig1.as_key().to_hex()); } + + #[test] + fn schnorr_pubkey_test() { + // default + let tweak = + ByteData::from_str("e48441762fb75010b2aa31a512b62b4148aa3fb08eb0765d76b252559064a614") + .expect("Fail"); + let sk = Privkey::from_str("688c77bc2d5aaff5491cf309d4753b732135470d05b7b2cd21add0744fe97bef") + .expect("Fail"); + let pk = Pubkey::from_str("03b33cc9edc096d0a83416964bd3c6247b8fecd256e4efa7870d2c854bdeb33390") + .expect("Fail"); + let pubkey = + SchnorrPubkey::from_str("b33cc9edc096d0a83416964bd3c6247b8fecd256e4efa7870d2c854bdeb33390") + .expect("Fail"); + let exp_tweaked_pk = + SchnorrPubkey::from_str("1fc8e882e34cc7942a15f39ffaebcbdf58a19239bcb17b7f5aa88e0eb808f906") + .expect("Fail"); + let exp_tweaked_sk = + Privkey::from_str("7bf7c9ba025ca01b698d3e9b3e40efce2774f8a388f8c390550481e1407b2a25") + .expect("Fail"); + + let (schnorr_pubkey, parity) = SchnorrPubkey::from_privkey(&sk).expect("Fail"); + assert_eq!(pubkey.to_hex(), schnorr_pubkey.to_hex()); + assert_eq!(true, parity); + + let (schnorr_pubkey, parity) = SchnorrPubkey::from_pubkey(&pk).expect("Fail"); + assert_eq!(pubkey.to_hex(), schnorr_pubkey.to_hex()); + assert_eq!(true, parity); + + let (tweaked_pubkey, tweaked_parity) = pubkey.tweak_add(tweak.to_slice()).expect("Fail"); + assert_eq!(exp_tweaked_pk.to_hex(), tweaked_pubkey.to_hex()); + assert_eq!(true, tweaked_parity); + + let gen_key_ret = + SchnorrPubkey::get_tweak_add_from_privkey(&sk, tweak.to_slice()).expect("Fail"); + let (tweaked_pubkey, tweaked_parity, tweaked_privkey) = gen_key_ret; + assert_eq!(exp_tweaked_pk.to_hex(), tweaked_pubkey.to_hex()); + assert_eq!(true, tweaked_parity); + assert_eq!(exp_tweaked_sk.to_hex(), tweaked_privkey.to_hex()); + + let is_valid = tweaked_pubkey + .is_tweaked(true, &pubkey, tweak.to_slice()) + .expect("Fail"); + assert_eq!(true, is_valid); + let is_valid = tweaked_pubkey + .is_tweaked(false, &pubkey, tweak.to_slice()) + .expect("Fail"); + assert_eq!(false, is_valid); + } }