Skip to main content

kaspa_bip32/
derivation_path.rs

1//! Derivation paths
2
3use crate::{ChildNumber, Error, Result};
4//use alloc::vec::{self, Vec};
5use core::{
6    fmt::{self, Display},
7    str::FromStr,
8};
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
10
11/// Prefix for all derivation paths.
12const PREFIX: &str = "m";
13
14/// Derivation paths within a hierarchical keyspace.
15#[derive(Clone, Debug, Default, Eq, PartialEq)]
16pub struct DerivationPath {
17    path: Vec<ChildNumber>,
18}
19
20impl<'de> Deserialize<'de> for DerivationPath {
21    fn deserialize<D>(deserializer: D) -> std::result::Result<DerivationPath, D::Error>
22    where
23        D: Deserializer<'de>,
24    {
25        struct DerivationPathVisitor;
26        impl<'de> de::Visitor<'de> for DerivationPathVisitor {
27            type Value = DerivationPath;
28            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
29                formatter.write_str("a string containing list of permissions separated by a '+'")
30            }
31
32            fn visit_str<E>(self, value: &str) -> std::result::Result<Self::Value, E>
33            where
34                E: de::Error,
35            {
36                DerivationPath::from_str(value).map_err(|err| de::Error::custom(err.to_string()))
37            }
38            fn visit_borrowed_str<E>(self, v: &'de str) -> std::result::Result<Self::Value, E>
39            where
40                E: de::Error,
41            {
42                DerivationPath::from_str(v).map_err(|err| de::Error::custom(err.to_string()))
43            }
44        }
45
46        deserializer.deserialize_str(DerivationPathVisitor)
47    }
48}
49
50impl Serialize for DerivationPath {
51    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
52    where
53        S: Serializer,
54    {
55        serializer.serialize_str(&self.to_string())
56    }
57}
58
59impl DerivationPath {
60    /// Iterate over the [`ChildNumber`] values in this derivation path.
61    pub fn iter(&self) -> impl Iterator<Item = ChildNumber> + '_ {
62        self.path.iter().cloned()
63    }
64
65    /// Is this derivation path empty? (i.e. the root)
66    pub fn is_empty(&self) -> bool {
67        self.path.is_empty()
68    }
69
70    /// Get the count of [`ChildNumber`] values in this derivation path.
71    pub fn len(&self) -> usize {
72        self.path.len()
73    }
74
75    /// Get the parent [`DerivationPath`] for the current one.
76    ///
77    /// Returns `None` if this is already the root path.
78    pub fn parent(&self) -> Option<Self> {
79        self.path.len().checked_sub(1).map(|n| {
80            let mut parent = self.clone();
81            parent.path.truncate(n);
82            parent
83        })
84    }
85
86    /// Push a [`ChildNumber`] onto an existing derivation path.
87    pub fn push(&mut self, child_number: ChildNumber) {
88        self.path.push(child_number)
89    }
90}
91
92impl AsRef<[ChildNumber]> for DerivationPath {
93    fn as_ref(&self) -> &[ChildNumber] {
94        &self.path
95    }
96}
97
98impl Display for DerivationPath {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        f.write_str(PREFIX)?;
101
102        for child_number in self.iter() {
103            write!(f, "/{}", child_number)?;
104        }
105
106        Ok(())
107    }
108}
109
110impl Extend<ChildNumber> for DerivationPath {
111    fn extend<T>(&mut self, iter: T)
112    where
113        T: IntoIterator<Item = ChildNumber>,
114    {
115        self.path.extend(iter);
116    }
117}
118
119impl FromStr for DerivationPath {
120    type Err = Error;
121
122    fn from_str(path: &str) -> Result<DerivationPath> {
123        let mut path = path.split('/');
124
125        if path.next() != Some(PREFIX) {
126            return Err(Error::String(format!("Derivation don't start with `{PREFIX}/`")));
127        }
128
129        Ok(DerivationPath { path: path.map(str::parse).collect::<Result<_>>()? })
130    }
131}
132
133impl IntoIterator for DerivationPath {
134    type Item = ChildNumber;
135    type IntoIter = std::vec::IntoIter<ChildNumber>;
136
137    fn into_iter(self) -> std::vec::IntoIter<ChildNumber> {
138        self.path.into_iter()
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::DerivationPath;
145    //use alloc::string::ToString;
146
147    /// BIP32 test vectors
148    // TODO(tarcieri): consolidate test vectors
149    #[test]
150    fn round_trip() {
151        let path_m = "m";
152        assert_eq!(path_m.parse::<DerivationPath>().unwrap().to_string(), path_m);
153
154        let path_m_0 = "m/0";
155        assert_eq!(path_m_0.parse::<DerivationPath>().unwrap().to_string(), path_m_0);
156
157        let path_m_0_2147483647h = "m/0/2147483647'";
158        assert_eq!(path_m_0_2147483647h.parse::<DerivationPath>().unwrap().to_string(), path_m_0_2147483647h);
159
160        let path_m_0_2147483647h_1 = "m/0/2147483647'/1";
161        assert_eq!(path_m_0_2147483647h_1.parse::<DerivationPath>().unwrap().to_string(), path_m_0_2147483647h_1);
162
163        let path_m_0_2147483647h_1_2147483646h = "m/0/2147483647'/1/2147483646'";
164        assert_eq!(
165            path_m_0_2147483647h_1_2147483646h.parse::<DerivationPath>().unwrap().to_string(),
166            path_m_0_2147483647h_1_2147483646h
167        );
168
169        let path_m_0_2147483647h_1_2147483646h_2 = "m/0/2147483647'/1/2147483646'/2";
170        assert_eq!(
171            path_m_0_2147483647h_1_2147483646h_2.parse::<DerivationPath>().unwrap().to_string(),
172            path_m_0_2147483647h_1_2147483646h_2
173        );
174    }
175
176    #[test]
177    fn parent() {
178        let path_m_0_2147483647h = "m/0/2147483647'".parse::<DerivationPath>().unwrap();
179        let path_m_0 = path_m_0_2147483647h.parent().unwrap();
180        assert_eq!("m/0", path_m_0.to_string());
181
182        let path_m = path_m_0.parent().unwrap();
183        assert_eq!("m", path_m.to_string());
184        assert_eq!(path_m.parent(), None);
185    }
186}