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