ppad-bip32 0.1.1 → 0.2.0
raw patch · 5 files changed
+109/−70 lines, 5 filesdep ~ppad-secp256k1PVP ok
version bump matches the API change (PVP)
Dependency ranges changed: ppad-secp256k1
API changes (from Hackage documentation)
- Crypto.HDKey.BIP32: X :: !a -> !ByteString -> X a
- Crypto.HDKey.BIP32: XPrv :: X Integer -> XPrv
- Crypto.HDKey.BIP32: XPub :: X Projective -> XPub
- Crypto.HDKey.BIP32: newtype XPrv
- Crypto.HDKey.BIP32: newtype XPub
+ Crypto.HDKey.BIP32: data XPrv
+ Crypto.HDKey.BIP32: data XPub
+ Crypto.HDKey.BIP32: instance GHC.Generics.Generic (Crypto.HDKey.BIP32.X a)
+ Crypto.HDKey.BIP32: instance GHC.Generics.Generic Crypto.HDKey.BIP32.HDKey
+ Crypto.HDKey.BIP32: instance GHC.Generics.Generic Crypto.HDKey.BIP32.XPrv
+ Crypto.HDKey.BIP32: instance GHC.Generics.Generic Crypto.HDKey.BIP32.XPub
+ Crypto.HDKey.BIP32: xprv_cod :: XPrv -> ByteString
+ Crypto.HDKey.BIP32: xprv_key :: XPrv -> Integer
+ Crypto.HDKey.BIP32: xpub_cod :: XPub -> ByteString
+ Crypto.HDKey.BIP32: xpub_key :: XPub -> Projective
- Crypto.HDKey.BIP32: tprv :: HDKey -> ByteString
+ Crypto.HDKey.BIP32: tprv :: HDKey -> Maybe ByteString
- Crypto.HDKey.BIP32: xprv :: HDKey -> ByteString
+ Crypto.HDKey.BIP32: xprv :: HDKey -> Maybe ByteString
Files
- CHANGELOG +7/−0
- bench/Main.hs +8/−10
- lib/Crypto/HDKey/BIP32.hs +64/−36
- ppad-bip32.cabal +2/−2
- test/Main.hs +28/−22
CHANGELOG view
@@ -1,5 +1,12 @@ # Changelog +- 0.2.0 (2025-06-21)+ * The 'xprv' and 'tprv' functions are now total, returning 'Nothing' when+ derivation is impossible.+ * The 'XPrv', 'XPub', and 'X' constructors are no longer exported.+ Instead, use the 'x{prv,pub}_key' and 'x{prv,pub}_cod' functions to+ work with their contents.+ - 0.1.1 (2025-02-23) * Haddock improvements and examples.
bench/Main.hs view
@@ -1,5 +1,6 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StandaloneDeriving #-} @@ -9,17 +10,14 @@ import Crypto.HDKey.BIP32 import Control.DeepSeq import Crypto.Curve.Secp256k1 as S+import qualified Data.Maybe as M instance NFData S.Projective--instance NFData XPub where- rnf (XPub (X a b)) = a `deepseq` b `deepseq` ()--instance NFData XPrv where- rnf (XPrv (X a b)) = a `deepseq` b `deepseq` ()--instance NFData HDKey where- rnf (HDKey a b c d) = a `deepseq` b `deepseq` c `deepseq` d `deepseq` ()+instance NFData (X Integer)+instance NFData (X S.Projective)+instance NFData XPub+instance NFData XPrv+instance NFData HDKey main :: IO () main = defaultMain [@@ -54,5 +52,5 @@ bench_xprv = bench "xprv" $ nf xprv m bench_parse :: Benchmark-bench_parse = bench "parse" $ nf parse (xprv m)+bench_parse = bench "parse" $ nf parse (M.fromJust (xprv m))
lib/Crypto/HDKey/BIP32.hs view
@@ -1,6 +1,7 @@ {-# OPTIONS_HADDOCK prune #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE BinaryLiterals #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-}@@ -24,9 +25,13 @@ -- * Extended keys , Extended(..)- , XPub(..)- , XPrv(..)- , X(..)+ , XPub+ , xpub_key+ , xpub_cod+ , XPrv+ , xprv_key+ , xprv_cod+ , X , ckd_pub , ckd_priv , n@@ -61,6 +66,7 @@ import qualified Data.ByteString.Builder as BSB import qualified Data.ByteString.Internal as BI import Data.Word (Word8, Word32)+import GHC.Generics -- utilities ------------------------------------------------------------------ @@ -81,7 +87,7 @@ parse256 :: BS.ByteString -> Integer parse256 bs@(BI.PS _ _ l) | l == 32 = BS.foldl' alg 0 bs- | otherwise = error "ppad-bip32 (parse256): invalid input"+ | otherwise = error "ppad-bip32 (parse256): internal error" where alg !a (fi -> !b) = (a .<<. 8) .|. b @@ -106,15 +112,31 @@ -- | An extended public key. newtype XPub = XPub (X Secp256k1.Projective)- deriving (Eq, Show)+ deriving (Eq, Show, Generic) +-- | Read the raw public key from an 'XPub'.+xpub_key :: XPub -> Secp256k1.Projective+xpub_key (XPub (X pub _)) = pub++-- | Read the raw chain code from an 'XPub'.+xpub_cod :: XPub -> BS.ByteString+xpub_cod (XPub (X _ cod)) = cod+ -- | An extended private key. newtype XPrv = XPrv (X Integer)- deriving (Eq, Show)+ deriving (Eq, Show, Generic) +-- | Read the raw private key from an 'XPrv'.+xprv_key :: XPrv -> Integer+xprv_key (XPrv (X sec _)) = sec++-- | Read the raw chain code from an 'XPrv'.+xprv_cod :: XPrv -> BS.ByteString+xprv_cod (XPrv (X _ cod)) = cod+ -- | A public or private key, extended with a chain code. data X a = X !a !BS.ByteString- deriving (Eq, Show)+ deriving (Eq, Show, Generic) -- | Key types supporting identifier/fingerprint calculation. --@@ -142,10 +164,12 @@ in RIPEMD160.hash (SHA256.hash ser) instance Extended XPrv where- identifier (XPrv (X sec _)) =- let p = Secp256k1.mul Secp256k1._CURVE_G sec- ser = Secp256k1.serialize_point p- in RIPEMD160.hash (SHA256.hash ser)+ identifier (XPrv (X sec _)) = case Secp256k1.mul Secp256k1._CURVE_G sec of+ Nothing ->+ error "ppad-bip32 (identifier): internal error, evil extended key"+ Just p ->+ let ser = Secp256k1.serialize_point p+ in RIPEMD160.hash (SHA256.hash ser) -- internal key derivation functions------------------------------------------- @@ -160,7 +184,7 @@ | otherwise = do let i = SHA512.hmac "Bitcoin seed" seed (il, c) = BS.splitAt 32 i- s = parse256 il+ s = parse256 il -- safe due to 512-bit hmac pure $! (XPrv (X s c)) -- private parent key -> private child key@@ -168,16 +192,17 @@ ckd_priv _xprv@(XPrv (X sec cod)) i = let l = SHA512.hmac cod dat (il, ci) = BS.splitAt 32 l- pil = parse256 il+ pil = parse256 il -- safe due to 512-bit hmac ki = Secp256k1.modQ (pil + sec) in if pil >= Secp256k1._CURVE_Q || ki == 0 -- negl then ckd_priv _xprv (succ i) else XPrv (X ki ci) where dat | hardened i = BS.singleton 0x00 <> ser256 sec <> ser32 i- | otherwise =- let p = Secp256k1.mul Secp256k1._CURVE_G sec- in Secp256k1.serialize_point p <> ser32 i+ | otherwise = case Secp256k1.mul Secp256k1._CURVE_G sec of+ Nothing ->+ error "ppad-bip32 (ckd_priv): internal error, evil extended key"+ Just p -> Secp256k1.serialize_point p <> ser32 i -- public parent key -> public child key ckd_pub :: XPub -> Word32 -> Maybe XPub@@ -187,17 +212,18 @@ let dat = Secp256k1.serialize_point pub <> ser32 i l = SHA512.hmac cod dat (il, ci) = BS.splitAt 32 l- pil = parse256 il- ki = Secp256k1.mul_unsafe Secp256k1._CURVE_G pil `Secp256k1.add` pub+ pil = parse256 il -- safe due to 512-bit hmac+ pt <- Secp256k1.mul_unsafe Secp256k1._CURVE_G pil+ let ki = pt `Secp256k1.add` pub if pil >= Secp256k1._CURVE_Q || ki == Secp256k1._CURVE_ZERO -- negl then ckd_pub _xpub (succ i) else pure (XPub (X ki ci)) -- private parent key -> public child key n :: XPrv -> XPub-n (XPrv (X sec cod)) =- let p = Secp256k1.mul Secp256k1._CURVE_G sec- in XPub (X p cod)+n (XPrv (X sec cod)) = case Secp256k1.mul Secp256k1._CURVE_G sec of+ Nothing -> error "ppad-bip32 (n): internal error, evil extended key"+ Just p -> XPub (X p cod) -- hierarchical deterministic keys -------------------------------------------- @@ -211,7 +237,7 @@ , hd_parent :: !BS.ByteString -- ^ parent fingerprint , hd_child :: !BS.ByteString -- ^ index or child number }- deriving (Eq, Show)+ deriving (Eq, Show, Generic) instance Extended HDKey where identifier (HDKey ekey _ _ _) = case ekey of@@ -338,7 +364,7 @@ -- Fails with 'error' if derivation is impossible, or if the provided -- path is invalid. ----- >>> let Just other_child = derive hd "m/44'/0'/0'/0/1"+-- >>> let other_child = derive_partial hd "m/44'/0'/0'/0/1" -- >>> xpub other_child -- "xpub6FvaeGNFmCkLpkT3uahJnGPTfEX62PtH7uZAyjtru8S2FvPuYTQKn8ct6CNQAwHMXaGN6EYuwi1Tz2VD7msftH8VTAtzgNra9CForA9FBP4" derive_partial@@ -386,12 +412,13 @@ -- | Serialize a mainnet extended private key in base58check format. -- -- >>> xprv hd--- "xprv9s21ZrQH143K3yDvnXtqCqvCBvSiGF7gHVuzGt5rUtjvNPdusR8oS5pErywDM1jDDTcLpNNCbg9a9NuidBczRzSUp7seDeu8am64h6nfdrg"-xprv :: HDKey -> BS.ByteString-xprv x@HDKey {..} = B58C.encode . BS.toStrict . BSB.toLazyByteString $- case hd_key of- Left _ -> error "ppad-bip32 (xprv): no private key"- Right _ -> _serialize _MAINNET_PRV x+-- Just "xprv9s21ZrQH143K3yDvnXtqCqvCBvSiGF7gHVuzGt5rUtjvNPdusR8oS5pErywDM1jDDTcLpNNCbg9a9NuidBczRzSUp7seDeu8am64h6nfdrg"+xprv :: HDKey -> Maybe BS.ByteString+xprv x@HDKey {..} = case hd_key of+ Left _ -> Nothing+ Right _ -> do+ let ser = _serialize _MAINNET_PRV x+ pure $! (B58C.encode . BS.toStrict . BSB.toLazyByteString) ser -- | Serialize a testnet extended public key in base58check format. --@@ -409,12 +436,13 @@ -- | Serialize a testnet extended private key in base58check format. -- -- >>> tprv hd--- "tprv8ZgxMBicQKsPenTTT6kLNVYBW3rvVm9gd3q79JWJxsEQ9zNzrnUYwqBgnA6sMP7Xau97pTyxm2jNcETTkPxwF3i5Lm5wt1dBVrqV8kKi5v5"-tprv :: HDKey -> BS.ByteString-tprv x@HDKey {..} = B58C.encode . BS.toStrict . BSB.toLazyByteString $- case hd_key of- Left _ -> error "ppad-bip32 (tprv): no private key"- Right _ -> _serialize _TESTNET_PRV x+-- Just "tprv8ZgxMBicQKsPenTTT6kLNVYBW3rvVm9gd3q79JWJxsEQ9zNzrnUYwqBgnA6sMP7Xau97pTyxm2jNcETTkPxwF3i5Lm5wt1dBVrqV8kKi5v5"+tprv :: HDKey -> Maybe BS.ByteString+tprv x@HDKey {..} = case hd_key of+ Left _ -> Nothing+ Right _ -> do+ let ser = _serialize _TESTNET_PRV x+ pure $! (B58C.encode . BS.toStrict . BSB.toLazyByteString) ser _serialize :: Word32 -> HDKey -> BSB.Builder _serialize version HDKey {..} =@@ -477,7 +505,7 @@ let hd_key = Left (XPub (X pub cod)) pure HDKey {..} Prv -> do- (b, parse256 -> prv) <- BS.uncons key+ (b, parse256 -> prv) <- BS.uncons key -- safe due to guarded keylen guard (b == 0) guard (prv > 0 && prv < Secp256k1._CURVE_Q) let hd_key = Right (XPrv (X prv cod))
ppad-bip32.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: ppad-bip32-version: 0.1.1+version: 0.2.0 synopsis: BIP32 hierarchical deterministic wallets. license: MIT license-file: LICENSE@@ -31,7 +31,7 @@ , bytestring >= 0.9 && < 0.13 , ppad-base58 >= 0.2 && < 0.3 , ppad-ripemd160 >= 0.1.3 && < 0.2- , ppad-secp256k1 >= 0.2.2 && < 0.3+ , ppad-secp256k1 >= 0.4 && < 0.5 , ppad-sha256 >= 0.2.3 && < 0.3 , ppad-sha512 >= 0.1.3 && < 0.2
test/Main.hs view
@@ -9,6 +9,12 @@ import Test.Tasty import qualified Test.Tasty.HUnit as H +-- for testing+xprv_partial :: HDKey -> BS.ByteString+xprv_partial val = case xprv val of+ Nothing -> error "bang"+ Just v -> v+ main :: IO () main = defaultMain $ testGroup "BIP32 vectors" [ vector_1@@ -63,38 +69,38 @@ vector_1 = H.testCase "BIP32 vector 1" $ do let Just _m = master seed_1 H.assertEqual "M" xpub_1_m (xpub _m)- H.assertEqual "m" xprv_1_m (xprv _m)+ H.assertEqual "m" xprv_1_m (xprv_partial _m) let Just _m_0' = derive_child_priv _m 0x80000000 H.assertEqual "M/0'" xpub_1_m_0' (xpub _m_0')- H.assertEqual "m/0'" xprv_1_m_0' (xprv _m_0')+ H.assertEqual "m/0'" xprv_1_m_0' (xprv_partial _m_0') H.assertEqual "M/0', path" xpub_1_m_0' (xpub (derive_partial _m "m/0'"))- H.assertEqual "m/0', path" xprv_1_m_0' (xprv (derive_partial _m "m/0'"))+ H.assertEqual "m/0', path" xprv_1_m_0' (xprv_partial (derive_partial _m "m/0'")) let Just _m_0'_1 = derive_child_priv _m_0' 1 H.assertEqual "M/0'/1" xpub_1_m_0'_1 (xpub _m_0'_1)- H.assertEqual "m/0'/1" xprv_1_m_0'_1 (xprv _m_0'_1)+ H.assertEqual "m/0'/1" xprv_1_m_0'_1 (xprv_partial _m_0'_1) H.assertEqual "M/0'/1" xpub_1_m_0'_1 (xpub (derive_partial _m "m/0'/1"))- H.assertEqual "m/0'/1" xprv_1_m_0'_1 (xprv (derive_partial _m "m/0'/1"))+ H.assertEqual "m/0'/1" xprv_1_m_0'_1 (xprv_partial (derive_partial _m "m/0'/1")) let Just _m_0'_1_2' = derive_child_priv _m_0'_1 (0x80000000 + 2) H.assertEqual "M/0'/1/2'" xpub_1_m_0'_1_2' (xpub _m_0'_1_2')- H.assertEqual "m/0'/1/2'" xprv_1_m_0'_1_2' (xprv _m_0'_1_2')+ H.assertEqual "m/0'/1/2'" xprv_1_m_0'_1_2' (xprv_partial _m_0'_1_2') H.assertEqual "M/0'/1/2'" xpub_1_m_0'_1_2' (xpub (derive_partial _m "m/0'/1/2'"))- H.assertEqual "m/0'/1/2'" xprv_1_m_0'_1_2' (xprv (derive_partial _m "m/0'/1/2'"))+ H.assertEqual "m/0'/1/2'" xprv_1_m_0'_1_2' (xprv_partial (derive_partial _m "m/0'/1/2'")) let Just _m_0'_1_2'_2 = derive_child_priv _m_0'_1_2' 2 H.assertEqual "M/0'/1/2'/2" xpub_1_m_0'_1_2'_2 (xpub _m_0'_1_2'_2)- H.assertEqual "m/0'/1/2'/2" xprv_1_m_0'_1_2'_2 (xprv _m_0'_1_2'_2)+ H.assertEqual "m/0'/1/2'/2" xprv_1_m_0'_1_2'_2 (xprv_partial _m_0'_1_2'_2) H.assertEqual "M/0'/1/2'/2" xpub_1_m_0'_1_2'_2 (xpub (derive_partial _m "m/0'/1/2'/2")) H.assertEqual "m/0'/1/2'/2" xprv_1_m_0'_1_2'_2- (xprv (derive_partial _m "m/0'/1/2'/2"))+ (xprv_partial (derive_partial _m "m/0'/1/2'/2")) let Just _m_0'_1_2'_2_1000000000 = derive_child_priv _m_0'_1_2'_2 1000000000 H.assertEqual "M/0'/1/2'/2/1000000000" xpub_1_m_0'_1_2'_2_1000000000 (xpub _m_0'_1_2'_2_1000000000) H.assertEqual "m/0'/1/2'/2/1000000000" xprv_1_m_0'_1_2'_2_1000000000- (xprv _m_0'_1_2'_2_1000000000)+ (xprv_partial _m_0'_1_2'_2_1000000000) H.assertEqual "M/0'/1/2'/2/1000000000" xpub_1_m_0'_1_2'_2_1000000000 (xpub (derive_partial _m "m/0'/1/2'/2/1000000000")) H.assertEqual "m/0'/1/2'/2/1000000000" xprv_1_m_0'_1_2'_2_1000000000- (xprv (derive_partial _m "m/0'/1/2'/2/1000000000"))+ (xprv_partial (derive_partial _m "m/0'/1/2'/2/1000000000")) seed_2 :: BS.ByteString seed_2 = case B16.decode "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" of@@ -108,34 +114,34 @@ H.assertEqual "M" "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" (xpub _m) H.assertEqual "m" "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"- (xprv _m)+ (xprv_partial _m) let _m_0 = derive_partial mas "m/0" H.assertEqual "M/0" "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" (xpub _m_0) H.assertEqual "m/0" "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"- (xprv _m_0)+ (xprv_partial _m_0) let _m_0_2147483647' = derive_partial mas "m/0/2147483647'" H.assertEqual "M/0/2147483647'" "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a" (xpub _m_0_2147483647') H.assertEqual "m/0/2147483647'" "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"- (xprv _m_0_2147483647')+ (xprv_partial _m_0_2147483647') let _m_0_2147483647'_1 = derive_partial mas "m/0/2147483647'/1" H.assertEqual "M/0/2147483647'/1" "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon" (xpub _m_0_2147483647'_1) H.assertEqual "m/0/2147483647'/1" "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"- (xprv _m_0_2147483647'_1)+ (xprv_partial _m_0_2147483647'_1) let _m_0_2147483647'_1_2147483646' = derive_partial mas "m/0/2147483647'/1/2147483646'" H.assertEqual "M/0/2147483647'/1/2147483646'" "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL" (xpub _m_0_2147483647'_1_2147483646') H.assertEqual "m/0/2147483647'/1/2147483646'" "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"- (xprv _m_0_2147483647'_1_2147483646')+ (xprv_partial _m_0_2147483647'_1_2147483646') let _m_0_2147483647'_1_2147483646'_2 = derive_partial mas "m/0/2147483647'/1/2147483646'/2" H.assertEqual "M/0/2147483647'/1/2147483646'/2" "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt" (xpub _m_0_2147483647'_1_2147483646'_2) H.assertEqual "m/0/2147483647'/1/2147483646'/2" "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"- (xprv _m_0_2147483647'_1_2147483646'_2)+ (xprv_partial _m_0_2147483647'_1_2147483646'_2) seed_3 :: BS.ByteString seed_3 = case B16.decode "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be" of@@ -149,12 +155,12 @@ H.assertEqual "M" "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13" (xpub _m) H.assertEqual "m" "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"- (xprv _m)+ (xprv_partial _m) let _m_0' = derive_partial mas "m/0'" H.assertEqual "M/0'" "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y" (xpub _m_0') H.assertEqual "m/0'" "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L"- (xprv _m_0')+ (xprv_partial _m_0') seed_4 :: BS.ByteString seed_4 = case B16.decode "3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678" of@@ -168,17 +174,17 @@ H.assertEqual "M" "xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa" (xpub _m) H.assertEqual "m" "xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv"- (xprv _m)+ (xprv_partial _m) let _m_0' = derive_partial mas "m/0'" H.assertEqual "M/0'" "xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m" (xpub _m_0') H.assertEqual "m/0'" "xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G"- (xprv _m_0')+ (xprv_partial _m_0') let _m_0'_1' = derive_partial mas "m/0'/1'" H.assertEqual "M/0'/1'" "xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt" (xpub _m_0'_1') H.assertEqual "m/0'/1'" "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1"- (xprv _m_0'_1')+ (xprv_partial _m_0'_1') vector_5 :: TestTree vector_5 = H.testCase "BIP32 vector 5" $ do