From e4a9b53f783c760db84999ca87ffd4ff28acab8b Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Thu, 2 May 2024 22:57:32 +0200 Subject: [PATCH 1/5] implement DerivationPath in c-bindings --- .../ergo-lib-c-core/src/derivation_path.rs | 76 +++++++++++++++++++ bindings/ergo-lib-c-core/src/lib.rs | 1 + bindings/ergo-lib-c/src/derivation_path.rs | 73 ++++++++++++++++++ bindings/ergo-lib-c/src/lib.rs | 1 + .../Sources/ErgoLib/DerivationPath.swift | 21 +++++ 5 files changed, 172 insertions(+) create mode 100644 bindings/ergo-lib-c-core/src/derivation_path.rs create mode 100644 bindings/ergo-lib-c/src/derivation_path.rs create mode 100644 bindings/ergo-lib-ios/Sources/ErgoLib/DerivationPath.swift diff --git a/bindings/ergo-lib-c-core/src/derivation_path.rs b/bindings/ergo-lib-c-core/src/derivation_path.rs new file mode 100644 index 000000000..e4fc45869 --- /dev/null +++ b/bindings/ergo-lib-c-core/src/derivation_path.rs @@ -0,0 +1,76 @@ +//! Derivation Path functionality + +use crate::util::const_ptr_as_ref; +use crate::{util::mut_ptr_as_mut, Error}; +use derive_more::{From, Into}; +use ergo_lib::wallet; +use ergo_lib::wallet::derivation_path::{ + ChildIndexError, ChildIndexHardened, ChildIndexNormal, DerivationPath as InnerDerivationPath, +}; +use std::str::FromStr; + +#[derive(From, Into)] +pub struct DerivationPath(pub InnerDerivationPath); +pub type DerivationPathPtr = *mut DerivationPath; +pub type ConstDerivationPathPtr = *const DerivationPath; + +/// Create DerivationPath from account index and address indices +pub unsafe fn derivation_path_new( + account: u32, + address_indices: &[u32], + derivation_path_out: *mut DerivationPathPtr, +) -> Result<(), Error> { + let derivation_path_out = mut_ptr_as_mut(derivation_path_out, "derivation_path_out")?; + let acc = ChildIndexHardened::from_31_bit(account)?; + let address_indices = address_indices + .iter() + .map(|i| ChildIndexNormal::normal(*i)) + .collect::, ChildIndexError>>() + .map_err(Error::misc)?; + let derivation_path = DerivationPath(InnerDerivationPath::new(acc, address_indices)); + *derivation_path_out = Box::into_raw(Box::new(derivation_path)); + Ok(()) +} + +/// Create derivation path from string +/// String should be in the form of: m/44/429/acc'/0/addr +pub unsafe fn derivation_path_from_str( + derivation_path_str: &str, + derivation_path_out: *mut DerivationPathPtr, +) -> Result<(), Error> { + let derivation_path_out = mut_ptr_as_mut(derivation_path_out, "derivation_path_out")?; + let derivation_path = wallet::derivation_path::DerivationPath::from_str(derivation_path_str) + .map_err(Error::misc)?; + *derivation_path_out = Box::into_raw(Box::new(DerivationPath(derivation_path))); + Ok(()) +} + +/// Get derivation path as string in the m/44/429/acc'/0/addr format +pub unsafe fn derivation_path_to_str( + derivation_path_ptr: ConstDerivationPathPtr, +) -> Result { + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + let s = derivation_path.0.to_string(); + Ok(s) +} + +/// Returns the length of the derivation path +pub unsafe fn derivation_path_depth( + derivation_path_ptr: ConstDerivationPathPtr, +) -> Result { + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + Ok(derivation_path.0.depth()) +} + +/// Returns a new derivation path with the last element of the derivation path being increased, e.g. m/1/2 -> m/1/3 +pub unsafe fn derivation_path_next( + derivation_path_ptr: ConstDerivationPathPtr, + derivation_path_out: *mut DerivationPathPtr, +) -> Result<(), Error> { + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + let derivation_path_out = mut_ptr_as_mut(derivation_path_out, "derivation_path_out")?; + *derivation_path_out = Box::into_raw(Box::new(DerivationPath( + derivation_path.0.next().map_err(Error::misc)?, + ))); + Ok(()) +} diff --git a/bindings/ergo-lib-c-core/src/lib.rs b/bindings/ergo-lib-c-core/src/lib.rs index 87d6ad7c3..ce82a5168 100644 --- a/bindings/ergo-lib-c-core/src/lib.rs +++ b/bindings/ergo-lib-c-core/src/lib.rs @@ -38,6 +38,7 @@ pub mod tx_builder; pub mod util; pub mod wallet; pub use crate::error::*; +pub mod derivation_path; mod error; #[cfg(feature = "rest")] pub mod rest; diff --git a/bindings/ergo-lib-c/src/derivation_path.rs b/bindings/ergo-lib-c/src/derivation_path.rs new file mode 100644 index 000000000..e91fcc2ed --- /dev/null +++ b/bindings/ergo-lib-c/src/derivation_path.rs @@ -0,0 +1,73 @@ +//! Derivation Path functionality + +use crate::{delete_ptr, ErrorPtr}; +use ergo_lib_c_core::derivation_path::{ + derivation_path_depth, derivation_path_from_str, derivation_path_new, derivation_path_next, + derivation_path_to_str, ConstDerivationPathPtr, DerivationPathPtr, +}; +use ergo_lib_c_core::Error; +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +/// Create DerivationPath from account index and address indices +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_derivation_path_new( + account: u32, + address_indices: *const u32, + len: usize, + derivation_path_out: *mut DerivationPathPtr, +) -> ErrorPtr { + let address_indices = std::slice::from_raw_parts(address_indices, len); + let res = derivation_path_new(account, address_indices, derivation_path_out); + Error::c_api_from(res) +} + +/// Create derivation path from string +/// String should be in the form of: m/44/429/acc'/0/addr +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_derivation_path_from_str( + derivation_path_str: *const c_char, + derivation_path_out: *mut DerivationPathPtr, +) -> ErrorPtr { + let derivation_path_str = CStr::from_ptr(derivation_path_str).to_string_lossy(); + let res = derivation_path_from_str(&derivation_path_str, derivation_path_out); + Error::c_api_from(res) +} + +/// Get derivation path as string in the m/44/429/acc'/0/addr format +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_derivation_path_to_str( + derivation_path_ptr: ConstDerivationPathPtr, + _derivation_path_str: *mut *const c_char, +) { + #[allow(clippy::unwrap_used)] + { + let s = derivation_path_to_str(derivation_path_ptr).unwrap(); + *_derivation_path_str = CString::new(s).unwrap().into_raw(); + } +} + +/// Returns the length of the derivation path +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_derivation_path_depth( + derivation_path_ptr: ConstDerivationPathPtr, +) -> usize { + #[allow(clippy::unwrap_used)] + derivation_path_depth(derivation_path_ptr).unwrap() +} + +/// Returns a new derivation path with the last element of the derivation path being increased, e.g. m/1/2 -> m/1/3 +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_derivation_path_next( + derivation_path_ptr: ConstDerivationPathPtr, + derivation_path_out: *mut DerivationPathPtr, +) -> ErrorPtr { + let res = derivation_path_next(derivation_path_ptr, derivation_path_out); + Error::c_api_from(res) +} + +/// Drop `DerivationPath` +#[no_mangle] +pub extern "C" fn ergo_lib_derivation_path_delete(ptr: DerivationPathPtr) { + unsafe { delete_ptr(ptr) } +} diff --git a/bindings/ergo-lib-c/src/lib.rs b/bindings/ergo-lib-c/src/lib.rs index d068985f9..4ce7dd2fd 100644 --- a/bindings/ergo-lib-c/src/lib.rs +++ b/bindings/ergo-lib-c/src/lib.rs @@ -38,6 +38,7 @@ mod reduced; #[cfg(feature = "rest")] mod rest; +mod derivation_path; mod secret_key; mod token; mod transaction; diff --git a/bindings/ergo-lib-ios/Sources/ErgoLib/DerivationPath.swift b/bindings/ergo-lib-ios/Sources/ErgoLib/DerivationPath.swift new file mode 100644 index 000000000..80dcff989 --- /dev/null +++ b/bindings/ergo-lib-ios/Sources/ErgoLib/DerivationPath.swift @@ -0,0 +1,21 @@ +import Foundation +import ErgoLibC + +class DerivationPath { + internal var pointer: DerivationPathPtr + + /// Create DerivationPath from string + /// String should be in the form of: m/44/429/acc'/0/addr + init(derivationPathStr: String) throws { + var ptr: DerivationPathPtr? + let error = derivationPathStr.withCString { cs in + ergo_lib_derivation_path_from_str(cs, &ptr) + } + try checkError(error) + self.pointer = ptr! + } + + deinit { + ergo_lib_derivation_path_delete(self.pointer) + } +} From 5693da5e8812b10ea60b33a6314fa57f2b203d44 Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Thu, 2 May 2024 22:58:39 +0200 Subject: [PATCH 2/5] implement ExtPubKey in c-bindings --- bindings/ergo-lib-c-core/src/ext_pub_key.rs | 87 +++++++++++++++++++++ bindings/ergo-lib-c-core/src/lib.rs | 1 + bindings/ergo-lib-c/src/ext_pub_key.rs | 67 ++++++++++++++++ bindings/ergo-lib-c/src/lib.rs | 1 + 4 files changed, 156 insertions(+) create mode 100644 bindings/ergo-lib-c-core/src/ext_pub_key.rs create mode 100644 bindings/ergo-lib-c/src/ext_pub_key.rs diff --git a/bindings/ergo-lib-c-core/src/ext_pub_key.rs b/bindings/ergo-lib-c-core/src/ext_pub_key.rs new file mode 100644 index 000000000..ff7c2bf60 --- /dev/null +++ b/bindings/ergo-lib-c-core/src/ext_pub_key.rs @@ -0,0 +1,87 @@ +//! Extended Public Key functionality + +use derive_more::{From, Into}; + +use ergo_lib::ergotree_ir::chain::address::Address as InnerAddress; +use ergo_lib::wallet::derivation_path::ChildIndexNormal; +use ergo_lib::wallet::ext_pub_key::{ExtPubKey as InnerExtPubKey, PubKeyBytes}; +use ergo_lib::ArrLength; + +use crate::address::{Address, AddressPtr}; +use crate::derivation_path::ConstDerivationPathPtr; +use crate::util::{const_ptr_as_ref, mut_ptr_as_mut}; +use crate::Error; + +#[derive(From, Into)] +pub struct ExtPubKey(pub InnerExtPubKey); +pub type ExtPubKeyPtr = *mut ExtPubKey; +pub type ConstExtPubKeyPtr = *const ExtPubKey; + +/// Create ExtPubKey from public key bytes, chain code and derivation path +pub unsafe fn ext_pub_key_new( + public_key_bytes: *const u8, + chain_code: *const u8, + derivation_path_ptr: ConstDerivationPathPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> Result<(), Error> { + let ext_pub_key_out = mut_ptr_as_mut(ext_pub_key_out, "ext_pub_key_out")?; + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + let secret_key_bytes = std::slice::from_raw_parts(public_key_bytes, PubKeyBytes::LEN); + let chain_code = + std::slice::from_raw_parts(chain_code, ergo_lib::wallet::ext_pub_key::ChainCode::LEN); + let key = InnerExtPubKey::new( + secret_key_bytes.try_into().map_err(Error::misc)?, + chain_code.try_into().map_err(Error::misc)?, + derivation_path.0.clone(), + ) + .map_err(Error::misc)?; + *ext_pub_key_out = Box::into_raw(Box::new(ExtPubKey(key))); + Ok(()) +} + +/// Derive a new extended public key from the provided index +/// The index is in the form of soft or hardened indices +/// For example: 4 or 4' respectively +pub unsafe fn ext_pub_key_child( + derive_from_key_ptr: ConstExtPubKeyPtr, + child_index: u32, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> Result<(), Error> { + let ext_pub_key = const_ptr_as_ref(derive_from_key_ptr, "derive_from_key_ptr")?; + let ext_pub_key_out = mut_ptr_as_mut(ext_pub_key_out, "ext_pub_key_out")?; + let index = ChildIndexNormal::normal(child_index).map_err(Error::misc)?; + let key = ext_pub_key.0.child(index); + *ext_pub_key_out = Box::into_raw(Box::new(ExtPubKey(key))); + Ok(()) +} + +/// Derive a new extended public key from the derivation path +pub unsafe fn ext_pub_key_derive( + ext_pub_key_ptr: ConstExtPubKeyPtr, + derivation_path_ptr: ConstDerivationPathPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> Result<(), Error> { + let ext_pub_key_ptr = const_ptr_as_ref(ext_pub_key_ptr, "ext_pub_key_ptr")?; + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + let ext_pub_key_out = mut_ptr_as_mut(ext_pub_key_out, "ext_pub_key_out")?; + *ext_pub_key_out = Box::into_raw(Box::new(ExtPubKey( + ext_pub_key_ptr + .0 + .derive(derivation_path.0.clone()) + .map_err(Error::misc)?, + ))); + Ok(()) +} + +/// Get address for extended public key +pub unsafe fn ext_pub_key_address( + ext_pub_key_ptr: ConstExtPubKeyPtr, + address_out: *mut AddressPtr, +) -> Result<(), Error> { + let ext_pub_key_ptr = const_ptr_as_ref(ext_pub_key_ptr, "ext_pub_key_ptr")?; + let address_out = mut_ptr_as_mut(address_out, "address_out")?; + let ext_pub_key: InnerExtPubKey = ext_pub_key_ptr.0.clone(); + let address = InnerAddress::from(ext_pub_key); + *address_out = Box::into_raw(Box::new(Address(address))); + Ok(()) +} diff --git a/bindings/ergo-lib-c-core/src/lib.rs b/bindings/ergo-lib-c-core/src/lib.rs index ce82a5168..f268d5860 100644 --- a/bindings/ergo-lib-c-core/src/lib.rs +++ b/bindings/ergo-lib-c-core/src/lib.rs @@ -40,5 +40,6 @@ pub mod wallet; pub use crate::error::*; pub mod derivation_path; mod error; +pub mod ext_pub_key; #[cfg(feature = "rest")] pub mod rest; diff --git a/bindings/ergo-lib-c/src/ext_pub_key.rs b/bindings/ergo-lib-c/src/ext_pub_key.rs new file mode 100644 index 000000000..d90452079 --- /dev/null +++ b/bindings/ergo-lib-c/src/ext_pub_key.rs @@ -0,0 +1,67 @@ +//! Extended Public Key functionality + +use crate::{delete_ptr, ErrorPtr}; +use ergo_lib_c_core::address::AddressPtr; +use ergo_lib_c_core::derivation_path::ConstDerivationPathPtr; +use ergo_lib_c_core::ext_pub_key::{ + ext_pub_key_address, ext_pub_key_child, ext_pub_key_derive, ext_pub_key_new, ConstExtPubKeyPtr, + ExtPubKeyPtr, +}; +use ergo_lib_c_core::Error; + +/// Create ExtPubKey from public key bytes, chain code and derivation path +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_pub_key_new( + public_key_bytes: *const u8, + chain_code_ptr: *const u8, + derivation_path_ptr: ConstDerivationPathPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> ErrorPtr { + let res = ext_pub_key_new( + public_key_bytes, + chain_code_ptr, + derivation_path_ptr, + ext_pub_key_out, + ); + Error::c_api_from(res) +} + +/// Derive a new extended public key from the provided index +/// The index is in the form of soft or hardened indices +/// For example: 4 or 4' respectively +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_pub_key_child( + derive_from_key_ptr: ConstExtPubKeyPtr, + child_index: u32, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> ErrorPtr { + let res = ext_pub_key_child(derive_from_key_ptr, child_index, ext_pub_key_out); + Error::c_api_from(res) +} + +/// Derive a new extended public key from the derivation path +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_pub_key_derive( + ext_pub_key_ptr: ConstExtPubKeyPtr, + derivation_path_ptr: ConstDerivationPathPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> ErrorPtr { + let res = ext_pub_key_derive(ext_pub_key_ptr, derivation_path_ptr, ext_pub_key_out); + Error::c_api_from(res) +} + +/// Get address for extended public key +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_pub_key_address( + ext_pub_key_ptr: ConstExtPubKeyPtr, + address_out: *mut AddressPtr, +) { + #[allow(clippy::unwrap_used)] + ext_pub_key_address(ext_pub_key_ptr, address_out).unwrap() +} + +/// Drop `ExtPubKey` +#[no_mangle] +pub extern "C" fn ergo_lib_ext_pub_key_delete(ptr: ExtPubKeyPtr) { + unsafe { delete_ptr(ptr) } +} diff --git a/bindings/ergo-lib-c/src/lib.rs b/bindings/ergo-lib-c/src/lib.rs index 4ce7dd2fd..e500bb330 100644 --- a/bindings/ergo-lib-c/src/lib.rs +++ b/bindings/ergo-lib-c/src/lib.rs @@ -39,6 +39,7 @@ mod reduced; mod rest; mod derivation_path; +mod ext_pub_key; mod secret_key; mod token; mod transaction; From f6712f03beb02f4b6b8e1a9598cd2898db187a96 Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Thu, 2 May 2024 22:59:59 +0200 Subject: [PATCH 3/5] add missing functions for ExtSecretKey in c-bindings - change function ext_secret_key_new to accept DerivationPath as parameter --- .../ergo-lib-c-core/src/ext_secret_key.rs | 70 +++++++++++++++++-- bindings/ergo-lib-c/src/ext_secret_key.rs | 54 ++++++++++++-- .../Sources/ErgoLib/ExtSecretKey.swift | 7 +- 3 files changed, 115 insertions(+), 16 deletions(-) diff --git a/bindings/ergo-lib-c-core/src/ext_secret_key.rs b/bindings/ergo-lib-c-core/src/ext_secret_key.rs index eeb431d15..7fadb5a9f 100644 --- a/bindings/ergo-lib-c-core/src/ext_secret_key.rs +++ b/bindings/ergo-lib-c-core/src/ext_secret_key.rs @@ -2,37 +2,41 @@ use std::convert::TryInto; -use crate::util::const_ptr_as_ref; -use crate::{util::mut_ptr_as_mut, Error}; use derive_more::{From, Into}; -use ergo_lib::wallet::derivation_path::{ChildIndex, DerivationPath}; + +use ergo_lib::wallet::derivation_path::ChildIndex; use ergo_lib::wallet::ext_secret_key::{ ChainCode, ExtSecretKey as InnerExtSecretKey, SecretKeyBytes, }; use ergo_lib::wallet::mnemonic::MnemonicSeed; use ergo_lib::ArrLength; +use crate::derivation_path::{ConstDerivationPathPtr, DerivationPath, DerivationPathPtr}; +use crate::ext_pub_key::{ExtPubKey, ExtPubKeyPtr}; +use crate::secret_key::{SecretKey, SecretKeyPtr}; +use crate::util::const_ptr_as_ref; +use crate::{util::mut_ptr_as_mut, Error}; + #[derive(From, Into)] pub struct ExtSecretKey(InnerExtSecretKey); pub type ExtSecretKeyPtr = *mut ExtSecretKey; pub type ConstExtSecretKeyPtr = *const ExtSecretKey; /// Create ExtSecretKey from secret key bytes, chain code and derivation path -/// Derivation path should be a string in the form of: m/44/429/acc'/0/addr pub unsafe fn ext_secret_key_new( secret_key_bytes: *const u8, chain_code: *const u8, - derivation_path: &str, + derivation_path_ptr: ConstDerivationPathPtr, ext_secret_key_out: *mut ExtSecretKeyPtr, ) -> Result<(), Error> { let ext_secret_key_out = mut_ptr_as_mut(ext_secret_key_out, "ext_secret_key_out")?; - let derivation_path: DerivationPath = derivation_path.parse().map_err(Error::misc)?; + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; let secret_key_bytes = std::slice::from_raw_parts(secret_key_bytes, SecretKeyBytes::LEN); let chain_code = std::slice::from_raw_parts(chain_code, ChainCode::LEN); let key = InnerExtSecretKey::new( secret_key_bytes.try_into().map_err(Error::misc)?, chain_code.try_into().map_err(Error::misc)?, - derivation_path, + derivation_path.0.clone(), ) .map_err(Error::misc)?; *ext_secret_key_out = Box::into_raw(Box::new(ExtSecretKey(key))); @@ -67,3 +71,55 @@ pub unsafe fn ext_secret_key_child( *ext_secret_key_out = Box::into_raw(Box::new(ExtSecretKey(key))); Ok(()) } + +/// Get derivation path for extended secret key +pub unsafe fn ext_secret_key_path( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + derivation_path_out: *mut DerivationPathPtr, +) -> Result<(), Error> { + let ext_secret_key = const_ptr_as_ref(ext_secret_key_ptr, "ext_secret_key_ptr")?; + let derivation_path_out = mut_ptr_as_mut(derivation_path_out, "derivation_path_out")?; + *derivation_path_out = Box::into_raw(Box::new(DerivationPath(ext_secret_key.0.path()))); + Ok(()) +} + +/// Get secret key for extended secret key +pub unsafe fn ext_secret_key_get_secret_key( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + secret_key_out: *mut SecretKeyPtr, +) -> Result<(), Error> { + let ext_secret_key = const_ptr_as_ref(ext_secret_key_ptr, "ext_secret_key_ptr")?; + let secret_key_out = mut_ptr_as_mut(secret_key_out, "secret_key_out")?; + *secret_key_out = Box::into_raw(Box::new(SecretKey(ext_secret_key.0.secret_key()))); + Ok(()) +} + +/// The extended public key associated with this secret key +pub unsafe fn ext_secret_key_public_key( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) -> Result<(), Error> { + let ext_secret_key = const_ptr_as_ref(ext_secret_key_ptr, "ext_secret_key_ptr")?; + let ext_pub_key_out = mut_ptr_as_mut(ext_pub_key_out, "ext_pub_key_out")?; + let ext_pub_key = ExtPubKey(ext_secret_key.0.public_key().map_err(Error::misc)?); + *ext_pub_key_out = Box::into_raw(Box::new(ext_pub_key)); + Ok(()) +} + +/// Derive a new extended secret key from the derivation path +pub unsafe fn ext_secret_key_derive( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + derivation_path_ptr: ConstDerivationPathPtr, + ext_secret_key_out: *mut ExtSecretKeyPtr, +) -> Result<(), Error> { + let ext_secret_key = const_ptr_as_ref(ext_secret_key_ptr, "ext_secret_key_ptr")?; + let derivation_path = const_ptr_as_ref(derivation_path_ptr, "derivation_path_ptr")?; + let ext_secret_key_out = mut_ptr_as_mut(ext_secret_key_out, "ext_secret_key_out")?; + *ext_secret_key_out = Box::into_raw(Box::new(ExtSecretKey( + ext_secret_key + .0 + .derive(derivation_path.0.clone()) + .map_err(Error::misc)?, + ))); + Ok(()) +} diff --git a/bindings/ergo-lib-c/src/ext_secret_key.rs b/bindings/ergo-lib-c/src/ext_secret_key.rs index a79833774..43b2edf22 100644 --- a/bindings/ergo-lib-c/src/ext_secret_key.rs +++ b/bindings/ergo-lib-c/src/ext_secret_key.rs @@ -2,7 +2,14 @@ use std::{ffi::CStr, os::raw::c_char}; +use ergo_lib_c_core::derivation_path::{ConstDerivationPathPtr, DerivationPathPtr}; +use ergo_lib_c_core::ext_secret_key::{ + ext_secret_key_derive, ext_secret_key_get_secret_key, ext_secret_key_path, + ext_secret_key_public_key, +}; +use ergo_lib_c_core::secret_key::SecretKeyPtr; use ergo_lib_c_core::{ + ext_pub_key::ExtPubKeyPtr, ext_secret_key::{ ext_secret_key_child, ext_secret_key_derive_master, ext_secret_key_new, ConstExtSecretKeyPtr, ExtSecretKeyPtr, @@ -13,19 +20,17 @@ use ergo_lib_c_core::{ use crate::{delete_ptr, ErrorPtr}; /// Create ExtSecretKey from secret key bytes, chain code and derivation path -/// Derivation path should be a string in the form of: m/44/429/acc'/0/addr #[no_mangle] pub unsafe extern "C" fn ergo_lib_ext_secret_key_new( secret_key_bytes_ptr: *const u8, chain_code_ptr: *const u8, - derivation_path_str: *const c_char, + derivation_path_ptr: ConstDerivationPathPtr, ext_secret_key_out: *mut ExtSecretKeyPtr, ) -> ErrorPtr { - let derivation_path = CStr::from_ptr(derivation_path_str).to_string_lossy(); let res = ext_secret_key_new( secret_key_bytes_ptr, chain_code_ptr, - &derivation_path, + derivation_path_ptr, ext_secret_key_out, ); Error::c_api_from(res) @@ -55,6 +60,47 @@ pub unsafe extern "C" fn ergo_lib_ext_secret_key_child( Error::c_api_from(res) } +/// Get derivation path for extended secret key +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_secret_key_path( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + derivation_path_out: *mut DerivationPathPtr, +) { + #[allow(clippy::unwrap_used)] + ext_secret_key_path(ext_secret_key_ptr, derivation_path_out).unwrap() +} + +/// Get secret key for extended secret key +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_secret_key_get_secret_key( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + secret_key_out: *mut SecretKeyPtr, +) { + #[allow(clippy::unwrap_used)] + ext_secret_key_get_secret_key(ext_secret_key_ptr, secret_key_out).unwrap() +} + +/// The extended public key associated with this secret key +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_secret_key_public_key( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + ext_pub_key_out: *mut ExtPubKeyPtr, +) { + #[allow(clippy::unwrap_used)] + ext_secret_key_public_key(ext_secret_key_ptr, ext_pub_key_out).unwrap() +} + +/// Derive a new extended secret key from the derivation path +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_ext_secret_key_derive( + ext_secret_key_ptr: ConstExtSecretKeyPtr, + derivation_path_ptr: ConstDerivationPathPtr, + ext_secret_key_out: *mut ExtSecretKeyPtr, +) -> ErrorPtr { + let res = ext_secret_key_derive(ext_secret_key_ptr, derivation_path_ptr, ext_secret_key_out); + Error::c_api_from(res) +} + /// Drop `ExtSecretKey` #[no_mangle] pub extern "C" fn ergo_lib_ext_secret_key_delete(ptr: ExtSecretKeyPtr) { diff --git a/bindings/ergo-lib-ios/Sources/ErgoLib/ExtSecretKey.swift b/bindings/ergo-lib-ios/Sources/ErgoLib/ExtSecretKey.swift index 4e9f1fe08..2f908adff 100644 --- a/bindings/ergo-lib-ios/Sources/ErgoLib/ExtSecretKey.swift +++ b/bindings/ergo-lib-ios/Sources/ErgoLib/ExtSecretKey.swift @@ -5,12 +5,9 @@ class ExtSecretKey { internal var pointer: ExtSecretKeyPtr /// Create ExtSecretKey from secret key bytes, chain code and derivation path - /// Derivation path should be a string in the form of: m/44/429/acc'/0/addr - init(secretKeyBytes: [UInt8], chainCodeBytes: [UInt8], derivationPathStr: String) throws { + init(secretKeyBytes: [UInt8], chainCodeBytes: [UInt8], derivationPath: DerivationPath) throws { var ptr: ExtSecretKeyPtr? - let error = derivationPathStr.withCString { cs in - ergo_lib_ext_secret_key_new(secretKeyBytes, chainCodeBytes, cs, &ptr) - } + let error = ergo_lib_ext_secret_key_new(secretKeyBytes, chainCodeBytes, derivationPath.pointer, &ptr) try checkError(error) self.pointer = ptr! } From 4d69b00ce2509fd5b336a34347a72d0e5d8662b9 Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Sat, 4 May 2024 02:53:31 +0200 Subject: [PATCH 4/5] add mnemonic conversion for c-bindings --- bindings/ergo-lib-c-core/src/lib.rs | 1 + bindings/ergo-lib-c-core/src/mnemonic.rs | 14 ++++++++++++++ bindings/ergo-lib-c/src/lib.rs | 1 + bindings/ergo-lib-c/src/mnemonic.rs | 17 +++++++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 bindings/ergo-lib-c-core/src/mnemonic.rs create mode 100644 bindings/ergo-lib-c/src/mnemonic.rs diff --git a/bindings/ergo-lib-c-core/src/lib.rs b/bindings/ergo-lib-c-core/src/lib.rs index f268d5860..2bd6ba51b 100644 --- a/bindings/ergo-lib-c-core/src/lib.rs +++ b/bindings/ergo-lib-c-core/src/lib.rs @@ -41,5 +41,6 @@ pub use crate::error::*; pub mod derivation_path; mod error; pub mod ext_pub_key; +pub mod mnemonic; #[cfg(feature = "rest")] pub mod rest; diff --git a/bindings/ergo-lib-c-core/src/mnemonic.rs b/bindings/ergo-lib-c-core/src/mnemonic.rs new file mode 100644 index 000000000..7daa0cddd --- /dev/null +++ b/bindings/ergo-lib-c-core/src/mnemonic.rs @@ -0,0 +1,14 @@ +use crate::Error; +use ergo_lib::wallet::mnemonic::Mnemonic as InnerMnemonic; + +/// Convert a mnemonic phrase into a mnemonic seed +/// mnemonic_pass is optional and is used to salt the seed +pub unsafe fn mnemonic_to_seed( + mnemonic_phrase: &str, + mnemonic_pass: &str, + output: *mut u8, +) -> Result<(), Error> { + let src: Vec = InnerMnemonic::to_seed(mnemonic_phrase, mnemonic_pass).into(); + std::ptr::copy_nonoverlapping(src.as_ptr(), output, src.len()); + Ok(()) +} diff --git a/bindings/ergo-lib-c/src/lib.rs b/bindings/ergo-lib-c/src/lib.rs index e500bb330..51f55c7ba 100644 --- a/bindings/ergo-lib-c/src/lib.rs +++ b/bindings/ergo-lib-c/src/lib.rs @@ -40,6 +40,7 @@ mod rest; mod derivation_path; mod ext_pub_key; +mod mnemonic; mod secret_key; mod token; mod transaction; diff --git a/bindings/ergo-lib-c/src/mnemonic.rs b/bindings/ergo-lib-c/src/mnemonic.rs new file mode 100644 index 000000000..1c1f4a09c --- /dev/null +++ b/bindings/ergo-lib-c/src/mnemonic.rs @@ -0,0 +1,17 @@ +use ergo_lib_c_core::mnemonic::mnemonic_to_seed; +use std::ffi::CStr; +use std::os::raw::c_char; + +/// Convert a mnemonic phrase into a mnemonic seed +/// mnemonic_pass is optional and is used to salt the seed +#[no_mangle] +pub unsafe extern "C" fn ergo_lib_mnemonic_to_seed( + mnemonic_phrase: *const c_char, + mnemonic_pass: *const c_char, + output: *mut u8, +) { + let mnemonic_phrase = CStr::from_ptr(mnemonic_phrase).to_string_lossy(); + let mnemonic_pass = CStr::from_ptr(mnemonic_pass).to_string_lossy(); + #[allow(clippy::unwrap_used)] + mnemonic_to_seed(&mnemonic_phrase, &mnemonic_pass, output).unwrap() +} From ee5356152d1d8473d4615bc1d06500dbcf66aede Mon Sep 17 00:00:00 2001 From: Manuel Haug Date: Sat, 4 May 2024 16:27:50 +0200 Subject: [PATCH 5/5] add required byte length to documentation --- bindings/ergo-lib-c-core/src/ext_pub_key.rs | 2 ++ bindings/ergo-lib-c-core/src/ext_secret_key.rs | 2 ++ bindings/ergo-lib-c/src/ext_pub_key.rs | 2 ++ bindings/ergo-lib-c/src/ext_secret_key.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/bindings/ergo-lib-c-core/src/ext_pub_key.rs b/bindings/ergo-lib-c-core/src/ext_pub_key.rs index ff7c2bf60..6ae7ae701 100644 --- a/bindings/ergo-lib-c-core/src/ext_pub_key.rs +++ b/bindings/ergo-lib-c-core/src/ext_pub_key.rs @@ -18,6 +18,8 @@ pub type ExtPubKeyPtr = *mut ExtPubKey; pub type ConstExtPubKeyPtr = *const ExtPubKey; /// Create ExtPubKey from public key bytes, chain code and derivation path +/// public_key_bytes needs to be the length of PubKeyBytes::LEN (33 bytes) +/// chain_code needs to be the length of ChainCode::LEN (32 bytes) pub unsafe fn ext_pub_key_new( public_key_bytes: *const u8, chain_code: *const u8, diff --git a/bindings/ergo-lib-c-core/src/ext_secret_key.rs b/bindings/ergo-lib-c-core/src/ext_secret_key.rs index 7fadb5a9f..ad4287834 100644 --- a/bindings/ergo-lib-c-core/src/ext_secret_key.rs +++ b/bindings/ergo-lib-c-core/src/ext_secret_key.rs @@ -23,6 +23,8 @@ pub type ExtSecretKeyPtr = *mut ExtSecretKey; pub type ConstExtSecretKeyPtr = *const ExtSecretKey; /// Create ExtSecretKey from secret key bytes, chain code and derivation path +/// secret_key_bytes needs to be the length of SecretKeyBytes::LEN (32 bytes) +/// chain_code needs to be the length of ChainCode::LEN (32 bytes) pub unsafe fn ext_secret_key_new( secret_key_bytes: *const u8, chain_code: *const u8, diff --git a/bindings/ergo-lib-c/src/ext_pub_key.rs b/bindings/ergo-lib-c/src/ext_pub_key.rs index d90452079..3e44931aa 100644 --- a/bindings/ergo-lib-c/src/ext_pub_key.rs +++ b/bindings/ergo-lib-c/src/ext_pub_key.rs @@ -10,6 +10,8 @@ use ergo_lib_c_core::ext_pub_key::{ use ergo_lib_c_core::Error; /// Create ExtPubKey from public key bytes, chain code and derivation path +/// public_key_bytes needs to be the length of PubKeyBytes::LEN (33 bytes) +/// chain_code_ptr needs to be the length of ChainCode::LEN (32 bytes) #[no_mangle] pub unsafe extern "C" fn ergo_lib_ext_pub_key_new( public_key_bytes: *const u8, diff --git a/bindings/ergo-lib-c/src/ext_secret_key.rs b/bindings/ergo-lib-c/src/ext_secret_key.rs index 43b2edf22..93a222bf1 100644 --- a/bindings/ergo-lib-c/src/ext_secret_key.rs +++ b/bindings/ergo-lib-c/src/ext_secret_key.rs @@ -20,6 +20,8 @@ use ergo_lib_c_core::{ use crate::{delete_ptr, ErrorPtr}; /// Create ExtSecretKey from secret key bytes, chain code and derivation path +/// secret_key_bytes_ptr needs to be the length of SecretKeyBytes::LEN (32 bytes) +/// chain_code_ptr needs to be the length of ChainCode::LEN (32 bytes) #[no_mangle] pub unsafe extern "C" fn ergo_lib_ext_secret_key_new( secret_key_bytes_ptr: *const u8,