packages feed

haskoin-core 0.13.1 → 0.13.2

raw patch · 115 files changed

+15126/−15153 lines, 115 files

Files

CHANGELOG.md view
@@ -4,6 +4,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 0.13.2+### Changed+- Move all packages from Network.Haskoin namespace to Haskoin namespace.+- Expose all top-level modules directly.+ ## 0.13.1 ### Changed - Faster JSON serialization.
haskoin-core.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: ad4c6376deb0bf12b7271c03f9d6602888452d75a32a16f711ef2f018b500a10+-- hash: bd9822c23e8579ff68357d7e634d820b25e23e1d6ce81b019ebb8e96bc872370  name:           haskoin-core-version:        0.13.1+version:        0.13.2 synopsis:       Bitcoin & Bitcoin Cash library for Haskell description:    Haskoin Core is a complete Bitcoin and Bitcoin Cash library of functions and data types for Haskell developers. category:       Bitcoin, Finance, Network@@ -37,38 +37,38 @@ library   exposed-modules:       Haskoin+      Haskoin.Address+      Haskoin.Block+      Haskoin.Constants+      Haskoin.Crypto+      Haskoin.Keys+      Haskoin.Network+      Haskoin.Script+      Haskoin.Transaction+      Haskoin.Util   other-modules:-      Network.Haskoin.Address-      Network.Haskoin.Address.Base58-      Network.Haskoin.Address.Bech32-      Network.Haskoin.Address.CashAddr-      Network.Haskoin.Block-      Network.Haskoin.Block.Common-      Network.Haskoin.Block.Headers-      Network.Haskoin.Block.Merkle-      Network.Haskoin.Constants-      Network.Haskoin.Crypto-      Network.Haskoin.Crypto.Hash-      Network.Haskoin.Crypto.Signature-      Network.Haskoin.Keys-      Network.Haskoin.Keys.Common-      Network.Haskoin.Keys.Extended-      Network.Haskoin.Keys.Mnemonic-      Network.Haskoin.Network-      Network.Haskoin.Network.Bloom-      Network.Haskoin.Network.Common-      Network.Haskoin.Network.Message-      Network.Haskoin.Script-      Network.Haskoin.Script.Common-      Network.Haskoin.Script.SigHash-      Network.Haskoin.Script.Standard-      Network.Haskoin.Transaction-      Network.Haskoin.Transaction.Builder-      Network.Haskoin.Transaction.Builder.Sign-      Network.Haskoin.Transaction.Common-      Network.Haskoin.Transaction.Partial-      Network.Haskoin.Transaction.Segwit-      Network.Haskoin.Util+      Haskoin.Address.Base58+      Haskoin.Address.Bech32+      Haskoin.Address.CashAddr+      Haskoin.Block.Common+      Haskoin.Block.Headers+      Haskoin.Block.Merkle+      Haskoin.Crypto.Hash+      Haskoin.Crypto.Signature+      Haskoin.Keys.Common+      Haskoin.Keys.Extended+      Haskoin.Keys.Mnemonic+      Haskoin.Network.Bloom+      Haskoin.Network.Common+      Haskoin.Network.Message+      Haskoin.Script.Common+      Haskoin.Script.SigHash+      Haskoin.Script.Standard+      Haskoin.Transaction.Builder+      Haskoin.Transaction.Builder.Sign+      Haskoin.Transaction.Common+      Haskoin.Transaction.Partial+      Haskoin.Transaction.Segwit       Paths_haskoin_core   hs-source-dirs:       src@@ -105,63 +105,63 @@   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      Network.Haskoin.Address.Bech32Spec-      Network.Haskoin.Address.CashAddrSpec-      Network.Haskoin.AddressSpec-      Network.Haskoin.BlockSpec-      Network.Haskoin.Crypto.HashSpec-      Network.Haskoin.Crypto.SignatureSpec-      Network.Haskoin.CryptoSpec-      Network.Haskoin.Keys.ExtendedSpec-      Network.Haskoin.Keys.MnemonicSpec-      Network.Haskoin.KeysSpec-      Network.Haskoin.NetworkSpec-      Network.Haskoin.ScriptSpec-      Network.Haskoin.Test-      Network.Haskoin.Test.Address-      Network.Haskoin.Test.Block-      Network.Haskoin.Test.Crypto-      Network.Haskoin.Test.Keys-      Network.Haskoin.Test.Message-      Network.Haskoin.Test.Network-      Network.Haskoin.Test.Script-      Network.Haskoin.Test.Transaction-      Network.Haskoin.Test.Util-      Network.Haskoin.Transaction.PartialSpec-      Network.Haskoin.TransactionSpec-      Network.Haskoin.UtilSpec+      Haskoin.Address.Bech32Spec+      Haskoin.Address.CashAddrSpec+      Haskoin.AddressSpec+      Haskoin.BlockSpec+      Haskoin.Crypto.HashSpec+      Haskoin.Crypto.SignatureSpec+      Haskoin.CryptoSpec+      Haskoin.Keys.ExtendedSpec+      Haskoin.Keys.MnemonicSpec+      Haskoin.KeysSpec+      Haskoin.NetworkSpec+      Haskoin.ScriptSpec+      Haskoin.Test+      Haskoin.Test.Address+      Haskoin.Test.Block+      Haskoin.Test.Crypto+      Haskoin.Test.Keys+      Haskoin.Test.Message+      Haskoin.Test.Network+      Haskoin.Test.Script+      Haskoin.Test.Transaction+      Haskoin.Test.Util+      Haskoin.Transaction.PartialSpec+      Haskoin.TransactionSpec+      Haskoin.UtilSpec       Haskoin-      Network.Haskoin.Address-      Network.Haskoin.Address.Base58-      Network.Haskoin.Address.Bech32-      Network.Haskoin.Address.CashAddr-      Network.Haskoin.Block-      Network.Haskoin.Block.Common-      Network.Haskoin.Block.Headers-      Network.Haskoin.Block.Merkle-      Network.Haskoin.Constants-      Network.Haskoin.Crypto-      Network.Haskoin.Crypto.Hash-      Network.Haskoin.Crypto.Signature-      Network.Haskoin.Keys-      Network.Haskoin.Keys.Common-      Network.Haskoin.Keys.Extended-      Network.Haskoin.Keys.Mnemonic-      Network.Haskoin.Network-      Network.Haskoin.Network.Bloom-      Network.Haskoin.Network.Common-      Network.Haskoin.Network.Message-      Network.Haskoin.Script-      Network.Haskoin.Script.Common-      Network.Haskoin.Script.SigHash-      Network.Haskoin.Script.Standard-      Network.Haskoin.Transaction-      Network.Haskoin.Transaction.Builder-      Network.Haskoin.Transaction.Builder.Sign-      Network.Haskoin.Transaction.Common-      Network.Haskoin.Transaction.Partial-      Network.Haskoin.Transaction.Segwit-      Network.Haskoin.Util+      Haskoin.Address+      Haskoin.Address.Base58+      Haskoin.Address.Bech32+      Haskoin.Address.CashAddr+      Haskoin.Block+      Haskoin.Block.Common+      Haskoin.Block.Headers+      Haskoin.Block.Merkle+      Haskoin.Constants+      Haskoin.Crypto+      Haskoin.Crypto.Hash+      Haskoin.Crypto.Signature+      Haskoin.Keys+      Haskoin.Keys.Common+      Haskoin.Keys.Extended+      Haskoin.Keys.Mnemonic+      Haskoin.Network+      Haskoin.Network.Bloom+      Haskoin.Network.Common+      Haskoin.Network.Message+      Haskoin.Script+      Haskoin.Script.Common+      Haskoin.Script.SigHash+      Haskoin.Script.Standard+      Haskoin.Transaction+      Haskoin.Transaction.Builder+      Haskoin.Transaction.Builder.Sign+      Haskoin.Transaction.Common+      Haskoin.Transaction.Partial+      Haskoin.Transaction.Segwit+      Haskoin.Util       Paths_haskoin_core   hs-source-dirs:       test
src/Haskoin.hs view
@@ -33,13 +33,13 @@     , module Util     ) where -import           Network.Haskoin.Address             as Address-import           Network.Haskoin.Block               as Block-import           Network.Haskoin.Constants           as Constants-import           Network.Haskoin.Crypto              as Crypto-import           Network.Haskoin.Keys                as Keys-import           Network.Haskoin.Network             as Network-import           Network.Haskoin.Script              as Script-import           Network.Haskoin.Transaction         as Transaction-import           Network.Haskoin.Transaction.Partial as Partial-import           Network.Haskoin.Util                as Util+import           Haskoin.Address             as Address+import           Haskoin.Block               as Block+import           Haskoin.Constants           as Constants+import           Haskoin.Crypto              as Crypto+import           Haskoin.Keys                as Keys+import           Haskoin.Network             as Network+import           Haskoin.Script              as Script+import           Haskoin.Transaction         as Transaction+import           Haskoin.Transaction.Partial as Partial+import           Haskoin.Util                as Util
+ src/Haskoin/Address.hs view
@@ -0,0 +1,314 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Address+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Base58, CashAddr, Bech32 address and WIF private key serialization support.+-}+module Haskoin.Address+    ( Address(..)+    , isPubKeyAddress+    , isScriptAddress+    , isWitnessPubKeyAddress+    , isWitnessScriptAddress+    , addrToString+    , stringToAddr+    , addrToJSON+    , addrToEncoding+    , addrFromJSON+    , pubKeyAddr+    , pubKeyWitnessAddr+    , pubKeyCompatWitnessAddr+    , p2pkhAddr+    , p2wpkhAddr+    , p2shAddr+    , p2wshAddr+    , inputAddress+    , outputAddress+    , addressToScript+    , addressToScriptBS+    , addressToOutput+    , payToScriptAddress+    , payToWitnessScriptAddress+    , payToNestedScriptAddress+    , scriptToAddress+    , scriptToAddressBS+      -- * Private Key Wallet Import Format (WIF)+    , fromWif+    , toWif+      -- * Base58+    , module Haskoin.Address.Base58+      -- * Bech32+    , module Haskoin.Address.Bech32+      -- * CashAddr+    , module Haskoin.Address.CashAddr+    ) where++import           Control.Applicative+import           Control.DeepSeq+import           Control.Monad+import           Data.Aeson               as A+import           Data.Aeson.Encoding      as A+import           Data.Aeson.Types+import           Data.ByteString          (ByteString)+import qualified Data.ByteString          as B+import           Data.Hashable+import           Data.Maybe+import           Data.Serialize           as S+import           Data.Text                (Text)+import           GHC.Generics             (Generic)+import           Haskoin.Address.Base58+import           Haskoin.Address.Bech32+import           Haskoin.Address.CashAddr+import           Haskoin.Constants+import           Haskoin.Crypto+import           Haskoin.Keys.Common+import           Haskoin.Script+import           Haskoin.Util++-- | Address format for Bitcoin and Bitcoin Cash.+data Address+    -- | pay to public key hash (regular)+    = PubKeyAddress { getAddrHash160 :: !Hash160+                      -- ^ RIPEMD160 hash of public key's SHA256 hash+                     }+    -- | pay to script hash+    | ScriptAddress { getAddrHash160 :: !Hash160+                      -- ^ RIPEMD160 hash of script's SHA256 hash+                     }+    -- | pay to witness public key hash+    | WitnessPubKeyAddress { getAddrHash160 :: !Hash160+                             -- ^ RIPEMD160 hash of public key's SHA256 hash+                            }+    -- | pay to witness script hash+    | WitnessScriptAddress { getAddrHash256 :: !Hash256+                             -- ^ HASH256 hash of script+                            }+    deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)++-- | 'Address' pays to a public key hash.+isPubKeyAddress :: Address -> Bool+isPubKeyAddress PubKeyAddress {} = True+isPubKeyAddress _                = False++-- | 'Address' pays to a script hash.+isScriptAddress :: Address -> Bool+isScriptAddress ScriptAddress {} = True+isScriptAddress _                = False++-- | 'Address' pays to a witness public key hash. Only valid for SegWit+-- networks.+isWitnessPubKeyAddress :: Address -> Bool+isWitnessPubKeyAddress WitnessPubKeyAddress {} = True+isWitnessPubKeyAddress _                       = False++-- | 'Address' pays to a witness script hash. Only valid for SegWit networks.+isWitnessScriptAddress :: Address -> Bool+isWitnessScriptAddress WitnessScriptAddress {} = True+isWitnessScriptAddress _                       = False++-- | Deserializer for binary 'Base58' addresses.+base58get :: Network -> Get Address+base58get net = do+    pfx <- getWord8+    addr <- S.get+    f pfx addr+  where+    f x a+        | x == getAddrPrefix net = return $ PubKeyAddress a+        | x == getScriptPrefix net = return $ ScriptAddress a+        | otherwise = fail "Does not recognize address prefix"++-- | Binary serializer for 'Base58' addresses.+base58put :: Network -> Putter Address+base58put net (PubKeyAddress h) = do+        putWord8 (getAddrPrefix net)+        put h+base58put net (ScriptAddress h) = do+        putWord8 (getScriptPrefix net)+        put h+base58put _ _ = error "Cannot serialize this address as Base58"++addrToJSON :: Network -> Address -> Value+addrToJSON net a = toJSON (addrToString net a)++addrToEncoding :: Network -> Address -> Encoding+addrToEncoding net a =+    case addrToString net a of+        Nothing  -> null_+        Just txt -> text txt++-- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and+-- 'Bech32'.+addrFromJSON :: Network -> Value -> Parser Address+addrFromJSON net =+    withText "address" $ \t ->+        case stringToAddr net t of+            Nothing -> fail "could not decode address"+            Just x  -> return x++-- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or+-- 'CashAddr' depending on network.+addrToString :: Network -> Address -> Maybe Text+addrToString net a@PubKeyAddress {getAddrHash160 = h}+    | isNothing (getCashAddrPrefix net) =+        Just . encodeBase58Check . runPut $ base58put net a+    | otherwise =+        cashAddrEncode net 0 (S.encode h)+addrToString net a@ScriptAddress {getAddrHash160 = h}+    | isNothing (getCashAddrPrefix net) =+        Just . encodeBase58Check . runPut $ base58put net a+    | otherwise =+        cashAddrEncode net 1 (S.encode h)+addrToString net WitnessPubKeyAddress {getAddrHash160 = h} = do+    hrp <- getBech32Prefix net+    segwitEncode hrp 0 (B.unpack (S.encode h))+addrToString net WitnessScriptAddress {getAddrHash256 = h} = do+    hrp <- getBech32Prefix net+    segwitEncode hrp 0 (B.unpack (S.encode h))++-- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.+stringToAddr :: Network -> Text -> Maybe Address+stringToAddr net bs = cash <|> segwit <|> b58+  where+    b58 = eitherToMaybe . runGet (base58get net) =<< decodeBase58Check bs+    cash = cashAddrDecode net bs >>= \(ver, bs') -> case ver of+        0 -> do+            h <- eitherToMaybe (S.decode bs')+            return $ PubKeyAddress h+        1 -> do+            h <- eitherToMaybe (S.decode bs')+            return $ ScriptAddress h+        _ -> Nothing+    segwit = do+        hrp <- getBech32Prefix net+        (ver, bs') <- segwitDecode hrp bs+        guard (ver == 0)+        let bs'' = B.pack bs'+        case B.length bs'' of+            20 -> do+                h <- eitherToMaybe (S.decode bs'')+                return $ WitnessPubKeyAddress h+            32 -> do+                h <- eitherToMaybe (S.decode bs'')+                return $ WitnessScriptAddress h+            _ -> Nothing++-- | Obtain a standard pay-to-public-key-hash address from a public key.+pubKeyAddr :: PubKeyI -> Address+pubKeyAddr = PubKeyAddress . addressHash . S.encode++-- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.+p2pkhAddr :: Hash160 -> Address+p2pkhAddr = PubKeyAddress++-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a+-- public key.+pubKeyWitnessAddr :: PubKeyI -> Address+pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . S.encode++-- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.+pubKeyCompatWitnessAddr :: PubKeyI -> Address+pubKeyCompatWitnessAddr =+    p2shAddr .+    addressHash . encodeOutputBS . PayWitnessPKHash . addressHash . S.encode++-- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a+-- 'Hash160'.+p2wpkhAddr :: Hash160 -> Address+p2wpkhAddr = WitnessPubKeyAddress++-- | Obtain a standard pay-to-script-hash (P2SH) address from a 'Hash160'.+p2shAddr :: Hash160 -> Address+p2shAddr = ScriptAddress++-- | Obtain a SegWit pay-to-witness-script-hash (P2WSH) address from a 'Hash256'+p2wshAddr :: Hash256 -> Address+p2wshAddr = WitnessScriptAddress++-- | Compute a standard pay-to-script-hash (P2SH) address for an output script.+payToScriptAddress :: ScriptOutput -> Address+payToScriptAddress = p2shAddr . addressHash . encodeOutputBS++-- | Compute a SegWit pay-to-witness-script-hash (P2WSH) address for an output+-- script.+payToWitnessScriptAddress :: ScriptOutput -> Address+payToWitnessScriptAddress = p2wshAddr . sha256 . encodeOutputBS++-- | Compute a backwards-compatible SegWit P2SH-P2WSH address.+payToNestedScriptAddress :: ScriptOutput -> Address+payToNestedScriptAddress =+    p2shAddr . addressHash . encodeOutputBS . toP2WSH . encodeOutput++-- | Encode an output script from an address. Will fail if using a+-- pay-to-witness address on a non-SegWit network.+addressToOutput :: Address -> ScriptOutput+addressToOutput (PubKeyAddress h)        = PayPKHash h+addressToOutput (ScriptAddress h)        = PayScriptHash h+addressToOutput (WitnessPubKeyAddress h) = PayWitnessPKHash h+addressToOutput (WitnessScriptAddress h) = PayWitnessScriptHash h++-- | Get output script AST for an 'Address'.+addressToScript :: Address -> Script+addressToScript = encodeOutput . addressToOutput++-- | Encode address as output script in 'ByteString' form.+addressToScriptBS :: Address -> ByteString+addressToScriptBS = S.encode . addressToScript++-- | Decode an output script into an 'Address' if it has such representation.+scriptToAddress :: Script -> Either String Address+scriptToAddress =+    maybeToEither "Could not decode address" . outputAddress <=< decodeOutput++-- | Decode a serialized script into an 'Address'.+scriptToAddressBS :: ByteString -> Either String Address+scriptToAddressBS =+    maybeToEither "Could not decode address" . outputAddress <=< decodeOutputBS++-- | Get the 'Address' of a 'ScriptOutput'.+outputAddress :: ScriptOutput -> Maybe Address+outputAddress (PayPKHash h)            = Just $ PubKeyAddress h+outputAddress (PayScriptHash h)        = Just $ ScriptAddress h+outputAddress (PayPK k)                = Just $ pubKeyAddr k+outputAddress (PayWitnessPKHash h)     = Just $ WitnessPubKeyAddress h+outputAddress (PayWitnessScriptHash h) = Just $ WitnessScriptAddress h+outputAddress _                        = Nothing++-- | Infer the 'Address' of a 'ScriptInput'.+inputAddress :: ScriptInput -> Maybe Address+inputAddress (RegularInput (SpendPKHash _ key)) = Just $ pubKeyAddr key+inputAddress (ScriptHashInput _ rdm) = Just $ payToScriptAddress rdm+inputAddress _ = Nothing++-- | Decode private key from WIF (wallet import format) string.+fromWif :: Network -> Base58 -> Maybe SecKeyI+fromWif net wif = do+    bs <- decodeBase58Check wif+    -- Check that this is a private key+    guard (B.head bs == getSecretPrefix net)+    case B.length bs of+        -- Uncompressed format+        33 -> wrapSecKey False <$> secKey (B.tail bs)+        -- Compressed format+        34 -> do+            guard $ B.last bs == 0x01+            wrapSecKey True <$> secKey (B.tail $ B.init bs)+        -- Bad length+        _  -> Nothing++-- | Encode private key into a WIF string.+toWif :: Network -> SecKeyI -> Base58+toWif net (SecKeyI k c) =+    encodeBase58Check . B.cons (getSecretPrefix net) $+    if c+        then getSecKey k `B.snoc` 0x01+        else getSecKey k
+ src/Haskoin/Address/Base58.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Address.Base58+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Support for legacy 'Base58' addresses. Superseded by Bech32 for Bitcoin SegWit+(BTC) and CashAddr for Bitcoin Cash (BCH).+-}+module Haskoin.Address.Base58+    ( Base58+    , encodeBase58+    , decodeBase58+    , encodeBase58Check+    , decodeBase58Check+    ) where++import           Control.Monad+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import qualified Data.ByteString.Char8   as C+import           Data.Maybe              (fromMaybe, isJust, listToMaybe)+import           Data.Serialize          as S+import           Data.String.Conversions (cs)+import           Data.Text               (Text)+import qualified Data.Text               as T+import           Haskoin.Crypto.Hash+import           Haskoin.Util+import           Numeric                 (readInt, showIntAtBase)++-- | 'Base58' classic Bitcoin address format.+type Base58 = Text++-- | Symbols for Base58 encoding.+b58Data :: ByteString+b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"++-- | Convert a number less than or equal to provided integer into a 'Base58'+-- character.+b58 :: Int -> Char+b58 = C.index b58Data++-- | Convert a 'Base58' character into the number it represents.+b58' :: Char -> Maybe Int+b58' = flip C.elemIndex b58Data++-- | Encode an arbitrary-length 'Integer' into a 'Base58' string. Leading zeroes+-- will not be part of the resulting string.+encodeBase58I :: Integer -> Base58+encodeBase58I i = cs $ showIntAtBase 58 b58 i ""++-- | Decode a 'Base58' string into an arbitrary-length 'Integer'.+decodeBase58I :: Base58 -> Maybe Integer+decodeBase58I s =+    case go of+        Just (r,[]) -> Just r+        _           -> Nothing+  where+    p  = isJust . b58'+    f  = fromMaybe e . b58'+    go = listToMaybe $ readInt 58 p f (cs s)+    e  = error "Could not decode base58"++-- | Encode an arbitrary 'ByteString' into a its 'Base58' representation,+-- preserving leading zeroes.+encodeBase58 :: ByteString -> Base58+encodeBase58 bs =+    l `mappend` r+  where+    (z, b) = BS.span (== 0) bs+    l = cs $ BS.replicate (BS.length z) (BS.index b58Data 0) -- preserve leading 0's+    r | BS.null b = T.empty+      | otherwise = encodeBase58I $ bsToInteger b++-- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'.+decodeBase58 :: Base58 -> Maybe ByteString+decodeBase58 t =+    BS.append prefix <$> r+  where+    (z, b) = BS.span (== BS.index b58Data 0) (cs t)+    prefix = BS.replicate (BS.length z) 0 -- preserve leading 1's+    r | BS.null b = Just BS.empty+      | otherwise = integerToBS <$> decodeBase58I (cs b)++-- | Computes a checksum for the input 'ByteString' and encodes the input and+-- the checksum as 'Base58'.+encodeBase58Check :: ByteString -> Base58+encodeBase58Check bs =+    encodeBase58 $ BS.append bs $ encode $ checkSum32 bs++-- | Decode a 'Base58'-encoded string that contains a checksum. This function+-- returns 'Nothing' if the input string contains invalid 'Base58' characters or+-- if the checksum fails.+decodeBase58Check :: Base58 -> Maybe ByteString+decodeBase58Check bs = do+    rs <- decodeBase58 bs+    let (res, chk) = BS.splitAt (BS.length rs - 4) rs+    guard $ chk == encode (checkSum32 res)+    return res
+ src/Haskoin/Address/Bech32.hs view
@@ -0,0 +1,228 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Address.Base58+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Support for Bitcoin SegWit (BTC) Bech32 addresses. This module is a modified+version of Marko Bencun's reference implementation.+-}+module Haskoin.Address.Bech32+    ( HRP+    , Bech32+    , Data+    , bech32Encode+    , bech32Decode+    , toBase32+    , toBase256+    , segwitEncode+    , segwitDecode+    , Word5(..)+    , word5+    , fromWord5+    ) where++import           Control.Monad         (guard)+import           Data.Array            (Array, assocs, bounds, listArray, (!),+                                        (//))+import           Data.Bits             (Bits, testBit, unsafeShiftL,+                                        unsafeShiftR, xor, (.&.), (.|.))+import qualified Data.ByteString       as B+import           Data.Char             (toUpper)+import           Data.Foldable         (foldl')+import           Data.Functor.Identity (Identity, runIdentity)+import           Data.Ix               (Ix (..))+import           Data.Text             (Text)+import qualified Data.Text             as T+import qualified Data.Text.Encoding    as E+import           Data.Word             (Word8)++-- | Bech32 human-readable string.+type Bech32 = Text++-- | Human-readable part of 'Bech32' address.+type HRP = Text++-- | Data part of 'Bech32' address.+type Data = [Word8]++(.>>.), (.<<.) :: Bits a => a -> Int -> a+(.>>.) = unsafeShiftR+(.<<.) = unsafeShiftL++-- | Five-bit word for Bech32.+newtype Word5 =+    UnsafeWord5 Word8+    deriving (Eq, Ord)++instance Ix Word5 where+    range (UnsafeWord5 m, UnsafeWord5 n) = map UnsafeWord5 $ range (m, n)+    index (UnsafeWord5 m, UnsafeWord5 n) (UnsafeWord5 i) = index (m, n) i+    inRange (m, n) i = m <= i && i <= n++-- | Convert an integer number into a five-bit word.+word5 :: Integral a => a -> Word5+word5 x = UnsafeWord5 (fromIntegral x .&. 31)+{-# INLINE word5 #-}+{-# SPECIALIZE INLINE word5 :: Word8 -> Word5 #-}++-- | Convert a five-bit word into a number.+fromWord5 :: Num a => Word5 -> a+fromWord5 (UnsafeWord5 x) = fromIntegral x+{-# INLINE fromWord5 #-}+{-# SPECIALIZE INLINE fromWord5 :: Word5 -> Word8 #-}++-- | 'Bech32' character map as array of five-bit integers to character.+charset :: Array Word5 Char+charset =+    listArray (UnsafeWord5 0, UnsafeWord5 31) "qpzry9x8gf2tvdw0s3jn54khce6mua7l"++-- | Convert a character to its five-bit value from 'Bech32' 'charset'.+charsetMap :: Char -> Maybe Word5+charsetMap c+    | inRange (bounds inv) upperC = inv ! upperC+    | otherwise = Nothing+  where+    upperC = toUpper c+    inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)+    swap (a, b) = (toUpper b, Just a)++-- | Calculate or validate 'Bech32' checksum.+bech32Polymod :: [Word5] -> Word+bech32Polymod values = foldl' go 1 values .&. 0x3fffffff+  where+    go chk value =+        foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]+      where+        generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]+        chk' = chk .<<. 5 `xor` fromWord5 value++-- | Convert human-readable part of 'Bech32' string into a list of five-bit+-- words.+bech32HRPExpand :: HRP -> [Word5]+bech32HRPExpand hrp =+    map (UnsafeWord5 . (.>>. 5)) hrpBytes +++    [UnsafeWord5 0] ++ map word5 hrpBytes+  where+    hrpBytes = B.unpack $ E.encodeUtf8 hrp++-- | Calculate checksum for a string of five-bit words.+bech32CreateChecksum :: HRP -> [Word5] -> [Word5]+bech32CreateChecksum hrp dat = [word5 (polymod .>>. i) | i <- [25,20 .. 0]]+  where+    values = bech32HRPExpand hrp ++ dat+    polymod =+        bech32Polymod (values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]) `xor` 1++-- | Verify checksum for a human-readable part and string of five-bit words.+bech32VerifyChecksum :: HRP -> [Word5] -> Bool+bech32VerifyChecksum hrp dat = bech32Polymod (bech32HRPExpand hrp ++ dat) == 1++-- | Maximum length of a Bech32 result.+maxBech32Length :: Int+maxBech32Length = 90++-- | Encode string of five-bit words into 'Bech32' using a provided+-- human-readable part. Can fail if 'HRP' is invalid or result would be longer+-- than 90 characters.+bech32Encode :: HRP -> [Word5] -> Maybe Bech32+bech32Encode hrp dat = do+    guard $ checkHRP hrp+    let dat' = dat ++ bech32CreateChecksum (T.toLower hrp) dat+        rest = map (charset !) dat'+        result = T.concat [T.toLower hrp, T.pack "1", T.pack rest]+    guard $ T.length result <= maxBech32Length+    return result++-- | Check that human-readable part is valid for a 'Bech32' string.+checkHRP :: HRP -> Bool+checkHRP hrp = not (T.null hrp) && T.all (\char -> char >= '\x21' && char <= '\x7e') hrp++-- | Decode human-readable 'Bech32' string into a human-readable part and a+-- string of five-bit words.+bech32Decode :: Bech32 -> Maybe (HRP, [Word5])+bech32Decode bech32 = do+    guard $ T.length bech32 <= maxBech32Length+    guard $ T.toUpper bech32 == bech32 || lowerBech32 == bech32+    let (hrp, dat) = T.breakOnEnd "1" lowerBech32+    guard $ T.length dat >= 6+    hrp' <- T.stripSuffix "1" hrp+    guard $ checkHRP hrp'+    dat' <- mapM charsetMap $ T.unpack dat+    guard $ bech32VerifyChecksum hrp' dat'+    return (hrp', take (T.length dat - 6) dat')+  where+    lowerBech32 = T.toLower bech32++type Pad f = Int -> Int -> Word -> [[Word]] -> f [[Word]]++yesPadding :: Pad Identity+yesPadding _ 0 _ result        = return result+yesPadding _ _ padValue result = return $ [padValue] : result+{-# INLINE yesPadding #-}++noPadding :: Pad Maybe+noPadding frombits bits padValue result = do+    guard $ bits < frombits && padValue == 0+    return result+{-# INLINE noPadding #-}++-- | Big endian conversion of a bytestring from base \(2^{frombits}\) to base+-- \(2^{tobits}\). {frombits} and {twobits} must be positive and+-- \(2^{frombits}\) and \(2^{tobits}\) must be smaller than the size of Word.+-- Every value in 'dat' must be strictly smaller than \(2^{frombits}\).+convertBits :: Functor f => [Word] -> Int -> Int -> Pad f -> f [Word]+convertBits dat frombits tobits pad = concat . reverse <$> go dat 0 0 []+  where+    go [] acc bits result =+        let padValue = (acc .<<. (tobits - bits)) .&. maxv+        in pad frombits bits padValue result+    go (value:dat') acc bits result =+        go dat' acc' (bits' `rem` tobits) (result' : result)+      where+        acc' = (acc .<<. frombits) .|. fromIntegral value+        bits' = bits + frombits+        result' =+            [ (acc' .>>. b) .&. maxv+            | b <- [bits' - tobits,bits' - 2 * tobits .. 0]+            ]+    maxv = (1 .<<. tobits) - 1+{-# INLINE convertBits #-}++-- | Convert from eight-bit to five-bit word string, adding padding as required.+toBase32 :: [Word8] -> [Word5]+toBase32 dat =+    map word5 $ runIdentity $ convertBits (map fromIntegral dat) 8 5 yesPadding++-- | Convert from five-bit word string to eight-bit word string, ignoring padding.+toBase256 :: [Word5] -> Maybe [Word8]+toBase256 dat =+    map fromIntegral <$> convertBits (map fromWord5 dat) 5 8 noPadding++-- | Check if witness version and program are valid.+segwitCheck :: Word8 -> Data -> Bool+segwitCheck witver witprog =+    witver <= 16 &&+    if witver == 0+        then length witprog == 20 || length witprog == 32+        else length witprog >= 2 && length witprog <= 40++-- | Decode SegWit 'Bech32' address from a string and expected human-readable part.+segwitDecode :: HRP -> Bech32 -> Maybe (Word8, Data)+segwitDecode hrp addr = do+    (hrp', dat) <- bech32Decode addr+    guard $ (hrp == hrp') && not (null dat)+    let (UnsafeWord5 witver:datBase32) = dat+    decoded <- toBase256 datBase32+    guard $ segwitCheck witver decoded+    return (witver, decoded)++-- | Encode 'Data' as a SegWit 'Bech32' address. Needs human-readable part and+-- witness program version.+segwitEncode :: HRP -> Word8 -> Data -> Maybe Text+segwitEncode hrp witver witprog = do+    guard $ segwitCheck witver witprog+    bech32Encode hrp $ UnsafeWord5 witver : toBase32 witprog
+ src/Haskoin/Address/CashAddr.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Address.CashAddr+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Support for Bitcoin Cash (BCH) CashAddr format.+-}+module Haskoin.Address.CashAddr+    ( CashPrefix+    , CashVersion+    , CashAddr+    , Cash32+    , cashAddrDecode+    , cashAddrEncode+    , cash32decodeType+    , cash32encodeType+    , cash32decode+    , cash32encode+    ) where++import           Control.Monad+import           Data.Bits+import           Data.ByteString       (ByteString)+import qualified Data.ByteString       as B+import qualified Data.ByteString.Char8 as C+import           Data.Char+import           Data.List+import           Data.Text             (Text)+import qualified Data.Text             as T+import qualified Data.Text.Encoding    as E+import           Data.Word+import           Haskoin.Constants+import           Haskoin.Util++-- | 'CashAddr' prefix, usually shown before the colon in addresses, but sometimes+-- omitted. It is used in the checksum calculation to avoid parsing an address+-- from the wrong network.+type CashPrefix = Text++-- | 'CashAddr' version, until new address schemes appear it will be zero.+type CashVersion = Word8++-- | High level 'CashAddr' human-reabale string, with explicit or implicit prefix.+type CashAddr = Text++-- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'. It+-- need not encode a valid address but any binary data.+type Cash32 = Text++-- | Symbols for encoding 'Cash32' data in human-readable strings.+charset :: String+charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"++-- | Get the 32-bit number associated with this 'Cash32' character.+base32char :: Char -> Maybe Word8+base32char = fmap fromIntegral . (`elemIndex` charset)++-- | High-Level: decode 'CashAddr' string if it is valid for the+-- provided 'Network'. Prefix may be omitted from the string.+cashAddrDecode :: Network -> CashAddr -> Maybe (CashVersion, ByteString)+cashAddrDecode net ca = do+    epfx <- getCashAddrPrefix net+    let (cpfx, cdat) = T.breakOnEnd ":" (T.toLower ca)+    guard (T.null cpfx || T.init cpfx == epfx)+    (dpfx, ver, bs) <- cash32decodeType (epfx <> ":" <> cdat)+    guard (dpfx == epfx)+    return (ver, bs)++-- | High-Level: encode 'CashAddr' string for the provided network and hash.+-- Fails if the 'CashVersion' or length of hash 'ByteString' is invalid.+cashAddrEncode :: Network -> CashVersion -> ByteString -> Maybe CashAddr+cashAddrEncode net cv bs = do+    pfx <- getCashAddrPrefix net+    cash32encodeType pfx cv bs++-- | Mid-Level: decode 'CashAddr' string containing arbitrary prefix, plus a+-- version byte before the 'ByteString' that encodes type and length.+cash32decodeType :: Cash32 -> Maybe (CashPrefix, CashVersion, ByteString)+cash32decodeType ca' = do+    guard (T.toUpper ca' == ca' || ca == ca')+    (dpfx, bs) <- cash32decode ca+    guard (not (B.null bs))+    let vb = B.head bs+        pay = B.tail bs+    (ver, len) <- decodeVersionByte vb+    guard (B.length pay == len)+    return (dpfx, ver, pay)+  where+    ca = T.toLower ca'++-- | Mid-Level: encode 'CashAddr' string containing arbitrary prefix and+-- 'CashVersion'. Length must be among those allowed by the standard.+cash32encodeType :: CashPrefix -> CashVersion -> ByteString -> Maybe Cash32+cash32encodeType pfx cv bs = do+    let len = B.length bs+    vb <- encodeVersionByte cv len+    let pl = vb `B.cons` bs+    return (cash32encode pfx pl)++-- | Low-Level: decode 'Cash32' string. 'CashPrefix' must be part of the string.+-- No version or hash length validation is performed.+cash32decode :: Cash32 -> Maybe (CashPrefix, ByteString)+cash32decode text = do+    let bs = C.map toLower bs'+    guard (C.map toUpper bs' == bs' || bs == bs')+    let (pfx', dat) = C.breakEnd (== ':') bs+    pfx <-+        if B.null pfx' || pfx' == C.singleton ':'+            then Nothing+            else Just (B.init pfx')+    b32 <- B.pack <$> mapM base32char (C.unpack dat)+    let px = B.map (.&. 0x1f) pfx+        pd = px <> B.singleton 0 <> b32+        cs = cash32Polymod pd+        bb = B.take (B.length b32 - 8) b32+    guard (verifyCash32Polymod cs)+    let out = toBase256 bb+    return (E.decodeUtf8 pfx, out)+  where+    bs' = E.encodeUtf8 text++-- | Low-Level: encode 'Cash32' string for 'CashPrefix' provided. Can encode+-- arbitrary data. No prefix or length validation is performed.+cash32encode :: CashPrefix -> ByteString -> Cash32+cash32encode pfx bs =+    let b32 = toBase32 bs+        px = B.map (.&. 0x1f) (E.encodeUtf8 pfx)+        pd = px <> B.singleton 0 <> b32 <> B.replicate 8 0+        cs = cash32Polymod pd+        c32 = B.map f (b32 <> cs)+        f = fromIntegral . ord . (charset !!) . fromIntegral+    in pfx <> ":" <> E.decodeUtf8 c32++-- | Convert base of 'ByteString' from eight bits per byte to five bits per+-- byte, adding padding as necessary.+toBase32 :: ByteString -> ByteString+toBase32 =+    B.pack .+    map fromIntegral . fst . convertBits True 8 5 . map fromIntegral . B.unpack++-- | Convert base of 'ByteString' from five to eight bits per byte. Ignore+-- padding to be symmetric with respect to 'toBase32' function.+toBase256 :: ByteString -> ByteString+toBase256 =+    B.pack .+    map fromIntegral . fst . convertBits False 5 8 . map fromIntegral . B.unpack++-- | Obtain 'CashVersion' and payload length from 'CashAddr' version byte.+decodeVersionByte :: Word8 -> Maybe (CashVersion, Int)+decodeVersionByte vb = do+    guard (vb .&. 0x80 == 0)+    return (ver, len)+  where+    ver = vb `shiftR` 3+    len = ls !! fromIntegral (vb .&. 0x07)+    ls = [20, 24, 28, 32, 40, 48, 56, 64]++-- | Encode 'CashVersion' and length into version byte. Fail if version is+-- larger than five bits, or length incorrect, since that is invalid.+encodeVersionByte :: CashVersion -> Int -> Maybe Word8+encodeVersionByte ver len = do+    guard (ver == ver .&. 0x0f)+    l <- case len of+        20 -> Just 0+        24 -> Just 1+        28 -> Just 2+        32 -> Just 3+        40 -> Just 4+        48 -> Just 5+        56 -> Just 6+        64 -> Just 7+        _  -> Nothing+    return ((ver `shiftL` 3) .|. l)++-- | Calculate or validate checksum from base32 'ByteString' (excluding prefix).+cash32Polymod :: ByteString -> ByteString+cash32Polymod v =+    B.pack+        [fromIntegral (polymod `shiftR` (5 * (7 - i))) .&. 0x1f | i <- [0 .. 7]]+  where+    polymod = B.foldl' outer (1 :: Word64) v `xor` 1+    outer c d =+        let c0 = (fromIntegral (c `shiftR` 35) :: Word8)+            c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d+        in foldl' (inner c0) c' (zip [0 ..] generator)+    generator =+        [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470]+    inner c0 c (b, g)+        | c0 `testBit` b = c `xor` g+        | otherwise = c++-- | Validate that polymod 'ByteString' (eight bytes) is equal to zero.+verifyCash32Polymod :: ByteString -> Bool+verifyCash32Polymod = (== B.replicate 8 0)
+ src/Haskoin/Block.hs view
@@ -0,0 +1,21 @@+{-|+Module      : Haskoin.Block+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Most functions relating to blocks are exported by this module.+-}+module Haskoin.Block+    ( module Haskoin.Block.Common+      -- * Block Header Chain+    , module Haskoin.Block.Headers+      -- * Merkle Blocks+    , module Haskoin.Block.Merkle+    ) where++import           Haskoin.Block.Common+import           Haskoin.Block.Headers+import           Haskoin.Block.Merkle
+ src/Haskoin/Block/Common.hs view
@@ -0,0 +1,345 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}+{-|+Module      : Haskoin.Block.Common+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Common data types and functions to handle blocks from the block chain.+-}+module Haskoin.Block.Common+    ( Block(..)+    , BlockHeight+    , Timestamp+    , BlockHeader(..)+    , headerHash+    , BlockLocator+    , GetBlocks(..)+    , GetHeaders(..)+    , BlockHeaderCount+    , BlockHash(..)+    , blockHashToHex+    , hexToBlockHash+    , Headers(..)+    , decodeCompact+    , encodeCompact+    ) where++import           Control.DeepSeq+import           Control.Monad              (forM_, liftM2, mzero, replicateM)+import           Data.Aeson                 (FromJSON (..), ToJSON (..),+                                             Value (String), toJSON, withText)+import           Data.Aeson.Encoding        (unsafeToEncoding)+import           Data.Bits                  (shiftL, shiftR, (.&.), (.|.))+import qualified Data.ByteString            as B+import           Data.ByteString.Builder    (char7)+import           Data.Hashable              (Hashable)+import           Data.Maybe                 (fromMaybe)+import           Data.Serialize             (Serialize, decode, encode, get,+                                             put)+import           Data.Serialize.Get         (getWord32le)+import           Data.Serialize.Put         (Put, putWord32le)+import           Data.String                (IsString, fromString)+import           Data.String.Conversions    (cs)+import           Data.Text                  (Text)+import           Data.Word                  (Word32)+import           GHC.Generics               (Generic)+import           Haskoin.Crypto.Hash+import           Haskoin.Network.Common+import           Haskoin.Transaction.Common+import           Haskoin.Util+import qualified Text.Read                  as R++-- | Height of a block in the block chain, starting at 0 for Genesis.+type BlockHeight = Word32++-- | Block timestamp as Unix time (seconds since 1970-01-01 00:00 UTC).+type Timestamp = Word32++-- | Block header and transactions.+data Block =+    Block { blockHeader :: !BlockHeader+          , blockTxns   :: ![Tx]+          } deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance Serialize Block where+    get = do+        header     <- get+        (VarInt c) <- get+        txs        <- replicateM (fromIntegral c) get+        return $ Block header txs+    put (Block h txs) = do+        put h+        putVarInt $ length txs+        forM_ txs put++instance ToJSON Block where+    toJSON = String . encodeHex . encode+    toEncoding s =+        unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'++instance FromJSON Block where+    parseJSON =+        withText "Block" $ \t -> do+            bin <-+                case decodeHex t of+                    Nothing  -> mzero+                    Just bin -> return bin+            case decode bin of+                Left e  -> fail e+                Right h -> return h++instance ToJSON BlockHeader where+    toJSON = String . encodeHex . encode+    toEncoding s =+        unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'++instance FromJSON BlockHeader where+    parseJSON =+        withText "BlockHeader" $ \t -> do+            bin <-+                case decodeHex t of+                    Nothing  -> mzero+                    Just bin -> return bin+            case decode bin of+                Left e  -> fail e+                Right h -> return h++-- | Block header hash. To be serialized reversed for display purposes.+newtype BlockHash = BlockHash+    { getBlockHash :: Hash256 }+    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)++instance Show BlockHash where+    showsPrec _ = shows . blockHashToHex++instance Read BlockHash where+    readPrec = do+        R.String str <- R.lexP+        maybe R.pfail return $ hexToBlockHash $ cs str++instance IsString BlockHash where+    fromString s =+        let e = error "Could not read block hash from hex string"+        in fromMaybe e $ hexToBlockHash $ cs s++instance FromJSON BlockHash where+    parseJSON = withText "block hash" $+        maybe mzero return . hexToBlockHash++instance ToJSON BlockHash where+    toJSON = String . blockHashToHex+    toEncoding s =+        unsafeToEncoding $+        char7 '"' <> hexBuilder (B.reverse (encode s)) <> char7 '"'++-- | Block hashes are reversed with respect to the in-memory byte order in a+-- block hash when displayed.+blockHashToHex :: BlockHash -> Text+blockHashToHex (BlockHash h) = encodeHex (B.reverse (encode h))++-- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are+-- reversed as normal.+hexToBlockHash :: Text -> Maybe BlockHash+hexToBlockHash hex = do+    bs <- B.reverse <$> decodeHex hex+    h <- eitherToMaybe (decode bs)+    return $ BlockHash h++-- | Data type recording information of a 'Block'. The hash of a block is+-- defined as the hash of this data structure, serialized. The block mining+-- process involves finding a partial hash collision by varying the nonce in the+-- 'BlockHeader' and/or additional entropy in the coinbase 'Transaction' of this+-- 'Block'. Variations in the coinbase will result in different merkle roots in+-- the 'BlockHeader'.+data BlockHeader =+    BlockHeader { blockVersion   :: !Word32      --  4 bytes+                  -- | hash of the previous block (parent)+                , prevBlock      :: !BlockHash   -- 32 bytes+                  -- | root of the merkle tree of transactions+                , merkleRoot     :: !Hash256     -- 32 bytes+                  -- | unix timestamp+                , blockTimestamp :: !Timestamp   --  4 bytes+                  -- | difficulty target+                , blockBits      :: !Word32      --  4 bytes+                  -- | random nonce+                , bhNonce        :: !Word32      --  4 bytes+                } deriving (Eq, Ord, Show, Read, Generic, Hashable, NFData)+                                                 -- 80 bytes++-- | Compute hash of 'BlockHeader'.+headerHash :: BlockHeader -> BlockHash+headerHash = BlockHash . doubleSHA256 . encode++instance Serialize BlockHeader where+    get = do+        v <- getWord32le+        p <- get+        m <- get+        t <- getWord32le+        b <- getWord32le+        n <- getWord32le+        return BlockHeader+            { blockVersion   = v+            , prevBlock      = p+            , merkleRoot     = m+            , blockTimestamp = t+            , blockBits      = b+            , bhNonce        = n+            }++    put (BlockHeader v p m bt bb n) = do+        putWord32le v+        put         p+        put         m+        putWord32le bt+        putWord32le bb+        putWord32le n++-- | A block locator is a set of block headers, denser towards the best block+-- and sparser towards the genesis block. It starts at the highest block known.+-- It is used by a node to synchronize against the network. When the locator is+-- provided to a peer, it will send back block hashes starting from the first+-- block in the locator that it recognizes.+type BlockLocator = [BlockHash]++-- | Data type representing a getblocks message request. It is used in the+-- bitcoin protocol to retrieve blocks from a peer by providing it a+-- 'BlockLocator' object. The response to a 'GetBlocks' message is an 'Inv'+-- message containing a list of block hashes that the peer believes this node is+-- missing. The number of block hashes in that inv message will end at the stop+-- block hash, at at the tip of the chain, or after 500 entries, whichever comes+-- earlier.+data GetBlocks =+    GetBlocks { -- | protocol version.+                getBlocksVersion  :: !Word32+                -- | block locator object+              , getBlocksLocator  :: !BlockLocator+                -- | hash of the last desired block+              , getBlocksHashStop :: !BlockHash+              } deriving (Eq, Show, Generic, NFData)++instance Serialize GetBlocks where++    get = GetBlocks <$> getWord32le+                    <*> (repList =<< get)+                    <*> get+      where+        repList (VarInt c) = replicateM (fromIntegral c) get++    put (GetBlocks v xs h) = putGetBlockMsg v xs h++putGetBlockMsg :: Word32 -> BlockLocator -> BlockHash -> Put+putGetBlockMsg v xs h = do+    putWord32le v+    putVarInt $ length xs+    forM_ xs put+    put h++-- | Similar to the 'GetBlocks' message type but for retrieving block headers+-- only. The response to a 'GetHeaders' request is a 'Headers' message+-- containing a list of block headers. A maximum of 2000 block headers can be+-- returned. 'GetHeaders' is used by simplified payment verification (SPV)+-- clients to exclude block contents when synchronizing the block chain.+data GetHeaders =+    GetHeaders {+                 -- | protocol version+                 getHeadersVersion  :: !Word32+                 -- | block locator object+               , getHeadersBL       :: !BlockLocator+                 -- | hash of the last desired block header+               , getHeadersHashStop :: !BlockHash+               } deriving (Eq, Show, Generic, NFData)++instance Serialize GetHeaders where++    get = GetHeaders <$> getWord32le+                     <*> (repList =<< get)+                     <*> get+      where+        repList (VarInt c) = replicateM (fromIntegral c) get++    put (GetHeaders v xs h) = putGetBlockMsg v xs h++-- | 'BlockHeader' type with a transaction count as 'VarInt'+type BlockHeaderCount = (BlockHeader, VarInt)++-- | The 'Headers' type is used to return a list of block headers in+-- response to a 'GetHeaders' message.+newtype Headers =+    Headers { -- | list of block headers with transaction count+              headersList :: [BlockHeaderCount]+            }+    deriving (Eq, Show, Generic, NFData)++instance Serialize Headers where++    get = Headers <$> (repList =<< get)+      where+        repList (VarInt c) = replicateM (fromIntegral c) action+        action = liftM2 (,) get get++    put (Headers xs) = do+        putVarInt $ length xs+        forM_ xs $ \(a,b) -> put a >> put b++-- | Decode the compact number used in the difficulty target of a block.+--+-- The compact format is a representation of a whole number \(N\) using an+-- unsigned 32-bit number similar to a floating point format. The most+-- significant 8 bits are the unsigned exponent of base 256. This exponent can+-- be thought of as the number of bytes of \(N\). The lower 23 bits are the+-- mantissa. Bit number 24 represents the sign of \(N\).+--+-- \[+-- N = -1^{sign} \times mantissa \times 256^{exponent-3}+-- \]+decodeCompact :: Word32 -> (Integer, Bool) -- ^ true means overflow+decodeCompact nCompact = (if neg then res * (-1) else res, over)+  where+    nSize :: Int+    nSize = fromIntegral nCompact `shiftR` 24+    nWord' :: Word32+    nWord' = nCompact .&. 0x007fffff+    nWord :: Word32+    nWord | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))+          | otherwise = nWord'+    res :: Integer+    res | nSize <= 3 = fromIntegral nWord+        | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))+    neg = nWord /= 0 && (nCompact .&. 0x00800000) /= 0+    over = nWord /= 0 && (nSize > 34 ||+                          nWord > 0xff && nSize > 33 ||+                          nWord > 0xffff && nSize > 32)++-- | Encode an 'Integer' to the compact number format used in the difficulty+-- target of a block.+encodeCompact :: Integer+              -> Word32+encodeCompact i = nCompact+  where+    i' = abs i+    neg = i < 0+    nSize' :: Int+    nSize' = let f 0 = 0+                 f n = 1 + f (n `shiftR` 8)+             in f i'+    nCompact''' :: Word32+    nCompact'''+        | nSize' <= 3 = fromIntegral $ (low64 .&. i') `shiftL` (8 * (3 - nSize'))+        | otherwise = fromIntegral $ low64 .&. (i' `shiftR` (8 * (nSize' - 3)))+    nCompact'' :: Word32+    nSize :: Int+    (nCompact'', nSize)+        | nCompact''' .&. 0x00800000 /= 0 = (nCompact''' `shiftR` 8, nSize' + 1)+        | otherwise = (nCompact''', nSize')+    nCompact' :: Word32+    nCompact' = nCompact'' .|. (fromIntegral nSize `shiftL` 24)+    nCompact :: Word32+    nCompact | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000+             | otherwise = nCompact'+    low64 :: Integer+    low64 = 0xffffffffffffffff
+ src/Haskoin/Block/Headers.hs view
@@ -0,0 +1,740 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE RecordWildCards   #-}+{-|+Module      : Haskoin.Block.Headers+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Block chain header synchronization and proof-of-work consensus functions.+-}+module Haskoin.Block.Headers+    ( BlockNode(..)+    , BlockHeaders(..)+    , BlockWork+    , genesisNode+    , genesisBlock+    , isGenesis+    , chooseBest+      -- * Header Chain Storage Functions+    , parentBlock+    , getParents+    , getAncestor+    , splitPoint+    , connectBlocks+    , connectBlock+    , blockLocator+      -- * In-Memory Header Chain Store+    , HeaderMemory(..)+    , ShortBlockHash+    , BlockMap+    , shortBlockHash+    , initialChain+    , genesisMap+      -- * Helper Functions+    , appendBlocks+    , validBlock+    , validCP+    , afterLastCP+    , bip34+    , validVersion+    , lastNoMinDiff+    , nextWorkRequired+    , nextEdaWorkRequired+    , nextDaaWorkRequired+    , computeTarget+    , getSuitableBlock+    , nextPowWorkRequired+    , calcNextWork+    , isValidPOW+    , blockPOW+    , headerWork+    , diffInterval+    , blockLocatorNodes+    , mineBlock+    , computeSubsidy+    , ) where++import           Control.Applicative        ((<|>))+import           Control.DeepSeq+import           Control.Monad              (guard, unless, when)+import           Control.Monad.Except       (ExceptT (..), runExceptT,+                                             throwError)+import           Control.Monad.State.Strict as State (StateT, get, gets, lift,+                                                      modify)+import           Control.Monad.Trans.Maybe+import           Data.Bits                  (shiftL, shiftR, (.&.))+import qualified Data.ByteString            as B+import           Data.ByteString.Short      (ShortByteString, fromShort,+                                             toShort)+import           Data.Function              (on)+import           Data.Hashable+import           Data.HashMap.Strict        (HashMap)+import qualified Data.HashMap.Strict        as HashMap+import           Data.List                  (sort, sortBy)+import           Data.Maybe                 (fromMaybe, listToMaybe)+import           Data.Serialize             as S (Serialize (..), decode,+                                                  encode, get, put)+import           Data.Serialize.Get         as S+import           Data.Serialize.Put         as S+import           Data.Typeable              (Typeable)+import           Data.Word                  (Word32, Word64)+import           GHC.Generics               (Generic)+import           Haskoin.Block.Common+import           Haskoin.Constants+import           Haskoin.Crypto+import           Haskoin.Transaction.Common+import           Haskoin.Util++-- | Short version of the block hash. Uses the good end of the hash (the part+-- that doesn't have a long string of zeroes).+type ShortBlockHash = Word64++-- | Memory-based map to a serialized 'BlockNode' data structure.+-- 'ShortByteString' is used to avoid memory fragmentation and make the data+-- structure compact.+type BlockMap = HashMap ShortBlockHash ShortByteString++-- | Represents accumulated work in the block chain so far.+type BlockWork = Integer++-- | Data structure representing a block header and its position in the+-- block chain.+data BlockNode+    -- | non-Genesis block header+    = BlockNode { nodeHeader :: !BlockHeader+                , nodeHeight :: !BlockHeight+        -- | accumulated work so far+                , nodeWork   :: !BlockWork+        -- | akip magic block hash+                , nodeSkip   :: !BlockHash }+    -- | Genesis block header+    | GenesisNode { nodeHeader :: !BlockHeader+                  , nodeHeight :: !BlockHeight+                  , nodeWork   :: !BlockWork }+    deriving (Show, Read, Generic, Hashable, NFData)++instance Serialize BlockNode where+    get = do+        nodeHeader <- S.get+        nodeHeight <- getWord32le+        nodeWork <- S.get+        if nodeHeight == 0+            then return GenesisNode {..}+            else do+                nodeSkip <- S.get+                return BlockNode {..}+    put bn = do+        put $ nodeHeader bn+        putWord32le $ nodeHeight bn+        put $ nodeWork bn+        case bn of+            GenesisNode {} -> return ()+            BlockNode {}   -> put $ nodeSkip bn++instance Eq BlockNode where+    (==) = (==) `on` nodeHeader++instance Ord BlockNode where+    compare = compare `on` nodeHeight++-- | Memory-based header tree.+data HeaderMemory = HeaderMemory+    { memoryHeaderMap  :: !BlockMap+    , memoryBestHeader :: !BlockNode+    } deriving (Eq, Typeable, Show, Read, Generic, Hashable, NFData)++-- | Typeclass for block header chain storage monad.+class Monad m => BlockHeaders m where+    -- | Add a new 'BlockNode' to the chain. Does not validate.+    addBlockHeader :: BlockNode -> m ()+    -- | Get a 'BlockNode' associated with a 'BlockHash'.+    getBlockHeader :: BlockHash -> m (Maybe BlockNode)+    -- | Locate the 'BlockNode' for the highest block in the chain+    getBestBlockHeader :: m BlockNode+    -- | Set the highest block in the chain.+    setBestBlockHeader :: BlockNode -> m ()+    -- | Add a continuous bunch of block headers the chain. Does not validate.+    addBlockHeaders :: [BlockNode] -> m ()+    addBlockHeaders = mapM_ addBlockHeader++instance Monad m => BlockHeaders (StateT HeaderMemory m) where+    addBlockHeader = modify . addBlockHeaderMemory+    getBlockHeader bh = getBlockHeaderMemory bh <$> State.get+    getBestBlockHeader = gets memoryBestHeader+    setBestBlockHeader bn = modify $ \s -> s { memoryBestHeader = bn }++-- | Initialize memory-based chain.+initialChain :: Network -> HeaderMemory+initialChain net = HeaderMemory+    { memoryHeaderMap = genesisMap net+    , memoryBestHeader = genesisNode net+    }++-- | Initialize map for memory-based chain.+genesisMap :: Network -> BlockMap+genesisMap net =+    HashMap.singleton+        (shortBlockHash (headerHash (getGenesisHeader net)))+        (toShort (encode (genesisNode net)))++-- | Add block header to memory block map.+addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory+addBlockHeaderMemory bn s@HeaderMemory{..} =+    let bm' = addBlockToMap bn memoryHeaderMap+    in s { memoryHeaderMap = bm' }++-- | Get block header from memory block map.+getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode+getBlockHeaderMemory bh HeaderMemory {..} = do+    bs <- shortBlockHash bh `HashMap.lookup` memoryHeaderMap+    eitherToMaybe . decode $ fromShort bs++-- | Calculate short block hash taking eight non-zero bytes from the 16-byte+-- hash. This function will take the bytes that are not on the zero-side of the+-- hash, making colissions between short block hashes difficult.+shortBlockHash :: BlockHash -> ShortBlockHash+shortBlockHash = either error id . decode . B.take 8 . encode++-- | Add a block to memory-based block map.+addBlockToMap :: BlockNode -> BlockMap -> BlockMap+addBlockToMap node =+    HashMap.insert+    (shortBlockHash $ headerHash $ nodeHeader node)+    (toShort $ encode node)++-- | Get the ancestor of the provided 'BlockNode' at the specified+-- 'BlockHeight'.+getAncestor :: BlockHeaders m+            => BlockHeight+            -> BlockNode+            -> m (Maybe BlockNode)+getAncestor height node+    | height > nodeHeight node = return Nothing+    | otherwise = go node+  where+    e1 = error "Could not get skip header"+    e2 = error "Could not get previous block header"+    go walk+        | nodeHeight walk > height =+            let heightSkip = skipHeight (nodeHeight walk)+                heightSkipPrev = skipHeight (nodeHeight walk - 1)+             in if not (isGenesis walk) &&+                   (heightSkip == height ||+                    (heightSkip > height &&+                     not+                         (heightSkipPrev < heightSkip - 2 &&+                          heightSkipPrev >= height)))+                    then do+                        walk' <- fromMaybe e1 <$> getBlockHeader (nodeSkip walk)+                        go walk'+                    else do+                        walk' <-+                            fromMaybe e2 <$>+                            getBlockHeader (prevBlock (nodeHeader walk))+                        go walk'+        | otherwise = return $ Just walk++-- | Is the provided 'BlockNode' the Genesis block?+isGenesis :: BlockNode -> Bool+isGenesis GenesisNode{} = True+isGenesis BlockNode{}   = False++-- | Build the genesis 'BlockNode' for the supplied 'Network'.+genesisNode :: Network -> BlockNode+genesisNode net =+    GenesisNode+        { nodeHeader = getGenesisHeader net+        , nodeHeight = 0+        , nodeWork = headerWork (getGenesisHeader net)+        }++-- | Validate a list of continuous block headers and import them to the+-- block chain. Return 'Left' on failure with error information.+connectBlocks :: BlockHeaders m+              => Network+              -> Timestamp       -- ^ current time+              -> [BlockHeader]+              -> m (Either String [BlockNode])+connectBlocks _ _ [] = return $ Right []+connectBlocks net t bhs@(bh:_) =+    runExceptT $ do+        unless (chained bhs) $+            throwError "Blocks to connect do not form a chain"+        par <-+            maybeToExceptT+                "Could not get parent block"+                (MaybeT (parentBlock bh))+        pars <- lift $ getParents 10 par+        bb <- lift getBestBlockHeader+        go par [] bb par pars bhs >>= \case+            bns@(bn:_) -> do+                lift $ addBlockHeaders bns+                let bb' = chooseBest bn bb+                when (bb' /= bb) $ lift $ setBestBlockHeader bb'+                return bns+            _ -> undefined+  where+    chained (h1:h2:hs) = headerHash h1 == prevBlock h2 && chained (h2 : hs)+    chained _          = True+    skipit lbh ls par+        | sh == nodeHeight lbh = return lbh+        | sh < nodeHeight lbh = do+            skM <- lift $ getAncestor sh lbh+            case skM of+                Just sk -> return sk+                Nothing ->+                    throwError $+                    "BUG: Could not get skip for block " +++                    show (headerHash $ nodeHeader par)+        | otherwise = do+            let sn = ls !! fromIntegral (nodeHeight par - sh)+            when (nodeHeight sn /= sh) $+                throwError "BUG: Node height not right in skip"+            return sn+      where+        sh = skipHeight (nodeHeight par + 1)+    go _ acc _ _ _ [] = return acc+    go lbh acc bb par pars (h:hs) = do+        sk <- skipit lbh acc par+        bn <- ExceptT . return $ validBlock net t bb par pars h sk+        go lbh (bn : acc) (chooseBest bn bb) bn (take 10 $ par : pars) hs++-- | Block's parent. If the block header is in the store, its parent must also+-- be there. No block header get deleted or pruned from the store.+parentBlock :: BlockHeaders m+            => BlockHeader+            -> m (Maybe BlockNode)+parentBlock bh = getBlockHeader (prevBlock bh)++-- | Validate and connect single block header to the block chain. Return 'Left' if fails+-- to be validated.+connectBlock ::+       BlockHeaders m+    => Network+    -> Timestamp -- ^ current time+    -> BlockHeader+    -> m (Either String BlockNode)+connectBlock net t bh =+    runExceptT $ do+        par <-+            maybeToExceptT+                "Could not get parent block"+                (MaybeT (parentBlock bh))+        pars <- lift $ getParents 10 par+        skM <- lift $ getAncestor (skipHeight (nodeHeight par + 1)) par+        sk <-+            case skM of+                Just sk -> return sk+                Nothing ->+                    throwError $+                    "BUG: Could not get skip for block " +++                    show (headerHash $ nodeHeader par)+        bb <- lift getBestBlockHeader+        bn <- ExceptT . return $ validBlock net t bb par pars bh sk+        let bb' = chooseBest bb bn+        lift $ addBlockHeader bn+        when (bb /= bb') . lift $ setBestBlockHeader bb'+        return bn++-- | Validate this block header. Build a 'BlockNode' if successful.+validBlock :: Network+           -> Timestamp     -- ^ current time+           -> BlockNode     -- ^ best block+           -> BlockNode     -- ^ immediate parent+           -> [BlockNode]   -- ^ 10 parents above+           -> BlockHeader   -- ^ header to validate+           -> BlockNode     -- ^ skip node (black magic)+           -> Either String BlockNode+validBlock net t bb par pars bh sk = do+    let mt = medianTime . map (blockTimestamp . nodeHeader) $ par : pars+        nt = blockTimestamp bh+        hh = headerHash bh+        nv = blockVersion bh+        ng = nodeHeight par + 1+        aw = nodeWork par + headerWork bh+    unless (isValidPOW net bh) $+        Left $ "Proof of work failed: " ++ show (headerHash bh)+    unless (nt <= t + 2 * 60 * 60) $+        Left $ "Invalid header timestamp: " ++ show nt+    unless (nt >= mt) $+        Left $ "Block timestamp too early: " ++ show nt+    unless (afterLastCP net (nodeHeight bb) ng) $+        Left $ "Rewriting pre-checkpoint chain: " ++ show ng+    unless (validCP net ng hh) $+        Left $ "Rejected checkpoint: " ++ show ng+    unless (bip34 net ng hh) $+        Left $ "Rejected BIP-34 block: " ++ show hh+    unless (validVersion net ng nv) $+        Left $ "Invalid block version: " ++ show nv+    return BlockNode { nodeHeader = bh+                     , nodeHeight = ng+                     , nodeWork = aw+                     , nodeSkip = headerHash $ nodeHeader sk+                     }++-- | Return the median of all provided timestamps. Can be unsorted. Error on+-- empty list.+medianTime :: [Timestamp] -> Timestamp+medianTime ts+    | null ts = error "Cannot compute median time of empty header list"+    | otherwise = sort ts !! (length ts `div` 2)++-- | Calculate the height of the skip (magic) block that corresponds to the+-- given height. The block hash of the ancestor at that height will be placed on+-- the 'BlockNode' structure to help locate ancestors at any height quickly.+skipHeight :: BlockHeight -> BlockHeight+skipHeight height+    | height < 2 = 0+    | height .&. 1 /= 0 = invertLowestOne (invertLowestOne $ height - 1) + 1+    | otherwise = invertLowestOne height++-- | Part of the skip black magic calculation.+invertLowestOne :: BlockHeight -> BlockHeight+invertLowestOne height = height .&. (height - 1)++-- | Get a number of parents for the provided block.+getParents :: BlockHeaders m+           => Int+           -> BlockNode+           -> m [BlockNode]   -- ^ starts from immediate parent+getParents = getpars []+  where+    getpars acc 0 _ = return $ reverse acc+    getpars acc _ GenesisNode{} = return $ reverse acc+    getpars acc n BlockNode{..} = do+        parM <- getBlockHeader $ prevBlock nodeHeader+        case parM of+            Just bn -> getpars (bn : acc) (n - 1) bn+            Nothing -> error "BUG: All non-genesis blocks should have a parent"++-- | Verify that checkpoint location is valid.+validCP :: Network+        -> BlockHeight  -- ^ new child height+        -> BlockHash    -- ^ new child hash+        -> Bool+validCP net height newChildHash =+    case lookup height (getCheckpoints net) of+        Just cpHash -> cpHash == newChildHash+        Nothing     -> True++-- | New block height above the last checkpoint imported. Used to prevent a+-- reorg below the highest checkpoint that was already imported.+afterLastCP :: Network+            -> BlockHeight  -- ^ best height+            -> BlockHeight  -- ^ new imported block height+            -> Bool+afterLastCP net bestHeight newChildHeight =+    case lM of+        Just l  -> l < newChildHeight+        Nothing -> True+  where+    lM =+        listToMaybe . reverse $+        [c | (c, _) <- getCheckpoints net, c <= bestHeight]++-- | This block should be at least version 2 (BIP34). Block height must be+-- included in the coinbase transaction to prevent non-unique transaction+-- hashes.+bip34 :: Network+      -> BlockHeight  -- ^ new child height+      -> BlockHash    -- ^ new child hash+      -> Bool+bip34 net height hsh+    | fst (getBip34Block net) == 0 = True+    | fst (getBip34Block net) == height = snd (getBip34Block net) == hsh+    | otherwise = True++-- | Check if the provided block height and version are valid.+validVersion :: Network+             -> BlockHeight  -- ^ new child height+             -> Word32       -- ^ new child version+             -> Bool+validVersion net height version+    | version < 2 = height < fst (getBip34Block net)+    | version < 3 = height < getBip66Height net+    | version < 4 = height < getBip65Height net+    | otherwise = True++-- | Find last block with normal, as opposed to minimum difficulty (for test+-- networks).+lastNoMinDiff :: BlockHeaders m => Network -> BlockNode -> m BlockNode+lastNoMinDiff net bn@BlockNode {..} = do+    let i = nodeHeight `mod` diffInterval net /= 0+        c = encodeCompact (getPowLimit net)+        l = blockBits nodeHeader == c+        e1 =+            error $+            "Could not get block header for parent of " +++            show (headerHash nodeHeader)+    if i && l+        then do+            bn' <- fromMaybe e1 <$> getBlockHeader (prevBlock nodeHeader)+            lastNoMinDiff net bn'+        else return bn++lastNoMinDiff _ bn@GenesisNode{} = return bn++-- | Returns the work required on a block header given the previous block. This+-- coresponds to @bitcoind@ function @GetNextWorkRequired@ in @main.cpp@.+nextWorkRequired :: BlockHeaders m+                 => Network+                 -> BlockNode+                 -> BlockHeader+                 -> m Word32+nextWorkRequired net par bh = do+    let mf = daa <|> eda <|> pow+    case mf of+        Just f -> f net par bh+        Nothing ->+            error+                "Could not get an appropriate difficulty calculation algorithm"+  where+    daa = getDaaBlockHeight net >>= \daaHeight -> do+        guard (nodeHeight par + 1 >= daaHeight)+        return nextDaaWorkRequired+    eda = getEdaBlockHeight net >>= \edaHeight -> do+        guard (nodeHeight par + 1 >= edaHeight)+        return nextEdaWorkRequired+    pow = return nextPowWorkRequired++-- | Find out the next amount of work required according to the Emergency+-- Difficulty Adjustment (EDA) algorithm from Bitcoin Cash.+nextEdaWorkRequired ::+       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32+nextEdaWorkRequired net par bh+    | nodeHeight par + 1 `mod` diffInterval net == 0 =+        nextWorkRequired net par bh+    | minDifficulty = return (encodeCompact (getPowLimit net))+    | blockBits (nodeHeader par) == encodeCompact (getPowLimit net) =+        return (encodeCompact (getPowLimit net))+    | otherwise = do+        par6 <- fromMaybe e1 <$> getAncestor (nodeHeight par - 6) par+        pars <- getParents 10 par+        pars6 <- getParents 10 par6+        let par6med =+                medianTime $ map (blockTimestamp . nodeHeader) (par6 : pars6)+            parmed = medianTime $ map (blockTimestamp . nodeHeader) (par : pars)+            mtp6 = parmed - par6med+        if mtp6 < 12 * 3600+            then return $ blockBits (nodeHeader par)+            else return $+                 let (diff, _) = decodeCompact (blockBits (nodeHeader par))+                     ndiff = diff + (diff `shiftR` 2)+                  in if getPowLimit net > ndiff+                         then encodeCompact (getPowLimit net)+                         else encodeCompact ndiff+  where+    minDifficulty =+        blockTimestamp bh >+        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2+    e1 = error "Could not get seventh ancestor of block"++-- | Find the next amount of work required according to the Difficulty+-- Adjustment Algorithm (DAA) from Bitcoin Cash.+nextDaaWorkRequired ::+       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32+nextDaaWorkRequired net par bh+    | minDifficulty = return (encodeCompact (getPowLimit net))+    | otherwise = do+        let height = nodeHeight par+        unless (height >= diffInterval net) $+            error "Block height below difficulty interval"+        l <- getSuitableBlock par+        par144 <- fromMaybe e1 <$> getAncestor (height - 144) par+        f <- getSuitableBlock par144+        let nextTarget = computeTarget net f l+        if nextTarget > getPowLimit net+            then return $ encodeCompact (getPowLimit net)+            else return $ encodeCompact nextTarget+  where+    e1 = error "Cannot get ancestor at parent - 144 height"+    minDifficulty =+        blockTimestamp bh >+        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2++-- | Compute Bitcoin Cash DAA target for a new block.+computeTarget :: Network -> BlockNode -> BlockNode -> Integer+computeTarget net f l =+    let work = (nodeWork l - nodeWork f) * fromIntegral (getTargetSpacing net)+        actualTimespan =+            blockTimestamp (nodeHeader l) - blockTimestamp (nodeHeader f)+        actualTimespan'+            | actualTimespan > 288 * getTargetSpacing net =+                288 * getTargetSpacing net+            | actualTimespan < 72 * getTargetSpacing net =+                72 * getTargetSpacing net+            | otherwise = actualTimespan+        work' = work `div` fromIntegral actualTimespan'+     in 2 ^ (256 :: Integer) `div` work'++-- | Get suitable block for Bitcoin Cash DAA computation.+getSuitableBlock :: BlockHeaders m => BlockNode -> m BlockNode+getSuitableBlock par = do+    unless (nodeHeight par >= 3) $ error "Block height is less than three"+    blocks <- (par :) <$> getParents 2 par+    return $ sortBy (compare `on` blockTimestamp . nodeHeader) blocks !! 1++-- | Returns the work required on a block header given the previous block. This+-- coresponds to bitcoind function GetNextWorkRequired in main.cpp.+nextPowWorkRequired ::+       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32+nextPowWorkRequired net par bh+    | nodeHeight par + 1 `mod` diffInterval net /= 0 =+        if getAllowMinDifficultyBlocks net+            then if ht > pt + delta+                     then return $ encodeCompact (getPowLimit net)+                     else do+                         d <- lastNoMinDiff net par+                         return $ blockBits $ nodeHeader d+            else return $ blockBits $ nodeHeader par+    | otherwise = do+        let rh = nodeHeight par - (diffInterval net - 1)+        a <- fromMaybe e1 <$> getAncestor rh par+        let t = blockTimestamp $ nodeHeader a+        return $ calcNextWork net (nodeHeader par) t+  where+    e1 = error "Could not get ancestor for block header"+    pt = blockTimestamp $ nodeHeader par+    ht = blockTimestamp bh+    delta = getTargetSpacing net * 2++-- | Computes the work required for the first block in a new retarget period.+calcNextWork :: Network+             -> BlockHeader  -- ^ last block in previous retarget (parent)+             -> Timestamp    -- ^ timestamp of first block in previous retarget+             -> Word32+calcNextWork net header time+    | getPowNoRetargetting net = blockBits header+    | new > getPowLimit net = encodeCompact (getPowLimit net)+    | otherwise = encodeCompact new+  where+    s = blockTimestamp header - time+    n | s < getTargetTimespan net `div` 4 = getTargetTimespan net `div` 4+      | s > getTargetTimespan net * 4 = getTargetTimespan net * 4+      | otherwise = s+    l = fst $ decodeCompact $ blockBits header+    new = l * fromIntegral n `div` fromIntegral (getTargetTimespan net)++-- | Returns True if the difficulty target (bits) of the header is valid and the+-- proof of work of the header matches the advertised difficulty target. This+-- function corresponds to the function @CheckProofOfWork@ from @bitcoind@ in+-- @main.cpp@.+isValidPOW :: Network -> BlockHeader -> Bool+isValidPOW net h+    | target <= 0 || over || target > getPowLimit net = False+    | otherwise = blockPOW (headerHash h) <= fromIntegral target+  where+    (target, over) = decodeCompact $ blockBits h++-- | Returns the proof of work of a block header hash as an 'Integer' number.+blockPOW :: BlockHash -> Integer+blockPOW =  bsToInteger . B.reverse . encode++-- | Returns the work represented by this block. Work is defined as the number+-- of tries needed to solve a block in the average case with respect to the+-- target.+headerWork :: BlockHeader -> Integer+headerWork bh = largestHash `div` (target + 1)+  where+    target      = fst $ decodeCompact $ blockBits bh+    largestHash = 1 `shiftL` 256++-- | Number of blocks on average between difficulty cycles (2016 blocks).+diffInterval :: Network -> Word32+diffInterval net = getTargetTimespan net `div` getTargetSpacing net++-- | Compare two blocks to get the best.+chooseBest :: BlockNode -> BlockNode -> BlockNode+chooseBest b1 b2 | nodeWork b1 == nodeWork b2 =+                       if nodeHeight b1 >= nodeHeight b2+                       then b1+                       else b2+                 | nodeWork b1 > nodeWork b2 = b1+                 | otherwise = b2++-- | Get list of blocks for a block locator.+blockLocatorNodes :: BlockHeaders m => BlockNode -> m [BlockNode]+blockLocatorNodes best =+    reverse <$> go [] best 1+  where+    e1 = error "Could not get ancestor"+    go loc bn n =+        let loc' = bn : loc+            n' = if length loc' > 10+                 then n * 2+                 else 1+        in if nodeHeight bn < n'+           then do a <- fromMaybe e1 <$> getAncestor 0 bn+                   return $ a : loc'+           else do let h = nodeHeight bn - n'+                   bn' <- fromMaybe e1 <$> getAncestor h bn+                   go loc' bn' n'++-- | Get block locator.+blockLocator :: BlockHeaders m => BlockNode -> m BlockLocator+blockLocator bn = map (headerHash . nodeHeader) <$> blockLocatorNodes bn++-- | Become rich beyond your wildest dreams.+mineBlock :: Network -> Word32 -> BlockHeader -> BlockHeader+mineBlock net seed h =+    head+        [ j+        | i <- (+ seed) <$> [0 .. maxBound]+        , let j = h {bhNonce = i}+        , isValidPOW net j+        ]++-- | Generate and append new blocks (mining). Only practical in regtest network.+appendBlocks ::+       Network+    -> Word32 -- ^ random seed+    -> BlockHeader+    -> Int+    -> [BlockHeader]+appendBlocks _ _ _ 0 = []+appendBlocks net seed bh i =+    bh' : appendBlocks net seed bh' (i - 1)+  where+    bh' = mineBlock net seed bh+        { prevBlock = headerHash bh+          -- Just to make it different in every header+        , merkleRoot = sha256 $ encode seed+        }++-- | Find the last common block ancestor between provided block headers.+splitPoint :: BlockHeaders m => BlockNode -> BlockNode -> m BlockNode+splitPoint l r = do+    let h = min (nodeHeight l) (nodeHeight r)+    ll <- fromMaybe e <$> getAncestor h l+    lr <- fromMaybe e <$> getAncestor h r+    f ll lr+  where+    e = error "BUG: Could not get ancestor at lowest height"+    f ll lr =+        if ll == lr+            then return lr+            else do+                let h = nodeHeight ll - 1+                pl <- fromMaybe e <$> getAncestor h ll+                pr <- fromMaybe e <$> getAncestor h lr+                f pl pr++-- | Generate the entire Genesis block for 'Network'.+genesisBlock :: Network -> Block+genesisBlock net = Block (getGenesisHeader net) [genesisTx]++-- | Compute block subsidy at particular height.+computeSubsidy :: Network -> BlockHeight -> Word64+computeSubsidy net height =+    let halvings = height `div` getHalvingInterval net+        ini = 50 * 100 * 1000 * 1000+     in if halvings >= 64+            then 0+            else ini `shiftR` fromIntegral halvings
+ src/Haskoin/Block/Merkle.hs view
@@ -0,0 +1,255 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}+{-|+Module      : Haskoin.Block.Merkle+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Function to deal with Merkle trees inside blocks.+-}+module Haskoin.Block.Merkle+    ( MerkleBlock(..)+    , MerkleRoot+    , FlagBits+    , PartialMerkleTree+    , buildMerkleRoot+    , merkleBlockTxs+    , testMerkleRoot+    -- * Helper functions+    , buildPartialMerkle+    , decodeMerkleFlags+    , encodeMerkleFlags+    , calcTreeHeight+    , calcTreeWidth+    , hash2+    , calcHash+    , traverseAndBuild+    , traverseAndExtract+    , extractMatches+    , splitIn+    , boolsToWord8+    ) where++import           Control.DeepSeq+import           Control.Monad              (forM_, replicateM, when)+import           Data.Bits+import qualified Data.ByteString            as BS+import           Data.Either                (isRight)+import           Data.Hashable+import           Data.Maybe+import           Data.Serialize             (Serialize, encode, get, put)+import           Data.Serialize.Get         (getWord32le, getWord8)+import           Data.Serialize.Put         (putWord32le, putWord8)+import           Data.Word                  (Word32, Word8)+import           GHC.Generics+import           Haskoin.Block.Common+import           Haskoin.Constants+import           Haskoin.Crypto.Hash+import           Haskoin.Network.Common+import           Haskoin.Transaction.Common++-- | Hash of the block's Merkle root.+type MerkleRoot        = Hash256++-- | Bits that are used to rebuild partial merkle tree transaction hash list.+type FlagBits          = [Bool]++-- | Partial Merkle tree for a filtered block.+type PartialMerkleTree = [Hash256]++-- | Filtered block: a block with a partial Merkle tree that only includes the+-- transactions that pass a bloom filter that was negotiated.+data MerkleBlock =+    MerkleBlock {+                -- | block header+                  merkleHeader    :: !BlockHeader+                -- | total number of transactions in block+                , merkleTotalTxns :: !Word32+                -- | hashes in depth-first order+                , mHashes         :: !PartialMerkleTree+                -- | bits to rebuild partial merkle tree+                , mFlags          :: !FlagBits+                } deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance Serialize MerkleBlock where++    get = do+        header <- get+        ntx    <- getWord32le+        (VarInt matchLen) <- get+        hashes <- replicateM (fromIntegral matchLen) get+        (VarInt flagLen)  <- get+        ws <- replicateM (fromIntegral flagLen) getWord8+        return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)++    put (MerkleBlock h ntx hashes flags) = do+        put h+        putWord32le ntx+        putVarInt $ length hashes+        forM_ hashes put+        let ws = encodeMerkleFlags flags+        putVarInt $ length ws+        forM_ ws putWord8++-- | Unpack Merkle flags into 'FlagBits' structure.+decodeMerkleFlags :: [Word8] -> FlagBits+decodeMerkleFlags ws =+    [ b | p <- [ 0 .. length ws * 8 - 1 ]+        , b <- [ testBit (ws !! (p `div` 8)) (p `mod` 8) ]+    ]++-- | Pack Merkle flags from 'FlagBits'.+encodeMerkleFlags :: FlagBits -> [Word8]+encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs++-- | Computes the height of a Merkle tree.+calcTreeHeight :: Int -- ^ number of transactions (leaf nodes)+               -> Int -- ^ height of the merkle tree+calcTreeHeight ntx | ntx < 2 = 0+                   | even ntx  = 1 + calcTreeHeight (ntx `div` 2)+                   | otherwise = calcTreeHeight $ ntx + 1++-- | Computes the width of a Merkle tree at a specific height. The transactions+-- are at height 0.+calcTreeWidth :: Int -- ^ number of transactions (leaf nodes)+              -> Int -- ^ height at which we want to compute the width+              -> Int -- ^ width of the Merkle tree+calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h++-- | Computes the root of a Merkle tree from a list of leaf node hashes.+buildMerkleRoot :: [TxHash]   -- ^ transaction hashes (leaf nodes)+                -> MerkleRoot -- ^ root of the Merkle tree+buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs++-- | Concatenate and compute double SHA256.+hash2 :: Hash256 -> Hash256 -> Hash256+hash2 a b = doubleSHA256 $ encode a `BS.append` encode b++-- | Computes the hash of a specific node in a Merkle tree.+calcHash :: Int       -- ^ height of the node+         -> Int       -- ^ position of the node (0 for the leftmost node)+         -> [TxHash]  -- ^ transaction hashes (leaf nodes)+         -> Hash256   -- ^ hash of the node at the specified position+calcHash height pos txs+    | height < 0 || pos < 0 = error "calcHash: Invalid parameters"+    | height == 0 = getTxHash $ txs !! pos+    | otherwise = hash2 left right+  where+    left = calcHash (height-1) (pos*2) txs+    right | pos*2+1 < calcTreeWidth (length txs) (height-1) =+                calcHash (height-1) (pos*2+1) txs+          | otherwise = left++-- | Build a partial Merkle tree. Provide a list of tuples with all transaction+-- hashes in the block, and whether the transaction is to be included in the+-- partial tree. Returns a flag bits structure and the computed partial Merkle+-- tree.+buildPartialMerkle ::+       [(TxHash, Bool)] -- ^ transaction hash and whether to include+    -> (FlagBits, PartialMerkleTree) -- ^ flag bits and partial Merkle tree+buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs++-- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'+-- above.+traverseAndBuild ::+       Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)+traverseAndBuild height pos txs+    | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters"+    | height == 0 || not match = ([match], [calcHash height pos t])+    | otherwise = (match : lb ++ rb, lh ++ rh)+  where+    t = map fst txs+    s = pos `shiftL` height+    e = min (length txs) $ (pos + 1) `shiftL` height+    match = any snd $ take (e - s) $ drop s txs+    (lb, lh) = traverseAndBuild (height - 1) (pos * 2) txs+    (rb, rh)+        | (pos * 2 + 1) < calcTreeWidth (length txs) (height - 1) =+            traverseAndBuild (height - 1) (pos * 2 + 1) txs+        | otherwise = ([], [])++-- | Helper function to extract transaction hashes from partial Merkle tree.+traverseAndExtract :: Int -> Int -> Int -> FlagBits -> PartialMerkleTree+                   -> Maybe (MerkleRoot, [TxHash], Int, Int)+traverseAndExtract height pos ntx flags hashes+    | null flags               = Nothing+    | height == 0 || not match = leafResult+    | isNothing leftM          = Nothing+    | (pos*2+1) >= calcTreeWidth ntx (height-1) =+        Just (hash2 lh lh, lm, lcf+1, lch)+    | isNothing rightM         = Nothing+    | otherwise =+        Just (hash2 lh rh, lm ++ rm, lcf+rcf+1, lch+rch)+  where+    leafResult+        | null hashes = Nothing+        | otherwise = Just (h, [ TxHash h | height == 0 && match ], 1, 1)+    (match:fs) = flags+    (h:_)     = hashes+    leftM  = traverseAndExtract (height-1) (pos*2) ntx fs hashes+    (lh,lm,lcf,lch) = fromMaybe e leftM+    rightM = traverseAndExtract (height-1) (pos*2+1) ntx+                (drop lcf fs) (drop lch hashes)+    (rh,rm,rcf,rch) = fromMaybe e rightM+    e = error "traverseAndExtract: unexpected error extracting a Maybe value"++-- | Extracts the matching hashes from a partial merkle tree. This will return+-- the list of transaction hashes that have been included (set to true) in+-- a call to 'buildPartialMerkle'.+extractMatches :: Network+               -> FlagBits+               -> PartialMerkleTree+               -> Int -- ^ number of transaction at height 0 (leaf nodes)+               -> Either String (MerkleRoot, [TxHash])+               -- ^ Merkle root and list of matching transaction hashes+extractMatches net flags hashes ntx+    | ntx == 0 = Left+        "extractMatches: number of transactions can not be 0"+    | ntx > getMaxBlockSize net `div` 60 = Left+        "extractMatches: number of transactions excessively high"+    | length hashes > ntx = Left+        "extractMatches: More hashes provided than the number of transactions"+    | length flags < length hashes = Left+        "extractMatches: At least one bit per node and one bit per hash"+    | isNothing resM = Left+        "extractMatches: traverseAndExtract failed"+    | (nBitsUsed+7) `div` 8 /= (length flags+7) `div` 8 = Left+        "extractMatches: All bits were not consumed"+    | nHashUsed /= length hashes = Left $+        "extractMatches: All hashes were not consumed: " ++ show nHashUsed+    | otherwise = return (merkRoot, matches)+  where+    resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes+    (merkRoot, matches, nBitsUsed, nHashUsed) = fromMaybe e resM+    e = error "extractMatches: unexpected error extracting a Maybe value"++-- | Helper function to split a list in chunks 'Int' length. Last chunk may be+-- smaller.+splitIn :: Int -> [a] -> [[a]]+splitIn _ [] = []+splitIn c xs = xs1 : splitIn c xs2+  where+    (xs1, xs2) = splitAt c xs++-- | Pack up to eight bools in a byte.+boolsToWord8 :: [Bool] -> Word8+boolsToWord8 [] = 0+boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])++-- | Get matching transactions from Merkle block.+merkleBlockTxs :: Network -> MerkleBlock -> Either String [TxHash]+merkleBlockTxs net b =+    let flags = mFlags b+        hs = mHashes b+        n = fromIntegral $ merkleTotalTxns b+        merkle = merkleRoot $ merkleHeader b+    in do (root, ths) <- extractMatches net flags hs n+          when (root /= merkle) $ Left "merkleBlockTxs: Merkle root incorrect"+          return ths++-- | Check if Merkle block root is valid against the block header.+testMerkleRoot :: Network -> MerkleBlock -> Bool+testMerkleRoot net = isRight . merkleBlockTxs net
+ src/Haskoin/Constants.hs view
@@ -0,0 +1,540 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Constants+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Network constants for various networks, including Bitcoin SegWit (BTC), Bitcoin+Cash (BCH), and corresponding public test and private regression test networks.+-}+module Haskoin.Constants+    ( Network(..)+    , btc+    , btcTest+    , btcRegTest+    , bch+    , bchTest+    , bchRegTest+    , allNets+    , netByName+    , netByIdent+    ) where++import           Control.DeepSeq+import           Data.ByteString      (ByteString)+import           Data.List+import           Data.Maybe+import           Data.Serialize+import           Data.String+import           Data.Text            (Text)+import           Data.Version+import           Data.Word            (Word32, Word64, Word8)+import           GHC.Generics         (Generic)+import           Haskoin.Block.Common+import           Paths_haskoin_core+import           Text.Read++-- | Version of Haskoin Core package.+versionString :: IsString a => a+versionString = fromString (showVersion version)++-- | Constants for network.+data Network = Network+    { -- | lowercase alphanumeric and dashes+      getNetworkName              :: !String+      -- | network Haskell identifier+    , getNetworkIdent             :: !String+      -- | prefix for 'Base58' P2PKH addresses+    , getAddrPrefix               :: !Word8+      -- | prefix for 'Base58' P2SH addresses+    , getScriptPrefix             :: !Word8+      -- | prefix for WIF private key+    , getSecretPrefix             :: !Word8+      -- | prefix for extended public key+    , getExtPubKeyPrefix          :: !Word32+      -- | prefix for extended private key+    , getExtSecretPrefix          :: !Word32+      -- | network magic+    , getNetworkMagic             :: !Word32+      -- | genesis block header+    , getGenesisHeader            :: !BlockHeader+      -- | maximum block size in bytes+    , getMaxBlockSize             :: !Int+      -- | maximum amount of satoshi+    , getMaxSatoshi               :: !Word64+      -- | user agent string+    , getHaskoinUserAgent         :: !ByteString+      -- | default port for P2P connections+    , getDefaultPort              :: !Int+      -- | allow min difficulty blocks (testnet)+    , getAllowMinDifficultyBlocks :: !Bool+      -- | do not retarget difficulty (regtest)+    , getPowNoRetargetting        :: !Bool+      -- | proof-of-work target higest possible value+    , getPowLimit                 :: !Integer+      -- | block at which BIP34 activates+    , getBip34Block               :: !(BlockHeight, BlockHash)+      -- | block at which BIP65 activates+    , getBip65Height              :: !BlockHeight+      -- | block at which BIP66 activates+    , getBip66Height              :: !BlockHeight+      -- | time between difficulty retargets+    , getTargetTimespan           :: !Word32+      -- | time between blocks+    , getTargetSpacing            :: !Word32+      -- | checkpoints+    , getCheckpoints              :: ![(BlockHeight, BlockHash)]+      -- | BIP44 derivation path root+    , getBip44Coin                :: !Word32+      -- | peer-to-peer network seeds+    , getSeeds                    :: ![String]+      -- | fork id for replay protection+    , getSigHashForkId            :: !(Maybe Word32)+      -- | EDA start block height+    , getEdaBlockHeight           :: !(Maybe Word32)+      -- | DAA start block height+    , getDaaBlockHeight           :: !(Maybe Word32)+      -- | segregated witness active+    , getSegWit                   :: !Bool+      -- | 'CashAddr' prefix (for Bitcoin Cash)+    , getCashAddrPrefix           :: !(Maybe Text)+      -- | 'Bech32' prefix (for SegWit network)+    , getBech32Prefix             :: !(Maybe Text)+      -- | Replace-By-Fee (BIP-125)+    , getReplaceByFee             :: !Bool+      -- | Subsidy halving interval+    , getHalvingInterval          :: !Word32+    } deriving (Eq, Generic, NFData)++instance Serialize Network where+    put net =+        putWord32be $ getNetworkMagic net+    get = do+        magic <- getWord32be+        case find ((== magic) . getNetworkMagic) allNets of+            Nothing  -> fail $ "Network magic unknown: " <> show magic+            Just net -> return net++instance Show Network where+    show = getNetworkIdent++instance Read Network where+    readPrec = do+        Ident str <- lexP+        maybe pfail return (netByIdent str)++instance IsString Network where+    fromString = fromMaybe (error "Network name invalid") . netByName++-- | Query known networks by name.+netByName :: String -> Maybe Network+netByName str = find ((== str) . getNetworkName) allNets++-- | Query known networks by Haskell identifier.+netByIdent :: String -> Maybe Network+netByIdent str = find ((== str) . getNetworkIdent) allNets++-- | Bitcoin SegWit network. Symbol: BTC.+btc :: Network+btc =+    Network+    { getNetworkName = "btc"+    , getNetworkIdent = "btc"+    , getAddrPrefix = 0+    , getScriptPrefix = 5+    , getSecretPrefix = 128+    , getExtPubKeyPrefix = 0x0488b21e+    , getExtSecretPrefix = 0x0488ade4+    , getNetworkMagic = 0xf9beb4d9+    , getGenesisHeader =+          BlockHeader+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1231006505+              0x1d00ffff+              2083236893+            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+    , getMaxBlockSize = 1000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent =+          "/haskoin-btc:" <> versionString <> "/"+    , getDefaultPort = 8333+    , getAllowMinDifficultyBlocks = False+    , getPowNoRetargetting = False+    , getPowLimit =+          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 227931+          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")+    , getBip65Height = 388381+    , getBip66Height = 363725+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints =+          [ ( 11111+            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")+          , ( 33333+            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")+          , ( 74000+            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")+          , ( 105000+            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")+          , ( 134444+            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")+          , ( 168000+            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")+          , ( 193000+            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")+          , ( 210000+            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")+          , ( 216116+            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")+          , ( 225430+            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")+          , ( 250000+            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")+          , ( 279000+            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")+          , ( 295000+            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")+          ]+    , getSeeds =+          [ "seed.bitcoin.sipa.be" -- Pieter Wuille+          , "dnsseed.bluematt.me" -- Matt Corallo+          , "dnsseed.bitcoin.dashjr.org" -- Luke Dashjr+          , "seed.bitcoinstats.com" -- Chris Decker+          , "seed.bitcoin.jonasschnelli.ch" -- Jonas Schnelli+          , "seed.btc.petertodd.org" -- Peter Todd+          , "seed.bitcoin.sprovoost.nl" -- Sjors Provoost+          ]+    , getBip44Coin = 0+    , getSigHashForkId = Nothing+    , getEdaBlockHeight = Nothing+    , getDaaBlockHeight = Nothing+    , getSegWit = True+    , getCashAddrPrefix = Nothing+    , getBech32Prefix = Just "bc"+    , getReplaceByFee = True+    , getHalvingInterval = 210000+    }++-- | Testnet for Bitcoin SegWit network.+btcTest :: Network+btcTest =+    Network+    { getNetworkName = "btctest"+    , getNetworkIdent = "btcTest"+    , getAddrPrefix = 111+    , getScriptPrefix = 196+    , getSecretPrefix = 239+    , getExtPubKeyPrefix = 0x043587cf+    , getExtSecretPrefix = 0x04358394+    , getNetworkMagic = 0x0b110907+    , getGenesisHeader =+          BlockHeader+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1296688602+              486604799+              414098458+            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+    , getMaxBlockSize = 1000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent = "/haskoin-btc-test:" <> versionString <> "/"+    , getDefaultPort = 18333+    , getAllowMinDifficultyBlocks = True+    , getPowNoRetargetting = False+    , getPowLimit =+          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 21111+          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")+    , getBip65Height = 581885+    , getBip66Height = 330776+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints =+          [ ( 546+            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")+          ]+    , getSeeds =+          [ "testnet-seed.bitcoin.jonasschnelli.ch"+          , "seed.tbtc.petertodd.org"+          , "seed.testnet.bitcoin.sprovoost.nl"+          , "testnet-seed.bluematt.me"+          ]+    , getBip44Coin = 1+    , getSigHashForkId = Nothing+    , getEdaBlockHeight = Nothing+    , getDaaBlockHeight = Nothing+    , getSegWit = True+    , getCashAddrPrefix = Nothing+    , getBech32Prefix = Just "tb"+    , getReplaceByFee = True+    , getHalvingInterval = 210000+    }++-- | RegTest for Bitcoin SegWit network.+btcRegTest :: Network+btcRegTest =+    Network+    { getNetworkName = "btcreg"+    , getNetworkIdent = "btcRegTest"+    , getAddrPrefix = 111+    , getScriptPrefix = 196+    , getSecretPrefix = 239+    , getExtPubKeyPrefix = 0x043587cf+    , getExtSecretPrefix = 0x04358394+    , getNetworkMagic = 0xfabfb5da+    , getGenesisHeader =+          BlockHeader+           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1296688602+              0x207fffff+              2+    , getMaxBlockSize = 1000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent = "/haskoin-btc-regtest:" <> versionString <> "/"+    , getDefaultPort = 18444+    , getAllowMinDifficultyBlocks = True+    , getPowNoRetargetting = True+    , getPowLimit =+          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 100000000+          , "0000000000000000000000000000000000000000000000000000000000000000")+    , getBip65Height = 1351+    , getBip66Height = 1251+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints = []+    , getSeeds = ["localhost"]+    , getBip44Coin = 1+    , getSigHashForkId = Nothing+    , getEdaBlockHeight = Nothing+    , getDaaBlockHeight = Nothing+    , getSegWit = True+    , getCashAddrPrefix = Nothing+    , getBech32Prefix = Just "bcrt"+    , getReplaceByFee = True+    , getHalvingInterval = 150+    }++-- | Bitcoin Cash network. Symbol: BCH.+bch :: Network+bch =+    Network+    { getNetworkName = "bch"+    , getNetworkIdent = "bch"+    , getAddrPrefix = 0+    , getScriptPrefix = 5+    , getSecretPrefix = 128+    , getExtPubKeyPrefix = 0x0488b21e+    , getExtSecretPrefix = 0x0488ade4+    , getNetworkMagic = 0xe3e1f3e8+    , getGenesisHeader =+          BlockHeader+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1231006505+              0x1d00ffff+              2083236893+            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+    , getMaxBlockSize = 32000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent = "/haskoin-bch:" <> versionString <> "/"+    , getDefaultPort = 8333+    , getAllowMinDifficultyBlocks = False+    , getPowNoRetargetting = False+    , getPowLimit =+          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 227931+          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")+    , getBip65Height = 388381+    , getBip66Height = 363725+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints =+          [ ( 11111+            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")+          , ( 33333+            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")+          , ( 74000+            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")+          , ( 105000+            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")+          , ( 134444+            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")+          , ( 168000+            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")+          , ( 193000+            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")+          , ( 210000+            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")+          , ( 216116+            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")+          , ( 225430+            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")+          , ( 250000+            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")+          , ( 279000+            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")+          , ( 295000+            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")+            -- UAHF fork block.+          , ( 478559+            , "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec")+            -- Nov, 13 DAA activation block.+          , ( 504031+            , "0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c")+          ]+    , getSeeds =+          [ "seed.bitcoinabc.org"+          , "seed-abc.bitcoinforks.org"+          , "btccash-seeder.bitcoinunlimited.info"+          , "seed.bitprim.org"+          , "seed.deadalnix.me"+          , "seeder.criptolayer.net"+          ]+    , getBip44Coin = 145+    , getSigHashForkId = Just 0+    , getEdaBlockHeight = Just 478559+    , getDaaBlockHeight = Just 404031+    , getSegWit = False+    , getCashAddrPrefix = Just "bitcoincash"+    , getBech32Prefix = Nothing+    , getReplaceByFee = False+    , getHalvingInterval = 210000+    }++-- | Testnet for Bitcoin Cash network.+bchTest :: Network+bchTest =+    Network+    { getNetworkName = "bchtest"+    , getNetworkIdent = "bchTest"+    , getAddrPrefix = 111+    , getScriptPrefix = 196+    , getSecretPrefix = 239+    , getExtPubKeyPrefix = 0x043587cf+    , getExtSecretPrefix = 0x04358394+    , getNetworkMagic = 0xf4e5f3f4+    , getGenesisHeader =+          BlockHeader+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1296688602+              486604799+              414098458+            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+    , getMaxBlockSize = 32000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent = "/haskoin-bch-test:" <> versionString <> "/"+    , getDefaultPort = 18333+    , getAllowMinDifficultyBlocks = True+    , getPowNoRetargetting = False+    , getPowLimit =+          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 21111+          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")+    , getBip65Height = 581885+    , getBip66Height = 330776+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints =+          [ ( 546+            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")+            -- UAHF fork block.+          , ( 1155876+            , "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5")+            -- Nov, 13. DAA activation block.+          , ( 1188697+            , "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb")+          ]+    , getSeeds =+          [ "testnet-seed.bitcoinabc.org"+          , "testnet-seed-abc.bitcoinforks.org"+          , "testnet-seed.bitprim.org"+          , "testnet-seed.deadalnix.me"+          , "testnet-seeder.criptolayer.net"+          ]+    , getBip44Coin = 1+    , getSigHashForkId = Just 0+    , getEdaBlockHeight = Just 1155876+    , getDaaBlockHeight = Just 1188697+    , getSegWit = False+    , getCashAddrPrefix = Just "bchtest"+    , getBech32Prefix = Nothing+    , getReplaceByFee = False+    , getHalvingInterval = 210000+    }++-- | RegTest for Bitcoin Cash network.+bchRegTest :: Network+bchRegTest =+    Network+    { getNetworkName = "bchreg"+    , getNetworkIdent = "bchRegTest"+    , getAddrPrefix = 111+    , getScriptPrefix = 196+    , getSecretPrefix = 239+    , getExtPubKeyPrefix = 0x043587cf+    , getExtSecretPrefix = 0x04358394+    , getNetworkMagic = 0xdab5bffa+    , getGenesisHeader =+          BlockHeader+           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206+              0x01+              "0000000000000000000000000000000000000000000000000000000000000000"+              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"+              1296688602+              0x207fffff+              2+    , getMaxBlockSize = 1000000+    , getMaxSatoshi = 2100000000000000+    , getHaskoinUserAgent = "/haskoin-bch-regtest:" <> versionString <> "/"+    , getDefaultPort = 18444+    , getAllowMinDifficultyBlocks = True+    , getPowNoRetargetting = True+    , getPowLimit =+          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff+    , getBip34Block =+          ( 100000000+          , "0000000000000000000000000000000000000000000000000000000000000000")+    , getBip65Height = 1351+    , getBip66Height = 1251+    , getTargetTimespan = 14 * 24 * 60 * 60+    , getTargetSpacing = 10 * 60+    , getCheckpoints =+          [ ( 0+            , "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")+          ]+    , getSeeds = ["localhost"]+    , getBip44Coin = 1+    , getSigHashForkId = Just 0+    , getEdaBlockHeight = Nothing+    , getDaaBlockHeight = Just 0+    , getSegWit = False+    , getCashAddrPrefix = Just "bchreg"+    , getBech32Prefix = Nothing+    , getReplaceByFee = False+    , getHalvingInterval = 150+    }++-- | List of all networks supported by this library.+allNets :: [Network]+allNets = [btc, bch, btcTest, bchTest, btcRegTest, bchRegTest]
+ src/Haskoin/Crypto.hs view
@@ -0,0 +1,22 @@+{-|+Module      : Haskoin.Crypto+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Hashing functions and ECDSA signatures.+-}+module Haskoin.Crypto+    ( -- * Hashes+      module Hash+      -- * Signatures+    , module Signature+      -- * Secp256k1 (re-exported)+    , module Secp256k1+    ) where++import           Crypto.Secp256k1         as Secp256k1+import           Haskoin.Crypto.Hash      as Hash+import           Haskoin.Crypto.Signature as Signature
+ src/Haskoin/Crypto/Hash.hs view
@@ -0,0 +1,202 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}+{-|+Module      : Haskoin.Crypto.Hash+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Hashing functions and corresponding data types. Uses functions from the+cryptonite library.+-}+module Haskoin.Crypto.Hash+    ( -- * Hashes+      Hash512(getHash512)+    , Hash256(getHash256)+    , Hash160(getHash160)+    , CheckSum32(getCheckSum32)+    , sha512+    , sha256+    , ripemd160+    , sha1+    , doubleSHA256+    , addressHash+    , checkSum32+    , hmac512+    , hmac256+    , split512+    , join512+    ) where++import           Control.DeepSeq+import           Crypto.Hash             (RIPEMD160 (..), SHA1 (..),+                                          SHA256 (..), SHA512 (..), hashWith)+import           Crypto.MAC.HMAC         (HMAC, hmac)+import           Data.ByteArray          (ByteArrayAccess)+import qualified Data.ByteArray          as BA+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import           Data.ByteString.Short   (ShortByteString)+import qualified Data.ByteString.Short   as BSS+import           Data.Either             (fromRight)+import           Data.Hashable           (Hashable)+import           Data.Serialize          (Serialize (..), decode)+import qualified Data.Serialize.Get      as Get+import qualified Data.Serialize.Put      as Put+import           Data.String             (IsString, fromString)+import           Data.String.Conversions (cs)+import           Data.Word               (Word32)+import           GHC.Generics            (Generic)+import           Haskoin.Util+import           Text.Read               as R++-- | 'Word32' wrapped for type-safe 32-bit checksums.+newtype CheckSum32 = CheckSum32+    { getCheckSum32 :: Word32+    } deriving (Eq, Ord, Serialize, Show, Read, Hashable, Generic, NFData)++-- | Type for 512-bit hashes.+newtype Hash512 = Hash512 { getHash512 :: ShortByteString }+    deriving (Eq, Ord, Hashable, Generic, NFData)++-- | Type for 256-bit hashes.+newtype Hash256 = Hash256 { getHash256 :: ShortByteString }+    deriving (Eq, Ord, Hashable, Generic, NFData)++-- | Type for 160-bit hashes.+newtype Hash160 = Hash160 { getHash160 :: ShortByteString }+    deriving (Eq, Ord, Hashable, Generic, NFData)++instance Show Hash512 where+    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash512++instance Read Hash512 where+    readPrec = do+        R.String str <- lexP+        maybe pfail return $ Hash512 . BSS.toShort <$> decodeHex (cs str)++instance Show Hash256 where+    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash256++instance Read Hash256 where+    readPrec = do+        R.String str <- lexP+        maybe pfail return $ Hash256 . BSS.toShort <$> decodeHex (cs str)++instance Show Hash160 where+    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash160++instance Read Hash160 where+    readPrec = do+        R.String str <- lexP+        maybe pfail return $ Hash160 . BSS.toShort <$> decodeHex (cs str)++instance IsString Hash512 where+    fromString str =+        case decodeHex $ cs str of+            Nothing -> e+            Just bs ->+                case BS.length bs of+                    64 -> Hash512 (BSS.toShort bs)+                    _  -> e+      where+        e = error "Could not decode hash from hex string"++instance Serialize Hash512 where+    get = Hash512 <$> Get.getShortByteString 64+    put = Put.putShortByteString . getHash512++instance IsString Hash256 where+    fromString str =+        case decodeHex $ cs str of+            Nothing -> e+            Just bs ->+                case BS.length bs of+                    32 -> Hash256 (BSS.toShort bs)+                    _  -> e+      where+        e = error "Could not decode hash from hex string"++instance Serialize Hash256 where+    get = Hash256 <$> Get.getShortByteString 32+    put = Put.putShortByteString . getHash256++instance IsString Hash160 where+    fromString str =+        case decodeHex $ cs str of+            Nothing -> e+            Just bs ->+                case BS.length bs of+                    20 -> Hash160 (BSS.toShort bs)+                    _  -> e+      where+        e = error "Could not decode hash from hex string"++instance Serialize Hash160 where+    get = Hash160 <$> Get.getShortByteString 20+    put = Put.putShortByteString . getHash160++-- | Calculate SHA512 hash.+sha512 :: ByteArrayAccess b => b -> Hash512+sha512 = Hash512 . BSS.toShort . BA.convert . hashWith SHA512++-- | Calculate SHA256 hash.+sha256 :: ByteArrayAccess b => b -> Hash256+sha256 = Hash256 . BSS.toShort . BA.convert . hashWith SHA256++-- | Calculate RIPEMD160 hash.+ripemd160 :: ByteArrayAccess b => b -> Hash160+ripemd160 = Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160++-- | Claculate SHA1 hash.+sha1 :: ByteArrayAccess b => b -> Hash160+sha1 = Hash160 . BSS.toShort . BA.convert . hashWith SHA1++-- | Compute two rounds of SHA-256.+doubleSHA256 :: ByteArrayAccess b => b -> Hash256+doubleSHA256 =+    Hash256 . BSS.toShort . BA.convert . hashWith SHA256 . hashWith SHA256++-- | Compute SHA-256 followed by RIPMED-160.+addressHash :: ByteArrayAccess b => b -> Hash160+addressHash =+    Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160 . hashWith SHA256++{- CheckSum -}++-- | Computes a 32 bit checksum.+checkSum32 :: ByteArrayAccess b => b -> CheckSum32+checkSum32 = fromRight (error "Could not decode bytes as CheckSum32")+             . decode+             . BS.take 4+             . BA.convert+             . hashWith SHA256+             . hashWith SHA256++{- HMAC -}++-- | Computes HMAC over SHA-512.+hmac512 :: ByteString -> ByteString -> Hash512+hmac512 key msg =+    Hash512 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA512)++-- | Computes HMAC over SHA-256.+hmac256 :: (ByteArrayAccess k, ByteArrayAccess m) => k -> m -> Hash256+hmac256 key msg =+    Hash256 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA256)++-- | Split a 'Hash512' into a pair of 'Hash256'.+split512 :: Hash512 -> (Hash256, Hash256)+split512 h =+    (Hash256 (BSS.toShort a), Hash256 (BSS.toShort b))+  where+    (a, b) = BS.splitAt 32 . BSS.fromShort $ getHash512 h++-- | Join a pair of 'Hash256' into a 'Hash512'.+join512 :: (Hash256, Hash256) -> Hash512+join512 (a, b) =+    Hash512 .+    BSS.toShort $+        BSS.fromShort (getHash256 a) `BS.append` BSS.fromShort (getHash256 b)
+ src/Haskoin/Crypto/Signature.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Crypto.Signature+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++ECDSA signatures using secp256k1 curve. Uses functions from upstream secp256k1+library.+-}+module Haskoin.Crypto.Signature+    ( putSig+    , getSig+    , signHash+    , verifyHashSig+    , isCanonicalHalfOrder+    , decodeStrictSig+    , exportSig+    ) where++import           Control.Monad         (guard, unless, when)+import           Crypto.Secp256k1+import           Data.ByteString       (ByteString)+import qualified Data.ByteString       as BS+import           Data.ByteString.Short (toShort)+import           Data.Maybe            (fromMaybe)+import           Data.Serialize        as S+import           Data.Serialize.Put    (Putter, putByteString)+import           Haskoin.Crypto.Hash+import           Numeric               (showHex)++-- | Convert 256-bit hash into a 'Msg' for signing or verification.+hashToMsg :: Hash256 -> Msg+hashToMsg =+    fromMaybe e . msg . encode+  where+    e = error "Could not convert 32-byte hash to secp256k1 message"++-- | Sign a 256-bit hash using secp256k1 elliptic curve.+signHash :: SecKey -> Hash256 -> Sig+signHash k = signMsg k . hashToMsg++-- | Verify an ECDSA signature for a 256-bit hash.+verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool+verifyHashSig h s p =+    verifySig p g m+  where+    (g, _) = normalizeSig s+    m = hashToMsg h++-- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.+getSig :: Get Sig+getSig = do+        l <- lookAhead $ do+            t <- getWord8+            -- 0x30 is DER sequence type+            unless (t == 0x30) $ fail $+                "Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"+            l <- getWord8+            when (l == 0x00) $ fail "Indeterminate form unsupported"+            when (l >= 0x80) $ fail "Multi-octect length not supported"+            return $ fromIntegral l+        bs <- getByteString $ l + 2+        case decodeStrictSig bs of+            Just s  -> return s+            Nothing -> fail "Invalid signature"++-- | Serialize an ECDSA signature for Bitcoin use.+putSig :: Putter Sig+putSig s = putByteString $ exportSig s++-- | Is canonical half order.+isCanonicalHalfOrder :: Sig -> Bool+isCanonicalHalfOrder = not . snd . normalizeSig++-- | Decode signature strictly.+decodeStrictSig :: ByteString -> Maybe Sig+decodeStrictSig bs = do+    g <- importSig bs+    let compact = exportCompactSig g+    -- <http://www.secg.org/sec1-v2.pdf Section 4.1.4>+    -- 4.1.4.1 (r and s can not be zero)+    guard $ getCompactSigR compact /= zero+    guard $ getCompactSigS compact /= zero+    guard $ isCanonicalHalfOrder g+    return g+  where+    zero = toShort $ BS.replicate 32 0
+ src/Haskoin/Keys.hs view
@@ -0,0 +1,22 @@+{-|+Module      : Haskoin.Keys+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++ECDSA private and public keys, extended keys (BIP-32) and mnemonic sentences+(BIP-39).+-}+module Haskoin.Keys+    ( module Haskoin.Keys.Common+    -- * Extended Keys+    , module Haskoin.Keys.Extended+    -- * Mnemonic+    , module Haskoin.Keys.Mnemonic+    ) where++import           Haskoin.Keys.Common+import           Haskoin.Keys.Extended+import           Haskoin.Keys.Mnemonic
+ src/Haskoin/Keys/Common.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Keys.Common+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++ECDSA private and public key functions.+-}+module Haskoin.Keys.Common+    ( -- * Public & Private Keys+      PubKeyI(..)+    , SecKeyI(..)+    , exportPubKey+    , importPubKey+    , wrapPubKey+    , derivePubKeyI+    , wrapSecKey+    , fromMiniKey+    , tweakPubKey+    , tweakSecKey+    , secKeyPut+    , secKeyGet+    , getSecKey+    , secKey+    ) where++import           Control.Applicative     ((<|>))+import           Control.DeepSeq+import           Control.Monad           (guard, mzero, (<=<))+import           Crypto.Secp256k1+import           Data.Aeson              (FromJSON, ToJSON (..), Value (String),+                                          parseJSON, withText)+import           Data.Aeson.Encoding     (unsafeToEncoding)+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import           Data.ByteString.Builder (char7)+import           Data.Hashable+import           Data.Maybe              (fromMaybe)+import           Data.Serialize          (Serialize, decode, encode, get, put)+import           Data.Serialize.Get      (Get, getByteString)+import           Data.Serialize.Put      (Putter, putByteString)+import           Data.String             (IsString, fromString)+import           Data.String.Conversions (cs)+import           GHC.Generics            (Generic)+import           Haskoin.Crypto.Hash+import           Haskoin.Util++-- | Elliptic curve public key type with expected serialized compression flag.+data PubKeyI = PubKeyI+    { pubKeyPoint      :: !PubKey+    , pubKeyCompressed :: !Bool+    } deriving (Generic, Eq, Show, Read, Hashable, NFData)++instance IsString PubKeyI where+    fromString str =+        fromMaybe e $ eitherToMaybe . decode <=< decodeHex $ cs str+      where+        e = error "Could not decode public key"++instance ToJSON PubKeyI where+    toJSON = String . encodeHex . encode+    toEncoding s = unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'++instance FromJSON PubKeyI where+    parseJSON = withText "PubKeyI" $+        maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex++instance Serialize PubKeyI where+    get = c <|> u+      where+        c = do+            bs <- getByteString 33+            guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]+            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True+        u = do+            bs <- getByteString 65+            guard $ BS.head bs == 0x04+            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False++    put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)++-- | Wrap a public key from secp256k1 library adding information about compression.+wrapPubKey :: Bool -> PubKey -> PubKeyI+wrapPubKey c p = PubKeyI p c++-- | Derives a public key from a private key. This function will preserve+-- compression flag.+derivePubKeyI :: SecKeyI -> PubKeyI+derivePubKeyI (SecKeyI d c) = PubKeyI (derivePubKey d) c++-- | Tweak a public key.+tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey+tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)++-- | Elliptic curve private key type with expected public key compression+-- information. Compression information is stored in private key WIF formats and+-- needs to be preserved to generate the correct address from the corresponding+-- public key.+data SecKeyI = SecKeyI+    { secKeyData       :: !SecKey+    , secKeyCompressed :: !Bool+    } deriving (Eq, Show, Read, Generic, NFData)++-- | Wrap private key with corresponding public key compression flag.+wrapSecKey :: Bool -> SecKey -> SecKeyI+wrapSecKey c d = SecKeyI d c++-- | Deserialize 'SecKey'.+secKeyGet :: Get SecKey+secKeyGet = do+    bs <- getByteString 32+    maybe (fail "invalid private key") return (secKey bs)++-- | Serialize 'SecKey'.+secKeyPut :: Putter SecKey+secKeyPut = putByteString . getSecKey++-- | Decode Casascius mini private keys (22 or 30 characters).+fromMiniKey :: ByteString -> Maybe SecKeyI+fromMiniKey bs = do+    guard checkShortKey+    wrapSecKey False <$> secKey (encode (sha256 bs))+  where+    checkHash = encode $ sha256 $ bs `BS.append` "?"+    checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00++-- | Tweak a private key.+tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey+tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)
+ src/Haskoin/Keys/Extended.hs view
@@ -0,0 +1,918 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Keys.Extended+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++BIP-32 extended keys.+-}+module Haskoin.Keys.Extended+    (+      -- * Extended Keys+      XPubKey(..)+    , XPrvKey(..)+    , ChainCode+    , KeyIndex+    , Fingerprint+    , DerivationException(..)+    , makeXPrvKey+    , deriveXPubKey+    , prvSubKey+    , pubSubKey+    , hardSubKey+    , xPrvIsHard+    , xPubIsHard+    , xPrvChild+    , xPubChild+    , xPubID+    , xPrvID+    , xPubFP+    , xPrvFP+    , xPubAddr+    , xPubWitnessAddr+    , xPubCompatWitnessAddr+    , xPubExport+    , xPubToJSON+    , xPubToEncoding+    , xPubFromJSON+    , xPrvExport+    , xPrvToJSON+    , xPrvToEncoding+    , xPrvFromJSON+    , xPubImport+    , xPrvImport+    , xPrvWif+    , putXPrvKey+    , putXPubKey+    , getXPrvKey+    , getXPubKey++      -- ** Helper Functions+    , prvSubKeys+    , pubSubKeys+    , hardSubKeys+    , deriveAddr+    , deriveWitnessAddr+    , deriveCompatWitnessAddr+    , deriveAddrs+    , deriveWitnessAddrs+    , deriveCompatWitnessAddrs+    , deriveMSAddr+    , deriveMSAddrs+    , cycleIndex++      -- ** Derivation Paths+    , DerivPathI(..)+    , AnyDeriv, HardDeriv, SoftDeriv+    , HardOrAny+    , AnyOrSoft+    , DerivPath+    , HardPath+    , SoftPath+    , Bip32PathIndex (..)+    , derivePath+    , derivePubPath+    , toHard+    , toSoft+    , toGeneric+    , (++/)+    , pathToStr+    , listToPath+    , pathToList++      -- *** Derivation Path Parser+    , XKey(..)+    , ParsedPath(..)+    , parsePath+    , parseHard+    , parseSoft+    , applyPath+    , derivePathAddr+    , derivePathAddrs+    , derivePathMSAddr+    , derivePathMSAddrs+    , concatBip32Segments+    ) where++import           Control.Applicative+import           Control.DeepSeq+import           Control.Exception       (Exception, throw)+import           Control.Monad           (guard, mzero, unless, (<=<))+import           Crypto.Secp256k1+import           Data.Aeson              as A (FromJSON, ToJSON (..),+                                               Value (String), parseJSON,+                                               toJSON, withText)+import           Data.Aeson.Encoding     (Encoding, text)+import           Data.Aeson.Types        (Parser)+import           Data.Bits               (clearBit, setBit, testBit)+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as B+import           Data.Either             (fromRight)+import           Data.List               (foldl')+import           Data.List.Split         (splitOn)+import           Data.Maybe              (fromMaybe)+import           Data.Serialize          as S (Serialize, decode, encode, get,+                                               put)+import           Data.Serialize.Get      (Get, getWord32be, getWord8, runGet)+import           Data.Serialize.Put      (Putter, putWord32be, putWord8, runPut)+import           Data.String             (IsString, fromString)+import           Data.String.Conversions (cs)+import           Data.Typeable           (Typeable)+import           Data.Word               (Word32, Word8)+import           GHC.Generics            (Generic)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Crypto.Hash+import           Haskoin.Keys.Common+import           Haskoin.Script+import           Haskoin.Util+import           Text.Read               as R+import           Text.Read.Lex+++-- | A derivation exception is thrown in the very unlikely event that a+-- derivation is invalid.+newtype DerivationException = DerivationException String+    deriving (Eq, Read, Show, Typeable, Generic, NFData)++instance Exception DerivationException++-- | Chain code as specified in BIP-32.+type ChainCode = Hash256++-- | Index of key as specified in BIP-32.+type KeyIndex = Word32++-- | Fingerprint of parent+type Fingerprint = Word32++-- | Data type representing an extended BIP32 private key. An extended key+-- is a node in a tree of key derivations. It has a depth in the tree, a+-- parent node and an index to differentiate it from other siblings.+data XPrvKey = XPrvKey+    { xPrvDepth  :: !Word8       -- ^ depth in the tree+    , xPrvParent :: !Fingerprint -- ^ fingerprint of parent+    , xPrvIndex  :: !KeyIndex    -- ^ derivation index+    , xPrvChain  :: !ChainCode   -- ^ chain code+    , xPrvKey    :: !SecKey      -- ^ private key of this node+    } deriving (Generic, Eq, Show, Read, NFData)++xPrvToJSON :: Network -> XPrvKey -> Value+xPrvToJSON net = A.String . xPrvExport net++xPrvToEncoding :: Network -> XPrvKey -> Encoding+xPrvToEncoding net = text . xPrvExport net++-- | Data type representing an extended BIP32 public key.+data XPubKey = XPubKey+    { xPubDepth  :: !Word8     -- ^ depth in the tree+    , xPubParent :: !Fingerprint    -- ^ fingerprint of parent+    , xPubIndex  :: !KeyIndex  -- ^ derivation index+    , xPubChain  :: !ChainCode -- ^ chain code+    , xPubKey    :: !PubKey    -- ^ public key of this node+    } deriving (Generic, Eq, Show, Read, NFData)+++-- | Decode an extended public key from a JSON string+xPubFromJSON :: Network -> Value -> Parser XPubKey+xPubFromJSON net =+    withText "xpub" $ \t ->+        case xPubImport net t of+            Nothing -> fail "could not read xpub"+            Just x  -> return x++-- | Get JSON 'Value' from 'XPubKey'.+xPubToJSON :: Network -> XPubKey -> Value+xPubToJSON net = A.String . xPubExport net++xPubToEncoding :: Network -> XPubKey -> Encoding+xPubToEncoding net = text . xPubExport net++-- | Decode an extended private key from a JSON string+xPrvFromJSON :: Network -> Value -> Parser XPrvKey+xPrvFromJSON net =+    withText "xprv" $ \t ->+        case xPrvImport net t of+            Nothing -> fail "could not read xprv"+            Just x  -> return x++-- | Build a BIP32 compatible extended private key from a bytestring. This will+-- produce a root node (@depth=0@ and @parent=0@).+makeXPrvKey :: ByteString -> XPrvKey+makeXPrvKey bs =+    XPrvKey 0 0 0 c k+  where+    (p, c) = split512 $ hmac512 "Bitcoin seed" bs+    k     = fromMaybe err (secKey (encode p))+    err   = throw $ DerivationException "Invalid seed"++-- | Derive an extended public key from an extended private key. This function+-- will preserve the depth, parent, index and chaincode fields of the extended+-- private keys.+deriveXPubKey :: XPrvKey -> XPubKey+deriveXPubKey (XPrvKey d p i c k) = XPubKey d p i c (derivePubKey k)++-- | Compute a private, soft child key derivation. A private soft derivation+-- will allow the equivalent extended public key to derive the public key for+-- this child. Given a parent key /m/ and a derivation index /i/, this function+-- will compute /m\/i/.+--+-- Soft derivations allow for more flexibility such as read-only wallets.+-- However, care must be taken not the leak both the parent extended public key+-- and one of the extended child private keys as this would compromise the+-- extended parent private key.+prvSubKey :: XPrvKey  -- ^ extended parent private key+          -> KeyIndex -- ^ child derivation index+          -> XPrvKey  -- ^ extended child private key+prvSubKey xkey child+    | child >= 0 && child < 0x80000000 =+        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) child c k+    | otherwise = error "Invalid child derivation index"+  where+    pK = xPubKey $ deriveXPubKey xkey+    m = B.append (exportPubKey True pK) (encode child)+    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m+    k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a+    err = throw $ DerivationException "Invalid prvSubKey derivation"++-- | Compute a public, soft child key derivation. Given a parent key /M/+-- and a derivation index /i/, this function will compute /M\/i/.+pubSubKey :: XPubKey  -- ^ extended parent public key+          -> KeyIndex -- ^ child derivation index+          -> XPubKey  -- ^ extended child public key+pubSubKey xKey child+    | child >= 0 && child < 0x80000000 =+        XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK+    | otherwise = error "Invalid child derivation index"+  where+    m      = B.append (exportPubKey True (xPubKey xKey)) (encode child)+    (a, c) = split512 $ hmac512 (encode $ xPubChain xKey) m+    pK     = fromMaybe err $ tweakPubKey (xPubKey xKey) a+    err    = throw $ DerivationException "Invalid pubSubKey derivation"++-- | Compute a hard child key derivation. Hard derivations can only be computed+-- for private keys. Hard derivations do not allow the parent public key to+-- derive the child public keys. However, they are safer as a breach of the+-- parent public key and child private keys does not lead to a breach of the+-- parent private key. Given a parent key /m/ and a derivation index /i/, this+-- function will compute /m\/i'/.+hardSubKey :: XPrvKey  -- ^ extended parent private key+           -> KeyIndex -- ^ child derivation index+           -> XPrvKey  -- ^ extended child private key+hardSubKey xkey child+    | child >= 0 && child < 0x80000000 =+        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) i c k+    | otherwise = error "Invalid child derivation index"+  where+    i      = setBit child 31+    m      = B.append (bsPadPrvKey $ xPrvKey xkey) (encode i)+    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m+    k      = fromMaybe err $ tweakSecKey (xPrvKey xkey) a+    err    = throw $ DerivationException "Invalid hardSubKey derivation"++-- | Returns true if the extended private key was derived through a hard+-- derivation.+xPrvIsHard :: XPrvKey -> Bool+xPrvIsHard k = testBit (xPrvIndex k) 31++-- | Returns true if the extended public key was derived through a hard+-- derivation.+xPubIsHard :: XPubKey -> Bool+xPubIsHard k = testBit (xPubIndex k) 31++-- | Returns the derivation index of this extended private key without the hard+-- bit set.+xPrvChild :: XPrvKey -> KeyIndex+xPrvChild k = clearBit (xPrvIndex k) 31++-- | Returns the derivation index of this extended public key without the hard+-- bit set.+xPubChild :: XPubKey -> KeyIndex+xPubChild k = clearBit (xPubIndex k) 31++-- | Computes the key identifier of an extended private key.+xPrvID :: XPrvKey -> Hash160+xPrvID = xPubID . deriveXPubKey++-- | Computes the key identifier of an extended public key.+xPubID :: XPubKey -> Hash160+xPubID = ripemd160 . encode . sha256 . exportPubKey True . xPubKey++-- | Computes the key fingerprint of an extended private key.+xPrvFP :: XPrvKey -> Fingerprint+xPrvFP =+    fromRight err . decode . B.take 4 . encode . xPrvID+  where+    err = error "Could not decode xPrvFP"++-- | Computes the key fingerprint of an extended public key.+xPubFP :: XPubKey -> Fingerprint+xPubFP =+    fromRight err . decode . B.take 4 . encode . xPubID+  where+    err = error "Could not decode xPubFP"++-- | Compute a standard P2PKH address for an extended public key.+xPubAddr :: XPubKey -> Address+xPubAddr xkey = pubKeyAddr (wrapPubKey True (xPubKey xkey))++-- | Compute a SegWit P2WPKH address for an extended public key.+xPubWitnessAddr :: XPubKey -> Address+xPubWitnessAddr xkey = pubKeyWitnessAddr (wrapPubKey True (xPubKey xkey))++-- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended+-- public key.+xPubCompatWitnessAddr :: XPubKey -> Address+xPubCompatWitnessAddr xkey =+    pubKeyCompatWitnessAddr (wrapPubKey True (xPubKey xkey))++-- | Exports an extended private key to the BIP32 key export format ('Base58').+xPrvExport :: Network -> XPrvKey -> Base58+xPrvExport net = encodeBase58Check . runPut . putXPrvKey net++-- | Exports an extended public key to the BIP32 key export format ('Base58').+xPubExport :: Network -> XPubKey -> Base58+xPubExport net = encodeBase58Check . runPut . putXPubKey net++-- | Decodes a BIP32 encoded extended private key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPrvImport :: Network -> Base58 -> Maybe XPrvKey+xPrvImport net = eitherToMaybe . runGet (getXPrvKey net) <=< decodeBase58Check++-- | Decodes a BIP32 encoded extended public key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPubImport :: Network -> Base58 -> Maybe XPubKey+xPubImport net = eitherToMaybe . runGet (getXPubKey net) <=< decodeBase58Check++-- | Export an extended private key to WIF (Wallet Import Format).+xPrvWif :: Network -> XPrvKey -> Base58+xPrvWif net xkey = toWif net (wrapSecKey True (xPrvKey xkey))++-- | Parse a binary extended private key.+getXPrvKey :: Network -> Get XPrvKey+getXPrvKey net = do+        ver <- getWord32be+        unless (ver == getExtSecretPrefix net) $ fail+            "Get: Invalid version for extended private key"+        XPrvKey <$> getWord8+                <*> getWord32be+                <*> getWord32be+                <*> S.get+                <*> getPadPrvKey++-- | Serialize an extended private key.+putXPrvKey :: Network -> Putter XPrvKey+putXPrvKey net k = do+        putWord32be  $ getExtSecretPrefix net+        putWord8     $ xPrvDepth k+        putWord32be  $ xPrvParent k+        putWord32be  $ xPrvIndex k+        put          $ xPrvChain k+        putPadPrvKey $ xPrvKey k++-- | Parse a binary extended public key.+getXPubKey :: Network -> Get XPubKey+getXPubKey net = do+        ver <- getWord32be+        unless (ver == getExtPubKeyPrefix net) $ fail+            "Get: Invalid version for extended public key"+        XPubKey <$> getWord8+                <*> getWord32be+                <*> getWord32be+                <*> S.get+                <*> (pubKeyPoint <$> S.get)++-- | Serialize an extended public key.+putXPubKey :: Network -> Putter XPubKey+putXPubKey net k = do+        putWord32be $ getExtPubKeyPrefix net+        putWord8    $ xPubDepth k+        putWord32be $ xPubParent k+        putWord32be $ xPubIndex k+        put         $ xPubChain k+        put         $ wrapPubKey True (xPubKey k)++{- Derivation helpers -}++-- | Cyclic list of all private soft child key derivations of a parent key+-- starting from an offset index.+prvSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]+prvSubKeys k = map (\i -> (prvSubKey k i, i)) . cycleIndex++-- | Cyclic list of all public soft child key derivations of a parent key+-- starting from an offset index.+pubSubKeys :: XPubKey -> KeyIndex -> [(XPubKey, KeyIndex)]+pubSubKeys k = map (\i -> (pubSubKey k i, i)) . cycleIndex++-- | Cyclic list of all hard child key derivations of a parent key starting+-- from an offset index.+hardSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]+hardSubKeys k = map (\i -> (hardSubKey k i, i)) . cycleIndex++-- | Derive a standard address from an extended public key and an index.+deriveAddr :: XPubKey -> KeyIndex -> (Address, PubKey)+deriveAddr k i =+    (xPubAddr key, xPubKey key)+  where+    key = pubSubKey k i++-- | Derive a SegWit P2WPKH address from an extended public key and an index.+deriveWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)+deriveWitnessAddr k i =+    (xPubWitnessAddr key, xPubKey key)+  where+    key = pubSubKey k i++-- | Derive a backwards-compatible SegWit P2SH-P2WPKH address from an extended+-- public key and an index.+deriveCompatWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)+deriveCompatWitnessAddr k i =+    (xPubCompatWitnessAddr key, xPubKey key)+  where+    key = pubSubKey k i++-- | Cyclic list of all addresses derived from a public key starting from an+-- offset index.+deriveAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveAddrs k =+    map f . cycleIndex+  where+    f i = let (a, key) = deriveAddr k i in (a, key, i)++-- | Cyclic list of all SegWit P2WPKH addresses derived from a public key+-- starting from an offset index.+deriveWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveWitnessAddrs k =+    map f . cycleIndex+  where+    f i = let (a, key) = deriveWitnessAddr k i in (a, key, i)++-- | Cyclic list of all backwards-compatible SegWit P2SH-P2WPKH addresses+-- derived from a public key starting from an offset index.+deriveCompatWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]+deriveCompatWitnessAddrs k =+    map f . cycleIndex+  where+    f i = let (a, key) = deriveCompatWitnessAddr k i in (a, key, i)++-- | Derive a multisig address from a list of public keys, the number of+-- required signatures /m/ and a derivation index. The derivation type is a+-- public, soft derivation.+deriveMSAddr :: [XPubKey] -> Int -> KeyIndex -> (Address, RedeemScript)+deriveMSAddr keys m i = (payToScriptAddress rdm, rdm)+  where+    rdm = sortMulSig $ PayMulSig k m+    k = map (wrapPubKey True . xPubKey . flip pubSubKey i) keys++-- | Cyclic list of all multisig addresses derived from a list of public keys,+-- a number of required signatures /m/ and starting from an offset index. The+-- derivation type is a public, soft derivation.+deriveMSAddrs :: [XPubKey] -> Int -> KeyIndex+              -> [(Address, RedeemScript, KeyIndex)]+deriveMSAddrs keys m = map f . cycleIndex+  where+    f i =+        let (a, rdm) = deriveMSAddr keys m i+         in (a, rdm, i)++-- | Helper function to go through derivation indices.+cycleIndex :: KeyIndex -> [KeyIndex]+cycleIndex i+    | i == 0         = cycle [0..0x7fffffff]+    | i < 0x80000000 = cycle $ [i..0x7fffffff] ++ [0..(i-1)]+    | otherwise      = error $ "cycleIndex: invalid index " ++ show i++{- Derivation Paths -}++-- | Phantom type signaling a hardened derivation path that can only be computed+-- from private extended key.+data HardDeriv deriving (Generic, NFData)++-- | Phantom type signaling no knowledge about derivation path: can be hardened or not.+data AnyDeriv deriving (Generic, NFData)++-- | Phantom type signaling derivation path including only non-hardened paths+-- that can be computed from an extended public key.+data SoftDeriv deriving (Generic, NFData)++-- | Hardened derivation path. Can be computed from extended private key only.+type HardPath = DerivPathI HardDeriv++-- | Any derivation path.+type DerivPath = DerivPathI AnyDeriv++-- | Non-hardened derivation path can be computed from extended public key.+type SoftPath = DerivPathI SoftDeriv++-- | Helper class to perform validations on a hardened derivation path.+class HardOrAny a+instance HardOrAny HardDeriv+instance HardOrAny AnyDeriv++-- | Helper class to perform validations on a non-hardened derivation path.+class AnyOrSoft a+instance AnyOrSoft AnyDeriv+instance AnyOrSoft SoftDeriv++-- | Data type representing a derivation path. Two constructors are provided+-- for specifying soft or hard derivations. The path /\/0\/1'\/2/ for example can be+-- expressed as @'Deriv' :\/ 0 :| 1 :\/ 2@. The 'HardOrAny' and 'AnyOrSoft' type+-- classes are used to constrain the valid values for the phantom type /t/. If+-- you mix hard '(:|)' and soft '(:\/)' paths, the only valid type for /t/ is 'AnyDeriv'.+-- Otherwise, /t/ can be 'HardDeriv' if you only have hard derivation or 'SoftDeriv'+-- if you only have soft derivations.+--+-- Using this type is as easy as writing the required derivation like in these+-- example:+--+-- > Deriv :/ 0 :/ 1 :/ 2 :: SoftPath+-- > Deriv :| 0 :| 1 :| 2 :: HardPath+-- > Deriv :| 0 :/ 1 :/ 2 :: DerivPath+data DerivPathI t where+    (:|)  :: HardOrAny t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+    (:/)  :: AnyOrSoft t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t+    Deriv :: DerivPathI t++instance NFData (DerivPathI t) where+    rnf (a :| b) = rnf a `seq` rnf b `seq` ()+    rnf (a :/ b) = rnf a `seq` rnf b `seq` ()+    rnf Deriv    = ()++instance Eq (DerivPathI t) where+    (nextA :| iA) == (nextB :| iB) = iA == iB && nextA == nextB+    (nextA :/ iA) == (nextB :/ iB) = iA == iB && nextA == nextB+    Deriv         == Deriv         = True+    _             == _             = False++instance Ord (DerivPathI t) where+    -- Same hardness on each side+    (nextA :| iA) `compare` (nextB :| iB) =+        if nextA == nextB then iA `compare` iB else nextA `compare` nextB+    (nextA :/ iA) `compare` (nextB :/ iB) =+        if nextA == nextB then iA `compare` iB else nextA `compare` nextB++    -- Different hardness: hard paths are LT soft paths+    (nextA :/ _iA) `compare` (nextB :| _iB) =+        if nextA == nextB then LT else nextA `compare` nextB+    (nextA :| _iA) `compare` (nextB :/ _iB) =+        if nextA == nextB then GT else nextA `compare` nextB++    Deriv `compare` Deriv  = EQ+    Deriv `compare` _      = LT+    _     `compare` Deriv  = GT+++instance Serialize DerivPath where+    get = listToPath <$> S.get+    put = put . pathToList++-- | Get a list of derivation indices from a derivation path.+pathToList :: DerivPathI t -> [KeyIndex]+pathToList =+    reverse . go+  where+    go (next :| i) = setBit i 31 : go next+    go (next :/ i) = i : go next+    go _           = []++-- | Convert a list of derivation indices to a derivation path.+listToPath :: [KeyIndex] -> DerivPath+listToPath =+    go . reverse+  where+    go (i:is)+        | testBit i 31 = go is :| clearBit i 31+        | otherwise    = go is :/ i+    go [] = Deriv++-- | Convert a derivation path to a human-readable string.+pathToStr :: DerivPathI t -> String+pathToStr p =+    case p of+        next :| i -> concat [ pathToStr next, "/", show i, "'" ]+        next :/ i -> concat [ pathToStr next, "/", show i ]+        Deriv     -> ""++-- | Turn a derivation path into a hard derivation path. Will fail if the path+-- contains soft derivations.+toHard :: DerivPathI t -> Maybe HardPath+toHard p = case p of+    next :| i -> (:| i) <$> toHard next+    Deriv     -> Just Deriv+    _         -> Nothing++-- | Turn a derivation path into a soft derivation path. Will fail if the path+-- has hard derivations.+toSoft :: DerivPathI t -> Maybe SoftPath+toSoft p = case p of+    next :/ i -> (:/ i) <$> toSoft next+    Deriv     -> Just Deriv+    _         -> Nothing++-- | Make a derivation path generic.+toGeneric :: DerivPathI t -> DerivPath+toGeneric p = case p of+    next :/ i -> toGeneric next :/ i+    next :| i -> toGeneric next :| i+    Deriv     -> Deriv++-- | Append two derivation paths together. The result will be a mixed+-- derivation path.+(++/) :: DerivPathI t1 -> DerivPathI t2 -> DerivPath+(++/) p1 p2 =+    go id (toGeneric p2) $ toGeneric p1+  where+    go f p = case p of+        next :/ i -> go (f . (:/ i)) $ toGeneric next+        next :| i -> go (f . (:| i)) $ toGeneric next+        _         -> f++-- | Derive a private key from a derivation path+derivePath :: DerivPathI t -> XPrvKey -> XPrvKey+derivePath = go id+  where+    -- Build the full derivation function starting from the end+    go f p = case p of+        next :| i -> go (f . flip hardSubKey i) next+        next :/ i -> go (f . flip prvSubKey i) next+        _         -> f++-- | Derive a public key from a soft derivation path+derivePubPath :: SoftPath -> XPubKey -> XPubKey+derivePubPath = go id+  where+    -- Build the full derivation function starting from the end+    go f p = case p of+        next :/ i -> go (f . flip pubSubKey i) next+        _         -> f++instance Show DerivPath where+    showsPrec d p = showParen (d > 10) $+        showString "DerivPath " . shows (pathToStr p)++instance Read DerivPath where+    readPrec = parens $ do+        R.Ident "DerivPath" <- lexP+        R.String str <- lexP+        maybe pfail return $ getParsedPath <$> parsePath str++instance Show HardPath where+    showsPrec d p = showParen (d > 10) $+        showString "HardPath " . shows (pathToStr p)++instance Read HardPath where+    readPrec = parens $ do+        R.Ident "HardPath" <- lexP+        R.String str <- lexP+        maybe pfail return $ parseHard str++instance Show SoftPath where+    showsPrec d p = showParen (d > 10) $+        showString "SoftPath " . shows (pathToStr p)++instance Read SoftPath where+    readPrec = parens $ do+        R.Ident "SoftPath" <- lexP+        R.String str <- lexP+        maybe pfail return $ parseSoft str++instance IsString ParsedPath where+    fromString =+        fromMaybe e . parsePath+      where+        e = error "Could not parse derivation path"++instance IsString DerivPath where+    fromString =+        getParsedPath . fromMaybe e . parsePath+      where+        e = error "Could not parse derivation path"++instance IsString HardPath where+    fromString =+        fromMaybe e . parseHard+      where+        e = error "Could not parse hard derivation path"++instance IsString SoftPath where+    fromString =+        fromMaybe e . parseSoft+      where+        e = error "Could not parse soft derivation path"++instance FromJSON ParsedPath where+    parseJSON = withText "ParsedPath" $ \str -> case parsePath $ cs str of+        Just p -> return p+        _      -> mzero++instance FromJSON DerivPath where+    parseJSON = withText "DerivPath" $ \str -> case parsePath $ cs str of+        Just p -> return $ getParsedPath p+        _      -> mzero++instance FromJSON HardPath where+    parseJSON = withText "HardPath" $ \str -> case parseHard $ cs str of+        Just p -> return p+        _      -> mzero++instance FromJSON SoftPath where+    parseJSON = withText "SoftPath" $ \str -> case parseSoft $ cs str of+        Just p -> return p+        _      -> mzero++instance ToJSON (DerivPathI t) where+    toJSON = A.String . cs . pathToStr++instance ToJSON ParsedPath where+    toJSON (ParsedPrv p)   = A.String . cs . ("m" ++) . pathToStr $ p+    toJSON (ParsedPub p)   = A.String . cs . ("M" ++) . pathToStr $ p+    toJSON (ParsedEmpty p) = A.String . cs . ("" ++) . pathToStr $ p++{- Parsing derivation paths of the form m/1/2'/3 or M/1/2'/3 -}++-- | Type for parsing derivation paths of the form /m\/1\/2'\/3/ or+-- /M\/1\/2'\/3/.+data ParsedPath = ParsedPrv   { getParsedPath :: !DerivPath }+                | ParsedPub   { getParsedPath :: !DerivPath }+                | ParsedEmpty { getParsedPath :: !DerivPath }+  deriving (Eq, Generic, NFData)++instance Show ParsedPath where+    showsPrec d p = showParen (d > 10) $ showString "ParsedPath " . shows f+      where+        f =+            case p of+                ParsedPrv d'   -> "m" <> pathToStr d'+                ParsedPub d'   -> "M" <> pathToStr d'+                ParsedEmpty d' -> pathToStr d'++instance Read ParsedPath where+    readPrec = parens $ do+        R.Ident "ParsedPath" <- lexP+        R.String str <- lexP+        maybe pfail return $ parsePath str++-- | Parse derivation path string for extended key.+-- Forms: /m\/0'\/2/, /M\/2\/3\/4/.+parsePath :: String -> Maybe ParsedPath+parsePath str = do+    res <- concatBip32Segments <$> mapM parseBip32PathIndex xs+    case x of+        "m" -> Just $ ParsedPrv res+        "M" -> Just $ ParsedPub res+        ""  -> Just $ ParsedEmpty res+        _   -> Nothing+  where+    (x : xs) = splitOn "/" str++-- | Concatenate derivation path indices into a derivation path.+concatBip32Segments :: [Bip32PathIndex] -> DerivPath+concatBip32Segments = foldl' appendBip32Segment Deriv++-- | Append an extra derivation path index element into an existing path.+appendBip32Segment :: DerivPath -> Bip32PathIndex  -> DerivPath+appendBip32Segment d (Bip32SoftIndex i) = d :/ i+appendBip32Segment d (Bip32HardIndex i) = d :| i++-- | Parse a BIP32 derivation path index element from a string.+parseBip32PathIndex :: String -> Maybe Bip32PathIndex+parseBip32PathIndex segment = case reads segment of+    [(i, "" )] -> guard (is31Bit i) >> return (Bip32SoftIndex i)+    [(i, "'")] -> guard (is31Bit i) >> return (Bip32HardIndex i)+    _          -> Nothing++-- | Type for BIP32 path index element.+data Bip32PathIndex = Bip32HardIndex KeyIndex+                    | Bip32SoftIndex KeyIndex+  deriving (Eq, Generic, NFData)++instance Show Bip32PathIndex where+    showsPrec d (Bip32HardIndex i) = showParen (d > 10) $+        showString "Bip32HardIndex " . shows i+    showsPrec d (Bip32SoftIndex i) = showParen (d > 10) $+        showString "Bip32SoftIndex " . shows i++instance Read Bip32PathIndex where+    readPrec = h <|> s+      where+        h =+            parens $ do+                R.Ident "Bip32HardIndex" <- lexP+                R.Number n <- lexP+                maybe pfail return $+                    Bip32HardIndex . fromIntegral <$> numberToInteger n+        s =+            parens $ do+                R.Ident "Bip32SoftIndex" <- lexP+                R.Number n <- lexP+                maybe pfail return $+                    Bip32SoftIndex . fromIntegral <$> numberToInteger n++-- | Test whether the number could be a valid BIP32 derivation index.+is31Bit :: (Integral a) => a -> Bool+is31Bit i = i >= 0 && i < 0x80000000++-- | Helper function to parse a hard path.+parseHard :: String -> Maybe HardPath+parseHard = toHard . getParsedPath <=< parsePath++-- | Helper function to parse a soft path.+parseSoft :: String -> Maybe SoftPath+parseSoft = toSoft . getParsedPath <=< parsePath++-- | Data type representing a private or public key with its respective network.+data XKey+    = XPrv { getXKeyPrv :: !XPrvKey+           , getXKeyNet :: !Network }+    | XPub { getXKeyPub :: !XPubKey+           , getXKeyNet :: !Network }+    deriving (Eq, Show, Generic, NFData)++-- | Apply a parsed path to an extended key to derive the new key defined in the+-- path. If the path starts with /m/, a private key will be returned and if the+-- path starts with /M/, a public key will be returned. Private derivations on a+-- public key, and public derivations with a hard segment, return an error+-- value.+applyPath :: ParsedPath -> XKey -> Either String XKey+applyPath path key =+    case (path, key) of+        (ParsedPrv _, XPrv k n) -> return $ XPrv (derivPrvF k) n+        (ParsedPrv _, XPub {}) -> Left "applyPath: Invalid public key"+        (ParsedPub _, XPrv k n) -> return $ XPub (deriveXPubKey (derivPrvF k)) n+        (ParsedPub _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n+    -- For empty parsed paths, we take a hint from the provided key+        (ParsedEmpty _, XPrv k n) -> return $ XPrv (derivPrvF k) n+        (ParsedEmpty _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n+  where+    derivPrvF = goPrv id $ getParsedPath path+    derivPubFE = goPubE id $ getParsedPath path+    -- Build the full private derivation function starting from the end+    goPrv f p =+        case p of+            next :| i -> goPrv (f . flip hardSubKey i) next+            next :/ i -> goPrv (f . flip prvSubKey i) next+            Deriv     -> f+    -- Build the full public derivation function starting from the end+    goPubE f p =+        case p of+            next :/ i -> goPubE (f . flip pubSubKey i) next+            Deriv     -> Right f+            _         -> Left "applyPath: Invalid hard derivation"++{- Helpers for derivation paths and addresses -}++-- | Derive an address from a given parent path.+derivePathAddr :: XPubKey -> SoftPath -> KeyIndex -> (Address, PubKey)+derivePathAddr key path = deriveAddr (derivePubPath path key)++-- | Cyclic list of all addresses derived from a given parent path and starting+-- from the given offset index.+derivePathAddrs ::+       XPubKey -> SoftPath -> KeyIndex -> [(Address, PubKey, KeyIndex)]+derivePathAddrs key path = deriveAddrs (derivePubPath path key)++-- | Derive a multisig address from a given parent path. The number of required+-- signatures (m in m of n) is also needed.+derivePathMSAddr ::+       [XPubKey]+    -> SoftPath+    -> Int+    -> KeyIndex+    -> (Address, RedeemScript)+derivePathMSAddr keys path =+    deriveMSAddr $ map (derivePubPath path) keys++-- | Cyclic list of all multisig addresses derived from a given parent path and+-- starting from the given offset index. The number of required signatures+-- (m in m of n) is also needed.+derivePathMSAddrs ::+       [XPubKey]+    -> SoftPath+    -> Int+    -> KeyIndex+    -> [(Address, RedeemScript, KeyIndex)]+derivePathMSAddrs keys path =+    deriveMSAddrs $ map (derivePubPath path) keys++{- Utilities for extended keys -}++-- | De-serialize HDW-specific private key.+getPadPrvKey :: Get SecKey+getPadPrvKey = do+    pad <- getWord8+    unless (pad == 0x00) $ fail "Private key must be padded with 0x00"+    secKeyGet++-- | Serialize HDW-specific private key.+putPadPrvKey :: Putter SecKey+putPadPrvKey p = putWord8 0x00 >> secKeyPut p++bsPadPrvKey :: SecKey -> ByteString+bsPadPrvKey = runPut . putPadPrvKey
+ src/Haskoin/Keys/Mnemonic.hs view
@@ -0,0 +1,516 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Keys.Mnemonic+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Mnemonic keys (BIP-39). Only English dictionary.+-}+module Haskoin.Keys.Mnemonic+    ( -- * Mnemonic Sentences+      Entropy+    , Mnemonic+    , Passphrase+    , Seed+    , toMnemonic+    , fromMnemonic+    , mnemonicToSeed+    ) where++import           Control.Monad           (when)+import           Crypto.Hash             (SHA256 (..), hashWith)+import           Crypto.KDF.PBKDF2       (Parameters (..), fastPBKDF2_SHA512)+import           Data.Bits               (shiftL, shiftR)+import qualified Data.ByteArray          as BA+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import           Data.List+import qualified Data.Map.Strict         as M+import           Data.Maybe+import           Data.String.Conversions (cs)+import           Data.Text               (Text)+import qualified Data.Text               as T+import qualified Data.Text.Encoding      as E+import           Data.Vector             (Vector, (!))+import qualified Data.Vector             as V+import           Haskoin.Util++-- | Random data used to create a mnemonic sentence. Use a good entropy source.+-- You will get your coins stolen if you don't. You have been warned.+type Entropy = ByteString++-- | Human-readable mnemonic sentence.+type Mnemonic = Text++-- | Optional passphrase for mnemnoic sentence.+type Passphrase = ByteString++-- | Seed for a private key from a mnemonic sentence.+type Seed = ByteString++-- | Mnemonic key checksum.+type Checksum = ByteString++-- | Paremeters for PBKDF2 function.+pbkdfParams :: Parameters+pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}++-- | Provide intial 'Entropy' as a 'ByteString' of length multiple of 4 bytes.+-- Output a 'Mnemonic' sentence.+toMnemonic :: Entropy -> Either String Mnemonic+toMnemonic ent = do+    when (BS.null ent) $+        Left "toMnemonic: entropy can not be empty"+    when (remainder /= 0) $+        Left "toMnemonic: entropy must be multiples of 4 bytes"+    when (cs_len > 16) $+        Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"+    return ms+  where+    (cs_len, remainder) = BS.length ent `quotRem` 4+    c = calcCS cs_len ent+    indices = bsToIndices $ ent `BS.append` c+    ms = T.unwords $ map (wl!) indices++-- | Revert 'toMnemonic'. Do not use this to generate a 'Seed'. Instead use+-- 'mnemonicToSeed'. This outputs the original 'Entropy' used to generate a+-- 'Mnemonic' sentence.+fromMnemonic :: Mnemonic -> Either String Entropy+fromMnemonic ms = do+    when (T.null ms) $+        Left "fromMnemonic: empty mnemonic"+    when (word_count > 48) $+        Left $ "fromMnemonic: too many words: " ++ show word_count+    when (word_count `mod` 3 /= 0) $+        Left $ "fromMnemonic: wrong number of words:" ++ show word_count+    ms_bs <- indicesToBS =<< getIndices ms_words+    let (ms_ent, ms_cs) = BS.splitAt (ent_len * 4) ms_bs+        ms_cs_num = numCS cs_len ms_cs+        ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent+    when (ent_cs_num /= ms_cs_num) $+        Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num+    return ms_ent+  where+    ms_words = T.words ms+    word_count = length ms_words+    (ent_len, cs_len) = (word_count * 11) `quotRem` 32+    sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b++-- | Compute 'Checksum'.+calcCS :: Int -> Entropy -> Checksum+calcCS len = getBits len . BA.convert . hashWith SHA256++numCS :: Int -> Entropy -> Integer+numCS len =+    shiftCS . bsToInteger+  where+    shiftCS = case 8 - len `mod` 8 of+        8 -> id+        x -> flip shiftR x++-- | Turn an arbitrary sequence of characters into a 512-bit 'Seed'. Use+-- 'mnemonicToSeed' to get a seed from a 'Mnemonic' sentence. Warning: Does not+-- perform NFKD normalization.+anyToSeed :: Passphrase -> Mnemonic -> Seed+anyToSeed pf ms =+    fastPBKDF2_SHA512 pbkdfParams (E.encodeUtf8 ms) ("mnemonic" `mappend` pf)++-- | Get a 512-bit 'Seed' from a 'Mnemonic' sentence. Will validate checksum.+-- 'Passphrase' can be used to protect the 'Mnemonic'. Use an empty string as+-- 'Passphrase' if none is required.+mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed+mnemonicToSeed pf ms = do+    ent <- fromMnemonic ms+    mnm <- toMnemonic ent+    return $ anyToSeed pf mnm++-- | Get indices of words in word list.+getIndices :: [Text] -> Either String [Int]+getIndices ws+    | null n = return $ catMaybes i+    | otherwise = Left $ "getIndices: words not found: " ++ cs w+  where+    i = map (`M.lookup` wl') ws+    n = elemIndices Nothing i+    w = T.unwords $ map (ws !!) n++-- | Turn a list of 11-bit numbers into a 'ByteString'+indicesToBS :: [Int] -> Either String ByteString+indicesToBS is = do+    when lrg $ Left "indicesToBS: index larger or equal than 2048"+    return . pad . integerToBS $ foldl' f 0 is `shiftL` shift_width+  where+    lrg = isJust $ find (>= 2048) is+    (q, r) = (length is * 11) `quotRem` 8+    shift_width =+        if r == 0+            then 0+            else 8 - r+    bl =+        if r == 0+            then q+            else q + 1 -- length of resulting ByteString+    pad bs = BS.append (BS.replicate (bl - BS.length bs) 0x00) bs+    f acc x = (acc `shiftL` 11) + fromIntegral x++-- | Turn a 'ByteString' into a list of 11-bit numbers.+bsToIndices :: ByteString -> [Int]+bsToIndices bs =+    reverse . go q $ bsToInteger bs `shiftR` r+  where+    (q, r) = (BS.length bs * 8) `quotRem` 11+    go 0 _ = []+    go n i = fromIntegral (i `mod` 2048) : go (n - 1) (i `shiftR` 11)++wl' :: M.Map Text Int+wl' = V.ifoldr' (\i w m -> M.insert w i m) M.empty wl++-- | Standard English dictionary from BIP-39 specification.+wl :: Vector Text+wl = V.fromListN 2048+    [ "abandon", "ability", "able", "about", "above", "absent"+    , "absorb", "abstract", "absurd", "abuse", "access", "accident"+    , "account", "accuse", "achieve", "acid", "acoustic", "acquire"+    , "across", "act", "action", "actor", "actress", "actual"+    , "adapt", "add", "addict", "address", "adjust", "admit"+    , "adult", "advance", "advice", "aerobic", "affair", "afford"+    , "afraid", "again", "age", "agent", "agree", "ahead"+    , "aim", "air", "airport", "aisle", "alarm", "album"+    , "alcohol", "alert", "alien", "all", "alley", "allow"+    , "almost", "alone", "alpha", "already", "also", "alter"+    , "always", "amateur", "amazing", "among", "amount", "amused"+    , "analyst", "anchor", "ancient", "anger", "angle", "angry"+    , "animal", "ankle", "announce", "annual", "another", "answer"+    , "antenna", "antique", "anxiety", "any", "apart", "apology"+    , "appear", "apple", "approve", "april", "arch", "arctic"+    , "area", "arena", "argue", "arm", "armed", "armor"+    , "army", "around", "arrange", "arrest", "arrive", "arrow"+    , "art", "artefact", "artist", "artwork", "ask", "aspect"+    , "assault", "asset", "assist", "assume", "asthma", "athlete"+    , "atom", "attack", "attend", "attitude", "attract", "auction"+    , "audit", "august", "aunt", "author", "auto", "autumn"+    , "average", "avocado", "avoid", "awake", "aware", "away"+    , "awesome", "awful", "awkward", "axis", "baby", "bachelor"+    , "bacon", "badge", "bag", "balance", "balcony", "ball"+    , "bamboo", "banana", "banner", "bar", "barely", "bargain"+    , "barrel", "base", "basic", "basket", "battle", "beach"+    , "bean", "beauty", "because", "become", "beef", "before"+    , "begin", "behave", "behind", "believe", "below", "belt"+    , "bench", "benefit", "best", "betray", "better", "between"+    , "beyond", "bicycle", "bid", "bike", "bind", "biology"+    , "bird", "birth", "bitter", "black", "blade", "blame"+    , "blanket", "blast", "bleak", "bless", "blind", "blood"+    , "blossom", "blouse", "blue", "blur", "blush", "board"+    , "boat", "body", "boil", "bomb", "bone", "bonus"+    , "book", "boost", "border", "boring", "borrow", "boss"+    , "bottom", "bounce", "box", "boy", "bracket", "brain"+    , "brand", "brass", "brave", "bread", "breeze", "brick"+    , "bridge", "brief", "bright", "bring", "brisk", "broccoli"+    , "broken", "bronze", "broom", "brother", "brown", "brush"+    , "bubble", "buddy", "budget", "buffalo", "build", "bulb"+    , "bulk", "bullet", "bundle", "bunker", "burden", "burger"+    , "burst", "bus", "business", "busy", "butter", "buyer"+    , "buzz", "cabbage", "cabin", "cable", "cactus", "cage"+    , "cake", "call", "calm", "camera", "camp", "can"+    , "canal", "cancel", "candy", "cannon", "canoe", "canvas"+    , "canyon", "capable", "capital", "captain", "car", "carbon"+    , "card", "cargo", "carpet", "carry", "cart", "case"+    , "cash", "casino", "castle", "casual", "cat", "catalog"+    , "catch", "category", "cattle", "caught", "cause", "caution"+    , "cave", "ceiling", "celery", "cement", "census", "century"+    , "cereal", "certain", "chair", "chalk", "champion", "change"+    , "chaos", "chapter", "charge", "chase", "chat", "cheap"+    , "check", "cheese", "chef", "cherry", "chest", "chicken"+    , "chief", "child", "chimney", "choice", "choose", "chronic"+    , "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle"+    , "citizen", "city", "civil", "claim", "clap", "clarify"+    , "claw", "clay", "clean", "clerk", "clever", "click"+    , "client", "cliff", "climb", "clinic", "clip", "clock"+    , "clog", "close", "cloth", "cloud", "clown", "club"+    , "clump", "cluster", "clutch", "coach", "coast", "coconut"+    , "code", "coffee", "coil", "coin", "collect", "color"+    , "column", "combine", "come", "comfort", "comic", "common"+    , "company", "concert", "conduct", "confirm", "congress", "connect"+    , "consider", "control", "convince", "cook", "cool", "copper"+    , "copy", "coral", "core", "corn", "correct", "cost"+    , "cotton", "couch", "country", "couple", "course", "cousin"+    , "cover", "coyote", "crack", "cradle", "craft", "cram"+    , "crane", "crash", "crater", "crawl", "crazy", "cream"+    , "credit", "creek", "crew", "cricket", "crime", "crisp"+    , "critic", "crop", "cross", "crouch", "crowd", "crucial"+    , "cruel", "cruise", "crumble", "crunch", "crush", "cry"+    , "crystal", "cube", "culture", "cup", "cupboard", "curious"+    , "current", "curtain", "curve", "cushion", "custom", "cute"+    , "cycle", "dad", "damage", "damp", "dance", "danger"+    , "daring", "dash", "daughter", "dawn", "day", "deal"+    , "debate", "debris", "decade", "december", "decide", "decline"+    , "decorate", "decrease", "deer", "defense", "define", "defy"+    , "degree", "delay", "deliver", "demand", "demise", "denial"+    , "dentist", "deny", "depart", "depend", "deposit", "depth"+    , "deputy", "derive", "describe", "desert", "design", "desk"+    , "despair", "destroy", "detail", "detect", "develop", "device"+    , "devote", "diagram", "dial", "diamond", "diary", "dice"+    , "diesel", "diet", "differ", "digital", "dignity", "dilemma"+    , "dinner", "dinosaur", "direct", "dirt", "disagree", "discover"+    , "disease", "dish", "dismiss", "disorder", "display", "distance"+    , "divert", "divide", "divorce", "dizzy", "doctor", "document"+    , "dog", "doll", "dolphin", "domain", "donate", "donkey"+    , "donor", "door", "dose", "double", "dove", "draft"+    , "dragon", "drama", "drastic", "draw", "dream", "dress"+    , "drift", "drill", "drink", "drip", "drive", "drop"+    , "drum", "dry", "duck", "dumb", "dune", "during"+    , "dust", "dutch", "duty", "dwarf", "dynamic", "eager"+    , "eagle", "early", "earn", "earth", "easily", "east"+    , "easy", "echo", "ecology", "economy", "edge", "edit"+    , "educate", "effort", "egg", "eight", "either", "elbow"+    , "elder", "electric", "elegant", "element", "elephant", "elevator"+    , "elite", "else", "embark", "embody", "embrace", "emerge"+    , "emotion", "employ", "empower", "empty", "enable", "enact"+    , "end", "endless", "endorse", "enemy", "energy", "enforce"+    , "engage", "engine", "enhance", "enjoy", "enlist", "enough"+    , "enrich", "enroll", "ensure", "enter", "entire", "entry"+    , "envelope", "episode", "equal", "equip", "era", "erase"+    , "erode", "erosion", "error", "erupt", "escape", "essay"+    , "essence", "estate", "eternal", "ethics", "evidence", "evil"+    , "evoke", "evolve", "exact", "example", "excess", "exchange"+    , "excite", "exclude", "excuse", "execute", "exercise", "exhaust"+    , "exhibit", "exile", "exist", "exit", "exotic", "expand"+    , "expect", "expire", "explain", "expose", "express", "extend"+    , "extra", "eye", "eyebrow", "fabric", "face", "faculty"+    , "fade", "faint", "faith", "fall", "false", "fame"+    , "family", "famous", "fan", "fancy", "fantasy", "farm"+    , "fashion", "fat", "fatal", "father", "fatigue", "fault"+    , "favorite", "feature", "february", "federal", "fee", "feed"+    , "feel", "female", "fence", "festival", "fetch", "fever"+    , "few", "fiber", "fiction", "field", "figure", "file"+    , "film", "filter", "final", "find", "fine", "finger"+    , "finish", "fire", "firm", "first", "fiscal", "fish"+    , "fit", "fitness", "fix", "flag", "flame", "flash"+    , "flat", "flavor", "flee", "flight", "flip", "float"+    , "flock", "floor", "flower", "fluid", "flush", "fly"+    , "foam", "focus", "fog", "foil", "fold", "follow"+    , "food", "foot", "force", "forest", "forget", "fork"+    , "fortune", "forum", "forward", "fossil", "foster", "found"+    , "fox", "fragile", "frame", "frequent", "fresh", "friend"+    , "fringe", "frog", "front", "frost", "frown", "frozen"+    , "fruit", "fuel", "fun", "funny", "furnace", "fury"+    , "future", "gadget", "gain", "galaxy", "gallery", "game"+    , "gap", "garage", "garbage", "garden", "garlic", "garment"+    , "gas", "gasp", "gate", "gather", "gauge", "gaze"+    , "general", "genius", "genre", "gentle", "genuine", "gesture"+    , "ghost", "giant", "gift", "giggle", "ginger", "giraffe"+    , "girl", "give", "glad", "glance", "glare", "glass"+    , "glide", "glimpse", "globe", "gloom", "glory", "glove"+    , "glow", "glue", "goat", "goddess", "gold", "good"+    , "goose", "gorilla", "gospel", "gossip", "govern", "gown"+    , "grab", "grace", "grain", "grant", "grape", "grass"+    , "gravity", "great", "green", "grid", "grief", "grit"+    , "grocery", "group", "grow", "grunt", "guard", "guess"+    , "guide", "guilt", "guitar", "gun", "gym", "habit"+    , "hair", "half", "hammer", "hamster", "hand", "happy"+    , "harbor", "hard", "harsh", "harvest", "hat", "have"+    , "hawk", "hazard", "head", "health", "heart", "heavy"+    , "hedgehog", "height", "hello", "helmet", "help", "hen"+    , "hero", "hidden", "high", "hill", "hint", "hip"+    , "hire", "history", "hobby", "hockey", "hold", "hole"+    , "holiday", "hollow", "home", "honey", "hood", "hope"+    , "horn", "horror", "horse", "hospital", "host", "hotel"+    , "hour", "hover", "hub", "huge", "human", "humble"+    , "humor", "hundred", "hungry", "hunt", "hurdle", "hurry"+    , "hurt", "husband", "hybrid", "ice", "icon", "idea"+    , "identify", "idle", "ignore", "ill", "illegal", "illness"+    , "image", "imitate", "immense", "immune", "impact", "impose"+    , "improve", "impulse", "inch", "include", "income", "increase"+    , "index", "indicate", "indoor", "industry", "infant", "inflict"+    , "inform", "inhale", "inherit", "initial", "inject", "injury"+    , "inmate", "inner", "innocent", "input", "inquiry", "insane"+    , "insect", "inside", "inspire", "install", "intact", "interest"+    , "into", "invest", "invite", "involve", "iron", "island"+    , "isolate", "issue", "item", "ivory", "jacket", "jaguar"+    , "jar", "jazz", "jealous", "jeans", "jelly", "jewel"+    , "job", "join", "joke", "journey", "joy", "judge"+    , "juice", "jump", "jungle", "junior", "junk", "just"+    , "kangaroo", "keen", "keep", "ketchup", "key", "kick"+    , "kid", "kidney", "kind", "kingdom", "kiss", "kit"+    , "kitchen", "kite", "kitten", "kiwi", "knee", "knife"+    , "knock", "know", "lab", "label", "labor", "ladder"+    , "lady", "lake", "lamp", "language", "laptop", "large"+    , "later", "latin", "laugh", "laundry", "lava", "law"+    , "lawn", "lawsuit", "layer", "lazy", "leader", "leaf"+    , "learn", "leave", "lecture", "left", "leg", "legal"+    , "legend", "leisure", "lemon", "lend", "length", "lens"+    , "leopard", "lesson", "letter", "level", "liar", "liberty"+    , "library", "license", "life", "lift", "light", "like"+    , "limb", "limit", "link", "lion", "liquid", "list"+    , "little", "live", "lizard", "load", "loan", "lobster"+    , "local", "lock", "logic", "lonely", "long", "loop"+    , "lottery", "loud", "lounge", "love", "loyal", "lucky"+    , "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics"+    , "machine", "mad", "magic", "magnet", "maid", "mail"+    , "main", "major", "make", "mammal", "man", "manage"+    , "mandate", "mango", "mansion", "manual", "maple", "marble"+    , "march", "margin", "marine", "market", "marriage", "mask"+    , "mass", "master", "match", "material", "math", "matrix"+    , "matter", "maximum", "maze", "meadow", "mean", "measure"+    , "meat", "mechanic", "medal", "media", "melody", "melt"+    , "member", "memory", "mention", "menu", "mercy", "merge"+    , "merit", "merry", "mesh", "message", "metal", "method"+    , "middle", "midnight", "milk", "million", "mimic", "mind"+    , "minimum", "minor", "minute", "miracle", "mirror", "misery"+    , "miss", "mistake", "mix", "mixed", "mixture", "mobile"+    , "model", "modify", "mom", "moment", "monitor", "monkey"+    , "monster", "month", "moon", "moral", "more", "morning"+    , "mosquito", "mother", "motion", "motor", "mountain", "mouse"+    , "move", "movie", "much", "muffin", "mule", "multiply"+    , "muscle", "museum", "mushroom", "music", "must", "mutual"+    , "myself", "mystery", "myth", "naive", "name", "napkin"+    , "narrow", "nasty", "nation", "nature", "near", "neck"+    , "need", "negative", "neglect", "neither", "nephew", "nerve"+    , "nest", "net", "network", "neutral", "never", "news"+    , "next", "nice", "night", "noble", "noise", "nominee"+    , "noodle", "normal", "north", "nose", "notable", "note"+    , "nothing", "notice", "novel", "now", "nuclear", "number"+    , "nurse", "nut", "oak", "obey", "object", "oblige"+    , "obscure", "observe", "obtain", "obvious", "occur", "ocean"+    , "october", "odor", "off", "offer", "office", "often"+    , "oil", "okay", "old", "olive", "olympic", "omit"+    , "once", "one", "onion", "online", "only", "open"+    , "opera", "opinion", "oppose", "option", "orange", "orbit"+    , "orchard", "order", "ordinary", "organ", "orient", "original"+    , "orphan", "ostrich", "other", "outdoor", "outer", "output"+    , "outside", "oval", "oven", "over", "own", "owner"+    , "oxygen", "oyster", "ozone", "pact", "paddle", "page"+    , "pair", "palace", "palm", "panda", "panel", "panic"+    , "panther", "paper", "parade", "parent", "park", "parrot"+    , "party", "pass", "patch", "path", "patient", "patrol"+    , "pattern", "pause", "pave", "payment", "peace", "peanut"+    , "pear", "peasant", "pelican", "pen", "penalty", "pencil"+    , "people", "pepper", "perfect", "permit", "person", "pet"+    , "phone", "photo", "phrase", "physical", "piano", "picnic"+    , "picture", "piece", "pig", "pigeon", "pill", "pilot"+    , "pink", "pioneer", "pipe", "pistol", "pitch", "pizza"+    , "place", "planet", "plastic", "plate", "play", "please"+    , "pledge", "pluck", "plug", "plunge", "poem", "poet"+    , "point", "polar", "pole", "police", "pond", "pony"+    , "pool", "popular", "portion", "position", "possible", "post"+    , "potato", "pottery", "poverty", "powder", "power", "practice"+    , "praise", "predict", "prefer", "prepare", "present", "pretty"+    , "prevent", "price", "pride", "primary", "print", "priority"+    , "prison", "private", "prize", "problem", "process", "produce"+    , "profit", "program", "project", "promote", "proof", "property"+    , "prosper", "protect", "proud", "provide", "public", "pudding"+    , "pull", "pulp", "pulse", "pumpkin", "punch", "pupil"+    , "puppy", "purchase", "purity", "purpose", "purse", "push"+    , "put", "puzzle", "pyramid", "quality", "quantum", "quarter"+    , "question", "quick", "quit", "quiz", "quote", "rabbit"+    , "raccoon", "race", "rack", "radar", "radio", "rail"+    , "rain", "raise", "rally", "ramp", "ranch", "random"+    , "range", "rapid", "rare", "rate", "rather", "raven"+    , "raw", "razor", "ready", "real", "reason", "rebel"+    , "rebuild", "recall", "receive", "recipe", "record", "recycle"+    , "reduce", "reflect", "reform", "refuse", "region", "regret"+    , "regular", "reject", "relax", "release", "relief", "rely"+    , "remain", "remember", "remind", "remove", "render", "renew"+    , "rent", "reopen", "repair", "repeat", "replace", "report"+    , "require", "rescue", "resemble", "resist", "resource", "response"+    , "result", "retire", "retreat", "return", "reunion", "reveal"+    , "review", "reward", "rhythm", "rib", "ribbon", "rice"+    , "rich", "ride", "ridge", "rifle", "right", "rigid"+    , "ring", "riot", "ripple", "risk", "ritual", "rival"+    , "river", "road", "roast", "robot", "robust", "rocket"+    , "romance", "roof", "rookie", "room", "rose", "rotate"+    , "rough", "round", "route", "royal", "rubber", "rude"+    , "rug", "rule", "run", "runway", "rural", "sad"+    , "saddle", "sadness", "safe", "sail", "salad", "salmon"+    , "salon", "salt", "salute", "same", "sample", "sand"+    , "satisfy", "satoshi", "sauce", "sausage", "save", "say"+    , "scale", "scan", "scare", "scatter", "scene", "scheme"+    , "school", "science", "scissors", "scorpion", "scout", "scrap"+    , "screen", "script", "scrub", "sea", "search", "season"+    , "seat", "second", "secret", "section", "security", "seed"+    , "seek", "segment", "select", "sell", "seminar", "senior"+    , "sense", "sentence", "series", "service", "session", "settle"+    , "setup", "seven", "shadow", "shaft", "shallow", "share"+    , "shed", "shell", "sheriff", "shield", "shift", "shine"+    , "ship", "shiver", "shock", "shoe", "shoot", "shop"+    , "short", "shoulder", "shove", "shrimp", "shrug", "shuffle"+    , "shy", "sibling", "sick", "side", "siege", "sight"+    , "sign", "silent", "silk", "silly", "silver", "similar"+    , "simple", "since", "sing", "siren", "sister", "situate"+    , "six", "size", "skate", "sketch", "ski", "skill"+    , "skin", "skirt", "skull", "slab", "slam", "sleep"+    , "slender", "slice", "slide", "slight", "slim", "slogan"+    , "slot", "slow", "slush", "small", "smart", "smile"+    , "smoke", "smooth", "snack", "snake", "snap", "sniff"+    , "snow", "soap", "soccer", "social", "sock", "soda"+    , "soft", "solar", "soldier", "solid", "solution", "solve"+    , "someone", "song", "soon", "sorry", "sort", "soul"+    , "sound", "soup", "source", "south", "space", "spare"+    , "spatial", "spawn", "speak", "special", "speed", "spell"+    , "spend", "sphere", "spice", "spider", "spike", "spin"+    , "spirit", "split", "spoil", "sponsor", "spoon", "sport"+    , "spot", "spray", "spread", "spring", "spy", "square"+    , "squeeze", "squirrel", "stable", "stadium", "staff", "stage"+    , "stairs", "stamp", "stand", "start", "state", "stay"+    , "steak", "steel", "stem", "step", "stereo", "stick"+    , "still", "sting", "stock", "stomach", "stone", "stool"+    , "story", "stove", "strategy", "street", "strike", "strong"+    , "struggle", "student", "stuff", "stumble", "style", "subject"+    , "submit", "subway", "success", "such", "sudden", "suffer"+    , "sugar", "suggest", "suit", "summer", "sun", "sunny"+    , "sunset", "super", "supply", "supreme", "sure", "surface"+    , "surge", "surprise", "surround", "survey", "suspect", "sustain"+    , "swallow", "swamp", "swap", "swarm", "swear", "sweet"+    , "swift", "swim", "swing", "switch", "sword", "symbol"+    , "symptom", "syrup", "system", "table", "tackle", "tag"+    , "tail", "talent", "talk", "tank", "tape", "target"+    , "task", "taste", "tattoo", "taxi", "teach", "team"+    , "tell", "ten", "tenant", "tennis", "tent", "term"+    , "test", "text", "thank", "that", "theme", "then"+    , "theory", "there", "they", "thing", "this", "thought"+    , "three", "thrive", "throw", "thumb", "thunder", "ticket"+    , "tide", "tiger", "tilt", "timber", "time", "tiny"+    , "tip", "tired", "tissue", "title", "toast", "tobacco"+    , "today", "toddler", "toe", "together", "toilet", "token"+    , "tomato", "tomorrow", "tone", "tongue", "tonight", "tool"+    , "tooth", "top", "topic", "topple", "torch", "tornado"+    , "tortoise", "toss", "total", "tourist", "toward", "tower"+    , "town", "toy", "track", "trade", "traffic", "tragic"+    , "train", "transfer", "trap", "trash", "travel", "tray"+    , "treat", "tree", "trend", "trial", "tribe", "trick"+    , "trigger", "trim", "trip", "trophy", "trouble", "truck"+    , "true", "truly", "trumpet", "trust", "truth", "try"+    , "tube", "tuition", "tumble", "tuna", "tunnel", "turkey"+    , "turn", "turtle", "twelve", "twenty", "twice", "twin"+    , "twist", "two", "type", "typical", "ugly", "umbrella"+    , "unable", "unaware", "uncle", "uncover", "under", "undo"+    , "unfair", "unfold", "unhappy", "uniform", "unique", "unit"+    , "universe", "unknown", "unlock", "until", "unusual", "unveil"+    , "update", "upgrade", "uphold", "upon", "upper", "upset"+    , "urban", "urge", "usage", "use", "used", "useful"+    , "useless", "usual", "utility", "vacant", "vacuum", "vague"+    , "valid", "valley", "valve", "van", "vanish", "vapor"+    , "various", "vast", "vault", "vehicle", "velvet", "vendor"+    , "venture", "venue", "verb", "verify", "version", "very"+    , "vessel", "veteran", "viable", "vibrant", "vicious", "victory"+    , "video", "view", "village", "vintage", "violin", "virtual"+    , "virus", "visa", "visit", "visual", "vital", "vivid"+    , "vocal", "voice", "void", "volcano", "volume", "vote"+    , "voyage", "wage", "wagon", "wait", "walk", "wall"+    , "walnut", "want", "warfare", "warm", "warrior", "wash"+    , "wasp", "waste", "water", "wave", "way", "wealth"+    , "weapon", "wear", "weasel", "weather", "web", "wedding"+    , "weekend", "weird", "welcome", "west", "wet", "whale"+    , "what", "wheat", "wheel", "when", "where", "whip"+    , "whisper", "wide", "width", "wife", "wild", "will"+    , "win", "window", "wine", "wing", "wink", "winner"+    , "winter", "wire", "wisdom", "wise", "wish", "witness"+    , "wolf", "woman", "wonder", "wood", "wool", "word"+    , "work", "world", "worry", "worth", "wrap", "wreck"+    , "wrestle", "wrist", "write", "wrong", "yard", "year"+    , "yellow", "you", "young", "youth", "zebra", "zero"+    , "zone", "zoo"+    ]
+ src/Haskoin/Network.hs view
@@ -0,0 +1,21 @@+{-|+Module      : Haskoin.Network+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++This module provides basic types used for the Bitcoin networking protocol+together with 'Data.Serialize' instances for efficiently serializing and+de-serializing them.+-}+module Haskoin.Network+    ( module Common+    , module Message+    , module Bloom+    ) where++import           Haskoin.Network.Bloom   as Bloom+import           Haskoin.Network.Common  as Common+import           Haskoin.Network.Message as Message
+ src/Haskoin/Network/Bloom.hs view
@@ -0,0 +1,262 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}+{-|+Module      : Haskoin.Network.Bloom+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Bloom filters are used to reduce data transfer when synchronizing thin cients.+When bloom filters are used a client will obtain filtered blocks that only+contain transactions that pass the bloom filter. Transactions announced via inv+messages also pass the filter.+-}+module Haskoin.Network.Bloom+    ( -- * Bloom Filters+      BloomFlags(..)+    , BloomFilter(..)+    , FilterLoad(..)+    , FilterAdd(..)+    , bloomCreate+    , bloomInsert+    , bloomContains+    , isBloomValid+    , isBloomEmpty+    , isBloomFull+    , acceptsFilters+    , bloomRelevantUpdate+    ) where++import           Control.DeepSeq+import           Control.Monad              (forM_, replicateM)+import           Data.Bits+import           Data.ByteString            (ByteString)+import qualified Data.ByteString            as BS+import qualified Data.Foldable              as F+import           Data.Hash.Murmur           (murmur3)+import           Data.List                  (foldl')+import qualified Data.Sequence              as S+import           Data.Serialize             (Serialize, encode, get, put)+import           Data.Serialize.Get         (getByteString, getWord32le,+                                             getWord8)+import           Data.Serialize.Put         (putByteString, putWord32le,+                                             putWord8)+import           Data.Word+import           GHC.Generics               (Generic)+import           Haskoin.Network.Common+import           Haskoin.Script.Common+import           Haskoin.Transaction.Common+-- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%+maxBloomSize :: Int+maxBloomSize = 36000++maxHashFuncs :: Word32+maxHashFuncs = 50++ln2Squared :: Double+ln2Squared = 0.4804530139182014246671025263266649717305529515945455++ln2 :: Double+ln2 = 0.6931471805599453094172321214581765680755001343602552++bitMask :: [Word8]+bitMask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]++-- | The bloom flags are used to tell the remote peer how to auto-update+-- the provided bloom filter.+data BloomFlags+    = BloomUpdateNone -- ^ never update+    | BloomUpdateAll -- ^ auto-update on all outputs+    | BloomUpdateP2PubKeyOnly+    -- ^ auto-update on pay-to-pubkey or pay-to-multisig (default)+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize BloomFlags where+    get = go =<< getWord8+      where+        go 0 = return BloomUpdateNone+        go 1 = return BloomUpdateAll+        go 2 = return BloomUpdateP2PubKeyOnly+        go _ = fail "BloomFlags get: Invalid bloom flag"++    put f = putWord8 $ case f of+        BloomUpdateNone         -> 0+        BloomUpdateAll          -> 1+        BloomUpdateP2PubKeyOnly -> 2++-- | A bloom filter is a probabilistic data structure that SPV clients send to+-- other peers to filter the set of transactions received from them. Bloom+-- filters can have false positives but not false negatives. Some transactions+-- that pass the filter may not be relevant to the receiving peer. By+-- controlling the false positive rate, SPV nodes can trade off bandwidth+-- versus privacy.+data BloomFilter = BloomFilter+    { bloomData      :: !(S.Seq Word8)+    -- ^ bloom filter data+    , bloomHashFuncs :: !Word32+    -- ^ number of hash functions for this filter+    , bloomTweak     :: !Word32+    -- ^ hash function random nonce+    , bloomFlags     :: !BloomFlags+    -- ^ bloom filter auto-update flags+    }+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize BloomFilter where++    get = BloomFilter <$> (S.fromList <$> (readDat =<< get))+                      <*> getWord32le <*> getWord32le+                      <*> get+      where+        readDat (VarInt len) = replicateM (fromIntegral len) getWord8++    put (BloomFilter dat hashFuncs tweak flags) = do+        putVarInt $ S.length dat+        forM_ (F.toList dat) putWord8+        putWord32le hashFuncs+        putWord32le tweak+        put flags++-- | Set a new bloom filter on the peer connection.+newtype FilterLoad = FilterLoad { filterLoadBloomFilter :: BloomFilter }+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize FilterLoad where+    get = FilterLoad <$> get+    put (FilterLoad f) = put f++-- | Add the given data element to the connections current filter without+-- requiring a completely new one to be set.+newtype FilterAdd = FilterAdd { getFilterData :: ByteString }+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize FilterAdd where+    get = do+        (VarInt len) <- get+        dat <- getByteString $ fromIntegral len+        return $ FilterAdd dat++    put (FilterAdd bs) = do+        putVarInt $ BS.length bs+        putByteString bs+++-- | Build a bloom filter that will provide the given false positive rate when+-- the given number of elements have been inserted.+bloomCreate :: Int          -- ^ number of elements+            -> Double       -- ^ false positive rate+            -> Word32       -- ^ random nonce (tweak) for the hash function+            -> BloomFlags   -- ^ bloom filter flags+            -> BloomFilter  -- ^ bloom filter+bloomCreate numElem fpRate =+    BloomFilter (S.replicate bloomSize 0) numHashF+  where+    -- Bloom filter size in bytes+    bloomSize = truncate $ min a b / 8+    -- Suggested size in bits+    a         = -1 / ln2Squared * fromIntegral numElem * log fpRate+    -- Maximum size in bits+    b         = fromIntegral $ maxBloomSize * 8+    numHashF  = truncate $ min c (fromIntegral maxHashFuncs)+    -- Suggested number of hash functions+    c         = fromIntegral bloomSize * 8 / fromIntegral numElem * ln2++bloomHash :: BloomFilter -> Word32 -> ByteString -> Word32+bloomHash bfilter hashNum bs =+    murmur3 seed bs `mod` (fromIntegral (S.length (bloomData bfilter)) * 8)+  where+    seed = hashNum * 0xfba4c795 + bloomTweak bfilter++-- | Insert arbitrary data into a bloom filter. Returns the new bloom filter+-- containing the new data.+bloomInsert :: BloomFilter    -- ^ Original bloom filter+            -> ByteString     -- ^ New data to insert+            -> BloomFilter    -- ^ Bloom filter containing the new data+bloomInsert bfilter bs+    | isBloomFull bfilter = bfilter+    | otherwise = bfilter { bloomData = newData }+  where+    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]+    upd s i = S.adjust (.|. bitMask !! fromIntegral (7 .&. i))+                       (fromIntegral $ i `shiftR` 3) s+    newData = foldl upd (bloomData bfilter) idxs++-- | Tests if some arbitrary data matches the filter. This can be either because+-- the data was inserted into the filter or because it is a false positive.+bloomContains :: BloomFilter    -- ^ Bloom filter+              -> ByteString+              -- ^ Data that will be checked against the given bloom filter+              -> Bool+              -- ^ Returns True if the data matches the filter+bloomContains bfilter bs+    | isBloomFull bfilter  = True+    | isBloomEmpty bfilter = False+    | otherwise            = all isSet idxs+  where+    s       = bloomData bfilter+    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]+    isSet i = S.index s (fromIntegral $ i `shiftR` 3)+          .&. (bitMask !! fromIntegral (7 .&. i)) /= 0++-- | Checks if any of the outputs of a tx is in the current bloom filter.+-- If it is, add the txid and vout as an outpoint (i.e. so that+-- a future tx that spends the output won't be missed).+bloomRelevantUpdate :: BloomFilter+                    -- ^ Bloom filter+                    -> Tx+                    -- ^ Tx that may (or may not) have relevant outputs+                    -> Maybe BloomFilter+                    -- ^ Returns an updated bloom filter adding relevant output+bloomRelevantUpdate bfilter tx+    | isBloomFull bfilter || isBloomEmpty bfilter = Nothing+    | bloomFlags bfilter == BloomUpdateNone = Nothing+    | length matchOuts > 0 = Just $ foldl' (addRelevant) bfilter matchOuts+    | otherwise = Nothing+    where+        -- TxHash if we end up inserting an outpoint+        h = txHash tx+        -- Decode the scriptOutpus and add vOuts in case we make them outpoints+        decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx+        err = error $ "Error Decoding output script"+        idxOutputScripts = either (const err) (zip [0..]) decodedOutputScripts+        -- Check if any txOuts were contained in the bloom filter+        matchFilter = filter (\(_,op) -> bloomContains bfilter $ encodeScriptOut op)+        matchOuts = matchFilter idxOutputScripts++        addRelevant :: BloomFilter -> (Word32,ScriptOutput) -> BloomFilter+        addRelevant bfilter (id,scriptOut) = case (bloomFlags bfilter,scriptType) of+            -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig+            (_, True) -> bloomInsert bfilter outpoint+            (BloomUpdateAll, _ ) -> bloomInsert bfilter outpoint+            _ ->  error "Error Updating Bloom Filter with relevant outpoint"+            where+                outpoint = encode $ OutPoint{ outPointHash = h, outPointIndex = id}+                scriptType = (\s -> isPayPK s ||isPayMulSig s) scriptOut++        -- Encodes a scriptOutput so it can be checked agains the Bloom Filter+        encodeScriptOut :: ScriptOutput -> ByteString+        encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig+        encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash+        encodeScriptOut (DataCarrier getOutputData) = encode getOutputData+        encodeScriptOut outputHash = (encode . getOutputHash) outputHash++-- | Returns True if the filter is empty (all bytes set to 0x00)+isBloomEmpty :: BloomFilter -> Bool+isBloomEmpty bfilter = all (== 0x00) $ F.toList $ bloomData bfilter++-- | Returns True if the filter is full (all bytes set to 0xff)+isBloomFull :: BloomFilter -> Bool+isBloomFull bfilter = all (== 0xff) $ F.toList $ bloomData bfilter++-- | Tests if a given bloom filter is valid.+isBloomValid :: BloomFilter -- ^ Bloom filter to test+             -> Bool        -- ^ True if the given filter is valid+isBloomValid bfilter =+    S.length (bloomData bfilter) <= maxBloomSize &&+    bloomHashFuncs bfilter <= maxHashFuncs++-- | Does the peer with these version services accept bloom filters?+acceptsFilters :: Word64 -> Bool+acceptsFilters srv = srv .&. (1 `shiftL` 2) /= 0
+ src/Haskoin/Network/Common.hs view
@@ -0,0 +1,613 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Network.Common+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Common functions and data types related to peer-to-peer network.+-}+module Haskoin.Network.Common+    ( -- * Network Data Types+      Addr(..)+    , NetworkAddressTime+    , Alert(..)+    , GetData(..)+    , Inv(..)+    , InvVector(..)+    , InvType(..)+    , HostAddress+    , hostToSockAddr+    , sockToHostAddress+    , NetworkAddress(..)+    , NotFound(..)+    , Ping(..)+    , Pong(..)+    , Reject(..)+    , RejectCode(..)+    , VarInt(..)+    , VarString(..)+    , Version(..)+    , MessageCommand(..)+      -- ** Useful Functions+    , reject+    , nodeNone+    , nodeNetwork+    , nodeGetUTXO+    , nodeBloom+    , nodeWitness+    , nodeXThin+    , commandToString+    , stringToCommand+    , putVarInt+    ) where++import           Control.DeepSeq+import           Control.Monad           (forM_, liftM2, replicateM, unless)+import           Data.Bits               (shiftL)+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as B+import           Data.ByteString.Char8   as C (replicate)+import           Data.Serialize          as S+import           Data.String+import           Data.String.Conversions (cs)+import           Data.Word               (Word32, Word64)+import           GHC.Generics            (Generic)+import           Haskoin.Crypto.Hash+import           Network.Socket          (SockAddr (..))+import           Text.Read               as R++-- | Network address with a timestamp.+type NetworkAddressTime = (Word32, NetworkAddress)++-- | Provides information about known nodes in the bitcoin network. An 'Addr'+-- type is sent inside a 'Message' as a response to a 'GetAddr' message.+newtype Addr =+    Addr { -- List of addresses of other nodes on the network with timestamps.+           addrList :: [NetworkAddressTime]+         }+    deriving (Eq, Show, Generic, NFData)++instance Serialize Addr where++    get = Addr <$> (repList =<< S.get)+      where+        repList (VarInt c) = replicateM (fromIntegral c) action+        action             = liftM2 (,) getWord32le S.get++    put (Addr xs) = do+        putVarInt $ length xs+        forM_ xs $ \(a,b) -> putWord32le a >> put b++-- | Data type describing signed messages that can be sent between bitcoin+-- nodes to display important notifications to end users about the health of+-- the network.+data Alert =+    Alert {+          -- | Alert payload.+            alertPayload   :: !VarString+          -- | ECDSA signature of the payload+          , alertSignature :: !VarString+          } deriving (Eq, Show, Read, Generic, NFData)++instance Serialize Alert where+    get = Alert <$> S.get <*> S.get+    put (Alert p s) = put p >> put s++-- | The 'GetData' type is used to retrieve information on a specific object+-- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'+-- request is a list of 'InvVector' which represent all the hashes of objects+-- that a node wants. The response to a 'GetBlock' message will be either a+-- 'Block' or a 'Tx' message depending on the type of the object referenced by+-- the hash. Usually, 'GetData' messages are sent after a node receives an 'Inv'+-- message that contains unknown object hashes.+newtype GetData =+    GetData { -- | list of object hashes+              getDataList :: [InvVector]+            } deriving (Eq, Show, Generic, NFData)++instance Serialize GetData where++    get = GetData <$> (repList =<< S.get)+      where+        repList (VarInt c) = replicateM (fromIntegral c) S.get++    put (GetData xs) = do+        putVarInt $ length xs+        forM_ xs put++-- | 'Inv' messages are used by nodes to advertise their knowledge of new+-- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent+-- unsolicited or in response to a 'GetBlocks' message.+newtype Inv =+    Inv {+        -- | inventory+          invList :: [InvVector]+        } deriving (Eq, Show, Generic, NFData)++instance Serialize Inv where++    get = Inv <$> (repList =<< S.get)+      where+        repList (VarInt c) = replicateM (fromIntegral c) S.get++    put (Inv xs) = do+        putVarInt $ length xs+        forM_ xs put++-- | Data type identifying the type of an inventory vector. SegWit types are+-- only used in 'GetData' messages, not 'Inv'.+data InvType+    = InvError -- ^ error+    | InvTx -- ^ transaction+    | InvBlock -- ^ block+    | InvMerkleBlock -- ^ filtered block+    | InvWitnessTx -- ^ segwit transaction+    | InvWitnessBlock -- ^ segwit block+    | InvWitnessMerkleBlock -- ^ segwit filtere block+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize InvType where+    get = go =<< getWord32le+      where+        go x =+            case x of+                0 -> return InvError+                1 -> return InvTx+                2 -> return InvBlock+                3 -> return InvMerkleBlock+                _+                    | x == 1 `shiftL` 30 + 1 -> return InvWitnessTx+                    | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock+                    | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock+                    | otherwise -> fail "bitcoinGet InvType: Invalid Type"+    put x =+        putWord32le $+        case x of+            InvError              -> 0+            InvTx                 -> 1+            InvBlock              -> 2+            InvMerkleBlock        -> 3+            InvWitnessTx          -> 1 `shiftL` 30 + 1+            InvWitnessBlock       -> 1 `shiftL` 30 + 2+            InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3++-- | Invectory vectors represent hashes identifying objects such as a 'Block' or+-- a 'Tx'. They notify other peers about new data or data they have otherwise+-- requested.+data InvVector =+    InvVector {+                -- | type of object+                invType :: !InvType+                -- | 256-bit hash of object+              , invHash :: !Hash256+              } deriving (Eq, Show, Generic, NFData)++instance Serialize InvVector where+    get = InvVector <$> S.get <*> S.get+    put (InvVector t h) = put t >> put h++newtype HostAddress =+    HostAddress ByteString+    deriving (Eq, Show, Ord, Generic, NFData)++instance Serialize HostAddress where+    put (HostAddress bs) = putByteString bs+    get = HostAddress <$> getByteString 18++-- | Data type describing a bitcoin network address. Addresses are stored in+-- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6+-- addresses: <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>.+data NetworkAddress =+    NetworkAddress { -- | bitmask of services available for this address+                     naServices :: !Word64+                     -- | address and port information+                   , naAddress  :: !HostAddress+                   } deriving (Eq, Show, Generic, NFData)++hostToSockAddr :: HostAddress -> SockAddr+hostToSockAddr (HostAddress bs) =+    case runGet getSockAddr bs of+        Left e  -> error e+        Right x -> x++sockToHostAddress :: SockAddr -> HostAddress+sockToHostAddress = HostAddress . runPut . putSockAddr++putSockAddr :: SockAddr -> Put+putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do+    putWord32be a+    putWord32be b+    putWord32be c+    putWord32be d+    putWord16be (fromIntegral p)++putSockAddr (SockAddrInet p a) = do+    putWord32be 0x00000000+    putWord32be 0x00000000+    putWord32be 0x0000ffff+    putWord32host a+    putWord16be (fromIntegral p)++putSockAddr _ = error "Invalid address type"++getSockAddr :: Get SockAddr+getSockAddr = do+    a <- getWord32be+    b <- getWord32be+    c <- getWord32be+    if a == 0x00000000 && b == 0x00000000 && c == 0x0000ffff+        then do+            d <- getWord32host+            p <- getWord16be+            return $ SockAddrInet (fromIntegral p) d+        else do+            d <- getWord32be+            p <- getWord16be+            return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0++instance Serialize NetworkAddress where+    get = NetworkAddress <$> getWord64le <*> S.get+    put (NetworkAddress s a) = putWord64le s >> put a++-- | A 'NotFound' message is returned as a response to a 'GetData' message+-- whe one of the requested objects could not be retrieved. This could happen,+-- for example, if a tranasaction was requested and was not available in the+-- memory pool of the receiving node.+newtype NotFound =+    NotFound { -- | Inventory vectors related to this request+               notFoundList :: [InvVector]+             } deriving (Eq, Show, Generic, NFData)++instance Serialize NotFound where++    get = NotFound <$> (repList =<< S.get)+      where+        repList (VarInt c) = replicateM (fromIntegral c) S.get++    put (NotFound xs) = do+        putVarInt $ length xs+        forM_ xs put++-- | A 'Ping' message is sent to bitcoin peers to check if a connection is still+-- open.+newtype Ping =+    Ping { -- | A random nonce used to identify the recipient of the ping+           -- request once a Pong response is received.+           pingNonce :: Word64+         } deriving (Eq, Show, Read, Generic, NFData)++-- | A Pong message is sent as a response to a ping message.+newtype Pong =+    Pong {+           -- | nonce from corresponding 'Ping'+           pongNonce :: Word64+         } deriving (Eq, Show, Read, Generic, NFData)++instance Serialize Ping where+    get = Ping <$> getWord64le+    put (Ping n) = putWord64le n++instance Serialize Pong where+    get = Pong <$> getWord64le+    put (Pong n) = putWord64le n++-- | The 'Reject' message is sent when messages are rejected by a peer.+data Reject =+    Reject {+             -- | type of message rejected+             rejectMessage :: !MessageCommand+             -- | rejection code+           , rejectCode    :: !RejectCode+             -- | text reason for rejection+           , rejectReason  :: !VarString+             -- | extra data such as block or tx hash+           , rejectData    :: !ByteString+           } deriving (Eq, Show, Read, Generic, NFData)++-- | Rejection code associated to the 'Reject' message.+data RejectCode+    = RejectMalformed+    | RejectInvalid+    | RejectObsolete+    | RejectDuplicate+    | RejectNonStandard+    | RejectDust+    | RejectInsufficientFee+    | RejectCheckpoint+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize RejectCode where++    get = getWord8 >>= \code -> case code of+        0x01 -> return RejectMalformed+        0x10 -> return RejectInvalid+        0x11 -> return RejectObsolete+        0x12 -> return RejectDuplicate+        0x40 -> return RejectNonStandard+        0x41 -> return RejectDust+        0x42 -> return RejectInsufficientFee+        0x43 -> return RejectCheckpoint+        _    -> fail $ unwords+            [ "Reject get: Invalid code"+            , show code+            ]++    put code = putWord8 $ case code of+        RejectMalformed       -> 0x01+        RejectInvalid         -> 0x10+        RejectObsolete        -> 0x11+        RejectDuplicate       -> 0x12+        RejectNonStandard     -> 0x40+        RejectDust            -> 0x41+        RejectInsufficientFee -> 0x42+        RejectCheckpoint      -> 0x43++-- | Convenience function to build a 'Reject' message.+reject :: MessageCommand -> RejectCode -> ByteString -> Reject+reject cmd code reason =+    Reject cmd code (VarString reason) B.empty++instance Serialize Reject where+    get =+        S.get >>= \(VarString bs) ->+            Reject (stringToCommand bs) <$> S.get <*> S.get <*> maybeData+      where+        maybeData =+            isEmpty >>= \done ->+                if done+                    then return B.empty+                    else getByteString 32+    put (Reject cmd code reason dat) = do+        put $ VarString $ commandToString cmd+        put code+        put reason+        unless (B.null dat) $ putByteString dat++-- | Data type representing a variable-length integer. The 'VarInt' type+-- usually precedes an array or a string that can vary in length.+newtype VarInt = VarInt { getVarInt :: Word64 }+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize VarInt where++    get = VarInt <$> ( getWord8 >>= go )+      where+        go 0xff = getWord64le+        go 0xfe = fromIntegral <$> getWord32le+        go 0xfd = fromIntegral <$> getWord16le+        go x    = fromIntegral <$> return x++    put (VarInt x)+        | x < 0xfd =+            putWord8 $ fromIntegral x+        | x <= 0xffff = do+            putWord8 0xfd+            putWord16le $ fromIntegral x+        | x <= 0xffffffff = do+            putWord8 0xfe+            putWord32le $ fromIntegral x+        | otherwise = do+            putWord8 0xff+            putWord64le x++putVarInt :: Integral a => a -> Put+putVarInt = put . VarInt . fromIntegral++-- | Data type for serialization of variable-length strings.+newtype VarString = VarString { getVarString :: ByteString }+    deriving (Eq, Show, Read, Generic, NFData)++instance Serialize VarString where++    get = VarString <$> (readBS =<< S.get)+      where+        readBS (VarInt len) = getByteString (fromIntegral len)++    put (VarString bs) = do+        putVarInt $ B.length bs+        putByteString bs++-- | When a bitcoin node creates an outgoing connection to another node,+-- the first message it will send is a 'Version' message. The other node+-- will similarly respond with it's own 'Version' message.+data Version =+    Version {+              -- | protocol version+              version     :: !Word32+              -- | features supported by this connection+            , services    :: !Word64+              -- | unix timestamp+            , timestamp   :: !Word64+              -- | network address of remote node+            , addrRecv    :: !NetworkAddress+              -- | network address of sending node+            , addrSend    :: !NetworkAddress+              -- | random nonce to detect connection to self+            , verNonce    :: !Word64+              -- | user agent string+            , userAgent   :: !VarString+              -- | height of the last block in sending node+            , startHeight :: !Word32+              -- | relay transactions flag (BIP-37)+            , relay       :: !Bool+            } deriving (Eq, Show, Generic, NFData)++instance Serialize Version where++    get = Version <$> getWord32le+                  <*> getWord64le+                  <*> getWord64le+                  <*> S.get+                  <*> S.get+                  <*> getWord64le+                  <*> S.get+                  <*> getWord32le+                  <*> (go =<< isEmpty)+      where+        go True  = return True+        go False = getBool++    put (Version v s t ar as n ua sh r) = do+        putWord32le v+        putWord64le s+        putWord64le t+        put         ar+        put         as+        putWord64le n+        put         ua+        putWord32le sh+        putBool     r++-- | 0x00 is 'False', anything else is 'True'.+getBool :: Get Bool+getBool = go =<< getWord8+  where+    go 0 = return False+    go _ = return True++putBool :: Bool -> Put+putBool True  = putWord8 1+putBool False = putWord8 0++-- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify+-- the type of message present in the payload. This allows the message+-- de-serialization code to know how to decode a particular message payload.+-- Every valid 'Message' constructor has a corresponding 'MessageCommand'+-- constructor.+data MessageCommand+    = MCVersion+    | MCVerAck+    | MCAddr+    | MCInv+    | MCGetData+    | MCNotFound+    | MCGetBlocks+    | MCGetHeaders+    | MCTx+    | MCBlock+    | MCMerkleBlock+    | MCHeaders+    | MCGetAddr+    | MCFilterLoad+    | MCFilterAdd+    | MCFilterClear+    | MCPing+    | MCPong+    | MCAlert+    | MCMempool+    | MCReject+    | MCSendHeaders+    | MCOther ByteString+    deriving (Eq, Generic, NFData)++instance Show MessageCommand where+    showsPrec _ = shows . commandToString++instance Read MessageCommand where+    readPrec = do+        String str <- lexP+        return (stringToCommand (cs str))++instance Serialize MessageCommand where+    get = go <$> getByteString 12+      where+        go bs =+            let str = unpackCommand bs+             in stringToCommand str+    put mc = putByteString $ packCommand $ commandToString mc++instance IsString MessageCommand where+    fromString str = stringToCommand (cs str)++-- | Read a 'MessageCommand' from its string representation.+stringToCommand :: ByteString -> MessageCommand+stringToCommand str = case str of+    "version"     -> MCVersion+    "verack"      -> MCVerAck+    "addr"        -> MCAddr+    "inv"         -> MCInv+    "getdata"     -> MCGetData+    "notfound"    -> MCNotFound+    "getblocks"   -> MCGetBlocks+    "getheaders"  -> MCGetHeaders+    "tx"          -> MCTx+    "block"       -> MCBlock+    "merkleblock" -> MCMerkleBlock+    "headers"     -> MCHeaders+    "getaddr"     -> MCGetAddr+    "filterload"  -> MCFilterLoad+    "filteradd"   -> MCFilterAdd+    "filterclear" -> MCFilterClear+    "ping"        -> MCPing+    "pong"        -> MCPong+    "alert"       -> MCAlert+    "mempool"     -> MCMempool+    "reject"      -> MCReject+    "sendheaders" -> MCSendHeaders+    _             -> MCOther str++-- | Convert a 'MessageCommand' to its string representation.+commandToString :: MessageCommand -> ByteString+commandToString mc = case mc of+    MCVersion     -> "version"+    MCVerAck      -> "verack"+    MCAddr        -> "addr"+    MCInv         -> "inv"+    MCGetData     -> "getdata"+    MCNotFound    -> "notfound"+    MCGetBlocks   -> "getblocks"+    MCGetHeaders  -> "getheaders"+    MCTx          -> "tx"+    MCBlock       -> "block"+    MCMerkleBlock -> "merkleblock"+    MCHeaders     -> "headers"+    MCGetAddr     -> "getaddr"+    MCFilterLoad  -> "filterload"+    MCFilterAdd   -> "filteradd"+    MCFilterClear -> "filterclear"+    MCPing        -> "ping"+    MCPong        -> "pong"+    MCAlert       -> "alert"+    MCMempool     -> "mempool"+    MCReject      -> "reject"+    MCSendHeaders -> "sendheaders"+    MCOther c     -> c++-- | Pack a string 'MessageCommand' so that it is exactly 12-bytes long.+packCommand :: ByteString -> ByteString+packCommand s = B.take 12 $+    s `mappend` C.replicate 12 '\NUL'++-- | Undo packing done by 'packCommand'.+unpackCommand :: ByteString -> ByteString+unpackCommand = B.takeWhile (/= 0)++-- | Node offers no services.+nodeNone :: Word64+nodeNone = 0++-- | Services indicate node is a full node that can serve full blocks.+nodeNetwork :: Word64+nodeNetwork = 1++-- | Services indicate node allows to request 'UTXO' set.+nodeGetUTXO :: Word64+nodeGetUTXO = 1 `shiftL` 1++-- | Services indicate node accepts bloom filters.+nodeBloom :: Word64+nodeBloom = 1 `shiftL` 2++-- | Services indicate SegWit-capable node.+nodeWitness :: Word64+nodeWitness = 1 `shiftL` 3++-- | Services indicate Xtreme Thinblocks compatibility.+nodeXThin :: Word64+nodeXThin = 1 `shiftL` 4
+ src/Haskoin/Network/Message.hs view
@@ -0,0 +1,201 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric  #-}+{-|+Module      : Haskoin.Network.Message+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Peer-to-peer network message serialization.+-}+module Haskoin.Network.Message+    ( -- * Network Message+      Message(..)+    , MessageHeader(..)+    , msgType+    , putMessage+    , getMessage+    ) where++import           Control.DeepSeq+import           Control.Monad              (unless)+import           Data.ByteString            (ByteString)+import qualified Data.ByteString            as BS+import           Data.Serialize             (Serialize, encode, get, put)+import           Data.Serialize.Get         (Get, getByteString, getWord32be,+                                             getWord32le, isolate, lookAhead)+import           Data.Serialize.Put         (Putter, putByteString, putWord32be,+                                             putWord32le)+import           Data.Word                  (Word32)+import           GHC.Generics               (Generic)+import           Haskoin.Block.Common+import           Haskoin.Block.Merkle+import           Haskoin.Constants+import           Haskoin.Crypto.Hash+import           Haskoin.Network.Bloom+import           Haskoin.Network.Common+import           Haskoin.Transaction.Common++-- | Data type representing the header of a 'Message'. All messages sent between+-- nodes contain a message header.+data MessageHeader = MessageHeader+    { -- | magic bytes identify network+      headMagic       :: !Word32+      -- | message type+    , headCmd         :: !MessageCommand+      -- | length of payload+    , headPayloadSize :: !Word32+      -- | checksum of payload+    , headChecksum    :: !CheckSum32+    } deriving (Eq, Show, Generic, NFData)++instance Serialize MessageHeader where++    get = MessageHeader <$> getWord32be+                        <*> get+                        <*> getWord32le+                        <*> get++    put (MessageHeader m c l chk) = do+        putWord32be m+        put         c+        putWord32le l+        put         chk++-- | The 'Message' type is used to identify all the valid messages that can be+-- sent between bitcoin peers. Only values of type 'Message' will be accepted+-- by other bitcoin peers as bitcoin protocol messages need to be correctly+-- serialized with message headers. Serializing a 'Message' value will+-- include the 'MessageHeader' with the correct checksum value automatically.+-- No need to add the 'MessageHeader' separately.+data Message+    = MVersion !Version+    | MVerAck+    | MAddr !Addr+    | MInv !Inv+    | MGetData !GetData+    | MNotFound !NotFound+    | MGetBlocks !GetBlocks+    | MGetHeaders !GetHeaders+    | MTx !Tx+    | MBlock !Block+    | MMerkleBlock !MerkleBlock+    | MHeaders !Headers+    | MGetAddr+    | MFilterLoad !FilterLoad+    | MFilterAdd !FilterAdd+    | MFilterClear+    | MPing !Ping+    | MPong !Pong+    | MAlert !Alert+    | MMempool+    | MReject !Reject+    | MSendHeaders+    | MOther !ByteString !ByteString+    deriving (Eq, Show, Generic, NFData)++-- | Get 'MessageCommand' assocated with a message.+msgType :: Message -> MessageCommand+msgType (MVersion _)     = MCVersion+msgType MVerAck          = MCVerAck+msgType (MAddr _)        = MCAddr+msgType (MInv _)         = MCInv+msgType (MGetData _)     = MCGetData+msgType (MNotFound _)    = MCNotFound+msgType (MGetBlocks _)   = MCGetBlocks+msgType (MGetHeaders _)  = MCGetHeaders+msgType (MTx _)          = MCTx+msgType (MBlock _)       = MCBlock+msgType (MMerkleBlock _) = MCMerkleBlock+msgType (MHeaders _)     = MCHeaders++msgType (MFilterLoad _)  = MCFilterLoad+msgType (MFilterAdd _)   = MCFilterAdd+msgType MFilterClear     = MCFilterClear+msgType (MPing _)        = MCPing+msgType (MPong _)        = MCPong+msgType (MAlert _)       = MCAlert+msgType MMempool         = MCMempool+msgType (MReject _)      = MCReject+msgType MSendHeaders     = MCSendHeaders+msgType MGetAddr         = MCGetAddr+msgType (MOther c _)     = MCOther c++-- | Deserializer for network messages.+getMessage :: Network -> Get Message+getMessage net = do+    (MessageHeader mgc cmd len chk) <- get+    bs <- lookAhead $ getByteString $ fromIntegral len+    unless+        (mgc == getNetworkMagic net)+        (fail $ "get: Invalid network magic bytes: " ++ show mgc)+    unless+        (checkSum32 bs == chk)+        (fail $ "get: Invalid message checksum: " ++ show chk)+    if len > 0+        then isolate (fromIntegral len) $+             case cmd of+                 MCVersion     -> MVersion <$> get+                 MCAddr        -> MAddr <$> get+                 MCInv         -> MInv <$> get+                 MCGetData     -> MGetData <$> get+                 MCNotFound    -> MNotFound <$> get+                 MCGetBlocks   -> MGetBlocks <$> get+                 MCGetHeaders  -> MGetHeaders <$> get+                 MCTx          -> MTx <$> get+                 MCBlock       -> MBlock <$> get+                 MCMerkleBlock -> MMerkleBlock <$> get+                 MCHeaders     -> MHeaders <$> get+                 MCFilterLoad  -> MFilterLoad <$> get+                 MCFilterAdd   -> MFilterAdd <$> get+                 MCPing        -> MPing <$> get+                 MCPong        -> MPong <$> get+                 MCAlert       -> MAlert <$> get+                 MCReject      -> MReject <$> get+                 MCOther c     -> MOther c <$> getByteString (fromIntegral len)+                 _             -> fail $ "get: command " ++ show cmd +++                                         " should not carry a payload"+        else case cmd of+                 MCGetAddr     -> return MGetAddr+                 MCVerAck      -> return MVerAck+                 MCFilterClear -> return MFilterClear+                 MCMempool     -> return MMempool+                 MCSendHeaders -> return MSendHeaders+                 _             -> fail $ "get: command " ++ show cmd +++                                         " is expected to carry a payload"++-- | Serializer for network messages.+putMessage :: Network -> Putter Message+putMessage net msg = do+    let (cmd, payload) =+            case msg of+                MVersion m     -> (MCVersion, encode m)+                MVerAck        -> (MCVerAck, BS.empty)+                MAddr m        -> (MCAddr, encode m)+                MInv m         -> (MCInv, encode m)+                MGetData m     -> (MCGetData, encode m)+                MNotFound m    -> (MCNotFound, encode m)+                MGetBlocks m   -> (MCGetBlocks, encode m)+                MGetHeaders m  -> (MCGetHeaders, encode m)+                MTx m          -> (MCTx, encode m)+                MBlock m       -> (MCBlock, encode m)+                MMerkleBlock m -> (MCMerkleBlock, encode m)+                MHeaders m     -> (MCHeaders, encode m)+                MGetAddr       -> (MCGetAddr, BS.empty)+                MFilterLoad m  -> (MCFilterLoad, encode m)+                MFilterAdd m   -> (MCFilterAdd, encode m)+                MFilterClear   -> (MCFilterClear, BS.empty)+                MPing m        -> (MCPing, encode m)+                MPong m        -> (MCPong, encode m)+                MAlert m       -> (MCAlert, encode m)+                MMempool       -> (MCMempool, BS.empty)+                MReject m      -> (MCReject, encode m)+                MSendHeaders   -> (MCSendHeaders, BS.empty)+                MOther c p     -> (MCOther c, p)+        chk = checkSum32 payload+        len = fromIntegral $ BS.length payload+        header = MessageHeader (getNetworkMagic net) cmd len chk+    put header+    putByteString payload
+ src/Haskoin/Script.hs view
@@ -0,0 +1,19 @@+{-|+Module      : Haskoin.Script+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++This module provides functions for parsing and evaluating bitcoin+transaction scripts. Data types are provided for building and+deconstructing all of the standard input and output script types.+-}+module Haskoin.Script+    ( module X+    ) where++import           Haskoin.Script.Common   as X+import           Haskoin.Script.SigHash  as X+import           Haskoin.Script.Standard as X
+ src/Haskoin/Script/Common.hs view
@@ -0,0 +1,731 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Script.Common+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Common script-related functions and data types.+-}+module Haskoin.Script.Common+    ( ScriptOp(..)+    , Script(..)+    , PushDataType(..)+    , ScriptOutput(..)+    , isPayPK+    , isPayPKHash+    , isPayMulSig+    , isPayScriptHash+    , isPayWitnessPKHash+    , isPayWitnessScriptHash+    , isDataCarrier+    , encodeOutput+    , encodeOutputBS+    , decodeOutput+    , decodeOutputBS+    , toP2SH+    , toP2WSH+    , isPushOp+    , opPushData+    , intToScriptOp+    , scriptOpToInt+    ) where++import           Control.DeepSeq+import           Control.Monad+import           Data.Aeson          as A+import           Data.Aeson.Encoding (text)+import           Data.ByteString     (ByteString)+import qualified Data.ByteString     as B+import           Data.Hashable+import           Data.Serialize      as S+import           Data.Word           (Word8)+import           GHC.Generics        (Generic)+import           Haskoin.Crypto.Hash+import           Haskoin.Keys.Common+import           Haskoin.Util++-- | Data type representing a transaction script. Scripts are defined as lists+-- of script operators 'ScriptOp'. Scripts are used to:+--+-- * Define the spending conditions in the output of a transaction.+-- * Provide signatures in the input of a transaction (except SegWit).+--+-- SigWit only: the segregated witness data structure, and not the input script,+-- contains signatures and redeem script for pay-to-witness-script and+-- pay-to-witness-public-key-hash transactions.+newtype Script =+    Script {+             -- | script operators defining this script+             scriptOps :: [ScriptOp]+           }+    deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance Serialize Script where+    get =+        Script <$> getScriptOps+      where+        getScriptOps = do+            empty <- isEmpty+            if empty+                then return []+                else (:) <$> get <*> getScriptOps++    put (Script ops) = forM_ ops put++-- | Data type representing the type of an OP_PUSHDATA opcode.+data PushDataType+    =+      -- | next opcode bytes is data to be pushed+      OPCODE+      -- | next byte contains number of bytes of data to be pushed+    | OPDATA1+      -- | next two bytes contains number of bytes to be pushed+    | OPDATA2+      -- | next four bytes contains the number of bytes to be pushed+    | OPDATA4+    deriving (Show, Read, Eq, Generic, Hashable, NFData)++-- | Data type representing an operator allowed inside a 'Script'.+data ScriptOp+      -- Pushing Data+    = OP_PUSHDATA !ByteString+                  !PushDataType+    | OP_0+    | OP_1NEGATE+    | OP_RESERVED+    | OP_1+    | OP_2+    | OP_3+    | OP_4+    | OP_5+    | OP_6+    | OP_7+    | OP_8+    | OP_9+    | OP_10+    | OP_11+    | OP_12+    | OP_13+    | OP_14+    | OP_15+    | OP_16+      -- Flow control+    | OP_NOP+    | OP_VER -- reserved+    | OP_IF+    | OP_NOTIF+    | OP_VERIF -- resreved+    | OP_VERNOTIF -- reserved+    | OP_ELSE+    | OP_ENDIF+    | OP_VERIFY+    | OP_RETURN+      -- Stack operations+    | OP_TOALTSTACK+    | OP_FROMALTSTACK+    | OP_IFDUP+    | OP_DEPTH+    | OP_DROP+    | OP_DUP+    | OP_NIP+    | OP_OVER+    | OP_PICK+    | OP_ROLL+    | OP_ROT+    | OP_SWAP+    | OP_TUCK+    | OP_2DROP+    | OP_2DUP+    | OP_3DUP+    | OP_2OVER+    | OP_2ROT+    | OP_2SWAP+      -- Splice+    | OP_CAT+    | OP_SUBSTR+    | OP_LEFT+    | OP_RIGHT+    | OP_SIZE+      -- Bitwise logic+    | OP_INVERT+    | OP_AND+    | OP_OR+    | OP_XOR+    | OP_EQUAL+    | OP_EQUALVERIFY+    | OP_RESERVED1+    | OP_RESERVED2+      -- Arithmetic+    | OP_1ADD+    | OP_1SUB+    | OP_2MUL+    | OP_2DIV+    | OP_NEGATE+    | OP_ABS+    | OP_NOT+    | OP_0NOTEQUAL+    | OP_ADD+    | OP_SUB+    | OP_MUL+    | OP_DIV+    | OP_MOD+    | OP_LSHIFT+    | OP_RSHIFT+    | OP_BOOLAND+    | OP_BOOLOR+    | OP_NUMEQUAL+    | OP_NUMEQUALVERIFY+    | OP_NUMNOTEQUAL+    | OP_LESSTHAN+    | OP_GREATERTHAN+    | OP_LESSTHANOREQUAL+    | OP_GREATERTHANOREQUAL+    | OP_MIN+    | OP_MAX+    | OP_WITHIN+      -- Crypto+    | OP_RIPEMD160+    | OP_SHA1+    | OP_SHA256+    | OP_HASH160+    | OP_HASH256+    | OP_CODESEPARATOR+    | OP_CHECKSIG+    | OP_CHECKSIGVERIFY+    | OP_CHECKMULTISIG+    | OP_CHECKMULTISIGVERIFY+      -- Expansion+    | OP_NOP1+    | OP_NOP2+    | OP_NOP3+    | OP_NOP4+    | OP_NOP5+    | OP_NOP6+    | OP_NOP7+    | OP_NOP8+    | OP_NOP9+    | OP_NOP10+      -- Bitcoin Cash Nov 2018 hard fork+    | OP_CHECKDATASIG+    | OP_CHECKDATASIGVERIFY+      -- Other+    | OP_PUBKEYHASH+    | OP_PUBKEY+    | OP_INVALIDOPCODE !Word8+    deriving (Show, Read, Eq, Generic, Hashable, NFData)++instance Serialize ScriptOp where+    get = go =<< (fromIntegral <$> getWord8)+      where+        go op+            | op == 0x00 = return OP_0+            | op <= 0x4b = do+                payload <- getByteString (fromIntegral op)+                return $ OP_PUSHDATA payload OPCODE+            | op == 0x4c = do+                len  <- getWord8+                payload <- getByteString (fromIntegral len)+                return $ OP_PUSHDATA payload OPDATA1+            | op == 0x4d = do+                len  <- getWord16le+                payload <- getByteString (fromIntegral len)+                return $ OP_PUSHDATA payload OPDATA2+            | op == 0x4e = do+                len  <- getWord32le+                payload <- getByteString (fromIntegral len)+                return $ OP_PUSHDATA payload OPDATA4++            | op == 0x4f = return OP_1NEGATE+            | op == 0x50 = return OP_RESERVED+            | op == 0x51 = return OP_1+            | op == 0x52 = return OP_2+            | op == 0x53 = return OP_3+            | op == 0x54 = return OP_4+            | op == 0x55 = return OP_5+            | op == 0x56 = return OP_6+            | op == 0x57 = return OP_7+            | op == 0x58 = return OP_8+            | op == 0x59 = return OP_9+            | op == 0x5a = return OP_10+            | op == 0x5b = return OP_11+            | op == 0x5c = return OP_12+            | op == 0x5d = return OP_13+            | op == 0x5e = return OP_14+            | op == 0x5f = return OP_15+            | op == 0x60 = return OP_16++            -- Flow control+            | op == 0x61 = return OP_NOP+            | op == 0x62 = return OP_VER        -- reserved+            | op == 0x63 = return OP_IF+            | op == 0x64 = return OP_NOTIF+            | op == 0x65 = return OP_VERIF      -- reserved+            | op == 0x66 = return OP_VERNOTIF   -- reserved+            | op == 0x67 = return OP_ELSE+            | op == 0x68 = return OP_ENDIF+            | op == 0x69 = return OP_VERIFY+            | op == 0x6a = return OP_RETURN++            -- Stack+            | op == 0x6b = return OP_TOALTSTACK+            | op == 0x6c = return OP_FROMALTSTACK+            | op == 0x6d = return OP_2DROP+            | op == 0x6e = return OP_2DUP+            | op == 0x6f = return OP_3DUP+            | op == 0x70 = return OP_2OVER+            | op == 0x71 = return OP_2ROT+            | op == 0x72 = return OP_2SWAP+            | op == 0x73 = return OP_IFDUP+            | op == 0x74 = return OP_DEPTH+            | op == 0x75 = return OP_DROP+            | op == 0x76 = return OP_DUP+            | op == 0x77 = return OP_NIP+            | op == 0x78 = return OP_OVER+            | op == 0x79 = return OP_PICK+            | op == 0x7a = return OP_ROLL+            | op == 0x7b = return OP_ROT+            | op == 0x7c = return OP_SWAP+            | op == 0x7d = return OP_TUCK++            -- Splice+            | op == 0x7e = return OP_CAT+            | op == 0x7f = return OP_SUBSTR+            | op == 0x80 = return OP_LEFT+            | op == 0x81 = return OP_RIGHT+            | op == 0x82 = return OP_SIZE++            -- Bitwise logic+            | op == 0x83 = return OP_INVERT+            | op == 0x84 = return OP_AND+            | op == 0x85 = return OP_OR+            | op == 0x86 = return OP_XOR+            | op == 0x87 = return OP_EQUAL+            | op == 0x88 = return OP_EQUALVERIFY+            | op == 0x89 = return OP_RESERVED1+            | op == 0x8a = return OP_RESERVED2++            -- Arithmetic+            | op == 0x8b = return OP_1ADD+            | op == 0x8c = return OP_1SUB+            | op == 0x8d = return OP_2MUL+            | op == 0x8e = return OP_2DIV+            | op == 0x8f = return OP_NEGATE+            | op == 0x90 = return OP_ABS+            | op == 0x91 = return OP_NOT+            | op == 0x92 = return OP_0NOTEQUAL+            | op == 0x93 = return OP_ADD+            | op == 0x94 = return OP_SUB+            | op == 0x95 = return OP_MUL+            | op == 0x96 = return OP_DIV+            | op == 0x97 = return OP_MOD+            | op == 0x98 = return OP_LSHIFT+            | op == 0x99 = return OP_RSHIFT+            | op == 0x9a = return OP_BOOLAND+            | op == 0x9b = return OP_BOOLOR+            | op == 0x9c = return OP_NUMEQUAL+            | op == 0x9d = return OP_NUMEQUALVERIFY+            | op == 0x9e = return OP_NUMNOTEQUAL+            | op == 0x9f = return OP_LESSTHAN+            | op == 0xa0 = return OP_GREATERTHAN+            | op == 0xa1 = return OP_LESSTHANOREQUAL+            | op == 0xa2 = return OP_GREATERTHANOREQUAL+            | op == 0xa3 = return OP_MIN+            | op == 0xa4 = return OP_MAX+            | op == 0xa5 = return OP_WITHIN++            -- Crypto+            | op == 0xa6 = return OP_RIPEMD160+            | op == 0xa7 = return OP_SHA1+            | op == 0xa8 = return OP_SHA256+            | op == 0xa9 = return OP_HASH160+            | op == 0xaa = return OP_HASH256+            | op == 0xab = return OP_CODESEPARATOR+            | op == 0xac = return OP_CHECKSIG+            | op == 0xad = return OP_CHECKSIGVERIFY+            | op == 0xae = return OP_CHECKMULTISIG+            | op == 0xaf = return OP_CHECKMULTISIGVERIFY++            -- More NOPs+            | op == 0xb0 = return OP_NOP1+            | op == 0xb1 = return OP_NOP2+            | op == 0xb2 = return OP_NOP3+            | op == 0xb3 = return OP_NOP4+            | op == 0xb4 = return OP_NOP5+            | op == 0xb5 = return OP_NOP6+            | op == 0xb6 = return OP_NOP7+            | op == 0xb7 = return OP_NOP8+            | op == 0xb8 = return OP_NOP9+            | op == 0xb9 = return OP_NOP10++            -- Bitcoin Cash Nov 2018 hard fork+            | op == 0xba = return OP_CHECKDATASIG+            | op == 0xbb = return OP_CHECKDATASIGVERIFY++            -- Constants+            | op == 0xfd = return OP_PUBKEYHASH+            | op == 0xfe = return OP_PUBKEY++            | otherwise = return $ OP_INVALIDOPCODE op++    put op = case op of++        (OP_PUSHDATA payload optype)-> do+            let len = B.length payload+            case optype of+                OPCODE -> do+                    unless (len <= 0x4b) $+                        error "OP_PUSHDATA OPCODE: Payload size too big"+                    putWord8 $ fromIntegral len+                OPDATA1 -> do+                    unless (len <= 0xff) $+                        error "OP_PUSHDATA OPDATA1: Payload size too big"+                    putWord8 0x4c+                    putWord8 $ fromIntegral len+                OPDATA2 -> do+                    unless (len <= 0xffff) $+                        error "OP_PUSHDATA OPDATA2: Payload size too big"+                    putWord8 0x4d+                    putWord16le $ fromIntegral len+                OPDATA4 -> do+                    unless (len <= 0x7fffffff) $+                        error "OP_PUSHDATA OPDATA4: Payload size too big"+                    putWord8 0x4e+                    putWord32le $ fromIntegral len+            putByteString payload++        -- Constants+        OP_0                 -> putWord8 0x00+        OP_1NEGATE           -> putWord8 0x4f+        OP_RESERVED          -> putWord8 0x50+        OP_1                 -> putWord8 0x51+        OP_2                 -> putWord8 0x52+        OP_3                 -> putWord8 0x53+        OP_4                 -> putWord8 0x54+        OP_5                 -> putWord8 0x55+        OP_6                 -> putWord8 0x56+        OP_7                 -> putWord8 0x57+        OP_8                 -> putWord8 0x58+        OP_9                 -> putWord8 0x59+        OP_10                -> putWord8 0x5a+        OP_11                -> putWord8 0x5b+        OP_12                -> putWord8 0x5c+        OP_13                -> putWord8 0x5d+        OP_14                -> putWord8 0x5e+        OP_15                -> putWord8 0x5f+        OP_16                -> putWord8 0x60++        -- Crypto Constants+        OP_PUBKEY            -> putWord8 0xfe+        OP_PUBKEYHASH        -> putWord8 0xfd++        -- Invalid Opcodes+        (OP_INVALIDOPCODE x) -> putWord8 x++        -- Flow Control+        OP_NOP               -> putWord8 0x61+        OP_VER               -> putWord8 0x62+        OP_IF                -> putWord8 0x63+        OP_NOTIF             -> putWord8 0x64+        OP_VERIF             -> putWord8 0x65+        OP_VERNOTIF          -> putWord8 0x66+        OP_ELSE              -> putWord8 0x67+        OP_ENDIF             -> putWord8 0x68+        OP_VERIFY            -> putWord8 0x69+        OP_RETURN            -> putWord8 0x6a++        -- Stack Operations+        OP_TOALTSTACK        -> putWord8 0x6b+        OP_FROMALTSTACK      -> putWord8 0x6c+        OP_2DROP             -> putWord8 0x6d+        OP_2DUP              -> putWord8 0x6e+        OP_3DUP              -> putWord8 0x6f+        OP_2OVER             -> putWord8 0x70+        OP_2ROT              -> putWord8 0x71+        OP_2SWAP             -> putWord8 0x72+        OP_IFDUP             -> putWord8 0x73+        OP_DEPTH             -> putWord8 0x74+        OP_DROP              -> putWord8 0x75+        OP_DUP               -> putWord8 0x76+        OP_NIP               -> putWord8 0x77+        OP_OVER              -> putWord8 0x78+        OP_PICK              -> putWord8 0x79+        OP_ROLL              -> putWord8 0x7a+        OP_ROT               -> putWord8 0x7b+        OP_SWAP              -> putWord8 0x7c+        OP_TUCK              -> putWord8 0x7d++        -- Splice+        OP_CAT               -> putWord8 0x7e+        OP_SUBSTR            -> putWord8 0x7f+        OP_LEFT              -> putWord8 0x80+        OP_RIGHT             -> putWord8 0x81+        OP_SIZE              -> putWord8 0x82++        -- Bitwise Logic+        OP_INVERT            -> putWord8 0x83+        OP_AND               -> putWord8 0x84+        OP_OR                -> putWord8 0x85+        OP_XOR               -> putWord8 0x86+        OP_EQUAL             -> putWord8 0x87+        OP_EQUALVERIFY       -> putWord8 0x88+        OP_RESERVED1         -> putWord8 0x89+        OP_RESERVED2         -> putWord8 0x8a++        -- Arithmetic+        OP_1ADD              -> putWord8 0x8b+        OP_1SUB              -> putWord8 0x8c+        OP_2MUL              -> putWord8 0x8d+        OP_2DIV              -> putWord8 0x8e+        OP_NEGATE            -> putWord8 0x8f+        OP_ABS               -> putWord8 0x90+        OP_NOT               -> putWord8 0x91+        OP_0NOTEQUAL         -> putWord8 0x92+        OP_ADD               -> putWord8 0x93+        OP_SUB               -> putWord8 0x94+        OP_MUL               -> putWord8 0x95+        OP_DIV               -> putWord8 0x96+        OP_MOD               -> putWord8 0x97+        OP_LSHIFT            -> putWord8 0x98+        OP_RSHIFT            -> putWord8 0x99+        OP_BOOLAND           -> putWord8 0x9a+        OP_BOOLOR            -> putWord8 0x9b+        OP_NUMEQUAL          -> putWord8 0x9c+        OP_NUMEQUALVERIFY    -> putWord8 0x9d+        OP_NUMNOTEQUAL       -> putWord8 0x9e+        OP_LESSTHAN          -> putWord8 0x9f+        OP_GREATERTHAN       -> putWord8 0xa0+        OP_LESSTHANOREQUAL   -> putWord8 0xa1+        OP_GREATERTHANOREQUAL-> putWord8 0xa2+        OP_MIN               -> putWord8 0xa3+        OP_MAX               -> putWord8 0xa4+        OP_WITHIN            -> putWord8 0xa5++        -- Crypto+        OP_RIPEMD160         -> putWord8 0xa6+        OP_SHA1              -> putWord8 0xa7+        OP_SHA256            -> putWord8 0xa8+        OP_HASH160           -> putWord8 0xa9+        OP_HASH256           -> putWord8 0xaa+        OP_CODESEPARATOR     -> putWord8 0xab+        OP_CHECKSIG          -> putWord8 0xac+        OP_CHECKSIGVERIFY    -> putWord8 0xad+        OP_CHECKMULTISIG     -> putWord8 0xae+        OP_CHECKMULTISIGVERIFY -> putWord8 0xaf++        -- More NOPs+        OP_NOP1              -> putWord8 0xb0+        OP_NOP2              -> putWord8 0xb1+        OP_NOP3              -> putWord8 0xb2+        OP_NOP4              -> putWord8 0xb3+        OP_NOP5              -> putWord8 0xb4+        OP_NOP6              -> putWord8 0xb5+        OP_NOP7              -> putWord8 0xb6+        OP_NOP8              -> putWord8 0xb7+        OP_NOP9              -> putWord8 0xb8+        OP_NOP10             -> putWord8 0xb9++        -- Bitcoin Cash Nov 2018 hard fork+        OP_CHECKDATASIG      -> putWord8 0xba+        OP_CHECKDATASIGVERIFY -> putWord8 0xbb+++-- | Check whether opcode is only data.+isPushOp :: ScriptOp -> Bool+isPushOp op = case op of+    OP_PUSHDATA _ _ -> True+    OP_0            -> True+    OP_1NEGATE      -> True+    OP_1            -> True+    OP_2            -> True+    OP_3            -> True+    OP_4            -> True+    OP_5            -> True+    OP_6            -> True+    OP_7            -> True+    OP_8            -> True+    OP_9            -> True+    OP_10           -> True+    OP_11           -> True+    OP_12           -> True+    OP_13           -> True+    OP_14           -> True+    OP_15           -> True+    OP_16           -> True+    _               -> False++-- | Optimally encode data using one of the 4 types of data pushing opcodes.+opPushData :: ByteString -> ScriptOp+opPushData bs+    | len <= 0x4b       = OP_PUSHDATA bs OPCODE+    | len <= 0xff       = OP_PUSHDATA bs OPDATA1+    | len <= 0xffff     = OP_PUSHDATA bs OPDATA2+    | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4+    | otherwise         = error "opPushData: payload size too big"+  where+    len = B.length bs++-- | Transforms integers @[1 .. 16]@ to 'ScriptOp' @[OP_1 .. OP_16]@.+intToScriptOp :: Int -> ScriptOp+intToScriptOp i+    | i `elem` [1 .. 16] = op+    | otherwise = err+  where+    op = either (const err) id . S.decode . B.singleton . fromIntegral $ i + 0x50+    err = error $ "intToScriptOp: Invalid integer " ++ show i++-- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions+-- fails for other values of 'ScriptOp'+scriptOpToInt :: ScriptOp -> Either String Int+scriptOpToInt s+    | res `elem` [1..16] = return res+    | otherwise          = Left $ "scriptOpToInt: invalid opcode " ++ show s+  where+    res = fromIntegral (B.head $ S.encode s) - 0x50++-- | Data type describing standard transaction output scripts. Output scripts+-- provide the conditions that must be fulfilled for someone to spend the funds+-- in a transaction output.+data ScriptOutput+      -- | pay to public key+    = PayPK { getOutputPubKey :: !PubKeyI }+      -- | pay to public key hash+    | PayPKHash { getOutputHash :: !Hash160 }+      -- | multisig+    | PayMulSig { getOutputMulSigKeys     :: ![PubKeyI]+                , getOutputMulSigRequired :: !Int }+      -- | pay to a script hash+    | PayScriptHash { getOutputHash :: !Hash160 }+      -- | pay to witness public key hash+    | PayWitnessPKHash { getOutputHash :: !Hash160 }+      -- | pay to witness script hash+    | PayWitnessScriptHash { getScriptHash :: !Hash256 }+      -- | provably unspendable data carrier+    | DataCarrier { getOutputData :: !ByteString }+    deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance FromJSON ScriptOutput where+    parseJSON = withText "scriptoutput" $ \t -> either fail return $+        maybeToEither "scriptoutput not hex" (decodeHex t) >>=+        decodeOutputBS++instance ToJSON ScriptOutput where+    toJSON = String . encodeHex . encodeOutputBS+    toEncoding = text . encodeHex . encodeOutputBS++-- | Is script a pay-to-public-key output?+isPayPK :: ScriptOutput -> Bool+isPayPK (PayPK _) = True+isPayPK _         = False++-- | Is script a pay-to-pub-key-hash output?+isPayPKHash :: ScriptOutput -> Bool+isPayPKHash (PayPKHash _) = True+isPayPKHash _             = False++-- | Is script a pay-to-multi-sig output?+isPayMulSig :: ScriptOutput -> Bool+isPayMulSig (PayMulSig _ _) = True+isPayMulSig _               = False++-- | Is script a pay-to-script-hash output?+isPayScriptHash :: ScriptOutput -> Bool+isPayScriptHash (PayScriptHash _) = True+isPayScriptHash _                 = False++-- | Is script a pay-to-witness-pub-key-hash output?+isPayWitnessPKHash :: ScriptOutput -> Bool+isPayWitnessPKHash (PayWitnessPKHash _) = True+isPayWitnessPKHash _                    = False++-- | Is script a pay-to-witness-script-hash output?+isPayWitnessScriptHash :: ScriptOutput -> Bool+isPayWitnessScriptHash (PayWitnessScriptHash _) = True+isPayWitnessScriptHash _                        = False++-- | Is script a data carrier output?+isDataCarrier :: ScriptOutput -> Bool+isDataCarrier (DataCarrier _) = True+isDataCarrier _               = False++-- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the+-- script is not recognized as any of the standard output types.+decodeOutput :: Script -> Either String ScriptOutput+decodeOutput s = case scriptOps s of+    -- Pay to PubKey+    [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs+    -- Pay to PubKey Hash+    [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->+        PayPKHash <$> S.decode bs+    -- Pay to Script Hash+    [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->+        PayScriptHash <$> S.decode  bs+    -- Pay to Witness+    [OP_0, OP_PUSHDATA bs OPCODE]+      | B.length bs == 20 -> PayWitnessPKHash     <$> S.decode bs+      | B.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs+    -- Provably unspendable data carrier output+    [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs+    -- Pay to MultiSig Keys+    _ -> matchPayMulSig s++-- | Similar to 'decodeOutput' but decodes from a 'ByteString'.+decodeOutputBS :: ByteString -> Either String ScriptOutput+decodeOutputBS = decodeOutput <=< S.decode++-- | Computes a 'Script' from a standard 'ScriptOutput'.+encodeOutput :: ScriptOutput -> Script+encodeOutput s = Script $ case s of+    -- Pay to PubKey+    (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]+    -- Pay to PubKey Hash Address+    (PayPKHash h) ->+        [ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]+    -- Pay to MultiSig Keys+    (PayMulSig ps r)+      | r <= length ps ->+        let opM = intToScriptOp r+            opN = intToScriptOp $ length ps+            keys = map (opPushData . S.encode) ps+            in opM : keys ++ [opN, OP_CHECKMULTISIG]+      | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"+    -- Pay to Script Hash Address+    (PayScriptHash h) ->+        [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]+    -- Pay to Witness PubKey Hash Address+    (PayWitnessPKHash h) ->+        [ OP_0, opPushData $ S.encode h ]+    (PayWitnessScriptHash h) ->+        [ OP_0, opPushData $ S.encode h ]+    -- Provably unspendable output+    (DataCarrier d) -> [OP_RETURN, opPushData d]++-- | Similar to 'encodeOutput' but encodes to a ByteString+encodeOutputBS :: ScriptOutput -> ByteString+encodeOutputBS = S.encode . encodeOutput++-- | Encode script as pay-to-script-hash script+toP2SH :: Script -> ScriptOutput+toP2SH = PayScriptHash . addressHash . S.encode++-- | Encode script as a pay-to-witness-script-hash script+toP2WSH :: Script -> ScriptOutput+toP2WSH = PayWitnessScriptHash . sha256 . S.encode++-- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@+matchPayMulSig :: Script -> Either String ScriptOutput+matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of+    (m:xs,[n,OP_CHECKMULTISIG]) -> do+        (intM,intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)+        if intM <= intN && length xs == intN+            then liftM2 PayMulSig (go xs) (return intM)+            else Left "matchPayMulSig: Invalid M or N parameters"+    _ -> Left "matchPayMulSig: script did not match output template"+  where+    go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)+    go []                    = return []+    go  _                    = Left "matchPayMulSig: invalid multisig opcode"
+ src/Haskoin/Script/SigHash.hs view
@@ -0,0 +1,319 @@+{-# LANGUAGE DeriveGeneric              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings          #-}+{-|+Module      : Haskoin.Script.SigHash+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Transaction signatures and related functions.+-}+module Haskoin.Script.SigHash+( SigHash+, SigHashFlag(..)+, sigHashAll+, sigHashNone+, sigHashSingle+, hasAnyoneCanPayFlag+, hasForkIdFlag+, setAnyoneCanPayFlag+, setForkIdFlag+, isSigHashAll+, isSigHashNone+, isSigHashSingle+, isSigHashUnknown+, sigHashAddForkId+, sigHashGetForkId+, sigHashAddNetworkId+, txSigHash+, txSigHashForkId+, TxSignature(..)+, encodeTxSig+, decodeTxSig+) where++import           Control.DeepSeq+import           Control.Monad+import           Crypto.Secp256k1+import qualified Data.Aeson                 as J+import           Data.Bits+import           Data.ByteString            (ByteString)+import qualified Data.ByteString            as BS+import           Data.Hashable+import           Data.Maybe+import           Data.Scientific+import           Data.Serialize+import           Data.Word+import           GHC.Generics               (Generic)+import           Haskoin.Constants+import           Haskoin.Crypto.Hash+import           Haskoin.Crypto.Signature+import           Haskoin.Network+import           Haskoin.Script.Common+import           Haskoin.Transaction.Common+import           Haskoin.Util++-- | Constant representing a SIGHASH flag that controls what is being signed.+data SigHashFlag+    = SIGHASH_ALL+      -- ^ sign all outputs+    | SIGHASH_NONE+      -- ^ sign no outputs+    | SIGHASH_SINGLE+      -- ^ sign the output index corresponding to the input+    | SIGHASH_FORKID+      -- ^ replay protection for Bitcoin Cash transactions+    | SIGHASH_ANYONECANPAY+      -- ^ new inputs can be added+    deriving (Eq, Ord, Show, Read, Generic)++instance NFData SigHashFlag++instance Hashable SigHashFlag++instance Enum SigHashFlag where+    fromEnum SIGHASH_ALL          = 0x01+    fromEnum SIGHASH_NONE         = 0x02+    fromEnum SIGHASH_SINGLE       = 0x03+    fromEnum SIGHASH_FORKID       = 0x40+    fromEnum SIGHASH_ANYONECANPAY = 0x80+    toEnum 0x01 = SIGHASH_ALL+    toEnum 0x02 = SIGHASH_NONE+    toEnum 0x03 = SIGHASH_SINGLE+    toEnum 0x40 = SIGHASH_FORKID+    toEnum 0x80 = SIGHASH_ANYONECANPAY+    toEnum _    = error "Not a valid sighash flag"++-- | Data type representing the different ways a transaction can be signed.+-- When producing a signature, a hash of the transaction is used as the message+-- to be signed. The 'SigHash' parameter controls which parts of the+-- transaction are used or ignored to produce the transaction hash. The idea is+-- that if some part of a transaction is not used to produce the transaction+-- hash, then you can change that part of the transaction after producing a+-- signature without invalidating that signature.+--+-- If the 'SIGHASH_ANYONECANPAY' flag is set (true), then only the current input+-- is signed. Otherwise, all of the inputs of a transaction are signed. The+-- default value for 'SIGHASH_ANYONECANPAY' is unset (false).+newtype SigHash =+    SigHash Word32+    deriving ( Eq+             , Ord+             , Bits+             , Enum+             , Integral+             , Num+             , Real+             , Show+             , Read+             , Generic+             , Hashable+             , NFData+             )++instance J.FromJSON SigHash where+    parseJSON =+        J.withScientific "sighash" $+        maybe mzero (return . SigHash) . toBoundedInteger++instance J.ToJSON SigHash where+    toJSON = J.Number . fromIntegral+    toEncoding (SigHash n) = J.toEncoding n++-- | SIGHASH_NONE as a byte.+sigHashNone :: SigHash+sigHashNone = fromIntegral $ fromEnum SIGHASH_NONE++-- | SIGHASH_ALL as a byte.+sigHashAll :: SigHash+sigHashAll = fromIntegral $ fromEnum SIGHASH_ALL++-- | SIGHASH_SINGLE as a byte.+sigHashSingle :: SigHash+sigHashSingle = fromIntegral $ fromEnum SIGHASH_SINGLE++-- | SIGHASH_FORKID as a byte.+sigHashForkId :: SigHash+sigHashForkId = fromIntegral $ fromEnum SIGHASH_FORKID++-- | SIGHASH_ANYONECANPAY as a byte.+sigHashAnyoneCanPay :: SigHash+sigHashAnyoneCanPay = fromIntegral $ fromEnum SIGHASH_ANYONECANPAY++-- | Set SIGHASH_FORKID flag.+setForkIdFlag :: SigHash -> SigHash+setForkIdFlag = (.|. sigHashForkId)++-- | Set SIGHASH_ANYONECANPAY flag.+setAnyoneCanPayFlag :: SigHash -> SigHash+setAnyoneCanPayFlag = (.|. sigHashAnyoneCanPay)++-- | Is the SIGHASH_FORKID flag set?+hasForkIdFlag :: SigHash -> Bool+hasForkIdFlag = (/= 0) . (.&. sigHashForkId)++-- | Is the SIGHASH_ANYONECANPAY flag set?+hasAnyoneCanPayFlag :: SigHash -> Bool+hasAnyoneCanPayFlag = (/= 0) . (.&. sigHashAnyoneCanPay)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_ALL'.+isSigHashAll :: SigHash -> Bool+isSigHashAll = (== sigHashAll) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_NONE'.+isSigHashNone :: SigHash -> Bool+isSigHashNone = (== sigHashNone) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_SINGLE'.+isSigHashSingle :: SigHash -> Bool+isSigHashSingle = (== sigHashSingle) . (.&. 0x1f)++-- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'.+isSigHashUnknown :: SigHash -> Bool+isSigHashUnknown =+    (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)++-- | Add a fork id to a 'SigHash'.+sigHashAddForkId :: SigHash -> Word32 -> SigHash+sigHashAddForkId sh w = (fromIntegral w `shiftL` 8) .|. (sh .&. 0x000000ff)++-- | Add fork id of a particular network to a 'SigHash'.+sigHashAddNetworkId :: Network -> SigHash -> SigHash+sigHashAddNetworkId net =+    (`sigHashAddForkId` fromMaybe 0 (getSigHashForkId net))++-- | Get fork id from 'SigHash'.+sigHashGetForkId :: SigHash -> Word32+sigHashGetForkId (SigHash n) = fromIntegral $ n `shiftR` 8++-- | Computes the hash that will be used for signing a transaction.+txSigHash :: Network+          -> Tx      -- ^ transaction to sign+          -> Script  -- ^ script from output being spent+          -> Word64  -- ^ value of output being spent+          -> Int     -- ^ index of input being signed+          -> SigHash -- ^ what to sign+          -> Hash256 -- ^ hash to be signed+txSigHash net tx out v i sh+    | hasForkIdFlag sh && isJust (getSigHashForkId net) =+        txSigHashForkId net tx out v i sh+    | otherwise = do+        let newIn = buildInputs (txIn tx) fout i sh+        -- When SigSingle and input index > outputs, then sign integer 1+        fromMaybe one $ do+            newOut <- buildOutputs (txOut tx) i sh+            let newTx = Tx (txVersion tx) newIn newOut [] (txLockTime tx)+            return $+                doubleSHA256 $+                runPut $ do+                    put newTx+                    putWord32le $ fromIntegral sh+  where+    fout = Script $ filter (/= OP_CODESEPARATOR) $ scriptOps out+    one = "0100000000000000000000000000000000000000000000000000000000000000"++-- | Build transaction inputs for computing sighashes.+buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn]+buildInputs txins out i sh+    | hasAnyoneCanPayFlag sh =+        [ (txins !! i) { scriptInput = encode out } ]+    | isSigHashAll sh || isSigHashUnknown sh = single+    | otherwise = zipWith noSeq single [0 ..]+  where+    emptyIn = map (\ti -> ti { scriptInput = BS.empty }) txins+    single =+        updateIndex i emptyIn $ \ti -> ti { scriptInput = encode out }+    noSeq ti j =+        if i == j+        then ti+        else ti { txInSequence = 0 }++-- | Build transaction outputs for computing sighashes.+buildOutputs :: [TxOut] -> Int -> SigHash -> Maybe [TxOut]+buildOutputs txos i sh+    | isSigHashAll sh || isSigHashUnknown sh = return txos+    | isSigHashNone sh = return []+    | i >= length txos = Nothing+    | otherwise = return $ buffer ++ [txos !! i]+  where+    buffer = replicate i $ TxOut maxBound BS.empty++-- | Compute the hash that will be used for signing a transaction. This+-- function is used when the 'SIGHASH_FORKID' flag is set.+txSigHashForkId+    :: Network+    -> Tx      -- ^ transaction to sign+    -> Script  -- ^ script from output being spent+    -> Word64  -- ^ value of output being spent+    -> Int     -- ^ index of input being signed+    -> SigHash -- ^ what to sign+    -> Hash256 -- ^ hash to be signed+txSigHashForkId net tx out v i sh =+    doubleSHA256 . runPut $ do+        putWord32le $ txVersion tx+        put hashPrevouts+        put hashSequence+        put $ prevOutput $ txIn tx !! i+        putScript out+        putWord64le v+        putWord32le $ txInSequence $ txIn tx !! i+        put hashOutputs+        putWord32le $ txLockTime tx+        putWord32le $ fromIntegral $ sigHashAddNetworkId net sh+  where+    hashPrevouts+        | not $ hasAnyoneCanPayFlag sh =+            doubleSHA256 $ runPut $ mapM_ (put . prevOutput) $ txIn tx+        | otherwise = zeros+    hashSequence+        | not (hasAnyoneCanPayFlag sh) &&+              not (isSigHashSingle sh) && not (isSigHashNone sh) =+            doubleSHA256 $ runPut $ mapM_ (putWord32le . txInSequence) $ txIn tx+        | otherwise = zeros+    hashOutputs+        | not (isSigHashSingle sh) && not (isSigHashNone sh) =+            doubleSHA256 $ runPut $ mapM_ put $ txOut tx+        | isSigHashSingle sh && i < length (txOut tx) =+            doubleSHA256 $ encode $ txOut tx !! i+        | otherwise = zeros+    putScript s = do+        let encodedScript = encode s+        putVarInt $ BS.length encodedScript+        putByteString encodedScript+    zeros :: Hash256+    zeros = "0000000000000000000000000000000000000000000000000000000000000000"++-- | Data type representing a signature together with a 'SigHash'. The 'SigHash'+-- is serialized as one byte at the end of an ECDSA 'Sig'. All signatures in+-- transaction inputs are of type 'TxSignature'.+data TxSignature+    = TxSignature { txSignature        :: !Sig+                  , txSignatureSigHash :: !SigHash+                  }+    | TxSignatureEmpty+    deriving (Eq, Show, Generic)++instance NFData TxSignature++-- | Serialize a 'TxSignature'.+encodeTxSig :: TxSignature -> ByteString+encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature"+encodeTxSig (TxSignature sig (SigHash n)) =+    runPut $ putSig sig >> putWord8 (fromIntegral n)++-- | Deserialize a 'TxSignature'.+decodeTxSig :: Network -> ByteString -> Either String TxSignature+decodeTxSig _   bs | BS.null bs = Left "Empty signature candidate"+decodeTxSig net bs =+    case decodeStrictSig $ BS.init bs of+        Just sig -> do+            let sh = fromIntegral $ BS.last bs+            when (isSigHashUnknown sh) $+                Left "Non-canonical signature: unknown hashtype byte"+            when (isNothing (getSigHashForkId net) && hasForkIdFlag sh) $+                Left "Non-canonical signature: invalid network for forkId"+            return $ TxSignature sig sh+        Nothing -> Left "Non-canonical signature: could not parse signature"
+ src/Haskoin/Script/Standard.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Script.Standard+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Standard scripts like pay-to-public-key, pay-to-public-key-hash,+pay-to-script-hash, pay-to-multisig and corresponding SegWit variants.+-}+module Haskoin.Script.Standard+    ( ScriptInput(..)+    , SimpleInput(..)+    , RedeemScript+    , encodeInput+    , encodeInputBS+    , decodeInput+    , decodeInputBS+    , sortMulSig+    , scriptOpToInt+    , isSpendPK+    , isSpendPKHash+    , isSpendMulSig+    , isScriptHashInput+    ) where++import           Control.Applicative    ((<|>))+import           Control.DeepSeq+import           Control.Monad          (guard, (<=<))+import           Data.ByteString        (ByteString)+import           Data.Function          (on)+import           Data.List              (sortBy)+import           Data.Serialize         (decode, encode)+import           GHC.Generics           (Generic)+import           Haskoin.Constants+import           Haskoin.Keys.Common+import           Haskoin.Script.Common+import           Haskoin.Script.SigHash+import           Haskoin.Util+++-- | Data type describing standard transaction input scripts. Input scripts+-- provide the signing data required to unlock the coins of the output they are+-- trying to spend, except in pay-to-witness-public-key-hash and+-- pay-to-script-hash transactions.+data SimpleInput+      -- | spend pay-to-public-key output+    = SpendPK { getInputSig :: !TxSignature+                  -- ^ transaction signature+               }+      -- | spend pay-to-public-key-hash output+    | SpendPKHash { getInputSig :: !TxSignature+                      -- ^ embedded signature+                  , getInputKey :: !PubKeyI+                      -- ^ public key+                   }+      -- | spend multisig output+    | SpendMulSig { getInputMulSigKeys :: ![TxSignature]+                      -- ^ list of signatures+                   }+    deriving (Eq, Show, Generic, NFData)++-- | Returns true if the input script is spending from a pay-to-public-key+-- output.+isSpendPK :: ScriptInput -> Bool+isSpendPK (RegularInput (SpendPK _)) = True+isSpendPK _                          = False++-- | Returns true if the input script is spending from a pay-to-public-key-hash+-- output.+isSpendPKHash :: ScriptInput -> Bool+isSpendPKHash (RegularInput (SpendPKHash _ _)) = True+isSpendPKHash _                                = False++-- | Returns true if the input script is spending a multisig output.+isSpendMulSig :: ScriptInput -> Bool+isSpendMulSig (RegularInput (SpendMulSig _)) = True+isSpendMulSig _                              = False++-- | Returns true if the input script is spending a pay-to-script-hash output.+isScriptHashInput :: ScriptInput -> Bool+isScriptHashInput (ScriptHashInput _ _) = True+isScriptHashInput _                     = False++-- | A redeem script is the output script serialized into the spending input+-- script. It must be included in inputs that spend pay-to-script-hash outputs.+type RedeemScript = ScriptOutput++-- | Standard input script high-level representation.+data ScriptInput+    = RegularInput+          { getRegularInput :: !SimpleInput+            -- ^ get wrapped simple input+          }+    | ScriptHashInput+          { getScriptHashInput  :: !SimpleInput+            -- ^ get simple input associated with redeem script+          , getScriptHashRedeem :: !RedeemScript+            -- ^ redeem script+          }+    deriving (Eq, Show, Generic, NFData)++-- | Heuristic to decode an input script into one of the standard types.+decodeSimpleInput :: Network -> Script -> Either String SimpleInput+decodeSimpleInput net (Script ops) =+    maybeToEither errMsg $ matchPK ops <|> matchPKHash ops <|> matchMulSig ops+  where+    matchPK [op] = SpendPK <$> f op+    matchPK _    = Nothing+    matchPKHash [op, OP_PUSHDATA pub _] =+        SpendPKHash <$> f op <*> eitherToMaybe (decode pub)+    matchPKHash _ = Nothing+    matchMulSig (x:xs) = do+        guard $ x == OP_0+        SpendMulSig <$> mapM f xs+    matchMulSig _ = Nothing+    f OP_0                    = return TxSignatureEmpty+    f (OP_PUSHDATA "" OPCODE) = f OP_0+    f (OP_PUSHDATA bs _)      = eitherToMaybe $ decodeTxSig net bs+    f _                       = Nothing+    errMsg = "decodeInput: Could not decode script input"++-- | Heuristic to decode a 'ScriptInput' from a 'Script'. This function fails if+-- the script can not be parsed as a standard script input.+decodeInput :: Network -> Script -> Either String ScriptInput+decodeInput net s@(Script ops) =+    maybeToEither errMsg $ matchSimpleInput <|> matchPayScriptHash+  where+    matchSimpleInput =+        RegularInput <$> eitherToMaybe (decodeSimpleInput net s)+    matchPayScriptHash =+        case splitAt (length (scriptOps s) - 1) ops of+            (is, [OP_PUSHDATA bs _]) -> do+                rdm <- eitherToMaybe $ decodeOutputBS bs+                inp <- eitherToMaybe $ decodeSimpleInput net $ Script is+                return $ ScriptHashInput inp rdm+            _ -> Nothing+    errMsg = "decodeInput: Could not decode script input"++-- | Like 'decodeInput' but decodes directly from a serialized script+-- 'ByteString'.+decodeInputBS :: Network -> ByteString -> Either String ScriptInput+decodeInputBS net = decodeInput net <=< decode++-- | Encode a standard input into a script.+encodeInput :: ScriptInput -> Script+encodeInput s = case s of+    RegularInput ri -> encodeSimpleInput ri+    ScriptHashInput i o -> Script $+        scriptOps (encodeSimpleInput i) ++ [opPushData $ encodeOutputBS o]++-- | Similar to 'encodeInput' but encodes directly to a serialized script+-- 'ByteString'.+encodeInputBS :: ScriptInput -> ByteString+encodeInputBS = encode . encodeInput++-- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'.+encodeSimpleInput :: SimpleInput -> Script+encodeSimpleInput s =+    Script $+    case s of+        SpendPK ts       -> [f ts]+        SpendPKHash ts p -> [f ts, opPushData $ encode p]+        SpendMulSig xs   -> OP_0 : map f xs+  where+    f TxSignatureEmpty = OP_0+    f ts               = opPushData $ encodeTxSig ts++-- | Sort the public keys of a multisig output in ascending order by comparing+-- their compressed serialized representations. Refer to BIP-67.+sortMulSig :: ScriptOutput -> ScriptOutput+sortMulSig out = case out of+    PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r+    _ -> error "Can only call orderMulSig on PayMulSig scripts"
+ src/Haskoin/Transaction.hs view
@@ -0,0 +1,24 @@+{-|+Module      : Haskoin.Transaction+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Transactions and related code.+-}+module Haskoin.Transaction+    ( module Common+    -- * Builder+    , module Builder+    -- * Segwit+    , module Segwit+    -- * Partial+    , module Partial+    ) where++import           Haskoin.Transaction.Builder as Builder+import           Haskoin.Transaction.Common  as Common+import           Haskoin.Transaction.Partial as Partial+import           Haskoin.Transaction.Segwit  as Segwit
+ src/Haskoin/Transaction/Builder.hs view
@@ -0,0 +1,467 @@+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Transaction.Builder+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Code to simplify transaction creation, signing, fee calculation and coin+selection.+-}+module Haskoin.Transaction.Builder+    ( -- * Transaction Creation & Signing+      buildAddrTx+    , buildTx+    , buildInput+    , SigInput(..)+    , signTx+    , signNestedWitnessTx+    , makeSignature+    , signInput+    , signNestedInput+    , verifyStdTx+    , mergeTxs+    , sigKeys+    , mergeTxInput+    , findSigInput+    , verifyStdInput+      -- * Coin Selection+    , Coin(..)+    , chooseCoins+    , chooseCoinsSink+    , chooseMSCoins+    , chooseMSCoinsSink+    , countMulSig+    , greedyAddSink+    , guessTxFee+    , guessMSTxFee+    , guessTxSize+    , guessMSSize+    ) where++import           Control.Applicative              ((<|>))+import           Control.Arrow                    (first)+import           Control.Monad                    (foldM, unless)+import           Control.Monad.Identity           (runIdentity)+import           Crypto.Secp256k1+import qualified Data.ByteString                  as B+import           Data.Conduit                     (ConduitT, Void, await,+                                                   runConduit, (.|))+import           Data.Conduit.List                (sourceList)+import           Data.Either                      (fromRight)+import           Data.List                        (nub)+import           Data.Maybe                       (catMaybes, fromJust, isJust)+import           Data.Serialize                   (decode, encode)+import           Data.String.Conversions          (cs)+import           Data.Text                        (Text)+import           Data.Word                        (Word64)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Crypto.Hash              (Hash256, addressHash)+import           Haskoin.Crypto.Signature+import           Haskoin.Keys.Common+import           Haskoin.Network.Common+import           Haskoin.Script+import           Haskoin.Transaction.Builder.Sign (SigInput (..), buildInput,+                                                   makeSignature, sigKeys)+import qualified Haskoin.Transaction.Builder.Sign as S+import           Haskoin.Transaction.Common+import           Haskoin.Transaction.Segwit       (decodeWitnessInput, isSegwit,+                                                   viewWitnessProgram)+import           Haskoin.Util++-- | Any type can be used as a Coin if it can provide a value in Satoshi.+-- The value is used in coin selection algorithms.+class Coin c where+    coinValue :: c -> Word64++-- | Coin selection algorithm for normal (non-multisig) transactions. This+-- function returns the selected coins together with the amount of change to+-- send back to yourself, taking the fee into account.+chooseCoins :: Coin c+            => Word64 -- ^ value to send+            -> Word64 -- ^ fee per byte+            -> Int    -- ^ number of outputs (including change)+            -> Bool   -- ^ try to find better solutions+            -> [c]    -- ^ list of ordered coins to choose from+            -> Either String ([c], Word64)+            -- ^ coin selection and change+chooseCoins target fee nOut continue coins =+    runIdentity . runConduit $+    sourceList coins .| chooseCoinsSink target fee nOut continue++-- | Coin selection algorithm for normal (non-multisig) transactions. This+-- function returns the selected coins together with the amount of change to+-- send back to yourself, taking the fee into account. This version uses a Sink+-- for conduit-based coin selection.+chooseCoinsSink :: (Monad m, Coin c)+                => Word64 -- ^ value to send+                -> Word64 -- ^ fee per byte+                -> Int    -- ^ number of outputs (including change)+                -> Bool   -- ^ try to find better solution+                -> ConduitT c Void m (Either String ([c], Word64))+                -- ^ coin selection and change+chooseCoinsSink target fee nOut continue+    | target > 0 =+        maybeToEither err <$>+            greedyAddSink target (guessTxFee fee nOut) continue+    | otherwise = return $ Left "chooseCoins: Target must be > 0"+  where+    err = "chooseCoins: No solution found"++-- | Coin selection algorithm for multisig transactions. This function returns+-- the selected coins together with the amount of change to send back to+-- yourself, taking the fee into account. This function assumes all the coins+-- are script hash outputs that send funds to a multisignature address.+chooseMSCoins :: Coin c+              => Word64     -- ^ value to send+              -> Word64     -- ^ fee per byte+              -> (Int, Int) -- ^ m of n multisig+              -> Int        -- ^ number of outputs (including change)+              -> Bool       -- ^ try to find better solution+              -> [c]+              -> Either String ([c], Word64)+              -- ^ coin selection change amount+chooseMSCoins target fee ms nOut continue coins =+    runIdentity . runConduit $+        sourceList coins .| chooseMSCoinsSink target fee ms nOut continue++-- | Coin selection algorithm for multisig transactions. This function returns+-- the selected coins together with the amount of change to send back to+-- yourself, taking the fee into account. This function assumes all the coins+-- are script hash outputs that send funds to a multisignature address. This+-- version uses a Sink if you need conduit-based coin selection.+chooseMSCoinsSink :: (Monad m, Coin c)+                  => Word64     -- ^ value to send+                  -> Word64     -- ^ fee per byte+                  -> (Int, Int) -- ^ m of n multisig+                  -> Int        -- ^ number of outputs (including change)+                  -> Bool       -- ^ try to find better solution+                  -> ConduitT c Void m (Either String ([c], Word64))+                  -- ^ coin selection and change+chooseMSCoinsSink target fee ms nOut continue+    | target > 0 =+        maybeToEither err <$>+            greedyAddSink target (guessMSTxFee fee ms nOut) continue+    | otherwise = return $ Left "chooseMSCoins: Target must be > 0"+  where+    err = "chooseMSCoins: No solution found"++-- | Select coins greedily by starting from an empty solution. If the 'continue'+-- flag is set, the algorithm will try to find a better solution in the stream+-- after a solution is found. If the next solution found is not strictly better+-- than the previously found solution, the algorithm stops and returns the+-- previous solution. If the continue flag is not set, the algorithm will return+-- the first solution it finds in the stream.+greedyAddSink :: (Monad m, Coin c)+              => Word64          -- ^ value to send+              -> (Int -> Word64) -- ^ coin count to fee function+              -> Bool            -- ^ try to find better solutions+              -> ConduitT c Void m (Maybe ([c], Word64))+              -- ^ coin selection and change+greedyAddSink target guessFee continue =+    go [] 0 [] 0+  where+    -- The goal is the value we must reach (including the fee) for a certain+    -- amount of selected coins.+    goal c = target + guessFee c+    go acc aTot ps pTot = await >>= \case+        -- A coin is available in the stream+        Just coin -> do+            let val = coinValue coin+            -- We have reached the goal using this coin+            if val + aTot >= goal (length acc + 1)+                -- If we want to continue searching for better solutions+                then if continue+                    -- This solution is the first one or+                    -- This solution is better than the previous one+                    then if pTot == 0 || val + aTot < pTot+                        -- Continue searching for better solutions in the stream+                        then go [] 0 (coin:acc) (val + aTot)+                        -- Otherwise, we stop here and return the previous+                        -- solution+                        else return $ Just (ps, pTot - goal (length ps))+                    -- Otherwise, return this solution+                    else return $+                        Just (coin : acc, val + aTot - goal (length acc + 1))+                -- We have not yet reached the goal. Add the coin to the+                -- accumulator+                else go (coin:acc) (val + aTot) ps pTot+        -- We reached the end of the stream+        Nothing ->+            return $ if null ps+                -- If no solution was found, return Nothing+                then Nothing+                -- If we have a solution, return it+                else Just (ps, pTot - goal (length ps))++-- | Estimate tranasction fee to pay based on transaction size estimation.+guessTxFee :: Word64 -> Int -> Int -> Word64+guessTxFee byteFee nOut nIn =+    byteFee * fromIntegral (guessTxSize nIn [] nOut 0)++-- | Same as 'guessTxFee' but for multisig transactions.+guessMSTxFee :: Word64 -> (Int, Int) -> Int -> Int -> Word64+guessMSTxFee byteFee ms nOut nIn =+    byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0)++-- | Computes an upper bound on the size of a transaction based on some known+-- properties of the transaction.+guessTxSize :: Int         -- ^ number of regular transaction inputs+            -> [(Int,Int)] -- ^ multisig m of n for each input+            -> Int         -- ^ number of P2PKH outputs+            -> Int         -- ^ number of P2SH outputs+            -> Int         -- ^ upper bound on transaction size+guessTxSize pki msi pkout msout =+    8 + inpLen + inp + outLen + out+  where+    inpLen = B.length $ encode $ VarInt $ fromIntegral $ length msi + pki+    outLen = B.length $ encode $ VarInt $ fromIntegral $ pkout + msout+    inp = pki * 148 + sum (map guessMSSize msi)+             -- (20: hash160) + (5: opcodes) ++             -- (1: script len) + (8: Word64)+    out =+        pkout * 34 ++             -- (20: hash160) + (3: opcodes) ++             -- (1: script len) + (8: Word64)+        msout * 32++-- | Size of a multisig P2SH input.+guessMSSize :: (Int,Int) -> Int+guessMSSize (m, n)+    -- OutPoint (36) + Sequence (4) + Script+ = 40 + fromIntegral (B.length $ encode $ VarInt $ fromIntegral scp) + scp+    -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG+  where+    rdm =+        fromIntegral $+        B.length $ encode $ opPushData $ B.replicate (n * 34 + 3) 0+    -- Redeem + m*sig + OP_0+    scp = rdm + m * 73 + 1++{- Build a new Tx -}++-- | Build a transaction by providing a list of outpoints as inputs+-- and a list of recipient addresses and amounts as outputs.+buildAddrTx :: Network -> [OutPoint] -> [(Text, Word64)] -> Either String Tx+buildAddrTx net xs ys = buildTx xs =<< mapM f ys+  where+    f (s, v) =+        maybe (Left ("buildAddrTx: Invalid address " ++ cs s)) Right $ do+            a <- stringToAddr net s+            let o = addressToOutput a+            return (o, v)++-- | Build a transaction by providing a list of outpoints as inputs+-- and a list of 'ScriptOutput' and amounts as outputs.+buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Either String Tx+buildTx xs ys =+    mapM fo ys >>= \os -> return $ Tx 1 (map fi xs) os [] 0+  where+    fi outPoint = TxIn outPoint B.empty maxBound+    fo (o, v)+        | v <= 2100000000000000 = return $ TxOut v $ encodeOutputBS o+        | otherwise =+            Left $ "buildTx: Invalid amount " ++ show v++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed deterministically as defined+-- in RFC-6979.+--+-- Example: P2SH-P2WKH+--+-- > sigIn = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing+-- > signedTx = signTx btc unsignedTx [sigIn] [key]+--+-- Example: P2SH-P2WSH multisig+--+-- > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)+-- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]+signTx :: Network+       -> Tx               -- ^ transaction to sign+       -> [SigInput]       -- ^ signing parameters+       -> [SecKey]         -- ^ private keys to sign with+       -> Either String Tx -- ^ signed transaction+signTx net tx si = S.signTx net tx $ notNested <$> si+  where notNested s = (s, False)++-- | This function differs from 'signTx' by assuming all segwit inputs are+-- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.+signNestedWitnessTx :: Network+                    -> Tx               -- ^ transaction to sign+                    -> [SigInput]       -- ^ signing parameters+                    -> [SecKey]         -- ^ private keys to sign with+                    -> Either String Tx -- ^ signed transaction+signNestedWitnessTx net tx si = S.signTx net tx $ nested <$> si+  where+    -- NOTE: the nesting flag is ignored for non-segwit inputs+    nested s = (s, True)+++-- | Sign a single input in a transaction deterministically (RFC-6979).+signInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx+signInput net tx i si = S.signInput net tx i (si, False)++-- | Like 'signInput' but treat segwit inputs as nested+signNestedInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx+signNestedInput net tx i si = S.signInput net tx i (si, True)++-- | Order the 'SigInput' with respect to the transaction inputs. This allows+-- the user to provide the 'SigInput' in any order. Users can also provide only+-- a partial set of 'SigInput' entries.+findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]+findSigInput = S.findInputIndex sigInputOP++{- Merge multisig transactions -}++-- | Merge partially-signed multisig transactions.  This function does not+-- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with+-- segwit inputs.+mergeTxs :: Network -> [Tx] -> [(ScriptOutput, Word64, OutPoint)] -> Either String Tx+mergeTxs net txs os+    | null txs = Left "Transaction list is empty"+    | length (nub emptyTxs) /= 1 = Left "Transactions do not match"+    | length txs == 1 = return $ head txs+    | otherwise = foldM (mergeTxInput net txs) (head emptyTxs) outs+  where+    zipOp = zip (matchTemplate os (txIn $ head txs) f) [0..]+    outs = map (first $ (\(o,v,_) -> (o,v)) . fromJust) $ filter (isJust . fst) zipOp+    f (_, _, o) txin = o == prevOutput txin+    emptyTxs = map (\tx -> foldl clearInput tx outs) txs+    ins is i = updateIndex i is (\ti -> ti{ scriptInput = B.empty })+    clearInput tx (_, i) =+        Tx (txVersion tx) (ins (txIn tx) i) (txOut tx) [] (txLockTime tx)++-- | Merge input from partially-signed multisig transactions.  This function+-- does not support segwit and P2SH-segwit inputs.+mergeTxInput ::+       Network+    -> [Tx]+    -> Tx+    -> ((ScriptOutput, Word64), Int)+    -> Either String Tx+mergeTxInput net txs tx ((so, val), i)+    -- Ignore transactions with empty inputs+ = do+    let ins = map (scriptInput . (!! i) . txIn) txs+    sigRes <- mapM extractSigs $ filter (not . B.null) ins+    let rdm = snd $ head sigRes+    unless (all (== rdm) $ map snd sigRes) $ Left "Redeem scripts do not match"+    si <- encodeInputBS <$> go (nub $ concatMap fst sigRes) so rdm+    let ins' = updateIndex i (txIn tx) (\ti -> ti {scriptInput = si})+    return $ Tx (txVersion tx) ins' (txOut tx) [] (txLockTime tx)+  where+    go allSigs out rdmM =+        case out of+            PayMulSig msPubs r ->+                let sigs =+                        take r $+                        catMaybes $ matchTemplate allSigs msPubs $ f out+                 in return $ RegularInput $ SpendMulSig sigs+            PayScriptHash _ ->+                case rdmM of+                    Just rdm -> do+                        si <- go allSigs rdm Nothing+                        return $ ScriptHashInput (getRegularInput si) rdm+                    _ -> Left "Invalid output script type"+            _ -> Left "Invalid output script type"+    extractSigs si =+        case decodeInputBS net si of+            Right (RegularInput (SpendMulSig sigs)) -> Right (sigs, Nothing)+            Right (ScriptHashInput (SpendMulSig sigs) rdm) ->+                Right (sigs, Just rdm)+            _ -> Left "Invalid script input type"+    f out (TxSignature x sh) p =+        verifyHashSig+            (txSigHash net tx (encodeOutput out) val i sh)+            x+            (pubKeyPoint p)+    f _ TxSignatureEmpty _ = False++{- Tx verification -}++-- | Verify if a transaction is valid and all of its inputs are standard.+verifyStdTx :: Network -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool+verifyStdTx net tx xs =+    not (null (txIn tx)) && all go (zip (matchTemplate xs (txIn tx) f) [0 ..])+  where+    f (_, _, o) txin = o == prevOutput txin+    go (Just (so, val, _), i) = verifyStdInput net tx i so val+    go _                      = False++-- | Verify if a transaction input is valid and standard.+verifyStdInput :: Network -> Tx -> Int -> ScriptOutput -> Word64 -> Bool+verifyStdInput net tx i so0 val+    | isSegwit so0 = fromRight False $ (inp == mempty &&) . verifySegwitInput so0 <$> wp so0+    | otherwise    = fromRight False+                   $ verifyLegacyInput so0 <$> decodeInputBS net inp+                 <|> (nestedScriptOutput >>= \so -> verifyNestedInput so0 so <$> wp so)+  where+    verifyLegacyInput so si = case (so, si) of+        (PayPK pub, RegularInput (SpendPK (TxSignature sig sh))) ->+            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)+        (PayPKHash h, RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->+            pubKeyAddr pub == p2pkhAddr h &&+            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)+        (PayMulSig pubs r, RegularInput (SpendMulSig sigs)) ->+            countMulSig net tx out val i (pubKeyPoint <$> pubs) sigs == r+        (PayScriptHash h, ScriptHashInput si' rdm) ->+            payToScriptAddress rdm == p2shAddr h && verifyLegacyInput rdm (RegularInput si')+        _ -> False+      where out = encodeOutput so++    verifySegwitInput so (rdm, si) = case (so, rdm, si) of+        (PayWitnessPKHash h, Nothing, SpendPKHash (TxSignature sig sh) pub) ->+            pubKeyWitnessAddr pub == p2wpkhAddr h &&+            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)+        (PayWitnessScriptHash h, Just rdm'@(PayPK pub), SpendPK (TxSignature sig sh)) ->+            payToWitnessScriptAddress rdm' == p2wshAddr h &&+            verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub)+        (PayWitnessScriptHash h, Just rdm'@(PayPKHash kh), SpendPKHash (TxSignature sig sh) pub) ->+            payToWitnessScriptAddress rdm' == p2wshAddr h &&+            addressHash (encode pub) == kh &&+            verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub)+        (PayWitnessScriptHash h, Just rdm'@(PayMulSig pubs r), SpendMulSig sigs) ->+            payToWitnessScriptAddress rdm' == p2wshAddr h &&+            countMulSig' (\sh -> theTxSigHash so sh $ Just rdm') (pubKeyPoint <$> pubs) sigs == r+        _ -> False++    verifyNestedInput so so' x = case so of+        PayScriptHash h -> payToScriptAddress so' == p2shAddr h && verifySegwitInput so' x+        _               -> False++    inp             = scriptInput $ txIn tx !! i+    theTxSigHash so = S.makeSigHash net tx i so val++    ws | length (txWitness tx) > i = txWitness tx !! i+       | otherwise                 = []+    wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws++    nestedScriptOutput = scriptOps <$> decode inp >>= \case+        [OP_PUSHDATA bs _] -> decodeOutputBS bs+        _                  -> Left "nestedScriptOutput: not a nested output"++-- | Count the number of valid signatures for a multi-signature transaction.+countMulSig ::+       Network+    -> Tx+    -> Script+    -> Word64+    -> Int+    -> [PubKey]+    -> [TxSignature]+    -> Int+countMulSig net tx out val i = countMulSig' h+  where+    h = txSigHash net tx out val i++countMulSig' :: (SigHash -> Hash256) -> [PubKey] -> [TxSignature] -> Int+countMulSig' _ [] _ = 0+countMulSig' _ _ [] = 0+countMulSig' h (_:pubs) (TxSignatureEmpty:sigs) = countMulSig' h pubs sigs+countMulSig' h (pub:pubs) sigs@(TxSignature sig sh : sigs')+    | verifyHashSig (h sh) sig pub = 1 + countMulSig' h pubs sigs'+    | otherwise                    = countMulSig' h pubs sigs
+ src/Haskoin/Transaction/Builder/Sign.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Transaction.Builder.Sign+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Types and logic for signing transactions.+-}+module Haskoin.Transaction.Builder.Sign+    ( SigInput (..)+    , makeSignature+    , makeSigHash+    , signTx+    , findInputIndex+    , signInput+    , buildInput+    , sigKeys+    ) where++import           Control.DeepSeq            (NFData)+import           Control.Monad              (foldM, mzero, when)+import           Data.Aeson                 (FromJSON, ToJSON (..),+                                             Value (Object), object, pairs,+                                             parseJSON, (.:), (.:?), (.=))+import           Data.Either                (rights)+import           Data.Hashable              (Hashable)+import           Data.List                  (find, nub)+import           Data.Maybe                 (catMaybes, fromMaybe, mapMaybe,+                                             maybeToList)+import qualified Data.Serialize             as S+import           Data.Word                  (Word64)+import           GHC.Generics               (Generic)++import           Haskoin.Address            (getAddrHash160, pubKeyAddr)+import           Haskoin.Constants          (Network)+import           Haskoin.Crypto             (Hash256, SecKey)+import           Haskoin.Crypto.Signature   (signHash, verifyHashSig)+import           Haskoin.Keys.Common        (PubKeyI (..), SecKeyI (..),+                                             derivePubKeyI, wrapSecKey)+import           Haskoin.Script.Common      (ScriptOutput (..), encodeOutput,+                                             encodeOutputBS, opPushData)+import           Haskoin.Script.SigHash     (SigHash, TxSignature (..),+                                             decodeTxSig, txSigHash,+                                             txSigHashForkId)+import           Haskoin.Script.Standard    (RedeemScript, ScriptInput (..),+                                             SimpleInput (..), decodeInputBS,+                                             encodeInputBS)+import           Haskoin.Transaction.Common (OutPoint, Tx (..), TxIn (..),+                                             WitnessData)+import           Haskoin.Transaction.Segwit (WitnessProgram (..),+                                             calcWitnessProgram, isSegwit,+                                             toWitnessStack)+import           Haskoin.Util               (matchTemplate, updateIndex)++-- | Data type used to specify the signing parameters of a transaction input.+-- To sign an input, the previous output script, outpoint and sighash are+-- required. When signing a pay to script hash output, an additional redeem+-- script is required.+data SigInput = SigInput+    { sigInputScript :: !ScriptOutput         -- ^ output script to spend+    , sigInputValue  :: !Word64               -- ^ output script value+    , sigInputOP     :: !OutPoint             -- ^ outpoint to spend+    , sigInputSH     :: !SigHash              -- ^ signature type+    , sigInputRedeem :: !(Maybe RedeemScript) -- ^ redeem script+    } deriving (Eq, Show, Read, Generic, Hashable, NFData)++instance ToJSON SigInput where+    toJSON (SigInput so val op sh rdm) = object $+        [ "pkscript" .= so+        , "value"    .= val+        , "outpoint" .= op+        , "sighash"  .= sh+        ] ++ [ "redeem" .= r | r <- maybeToList rdm ]+    toEncoding (SigInput so val op sh rdm) = pairs $+        "pkscript" .= so+        <> "value"    .= val+        <> "outpoint" .= op+        <> "sighash"  .= sh+        <> (case rdm of Nothing -> mempty+                        Just r  -> "redeem" .= r)++instance FromJSON SigInput where+    parseJSON (Object o) = do+        so  <- o .: "pkscript"+        val <- o .: "value"+        op  <- o .: "outpoint"+        sh  <- o .: "sighash"+        rdm <- o .:? "redeem"+        return $ SigInput so val op sh rdm+    parseJSON _ = mzero++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed deterministically as defined+-- in RFC-6979.+signTx :: Network+       -> Tx                 -- ^ transaction to sign+       -> [(SigInput, Bool)] -- ^ signing parameters, with nesting flag+       -> [SecKey]           -- ^ private keys to sign with+       -> Either String Tx   -- ^ signed transaction+signTx net otx sigis allKeys+    | null ti   = Left "signTx: Transaction has no inputs"+    | otherwise = foldM go otx $ findInputIndex (sigInputOP . fst) sigis ti+  where+    ti = txIn otx+    go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do+        keys <- sigKeys so rdmM allKeys+        foldM (\t k -> signInput net t i sigi k) tx keys++-- | Sign a single input in a transaction deterministically (RFC-6979).  The+-- nesting flag only affects the behavior of segwit inputs.+signInput ::+       Network+    -> Tx+    -> Int+    -> (SigInput, Bool) -- ^ boolean flag: nest input+    -> SecKeyI+    -> Either String Tx+signInput net tx i (sigIn@(SigInput so val _ _ rdmM), nest) key = do+    let sig = makeSignature net tx i sigIn key+    si <- buildInput net tx i so val rdmM sig $ derivePubKeyI key+    w  <- updatedWitnessData tx i so si+    return tx { txIn      = nextTxIn so si+              , txWitness = w+              }+  where+    f si x = x {scriptInput = encodeInputBS si}+    g so' x = x {scriptInput = S.encode . opPushData $ encodeOutputBS so'}+    txis = txIn tx+    nextTxIn so' si+        | isSegwit so' && nest = updateIndex i txis (g so')+        | isSegwit so'         = txIn tx+        | otherwise            = updateIndex i txis (f si)++-- | Add the witness data of the transaction given segwit parameters for an input.+--+-- @since 0.11.0.0+updatedWitnessData :: Tx -> Int -> ScriptOutput -> ScriptInput -> Either String WitnessData+updatedWitnessData tx i so si+    | isSegwit so = updateWitness . toWitnessStack =<< calcWitnessProgram so si+    | otherwise   = return $ txWitness tx+  where+    updateWitness w+        | null $ txWitness tx        = return $ updateIndex i defaultStack (const w)+        | length (txWitness tx) /= n = Left "Invalid number of witness stacks"+        | otherwise                  = return $ updateIndex i (txWitness tx) (const w)+    defaultStack = replicate n $ toWitnessStack EmptyWitnessProgram+    n = length $ txIn tx++-- | Associate an input index to each value in a list+findInputIndex ::+       (a -> OutPoint) -- ^ extract an outpoint+    -> [a]             -- ^ input list+    -> [TxIn]          -- ^ reference list of inputs+    -> [(a, Int)]+findInputIndex getOutPoint as ti =+    mapMaybe g $ zip (matchTemplate as ti f) [0..]+  where+    f s txin = getOutPoint s == prevOutput txin+    g (Just s, i)  = Just (s,i)+    g (Nothing, _) = Nothing++-- | Find from the list of provided private keys which one is required to sign+-- the 'ScriptOutput'.+sigKeys ::+       ScriptOutput+    -> Maybe RedeemScript+    -> [SecKey]+    -> Either String [SecKeyI]+sigKeys so rdmM keys =+    case (so, rdmM) of+        (PayPK p, Nothing) ->+            return . map fst . maybeToList $ find ((== p) . snd) zipKeys+        (PayPKHash h, Nothing) -> return $ keyByHash h+        (PayMulSig ps r, Nothing) ->+            return $ map fst $ take r $ filter ((`elem` ps) . snd) zipKeys+        (PayScriptHash _, Just rdm) -> sigKeys rdm Nothing keys+        (PayWitnessPKHash h, _) -> return $ keyByHash h+        (PayWitnessScriptHash _, Just rdm) -> sigKeys rdm Nothing keys+        _ -> Left "sigKeys: Could not decode output script"+  where+    zipKeys =+        [ (prv, pub)+        | k <- keys+        , t <- [True, False]+        , let prv = wrapSecKey t k+        , let pub = derivePubKeyI prv+        ]+    keyByHash h = fmap fst . maybeToList . findKey h $ zipKeys+    findKey h   = find $ (== h) . getAddrHash160 . pubKeyAddr . snd++-- | Construct an input for a transaction given a signature, public key and data+-- about the previous output.+buildInput ::+       Network+    -> Tx                 -- ^ transaction where input will be added+    -> Int                -- ^ input index where signature will go+    -> ScriptOutput       -- ^ output script being spent+    -> Word64             -- ^ amount of previous output+    -> Maybe RedeemScript -- ^ redeem script if pay-to-script-hash+    -> TxSignature+    -> PubKeyI+    -> Either String ScriptInput+buildInput net tx i so val rdmM sig pub = do+    when (i >= length (txIn tx)) $ Left "buildInput: Invalid input index"+    case (so, rdmM) of+        (PayScriptHash _, Just rdm)        -> buildScriptHashInput rdm+        (PayWitnessScriptHash _, Just rdm) -> buildScriptHashInput rdm+        (PayWitnessPKHash _, Nothing)      -> return . RegularInput $ SpendPKHash sig pub+        (_, Nothing)                       -> buildRegularInput so+        _ -> Left "buildInput: Invalid output/redeem script combination"+  where+    buildRegularInput = \case+        PayPK _ -> return $ RegularInput $ SpendPK sig+        PayPKHash _ -> return $ RegularInput $ SpendPKHash sig pub+        PayMulSig msPubs r -> do+            let mSigs   = take r $ catMaybes $ matchTemplate allSigs msPubs f+                allSigs = nub $ sig : parseExistingSigs net tx so i+            return $ RegularInput $ SpendMulSig mSigs+        _ -> Left "buildInput: Invalid output/redeem script combination"+    buildScriptHashInput rdm = do+        inp <- buildRegularInput rdm+        return $ ScriptHashInput (getRegularInput inp) rdm+    f (TxSignature x sh) p =+        verifyHashSig (makeSigHash net tx i so val sh rdmM) x (pubKeyPoint p)+    f TxSignatureEmpty _ = False++-- | Apply heuristics to extract the signatures for a particular input that are+-- embedded in the transaction.+--+-- @since 0.11.0.0+parseExistingSigs :: Network -> Tx -> ScriptOutput -> Int -> [TxSignature]+parseExistingSigs net tx so i = insSigs <> witSigs+  where+    insSigs = case decodeInputBS net scp of+            Right (ScriptHashInput (SpendMulSig xs) _) -> xs+            Right (RegularInput (SpendMulSig xs))      -> xs+            _                                          -> []+    scp = scriptInput $ txIn tx !! i+    witSigs+        | not $ isSegwit so   = []+        | null $ txWitness tx = []+        | otherwise           = rights $ decodeTxSig net <$> (txWitness tx !! i)++-- | Produce a structured representation of a deterministic (RFC-6979) signature over an input.+makeSignature :: Network -> Tx -> Int -> SigInput -> SecKeyI -> TxSignature+makeSignature net tx i (SigInput so val _ sh rdmM) key =+    TxSignature (signHash (secKeyData key) m) sh+  where+    m = makeSigHash net tx i so val sh rdmM++-- | A function which selects the digest algorithm and parameters as appropriate+--+-- @since 0.11.0.0+makeSigHash ::+       Network+    -> Tx+    -> Int+    -> ScriptOutput+    -> Word64+    -> SigHash+    -> Maybe RedeemScript+    -> Hash256+makeSigHash net tx i so val sh rdmM = h net tx (encodeOutput so') val i sh+  where+    so' = case so of+        PayWitnessPKHash h' -> PayPKHash h'+        _                   -> fromMaybe so rdmM+    h | isSegwit so = txSigHashForkId+      | otherwise   = txSigHash
+ src/Haskoin/Transaction/Common.hs view
@@ -0,0 +1,312 @@+{-# LANGUAGE DeriveAnyClass    #-}+{-# LANGUAGE DeriveGeneric     #-}+{-# LANGUAGE OverloadedStrings #-}+{-|+Module      : Haskoin.Transaction.Common+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Code related to transactions parsing and serialization.+-}+module Haskoin.Transaction.Common+    ( Tx(..)+    , TxIn(..)+    , TxOut(..)+    , OutPoint(..)+    , TxHash(..)+    , WitnessData+    , WitnessStack+    , WitnessStackItem+    , txHash+    , hexToTxHash+    , txHashToHex+    , nosigTxHash+    , nullOutPoint+    , genesisTx+    ) where++import           Control.Applicative     ((<|>))+import           Control.DeepSeq+import           Control.Monad           (forM_, guard, liftM2, mzero,+                                          replicateM, (<=<))+import           Data.Aeson              as A+import           Data.Aeson.Encoding     (unsafeToEncoding)+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as B+import           Data.ByteString.Builder (char7)+import           Data.Hashable           (Hashable)+import           Data.Maybe              (fromMaybe)+import           Data.Serialize          as S+import           Data.String             (IsString, fromString)+import           Data.String.Conversions (cs)+import           Data.Text               (Text)+import           Data.Word               (Word32, Word64)+import           GHC.Generics            (Generic)+import           Haskoin.Crypto.Hash+import           Haskoin.Network.Common+import           Haskoin.Script.Common+import           Haskoin.Util+import           Text.Read               as R++-- | Transaction id: hash of transaction excluding witness data.+newtype TxHash = TxHash { getTxHash :: Hash256 }+    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)++instance Show TxHash where+    showsPrec _ = shows . txHashToHex++instance Read TxHash where+    readPrec = do+        R.String str <- R.lexP+        maybe R.pfail return $ hexToTxHash $ cs str++instance IsString TxHash where+    fromString s =+        let e = error "Could not read transaction hash from hex string"+        in fromMaybe e $ hexToTxHash $ cs s++instance FromJSON TxHash where+    parseJSON = withText "txid" $+        maybe mzero return . hexToTxHash++instance ToJSON TxHash where+    toJSON = A.String . txHashToHex+    toEncoding (TxHash h) =+        unsafeToEncoding $+        char7 '"' <> hexBuilder (B.reverse (S.encode h)) <> char7 '"'++-- | Transaction hash excluding signatures.+nosigTxHash :: Tx -> TxHash+nosigTxHash tx =+    TxHash $ doubleSHA256 $ S.encode tx { txIn = map clearInput $ txIn tx }+  where+    clearInput ti = ti { scriptInput = B.empty }++-- | Convert transaction hash to hex form, reversing bytes.+txHashToHex :: TxHash -> Text+txHashToHex (TxHash h) = encodeHex (B.reverse (S.encode h))++-- | Convert transaction hash from hex, reversing bytes.+hexToTxHash :: Text -> Maybe TxHash+hexToTxHash hex = do+    bs <- B.reverse <$> decodeHex hex+    h <- either (const Nothing) Just (S.decode bs)+    return $ TxHash h++-- | Witness stack for SegWit transactions.+type WitnessData = [WitnessStack]+-- | Witness stack for SegWit transactions.+type WitnessStack = [WitnessStackItem]+-- | Witness stack item for SegWit transactions.+type WitnessStackItem = ByteString++-- | Data type representing a transaction.+data Tx = Tx+    { -- | transaction data format version+      txVersion  :: !Word32+      -- | list of transaction inputs+    , txIn       :: ![TxIn]+      -- | list of transaction outputs+    , txOut      :: ![TxOut]+      -- | witness data for the transaction+    , txWitness  :: !WitnessData+      -- | earliest mining height or time+    , txLockTime :: !Word32+    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++-- | Compute transaction hash.+txHash :: Tx -> TxHash+txHash tx = TxHash (doubleSHA256 (S.encode tx {txWitness = []}))++instance IsString Tx where+    fromString =+        fromMaybe e . (eitherToMaybe . S.decode <=< decodeHex) . cs+      where+        e = error "Could not read transaction from hex string"++instance Serialize Tx where+    get = parseWitnessTx <|> parseLegacyTx+    put tx+        | null (txWitness tx) = putLegacyTx tx+        | otherwise = putWitnessTx tx++putInOut :: Tx -> Put+putInOut tx = do+    putVarInt $ length (txIn tx)+    forM_ (txIn tx) put+    putVarInt $ length (txOut tx)+    forM_ (txOut tx) put++-- | Non-SegWit transaction serializer.+putLegacyTx :: Tx -> Put+putLegacyTx tx = do+    putWord32le (txVersion tx)+    putInOut tx+    putWord32le (txLockTime tx)++-- | Witness transaciton serializer.+putWitnessTx :: Tx -> Put+putWitnessTx tx = do+    putWord32le (txVersion tx)+    putWord8 0x00+    putWord8 0x01+    putInOut tx+    putWitnessData (txWitness tx)+    putWord32le (txLockTime tx)++-- | Non-SegWit transaction deseralizer.+parseLegacyTx :: Get Tx+parseLegacyTx = do+    v <- getWord32le+    is <- replicateList =<< S.get+    os <- replicateList =<< S.get+    l <- getWord32le+    return+        Tx+        {txVersion = v, txIn = is, txOut = os, txWitness = [], txLockTime = l}+  where+    replicateList (VarInt c) = replicateM (fromIntegral c) S.get++-- | Witness transaction deserializer.+parseWitnessTx :: Get Tx+parseWitnessTx = do+    v <- getWord32le+    m <- getWord8+    f <- getWord8+    guard $ m == 0x00+    guard $ f == 0x01+    is <- replicateList =<< S.get+    os <- replicateList =<< S.get+    w <- parseWitnessData $ length is+    l <- getWord32le+    return+        Tx {txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l}+  where+    replicateList (VarInt c) = replicateM (fromIntegral c) S.get++-- | Witness data deserializer. Requires count of inputs.+parseWitnessData :: Int -> Get WitnessData+parseWitnessData n = replicateM n parseWitnessStack+  where+    parseWitnessStack = do+        VarInt i <- S.get+        replicateM (fromIntegral i) parseWitnessStackItem+    parseWitnessStackItem = do+        VarInt i <- S.get+        getByteString $ fromIntegral i++-- | Witness data serializer.+putWitnessData :: WitnessData -> Put+putWitnessData = mapM_ putWitnessStack+  where+    putWitnessStack ws = do+        putVarInt $ length ws+        mapM_ putWitnessStackItem ws+    putWitnessStackItem bs = do+        putVarInt $ B.length bs+        putByteString bs++instance FromJSON Tx where+    parseJSON = withText "Tx" $+        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)++instance ToJSON Tx where+    toJSON = A.String . encodeHex . S.encode+    toEncoding tx =+        unsafeToEncoding $ char7 '"' <> hexBuilder (S.encode tx) <> char7 '"'++-- | Data type representing a transaction input.+data TxIn =+    TxIn {+           -- | output being spent+           prevOutput   :: !OutPoint+           -- | signatures and redeem script+         , scriptInput  :: !ByteString+           -- | lock-time using sequence numbers (BIP-68)+         , txInSequence :: !Word32+         } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)++instance Serialize TxIn where+    get =+        TxIn <$> S.get <*> (readBS =<< S.get) <*> getWord32le+      where+        readBS (VarInt len) = getByteString $ fromIntegral len++    put (TxIn o s q) = do+        put o+        putVarInt $ B.length s+        putByteString s+        putWord32le q++-- | Data type representing a transaction output.+data TxOut =+    TxOut {+            -- | value of output is satoshi+            outValue     :: !Word64+            -- | pubkey script+          , scriptOutput :: !ByteString+          } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)++instance Serialize TxOut where+    get = do+        val <- getWord64le+        (VarInt len) <- S.get+        TxOut val <$> getByteString (fromIntegral len)++    put (TxOut o s) = do+        putWord64le o+        putVarInt $ B.length s+        putByteString s++-- | The 'OutPoint' refers to a transaction output being spent.+data OutPoint = OutPoint+    { -- | hash of previous transaction+      outPointHash  :: !TxHash+      -- | position of output in previous transaction+    , outPointIndex :: !Word32+    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)++instance FromJSON OutPoint where+    parseJSON = withText "OutPoint" $+        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)++instance ToJSON OutPoint where+    toJSON = A.String . encodeHex . S.encode+    toEncoding op =+        unsafeToEncoding $ char7 '"' <> hexBuilder (S.encode op) <> char7 '"'++instance Serialize OutPoint where+    get = do+        (h,i) <- liftM2 (,) S.get getWord32le+        return $ OutPoint h i+    put (OutPoint h i) = put h >> putWord32le i++-- | Outpoint used in coinbase transactions.+nullOutPoint :: OutPoint+nullOutPoint =+    OutPoint+    { outPointHash =+          "0000000000000000000000000000000000000000000000000000000000000000"+    , outPointIndex = maxBound+    }++-- | Transaction from Genesis block.+genesisTx :: Tx+genesisTx =+    Tx 1 [txin] [txout] [] locktime+  where+    txin = TxIn outpoint inputBS maxBound+    txout = TxOut 5000000000 (encodeOutputBS output)+    locktime = 0+    outpoint = OutPoint z maxBound+    Just inputBS = decodeHex $ fromString $+        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" +++        "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" +++        "757420666f722062616e6b73"+    output = PayPK $ fromString $+        "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" +++        "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"+    z = "0000000000000000000000000000000000000000000000000000000000000000"
+ src/Haskoin/Transaction/Partial.hs view
@@ -0,0 +1,500 @@+{-# LANGUAGE DeriveGeneric              #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NamedFieldPuns             #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-|+Module      : Haskoin.Transaction.Partial+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : matt@bitnomial.com+Stability   : experimental+Portability : POSIX++Code related to PSBT parsing and serialization.+-}+module Haskoin.Transaction.Partial+    ( PartiallySignedTransaction (..)+    , Input (..)+    , Output (..)+    , UnknownMap (..)+    , Key (..)+    , merge+    , mergeInput+    , mergeOutput+    , complete+    , finalTransaction+    , emptyPSBT+    , emptyInput+    , emptyOutput+    ) where++import           Control.Applicative        ((<|>))+import           Control.DeepSeq+import           Control.Monad              (guard, replicateM, void)+import           Data.ByteString            (ByteString)+import qualified Data.ByteString            as B+import           Data.Hashable              (Hashable)+import           Data.HashMap.Strict        (HashMap)+import qualified Data.HashMap.Strict        as HashMap+import           Data.List                  (foldl')+import           Data.Maybe                 (fromMaybe, isJust)+import           Data.Serialize             as S+import           GHC.Generics               (Generic)+import           GHC.Word                   (Word32, Word8)+import           Haskoin.Address            (Address (..), pubKeyAddr)+import           Haskoin.Keys               (Fingerprint, KeyIndex, PubKeyI)+import           Haskoin.Network            (VarInt (..), VarString (..),+                                             putVarInt)+import           Haskoin.Script             (Script (..), ScriptOp (..),+                                             ScriptOutput (..), SigHash,+                                             decodeOutput, decodeOutputBS,+                                             encodeOutputBS, isPayScriptHash,+                                             opPushData, toP2SH, toP2WSH)+import           Haskoin.Transaction.Common (Tx (..), TxOut, WitnessStack,+                                             outPointIndex, prevOutput,+                                             scriptInput, scriptOutput)+import           Haskoin.Util               (eitherToMaybe)++-- | PSBT data type as specified in+-- [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).+-- This contains an unsigned transaction, inputs and outputs, and unspecified+-- extra data. There is one input per input in the unsigned transaction, and one+-- output per output in the unsigned transaction. The inputs and outputs in the+-- 'PartiallySignedTransaction' line up by index with the inputs and outputs in+-- the unsigned transaction.+data PartiallySignedTransaction = PartiallySignedTransaction+    { unsignedTransaction :: Tx+    , globalUnknown       :: UnknownMap+    , inputs              :: [Input]+    , outputs             :: [Output]+    } deriving (Show, Eq, Generic)++instance NFData PartiallySignedTransaction++-- | Inputs contain all of the data needed to sign a transaction and all of the+-- resulting signature data after signing.+data Input = Input+    { nonWitnessUtxo     :: Maybe Tx+    , witnessUtxo        :: Maybe TxOut+    , partialSigs        :: HashMap PubKeyI ByteString+    , sigHashType        :: Maybe SigHash+    , inputRedeemScript  :: Maybe Script+    , inputWitnessScript :: Maybe Script+    , inputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])+    , finalScriptSig     :: Maybe Script+    , finalScriptWitness :: Maybe WitnessStack+    , inputUnknown       :: UnknownMap+    } deriving (Show, Eq, Generic)++instance NFData Input++-- | Outputs can contain information needed to spend the output at a later date.+data Output = Output+    { outputRedeemScript  :: Maybe Script+    , outputWitnessScript :: Maybe Script+    , outputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])+    , outputUnknown       :: UnknownMap+    } deriving (Show, Eq, Generic)++instance NFData Output++-- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field cannot overlap with any of the reserved+-- 'keyType' fields specified in the PSBT specification.+newtype UnknownMap = UnknownMap { unknownMap :: HashMap Key ByteString }+    deriving (Show, Eq, Semigroup, Monoid, Generic)++instance NFData UnknownMap++-- | Raw keys for the map type used in PSBTs.+data Key = Key+    { keyType :: Word8+    , key     :: ByteString+    } deriving (Show, Eq, Generic)++instance NFData Key++instance Hashable Key++-- | Take two 'PartiallySignedTransaction's and merge them. The 'unsignedTransaction' field in both must be the same.+merge :: PartiallySignedTransaction -> PartiallySignedTransaction -> Maybe PartiallySignedTransaction+merge psbt1 psbt2+    | unsignedTransaction psbt1 == unsignedTransaction psbt2+    = Just $ psbt1+        { globalUnknown = globalUnknown psbt1 <> globalUnknown psbt2+        , inputs = zipWith mergeInput (inputs psbt1) (inputs psbt2)+        , outputs = zipWith mergeOutput (outputs psbt1) (outputs psbt2)+        }+merge _ _ = Nothing++mergeInput :: Input -> Input -> Input+mergeInput a b = Input+    { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b+    , witnessUtxo = witUtx+    , sigHashType = sigHashType a <|> sigHashType b+    , partialSigs = partialSigs a <> partialSigs b+    , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b+    , inputUnknown = inputUnknown a <> inputUnknown b+    , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b+    , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b+    , finalScriptSig = finalScriptSig a <|> finalScriptSig b+    , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b+    }+  where+    witUtx = witnessUtxo a <|> witnessUtxo b++mergeOutput :: Output -> Output -> Output+mergeOutput a b = Output+    { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b+    , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b+    , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b+    , outputUnknown = outputUnknown a <> outputUnknown b+    }++-- | Take partial signatures from all of the 'Input's and finalize the signature.+complete :: PartiallySignedTransaction -> PartiallySignedTransaction+complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (indexed $ inputs psbt) }+  where+    analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)+      where+        nonWitScript = getPrevOut i =<< nonWitnessUtxo input++    getPrevOut i tx =+       (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i+    xs !!? i = lookup i $ indexed xs++    outputScript = eitherToMaybe . decodeOutputBS . scriptOutput++    completeInput (Nothing, input)     = input+    completeInput (Just script, input) = completeSig input script++    indexed :: [a] -> [(Word32, a)]+    indexed = zip [0..]++completeSig :: Input -> ScriptOutput -> Input+completeSig input (PayPK k) =+    input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }+completeSig input (PayPKHash h)+    | [(k, sig)] <- HashMap.toList $ partialSigs input+    , PubKeyAddress h == pubKeyAddr k+    = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }+completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }+  where+    sigs = collectSigs m pubKeys input+    finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input+completeSig input (PayScriptHash h)+    | Just rdmScript <- inputRedeemScript input+    , PayScriptHash h == toP2SH rdmScript+    , Right decodedScript <- decodeOutput rdmScript+    , not (isPayScriptHash decodedScript)+    = completeSig input decodedScript+completeSig input (PayWitnessPKHash h)+    | [(k, sig)] <- HashMap.toList $ partialSigs input+    , PubKeyAddress h == pubKeyAddr k+    = input { finalScriptWitness = Just [sig, S.encode k]+            , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input+            }+completeSig input (PayWitnessScriptHash h)+    | Just witScript <- inputWitnessScript input+    , PayWitnessScriptHash h == toP2WSH witScript+    , Right decodedScript <- decodeOutput witScript+    = completeWitnessSig input decodedScript+completeSig input _ = input++completeWitnessSig :: Input -> ScriptOutput -> Input+completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input+    { finalScriptWitness = Just finalWit+    , finalScriptSig = finalSig+    }+  where+    sigs = collectSigs m pubKeys input+    finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input+    finalWit = mempty : sigs <> [encodeOutputBS script]+completeWitnessSig input _ = input++collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]+collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys+  where+    lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)++-- | Take a finalized 'PartiallySignedTransaction' and produce the signed final transaction.  You may need to call+-- 'complete' on the 'PartiallySignedTransaction' before producing the final transaction.+finalTransaction :: PartiallySignedTransaction -> Tx+finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)+  where+    tx = unsignedTransaction psbt+    hasWitness = any (isJust . finalScriptWitness) (inputs psbt)+    setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }+    finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput+      where+        finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)+        finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)++-- | Take an unsigned transaction and produce an empty 'PartiallySignedTransaction'+emptyPSBT :: Tx -> PartiallySignedTransaction+emptyPSBT tx = PartiallySignedTransaction+    { unsignedTransaction = tx+    , globalUnknown = mempty+    , inputs = replicate (length (txIn tx)) emptyInput+    , outputs = replicate (length (txOut tx)) emptyOutput+    }++emptyInput :: Input+emptyInput = Input+    Nothing Nothing HashMap.empty Nothing+    Nothing Nothing HashMap.empty+    Nothing Nothing (UnknownMap HashMap.empty)++emptyOutput :: Output+emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)++instance Serialize PartiallySignedTransaction where+    get = do+        magic <- getBytes 4+        guard $ magic == "psbt"+        headerSep <- getWord8+        guard $ headerSep == 0xff++        keySize <- getWord8+        guard $ keySize == 1+        globalUnsignedTxType <- getWord8+        guard $ globalUnsignedTxType == 0x00+        unsignedTransaction <- getSizedBytes+        guard $ all (B.null . scriptInput) (txIn unsignedTransaction)+        guard $ null (txWitness unsignedTransaction)++        globalUnknown <- get+        globalEnd <- getWord8+        guard $ globalEnd == 0x00++        inputs <- replicateM (length $ txIn unsignedTransaction) get+        outputs <- replicateM (length $ txOut unsignedTransaction) get++        return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }++    put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do+        putByteString "psbt"+        putWord8 0xff -- Header separator++        putWord8 0x01 -- Key size+        putWord8 0x00 -- Unsigned Transaction type+        putSizedBytes unsignedTransaction+        put globalUnknown+        putWord8 0x00 -- Global end++        mapM_ put inputs+        mapM_ put outputs++instance Serialize Key where+    get = do+        VarInt keySize <- get+        guard $ keySize > 0+        t <- getWord8+        k <- getBytes (fromIntegral keySize - 1)+        return (Key t k)++    put (Key t k) = do+        putVarInt $ 1 + B.length k+        putWord8 t+        putByteString k++instance Serialize UnknownMap where+    get = go HashMap.empty+      where+        getItem m = do+            k <- get+            VarString v <- get+            go $ HashMap.insert k v m+        go m = do+            isEnd <- lookAhead getWord8+            if isEnd == 0x00+                then return (UnknownMap m)+                else getItem m++    put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m++instance Serialize Input where+    get = getMap getInputItem setInputUnknown emptyInput+      where+        setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }++    put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType+                  , inputRedeemScript, inputWitnessScript, inputHDKeypaths+                  , finalScriptSig, finalScriptWitness, inputUnknown+                  } = do+        whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo+        whenJust (putKeyValue InWitnessUtxo) witnessUtxo+        putPartialSig partialSigs+        whenJust putSigHash sigHashType+        whenJust (putKeyValue InRedeemScript) inputRedeemScript+        whenJust (putKeyValue InWitnessScript) inputWitnessScript+        putHDPath InBIP32Derivation inputHDKeypaths+        whenJust (putKeyValue InFinalScriptSig) finalScriptSig+        whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness+        put inputUnknown+        putWord8 0x00+      where+        putPartialSig = putPubKeyMap InPartialSig . fmap VarString+        putSigHash sigHash = do+            putKey InSigHashType+            putWord8 0x04+            putWord32le (fromIntegral sigHash)++instance Serialize Output where+    get = getMap getOutputItem setOutputUnknown emptyOutput+      where+        setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }++    put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do+        whenJust (putKeyValue OutRedeemScript) outputRedeemScript+        whenJust (putKeyValue OutWitnessScript) outputWitnessScript+        putHDPath OutBIP32Derivation outputHDKeypaths+        put outputUnknown+        putWord8 0x00++putSizedBytes :: Serialize a => a -> Put+putSizedBytes a = putVarInt (B.length bs) >> putByteString bs+  where bs = encode a++getSizedBytes :: Serialize a => Get a+getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get++putKeyValue :: (Enum t, Serialize v) => t -> v -> Put+putKeyValue t v = putKey t >> putSizedBytes v++putKey :: Enum t => t -> Put+putKey t = putVarInt (1 :: Word8) >> putWord8 (enumWord8 t)++getMap :: (Bounded t, Enum t)+       => (Int -> a -> t -> Get a)+       -> ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a)+       -> a -> Get a+getMap getMapItem setUnknown = go+  where+    getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go+    getItem keySize m (Left t)  = do+        k <- getBytes (fromIntegral keySize - 1)+        VarString v <- get+        go $ setUnknown (HashMap.insert (Key t k) v) m+    go m = do+        keySize <- getVarInt <$> get+        if keySize == 0+            then return m+            else getItem keySize m =<< (word8Enum <$> getWord8)++data InputType+    = InNonWitnessUtxo+    | InWitnessUtxo+    | InPartialSig+    | InSigHashType+    | InRedeemScript+    | InWitnessScript+    | InBIP32Derivation+    | InFinalScriptSig+    | InFinalScriptWitness+    deriving (Show, Eq, Enum, Bounded, Generic)++instance NFData InputType++data OutputType+    = OutRedeemScript+    | OutWitnessScript+    | OutBIP32Derivation+    deriving (Show, Eq, Enum, Bounded, Generic)++instance NFData OutputType++getInputItem :: Int -> Input -> InputType -> Get Input+getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do+    utxo <- getSizedBytes+    return $ input { nonWitnessUtxo = Just utxo }+getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do+    utxo <- getSizedBytes+    return $ input { witnessUtxo = Just utxo }+getInputItem keySize input InPartialSig = do+    (k, v) <- getPartialSig+    return $ input { partialSigs = HashMap.insert k v (partialSigs input) }+  where+    getPartialSig = (,) <$> isolate keySize get <*> (getVarString <$> get)+getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do+    VarInt size <- get+    guard $ size == 0x04+    sigHash <- fromIntegral <$> getWord32le+    return $ input { sigHashType = Just sigHash }+getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do+    script <- getSizedBytes+    return $ input { inputRedeemScript = Just script }+getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do+    script <- getSizedBytes+    return $ input { inputWitnessScript = Just script }+getInputItem keySize input InBIP32Derivation = do+    (k, v) <- getHDPath keySize+    return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }+getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do+    script <- getSizedBytes+    return $ input { finalScriptSig = Just script }+getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do+    scripts <- map getVarString <$> getVarIntList+    return $ input { finalScriptWitness = Just scripts }+  where+    getVarIntList = do+        VarInt n <- get+        replicateM (fromIntegral n) get+getInputItem keySize input inputType = fail $+    "Incorrect key size for input item or item already existed: " <>+    show (keySize, input, inputType)++getOutputItem :: Int -> Output -> OutputType -> Get Output+getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do+    script <- getSizedBytes+    return $ output { outputRedeemScript = Just script }+getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do+    script <- getSizedBytes+    return $ output { outputWitnessScript = Just script }+getOutputItem keySize output OutBIP32Derivation = do+    (k, v) <- getHDPath keySize+    return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }+getOutputItem keySize output outputType = fail $+    "Incorrect key size for output item or item already existed: " <>+    show (keySize, output, outputType)++getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))+getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)++putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put+putHDPath t = putPubKeyMap t . fmap PSBTHDPath++newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }+    deriving (Show, Eq, Generic)++instance NFData PSBTHDPath++instance Serialize PSBTHDPath where+    get = do+        VarInt valueSize <- get+        guard $ valueSize `mod` 4 == 0+        let numIndices = (fromIntegral valueSize - 4) `div` 4+        PSBTHDPath <$>+            isolate+                (fromIntegral valueSize)+                ((,) <$> getWord32le <*> getKeyIndexList numIndices)+      where+        getKeyIndexList n = replicateM n getWord32le+    put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs+      where+        bs = runPut $ putWord32le fp >> mapM_ putWord32le kis++putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put+putPubKeyMap t = void . HashMap.traverseWithKey putItem+  where+    putItem k v = put (Key (enumWord8 t) (encode k)) >> put v++enumWord8 :: Enum a => a -> Word8+enumWord8 = fromIntegral . fromEnum++word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a+word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n+word8Enum n = Left n++whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()+whenJust = maybe (return ())
+ src/Haskoin/Transaction/Segwit.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections     #-}+{-|+Module      : Haskoin.Transaction.Segwit+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Types to represent segregated witness data and auxilliary functions to+manipulate it.  See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)+and [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) for+details.+-}+module Haskoin.Transaction.Segwit+    ( WitnessProgram (..)+    , WitnessProgramPKH (..)+    , WitnessProgramSH (..)+    , isSegwit++    , viewWitnessProgram+    , decodeWitnessInput++    , calcWitnessProgram+    , simpleInputStack+    , toWitnessStack+    ) where++import           Data.ByteString            (ByteString)+import qualified Data.Serialize             as S+import           Haskoin.Constants          (Network)+import           Haskoin.Keys.Common        (PubKeyI)+import           Haskoin.Script.Common      (Script (..), ScriptOutput (..),+                                             decodeOutput, encodeOutput)+import           Haskoin.Script.SigHash     (TxSignature (..), decodeTxSig,+                                             encodeTxSig)+import           Haskoin.Script.Standard    (ScriptInput (..), SimpleInput (..))+import           Haskoin.Transaction.Common (WitnessStack)++-- | Test if a 'ScriptOutput' is P2WPKH or P2WSH+--+-- @since 0.11.0.0+isSegwit :: ScriptOutput -> Bool+isSegwit = \case+    PayWitnessPKHash{}     -> True+    PayWitnessScriptHash{} -> True+    _                      -> False++-- | High level represenation of a (v0) witness program+--+-- @since 0.11.0.0+data WitnessProgram = P2WPKH WitnessProgramPKH | P2WSH WitnessProgramSH | EmptyWitnessProgram+    deriving (Eq, Show)++-- | Encode a witness program+--+-- @since 0.11.0.0+toWitnessStack :: WitnessProgram -> WitnessStack+toWitnessStack = \case+    P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, S.encode key]+    P2WSH (WitnessProgramSH stack scr) -> stack <> [S.encode scr]+    EmptyWitnessProgram                -> mempty++-- | High level representation of a P2WPKH witness+--+-- @since 0.11.0.0+data WitnessProgramPKH = WitnessProgramPKH+    { witnessSignature :: !TxSignature+    , witnessPubKey    :: !PubKeyI+    } deriving (Eq, Show)++-- | High-level representation of a P2WSH witness+--+-- @since 0.11.0.0+data WitnessProgramSH = WitnessProgramSH+    { witnessScriptHashStack  :: ![ByteString]+    , witnessScriptHashScript :: !Script+    } deriving (Eq, Show)++-- | Calculate the witness program from the transaction data+--+-- @since 0.11.0.0+viewWitnessProgram :: Network -> ScriptOutput -> WitnessStack -> Either String WitnessProgram+viewWitnessProgram net so witness = case so of+    PayWitnessPKHash _ | length witness == 2 -> do+        sig    <- decodeTxSig net $ head witness+        pubkey <- S.decode $ witness !! 1+        return . P2WPKH $ WitnessProgramPKH sig pubkey+    PayWitnessScriptHash _ | not (null witness) -> do+        redeemScript <- S.decode $ last witness+        return . P2WSH $ WitnessProgramSH (init witness) redeemScript+    _ | null witness -> return EmptyWitnessProgram+      | otherwise    -> Left "viewWitnessProgram: Invalid witness program"++-- | Analyze the witness, trying to match it with standard input structures+--+-- @since 0.11.0.0+decodeWitnessInput :: Network -> WitnessProgram -> Either String (Maybe ScriptOutput, SimpleInput)+decodeWitnessInput net = \case+    P2WPKH (WitnessProgramPKH sig key) -> return (Nothing, SpendPKHash sig key)+    P2WSH (WitnessProgramSH st scr) -> do+        so <- decodeOutput scr+        fmap (Just so, ) $ case (so, st) of+            (PayPK _, [sigBS]) ->+                SpendPK <$> decodeTxSig net sigBS+            (PayPKHash _, [sigBS, keyBS]) ->+                SpendPKHash <$> decodeTxSig net sigBS <*> S.decode keyBS+            (PayMulSig _ _, "" : sigsBS) ->+                SpendMulSig <$> traverse (decodeTxSig net) sigsBS+            _ -> Left "decodeWitnessInput: Non-standard script output"+    EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"++-- | Create the witness program for a standard input+--+-- @since 0.11.0.0+calcWitnessProgram :: ScriptOutput -> ScriptInput -> Either String WitnessProgram+calcWitnessProgram so si = case (so, si) of+    (PayWitnessPKHash{}, RegularInput (SpendPKHash sig pk)) -> p2wpkh sig pk+    (PayScriptHash{}, RegularInput (SpendPKHash sig pk))    -> p2wpkh sig pk+    (PayWitnessScriptHash{}, ScriptHashInput i o)           -> p2wsh i o+    (PayScriptHash{}, ScriptHashInput i o)                  -> p2wsh i o+    _ -> Left "calcWitnessProgram: Invalid segwit SigInput"+  where+    p2wpkh sig = return . P2WPKH . WitnessProgramPKH sig+    p2wsh i o  = return . P2WSH $ WitnessProgramSH (simpleInputStack i) (encodeOutput o)++-- | Create the witness stack required to spend a standard P2WSH input+--+-- @since 0.11.0.0+simpleInputStack :: SimpleInput -> [ByteString]+simpleInputStack = \case+    SpendPK sig       -> [f sig]+    SpendPKHash sig k -> [f sig, S.encode k]+    SpendMulSig sigs  -> "" : fmap f sigs+  where+    f TxSignatureEmpty = ""+    f sig              = encodeTxSig sig
+ src/Haskoin/Util.hs view
@@ -0,0 +1,202 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-|+Module      : Haskoin.Util+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++This module defines various utility functions used across the library.+-}+module Haskoin.Util+    (+      -- * ByteString Helpers+      bsToInteger+    , integerToBS+    , hexBuilder+    , encodeHex+    , decodeHex+    , getBits++      -- * Maybe & Either Helpers+    , eitherToMaybe+    , maybeToEither+    , liftEither+    , liftMaybe++      -- * Other Helpers+    , updateIndex+    , matchTemplate+    , convertBits++      -- * Triples+    , fst3+    , snd3+    , lst3++      -- * JSON Utilities+    , dropFieldLabel+    , dropSumLabels++    ) where++import           Control.Monad           (guard)+import           Control.Monad.Except    (ExceptT (..), liftEither)+import           Data.Aeson.Types        (Options (..), SumEncoding (..),+                                          defaultOptions, defaultTaggedObject)+import           Data.Bits+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import qualified Data.ByteString.Base16  as B16+import           Data.ByteString.Builder+import qualified Data.ByteString.Lazy    as BL+import           Data.Char               (toLower)+import           Data.List+import           Data.Text               (Text)+import qualified Data.Text.Encoding      as E+import           Data.Word               (Word8)++-- ByteString helpers++-- | Decode a big endian 'Integer' from a 'ByteString'.+bsToInteger :: ByteString -> Integer+bsToInteger = BS.foldr f 0 . BS.reverse+  where+    f w n = toInteger w .|. shiftL n 8++-- | Encode an 'Integer' to a 'ByteString' as big endian.+integerToBS :: Integer -> ByteString+integerToBS 0 = BS.pack [0]+integerToBS i+    | i > 0     = BS.reverse $ BS.unfoldr f i+    | otherwise = error "integerToBS not defined for negative values"+  where+    f 0 = Nothing+    f x = Just (fromInteger x :: Word8, x `shiftR` 8)++hexBuilder :: ByteString -> Builder+hexBuilder = byteStringHex++-- | Encode as string of human-readable hex characters.+encodeHex :: ByteString -> Text+encodeHex = E.decodeUtf8 . BL.toStrict . toLazyByteString . byteStringHex++-- | Decode string of human-readable hex characters.+decodeHex :: Text -> Maybe ByteString+decodeHex text =+    let (x, b) = B16.decode (E.encodeUtf8 text)+    in guard (b == BS.empty) >> return x++-- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'+-- will be smallest required to hold that many bits, padded with zeroes to the+-- right.+getBits :: Int -> ByteString -> ByteString+getBits b bs+    | r == 0 = BS.take q bs+    | otherwise = i `BS.snoc` l+  where+    (q, r) = b `quotRem` 8+    s = BS.take (q + 1) bs+    i = BS.init s+    l = BS.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits++-- Maybe and Either monad helpers++-- | Transform an 'Either' value into a 'Maybe' value. 'Right' is mapped to+-- 'Just' and 'Left' is mapped to 'Nothing'. The value inside 'Left' is lost.+eitherToMaybe :: Either a b -> Maybe b+eitherToMaybe (Right b) = Just b+eitherToMaybe _         = Nothing++-- | Transform a 'Maybe' value into an 'Either' value. 'Just' is mapped to+-- 'Right' and 'Nothing' is mapped to 'Left'. Default 'Left' required.+maybeToEither :: b -> Maybe a -> Either b a+maybeToEither err = maybe (Left err) Right++-- | Lift a 'Maybe' computation into the 'ExceptT' monad.+liftMaybe :: Monad m => b -> Maybe a -> ExceptT b m a+liftMaybe err = liftEither . maybeToEither err++-- Various helpers++-- | Applies a function to only one element of a list defined by its index.  If+-- the index is out of the bounds of the list, the original list is returned.+updateIndex :: Int      -- ^ index of the element to change+            -> [a]      -- ^ list of elements+            -> (a -> a) -- ^ function to apply+            -> [a]      -- ^ result with one element changed+updateIndex i xs f+    | i < 0 || i >= length xs = xs+    | otherwise = l ++ (f h : r)+  where+    (l,h:r) = splitAt i xs++-- | Use the list @[b]@ as a template and try to match the elements of @[a]@+-- against it. For each element of @[b]@ return the (first) matching element of+-- @[a]@, or 'Nothing'. Output list has same size as @[b]@ and contains results+-- in same order. Elements of @[a]@ can only appear once.+matchTemplate :: [a]              -- ^ input list+              -> [b]              -- ^ list to serve as a template+              -> (a -> b -> Bool) -- ^ comparison function+              -> [Maybe a]+matchTemplate [] bs _ = replicate (length bs) Nothing+matchTemplate _  [] _ = []+matchTemplate as (b:bs) f = case break (`f` b) as of+    (l,r:rs) -> Just r  : matchTemplate (l ++ rs) bs f+    _        -> Nothing : matchTemplate as bs f++-- | Returns the first value of a triple.+fst3 :: (a,b,c) -> a+fst3 (a,_,_) = a++-- | Returns the second value of a triple.+snd3 :: (a,b,c) -> b+snd3 (_,b,_) = b++-- | Returns the last value of a triple.+lst3 :: (a,b,c) -> c+lst3 (_,_,c) = c++-- | Field label goes lowercase and first @n@ characters get removed.+dropFieldLabel :: Int -> Options+dropFieldLabel n = defaultOptions+    { fieldLabelModifier = map toLower . drop n+    }++-- | Transformation from 'dropFieldLabel' is applied with argument @f@, plus+-- constructor tags are lowercased and first @c@ characters removed. @tag@ is+-- used as the name of the object field name that will hold the transformed+-- constructor tag as its value.+dropSumLabels :: Int -> Int -> String -> Options+dropSumLabels c f tag = (dropFieldLabel f)+    { constructorTagModifier = map toLower . drop c+    , sumEncoding = defaultTaggedObject { tagFieldName = tag }+    }++-- | Convert from one power-of-two base to another, as long as it fits in a+-- 'Word'.+convertBits :: Bool -> Int -> Int -> [Word] -> ([Word], Bool)+convertBits pad frombits tobits i = (reverse yout, rem')+  where+    (xacc, xbits, xout) = foldl' outer (0, 0, []) i+    (yout, rem')+        | pad && xbits /= 0 =+            let xout' = (xacc `shiftL` (tobits - xbits)) .&. maxv : xout+            in (xout', False)+        | pad = (xout, False)+        | xbits /= 0 = (xout, True)+        | otherwise = (xout, False)+    maxv = 1 `shiftL` tobits - 1+    max_acc = 1 `shiftL` (frombits + tobits - 1) - 1+    outer (acc, bits, out) it =+        let acc' = ((acc `shiftL` frombits) .|. it) .&. max_acc+            bits' = bits + frombits+            (out', bits'') = inner acc' out bits'+        in (acc', bits'', out')+    inner acc out bits+        | bits >= tobits =+            let bits' = bits - tobits+                out' = ((acc `shiftR` bits') .&. maxv) : out+            in inner acc out' bits'+        | otherwise = (out, bits)
− src/Network/Haskoin/Address.hs
@@ -1,314 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Base58, CashAddr, Bech32 address and WIF private key serialization support.--}-module Network.Haskoin.Address-    ( Address(..)-    , isPubKeyAddress-    , isScriptAddress-    , isWitnessPubKeyAddress-    , isWitnessScriptAddress-    , addrToString-    , stringToAddr-    , addrToJSON-    , addrToEncoding-    , addrFromJSON-    , pubKeyAddr-    , pubKeyWitnessAddr-    , pubKeyCompatWitnessAddr-    , p2pkhAddr-    , p2wpkhAddr-    , p2shAddr-    , p2wshAddr-    , inputAddress-    , outputAddress-    , addressToScript-    , addressToScriptBS-    , addressToOutput-    , payToScriptAddress-    , payToWitnessScriptAddress-    , payToNestedScriptAddress-    , scriptToAddress-    , scriptToAddressBS-      -- * Private Key Wallet Import Format (WIF)-    , fromWif-    , toWif-      -- * Base58-    , module Network.Haskoin.Address.Base58-      -- * Bech32-    , module Network.Haskoin.Address.Bech32-      -- * CashAddr-    , module Network.Haskoin.Address.CashAddr-    ) where--import           Control.Applicative-import           Control.DeepSeq-import           Control.Monad-import           Data.Aeson                       as A-import           Data.Aeson.Encoding              as A-import           Data.Aeson.Types-import           Data.ByteString                  (ByteString)-import qualified Data.ByteString                  as B-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize                   as S-import           Data.Text                        (Text)-import           GHC.Generics                     (Generic)-import           Network.Haskoin.Address.Base58-import           Network.Haskoin.Address.Bech32-import           Network.Haskoin.Address.CashAddr-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Util---- | Address format for Bitcoin and Bitcoin Cash.-data Address-    -- | pay to public key hash (regular)-    = PubKeyAddress { getAddrHash160 :: !Hash160-                      -- ^ RIPEMD160 hash of public key's SHA256 hash-                     }-    -- | pay to script hash-    | ScriptAddress { getAddrHash160 :: !Hash160-                      -- ^ RIPEMD160 hash of script's SHA256 hash-                     }-    -- | pay to witness public key hash-    | WitnessPubKeyAddress { getAddrHash160 :: !Hash160-                             -- ^ RIPEMD160 hash of public key's SHA256 hash-                            }-    -- | pay to witness script hash-    | WitnessScriptAddress { getAddrHash256 :: !Hash256-                             -- ^ HASH256 hash of script-                            }-    deriving (Eq, Ord, Generic, Show, Read, Serialize, Hashable, NFData)---- | 'Address' pays to a public key hash.-isPubKeyAddress :: Address -> Bool-isPubKeyAddress PubKeyAddress {} = True-isPubKeyAddress _                = False---- | 'Address' pays to a script hash.-isScriptAddress :: Address -> Bool-isScriptAddress ScriptAddress {} = True-isScriptAddress _                = False---- | 'Address' pays to a witness public key hash. Only valid for SegWit--- networks.-isWitnessPubKeyAddress :: Address -> Bool-isWitnessPubKeyAddress WitnessPubKeyAddress {} = True-isWitnessPubKeyAddress _                       = False---- | 'Address' pays to a witness script hash. Only valid for SegWit networks.-isWitnessScriptAddress :: Address -> Bool-isWitnessScriptAddress WitnessScriptAddress {} = True-isWitnessScriptAddress _                       = False---- | Deserializer for binary 'Base58' addresses.-base58get :: Network -> Get Address-base58get net = do-    pfx <- getWord8-    addr <- S.get-    f pfx addr-  where-    f x a-        | x == getAddrPrefix net = return $ PubKeyAddress a-        | x == getScriptPrefix net = return $ ScriptAddress a-        | otherwise = fail "Does not recognize address prefix"---- | Binary serializer for 'Base58' addresses.-base58put :: Network -> Putter Address-base58put net (PubKeyAddress h) = do-        putWord8 (getAddrPrefix net)-        put h-base58put net (ScriptAddress h) = do-        putWord8 (getScriptPrefix net)-        put h-base58put _ _ = error "Cannot serialize this address as Base58"--addrToJSON :: Network -> Address -> Value-addrToJSON net a = toJSON (addrToString net a)--addrToEncoding :: Network -> Address -> Encoding-addrToEncoding net a =-    case addrToString net a of-        Nothing  -> null_-        Just txt -> text txt---- | JSON parsing for Bitcoin addresses. Works with 'Base58', 'CashAddr' and--- 'Bech32'.-addrFromJSON :: Network -> Value -> Parser Address-addrFromJSON net =-    withText "address" $ \t ->-        case stringToAddr net t of-            Nothing -> fail "could not decode address"-            Just x  -> return x---- | Convert address to human-readable string. Uses 'Base58', 'Bech32', or--- 'CashAddr' depending on network.-addrToString :: Network -> Address -> Maybe Text-addrToString net a@PubKeyAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise =-        cashAddrEncode net 0 (S.encode h)-addrToString net a@ScriptAddress {getAddrHash160 = h}-    | isNothing (getCashAddrPrefix net) =-        Just . encodeBase58Check . runPut $ base58put net a-    | otherwise =-        cashAddrEncode net 1 (S.encode h)-addrToString net WitnessPubKeyAddress {getAddrHash160 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))-addrToString net WitnessScriptAddress {getAddrHash256 = h} = do-    hrp <- getBech32Prefix net-    segwitEncode hrp 0 (B.unpack (S.encode h))---- | Parse 'Base58', 'Bech32' or 'CashAddr' address, depending on network.-stringToAddr :: Network -> Text -> Maybe Address-stringToAddr net bs = cash <|> segwit <|> b58-  where-    b58 = eitherToMaybe . runGet (base58get net) =<< decodeBase58Check bs-    cash = cashAddrDecode net bs >>= \(ver, bs') -> case ver of-        0 -> do-            h <- eitherToMaybe (S.decode bs')-            return $ PubKeyAddress h-        1 -> do-            h <- eitherToMaybe (S.decode bs')-            return $ ScriptAddress h-        _ -> Nothing-    segwit = do-        hrp <- getBech32Prefix net-        (ver, bs') <- segwitDecode hrp bs-        guard (ver == 0)-        let bs'' = B.pack bs'-        case B.length bs'' of-            20 -> do-                h <- eitherToMaybe (S.decode bs'')-                return $ WitnessPubKeyAddress h-            32 -> do-                h <- eitherToMaybe (S.decode bs'')-                return $ WitnessScriptAddress h-            _ -> Nothing---- | Obtain a standard pay-to-public-key-hash address from a public key.-pubKeyAddr :: PubKeyI -> Address-pubKeyAddr = PubKeyAddress . addressHash . S.encode---- | Obtain a standard pay-to-public-key-hash (P2PKH) address from a 'Hash160'.-p2pkhAddr :: Hash160 -> Address-p2pkhAddr = PubKeyAddress---- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a--- public key.-pubKeyWitnessAddr :: PubKeyI -> Address-pubKeyWitnessAddr = WitnessPubKeyAddress . addressHash . S.encode---- | Obtain a backwards-compatible SegWit P2SH-P2WPKH address from a public key.-pubKeyCompatWitnessAddr :: PubKeyI -> Address-pubKeyCompatWitnessAddr =-    p2shAddr .-    addressHash . encodeOutputBS . PayWitnessPKHash . addressHash . S.encode---- | Obtain a SegWit pay-to-witness-public-key-hash (P2WPKH) address from a--- 'Hash160'.-p2wpkhAddr :: Hash160 -> Address-p2wpkhAddr = WitnessPubKeyAddress---- | Obtain a standard pay-to-script-hash (P2SH) address from a 'Hash160'.-p2shAddr :: Hash160 -> Address-p2shAddr = ScriptAddress---- | Obtain a SegWit pay-to-witness-script-hash (P2WSH) address from a 'Hash256'-p2wshAddr :: Hash256 -> Address-p2wshAddr = WitnessScriptAddress---- | Compute a standard pay-to-script-hash (P2SH) address for an output script.-payToScriptAddress :: ScriptOutput -> Address-payToScriptAddress = p2shAddr . addressHash . encodeOutputBS---- | Compute a SegWit pay-to-witness-script-hash (P2WSH) address for an output--- script.-payToWitnessScriptAddress :: ScriptOutput -> Address-payToWitnessScriptAddress = p2wshAddr . sha256 . encodeOutputBS---- | Compute a backwards-compatible SegWit P2SH-P2WSH address.-payToNestedScriptAddress :: ScriptOutput -> Address-payToNestedScriptAddress =-    p2shAddr . addressHash . encodeOutputBS . toP2WSH . encodeOutput---- | Encode an output script from an address. Will fail if using a--- pay-to-witness address on a non-SegWit network.-addressToOutput :: Address -> ScriptOutput-addressToOutput (PubKeyAddress h)        = PayPKHash h-addressToOutput (ScriptAddress h)        = PayScriptHash h-addressToOutput (WitnessPubKeyAddress h) = PayWitnessPKHash h-addressToOutput (WitnessScriptAddress h) = PayWitnessScriptHash h---- | Get output script AST for an 'Address'.-addressToScript :: Address -> Script-addressToScript = encodeOutput . addressToOutput---- | Encode address as output script in 'ByteString' form.-addressToScriptBS :: Address -> ByteString-addressToScriptBS = S.encode . addressToScript---- | Decode an output script into an 'Address' if it has such representation.-scriptToAddress :: Script -> Either String Address-scriptToAddress =-    maybeToEither "Could not decode address" . outputAddress <=< decodeOutput---- | Decode a serialized script into an 'Address'.-scriptToAddressBS :: ByteString -> Either String Address-scriptToAddressBS =-    maybeToEither "Could not decode address" . outputAddress <=< decodeOutputBS---- | Get the 'Address' of a 'ScriptOutput'.-outputAddress :: ScriptOutput -> Maybe Address-outputAddress (PayPKHash h)            = Just $ PubKeyAddress h-outputAddress (PayScriptHash h)        = Just $ ScriptAddress h-outputAddress (PayPK k)                = Just $ pubKeyAddr k-outputAddress (PayWitnessPKHash h)     = Just $ WitnessPubKeyAddress h-outputAddress (PayWitnessScriptHash h) = Just $ WitnessScriptAddress h-outputAddress _                        = Nothing---- | Infer the 'Address' of a 'ScriptInput'.-inputAddress :: ScriptInput -> Maybe Address-inputAddress (RegularInput (SpendPKHash _ key)) = Just $ pubKeyAddr key-inputAddress (ScriptHashInput _ rdm) = Just $ payToScriptAddress rdm-inputAddress _ = Nothing---- | Decode private key from WIF (wallet import format) string.-fromWif :: Network -> Base58 -> Maybe SecKeyI-fromWif net wif = do-    bs <- decodeBase58Check wif-    -- Check that this is a private key-    guard (B.head bs == getSecretPrefix net)-    case B.length bs of-        -- Uncompressed format-        33 -> wrapSecKey False <$> secKey (B.tail bs)-        -- Compressed format-        34 -> do-            guard $ B.last bs == 0x01-            wrapSecKey True <$> secKey (B.tail $ B.init bs)-        -- Bad length-        _  -> Nothing---- | Encode private key into a WIF string.-toWif :: Network -> SecKeyI -> Base58-toWif net (SecKeyI k c) =-    encodeBase58Check . B.cons (getSecretPrefix net) $-    if c-        then getSecKey k `B.snoc` 0x01-        else getSecKey k
− src/Network/Haskoin/Address/Base58.hs
@@ -1,102 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.Base58-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for legacy 'Base58' addresses. Superseded by Bech32 for Bitcoin SegWit-(BTC) and CashAddr for Bitcoin Cash (BCH).--}-module Network.Haskoin.Address.Base58-    ( Base58-    , encodeBase58-    , decodeBase58-    , encodeBase58Check-    , decodeBase58Check-    ) where--import           Control.Monad-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import qualified Data.ByteString.Char8       as C-import           Data.Maybe                  (fromMaybe, isJust, listToMaybe)-import           Data.Serialize              as S-import           Data.String.Conversions     (cs)-import           Data.Text                   (Text)-import qualified Data.Text                   as T-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Util-import           Numeric                     (readInt, showIntAtBase)---- | 'Base58' classic Bitcoin address format.-type Base58 = Text---- | Symbols for Base58 encoding.-b58Data :: ByteString-b58Data = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"---- | Convert a number less than or equal to provided integer into a 'Base58'--- character.-b58 :: Int -> Char-b58 = C.index b58Data---- | Convert a 'Base58' character into the number it represents.-b58' :: Char -> Maybe Int-b58' = flip C.elemIndex b58Data---- | Encode an arbitrary-length 'Integer' into a 'Base58' string. Leading zeroes--- will not be part of the resulting string.-encodeBase58I :: Integer -> Base58-encodeBase58I i = cs $ showIntAtBase 58 b58 i ""---- | Decode a 'Base58' string into an arbitrary-length 'Integer'.-decodeBase58I :: Base58 -> Maybe Integer-decodeBase58I s =-    case go of-        Just (r,[]) -> Just r-        _           -> Nothing-  where-    p  = isJust . b58'-    f  = fromMaybe e . b58'-    go = listToMaybe $ readInt 58 p f (cs s)-    e  = error "Could not decode base58"---- | Encode an arbitrary 'ByteString' into a its 'Base58' representation,--- preserving leading zeroes.-encodeBase58 :: ByteString -> Base58-encodeBase58 bs =-    l `mappend` r-  where-    (z, b) = BS.span (== 0) bs-    l = cs $ BS.replicate (BS.length z) (BS.index b58Data 0) -- preserve leading 0's-    r | BS.null b = T.empty-      | otherwise = encodeBase58I $ bsToInteger b---- | Decode a 'Base58'-encoded 'Text' to a 'ByteString'.-decodeBase58 :: Base58 -> Maybe ByteString-decodeBase58 t =-    BS.append prefix <$> r-  where-    (z, b) = BS.span (== BS.index b58Data 0) (cs t)-    prefix = BS.replicate (BS.length z) 0 -- preserve leading 1's-    r | BS.null b = Just BS.empty-      | otherwise = integerToBS <$> decodeBase58I (cs b)---- | Computes a checksum for the input 'ByteString' and encodes the input and--- the checksum as 'Base58'.-encodeBase58Check :: ByteString -> Base58-encodeBase58Check bs =-    encodeBase58 $ BS.append bs $ encode $ checkSum32 bs---- | Decode a 'Base58'-encoded string that contains a checksum. This function--- returns 'Nothing' if the input string contains invalid 'Base58' characters or--- if the checksum fails.-decodeBase58Check :: Base58 -> Maybe ByteString-decodeBase58Check bs = do-    rs <- decodeBase58 bs-    let (res, chk) = BS.splitAt (BS.length rs - 4) rs-    guard $ chk == encode (checkSum32 res)-    return res
− src/Network/Haskoin/Address/Bech32.hs
@@ -1,228 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.Base58-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for Bitcoin SegWit (BTC) Bech32 addresses. This module is a modified-version of Marko Bencun's reference implementation.--}-module Network.Haskoin.Address.Bech32-    ( HRP-    , Bech32-    , Data-    , bech32Encode-    , bech32Decode-    , toBase32-    , toBase256-    , segwitEncode-    , segwitDecode-    , Word5(..)-    , word5-    , fromWord5-    ) where--import           Control.Monad         (guard)-import           Data.Array            (Array, assocs, bounds, listArray, (!),-                                        (//))-import           Data.Bits             (Bits, testBit, unsafeShiftL,-                                        unsafeShiftR, xor, (.&.), (.|.))-import qualified Data.ByteString       as B-import           Data.Char             (toUpper)-import           Data.Foldable         (foldl')-import           Data.Functor.Identity (Identity, runIdentity)-import           Data.Ix               (Ix (..))-import           Data.Text             (Text)-import qualified Data.Text             as T-import qualified Data.Text.Encoding    as E-import           Data.Word             (Word8)---- | Bech32 human-readable string.-type Bech32 = Text---- | Human-readable part of 'Bech32' address.-type HRP = Text---- | Data part of 'Bech32' address.-type Data = [Word8]--(.>>.), (.<<.) :: Bits a => a -> Int -> a-(.>>.) = unsafeShiftR-(.<<.) = unsafeShiftL---- | Five-bit word for Bech32.-newtype Word5 =-    UnsafeWord5 Word8-    deriving (Eq, Ord)--instance Ix Word5 where-    range (UnsafeWord5 m, UnsafeWord5 n) = map UnsafeWord5 $ range (m, n)-    index (UnsafeWord5 m, UnsafeWord5 n) (UnsafeWord5 i) = index (m, n) i-    inRange (m, n) i = m <= i && i <= n---- | Convert an integer number into a five-bit word.-word5 :: Integral a => a -> Word5-word5 x = UnsafeWord5 (fromIntegral x .&. 31)-{-# INLINE word5 #-}-{-# SPECIALIZE INLINE word5 :: Word8 -> Word5 #-}---- | Convert a five-bit word into a number.-fromWord5 :: Num a => Word5 -> a-fromWord5 (UnsafeWord5 x) = fromIntegral x-{-# INLINE fromWord5 #-}-{-# SPECIALIZE INLINE fromWord5 :: Word5 -> Word8 #-}---- | 'Bech32' character map as array of five-bit integers to character.-charset :: Array Word5 Char-charset =-    listArray (UnsafeWord5 0, UnsafeWord5 31) "qpzry9x8gf2tvdw0s3jn54khce6mua7l"---- | Convert a character to its five-bit value from 'Bech32' 'charset'.-charsetMap :: Char -> Maybe Word5-charsetMap c-    | inRange (bounds inv) upperC = inv ! upperC-    | otherwise = Nothing-  where-    upperC = toUpper c-    inv = listArray ('0', 'Z') (repeat Nothing) // map swap (assocs charset)-    swap (a, b) = (toUpper b, Just a)---- | Calculate or validate 'Bech32' checksum.-bech32Polymod :: [Word5] -> Word-bech32Polymod values = foldl' go 1 values .&. 0x3fffffff-  where-    go chk value =-        foldl' xor chk' [g | (g, i) <- zip generator [25 ..], testBit chk i]-      where-        generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]-        chk' = chk .<<. 5 `xor` fromWord5 value---- | Convert human-readable part of 'Bech32' string into a list of five-bit--- words.-bech32HRPExpand :: HRP -> [Word5]-bech32HRPExpand hrp =-    map (UnsafeWord5 . (.>>. 5)) hrpBytes ++-    [UnsafeWord5 0] ++ map word5 hrpBytes-  where-    hrpBytes = B.unpack $ E.encodeUtf8 hrp---- | Calculate checksum for a string of five-bit words.-bech32CreateChecksum :: HRP -> [Word5] -> [Word5]-bech32CreateChecksum hrp dat = [word5 (polymod .>>. i) | i <- [25,20 .. 0]]-  where-    values = bech32HRPExpand hrp ++ dat-    polymod =-        bech32Polymod (values ++ map UnsafeWord5 [0, 0, 0, 0, 0, 0]) `xor` 1---- | Verify checksum for a human-readable part and string of five-bit words.-bech32VerifyChecksum :: HRP -> [Word5] -> Bool-bech32VerifyChecksum hrp dat = bech32Polymod (bech32HRPExpand hrp ++ dat) == 1---- | Maximum length of a Bech32 result.-maxBech32Length :: Int-maxBech32Length = 90---- | Encode string of five-bit words into 'Bech32' using a provided--- human-readable part. Can fail if 'HRP' is invalid or result would be longer--- than 90 characters.-bech32Encode :: HRP -> [Word5] -> Maybe Bech32-bech32Encode hrp dat = do-    guard $ checkHRP hrp-    let dat' = dat ++ bech32CreateChecksum (T.toLower hrp) dat-        rest = map (charset !) dat'-        result = T.concat [T.toLower hrp, T.pack "1", T.pack rest]-    guard $ T.length result <= maxBech32Length-    return result---- | Check that human-readable part is valid for a 'Bech32' string.-checkHRP :: HRP -> Bool-checkHRP hrp = not (T.null hrp) && T.all (\char -> char >= '\x21' && char <= '\x7e') hrp---- | Decode human-readable 'Bech32' string into a human-readable part and a--- string of five-bit words.-bech32Decode :: Bech32 -> Maybe (HRP, [Word5])-bech32Decode bech32 = do-    guard $ T.length bech32 <= maxBech32Length-    guard $ T.toUpper bech32 == bech32 || lowerBech32 == bech32-    let (hrp, dat) = T.breakOnEnd "1" lowerBech32-    guard $ T.length dat >= 6-    hrp' <- T.stripSuffix "1" hrp-    guard $ checkHRP hrp'-    dat' <- mapM charsetMap $ T.unpack dat-    guard $ bech32VerifyChecksum hrp' dat'-    return (hrp', take (T.length dat - 6) dat')-  where-    lowerBech32 = T.toLower bech32--type Pad f = Int -> Int -> Word -> [[Word]] -> f [[Word]]--yesPadding :: Pad Identity-yesPadding _ 0 _ result        = return result-yesPadding _ _ padValue result = return $ [padValue] : result-{-# INLINE yesPadding #-}--noPadding :: Pad Maybe-noPadding frombits bits padValue result = do-    guard $ bits < frombits && padValue == 0-    return result-{-# INLINE noPadding #-}---- | Big endian conversion of a bytestring from base \(2^{frombits}\) to base--- \(2^{tobits}\). {frombits} and {twobits} must be positive and--- \(2^{frombits}\) and \(2^{tobits}\) must be smaller than the size of Word.--- Every value in 'dat' must be strictly smaller than \(2^{frombits}\).-convertBits :: Functor f => [Word] -> Int -> Int -> Pad f -> f [Word]-convertBits dat frombits tobits pad = concat . reverse <$> go dat 0 0 []-  where-    go [] acc bits result =-        let padValue = (acc .<<. (tobits - bits)) .&. maxv-        in pad frombits bits padValue result-    go (value:dat') acc bits result =-        go dat' acc' (bits' `rem` tobits) (result' : result)-      where-        acc' = (acc .<<. frombits) .|. fromIntegral value-        bits' = bits + frombits-        result' =-            [ (acc' .>>. b) .&. maxv-            | b <- [bits' - tobits,bits' - 2 * tobits .. 0]-            ]-    maxv = (1 .<<. tobits) - 1-{-# INLINE convertBits #-}---- | Convert from eight-bit to five-bit word string, adding padding as required.-toBase32 :: [Word8] -> [Word5]-toBase32 dat =-    map word5 $ runIdentity $ convertBits (map fromIntegral dat) 8 5 yesPadding---- | Convert from five-bit word string to eight-bit word string, ignoring padding.-toBase256 :: [Word5] -> Maybe [Word8]-toBase256 dat =-    map fromIntegral <$> convertBits (map fromWord5 dat) 5 8 noPadding---- | Check if witness version and program are valid.-segwitCheck :: Word8 -> Data -> Bool-segwitCheck witver witprog =-    witver <= 16 &&-    if witver == 0-        then length witprog == 20 || length witprog == 32-        else length witprog >= 2 && length witprog <= 40---- | Decode SegWit 'Bech32' address from a string and expected human-readable part.-segwitDecode :: HRP -> Bech32 -> Maybe (Word8, Data)-segwitDecode hrp addr = do-    (hrp', dat) <- bech32Decode addr-    guard $ (hrp == hrp') && not (null dat)-    let (UnsafeWord5 witver:datBase32) = dat-    decoded <- toBase256 datBase32-    guard $ segwitCheck witver decoded-    return (witver, decoded)---- | Encode 'Data' as a SegWit 'Bech32' address. Needs human-readable part and--- witness program version.-segwitEncode :: HRP -> Word8 -> Data -> Maybe Text-segwitEncode hrp witver witprog = do-    guard $ segwitCheck witver witprog-    bech32Encode hrp $ UnsafeWord5 witver : toBase32 witprog
− src/Network/Haskoin/Address/CashAddr.hs
@@ -1,198 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Address.CashAddr-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Support for Bitcoin Cash (BCH) CashAddr format.--}-module Network.Haskoin.Address.CashAddr-    ( CashPrefix-    , CashVersion-    , CashAddr-    , Cash32-    , cashAddrDecode-    , cashAddrEncode-    , cash32decodeType-    , cash32encodeType-    , cash32decode-    , cash32encode-    ) where--import           Control.Monad-import           Data.Bits-import           Data.ByteString            (ByteString)-import qualified Data.ByteString            as B-import qualified Data.ByteString.Char8      as C-import           Data.Char-import           Data.List-import           Data.Text                  (Text)-import qualified Data.Text                  as T-import qualified Data.Text.Encoding         as E-import           Data.Word-import           Network.Haskoin.Constants-import           Network.Haskoin.Util---- | 'CashAddr' prefix, usually shown before the colon in addresses, but sometimes--- omitted. It is used in the checksum calculation to avoid parsing an address--- from the wrong network.-type CashPrefix = Text---- | 'CashAddr' version, until new address schemes appear it will be zero.-type CashVersion = Word8---- | High level 'CashAddr' human-reabale string, with explicit or implicit prefix.-type CashAddr = Text---- | Low level 'Cash32' is the human-readable low-level encoding used by 'CashAddr'. It--- need not encode a valid address but any binary data.-type Cash32 = Text---- | Symbols for encoding 'Cash32' data in human-readable strings.-charset :: String-charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"---- | Get the 32-bit number associated with this 'Cash32' character.-base32char :: Char -> Maybe Word8-base32char = fmap fromIntegral . (`elemIndex` charset)---- | High-Level: decode 'CashAddr' string if it is valid for the--- provided 'Network'. Prefix may be omitted from the string.-cashAddrDecode :: Network -> CashAddr -> Maybe (CashVersion, ByteString)-cashAddrDecode net ca = do-    epfx <- getCashAddrPrefix net-    let (cpfx, cdat) = T.breakOnEnd ":" (T.toLower ca)-    guard (T.null cpfx || T.init cpfx == epfx)-    (dpfx, ver, bs) <- cash32decodeType (epfx <> ":" <> cdat)-    guard (dpfx == epfx)-    return (ver, bs)---- | High-Level: encode 'CashAddr' string for the provided network and hash.--- Fails if the 'CashVersion' or length of hash 'ByteString' is invalid.-cashAddrEncode :: Network -> CashVersion -> ByteString -> Maybe CashAddr-cashAddrEncode net cv bs = do-    pfx <- getCashAddrPrefix net-    cash32encodeType pfx cv bs---- | Mid-Level: decode 'CashAddr' string containing arbitrary prefix, plus a--- version byte before the 'ByteString' that encodes type and length.-cash32decodeType :: Cash32 -> Maybe (CashPrefix, CashVersion, ByteString)-cash32decodeType ca' = do-    guard (T.toUpper ca' == ca' || ca == ca')-    (dpfx, bs) <- cash32decode ca-    guard (not (B.null bs))-    let vb = B.head bs-        pay = B.tail bs-    (ver, len) <- decodeVersionByte vb-    guard (B.length pay == len)-    return (dpfx, ver, pay)-  where-    ca = T.toLower ca'---- | Mid-Level: encode 'CashAddr' string containing arbitrary prefix and--- 'CashVersion'. Length must be among those allowed by the standard.-cash32encodeType :: CashPrefix -> CashVersion -> ByteString -> Maybe Cash32-cash32encodeType pfx cv bs = do-    let len = B.length bs-    vb <- encodeVersionByte cv len-    let pl = vb `B.cons` bs-    return (cash32encode pfx pl)---- | Low-Level: decode 'Cash32' string. 'CashPrefix' must be part of the string.--- No version or hash length validation is performed.-cash32decode :: Cash32 -> Maybe (CashPrefix, ByteString)-cash32decode text = do-    let bs = C.map toLower bs'-    guard (C.map toUpper bs' == bs' || bs == bs')-    let (pfx', dat) = C.breakEnd (== ':') bs-    pfx <--        if B.null pfx' || pfx' == C.singleton ':'-            then Nothing-            else Just (B.init pfx')-    b32 <- B.pack <$> mapM base32char (C.unpack dat)-    let px = B.map (.&. 0x1f) pfx-        pd = px <> B.singleton 0 <> b32-        cs = cash32Polymod pd-        bb = B.take (B.length b32 - 8) b32-    guard (verifyCash32Polymod cs)-    let out = toBase256 bb-    return (E.decodeUtf8 pfx, out)-  where-    bs' = E.encodeUtf8 text---- | Low-Level: encode 'Cash32' string for 'CashPrefix' provided. Can encode--- arbitrary data. No prefix or length validation is performed.-cash32encode :: CashPrefix -> ByteString -> Cash32-cash32encode pfx bs =-    let b32 = toBase32 bs-        px = B.map (.&. 0x1f) (E.encodeUtf8 pfx)-        pd = px <> B.singleton 0 <> b32 <> B.replicate 8 0-        cs = cash32Polymod pd-        c32 = B.map f (b32 <> cs)-        f = fromIntegral . ord . (charset !!) . fromIntegral-    in pfx <> ":" <> E.decodeUtf8 c32---- | Convert base of 'ByteString' from eight bits per byte to five bits per--- byte, adding padding as necessary.-toBase32 :: ByteString -> ByteString-toBase32 =-    B.pack .-    map fromIntegral . fst . convertBits True 8 5 . map fromIntegral . B.unpack---- | Convert base of 'ByteString' from five to eight bits per byte. Ignore--- padding to be symmetric with respect to 'toBase32' function.-toBase256 :: ByteString -> ByteString-toBase256 =-    B.pack .-    map fromIntegral . fst . convertBits False 5 8 . map fromIntegral . B.unpack---- | Obtain 'CashVersion' and payload length from 'CashAddr' version byte.-decodeVersionByte :: Word8 -> Maybe (CashVersion, Int)-decodeVersionByte vb = do-    guard (vb .&. 0x80 == 0)-    return (ver, len)-  where-    ver = vb `shiftR` 3-    len = ls !! fromIntegral (vb .&. 0x07)-    ls = [20, 24, 28, 32, 40, 48, 56, 64]---- | Encode 'CashVersion' and length into version byte. Fail if version is--- larger than five bits, or length incorrect, since that is invalid.-encodeVersionByte :: CashVersion -> Int -> Maybe Word8-encodeVersionByte ver len = do-    guard (ver == ver .&. 0x0f)-    l <- case len of-        20 -> Just 0-        24 -> Just 1-        28 -> Just 2-        32 -> Just 3-        40 -> Just 4-        48 -> Just 5-        56 -> Just 6-        64 -> Just 7-        _  -> Nothing-    return ((ver `shiftL` 3) .|. l)---- | Calculate or validate checksum from base32 'ByteString' (excluding prefix).-cash32Polymod :: ByteString -> ByteString-cash32Polymod v =-    B.pack-        [fromIntegral (polymod `shiftR` (5 * (7 - i))) .&. 0x1f | i <- [0 .. 7]]-  where-    polymod = B.foldl' outer (1 :: Word64) v `xor` 1-    outer c d =-        let c0 = (fromIntegral (c `shiftR` 35) :: Word8)-            c' = ((c .&. 0x07ffffffff) `shiftL` 5) `xor` fromIntegral d-        in foldl' (inner c0) c' (zip [0 ..] generator)-    generator =-        [0x98f2bc8e61, 0x79b76d99e2, 0xf33e5fb3c4, 0xae2eabe2a8, 0x1e4f43e470]-    inner c0 c (b, g)-        | c0 `testBit` b = c `xor` g-        | otherwise = c---- | Validate that polymod 'ByteString' (eight bytes) is equal to zero.-verifyCash32Polymod :: ByteString -> Bool-verifyCash32Polymod = (== B.replicate 8 0)
− src/Network/Haskoin/Block.hs
@@ -1,21 +0,0 @@-{-|-Module      : Network.Haskoin.Block-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Most functions relating to blocks are exported by this module.--}-module Network.Haskoin.Block-    ( module Network.Haskoin.Block.Common-      -- * Block Header Chain-    , module Network.Haskoin.Block.Headers-      -- * Merkle Blocks-    , module Network.Haskoin.Block.Merkle-    ) where--import           Network.Haskoin.Block.Headers-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Block.Common
− src/Network/Haskoin/Block/Common.hs
@@ -1,348 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Block.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common data types and functions to handle blocks from the block chain.--}-module Network.Haskoin.Block.Common-    ( Block(..)-    , BlockHeight-    , Timestamp-    , BlockHeader(..)-    , headerHash-    , BlockLocator-    , GetBlocks(..)-    , GetHeaders(..)-    , BlockHeaderCount-    , BlockHash(..)-    , blockHashToHex-    , hexToBlockHash-    , Headers(..)-    , decodeCompact-    , encodeCompact-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, liftM2, mzero,-                                                     replicateM)-import           Data.Aeson                         (FromJSON (..), ToJSON (..),-                                                     Value (String), toJSON,-                                                     withText)-import           Data.Aeson.Encoding                (unsafeToEncoding)-import           Data.Bits                          (shiftL, shiftR, (.&.),-                                                     (.|.))-import qualified Data.ByteString                    as B-import           Data.ByteString.Builder            (char7)-import           Data.Hashable                      (Hashable)-import           Data.Maybe                         (fromMaybe)-import           Data.Serialize                     (Serialize, decode, encode,-                                                     get, put)-import           Data.Serialize.Get                 (getWord32le)-import           Data.Serialize.Put                 (Put, putWord32le)-import           Data.String                        (IsString, fromString)-import           Data.String.Conversions            (cs)-import           Data.Text                          (Text)-import           Data.Word                          (Word32)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util-import qualified Text.Read                          as R---- | Height of a block in the block chain, starting at 0 for Genesis.-type BlockHeight = Word32---- | Block timestamp as Unix time (seconds since 1970-01-01 00:00 UTC).-type Timestamp = Word32---- | Block header and transactions.-data Block =-    Block { blockHeader :: !BlockHeader-          , blockTxns   :: ![Tx]-          } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize Block where-    get = do-        header     <- get-        (VarInt c) <- get-        txs        <- replicateM (fromIntegral c) get-        return $ Block header txs-    put (Block h txs) = do-        put h-        putVarInt $ length txs-        forM_ txs put--instance ToJSON Block where-    toJSON = String . encodeHex . encode-    toEncoding s =-        unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'--instance FromJSON Block where-    parseJSON =-        withText "Block" $ \t -> do-            bin <--                case decodeHex t of-                    Nothing  -> mzero-                    Just bin -> return bin-            case decode bin of-                Left e  -> fail e-                Right h -> return h--instance ToJSON BlockHeader where-    toJSON = String . encodeHex . encode-    toEncoding s =-        unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'--instance FromJSON BlockHeader where-    parseJSON =-        withText "BlockHeader" $ \t -> do-            bin <--                case decodeHex t of-                    Nothing  -> mzero-                    Just bin -> return bin-            case decode bin of-                Left e  -> fail e-                Right h -> return h---- | Block header hash. To be serialized reversed for display purposes.-newtype BlockHash = BlockHash-    { getBlockHash :: Hash256 }-    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)--instance Show BlockHash where-    showsPrec _ = shows . blockHashToHex--instance Read BlockHash where-    readPrec = do-        R.String str <- R.lexP-        maybe R.pfail return $ hexToBlockHash $ cs str--instance IsString BlockHash where-    fromString s =-        let e = error "Could not read block hash from hex string"-        in fromMaybe e $ hexToBlockHash $ cs s--instance FromJSON BlockHash where-    parseJSON = withText "block hash" $-        maybe mzero return . hexToBlockHash--instance ToJSON BlockHash where-    toJSON = String . blockHashToHex-    toEncoding s =-        unsafeToEncoding $-        char7 '"' <> hexBuilder (B.reverse (encode s)) <> char7 '"'---- | Block hashes are reversed with respect to the in-memory byte order in a--- block hash when displayed.-blockHashToHex :: BlockHash -> Text-blockHashToHex (BlockHash h) = encodeHex (B.reverse (encode h))---- | Convert a human-readable hex block hash into a 'BlockHash'. Bytes are--- reversed as normal.-hexToBlockHash :: Text -> Maybe BlockHash-hexToBlockHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- eitherToMaybe (decode bs)-    return $ BlockHash h---- | Data type recording information of a 'Block'. The hash of a block is--- defined as the hash of this data structure, serialized. The block mining--- process involves finding a partial hash collision by varying the nonce in the--- 'BlockHeader' and/or additional entropy in the coinbase 'Transaction' of this--- 'Block'. Variations in the coinbase will result in different merkle roots in--- the 'BlockHeader'.-data BlockHeader =-    BlockHeader { blockVersion   :: !Word32      --  4 bytes-                  -- | hash of the previous block (parent)-                , prevBlock      :: !BlockHash   -- 32 bytes-                  -- | root of the merkle tree of transactions-                , merkleRoot     :: !Hash256     -- 32 bytes-                  -- | unix timestamp-                , blockTimestamp :: !Timestamp   --  4 bytes-                  -- | difficulty target-                , blockBits      :: !Word32      --  4 bytes-                  -- | random nonce-                , bhNonce        :: !Word32      --  4 bytes-                } deriving (Eq, Ord, Show, Read, Generic, Hashable, NFData)-                                                 -- 80 bytes---- | Compute hash of 'BlockHeader'.-headerHash :: BlockHeader -> BlockHash-headerHash = BlockHash . doubleSHA256 . encode--instance Serialize BlockHeader where-    get = do-        v <- getWord32le-        p <- get-        m <- get-        t <- getWord32le-        b <- getWord32le-        n <- getWord32le-        return BlockHeader-            { blockVersion   = v-            , prevBlock      = p-            , merkleRoot     = m-            , blockTimestamp = t-            , blockBits      = b-            , bhNonce        = n-            }--    put (BlockHeader v p m bt bb n) = do-        putWord32le v-        put         p-        put         m-        putWord32le bt-        putWord32le bb-        putWord32le n---- | A block locator is a set of block headers, denser towards the best block--- and sparser towards the genesis block. It starts at the highest block known.--- It is used by a node to synchronize against the network. When the locator is--- provided to a peer, it will send back block hashes starting from the first--- block in the locator that it recognizes.-type BlockLocator = [BlockHash]---- | Data type representing a getblocks message request. It is used in the--- bitcoin protocol to retrieve blocks from a peer by providing it a--- 'BlockLocator' object. The response to a 'GetBlocks' message is an 'Inv'--- message containing a list of block hashes that the peer believes this node is--- missing. The number of block hashes in that inv message will end at the stop--- block hash, at at the tip of the chain, or after 500 entries, whichever comes--- earlier.-data GetBlocks =-    GetBlocks { -- | protocol version.-                getBlocksVersion  :: !Word32-                -- | block locator object-              , getBlocksLocator  :: !BlockLocator-                -- | hash of the last desired block-              , getBlocksHashStop :: !BlockHash-              } deriving (Eq, Show, Generic, NFData)--instance Serialize GetBlocks where--    get = GetBlocks <$> getWord32le-                    <*> (repList =<< get)-                    <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get--    put (GetBlocks v xs h) = putGetBlockMsg v xs h--putGetBlockMsg :: Word32 -> BlockLocator -> BlockHash -> Put-putGetBlockMsg v xs h = do-    putWord32le v-    putVarInt $ length xs-    forM_ xs put-    put h---- | Similar to the 'GetBlocks' message type but for retrieving block headers--- only. The response to a 'GetHeaders' request is a 'Headers' message--- containing a list of block headers. A maximum of 2000 block headers can be--- returned. 'GetHeaders' is used by simplified payment verification (SPV)--- clients to exclude block contents when synchronizing the block chain.-data GetHeaders =-    GetHeaders {-                 -- | protocol version-                 getHeadersVersion  :: !Word32-                 -- | block locator object-               , getHeadersBL       :: !BlockLocator-                 -- | hash of the last desired block header-               , getHeadersHashStop :: !BlockHash-               } deriving (Eq, Show, Generic, NFData)--instance Serialize GetHeaders where--    get = GetHeaders <$> getWord32le-                     <*> (repList =<< get)-                     <*> get-      where-        repList (VarInt c) = replicateM (fromIntegral c) get--    put (GetHeaders v xs h) = putGetBlockMsg v xs h---- | 'BlockHeader' type with a transaction count as 'VarInt'-type BlockHeaderCount = (BlockHeader, VarInt)---- | The 'Headers' type is used to return a list of block headers in--- response to a 'GetHeaders' message.-newtype Headers =-    Headers { -- | list of block headers with transaction count-              headersList :: [BlockHeaderCount]-            }-    deriving (Eq, Show, Generic, NFData)--instance Serialize Headers where--    get = Headers <$> (repList =<< get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action = liftM2 (,) get get--    put (Headers xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a,b) -> put a >> put b---- | Decode the compact number used in the difficulty target of a block.------ The compact format is a representation of a whole number \(N\) using an--- unsigned 32-bit number similar to a floating point format. The most--- significant 8 bits are the unsigned exponent of base 256. This exponent can--- be thought of as the number of bytes of \(N\). The lower 23 bits are the--- mantissa. Bit number 24 represents the sign of \(N\).------ \[--- N = -1^{sign} \times mantissa \times 256^{exponent-3}--- \]-decodeCompact :: Word32 -> (Integer, Bool) -- ^ true means overflow-decodeCompact nCompact = (if neg then res * (-1) else res, over)-  where-    nSize :: Int-    nSize = fromIntegral nCompact `shiftR` 24-    nWord' :: Word32-    nWord' = nCompact .&. 0x007fffff-    nWord :: Word32-    nWord | nSize <= 3 = nWord' `shiftR` (8 * (3 - nSize))-          | otherwise = nWord'-    res :: Integer-    res | nSize <= 3 = fromIntegral nWord-        | otherwise = fromIntegral nWord `shiftL` (8 * (nSize - 3))-    neg = nWord /= 0 && (nCompact .&. 0x00800000) /= 0-    over = nWord /= 0 && (nSize > 34 ||-                          nWord > 0xff && nSize > 33 ||-                          nWord > 0xffff && nSize > 32)---- | Encode an 'Integer' to the compact number format used in the difficulty--- target of a block.-encodeCompact :: Integer-              -> Word32-encodeCompact i = nCompact-  where-    i' = abs i-    neg = i < 0-    nSize' :: Int-    nSize' = let f 0 = 0-                 f n = 1 + f (n `shiftR` 8)-             in f i'-    nCompact''' :: Word32-    nCompact'''-        | nSize' <= 3 = fromIntegral $ (low64 .&. i') `shiftL` (8 * (3 - nSize'))-        | otherwise = fromIntegral $ low64 .&. (i' `shiftR` (8 * (nSize' - 3)))-    nCompact'' :: Word32-    nSize :: Int-    (nCompact'', nSize)-        | nCompact''' .&. 0x00800000 /= 0 = (nCompact''' `shiftR` 8, nSize' + 1)-        | otherwise = (nCompact''', nSize')-    nCompact' :: Word32-    nCompact' = nCompact'' .|. (fromIntegral nSize `shiftL` 24)-    nCompact :: Word32-    nCompact | neg && (nCompact' .&. 0x007fffff /= 0) = nCompact' .|. 0x00800000-             | otherwise = nCompact'-    low64 :: Integer-    low64 = 0xffffffffffffffff
− src/Network/Haskoin/Block/Headers.hs
@@ -1,741 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE RecordWildCards   #-}-{-|-Module      : Network.Haskoin.Block.Headers-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Block chain header synchronization and proof-of-work consensus functions.--}-module Network.Haskoin.Block.Headers-    ( BlockNode(..)-    , BlockHeaders(..)-    , BlockWork-    , genesisNode-    , genesisBlock-    , isGenesis-    , chooseBest-      -- * Header Chain Storage Functions-    , parentBlock-    , getParents-    , getAncestor-    , splitPoint-    , connectBlocks-    , connectBlock-    , blockLocator-      -- * In-Memory Header Chain Store-    , HeaderMemory(..)-    , ShortBlockHash-    , BlockMap-    , shortBlockHash-    , initialChain-    , genesisMap-      -- * Helper Functions-    , appendBlocks-    , validBlock-    , validCP-    , afterLastCP-    , bip34-    , validVersion-    , lastNoMinDiff-    , nextWorkRequired-    , nextEdaWorkRequired-    , nextDaaWorkRequired-    , computeTarget-    , getSuitableBlock-    , nextPowWorkRequired-    , calcNextWork-    , isValidPOW-    , blockPOW-    , headerWork-    , diffInterval-    , blockLocatorNodes-    , mineBlock-    , computeSubsidy-    , ) where--import           Control.Applicative                ((<|>))-import           Control.DeepSeq-import           Control.Monad                      (guard, unless, when)-import           Control.Monad.Except               (ExceptT (..), runExceptT,-                                                     throwError)-import           Control.Monad.State.Strict         as State (StateT, get, gets,-                                                              lift, modify)-import           Control.Monad.Trans.Maybe-import           Data.Bits                          (shiftL, shiftR, (.&.))-import qualified Data.ByteString                    as B-import           Data.ByteString.Short              (ShortByteString, fromShort,-                                                     toShort)-import           Data.Function                      (on)-import           Data.Hashable-import           Data.HashMap.Strict                (HashMap)-import qualified Data.HashMap.Strict                as HashMap-import           Data.List                          (sort, sortBy)-import           Data.Maybe                         (fromMaybe, listToMaybe)-import           Data.Serialize                     as S (Serialize (..),-                                                          decode, encode, get,-                                                          put)-import           Data.Serialize.Get                 as S-import           Data.Serialize.Put                 as S-import           Data.Typeable                      (Typeable)-import           Data.Word                          (Word32, Word64)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util---- | Short version of the block hash. Uses the good end of the hash (the part--- that doesn't have a long string of zeroes).-type ShortBlockHash = Word64---- | Memory-based map to a serialized 'BlockNode' data structure.--- 'ShortByteString' is used to avoid memory fragmentation and make the data--- structure compact.-type BlockMap = HashMap ShortBlockHash ShortByteString---- | Represents accumulated work in the block chain so far.-type BlockWork = Integer---- | Data structure representing a block header and its position in the--- block chain.-data BlockNode-    -- | non-Genesis block header-    = BlockNode { nodeHeader :: !BlockHeader-                , nodeHeight :: !BlockHeight-        -- | accumulated work so far-                , nodeWork   :: !BlockWork-        -- | akip magic block hash-                , nodeSkip   :: !BlockHash }-    -- | Genesis block header-    | GenesisNode { nodeHeader :: !BlockHeader-                  , nodeHeight :: !BlockHeight-                  , nodeWork   :: !BlockWork }-    deriving (Show, Read, Generic, Hashable, NFData)--instance Serialize BlockNode where-    get = do-        nodeHeader <- S.get-        nodeHeight <- getWord32le-        nodeWork <- S.get-        if nodeHeight == 0-            then return GenesisNode {..}-            else do-                nodeSkip <- S.get-                return BlockNode {..}-    put bn = do-        put $ nodeHeader bn-        putWord32le $ nodeHeight bn-        put $ nodeWork bn-        case bn of-            GenesisNode {} -> return ()-            BlockNode {}   -> put $ nodeSkip bn--instance Eq BlockNode where-    (==) = (==) `on` nodeHeader--instance Ord BlockNode where-    compare = compare `on` nodeHeight---- | Memory-based header tree.-data HeaderMemory = HeaderMemory-    { memoryHeaderMap  :: !BlockMap-    , memoryBestHeader :: !BlockNode-    } deriving (Eq, Typeable, Show, Read, Generic, Hashable, NFData)---- | Typeclass for block header chain storage monad.-class Monad m => BlockHeaders m where-    -- | Add a new 'BlockNode' to the chain. Does not validate.-    addBlockHeader :: BlockNode -> m ()-    -- | Get a 'BlockNode' associated with a 'BlockHash'.-    getBlockHeader :: BlockHash -> m (Maybe BlockNode)-    -- | Locate the 'BlockNode' for the highest block in the chain-    getBestBlockHeader :: m BlockNode-    -- | Set the highest block in the chain.-    setBestBlockHeader :: BlockNode -> m ()-    -- | Add a continuous bunch of block headers the chain. Does not validate.-    addBlockHeaders :: [BlockNode] -> m ()-    addBlockHeaders = mapM_ addBlockHeader--instance Monad m => BlockHeaders (StateT HeaderMemory m) where-    addBlockHeader = modify . addBlockHeaderMemory-    getBlockHeader bh = getBlockHeaderMemory bh <$> State.get-    getBestBlockHeader = gets memoryBestHeader-    setBestBlockHeader bn = modify $ \s -> s { memoryBestHeader = bn }---- | Initialize memory-based chain.-initialChain :: Network -> HeaderMemory-initialChain net = HeaderMemory-    { memoryHeaderMap = genesisMap net-    , memoryBestHeader = genesisNode net-    }---- | Initialize map for memory-based chain.-genesisMap :: Network -> BlockMap-genesisMap net =-    HashMap.singleton-        (shortBlockHash (headerHash (getGenesisHeader net)))-        (toShort (encode (genesisNode net)))---- | Add block header to memory block map.-addBlockHeaderMemory :: BlockNode -> HeaderMemory -> HeaderMemory-addBlockHeaderMemory bn s@HeaderMemory{..} =-    let bm' = addBlockToMap bn memoryHeaderMap-    in s { memoryHeaderMap = bm' }---- | Get block header from memory block map.-getBlockHeaderMemory :: BlockHash -> HeaderMemory -> Maybe BlockNode-getBlockHeaderMemory bh HeaderMemory {..} = do-    bs <- shortBlockHash bh `HashMap.lookup` memoryHeaderMap-    eitherToMaybe . decode $ fromShort bs---- | Calculate short block hash taking eight non-zero bytes from the 16-byte--- hash. This function will take the bytes that are not on the zero-side of the--- hash, making colissions between short block hashes difficult.-shortBlockHash :: BlockHash -> ShortBlockHash-shortBlockHash = either error id . decode . B.take 8 . encode---- | Add a block to memory-based block map.-addBlockToMap :: BlockNode -> BlockMap -> BlockMap-addBlockToMap node =-    HashMap.insert-    (shortBlockHash $ headerHash $ nodeHeader node)-    (toShort $ encode node)---- | Get the ancestor of the provided 'BlockNode' at the specified--- 'BlockHeight'.-getAncestor :: BlockHeaders m-            => BlockHeight-            -> BlockNode-            -> m (Maybe BlockNode)-getAncestor height node-    | height > nodeHeight node = return Nothing-    | otherwise = go node-  where-    e1 = error "Could not get skip header"-    e2 = error "Could not get previous block header"-    go walk-        | nodeHeight walk > height =-            let heightSkip = skipHeight (nodeHeight walk)-                heightSkipPrev = skipHeight (nodeHeight walk - 1)-             in if not (isGenesis walk) &&-                   (heightSkip == height ||-                    (heightSkip > height &&-                     not-                         (heightSkipPrev < heightSkip - 2 &&-                          heightSkipPrev >= height)))-                    then do-                        walk' <- fromMaybe e1 <$> getBlockHeader (nodeSkip walk)-                        go walk'-                    else do-                        walk' <--                            fromMaybe e2 <$>-                            getBlockHeader (prevBlock (nodeHeader walk))-                        go walk'-        | otherwise = return $ Just walk---- | Is the provided 'BlockNode' the Genesis block?-isGenesis :: BlockNode -> Bool-isGenesis GenesisNode{} = True-isGenesis BlockNode{}   = False---- | Build the genesis 'BlockNode' for the supplied 'Network'.-genesisNode :: Network -> BlockNode-genesisNode net =-    GenesisNode-        { nodeHeader = getGenesisHeader net-        , nodeHeight = 0-        , nodeWork = headerWork (getGenesisHeader net)-        }---- | Validate a list of continuous block headers and import them to the--- block chain. Return 'Left' on failure with error information.-connectBlocks :: BlockHeaders m-              => Network-              -> Timestamp       -- ^ current time-              -> [BlockHeader]-              -> m (Either String [BlockNode])-connectBlocks _ _ [] = return $ Right []-connectBlocks net t bhs@(bh:_) =-    runExceptT $ do-        unless (chained bhs) $-            throwError "Blocks to connect do not form a chain"-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        bb <- lift getBestBlockHeader-        go par [] bb par pars bhs >>= \case-            bns@(bn:_) -> do-                lift $ addBlockHeaders bns-                let bb' = chooseBest bn bb-                when (bb' /= bb) $ lift $ setBestBlockHeader bb'-                return bns-            _ -> undefined-  where-    chained (h1:h2:hs) = headerHash h1 == prevBlock h2 && chained (h2 : hs)-    chained _          = True-    skipit lbh ls par-        | sh == nodeHeight lbh = return lbh-        | sh < nodeHeight lbh = do-            skM <- lift $ getAncestor sh lbh-            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        | otherwise = do-            let sn = ls !! fromIntegral (nodeHeight par - sh)-            when (nodeHeight sn /= sh) $-                throwError "BUG: Node height not right in skip"-            return sn-      where-        sh = skipHeight (nodeHeight par + 1)-    go _ acc _ _ _ [] = return acc-    go lbh acc bb par pars (h:hs) = do-        sk <- skipit lbh acc par-        bn <- ExceptT . return $ validBlock net t bb par pars h sk-        go lbh (bn : acc) (chooseBest bn bb) bn (take 10 $ par : pars) hs---- | Block's parent. If the block header is in the store, its parent must also--- be there. No block header get deleted or pruned from the store.-parentBlock :: BlockHeaders m-            => BlockHeader-            -> m (Maybe BlockNode)-parentBlock bh = getBlockHeader (prevBlock bh)---- | Validate and connect single block header to the block chain. Return 'Left' if fails--- to be validated.-connectBlock ::-       BlockHeaders m-    => Network-    -> Timestamp -- ^ current time-    -> BlockHeader-    -> m (Either String BlockNode)-connectBlock net t bh =-    runExceptT $ do-        par <--            maybeToExceptT-                "Could not get parent block"-                (MaybeT (parentBlock bh))-        pars <- lift $ getParents 10 par-        skM <- lift $ getAncestor (skipHeight (nodeHeight par + 1)) par-        sk <--            case skM of-                Just sk -> return sk-                Nothing ->-                    throwError $-                    "BUG: Could not get skip for block " ++-                    show (headerHash $ nodeHeader par)-        bb <- lift getBestBlockHeader-        bn <- ExceptT . return $ validBlock net t bb par pars bh sk-        let bb' = chooseBest bb bn-        lift $ addBlockHeader bn-        when (bb /= bb') . lift $ setBestBlockHeader bb'-        return bn---- | Validate this block header. Build a 'BlockNode' if successful.-validBlock :: Network-           -> Timestamp     -- ^ current time-           -> BlockNode     -- ^ best block-           -> BlockNode     -- ^ immediate parent-           -> [BlockNode]   -- ^ 10 parents above-           -> BlockHeader   -- ^ header to validate-           -> BlockNode     -- ^ skip node (black magic)-           -> Either String BlockNode-validBlock net t bb par pars bh sk = do-    let mt = medianTime . map (blockTimestamp . nodeHeader) $ par : pars-        nt = blockTimestamp bh-        hh = headerHash bh-        nv = blockVersion bh-        ng = nodeHeight par + 1-        aw = nodeWork par + headerWork bh-    unless (isValidPOW net bh) $-        Left $ "Proof of work failed: " ++ show (headerHash bh)-    unless (nt <= t + 2 * 60 * 60) $-        Left $ "Invalid header timestamp: " ++ show nt-    unless (nt >= mt) $-        Left $ "Block timestamp too early: " ++ show nt-    unless (afterLastCP net (nodeHeight bb) ng) $-        Left $ "Rewriting pre-checkpoint chain: " ++ show ng-    unless (validCP net ng hh) $-        Left $ "Rejected checkpoint: " ++ show ng-    unless (bip34 net ng hh) $-        Left $ "Rejected BIP-34 block: " ++ show hh-    unless (validVersion net ng nv) $-        Left $ "Invalid block version: " ++ show nv-    return BlockNode { nodeHeader = bh-                     , nodeHeight = ng-                     , nodeWork = aw-                     , nodeSkip = headerHash $ nodeHeader sk-                     }---- | Return the median of all provided timestamps. Can be unsorted. Error on--- empty list.-medianTime :: [Timestamp] -> Timestamp-medianTime ts-    | null ts = error "Cannot compute median time of empty header list"-    | otherwise = sort ts !! (length ts `div` 2)---- | Calculate the height of the skip (magic) block that corresponds to the--- given height. The block hash of the ancestor at that height will be placed on--- the 'BlockNode' structure to help locate ancestors at any height quickly.-skipHeight :: BlockHeight -> BlockHeight-skipHeight height-    | height < 2 = 0-    | height .&. 1 /= 0 = invertLowestOne (invertLowestOne $ height - 1) + 1-    | otherwise = invertLowestOne height---- | Part of the skip black magic calculation.-invertLowestOne :: BlockHeight -> BlockHeight-invertLowestOne height = height .&. (height - 1)---- | Get a number of parents for the provided block.-getParents :: BlockHeaders m-           => Int-           -> BlockNode-           -> m [BlockNode]   -- ^ starts from immediate parent-getParents = getpars []-  where-    getpars acc 0 _ = return $ reverse acc-    getpars acc _ GenesisNode{} = return $ reverse acc-    getpars acc n BlockNode{..} = do-        parM <- getBlockHeader $ prevBlock nodeHeader-        case parM of-            Just bn -> getpars (bn : acc) (n - 1) bn-            Nothing -> error "BUG: All non-genesis blocks should have a parent"---- | Verify that checkpoint location is valid.-validCP :: Network-        -> BlockHeight  -- ^ new child height-        -> BlockHash    -- ^ new child hash-        -> Bool-validCP net height newChildHash =-    case lookup height (getCheckpoints net) of-        Just cpHash -> cpHash == newChildHash-        Nothing     -> True---- | New block height above the last checkpoint imported. Used to prevent a--- reorg below the highest checkpoint that was already imported.-afterLastCP :: Network-            -> BlockHeight  -- ^ best height-            -> BlockHeight  -- ^ new imported block height-            -> Bool-afterLastCP net bestHeight newChildHeight =-    case lM of-        Just l  -> l < newChildHeight-        Nothing -> True-  where-    lM =-        listToMaybe . reverse $-        [c | (c, _) <- getCheckpoints net, c <= bestHeight]---- | This block should be at least version 2 (BIP34). Block height must be--- included in the coinbase transaction to prevent non-unique transaction--- hashes.-bip34 :: Network-      -> BlockHeight  -- ^ new child height-      -> BlockHash    -- ^ new child hash-      -> Bool-bip34 net height hsh-    | fst (getBip34Block net) == 0 = True-    | fst (getBip34Block net) == height = snd (getBip34Block net) == hsh-    | otherwise = True---- | Check if the provided block height and version are valid.-validVersion :: Network-             -> BlockHeight  -- ^ new child height-             -> Word32       -- ^ new child version-             -> Bool-validVersion net height version-    | version < 2 = height < fst (getBip34Block net)-    | version < 3 = height < getBip66Height net-    | version < 4 = height < getBip65Height net-    | otherwise = True---- | Find last block with normal, as opposed to minimum difficulty (for test--- networks).-lastNoMinDiff :: BlockHeaders m => Network -> BlockNode -> m BlockNode-lastNoMinDiff net bn@BlockNode {..} = do-    let i = nodeHeight `mod` diffInterval net /= 0-        c = encodeCompact (getPowLimit net)-        l = blockBits nodeHeader == c-        e1 =-            error $-            "Could not get block header for parent of " ++-            show (headerHash nodeHeader)-    if i && l-        then do-            bn' <- fromMaybe e1 <$> getBlockHeader (prevBlock nodeHeader)-            lastNoMinDiff net bn'-        else return bn--lastNoMinDiff _ bn@GenesisNode{} = return bn---- | Returns the work required on a block header given the previous block. This--- coresponds to @bitcoind@ function @GetNextWorkRequired@ in @main.cpp@.-nextWorkRequired :: BlockHeaders m-                 => Network-                 -> BlockNode-                 -> BlockHeader-                 -> m Word32-nextWorkRequired net par bh = do-    let mf = daa <|> eda <|> pow-    case mf of-        Just f -> f net par bh-        Nothing ->-            error-                "Could not get an appropriate difficulty calculation algorithm"-  where-    daa = getDaaBlockHeight net >>= \daaHeight -> do-        guard (nodeHeight par + 1 >= daaHeight)-        return nextDaaWorkRequired-    eda = getEdaBlockHeight net >>= \edaHeight -> do-        guard (nodeHeight par + 1 >= edaHeight)-        return nextEdaWorkRequired-    pow = return nextPowWorkRequired---- | Find out the next amount of work required according to the Emergency--- Difficulty Adjustment (EDA) algorithm from Bitcoin Cash.-nextEdaWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextEdaWorkRequired net par bh-    | nodeHeight par + 1 `mod` diffInterval net == 0 =-        nextWorkRequired net par bh-    | minDifficulty = return (encodeCompact (getPowLimit net))-    | blockBits (nodeHeader par) == encodeCompact (getPowLimit net) =-        return (encodeCompact (getPowLimit net))-    | otherwise = do-        par6 <- fromMaybe e1 <$> getAncestor (nodeHeight par - 6) par-        pars <- getParents 10 par-        pars6 <- getParents 10 par6-        let par6med =-                medianTime $ map (blockTimestamp . nodeHeader) (par6 : pars6)-            parmed = medianTime $ map (blockTimestamp . nodeHeader) (par : pars)-            mtp6 = parmed - par6med-        if mtp6 < 12 * 3600-            then return $ blockBits (nodeHeader par)-            else return $-                 let (diff, _) = decodeCompact (blockBits (nodeHeader par))-                     ndiff = diff + (diff `shiftR` 2)-                  in if getPowLimit net > ndiff-                         then encodeCompact (getPowLimit net)-                         else encodeCompact ndiff-  where-    minDifficulty =-        blockTimestamp bh >-        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2-    e1 = error "Could not get seventh ancestor of block"---- | Find the next amount of work required according to the Difficulty--- Adjustment Algorithm (DAA) from Bitcoin Cash.-nextDaaWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextDaaWorkRequired net par bh-    | minDifficulty = return (encodeCompact (getPowLimit net))-    | otherwise = do-        let height = nodeHeight par-        unless (height >= diffInterval net) $-            error "Block height below difficulty interval"-        l <- getSuitableBlock par-        par144 <- fromMaybe e1 <$> getAncestor (height - 144) par-        f <- getSuitableBlock par144-        let nextTarget = computeTarget net f l-        if nextTarget > getPowLimit net-            then return $ encodeCompact (getPowLimit net)-            else return $ encodeCompact nextTarget-  where-    e1 = error "Cannot get ancestor at parent - 144 height"-    minDifficulty =-        blockTimestamp bh >-        blockTimestamp (nodeHeader par) + getTargetSpacing net * 2---- | Compute Bitcoin Cash DAA target for a new block.-computeTarget :: Network -> BlockNode -> BlockNode -> Integer-computeTarget net f l =-    let work = (nodeWork l - nodeWork f) * fromIntegral (getTargetSpacing net)-        actualTimespan =-            blockTimestamp (nodeHeader l) - blockTimestamp (nodeHeader f)-        actualTimespan'-            | actualTimespan > 288 * getTargetSpacing net =-                288 * getTargetSpacing net-            | actualTimespan < 72 * getTargetSpacing net =-                72 * getTargetSpacing net-            | otherwise = actualTimespan-        work' = work `div` fromIntegral actualTimespan'-     in 2 ^ (256 :: Integer) `div` work'---- | Get suitable block for Bitcoin Cash DAA computation.-getSuitableBlock :: BlockHeaders m => BlockNode -> m BlockNode-getSuitableBlock par = do-    unless (nodeHeight par >= 3) $ error "Block height is less than three"-    blocks <- (par :) <$> getParents 2 par-    return $ sortBy (compare `on` blockTimestamp . nodeHeader) blocks !! 1---- | Returns the work required on a block header given the previous block. This--- coresponds to bitcoind function GetNextWorkRequired in main.cpp.-nextPowWorkRequired ::-       BlockHeaders m => Network -> BlockNode -> BlockHeader -> m Word32-nextPowWorkRequired net par bh-    | nodeHeight par + 1 `mod` diffInterval net /= 0 =-        if getAllowMinDifficultyBlocks net-            then if ht > pt + delta-                     then return $ encodeCompact (getPowLimit net)-                     else do-                         d <- lastNoMinDiff net par-                         return $ blockBits $ nodeHeader d-            else return $ blockBits $ nodeHeader par-    | otherwise = do-        let rh = nodeHeight par - (diffInterval net - 1)-        a <- fromMaybe e1 <$> getAncestor rh par-        let t = blockTimestamp $ nodeHeader a-        return $ calcNextWork net (nodeHeader par) t-  where-    e1 = error "Could not get ancestor for block header"-    pt = blockTimestamp $ nodeHeader par-    ht = blockTimestamp bh-    delta = getTargetSpacing net * 2---- | Computes the work required for the first block in a new retarget period.-calcNextWork :: Network-             -> BlockHeader  -- ^ last block in previous retarget (parent)-             -> Timestamp    -- ^ timestamp of first block in previous retarget-             -> Word32-calcNextWork net header time-    | getPowNoRetargetting net = blockBits header-    | new > getPowLimit net = encodeCompact (getPowLimit net)-    | otherwise = encodeCompact new-  where-    s = blockTimestamp header - time-    n | s < getTargetTimespan net `div` 4 = getTargetTimespan net `div` 4-      | s > getTargetTimespan net * 4 = getTargetTimespan net * 4-      | otherwise = s-    l = fst $ decodeCompact $ blockBits header-    new = l * fromIntegral n `div` fromIntegral (getTargetTimespan net)---- | Returns True if the difficulty target (bits) of the header is valid and the--- proof of work of the header matches the advertised difficulty target. This--- function corresponds to the function @CheckProofOfWork@ from @bitcoind@ in--- @main.cpp@.-isValidPOW :: Network -> BlockHeader -> Bool-isValidPOW net h-    | target <= 0 || over || target > getPowLimit net = False-    | otherwise = blockPOW (headerHash h) <= fromIntegral target-  where-    (target, over) = decodeCompact $ blockBits h---- | Returns the proof of work of a block header hash as an 'Integer' number.-blockPOW :: BlockHash -> Integer-blockPOW =  bsToInteger . B.reverse . encode---- | Returns the work represented by this block. Work is defined as the number--- of tries needed to solve a block in the average case with respect to the--- target.-headerWork :: BlockHeader -> Integer-headerWork bh = largestHash `div` (target + 1)-  where-    target      = fst $ decodeCompact $ blockBits bh-    largestHash = 1 `shiftL` 256---- | Number of blocks on average between difficulty cycles (2016 blocks).-diffInterval :: Network -> Word32-diffInterval net = getTargetTimespan net `div` getTargetSpacing net---- | Compare two blocks to get the best.-chooseBest :: BlockNode -> BlockNode -> BlockNode-chooseBest b1 b2 | nodeWork b1 == nodeWork b2 =-                       if nodeHeight b1 >= nodeHeight b2-                       then b1-                       else b2-                 | nodeWork b1 > nodeWork b2 = b1-                 | otherwise = b2---- | Get list of blocks for a block locator.-blockLocatorNodes :: BlockHeaders m => BlockNode -> m [BlockNode]-blockLocatorNodes best =-    reverse <$> go [] best 1-  where-    e1 = error "Could not get ancestor"-    go loc bn n =-        let loc' = bn : loc-            n' = if length loc' > 10-                 then n * 2-                 else 1-        in if nodeHeight bn < n'-           then do a <- fromMaybe e1 <$> getAncestor 0 bn-                   return $ a : loc'-           else do let h = nodeHeight bn - n'-                   bn' <- fromMaybe e1 <$> getAncestor h bn-                   go loc' bn' n'---- | Get block locator.-blockLocator :: BlockHeaders m => BlockNode -> m BlockLocator-blockLocator bn = map (headerHash . nodeHeader) <$> blockLocatorNodes bn---- | Become rich beyond your wildest dreams.-mineBlock :: Network -> Word32 -> BlockHeader -> BlockHeader-mineBlock net seed h =-    head-        [ j-        | i <- (+ seed) <$> [0 .. maxBound]-        , let j = h {bhNonce = i}-        , isValidPOW net j-        ]---- | Generate and append new blocks (mining). Only practical in regtest network.-appendBlocks ::-       Network-    -> Word32 -- ^ random seed-    -> BlockHeader-    -> Int-    -> [BlockHeader]-appendBlocks _ _ _ 0 = []-appendBlocks net seed bh i =-    bh' : appendBlocks net seed bh' (i - 1)-  where-    bh' = mineBlock net seed bh-        { prevBlock = headerHash bh-          -- Just to make it different in every header-        , merkleRoot = sha256 $ encode seed-        }---- | Find the last common block ancestor between provided block headers.-splitPoint :: BlockHeaders m => BlockNode -> BlockNode -> m BlockNode-splitPoint l r = do-    let h = min (nodeHeight l) (nodeHeight r)-    ll <- fromMaybe e <$> getAncestor h l-    lr <- fromMaybe e <$> getAncestor h r-    f ll lr-  where-    e = error "BUG: Could not get ancestor at lowest height"-    f ll lr =-        if ll == lr-            then return lr-            else do-                let h = nodeHeight ll - 1-                pl <- fromMaybe e <$> getAncestor h ll-                pr <- fromMaybe e <$> getAncestor h lr-                f pl pr---- | Generate the entire Genesis block for 'Network'.-genesisBlock :: Network -> Block-genesisBlock net = Block (getGenesisHeader net) [genesisTx]---- | Compute block subsidy at particular height.-computeSubsidy :: Network -> BlockHeight -> Word64-computeSubsidy net height =-    let halvings = height `div` getHalvingInterval net-        ini = 50 * 100 * 1000 * 1000-     in if halvings >= 64-            then 0-            else ini `shiftR` fromIntegral halvings
− src/Network/Haskoin/Block/Merkle.hs
@@ -1,256 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Block.Merkle-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Function to deal with Merkle trees inside blocks.--}-module Network.Haskoin.Block.Merkle-    ( MerkleBlock(..)-    , MerkleRoot-    , FlagBits-    , PartialMerkleTree-    , buildMerkleRoot-    , merkleBlockTxs-    , testMerkleRoot-    -- * Helper functions-    , buildPartialMerkle-    , decodeMerkleFlags-    , encodeMerkleFlags-    , calcTreeHeight-    , calcTreeWidth-    , hash2-    , calcHash-    , traverseAndBuild-    , traverseAndExtract-    , extractMatches-    , splitIn-    , boolsToWord8-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, replicateM, when)-import           Data.Bits-import qualified Data.ByteString                    as BS-import           Data.Either                        (isRight)-import           Data.Hashable-import           Data.Maybe-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (getWord32le, getWord8)-import           Data.Serialize.Put                 (putWord32le, putWord8)-import           Data.Word                          (Word32, Word8)-import           GHC.Generics-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common---- | Hash of the block's Merkle root.-type MerkleRoot        = Hash256---- | Bits that are used to rebuild partial merkle tree transaction hash list.-type FlagBits          = [Bool]---- | Partial Merkle tree for a filtered block.-type PartialMerkleTree = [Hash256]---- | Filtered block: a block with a partial Merkle tree that only includes the--- transactions that pass a bloom filter that was negotiated.-data MerkleBlock =-    MerkleBlock {-                -- | block header-                  merkleHeader    :: !BlockHeader-                -- | total number of transactions in block-                , merkleTotalTxns :: !Word32-                -- | hashes in depth-first order-                , mHashes         :: !PartialMerkleTree-                -- | bits to rebuild partial merkle tree-                , mFlags          :: !FlagBits-                } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize MerkleBlock where--    get = do-        header <- get-        ntx    <- getWord32le-        (VarInt matchLen) <- get-        hashes <- replicateM (fromIntegral matchLen) get-        (VarInt flagLen)  <- get-        ws <- replicateM (fromIntegral flagLen) getWord8-        return $ MerkleBlock header ntx hashes (decodeMerkleFlags ws)--    put (MerkleBlock h ntx hashes flags) = do-        put h-        putWord32le ntx-        putVarInt $ length hashes-        forM_ hashes put-        let ws = encodeMerkleFlags flags-        putVarInt $ length ws-        forM_ ws putWord8---- | Unpack Merkle flags into 'FlagBits' structure.-decodeMerkleFlags :: [Word8] -> FlagBits-decodeMerkleFlags ws =-    [ b | p <- [ 0 .. length ws * 8 - 1 ]-        , b <- [ testBit (ws !! (p `div` 8)) (p `mod` 8) ]-    ]---- | Pack Merkle flags from 'FlagBits'.-encodeMerkleFlags :: FlagBits -> [Word8]-encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs---- | Computes the height of a Merkle tree.-calcTreeHeight :: Int -- ^ number of transactions (leaf nodes)-               -> Int -- ^ height of the merkle tree-calcTreeHeight ntx | ntx < 2 = 0-                   | even ntx  = 1 + calcTreeHeight (ntx `div` 2)-                   | otherwise = calcTreeHeight $ ntx + 1---- | Computes the width of a Merkle tree at a specific height. The transactions--- are at height 0.-calcTreeWidth :: Int -- ^ number of transactions (leaf nodes)-              -> Int -- ^ height at which we want to compute the width-              -> Int -- ^ width of the Merkle tree-calcTreeWidth ntx h = (ntx + (1 `shiftL` h) - 1) `shiftR` h---- | Computes the root of a Merkle tree from a list of leaf node hashes.-buildMerkleRoot :: [TxHash]   -- ^ transaction hashes (leaf nodes)-                -> MerkleRoot -- ^ root of the Merkle tree-buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs---- | Concatenate and compute double SHA256.-hash2 :: Hash256 -> Hash256 -> Hash256-hash2 a b = doubleSHA256 $ encode a `BS.append` encode b---- | Computes the hash of a specific node in a Merkle tree.-calcHash :: Int       -- ^ height of the node-         -> Int       -- ^ position of the node (0 for the leftmost node)-         -> [TxHash]  -- ^ transaction hashes (leaf nodes)-         -> Hash256   -- ^ hash of the node at the specified position-calcHash height pos txs-    | height < 0 || pos < 0 = error "calcHash: Invalid parameters"-    | height == 0 = getTxHash $ txs !! pos-    | otherwise = hash2 left right-  where-    left = calcHash (height-1) (pos*2) txs-    right | pos*2+1 < calcTreeWidth (length txs) (height-1) =-                calcHash (height-1) (pos*2+1) txs-          | otherwise = left---- | Build a partial Merkle tree. Provide a list of tuples with all transaction--- hashes in the block, and whether the transaction is to be included in the--- partial tree. Returns a flag bits structure and the computed partial Merkle--- tree.-buildPartialMerkle ::-       [(TxHash, Bool)] -- ^ transaction hash and whether to include-    -> (FlagBits, PartialMerkleTree) -- ^ flag bits and partial Merkle tree-buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs---- | Helper function to build partial Merkle tree. Used by 'buildPartialMerkle'--- above.-traverseAndBuild ::-       Int -> Int -> [(TxHash, Bool)] -> (FlagBits, PartialMerkleTree)-traverseAndBuild height pos txs-    | height < 0 || pos < 0 = error "traverseAndBuild: Invalid parameters"-    | height == 0 || not match = ([match], [calcHash height pos t])-    | otherwise = (match : lb ++ rb, lh ++ rh)-  where-    t = map fst txs-    s = pos `shiftL` height-    e = min (length txs) $ (pos + 1) `shiftL` height-    match = any snd $ take (e - s) $ drop s txs-    (lb, lh) = traverseAndBuild (height - 1) (pos * 2) txs-    (rb, rh)-        | (pos * 2 + 1) < calcTreeWidth (length txs) (height - 1) =-            traverseAndBuild (height - 1) (pos * 2 + 1) txs-        | otherwise = ([], [])---- | Helper function to extract transaction hashes from partial Merkle tree.-traverseAndExtract :: Int -> Int -> Int -> FlagBits -> PartialMerkleTree-                   -> Maybe (MerkleRoot, [TxHash], Int, Int)-traverseAndExtract height pos ntx flags hashes-    | null flags               = Nothing-    | height == 0 || not match = leafResult-    | isNothing leftM          = Nothing-    | (pos*2+1) >= calcTreeWidth ntx (height-1) =-        Just (hash2 lh lh, lm, lcf+1, lch)-    | isNothing rightM         = Nothing-    | otherwise =-        Just (hash2 lh rh, lm ++ rm, lcf+rcf+1, lch+rch)-  where-    leafResult-        | null hashes = Nothing-        | otherwise = Just (h, [ TxHash h | height == 0 && match ], 1, 1)-    (match:fs) = flags-    (h:_)     = hashes-    leftM  = traverseAndExtract (height-1) (pos*2) ntx fs hashes-    (lh,lm,lcf,lch) = fromMaybe e leftM-    rightM = traverseAndExtract (height-1) (pos*2+1) ntx-                (drop lcf fs) (drop lch hashes)-    (rh,rm,rcf,rch) = fromMaybe e rightM-    e = error "traverseAndExtract: unexpected error extracting a Maybe value"---- | Extracts the matching hashes from a partial merkle tree. This will return--- the list of transaction hashes that have been included (set to true) in--- a call to 'buildPartialMerkle'.-extractMatches :: Network-               -> FlagBits-               -> PartialMerkleTree-               -> Int -- ^ number of transaction at height 0 (leaf nodes)-               -> Either String (MerkleRoot, [TxHash])-               -- ^ Merkle root and list of matching transaction hashes-extractMatches net flags hashes ntx-    | ntx == 0 = Left-        "extractMatches: number of transactions can not be 0"-    | ntx > getMaxBlockSize net `div` 60 = Left-        "extractMatches: number of transactions excessively high"-    | length hashes > ntx = Left-        "extractMatches: More hashes provided than the number of transactions"-    | length flags < length hashes = Left-        "extractMatches: At least one bit per node and one bit per hash"-    | isNothing resM = Left-        "extractMatches: traverseAndExtract failed"-    | (nBitsUsed+7) `div` 8 /= (length flags+7) `div` 8 = Left-        "extractMatches: All bits were not consumed"-    | nHashUsed /= length hashes = Left $-        "extractMatches: All hashes were not consumed: " ++ show nHashUsed-    | otherwise = return (merkRoot, matches)-  where-    resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes-    (merkRoot, matches, nBitsUsed, nHashUsed) = fromMaybe e resM-    e = error "extractMatches: unexpected error extracting a Maybe value"---- | Helper function to split a list in chunks 'Int' length. Last chunk may be--- smaller.-splitIn :: Int -> [a] -> [[a]]-splitIn _ [] = []-splitIn c xs = xs1 : splitIn c xs2-  where-    (xs1, xs2) = splitAt c xs---- | Pack up to eight bools in a byte.-boolsToWord8 :: [Bool] -> Word8-boolsToWord8 [] = 0-boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])---- | Get matching transactions from Merkle block.-merkleBlockTxs :: Network -> MerkleBlock -> Either String [TxHash]-merkleBlockTxs net b =-    let flags = mFlags b-        hs = mHashes b-        n = fromIntegral $ merkleTotalTxns b-        merkle = merkleRoot $ merkleHeader b-    in do (root, ths) <- extractMatches net flags hs n-          when (root /= merkle) $ Left "merkleBlockTxs: Merkle root incorrect"-          return ths---- | Check if Merkle block root is valid against the block header.-testMerkleRoot :: Network -> MerkleBlock -> Bool-testMerkleRoot net = isRight . merkleBlockTxs net
− src/Network/Haskoin/Constants.hs
@@ -1,540 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Constants-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Network constants for various networks, including Bitcoin SegWit (BTC), Bitcoin-Cash (BCH), and corresponding public test and private regression test networks.--}-module Network.Haskoin.Constants-    ( Network(..)-    , btc-    , btcTest-    , btcRegTest-    , bch-    , bchTest-    , bchRegTest-    , allNets-    , netByName-    , netByIdent-    ) where--import           Control.DeepSeq-import           Data.ByteString              (ByteString)-import           Data.List-import           Data.Maybe-import           Data.Serialize-import           Data.String-import           Data.Text                    (Text)-import           Data.Version-import           Data.Word                    (Word32, Word64, Word8)-import           GHC.Generics                 (Generic)-import           Network.Haskoin.Block.Common-import           Paths_haskoin_core-import           Text.Read---- | Version of Haskoin Core package.-versionString :: IsString a => a-versionString = fromString (showVersion version)---- | Constants for network.-data Network = Network-    { -- | lowercase alphanumeric and dashes-      getNetworkName              :: !String-      -- | network Haskell identifier-    , getNetworkIdent             :: !String-      -- | prefix for 'Base58' P2PKH addresses-    , getAddrPrefix               :: !Word8-      -- | prefix for 'Base58' P2SH addresses-    , getScriptPrefix             :: !Word8-      -- | prefix for WIF private key-    , getSecretPrefix             :: !Word8-      -- | prefix for extended public key-    , getExtPubKeyPrefix          :: !Word32-      -- | prefix for extended private key-    , getExtSecretPrefix          :: !Word32-      -- | network magic-    , getNetworkMagic             :: !Word32-      -- | genesis block header-    , getGenesisHeader            :: !BlockHeader-      -- | maximum block size in bytes-    , getMaxBlockSize             :: !Int-      -- | maximum amount of satoshi-    , getMaxSatoshi               :: !Word64-      -- | user agent string-    , getHaskoinUserAgent         :: !ByteString-      -- | default port for P2P connections-    , getDefaultPort              :: !Int-      -- | allow min difficulty blocks (testnet)-    , getAllowMinDifficultyBlocks :: !Bool-      -- | do not retarget difficulty (regtest)-    , getPowNoRetargetting        :: !Bool-      -- | proof-of-work target higest possible value-    , getPowLimit                 :: !Integer-      -- | block at which BIP34 activates-    , getBip34Block               :: !(BlockHeight, BlockHash)-      -- | block at which BIP65 activates-    , getBip65Height              :: !BlockHeight-      -- | block at which BIP66 activates-    , getBip66Height              :: !BlockHeight-      -- | time between difficulty retargets-    , getTargetTimespan           :: !Word32-      -- | time between blocks-    , getTargetSpacing            :: !Word32-      -- | checkpoints-    , getCheckpoints              :: ![(BlockHeight, BlockHash)]-      -- | BIP44 derivation path root-    , getBip44Coin                :: !Word32-      -- | peer-to-peer network seeds-    , getSeeds                    :: ![String]-      -- | fork id for replay protection-    , getSigHashForkId            :: !(Maybe Word32)-      -- | EDA start block height-    , getEdaBlockHeight           :: !(Maybe Word32)-      -- | DAA start block height-    , getDaaBlockHeight           :: !(Maybe Word32)-      -- | segregated witness active-    , getSegWit                   :: !Bool-      -- | 'CashAddr' prefix (for Bitcoin Cash)-    , getCashAddrPrefix           :: !(Maybe Text)-      -- | 'Bech32' prefix (for SegWit network)-    , getBech32Prefix             :: !(Maybe Text)-      -- | Replace-By-Fee (BIP-125)-    , getReplaceByFee             :: !Bool-      -- | Subsidy halving interval-    , getHalvingInterval          :: !Word32-    } deriving (Eq, Generic, NFData)--instance Serialize Network where-    put net =-        putWord32be $ getNetworkMagic net-    get = do-        magic <- getWord32be-        case find ((== magic) . getNetworkMagic) allNets of-            Nothing  -> fail $ "Network magic unknown: " <> show magic-            Just net -> return net--instance Show Network where-    show = getNetworkIdent--instance Read Network where-    readPrec = do-        Ident str <- lexP-        maybe pfail return (netByIdent str)--instance IsString Network where-    fromString = fromMaybe (error "Network name invalid") . netByName---- | Query known networks by name.-netByName :: String -> Maybe Network-netByName str = find ((== str) . getNetworkName) allNets---- | Query known networks by Haskell identifier.-netByIdent :: String -> Maybe Network-netByIdent str = find ((== str) . getNetworkIdent) allNets---- | Bitcoin SegWit network. Symbol: BTC.-btc :: Network-btc =-    Network-    { getNetworkName = "btc"-    , getNetworkIdent = "btc"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xf9beb4d9-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent =-          "/haskoin-btc:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-          ]-    , getSeeds =-          [ "seed.bitcoin.sipa.be" -- Pieter Wuille-          , "dnsseed.bluematt.me" -- Matt Corallo-          , "dnsseed.bitcoin.dashjr.org" -- Luke Dashjr-          , "seed.bitcoinstats.com" -- Chris Decker-          , "seed.bitcoin.jonasschnelli.ch" -- Jonas Schnelli-          , "seed.btc.petertodd.org" -- Peter Todd-          , "seed.bitcoin.sprovoost.nl" -- Sjors Provoost-          ]-    , getBip44Coin = 0-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "bc"-    , getReplaceByFee = True-    , getHalvingInterval = 210000-    }---- | Testnet for Bitcoin SegWit network.-btcTest :: Network-btcTest =-    Network-    { getNetworkName = "btctest"-    , getNetworkIdent = "btcTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0x0b110907-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoin.jonasschnelli.ch"-          , "seed.tbtc.petertodd.org"-          , "seed.testnet.bitcoin.sprovoost.nl"-          , "testnet-seed.bluematt.me"-          ]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "tb"-    , getReplaceByFee = True-    , getHalvingInterval = 210000-    }---- | RegTest for Bitcoin SegWit network.-btcRegTest :: Network-btcRegTest =-    Network-    { getNetworkName = "btcreg"-    , getNetworkIdent = "btcRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xfabfb5da-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-btc-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints = []-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Nothing-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Nothing-    , getSegWit = True-    , getCashAddrPrefix = Nothing-    , getBech32Prefix = Just "bcrt"-    , getReplaceByFee = True-    , getHalvingInterval = 150-    }---- | Bitcoin Cash network. Symbol: BCH.-bch :: Network-bch =-    Network-    { getNetworkName = "bch"-    , getNetworkIdent = "bch"-    , getAddrPrefix = 0-    , getScriptPrefix = 5-    , getSecretPrefix = 128-    , getExtPubKeyPrefix = 0x0488b21e-    , getExtSecretPrefix = 0x0488ade4-    , getNetworkMagic = 0xe3e1f3e8-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1231006505-              0x1d00ffff-              2083236893-            -- Hash 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch:" <> versionString <> "/"-    , getDefaultPort = 8333-    , getAllowMinDifficultyBlocks = False-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 227931-          , "000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8")-    , getBip65Height = 388381-    , getBip66Height = 363725-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 11111-            , "0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d")-          , ( 33333-            , "000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6")-          , ( 74000-            , "0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20")-          , ( 105000-            , "00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97")-          , ( 134444-            , "00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe")-          , ( 168000-            , "000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763")-          , ( 193000-            , "000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317")-          , ( 210000-            , "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e")-          , ( 216116-            , "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e")-          , ( 225430-            , "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932")-          , ( 250000-            , "000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214")-          , ( 279000-            , "0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40")-          , ( 295000-            , "00000000000000004d9b4ef50f0f9d686fd69db2e03af35a100370c64632a983")-            -- UAHF fork block.-          , ( 478559-            , "000000000000000000651ef99cb9fcbe0dadde1d424bd9f15ff20136191a5eec")-            -- Nov, 13 DAA activation block.-          , ( 504031-            , "0000000000000000011ebf65b60d0a3de80b8175be709d653b4c1a1beeb6ab9c")-          ]-    , getSeeds =-          [ "seed.bitcoinabc.org"-          , "seed-abc.bitcoinforks.org"-          , "btccash-seeder.bitcoinunlimited.info"-          , "seed.bitprim.org"-          , "seed.deadalnix.me"-          , "seeder.criptolayer.net"-          ]-    , getBip44Coin = 145-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Just 478559-    , getDaaBlockHeight = Just 404031-    , getSegWit = False-    , getCashAddrPrefix = Just "bitcoincash"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 210000-    }---- | Testnet for Bitcoin Cash network.-bchTest :: Network-bchTest =-    Network-    { getNetworkName = "bchtest"-    , getNetworkIdent = "bchTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xf4e5f3f4-    , getGenesisHeader =-          BlockHeader-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              486604799-              414098458-            -- Hash 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943-    , getMaxBlockSize = 32000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-test:" <> versionString <> "/"-    , getDefaultPort = 18333-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = False-    , getPowLimit =-          0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 21111-          , "0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8")-    , getBip65Height = 581885-    , getBip66Height = 330776-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 546-            , "000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")-            -- UAHF fork block.-          , ( 1155876-            , "00000000000e38fef93ed9582a7df43815d5c2ba9fd37ef70c9a0ea4a285b8f5")-            -- Nov, 13. DAA activation block.-          , ( 1188697-            , "0000000000170ed0918077bde7b4d36cc4c91be69fa09211f748240dabe047fb")-          ]-    , getSeeds =-          [ "testnet-seed.bitcoinabc.org"-          , "testnet-seed-abc.bitcoinforks.org"-          , "testnet-seed.bitprim.org"-          , "testnet-seed.deadalnix.me"-          , "testnet-seeder.criptolayer.net"-          ]-    , getBip44Coin = 1-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Just 1155876-    , getDaaBlockHeight = Just 1188697-    , getSegWit = False-    , getCashAddrPrefix = Just "bchtest"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 210000-    }---- | RegTest for Bitcoin Cash network.-bchRegTest :: Network-bchRegTest =-    Network-    { getNetworkName = "bchreg"-    , getNetworkIdent = "bchRegTest"-    , getAddrPrefix = 111-    , getScriptPrefix = 196-    , getSecretPrefix = 239-    , getExtPubKeyPrefix = 0x043587cf-    , getExtSecretPrefix = 0x04358394-    , getNetworkMagic = 0xdab5bffa-    , getGenesisHeader =-          BlockHeader-           -- 0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206-              0x01-              "0000000000000000000000000000000000000000000000000000000000000000"-              "3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a"-              1296688602-              0x207fffff-              2-    , getMaxBlockSize = 1000000-    , getMaxSatoshi = 2100000000000000-    , getHaskoinUserAgent = "/haskoin-bch-regtest:" <> versionString <> "/"-    , getDefaultPort = 18444-    , getAllowMinDifficultyBlocks = True-    , getPowNoRetargetting = True-    , getPowLimit =-          0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-    , getBip34Block =-          ( 100000000-          , "0000000000000000000000000000000000000000000000000000000000000000")-    , getBip65Height = 1351-    , getBip66Height = 1251-    , getTargetTimespan = 14 * 24 * 60 * 60-    , getTargetSpacing = 10 * 60-    , getCheckpoints =-          [ ( 0-            , "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")-          ]-    , getSeeds = ["localhost"]-    , getBip44Coin = 1-    , getSigHashForkId = Just 0-    , getEdaBlockHeight = Nothing-    , getDaaBlockHeight = Just 0-    , getSegWit = False-    , getCashAddrPrefix = Just "bchreg"-    , getBech32Prefix = Nothing-    , getReplaceByFee = False-    , getHalvingInterval = 150-    }---- | List of all networks supported by this library.-allNets :: [Network]-allNets = [btc, bch, btcTest, bchTest, btcRegTest, bchRegTest]
− src/Network/Haskoin/Crypto.hs
@@ -1,22 +0,0 @@-{-|-Module      : Network.Haskoin.Crypto-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Hashing functions and ECDSA signatures.--}-module Network.Haskoin.Crypto-    ( -- * Hashes-      module Hash-      -- * Signatures-    , module Signature-      -- * Secp256k1 (re-exported)-    , module Secp256k1-    ) where--import           Crypto.Secp256k1                 as Secp256k1-import           Network.Haskoin.Crypto.Hash      as Hash-import           Network.Haskoin.Crypto.Signature as Signature
− src/Network/Haskoin/Crypto/Hash.hs
@@ -1,202 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveAnyClass #-}-{-|-Module      : Network.Haskoin.Crypto.Hash-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Hashing functions and corresponding data types. Uses functions from the-cryptonite library.--}-module Network.Haskoin.Crypto.Hash-    ( -- * Hashes-      Hash512(getHash512)-    , Hash256(getHash256)-    , Hash160(getHash160)-    , CheckSum32(getCheckSum32)-    , sha512-    , sha256-    , ripemd160-    , sha1-    , doubleSHA256-    , addressHash-    , checkSum32-    , hmac512-    , hmac256-    , split512-    , join512-    ) where--import           Control.DeepSeq-import           Crypto.Hash             (RIPEMD160 (..), SHA1 (..),-                                          SHA256 (..), SHA512 (..), hashWith)-import           Crypto.MAC.HMAC         (HMAC, hmac)-import           Data.ByteArray          (ByteArrayAccess)-import qualified Data.ByteArray          as BA-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.ByteString.Short   (ShortByteString)-import qualified Data.ByteString.Short   as BSS-import           Data.Either             (fromRight)-import           Data.Hashable           (Hashable)-import           Data.Serialize          (Serialize (..), decode)-import qualified Data.Serialize.Get      as Get-import qualified Data.Serialize.Put      as Put-import           Data.String             (IsString, fromString)-import           Data.String.Conversions (cs)-import           Data.Word               (Word32)-import           GHC.Generics            (Generic)-import           Network.Haskoin.Util-import           Text.Read               as R---- | 'Word32' wrapped for type-safe 32-bit checksums.-newtype CheckSum32 = CheckSum32-    { getCheckSum32 :: Word32-    } deriving (Eq, Ord, Serialize, Show, Read, Hashable, Generic, NFData)---- | Type for 512-bit hashes.-newtype Hash512 = Hash512 { getHash512 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)---- | Type for 256-bit hashes.-newtype Hash256 = Hash256 { getHash256 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)---- | Type for 160-bit hashes.-newtype Hash160 = Hash160 { getHash160 :: ShortByteString }-    deriving (Eq, Ord, Hashable, Generic, NFData)--instance Show Hash512 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash512--instance Read Hash512 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash512 . BSS.toShort <$> decodeHex (cs str)--instance Show Hash256 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash256--instance Read Hash256 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash256 . BSS.toShort <$> decodeHex (cs str)--instance Show Hash160 where-    showsPrec _ = shows . encodeHex . BSS.fromShort . getHash160--instance Read Hash160 where-    readPrec = do-        R.String str <- lexP-        maybe pfail return $ Hash160 . BSS.toShort <$> decodeHex (cs str)--instance IsString Hash512 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    64 -> Hash512 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash512 where-    get = Hash512 <$> Get.getShortByteString 64-    put = Put.putShortByteString . getHash512--instance IsString Hash256 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    32 -> Hash256 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash256 where-    get = Hash256 <$> Get.getShortByteString 32-    put = Put.putShortByteString . getHash256--instance IsString Hash160 where-    fromString str =-        case decodeHex $ cs str of-            Nothing -> e-            Just bs ->-                case BS.length bs of-                    20 -> Hash160 (BSS.toShort bs)-                    _  -> e-      where-        e = error "Could not decode hash from hex string"--instance Serialize Hash160 where-    get = Hash160 <$> Get.getShortByteString 20-    put = Put.putShortByteString . getHash160---- | Calculate SHA512 hash.-sha512 :: ByteArrayAccess b => b -> Hash512-sha512 = Hash512 . BSS.toShort . BA.convert . hashWith SHA512---- | Calculate SHA256 hash.-sha256 :: ByteArrayAccess b => b -> Hash256-sha256 = Hash256 . BSS.toShort . BA.convert . hashWith SHA256---- | Calculate RIPEMD160 hash.-ripemd160 :: ByteArrayAccess b => b -> Hash160-ripemd160 = Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160---- | Claculate SHA1 hash.-sha1 :: ByteArrayAccess b => b -> Hash160-sha1 = Hash160 . BSS.toShort . BA.convert . hashWith SHA1---- | Compute two rounds of SHA-256.-doubleSHA256 :: ByteArrayAccess b => b -> Hash256-doubleSHA256 =-    Hash256 . BSS.toShort . BA.convert . hashWith SHA256 . hashWith SHA256---- | Compute SHA-256 followed by RIPMED-160.-addressHash :: ByteArrayAccess b => b -> Hash160-addressHash =-    Hash160 . BSS.toShort . BA.convert . hashWith RIPEMD160 . hashWith SHA256--{- CheckSum -}---- | Computes a 32 bit checksum.-checkSum32 :: ByteArrayAccess b => b -> CheckSum32-checkSum32 = fromRight (error "Could not decode bytes as CheckSum32")-             . decode-             . BS.take 4-             . BA.convert-             . hashWith SHA256-             . hashWith SHA256--{- HMAC -}---- | Computes HMAC over SHA-512.-hmac512 :: ByteString -> ByteString -> Hash512-hmac512 key msg =-    Hash512 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA512)---- | Computes HMAC over SHA-256.-hmac256 :: (ByteArrayAccess k, ByteArrayAccess m) => k -> m -> Hash256-hmac256 key msg =-    Hash256 $ BSS.toShort $ BA.convert (hmac key msg :: HMAC SHA256)---- | Split a 'Hash512' into a pair of 'Hash256'.-split512 :: Hash512 -> (Hash256, Hash256)-split512 h =-    (Hash256 (BSS.toShort a), Hash256 (BSS.toShort b))-  where-    (a, b) = BS.splitAt 32 . BSS.fromShort $ getHash512 h---- | Join a pair of 'Hash256' into a 'Hash512'.-join512 :: (Hash256, Hash256) -> Hash512-join512 (a, b) =-    Hash512 .-    BSS.toShort $-        BSS.fromShort (getHash256 a) `BS.append` BSS.fromShort (getHash256 b)
− src/Network/Haskoin/Crypto/Signature.hs
@@ -1,90 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Crypto.Signature-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA signatures using secp256k1 curve. Uses functions from upstream secp256k1-library.--}-module Network.Haskoin.Crypto.Signature-    ( putSig-    , getSig-    , signHash-    , verifyHashSig-    , isCanonicalHalfOrder-    , decodeStrictSig-    , exportSig-    ) where--import           Control.Monad               (guard, unless, when)-import           Crypto.Secp256k1-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import           Data.ByteString.Short       (toShort)-import           Data.Maybe                  (fromMaybe)-import           Data.Serialize              as S-import           Data.Serialize.Put          (Putter, putByteString)-import           Network.Haskoin.Crypto.Hash-import           Numeric                     (showHex)---- | Convert 256-bit hash into a 'Msg' for signing or verification.-hashToMsg :: Hash256 -> Msg-hashToMsg =-    fromMaybe e . msg . encode-  where-    e = error "Could not convert 32-byte hash to secp256k1 message"---- | Sign a 256-bit hash using secp256k1 elliptic curve.-signHash :: SecKey -> Hash256 -> Sig-signHash k = signMsg k . hashToMsg---- | Verify an ECDSA signature for a 256-bit hash.-verifyHashSig :: Hash256 -> Sig -> PubKey -> Bool-verifyHashSig h s p =-    verifySig p g m-  where-    (g, _) = normalizeSig s-    m = hashToMsg h---- | Deserialize an ECDSA signature as commonly encoded in Bitcoin.-getSig :: Get Sig-getSig = do-        l <- lookAhead $ do-            t <- getWord8-            -- 0x30 is DER sequence type-            unless (t == 0x30) $ fail $-                "Bad DER identifier byte 0x" ++ showHex t ". Expecting 0x30"-            l <- getWord8-            when (l == 0x00) $ fail "Indeterminate form unsupported"-            when (l >= 0x80) $ fail "Multi-octect length not supported"-            return $ fromIntegral l-        bs <- getByteString $ l + 2-        case decodeStrictSig bs of-            Just s  -> return s-            Nothing -> fail "Invalid signature"---- | Serialize an ECDSA signature for Bitcoin use.-putSig :: Putter Sig-putSig s = putByteString $ exportSig s---- | Is canonical half order.-isCanonicalHalfOrder :: Sig -> Bool-isCanonicalHalfOrder = not . snd . normalizeSig---- | Decode signature strictly.-decodeStrictSig :: ByteString -> Maybe Sig-decodeStrictSig bs = do-    g <- importSig bs-    let compact = exportCompactSig g-    -- <http://www.secg.org/sec1-v2.pdf Section 4.1.4>-    -- 4.1.4.1 (r and s can not be zero)-    guard $ getCompactSigR compact /= zero-    guard $ getCompactSigS compact /= zero-    guard $ isCanonicalHalfOrder g-    return g-  where-    zero = toShort $ BS.replicate 32 0
− src/Network/Haskoin/Keys.hs
@@ -1,22 +0,0 @@-{-|-Module      : Network.Haskoin.Keys-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA private and public keys, extended keys (BIP-32) and mnemonic sentences-(BIP-39).--}-module Network.Haskoin.Keys-    ( module Network.Haskoin.Keys.Common-    -- * Extended Keys-    , module Network.Haskoin.Keys.Extended-    -- * Mnemonic-    , module Network.Haskoin.Keys.Mnemonic-    ) where--import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Keys.Extended-import           Network.Haskoin.Keys.Mnemonic
− src/Network/Haskoin/Keys/Common.hs
@@ -1,138 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--ECDSA private and public key functions.--}-module Network.Haskoin.Keys.Common-    ( -- * Public & Private Keys-      PubKeyI(..)-    , SecKeyI(..)-    , exportPubKey-    , importPubKey-    , wrapPubKey-    , derivePubKeyI-    , wrapSecKey-    , fromMiniKey-    , tweakPubKey-    , tweakSecKey-    , secKeyPut-    , secKeyGet-    , getSecKey-    , secKey-    ) where--import           Control.Applicative         ((<|>))-import           Control.DeepSeq-import           Control.Monad               (guard, mzero, (<=<))-import           Crypto.Secp256k1-import           Data.Aeson                  (FromJSON, ToJSON (..),-                                              Value (String), parseJSON,-                                              withText)-import           Data.Aeson.Encoding         (unsafeToEncoding)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as BS-import           Data.ByteString.Builder     (char7)-import           Data.Hashable-import           Data.Maybe                  (fromMaybe)-import           Data.Serialize              (Serialize, decode, encode, get,-                                              put)-import           Data.Serialize.Get          (Get, getByteString)-import           Data.Serialize.Put          (Putter, putByteString)-import           Data.String                 (IsString, fromString)-import           Data.String.Conversions     (cs)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Util---- | Elliptic curve public key type with expected serialized compression flag.-data PubKeyI = PubKeyI-    { pubKeyPoint      :: !PubKey-    , pubKeyCompressed :: !Bool-    } deriving (Generic, Eq, Show, Read, Hashable, NFData)--instance IsString PubKeyI where-    fromString str =-        fromMaybe e $ eitherToMaybe . decode <=< decodeHex $ cs str-      where-        e = error "Could not decode public key"--instance ToJSON PubKeyI where-    toJSON = String . encodeHex . encode-    toEncoding s = unsafeToEncoding $ char7 '"' <> hexBuilder (encode s) <> char7 '"'--instance FromJSON PubKeyI where-    parseJSON = withText "PubKeyI" $-        maybe mzero return . (eitherToMaybe . decode =<<) . decodeHex--instance Serialize PubKeyI where-    get = c <|> u-      where-        c = do-            bs <- getByteString 33-            guard $ BS.head bs `BS.elem` BS.pack [0x02, 0x03]-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure True-        u = do-            bs <- getByteString 65-            guard $ BS.head bs == 0x04-            maybe mzero return $ PubKeyI <$> importPubKey bs <*> pure False--    put pk = putByteString $ exportPubKey (pubKeyCompressed pk) (pubKeyPoint pk)---- | Wrap a public key from secp256k1 library adding information about compression.-wrapPubKey :: Bool -> PubKey -> PubKeyI-wrapPubKey c p = PubKeyI p c---- | Derives a public key from a private key. This function will preserve--- compression flag.-derivePubKeyI :: SecKeyI -> PubKeyI-derivePubKeyI (SecKeyI d c) = PubKeyI (derivePubKey d) c---- | Tweak a public key.-tweakPubKey :: PubKey -> Hash256 -> Maybe PubKey-tweakPubKey p h = tweakAddPubKey p =<< tweak (encode h)---- | Elliptic curve private key type with expected public key compression--- information. Compression information is stored in private key WIF formats and--- needs to be preserved to generate the correct address from the corresponding--- public key.-data SecKeyI = SecKeyI-    { secKeyData       :: !SecKey-    , secKeyCompressed :: !Bool-    } deriving (Eq, Show, Read, Generic, NFData)---- | Wrap private key with corresponding public key compression flag.-wrapSecKey :: Bool -> SecKey -> SecKeyI-wrapSecKey c d = SecKeyI d c---- | Deserialize 'SecKey'.-secKeyGet :: Get SecKey-secKeyGet = do-    bs <- getByteString 32-    maybe (fail "invalid private key") return (secKey bs)---- | Serialize 'SecKey'.-secKeyPut :: Putter SecKey-secKeyPut = putByteString . getSecKey---- | Decode Casascius mini private keys (22 or 30 characters).-fromMiniKey :: ByteString -> Maybe SecKeyI-fromMiniKey bs = do-    guard checkShortKey-    wrapSecKey False <$> secKey (encode (sha256 bs))-  where-    checkHash = encode $ sha256 $ bs `BS.append` "?"-    checkShortKey = BS.length bs `elem` [22, 30] && BS.head checkHash == 0x00---- | Tweak a private key.-tweakSecKey :: SecKey -> Hash256 -> Maybe SecKey-tweakSecKey key h = tweakAddSecKey key =<< tweak (encode h)
− src/Network/Haskoin/Keys/Extended.hs
@@ -1,920 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs             #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Extended-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--BIP-32 extended keys.--}-module Network.Haskoin.Keys.Extended-    (-      -- * Extended Keys-      XPubKey(..)-    , XPrvKey(..)-    , ChainCode-    , KeyIndex-    , Fingerprint-    , DerivationException(..)-    , makeXPrvKey-    , deriveXPubKey-    , prvSubKey-    , pubSubKey-    , hardSubKey-    , xPrvIsHard-    , xPubIsHard-    , xPrvChild-    , xPubChild-    , xPubID-    , xPrvID-    , xPubFP-    , xPrvFP-    , xPubAddr-    , xPubWitnessAddr-    , xPubCompatWitnessAddr-    , xPubExport-    , xPubToJSON-    , xPubToEncoding-    , xPubFromJSON-    , xPrvExport-    , xPrvToJSON-    , xPrvToEncoding-    , xPrvFromJSON-    , xPubImport-    , xPrvImport-    , xPrvWif-    , putXPrvKey-    , putXPubKey-    , getXPrvKey-    , getXPubKey--      -- ** Helper Functions-    , prvSubKeys-    , pubSubKeys-    , hardSubKeys-    , deriveAddr-    , deriveWitnessAddr-    , deriveCompatWitnessAddr-    , deriveAddrs-    , deriveWitnessAddrs-    , deriveCompatWitnessAddrs-    , deriveMSAddr-    , deriveMSAddrs-    , cycleIndex--      -- ** Derivation Paths-    , DerivPathI(..)-    , AnyDeriv, HardDeriv, SoftDeriv-    , HardOrAny-    , AnyOrSoft-    , DerivPath-    , HardPath-    , SoftPath-    , Bip32PathIndex (..)-    , derivePath-    , derivePubPath-    , toHard-    , toSoft-    , toGeneric-    , (++/)-    , pathToStr-    , listToPath-    , pathToList--      -- *** Derivation Path Parser-    , XKey(..)-    , ParsedPath(..)-    , parsePath-    , parseHard-    , parseSoft-    , applyPath-    , derivePathAddr-    , derivePathAddrs-    , derivePathMSAddr-    , derivePathMSAddrs-    , concatBip32Segments-    ) where--import           Control.Applicative-import           Control.DeepSeq-import           Control.Exception              (Exception, throw)-import           Control.Monad                  (guard, mzero, unless, (<=<))-import           Crypto.Secp256k1-import           Data.Aeson                     as A (FromJSON, ToJSON (..),-                                                      Value (String), parseJSON,-                                                      toJSON, withText)-import           Data.Aeson.Encoding            (Encoding, text)-import           Data.Aeson.Types               (Parser)-import           Data.Bits                      (clearBit, setBit, testBit)-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.Either                    (fromRight)-import           Data.List                      (foldl')-import           Data.List.Split                (splitOn)-import           Data.Maybe                     (fromMaybe)-import           Data.Serialize                 as S (Serialize, decode, encode,-                                                      get, put)-import           Data.Serialize.Get             (Get, getWord32be, getWord8,-                                                 runGet)-import           Data.Serialize.Put             (Putter, putWord32be, putWord8,-                                                 runPut)-import           Data.String                    (IsString, fromString)-import           Data.String.Conversions        (cs)-import           Data.Typeable                  (Typeable)-import           Data.Word                      (Word32, Word8)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Util-import           Text.Read                      as R-import           Text.Read.Lex----- | A derivation exception is thrown in the very unlikely event that a--- derivation is invalid.-newtype DerivationException = DerivationException String-    deriving (Eq, Read, Show, Typeable, Generic, NFData)--instance Exception DerivationException---- | Chain code as specified in BIP-32.-type ChainCode = Hash256---- | Index of key as specified in BIP-32.-type KeyIndex = Word32---- | Fingerprint of parent-type Fingerprint = Word32---- | Data type representing an extended BIP32 private key. An extended key--- is a node in a tree of key derivations. It has a depth in the tree, a--- parent node and an index to differentiate it from other siblings.-data XPrvKey = XPrvKey-    { xPrvDepth  :: !Word8       -- ^ depth in the tree-    , xPrvParent :: !Fingerprint -- ^ fingerprint of parent-    , xPrvIndex  :: !KeyIndex    -- ^ derivation index-    , xPrvChain  :: !ChainCode   -- ^ chain code-    , xPrvKey    :: !SecKey      -- ^ private key of this node-    } deriving (Generic, Eq, Show, Read, NFData)--xPrvToJSON :: Network -> XPrvKey -> Value-xPrvToJSON net = A.String . xPrvExport net--xPrvToEncoding :: Network -> XPrvKey -> Encoding-xPrvToEncoding net = text . xPrvExport net---- | Data type representing an extended BIP32 public key.-data XPubKey = XPubKey-    { xPubDepth  :: !Word8     -- ^ depth in the tree-    , xPubParent :: !Fingerprint    -- ^ fingerprint of parent-    , xPubIndex  :: !KeyIndex  -- ^ derivation index-    , xPubChain  :: !ChainCode -- ^ chain code-    , xPubKey    :: !PubKey    -- ^ public key of this node-    } deriving (Generic, Eq, Show, Read, NFData)----- | Decode an extended public key from a JSON string-xPubFromJSON :: Network -> Value -> Parser XPubKey-xPubFromJSON net =-    withText "xpub" $ \t ->-        case xPubImport net t of-            Nothing -> fail "could not read xpub"-            Just x  -> return x---- | Get JSON 'Value' from 'XPubKey'.-xPubToJSON :: Network -> XPubKey -> Value-xPubToJSON net = A.String . xPubExport net--xPubToEncoding :: Network -> XPubKey -> Encoding-xPubToEncoding net = text . xPubExport net---- | Decode an extended private key from a JSON string-xPrvFromJSON :: Network -> Value -> Parser XPrvKey-xPrvFromJSON net =-    withText "xprv" $ \t ->-        case xPrvImport net t of-            Nothing -> fail "could not read xprv"-            Just x  -> return x---- | Build a BIP32 compatible extended private key from a bytestring. This will--- produce a root node (@depth=0@ and @parent=0@).-makeXPrvKey :: ByteString -> XPrvKey-makeXPrvKey bs =-    XPrvKey 0 0 0 c k-  where-    (p, c) = split512 $ hmac512 "Bitcoin seed" bs-    k     = fromMaybe err (secKey (encode p))-    err   = throw $ DerivationException "Invalid seed"---- | Derive an extended public key from an extended private key. This function--- will preserve the depth, parent, index and chaincode fields of the extended--- private keys.-deriveXPubKey :: XPrvKey -> XPubKey-deriveXPubKey (XPrvKey d p i c k) = XPubKey d p i c (derivePubKey k)---- | Compute a private, soft child key derivation. A private soft derivation--- will allow the equivalent extended public key to derive the public key for--- this child. Given a parent key /m/ and a derivation index /i/, this function--- will compute /m\/i/.------ Soft derivations allow for more flexibility such as read-only wallets.--- However, care must be taken not the leak both the parent extended public key--- and one of the extended child private keys as this would compromise the--- extended parent private key.-prvSubKey :: XPrvKey  -- ^ extended parent private key-          -> KeyIndex -- ^ child derivation index-          -> XPrvKey  -- ^ extended child private key-prvSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) child c k-    | otherwise = error "Invalid child derivation index"-  where-    pK = xPubKey $ deriveXPubKey xkey-    m = B.append (exportPubKey True pK) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err = throw $ DerivationException "Invalid prvSubKey derivation"---- | Compute a public, soft child key derivation. Given a parent key /M/--- and a derivation index /i/, this function will compute /M\/i/.-pubSubKey :: XPubKey  -- ^ extended parent public key-          -> KeyIndex -- ^ child derivation index-          -> XPubKey  -- ^ extended child public key-pubSubKey xKey child-    | child >= 0 && child < 0x80000000 =-        XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK-    | otherwise = error "Invalid child derivation index"-  where-    m      = B.append (exportPubKey True (xPubKey xKey)) (encode child)-    (a, c) = split512 $ hmac512 (encode $ xPubChain xKey) m-    pK     = fromMaybe err $ tweakPubKey (xPubKey xKey) a-    err    = throw $ DerivationException "Invalid pubSubKey derivation"---- | Compute a hard child key derivation. Hard derivations can only be computed--- for private keys. Hard derivations do not allow the parent public key to--- derive the child public keys. However, they are safer as a breach of the--- parent public key and child private keys does not lead to a breach of the--- parent private key. Given a parent key /m/ and a derivation index /i/, this--- function will compute /m\/i'/.-hardSubKey :: XPrvKey  -- ^ extended parent private key-           -> KeyIndex -- ^ child derivation index-           -> XPrvKey  -- ^ extended child private key-hardSubKey xkey child-    | child >= 0 && child < 0x80000000 =-        XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) i c k-    | otherwise = error "Invalid child derivation index"-  where-    i      = setBit child 31-    m      = B.append (bsPadPrvKey $ xPrvKey xkey) (encode i)-    (a, c) = split512 $ hmac512 (encode $ xPrvChain xkey) m-    k      = fromMaybe err $ tweakSecKey (xPrvKey xkey) a-    err    = throw $ DerivationException "Invalid hardSubKey derivation"---- | Returns true if the extended private key was derived through a hard--- derivation.-xPrvIsHard :: XPrvKey -> Bool-xPrvIsHard k = testBit (xPrvIndex k) 31---- | Returns true if the extended public key was derived through a hard--- derivation.-xPubIsHard :: XPubKey -> Bool-xPubIsHard k = testBit (xPubIndex k) 31---- | Returns the derivation index of this extended private key without the hard--- bit set.-xPrvChild :: XPrvKey -> KeyIndex-xPrvChild k = clearBit (xPrvIndex k) 31---- | Returns the derivation index of this extended public key without the hard--- bit set.-xPubChild :: XPubKey -> KeyIndex-xPubChild k = clearBit (xPubIndex k) 31---- | Computes the key identifier of an extended private key.-xPrvID :: XPrvKey -> Hash160-xPrvID = xPubID . deriveXPubKey---- | Computes the key identifier of an extended public key.-xPubID :: XPubKey -> Hash160-xPubID = ripemd160 . encode . sha256 . exportPubKey True . xPubKey---- | Computes the key fingerprint of an extended private key.-xPrvFP :: XPrvKey -> Fingerprint-xPrvFP =-    fromRight err . decode . B.take 4 . encode . xPrvID-  where-    err = error "Could not decode xPrvFP"---- | Computes the key fingerprint of an extended public key.-xPubFP :: XPubKey -> Fingerprint-xPubFP =-    fromRight err . decode . B.take 4 . encode . xPubID-  where-    err = error "Could not decode xPubFP"---- | Compute a standard P2PKH address for an extended public key.-xPubAddr :: XPubKey -> Address-xPubAddr xkey = pubKeyAddr (wrapPubKey True (xPubKey xkey))---- | Compute a SegWit P2WPKH address for an extended public key.-xPubWitnessAddr :: XPubKey -> Address-xPubWitnessAddr xkey = pubKeyWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Compute a backwards-compatible SegWit P2SH-P2WPKH address for an extended--- public key.-xPubCompatWitnessAddr :: XPubKey -> Address-xPubCompatWitnessAddr xkey =-    pubKeyCompatWitnessAddr (wrapPubKey True (xPubKey xkey))---- | Exports an extended private key to the BIP32 key export format ('Base58').-xPrvExport :: Network -> XPrvKey -> Base58-xPrvExport net = encodeBase58Check . runPut . putXPrvKey net---- | Exports an extended public key to the BIP32 key export format ('Base58').-xPubExport :: Network -> XPubKey -> Base58-xPubExport net = encodeBase58Check . runPut . putXPubKey net---- | Decodes a BIP32 encoded extended private key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPrvImport :: Network -> Base58 -> Maybe XPrvKey-xPrvImport net = eitherToMaybe . runGet (getXPrvKey net) <=< decodeBase58Check---- | Decodes a BIP32 encoded extended public key. This function will fail if--- invalid base 58 characters are detected or if the checksum fails.-xPubImport :: Network -> Base58 -> Maybe XPubKey-xPubImport net = eitherToMaybe . runGet (getXPubKey net) <=< decodeBase58Check---- | Export an extended private key to WIF (Wallet Import Format).-xPrvWif :: Network -> XPrvKey -> Base58-xPrvWif net xkey = toWif net (wrapSecKey True (xPrvKey xkey))---- | Parse a binary extended private key.-getXPrvKey :: Network -> Get XPrvKey-getXPrvKey net = do-        ver <- getWord32be-        unless (ver == getExtSecretPrefix net) $ fail-            "Get: Invalid version for extended private key"-        XPrvKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> getPadPrvKey---- | Serialize an extended private key.-putXPrvKey :: Network -> Putter XPrvKey-putXPrvKey net k = do-        putWord32be  $ getExtSecretPrefix net-        putWord8     $ xPrvDepth k-        putWord32be  $ xPrvParent k-        putWord32be  $ xPrvIndex k-        put          $ xPrvChain k-        putPadPrvKey $ xPrvKey k---- | Parse a binary extended public key.-getXPubKey :: Network -> Get XPubKey-getXPubKey net = do-        ver <- getWord32be-        unless (ver == getExtPubKeyPrefix net) $ fail-            "Get: Invalid version for extended public key"-        XPubKey <$> getWord8-                <*> getWord32be-                <*> getWord32be-                <*> S.get-                <*> (pubKeyPoint <$> S.get)---- | Serialize an extended public key.-putXPubKey :: Network -> Putter XPubKey-putXPubKey net k = do-        putWord32be $ getExtPubKeyPrefix net-        putWord8    $ xPubDepth k-        putWord32be $ xPubParent k-        putWord32be $ xPubIndex k-        put         $ xPubChain k-        put         $ wrapPubKey True (xPubKey k)--{- Derivation helpers -}---- | Cyclic list of all private soft child key derivations of a parent key--- starting from an offset index.-prvSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]-prvSubKeys k = map (\i -> (prvSubKey k i, i)) . cycleIndex---- | Cyclic list of all public soft child key derivations of a parent key--- starting from an offset index.-pubSubKeys :: XPubKey -> KeyIndex -> [(XPubKey, KeyIndex)]-pubSubKeys k = map (\i -> (pubSubKey k i, i)) . cycleIndex---- | Cyclic list of all hard child key derivations of a parent key starting--- from an offset index.-hardSubKeys :: XPrvKey -> KeyIndex -> [(XPrvKey, KeyIndex)]-hardSubKeys k = map (\i -> (hardSubKey k i, i)) . cycleIndex---- | Derive a standard address from an extended public key and an index.-deriveAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveAddr k i =-    (xPubAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Derive a SegWit P2WPKH address from an extended public key and an index.-deriveWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveWitnessAddr k i =-    (xPubWitnessAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Derive a backwards-compatible SegWit P2SH-P2WPKH address from an extended--- public key and an index.-deriveCompatWitnessAddr :: XPubKey -> KeyIndex -> (Address, PubKey)-deriveCompatWitnessAddr k i =-    (xPubCompatWitnessAddr key, xPubKey key)-  where-    key = pubSubKey k i---- | Cyclic list of all addresses derived from a public key starting from an--- offset index.-deriveAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveAddr k i in (a, key, i)---- | Cyclic list of all SegWit P2WPKH addresses derived from a public key--- starting from an offset index.-deriveWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveWitnessAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveWitnessAddr k i in (a, key, i)---- | Cyclic list of all backwards-compatible SegWit P2SH-P2WPKH addresses--- derived from a public key starting from an offset index.-deriveCompatWitnessAddrs :: XPubKey -> KeyIndex -> [(Address, PubKey, KeyIndex)]-deriveCompatWitnessAddrs k =-    map f . cycleIndex-  where-    f i = let (a, key) = deriveCompatWitnessAddr k i in (a, key, i)---- | Derive a multisig address from a list of public keys, the number of--- required signatures /m/ and a derivation index. The derivation type is a--- public, soft derivation.-deriveMSAddr :: [XPubKey] -> Int -> KeyIndex -> (Address, RedeemScript)-deriveMSAddr keys m i = (payToScriptAddress rdm, rdm)-  where-    rdm = sortMulSig $ PayMulSig k m-    k = map (wrapPubKey True . xPubKey . flip pubSubKey i) keys---- | Cyclic list of all multisig addresses derived from a list of public keys,--- a number of required signatures /m/ and starting from an offset index. The--- derivation type is a public, soft derivation.-deriveMSAddrs :: [XPubKey] -> Int -> KeyIndex-              -> [(Address, RedeemScript, KeyIndex)]-deriveMSAddrs keys m = map f . cycleIndex-  where-    f i =-        let (a, rdm) = deriveMSAddr keys m i-         in (a, rdm, i)---- | Helper function to go through derivation indices.-cycleIndex :: KeyIndex -> [KeyIndex]-cycleIndex i-    | i == 0         = cycle [0..0x7fffffff]-    | i < 0x80000000 = cycle $ [i..0x7fffffff] ++ [0..(i-1)]-    | otherwise      = error $ "cycleIndex: invalid index " ++ show i--{- Derivation Paths -}---- | Phantom type signaling a hardened derivation path that can only be computed--- from private extended key.-data HardDeriv deriving (Generic, NFData)---- | Phantom type signaling no knowledge about derivation path: can be hardened or not.-data AnyDeriv deriving (Generic, NFData)---- | Phantom type signaling derivation path including only non-hardened paths--- that can be computed from an extended public key.-data SoftDeriv deriving (Generic, NFData)---- | Hardened derivation path. Can be computed from extended private key only.-type HardPath = DerivPathI HardDeriv---- | Any derivation path.-type DerivPath = DerivPathI AnyDeriv---- | Non-hardened derivation path can be computed from extended public key.-type SoftPath = DerivPathI SoftDeriv---- | Helper class to perform validations on a hardened derivation path.-class HardOrAny a-instance HardOrAny HardDeriv-instance HardOrAny AnyDeriv---- | Helper class to perform validations on a non-hardened derivation path.-class AnyOrSoft a-instance AnyOrSoft AnyDeriv-instance AnyOrSoft SoftDeriv---- | Data type representing a derivation path. Two constructors are provided--- for specifying soft or hard derivations. The path /\/0\/1'\/2/ for example can be--- expressed as @'Deriv' :\/ 0 :| 1 :\/ 2@. The 'HardOrAny' and 'AnyOrSoft' type--- classes are used to constrain the valid values for the phantom type /t/. If--- you mix hard '(:|)' and soft '(:\/)' paths, the only valid type for /t/ is 'AnyDeriv'.--- Otherwise, /t/ can be 'HardDeriv' if you only have hard derivation or 'SoftDeriv'--- if you only have soft derivations.------ Using this type is as easy as writing the required derivation like in these--- example:------ > Deriv :/ 0 :/ 1 :/ 2 :: SoftPath--- > Deriv :| 0 :| 1 :| 2 :: HardPath--- > Deriv :| 0 :/ 1 :/ 2 :: DerivPath-data DerivPathI t where-    (:|)  :: HardOrAny t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t-    (:/)  :: AnyOrSoft t => !(DerivPathI t) -> !KeyIndex -> DerivPathI t-    Deriv :: DerivPathI t--instance NFData (DerivPathI t) where-    rnf (a :| b) = rnf a `seq` rnf b `seq` ()-    rnf (a :/ b) = rnf a `seq` rnf b `seq` ()-    rnf Deriv    = ()--instance Eq (DerivPathI t) where-    (nextA :| iA) == (nextB :| iB) = iA == iB && nextA == nextB-    (nextA :/ iA) == (nextB :/ iB) = iA == iB && nextA == nextB-    Deriv         == Deriv         = True-    _             == _             = False--instance Ord (DerivPathI t) where-    -- Same hardness on each side-    (nextA :| iA) `compare` (nextB :| iB) =-        if nextA == nextB then iA `compare` iB else nextA `compare` nextB-    (nextA :/ iA) `compare` (nextB :/ iB) =-        if nextA == nextB then iA `compare` iB else nextA `compare` nextB--    -- Different hardness: hard paths are LT soft paths-    (nextA :/ _iA) `compare` (nextB :| _iB) =-        if nextA == nextB then LT else nextA `compare` nextB-    (nextA :| _iA) `compare` (nextB :/ _iB) =-        if nextA == nextB then GT else nextA `compare` nextB--    Deriv `compare` Deriv  = EQ-    Deriv `compare` _      = LT-    _     `compare` Deriv  = GT---instance Serialize DerivPath where-    get = listToPath <$> S.get-    put = put . pathToList---- | Get a list of derivation indices from a derivation path.-pathToList :: DerivPathI t -> [KeyIndex]-pathToList =-    reverse . go-  where-    go (next :| i) = setBit i 31 : go next-    go (next :/ i) = i : go next-    go _           = []---- | Convert a list of derivation indices to a derivation path.-listToPath :: [KeyIndex] -> DerivPath-listToPath =-    go . reverse-  where-    go (i:is)-        | testBit i 31 = go is :| clearBit i 31-        | otherwise    = go is :/ i-    go [] = Deriv---- | Convert a derivation path to a human-readable string.-pathToStr :: DerivPathI t -> String-pathToStr p =-    case p of-        next :| i -> concat [ pathToStr next, "/", show i, "'" ]-        next :/ i -> concat [ pathToStr next, "/", show i ]-        Deriv     -> ""---- | Turn a derivation path into a hard derivation path. Will fail if the path--- contains soft derivations.-toHard :: DerivPathI t -> Maybe HardPath-toHard p = case p of-    next :| i -> (:| i) <$> toHard next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Turn a derivation path into a soft derivation path. Will fail if the path--- has hard derivations.-toSoft :: DerivPathI t -> Maybe SoftPath-toSoft p = case p of-    next :/ i -> (:/ i) <$> toSoft next-    Deriv     -> Just Deriv-    _         -> Nothing---- | Make a derivation path generic.-toGeneric :: DerivPathI t -> DerivPath-toGeneric p = case p of-    next :/ i -> toGeneric next :/ i-    next :| i -> toGeneric next :| i-    Deriv     -> Deriv---- | Append two derivation paths together. The result will be a mixed--- derivation path.-(++/) :: DerivPathI t1 -> DerivPathI t2 -> DerivPath-(++/) p1 p2 =-    go id (toGeneric p2) $ toGeneric p1-  where-    go f p = case p of-        next :/ i -> go (f . (:/ i)) $ toGeneric next-        next :| i -> go (f . (:| i)) $ toGeneric next-        _         -> f---- | Derive a private key from a derivation path-derivePath :: DerivPathI t -> XPrvKey -> XPrvKey-derivePath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :| i -> go (f . flip hardSubKey i) next-        next :/ i -> go (f . flip prvSubKey i) next-        _         -> f---- | Derive a public key from a soft derivation path-derivePubPath :: SoftPath -> XPubKey -> XPubKey-derivePubPath = go id-  where-    -- Build the full derivation function starting from the end-    go f p = case p of-        next :/ i -> go (f . flip pubSubKey i) next-        _         -> f--instance Show DerivPath where-    showsPrec d p = showParen (d > 10) $-        showString "DerivPath " . shows (pathToStr p)--instance Read DerivPath where-    readPrec = parens $ do-        R.Ident "DerivPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ getParsedPath <$> parsePath str--instance Show HardPath where-    showsPrec d p = showParen (d > 10) $-        showString "HardPath " . shows (pathToStr p)--instance Read HardPath where-    readPrec = parens $ do-        R.Ident "HardPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseHard str--instance Show SoftPath where-    showsPrec d p = showParen (d > 10) $-        showString "SoftPath " . shows (pathToStr p)--instance Read SoftPath where-    readPrec = parens $ do-        R.Ident "SoftPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parseSoft str--instance IsString ParsedPath where-    fromString =-        fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString DerivPath where-    fromString =-        getParsedPath . fromMaybe e . parsePath-      where-        e = error "Could not parse derivation path"--instance IsString HardPath where-    fromString =-        fromMaybe e . parseHard-      where-        e = error "Could not parse hard derivation path"--instance IsString SoftPath where-    fromString =-        fromMaybe e . parseSoft-      where-        e = error "Could not parse soft derivation path"--instance FromJSON ParsedPath where-    parseJSON = withText "ParsedPath" $ \str -> case parsePath $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON DerivPath where-    parseJSON = withText "DerivPath" $ \str -> case parsePath $ cs str of-        Just p -> return $ getParsedPath p-        _      -> mzero--instance FromJSON HardPath where-    parseJSON = withText "HardPath" $ \str -> case parseHard $ cs str of-        Just p -> return p-        _      -> mzero--instance FromJSON SoftPath where-    parseJSON = withText "SoftPath" $ \str -> case parseSoft $ cs str of-        Just p -> return p-        _      -> mzero--instance ToJSON (DerivPathI t) where-    toJSON = A.String . cs . pathToStr--instance ToJSON ParsedPath where-    toJSON (ParsedPrv p)   = A.String . cs . ("m" ++) . pathToStr $ p-    toJSON (ParsedPub p)   = A.String . cs . ("M" ++) . pathToStr $ p-    toJSON (ParsedEmpty p) = A.String . cs . ("" ++) . pathToStr $ p--{- Parsing derivation paths of the form m/1/2'/3 or M/1/2'/3 -}---- | Type for parsing derivation paths of the form /m\/1\/2'\/3/ or--- /M\/1\/2'\/3/.-data ParsedPath = ParsedPrv   { getParsedPath :: !DerivPath }-                | ParsedPub   { getParsedPath :: !DerivPath }-                | ParsedEmpty { getParsedPath :: !DerivPath }-  deriving (Eq, Generic, NFData)--instance Show ParsedPath where-    showsPrec d p = showParen (d > 10) $ showString "ParsedPath " . shows f-      where-        f =-            case p of-                ParsedPrv d'   -> "m" <> pathToStr d'-                ParsedPub d'   -> "M" <> pathToStr d'-                ParsedEmpty d' -> pathToStr d'--instance Read ParsedPath where-    readPrec = parens $ do-        R.Ident "ParsedPath" <- lexP-        R.String str <- lexP-        maybe pfail return $ parsePath str---- | Parse derivation path string for extended key.--- Forms: /m\/0'\/2/, /M\/2\/3\/4/.-parsePath :: String -> Maybe ParsedPath-parsePath str = do-    res <- concatBip32Segments <$> mapM parseBip32PathIndex xs-    case x of-        "m" -> Just $ ParsedPrv res-        "M" -> Just $ ParsedPub res-        ""  -> Just $ ParsedEmpty res-        _   -> Nothing-  where-    (x : xs) = splitOn "/" str---- | Concatenate derivation path indices into a derivation path.-concatBip32Segments :: [Bip32PathIndex] -> DerivPath-concatBip32Segments = foldl' appendBip32Segment Deriv---- | Append an extra derivation path index element into an existing path.-appendBip32Segment :: DerivPath -> Bip32PathIndex  -> DerivPath-appendBip32Segment d (Bip32SoftIndex i) = d :/ i-appendBip32Segment d (Bip32HardIndex i) = d :| i---- | Parse a BIP32 derivation path index element from a string.-parseBip32PathIndex :: String -> Maybe Bip32PathIndex-parseBip32PathIndex segment = case reads segment of-    [(i, "" )] -> guard (is31Bit i) >> return (Bip32SoftIndex i)-    [(i, "'")] -> guard (is31Bit i) >> return (Bip32HardIndex i)-    _          -> Nothing---- | Type for BIP32 path index element.-data Bip32PathIndex = Bip32HardIndex KeyIndex-                    | Bip32SoftIndex KeyIndex-  deriving (Eq, Generic, NFData)--instance Show Bip32PathIndex where-    showsPrec d (Bip32HardIndex i) = showParen (d > 10) $-        showString "Bip32HardIndex " . shows i-    showsPrec d (Bip32SoftIndex i) = showParen (d > 10) $-        showString "Bip32SoftIndex " . shows i--instance Read Bip32PathIndex where-    readPrec = h <|> s-      where-        h =-            parens $ do-                R.Ident "Bip32HardIndex" <- lexP-                R.Number n <- lexP-                maybe pfail return $-                    Bip32HardIndex . fromIntegral <$> numberToInteger n-        s =-            parens $ do-                R.Ident "Bip32SoftIndex" <- lexP-                R.Number n <- lexP-                maybe pfail return $-                    Bip32SoftIndex . fromIntegral <$> numberToInteger n---- | Test whether the number could be a valid BIP32 derivation index.-is31Bit :: (Integral a) => a -> Bool-is31Bit i = i >= 0 && i < 0x80000000---- | Helper function to parse a hard path.-parseHard :: String -> Maybe HardPath-parseHard = toHard . getParsedPath <=< parsePath---- | Helper function to parse a soft path.-parseSoft :: String -> Maybe SoftPath-parseSoft = toSoft . getParsedPath <=< parsePath---- | Data type representing a private or public key with its respective network.-data XKey-    = XPrv { getXKeyPrv :: !XPrvKey-           , getXKeyNet :: !Network }-    | XPub { getXKeyPub :: !XPubKey-           , getXKeyNet :: !Network }-    deriving (Eq, Show, Generic, NFData)---- | Apply a parsed path to an extended key to derive the new key defined in the--- path. If the path starts with /m/, a private key will be returned and if the--- path starts with /M/, a public key will be returned. Private derivations on a--- public key, and public derivations with a hard segment, return an error--- value.-applyPath :: ParsedPath -> XKey -> Either String XKey-applyPath path key =-    case (path, key) of-        (ParsedPrv _, XPrv k n) -> return $ XPrv (derivPrvF k) n-        (ParsedPrv _, XPub {}) -> Left "applyPath: Invalid public key"-        (ParsedPub _, XPrv k n) -> return $ XPub (deriveXPubKey (derivPrvF k)) n-        (ParsedPub _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n-    -- For empty parsed paths, we take a hint from the provided key-        (ParsedEmpty _, XPrv k n) -> return $ XPrv (derivPrvF k) n-        (ParsedEmpty _, XPub k n) -> derivPubFE >>= \f -> return $ XPub (f k) n-  where-    derivPrvF = goPrv id $ getParsedPath path-    derivPubFE = goPubE id $ getParsedPath path-    -- Build the full private derivation function starting from the end-    goPrv f p =-        case p of-            next :| i -> goPrv (f . flip hardSubKey i) next-            next :/ i -> goPrv (f . flip prvSubKey i) next-            Deriv     -> f-    -- Build the full public derivation function starting from the end-    goPubE f p =-        case p of-            next :/ i -> goPubE (f . flip pubSubKey i) next-            Deriv     -> Right f-            _         -> Left "applyPath: Invalid hard derivation"--{- Helpers for derivation paths and addresses -}---- | Derive an address from a given parent path.-derivePathAddr :: XPubKey -> SoftPath -> KeyIndex -> (Address, PubKey)-derivePathAddr key path = deriveAddr (derivePubPath path key)---- | Cyclic list of all addresses derived from a given parent path and starting--- from the given offset index.-derivePathAddrs ::-       XPubKey -> SoftPath -> KeyIndex -> [(Address, PubKey, KeyIndex)]-derivePathAddrs key path = deriveAddrs (derivePubPath path key)---- | Derive a multisig address from a given parent path. The number of required--- signatures (m in m of n) is also needed.-derivePathMSAddr ::-       [XPubKey]-    -> SoftPath-    -> Int-    -> KeyIndex-    -> (Address, RedeemScript)-derivePathMSAddr keys path =-    deriveMSAddr $ map (derivePubPath path) keys---- | Cyclic list of all multisig addresses derived from a given parent path and--- starting from the given offset index. The number of required signatures--- (m in m of n) is also needed.-derivePathMSAddrs ::-       [XPubKey]-    -> SoftPath-    -> Int-    -> KeyIndex-    -> [(Address, RedeemScript, KeyIndex)]-derivePathMSAddrs keys path =-    deriveMSAddrs $ map (derivePubPath path) keys--{- Utilities for extended keys -}---- | De-serialize HDW-specific private key.-getPadPrvKey :: Get SecKey-getPadPrvKey = do-    pad <- getWord8-    unless (pad == 0x00) $ fail "Private key must be padded with 0x00"-    secKeyGet---- | Serialize HDW-specific private key.-putPadPrvKey :: Putter SecKey-putPadPrvKey p = putWord8 0x00 >> secKeyPut p--bsPadPrvKey :: SecKey -> ByteString-bsPadPrvKey = runPut . putPadPrvKey
− src/Network/Haskoin/Keys/Mnemonic.hs
@@ -1,516 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Keys.Mnemonic-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Mnemonic keys (BIP-39). Only English dictionary.--}-module Network.Haskoin.Keys.Mnemonic-    ( -- * Mnemonic Sentences-      Entropy-    , Mnemonic-    , Passphrase-    , Seed-    , toMnemonic-    , fromMnemonic-    , mnemonicToSeed-    ) where--import           Control.Monad           (when)-import           Crypto.Hash             (SHA256 (..), hashWith)-import           Crypto.KDF.PBKDF2       (Parameters (..), fastPBKDF2_SHA512)-import           Data.Bits               (shiftL, shiftR)-import qualified Data.ByteArray          as BA-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import           Data.List-import qualified Data.Map.Strict         as M-import           Data.Maybe-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import qualified Data.Text               as T-import qualified Data.Text.Encoding      as E-import           Data.Vector             (Vector, (!))-import qualified Data.Vector             as V-import           Network.Haskoin.Util---- | Random data used to create a mnemonic sentence. Use a good entropy source.--- You will get your coins stolen if you don't. You have been warned.-type Entropy = ByteString---- | Human-readable mnemonic sentence.-type Mnemonic = Text---- | Optional passphrase for mnemnoic sentence.-type Passphrase = ByteString---- | Seed for a private key from a mnemonic sentence.-type Seed = ByteString---- | Mnemonic key checksum.-type Checksum = ByteString---- | Paremeters for PBKDF2 function.-pbkdfParams :: Parameters-pbkdfParams = Parameters {iterCounts = 2048, outputLength = 64}---- | Provide intial 'Entropy' as a 'ByteString' of length multiple of 4 bytes.--- Output a 'Mnemonic' sentence.-toMnemonic :: Entropy -> Either String Mnemonic-toMnemonic ent = do-    when (BS.null ent) $-        Left "toMnemonic: entropy can not be empty"-    when (remainder /= 0) $-        Left "toMnemonic: entropy must be multiples of 4 bytes"-    when (cs_len > 16) $-        Left "toMnemonic: maximum entropy is 64 bytes (512 bits)"-    return ms-  where-    (cs_len, remainder) = BS.length ent `quotRem` 4-    c = calcCS cs_len ent-    indices = bsToIndices $ ent `BS.append` c-    ms = T.unwords $ map (wl!) indices---- | Revert 'toMnemonic'. Do not use this to generate a 'Seed'. Instead use--- 'mnemonicToSeed'. This outputs the original 'Entropy' used to generate a--- 'Mnemonic' sentence.-fromMnemonic :: Mnemonic -> Either String Entropy-fromMnemonic ms = do-    when (T.null ms) $-        Left "fromMnemonic: empty mnemonic"-    when (word_count > 48) $-        Left $ "fromMnemonic: too many words: " ++ show word_count-    when (word_count `mod` 3 /= 0) $-        Left $ "fromMnemonic: wrong number of words:" ++ show word_count-    ms_bs <- indicesToBS =<< getIndices ms_words-    let (ms_ent, ms_cs) = BS.splitAt (ent_len * 4) ms_bs-        ms_cs_num = numCS cs_len ms_cs-        ent_cs_num = numCS cs_len $ calcCS cs_len ms_ent-    when (ent_cs_num /= ms_cs_num) $-        Left $ "fromMnemonic: checksum failed: " ++ sh ent_cs_num ms_cs_num-    return ms_ent-  where-    ms_words = T.words ms-    word_count = length ms_words-    (ent_len, cs_len) = (word_count * 11) `quotRem` 32-    sh cs_a cs_b = show cs_a ++ " /= " ++ show cs_b---- | Compute 'Checksum'.-calcCS :: Int -> Entropy -> Checksum-calcCS len = getBits len . BA.convert . hashWith SHA256--numCS :: Int -> Entropy -> Integer-numCS len =-    shiftCS . bsToInteger-  where-    shiftCS = case 8 - len `mod` 8 of-        8 -> id-        x -> flip shiftR x---- | Turn an arbitrary sequence of characters into a 512-bit 'Seed'. Use--- 'mnemonicToSeed' to get a seed from a 'Mnemonic' sentence. Warning: Does not--- perform NFKD normalization.-anyToSeed :: Passphrase -> Mnemonic -> Seed-anyToSeed pf ms =-    fastPBKDF2_SHA512 pbkdfParams (E.encodeUtf8 ms) ("mnemonic" `mappend` pf)---- | Get a 512-bit 'Seed' from a 'Mnemonic' sentence. Will validate checksum.--- 'Passphrase' can be used to protect the 'Mnemonic'. Use an empty string as--- 'Passphrase' if none is required.-mnemonicToSeed :: Passphrase -> Mnemonic -> Either String Seed-mnemonicToSeed pf ms = do-    ent <- fromMnemonic ms-    mnm <- toMnemonic ent-    return $ anyToSeed pf mnm---- | Get indices of words in word list.-getIndices :: [Text] -> Either String [Int]-getIndices ws-    | null n = return $ catMaybes i-    | otherwise = Left $ "getIndices: words not found: " ++ cs w-  where-    i = map (`M.lookup` wl') ws-    n = elemIndices Nothing i-    w = T.unwords $ map (ws !!) n---- | Turn a list of 11-bit numbers into a 'ByteString'-indicesToBS :: [Int] -> Either String ByteString-indicesToBS is = do-    when lrg $ Left "indicesToBS: index larger or equal than 2048"-    return . pad . integerToBS $ foldl' f 0 is `shiftL` shift_width-  where-    lrg = isJust $ find (>= 2048) is-    (q, r) = (length is * 11) `quotRem` 8-    shift_width =-        if r == 0-            then 0-            else 8 - r-    bl =-        if r == 0-            then q-            else q + 1 -- length of resulting ByteString-    pad bs = BS.append (BS.replicate (bl - BS.length bs) 0x00) bs-    f acc x = (acc `shiftL` 11) + fromIntegral x---- | Turn a 'ByteString' into a list of 11-bit numbers.-bsToIndices :: ByteString -> [Int]-bsToIndices bs =-    reverse . go q $ bsToInteger bs `shiftR` r-  where-    (q, r) = (BS.length bs * 8) `quotRem` 11-    go 0 _ = []-    go n i = fromIntegral (i `mod` 2048) : go (n - 1) (i `shiftR` 11)--wl' :: M.Map Text Int-wl' = V.ifoldr' (\i w m -> M.insert w i m) M.empty wl---- | Standard English dictionary from BIP-39 specification.-wl :: Vector Text-wl = V.fromListN 2048-    [ "abandon", "ability", "able", "about", "above", "absent"-    , "absorb", "abstract", "absurd", "abuse", "access", "accident"-    , "account", "accuse", "achieve", "acid", "acoustic", "acquire"-    , "across", "act", "action", "actor", "actress", "actual"-    , "adapt", "add", "addict", "address", "adjust", "admit"-    , "adult", "advance", "advice", "aerobic", "affair", "afford"-    , "afraid", "again", "age", "agent", "agree", "ahead"-    , "aim", "air", "airport", "aisle", "alarm", "album"-    , "alcohol", "alert", "alien", "all", "alley", "allow"-    , "almost", "alone", "alpha", "already", "also", "alter"-    , "always", "amateur", "amazing", "among", "amount", "amused"-    , "analyst", "anchor", "ancient", "anger", "angle", "angry"-    , "animal", "ankle", "announce", "annual", "another", "answer"-    , "antenna", "antique", "anxiety", "any", "apart", "apology"-    , "appear", "apple", "approve", "april", "arch", "arctic"-    , "area", "arena", "argue", "arm", "armed", "armor"-    , "army", "around", "arrange", "arrest", "arrive", "arrow"-    , "art", "artefact", "artist", "artwork", "ask", "aspect"-    , "assault", "asset", "assist", "assume", "asthma", "athlete"-    , "atom", "attack", "attend", "attitude", "attract", "auction"-    , "audit", "august", "aunt", "author", "auto", "autumn"-    , "average", "avocado", "avoid", "awake", "aware", "away"-    , "awesome", "awful", "awkward", "axis", "baby", "bachelor"-    , "bacon", "badge", "bag", "balance", "balcony", "ball"-    , "bamboo", "banana", "banner", "bar", "barely", "bargain"-    , "barrel", "base", "basic", "basket", "battle", "beach"-    , "bean", "beauty", "because", "become", "beef", "before"-    , "begin", "behave", "behind", "believe", "below", "belt"-    , "bench", "benefit", "best", "betray", "better", "between"-    , "beyond", "bicycle", "bid", "bike", "bind", "biology"-    , "bird", "birth", "bitter", "black", "blade", "blame"-    , "blanket", "blast", "bleak", "bless", "blind", "blood"-    , "blossom", "blouse", "blue", "blur", "blush", "board"-    , "boat", "body", "boil", "bomb", "bone", "bonus"-    , "book", "boost", "border", "boring", "borrow", "boss"-    , "bottom", "bounce", "box", "boy", "bracket", "brain"-    , "brand", "brass", "brave", "bread", "breeze", "brick"-    , "bridge", "brief", "bright", "bring", "brisk", "broccoli"-    , "broken", "bronze", "broom", "brother", "brown", "brush"-    , "bubble", "buddy", "budget", "buffalo", "build", "bulb"-    , "bulk", "bullet", "bundle", "bunker", "burden", "burger"-    , "burst", "bus", "business", "busy", "butter", "buyer"-    , "buzz", "cabbage", "cabin", "cable", "cactus", "cage"-    , "cake", "call", "calm", "camera", "camp", "can"-    , "canal", "cancel", "candy", "cannon", "canoe", "canvas"-    , "canyon", "capable", "capital", "captain", "car", "carbon"-    , "card", "cargo", "carpet", "carry", "cart", "case"-    , "cash", "casino", "castle", "casual", "cat", "catalog"-    , "catch", "category", "cattle", "caught", "cause", "caution"-    , "cave", "ceiling", "celery", "cement", "census", "century"-    , "cereal", "certain", "chair", "chalk", "champion", "change"-    , "chaos", "chapter", "charge", "chase", "chat", "cheap"-    , "check", "cheese", "chef", "cherry", "chest", "chicken"-    , "chief", "child", "chimney", "choice", "choose", "chronic"-    , "chuckle", "chunk", "churn", "cigar", "cinnamon", "circle"-    , "citizen", "city", "civil", "claim", "clap", "clarify"-    , "claw", "clay", "clean", "clerk", "clever", "click"-    , "client", "cliff", "climb", "clinic", "clip", "clock"-    , "clog", "close", "cloth", "cloud", "clown", "club"-    , "clump", "cluster", "clutch", "coach", "coast", "coconut"-    , "code", "coffee", "coil", "coin", "collect", "color"-    , "column", "combine", "come", "comfort", "comic", "common"-    , "company", "concert", "conduct", "confirm", "congress", "connect"-    , "consider", "control", "convince", "cook", "cool", "copper"-    , "copy", "coral", "core", "corn", "correct", "cost"-    , "cotton", "couch", "country", "couple", "course", "cousin"-    , "cover", "coyote", "crack", "cradle", "craft", "cram"-    , "crane", "crash", "crater", "crawl", "crazy", "cream"-    , "credit", "creek", "crew", "cricket", "crime", "crisp"-    , "critic", "crop", "cross", "crouch", "crowd", "crucial"-    , "cruel", "cruise", "crumble", "crunch", "crush", "cry"-    , "crystal", "cube", "culture", "cup", "cupboard", "curious"-    , "current", "curtain", "curve", "cushion", "custom", "cute"-    , "cycle", "dad", "damage", "damp", "dance", "danger"-    , "daring", "dash", "daughter", "dawn", "day", "deal"-    , "debate", "debris", "decade", "december", "decide", "decline"-    , "decorate", "decrease", "deer", "defense", "define", "defy"-    , "degree", "delay", "deliver", "demand", "demise", "denial"-    , "dentist", "deny", "depart", "depend", "deposit", "depth"-    , "deputy", "derive", "describe", "desert", "design", "desk"-    , "despair", "destroy", "detail", "detect", "develop", "device"-    , "devote", "diagram", "dial", "diamond", "diary", "dice"-    , "diesel", "diet", "differ", "digital", "dignity", "dilemma"-    , "dinner", "dinosaur", "direct", "dirt", "disagree", "discover"-    , "disease", "dish", "dismiss", "disorder", "display", "distance"-    , "divert", "divide", "divorce", "dizzy", "doctor", "document"-    , "dog", "doll", "dolphin", "domain", "donate", "donkey"-    , "donor", "door", "dose", "double", "dove", "draft"-    , "dragon", "drama", "drastic", "draw", "dream", "dress"-    , "drift", "drill", "drink", "drip", "drive", "drop"-    , "drum", "dry", "duck", "dumb", "dune", "during"-    , "dust", "dutch", "duty", "dwarf", "dynamic", "eager"-    , "eagle", "early", "earn", "earth", "easily", "east"-    , "easy", "echo", "ecology", "economy", "edge", "edit"-    , "educate", "effort", "egg", "eight", "either", "elbow"-    , "elder", "electric", "elegant", "element", "elephant", "elevator"-    , "elite", "else", "embark", "embody", "embrace", "emerge"-    , "emotion", "employ", "empower", "empty", "enable", "enact"-    , "end", "endless", "endorse", "enemy", "energy", "enforce"-    , "engage", "engine", "enhance", "enjoy", "enlist", "enough"-    , "enrich", "enroll", "ensure", "enter", "entire", "entry"-    , "envelope", "episode", "equal", "equip", "era", "erase"-    , "erode", "erosion", "error", "erupt", "escape", "essay"-    , "essence", "estate", "eternal", "ethics", "evidence", "evil"-    , "evoke", "evolve", "exact", "example", "excess", "exchange"-    , "excite", "exclude", "excuse", "execute", "exercise", "exhaust"-    , "exhibit", "exile", "exist", "exit", "exotic", "expand"-    , "expect", "expire", "explain", "expose", "express", "extend"-    , "extra", "eye", "eyebrow", "fabric", "face", "faculty"-    , "fade", "faint", "faith", "fall", "false", "fame"-    , "family", "famous", "fan", "fancy", "fantasy", "farm"-    , "fashion", "fat", "fatal", "father", "fatigue", "fault"-    , "favorite", "feature", "february", "federal", "fee", "feed"-    , "feel", "female", "fence", "festival", "fetch", "fever"-    , "few", "fiber", "fiction", "field", "figure", "file"-    , "film", "filter", "final", "find", "fine", "finger"-    , "finish", "fire", "firm", "first", "fiscal", "fish"-    , "fit", "fitness", "fix", "flag", "flame", "flash"-    , "flat", "flavor", "flee", "flight", "flip", "float"-    , "flock", "floor", "flower", "fluid", "flush", "fly"-    , "foam", "focus", "fog", "foil", "fold", "follow"-    , "food", "foot", "force", "forest", "forget", "fork"-    , "fortune", "forum", "forward", "fossil", "foster", "found"-    , "fox", "fragile", "frame", "frequent", "fresh", "friend"-    , "fringe", "frog", "front", "frost", "frown", "frozen"-    , "fruit", "fuel", "fun", "funny", "furnace", "fury"-    , "future", "gadget", "gain", "galaxy", "gallery", "game"-    , "gap", "garage", "garbage", "garden", "garlic", "garment"-    , "gas", "gasp", "gate", "gather", "gauge", "gaze"-    , "general", "genius", "genre", "gentle", "genuine", "gesture"-    , "ghost", "giant", "gift", "giggle", "ginger", "giraffe"-    , "girl", "give", "glad", "glance", "glare", "glass"-    , "glide", "glimpse", "globe", "gloom", "glory", "glove"-    , "glow", "glue", "goat", "goddess", "gold", "good"-    , "goose", "gorilla", "gospel", "gossip", "govern", "gown"-    , "grab", "grace", "grain", "grant", "grape", "grass"-    , "gravity", "great", "green", "grid", "grief", "grit"-    , "grocery", "group", "grow", "grunt", "guard", "guess"-    , "guide", "guilt", "guitar", "gun", "gym", "habit"-    , "hair", "half", "hammer", "hamster", "hand", "happy"-    , "harbor", "hard", "harsh", "harvest", "hat", "have"-    , "hawk", "hazard", "head", "health", "heart", "heavy"-    , "hedgehog", "height", "hello", "helmet", "help", "hen"-    , "hero", "hidden", "high", "hill", "hint", "hip"-    , "hire", "history", "hobby", "hockey", "hold", "hole"-    , "holiday", "hollow", "home", "honey", "hood", "hope"-    , "horn", "horror", "horse", "hospital", "host", "hotel"-    , "hour", "hover", "hub", "huge", "human", "humble"-    , "humor", "hundred", "hungry", "hunt", "hurdle", "hurry"-    , "hurt", "husband", "hybrid", "ice", "icon", "idea"-    , "identify", "idle", "ignore", "ill", "illegal", "illness"-    , "image", "imitate", "immense", "immune", "impact", "impose"-    , "improve", "impulse", "inch", "include", "income", "increase"-    , "index", "indicate", "indoor", "industry", "infant", "inflict"-    , "inform", "inhale", "inherit", "initial", "inject", "injury"-    , "inmate", "inner", "innocent", "input", "inquiry", "insane"-    , "insect", "inside", "inspire", "install", "intact", "interest"-    , "into", "invest", "invite", "involve", "iron", "island"-    , "isolate", "issue", "item", "ivory", "jacket", "jaguar"-    , "jar", "jazz", "jealous", "jeans", "jelly", "jewel"-    , "job", "join", "joke", "journey", "joy", "judge"-    , "juice", "jump", "jungle", "junior", "junk", "just"-    , "kangaroo", "keen", "keep", "ketchup", "key", "kick"-    , "kid", "kidney", "kind", "kingdom", "kiss", "kit"-    , "kitchen", "kite", "kitten", "kiwi", "knee", "knife"-    , "knock", "know", "lab", "label", "labor", "ladder"-    , "lady", "lake", "lamp", "language", "laptop", "large"-    , "later", "latin", "laugh", "laundry", "lava", "law"-    , "lawn", "lawsuit", "layer", "lazy", "leader", "leaf"-    , "learn", "leave", "lecture", "left", "leg", "legal"-    , "legend", "leisure", "lemon", "lend", "length", "lens"-    , "leopard", "lesson", "letter", "level", "liar", "liberty"-    , "library", "license", "life", "lift", "light", "like"-    , "limb", "limit", "link", "lion", "liquid", "list"-    , "little", "live", "lizard", "load", "loan", "lobster"-    , "local", "lock", "logic", "lonely", "long", "loop"-    , "lottery", "loud", "lounge", "love", "loyal", "lucky"-    , "luggage", "lumber", "lunar", "lunch", "luxury", "lyrics"-    , "machine", "mad", "magic", "magnet", "maid", "mail"-    , "main", "major", "make", "mammal", "man", "manage"-    , "mandate", "mango", "mansion", "manual", "maple", "marble"-    , "march", "margin", "marine", "market", "marriage", "mask"-    , "mass", "master", "match", "material", "math", "matrix"-    , "matter", "maximum", "maze", "meadow", "mean", "measure"-    , "meat", "mechanic", "medal", "media", "melody", "melt"-    , "member", "memory", "mention", "menu", "mercy", "merge"-    , "merit", "merry", "mesh", "message", "metal", "method"-    , "middle", "midnight", "milk", "million", "mimic", "mind"-    , "minimum", "minor", "minute", "miracle", "mirror", "misery"-    , "miss", "mistake", "mix", "mixed", "mixture", "mobile"-    , "model", "modify", "mom", "moment", "monitor", "monkey"-    , "monster", "month", "moon", "moral", "more", "morning"-    , "mosquito", "mother", "motion", "motor", "mountain", "mouse"-    , "move", "movie", "much", "muffin", "mule", "multiply"-    , "muscle", "museum", "mushroom", "music", "must", "mutual"-    , "myself", "mystery", "myth", "naive", "name", "napkin"-    , "narrow", "nasty", "nation", "nature", "near", "neck"-    , "need", "negative", "neglect", "neither", "nephew", "nerve"-    , "nest", "net", "network", "neutral", "never", "news"-    , "next", "nice", "night", "noble", "noise", "nominee"-    , "noodle", "normal", "north", "nose", "notable", "note"-    , "nothing", "notice", "novel", "now", "nuclear", "number"-    , "nurse", "nut", "oak", "obey", "object", "oblige"-    , "obscure", "observe", "obtain", "obvious", "occur", "ocean"-    , "october", "odor", "off", "offer", "office", "often"-    , "oil", "okay", "old", "olive", "olympic", "omit"-    , "once", "one", "onion", "online", "only", "open"-    , "opera", "opinion", "oppose", "option", "orange", "orbit"-    , "orchard", "order", "ordinary", "organ", "orient", "original"-    , "orphan", "ostrich", "other", "outdoor", "outer", "output"-    , "outside", "oval", "oven", "over", "own", "owner"-    , "oxygen", "oyster", "ozone", "pact", "paddle", "page"-    , "pair", "palace", "palm", "panda", "panel", "panic"-    , "panther", "paper", "parade", "parent", "park", "parrot"-    , "party", "pass", "patch", "path", "patient", "patrol"-    , "pattern", "pause", "pave", "payment", "peace", "peanut"-    , "pear", "peasant", "pelican", "pen", "penalty", "pencil"-    , "people", "pepper", "perfect", "permit", "person", "pet"-    , "phone", "photo", "phrase", "physical", "piano", "picnic"-    , "picture", "piece", "pig", "pigeon", "pill", "pilot"-    , "pink", "pioneer", "pipe", "pistol", "pitch", "pizza"-    , "place", "planet", "plastic", "plate", "play", "please"-    , "pledge", "pluck", "plug", "plunge", "poem", "poet"-    , "point", "polar", "pole", "police", "pond", "pony"-    , "pool", "popular", "portion", "position", "possible", "post"-    , "potato", "pottery", "poverty", "powder", "power", "practice"-    , "praise", "predict", "prefer", "prepare", "present", "pretty"-    , "prevent", "price", "pride", "primary", "print", "priority"-    , "prison", "private", "prize", "problem", "process", "produce"-    , "profit", "program", "project", "promote", "proof", "property"-    , "prosper", "protect", "proud", "provide", "public", "pudding"-    , "pull", "pulp", "pulse", "pumpkin", "punch", "pupil"-    , "puppy", "purchase", "purity", "purpose", "purse", "push"-    , "put", "puzzle", "pyramid", "quality", "quantum", "quarter"-    , "question", "quick", "quit", "quiz", "quote", "rabbit"-    , "raccoon", "race", "rack", "radar", "radio", "rail"-    , "rain", "raise", "rally", "ramp", "ranch", "random"-    , "range", "rapid", "rare", "rate", "rather", "raven"-    , "raw", "razor", "ready", "real", "reason", "rebel"-    , "rebuild", "recall", "receive", "recipe", "record", "recycle"-    , "reduce", "reflect", "reform", "refuse", "region", "regret"-    , "regular", "reject", "relax", "release", "relief", "rely"-    , "remain", "remember", "remind", "remove", "render", "renew"-    , "rent", "reopen", "repair", "repeat", "replace", "report"-    , "require", "rescue", "resemble", "resist", "resource", "response"-    , "result", "retire", "retreat", "return", "reunion", "reveal"-    , "review", "reward", "rhythm", "rib", "ribbon", "rice"-    , "rich", "ride", "ridge", "rifle", "right", "rigid"-    , "ring", "riot", "ripple", "risk", "ritual", "rival"-    , "river", "road", "roast", "robot", "robust", "rocket"-    , "romance", "roof", "rookie", "room", "rose", "rotate"-    , "rough", "round", "route", "royal", "rubber", "rude"-    , "rug", "rule", "run", "runway", "rural", "sad"-    , "saddle", "sadness", "safe", "sail", "salad", "salmon"-    , "salon", "salt", "salute", "same", "sample", "sand"-    , "satisfy", "satoshi", "sauce", "sausage", "save", "say"-    , "scale", "scan", "scare", "scatter", "scene", "scheme"-    , "school", "science", "scissors", "scorpion", "scout", "scrap"-    , "screen", "script", "scrub", "sea", "search", "season"-    , "seat", "second", "secret", "section", "security", "seed"-    , "seek", "segment", "select", "sell", "seminar", "senior"-    , "sense", "sentence", "series", "service", "session", "settle"-    , "setup", "seven", "shadow", "shaft", "shallow", "share"-    , "shed", "shell", "sheriff", "shield", "shift", "shine"-    , "ship", "shiver", "shock", "shoe", "shoot", "shop"-    , "short", "shoulder", "shove", "shrimp", "shrug", "shuffle"-    , "shy", "sibling", "sick", "side", "siege", "sight"-    , "sign", "silent", "silk", "silly", "silver", "similar"-    , "simple", "since", "sing", "siren", "sister", "situate"-    , "six", "size", "skate", "sketch", "ski", "skill"-    , "skin", "skirt", "skull", "slab", "slam", "sleep"-    , "slender", "slice", "slide", "slight", "slim", "slogan"-    , "slot", "slow", "slush", "small", "smart", "smile"-    , "smoke", "smooth", "snack", "snake", "snap", "sniff"-    , "snow", "soap", "soccer", "social", "sock", "soda"-    , "soft", "solar", "soldier", "solid", "solution", "solve"-    , "someone", "song", "soon", "sorry", "sort", "soul"-    , "sound", "soup", "source", "south", "space", "spare"-    , "spatial", "spawn", "speak", "special", "speed", "spell"-    , "spend", "sphere", "spice", "spider", "spike", "spin"-    , "spirit", "split", "spoil", "sponsor", "spoon", "sport"-    , "spot", "spray", "spread", "spring", "spy", "square"-    , "squeeze", "squirrel", "stable", "stadium", "staff", "stage"-    , "stairs", "stamp", "stand", "start", "state", "stay"-    , "steak", "steel", "stem", "step", "stereo", "stick"-    , "still", "sting", "stock", "stomach", "stone", "stool"-    , "story", "stove", "strategy", "street", "strike", "strong"-    , "struggle", "student", "stuff", "stumble", "style", "subject"-    , "submit", "subway", "success", "such", "sudden", "suffer"-    , "sugar", "suggest", "suit", "summer", "sun", "sunny"-    , "sunset", "super", "supply", "supreme", "sure", "surface"-    , "surge", "surprise", "surround", "survey", "suspect", "sustain"-    , "swallow", "swamp", "swap", "swarm", "swear", "sweet"-    , "swift", "swim", "swing", "switch", "sword", "symbol"-    , "symptom", "syrup", "system", "table", "tackle", "tag"-    , "tail", "talent", "talk", "tank", "tape", "target"-    , "task", "taste", "tattoo", "taxi", "teach", "team"-    , "tell", "ten", "tenant", "tennis", "tent", "term"-    , "test", "text", "thank", "that", "theme", "then"-    , "theory", "there", "they", "thing", "this", "thought"-    , "three", "thrive", "throw", "thumb", "thunder", "ticket"-    , "tide", "tiger", "tilt", "timber", "time", "tiny"-    , "tip", "tired", "tissue", "title", "toast", "tobacco"-    , "today", "toddler", "toe", "together", "toilet", "token"-    , "tomato", "tomorrow", "tone", "tongue", "tonight", "tool"-    , "tooth", "top", "topic", "topple", "torch", "tornado"-    , "tortoise", "toss", "total", "tourist", "toward", "tower"-    , "town", "toy", "track", "trade", "traffic", "tragic"-    , "train", "transfer", "trap", "trash", "travel", "tray"-    , "treat", "tree", "trend", "trial", "tribe", "trick"-    , "trigger", "trim", "trip", "trophy", "trouble", "truck"-    , "true", "truly", "trumpet", "trust", "truth", "try"-    , "tube", "tuition", "tumble", "tuna", "tunnel", "turkey"-    , "turn", "turtle", "twelve", "twenty", "twice", "twin"-    , "twist", "two", "type", "typical", "ugly", "umbrella"-    , "unable", "unaware", "uncle", "uncover", "under", "undo"-    , "unfair", "unfold", "unhappy", "uniform", "unique", "unit"-    , "universe", "unknown", "unlock", "until", "unusual", "unveil"-    , "update", "upgrade", "uphold", "upon", "upper", "upset"-    , "urban", "urge", "usage", "use", "used", "useful"-    , "useless", "usual", "utility", "vacant", "vacuum", "vague"-    , "valid", "valley", "valve", "van", "vanish", "vapor"-    , "various", "vast", "vault", "vehicle", "velvet", "vendor"-    , "venture", "venue", "verb", "verify", "version", "very"-    , "vessel", "veteran", "viable", "vibrant", "vicious", "victory"-    , "video", "view", "village", "vintage", "violin", "virtual"-    , "virus", "visa", "visit", "visual", "vital", "vivid"-    , "vocal", "voice", "void", "volcano", "volume", "vote"-    , "voyage", "wage", "wagon", "wait", "walk", "wall"-    , "walnut", "want", "warfare", "warm", "warrior", "wash"-    , "wasp", "waste", "water", "wave", "way", "wealth"-    , "weapon", "wear", "weasel", "weather", "web", "wedding"-    , "weekend", "weird", "welcome", "west", "wet", "whale"-    , "what", "wheat", "wheel", "when", "where", "whip"-    , "whisper", "wide", "width", "wife", "wild", "will"-    , "win", "window", "wine", "wing", "wink", "winner"-    , "winter", "wire", "wisdom", "wise", "wish", "witness"-    , "wolf", "woman", "wonder", "wood", "wool", "word"-    , "work", "world", "worry", "worth", "wrap", "wreck"-    , "wrestle", "wrist", "write", "wrong", "yard", "year"-    , "yellow", "you", "young", "youth", "zebra", "zero"-    , "zone", "zoo"-    ]
− src/Network/Haskoin/Network.hs
@@ -1,21 +0,0 @@-{-|-Module      : Network.Haskoin.Network-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module provides basic types used for the Bitcoin networking protocol-together with 'Data.Serialize' instances for efficiently serializing and-de-serializing them.--}-module Network.Haskoin.Network-    ( module Common-    , module Message-    , module Bloom-    ) where--import           Network.Haskoin.Network.Bloom   as Bloom-import           Network.Haskoin.Network.Common  as Common-import           Network.Haskoin.Network.Message as Message
− src/Network/Haskoin/Network/Bloom.hs
@@ -1,263 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Network.Bloom-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Bloom filters are used to reduce data transfer when synchronizing thin cients.-When bloom filters are used a client will obtain filtered blocks that only-contain transactions that pass the bloom filter. Transactions announced via inv-messages also pass the filter.--}-module Network.Haskoin.Network.Bloom-    ( -- * Bloom Filters-      BloomFlags(..)-    , BloomFilter(..)-    , FilterLoad(..)-    , FilterAdd(..)-    , bloomCreate-    , bloomInsert-    , bloomContains-    , isBloomValid-    , isBloomEmpty-    , isBloomFull-    , acceptsFilters-    , bloomRelevantUpdate-    ) where--import           Control.DeepSeq-import           Control.Monad                      (forM_, replicateM)-import           Data.Bits-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import qualified Data.Foldable                      as F-import           Data.Hash.Murmur                   (murmur3)-import           Data.List                          (foldl')-import qualified Data.Sequence                      as S-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (getByteString, getWord32le,-                                                     getWord8)-import           Data.Serialize.Put                 (putByteString, putWord32le,-                                                     putWord8)-import           Data.Word-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Transaction.Common--- | 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001%-maxBloomSize :: Int-maxBloomSize = 36000--maxHashFuncs :: Word32-maxHashFuncs = 50--ln2Squared :: Double-ln2Squared = 0.4804530139182014246671025263266649717305529515945455--ln2 :: Double-ln2 = 0.6931471805599453094172321214581765680755001343602552--bitMask :: [Word8]-bitMask = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]---- | The bloom flags are used to tell the remote peer how to auto-update--- the provided bloom filter.-data BloomFlags-    = BloomUpdateNone -- ^ never update-    | BloomUpdateAll -- ^ auto-update on all outputs-    | BloomUpdateP2PubKeyOnly-    -- ^ auto-update on pay-to-pubkey or pay-to-multisig (default)-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize BloomFlags where-    get = go =<< getWord8-      where-        go 0 = return BloomUpdateNone-        go 1 = return BloomUpdateAll-        go 2 = return BloomUpdateP2PubKeyOnly-        go _ = fail "BloomFlags get: Invalid bloom flag"--    put f = putWord8 $ case f of-        BloomUpdateNone         -> 0-        BloomUpdateAll          -> 1-        BloomUpdateP2PubKeyOnly -> 2---- | A bloom filter is a probabilistic data structure that SPV clients send to--- other peers to filter the set of transactions received from them. Bloom--- filters can have false positives but not false negatives. Some transactions--- that pass the filter may not be relevant to the receiving peer. By--- controlling the false positive rate, SPV nodes can trade off bandwidth--- versus privacy.-data BloomFilter = BloomFilter-    { bloomData      :: !(S.Seq Word8)-    -- ^ bloom filter data-    , bloomHashFuncs :: !Word32-    -- ^ number of hash functions for this filter-    , bloomTweak     :: !Word32-    -- ^ hash function random nonce-    , bloomFlags     :: !BloomFlags-    -- ^ bloom filter auto-update flags-    }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize BloomFilter where--    get = BloomFilter <$> (S.fromList <$> (readDat =<< get))-                      <*> getWord32le <*> getWord32le-                      <*> get-      where-        readDat (VarInt len) = replicateM (fromIntegral len) getWord8--    put (BloomFilter dat hashFuncs tweak flags) = do-        putVarInt $ S.length dat-        forM_ (F.toList dat) putWord8-        putWord32le hashFuncs-        putWord32le tweak-        put flags---- | Set a new bloom filter on the peer connection.-newtype FilterLoad = FilterLoad { filterLoadBloomFilter :: BloomFilter }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize FilterLoad where-    get = FilterLoad <$> get-    put (FilterLoad f) = put f---- | Add the given data element to the connections current filter without--- requiring a completely new one to be set.-newtype FilterAdd = FilterAdd { getFilterData :: ByteString }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize FilterAdd where-    get = do-        (VarInt len) <- get-        dat <- getByteString $ fromIntegral len-        return $ FilterAdd dat--    put (FilterAdd bs) = do-        putVarInt $ BS.length bs-        putByteString bs----- | Build a bloom filter that will provide the given false positive rate when--- the given number of elements have been inserted.-bloomCreate :: Int          -- ^ number of elements-            -> Double       -- ^ false positive rate-            -> Word32       -- ^ random nonce (tweak) for the hash function-            -> BloomFlags   -- ^ bloom filter flags-            -> BloomFilter  -- ^ bloom filter-bloomCreate numElem fpRate =-    BloomFilter (S.replicate bloomSize 0) numHashF-  where-    -- Bloom filter size in bytes-    bloomSize = truncate $ min a b / 8-    -- Suggested size in bits-    a         = -1 / ln2Squared * fromIntegral numElem * log fpRate-    -- Maximum size in bits-    b         = fromIntegral $ maxBloomSize * 8-    numHashF  = truncate $ min c (fromIntegral maxHashFuncs)-    -- Suggested number of hash functions-    c         = fromIntegral bloomSize * 8 / fromIntegral numElem * ln2--bloomHash :: BloomFilter -> Word32 -> ByteString -> Word32-bloomHash bfilter hashNum bs =-    murmur3 seed bs `mod` (fromIntegral (S.length (bloomData bfilter)) * 8)-  where-    seed = hashNum * 0xfba4c795 + bloomTweak bfilter---- | Insert arbitrary data into a bloom filter. Returns the new bloom filter--- containing the new data.-bloomInsert :: BloomFilter    -- ^ Original bloom filter-            -> ByteString     -- ^ New data to insert-            -> BloomFilter    -- ^ Bloom filter containing the new data-bloomInsert bfilter bs-    | isBloomFull bfilter = bfilter-    | otherwise = bfilter { bloomData = newData }-  where-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    upd s i = S.adjust (.|. bitMask !! fromIntegral (7 .&. i))-                       (fromIntegral $ i `shiftR` 3) s-    newData = foldl upd (bloomData bfilter) idxs---- | Tests if some arbitrary data matches the filter. This can be either because--- the data was inserted into the filter or because it is a false positive.-bloomContains :: BloomFilter    -- ^ Bloom filter-              -> ByteString-              -- ^ Data that will be checked against the given bloom filter-              -> Bool-              -- ^ Returns True if the data matches the filter-bloomContains bfilter bs-    | isBloomFull bfilter  = True-    | isBloomEmpty bfilter = False-    | otherwise            = all isSet idxs-  where-    s       = bloomData bfilter-    idxs    = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]-    isSet i = S.index s (fromIntegral $ i `shiftR` 3)-          .&. (bitMask !! fromIntegral (7 .&. i)) /= 0---- | Checks if any of the outputs of a tx is in the current bloom filter.--- If it is, add the txid and vout as an outpoint (i.e. so that--- a future tx that spends the output won't be missed).-bloomRelevantUpdate :: BloomFilter-                    -- ^ Bloom filter-                    -> Tx-                    -- ^ Tx that may (or may not) have relevant outputs-                    -> Maybe BloomFilter-                    -- ^ Returns an updated bloom filter adding relevant output-bloomRelevantUpdate bfilter tx-    | isBloomFull bfilter || isBloomEmpty bfilter = Nothing-    | bloomFlags bfilter == BloomUpdateNone = Nothing-    | length matchOuts > 0 = Just $ foldl' (addRelevant) bfilter matchOuts-    | otherwise = Nothing-    where-        -- TxHash if we end up inserting an outpoint-        h = txHash tx-        -- Decode the scriptOutpus and add vOuts in case we make them outpoints-        decodedOutputScripts = traverse (decodeOutputBS . scriptOutput) $ txOut tx-        err = error $ "Error Decoding output script"-        idxOutputScripts = either (const err) (zip [0..]) decodedOutputScripts-        -- Check if any txOuts were contained in the bloom filter-        matchFilter = filter (\(_,op) -> bloomContains bfilter $ encodeScriptOut op)-        matchOuts = matchFilter idxOutputScripts--        addRelevant :: BloomFilter -> (Word32,ScriptOutput) -> BloomFilter-        addRelevant bfilter (id,scriptOut) = case (bloomFlags bfilter,scriptType) of-            -- We filtered out BloomUpdateNone so we insert any PayPk or PayMulSig-            (_, True) -> bloomInsert bfilter outpoint-            (BloomUpdateAll, _ ) -> bloomInsert bfilter outpoint-            _ ->  error "Error Updating Bloom Filter with relevant outpoint"-            where-                outpoint = encode $ OutPoint{ outPointHash = h, outPointIndex = id}-                scriptType = (\s -> isPayPK s ||isPayMulSig s) scriptOut--        -- Encodes a scriptOutput so it can be checked agains the Bloom Filter-        encodeScriptOut :: ScriptOutput -> ByteString-        encodeScriptOut (PayMulSig outputMuSig _) = encode outputMuSig-        encodeScriptOut (PayWitnessScriptHash scriptHash) = encode scriptHash-        encodeScriptOut (DataCarrier getOutputData) = encode getOutputData-        encodeScriptOut outputHash = (encode . getOutputHash) outputHash---- | Returns True if the filter is empty (all bytes set to 0x00)-isBloomEmpty :: BloomFilter -> Bool-isBloomEmpty bfilter = all (== 0x00) $ F.toList $ bloomData bfilter---- | Returns True if the filter is full (all bytes set to 0xff)-isBloomFull :: BloomFilter -> Bool-isBloomFull bfilter = all (== 0xff) $ F.toList $ bloomData bfilter---- | Tests if a given bloom filter is valid.-isBloomValid :: BloomFilter -- ^ Bloom filter to test-             -> Bool        -- ^ True if the given filter is valid-isBloomValid bfilter =-    S.length (bloomData bfilter) <= maxBloomSize &&-    bloomHashFuncs bfilter <= maxHashFuncs---- | Does the peer with these version services accept bloom filters?-acceptsFilters :: Word64 -> Bool-acceptsFilters srv = srv .&. (1 `shiftL` 2) /= 0
− src/Network/Haskoin/Network/Common.hs
@@ -1,613 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Network.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common functions and data types related to peer-to-peer network.--}-module Network.Haskoin.Network.Common-    ( -- * Network Data Types-      Addr(..)-    , NetworkAddressTime-    , Alert(..)-    , GetData(..)-    , Inv(..)-    , InvVector(..)-    , InvType(..)-    , HostAddress-    , hostToSockAddr-    , sockToHostAddress-    , NetworkAddress(..)-    , NotFound(..)-    , Ping(..)-    , Pong(..)-    , Reject(..)-    , RejectCode(..)-    , VarInt(..)-    , VarString(..)-    , Version(..)-    , MessageCommand(..)-      -- ** Useful Functions-    , reject-    , nodeNone-    , nodeNetwork-    , nodeGetUTXO-    , nodeBloom-    , nodeWitness-    , nodeXThin-    , commandToString-    , stringToCommand-    , putVarInt-    ) where--import           Control.DeepSeq-import           Control.Monad               (forM_, liftM2, replicateM, unless)-import           Data.Bits                   (shiftL)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import           Data.ByteString.Char8       as C (replicate)-import           Data.Serialize              as S-import           Data.String-import           Data.String.Conversions     (cs)-import           Data.Word                   (Word32, Word64)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Socket              (SockAddr (..))-import           Text.Read                   as R---- | Network address with a timestamp.-type NetworkAddressTime = (Word32, NetworkAddress)---- | Provides information about known nodes in the bitcoin network. An 'Addr'--- type is sent inside a 'Message' as a response to a 'GetAddr' message.-newtype Addr =-    Addr { -- List of addresses of other nodes on the network with timestamps.-           addrList :: [NetworkAddressTime]-         }-    deriving (Eq, Show, Generic, NFData)--instance Serialize Addr where--    get = Addr <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) action-        action             = liftM2 (,) getWord32le S.get--    put (Addr xs) = do-        putVarInt $ length xs-        forM_ xs $ \(a,b) -> putWord32le a >> put b---- | Data type describing signed messages that can be sent between bitcoin--- nodes to display important notifications to end users about the health of--- the network.-data Alert =-    Alert {-          -- | Alert payload.-            alertPayload   :: !VarString-          -- | ECDSA signature of the payload-          , alertSignature :: !VarString-          } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Alert where-    get = Alert <$> S.get <*> S.get-    put (Alert p s) = put p >> put s---- | The 'GetData' type is used to retrieve information on a specific object--- ('Block' or 'Tx') identified by the objects hash. The payload of a 'GetData'--- request is a list of 'InvVector' which represent all the hashes of objects--- that a node wants. The response to a 'GetBlock' message will be either a--- 'Block' or a 'Tx' message depending on the type of the object referenced by--- the hash. Usually, 'GetData' messages are sent after a node receives an 'Inv'--- message that contains unknown object hashes.-newtype GetData =-    GetData { -- | list of object hashes-              getDataList :: [InvVector]-            } deriving (Eq, Show, Generic, NFData)--instance Serialize GetData where--    get = GetData <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (GetData xs) = do-        putVarInt $ length xs-        forM_ xs put---- | 'Inv' messages are used by nodes to advertise their knowledge of new--- objects by publishing a list of hashes to a peer. 'Inv' messages can be sent--- unsolicited or in response to a 'GetBlocks' message.-newtype Inv =-    Inv {-        -- | inventory-          invList :: [InvVector]-        } deriving (Eq, Show, Generic, NFData)--instance Serialize Inv where--    get = Inv <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (Inv xs) = do-        putVarInt $ length xs-        forM_ xs put---- | Data type identifying the type of an inventory vector. SegWit types are--- only used in 'GetData' messages, not 'Inv'.-data InvType-    = InvError -- ^ error-    | InvTx -- ^ transaction-    | InvBlock -- ^ block-    | InvMerkleBlock -- ^ filtered block-    | InvWitnessTx -- ^ segwit transaction-    | InvWitnessBlock -- ^ segwit block-    | InvWitnessMerkleBlock -- ^ segwit filtere block-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize InvType where-    get = go =<< getWord32le-      where-        go x =-            case x of-                0 -> return InvError-                1 -> return InvTx-                2 -> return InvBlock-                3 -> return InvMerkleBlock-                _-                    | x == 1 `shiftL` 30 + 1 -> return InvWitnessTx-                    | x == 1 `shiftL` 30 + 2 -> return InvWitnessBlock-                    | x == 1 `shiftL` 30 + 3 -> return InvWitnessMerkleBlock-                    | otherwise -> fail "bitcoinGet InvType: Invalid Type"-    put x =-        putWord32le $-        case x of-            InvError              -> 0-            InvTx                 -> 1-            InvBlock              -> 2-            InvMerkleBlock        -> 3-            InvWitnessTx          -> 1 `shiftL` 30 + 1-            InvWitnessBlock       -> 1 `shiftL` 30 + 2-            InvWitnessMerkleBlock -> 1 `shiftL` 30 + 3---- | Invectory vectors represent hashes identifying objects such as a 'Block' or--- a 'Tx'. They notify other peers about new data or data they have otherwise--- requested.-data InvVector =-    InvVector {-                -- | type of object-                invType :: !InvType-                -- | 256-bit hash of object-              , invHash :: !Hash256-              } deriving (Eq, Show, Generic, NFData)--instance Serialize InvVector where-    get = InvVector <$> S.get <*> S.get-    put (InvVector t h) = put t >> put h--newtype HostAddress =-    HostAddress ByteString-    deriving (Eq, Show, Ord, Generic, NFData)--instance Serialize HostAddress where-    put (HostAddress bs) = putByteString bs-    get = HostAddress <$> getByteString 18---- | Data type describing a bitcoin network address. Addresses are stored in--- IPv6 format. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6--- addresses: <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>.-data NetworkAddress =-    NetworkAddress { -- | bitmask of services available for this address-                     naServices :: !Word64-                     -- | address and port information-                   , naAddress  :: !HostAddress-                   } deriving (Eq, Show, Generic, NFData)--hostToSockAddr :: HostAddress -> SockAddr-hostToSockAddr (HostAddress bs) =-    case runGet getSockAddr bs of-        Left e  -> error e-        Right x -> x--sockToHostAddress :: SockAddr -> HostAddress-sockToHostAddress = HostAddress . runPut . putSockAddr--putSockAddr :: SockAddr -> Put-putSockAddr (SockAddrInet6 p _ (a, b, c, d) _) = do-    putWord32be a-    putWord32be b-    putWord32be c-    putWord32be d-    putWord16be (fromIntegral p)--putSockAddr (SockAddrInet p a) = do-    putWord32be 0x00000000-    putWord32be 0x00000000-    putWord32be 0x0000ffff-    putWord32host a-    putWord16be (fromIntegral p)--putSockAddr _ = error "Invalid address type"--getSockAddr :: Get SockAddr-getSockAddr = do-    a <- getWord32be-    b <- getWord32be-    c <- getWord32be-    if a == 0x00000000 && b == 0x00000000 && c == 0x0000ffff-        then do-            d <- getWord32host-            p <- getWord16be-            return $ SockAddrInet (fromIntegral p) d-        else do-            d <- getWord32be-            p <- getWord16be-            return $ SockAddrInet6 (fromIntegral p) 0 (a, b, c, d) 0--instance Serialize NetworkAddress where-    get = NetworkAddress <$> getWord64le <*> S.get-    put (NetworkAddress s a) = putWord64le s >> put a---- | A 'NotFound' message is returned as a response to a 'GetData' message--- whe one of the requested objects could not be retrieved. This could happen,--- for example, if a tranasaction was requested and was not available in the--- memory pool of the receiving node.-newtype NotFound =-    NotFound { -- | Inventory vectors related to this request-               notFoundList :: [InvVector]-             } deriving (Eq, Show, Generic, NFData)--instance Serialize NotFound where--    get = NotFound <$> (repList =<< S.get)-      where-        repList (VarInt c) = replicateM (fromIntegral c) S.get--    put (NotFound xs) = do-        putVarInt $ length xs-        forM_ xs put---- | A 'Ping' message is sent to bitcoin peers to check if a connection is still--- open.-newtype Ping =-    Ping { -- | A random nonce used to identify the recipient of the ping-           -- request once a Pong response is received.-           pingNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)---- | A Pong message is sent as a response to a ping message.-newtype Pong =-    Pong {-           -- | nonce from corresponding 'Ping'-           pongNonce :: Word64-         } deriving (Eq, Show, Read, Generic, NFData)--instance Serialize Ping where-    get = Ping <$> getWord64le-    put (Ping n) = putWord64le n--instance Serialize Pong where-    get = Pong <$> getWord64le-    put (Pong n) = putWord64le n---- | The 'Reject' message is sent when messages are rejected by a peer.-data Reject =-    Reject {-             -- | type of message rejected-             rejectMessage :: !MessageCommand-             -- | rejection code-           , rejectCode    :: !RejectCode-             -- | text reason for rejection-           , rejectReason  :: !VarString-             -- | extra data such as block or tx hash-           , rejectData    :: !ByteString-           } deriving (Eq, Show, Read, Generic, NFData)---- | Rejection code associated to the 'Reject' message.-data RejectCode-    = RejectMalformed-    | RejectInvalid-    | RejectObsolete-    | RejectDuplicate-    | RejectNonStandard-    | RejectDust-    | RejectInsufficientFee-    | RejectCheckpoint-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize RejectCode where--    get = getWord8 >>= \code -> case code of-        0x01 -> return RejectMalformed-        0x10 -> return RejectInvalid-        0x11 -> return RejectObsolete-        0x12 -> return RejectDuplicate-        0x40 -> return RejectNonStandard-        0x41 -> return RejectDust-        0x42 -> return RejectInsufficientFee-        0x43 -> return RejectCheckpoint-        _    -> fail $ unwords-            [ "Reject get: Invalid code"-            , show code-            ]--    put code = putWord8 $ case code of-        RejectMalformed       -> 0x01-        RejectInvalid         -> 0x10-        RejectObsolete        -> 0x11-        RejectDuplicate       -> 0x12-        RejectNonStandard     -> 0x40-        RejectDust            -> 0x41-        RejectInsufficientFee -> 0x42-        RejectCheckpoint      -> 0x43---- | Convenience function to build a 'Reject' message.-reject :: MessageCommand -> RejectCode -> ByteString -> Reject-reject cmd code reason =-    Reject cmd code (VarString reason) B.empty--instance Serialize Reject where-    get =-        S.get >>= \(VarString bs) ->-            Reject (stringToCommand bs) <$> S.get <*> S.get <*> maybeData-      where-        maybeData =-            isEmpty >>= \done ->-                if done-                    then return B.empty-                    else getByteString 32-    put (Reject cmd code reason dat) = do-        put $ VarString $ commandToString cmd-        put code-        put reason-        unless (B.null dat) $ putByteString dat---- | Data type representing a variable-length integer. The 'VarInt' type--- usually precedes an array or a string that can vary in length.-newtype VarInt = VarInt { getVarInt :: Word64 }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarInt where--    get = VarInt <$> ( getWord8 >>= go )-      where-        go 0xff = getWord64le-        go 0xfe = fromIntegral <$> getWord32le-        go 0xfd = fromIntegral <$> getWord16le-        go x    = fromIntegral <$> return x--    put (VarInt x)-        | x < 0xfd =-            putWord8 $ fromIntegral x-        | x <= 0xffff = do-            putWord8 0xfd-            putWord16le $ fromIntegral x-        | x <= 0xffffffff = do-            putWord8 0xfe-            putWord32le $ fromIntegral x-        | otherwise = do-            putWord8 0xff-            putWord64le x--putVarInt :: Integral a => a -> Put-putVarInt = put . VarInt . fromIntegral---- | Data type for serialization of variable-length strings.-newtype VarString = VarString { getVarString :: ByteString }-    deriving (Eq, Show, Read, Generic, NFData)--instance Serialize VarString where--    get = VarString <$> (readBS =<< S.get)-      where-        readBS (VarInt len) = getByteString (fromIntegral len)--    put (VarString bs) = do-        putVarInt $ B.length bs-        putByteString bs---- | When a bitcoin node creates an outgoing connection to another node,--- the first message it will send is a 'Version' message. The other node--- will similarly respond with it's own 'Version' message.-data Version =-    Version {-              -- | protocol version-              version     :: !Word32-              -- | features supported by this connection-            , services    :: !Word64-              -- | unix timestamp-            , timestamp   :: !Word64-              -- | network address of remote node-            , addrRecv    :: !NetworkAddress-              -- | network address of sending node-            , addrSend    :: !NetworkAddress-              -- | random nonce to detect connection to self-            , verNonce    :: !Word64-              -- | user agent string-            , userAgent   :: !VarString-              -- | height of the last block in sending node-            , startHeight :: !Word32-              -- | relay transactions flag (BIP-37)-            , relay       :: !Bool-            } deriving (Eq, Show, Generic, NFData)--instance Serialize Version where--    get = Version <$> getWord32le-                  <*> getWord64le-                  <*> getWord64le-                  <*> S.get-                  <*> S.get-                  <*> getWord64le-                  <*> S.get-                  <*> getWord32le-                  <*> (go =<< isEmpty)-      where-        go True  = return True-        go False = getBool--    put (Version v s t ar as n ua sh r) = do-        putWord32le v-        putWord64le s-        putWord64le t-        put         ar-        put         as-        putWord64le n-        put         ua-        putWord32le sh-        putBool     r---- | 0x00 is 'False', anything else is 'True'.-getBool :: Get Bool-getBool = go =<< getWord8-  where-    go 0 = return False-    go _ = return True--putBool :: Bool -> Put-putBool True  = putWord8 1-putBool False = putWord8 0---- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify--- the type of message present in the payload. This allows the message--- de-serialization code to know how to decode a particular message payload.--- Every valid 'Message' constructor has a corresponding 'MessageCommand'--- constructor.-data MessageCommand-    = MCVersion-    | MCVerAck-    | MCAddr-    | MCInv-    | MCGetData-    | MCNotFound-    | MCGetBlocks-    | MCGetHeaders-    | MCTx-    | MCBlock-    | MCMerkleBlock-    | MCHeaders-    | MCGetAddr-    | MCFilterLoad-    | MCFilterAdd-    | MCFilterClear-    | MCPing-    | MCPong-    | MCAlert-    | MCMempool-    | MCReject-    | MCSendHeaders-    | MCOther ByteString-    deriving (Eq, Generic, NFData)--instance Show MessageCommand where-    showsPrec _ = shows . commandToString--instance Read MessageCommand where-    readPrec = do-        String str <- lexP-        return (stringToCommand (cs str))--instance Serialize MessageCommand where-    get = go <$> getByteString 12-      where-        go bs =-            let str = unpackCommand bs-             in stringToCommand str-    put mc = putByteString $ packCommand $ commandToString mc--instance IsString MessageCommand where-    fromString str = stringToCommand (cs str)---- | Read a 'MessageCommand' from its string representation.-stringToCommand :: ByteString -> MessageCommand-stringToCommand str = case str of-    "version"     -> MCVersion-    "verack"      -> MCVerAck-    "addr"        -> MCAddr-    "inv"         -> MCInv-    "getdata"     -> MCGetData-    "notfound"    -> MCNotFound-    "getblocks"   -> MCGetBlocks-    "getheaders"  -> MCGetHeaders-    "tx"          -> MCTx-    "block"       -> MCBlock-    "merkleblock" -> MCMerkleBlock-    "headers"     -> MCHeaders-    "getaddr"     -> MCGetAddr-    "filterload"  -> MCFilterLoad-    "filteradd"   -> MCFilterAdd-    "filterclear" -> MCFilterClear-    "ping"        -> MCPing-    "pong"        -> MCPong-    "alert"       -> MCAlert-    "mempool"     -> MCMempool-    "reject"      -> MCReject-    "sendheaders" -> MCSendHeaders-    _             -> MCOther str---- | Convert a 'MessageCommand' to its string representation.-commandToString :: MessageCommand -> ByteString-commandToString mc = case mc of-    MCVersion     -> "version"-    MCVerAck      -> "verack"-    MCAddr        -> "addr"-    MCInv         -> "inv"-    MCGetData     -> "getdata"-    MCNotFound    -> "notfound"-    MCGetBlocks   -> "getblocks"-    MCGetHeaders  -> "getheaders"-    MCTx          -> "tx"-    MCBlock       -> "block"-    MCMerkleBlock -> "merkleblock"-    MCHeaders     -> "headers"-    MCGetAddr     -> "getaddr"-    MCFilterLoad  -> "filterload"-    MCFilterAdd   -> "filteradd"-    MCFilterClear -> "filterclear"-    MCPing        -> "ping"-    MCPong        -> "pong"-    MCAlert       -> "alert"-    MCMempool     -> "mempool"-    MCReject      -> "reject"-    MCSendHeaders -> "sendheaders"-    MCOther c     -> c---- | Pack a string 'MessageCommand' so that it is exactly 12-bytes long.-packCommand :: ByteString -> ByteString-packCommand s = B.take 12 $-    s `mappend` C.replicate 12 '\NUL'---- | Undo packing done by 'packCommand'.-unpackCommand :: ByteString -> ByteString-unpackCommand = B.takeWhile (/= 0)---- | Node offers no services.-nodeNone :: Word64-nodeNone = 0---- | Services indicate node is a full node that can serve full blocks.-nodeNetwork :: Word64-nodeNetwork = 1---- | Services indicate node allows to request 'UTXO' set.-nodeGetUTXO :: Word64-nodeGetUTXO = 1 `shiftL` 1---- | Services indicate node accepts bloom filters.-nodeBloom :: Word64-nodeBloom = 1 `shiftL` 2---- | Services indicate SegWit-capable node.-nodeWitness :: Word64-nodeWitness = 1 `shiftL` 3---- | Services indicate Xtreme Thinblocks compatibility.-nodeXThin :: Word64-nodeXThin = 1 `shiftL` 4
− src/Network/Haskoin/Network/Message.hs
@@ -1,203 +0,0 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}-{-|-Module      : Network.Haskoin.Network.Message-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Peer-to-peer network message serialization.--}-module Network.Haskoin.Network.Message-    ( -- * Network Message-      Message(..)-    , MessageHeader(..)-    , msgType-    , putMessage-    , getMessage-    ) where--import           Control.Monad                      (unless)-import           Control.DeepSeq-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import           Data.Serialize                     (Serialize, encode, get,-                                                     put)-import           Data.Serialize.Get                 (Get, getByteString,-                                                     getWord32be, getWord32le,-                                                     isolate, lookAhead)-import           Data.Serialize.Put                 (Putter, putByteString,-                                                     putWord32be, putWord32le)-import           Data.Word                          (Word32)-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Bloom-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Transaction.Common---- | Data type representing the header of a 'Message'. All messages sent between--- nodes contain a message header.-data MessageHeader = MessageHeader-    { -- | magic bytes identify network-      headMagic       :: !Word32-      -- | message type-    , headCmd         :: !MessageCommand-      -- | length of payload-    , headPayloadSize :: !Word32-      -- | checksum of payload-    , headChecksum    :: !CheckSum32-    } deriving (Eq, Show, Generic, NFData)--instance Serialize MessageHeader where--    get = MessageHeader <$> getWord32be-                        <*> get-                        <*> getWord32le-                        <*> get--    put (MessageHeader m c l chk) = do-        putWord32be m-        put         c-        putWord32le l-        put         chk---- | The 'Message' type is used to identify all the valid messages that can be--- sent between bitcoin peers. Only values of type 'Message' will be accepted--- by other bitcoin peers as bitcoin protocol messages need to be correctly--- serialized with message headers. Serializing a 'Message' value will--- include the 'MessageHeader' with the correct checksum value automatically.--- No need to add the 'MessageHeader' separately.-data Message-    = MVersion !Version-    | MVerAck-    | MAddr !Addr-    | MInv !Inv-    | MGetData !GetData-    | MNotFound !NotFound-    | MGetBlocks !GetBlocks-    | MGetHeaders !GetHeaders-    | MTx !Tx-    | MBlock !Block-    | MMerkleBlock !MerkleBlock-    | MHeaders !Headers-    | MGetAddr-    | MFilterLoad !FilterLoad-    | MFilterAdd !FilterAdd-    | MFilterClear-    | MPing !Ping-    | MPong !Pong-    | MAlert !Alert-    | MMempool-    | MReject !Reject-    | MSendHeaders-    | MOther !ByteString !ByteString-    deriving (Eq, Show, Generic, NFData)---- | Get 'MessageCommand' assocated with a message.-msgType :: Message -> MessageCommand-msgType (MVersion _)     = MCVersion-msgType MVerAck          = MCVerAck-msgType (MAddr _)        = MCAddr-msgType (MInv _)         = MCInv-msgType (MGetData _)     = MCGetData-msgType (MNotFound _)    = MCNotFound-msgType (MGetBlocks _)   = MCGetBlocks-msgType (MGetHeaders _)  = MCGetHeaders-msgType (MTx _)          = MCTx-msgType (MBlock _)       = MCBlock-msgType (MMerkleBlock _) = MCMerkleBlock-msgType (MHeaders _)     = MCHeaders--msgType (MFilterLoad _)  = MCFilterLoad-msgType (MFilterAdd _)   = MCFilterAdd-msgType MFilterClear     = MCFilterClear-msgType (MPing _)        = MCPing-msgType (MPong _)        = MCPong-msgType (MAlert _)       = MCAlert-msgType MMempool         = MCMempool-msgType (MReject _)      = MCReject-msgType MSendHeaders     = MCSendHeaders-msgType MGetAddr         = MCGetAddr-msgType (MOther c _)     = MCOther c---- | Deserializer for network messages.-getMessage :: Network -> Get Message-getMessage net = do-    (MessageHeader mgc cmd len chk) <- get-    bs <- lookAhead $ getByteString $ fromIntegral len-    unless-        (mgc == getNetworkMagic net)-        (fail $ "get: Invalid network magic bytes: " ++ show mgc)-    unless-        (checkSum32 bs == chk)-        (fail $ "get: Invalid message checksum: " ++ show chk)-    if len > 0-        then isolate (fromIntegral len) $-             case cmd of-                 MCVersion     -> MVersion <$> get-                 MCAddr        -> MAddr <$> get-                 MCInv         -> MInv <$> get-                 MCGetData     -> MGetData <$> get-                 MCNotFound    -> MNotFound <$> get-                 MCGetBlocks   -> MGetBlocks <$> get-                 MCGetHeaders  -> MGetHeaders <$> get-                 MCTx          -> MTx <$> get-                 MCBlock       -> MBlock <$> get-                 MCMerkleBlock -> MMerkleBlock <$> get-                 MCHeaders     -> MHeaders <$> get-                 MCFilterLoad  -> MFilterLoad <$> get-                 MCFilterAdd   -> MFilterAdd <$> get-                 MCPing        -> MPing <$> get-                 MCPong        -> MPong <$> get-                 MCAlert       -> MAlert <$> get-                 MCReject      -> MReject <$> get-                 MCOther c     -> MOther c <$> getByteString (fromIntegral len)-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " should not carry a payload"-        else case cmd of-                 MCGetAddr     -> return MGetAddr-                 MCVerAck      -> return MVerAck-                 MCFilterClear -> return MFilterClear-                 MCMempool     -> return MMempool-                 MCSendHeaders -> return MSendHeaders-                 _             -> fail $ "get: command " ++ show cmd ++-                                         " is expected to carry a payload"---- | Serializer for network messages.-putMessage :: Network -> Putter Message-putMessage net msg = do-    let (cmd, payload) =-            case msg of-                MVersion m     -> (MCVersion, encode m)-                MVerAck        -> (MCVerAck, BS.empty)-                MAddr m        -> (MCAddr, encode m)-                MInv m         -> (MCInv, encode m)-                MGetData m     -> (MCGetData, encode m)-                MNotFound m    -> (MCNotFound, encode m)-                MGetBlocks m   -> (MCGetBlocks, encode m)-                MGetHeaders m  -> (MCGetHeaders, encode m)-                MTx m          -> (MCTx, encode m)-                MBlock m       -> (MCBlock, encode m)-                MMerkleBlock m -> (MCMerkleBlock, encode m)-                MHeaders m     -> (MCHeaders, encode m)-                MGetAddr       -> (MCGetAddr, BS.empty)-                MFilterLoad m  -> (MCFilterLoad, encode m)-                MFilterAdd m   -> (MCFilterAdd, encode m)-                MFilterClear   -> (MCFilterClear, BS.empty)-                MPing m        -> (MCPing, encode m)-                MPong m        -> (MCPong, encode m)-                MAlert m       -> (MCAlert, encode m)-                MMempool       -> (MCMempool, BS.empty)-                MReject m      -> (MCReject, encode m)-                MSendHeaders   -> (MCSendHeaders, BS.empty)-                MOther c p     -> (MCOther c, p)-        chk = checkSum32 payload-        len = fromIntegral $ BS.length payload-        header = MessageHeader (getNetworkMagic net) cmd len chk-    put header-    putByteString payload
− src/Network/Haskoin/Script.hs
@@ -1,19 +0,0 @@-{-|-Module      : Network.Haskoin.Script-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module provides functions for parsing and evaluating bitcoin-transaction scripts. Data types are provided for building and-deconstructing all of the standard input and output script types.--}-module Network.Haskoin.Script-    ( module X-    ) where--import           Network.Haskoin.Script.Common   as X-import           Network.Haskoin.Script.SigHash  as X-import           Network.Haskoin.Script.Standard as X
− src/Network/Haskoin/Script/Common.hs
@@ -1,731 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Script.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Common script-related functions and data types.--}-module Network.Haskoin.Script.Common-    ( ScriptOp(..)-    , Script(..)-    , PushDataType(..)-    , ScriptOutput(..)-    , isPayPK-    , isPayPKHash-    , isPayMulSig-    , isPayScriptHash-    , isPayWitnessPKHash-    , isPayWitnessScriptHash-    , isDataCarrier-    , encodeOutput-    , encodeOutputBS-    , decodeOutput-    , decodeOutputBS-    , toP2SH-    , toP2WSH-    , isPushOp-    , opPushData-    , intToScriptOp-    , scriptOpToInt-    ) where--import           Control.DeepSeq-import           Control.Monad-import           Data.Aeson                  as A-import           Data.Aeson.Encoding         (text)-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import           Data.Hashable-import           Data.Serialize              as S-import           Data.Word                   (Word8)-import           GHC.Generics                (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Util---- | Data type representing a transaction script. Scripts are defined as lists--- of script operators 'ScriptOp'. Scripts are used to:------ * Define the spending conditions in the output of a transaction.--- * Provide signatures in the input of a transaction (except SegWit).------ SigWit only: the segregated witness data structure, and not the input script,--- contains signatures and redeem script for pay-to-witness-script and--- pay-to-witness-public-key-hash transactions.-newtype Script =-    Script {-             -- | script operators defining this script-             scriptOps :: [ScriptOp]-           }-    deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance Serialize Script where-    get =-        Script <$> getScriptOps-      where-        getScriptOps = do-            empty <- isEmpty-            if empty-                then return []-                else (:) <$> get <*> getScriptOps--    put (Script ops) = forM_ ops put---- | Data type representing the type of an OP_PUSHDATA opcode.-data PushDataType-    =-      -- | next opcode bytes is data to be pushed-      OPCODE-      -- | next byte contains number of bytes of data to be pushed-    | OPDATA1-      -- | next two bytes contains number of bytes to be pushed-    | OPDATA2-      -- | next four bytes contains the number of bytes to be pushed-    | OPDATA4-    deriving (Show, Read, Eq, Generic, Hashable, NFData)---- | Data type representing an operator allowed inside a 'Script'.-data ScriptOp-      -- Pushing Data-    = OP_PUSHDATA !ByteString-                  !PushDataType-    | OP_0-    | OP_1NEGATE-    | OP_RESERVED-    | OP_1-    | OP_2-    | OP_3-    | OP_4-    | OP_5-    | OP_6-    | OP_7-    | OP_8-    | OP_9-    | OP_10-    | OP_11-    | OP_12-    | OP_13-    | OP_14-    | OP_15-    | OP_16-      -- Flow control-    | OP_NOP-    | OP_VER -- reserved-    | OP_IF-    | OP_NOTIF-    | OP_VERIF -- resreved-    | OP_VERNOTIF -- reserved-    | OP_ELSE-    | OP_ENDIF-    | OP_VERIFY-    | OP_RETURN-      -- Stack operations-    | OP_TOALTSTACK-    | OP_FROMALTSTACK-    | OP_IFDUP-    | OP_DEPTH-    | OP_DROP-    | OP_DUP-    | OP_NIP-    | OP_OVER-    | OP_PICK-    | OP_ROLL-    | OP_ROT-    | OP_SWAP-    | OP_TUCK-    | OP_2DROP-    | OP_2DUP-    | OP_3DUP-    | OP_2OVER-    | OP_2ROT-    | OP_2SWAP-      -- Splice-    | OP_CAT-    | OP_SUBSTR-    | OP_LEFT-    | OP_RIGHT-    | OP_SIZE-      -- Bitwise logic-    | OP_INVERT-    | OP_AND-    | OP_OR-    | OP_XOR-    | OP_EQUAL-    | OP_EQUALVERIFY-    | OP_RESERVED1-    | OP_RESERVED2-      -- Arithmetic-    | OP_1ADD-    | OP_1SUB-    | OP_2MUL-    | OP_2DIV-    | OP_NEGATE-    | OP_ABS-    | OP_NOT-    | OP_0NOTEQUAL-    | OP_ADD-    | OP_SUB-    | OP_MUL-    | OP_DIV-    | OP_MOD-    | OP_LSHIFT-    | OP_RSHIFT-    | OP_BOOLAND-    | OP_BOOLOR-    | OP_NUMEQUAL-    | OP_NUMEQUALVERIFY-    | OP_NUMNOTEQUAL-    | OP_LESSTHAN-    | OP_GREATERTHAN-    | OP_LESSTHANOREQUAL-    | OP_GREATERTHANOREQUAL-    | OP_MIN-    | OP_MAX-    | OP_WITHIN-      -- Crypto-    | OP_RIPEMD160-    | OP_SHA1-    | OP_SHA256-    | OP_HASH160-    | OP_HASH256-    | OP_CODESEPARATOR-    | OP_CHECKSIG-    | OP_CHECKSIGVERIFY-    | OP_CHECKMULTISIG-    | OP_CHECKMULTISIGVERIFY-      -- Expansion-    | OP_NOP1-    | OP_NOP2-    | OP_NOP3-    | OP_NOP4-    | OP_NOP5-    | OP_NOP6-    | OP_NOP7-    | OP_NOP8-    | OP_NOP9-    | OP_NOP10-      -- Bitcoin Cash Nov 2018 hard fork-    | OP_CHECKDATASIG-    | OP_CHECKDATASIGVERIFY-      -- Other-    | OP_PUBKEYHASH-    | OP_PUBKEY-    | OP_INVALIDOPCODE !Word8-    deriving (Show, Read, Eq, Generic, Hashable, NFData)--instance Serialize ScriptOp where-    get = go =<< (fromIntegral <$> getWord8)-      where-        go op-            | op == 0x00 = return OP_0-            | op <= 0x4b = do-                payload <- getByteString (fromIntegral op)-                return $ OP_PUSHDATA payload OPCODE-            | op == 0x4c = do-                len  <- getWord8-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA1-            | op == 0x4d = do-                len  <- getWord16le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA2-            | op == 0x4e = do-                len  <- getWord32le-                payload <- getByteString (fromIntegral len)-                return $ OP_PUSHDATA payload OPDATA4--            | op == 0x4f = return OP_1NEGATE-            | op == 0x50 = return OP_RESERVED-            | op == 0x51 = return OP_1-            | op == 0x52 = return OP_2-            | op == 0x53 = return OP_3-            | op == 0x54 = return OP_4-            | op == 0x55 = return OP_5-            | op == 0x56 = return OP_6-            | op == 0x57 = return OP_7-            | op == 0x58 = return OP_8-            | op == 0x59 = return OP_9-            | op == 0x5a = return OP_10-            | op == 0x5b = return OP_11-            | op == 0x5c = return OP_12-            | op == 0x5d = return OP_13-            | op == 0x5e = return OP_14-            | op == 0x5f = return OP_15-            | op == 0x60 = return OP_16--            -- Flow control-            | op == 0x61 = return OP_NOP-            | op == 0x62 = return OP_VER        -- reserved-            | op == 0x63 = return OP_IF-            | op == 0x64 = return OP_NOTIF-            | op == 0x65 = return OP_VERIF      -- reserved-            | op == 0x66 = return OP_VERNOTIF   -- reserved-            | op == 0x67 = return OP_ELSE-            | op == 0x68 = return OP_ENDIF-            | op == 0x69 = return OP_VERIFY-            | op == 0x6a = return OP_RETURN--            -- Stack-            | op == 0x6b = return OP_TOALTSTACK-            | op == 0x6c = return OP_FROMALTSTACK-            | op == 0x6d = return OP_2DROP-            | op == 0x6e = return OP_2DUP-            | op == 0x6f = return OP_3DUP-            | op == 0x70 = return OP_2OVER-            | op == 0x71 = return OP_2ROT-            | op == 0x72 = return OP_2SWAP-            | op == 0x73 = return OP_IFDUP-            | op == 0x74 = return OP_DEPTH-            | op == 0x75 = return OP_DROP-            | op == 0x76 = return OP_DUP-            | op == 0x77 = return OP_NIP-            | op == 0x78 = return OP_OVER-            | op == 0x79 = return OP_PICK-            | op == 0x7a = return OP_ROLL-            | op == 0x7b = return OP_ROT-            | op == 0x7c = return OP_SWAP-            | op == 0x7d = return OP_TUCK--            -- Splice-            | op == 0x7e = return OP_CAT-            | op == 0x7f = return OP_SUBSTR-            | op == 0x80 = return OP_LEFT-            | op == 0x81 = return OP_RIGHT-            | op == 0x82 = return OP_SIZE--            -- Bitwise logic-            | op == 0x83 = return OP_INVERT-            | op == 0x84 = return OP_AND-            | op == 0x85 = return OP_OR-            | op == 0x86 = return OP_XOR-            | op == 0x87 = return OP_EQUAL-            | op == 0x88 = return OP_EQUALVERIFY-            | op == 0x89 = return OP_RESERVED1-            | op == 0x8a = return OP_RESERVED2--            -- Arithmetic-            | op == 0x8b = return OP_1ADD-            | op == 0x8c = return OP_1SUB-            | op == 0x8d = return OP_2MUL-            | op == 0x8e = return OP_2DIV-            | op == 0x8f = return OP_NEGATE-            | op == 0x90 = return OP_ABS-            | op == 0x91 = return OP_NOT-            | op == 0x92 = return OP_0NOTEQUAL-            | op == 0x93 = return OP_ADD-            | op == 0x94 = return OP_SUB-            | op == 0x95 = return OP_MUL-            | op == 0x96 = return OP_DIV-            | op == 0x97 = return OP_MOD-            | op == 0x98 = return OP_LSHIFT-            | op == 0x99 = return OP_RSHIFT-            | op == 0x9a = return OP_BOOLAND-            | op == 0x9b = return OP_BOOLOR-            | op == 0x9c = return OP_NUMEQUAL-            | op == 0x9d = return OP_NUMEQUALVERIFY-            | op == 0x9e = return OP_NUMNOTEQUAL-            | op == 0x9f = return OP_LESSTHAN-            | op == 0xa0 = return OP_GREATERTHAN-            | op == 0xa1 = return OP_LESSTHANOREQUAL-            | op == 0xa2 = return OP_GREATERTHANOREQUAL-            | op == 0xa3 = return OP_MIN-            | op == 0xa4 = return OP_MAX-            | op == 0xa5 = return OP_WITHIN--            -- Crypto-            | op == 0xa6 = return OP_RIPEMD160-            | op == 0xa7 = return OP_SHA1-            | op == 0xa8 = return OP_SHA256-            | op == 0xa9 = return OP_HASH160-            | op == 0xaa = return OP_HASH256-            | op == 0xab = return OP_CODESEPARATOR-            | op == 0xac = return OP_CHECKSIG-            | op == 0xad = return OP_CHECKSIGVERIFY-            | op == 0xae = return OP_CHECKMULTISIG-            | op == 0xaf = return OP_CHECKMULTISIGVERIFY--            -- More NOPs-            | op == 0xb0 = return OP_NOP1-            | op == 0xb1 = return OP_NOP2-            | op == 0xb2 = return OP_NOP3-            | op == 0xb3 = return OP_NOP4-            | op == 0xb4 = return OP_NOP5-            | op == 0xb5 = return OP_NOP6-            | op == 0xb6 = return OP_NOP7-            | op == 0xb7 = return OP_NOP8-            | op == 0xb8 = return OP_NOP9-            | op == 0xb9 = return OP_NOP10--            -- Bitcoin Cash Nov 2018 hard fork-            | op == 0xba = return OP_CHECKDATASIG-            | op == 0xbb = return OP_CHECKDATASIGVERIFY--            -- Constants-            | op == 0xfd = return OP_PUBKEYHASH-            | op == 0xfe = return OP_PUBKEY--            | otherwise = return $ OP_INVALIDOPCODE op--    put op = case op of--        (OP_PUSHDATA payload optype)-> do-            let len = B.length payload-            case optype of-                OPCODE -> do-                    unless (len <= 0x4b) $-                        error "OP_PUSHDATA OPCODE: Payload size too big"-                    putWord8 $ fromIntegral len-                OPDATA1 -> do-                    unless (len <= 0xff) $-                        error "OP_PUSHDATA OPDATA1: Payload size too big"-                    putWord8 0x4c-                    putWord8 $ fromIntegral len-                OPDATA2 -> do-                    unless (len <= 0xffff) $-                        error "OP_PUSHDATA OPDATA2: Payload size too big"-                    putWord8 0x4d-                    putWord16le $ fromIntegral len-                OPDATA4 -> do-                    unless (len <= 0x7fffffff) $-                        error "OP_PUSHDATA OPDATA4: Payload size too big"-                    putWord8 0x4e-                    putWord32le $ fromIntegral len-            putByteString payload--        -- Constants-        OP_0                 -> putWord8 0x00-        OP_1NEGATE           -> putWord8 0x4f-        OP_RESERVED          -> putWord8 0x50-        OP_1                 -> putWord8 0x51-        OP_2                 -> putWord8 0x52-        OP_3                 -> putWord8 0x53-        OP_4                 -> putWord8 0x54-        OP_5                 -> putWord8 0x55-        OP_6                 -> putWord8 0x56-        OP_7                 -> putWord8 0x57-        OP_8                 -> putWord8 0x58-        OP_9                 -> putWord8 0x59-        OP_10                -> putWord8 0x5a-        OP_11                -> putWord8 0x5b-        OP_12                -> putWord8 0x5c-        OP_13                -> putWord8 0x5d-        OP_14                -> putWord8 0x5e-        OP_15                -> putWord8 0x5f-        OP_16                -> putWord8 0x60--        -- Crypto Constants-        OP_PUBKEY            -> putWord8 0xfe-        OP_PUBKEYHASH        -> putWord8 0xfd--        -- Invalid Opcodes-        (OP_INVALIDOPCODE x) -> putWord8 x--        -- Flow Control-        OP_NOP               -> putWord8 0x61-        OP_VER               -> putWord8 0x62-        OP_IF                -> putWord8 0x63-        OP_NOTIF             -> putWord8 0x64-        OP_VERIF             -> putWord8 0x65-        OP_VERNOTIF          -> putWord8 0x66-        OP_ELSE              -> putWord8 0x67-        OP_ENDIF             -> putWord8 0x68-        OP_VERIFY            -> putWord8 0x69-        OP_RETURN            -> putWord8 0x6a--        -- Stack Operations-        OP_TOALTSTACK        -> putWord8 0x6b-        OP_FROMALTSTACK      -> putWord8 0x6c-        OP_2DROP             -> putWord8 0x6d-        OP_2DUP              -> putWord8 0x6e-        OP_3DUP              -> putWord8 0x6f-        OP_2OVER             -> putWord8 0x70-        OP_2ROT              -> putWord8 0x71-        OP_2SWAP             -> putWord8 0x72-        OP_IFDUP             -> putWord8 0x73-        OP_DEPTH             -> putWord8 0x74-        OP_DROP              -> putWord8 0x75-        OP_DUP               -> putWord8 0x76-        OP_NIP               -> putWord8 0x77-        OP_OVER              -> putWord8 0x78-        OP_PICK              -> putWord8 0x79-        OP_ROLL              -> putWord8 0x7a-        OP_ROT               -> putWord8 0x7b-        OP_SWAP              -> putWord8 0x7c-        OP_TUCK              -> putWord8 0x7d--        -- Splice-        OP_CAT               -> putWord8 0x7e-        OP_SUBSTR            -> putWord8 0x7f-        OP_LEFT              -> putWord8 0x80-        OP_RIGHT             -> putWord8 0x81-        OP_SIZE              -> putWord8 0x82--        -- Bitwise Logic-        OP_INVERT            -> putWord8 0x83-        OP_AND               -> putWord8 0x84-        OP_OR                -> putWord8 0x85-        OP_XOR               -> putWord8 0x86-        OP_EQUAL             -> putWord8 0x87-        OP_EQUALVERIFY       -> putWord8 0x88-        OP_RESERVED1         -> putWord8 0x89-        OP_RESERVED2         -> putWord8 0x8a--        -- Arithmetic-        OP_1ADD              -> putWord8 0x8b-        OP_1SUB              -> putWord8 0x8c-        OP_2MUL              -> putWord8 0x8d-        OP_2DIV              -> putWord8 0x8e-        OP_NEGATE            -> putWord8 0x8f-        OP_ABS               -> putWord8 0x90-        OP_NOT               -> putWord8 0x91-        OP_0NOTEQUAL         -> putWord8 0x92-        OP_ADD               -> putWord8 0x93-        OP_SUB               -> putWord8 0x94-        OP_MUL               -> putWord8 0x95-        OP_DIV               -> putWord8 0x96-        OP_MOD               -> putWord8 0x97-        OP_LSHIFT            -> putWord8 0x98-        OP_RSHIFT            -> putWord8 0x99-        OP_BOOLAND           -> putWord8 0x9a-        OP_BOOLOR            -> putWord8 0x9b-        OP_NUMEQUAL          -> putWord8 0x9c-        OP_NUMEQUALVERIFY    -> putWord8 0x9d-        OP_NUMNOTEQUAL       -> putWord8 0x9e-        OP_LESSTHAN          -> putWord8 0x9f-        OP_GREATERTHAN       -> putWord8 0xa0-        OP_LESSTHANOREQUAL   -> putWord8 0xa1-        OP_GREATERTHANOREQUAL-> putWord8 0xa2-        OP_MIN               -> putWord8 0xa3-        OP_MAX               -> putWord8 0xa4-        OP_WITHIN            -> putWord8 0xa5--        -- Crypto-        OP_RIPEMD160         -> putWord8 0xa6-        OP_SHA1              -> putWord8 0xa7-        OP_SHA256            -> putWord8 0xa8-        OP_HASH160           -> putWord8 0xa9-        OP_HASH256           -> putWord8 0xaa-        OP_CODESEPARATOR     -> putWord8 0xab-        OP_CHECKSIG          -> putWord8 0xac-        OP_CHECKSIGVERIFY    -> putWord8 0xad-        OP_CHECKMULTISIG     -> putWord8 0xae-        OP_CHECKMULTISIGVERIFY -> putWord8 0xaf--        -- More NOPs-        OP_NOP1              -> putWord8 0xb0-        OP_NOP2              -> putWord8 0xb1-        OP_NOP3              -> putWord8 0xb2-        OP_NOP4              -> putWord8 0xb3-        OP_NOP5              -> putWord8 0xb4-        OP_NOP6              -> putWord8 0xb5-        OP_NOP7              -> putWord8 0xb6-        OP_NOP8              -> putWord8 0xb7-        OP_NOP9              -> putWord8 0xb8-        OP_NOP10             -> putWord8 0xb9--        -- Bitcoin Cash Nov 2018 hard fork-        OP_CHECKDATASIG      -> putWord8 0xba-        OP_CHECKDATASIGVERIFY -> putWord8 0xbb----- | Check whether opcode is only data.-isPushOp :: ScriptOp -> Bool-isPushOp op = case op of-    OP_PUSHDATA _ _ -> True-    OP_0            -> True-    OP_1NEGATE      -> True-    OP_1            -> True-    OP_2            -> True-    OP_3            -> True-    OP_4            -> True-    OP_5            -> True-    OP_6            -> True-    OP_7            -> True-    OP_8            -> True-    OP_9            -> True-    OP_10           -> True-    OP_11           -> True-    OP_12           -> True-    OP_13           -> True-    OP_14           -> True-    OP_15           -> True-    OP_16           -> True-    _               -> False---- | Optimally encode data using one of the 4 types of data pushing opcodes.-opPushData :: ByteString -> ScriptOp-opPushData bs-    | len <= 0x4b       = OP_PUSHDATA bs OPCODE-    | len <= 0xff       = OP_PUSHDATA bs OPDATA1-    | len <= 0xffff     = OP_PUSHDATA bs OPDATA2-    | len <= 0xffffffff = OP_PUSHDATA bs OPDATA4-    | otherwise         = error "opPushData: payload size too big"-  where-    len = B.length bs---- | Transforms integers @[1 .. 16]@ to 'ScriptOp' @[OP_1 .. OP_16]@.-intToScriptOp :: Int -> ScriptOp-intToScriptOp i-    | i `elem` [1 .. 16] = op-    | otherwise = err-  where-    op = either (const err) id . S.decode . B.singleton . fromIntegral $ i + 0x50-    err = error $ "intToScriptOp: Invalid integer " ++ show i---- | Decode 'ScriptOp' @[OP_1 .. OP_16]@ to integers @[1 .. 16]@. This functions--- fails for other values of 'ScriptOp'-scriptOpToInt :: ScriptOp -> Either String Int-scriptOpToInt s-    | res `elem` [1..16] = return res-    | otherwise          = Left $ "scriptOpToInt: invalid opcode " ++ show s-  where-    res = fromIntegral (B.head $ S.encode s) - 0x50---- | Data type describing standard transaction output scripts. Output scripts--- provide the conditions that must be fulfilled for someone to spend the funds--- in a transaction output.-data ScriptOutput-      -- | pay to public key-    = PayPK { getOutputPubKey :: !PubKeyI }-      -- | pay to public key hash-    | PayPKHash { getOutputHash :: !Hash160 }-      -- | multisig-    | PayMulSig { getOutputMulSigKeys     :: ![PubKeyI]-                , getOutputMulSigRequired :: !Int }-      -- | pay to a script hash-    | PayScriptHash { getOutputHash :: !Hash160 }-      -- | pay to witness public key hash-    | PayWitnessPKHash { getOutputHash :: !Hash160 }-      -- | pay to witness script hash-    | PayWitnessScriptHash { getScriptHash :: !Hash256 }-      -- | provably unspendable data carrier-    | DataCarrier { getOutputData :: !ByteString }-    deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance FromJSON ScriptOutput where-    parseJSON = withText "scriptoutput" $ \t -> either fail return $-        maybeToEither "scriptoutput not hex" (decodeHex t) >>=-        decodeOutputBS--instance ToJSON ScriptOutput where-    toJSON = String . encodeHex . encodeOutputBS-    toEncoding = text . encodeHex . encodeOutputBS---- | Is script a pay-to-public-key output?-isPayPK :: ScriptOutput -> Bool-isPayPK (PayPK _) = True-isPayPK _         = False---- | Is script a pay-to-pub-key-hash output?-isPayPKHash :: ScriptOutput -> Bool-isPayPKHash (PayPKHash _) = True-isPayPKHash _             = False---- | Is script a pay-to-multi-sig output?-isPayMulSig :: ScriptOutput -> Bool-isPayMulSig (PayMulSig _ _) = True-isPayMulSig _               = False---- | Is script a pay-to-script-hash output?-isPayScriptHash :: ScriptOutput -> Bool-isPayScriptHash (PayScriptHash _) = True-isPayScriptHash _                 = False---- | Is script a pay-to-witness-pub-key-hash output?-isPayWitnessPKHash :: ScriptOutput -> Bool-isPayWitnessPKHash (PayWitnessPKHash _) = True-isPayWitnessPKHash _                    = False---- | Is script a pay-to-witness-script-hash output?-isPayWitnessScriptHash :: ScriptOutput -> Bool-isPayWitnessScriptHash (PayWitnessScriptHash _) = True-isPayWitnessScriptHash _                        = False---- | Is script a data carrier output?-isDataCarrier :: ScriptOutput -> Bool-isDataCarrier (DataCarrier _) = True-isDataCarrier _               = False---- | Tries to decode a 'ScriptOutput' from a 'Script'. This can fail if the--- script is not recognized as any of the standard output types.-decodeOutput :: Script -> Either String ScriptOutput-decodeOutput s = case scriptOps s of-    -- Pay to PubKey-    [OP_PUSHDATA bs _, OP_CHECKSIG] -> PayPK <$> S.decode bs-    -- Pay to PubKey Hash-    [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] ->-        PayPKHash <$> S.decode bs-    -- Pay to Script Hash-    [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] ->-        PayScriptHash <$> S.decode  bs-    -- Pay to Witness-    [OP_0, OP_PUSHDATA bs OPCODE]-      | B.length bs == 20 -> PayWitnessPKHash     <$> S.decode bs-      | B.length bs == 32 -> PayWitnessScriptHash <$> S.decode bs-    -- Provably unspendable data carrier output-    [OP_RETURN, OP_PUSHDATA bs _] -> Right $ DataCarrier bs-    -- Pay to MultiSig Keys-    _ -> matchPayMulSig s---- | Similar to 'decodeOutput' but decodes from a 'ByteString'.-decodeOutputBS :: ByteString -> Either String ScriptOutput-decodeOutputBS = decodeOutput <=< S.decode---- | Computes a 'Script' from a standard 'ScriptOutput'.-encodeOutput :: ScriptOutput -> Script-encodeOutput s = Script $ case s of-    -- Pay to PubKey-    (PayPK k) -> [opPushData $ S.encode k, OP_CHECKSIG]-    -- Pay to PubKey Hash Address-    (PayPKHash h) ->-        [ OP_DUP, OP_HASH160, opPushData $ S.encode h, OP_EQUALVERIFY, OP_CHECKSIG]-    -- Pay to MultiSig Keys-    (PayMulSig ps r)-      | r <= length ps ->-        let opM = intToScriptOp r-            opN = intToScriptOp $ length ps-            keys = map (opPushData . S.encode) ps-            in opM : keys ++ [opN, OP_CHECKMULTISIG]-      | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"-    -- Pay to Script Hash Address-    (PayScriptHash h) ->-        [ OP_HASH160, opPushData $ S.encode h, OP_EQUAL]-    -- Pay to Witness PubKey Hash Address-    (PayWitnessPKHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    (PayWitnessScriptHash h) ->-        [ OP_0, opPushData $ S.encode h ]-    -- Provably unspendable output-    (DataCarrier d) -> [OP_RETURN, opPushData d]---- | Similar to 'encodeOutput' but encodes to a ByteString-encodeOutputBS :: ScriptOutput -> ByteString-encodeOutputBS = S.encode . encodeOutput---- | Encode script as pay-to-script-hash script-toP2SH :: Script -> ScriptOutput-toP2SH = PayScriptHash . addressHash . S.encode---- | Encode script as a pay-to-witness-script-hash script-toP2WSH :: Script -> ScriptOutput-toP2WSH = PayWitnessScriptHash . sha256 . S.encode---- | Match @[OP_N, PubKey1, ..., PubKeyM, OP_M, OP_CHECKMULTISIG]@-matchPayMulSig :: Script -> Either String ScriptOutput-matchPayMulSig (Script ops) = case splitAt (length ops - 2) ops of-    (m:xs,[n,OP_CHECKMULTISIG]) -> do-        (intM,intN) <- liftM2 (,) (scriptOpToInt m) (scriptOpToInt n)-        if intM <= intN && length xs == intN-            then liftM2 PayMulSig (go xs) (return intM)-            else Left "matchPayMulSig: Invalid M or N parameters"-    _ -> Left "matchPayMulSig: script did not match output template"-  where-    go (OP_PUSHDATA bs _:xs) = liftM2 (:) (S.decode bs) (go xs)-    go []                    = return []-    go  _                    = Left "matchPayMulSig: invalid multisig opcode"
− src/Network/Haskoin/Script/SigHash.hs
@@ -1,319 +0,0 @@-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE OverloadedStrings          #-}-{-|-Module      : Network.Haskoin.Script.SigHash-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Transaction signatures and related functions.--}-module Network.Haskoin.Script.SigHash-( SigHash-, SigHashFlag(..)-, sigHashAll-, sigHashNone-, sigHashSingle-, hasAnyoneCanPayFlag-, hasForkIdFlag-, setAnyoneCanPayFlag-, setForkIdFlag-, isSigHashAll-, isSigHashNone-, isSigHashSingle-, isSigHashUnknown-, sigHashAddForkId-, sigHashGetForkId-, sigHashAddNetworkId-, txSigHash-, txSigHashForkId-, TxSignature(..)-, encodeTxSig-, decodeTxSig-) where--import           Control.DeepSeq-import           Control.Monad-import           Crypto.Secp256k1-import qualified Data.Aeson                         as J-import           Data.Bits-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as BS-import           Data.Hashable-import           Data.Maybe-import           Data.Scientific-import           Data.Serialize-import           Data.Word-import           GHC.Generics                       (Generic)-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Crypto.Signature-import           Network.Haskoin.Network-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util---- | Constant representing a SIGHASH flag that controls what is being signed.-data SigHashFlag-    = SIGHASH_ALL-      -- ^ sign all outputs-    | SIGHASH_NONE-      -- ^ sign no outputs-    | SIGHASH_SINGLE-      -- ^ sign the output index corresponding to the input-    | SIGHASH_FORKID-      -- ^ replay protection for Bitcoin Cash transactions-    | SIGHASH_ANYONECANPAY-      -- ^ new inputs can be added-    deriving (Eq, Ord, Show, Read, Generic)--instance NFData SigHashFlag--instance Hashable SigHashFlag--instance Enum SigHashFlag where-    fromEnum SIGHASH_ALL          = 0x01-    fromEnum SIGHASH_NONE         = 0x02-    fromEnum SIGHASH_SINGLE       = 0x03-    fromEnum SIGHASH_FORKID       = 0x40-    fromEnum SIGHASH_ANYONECANPAY = 0x80-    toEnum 0x01 = SIGHASH_ALL-    toEnum 0x02 = SIGHASH_NONE-    toEnum 0x03 = SIGHASH_SINGLE-    toEnum 0x40 = SIGHASH_FORKID-    toEnum 0x80 = SIGHASH_ANYONECANPAY-    toEnum _    = error "Not a valid sighash flag"---- | Data type representing the different ways a transaction can be signed.--- When producing a signature, a hash of the transaction is used as the message--- to be signed. The 'SigHash' parameter controls which parts of the--- transaction are used or ignored to produce the transaction hash. The idea is--- that if some part of a transaction is not used to produce the transaction--- hash, then you can change that part of the transaction after producing a--- signature without invalidating that signature.------ If the 'SIGHASH_ANYONECANPAY' flag is set (true), then only the current input--- is signed. Otherwise, all of the inputs of a transaction are signed. The--- default value for 'SIGHASH_ANYONECANPAY' is unset (false).-newtype SigHash =-    SigHash Word32-    deriving ( Eq-             , Ord-             , Bits-             , Enum-             , Integral-             , Num-             , Real-             , Show-             , Read-             , Generic-             , Hashable-             , NFData-             )--instance J.FromJSON SigHash where-    parseJSON =-        J.withScientific "sighash" $-        maybe mzero (return . SigHash) . toBoundedInteger--instance J.ToJSON SigHash where-    toJSON = J.Number . fromIntegral-    toEncoding (SigHash n) = J.toEncoding n---- | SIGHASH_NONE as a byte.-sigHashNone :: SigHash-sigHashNone = fromIntegral $ fromEnum SIGHASH_NONE---- | SIGHASH_ALL as a byte.-sigHashAll :: SigHash-sigHashAll = fromIntegral $ fromEnum SIGHASH_ALL---- | SIGHASH_SINGLE as a byte.-sigHashSingle :: SigHash-sigHashSingle = fromIntegral $ fromEnum SIGHASH_SINGLE---- | SIGHASH_FORKID as a byte.-sigHashForkId :: SigHash-sigHashForkId = fromIntegral $ fromEnum SIGHASH_FORKID---- | SIGHASH_ANYONECANPAY as a byte.-sigHashAnyoneCanPay :: SigHash-sigHashAnyoneCanPay = fromIntegral $ fromEnum SIGHASH_ANYONECANPAY---- | Set SIGHASH_FORKID flag.-setForkIdFlag :: SigHash -> SigHash-setForkIdFlag = (.|. sigHashForkId)---- | Set SIGHASH_ANYONECANPAY flag.-setAnyoneCanPayFlag :: SigHash -> SigHash-setAnyoneCanPayFlag = (.|. sigHashAnyoneCanPay)---- | Is the SIGHASH_FORKID flag set?-hasForkIdFlag :: SigHash -> Bool-hasForkIdFlag = (/= 0) . (.&. sigHashForkId)---- | Is the SIGHASH_ANYONECANPAY flag set?-hasAnyoneCanPayFlag :: SigHash -> Bool-hasAnyoneCanPayFlag = (/= 0) . (.&. sigHashAnyoneCanPay)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_ALL'.-isSigHashAll :: SigHash -> Bool-isSigHashAll = (== sigHashAll) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_NONE'.-isSigHashNone :: SigHash -> Bool-isSigHashNone = (== sigHashNone) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_SINGLE'.-isSigHashSingle :: SigHash -> Bool-isSigHashSingle = (== sigHashSingle) . (.&. 0x1f)---- | Returns 'True' if the 'SigHash' has the value 'SIGHASH_UNKNOWN'.-isSigHashUnknown :: SigHash -> Bool-isSigHashUnknown =-    (`notElem` [sigHashAll, sigHashNone, sigHashSingle]) . (.&. 0x1f)---- | Add a fork id to a 'SigHash'.-sigHashAddForkId :: SigHash -> Word32 -> SigHash-sigHashAddForkId sh w = (fromIntegral w `shiftL` 8) .|. (sh .&. 0x000000ff)---- | Add fork id of a particular network to a 'SigHash'.-sigHashAddNetworkId :: Network -> SigHash -> SigHash-sigHashAddNetworkId net =-    (`sigHashAddForkId` fromMaybe 0 (getSigHashForkId net))---- | Get fork id from 'SigHash'.-sigHashGetForkId :: SigHash -> Word32-sigHashGetForkId (SigHash n) = fromIntegral $ n `shiftR` 8---- | Computes the hash that will be used for signing a transaction.-txSigHash :: Network-          -> Tx      -- ^ transaction to sign-          -> Script  -- ^ script from output being spent-          -> Word64  -- ^ value of output being spent-          -> Int     -- ^ index of input being signed-          -> SigHash -- ^ what to sign-          -> Hash256 -- ^ hash to be signed-txSigHash net tx out v i sh-    | hasForkIdFlag sh && isJust (getSigHashForkId net) =-        txSigHashForkId net tx out v i sh-    | otherwise = do-        let newIn = buildInputs (txIn tx) fout i sh-        -- When SigSingle and input index > outputs, then sign integer 1-        fromMaybe one $ do-            newOut <- buildOutputs (txOut tx) i sh-            let newTx = Tx (txVersion tx) newIn newOut [] (txLockTime tx)-            return $-                doubleSHA256 $-                runPut $ do-                    put newTx-                    putWord32le $ fromIntegral sh-  where-    fout = Script $ filter (/= OP_CODESEPARATOR) $ scriptOps out-    one = "0100000000000000000000000000000000000000000000000000000000000000"---- | Build transaction inputs for computing sighashes.-buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn]-buildInputs txins out i sh-    | hasAnyoneCanPayFlag sh =-        [ (txins !! i) { scriptInput = encode out } ]-    | isSigHashAll sh || isSigHashUnknown sh = single-    | otherwise = zipWith noSeq single [0 ..]-  where-    emptyIn = map (\ti -> ti { scriptInput = BS.empty }) txins-    single =-        updateIndex i emptyIn $ \ti -> ti { scriptInput = encode out }-    noSeq ti j =-        if i == j-        then ti-        else ti { txInSequence = 0 }---- | Build transaction outputs for computing sighashes.-buildOutputs :: [TxOut] -> Int -> SigHash -> Maybe [TxOut]-buildOutputs txos i sh-    | isSigHashAll sh || isSigHashUnknown sh = return txos-    | isSigHashNone sh = return []-    | i >= length txos = Nothing-    | otherwise = return $ buffer ++ [txos !! i]-  where-    buffer = replicate i $ TxOut maxBound BS.empty---- | Compute the hash that will be used for signing a transaction. This--- function is used when the 'SIGHASH_FORKID' flag is set.-txSigHashForkId-    :: Network-    -> Tx      -- ^ transaction to sign-    -> Script  -- ^ script from output being spent-    -> Word64  -- ^ value of output being spent-    -> Int     -- ^ index of input being signed-    -> SigHash -- ^ what to sign-    -> Hash256 -- ^ hash to be signed-txSigHashForkId net tx out v i sh =-    doubleSHA256 . runPut $ do-        putWord32le $ txVersion tx-        put hashPrevouts-        put hashSequence-        put $ prevOutput $ txIn tx !! i-        putScript out-        putWord64le v-        putWord32le $ txInSequence $ txIn tx !! i-        put hashOutputs-        putWord32le $ txLockTime tx-        putWord32le $ fromIntegral $ sigHashAddNetworkId net sh-  where-    hashPrevouts-        | not $ hasAnyoneCanPayFlag sh =-            doubleSHA256 $ runPut $ mapM_ (put . prevOutput) $ txIn tx-        | otherwise = zeros-    hashSequence-        | not (hasAnyoneCanPayFlag sh) &&-              not (isSigHashSingle sh) && not (isSigHashNone sh) =-            doubleSHA256 $ runPut $ mapM_ (putWord32le . txInSequence) $ txIn tx-        | otherwise = zeros-    hashOutputs-        | not (isSigHashSingle sh) && not (isSigHashNone sh) =-            doubleSHA256 $ runPut $ mapM_ put $ txOut tx-        | isSigHashSingle sh && i < length (txOut tx) =-            doubleSHA256 $ encode $ txOut tx !! i-        | otherwise = zeros-    putScript s = do-        let encodedScript = encode s-        putVarInt $ BS.length encodedScript-        putByteString encodedScript-    zeros :: Hash256-    zeros = "0000000000000000000000000000000000000000000000000000000000000000"---- | Data type representing a signature together with a 'SigHash'. The 'SigHash'--- is serialized as one byte at the end of an ECDSA 'Sig'. All signatures in--- transaction inputs are of type 'TxSignature'.-data TxSignature-    = TxSignature { txSignature        :: !Sig-                  , txSignatureSigHash :: !SigHash-                  }-    | TxSignatureEmpty-    deriving (Eq, Show, Generic)--instance NFData TxSignature---- | Serialize a 'TxSignature'.-encodeTxSig :: TxSignature -> ByteString-encodeTxSig TxSignatureEmpty = error "Can not encode an empty signature"-encodeTxSig (TxSignature sig (SigHash n)) =-    runPut $ putSig sig >> putWord8 (fromIntegral n)---- | Deserialize a 'TxSignature'.-decodeTxSig :: Network -> ByteString -> Either String TxSignature-decodeTxSig _   bs | BS.null bs = Left "Empty signature candidate"-decodeTxSig net bs =-    case decodeStrictSig $ BS.init bs of-        Just sig -> do-            let sh = fromIntegral $ BS.last bs-            when (isSigHashUnknown sh) $-                Left "Non-canonical signature: unknown hashtype byte"-            when (isNothing (getSigHashForkId net) && hasForkIdFlag sh) $-                Left "Non-canonical signature: invalid network for forkId"-            return $ TxSignature sig sh-        Nothing -> Left "Non-canonical signature: could not parse signature"
− src/Network/Haskoin/Script/Standard.hs
@@ -1,178 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Script.Standard-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Standard scripts like pay-to-public-key, pay-to-public-key-hash,-pay-to-script-hash, pay-to-multisig and corresponding SegWit variants.--}-module Network.Haskoin.Script.Standard-    ( ScriptInput(..)-    , SimpleInput(..)-    , RedeemScript-    , encodeInput-    , encodeInputBS-    , decodeInput-    , decodeInputBS-    , sortMulSig-    , scriptOpToInt-    , isSpendPK-    , isSpendPKHash-    , isSpendMulSig-    , isScriptHashInput-    ) where--import           Control.Applicative            ((<|>))-import           Control.DeepSeq-import           Control.Monad                  (guard, (<=<))-import           Data.ByteString                (ByteString)-import           Data.Function                  (on)-import           Data.List                      (sortBy)-import           Data.Serialize                 (decode, encode)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Script.SigHash-import           Network.Haskoin.Util----- | Data type describing standard transaction input scripts. Input scripts--- provide the signing data required to unlock the coins of the output they are--- trying to spend, except in pay-to-witness-public-key-hash and--- pay-to-script-hash transactions.-data SimpleInput-      -- | spend pay-to-public-key output-    = SpendPK { getInputSig :: !TxSignature-                  -- ^ transaction signature-               }-      -- | spend pay-to-public-key-hash output-    | SpendPKHash { getInputSig :: !TxSignature-                      -- ^ embedded signature-                  , getInputKey :: !PubKeyI-                      -- ^ public key-                   }-      -- | spend multisig output-    | SpendMulSig { getInputMulSigKeys :: ![TxSignature]-                      -- ^ list of signatures-                   }-    deriving (Eq, Show, Generic, NFData)---- | Returns true if the input script is spending from a pay-to-public-key--- output.-isSpendPK :: ScriptInput -> Bool-isSpendPK (RegularInput (SpendPK _)) = True-isSpendPK _                          = False---- | Returns true if the input script is spending from a pay-to-public-key-hash--- output.-isSpendPKHash :: ScriptInput -> Bool-isSpendPKHash (RegularInput (SpendPKHash _ _)) = True-isSpendPKHash _                                = False---- | Returns true if the input script is spending a multisig output.-isSpendMulSig :: ScriptInput -> Bool-isSpendMulSig (RegularInput (SpendMulSig _)) = True-isSpendMulSig _                              = False---- | Returns true if the input script is spending a pay-to-script-hash output.-isScriptHashInput :: ScriptInput -> Bool-isScriptHashInput (ScriptHashInput _ _) = True-isScriptHashInput _                     = False---- | A redeem script is the output script serialized into the spending input--- script. It must be included in inputs that spend pay-to-script-hash outputs.-type RedeemScript = ScriptOutput---- | Standard input script high-level representation.-data ScriptInput-    = RegularInput-          { getRegularInput :: !SimpleInput-            -- ^ get wrapped simple input-          }-    | ScriptHashInput-          { getScriptHashInput  :: !SimpleInput-            -- ^ get simple input associated with redeem script-          , getScriptHashRedeem :: !RedeemScript-            -- ^ redeem script-          }-    deriving (Eq, Show, Generic, NFData)---- | Heuristic to decode an input script into one of the standard types.-decodeSimpleInput :: Network -> Script -> Either String SimpleInput-decodeSimpleInput net (Script ops) =-    maybeToEither errMsg $ matchPK ops <|> matchPKHash ops <|> matchMulSig ops-  where-    matchPK [op] = SpendPK <$> f op-    matchPK _    = Nothing-    matchPKHash [op, OP_PUSHDATA pub _] =-        SpendPKHash <$> f op <*> eitherToMaybe (decode pub)-    matchPKHash _ = Nothing-    matchMulSig (x:xs) = do-        guard $ x == OP_0-        SpendMulSig <$> mapM f xs-    matchMulSig _ = Nothing-    f OP_0                    = return TxSignatureEmpty-    f (OP_PUSHDATA "" OPCODE) = f OP_0-    f (OP_PUSHDATA bs _)      = eitherToMaybe $ decodeTxSig net bs-    f _                       = Nothing-    errMsg = "decodeInput: Could not decode script input"---- | Heuristic to decode a 'ScriptInput' from a 'Script'. This function fails if--- the script can not be parsed as a standard script input.-decodeInput :: Network -> Script -> Either String ScriptInput-decodeInput net s@(Script ops) =-    maybeToEither errMsg $ matchSimpleInput <|> matchPayScriptHash-  where-    matchSimpleInput =-        RegularInput <$> eitherToMaybe (decodeSimpleInput net s)-    matchPayScriptHash =-        case splitAt (length (scriptOps s) - 1) ops of-            (is, [OP_PUSHDATA bs _]) -> do-                rdm <- eitherToMaybe $ decodeOutputBS bs-                inp <- eitherToMaybe $ decodeSimpleInput net $ Script is-                return $ ScriptHashInput inp rdm-            _ -> Nothing-    errMsg = "decodeInput: Could not decode script input"---- | Like 'decodeInput' but decodes directly from a serialized script--- 'ByteString'.-decodeInputBS :: Network -> ByteString -> Either String ScriptInput-decodeInputBS net = decodeInput net <=< decode---- | Encode a standard input into a script.-encodeInput :: ScriptInput -> Script-encodeInput s = case s of-    RegularInput ri -> encodeSimpleInput ri-    ScriptHashInput i o -> Script $-        scriptOps (encodeSimpleInput i) ++ [opPushData $ encodeOutputBS o]---- | Similar to 'encodeInput' but encodes directly to a serialized script--- 'ByteString'.-encodeInputBS :: ScriptInput -> ByteString-encodeInputBS = encode . encodeInput---- | Encode a standard 'SimpleInput' into opcodes as an input 'Script'.-encodeSimpleInput :: SimpleInput -> Script-encodeSimpleInput s =-    Script $-    case s of-        SpendPK ts       -> [f ts]-        SpendPKHash ts p -> [f ts, opPushData $ encode p]-        SpendMulSig xs   -> OP_0 : map f xs-  where-    f TxSignatureEmpty = OP_0-    f ts               = opPushData $ encodeTxSig ts---- | Sort the public keys of a multisig output in ascending order by comparing--- their compressed serialized representations. Refer to BIP-67.-sortMulSig :: ScriptOutput -> ScriptOutput-sortMulSig out = case out of-    PayMulSig keys r -> PayMulSig (sortBy (compare `on` encode) keys) r-    _ -> error "Can only call orderMulSig on PayMulSig scripts"
− src/Network/Haskoin/Transaction.hs
@@ -1,24 +0,0 @@-{-|-Module      : Network.Haskoin.Transaction-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Transactions and related code.--}-module Network.Haskoin.Transaction-    ( module Common-    -- * Builder-    , module Builder-    -- * Segwit-    , module Segwit-    -- * Partial-    , module Partial-    ) where--import           Network.Haskoin.Transaction.Builder as Builder-import           Network.Haskoin.Transaction.Common  as Common-import           Network.Haskoin.Transaction.Partial as Partial-import           Network.Haskoin.Transaction.Segwit  as Segwit
− src/Network/Haskoin/Transaction/Builder.hs
@@ -1,472 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Builder-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Code to simplify transaction creation, signing, fee calculation and coin-selection.--}-module Network.Haskoin.Transaction.Builder-    ( -- * Transaction Creation & Signing-      buildAddrTx-    , buildTx-    , buildInput-    , SigInput(..)-    , signTx-    , signNestedWitnessTx-    , makeSignature-    , signInput-    , signNestedInput-    , verifyStdTx-    , mergeTxs-    , sigKeys-    , mergeTxInput-    , findSigInput-    , verifyStdInput-      -- * Coin Selection-    , Coin(..)-    , chooseCoins-    , chooseCoinsSink-    , chooseMSCoins-    , chooseMSCoinsSink-    , countMulSig-    , greedyAddSink-    , guessTxFee-    , guessMSTxFee-    , guessTxSize-    , guessMSSize-    ) where--import           Control.Applicative                      ((<|>))-import           Control.Arrow                            (first)-import           Control.Monad                            (foldM, unless)-import           Control.Monad.Identity                   (runIdentity)-import           Crypto.Secp256k1-import qualified Data.ByteString                          as B-import           Data.Conduit                             (ConduitT, Void,-                                                           await, runConduit,-                                                           (.|))-import           Data.Conduit.List                        (sourceList)-import           Data.Either                              (fromRight)-import           Data.List                                (nub)-import           Data.Maybe                               (catMaybes, fromJust,-                                                           isJust)-import           Data.Serialize                           (decode, encode)-import           Data.String.Conversions                  (cs)-import           Data.Text                                (Text)-import           Data.Word                                (Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto.Hash              (Hash256, addressHash)-import           Network.Haskoin.Crypto.Signature-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Transaction.Builder.Sign (SigInput (..),-                                                           buildInput,-                                                           makeSignature,-                                                           sigKeys)-import qualified Network.Haskoin.Transaction.Builder.Sign as S-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Transaction.Segwit       (decodeWitnessInput,-                                                           isSegwit,-                                                           viewWitnessProgram)-import           Network.Haskoin.Util---- | Any type can be used as a Coin if it can provide a value in Satoshi.--- The value is used in coin selection algorithms.-class Coin c where-    coinValue :: c -> Word64---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account.-chooseCoins :: Coin c-            => Word64 -- ^ value to send-            -> Word64 -- ^ fee per byte-            -> Int    -- ^ number of outputs (including change)-            -> Bool   -- ^ try to find better solutions-            -> [c]    -- ^ list of ordered coins to choose from-            -> Either String ([c], Word64)-            -- ^ coin selection and change-chooseCoins target fee nOut continue coins =-    runIdentity . runConduit $-    sourceList coins .| chooseCoinsSink target fee nOut continue---- | Coin selection algorithm for normal (non-multisig) transactions. This--- function returns the selected coins together with the amount of change to--- send back to yourself, taking the fee into account. This version uses a Sink--- for conduit-based coin selection.-chooseCoinsSink :: (Monad m, Coin c)-                => Word64 -- ^ value to send-                -> Word64 -- ^ fee per byte-                -> Int    -- ^ number of outputs (including change)-                -> Bool   -- ^ try to find better solution-                -> ConduitT c Void m (Either String ([c], Word64))-                -- ^ coin selection and change-chooseCoinsSink target fee nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessTxFee fee nOut) continue-    | otherwise = return $ Left "chooseCoins: Target must be > 0"-  where-    err = "chooseCoins: No solution found"---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address.-chooseMSCoins :: Coin c-              => Word64     -- ^ value to send-              -> Word64     -- ^ fee per byte-              -> (Int, Int) -- ^ m of n multisig-              -> Int        -- ^ number of outputs (including change)-              -> Bool       -- ^ try to find better solution-              -> [c]-              -> Either String ([c], Word64)-              -- ^ coin selection change amount-chooseMSCoins target fee ms nOut continue coins =-    runIdentity . runConduit $-        sourceList coins .| chooseMSCoinsSink target fee ms nOut continue---- | Coin selection algorithm for multisig transactions. This function returns--- the selected coins together with the amount of change to send back to--- yourself, taking the fee into account. This function assumes all the coins--- are script hash outputs that send funds to a multisignature address. This--- version uses a Sink if you need conduit-based coin selection.-chooseMSCoinsSink :: (Monad m, Coin c)-                  => Word64     -- ^ value to send-                  -> Word64     -- ^ fee per byte-                  -> (Int, Int) -- ^ m of n multisig-                  -> Int        -- ^ number of outputs (including change)-                  -> Bool       -- ^ try to find better solution-                  -> ConduitT c Void m (Either String ([c], Word64))-                  -- ^ coin selection and change-chooseMSCoinsSink target fee ms nOut continue-    | target > 0 =-        maybeToEither err <$>-            greedyAddSink target (guessMSTxFee fee ms nOut) continue-    | otherwise = return $ Left "chooseMSCoins: Target must be > 0"-  where-    err = "chooseMSCoins: No solution found"---- | Select coins greedily by starting from an empty solution. If the 'continue'--- flag is set, the algorithm will try to find a better solution in the stream--- after a solution is found. If the next solution found is not strictly better--- than the previously found solution, the algorithm stops and returns the--- previous solution. If the continue flag is not set, the algorithm will return--- the first solution it finds in the stream.-greedyAddSink :: (Monad m, Coin c)-              => Word64          -- ^ value to send-              -> (Int -> Word64) -- ^ coin count to fee function-              -> Bool            -- ^ try to find better solutions-              -> ConduitT c Void m (Maybe ([c], Word64))-              -- ^ coin selection and change-greedyAddSink target guessFee continue =-    go [] 0 [] 0-  where-    -- The goal is the value we must reach (including the fee) for a certain-    -- amount of selected coins.-    goal c = target + guessFee c-    go acc aTot ps pTot = await >>= \case-        -- A coin is available in the stream-        Just coin -> do-            let val = coinValue coin-            -- We have reached the goal using this coin-            if val + aTot >= goal (length acc + 1)-                -- If we want to continue searching for better solutions-                then if continue-                    -- This solution is the first one or-                    -- This solution is better than the previous one-                    then if pTot == 0 || val + aTot < pTot-                        -- Continue searching for better solutions in the stream-                        then go [] 0 (coin:acc) (val + aTot)-                        -- Otherwise, we stop here and return the previous-                        -- solution-                        else return $ Just (ps, pTot - goal (length ps))-                    -- Otherwise, return this solution-                    else return $-                        Just (coin : acc, val + aTot - goal (length acc + 1))-                -- We have not yet reached the goal. Add the coin to the-                -- accumulator-                else go (coin:acc) (val + aTot) ps pTot-        -- We reached the end of the stream-        Nothing ->-            return $ if null ps-                -- If no solution was found, return Nothing-                then Nothing-                -- If we have a solution, return it-                else Just (ps, pTot - goal (length ps))---- | Estimate tranasction fee to pay based on transaction size estimation.-guessTxFee :: Word64 -> Int -> Int -> Word64-guessTxFee byteFee nOut nIn =-    byteFee * fromIntegral (guessTxSize nIn [] nOut 0)---- | Same as 'guessTxFee' but for multisig transactions.-guessMSTxFee :: Word64 -> (Int, Int) -> Int -> Int -> Word64-guessMSTxFee byteFee ms nOut nIn =-    byteFee * fromIntegral (guessTxSize 0 (replicate nIn ms) nOut 0)---- | Computes an upper bound on the size of a transaction based on some known--- properties of the transaction.-guessTxSize :: Int         -- ^ number of regular transaction inputs-            -> [(Int,Int)] -- ^ multisig m of n for each input-            -> Int         -- ^ number of P2PKH outputs-            -> Int         -- ^ number of P2SH outputs-            -> Int         -- ^ upper bound on transaction size-guessTxSize pki msi pkout msout =-    8 + inpLen + inp + outLen + out-  where-    inpLen = B.length $ encode $ VarInt $ fromIntegral $ length msi + pki-    outLen = B.length $ encode $ VarInt $ fromIntegral $ pkout + msout-    inp = pki * 148 + sum (map guessMSSize msi)-             -- (20: hash160) + (5: opcodes) +-             -- (1: script len) + (8: Word64)-    out =-        pkout * 34 +-             -- (20: hash160) + (3: opcodes) +-             -- (1: script len) + (8: Word64)-        msout * 32---- | Size of a multisig P2SH input.-guessMSSize :: (Int,Int) -> Int-guessMSSize (m, n)-    -- OutPoint (36) + Sequence (4) + Script- = 40 + fromIntegral (B.length $ encode $ VarInt $ fromIntegral scp) + scp-    -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG-  where-    rdm =-        fromIntegral $-        B.length $ encode $ opPushData $ B.replicate (n * 34 + 3) 0-    -- Redeem + m*sig + OP_0-    scp = rdm + m * 73 + 1--{- Build a new Tx -}---- | Build a transaction by providing a list of outpoints as inputs--- and a list of recipient addresses and amounts as outputs.-buildAddrTx :: Network -> [OutPoint] -> [(Text, Word64)] -> Either String Tx-buildAddrTx net xs ys = buildTx xs =<< mapM f ys-  where-    f (s, v) =-        maybe (Left ("buildAddrTx: Invalid address " ++ cs s)) Right $ do-            a <- stringToAddr net s-            let o = addressToOutput a-            return (o, v)---- | Build a transaction by providing a list of outpoints as inputs--- and a list of 'ScriptOutput' and amounts as outputs.-buildTx :: [OutPoint] -> [(ScriptOutput, Word64)] -> Either String Tx-buildTx xs ys =-    mapM fo ys >>= \os -> return $ Tx 1 (map fi xs) os [] 0-  where-    fi outPoint = TxIn outPoint B.empty maxBound-    fo (o, v)-        | v <= 2100000000000000 = return $ TxOut v $ encodeOutputBS o-        | otherwise =-            Left $ "buildTx: Invalid amount " ++ show v---- | Sign a transaction by providing the 'SigInput' signing parameters and a--- list of private keys. The signature is computed deterministically as defined--- in RFC-6979.------ Example: P2SH-P2WKH------ > sigIn = SigInput (PayWitnessPKHash h) 100000 op sigHashAll Nothing--- > signedTx = signTx btc unsignedTx [sigIn] [key]------ Example: P2SH-P2WSH multisig------ > sigIn = SigInput (PayWitnessScriptHash h) 100000 op sigHashAll (Just $ PayMulSig [p1,p2,p3] 2)--- > signedTx = signTx btc unsignedTx [sigIn] [k1,k3]-signTx :: Network-       -> Tx               -- ^ transaction to sign-       -> [SigInput]       -- ^ signing parameters-       -> [SecKey]         -- ^ private keys to sign with-       -> Either String Tx -- ^ signed transaction-signTx net tx si = S.signTx net tx $ notNested <$> si-  where notNested s = (s, False)---- | This function differs from 'signTx' by assuming all segwit inputs are--- P2SH-nested.  Use the same signing parameters for segwit inputs as in 'signTx'.-signNestedWitnessTx :: Network-                    -> Tx               -- ^ transaction to sign-                    -> [SigInput]       -- ^ signing parameters-                    -> [SecKey]         -- ^ private keys to sign with-                    -> Either String Tx -- ^ signed transaction-signNestedWitnessTx net tx si = S.signTx net tx $ nested <$> si-  where-    -- NOTE: the nesting flag is ignored for non-segwit inputs-    nested s = (s, True)----- | Sign a single input in a transaction deterministically (RFC-6979).-signInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signInput net tx i si = S.signInput net tx i (si, False)---- | Like 'signInput' but treat segwit inputs as nested-signNestedInput :: Network -> Tx -> Int -> SigInput -> SecKeyI -> Either String Tx-signNestedInput net tx i si = S.signInput net tx i (si, True)---- | Order the 'SigInput' with respect to the transaction inputs. This allows--- the user to provide the 'SigInput' in any order. Users can also provide only--- a partial set of 'SigInput' entries.-findSigInput :: [SigInput] -> [TxIn] -> [(SigInput, Int)]-findSigInput = S.findInputIndex sigInputOP--{- Merge multisig transactions -}---- | Merge partially-signed multisig transactions.  This function does not--- support segwit and P2SH-segwit inputs.  Use PSBTs to merge transactions with--- segwit inputs.-mergeTxs :: Network -> [Tx] -> [(ScriptOutput, Word64, OutPoint)] -> Either String Tx-mergeTxs net txs os-    | null txs = Left "Transaction list is empty"-    | length (nub emptyTxs) /= 1 = Left "Transactions do not match"-    | length txs == 1 = return $ head txs-    | otherwise = foldM (mergeTxInput net txs) (head emptyTxs) outs-  where-    zipOp = zip (matchTemplate os (txIn $ head txs) f) [0..]-    outs = map (first $ (\(o,v,_) -> (o,v)) . fromJust) $ filter (isJust . fst) zipOp-    f (_, _, o) txin = o == prevOutput txin-    emptyTxs = map (\tx -> foldl clearInput tx outs) txs-    ins is i = updateIndex i is (\ti -> ti{ scriptInput = B.empty })-    clearInput tx (_, i) =-        Tx (txVersion tx) (ins (txIn tx) i) (txOut tx) [] (txLockTime tx)---- | Merge input from partially-signed multisig transactions.  This function--- does not support segwit and P2SH-segwit inputs.-mergeTxInput ::-       Network-    -> [Tx]-    -> Tx-    -> ((ScriptOutput, Word64), Int)-    -> Either String Tx-mergeTxInput net txs tx ((so, val), i)-    -- Ignore transactions with empty inputs- = do-    let ins = map (scriptInput . (!! i) . txIn) txs-    sigRes <- mapM extractSigs $ filter (not . B.null) ins-    let rdm = snd $ head sigRes-    unless (all (== rdm) $ map snd sigRes) $ Left "Redeem scripts do not match"-    si <- encodeInputBS <$> go (nub $ concatMap fst sigRes) so rdm-    let ins' = updateIndex i (txIn tx) (\ti -> ti {scriptInput = si})-    return $ Tx (txVersion tx) ins' (txOut tx) [] (txLockTime tx)-  where-    go allSigs out rdmM =-        case out of-            PayMulSig msPubs r ->-                let sigs =-                        take r $-                        catMaybes $ matchTemplate allSigs msPubs $ f out-                 in return $ RegularInput $ SpendMulSig sigs-            PayScriptHash _ ->-                case rdmM of-                    Just rdm -> do-                        si <- go allSigs rdm Nothing-                        return $ ScriptHashInput (getRegularInput si) rdm-                    _ -> Left "Invalid output script type"-            _ -> Left "Invalid output script type"-    extractSigs si =-        case decodeInputBS net si of-            Right (RegularInput (SpendMulSig sigs)) -> Right (sigs, Nothing)-            Right (ScriptHashInput (SpendMulSig sigs) rdm) ->-                Right (sigs, Just rdm)-            _ -> Left "Invalid script input type"-    f out (TxSignature x sh) p =-        verifyHashSig-            (txSigHash net tx (encodeOutput out) val i sh)-            x-            (pubKeyPoint p)-    f _ TxSignatureEmpty _ = False--{- Tx verification -}---- | Verify if a transaction is valid and all of its inputs are standard.-verifyStdTx :: Network -> Tx -> [(ScriptOutput, Word64, OutPoint)] -> Bool-verifyStdTx net tx xs =-    not (null (txIn tx)) && all go (zip (matchTemplate xs (txIn tx) f) [0 ..])-  where-    f (_, _, o) txin = o == prevOutput txin-    go (Just (so, val, _), i) = verifyStdInput net tx i so val-    go _                      = False---- | Verify if a transaction input is valid and standard.-verifyStdInput :: Network -> Tx -> Int -> ScriptOutput -> Word64 -> Bool-verifyStdInput net tx i so0 val-    | isSegwit so0 = fromRight False $ (inp == mempty &&) . verifySegwitInput so0 <$> wp so0-    | otherwise    = fromRight False-                   $ verifyLegacyInput so0 <$> decodeInputBS net inp-                 <|> (nestedScriptOutput >>= \so -> verifyNestedInput so0 so <$> wp so)-  where-    verifyLegacyInput so si = case (so, si) of-        (PayPK pub, RegularInput (SpendPK (TxSignature sig sh))) ->-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayPKHash h, RegularInput (SpendPKHash (TxSignature sig sh) pub)) ->-            pubKeyAddr pub == p2pkhAddr h &&-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayMulSig pubs r, RegularInput (SpendMulSig sigs)) ->-            countMulSig net tx out val i (pubKeyPoint <$> pubs) sigs == r-        (PayScriptHash h, ScriptHashInput si' rdm) ->-            payToScriptAddress rdm == p2shAddr h && verifyLegacyInput rdm (RegularInput si')-        _ -> False-      where out = encodeOutput so--    verifySegwitInput so (rdm, si) = case (so, rdm, si) of-        (PayWitnessPKHash h, Nothing, SpendPKHash (TxSignature sig sh) pub) ->-            pubKeyWitnessAddr pub == p2wpkhAddr h &&-            verifyHashSig (theTxSigHash so sh Nothing) sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm'@(PayPK pub), SpendPK (TxSignature sig sh)) ->-            payToWitnessScriptAddress rdm' == p2wshAddr h &&-            verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm'@(PayPKHash kh), SpendPKHash (TxSignature sig sh) pub) ->-            payToWitnessScriptAddress rdm' == p2wshAddr h &&-            addressHash (encode pub) == kh &&-            verifyHashSig (theTxSigHash so sh $ Just rdm') sig (pubKeyPoint pub)-        (PayWitnessScriptHash h, Just rdm'@(PayMulSig pubs r), SpendMulSig sigs) ->-            payToWitnessScriptAddress rdm' == p2wshAddr h &&-            countMulSig' (\sh -> theTxSigHash so sh $ Just rdm') (pubKeyPoint <$> pubs) sigs == r-        _ -> False--    verifyNestedInput so so' x = case so of-        PayScriptHash h -> payToScriptAddress so' == p2shAddr h && verifySegwitInput so' x-        _               -> False--    inp             = scriptInput $ txIn tx !! i-    theTxSigHash so = S.makeSigHash net tx i so val--    ws | length (txWitness tx) > i = txWitness tx !! i-       | otherwise                 = []-    wp so = decodeWitnessInput net =<< viewWitnessProgram net so ws--    nestedScriptOutput = scriptOps <$> decode inp >>= \case-        [OP_PUSHDATA bs _] -> decodeOutputBS bs-        _                  -> Left "nestedScriptOutput: not a nested output"---- | Count the number of valid signatures for a multi-signature transaction.-countMulSig ::-       Network-    -> Tx-    -> Script-    -> Word64-    -> Int-    -> [PubKey]-    -> [TxSignature]-    -> Int-countMulSig net tx out val i = countMulSig' h-  where-    h = txSigHash net tx out val i--countMulSig' :: (SigHash -> Hash256) -> [PubKey] -> [TxSignature] -> Int-countMulSig' _ [] _ = 0-countMulSig' _ _ [] = 0-countMulSig' h (_:pubs) (TxSignatureEmpty:sigs) = countMulSig' h pubs sigs-countMulSig' h (pub:pubs) sigs@(TxSignature sig sh : sigs')-    | verifyHashSig (h sh) sig pub = 1 + countMulSig' h pubs sigs'-    | otherwise                    = countMulSig' h pubs sigs
− src/Network/Haskoin/Transaction/Builder/Sign.hs
@@ -1,279 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Builder.Sign-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Types and logic for signing transactions.--}-module Network.Haskoin.Transaction.Builder.Sign-    ( SigInput (..)-    , makeSignature-    , makeSigHash-    , signTx-    , findInputIndex-    , signInput-    , buildInput-    , sigKeys-    ) where--import           Control.DeepSeq                    (NFData)-import           Control.Monad                      (foldM, mzero, when)-import           Data.Aeson                         (FromJSON, ToJSON (..),-                                                     Value (Object), object,-                                                     parseJSON, (.:), (.:?),-                                                     (.=), pairs)-import           Data.Either                        (rights)-import           Data.Hashable                      (Hashable)-import           Data.List                          (find, nub)-import           Data.Maybe                         (catMaybes, fromMaybe,-                                                     mapMaybe, maybeToList)-import qualified Data.Serialize                     as S-import           Data.Word                          (Word64)-import           GHC.Generics                       (Generic)--import           Network.Haskoin.Address            (getAddrHash160, pubKeyAddr)-import           Network.Haskoin.Constants          (Network)-import           Network.Haskoin.Crypto             (Hash256, SecKey)-import           Network.Haskoin.Crypto.Signature   (signHash, verifyHashSig)-import           Network.Haskoin.Keys.Common        (PubKeyI (..), SecKeyI (..),-                                                     derivePubKeyI, wrapSecKey)-import           Network.Haskoin.Script.Common      (ScriptOutput (..),-                                                     encodeOutput,-                                                     encodeOutputBS, opPushData)-import           Network.Haskoin.Script.SigHash     (SigHash, TxSignature (..),-                                                     decodeTxSig, txSigHash,-                                                     txSigHashForkId)-import           Network.Haskoin.Script.Standard    (RedeemScript,-                                                     ScriptInput (..),-                                                     SimpleInput (..),-                                                     decodeInputBS,-                                                     encodeInputBS)-import           Network.Haskoin.Transaction.Common (OutPoint, Tx (..),-                                                     TxIn (..), WitnessData)-import           Network.Haskoin.Transaction.Segwit (WitnessProgram (..),-                                                     calcWitnessProgram,-                                                     isSegwit, toWitnessStack)-import           Network.Haskoin.Util               (matchTemplate, updateIndex)---- | Data type used to specify the signing parameters of a transaction input.--- To sign an input, the previous output script, outpoint and sighash are--- required. When signing a pay to script hash output, an additional redeem--- script is required.-data SigInput = SigInput-    { sigInputScript :: !ScriptOutput         -- ^ output script to spend-    , sigInputValue  :: !Word64               -- ^ output script value-    , sigInputOP     :: !OutPoint             -- ^ outpoint to spend-    , sigInputSH     :: !SigHash              -- ^ signature type-    , sigInputRedeem :: !(Maybe RedeemScript) -- ^ redeem script-    } deriving (Eq, Show, Read, Generic, Hashable, NFData)--instance ToJSON SigInput where-    toJSON (SigInput so val op sh rdm) = object $-        [ "pkscript" .= so-        , "value"    .= val-        , "outpoint" .= op-        , "sighash"  .= sh-        ] ++ [ "redeem" .= r | r <- maybeToList rdm ]-    toEncoding (SigInput so val op sh rdm) = pairs $-        "pkscript" .= so-        <> "value"    .= val-        <> "outpoint" .= op-        <> "sighash"  .= sh-        <> (case rdm of Nothing -> mempty-                        Just r  -> "redeem" .= r)--instance FromJSON SigInput where-    parseJSON (Object o) = do-        so  <- o .: "pkscript"-        val <- o .: "value"-        op  <- o .: "outpoint"-        sh  <- o .: "sighash"-        rdm <- o .:? "redeem"-        return $ SigInput so val op sh rdm-    parseJSON _ = mzero---- | Sign a transaction by providing the 'SigInput' signing parameters and a--- list of private keys. The signature is computed deterministically as defined--- in RFC-6979.-signTx :: Network-       -> Tx                 -- ^ transaction to sign-       -> [(SigInput, Bool)] -- ^ signing parameters, with nesting flag-       -> [SecKey]           -- ^ private keys to sign with-       -> Either String Tx   -- ^ signed transaction-signTx net otx sigis allKeys-    | null ti   = Left "signTx: Transaction has no inputs"-    | otherwise = foldM go otx $ findInputIndex (sigInputOP . fst) sigis ti-  where-    ti = txIn otx-    go tx (sigi@(SigInput so _ _ _ rdmM, _), i) = do-        keys <- sigKeys so rdmM allKeys-        foldM (\t k -> signInput net t i sigi k) tx keys---- | Sign a single input in a transaction deterministically (RFC-6979).  The--- nesting flag only affects the behavior of segwit inputs.-signInput ::-       Network-    -> Tx-    -> Int-    -> (SigInput, Bool) -- ^ boolean flag: nest input-    -> SecKeyI-    -> Either String Tx-signInput net tx i (sigIn@(SigInput so val _ _ rdmM), nest) key = do-    let sig = makeSignature net tx i sigIn key-    si <- buildInput net tx i so val rdmM sig $ derivePubKeyI key-    w  <- updatedWitnessData tx i so si-    return tx { txIn      = nextTxIn so si-              , txWitness = w-              }-  where-    f si x = x {scriptInput = encodeInputBS si}-    g so' x = x {scriptInput = S.encode . opPushData $ encodeOutputBS so'}-    txis = txIn tx-    nextTxIn so' si-        | isSegwit so' && nest = updateIndex i txis (g so')-        | isSegwit so'         = txIn tx-        | otherwise            = updateIndex i txis (f si)---- | Add the witness data of the transaction given segwit parameters for an input.------ @since 0.11.0.0-updatedWitnessData :: Tx -> Int -> ScriptOutput -> ScriptInput -> Either String WitnessData-updatedWitnessData tx i so si-    | isSegwit so = updateWitness . toWitnessStack =<< calcWitnessProgram so si-    | otherwise   = return $ txWitness tx-  where-    updateWitness w-        | null $ txWitness tx        = return $ updateIndex i defaultStack (const w)-        | length (txWitness tx) /= n = Left "Invalid number of witness stacks"-        | otherwise                  = return $ updateIndex i (txWitness tx) (const w)-    defaultStack = replicate n $ toWitnessStack EmptyWitnessProgram-    n = length $ txIn tx---- | Associate an input index to each value in a list-findInputIndex ::-       (a -> OutPoint) -- ^ extract an outpoint-    -> [a]             -- ^ input list-    -> [TxIn]          -- ^ reference list of inputs-    -> [(a, Int)]-findInputIndex getOutPoint as ti =-    mapMaybe g $ zip (matchTemplate as ti f) [0..]-  where-    f s txin = getOutPoint s == prevOutput txin-    g (Just s, i)  = Just (s,i)-    g (Nothing, _) = Nothing---- | Find from the list of provided private keys which one is required to sign--- the 'ScriptOutput'.-sigKeys ::-       ScriptOutput-    -> Maybe RedeemScript-    -> [SecKey]-    -> Either String [SecKeyI]-sigKeys so rdmM keys =-    case (so, rdmM) of-        (PayPK p, Nothing) ->-            return . map fst . maybeToList $ find ((== p) . snd) zipKeys-        (PayPKHash h, Nothing) -> return $ keyByHash h-        (PayMulSig ps r, Nothing) ->-            return $ map fst $ take r $ filter ((`elem` ps) . snd) zipKeys-        (PayScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        (PayWitnessPKHash h, _) -> return $ keyByHash h-        (PayWitnessScriptHash _, Just rdm) -> sigKeys rdm Nothing keys-        _ -> Left "sigKeys: Could not decode output script"-  where-    zipKeys =-        [ (prv, pub)-        | k <- keys-        , t <- [True, False]-        , let prv = wrapSecKey t k-        , let pub = derivePubKeyI prv-        ]-    keyByHash h = fmap fst . maybeToList . findKey h $ zipKeys-    findKey h   = find $ (== h) . getAddrHash160 . pubKeyAddr . snd---- | Construct an input for a transaction given a signature, public key and data--- about the previous output.-buildInput ::-       Network-    -> Tx                 -- ^ transaction where input will be added-    -> Int                -- ^ input index where signature will go-    -> ScriptOutput       -- ^ output script being spent-    -> Word64             -- ^ amount of previous output-    -> Maybe RedeemScript -- ^ redeem script if pay-to-script-hash-    -> TxSignature-    -> PubKeyI-    -> Either String ScriptInput-buildInput net tx i so val rdmM sig pub = do-    when (i >= length (txIn tx)) $ Left "buildInput: Invalid input index"-    case (so, rdmM) of-        (PayScriptHash _, Just rdm)        -> buildScriptHashInput rdm-        (PayWitnessScriptHash _, Just rdm) -> buildScriptHashInput rdm-        (PayWitnessPKHash _, Nothing)      -> return . RegularInput $ SpendPKHash sig pub-        (_, Nothing)                       -> buildRegularInput so-        _ -> Left "buildInput: Invalid output/redeem script combination"-  where-    buildRegularInput = \case-        PayPK _ -> return $ RegularInput $ SpendPK sig-        PayPKHash _ -> return $ RegularInput $ SpendPKHash sig pub-        PayMulSig msPubs r -> do-            let mSigs   = take r $ catMaybes $ matchTemplate allSigs msPubs f-                allSigs = nub $ sig : parseExistingSigs net tx so i-            return $ RegularInput $ SpendMulSig mSigs-        _ -> Left "buildInput: Invalid output/redeem script combination"-    buildScriptHashInput rdm = do-        inp <- buildRegularInput rdm-        return $ ScriptHashInput (getRegularInput inp) rdm-    f (TxSignature x sh) p =-        verifyHashSig (makeSigHash net tx i so val sh rdmM) x (pubKeyPoint p)-    f TxSignatureEmpty _ = False---- | Apply heuristics to extract the signatures for a particular input that are--- embedded in the transaction.------ @since 0.11.0.0-parseExistingSigs :: Network -> Tx -> ScriptOutput -> Int -> [TxSignature]-parseExistingSigs net tx so i = insSigs <> witSigs-  where-    insSigs = case decodeInputBS net scp of-            Right (ScriptHashInput (SpendMulSig xs) _) -> xs-            Right (RegularInput (SpendMulSig xs))      -> xs-            _                                          -> []-    scp = scriptInput $ txIn tx !! i-    witSigs-        | not $ isSegwit so   = []-        | null $ txWitness tx = []-        | otherwise           = rights $ decodeTxSig net <$> (txWitness tx !! i)---- | Produce a structured representation of a deterministic (RFC-6979) signature over an input.-makeSignature :: Network -> Tx -> Int -> SigInput -> SecKeyI -> TxSignature-makeSignature net tx i (SigInput so val _ sh rdmM) key =-    TxSignature (signHash (secKeyData key) m) sh-  where-    m = makeSigHash net tx i so val sh rdmM---- | A function which selects the digest algorithm and parameters as appropriate------ @since 0.11.0.0-makeSigHash ::-       Network-    -> Tx-    -> Int-    -> ScriptOutput-    -> Word64-    -> SigHash-    -> Maybe RedeemScript-    -> Hash256-makeSigHash net tx i so val sh rdmM = h net tx (encodeOutput so') val i sh-  where-    so' = case so of-        PayWitnessPKHash h' -> PayPKHash h'-        _                   -> fromMaybe so rdmM-    h | isSegwit so = txSigHashForkId-      | otherwise   = txSigHash
− src/Network/Haskoin/Transaction/Common.hs
@@ -1,312 +0,0 @@-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-}-{-# LANGUAGE OverloadedStrings #-}-{-|-Module      : Network.Haskoin.Transaction.Common-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Code related to transactions parsing and serialization.--}-module Network.Haskoin.Transaction.Common-    ( Tx(..)-    , TxIn(..)-    , TxOut(..)-    , OutPoint(..)-    , TxHash(..)-    , WitnessData-    , WitnessStack-    , WitnessStackItem-    , txHash-    , hexToTxHash-    , txHashToHex-    , nosigTxHash-    , nullOutPoint-    , genesisTx-    ) where--import           Control.Applicative            ((<|>))-import           Control.DeepSeq-import           Control.Monad                  (forM_, guard, liftM2, mzero,-                                                 replicateM, (<=<))-import           Data.Aeson                     as A-import           Data.Aeson.Encoding            (unsafeToEncoding)-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.ByteString.Builder        (char7)-import           Data.Hashable                  (Hashable)-import           Data.Maybe                     (fromMaybe)-import           Data.Serialize                 as S-import           Data.String                    (IsString, fromString)-import           Data.String.Conversions        (cs)-import           Data.Text                      (Text)-import           Data.Word                      (Word32, Word64)-import           GHC.Generics                   (Generic)-import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Network.Common-import           Network.Haskoin.Script.Common-import           Network.Haskoin.Util-import           Text.Read                      as R---- | Transaction id: hash of transaction excluding witness data.-newtype TxHash = TxHash { getTxHash :: Hash256 }-    deriving (Eq, Ord, Generic, Hashable, Serialize, NFData)--instance Show TxHash where-    showsPrec _ = shows . txHashToHex--instance Read TxHash where-    readPrec = do-        R.String str <- R.lexP-        maybe R.pfail return $ hexToTxHash $ cs str--instance IsString TxHash where-    fromString s =-        let e = error "Could not read transaction hash from hex string"-        in fromMaybe e $ hexToTxHash $ cs s--instance FromJSON TxHash where-    parseJSON = withText "txid" $-        maybe mzero return . hexToTxHash--instance ToJSON TxHash where-    toJSON = A.String . txHashToHex-    toEncoding (TxHash h) =-        unsafeToEncoding $-        char7 '"' <> hexBuilder (B.reverse (S.encode h)) <> char7 '"'---- | Transaction hash excluding signatures.-nosigTxHash :: Tx -> TxHash-nosigTxHash tx =-    TxHash $ doubleSHA256 $ S.encode tx { txIn = map clearInput $ txIn tx }-  where-    clearInput ti = ti { scriptInput = B.empty }---- | Convert transaction hash to hex form, reversing bytes.-txHashToHex :: TxHash -> Text-txHashToHex (TxHash h) = encodeHex (B.reverse (S.encode h))---- | Convert transaction hash from hex, reversing bytes.-hexToTxHash :: Text -> Maybe TxHash-hexToTxHash hex = do-    bs <- B.reverse <$> decodeHex hex-    h <- either (const Nothing) Just (S.decode bs)-    return $ TxHash h---- | Witness stack for SegWit transactions.-type WitnessData = [WitnessStack]--- | Witness stack for SegWit transactions.-type WitnessStack = [WitnessStackItem]--- | Witness stack item for SegWit transactions.-type WitnessStackItem = ByteString---- | Data type representing a transaction.-data Tx = Tx-    { -- | transaction data format version-      txVersion  :: !Word32-      -- | list of transaction inputs-    , txIn       :: ![TxIn]-      -- | list of transaction outputs-    , txOut      :: ![TxOut]-      -- | witness data for the transaction-    , txWitness  :: !WitnessData-      -- | earliest mining height or time-    , txLockTime :: !Word32-    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)---- | Compute transaction hash.-txHash :: Tx -> TxHash-txHash tx = TxHash (doubleSHA256 (S.encode tx {txWitness = []}))--instance IsString Tx where-    fromString =-        fromMaybe e . (eitherToMaybe . S.decode <=< decodeHex) . cs-      where-        e = error "Could not read transaction from hex string"--instance Serialize Tx where-    get = parseWitnessTx <|> parseLegacyTx-    put tx-        | null (txWitness tx) = putLegacyTx tx-        | otherwise = putWitnessTx tx--putInOut :: Tx -> Put-putInOut tx = do-    putVarInt $ length (txIn tx)-    forM_ (txIn tx) put-    putVarInt $ length (txOut tx)-    forM_ (txOut tx) put---- | Non-SegWit transaction serializer.-putLegacyTx :: Tx -> Put-putLegacyTx tx = do-    putWord32le (txVersion tx)-    putInOut tx-    putWord32le (txLockTime tx)---- | Witness transaciton serializer.-putWitnessTx :: Tx -> Put-putWitnessTx tx = do-    putWord32le (txVersion tx)-    putWord8 0x00-    putWord8 0x01-    putInOut tx-    putWitnessData (txWitness tx)-    putWord32le (txLockTime tx)---- | Non-SegWit transaction deseralizer.-parseLegacyTx :: Get Tx-parseLegacyTx = do-    v <- getWord32le-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    l <- getWord32le-    return-        Tx-        {txVersion = v, txIn = is, txOut = os, txWitness = [], txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get---- | Witness transaction deserializer.-parseWitnessTx :: Get Tx-parseWitnessTx = do-    v <- getWord32le-    m <- getWord8-    f <- getWord8-    guard $ m == 0x00-    guard $ f == 0x01-    is <- replicateList =<< S.get-    os <- replicateList =<< S.get-    w <- parseWitnessData $ length is-    l <- getWord32le-    return-        Tx {txVersion = v, txIn = is, txOut = os, txWitness = w, txLockTime = l}-  where-    replicateList (VarInt c) = replicateM (fromIntegral c) S.get---- | Witness data deserializer. Requires count of inputs.-parseWitnessData :: Int -> Get WitnessData-parseWitnessData n = replicateM n parseWitnessStack-  where-    parseWitnessStack = do-        VarInt i <- S.get-        replicateM (fromIntegral i) parseWitnessStackItem-    parseWitnessStackItem = do-        VarInt i <- S.get-        getByteString $ fromIntegral i---- | Witness data serializer.-putWitnessData :: WitnessData -> Put-putWitnessData = mapM_ putWitnessStack-  where-    putWitnessStack ws = do-        putVarInt $ length ws-        mapM_ putWitnessStackItem ws-    putWitnessStackItem bs = do-        putVarInt $ B.length bs-        putByteString bs--instance FromJSON Tx where-    parseJSON = withText "Tx" $-        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)--instance ToJSON Tx where-    toJSON = A.String . encodeHex . S.encode-    toEncoding tx =-        unsafeToEncoding $ char7 '"' <> hexBuilder (S.encode tx) <> char7 '"'---- | Data type representing a transaction input.-data TxIn =-    TxIn {-           -- | output being spent-           prevOutput   :: !OutPoint-           -- | signatures and redeem script-         , scriptInput  :: !ByteString-           -- | lock-time using sequence numbers (BIP-68)-         , txInSequence :: !Word32-         } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)--instance Serialize TxIn where-    get =-        TxIn <$> S.get <*> (readBS =<< S.get) <*> getWord32le-      where-        readBS (VarInt len) = getByteString $ fromIntegral len--    put (TxIn o s q) = do-        put o-        putVarInt $ B.length s-        putByteString s-        putWord32le q---- | Data type representing a transaction output.-data TxOut =-    TxOut {-            -- | value of output is satoshi-            outValue     :: !Word64-            -- | pubkey script-          , scriptOutput :: !ByteString-          } deriving (Eq, Show, Read, Ord, Generic, Hashable, NFData)--instance Serialize TxOut where-    get = do-        val <- getWord64le-        (VarInt len) <- S.get-        TxOut val <$> getByteString (fromIntegral len)--    put (TxOut o s) = do-        putWord64le o-        putVarInt $ B.length s-        putByteString s---- | The 'OutPoint' refers to a transaction output being spent.-data OutPoint = OutPoint-    { -- | hash of previous transaction-      outPointHash  :: !TxHash-      -- | position of output in previous transaction-    , outPointIndex :: !Word32-    } deriving (Show, Read, Eq, Ord, Generic, Hashable, NFData)--instance FromJSON OutPoint where-    parseJSON = withText "OutPoint" $-        maybe mzero return . (eitherToMaybe . S.decode <=< decodeHex)--instance ToJSON OutPoint where-    toJSON = A.String . encodeHex . S.encode-    toEncoding op =-        unsafeToEncoding $ char7 '"' <> hexBuilder (S.encode op) <> char7 '"'--instance Serialize OutPoint where-    get = do-        (h,i) <- liftM2 (,) S.get getWord32le-        return $ OutPoint h i-    put (OutPoint h i) = put h >> putWord32le i---- | Outpoint used in coinbase transactions.-nullOutPoint :: OutPoint-nullOutPoint =-    OutPoint-    { outPointHash =-          "0000000000000000000000000000000000000000000000000000000000000000"-    , outPointIndex = maxBound-    }---- | Transaction from Genesis block.-genesisTx :: Tx-genesisTx =-    Tx 1 [txin] [txout] [] locktime-  where-    txin = TxIn outpoint inputBS maxBound-    txout = TxOut 5000000000 (encodeOutputBS output)-    locktime = 0-    outpoint = OutPoint z maxBound-    Just inputBS = decodeHex $ fromString $-        "04ffff001d0104455468652054696d65732030332f4a616e2f323030392043686" ++-        "16e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f" ++-        "757420666f722062616e6b73"-    output = PayPK $ fromString $-        "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb" ++-        "649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"-    z = "0000000000000000000000000000000000000000000000000000000000000000"
− src/Network/Haskoin/Transaction/Partial.hs
@@ -1,505 +0,0 @@-{-# LANGUAGE DeriveGeneric              #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns             #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE ScopedTypeVariables        #-}-{-|-Module      : Network.Haskoin.Transaction.Partial-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : matt@bitnomial.com-Stability   : experimental-Portability : POSIX--Code related to PSBT parsing and serialization.--}-module Network.Haskoin.Transaction.Partial-    ( PartiallySignedTransaction (..)-    , Input (..)-    , Output (..)-    , UnknownMap (..)-    , Key (..)-    , merge-    , mergeInput-    , mergeOutput-    , complete-    , finalTransaction-    , emptyPSBT-    , emptyInput-    , emptyOutput-    ) where--import           Control.Applicative                ((<|>))-import           Control.DeepSeq-import           Control.Monad                      (guard, replicateM, void)-import           Data.ByteString                    (ByteString)-import qualified Data.ByteString                    as B-import           Data.Hashable                      (Hashable)-import           Data.HashMap.Strict                (HashMap)-import qualified Data.HashMap.Strict                as HashMap-import           Data.List                          (foldl')-import           Data.Maybe                         (fromMaybe, isJust)-import           Data.Serialize                     as S-import           GHC.Generics                       (Generic)-import           GHC.Word                           (Word32, Word8)-import           Network.Haskoin.Address            (Address (..), pubKeyAddr)-import           Network.Haskoin.Keys               (Fingerprint, KeyIndex,-                                                     PubKeyI)-import           Network.Haskoin.Network            (VarInt (..),-                                                     VarString (..), putVarInt)-import           Network.Haskoin.Script             (Script (..), ScriptOp (..),-                                                     ScriptOutput (..), SigHash,-                                                     decodeOutput,-                                                     decodeOutputBS,-                                                     encodeOutputBS,-                                                     isPayScriptHash,-                                                     opPushData, toP2SH,-                                                     toP2WSH)-import           Network.Haskoin.Transaction.Common (Tx (..), TxOut,-                                                     WitnessStack,-                                                     outPointIndex, prevOutput,-                                                     scriptInput, scriptOutput)-import           Network.Haskoin.Util               (eitherToMaybe)---- | PSBT data type as specified in--- [BIP-174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).--- This contains an unsigned transaction, inputs and outputs, and unspecified--- extra data. There is one input per input in the unsigned transaction, and one--- output per output in the unsigned transaction. The inputs and outputs in the--- 'PartiallySignedTransaction' line up by index with the inputs and outputs in--- the unsigned transaction.-data PartiallySignedTransaction = PartiallySignedTransaction-    { unsignedTransaction :: Tx-    , globalUnknown       :: UnknownMap-    , inputs              :: [Input]-    , outputs             :: [Output]-    } deriving (Show, Eq, Generic)--instance NFData PartiallySignedTransaction---- | Inputs contain all of the data needed to sign a transaction and all of the--- resulting signature data after signing.-data Input = Input-    { nonWitnessUtxo     :: Maybe Tx-    , witnessUtxo        :: Maybe TxOut-    , partialSigs        :: HashMap PubKeyI ByteString-    , sigHashType        :: Maybe SigHash-    , inputRedeemScript  :: Maybe Script-    , inputWitnessScript :: Maybe Script-    , inputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])-    , finalScriptSig     :: Maybe Script-    , finalScriptWitness :: Maybe WitnessStack-    , inputUnknown       :: UnknownMap-    } deriving (Show, Eq, Generic)--instance NFData Input---- | Outputs can contain information needed to spend the output at a later date.-data Output = Output-    { outputRedeemScript  :: Maybe Script-    , outputWitnessScript :: Maybe Script-    , outputHDKeypaths    :: HashMap PubKeyI (Fingerprint, [KeyIndex])-    , outputUnknown       :: UnknownMap-    } deriving (Show, Eq, Generic)--instance NFData Output---- | A map of raw PSBT keys to byte strings for extra data. The 'keyType' field cannot overlap with any of the reserved--- 'keyType' fields specified in the PSBT specification.-newtype UnknownMap = UnknownMap { unknownMap :: HashMap Key ByteString }-    deriving (Show, Eq, Semigroup, Monoid, Generic)--instance NFData UnknownMap---- | Raw keys for the map type used in PSBTs.-data Key = Key-    { keyType :: Word8-    , key     :: ByteString-    } deriving (Show, Eq, Generic)--instance NFData Key--instance Hashable Key---- | Take two 'PartiallySignedTransaction's and merge them. The 'unsignedTransaction' field in both must be the same.-merge :: PartiallySignedTransaction -> PartiallySignedTransaction -> Maybe PartiallySignedTransaction-merge psbt1 psbt2-    | unsignedTransaction psbt1 == unsignedTransaction psbt2-    = Just $ psbt1-        { globalUnknown = globalUnknown psbt1 <> globalUnknown psbt2-        , inputs = zipWith mergeInput (inputs psbt1) (inputs psbt2)-        , outputs = zipWith mergeOutput (outputs psbt1) (outputs psbt2)-        }-merge _ _ = Nothing--mergeInput :: Input -> Input -> Input-mergeInput a b = Input-    { nonWitnessUtxo = if isJust witUtx then Nothing else nonWitnessUtxo a <|> nonWitnessUtxo b-    , witnessUtxo = witUtx-    , sigHashType = sigHashType a <|> sigHashType b-    , partialSigs = partialSigs a <> partialSigs b-    , inputHDKeypaths = inputHDKeypaths a <> inputHDKeypaths b-    , inputUnknown = inputUnknown a <> inputUnknown b-    , inputRedeemScript = inputRedeemScript a <|> inputRedeemScript b-    , inputWitnessScript = inputWitnessScript a <|> inputWitnessScript b-    , finalScriptSig = finalScriptSig a <|> finalScriptSig b-    , finalScriptWitness = finalScriptWitness a <|> finalScriptWitness b-    }-  where-    witUtx = witnessUtxo a <|> witnessUtxo b--mergeOutput :: Output -> Output -> Output-mergeOutput a b = Output-    { outputRedeemScript = outputRedeemScript a <|> outputRedeemScript b-    , outputWitnessScript = outputWitnessScript a <|> outputWitnessScript b-    , outputHDKeypaths = outputHDKeypaths a <> outputHDKeypaths b-    , outputUnknown = outputUnknown a <> outputUnknown b-    }---- | Take partial signatures from all of the 'Input's and finalize the signature.-complete :: PartiallySignedTransaction -> PartiallySignedTransaction-complete psbt = psbt { inputs = map (completeInput . analyzeInputs) (indexed $ inputs psbt) }-  where-    analyzeInputs (i, input) = (outputScript =<< witnessUtxo input <|> nonWitScript, input)-      where-        nonWitScript = getPrevOut i =<< nonWitnessUtxo input--    getPrevOut i tx =-       (txOut tx !!?) . fromIntegral . outPointIndex . prevOutput =<< txIn (unsignedTransaction psbt) !!? i-    xs !!? i = lookup i $ indexed xs--    outputScript = eitherToMaybe . decodeOutputBS . scriptOutput--    completeInput (Nothing, input)     = input-    completeInput (Just script, input) = completeSig input script--    indexed :: [a] -> [(Word32, a)]-    indexed = zip [0..]--completeSig :: Input -> ScriptOutput -> Input-completeSig input (PayPK k) =-    input { finalScriptSig = eitherToMaybe . S.decode =<< HashMap.lookup k (partialSigs input) }-completeSig input (PayPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptSig = Just $ Script [opPushData sig, opPushData (S.encode k)] }-completeSig input (PayMulSig pubKeys m) | length sigs >= m = input { finalScriptSig = finalSig }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . (OP_0 :) . (map opPushData sigs <>) . pure . opPushData . S.encode <$> inputRedeemScript input-completeSig input (PayScriptHash h)-    | Just rdmScript <- inputRedeemScript input-    , PayScriptHash h == toP2SH rdmScript-    , Right decodedScript <- decodeOutput rdmScript-    , not (isPayScriptHash decodedScript)-    = completeSig input decodedScript-completeSig input (PayWitnessPKHash h)-    | [(k, sig)] <- HashMap.toList $ partialSigs input-    , PubKeyAddress h == pubKeyAddr k-    = input { finalScriptWitness = Just [sig, S.encode k]-            , finalScriptSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-            }-completeSig input (PayWitnessScriptHash h)-    | Just witScript <- inputWitnessScript input-    , PayWitnessScriptHash h == toP2WSH witScript-    , Right decodedScript <- decodeOutput witScript-    = completeWitnessSig input decodedScript-completeSig input _ = input--completeWitnessSig :: Input -> ScriptOutput -> Input-completeWitnessSig input script@(PayMulSig pubKeys m) | length sigs >= m = input-    { finalScriptWitness = Just finalWit-    , finalScriptSig = finalSig-    }-  where-    sigs = collectSigs m pubKeys input-    finalSig = Script . pure . opPushData . S.encode <$> inputRedeemScript input-    finalWit = mempty : sigs <> [encodeOutputBS script]-completeWitnessSig input _ = input--collectSigs :: Int -> [PubKeyI] -> Input -> [ByteString]-collectSigs m pubKeys input = take m . reverse $ foldl' lookupKey [] pubKeys-  where-    lookupKey sigs key = maybe sigs (:sigs) $ HashMap.lookup key (partialSigs input)---- | Take a finalized 'PartiallySignedTransaction' and produce the signed final transaction.  You may need to call--- 'complete' on the 'PartiallySignedTransaction' before producing the final transaction.-finalTransaction :: PartiallySignedTransaction -> Tx-finalTransaction psbt = setInputs . foldl' finalizeInput ([], []) $ zip (txIn tx) (inputs psbt)-  where-    tx = unsignedTransaction psbt-    hasWitness = any (isJust . finalScriptWitness) (inputs psbt)-    setInputs (ins, witData) = tx { txIn = reverse ins, txWitness = if hasWitness then reverse witData else [] }-    finalizeInput (ins, witData) (txInput, psbtInput) = maybe finalWitness finalScript $ finalScriptSig psbtInput-      where-        finalScript script = (txInput { scriptInput = encode script }:ins, []:witData)-        finalWitness = (ins, fromMaybe [] (finalScriptWitness psbtInput):witData)---- | Take an unsigned transaction and produce an empty 'PartiallySignedTransaction'-emptyPSBT :: Tx -> PartiallySignedTransaction-emptyPSBT tx = PartiallySignedTransaction-    { unsignedTransaction = tx-    , globalUnknown = mempty-    , inputs = replicate (length (txIn tx)) emptyInput-    , outputs = replicate (length (txOut tx)) emptyOutput-    }--emptyInput :: Input-emptyInput = Input-    Nothing Nothing HashMap.empty Nothing-    Nothing Nothing HashMap.empty-    Nothing Nothing (UnknownMap HashMap.empty)--emptyOutput :: Output-emptyOutput = Output Nothing Nothing HashMap.empty (UnknownMap HashMap.empty)--instance Serialize PartiallySignedTransaction where-    get = do-        magic <- getBytes 4-        guard $ magic == "psbt"-        headerSep <- getWord8-        guard $ headerSep == 0xff--        keySize <- getWord8-        guard $ keySize == 1-        globalUnsignedTxType <- getWord8-        guard $ globalUnsignedTxType == 0x00-        unsignedTransaction <- getSizedBytes-        guard $ all (B.null . scriptInput) (txIn unsignedTransaction)-        guard $ null (txWitness unsignedTransaction)--        globalUnknown <- get-        globalEnd <- getWord8-        guard $ globalEnd == 0x00--        inputs <- replicateM (length $ txIn unsignedTransaction) get-        outputs <- replicateM (length $ txOut unsignedTransaction) get--        return PartiallySignedTransaction { unsignedTransaction, globalUnknown, inputs, outputs }--    put PartiallySignedTransaction{ unsignedTransaction, globalUnknown, inputs, outputs } = do-        putByteString "psbt"-        putWord8 0xff -- Header separator--        putWord8 0x01 -- Key size-        putWord8 0x00 -- Unsigned Transaction type-        putSizedBytes unsignedTransaction-        put globalUnknown-        putWord8 0x00 -- Global end--        mapM_ put inputs-        mapM_ put outputs--instance Serialize Key where-    get = do-        VarInt keySize <- get-        guard $ keySize > 0-        t <- getWord8-        k <- getBytes (fromIntegral keySize - 1)-        return (Key t k)--    put (Key t k) = do-        putVarInt $ 1 + B.length k-        putWord8 t-        putByteString k--instance Serialize UnknownMap where-    get = go HashMap.empty-      where-        getItem m = do-            k <- get-            VarString v <- get-            go $ HashMap.insert k v m-        go m = do-            isEnd <- lookAhead getWord8-            if isEnd == 0x00-                then return (UnknownMap m)-                else getItem m--    put (UnknownMap m) = void $ HashMap.traverseWithKey (\k v -> put k >> put (VarString v)) m--instance Serialize Input where-    get = getMap getInputItem setInputUnknown emptyInput-      where-        setInputUnknown f input = input { inputUnknown = UnknownMap $ f (unknownMap $ inputUnknown input) }--    put Input { nonWitnessUtxo, witnessUtxo, partialSigs, sigHashType-                  , inputRedeemScript, inputWitnessScript, inputHDKeypaths-                  , finalScriptSig, finalScriptWitness, inputUnknown-                  } = do-        whenJust (putKeyValue InNonWitnessUtxo) nonWitnessUtxo-        whenJust (putKeyValue InWitnessUtxo) witnessUtxo-        putPartialSig partialSigs-        whenJust putSigHash sigHashType-        whenJust (putKeyValue InRedeemScript) inputRedeemScript-        whenJust (putKeyValue InWitnessScript) inputWitnessScript-        putHDPath InBIP32Derivation inputHDKeypaths-        whenJust (putKeyValue InFinalScriptSig) finalScriptSig-        whenJust (putKeyValue InFinalScriptWitness) finalScriptWitness-        put inputUnknown-        putWord8 0x00-      where-        putPartialSig = putPubKeyMap InPartialSig . fmap VarString-        putSigHash sigHash = do-            putKey InSigHashType-            putWord8 0x04-            putWord32le (fromIntegral sigHash)--instance Serialize Output where-    get = getMap getOutputItem setOutputUnknown emptyOutput-      where-        setOutputUnknown f output = output { outputUnknown = UnknownMap $ f (unknownMap $ outputUnknown output) }--    put Output{ outputRedeemScript, outputWitnessScript, outputHDKeypaths, outputUnknown } = do-        whenJust (putKeyValue OutRedeemScript) outputRedeemScript-        whenJust (putKeyValue OutWitnessScript) outputWitnessScript-        putHDPath OutBIP32Derivation outputHDKeypaths-        put outputUnknown-        putWord8 0x00--putSizedBytes :: Serialize a => a -> Put-putSizedBytes a = putVarInt (B.length bs) >> putByteString bs-  where bs = encode a--getSizedBytes :: Serialize a => Get a-getSizedBytes = getNested (fromIntegral . getVarInt <$> get) get--putKeyValue :: (Enum t, Serialize v) => t -> v -> Put-putKeyValue t v = putKey t >> putSizedBytes v--putKey :: Enum t => t -> Put-putKey t = putVarInt (1 :: Word8) >> putWord8 (enumWord8 t)--getMap :: (Bounded t, Enum t)-       => (Int -> a -> t -> Get a)-       -> ((HashMap Key ByteString -> HashMap Key ByteString) -> a -> a)-       -> a -> Get a-getMap getMapItem setUnknown = go-  where-    getItem keySize m (Right t) = getMapItem (fromIntegral keySize - 1) m t >>= go-    getItem keySize m (Left t)  = do-        k <- getBytes (fromIntegral keySize - 1)-        VarString v <- get-        go $ setUnknown (HashMap.insert (Key t k) v) m-    go m = do-        keySize <- getVarInt <$> get-        if keySize == 0-            then return m-            else getItem keySize m =<< (word8Enum <$> getWord8)--data InputType-    = InNonWitnessUtxo-    | InWitnessUtxo-    | InPartialSig-    | InSigHashType-    | InRedeemScript-    | InWitnessScript-    | InBIP32Derivation-    | InFinalScriptSig-    | InFinalScriptWitness-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData InputType--data OutputType-    = OutRedeemScript-    | OutWitnessScript-    | OutBIP32Derivation-    deriving (Show, Eq, Enum, Bounded, Generic)--instance NFData OutputType--getInputItem :: Int -> Input -> InputType -> Get Input-getInputItem 0 input@Input{nonWitnessUtxo = Nothing} InNonWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { nonWitnessUtxo = Just utxo }-getInputItem 0 input@Input{witnessUtxo = Nothing} InWitnessUtxo = do-    utxo <- getSizedBytes-    return $ input { witnessUtxo = Just utxo }-getInputItem keySize input InPartialSig = do-    (k, v) <- getPartialSig-    return $ input { partialSigs = HashMap.insert k v (partialSigs input) }-  where-    getPartialSig = (,) <$> isolate keySize get <*> (getVarString <$> get)-getInputItem 0 input@Input{sigHashType = Nothing} InSigHashType = do-    VarInt size <- get-    guard $ size == 0x04-    sigHash <- fromIntegral <$> getWord32le-    return $ input { sigHashType = Just sigHash }-getInputItem 0 input@Input{inputRedeemScript = Nothing} InRedeemScript = do-    script <- getSizedBytes-    return $ input { inputRedeemScript = Just script }-getInputItem 0 input@Input{inputWitnessScript = Nothing} InWitnessScript = do-    script <- getSizedBytes-    return $ input { inputWitnessScript = Just script }-getInputItem keySize input InBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ input { inputHDKeypaths = HashMap.insert k v (inputHDKeypaths input) }-getInputItem 0 input@Input{finalScriptSig = Nothing} InFinalScriptSig = do-    script <- getSizedBytes-    return $ input { finalScriptSig = Just script }-getInputItem 0 input@Input{finalScriptWitness = Nothing} InFinalScriptWitness = do-    scripts <- map getVarString <$> getVarIntList-    return $ input { finalScriptWitness = Just scripts }-  where-    getVarIntList = do-        VarInt n <- get-        replicateM (fromIntegral n) get-getInputItem keySize input inputType = fail $-    "Incorrect key size for input item or item already existed: " <>-    show (keySize, input, inputType)--getOutputItem :: Int -> Output -> OutputType -> Get Output-getOutputItem 0 output@Output{outputRedeemScript = Nothing} OutRedeemScript = do-    script <- getSizedBytes-    return $ output { outputRedeemScript = Just script }-getOutputItem 0 output@Output{outputWitnessScript = Nothing} OutWitnessScript = do-    script <- getSizedBytes-    return $ output { outputWitnessScript = Just script }-getOutputItem keySize output OutBIP32Derivation = do-    (k, v) <- getHDPath keySize-    return $ output { outputHDKeypaths = HashMap.insert k v (outputHDKeypaths output) }-getOutputItem keySize output outputType = fail $-    "Incorrect key size for output item or item already existed: " <>-    show (keySize, output, outputType)--getHDPath :: Int -> Get (PubKeyI, (Fingerprint, [KeyIndex]))-getHDPath keySize = (,) <$> isolate keySize get <*> (unPSBTHDPath <$> get)--putHDPath :: Enum t => t -> HashMap PubKeyI (Fingerprint, [KeyIndex]) -> Put-putHDPath t = putPubKeyMap t . fmap PSBTHDPath--newtype PSBTHDPath = PSBTHDPath { unPSBTHDPath :: (Fingerprint, [KeyIndex]) }-    deriving (Show, Eq, Generic)--instance NFData PSBTHDPath--instance Serialize PSBTHDPath where-    get = do-        VarInt valueSize <- get-        guard $ valueSize `mod` 4 == 0-        let numIndices = (fromIntegral valueSize - 4) `div` 4-        PSBTHDPath <$>-            isolate-                (fromIntegral valueSize)-                ((,) <$> getWord32le <*> getKeyIndexList numIndices)-      where-        getKeyIndexList n = replicateM n getWord32le-    put (PSBTHDPath (fp, kis)) = putVarInt (B.length bs) >> putByteString bs-      where-        bs = runPut $ putWord32le fp >> mapM_ putWord32le kis--putPubKeyMap :: (Serialize a, Enum t) => t -> HashMap PubKeyI a -> Put-putPubKeyMap t = void . HashMap.traverseWithKey putItem-  where-    putItem k v = put (Key (enumWord8 t) (encode k)) >> put v--enumWord8 :: Enum a => a -> Word8-enumWord8 = fromIntegral . fromEnum--word8Enum :: forall a. (Bounded a, Enum a) => Word8 -> Either Word8 a-word8Enum n | n <= enumWord8 (maxBound :: a) = Right . toEnum $ fromIntegral n-word8Enum n = Left n--whenJust :: Monad m => (a -> m ()) -> Maybe a -> m ()-whenJust = maybe (return ())
− src/Network/Haskoin/Transaction/Segwit.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections     #-}-{-|-Module      : Network.Haskoin.Transaction.Segwit-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Types to represent segregated witness data and auxilliary functions to-manipulate it.  See [BIP 141](https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki)-and [BIP 143](https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki) for-details.--}-module Network.Haskoin.Transaction.Segwit-    ( WitnessProgram (..)-    , WitnessProgramPKH (..)-    , WitnessProgramSH (..)-    , isSegwit--    , viewWitnessProgram-    , decodeWitnessInput--    , calcWitnessProgram-    , simpleInputStack-    , toWitnessStack-    ) where--import           Data.ByteString                    (ByteString)-import qualified Data.Serialize                     as S-import           Network.Haskoin.Constants          (Network)-import           Network.Haskoin.Keys.Common        (PubKeyI)-import           Network.Haskoin.Script.Common      (Script (..),-                                                     ScriptOutput (..),-                                                     decodeOutput, encodeOutput)-import           Network.Haskoin.Script.SigHash     (TxSignature (..),-                                                     decodeTxSig, encodeTxSig)-import           Network.Haskoin.Script.Standard    (ScriptInput (..),-                                                     SimpleInput (..))-import           Network.Haskoin.Transaction.Common (WitnessStack)---- | Test if a 'ScriptOutput' is P2WPKH or P2WSH------ @since 0.11.0.0-isSegwit :: ScriptOutput -> Bool-isSegwit = \case-    PayWitnessPKHash{}     -> True-    PayWitnessScriptHash{} -> True-    _                      -> False---- | High level represenation of a (v0) witness program------ @since 0.11.0.0-data WitnessProgram = P2WPKH WitnessProgramPKH | P2WSH WitnessProgramSH | EmptyWitnessProgram-    deriving (Eq, Show)---- | Encode a witness program------ @since 0.11.0.0-toWitnessStack :: WitnessProgram -> WitnessStack-toWitnessStack = \case-    P2WPKH (WitnessProgramPKH sig key) -> [encodeTxSig sig, S.encode key]-    P2WSH (WitnessProgramSH stack scr) -> stack <> [S.encode scr]-    EmptyWitnessProgram                -> mempty---- | High level representation of a P2WPKH witness------ @since 0.11.0.0-data WitnessProgramPKH = WitnessProgramPKH-    { witnessSignature :: !TxSignature-    , witnessPubKey    :: !PubKeyI-    } deriving (Eq, Show)---- | High-level representation of a P2WSH witness------ @since 0.11.0.0-data WitnessProgramSH = WitnessProgramSH-    { witnessScriptHashStack  :: ![ByteString]-    , witnessScriptHashScript :: !Script-    } deriving (Eq, Show)---- | Calculate the witness program from the transaction data------ @since 0.11.0.0-viewWitnessProgram :: Network -> ScriptOutput -> WitnessStack -> Either String WitnessProgram-viewWitnessProgram net so witness = case so of-    PayWitnessPKHash _ | length witness == 2 -> do-        sig    <- decodeTxSig net $ head witness-        pubkey <- S.decode $ witness !! 1-        return . P2WPKH $ WitnessProgramPKH sig pubkey-    PayWitnessScriptHash _ | not (null witness) -> do-        redeemScript <- S.decode $ last witness-        return . P2WSH $ WitnessProgramSH (init witness) redeemScript-    _ | null witness -> return EmptyWitnessProgram-      | otherwise    -> Left "viewWitnessProgram: Invalid witness program"---- | Analyze the witness, trying to match it with standard input structures------ @since 0.11.0.0-decodeWitnessInput :: Network -> WitnessProgram -> Either String (Maybe ScriptOutput, SimpleInput)-decodeWitnessInput net = \case-    P2WPKH (WitnessProgramPKH sig key) -> return (Nothing, SpendPKHash sig key)-    P2WSH (WitnessProgramSH st scr) -> do-        so <- decodeOutput scr-        fmap (Just so, ) $ case (so, st) of-            (PayPK _, [sigBS]) ->-                SpendPK <$> decodeTxSig net sigBS-            (PayPKHash _, [sigBS, keyBS]) ->-                SpendPKHash <$> decodeTxSig net sigBS <*> S.decode keyBS-            (PayMulSig _ _, "" : sigsBS) ->-                SpendMulSig <$> traverse (decodeTxSig net) sigsBS-            _ -> Left "decodeWitnessInput: Non-standard script output"-    EmptyWitnessProgram -> Left "decodeWitnessInput: Empty witness program"---- | Create the witness program for a standard input------ @since 0.11.0.0-calcWitnessProgram :: ScriptOutput -> ScriptInput -> Either String WitnessProgram-calcWitnessProgram so si = case (so, si) of-    (PayWitnessPKHash{}, RegularInput (SpendPKHash sig pk)) -> p2wpkh sig pk-    (PayScriptHash{}, RegularInput (SpendPKHash sig pk))    -> p2wpkh sig pk-    (PayWitnessScriptHash{}, ScriptHashInput i o)           -> p2wsh i o-    (PayScriptHash{}, ScriptHashInput i o)                  -> p2wsh i o-    _ -> Left "calcWitnessProgram: Invalid segwit SigInput"-  where-    p2wpkh sig = return . P2WPKH . WitnessProgramPKH sig-    p2wsh i o  = return . P2WSH $ WitnessProgramSH (simpleInputStack i) (encodeOutput o)---- | Create the witness stack required to spend a standard P2WSH input------ @since 0.11.0.0-simpleInputStack :: SimpleInput -> [ByteString]-simpleInputStack = \case-    SpendPK sig       -> [f sig]-    SpendPKHash sig k -> [f sig, S.encode k]-    SpendMulSig sigs  -> "" : fmap f sigs-  where-    f TxSignatureEmpty = ""-    f sig              = encodeTxSig sig
− src/Network/Haskoin/Util.hs
@@ -1,202 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-|-Module      : Network.Haskoin.Util-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--This module defines various utility functions used across the library.--}-module Network.Haskoin.Util-    (-      -- * ByteString Helpers-      bsToInteger-    , integerToBS-    , hexBuilder-    , encodeHex-    , decodeHex-    , getBits--      -- * Maybe & Either Helpers-    , eitherToMaybe-    , maybeToEither-    , liftEither-    , liftMaybe--      -- * Other Helpers-    , updateIndex-    , matchTemplate-    , convertBits--      -- * Triples-    , fst3-    , snd3-    , lst3--      -- * JSON Utilities-    , dropFieldLabel-    , dropSumLabels--    ) where--import           Control.Monad           (guard)-import           Control.Monad.Except    (ExceptT (..), liftEither)-import           Data.Aeson.Types        (Options (..), SumEncoding (..),-                                          defaultOptions, defaultTaggedObject)-import           Data.Bits-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import qualified Data.ByteString.Base16  as B16-import           Data.ByteString.Builder-import qualified Data.ByteString.Lazy    as BL-import           Data.Char               (toLower)-import           Data.List-import           Data.Text               (Text)-import qualified Data.Text.Encoding      as E-import           Data.Word               (Word8)---- ByteString helpers---- | Decode a big endian 'Integer' from a 'ByteString'.-bsToInteger :: ByteString -> Integer-bsToInteger = BS.foldr f 0 . BS.reverse-  where-    f w n = toInteger w .|. shiftL n 8---- | Encode an 'Integer' to a 'ByteString' as big endian.-integerToBS :: Integer -> ByteString-integerToBS 0 = BS.pack [0]-integerToBS i-    | i > 0     = BS.reverse $ BS.unfoldr f i-    | otherwise = error "integerToBS not defined for negative values"-  where-    f 0 = Nothing-    f x = Just (fromInteger x :: Word8, x `shiftR` 8)--hexBuilder :: ByteString -> Builder-hexBuilder = byteStringHex---- | Encode as string of human-readable hex characters.-encodeHex :: ByteString -> Text-encodeHex = E.decodeUtf8 . BL.toStrict . toLazyByteString . byteStringHex---- | Decode string of human-readable hex characters.-decodeHex :: Text -> Maybe ByteString-decodeHex text =-    let (x, b) = B16.decode (E.encodeUtf8 text)-    in guard (b == BS.empty) >> return x---- | Obtain 'Int' bits from beginning of 'ByteString'. Resulting 'ByteString'--- will be smallest required to hold that many bits, padded with zeroes to the--- right.-getBits :: Int -> ByteString -> ByteString-getBits b bs-    | r == 0 = BS.take q bs-    | otherwise = i `BS.snoc` l-  where-    (q, r) = b `quotRem` 8-    s = BS.take (q + 1) bs-    i = BS.init s-    l = BS.last s .&. (0xff `shiftL` (8 - r)) -- zero unneeded bits---- Maybe and Either monad helpers---- | Transform an 'Either' value into a 'Maybe' value. 'Right' is mapped to--- 'Just' and 'Left' is mapped to 'Nothing'. The value inside 'Left' is lost.-eitherToMaybe :: Either a b -> Maybe b-eitherToMaybe (Right b) = Just b-eitherToMaybe _         = Nothing---- | Transform a 'Maybe' value into an 'Either' value. 'Just' is mapped to--- 'Right' and 'Nothing' is mapped to 'Left'. Default 'Left' required.-maybeToEither :: b -> Maybe a -> Either b a-maybeToEither err = maybe (Left err) Right---- | Lift a 'Maybe' computation into the 'ExceptT' monad.-liftMaybe :: Monad m => b -> Maybe a -> ExceptT b m a-liftMaybe err = liftEither . maybeToEither err---- Various helpers---- | Applies a function to only one element of a list defined by its index.  If--- the index is out of the bounds of the list, the original list is returned.-updateIndex :: Int      -- ^ index of the element to change-            -> [a]      -- ^ list of elements-            -> (a -> a) -- ^ function to apply-            -> [a]      -- ^ result with one element changed-updateIndex i xs f-    | i < 0 || i >= length xs = xs-    | otherwise = l ++ (f h : r)-  where-    (l,h:r) = splitAt i xs---- | Use the list @[b]@ as a template and try to match the elements of @[a]@--- against it. For each element of @[b]@ return the (first) matching element of--- @[a]@, or 'Nothing'. Output list has same size as @[b]@ and contains results--- in same order. Elements of @[a]@ can only appear once.-matchTemplate :: [a]              -- ^ input list-              -> [b]              -- ^ list to serve as a template-              -> (a -> b -> Bool) -- ^ comparison function-              -> [Maybe a]-matchTemplate [] bs _ = replicate (length bs) Nothing-matchTemplate _  [] _ = []-matchTemplate as (b:bs) f = case break (`f` b) as of-    (l,r:rs) -> Just r  : matchTemplate (l ++ rs) bs f-    _        -> Nothing : matchTemplate as bs f---- | Returns the first value of a triple.-fst3 :: (a,b,c) -> a-fst3 (a,_,_) = a---- | Returns the second value of a triple.-snd3 :: (a,b,c) -> b-snd3 (_,b,_) = b---- | Returns the last value of a triple.-lst3 :: (a,b,c) -> c-lst3 (_,_,c) = c---- | Field label goes lowercase and first @n@ characters get removed.-dropFieldLabel :: Int -> Options-dropFieldLabel n = defaultOptions-    { fieldLabelModifier = map toLower . drop n-    }---- | Transformation from 'dropFieldLabel' is applied with argument @f@, plus--- constructor tags are lowercased and first @c@ characters removed. @tag@ is--- used as the name of the object field name that will hold the transformed--- constructor tag as its value.-dropSumLabels :: Int -> Int -> String -> Options-dropSumLabels c f tag = (dropFieldLabel f)-    { constructorTagModifier = map toLower . drop c-    , sumEncoding = defaultTaggedObject { tagFieldName = tag }-    }---- | Convert from one power-of-two base to another, as long as it fits in a--- 'Word'.-convertBits :: Bool -> Int -> Int -> [Word] -> ([Word], Bool)-convertBits pad frombits tobits i = (reverse yout, rem')-  where-    (xacc, xbits, xout) = foldl' outer (0, 0, []) i-    (yout, rem')-        | pad && xbits /= 0 =-            let xout' = (xacc `shiftL` (tobits - xbits)) .&. maxv : xout-            in (xout', False)-        | pad = (xout, False)-        | xbits /= 0 = (xout, True)-        | otherwise = (xout, False)-    maxv = 1 `shiftL` tobits - 1-    max_acc = 1 `shiftL` (frombits + tobits - 1) - 1-    outer (acc, bits, out) it =-        let acc' = ((acc `shiftL` frombits) .|. it) .&. max_acc-            bits' = bits + frombits-            (out', bits'') = inner acc' out bits'-        in (acc', bits'', out')-    inner acc out bits-        | bits >= tobits =-            let bits' = bits - tobits-                out' = ((acc `shiftR` bits') .&. maxv) : out-            in inner acc out' bits'-        | otherwise = (out, bits)
+ test/Haskoin/Address/Bech32Spec.hs view
@@ -0,0 +1,154 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Address.Bech32Spec+    ( spec+    ) where++import           Control.Monad+import           Data.Bits               (xor)+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as B+import           Data.Char               (chr, ord, toLower)+import           Data.Maybe+import           Data.String.Conversions+import           Data.Text               (Text, append, pack, snoc, uncons)+import qualified Data.Text               as T+import           Data.Word               (Word8)+import           Haskoin.Address+import           Haskoin.Address.Bech32+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit++spec = do+    describe "bech32 checksum" $ do+        it "should have valid checksum" $ forM_ validChecksums testValidChecksum+        it "should have invalid checksum" $+            forM_ invalidChecksums testInvalidChecksum+        it "should be a valid address" $ forM_ validAddresses testValidAddress+        it "should be an invalid address" $+            forM_ invalidAddresses testInvalidAddress+        it "should be same for the input in different case" $+            all (== Just "test12hrzfj") . map (flip bech32Encode []) $ hrpCaseVariants+    describe "more encoding/decoding cases" $ do+        it "length > 90" $+            assert $+            isNothing $+            bech32Encode "bc" (replicate 82 (word5 (1 :: Word8)))+        it "segwit version bounds" $+            assert $ isNothing $ segwitEncode "bc" 17 []+        it "segwit prog len version 0" $+            assert $ isNothing $ segwitEncode "bc" 0 (replicate 30 1)+        it "segwit prog len version != 0" $+            assert $ isJust $ segwitEncode "bc" 1 (replicate 30 1)+        it "segwit prog len version != 0" $+            assert $ isNothing $ segwitEncode "bc" 1 (replicate 41 1)+        it "empty HRP encode" $ assert $ isNothing $ bech32Encode "" []+        it "empty HRP encode" $+            assert $ isNothing $ bech32Decode "10a06t8"+        it "hrp lowercased" $+            bech32Encode "HRP" [] `shouldBe` bech32Encode "hrp" []+++testValidChecksum :: Bech32 -> Assertion+testValidChecksum checksum = case bech32Decode checksum of+    Nothing                      -> assertFailure (show checksum)+    Just (resultHRP, resultData) -> do+        -- test that a corrupted checksum fails decoding.+        let (hrp, rest)         = T.breakOnEnd "1" checksum+            Just (first, rest') = uncons rest+            checksumCorrupted   = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'+        assertBool (show checksum ++ " corrupted")+            $ isNothing (bech32Decode checksumCorrupted)+        -- test that re-encoding the decoded checksum results in the same checksum.+        let checksumEncoded  = bech32Encode resultHRP resultData+            expectedChecksum = Just $ T.toLower checksum+        assertEqual (show checksum ++ " re-encode")+                    expectedChecksum+                    checksumEncoded++testInvalidChecksum :: Bech32 -> Assertion+testInvalidChecksum checksum =+    assertBool (show checksum) (isNothing $ bech32Decode checksum)++testValidAddress :: (Text, Text) -> Assertion+testValidAddress (address, hexscript) = do+    let address' = T.toLower address+        hrp      = T.take 2 address'+    case segwitDecode hrp address of+        Nothing                -> assertFailure "decode failed"+        Just (witver, witprog) -> do+            assertEqual (show address)+                        (decodeHex hexscript)+                        (Just $ segwitScriptPubkey witver witprog)+            assertEqual (show address)+                        (Just address')+                        (segwitEncode hrp witver witprog)++testInvalidAddress :: Text -> Assertion+testInvalidAddress address = do+    assertBool (show address) (isNothing $ segwitDecode "bc" address)+    assertBool (show address) (isNothing $ segwitDecode "tb" address)++segwitScriptPubkey :: Word8 -> [Word8] -> ByteString+segwitScriptPubkey witver witprog =+    B.pack $ witver' : fromIntegral (length witprog) : witprog+    where witver' = if witver == 0 then 0 else witver + 0x50++validChecksums :: [Text]+validChecksums =+    [ "A12UEL5L"+    , "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"+    , "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"+    , "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"+    , "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"+    ]++invalidChecksums :: [Text]+invalidChecksums =+    [ " 1nwldj5"+    , "\DEL1axkwrx"+    , "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx"+    , "pzry9x0s0muk"+    , "1pzry9x0s0muk"+    , "x1b4n0q5v"+    , "li1dgmt3"+    , "de1lg7wt\xFF"+    ]++validAddresses :: [(Text, Text)]+validAddresses =+    [ ("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6")+    , ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"+      ,"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262")+    , ("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"+      ,"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6")+    , ("BC1SW50QA3JX3S", "6002751e")+    , ("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323")+    , ("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"+      ,"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433")+    ]++invalidAddresses :: [Text]+invalidAddresses =+    [ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"+    , "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"+    , "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"+    , "bc1rw5uspcuh"+    , "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"+    , "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"+    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"+    , "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"+    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"+    , "bc1gmk9yu"+    ]++hrpCaseVariants:: [Text]+hrpCaseVariants = map T.pack hrpTestPermutations++hrpTestPermutations :: [String]+hrpTestPermutations = do+    a <- ['t', 'T']+    b <- ['e', 'E']+    c <- ['s', 'S']+    d <- ['t', 'T']+    return [a, b, c, d]
+ test/Haskoin/Address/CashAddrSpec.hs view
@@ -0,0 +1,280 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Address.CashAddrSpec (spec) where++import           Control.Monad+import qualified Data.ByteString.Char8    as C+import           Data.Maybe+import           Data.String.Conversions+import           Data.Text                (Text)+import           Haskoin.Address+import           Haskoin.Address.CashAddr+import           Haskoin.Constants+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit++spec :: Spec+spec = do+    describe "cashaddr checksum test vectors" $ do+        it "prefix:x64nx6hz" $ do+            let mpb = cash32decode "prefix:x64nx6hz"+            mpb `shouldBe` Just ("prefix", "")+        it "p:gpf8m4h7" $ do+            let mpb = cash32decode "p:gpf8m4h7"+            mpb `shouldBe` Just ("p", "")+        it "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn" $ do+            let mpb =+                    cash32decode+                        "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn"+            mpb `shouldBe`+                Just+                    ( "bitcoincash"+                    , "\NULD2\DC4\199BT\182\&5\207\132e:V\215\198u\190w\223")+        it "bchtest:testnetaddress4d6njnut" $ do+            let mpb = cash32decode "bchtest:testnetaddress4d6njnut"+            mpb `shouldBe` Just ("bchtest", "^`\185\229}kG\152")+        it "bchreg:555555555555555555555555555555555555555555555udxmlmrz" $ do+            let mpb =+                    cash32decode+                        "bchreg:555555555555555555555555555555555555555555555udxmlmrz"+            mpb `shouldBe`+                Just+                    ( "bchreg"+                    , "\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)J")+    describe "cashaddr to base58 translation test vectors" $ do+        it "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+            addr `shouldBe`+                Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+        it "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"+            addr `shouldBe`+                Just "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"+        it "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"+            addr `shouldBe`+                Just "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"+        it "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+            addr `shouldBe`+                Just "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"+        it "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"+            addr `shouldBe`+                Just "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"+        it "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" $ do+            let addr =+                    addrToString bch =<<+                    stringToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"+            addr `shouldBe`+                Just "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"+    describe "base58 to cashaddr translation test vectors" $ do+        it "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"+            addr `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"+        it "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"+            addr `shouldBe` Just "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"+        it "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"+            addr `shouldBe` Just "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"+        it "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"+            addr `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"+        it "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"+            addr `shouldBe` Just "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"+        it "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" $ do+            let addr =+                    addrToString btc =<<+                    stringToAddr+                        bch+                        "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"+            addr `shouldBe` Just "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"+    describe "cashaddr larger test vectors" $+        forM_ (zip [0 ..] vectors) $ \(i, vec) ->+            it ("cashaddr test vector " <> show (i :: Int)) $ testCashAddr vec++{- Various utilities -}++testCashAddr :: (Int, CashVersion, Cash32, Text) -> Assertion+testCashAddr (len, typ, addr, hex) = do+    let mbs = decodeHex hex+    assertBool "Could not decode hex payload from test vector" (isJust mbs)+    let mlow = cash32decode addr+    assertBool "Could not decode low level address" (isJust mlow)+    let Just (_, lbs) = mlow+    assertEqual "Low-level payload size incorrect" len (C.length lbs - 1)+    assertEqual "Low-level payload doesn't match" bs (C.tail lbs)+    let mdec = cash32decodeType addr+    assertBool ("Could not decode test address: " <> cs addr) (isJust mdec)+    assertEqual "Length doesn't match" len (C.length pay)+    assertEqual "Version doesn't match" typ ver+    assertEqual "Payload doesn't match" bs pay+  where+    Just bs = decodeHex hex+    Just (_, ver, pay) = cash32decodeType addr++-- | All vectors starting with @pref@ had the wrong version in the spec+-- document.+vectors :: [(Int, CashVersion, Text, Text)]+vectors =+    [ ( 20+      , 0+      , "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2"+      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")+    , ( 20+      , 1+      , "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t"+      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")+    , ( 20+      , 1+      , "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5"+      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")+    , ( 20+      , 15+      , "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf"+      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")+    , ( 24+      , 0+      , "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0"+      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")+    , ( 24+      , 1+      , "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr"+      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")+    , ( 24+      , 1+      , "pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v"+      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")+    , ( 24+      , 15+      , "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp"+      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")+    , ( 28+      , 0+      , "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz"+      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")+    , ( 28+      , 1+      , "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt"+      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")+    , ( 28+      , 1+      , "pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn"+      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")+    , ( 28+      , 15+      , "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g"+      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")+    , ( 32+      , 0+      , "bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake"+      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")+    , ( 32+      , 1+      , "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6"+      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")+    , ( 32+      , 1+      , "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9"+      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")+    , ( 32+      , 15+      , "prefix:0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w"+      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")+    , ( 40+      , 0+      , "bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz"+      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")+    , ( 40+      , 1+      , "bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej"+      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")+    , ( 40+      , 1+      , "pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3"+      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")+    , ( 40+      , 15+      , "prefix:0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsvctzqy"+      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")+    , ( 48+      , 0+      , "bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl"+      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")+    , ( 48+      , 1+      , "bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x"+      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")+    , ( 48+      , 1+      , "pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg"+      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")+    , ( 48+      , 15+      , "prefix:0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqakcssnmn"+      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")+    , ( 56+      , 0+      , "bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f"+      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")+    , ( 56+      , 1+      , "bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g"+      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")+    , ( 56+      , 1+      , "pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl"+      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")+    , ( 56+      , 15+      , "prefix:0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsgjrqpnw8"+      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")+    , ( 64+      , 0+      , "bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w"+      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")+    , ( 64+      , 1+      , "bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez"+      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")+    , ( 64+      , 1+      , "pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8"+      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")+    , ( 64+      , 15+      , "prefix:0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845"+      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")+    ]
+ test/Haskoin/AddressSpec.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.AddressSpec (spec) where++import           Data.Aeson+import           Data.Aeson.Types+import           Data.ByteString        (ByteString)+import qualified Data.ByteString        as BS (append, empty, pack)+import           Data.Either            (isRight)+import           Data.Maybe             (fromJust, isJust)+import qualified Data.Serialize         as S+import           Data.Text              (Text)+import           Haskoin.Address+import           Haskoin.Address.Base58+import           Haskoin.Constants+import           Haskoin.Keys           (derivePubKeyI)+import           Haskoin.Test+import           Test.Hspec+import           Test.HUnit             (Assertion, assertBool, assertEqual)+import           Test.QuickCheck++spec :: Spec+spec = do+    let net = btc+    describe "btc address" . it "base58 encodings" $ mapM_ runVector vectors+    describe "btc-test address" $ props btcTest+    describe "bch address" $ props bch+    describe "bch-test address" $ props bchTest+    describe "bch-regtest address" $ props bchRegTest+    describe "json serialization" . it "encodes and decodes address" $+        forAll arbitraryAddress (testCustom (addrFromJSON net) (addrToJSON net))+    describe "witness address vectors" . it "p2sh(pwpkh)" $+        mapM_ testCompatWitness compatWitnessVectors++props :: Network -> Spec+props net = do+    it "encodes and decodes base58 bytestring" $+        property $+        forAll arbitraryBS $ \bs -> decodeBase58 (encodeBase58 bs) == Just bs+    it "encodes and decodes base58 bytestring with checksum" $+        property $+        forAll arbitraryBS $ \bs ->+            decodeBase58Check (encodeBase58Check bs) == Just bs+    it "encodes and decodes address" $+        property $+        forAll arbitraryAddress $ \a ->+            (stringToAddr net =<< addrToString net a) == Just a+    it "shows and reads address" $+        property $ forAll arbitraryAddress $ \a -> read (show a) == a++runVector :: (ByteString, Text, Text) -> Assertion+runVector (bs, e, chk) = do+    assertEqual "encodeBase58" e b58+    assertEqual "encodeBase58Check" chk b58Chk+    assertEqual "decodeBase58" (Just bs) (decodeBase58 b58)+    assertEqual "decodeBase58Check" (Just bs) (decodeBase58Check b58Chk)+  where+    b58    = encodeBase58 bs+    b58Chk = encodeBase58Check bs++vectors :: [(ByteString, Text, Text)]+vectors =+    [ ( BS.empty, "", "3QJmnh" )+    , ( BS.pack [0], "1", "1Wh4bh" )+    , ( BS.pack [0,0,0,0], "1111", "11114bdQda" )+    , ( BS.pack [0,0,1,0,0], "11LUw", "113CUwsFVuo" )+    , ( BS.pack [255], "5Q", "VrZDWwe" )+    , ( BS.pack [0,0,0,0] `BS.append` BS.pack [1..255]+      , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"+      , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYtFHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5eckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7KeQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZvkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfdUhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcqpJTN9vt"+      )+    ]++testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool+testCustom f g x = parseMaybe f (g x) == Just x++compatWitnessVectors :: [(Network, Text, Text)]+compatWitnessVectors =+    [ ( btcTest+      , "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q"+      , "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw")+    ]++testCompatWitness :: (Network, Text, Text) -> Assertion+testCompatWitness (net, seckey, addr) = do+    let seckeyM = fromWif net seckey+    assertBool "decode seckey" (isJust seckeyM)+    let pubkey = derivePubKeyI (fromJust seckeyM)+    let addrM = addrToString btcTest (pubKeyCompatWitnessAddr pubkey)+    assertBool "address can be encoded" (isJust addrM)+    assertEqual "witness address matches" addr (fromJust addrM)
+ test/Haskoin/BlockSpec.hs view
@@ -0,0 +1,359 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.BlockSpec+    ( spec+    ) where++import           Control.Monad.State.Strict+import           Data.Aeson                 as A+import           Data.Either                (fromRight)+import           Data.Map.Strict            (singleton)+import           Data.Maybe                 (fromJust)+import           Data.Serialize             as S+import           Data.String                (fromString)+import           Data.String.Conversions    (cs)+import           Data.Text                  (Text)+import           Haskoin.Block+import           Haskoin.Block.Headers+import           Haskoin.Block.Merkle+import           Haskoin.Constants+import           Haskoin.Test+import           Haskoin.Transaction+import           Test.Hspec+import           Test.HUnit                 hiding (State)+import           Test.QuickCheck++myTime :: Timestamp+myTime = 1499083075++withChain :: Network -> State HeaderMemory a -> a+withChain net f = evalState f (initialChain net)++chain :: BlockHeaders m => Network -> BlockHeader -> Int -> m ()+chain net bh i = do+    bnsE <- connectBlocks net myTime bhs+    either error (const $ return ()) bnsE+  where+    bhs = appendBlocks net 6 bh i++spec :: Spec+spec = do+    let net = bchRegTest+    describe "blockchain headers" $ do+        it "gets best block" $+            let bb =+                    withChain net $ do+                        chain net (getGenesisHeader net) 100+                        getBestBlockHeader+             in nodeHeight bb `shouldBe` 100+        it "builds a block locator" $+            let loc =+                    withChain net $ do+                        chain net (getGenesisHeader net) 100+                        bb <- getBestBlockHeader+                        blockLocatorNodes bb+                heights = map nodeHeight loc+             in heights `shouldBe` [100,99 .. 90] <> [88, 84, 76, 60, 28, 0]+        it "follows split chains" $+            let bb = withChain net $ splitChain net >> getBestBlockHeader+             in nodeHeight bb `shouldBe` 4035+    describe "block hash" $ do+        it "encodes and decodes block hash" $+            property $+            forAll arbitraryBlockHash $ \h ->+                hexToBlockHash (blockHashToHex h) == Just h+        it "from string block hash" $+            property $+            forAll arbitraryBlockHash $ \h ->+                fromString (cs $ blockHashToHex h) == h+        it "show and read block hash" $+            property $ forAll arbitraryBlockHash $ \h -> read (show h) == h+        it "json block hash" $ property $ forAll arbitraryBlockHash testID+    describe "merkle trees" $ do+        let net' = btc+        it "builds tree of right width at height 1" $ property testTreeWidth+        it "builds tree of right width at height 0" $ property testBaseWidth+        it "builds and extracts partial merkle tree" $+            property $+            forAll+                (listOf1 ((,) <$> arbitraryTxHash <*> arbitrary))+                (buildExtractTree net')+        it "merkle root test vectors" $+            mapM_ runMerkleVector merkleVectors+    describe "compact number" $ do+        it "compact number local vectors" testCompact+        it "compact number imported vectors" testCompactBitcoinCore+    describe "block serialization" $ do+        it "encodes and decodes block" $+            property $ forAll (arbitraryBlock net) cerealID+        it "encodes and decodes block JSON" $+            property $ forAll (arbitraryBlock net) testID+        it "encodes and decodes block header" $+            property $ forAll arbitraryBlockHeader cerealID+        it "encodes and decodes block header JSON" $+            property $ forAll arbitraryBlockHeader testID+        it "encodes and decodes getblocks" $+            property $ forAll arbitraryGetBlocks cerealID+        it "encodes and decodes getheaders" $+            property $ forAll arbitraryGetHeaders cerealID+        it "encodes and decdoes headers" $+            property $ forAll arbitraryHeaders cerealID+        it "encodes and decodes merkle block" $+            property $ forAll arbitraryMerkleBlock cerealID+    describe "helper functions" $ do+        it "computes bitcoin block subsidy correctly" (testSubsidy btc)+        it "computes regtest block subsidy correctly" (testSubsidy btcRegTest)++-- 0 → → 2015 → → → → → → → 4031+--       ↓+--       → → 2035 → → → → → → 4035*+--           ↓+--           → → 2185+splitChain :: Network -> State HeaderMemory ()+splitChain net = do+    start <- go 1 (getGenesisHeader net) 2015+    e 2015 (head start)+    tail1 <- go 2 (nodeHeader $ head start) 2016+    e 4031 (head tail1)+    tail2 <- go 3 (nodeHeader $ head start) 20+    e 2035 (head tail2)+    tail3 <- go 4 (nodeHeader $ head tail2) 2000+    e 4035 (head tail3)+    tail4 <- go 5 (nodeHeader $ head tail2) 150+    e 2185 (head tail4)+    sp1 <- splitPoint (head tail1) (head tail3)+    unless (sp1 == head start) $+        error $+        "Split point wrong between blocks 4031 and 4035: " +++        show (nodeHeight sp1)+    sp2 <- splitPoint (head tail4) (head tail3)+    unless (sp2 == head tail2) $+        error $+        "Split point wrong between blocks 2185 and 4035: " +++        show (nodeHeight sp2)+  where+    e n bn =+        unless (nodeHeight bn == n) $+        error $+        "Node height " +++        show (nodeHeight bn) ++ " of first chunk should be " ++ show n+    go seed start n = do+        let bhs = appendBlocks net seed start n+        bnE <- connectBlocks net myTime bhs+        case bnE of+            Right bn -> return bn+            Left ex  -> error ex++{- Merkle Trees -}++testTreeWidth :: Int -> Property+testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1++testBaseWidth :: Int -> Property+testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i++buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool+buildExtractTree net txs =+    r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)+  where+    (f, h) = buildPartialMerkle txs+    (r, m) =+        fromRight (error "Could not extract matches from Merkle tree") $+        extractMatches net f h (length txs)++testCompact :: Assertion+testCompact = do+    assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)+    assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)+    assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)+    assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)+    assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))+    assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)++testCompactBitcoinCore :: Assertion+testCompactBitcoinCore = do+    assertEqual "zero" (0, False) (decodeCompact 0x00000000)+    assertEqual+        "zero (encode · decode)"+        0x00000000+        (encodeCompact . fst $ decodeCompact 0x00000000)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)+    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)+    assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)+    assertEqual+        "vector 1 (encode · decode)"+        0x01120000+        (encodeCompact . fst $ decodeCompact 0x01123456)+    assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)+    assertEqual+        "vector 2 (negative) (decode)"+        (-0x7e, False)+        (decodeCompact 0x01fedcba)+    assertEqual+        "vector 2 (negative) (encode · decode)"+        0x01fe0000+        (encodeCompact . fst $ decodeCompact 0x01fedcba)+    assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)+    assertEqual+        "vector 3 (encode · decode)"+        0x02123400+        (encodeCompact . fst $ decodeCompact 0x02123456)+    assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)+    assertEqual+        "vector 4 (encode · decode)"+        0x03123456+        (encodeCompact . fst $ decodeCompact 0x03123456)+    assertEqual+        "vector 5 (decode)"+        (0x12345600, False)+        (decodeCompact 0x04123456)+    assertEqual+        "vector 5 (encode · decode)"+        0x04123456+        (encodeCompact . fst $ decodeCompact 0x04123456)+    assertEqual+        "vector 6 (decode)"+        (-0x12345600, False)+        (decodeCompact 0x04923456)+    assertEqual+        "vector 6 (encode · decode)"+        0x04923456+        (encodeCompact . fst $ decodeCompact 0x04923456)+    assertEqual+        "vector 7 (decode)"+        (0x92340000, False)+        (decodeCompact 0x05009234)+    assertEqual+        "vector 7 (encode · decode)"+        0x05009234+        (encodeCompact . fst $ decodeCompact 0x05009234)+    assertEqual+        "vector 8 (decode)"+        ( 0x1234560000000000000000000000000000000000000000000000000000000000+        , False)+        (decodeCompact 0x20123456)+    assertEqual+        "vector 8 (encode · decode)"+        0x20123456+        (encodeCompact . fst $ decodeCompact 0x20123456)+    assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)+    assertBool+        "vector 9 (decode) (positive)"+        ((> 0) . fst $ decodeCompact 0xff123456)++runMerkleVector :: (Text, [Text]) -> Assertion+runMerkleVector (r, hs) =+    assertBool "merkle vector" $+        buildMerkleRoot (map f hs) == getTxHash (f r)+  where+    f = fromJust . hexToTxHash++merkleVectors :: [(Text, [Text])]+merkleVectors =+      -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1+    [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f"+      , [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52"+        , "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9"+        , "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41"+        , "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f"+        , "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6"+        , "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc"+        , "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3"+        , "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66"+        , "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f"+        , "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4"+        , "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"+        ]+      )+      -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4+    , ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c"+      , [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e"+        , "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51"+        , "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a"+        , "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e"+        , "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471"+        , "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3"+        , "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030"+        , "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13"+        , "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2"+        , "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"+        ]+      )+      -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048+    , ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"+      , [ "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" ]+      )+      -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c+    , ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c"+      , [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028"+        , "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"+        ]+      )+      -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e+    , ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c"+      , [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549"+        , "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb"+        , "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"+        ]+      )+      -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021+    , ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2"+      , [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81"+        , "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92"+        , "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27"+        , "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a"+        , "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924"+        , "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2"+        , "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95"+        , "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4"+        , "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d"+        , "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f"+        , "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909"+        , "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d"+        , "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53"+        , "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b"+        , "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174"+        , "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494"+        , "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4"+        , "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa"+        , "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d"+        , "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6"+        , "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9"+        , "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55"+        , "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776"+        , "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d"+        , "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666"+        , "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f"+        , "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9"+        , "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4"+        , "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"+        ]+      )+    ]++testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+testID x =+    (A.decode . A.encode) (singleton ("object" :: String) x) ==+    Just (singleton ("object" :: String) x)++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = S.decode (S.encode x) == Right x++testSubsidy :: Network -> Assertion+testSubsidy net = go (2 * 50 * 100 * 1000 * 1000) 0+  where+    go previous_subsidy halvings = do+        let height = halvings * getHalvingInterval net+            subsidy = computeSubsidy net height+        if halvings >= 64+            then subsidy `shouldBe` 0+            else do+                subsidy `shouldBe` (previous_subsidy `div` 2)+                go subsidy (halvings + 1)
+ test/Haskoin/Crypto/HashSpec.hs view
@@ -0,0 +1,1269 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Crypto.HashSpec (spec) where++import           Data.ByteString         (ByteString)+import           Data.Map.Strict         (singleton)+import           Data.Maybe              (fromJust)+import           Data.Serialize          as S+import           Data.String             (fromString)+import           Data.String.Conversions+import           Haskoin.Block+import           Haskoin.Crypto+import           Haskoin.Test+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit              (Assertion, assertBool)+import           Test.QuickCheck++-- Test vectors from NIST+-- http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip+-- About 1/3 of HMAC DRBG SHA-256 test vectors are tested here++spec :: Spec+spec =+    describe "hash" $ do+        it "join512( split512(h) ) == h" $+            property $+            forAll arbitraryHash256 $ forAll arbitraryHash256 . joinSplit512+        it "decodeCompact . encodeCompact i == i" $ property decEncCompact+        it "from string 64-byte hash" $+            property $+            forAll arbitraryHash512 $ \h ->+                fromString (cs $ encodeHex $ encode h) == h+        it "from string 32-byte hash" $+            property $+            forAll arbitraryHash256 $ \h ->+                fromString (cs $ encodeHex $ encode h) == h+        it "from string 20-byte hash" $+            property $+            forAll arbitraryHash160 $ \h ->+                fromString (cs $ encodeHex $ encode h) == h+        it "encodes and decodes bytestring" $+            property $ forAll arbitraryBS cerealID+        it "encodes and decodes hash160" $+            property $ forAll arbitraryHash160 cerealID+        it "encodes and decodes hash256" $+            property $ forAll arbitraryHash256 cerealID+        it "encodes and decodes hash512" $+            property $ forAll arbitraryHash512 cerealID++type TestVector = [ByteString]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 0]+    [AdditionalInputLen = 0]+    [ReturnedBitsLen = 1024]+-}++joinSplit512 :: Hash256 -> Hash256 -> Bool+joinSplit512 a b = split512 (join512 (a, b)) == (a, b)++-- After encoding and decoding, we may loose precision so the new result is >=+-- to the old one.+decEncCompact :: Integer -> Bool+decEncCompact i+    -- Integer completely fits inside the mantisse+    | abs i <= 0x007fffff = decodeCompact (encodeCompact i) == (i, False)+    -- Otherwise precision will be lost and the decoded result will+    -- be smaller than the original number+    | i >= 0              = fst (decodeCompact (encodeCompact i)) < i+    | otherwise           = fst (decodeCompact (encodeCompact i)) > i++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = decode (encode x) == Right x++t1 :: [TestVector]+t1 =+    [+    -- COUNT = 0+    [ "ca851911349384bffe89de1cbdc46e6831e44d34a4fb935ee285dd14b71a7488"+    , "659ba96c601dc69fc902940805ec0ca8"+    , ""+    , ""+    , ""+    , "e528e9abf2dece54d47c7e75e5fe302149f817ea9fb4bee6f4199697d04d5b89d54fbb978a15b5c443c9ec21036d2460b6f73ebad0dc2aba6e624abf07745bc107694bb7547bb0995f70de25d6b29e2d3011bb19d27676c07162c8b5ccde0668961df86803482cb37ed6d5c0bb8d50cf1f50d476aa0458bdaba806f48be9dcb8"+    ],+    -- COUNT = 1+    [ "79737479ba4e7642a221fcfd1b820b134e9e3540a35bb48ffae29c20f5418ea3"+    , "3593259c092bef4129bc2c6c9e19f343"+    , ""+    , ""+    , ""+    , "cf5ad5984f9e43917aa9087380dac46e410ddc8a7731859c84e9d0f31bd43655b924159413e2293b17610f211e09f770f172b8fb693a35b85d3b9e5e63b1dc252ac0e115002e9bedfb4b5b6fd43f33b8e0eafb2d072e1a6fee1f159df9b51e6c8da737e60d5032dd30544ec51558c6f080bdbdab1de8a939e961e06b5f1aca37"+    ],+    -- COUNT = 2+    [ "b340907445b97a8b589264de4a17c0bea11bb53ad72f9f33297f05d2879d898d"+    , "65cb27735d83c0708f72684ea58f7ee5"+    , ""+    , ""+    , ""+    , "75183aaaf3574bc68003352ad655d0e9ce9dd17552723b47fab0e84ef903694a32987eeddbdc48efd24195dbdac8a46ba2d972f5808f23a869e71343140361f58b243e62722088fe10a98e43372d252b144e00c89c215a76a121734bdc485486f65c0b16b8963524a3a70e6f38f169c12f6cbdd169dd48fe4421a235847a23ff"+    ],+    -- COUNT = 3+    [ "8e159f60060a7d6a7e6fe7c9f769c30b98acb1240b25e7ee33f1da834c0858e7"+    , "c39d35052201bdcce4e127a04f04d644"+    , ""+    , ""+    , ""+    , "62910a77213967ea93d6457e255af51fc79d49629af2fccd81840cdfbb4910991f50a477cbd29edd8a47c4fec9d141f50dfde7c4d8fcab473eff3cc2ee9e7cc90871f180777a97841597b0dd7e779eff9784b9cc33689fd7d48c0dcd341515ac8fecf5c55a6327aea8d58f97220b7462373e84e3b7417a57e80ce946d6120db5"+    ],+    -- COUNT = 4+    [ "74755f196305f7fb6689b2fe6835dc1d81484fc481a6b8087f649a1952f4df6a"+    , "c36387a544a5f2b78007651a7b74b749"+    , ""+    , ""+    , ""+    , "b2896f3af4375dab67e8062d82c1a005ef4ed119d13a9f18371b1b873774418684805fd659bfd69964f83a5cfe08667ddad672cafd16befffa9faed49865214f703951b443e6dca22edb636f3308380144b9333de4bcb0735710e4d9266786342fc53babe7bdbe3c01a3addb7f23c63ce2834729fabbd419b47beceb4a460236"+    ],+    -- COUNT = 5+    [ "4b222718f56a3260b3c2625a4cf80950b7d6c1250f170bd5c28b118abdf23b2f"+    , "7aed52d0016fcaef0b6492bc40bbe0e9"+    , ""+    , ""+    , ""+    , "a6da029b3665cd39fd50a54c553f99fed3626f4902ffe322dc51f0670dfe8742ed48415cf04bbad5ed3b23b18b7892d170a7dcf3ef8052d5717cb0c1a8b3010d9a9ea5de70ae5356249c0e098946030c46d9d3d209864539444374d8fbcae068e1d6548fa59e6562e6b2d1acbda8da0318c23752ebc9be0c1c1c5b3cf66dd967"+    ],+    -- COUNT = 6+    [ "b512633f27fb182a076917e39888ba3ff35d23c3742eb8f3c635a044163768e0"+    , "e2c39b84629a3de5c301db5643af1c21"+    , ""+    , ""+    , ""+    , "fb931d0d0194a97b48d5d4c231fdad5c61aedf1c3a55ac24983ecbf38487b1c93396c6b86ff3920cfa8c77e0146de835ea5809676e702dee6a78100da9aa43d8ec0bf5720befa71f82193205ac2ea403e8d7e0e6270b366dc4200be26afd9f63b7e79286a35c688c57cbff55ac747d4c28bb80a2b2097b3b62ea439950d75dff"+    ],+    -- COUNT = 7+    [ "aae3ffc8605a975befefcea0a7a286642bc3b95fb37bd0eb0585a4cabf8b3d1e"+    , "9504c3c0c4310c1c0746a036c91d9034"+    , ""+    , ""+    , ""+    , "2819bd3b0d216dad59ddd6c354c4518153a2b04374b07c49e64a8e4d055575dfbc9a8fcde68bd257ff1ba5c6000564b46d6dd7ecd9c5d684fd757df62d85211575d3562d7814008ab5c8bc00e7b5a649eae2318665b55d762de36eba00c2906c0e0ec8706edb493e51ca5eb4b9f015dc932f262f52a86b11c41e9a6d5b3bd431"+    ],+    -- COUNT = 8+    [ "b9475210b79b87180e746df704b3cbc7bf8424750e416a7fbb5ce3ef25a82cc6"+    , "24baf03599c10df6ef44065d715a93f7"+    , ""+    , ""+    , ""+    , "ae12d784f796183c50db5a1a283aa35ed9a2b685dacea97c596ff8c294906d1b1305ba1f80254eb062b874a8dfffa3378c809ab2869aa51a4e6a489692284a25038908a347342175c38401193b8afc498077e10522bec5c70882b7f760ea5946870bd9fc72961eedbe8bff4fd58c7cc1589bb4f369ed0d3bf26c5bbc62e0b2b2"+    ],+    -- COUNT = 9+    [ "27838eb44ceccb4e36210703ebf38f659bc39dd3277cd76b7a9bcd6bc964b628"+    , "39cfe0210db2e7b0eb52a387476e7ea1"+    , ""+    , ""+    , ""+    , "e5e72a53605d2aaa67832f97536445ab774dd9bff7f13a0d11fd27bf6593bfb52309f2d4f09d147192199ea584503181de87002f4ee085c7dc18bf32ce5315647a3708e6f404d6588c92b2dda599c131aa350d18c747b33dc8eda15cf40e95263d1231e1b4b68f8d829f86054d49cfdb1b8d96ab0465110569c8583a424a099a"+    ],+    -- COUNT = 10+    [ "d7129e4f47008ad60c9b5d081ff4ca8eb821a6e4deb91608bf4e2647835373a5"+    , "a72882773f78c2fc4878295840a53012"+    , ""+    , ""+    , ""+    , "0cbf48585c5de9183b7ff76557f8fc9ebcfdfde07e588a8641156f61b7952725bbee954f87e9b937513b16bba0f2e523d095114658e00f0f3772175acfcb3240a01de631c19c5a834c94cc58d04a6837f0d2782fa53d2f9f65178ee9c837222494c799e64c60406069bd319549b889fa00a0032dd7ba5b1cc9edbf58de82bfcd"+    ],+    -- COUNT = 11+    [ "67fe5e300c513371976c80de4b20d4473889c9f1214bce718bc32d1da3ab7532"+    , "e256d88497738a33923aa003a8d7845c"+    , ""+    , ""+    , ""+    , "b44660d64ef7bcebc7a1ab71f8407a02285c7592d755ae6766059e894f694373ed9c776c0cfc8594413eefb400ed427e158d687e28da3ecc205e0f7370fb089676bbb0fa591ec8d916c3d5f18a3eb4a417120705f3e2198154cd60648dbfcfc901242e15711cacd501b2c2826abe870ba32da785ed6f1fdc68f203d1ab43a64f"+    ],+    -- COUNT = 12+    [ "de8142541255c46d66efc6173b0fe3ffaf5936c897a3ce2e9d5835616aafa2cb"+    , "d01f9002c407127bc3297a561d89b81d"+    , ""+    , ""+    , ""+    , "64d1020929d74716446d8a4e17205d0756b5264867811aa24d0d0da8644db25d5cde474143c57d12482f6bf0f31d10af9d1da4eb6d701bdd605a8db74fb4e77f79aaa9e450afda50b18d19fae68f03db1d7b5f1738d2fdce9ad3ee9461b58ee242daf7a1d72c45c9213eca34e14810a9fca5208d5c56d8066bab1586f1513de7"+    ],+    -- COUNT = 13+    [ "4a8e0bd90bdb12f7748ad5f147b115d7385bb1b06aee7d8b76136a25d779bcb7"+    , "7f3cce4af8c8ce3c45bdf23c6b181a00"+    , ""+    , ""+    , ""+    , "320c7ca4bbeb7af977bc054f604b5086a3f237aa5501658112f3e7a33d2231f5536d2c85c1dad9d9b0bf7f619c81be4854661626839c8c10ae7fdc0c0b571be34b58d66da553676167b00e7d8e49f416aacb2926c6eb2c66ec98bffae20864cf92496db15e3b09e530b7b9648be8d3916b3c20a3a779bec7d66da63396849aaf"+    ],+    -- COUNT = 14+    [ "451ed024bc4b95f1025b14ec3616f5e42e80824541dc795a2f07500f92adc665"+    , "2f28e6ee8de5879db1eccd58c994e5f0"+    , ""+    , ""+    , ""+    , "3fb637085ab75f4e95655faae95885166a5fbb423bb03dbf0543be063bcd48799c4f05d4e522634d9275fe02e1edd920e26d9accd43709cb0d8f6e50aa54a5f3bdd618be23cf73ef736ed0ef7524b0d14d5bef8c8aec1cf1ed3e1c38a808b35e61a44078127c7cb3a8fd7addfa50fcf3ff3bc6d6bc355d5436fe9b71eb44f7fd"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 0]+    [AdditionalInputLen = 256]+    [ReturnedBitsLen = 1024]+-}++t2 :: [TestVector]+t2 =+    [+    -- COUNT = 0+    [ "d3cc4d1acf3dde0c4bd2290d262337042dc632948223d3a2eaab87da44295fbd"+    , "0109b0e729f457328aa18569a9224921"+    , ""+    , "3c311848183c9a212a26f27f8c6647e40375e466a0857cc39c4e47575d53f1f6"+    , "fcb9abd19ccfbccef88c9c39bfb3dd7b1c12266c9808992e305bc3cff566e4e4"+    , "9c7b758b212cd0fcecd5daa489821712e3cdea4467b560ef5ddc24ab47749a1f1ffdbbb118f4e62fcfca3371b8fbfc5b0646b83e06bfbbab5fac30ea09ea2bc76f1ea568c9be0444b2cc90517b20ca825f2d0eccd88e7175538b85d90ab390183ca6395535d34473af6b5a5b88f5a59ee7561573337ea819da0dcc3573a22974"+    ],+    -- COUNT = 1+    [ "f97a3cfd91faa046b9e61b9493d436c4931f604b22f1081521b3419151e8ff06"+    , "11f3a7d43595357d58120bd1e2dd8aed"+    , ""+    , "517289afe444a0fe5ed1a41dbbb5eb17150079bdd31e29cf2ff30034d8268e3b"+    , "88028d29ef80b4e6f0fe12f91d7449fe75062682e89c571440c0c9b52c42a6e0"+    , "c6871cff0824fe55ea7689a52229886730450e5d362da5bf590dcf9acd67fed4cb32107df5d03969a66b1f6494fdf5d63d5b4d0d34ea7399a07d0116126d0d518c7c55ba46e12f62efc8fe28a51c9d428e6d371d7397ab319fc73ded4722e5b4f30004032a6128df5e7497ecf82ca7b0a50e867ef6728a4f509a8c859087039c"+    ],+    -- COUNT = 2+    [ "0f2f23d64f481cabec7abb01db3aabf125c3173a044b9bf26844300b69dcac8b"+    , "9a5ae13232b43aa19cfe8d7958b4b590"+    , ""+    , "ec4c7a62acab73385f567da10e892ff395a0929f959231a5628188ce0c26e818"+    , "6b97b8c6b6bb8935e676c410c17caa8042aa3145f856d0a32b641e4ae5298648"+    , "7480a361058bd9afa3db82c9d7586e42269102013f6ec5c269b6d05f17987847748684766b44918fd4b65e1648622fc0e0954178b0279dfc9fa99b66c6f53e51c4860131e9e0644287a4afe4ca8e480417e070db68008a97c3397e4b320b5d1a1d7e1d18a95cfedd7d1e74997052bf649d132deb9ec53aae7dafdab55e6dae93"+    ],+    -- COUNT = 3+    [ "53c56660c78481be9c63284e005fcc14fbc7fb27732c9bf1366d01a426765a31"+    , "dc7a14d0eb5b0b3534e717a0b3c64614"+    , ""+    , "3aa848706ecb877f5bedf4ffc332d57c22e08747a47e75cff6f0fd1316861c95"+    , "9a401afa739b8f752fddacd291e0b854f5eff4a55b515e20cb319852189d3722"+    , "5c0eb420e0bf41ce9323e815310e4e8303cd677a8a8b023f31f0d79f0ca15aeb636099a369fd074d69889865eac1b72ab3cbfebdb8cf460b00072802e2ec648b1349a5303be4ccaadd729f1a9ea17482fd026aaeb93f1602bc1404b9853adde40d6c34b844cf148bc088941ecfc1642c8c0b9778e45f3b07e06e21ee2c9e0300"+    ],+    -- COUNT = 4+    [ "f63c804404902db334c54bb298fc271a21d7acd9f770278e089775710bf4fdd7"+    , "3e45009ea9cb2a36ba1aa4bf39178200"+    , ""+    , "d165a13dc8cc43f3f0952c3f5d3de4136954d983683d4a3e6d2dc4c89bf23423"+    , "75106bc86d0336df85097f6af8e80e2da59046a03fa65b06706b8bbc7ffc6785"+    , "6363139bba32c22a0f5cd23ca6d437b5669b7d432f786b8af445471bee0b2d24c9d5f2f93717cbe00d1f010cc3b9c515fc9f7336d53d4d26ba5c0d76a90186663c8582eb739c7b6578a3328bf68dc2cec2cd89b3a90201f6993adcc854df0f5c6974d0f5570765a15fe03dbce28942dd2fd16ba2027e68abac83926969349af8"+    ],+    -- COUNT = 5+    [ "2aaca9147da66c176615726b69e3e851cc3537f5f279fe7344233d8e44cfc99d"+    , "4e171f080af9a6081bee9f183ac9e340"+    , ""+    , "d75a2a6eb66c3833e50f5ec3d2e434cf791448d618026d0c360806d120ded669"+    , "b643b74c15b37612e6577ed7ca2a4c67a78d560af9eb50a4108fca742e87b8d6"+    , "501dcdc977f4ba856f24eaa4968b374bebb3166b280334cb510232c31ebffde10fa47b7840ef3fe3b77725c2272d3a1d4219baf23e0290c622271edcced58838cf428f0517425d2e19e0d8c89377eecfc378245f283236fafa466c914b99672ceafab369e8889a0c866d8bd639db9fb797254262c6fd44cfa9045ad6340a60ef"+    ],+    -- COUNT = 6+    [ "a2e4cd48a5cf918d6f55942d95fcb4e8465cdc4f77b7c52b6fae5b16a25ca306"+    , "bef036716440db6e6d333d9d760b7ca8"+    , ""+    , "bfa591c7287f3f931168f95e38869441d1f9a11035ad8ea625bb61b9ea17591c"+    , "c00c735463bca215adc372cb892b05e939bf669583341c06d4e31d0e5b363a37"+    , "e7d136af69926a5421d4266ee0420fd729f2a4f7c295d3c966bdfa05268180b508b8a2852d1b3a06fd2ab3e13c54005123ef319f42d0c6d3a575e6e7e1496cb28aacadbcf83740fba8f35fcee04bb2ed8a51db3d3362b01094a62fb57e33c99a432f29fce6676cffbbcc05107e794e75e44a02d5e6d9d748c5fbff00a0178d65"+    ],+    -- COUNT = 7+    [ "95a67771cba69011a79776e713145d309edae56fad5fd6d41d83eaff89df6e5e"+    , "be5b5164e31ecc51ba6f7c3c5199eb33"+    , ""+    , "065f693b229a7c4fd373cd15b3807552dd9bf98c5485cef361949d4e7d774b53"+    , "9afb62406f0e812c4f156d58b19a656c904813c1b4a45a0029ae7f50731f8014"+    , "f61b61a6e79a41183e8ed6647899d2dc85cdaf5c3abf5c7f3bf37685946dc28f4923dc842f2d4326bd6ce0d50a84cb3ba869d72a36e246910eba6512ba36cd7ed3a5437c9245b00a344308c792b668b458d3c3e16dee2fbec41867da31084d46d8ec168de2148ef64fc5b72069abf5a6ada1ead2b7146bb793ff1c9c3690fa56"+    ],+    -- COUNT = 8+    [ "a459e1815cbca4514ec8094d5ab2414a557ba6fe10e613c345338d0521e4bf90"+    , "62221392e2552e76cd0d36df6e6068eb"+    , ""+    , "0a3642b02b23b3ef62c701a63401124022f5b896de86dab6e6c7451497aa1dcc"+    , "c80514865901371c45ba92d9f95d50bb7c9dd1768cb3dfbc45b968da94965c6e"+    , "464e6977b8adaef307c9623e41c357013249c9ffd77f405f3925cebb69f151ce8fbb6a277164002aee7858fc224f6499042aa1e6322deee9a5d133c31d640e12a7487c731ba03ad866a24675badb1d79220c40be689f79c2a0be93cb4dada3e0eac4ab140cb91998b6f11953e68f2319b050c40f71c34de9905ae41b2de1c2f6"+    ],+    -- COUNT = 9+    [ "252c2cad613e002478162861880979ee4e323025eebb6fb2e0aa9f200e28e0a1"+    , "d001bc9a8f2c8c242e4369df0c191989"+    , ""+    , "9bcfc61cb2bc000034bb3db980eb47c76fb5ecdd40553eff113368d639b947fd"+    , "8b0565c767c2610ee0014582e9fbecb96e173005b60e9581503a6dca5637a26e"+    , "e96c15fe8a60692b0a7d67171e0195ff6e1c87aab844221e71700d1bbee75feea695f6a740c9760bbe0e812ecf4061d8f0955bc0195e18c4fd1516ebca50ba6a6db86881737dbab8321707675479b87611db6af2c97ea361a5484555ead454defb1a64335de964fc803d40f3a6f057893d2afc25725754f4f00abc51920743dc"+    ],+    -- COUNT = 10+    [ "8be0ca6adc8b3870c9d69d6021bc1f1d8eb9e649073d35ee6c5aa0b7e56ad8a5"+    , "9d1265f7d51fdb65377f1e6edd6ae0e4"+    , ""+    , "da86167ac997c406bb7979f423986a84ec6614d6caa7afc10aff0699a9b2cf7f"+    , "e4baa3c555950b53e2bfdba480cb4c94b59381bac1e33947e0c22e838a9534cf"+    , "64384ecc4ea6b458efc227ca697eac5510092265520c0a0d8a0ccf9ed3ca9d58074671188c6a7ad16d0b050cdc072c125d7298d3a31d9f044a9ee40da0089a84fea28cc7f05f1716db952fad29a0e779635cb7a912a959be67be2f0a4170aace2981802e2ff6467e5b46f0ffbff3b42ba5935fd553c82482ac266acf1cd247d7"+    ],+    -- COUNT = 11+    [ "d43a75b6adf26d60322284cb12ac38327792442aa8f040f60a2f331b33ac4a8f"+    , "0682f8b091f811afacaacaec9b04d279"+    , ""+    , "7fd3b8f512940da7de5d80199d9a7b42670c04a945775a3dba869546cbb9bc65"+    , "2575db20bc7aafc2a90a5dabab760db851d754777bc9f05616af1858b24ff3da"+    , "0da7a8dc73c163014bf0841913d3067806456bbca6d5de92b85534c6545467313648d71ef17c923d090dc92cff8d4d1a9a2bb63e001dc2e8ab1a597999be3d6cf70ff63fee9985801395fbd4f4990430c4259fcae4fa1fcd73dc3187ccc102d04af7c07532885e5a226fc42809c48f22eecf4f6ab996ae4fcb144786957d9f41"+    ],+    -- COUNT = 12+    [ "64352f236af5d32067a529a8fd05ba00a338c9de306371a0b00c36e610a48d18"+    , "df99ed2c7608c870624b962a5dc68acd"+    , ""+    , "da416335e7aaf60cf3d06fb438735ce796aad09034f8969c8f8c3f81e32fef24"+    , "a28c07c21a2297311adf172c19e83ca0a87731bdffb80548978d2d1cd82cf8a3"+    , "132b9f25868729e3853d3c51f99a3b5fae6d4204bea70890daf62e042b776a526c8fb831b80a6d5d3f153237df1fd39b6fd9137963f5516d9cdd4e3f9195c46e9972c15d3edc6606e3368bde1594977fb88d0ca6e6f5f3d057ccadc7d7dab77dfc42658a1e972aa446b20d418286386a52dfc1c714d2ac548713268b0b709729"+    ],+    -- COUNT = 13+    [ "282f4d2e05a2cd30e9087f5633089389449f04bac11df718c90bb351cd3653a5"+    , "90a7daf3c0de9ea286081efc4a684dfb"+    , ""+    , "2630b4ccc7271cc379cb580b0aaede3d3aa8c1c7ba002cf791f0752c3d739007"+    , "c31d69de499f1017be44e3d4fa77ecebc6a9b9934749fcf136f267b29115d2cc"+    , "c899094520e0197c37b91dd50778e20a5b950decfb308d39f1db709447ae48f6101d9abe63a783fbb830eec1d359a5f61a2013728966d349213ee96382614aa4135058a967627183810c6622a2158cababe3b8ab99169c89e362108bf5955b4ffc47440f87e4bad0d36bc738e737e072e64d8842e7619f1be0af1141f05afe2d"+    ],+    -- COUNT = 14+    [ "13c752b9e745ce77bbc7c0dbda982313d3fe66f903e83ebd8dbe4ff0c11380e9"+    , "f1a533095d6174164bd7c82532464ae7"+    , ""+    , "4f53db89b9ba7fc00767bc751fb8f3c103fe0f76acd6d5c7891ab15b2b7cf67c"+    , "582c2a7d34679088cca6bd28723c99aac07db46c332dc0153d1673256903b446"+    , "6311f4c0c4cd1f86bd48349abb9eb930d4f63df5e5f7217d1d1b91a71d8a6938b0ad2b3e897bd7e3d8703db125fab30e03464fad41e5ddf5bf9aeeb5161b244468cfb26a9d956931a5412c97d64188b0da1bd907819c686f39af82e91cfeef0cbffb5d1e229e383bed26d06412988640706815a6e820796876f416653e464961"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 256]+    [AdditionalInputLen = 0]+    [ReturnedBitsLen = 1024]+-}++t3 :: [TestVector]+t3 =+    [+    -- COUNT = 0+    [ "5cacc68165a2e2ee20812f35ec73a79dbf30fd475476ac0c44fc6174cdac2b55"+    , "6f885496c1e63af620becd9e71ecb824"+    , "e72dd8590d4ed5295515c35ed6199e9d211b8f069b3058caa6670b96ef1208d0"+    , ""+    , ""+    , "f1012cf543f94533df27fedfbf58e5b79a3dc517a9c402bdbfc9a0c0f721f9d53faf4aafdc4b8f7a1b580fcaa52338d4bd95f58966a243cdcd3f446ed4bc546d9f607b190dd69954450d16cd0e2d6437067d8b44d19a6af7a7cfa8794e5fbd728e8fb2f2e8db5dd4ff1aa275f35886098e80ff844886060da8b1e7137846b23b"+    ],+    -- COUNT = 1+    [ "8df013b4d103523073917ddf6a869793059e9943fc8654549e7ab22f7c29f122"+    , "da2625af2ddd4abcce3cf4fa4659d84e"+    , "b571e66d7c338bc07b76ad3757bb2f9452bf7e07437ae8581ce7bc7c3ac651a9"+    , ""+    , ""+    , "b91cba4cc84fa25df8610b81b641402768a2097234932e37d590b1154cbd23f97452e310e291c45146147f0da2d81761fe90fba64f94419c0f662b28c1ed94da487bb7e73eec798fbcf981b791d1be4f177a8907aa3c401643a5b62b87b89d66b3a60e40d4a8e4e9d82af6d2700e6f535cdb51f75c321729103741030ccc3a56"+    ],+    -- COUNT = 2+    [ "565b2b77937ba46536b0f693b3d5e4a8a24563f9ef1f676e8b5b2ef17823832f"+    , "4ef3064ec29f5b7f9686d75a23d170e3"+    , "3b722433226c9dba745087270ab3af2c909425ba6d39f5ce46f07256068319d9"+    , ""+    , ""+    , "d144ee7f8363d128872f82c15663fe658413cd42651098e0a7c51a970de75287ec943f9061e902280a5a9e183a7817a44222d198fbfab184881431b4adf35d3d1019da5a90b3696b2349c8fba15a56d0f9d010a88e3f9eeedb67a69bcaa71281b41afa11af576b765e66858f0eb2e4ec4081609ec81da81df0a0eb06787340ea"+    ],+    -- COUNT = 3+    [ "fc3832a91b1dcdcaa944f2d93cbceb85c267c491b7b59d017cde4add79a836b6"+    , "d5e76ce9eabafed06e33a913e395c5e0"+    , "ffc5f6eefd51da64a0f67b5f0cf60d7ab43fc7836bca650022a0cee57a43c148"+    , ""+    , ""+    , "0e713c6cc9a4dbd4249201d12b7bf5c69c3e18eb504bf3252db2f43675e17d99b6a908400cea304011c2e54166dae1f20260008efe4e06a87e0ce525ca482bca223a902a14adcf2374a739a5dfeaf14cadd72efa4d55d15154c974d9521535bcb70658c5b6c944020afb04a87b223b4b8e5d89821704a9985bb010405ba8f3d4"+    ],+    -- COUNT = 4+    [ "8009eb2cb49fdf16403bcdfd4a9f952191062acb9cc111eca019f957fb9f4451"+    , "355598866952394b1eddd85d59f81c9d"+    , "09ff1d4b97d83b223d002e05f754be480d13ba968e5aac306d71cc9fc49cc2dd"+    , ""+    , ""+    , "9550903c2f02cf77c8f9c9a37041d0040ee1e3ef65ba1a1fbbcf44fb7a2172bd6b3aaabe850281c3a1778277bacd09614dfefececac64338ae24a1bf150cbf9d9541173a82ecba08aa19b75abb779eb10efa4257d5252e8afcac414bc3bb5d3006b6f36fb9daea4c8c359ef6cdbeff27c1068571dd3c89dc87eda9190086888d"+    ],+    -- COUNT = 5+    [ "a6e4c9a8bd6da23b9c2b10a7748fd08c4f782fadbac7ea501c17efdc6f6087bd"+    , "acdc47edf1d3b21d0aec7631abb6d7d5"+    , "c16ee0908a5886dccf332fbc61de9ec7b7972d2c4c83c477409ce8a15c623294"+    , ""+    , ""+    , "a52f93ccb363e2bdf0903622c3caedb7cffd04b726052b8d455744c71b76dee1b71db9880dc3c21850489cb29e412d7d80849cfa9151a151dcbf32a32b4a54cac01d3200200ed66a3a5e5c131a49655ffbf1a8824ff7f265690dffb4054df46a707b9213924c631c5bce379944c856c4f7846e281ac89c64fad3a49909dfb92b"+    ],+    -- COUNT = 6+    [ "59d6307460a9bdd392dfc0904973991d585696010a71e52d590a5039b4849fa4"+    , "34a0aafb95917cbf8c38fc5548373c05"+    , "0407b7c57bc11361747c3d67526c36e228028a5d0b145d66ab9a2fe4b07507a0"+    , ""+    , ""+    , "299aba0661315211b09d2861855d0b4b125ab24649461341af6abd903ed6f025223b3299f2126fcad44c675166d800619cf49540946b12138989417904324b0ddad121327211a297f11259c9c34ce4c70c322a653675f78d385e4e2443f8058d141195e17e0bd1b9d44bf3e48c376e6eb44ef020b11cf03eb141c46ecb43cf3d"+    ],+    -- COUNT = 7+    [ "9ae3506aadbc8358696ba1ba17e876e1157b7048235921503d36d9211b430342"+    , "9abf7d66afee5d2b811cba358bbc527d"+    , "0d645f6238e9ceb038e4af9772426ca110c5be052f8673b8b5a65c4e53d2f519"+    , ""+    , ""+    , "5f032c7fec6320fe423b6f38085cbad59d826085afe915247b3d546c4c6b174554dd4877c0d671de9554b505393a44e71f209b70f991ac8aa6e08f983fff2a4c817b0cd26c12b2c929378506489a75b2025b358cb5d0400821e7e252ac6376cd94a40c911a7ed8b6087e3de5fa39fa6b314c3ba1c593b864ce4ff281a97c325b"+    ],+    -- COUNT = 8+    [ "96ae3b8775b36da2a29b889ad878941f43c7d51295d47440cd0e3c4999193109"+    , "1fe022a6fc0237b055d4d6a7036b18d5"+    , "1e40e97362d0a823d3964c26b81ab53825c56446c5261689011886f19b08e5c2"+    , ""+    , ""+    , "e707cd14b06ce1e6dbcceaedbf08d88891b03f44ad6a797bd12fdeb557d0151df9346a028dec004844ca46adec3051dafb345895fa9f4604d8a13c8ff66ae093fa63c4d9c0816d55a0066d31e8404c841e87b6b2c7b5ae9d7afb6840c2f7b441bf2d3d8bd3f40349c1c014347c1979213c76103e0bece26ad7720601eff42275"+    ],+    -- COUNT = 9+    [ "33f5120396336e51ee3b0b619b5f873db05ca57cda86aeae2964f51480d14992"+    , "6f1f6e9807ba5393edcf3cb4e4bb6113"+    , "3709605af44d90196867c927512aa8ba31837063337b4879408d91a05c8efa9f"+    , ""+    , ""+    , "8b8291126ded9acef12516025c99ccce225d844308b584b872c903c7bc6467599a1cead003dc4c70f6d519f5b51ce0da57f53da90dbe8f666a1a1dde297727fee2d44cebd1301fc1ca75956a3fcae0d374e0df6009b668fd21638d2b733e6902d22d5bfb4af1b455975e08eef0ebe4dc87705801e7776583c8de11672729f723"+    ],+    -- COUNT = 10+    [ "ad300b799005f290fee7f930eebce158b98fb6cb449987fe433f955456b35300"+    , "06aa2514e4bd114edf7ac105cfef2772"+    , "87ada711465e4169da2a74c931afb9b5a5b190d07b7af342aa99570401c3ee8a"+    , ""+    , ""+    , "80d7c606ff49415a3a92ba1f2943235c01339c8f9cd0b0511fbfdf3ef23c42ffff008524193faaa4b7f2f2eb0cfa221d9df89bd373fe4e158ec06fad3ecf1eb48b8239b0bb826ee69d773883a3e8edac66254610ff70b6609836860e39ea1f3bfa04596fee1f2baca6cebb244774c6c3eb4af1f02899eba8f4188f91776de16f"+    ],+    -- COUNT = 11+    [ "130b044e2c15ab89375e54b72e7baae6d4cad734b013a090f4df057e634f6ff0"+    , "65fd6ac602cd44107d705dbc066e52b6"+    , "f374aba16f34d54aae5e494505b67d3818ef1c08ea24967a76876d4361379aec"+    , ""+    , ""+    , "5d179534fb0dba3526993ed8e27ec9f915183d967336bb24352c67f4ab5d7935d3168e57008da851515efbaecb69904b6d899d3bfa6e9805659aef2942c4903875b8fcbc0d1d24d1c075f0ff667c1fc240d8b410dff582fa71fa30878955ce2ed786ef32ef852706e62439b69921f26e84e0f54f62b938f04905f05fcd7c2204"+    ],+    -- COUNT = 12+    [ "716430e999964b35459c17921fe5f60e09bd9ab234cb8f4ba4932bec4a60a1d5"+    , "9533b711e061b07d505da707cafbca03"+    , "372ae616d1a1fc45c5aecad0939c49b9e01c93bfb40c835eebd837af747f079d"+    , ""+    , ""+    , "a80d6a1b2d0ce01fe0d26e70fb73da20d45841cf01bfbd50b90d2751a46114c0e758cb787d281a0a9cf62f5c8ce2ee7ca74fefff330efe74926acca6d6f0646e4e3c1a1e52fce1d57b88beda4a5815896f25f38a652cc240deb582921c8b1d03a1da966dd04c2e7eee274df2cd1837096b9f7a0d89a82434076bc30173229a60"+    ],+    -- COUNT = 13+    [ "7679f154296e6d580854826539003a82d1c54e2e062c619d00da6c6ac820789b"+    , "55d12941b0896462e7d888e5322a99a3"+    , "ba4d1ed696f58ef64596c76cee87cc1ca83069a79e7982b9a06f9d62f4209faf"+    , ""+    , ""+    , "10dc7cd2bb68c2c28f76d1b04ae2aa287071e04c3b688e1986b05cc1209f691daa55868ebb05b633c75a40a32b49663185fe5bb8f906008347ef51590530948b87613920014802e5864e0758f012e1eae31f0c4c031ef823aecfb2f8a73aaa946fc507037f9050b277bdeaa023123f9d22da1606e82cb7e56de34bf009eccb46"+    ],+    -- COUNT = 14+    [ "8ca4a964e1ff68753db86753d09222e09b888b500be46f2a3830afa9172a1d6d"+    , "a59394e0af764e2f21cf751f623ffa6c"+    , "eb8164b3bf6c1750a8de8528af16cffdf400856d82260acd5958894a98afeed5"+    , ""+    , ""+    , "fc5701b508f0264f4fdb88414768e1afb0a5b445400dcfdeddd0eba67b4fea8c056d79a69fd050759fb3d626b29adb8438326fd583f1ba0475ce7707bd294ab01743d077605866425b1cbd0f6c7bba972b30fbe9fce0a719b044fcc1394354895a9f8304a2b5101909808ddfdf66df6237142b6566588e4e1e8949b90c27fc1f"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 256]+    [AdditionalInputLen = 256]+    [ReturnedBitsLen = 1024]+-}++t4 :: [TestVector]+t4 =+    [+    -- COUNT = 0+    [ "5d3286bc53a258a53ba781e2c4dcd79a790e43bbe0e89fb3eed39086be34174b"+    , "c5422294b7318952ace7055ab7570abf"+    , "2dba094d008e150d51c4135bb2f03dcde9cbf3468a12908a1b025c120c985b9d"+    , "793a7ef8f6f0482beac542bb785c10f8b7b406a4de92667ab168ecc2cf7573c6"+    , "2238cdb4e23d629fe0c2a83dd8d5144ce1a6229ef41dabe2a99ff722e510b530"+    , "d04678198ae7e1aeb435b45291458ffde0891560748b43330eaf866b5a6385e74c6fa5a5a44bdb284d436e98d244018d6acedcdfa2e9f499d8089e4db86ae89a6ab2d19cb705e2f048f97fb597f04106a1fa6a1416ad3d859118e079a0c319eb95686f4cbcce3b5101c7a0b010ef029c4ef6d06cdfac97efb9773891688c37cf"+    ],+    -- COUNT = 1+    [ "c2a566a9a1817b15c5c3b778177ac87c24e797be0a845f11c2fe399dd37732f2"+    , "cb1894eb2b97b3c56e628329516f86ec"+    , "13ce4d8dd2db9796f94156c8e8f0769b0aa1c82c1323b61536603bca37c9ee29"+    , "413dd83fe56835abd478cb9693d67635901c40239a266462d3133b83e49c820b"+    , "d5c4a71f9d6d95a1bedf0bd2247c277d1f84a4e57a4a8825b82a2d097de63ef1"+    , "b3a3698d777699a0dd9fa3f0a9fa57832d3cefac5df24437c6d73a0fe41040f1729038aef1e926352ea59de120bfb7b073183a34106efed6278ff8ad844ba0448115dfddf3319a82de6bb11d80bd871a9acd35c73645e1270fb9fe4fa88ec0e465409ea0cba809fe2f45e04943a2e396bbb7dd2f4e0795303524cc9cc5ea54a1"+    ],+    -- COUNT = 2+    [ "a33288a96f41dd54b945e060c8bd0c094f1e28267cc1dcbba52063c1a9d54c4d"+    , "36918c977e1a7276a2bb475591c367b7"+    , "6aa528c940962638dc2201738850fd1fe6f5d0eb9f687ff1af39d9c7b36830d9"+    , "37ee633a635e43af59abdb1762c7ea45bfe060ec1d9077ecd2a43a658673f3c7"+    , "2eb96f2e28fa9f674bb03ade703b8f791ee5356e2ee85c7ed5bda96325256c61"+    , "db2f91932767eb846961ce5321c7003431870508e8c6f8d432ca1f9cee5cdc1aed6e0f133d317eb6990c4b3b0a360cdfb5b43a6e712bd46bca04c414868fab22c6a49c4b89c812697c3a7fbfc8ddf10c8aa5ebf13a09fd114eb2a02a07f69786f3ce7fd30231f22779bc8db103b13fa546dbc45a89a86275281172761683d384"+    ],+    -- COUNT = 3+    [ "5f37b6e47e1776e735adc03d4b999879477ff4a206231924033d94c0114f911b"+    , "7d12d62c79c9f6234ae0314156947459"+    , "92d4d9fab5f8bf5119f2663a9df7334f50dcde74fb9d7732f7eba56501e60d54"+    , "c9aef0d7a9ba7345d08b6d5b5ce5645c7495b8685e6b93846ffcf470f5abd40d"+    , "50d9d1f5074f7d9f1a24a9c63aa47b94da5ba78db1b0f18e4d4fe45c6875813c"+    , "20d942bbd7d98700faa37e94d53bf74f2d6bd1d8c95c0b88d842c4857797d59e7c8788aeeac29740122f208f703bf35dc32b0035db0648384feb6aa17a3274bc09b2d2b746c5a06fd82f4469fb86131a49482cb7be7d9b4b95042394cfb18b13f333ec0fe5c227bf1d8f33ecb2e42e358b6c3e034cb585331bd1d27f638029b9"+    ],+    -- COUNT = 4+    [ "2311c5afd64c584484b2729e84db80c0b4063fe9ca7edc83350488d7e67264a0"+    , "6a6dfd975a0dc7b72df1f107c4b3b3a6"+    , "2abd870ec5fe26ed14dfa57a3309f920131b70580c3639af2645cd1af93db1b1"+    , "c6e532a3b25653b6002aed5269cc2118749306e736bde039d4d569d4f967773f"+    , "5e7d26c4da769c373092b2b4f72b109fe34bdb7d169ea38f78ebae5df4a15759"+    , "cacaeb1b4ac2305d8714eb50cbe1c67c5a2c0bbc7938fdfdcafef7c85fc40becbf777a4cfb6f14c6eee320943a493d2b0a744a6eb3c256ee9a3763037437df9adce3e2260f0c35e958af0edb5a81debd8bdaf2b8bb2b98b9186e5a222a21609ff58df4cbe1d4898d10d6e7c46f31f5cb1041bfd83a5fb27d5c56c961e91403fc"+    ],+    -- COUNT = 5+    [ "362ece9d330e1172a8f9e50258476d0c79c3ee50346524ba12d970ee3a6ef8c5"+    , "cf11bcb4d9d51311ceacfca8705e833f"+    , "abb5a8edde02e526449284ecc31bc713383df3ed085f752e3b6a32f305861eed"+    , "746302ab1f4a86b17546bea762e929360f2e95c7788a63545a264ef997c8c65e"+    , "b907c5b2a8833a48e56e819228ce9a050b41b3309f5ca37bed720311d92b33af"+    , "73c7131a558350590053580873ef956ff952f2aa6ff1bea452e013d1bc2afddea2311756dbe756e63ba6258480c48f3f6c1319b5f572f67ca530af09e39413d1d432bea8f89206619618cb0e7c88e9f2033639d0eb0efc20616b64f940da99b88231984c3fb23f19e890576f555fde394dbd4351f17a7ffd5c369379001bda03"+    ],+    -- COUNT = 6+    [ "cf614bc29946bc0095f415e8bdeda10aab05392f9cc9187a86ea6ec95ee422e1"+    , "77fb5ec22dc0432cc13f4693e2e3bd9a"+    , "e4ce77914ffbc5fddf1fb51edfafdc196109139b84c741354135ec8d314c7c43"+    , "e1e83ee1205acaf6164dc287aec08e5b32789e5be818078db39e53cad589db51"+    , "4e20c0226d5e1e7e805679f03f72452b5bea2d0ba41e0c12329bf60eb3016dd1"+    , "838fdf1418a746aa52ae4005d90c3fd301f648c5770ffef2a9f3912e37a93850cc4b8bfcce910aead0cb75958823b1a62e283901c5e4a3980e4ea36257458e2e4953555819b8852a26489b1d74821f80c9908469b43f124ff7ea62497c36159a47353098a1b9ec32e54800d6704371cc37f357ad74aacc203e9b6db97f94d0c4"+    ],+    -- COUNT = 7+    [ "a8da1d3e233f393fd44d204c200202f7d01896e72c5ac652940cfd15b5d4b0bd"+    , "0a112b4cb0890af0a495e0f49fcf6874"+    , "d2e32799bc822b8d033299bdf63dc35774f7649e935d25be5b10512c430d1bda"+    , "920a82d76fcd2cd106ada64bba232b7b2344f3afe6b1d1d20ee8795144571009"+    , "eeaac5878275372025f8231febed64db6a11273c3c00d625fc80a95f18ad7d3f"+    , "5f6dae489b53d89027b2cc333c700f090152d77b3eaf01d47f56ce6eca9893ef877b4cb560fab0fbdb34e3d1c6cd8480b33c053d2661a10aa531df4961b97d659c7492584236582b3fe701055efa59c328194cd1e07fcffd910d9ee01b7b9e8c8fda7f7ac01a8e203b8b26eb8078a9b9a5021562c44af24089e3ef84c1d5a6bd"+    ],+    -- COUNT = 8+    [ "a77b1ed4ecaa650374e1052c405f1d88881c25c87d13dbe1334d8c1a847fa76b"+    , "05c143e2f145db216fe7be9ed23635d0"+    , "b5c750968ff09ed251d4a1c05342ac843db5246b19045728a634fa4f6e752e54"+    , "ff5937bcd01a363696bf8e40adc8e4ab3e56dbf7e7d09451c99e538785fe6697"+    , "4acb34eea8266badcf8f6557a0eecf3eb4d7a295c876d6175598cb66a388efb8"+    , "ec13eadfcc84e77d2a2efa1a2cd8b1355587cb27feb3d19d75b37f0446333ddb8236e751c63b7a6e595ec24a25051a696dbe8c062dd8896d1446db228a2f10e8094ee07e7ee648ed6bebb2f5ec5aae24c9c640665c28355cc11c116795ecc070790f7fdfc4398900311b6695d5da0175091ed1828d2731085bfb4a20bd86cce0"+    ],+    -- COUNT = 9+    [ "491686c781e83eb4e21d9989e8d718100b0d21a2c56295888baef1a65f219651"+    , "499085296d21065feabf3106101c8d6f"+    , "d208a72f9ae34f0817669fb04f49239dd31700f3dc9a93db8d75fb79f9b686c1"+    , "9ffc61893a293a864008fdd56d3292600d9e2ec8a1ea8f34ac5931e968905a23"+    , "4ff3a397dfdae0912032a302a5e7a07dceca8d9013a21545689319b7c024cd07"+    , "3c258ebf2203fca3b322ad1b016e21c7f5c148425f81e4fb0a0e462dce9dfa569c37a006527768297a5b68461b08912642a341b88c85597e30e7561206886098c4e2d861f11513f0ffdbbc78d3a2dd60c105abbb33c5e05ae27081b690fb8b3610917aa9bf1a4ad74481b5ff8334f14e5ad6a6a1eb2259476078076fb7e3a992"+    ],+    -- COUNT = 10+    [ "36a5267eeeb5a1a7d46de0f8f9281f73cd9611f01198fdaa78c5315205e5a177"+    , "b66b5337970df36219321badacc624eb"+    , "c2a7b164949da102bece44a423197682ff97627d1fe9654266b8527f64e5b386"+    , "a977e2d8637b019c74063d163bb25387dc56f4eb40e502cefc5ae6ad26a6abdc"+    , "c5c9819557b1e7d8a86fa8c60be42993edc3ef539c13d9a51fb64b0de06e145e"+    , "b471711a4fc7ab7247e65d2c2fe49a50169187187b7978cd2fdb0f8318be3ec55fc68ed4577ad9b42cbb57100b5d35ac86c244c4c93a5b28c1a11c2dfe905d608ec7804dec5bb15cf8d79695534d5e13a6a7e18a887ec9cf184da0cbbc6267f3a952a769403bafcdbb559401be0d8b3300ea7258b4026fc892175efd55ba1a67"+    ],+    -- COUNT = 11+    [ "a76b0366df89e4073a6b6b9c04da1d6817ce26f1c4825cad4097bdf4d7b9445e"+    , "773d3cc3290176773847869be528d1a4"+    , "1bfd3bcfb9287a5ad055d1b2b8615fa81c94ac24bc1c219a0f8de58789e0404a"+    , "edd879fa56f21d93029da875b683ce50f6fdc4c0da41da051d000eed2afefefa"+    , "f528ffd29160039260133ed9654589ce60e39e7f667c34f82cda65ddcf5fff14"+    , "39d1ff8848e74dd2cdc6b818ad69823878062116fdf1679942f892c7e191be1c4b6ea268ecdff001b22af0d510f30c2c25b90fc34927f46e3f45d36b0e1848b3a5d54c36c7c65ee7287d325dfbb51b56a438feb6650ce13df88bf06b87ac4a35d2a199ea888629fb0d83f82f0ea160dc79ed220d8ef195b9e80c542f60c2d320"+    ],+    -- COUNT = 12+    [ "46571e1df43e5e141235e2a9ec85bb0faf1dc0566031e14d41a2fbd0315653ec"+    , "b60ef6a3347967519aabeaf748e4e991"+    , "759fd8593e3688b23c4a003b655311770d670789878570eb3b155a8e6c2d8c45"+    , "033128460b449e1accb0e9c54508759ddc2538bc64b51e6277553f0c60a02723"+    , "a5e4a717240bdeac18a0c0e231a11dc04a47d7550f342fa9a7a5ff334eb9327d"+    , "9d222df1d530ea7f8f2297a0c79d637da570b48042ecddded75956bba0f0e70b271ffa3c9a53bada6ee1b8a4203c22bfde82a5e2eb1b150f54c6483458569422c1a34a8997d42cc09750167a78bf52a0bd158397af9f83caabe689185c099bf0a9a4853dd3cf8b8e89efebb6a27dba873e65e9927741b22968f2875789b44e01"+    ],+    -- COUNT = 13+    [ "d63980e63bbe4ac08d2ac5646bf085b82c75995e3fdfc23bb9cc734cd85ca7d2"+    , "d33ed1dcae13fb634ba08272d6697590"+    , "acd0da070072a5340c4f5f4395568e1a36374e074196ae87f3692ee40487e1df"+    , "f567677b5e12e26f3544be3da9314c88fc475bf84804a89a51f12b191392c02b"+    , "c01cc7873e93c86e2bfb8fc984cfc2eab5cc58eeef018fedb5cba5aedd386156"+    , "b133446f633bcb40724bbf9fa187c39a44b9c094a0a0d40e98977e5466dc2c9adf62a5f4551eeb6406a14658de8a0ed7487c3bf6277e811101284a941745ce16176acc875f1435e14161772fa84609e8123c53dd03cbb868030835c0d11d8d6aa04a1b6f908248b028997737f54735ec4ed7a81fc868199ffb61a779d9340334"+    ],+    -- COUNT = 14+    [ "3d99f9b7ac3a2fbe9cf15d960bf41f5588fc4db1e0d2a5c9c0fe9059f03593fb"+    , "411f504bb63a9b3afa7ffa1357bb48be"+    , "0bb5ebd55981a25ba69164da49fa92f2871fd3fc65eb30d0f0d0b8d798a4f8f2"+    , "288e948a551284eb3cb23e26299955c2fb8f063c132a92683c1615ecaed80f30"+    , "d975b22f79e34acf5db25a2a167ef60a10682dd9964e15533d75f7fa9efc5dcb"+    , "ee8d707eea9bc7080d58768c8c64a991606bb808600cafab834db8bc884f866941b4a7eb8d0334d876c0f1151bccc7ce8970593dad0c1809075ce6dbca54c4d4667227331eeac97f83ccb76901762f153c5e8562a8ccf12c8a1f2f480ec6f1975ac097a49770219107d4edea54fb5ee23a8403874929d073d7ef0526a647011a"+    ]+    ]++{- Reseed test vectors -}++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 0]+    [AdditionalInputLen = 0]+    [ReturnedBitsLen = 1024]+-}++r1 :: [TestVector]+r1 =+    [+    -- COUNT = 0+    [ "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"+    , "0e66f71edc43e42a45ad3c6fc6cdc4df"+    , ""+    , "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"+    , ""+    , ""+    , ""+    , "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"+    ],+    -- COUNT = 1+    [ "aadcf337788bb8ac01976640726bc51635d417777fe6939eded9ccc8a378c76a"+    , "9ccc9d80c89ac55a8cfe0f99942f5a4d"+    , ""+    , "03a57792547e0c98ea1776e4ba80c007346296a56a270a35fd9ea2845c7e81e2"+    , ""+    , ""+    , ""+    , "17d09f40a43771f4a2f0db327df637dea972bfff30c98ebc8842dc7a9e3d681c61902f71bffaf5093607fbfba9674a70d048e562ee88f027f630a78522ec6f706bb44ae130e05c8d7eac668bf6980d99b4c0242946452399cb032cc6f9fd96284709bd2fa565b9eb9f2004be6c9ea9ff9128c3f93b60dc30c5fc8587a10de68c"+    ],+    -- COUNT = 2+    [ "62cda441dd802c7652c00b99cac3652a64fc75388dc9adcf763530ac31df9214"+    , "5fdc897a0c1c482204ef07e0805c014b"+    , ""+    , "bd9bbf717467bf4b5db2aa344dd0d90997c8201b2265f4451270128f5ac05a1a"+    , ""+    , ""+    , ""+    , "7e41f9647a5e6750eb8acf13a02f23f3be77611e51992cedb6602c314531aff2a6e4c557da0777d4e85faefcb143f1a92e0dbac8de8b885ced62a124f0b10620f1409ae87e228994b830eca638ccdceedd3fcd07d024b646704f44d5d9c4c3a7b705f37104b45b9cfc2d933ae43c12f53e3e6f798c51be5f640115d45cf919a4"+    ],+    -- COUNT = 3+    [ "6bdc6ca8eef0e3533abd02580ebbc8a92f382c5b1c8e3eaa12566ecfb90389a3"+    , "8f8481cc7735827477e0e4acb7f4a0fa"+    , ""+    , "72eca6f1560720e6bd1ff0152c12eeff1f959462fd62c72b7dde96abcb7f79fb"+    , ""+    , ""+    , ""+    , "d5a2e2f254b5ae65590d4fd1ff5c758e425be4bacdeede7989669f0a22d34274fdfc2bf87135e30abdae2691629c2f6f425bd4e119904d4785ecd9328f15259563e5a71f915ec0c02b66655471067b01016fdf934a47b017e07c21332641400bbe5719050dba22c020b9b2d2cdb933dbc70f76fec4b1d83980fd1a13c4565836"+    ],+    -- COUNT = 4+    [ "096ef37294d369face1add3eb8b425895e921626495705c5a03ee566b34158ec"+    , "6e2e0825534d2989715cc85956e0148d"+    , ""+    , "1b4f7125f472c253837fa787d5acf0382a3b89c3f41c211d263052402dcc62c5"+    , ""+    , ""+    , ""+    , "4541f24f759b5f2ac2b57b51125077cc740b3859a719a9bab1196e6c0ca2bd057af9d3892386a1813fc8875d8d364f15e7fd69d1cc6659470415278164df656295ba9cfcee79f6cbe26ee136e6b45ec224ad379c6079b10a2e0cb5f7f785ef0ab7a7c3fcd9cb6506054d20e2f3ec610cbba9b045a248af56e4f6d3f0c8d96a23"+    ],+    -- COUNT = 5+    [ "a7dccdd431ae5726b83585b54eae4108f7b7a25c70187c0acbb94c96cc277aa8"+    , "94c8f4b8e195a47356a89a50d1389ab5"+    , ""+    , "51733eee2e922f4055e53939e222e71fae730eb037443db2c7679708abb86a65"+    , ""+    , ""+    , ""+    , "99ba2691a622afecc9472418e6a8f9f1cdc1e3583c3bc7a2a650a1ab79dcbccbd656636c573179276e782569420c97438c06be898867f628b1c01eb570263d2c0f09c7aab536f6fba7df6aad19e05c236b645674667c03d1b6a04d7fc11177fe78933b309679f5bf26a4632b9a13e314c4bf4532428d3d95c689002b6dc1fbb1"+    ],+    -- COUNT = 6+    [ "c286425ecf543a49bcc9196b0db1a80bc54e4948adba6f41712a350a02891fa6"+    , "957a659a4ec2e0b7ad185483c220fd61"+    , ""+    , "08c2129813eea0776fba72788fdf2718759cc3c4207fa20a5fe23ac6e32cc28e"+    , ""+    , ""+    , ""+    , "8e1020a4fd84c99e0fc7e3f7ce48de5ed9ec9a5c2ccd624dbe6f30e2f688a31dc55957630357a5d48ca2a456241a28bfb16d8bb000877697a7ce24d9ad4d22b0c15117996f1f270b94f46d7a9bdfa7608fa1dd849177a9b8049e51b6b7a2742623854a1fddb5efc447eed1ea1aed6f02b4b2754ecf71ea0509da2e54f524a7e7"+    ],+    -- COUNT = 7+    [ "02818bd7c1ec456ace55beeba99f646a6d3aa0ea78356ea726b763ff0dd2d656"+    , "c482687d508c9b5c2a75f7ce390014e8"+    , ""+    , "cf319bfa63980e3cb997fd28771bb5614e3acb1149ba45c133ffbbab17433193"+    , ""+    , ""+    , ""+    , "19a231ff26c1865ce75d7a7185c30dd0b333126433d0c8cbf1be0d2b384d4eb3a8aff03540fbfa5f5496521a4e4a64071b44c78bd0b7e68fac9e5695c5c13fd3b9dbe7f7739781a4c8f0b980f1b17d99bce17ceb52b56866ae02456ffef83399c8cf7826f3c45c8a19315890919d20f40fc4e18d07e9c8ccd16c3327b5988f71"+    ],+    -- COUNT = 8+    [ "77a5c86d99be7bc2502870f4025f9f7563e9174ec67c5f481f21fcf2b41cae4b"+    , "ed044ad72ee822506a6d0b1211502967"+    , ""+    , "778100749f01a4d35c3b4a958aafe296877e0acafd089f50bc7797a42a33ab71"+    , ""+    , ""+    , ""+    , "831a4da566f46289904893ef1cc1cd4ad19ee48f3857e2b69e936d10afbdc29822e85d02663d346ef3e09a848b1d9cc04f4c4c6e3b3b0e56a034e2334d34ca08f8097be307ba41d020bc94f8c1937fe85644eeb5592c2b5a2138f7ded9a5b44b200c8b5beb27597c790f94d660eb61e8248391edc3ae2d77656cbe8354275b13"+    ],+    -- COUNT = 9+    [ "0ea458cff8bfd1dd8b1addcba9c01317d53039e533104e32f96e7d342e6c7b9b"+    , "935a4b66fc74c2a48757a99c399e64e3"+    , ""+    , "6c5f3708e7b714c4ed139b4fa9e8c763af01773484005109a85e33653bb0ce98"+    , ""+    , ""+    , ""+    , "373a37af84fddec13645a9768d6a785ae5a2589d64cd9b37980dde2541499210c4f408335de1d585349064f3f53a2b4c5ec6dc2a09591f99ad9fad528ac83474164b45497bf167f81e66fa08463ffea917f6891e48f149fafc20622bb1172f34886feb45c26fd446a4a4e2891b4bc594186896141aaaeeb301b49e7c1a26fec7"+    ],+    -- COUNT = 10+    [ "bfb68be4ce1756d25bdfad5e0c2f8bec29360901cc4da51d423d1591cc57e1ba"+    , "98afe4bd194c143e099680c504cceaab"+    , ""+    , "b97caf210e82498c3408790d41c320dd4a72007778389b44b7bc3c1c4b8c53f8"+    , ""+    , ""+    , ""+    , "409e0aa949fb3b38231bf8732e7959e943a338ea399026b744df15cbfeff8d71b3da023dcce059a88cf0d4b7475f628e4764c8bef13c70cfbbbb6da2a18aabcad919db09d04fc59765edb165147c88dd473a0f3c5ee19237ca955697e001ba654c5ee0bd26761b49333154426bc63286298a8be634fe0d72cfdeef0f3fc48eca"+    ],+    -- COUNT = 11+    [ "4f6880a64610004463031d67d7924fa446c39138d4d41007e8df3d65691a9367"+    , "6b33b2c13600f4b1df6ca3d1960e8dd4"+    , ""+    , "57b87b8c8f48312b5333d43b367730c0a5ad4725a16778fcb53fe136d136cbfd"+    , ""+    , ""+    , ""+    , "73d0f324ed186e2ad06bd1800e262bdbda79ba54e626761bd60f74f43e3bb62958ec1e2f1d940af163e1cadc124e7ebaba2f72e67efd746c7f6d0cad53ef03d859d93cff778a32ee5be172fe7fdbdc232ded360d704a6fa0f70bebe942e56478345492f49dc5c6fc346b88a58947ad250e688e8c626fe1efe7624620e571976e"+    ],+    -- COUNT = 12+    [ "aae352e111843219cae8f70e7b8f6eb9bb53d246cbec1e4f07d42757143295b4"+    , "b84485dccd1bf93210e322eafcbebcd9"+    , ""+    , "f9237f00d744d8fbff21b9d0043c258e8731817e6a5fb7b4bf5011680e5bc642"+    , ""+    , ""+    , ""+    , "cfb28b93522c7d61d8d3ce3f080e435e4c83c7e13a9dab788db8fef0407267a14fbc9324e090e24df5491fedfa81116869983938d4d4d7324a310c3af33a6f7938f602c5e4e63f1771cdaabdab0782b5affb54eb53047c109a9606739dd0065bd21eca33132986554878354f5f9f852e674dd690163b0ff74c7a25e6bae8ce39"+    ],+    -- COUNT = 13+    [ "589e79e339b7d2a1b879f0b0e1a7d1ad2474eaa8025b070f1ffa877b7124d4ff"+    , "0961ed64dbd62065d96e75de6d2ff9d6"+    , ""+    , "e928388d3af48c2968527a4d2f9c2626fbc3f3f5a5d84e0583ab6f78e7f8b081"+    , ""+    , ""+    , ""+    , "fce6ced1ecf474d181ab331f79c3d2cc8a768ec2818de5b3fc7cf418322716d6a6853733561a497c0c25cb288d2c9fcfbca891bafd5a834c85f3603f402acf1a7b1ea92db847ed5c252a862ad4ab5e259715f1fc81da67f5230bf8be50ee8069758095f7d0e559e03f2c6072290e61794458437609e473eb66580cddaad19b71"+    ],+    -- COUNT = 14+    [ "714277d408ad87fde317f0a94732fce62f1352bdc90936673b4f1daa0925aa26"+    , "d16582a99f23010b4248b88d86485419"+    , ""+    , "bd9fc7cb2fd5063b2c3c0c4f346ad2e3879371a9c805e59b9f2cd2cc2a40894f"+    , ""+    , ""+    , ""+    , "62ef7a431288252e0d736c1d4e36cc9ac37107dcd0d0e971a22444a4adae73a41eff0b11c8625e118dbc9226142fd0a6aa10ac9b190919bda44e7248d6c88874612abd77fb3716ea515a2d563237c446e2a282e7c3b0a3aef27d3427cc7d0a7d38714659c3401dbc91d3595159318ebca01ae7d7fd1c89f6ad6b604173b0c744"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 0]+    [AdditionalInputLen = 256]+    [ReturnedBitsLen = 1024]+-}++r2 :: [TestVector]+r2 =+    [+    -- COUNT = 0+    [ "05ac9fc4c62a02e3f90840da5616218c6de5743d66b8e0fbf833759c5928b53d"+    , "2b89a17904922ed8f017a63044848545"+    , ""+    , "2791126b8b52ee1fd9392a0a13e0083bed4186dc649b739607ac70ec8dcecf9b"+    , "43bac13bae715092cf7eb280a2e10a962faf7233c41412f69bc74a35a584e54c"+    , "3f2fed4b68d506ecefa21f3f5bb907beb0f17dbc30f6ffbba5e5861408c53a1e"+    , "529030df50f410985fde068df82b935ec23d839cb4b269414c0ede6cffea5b68"+    , "02ddff5173da2fcffa10215b030d660d61179e61ecc22609b1151a75f1cbcbb4363c3a89299b4b63aca5e581e73c860491010aa35de3337cc6c09ebec8c91a6287586f3a74d9694b462d2720ea2e11bbd02af33adefb4a16e6b370fa0effd57d607547bdcfbb7831f54de7073ad2a7da987a0016a82fa958779a168674b56524"+    ],+    -- COUNT = 1+    [ "1bea3296f24e9242b96ed00648ac6255007c91f7c1a5088b2482c28c834942bf"+    , "71073136a5cc1eb5b5fa09e1790a0bed"+    , ""+    , "d714329f3fbea1df9d0b0b0d88dfe3774beb63d011935923d048e521b710dc6f"+    , "4ef872fd211a426ea1085ab39eb220cc698fdfeabe49b8835d620ab7885de7a4"+    , "d74d1669e89875852d9ccbf11c20fe3c13a621ebcb3f7edeea39a2b3379fdcf5"+    , "0c8aa67ca310bd8e58c16aba35880f747266dbf624e88ec8f9ee9be5d08fdeb1"+    , "ce95b98f13adcdf7a32aa34709d6e02f658ae498d2ab01ce920f69e7e42c4be1d005acf0ca6b17891dfafc620dd4cd3894f8492a5c846089b9b452483eb0b91f3649ec0b6f98d1aaabc2e42cd39c2b25081b85ab50cb723007a0fd83550f32c210b7c4150b5a6bb3b0c9e3c971a09d43acb48e410a77f824b957092aa8ef98bc"+    ],+    -- COUNT = 2+    [ "a7ea449b49db48601fc3a3d5d77081fab092b8d420ed1b266f704f94352dd726"+    , "d11a159b60af8d20a0e37d27e6c74aa3"+    , ""+    , "50916ab47e8cb5dc843f9fba80639103711f86be8e3aa94f8a64a3fe0e6e5b35"+    , "e2bb6768120555e7b9e0d573537a82f8f32f54560e1050b6abb1588fb3441e66"+    , "a50cec9d1ecddb2c163d24019e81c31a2b350ccd3ad8181fd31bb8d1f64fa50e"+    , "591dbbd48b51abced67f9c6269cf0133cd3dcbb5cfafcb6ef758569c555a5773"+    , "0a464abcc8685158372d544635b953fcb1d3821c30aaa93982f9b788935f00f88115aad61d5cee003b3d1cb50f3e961a501e2dd0fc7e1724778b184a4bdf9f64e110dda7446e5544a30bd49a400ea1a5411800e1edfeea349323618afc5dc5782dc4b71d2da4d6a4785f8dd346feb9c8740ffd26bf644e3e4323ff24c30b9f10"+    ],+    -- COUNT = 3+    [ "14683ec508a29d7812e0f04a3e9d87897000dc07b4fbcfda58eb7cdabc492e58"+    , "b2243e744eb980b3ece25ce76383fd46"+    , ""+    , "18590e0ef4ee2bdae462f76d9324b3002559f74c370cfccf96a571d6955703a7"+    , "9ea3ccca1e8d791d22fcda621fc4d51b882df32d94ea8f20ee449313e6909b78"+    , "16366a578b5ea4d0cb547790ef5b4fd45d7cd845bc8a7c45e99419c8737debb4"+    , "a68caa29a53f1ba857e484d095805dc319fe6963e4c4daaf355f722eba746b92"+    , "c4e7532ee816789c2d3da9ff9f4b37139a8515dbf8f9e1d0bf00c12addd79ebbd76236f75f2aa705a09f7955038ebff0d566911c5ea13214e2c2eeb46d23ad86a33b60f7b9448d63eec3e1d59f48b39552857447dc5d7944667a230e3dbfa30ca322f6eacaf7536a286706a627c5083c32de0658b9073857c30fb1d86eb8ad1b"+    ],+    -- COUNT = 4+    [ "fa261fb230e2822458532ca2d5c39758750e6819a6fcebef10579ba995096959"+    , "564e1c9fbcb12878df2bd49202cbf821"+    , ""+    , "bf7de29e99e7f0e1b9f96f3b1902fb4049c8c6234d20de8316ebe66d97725457"+    , "8b7326621f6afbd44a726de48d03bcc5331f7306026c229ea9523497fbeaa88d"+    , "33b00b31623d6160c4c6740363a96481be14b19bc47be95641227284c366922a"+    , "2d812c8203575790ad6b6f2ed91a49d57460de779a3e881bef3be12e8766dc91"+    , "5574e0b4efc17e8ce136e592beabfe32551072bddd740929e698467b40b3991f028a22c760f7034853cc53007e3793e3c4a600d9e9d94528f8dc09aeba86146cdde2b7f71255ae0efc529b49be2205979dba6525bfe155e8819e8e2aeeaa285704242da90b4c4535101cc47d94b0e388a1b2e63ad0cbe158b9e1bbae9cc0007c"+    ],+    -- COUNT = 5+    [ "61f1471ced56aa04c57e1b512307d4cb92497d9592d7e9e35356e99d585cab1b"+    , "84714e960c403a4fac06b2828cc564d9"+    , ""+    , "7bf97db3c102edc81596d4757045fe6bdc008f35792fc6290b77d889c09c33a8"+    , "5b8bdc41f76d98cfa71ed976ea3994706375c8841adb8b6b3b6418e3132e8832"+    , "94c8a8fdf38a6ccb8571c89420d899adab169214bb0dfcd43a04622e289935b2"+    , "8a4b46e0a7a55907365f82d4ab9376509bd44728cab8cbafb0da901012ad8dcd"+    , "933eb159a6af7455b60e40586c064f05f1970f564281b1ebc4662701ac1f299e4eb908c4afcb2e065191281ab576f684aefedd6904bad04d96bd93c0516c62a496c3073a0cda0676a11cc08866b0cc74f62cb9d3db48673b2c3fbeada69f922b4b795ccba22df12ef7125909381f7d681f6b9caba02fb913c5437b98c040c576"+    ],+    -- COUNT = 6+    [ "a1d5bb7d70621dee6b668b28c56d5610c2f8ced30284cc3e0e48de331af05062"+    , "88a49e3e54c5ea54c98b95de81bcc807"+    , ""+    , "b4e2426e98f6eed97a6cdf690a89ee109e84c3dca16c883c26fa4ac671638d8d"+    , "5bd1e086ed228cfd8b55c1731fea40c3a63d022599ca2da4bb23118f4821ba62"+    , "b754b53ac226e8ebe47a3d31496ec822de06fca2e7ef5bf1dec6c83d05368ec3"+    , "fa7e76b2805d90b3d89fff545010d84f67aa3a2c9eb2ba232e75f4d53267dac3"+    , "df6b2460688fa537df3ddfe5575fca5eb8abad56cbc4e5a618a2b4a7daf6e215c3a497974c502f9d0ec35de3fc2ea5d4f10de9b2aee66dcc7e7ae6357983095959b817f0383e3030771bd2ed97406acf78a1a4a5f30fa0992289c9202e69e3eb1eabe227c11409ff430f6dfca1a923a8b17bc4b87e908007f5e9759c41482b01"+    ],+    -- COUNT = 7+    [ "68f21d14525d56233c7e263482d344c388a840103a77fb20ac60ce463cabdc79"+    , "59fa80ae570f3e0c60ac7e2578cec3cb"+    , ""+    , "7584b4166530442f06e241dd904f562167e2fdae3247ab853a4a9d4884a5fa46"+    , "f6a5482f139045c5389c9246d772c782c4ebf79c3a84b5cf779f458a69a52914"+    , "9d37b1ce99f8079993ddf0bd54bab218016685b22655a678ce4300105f3a45b7"+    , "4c97c67026ff43c2ee730e7b2ce8cce4794fd0588deb16185fa6792ddd0d46de"+    , "e5f8874be0a8345aabf2f829a7c06bb40e60869508c2bdef071d73692c0265f6a5bf9ca6cf47d75cbd9df88b9cb236cdfce37d2fd4913f177dbd41887dae116edfbdad4fd6e4c1a51aad9f9d6afe7fcafced45a4913d742a7ec00fd6170d63a68f986d8c2357765e4d38835d3fea301afab43a50bd9edd2dec6a979732b25292"+    ],+    -- COUNT = 8+    [ "7988146cbf9598d74cf88dc314af6b25c3f7de96ae9892fb0756318cea01987e"+    , "280bc1ae9bfdf8a73c2df07b82a32c9c"+    , ""+    , "2bbc607085232e5e12ccf7c0c19a5dc80e45eb4b3d4a147fe941fa6c13333474"+    , "f3f5c1bb5da59252861753c4980c23f72be1732f899fdea7183b5c024c858a12"+    , "44d0cfc4f56ab38fa465a659151b3461b65b2462d1ad6b3463b5cf96ad9dc577"+    , "34fb9a3cdacc834ff6241474c4f6e73ed6f5d9ea0337ab2b7468f01ad8a26e93"+    , "4caec9e760c4d468e47613fe50de4a366ae20ba76793744a4e14433ea4de79dc188601eb86c803b094641ab2337b99d459d37decc7d27473057be45ba848868ee0fb5f1cf303d2fcd0b3e0c36f65a65f81b3fee8778a1f22302e25dfe34e6d587fa8864e621121880f7cd55f350531c4ce0530099eec2d0059706dcd657708d9"+    ],+    -- COUNT = 9+    [ "1c974c953fa2a057c9fc9409a6843f6f839aa544bca4fa11e48afd77931d4656"+    , "ed7c08285464af7a5dbdc10b944a1270"+    , ""+    , "78146ad135acb836360d36afc50653dcc36c21662da2a6f6ae05222e75f34000"+    , "263c4984c238ded333c86472866353817379502157172cfa51371d82b1efd7b5"+    , "79b591529f9a26a0d7c8f8fd64e354b0c134ef1f757e43f9463b3dbb7a3da1ab"+    , "7d8f7204b0b5401ddce9e88dcf5facb9a44660a9f5f1c862748e7269c29f7964"+    , "72e2ca257b9edaf59b50e05a144f56fb517832fb9ad3489b1e664e3d5412cbf6b2883e891703b2e73aff9ab56da1009fcdef010ab4cdab996795c8f7c47fb1192bb160353997ad39d7d5fd0e2efc9103a7c3f158246afd53fe53ca6782f809698ef5f1f0d85536780a3fd6a8bafa475891c09213088bd1a3dc169257c34a517a"+    ],+    -- COUNT = 10+    [ "56216d71984a77154569122c777ce57e1d101a6025b28163a25971d39c1c5d0f"+    , "5cd148ba7e54f4975ac8e3e0f9b5d06a"+    , ""+    , "3580f8ca974626c77259c6e37383cb8150b4d0ab0b30e377bed0dc9d1ff1a1bf"+    , "15633e3a62b21594d49d3d26c4c3509f96011d4dbb9d48bbbea1b61c453f6abe"+    , "6068eaca85c14165b101bb3e8c387c41d3f298918c7f3da2a28786ab0738a6fc"+    , "e34f92d2b6aeeeea4ff49bfe7e4b1f462eabb853f0e86fbae0e8b3d51409ce49"+    , "587fdb856abc19ede9078797ecb44099e07aadcd83acdcb2b090601d653f4a14c68ab2ebdda63578c5633a825bae4c0c818f89aac58d30fd7b0b5d459a0f3d86fcad78f4bb14dfff08ad81e4ea9f487cb426e91d6e80dfed436ba38fce8d6f21ca2151c92dd5c323b077d6139c66395558f0537026c4a028affa271ef4e7ea23"+    ],+    -- COUNT = 11+    [ "83eb48bedc1e9294866ab8e5322ef83f6f271f8188e8fdabe5817788bd31570d"+    , "d6ed90bc692237f132441ede857a6629"+    , ""+    , "a4e5e127f992bd5ca79ee56bb8a9bccf74c21814bfaf97ffd052211e802e12e4"+    , "84136e403d9ed7f4515c188213abcfaca35715fa55de6d734aec63c4606a68f1"+    , "fe9d8ef26e2d2e94b99943148392b2b33a581b4b97a8d7a0ecd41660a61dd10b"+    , "594dad642183ce2cdc9494d6bcb358e0e7b767c5a0fa33e456971b8754a9abd5"+    , "86715d43ba95fbbca9b7193ea977a820f4b61ba1b7e3b8d161b6c51b09dfd5040d94c04338b14d97ed25af577186b36ae7251a486c8a2d24a35e84a95c89d669d49e307b4a368b72164135ac54d020a970a180dfbed135d2c86f01270846d5301bd73db2c431a8aa10a0a3d03d146e5fafb9a2aa0b4efc80edab06ff3b532236"+    ],+    -- COUNT = 12+    [ "ba2c94203dab2e6499d8c50dca7b5c34a6b4764834f9816631aa21b9f9c37361"+    , "67db133bdefb25e395085bceee5a0afc"+    , ""+    , "fa8984d16d35302cda35a3a355ab9242ec96fec0652d39282d4a0abf0a80df87"+    , "b6fed10255a3fea6772ae1ae6d9f6cbb9bfaa34804e58a5b786f9bc60b348ccd"+    , "445e072244edc716d3528f0e0a20ff0cd8f819c0d031736c8da122748f24d6c6"+    , "1f856e403c4fa035bac9aa81a20e347c7d8b213aab699d69d9d6186a06ac45c1"+    , "79f33fc36b3b47d9ac805bdbbe699909a8d0beb689a8b2723c291bd5bf7f3ce61343d4722a14e4add36312dbb0594910c8828aff1abc159915d498106f9ffb31147478d8c9ef75d1536ba5036506b313f6e85033f8f6fea2a4de817c867a59378c53c70a2f108275daedd415c05b61c4fd5d48c54be9adb9dea6c40a2ec99ee0"+    ],+    -- COUNT = 13+    [ "0db4c51492db4fe973b4bb1c52a1e873b58fc6bb37a3a4bfc252b03b994495d1"+    , "a2a3900f169bba3f78a42526c700de62"+    , ""+    , "29d5aab356876447e3a20d81c7e3fc6975e2b984180a91493044442999e1ca3a"+    , "40b34183b4e72cdff5952b317b3d45943d0fdcfa0527f3563055f7c73ae8f892"+    , "dc94220c99ffb595c7c4d6de8de5a6bb4b38847169e24a557ef6d879ad84149d"+    , "b2376626fd2f5218b3ed4a5609b43aa24d371cd2176ea017c2b99cf868060021"+    , "f0bd6bc4c506d9427a09352d9c1970b146360732841a6323f4cb602c87dedfb5ff7e6964b9144933af3c5c83017ccd6a94bdca467a504564aaa7b452591a16ff6a1e7e94ddc98f9a58016cdcb8caaed6c80671ba48cc81a832d341093dda1d4e5001ec6bf66348b21e3692a13df92538ad572bb2023822072fc95f9590293ffc"+    ],+    --  COUNT = 14+    [ "593845f0adfeffa7c169f8a610147ae8a08c0072fc0c14c3977d3de0d00b55af"+    , "9e0eb2507342ee01c02beadee7d077bd"+    , ""+    , "aefe591697eab678c52e20013aa424b95cfd217b259757fbe17335563f5b5706"+    , "cbb5be0ef9bf0555ee58955c4d971fb9baa6d6070c3f7244a4eb88b48f0793bf"+    , "6dd878394abdc0402146ba07005327c55f4d821bfebca08d04e66824e3760ab4"+    , "ba86a691d6cbf452b1e2fd1dfb5d31ef9ea5b8be92c4988dc5f560733b371f69"+    , "00735cbfafac5df82e5cb28fc619b01e2ba9571dc0023d26f09c37fb37d0e809066165a97e532bf86fa7d148078e865fe1a09e27a6889be1533b459cd9cd229494b5cf4d2abf28c38180278d47281f13820276ec85effb8d45284eb9eef5d179ab4880023ab2bd08ee3f766f990286bf32430c042f5521bbfd0c7ee09e2254d7"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 256]+    [AdditionalInputLen = 0]+    [ReturnedBitsLen = 1024]+-}++r3 :: [TestVector]+r3 =+    [+    -- COUNT = 0+    [ "fa0ee1fe39c7c390aa94159d0de97564342b591777f3e5f6a4ba2aea342ec840"+    , "dd0820655cb2ffdb0da9e9310a67c9e5"+    , "f2e58fe60a3afc59dad37595415ffd318ccf69d67780f6fa0797dc9aa43e144c"+    , "e0629b6d7975ddfa96a399648740e60f1f9557dc58b3d7415f9ba9d4dbb501f6"+    , ""+    , ""+    , ""+    , "f92d4cf99a535b20222a52a68db04c5af6f5ffc7b66a473a37a256bd8d298f9b4aa4af7e8d181e02367903f93bdb744c6c2f3f3472626b40ce9bd6a70e7b8f93992a16a76fab6b5f162568e08ee6c3e804aefd952ddd3acb791c50f2ad69e9a04028a06a9c01d3a62aca2aaf6efe69ed97a016213a2dd642b4886764072d9cbe"+    ],+    -- COUNT = 1+    [ "cff72f345115376a57f4db8a5c9f64053e7379171a5a1e81e82aad3448d17d44"+    , "d1e971ec795d098b3dae14ffcbeecfd9"+    , "6ec0c798c240f22740cad7e27b41f5e42dccaf66def3b7f341c4d827294f83c9"+    , "45ec80f0c00cad0ff0b7616d2a930af3f5cf23cd61be7fbf7c65be0031e93e38"+    , ""+    , ""+    , ""+    , "17a7901e2550de088f472518d377cc4cc6979f4a64f4975c74344215e4807a1234eefef99f64cb8abc3fb86209f6fc7ddd03e94f83746c5abe5360cdde4f2525ccf7167e6f0befae05b38fd6089a2ab83719874ce8f670480d5f3ed9bf40538a15aaad112db1618a58b10687b68875f00f139a72bdf043f736e4a320c06efd2c"+    ],+    -- COUNT = 2+    [ "b7099b06fc7a8a74c58219729db6b0f780d7b4fa307bc3d3f9f22bfb763596a3"+    , "b8772059a135a6b61da72f375411de26"+    , "2ac1bfb24e0b8c6ac2803e89261822b7f72a0320df2b199171b79bcbdb40b719"+    , "9aec4f56ec5e96fbd96048b9a63ac8d047aedbbeea7712e241133b1a357ecfc4"+    , ""+    , ""+    , ""+    , "0e1f2bfef778f5e5be671ecb4971624ec784ed2732abc4fbb98a8b482fb68737df91fd15acfad2951403ac77c5ca3edffc1e03398ae6cf6ac24a91678db5c7290abc3fa001aa02d50399326f85d2b8942199a1575f6746364740a5910552c639804d7530c0d41339345a58ff0080eccf1711895192a3817a8dc3f00f28cc10cc"+    ],+    -- COUNT = 3+    [ "7ba02a734c8744b15ef8b4074fe639b32e4431762ab5b7cd4d5df675ea90672b"+    , "8a424f32108607c8f1f45d97f500ee12"+    , "3ad627433f465187c48141e30c2678106091e7a680229a534b851b8d46feb957"+    , "d8f02b59b6a3dd276bc69cba68efcf11ab83ead1397afd9841786bd1bb5da97a"+    , ""+    , ""+    , ""+    , "1fb91186ba4b4459d994b4b9f4ca252c7be6294d6cdb5fe56f8ff784d4b190a1c6456e0a41223bbbdf83ed8e7cfbfa765d9d8bc7ea5f4d79ea7eccb4928081a21de4cca36620d6267f55d9a352b76fc0a57375884112c31f65ff28e76d315698c29e6c4c05cb58b0a07ae66143b4abc78b9d25c78b4121e1e45bef1a6c1793e2"+    ],+    -- COUNT = 4+    [ "9a8865dfe053ae77cb6a9365b88f34eec17ea5cbfb0b1f04d1459e7fa9c4f3cb"+    , "180c0a74da3ec464df11fac172d1c632"+    , "336372ec82d0d68befad83691966ef6ffc65105388eb2d6eed826c2285037c77"+    , "75b95108eff1fabe83613e1c4de575e72a5cdc4bb9311dd006f971a052386692"+    , ""+    , ""+    , ""+    , "3c683f6d4f8f5a4018d01633dfee74266aaa68ed6fc649e81b64dfdf5f75e75d5c058d66cf5fd01a4f143a6ff695517a4a43bd3adfd1fb2c28ba9a41063140bedbffdb4d21b1ace1550d59209ec61f1e2dbacb2a9116a79cb1410bf2deca5218080aacd9c68e1d6557721a8913e23f617e30f2e594f61267d5ed81464ee730b2"+    ],+    -- COUNT = 5+    [ "22c1af2f2a4c885f06988567da9fc90f34f80f6dd5101c281beef497a6a1b2f8"+    , "3fafdecf79a4174801f133131629037b"+    , "80327dac486111b8a8b2c8e8381fb2d713a67695c2e660b2b0d4af696cc3e1de"+    , "f95a0e4bd24f0e2e9e444f511b7632868ead0d5bb3846771264e03f8ab8ed074"+    , ""+    , ""+    , ""+    , "77a7fea2f35a188f6d1bfdd49b569d8c45e2dd431d35a18c6f432c724f1e33ae92cb89a9cf91519e50705a53199f5b572dc85c1aef8f28fb52dc7986228f66954d54eda84a86962cf25cf765bd9949876349291b1aae5f88fcf4b376912d205add4f53b2770c657946c0d824281f441509153f48356d9d43f8a927e0693db8fc"+    ],+    -- COUNT = 6+    [ "d0840e3a8d629d5b883d33e053a341b21c674e67e1999f068c497ecfaabfd6f6"+    , "071de7244ecb2fdf7ab27f2d84aa7b7a"+    , "90d609527fad96ffe64ab153860346f3d237c8940555ae17b47842d82d3b0943"+    , "1dd1a8b59856c49a388f594c5f42cc2e4a56b3ccb8a65e7066e44c12f4344d50"+    , ""+    , ""+    , ""+    , "7ab28a9b2d3ae999195553e6550cced4c2daccbe7ec9dcbb0d467fabba185b727fbfd9830242cd098f4db3cf4a85e8bf8e8d5974b62b28550922b32ed5bfc1a522b6605cf93bf8d90bdec1c5b9e59c6fc37a817d437068a87254be1f7c4618ada46fbc3a2efb02e44524e21d91be7534cf05fbfd858304b706d6a91ea1cc6ad5"+    ],+    -- COUNT = 7+    [ "2e2dd56869104492767a59778652831919e1c8b970f84e824ae4116597a0ab7f"+    , "01c42a7e983641de46c82fd09b4f2f76"+    , "bcd9e1508fcc22820a8be07180fea5045367333b569e111b011cd57dc1858765"+    , "7306507cd3ca7eec667e640d270cfbb033063d97520b6b7e38ff3cea0e79d12b"+    , ""+    , ""+    , ""+    , "b915726c7b8c5dc3975f1a334684b973abf6a9495d930088cf5d071548e4fd29a67b55cc561ed6949ad28150a9fb4307c1fa5f783a7ea872e8d7c7e67ff0c2906081ee915737d813c25be5c30b952a36f393e6baa56ab01adc2b4776ad7b5d036a53659877c7a4e5220a897d6c0799af37beeed91173fbe9c613c3b6b9bb28e5"+    ],+    -- COUNT = 8+    [ "d1aab0f16bd47a5ccd67c22e094daa3735eae21aa57f0bcd9e053d9d0d545cb8"+    , "199310dfe1b01265b8c0d2b46d6c7c9f"+    , "625b4b8f4de72ea9cb6f70556322dc2a19d6b2b32de623f557e419a084ba60fd"+    , "f50cabae4e060f3971096b78e550cda2837a26a693d905db2d992d589b268f44"+    , ""+    , ""+    , ""+    , "987e1fdfe004c619cf1e9034576707eccd849400e19c87a1fef5b0179ec51c42a2f8c45d7942d0023a023c89f188b2634362703985695369863322f58619c50a7385a2dc91fc78f94b59f0131dc2b56a0d7c699d427285da1c104b0ad1739da10d8071c23993787045dc21f0070e1e9aa1658fc8e3add73dac7262e80e0aa2ee"+    ],+    -- COUNT = 9+    [ "449480eaa100aff6f48dc6286a5a81b9728b084864f78a9da98f606a00a6a41f"+    , "e53c6c5ac3da9f4726389a03f97bb640"+    , "6b8fedc084d8e28d333aef6db3702b6351f0d24e30908cccb63794282655886b"+    , "73a6d64e1966ae324388dc12c14544e9dc5ae4fcb331e99d350c456ff16f9aa0"+    , ""+    , ""+    , ""+    , "a06912d362da7eb25598857f6d65344c3e23ec3deb80c6e43158845b95eaeca241c0bbbd67ac385e24693444455cc1c2c08c1134d956b8bc93b28be9c2d3322b3e09252979dfb8d39d04c94f81bebda5c73110605a237b561216bda9ee9bdee1cc0c7728bcc8304682334ca944e467a27a85313fa5395a9c790e35defd2edb12"+    ],+    -- COUNT = 10+    [ "9a6174166e97aa4981ddf580bc01c96754b9f0ba042750aabfda1cffe56e8581"+    , "d7512ff6b7db7ce141b2bb01dcd0425e"+    , "ed75288f23275f9422444da5d3b53ccb3c4ac8acfb659a1e9b7655c2db52f879"+    , "6888b9277e57dc57663d402eba8d03cf56a070dc868e6a128b18040002baf690"+    , ""+    , ""+    , ""+    , "03519dfb2ff88cc2b53eecc48ae2a18ddcf91a5d69d5aefcdda8444e6df790a5240e67b2a4de75b4bb8a31f0f8aeb5e785ffb7a1341bb52fe00a05ee66fa2d44ea9956e055f9ffa6647c3bfe851ab364ade71a0d356de710ddafb7622b1da1bc53fd4d3210407289c68d8aeb346bf15806dbe787e781b94f63da3e1f61b5ac60"+    ],+    -- COUNT = 11+    [ "9c6ae1002ee1b0add0be563ce50f899da936e13efa620d08c2688c192514763a"+    , "fde7db5160c73044be73e9d4c1b22d86"+    , "8fdaaeffd64e53f7b4374d902d441209964e12b65d29afec258e65db6de167ca"+    , "bcc28fd58e397f53f494ad8132df82c5d8c4c22ea0b7139bd81eeba65667bb69"+    , ""+    , ""+    , ""+    , "021d938c9b4db780c7d8134aeff1053e5b8843370b8ae9a6749fca7199d809810f1bc8dfa49426470c30c3616f903e35fbacb23420a32f1bee567cc32300f704246ddc0217f236ef52c3ec9e2433ca66f05c25721f7661c43f22c1a125ed5db531bd0836eb435c27eefc7424ce9d845e1d4cc4c503097b4ffca788e674a5cb53"+    ],+    -- COUNT = 12+    [ "fe96a85b69d46b540918927bb609dc57642eeaefd46bb5da2163a0bc60294b58"+    , "22195a410d24db45589448dfe979d3fd"+    , "20f698833a4472fd7b78fb9b0c4eb68604f166a2694c4af48dac2b2376790e1e"+    , "09cb870879d3f734214f6a4bd2e08c62a2a954bebe559416d8c3551aafe71d6a"+    , ""+    , ""+    , ""+    , "d3e96dbe29e1fcb8ed83b19dbfb240e6f41679fbe83853aa71446617e63e5af78cf98b331d15bccb8c673c4e5d5dcec467a1fe26a6cd1696d0c9bc49f78139d051287df7f3ae0dbb4bbf581cb8211931063c3f4612ced53f59d1b4ebb875729139f5d2a7d60642e8f2835eed888b7e3e49c0dffd012cd746abfa3e1c5c2308c6"+    ],+    -- COUNT = 13+    [ "a4fd693ff0a8af24bcec352d3196549fd0da5ee5d99ca58416ca03ce4c50f38e"+    , "8cd67f2bf71d4366ce61396642531ff5"+    , "368969c15a4849d7593be8b162113b9298a535c148ff668a9e8b147fb3af4eba"+    , "83d2be9a0d74e6a42159ae630acebf4e15271ef7f14f3de14752be0e0e822b11"+    , ""+    , ""+    , ""+    , "e9188fc0eaec74b2608e21e3a40be94aaf4ae08eb684de8f8bba2d5fd3b073aa5531c938c0fc628da65725c54b5c68bb91d7d326565e96685e0a4e7b220c50e0caf1628edba5bd755b31894f8cb90afa76e88c5eb9e61b4932444c1397dee3e32241a3fb70a3929e49f6da02eea54812abb3d6b5cee18f03af1e0b4958430ab3"+    ],+    -- COUNT = 14+    [ "254ff5687a6dad3f1d237dc762f58d24ef2e2c084d0a48d26a3dc81e5490cda3"+    , "f2ec392acca491e03ce47b95963a49fc"+    , "f806b9b4a56682c61b55cb6a334caf87ffe135adfea6d0c3fc22b39898fbd078"+    , "b8494b1c1f1752fb6f80d732a89b08115857f7cc96e7dff05ebb822706889917"+    , ""+    , ""+    , ""+    , "0e527e00494d55564f9d9b28e7110f9a61ce36c883b5be2dcb055444164cdddd1a9f2731716f22d6ff476ce413c77abfc0e946871d5481345c2e97b4bfdd12ac03df606fc56bdb99ac7b71a69b5b9160373bbec3e9dde477180af454e7acc6bc58dc0afb4281c0de4354c1bf599054e3800c6d60d892858865b5361f50bfca9b"+    ]+    ]++{-+    [SHA-256]+    [PredictionResistance = False]+    [EntropyInputLen = 256]+    [NonceLen = 128]+    [PersonalizationStringLen = 256]+    [AdditionalInputLen = 256]+    [ReturnedBitsLen = 1024]+-}++r4 :: [TestVector]+r4 =+    [+    -- COUNT = 0+    [ "cdb0d9117cc6dbc9ef9dcb06a97579841d72dc18b2d46a1cb61e314012bdf416"+    , "d0c0d01d156016d0eb6b7e9c7c3c8da8"+    , "6f0fb9eab3f9ea7ab0a719bfa879bf0aaed683307fda0c6d73ce018b6e34faaa"+    , "8ec6f7d5a8e2e88f43986f70b86e050d07c84b931bcf18e601c5a3eee3064c82"+    , "1ab4ca9014fa98a55938316de8ba5a68c629b0741bdd058c4d70c91cda5099b3"+    , "16e2d0721b58d839a122852abd3bf2c942a31c84d82fca74211871880d7162ff"+    , "53686f042a7b087d5d2eca0d2a96de131f275ed7151189f7ca52deaa78b79fb2"+    , "dda04a2ca7b8147af1548f5d086591ca4fd951a345ce52b3cd49d47e84aa31a183e31fbc42a1ff1d95afec7143c8008c97bc2a9c091df0a763848391f68cb4a366ad89857ac725a53b303ddea767be8dc5f605b1b95f6d24c9f06be65a973a089320b3cc42569dcfd4b92b62a993785b0301b3fc452445656fce22664827b88f"+    ],+    -- COUNT = 1+    [ "3e42348bf76c0559cce9a44704308c85d9c205b676af0ac6ba377a5da12d3244"+    , "9af783973c632a490f03dbb4b4852b1e"+    , "2e51c7a8ac70adc37fc7e40d59a8e5bf8dfd8f7b027c77e6ec648bd0c41a78de"+    , "45718ac567fd2660b91c8f5f1f8f186c58c6284b6968eadc9810b7beeca148a1"+    , "63a107246a2070739aa4bed6746439d8c2ce678a54fc887c5aba29c502da7ba9"+    , "e4576291b1cde51c5044fdc5375624cebf63333c58c7457ca7490da037a9556e"+    , "b5a3fbd57784b15fd875e0b0c5e59ec5f089829fac51620aa998fff003534d6f"+    , "c624d26087ffb8f39836c067ba37217f1977c47172d5dcb7d40193a1cfe20158b774558cbee8eb6f9c62d629e1bcf70a1439e46c5709ba4c94a006ba94994796e10660d6cb1e150a243f7ba5d35c8572fd96f43c08490131797e86d3ed8467b692f92f668631b1d32862c3dc43bfba686fe72fdd947db2792463e920522eb4bc"+    ],+    -- COUNT = 2+    [ "b63fdd83c674699ba473faab9c358434771c5fa0348ca0faf7ebd7cf5891826b"+    , "5fd204e2598d9626edab4158a8cfd95f"+    , "2a5dfad8494306d9d4648a805c4602216a746ae3493492693a50a86d1ba05c64"+    , "adea5ba92f8010bb1a6a4b6fae2caa0b384165adf721253afd635d6021f764af"+    , "07c69d8d2b8aa1454c5c48083dd41477fda6bfcf0385638379933a60ed2e0a77"+    , "a14e902247a3d6493d3fbc8519518b71a660e5502cf7ecfc796cfaa5b4ee4baa"+    , "60e690e4a1eba14aec5187112a383e9991347fab7bac7cb2a40a52579a0d2718"+    , "792b47b6ed221623bb187d63e3f039c6983d94efd5771dc9b4c40bee65924513485a6332baeda6a96f9bb431f592d73462b61d9d914a72b56fa9d87597426fb246424ebcd7abd51b2eefec8f5b839c0b3c34015342ace296b5f2218fa194b50aea1c89663460292c92c45f112ddbf6b9406f6e7ccee9c47ed2d90a27be5dd73e"+    ],+    -- COUNT = 3+    [ "dab85f98eaf0cfba013b97de4d9c264ca6fe120366cb83e8b3113c68b34e39d5"+    , "d05108e1028ae67b4ea63bdc6d75eb88"+    , "09fed3822f6f5e5b9e575d31dc215de1607b0dfc927412618c2d8f79166dbaba"+    , "1794885a64470744198b7d0bc24472ffe8daf3c7eb219df6ddf180e484fe0aa5"+    , "8d74d01b582f70b92f53b43468084e1586d9b36465d333d5faaf6911e62fe40e"+    , "ef7f6b6eb479ab05b3f9ab6dd72eac8b1e86d887f1bcae363cae386d0275a06f"+    , "7442b2a792a6a29559bb8a515d56916ee18200580aa02e1237dd358619382d8f"+    , "49d2cbfa0897b7d961c293c1e572fb26f28e7b956e746f6eda90454c1370a29e25303ceadc7837514dc638553b487ef9487c977c10625409178ad6506d103c487a66655d08659d92a4d5994d1c8ddb28fe60f2e49577d6e80cae1478068c98268f45e6293c9326c7f726ec89601351c0a26fd3a6549f8a41c6f58692c86594c0"+    ],+    -- COUNT = 4+    [ "0f0aa84ef12e10ae2b279e799c683441862457b9bc25581c2cd3d5b58a5b3246"+    , "f74f4230c2427a52f01f39e825d250ac"+    , "d02b2f53da48b923c2921e0f75bd7e6139d7030aead5aeebe46c20b9ca47a38a"+    , "5222b26e79f7c3b7066d581185b1a1f6376796f3d67f59d025dd2a7b1886d258"+    , "d11512457bf3b92d1b1c0923989911f58f74e136b1436f00bad440dd1d6f1209"+    , "54d9ea7d40b7255ef3d0ab16ea9fdf29b9a281920962b5c72d97b0e371b9d816"+    , "601cef261da8864f1e30196c827143e4c363d3fa865b808e9450b13e251d47fa"+    , "e9847cefea3b88062ea63f92dc9e96767ce9202a6e049c98dc1dcbc6d707687bd0e98ed2cc215780c454936292e44a7c6856d664581220b8c8ca1d413a2b81120380bfd0da5ff2bf737b602727709523745c2ced8daef6f47d1e93ef9bc141a135674cba23045e1f99aa78f8cead12eeffff20de2008878b1f806a2652db565a"+    ],+    -- COUNT = 5+    [ "6a868ce39a3adcd189bd704348ba732936628f083de8208640dbd42731447d4e"+    , "efdde4e22b376e5e7385e79024350699"+    , "f7285cd5647ff0e2c71a9b54b57f04392641a4bde4a4024fa11c859fecaad713"+    , "0174f7f456ac06c1d789facc071701f8b60e9accebced73a634a6ad0e1a697d4"+    , "5463bb2241d10c970b68c3abc356c0fe5ef87439fc6457c5ee94be0a3fb89834"+    , "3ab62cdbc638c1b2b50533d28f31b1758c3b8435fe24bb6d4740005a73e54ce6"+    , "2dbf4c9123e97177969139f5d06466c272f60d067fefadf326ccc47971115469"+    , "8afce49dccc4ff64c65a83d8c0638bd8e3b7c13c52c3c59d110a8198753e96da512c7e03aeed30918706f3ad3b819e6571cfa87369c179fb9c9bbc88110baa490032a9d41f9931434e80c40ae0051400b7498810d769fb42dddbc7aa19bdf79603172efe9c0f5d1a65372b463a31178cbae581fa287f39c4fbf8434051b7419f"+    ],+    -- COUNT = 6+    [ "bb6b339eae26072487084ec9e4b53f2f1d4267d205042e74c77fb9ca0591ba50"+    , "c0e7bf6eb07feccbc494af4098e59d30"+    , "34aeec7ed0cae83701b6477709c8654a1114212401dc91cbe7de39d71f0c06e1"+    , "f47fc60afbeb807236f7974d837335bc0b22288ef09ddfcb684e16b4c36a050b"+    , "e8071ccd84ac4527e5c6e85b0709ed867776f25ae0e04180dcb7105ecd3e3490"+    , "fbac45b5952200ad7c4232500f2417a1c14723bdd1cc078821bc2fe138b86597"+    , "c4292d7dbef3ba7c18bf46bcf26776add22ab8ee206d6c722665dec6576b1bc0"+    , "228aa2a314fcbfe63089ce953ac457093deaa39dd9ce2a4ece56a6028a476a98129be516d6979eff5587c032cdf4739d7ac712970f600fa781a8e542e399661183e34e4b90c59ec5dc5cad86f91083529d41c77b8f36c5a8e28ba1a548223a02eaed8426f6fe9f349ebec11bc743e767482e3472ec2799c1f530ebdc6c03bc4b"+    ],+    -- COUNT = 7+    [ "be658e56f80436039e2a9c0a62952dd7d70842244b5ab10f3b8a87d36104e629"+    , "33c9627455dfde91865aee93e5071147"+    , "d3a6eb29b180b791984deb056d72c0608a2c9044237aecf100ccb03700064c5e"+    , "bef24dc9a5aa23003d3825f9b2b00e7dab571ea6ad86415dbd30c0bbdce7b972"+    , "047c29e4d1584fa70cb66e2aa148a2aa29837c5eee64dcac60fdba356cdf90bb"+    , "41c4792161b1b00d410cb79cd56bd311a714fb78dc3471c25bdd7479f2e9a952"+    , "cd4936d7bc3ea0e7201bcbefbc908215a97680ca6ce8672360aea600b6564308"+    , "2c25557f6db07db057f56ad5b6dc0427d1a0e825c48c19a526f9a65087c6d1ead7c78363a61616c84f1022653af65173a3f9ec3275f2b0a0d0bc750194673c0eaa6c623cd88abb0c8979baee4cd85bfce2e4a20bfebf2c3be61676563767dfe229e0b7be67ad6fcd116dd0b460708b1b0e5c3d60f3dd8138030404d197375d75"+    ],+    -- COUNT = 8+    [ "ae537f31a28ca14500e759716bc207983bfeab60b25079fa30b77b8d41244cb9"+    , "fca9e27d8ab84cf9b9ce491ec5d8cb67"+    , "8c9cb2b19aa3abe83c8fe7da96e9c11648252653a29dcd5bf0ac334ac587f032"+    , "1eb52777be480f05115ae6370f30159a94d50ffcc64454678ab1d1ac6f166fa7"+    , "9cdf6f1a2bc07acd4b0f43b5f2b892a1153e2669f237d257923636094fb40b54"+    , "692d512722de6ba720fd23c8994ac63179b5f7e611addf9cfacd60e06e144a6a"+    , "bbeea7b2bea821f339f494947c0b4bae8056119db69a3cbef21914953729cdef"+    , "c0c4fb7080c0fbe425c1b756fb3a090cb0d08c7027d1bb82ed3b07613e2a757f83a78d42f9d8653954b489f800a5e058ebc4f5a1747526541d8448cb72e2232db20569dc96342c36672c4be625b363b4587f44557e58cedb4597cb57d006fda27e027818ae89e15b4c6382b9e7a4453290ea43163b4f9cae38b1023de6a47f7b"+    ],+    -- COUNT = 9+    [ "2f8994c949e08862db0204008f55d3561f3e0362df13b9d9a70fda39938f2d33"+    , "1bf3e94ea858160b832fe85d301256f5"+    , "b46671cf7fa142e7012ed261e1fe86714711c246c7d1c0330fa692141e86d5d1"+    , "5ecdb1e8fe12260b9bfe12d6e6f161474fa2311e12e39b0beb0fcd92a6737b73"+    , "3ce9a29f0207d079e6dc81fb830356e555f96a23ea71424972ea9308965786d3"+    , "db950000c0776cc0e049929ce021020adc42d29cd9b5d8f7117fbe6bde3e594f"+    , "fc18ee6dd3dac2306774f0ac36cd789e33462d72a8c75df9057123db33e5f7bc"+    , "8546362cc8af9b78dd6e8eb2c37db96e70708852bfd9380abedc7f324575a167bea18f632f3e19d099cfbf310773f9719eec036d2e09f393a023add8ebdc4fb87af43b2fe6c7eaa4d39f8022ce247aa45fdc84d1b92cacce6eae8252a03ec2ec5330c01f56d113fd2ec3d0240af0afcf13ddde205bb5e7c2d912dcb4aee5dcf3"+    ],+    -- COUNT = 10+    [ "0c85e31487de1d7ba4a7b998ac56dc42c6dc0eae7bf5c8aaf1e4e78875f5fb47"+    , "de878f728f73f83dc2a2f550b96c8b97"+    , "9aac37bce1a6a81dc7934e23747991e3cf48c55ffe5a57781c41768a35220a01"+    , "2d5ca8af1a70cfdccd015ee3bf0665dd1941fc6a7317b9d0d06658f5744cfbd9"+    , "db881e6d0dc3b62793d7da5fe5a18e33be9b93f4a63a00a878dfbecf0d383bd2"+    , "f743ce1b72f3de4c901369eed581c626ed3081ca707e6634fdaff46721ce0878"+    , "cd52da3ec8a839c537dacdea8506a3eeee879de388ff5e513322d6d1bb3ff694"+    , "a5bdd57cb8fde6298e7c5e563afcca60dd472eca484bd8c3cc17f3307be09b601744dd3ab9e8a44107c5868824575f850c0f399b280cf198006f83ede8c0b537e9be227fa140b65995ad9dfa1f2303d560c3b7f59bedd93c1282ea263924469411c2653f87fd814c74cb91c148430481d64bad0fec3cbb3dd1f39aa55c36f81b"+    ],+    -- COUNT = 11+    [ "93161b2dc08cb0fd50171141c865a841ca935cfdd2b5907d6ff8ab0348c4ceb0"+    , "5cb9f6e5912b90c3349a50ab881b35a1"+    , "0dceb4a36326c4df1685df43fddeecb5d0c76f00eb44826694f27e610290f6e1"+    , "d8e9be44b5f293482548d4787762ebfb03c73c40e45385e8b98907cd66f493dd"+    , "105a8f85d6959f3e043ef508cfea21d52123f03b7aea8034c4eec761eaba1fee"+    , "bf781f7e489d9b4b5aa5ee6d1796468af672a8d25f311edf3c4b4dbf433d703f"+    , "c81d6bcf1e5bf37e39dda1735c6f193df115b1a854a12e7cafe060afe4589335"+    , "4306628124d0100fade7eaaf5edf227d50771f9e5f2e1e983800eef9a39fde0b0c280e63c8728d836b5b93ea794a32c1c04cfc54bd5300e3febb5fe2e1023eded8d7cd180279a598f76823e8d5a7dffcc93a09deec5d1f80838e938fba4de9f47e94b99382ae55f116df9c3b3ddf7e50516e203645852a415796f03a86418107"+    ],+    -- COUNT = 12+    [ "1ae12a5e4e9a4a5bfa79da30a9e6c62ffc639572ef1254194d129a16eb53c716"+    , "5399b3481fdf24d373222267790a0fec"+    , "8280cfdcd7a575816e0199e115da0ea77cae9d30b49c891a6c225e9037ba67e2"+    , "681554ff702658122e91ba017450cfdfc8e3f4911153f7bcc428403e9c7b9d68"+    , "226732b7a457cf0ac0ef09fd4f81296573b49a68de5e7ac3070e148c95e8e323"+    , "45942b5e9a1a128e85e12c34596374ddc85fd7502e5633c7390fc6e6f1e5ef56"+    , "6fc59929b41e77072886aff45f737b449b105ed7eacbd74c7cbfedf533dbeaa1"+    , "b7547332e1509663fcfea2128f7f3a3df484cd8df034b00199157d35d61e35f1a9d481c7d2e81305616d70fc371ee459b0b2267d627e928590edcac3231898b24ef378aa9c3d381619f665379be76c7c1bd535505c563db3725f034786e35bdd90429305fd71d7bf680e8cdd6d4c348d97078f5cf5e89dee2dc410fad4f2a30f"+    ],+    -- COUNT = 13+    [ "29e20d724dfa459960df21c6ec76b1e6cabd23a9e9456d6c591d7e4529da0ef8"+    , "95df1f837eba47a1687aa5c4ddcf8aaf"+    , "3713b601e164b1a51dda1ca9242ff477514648e90d311a06e10ce5aa15da5d7f"+    , "2a2a312626ca3e20034fc4f28033c7d573f66ef61ab2ea0c7bf0411a9d247264"+    , "ec68be33ac8ff3dd127e051604898c0f9a501271859376653a0516336180993d"+    , "9935499661d699a00c622a875441b4df5204958fe95892c8ce67f7dfb2be3e4a"+    , "256a4ba9e8f439d5487fa5eb45efcf1bc1120491724db3abe328d951f2739fc9"+    , "73114cb3624d687d4cd49a6e769dfc7a3f8901dc41f6ad1df4ce480536fa82e52ae958d0528640d92b8bb981b755058e32c4733682e5c4c0df41f3505a1643a0dd49cfdeaf7a18adffca88256c6d2cceb838af6c92a64bc21cb7a760a0391291bfe3575e014fc156323f8eb5e86518c669dad8d29ad5fd4ef6e296f4a0764c26"+    ],+    -- COUNT = 14+    [ "1353f3543eb1134980e061fc4382394975dbc74f1f1ea5ecc02780a813ac5ee6"+    , "cf584db2447afbe2c8fa0c15575ee391"+    , "345b0cc016f2765a8c33fc24f1dcfa182cbe29d7eacbcdc9bcda988521458fc2"+    , "ba60219332a67b95d90ec9de6b8453d4c8af991ae9277461ff3af1b92fc985d3"+    , "6964b9b9842aec9c7ec2aad926d701f30eec76fe699265ae2a7765d716958069"+    , "6a03c28a9365c558c33d3fdc7e5ebf0b4d32caac70df71403fd70ced09757528"+    , "a58546c72a0b4d47c9bd6c19e7cf4ab73b2d7ba36c6c6dc08606f608795ebd29"+    , "5b029ef68b6799868b04dc28dbea26bc2fa9fcc8c2b2795aafeed0127b7297fa19a4ef2ba60c42ff8259d5a759f92bd90fdfb27145e82d798bb3ab7fd60bfaefb7aefb116ca2a4fa8b01d96a03c47c8d987fdd33c460e560b138891278313bb619d0c3c6f9d7c5a37e88fce83e94943705c6ff68e00484e74ad4097b0c9e5f10"+    ]+    ]
+ test/Haskoin/Crypto/SignatureSpec.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Crypto.SignatureSpec (spec) where++import           Data.Bits             (testBit)+import           Data.ByteString       (ByteString)+import qualified Data.ByteString       as BS (index, length)+import           Data.Serialize        as S+import           Data.Text             (Text)+import           Haskoin.Crypto        (SecKey, Sig, decodeStrictSig,+                                        derivePubKey, exportSig,+                                        importCompactSig, isCanonicalHalfOrder,+                                        sha256, signHash, verifyHashSig)+import           Haskoin.Test          (arbitrarySignature)+import           Haskoin.Util          (decodeHex, eitherToMaybe, lst3)+import           Test.Hspec            (Spec, describe, it)+import           Test.Hspec.QuickCheck (prop)+import           Test.HUnit            (Assertion, assertBool)+import           Test.QuickCheck       (forAll)++spec :: Spec+spec = do+    describe "signatures" $ do+        prop "verify signature" $+            forAll arbitrarySignature $ \(msg, key, sig) ->+                verifyHashSig msg sig (derivePubKey key)+        prop "s component less than half order" $+            forAll arbitrarySignature $ isCanonicalHalfOrder . lst3+        prop "encoded signature is canonical" $+            forAll arbitrarySignature $ testIsCanonical . lst3+        prop "encode signature and decode strictly" $+            forAll arbitrarySignature $+            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3+        prop "encodes and decodes signature" $+            forAll arbitrarySignature $+            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3+    describe "trezor rfc6979 test vectors" $ do+        it "rfc6979 test vector 1" (testSigning $ head detVec)+        it "rfc6979 test vector 2" (testSigning $ detVec !! 1)+        it "rfc6979 test vector 3" (testSigning $ detVec !! 2)+        it "rfc6979 test vector 4" (testSigning $ detVec !! 3)+        it "rfc6979 test vector 5" (testSigning $ detVec !! 4)+        it "rfc6979 test vector 6" (testSigning $ detVec !! 5)+        it "rfc6979 test vector 7" (testSigning $ detVec !! 6)+        it "rfc6979 test vector 8" (testSigning $ detVec !! 7)+        it "rfc6979 test vector 9" (testSigning $ detVec !! 8)+        it "rfc6979 test vector 10" (testSigning $ detVec !! 9)+        it "rfc6979 test vector 11" (testSigning $ detVec !! 10)+        it "rfc6979 test vector 12" (testSigning $ detVec !! 11)++-- No longer validating that generated signature is exactly equal to provided+-- one. Fedora includes libsecp256k1 from Bitcoin ABC that computes+-- deterministic signatures using a slightly different nonce generation+-- algorithm.+testSigning :: (SecKey, ByteString, Text) -> Assertion+testSigning (prv, msg, str) = do+    assertBool "my sig valid" $ verifyHashSig msg' g (derivePubKey prv)+    assertBool "valid sig" $ verifyHashSig msg' g' (derivePubKey prv)+  where+    Just g' = importCompactSig =<< eitherToMaybe . decode =<< decodeHex str+    g = signHash prv msg'+    msg' = sha256 msg+++{- ECDSA Canonical -}++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalSignature+testIsCanonical :: Sig -> Bool+testIsCanonical sig = not $+    -- Non-canonical signature: too short+    (len < 8) ||+    -- Non-canonical signature: too long+    (len > 72) ||+    -- Non-canonical signature: wrong type+    (BS.index s 0 /= 0x30) ||+    -- Non-canonical signature: wrong length marker+    (BS.index s 1 /= len - 2) ||+    -- Non-canonical signature: S length misplaced+    (5 + rlen >= len) ||+    -- Non-canonical signature: R+S length mismatch+    (rlen + slen + 6 /= len) ||+    -- Non-canonical signature: R value type mismatch+    (BS.index s 2 /= 0x02) ||+    -- Non-canonical signature: R length is zero+    (rlen == 0) ||+    -- Non-canonical signature: R value negative+    testBit (BS.index s 4) 7 ||+    -- Non-canonical signature: R value excessively padded+    (  rlen > 1+    && BS.index s 4 == 0+    && not (testBit (BS.index s 5) 7)+    ) ||+    -- Non-canonical signature: S value type mismatch+    (BS.index s (fromIntegral rlen + 4) /= 0x02) ||+    -- Non-canonical signature: S length is zero+    (slen == 0) ||+    -- Non-canonical signature: S value negative+    testBit (BS.index s (fromIntegral rlen+6)) 7 ||+    -- Non-canonical signature: S value excessively padded+    (  slen > 1+    && BS.index s (fromIntegral rlen + 6) == 0+    && not (testBit (BS.index s (fromIntegral rlen + 7)) 7)+    )+  where+    s = exportSig sig+    len = fromIntegral $ BS.length s+    rlen = BS.index s 3+    slen = BS.index s (fromIntegral rlen + 5)+++{- Trezor RFC 6979 Test Vectors -}+-- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py++detVec :: [(SecKey, ByteString, Text)]+detVec =+    [+      ( "0000000000000000000000000000000000000000000000000000000000000001"+      , "Satoshi Nakamoto"+      , "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"+      )+    , ( "0000000000000000000000000000000000000000000000000000000000000001"+      , "All those moments will be lost in time, like tears in rain. Time to die..."+      , "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"+      )+    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+      , "Satoshi Nakamoto"+      , "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"+      )+    , ( "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181"+      , "Alan Turing"+      , "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"+      )+    , ( "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2"+      , "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!"+      , "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"+      )+    , ( "0000000000000000000000000000000000000000000000000000000000000001"+      , "Everything should be made as simple as possible, but not simpler."+      , "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"+      )+    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+      , "Equations are more important to me, because politics is for the present, but an equation is something for eternity."+      , "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"+      )+    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"+      , "Not only is the Universe stranger than we think, it is stranger than we can think."+      , "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"+      )+    , ( "0000000000000000000000000000000000000000000000000000000000000001"+      , "How wonderful that we have met with a paradox. Now we have some hope of making progress."+      , "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"+      )+    , ( "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64"+      , "Computer science is no more about computers than astronomy is about telescopes."+      , "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"+      )+    , ( "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637"+      , "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough"+      , "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"+      )+    , ( "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3"+      , "The question of whether computers can think is like the question of whether submarines can swim."+      , "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"+      )+    ]
+ test/Haskoin/CryptoSpec.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.CryptoSpec (spec) where++import           Control.Monad+import           Data.ByteString       (ByteString)+import qualified Data.ByteString.Char8 as C (pack)+import           Data.Maybe            (fromMaybe, isJust, isNothing)+import           Data.Text             (Text)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Crypto+import           Haskoin.Keys+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit            (Assertion, assertBool)++-- Unit tests copied from bitcoind implementation+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp++strSecret1 :: Text+strSecret1  = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"++strSecret2 :: Text+strSecret2  = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"++strSecret1C :: Text+strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"++strSecret2C :: Text+strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"++addr1 :: Text+addr1  = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"++addr2 :: Text+addr2  = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"++addr1C :: Text+addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"++addr2C :: Text+addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"++strAddressBad :: Text+strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"++sigMsg :: [ByteString]+sigMsg =+    [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]+    | i <- [0..15]+    ]++sec1 :: SecKeyI+sec1 =+    fromMaybe (error "Could not decode WIF secret 1") (fromWif btc strSecret1)++sec2 :: SecKeyI+sec2 =+    fromMaybe (error "Could not decode WIF secret 2") (fromWif btc strSecret2)++sec1C :: SecKeyI+sec1C =+    fromMaybe+        (error "Could not decode WIF compressed secret 1")+        (fromWif btc strSecret1C)++sec2C :: SecKeyI+sec2C =+    fromMaybe+        (error "Could not decode WIF compressed secret 2")+        (fromWif btc strSecret2C)++pub1 :: PubKeyI+pub1  = derivePubKeyI sec1++pub2 :: PubKeyI+pub2  = derivePubKeyI sec2++pub1C :: PubKeyI+pub1C = derivePubKeyI sec1C++pub2C :: PubKeyI+pub2C = derivePubKeyI sec2C++spec :: Spec+spec =+    describe "bitcoind /src/test/key_tests.cpp" $ do+        it "decode valid wif" checkPrivkey+        it "decode invalid wif" checkInvalidKey+        it "decode minikey format" checkMiniKey+        it "check private key compression" checkPrvKeyCompressed+        it "check public key compression" checkKeyCompressed+        it "check matching address" checkMatchingAddress+        it "check various signatures" sigCheck++sigCheck :: Assertion+sigCheck = forM_ sigMsg $ checkSignatures . doubleSHA256++{- bitcoind /src/test/key_tests.cpp -}++checkPrivkey :: Assertion+checkPrivkey = do+    assertBool "Key 1"  $ isJust $ fromWif btc strSecret1+    assertBool "Key 2"  $ isJust $ fromWif btc strSecret2+    assertBool "Key 1C" $ isJust $ fromWif btc strSecret1C+    assertBool "Key 2C" $ isJust $ fromWif btc strSecret2C++checkInvalidKey :: Assertion+checkInvalidKey =+    assertBool "Bad key" $ isNothing $ fromWif btc strAddressBad++checkMiniKey :: Assertion+checkMiniKey =+    assertBool "Bad mini key" $+    isJust res && fromMiniKey "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy" == res+  where+    res = do+        bs <-+            decodeHex+                "4C7A9640C72DC2099F23715D0C8A0D8A35F8906E3CAB61DD3F78B67BF887C9AB"+        wrapSecKey False <$> secKey bs++checkPrvKeyCompressed :: Assertion+checkPrvKeyCompressed = do+    assertBool "Key 1"  $ not $ secKeyCompressed sec1+    assertBool "Key 2"  $ not $ secKeyCompressed sec2+    assertBool "Key 1C" $ secKeyCompressed sec1C+    assertBool "Key 2C" $ secKeyCompressed sec2C++checkKeyCompressed :: Assertion+checkKeyCompressed = do+    assertBool "Key 1"  $ not $ pubKeyCompressed pub1+    assertBool "Key 2"  $ not $ pubKeyCompressed pub2+    assertBool "Key 1C" $ pubKeyCompressed pub1C+    assertBool "Key 2C" $ pubKeyCompressed pub2C++checkMatchingAddress :: Assertion+checkMatchingAddress = do+    assertBool "Key 1"  $ Just addr1  == addrToString btc (pubKeyAddr pub1)+    assertBool "Key 2"  $ Just addr2  == addrToString btc (pubKeyAddr pub2)+    assertBool "Key 1C" $ Just addr1C == addrToString btc (pubKeyAddr pub1C)+    assertBool "Key 2C" $ Just addr2C == addrToString btc (pubKeyAddr pub2C)++checkSignatures :: Hash256 -> Assertion+checkSignatures h = do+    let sign1  = signHash (secKeyData sec1) h+        sign2  = signHash (secKeyData sec2) h+        sign1C = signHash (secKeyData sec1C) h+        sign2C = signHash (secKeyData sec2C) h+    assertBool "Key 1, Sign1"   $ verifyHashSig h sign1 (pubKeyPoint pub1)+    assertBool "Key 1, Sign2"   $ not $ verifyHashSig h sign2 (pubKeyPoint pub1)+    assertBool "Key 1, Sign1C"  $ verifyHashSig h sign1C (pubKeyPoint pub1)+    assertBool "Key 1, Sign2C"  $ not $ verifyHashSig h sign2C (pubKeyPoint pub1)+    assertBool "Key 2, Sign1"   $ not $ verifyHashSig h sign1 (pubKeyPoint pub2)+    assertBool "Key 2, Sign2"   $ verifyHashSig h sign2 (pubKeyPoint pub2)+    assertBool "Key 2, Sign1C"  $ not $ verifyHashSig h sign1C (pubKeyPoint pub2)+    assertBool "Key 2, Sign2C"  $ verifyHashSig h sign2C (pubKeyPoint pub2)+    assertBool "Key 1C, Sign1"  $ verifyHashSig h sign1 (pubKeyPoint pub1C)+    assertBool "Key 1C, Sign2"  $ not $ verifyHashSig h sign2 (pubKeyPoint pub1C)+    assertBool "Key 1C, Sign1C" $ verifyHashSig h sign1C (pubKeyPoint pub1C)+    assertBool "Key 1C, Sign2C" $ not $ verifyHashSig h sign2C (pubKeyPoint pub1C)+    assertBool "Key 2C, Sign1"  $ not $ verifyHashSig h sign1 (pubKeyPoint pub2C)+    assertBool "Key 2C, Sign2"  $ verifyHashSig h sign2 (pubKeyPoint pub2C)+    assertBool "Key 2C, Sign1C" $ not $ verifyHashSig h sign1C (pubKeyPoint pub2C)+    assertBool "Key 2C, Sign2C" $ verifyHashSig h sign2C (pubKeyPoint pub2C)
+ test/Haskoin/Keys/ExtendedSpec.hs view
@@ -0,0 +1,521 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Keys.ExtendedSpec (spec) where++import           Data.Aeson                 as A+import           Data.Aeson.Types           as A+import           Data.Bits                  ((.&.))+import qualified Data.ByteString.Lazy.Char8 as B8+import           Data.Either                (isLeft)+import           Data.Map.Strict            (singleton)+import           Data.Maybe                 (fromJust, isJust, isNothing)+import           Data.Serialize             as S+import           Data.String                (fromString)+import           Data.String.Conversions    (cs)+import           Data.Text                  (Text)+import           Data.Word                  (Word32)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys+import           Haskoin.Test+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit                 (Assertion, assertBool, assertEqual)+import           Test.QuickCheck            hiding ((.&.))++spec :: Spec+spec = do+    describe "bip32 derivation vector 1" $ do+        it "chain m" $ runXKeyVec (head xKeyVec)+        it "chain m/0'" $ runXKeyVec (xKeyVec !! 1)+        it "chain m/0'/1" $ runXKeyVec (xKeyVec !! 2)+        it "chain m/0'/1/2'" $ runXKeyVec (xKeyVec !! 3)+        it "chain m/0'/1/2'/2" $ runXKeyVec (xKeyVec !! 4)+        it "chain m/0'/1/2'/2/1000000000" $ runXKeyVec (xKeyVec !! 5)+    describe "bip32 subkey derivation vector 2" $ do+        it "chain m" $ runXKeyVec (head xKeyVec2)+        it "chain m/0" $ runXKeyVec (xKeyVec2 !! 1)+        it "chain m/0/2147483647'" $ runXKeyVec (xKeyVec2 !! 2)+        it "chain m/0/2147483647'/1" $ runXKeyVec (xKeyVec2 !! 3)+        it "chain m/0/2147483647'/1/2147483646'" $ runXKeyVec (xKeyVec2 !! 4)+        it "Chain m/0/2147483647'/1/2147483646'/2" $ runXKeyVec (xKeyVec2 !! 5)+    describe "bip32 subkey derivation using string path" $ do+        it "either derivations" testApplyPath+        it "either derivations" testBadApplyPath+        it "dublic derivations" testDerivePubPath+        it "private derivations" testDerivePrvPath+        it "path parsing" testParsePath+        it "from json" testFromJsonPath+        it "to json" testToJsonPath+    describe "extended keys" $ do+        let net = btc+        it "computes pubkey of a subkey is subkey of the pubkey" $+            property $ forAll arbitraryXPrvKey pubKeyOfSubKeyIsSubKeyOfPubKey+        it "exports and imports extended private key" $+            property $+            forAll arbitraryXPrvKey $ \k ->+                xPrvImport net (xPrvExport net k) == Just k+        it "exports and imports extended public key" $+            property $+            forAll arbitraryXPubKey $ \(_, k) ->+                xPubImport net (xPubExport net k) == Just k+        it "show and read derivation path" $+            property $ forAll arbitraryDerivPath $ \p -> read (show p) == p+        it "show and read hard derivation path" $+            property $ forAll arbitraryHardPath $ \p -> read (show p) == p+        it "show and read soft derivation path" $+            property $ forAll arbitrarySoftPath $ \p -> read (show p) == p+        it "from string derivation path" $+            property $+            forAll arbitraryDerivPath $ \p -> fromString (cs $ pathToStr p) == p+        it "from string hard derivation path" $+            property $+            forAll arbitraryHardPath $ \p -> fromString (cs $ pathToStr p) == p+        it "from string soft derivation path" $+            property $+            forAll arbitrarySoftPath $ \p -> fromString (cs $ pathToStr p) == p+        it "from and to lists of derivation paths" $+            property $+            forAll arbitraryDerivPath $ \p -> listToPath (pathToList p) == p+        it "from and to lists of hard derivation paths" $+            property $+            forAll arbitraryHardPath $ \p ->+                toHard (listToPath $ pathToList p) == Just p+        it "from and to lists of soft derivation paths" $+            property $+            forAll arbitrarySoftPath $ \p ->+                toSoft (listToPath $ pathToList p) == Just p+        it "read and show parsed path" $+            property $ forAll arbitraryParsedPath $ \p -> read (show p) == p+        it "encodes and decodes extended private key" $+            forAll+                arbitraryXPrvKey+                (testCustom (xPrvFromJSON net) (xPrvToJSON net))+        it "encodes and decodes extended public key" $+            forAll+                arbitraryXPubKey+                (testCustom (xPubFromJSON net) (xPubToJSON net) . snd)+        it "encodes and decodes derivation path" $+            forAll arbitraryDerivPath testID+        it "encodes and decodes parsed derivation path" $+            forAll arbitraryParsedPath testID+        it "encodes and decodes extended private key" $+            property $+            forAll arbitraryXPrvKey $+            testPutGet (getXPrvKey net) (putXPrvKey net)+        it "shows and reads extended private key" $+            property $+            forAll arbitraryXPrvKey $ \k -> read (show k) `shouldBe` k+        it "shows and reads extended private key" $+            property $+            forAll arbitraryXPubKey $ \(prv, pub) ->+                read (show (prv, pub)) `shouldBe` (prv, pub)++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 :: Assertion+testApplyPath =+    sequence_ $ do+        (key, path, final) <- applyPathVectors+        return $+            assertEqual path final $ applyPath (fromJust $ parsePath path) key++testBadApplyPath :: Assertion+testBadApplyPath =+    sequence_ $ do+        (key, path) <- badApplyPathVectors+        return $+            assertBool path $ isLeft $ applyPath (fromJust $ parsePath path) key++testDerivePubPath :: Assertion+testDerivePubPath =+    sequence_ $ do+        (key, path, final) <- derivePubPathVectors+        return $+            assertEqual path final $+            derivePubPath (fromString path :: SoftPath) key++testDerivePrvPath :: Assertion+testDerivePrvPath =+    sequence_ $ do+        (key, path, final) <- derivePrvPathVectors+        return $+            assertEqual path final $+            derivePath (fromString path :: DerivPath) key++derivePubPathVectors :: [(XPubKey, String, XPubKey)]+derivePubPathVectors =+    [ ( xpub, "M", xpub )+    , ( xpub, "M/8", pubSubKey xpub 8 )+    , ( xpub, "M/8/30/1", foldl pubSubKey xpub [8,30,1] )+    ]+  where+    xprv = fromJust $ xPrvImport btc+        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"+    xpub = deriveXPubKey xprv++derivePrvPathVectors :: [(XPrvKey, String, XPrvKey)]+derivePrvPathVectors =+    [ ( xprv, "m", xprv )+    , ( xprv, "M", xprv )+    , ( xprv, "m/8'", hardSubKey xprv 8 )+    , ( xprv, "M/8'", hardSubKey xprv 8 )+    , ( xprv, "m/8'/30/1"+      , foldl prvSubKey (hardSubKey xprv 8) [30,1]+      )+    , ( xprv, "M/8'/30/1"+      , foldl prvSubKey (hardSubKey xprv 8) [30,1]+      )+    , ( xprv, "m/3/20"+      , foldl prvSubKey xprv [3,20]+      )+    , ( xprv, "M/3/20"+      , foldl prvSubKey xprv [3,20]+      )+    ]+  where+    xprv = fromJust $ xPrvImport btc+        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"++applyPathVectors :: [(XKey, String, Either String XKey)]+applyPathVectors =+    [ (XPrv xprv btc, "m", Right (XPrv xprv btc))+    , (XPrv xprv btc, "M", Right (XPub xpub btc))+    , (XPrv xprv btc, "m/8'", Right (XPrv (hardSubKey xprv 8) btc))+    , ( XPrv xprv btc+      , "M/8'"+      , Right (XPub (deriveXPubKey (hardSubKey xprv 8)) btc))+    , ( XPrv xprv btc+      , "m/8'/30/1"+      , Right (XPrv (foldl prvSubKey (hardSubKey xprv 8) [30, 1]) btc))+    , ( XPrv xprv btc+      , "M/8'/30/1"+      , Right+            (XPub+                 (deriveXPubKey (foldl prvSubKey (hardSubKey xprv 8) [30, 1]))+                 btc))+    , (XPrv xprv btc, "m/3/20", Right (XPrv (foldl prvSubKey xprv [3, 20]) btc))+    , ( XPrv xprv btc+      , "M/3/20"+      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))+    , ( XPub xpub btc+      , "M/3/20"+      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))+    ]+  where+    xprv =+        fromJust $+        xPrvImport+            btc+            "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\+        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"+    xpub = deriveXPubKey xprv++badApplyPathVectors :: [(XKey, String)]+badApplyPathVectors =+    [ (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 xprv+++runXKeyVec :: ([Text], XPrvKey) -> Assertion+runXKeyVec (v, m) = do+    assertBool "xPrvID" $ encodeHex (S.encode $ xPrvID m) == head v+    assertBool "xPrvFP" $ encodeHex (S.encode $ xPrvFP m) == v !! 1+    assertBool "xPrvAddr" $+        addrToString btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2)+    assertBool "prvKey" $ encodeHex (getSecKey $ xPrvKey m) == v !! 3+    assertBool "xPrvWIF" $ xPrvWif btc m == v !! 4+    assertBool "pubKey" $+        encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m) == v !! 5+    assertBool "chain code" $ encodeHex (S.encode $ xPrvChain m) == v !! 6+    assertBool "Hex PubKey" $+        encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m) == v !! 7+    assertBool "Hex PrvKey" $ encodeHex (runPut (putXPrvKey btc m)) == v !! 8+    assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 9+    assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 10++-- BIP 0032 Test Vectors+-- https://en.bitcoin.it/wiki/BIP_0032_TestVectors++xKeyVec :: [([Text], XPrvKey)]+xKeyVec = zip xKeyResVec $ foldl f [m] der+    where f acc d = acc ++ [d $ last acc]+          m   = makeXPrvKey $ fromJust $ decodeHex m0+          der = [ flip hardSubKey 0+                , flip prvSubKey 1+                , flip hardSubKey 2+                , flip prvSubKey 2+                , flip prvSubKey 1000000000+                ]++xKeyVec2 :: [([Text], XPrvKey)]+xKeyVec2 = zip xKeyResVec2 $ foldl f [m] der+    where f acc d = acc ++ [d $ last acc]+          m   = makeXPrvKey $ fromJust $ decodeHex m1+          der = [ flip prvSubKey 0+                , flip hardSubKey 2147483647+                , flip prvSubKey 1+                , flip hardSubKey 2147483646+                , flip prvSubKey 2+                ]++m0 :: Text+m0 = "000102030405060708090a0b0c0d0e0f"++xKeyResVec :: [[Text]]+xKeyResVec =+    [+      -- m+      [ "3442193e1bb70916e914552172cd4e2dbc9df811"+      , "3442193e"+      , "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"+      , "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"+      , "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW"+      , "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"+      , "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"+      , "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"+      , "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"+      , "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"+      , "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"+      ]+      -- m/0'+    , [ "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"+      , "5c1bd648"+      , "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"+      , "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"+      , "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT"+      , "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"+      , "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"+      , "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"+      , "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"+      , "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"+      , "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"+      ]+      -- m/0'/1+    , [ "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"+      , "bef5a2f9"+      , "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"+      , "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"+      , "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM"+      , "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"+      , "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"+      , "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"+      , "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"+      , "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"+      , "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"+      ]+      -- m/0'/1/2'+    , [ "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72"+      , "ee7ab90c"+      , "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x"+      , "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"+      , "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU"+      , "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"+      , "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"+      , "0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"+      , "0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"+      , "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"+      , "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"+      ]+      -- m/0'/1/2'/2+    , [ "d880d7d893848509a62d8fb74e32148dac68412f"+      , "d880d7d8"+      , "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt"+      , "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"+      , "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR"+      , "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"+      , "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"+      , "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"+      , "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"+      , "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"+      , "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"+      ]+      -- m/0'/1/2'/2/1000000000+    , [ "d69aa102255fed74378278c7812701ea641fdf32"+      , "d69aa102"+      , "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam"+      , "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"+      , "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs"+      , "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"+      , "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"+      , "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"+      , "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"+      , "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"+      , "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"+      ]+    ]++m1 :: Text+m1 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"++xKeyResVec2 :: [[Text]]+xKeyResVec2 =+    [+      -- m+      [ "bd16bee53961a47d6ad888e29545434a89bdfe95"+      , "bd16bee5"+      , "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg"+      , "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"+      , "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy"+      , "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"+      , "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"+      , "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"+      , "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"+      , "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"+      , "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"+      ]+      -- m/0+    , [ "5a61ff8eb7aaca3010db97ebda76121610b78096"+      , "5a61ff8e"+      , "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"+      , "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"+      , "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj"+      , "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"+      , "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"+      , "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"+      , "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"+      , "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"+      , "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"+      ]+      -- m/0/2147483647'+    , [ "d8ab493736da02f11ed682f88339e720fb0379d1"+      , "d8ab4937"+      , "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk"+      , "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"+      , "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr"+      , "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"+      , "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"+      , "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"+      , "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"+      , "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"+      , "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"+      ]+      -- m/0/2147483647'/1+    , [ "78412e3a2296a40de124307b6485bd19833e2e34"+      , "78412e3a"+      , "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW"+      , "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"+      , "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2"+      , "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"+      , "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"+      , "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"+      , "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"+      , "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"+      , "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"+      ]+      -- m/0/2147483647'/1/2147483646'+    , [ "31a507b815593dfc51ffc7245ae7e5aee304246e"+      , "31a507b8"+      , "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R"+      , "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"+      , "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF"+      , "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"+      , "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"+      , "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"+      , "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"+      , "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"+      , "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"+      ]+      -- m/0/2147483647'/1/2147483646'/2+    , [ "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220"+      , "26132fdb"+      , "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt"+      , "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"+      , "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK"+      , "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"+      , "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"+      , "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"+      , "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"+      , "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"+      , "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"+      ]+    ]++pubKeyOfSubKeyIsSubKeyOfPubKey :: XPrvKey -> Word32 -> Bool+pubKeyOfSubKeyIsSubKeyOfPubKey k i =+    deriveXPubKey (prvSubKey k i') == pubSubKey (deriveXPubKey k) i'+  where+    i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation+++testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+testID x =+    (A.decode . A.encode) (singleton ("object" :: String) x) ==+    Just (singleton ("object" :: String) x)++testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool+testCustom f g x = parseMaybe f (g x) == Just x++testPutGet :: Eq a => Get a -> Putter a -> a -> Bool+testPutGet g p a = runGet g (runPut (p a)) == Right a
+ test/Haskoin/Keys/MnemonicSpec.hs view
@@ -0,0 +1,494 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.Keys.MnemonicSpec (spec) where++import           Control.Monad           (zipWithM_)+import           Data.Bits               (shiftR, (.&.))+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as BS+import qualified Data.ByteString.Char8   as C+import           Data.Either             (fromRight)+import           Data.List               (isPrefixOf)+import           Data.Maybe              (fromJust)+import           Data.Serialize          (Serialize, encode)+import           Data.String.Conversions (cs)+import           Data.Text               (Text)+import qualified Data.Text               as T+import           Data.Word               (Word32, Word64)+import           Haskoin.Crypto+import           Haskoin.Keys+import           Haskoin.Test+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit+import           Test.QuickCheck         hiding ((.&.))++spec :: Spec+spec =+    describe "mnemonic" $ do+        it "entropy to mnemonic sentence" toMnemonicTest+        it "mnemonic sentence to entropy" fromMnemonicTest+        it "mnemonic sentence to seed" mnemonicToSeedTest+        it "mnemonic sentence with invalid checksum" fromMnemonicInvalidTest+        it "empty mnemonic sentence is invalid" $ sequence_ [emptyMnemonicTest]+        it "generate 12 words" $ property toMnemonic128+        it "generate 18 words" $ property toMnemonic160+        it "generate 24 words" $ property toMnemonic256+        it "generate 48 words" $ property toMnemonic512+        it "generate any number of words" $ property toMnemonicVar+        it "encode and decode 128-bit entropy" $ property fromToMnemonic128+        it "encode and decode 160-bit entropy" $ property fromToMnemonic160+        it "encode and decode 256-bit entropy" $ property fromToMnemonic256+        it "encode and decode 512-bit entropy" $ property fromToMnemonic512+        it "encode and decode n-bit entropy" $ property fromToMnemonicVar+        it "convert 128-bit mnemonic to seed" $ property mnemonicToSeed128+        it "convert 160-bit mnemonic to seed" $ property mnemonicToSeed160+        it "convert 256-bit mnemonic to seed" $ property mnemonicToSeed256+        it "convert 512-bit mnemonic to seed" $ property mnemonicToSeed512+        it "convert n-bit mnemonic to seed" $ property mnemonicToSeedVar+        it "get bits" $ property getBitsByteCount+        it "get end bits" $ property getBitsEndBits++toMnemonicTest :: Assertion+toMnemonicTest = zipWithM_ f ents mss+  where+    f e m = assertEqual "" m . h $ e+    h =+        fromRight (error "Could not decode mnemonic sentence") .+        toMnemonic . fromJust . decodeHex++fromMnemonicTest :: Assertion+fromMnemonicTest = zipWithM_ f ents mss+  where+    f e = assertEqual "" e . h+    h =+        encodeHex .+        fromRight (error "Could not decode mnemonic sentence") . fromMnemonic++mnemonicToSeedTest :: Assertion+mnemonicToSeedTest = zipWithM_ f mss seeds+  where+    f m s = assertEqual "" s . h $ m+    h =+        encodeHex .+        fromRight (error "Could not decode mnemonic seed") .+        mnemonicToSeed "TREZOR"++fromMnemonicInvalidTest :: Assertion+fromMnemonicInvalidTest = mapM_ f invalidMss+  where+    f = assertBool "" . h+    h m = case fromMnemonic m of+            Right _  -> False+            Left err -> "fromMnemonic: checksum failed:" `isPrefixOf` err++emptyMnemonicTest :: Assertion+emptyMnemonicTest =+    assertBool "" $+    case fromMnemonic "" of+        Right _  -> False+        Left err -> "fromMnemonic: empty mnemonic" `isPrefixOf` err++ents :: [Text]+ents =+    [ "00000000000000000000000000000000"+    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"+    , "80808080808080808080808080808080"+    , "ffffffffffffffffffffffffffffffff"+    , "000000000000000000000000000000000000000000000000"+    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"+    , "808080808080808080808080808080808080808080808080"+    , "ffffffffffffffffffffffffffffffffffffffffffffffff"+    , "0000000000000000000000000000000000000000000000000000000000000000"+    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"+    , "8080808080808080808080808080808080808080808080808080808080808080"+    , "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"+    , "77c2b00716cec7213839159e404db50d"+    , "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b"+    , "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982"+    , "0460ef47585604c5660618db2e6a7e7f"+    , "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f"+    , "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416"+    , "eaebabb2383351fd31d703840b32e9e2"+    , "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78"+    , "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef"+    , "18ab19a9f54a9274f03e5209a2ac8a91"+    , "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4"+    , "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419"+    ]++mss :: [Mnemonic]+mss =+    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon about"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ yellow"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage above"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"+    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon abandon abandon abandon agent"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ year wave sausage worth useful legal will"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage absurd amount doctor acoustic avoid letter always"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+      \ when"+    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon art"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ year wave sausage worth useful legal winner thank year wave sausage\+      \ worth title"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\+      \ amount doctor acoustic bless"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+      \ zoo zoo zoo zoo zoo vote"+    , "jelly better achieve collect unaware mountain thought cargo oxygen act\+      \ hood bridge"+    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\+      \ fault little machine walk stumble urge swap"+    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\+      \ multiply desk host tomato cradle drill spread actor shine dismiss\+      \ champion exotic"+    , "afford alter spike radar gate glance object seek swamp infant panel\+      \ yellow"+    , "indicate race push merry suffer human cruise dwarf pole review arch keep\+      \ canvas theme poem divorce alter left"+    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\+      \ recipe series open hour vintage deposit universe tip job dress radar\+      \ refuse motion taste"+    , "turtle front uncle idea crush write shrug there lottery flower risk\+      \ shell"+    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\+      \ void custom frequent obey nut hole price segment"+    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\+      \ catch course end lucky hurt sentence oven short ball bird grab wing top"+    , "board flee heavy tunnel powder denial science ski answer betray cargo\+      \ cat"+    , "board blade invite damage undo sun mimic interest slam gaze truly\+      \ inherit resist great inject rocket museum chief"+    , "beyond stage sleep clip because twist token leaf atom beauty genius food\+      \ business side grid unable middle armed observe pair crouch tonight away\+      \ coconut"+    ]++seeds :: [Text]+seeds =+    [ "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a69\+      \87599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"+    , "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1\+      \296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"+    , "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f1\+      \2eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8"+    , "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a133325729\+      \17f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069"+    , "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9\+      \a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa"+    , "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c3\+      \92d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd"+    , "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913f\+      \fb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65"+    , "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43\+      \348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528"+    , "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4\+      \d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8"+    , "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146a\+      \d717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87"+    , "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61a\+      \f0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f"+    , "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0\+      \a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad"+    , "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f6\+      \72a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff"+    , "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941\+      \c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5"+    , "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da2\+      \0af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67"+    , "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41\+      \f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4"+    , "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349db\+      \e2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba"+    , "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f\+      \86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449"+    , "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c4\+      \04f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c"+    , "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079\+      \403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79"+    , "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b\+      \14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c"+    , "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe0\+      \05831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8"+    , "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf0\+      \7c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9"+    , "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb\+      \86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"+    ]++invalidMss :: [Mnemonic]+invalidMss =+    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ thank"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage sausage"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"+    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ year wave sausage worth useful legal letter"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage absurd amount doctor acoustic avoid letter abandon"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+      \ zoo"+    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\+      \ abandon abandon abandon abandon abandon abandon"+    , "legal winner thank year wave sausage worth useful legal winner thank\+      \ year wave sausage worth useful legal winner thank year wave sausage\+      \ worth letter"+    , "letter advice cage absurd amount doctor acoustic avoid letter advice\+      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\+      \ amount doctor acoustic letter"+    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\+      \ zoo zoo zoo zoo zoo zoo"+    , "jelly better achieve collect unaware mountain thought cargo oxygen act\+      \ hood zoo"+    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\+      \ fault little machine walk stumble urge zoo"+    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\+      \ multiply desk host tomato cradle drill spread actor shine dismiss\+      \ champion zoo"+    , "afford alter spike radar gate glance object seek swamp infant panel\+      \ zoo"+    , "indicate race push merry suffer human cruise dwarf pole review arch keep\+      \ canvas theme poem divorce alter zoo"+    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\+      \ recipe series open hour vintage deposit universe tip job dress radar\+      \ refuse motion zoo"+    , "turtle front uncle idea crush write shrug there lottery flower risk\+      \ zoo"+    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\+      \ void custom frequent obey nut hole price zoo"+    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\+      \ catch course end lucky hurt sentence oven short ball bird grab wing zoo"+    , "board flee heavy tunnel powder denial science ski answer betray cargo\+      \ zoo"+    , "board blade invite damage undo sun mimic interest slam gaze truly\+      \ inherit resist great inject rocket museum zoo"+    , "beyond stage sleep clip because twist token leaf atom beauty genius food\+      \ business side grid unable middle armed observe pair crouch tonight away\+      \ zoo"+    ]++binWordsToBS :: Serialize a => [a] -> BS.ByteString+binWordsToBS = foldr f BS.empty+  where+    f b a = a `BS.append` encode b++{- Encode mnemonic -}++toMnemonic128 :: (Word64, Word64) -> Bool+toMnemonic128 (a, b) = l == 12+  where+    bs = encode a `BS.append` encode b+    l =+        length .+        T.words . fromRight (error "Could not decode mnemonic senttence") $+        toMnemonic bs++toMnemonic160 :: (Word32, Word64, Word64) -> Bool+toMnemonic160 (a, b, c) = l == 15+  where+    bs = BS.concat [encode a, encode b, encode c]+    l =+        length .+        T.words . fromRight (error "Could not decode mnemonic sentence") $+        toMnemonic bs++toMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool+toMnemonic256 (a, b, c, d) = l == 24+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    l =+        length .+        T.words . fromRight (error "Could not decode mnemonic sentence") $+        toMnemonic bs++toMnemonic512 ::+    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+toMnemonic512 ((a, b, c, d), (e, f, g, h)) = l == 48+  where+    bs =+        BS.concat+            [ encode a+            , encode b+            , encode c+            , encode d+            , encode e+            , encode f+            , encode g+            , encode h+            ]+    l =+        length .+        T.words . fromRight (error "Could not decode mnemonic sentence") $+        toMnemonic bs++toMnemonicVar :: [Word32] -> Property+toMnemonicVar ls = not (null ls) && length ls <= 8 ==> l == wc+  where+    bs = binWordsToBS ls+    bl = BS.length bs+    cb = bl `div` 4+    wc = (cb + bl * 8) `div` 11+    l =+        length . T.words .+        fromRight (error "Could not decode mnemonic sentence") $+        toMnemonic bs++{- Encode/Decode -}++fromToMnemonic128 :: (Word64, Word64) -> Bool+fromToMnemonic128 (a, b) = bs == bs'+  where+    bs = encode a `BS.append` encode b+    bs' =+        fromRight+            (error "Could not decode mnemonic entropy")+            (fromMnemonic =<< toMnemonic bs)++fromToMnemonic160 :: (Word32, Word64, Word64) -> Bool+fromToMnemonic160 (a, b, c) = bs == bs'+  where+    bs = BS.concat [encode a, encode b, encode c]+    bs' =+        fromRight+            (error "Could not decode mnemonic entropy")+            (fromMnemonic =<< toMnemonic bs)++fromToMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool+fromToMnemonic256 (a, b, c, d) = bs == bs'+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    bs' =+        fromRight+            (error "Could not decode mnemonic entropy")+            (fromMnemonic =<< toMnemonic bs)++fromToMnemonic512 ::+    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+fromToMnemonic512 ((a, b, c, d), (e, f, g, h)) = bs == bs'+  where+    bs =+        BS.concat+            [ encode a+            , encode b+            , encode c+            , encode d+            , encode e+            , encode f+            , encode g+            , encode h+            ]+    bs' =+        fromRight+            (error "Could not decode mnemonic entropy")+            (fromMnemonic =<< toMnemonic bs)++fromToMnemonicVar :: [Word32] -> Property+fromToMnemonicVar ls = not (null ls) && length ls <= 8 ==> bs == bs'+  where+    bs = binWordsToBS ls+    bs' =+        fromRight+            (error "Could not decode mnemonic entropy")+            (fromMnemonic =<< toMnemonic bs)++{- Mnemonic to seed -}++mnemonicToSeed128 :: (Word64, Word64) -> Bool+mnemonicToSeed128 (a, b) = l == 64+  where+    bs = encode a `BS.append` encode b+    seed =+        fromRight+            (error "Could not decode mnemonic seed")+            (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed160 :: (Word32, Word64, Word64) -> Bool+mnemonicToSeed160 (a, b, c) = l == 64+  where+    bs = BS.concat [encode a, encode b, encode c]+    seed =+        fromRight+            (error "Could not decode mnemonic seed")+            (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed256 :: (Word64, Word64, Word64, Word64) -> Bool+mnemonicToSeed256 (a, b, c, d) = l == 64+  where+    bs = BS.concat [encode a, encode b, encode c, encode d]+    seed =+        fromRight+            (error "Could not decode mnemonic seed")+            (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeed512 ::+    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool+mnemonicToSeed512 ((a, b, c, d), (e, f, g, h)) = l == 64+  where+    bs =+        BS.concat+            [ encode a+            , encode b+            , encode c+            , encode d+            , encode e+            , encode f+            , encode g+            , encode h+            ]+    seed =+        fromRight+            (error "Could not decode mnemonic seed")+            (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++mnemonicToSeedVar :: [Word32] -> Property+mnemonicToSeedVar ls = not (null ls) && length ls <= 16 ==> l == 64+  where+    bs = binWordsToBS ls+    seed =+        fromRight+            (error "Could not decode mnemonic seed")+            (mnemonicToSeed "" =<< toMnemonic bs)+    l = BS.length seed++{- Get bits from ByteString -}++data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show++instance Arbitrary ByteCountGen where+    arbitrary = do+        bs <- arbitraryBS+        i <- choose (0, BS.length bs * 8)+        return $ ByteCountGen bs i++getBitsByteCount :: ByteCountGen -> Bool+getBitsByteCount (ByteCountGen bs i) = BS.length bits == l+  where+    (q, r) = i `quotRem` 8+    bits = getBits i bs+    l = if r == 0 then q else q + 1++getBitsEndBits :: ByteCountGen -> Bool+getBitsEndBits (ByteCountGen bs i) =+    (r == 0) || (BS.last bits .&. (0xff `shiftR` r) == 0x00)+  where+    r = i `mod` 8+    bits = getBits i bs
+ test/Haskoin/KeysSpec.hs view
@@ -0,0 +1,72 @@+module Haskoin.KeysSpec (spec) where++import           Data.Aeson              as A+import qualified Data.ByteString         as BS+import           Data.Map.Strict         (singleton)+import           Data.Serialize          as S+import           Data.String             (fromString)+import           Data.String.Conversions (cs)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Crypto+import           Haskoin.Keys+import           Haskoin.Test+import           Haskoin.Util+import           Test.Hspec+import           Test.QuickCheck++spec :: Spec+spec =+    describe "keys" $ do+        let net = btc+        it "is public key canonical" $+            property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)+        it "encode and decode wif private keys" $+            property $+            forAll arbitraryKeyPair $ \(pk, _) ->+                fromWif net (toWif net pk) == Just pk+        it "encode and decode serialized private key" $+            property $ forAll arbitrary binaryPrvKey+        it "read and show public key" $+            property $ forAll arbitraryKeyPair $ \(_, k) -> read (show k) == k+        it "read and show private key" $+            property $ forAll arbitrarySecKeyI $ \k -> read (show k) == k+        it "from string public key" $+            property $+            forAll arbitraryKeyPair $ \(_, k) ->+                fromString (cs . encodeHex $ S.encode k) == k+        it "encode and decode json public key" $+            property $ forAll arbitraryKeyPair (testID . snd)+        it "encodes and decodes serialized public key" $+            property $ forAll arbitraryKeyPair $ cerealID . snd+++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalPubKey+isCanonicalPubKey :: PubKeyI -> Bool+isCanonicalPubKey p = not $+    -- Non-canonical public key: too short+    (BS.length bs < 33) ||+    -- Non-canonical public key: invalid length for uncompressed key+    (BS.index bs 0 == 4 && BS.length bs /= 65) ||+    -- Non-canonical public key: invalid length for compressed key+    (BS.index bs 0 `elem` [2,3] && BS.length bs /= 33) ||+    -- Non-canonical public key: compressed nor uncompressed+    (BS.index bs 0 `notElem` [2,3,4])+  where+    bs = S.encode p++{- Key formats -}++binaryPrvKey :: SecKey -> Bool+binaryPrvKey k =+    Right k == runGet secKeyGet (runPut (secKeyPut k)) &&+    Just k == secKey (getSecKey k)++testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+testID x =+    (A.decode . A.encode) (singleton ("object" :: String) x) ==+    Just (singleton ("object" :: String) x)++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = S.decode (S.encode x) == Right x
+ test/Haskoin/NetworkSpec.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.NetworkSpec (spec) where++import           Data.Maybe          (fromJust)+import           Data.Serialize      as S+import           Data.Text           (Text)+import           Data.Word           (Word32)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys+import           Haskoin.Network+import           Haskoin.Test+import           Haskoin.Transaction+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit          (Assertion, assertBool, assertEqual)+import           Test.QuickCheck++spec :: Spec+spec = do+    let net = btc+    describe "bloom filters" $ do+        it "bloom filter vector 1" bloomFilter1+        it "bloom filter vector 2" bloomFilter2+        it "bloom filter vector 3" bloomFilter3+    describe "relevant bloom filter update" $ do+        it "Relevant Update" relevantOutputUpdated+        it "Irrelevant Update" irrelevantOutputNotUpdated+    describe "serialization of protocol types" $ do+        it "encodes and decodes varint" $+            property $ forAll arbitraryVarInt cerealID+        it "encodes and decodes varstring" $+            property $ forAll arbitraryVarString cerealID+        it "encodes and decodes network address" $+            property $ forAll arbitraryNetworkAddress cerealID+        it "encodes and decodes invtype" $+            property $ forAll arbitraryInvType cerealID+        it "encodes and decodes invvector" $+            property $ forAll arbitraryInvVector cerealID+        it "encodes and decodes inv" $ property $ forAll arbitraryInv1 cerealID+        it "encodes and decodes version" $+            property $ forAll arbitraryVersion cerealID+        it "encodes and decodes addr" $ property $ forAll arbitraryAddr1 cerealID+        it "encodes and decodes alert" $ property $ forAll arbitraryAlert cerealID+        it "encodes and decodes reject" $+            property $ forAll arbitraryReject cerealID+        it "encodes and decodes getdata" $+            property $ forAll arbitraryGetData cerealID+        it "encodes and decodes notfound" $+            property $ forAll arbitraryNotFound cerealID+        it "encodes and decodes ping" $ property $ forAll arbitraryPing cerealID+        it "encodes and decodes pong" $ property $ forAll arbitraryPong cerealID+        it "encodes and decodes message command" $+            property $ forAll arbitraryMessageCommand cerealID+        it "encodes and decodes message header" $+            property $ forAll arbitraryMessageHeader cerealID+        it "encodes and decodes message" $+            property $+            forAll (arbitraryMessage net) $+            testPutGet (getMessage net) (putMessage net)+    describe "serialization of bloom types" $ do+        it "encodes and decodes bloom flags" $+            property $ forAll arbitraryBloomFlags cerealID+        it "encodes and decodes bloom filter" $+            property $ forAll arbitraryBloomFilter $ cerealID . lst3+        it "encodes and decodes filterload" $+            property $ forAll arbitraryFilterLoad cerealID+        it "encodes and decodes filteradd" $+            property $ forAll arbitraryFilterAdd cerealID++bloomFilter :: Word32 -> Text -> Assertion+bloomFilter n x = do+    assertBool "Bloom filter doesn't contain vector 1" $ bloomContains f1 v1+    assertBool "Bloom filter contains something it should not" $+        not $ bloomContains f1 v2+    assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3+    assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4+    assertBool "Bloom filter serialization is incorrect" $+        S.encode f4 == bs+  where+    f0 = bloomCreate 3 0.01 n BloomUpdateAll+    f1 = bloomInsert f0 v1+    f3 = bloomInsert f1 v3+    f4 = bloomInsert f3 v4+    v1 = fromJust $ decodeHex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v2 = fromJust $ decodeHex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"+    v3 = fromJust $ decodeHex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"+    v4 = fromJust $ decodeHex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"+    bs = fromJust $ decodeHex x++bloomFilter1 :: Assertion+bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"++bloomFilter2 :: Assertion+bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"++bloomFilter3 :: Assertion+bloomFilter3 =+    assertBool "Bloom filter serialization is incorrect" $+        S.encode f2 == bs+  where+    f0 = bloomCreate 2 0.001 0 BloomUpdateAll+    f1 = bloomInsert f0 $ S.encode p+    f2 = bloomInsert f1 $ S.encode $ getAddrHash160 $ pubKeyAddr p+    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"+    p = derivePubKeyI k+    bs = fromJust $ decodeHex "038fc16b080000000000000001"++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = S.decode (S.encode x) == Right x++testPutGet :: Eq a => Get a -> Putter a -> a -> Bool+testPutGet g p a = runGet g (runPut (p a)) == Right a++relevantOutputUpdated :: Assertion+relevantOutputUpdated = assertBool "Bloom filter output updated" $+    any (bloomContains bf2) spendTxInput+    where+        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"+        bf1 = bloomInsert bf0 relevantOutputHash+        bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx+        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx++irrelevantOutputNotUpdated :: Assertion+irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2+    where+        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll+        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"+        bf1 = bloomInsert bf0 relevantOutputHash+        bf2 = bloomRelevantUpdate bf1 unrelatedTx+        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx++-- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)+relevantTx :: Tx+relevantTx = Tx+        { txVersion = 1+        , txIn = [ TxIn+            { prevOutput = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1+            , scriptInput = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6"+            , txInSequence = 4294967295+            } ]+        , txOut =+            [ TxOut { outValue = 100000000, scriptOutput = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac" }+            , TxOut { outValue = 107980000, scriptOutput = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac" }+            ]+        , txWitness = []+        , txLockTime = 0+        }++-- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)+spendRelevantTx :: Tx+spendRelevantTx = Tx+        { txVersion = 1+        , txIn = [ TxIn+            { prevOutput = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0+            , scriptInput = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88"+            , txInSequence = 4294967295+            }, TxIn+            { prevOutput = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0+            , scriptInput = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee"+            , txInSequence = 4294967295+            } ]+        , txOut =+            [ TxOut { outValue = 3851100, scriptOutput = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac" }+            , TxOut { outValue = 111000000, scriptOutput = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac" }+            ]+        , txWitness = []+        , txLockTime = 0+        }++-- This random transaction is unrelated to the others+unrelatedTx :: Tx+unrelatedTx = Tx+        { txVersion = 1+        , txIn = [ TxIn+            { prevOutput = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20+            , scriptInput = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1"+            , txInSequence = 4294967295+            }]+        , txOut =+            [ TxOut { outValue = 12600000, scriptOutput = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac" }+            ]+        , txWitness = []+        , txLockTime = 0+        }
+ test/Haskoin/ScriptSpec.hs view
@@ -0,0 +1,406 @@+{-# LANGUAGE OverloadedStrings #-}+module Haskoin.ScriptSpec (spec) where++import           Control.Monad+import           Data.Aeson              as A+import           Data.ByteString         (ByteString)+import qualified Data.ByteString         as B+import qualified Data.ByteString.Lazy    as L+import           Data.Either+import           Data.List+import           Data.Map.Strict         (singleton)+import           Data.Maybe+import           Data.Monoid             ((<>))+import           Data.Serialize          as S+import           Data.String+import           Data.String.Conversions (cs)+import           Data.Text               (Text)+import           Data.Word+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys+import           Haskoin.Script+import           Haskoin.Test+import           Haskoin.Transaction+import           Haskoin.Util+import           Test.Hspec+import           Test.HUnit              as HUnit+import           Test.QuickCheck+import           Text.Read++spec :: Spec+spec = do+    let net = btc+    describe "btc scripts" $ props btc+    describe "bch scripts" $ props bch+    describe "multi signatures" $+        zipWithM_ (curry mapMulSigVector) mulSigVectors [0 ..]+    describe "signature decoding" $+        zipWithM_ (curry (sigDecodeMap net)) scriptSigSignatures [0 ..]+    describe "json serialization" $ do+        it "encodes and decodes script output" $+            forAll (arbitraryScriptOutput net) testID+        it "encodes and decodes outpoint" $ forAll arbitraryOutPoint testID+        it "encodes and decodes sighash" $ forAll arbitrarySigHash testID+        it "encodes and decodes siginput" $+            forAll (arbitrarySigInput net) (testID . fst)+    describe "script serialization" $ do+        it "encodes and decodes script op" $+            property $ forAll arbitraryScriptOp cerealID+        it "encodes and decodes script" $+            property $ forAll arbitraryScript cerealID++props :: Network -> Spec+props net = do+    standardSpec net+    strictSigSpec net+    scriptSpec net+    txSigHashForkIdSpec net+    forkIdScriptSpec net+    sigHashSpec net+    txSigHashSpec net++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = S.decode (S.encode x) == Right x++standardSpec :: Network -> Spec+standardSpec net = do+    it "has intToScriptOp . scriptOpToInt identity" $+        property $+        forAll arbitraryIntScriptOp $ \i ->+            intToScriptOp <$> scriptOpToInt i `shouldBe` Right i+    it "has decodeOutput . encodeOutput identity" $+        property $+        forAll (arbitraryScriptOutput net) $ \so ->+            decodeOutput (encodeOutput so) `shouldBe` Right so+    it "has decodeInput . encodeOutput identity" $+        property $+        forAll (arbitraryScriptInput net) $ \si ->+            decodeInput net (encodeInput si) `shouldBe` Right si+    it "can sort multisig scripts" $+        forAll arbitraryMSOutput $ \out ->+            map S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs ->+                xs == sort xs+    it "can decode inputs with empty signatures" $ do+        decodeInput net (Script [OP_0]) `shouldBe`+            Right (RegularInput (SpendPK TxSignatureEmpty))+        decodeInput net (Script [opPushData ""]) `shouldBe`+            Right (RegularInput (SpendPK TxSignatureEmpty))+        let pk =+                derivePubKeyI $+                wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1+        decodeInput net (Script [OP_0, opPushData $ S.encode pk]) `shouldBe`+            Right (RegularInput (SpendPKHash TxSignatureEmpty pk))+        decodeInput net (Script [OP_0, OP_0]) `shouldBe`+            Right (RegularInput (SpendMulSig [TxSignatureEmpty]))+        decodeInput net (Script [OP_0, OP_0, OP_0, OP_0]) `shouldBe`+            Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty))++scriptSpec :: Network -> Spec+scriptSpec net =+    when (getNetworkName net == "btc") $+    it "can verify standard scripts from script_tests.json file" $ do+        xs <- readTestFile "script_tests" :: IO [A.Value]+        let vectorsA =+                mapMaybe (A.decode . A.encode) xs :: [( String+                                                      , String+                                                      , String+                                                      , String+                                                      , String)]+            vectorsB =+                mapMaybe (A.decode . A.encode) xs :: [( [Word64]+                                                      , String+                                                      , String+                                                      , String+                                                      , String+                                                      , String)]+            vectors =+                map (\(a, b, c, d, e) -> ([0], a, b, c, d, e)) vectorsA <>+                vectorsB+        length vectors `shouldBe` 86+        forM_ vectors $ \([val], siStr, soStr, flags, res, desc)+          -- We can disable specific tests by adding a DISABLED flag in the data+         ->+            unless ("DISABLED" `isInfixOf` flags) $ do+                let _strict =+                        "DERSIG" `isInfixOf` flags ||+                        "STRICTENC" `isInfixOf` flags ||+                        "NULLDUMMY" `isInfixOf` flags+                    scriptSig     = parseScript siStr+                    scriptPubKey  = parseScript soStr+                    decodedOutput = decodeOutputBS scriptPubKey+                    ver = either (const False) $ \so ->+                        verifyStdInput+                            net+                            (spendTx scriptPubKey 0 scriptSig)+                            0+                            so+                            (val * 100000000)+                case res of+                    "OK" -> assertBool desc $ ver decodedOutput+                    _    -> assertBool desc (not $ ver decodedOutput)++forkIdScriptSpec :: Network -> Spec+forkIdScriptSpec net =+    when (isJust (getSigHashForkId net)) $+    it "can verify scripts from forkid_script_tests.json file" $ do+        xs <- readTestFile "forkid_script_tests" :: IO [A.Value]+        let vectors =+                mapMaybe (A.decode . A.encode) xs :: [( [Word64]+                                                      , String+                                                      , String+                                                      , String+                                                      , String+                                                      , String)]+        length vectors `shouldBe` 3+        forM_ vectors $ \([valBTC], siStr, soStr, _, res, _) -> do+            let val = valBTC * 100000000+                scriptSig = parseScript siStr+                scriptPubKey = parseScript soStr+                decodedOutput =+                    fromRight (error $ "Could not decode output: " <> soStr) $+                    decodeOutputBS scriptPubKey+                ver =+                    verifyStdInput+                        net+                        (spendTx scriptPubKey val scriptSig)+                        0+                        decodedOutput+                        val+            case res of+                "OK" -> ver `shouldBe` True+                _    -> ver `shouldBe` False++creditTx :: ByteString -> Word64 -> Tx+creditTx scriptPubKey val =+    Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {outValue = val, scriptOutput = scriptPubKey}+    txI =+        TxIn+        { prevOutput = nullOutPoint+        , scriptInput = S.encode $ Script [OP_0, OP_0]+        , txInSequence = maxBound+        }++spendTx :: ByteString -> Word64 -> ByteString -> Tx+spendTx scriptPubKey val scriptSig =+    Tx 1 [txI] [txO] [] 0+  where+    txO = TxOut {outValue = val, scriptOutput = B.empty}+    txI =+        TxIn+        { prevOutput = OutPoint (txHash $ creditTx scriptPubKey val) 0+        , scriptInput = scriptSig+        , txInSequence = maxBound+        }++parseScript :: String -> ByteString+parseScript str =+    B.concat $ fromMaybe err $ mapM f $ words str+  where+    f = decodeHex . cs . dropHex . replaceToken+    dropHex ('0':'x':xs) = xs+    dropHex xs           = xs+    err = error $ "Could not decode script: " <> str++replaceToken :: String -> String+replaceToken str = case readMaybe $ "OP_" <> str of+    Just opcode -> "0x" <> cs (encodeHex $ S.encode (opcode :: ScriptOp))+    _           -> str++strictSigSpec :: Network -> Spec+strictSigSpec net =+    when (getNetworkName net == "btc") $ do+        it "can decode strict signatures" $ do+            xs <- readTestFile "sig_strict"+            let vectors = mapMaybe decodeHex xs+            length vectors `shouldBe` 3+            forM_ vectors $ \sig ->+                decodeTxSig net sig `shouldSatisfy` isRight+        it "can detect non-strict signatures" $ do+            xs <- readTestFile "sig_nonstrict"+            let vectors = mapMaybe decodeHex xs+            length vectors `shouldBe` 17+            forM_ vectors $ \sig ->+                decodeTxSig net sig `shouldSatisfy` isLeft++txSigHashSpec :: Network -> Spec+txSigHashSpec net =+    when (getNetworkName net == "btc") $+    it "can produce valid sighashes from sighash.json test vectors" $ do+        xs <- readTestFile "sighash" :: IO [A.Value]+        let vectors =+                mapMaybe (A.decode . A.encode) xs :: [( String+                                                      , String+                                                      , Int+                                                      , Integer+                                                      , String)]+        length vectors `shouldBe` 500+        forM_ vectors $ \(txStr, scpStr, i, shI, resStr) -> do+            let tx = fromString txStr+                s =+                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $+                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)+                sh = fromIntegral shI+                res =+                    eitherToMaybe . S.decode . B.reverse =<<+                    decodeHex (cs resStr)+            Just (txSigHash net tx s 0 i sh) `shouldBe` res++txSigHashForkIdSpec :: Network -> Spec+txSigHashForkIdSpec net =+    when (getNetworkName net == "btc") $+    it "can produce valid sighashes from forkid_sighash.json test vectors" $ do+        xs <- readTestFile "forkid_sighash" :: IO [A.Value]+        let vectors =+                mapMaybe (A.decode . A.encode) xs :: [( String+                                                      , String+                                                      , Int+                                                      , Word64+                                                      , Integer+                                                      , String)]+        length vectors `shouldBe` 13+        forM_ vectors $ \(txStr, scpStr, i, val, shI, resStr) -> do+            let tx = fromString txStr+                s =+                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $+                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)+                sh = fromIntegral shI+                res = eitherToMaybe . S.decode =<< decodeHex (cs resStr)+            Just (txSigHashForkId net tx s val i sh) `shouldBe` res++sigHashSpec :: Network -> Spec+sigHashSpec net = do+    it "can read . show" $+        property $ forAll arbitrarySigHash $ \sh -> read (show sh) `shouldBe` sh+    it "can correctly show" $ do+        show (0x00 :: SigHash) `shouldBe` "SigHash " <> show (0x00 :: Word32)+        show (0x01 :: SigHash) `shouldBe` "SigHash " <> show (0x01 :: Word32)+        show (0xff :: SigHash) `shouldBe` "SigHash " <> show (0xff :: Word32)+        show (0xabac3344 :: SigHash) `shouldBe` "SigHash " <>+            show (0xabac3344 :: Word32)+    it "can add a forkid" $ do+        0x00 `sigHashAddForkId` 0x00 `shouldBe` 0x00+        0xff `sigHashAddForkId` 0x00ffffff `shouldBe` 0xffffffff+        0xffff `sigHashAddForkId` 0x00aaaaaa `shouldBe` 0xaaaaaaff+        0xffff `sigHashAddForkId` 0xaaaaaaaa `shouldBe` 0xaaaaaaff+        0xffff `sigHashAddForkId` 0x00004444 `shouldBe` 0x004444ff+        0xff01 `sigHashAddForkId` 0x44440000 `shouldBe` 0x44000001+        0xff03 `sigHashAddForkId` 0x00550000 `shouldBe` 0x55000003+    it "can extract a forkid" $ do+        sigHashGetForkId 0x00000000 `shouldBe` 0x00000000+        sigHashGetForkId 0x80000000 `shouldBe` 0x00800000+        sigHashGetForkId 0xffffffff `shouldBe` 0x00ffffff+        sigHashGetForkId 0xabac3403 `shouldBe` 0x00abac34+    it "can build some vectors" $ do+        sigHashAll `shouldBe` 0x01+        sigHashNone `shouldBe` 0x02+        sigHashSingle `shouldBe` 0x03+        setForkIdFlag sigHashAll `shouldBe` 0x41+        setAnyoneCanPayFlag sigHashAll `shouldBe` 0x81+        setAnyoneCanPayFlag (setForkIdFlag sigHashAll) `shouldBe` 0xc1+    it "can test flags" $ do+        hasForkIdFlag sigHashAll `shouldBe` False+        hasForkIdFlag (setForkIdFlag sigHashAll) `shouldBe` True+        hasAnyoneCanPayFlag sigHashAll `shouldBe` False+        hasAnyoneCanPayFlag (setAnyoneCanPayFlag sigHashAll) `shouldBe` True+        isSigHashAll sigHashNone `shouldBe` False+        isSigHashAll sigHashAll `shouldBe` True+        isSigHashNone sigHashSingle `shouldBe` False+        isSigHashNone sigHashNone `shouldBe` True+        isSigHashSingle sigHashAll `shouldBe` False+        isSigHashSingle sigHashSingle `shouldBe` True+        isSigHashUnknown sigHashAll `shouldBe` False+        isSigHashUnknown sigHashNone `shouldBe` False+        isSigHashUnknown sigHashSingle `shouldBe` False+        isSigHashUnknown 0x00 `shouldBe` True+        isSigHashUnknown 0x04 `shouldBe` True+    it "can decodeTxSig . encode a TxSignature" $+        property $+        forAll (arbitraryTxSignature net) $ \(_, _, ts) ->+            decodeTxSig net (encodeTxSig ts) `shouldBe` Right ts+    it "can produce the sighash one" $+        property $+        forAll (arbitraryTx net) $ forAll arbitraryScript . testSigHashOne net++testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property+testSigHashOne net tx s val acp =+    not (null $ txIn tx) ==>+    if length (txIn tx) > length (txOut tx)+        then res `shouldBe` one+        else res `shouldNotBe` one+  where+    res = txSigHash net tx s val (length (txIn tx) - 1) (f sigHashSingle)+    one = "0100000000000000000000000000000000000000000000000000000000000000"+    f =+        if acp+            then setAnyoneCanPayFlag+            else id++{-- Test Utilities --}++readTestFile :: A.FromJSON a => FilePath -> IO a+readTestFile fp = do+    bs <- L.readFile $ "data/" <> fp <> ".json"+    maybe (error $ "Could not read test file " <> fp) return $ A.decode bs++{- Parse tests from bitcoin-qt repository -}++mapMulSigVector :: ((Text, Text), Int) -> Spec+mapMulSigVector (v, i) =+    it name $ runMulSigVector v+  where+    name = "check multisig vector " <> show i++runMulSigVector :: (Text, Text) -> Assertion+runMulSigVector (a, ops) = assertBool "multisig vector" $ Just a == b+  where+    s = do+        s' <- decodeHex ops+        eitherToMaybe $ S.decode s'+    b = do+        o <- s+        d <- eitherToMaybe $ decodeOutput o+        addrToString btc $ payToScriptAddress d++sigDecodeMap :: Network -> (Text, Int) -> Spec+sigDecodeMap net (_, i) =+    it ("check signature " ++ show i) func+  where+    func = testSigDecode net $ scriptSigSignatures !! i++testSigDecode :: Network -> Text -> Assertion+testSigDecode net str =+    let bs = fromJust $ decodeHex str+        eitherSig = decodeTxSig net bs+    in assertBool+           (unwords+                [ "Decode failed:"+                , fromLeft (error "Decode did not fail") eitherSig+                ]) $+       isRight eitherSig++mulSigVectors :: [(Text, Text)]+mulSigVectors =+    [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"+      , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"+      )+    ]++scriptSigSignatures :: [Text]+scriptSigSignatures =+     -- Signature in input of txid 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053+    [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e401"+      -- Signature in input of txid+      -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.+      -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only+      -- decode strict signatures.+      -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501"+    ]+++testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+testID x =+    (A.decode . A.encode) (singleton ("object" :: String) x) ==+    Just (singleton ("object" :: String) x)
+ test/Haskoin/Test.hs view
@@ -0,0 +1,23 @@+{-|+Module      : Haskoin.Test+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX++Arbitrary instances for testing.+-}+module Haskoin.Test+    ( module X+    ) where++import           Haskoin.Test.Address     as X+import           Haskoin.Test.Block       as X+import           Haskoin.Test.Crypto      as X+import           Haskoin.Test.Keys        as X+import           Haskoin.Test.Message     as X+import           Haskoin.Test.Network     as X+import           Haskoin.Test.Script      as X+import           Haskoin.Test.Transaction as X+import           Haskoin.Test.Util        as X
+ test/Haskoin/Test/Address.hs view
@@ -0,0 +1,25 @@+{-|+Module      : Haskoin.Test.Address+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Address where++import           Haskoin.Address+import           Haskoin.Test.Crypto+import           Test.QuickCheck++-- | Arbitrary pay-to-public-key-hash or pay-to-script-hash address.+arbitraryAddress :: Gen Address+arbitraryAddress = oneof [arbitraryPubKeyAddress, arbitraryScriptAddress]++-- | Arbitrary pay-to-public-key-hash address.+arbitraryPubKeyAddress :: Gen Address+arbitraryPubKeyAddress = p2pkhAddr <$> arbitraryHash160++-- | Arbitrary pay-to-script-hash address.+arbitraryScriptAddress :: Gen Address+arbitraryScriptAddress = p2shAddr <$> arbitraryHash160
+ test/Haskoin/Test/Block.hs view
@@ -0,0 +1,69 @@+{-|+Module      : Haskoin.Test.Block+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Block where++import           Haskoin.Block.Common+import           Haskoin.Block.Merkle+import           Haskoin.Constants+import           Haskoin.Test.Crypto+import           Haskoin.Test.Network+import           Haskoin.Test.Transaction+import           Test.QuickCheck++-- | Block full or arbitrary transactions.+arbitraryBlock :: Network -> Gen Block+arbitraryBlock net = do+    h   <- arbitraryBlockHeader+    c   <- choose (0,10)+    txs <- vectorOf c (arbitraryTx net)+    return $ Block h txs++-- | Block header with random hash.+arbitraryBlockHeader :: Gen BlockHeader+arbitraryBlockHeader =+    BlockHeader <$> arbitrary+                      <*> arbitraryBlockHash+                      <*> arbitraryHash256+                      <*> arbitrary+                      <*> arbitrary+                      <*> arbitrary++-- | Arbitrary block hash.+arbitraryBlockHash :: Gen BlockHash+arbitraryBlockHash = BlockHash <$> arbitraryHash256++-- | Arbitrary 'GetBlocks' object with at least one block hash.+arbitraryGetBlocks :: Gen GetBlocks+arbitraryGetBlocks =+    GetBlocks <$> arbitrary+              <*> listOf1 arbitraryBlockHash+              <*> arbitraryBlockHash++-- | Arbitrary 'GetHeaders' object with at least one block header.+arbitraryGetHeaders :: Gen GetHeaders+arbitraryGetHeaders =+    GetHeaders <$> arbitrary+               <*> listOf1 arbitraryBlockHash+               <*> arbitraryBlockHash++-- | Arbitrary 'Headers' object with at least one block header.+arbitraryHeaders :: Gen Headers+arbitraryHeaders =+    Headers <$> listOf1 ((,) <$> arbitraryBlockHeader <*> arbitraryVarInt)++-- | Arbitrary 'MerkleBlock' with at least one hash.+arbitraryMerkleBlock :: Gen MerkleBlock+arbitraryMerkleBlock = do+    bh     <- arbitraryBlockHeader+    ntx    <- arbitrary+    hashes <- listOf1 arbitraryHash256+    c      <- choose (1,10)+    flags  <- vectorOf (c*8) arbitrary+    return $ MerkleBlock bh ntx hashes flags+
+ test/Haskoin/Test/Crypto.hs view
@@ -0,0 +1,33 @@+{-|+Module      : Haskoin.Test.Crypto+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Crypto where++import           Haskoin.Crypto.Hash+import           Haskoin.Test.Util+import           Test.QuickCheck++-- | Arbitrary 160-bit hash.+arbitraryHash160 :: Gen Hash160+arbitraryHash160 =+    ripemd160 <$> arbitraryBSn 20++-- | Arbitrary 256-bit hash.+arbitraryHash256 :: Gen Hash256+arbitraryHash256 =+    sha256 <$> arbitraryBSn 32++-- | Arbitrary 512-bit hash.+arbitraryHash512 :: Gen Hash512+arbitraryHash512 =+    sha512 <$> arbitraryBSn 64++-- | Arbitrary 32-bit checksum.+arbitraryCheckSum32 :: Gen CheckSum32+arbitraryCheckSum32 =+    checkSum32 <$> arbitraryBSn 4
+ test/Haskoin/Test/Keys.hs view
@@ -0,0 +1,85 @@+{-|+Module      : Haskoin.Test.Keys+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Keys where++import           Data.Bits             (clearBit)+import           Data.List             (foldl')+import           Data.Word             (Word32)+import           Haskoin.Crypto+import           Haskoin.Keys.Common+import           Haskoin.Keys.Extended+import           Haskoin.Test.Crypto+import           Test.QuickCheck++-- | Arbitrary private key with arbitrary compressed flag.+arbitrarySecKeyI :: Gen SecKeyI+arbitrarySecKeyI = wrapSecKey <$> arbitrary <*> arbitrary++-- | Arbitrary keypair, both either compressed or not.+arbitraryKeyPair :: Gen (SecKeyI, PubKeyI)+arbitraryKeyPair = do+    k <- arbitrarySecKeyI+    return (k, derivePubKeyI k)++-- | Arbitrary extended private key.+arbitraryXPrvKey :: Gen XPrvKey+arbitraryXPrvKey =+    XPrvKey <$> arbitrary+            <*> arbitrary+            <*> arbitrary+            <*> arbitraryHash256+            <*> arbitrary++-- | Arbitrary extended public key with its corresponding private key.+arbitraryXPubKey :: Gen (XPrvKey, XPubKey)+arbitraryXPubKey = (\k -> (k, deriveXPubKey k)) <$> arbitraryXPrvKey++{- Custom derivations -}++-- | Arbitrary derivation index with last bit unset.+genIndex :: Gen Word32+genIndex = (`clearBit` 31) <$> arbitrary++-- | Arbitrary BIP-32 path index. Can be hardened or not.+arbitraryBip32PathIndex :: Gen Bip32PathIndex+arbitraryBip32PathIndex =+    oneof [ Bip32SoftIndex <$> genIndex+          , Bip32HardIndex <$> genIndex+          ]++-- | Arbitrary BIP-32 derivation path composed of only hardened derivations.+arbitraryHardPath :: Gen HardPath+arbitraryHardPath = foldl' (:|) Deriv <$> listOf genIndex++-- | Arbitrary BIP-32 derivation path composed of only non-hardened derivations.+arbitrarySoftPath :: Gen SoftPath+arbitrarySoftPath = foldl' (:/) Deriv <$> listOf genIndex++-- | Arbitrary derivation path composed of hardened and non-hardened derivations.+arbitraryDerivPath :: Gen DerivPath+arbitraryDerivPath = concatBip32Segments <$> listOf arbitraryBip32PathIndex++-- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or+-- 'ParsedEmpty' elements.+arbitraryParsedPath :: Gen ParsedPath+arbitraryParsedPath =+    oneof [ ParsedPrv <$> arbitraryDerivPath+          , ParsedPub <$> arbitraryDerivPath+          , ParsedEmpty <$> arbitraryDerivPath+          ]++-- | Arbitrary message hash, private key, nonce and corresponding signature. The+-- signature is generated with a random message, random private key and a random+-- nonce.+arbitrarySignature :: Gen (Hash256, SecKey, Sig)+arbitrarySignature = do+    m <- arbitraryHash256+    key <- arbitrary+    let sig = signHash key m+    return (m, key, sig)
+ test/Haskoin/Test/Message.hs view
@@ -0,0 +1,52 @@+{-|+Module      : Haskoin.Test.Message+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Message where++import           Haskoin.Constants+import           Haskoin.Network.Message+import           Haskoin.Test.Block+import           Haskoin.Test.Crypto+import           Haskoin.Test.Network+import           Haskoin.Test.Transaction+import           Test.QuickCheck++-- | Arbitrary 'MessageHeader'.+arbitraryMessageHeader :: Gen MessageHeader+arbitraryMessageHeader =+    MessageHeader <$> arbitrary+                  <*> arbitraryMessageCommand+                  <*> arbitrary+                  <*> arbitraryCheckSum32++-- | Arbitrary 'Message'.+arbitraryMessage :: Network -> Gen Message+arbitraryMessage net =+    oneof+        [ MVersion <$> arbitraryVersion+        , return MVerAck+        , MAddr <$> arbitraryAddr1+        , MInv <$> arbitraryInv1+        , MGetData <$> arbitraryGetData+        , MNotFound <$> arbitraryNotFound+        , MGetBlocks <$> arbitraryGetBlocks+        , MGetHeaders <$> arbitraryGetHeaders+        , MTx <$> arbitraryTx net+        , MBlock <$> arbitraryBlock net+        , MMerkleBlock <$> arbitraryMerkleBlock+        , MHeaders <$> arbitraryHeaders+        , return MGetAddr+        , MFilterLoad <$> arbitraryFilterLoad+        , MFilterAdd <$> arbitraryFilterAdd+        , return MFilterClear+        , MPing <$> arbitraryPing+        , MPong <$> arbitraryPong+        , MAlert <$> arbitraryAlert+        , MReject <$> arbitraryReject+        , return MSendHeaders+        ]
+ test/Haskoin/Test/Network.hs view
@@ -0,0 +1,178 @@+{-|+Module      : Haskoin.Test.Network+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Network where++import qualified Data.ByteString       as BS (empty, pack)+import qualified Data.ByteString.Char8 as C8+import           Data.Word             (Word16, Word32)+import           Haskoin.Network+import           Haskoin.Test.Crypto+import           Haskoin.Test.Util+import           Network.Socket        (SockAddr (..))+import           Test.QuickCheck++-- | Arbitrary 'VarInt'.+arbitraryVarInt :: Gen VarInt+arbitraryVarInt = VarInt <$> arbitrary++-- | Arbitrary 'VarString'.+arbitraryVarString :: Gen VarString+arbitraryVarString = VarString <$> arbitraryBS++-- | Arbitrary 'NetworkAddress'.+arbitraryNetworkAddress :: Gen NetworkAddress+arbitraryNetworkAddress = do+    s <- arbitrary+    a <- arbitrary+    p <- arbitrary+    d <- oneof+        [ do+            b <- arbitrary+            c <- arbitrary+            d <- arbitrary+            return $ SockAddrInet6 (fromIntegral p) 0 (a,b,c,d) 0+        , return $ SockAddrInet (fromIntegral (p :: Word16)) a+        ]+    let n = sockToHostAddress d+    return $ NetworkAddress s n++-- | Arbitrary 'NetworkAddressTime'.+arbitraryNetworkAddressTime :: Gen (Word32, NetworkAddress)+arbitraryNetworkAddressTime = (,) <$> arbitrary <*> arbitraryNetworkAddress++-- | Arbitrary 'InvType'.+arbitraryInvType :: Gen InvType+arbitraryInvType = elements [InvError, InvTx, InvBlock, InvMerkleBlock]++-- | Arbitrary 'InvVector'.+arbitraryInvVector :: Gen InvVector+arbitraryInvVector = InvVector <$> arbitraryInvType <*> arbitraryHash256++-- | Arbitrary non-empty 'Inv'.+arbitraryInv1 :: Gen Inv+arbitraryInv1 = Inv <$> listOf1 arbitraryInvVector++-- | Arbitrary 'Version'.+arbitraryVersion :: Gen Version+arbitraryVersion =+    Version <$> arbitrary+            <*> arbitrary+            <*> arbitrary+            <*> arbitraryNetworkAddress+            <*> arbitraryNetworkAddress+            <*> arbitrary+            <*> arbitraryVarString+            <*> arbitrary+            <*> arbitrary++-- | Arbitrary non-empty 'Addr'.+arbitraryAddr1 :: Gen Addr+arbitraryAddr1 = Addr <$> listOf1 arbitraryNetworkAddressTime++-- | Arbitrary 'Alert' with random payload and signature. Signature is not+-- valid.+arbitraryAlert :: Gen Alert+arbitraryAlert = Alert <$> arbitraryVarString <*> arbitraryVarString++-- | Arbitrary 'Reject'.+arbitraryReject :: Gen Reject+arbitraryReject = do+    m <- arbitraryMessageCommand+    c <- arbitraryRejectCode+    s <- arbitraryVarString+    d <- oneof [ return BS.empty+               , BS.pack <$> vectorOf 32 arbitrary+               ]+    return $ Reject m c s d++-- | Arbitrary 'RejectCode'.+arbitraryRejectCode :: Gen RejectCode+arbitraryRejectCode =+    elements+        [ RejectMalformed+        , RejectInvalid+        , RejectInvalid+        , RejectDuplicate+        , RejectNonStandard+        , RejectDust+        , RejectInsufficientFee+        , RejectCheckpoint+        ]++-- | Arbitrary non-empty 'GetData'.+arbitraryGetData :: Gen GetData+arbitraryGetData = GetData <$> listOf1 arbitraryInvVector++-- | Arbitrary 'NotFound'.+arbitraryNotFound :: Gen NotFound+arbitraryNotFound = NotFound <$> listOf1 arbitraryInvVector++-- | Arbitrary 'Ping'.+arbitraryPing :: Gen Ping+arbitraryPing = Ping <$> arbitrary++-- | Arbitrary 'Pong'.+arbitraryPong :: Gen Pong+arbitraryPong = Pong <$> arbitrary++-- | Arbitrary bloom filter flags.+arbitraryBloomFlags :: Gen BloomFlags+arbitraryBloomFlags =+    elements+        [ BloomUpdateNone+        , BloomUpdateAll+        , BloomUpdateP2PubKeyOnly+        ]++-- | Arbitrary bloom filter with its corresponding number of elements+-- and false positive rate.+arbitraryBloomFilter :: Gen (Int, Double, BloomFilter)+arbitraryBloomFilter = do+    n     <- choose (0,100000)+    fp    <- choose (1e-8,1)+    tweak <- arbitrary+    fl    <- arbitraryBloomFlags+    return (n, fp, bloomCreate n fp tweak fl)++-- | Arbitrary 'FilterLoad'.+arbitraryFilterLoad :: Gen FilterLoad+arbitraryFilterLoad = do+    (_, _, bf) <- arbitraryBloomFilter+    return $ FilterLoad bf++-- | Arbitrary 'FilterAdd'.+arbitraryFilterAdd :: Gen FilterAdd+arbitraryFilterAdd = FilterAdd <$> arbitraryBS++-- | Arbitrary 'MessageCommand'.+arbitraryMessageCommand :: Gen MessageCommand+arbitraryMessageCommand = do+    ASCIIString str <- arbitrary+    elements+        [ MCVersion+        , MCVerAck+        , MCAddr+        , MCInv+        , MCGetData+        , MCNotFound+        , MCGetBlocks+        , MCGetHeaders+        , MCTx+        , MCBlock+        , MCMerkleBlock+        , MCHeaders+        , MCGetAddr+        , MCFilterLoad+        , MCFilterAdd+        , MCFilterClear+        , MCPing+        , MCPong+        , MCAlert+        , MCOther (C8.take 12 (C8.pack (filter (/= '\NUL') str)))+        ]
+ test/Haskoin/Test/Script.hs view
@@ -0,0 +1,374 @@+{-# LANGUAGE LambdaCase #-}+{-|+Module      : Haskoin.Test.Script+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Script where++import           Crypto.Secp256k1+import           Data.Maybe+import           Data.Word+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys.Common+import           Haskoin.Script+import           Haskoin.Test.Address+import           Haskoin.Test.Crypto+import           Haskoin.Test.Keys+import           Haskoin.Test.Util+import           Haskoin.Transaction.Common+import           Haskoin.Util+import           Test.QuickCheck++-- | Arbitrary 'Script' with random script ops.+arbitraryScript :: Gen Script+arbitraryScript = Script <$> listOf arbitraryScriptOp++-- | Arbitrary 'ScriptOp' (push operations have random data).+arbitraryScriptOp :: Gen ScriptOp+arbitraryScriptOp =+    oneof+          -- Pushing Data+        [ opPushData <$> arbitraryBS1+        , return OP_0+        , return OP_1NEGATE+        , return OP_RESERVED+        , return OP_1+        , return OP_2+        , return OP_3+        , return OP_4+        , return OP_5+        , return OP_6+        , return OP_7+        , return OP_8+        , return OP_9+        , return OP_10+        , return OP_11+        , return OP_12+        , return OP_13+        , return OP_14+        , return OP_15+        , return OP_16+        -- Flow control+        , return OP_NOP+        , return OP_VER+        , return OP_IF+        , return OP_NOTIF+        , return OP_VERIF+        , return OP_VERNOTIF+        , return OP_ELSE+        , return OP_ENDIF+        , return OP_VERIFY+        , return OP_RETURN+        -- Stack operations+        , return OP_TOALTSTACK+        , return OP_FROMALTSTACK+        , return OP_IFDUP+        , return OP_DEPTH+        , return OP_DROP+        , return OP_DUP+        , return OP_NIP+        , return OP_OVER+        , return OP_PICK+        , return OP_ROLL+        , return OP_ROT+        , return OP_SWAP+        , return OP_TUCK+        , return OP_2DROP+        , return OP_2DUP+        , return OP_3DUP+        , return OP_2OVER+        , return OP_2ROT+        , return OP_2SWAP+        -- Splice+        , return OP_CAT+        , return OP_SUBSTR+        , return OP_LEFT+        , return OP_RIGHT+        , return OP_SIZE+        -- Bitwise logic+        , return OP_INVERT+        , return OP_AND+        , return OP_OR+        , return OP_XOR+        , return OP_EQUAL+        , return OP_EQUALVERIFY+        , return OP_RESERVED1+        , return OP_RESERVED2+        -- Arithmetic+        , return OP_1ADD+        , return OP_1SUB+        , return OP_2MUL+        , return OP_2DIV+        , return OP_NEGATE+        , return OP_ABS+        , return OP_NOT+        , return OP_0NOTEQUAL+        , return OP_ADD+        , return OP_SUB+        , return OP_MUL+        , return OP_DIV+        , return OP_MOD+        , return OP_LSHIFT+        , return OP_RSHIFT+        , return OP_BOOLAND+        , return OP_BOOLOR+        , return OP_NUMEQUAL+        , return OP_NUMEQUALVERIFY+        , return OP_NUMNOTEQUAL+        , return OP_LESSTHAN+        , return OP_GREATERTHAN+        , return OP_LESSTHANOREQUAL+        , return OP_GREATERTHANOREQUAL+        , return OP_MIN+        , return OP_MAX+        , return OP_WITHIN+        -- Crypto+        , return OP_RIPEMD160+        , return OP_SHA1+        , return OP_SHA256+        , return OP_HASH160+        , return OP_HASH256+        , return OP_CODESEPARATOR+        , return OP_CHECKSIG+        , return OP_CHECKSIGVERIFY+        , return OP_CHECKMULTISIG+        , return OP_CHECKMULTISIGVERIFY+        -- Expansion+        , return OP_NOP1+        , return OP_NOP2+        , return OP_NOP3+        , return OP_NOP4+        , return OP_NOP5+        , return OP_NOP6+        , return OP_NOP7+        , return OP_NOP8+        , return OP_NOP9+        , return OP_NOP10+        -- Other+        , return OP_PUBKEYHASH+        , return OP_PUBKEY+        , return $ OP_INVALIDOPCODE 0xff+        ]++-- | Arbtirary 'ScriptOp' with a value in @[OP_1 .. OP_16]@.+arbitraryIntScriptOp :: Gen ScriptOp+arbitraryIntScriptOp =+    elements+        [ OP_1,  OP_2,  OP_3,  OP_4+        , OP_5,  OP_6,  OP_7,  OP_8+        , OP_9,  OP_10, OP_11, OP_12+        , OP_13, OP_14, OP_15, OP_16+        ]++-- | Arbitrary 'PushDataType'.+arbitraryPushDataType :: Gen PushDataType+arbitraryPushDataType = elements [OPCODE, OPDATA1, OPDATA2, OPDATA4]++-- | Arbitrary 'SigHash' (including invalid/unknown sighash codes).+arbitrarySigHash :: Gen SigHash+arbitrarySigHash = fromIntegral <$> (arbitrary :: Gen Word32)++-- | Arbitrary valid 'SigHash'.+arbitraryValidSigHash :: Network -> Gen SigHash+arbitraryValidSigHash net = do+    sh <- elements [sigHashAll, sigHashNone, sigHashSingle]+    f1 <-+        elements $+        if isJust (getSigHashForkId net)+            then [id, setForkIdFlag]+            else [id]+    f2 <- elements [id, setAnyoneCanPayFlag]+    return $ f1 $ f2 sh++-- | Arbitrary message hash, private key and corresponding 'TxSignature'. The+-- signature is generated deterministically using a random message and a random+-- private key.+arbitraryTxSignature :: Network -> Gen (TxHash, SecKey, TxSignature)+arbitraryTxSignature net = do+    (m, key, sig) <- arbitrarySignature+    sh <- (fromIntegral <$> (arbitrary :: Gen Word8)) `suchThat` filterBad+    let txsig = TxSignature sig sh+    return (TxHash m, key, txsig)+  where+    filterBad sh = not $+        isSigHashUnknown sh ||+        isNothing (getSigHashForkId net) && hasForkIdFlag sh++-- | Arbitrary transaction signature that could also be empty.+arbitraryTxSignatureEmpty :: Network -> Gen TxSignature+arbitraryTxSignatureEmpty net =+    frequency [ (1, return TxSignatureEmpty)+              , (10, lst3 <$> arbitraryTxSignature net)+              ]++-- | Arbitrary m of n parameters.+arbitraryMSParam :: Gen (Int, Int)+arbitraryMSParam = do+    m <- choose (1,16)+    n <- choose (m,16)+    return (m, n)++-- | Arbitrary 'ScriptOutput' (Can by any valid type).+arbitraryScriptOutput :: Network -> Gen ScriptOutput+arbitraryScriptOutput net =+    oneof $+    [ arbitraryPKOutput+    , arbitraryPKHashOutput+    , arbitraryMSOutput+    , arbitrarySHOutput+    , arbitraryDCOutput+    ] +++    if getSegWit net+        then [arbitraryWPKHashOutput, arbitraryWSHOutput]+        else []++-- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS'+-- (Not 'PayScriptHash', 'DataCarrier', or SegWit)+arbitrarySimpleOutput :: Gen ScriptOutput+arbitrarySimpleOutput =+    oneof+        [ arbitraryPKOutput+        , arbitraryPKHashOutput+        , arbitraryMSOutput+        ]++-- | Arbitrary 'ScriptOutput' of type 'PayPK'+arbitraryPKOutput :: Gen ScriptOutput+arbitraryPKOutput =  PayPK . snd <$> arbitraryKeyPair++-- | Arbitrary 'ScriptOutput' of type 'PayPKHash'+arbitraryPKHashOutput :: Gen ScriptOutput+arbitraryPKHashOutput = PayPKHash <$> arbitraryHash160++-- | Arbitrary 'PayWitnessPKHash' output.+arbitraryWPKHashOutput :: Gen ScriptOutput+arbitraryWPKHashOutput = PayWitnessPKHash <$> arbitraryHash160++-- | Arbitrary 'PayWitnessScriptHash' output.+arbitraryWSHOutput :: Gen ScriptOutput+arbitraryWSHOutput = PayWitnessScriptHash <$> arbitraryHash256++-- | Arbitrary 'ScriptOutput' of type 'PayMS'.+arbitraryMSOutput :: Gen ScriptOutput+arbitraryMSOutput = do+    (m, n) <- arbitraryMSParam+    keys <- map snd <$> vectorOf n arbitraryKeyPair+    return $ PayMulSig keys m++-- | Arbitrary 'ScriptOutput' of type 'PayMS', only using compressed keys.+arbitraryMSOutputC :: Gen ScriptOutput+arbitraryMSOutputC = do+    (m, n) <- arbitraryMSParam+    keys <-+        map snd <$>+        vectorOf n (arbitraryKeyPair `suchThat` (pubKeyCompressed . snd))+    return $ PayMulSig keys m++-- | Arbitrary 'ScriptOutput' of type 'PayScriptHash'.+arbitrarySHOutput :: Gen ScriptOutput+arbitrarySHOutput = PayScriptHash . getAddrHash160 <$> arbitraryScriptAddress++-- | Arbitrary 'ScriptOutput' of type 'DataCarrier'.+arbitraryDCOutput :: Gen ScriptOutput+arbitraryDCOutput = DataCarrier <$> arbitraryBS1++-- | Arbitrary 'ScriptInput'.+arbitraryScriptInput :: Network -> Gen ScriptInput+arbitraryScriptInput net =+    oneof+        [ arbitraryPKInput net+        , arbitraryPKHashInput net+        , arbitraryMSInput net+        , arbitrarySHInput net+        ]++-- | Arbitrary 'ScriptInput' of type 'SpendPK', 'SpendPKHash' or 'SpendMulSig'+-- (not 'ScriptHashInput')+arbitrarySimpleInput :: Network -> Gen ScriptInput+arbitrarySimpleInput net =+    oneof+        [ arbitraryPKInput net+        , arbitraryPKHashInput net+        , arbitraryMSInput net+        ]++-- | Arbitrary 'ScriptInput' of type 'SpendPK'.+arbitraryPKInput :: Network -> Gen ScriptInput+arbitraryPKInput net = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net++-- | Arbitrary 'ScriptInput' of type 'SpendPK'.+arbitraryPKHashInput :: Network -> Gen ScriptInput+arbitraryPKHashInput net = do+    sig <- arbitraryTxSignatureEmpty net+    key <- snd <$> arbitraryKeyPair+    return $ RegularInput $ SpendPKHash sig key++-- | Like 'arbitraryPKHashInput' without empty signatures.+arbitraryPKHashInputFull :: Network -> Gen ScriptInput+arbitraryPKHashInputFull net = do+    sig <- lst3 <$> arbitraryTxSignature net+    key <- snd <$> arbitraryKeyPair+    return $ RegularInput $ SpendPKHash sig key++-- | Like above but only compressed.+arbitraryPKHashInputFullC :: Network -> Gen ScriptInput+arbitraryPKHashInputFullC net = do+    sig <- lst3 <$> arbitraryTxSignature net+    key <- fmap snd $ arbitraryKeyPair `suchThat` (pubKeyCompressed . snd)+    return $ RegularInput $ SpendPKHash sig key++-- | Arbitrary 'ScriptInput' of type 'SpendMulSig'.+arbitraryMSInput :: Network -> Gen ScriptInput+arbitraryMSInput net = do+    m    <- fst <$> arbitraryMSParam+    sigs <- vectorOf m (arbitraryTxSignatureEmpty net)+    return $ RegularInput $ SpendMulSig sigs++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput'.+arbitrarySHInput :: Network -> Gen ScriptInput+arbitrarySHInput net = do+    i <- arbitrarySimpleInput net+    ScriptHashInput (getRegularInput i) <$> arbitrarySimpleOutput++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a+-- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.+arbitraryMulSigSHInput :: Network -> Gen ScriptInput+arbitraryMulSigSHInput net =+    arbitraryMSOutput >>= \case+        rdm@(PayMulSig _ m) -> do+            sigs <- vectorOf m (arbitraryTxSignatureEmpty net)+            return $ ScriptHashInput (SpendMulSig sigs) rdm+        _ -> undefined++-- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a+-- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.+arbitraryMulSigSHInputC :: Network -> Gen ScriptInput+arbitraryMulSigSHInputC net =+    arbitraryMSOutputC >>= \case+        rdm@(PayMulSig _ m) -> do+          sigs <- vectorOf m (arbitraryTxSignatureEmpty net)+          return $ ScriptHashInput (SpendMulSig sigs) rdm+        _ -> undefined++-- | Like 'arbitraryMulSigSHCInput' with no empty signatures.+arbitraryMulSigSHInputFull :: Network -> Gen ScriptInput+arbitraryMulSigSHInputFull net =+    arbitraryMSOutput >>= \case+        rdm@(PayMulSig _ m) -> do+            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)+            return $ ScriptHashInput (SpendMulSig sigs) rdm+        _ -> undefined++-- | Like 'arbitraryMulSigSHCInput' with no empty signatures.+arbitraryMulSigSHInputFullC :: Network -> Gen ScriptInput+arbitraryMulSigSHInputFullC net =+    arbitraryMSOutputC >>= \case+        rdm@(PayMulSig _ m) -> do+            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)+            return $ ScriptHashInput (SpendMulSig sigs) rdm+        _ -> undefined
+ test/Haskoin/Test/Transaction.hs view
@@ -0,0 +1,275 @@+{-|+Module      : Haskoin.Test.Transaction+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Transaction where++import           Control.Monad+import qualified Data.ByteString     as BS+import           Data.Either         (fromRight)+import           Data.List           (nub, nubBy, permutations)+import           Data.Word           (Word64)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys.Common+import           Haskoin.Script+import           Haskoin.Test.Crypto+import           Haskoin.Test.Keys+import           Haskoin.Test.Script+import           Haskoin.Test.Util+import           Haskoin.Transaction+import           Test.QuickCheck++-- | Wrapped coin value for testing.+newtype TestCoin = TestCoin { getTestCoin :: Word64 }+    deriving (Eq, Show)++instance Coin TestCoin where+    coinValue = getTestCoin++-- | Arbitrary transaction hash (for non-existent transaction).+arbitraryTxHash :: Gen TxHash+arbitraryTxHash = TxHash <$> arbitraryHash256++-- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14)+arbitrarySatoshi :: Network -> Gen TestCoin+arbitrarySatoshi net = TestCoin <$> choose (1, getMaxSatoshi net)++-- | Arbitrary 'OutPoint'.+arbitraryOutPoint :: Gen OutPoint+arbitraryOutPoint = OutPoint <$> arbitraryTxHash <*> arbitrary++-- | Arbitrary 'TxOut'.+arbitraryTxOut :: Network -> Gen TxOut+arbitraryTxOut net =+    TxOut <$> (getTestCoin <$> arbitrarySatoshi net)+          <*> (encodeOutputBS <$> arbitraryScriptOutput net)++-- | Arbitrary 'TxIn'.+arbitraryTxIn :: Network -> Gen TxIn+arbitraryTxIn net =+    TxIn <$> arbitraryOutPoint+         <*> (encodeInputBS <$> arbitraryScriptInput net)+         <*> arbitrary++-- | Arbitrary transaction. Can be regular or with witnesses.+arbitraryTx :: Network -> Gen Tx+arbitraryTx net = oneof [arbitraryLegacyTx net, arbitraryWitnessTx net]++-- | Arbitrary regular transaction.+arbitraryLegacyTx :: Network -> Gen Tx+arbitraryLegacyTx net = arbitraryWLTx net False++-- | Arbitrary witness transaction (witness data is fake).+arbitraryWitnessTx :: Network -> Gen Tx+arbitraryWitnessTx net = arbitraryWLTx net True++-- | Arbitrary witness or legacy transaction.+arbitraryWLTx :: Network -> Bool -> Gen Tx+arbitraryWLTx net wit = do+    ni <- choose (0, 5)+    no <-+        if wit+            then choose (0, 5)+            else choose+                     ( if ni == 0+                           then 2+                           else 0+                     , 5 -- avoid witness case+                     )+    inps <- vectorOf ni (arbitraryTxIn net)+    outs <- vectorOf no (arbitraryTxOut net)+    let uniqueInps = nubBy (\a b -> prevOutput a == prevOutput b) inps+    w <- if wit then vectorOf (length uniqueInps) (listOf arbitraryBS) else return []+    Tx <$> arbitrary <*> pure uniqueInps <*> pure outs <*> pure w <*> arbitrary++-- | Arbitrary transaction containing only inputs of type 'SpendPKHash',+-- 'SpendScriptHash' (multisig) and outputs of type 'PayPKHash' and 'PaySH'.+-- Only compressed public keys are used.+arbitraryAddrOnlyTx :: Network -> Gen Tx+arbitraryAddrOnlyTx net = do+    ni <- choose (0, 5)+    no <- choose (0, 5)+    inps <- vectorOf ni (arbitraryAddrOnlyTxIn net)+    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)+    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary++-- | Like 'arbitraryAddrOnlyTx' without empty signatures in the inputs.+arbitraryAddrOnlyTxFull :: Network -> Gen Tx+arbitraryAddrOnlyTxFull net = do+    ni <- choose (0, 5)+    no <- choose (0, 5)+    inps <- vectorOf ni (arbitraryAddrOnlyTxInFull net)+    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)+    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary++-- | Arbitrary TxIn that can only be of type 'SpendPKHash' or 'SpendScriptHash'+-- (multisig). Only compressed public keys are used.+arbitraryAddrOnlyTxIn :: Network -> Gen TxIn+arbitraryAddrOnlyTxIn net = do+    inp <- oneof [arbitraryPKHashInput net, arbitraryMulSigSHInput net]+    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary++-- | like 'arbitraryAddrOnlyTxIn' with no empty signatures.+arbitraryAddrOnlyTxInFull :: Network -> Gen TxIn+arbitraryAddrOnlyTxInFull net = do+    inp <-+        oneof [arbitraryPKHashInputFullC net, arbitraryMulSigSHInputFullC net]+    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary++-- | Arbitrary 'TxOut' that can only be of type 'PayPKHash' or 'PaySH'.+arbitraryAddrOnlyTxOut :: Network -> Gen TxOut+arbitraryAddrOnlyTxOut net = do+    v <- getTestCoin <$> arbitrarySatoshi net+    out <- oneof [arbitraryPKHashOutput, arbitrarySHOutput]+    return $ TxOut v $ encodeOutputBS out++-- | Arbitrary 'SigInput' with the corresponding private keys used+-- to generate the 'ScriptOutput' or 'RedeemScript'.+arbitrarySigInput :: Network -> Gen (SigInput, [SecKeyI])+arbitrarySigInput net =+    oneof+        [ wrapKey <$> arbitraryPKSigInput net+        , wrapKey <$> arbitraryPKHashSigInput net+        , arbitraryMSSigInput net+        , arbitrarySHSigInput net+        , wrapKey <$> arbitraryWPKHSigInput net+        , arbitraryWSHSigInput net+        ]++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPK'.+arbitraryPKSigInput :: Network -> Gen (SigInput, SecKeyI)+arbitraryPKSigInput net = arbitraryAnyInput net False++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash'.+arbitraryPKHashSigInput :: Network -> Gen (SigInput, SecKeyI)+arbitraryPKHashSigInput net = arbitraryAnyInput net True++-- | Arbitrary 'SigInput'.+arbitraryAnyInput :: Network -> Bool -> Gen (SigInput, SecKeyI)+arbitraryAnyInput net pkh = do+    (k, p) <- arbitraryKeyPair+    let out | pkh = PayPKHash $ getAddrHash160 $ pubKeyAddr p+            | otherwise = PayPK p+    (val, op, sh) <- arbitraryInputStuff net+    return (SigInput out val op sh Nothing, k)++-- | Arbitrary value, out point and sighash for an input.+arbitraryInputStuff :: Network -> Gen (Word64, OutPoint, SigHash)+arbitraryInputStuff net = do+    val <- getTestCoin <$> arbitrarySatoshi net+    op <- arbitraryOutPoint+    sh <- arbitraryValidSigHash net+    return (val, op, sh)++-- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig'.+arbitraryMSSigInput :: Network -> Gen (SigInput, [SecKeyI])+arbitraryMSSigInput net = do+    (m, n) <- arbitraryMSParam+    ks <- vectorOf n arbitraryKeyPair+    let out = PayMulSig (map snd ks) m+    (val, op, sh) <- arbitraryInputStuff net+    perm <- choose (0, n - 1)+    let ksPerm = map fst $ take m $ permutations ks !! perm+    return (SigInput out val op sh Nothing, ksPerm)++-- | Arbitrary 'SigInput' with 'ScriptOutput' of type 'PaySH' and a+-- 'RedeemScript'.+arbitrarySHSigInput :: Network -> Gen (SigInput, [SecKeyI])+arbitrarySHSigInput net = do+    (SigInput rdm val op sh _, ks) <- oneof+        [ wrapKey <$> arbitraryPKSigInput net+        , wrapKey <$> arbitraryPKHashSigInput net+        , arbitraryMSSigInput net+        ]+    let out = PayScriptHash $ getAddrHash160 $ payToScriptAddress rdm+    return (SigInput out val op sh $ Just rdm, ks)++arbitraryWPKHSigInput :: Network -> Gen (SigInput, SecKeyI)+arbitraryWPKHSigInput net = do+    (k, p) <- arbitraryKeyPair+    (val, op, sh) <- arbitraryInputStuff net+    let out = PayWitnessPKHash . getAddrHash160 $ pubKeyAddr p+    return (SigInput out val op sh Nothing, k)++arbitraryWSHSigInput :: Network -> Gen (SigInput, [SecKeyI])+arbitraryWSHSigInput net = do+    (SigInput rdm val op sh _, ks) <- oneof+        [ wrapKey <$> arbitraryPKSigInput net+        , wrapKey <$> arbitraryPKHashSigInput net+        , arbitraryMSSigInput net+        ]+    let out = PayWitnessScriptHash . getAddrHash256 $ payToWitnessScriptAddress rdm+    return (SigInput out val op sh $ Just rdm, ks)++-- | Arbitrary 'Tx' (empty 'TxIn'), 'SigInputs' and private keys that can be+-- passed to 'signTx' or 'detSignTx' to fully sign the 'Tx'.+arbitrarySigningData :: Network -> Gen (Tx, [SigInput], [SecKeyI])+arbitrarySigningData net = do+    v <- arbitrary+    ni <- choose (1, 5)+    no <- choose (1, 5)+    sigis <- vectorOf ni (arbitrarySigInput net)+    let uSigis = nubBy (\(a, _) (b, _) -> sigInputOP a == sigInputOP b) sigis+    inps <- forM uSigis $ \(s, _) -> TxIn (sigInputOP s) BS.empty <$> arbitrary+    outs <- vectorOf no (arbitraryTxOut net)+    l <- arbitrary+    perm <- choose (0, length inps - 1)+    let tx = Tx v (permutations inps !! perm) outs [] l+        keys = concatMap snd uSigis+    return (tx, map fst uSigis, keys)++-- | Arbitrary transaction with empty inputs.+arbitraryEmptyTx :: Network -> Gen Tx+arbitraryEmptyTx net = do+    v    <- arbitrary+    no   <- choose (1,5)+    ni   <- choose (1,5)+    outs <- vectorOf no (arbitraryTxOut net)+    ops  <- vectorOf ni arbitraryOutPoint+    t    <- arbitrary+    s    <- arbitrary+    return $ Tx v (map (\op -> TxIn op BS.empty s) (nub ops)) outs [] t++-- | Arbitrary partially-signed transactions.+arbitraryPartialTxs ::+       Network -> Gen ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)])+arbitraryPartialTxs net = do+    tx <- arbitraryEmptyTx net+    res <-+        forM (map prevOutput $ txIn tx) $ \op -> do+            (so, val, rdmM, prvs, m, n) <- arbitraryData+            txs <- mapM (singleSig so val rdmM tx op . secKeyData) prvs+            return (txs, (so, val, op, m, n))+    return (concatMap fst res, map snd res)+  where+    singleSig so val rdmM tx op prv = do+        sh <- arbitraryValidSigHash net+        let sigi = SigInput so val op sh rdmM+        return . fromRight (error "Could not decode transaction") $+            signTx net tx [sigi] [prv]+    arbitraryData = do+        (m, n) <- arbitraryMSParam+        val <- getTestCoin <$> arbitrarySatoshi net+        nPrv <- choose (m, n)+        keys <- vectorOf n arbitraryKeyPair+        perm <- choose (0, length keys - 1)+        let pubKeys = map snd keys+            prvKeys = take nPrv $ permutations (map fst keys) !! perm+        let so = PayMulSig pubKeys m+        elements+            [ (so, val, Nothing, prvKeys, m, n)+            , ( PayScriptHash $ getAddrHash160 $ payToScriptAddress so+              , val+              , Just so+              , prvKeys+              , m+              , n)+            ]++wrapKey :: (SigInput, SecKeyI) -> (SigInput, [SecKeyI])+wrapKey (s, k) = (s, [k])
+ test/Haskoin/Test/Util.hs view
@@ -0,0 +1,40 @@+{-|+Module      : Haskoin.Test.Util+Copyright   : No rights reserved+License     : UNLICENSE+Maintainer  : jprupp@protonmail.ch+Stability   : experimental+Portability : POSIX+-}+module Haskoin.Test.Util where++import           Data.ByteString       (ByteString, pack)+import           Data.Time.Clock       (UTCTime (..))+import           Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import           Data.Word             (Word32)+import           Test.QuickCheck++-- | Arbitrary strict 'ByteString'.+arbitraryBS :: Gen ByteString+arbitraryBS = pack <$> arbitrary++-- | Arbitrary non-empty strict ByteString+arbitraryBS1 :: Gen ByteString+arbitraryBS1 = pack <$> listOf1 arbitrary++-- | Arbitrary strict ByteString of a given length+arbitraryBSn :: Int -> Gen ByteString+arbitraryBSn n = pack <$> vectorOf n arbitrary++-- | Arbitrary UTCTime that generates dates after 01 Jan 1970 01:00:00 CET+arbitraryUTCTime :: Gen UTCTime+arbitraryUTCTime = do+    w <- arbitrary :: Gen Word32+    return $ posixSecondsToUTCTime $ realToFrac w++-- | Generate a Maybe from a Gen a+arbitraryMaybe :: Gen a -> Gen (Maybe a)+arbitraryMaybe g = frequency [ (1, return Nothing)+                             , (5, Just <$> g)+                             ]+
+ test/Haskoin/Transaction/PartialSpec.hs view
@@ -0,0 +1,335 @@+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.Transaction.PartialSpec (spec) where++import           Control.Monad.Fail          (MonadFail)+import           Data.ByteString             (ByteString)+import           Data.Either                 (fromRight, isLeft, isRight)+import           Data.HashMap.Strict         (fromList, singleton)+import           Data.Maybe                  (fromJust, isJust)+import           Data.Serialize              as S+import           Data.Text                   (Text)+import           Test.Hspec+import           Test.HUnit                  (Assertion, assertBool,+                                              assertEqual)+import           Test.QuickCheck++import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Crypto+import           Haskoin.Keys+import           Haskoin.Script+import           Haskoin.Test+import           Haskoin.Transaction+import           Haskoin.Transaction.Partial+import           Haskoin.Util++spec :: Spec+spec = describe "partially signed bitcoin transaction unit tests" $ do+    it "encodes trivial psbt" $+        encodeHex (S.encode trivialPSBT) == trivialPSBTHex+    it "decodes trivial psbt" $+        decodeHexPSBT trivialPSBTHex == Right trivialPSBT+    it "encodes and decodes non-empty transactions" $+        S.decode (S.encode nonEmptyTransactionPSBT) == Right nonEmptyTransactionPSBT+    it "does not decode invalid bip vectors" $+        mapM_ invalidVecTest invalidVec+    it "encodes valid bip vecs" $+        mapM_ (uncurry encodeVecTest) validEncodeVec+    it "decodes valid bip vecs" $+        mapM_ (uncurry decodeVecTest) $ zip [1..] validVec+    it "decodes vector 2" vec2Test+    it "decodes vector 3" vec3Test+    it "decodes vector 4" vec4Test+    it "decodes vector 5" vec5Test+    it "decodes vector 6" vec6Test+    it "signed and finalized p2pkh PSBTs verify" $ property $+        forAll arbitraryKeyPair $ verifyNonWitnessPSBT btc . unfinalizedPkhPSBT btc+    it "signed and finalized multisig PSBTs verify" $ property $+        forAll arbitraryMultiSig $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc++vec2Test :: Assertion+vec2Test = do+    psbt <- decodeHexPSBTM "Cannot parse validVec2" validVec2Hex+    assertEqual "2 inputs" 2 (length $ inputs psbt)+    assertEqual "2 outputs" 2 (length $ outputs psbt)+    assertBool "final script sig" $ isJust (finalScriptSig . head $ inputs psbt)++    let rdmScript = fromJust . inputRedeemScript $ inputs psbt !! 1+    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True++    let scrptPubKey = witnessScriptPubKey $ inputs psbt !! 1+        rdmScriptP2SH = toP2SH rdmScript+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH++    mapM_ (assertEqual "outputs are empty" emptyOutput) (outputs psbt)++vec3Test :: Assertion+vec3Test = do+    psbt <- decodeHexPSBTM "Cannot parse validVec3" validVec3Hex+    assertEqual "1 input" 1 (length $ inputs psbt)+    assertEqual "2 outputs" 2 (length $ outputs psbt)+    let txInput = head . txIn $ unsignedTransaction psbt+        firstInput = head $ inputs psbt+        Just utx = nonWitnessUtxo firstInput+        OutPoint prevHash prevVOut = prevOutput txInput+    assertEqual "txids of inputs match" prevHash (txHash utx)+    let prevOutputKey = fromRight (error "Could not decode key")+                      . decodeOutputBS . scriptOutput $ txOut utx !! fromIntegral prevVOut+    assertBool "p2pkh" $ isPayPKHash prevOutputKey+    assertEqual "sighash type" sigHashAll (fromJust $ sigHashType firstInput)++vec4Test :: Assertion+vec4Test = do+    psbt <- decodeHexPSBTM "Cannot parse validVec4" validVec4Hex+    assertEqual "2 inputs" 2 (length $ inputs psbt)+    assertEqual "2 outputs" 2 (length $ outputs psbt)+    let firstInput = head $ inputs psbt+        secondInput = inputs psbt !! 1+    assertEqual "first input not final script sig" Nothing (finalScriptSig firstInput)+    assertEqual "second input not final script sig" Nothing (finalScriptSig secondInput)++    let rdmScript = fromJust $ inputRedeemScript secondInput+    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True++    let scrptPubKey = witnessScriptPubKey secondInput+        rdmScriptP2SH = toP2SH rdmScript+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH++    assertBool "all non-empty outputs" $ emptyOutput `notElem` outputs psbt++vec5Test :: Assertion+vec5Test = do+    psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex+    assertEqual "1 input" 1 (length $ inputs psbt)+    assertEqual "1 output" 1 (length $ outputs psbt)+    let input = head $ inputs psbt+    assertEqual "input not final script sig" Nothing (finalScriptSig input)++    let rdmScript = fromJust $ inputRedeemScript input+    assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True++    let scrptPubKey = witnessScriptPubKey input+        rdmScriptP2SH = toP2SH rdmScript+    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey+    assertEqual "expected redeem script"  expectedOut2 rdmScriptP2SH+  where+    expectedOut2 = fromRight (error "could not decode expected output")+                . decodeOutputBS . fromJust $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"++vec6Test :: Assertion+vec6Test = do+    psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex+    assertEqual "1 input" 1 (length $ inputs psbt)+    assertEqual "1 output" 1 (length $ outputs psbt)++    let tx = unsignedTransaction psbt+    assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)++    assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)+  where+    expectedUnknowns = UnknownMap $ singleton+        (Key 0x0f (fromJust $ decodeHex "010203040506070809"))+        (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")++expectedOut :: ScriptOutput+expectedOut = fromRight (error "could not decode expected output")+            . decodeOutputBS . fromJust $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"++witnessScriptPubKey :: Input -> ScriptOutput+witnessScriptPubKey = fromRight (error "could not decode witness utxo")+                    . decodeOutputBS . scriptOutput . fromJust . witnessUtxo++decodeHexPSBT :: Text -> Either String PartiallySignedTransaction+decodeHexPSBT = S.decode . fromJust . decodeHex++decodeHexPSBTM :: (Monad m, MonadFail m) => String -> Text -> m PartiallySignedTransaction+decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT++hexScript :: Text -> ByteString+hexScript =+    either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex+  where+    encodeScript :: Script -> ByteString+    encodeScript = S.encode++invalidVecTest :: Text -> Assertion+invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT++decodeVecTest :: Int -> Text -> Assertion+decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT++encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion+encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)++trivialPSBT :: PartiallySignedTransaction+trivialPSBT = PartiallySignedTransaction+    { unsignedTransaction = Tx { txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0 }+    , globalUnknown = UnknownMap mempty+    , inputs = []+    , outputs = []+    }++trivialPSBTHex :: Text+trivialPSBTHex = "70736274ff01000a0200000000000000000000"++nonEmptyTransactionPSBT :: PartiallySignedTransaction+nonEmptyTransactionPSBT = emptyPSBT testTx1++verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool+verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData+  where+    sigData = inputSigData =<< zip (inputs psbt) (txIn $ unsignedTransaction psbt)+    decodeOutScript = fromRight (error "Could not parse output script") . decodeOutputBS+    inputSigData (input, txInput) =+        map (\(TxOut val script) -> (decodeOutScript script, val, prevOutput txInput))+            (txOut . fromJust $ nonWitnessUtxo input)++unfinalizedPkhPSBT :: Network -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction+unfinalizedPkhPSBT net (prvKey, pubKey) = (emptyPSBT currTx)+    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig } ] }+  where+    currTx = unfinalizedTx (txHash prevTx)+    prevTx = testUtxo [prevOut]+    prevOutScript = addressToScript (pubKeyAddr pubKey)+    prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }+    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll+    sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll++arbitraryMultiSig :: Gen ([(SecKeyI, PubKeyI)], Int)+arbitraryMultiSig = do+    (m, n) <- arbitraryMSParam+    keys <- vectorOf n arbitraryKeyPair+    return (keys, m)++unfinalizedMsPSBT :: Network -> ([(SecKeyI, PubKeyI)], Int) -> PartiallySignedTransaction+unfinalizedMsPSBT net (keys, m) = (emptyPSBT currTx)+    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = sigs+                            , inputRedeemScript = Just prevOutScript+                            } ] }+  where+    currTx = unfinalizedTx (txHash prevTx)+    prevTx = testUtxo [prevOut]+    prevOutScript = encodeOutput $ PayMulSig (map snd keys) m+    prevOut = TxOut { outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript) }+    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll+    sigs = fromList $ map sig keys+    sig (prvKey, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)++unfinalizedTx :: TxHash -> Tx+unfinalizedTx prevHash = Tx+        { txVersion = 2+        , txIn = [ TxIn+            { prevOutput = OutPoint prevHash 0+            , scriptInput = ""+            , txInSequence = 4294967294+            } ]+        , txOut =+            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }+            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }+            ]+        , txWitness = []+        , txLockTime = 1257139+        }++invalidVec :: [Text]+invalidVec =+    [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300"+    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"+    , "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"+    , "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"+    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000"+    , "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"+    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"+    ]+++validEncodeVec :: [(PartiallySignedTransaction, Text)]+validEncodeVec = [(validVec1, validVec1Hex)]++testTx1 :: Tx+testTx1 = Tx+        { txVersion = 2+        , txIn = [ TxIn+            { prevOutput = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0+            , scriptInput = ""+            , txInSequence = 4294967294+            } ]+        , txOut =+            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }+            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }+            ]+        , txWitness = []+        , txLockTime = 1257139+        }++testUtxo :: [TxOut] -> Tx+testUtxo prevOuts = Tx+        { txVersion = 1+        , txIn =+            [ TxIn+                { prevOutput = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1+                , scriptInput = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985"+                , txInSequence = 4294967295+                }+            , TxIn+                { prevOutput = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1+                , scriptInput = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353"+                , txInSequence = 4294967295+                }+            ]+        , txOut = prevOuts+        , txWitness =+            [+              [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01"+              , fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"+              ]+            , [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01"+              , fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"+              ]+            ]+        , txLockTime = 0+        }++testUtxo1 :: Tx+testUtxo1 = testUtxo+    [ TxOut { outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac" }+    , TxOut { outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587" }+    ]++validVec1 :: PartiallySignedTransaction+validVec1 = (emptyPSBT testTx1) { inputs = [ emptyInput { nonWitnessUtxo = Just testUtxo1 } ] }++validVec :: [Text]+validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]++validVec1Hex :: Text+validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"++validVec2Hex :: Text+validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"++validVec3Hex :: Text+validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"++validVec4Hex :: Text+validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"++validVec5Hex :: Text+validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"++validVec6Hex :: Text+validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"
+ test/Haskoin/TransactionSpec.hs view
@@ -0,0 +1,396 @@+{-# LANGUAGE OverloadedStrings #-}++module Haskoin.TransactionSpec (spec) where++import           Data.Aeson                 as A+import qualified Data.ByteString            as B+import           Data.Either+import           Data.Map.Strict            (singleton)+import           Data.Maybe+import           Data.Serialize             as S+import           Data.String                (fromString)+import           Data.String.Conversions+import           Data.Text                  (Text)+import           Data.Word                  (Word32, Word64)+import           Haskoin.Address+import           Haskoin.Constants+import           Haskoin.Keys+import           Haskoin.Script+import           Haskoin.Test+import           Haskoin.Transaction+import           Haskoin.Transaction.Segwit (isSegwit)+import           Haskoin.Util+import           Safe                       (readMay)+import           Test.Hspec+import           Test.HUnit                 (Assertion, assertBool)+import           Test.QuickCheck++spec :: Spec+spec = do+    let net = btc+    describe "transaction unit tests" $ do+        it "compute txid from tx" $+            mapM_ runTxIDVec txIDVec+        it "build pkhash transaction (generated from bitcoind)" $+            mapM_ runPKHashVec pkHashVec+        it "encode satoshi core script pubkey" tEncodeSatoshiCoreScriptPubKey+        --+        -- These tests depend on signatures matching exactly those in the spec.+        -- Fedora includes a version of libsecp256k1 that computes signatures+        -- using a slighlty different deterministic nonce algorithm.+        --+        -- it "agrees with BIP143 p2wpkh example" testBip143p2wpkh+        -- it "agrees with BIP143 p2sh-p2wpkh example" testBip143p2shp2wpkh+        -- it "builds a p2wsh multisig transaction" testP2WSHMulsig+        -- it "agrees with BIP143 p2sh-p2wsh multisig example" testBip143p2shp2wpkhMulsig+    describe "btc transaction" $ do+        it "decode and encode txid" $+            property $+            forAll arbitraryTxHash $ \h -> hexToTxHash (txHashToHex h) == Just h+        it "from string transaction id" $+            property $+            forAll arbitraryTxHash $ \h -> fromString (cs $ txHashToHex h) == h+        it "building address tx" $+            property $+            forAll arbitraryAddress $+            forAll (arbitrarySatoshi net) . testBuildAddrTx net+        it "guess transaction size" $+            property $ forAll (arbitraryAddrOnlyTxFull net) (testGuessSize net)+        it "choose coins" $+            property $ forAll (listOf (arbitrarySatoshi net)) testChooseCoins+        it "choose multisig coins" $+            property $+            forAll arbitraryMSParam $+            forAll (listOf (arbitrarySatoshi net)) . testChooseMSCoins+        it "sign and validate transaction" $+            property $ forAll (arbitrarySigningData net) (testDetSignTx net)+        it "sign and validate (nested) transaction" $+            property $ forAll (arbitrarySigningData net) (testDetSignNestedTx net)+        it "merge partially signed transactions" $+            property $ forAll (arbitraryPartialTxs net) (testMergeTx net)+    describe "json serialization" $ do+        it "encodes and decodes transaction" $+            property $ forAll (arbitraryTx net) testID+        it "encodes and decodes transaction hash" $+            property $ forAll arbitraryTxHash testID+    describe "transaction serialization" $ do+        it "encodes and decodes tx input" $+            property $ forAll (arbitraryTxIn net) cerealID+        it "encodes and decodes tx output" $+            property $ forAll (arbitraryTxOut net) cerealID+        it "encodes and decodes outpoint" $+            property $ forAll arbitraryOutPoint cerealID+        it "encodes and decodes transaction" $+            property $ forAll (arbitraryTx net) cerealID+        it "encodes and decodes witness transaction" $+            property $ forAll (arbitraryWitnessTx net) cerealID+        it "encodes and decodes legacy transaction" $+            property $ forAll (arbitraryLegacyTx net) cerealID++cerealID :: (Serialize a, Eq a) => a -> Bool+cerealID x = S.decode (S.encode x) == Right x++runTxIDVec :: (Text, Text) -> Assertion+runTxIDVec (tid, tx) = assertBool "txid" $ txHashToHex (txHash txBS) == tid+  where+    txBS = fromJust $ either (const Nothing) return . S.decode =<< decodeHex tx++txIDVec :: [(Text, Text)]+txIDVec =+    [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"+      , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+      )+    , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"+      , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"+      )+    , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"+      , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"+      )+    , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"+      , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"+      )+    ]++runPKHashVec :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion+runPKHashVec (xs, ys, res) =+    assertBool "Build PKHash Tx" $ encodeHex (S.encode tx) == res+  where+    tx =+        fromRight (error "Could not decode transaction") $+        buildAddrTx btc (map f xs) ys+    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix++-- These test vectors have been generated from bitcoind raw transaction api++pkHashVec :: [([(Text, Word32)], [(Text, Word64)], Text)]+pkHashVec =+    [+      ( [("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",14)]+      , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",90000000)]+      , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"+      )+    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)+        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)+        ]+      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)+        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)+        ]+      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"+      )+    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)+        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)+        ]+      , []+      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0000000000"+      )+    , ( []+      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)+        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)+        ]+      , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"+      )+    ]++tEncodeSatoshiCoreScriptPubKey :: Assertion+tEncodeSatoshiCoreScriptPubKey = assertBool "tEncodeSatoshiCoreScriptPubKey" $+  t1BsOutputScriptPubKey == encodeSatoshiCoreScriptPubKey t1SatoshiCoreJsonScriptPubKey+  where+    t1BsOutputScriptPubKey :: Text+    t1BsOutputScriptPubKey = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"+    t1SatoshiCoreJsonScriptPubKey :: String+    t1SatoshiCoreJsonScriptPubKey = "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"++encodeSatoshiCoreScriptPubKey :: String -> Text+encodeSatoshiCoreScriptPubKey =+  mconcat . map encodeSatoshiCoreScriptPiece . words+  where+    encodeSatoshiCoreScriptPiece :: String -> Text+    encodeSatoshiCoreScriptPiece s = case (readMay ("OP_" ++ s) :: Maybe ScriptOp) of+      Just op -> encodeHex . S.encode $ op+      Nothing -> case take 2 s of+          "OP" -> encodeHex . S.encode . (read :: String -> ScriptOp) $ s+          "0x" -> (fromString . drop 2 :: String -> Text) s+          _ -> case (readMay s :: Maybe Int) of -- can we get rid of this case now?+            Just i  -> encodeHex . S.encode . intToScriptOp $ i+            Nothing -> error $ "encodeSatoshiCoreScriptPubKey: " ++ s++--+-- Following tests depend on specific secp256k1 deterministic nonce computation.+-- Fedora distributes the Bitcoin ABC fork of this library, which computes nonces differently.+--++-- -- Reproduce the P2WPKH example from BIP 143+-- testBip143p2wpkh :: Assertion+-- testBip143p2wpkh = assertEqual "BIP143 p2wpkh" (Right exampleSignedTx) generatedSignedTx+--   where+--     exampleSignedTx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"++--     unsignedTx = "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"++--     Just key0 = secHexKey "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"+--     pubKey0   = toPubKey key0++--     Just key1 = secHexKey "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"++--     [op0, op1] = prevOutput <$> txIn unsignedTx++--     sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing++--     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1+--     sigIn1                 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing++--     generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]++-- -- Reproduce the P2SH-P2WPKH example from BIP 143+-- testBip143p2shp2wpkh :: Assertion+-- testBip143p2shp2wpkh = assertEqual "BIP143 p2sh-p2wpkh" (Right exampleSignedTx) generatedSignedTx+--   where+--     exampleSignedTx = "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"++--     unsignedTx = "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"++--     Just key0 = secHexKey "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"++--     op0                    = prevOutput . head $ txIn unsignedTx+--     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key0+--     sigIn0                 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing++--     generatedSignedTx = signNestedWitnessTx btc unsignedTx [sigIn0] [key0]++-- -- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)+-- testP2WSHMulsig :: Assertion+-- testP2WSHMulsig = assertEqual "p2wsh multisig" (Right exampleSignedTx) generatedSignedTx+--   where+--     exampleSignedTx = "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100fad4fedd2bb4c439c64637eb8e9150d9020a7212808b8dc0578d5ff5b4ad65fe0220714640f261b37eb3106310bf853f4b706e51436fb6b64c2ab00768814eb55b9801473044022100baff4e4ceea4022b9725a2e6f6d77997a554f858165b91ac8c16c9833008bee9021f5f70ebc3f8580dc0a5e96451e3697bdf1f1f5883944f0f33ab0cfb272354040169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea811549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f69ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695e2d8e007a7f26db96c2ee42db15dc953ae00000000"++--     unsignedTx = "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac00000000"++--     op0 = head $ prevOutput <$> txIn unsignedTx++--     Just keys = traverse secHexKey [ "3030303030303030303030303030303030303030303030303030303030303031"+--                                    , "3030303030303030303030303030303030303030303030303030303030303032"+--                                    , "3030303030303030303030303030303030303030303030303030303030303033"+--                                    ]++--     rdm               = PayMulSig (toPubKey <$> keys) 2+--     sigIn             = SigInput (toP2WSH $ encodeOutput rdm) 100000000 op0 sigHashAll (Just rdm)+--     generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)++-- -- Reproduce the P2SH-P2WSH multisig example from BIP 143+-- testBip143p2shp2wpkhMulsig :: Assertion+-- testBip143p2shp2wpkhMulsig =+--     assertEqual "BIP143 p2sh-p2wsh multisig" (Right exampleSignedTx) generatedSignedTx+--   where+--     exampleSignedTx = "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"++--     unsignedTx = "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000"++--     op0 = head $ prevOutput <$> txIn unsignedTx++--     rawKeys = [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6"+--               , "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3"+--               , "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661"+--               , "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49"+--               , "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323"+--               , "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"+--               ]++--     Just keys@[key0, key1, key2, key3, key4, key5] = traverse secHexKey rawKeys++--     rdm      = PayMulSig (toPubKey <$> keys) 6+--     sigIn sh = SigInput (toP2WSH $ encodeOutput rdm) 987654321 op0 sh (Just rdm)++--     sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]+--     sigHashesB = setAnyoneCanPayFlag <$> sigHashesA+--     sigIns     = sigIn <$> (sigHashesA <> sigHashesB)++--     generatedSignedTx      = foldM addSig unsignedTx $ zip sigIns keys+--     addSig tx (sigIn, key) = signNestedWitnessTx btc tx [sigIn] [key]++-- secHexKey :: Text -> Maybe SecKey+-- secHexKey = decodeHex >=> secKey++-- toPubKey :: SecKey -> PubKeyI+-- toPubKey = derivePubKeyI . wrapSecKey True++{- Building Transactions -}++testBuildAddrTx :: Network -> Address -> TestCoin -> Bool+testBuildAddrTx net a (TestCoin v)+    | isPubKeyAddress a = Right (PayPKHash (getAddrHash160 a)) == out+    | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out+    | otherwise = undefined+  where+    tx = buildAddrTx net [] [(fromJust (addrToString net a), v)]+    out =+        decodeOutputBS $+        scriptOutput $+        head $ txOut (fromRight (error "Could not build transaction") tx)++testGuessSize :: Network -> Tx -> Bool+testGuessSize net tx+    -- We compute an upper bound but it should be close enough to the real size+    -- We give 2 bytes of slack on every signature (1 on r and 1 on s)+ = guess >= len && guess <= len + 2 * delta+  where+    delta = pki + sum (map fst msi)+    guess = guessTxSize pki msi pkout msout+    len = B.length $ S.encode tx+    ins = map f $ txIn tx+    f i =+        fromRight (error "Could not decode input") $+        decodeInputBS net $ scriptInput i+    pki = length $ filter isSpendPKHash ins+    msi = concatMap shData ins+    shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]+    shData _                                      = []+    out =+        map+            (fromRight (error "Could not decode transaction output") .+             decodeOutputBS . scriptOutput) $+        txOut tx+    pkout = length $ filter isPayPKHash out+    msout = length $ filter isPayScriptHash out++testChooseCoins :: [TestCoin] -> Word64 -> Word64 -> Int -> Property+testChooseCoins coins target byteFee nOut = nOut >= 0 ==>+    case chooseCoins target byteFee nOut True coins of+        Right (chosen, change) ->+            let outSum = sum $ map coinValue chosen+                fee    = guessTxFee byteFee nOut (length chosen)+            in outSum == target + change + fee+        Left _ ->+            let fee = guessTxFee byteFee nOut (length coins)+            in target == 0 || s < target + fee+  where+    s  = sum $ map coinValue coins++testChooseMSCoins :: (Int, Int) -> [TestCoin]+                  -> Word64 -> Word64 -> Int -> Property+testChooseMSCoins (m, n) coins target byteFee nOut = nOut >= 0 ==>+    case chooseMSCoins target byteFee (m,n) nOut True coins of+        Right (chosen,change) ->+            let outSum = sum $ map coinValue chosen+                fee    = guessMSTxFee byteFee (m,n) nOut (length chosen)+            in outSum == target + change + fee+        Left _ ->+            let fee = guessMSTxFee byteFee (m,n) nOut (length coins)+            in target == 0 || s < target + fee+  where+    s = sum $ map coinValue coins++{- Signing Transactions -}++testDetSignTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool+testDetSignTx net  (tx, sigis, prv) =+    not (verifyStdTx net tx verData) &&+    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData+  where+    txSigP =+        fromRight (error "Could not decode transaction") $+        signTx net tx sigis (map secKeyData (tail prv))+    txSigC =+        fromRight (error "Could not decode transaction") $+        signTx net txSigP sigis [secKeyData (head prv)]+    verData = map (\(SigInput s v o _ _) -> (s, v, o)) sigis++testDetSignNestedTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool+testDetSignNestedTx net  (tx, sigis, prv) =+    not (verifyStdTx net tx verData) &&+    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData+  where+    txSigP =+        fromRight (error "Could not decode transaction") $+        signNestedWitnessTx net tx sigis (secKeyData <$> tail prv)+    txSigC =+        fromRight (error "Could not decode transaction") $+        signNestedWitnessTx net txSigP sigis [secKeyData (head prv)]+    verData = handleSegwit <$> sigis+    handleSegwit (SigInput s v o _ _)+        | isSegwit s = (toP2SH $ encodeOutput s, v, o)+        | otherwise  = (s, v, o)++testMergeTx :: Network -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool+testMergeTx net (txs, os) = and+    [ isRight mergeRes+    , length (txIn mergedTx) == length os+    , if enoughSigs then isValid else not isValid+    -- Signature count == min (length txs) (sum required signatures)+    , sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))+    ]+  where+    outs = map (\(so, val, op, _, _) -> (so, val, op)) os+    mergeRes = mergeTxs net txs outs+    mergedTx = fromRight (error "Could not merge") mergeRes+    isValid = verifyStdTx net mergedTx outs+    enoughSigs = all (\(m,c) -> c >= m) sigMap+    sigMap = map (\((_,_,_,m,_), inp) -> (m, sigCnt inp)) $ zip os $ txIn mergedTx+    sigCnt inp = case decodeInputBS net $ scriptInput inp of+        Right (RegularInput (SpendMulSig sigs)) -> length sigs+        Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs+        _ -> error "Invalid input script type"++testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool+testID x =+    (A.decode . A.encode) (singleton ("object" :: String) x) ==+    Just (singleton ("object" :: String) x)
+ test/Haskoin/UtilSpec.hs view
@@ -0,0 +1,64 @@+module Haskoin.UtilSpec (spec) where++import qualified Data.ByteString as BS+import           Data.Either     (fromLeft, fromRight, isLeft, isRight)+import           Data.Foldable   (toList)+import           Data.List       (permutations)+import           Data.Maybe+import qualified Data.Sequence   as Seq+import           Haskoin.Test+import           Haskoin.Util+import           Test.Hspec+import           Test.QuickCheck++spec :: Spec+spec =+    describe "utility functions" $ do+        it "bsToInteger . integerToBS" $ property getPutInteger+        it "decodeHex . encodeHex" $ property $ forAll arbitraryBS fromToHex+        it "compare updateIndex with Data.Sequence" $ property testUpdateIndex+        it "matchTemplate" $ property testMatchTemplate+        it "testing matchTemplate with two lists" $+            property testMatchTemplateLen+        it "either helper functions" $ property testEither++{- Various utilities -}++getPutInteger :: Integer -> Bool+getPutInteger i = bsToInteger (integerToBS $ abs i) == abs i++fromToHex :: BS.ByteString -> Bool+fromToHex bs = decodeHex (encodeHex bs) == Just bs++testUpdateIndex :: [Int] -> Int -> Int -> Bool+testUpdateIndex xs v i =+    updateIndex i xs (const v) == toList (Seq.update i v $ Seq.fromList xs)++testMatchTemplate :: [Int] -> Int -> Bool+testMatchTemplate as i = catMaybes res == bs+  where+    res = matchTemplate as bs (==)+    idx =+        if null as+            then 0+            else i `mod` length as+    bs = permutations as !! idx++testMatchTemplateLen :: [Int] -> [Int] -> Bool+testMatchTemplateLen as bs = length bs == length res+  where+    res = matchTemplate as bs (==)++testEither :: Either String Int -> Bool+testEither e =+    case e of+        (Right v) ->+            isRight e &&+            not (isLeft e) &&+            fromRight (error "Unexpected Left") e == v &&+            eitherToMaybe e == Just v+        (Left v) ->+            isLeft e &&+            not (isRight e) &&+            fromLeft (error "Unexpected Right") e == v &&+            isNothing (eitherToMaybe e)
− test/Network/Haskoin/Address/Bech32Spec.hs
@@ -1,155 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Address.Bech32Spec-    ( spec-    ) where--import           Control.Monad-import           Data.Bits                      (xor)-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as B-import           Data.Char                      (chr, ord, toLower)-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text                      (Text, append, pack, snoc,-                                                 uncons)-import qualified Data.Text                      as T-import           Data.Word                      (Word8)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.Bech32-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit--spec = do-    describe "bech32 checksum" $ do-        it "should have valid checksum" $ forM_ validChecksums testValidChecksum-        it "should have invalid checksum" $-            forM_ invalidChecksums testInvalidChecksum-        it "should be a valid address" $ forM_ validAddresses testValidAddress-        it "should be an invalid address" $-            forM_ invalidAddresses testInvalidAddress-        it "should be same for the input in different case" $-            all (== Just "test12hrzfj") . map (flip bech32Encode []) $ hrpCaseVariants-    describe "more encoding/decoding cases" $ do-        it "length > 90" $-            assert $-            isNothing $-            bech32Encode "bc" (replicate 82 (word5 (1 :: Word8)))-        it "segwit version bounds" $-            assert $ isNothing $ segwitEncode "bc" 17 []-        it "segwit prog len version 0" $-            assert $ isNothing $ segwitEncode "bc" 0 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isJust $ segwitEncode "bc" 1 (replicate 30 1)-        it "segwit prog len version != 0" $-            assert $ isNothing $ segwitEncode "bc" 1 (replicate 41 1)-        it "empty HRP encode" $ assert $ isNothing $ bech32Encode "" []-        it "empty HRP encode" $-            assert $ isNothing $ bech32Decode "10a06t8"-        it "hrp lowercased" $-            bech32Encode "HRP" [] `shouldBe` bech32Encode "hrp" []---testValidChecksum :: Bech32 -> Assertion-testValidChecksum checksum = case bech32Decode checksum of-    Nothing                      -> assertFailure (show checksum)-    Just (resultHRP, resultData) -> do-        -- test that a corrupted checksum fails decoding.-        let (hrp, rest)         = T.breakOnEnd "1" checksum-            Just (first, rest') = uncons rest-            checksumCorrupted   = (hrp `snoc` chr (ord first `xor` 1)) `append` rest'-        assertBool (show checksum ++ " corrupted")-            $ isNothing (bech32Decode checksumCorrupted)-        -- test that re-encoding the decoded checksum results in the same checksum.-        let checksumEncoded  = bech32Encode resultHRP resultData-            expectedChecksum = Just $ T.toLower checksum-        assertEqual (show checksum ++ " re-encode")-                    expectedChecksum-                    checksumEncoded--testInvalidChecksum :: Bech32 -> Assertion-testInvalidChecksum checksum =-    assertBool (show checksum) (isNothing $ bech32Decode checksum)--testValidAddress :: (Text, Text) -> Assertion-testValidAddress (address, hexscript) = do-    let address' = T.toLower address-        hrp      = T.take 2 address'-    case segwitDecode hrp address of-        Nothing                -> assertFailure "decode failed"-        Just (witver, witprog) -> do-            assertEqual (show address)-                        (decodeHex hexscript)-                        (Just $ segwitScriptPubkey witver witprog)-            assertEqual (show address)-                        (Just address')-                        (segwitEncode hrp witver witprog)--testInvalidAddress :: Text -> Assertion-testInvalidAddress address = do-    assertBool (show address) (isNothing $ segwitDecode "bc" address)-    assertBool (show address) (isNothing $ segwitDecode "tb" address)--segwitScriptPubkey :: Word8 -> [Word8] -> ByteString-segwitScriptPubkey witver witprog =-    B.pack $ witver' : fromIntegral (length witprog) : witprog-    where witver' = if witver == 0 then 0 else witver + 0x50--validChecksums :: [Text]-validChecksums =-    [ "A12UEL5L"-    , "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"-    , "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"-    , "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"-    , "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"-    ]--invalidChecksums :: [Text]-invalidChecksums =-    [ " 1nwldj5"-    , "\DEL1axkwrx"-    , "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx"-    , "pzry9x0s0muk"-    , "1pzry9x0s0muk"-    , "x1b4n0q5v"-    , "li1dgmt3"-    , "de1lg7wt\xFF"-    ]--validAddresses :: [(Text, Text)]-validAddresses =-    [ ("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"-      ,"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262")-    , ("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"-      ,"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6")-    , ("BC1SW50QA3JX3S", "6002751e")-    , ("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323")-    , ("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"-      ,"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433")-    ]--invalidAddresses :: [Text]-invalidAddresses =-    [ "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"-    , "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"-    , "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"-    , "bc1rw5uspcuh"-    , "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"-    , "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"-    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"-    , "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"-    , "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"-    , "bc1gmk9yu"-    ]--hrpCaseVariants:: [Text]-hrpCaseVariants = map T.pack hrpTestPermutations--hrpTestPermutations :: [String]-hrpTestPermutations = do-    a <- ['t', 'T']-    b <- ['e', 'E']-    c <- ['s', 'S']-    d <- ['t', 'T']-    return [a, b, c, d]
− test/Network/Haskoin/Address/CashAddrSpec.hs
@@ -1,280 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Address.CashAddrSpec (spec) where--import           Control.Monad-import qualified Data.ByteString.Char8            as C-import           Data.Maybe-import           Data.String.Conversions-import           Data.Text                        (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.CashAddr-import           Network.Haskoin.Constants-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit--spec :: Spec-spec = do-    describe "cashaddr checksum test vectors" $ do-        it "prefix:x64nx6hz" $ do-            let mpb = cash32decode "prefix:x64nx6hz"-            mpb `shouldBe` Just ("prefix", "")-        it "p:gpf8m4h7" $ do-            let mpb = cash32decode "p:gpf8m4h7"-            mpb `shouldBe` Just ("p", "")-        it "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn" $ do-            let mpb =-                    cash32decode-                        "bitcoincash:qpzry9x8gf2tvdw0s3jn54khce6mua7lcw20ayyn"-            mpb `shouldBe`-                Just-                    ( "bitcoincash"-                    , "\NULD2\DC4\199BT\182\&5\207\132e:V\215\198u\190w\223")-        it "bchtest:testnetaddress4d6njnut" $ do-            let mpb = cash32decode "bchtest:testnetaddress4d6njnut"-            mpb `shouldBe` Just ("bchtest", "^`\185\229}kG\152")-        it "bchreg:555555555555555555555555555555555555555555555udxmlmrz" $ do-            let mpb =-                    cash32decode-                        "bchreg:555555555555555555555555555555555555555555555udxmlmrz"-            mpb `shouldBe`-                Just-                    ( "bchreg"-                    , "\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)JR\148\165)J")-    describe "cashaddr to base58 translation test vectors" $ do-        it "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"-            addr `shouldBe`-                Just "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"-        it "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"-            addr `shouldBe`-                Just "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"-        it "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"-            addr `shouldBe`-                Just "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"-        it "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"-            addr `shouldBe`-                Just "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"-        it "3LDsS579y7sruadqu11beEJoTjdFiFCdX4" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"-            addr `shouldBe`-                Just "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"-        it "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw" $ do-            let addr =-                    addrToString bch =<<-                    stringToAddr btc "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"-            addr `shouldBe`-                Just "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"-    describe "base58 to cashaddr translation test vectors" $ do-        it "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a"-            addr `shouldBe` Just "1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu"-        it "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy"-            addr `shouldBe` Just "1KXrWXciRDZUpQwQmuM1DbwsKDLYAYsVLR"-        it "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:qqq3728yw0y47sqn6l2na30mcw6zm78dzqre909m2r"-            addr `shouldBe` Just "16w1D5WRVKJuZUsSRzdLp9w3YGcgoxDXb"-        it "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:ppm2qsznhks23z7629mms6s4cwef74vcwvn0h829pq"-            addr `shouldBe` Just "3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC"-        it "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:pr95sy3j9xwd2ap32xkykttr4cvcu7as4yc93ky28e"-            addr `shouldBe` Just "3LDsS579y7sruadqu11beEJoTjdFiFCdX4"-        it "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37" $ do-            let addr =-                    addrToString btc =<<-                    stringToAddr-                        bch-                        "bitcoincash:pqq3728yw0y47sqn6l2na30mcw6zm78dzq5ucqzc37"-            addr `shouldBe` Just "31nwvkZwyPdgzjBJZXfDmSWsC4ZLKpYyUw"-    describe "cashaddr larger test vectors" $-        forM_ (zip [0 ..] vectors) $ \(i, vec) ->-            it ("cashaddr test vector " <> show (i :: Int)) $ testCashAddr vec--{- Various utilities -}--testCashAddr :: (Int, CashVersion, Cash32, Text) -> Assertion-testCashAddr (len, typ, addr, hex) = do-    let mbs = decodeHex hex-    assertBool "Could not decode hex payload from test vector" (isJust mbs)-    let mlow = cash32decode addr-    assertBool "Could not decode low level address" (isJust mlow)-    let Just (_, lbs) = mlow-    assertEqual "Low-level payload size incorrect" len (C.length lbs - 1)-    assertEqual "Low-level payload doesn't match" bs (C.tail lbs)-    let mdec = cash32decodeType addr-    assertBool ("Could not decode test address: " <> cs addr) (isJust mdec)-    assertEqual "Length doesn't match" len (C.length pay)-    assertEqual "Version doesn't match" typ ver-    assertEqual "Payload doesn't match" bs pay-  where-    Just bs = decodeHex hex-    Just (_, ver, pay) = cash32decodeType addr---- | All vectors starting with @pref@ had the wrong version in the spec--- document.-vectors :: [(Int, CashVersion, Text, Text)]-vectors =-    [ ( 20-      , 0-      , "bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 1-      , "bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 1-      , "pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 20-      , 15-      , "prefix:0r6m7j9njldwwzlg9v7v53unlr4jkmx6ey3qnjwsrf"-      , "F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9")-    , ( 24-      , 0-      , "bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 1-      , "bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 1-      , "pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 24-      , 15-      , "prefix:09adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2p29kc2lp"-      , "7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA")-    , ( 28-      , 0-      , "bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 1-      , "bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 1-      , "pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 28-      , 15-      , "prefix:0gagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkc5djw8s9g"-      , "3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B")-    , ( 32-      , 0-      , "bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 1-      , "bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 1-      , "pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 32-      , 15-      , "prefix:0vch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxqsh6jgp6w"-      , "3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060")-    , ( 40-      , 0-      , "bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 1-      , "bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 1-      , "pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 40-      , 15-      , "prefix:0nq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvwsvctzqy"-      , "C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB")-    , ( 48-      , 0-      , "bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 1-      , "bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 1-      , "pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 48-      , 15-      , "prefix:0h3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqakcssnmn"-      , "E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C")-    , ( 56-      , 0-      , "bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 1-      , "bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 1-      , "pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 56-      , 15-      , "prefix:0mvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsgjrqpnw8"-      , "D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041")-    , ( 64-      , 0-      , "bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 1-      , "bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 1-      , "pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    , ( 64-      , 15-      , "prefix:0lg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96ms92w6845"-      , "D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B")-    ]
− test/Network/Haskoin/AddressSpec.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.AddressSpec (spec) where--import           Data.Aeson-import           Data.Aeson.Types-import           Data.ByteString                (ByteString)-import qualified Data.ByteString                as BS (append, empty, pack)-import           Data.Either                    (isRight)-import           Data.Maybe                     (isJust, fromJust)-import qualified Data.Serialize                 as S-import           Data.Text                      (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Address.Base58-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys           (derivePubKeyI)-import           Network.Haskoin.Test-import           Test.Hspec-import           Test.HUnit                     (Assertion, assertBool,-                                                 assertEqual)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "btc address" . it "base58 encodings" $ mapM_ runVector vectors-    describe "btc-test address" $ props btcTest-    describe "bch address" $ props bch-    describe "bch-test address" $ props bchTest-    describe "bch-regtest address" $ props bchRegTest-    describe "json serialization" . it "encodes and decodes address" $-        forAll arbitraryAddress (testCustom (addrFromJSON net) (addrToJSON net))-    describe "witness address vectors" . it "p2sh(pwpkh)" $-        mapM_ testCompatWitness compatWitnessVectors--props :: Network -> Spec-props net = do-    it "encodes and decodes base58 bytestring" $-        property $-        forAll arbitraryBS $ \bs -> decodeBase58 (encodeBase58 bs) == Just bs-    it "encodes and decodes base58 bytestring with checksum" $-        property $-        forAll arbitraryBS $ \bs ->-            decodeBase58Check (encodeBase58Check bs) == Just bs-    it "encodes and decodes address" $-        property $-        forAll arbitraryAddress $ \a ->-            (stringToAddr net =<< addrToString net a) == Just a-    it "shows and reads address" $-        property $ forAll arbitraryAddress $ \a -> read (show a) == a--runVector :: (ByteString, Text, Text) -> Assertion-runVector (bs, e, chk) = do-    assertEqual "encodeBase58" e b58-    assertEqual "encodeBase58Check" chk b58Chk-    assertEqual "decodeBase58" (Just bs) (decodeBase58 b58)-    assertEqual "decodeBase58Check" (Just bs) (decodeBase58Check b58Chk)-  where-    b58    = encodeBase58 bs-    b58Chk = encodeBase58Check bs--vectors :: [(ByteString, Text, Text)]-vectors =-    [ ( BS.empty, "", "3QJmnh" )-    , ( BS.pack [0], "1", "1Wh4bh" )-    , ( BS.pack [0,0,0,0], "1111", "11114bdQda" )-    , ( BS.pack [0,0,1,0,0], "11LUw", "113CUwsFVuo" )-    , ( BS.pack [255], "5Q", "VrZDWwe" )-    , ( BS.pack [0,0,0,0] `BS.append` BS.pack [1..255]-      , "1111cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"-      , "111151KWPPBRzdWPr1ASeu172gVgLf1YfUp6VJyk6K9t4cLqYtFHcMa2iX8S3NJEprUcW7W5LvaPRpz7UG7puBj5STE3nKhCGt5eckYq7mMn5nT7oTTic2BAX6zDdqrmGCnkszQkzkz8e5QLGDjf7KeQgtEDm4UER6DMSdBjFQVa6cHrrJn9myVyyhUrsVnfUk2WmNFZvkWv3Tnvzo2cJ1xW62XDfUgYz1pd97eUGGPuXvDFfLsBVd1dfdUhPwxW7pMPgdWHTmg5uqKGFF6vE4xXpAqZTbTxRZjCDdTn68c2wrcxApm8hq3JX65Hix7VtcD13FF8b7BzBtwjXq1ze6NMjKgUcqpJTN9vt"-      )-    ]--testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool-testCustom f g x = parseMaybe f (g x) == Just x--compatWitnessVectors :: [(Network, Text, Text)]-compatWitnessVectors =-    [ ( btcTest-      , "cNUnpYpMsJXYCERYBciJnsWBpcYEFjdcbq6dxj4SskGhs7uHuJ7Q"-      , "2N6PDTueBHvXzW61B4oe5SW1D3v2Z3Vpbvw")-    ]--testCompatWitness :: (Network, Text, Text) -> Assertion-testCompatWitness (net, seckey, addr) = do-    let seckeyM = fromWif net seckey-    assertBool "decode seckey" (isJust seckeyM)-    let pubkey = derivePubKeyI (fromJust seckeyM)-    let addrM = addrToString btcTest (pubKeyCompatWitnessAddr pubkey)-    assertBool "address can be encoded" (isJust addrM)-    assertEqual "witness address matches" addr (fromJust addrM)
− test/Network/Haskoin/BlockSpec.hs
@@ -1,359 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.BlockSpec-    ( spec-    ) where--import           Control.Monad.State.Strict-import           Data.Aeson                    as A-import           Data.Either                   (fromRight)-import           Data.Map.Strict               (singleton)-import           Data.Maybe                    (fromJust)-import           Data.Serialize                as S-import           Data.String                   (fromString)-import           Data.String.Conversions       (cs)-import           Data.Text                     (Text)-import           Network.Haskoin.Block-import           Network.Haskoin.Block.Headers-import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Constants-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Test.Hspec-import           Test.HUnit                    hiding (State)-import           Test.QuickCheck--myTime :: Timestamp-myTime = 1499083075--withChain :: Network -> State HeaderMemory a -> a-withChain net f = evalState f (initialChain net)--chain :: BlockHeaders m => Network -> BlockHeader -> Int -> m ()-chain net bh i = do-    bnsE <- connectBlocks net myTime bhs-    either error (const $ return ()) bnsE-  where-    bhs = appendBlocks net 6 bh i--spec :: Spec-spec = do-    let net = bchRegTest-    describe "blockchain headers" $ do-        it "gets best block" $-            let bb =-                    withChain net $ do-                        chain net (getGenesisHeader net) 100-                        getBestBlockHeader-             in nodeHeight bb `shouldBe` 100-        it "builds a block locator" $-            let loc =-                    withChain net $ do-                        chain net (getGenesisHeader net) 100-                        bb <- getBestBlockHeader-                        blockLocatorNodes bb-                heights = map nodeHeight loc-             in heights `shouldBe` [100,99 .. 90] <> [88, 84, 76, 60, 28, 0]-        it "follows split chains" $-            let bb = withChain net $ splitChain net >> getBestBlockHeader-             in nodeHeight bb `shouldBe` 4035-    describe "block hash" $ do-        it "encodes and decodes block hash" $-            property $-            forAll arbitraryBlockHash $ \h ->-                hexToBlockHash (blockHashToHex h) == Just h-        it "from string block hash" $-            property $-            forAll arbitraryBlockHash $ \h ->-                fromString (cs $ blockHashToHex h) == h-        it "show and read block hash" $-            property $ forAll arbitraryBlockHash $ \h -> read (show h) == h-        it "json block hash" $ property $ forAll arbitraryBlockHash testID-    describe "merkle trees" $ do-        let net' = btc-        it "builds tree of right width at height 1" $ property testTreeWidth-        it "builds tree of right width at height 0" $ property testBaseWidth-        it "builds and extracts partial merkle tree" $-            property $-            forAll-                (listOf1 ((,) <$> arbitraryTxHash <*> arbitrary))-                (buildExtractTree net')-        it "merkle root test vectors" $-            mapM_ runMerkleVector merkleVectors-    describe "compact number" $ do-        it "compact number local vectors" testCompact-        it "compact number imported vectors" testCompactBitcoinCore-    describe "block serialization" $ do-        it "encodes and decodes block" $-            property $ forAll (arbitraryBlock net) cerealID-        it "encodes and decodes block JSON" $-            property $ forAll (arbitraryBlock net) testID-        it "encodes and decodes block header" $-            property $ forAll arbitraryBlockHeader cerealID-        it "encodes and decodes block header JSON" $-            property $ forAll arbitraryBlockHeader testID-        it "encodes and decodes getblocks" $-            property $ forAll arbitraryGetBlocks cerealID-        it "encodes and decodes getheaders" $-            property $ forAll arbitraryGetHeaders cerealID-        it "encodes and decdoes headers" $-            property $ forAll arbitraryHeaders cerealID-        it "encodes and decodes merkle block" $-            property $ forAll arbitraryMerkleBlock cerealID-    describe "helper functions" $ do-        it "computes bitcoin block subsidy correctly" (testSubsidy btc)-        it "computes regtest block subsidy correctly" (testSubsidy btcRegTest)---- 0 → → 2015 → → → → → → → 4031---       ↓---       → → 2035 → → → → → → 4035*---           ↓---           → → 2185-splitChain :: Network -> State HeaderMemory ()-splitChain net = do-    start <- go 1 (getGenesisHeader net) 2015-    e 2015 (head start)-    tail1 <- go 2 (nodeHeader $ head start) 2016-    e 4031 (head tail1)-    tail2 <- go 3 (nodeHeader $ head start) 20-    e 2035 (head tail2)-    tail3 <- go 4 (nodeHeader $ head tail2) 2000-    e 4035 (head tail3)-    tail4 <- go 5 (nodeHeader $ head tail2) 150-    e 2185 (head tail4)-    sp1 <- splitPoint (head tail1) (head tail3)-    unless (sp1 == head start) $-        error $-        "Split point wrong between blocks 4031 and 4035: " ++-        show (nodeHeight sp1)-    sp2 <- splitPoint (head tail4) (head tail3)-    unless (sp2 == head tail2) $-        error $-        "Split point wrong between blocks 2185 and 4035: " ++-        show (nodeHeight sp2)-  where-    e n bn =-        unless (nodeHeight bn == n) $-        error $-        "Node height " ++-        show (nodeHeight bn) ++ " of first chunk should be " ++ show n-    go seed start n = do-        let bhs = appendBlocks net seed start n-        bnE <- connectBlocks net myTime bhs-        case bnE of-            Right bn -> return bn-            Left ex  -> error ex--{- Merkle Trees -}--testTreeWidth :: Int -> Property-testTreeWidth i = i /= 0 ==> calcTreeWidth (abs i) (calcTreeHeight $ abs i) == 1--testBaseWidth :: Int -> Property-testBaseWidth i = i /= 0 ==> calcTreeWidth (abs i) 0 == abs i--buildExtractTree :: Network -> [(TxHash, Bool)] -> Bool-buildExtractTree net txs =-    r == buildMerkleRoot (map fst txs) && m == map fst (filter snd txs)-  where-    (f, h) = buildPartialMerkle txs-    (r, m) =-        fromRight (error "Could not extract matches from Merkle tree") $-        extractMatches net f h (length txs)--testCompact :: Assertion-testCompact = do-    assertEqual "vector 1" 0x05123456 (encodeCompact 0x1234560000)-    assertEqual "vector 2" (0x1234560000, False) (decodeCompact 0x05123456)-    assertEqual "vector 3" 0x0600c0de (encodeCompact 0xc0de000000)-    assertEqual "vector 4" (0xc0de000000, False) (decodeCompact 0x0600c0de)-    assertEqual "vector 5" 0x05c0de00 (encodeCompact (-0x40de000000))-    assertEqual "vector 6" (-0x40de000000, False) (decodeCompact 0x05c0de00)--testCompactBitcoinCore :: Assertion-testCompactBitcoinCore = do-    assertEqual "zero" (0, False) (decodeCompact 0x00000000)-    assertEqual-        "zero (encode · decode)"-        0x00000000-        (encodeCompact . fst $ decodeCompact 0x00000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00123456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01003456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02000056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04000000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x00923456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x01803456)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x02800056)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x03800000)-    assertEqual "rounds to zero" (0, False) (decodeCompact 0x04800000)-    assertEqual "vector 1 (decode)" (0x12, False) (decodeCompact 0x01123456)-    assertEqual-        "vector 1 (encode · decode)"-        0x01120000-        (encodeCompact . fst $ decodeCompact 0x01123456)-    assertEqual "0x80 bit set" 0x02008000 (encodeCompact 0x80)-    assertEqual-        "vector 2 (negative) (decode)"-        (-0x7e, False)-        (decodeCompact 0x01fedcba)-    assertEqual-        "vector 2 (negative) (encode · decode)"-        0x01fe0000-        (encodeCompact . fst $ decodeCompact 0x01fedcba)-    assertEqual "vector 3 (decode)" (0x1234, False) (decodeCompact 0x02123456)-    assertEqual-        "vector 3 (encode · decode)"-        0x02123400-        (encodeCompact . fst $ decodeCompact 0x02123456)-    assertEqual "vector 4 (decode)" (0x123456, False) (decodeCompact 0x03123456)-    assertEqual-        "vector 4 (encode · decode)"-        0x03123456-        (encodeCompact . fst $ decodeCompact 0x03123456)-    assertEqual-        "vector 5 (decode)"-        (0x12345600, False)-        (decodeCompact 0x04123456)-    assertEqual-        "vector 5 (encode · decode)"-        0x04123456-        (encodeCompact . fst $ decodeCompact 0x04123456)-    assertEqual-        "vector 6 (decode)"-        (-0x12345600, False)-        (decodeCompact 0x04923456)-    assertEqual-        "vector 6 (encode · decode)"-        0x04923456-        (encodeCompact . fst $ decodeCompact 0x04923456)-    assertEqual-        "vector 7 (decode)"-        (0x92340000, False)-        (decodeCompact 0x05009234)-    assertEqual-        "vector 7 (encode · decode)"-        0x05009234-        (encodeCompact . fst $ decodeCompact 0x05009234)-    assertEqual-        "vector 8 (decode)"-        ( 0x1234560000000000000000000000000000000000000000000000000000000000-        , False)-        (decodeCompact 0x20123456)-    assertEqual-        "vector 8 (encode · decode)"-        0x20123456-        (encodeCompact . fst $ decodeCompact 0x20123456)-    assertBool "vector 9 (decode) (overflow)" (snd $ decodeCompact 0xff123456)-    assertBool-        "vector 9 (decode) (positive)"-        ((> 0) . fst $ decodeCompact 0xff123456)--runMerkleVector :: (Text, [Text]) -> Assertion-runMerkleVector (r, hs) =-    assertBool "merkle vector" $-        buildMerkleRoot (map f hs) == getTxHash (f r)-  where-    f = fromJust . hexToTxHash--merkleVectors :: [(Text, [Text])]-merkleVectors =-      -- Block 000000000000cd7e8cf6510303dde76121a1a791c15dba0be4be7022b07cf9e1-    [ ( "fb6698ac95b754256c5e71b4fbe07638cb6ca83ee67f44e181b91727f09f4b1f"-      , [ "dd96fdcfaec994bf583af650ff6022980ee0ba1686d84d0a3a2d24eabf34bc52"-        , "1bc216f786a564378710ae589916fc8e092ddfb9f24fe6c47b733550d476d5d9"-        , "a1db0b0194426064b067899ff2d975fb277fd52dbb1a38370800c76dd6503d41"-        , "d69f7fb0e668fbd437d1bf5211cc34d7eb8746f50cfddf705fe10bc2f8f7035f"-        , "5b4057cd80be7df5ed2ac42b776897ed3c26e3a01e4072075b8129c587094ef6"-        , "ed6dabcfba0ef43c50d89a8a0e4b236b1bc6585d4c3bbf49728b55f44312d6bc"-        , "056aaa9a3c635909c794e9b0acc7dccb0456c59a84c6b08417335bee4515e3d3"-        , "05bae5f1d1c874171692e1fc06f664e63eb143d3f096601ef938e4a9012eee66"-        , "b5e48e94e3f2fba197b3f591e01f47e185d7834d669529d44078e41c671aab0f"-        , "3b56aeadfc0c5484fd507bc89f13f2e5f61c42e0a4ae9062eda9a9aeef7db6a4"-        , "2affa187e1ebb94a2a86578b9f64951e854ff3d346fef259acfb6d0f5212e0d3"-        ]-      )-      -- Block 00000000000007cc4b6f07bfed72bccc1ed8dd031a93969a4c22211f784457d4-    , ( "886fea311d2dc64c315519f2d647e43998d780d2170f77e53dc0d85bf2ee680c"-      , [ "c9c9e5211512629fd111cc071d745b8c79bf486b4ea95489eb5de08b5d786b8e"-        , "20beb0ee30dfd323ade790ce9a46ae7a174f9ea44ce22a17c4d4eb23b7016f51"-        , "d4cb7dd741e78a8f57e12f6c8ddb0361ff2a5bf9365bd7d7df761060847daf9a"-        , "ddbfa6fdd29d4b47aeaadf82a4bf0a93d58cd7d8401fabf860a1ae8eeb51f42e"-        , "9d82bafe44abee248b968c86f165051c8413482c232659795335c52922dab471"-        , "86035372d31b53efd848cea7231aa9738c209aff64d3c59b1619341afb5b6ba3"-        , "11e7a7393d9658813dfaebc04fa6d4b73bac8d641bffa7067da879523d43d030"-        , "2f676b9aa5bc0ebf3395032c84c466e40cac29f80434cd1138e31c2d0fcc5c13"-        , "37567d559fbfae07fda9a90de0ce30b202128bc8ebdfef5ad2b53e865a3478c2"-        , "0b8e6c1200c454361e94e261738429e9c9b8dcffd85ec8511bbf5dc7e2e0ada8"-        ]-      )-      -- Block 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048-    , ( "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"-      , [ "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" ]-      )-      -- Block 000000000004d160ac1f7b775d7c1823345aeadd5fcb29ca2ad2403bb7babd4c-    , ( "aae018650f513fc42d55b2210ec3ceeeb194fb1261d37989de07451fc0cbac5c"-      , [ "a4454f22831acd7904a9902c5070a3ee4bf4c2b13bc6b2dc66735dd3c4414028"-        , "45297f334278885108dd38a0b689ed95a4373dd3f7e4413e6aebdc2654fb771b"-        ]-      )-      -- Block 000000000001d1b13a7e86ddb20da178f20d6da5cd037a29c2a15b8b84cc774e-    , ( "ca3580505feb87544760ac14a5859659e23be05f765bbed9f86a3c9aad1a5d0c"-      , [ "60702384c6e9d34ff03c2b3e726bdc649befe603216815bd0a2974921d0d9549"-        , "11f40f58941d2a81a1616a3b84b7dd8b9d07e68750827de488c11a18f54220bb"-        , "d78e82527aa8cf16e375010bc666362c0258d3c0da1885a1871121706da8b633"-        ]-      )-      -- Block 0000000000000630a4e2266a31776e952a19b7c99a6387917d9de9032f608021-    , ( "dcce8be0a9a41e7bb726c5b49d957d90b5308e3dc5dce070ccbc8996e265a6c2"-      , [ "c0f58ff12cd1023b05f8f7035cc62bf50958ddb216a4e0eb5471deb7ef25fe81"-        , "24e5bbf9008641b8fcf3d076fef66c28c695362ba9f6a6042f8275a98414ee92"-        , "e8e1f72abad5e34dabc0f6de46a484b17a9af857d1c41de19482fadf6f7f4b27"-        , "540e4d34d9fd9e5ec02853054be7ad9260379bc23388489049cca1b0f7cf518a"-        , "324444835c5fe0545f98c4240011b75e6ea1bb76f41829e4cfbe7f75b6cee924"-        , "e7d31437ac21bceb0c222a82b2723e2b8a7654147e33397679f041537022a4b2"-        , "a8b5768d8b33525ee89d546a6a6897f8e42ba9d56a2c5e871a5d2ab40258dc95"-        , "7ba712b31bae8d45810a5cda3838c7e7fb9abd6e88bb4b3ee79be9ea2f714bb4"-        , "2ae1c4d927b06edaa626b230976ad8062bbae24da9378d1de2409da5ab08a26d"-        , "3c417dc8087d6878003624b74431e17fec9ca761389034b1b1e0f32cbfb11f4f"-        , "de6de7beae8d8c98c7d46b4409d5460e58e3204d8b4caed256c7471998595909"-        , "c7c3c211402b7c4379f7b01fadc67260ee58d11e8d0bcce3d68cb45f3467e99d"-        , "77aa2717e727a096d81074bd46ae59462692d20a1acc1a01b2535518ae5aeb53"-        , "4859a710bb673aca46208bbd59d1000ae990dafff5f70b56f0853aeeaea3948b"-        , "38deca6991988e461b83aa0d49ffef0f304c4b760371682d152eeb8c56a48174"-        , "648f4f50dada3574e2dfe2dc68956b01dd97d543859a3540bbe1ef5418d0e494"-        , "9cd7be42c2f0cd8bf38738c162cd05108e213ec7958bf2571cb627872963f5c4"-        , "6740e0dd8b97e23864af41839fc197238d2f0dbefce9a82c657556be65c465fa"-        , "f75c2e4b70db4b0aabc44b77af1ae75d305340fcf6e7b5f806ddcba4aa42b55d"-        , "e125c488636749da68e6696b97525a77146c0777c7946927e37afd513d74a4e6"-        , "c20526f119aea10880af631eba7f0b60385a22e0b0c402fe8508d41952e58be9"-        , "6456c023c7e245f5c57a168633a23f57f4fadb651115f807694a6bed14ae3b55"-        , "98b26e364e2888c9f264e4b5e13103c89608609774eb07ce933d8a2a45d19776"-        , "2efaa4f167bb65ba5684f8076cd9279fd67fd9c67388c8862809bab5542e637d"-        , "ec44eeb84d8d976d77079a822710b4dfdb11a2d9a03d8cc00bab0ae424e84666"-        , "410730d9f807d81ac48b8eafac6f1d36642c1c370241b367a35f0bac6ac7c05f"-        , "e95a7d0d477fd3db22756a3fd390a50c7bc48dc9e946fea9d24bd0866b3bb0e9"-        , "a72fec99d14939216628aaf7a0afc4c017113bcae964e777e6b508864eeaacc4"-        , "8548433310fcf75dbbc042121e8318c678e0a017534786dd322a91cebe8d213f"-        ]-      )-    ]--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--testSubsidy :: Network -> Assertion-testSubsidy net = go (2 * 50 * 100 * 1000 * 1000) 0-  where-    go previous_subsidy halvings = do-        let height = halvings * getHalvingInterval net-            subsidy = computeSubsidy net height-        if halvings >= 64-            then subsidy `shouldBe` 0-            else do-                subsidy `shouldBe` (previous_subsidy `div` 2)-                go subsidy (halvings + 1)
− test/Network/Haskoin/Crypto/HashSpec.hs
@@ -1,1269 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Crypto.HashSpec (spec) where--import           Data.ByteString         (ByteString)-import           Data.Map.Strict         (singleton)-import           Data.Maybe              (fromJust)-import           Data.Serialize          as S-import           Data.String             (fromString)-import           Data.String.Conversions-import           Network.Haskoin.Block-import           Network.Haskoin.Crypto-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit              (Assertion, assertBool)-import           Test.QuickCheck---- Test vectors from NIST--- http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip--- About 1/3 of HMAC DRBG SHA-256 test vectors are tested here--spec :: Spec-spec =-    describe "hash" $ do-        it "join512( split512(h) ) == h" $-            property $-            forAll arbitraryHash256 $ forAll arbitraryHash256 . joinSplit512-        it "decodeCompact . encodeCompact i == i" $ property decEncCompact-        it "from string 64-byte hash" $-            property $-            forAll arbitraryHash512 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "from string 32-byte hash" $-            property $-            forAll arbitraryHash256 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "from string 20-byte hash" $-            property $-            forAll arbitraryHash160 $ \h ->-                fromString (cs $ encodeHex $ encode h) == h-        it "encodes and decodes bytestring" $-            property $ forAll arbitraryBS cerealID-        it "encodes and decodes hash160" $-            property $ forAll arbitraryHash160 cerealID-        it "encodes and decodes hash256" $-            property $ forAll arbitraryHash256 cerealID-        it "encodes and decodes hash512" $-            property $ forAll arbitraryHash512 cerealID--type TestVector = [ByteString]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--joinSplit512 :: Hash256 -> Hash256 -> Bool-joinSplit512 a b = split512 (join512 (a, b)) == (a, b)---- After encoding and decoding, we may loose precision so the new result is >=--- to the old one.-decEncCompact :: Integer -> Bool-decEncCompact i-    -- Integer completely fits inside the mantisse-    | abs i <= 0x007fffff = decodeCompact (encodeCompact i) == (i, False)-    -- Otherwise precision will be lost and the decoded result will-    -- be smaller than the original number-    | i >= 0              = fst (decodeCompact (encodeCompact i)) < i-    | otherwise           = fst (decodeCompact (encodeCompact i)) > i--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = decode (encode x) == Right x--t1 :: [TestVector]-t1 =-    [-    -- COUNT = 0-    [ "ca851911349384bffe89de1cbdc46e6831e44d34a4fb935ee285dd14b71a7488"-    , "659ba96c601dc69fc902940805ec0ca8"-    , ""-    , ""-    , ""-    , "e528e9abf2dece54d47c7e75e5fe302149f817ea9fb4bee6f4199697d04d5b89d54fbb978a15b5c443c9ec21036d2460b6f73ebad0dc2aba6e624abf07745bc107694bb7547bb0995f70de25d6b29e2d3011bb19d27676c07162c8b5ccde0668961df86803482cb37ed6d5c0bb8d50cf1f50d476aa0458bdaba806f48be9dcb8"-    ],-    -- COUNT = 1-    [ "79737479ba4e7642a221fcfd1b820b134e9e3540a35bb48ffae29c20f5418ea3"-    , "3593259c092bef4129bc2c6c9e19f343"-    , ""-    , ""-    , ""-    , "cf5ad5984f9e43917aa9087380dac46e410ddc8a7731859c84e9d0f31bd43655b924159413e2293b17610f211e09f770f172b8fb693a35b85d3b9e5e63b1dc252ac0e115002e9bedfb4b5b6fd43f33b8e0eafb2d072e1a6fee1f159df9b51e6c8da737e60d5032dd30544ec51558c6f080bdbdab1de8a939e961e06b5f1aca37"-    ],-    -- COUNT = 2-    [ "b340907445b97a8b589264de4a17c0bea11bb53ad72f9f33297f05d2879d898d"-    , "65cb27735d83c0708f72684ea58f7ee5"-    , ""-    , ""-    , ""-    , "75183aaaf3574bc68003352ad655d0e9ce9dd17552723b47fab0e84ef903694a32987eeddbdc48efd24195dbdac8a46ba2d972f5808f23a869e71343140361f58b243e62722088fe10a98e43372d252b144e00c89c215a76a121734bdc485486f65c0b16b8963524a3a70e6f38f169c12f6cbdd169dd48fe4421a235847a23ff"-    ],-    -- COUNT = 3-    [ "8e159f60060a7d6a7e6fe7c9f769c30b98acb1240b25e7ee33f1da834c0858e7"-    , "c39d35052201bdcce4e127a04f04d644"-    , ""-    , ""-    , ""-    , "62910a77213967ea93d6457e255af51fc79d49629af2fccd81840cdfbb4910991f50a477cbd29edd8a47c4fec9d141f50dfde7c4d8fcab473eff3cc2ee9e7cc90871f180777a97841597b0dd7e779eff9784b9cc33689fd7d48c0dcd341515ac8fecf5c55a6327aea8d58f97220b7462373e84e3b7417a57e80ce946d6120db5"-    ],-    -- COUNT = 4-    [ "74755f196305f7fb6689b2fe6835dc1d81484fc481a6b8087f649a1952f4df6a"-    , "c36387a544a5f2b78007651a7b74b749"-    , ""-    , ""-    , ""-    , "b2896f3af4375dab67e8062d82c1a005ef4ed119d13a9f18371b1b873774418684805fd659bfd69964f83a5cfe08667ddad672cafd16befffa9faed49865214f703951b443e6dca22edb636f3308380144b9333de4bcb0735710e4d9266786342fc53babe7bdbe3c01a3addb7f23c63ce2834729fabbd419b47beceb4a460236"-    ],-    -- COUNT = 5-    [ "4b222718f56a3260b3c2625a4cf80950b7d6c1250f170bd5c28b118abdf23b2f"-    , "7aed52d0016fcaef0b6492bc40bbe0e9"-    , ""-    , ""-    , ""-    , "a6da029b3665cd39fd50a54c553f99fed3626f4902ffe322dc51f0670dfe8742ed48415cf04bbad5ed3b23b18b7892d170a7dcf3ef8052d5717cb0c1a8b3010d9a9ea5de70ae5356249c0e098946030c46d9d3d209864539444374d8fbcae068e1d6548fa59e6562e6b2d1acbda8da0318c23752ebc9be0c1c1c5b3cf66dd967"-    ],-    -- COUNT = 6-    [ "b512633f27fb182a076917e39888ba3ff35d23c3742eb8f3c635a044163768e0"-    , "e2c39b84629a3de5c301db5643af1c21"-    , ""-    , ""-    , ""-    , "fb931d0d0194a97b48d5d4c231fdad5c61aedf1c3a55ac24983ecbf38487b1c93396c6b86ff3920cfa8c77e0146de835ea5809676e702dee6a78100da9aa43d8ec0bf5720befa71f82193205ac2ea403e8d7e0e6270b366dc4200be26afd9f63b7e79286a35c688c57cbff55ac747d4c28bb80a2b2097b3b62ea439950d75dff"-    ],-    -- COUNT = 7-    [ "aae3ffc8605a975befefcea0a7a286642bc3b95fb37bd0eb0585a4cabf8b3d1e"-    , "9504c3c0c4310c1c0746a036c91d9034"-    , ""-    , ""-    , ""-    , "2819bd3b0d216dad59ddd6c354c4518153a2b04374b07c49e64a8e4d055575dfbc9a8fcde68bd257ff1ba5c6000564b46d6dd7ecd9c5d684fd757df62d85211575d3562d7814008ab5c8bc00e7b5a649eae2318665b55d762de36eba00c2906c0e0ec8706edb493e51ca5eb4b9f015dc932f262f52a86b11c41e9a6d5b3bd431"-    ],-    -- COUNT = 8-    [ "b9475210b79b87180e746df704b3cbc7bf8424750e416a7fbb5ce3ef25a82cc6"-    , "24baf03599c10df6ef44065d715a93f7"-    , ""-    , ""-    , ""-    , "ae12d784f796183c50db5a1a283aa35ed9a2b685dacea97c596ff8c294906d1b1305ba1f80254eb062b874a8dfffa3378c809ab2869aa51a4e6a489692284a25038908a347342175c38401193b8afc498077e10522bec5c70882b7f760ea5946870bd9fc72961eedbe8bff4fd58c7cc1589bb4f369ed0d3bf26c5bbc62e0b2b2"-    ],-    -- COUNT = 9-    [ "27838eb44ceccb4e36210703ebf38f659bc39dd3277cd76b7a9bcd6bc964b628"-    , "39cfe0210db2e7b0eb52a387476e7ea1"-    , ""-    , ""-    , ""-    , "e5e72a53605d2aaa67832f97536445ab774dd9bff7f13a0d11fd27bf6593bfb52309f2d4f09d147192199ea584503181de87002f4ee085c7dc18bf32ce5315647a3708e6f404d6588c92b2dda599c131aa350d18c747b33dc8eda15cf40e95263d1231e1b4b68f8d829f86054d49cfdb1b8d96ab0465110569c8583a424a099a"-    ],-    -- COUNT = 10-    [ "d7129e4f47008ad60c9b5d081ff4ca8eb821a6e4deb91608bf4e2647835373a5"-    , "a72882773f78c2fc4878295840a53012"-    , ""-    , ""-    , ""-    , "0cbf48585c5de9183b7ff76557f8fc9ebcfdfde07e588a8641156f61b7952725bbee954f87e9b937513b16bba0f2e523d095114658e00f0f3772175acfcb3240a01de631c19c5a834c94cc58d04a6837f0d2782fa53d2f9f65178ee9c837222494c799e64c60406069bd319549b889fa00a0032dd7ba5b1cc9edbf58de82bfcd"-    ],-    -- COUNT = 11-    [ "67fe5e300c513371976c80de4b20d4473889c9f1214bce718bc32d1da3ab7532"-    , "e256d88497738a33923aa003a8d7845c"-    , ""-    , ""-    , ""-    , "b44660d64ef7bcebc7a1ab71f8407a02285c7592d755ae6766059e894f694373ed9c776c0cfc8594413eefb400ed427e158d687e28da3ecc205e0f7370fb089676bbb0fa591ec8d916c3d5f18a3eb4a417120705f3e2198154cd60648dbfcfc901242e15711cacd501b2c2826abe870ba32da785ed6f1fdc68f203d1ab43a64f"-    ],-    -- COUNT = 12-    [ "de8142541255c46d66efc6173b0fe3ffaf5936c897a3ce2e9d5835616aafa2cb"-    , "d01f9002c407127bc3297a561d89b81d"-    , ""-    , ""-    , ""-    , "64d1020929d74716446d8a4e17205d0756b5264867811aa24d0d0da8644db25d5cde474143c57d12482f6bf0f31d10af9d1da4eb6d701bdd605a8db74fb4e77f79aaa9e450afda50b18d19fae68f03db1d7b5f1738d2fdce9ad3ee9461b58ee242daf7a1d72c45c9213eca34e14810a9fca5208d5c56d8066bab1586f1513de7"-    ],-    -- COUNT = 13-    [ "4a8e0bd90bdb12f7748ad5f147b115d7385bb1b06aee7d8b76136a25d779bcb7"-    , "7f3cce4af8c8ce3c45bdf23c6b181a00"-    , ""-    , ""-    , ""-    , "320c7ca4bbeb7af977bc054f604b5086a3f237aa5501658112f3e7a33d2231f5536d2c85c1dad9d9b0bf7f619c81be4854661626839c8c10ae7fdc0c0b571be34b58d66da553676167b00e7d8e49f416aacb2926c6eb2c66ec98bffae20864cf92496db15e3b09e530b7b9648be8d3916b3c20a3a779bec7d66da63396849aaf"-    ],-    -- COUNT = 14-    [ "451ed024bc4b95f1025b14ec3616f5e42e80824541dc795a2f07500f92adc665"-    , "2f28e6ee8de5879db1eccd58c994e5f0"-    , ""-    , ""-    , ""-    , "3fb637085ab75f4e95655faae95885166a5fbb423bb03dbf0543be063bcd48799c4f05d4e522634d9275fe02e1edd920e26d9accd43709cb0d8f6e50aa54a5f3bdd618be23cf73ef736ed0ef7524b0d14d5bef8c8aec1cf1ed3e1c38a808b35e61a44078127c7cb3a8fd7addfa50fcf3ff3bc6d6bc355d5436fe9b71eb44f7fd"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--t2 :: [TestVector]-t2 =-    [-    -- COUNT = 0-    [ "d3cc4d1acf3dde0c4bd2290d262337042dc632948223d3a2eaab87da44295fbd"-    , "0109b0e729f457328aa18569a9224921"-    , ""-    , "3c311848183c9a212a26f27f8c6647e40375e466a0857cc39c4e47575d53f1f6"-    , "fcb9abd19ccfbccef88c9c39bfb3dd7b1c12266c9808992e305bc3cff566e4e4"-    , "9c7b758b212cd0fcecd5daa489821712e3cdea4467b560ef5ddc24ab47749a1f1ffdbbb118f4e62fcfca3371b8fbfc5b0646b83e06bfbbab5fac30ea09ea2bc76f1ea568c9be0444b2cc90517b20ca825f2d0eccd88e7175538b85d90ab390183ca6395535d34473af6b5a5b88f5a59ee7561573337ea819da0dcc3573a22974"-    ],-    -- COUNT = 1-    [ "f97a3cfd91faa046b9e61b9493d436c4931f604b22f1081521b3419151e8ff06"-    , "11f3a7d43595357d58120bd1e2dd8aed"-    , ""-    , "517289afe444a0fe5ed1a41dbbb5eb17150079bdd31e29cf2ff30034d8268e3b"-    , "88028d29ef80b4e6f0fe12f91d7449fe75062682e89c571440c0c9b52c42a6e0"-    , "c6871cff0824fe55ea7689a52229886730450e5d362da5bf590dcf9acd67fed4cb32107df5d03969a66b1f6494fdf5d63d5b4d0d34ea7399a07d0116126d0d518c7c55ba46e12f62efc8fe28a51c9d428e6d371d7397ab319fc73ded4722e5b4f30004032a6128df5e7497ecf82ca7b0a50e867ef6728a4f509a8c859087039c"-    ],-    -- COUNT = 2-    [ "0f2f23d64f481cabec7abb01db3aabf125c3173a044b9bf26844300b69dcac8b"-    , "9a5ae13232b43aa19cfe8d7958b4b590"-    , ""-    , "ec4c7a62acab73385f567da10e892ff395a0929f959231a5628188ce0c26e818"-    , "6b97b8c6b6bb8935e676c410c17caa8042aa3145f856d0a32b641e4ae5298648"-    , "7480a361058bd9afa3db82c9d7586e42269102013f6ec5c269b6d05f17987847748684766b44918fd4b65e1648622fc0e0954178b0279dfc9fa99b66c6f53e51c4860131e9e0644287a4afe4ca8e480417e070db68008a97c3397e4b320b5d1a1d7e1d18a95cfedd7d1e74997052bf649d132deb9ec53aae7dafdab55e6dae93"-    ],-    -- COUNT = 3-    [ "53c56660c78481be9c63284e005fcc14fbc7fb27732c9bf1366d01a426765a31"-    , "dc7a14d0eb5b0b3534e717a0b3c64614"-    , ""-    , "3aa848706ecb877f5bedf4ffc332d57c22e08747a47e75cff6f0fd1316861c95"-    , "9a401afa739b8f752fddacd291e0b854f5eff4a55b515e20cb319852189d3722"-    , "5c0eb420e0bf41ce9323e815310e4e8303cd677a8a8b023f31f0d79f0ca15aeb636099a369fd074d69889865eac1b72ab3cbfebdb8cf460b00072802e2ec648b1349a5303be4ccaadd729f1a9ea17482fd026aaeb93f1602bc1404b9853adde40d6c34b844cf148bc088941ecfc1642c8c0b9778e45f3b07e06e21ee2c9e0300"-    ],-    -- COUNT = 4-    [ "f63c804404902db334c54bb298fc271a21d7acd9f770278e089775710bf4fdd7"-    , "3e45009ea9cb2a36ba1aa4bf39178200"-    , ""-    , "d165a13dc8cc43f3f0952c3f5d3de4136954d983683d4a3e6d2dc4c89bf23423"-    , "75106bc86d0336df85097f6af8e80e2da59046a03fa65b06706b8bbc7ffc6785"-    , "6363139bba32c22a0f5cd23ca6d437b5669b7d432f786b8af445471bee0b2d24c9d5f2f93717cbe00d1f010cc3b9c515fc9f7336d53d4d26ba5c0d76a90186663c8582eb739c7b6578a3328bf68dc2cec2cd89b3a90201f6993adcc854df0f5c6974d0f5570765a15fe03dbce28942dd2fd16ba2027e68abac83926969349af8"-    ],-    -- COUNT = 5-    [ "2aaca9147da66c176615726b69e3e851cc3537f5f279fe7344233d8e44cfc99d"-    , "4e171f080af9a6081bee9f183ac9e340"-    , ""-    , "d75a2a6eb66c3833e50f5ec3d2e434cf791448d618026d0c360806d120ded669"-    , "b643b74c15b37612e6577ed7ca2a4c67a78d560af9eb50a4108fca742e87b8d6"-    , "501dcdc977f4ba856f24eaa4968b374bebb3166b280334cb510232c31ebffde10fa47b7840ef3fe3b77725c2272d3a1d4219baf23e0290c622271edcced58838cf428f0517425d2e19e0d8c89377eecfc378245f283236fafa466c914b99672ceafab369e8889a0c866d8bd639db9fb797254262c6fd44cfa9045ad6340a60ef"-    ],-    -- COUNT = 6-    [ "a2e4cd48a5cf918d6f55942d95fcb4e8465cdc4f77b7c52b6fae5b16a25ca306"-    , "bef036716440db6e6d333d9d760b7ca8"-    , ""-    , "bfa591c7287f3f931168f95e38869441d1f9a11035ad8ea625bb61b9ea17591c"-    , "c00c735463bca215adc372cb892b05e939bf669583341c06d4e31d0e5b363a37"-    , "e7d136af69926a5421d4266ee0420fd729f2a4f7c295d3c966bdfa05268180b508b8a2852d1b3a06fd2ab3e13c54005123ef319f42d0c6d3a575e6e7e1496cb28aacadbcf83740fba8f35fcee04bb2ed8a51db3d3362b01094a62fb57e33c99a432f29fce6676cffbbcc05107e794e75e44a02d5e6d9d748c5fbff00a0178d65"-    ],-    -- COUNT = 7-    [ "95a67771cba69011a79776e713145d309edae56fad5fd6d41d83eaff89df6e5e"-    , "be5b5164e31ecc51ba6f7c3c5199eb33"-    , ""-    , "065f693b229a7c4fd373cd15b3807552dd9bf98c5485cef361949d4e7d774b53"-    , "9afb62406f0e812c4f156d58b19a656c904813c1b4a45a0029ae7f50731f8014"-    , "f61b61a6e79a41183e8ed6647899d2dc85cdaf5c3abf5c7f3bf37685946dc28f4923dc842f2d4326bd6ce0d50a84cb3ba869d72a36e246910eba6512ba36cd7ed3a5437c9245b00a344308c792b668b458d3c3e16dee2fbec41867da31084d46d8ec168de2148ef64fc5b72069abf5a6ada1ead2b7146bb793ff1c9c3690fa56"-    ],-    -- COUNT = 8-    [ "a459e1815cbca4514ec8094d5ab2414a557ba6fe10e613c345338d0521e4bf90"-    , "62221392e2552e76cd0d36df6e6068eb"-    , ""-    , "0a3642b02b23b3ef62c701a63401124022f5b896de86dab6e6c7451497aa1dcc"-    , "c80514865901371c45ba92d9f95d50bb7c9dd1768cb3dfbc45b968da94965c6e"-    , "464e6977b8adaef307c9623e41c357013249c9ffd77f405f3925cebb69f151ce8fbb6a277164002aee7858fc224f6499042aa1e6322deee9a5d133c31d640e12a7487c731ba03ad866a24675badb1d79220c40be689f79c2a0be93cb4dada3e0eac4ab140cb91998b6f11953e68f2319b050c40f71c34de9905ae41b2de1c2f6"-    ],-    -- COUNT = 9-    [ "252c2cad613e002478162861880979ee4e323025eebb6fb2e0aa9f200e28e0a1"-    , "d001bc9a8f2c8c242e4369df0c191989"-    , ""-    , "9bcfc61cb2bc000034bb3db980eb47c76fb5ecdd40553eff113368d639b947fd"-    , "8b0565c767c2610ee0014582e9fbecb96e173005b60e9581503a6dca5637a26e"-    , "e96c15fe8a60692b0a7d67171e0195ff6e1c87aab844221e71700d1bbee75feea695f6a740c9760bbe0e812ecf4061d8f0955bc0195e18c4fd1516ebca50ba6a6db86881737dbab8321707675479b87611db6af2c97ea361a5484555ead454defb1a64335de964fc803d40f3a6f057893d2afc25725754f4f00abc51920743dc"-    ],-    -- COUNT = 10-    [ "8be0ca6adc8b3870c9d69d6021bc1f1d8eb9e649073d35ee6c5aa0b7e56ad8a5"-    , "9d1265f7d51fdb65377f1e6edd6ae0e4"-    , ""-    , "da86167ac997c406bb7979f423986a84ec6614d6caa7afc10aff0699a9b2cf7f"-    , "e4baa3c555950b53e2bfdba480cb4c94b59381bac1e33947e0c22e838a9534cf"-    , "64384ecc4ea6b458efc227ca697eac5510092265520c0a0d8a0ccf9ed3ca9d58074671188c6a7ad16d0b050cdc072c125d7298d3a31d9f044a9ee40da0089a84fea28cc7f05f1716db952fad29a0e779635cb7a912a959be67be2f0a4170aace2981802e2ff6467e5b46f0ffbff3b42ba5935fd553c82482ac266acf1cd247d7"-    ],-    -- COUNT = 11-    [ "d43a75b6adf26d60322284cb12ac38327792442aa8f040f60a2f331b33ac4a8f"-    , "0682f8b091f811afacaacaec9b04d279"-    , ""-    , "7fd3b8f512940da7de5d80199d9a7b42670c04a945775a3dba869546cbb9bc65"-    , "2575db20bc7aafc2a90a5dabab760db851d754777bc9f05616af1858b24ff3da"-    , "0da7a8dc73c163014bf0841913d3067806456bbca6d5de92b85534c6545467313648d71ef17c923d090dc92cff8d4d1a9a2bb63e001dc2e8ab1a597999be3d6cf70ff63fee9985801395fbd4f4990430c4259fcae4fa1fcd73dc3187ccc102d04af7c07532885e5a226fc42809c48f22eecf4f6ab996ae4fcb144786957d9f41"-    ],-    -- COUNT = 12-    [ "64352f236af5d32067a529a8fd05ba00a338c9de306371a0b00c36e610a48d18"-    , "df99ed2c7608c870624b962a5dc68acd"-    , ""-    , "da416335e7aaf60cf3d06fb438735ce796aad09034f8969c8f8c3f81e32fef24"-    , "a28c07c21a2297311adf172c19e83ca0a87731bdffb80548978d2d1cd82cf8a3"-    , "132b9f25868729e3853d3c51f99a3b5fae6d4204bea70890daf62e042b776a526c8fb831b80a6d5d3f153237df1fd39b6fd9137963f5516d9cdd4e3f9195c46e9972c15d3edc6606e3368bde1594977fb88d0ca6e6f5f3d057ccadc7d7dab77dfc42658a1e972aa446b20d418286386a52dfc1c714d2ac548713268b0b709729"-    ],-    -- COUNT = 13-    [ "282f4d2e05a2cd30e9087f5633089389449f04bac11df718c90bb351cd3653a5"-    , "90a7daf3c0de9ea286081efc4a684dfb"-    , ""-    , "2630b4ccc7271cc379cb580b0aaede3d3aa8c1c7ba002cf791f0752c3d739007"-    , "c31d69de499f1017be44e3d4fa77ecebc6a9b9934749fcf136f267b29115d2cc"-    , "c899094520e0197c37b91dd50778e20a5b950decfb308d39f1db709447ae48f6101d9abe63a783fbb830eec1d359a5f61a2013728966d349213ee96382614aa4135058a967627183810c6622a2158cababe3b8ab99169c89e362108bf5955b4ffc47440f87e4bad0d36bc738e737e072e64d8842e7619f1be0af1141f05afe2d"-    ],-    -- COUNT = 14-    [ "13c752b9e745ce77bbc7c0dbda982313d3fe66f903e83ebd8dbe4ff0c11380e9"-    , "f1a533095d6174164bd7c82532464ae7"-    , ""-    , "4f53db89b9ba7fc00767bc751fb8f3c103fe0f76acd6d5c7891ab15b2b7cf67c"-    , "582c2a7d34679088cca6bd28723c99aac07db46c332dc0153d1673256903b446"-    , "6311f4c0c4cd1f86bd48349abb9eb930d4f63df5e5f7217d1d1b91a71d8a6938b0ad2b3e897bd7e3d8703db125fab30e03464fad41e5ddf5bf9aeeb5161b244468cfb26a9d956931a5412c97d64188b0da1bd907819c686f39af82e91cfeef0cbffb5d1e229e383bed26d06412988640706815a6e820796876f416653e464961"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--t3 :: [TestVector]-t3 =-    [-    -- COUNT = 0-    [ "5cacc68165a2e2ee20812f35ec73a79dbf30fd475476ac0c44fc6174cdac2b55"-    , "6f885496c1e63af620becd9e71ecb824"-    , "e72dd8590d4ed5295515c35ed6199e9d211b8f069b3058caa6670b96ef1208d0"-    , ""-    , ""-    , "f1012cf543f94533df27fedfbf58e5b79a3dc517a9c402bdbfc9a0c0f721f9d53faf4aafdc4b8f7a1b580fcaa52338d4bd95f58966a243cdcd3f446ed4bc546d9f607b190dd69954450d16cd0e2d6437067d8b44d19a6af7a7cfa8794e5fbd728e8fb2f2e8db5dd4ff1aa275f35886098e80ff844886060da8b1e7137846b23b"-    ],-    -- COUNT = 1-    [ "8df013b4d103523073917ddf6a869793059e9943fc8654549e7ab22f7c29f122"-    , "da2625af2ddd4abcce3cf4fa4659d84e"-    , "b571e66d7c338bc07b76ad3757bb2f9452bf7e07437ae8581ce7bc7c3ac651a9"-    , ""-    , ""-    , "b91cba4cc84fa25df8610b81b641402768a2097234932e37d590b1154cbd23f97452e310e291c45146147f0da2d81761fe90fba64f94419c0f662b28c1ed94da487bb7e73eec798fbcf981b791d1be4f177a8907aa3c401643a5b62b87b89d66b3a60e40d4a8e4e9d82af6d2700e6f535cdb51f75c321729103741030ccc3a56"-    ],-    -- COUNT = 2-    [ "565b2b77937ba46536b0f693b3d5e4a8a24563f9ef1f676e8b5b2ef17823832f"-    , "4ef3064ec29f5b7f9686d75a23d170e3"-    , "3b722433226c9dba745087270ab3af2c909425ba6d39f5ce46f07256068319d9"-    , ""-    , ""-    , "d144ee7f8363d128872f82c15663fe658413cd42651098e0a7c51a970de75287ec943f9061e902280a5a9e183a7817a44222d198fbfab184881431b4adf35d3d1019da5a90b3696b2349c8fba15a56d0f9d010a88e3f9eeedb67a69bcaa71281b41afa11af576b765e66858f0eb2e4ec4081609ec81da81df0a0eb06787340ea"-    ],-    -- COUNT = 3-    [ "fc3832a91b1dcdcaa944f2d93cbceb85c267c491b7b59d017cde4add79a836b6"-    , "d5e76ce9eabafed06e33a913e395c5e0"-    , "ffc5f6eefd51da64a0f67b5f0cf60d7ab43fc7836bca650022a0cee57a43c148"-    , ""-    , ""-    , "0e713c6cc9a4dbd4249201d12b7bf5c69c3e18eb504bf3252db2f43675e17d99b6a908400cea304011c2e54166dae1f20260008efe4e06a87e0ce525ca482bca223a902a14adcf2374a739a5dfeaf14cadd72efa4d55d15154c974d9521535bcb70658c5b6c944020afb04a87b223b4b8e5d89821704a9985bb010405ba8f3d4"-    ],-    -- COUNT = 4-    [ "8009eb2cb49fdf16403bcdfd4a9f952191062acb9cc111eca019f957fb9f4451"-    , "355598866952394b1eddd85d59f81c9d"-    , "09ff1d4b97d83b223d002e05f754be480d13ba968e5aac306d71cc9fc49cc2dd"-    , ""-    , ""-    , "9550903c2f02cf77c8f9c9a37041d0040ee1e3ef65ba1a1fbbcf44fb7a2172bd6b3aaabe850281c3a1778277bacd09614dfefececac64338ae24a1bf150cbf9d9541173a82ecba08aa19b75abb779eb10efa4257d5252e8afcac414bc3bb5d3006b6f36fb9daea4c8c359ef6cdbeff27c1068571dd3c89dc87eda9190086888d"-    ],-    -- COUNT = 5-    [ "a6e4c9a8bd6da23b9c2b10a7748fd08c4f782fadbac7ea501c17efdc6f6087bd"-    , "acdc47edf1d3b21d0aec7631abb6d7d5"-    , "c16ee0908a5886dccf332fbc61de9ec7b7972d2c4c83c477409ce8a15c623294"-    , ""-    , ""-    , "a52f93ccb363e2bdf0903622c3caedb7cffd04b726052b8d455744c71b76dee1b71db9880dc3c21850489cb29e412d7d80849cfa9151a151dcbf32a32b4a54cac01d3200200ed66a3a5e5c131a49655ffbf1a8824ff7f265690dffb4054df46a707b9213924c631c5bce379944c856c4f7846e281ac89c64fad3a49909dfb92b"-    ],-    -- COUNT = 6-    [ "59d6307460a9bdd392dfc0904973991d585696010a71e52d590a5039b4849fa4"-    , "34a0aafb95917cbf8c38fc5548373c05"-    , "0407b7c57bc11361747c3d67526c36e228028a5d0b145d66ab9a2fe4b07507a0"-    , ""-    , ""-    , "299aba0661315211b09d2861855d0b4b125ab24649461341af6abd903ed6f025223b3299f2126fcad44c675166d800619cf49540946b12138989417904324b0ddad121327211a297f11259c9c34ce4c70c322a653675f78d385e4e2443f8058d141195e17e0bd1b9d44bf3e48c376e6eb44ef020b11cf03eb141c46ecb43cf3d"-    ],-    -- COUNT = 7-    [ "9ae3506aadbc8358696ba1ba17e876e1157b7048235921503d36d9211b430342"-    , "9abf7d66afee5d2b811cba358bbc527d"-    , "0d645f6238e9ceb038e4af9772426ca110c5be052f8673b8b5a65c4e53d2f519"-    , ""-    , ""-    , "5f032c7fec6320fe423b6f38085cbad59d826085afe915247b3d546c4c6b174554dd4877c0d671de9554b505393a44e71f209b70f991ac8aa6e08f983fff2a4c817b0cd26c12b2c929378506489a75b2025b358cb5d0400821e7e252ac6376cd94a40c911a7ed8b6087e3de5fa39fa6b314c3ba1c593b864ce4ff281a97c325b"-    ],-    -- COUNT = 8-    [ "96ae3b8775b36da2a29b889ad878941f43c7d51295d47440cd0e3c4999193109"-    , "1fe022a6fc0237b055d4d6a7036b18d5"-    , "1e40e97362d0a823d3964c26b81ab53825c56446c5261689011886f19b08e5c2"-    , ""-    , ""-    , "e707cd14b06ce1e6dbcceaedbf08d88891b03f44ad6a797bd12fdeb557d0151df9346a028dec004844ca46adec3051dafb345895fa9f4604d8a13c8ff66ae093fa63c4d9c0816d55a0066d31e8404c841e87b6b2c7b5ae9d7afb6840c2f7b441bf2d3d8bd3f40349c1c014347c1979213c76103e0bece26ad7720601eff42275"-    ],-    -- COUNT = 9-    [ "33f5120396336e51ee3b0b619b5f873db05ca57cda86aeae2964f51480d14992"-    , "6f1f6e9807ba5393edcf3cb4e4bb6113"-    , "3709605af44d90196867c927512aa8ba31837063337b4879408d91a05c8efa9f"-    , ""-    , ""-    , "8b8291126ded9acef12516025c99ccce225d844308b584b872c903c7bc6467599a1cead003dc4c70f6d519f5b51ce0da57f53da90dbe8f666a1a1dde297727fee2d44cebd1301fc1ca75956a3fcae0d374e0df6009b668fd21638d2b733e6902d22d5bfb4af1b455975e08eef0ebe4dc87705801e7776583c8de11672729f723"-    ],-    -- COUNT = 10-    [ "ad300b799005f290fee7f930eebce158b98fb6cb449987fe433f955456b35300"-    , "06aa2514e4bd114edf7ac105cfef2772"-    , "87ada711465e4169da2a74c931afb9b5a5b190d07b7af342aa99570401c3ee8a"-    , ""-    , ""-    , "80d7c606ff49415a3a92ba1f2943235c01339c8f9cd0b0511fbfdf3ef23c42ffff008524193faaa4b7f2f2eb0cfa221d9df89bd373fe4e158ec06fad3ecf1eb48b8239b0bb826ee69d773883a3e8edac66254610ff70b6609836860e39ea1f3bfa04596fee1f2baca6cebb244774c6c3eb4af1f02899eba8f4188f91776de16f"-    ],-    -- COUNT = 11-    [ "130b044e2c15ab89375e54b72e7baae6d4cad734b013a090f4df057e634f6ff0"-    , "65fd6ac602cd44107d705dbc066e52b6"-    , "f374aba16f34d54aae5e494505b67d3818ef1c08ea24967a76876d4361379aec"-    , ""-    , ""-    , "5d179534fb0dba3526993ed8e27ec9f915183d967336bb24352c67f4ab5d7935d3168e57008da851515efbaecb69904b6d899d3bfa6e9805659aef2942c4903875b8fcbc0d1d24d1c075f0ff667c1fc240d8b410dff582fa71fa30878955ce2ed786ef32ef852706e62439b69921f26e84e0f54f62b938f04905f05fcd7c2204"-    ],-    -- COUNT = 12-    [ "716430e999964b35459c17921fe5f60e09bd9ab234cb8f4ba4932bec4a60a1d5"-    , "9533b711e061b07d505da707cafbca03"-    , "372ae616d1a1fc45c5aecad0939c49b9e01c93bfb40c835eebd837af747f079d"-    , ""-    , ""-    , "a80d6a1b2d0ce01fe0d26e70fb73da20d45841cf01bfbd50b90d2751a46114c0e758cb787d281a0a9cf62f5c8ce2ee7ca74fefff330efe74926acca6d6f0646e4e3c1a1e52fce1d57b88beda4a5815896f25f38a652cc240deb582921c8b1d03a1da966dd04c2e7eee274df2cd1837096b9f7a0d89a82434076bc30173229a60"-    ],-    -- COUNT = 13-    [ "7679f154296e6d580854826539003a82d1c54e2e062c619d00da6c6ac820789b"-    , "55d12941b0896462e7d888e5322a99a3"-    , "ba4d1ed696f58ef64596c76cee87cc1ca83069a79e7982b9a06f9d62f4209faf"-    , ""-    , ""-    , "10dc7cd2bb68c2c28f76d1b04ae2aa287071e04c3b688e1986b05cc1209f691daa55868ebb05b633c75a40a32b49663185fe5bb8f906008347ef51590530948b87613920014802e5864e0758f012e1eae31f0c4c031ef823aecfb2f8a73aaa946fc507037f9050b277bdeaa023123f9d22da1606e82cb7e56de34bf009eccb46"-    ],-    -- COUNT = 14-    [ "8ca4a964e1ff68753db86753d09222e09b888b500be46f2a3830afa9172a1d6d"-    , "a59394e0af764e2f21cf751f623ffa6c"-    , "eb8164b3bf6c1750a8de8528af16cffdf400856d82260acd5958894a98afeed5"-    , ""-    , ""-    , "fc5701b508f0264f4fdb88414768e1afb0a5b445400dcfdeddd0eba67b4fea8c056d79a69fd050759fb3d626b29adb8438326fd583f1ba0475ce7707bd294ab01743d077605866425b1cbd0f6c7bba972b30fbe9fce0a719b044fcc1394354895a9f8304a2b5101909808ddfdf66df6237142b6566588e4e1e8949b90c27fc1f"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--t4 :: [TestVector]-t4 =-    [-    -- COUNT = 0-    [ "5d3286bc53a258a53ba781e2c4dcd79a790e43bbe0e89fb3eed39086be34174b"-    , "c5422294b7318952ace7055ab7570abf"-    , "2dba094d008e150d51c4135bb2f03dcde9cbf3468a12908a1b025c120c985b9d"-    , "793a7ef8f6f0482beac542bb785c10f8b7b406a4de92667ab168ecc2cf7573c6"-    , "2238cdb4e23d629fe0c2a83dd8d5144ce1a6229ef41dabe2a99ff722e510b530"-    , "d04678198ae7e1aeb435b45291458ffde0891560748b43330eaf866b5a6385e74c6fa5a5a44bdb284d436e98d244018d6acedcdfa2e9f499d8089e4db86ae89a6ab2d19cb705e2f048f97fb597f04106a1fa6a1416ad3d859118e079a0c319eb95686f4cbcce3b5101c7a0b010ef029c4ef6d06cdfac97efb9773891688c37cf"-    ],-    -- COUNT = 1-    [ "c2a566a9a1817b15c5c3b778177ac87c24e797be0a845f11c2fe399dd37732f2"-    , "cb1894eb2b97b3c56e628329516f86ec"-    , "13ce4d8dd2db9796f94156c8e8f0769b0aa1c82c1323b61536603bca37c9ee29"-    , "413dd83fe56835abd478cb9693d67635901c40239a266462d3133b83e49c820b"-    , "d5c4a71f9d6d95a1bedf0bd2247c277d1f84a4e57a4a8825b82a2d097de63ef1"-    , "b3a3698d777699a0dd9fa3f0a9fa57832d3cefac5df24437c6d73a0fe41040f1729038aef1e926352ea59de120bfb7b073183a34106efed6278ff8ad844ba0448115dfddf3319a82de6bb11d80bd871a9acd35c73645e1270fb9fe4fa88ec0e465409ea0cba809fe2f45e04943a2e396bbb7dd2f4e0795303524cc9cc5ea54a1"-    ],-    -- COUNT = 2-    [ "a33288a96f41dd54b945e060c8bd0c094f1e28267cc1dcbba52063c1a9d54c4d"-    , "36918c977e1a7276a2bb475591c367b7"-    , "6aa528c940962638dc2201738850fd1fe6f5d0eb9f687ff1af39d9c7b36830d9"-    , "37ee633a635e43af59abdb1762c7ea45bfe060ec1d9077ecd2a43a658673f3c7"-    , "2eb96f2e28fa9f674bb03ade703b8f791ee5356e2ee85c7ed5bda96325256c61"-    , "db2f91932767eb846961ce5321c7003431870508e8c6f8d432ca1f9cee5cdc1aed6e0f133d317eb6990c4b3b0a360cdfb5b43a6e712bd46bca04c414868fab22c6a49c4b89c812697c3a7fbfc8ddf10c8aa5ebf13a09fd114eb2a02a07f69786f3ce7fd30231f22779bc8db103b13fa546dbc45a89a86275281172761683d384"-    ],-    -- COUNT = 3-    [ "5f37b6e47e1776e735adc03d4b999879477ff4a206231924033d94c0114f911b"-    , "7d12d62c79c9f6234ae0314156947459"-    , "92d4d9fab5f8bf5119f2663a9df7334f50dcde74fb9d7732f7eba56501e60d54"-    , "c9aef0d7a9ba7345d08b6d5b5ce5645c7495b8685e6b93846ffcf470f5abd40d"-    , "50d9d1f5074f7d9f1a24a9c63aa47b94da5ba78db1b0f18e4d4fe45c6875813c"-    , "20d942bbd7d98700faa37e94d53bf74f2d6bd1d8c95c0b88d842c4857797d59e7c8788aeeac29740122f208f703bf35dc32b0035db0648384feb6aa17a3274bc09b2d2b746c5a06fd82f4469fb86131a49482cb7be7d9b4b95042394cfb18b13f333ec0fe5c227bf1d8f33ecb2e42e358b6c3e034cb585331bd1d27f638029b9"-    ],-    -- COUNT = 4-    [ "2311c5afd64c584484b2729e84db80c0b4063fe9ca7edc83350488d7e67264a0"-    , "6a6dfd975a0dc7b72df1f107c4b3b3a6"-    , "2abd870ec5fe26ed14dfa57a3309f920131b70580c3639af2645cd1af93db1b1"-    , "c6e532a3b25653b6002aed5269cc2118749306e736bde039d4d569d4f967773f"-    , "5e7d26c4da769c373092b2b4f72b109fe34bdb7d169ea38f78ebae5df4a15759"-    , "cacaeb1b4ac2305d8714eb50cbe1c67c5a2c0bbc7938fdfdcafef7c85fc40becbf777a4cfb6f14c6eee320943a493d2b0a744a6eb3c256ee9a3763037437df9adce3e2260f0c35e958af0edb5a81debd8bdaf2b8bb2b98b9186e5a222a21609ff58df4cbe1d4898d10d6e7c46f31f5cb1041bfd83a5fb27d5c56c961e91403fc"-    ],-    -- COUNT = 5-    [ "362ece9d330e1172a8f9e50258476d0c79c3ee50346524ba12d970ee3a6ef8c5"-    , "cf11bcb4d9d51311ceacfca8705e833f"-    , "abb5a8edde02e526449284ecc31bc713383df3ed085f752e3b6a32f305861eed"-    , "746302ab1f4a86b17546bea762e929360f2e95c7788a63545a264ef997c8c65e"-    , "b907c5b2a8833a48e56e819228ce9a050b41b3309f5ca37bed720311d92b33af"-    , "73c7131a558350590053580873ef956ff952f2aa6ff1bea452e013d1bc2afddea2311756dbe756e63ba6258480c48f3f6c1319b5f572f67ca530af09e39413d1d432bea8f89206619618cb0e7c88e9f2033639d0eb0efc20616b64f940da99b88231984c3fb23f19e890576f555fde394dbd4351f17a7ffd5c369379001bda03"-    ],-    -- COUNT = 6-    [ "cf614bc29946bc0095f415e8bdeda10aab05392f9cc9187a86ea6ec95ee422e1"-    , "77fb5ec22dc0432cc13f4693e2e3bd9a"-    , "e4ce77914ffbc5fddf1fb51edfafdc196109139b84c741354135ec8d314c7c43"-    , "e1e83ee1205acaf6164dc287aec08e5b32789e5be818078db39e53cad589db51"-    , "4e20c0226d5e1e7e805679f03f72452b5bea2d0ba41e0c12329bf60eb3016dd1"-    , "838fdf1418a746aa52ae4005d90c3fd301f648c5770ffef2a9f3912e37a93850cc4b8bfcce910aead0cb75958823b1a62e283901c5e4a3980e4ea36257458e2e4953555819b8852a26489b1d74821f80c9908469b43f124ff7ea62497c36159a47353098a1b9ec32e54800d6704371cc37f357ad74aacc203e9b6db97f94d0c4"-    ],-    -- COUNT = 7-    [ "a8da1d3e233f393fd44d204c200202f7d01896e72c5ac652940cfd15b5d4b0bd"-    , "0a112b4cb0890af0a495e0f49fcf6874"-    , "d2e32799bc822b8d033299bdf63dc35774f7649e935d25be5b10512c430d1bda"-    , "920a82d76fcd2cd106ada64bba232b7b2344f3afe6b1d1d20ee8795144571009"-    , "eeaac5878275372025f8231febed64db6a11273c3c00d625fc80a95f18ad7d3f"-    , "5f6dae489b53d89027b2cc333c700f090152d77b3eaf01d47f56ce6eca9893ef877b4cb560fab0fbdb34e3d1c6cd8480b33c053d2661a10aa531df4961b97d659c7492584236582b3fe701055efa59c328194cd1e07fcffd910d9ee01b7b9e8c8fda7f7ac01a8e203b8b26eb8078a9b9a5021562c44af24089e3ef84c1d5a6bd"-    ],-    -- COUNT = 8-    [ "a77b1ed4ecaa650374e1052c405f1d88881c25c87d13dbe1334d8c1a847fa76b"-    , "05c143e2f145db216fe7be9ed23635d0"-    , "b5c750968ff09ed251d4a1c05342ac843db5246b19045728a634fa4f6e752e54"-    , "ff5937bcd01a363696bf8e40adc8e4ab3e56dbf7e7d09451c99e538785fe6697"-    , "4acb34eea8266badcf8f6557a0eecf3eb4d7a295c876d6175598cb66a388efb8"-    , "ec13eadfcc84e77d2a2efa1a2cd8b1355587cb27feb3d19d75b37f0446333ddb8236e751c63b7a6e595ec24a25051a696dbe8c062dd8896d1446db228a2f10e8094ee07e7ee648ed6bebb2f5ec5aae24c9c640665c28355cc11c116795ecc070790f7fdfc4398900311b6695d5da0175091ed1828d2731085bfb4a20bd86cce0"-    ],-    -- COUNT = 9-    [ "491686c781e83eb4e21d9989e8d718100b0d21a2c56295888baef1a65f219651"-    , "499085296d21065feabf3106101c8d6f"-    , "d208a72f9ae34f0817669fb04f49239dd31700f3dc9a93db8d75fb79f9b686c1"-    , "9ffc61893a293a864008fdd56d3292600d9e2ec8a1ea8f34ac5931e968905a23"-    , "4ff3a397dfdae0912032a302a5e7a07dceca8d9013a21545689319b7c024cd07"-    , "3c258ebf2203fca3b322ad1b016e21c7f5c148425f81e4fb0a0e462dce9dfa569c37a006527768297a5b68461b08912642a341b88c85597e30e7561206886098c4e2d861f11513f0ffdbbc78d3a2dd60c105abbb33c5e05ae27081b690fb8b3610917aa9bf1a4ad74481b5ff8334f14e5ad6a6a1eb2259476078076fb7e3a992"-    ],-    -- COUNT = 10-    [ "36a5267eeeb5a1a7d46de0f8f9281f73cd9611f01198fdaa78c5315205e5a177"-    , "b66b5337970df36219321badacc624eb"-    , "c2a7b164949da102bece44a423197682ff97627d1fe9654266b8527f64e5b386"-    , "a977e2d8637b019c74063d163bb25387dc56f4eb40e502cefc5ae6ad26a6abdc"-    , "c5c9819557b1e7d8a86fa8c60be42993edc3ef539c13d9a51fb64b0de06e145e"-    , "b471711a4fc7ab7247e65d2c2fe49a50169187187b7978cd2fdb0f8318be3ec55fc68ed4577ad9b42cbb57100b5d35ac86c244c4c93a5b28c1a11c2dfe905d608ec7804dec5bb15cf8d79695534d5e13a6a7e18a887ec9cf184da0cbbc6267f3a952a769403bafcdbb559401be0d8b3300ea7258b4026fc892175efd55ba1a67"-    ],-    -- COUNT = 11-    [ "a76b0366df89e4073a6b6b9c04da1d6817ce26f1c4825cad4097bdf4d7b9445e"-    , "773d3cc3290176773847869be528d1a4"-    , "1bfd3bcfb9287a5ad055d1b2b8615fa81c94ac24bc1c219a0f8de58789e0404a"-    , "edd879fa56f21d93029da875b683ce50f6fdc4c0da41da051d000eed2afefefa"-    , "f528ffd29160039260133ed9654589ce60e39e7f667c34f82cda65ddcf5fff14"-    , "39d1ff8848e74dd2cdc6b818ad69823878062116fdf1679942f892c7e191be1c4b6ea268ecdff001b22af0d510f30c2c25b90fc34927f46e3f45d36b0e1848b3a5d54c36c7c65ee7287d325dfbb51b56a438feb6650ce13df88bf06b87ac4a35d2a199ea888629fb0d83f82f0ea160dc79ed220d8ef195b9e80c542f60c2d320"-    ],-    -- COUNT = 12-    [ "46571e1df43e5e141235e2a9ec85bb0faf1dc0566031e14d41a2fbd0315653ec"-    , "b60ef6a3347967519aabeaf748e4e991"-    , "759fd8593e3688b23c4a003b655311770d670789878570eb3b155a8e6c2d8c45"-    , "033128460b449e1accb0e9c54508759ddc2538bc64b51e6277553f0c60a02723"-    , "a5e4a717240bdeac18a0c0e231a11dc04a47d7550f342fa9a7a5ff334eb9327d"-    , "9d222df1d530ea7f8f2297a0c79d637da570b48042ecddded75956bba0f0e70b271ffa3c9a53bada6ee1b8a4203c22bfde82a5e2eb1b150f54c6483458569422c1a34a8997d42cc09750167a78bf52a0bd158397af9f83caabe689185c099bf0a9a4853dd3cf8b8e89efebb6a27dba873e65e9927741b22968f2875789b44e01"-    ],-    -- COUNT = 13-    [ "d63980e63bbe4ac08d2ac5646bf085b82c75995e3fdfc23bb9cc734cd85ca7d2"-    , "d33ed1dcae13fb634ba08272d6697590"-    , "acd0da070072a5340c4f5f4395568e1a36374e074196ae87f3692ee40487e1df"-    , "f567677b5e12e26f3544be3da9314c88fc475bf84804a89a51f12b191392c02b"-    , "c01cc7873e93c86e2bfb8fc984cfc2eab5cc58eeef018fedb5cba5aedd386156"-    , "b133446f633bcb40724bbf9fa187c39a44b9c094a0a0d40e98977e5466dc2c9adf62a5f4551eeb6406a14658de8a0ed7487c3bf6277e811101284a941745ce16176acc875f1435e14161772fa84609e8123c53dd03cbb868030835c0d11d8d6aa04a1b6f908248b028997737f54735ec4ed7a81fc868199ffb61a779d9340334"-    ],-    -- COUNT = 14-    [ "3d99f9b7ac3a2fbe9cf15d960bf41f5588fc4db1e0d2a5c9c0fe9059f03593fb"-    , "411f504bb63a9b3afa7ffa1357bb48be"-    , "0bb5ebd55981a25ba69164da49fa92f2871fd3fc65eb30d0f0d0b8d798a4f8f2"-    , "288e948a551284eb3cb23e26299955c2fb8f063c132a92683c1615ecaed80f30"-    , "d975b22f79e34acf5db25a2a167ef60a10682dd9964e15533d75f7fa9efc5dcb"-    , "ee8d707eea9bc7080d58768c8c64a991606bb808600cafab834db8bc884f866941b4a7eb8d0334d876c0f1151bccc7ce8970593dad0c1809075ce6dbca54c4d4667227331eeac97f83ccb76901762f153c5e8562a8ccf12c8a1f2f480ec6f1975ac097a49770219107d4edea54fb5ee23a8403874929d073d7ef0526a647011a"-    ]-    ]--{- Reseed test vectors -}--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--r1 :: [TestVector]-r1 =-    [-    -- COUNT = 0-    [ "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"-    , "0e66f71edc43e42a45ad3c6fc6cdc4df"-    , ""-    , "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"-    , ""-    , ""-    , ""-    , "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"-    ],-    -- COUNT = 1-    [ "aadcf337788bb8ac01976640726bc51635d417777fe6939eded9ccc8a378c76a"-    , "9ccc9d80c89ac55a8cfe0f99942f5a4d"-    , ""-    , "03a57792547e0c98ea1776e4ba80c007346296a56a270a35fd9ea2845c7e81e2"-    , ""-    , ""-    , ""-    , "17d09f40a43771f4a2f0db327df637dea972bfff30c98ebc8842dc7a9e3d681c61902f71bffaf5093607fbfba9674a70d048e562ee88f027f630a78522ec6f706bb44ae130e05c8d7eac668bf6980d99b4c0242946452399cb032cc6f9fd96284709bd2fa565b9eb9f2004be6c9ea9ff9128c3f93b60dc30c5fc8587a10de68c"-    ],-    -- COUNT = 2-    [ "62cda441dd802c7652c00b99cac3652a64fc75388dc9adcf763530ac31df9214"-    , "5fdc897a0c1c482204ef07e0805c014b"-    , ""-    , "bd9bbf717467bf4b5db2aa344dd0d90997c8201b2265f4451270128f5ac05a1a"-    , ""-    , ""-    , ""-    , "7e41f9647a5e6750eb8acf13a02f23f3be77611e51992cedb6602c314531aff2a6e4c557da0777d4e85faefcb143f1a92e0dbac8de8b885ced62a124f0b10620f1409ae87e228994b830eca638ccdceedd3fcd07d024b646704f44d5d9c4c3a7b705f37104b45b9cfc2d933ae43c12f53e3e6f798c51be5f640115d45cf919a4"-    ],-    -- COUNT = 3-    [ "6bdc6ca8eef0e3533abd02580ebbc8a92f382c5b1c8e3eaa12566ecfb90389a3"-    , "8f8481cc7735827477e0e4acb7f4a0fa"-    , ""-    , "72eca6f1560720e6bd1ff0152c12eeff1f959462fd62c72b7dde96abcb7f79fb"-    , ""-    , ""-    , ""-    , "d5a2e2f254b5ae65590d4fd1ff5c758e425be4bacdeede7989669f0a22d34274fdfc2bf87135e30abdae2691629c2f6f425bd4e119904d4785ecd9328f15259563e5a71f915ec0c02b66655471067b01016fdf934a47b017e07c21332641400bbe5719050dba22c020b9b2d2cdb933dbc70f76fec4b1d83980fd1a13c4565836"-    ],-    -- COUNT = 4-    [ "096ef37294d369face1add3eb8b425895e921626495705c5a03ee566b34158ec"-    , "6e2e0825534d2989715cc85956e0148d"-    , ""-    , "1b4f7125f472c253837fa787d5acf0382a3b89c3f41c211d263052402dcc62c5"-    , ""-    , ""-    , ""-    , "4541f24f759b5f2ac2b57b51125077cc740b3859a719a9bab1196e6c0ca2bd057af9d3892386a1813fc8875d8d364f15e7fd69d1cc6659470415278164df656295ba9cfcee79f6cbe26ee136e6b45ec224ad379c6079b10a2e0cb5f7f785ef0ab7a7c3fcd9cb6506054d20e2f3ec610cbba9b045a248af56e4f6d3f0c8d96a23"-    ],-    -- COUNT = 5-    [ "a7dccdd431ae5726b83585b54eae4108f7b7a25c70187c0acbb94c96cc277aa8"-    , "94c8f4b8e195a47356a89a50d1389ab5"-    , ""-    , "51733eee2e922f4055e53939e222e71fae730eb037443db2c7679708abb86a65"-    , ""-    , ""-    , ""-    , "99ba2691a622afecc9472418e6a8f9f1cdc1e3583c3bc7a2a650a1ab79dcbccbd656636c573179276e782569420c97438c06be898867f628b1c01eb570263d2c0f09c7aab536f6fba7df6aad19e05c236b645674667c03d1b6a04d7fc11177fe78933b309679f5bf26a4632b9a13e314c4bf4532428d3d95c689002b6dc1fbb1"-    ],-    -- COUNT = 6-    [ "c286425ecf543a49bcc9196b0db1a80bc54e4948adba6f41712a350a02891fa6"-    , "957a659a4ec2e0b7ad185483c220fd61"-    , ""-    , "08c2129813eea0776fba72788fdf2718759cc3c4207fa20a5fe23ac6e32cc28e"-    , ""-    , ""-    , ""-    , "8e1020a4fd84c99e0fc7e3f7ce48de5ed9ec9a5c2ccd624dbe6f30e2f688a31dc55957630357a5d48ca2a456241a28bfb16d8bb000877697a7ce24d9ad4d22b0c15117996f1f270b94f46d7a9bdfa7608fa1dd849177a9b8049e51b6b7a2742623854a1fddb5efc447eed1ea1aed6f02b4b2754ecf71ea0509da2e54f524a7e7"-    ],-    -- COUNT = 7-    [ "02818bd7c1ec456ace55beeba99f646a6d3aa0ea78356ea726b763ff0dd2d656"-    , "c482687d508c9b5c2a75f7ce390014e8"-    , ""-    , "cf319bfa63980e3cb997fd28771bb5614e3acb1149ba45c133ffbbab17433193"-    , ""-    , ""-    , ""-    , "19a231ff26c1865ce75d7a7185c30dd0b333126433d0c8cbf1be0d2b384d4eb3a8aff03540fbfa5f5496521a4e4a64071b44c78bd0b7e68fac9e5695c5c13fd3b9dbe7f7739781a4c8f0b980f1b17d99bce17ceb52b56866ae02456ffef83399c8cf7826f3c45c8a19315890919d20f40fc4e18d07e9c8ccd16c3327b5988f71"-    ],-    -- COUNT = 8-    [ "77a5c86d99be7bc2502870f4025f9f7563e9174ec67c5f481f21fcf2b41cae4b"-    , "ed044ad72ee822506a6d0b1211502967"-    , ""-    , "778100749f01a4d35c3b4a958aafe296877e0acafd089f50bc7797a42a33ab71"-    , ""-    , ""-    , ""-    , "831a4da566f46289904893ef1cc1cd4ad19ee48f3857e2b69e936d10afbdc29822e85d02663d346ef3e09a848b1d9cc04f4c4c6e3b3b0e56a034e2334d34ca08f8097be307ba41d020bc94f8c1937fe85644eeb5592c2b5a2138f7ded9a5b44b200c8b5beb27597c790f94d660eb61e8248391edc3ae2d77656cbe8354275b13"-    ],-    -- COUNT = 9-    [ "0ea458cff8bfd1dd8b1addcba9c01317d53039e533104e32f96e7d342e6c7b9b"-    , "935a4b66fc74c2a48757a99c399e64e3"-    , ""-    , "6c5f3708e7b714c4ed139b4fa9e8c763af01773484005109a85e33653bb0ce98"-    , ""-    , ""-    , ""-    , "373a37af84fddec13645a9768d6a785ae5a2589d64cd9b37980dde2541499210c4f408335de1d585349064f3f53a2b4c5ec6dc2a09591f99ad9fad528ac83474164b45497bf167f81e66fa08463ffea917f6891e48f149fafc20622bb1172f34886feb45c26fd446a4a4e2891b4bc594186896141aaaeeb301b49e7c1a26fec7"-    ],-    -- COUNT = 10-    [ "bfb68be4ce1756d25bdfad5e0c2f8bec29360901cc4da51d423d1591cc57e1ba"-    , "98afe4bd194c143e099680c504cceaab"-    , ""-    , "b97caf210e82498c3408790d41c320dd4a72007778389b44b7bc3c1c4b8c53f8"-    , ""-    , ""-    , ""-    , "409e0aa949fb3b38231bf8732e7959e943a338ea399026b744df15cbfeff8d71b3da023dcce059a88cf0d4b7475f628e4764c8bef13c70cfbbbb6da2a18aabcad919db09d04fc59765edb165147c88dd473a0f3c5ee19237ca955697e001ba654c5ee0bd26761b49333154426bc63286298a8be634fe0d72cfdeef0f3fc48eca"-    ],-    -- COUNT = 11-    [ "4f6880a64610004463031d67d7924fa446c39138d4d41007e8df3d65691a9367"-    , "6b33b2c13600f4b1df6ca3d1960e8dd4"-    , ""-    , "57b87b8c8f48312b5333d43b367730c0a5ad4725a16778fcb53fe136d136cbfd"-    , ""-    , ""-    , ""-    , "73d0f324ed186e2ad06bd1800e262bdbda79ba54e626761bd60f74f43e3bb62958ec1e2f1d940af163e1cadc124e7ebaba2f72e67efd746c7f6d0cad53ef03d859d93cff778a32ee5be172fe7fdbdc232ded360d704a6fa0f70bebe942e56478345492f49dc5c6fc346b88a58947ad250e688e8c626fe1efe7624620e571976e"-    ],-    -- COUNT = 12-    [ "aae352e111843219cae8f70e7b8f6eb9bb53d246cbec1e4f07d42757143295b4"-    , "b84485dccd1bf93210e322eafcbebcd9"-    , ""-    , "f9237f00d744d8fbff21b9d0043c258e8731817e6a5fb7b4bf5011680e5bc642"-    , ""-    , ""-    , ""-    , "cfb28b93522c7d61d8d3ce3f080e435e4c83c7e13a9dab788db8fef0407267a14fbc9324e090e24df5491fedfa81116869983938d4d4d7324a310c3af33a6f7938f602c5e4e63f1771cdaabdab0782b5affb54eb53047c109a9606739dd0065bd21eca33132986554878354f5f9f852e674dd690163b0ff74c7a25e6bae8ce39"-    ],-    -- COUNT = 13-    [ "589e79e339b7d2a1b879f0b0e1a7d1ad2474eaa8025b070f1ffa877b7124d4ff"-    , "0961ed64dbd62065d96e75de6d2ff9d6"-    , ""-    , "e928388d3af48c2968527a4d2f9c2626fbc3f3f5a5d84e0583ab6f78e7f8b081"-    , ""-    , ""-    , ""-    , "fce6ced1ecf474d181ab331f79c3d2cc8a768ec2818de5b3fc7cf418322716d6a6853733561a497c0c25cb288d2c9fcfbca891bafd5a834c85f3603f402acf1a7b1ea92db847ed5c252a862ad4ab5e259715f1fc81da67f5230bf8be50ee8069758095f7d0e559e03f2c6072290e61794458437609e473eb66580cddaad19b71"-    ],-    -- COUNT = 14-    [ "714277d408ad87fde317f0a94732fce62f1352bdc90936673b4f1daa0925aa26"-    , "d16582a99f23010b4248b88d86485419"-    , ""-    , "bd9fc7cb2fd5063b2c3c0c4f346ad2e3879371a9c805e59b9f2cd2cc2a40894f"-    , ""-    , ""-    , ""-    , "62ef7a431288252e0d736c1d4e36cc9ac37107dcd0d0e971a22444a4adae73a41eff0b11c8625e118dbc9226142fd0a6aa10ac9b190919bda44e7248d6c88874612abd77fb3716ea515a2d563237c446e2a282e7c3b0a3aef27d3427cc7d0a7d38714659c3401dbc91d3595159318ebca01ae7d7fd1c89f6ad6b604173b0c744"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 0]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--r2 :: [TestVector]-r2 =-    [-    -- COUNT = 0-    [ "05ac9fc4c62a02e3f90840da5616218c6de5743d66b8e0fbf833759c5928b53d"-    , "2b89a17904922ed8f017a63044848545"-    , ""-    , "2791126b8b52ee1fd9392a0a13e0083bed4186dc649b739607ac70ec8dcecf9b"-    , "43bac13bae715092cf7eb280a2e10a962faf7233c41412f69bc74a35a584e54c"-    , "3f2fed4b68d506ecefa21f3f5bb907beb0f17dbc30f6ffbba5e5861408c53a1e"-    , "529030df50f410985fde068df82b935ec23d839cb4b269414c0ede6cffea5b68"-    , "02ddff5173da2fcffa10215b030d660d61179e61ecc22609b1151a75f1cbcbb4363c3a89299b4b63aca5e581e73c860491010aa35de3337cc6c09ebec8c91a6287586f3a74d9694b462d2720ea2e11bbd02af33adefb4a16e6b370fa0effd57d607547bdcfbb7831f54de7073ad2a7da987a0016a82fa958779a168674b56524"-    ],-    -- COUNT = 1-    [ "1bea3296f24e9242b96ed00648ac6255007c91f7c1a5088b2482c28c834942bf"-    , "71073136a5cc1eb5b5fa09e1790a0bed"-    , ""-    , "d714329f3fbea1df9d0b0b0d88dfe3774beb63d011935923d048e521b710dc6f"-    , "4ef872fd211a426ea1085ab39eb220cc698fdfeabe49b8835d620ab7885de7a4"-    , "d74d1669e89875852d9ccbf11c20fe3c13a621ebcb3f7edeea39a2b3379fdcf5"-    , "0c8aa67ca310bd8e58c16aba35880f747266dbf624e88ec8f9ee9be5d08fdeb1"-    , "ce95b98f13adcdf7a32aa34709d6e02f658ae498d2ab01ce920f69e7e42c4be1d005acf0ca6b17891dfafc620dd4cd3894f8492a5c846089b9b452483eb0b91f3649ec0b6f98d1aaabc2e42cd39c2b25081b85ab50cb723007a0fd83550f32c210b7c4150b5a6bb3b0c9e3c971a09d43acb48e410a77f824b957092aa8ef98bc"-    ],-    -- COUNT = 2-    [ "a7ea449b49db48601fc3a3d5d77081fab092b8d420ed1b266f704f94352dd726"-    , "d11a159b60af8d20a0e37d27e6c74aa3"-    , ""-    , "50916ab47e8cb5dc843f9fba80639103711f86be8e3aa94f8a64a3fe0e6e5b35"-    , "e2bb6768120555e7b9e0d573537a82f8f32f54560e1050b6abb1588fb3441e66"-    , "a50cec9d1ecddb2c163d24019e81c31a2b350ccd3ad8181fd31bb8d1f64fa50e"-    , "591dbbd48b51abced67f9c6269cf0133cd3dcbb5cfafcb6ef758569c555a5773"-    , "0a464abcc8685158372d544635b953fcb1d3821c30aaa93982f9b788935f00f88115aad61d5cee003b3d1cb50f3e961a501e2dd0fc7e1724778b184a4bdf9f64e110dda7446e5544a30bd49a400ea1a5411800e1edfeea349323618afc5dc5782dc4b71d2da4d6a4785f8dd346feb9c8740ffd26bf644e3e4323ff24c30b9f10"-    ],-    -- COUNT = 3-    [ "14683ec508a29d7812e0f04a3e9d87897000dc07b4fbcfda58eb7cdabc492e58"-    , "b2243e744eb980b3ece25ce76383fd46"-    , ""-    , "18590e0ef4ee2bdae462f76d9324b3002559f74c370cfccf96a571d6955703a7"-    , "9ea3ccca1e8d791d22fcda621fc4d51b882df32d94ea8f20ee449313e6909b78"-    , "16366a578b5ea4d0cb547790ef5b4fd45d7cd845bc8a7c45e99419c8737debb4"-    , "a68caa29a53f1ba857e484d095805dc319fe6963e4c4daaf355f722eba746b92"-    , "c4e7532ee816789c2d3da9ff9f4b37139a8515dbf8f9e1d0bf00c12addd79ebbd76236f75f2aa705a09f7955038ebff0d566911c5ea13214e2c2eeb46d23ad86a33b60f7b9448d63eec3e1d59f48b39552857447dc5d7944667a230e3dbfa30ca322f6eacaf7536a286706a627c5083c32de0658b9073857c30fb1d86eb8ad1b"-    ],-    -- COUNT = 4-    [ "fa261fb230e2822458532ca2d5c39758750e6819a6fcebef10579ba995096959"-    , "564e1c9fbcb12878df2bd49202cbf821"-    , ""-    , "bf7de29e99e7f0e1b9f96f3b1902fb4049c8c6234d20de8316ebe66d97725457"-    , "8b7326621f6afbd44a726de48d03bcc5331f7306026c229ea9523497fbeaa88d"-    , "33b00b31623d6160c4c6740363a96481be14b19bc47be95641227284c366922a"-    , "2d812c8203575790ad6b6f2ed91a49d57460de779a3e881bef3be12e8766dc91"-    , "5574e0b4efc17e8ce136e592beabfe32551072bddd740929e698467b40b3991f028a22c760f7034853cc53007e3793e3c4a600d9e9d94528f8dc09aeba86146cdde2b7f71255ae0efc529b49be2205979dba6525bfe155e8819e8e2aeeaa285704242da90b4c4535101cc47d94b0e388a1b2e63ad0cbe158b9e1bbae9cc0007c"-    ],-    -- COUNT = 5-    [ "61f1471ced56aa04c57e1b512307d4cb92497d9592d7e9e35356e99d585cab1b"-    , "84714e960c403a4fac06b2828cc564d9"-    , ""-    , "7bf97db3c102edc81596d4757045fe6bdc008f35792fc6290b77d889c09c33a8"-    , "5b8bdc41f76d98cfa71ed976ea3994706375c8841adb8b6b3b6418e3132e8832"-    , "94c8a8fdf38a6ccb8571c89420d899adab169214bb0dfcd43a04622e289935b2"-    , "8a4b46e0a7a55907365f82d4ab9376509bd44728cab8cbafb0da901012ad8dcd"-    , "933eb159a6af7455b60e40586c064f05f1970f564281b1ebc4662701ac1f299e4eb908c4afcb2e065191281ab576f684aefedd6904bad04d96bd93c0516c62a496c3073a0cda0676a11cc08866b0cc74f62cb9d3db48673b2c3fbeada69f922b4b795ccba22df12ef7125909381f7d681f6b9caba02fb913c5437b98c040c576"-    ],-    -- COUNT = 6-    [ "a1d5bb7d70621dee6b668b28c56d5610c2f8ced30284cc3e0e48de331af05062"-    , "88a49e3e54c5ea54c98b95de81bcc807"-    , ""-    , "b4e2426e98f6eed97a6cdf690a89ee109e84c3dca16c883c26fa4ac671638d8d"-    , "5bd1e086ed228cfd8b55c1731fea40c3a63d022599ca2da4bb23118f4821ba62"-    , "b754b53ac226e8ebe47a3d31496ec822de06fca2e7ef5bf1dec6c83d05368ec3"-    , "fa7e76b2805d90b3d89fff545010d84f67aa3a2c9eb2ba232e75f4d53267dac3"-    , "df6b2460688fa537df3ddfe5575fca5eb8abad56cbc4e5a618a2b4a7daf6e215c3a497974c502f9d0ec35de3fc2ea5d4f10de9b2aee66dcc7e7ae6357983095959b817f0383e3030771bd2ed97406acf78a1a4a5f30fa0992289c9202e69e3eb1eabe227c11409ff430f6dfca1a923a8b17bc4b87e908007f5e9759c41482b01"-    ],-    -- COUNT = 7-    [ "68f21d14525d56233c7e263482d344c388a840103a77fb20ac60ce463cabdc79"-    , "59fa80ae570f3e0c60ac7e2578cec3cb"-    , ""-    , "7584b4166530442f06e241dd904f562167e2fdae3247ab853a4a9d4884a5fa46"-    , "f6a5482f139045c5389c9246d772c782c4ebf79c3a84b5cf779f458a69a52914"-    , "9d37b1ce99f8079993ddf0bd54bab218016685b22655a678ce4300105f3a45b7"-    , "4c97c67026ff43c2ee730e7b2ce8cce4794fd0588deb16185fa6792ddd0d46de"-    , "e5f8874be0a8345aabf2f829a7c06bb40e60869508c2bdef071d73692c0265f6a5bf9ca6cf47d75cbd9df88b9cb236cdfce37d2fd4913f177dbd41887dae116edfbdad4fd6e4c1a51aad9f9d6afe7fcafced45a4913d742a7ec00fd6170d63a68f986d8c2357765e4d38835d3fea301afab43a50bd9edd2dec6a979732b25292"-    ],-    -- COUNT = 8-    [ "7988146cbf9598d74cf88dc314af6b25c3f7de96ae9892fb0756318cea01987e"-    , "280bc1ae9bfdf8a73c2df07b82a32c9c"-    , ""-    , "2bbc607085232e5e12ccf7c0c19a5dc80e45eb4b3d4a147fe941fa6c13333474"-    , "f3f5c1bb5da59252861753c4980c23f72be1732f899fdea7183b5c024c858a12"-    , "44d0cfc4f56ab38fa465a659151b3461b65b2462d1ad6b3463b5cf96ad9dc577"-    , "34fb9a3cdacc834ff6241474c4f6e73ed6f5d9ea0337ab2b7468f01ad8a26e93"-    , "4caec9e760c4d468e47613fe50de4a366ae20ba76793744a4e14433ea4de79dc188601eb86c803b094641ab2337b99d459d37decc7d27473057be45ba848868ee0fb5f1cf303d2fcd0b3e0c36f65a65f81b3fee8778a1f22302e25dfe34e6d587fa8864e621121880f7cd55f350531c4ce0530099eec2d0059706dcd657708d9"-    ],-    -- COUNT = 9-    [ "1c974c953fa2a057c9fc9409a6843f6f839aa544bca4fa11e48afd77931d4656"-    , "ed7c08285464af7a5dbdc10b944a1270"-    , ""-    , "78146ad135acb836360d36afc50653dcc36c21662da2a6f6ae05222e75f34000"-    , "263c4984c238ded333c86472866353817379502157172cfa51371d82b1efd7b5"-    , "79b591529f9a26a0d7c8f8fd64e354b0c134ef1f757e43f9463b3dbb7a3da1ab"-    , "7d8f7204b0b5401ddce9e88dcf5facb9a44660a9f5f1c862748e7269c29f7964"-    , "72e2ca257b9edaf59b50e05a144f56fb517832fb9ad3489b1e664e3d5412cbf6b2883e891703b2e73aff9ab56da1009fcdef010ab4cdab996795c8f7c47fb1192bb160353997ad39d7d5fd0e2efc9103a7c3f158246afd53fe53ca6782f809698ef5f1f0d85536780a3fd6a8bafa475891c09213088bd1a3dc169257c34a517a"-    ],-    -- COUNT = 10-    [ "56216d71984a77154569122c777ce57e1d101a6025b28163a25971d39c1c5d0f"-    , "5cd148ba7e54f4975ac8e3e0f9b5d06a"-    , ""-    , "3580f8ca974626c77259c6e37383cb8150b4d0ab0b30e377bed0dc9d1ff1a1bf"-    , "15633e3a62b21594d49d3d26c4c3509f96011d4dbb9d48bbbea1b61c453f6abe"-    , "6068eaca85c14165b101bb3e8c387c41d3f298918c7f3da2a28786ab0738a6fc"-    , "e34f92d2b6aeeeea4ff49bfe7e4b1f462eabb853f0e86fbae0e8b3d51409ce49"-    , "587fdb856abc19ede9078797ecb44099e07aadcd83acdcb2b090601d653f4a14c68ab2ebdda63578c5633a825bae4c0c818f89aac58d30fd7b0b5d459a0f3d86fcad78f4bb14dfff08ad81e4ea9f487cb426e91d6e80dfed436ba38fce8d6f21ca2151c92dd5c323b077d6139c66395558f0537026c4a028affa271ef4e7ea23"-    ],-    -- COUNT = 11-    [ "83eb48bedc1e9294866ab8e5322ef83f6f271f8188e8fdabe5817788bd31570d"-    , "d6ed90bc692237f132441ede857a6629"-    , ""-    , "a4e5e127f992bd5ca79ee56bb8a9bccf74c21814bfaf97ffd052211e802e12e4"-    , "84136e403d9ed7f4515c188213abcfaca35715fa55de6d734aec63c4606a68f1"-    , "fe9d8ef26e2d2e94b99943148392b2b33a581b4b97a8d7a0ecd41660a61dd10b"-    , "594dad642183ce2cdc9494d6bcb358e0e7b767c5a0fa33e456971b8754a9abd5"-    , "86715d43ba95fbbca9b7193ea977a820f4b61ba1b7e3b8d161b6c51b09dfd5040d94c04338b14d97ed25af577186b36ae7251a486c8a2d24a35e84a95c89d669d49e307b4a368b72164135ac54d020a970a180dfbed135d2c86f01270846d5301bd73db2c431a8aa10a0a3d03d146e5fafb9a2aa0b4efc80edab06ff3b532236"-    ],-    -- COUNT = 12-    [ "ba2c94203dab2e6499d8c50dca7b5c34a6b4764834f9816631aa21b9f9c37361"-    , "67db133bdefb25e395085bceee5a0afc"-    , ""-    , "fa8984d16d35302cda35a3a355ab9242ec96fec0652d39282d4a0abf0a80df87"-    , "b6fed10255a3fea6772ae1ae6d9f6cbb9bfaa34804e58a5b786f9bc60b348ccd"-    , "445e072244edc716d3528f0e0a20ff0cd8f819c0d031736c8da122748f24d6c6"-    , "1f856e403c4fa035bac9aa81a20e347c7d8b213aab699d69d9d6186a06ac45c1"-    , "79f33fc36b3b47d9ac805bdbbe699909a8d0beb689a8b2723c291bd5bf7f3ce61343d4722a14e4add36312dbb0594910c8828aff1abc159915d498106f9ffb31147478d8c9ef75d1536ba5036506b313f6e85033f8f6fea2a4de817c867a59378c53c70a2f108275daedd415c05b61c4fd5d48c54be9adb9dea6c40a2ec99ee0"-    ],-    -- COUNT = 13-    [ "0db4c51492db4fe973b4bb1c52a1e873b58fc6bb37a3a4bfc252b03b994495d1"-    , "a2a3900f169bba3f78a42526c700de62"-    , ""-    , "29d5aab356876447e3a20d81c7e3fc6975e2b984180a91493044442999e1ca3a"-    , "40b34183b4e72cdff5952b317b3d45943d0fdcfa0527f3563055f7c73ae8f892"-    , "dc94220c99ffb595c7c4d6de8de5a6bb4b38847169e24a557ef6d879ad84149d"-    , "b2376626fd2f5218b3ed4a5609b43aa24d371cd2176ea017c2b99cf868060021"-    , "f0bd6bc4c506d9427a09352d9c1970b146360732841a6323f4cb602c87dedfb5ff7e6964b9144933af3c5c83017ccd6a94bdca467a504564aaa7b452591a16ff6a1e7e94ddc98f9a58016cdcb8caaed6c80671ba48cc81a832d341093dda1d4e5001ec6bf66348b21e3692a13df92538ad572bb2023822072fc95f9590293ffc"-    ],-    --  COUNT = 14-    [ "593845f0adfeffa7c169f8a610147ae8a08c0072fc0c14c3977d3de0d00b55af"-    , "9e0eb2507342ee01c02beadee7d077bd"-    , ""-    , "aefe591697eab678c52e20013aa424b95cfd217b259757fbe17335563f5b5706"-    , "cbb5be0ef9bf0555ee58955c4d971fb9baa6d6070c3f7244a4eb88b48f0793bf"-    , "6dd878394abdc0402146ba07005327c55f4d821bfebca08d04e66824e3760ab4"-    , "ba86a691d6cbf452b1e2fd1dfb5d31ef9ea5b8be92c4988dc5f560733b371f69"-    , "00735cbfafac5df82e5cb28fc619b01e2ba9571dc0023d26f09c37fb37d0e809066165a97e532bf86fa7d148078e865fe1a09e27a6889be1533b459cd9cd229494b5cf4d2abf28c38180278d47281f13820276ec85effb8d45284eb9eef5d179ab4880023ab2bd08ee3f766f990286bf32430c042f5521bbfd0c7ee09e2254d7"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 0]-    [ReturnedBitsLen = 1024]--}--r3 :: [TestVector]-r3 =-    [-    -- COUNT = 0-    [ "fa0ee1fe39c7c390aa94159d0de97564342b591777f3e5f6a4ba2aea342ec840"-    , "dd0820655cb2ffdb0da9e9310a67c9e5"-    , "f2e58fe60a3afc59dad37595415ffd318ccf69d67780f6fa0797dc9aa43e144c"-    , "e0629b6d7975ddfa96a399648740e60f1f9557dc58b3d7415f9ba9d4dbb501f6"-    , ""-    , ""-    , ""-    , "f92d4cf99a535b20222a52a68db04c5af6f5ffc7b66a473a37a256bd8d298f9b4aa4af7e8d181e02367903f93bdb744c6c2f3f3472626b40ce9bd6a70e7b8f93992a16a76fab6b5f162568e08ee6c3e804aefd952ddd3acb791c50f2ad69e9a04028a06a9c01d3a62aca2aaf6efe69ed97a016213a2dd642b4886764072d9cbe"-    ],-    -- COUNT = 1-    [ "cff72f345115376a57f4db8a5c9f64053e7379171a5a1e81e82aad3448d17d44"-    , "d1e971ec795d098b3dae14ffcbeecfd9"-    , "6ec0c798c240f22740cad7e27b41f5e42dccaf66def3b7f341c4d827294f83c9"-    , "45ec80f0c00cad0ff0b7616d2a930af3f5cf23cd61be7fbf7c65be0031e93e38"-    , ""-    , ""-    , ""-    , "17a7901e2550de088f472518d377cc4cc6979f4a64f4975c74344215e4807a1234eefef99f64cb8abc3fb86209f6fc7ddd03e94f83746c5abe5360cdde4f2525ccf7167e6f0befae05b38fd6089a2ab83719874ce8f670480d5f3ed9bf40538a15aaad112db1618a58b10687b68875f00f139a72bdf043f736e4a320c06efd2c"-    ],-    -- COUNT = 2-    [ "b7099b06fc7a8a74c58219729db6b0f780d7b4fa307bc3d3f9f22bfb763596a3"-    , "b8772059a135a6b61da72f375411de26"-    , "2ac1bfb24e0b8c6ac2803e89261822b7f72a0320df2b199171b79bcbdb40b719"-    , "9aec4f56ec5e96fbd96048b9a63ac8d047aedbbeea7712e241133b1a357ecfc4"-    , ""-    , ""-    , ""-    , "0e1f2bfef778f5e5be671ecb4971624ec784ed2732abc4fbb98a8b482fb68737df91fd15acfad2951403ac77c5ca3edffc1e03398ae6cf6ac24a91678db5c7290abc3fa001aa02d50399326f85d2b8942199a1575f6746364740a5910552c639804d7530c0d41339345a58ff0080eccf1711895192a3817a8dc3f00f28cc10cc"-    ],-    -- COUNT = 3-    [ "7ba02a734c8744b15ef8b4074fe639b32e4431762ab5b7cd4d5df675ea90672b"-    , "8a424f32108607c8f1f45d97f500ee12"-    , "3ad627433f465187c48141e30c2678106091e7a680229a534b851b8d46feb957"-    , "d8f02b59b6a3dd276bc69cba68efcf11ab83ead1397afd9841786bd1bb5da97a"-    , ""-    , ""-    , ""-    , "1fb91186ba4b4459d994b4b9f4ca252c7be6294d6cdb5fe56f8ff784d4b190a1c6456e0a41223bbbdf83ed8e7cfbfa765d9d8bc7ea5f4d79ea7eccb4928081a21de4cca36620d6267f55d9a352b76fc0a57375884112c31f65ff28e76d315698c29e6c4c05cb58b0a07ae66143b4abc78b9d25c78b4121e1e45bef1a6c1793e2"-    ],-    -- COUNT = 4-    [ "9a8865dfe053ae77cb6a9365b88f34eec17ea5cbfb0b1f04d1459e7fa9c4f3cb"-    , "180c0a74da3ec464df11fac172d1c632"-    , "336372ec82d0d68befad83691966ef6ffc65105388eb2d6eed826c2285037c77"-    , "75b95108eff1fabe83613e1c4de575e72a5cdc4bb9311dd006f971a052386692"-    , ""-    , ""-    , ""-    , "3c683f6d4f8f5a4018d01633dfee74266aaa68ed6fc649e81b64dfdf5f75e75d5c058d66cf5fd01a4f143a6ff695517a4a43bd3adfd1fb2c28ba9a41063140bedbffdb4d21b1ace1550d59209ec61f1e2dbacb2a9116a79cb1410bf2deca5218080aacd9c68e1d6557721a8913e23f617e30f2e594f61267d5ed81464ee730b2"-    ],-    -- COUNT = 5-    [ "22c1af2f2a4c885f06988567da9fc90f34f80f6dd5101c281beef497a6a1b2f8"-    , "3fafdecf79a4174801f133131629037b"-    , "80327dac486111b8a8b2c8e8381fb2d713a67695c2e660b2b0d4af696cc3e1de"-    , "f95a0e4bd24f0e2e9e444f511b7632868ead0d5bb3846771264e03f8ab8ed074"-    , ""-    , ""-    , ""-    , "77a7fea2f35a188f6d1bfdd49b569d8c45e2dd431d35a18c6f432c724f1e33ae92cb89a9cf91519e50705a53199f5b572dc85c1aef8f28fb52dc7986228f66954d54eda84a86962cf25cf765bd9949876349291b1aae5f88fcf4b376912d205add4f53b2770c657946c0d824281f441509153f48356d9d43f8a927e0693db8fc"-    ],-    -- COUNT = 6-    [ "d0840e3a8d629d5b883d33e053a341b21c674e67e1999f068c497ecfaabfd6f6"-    , "071de7244ecb2fdf7ab27f2d84aa7b7a"-    , "90d609527fad96ffe64ab153860346f3d237c8940555ae17b47842d82d3b0943"-    , "1dd1a8b59856c49a388f594c5f42cc2e4a56b3ccb8a65e7066e44c12f4344d50"-    , ""-    , ""-    , ""-    , "7ab28a9b2d3ae999195553e6550cced4c2daccbe7ec9dcbb0d467fabba185b727fbfd9830242cd098f4db3cf4a85e8bf8e8d5974b62b28550922b32ed5bfc1a522b6605cf93bf8d90bdec1c5b9e59c6fc37a817d437068a87254be1f7c4618ada46fbc3a2efb02e44524e21d91be7534cf05fbfd858304b706d6a91ea1cc6ad5"-    ],-    -- COUNT = 7-    [ "2e2dd56869104492767a59778652831919e1c8b970f84e824ae4116597a0ab7f"-    , "01c42a7e983641de46c82fd09b4f2f76"-    , "bcd9e1508fcc22820a8be07180fea5045367333b569e111b011cd57dc1858765"-    , "7306507cd3ca7eec667e640d270cfbb033063d97520b6b7e38ff3cea0e79d12b"-    , ""-    , ""-    , ""-    , "b915726c7b8c5dc3975f1a334684b973abf6a9495d930088cf5d071548e4fd29a67b55cc561ed6949ad28150a9fb4307c1fa5f783a7ea872e8d7c7e67ff0c2906081ee915737d813c25be5c30b952a36f393e6baa56ab01adc2b4776ad7b5d036a53659877c7a4e5220a897d6c0799af37beeed91173fbe9c613c3b6b9bb28e5"-    ],-    -- COUNT = 8-    [ "d1aab0f16bd47a5ccd67c22e094daa3735eae21aa57f0bcd9e053d9d0d545cb8"-    , "199310dfe1b01265b8c0d2b46d6c7c9f"-    , "625b4b8f4de72ea9cb6f70556322dc2a19d6b2b32de623f557e419a084ba60fd"-    , "f50cabae4e060f3971096b78e550cda2837a26a693d905db2d992d589b268f44"-    , ""-    , ""-    , ""-    , "987e1fdfe004c619cf1e9034576707eccd849400e19c87a1fef5b0179ec51c42a2f8c45d7942d0023a023c89f188b2634362703985695369863322f58619c50a7385a2dc91fc78f94b59f0131dc2b56a0d7c699d427285da1c104b0ad1739da10d8071c23993787045dc21f0070e1e9aa1658fc8e3add73dac7262e80e0aa2ee"-    ],-    -- COUNT = 9-    [ "449480eaa100aff6f48dc6286a5a81b9728b084864f78a9da98f606a00a6a41f"-    , "e53c6c5ac3da9f4726389a03f97bb640"-    , "6b8fedc084d8e28d333aef6db3702b6351f0d24e30908cccb63794282655886b"-    , "73a6d64e1966ae324388dc12c14544e9dc5ae4fcb331e99d350c456ff16f9aa0"-    , ""-    , ""-    , ""-    , "a06912d362da7eb25598857f6d65344c3e23ec3deb80c6e43158845b95eaeca241c0bbbd67ac385e24693444455cc1c2c08c1134d956b8bc93b28be9c2d3322b3e09252979dfb8d39d04c94f81bebda5c73110605a237b561216bda9ee9bdee1cc0c7728bcc8304682334ca944e467a27a85313fa5395a9c790e35defd2edb12"-    ],-    -- COUNT = 10-    [ "9a6174166e97aa4981ddf580bc01c96754b9f0ba042750aabfda1cffe56e8581"-    , "d7512ff6b7db7ce141b2bb01dcd0425e"-    , "ed75288f23275f9422444da5d3b53ccb3c4ac8acfb659a1e9b7655c2db52f879"-    , "6888b9277e57dc57663d402eba8d03cf56a070dc868e6a128b18040002baf690"-    , ""-    , ""-    , ""-    , "03519dfb2ff88cc2b53eecc48ae2a18ddcf91a5d69d5aefcdda8444e6df790a5240e67b2a4de75b4bb8a31f0f8aeb5e785ffb7a1341bb52fe00a05ee66fa2d44ea9956e055f9ffa6647c3bfe851ab364ade71a0d356de710ddafb7622b1da1bc53fd4d3210407289c68d8aeb346bf15806dbe787e781b94f63da3e1f61b5ac60"-    ],-    -- COUNT = 11-    [ "9c6ae1002ee1b0add0be563ce50f899da936e13efa620d08c2688c192514763a"-    , "fde7db5160c73044be73e9d4c1b22d86"-    , "8fdaaeffd64e53f7b4374d902d441209964e12b65d29afec258e65db6de167ca"-    , "bcc28fd58e397f53f494ad8132df82c5d8c4c22ea0b7139bd81eeba65667bb69"-    , ""-    , ""-    , ""-    , "021d938c9b4db780c7d8134aeff1053e5b8843370b8ae9a6749fca7199d809810f1bc8dfa49426470c30c3616f903e35fbacb23420a32f1bee567cc32300f704246ddc0217f236ef52c3ec9e2433ca66f05c25721f7661c43f22c1a125ed5db531bd0836eb435c27eefc7424ce9d845e1d4cc4c503097b4ffca788e674a5cb53"-    ],-    -- COUNT = 12-    [ "fe96a85b69d46b540918927bb609dc57642eeaefd46bb5da2163a0bc60294b58"-    , "22195a410d24db45589448dfe979d3fd"-    , "20f698833a4472fd7b78fb9b0c4eb68604f166a2694c4af48dac2b2376790e1e"-    , "09cb870879d3f734214f6a4bd2e08c62a2a954bebe559416d8c3551aafe71d6a"-    , ""-    , ""-    , ""-    , "d3e96dbe29e1fcb8ed83b19dbfb240e6f41679fbe83853aa71446617e63e5af78cf98b331d15bccb8c673c4e5d5dcec467a1fe26a6cd1696d0c9bc49f78139d051287df7f3ae0dbb4bbf581cb8211931063c3f4612ced53f59d1b4ebb875729139f5d2a7d60642e8f2835eed888b7e3e49c0dffd012cd746abfa3e1c5c2308c6"-    ],-    -- COUNT = 13-    [ "a4fd693ff0a8af24bcec352d3196549fd0da5ee5d99ca58416ca03ce4c50f38e"-    , "8cd67f2bf71d4366ce61396642531ff5"-    , "368969c15a4849d7593be8b162113b9298a535c148ff668a9e8b147fb3af4eba"-    , "83d2be9a0d74e6a42159ae630acebf4e15271ef7f14f3de14752be0e0e822b11"-    , ""-    , ""-    , ""-    , "e9188fc0eaec74b2608e21e3a40be94aaf4ae08eb684de8f8bba2d5fd3b073aa5531c938c0fc628da65725c54b5c68bb91d7d326565e96685e0a4e7b220c50e0caf1628edba5bd755b31894f8cb90afa76e88c5eb9e61b4932444c1397dee3e32241a3fb70a3929e49f6da02eea54812abb3d6b5cee18f03af1e0b4958430ab3"-    ],-    -- COUNT = 14-    [ "254ff5687a6dad3f1d237dc762f58d24ef2e2c084d0a48d26a3dc81e5490cda3"-    , "f2ec392acca491e03ce47b95963a49fc"-    , "f806b9b4a56682c61b55cb6a334caf87ffe135adfea6d0c3fc22b39898fbd078"-    , "b8494b1c1f1752fb6f80d732a89b08115857f7cc96e7dff05ebb822706889917"-    , ""-    , ""-    , ""-    , "0e527e00494d55564f9d9b28e7110f9a61ce36c883b5be2dcb055444164cdddd1a9f2731716f22d6ff476ce413c77abfc0e946871d5481345c2e97b4bfdd12ac03df606fc56bdb99ac7b71a69b5b9160373bbec3e9dde477180af454e7acc6bc58dc0afb4281c0de4354c1bf599054e3800c6d60d892858865b5361f50bfca9b"-    ]-    ]--{--    [SHA-256]-    [PredictionResistance = False]-    [EntropyInputLen = 256]-    [NonceLen = 128]-    [PersonalizationStringLen = 256]-    [AdditionalInputLen = 256]-    [ReturnedBitsLen = 1024]--}--r4 :: [TestVector]-r4 =-    [-    -- COUNT = 0-    [ "cdb0d9117cc6dbc9ef9dcb06a97579841d72dc18b2d46a1cb61e314012bdf416"-    , "d0c0d01d156016d0eb6b7e9c7c3c8da8"-    , "6f0fb9eab3f9ea7ab0a719bfa879bf0aaed683307fda0c6d73ce018b6e34faaa"-    , "8ec6f7d5a8e2e88f43986f70b86e050d07c84b931bcf18e601c5a3eee3064c82"-    , "1ab4ca9014fa98a55938316de8ba5a68c629b0741bdd058c4d70c91cda5099b3"-    , "16e2d0721b58d839a122852abd3bf2c942a31c84d82fca74211871880d7162ff"-    , "53686f042a7b087d5d2eca0d2a96de131f275ed7151189f7ca52deaa78b79fb2"-    , "dda04a2ca7b8147af1548f5d086591ca4fd951a345ce52b3cd49d47e84aa31a183e31fbc42a1ff1d95afec7143c8008c97bc2a9c091df0a763848391f68cb4a366ad89857ac725a53b303ddea767be8dc5f605b1b95f6d24c9f06be65a973a089320b3cc42569dcfd4b92b62a993785b0301b3fc452445656fce22664827b88f"-    ],-    -- COUNT = 1-    [ "3e42348bf76c0559cce9a44704308c85d9c205b676af0ac6ba377a5da12d3244"-    , "9af783973c632a490f03dbb4b4852b1e"-    , "2e51c7a8ac70adc37fc7e40d59a8e5bf8dfd8f7b027c77e6ec648bd0c41a78de"-    , "45718ac567fd2660b91c8f5f1f8f186c58c6284b6968eadc9810b7beeca148a1"-    , "63a107246a2070739aa4bed6746439d8c2ce678a54fc887c5aba29c502da7ba9"-    , "e4576291b1cde51c5044fdc5375624cebf63333c58c7457ca7490da037a9556e"-    , "b5a3fbd57784b15fd875e0b0c5e59ec5f089829fac51620aa998fff003534d6f"-    , "c624d26087ffb8f39836c067ba37217f1977c47172d5dcb7d40193a1cfe20158b774558cbee8eb6f9c62d629e1bcf70a1439e46c5709ba4c94a006ba94994796e10660d6cb1e150a243f7ba5d35c8572fd96f43c08490131797e86d3ed8467b692f92f668631b1d32862c3dc43bfba686fe72fdd947db2792463e920522eb4bc"-    ],-    -- COUNT = 2-    [ "b63fdd83c674699ba473faab9c358434771c5fa0348ca0faf7ebd7cf5891826b"-    , "5fd204e2598d9626edab4158a8cfd95f"-    , "2a5dfad8494306d9d4648a805c4602216a746ae3493492693a50a86d1ba05c64"-    , "adea5ba92f8010bb1a6a4b6fae2caa0b384165adf721253afd635d6021f764af"-    , "07c69d8d2b8aa1454c5c48083dd41477fda6bfcf0385638379933a60ed2e0a77"-    , "a14e902247a3d6493d3fbc8519518b71a660e5502cf7ecfc796cfaa5b4ee4baa"-    , "60e690e4a1eba14aec5187112a383e9991347fab7bac7cb2a40a52579a0d2718"-    , "792b47b6ed221623bb187d63e3f039c6983d94efd5771dc9b4c40bee65924513485a6332baeda6a96f9bb431f592d73462b61d9d914a72b56fa9d87597426fb246424ebcd7abd51b2eefec8f5b839c0b3c34015342ace296b5f2218fa194b50aea1c89663460292c92c45f112ddbf6b9406f6e7ccee9c47ed2d90a27be5dd73e"-    ],-    -- COUNT = 3-    [ "dab85f98eaf0cfba013b97de4d9c264ca6fe120366cb83e8b3113c68b34e39d5"-    , "d05108e1028ae67b4ea63bdc6d75eb88"-    , "09fed3822f6f5e5b9e575d31dc215de1607b0dfc927412618c2d8f79166dbaba"-    , "1794885a64470744198b7d0bc24472ffe8daf3c7eb219df6ddf180e484fe0aa5"-    , "8d74d01b582f70b92f53b43468084e1586d9b36465d333d5faaf6911e62fe40e"-    , "ef7f6b6eb479ab05b3f9ab6dd72eac8b1e86d887f1bcae363cae386d0275a06f"-    , "7442b2a792a6a29559bb8a515d56916ee18200580aa02e1237dd358619382d8f"-    , "49d2cbfa0897b7d961c293c1e572fb26f28e7b956e746f6eda90454c1370a29e25303ceadc7837514dc638553b487ef9487c977c10625409178ad6506d103c487a66655d08659d92a4d5994d1c8ddb28fe60f2e49577d6e80cae1478068c98268f45e6293c9326c7f726ec89601351c0a26fd3a6549f8a41c6f58692c86594c0"-    ],-    -- COUNT = 4-    [ "0f0aa84ef12e10ae2b279e799c683441862457b9bc25581c2cd3d5b58a5b3246"-    , "f74f4230c2427a52f01f39e825d250ac"-    , "d02b2f53da48b923c2921e0f75bd7e6139d7030aead5aeebe46c20b9ca47a38a"-    , "5222b26e79f7c3b7066d581185b1a1f6376796f3d67f59d025dd2a7b1886d258"-    , "d11512457bf3b92d1b1c0923989911f58f74e136b1436f00bad440dd1d6f1209"-    , "54d9ea7d40b7255ef3d0ab16ea9fdf29b9a281920962b5c72d97b0e371b9d816"-    , "601cef261da8864f1e30196c827143e4c363d3fa865b808e9450b13e251d47fa"-    , "e9847cefea3b88062ea63f92dc9e96767ce9202a6e049c98dc1dcbc6d707687bd0e98ed2cc215780c454936292e44a7c6856d664581220b8c8ca1d413a2b81120380bfd0da5ff2bf737b602727709523745c2ced8daef6f47d1e93ef9bc141a135674cba23045e1f99aa78f8cead12eeffff20de2008878b1f806a2652db565a"-    ],-    -- COUNT = 5-    [ "6a868ce39a3adcd189bd704348ba732936628f083de8208640dbd42731447d4e"-    , "efdde4e22b376e5e7385e79024350699"-    , "f7285cd5647ff0e2c71a9b54b57f04392641a4bde4a4024fa11c859fecaad713"-    , "0174f7f456ac06c1d789facc071701f8b60e9accebced73a634a6ad0e1a697d4"-    , "5463bb2241d10c970b68c3abc356c0fe5ef87439fc6457c5ee94be0a3fb89834"-    , "3ab62cdbc638c1b2b50533d28f31b1758c3b8435fe24bb6d4740005a73e54ce6"-    , "2dbf4c9123e97177969139f5d06466c272f60d067fefadf326ccc47971115469"-    , "8afce49dccc4ff64c65a83d8c0638bd8e3b7c13c52c3c59d110a8198753e96da512c7e03aeed30918706f3ad3b819e6571cfa87369c179fb9c9bbc88110baa490032a9d41f9931434e80c40ae0051400b7498810d769fb42dddbc7aa19bdf79603172efe9c0f5d1a65372b463a31178cbae581fa287f39c4fbf8434051b7419f"-    ],-    -- COUNT = 6-    [ "bb6b339eae26072487084ec9e4b53f2f1d4267d205042e74c77fb9ca0591ba50"-    , "c0e7bf6eb07feccbc494af4098e59d30"-    , "34aeec7ed0cae83701b6477709c8654a1114212401dc91cbe7de39d71f0c06e1"-    , "f47fc60afbeb807236f7974d837335bc0b22288ef09ddfcb684e16b4c36a050b"-    , "e8071ccd84ac4527e5c6e85b0709ed867776f25ae0e04180dcb7105ecd3e3490"-    , "fbac45b5952200ad7c4232500f2417a1c14723bdd1cc078821bc2fe138b86597"-    , "c4292d7dbef3ba7c18bf46bcf26776add22ab8ee206d6c722665dec6576b1bc0"-    , "228aa2a314fcbfe63089ce953ac457093deaa39dd9ce2a4ece56a6028a476a98129be516d6979eff5587c032cdf4739d7ac712970f600fa781a8e542e399661183e34e4b90c59ec5dc5cad86f91083529d41c77b8f36c5a8e28ba1a548223a02eaed8426f6fe9f349ebec11bc743e767482e3472ec2799c1f530ebdc6c03bc4b"-    ],-    -- COUNT = 7-    [ "be658e56f80436039e2a9c0a62952dd7d70842244b5ab10f3b8a87d36104e629"-    , "33c9627455dfde91865aee93e5071147"-    , "d3a6eb29b180b791984deb056d72c0608a2c9044237aecf100ccb03700064c5e"-    , "bef24dc9a5aa23003d3825f9b2b00e7dab571ea6ad86415dbd30c0bbdce7b972"-    , "047c29e4d1584fa70cb66e2aa148a2aa29837c5eee64dcac60fdba356cdf90bb"-    , "41c4792161b1b00d410cb79cd56bd311a714fb78dc3471c25bdd7479f2e9a952"-    , "cd4936d7bc3ea0e7201bcbefbc908215a97680ca6ce8672360aea600b6564308"-    , "2c25557f6db07db057f56ad5b6dc0427d1a0e825c48c19a526f9a65087c6d1ead7c78363a61616c84f1022653af65173a3f9ec3275f2b0a0d0bc750194673c0eaa6c623cd88abb0c8979baee4cd85bfce2e4a20bfebf2c3be61676563767dfe229e0b7be67ad6fcd116dd0b460708b1b0e5c3d60f3dd8138030404d197375d75"-    ],-    -- COUNT = 8-    [ "ae537f31a28ca14500e759716bc207983bfeab60b25079fa30b77b8d41244cb9"-    , "fca9e27d8ab84cf9b9ce491ec5d8cb67"-    , "8c9cb2b19aa3abe83c8fe7da96e9c11648252653a29dcd5bf0ac334ac587f032"-    , "1eb52777be480f05115ae6370f30159a94d50ffcc64454678ab1d1ac6f166fa7"-    , "9cdf6f1a2bc07acd4b0f43b5f2b892a1153e2669f237d257923636094fb40b54"-    , "692d512722de6ba720fd23c8994ac63179b5f7e611addf9cfacd60e06e144a6a"-    , "bbeea7b2bea821f339f494947c0b4bae8056119db69a3cbef21914953729cdef"-    , "c0c4fb7080c0fbe425c1b756fb3a090cb0d08c7027d1bb82ed3b07613e2a757f83a78d42f9d8653954b489f800a5e058ebc4f5a1747526541d8448cb72e2232db20569dc96342c36672c4be625b363b4587f44557e58cedb4597cb57d006fda27e027818ae89e15b4c6382b9e7a4453290ea43163b4f9cae38b1023de6a47f7b"-    ],-    -- COUNT = 9-    [ "2f8994c949e08862db0204008f55d3561f3e0362df13b9d9a70fda39938f2d33"-    , "1bf3e94ea858160b832fe85d301256f5"-    , "b46671cf7fa142e7012ed261e1fe86714711c246c7d1c0330fa692141e86d5d1"-    , "5ecdb1e8fe12260b9bfe12d6e6f161474fa2311e12e39b0beb0fcd92a6737b73"-    , "3ce9a29f0207d079e6dc81fb830356e555f96a23ea71424972ea9308965786d3"-    , "db950000c0776cc0e049929ce021020adc42d29cd9b5d8f7117fbe6bde3e594f"-    , "fc18ee6dd3dac2306774f0ac36cd789e33462d72a8c75df9057123db33e5f7bc"-    , "8546362cc8af9b78dd6e8eb2c37db96e70708852bfd9380abedc7f324575a167bea18f632f3e19d099cfbf310773f9719eec036d2e09f393a023add8ebdc4fb87af43b2fe6c7eaa4d39f8022ce247aa45fdc84d1b92cacce6eae8252a03ec2ec5330c01f56d113fd2ec3d0240af0afcf13ddde205bb5e7c2d912dcb4aee5dcf3"-    ],-    -- COUNT = 10-    [ "0c85e31487de1d7ba4a7b998ac56dc42c6dc0eae7bf5c8aaf1e4e78875f5fb47"-    , "de878f728f73f83dc2a2f550b96c8b97"-    , "9aac37bce1a6a81dc7934e23747991e3cf48c55ffe5a57781c41768a35220a01"-    , "2d5ca8af1a70cfdccd015ee3bf0665dd1941fc6a7317b9d0d06658f5744cfbd9"-    , "db881e6d0dc3b62793d7da5fe5a18e33be9b93f4a63a00a878dfbecf0d383bd2"-    , "f743ce1b72f3de4c901369eed581c626ed3081ca707e6634fdaff46721ce0878"-    , "cd52da3ec8a839c537dacdea8506a3eeee879de388ff5e513322d6d1bb3ff694"-    , "a5bdd57cb8fde6298e7c5e563afcca60dd472eca484bd8c3cc17f3307be09b601744dd3ab9e8a44107c5868824575f850c0f399b280cf198006f83ede8c0b537e9be227fa140b65995ad9dfa1f2303d560c3b7f59bedd93c1282ea263924469411c2653f87fd814c74cb91c148430481d64bad0fec3cbb3dd1f39aa55c36f81b"-    ],-    -- COUNT = 11-    [ "93161b2dc08cb0fd50171141c865a841ca935cfdd2b5907d6ff8ab0348c4ceb0"-    , "5cb9f6e5912b90c3349a50ab881b35a1"-    , "0dceb4a36326c4df1685df43fddeecb5d0c76f00eb44826694f27e610290f6e1"-    , "d8e9be44b5f293482548d4787762ebfb03c73c40e45385e8b98907cd66f493dd"-    , "105a8f85d6959f3e043ef508cfea21d52123f03b7aea8034c4eec761eaba1fee"-    , "bf781f7e489d9b4b5aa5ee6d1796468af672a8d25f311edf3c4b4dbf433d703f"-    , "c81d6bcf1e5bf37e39dda1735c6f193df115b1a854a12e7cafe060afe4589335"-    , "4306628124d0100fade7eaaf5edf227d50771f9e5f2e1e983800eef9a39fde0b0c280e63c8728d836b5b93ea794a32c1c04cfc54bd5300e3febb5fe2e1023eded8d7cd180279a598f76823e8d5a7dffcc93a09deec5d1f80838e938fba4de9f47e94b99382ae55f116df9c3b3ddf7e50516e203645852a415796f03a86418107"-    ],-    -- COUNT = 12-    [ "1ae12a5e4e9a4a5bfa79da30a9e6c62ffc639572ef1254194d129a16eb53c716"-    , "5399b3481fdf24d373222267790a0fec"-    , "8280cfdcd7a575816e0199e115da0ea77cae9d30b49c891a6c225e9037ba67e2"-    , "681554ff702658122e91ba017450cfdfc8e3f4911153f7bcc428403e9c7b9d68"-    , "226732b7a457cf0ac0ef09fd4f81296573b49a68de5e7ac3070e148c95e8e323"-    , "45942b5e9a1a128e85e12c34596374ddc85fd7502e5633c7390fc6e6f1e5ef56"-    , "6fc59929b41e77072886aff45f737b449b105ed7eacbd74c7cbfedf533dbeaa1"-    , "b7547332e1509663fcfea2128f7f3a3df484cd8df034b00199157d35d61e35f1a9d481c7d2e81305616d70fc371ee459b0b2267d627e928590edcac3231898b24ef378aa9c3d381619f665379be76c7c1bd535505c563db3725f034786e35bdd90429305fd71d7bf680e8cdd6d4c348d97078f5cf5e89dee2dc410fad4f2a30f"-    ],-    -- COUNT = 13-    [ "29e20d724dfa459960df21c6ec76b1e6cabd23a9e9456d6c591d7e4529da0ef8"-    , "95df1f837eba47a1687aa5c4ddcf8aaf"-    , "3713b601e164b1a51dda1ca9242ff477514648e90d311a06e10ce5aa15da5d7f"-    , "2a2a312626ca3e20034fc4f28033c7d573f66ef61ab2ea0c7bf0411a9d247264"-    , "ec68be33ac8ff3dd127e051604898c0f9a501271859376653a0516336180993d"-    , "9935499661d699a00c622a875441b4df5204958fe95892c8ce67f7dfb2be3e4a"-    , "256a4ba9e8f439d5487fa5eb45efcf1bc1120491724db3abe328d951f2739fc9"-    , "73114cb3624d687d4cd49a6e769dfc7a3f8901dc41f6ad1df4ce480536fa82e52ae958d0528640d92b8bb981b755058e32c4733682e5c4c0df41f3505a1643a0dd49cfdeaf7a18adffca88256c6d2cceb838af6c92a64bc21cb7a760a0391291bfe3575e014fc156323f8eb5e86518c669dad8d29ad5fd4ef6e296f4a0764c26"-    ],-    -- COUNT = 14-    [ "1353f3543eb1134980e061fc4382394975dbc74f1f1ea5ecc02780a813ac5ee6"-    , "cf584db2447afbe2c8fa0c15575ee391"-    , "345b0cc016f2765a8c33fc24f1dcfa182cbe29d7eacbcdc9bcda988521458fc2"-    , "ba60219332a67b95d90ec9de6b8453d4c8af991ae9277461ff3af1b92fc985d3"-    , "6964b9b9842aec9c7ec2aad926d701f30eec76fe699265ae2a7765d716958069"-    , "6a03c28a9365c558c33d3fdc7e5ebf0b4d32caac70df71403fd70ced09757528"-    , "a58546c72a0b4d47c9bd6c19e7cf4ab73b2d7ba36c6c6dc08606f608795ebd29"-    , "5b029ef68b6799868b04dc28dbea26bc2fa9fcc8c2b2795aafeed0127b7297fa19a4ef2ba60c42ff8259d5a759f92bd90fdfb27145e82d798bb3ab7fd60bfaefb7aefb116ca2a4fa8b01d96a03c47c8d987fdd33c460e560b138891278313bb619d0c3c6f9d7c5a37e88fce83e94943705c6ff68e00484e74ad4097b0c9e5f10"-    ]-    ]
− test/Network/Haskoin/Crypto/SignatureSpec.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Crypto.SignatureSpec (spec) where--import           Data.Bits              (testBit)-import           Data.ByteString        (ByteString)-import qualified Data.ByteString        as BS (index, length)-import           Data.Serialize         as S-import           Data.Text              (Text)-import           Network.Haskoin.Crypto (SecKey, Sig, decodeStrictSig,-                                         derivePubKey, exportSig,-                                         importCompactSig, isCanonicalHalfOrder,-                                         sha256, signHash, verifyHashSig)-import           Network.Haskoin.Test   (arbitrarySignature)-import           Network.Haskoin.Util   (decodeHex, eitherToMaybe, lst3)-import           Test.Hspec             (Spec, describe, it)-import           Test.Hspec.QuickCheck  (prop)-import           Test.HUnit             (Assertion, assertBool)-import           Test.QuickCheck        (forAll)--spec :: Spec-spec = do-    describe "signatures" $ do-        prop "verify signature" $-            forAll arbitrarySignature $ \(msg, key, sig) ->-                verifyHashSig msg sig (derivePubKey key)-        prop "s component less than half order" $-            forAll arbitrarySignature $ isCanonicalHalfOrder . lst3-        prop "encoded signature is canonical" $-            forAll arbitrarySignature $ testIsCanonical . lst3-        prop "encode signature and decode strictly" $-            forAll arbitrarySignature $-            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3-        prop "encodes and decodes signature" $-            forAll arbitrarySignature $-            (\s -> decodeStrictSig (exportSig s) == Just s) . lst3-    describe "trezor rfc6979 test vectors" $ do-        it "rfc6979 test vector 1" (testSigning $ head detVec)-        it "rfc6979 test vector 2" (testSigning $ detVec !! 1)-        it "rfc6979 test vector 3" (testSigning $ detVec !! 2)-        it "rfc6979 test vector 4" (testSigning $ detVec !! 3)-        it "rfc6979 test vector 5" (testSigning $ detVec !! 4)-        it "rfc6979 test vector 6" (testSigning $ detVec !! 5)-        it "rfc6979 test vector 7" (testSigning $ detVec !! 6)-        it "rfc6979 test vector 8" (testSigning $ detVec !! 7)-        it "rfc6979 test vector 9" (testSigning $ detVec !! 8)-        it "rfc6979 test vector 10" (testSigning $ detVec !! 9)-        it "rfc6979 test vector 11" (testSigning $ detVec !! 10)-        it "rfc6979 test vector 12" (testSigning $ detVec !! 11)---- No longer validating that generated signature is exactly equal to provided--- one. Fedora includes libsecp256k1 from Bitcoin ABC that computes--- deterministic signatures using a slightly different nonce generation--- algorithm.-testSigning :: (SecKey, ByteString, Text) -> Assertion-testSigning (prv, msg, str) = do-    assertBool "my sig valid" $ verifyHashSig msg' g (derivePubKey prv)-    assertBool "valid sig" $ verifyHashSig msg' g' (derivePubKey prv)-  where-    Just g' = importCompactSig =<< eitherToMaybe . decode =<< decodeHex str-    g = signHash prv msg'-    msg' = sha256 msg---{- ECDSA Canonical -}---- github.com/bitcoin/bitcoin/blob/master/src/script.cpp--- from function IsCanonicalSignature-testIsCanonical :: Sig -> Bool-testIsCanonical sig = not $-    -- Non-canonical signature: too short-    (len < 8) ||-    -- Non-canonical signature: too long-    (len > 72) ||-    -- Non-canonical signature: wrong type-    (BS.index s 0 /= 0x30) ||-    -- Non-canonical signature: wrong length marker-    (BS.index s 1 /= len - 2) ||-    -- Non-canonical signature: S length misplaced-    (5 + rlen >= len) ||-    -- Non-canonical signature: R+S length mismatch-    (rlen + slen + 6 /= len) ||-    -- Non-canonical signature: R value type mismatch-    (BS.index s 2 /= 0x02) ||-    -- Non-canonical signature: R length is zero-    (rlen == 0) ||-    -- Non-canonical signature: R value negative-    testBit (BS.index s 4) 7 ||-    -- Non-canonical signature: R value excessively padded-    (  rlen > 1-    && BS.index s 4 == 0-    && not (testBit (BS.index s 5) 7)-    ) ||-    -- Non-canonical signature: S value type mismatch-    (BS.index s (fromIntegral rlen + 4) /= 0x02) ||-    -- Non-canonical signature: S length is zero-    (slen == 0) ||-    -- Non-canonical signature: S value negative-    testBit (BS.index s (fromIntegral rlen+6)) 7 ||-    -- Non-canonical signature: S value excessively padded-    (  slen > 1-    && BS.index s (fromIntegral rlen + 6) == 0-    && not (testBit (BS.index s (fromIntegral rlen + 7)) 7)-    )-  where-    s = exportSig sig-    len = fromIntegral $ BS.length s-    rlen = BS.index s 3-    slen = BS.index s (fromIntegral rlen + 5)---{- Trezor RFC 6979 Test Vectors -}--- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py--detVec :: [(SecKey, ByteString, Text)]-detVec =-    [-      ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "Satoshi Nakamoto"-      , "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "All those moments will be lost in time, like tears in rain. Time to die..."-      , "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Satoshi Nakamoto"-      , "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"-      )-    , ( "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181"-      , "Alan Turing"-      , "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"-      )-    , ( "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2"-      , "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!"-      , "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "Everything should be made as simple as possible, but not simpler."-      , "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Equations are more important to me, because politics is for the present, but an equation is something for eternity."-      , "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"-      )-    , ( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140"-      , "Not only is the Universe stranger than we think, it is stranger than we can think."-      , "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"-      )-    , ( "0000000000000000000000000000000000000000000000000000000000000001"-      , "How wonderful that we have met with a paradox. Now we have some hope of making progress."-      , "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"-      )-    , ( "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64"-      , "Computer science is no more about computers than astronomy is about telescopes."-      , "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"-      )-    , ( "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637"-      , "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough"-      , "fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37"-      )-    , ( "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3"-      , "The question of whether computers can think is like the question of whether submarines can swim."-      , "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"-      )-    ]
− test/Network/Haskoin/CryptoSpec.hs
@@ -1,165 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.CryptoSpec (spec) where--import           Control.Monad-import           Data.ByteString           (ByteString)-import qualified Data.ByteString.Char8     as C (pack)-import           Data.Maybe                (fromMaybe, isJust, isNothing)-import           Data.Text                 (Text)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                (Assertion, assertBool)---- Unit tests copied from bitcoind implementation--- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp--strSecret1 :: Text-strSecret1  = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"--strSecret2 :: Text-strSecret2  = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"--strSecret1C :: Text-strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"--strSecret2C :: Text-strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"--addr1 :: Text-addr1  = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"--addr2 :: Text-addr2  = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"--addr1C :: Text-addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"--addr2C :: Text-addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"--strAddressBad :: Text-strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"--sigMsg :: [ByteString]-sigMsg =-    [ mconcat ["Very secret message ", C.pack (show (i :: Int)), ": 11"]-    | i <- [0..15]-    ]--sec1 :: SecKeyI-sec1 =-    fromMaybe (error "Could not decode WIF secret 1") (fromWif btc strSecret1)--sec2 :: SecKeyI-sec2 =-    fromMaybe (error "Could not decode WIF secret 2") (fromWif btc strSecret2)--sec1C :: SecKeyI-sec1C =-    fromMaybe-        (error "Could not decode WIF compressed secret 1")-        (fromWif btc strSecret1C)--sec2C :: SecKeyI-sec2C =-    fromMaybe-        (error "Could not decode WIF compressed secret 2")-        (fromWif btc strSecret2C)--pub1 :: PubKeyI-pub1  = derivePubKeyI sec1--pub2 :: PubKeyI-pub2  = derivePubKeyI sec2--pub1C :: PubKeyI-pub1C = derivePubKeyI sec1C--pub2C :: PubKeyI-pub2C = derivePubKeyI sec2C--spec :: Spec-spec =-    describe "bitcoind /src/test/key_tests.cpp" $ do-        it "decode valid wif" checkPrivkey-        it "decode invalid wif" checkInvalidKey-        it "decode minikey format" checkMiniKey-        it "check private key compression" checkPrvKeyCompressed-        it "check public key compression" checkKeyCompressed-        it "check matching address" checkMatchingAddress-        it "check various signatures" sigCheck--sigCheck :: Assertion-sigCheck = forM_ sigMsg $ checkSignatures . doubleSHA256--{- bitcoind /src/test/key_tests.cpp -}--checkPrivkey :: Assertion-checkPrivkey = do-    assertBool "Key 1"  $ isJust $ fromWif btc strSecret1-    assertBool "Key 2"  $ isJust $ fromWif btc strSecret2-    assertBool "Key 1C" $ isJust $ fromWif btc strSecret1C-    assertBool "Key 2C" $ isJust $ fromWif btc strSecret2C--checkInvalidKey :: Assertion-checkInvalidKey =-    assertBool "Bad key" $ isNothing $ fromWif btc strAddressBad--checkMiniKey :: Assertion-checkMiniKey =-    assertBool "Bad mini key" $-    isJust res && fromMiniKey "S6c56bnXQiBjk9mqSYE7ykVQ7NzrRy" == res-  where-    res = do-        bs <--            decodeHex-                "4C7A9640C72DC2099F23715D0C8A0D8A35F8906E3CAB61DD3F78B67BF887C9AB"-        wrapSecKey False <$> secKey bs--checkPrvKeyCompressed :: Assertion-checkPrvKeyCompressed = do-    assertBool "Key 1"  $ not $ secKeyCompressed sec1-    assertBool "Key 2"  $ not $ secKeyCompressed sec2-    assertBool "Key 1C" $ secKeyCompressed sec1C-    assertBool "Key 2C" $ secKeyCompressed sec2C--checkKeyCompressed :: Assertion-checkKeyCompressed = do-    assertBool "Key 1"  $ not $ pubKeyCompressed pub1-    assertBool "Key 2"  $ not $ pubKeyCompressed pub2-    assertBool "Key 1C" $ pubKeyCompressed pub1C-    assertBool "Key 2C" $ pubKeyCompressed pub2C--checkMatchingAddress :: Assertion-checkMatchingAddress = do-    assertBool "Key 1"  $ Just addr1  == addrToString btc (pubKeyAddr pub1)-    assertBool "Key 2"  $ Just addr2  == addrToString btc (pubKeyAddr pub2)-    assertBool "Key 1C" $ Just addr1C == addrToString btc (pubKeyAddr pub1C)-    assertBool "Key 2C" $ Just addr2C == addrToString btc (pubKeyAddr pub2C)--checkSignatures :: Hash256 -> Assertion-checkSignatures h = do-    let sign1  = signHash (secKeyData sec1) h-        sign2  = signHash (secKeyData sec2) h-        sign1C = signHash (secKeyData sec1C) h-        sign2C = signHash (secKeyData sec2C) h-    assertBool "Key 1, Sign1"   $ verifyHashSig h sign1 (pubKeyPoint pub1)-    assertBool "Key 1, Sign2"   $ not $ verifyHashSig h sign2 (pubKeyPoint pub1)-    assertBool "Key 1, Sign1C"  $ verifyHashSig h sign1C (pubKeyPoint pub1)-    assertBool "Key 1, Sign2C"  $ not $ verifyHashSig h sign2C (pubKeyPoint pub1)-    assertBool "Key 2, Sign1"   $ not $ verifyHashSig h sign1 (pubKeyPoint pub2)-    assertBool "Key 2, Sign2"   $ verifyHashSig h sign2 (pubKeyPoint pub2)-    assertBool "Key 2, Sign1C"  $ not $ verifyHashSig h sign1C (pubKeyPoint pub2)-    assertBool "Key 2, Sign2C"  $ verifyHashSig h sign2C (pubKeyPoint pub2)-    assertBool "Key 1C, Sign1"  $ verifyHashSig h sign1 (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign2"  $ not $ verifyHashSig h sign2 (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign1C" $ verifyHashSig h sign1C (pubKeyPoint pub1C)-    assertBool "Key 1C, Sign2C" $ not $ verifyHashSig h sign2C (pubKeyPoint pub1C)-    assertBool "Key 2C, Sign1"  $ not $ verifyHashSig h sign1 (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign2"  $ verifyHashSig h sign2 (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign1C" $ not $ verifyHashSig h sign1C (pubKeyPoint pub2C)-    assertBool "Key 2C, Sign2C" $ verifyHashSig h sign2C (pubKeyPoint pub2C)
− test/Network/Haskoin/Keys/ExtendedSpec.hs
@@ -1,521 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Keys.ExtendedSpec (spec) where--import           Data.Aeson                 as A-import           Data.Aeson.Types           as A-import           Data.Bits                  ((.&.))-import qualified Data.ByteString.Lazy.Char8 as B8-import           Data.Either                (isLeft)-import           Data.Map.Strict            (singleton)-import           Data.Maybe                 (fromJust, isJust, isNothing)-import           Data.Serialize             as S-import           Data.String                (fromString)-import           Data.String.Conversions    (cs)-import           Data.Text                  (Text)-import           Data.Word                  (Word32)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                 (Assertion, assertBool, assertEqual)-import           Test.QuickCheck            hiding ((.&.))--spec :: Spec-spec = do-    describe "bip32 derivation vector 1" $ do-        it "chain m" $ runXKeyVec (head xKeyVec)-        it "chain m/0'" $ runXKeyVec (xKeyVec !! 1)-        it "chain m/0'/1" $ runXKeyVec (xKeyVec !! 2)-        it "chain m/0'/1/2'" $ runXKeyVec (xKeyVec !! 3)-        it "chain m/0'/1/2'/2" $ runXKeyVec (xKeyVec !! 4)-        it "chain m/0'/1/2'/2/1000000000" $ runXKeyVec (xKeyVec !! 5)-    describe "bip32 subkey derivation vector 2" $ do-        it "chain m" $ runXKeyVec (head xKeyVec2)-        it "chain m/0" $ runXKeyVec (xKeyVec2 !! 1)-        it "chain m/0/2147483647'" $ runXKeyVec (xKeyVec2 !! 2)-        it "chain m/0/2147483647'/1" $ runXKeyVec (xKeyVec2 !! 3)-        it "chain m/0/2147483647'/1/2147483646'" $ runXKeyVec (xKeyVec2 !! 4)-        it "Chain m/0/2147483647'/1/2147483646'/2" $ runXKeyVec (xKeyVec2 !! 5)-    describe "bip32 subkey derivation using string path" $ do-        it "either derivations" testApplyPath-        it "either derivations" testBadApplyPath-        it "dublic derivations" testDerivePubPath-        it "private derivations" testDerivePrvPath-        it "path parsing" testParsePath-        it "from json" testFromJsonPath-        it "to json" testToJsonPath-    describe "extended keys" $ do-        let net = btc-        it "computes pubkey of a subkey is subkey of the pubkey" $-            property $ forAll arbitraryXPrvKey pubKeyOfSubKeyIsSubKeyOfPubKey-        it "exports and imports extended private key" $-            property $-            forAll arbitraryXPrvKey $ \k ->-                xPrvImport net (xPrvExport net k) == Just k-        it "exports and imports extended public key" $-            property $-            forAll arbitraryXPubKey $ \(_, k) ->-                xPubImport net (xPubExport net k) == Just k-        it "show and read derivation path" $-            property $ forAll arbitraryDerivPath $ \p -> read (show p) == p-        it "show and read hard derivation path" $-            property $ forAll arbitraryHardPath $ \p -> read (show p) == p-        it "show and read soft derivation path" $-            property $ forAll arbitrarySoftPath $ \p -> read (show p) == p-        it "from string derivation path" $-            property $-            forAll arbitraryDerivPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from string hard derivation path" $-            property $-            forAll arbitraryHardPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from string soft derivation path" $-            property $-            forAll arbitrarySoftPath $ \p -> fromString (cs $ pathToStr p) == p-        it "from and to lists of derivation paths" $-            property $-            forAll arbitraryDerivPath $ \p -> listToPath (pathToList p) == p-        it "from and to lists of hard derivation paths" $-            property $-            forAll arbitraryHardPath $ \p ->-                toHard (listToPath $ pathToList p) == Just p-        it "from and to lists of soft derivation paths" $-            property $-            forAll arbitrarySoftPath $ \p ->-                toSoft (listToPath $ pathToList p) == Just p-        it "read and show parsed path" $-            property $ forAll arbitraryParsedPath $ \p -> read (show p) == p-        it "encodes and decodes extended private key" $-            forAll-                arbitraryXPrvKey-                (testCustom (xPrvFromJSON net) (xPrvToJSON net))-        it "encodes and decodes extended public key" $-            forAll-                arbitraryXPubKey-                (testCustom (xPubFromJSON net) (xPubToJSON net) . snd)-        it "encodes and decodes derivation path" $-            forAll arbitraryDerivPath testID-        it "encodes and decodes parsed derivation path" $-            forAll arbitraryParsedPath testID-        it "encodes and decodes extended private key" $-            property $-            forAll arbitraryXPrvKey $-            testPutGet (getXPrvKey net) (putXPrvKey net)-        it "shows and reads extended private key" $-            property $-            forAll arbitraryXPrvKey $ \k -> read (show k) `shouldBe` k-        it "shows and reads extended private key" $-            property $-            forAll arbitraryXPubKey $ \(prv, pub) ->-                read (show (prv, pub)) `shouldBe` (prv, pub)--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 :: Assertion-testApplyPath =-    sequence_ $ do-        (key, path, final) <- applyPathVectors-        return $-            assertEqual path final $ applyPath (fromJust $ parsePath path) key--testBadApplyPath :: Assertion-testBadApplyPath =-    sequence_ $ do-        (key, path) <- badApplyPathVectors-        return $-            assertBool path $ isLeft $ applyPath (fromJust $ parsePath path) key--testDerivePubPath :: Assertion-testDerivePubPath =-    sequence_ $ do-        (key, path, final) <- derivePubPathVectors-        return $-            assertEqual path final $-            derivePubPath (fromString path :: SoftPath) key--testDerivePrvPath :: Assertion-testDerivePrvPath =-    sequence_ $ do-        (key, path, final) <- derivePrvPathVectors-        return $-            assertEqual path final $-            derivePath (fromString path :: DerivPath) key--derivePubPathVectors :: [(XPubKey, String, XPubKey)]-derivePubPathVectors =-    [ ( xpub, "M", xpub )-    , ( xpub, "M/8", pubSubKey xpub 8 )-    , ( xpub, "M/8/30/1", foldl pubSubKey xpub [8,30,1] )-    ]-  where-    xprv = fromJust $ xPrvImport btc-        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"-    xpub = deriveXPubKey xprv--derivePrvPathVectors :: [(XPrvKey, String, XPrvKey)]-derivePrvPathVectors =-    [ ( xprv, "m", xprv )-    , ( xprv, "M", xprv )-    , ( xprv, "m/8'", hardSubKey xprv 8 )-    , ( xprv, "M/8'", hardSubKey xprv 8 )-    , ( xprv, "m/8'/30/1"-      , foldl prvSubKey (hardSubKey xprv 8) [30,1]-      )-    , ( xprv, "M/8'/30/1"-      , foldl prvSubKey (hardSubKey xprv 8) [30,1]-      )-    , ( xprv, "m/3/20"-      , foldl prvSubKey xprv [3,20]-      )-    , ( xprv, "M/3/20"-      , foldl prvSubKey xprv [3,20]-      )-    ]-  where-    xprv = fromJust $ xPrvImport btc-        "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"--applyPathVectors :: [(XKey, String, Either String XKey)]-applyPathVectors =-    [ (XPrv xprv btc, "m", Right (XPrv xprv btc))-    , (XPrv xprv btc, "M", Right (XPub xpub btc))-    , (XPrv xprv btc, "m/8'", Right (XPrv (hardSubKey xprv 8) btc))-    , ( XPrv xprv btc-      , "M/8'"-      , Right (XPub (deriveXPubKey (hardSubKey xprv 8)) btc))-    , ( XPrv xprv btc-      , "m/8'/30/1"-      , Right (XPrv (foldl prvSubKey (hardSubKey xprv 8) [30, 1]) btc))-    , ( XPrv xprv btc-      , "M/8'/30/1"-      , Right-            (XPub-                 (deriveXPubKey (foldl prvSubKey (hardSubKey xprv 8) [30, 1]))-                 btc))-    , (XPrv xprv btc, "m/3/20", Right (XPrv (foldl prvSubKey xprv [3, 20]) btc))-    , ( XPrv xprv btc-      , "M/3/20"-      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))-    , ( XPub xpub btc-      , "M/3/20"-      , Right (XPub (deriveXPubKey (foldl prvSubKey xprv [3, 20])) btc))-    ]-  where-    xprv =-        fromJust $-        xPrvImport-            btc-            "xprv9s21ZrQH143K46iDVRSyFfGfMgQjzC4BV3ZUfNbG7PHQrJjE53ofAn5gYkp6KQ\-        \WzGmb8oageSRxBY8s4rjr9VXPVp2HQDbwPt4H31Gg4LpB"-    xpub = deriveXPubKey xprv--badApplyPathVectors :: [(XKey, String)]-badApplyPathVectors =-    [ (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 xprv---runXKeyVec :: ([Text], XPrvKey) -> Assertion-runXKeyVec (v, m) = do-    assertBool "xPrvID" $ encodeHex (S.encode $ xPrvID m) == head v-    assertBool "xPrvFP" $ encodeHex (S.encode $ xPrvFP m) == v !! 1-    assertBool "xPrvAddr" $-        addrToString btc (xPubAddr $ deriveXPubKey m) == Just (v !! 2)-    assertBool "prvKey" $ encodeHex (getSecKey $ xPrvKey m) == v !! 3-    assertBool "xPrvWIF" $ xPrvWif btc m == v !! 4-    assertBool "pubKey" $-        encodeHex (exportPubKey True $ xPubKey $ deriveXPubKey m) == v !! 5-    assertBool "chain code" $ encodeHex (S.encode $ xPrvChain m) == v !! 6-    assertBool "Hex PubKey" $-        encodeHex (runPut $ putXPubKey btc $ deriveXPubKey m) == v !! 7-    assertBool "Hex PrvKey" $ encodeHex (runPut (putXPrvKey btc m)) == v !! 8-    assertBool "Base58 PubKey" $ xPubExport btc (deriveXPubKey m) == v !! 9-    assertBool "Base58 PrvKey" $ xPrvExport btc m == v !! 10---- BIP 0032 Test Vectors--- https://en.bitcoin.it/wiki/BIP_0032_TestVectors--xKeyVec :: [([Text], XPrvKey)]-xKeyVec = zip xKeyResVec $ foldl f [m] der-    where f acc d = acc ++ [d $ last acc]-          m   = makeXPrvKey $ fromJust $ decodeHex m0-          der = [ flip hardSubKey 0-                , flip prvSubKey 1-                , flip hardSubKey 2-                , flip prvSubKey 2-                , flip prvSubKey 1000000000-                ]--xKeyVec2 :: [([Text], XPrvKey)]-xKeyVec2 = zip xKeyResVec2 $ foldl f [m] der-    where f acc d = acc ++ [d $ last acc]-          m   = makeXPrvKey $ fromJust $ decodeHex m1-          der = [ flip prvSubKey 0-                , flip hardSubKey 2147483647-                , flip prvSubKey 1-                , flip hardSubKey 2147483646-                , flip prvSubKey 2-                ]--m0 :: Text-m0 = "000102030405060708090a0b0c0d0e0f"--xKeyResVec :: [[Text]]-xKeyResVec =-    [-      -- m-      [ "3442193e1bb70916e914552172cd4e2dbc9df811"-      , "3442193e"-      , "15mKKb2eos1hWa6tisdPwwDC1a5J1y9nma"-      , "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"-      , "L52XzL2cMkHxqxBXRyEpnPQZGUs3uKiL3R11XbAdHigRzDozKZeW"-      , "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"-      , "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"-      , "0488b21e000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d5080339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"-      , "0488ade4000000000000000000873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d50800e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"-      , "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"-      , "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"-      ]-      -- m/0'-    , [ "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7"-      , "5c1bd648"-      , "19Q2WoS5hSS6T8GjhK8KZLMgmWaq4neXrh"-      , "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"-      , "L5BmPijJjrKbiUfG4zbiFKNqkvuJ8usooJmzuD7Z8dkRoTThYnAT"-      , "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"-      , "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"-      , "0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"-      , "0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"-      , "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"-      , "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"-      ]-      -- m/0'/1-    , [ "bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe"-      , "bef5a2f9"-      , "1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj"-      , "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"-      , "KyFAjQ5rgrKvhXvNMtFB5PCSKUYD1yyPEe3xr3T34TZSUHycXtMM"-      , "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"-      , "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"-      , "0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"-      , "0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"-      , "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"-      , "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"-      ]-      -- m/0'/1/2'-    , [ "ee7ab90cde56a8c0e2bb086ac49748b8db9dce72"-      , "ee7ab90c"-      , "1NjxqbA9aZWnh17q1UW3rB4EPu79wDXj7x"-      , "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"-      , "L43t3od1Gh7Lj55Bzjj1xDAgJDcL7YFo2nEcNaMGiyRZS1CidBVU"-      , "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"-      , "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"-      , "0488b21e03bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"-      , "0488ade403bef5a2f98000000204466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f00cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"-      , "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"-      , "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"-      ]-      -- m/0'/1/2'/2-    , [ "d880d7d893848509a62d8fb74e32148dac68412f"-      , "d880d7d8"-      , "1LjmJcdPnDHhNTUgrWyhLGnRDKxQjoxAgt"-      , "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"-      , "KwjQsVuMjbCP2Zmr3VaFaStav7NvevwjvvkqrWd5Qmh1XVnCteBR"-      , "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"-      , "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"-      , "0488b21e04ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"-      , "0488ade404ee7ab90c00000002cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd000f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"-      , "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"-      , "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"-      ]-      -- m/0'/1/2'/2/1000000000-    , [ "d69aa102255fed74378278c7812701ea641fdf32"-      , "d69aa102"-      , "1LZiqrop2HGR4qrH1ULZPyBpU6AUP49Uam"-      , "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"-      , "Kybw8izYevo5xMh1TK7aUr7jHFCxXS1zv8p3oqFz3o2zFbhRXHYs"-      , "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"-      , "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"-      , "0488b21e05d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"-      , "0488ade405d880d7d83b9aca00c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e00471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"-      , "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"-      , "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"-      ]-    ]--m1 :: Text-m1 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"--xKeyResVec2 :: [[Text]]-xKeyResVec2 =-    [-      -- m-      [ "bd16bee53961a47d6ad888e29545434a89bdfe95"-      , "bd16bee5"-      , "1JEoxevbLLG8cVqeoGKQiAwoWbNYSUyYjg"-      , "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"-      , "KyjXhyHF9wTphBkfpxjL8hkDXDUSbE3tKANT94kXSyh6vn6nKaoy"-      , "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"-      , "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"-      , "0488b21e00000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd968903cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"-      , "0488ade400000000000000000060499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689004b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"-      , "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"-      , "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"-      ]-      -- m/0-    , [ "5a61ff8eb7aaca3010db97ebda76121610b78096"-      , "5a61ff8e"-      , "19EuDJdgfRkwCmRzbzVBHZWQG9QNWhftbZ"-      , "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"-      , "L2ysLrR6KMSAtx7uPqmYpoTeiRzydXBattRXjXz5GDFPrdfPzKbj"-      , "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"-      , "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"-      , "0488b21e01bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"-      , "0488ade401bd16bee500000000f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c00abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"-      , "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"-      , "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"-      ]-      -- m/0/2147483647'-    , [ "d8ab493736da02f11ed682f88339e720fb0379d1"-      , "d8ab4937"-      , "1Lke9bXGhn5VPrBuXgN12uGUphrttUErmk"-      , "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"-      , "L1m5VpbXmMp57P3knskwhoMTLdhAAaXiHvnGLMribbfwzVRpz2Sr"-      , "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"-      , "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"-      , "0488b21e025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d903c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"-      , "0488ade4025a61ff8effffffffbe17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d900877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"-      , "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"-      , "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"-      ]-      -- m/0/2147483647'/1-    , [ "78412e3a2296a40de124307b6485bd19833e2e34"-      , "78412e3a"-      , "1BxrAr2pHpeBheusmd6fHDP2tSLAUa3qsW"-      , "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"-      , "KzyzXnznxSv249b4KuNkBwowaN3akiNeEHy5FWoPCJpStZbEKXN2"-      , "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"-      , "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"-      , "0488b21e03d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"-      , "0488ade403d8ab493700000001f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb00704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"-      , "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"-      , "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"-      ]-      -- m/0/2147483647'/1/2147483646'-    , [ "31a507b815593dfc51ffc7245ae7e5aee304246e"-      , "31a507b8"-      , "15XVotxCAV7sRx1PSCkQNsGw3W9jT9A94R"-      , "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"-      , "L5KhaMvPYRW1ZoFmRjUtxxPypQ94m6BcDrPhqArhggdaTbbAFJEF"-      , "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"-      , "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"-      , "0488b21e0478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2902d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"-      , "0488ade40478412e3afffffffe637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e2900f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"-      , "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"-      , "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"-      ]-      -- m/0/2147483647'/1/2147483646'/2-    , [ "26132fdbe7bf89cbc64cf8dafa3f9f88b8666220"-      , "26132fdb"-      , "14UKfRV9ZPUp6ZC9PLhqbRtxdihW9em3xt"-      , "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"-      , "L3WAYNAZPxx1fr7KCz7GN9nD5qMBnNiqEJNJMU1z9MMaannAt4aK"-      , "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"-      , "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"-      , "0488b21e0531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"-      , "0488ade40531a507b8000000029452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed27100bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"-      , "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"-      , "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"-      ]-    ]--pubKeyOfSubKeyIsSubKeyOfPubKey :: XPrvKey -> Word32 -> Bool-pubKeyOfSubKeyIsSubKeyOfPubKey k i =-    deriveXPubKey (prvSubKey k i') == pubSubKey (deriveXPubKey k) i'-  where-    i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation---testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--testCustom :: Eq a => (Value -> Parser a) -> (a -> Value) -> a -> Bool-testCustom f g x = parseMaybe f (g x) == Just x--testPutGet :: Eq a => Get a -> Putter a -> a -> Bool-testPutGet g p a = runGet g (runPut (p a)) == Right a
− test/Network/Haskoin/Keys/MnemonicSpec.hs
@@ -1,494 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.Keys.MnemonicSpec (spec) where--import           Control.Monad           (zipWithM_)-import           Data.Bits               (shiftR, (.&.))-import           Data.ByteString         (ByteString)-import qualified Data.ByteString         as BS-import qualified Data.ByteString.Char8   as C-import           Data.Either             (fromRight)-import           Data.List               (isPrefixOf)-import           Data.Maybe              (fromJust)-import           Data.Serialize          (Serialize, encode)-import           Data.String.Conversions (cs)-import           Data.Text               (Text)-import qualified Data.Text               as T-import           Data.Word               (Word32, Word64)-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit-import           Test.QuickCheck         hiding ((.&.))--spec :: Spec-spec =-    describe "mnemonic" $ do-        it "entropy to mnemonic sentence" toMnemonicTest-        it "mnemonic sentence to entropy" fromMnemonicTest-        it "mnemonic sentence to seed" mnemonicToSeedTest-        it "mnemonic sentence with invalid checksum" fromMnemonicInvalidTest-        it "empty mnemonic sentence is invalid" $ sequence_ [emptyMnemonicTest]-        it "generate 12 words" $ property toMnemonic128-        it "generate 18 words" $ property toMnemonic160-        it "generate 24 words" $ property toMnemonic256-        it "generate 48 words" $ property toMnemonic512-        it "generate any number of words" $ property toMnemonicVar-        it "encode and decode 128-bit entropy" $ property fromToMnemonic128-        it "encode and decode 160-bit entropy" $ property fromToMnemonic160-        it "encode and decode 256-bit entropy" $ property fromToMnemonic256-        it "encode and decode 512-bit entropy" $ property fromToMnemonic512-        it "encode and decode n-bit entropy" $ property fromToMnemonicVar-        it "convert 128-bit mnemonic to seed" $ property mnemonicToSeed128-        it "convert 160-bit mnemonic to seed" $ property mnemonicToSeed160-        it "convert 256-bit mnemonic to seed" $ property mnemonicToSeed256-        it "convert 512-bit mnemonic to seed" $ property mnemonicToSeed512-        it "convert n-bit mnemonic to seed" $ property mnemonicToSeedVar-        it "get bits" $ property getBitsByteCount-        it "get end bits" $ property getBitsEndBits--toMnemonicTest :: Assertion-toMnemonicTest = zipWithM_ f ents mss-  where-    f e m = assertEqual "" m . h $ e-    h =-        fromRight (error "Could not decode mnemonic sentence") .-        toMnemonic . fromJust . decodeHex--fromMnemonicTest :: Assertion-fromMnemonicTest = zipWithM_ f ents mss-  where-    f e = assertEqual "" e . h-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic sentence") . fromMnemonic--mnemonicToSeedTest :: Assertion-mnemonicToSeedTest = zipWithM_ f mss seeds-  where-    f m s = assertEqual "" s . h $ m-    h =-        encodeHex .-        fromRight (error "Could not decode mnemonic seed") .-        mnemonicToSeed "TREZOR"--fromMnemonicInvalidTest :: Assertion-fromMnemonicInvalidTest = mapM_ f invalidMss-  where-    f = assertBool "" . h-    h m = case fromMnemonic m of-            Right _  -> False-            Left err -> "fromMnemonic: checksum failed:" `isPrefixOf` err--emptyMnemonicTest :: Assertion-emptyMnemonicTest =-    assertBool "" $-    case fromMnemonic "" of-        Right _  -> False-        Left err -> "fromMnemonic: empty mnemonic" `isPrefixOf` err--ents :: [Text]-ents =-    [ "00000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "80808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffff"-    , "000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffff"-    , "0000000000000000000000000000000000000000000000000000000000000000"-    , "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"-    , "8080808080808080808080808080808080808080808080808080808080808080"-    , "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"-    , "77c2b00716cec7213839159e404db50d"-    , "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b"-    , "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982"-    , "0460ef47585604c5660618db2e6a7e7f"-    , "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f"-    , "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416"-    , "eaebabb2383351fd31d703840b32e9e2"-    , "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78"-    , "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef"-    , "18ab19a9f54a9274f03e5209a2ac8a91"-    , "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4"-    , "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419"-    ]--mss :: [Mnemonic]-mss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon about"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ yellow"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage above"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon agent"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal will"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter always"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ when"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon art"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth title"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic bless"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo vote"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood bridge"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge swap"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion exotic"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ yellow"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter left"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion taste"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ shell"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price segment"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing top"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ cat"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum chief"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ coconut"-    ]--seeds :: [Text]-seeds =-    [ "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a69\-      \87599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04"-    , "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1\-      \296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607"-    , "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f1\-      \2eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8"-    , "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a133325729\-      \17f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069"-    , "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9\-      \a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa"-    , "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c3\-      \92d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd"-    , "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913f\-      \fb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65"-    , "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43\-      \348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528"-    , "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4\-      \d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8"-    , "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146a\-      \d717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87"-    , "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61a\-      \f0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f"-    , "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0\-      \a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad"-    , "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f6\-      \72a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff"-    , "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941\-      \c69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5"-    , "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da2\-      \0af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67"-    , "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41\-      \f8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4"-    , "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349db\-      \e2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba"-    , "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f\-      \86360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449"-    , "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c4\-      \04f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c"-    , "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b079\-      \403485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79"-    , "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b\-      \14d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c"-    , "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe0\-      \05831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8"-    , "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf0\-      \7c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9"-    , "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb\-      \86a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd"-    ]--invalidMss :: [Mnemonic]-invalidMss =-    [ "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ thank"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage sausage"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter abandon"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo"-    , "abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon abandon abandon abandon\-      \ abandon abandon abandon abandon abandon abandon"-    , "legal winner thank year wave sausage worth useful legal winner thank\-      \ year wave sausage worth useful legal winner thank year wave sausage\-      \ worth letter"-    , "letter advice cage absurd amount doctor acoustic avoid letter advice\-      \ cage absurd amount doctor acoustic avoid letter advice cage absurd\-      \ amount doctor acoustic letter"-    , "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo\-      \ zoo zoo zoo zoo zoo zoo"-    , "jelly better achieve collect unaware mountain thought cargo oxygen act\-      \ hood zoo"-    , "renew stay biology evidence goat welcome casual join adapt armor shuffle\-      \ fault little machine walk stumble urge zoo"-    , "dignity pass list indicate nasty swamp pool script soccer toe leaf photo\-      \ multiply desk host tomato cradle drill spread actor shine dismiss\-      \ champion zoo"-    , "afford alter spike radar gate glance object seek swamp infant panel\-      \ zoo"-    , "indicate race push merry suffer human cruise dwarf pole review arch keep\-      \ canvas theme poem divorce alter zoo"-    , "clutch control vehicle tonight unusual clog visa ice plunge glimpse\-      \ recipe series open hour vintage deposit universe tip job dress radar\-      \ refuse motion zoo"-    , "turtle front uncle idea crush write shrug there lottery flower risk\-      \ zoo"-    , "kiss carry display unusual confirm curtain upgrade antique rotate hello\-      \ void custom frequent obey nut hole price zoo"-    , "exile ask congress lamp submit jacket era scheme attend cousin alcohol\-      \ catch course end lucky hurt sentence oven short ball bird grab wing zoo"-    , "board flee heavy tunnel powder denial science ski answer betray cargo\-      \ zoo"-    , "board blade invite damage undo sun mimic interest slam gaze truly\-      \ inherit resist great inject rocket museum zoo"-    , "beyond stage sleep clip because twist token leaf atom beauty genius food\-      \ business side grid unable middle armed observe pair crouch tonight away\-      \ zoo"-    ]--binWordsToBS :: Serialize a => [a] -> BS.ByteString-binWordsToBS = foldr f BS.empty-  where-    f b a = a `BS.append` encode b--{- Encode mnemonic -}--toMnemonic128 :: (Word64, Word64) -> Bool-toMnemonic128 (a, b) = l == 12-  where-    bs = encode a `BS.append` encode b-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic senttence") $-        toMnemonic bs--toMnemonic160 :: (Word32, Word64, Word64) -> Bool-toMnemonic160 (a, b, c) = l == 15-  where-    bs = BS.concat [encode a, encode b, encode c]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-toMnemonic256 (a, b, c, d) = l == 24-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-toMnemonic512 ((a, b, c, d), (e, f, g, h)) = l == 48-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    l =-        length .-        T.words . fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--toMnemonicVar :: [Word32] -> Property-toMnemonicVar ls = not (null ls) && length ls <= 8 ==> l == wc-  where-    bs = binWordsToBS ls-    bl = BS.length bs-    cb = bl `div` 4-    wc = (cb + bl * 8) `div` 11-    l =-        length . T.words .-        fromRight (error "Could not decode mnemonic sentence") $-        toMnemonic bs--{- Encode/Decode -}--fromToMnemonic128 :: (Word64, Word64) -> Bool-fromToMnemonic128 (a, b) = bs == bs'-  where-    bs = encode a `BS.append` encode b-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic160 :: (Word32, Word64, Word64) -> Bool-fromToMnemonic160 (a, b, c) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic256 :: (Word64, Word64, Word64, Word64) -> Bool-fromToMnemonic256 (a, b, c, d) = bs == bs'-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonic512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-fromToMnemonic512 ((a, b, c, d), (e, f, g, h)) = bs == bs'-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--fromToMnemonicVar :: [Word32] -> Property-fromToMnemonicVar ls = not (null ls) && length ls <= 8 ==> bs == bs'-  where-    bs = binWordsToBS ls-    bs' =-        fromRight-            (error "Could not decode mnemonic entropy")-            (fromMnemonic =<< toMnemonic bs)--{- Mnemonic to seed -}--mnemonicToSeed128 :: (Word64, Word64) -> Bool-mnemonicToSeed128 (a, b) = l == 64-  where-    bs = encode a `BS.append` encode b-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed160 :: (Word32, Word64, Word64) -> Bool-mnemonicToSeed160 (a, b, c) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed256 :: (Word64, Word64, Word64, Word64) -> Bool-mnemonicToSeed256 (a, b, c, d) = l == 64-  where-    bs = BS.concat [encode a, encode b, encode c, encode d]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeed512 ::-    ((Word64, Word64, Word64, Word64), (Word64, Word64, Word64, Word64)) -> Bool-mnemonicToSeed512 ((a, b, c, d), (e, f, g, h)) = l == 64-  where-    bs =-        BS.concat-            [ encode a-            , encode b-            , encode c-            , encode d-            , encode e-            , encode f-            , encode g-            , encode h-            ]-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--mnemonicToSeedVar :: [Word32] -> Property-mnemonicToSeedVar ls = not (null ls) && length ls <= 16 ==> l == 64-  where-    bs = binWordsToBS ls-    seed =-        fromRight-            (error "Could not decode mnemonic seed")-            (mnemonicToSeed "" =<< toMnemonic bs)-    l = BS.length seed--{- Get bits from ByteString -}--data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show--instance Arbitrary ByteCountGen where-    arbitrary = do-        bs <- arbitraryBS-        i <- choose (0, BS.length bs * 8)-        return $ ByteCountGen bs i--getBitsByteCount :: ByteCountGen -> Bool-getBitsByteCount (ByteCountGen bs i) = BS.length bits == l-  where-    (q, r) = i `quotRem` 8-    bits = getBits i bs-    l = if r == 0 then q else q + 1--getBitsEndBits :: ByteCountGen -> Bool-getBitsEndBits (ByteCountGen bs i) =-    (r == 0) || (BS.last bits .&. (0xff `shiftR` r) == 0x00)-  where-    r = i `mod` 8-    bits = getBits i bs
− test/Network/Haskoin/KeysSpec.hs
@@ -1,72 +0,0 @@-module Network.Haskoin.KeysSpec (spec) where--import           Data.Aeson                as A-import qualified Data.ByteString           as BS-import           Data.Map.Strict           (singleton)-import           Data.Serialize            as S-import           Data.String               (fromString)-import           Data.String.Conversions   (cs)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.QuickCheck--spec :: Spec-spec =-    describe "keys" $ do-        let net = btc-        it "is public key canonical" $-            property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)-        it "encode and decode wif private keys" $-            property $-            forAll arbitraryKeyPair $ \(pk, _) ->-                fromWif net (toWif net pk) == Just pk-        it "encode and decode serialized private key" $-            property $ forAll arbitrary binaryPrvKey-        it "read and show public key" $-            property $ forAll arbitraryKeyPair $ \(_, k) -> read (show k) == k-        it "read and show private key" $-            property $ forAll arbitrarySecKeyI $ \k -> read (show k) == k-        it "from string public key" $-            property $-            forAll arbitraryKeyPair $ \(_, k) ->-                fromString (cs . encodeHex $ S.encode k) == k-        it "encode and decode json public key" $-            property $ forAll arbitraryKeyPair (testID . snd)-        it "encodes and decodes serialized public key" $-            property $ forAll arbitraryKeyPair $ cerealID . snd----- github.com/bitcoin/bitcoin/blob/master/src/script.cpp--- from function IsCanonicalPubKey-isCanonicalPubKey :: PubKeyI -> Bool-isCanonicalPubKey p = not $-    -- Non-canonical public key: too short-    (BS.length bs < 33) ||-    -- Non-canonical public key: invalid length for uncompressed key-    (BS.index bs 0 == 4 && BS.length bs /= 65) ||-    -- Non-canonical public key: invalid length for compressed key-    (BS.index bs 0 `elem` [2,3] && BS.length bs /= 33) ||-    -- Non-canonical public key: compressed nor uncompressed-    (BS.index bs 0 `notElem` [2,3,4])-  where-    bs = S.encode p--{- Key formats -}--binaryPrvKey :: SecKey -> Bool-binaryPrvKey k =-    Right k == runGet secKeyGet (runPut (secKeyPut k)) &&-    Just k == secKey (getSecKey k)--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x
− test/Network/Haskoin/NetworkSpec.hs
@@ -1,187 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.NetworkSpec (spec) where--import           Data.Maybe                  (fromJust)-import           Data.Serialize              as S-import           Data.Text                   (Text)-import           Data.Word                   (Word32)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Network-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                  (Assertion, assertBool,-                                              assertEqual)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "bloom filters" $ do-        it "bloom filter vector 1" bloomFilter1-        it "bloom filter vector 2" bloomFilter2-        it "bloom filter vector 3" bloomFilter3-    describe "relevant bloom filter update" $ do-        it "Relevant Update" relevantOutputUpdated-        it "Irrelevant Update" irrelevantOutputNotUpdated-    describe "serialization of protocol types" $ do-        it "encodes and decodes varint" $-            property $ forAll arbitraryVarInt cerealID-        it "encodes and decodes varstring" $-            property $ forAll arbitraryVarString cerealID-        it "encodes and decodes network address" $-            property $ forAll arbitraryNetworkAddress cerealID-        it "encodes and decodes invtype" $-            property $ forAll arbitraryInvType cerealID-        it "encodes and decodes invvector" $-            property $ forAll arbitraryInvVector cerealID-        it "encodes and decodes inv" $ property $ forAll arbitraryInv1 cerealID-        it "encodes and decodes version" $-            property $ forAll arbitraryVersion cerealID-        it "encodes and decodes addr" $ property $ forAll arbitraryAddr1 cerealID-        it "encodes and decodes alert" $ property $ forAll arbitraryAlert cerealID-        it "encodes and decodes reject" $-            property $ forAll arbitraryReject cerealID-        it "encodes and decodes getdata" $-            property $ forAll arbitraryGetData cerealID-        it "encodes and decodes notfound" $-            property $ forAll arbitraryNotFound cerealID-        it "encodes and decodes ping" $ property $ forAll arbitraryPing cerealID-        it "encodes and decodes pong" $ property $ forAll arbitraryPong cerealID-        it "encodes and decodes message command" $-            property $ forAll arbitraryMessageCommand cerealID-        it "encodes and decodes message header" $-            property $ forAll arbitraryMessageHeader cerealID-        it "encodes and decodes message" $-            property $-            forAll (arbitraryMessage net) $-            testPutGet (getMessage net) (putMessage net)-    describe "serialization of bloom types" $ do-        it "encodes and decodes bloom flags" $-            property $ forAll arbitraryBloomFlags cerealID-        it "encodes and decodes bloom filter" $-            property $ forAll arbitraryBloomFilter $ cerealID . lst3-        it "encodes and decodes filterload" $-            property $ forAll arbitraryFilterLoad cerealID-        it "encodes and decodes filteradd" $-            property $ forAll arbitraryFilterAdd cerealID--bloomFilter :: Word32 -> Text -> Assertion-bloomFilter n x = do-    assertBool "Bloom filter doesn't contain vector 1" $ bloomContains f1 v1-    assertBool "Bloom filter contains something it should not" $-        not $ bloomContains f1 v2-    assertBool "Bloom filter doesn't contain vector 3" $ bloomContains f3 v3-    assertBool "Bloom filter doesn't contain vector 4" $ bloomContains f4 v4-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f4 == bs-  where-    f0 = bloomCreate 3 0.01 n BloomUpdateAll-    f1 = bloomInsert f0 v1-    f3 = bloomInsert f1 v3-    f4 = bloomInsert f3 v4-    v1 = fromJust $ decodeHex "99108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v2 = fromJust $ decodeHex "19108ad8ed9bb6274d3980bab5a85c048f0950c8"-    v3 = fromJust $ decodeHex "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"-    v4 = fromJust $ decodeHex "b9300670b4c5366e95b2699e8b18bc75e5f729c5"-    bs = fromJust $ decodeHex x--bloomFilter1 :: Assertion-bloomFilter1 = bloomFilter 0 "03614e9b050000000000000001"--bloomFilter2 :: Assertion-bloomFilter2 = bloomFilter 2147483649 "03ce4299050000000100008001"--bloomFilter3 :: Assertion-bloomFilter3 =-    assertBool "Bloom filter serialization is incorrect" $-        S.encode f2 == bs-  where-    f0 = bloomCreate 2 0.001 0 BloomUpdateAll-    f1 = bloomInsert f0 $ S.encode p-    f2 = bloomInsert f1 $ S.encode $ getAddrHash160 $ pubKeyAddr p-    k = fromJust $ fromWif btc "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"-    p = derivePubKeyI k-    bs = fromJust $ decodeHex "038fc16b080000000000000001"--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--testPutGet :: Eq a => Get a -> Putter a -> a -> Bool-testPutGet g p a = runGet g (runPut (p a)) == Right a--relevantOutputUpdated :: Assertion-relevantOutputUpdated = assertBool "Bloom filter output updated" $-    any (bloomContains bf2) spendTxInput-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = fromJust $ bloomRelevantUpdate bf1 relevantTx-        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx--irrelevantOutputNotUpdated :: Assertion-irrelevantOutputNotUpdated = assertEqual "Bloom filter not updated" Nothing bf2-    where-        bf0 = bloomCreate 10 0.000001 0 BloomUpdateAll-        relevantOutputHash = fromJust $ decodeHex"03f47604ea2736334151081e13265b4fe38e6fa8"-        bf1 = bloomInsert bf0 relevantOutputHash-        bf2 = bloomRelevantUpdate bf1 unrelatedTx-        spendTxInput = (encode .prevOutput) <$> txIn spendRelevantTx---- Random transaction (57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9)-relevantTx :: Tx-relevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "35fe9017b7e3af592920b56fa06ac02faf0c52cdb19dcb416129ac71c95d060e" 1-            , scriptInput = fromJust $ decodeHex "473044022032fc8eef299b7e94b9a986a6aa2dcb9733ab804bef80df995e443b9c1f8c604202203335df7a2e2b4789451cdb4b2b05a786a81c51519eb6a567fd6fe8cd7b2d33fe014104272502dc63a512dad1473cb82a71be9baf4f4303abd1ff6028fc8a78e1f3aec1218907119dec14f07354850758ff0948e88a904fa411c4df7d5444414ec64ad6"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 100000000, scriptOutput = fromJust $ decodeHex "76a91403f47604ea2736334151081e13265b4fe38e6fa888ac" }-            , TxOut { outValue = 107980000, scriptOutput = fromJust $ decodeHex "76a91481cc186a2f4a69f633ed4bf10ef4a78be13effdd88ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- Transaction that spends above (fd6e3b693b844aa431fad46765c1aa019a6b13aebfa9dae916b3ffa43283a300)-spendRelevantTx :: Tx-spendRelevantTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "57dc904f32ad4daab7b321dd469e8791ad09df784cdd273a73985150a4f225e9" 0-            , scriptInput = fromJust $ decodeHex "483045022100ecc334821e4e94cc2fdc841d5ad147d5bb942b993ba81460cc446e0410afa811022015fcbc542b734dbb61a05ec06012095096de5839c50808fe56f2b315e877c20d012103fb64e5792fa586172339b776b7017d3d529358cb73be6406a1fc994228d14f88"-            , txInSequence = 4294967295-            }, TxIn-            { prevOutput = OutPoint "cfee6a8d6e68e8fd16df6fff010afffcd19d7e075aa7b707dd1bae6adc420042" 0-            , scriptInput = fromJust $ decodeHex "47304402200e6bb95fa606f254d17089d83c4ceeb19c5d1699b4faddcd4f1f1568286e6b650220087fb8439f31e1b30e47710d095422405f601d6151f2f93e125e1a08a6e29ad4012103b49252e8fc6d5b49c8d14ee71fab45591df4a126a6c453c724f3d356e38f0cee"-            , txInSequence = 4294967295-            } ]-        , txOut =-            [ TxOut { outValue = 3851100, scriptOutput = fromJust $ decodeHex "76a914a297cae82a9a3b932bf023ae274fe2585295c9ca88ac" }-            , TxOut { outValue = 111000000, scriptOutput = fromJust $ decodeHex "76a9148f952c38600a61385974acc30a64f74407f9801488ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }---- This random transaction is unrelated to the others-unrelatedTx :: Tx-unrelatedTx = Tx-        { txVersion = 1-        , txIn = [ TxIn-            { prevOutput = OutPoint "3ec3a71431c68e5d978a5fb4a0a1081d8bee8384d8aa4c06b1fbaf9413e2214f" 20-            , scriptInput = fromJust $ decodeHex "483045022100ec9c202c9d3140b973aca9d7f21a82138aa4cfa43fddc5419098ac5e26a6f152022010848fd688f290ae010fb5cb493410caa03145fc12445900ec1ad2bde33aecd9012102c7445e72d723f99a0064526c28269d07f47c8fd81531a94a8d3bf5ebd5e23ef1"-            , txInSequence = 4294967295-            }]-        , txOut =-            [ TxOut { outValue = 12600000, scriptOutput = fromJust $ decodeHex "76a9148fef3b7051de8cc44e966159e7ea37f4520187e888ac" }-            ]-        , txWitness = []-        , txLockTime = 0-        }
− test/Network/Haskoin/ScriptSpec.hs
@@ -1,406 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Network.Haskoin.ScriptSpec (spec) where--import           Control.Monad-import           Data.Aeson                  as A-import           Data.ByteString             (ByteString)-import qualified Data.ByteString             as B-import qualified Data.ByteString.Lazy        as L-import           Data.Either-import           Data.List-import           Data.Map.Strict             (singleton)-import           Data.Maybe-import           Data.Monoid                 ((<>))-import           Data.Serialize              as S-import           Data.String-import           Data.String.Conversions     (cs)-import           Data.Text                   (Text)-import           Data.Word-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.HUnit                  as HUnit-import           Test.QuickCheck-import           Text.Read--spec :: Spec-spec = do-    let net = btc-    describe "btc scripts" $ props btc-    describe "bch scripts" $ props bch-    describe "multi signatures" $-        zipWithM_ (curry mapMulSigVector) mulSigVectors [0 ..]-    describe "signature decoding" $-        zipWithM_ (curry (sigDecodeMap net)) scriptSigSignatures [0 ..]-    describe "json serialization" $ do-        it "encodes and decodes script output" $-            forAll (arbitraryScriptOutput net) testID-        it "encodes and decodes outpoint" $ forAll arbitraryOutPoint testID-        it "encodes and decodes sighash" $ forAll arbitrarySigHash testID-        it "encodes and decodes siginput" $-            forAll (arbitrarySigInput net) (testID . fst)-    describe "script serialization" $ do-        it "encodes and decodes script op" $-            property $ forAll arbitraryScriptOp cerealID-        it "encodes and decodes script" $-            property $ forAll arbitraryScript cerealID--props :: Network -> Spec-props net = do-    standardSpec net-    strictSigSpec net-    scriptSpec net-    txSigHashForkIdSpec net-    forkIdScriptSpec net-    sigHashSpec net-    txSigHashSpec net--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--standardSpec :: Network -> Spec-standardSpec net = do-    it "has intToScriptOp . scriptOpToInt identity" $-        property $-        forAll arbitraryIntScriptOp $ \i ->-            intToScriptOp <$> scriptOpToInt i `shouldBe` Right i-    it "has decodeOutput . encodeOutput identity" $-        property $-        forAll (arbitraryScriptOutput net) $ \so ->-            decodeOutput (encodeOutput so) `shouldBe` Right so-    it "has decodeInput . encodeOutput identity" $-        property $-        forAll (arbitraryScriptInput net) $ \si ->-            decodeInput net (encodeInput si) `shouldBe` Right si-    it "can sort multisig scripts" $-        forAll arbitraryMSOutput $ \out ->-            map S.encode (getOutputMulSigKeys (sortMulSig out)) `shouldSatisfy` \xs ->-                xs == sort xs-    it "can decode inputs with empty signatures" $ do-        decodeInput net (Script [OP_0]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        decodeInput net (Script [opPushData ""]) `shouldBe`-            Right (RegularInput (SpendPK TxSignatureEmpty))-        let pk =-                derivePubKeyI $-                wrapSecKey True $ fromJust $ secKey $ B.replicate 32 1-        decodeInput net (Script [OP_0, opPushData $ S.encode pk]) `shouldBe`-            Right (RegularInput (SpendPKHash TxSignatureEmpty pk))-        decodeInput net (Script [OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig [TxSignatureEmpty]))-        decodeInput net (Script [OP_0, OP_0, OP_0, OP_0]) `shouldBe`-            Right (RegularInput (SpendMulSig $ replicate 3 TxSignatureEmpty))--scriptSpec :: Network -> Spec-scriptSpec net =-    when (getNetworkName net == "btc") $-    it "can verify standard scripts from script_tests.json file" $ do-        xs <- readTestFile "script_tests" :: IO [A.Value]-        let vectorsA =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-            vectorsB =-                mapMaybe (A.decode . A.encode) xs :: [( [Word64]-                                                      , String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-            vectors =-                map (\(a, b, c, d, e) -> ([0], a, b, c, d, e)) vectorsA <>-                vectorsB-        length vectors `shouldBe` 86-        forM_ vectors $ \([val], siStr, soStr, flags, res, desc)-          -- We can disable specific tests by adding a DISABLED flag in the data-         ->-            unless ("DISABLED" `isInfixOf` flags) $ do-                let _strict =-                        "DERSIG" `isInfixOf` flags ||-                        "STRICTENC" `isInfixOf` flags ||-                        "NULLDUMMY" `isInfixOf` flags-                    scriptSig     = parseScript siStr-                    scriptPubKey  = parseScript soStr-                    decodedOutput = decodeOutputBS scriptPubKey-                    ver = either (const False) $ \so ->-                        verifyStdInput-                            net-                            (spendTx scriptPubKey 0 scriptSig)-                            0-                            so-                            (val * 100000000)-                case res of-                    "OK" -> assertBool desc $ ver decodedOutput-                    _    -> assertBool desc (not $ ver decodedOutput)--forkIdScriptSpec :: Network -> Spec-forkIdScriptSpec net =-    when (isJust (getSigHashForkId net)) $-    it "can verify scripts from forkid_script_tests.json file" $ do-        xs <- readTestFile "forkid_script_tests" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( [Word64]-                                                      , String-                                                      , String-                                                      , String-                                                      , String-                                                      , String)]-        length vectors `shouldBe` 3-        forM_ vectors $ \([valBTC], siStr, soStr, _, res, _) -> do-            let val = valBTC * 100000000-                scriptSig = parseScript siStr-                scriptPubKey = parseScript soStr-                decodedOutput =-                    fromRight (error $ "Could not decode output: " <> soStr) $-                    decodeOutputBS scriptPubKey-                ver =-                    verifyStdInput-                        net-                        (spendTx scriptPubKey val scriptSig)-                        0-                        decodedOutput-                        val-            case res of-                "OK" -> ver `shouldBe` True-                _    -> ver `shouldBe` False--creditTx :: ByteString -> Word64 -> Tx-creditTx scriptPubKey val =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = scriptPubKey}-    txI =-        TxIn-        { prevOutput = nullOutPoint-        , scriptInput = S.encode $ Script [OP_0, OP_0]-        , txInSequence = maxBound-        }--spendTx :: ByteString -> Word64 -> ByteString -> Tx-spendTx scriptPubKey val scriptSig =-    Tx 1 [txI] [txO] [] 0-  where-    txO = TxOut {outValue = val, scriptOutput = B.empty}-    txI =-        TxIn-        { prevOutput = OutPoint (txHash $ creditTx scriptPubKey val) 0-        , scriptInput = scriptSig-        , txInSequence = maxBound-        }--parseScript :: String -> ByteString-parseScript str =-    B.concat $ fromMaybe err $ mapM f $ words str-  where-    f = decodeHex . cs . dropHex . replaceToken-    dropHex ('0':'x':xs) = xs-    dropHex xs           = xs-    err = error $ "Could not decode script: " <> str--replaceToken :: String -> String-replaceToken str = case readMaybe $ "OP_" <> str of-    Just opcode -> "0x" <> cs (encodeHex $ S.encode (opcode :: ScriptOp))-    _           -> str--strictSigSpec :: Network -> Spec-strictSigSpec net =-    when (getNetworkName net == "btc") $ do-        it "can decode strict signatures" $ do-            xs <- readTestFile "sig_strict"-            let vectors = mapMaybe decodeHex xs-            length vectors `shouldBe` 3-            forM_ vectors $ \sig ->-                decodeTxSig net sig `shouldSatisfy` isRight-        it "can detect non-strict signatures" $ do-            xs <- readTestFile "sig_nonstrict"-            let vectors = mapMaybe decodeHex xs-            length vectors `shouldBe` 17-            forM_ vectors $ \sig ->-                decodeTxSig net sig `shouldSatisfy` isLeft--txSigHashSpec :: Network -> Spec-txSigHashSpec net =-    when (getNetworkName net == "btc") $-    it "can produce valid sighashes from sighash.json test vectors" $ do-        xs <- readTestFile "sighash" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , Int-                                                      , Integer-                                                      , String)]-        length vectors `shouldBe` 500-        forM_ vectors $ \(txStr, scpStr, i, shI, resStr) -> do-            let tx = fromString txStr-                s =-                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $-                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res =-                    eitherToMaybe . S.decode . B.reverse =<<-                    decodeHex (cs resStr)-            Just (txSigHash net tx s 0 i sh) `shouldBe` res--txSigHashForkIdSpec :: Network -> Spec-txSigHashForkIdSpec net =-    when (getNetworkName net == "btc") $-    it "can produce valid sighashes from forkid_sighash.json test vectors" $ do-        xs <- readTestFile "forkid_sighash" :: IO [A.Value]-        let vectors =-                mapMaybe (A.decode . A.encode) xs :: [( String-                                                      , String-                                                      , Int-                                                      , Word64-                                                      , Integer-                                                      , String)]-        length vectors `shouldBe` 13-        forM_ vectors $ \(txStr, scpStr, i, val, shI, resStr) -> do-            let tx = fromString txStr-                s =-                    fromMaybe (error $ "Could not decode script: " <> cs scpStr) $-                    eitherToMaybe . S.decode =<< decodeHex (cs scpStr)-                sh = fromIntegral shI-                res = eitherToMaybe . S.decode =<< decodeHex (cs resStr)-            Just (txSigHashForkId net tx s val i sh) `shouldBe` res--sigHashSpec :: Network -> Spec-sigHashSpec net = do-    it "can read . show" $-        property $ forAll arbitrarySigHash $ \sh -> read (show sh) `shouldBe` sh-    it "can correctly show" $ do-        show (0x00 :: SigHash) `shouldBe` "SigHash " <> show (0x00 :: Word32)-        show (0x01 :: SigHash) `shouldBe` "SigHash " <> show (0x01 :: Word32)-        show (0xff :: SigHash) `shouldBe` "SigHash " <> show (0xff :: Word32)-        show (0xabac3344 :: SigHash) `shouldBe` "SigHash " <>-            show (0xabac3344 :: Word32)-    it "can add a forkid" $ do-        0x00 `sigHashAddForkId` 0x00 `shouldBe` 0x00-        0xff `sigHashAddForkId` 0x00ffffff `shouldBe` 0xffffffff-        0xffff `sigHashAddForkId` 0x00aaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0xaaaaaaaa `shouldBe` 0xaaaaaaff-        0xffff `sigHashAddForkId` 0x00004444 `shouldBe` 0x004444ff-        0xff01 `sigHashAddForkId` 0x44440000 `shouldBe` 0x44000001-        0xff03 `sigHashAddForkId` 0x00550000 `shouldBe` 0x55000003-    it "can extract a forkid" $ do-        sigHashGetForkId 0x00000000 `shouldBe` 0x00000000-        sigHashGetForkId 0x80000000 `shouldBe` 0x00800000-        sigHashGetForkId 0xffffffff `shouldBe` 0x00ffffff-        sigHashGetForkId 0xabac3403 `shouldBe` 0x00abac34-    it "can build some vectors" $ do-        sigHashAll `shouldBe` 0x01-        sigHashNone `shouldBe` 0x02-        sigHashSingle `shouldBe` 0x03-        setForkIdFlag sigHashAll `shouldBe` 0x41-        setAnyoneCanPayFlag sigHashAll `shouldBe` 0x81-        setAnyoneCanPayFlag (setForkIdFlag sigHashAll) `shouldBe` 0xc1-    it "can test flags" $ do-        hasForkIdFlag sigHashAll `shouldBe` False-        hasForkIdFlag (setForkIdFlag sigHashAll) `shouldBe` True-        hasAnyoneCanPayFlag sigHashAll `shouldBe` False-        hasAnyoneCanPayFlag (setAnyoneCanPayFlag sigHashAll) `shouldBe` True-        isSigHashAll sigHashNone `shouldBe` False-        isSigHashAll sigHashAll `shouldBe` True-        isSigHashNone sigHashSingle `shouldBe` False-        isSigHashNone sigHashNone `shouldBe` True-        isSigHashSingle sigHashAll `shouldBe` False-        isSigHashSingle sigHashSingle `shouldBe` True-        isSigHashUnknown sigHashAll `shouldBe` False-        isSigHashUnknown sigHashNone `shouldBe` False-        isSigHashUnknown sigHashSingle `shouldBe` False-        isSigHashUnknown 0x00 `shouldBe` True-        isSigHashUnknown 0x04 `shouldBe` True-    it "can decodeTxSig . encode a TxSignature" $-        property $-        forAll (arbitraryTxSignature net) $ \(_, _, ts) ->-            decodeTxSig net (encodeTxSig ts) `shouldBe` Right ts-    it "can produce the sighash one" $-        property $-        forAll (arbitraryTx net) $ forAll arbitraryScript . testSigHashOne net--testSigHashOne :: Network -> Tx -> Script -> Word64 -> Bool -> Property-testSigHashOne net tx s val acp =-    not (null $ txIn tx) ==>-    if length (txIn tx) > length (txOut tx)-        then res `shouldBe` one-        else res `shouldNotBe` one-  where-    res = txSigHash net tx s val (length (txIn tx) - 1) (f sigHashSingle)-    one = "0100000000000000000000000000000000000000000000000000000000000000"-    f =-        if acp-            then setAnyoneCanPayFlag-            else id--{-- Test Utilities --}--readTestFile :: A.FromJSON a => FilePath -> IO a-readTestFile fp = do-    bs <- L.readFile $ "data/" <> fp <> ".json"-    maybe (error $ "Could not read test file " <> fp) return $ A.decode bs--{- Parse tests from bitcoin-qt repository -}--mapMulSigVector :: ((Text, Text), Int) -> Spec-mapMulSigVector (v, i) =-    it name $ runMulSigVector v-  where-    name = "check multisig vector " <> show i--runMulSigVector :: (Text, Text) -> Assertion-runMulSigVector (a, ops) = assertBool "multisig vector" $ Just a == b-  where-    s = do-        s' <- decodeHex ops-        eitherToMaybe $ S.decode s'-    b = do-        o <- s-        d <- eitherToMaybe $ decodeOutput o-        addrToString btc $ payToScriptAddress d--sigDecodeMap :: Network -> (Text, Int) -> Spec-sigDecodeMap net (_, i) =-    it ("check signature " ++ show i) func-  where-    func = testSigDecode net $ scriptSigSignatures !! i--testSigDecode :: Network -> Text -> Assertion-testSigDecode net str =-    let bs = fromJust $ decodeHex str-        eitherSig = decodeTxSig net bs-    in assertBool-           (unwords-                [ "Decode failed:"-                , fromLeft (error "Decode did not fail") eitherSig-                ]) $-       isRight eitherSig--mulSigVectors :: [(Text, Text)]-mulSigVectors =-    [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"-      , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"-      )-    ]--scriptSigSignatures :: [Text]-scriptSigSignatures =-     -- Signature in input of txid 1983a69265920c24f89aac81942b1a59f7eb30821a8b3fb258f88882b6336053-    [ "304402205ca6249f43538908151fe67b26d020306c0e59fa206cf9f3ccf641f33357119d02206c82f244d04ac0a48024fb9cc246b66e58598acf206139bdb7b75a2941a2b1e401"-      -- Signature in input of txid-      -- fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d.-      -- Strange DER sizes, but in Blockchain. Now invalid as Haskoin can only-      -- decode strict signatures.-      -- "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501"-    ]---testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)
− test/Network/Haskoin/Test.hs
@@ -1,23 +0,0 @@-{-|-Module      : Network.Haskoin.Test-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--Arbitrary instances for testing.--}-module Network.Haskoin.Test-    ( module X-    ) where--import           Network.Haskoin.Test.Address     as X-import           Network.Haskoin.Test.Block       as X-import           Network.Haskoin.Test.Crypto      as X-import           Network.Haskoin.Test.Keys        as X-import           Network.Haskoin.Test.Message     as X-import           Network.Haskoin.Test.Network     as X-import           Network.Haskoin.Test.Script      as X-import           Network.Haskoin.Test.Transaction as X-import           Network.Haskoin.Test.Util        as X
− test/Network/Haskoin/Test/Address.hs
@@ -1,25 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Address-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Address where--import           Network.Haskoin.Address-import           Network.Haskoin.Test.Crypto-import           Test.QuickCheck---- | Arbitrary pay-to-public-key-hash or pay-to-script-hash address.-arbitraryAddress :: Gen Address-arbitraryAddress = oneof [arbitraryPubKeyAddress, arbitraryScriptAddress]---- | Arbitrary pay-to-public-key-hash address.-arbitraryPubKeyAddress :: Gen Address-arbitraryPubKeyAddress = p2pkhAddr <$> arbitraryHash160---- | Arbitrary pay-to-script-hash address.-arbitraryScriptAddress :: Gen Address-arbitraryScriptAddress = p2shAddr <$> arbitraryHash160
− test/Network/Haskoin/Test/Block.hs
@@ -1,69 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Block-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Block where--import           Network.Haskoin.Block.Merkle-import           Network.Haskoin.Block.Common-import           Network.Haskoin.Constants-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Network-import           Network.Haskoin.Test.Transaction-import           Test.QuickCheck---- | Block full or arbitrary transactions.-arbitraryBlock :: Network -> Gen Block-arbitraryBlock net = do-    h   <- arbitraryBlockHeader-    c   <- choose (0,10)-    txs <- vectorOf c (arbitraryTx net)-    return $ Block h txs---- | Block header with random hash.-arbitraryBlockHeader :: Gen BlockHeader-arbitraryBlockHeader =-    BlockHeader <$> arbitrary-                      <*> arbitraryBlockHash-                      <*> arbitraryHash256-                      <*> arbitrary-                      <*> arbitrary-                      <*> arbitrary---- | Arbitrary block hash.-arbitraryBlockHash :: Gen BlockHash-arbitraryBlockHash = BlockHash <$> arbitraryHash256---- | Arbitrary 'GetBlocks' object with at least one block hash.-arbitraryGetBlocks :: Gen GetBlocks-arbitraryGetBlocks =-    GetBlocks <$> arbitrary-              <*> listOf1 arbitraryBlockHash-              <*> arbitraryBlockHash---- | Arbitrary 'GetHeaders' object with at least one block header.-arbitraryGetHeaders :: Gen GetHeaders-arbitraryGetHeaders =-    GetHeaders <$> arbitrary-               <*> listOf1 arbitraryBlockHash-               <*> arbitraryBlockHash---- | Arbitrary 'Headers' object with at least one block header.-arbitraryHeaders :: Gen Headers-arbitraryHeaders =-    Headers <$> listOf1 ((,) <$> arbitraryBlockHeader <*> arbitraryVarInt)---- | Arbitrary 'MerkleBlock' with at least one hash.-arbitraryMerkleBlock :: Gen MerkleBlock-arbitraryMerkleBlock = do-    bh     <- arbitraryBlockHeader-    ntx    <- arbitrary-    hashes <- listOf1 arbitraryHash256-    c      <- choose (1,10)-    flags  <- vectorOf (c*8) arbitrary-    return $ MerkleBlock bh ntx hashes flags-
− test/Network/Haskoin/Test/Crypto.hs
@@ -1,33 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Crypto-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Crypto where--import           Network.Haskoin.Crypto.Hash-import           Network.Haskoin.Test.Util-import           Test.QuickCheck---- | Arbitrary 160-bit hash.-arbitraryHash160 :: Gen Hash160-arbitraryHash160 =-    ripemd160 <$> arbitraryBSn 20---- | Arbitrary 256-bit hash.-arbitraryHash256 :: Gen Hash256-arbitraryHash256 =-    sha256 <$> arbitraryBSn 32---- | Arbitrary 512-bit hash.-arbitraryHash512 :: Gen Hash512-arbitraryHash512 =-    sha512 <$> arbitraryBSn 64---- | Arbitrary 32-bit checksum.-arbitraryCheckSum32 :: Gen CheckSum32-arbitraryCheckSum32 =-    checkSum32 <$> arbitraryBSn 4
− test/Network/Haskoin/Test/Keys.hs
@@ -1,85 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Keys-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Keys where--import           Data.Bits                     (clearBit)-import           Data.List                     (foldl')-import           Data.Word                     (Word32)-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Keys.Extended-import           Network.Haskoin.Test.Crypto-import           Test.QuickCheck---- | Arbitrary private key with arbitrary compressed flag.-arbitrarySecKeyI :: Gen SecKeyI-arbitrarySecKeyI = wrapSecKey <$> arbitrary <*> arbitrary---- | Arbitrary keypair, both either compressed or not.-arbitraryKeyPair :: Gen (SecKeyI, PubKeyI)-arbitraryKeyPair = do-    k <- arbitrarySecKeyI-    return (k, derivePubKeyI k)---- | Arbitrary extended private key.-arbitraryXPrvKey :: Gen XPrvKey-arbitraryXPrvKey =-    XPrvKey <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryHash256-            <*> arbitrary---- | Arbitrary extended public key with its corresponding private key.-arbitraryXPubKey :: Gen (XPrvKey, XPubKey)-arbitraryXPubKey = (\k -> (k, deriveXPubKey k)) <$> arbitraryXPrvKey--{- Custom derivations -}---- | Arbitrary derivation index with last bit unset.-genIndex :: Gen Word32-genIndex = (`clearBit` 31) <$> arbitrary---- | Arbitrary BIP-32 path index. Can be hardened or not.-arbitraryBip32PathIndex :: Gen Bip32PathIndex-arbitraryBip32PathIndex =-    oneof [ Bip32SoftIndex <$> genIndex-          , Bip32HardIndex <$> genIndex-          ]---- | Arbitrary BIP-32 derivation path composed of only hardened derivations.-arbitraryHardPath :: Gen HardPath-arbitraryHardPath = foldl' (:|) Deriv <$> listOf genIndex---- | Arbitrary BIP-32 derivation path composed of only non-hardened derivations.-arbitrarySoftPath :: Gen SoftPath-arbitrarySoftPath = foldl' (:/) Deriv <$> listOf genIndex---- | Arbitrary derivation path composed of hardened and non-hardened derivations.-arbitraryDerivPath :: Gen DerivPath-arbitraryDerivPath = concatBip32Segments <$> listOf arbitraryBip32PathIndex---- | Arbitrary parsed derivation path. Can contain 'ParsedPrv', 'ParsedPub' or--- 'ParsedEmpty' elements.-arbitraryParsedPath :: Gen ParsedPath-arbitraryParsedPath =-    oneof [ ParsedPrv <$> arbitraryDerivPath-          , ParsedPub <$> arbitraryDerivPath-          , ParsedEmpty <$> arbitraryDerivPath-          ]---- | Arbitrary message hash, private key, nonce and corresponding signature. The--- signature is generated with a random message, random private key and a random--- nonce.-arbitrarySignature :: Gen (Hash256, SecKey, Sig)-arbitrarySignature = do-    m <- arbitraryHash256-    key <- arbitrary-    let sig = signHash key m-    return (m, key, sig)
− test/Network/Haskoin/Test/Message.hs
@@ -1,52 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Message-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Message where--import           Network.Haskoin.Constants-import           Network.Haskoin.Network.Message-import           Network.Haskoin.Test.Block-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Network-import           Network.Haskoin.Test.Transaction-import           Test.QuickCheck---- | Arbitrary 'MessageHeader'.-arbitraryMessageHeader :: Gen MessageHeader-arbitraryMessageHeader =-    MessageHeader <$> arbitrary-                  <*> arbitraryMessageCommand-                  <*> arbitrary-                  <*> arbitraryCheckSum32---- | Arbitrary 'Message'.-arbitraryMessage :: Network -> Gen Message-arbitraryMessage net =-    oneof-        [ MVersion <$> arbitraryVersion-        , return MVerAck-        , MAddr <$> arbitraryAddr1-        , MInv <$> arbitraryInv1-        , MGetData <$> arbitraryGetData-        , MNotFound <$> arbitraryNotFound-        , MGetBlocks <$> arbitraryGetBlocks-        , MGetHeaders <$> arbitraryGetHeaders-        , MTx <$> arbitraryTx net-        , MBlock <$> arbitraryBlock net-        , MMerkleBlock <$> arbitraryMerkleBlock-        , MHeaders <$> arbitraryHeaders-        , return MGetAddr-        , MFilterLoad <$> arbitraryFilterLoad-        , MFilterAdd <$> arbitraryFilterAdd-        , return MFilterClear-        , MPing <$> arbitraryPing-        , MPong <$> arbitraryPong-        , MAlert <$> arbitraryAlert-        , MReject <$> arbitraryReject-        , return MSendHeaders-        ]
− test/Network/Haskoin/Test/Network.hs
@@ -1,178 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Network-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Network where--import qualified Data.ByteString             as BS (empty, pack)-import qualified Data.ByteString.Char8       as C8-import           Data.Word                   (Word16, Word32)-import           Network.Haskoin.Network-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Util-import           Network.Socket              (SockAddr (..))-import           Test.QuickCheck---- | Arbitrary 'VarInt'.-arbitraryVarInt :: Gen VarInt-arbitraryVarInt = VarInt <$> arbitrary---- | Arbitrary 'VarString'.-arbitraryVarString :: Gen VarString-arbitraryVarString = VarString <$> arbitraryBS---- | Arbitrary 'NetworkAddress'.-arbitraryNetworkAddress :: Gen NetworkAddress-arbitraryNetworkAddress = do-    s <- arbitrary-    a <- arbitrary-    p <- arbitrary-    d <- oneof-        [ do-            b <- arbitrary-            c <- arbitrary-            d <- arbitrary-            return $ SockAddrInet6 (fromIntegral p) 0 (a,b,c,d) 0-        , return $ SockAddrInet (fromIntegral (p :: Word16)) a-        ]-    let n = sockToHostAddress d-    return $ NetworkAddress s n---- | Arbitrary 'NetworkAddressTime'.-arbitraryNetworkAddressTime :: Gen (Word32, NetworkAddress)-arbitraryNetworkAddressTime = (,) <$> arbitrary <*> arbitraryNetworkAddress---- | Arbitrary 'InvType'.-arbitraryInvType :: Gen InvType-arbitraryInvType = elements [InvError, InvTx, InvBlock, InvMerkleBlock]---- | Arbitrary 'InvVector'.-arbitraryInvVector :: Gen InvVector-arbitraryInvVector = InvVector <$> arbitraryInvType <*> arbitraryHash256---- | Arbitrary non-empty 'Inv'.-arbitraryInv1 :: Gen Inv-arbitraryInv1 = Inv <$> listOf1 arbitraryInvVector---- | Arbitrary 'Version'.-arbitraryVersion :: Gen Version-arbitraryVersion =-    Version <$> arbitrary-            <*> arbitrary-            <*> arbitrary-            <*> arbitraryNetworkAddress-            <*> arbitraryNetworkAddress-            <*> arbitrary-            <*> arbitraryVarString-            <*> arbitrary-            <*> arbitrary---- | Arbitrary non-empty 'Addr'.-arbitraryAddr1 :: Gen Addr-arbitraryAddr1 = Addr <$> listOf1 arbitraryNetworkAddressTime---- | Arbitrary 'Alert' with random payload and signature. Signature is not--- valid.-arbitraryAlert :: Gen Alert-arbitraryAlert = Alert <$> arbitraryVarString <*> arbitraryVarString---- | Arbitrary 'Reject'.-arbitraryReject :: Gen Reject-arbitraryReject = do-    m <- arbitraryMessageCommand-    c <- arbitraryRejectCode-    s <- arbitraryVarString-    d <- oneof [ return BS.empty-               , BS.pack <$> vectorOf 32 arbitrary-               ]-    return $ Reject m c s d---- | Arbitrary 'RejectCode'.-arbitraryRejectCode :: Gen RejectCode-arbitraryRejectCode =-    elements-        [ RejectMalformed-        , RejectInvalid-        , RejectInvalid-        , RejectDuplicate-        , RejectNonStandard-        , RejectDust-        , RejectInsufficientFee-        , RejectCheckpoint-        ]---- | Arbitrary non-empty 'GetData'.-arbitraryGetData :: Gen GetData-arbitraryGetData = GetData <$> listOf1 arbitraryInvVector---- | Arbitrary 'NotFound'.-arbitraryNotFound :: Gen NotFound-arbitraryNotFound = NotFound <$> listOf1 arbitraryInvVector---- | Arbitrary 'Ping'.-arbitraryPing :: Gen Ping-arbitraryPing = Ping <$> arbitrary---- | Arbitrary 'Pong'.-arbitraryPong :: Gen Pong-arbitraryPong = Pong <$> arbitrary---- | Arbitrary bloom filter flags.-arbitraryBloomFlags :: Gen BloomFlags-arbitraryBloomFlags =-    elements-        [ BloomUpdateNone-        , BloomUpdateAll-        , BloomUpdateP2PubKeyOnly-        ]---- | Arbitrary bloom filter with its corresponding number of elements--- and false positive rate.-arbitraryBloomFilter :: Gen (Int, Double, BloomFilter)-arbitraryBloomFilter = do-    n     <- choose (0,100000)-    fp    <- choose (1e-8,1)-    tweak <- arbitrary-    fl    <- arbitraryBloomFlags-    return (n, fp, bloomCreate n fp tweak fl)---- | Arbitrary 'FilterLoad'.-arbitraryFilterLoad :: Gen FilterLoad-arbitraryFilterLoad = do-    (_, _, bf) <- arbitraryBloomFilter-    return $ FilterLoad bf---- | Arbitrary 'FilterAdd'.-arbitraryFilterAdd :: Gen FilterAdd-arbitraryFilterAdd = FilterAdd <$> arbitraryBS---- | Arbitrary 'MessageCommand'.-arbitraryMessageCommand :: Gen MessageCommand-arbitraryMessageCommand = do-    ASCIIString str <- arbitrary-    elements-        [ MCVersion-        , MCVerAck-        , MCAddr-        , MCInv-        , MCGetData-        , MCNotFound-        , MCGetBlocks-        , MCGetHeaders-        , MCTx-        , MCBlock-        , MCMerkleBlock-        , MCHeaders-        , MCGetAddr-        , MCFilterLoad-        , MCFilterAdd-        , MCFilterClear-        , MCPing-        , MCPong-        , MCAlert-        , MCOther (C8.take 12 (C8.pack (filter (/= '\NUL') str)))-        ]
− test/Network/Haskoin/Test/Script.hs
@@ -1,374 +0,0 @@-{-# LANGUAGE LambdaCase #-}-{-|-Module      : Network.Haskoin.Test.Script-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Script where--import           Crypto.Secp256k1-import           Data.Maybe-import           Data.Word-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Test.Address-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Keys-import           Network.Haskoin.Test.Util-import           Network.Haskoin.Transaction.Common-import           Network.Haskoin.Util-import           Test.QuickCheck---- | Arbitrary 'Script' with random script ops.-arbitraryScript :: Gen Script-arbitraryScript = Script <$> listOf arbitraryScriptOp---- | Arbitrary 'ScriptOp' (push operations have random data).-arbitraryScriptOp :: Gen ScriptOp-arbitraryScriptOp =-    oneof-          -- Pushing Data-        [ opPushData <$> arbitraryBS1-        , return OP_0-        , return OP_1NEGATE-        , return OP_RESERVED-        , return OP_1-        , return OP_2-        , return OP_3-        , return OP_4-        , return OP_5-        , return OP_6-        , return OP_7-        , return OP_8-        , return OP_9-        , return OP_10-        , return OP_11-        , return OP_12-        , return OP_13-        , return OP_14-        , return OP_15-        , return OP_16-        -- Flow control-        , return OP_NOP-        , return OP_VER-        , return OP_IF-        , return OP_NOTIF-        , return OP_VERIF-        , return OP_VERNOTIF-        , return OP_ELSE-        , return OP_ENDIF-        , return OP_VERIFY-        , return OP_RETURN-        -- Stack operations-        , return OP_TOALTSTACK-        , return OP_FROMALTSTACK-        , return OP_IFDUP-        , return OP_DEPTH-        , return OP_DROP-        , return OP_DUP-        , return OP_NIP-        , return OP_OVER-        , return OP_PICK-        , return OP_ROLL-        , return OP_ROT-        , return OP_SWAP-        , return OP_TUCK-        , return OP_2DROP-        , return OP_2DUP-        , return OP_3DUP-        , return OP_2OVER-        , return OP_2ROT-        , return OP_2SWAP-        -- Splice-        , return OP_CAT-        , return OP_SUBSTR-        , return OP_LEFT-        , return OP_RIGHT-        , return OP_SIZE-        -- Bitwise logic-        , return OP_INVERT-        , return OP_AND-        , return OP_OR-        , return OP_XOR-        , return OP_EQUAL-        , return OP_EQUALVERIFY-        , return OP_RESERVED1-        , return OP_RESERVED2-        -- Arithmetic-        , return OP_1ADD-        , return OP_1SUB-        , return OP_2MUL-        , return OP_2DIV-        , return OP_NEGATE-        , return OP_ABS-        , return OP_NOT-        , return OP_0NOTEQUAL-        , return OP_ADD-        , return OP_SUB-        , return OP_MUL-        , return OP_DIV-        , return OP_MOD-        , return OP_LSHIFT-        , return OP_RSHIFT-        , return OP_BOOLAND-        , return OP_BOOLOR-        , return OP_NUMEQUAL-        , return OP_NUMEQUALVERIFY-        , return OP_NUMNOTEQUAL-        , return OP_LESSTHAN-        , return OP_GREATERTHAN-        , return OP_LESSTHANOREQUAL-        , return OP_GREATERTHANOREQUAL-        , return OP_MIN-        , return OP_MAX-        , return OP_WITHIN-        -- Crypto-        , return OP_RIPEMD160-        , return OP_SHA1-        , return OP_SHA256-        , return OP_HASH160-        , return OP_HASH256-        , return OP_CODESEPARATOR-        , return OP_CHECKSIG-        , return OP_CHECKSIGVERIFY-        , return OP_CHECKMULTISIG-        , return OP_CHECKMULTISIGVERIFY-        -- Expansion-        , return OP_NOP1-        , return OP_NOP2-        , return OP_NOP3-        , return OP_NOP4-        , return OP_NOP5-        , return OP_NOP6-        , return OP_NOP7-        , return OP_NOP8-        , return OP_NOP9-        , return OP_NOP10-        -- Other-        , return OP_PUBKEYHASH-        , return OP_PUBKEY-        , return $ OP_INVALIDOPCODE 0xff-        ]---- | Arbtirary 'ScriptOp' with a value in @[OP_1 .. OP_16]@.-arbitraryIntScriptOp :: Gen ScriptOp-arbitraryIntScriptOp =-    elements-        [ OP_1,  OP_2,  OP_3,  OP_4-        , OP_5,  OP_6,  OP_7,  OP_8-        , OP_9,  OP_10, OP_11, OP_12-        , OP_13, OP_14, OP_15, OP_16-        ]---- | Arbitrary 'PushDataType'.-arbitraryPushDataType :: Gen PushDataType-arbitraryPushDataType = elements [OPCODE, OPDATA1, OPDATA2, OPDATA4]---- | Arbitrary 'SigHash' (including invalid/unknown sighash codes).-arbitrarySigHash :: Gen SigHash-arbitrarySigHash = fromIntegral <$> (arbitrary :: Gen Word32)---- | Arbitrary valid 'SigHash'.-arbitraryValidSigHash :: Network -> Gen SigHash-arbitraryValidSigHash net = do-    sh <- elements [sigHashAll, sigHashNone, sigHashSingle]-    f1 <--        elements $-        if isJust (getSigHashForkId net)-            then [id, setForkIdFlag]-            else [id]-    f2 <- elements [id, setAnyoneCanPayFlag]-    return $ f1 $ f2 sh---- | Arbitrary message hash, private key and corresponding 'TxSignature'. The--- signature is generated deterministically using a random message and a random--- private key.-arbitraryTxSignature :: Network -> Gen (TxHash, SecKey, TxSignature)-arbitraryTxSignature net = do-    (m, key, sig) <- arbitrarySignature-    sh <- (fromIntegral <$> (arbitrary :: Gen Word8)) `suchThat` filterBad-    let txsig = TxSignature sig sh-    return (TxHash m, key, txsig)-  where-    filterBad sh = not $-        isSigHashUnknown sh ||-        isNothing (getSigHashForkId net) && hasForkIdFlag sh---- | Arbitrary transaction signature that could also be empty.-arbitraryTxSignatureEmpty :: Network -> Gen TxSignature-arbitraryTxSignatureEmpty net =-    frequency [ (1, return TxSignatureEmpty)-              , (10, lst3 <$> arbitraryTxSignature net)-              ]---- | Arbitrary m of n parameters.-arbitraryMSParam :: Gen (Int, Int)-arbitraryMSParam = do-    m <- choose (1,16)-    n <- choose (m,16)-    return (m, n)---- | Arbitrary 'ScriptOutput' (Can by any valid type).-arbitraryScriptOutput :: Network -> Gen ScriptOutput-arbitraryScriptOutput net =-    oneof $-    [ arbitraryPKOutput-    , arbitraryPKHashOutput-    , arbitraryMSOutput-    , arbitrarySHOutput-    , arbitraryDCOutput-    ] ++-    if getSegWit net-        then [arbitraryWPKHashOutput, arbitraryWSHOutput]-        else []---- | Arbitrary 'ScriptOutput' of type 'PayPK', 'PayPKHash' or 'PayMS'--- (Not 'PayScriptHash', 'DataCarrier', or SegWit)-arbitrarySimpleOutput :: Gen ScriptOutput-arbitrarySimpleOutput =-    oneof-        [ arbitraryPKOutput-        , arbitraryPKHashOutput-        , arbitraryMSOutput-        ]---- | Arbitrary 'ScriptOutput' of type 'PayPK'-arbitraryPKOutput :: Gen ScriptOutput-arbitraryPKOutput =  PayPK . snd <$> arbitraryKeyPair---- | Arbitrary 'ScriptOutput' of type 'PayPKHash'-arbitraryPKHashOutput :: Gen ScriptOutput-arbitraryPKHashOutput = PayPKHash <$> arbitraryHash160---- | Arbitrary 'PayWitnessPKHash' output.-arbitraryWPKHashOutput :: Gen ScriptOutput-arbitraryWPKHashOutput = PayWitnessPKHash <$> arbitraryHash160---- | Arbitrary 'PayWitnessScriptHash' output.-arbitraryWSHOutput :: Gen ScriptOutput-arbitraryWSHOutput = PayWitnessScriptHash <$> arbitraryHash256---- | Arbitrary 'ScriptOutput' of type 'PayMS'.-arbitraryMSOutput :: Gen ScriptOutput-arbitraryMSOutput = do-    (m, n) <- arbitraryMSParam-    keys <- map snd <$> vectorOf n arbitraryKeyPair-    return $ PayMulSig keys m---- | Arbitrary 'ScriptOutput' of type 'PayMS', only using compressed keys.-arbitraryMSOutputC :: Gen ScriptOutput-arbitraryMSOutputC = do-    (m, n) <- arbitraryMSParam-    keys <--        map snd <$>-        vectorOf n (arbitraryKeyPair `suchThat` (pubKeyCompressed . snd))-    return $ PayMulSig keys m---- | Arbitrary 'ScriptOutput' of type 'PayScriptHash'.-arbitrarySHOutput :: Gen ScriptOutput-arbitrarySHOutput = PayScriptHash . getAddrHash160 <$> arbitraryScriptAddress---- | Arbitrary 'ScriptOutput' of type 'DataCarrier'.-arbitraryDCOutput :: Gen ScriptOutput-arbitraryDCOutput = DataCarrier <$> arbitraryBS1---- | Arbitrary 'ScriptInput'.-arbitraryScriptInput :: Network -> Gen ScriptInput-arbitraryScriptInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        , arbitrarySHInput net-        ]---- | Arbitrary 'ScriptInput' of type 'SpendPK', 'SpendPKHash' or 'SpendMulSig'--- (not 'ScriptHashInput')-arbitrarySimpleInput :: Network -> Gen ScriptInput-arbitrarySimpleInput net =-    oneof-        [ arbitraryPKInput net-        , arbitraryPKHashInput net-        , arbitraryMSInput net-        ]---- | Arbitrary 'ScriptInput' of type 'SpendPK'.-arbitraryPKInput :: Network -> Gen ScriptInput-arbitraryPKInput net = RegularInput . SpendPK <$> arbitraryTxSignatureEmpty net---- | Arbitrary 'ScriptInput' of type 'SpendPK'.-arbitraryPKHashInput :: Network -> Gen ScriptInput-arbitraryPKHashInput net = do-    sig <- arbitraryTxSignatureEmpty net-    key <- snd <$> arbitraryKeyPair-    return $ RegularInput $ SpendPKHash sig key---- | Like 'arbitraryPKHashInput' without empty signatures.-arbitraryPKHashInputFull :: Network -> Gen ScriptInput-arbitraryPKHashInputFull net = do-    sig <- lst3 <$> arbitraryTxSignature net-    key <- snd <$> arbitraryKeyPair-    return $ RegularInput $ SpendPKHash sig key---- | Like above but only compressed.-arbitraryPKHashInputFullC :: Network -> Gen ScriptInput-arbitraryPKHashInputFullC net = do-    sig <- lst3 <$> arbitraryTxSignature net-    key <- fmap snd $ arbitraryKeyPair `suchThat` (pubKeyCompressed . snd)-    return $ RegularInput $ SpendPKHash sig key---- | Arbitrary 'ScriptInput' of type 'SpendMulSig'.-arbitraryMSInput :: Network -> Gen ScriptInput-arbitraryMSInput net = do-    m    <- fst <$> arbitraryMSParam-    sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-    return $ RegularInput $ SpendMulSig sigs---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput'.-arbitrarySHInput :: Network -> Gen ScriptInput-arbitrarySHInput net = do-    i <- arbitrarySimpleInput net-    ScriptHashInput (getRegularInput i) <$> arbitrarySimpleOutput---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a--- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.-arbitraryMulSigSHInput :: Network -> Gen ScriptInput-arbitraryMulSigSHInput net =-    arbitraryMSOutput >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Arbitrary 'ScriptInput' of type 'ScriptHashInput' containing a--- 'RedeemScript' of type 'PayMulSig' and an input of type 'SpendMulSig'.-arbitraryMulSigSHInputC :: Network -> Gen ScriptInput-arbitraryMulSigSHInputC net =-    arbitraryMSOutputC >>= \case-        rdm@(PayMulSig _ m) -> do-          sigs <- vectorOf m (arbitraryTxSignatureEmpty net)-          return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Like 'arbitraryMulSigSHCInput' with no empty signatures.-arbitraryMulSigSHInputFull :: Network -> Gen ScriptInput-arbitraryMulSigSHInputFull net =-    arbitraryMSOutput >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined---- | Like 'arbitraryMulSigSHCInput' with no empty signatures.-arbitraryMulSigSHInputFullC :: Network -> Gen ScriptInput-arbitraryMulSigSHInputFullC net =-    arbitraryMSOutputC >>= \case-        rdm@(PayMulSig _ m) -> do-            sigs <- map lst3 <$> vectorOf m (arbitraryTxSignature net)-            return $ ScriptHashInput (SpendMulSig sigs) rdm-        _ -> undefined
− test/Network/Haskoin/Test/Transaction.hs
@@ -1,275 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Transaction-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Transaction where--import           Control.Monad-import qualified Data.ByteString             as BS-import           Data.Either                 (fromRight)-import           Data.List                   (nub, nubBy, permutations)-import           Data.Word                   (Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys.Common-import           Network.Haskoin.Script-import           Network.Haskoin.Test.Crypto-import           Network.Haskoin.Test.Keys-import           Network.Haskoin.Test.Script-import           Network.Haskoin.Test.Util-import           Network.Haskoin.Transaction-import           Test.QuickCheck---- | Wrapped coin value for testing.-newtype TestCoin = TestCoin { getTestCoin :: Word64 }-    deriving (Eq, Show)--instance Coin TestCoin where-    coinValue = getTestCoin---- | Arbitrary transaction hash (for non-existent transaction).-arbitraryTxHash :: Gen TxHash-arbitraryTxHash = TxHash <$> arbitraryHash256---- | Arbitrary amount of Satoshi as 'Word64' (Between 1 and 21e14)-arbitrarySatoshi :: Network -> Gen TestCoin-arbitrarySatoshi net = TestCoin <$> choose (1, getMaxSatoshi net)---- | Arbitrary 'OutPoint'.-arbitraryOutPoint :: Gen OutPoint-arbitraryOutPoint = OutPoint <$> arbitraryTxHash <*> arbitrary---- | Arbitrary 'TxOut'.-arbitraryTxOut :: Network -> Gen TxOut-arbitraryTxOut net =-    TxOut <$> (getTestCoin <$> arbitrarySatoshi net)-          <*> (encodeOutputBS <$> arbitraryScriptOutput net)---- | Arbitrary 'TxIn'.-arbitraryTxIn :: Network -> Gen TxIn-arbitraryTxIn net =-    TxIn <$> arbitraryOutPoint-         <*> (encodeInputBS <$> arbitraryScriptInput net)-         <*> arbitrary---- | Arbitrary transaction. Can be regular or with witnesses.-arbitraryTx :: Network -> Gen Tx-arbitraryTx net = oneof [arbitraryLegacyTx net, arbitraryWitnessTx net]---- | Arbitrary regular transaction.-arbitraryLegacyTx :: Network -> Gen Tx-arbitraryLegacyTx net = arbitraryWLTx net False---- | Arbitrary witness transaction (witness data is fake).-arbitraryWitnessTx :: Network -> Gen Tx-arbitraryWitnessTx net = arbitraryWLTx net True---- | Arbitrary witness or legacy transaction.-arbitraryWLTx :: Network -> Bool -> Gen Tx-arbitraryWLTx net wit = do-    ni <- choose (0, 5)-    no <--        if wit-            then choose (0, 5)-            else choose-                     ( if ni == 0-                           then 2-                           else 0-                     , 5 -- avoid witness case-                     )-    inps <- vectorOf ni (arbitraryTxIn net)-    outs <- vectorOf no (arbitraryTxOut net)-    let uniqueInps = nubBy (\a b -> prevOutput a == prevOutput b) inps-    w <- if wit then vectorOf (length uniqueInps) (listOf arbitraryBS) else return []-    Tx <$> arbitrary <*> pure uniqueInps <*> pure outs <*> pure w <*> arbitrary---- | Arbitrary transaction containing only inputs of type 'SpendPKHash',--- 'SpendScriptHash' (multisig) and outputs of type 'PayPKHash' and 'PaySH'.--- Only compressed public keys are used.-arbitraryAddrOnlyTx :: Network -> Gen Tx-arbitraryAddrOnlyTx net = do-    ni <- choose (0, 5)-    no <- choose (0, 5)-    inps <- vectorOf ni (arbitraryAddrOnlyTxIn net)-    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)-    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary---- | Like 'arbitraryAddrOnlyTx' without empty signatures in the inputs.-arbitraryAddrOnlyTxFull :: Network -> Gen Tx-arbitraryAddrOnlyTxFull net = do-    ni <- choose (0, 5)-    no <- choose (0, 5)-    inps <- vectorOf ni (arbitraryAddrOnlyTxInFull net)-    outs <- vectorOf no (arbitraryAddrOnlyTxOut net)-    Tx <$> arbitrary <*> pure inps <*> pure outs <*> pure [] <*> arbitrary---- | Arbitrary TxIn that can only be of type 'SpendPKHash' or 'SpendScriptHash'--- (multisig). Only compressed public keys are used.-arbitraryAddrOnlyTxIn :: Network -> Gen TxIn-arbitraryAddrOnlyTxIn net = do-    inp <- oneof [arbitraryPKHashInput net, arbitraryMulSigSHInput net]-    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary---- | like 'arbitraryAddrOnlyTxIn' with no empty signatures.-arbitraryAddrOnlyTxInFull :: Network -> Gen TxIn-arbitraryAddrOnlyTxInFull net = do-    inp <--        oneof [arbitraryPKHashInputFullC net, arbitraryMulSigSHInputFullC net]-    TxIn <$> arbitraryOutPoint <*> pure (encodeInputBS inp) <*> arbitrary---- | Arbitrary 'TxOut' that can only be of type 'PayPKHash' or 'PaySH'.-arbitraryAddrOnlyTxOut :: Network -> Gen TxOut-arbitraryAddrOnlyTxOut net = do-    v <- getTestCoin <$> arbitrarySatoshi net-    out <- oneof [arbitraryPKHashOutput, arbitrarySHOutput]-    return $ TxOut v $ encodeOutputBS out---- | Arbitrary 'SigInput' with the corresponding private keys used--- to generate the 'ScriptOutput' or 'RedeemScript'.-arbitrarySigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitrarySigInput net =-    oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        , arbitrarySHSigInput net-        , wrapKey <$> arbitraryWPKHSigInput net-        , arbitraryWSHSigInput net-        ]---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPK'.-arbitraryPKSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryPKSigInput net = arbitraryAnyInput net False---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayPKHash'.-arbitraryPKHashSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryPKHashSigInput net = arbitraryAnyInput net True---- | Arbitrary 'SigInput'.-arbitraryAnyInput :: Network -> Bool -> Gen (SigInput, SecKeyI)-arbitraryAnyInput net pkh = do-    (k, p) <- arbitraryKeyPair-    let out | pkh = PayPKHash $ getAddrHash160 $ pubKeyAddr p-            | otherwise = PayPK p-    (val, op, sh) <- arbitraryInputStuff net-    return (SigInput out val op sh Nothing, k)---- | Arbitrary value, out point and sighash for an input.-arbitraryInputStuff :: Network -> Gen (Word64, OutPoint, SigHash)-arbitraryInputStuff net = do-    val <- getTestCoin <$> arbitrarySatoshi net-    op <- arbitraryOutPoint-    sh <- arbitraryValidSigHash net-    return (val, op, sh)---- | Arbitrary 'SigInput' with a 'ScriptOutput' of type 'PayMulSig'.-arbitraryMSSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitraryMSSigInput net = do-    (m, n) <- arbitraryMSParam-    ks <- vectorOf n arbitraryKeyPair-    let out = PayMulSig (map snd ks) m-    (val, op, sh) <- arbitraryInputStuff net-    perm <- choose (0, n - 1)-    let ksPerm = map fst $ take m $ permutations ks !! perm-    return (SigInput out val op sh Nothing, ksPerm)---- | Arbitrary 'SigInput' with 'ScriptOutput' of type 'PaySH' and a--- 'RedeemScript'.-arbitrarySHSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitrarySHSigInput net = do-    (SigInput rdm val op sh _, ks) <- oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        ]-    let out = PayScriptHash $ getAddrHash160 $ payToScriptAddress rdm-    return (SigInput out val op sh $ Just rdm, ks)--arbitraryWPKHSigInput :: Network -> Gen (SigInput, SecKeyI)-arbitraryWPKHSigInput net = do-    (k, p) <- arbitraryKeyPair-    (val, op, sh) <- arbitraryInputStuff net-    let out = PayWitnessPKHash . getAddrHash160 $ pubKeyAddr p-    return (SigInput out val op sh Nothing, k)--arbitraryWSHSigInput :: Network -> Gen (SigInput, [SecKeyI])-arbitraryWSHSigInput net = do-    (SigInput rdm val op sh _, ks) <- oneof-        [ wrapKey <$> arbitraryPKSigInput net-        , wrapKey <$> arbitraryPKHashSigInput net-        , arbitraryMSSigInput net-        ]-    let out = PayWitnessScriptHash . getAddrHash256 $ payToWitnessScriptAddress rdm-    return (SigInput out val op sh $ Just rdm, ks)---- | Arbitrary 'Tx' (empty 'TxIn'), 'SigInputs' and private keys that can be--- passed to 'signTx' or 'detSignTx' to fully sign the 'Tx'.-arbitrarySigningData :: Network -> Gen (Tx, [SigInput], [SecKeyI])-arbitrarySigningData net = do-    v <- arbitrary-    ni <- choose (1, 5)-    no <- choose (1, 5)-    sigis <- vectorOf ni (arbitrarySigInput net)-    let uSigis = nubBy (\(a, _) (b, _) -> sigInputOP a == sigInputOP b) sigis-    inps <- forM uSigis $ \(s, _) -> TxIn (sigInputOP s) BS.empty <$> arbitrary-    outs <- vectorOf no (arbitraryTxOut net)-    l <- arbitrary-    perm <- choose (0, length inps - 1)-    let tx = Tx v (permutations inps !! perm) outs [] l-        keys = concatMap snd uSigis-    return (tx, map fst uSigis, keys)---- | Arbitrary transaction with empty inputs.-arbitraryEmptyTx :: Network -> Gen Tx-arbitraryEmptyTx net = do-    v    <- arbitrary-    no   <- choose (1,5)-    ni   <- choose (1,5)-    outs <- vectorOf no (arbitraryTxOut net)-    ops  <- vectorOf ni arbitraryOutPoint-    t    <- arbitrary-    s    <- arbitrary-    return $ Tx v (map (\op -> TxIn op BS.empty s) (nub ops)) outs [] t---- | Arbitrary partially-signed transactions.-arbitraryPartialTxs ::-       Network -> Gen ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)])-arbitraryPartialTxs net = do-    tx <- arbitraryEmptyTx net-    res <--        forM (map prevOutput $ txIn tx) $ \op -> do-            (so, val, rdmM, prvs, m, n) <- arbitraryData-            txs <- mapM (singleSig so val rdmM tx op . secKeyData) prvs-            return (txs, (so, val, op, m, n))-    return (concatMap fst res, map snd res)-  where-    singleSig so val rdmM tx op prv = do-        sh <- arbitraryValidSigHash net-        let sigi = SigInput so val op sh rdmM-        return . fromRight (error "Could not decode transaction") $-            signTx net tx [sigi] [prv]-    arbitraryData = do-        (m, n) <- arbitraryMSParam-        val <- getTestCoin <$> arbitrarySatoshi net-        nPrv <- choose (m, n)-        keys <- vectorOf n arbitraryKeyPair-        perm <- choose (0, length keys - 1)-        let pubKeys = map snd keys-            prvKeys = take nPrv $ permutations (map fst keys) !! perm-        let so = PayMulSig pubKeys m-        elements-            [ (so, val, Nothing, prvKeys, m, n)-            , ( PayScriptHash $ getAddrHash160 $ payToScriptAddress so-              , val-              , Just so-              , prvKeys-              , m-              , n)-            ]--wrapKey :: (SigInput, SecKeyI) -> (SigInput, [SecKeyI])-wrapKey (s, k) = (s, [k])
− test/Network/Haskoin/Test/Util.hs
@@ -1,40 +0,0 @@-{-|-Module      : Network.Haskoin.Test.Util-Copyright   : No rights reserved-License     : UNLICENSE-Maintainer  : jprupp@protonmail.ch-Stability   : experimental-Portability : POSIX--}-module Network.Haskoin.Test.Util where--import           Data.ByteString       (ByteString, pack)-import           Data.Time.Clock       (UTCTime (..))-import           Data.Time.Clock.POSIX (posixSecondsToUTCTime)-import           Data.Word             (Word32)-import           Test.QuickCheck---- | Arbitrary strict 'ByteString'.-arbitraryBS :: Gen ByteString-arbitraryBS = pack <$> arbitrary---- | Arbitrary non-empty strict ByteString-arbitraryBS1 :: Gen ByteString-arbitraryBS1 = pack <$> listOf1 arbitrary---- | Arbitrary strict ByteString of a given length-arbitraryBSn :: Int -> Gen ByteString-arbitraryBSn n = pack <$> vectorOf n arbitrary---- | Arbitrary UTCTime that generates dates after 01 Jan 1970 01:00:00 CET-arbitraryUTCTime :: Gen UTCTime-arbitraryUTCTime = do-    w <- arbitrary :: Gen Word32-    return $ posixSecondsToUTCTime $ realToFrac w---- | Generate a Maybe from a Gen a-arbitraryMaybe :: Gen a -> Gen (Maybe a)-arbitraryMaybe g = frequency [ (1, return Nothing)-                             , (5, Just <$> g)-                             ]-
− test/Network/Haskoin/Transaction/PartialSpec.hs
@@ -1,336 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Network.Haskoin.Transaction.PartialSpec (spec) where--import           Control.Monad.Fail                  (MonadFail)-import           Data.ByteString                     (ByteString)-import           Data.Either                         (fromRight, isLeft,-                                                      isRight)-import           Data.HashMap.Strict                 (fromList, singleton)-import           Data.Maybe                          (fromJust, isJust)-import           Data.Serialize                      as S-import           Data.Text                           (Text)-import           Test.Hspec-import           Test.HUnit                          (Assertion, assertBool,-                                                      assertEqual)-import           Test.QuickCheck--import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Crypto-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Transaction.Partial-import           Network.Haskoin.Util--spec :: Spec-spec = describe "partially signed bitcoin transaction unit tests" $ do-    it "encodes trivial psbt" $-        encodeHex (S.encode trivialPSBT) == trivialPSBTHex-    it "decodes trivial psbt" $-        decodeHexPSBT trivialPSBTHex == Right trivialPSBT-    it "encodes and decodes non-empty transactions" $-        S.decode (S.encode nonEmptyTransactionPSBT) == Right nonEmptyTransactionPSBT-    it "does not decode invalid bip vectors" $-        mapM_ invalidVecTest invalidVec-    it "encodes valid bip vecs" $-        mapM_ (uncurry encodeVecTest) validEncodeVec-    it "decodes valid bip vecs" $-        mapM_ (uncurry decodeVecTest) $ zip [1..] validVec-    it "decodes vector 2" vec2Test-    it "decodes vector 3" vec3Test-    it "decodes vector 4" vec4Test-    it "decodes vector 5" vec5Test-    it "decodes vector 6" vec6Test-    it "signed and finalized p2pkh PSBTs verify" $ property $-        forAll arbitraryKeyPair $ verifyNonWitnessPSBT btc . unfinalizedPkhPSBT btc-    it "signed and finalized multisig PSBTs verify" $ property $-        forAll arbitraryMultiSig $ verifyNonWitnessPSBT btc . unfinalizedMsPSBT btc--vec2Test :: Assertion-vec2Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec2" validVec2Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    assertBool "final script sig" $ isJust (finalScriptSig . head $ inputs psbt)--    let rdmScript = fromJust . inputRedeemScript $ inputs psbt !! 1-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey $ inputs psbt !! 1-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    mapM_ (assertEqual "outputs are empty" emptyOutput) (outputs psbt)--vec3Test :: Assertion-vec3Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec3" validVec3Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let txInput = head . txIn $ unsignedTransaction psbt-        firstInput = head $ inputs psbt-        Just utx = nonWitnessUtxo firstInput-        OutPoint prevHash prevVOut = prevOutput txInput-    assertEqual "txids of inputs match" prevHash (txHash utx)-    let prevOutputKey = fromRight (error "Could not decode key")-                      . decodeOutputBS . scriptOutput $ txOut utx !! fromIntegral prevVOut-    assertBool "p2pkh" $ isPayPKHash prevOutputKey-    assertEqual "sighash type" sigHashAll (fromJust $ sigHashType firstInput)--vec4Test :: Assertion-vec4Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec4" validVec4Hex-    assertEqual "2 inputs" 2 (length $ inputs psbt)-    assertEqual "2 outputs" 2 (length $ outputs psbt)-    let firstInput = head $ inputs psbt-        secondInput = inputs psbt !! 1-    assertEqual "first input not final script sig" Nothing (finalScriptSig firstInput)-    assertEqual "second input not final script sig" Nothing (finalScriptSig secondInput)--    let rdmScript = fromJust $ inputRedeemScript secondInput-    assertBool "p2wpkh" $ (isPayWitnessPKHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey secondInput-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut rdmScriptP2SH--    assertBool "all non-empty outputs" $ emptyOutput `notElem` outputs psbt--vec5Test :: Assertion-vec5Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec5" validVec5Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)-    let input = head $ inputs psbt-    assertEqual "input not final script sig" Nothing (finalScriptSig input)--    let rdmScript = fromJust $ inputRedeemScript input-    assertBool "p2wsh" $ (isPayWitnessScriptHash <$> decodeOutput rdmScript) == Right True--    let scrptPubKey = witnessScriptPubKey input-        rdmScriptP2SH = toP2SH rdmScript-    assertEqual "redeem script pubkey equal" rdmScriptP2SH scrptPubKey-    assertEqual "expected redeem script"  expectedOut2 rdmScriptP2SH-  where-    expectedOut2 = fromRight (error "could not decode expected output")-                . decodeOutputBS . fromJust $ decodeHex "a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87"--vec6Test :: Assertion-vec6Test = do-    psbt <- decodeHexPSBTM "Cannot parse validVec6" validVec6Hex-    assertEqual "1 input" 1 (length $ inputs psbt)-    assertEqual "1 output" 1 (length $ outputs psbt)--    let tx = unsignedTransaction psbt-    assertEqual "correct transaction" "75c5c9665a570569ad77dd1279e6fd4628a093c4dcbf8d41532614044c14c115" (txHash tx)--    assertEqual "correct unknowns" expectedUnknowns (inputUnknown . head $ inputs psbt)-  where-    expectedUnknowns = UnknownMap $ singleton-        (Key 0x0f (fromJust $ decodeHex "010203040506070809"))-        (fromJust $ decodeHex "0102030405060708090a0b0c0d0e0f")--expectedOut :: ScriptOutput-expectedOut = fromRight (error "could not decode expected output")-            . decodeOutputBS . fromJust $ decodeHex "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"--witnessScriptPubKey :: Input -> ScriptOutput-witnessScriptPubKey = fromRight (error "could not decode witness utxo")-                    . decodeOutputBS . scriptOutput . fromJust . witnessUtxo--decodeHexPSBT :: Text -> Either String PartiallySignedTransaction-decodeHexPSBT = S.decode . fromJust . decodeHex--decodeHexPSBTM :: (Monad m, MonadFail m) => String -> Text -> m PartiallySignedTransaction-decodeHexPSBTM errMsg = either (fail . (errMsg <>) . (": " <>)) return . decodeHexPSBT--hexScript :: Text -> ByteString-hexScript =-    either (error "Could not decode script") encodeScript . S.decode . fromJust . decodeHex-  where-    encodeScript :: Script -> ByteString-    encodeScript = S.encode--invalidVecTest :: Text -> Assertion-invalidVecTest = assertBool "invalid psbt" . isLeft . decodeHexPSBT--decodeVecTest :: Int -> Text -> Assertion-decodeVecTest i = assertBool (show i <> " decodes correctly") . isRight . decodeHexPSBT--encodeVecTest :: PartiallySignedTransaction -> Text -> Assertion-encodeVecTest psbt hex = assertEqual "encodes correctly" (S.encode psbt) (fromJust $ decodeHex hex)--trivialPSBT :: PartiallySignedTransaction-trivialPSBT = PartiallySignedTransaction-    { unsignedTransaction = Tx { txVersion = 2, txIn = [], txOut = [], txWitness = [], txLockTime = 0 }-    , globalUnknown = UnknownMap mempty-    , inputs = []-    , outputs = []-    }--trivialPSBTHex :: Text-trivialPSBTHex = "70736274ff01000a0200000000000000000000"--nonEmptyTransactionPSBT :: PartiallySignedTransaction-nonEmptyTransactionPSBT = emptyPSBT testTx1--verifyNonWitnessPSBT :: Network -> PartiallySignedTransaction -> Bool-verifyNonWitnessPSBT net psbt = verifyStdTx net (finalTransaction (complete psbt)) sigData-  where-    sigData = inputSigData =<< zip (inputs psbt) (txIn $ unsignedTransaction psbt)-    decodeOutScript = fromRight (error "Could not parse output script") . decodeOutputBS-    inputSigData (input, txInput) =-        map (\(TxOut val script) -> (decodeOutScript script, val, prevOutput txInput))-            (txOut . fromJust $ nonWitnessUtxo input)--unfinalizedPkhPSBT :: Network -> (SecKeyI, PubKeyI) -> PartiallySignedTransaction-unfinalizedPkhPSBT net (prvKey, pubKey) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = singleton pubKey sig } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = addressToScript (pubKeyAddr pubKey)-    prevOut = TxOut { outValue = 200000000, scriptOutput = S.encode prevOutScript }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sig = encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll--arbitraryMultiSig :: Gen ([(SecKeyI, PubKeyI)], Int)-arbitraryMultiSig = do-    (m, n) <- arbitraryMSParam-    keys <- vectorOf n arbitraryKeyPair-    return (keys, m)--unfinalizedMsPSBT :: Network -> ([(SecKeyI, PubKeyI)], Int) -> PartiallySignedTransaction-unfinalizedMsPSBT net (keys, m) = (emptyPSBT currTx)-    { inputs = [ emptyInput { nonWitnessUtxo = Just prevTx, partialSigs = sigs-                            , inputRedeemScript = Just prevOutScript-                            } ] }-  where-    currTx = unfinalizedTx (txHash prevTx)-    prevTx = testUtxo [prevOut]-    prevOutScript = encodeOutput $ PayMulSig (map snd keys) m-    prevOut = TxOut { outValue = 200000000, scriptOutput = encodeOutputBS (toP2SH prevOutScript) }-    h = txSigHash net currTx prevOutScript (outValue prevOut) 0 sigHashAll-    sigs = fromList $ map sig keys-    sig (prvKey, pubKey) = (pubKey, encodeTxSig $ TxSignature (signHash (secKeyData prvKey) h) sigHashAll)--unfinalizedTx :: TxHash -> Tx-unfinalizedTx prevHash = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint prevHash 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--invalidVec :: [Text]-invalidVec =-    [ "0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100fd0a010200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be4000000006a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa88292feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"-    , "70736274ff000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000000"-    , "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000000"-    , "70736274ff020001550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac000000000002010020955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87210203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd46304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a01020400220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d568102050047522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae210603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd10b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f0000000000020000bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000020700da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903020800da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00210203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca58710d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0203000100000000010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c0002000016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a65010125512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    , "70736274ff0100730200000001301ae986e516a1ec8ac5b4bc6573d32f83b465e23ad76167d68b38e730b4dbdb0000000000ffffffff02747b01000000000017a91403aa17ae882b5d0d54b25d63104e4ffece7b9ea2876043993b0000000017a914b921b1ba6f722e4bfa83b6557a3139986a42ec8387000000000001011f00ca9a3b00000000160014d2d94b64ae08587eefc8eeb187c601e939f9037c00010016001462e9e982fff34dd8239610316b090cd2a3b747cb000100220020876bad832f1d168015ed41232a9ea65a1815d9ef13c0ef8759f64b5b2b278a6521010025512103b7ce23a01c5b4bf00a642537cdfabb315b668332867478ef51309d2bd57f8a8751ae00"-    ]---validEncodeVec :: [(PartiallySignedTransaction, Text)]-validEncodeVec = [(validVec1, validVec1Hex)]--testTx1 :: Tx-testTx1 = Tx-        { txVersion = 2-        , txIn = [ TxIn-            { prevOutput = OutPoint "f61b1742ca13176464adb3cb66050c00787bb3a4eead37e985f2df1e37718126" 0-            , scriptInput = ""-            , txInSequence = 4294967294-            } ]-        , txOut =-            [ TxOut { outValue = 99999699, scriptOutput = hexScript "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" }-            , TxOut { outValue = 100000000, scriptOutput = hexScript "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" }-            ]-        , txWitness = []-        , txLockTime = 1257139-        }--testUtxo :: [TxOut] -> Tx-testUtxo prevOuts = Tx-        { txVersion = 1-        , txIn =-            [ TxIn-                { prevOutput = OutPoint "e567952fb6cc33857f392efa3a46c995a28f69cca4bb1b37e0204dab1ec7a389" 1-                , scriptInput = hexScript "160014be18d152a9b012039daf3da7de4f53349eecb985"-                , txInSequence = 4294967295-                }-            , TxIn-                { prevOutput = OutPoint "b490486aec3ae671012dddb2bb08466bef37720a533a894814ff1da743aaf886" 1-                , scriptInput = hexScript "160014fe3e9ef1a745e974d902c4355943abcb34bd5353"-                , txInSequence = 4294967295-                }-            ]-        , txOut = prevOuts-        , txWitness =-            [-              [ fromJust $ decodeHex "304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c01"-              , fromJust $ decodeHex "03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105"-              ]-            , [ fromJust $ decodeHex "3045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01"-              , fromJust $ decodeHex "0223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab3"-              ]-            ]-        , txLockTime = 0-        }--testUtxo1 :: Tx-testUtxo1 = testUtxo-    [ TxOut { outValue = 200000000, scriptOutput = hexScript "76a91485cff1097fd9e008bb34af709c62197b38978a4888ac" }-    , TxOut { outValue = 190303501938, scriptOutput = hexScript "a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587" }-    ]--validVec1 :: PartiallySignedTransaction-validVec1 = (emptyPSBT testTx1) { inputs = [ emptyInput { nonWitnessUtxo = Just testUtxo1 } ] }--validVec :: [Text]-validVec = [validVec1Hex, validVec2Hex, validVec3Hex, validVec4Hex, validVec5Hex, validVec6Hex]--validVec1Hex :: Text-validVec1Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000"--validVec2Hex :: Text-validVec2Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac000000000001076a47304402204759661797c01b036b25928948686218347d89864b719e1f7fcf57d1e511658702205309eabf56aa4d8891ffd111fdf1336f3a29da866d7f8486d75546ceedaf93190121035cdc61fc7ba971c0b501a646a2a83b102cb43881217ca682dc86e2d73fa882920001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb82308000000"--validVec3Hex :: Text-validVec3Hex = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab30000000001030401000000000000"--validVec4Hex :: Text-validVec4Hex = "70736274ff0100a00200000002ab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40000000000feffffffab0949a08c5af7c49b8212f417e2f15ab3f5c33dcf153821a8139f877a5b7be40100000000feffffff02603bea0b000000001976a914768a40bbd740cbe81d988e71de2a4d5c71396b1d88ac8e240000000000001976a9146f4620b553fa095e721b9ee0efe9fa039cca459788ac00000000000100df0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e13000001012000e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787010416001485d13537f2e265405a34dbafa9e3dda01fb8230800220202ead596687ca806043edc3de116cdf29d5e9257c196cd055cf698c8d02bf24e9910b4a6ba670000008000000080020000800022020394f62be9df19952c5587768aeb7698061ad2c4a25c894f47d8c162b4d7213d0510b4a6ba6700000080010000800200008000"--validVec5Hex :: Text-validVec5Hex = "70736274ff0100550200000001279a2323a5dfb51fc45f220fa58b0fc13e1e3342792a85d7e36cd6333b5cbc390000000000ffffffff01a05aea0b000000001976a914ffe9c0061097cc3b636f2cb0460fa4fc427d2b4588ac0000000000010120955eea0b0000000017a9146345200f68d189e1adc0df1c4d16ea8f14c0dbeb87220203b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4646304302200424b58effaaa694e1559ea5c93bbfd4a89064224055cdf070b6771469442d07021f5c8eb0fea6516d60b8acb33ad64ede60e8785bfb3aa94b99bdf86151db9a9a010104220020771fd18ad459666dd49f3d564e3dbc42f4c84774e360ada16816a8ed488d5681010547522103b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd462103de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd52ae220603b1341ccba7683b6af4f1238cd6e97e7167d569fac47f1e48d47541844355bd4610b4a6ba67000000800000008004000080220603de55d1e1dac805e3f8a58c1fbf9b94c02f3dbaafe127fefca4995f26f82083bd10b4a6ba670000008000000080050000800000"--validVec6Hex :: Text-validVec6Hex = "70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000"
− test/Network/Haskoin/TransactionSpec.hs
@@ -1,396 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Network.Haskoin.TransactionSpec (spec) where--import           Data.Aeson                         as A-import qualified Data.ByteString                    as B-import           Data.Either-import           Data.Map.Strict                    (singleton)-import           Data.Maybe-import           Data.Serialize                     as S-import           Data.String                        (fromString)-import           Data.String.Conversions-import           Data.Text                          (Text)-import           Data.Word                          (Word32, Word64)-import           Network.Haskoin.Address-import           Network.Haskoin.Constants-import           Network.Haskoin.Keys-import           Network.Haskoin.Script-import           Network.Haskoin.Test-import           Network.Haskoin.Transaction-import           Network.Haskoin.Transaction.Segwit (isSegwit)-import           Network.Haskoin.Util-import           Safe                               (readMay)-import           Test.Hspec-import           Test.HUnit                         (Assertion, assertBool)-import           Test.QuickCheck--spec :: Spec-spec = do-    let net = btc-    describe "transaction unit tests" $ do-        it "compute txid from tx" $-            mapM_ runTxIDVec txIDVec-        it "build pkhash transaction (generated from bitcoind)" $-            mapM_ runPKHashVec pkHashVec-        it "encode satoshi core script pubkey" tEncodeSatoshiCoreScriptPubKey-        ---        -- These tests depend on signatures matching exactly those in the spec.-        -- Fedora includes a version of libsecp256k1 that computes signatures-        -- using a slighlty different deterministic nonce algorithm.-        ---        -- it "agrees with BIP143 p2wpkh example" testBip143p2wpkh-        -- it "agrees with BIP143 p2sh-p2wpkh example" testBip143p2shp2wpkh-        -- it "builds a p2wsh multisig transaction" testP2WSHMulsig-        -- it "agrees with BIP143 p2sh-p2wsh multisig example" testBip143p2shp2wpkhMulsig-    describe "btc transaction" $ do-        it "decode and encode txid" $-            property $-            forAll arbitraryTxHash $ \h -> hexToTxHash (txHashToHex h) == Just h-        it "from string transaction id" $-            property $-            forAll arbitraryTxHash $ \h -> fromString (cs $ txHashToHex h) == h-        it "building address tx" $-            property $-            forAll arbitraryAddress $-            forAll (arbitrarySatoshi net) . testBuildAddrTx net-        it "guess transaction size" $-            property $ forAll (arbitraryAddrOnlyTxFull net) (testGuessSize net)-        it "choose coins" $-            property $ forAll (listOf (arbitrarySatoshi net)) testChooseCoins-        it "choose multisig coins" $-            property $-            forAll arbitraryMSParam $-            forAll (listOf (arbitrarySatoshi net)) . testChooseMSCoins-        it "sign and validate transaction" $-            property $ forAll (arbitrarySigningData net) (testDetSignTx net)-        it "sign and validate (nested) transaction" $-            property $ forAll (arbitrarySigningData net) (testDetSignNestedTx net)-        it "merge partially signed transactions" $-            property $ forAll (arbitraryPartialTxs net) (testMergeTx net)-    describe "json serialization" $ do-        it "encodes and decodes transaction" $-            property $ forAll (arbitraryTx net) testID-        it "encodes and decodes transaction hash" $-            property $ forAll arbitraryTxHash testID-    describe "transaction serialization" $ do-        it "encodes and decodes tx input" $-            property $ forAll (arbitraryTxIn net) cerealID-        it "encodes and decodes tx output" $-            property $ forAll (arbitraryTxOut net) cerealID-        it "encodes and decodes outpoint" $-            property $ forAll arbitraryOutPoint cerealID-        it "encodes and decodes transaction" $-            property $ forAll (arbitraryTx net) cerealID-        it "encodes and decodes witness transaction" $-            property $ forAll (arbitraryWitnessTx net) cerealID-        it "encodes and decodes legacy transaction" $-            property $ forAll (arbitraryLegacyTx net) cerealID--cerealID :: (Serialize a, Eq a) => a -> Bool-cerealID x = S.decode (S.encode x) == Right x--runTxIDVec :: (Text, Text) -> Assertion-runTxIDVec (tid, tx) = assertBool "txid" $ txHashToHex (txHash txBS) == tid-  where-    txBS = fromJust $ either (const Nothing) return . S.decode =<< decodeHex tx--txIDVec :: [(Text, Text)]-txIDVec =-    [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"-      , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"-      )-    , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"-      , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"-      )-    , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"-      , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"-      )-    , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"-      , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"-      )-    ]--runPKHashVec :: ([(Text, Word32)], [(Text, Word64)], Text) -> Assertion-runPKHashVec (xs, ys, res) =-    assertBool "Build PKHash Tx" $ encodeHex (S.encode tx) == res-  where-    tx =-        fromRight (error "Could not decode transaction") $-        buildAddrTx btc (map f xs) ys-    f (tid, ix) = OutPoint (fromJust $ hexToTxHash tid) ix---- These test vectors have been generated from bitcoind raw transaction api--pkHashVec :: [([(Text, Word32)], [(Text, Word64)], Text)]-pkHashVec =-    [-      ( [("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",14)]-      , [("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",90000000)]-      , "0100000001db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0e00000000ffffffff01804a5d05000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac00000000"-      )-    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)-        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)-        ]-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)-        ]-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"-      )-    , ( [ ("eb29eba154166f6541ebcc9cbdf5088756e026af051f123bcfb526df594549db",0)-        , ("0001000000000000000000000000000000000000000000000000000000000000",2147483647)-        ]-      , []-      , "0100000002db494559df26b5cf3b121f05af26e0568708f5bd9ccceb41656f1654a1eb29eb0000000000ffffffff0000000000000000000000000000000000000000000000000000000000000100ffffff7f00ffffffff0000000000"-      )-    , ( []-      , [ ("14LsRquZfURNFrzpcLVGdaHTfAPjjwiSPb",1)-        , ("19VCgS642vzEA1sdByoSn6GsWBwraV8D4n",2100000000000000)-        ]-      , "01000000000201000000000000001976a91424aa604689cc582292b97668bedd91dd5bf9374c88ac0040075af07507001976a9145d16672f53981ff21c5f42b40d1954993cbca54f88ac00000000"-      )-    ]--tEncodeSatoshiCoreScriptPubKey :: Assertion-tEncodeSatoshiCoreScriptPubKey = assertBool "tEncodeSatoshiCoreScriptPubKey" $-  t1BsOutputScriptPubKey == encodeSatoshiCoreScriptPubKey t1SatoshiCoreJsonScriptPubKey-  where-    t1BsOutputScriptPubKey :: Text-    t1BsOutputScriptPubKey = "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"-    t1SatoshiCoreJsonScriptPubKey :: String-    t1SatoshiCoreJsonScriptPubKey = "1 0x41 0x04cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4 0x41 0x0461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af 2 OP_CHECKMULTISIG"--encodeSatoshiCoreScriptPubKey :: String -> Text-encodeSatoshiCoreScriptPubKey =-  mconcat . map encodeSatoshiCoreScriptPiece . words-  where-    encodeSatoshiCoreScriptPiece :: String -> Text-    encodeSatoshiCoreScriptPiece s = case (readMay ("OP_" ++ s) :: Maybe ScriptOp) of-      Just op -> encodeHex . S.encode $ op-      Nothing -> case take 2 s of-          "OP" -> encodeHex . S.encode . (read :: String -> ScriptOp) $ s-          "0x" -> (fromString . drop 2 :: String -> Text) s-          _ -> case (readMay s :: Maybe Int) of -- can we get rid of this case now?-            Just i  -> encodeHex . S.encode . intToScriptOp $ i-            Nothing -> error $ "encodeSatoshiCoreScriptPubKey: " ++ s------- Following tests depend on specific secp256k1 deterministic nonce computation.--- Fedora distributes the Bitcoin ABC fork of this library, which computes nonces differently.------- -- Reproduce the P2WPKH example from BIP 143--- testBip143p2wpkh :: Assertion--- testBip143p2wpkh = assertEqual "BIP143 p2wpkh" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"----     unsignedTx = "0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000"----     Just key0 = secHexKey "bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"---     pubKey0   = toPubKey key0----     Just key1 = secHexKey "619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"----     [op0, op1] = prevOutput <$> txIn unsignedTx----     sigIn0 = SigInput (PayPK pubKey0) 625000000 op0 sigHashAll Nothing----     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key1---     sigIn1                 = SigInput (PayWitnessPKHash h) 600000000 op1 sigHashAll Nothing----     generatedSignedTx = signTx btc unsignedTx [sigIn0, sigIn1] [key0, key1]---- -- Reproduce the P2SH-P2WPKH example from BIP 143--- testBip143p2shp2wpkh :: Assertion--- testBip143p2shp2wpkh = assertEqual "BIP143 p2sh-p2wpkh" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"----     unsignedTx = "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"----     Just key0 = secHexKey "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf"----     op0                    = prevOutput . head $ txIn unsignedTx---     WitnessPubKeyAddress h = pubKeyWitnessAddr $ toPubKey key0---     sigIn0                 = SigInput (PayWitnessPKHash h) 1000000000 op0 sigHashAll Nothing----     generatedSignedTx = signNestedWitnessTx btc unsignedTx [sigIn0] [key0]---- -- P2WSH multisig example (tested against bitcoin-core 0.19.0.1)--- testP2WSHMulsig :: Assertion--- testP2WSHMulsig = assertEqual "p2wsh multisig" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "01000000000101d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac0400483045022100fad4fedd2bb4c439c64637eb8e9150d9020a7212808b8dc0578d5ff5b4ad65fe0220714640f261b37eb3106310bf853f4b706e51436fb6b64c2ab00768814eb55b9801473044022100baff4e4ceea4022b9725a2e6f6d77997a554f858165b91ac8c16c9833008bee9021f5f70ebc3f8580dc0a5e96451e3697bdf1f1f5883944f0f33ab0cfb272354040169522102ba46d3bb8db74c77c6cf082db57fc0548058fcdea811549e186526e3d10caf6721038ac8aef2dd9cea5e7d66e2f6e23f177a6c21f69ea311fa0c85d81badb6b37ceb2103d96d2bfbbc040faaf93491d69e2bfe9695e2d8e007a7f26db96c2ee42db15dc953ae00000000"----     unsignedTx = "0100000001d2e34df5d7ee565208eddd231548916b9b0e99f4f5071f896134a448c5fb07bf0100000000ffffffff01f0b9f505000000001976a9143d5a352cab583b12fbcb26d1269b4a2c951a33ad88ac00000000"----     op0 = head $ prevOutput <$> txIn unsignedTx----     Just keys = traverse secHexKey [ "3030303030303030303030303030303030303030303030303030303030303031"---                                    , "3030303030303030303030303030303030303030303030303030303030303032"---                                    , "3030303030303030303030303030303030303030303030303030303030303033"---                                    ]----     rdm               = PayMulSig (toPubKey <$> keys) 2---     sigIn             = SigInput (toP2WSH $ encodeOutput rdm) 100000000 op0 sigHashAll (Just rdm)---     generatedSignedTx = signTx btc unsignedTx [sigIn] (take 2 keys)---- -- Reproduce the P2SH-P2WSH multisig example from BIP 143--- testBip143p2shp2wpkhMulsig :: Assertion--- testBip143p2shp2wpkhMulsig =---     assertEqual "BIP143 p2sh-p2wsh multisig" (Right exampleSignedTx) generatedSignedTx---   where---     exampleSignedTx = "0100000000010136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000023220020a16b5755f7f6f96dbd65f5f0d6ab9418b89af4b1f14a1bb8a09062c35f0dcb54ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac080047304402206ac44d672dac41f9b00e28f4df20c52eeb087207e8d758d76d92c6fab3b73e2b0220367750dbbe19290069cba53d096f44530e4f98acaa594810388cf7409a1870ce01473044022068c7946a43232757cbdf9176f009a928e1cd9a1a8c212f15c1e11ac9f2925d9002205b75f937ff2f9f3c1246e547e54f62e027f64eefa2695578cc6432cdabce271502473044022059ebf56d98010a932cf8ecfec54c48e6139ed6adb0728c09cbe1e4fa0915302e022007cd986c8fa870ff5d2b3a89139c9fe7e499259875357e20fcbb15571c76795403483045022100fbefd94bd0a488d50b79102b5dad4ab6ced30c4069f1eaa69a4b5a763414067e02203156c6a5c9cf88f91265f5a942e96213afae16d83321c8b31bb342142a14d16381483045022100a5263ea0553ba89221984bd7f0b13613db16e7a70c549a86de0cc0444141a407022005c360ef0ae5a5d4f9f2f87a56c1546cc8268cab08c73501d6b3be2e1e1a8a08824730440220525406a1482936d5a21888260dc165497a90a15669636d8edca6b9fe490d309c022032af0c646a34a44d1f4576bf6a4a74b67940f8faa84c7df9abe12a01a11e2b4783cf56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae00000000"----     unsignedTx = "010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000"----     op0 = head $ prevOutput <$> txIn unsignedTx----     rawKeys = [ "730fff80e1413068a05b57d6a58261f07551163369787f349438ea38ca80fac6"---               , "11fa3d25a17cbc22b29c44a484ba552b5a53149d106d3d853e22fdd05a2d8bb3"---               , "77bf4141a87d55bdd7f3cd0bdccf6e9e642935fec45f2f30047be7b799120661"---               , "14af36970f5025ea3e8b5542c0f8ebe7763e674838d08808896b63c3351ffe49"---               , "fe9a95c19eef81dde2b95c1284ef39be497d128e2aa46916fb02d552485e0323"---               , "428a7aee9f0c2af0cd19af3cf1c78149951ea528726989b2e83e4778d2c3f890"---               ]----     Just keys@[key0, key1, key2, key3, key4, key5] = traverse secHexKey rawKeys----     rdm      = PayMulSig (toPubKey <$> keys) 6---     sigIn sh = SigInput (toP2WSH $ encodeOutput rdm) 987654321 op0 sh (Just rdm)----     sigHashesA = [sigHashAll, sigHashNone, sigHashSingle]---     sigHashesB = setAnyoneCanPayFlag <$> sigHashesA---     sigIns     = sigIn <$> (sigHashesA <> sigHashesB)----     generatedSignedTx      = foldM addSig unsignedTx $ zip sigIns keys---     addSig tx (sigIn, key) = signNestedWitnessTx btc tx [sigIn] [key]---- secHexKey :: Text -> Maybe SecKey--- secHexKey = decodeHex >=> secKey---- toPubKey :: SecKey -> PubKeyI--- toPubKey = derivePubKeyI . wrapSecKey True--{- Building Transactions -}--testBuildAddrTx :: Network -> Address -> TestCoin -> Bool-testBuildAddrTx net a (TestCoin v)-    | isPubKeyAddress a = Right (PayPKHash (getAddrHash160 a)) == out-    | isScriptAddress a = Right (PayScriptHash (getAddrHash160 a)) == out-    | otherwise = undefined-  where-    tx = buildAddrTx net [] [(fromJust (addrToString net a), v)]-    out =-        decodeOutputBS $-        scriptOutput $-        head $ txOut (fromRight (error "Could not build transaction") tx)--testGuessSize :: Network -> Tx -> Bool-testGuessSize net tx-    -- We compute an upper bound but it should be close enough to the real size-    -- We give 2 bytes of slack on every signature (1 on r and 1 on s)- = guess >= len && guess <= len + 2 * delta-  where-    delta = pki + sum (map fst msi)-    guess = guessTxSize pki msi pkout msout-    len = B.length $ S.encode tx-    ins = map f $ txIn tx-    f i =-        fromRight (error "Could not decode input") $-        decodeInputBS net $ scriptInput i-    pki = length $ filter isSpendPKHash ins-    msi = concatMap shData ins-    shData (ScriptHashInput _ (PayMulSig keys r)) = [(r, length keys)]-    shData _                                      = []-    out =-        map-            (fromRight (error "Could not decode transaction output") .-             decodeOutputBS . scriptOutput) $-        txOut tx-    pkout = length $ filter isPayPKHash out-    msout = length $ filter isPayScriptHash out--testChooseCoins :: [TestCoin] -> Word64 -> Word64 -> Int -> Property-testChooseCoins coins target byteFee nOut = nOut >= 0 ==>-    case chooseCoins target byteFee nOut True coins of-        Right (chosen, change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessTxFee byteFee nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessTxFee byteFee nOut (length coins)-            in target == 0 || s < target + fee-  where-    s  = sum $ map coinValue coins--testChooseMSCoins :: (Int, Int) -> [TestCoin]-                  -> Word64 -> Word64 -> Int -> Property-testChooseMSCoins (m, n) coins target byteFee nOut = nOut >= 0 ==>-    case chooseMSCoins target byteFee (m,n) nOut True coins of-        Right (chosen,change) ->-            let outSum = sum $ map coinValue chosen-                fee    = guessMSTxFee byteFee (m,n) nOut (length chosen)-            in outSum == target + change + fee-        Left _ ->-            let fee = guessMSTxFee byteFee (m,n) nOut (length coins)-            in target == 0 || s < target + fee-  where-    s = sum $ map coinValue coins--{- Signing Transactions -}--testDetSignTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignTx net  (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signTx net tx sigis (map secKeyData (tail prv))-    txSigC =-        fromRight (error "Could not decode transaction") $-        signTx net txSigP sigis [secKeyData (head prv)]-    verData = map (\(SigInput s v o _ _) -> (s, v, o)) sigis--testDetSignNestedTx :: Network -> (Tx, [SigInput], [SecKeyI]) -> Bool-testDetSignNestedTx net  (tx, sigis, prv) =-    not (verifyStdTx net tx verData) &&-    not (verifyStdTx net txSigP verData) && verifyStdTx net txSigC verData-  where-    txSigP =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net tx sigis (secKeyData <$> tail prv)-    txSigC =-        fromRight (error "Could not decode transaction") $-        signNestedWitnessTx net txSigP sigis [secKeyData (head prv)]-    verData = handleSegwit <$> sigis-    handleSegwit (SigInput s v o _ _)-        | isSegwit s = (toP2SH $ encodeOutput s, v, o)-        | otherwise  = (s, v, o)--testMergeTx :: Network -> ([Tx], [(ScriptOutput, Word64, OutPoint, Int, Int)]) -> Bool-testMergeTx net (txs, os) = and-    [ isRight mergeRes-    , length (txIn mergedTx) == length os-    , if enoughSigs then isValid else not isValid-    -- Signature count == min (length txs) (sum required signatures)-    , sum (map snd sigMap) == min (length txs) (sum (map fst sigMap))-    ]-  where-    outs = map (\(so, val, op, _, _) -> (so, val, op)) os-    mergeRes = mergeTxs net txs outs-    mergedTx = fromRight (error "Could not merge") mergeRes-    isValid = verifyStdTx net mergedTx outs-    enoughSigs = all (\(m,c) -> c >= m) sigMap-    sigMap = map (\((_,_,_,m,_), inp) -> (m, sigCnt inp)) $ zip os $ txIn mergedTx-    sigCnt inp = case decodeInputBS net $ scriptInput inp of-        Right (RegularInput (SpendMulSig sigs)) -> length sigs-        Right (ScriptHashInput (SpendMulSig sigs) _) -> length sigs-        _ -> error "Invalid input script type"--testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool-testID x =-    (A.decode . A.encode) (singleton ("object" :: String) x) ==-    Just (singleton ("object" :: String) x)
− test/Network/Haskoin/UtilSpec.hs
@@ -1,64 +0,0 @@-module Network.Haskoin.UtilSpec (spec) where--import qualified Data.ByteString      as BS-import           Data.Either          (fromLeft, fromRight, isLeft, isRight)-import           Data.Foldable        (toList)-import           Data.List            (permutations)-import           Data.Maybe-import qualified Data.Sequence        as Seq-import           Network.Haskoin.Test-import           Network.Haskoin.Util-import           Test.Hspec-import           Test.QuickCheck--spec :: Spec-spec =-    describe "utility functions" $ do-        it "bsToInteger . integerToBS" $ property getPutInteger-        it "decodeHex . encodeHex" $ property $ forAll arbitraryBS fromToHex-        it "compare updateIndex with Data.Sequence" $ property testUpdateIndex-        it "matchTemplate" $ property testMatchTemplate-        it "testing matchTemplate with two lists" $-            property testMatchTemplateLen-        it "either helper functions" $ property testEither--{- Various utilities -}--getPutInteger :: Integer -> Bool-getPutInteger i = bsToInteger (integerToBS $ abs i) == abs i--fromToHex :: BS.ByteString -> Bool-fromToHex bs = decodeHex (encodeHex bs) == Just bs--testUpdateIndex :: [Int] -> Int -> Int -> Bool-testUpdateIndex xs v i =-    updateIndex i xs (const v) == toList (Seq.update i v $ Seq.fromList xs)--testMatchTemplate :: [Int] -> Int -> Bool-testMatchTemplate as i = catMaybes res == bs-  where-    res = matchTemplate as bs (==)-    idx =-        if null as-            then 0-            else i `mod` length as-    bs = permutations as !! idx--testMatchTemplateLen :: [Int] -> [Int] -> Bool-testMatchTemplateLen as bs = length bs == length res-  where-    res = matchTemplate as bs (==)--testEither :: Either String Int -> Bool-testEither e =-    case e of-        (Right v) ->-            isRight e &&-            not (isLeft e) &&-            fromRight (error "Unexpected Left") e == v &&-            eitherToMaybe e == Just v-        (Left v) ->-            isLeft e &&-            not (isRight e) &&-            fromLeft (error "Unexpected Right") e == v &&-            isNothing (eitherToMaybe e)