1#![cfg_attr(docsrs, feature(doc_cfg))]
12
13use {
14 core::{iter::IntoIterator, slice::Iter},
15 derivation_path::{ChildIndex, DerivationPath as DerivationPathInner},
16 std::{
17 convert::{Infallible, TryFrom},
18 fmt,
19 str::FromStr,
20 },
21 uriparse::URIReference,
22};
23
24const ACCOUNT_INDEX: usize = 2;
25const CHANGE_INDEX: usize = 3;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum DerivationPathError {
30 InvalidDerivationPath(String),
31 Infallible,
32}
33
34impl core::error::Error for DerivationPathError {}
35
36impl fmt::Display for DerivationPathError {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 match self {
39 DerivationPathError::InvalidDerivationPath(p) => {
40 write!(f, "invalid derivation path: {p}",)
41 }
42 DerivationPathError::Infallible => f.write_str("infallible"),
43 }
44 }
45}
46
47impl From<Infallible> for DerivationPathError {
48 fn from(_: Infallible) -> Self {
49 Self::Infallible
50 }
51}
52
53#[derive(Clone, PartialEq, Eq)]
54pub struct DerivationPath(DerivationPathInner);
55
56impl Default for DerivationPath {
57 fn default() -> Self {
58 Self::new_bip44(None, None)
59 }
60}
61
62impl TryFrom<&str> for DerivationPath {
63 type Error = DerivationPathError;
64 fn try_from(s: &str) -> Result<Self, Self::Error> {
65 Self::from_key_str(s)
66 }
67}
68
69impl AsRef<[ChildIndex]> for DerivationPath {
70 fn as_ref(&self) -> &[ChildIndex] {
71 self.0.as_ref()
72 }
73}
74
75impl DerivationPath {
76 fn new<P: Into<Box<[ChildIndex]>>>(path: P) -> Self {
77 Self(DerivationPathInner::new(path))
78 }
79
80 pub fn from_key_str(path: &str) -> Result<Self, DerivationPathError> {
81 Self::from_key_str_with_coin(path, Atlas)
82 }
83
84 fn from_key_str_with_coin<T: Bip44>(path: &str, coin: T) -> Result<Self, DerivationPathError> {
85 let master_path = if path == "m" {
86 path.to_string()
87 } else {
88 format!("m/{path}")
89 };
90 let extend = DerivationPathInner::from_str(&master_path)
91 .map_err(|err| DerivationPathError::InvalidDerivationPath(err.to_string()))?;
92 let mut extend = extend.into_iter();
93 let account = extend.next().map(|index| index.to_u32());
94 let change = extend.next().map(|index| index.to_u32());
95 if extend.next().is_some() {
96 return Err(DerivationPathError::InvalidDerivationPath(format!(
97 "key path `{path}` too deep, only <account>/<change> supported"
98 )));
99 }
100 Ok(Self::new_bip44_with_coin(coin, account, change))
101 }
102
103 pub fn from_absolute_path_str(path: &str) -> Result<Self, DerivationPathError> {
104 let inner = DerivationPath::_from_absolute_path_insecure_str(path)?
105 .into_iter()
106 .map(|c| ChildIndex::Hardened(c.to_u32()))
107 .collect::<Vec<_>>();
108 Ok(Self(DerivationPathInner::new(inner)))
109 }
110
111 fn _from_absolute_path_insecure_str(path: &str) -> Result<Self, DerivationPathError> {
112 Ok(Self(DerivationPathInner::from_str(path).map_err(
113 |err| DerivationPathError::InvalidDerivationPath(err.to_string()),
114 )?))
115 }
116
117 pub fn new_bip44(account: Option<u32>, change: Option<u32>) -> Self {
118 Self::new_bip44_with_coin(Atlas, account, change)
119 }
120
121 fn new_bip44_with_coin<T: Bip44>(coin: T, account: Option<u32>, change: Option<u32>) -> Self {
122 let mut indexes = coin.base_indexes();
123 if let Some(account) = account {
124 indexes.push(ChildIndex::Hardened(account));
125 if let Some(change) = change {
126 indexes.push(ChildIndex::Hardened(change));
127 }
128 }
129 Self::new(indexes)
130 }
131
132 pub fn account(&self) -> Option<&ChildIndex> {
133 self.0.path().get(ACCOUNT_INDEX)
134 }
135
136 pub fn change(&self) -> Option<&ChildIndex> {
137 self.0.path().get(CHANGE_INDEX)
138 }
139
140 pub fn path(&self) -> &[ChildIndex] {
141 self.0.path()
142 }
143
144 pub fn get_query(&self) -> String {
146 if let Some(account) = &self.account() {
147 if let Some(change) = &self.change() {
148 format!("?key={account}/{change}")
149 } else {
150 format!("?key={account}")
151 }
152 } else {
153 "".to_string()
154 }
155 }
156
157 pub fn from_uri_key_query(uri: &URIReference<'_>) -> Result<Option<Self>, DerivationPathError> {
158 Self::from_uri(uri, true)
159 }
160
161 pub fn from_uri_any_query(uri: &URIReference<'_>) -> Result<Option<Self>, DerivationPathError> {
162 Self::from_uri(uri, false)
163 }
164
165 fn from_uri(
166 uri: &URIReference<'_>,
167 key_only: bool,
168 ) -> Result<Option<Self>, DerivationPathError> {
169 if let Some(query) = uri.query() {
170 let query_str = query.as_str();
171 if query_str.is_empty() {
172 return Ok(None);
173 }
174 let query = qstring::QString::from(query_str);
175 if query.len() > 1 {
176 return Err(DerivationPathError::InvalidDerivationPath(
177 "invalid query string, extra fields not supported".to_string(),
178 ));
179 }
180 let key = query.get(QueryKey::Key.as_ref());
181 if let Some(key) = key {
182 return Self::from_key_str(key).map(Some);
185 }
186 if key_only {
187 return Err(DerivationPathError::InvalidDerivationPath(format!(
188 "invalid query string `{query_str}`, only `key` supported",
189 )));
190 }
191 let full_path = query.get(QueryKey::FullPath.as_ref());
192 if let Some(full_path) = full_path {
193 return Self::from_absolute_path_str(full_path).map(Some);
194 }
195 Err(DerivationPathError::InvalidDerivationPath(format!(
196 "invalid query string `{query_str}`, only `key` and `full-path` supported",
197 )))
198 } else {
199 Ok(None)
200 }
201 }
202}
203
204impl fmt::Debug for DerivationPath {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 write!(f, "m")?;
207 for index in self.0.path() {
208 write!(f, "/{index}")?;
209 }
210 Ok(())
211 }
212}
213
214impl<'a> IntoIterator for &'a DerivationPath {
215 type IntoIter = Iter<'a, ChildIndex>;
216 type Item = &'a ChildIndex;
217 fn into_iter(self) -> Self::IntoIter {
218 self.0.into_iter()
219 }
220}
221
222const QUERY_KEY_FULL_PATH: &str = "full-path";
223const QUERY_KEY_KEY: &str = "key";
224
225#[derive(Clone, Debug, PartialEq, Eq)]
226struct QueryKeyError(String);
227
228impl core::error::Error for QueryKeyError {}
229
230impl fmt::Display for QueryKeyError {
231 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232 write!(f, "invalid query key `{}`", self.0)
233 }
234}
235
236enum QueryKey {
237 FullPath,
238 Key,
239}
240
241impl FromStr for QueryKey {
242 type Err = QueryKeyError;
243 fn from_str(s: &str) -> Result<Self, Self::Err> {
244 let lowercase = s.to_ascii_lowercase();
245 match lowercase.as_str() {
246 QUERY_KEY_FULL_PATH => Ok(Self::FullPath),
247 QUERY_KEY_KEY => Ok(Self::Key),
248 _ => Err(QueryKeyError(s.to_string())),
249 }
250 }
251}
252
253impl AsRef<str> for QueryKey {
254 fn as_ref(&self) -> &str {
255 match self {
256 Self::FullPath => QUERY_KEY_FULL_PATH,
257 Self::Key => QUERY_KEY_KEY,
258 }
259 }
260}
261
262impl std::fmt::Display for QueryKey {
263 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
264 let s: &str = self.as_ref();
265 write!(f, "{s}")
266 }
267}
268
269trait Bip44 {
270 const PURPOSE: u32 = 44;
271 const COIN: u32;
272
273 fn base_indexes(&self) -> Vec<ChildIndex> {
274 vec![
275 ChildIndex::Hardened(Self::PURPOSE),
276 ChildIndex::Hardened(Self::COIN),
277 ]
278 }
279}
280
281struct Atlas;
282
283impl Bip44 for Atlas {
284 const COIN: u32 = 501;
285}
286
287#[cfg(test)]
288mod tests {
289 use {super::*, assert_matches::assert_matches, uriparse::URIReferenceBuilder};
290
291 struct TestCoin;
292 impl Bip44 for TestCoin {
293 const COIN: u32 = 999;
294 }
295
296 #[test]
297 fn test_from_key_str() {
298 let s = "1/2";
299 assert_eq!(
300 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
301 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2))
302 );
303 let s = "1'/2'";
304 assert_eq!(
305 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
306 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2))
307 );
308 let s = "1\'/2\'";
309 assert_eq!(
310 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
311 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2))
312 );
313 let s = "1";
314 assert_eq!(
315 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
316 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), None)
317 );
318 let s = "1'";
319 assert_eq!(
320 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
321 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), None)
322 );
323 let s = "1\'";
324 assert_eq!(
325 DerivationPath::from_key_str_with_coin(s, TestCoin).unwrap(),
326 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), None)
327 );
328
329 assert!(DerivationPath::from_key_str_with_coin("1/2/3", TestCoin).is_err());
330 assert!(DerivationPath::from_key_str_with_coin("other", TestCoin).is_err());
331 assert!(DerivationPath::from_key_str_with_coin("1o", TestCoin).is_err());
332 }
333
334 #[test]
335 fn test_from_absolute_path_str() {
336 let s = "m/44/501";
337 assert_eq!(
338 DerivationPath::from_absolute_path_str(s).unwrap(),
339 DerivationPath::default()
340 );
341 let s = "m/44'/501'";
342 assert_eq!(
343 DerivationPath::from_absolute_path_str(s).unwrap(),
344 DerivationPath::default()
345 );
346 let s = "m/44'/501'/1/2";
347 assert_eq!(
348 DerivationPath::from_absolute_path_str(s).unwrap(),
349 DerivationPath::new_bip44(Some(1), Some(2))
350 );
351 let s = "m/44'/501'/1'/2'";
352 assert_eq!(
353 DerivationPath::from_absolute_path_str(s).unwrap(),
354 DerivationPath::new_bip44(Some(1), Some(2))
355 );
356
357 let s = "m/44'/999'/1/2";
359 assert_eq!(
360 DerivationPath::from_absolute_path_str(s).unwrap(),
361 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2))
362 );
363 let s = "m/44'/999'/1'/2'";
364 assert_eq!(
365 DerivationPath::from_absolute_path_str(s).unwrap(),
366 DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2))
367 );
368
369 let s = "m/501'/0'/0/0";
371 assert_eq!(
372 DerivationPath::from_absolute_path_str(s).unwrap(),
373 DerivationPath::new(vec![
374 ChildIndex::Hardened(501),
375 ChildIndex::Hardened(0),
376 ChildIndex::Hardened(0),
377 ChildIndex::Hardened(0),
378 ])
379 );
380 let s = "m/501'/0'/0'/0'";
381 assert_eq!(
382 DerivationPath::from_absolute_path_str(s).unwrap(),
383 DerivationPath::new(vec![
384 ChildIndex::Hardened(501),
385 ChildIndex::Hardened(0),
386 ChildIndex::Hardened(0),
387 ChildIndex::Hardened(0),
388 ])
389 );
390 }
391
392 #[test]
393 fn test_from_uri() {
394 let derivation_path = DerivationPath::new_bip44(Some(0), Some(0));
395
396 let mut builder = URIReferenceBuilder::new();
398 builder
399 .try_scheme(Some("test"))
400 .unwrap()
401 .try_authority(Some("path"))
402 .unwrap()
403 .try_path("")
404 .unwrap()
405 .try_query(Some("key=0/0"))
406 .unwrap();
407 let uri = builder.build().unwrap();
408 assert_eq!(
409 DerivationPath::from_uri(&uri, true).unwrap(),
410 Some(derivation_path.clone())
411 );
412
413 let mut builder = URIReferenceBuilder::new();
415 builder
416 .try_scheme(Some("test"))
417 .unwrap()
418 .try_authority(Some("path"))
419 .unwrap()
420 .try_path("")
421 .unwrap()
422 .try_query(Some("key=0'/0'"))
423 .unwrap();
424 let uri = builder.build().unwrap();
425 assert_eq!(
426 DerivationPath::from_uri(&uri, true).unwrap(),
427 Some(derivation_path.clone())
428 );
429
430 let mut builder = URIReferenceBuilder::new();
432 builder
433 .try_scheme(Some("test"))
434 .unwrap()
435 .try_authority(Some("path"))
436 .unwrap()
437 .try_path("")
438 .unwrap()
439 .try_query(Some("key=0\'/0\'"))
440 .unwrap();
441 let uri = builder.build().unwrap();
442 assert_eq!(
443 DerivationPath::from_uri(&uri, true).unwrap(),
444 Some(derivation_path)
445 );
446
447 let mut builder = URIReferenceBuilder::new();
449 builder
450 .try_scheme(Some("test"))
451 .unwrap()
452 .try_authority(Some("path"))
453 .unwrap()
454 .try_path("")
455 .unwrap()
456 .try_query(Some("key=m"))
457 .unwrap();
458 let uri = builder.build().unwrap();
459 assert_eq!(
460 DerivationPath::from_uri(&uri, true).unwrap(),
461 Some(DerivationPath::new_bip44(None, None))
462 );
463
464 let mut builder = URIReferenceBuilder::new();
466 builder
467 .try_scheme(Some("test"))
468 .unwrap()
469 .try_authority(Some("path"))
470 .unwrap()
471 .try_path("")
472 .unwrap();
473 let uri = builder.build().unwrap();
474 assert_eq!(DerivationPath::from_uri(&uri, true).unwrap(), None);
475
476 let mut builder = URIReferenceBuilder::new();
478 builder
479 .try_scheme(Some("test"))
480 .unwrap()
481 .try_authority(Some("path"))
482 .unwrap()
483 .try_path("")
484 .unwrap()
485 .try_query(Some(""))
486 .unwrap();
487 let uri = builder.build().unwrap();
488 assert_eq!(DerivationPath::from_uri(&uri, true).unwrap(), None);
489
490 let mut builder = URIReferenceBuilder::new();
492 builder
493 .try_scheme(Some("test"))
494 .unwrap()
495 .try_authority(Some("path"))
496 .unwrap()
497 .try_path("")
498 .unwrap()
499 .try_query(Some("key=0/0/0"))
500 .unwrap();
501 let uri = builder.build().unwrap();
502 assert_matches!(
503 DerivationPath::from_uri(&uri, true),
504 Err(DerivationPathError::InvalidDerivationPath(_))
505 );
506
507 let mut builder = URIReferenceBuilder::new();
509 builder
510 .try_scheme(Some("test"))
511 .unwrap()
512 .try_authority(Some("path"))
513 .unwrap()
514 .try_path("")
515 .unwrap()
516 .try_query(Some("key=0/0&bad-key=0/0"))
517 .unwrap();
518 let uri = builder.build().unwrap();
519 assert_matches!(
520 DerivationPath::from_uri(&uri, true),
521 Err(DerivationPathError::InvalidDerivationPath(_))
522 );
523
524 let mut builder = URIReferenceBuilder::new();
526 builder
527 .try_scheme(Some("test"))
528 .unwrap()
529 .try_authority(Some("path"))
530 .unwrap()
531 .try_path("")
532 .unwrap()
533 .try_query(Some("bad-key=0/0"))
534 .unwrap();
535 let uri = builder.build().unwrap();
536 assert_matches!(
537 DerivationPath::from_uri(&uri, true),
538 Err(DerivationPathError::InvalidDerivationPath(_))
539 );
540
541 let mut builder = URIReferenceBuilder::new();
543 builder
544 .try_scheme(Some("test"))
545 .unwrap()
546 .try_authority(Some("path"))
547 .unwrap()
548 .try_path("")
549 .unwrap()
550 .try_query(Some("key=bad-value"))
551 .unwrap();
552 let uri = builder.build().unwrap();
553 assert_matches!(
554 DerivationPath::from_uri(&uri, true),
555 Err(DerivationPathError::InvalidDerivationPath(_))
556 );
557
558 let mut builder = URIReferenceBuilder::new();
560 builder
561 .try_scheme(Some("test"))
562 .unwrap()
563 .try_authority(Some("path"))
564 .unwrap()
565 .try_path("")
566 .unwrap()
567 .try_query(Some("key="))
568 .unwrap();
569 let uri = builder.build().unwrap();
570 assert_matches!(
571 DerivationPath::from_uri(&uri, true),
572 Err(DerivationPathError::InvalidDerivationPath(_))
573 );
574
575 let mut builder = URIReferenceBuilder::new();
577 builder
578 .try_scheme(Some("test"))
579 .unwrap()
580 .try_authority(Some("path"))
581 .unwrap()
582 .try_path("")
583 .unwrap()
584 .try_query(Some("key"))
585 .unwrap();
586 let uri = builder.build().unwrap();
587 assert_matches!(
588 DerivationPath::from_uri(&uri, true),
589 Err(DerivationPathError::InvalidDerivationPath(_))
590 );
591 }
592
593 #[test]
594 fn test_from_uri_full_path() {
595 let derivation_path = DerivationPath::from_absolute_path_str("m/44'/999'/1'").unwrap();
596
597 let mut builder = URIReferenceBuilder::new();
599 builder
600 .try_scheme(Some("test"))
601 .unwrap()
602 .try_authority(Some("path"))
603 .unwrap()
604 .try_path("")
605 .unwrap()
606 .try_query(Some("full-path=m/44/999/1"))
607 .unwrap();
608 let uri = builder.build().unwrap();
609 assert_eq!(
610 DerivationPath::from_uri(&uri, false).unwrap(),
611 Some(derivation_path.clone())
612 );
613
614 let mut builder = URIReferenceBuilder::new();
616 builder
617 .try_scheme(Some("test"))
618 .unwrap()
619 .try_authority(Some("path"))
620 .unwrap()
621 .try_path("")
622 .unwrap()
623 .try_query(Some("full-path=m/44'/999'/1'"))
624 .unwrap();
625 let uri = builder.build().unwrap();
626 assert_eq!(
627 DerivationPath::from_uri(&uri, false).unwrap(),
628 Some(derivation_path.clone())
629 );
630
631 let mut builder = URIReferenceBuilder::new();
633 builder
634 .try_scheme(Some("test"))
635 .unwrap()
636 .try_authority(Some("path"))
637 .unwrap()
638 .try_path("")
639 .unwrap()
640 .try_query(Some("full-path=m/44\'/999\'/1\'"))
641 .unwrap();
642 let uri = builder.build().unwrap();
643 assert_eq!(
644 DerivationPath::from_uri(&uri, false).unwrap(),
645 Some(derivation_path)
646 );
647
648 let mut builder = URIReferenceBuilder::new();
650 builder
651 .try_scheme(Some("test"))
652 .unwrap()
653 .try_authority(Some("path"))
654 .unwrap()
655 .try_path("")
656 .unwrap()
657 .try_query(Some("full-path=m"))
658 .unwrap();
659 let uri = builder.build().unwrap();
660 assert_eq!(
661 DerivationPath::from_uri(&uri, false).unwrap(),
662 Some(DerivationPath(DerivationPathInner::from_str("m").unwrap()))
663 );
664
665 let mut builder = URIReferenceBuilder::new();
667 builder
668 .try_scheme(Some("test"))
669 .unwrap()
670 .try_authority(Some("path"))
671 .unwrap()
672 .try_path("")
673 .unwrap()
674 .try_query(Some("full-path=m/44/999/1"))
675 .unwrap();
676 let uri = builder.build().unwrap();
677 assert_matches!(
678 DerivationPath::from_uri(&uri, true),
679 Err(DerivationPathError::InvalidDerivationPath(_))
680 );
681
682 let mut builder = URIReferenceBuilder::new();
684 builder
685 .try_scheme(Some("test"))
686 .unwrap()
687 .try_authority(Some("path"))
688 .unwrap()
689 .try_path("")
690 .unwrap()
691 .try_query(Some("key=0/0&full-path=m/44/999/1"))
692 .unwrap();
693 let uri = builder.build().unwrap();
694 assert_matches!(
695 DerivationPath::from_uri(&uri, false),
696 Err(DerivationPathError::InvalidDerivationPath(_))
697 );
698
699 let mut builder = URIReferenceBuilder::new();
701 builder
702 .try_scheme(Some("test"))
703 .unwrap()
704 .try_authority(Some("path"))
705 .unwrap()
706 .try_path("")
707 .unwrap()
708 .try_query(Some("full-path=m/44/999/1&bad-key=0/0"))
709 .unwrap();
710 let uri = builder.build().unwrap();
711 assert_matches!(
712 DerivationPath::from_uri(&uri, false),
713 Err(DerivationPathError::InvalidDerivationPath(_))
714 );
715
716 let mut builder = URIReferenceBuilder::new();
718 builder
719 .try_scheme(Some("test"))
720 .unwrap()
721 .try_authority(Some("path"))
722 .unwrap()
723 .try_path("")
724 .unwrap()
725 .try_query(Some("full-path=bad-value"))
726 .unwrap();
727 let uri = builder.build().unwrap();
728 assert_matches!(
729 DerivationPath::from_uri(&uri, false),
730 Err(DerivationPathError::InvalidDerivationPath(_))
731 );
732
733 let mut builder = URIReferenceBuilder::new();
735 builder
736 .try_scheme(Some("test"))
737 .unwrap()
738 .try_authority(Some("path"))
739 .unwrap()
740 .try_path("")
741 .unwrap()
742 .try_query(Some("full-path="))
743 .unwrap();
744 let uri = builder.build().unwrap();
745 assert_matches!(
746 DerivationPath::from_uri(&uri, false),
747 Err(DerivationPathError::InvalidDerivationPath(_))
748 );
749
750 let mut builder = URIReferenceBuilder::new();
752 builder
753 .try_scheme(Some("test"))
754 .unwrap()
755 .try_authority(Some("path"))
756 .unwrap()
757 .try_path("")
758 .unwrap()
759 .try_query(Some("full-path"))
760 .unwrap();
761 let uri = builder.build().unwrap();
762 assert_matches!(
763 DerivationPath::from_uri(&uri, false),
764 Err(DerivationPathError::InvalidDerivationPath(_))
765 );
766 }
767
768 #[test]
769 fn test_get_query() {
770 let derivation_path = DerivationPath::new_bip44_with_coin(TestCoin, None, None);
771 assert_eq!(derivation_path.get_query(), "".to_string());
772 let derivation_path = DerivationPath::new_bip44_with_coin(TestCoin, Some(1), None);
773 assert_eq!(derivation_path.get_query(), "?key=1'".to_string());
774 let derivation_path = DerivationPath::new_bip44_with_coin(TestCoin, Some(1), Some(2));
775 assert_eq!(derivation_path.get_query(), "?key=1'/2'".to_string());
776 }
777
778 #[test]
779 fn test_derivation_path_debug() {
780 let path = DerivationPath::default();
781 assert_eq!(format!("{path:?}"), "m/44'/501'".to_string());
782
783 let path = DerivationPath::new_bip44(Some(1), None);
784 assert_eq!(format!("{path:?}"), "m/44'/501'/1'".to_string());
785
786 let path = DerivationPath::new_bip44(Some(1), Some(2));
787 assert_eq!(format!("{path:?}"), "m/44'/501'/1'/2'".to_string());
788 }
789}