Repository: ergoplatform/sigma-rust (language: rust)

Issue:
Add missing functions/implementations of ExtSecretKey, ExtPubKey and DerivationPath in c-bindings
Current c-bindings implementation is missing some useful functions for `ExtSecretKey` like:

- get derivation path from `ExtSecretKey`
- get secret key from `ExtSecretKey`
- get public key from `ExtSecretKey`
- derive a new `ExtSerectKey` based on derivation path

Currently the function `ext_secret_key_new` in `bindings/ergo-lib-c-core/src/ext_secret_key.rs` accepts a string as derivation path but should be `DerivationPath` which needs to be implemented first since it is missing (see below).

Following types are completely missing in c-bindings:

- `ExtPubKey`
- `DerivationPath`


Discussion / hints:

Fix patch:
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::<Result<Vec<ChildIndexNormal>, 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<String, Error> {
+    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<usize, Error> {
+    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/ext_pub_key.rs b/bindings/ergo-lib-c-core/src/ext_pub_key.rs
new file mode 100644
index 000000000..6ae7ae701
--- /dev/null
+++ b/bindings/ergo-lib-c-core/src/ext_pub_key.rs
@@ -0,0 +1,89 @@
+//! 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
+/// 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,
+    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/ext_secret_key.rs b/bindings/ergo-lib-c-core/src/ext_secret_key.rs
index eeb431d15..ad4287834 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,43 @@
 
 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
+/// 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,
-    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 +73,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-core/src/lib.rs b/bindings/ergo-lib-c-core/src/lib.rs
index 87d6ad7c3..2bd6ba51b 100644
--- a/bindings/ergo-lib-c-core/src/lib.rs
+++ b/bindings/ergo-lib-c-core/src/lib.rs
@@ -38,6 +38,9 @@ pub mod tx_builder;
 pub mod util;
 pub mod wallet;
 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<u8> = 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/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/ext_pub_key.rs b/bindings/ergo-lib-c/src/ext_pub_key.rs
new file mode 100644
index 000000000..3e44931aa
--- /dev/null
+++ b/bindings/ergo-lib-c/src/ext_pub_key.rs
@@ -0,0 +1,69 @@
+//! 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
+/// 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,
+    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/ext_secret_key.rs b/bindings/ergo-lib-c/src/ext_secret_key.rs
index a79833774..93a222bf1 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,19 @@ 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
+/// 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,
     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 +62,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-c/src/lib.rs b/bindings/ergo-lib-c/src/lib.rs
index d068985f9..51f55c7ba 100644
--- a/bindings/ergo-lib-c/src/lib.rs
+++ b/bindings/ergo-lib-c/src/lib.rs
@@ -38,6 +38,9 @@ mod reduced;
 #[cfg(feature = "rest")]
 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()
+}
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)
+    }
+}
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!
     }


Test patch: