use super::error::Bip32Error;
use std::{
convert::TryFrom,
iter::{FromIterator, IntoIterator},
slice::Iter,
str::FromStr,
};
pub const DEFAULT_DERIVATION_PATH_PREFIX: &str = "m/44'/60'/0'/0/";
pub const DEFAULT_DERIVATION_PATH: &str = "m/44'/60'/0'/0/0";
pub const BIP32_HARDEN: u32 = 0x8000_0000;
fn try_parse_index(s: &str) -> Result<u32, Bip32Error> {
let mut index_str = s.to_owned();
let harden = if s.ends_with('\'') || s.ends_with('h') {
index_str.pop();
true
} else {
false
};
index_str
.parse::<u32>()
.map(|v| if harden { harden_index(v) } else { v })
.map_err(|_| Bip32Error::MalformattedDerivation(s.to_owned()))
}
fn encode_index(idx: u32, harden: char) -> String {
let mut s = (idx % BIP32_HARDEN).to_string();
if idx >= BIP32_HARDEN {
s.push(harden);
}
s
}
pub const fn harden_index(index: u32) -> u32 {
index + BIP32_HARDEN
}
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct DerivationPath(Vec<u32>);
#[cfg(feature = "serde")]
impl serde::Serialize for DerivationPath {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.derivation_string())
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for DerivationPath {
fn deserialize<D>(deserializer: D) -> Result<DerivationPath, D::Error>
where
D: serde::Deserializer<'de>,
{
let s: &str = serde::Deserialize::deserialize(deserializer)?;
s.parse::<DerivationPath>().map_err(|e| serde::de::Error::custom(e.to_string()))
}
}
impl DerivationPath {
pub fn custom_string(&self, root: &str, joiner: char, harden: char) -> String {
std::iter::once(root.to_owned())
.chain(self.0.iter().map(|s| encode_index(*s, harden)))
.collect::<Vec<String>>()
.join(&joiner.to_string())
}
pub fn last(&self) -> Option<&u32> {
self.0.last()
}
pub fn derivation_string(&self) -> String {
self.custom_string("m", '/', '\'')
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter<'a>(&'a self) -> Iter<'a, u32> {
self.0.iter()
}
pub fn starts_with(&self, other: &Self) -> bool {
self.0.starts_with(&other.0)
}
pub fn without_prefix(&self, prefix: &Self) -> Option<DerivationPath> {
if !self.starts_with(prefix) {
None
} else {
Some(self.0[prefix.len()..].to_vec().into())
}
}
pub fn last_hardened(&self) -> (usize, Option<u32>) {
match self.iter().rev().position(|v| *v >= BIP32_HARDEN) {
Some(rev_pos) => {
let pos = self.len() - rev_pos - 1;
(pos, Some(self.0[pos]))
}
None => (0, None),
}
}
pub fn resized(&self, size: usize, pad_with: u32) -> Self {
let mut child = self.clone();
child.0.resize(size, pad_with);
child
}
pub fn extended(&self, idx: u32) -> Self {
let mut child = self.clone();
child.0.push(idx);
child
}
}
impl From<&DerivationPath> for DerivationPath {
fn from(v: &DerivationPath) -> Self {
v.clone()
}
}
impl From<Vec<u32>> for DerivationPath {
fn from(v: Vec<u32>) -> Self {
Self(v)
}
}
impl From<&Vec<u32>> for DerivationPath {
fn from(v: &Vec<u32>) -> Self {
Self(v.clone())
}
}
impl From<&[u32]> for DerivationPath {
fn from(v: &[u32]) -> Self {
Self(Vec::from(v))
}
}
impl TryFrom<u32> for DerivationPath {
type Error = Bip32Error;
fn try_from(v: u32) -> Result<Self, Self::Error> {
Ok(Self(vec![v]))
}
}
impl TryFrom<&str> for DerivationPath {
type Error = Bip32Error;
fn try_from(v: &str) -> Result<Self, Self::Error> {
v.parse()
}
}
impl FromIterator<u32> for DerivationPath {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = u32>,
{
Vec::from_iter(iter).into()
}
}
impl FromStr for DerivationPath {
type Err = Bip32Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.split('/')
.filter(|v| v != &"m")
.map(try_parse_index)
.collect::<Result<Vec<u32>, Bip32Error>>()
.map(|v| v.into())
.map_err(|_| Bip32Error::MalformattedDerivation(s.to_owned()))
}
}