ppad-bip32 0.2.0 → 0.3.0
raw patch · 5 files changed
+465/−44 lines, 5 filesdep +ppad-fixeddep ~ppad-secp256k1PVP ok
version bump matches the API change (PVP)
Dependencies added: ppad-fixed
Dependency ranges changed: ppad-secp256k1
API changes (from Hackage documentation)
+ Crypto.HDKey.BIP32: ckd_priv' :: Context -> XPrv -> Word32 -> XPrv
+ Crypto.HDKey.BIP32: ckd_pub' :: Context -> XPub -> Word32 -> Maybe XPub
+ Crypto.HDKey.BIP32: derive' :: Context -> HDKey -> ByteString -> Maybe HDKey
+ Crypto.HDKey.BIP32: derive_child_priv' :: Context -> HDKey -> Word32 -> Maybe HDKey
+ Crypto.HDKey.BIP32: derive_child_pub' :: Context -> HDKey -> Word32 -> Maybe HDKey
+ Crypto.HDKey.BIP32: derive_partial' :: Context -> HDKey -> ByteString -> HDKey
+ Crypto.HDKey.BIP32: n' :: Context -> XPrv -> XPub
+ Crypto.HDKey.BIP32: precompute :: Context
+ Crypto.HDKey.BIP32: type Context = Context
- Crypto.HDKey.BIP32: xprv_key :: XPrv -> Integer
+ Crypto.HDKey.BIP32: xprv_key :: XPrv -> Wider
Files
- CHANGELOG +6/−0
- bench/Main.hs +22/−3
- lib/Crypto/HDKey/BIP32.hs +275/−34
- ppad-bip32.cabal +11/−2
- test/Main.hs +151/−5
CHANGELOG view
@@ -1,5 +1,11 @@ # Changelog +- 0.3.0 (2025-12-21)+ * Upgrades to ppad-secp256k1 v0.5, with significant performance+ improvements as a result.+ * Adds wNAF-enabled functions, suffixed by an apostrophe, which perform+ about 2x faster than normal variants.+ - 0.2.0 (2025-06-21) * The 'xprv' and 'tprv' functions are now total, returning 'Nothing' when derivation is impossible.
bench/Main.hs view
@@ -9,16 +9,21 @@ import Criterion.Main import Crypto.HDKey.BIP32 import Control.DeepSeq-import Crypto.Curve.Secp256k1 as S+import qualified Crypto.Curve.Secp256k1 as S import qualified Data.Maybe as M+import qualified Data.Word.Wider as W instance NFData S.Projective-instance NFData (X Integer)+instance NFData (X W.Wider) instance NFData (X S.Projective) instance NFData XPub instance NFData XPrv instance NFData HDKey +-- precomputed context for wNAF benchmarks+ctx :: Context+ctx = precompute+ main :: IO () main = defaultMain [ bgroup "ppad-bip32" [@@ -28,7 +33,11 @@ , bench_xpub , bench_xprv , bench_parse- ]+ ]+ , bgroup "ppad-bip32 (wNAF)" [+ bench_derive_pub_wnaf+ , bench_derive_priv_wnaf+ ] ] m :: HDKey@@ -53,4 +62,14 @@ bench_parse :: Benchmark bench_parse = bench "parse" $ nf parse (M.fromJust (xprv m))++-- wNAF variants++bench_derive_pub_wnaf :: Benchmark+bench_derive_pub_wnaf =+ bench "derive_child_pub'" $ nf (derive_child_pub' ctx m) 0++bench_derive_priv_wnaf :: Benchmark+bench_derive_priv_wnaf =+ bench "derive_child_priv'" $ nf (derive_child_priv' ctx m) 0
lib/Crypto/HDKey/BIP32.hs view
@@ -3,9 +3,11 @@ {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE ViewPatterns #-} -- |@@ -52,6 +54,17 @@ -- * Child key derivation functions , derive_child_pub , derive_child_priv++ -- * Fast wNAF variants+ , Context+ , precompute+ , ckd_priv'+ , ckd_pub'+ , n'+ , derive'+ , derive_partial'+ , derive_child_priv'+ , derive_child_pub' ) where import Control.Monad (guard)@@ -59,44 +72,165 @@ import qualified Crypto.Hash.SHA512 as SHA512 import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 import qualified Crypto.Curve.Secp256k1 as Secp256k1-import Data.Bits ((.<<.), (.>>.), (.|.), (.&.))+import Data.Bits ((.>>.), (.&.)) import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Base58Check as B58C import qualified Data.ByteString.Builder as BSB import qualified Data.ByteString.Internal as BI+import qualified Data.ByteString.Unsafe as BU import Data.Word (Word8, Word32)+import Data.Word.Limb (Limb(..))+import qualified Data.Word.Limb as L+import Data.Word.Wider (Wider(..))+import qualified Foreign.Storable as Storable (pokeByteOff)+import qualified GHC.Exts as Exts import GHC.Generics+import qualified GHC.Word (Word8(..))+import qualified Numeric.Montgomery.Secp256k1.Scalar as S +-- | Precomputed multiples of the secp256k1 generator point, for faster+-- scalar multiplication.+type Context = Secp256k1.Context++-- | Create a secp256k1 context by precomputing multiples of the curve's+-- generator point.+--+-- This should be computed once and reused for all derivations.+--+-- >>> let !ctx = precompute+-- >>> derive' ctx hd "m/44'/0'/0'/0/0"+precompute :: Context+precompute = Secp256k1.precompute++-- parsing utilities ----------------------------------------------------------++-- convert a Word8 to a Limb+limb :: Word8 -> Limb+limb (GHC.Word.W8# (Exts.word8ToWord# -> w)) = Limb w+{-# INLINABLE limb #-}++-- convert a Limb to a Word8+word8 :: Limb -> Word8+word8 (Limb w) = GHC.Word.W8# (Exts.wordToWord8# w)+{-# INLINABLE word8 #-}++-- unsafely extract the first 64-bit word from a big-endian-encoded bytestring+unsafe_word0 :: BS.ByteString -> Limb+unsafe_word0 bs =+ (limb (BU.unsafeIndex bs 00) `L.shl#` 56#)+ `L.or#` (limb (BU.unsafeIndex bs 01) `L.shl#` 48#)+ `L.or#` (limb (BU.unsafeIndex bs 02) `L.shl#` 40#)+ `L.or#` (limb (BU.unsafeIndex bs 03) `L.shl#` 32#)+ `L.or#` (limb (BU.unsafeIndex bs 04) `L.shl#` 24#)+ `L.or#` (limb (BU.unsafeIndex bs 05) `L.shl#` 16#)+ `L.or#` (limb (BU.unsafeIndex bs 06) `L.shl#` 08#)+ `L.or#` (limb (BU.unsafeIndex bs 07))+{-# INLINABLE unsafe_word0 #-}++-- unsafely extract the second 64-bit word from a big-endian-encoded bytestring+unsafe_word1 :: BS.ByteString -> Limb+unsafe_word1 bs =+ (limb (BU.unsafeIndex bs 08) `L.shl#` 56#)+ `L.or#` (limb (BU.unsafeIndex bs 09) `L.shl#` 48#)+ `L.or#` (limb (BU.unsafeIndex bs 10) `L.shl#` 40#)+ `L.or#` (limb (BU.unsafeIndex bs 11) `L.shl#` 32#)+ `L.or#` (limb (BU.unsafeIndex bs 12) `L.shl#` 24#)+ `L.or#` (limb (BU.unsafeIndex bs 13) `L.shl#` 16#)+ `L.or#` (limb (BU.unsafeIndex bs 14) `L.shl#` 08#)+ `L.or#` (limb (BU.unsafeIndex bs 15))+{-# INLINABLE unsafe_word1 #-}++-- unsafely extract the third 64-bit word from a big-endian-encoded bytestring+unsafe_word2 :: BS.ByteString -> Limb+unsafe_word2 bs =+ (limb (BU.unsafeIndex bs 16) `L.shl#` 56#)+ `L.or#` (limb (BU.unsafeIndex bs 17) `L.shl#` 48#)+ `L.or#` (limb (BU.unsafeIndex bs 18) `L.shl#` 40#)+ `L.or#` (limb (BU.unsafeIndex bs 19) `L.shl#` 32#)+ `L.or#` (limb (BU.unsafeIndex bs 20) `L.shl#` 24#)+ `L.or#` (limb (BU.unsafeIndex bs 21) `L.shl#` 16#)+ `L.or#` (limb (BU.unsafeIndex bs 22) `L.shl#` 08#)+ `L.or#` (limb (BU.unsafeIndex bs 23))+{-# INLINABLE unsafe_word2 #-}++-- unsafely extract the fourth 64-bit word from a big-endian-encoded bytestring+unsafe_word3 :: BS.ByteString -> Limb+unsafe_word3 bs =+ (limb (BU.unsafeIndex bs 24) `L.shl#` 56#)+ `L.or#` (limb (BU.unsafeIndex bs 25) `L.shl#` 48#)+ `L.or#` (limb (BU.unsafeIndex bs 26) `L.shl#` 40#)+ `L.or#` (limb (BU.unsafeIndex bs 27) `L.shl#` 32#)+ `L.or#` (limb (BU.unsafeIndex bs 28) `L.shl#` 24#)+ `L.or#` (limb (BU.unsafeIndex bs 29) `L.shl#` 16#)+ `L.or#` (limb (BU.unsafeIndex bs 30) `L.shl#` 08#)+ `L.or#` (limb (BU.unsafeIndex bs 31))+{-# INLINABLE unsafe_word3 #-}++-- 256-bit big-endian bytestring decoding. the input size is not checked!+unsafe_roll32 :: BS.ByteString -> Wider+unsafe_roll32 bs =+ let !w0 = unsafe_word0 bs+ !w1 = unsafe_word1 bs+ !w2 = unsafe_word2 bs+ !w3 = unsafe_word3 bs+ in Wider (# w3, w2, w1, w0 #)+{-# INLINABLE unsafe_roll32 #-}++-- convert a Limb to a Word8 after right-shifting+word8s :: Limb -> Exts.Int# -> Word8+word8s l s =+ let !(Limb w) = L.shr# l s+ in GHC.Word.W8# (Exts.wordToWord8# w)+{-# INLINABLE word8s #-}+ -- utilities ------------------------------------------------------------------ fi :: (Integral a, Num b) => a -> b fi = fromIntegral {-# INLINE fi #-} --- big-endian bytestring encoding-unroll :: Integer -> BS.ByteString-unroll i = case i of- 0 -> BS.singleton 0- _ -> BS.reverse $ BS.unfoldr coalg i- where- coalg 0 = Nothing- coalg m = Just (fi m, m .>>. 8)---- parse 32 bytes to a 256-bit integer-parse256 :: BS.ByteString -> Integer-parse256 bs@(BI.PS _ _ l)- | l == 32 = BS.foldl' alg 0 bs- | otherwise = error "ppad-bip32 (parse256): internal error"- where- alg !a (fi -> !b) = (a .<<. 8) .|. b---- serialize a 256-bit integer to 32 bytes, left-padding with zeros if--- necessary. the size of the integer is not checked.-ser256 :: Integer -> BS.ByteString-ser256 (unroll -> u@(BI.PS _ _ l))- | l < 32 = BS.replicate (32 - l) 0 <> u- | otherwise = u+-- 256-bit big-endian bytestring encoding+unroll32 :: Wider -> BS.ByteString+unroll32 (Wider (# w0, w1, w2, w3 #)) =+ BI.unsafeCreate 32 $ \ptr -> do+ -- w0+ Storable.pokeByteOff ptr 00 (word8s w3 56#)+ Storable.pokeByteOff ptr 01 (word8s w3 48#)+ Storable.pokeByteOff ptr 02 (word8s w3 40#)+ Storable.pokeByteOff ptr 03 (word8s w3 32#)+ Storable.pokeByteOff ptr 04 (word8s w3 24#)+ Storable.pokeByteOff ptr 05 (word8s w3 16#)+ Storable.pokeByteOff ptr 06 (word8s w3 08#)+ Storable.pokeByteOff ptr 07 (word8 w3)+ -- w1+ Storable.pokeByteOff ptr 08 (word8s w2 56#)+ Storable.pokeByteOff ptr 09 (word8s w2 48#)+ Storable.pokeByteOff ptr 10 (word8s w2 40#)+ Storable.pokeByteOff ptr 11 (word8s w2 32#)+ Storable.pokeByteOff ptr 12 (word8s w2 24#)+ Storable.pokeByteOff ptr 13 (word8s w2 16#)+ Storable.pokeByteOff ptr 14 (word8s w2 08#)+ Storable.pokeByteOff ptr 15 (word8 w2)+ -- w2+ Storable.pokeByteOff ptr 16 (word8s w1 56#)+ Storable.pokeByteOff ptr 17 (word8s w1 48#)+ Storable.pokeByteOff ptr 18 (word8s w1 40#)+ Storable.pokeByteOff ptr 19 (word8s w1 32#)+ Storable.pokeByteOff ptr 20 (word8s w1 24#)+ Storable.pokeByteOff ptr 21 (word8s w1 16#)+ Storable.pokeByteOff ptr 22 (word8s w1 08#)+ Storable.pokeByteOff ptr 23 (word8 w1)+ -- w3+ Storable.pokeByteOff ptr 24 (word8s w0 56#)+ Storable.pokeByteOff ptr 25 (word8s w0 48#)+ Storable.pokeByteOff ptr 26 (word8s w0 40#)+ Storable.pokeByteOff ptr 27 (word8s w0 32#)+ Storable.pokeByteOff ptr 28 (word8s w0 24#)+ Storable.pokeByteOff ptr 29 (word8s w0 16#)+ Storable.pokeByteOff ptr 30 (word8s w0 08#)+ Storable.pokeByteOff ptr 31 (word8 w0)+{-# INLINABLE unroll32 #-} -- serialize a 32-bit word, MSB first ser32 :: Word32 -> BS.ByteString@@ -123,11 +257,11 @@ xpub_cod (XPub (X _ cod)) = cod -- | An extended private key.-newtype XPrv = XPrv (X Integer)+newtype XPrv = XPrv (X Wider) deriving (Eq, Show, Generic) -- | Read the raw private key from an 'XPrv'.-xprv_key :: XPrv -> Integer+xprv_key :: XPrv -> Wider xprv_key (XPrv (X sec _)) = sec -- | Read the raw chain code from an 'XPrv'.@@ -184,7 +318,7 @@ | otherwise = do let i = SHA512.hmac "Bitcoin seed" seed (il, c) = BS.splitAt 32 i- s = parse256 il -- safe due to 512-bit hmac+ s = unsafe_roll32 il -- safe due to 512-bit hmac pure $! (XPrv (X s c)) -- private parent key -> private child key@@ -192,13 +326,13 @@ ckd_priv _xprv@(XPrv (X sec cod)) i = let l = SHA512.hmac cod dat (il, ci) = BS.splitAt 32 l- pil = parse256 il -- safe due to 512-bit hmac- ki = Secp256k1.modQ (pil + sec)+ pil = unsafe_roll32 il -- safe due to 512-bit hmac+ ki = S.from (S.to pil + S.to 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+ dat | hardened i = BS.singleton 0x00 <> unroll32 sec <> ser32 i | otherwise = case Secp256k1.mul Secp256k1._CURVE_G sec of Nothing -> error "ppad-bip32 (ckd_priv): internal error, evil extended key"@@ -212,8 +346,8 @@ let dat = Secp256k1.serialize_point pub <> ser32 i l = SHA512.hmac cod dat (il, ci) = BS.splitAt 32 l- pil = parse256 il -- safe due to 512-bit hmac- pt <- Secp256k1.mul_unsafe Secp256k1._CURVE_G pil+ pil = unsafe_roll32 il -- safe due to 512-bit hmac+ pt <- Secp256k1.mul_vartime 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)@@ -225,6 +359,48 @@ Nothing -> error "ppad-bip32 (n): internal error, evil extended key" Just p -> XPub (X p cod) +-- fast variants --------------------------------------------------------------++-- | The same as 'ckd_priv', but uses a 'Context' to optimise internal+-- calculations.+ckd_priv' :: Context -> XPrv -> Word32 -> XPrv+ckd_priv' ctx _xprv@(XPrv (X sec cod)) i =+ let l = SHA512.hmac cod dat+ (il, ci) = BS.splitAt 32 l+ pil = unsafe_roll32 il -- safe due to 512-bit hmac+ ki = S.from (S.to pil + S.to sec)+ in if pil >= Secp256k1._CURVE_Q || ki == 0 -- negl+ then ckd_priv' ctx _xprv (succ i)+ else XPrv (X ki ci)+ where+ dat | hardened i = BS.singleton 0x00 <> unroll32 sec <> ser32 i+ | otherwise = case Secp256k1.mul_wnaf ctx sec of+ Nothing ->+ error "ppad-bip32 (ckd_priv'): internal error, evil extended key"+ Just p -> Secp256k1.serialize_point p <> ser32 i++-- | The same as 'ckd_pub', but uses a 'Context' to optimise internal+-- calculations.+ckd_pub' :: Context -> XPub -> Word32 -> Maybe XPub+ckd_pub' ctx _xpub@(XPub (X pub cod)) i+ | hardened i = Nothing+ | otherwise = do+ let dat = Secp256k1.serialize_point pub <> ser32 i+ l = SHA512.hmac cod dat+ (il, ci) = BS.splitAt 32 l+ pil = unsafe_roll32 il -- safe due to 512-bit hmac+ pt <- Secp256k1.mul_wnaf ctx pil+ let ki = pt `Secp256k1.add` pub+ if pil >= Secp256k1._CURVE_Q || ki == Secp256k1._CURVE_ZERO -- negl+ then ckd_pub' ctx _xpub (succ i)+ else pure (XPub (X ki ci))++-- | The same as 'n', but uses a 'Context' to optimise internal calculations.+n' :: Context -> XPrv -> XPub+n' ctx (XPrv (X sec cod)) = case Secp256k1.mul_wnaf ctx sec of+ Nothing -> error "ppad-bip32 (n'): internal error, evil extended key"+ Just p -> XPub (X p cod)+ -- hierarchical deterministic keys -------------------------------------------- -- | A BIP32 hierarchical deterministic key.@@ -299,6 +475,33 @@ child = ser32 i pure $! HDKey (Left key) depth parent child +-- | The same as 'derive_child_priv', but uses a 'Context' to optimise+-- internal calculations.+derive_child_priv' :: Context -> HDKey -> Word32 -> Maybe HDKey+derive_child_priv' ctx HDKey {..} i = case hd_key of+ Left _ -> Nothing+ Right _xprv -> pure $!+ let key = Right (ckd_priv' ctx _xprv i)+ depth = hd_depth + 1+ parent = fingerprint _xprv+ child = ser32 i+ in HDKey key depth parent child++-- | The same as 'derive_child_pub', but uses a 'Context' to optimise+-- internal calculations.+derive_child_pub' :: Context -> HDKey -> Word32 -> Maybe HDKey+derive_child_pub' ctx HDKey {..} i = do+ (key, parent) <- case hd_key of+ Left _xpub -> do+ pub <- ckd_pub' ctx _xpub i+ pure $! (pub, fingerprint _xpub)+ Right _xprv ->+ let pub = n' ctx (ckd_priv' ctx _xprv i)+ in pure $! (pub, fingerprint _xprv)+ let depth = hd_depth + 1+ child = ser32 i+ pure $! HDKey (Left key) depth parent child+ -- derivation path expression ------------------------------------------------- -- recursive derivation path@@ -375,6 +578,44 @@ Nothing -> error "ppad-bip32 (derive_partial): couldn't derive extended key" Just hdkey -> hdkey +-- | The same as 'derive', but uses a 'Context' to optimise internal+-- calculations.+--+-- >>> let !ctx = precompute+-- >>> let Just child = derive' ctx hd "m/44'/0'/0'/0/0"+derive'+ :: Context+ -> HDKey+ -> BS.ByteString -- ^ derivation path+ -> Maybe HDKey+derive' ctx hd pat = case parse_path pat of+ Nothing -> Nothing+ Just p -> go p+ where+ go = \case+ M -> pure hd+ p :| i -> do+ hdkey <- go p+ derive_child_priv' ctx hdkey (0x8000_0000 + i) -- 2 ^ 31+ p :/ i -> do+ hdkey <- go p+ derive_child_priv' ctx hdkey i++-- | The same as 'derive_partial', but uses a 'Context' to optimise internal+-- calculations.+--+-- >>> let !ctx = precompute+-- >>> let child = derive_partial' ctx hd "m/44'/0'/0'/0/0"+derive_partial'+ :: Context+ -> HDKey+ -> BS.ByteString+ -> HDKey+derive_partial' ctx hd pat = case derive' ctx hd pat of+ Nothing ->+ error "ppad-bip32 (derive_partial'): couldn't derive extended key"+ Just hdkey -> hdkey+ -- serialization -------------------------------------------------------------- _MAINNET_PUB, _MAINNET_PRV :: Word32@@ -457,7 +698,7 @@ Right (XPrv (X sec cod)) -> BSB.byteString cod <> BSB.word8 0x00- <> BSB.byteString (ser256 sec)+ <> BSB.byteString (unroll32 sec) -- parsing -------------------------------------------------------------------- @@ -505,7 +746,7 @@ let hd_key = Left (XPub (X pub cod)) pure HDKey {..} Prv -> do- (b, parse256 -> prv) <- BS.uncons key -- safe due to guarded keylen+ (b, unsafe_roll32 -> prv) <- BS.uncons key -- safe, 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.2.0+version: 0.3.0 synopsis: BIP32 hierarchical deterministic wallets. license: MIT license-file: LICENSE@@ -15,6 +15,11 @@ hierarchical deterministic wallets and extended keys, with support for serialization and parsing. +flag llvm+ description: Use GHC's LLVM backend.+ default: False+ manual: True+ source-repository head type: git location: git.ppad.tech/bip32.git@@ -24,14 +29,17 @@ hs-source-dirs: lib ghc-options: -Wall+ if flag(llvm)+ ghc-options: -fllvm -O2 exposed-modules: Crypto.HDKey.BIP32 build-depends: base >= 4.9 && < 5 , bytestring >= 0.9 && < 0.13 , ppad-base58 >= 0.2 && < 0.3+ , ppad-fixed >= 0.1 && < 0.2 , ppad-ripemd160 >= 0.1.3 && < 0.2- , ppad-secp256k1 >= 0.4 && < 0.5+ , ppad-secp256k1 >= 0.5 && < 0.6 , ppad-sha256 >= 0.2.3 && < 0.3 , ppad-sha512 >= 0.1.3 && < 0.2 @@ -70,5 +78,6 @@ , criterion , deepseq , ppad-bip32+ , ppad-fixed , ppad-secp256k1
test/Main.hs view
@@ -15,13 +15,25 @@ Nothing -> error "bang" Just v -> v +-- precomputed context for wNAF tests+ctx :: Context+ctx = precompute+ main :: IO () main = defaultMain $ testGroup "BIP32 vectors" [- vector_1- , vector_2- , vector_3- , vector_4- , vector_5+ testGroup "standard" [+ vector_1+ , vector_2+ , vector_3+ , vector_4+ , vector_5+ ]+ , testGroup "wNAF" [+ vector_1_wnaf+ , vector_2_wnaf+ , vector_3_wnaf+ , vector_4_wnaf+ ] ] seed_1 :: BS.ByteString@@ -205,4 +217,138 @@ H.assertEqual "k14" Nothing (parse "xpub661MyMwAqRbcEYS8w7XLSVeEsBXy79zSzH1J8vCdxAZningWLdN3zgtU6Q5JXayek4PRsn35jii4veMimro1xefsM58PgBMrvdYre8QyULY") H.assertEqual "k15" Nothing (parse "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL") +-- wNAF variants -------------------------------------------------------------- +vector_1_wnaf :: TestTree+vector_1_wnaf = H.testCase "BIP32 vector 1 (wNAF)" $ do+ let Just _m = master seed_1+ H.assertEqual "M" xpub_1_m (xpub _m)+ H.assertEqual "m" xprv_1_m (xprv_partial _m)+ let Just _m_0' = derive_child_priv' ctx _m 0x80000000+ H.assertEqual "M/0'" xpub_1_m_0' (xpub _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' ctx _m "m/0'"))+ H.assertEqual "m/0', path" xprv_1_m_0'+ (xprv_partial (derive_partial' ctx _m "m/0'"))+ let Just _m_0'_1 = derive_child_priv' ctx _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_partial _m_0'_1)+ H.assertEqual "M/0'/1" xpub_1_m_0'_1+ (xpub (derive_partial' ctx _m "m/0'/1"))+ H.assertEqual "m/0'/1" xprv_1_m_0'_1+ (xprv_partial (derive_partial' ctx _m "m/0'/1"))+ let Just _m_0'_1_2' = derive_child_priv' ctx _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_partial _m_0'_1_2')+ H.assertEqual "M/0'/1/2'" xpub_1_m_0'_1_2'+ (xpub (derive_partial' ctx _m "m/0'/1/2'"))+ H.assertEqual "m/0'/1/2'" xprv_1_m_0'_1_2'+ (xprv_partial (derive_partial' ctx _m "m/0'/1/2'"))+ let Just _m_0'_1_2'_2 = derive_child_priv' ctx _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_partial _m_0'_1_2'_2)+ H.assertEqual "M/0'/1/2'/2" xpub_1_m_0'_1_2'_2+ (xpub (derive_partial' ctx _m "m/0'/1/2'/2"))+ H.assertEqual "m/0'/1/2'/2" xprv_1_m_0'_1_2'_2+ (xprv_partial (derive_partial' ctx _m "m/0'/1/2'/2"))+ let Just _m_0'_1_2'_2_1000000000 = derive_child_priv' ctx _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_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' ctx _m "m/0'/1/2'/2/1000000000"))+ H.assertEqual "m/0'/1/2'/2/1000000000" xprv_1_m_0'_1_2'_2_1000000000+ (xprv_partial (derive_partial' ctx _m "m/0'/1/2'/2/1000000000"))++vector_2_wnaf :: TestTree+vector_2_wnaf = H.testCase "BIP32 vector 2 (wNAF)" $ do+ let Just mas = master seed_2+ _m = derive_partial' ctx mas "m"+ H.assertEqual "M"+ "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"+ (xpub _m)+ H.assertEqual "m"+ "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"+ (xprv_partial _m)+ let _m_0 = derive_partial' ctx mas "m/0"+ H.assertEqual "M/0"+ "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"+ (xpub _m_0)+ H.assertEqual "m/0"+ "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"+ (xprv_partial _m_0)+ let _m_0_2147483647' = derive_partial' ctx mas "m/0/2147483647'"+ H.assertEqual "M/0/2147483647'"+ "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"+ (xpub _m_0_2147483647')+ H.assertEqual "m/0/2147483647'"+ "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"+ (xprv_partial _m_0_2147483647')+ let _m_0_2147483647'_1 = derive_partial' ctx 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_partial _m_0_2147483647'_1)+ let _m_0_2147483647'_1_2147483646' =+ derive_partial' ctx 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_partial _m_0_2147483647'_1_2147483646')+ let _m_0_2147483647'_1_2147483646'_2 =+ derive_partial' ctx 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_partial _m_0_2147483647'_1_2147483646'_2)++vector_3_wnaf :: TestTree+vector_3_wnaf = H.testCase "BIP32 vector 3 (wNAF)" $ do+ let Just mas = master seed_3+ _m = derive_partial' ctx mas "m"+ H.assertEqual "M"+ "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"+ (xpub _m)+ H.assertEqual "m"+ "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"+ (xprv_partial _m)+ let _m_0' = derive_partial' ctx mas "m/0'"+ H.assertEqual "M/0'"+ "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y"+ (xpub _m_0')+ H.assertEqual "m/0'"+ "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L"+ (xprv_partial _m_0')++vector_4_wnaf :: TestTree+vector_4_wnaf = H.testCase "BIP32 vector 4 (wNAF)" $ do+ let Just mas = master seed_4+ _m = derive_partial' ctx mas "m"+ H.assertEqual "M"+ "xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuyu5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa"+ (xpub _m)+ H.assertEqual "m"+ "xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7fRDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv"+ (xprv_partial _m)+ let _m_0' = derive_partial' ctx mas "m/0'"+ H.assertEqual "M/0'"+ "xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xfFEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m"+ (xpub _m_0')+ H.assertEqual "m/0'"+ "xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaALmPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G"+ (xprv_partial _m_0')+ let _m_0'_1' = derive_partial' ctx mas "m/0'/1'"+ H.assertEqual "M/0'/1'"+ "xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt"+ (xpub _m_0'_1')+ H.assertEqual "m/0'/1'"+ "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJeHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1"+ (xprv_partial _m_0'_1')