packages feed

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 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