diff --git a/hd/src/standards.rs b/hd/src/standards.rs index 2efe9b4..ffd80cf 100644 --- a/hd/src/standards.rs +++ b/hd/src/standards.rs @@ -423,7 +423,7 @@ impl DerivationStandard for Bip43 { Some(match slip { KeyApplication::Hashed => Bip43::Bip44, KeyApplication::SegWit => Bip43::Bip84, - KeyApplication::SegWitMiltisig => Bip43::Bip48Native, + KeyApplication::SegWitMultisig => Bip43::Bip48Native, KeyApplication::Nested => Bip43::Bip49, KeyApplication::NestedMultisig => Bip43::Bip48Nested, _ => return None, @@ -585,7 +585,7 @@ impl DerivationStandard for Bip43 { Bip43::Bip44 => slip132::KeyApplication::Hashed, Bip43::Bip45 => return None, Bip43::Bip48Nested => slip132::KeyApplication::NestedMultisig, - Bip43::Bip48Native => slip132::KeyApplication::SegWitMiltisig, + Bip43::Bip48Native => slip132::KeyApplication::SegWitMultisig, Bip43::Bip49 => slip132::KeyApplication::Nested, Bip43::Bip84 => slip132::KeyApplication::SegWit, Bip43::Bip86 => return None, diff --git a/slip132/src/lib.rs b/slip132/src/lib.rs index e63372d..527170e 100644 --- a/slip132/src/lib.rs +++ b/slip132/src/lib.rs @@ -311,7 +311,7 @@ pub enum KeyApplication { /// descriptors #[display("BIP48-native")] #[cfg_attr(feature = "serde", serde(rename = "bip48-native"))] - SegWitMiltisig, + SegWitMultisig, /// yprv/ypub: keys that can be used for P2WPKH-in-P2SH scriptPubkey /// descriptors @@ -340,7 +340,7 @@ impl FromStr for KeyApplication { Ok(match s.to_lowercase().as_str() { "bip44" => KeyApplication::Hashed, "bip84" => KeyApplication::SegWit, - "bip48-native" => KeyApplication::SegWitMiltisig, + "bip48-native" => KeyApplication::SegWitMultisig, "bip49" => KeyApplication::Nested, "bip48-nested" => KeyApplication::NestedMultisig, _ => return Err(UnknownKeyApplicationError), @@ -353,7 +353,7 @@ impl KeyApplication { pub const ALL: [KeyApplication; 5] = [ KeyApplication::Hashed, KeyApplication::SegWit, - KeyApplication::SegWitMiltisig, + KeyApplication::SegWitMultisig, KeyApplication::Nested, KeyApplication::NestedMultisig, ]; @@ -362,16 +362,10 @@ impl KeyApplication { /// path, if possible. pub fn from_derivation_path(path: DerivationPath) -> Option { let path: Vec<_> = path.into(); - for application in &[ - KeyApplication::Hashed, - KeyApplication::SegWit, - KeyApplication::SegWitMiltisig, - KeyApplication::Nested, - KeyApplication::NestedMultisig, - ] { + for application in &Self::ALL { if let Some(standard) = application.to_derivation_path() { let standard: Vec<_> = standard.into(); - if standard.strip_prefix(path.as_slice()).is_some() { + if path.strip_prefix(standard.as_slice()).is_some() { return Some(*application); } } @@ -380,7 +374,7 @@ impl KeyApplication { if path.len() >= 4 && path[0] == bip48_purpose { match path[3] { ChildNumber::Hardened { index: 1 } => Some(KeyApplication::NestedMultisig), - ChildNumber::Hardened { index: 2 } => Some(KeyApplication::SegWitMiltisig), + ChildNumber::Hardened { index: 2 } => Some(KeyApplication::SegWitMultisig), _ => None, } } else { @@ -391,19 +385,16 @@ impl KeyApplication { /// Constructs derivation path matching the provided application pub fn to_derivation_path(&self) -> Option { match self { - Self::Hashed => Some(DerivationPath::from(vec![ - ChildNumber::Hardened { index: 44 }, - ChildNumber::Hardened { index: 0 }, - ])), - Self::Nested => Some(DerivationPath::from(vec![ - ChildNumber::Hardened { index: 49 }, - ChildNumber::Hardened { index: 0 }, - ])), - Self::SegWit => Some(DerivationPath::from(vec![ - ChildNumber::Hardened { index: 84 }, - ChildNumber::Hardened { index: 0 }, - ])), - _ => None, + Self::Hashed => Some(DerivationPath::from(vec![ChildNumber::Hardened { + index: 44, + }])), + Self::Nested => Some(DerivationPath::from(vec![ChildNumber::Hardened { + index: 49, + }])), + Self::SegWit => Some(DerivationPath::from(vec![ChildNumber::Hardened { + index: 84, + }])), + _ => None, // No Multisig? } } } @@ -475,10 +466,10 @@ impl VersionResolver for DefaultResolver { (Network::Bitcoin, KeyApplication::NestedMultisig, true) => { KeyVersion(VERSION_MAGIC_YPRV_MULTISIG) } - (Network::Bitcoin, KeyApplication::SegWitMiltisig, false) => { + (Network::Bitcoin, KeyApplication::SegWitMultisig, false) => { KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG) } - (Network::Bitcoin, KeyApplication::SegWitMiltisig, true) => { + (Network::Bitcoin, KeyApplication::SegWitMultisig, true) => { KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG) } (_, KeyApplication::Hashed, false) => KeyVersion(VERSION_MAGIC_TPUB), @@ -489,8 +480,8 @@ impl VersionResolver for DefaultResolver { (_, KeyApplication::SegWit, true) => KeyVersion(VERSION_MAGIC_VPRV), (_, KeyApplication::NestedMultisig, false) => KeyVersion(VERSION_MAGIC_UPUB_MULTISIG), (_, KeyApplication::NestedMultisig, true) => KeyVersion(VERSION_MAGIC_UPRV_MULTISIG), - (_, KeyApplication::SegWitMiltisig, false) => KeyVersion(VERSION_MAGIC_VPUB_MULTISIG), - (_, KeyApplication::SegWitMiltisig, true) => KeyVersion(VERSION_MAGIC_VPRV_MULTISIG), + (_, KeyApplication::SegWitMultisig, false) => KeyVersion(VERSION_MAGIC_VPUB_MULTISIG), + (_, KeyApplication::SegWitMultisig, true) => KeyVersion(VERSION_MAGIC_VPRV_MULTISIG), } } @@ -551,7 +542,7 @@ impl VersionResolver for DefaultResolver { fn application(kv: &KeyVersion) -> Option { match kv.as_bytes() { &VERSION_MAGIC_XPUB | &VERSION_MAGIC_XPRV | &VERSION_MAGIC_TPUB - | &VERSION_MAGIC_TPRV => None, + | &VERSION_MAGIC_TPRV => Some(KeyApplication::Hashed), &VERSION_MAGIC_YPUB | &VERSION_MAGIC_YPRV | &VERSION_MAGIC_UPUB | &VERSION_MAGIC_UPRV => Some(KeyApplication::Nested), &VERSION_MAGIC_YPUB_MULTISIG @@ -563,15 +554,21 @@ impl VersionResolver for DefaultResolver { &VERSION_MAGIC_ZPUB_MULTISIG | &VERSION_MAGIC_ZPRV_MULTISIG | &VERSION_MAGIC_VPUB_MULTISIG - | &VERSION_MAGIC_VPRV_MULTISIG => Some(KeyApplication::SegWitMiltisig), + | &VERSION_MAGIC_VPRV_MULTISIG => Some(KeyApplication::SegWitMultisig), _ => None, } } fn derivation_path(kv: &KeyVersion, account: Option) -> Option { match kv.as_bytes() { - &VERSION_MAGIC_XPUB | &VERSION_MAGIC_XPRV => None, - &VERSION_MAGIC_TPUB | &VERSION_MAGIC_TPRV => None, + &VERSION_MAGIC_XPUB | &VERSION_MAGIC_XPRV => Some(vec![ + ChildNumber::Hardened { index: 44 }, + ChildNumber::Hardened { index: 0 }, + ]), + &VERSION_MAGIC_TPUB | &VERSION_MAGIC_TPRV => Some(vec![ + ChildNumber::Hardened { index: 44 }, + ChildNumber::Hardened { index: 1 }, + ]), &VERSION_MAGIC_YPUB | &VERSION_MAGIC_YPRV => Some(vec![ ChildNumber::Hardened { index: 49 }, ChildNumber::Hardened { index: 0 }, @@ -702,7 +699,7 @@ impl VersionResolver for DefaultResolver { /// Trait for building standard BIP32 extended keys from SLIP132 variant. pub trait FromSlip132 { - /// Constructts standard BIP32 extended key from SLIP132 string. + /// Constructs standard BIP32 extended key from SLIP132 string. fn from_slip132_str(s: &str) -> Result where Self: Sized; @@ -796,10 +793,983 @@ mod test { use super::*; #[test] - fn bip48() { + fn key_application_from_str() { + assert_eq!( + KeyApplication::from_str("bip44"), + Ok(KeyApplication::Hashed) + ); + assert_eq!( + KeyApplication::from_str("bip84"), + Ok(KeyApplication::SegWit) + ); + assert_eq!( + KeyApplication::from_str("bip48-native"), + Ok(KeyApplication::SegWitMultisig) + ); + assert_eq!( + KeyApplication::from_str("bip49"), + Ok(KeyApplication::Nested) + ); + assert_eq!( + KeyApplication::from_str("bip48-nested"), + Ok(KeyApplication::NestedMultisig) + ); + assert_eq!( + KeyApplication::from_str("bip"), + Err(UnknownKeyApplicationError) + ); + } + + #[test] + fn key_application_from_derivation_path() { + // Mainnet + assert_eq!( + KeyApplication::from_derivation_path("m/44'/0'/3'".parse().unwrap()), + Some(KeyApplication::Hashed) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/49'/0'/5'".parse().unwrap()), + Some(KeyApplication::Nested) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/48'/0'/8'/1'".parse().unwrap()), + Some(KeyApplication::NestedMultisig) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/84'/0'/13'".parse().unwrap()), + Some(KeyApplication::SegWit) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/48'/0'/21'/2'".parse().unwrap()), + Some(KeyApplication::SegWitMultisig) + ); + + // Testnet + assert_eq!( + KeyApplication::from_derivation_path("m/44'/1'/34'".parse().unwrap()), + Some(KeyApplication::Hashed) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/49'/1'/55'".parse().unwrap()), + Some(KeyApplication::Nested) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/48'/1'/89'/1'".parse().unwrap()), + Some(KeyApplication::NestedMultisig) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/84'/1'/144'".parse().unwrap()), + Some(KeyApplication::SegWit) + ); + assert_eq!( + KeyApplication::from_derivation_path("m/48'/1'/233'/2'".parse().unwrap()), + Some(KeyApplication::SegWitMultisig) + ); + + // Unknown application 6' + assert_eq!( + KeyApplication::from_derivation_path("m/6'/0'/233'".parse().unwrap()), + None + ); + + // Unknown script type 21' + assert_eq!( + KeyApplication::from_derivation_path("m/48'/0'/0'/21'".parse().unwrap()), + None + ); + } + + #[test] + fn key_application_to_derivation_path() { + assert_eq!( + KeyApplication::Hashed.to_derivation_path(), + Some(DerivationPath::from_str("m/44'").unwrap()) + ); + assert_eq!( + KeyApplication::Nested.to_derivation_path(), + Some(DerivationPath::from_str("m/49'").unwrap()) + ); + assert_eq!( + KeyApplication::SegWit.to_derivation_path(), + Some(DerivationPath::from_str("m/84'").unwrap()) + ); + assert_eq!(KeyApplication::NestedMultisig.to_derivation_path(), None); + assert_eq!(KeyApplication::SegWitMultisig.to_derivation_path(), None); + } + + #[test] + fn key_version_from_slice() { + let bytes = [0, 2, 4, 8]; + assert_eq!( + KeyVersion::from_slice(&bytes[0..4]), + Some(KeyVersion(bytes)) + ); + + // Too short + assert!(KeyVersion::from_slice(&bytes[0..3]).is_none()); + + // Too long + assert!(KeyVersion::from_slice(&[0, 1, 2, 3, 4]).is_none()); + } + + #[test] + fn key_version_from_xkey_str() { + let zpub = "zpub6qUQGY8YyN3ZztQBDdN8gUrFNvgCdTdFyTNorQ79VfkfkmhMR6D4cHBZ4EnXdFog1e2ugyCJqTcyDE4ZpTGqcMiCEnyPEyJFKbPVL9knhKU"; + assert_eq!( + KeyVersion::from_xkey_str(zpub), + Ok(KeyVersion(VERSION_MAGIC_ZPUB)) + ); + } + + #[test] + fn key_version_from_bytes() { + let bytes = [0, 2, 4, 8]; + assert_eq!(KeyVersion::from_bytes(bytes), KeyVersion(bytes)); + } + + #[test] + fn key_version_from_u32() { + let be_u32 = 132104; + assert_eq!( + KeyVersion::from_u32(be_u32), + KeyVersion(be_u32.to_be_bytes()) + ); + } + + #[test] + fn key_version_to_u32() { + let bytes = [0, 2, 4, 8]; + let be_u32 = u32::from_be_bytes(bytes); + assert_eq!(KeyVersion(bytes).to_u32(), be_u32); + } + + #[test] + fn key_version_as_slice() { + let bytes = [0, 2, 4, 8]; + assert_eq!(KeyVersion(bytes).as_slice(), bytes); + } + + #[test] + fn key_version_as_bytes() { + let bytes = [0, 2, 4, 8]; + assert_eq!(KeyVersion(bytes).as_bytes(), &bytes); + } + + #[test] + fn key_version_to_bytes() { + let bytes = [0, 2, 4, 8]; + assert_eq!(KeyVersion(bytes).to_bytes(), bytes); + } + + #[test] + fn key_version_into_bytes() { + let bytes = [0, 2, 4, 8]; + assert_eq!(KeyVersion(bytes).into_bytes(), bytes); + } + + #[test] + fn default_resolver_resolve() { + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::Hashed, true), + KeyVersion(VERSION_MAGIC_TPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::Hashed, false), + KeyVersion(VERSION_MAGIC_TPUB) + ); + + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::Nested, true), + KeyVersion(VERSION_MAGIC_UPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::NestedMultisig, true), + KeyVersion(VERSION_MAGIC_UPRV_MULTISIG) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::Nested, false), + KeyVersion(VERSION_MAGIC_UPUB) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::NestedMultisig, false), + KeyVersion(VERSION_MAGIC_UPUB_MULTISIG) + ); + + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::SegWit, true), + KeyVersion(VERSION_MAGIC_VPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::SegWitMultisig, true), + KeyVersion(VERSION_MAGIC_VPRV_MULTISIG) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::SegWit, false), + KeyVersion(VERSION_MAGIC_VPUB) + ); + assert_eq!( + DefaultResolver::resolve(Network::Testnet, KeyApplication::SegWitMultisig, false), + KeyVersion(VERSION_MAGIC_VPUB_MULTISIG) + ); + + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::Hashed, true), + KeyVersion(VERSION_MAGIC_XPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::Hashed, false), + KeyVersion(VERSION_MAGIC_XPUB) + ); + + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::Nested, true), + KeyVersion(VERSION_MAGIC_YPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::NestedMultisig, true), + KeyVersion(VERSION_MAGIC_YPRV_MULTISIG) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::Nested, false), + KeyVersion(VERSION_MAGIC_YPUB) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::NestedMultisig, false), + KeyVersion(VERSION_MAGIC_YPUB_MULTISIG) + ); + + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::SegWit, true), + KeyVersion(VERSION_MAGIC_ZPRV) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::SegWitMultisig, true), + KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::SegWit, false), + KeyVersion(VERSION_MAGIC_ZPUB) + ); + assert_eq!( + DefaultResolver::resolve(Network::Bitcoin, KeyApplication::SegWitMultisig, false), + KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG) + ); + } + + #[test] + fn default_resolver_is_pub() { + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_TPRV)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_TPUB)).unwrap()); + + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_UPRV)).unwrap()); + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_UPUB)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)).unwrap()); + + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_VPRV)).unwrap()); + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_VPUB)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)).unwrap()); + + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_XPRV)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_XPUB)).unwrap()); + + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_YPRV)).unwrap()); + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_YPUB)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)).unwrap()); + + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_ZPRV)).unwrap()); + assert!(!DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_ZPUB)).unwrap()); + assert!(DefaultResolver::is_pub(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)).unwrap()); + + assert!(DefaultResolver::is_pub(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn default_resolver_is_prv() { + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_TPRV)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_TPUB)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_UPRV)).unwrap()); + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_UPUB)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_VPRV)).unwrap()); + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_VPUB)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_XPRV)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_XPUB)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_YPRV)).unwrap()); + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_YPUB)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_ZPRV)).unwrap()); + assert!(DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_ZPUB)).unwrap()); + assert!(!DefaultResolver::is_prv(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)).unwrap()); + + assert!(DefaultResolver::is_prv(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn default_resolver_network() { + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_TPRV)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_TPUB)), + Some(Network::Testnet) + ); + + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_UPRV)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_UPUB)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)), + Some(Network::Testnet) + ); + + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_VPRV)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_VPUB)), + Some(Network::Testnet) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)), + Some(Network::Testnet) + ); + + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_XPRV)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_XPUB)), + Some(Network::Bitcoin) + ); + + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_YPRV)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_YPUB)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)), + Some(Network::Bitcoin) + ); + + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_ZPRV)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_ZPUB)), + Some(Network::Bitcoin) + ); + assert_eq!( + DefaultResolver::network(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)), + Some(Network::Bitcoin) + ); + + assert!(DefaultResolver::network(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn default_resolver_application() { + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_TPRV)), + Some(KeyApplication::Hashed) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_TPUB)), + Some(KeyApplication::Hashed) + ); + + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_UPRV)), + Some(KeyApplication::Nested) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)), + Some(KeyApplication::NestedMultisig) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_UPUB)), + Some(KeyApplication::Nested) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)), + Some(KeyApplication::NestedMultisig) + ); + + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_VPRV)), + Some(KeyApplication::SegWit) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)), + Some(KeyApplication::SegWitMultisig) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_VPUB)), + Some(KeyApplication::SegWit) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)), + Some(KeyApplication::SegWitMultisig) + ); + + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_XPRV)), + Some(KeyApplication::Hashed) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_XPUB)), + Some(KeyApplication::Hashed) + ); + + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_YPRV)), + Some(KeyApplication::Nested) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)), + Some(KeyApplication::NestedMultisig) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_YPUB)), + Some(KeyApplication::Nested) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)), + Some(KeyApplication::NestedMultisig) + ); + + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_ZPRV)), + Some(KeyApplication::SegWit) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)), + Some(KeyApplication::SegWitMultisig) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_ZPUB)), + Some(KeyApplication::SegWit) + ); + assert_eq!( + DefaultResolver::application(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)), + Some(KeyApplication::SegWitMultisig) + ); + + assert!(DefaultResolver::application(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn default_resolver_derivation_path() { + let account = Some(ChildNumber::Hardened { index: 100 }); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_TPRV), account) + .unwrap() + .to_string(), + "m/44'/1'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_TPUB), account) + .unwrap() + .to_string(), + "m/44'/1'/100'" + ); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_UPRV), account) + .unwrap() + .to_string(), + "m/49'/1'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/1'/100'/1'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_UPUB), account) + .unwrap() + .to_string(), + "m/49'/1'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/1'/100'/1'" + ); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_VPRV), account) + .unwrap() + .to_string(), + "m/84'/1'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/1'/100'/2'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_VPUB), account) + .unwrap() + .to_string(), + "m/84'/1'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/1'/100'/2'" + ); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_XPRV), account) + .unwrap() + .to_string(), + "m/44'/0'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_XPUB), account) + .unwrap() + .to_string(), + "m/44'/0'/100'" + ); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_YPRV), account) + .unwrap() + .to_string(), + "m/49'/0'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/0'/100'/1'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_YPUB), account) + .unwrap() + .to_string(), + "m/49'/0'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/0'/100'/1'" + ); + + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_ZPRV), account) + .unwrap() + .to_string(), + "m/84'/0'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/0'/100'/2'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_ZPUB), account) + .unwrap() + .to_string(), + "m/84'/0'/100'" + ); + assert_eq!( + DefaultResolver::derivation_path(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG), account) + .unwrap() + .to_string(), + "m/48'/0'/100'/2'" + ); + + assert!(DefaultResolver::derivation_path(&KeyVersion([0, 0, 0, 0]), account).is_none()); + } + + #[test] + fn default_resolver_make_pub() { + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_TPRV)), + Some(KeyVersion(VERSION_MAGIC_TPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_TPUB)), + Some(KeyVersion(VERSION_MAGIC_TPUB)) + ); + + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_UPRV)), + Some(KeyVersion(VERSION_MAGIC_UPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_UPUB)), + Some(KeyVersion(VERSION_MAGIC_UPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_VPRV)), + Some(KeyVersion(VERSION_MAGIC_VPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_VPUB)), + Some(KeyVersion(VERSION_MAGIC_VPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_XPRV)), + Some(KeyVersion(VERSION_MAGIC_XPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_XPUB)), + Some(KeyVersion(VERSION_MAGIC_XPUB)) + ); + + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_YPRV)), + Some(KeyVersion(VERSION_MAGIC_YPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_YPUB)), + Some(KeyVersion(VERSION_MAGIC_YPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_ZPRV)), + Some(KeyVersion(VERSION_MAGIC_ZPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_ZPUB)), + Some(KeyVersion(VERSION_MAGIC_ZPUB)) + ); + assert_eq!( + DefaultResolver::make_pub(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)) + ); + + assert!(DefaultResolver::make_pub(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn default_resolver_make_prv() { + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_TPRV)), + Some(KeyVersion(VERSION_MAGIC_TPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_TPUB)), + Some(KeyVersion(VERSION_MAGIC_TPRV)) + ); + + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_UPRV)), + Some(KeyVersion(VERSION_MAGIC_UPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_UPUB)), + Some(KeyVersion(VERSION_MAGIC_UPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_UPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_UPRV_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_VPRV)), + Some(KeyVersion(VERSION_MAGIC_VPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_VPUB)), + Some(KeyVersion(VERSION_MAGIC_VPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_VPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_VPRV_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_XPRV)), + Some(KeyVersion(VERSION_MAGIC_XPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_XPUB)), + Some(KeyVersion(VERSION_MAGIC_XPRV)) + ); + + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_YPRV)), + Some(KeyVersion(VERSION_MAGIC_YPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_YPUB)), + Some(KeyVersion(VERSION_MAGIC_YPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_YPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_YPRV_MULTISIG)) + ); + + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_ZPRV)), + Some(KeyVersion(VERSION_MAGIC_ZPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_ZPUB)), + Some(KeyVersion(VERSION_MAGIC_ZPRV)) + ); + assert_eq!( + DefaultResolver::make_prv(&KeyVersion(VERSION_MAGIC_ZPUB_MULTISIG)), + Some(KeyVersion(VERSION_MAGIC_ZPRV_MULTISIG)) + ); + + assert!(DefaultResolver::make_prv(&KeyVersion([0, 0, 0, 0])).is_none()); + } + + #[test] + fn xpub_from_slip132_str() { + // Mainnet + let xpub_str = "xpub6BosfCnifzxcJJ1wYuntGJfF2zPJkDeG9ELNHcKNjezuea4tumswN9sH1psMdSVqCMoJC21Bv8usSeqSP4Sp1tLzW7aY59fGn9GCYzx5UTo"; + let xpub = ExtendedPubKey::from_str(xpub_str).unwrap(); + let ypub_str = "ypub6We8xsTdpgW69bD4PGaWUPkkCxXkgqdm4Lrb51DG7fNnhft8AS3VzDXR32pwdM9kbzv6wVbkNoGRKwT16krpp82bNTGxf4Um3sKqwYoGn8q"; + let ypub_multi = "Ypub6hYE67C5Pe4TaANSKw3VJU6Yvka1uCKMNcWFzGUoVSDCKrT2vqRn5LPLqjnRBnNeqTz5p5bsG1evT74mPz1mxc9GCvPN4TwkwbbiXTy4WMA"; + let zpub_str = "zpub6qUQGY8YyN3ZztQBDdN8gUrFNvgCdTdFyTNorQ79VfkfkmhMR6D4cHBZ4EnXdFog1e2ugyCJqTcyDE4ZpTGqcMiCEnyPEyJFKbPVL9knhKU"; + let zpub_multi = "Zpub72NVPmrzYKbwRTZZAHq7WZC46iiTqpJrHj2UmfNgsSb5NxGGBVbLhQ3Urwk1Bh2aF76tZZCRig1ULPgL7gRnkqps5G5neNmFDKfMv51dh4F"; + assert_eq!(ExtendedPubKey::from_slip132_str(xpub_str), Ok(xpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(ypub_str), Ok(xpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(ypub_multi), Ok(xpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(zpub_str), Ok(xpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(zpub_multi), Ok(xpub)); + + // Testnet + let tpub_str = "tpubDCBWBScQPGv4a6Co16myUDzcN7Uxjc9KgrvfeANX5ZkoPrjbyzj2WbY7Frx99wT4zGLCobX4TEjv8qL3mvJ3uKoHZiKqkgKWN6rcK3NAdLv"; + let tpub = ExtendedPubKey::from_str(tpub_str).unwrap(); + let upub_str = "upub5DK5kCmyDxLAkQSb3qS1e3NjX5wxvMfmPtmhwRdibdsGVGdD9oPFVxtrxCzbdiY4ySSswbDWY9rDnnzkDyCmdBJBu6VGKRCoxy5GPFTTwv5"; + let upub_multi = "Upub5QDAsSWQnutYAybxzVtzU7iYEszE8iMMiARNrguFyQhg7TC7vCmXb5knkux5C9kyCuWrpBDdRNEiuxcWXCMimfQrjZbfipforhM8yFdtHZV"; + let vpub_str = "vpub5Y9M3sStNdsebhdhtCDdr8UEh46QryfGK1HvipXbyeF9YNSSQTYp82YzyQxBddBzP5Zgh4p4zpCmg5cJwfcnRQynmSBguL2JEh8umtXSXHN"; + let vpub_multi = "Vpub5j3SB7BKwbS22Go5prgcgCp3Qr8g5LLrdGwbe5o9MR5ZAZ1MArw6D9Qvn7ufC4QtcYdfZepBt2bGoFE5EtmjZu6TbuJ6JjVJ8RQnMkMTT7U"; + assert_eq!(ExtendedPubKey::from_slip132_str(tpub_str), Ok(tpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(upub_str), Ok(tpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(upub_multi), Ok(tpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(vpub_str), Ok(tpub)); + assert_eq!(ExtendedPubKey::from_slip132_str(vpub_multi), Ok(tpub)); + } + + #[test] + fn xprv_from_slip132_str() { + // Mainnet + let xprv_str = "xprv9xpXFhFpqdQK5owUStFsuAiWUxYpLkvQn1QmVDumBKTvmmjkNEZgpMYoAaAftt3JVeDhRkvyLvrKathDToUMdz2FqRF7JNavF7uboJWArrw"; + let xprv = ExtendedPrivKey::from_str(xprv_str).unwrap(); + let yprv_str = "yprvAHenZMvjzJwnw78bHF3W7Fp1evhGHNuuh7vzGcoeZKqopsYyctjFSRCwBn8FtnhDuHLWBEXXobCsUBJnBVtNSDhrhkwXtHQQWqyFBpXETuS"; + let yprv_multi = "YprvAUYsgbfBZGWAMgHyDuWUwL9pNijXVjbW1PafBt5Bw6gDT47tPJ7XXY4rzV5jTDv88kQV3pXegobNbLvYUj3KahpXYE3wHgsQQaF7mkmDXua"; + let zprv_str = "zprvAcV3s2bf8zVGnQKi7bq8KLuWptqiDzuQcETD41hXwLDgsyNCsYtp4Us5Cz5qthM9JvTJvi86GFZRMTvLuCJPETPTa6dxUCDtna2taUzNeUa"; + let zprv_multi = "ZprvAoP8zGL6hx3eCyV64GJ79RFKYgsySMazvW6syGy5K746W9w7dxH69bj11h3KT8a3YPXHoJ8D9TwvUdY7CRTLNwW8QZkMsbgtgJJmANdRWza"; + assert_eq!(ExtendedPrivKey::from_slip132_str(xprv_str), Ok(xprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(yprv_str), Ok(xprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(yprv_multi), Ok(xprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(zprv_str), Ok(xprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(zprv_multi), Ok(xprv)); + + // Testnet + let tprv_str = "tprv8fVU32aAEuEPgdB17T7P4pLVo5y2aGxR7ZKtMeLDfHxQZNUqMbuSL6vF5kLKuFRcs5kURrYjWHS83kExb1pJT3HrN4TQxjJyADf2F32kmMf"; + let tprv = ExtendedPrivKey::from_str(tprv_str).unwrap(); + let uprv_str = "uprv8zKjLhF5PamsXvN7wou1GuRzy47UWtwv2fr793E73JLHcUJ4cG4zxAaP6xHuuA5YGisHBL9Hxwnfw2rXJiEKFGyTEQ9qYe8TRwifdcMUKTP"; + let uprv_multi = "Uprv9BDpTvyWxYLExVXVtUMz6ymogr9jjFdWLwVn4JVeR5AhEeryNfTH3HSJufFPTbJSWBwG3v9QrABB4CUHbwPGPm684sGEx3bTKfzYDSPHHCV"; + let vprv_str = "vprv9K9zeMuzYGKMPDZEnAgdUzXW92FvTWwQwnNKvS7zRJiAfa7HrvEZaEEX8AFVu4jTgMz5vojrRc9DpKU62QeL3Wf46jrG8YwwhfnK26J1Pi6"; + let vprv_multi = "Vprv1CMQ2h95oDkM8omHwD22Go9vqpcjv19x3yLpMZkqw9HAL4kaYU7W2eo4c1HqwNPSVN3wBuqrw5HUiA8z3zHz7cb2QFRfWnUkvYDCHhvLxCW"; + assert_eq!(ExtendedPrivKey::from_slip132_str(tprv_str), Ok(tprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(uprv_str), Ok(tprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(uprv_multi), Ok(tprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(vprv_str), Ok(tprv)); + assert_eq!(ExtendedPrivKey::from_slip132_str(vprv_multi), Ok(tprv)); + } + + #[test] + fn xpub_to_slip132_string() { + let xpub_str = "xpub6BosfCnifzxcJJ1wYuntGJfF2zPJkDeG9ELNHcKNjezuea4tumswN9sH1psMdSVqCMoJC21Bv8usSeqSP4Sp1tLzW7aY59fGn9GCYzx5UTo"; + let xpub = ExtendedPubKey::from_str(xpub_str).unwrap(); + + // Mainnet + assert_eq!( + xpub.to_slip132_string(KeyApplication::Hashed, Network::Bitcoin), + xpub_str + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::Nested, Network::Bitcoin), + "ypub6We8xsTdpgW69bD4PGaWUPkkCxXkgqdm4Lrb51DG7fNnhft8AS3VzDXR32pwdM9kbzv6wVbkNoGRKwT16krpp82bNTGxf4Um3sKqwYoGn8q" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::NestedMultisig, Network::Bitcoin), + "Ypub6hYE67C5Pe4TaANSKw3VJU6Yvka1uCKMNcWFzGUoVSDCKrT2vqRn5LPLqjnRBnNeqTz5p5bsG1evT74mPz1mxc9GCvPN4TwkwbbiXTy4WMA" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::SegWit, Network::Bitcoin), + "zpub6qUQGY8YyN3ZztQBDdN8gUrFNvgCdTdFyTNorQ79VfkfkmhMR6D4cHBZ4EnXdFog1e2ugyCJqTcyDE4ZpTGqcMiCEnyPEyJFKbPVL9knhKU" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::SegWitMultisig, Network::Bitcoin), + "Zpub72NVPmrzYKbwRTZZAHq7WZC46iiTqpJrHj2UmfNgsSb5NxGGBVbLhQ3Urwk1Bh2aF76tZZCRig1ULPgL7gRnkqps5G5neNmFDKfMv51dh4F" + ); + + // Testnet + assert_eq!( + xpub.to_slip132_string(KeyApplication::Hashed, Network::Testnet), + "tpubDCBWBScQPGv4a6Co16myUDzcN7Uxjc9KgrvfeANX5ZkoPrjbyzj2WbY7Frx99wT4zGLCobX4TEjv8qL3mvJ3uKoHZiKqkgKWN6rcK3NAdLv" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::Nested, Network::Testnet), + "upub5DK5kCmyDxLAkQSb3qS1e3NjX5wxvMfmPtmhwRdibdsGVGdD9oPFVxtrxCzbdiY4ySSswbDWY9rDnnzkDyCmdBJBu6VGKRCoxy5GPFTTwv5" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::NestedMultisig, Network::Testnet), + "Upub5QDAsSWQnutYAybxzVtzU7iYEszE8iMMiARNrguFyQhg7TC7vCmXb5knkux5C9kyCuWrpBDdRNEiuxcWXCMimfQrjZbfipforhM8yFdtHZV" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::SegWit, Network::Testnet), + "vpub5Y9M3sStNdsebhdhtCDdr8UEh46QryfGK1HvipXbyeF9YNSSQTYp82YzyQxBddBzP5Zgh4p4zpCmg5cJwfcnRQynmSBguL2JEh8umtXSXHN" + ); + assert_eq!( + xpub.to_slip132_string(KeyApplication::SegWitMultisig, Network::Testnet), + "Vpub5j3SB7BKwbS22Go5prgcgCp3Qr8g5LLrdGwbe5o9MR5ZAZ1MArw6D9Qvn7ufC4QtcYdfZepBt2bGoFE5EtmjZu6TbuJ6JjVJ8RQnMkMTT7U" + ); + } + + #[test] + fn xprv_to_slip132_string() { + let xprv_str = "xprv9xpXFhFpqdQK5owUStFsuAiWUxYpLkvQn1QmVDumBKTvmmjkNEZgpMYoAaAftt3JVeDhRkvyLvrKathDToUMdz2FqRF7JNavF7uboJWArrw"; + let xprv = ExtendedPrivKey::from_str(xprv_str).unwrap(); + + // Mainnet + assert_eq!( + xprv.to_slip132_string(KeyApplication::Hashed, Network::Bitcoin), + xprv_str + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::Nested, Network::Bitcoin), + "yprvAHenZMvjzJwnw78bHF3W7Fp1evhGHNuuh7vzGcoeZKqopsYyctjFSRCwBn8FtnhDuHLWBEXXobCsUBJnBVtNSDhrhkwXtHQQWqyFBpXETuS" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::NestedMultisig, Network::Bitcoin), + "YprvAUYsgbfBZGWAMgHyDuWUwL9pNijXVjbW1PafBt5Bw6gDT47tPJ7XXY4rzV5jTDv88kQV3pXegobNbLvYUj3KahpXYE3wHgsQQaF7mkmDXua" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::SegWit, Network::Bitcoin), + "zprvAcV3s2bf8zVGnQKi7bq8KLuWptqiDzuQcETD41hXwLDgsyNCsYtp4Us5Cz5qthM9JvTJvi86GFZRMTvLuCJPETPTa6dxUCDtna2taUzNeUa" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::SegWitMultisig, Network::Bitcoin), + "ZprvAoP8zGL6hx3eCyV64GJ79RFKYgsySMazvW6syGy5K746W9w7dxH69bj11h3KT8a3YPXHoJ8D9TwvUdY7CRTLNwW8QZkMsbgtgJJmANdRWza" + ); + + // Testnet + assert_eq!( + xprv.to_slip132_string(KeyApplication::Hashed, Network::Testnet), + "tprv8fVU32aAEuEPgdB17T7P4pLVo5y2aGxR7ZKtMeLDfHxQZNUqMbuSL6vF5kLKuFRcs5kURrYjWHS83kExb1pJT3HrN4TQxjJyADf2F32kmMf" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::Nested, Network::Testnet), + "uprv8zKjLhF5PamsXvN7wou1GuRzy47UWtwv2fr793E73JLHcUJ4cG4zxAaP6xHuuA5YGisHBL9Hxwnfw2rXJiEKFGyTEQ9qYe8TRwifdcMUKTP" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::NestedMultisig, Network::Testnet), + "Uprv9BDpTvyWxYLExVXVtUMz6ymogr9jjFdWLwVn4JVeR5AhEeryNfTH3HSJufFPTbJSWBwG3v9QrABB4CUHbwPGPm684sGEx3bTKfzYDSPHHCV" + ); + assert_eq!( + xprv.to_slip132_string(KeyApplication::SegWit, Network::Testnet), + "vprv9K9zeMuzYGKMPDZEnAgdUzXW92FvTWwQwnNKvS7zRJiAfa7HrvEZaEEX8AFVu4jTgMz5vojrRc9DpKU62QeL3Wf46jrG8YwwhfnK26J1Pi6" + ); assert_eq!( - KeyApplication::from_derivation_path("m/48'/0'/8'/2'".parse().unwrap()), - Some(KeyApplication::SegWitMiltisig) - ) + xprv.to_slip132_string(KeyApplication::SegWitMultisig, Network::Testnet), + "Vprv1CMQ2h95oDkM8omHwD22Go9vqpcjv19x3yLpMZkqw9HAL4kaYU7W2eo4c1HqwNPSVN3wBuqrw5HUiA8z3zHz7cb2QFRfWnUkvYDCHhvLxCW" + ); } }