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