From 091e00607638f4a390a0f8a0d23a9c51b9b21798 Mon Sep 17 00:00:00 2001 From: Yuki Kishimoto Date: Mon, 26 Feb 2024 14:36:47 +0100 Subject: [PATCH] core: fix `ExtendedPath::from_derivation_path` parsing --- keechain-core/src/bips/bip32.rs | 9 ++- keechain-core/src/bips/bip44.rs | 109 +++++++++++++++++++++----------- keechain-core/src/bips/bip48.rs | 4 +- 3 files changed, 80 insertions(+), 42 deletions(-) diff --git a/keechain-core/src/bips/bip32.rs b/keechain-core/src/bips/bip32.rs index 64669c6..ec0594b 100644 --- a/keechain-core/src/bips/bip32.rs +++ b/keechain-core/src/bips/bip32.rs @@ -47,7 +47,10 @@ pub fn account_extended_path( // Path: m/'/'/' let path: Vec = vec![ ChildNumber::from_hardened_idx(purpose)?, - ChildNumber::from_hardened_idx(u32::from(!network.eq(&Network::Bitcoin)))?, + ChildNumber::from_hardened_idx(match network { + Network::Bitcoin => 0, + _ => 1, + })?, ChildNumber::from_hardened_idx(account.unwrap_or(0))?, ]; Ok(DerivationPath::from(path)) @@ -61,7 +64,7 @@ pub fn extended_path( ) -> Result { // Path: m/'/'/'/ let base_path = account_extended_path(purpose, network, account)?; - let path: Vec = vec![ChildNumber::from_normal_idx(u32::from(change))?]; + let path: [ChildNumber; 1] = [ChildNumber::from_normal_idx(u32::from(change))?]; Ok(base_path.extend(path)) } @@ -74,6 +77,6 @@ pub fn get_path( ) -> Result { // Path: m/'/'/'// let base_path = extended_path(purpose, network, account, change)?; - let path: Vec = vec![ChildNumber::from_normal_idx(index.unwrap_or(0))?]; + let path: [ChildNumber; 1] = [ChildNumber::from_normal_idx(index.unwrap_or(0))?]; Ok(base_path.extend(path)) } diff --git a/keechain-core/src/bips/bip44.rs b/keechain-core/src/bips/bip44.rs index e1f4309..2467768 100644 --- a/keechain-core/src/bips/bip44.rs +++ b/keechain-core/src/bips/bip44.rs @@ -6,6 +6,7 @@ //! use core::fmt; +use core::slice::Iter; use super::bip32::{self, ChildNumber, DerivationPath}; use super::bip43::Purpose; @@ -77,45 +78,18 @@ pub struct ExtendedPath { impl ExtendedPath { pub fn from_derivation_path(path: &DerivationPath) -> Result { let mut path = path.into_iter(); - let purpose = path.next(); - let coin = path.next(); - let account = path.next(); - let change = path.next(); - - let coin: u32 = match coin { - Some(ChildNumber::Hardened { index: 0 }) => 0, - Some(ChildNumber::Hardened { index: 1 }) => 1, - c => { - return Err(Error::UnsupportedDerivationPath( - UnsupportedDerivationPathError::Coin(c.copied()), - )) - } - }; - - let account: u32 = match account { - Some(ChildNumber::Hardened { index }) => *index, - a => { - return Err(Error::UnsupportedDerivationPath( - UnsupportedDerivationPathError::Account(a.copied()), - )) - } - }; - - let change: bool = match change { - Some(ChildNumber::Normal { index }) => *index != 0, - c => { - return Err(Error::UnsupportedDerivationPath( - UnsupportedDerivationPathError::Change(c.copied()), - )) - } - }; + + let purpose: Option<&ChildNumber> = path.next(); + + let coin: u32 = extract_coin(&mut path)?; + let account: u32 = extract_account(&mut path)?; match purpose { Some(ChildNumber::Hardened { index: 44 }) => Ok(Self { purpose: Purpose::BIP44, coin, account, - change, + change: extract_change(&mut path)?, }), Some(ChildNumber::Hardened { index: 48 }) => { let script: ScriptType = match path.next() { @@ -128,26 +102,26 @@ impl ExtendedPath { purpose: Purpose::BIP48 { script }, coin, account, - change, + change: extract_change(&mut path)?, }) } Some(ChildNumber::Hardened { index: 49 }) => Ok(Self { purpose: Purpose::BIP49, coin, account, - change, + change: extract_change(&mut path)?, }), Some(ChildNumber::Hardened { index: 84 }) => Ok(Self { purpose: Purpose::BIP84, coin, account, - change, + change: extract_change(&mut path)?, }), Some(ChildNumber::Hardened { index: 86 }) => Ok(Self { purpose: Purpose::BIP86, coin, account, - change, + change: extract_change(&mut path)?, }), p => Err(Error::UnsupportedDerivationPath( UnsupportedDerivationPathError::Purpose(p.copied()), @@ -155,3 +129,64 @@ impl ExtendedPath { } } } + +fn extract_coin(path: &mut Iter<'_, ChildNumber>) -> Result { + match path.next() { + Some(ChildNumber::Hardened { index: 0 }) => Ok(0), + Some(ChildNumber::Hardened { index: 1 }) => Ok(1), + c => Err(Error::UnsupportedDerivationPath( + UnsupportedDerivationPathError::Coin(c.copied()), + )), + } +} + +fn extract_account(path: &mut Iter<'_, ChildNumber>) -> Result { + match path.next() { + Some(ChildNumber::Hardened { index }) => Ok(*index), + a => Err(Error::UnsupportedDerivationPath( + UnsupportedDerivationPathError::Account(a.copied()), + )), + } +} + +fn extract_change(path: &mut Iter<'_, ChildNumber>) -> Result { + match path.next() { + Some(ChildNumber::Normal { index: 0 }) => Ok(false), + Some(ChildNumber::Normal { index: 1 }) => Ok(true), + c => Err(Error::UnsupportedDerivationPath( + UnsupportedDerivationPathError::Change(c.copied()), + )), + } +} + +#[cfg(test)] +mod tests { + use core::str::FromStr; + + use super::*; + + #[test] + fn test_extended_path_parsing() { + // BIP44 + // BIP48 path + let path = DerivationPath::from_str("m/86'/0'/22'/1").unwrap(); + let p = ExtendedPath::from_derivation_path(&path).unwrap(); + assert_eq!(p.purpose, Purpose::BIP86); + assert_eq!(p.coin, 0); + assert_eq!(p.account, 22); + assert!(p.change); + + // BIP48 path + let path = DerivationPath::from_str("m/48'/1'/0'/3'/0").unwrap(); + let p = ExtendedPath::from_derivation_path(&path).unwrap(); + assert_eq!( + p.purpose, + Purpose::BIP48 { + script: ScriptType::P2TR + } + ); + assert_eq!(p.coin, 1); + assert_eq!(p.account, 0); + assert!(!p.change); + } +} diff --git a/keechain-core/src/bips/bip48.rs b/keechain-core/src/bips/bip48.rs index 4aa9812..666095c 100644 --- a/keechain-core/src/bips/bip48.rs +++ b/keechain-core/src/bips/bip48.rs @@ -31,7 +31,7 @@ pub fn account_extended_path( ) -> Result { // Path: m/'/'/'/' let base_path = bip32::account_extended_path(48, network, account)?; - let path: Vec = vec![ChildNumber::from_hardened_idx(script_type.as_u32())?]; + let path: [ChildNumber; 1] = [ChildNumber::from_hardened_idx(script_type.as_u32())?]; Ok(base_path.extend(path)) } @@ -43,7 +43,7 @@ pub fn extended_path( ) -> Result { // Path: m/'/'/'/'/ let base_path = account_extended_path(network, account, script_type)?; - let path: Vec = vec![ChildNumber::from_normal_idx(u32::from(change))?]; + let path: [ChildNumber; 1] = [ChildNumber::from_normal_idx(u32::from(change))?]; Ok(base_path.extend(path)) }