{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} module Haskoin.Crypto.Keys.ExtendedSpec (spec) where import Control.Monad (forM_) import Data.Aeson as A import Data.Bits ((.&.)) import Data.ByteString.Lazy.Char8 qualified as B8 import Data.Bytes.Get import Data.Bytes.Put import Data.Bytes.Serial import Data.Either (isLeft) import Data.Maybe (fromJust, isJust, isNothing) import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word32) import Haskoin.Address import Haskoin.Crypto import Haskoin.Network.Constants import Haskoin.Util import Haskoin.Util.Arbitrary import Test.HUnit import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck hiding ((.&.)) serialVals :: [SerialBox] serialVals = [ SerialBox arbitraryDerivPath, SerialBox arbitraryHardPath, SerialBox arbitrarySoftPath ] readVals :: Ctx -> [ReadBox] readVals ctx = [ ReadBox arbitraryDerivPath, ReadBox arbitraryHardPath, ReadBox arbitrarySoftPath, ReadBox arbitraryXPrvKey, ReadBox (snd <$> arbitraryXPubKey ctx), ReadBox arbitraryParsedPath, ReadBox arbitraryBip32PathIndex ] jsonVals :: [JsonBox] jsonVals = [ JsonBox arbitraryDerivPath, JsonBox arbitraryHardPath, JsonBox arbitrarySoftPath, JsonBox arbitraryParsedPath ] netVals :: Ctx -> [NetBox] netVals ctx = [ NetBox ( marshalValue, marshalEncoding, unmarshalValue, genNetData arbitraryXPrvKey ), NetBox ( marshalValue . (,ctx), marshalEncoding . (,ctx), unmarshalValue . (,ctx), genNetData (snd <$> arbitraryXPubKey ctx) ) ] spec :: Spec spec = prepareContext $ \ctx -> do testIdentity serialVals (readVals ctx) jsonVals (netVals ctx) describe "Custom identity tests" $ do prop "encodes and decodes extended private key" $ forAll arbitraryNetwork $ \net -> forAll arbitraryXPrvKey $ customCerealID (marshalGet net) (marshalPut net) prop "encodes and decodes extended public key" $ forAll arbitraryNetwork $ \net -> forAll (arbitraryXPubKey ctx) $ customCerealID (marshalGet (net, ctx)) (marshalPut (net, ctx)) . snd describe "bip32 subkey derivation vector 1" $ vectorSpec ctx m1 vector1 describe "bip32 subkey derivation vector 2" $ vectorSpec ctx m2 vector2 describe "bip32 subkey derivation vector 3" $ vectorSpec ctx m3 vector3 describe "bip32 subkey derivation using string path" $ do it "either derivations" $ testApplyPath ctx it "either derivations" $ testBadApplyPath ctx it "dublic derivations" $ testDerivePubPath ctx it "private derivations" $ testDerivePrvPath ctx it "path parsing" testParsePath it "from json" testFromJsonPath it "to json" testToJsonPath describe "Derivation Paths" $ do prop "from string derivation path" $ forAll arbitraryDerivPath $ \p -> fromString (cs $ pathToStr p) == p prop "from string hard derivation path" $ forAll arbitraryHardPath $ \p -> fromString (cs $ pathToStr p) == p prop "from string soft derivation path" $ forAll arbitrarySoftPath $ \p -> fromString (cs $ pathToStr p) == p prop "from and to lists of derivation paths" $ forAll arbitraryDerivPath $ \p -> listToPath (pathToList p) == p prop "from and to lists of hard derivation paths" $ forAll arbitraryHardPath $ \p -> toHard (listToPath $ pathToList p) == Just p prop "from and to lists of soft derivation paths" $ forAll arbitrarySoftPath $ \p -> toSoft (listToPath $ pathToList p) == Just p describe "Extended Keys" $ do let net = btc prop "computes pubkey of a subkey is subkey of the pubkey" $ forAll arbitraryXPrvKey $ pubKeyOfSubKeyIsSubKeyOfPubKey ctx prop "exports and imports extended private key" $ forAll arbitraryXPrvKey $ \k -> xPrvImport net (xPrvExport net k) == Just k prop "exports and imports extended public key" $ forAll (arbitraryXPubKey ctx) $ \(_, k) -> xPubImport net ctx (xPubExport net ctx k) == Just k pubKeyOfSubKeyIsSubKeyOfPubKey :: Ctx -> XPrvKey -> Word32 -> Bool pubKeyOfSubKeyIsSubKeyOfPubKey ctx k i = deriveXPubKey ctx (prvSubKey ctx k i') == pubSubKey ctx (deriveXPubKey ctx k) i' where i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation testFromJsonPath :: Assertion testFromJsonPath = sequence_ $ do path <- jsonPathVectors return $ assertEqual path (Just [fromString path :: DerivPath]) (A.decode $ B8.pack $ "[\"" ++ path ++ "\"]") testToJsonPath :: Assertion testToJsonPath = sequence_ $ do path <- jsonPathVectors return $ assertEqual path (B8.pack $ "[\"" ++ path ++ "\"]") (A.encode [fromString path :: ParsedPath]) jsonPathVectors :: [String] jsonPathVectors = [ "m", "m/0", "m/0'", "M/0'", "m/2147483647", "M/2147483647", "m/1/2/3/4/5/6/7/8", "M/1/2/3/4/5/6/7/8", "m/1'/2'/3/4", "M/1'/2'/3/4" ] testParsePath :: Assertion testParsePath = sequence_ $ do (path, t) <- parsePathVectors return $ assertBool path (t $ parsePath path) parsePathVectors :: [(String, Maybe ParsedPath -> Bool)] parsePathVectors = [ ("m", isJust), ("m/0'", isJust), ("M/0'", isJust), ("m/2147483648", isNothing), ("m/2147483647", isJust), ("M/2147483648", isNothing), ("M/2147483647", isJust), ("M/-1", isNothing), ("M/-2147483648", isNothing), ("m/1/2/3/4/5/6/7/8", isJust), ("M/1/2/3/4/5/6/7/8", isJust), ("m/1'/2'/3/4", isJust), ("M/1'/2'/3/4", isJust), ("m/1/2'/3/4'", isJust), ("M/1/2'/3/4'", isJust), ("meh", isNothing), ("infinity", isNothing), ("NaN", isNothing) ] testApplyPath :: Ctx -> Assertion testApplyPath ctx = sequence_ $ do (key, path, final) <- applyPathVectors ctx return $ assertEqual path final $ applyPath ctx (fromJust $ parsePath path) key testBadApplyPath :: Ctx -> Assertion testBadApplyPath ctx = sequence_ $ do (key, path) <- badApplyPathVectors ctx return $ assertBool path $ isLeft $ applyPath ctx (fromJust $ parsePath path) key testDerivePubPath :: Ctx -> Assertion testDerivePubPath ctx = sequence_ $ do (key, path, final) <- derivePubPathVectors ctx return $ assertEqual path final $ derivePubPath ctx (fromString path :: SoftPath) key testDerivePrvPath :: Ctx -> Assertion testDerivePrvPath ctx = sequence_ $ do (key, path, final) <- derivePrvPathVectors ctx return $ assertEqual path final $ derivePath ctx (fromString path :: DerivPath) key derivePubPathVectors :: Ctx -> [(XPubKey, String, XPubKey)] derivePubPathVectors ctx = [ (xpub, "M", xpub), (xpub, "M/8", pubSubKey ctx xpub 8), (xpub, "M/8/30/1", foldl (pubSubKey ctx) xpub [8, 30, 1]) ] where xprv = fromJust $ xPrvImport btc "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\ \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB" xpub = deriveXPubKey ctx xprv derivePrvPathVectors :: Ctx -> [(XPrvKey, String, XPrvKey)] derivePrvPathVectors ctx = [ (xprv, "m", xprv), (xprv, "M", xprv), (xprv, "m/8'", hardSubKey ctx xprv 8), (xprv, "M/8'", hardSubKey ctx xprv 8), ( xprv, "m/8'/30/1", foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1] ), ( xprv, "M/8'/30/1", foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1] ), ( xprv, "m/3/20", foldl (prvSubKey ctx) xprv [3, 20] ), ( xprv, "M/3/20", foldl (prvSubKey ctx) xprv [3, 20] ) ] where xprv = fromJust $ xPrvImport btc "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\ \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB" applyPathVectors :: Ctx -> [(XKey, String, Either String XKey)] applyPathVectors ctx = [ (XPrv xprv btc, "m", Right (XPrv xprv btc)), (XPrv xprv btc, "M", Right (XPub xpub btc)), (XPrv xprv btc, "m/8'", Right (XPrv (hardSubKey ctx xprv 8) btc)), ( XPrv xprv btc, "M/8'", Right (XPub (deriveXPubKey ctx (hardSubKey ctx xprv 8)) btc) ), ( XPrv xprv btc, "m/8'/30/1", Right (XPrv (foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1]) btc) ), ( XPrv xprv btc, "M/8'/30/1", Right ( XPub (deriveXPubKey ctx (foldl (prvSubKey ctx) (hardSubKey ctx xprv 8) [30, 1])) btc ) ), (XPrv xprv btc, "m/3/20", Right (XPrv (foldl (prvSubKey ctx) xprv [3, 20]) btc)), ( XPrv xprv btc, "M/3/20", Right (XPub (deriveXPubKey ctx (foldl (prvSubKey ctx) xprv [3, 20])) btc) ), ( XPub xpub btc, "M/3/20", Right (XPub (deriveXPubKey ctx (foldl (prvSubKey ctx) xprv [3, 20])) btc) ) ] where xprv = fromJust $ xPrvImport btc "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\ \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB" xpub = deriveXPubKey ctx xprv badApplyPathVectors :: Ctx -> [(XKey, String)] badApplyPathVectors ctx = [ (XPub xpub btc, "m/8'"), (XPub xpub btc, "M/8'"), (XPub xpub btc, "M/1/2/3'/4/5") ] where xprv = fromJust $ xPrvImport btc "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\ \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB" xpub = deriveXPubKey ctx xprv -- BIP 0032 Test Vectors -- https://en.bitcoin.it/wiki/BIP_0032_TestVectors bip44Addr :: DerivPath bip44Addr = Deriv :| 44 :| 0 :| 0 :/ 0 :/ 0 vectorSpec :: Ctx -> TestKey -> [TestVector] -> Spec vectorSpec ctx mTxt vecTxt = forM_ (parseVector ctx mTxt vecTxt) $ \(d, m, v) -> it ("chain " <> cs d) $ runVector ctx m v runVector :: Ctx -> XPrvKey -> TestVector -> Assertion runVector ctx m v = do assertBool "xPrvID" $ encodeHex ((runPutS . serialize) (xPrvID ctx m)) == head v assertBool "xPrvFP" $ encodeHex ((runPutS . serialize) (xPrvFP ctx m)) == v !! 1 assertBool "xPrvAddr" $ addrToText btc (xPubAddr ctx $ deriveXPubKey ctx m) == Just (v !! 2) assertBool "bip44Addr" $ addrToText btc (xPubAddr ctx $ deriveXPubKey ctx $ derivePath ctx bip44Addr m) == Just (v !! 3) assertBool "prvKey" $ encodeHex m.key.get == v !! 4 assertBool "xPrvWIF" $ xPrvWif btc m == v !! 5 assertBool "pubKey" $ encodeHex (exportPubKey ctx True (deriveXPubKey ctx m).key) == v !! 6 assertBool "chain code" $ encodeHex (runPutS (serialize m.chain)) == v !! 7 assertBool "Hex PubKey" $ encodeHex (marshal (btc, ctx) (deriveXPubKey ctx m)) == v !! 8 assertBool "Hex PrvKey" $ encodeHex (marshal btc m) == v !! 9 assertBool "Base58 PubKey" $ xPubExport btc ctx (deriveXPubKey ctx m) == v !! 10 assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 11 -- This function was used to generate addition data for the test vectors genVector :: Ctx -> XPrvKey -> [(Text, Text)] genVector ctx m = [ ("xPrvID", encodeHex (runPutS . serialize $ xPrvID ctx m)), ("xPrvFP", encodeHex (runPutS . serialize $ xPrvFP ctx m)), ("xPrvAddr", fromJust $ addrToText btc (xPubAddr ctx $ deriveXPubKey ctx m)), ( "bip44Addr", fromJust $ addrToText btc (xPubAddr ctx $ deriveXPubKey ctx $ derivePath ctx bip44Addr m) ), ("prvKey", encodeHex m.key.get), ("xPrvWIF", xPrvWif btc m), ("pubKey", encodeHex (exportPubKey ctx True (deriveXPubKey ctx m).key)), ("chain code", encodeHex ((runPutS . serialize) m.chain)), ("Hex PubKey", encodeHex (marshal (btc, ctx) (deriveXPubKey ctx m))), ("Hex PrvKey", encodeHex (marshal btc m)) ] parseVector :: Ctx -> TestKey -> [TestVector] -> [(Text, XPrvKey, TestVector)] parseVector ctx mTxt vs = go <$> vs where mast = makeXPrvKey $ fromJust $ decodeHex mTxt go (d : vec) = let deriv = ((.get) . fromJust . parsePath) (cs d) in (d, derivePath ctx deriv mast, vec) go _ = undefined type TestVector = [Text] type TestKey = Text m1 :: TestKey m1 = "000102030405060708090a0b0c0d0e0f" vector1 :: [TestVector] vector1 = [ [ "m", "3442193e1bb70916e914552172cd4e2dbc9df811", "3442193e", "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma", "1NQpH6Nf8QtR2HphLRcvuVqfhXBXsiWn8r", "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW", "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35", "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi" ], [ "m/0'", "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", "5c1bd648", "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh", "1DDVw6BRKUv9U8Hzg5rGsia13nDrgJQpBd", "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea", "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT", "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea", "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7" ], [ "m/0'/1", "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe", "bef5a2f9", "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj", "1KMg6dRggXSkpz9fFyU76ru83TUSwPePEZ", "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368", "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM", "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c", "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19", "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c", "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368", "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs" ], [ "m/0'/1/2'", "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72", "ee7ab90c", "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x", "1WykKhR25y7VDT21nZEwUUKSKDz9pENJh", "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca", "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU", "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f", "0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2", "0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca", "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM" ], [ "m/0'/1/2'/2", "d880d7d893848509a62d8fb74e32148dac68412f", "d880d7d8", "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt", "1asQ3smHhv2nv5R6hPpiUfkEorJpsdwwx", "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4", "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR", "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29", "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd", "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29", "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4", "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334" ], [ "m/0'/1/2'/2/1000000000", "d69aa102255fed74378278c7812701ea641fdf32", "d69aa102", "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam", "1HXJog342VFdc68AB9Cb6LwVmCjvcLMiwm", "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8", "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs", "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011", "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e", "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011", "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8", "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76" ] ] m2 :: TestKey m2 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" vector2 :: [TestVector] vector2 = [ [ "m", "bd16bee53961a47d6ad888e29545434a89bdfe95", "bd16bee5", "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg", "148CGtv7bwcC933EHtcDfzDQVneur1R8Y1", "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e", "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy", "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689", "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7", "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e", "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U" ], [ "m/0", "5a61ff8eb7aaca3010db97ebda76121610b78096", "5a61ff8e", "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ", "1KVyTSpsBGYs7NdyZmArEpVTfWJQSgiDCx", "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e", "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj", "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea", "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c", "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea", "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e", "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt" ], [ "m/0/2147483647'", "d8ab493736da02f11ed682f88339e720fb0379d1", "d8ab4937", "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk", "14MFLsfx1nc4RKiaH9khqDTNL9CRz3q347", "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93", "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr", "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b", "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9", "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b", "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93", "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9" ], [ "m/0/2147483647'/1", "78412e3a2296a40de124307b6485bd19833e2e34", "78412e3a", "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW", "19ou31MGyGW9VFx7woKBqwLe5JHhQBYaDD", "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7", "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2", "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9", "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb", "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9", "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7", "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef" ], [ "m/0/2147483647'/1/2147483646'", "31a507b815593dfc51ffc7245ae7e5aee304246e", "31a507b8", "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R", "18GYmRm4nyjk8ydvoVXFxMWQvxhksEFDZR", "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d", "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF", "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0", "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29", "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0", "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d", "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc" ], [ "m/0/2147483647'/1/2147483646'/2", "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220", "26132fdb", "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt", "1758mgwNZhyzpRLe4u7FjqpJtqKpaGhXh7", "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK", "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271", "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c", "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23", "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j" ] ] m3 :: TestKey m3 = "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be" vector3 :: [TestVector] vector3 = [ [ "m", "41d63b50d8dd5e730cdf4c79a56fc929a757c548", "41d63b50", "1717ZYpXhZW5CqAbWSjDJbCey3FyKUmCSf", "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F", "00ddb80b067e0d4993197fe10f2657a844a384589847602d56f0c629c81aae32", "KwFPqAq9SKx1sPg15Qk56mqkHwrfGPuywtLUxoWPkiTSBoxCs8am", "03683af1ba5743bdfc798cf814efeeab2735ec52d95eced528e692b8e34c4e5669", "01d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f", "0488b21e00000000000000000001d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f03683af1ba5743bdfc798cf814efeeab2735ec52d95eced528e692b8e34c4e5669", "0488ade400000000000000000001d28a3e53cffa419ec122c968b3259e16b65076495494d97cae10bbfec3c36f0000ddb80b067e0d4993197fe10f2657a844a384589847602d56f0c629c81aae32", "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6" ], [ "m/0'", "c61368bb50e066acd95bd04a0b23d3837fb75698", "c61368bb", "1K4L3YxEwg8HkSEapM4iSiGuR6HeQ53KPX", "13QeQVJNNakdUU55P1fc8xPMkkeYVzn4o6", "491f7a2eebc7b57028e0d3faa0acda02e75c33b03c48fb288c41e2ea44e1daef", "KyfrPaeirL5yYAoZvfzyoKXSdszeLqg5vb6dNy9ymvjzZrMZY8GW", "026557fdda1d5d43d79611f784780471f086d58e8126b8c40acb82272a7712e7f2", "e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd", "0488b21e0141d63b5080000000e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd026557fdda1d5d43d79611f784780471f086d58e8126b8c40acb82272a7712e7f2", "0488ade40141d63b5080000000e5fea12a97b927fc9dc3d2cb0d1ea1cf50aa5a1fdc1f933e8906bb38df3377bd00491f7a2eebc7b57028e0d3faa0acda02e75c33b03c48fb288c41e2ea44e1daef", "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L" ] ]