1#![cfg_attr(not(feature = "std"), no_std)]
19
20#[cfg(not(feature = "std"))]
21extern crate alloc;
22
23#[cfg(not(feature = "std"))]
24use alloc::{borrow::ToOwned, boxed::Box, string::String};
25
26use core::fmt;
27use core::iter::IntoIterator;
28use core::slice::Iter;
29use core::str::FromStr;
30
31#[derive(Debug, Clone)]
33pub enum DerivationPathError {
34 PathTooLong,
35 InvalidChildIndex(ChildIndexError),
36}
37
38impl fmt::Display for DerivationPathError {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Self::PathTooLong => f.write_str("path too long"),
42 Self::InvalidChildIndex(err) => {
43 f.write_fmt(format_args!("invalid child index: {}", err))
44 }
45 }
46 }
47}
48
49#[cfg(feature = "std")]
50impl std::error::Error for DerivationPathError {
51 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52 match self {
53 Self::InvalidChildIndex(err) => Some(err),
54 Self::PathTooLong => None,
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
61pub enum DerivationPathParseError {
62 Empty,
63 InvalidPrefix(String),
64 InvalidChildIndex(ChildIndexParseError),
65}
66
67impl fmt::Display for DerivationPathParseError {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 Self::Empty => f.write_str("empty"),
71 Self::InvalidPrefix(prefix) => f.write_fmt(format_args!("invalid prefix: {}", prefix)),
72 Self::InvalidChildIndex(err) => {
73 f.write_fmt(format_args!("invalid child index: {}", err))
74 }
75 }
76 }
77}
78
79#[cfg(feature = "std")]
80impl std::error::Error for DerivationPathParseError {
81 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
82 match self {
83 Self::InvalidChildIndex(err) => Some(err),
84 Self::Empty | Self::InvalidPrefix(_) => None,
85 }
86 }
87}
88
89#[derive(Clone, Debug, Eq, PartialEq)]
91pub struct DerivationPath(Box<[ChildIndex]>);
92
93#[derive(Copy, Clone, Debug, Eq, PartialEq)]
95pub enum DerivationPathType {
96 None,
97 BIP32,
98 BIP44,
99 BIP49,
100}
101
102impl DerivationPath {
103 #[inline]
105 pub fn new<P>(path: P) -> Self
106 where
107 P: Into<Box<[ChildIndex]>>,
108 {
109 DerivationPath(path.into())
110 }
111
112 pub fn bip32<P>(path: P) -> Result<Self, DerivationPathError>
115 where
116 P: Into<Box<[ChildIndex]>>,
117 {
118 let path = path.into();
119 if path.len() > 255 {
120 return Err(DerivationPathError::PathTooLong);
121 }
122 Ok(Self::new(path))
123 }
124
125 #[inline]
127 pub fn bip44(
128 coin: u32,
129 account: u32,
130 change: u32,
131 address: u32,
132 ) -> Result<Self, DerivationPathError> {
133 Self::bip4x(44, coin, account, change, address)
134 }
135
136 #[inline]
138 pub fn bip49(
139 coin: u32,
140 account: u32,
141 change: u32,
142 address: u32,
143 ) -> Result<Self, DerivationPathError> {
144 Self::bip4x(49, coin, account, change, address)
145 }
146
147 #[inline]
148 fn bip4x(
149 purpose: u32,
150 coin: u32,
151 account: u32,
152 change: u32,
153 address: u32,
154 ) -> Result<Self, DerivationPathError> {
155 Ok(Self::new(
156 [
157 ChildIndex::hardened(purpose)?,
158 ChildIndex::hardened(coin)?,
159 ChildIndex::hardened(account)?,
160 ChildIndex::normal(change)?,
161 ChildIndex::normal(address)?,
162 ]
163 .as_ref(),
164 ))
165 }
166
167 #[inline]
169 pub fn path(&self) -> &[ChildIndex] {
170 self.0.as_ref()
171 }
172
173 pub fn path_type(&self) -> DerivationPathType {
176 let path = self.path();
177 let len = path.len();
178 if len == 5
179 && path[1].is_hardened()
180 && path[2].is_hardened()
181 && path[3].is_normal()
182 && path[4].is_normal()
183 {
184 match path[0] {
185 ChildIndex::Hardened(44) => DerivationPathType::BIP44,
186 ChildIndex::Hardened(49) => DerivationPathType::BIP49,
187 _ => DerivationPathType::BIP32,
188 }
189 } else if len < 256 {
190 DerivationPathType::BIP32
191 } else {
192 DerivationPathType::None
193 }
194 }
195}
196
197impl fmt::Display for DerivationPath {
198 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 f.write_str("m")?;
200 for index in self.path() {
201 f.write_str("/")?;
202 fmt::Display::fmt(index, f)?;
203 }
204 Ok(())
205 }
206}
207
208impl FromStr for DerivationPath {
209 type Err = DerivationPathParseError;
210
211 fn from_str(s: &str) -> Result<Self, Self::Err> {
212 if s.is_empty() {
213 return Err(DerivationPathParseError::Empty);
214 }
215 let mut parts = s.split('/');
216 match parts.next().unwrap() {
217 "m" => (),
218 prefix => return Err(DerivationPathParseError::InvalidPrefix(prefix.to_owned())),
219 }
220 let path = parts
221 .map(|part| ChildIndex::from_str(part).map_err(|e| e.into()))
222 .collect::<Result<Box<[ChildIndex]>, DerivationPathParseError>>()?;
223 Ok(DerivationPath::new(path))
224 }
225}
226
227impl AsRef<[ChildIndex]> for DerivationPath {
228 fn as_ref(&self) -> &[ChildIndex] {
229 self.path()
230 }
231}
232
233impl<'a> IntoIterator for &'a DerivationPath {
234 type IntoIter = Iter<'a, ChildIndex>;
235 type Item = &'a ChildIndex;
236 fn into_iter(self) -> Self::IntoIter {
237 self.path().iter()
238 }
239}
240
241#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
243pub enum ChildIndex {
244 Normal(u32),
245 Hardened(u32),
246}
247
248#[derive(Debug, Clone)]
250pub enum ChildIndexParseError {
251 ParseIntError(core::num::ParseIntError),
252 ChildIndexError(ChildIndexError),
253}
254
255impl fmt::Display for ChildIndexParseError {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 match self {
258 Self::ParseIntError(err) => {
259 f.write_fmt(format_args!("could not parse child index: {}", err))
260 }
261 Self::ChildIndexError(err) => f.write_fmt(format_args!("invalid child index: {}", err)),
262 }
263 }
264}
265
266#[cfg(feature = "std")]
267impl std::error::Error for ChildIndexParseError {
268 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
269 match self {
270 Self::ParseIntError(err) => Some(err),
271 Self::ChildIndexError(err) => Some(err),
272 }
273 }
274}
275
276#[derive(Debug, Clone)]
278pub enum ChildIndexError {
279 NumberTooLarge(u32),
280}
281
282impl fmt::Display for ChildIndexError {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 match self {
285 Self::NumberTooLarge(num) => f.write_fmt(format_args!("number too large: {}", num)),
286 }
287 }
288}
289
290#[cfg(feature = "std")]
291impl std::error::Error for ChildIndexError {}
292
293impl ChildIndex {
294 pub fn hardened(num: u32) -> Result<Self, ChildIndexError> {
297 Ok(Self::Hardened(Self::check_size(num)?))
298 }
299
300 pub fn normal(num: u32) -> Result<Self, ChildIndexError> {
303 Ok(Self::Normal(Self::check_size(num)?))
304 }
305
306 fn check_size(num: u32) -> Result<u32, ChildIndexError> {
307 if num & (1 << 31) == 0 {
308 Ok(num)
309 } else {
310 Err(ChildIndexError::NumberTooLarge(num))
311 }
312 }
313
314 #[inline]
316 pub fn to_u32(self) -> u32 {
317 match self {
318 ChildIndex::Hardened(index) => index,
319 ChildIndex::Normal(index) => index,
320 }
321 }
322
323 #[inline]
327 pub fn to_bits(self) -> u32 {
328 match self {
329 ChildIndex::Hardened(index) => (1 << 31) | index,
330 ChildIndex::Normal(index) => index,
331 }
332 }
333
334 #[inline]
337 pub fn from_bits(bits: u32) -> Self {
338 if bits & (1 << 31) == 0 {
339 ChildIndex::Normal(bits)
340 } else {
341 ChildIndex::Hardened(bits & !(1 << 31))
342 }
343 }
344
345 #[inline]
347 pub fn is_hardened(self) -> bool {
348 matches!(self, Self::Hardened(_))
349 }
350
351 #[inline]
353 pub fn is_normal(self) -> bool {
354 matches!(self, Self::Normal(_))
355 }
356}
357
358impl fmt::Display for ChildIndex {
359 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360 fmt::Display::fmt(&self.to_u32(), f)?;
361 if self.is_hardened() {
362 f.write_str("'")?;
363 }
364 Ok(())
365 }
366}
367
368impl FromStr for ChildIndex {
369 type Err = ChildIndexParseError;
370
371 fn from_str(s: &str) -> Result<Self, Self::Err> {
372 let mut chars = s.chars();
373 Ok(match chars.next_back() {
374 Some('\'') => Self::hardened(u32::from_str(chars.as_str())?)?,
375 _ => Self::normal(u32::from_str(s)?)?,
376 })
377 }
378}
379
380impl From<core::num::ParseIntError> for ChildIndexParseError {
381 fn from(err: core::num::ParseIntError) -> Self {
382 Self::ParseIntError(err)
383 }
384}
385
386impl From<ChildIndexError> for ChildIndexParseError {
387 fn from(err: ChildIndexError) -> Self {
388 Self::ChildIndexError(err)
389 }
390}
391
392impl From<ChildIndexParseError> for DerivationPathParseError {
393 fn from(err: ChildIndexParseError) -> Self {
394 Self::InvalidChildIndex(err)
395 }
396}
397
398impl From<ChildIndexError> for DerivationPathError {
399 fn from(err: ChildIndexError) -> Self {
400 Self::InvalidChildIndex(err)
401 }
402}
403
404#[cfg(test)]
405mod test {
406 use super::*;
407
408 #[cfg(not(feature = "std"))]
409 use alloc::{string::ToString, vec};
410
411 #[test]
412 fn child_index_is_normal() {
413 assert!(ChildIndex::Hardened(0).is_hardened());
414 assert!(!ChildIndex::Normal(0).is_hardened());
415 }
416
417 #[test]
418 fn child_index_is_hardened() {
419 assert!(!ChildIndex::Hardened(0).is_normal());
420 assert!(ChildIndex::Normal(0).is_normal());
421 }
422
423 #[test]
424 fn child_index_range() {
425 assert!(ChildIndex::normal(0).is_ok());
426 assert!(ChildIndex::normal(1).is_ok());
427 assert!(ChildIndex::normal(100).is_ok());
428 assert!(ChildIndex::normal(1 << 31).is_err());
429
430 assert!(ChildIndex::hardened(0).is_ok());
431 assert!(ChildIndex::hardened(1 << 31).is_err());
432 }
433
434 #[test]
435 fn child_index_to_u32() {
436 assert_eq!(ChildIndex::Normal(0).to_u32(), 0);
437 assert_eq!(ChildIndex::Normal(1).to_u32(), 1);
438 assert_eq!(ChildIndex::Normal(100).to_u32(), 100);
439 assert_eq!(ChildIndex::Hardened(0).to_u32(), 0);
440 assert_eq!(ChildIndex::Hardened(1).to_u32(), 1);
441 }
442
443 #[test]
444 fn child_index_to_bits() {
445 assert_eq!(ChildIndex::Normal(0).to_bits(), 0);
446 assert_eq!(ChildIndex::Normal(1).to_bits(), 1);
447 assert_eq!(ChildIndex::Normal(100).to_bits(), 100);
448 assert_eq!(ChildIndex::Hardened(0).to_bits(), (1 << 31) | 0);
449 assert_eq!(ChildIndex::Hardened(1).to_bits(), (1 << 31) | 1);
450 assert_eq!(ChildIndex::Hardened(100).to_bits(), (1 << 31) | 100);
451 }
452
453 #[test]
454 fn child_index_from_bits() {
455 assert_eq!(ChildIndex::from_bits(0), ChildIndex::Normal(0));
456 assert_eq!(ChildIndex::from_bits(1), ChildIndex::Normal(1));
457 assert_eq!(ChildIndex::from_bits(100), ChildIndex::Normal(100));
458 assert_eq!(
459 ChildIndex::from_bits((1 << 31) | 0),
460 ChildIndex::Hardened(0)
461 );
462 assert_eq!(
463 ChildIndex::from_bits((1 << 31) | 1),
464 ChildIndex::Hardened(1)
465 );
466 assert_eq!(
467 ChildIndex::from_bits((1 << 31) | 100),
468 ChildIndex::Hardened(100)
469 );
470 }
471
472 #[test]
473 fn child_index_to_string() {
474 assert_eq!(&ChildIndex::Normal(0).to_string(), "0");
475 assert_eq!(&ChildIndex::Normal(1).to_string(), "1");
476 assert_eq!(&ChildIndex::Normal(100).to_string(), "100");
477 assert_eq!(&ChildIndex::Hardened(0).to_string(), "0'");
478 assert_eq!(&ChildIndex::Hardened(1).to_string(), "1'");
479 assert_eq!(&ChildIndex::Hardened(100).to_string(), "100'");
480 }
481
482 #[test]
483 fn child_index_from_str() {
484 assert_eq!(ChildIndex::Normal(0), "0".parse().unwrap());
485 assert_eq!(ChildIndex::Normal(1), "1".parse().unwrap());
486 assert_eq!(ChildIndex::Normal(100), "100".parse().unwrap());
487 assert_eq!(ChildIndex::Hardened(0), "0'".parse().unwrap());
488 assert_eq!(ChildIndex::Hardened(1), "1'".parse().unwrap());
489 assert_eq!(ChildIndex::Hardened(100), "100'".parse().unwrap());
490 assert!(matches!(
491 ChildIndex::from_str(""),
492 Err(ChildIndexParseError::ParseIntError(_))
493 ));
494 assert!(matches!(
495 ChildIndex::from_str("a"),
496 Err(ChildIndexParseError::ParseIntError(_))
497 ));
498 assert!(matches!(
499 ChildIndex::from_str("100 "),
500 Err(ChildIndexParseError::ParseIntError(_))
501 ));
502 assert!(matches!(
503 ChildIndex::from_str("99a"),
504 Err(ChildIndexParseError::ParseIntError(_))
505 ));
506 assert!(matches!(
507 ChildIndex::from_str("a10"),
508 Err(ChildIndexParseError::ParseIntError(_))
509 ));
510 assert!(matches!(
511 ChildIndex::from_str(" 10"),
512 Err(ChildIndexParseError::ParseIntError(_))
513 ));
514 assert!(matches!(
515 ChildIndex::from_str(&(1u32 << 31).to_string()),
516 Err(ChildIndexParseError::ChildIndexError(_))
517 ));
518 }
519
520 #[test]
521 fn derivation_path_new() {
522 let path = [
523 ChildIndex::Normal(1),
524 ChildIndex::Hardened(2),
525 ChildIndex::Normal(3),
526 ];
527 assert_eq!(&path, DerivationPath::new(path.as_ref()).path());
528
529 let path: [ChildIndex; 0] = [];
530 assert_eq!(&path, DerivationPath::new(path.as_ref()).path());
531
532 let path = vec![ChildIndex::Normal(0); 256];
533 assert_eq!(path.as_slice(), DerivationPath::new(path.as_ref()).path());
534 }
535
536 #[test]
537 fn derivation_bip32() {
538 let path = [
539 ChildIndex::Normal(1),
540 ChildIndex::Hardened(2),
541 ChildIndex::Normal(3),
542 ];
543 assert_eq!(&path, DerivationPath::bip32(path.as_ref()).unwrap().path());
544
545 let path: [ChildIndex; 0] = [];
546 assert_eq!(&path, DerivationPath::bip32(path.as_ref()).unwrap().path());
547
548 let path = vec![ChildIndex::Normal(0); 256];
549 assert!(matches!(
550 DerivationPath::bip32(path.as_ref()),
551 Err(DerivationPathError::PathTooLong)
552 ));
553 }
554
555 #[test]
556 fn derivation_bip44() {
557 assert_eq!(
558 DerivationPath::bip44(1, 2, 3, 4).unwrap().path(),
559 &[
560 ChildIndex::Hardened(44),
561 ChildIndex::Hardened(1),
562 ChildIndex::Hardened(2),
563 ChildIndex::Normal(3),
564 ChildIndex::Normal(4)
565 ]
566 );
567
568 assert!(matches!(
569 DerivationPath::bip44(1 << 31, 0, 0, 0),
570 Err(DerivationPathError::InvalidChildIndex(_))
571 ));
572 }
573
574 #[test]
575 fn derivation_bip49() {
576 assert_eq!(
577 DerivationPath::bip49(1, 2, 3, 4).unwrap().path(),
578 &[
579 ChildIndex::Hardened(49),
580 ChildIndex::Hardened(1),
581 ChildIndex::Hardened(2),
582 ChildIndex::Normal(3),
583 ChildIndex::Normal(4)
584 ]
585 );
586
587 assert!(matches!(
588 DerivationPath::bip44(1 << 31, 0, 0, 0),
589 Err(DerivationPathError::InvalidChildIndex(_))
590 ));
591 }
592
593 #[test]
594 fn derivation_path_type() {
595 assert_eq!(
596 DerivationPath::new(vec![
597 ChildIndex::Normal(0),
598 ChildIndex::Normal(0),
599 ChildIndex::Normal(0)
600 ])
601 .path_type(),
602 DerivationPathType::BIP32
603 );
604 assert_eq!(
605 DerivationPath::new(vec![]).path_type(),
606 DerivationPathType::BIP32
607 );
608 assert_eq!(
609 DerivationPath::new(vec![
610 ChildIndex::Hardened(44),
611 ChildIndex::Hardened(0),
612 ChildIndex::Hardened(0),
613 ChildIndex::Normal(0),
614 ChildIndex::Normal(0)
615 ])
616 .path_type(),
617 DerivationPathType::BIP44
618 );
619 assert_eq!(
620 DerivationPath::new(vec![
621 ChildIndex::Hardened(44),
622 ChildIndex::Hardened(0),
623 ChildIndex::Hardened(0),
624 ChildIndex::Normal(0),
625 ])
626 .path_type(),
627 DerivationPathType::BIP32
628 );
629 assert_eq!(
630 DerivationPath::new(vec![
631 ChildIndex::Hardened(43),
632 ChildIndex::Hardened(0),
633 ChildIndex::Hardened(0),
634 ChildIndex::Normal(0),
635 ChildIndex::Normal(0)
636 ])
637 .path_type(),
638 DerivationPathType::BIP32
639 );
640 assert_eq!(
641 DerivationPath::new(vec![
642 ChildIndex::Hardened(44),
643 ChildIndex::Hardened(0),
644 ChildIndex::Normal(0),
645 ChildIndex::Normal(0),
646 ChildIndex::Normal(0)
647 ])
648 .path_type(),
649 DerivationPathType::BIP32
650 );
651 assert_eq!(
652 DerivationPath::new(vec![
653 ChildIndex::Hardened(49),
654 ChildIndex::Hardened(0),
655 ChildIndex::Hardened(0),
656 ChildIndex::Normal(0),
657 ChildIndex::Normal(0)
658 ])
659 .path_type(),
660 DerivationPathType::BIP49
661 );
662 assert_eq!(
663 DerivationPath::new(vec![ChildIndex::Normal(0); 256]).path_type(),
664 DerivationPathType::None
665 );
666 }
667
668 #[test]
669 fn derivation_path_to_string() {
670 assert_eq!(
671 &DerivationPath::new(vec![
672 ChildIndex::Hardened(1),
673 ChildIndex::Hardened(2),
674 ChildIndex::Normal(3),
675 ChildIndex::Hardened(4)
676 ])
677 .to_string(),
678 "m/1'/2'/3/4'"
679 );
680 assert_eq!(
681 &DerivationPath::new(vec![
682 ChildIndex::Hardened(1),
683 ChildIndex::Hardened(2),
684 ChildIndex::Hardened(4),
685 ChildIndex::Normal(3),
686 ])
687 .to_string(),
688 "m/1'/2'/4'/3"
689 );
690 assert_eq!(
691 &DerivationPath::new(vec![
692 ChildIndex::Normal(100),
693 ChildIndex::Hardened(2),
694 ChildIndex::Hardened(4),
695 ChildIndex::Normal(3),
696 ])
697 .to_string(),
698 "m/100/2'/4'/3"
699 );
700 assert_eq!(
701 &DerivationPath::new(vec![ChildIndex::Normal(0),]).to_string(),
702 "m/0"
703 );
704 assert_eq!(&DerivationPath::new(vec![]).to_string(), "m");
705 }
706
707 #[test]
708 fn derivation_path_parsing() {
709 assert_eq!(
710 DerivationPath::new(vec![
711 ChildIndex::Hardened(1),
712 ChildIndex::Hardened(2),
713 ChildIndex::Normal(3),
714 ChildIndex::Hardened(4)
715 ]),
716 "m/1'/2'/3/4'".parse().unwrap()
717 );
718 assert_eq!(
719 DerivationPath::new(vec![
720 ChildIndex::Hardened(1),
721 ChildIndex::Hardened(2),
722 ChildIndex::Hardened(4),
723 ChildIndex::Normal(3),
724 ]),
725 "m/1'/2'/4'/3".parse().unwrap()
726 );
727 assert_eq!(
728 DerivationPath::new(vec![
729 ChildIndex::Normal(100),
730 ChildIndex::Hardened(2),
731 ChildIndex::Hardened(4),
732 ChildIndex::Normal(3),
733 ]),
734 "m/100/2'/4'/3".parse().unwrap()
735 );
736 assert_eq!(
737 DerivationPath::new(vec![ChildIndex::Normal(0),]),
738 "m/0".parse().unwrap()
739 );
740 assert_eq!(DerivationPath::new(vec![]), "m".parse().unwrap());
741
742 assert!(matches!(
743 DerivationPath::from_str(""),
744 Err(DerivationPathParseError::Empty)
745 ));
746 assert!(matches!(
747 DerivationPath::from_str("n/0"),
748 Err(DerivationPathParseError::InvalidPrefix(_))
749 ));
750 assert!(matches!(
751 DerivationPath::from_str("mn/0"),
752 Err(DerivationPathParseError::InvalidPrefix(_))
753 ));
754 assert!(matches!(
755 DerivationPath::from_str("m/0/"),
756 Err(DerivationPathParseError::InvalidChildIndex(_))
757 ));
758 assert!(matches!(
759 DerivationPath::from_str("m/0/a/1"),
760 Err(DerivationPathParseError::InvalidChildIndex(_))
761 ));
762 assert!(matches!(
763 DerivationPath::from_str("m/0///1"),
764 Err(DerivationPathParseError::InvalidChildIndex(_))
765 ));
766 assert!(matches!(
767 DerivationPath::from_str(&format!("m/0/{}/1", (1u32 << 31))),
768 Err(DerivationPathParseError::InvalidChildIndex(_))
769 ));
770 assert!(matches!(
771 DerivationPath::from_str("m|1"),
772 Err(DerivationPathParseError::InvalidPrefix(_))
773 ));
774 }
775}