keyfork-derive-util: add doctests/examples
This commit is contained in:
parent
f2250d00e1
commit
5096df993e
|
@ -68,14 +68,27 @@ where
|
||||||
/// mnemonic, but may take 16-byte seeds.
|
/// mnemonic, but may take 16-byte seeds.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
|
||||||
/// The method performs unchecked `try_into()` operations on a constant-sized slice.
|
/// The method performs unchecked `try_into()` operations on a constant-sized slice.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
|
||||||
/// An error may be returned if:
|
/// An error may be returned if:
|
||||||
/// * The given seed had an incorrect length.
|
/// * The given seed had an incorrect length.
|
||||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
|
pub fn new(seed: impl AsRef<[u8]>) -> Result<Self> {
|
||||||
Self::new_internal(seed.as_ref())
|
Self::new_internal(seed.as_ref())
|
||||||
}
|
}
|
||||||
|
@ -104,6 +117,23 @@ where
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The function may error if a private key can't be created from the seed.
|
/// The function may error if a private key can't be created from the seed.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let key: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let chain_code: &[u8; 32] = //
|
||||||
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
pub fn new_from_parts(seed: &[u8], depth: u8, chain_code: [u8; 32]) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
private_key: K::from_bytes(seed.try_into()?),
|
private_key: K::from_bytes(seed.try_into()?),
|
||||||
|
@ -113,26 +143,123 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the [`PrivateKey`].
|
/// Returns a reference to the [`PrivateKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::PrivateKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let key: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let chain_code: &[u8; 32] = //
|
||||||
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
||||||
|
/// assert_eq!(xprv.private_key(), &PrivateKey::from_bytes(key));
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn private_key(&self) -> &K {
|
pub fn private_key(&self) -> &K {
|
||||||
&self.private_key
|
&self.private_key
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an [`ExtendedPublicKey`] for the current [`PrivateKey`].
|
/// Create an [`ExtendedPublicKey`] for the current [`PrivateKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::PrivateKey as _,
|
||||||
|
/// # public_key::PublicKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// # let known_key: [u8; 33] = [
|
||||||
|
/// # 0, 242, 26, 9, 159, 68, 199, 0, 206, 71, 248,
|
||||||
|
/// # 102, 201, 210, 159, 219, 222, 42, 201, 44, 196, 27,
|
||||||
|
/// # 90, 221, 80, 85, 135, 79, 39, 253, 223, 35, 251
|
||||||
|
/// # ];
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||||
|
/// let xpub = xprv.extended_public_key();
|
||||||
|
/// assert_eq!(known_key, xpub.public_key().to_bytes());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn extended_public_key(&self) -> ExtendedPublicKey<K::PublicKey> {
|
pub fn extended_public_key(&self) -> ExtendedPublicKey<K::PublicKey> {
|
||||||
ExtendedPublicKey::new(self.public_key(), self.chain_code)
|
ExtendedPublicKey::new_from_parts(self.public_key(), self.depth, self.chain_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a public key for the current [`PrivateKey`].
|
/// Return a public key for the current [`PrivateKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::PrivateKey as _,
|
||||||
|
/// # public_key::PublicKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||||
|
/// let pubkey = xprv.public_key();
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn public_key(&self) -> K::PublicKey {
|
pub fn public_key(&self) -> K::PublicKey {
|
||||||
self.private_key.public_key()
|
self.private_key.public_key()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current depth.
|
/// Returns the current depth.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let key: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let chain_code: &[u8; 32] = //
|
||||||
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
||||||
|
/// assert_eq!(xprv.depth(), 4);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn depth(&self) -> u8 {
|
pub fn depth(&self) -> u8 {
|
||||||
self.depth
|
self.depth
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a copy of the current chain code.
|
/// Returns a copy of the current chain code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let key: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let chain_code: &[u8; 32] = //
|
||||||
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// let xprv = ExtendedPrivateKey::<PrivateKey>::new_from_parts(key, 4, *chain_code)?;
|
||||||
|
/// assert_eq!(chain_code, &xprv.chain_code());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn chain_code(&self) -> [u8; 32] {
|
pub fn chain_code(&self) -> [u8; 32] {
|
||||||
self.chain_code
|
self.chain_code
|
||||||
}
|
}
|
||||||
|
@ -140,9 +267,29 @@ where
|
||||||
/// Derive a child using the given [`DerivationPath`].
|
/// Derive a child using the given [`DerivationPath`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
|
||||||
/// An error may be returned under the same circumstances as
|
/// An error may be returned under the same circumstances as
|
||||||
/// [`ExtendedPrivateKey::derive_child`].
|
/// [`ExtendedPrivateKey::derive_child`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||||
|
/// let path = DerivationPath::default()
|
||||||
|
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, false)?);
|
||||||
|
/// let derived_xprv = root_xprv.derive_path(&path)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> {
|
pub fn derive_path(&self, path: &DerivationPath) -> Result<Self> {
|
||||||
if path.path.is_empty() {
|
if path.path.is_empty() {
|
||||||
Ok(self.clone())
|
Ok(self.clone())
|
||||||
|
@ -165,6 +312,33 @@ where
|
||||||
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
||||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||||
/// * Deriving a child key fails. Check the documentation for your [`PrivateKey`].
|
/// * Deriving a child key fails. Check the documentation for your [`PrivateKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn check_empty(p: &ExtendedPrivateKey<PrivateKey>) -> Result<(), std::io::Error> {
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let root_xprv = ExtendedPrivateKey::<PrivateKey>::new(seed)?;
|
||||||
|
/// let bip44_wallet = DerivationPath::default()
|
||||||
|
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, false)?);
|
||||||
|
/// let change_xprv = root_xprv.derive_path(&bip44_wallet)?;
|
||||||
|
/// for account in (0..20).map(|i| DerivationIndex::new(i, false).unwrap()) {
|
||||||
|
/// let account_xprv = change_xprv.derive_child(&account)?;
|
||||||
|
/// check_empty(&account_xprv)?;
|
||||||
|
/// }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
||||||
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
let depth = self.depth.checked_add(1).ok_or(Error::Depth)?;
|
||||||
|
|
||||||
|
|
|
@ -44,15 +44,51 @@ where
|
||||||
K: PublicKey,
|
K: PublicKey,
|
||||||
{
|
{
|
||||||
/// Create a new [`ExtendedPublicKey`] from previously known values.
|
/// Create a new [`ExtendedPublicKey`] from previously known values.
|
||||||
pub fn new(public_key: K, chain_code: ChainCode) -> Self {
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::PublicKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let key: &[u8; 33] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let chain_code: &[u8; 32] = //
|
||||||
|
/// # b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// let pubkey = PublicKey::from_bytes(key);
|
||||||
|
/// let xpub = ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn new_from_parts(public_key: K, depth: u8, chain_code: ChainCode) -> Self {
|
||||||
Self {
|
Self {
|
||||||
public_key,
|
public_key,
|
||||||
depth: 0,
|
depth,
|
||||||
chain_code,
|
chain_code,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the internal [`PublicKey`].
|
/// Return the internal [`PublicKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::PublicKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// # let pubkey = PublicKey::from_bytes(key);
|
||||||
|
/// let xpub = //
|
||||||
|
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||||
|
/// let pubkey = xpub.public_key();
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn public_key(&self) -> &K {
|
pub fn public_key(&self) -> &K {
|
||||||
&self.public_key
|
&self.public_key
|
||||||
}
|
}
|
||||||
|
@ -70,6 +106,25 @@ where
|
||||||
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
/// * The depth exceeds the maximum depth [`u8::MAX`].
|
||||||
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
/// * A `HmacSha512` can't be constructed - this should be impossible.
|
||||||
/// * Deriving a child key fails. Check the documentation for your [`PublicKey`].
|
/// * Deriving a child key fails. Check the documentation for your [`PublicKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # public_key::PublicKey as _,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// # let key: &[u8; 33] = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// # let chain_code: &[u8; 32] = b"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||||||
|
/// # let pubkey = PublicKey::from_bytes(key);
|
||||||
|
/// let xpub = //
|
||||||
|
/// # ExtendedPublicKey::<PublicKey>::new_from_parts(pubkey, 0, *chain_code);
|
||||||
|
/// let index = DerivationIndex::new(0, false)?;
|
||||||
|
/// let child = xpub.derive_child(&index)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
pub fn derive_child(&self, index: &DerivationIndex) -> Result<Self> {
|
||||||
if index.is_hardened() {
|
if index.is_hardened() {
|
||||||
return Err(Error::HardenedIndex);
|
return Err(Error::HardenedIndex);
|
||||||
|
|
|
@ -23,8 +23,20 @@ impl DerivationIndex {
|
||||||
/// Creates a new [`DerivationIndex`].
|
/// Creates a new [`DerivationIndex`].
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
|
||||||
/// Returns an error if the index is larger than the hardened flag.
|
/// Returns an error if the index is larger than the hardened flag.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::*;
|
||||||
|
/// let bip44 = DerivationIndex::new(44, true);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Using a derivation index that is higher than 2^31 returns an error:
|
||||||
|
///
|
||||||
|
/// ```rust,should_panic
|
||||||
|
/// # use keyfork_derive_util::*;
|
||||||
|
/// let too_high = DerivationIndex::new(u32::MAX, true).unwrap();
|
||||||
|
/// ```
|
||||||
pub const fn new(index: u32, hardened: bool) -> Result<Self> {
|
pub const fn new(index: u32, hardened: bool) -> Result<Self> {
|
||||||
if index & (0b1 << 31) > 0 {
|
if index & (0b1 << 31) > 0 {
|
||||||
return Err(Error::IndexTooLarge(index));
|
return Err(Error::IndexTooLarge(index));
|
||||||
|
@ -46,6 +58,13 @@ impl DerivationIndex {
|
||||||
|
|
||||||
/// Return the internal derivation index. Note that if the derivation index is hardened, the
|
/// Return the internal derivation index. Note that if the derivation index is hardened, the
|
||||||
/// highest bit will be set, and the value can't be used to create a new derivation index.
|
/// highest bit will be set, and the value can't be used to create a new derivation index.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::*;
|
||||||
|
/// assert_eq!(DerivationIndex::new(44, true).unwrap().inner(), 2147483692);
|
||||||
|
/// assert_eq!(DerivationIndex::new(200, false).unwrap().inner(), 200);
|
||||||
|
/// ```
|
||||||
pub fn inner(&self) -> u32 {
|
pub fn inner(&self) -> u32 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
@ -54,7 +73,15 @@ impl DerivationIndex {
|
||||||
self.0.to_be_bytes()
|
self.0.to_be_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not the index is hardened, allowing deriving the key from a known parent key.
|
/// Whether or not the index is hardened, allowing deriving the key from a known parent public
|
||||||
|
/// key.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::*;
|
||||||
|
/// assert_eq!(DerivationIndex::new(0, true).unwrap().is_hardened(), true);
|
||||||
|
/// assert_eq!(DerivationIndex::new(0, false).unwrap().is_hardened(), false);
|
||||||
|
/// ```
|
||||||
pub fn is_hardened(&self) -> bool {
|
pub fn is_hardened(&self) -> bool {
|
||||||
self.0 & (0b1 << 31) != 0
|
self.0 & (0b1 << 31) != 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,24 @@ impl DerivationPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append an index to the path, returning self to allow chaining method calls.
|
/// Append an index to the path, returning self to allow chaining method calls.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::*;
|
||||||
|
/// # fn discover_wallet(_p: DerivationPath) -> Result<bool, std::io::Error> { Ok(true) }
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let account = 0;
|
||||||
|
/// let path = DerivationPath::default()
|
||||||
|
/// .chain_push(DerivationIndex::new(44, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(0, true)?)
|
||||||
|
/// .chain_push(DerivationIndex::new(account, true)?);
|
||||||
|
/// let mut has_wallet = false;
|
||||||
|
/// for index in (0..20).map(|i| DerivationIndex::new(i, true).unwrap()) {
|
||||||
|
/// has_wallet = has_wallet || discover_wallet(path.clone().chain_push(index))?;
|
||||||
|
/// }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
pub fn chain_push(mut self, index: DerivationIndex) -> Self {
|
pub fn chain_push(mut self, index: DerivationIndex) -> Self {
|
||||||
self.path.push(index);
|
self.path.push(index);
|
||||||
self
|
self
|
||||||
|
|
|
@ -13,9 +13,32 @@ pub trait PrivateKey: Sized {
|
||||||
type Err: std::error::Error;
|
type Err: std::error::Error;
|
||||||
|
|
||||||
/// Create a Self from bytes.
|
/// Create a Self from bytes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// let key_data: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||||
|
/// ```
|
||||||
fn from_bytes(b: &PrivateKeyBytes) -> Self;
|
fn from_bytes(b: &PrivateKeyBytes) -> Self;
|
||||||
|
|
||||||
/// Convert a &Self to bytes.
|
/// Convert a &Self to bytes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// let key_data: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||||
|
/// assert_eq!(key_data, &private_key.to_bytes());
|
||||||
|
/// ```
|
||||||
fn to_bytes(&self) -> PrivateKeyBytes;
|
fn to_bytes(&self) -> PrivateKeyBytes;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -27,12 +50,35 @@ pub trait PrivateKey: Sized {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed".
|
/// The initial key for BIP-0032 and SLIP-0010 derivation, such as secp256k1's "Bitcoin seed".
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// assert_eq!(OurPrivateKey::key(), "testing seed");
|
||||||
|
/// ```
|
||||||
fn key() -> &'static str;
|
fn key() -> &'static str;
|
||||||
|
|
||||||
/// Generate a [`Self::PublicKey`].
|
/// Generate a [`Self::PublicKey`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// let key_data: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||||
|
/// let public_key = private_key.public_key();
|
||||||
|
/// ```
|
||||||
fn public_key(&self) -> Self::PublicKey;
|
fn public_key(&self) -> Self::PublicKey;
|
||||||
|
|
||||||
/// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`.
|
/// Derive a child [`PrivateKey`] with given `PrivateKeyBytes`. The implementation of
|
||||||
|
/// derivation is algorithm-specific and a specification should be consulted when implementing
|
||||||
|
/// this method.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
|
@ -129,3 +175,48 @@ impl PrivateKey for ed25519_dalek::SigningKey {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::public_key::TestPublicKey;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct TestPrivateKey {
|
||||||
|
key: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestPrivateKey {
|
||||||
|
pub(crate) fn public_key(&self) -> TestPublicKey {
|
||||||
|
let mut bytes = [0u8; 33];
|
||||||
|
for (i, byte) in self.key.iter().enumerate() {
|
||||||
|
bytes[i + 1] = byte ^ 0xFF;
|
||||||
|
}
|
||||||
|
TestPublicKey { key: bytes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrivateKey for TestPrivateKey {
|
||||||
|
type PublicKey = TestPublicKey;
|
||||||
|
type Err = PrivateKeyError;
|
||||||
|
|
||||||
|
fn from_bytes(b: &PrivateKeyBytes) -> Self {
|
||||||
|
Self {
|
||||||
|
key: *b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_bytes(&self) -> PrivateKeyBytes {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn key() -> &'static str {
|
||||||
|
"testing seed"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> Self::PublicKey {
|
||||||
|
self.public_key()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_child(&self, other: &PrivateKeyBytes) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self { key: *other })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,9 +19,23 @@ pub trait PublicKey: Sized {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// Convert a &Self to bytes.
|
/// Convert a &Self to bytes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// let key_data: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||||
|
/// let public_key_bytes = private_key.public_key().to_bytes();
|
||||||
|
/// ```
|
||||||
fn to_bytes(&self) -> PublicKeyBytes;
|
fn to_bytes(&self) -> PublicKeyBytes;
|
||||||
|
|
||||||
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`.
|
/// Derive a child [`PublicKey`] with given `PrivateKeyBytes`. The implementation of
|
||||||
|
/// derivation is algorithm-specific and a specification should be consulted when implementing
|
||||||
|
/// this method.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
|
@ -31,6 +45,18 @@ pub trait PublicKey: Sized {
|
||||||
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
|
fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Self::Err>;
|
||||||
|
|
||||||
/// Create a BIP-0032/SLIP-0010 fingerprint from the public key.
|
/// Create a BIP-0032/SLIP-0010 fingerprint from the public key.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # private_key::TestPrivateKey as OurPrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// let key_data: &[u8; 32] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let private_key = OurPrivateKey::from_bytes(key_data);
|
||||||
|
/// let fingerprint = private_key.public_key().fingerprint();
|
||||||
|
/// ```
|
||||||
fn fingerprint(&self) -> [u8; 4] {
|
fn fingerprint(&self) -> [u8; 4] {
|
||||||
let hash = Sha256::new().chain_update(self.to_bytes()).finalize();
|
let hash = Sha256::new().chain_update(self.to_bytes()).finalize();
|
||||||
let hash = Ripemd160::new().chain_update(hash).finalize();
|
let hash = Ripemd160::new().chain_update(hash).finalize();
|
||||||
|
@ -112,3 +138,32 @@ impl PublicKey for VerifyingKey {
|
||||||
Err(Self::Err::DerivationUnsupported)
|
Err(Self::Err::DerivationUnsupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestPublicKey {
|
||||||
|
pub(crate) key: [u8; 33],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestPublicKey {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn from_bytes(b: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
key: b.try_into().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PublicKey for TestPublicKey {
|
||||||
|
type Err = PublicKeyError;
|
||||||
|
|
||||||
|
fn to_bytes(&self) -> PublicKeyBytes {
|
||||||
|
self.key
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_child(&self, _other: PrivateKeyBytes) -> Result<Self, Self::Err> {
|
||||||
|
// whatever it takes for tests to pass...
|
||||||
|
Ok(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
#![allow(clippy::match_wildcard_for_single_variants)]
|
#![allow(clippy::match_wildcard_for_single_variants)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extended_key::private_key::Error as XPrvError, DerivationPath, ExtendedPrivateKey, PrivateKey,
|
extended_key::private_key::Error as XPrvError,
|
||||||
|
private_key::{PrivateKey, TestPrivateKey},
|
||||||
|
DerivationPath, ExtendedPrivateKey,
|
||||||
};
|
};
|
||||||
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
use keyfork_mnemonic_util::{Mnemonic, MnemonicGenerationError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -30,11 +32,14 @@ pub type Result<T, E = DerivationError> = std::result::Result<T, E>;
|
||||||
/// The algorithm to derive a key for. The choice of algorithm will result in a different resulting
|
/// The algorithm to derive a key for. The choice of algorithm will result in a different resulting
|
||||||
/// derivation.
|
/// derivation.
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum DerivationAlgorithm {
|
pub enum DerivationAlgorithm {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
Ed25519,
|
Ed25519,
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
Secp256k1,
|
Secp256k1,
|
||||||
|
#[doc(hidden)]
|
||||||
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerivationAlgorithm {
|
impl DerivationAlgorithm {
|
||||||
|
@ -42,7 +47,7 @@ impl DerivationAlgorithm {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||||
pub fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
|
fn derive(&self, seed: Vec<u8>, path: &DerivationPath) -> Result<DerivationResponse> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
Self::Ed25519 => {
|
Self::Ed25519 => {
|
||||||
|
@ -62,6 +67,14 @@ impl DerivationAlgorithm {
|
||||||
&derived_key,
|
&derived_key,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Self::Internal => {
|
||||||
|
let key = ExtendedPrivateKey::<TestPrivateKey>::new(seed)?;
|
||||||
|
let derived_key = key.derive_path(path)?;
|
||||||
|
Ok(DerivationResponse::with_algo_and_xprv(
|
||||||
|
self.clone(),
|
||||||
|
&derived_key,
|
||||||
|
))
|
||||||
|
}
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
_ => Err(DerivationError::Algorithm),
|
_ => Err(DerivationError::Algorithm),
|
||||||
}
|
}
|
||||||
|
@ -89,6 +102,23 @@ pub struct DerivationRequest {
|
||||||
|
|
||||||
impl DerivationRequest {
|
impl DerivationRequest {
|
||||||
/// Create a new derivation request.
|
/// Create a new derivation request.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # request::*,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let algo: DerivationAlgorithm = //
|
||||||
|
/// # DerivationAlgorithm::Internal;
|
||||||
|
/// let path: DerivationPath = //
|
||||||
|
/// # DerivationPath::default();
|
||||||
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
pub fn new(algorithm: DerivationAlgorithm, path: &DerivationPath) -> Self {
|
pub fn new(algorithm: DerivationAlgorithm, path: &DerivationPath) -> Self {
|
||||||
Self {
|
Self {
|
||||||
algorithm,
|
algorithm,
|
||||||
|
@ -97,6 +127,24 @@ impl DerivationRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the path of the derivation request.
|
/// Return the path of the derivation request.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # request::*,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let algo: DerivationAlgorithm = //
|
||||||
|
/// # DerivationAlgorithm::Internal;
|
||||||
|
/// let path: DerivationPath = //
|
||||||
|
/// # DerivationPath::default();
|
||||||
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
/// assert_eq!(&path, request.path());
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
pub fn path(&self) -> &DerivationPath {
|
pub fn path(&self) -> &DerivationPath {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
@ -105,6 +153,29 @@ impl DerivationRequest {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # request::*,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let mnemonic: keyfork_mnemonic_util::Mnemonic = //
|
||||||
|
/// # keyfork_mnemonic_util::Mnemonic::from_entropy(
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||||
|
/// # Default::default(),
|
||||||
|
/// # )?;
|
||||||
|
/// let algo: DerivationAlgorithm = //
|
||||||
|
/// # DerivationAlgorithm::Internal;
|
||||||
|
/// let path: DerivationPath = //
|
||||||
|
/// # DerivationPath::default();
|
||||||
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
/// let response = request.derive_with_mnemonic(&mnemonic)?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
pub fn derive_with_mnemonic(&self, mnemonic: &Mnemonic) -> Result<DerivationResponse> {
|
||||||
// TODO: passphrase support and/or store passphrase within mnemonic
|
// TODO: passphrase support and/or store passphrase within mnemonic
|
||||||
self.derive_with_master_seed(mnemonic.seed(None)?)
|
self.derive_with_master_seed(mnemonic.seed(None)?)
|
||||||
|
@ -114,6 +185,26 @@ impl DerivationRequest {
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// The method may error if the derivation fails or if the algorithm is not supported.
|
/// The method may error if the derivation fails or if the algorithm is not supported.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```rust
|
||||||
|
/// # use keyfork_derive_util::{
|
||||||
|
/// # *,
|
||||||
|
/// # request::*,
|
||||||
|
/// # public_key::TestPublicKey as PublicKey,
|
||||||
|
/// # private_key::TestPrivateKey as PrivateKey,
|
||||||
|
/// # };
|
||||||
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
/// let seed: &[u8; 64] = //
|
||||||
|
/// # b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
||||||
|
/// let algo: DerivationAlgorithm = //
|
||||||
|
/// # DerivationAlgorithm::Internal;
|
||||||
|
/// let path: DerivationPath = //
|
||||||
|
/// # DerivationPath::default();
|
||||||
|
/// let request = DerivationRequest::new(algo, &path);
|
||||||
|
/// let response = request.derive_with_master_seed(seed.to_vec())?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> {
|
pub fn derive_with_master_seed(&self, seed: Vec<u8>) -> Result<DerivationResponse> {
|
||||||
self.algorithm.derive(seed, &self.path)
|
self.algorithm.derive(seed, &self.path)
|
||||||
}
|
}
|
||||||
|
@ -137,7 +228,7 @@ pub struct DerivationResponse {
|
||||||
|
|
||||||
impl DerivationResponse {
|
impl DerivationResponse {
|
||||||
/// Create a [`DerivationResponse`] with the given values.
|
/// Create a [`DerivationResponse`] with the given values.
|
||||||
pub fn with_algo_and_xprv<T: PrivateKey + Clone>(
|
fn with_algo_and_xprv<T: PrivateKey + Clone>(
|
||||||
algorithm: DerivationAlgorithm,
|
algorithm: DerivationAlgorithm,
|
||||||
xprv: &ExtendedPrivateKey<T>,
|
xprv: &ExtendedPrivateKey<T>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
Loading…
Reference in New Issue