1use anyhow::Result;
3use std::str::FromStr;
4
5const HARDENED_BIT: u32 = 1 << 31;
6
7#[derive(Copy, Clone, PartialEq, Eq, Debug)]
9pub struct ChildNumber(u32);
10
11impl ChildNumber {
12 pub fn is_hardened(&self) -> bool {
14 self.0 & HARDENED_BIT == HARDENED_BIT
15 }
16
17 pub fn is_normal(&self) -> bool {
19 self.0 & HARDENED_BIT == 0
20 }
21
22 pub fn hardened_from_u32(index: u32) -> Self {
24 ChildNumber(index | HARDENED_BIT)
25 }
26
27 pub fn non_hardened_from_u32(index: u32) -> Self {
29 ChildNumber(index)
30 }
31
32 pub fn index(&self) -> u32 {
34 self.0 & (i32::MAX as u32)
35 }
36
37 pub fn to_bytes(&self) -> [u8; 4] {
39 self.0.to_be_bytes()
40 }
41
42 pub fn to_substrate_chain_code(&self) -> [u8; 32] {
44 let mut chain_code = [0; 32];
45 let bytes = (self.index() as u64).to_le_bytes();
46 chain_code[..bytes.len()].copy_from_slice(&bytes[..]);
47 chain_code
48 }
49}
50
51impl core::ops::Add<u32> for ChildNumber {
52 type Output = Self;
53
54 fn add(self, other: u32) -> Self::Output {
55 Self(self.0 + other)
56 }
57}
58
59impl FromStr for ChildNumber {
60 type Err = anyhow::Error;
61
62 fn from_str(child: &str) -> Result<ChildNumber> {
63 let (child, mask) = if let Some(child) = child.strip_suffix('\'') {
64 (child, HARDENED_BIT)
65 } else {
66 (child, 0)
67 };
68
69 let index: u32 = child.parse()?;
70
71 if index & HARDENED_BIT != 0 {
72 anyhow::bail!("invalid child number");
73 }
74
75 Ok(ChildNumber(index | mask))
76 }
77}
78
79#[derive(Clone, PartialEq, Eq, Debug, Default)]
81pub struct DerivationPath {
82 path: Vec<ChildNumber>,
83}
84
85impl FromStr for DerivationPath {
86 type Err = anyhow::Error;
87
88 fn from_str(path: &str) -> Result<DerivationPath> {
89 let mut path = path.split('/');
90
91 if path.next() != Some("m") {
92 anyhow::bail!("invalid derivation path");
93 }
94
95 Ok(DerivationPath {
96 path: path.map(str::parse).collect::<Result<Vec<ChildNumber>>>()?,
97 })
98 }
99}
100
101impl AsRef<[ChildNumber]> for DerivationPath {
102 fn as_ref(&self) -> &[ChildNumber] {
103 &self.path
104 }
105}
106
107impl DerivationPath {
108 pub fn iter(&self) -> impl Iterator<Item = &ChildNumber> {
110 self.path.iter()
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn derive_path() {
120 let path: DerivationPath = "m/44'/60'/0'/0".parse().unwrap();
121
122 assert_eq!(
123 path,
124 DerivationPath {
125 path: vec![
126 ChildNumber::hardened_from_u32(44),
127 ChildNumber::hardened_from_u32(60),
128 ChildNumber::hardened_from_u32(0),
129 ChildNumber::non_hardened_from_u32(0),
130 ],
131 }
132 );
133 }
134}