solana_sdk/
derivation_path.rs

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