haskoin (empty) → 0.0.2
raw patch · 87 files changed
+11791/−0 lines, 87 filesdep +HUnitdep +QuickCheckdep +aesonsetup-changed
Dependencies added: HUnit, QuickCheck, aeson, base, binary, byteable, bytestring, conduit, conduit-extra, containers, cryptohash, either, mtl, pbkdf, split, test-framework, test-framework-hunit, test-framework-quickcheck2, text, text-icu, vector
Files
- Network/Haskoin/Crypto.hs +231/−0
- Network/Haskoin/Crypto/Base58.hs +131/−0
- Network/Haskoin/Crypto/BigWord.hs +313/−0
- Network/Haskoin/Crypto/Bloom.hs +108/−0
- Network/Haskoin/Crypto/Curve.hs +27/−0
- Network/Haskoin/Crypto/ECDSA.hs +214/−0
- Network/Haskoin/Crypto/ExtendedKeys.hs +323/−0
- Network/Haskoin/Crypto/Hash.hs +300/−0
- Network/Haskoin/Crypto/Keys.hs +258/−0
- Network/Haskoin/Crypto/Merkle.hs +130/−0
- Network/Haskoin/Crypto/Mnemonic.hs +500/−0
- Network/Haskoin/Crypto/NormalizedKeys.hs +292/−0
- Network/Haskoin/Crypto/NumberTheory.hs +23/−0
- Network/Haskoin/Crypto/Point.hs +147/−0
- Network/Haskoin/Protocol.hs +99/−0
- Network/Haskoin/Protocol/Addr.hs +39/−0
- Network/Haskoin/Protocol/Alert.hs +21/−0
- Network/Haskoin/Protocol/Block.hs +38/−0
- Network/Haskoin/Protocol/BlockHeader.hs +63/−0
- Network/Haskoin/Protocol/BloomFilter.hs +101/−0
- Network/Haskoin/Protocol/GetBlocks.hs +53/−0
- Network/Haskoin/Protocol/GetData.hs +34/−0
- Network/Haskoin/Protocol/GetHeaders.hs +46/−0
- Network/Haskoin/Protocol/Headers.hs +36/−0
- Network/Haskoin/Protocol/Inv.hs +29/−0
- Network/Haskoin/Protocol/InvVector.hs +54/−0
- Network/Haskoin/Protocol/MerkleBlock.hs +67/−0
- Network/Haskoin/Protocol/Message.hs +120/−0
- Network/Haskoin/Protocol/MessageHeader.hs +134/−0
- Network/Haskoin/Protocol/NetworkAddress.hs +45/−0
- Network/Haskoin/Protocol/NotFound.hs +30/−0
- Network/Haskoin/Protocol/Ping.hs +37/−0
- Network/Haskoin/Protocol/Script.hs +252/−0
- Network/Haskoin/Protocol/Tx.hs +194/−0
- Network/Haskoin/Protocol/VarInt.hs +46/−0
- Network/Haskoin/Protocol/VarString.hs +29/−0
- Network/Haskoin/Protocol/Version.hs +85/−0
- Network/Haskoin/Script.hs +56/−0
- Network/Haskoin/Script/Parser.hs +287/−0
- Network/Haskoin/Script/SigHash.hs +199/−0
- Network/Haskoin/Stratum.hs +274/−0
- Network/Haskoin/Stratum/Conduit.hs +153/−0
- Network/Haskoin/Stratum/Message.hs +218/−0
- Network/Haskoin/Transaction.hs +26/−0
- Network/Haskoin/Transaction/Builder.hs +362/−0
- Network/Haskoin/Util.hs +332/−0
- Network/Haskoin/Util/BuildMonad.hs +136/−0
- Network/Haskoin/Util/Constants.hs +55/−0
- Network/Haskoin/Util/Constants/Testnet.hs +55/−0
- Setup.hs +2/−0
- UNLICENSE +24/−0
- haskoin.cabal +149/−0
- tests/Main.hs +68/−0
- tests/Network/Haskoin/Crypto/Arbitrary.hs +136/−0
- tests/Network/Haskoin/Crypto/Base58/Tests.hs +32/−0
- tests/Network/Haskoin/Crypto/BigWord/Tests.hs +349/−0
- tests/Network/Haskoin/Crypto/Bloom/Tests.hs +22/−0
- tests/Network/Haskoin/Crypto/Bloom/Units.hs +71/−0
- tests/Network/Haskoin/Crypto/ECDSA/Tests.hs +97/−0
- tests/Network/Haskoin/Crypto/ExtendedKeys/Tests.hs +66/−0
- tests/Network/Haskoin/Crypto/ExtendedKeys/Units.hs +250/−0
- tests/Network/Haskoin/Crypto/Hash/Tests.hs +30/−0
- tests/Network/Haskoin/Crypto/Hash/Units.hs +1300/−0
- tests/Network/Haskoin/Crypto/Keys/Tests.hs +133/−0
- tests/Network/Haskoin/Crypto/Merkle/Tests.hs +36/−0
- tests/Network/Haskoin/Crypto/Merkle/Units.hs +113/−0
- tests/Network/Haskoin/Crypto/Mnemonic/Tests.hs +179/−0
- tests/Network/Haskoin/Crypto/Mnemonic/Units.hs +177/−0
- tests/Network/Haskoin/Crypto/Point/Tests.hs +103/−0
- tests/Network/Haskoin/Crypto/Units.hs +264/−0
- tests/Network/Haskoin/Protocol/Arbitrary.hs +237/−0
- tests/Network/Haskoin/Protocol/Tests.hs +59/−0
- tests/Network/Haskoin/Protocol/Units.hs +44/−0
- tests/Network/Haskoin/Script/Arbitrary.hs +85/−0
- tests/Network/Haskoin/Script/Tests.hs +116/−0
- tests/Network/Haskoin/Script/Units.hs +100/−0
- tests/Network/Haskoin/Stratum/Units.hs +57/−0
- tests/Network/Haskoin/Transaction/Arbitrary.hs +162/−0
- tests/Network/Haskoin/Transaction/Tests.hs +114/−0
- tests/Network/Haskoin/Transaction/Units.hs +214/−0
- tests/Network/Haskoin/Util/Arbitrary.hs +43/−0
- tests/Network/Haskoin/Util/Tests.hs +115/−0
- tests/data/errors.json +7/−0
- tests/data/invalid.json +6/−0
- tests/data/notifications.json +7/−0
- tests/data/requests.json +12/−0
- tests/data/responses.json +10/−0
+ Network/Haskoin/Crypto.hs view
@@ -0,0 +1,231 @@+{-|+ This package provides the elliptic curve cryptography required for creating+ and validating bitcoin transactions. It also provides SHA-256 and RIPEMD-160+ hashing functions; as well as mnemonic keys from BIP-0039.+-}+module Network.Haskoin.Crypto+( + -- *Elliptic Curve Keys+ + -- **Public Keys+ PubKey(..)+, isValidPubKey+, isPubKeyU+, derivePubKey+, pubKeyAddr+, addPubKeys++ -- **Private Keys+, PrvKey(..)+, isValidPrvKey+, makePrvKey+, makePrvKeyU+, fromPrvKey+, isPrvKeyU+, addPrvKeys+, putPrvKey+, getPrvKey+, getPrvKeyU+, fromWIF+, toWIF++ -- *ECDSA+ -- **SecretT Monad+ -- | The SecretT monad is a monadic wrapper around HMAC DRBG (deterministic+ -- random byte generator) using SHA-256. The implementation is provided in + -- 'Network.Haskoin.Crypto.Hash' and the specification is defined in+ -- <http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf>. The+ -- SecretT monad is used to generate random private keys and random nonces+ -- for ECDSA signatures.+, SecretT+, withSource+, devURandom+, devRandom+, genPrvKey++ -- **Signatures+ -- | Elliptic curve cryptography standards are defined in+ -- <http://www.secg.org/download/aid-780/sec1-v2.pdf>+, Signature+, signMsg+, detSignMsg+, verifySig+, isCanonicalHalfOrder++ -- * Big words+, Word512+, Word256+, Word160+, Word128++ -- *Hash functions+, Hash512+, Hash256+, Hash160+, CheckSum32+, hash512+, hash512BS+, hash256+, hash256BS+, hash160+, hash160BS+, doubleHash256+, doubleHash256BS+, chksum32+, hmac512+, hmac512BS+, hmac256+, hmac256BS+, split512+, join512+, murmurHash3++ -- *Number representations+, decodeCompact+, encodeCompact++ -- *Base58 and Addresses+, Address(..)+, base58ToAddr+, addrToBase58+, encodeBase58+, decodeBase58+, encodeBase58Check+, decodeBase58Check++ -- *Mnemonic keys (BIP-0039)+, WordList+, Entropy+, Mnemonic+, Passphrase+, Seed+, toMnemonic+, fromMnemonic+, mnemonicToSeed+, anyToSeed+, english++ -- *Extended Keys+, ChainCode++ -- **Extended Private Keys+, XPrvKey(..)+, makeXPrvKey+, xPrvIsPrime+, xPrvChild+, xPrvID+, xPrvFP+, xPrvExport+, xPrvImport+, xPrvWIF++ -- **Extended Public Keys+, XPubKey(..)+, deriveXPubKey+, xPubIsPrime+, xPubChild+, xPubID+, xPubFP+, xPubAddr+, xPubExport+, xPubImport++ -- **Child key derivations+, prvSubKey+, pubSubKey+, primeSubKey+, prvSubKeys+, pubSubKeys+, primeSubKeys++ -- ***Multisig derivations+, mulSigSubKey+, mulSigSubKeys++ -- *Derivation tree interoperability++ -- | To improve BIP32 wallet interoperability, a standard derivation tree+ -- is used. All accounts are generated through prime derivations from the+ -- master key. This ensures that the master key is not compromised if+ -- an account is compromised. Every account will generate receiving+ -- addresses from the non-prime subtree index 0 and internal change+ -- addresses from the non-prime subtree index 1. MasterKey, AccountKey+ -- and AddressKey types are defined to conform to the wallet interoperability+ -- format.++, KeyIndex++ -- **Master keys+, MasterKey(..)+, makeMasterKey+, loadMasterKey++ -- **Account keys+, AccPrvKey(..)+, AccPubKey(..)+, loadPrvAcc+, loadPubAcc+, accPrvKey+, accPubKey+, accPrvKeys+, accPubKeys++ -- **Address keys+, AddrPrvKey(..)+, AddrPubKey(..)+, addr+, extPrvKey+, extPubKey+, intPrvKey+, intPubKey+, extPrvKeys+, extPubKeys+, intPrvKeys+, intPubKeys+, extAddr+, intAddr+, extAddrs+, intAddrs+, extAddrs'+, intAddrs'++ -- ***Multisig address keys+, extMulSigKey+, intMulSigKey+, extMulSigKeys+, intMulSigKeys+, extMulSigAddr+, intMulSigAddr+, extMulSigAddrs+, intMulSigAddrs++ -- *Bloom filters+, BloomFilter+, BloomFlags(..)+, bloomCreate+, bloomInsert+, bloomContains+, bloomUpdateEmptyFull+, bloomIsValid++ -- *Partial merkle trees+, calcTreeHeight+, calcTreeWidth+, buildMerkleRoot+, calcHash+, buildPartialMerkle+, extractMatches++) where++import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Crypto.Mnemonic+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.ExtendedKeys+import Network.Haskoin.Crypto.NormalizedKeys+import Network.Haskoin.Crypto.Merkle+import Network.Haskoin.Crypto.Bloom+
+ Network/Haskoin/Crypto/Base58.hs view
@@ -0,0 +1,131 @@+module Network.Haskoin.Crypto.Base58+( Address(..)+, addrToBase58+, base58ToAddr+, encodeBase58+, decodeBase58+, encodeBase58Check+, decodeBase58Check+) where++import Control.Monad (guard)+import Control.Applicative ((<$>),(<*>))++import Data.Char (ord)+import Data.Word (Word8)+import Data.Maybe (fromJust)+import Data.Aeson+ ( Value (String)+ , FromJSON+ , ToJSON+ , parseJSON+ , toJSON+ , withText + )++import qualified Data.ByteString as BS+import qualified Data.Map.Strict as M+import qualified Data.Text as T++import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Util ++b58String :: String+b58String = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"++b58Data :: BS.ByteString+b58Data = BS.pack $ map (fromIntegral . ord) b58String++b58Data' :: M.Map Word8 Int+b58Data' = M.fromList $ zip (BS.unpack b58Data) [0..57]++b58 :: Word8 -> Word8+b58 i = BS.index b58Data (fromIntegral i)++b58' :: Word8 -> Maybe Word8+b58' w = fromIntegral <$> M.lookup w b58Data'++encodeBase58I :: Integer -> BS.ByteString+encodeBase58I 0 = BS.pack [b58 0]+encodeBase58I i+ | i >= 0 = go BS.empty i+ | otherwise = error "encodeBase58 is not defined for negative Integers"+ where + go acc 0 = acc+ go acc n = go (BS.cons (fromIntegral b) acc) q+ where + (q,r) = n `quotRem` 58+ b = b58 $ fromIntegral r++-- | Encode a bytestring to a base 58 representation.+encodeBase58 :: BS.ByteString -> BS.ByteString+encodeBase58 bs = BS.append l r+ where + (z,b) = BS.span (== 0) bs+ l = BS.map b58 z -- preserve leading 0's+ r | BS.null b = BS.empty+ | otherwise = encodeBase58I $ bsToInteger b++-- | Decode a base 58 encoded bytestring. This can fail if the input bytestring+-- contains invalid base 58 characters such as 0,O,l,I+decodeBase58 :: BS.ByteString -> Maybe BS.ByteString+decodeBase58 bs = r >>= return . (BS.append prefix)+ where + (z,b) = BS.span (== (b58 0)) bs+ prefix = BS.map (fromJust . b58') z -- preserve leading 1's+ r | BS.null b = Just BS.empty+ | otherwise = integerToBS <$> foldl f (Just 0) (BS.unpack b)+ f i w = do+ n <- fromIntegral <$> b58' w+ p <- i+ return $ p*58 + n++-- | Computes a checksum for the input bytestring and encodes the input and+-- the checksum to a base 58 representation.+encodeBase58Check :: BS.ByteString -> BS.ByteString+encodeBase58Check bs = encodeBase58 $ BS.append bs chk+ where + chk = encode' $ chksum32 bs++-- | Decode a base 58 encoded bytestring that contains a checksum. This+-- function returns Nothing if the input bytestring contains invalid base 58+-- characters or if the checksum fails.+decodeBase58Check :: BS.ByteString -> Maybe BS.ByteString+decodeBase58Check bs = do+ rs <- decodeBase58 bs+ let (res,chk) = BS.splitAt ((BS.length rs) - 4) rs+ guard $ chk == (encode' $ chksum32 res)+ return res++-- |Data type representing a Bitcoin address+data Address + -- | Public Key Hash Address+ = PubKeyAddress { getAddrHash :: Hash160 }+ -- | Script Hash Address+ | ScriptAddress { getAddrHash :: Hash160 }+ deriving (Eq, Show)++instance FromJSON Address where+ parseJSON = withText "Address not a string: " $ \a -> do+ let s = T.unpack a+ maybe (fail $ "Not a Bitcoin address: " ++ s) return $ base58ToAddr s++instance ToJSON Address where+ toJSON = String . T.pack . addrToBase58++-- | Transforms an Address into a base58 encoded String+addrToBase58 :: Address -> String+addrToBase58 addr = bsToString $ encodeBase58Check $ case addr of+ PubKeyAddress i -> BS.cons addrPrefix $ encode' i+ ScriptAddress i -> BS.cons scriptPrefix $ encode' i++-- | Decodes an Address from a base58 encoded String. This function can fail+-- if the String is not properly encoded as base58 or the checksum fails.+base58ToAddr :: String -> Maybe Address+base58ToAddr str = do+ val <- decodeBase58Check $ stringToBS str+ let f | BS.head val == addrPrefix = Just PubKeyAddress+ | BS.head val == scriptPrefix = Just ScriptAddress+ | otherwise = Nothing+ f <*> decodeToMaybe (BS.tail val)+
+ Network/Haskoin/Crypto/BigWord.hs view
@@ -0,0 +1,313 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE EmptyDataDecls #-}+module Network.Haskoin.Crypto.BigWord+(+-- Useful type aliases+ Hash512+, Hash256+, Hash160+, Word512+, Word256+, Word160+, Word128+, FieldP+, FieldN++-- Data types+, BigWord(..)+, BigWordMod(..)++-- Functions+, toFieldN+, toFieldP+, toMod512+, toMod256+, toMod160+, toMod128+, inverseP+, inverseN+, quadraticResidue+, isIntegerValidKey+) where++import Data.Bits + ( Bits+ , (.&.), (.|.), xor+ , complement+ , shift, shiftL, shiftR+ , bit, testBit, bitSize+ , popCount, isSigned+ )+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( getWord64be+ , getWord32be+ , getWord8+ , getByteString+ , Get+ )+import Data.Binary.Put + ( putWord64be+ , putWord32be+ , putWord8+ , putByteString+ )+import Control.Monad (unless, guard)+import Control.Applicative ((<$>))+import Data.Ratio (numerator, denominator)+import qualified Data.ByteString as BS (head, length)++import Network.Haskoin.Crypto.Curve +import Network.Haskoin.Crypto.NumberTheory +import Network.Haskoin.Util ++-- | Data type representing a 512 bit unsigned integer.+-- It is implemented as an Integer modulo 2^512.+type Word512 = BigWord Mod512+type Hash512 = Word512+-- | Data type representing a 256 bit unsigned integer.+-- It is implemented as an Integer modulo 2^256.+type Word256 = BigWord Mod256+type Hash256 = Word256+-- | Data type representing a 160 bit unsigned integer.+-- It is implemented as an Integer modulo 2^160.+type Word160 = BigWord Mod160+type Hash160 = Word160+-- | Data type representing a 128 bit unsigned integer.+-- It is implemented as an Integer modulo 2^128.+type Word128 = BigWord Mod128+-- | Data type representing an Integer modulo coordinate field order P.+type FieldP = BigWord ModP+-- | Data type representing an Integer modulo curve order N. +type FieldN = BigWord ModN++data Mod512+data Mod256 +data Mod160+data Mod128+data ModP+data ModN++newtype BigWord n = BigWord { getBigWordInteger :: Integer }+ deriving (Eq, Ord, Read, Show)++toFieldN :: BigWord n -> FieldN+toFieldN (BigWord i) = fromInteger i++toFieldP :: BigWord n -> FieldP+toFieldP (BigWord i) = fromInteger i++toMod512 :: BigWord n -> Word512+toMod512 (BigWord i) = fromInteger i++toMod256 :: BigWord n -> Word256+toMod256 (BigWord i) = fromInteger i++toMod160 :: BigWord n -> Word160+toMod160 (BigWord i) = fromInteger i++toMod128 :: BigWord n -> Word128+toMod128 (BigWord i) = fromInteger i++inverseP :: FieldP -> FieldP+inverseP (BigWord i) = fromInteger $ mulInverse i curveP++inverseN :: FieldN -> FieldN+inverseN (BigWord i) = fromInteger $ mulInverse i curveN++class BigWordMod a where + rFromInteger :: Integer -> BigWord a+ rBitSize :: BigWord a -> Int++instance BigWordMod Mod512 where+ rFromInteger i = BigWord $ i `mod` 2 ^ (512 :: Int)+ rBitSize _ = 512++instance BigWordMod Mod256 where+ rFromInteger i = BigWord $ i `mod` 2 ^ (256 :: Int)+ rBitSize _ = 256++instance BigWordMod Mod160 where+ rFromInteger i = BigWord $ i `mod` 2 ^ (160 :: Int)+ rBitSize _ = 160++instance BigWordMod Mod128 where+ rFromInteger i = BigWord $ i `mod` 2 ^ (128 :: Int)+ rBitSize _ = 128++instance BigWordMod ModP where+ rFromInteger i = BigWord $ i `mod` curveP+ rBitSize _ = 256++instance BigWordMod ModN where+ rFromInteger i = BigWord $ i `mod` curveN+ rBitSize _ = 256++instance BigWordMod n => Num (BigWord n) where+ fromInteger = rFromInteger+ (BigWord i1) + (BigWord i2) = fromInteger $ i1 + i2+ (BigWord i1) * (BigWord i2) = fromInteger $ i1 * i2+ negate (BigWord i) = fromInteger $ negate i+ abs r = r+ signum (BigWord i) = fromInteger $ signum i++instance BigWordMod n => Bits (BigWord n) where+ (BigWord i1) .&. (BigWord i2) = fromInteger $ i1 .&. i2+ (BigWord i1) .|. (BigWord i2) = fromInteger $ i1 .|. i2+ (BigWord i1) `xor` (BigWord i2) = fromInteger $ i1 `xor` i2+ complement (BigWord i) = fromInteger $ complement i+ shift (BigWord i) j = fromInteger $ shift i j+ bitSize = rBitSize+ testBit (BigWord i) = testBit i+ bit n = fromInteger $ bit n+ popCount (BigWord i) = popCount i+ isSigned _ = False++instance BigWordMod n => Bounded (BigWord n) where+ minBound = fromInteger 0+ maxBound = fromInteger (-1)+ +instance BigWordMod n => Real (BigWord n) where+ toRational (BigWord i) = toRational i++instance BigWordMod n => Enum (BigWord n) where+ succ r@(BigWord i)+ | r == maxBound = error "BigWord: tried to take succ of maxBound"+ | otherwise = fromInteger $ succ i+ pred r@(BigWord i) + | r == minBound = error "BigWord: tried to take pred of minBound"+ | otherwise = fromInteger $ pred i+ toEnum i+ | toInteger i >= toInteger (minFrom r) && + toInteger i <= toInteger (maxFrom r) = r+ | otherwise = error "BigWord: toEnum is outside of bounds"+ where + r = fromInteger $ toEnum i+ minFrom :: BigWordMod a => BigWord a -> BigWord a+ minFrom _ = minBound+ maxFrom :: BigWordMod a => BigWord a -> BigWord a+ maxFrom _ = maxBound+ fromEnum (BigWord i) = fromEnum i++instance BigWordMod n => Integral (BigWord n) where+ (BigWord i1) `quot` (BigWord i2) = fromInteger $ i1 `quot` i2+ (BigWord i1) `rem` (BigWord i2) = fromInteger $ i1 `rem` i2+ (BigWord i1) `div` (BigWord i2) = fromInteger $ i1 `div` i2+ (BigWord i1) `mod` (BigWord i2) = fromInteger $ i1 `mod` i2+ (BigWord i1) `quotRem` (BigWord i2) = (fromInteger a, fromInteger b)+ where + (a,b) = i1 `quotRem` i2+ (BigWord i1) `divMod` (BigWord i2) = (fromInteger a, fromInteger b)+ where + (a,b) = i1 `divMod` i2+ toInteger (BigWord i) = i++{- Fractional is only defined for prime orders -}++instance Fractional (BigWord ModP) where+ recip = inverseP+ fromRational r = fromInteger (numerator r) / fromInteger (denominator r)++instance Fractional (BigWord ModN) where+ recip = inverseN+ fromRational r = fromInteger (numerator r) / fromInteger (denominator r)++{- Binary instances for serialization / deserialization -}++instance Binary (BigWord Mod512) where+ get = do+ a <- fromIntegral <$> (get :: Get Hash256)+ b <- fromIntegral <$> (get :: Get Hash256)+ return $ (a `shiftL` 256) + b++ put (BigWord i) = do+ put $ (fromIntegral (i `shiftR` 256) :: Hash256)+ put $ (fromIntegral i :: Hash256)++instance Binary (BigWord Mod256) where+ get = do+ a <- fromIntegral <$> getWord64be+ b <- fromIntegral <$> getWord64be+ c <- fromIntegral <$> getWord64be+ d <- fromIntegral <$> getWord64be+ return $ (a `shiftL` 192) + (b `shiftL` 128) + (c `shiftL` 64) + d++ put (BigWord i) = do+ putWord64be $ fromIntegral (i `shiftR` 192)+ putWord64be $ fromIntegral (i `shiftR` 128)+ putWord64be $ fromIntegral (i `shiftR` 64)+ putWord64be $ fromIntegral i++instance Binary (BigWord Mod160) where+ get = do+ a <- fromIntegral <$> getWord32be+ b <- fromIntegral <$> getWord64be+ c <- fromIntegral <$> getWord64be+ return $ (a `shiftL` 128) + (b `shiftL` 64) + c++ put (BigWord i) = do+ putWord32be $ fromIntegral (i `shiftR` 128)+ putWord64be $ fromIntegral (i `shiftR` 64)+ putWord64be $ fromIntegral i++instance Binary (BigWord Mod128) where+ get = do+ a <- fromIntegral <$> getWord64be+ b <- fromIntegral <$> getWord64be+ return $ (a `shiftL` 64) + b++ put (BigWord i) = do+ putWord64be $ fromIntegral (i `shiftR` 64)+ putWord64be $ fromIntegral i++-- DER encoding of a FieldN element as Integer+-- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf+instance Binary (BigWord ModN) where+ get = do+ t <- getWord8+ unless (t == 0x02) (fail $+ "Bad DER identifier byte " ++ (show t) ++ ". Expecting 0x02" )+ l <- getWord8+ unless (l <= 33) (fail $+ "Bad DER length " ++ (show l) ++ ". Expecting length <= 33" )+ i <- bsToInteger <$> getByteString (fromIntegral l)+ unless (isIntegerValidKey i) $ fail $ + "Invalid fieldN element: " ++ (show i)+ return $ fromInteger i++ put (BigWord 0) = error "0 is an invalid FieldN element to serialize"+ put (BigWord i) = do+ putWord8 0x02 -- Integer type+ let b = integerToBS i+ l = fromIntegral $ BS.length b+ if BS.head b >= 0x80 + then do+ putWord8 (l + 1)+ putWord8 0x00+ else do+ putWord8 l+ putByteString b++instance Binary (BigWord ModP) where++ -- Section 2.3.6 http://www.secg.org/download/aid-780/sec1-v2.pdf+ get = do+ (BigWord i) <- get :: Get Hash256+ unless (i < curveP) (fail $ "Get: Integer not in FieldP: " ++ (show i))+ return $ fromInteger i++ -- Section 2.3.7 http://www.secg.org/download/aid-780/sec1-v2.pdf+ put r = put $ toMod256 r+ ++-- curveP = 3 (mod 4), thus Lagrange solutions apply+-- http://en.wikipedia.org/wiki/Quadratic_residue+quadraticResidue :: FieldP -> [FieldP]+quadraticResidue x = guard (y^(2 :: Int) == x) >> [y, (-y)]+ where + q = (curveP + 1) `div` 4+ y = x^q++isIntegerValidKey :: Integer -> Bool+isIntegerValidKey i = i > 0 && i < curveN+
+ Network/Haskoin/Crypto/Bloom.hs view
@@ -0,0 +1,108 @@+module Network.Haskoin.Crypto.Bloom +( BloomFilter(..)+, BloomFlags(..)+, bloomCreate+, bloomInsert+, bloomContains+, bloomUpdateEmptyFull+, bloomIsValid+) where++import Data.Word+import Data.Bits+import qualified Data.Foldable as F+import qualified Data.Sequence as S+import qualified Data.ByteString as BS++import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Protocol++-- 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]++-- | 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 + -- ^ A random nonce (tweak) for the hash function. It should be+ -- a random number but the secureness of the random value is not+ -- of geat consequence.+ -> BloomFlags -- ^ Bloom filter flags+ -> BloomFilter -- ^ Bloom filter+bloomCreate numElem fpRate tweak flags =+ BloomFilter (S.replicate bloomSize 0) False False numHashF tweak flags+ where+ bloomSize = truncate $ (min a b) / 8+ a = -1 / ln2Squared * (fromIntegral numElem) * log fpRate+ b = fromIntegral $ maxBloomSize * 8+ numHashF = truncate $ min c (fromIntegral maxHashFuncs)+ c = (fromIntegral bloomSize) * 8 / (fromIntegral numElem) * ln2++bloomHash :: BloomFilter -> Word32 -> BS.ByteString -> Int+bloomHash bfilter hashNum bs =+ fromIntegral (murmurHash3 seed bs) `mod` (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+ -> BS.ByteString -- ^ New data to insert+ -> BloomFilter -- ^ Bloom filter containing the new data+bloomInsert bfilter bs + | bloomFull bfilter = bfilter+ | otherwise = bfilter { bloomData = newData, bloomEmpty = False }+ where+ idxs = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]+ upd s i = S.adjust (.|. bitMask !! (7 .&. i)) (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 + -> BS.ByteString + -- ^ Data that will be checked against the given bloom filter+ -> Bool+ -- ^ Returns True if the data matches the filter+bloomContains bfilter bs+ | bloomFull bfilter = True+ | bloomEmpty bfilter = False+ | otherwise = and $ map isSet idxs+ where+ s = bloomData bfilter+ idxs = map (\i -> bloomHash bfilter i bs) [0..bloomHashFuncs bfilter - 1]+ isSet i = (S.index s (i `shiftR` 3)) .&. (bitMask !! (7 .&. i)) /= 0++-- TODO: Write bloomRelevantUpdate+-- bloomRelevantUpdate :: BloomFilter -> Tx -> Hash256 -> Maybe BloomFilter++-- | Sets the bloom filter empty flag to True if the filter is empty, and+-- the bloom filter full flag to True if the filter is full.+bloomUpdateEmptyFull :: BloomFilter -- ^ Bloom filter to update+ -> BloomFilter -- ^ Filter with updated flags+bloomUpdateEmptyFull bfilter = + bfilter { bloomEmpty = all (== 0x00) l, bloomFull = all (== 0xff) l }+ where+ l = F.toList $ bloomData bfilter++-- | Tests if a given bloom filter is valid.+bloomIsValid :: BloomFilter -- ^ Bloom filter to test+ -> Bool -- ^ True if the given filter is valid+bloomIsValid bfilter =+ (S.length $ bloomData bfilter) <= maxBloomSize &&+ (bloomHashFuncs bfilter) <= maxHashFuncs+
+ Network/Haskoin/Crypto/Curve.hs view
@@ -0,0 +1,27 @@+module Network.Haskoin.Crypto.Curve+( pairG+, curveP+, curveN+, integerB+, integerA+) where++-- SECP256k1 curve parameters++pairG :: (Integer, Integer)+pairG = ( 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 + , 0X483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 + )++curveP :: Integer+curveP = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f ++curveN :: Integer+curveN = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 ++integerB :: Integer+integerB = 7++integerA :: Integer+integerA = 0+
+ Network/Haskoin/Crypto/ECDSA.hs view
@@ -0,0 +1,214 @@+-- | ECDSA Signatures+module Network.Haskoin.Crypto.ECDSA+( SecretT+, Signature(..)+, withSource+, devURandom+, devRandom+, signMsg+, detSignMsg+, unsafeSignMsg+, verifySig+, genPrvKey+, isCanonicalHalfOrder+) where++import System.IO++import Control.Monad (liftM, guard, unless)+import Control.Monad.Trans (MonadTrans, lift)+import Control.Applicative (Applicative, (<*>), (<$>))+import qualified Control.Monad.State as S+ ( StateT+ , evalStateT+ , get, put+ )++import Data.Maybe (fromJust, fromMaybe)+import Data.Binary (Binary, get, put)+import Data.Binary.Put (putWord8, putByteString)+import Data.Binary.Get (getWord8)++import qualified Data.ByteString as BS + ( ByteString+ , length+ , hGet+ , empty+ )+ +import Network.Haskoin.Util +import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Crypto.Keys +import Network.Haskoin.Crypto.Point +import Network.Haskoin.Crypto.BigWord ++-- | Internal state of the 'SecretT' monad+type SecretState m = (WorkingState, (Int -> m BS.ByteString))++-- | StateT monad stack tracking the internal state of HMAC DRBG +-- pseudo random number generator using SHA-256. The 'SecretT' monad is+-- run with the 'withSource' function by providing it a source of entropy.+type SecretT m a = S.StateT (SecretState m) m a++-- | Run a 'SecretT' monad by providing it a source of entropy. You can+-- use 'devURandom', 'devRandom' or provide your own entropy source function.+withSource :: Monad m => (Int -> m BS.ByteString) -> SecretT m a -> m a+withSource f m = do+ seed <- f 32 -- Read 256 bits from the random source+ nonce <- f 16 -- Read 128 bits from the random source+ let ws = hmacDRBGNew seed nonce (stringToBS haskoinUserAgent)+ S.evalStateT m (ws,f)++-- | \/dev\/urandom entropy source. This is only available on machines+-- supporting it. This function is meant to be used together with 'withSource'.+devURandom :: Int -> IO BS.ByteString+devURandom i = withBinaryFile "/dev/urandom" ReadMode $ flip BS.hGet i++-- | \/dev\/random entropy source. This is only available on machines+-- supporting it. This function is meant to be used together with 'withSource'.+devRandom :: Int -> IO BS.ByteString+devRandom i = withBinaryFile "/dev/random" ReadMode $ flip BS.hGet i++-- | Generate a new random 'FieldN' value from the 'SecretT' monad. This will+-- invoke the HMAC DRBG routine. Of the internal entropy pool of the HMAC DRBG+-- was stretched too much, this function will reseed it.+nextSecret :: Monad m => SecretT m FieldN+nextSecret = do+ (ws,f) <- S.get+ let (ws',randM) = hmacDRBGGen ws 32 (stringToBS haskoinUserAgent)+ case randM of+ (Just rand) -> do+ S.put (ws',f)+ let randI = bsToInteger rand+ if isIntegerValidKey randI+ then return $ fromInteger randI+ else nextSecret+ Nothing -> do+ seed <- lift $ f 32 -- Read 256 bits to re-seed the PRNG+ let ws0 = hmacDRBGRsd ws' seed (stringToBS haskoinUserAgent)+ S.put (ws0,f)+ nextSecret++-- | Produce a new 'PrvKey' randomly from the 'SecretT' monad.+genPrvKey :: Monad m => SecretT m PrvKey+genPrvKey = liftM (fromJust . makePrvKey . toInteger) nextSecret+ +-- Section 3.2.1 http://www.secg.org/download/aid-780/sec1-v2.pdf+-- Produce a new private/public key pair from the 'SecretT' monad.+genKeyPair :: Monad m => SecretT m (FieldN, Point)+genKeyPair = do+ -- 3.2.1.1 + d <- nextSecret+ -- 3.2.1.2+ let q = mulPoint d curveG+ -- 3.2.1.3+ return (d,q)++-- | Data type representing an ECDSA signature. +data Signature = + Signature { sigR :: !FieldN + , sigS :: !FieldN + }+ deriving (Show, Eq)++-- Section 4.1.3 http://www.secg.org/download/aid-780/sec1-v2.pdf+-- | Safely sign a message inside the 'SecretT' monad. The 'SecretT' monad will+-- generate a new nonce for each signature.+signMsg :: Monad m => Hash256 -> PrvKey -> SecretT m Signature+signMsg _ (PrvKey 0) = error "signMsg: Invalid private key 0"+signMsg _ (PrvKeyU 0) = error "signMsg: Invalid private key 0"+signMsg h d = do+ -- 4.1.3.1+ (k,p) <- genKeyPair+ case unsafeSignMsg h (prvKeyFieldN d) (k,p) of+ (Just sig) -> return sig+ -- If signing failed, retry with a new nonce+ Nothing -> signMsg h d++-- | Sign a message using ECDSA deterministic signatures as defined by+-- RFC 6979 <http://tools.ietf.org/html/rfc6979>+detSignMsg :: Hash256 -> PrvKey -> Signature+detSignMsg _ (PrvKey 0) = error "detSignMsg: Invalid private key 0"+detSignMsg _ (PrvKeyU 0) = error "detSignMsg: Invalid private key 0"+detSignMsg h d = go $ hmacDRBGNew (runPut' $ putPrvKey d) (encode' h) BS.empty+ where + go ws = case hmacDRBGGen ws 32 BS.empty of+ (_, Nothing) -> error "detSignMsg: No suitable K value found"+ (ws', Just k) -> + let kI = bsToInteger k+ p = mulPoint (fromInteger kI) curveG+ sigM = unsafeSignMsg h (prvKeyFieldN d) (fromInteger kI,p)+ in if isIntegerValidKey kI+ then fromMaybe (go ws') sigM+ else go ws'+ +-- Signs a message by providing the nonce+-- Re-using the same nonce twice will expose the private keys+-- Use signMsg within the SecretT monad or detSignMsg instead+-- Section 4.1.3 http://www.secg.org/download/aid-780/sec1-v2.pdf+unsafeSignMsg :: Hash256 -> FieldN -> (FieldN, Point) -> Maybe Signature+unsafeSignMsg _ 0 _ = Nothing+unsafeSignMsg h d (k,p) = do+ -- 4.1.3.1 (4.1.3.2 not required)+ (x,_) <- getAffine p+ -- 4.1.3.3+ let r = toFieldN x+ guard (r /= 0)+ -- 4.1.3.4 / 4.1.3.5+ let e = toFieldN h+ -- 4.1.3.6+ let s' = (e + r*d)/k+ -- Canonicalize signatures: s <= order/2+ -- maxBound/2 = (maxBound+1)/2 = order/2 (because order is odd)+ s = if s' > (maxBound `div` 2) then (-s') else s'+ guard (s /= 0)+ -- 4.1.3.7+ return $ Signature r s++-- Section 4.1.4 http://www.secg.org/download/aid-780/sec1-v2.pdf+-- | Verify an ECDSA signature+verifySig :: Hash256 -> Signature -> PubKey -> Bool+-- 4.1.4.1 (r and s can not be zero)+verifySig _ (Signature 0 _) _ = False+verifySig _ (Signature _ 0) _ = False+verifySig h (Signature r s) q = case getAffine p of+ Nothing -> False+ -- 4.1.4.7 / 4.1.4.8+ (Just (x,_)) -> (toFieldN x) == r+ where + -- 4.1.4.2 / 4.1.4.3+ e = toFieldN h+ -- 4.1.4.4+ s' = inverseN s+ u1 = e*s'+ u2 = r*s'+ -- 4.1.4.5 (u1*G + u2*q)+ p = shamirsTrick u1 curveG u2 (pubKeyPoint q)++-- | Returns True if the S component of a Signature is <= order/2.+-- Signatures need to pass this test to be canonical.+isCanonicalHalfOrder :: Signature -> Bool+isCanonicalHalfOrder (Signature _ s) = s <= maxBound `div` 2++instance Binary Signature where+ get = do+ t <- getWord8+ -- 0x30 is DER sequence type+ unless (t == 0x30) (fail $ + "Bad DER identifier byte " ++ (show t) ++ ". Expecting 0x30")+ l <- getWord8+ -- Length = (33 + 1 identifier byte + 1 length byte) * 2+ unless (l <= 70) (fail $+ "Bad DER length " ++ (show t) ++ ". Expecting length <= 70")+ isolate (fromIntegral l) $ do+ Signature <$> get <*> get++ put (Signature 0 _) = error "0 is an invalid r value in a Signature"+ put (Signature _ 0) = error "0 is an invalid s value in a Signature"+ put (Signature r s) = do+ putWord8 0x30+ let c = runPut' $ put r >> put s+ putWord8 (fromIntegral $ BS.length c)+ putByteString c++
+ Network/Haskoin/Crypto/ExtendedKeys.hs view
@@ -0,0 +1,323 @@+module Network.Haskoin.Crypto.ExtendedKeys+( XPubKey(..)+, XPrvKey(..)+, ChainCode+, makeXPrvKey+, deriveXPubKey+, prvSubKey+, pubSubKey+, primeSubKey+, prvSubKeys+, pubSubKeys+, primeSubKeys+, mulSigSubKey+, mulSigSubKeys+, xPrvIsPrime+, xPubIsPrime+, xPrvChild+, xPubChild+, xPubID+, xPrvID+, xPubFP+, xPrvFP+, xPubAddr+, xPubExport+, xPrvExport+, xPubImport+, xPrvImport+, xPrvWIF+, cycleIndex+, cycleIndex'+) where++import Control.Monad + ( guard+ , unless+ , when+ , liftM2+ )+import Data.Binary (Binary, get, put)+import Data.Binary.Get (Get, getWord8, getWord32be)+import Data.Binary.Put (Put, runPut, putWord8, putWord32be)+import Data.Word (Word8, Word32)+import Data.Bits + ( shiftR+ , setBit+ , testBit+ , clearBit+ )+import Data.Maybe (mapMaybe)+import qualified Data.ByteString as BS + ( ByteString+ , append+ )++import Network.Haskoin.Util+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.Base58++{- See BIP32 for details: https://en.bitcoin.it/wiki/BIP_0032 -}++type ChainCode = Hash256++-- | 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 of key derivations.+ , xPrvParent :: !Word32 -- ^ Fingerprint of the parent key.+ , xPrvIndex :: !Word32 -- ^ Key derivation index.+ , xPrvChain :: !ChainCode -- ^ Chain code.+ , xPrvKey :: !PrvKey -- ^ The private key of this extended key node.+ } deriving (Eq, Show, Read)++-- | Data type representing an extended BIP32 public key.+data XPubKey = XPubKey+ { xPubDepth :: !Word8 -- ^ Depth in the tree of key derivations.+ , xPubParent :: !Word32 -- ^ Fingerprint of the parent key.+ , xPubIndex :: !Word32 -- ^ Key derivation index.+ , xPubChain :: !ChainCode -- ^ Chain code.+ , xPubKey :: !PubKey -- ^ The public key of this extended key node.+ } deriving (Eq, Show, Read)++-- | Build a BIP32 compatible extended private key from a bytestring. This will+-- produce a root node (depth=0 and parent=0).+makeXPrvKey :: BS.ByteString -> Maybe XPrvKey+makeXPrvKey bs = do+ pk' <- makePrvKey $ fromIntegral pk+ return $ XPrvKey 0 0 0 c pk'+ where (pk,c) = split512 $ hmac512 (stringToBS "Bitcoin seed") bs++-- | 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, non-prime child key derivation. A private non-prime+-- 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\/. +--+-- Non-prime 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+ -> Word32 -- ^ Child derivation index+ -> Maybe XPrvKey -- ^ Extended child private key +prvSubKey xkey child = guardIndex child >> do+ k <- addPrvKeys (xPrvKey xkey) a+ return $ XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) child c k+ where pK = xPubKey $ deriveXPubKey xkey+ msg = BS.append (encode' pK) (encode' child)+ (a,c) = split512 $ hmac512 (encode' $ xPrvChain xkey) msg++-- | Compute a public, non-prime 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+ -> Word32 -- ^ Child derivation index+ -> Maybe XPubKey -- ^ Extended child public key+pubSubKey xKey child = guardIndex child >> do+ pK <- addPubKeys (xPubKey xKey) a+ return $ XPubKey (xPubDepth xKey + 1) (xPubFP xKey) child c pK+ where msg = BS.append (encode' $ xPubKey xKey) (encode' child)+ (a,c) = split512 $ hmac512 (encode' $ xPubChain xKey) msg++-- | Compute a prime child key derivation. Prime derivations can only be+-- computed for private keys. Prime 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'\/.+primeSubKey :: XPrvKey -- ^ Extended Parent private key+ -> Word32 -- ^ Child derivation index+ -> Maybe XPrvKey -- ^ Extended child private key+primeSubKey xkey child = guardIndex child >> do+ k <- addPrvKeys (xPrvKey xkey) a+ return $ XPrvKey (xPrvDepth xkey + 1) (xPrvFP xkey) i c k+ where i = setBit child 31+ msg = BS.append (bsPadPrvKey $ xPrvKey xkey) (encode' i)+ (a,c) = split512 $ hmac512 (encode' $ xPrvChain xkey) msg++-- | Cyclic list of all private non-prime child key derivations of a parent key+-- starting from an offset index.+prvSubKeys :: XPrvKey -> Word32 -> [(XPrvKey,Word32)]+prvSubKeys k i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (prvSubKey k j) (return j)++-- | Cyclic list of all public non-prime child key derivations of a parent key+-- starting from an offset index.+pubSubKeys :: XPubKey -> Word32 -> [(XPubKey,Word32)]+pubSubKeys k i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (pubSubKey k j) (return j)++-- | Cyclic list of all prime child key derivations of a parent key starting+-- from an offset index.+primeSubKeys :: XPrvKey -> Word32 -> [(XPrvKey,Word32)]+primeSubKeys k i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (primeSubKey k j) (return j)++-- | Compute a public, non-prime subkey derivation for all of the parent public+-- keys in the input. This function will succeed only if the child key+-- derivations for all the parent keys are valid. +--+-- This function is intended to be used in the context of multisignature+-- accounts. Parties exchanging their master public keys to create a+-- multisignature account can then individually generate all the receiving+-- multisignature addresses without further communication.+mulSigSubKey :: [XPubKey] -- ^ List of extended parent public keys+ -> Word32 -- ^ Child key derivation index+ -> Maybe [XPubKey] -- ^ List of extended child public keys+mulSigSubKey pubs i = mapM (flip pubSubKey i) pubs++-- | Cyclic list of all public, non-prime multisig key derivations of a list+-- of parent keys starting from an offset index.+mulSigSubKeys :: [XPubKey] -> Word32 -> [([XPubKey],Word32)]+mulSigSubKeys pubs i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (mulSigSubKey pubs j) (return j)++cycleIndex :: Word32 -> [Word32]+cycleIndex i+ | i == 0 = cycle [0..0x7fffffff]+ | i < 0x80000000 = cycle $ [i..0x7fffffff] ++ [0..(i-1)]+ | otherwise = error $ "cycleIndex: invalid index " ++ (show i)++-- Cycle in reverse+cycleIndex' :: Word32 -> [Word32]+cycleIndex' i+ | i == 0 = cycle $ 0 : [0x7fffffff,0x7ffffffe..1]+ | i == 0x7fffffff = cycle [0x7fffffff,0x7ffffffe..0]+ | i == 0x7ffffffe = cycle $ [0x7ffffffe,0x7ffffffd..0] ++ [0x7fffffff]+ | i < 0x80000000 = cycle $ [i,(i-1)..0] ++ [0x7fffffff,0x7ffffffe..(i+1)]+ | otherwise = error $ "cycleIndex: invalid index " ++ (show i)++guardIndex :: Word32 -> Maybe ()+guardIndex child = guard $ child >= 0 && child < 0x80000000++-- | Returns True if the extended private key was derived through a prime+-- derivation.+xPrvIsPrime :: XPrvKey -> Bool+xPrvIsPrime k = testBit (xPrvIndex k) 31++-- | Returns True if the extended public key was derived through a prime+-- derivation.+xPubIsPrime :: XPubKey -> Bool+xPubIsPrime k = testBit (xPubIndex k) 31++-- | Returns the derivation index of this extended private key without the+-- prime bit set.+xPrvChild :: XPrvKey -> Word32+xPrvChild k = clearBit (xPrvIndex k) 31++-- | Returns the derivation index of this extended public key without the prime+-- bit set.+xPubChild :: XPubKey -> Word32+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 = hash160 . hash256BS . encode' . xPubKey ++-- | Computes the key fingerprint of an extended private key.+xPrvFP :: XPrvKey -> Word32+xPrvFP = fromIntegral . (`shiftR` 128) . xPrvID++-- | Computes the key fingerprint of an extended public key.+xPubFP :: XPubKey -> Word32+xPubFP = fromIntegral . (`shiftR` 128) . xPubID++-- | Computer the 'Address' of an extended public key.+xPubAddr :: XPubKey -> Address+xPubAddr = pubKeyAddr . xPubKey++-- | Exports an extended private key to the BIP32 key export format (base 58).+xPrvExport :: XPrvKey -> String+xPrvExport = bsToString . encodeBase58Check . encode' ++-- | Exports an extended public key to the BIP32 key export format (base 58).+xPubExport :: XPubKey -> String+xPubExport = bsToString . encodeBase58Check . encode'++-- | Decodes a BIP32 encoded extended private key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPrvImport :: String -> Maybe XPrvKey+xPrvImport str = decodeToMaybe =<< (decodeBase58Check $ stringToBS str)++-- | Decodes a BIP32 encoded extended public key. This function will fail if+-- invalid base 58 characters are detected or if the checksum fails.+xPubImport :: String -> Maybe XPubKey+xPubImport str = decodeToMaybe =<< (decodeBase58Check $ stringToBS str)++-- | Export an extended private key to WIF (Wallet Import Format).+xPrvWIF :: XPrvKey -> String+xPrvWIF = toWIF . xPrvKey++instance Binary XPrvKey where++ get = do+ ver <- getWord32be+ unless (ver == extSecretPrefix) $ fail $+ "Get: Invalid version for extended private key"+ dep <- getWord8+ par <- getWord32be+ idx <- getWord32be+ chn <- get + prv <- getPadPrvKey+ return $ XPrvKey dep par idx chn prv++ put k = do+ putWord32be extSecretPrefix+ putWord8 $ xPrvDepth k+ putWord32be $ xPrvParent k+ putWord32be $ xPrvIndex k+ put $ xPrvChain k+ putPadPrvKey $ xPrvKey k++instance Binary XPubKey where++ get = do+ ver <- getWord32be+ unless (ver == extPubKeyPrefix) $ fail $+ "Get: Invalid version for extended public key"+ dep <- getWord8+ par <- getWord32be+ idx <- getWord32be+ chn <- get + pub <- get + when (isPubKeyU pub) $ fail $+ "Invalid public key. Only compressed format is supported"+ return $ XPubKey dep par idx chn pub++ put k = do+ putWord32be extPubKeyPrefix+ putWord8 $ xPubDepth k+ putWord32be $ xPubParent k+ putWord32be $ xPubIndex k+ put $ xPubChain k+ when (isPubKeyU (xPubKey k)) $ fail $+ "Only compressed public keys are supported"+ put $ xPubKey k+ +{- Utilities for extended keys -}++-- De-serialize HDW-specific private key+getPadPrvKey :: Get PrvKey+getPadPrvKey = do+ pad <- getWord8+ unless (pad == 0x00) $ fail $+ "Private key must be padded with 0x00"+ getPrvKey -- Compressed version++-- Serialize HDW-specific private key+putPadPrvKey :: PrvKey -> Put +putPadPrvKey p = putWord8 0x00 >> putPrvKey p++bsPadPrvKey :: PrvKey -> BS.ByteString+bsPadPrvKey = toStrictBS . runPut . putPadPrvKey +
+ Network/Haskoin/Crypto/Hash.hs view
@@ -0,0 +1,300 @@+-- | Hashing functions and HMAC DRBG definition+module Network.Haskoin.Crypto.Hash+( Hash512+, Hash256+, Hash160+, CheckSum32+, hash512+, hash256+, hash160+, hash512BS+, hash256BS+, hash160BS+, doubleHash256+, doubleHash256BS+, chksum32+, hmac512+, hmac512BS+, hmac256+, hmac256BS+, hmacDRBGNew+, hmacDRBGUpd+, hmacDRBGRsd+, hmacDRBGGen+, WorkingState+, murmurHash3+, split512+, join512+, decodeCompact+, encodeCompact+) where++import Control.Applicative ((<$>))+import Control.Monad (replicateM)++import Crypto.Hash + ( Digest + , SHA512+ , SHA256+ , RIPEMD160+ , hash+ )+import Crypto.MAC.HMAC (hmac)++import Data.Word (Word16, Word32)+import Data.Byteable (toBytes)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Bits + ( shiftL+ , shiftR+ , rotateL+ , xor+ , (.&.), (.|.)+ )++import qualified Data.ByteString as BS + ( ByteString+ , null+ , append+ , cons+ , concat+ , take+ , empty+ , length+ , replicate+ , drop+ )++import Network.Haskoin.Util +import Network.Haskoin.Crypto.BigWord ++-- | Data type representing a 32 bit checksum+newtype CheckSum32 = CheckSum32 Word32 deriving (Show, Eq, Read)++instance Binary CheckSum32 where+ get = CheckSum32 <$> get+ put (CheckSum32 w) = put w++run512 :: BS.ByteString -> BS.ByteString+run512 = (toBytes :: Digest SHA512 -> BS.ByteString) . hash++run256 :: BS.ByteString -> BS.ByteString+run256 = (toBytes :: Digest SHA256 -> BS.ByteString) . hash++run160 :: BS.ByteString -> BS.ByteString+run160 = (toBytes :: Digest RIPEMD160 -> BS.ByteString) . hash++-- | Computes SHA-512.+hash512 :: BS.ByteString -> Hash512+hash512 bs = runGet' get (run512 bs)++-- | Computes SHA-512 and returns the result as a bytestring.+hash512BS :: BS.ByteString -> BS.ByteString+hash512BS bs = run512 bs++-- | Computes SHA-256.+hash256 :: BS.ByteString -> Hash256+hash256 bs = runGet' get (run256 bs)++-- | Computes SHA-256 and returns the result as a bytestring.+hash256BS :: BS.ByteString -> BS.ByteString+hash256BS bs = run256 bs++-- | Computes RIPEMD-160.+hash160 :: BS.ByteString -> Hash160+hash160 bs = runGet' get (run160 bs)++-- | Computes RIPEMD-160 and returns the result as a bytestring.+hash160BS :: BS.ByteString -> BS.ByteString+hash160BS bs = run160 bs++-- | Computes two rounds of SHA-256.+doubleHash256 :: BS.ByteString -> Hash256+doubleHash256 bs = runGet' get (run256 $ run256 bs)++-- | Computes two rounds of SHA-256 and returns the result as a bytestring.+doubleHash256BS :: BS.ByteString -> BS.ByteString+doubleHash256BS bs = run256 $ run256 bs++{- CheckSum -}++-- | Computes a 32 bit checksum.+chksum32 :: BS.ByteString -> CheckSum32+chksum32 bs = CheckSum32 $ fromIntegral $ (doubleHash256 bs) `shiftR` 224++{- HMAC -}++-- | Computes HMAC over SHA-512.+hmac512 :: BS.ByteString -> BS.ByteString -> Hash512+hmac512 key = decode' . (hmac512BS key)++-- | Computes HMAC over SHA-512 and return the result as a bytestring.+hmac512BS :: BS.ByteString -> BS.ByteString -> BS.ByteString+hmac512BS key msg = hmac hash512BS 128 key msg++-- | Computes HMAC over SHA-256.+hmac256 :: BS.ByteString -> BS.ByteString -> Hash256+hmac256 key = decode' . (hmac256BS key)++-- | Computes HMAC over SHA-256 and return the result as a bytestring.+hmac256BS :: BS.ByteString -> BS.ByteString -> BS.ByteString+hmac256BS key msg = hmac hash256BS 64 key msg++-- | Split a 'Hash512' into a pair of 'Hash256'.+split512 :: Hash512 -> (Hash256, Hash256)+split512 i = (fromIntegral $ i `shiftR` 256, fromIntegral i)++-- | Join a pair of 'Hash256' into a 'Hash512'.+join512 :: (Hash256, Hash256) -> Hash512+join512 (a,b) = ((toMod512 a) `shiftL` 256) + (toMod512 b)++-- | Decode the compact number used in the difficulty target of a block into an+-- Integer. +--+-- As described in the Satoshi reference implementation /src/bignum.h:+--+-- The "compact" format is a representation of a whole number N using an+-- unsigned 32bit 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 "number of bytes of N". The lower 23 bits are the mantissa.+-- Bit number 24 (0x800000) represents the sign of N. +--+-- > N = (-1^sign) * mantissa * 256^(exponent-3)+decodeCompact :: Word32 -> Integer+decodeCompact c = + if neg then (-res) else res+ where+ size = fromIntegral $ c `shiftR` 24+ neg = (c .&. 0x00800000) /= 0+ wrd = c .&. 0x007fffff+ res | size <= 3 = (toInteger wrd) `shiftR` (8*(3 - size))+ | otherwise = (toInteger wrd) `shiftL` (8*(size - 3))++-- | Encode an Integer to the compact number format used in the difficulty +-- target of a block.+encodeCompact :: Integer -> Word32+encodeCompact i + | i < 0 = c3 .|. 0x00800000+ | otherwise = c3+ where+ posi = abs i+ s1 = BS.length $ integerToBS posi+ c1 | s1 < 3 = posi `shiftL` (8*(3 - s1))+ | otherwise = posi `shiftR` (8*(s1 - 3))+ (s2,c2) | c1 .&. 0x00800000 /= 0 = (s1 + 1, c1 `shiftR` 8)+ | otherwise = (s1, c1)+ c3 = fromIntegral $ c2 .|. ((toInteger s2) `shiftL` 24)++{- 10.1.2 HMAC_DRBG with HMAC-SHA256+ http://csrc.nist.gov/publications/nistpubs/800-90A/SP800-90A.pdf + Constants are based on recommentations in Appendix D section 2 (D.2)+-}++type WorkingState = (BS.ByteString, BS.ByteString, Word16)+type AdditionalInput = BS.ByteString+type ProvidedData = BS.ByteString+type EntropyInput = BS.ByteString+type Nonce = BS.ByteString+type PersString = BS.ByteString++-- 10.1.2.2 HMAC DRBG Update FUnction+hmacDRBGUpd :: ProvidedData -> BS.ByteString -> BS.ByteString+ -> (BS.ByteString, BS.ByteString)+hmacDRBGUpd info k0 v0 + | BS.null info = (k1,v1) -- 10.1.2.2.3+ | otherwise = (k2,v2) -- 10.1.2.2.6+ where + k1 = hmac256BS k0 $ v0 `BS.append` (0 `BS.cons` info) -- 10.1.2.2.1+ v1 = hmac256BS k1 v0 -- 10.1.2.2.2+ k2 = hmac256BS k1 $ v1 `BS.append` (1 `BS.cons` info) -- 10.1.2.2.4+ v2 = hmac256BS k2 v1 -- 10.1.2.2.5++-- 10.1.2.3 HMAC DRBG Instantiation+hmacDRBGNew :: EntropyInput -> Nonce -> PersString -> WorkingState+hmacDRBGNew seed nonce info + | (BS.length seed + BS.length nonce) * 8 < 384 = error $+ "Entropy + nonce input length must be at least 384 bit"+ | (BS.length seed + BS.length nonce) * 8 > 1000 = error $+ "Entropy + nonce input length can not be greater than 1000 bit"+ | BS.length info * 8 > 256 = error $ + "Maximum personalization string length is 256 bit"+ | otherwise = (k1,v1,1) -- 10.1.2.3.6+ where + s = BS.concat [seed, nonce, info] -- 10.1.2.3.1+ k0 = BS.replicate 32 0 -- 10.1.2.3.2+ v0 = BS.replicate 32 1 -- 10.1.2.3.3+ (k1,v1) = hmacDRBGUpd s k0 v0 -- 10.1.2.3.4++-- 10.1.2.4 HMAC DRBG Reseeding+hmacDRBGRsd :: WorkingState -> EntropyInput -> AdditionalInput -> WorkingState+hmacDRBGRsd (k,v,_) seed info + | BS.length seed * 8 < 256 = error $ + "Entropy input length must be at least 256 bit"+ | BS.length seed * 8 > 1000 = error $ + "Entropy input length can not be greater than 1000 bit"+ | otherwise = (k0,v0,1) -- 10.1.2.4.4+ where + s = seed `BS.append` info -- 10.1.2.4.1+ (k0,v0) = hmacDRBGUpd s k v -- 10.1.2.4.2++-- 10.1.2.5 HMAC DRBG Generation+hmacDRBGGen :: WorkingState -> Word16 -> AdditionalInput+ -> (WorkingState, Maybe BS.ByteString)+hmacDRBGGen (k0,v0,c0) bytes info + | bytes * 8 > 7500 = error "Maximum bits per request is 7500"+ | c0 > 10000 = ((k0,v0,c0), Nothing) -- 10.1.2.5.1+ | otherwise = ((k2,v3,c1), Just res) -- 10.1.2.5.8+ where + (k1,v1) | BS.null info = (k0,v0) + | otherwise = hmacDRBGUpd info k0 v0 -- 10.1.2.5.2+ (tmp,v2) = go (fromIntegral bytes) k1 v1 BS.empty -- 10.1.2.5.3/4+ res = BS.take (fromIntegral bytes) tmp -- 10.1.2.5.5+ (k2,v3) = hmacDRBGUpd info k1 v2 -- 10.1.2.5.6+ c1 = c0 + 1 -- 10.1.2.5.7+ go l k v acc | BS.length acc >= l = (acc,v)+ | otherwise = let vn = hmac256BS k v + in go l k vn (acc `BS.append` vn)++{- MurmurHash3 -}+ +-- | MurmurHash3 (x86_32). For more details, see+-- http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp+-- This code is used in the bloom filters of SPV nodes.+murmurHash3 :: Word32 -> BS.ByteString -> Word32+murmurHash3 nHashSeed bs = h8+ where+ -- Block and tail sizes+ nBlocks = BS.length bs `div` 4+ nTail = BS.length bs `mod` 4+ -- Data objects+ blocks = runGet' (replicateM nBlocks getWord32le) bs+ bsTail = BS.drop (nBlocks*4) bs `BS.append` BS.replicate (4-nTail) 0+ -- Body+ h1 = foldl mix nHashSeed blocks+ -- Tail+ t1 = runGet' getWord32le bsTail+ t2 = t1 * c1+ t3 = t2 `rotateL` 15+ t4 = t3 * c2+ h2 = h1 `xor` t4+ -- Finalization+ h3 = h2 `xor` (fromIntegral $ BS.length bs)+ h4 = h3 `xor` (h3 `shiftR` 16) + h5 = h4 * 0x85ebca6b+ h6 = h5 `xor` (h5 `shiftR` 13)+ h7 = h6 * 0xc2b2ae35+ h8 = h7 `xor` (h7 `shiftR` 16)+ -- Mix function+ mix r1 k1 = r4+ where+ k2 = k1 * c1+ k3 = k2 `rotateL` 15+ k4 = k3 * c2+ r2 = r1 `xor` k4+ r3 = r2 `rotateL` 13+ r4 = r3*5 + 0xe6546b64+ -- Constants+ c1 = 0xcc9e2d51 + c2 = 0x1b873593 +
+ Network/Haskoin/Crypto/Keys.hs view
@@ -0,0 +1,258 @@+module Network.Haskoin.Crypto.Keys+( PubKey(..)+, isValidPubKey+, isPubKeyU+, derivePubKey+, pubKeyAddr+, addPubKeys+, PrvKey(..)+, isValidPrvKey+, makePrvKey+, makePrvKeyU+, fromPrvKey+, isPrvKeyU+, addPrvKeys+, putPrvKey+, getPrvKey+, getPrvKeyU+, fromWIF+, toWIF+, curveG+) where++import Data.Binary (Binary, get, put)+import Data.Binary.Get (Get, getWord8)+import Data.Binary.Put (Put, putWord8)++import Control.Monad (when, unless, guard)+import Control.Applicative ((<$>),(<*>))+import Data.Maybe (isJust, fromJust)++import qualified Data.ByteString as BS + ( head, tail+ , last, init+ , cons, snoc+ , length+ )+import Network.Haskoin.Crypto.Curve +import Network.Haskoin.Crypto.BigWord +import Network.Haskoin.Crypto.Point +import Network.Haskoin.Crypto.Base58 +import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Util ++-- | G parameter of the EC curve expressed as a Point+curveG :: Point+curveG = fromJust $ makePoint (fromInteger $ fst pairG) + (fromInteger $ snd pairG)++-- | Elliptic curve public key type. Two constructors are provided for creating+-- compressed and uncompressed public keys from a Point. The use of compressed+-- keys is preferred as it produces shorter keys without compromising security.+-- Uncompressed keys are supported for backwards compatibility.+data PubKey + -- | Compressed public key+ = PubKey { pubKeyPoint :: !Point } + -- | Uncompressed public key+ | PubKeyU { pubKeyPoint :: !Point }+ deriving (Read, Show)++instance Eq PubKey where+ -- Compression does not matter for InfPoint+ (PubKey InfPoint) == (PubKeyU InfPoint) = True+ (PubKeyU InfPoint) == (PubKey InfPoint) = True+ (PubKey a) == (PubKey b) = a == b+ (PubKeyU a) == (PubKeyU b) = a == b+ _ == _ = False++-- | Returns True if the public key is valid. This will check if the public+-- key point lies on the curve.+isValidPubKey :: PubKey -> Bool+isValidPubKey = validatePoint . pubKeyPoint++-- | Add a public key to a private key defined by its Hash256 value. This will+-- transform the private key into a public key and add the respective public+-- key points together. This is provided as a helper for BIP32 wallet+-- implementations. This function fails for uncompressed keys and returns+-- Nothing if the private key value is >= than the order of the curve N.+addPubKeys :: PubKey -> Hash256 -> Maybe PubKey+addPubKeys pub i+ | isPubKeyU pub = error "Add: HDW only supports compressed formats"+ | toInteger i < curveN =+ let pt1 = mulPoint (toFieldN i) curveG+ pt2 = addPoint (pubKeyPoint pub) pt1+ in if isInfPoint pt2 then Nothing+ else Just $ PubKey pt2+ | otherwise = Nothing++-- | Returns True if the public key is uncompressed+isPubKeyU :: PubKey -> Bool+isPubKeyU (PubKey _) = False+isPubKeyU (PubKeyU _) = True++-- | Derives a public key from a private key. This function will preserve+-- information on key compression (PrvKey becomes PubKey and PrvKeyU becomes+-- PubKeyU)+derivePubKey :: PrvKey -> PubKey+derivePubKey k = case k of+ (PrvKey d) -> PubKey $ mulPoint d curveG+ (PrvKeyU d) -> PubKeyU $ mulPoint d curveG++instance Binary PubKey where++ -- Section 2.3.4 http://www.secg.org/download/aid-780/sec1-v2.pdf+ get = go =<< getWord8+ where + -- skip 2.3.4.1 and fail. InfPoint is an invalid public key+ go 0 = fail "InfPoint is not a valid public key"+ -- 2.3.4.3 Uncompressed format+ go 4 = getUncompressed+ -- 2.3.4.2 Compressed format+ -- 2 means pY is even, 3 means pY is odd+ go y | y == 2 || y == 3 = getCompressed (even y)+ | otherwise = fail "Get: Invalid public key encoding"++ -- Section 2.3.3 http://www.secg.org/download/aid-780/sec1-v2.pdf+ put pk = case getAffine (pubKeyPoint pk) of+ -- 2.3.3.1+ Nothing -> putWord8 0x00+ Just (x,y) -> case pk of+ -- Compressed+ PubKey _ -> putWord8 (if even y then 2 else 3) >> put x+ -- Uncompressed+ PubKeyU _ -> putWord8 4 >> put x >> put y++getUncompressed :: Get PubKey+getUncompressed = do+ p <- makePoint <$> get <*> get+ unless (isJust p) (fail "Get: Point not on the curve")+ return $ PubKeyU $ fromJust $ p++getCompressed :: Bool -> Get PubKey+getCompressed e = do+ -- 2.1 + x <- get :: Get FieldP+ -- 2.4.1 (deriving yP)+ let a = x ^ (3 :: Integer) + (curveA * x) + curveB+ ys = filter matchSign (quadraticResidue a)+ -- We found no square root (mod p)+ when (null ys) (fail $ "No ECC point for x = " ++ (show x))+ let p = makePoint x (head ys)+ -- Additionally, check that the point is on the curve+ unless (isJust p) (fail "Get: Point not on the curve")+ return $ PubKey $ fromJust $ p+ where + matchSign a = (even a) == e++-- | Computes an Address value from a public key+pubKeyAddr :: PubKey -> Address+pubKeyAddr = PubKeyAddress . hash160 . hash256BS . encode'++{- Private Keys -}++-- | Elliptic curve private key type. Two constructors are provided for creating+-- compressed or uncompressed private keys. Compression information is stored+-- in private key WIF formats and needs to be preserved to generate the correct+-- addresses from the corresponding public key. +data PrvKey + -- | Compressed private key+ = PrvKey { prvKeyFieldN :: !FieldN } + -- | Uncompressed private key+ | PrvKeyU { prvKeyFieldN :: !FieldN } + deriving (Eq, Show, Read)++-- | Returns True if the private key is valid. This will check if the integer+-- value representing the private key is greater than 0 and smaller than the+-- curve order N.+isValidPrvKey :: Integer -> Bool+isValidPrvKey = isIntegerValidKey++-- | Builds a compressed private key from an Integer value. Returns Nothing if+-- the Integer would not produce a valid private key. For security, the Integer+-- needs to be generated from a random source with sufficient entropy.+makePrvKey :: Integer -> Maybe PrvKey+makePrvKey i+ | isValidPrvKey i = Just $ PrvKey $ fromInteger i+ | otherwise = Nothing++-- | Builds an uncompressed private key from an Integer value. Returns Nothing+-- if the Integer would not produce a valid private key. For security, the+-- Integer needs to be generated from a random source with sufficient entropy.+makePrvKeyU :: Integer -> Maybe PrvKey+makePrvKeyU i+ | isValidPrvKey i = Just $ PrvKeyU $ fromInteger i+ | otherwise = Nothing++-- | Returns the Integer value of a private key+fromPrvKey :: PrvKey -> Integer+fromPrvKey = fromIntegral . prvKeyFieldN++-- | Add two private keys together. One of the keys is defined by a Hash256.+-- The functions fails on uncompressed private keys and return Nothing if the+-- Hash256 is smaller than the order of the curve N. This is provided+-- as a helper for implementing BIP32 wallets.+addPrvKeys :: PrvKey -> Hash256 -> Maybe PrvKey+addPrvKeys key i+ | isPrvKeyU key = error "Add: HDW only supports compressed formats"+ | toInteger i < curveN =+ let r = (prvKeyFieldN key) + (toFieldN i) + in makePrvKey $ toInteger r+ | otherwise = Nothing++-- | Returns True of the private key is uncompressed+isPrvKeyU :: PrvKey -> Bool+isPrvKeyU (PrvKey _) = False+isPrvKeyU (PrvKeyU _) = True++-- | Serialize a private key into the Data.Binary.Put monad as a 32 byte+-- big endian ByteString. This is useful when a constant length serialization+-- format for private keys is required+putPrvKey :: PrvKey -> Put+putPrvKey k | prvKeyFieldN k == 0 = error "Put: 0 is an invalid private key"+ | otherwise = put $ toMod256 $ prvKeyFieldN k++-- | Deserializes a compressed private key from the Data.Binary.Get monad as a+-- 32 byte big endian ByteString.+getPrvKey :: Get PrvKey+getPrvKey = do+ i <- get :: Get Hash256+ let res = makePrvKey $ fromIntegral i+ unless (isJust res) $ fail "Get: PrivateKey is invalid"+ return $ fromJust res++-- | Deserializes an uncompressed private key from the Data.Binary.Get monad as+-- a 32 byte big endian ByteString+getPrvKeyU :: Get PrvKey+getPrvKeyU = do+ i <- get :: Get Hash256+ let res = makePrvKeyU $ fromIntegral i+ unless (isJust res) $ fail "Get: PrivateKey is invalid"+ return $ fromJust res++-- | Decodes a private key from a WIF encoded String. This function can fail+-- if the input string does not decode correctly as a base 58 string or if +-- the checksum fails.+-- <http://en.bitcoin.it/wiki/Wallet_import_format>+fromWIF :: String -> Maybe PrvKey+fromWIF str = do+ bs <- decodeBase58Check $ stringToBS str+ -- Check that this is a private key+ guard (BS.head bs == secretPrefix) + case BS.length bs of+ 33 -> do -- Uncompressed format+ let i = bsToInteger (BS.tail bs)+ makePrvKeyU i+ 34 -> do -- Compressed format+ guard (BS.last bs == 0x01) + let i = bsToInteger $ BS.tail $ BS.init bs+ makePrvKey i+ _ -> Nothing -- Bad length++-- | Encodes a private key into WIF format+toWIF :: PrvKey -> String+toWIF k = bsToString $ encodeBase58Check $ BS.cons secretPrefix enc+ where + enc | isPrvKeyU k = bs+ | otherwise = BS.snoc bs 0x01+ bs = runPut' $ putPrvKey k+
+ Network/Haskoin/Crypto/Merkle.hs view
@@ -0,0 +1,130 @@+module Network.Haskoin.Crypto.Merkle +( calcTreeHeight+, calcTreeWidth+, buildMerkleRoot+, calcHash+, buildPartialMerkle+, extractMatches+) where++import Data.Bits+import Data.Maybe+import qualified Data.ByteString as BS ++import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Util++-- | Computes the height of a merkle tree.+calcTreeHeight :: Int -- ^ Number of transactions (leaf nodes).+ -> Int -- ^ Height of the merkle tree.+calcTreeHeight ntx = ceiling $ log (fromIntegral ntx :: Double) / log 2++-- | 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 :: [Hash256] -- ^ List of transaction hashes (leaf nodes).+ -> Hash256 -- ^ Root of the merkle tree.+buildMerkleRoot txs = calcHash (calcTreeHeight $ length txs) 0 txs++hash2 :: Hash256 -> Hash256 -> Hash256+hash2 a b = doubleHash256 $ encode' a `BS.append` encode' b++-- | Computes the hash of a specific node in a merkle tree.+calcHash :: Int -- ^ Height of the node in the merkle tree.+ -> Int -- ^ Position of the node (0 for the leftmost node).+ -> [Hash256] -- ^ Transaction hashes of the merkle tree (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 = 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.+buildPartialMerkle + :: [(Hash256,Bool)] + -- ^ List of transactions hashes forming the leaves of the merkle tree+ -- and a bool indicating if that transaction should be included in the + -- partial merkle tree.+ -> ([Bool], [Hash256]) + -- ^ Flag bits (used to parse the partial merkle tree) and the + -- partial merkle tree.+buildPartialMerkle hs = traverseAndBuild (calcTreeHeight $ length hs) 0 hs++traverseAndBuild :: Int -> Int -> [(Hash256,Bool)] -> ([Bool], [Hash256])+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 = or $ map 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 = ([],[])++traverseAndExtract :: Int -> Int -> Int -> [Bool] -> [Hash256] + -> Maybe (Hash256, [Hash256], Int, Int)+traverseAndExtract height pos ntx flags hashes+ | length flags == 0 = 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,if height == 0 && match then [h] else [],1,1)+ (match:fs) = flags+ (h:_) = hashes+ leftM = traverseAndExtract (height-1) (pos*2) ntx fs hashes+ (lh,lm,lcf,lch) = fromJust leftM+ rightM = traverseAndExtract (height-1) (pos*2+1) ntx + (drop lcf fs) (drop lch hashes)+ (rh,rm,rcf,rch) = fromJust rightM++-- | 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 :: [Bool] -- ^ Flag bits (produced by buildPartialMerkle).+ -> [Hash256] -- ^ Partial merkle tree.+ -> Int -- ^ Number of transaction at height 0 (leaf nodes).+ -> Either String (Hash256, [Hash256])+ -- ^ Merkle root and the list of matching transaction hashes.+extractMatches flags hashes ntx+ | ntx == 0 = Left $+ "extractMatches: number of transactions can not be 0"+ | ntx > maxBlockSize `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 (merkleRoot, matches)+ where+ resM = traverseAndExtract (calcTreeHeight ntx) 0 ntx flags hashes+ (merkleRoot, matches, nBitsUsed, nHashUsed) = fromJust resM+++
+ Network/Haskoin/Crypto/Mnemonic.hs view
@@ -0,0 +1,500 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Mnemonic keys (BIP-0039)+module Network.Haskoin.Crypto.Mnemonic+(+ -- * Data types+ WordList+, Entropy+, Mnemonic+, Passphrase+, Seed++ -- * Entropy encoding and decoding+, toMnemonic+, fromMnemonic++ -- * Generating 512-bit seeds+, anyToSeed+, mnemonicToSeed++ -- * Dictionaries+, english++ -- * Helper functions+, getBits+) where++import Control.Monad (when)+import qualified Crypto.Hash.SHA256 as SHA256+import Crypto.PBKDF.ByteString (sha512PBKDF2)+import Data.Bits ((.&.), shiftL, shiftR)+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import Data.List+import Data.Maybe+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding (encodeUtf8)+import Data.Text.ICU.Normalize (NormalizationMode(NFKD), normalize)+import Network.Haskoin.Util (bsToInteger, integerToBS)++type WordList = [Text]+type Entropy = ByteString+type Mnemonic = Text+type Passphrase = Text+type Seed = ByteString+type Checksum = ByteString++-- | Provide dictionary and intial entropy as a 'ByteString' with length+-- multiple of 32 bits (4 bytes). Output a mnemonic sentence as 'Text'.+toMnemonic :: WordList -> Entropy -> Either String Mnemonic+toMnemonic wl ent = do+ when (remainder /= 0)+ $ Left "toMnemonic: entropy must be multiples of 32 bits"+ when (cs_len > 16)+ $ Left "toMnemonic: maximum entropy is 512 bits"+ return ms+ where+ (cs_len, remainder) = BS.length ent `quotRem` 4+ cs = calcCS cs_len ent+ indices = bsToIndices $ ent `BS.append` cs+ ms = T.unwords $ map (wl !!) indices++-- | Revert 'toMnemonic'. Do not use this to generate seeds. Instead use+-- 'mnemonicToSeed'.+fromMnemonic :: WordList -> Mnemonic -> Either String Entropy+fromMnemonic wl ms = do+ 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 wl 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 $ normalize NFKD 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++calcCS :: Int -> Entropy -> Checksum+calcCS len = getBits len . SHA256.hash++numCS :: Int -> Entropy -> Integer+numCS len = shiftCS . bsToInteger+ where+ shiftCS = case 8 - len `mod` 8 of+ 0 -> id+ x -> flip shiftR x++-- | Turn any sequence of characters to a seed. Does not have to be a mnemonic+-- sentence generated from 'toMnemonic'. Use 'mnemonicToSeed' to get a seed+-- from a mnemonic sentence.+anyToSeed :: Passphrase -> Mnemonic -> Seed+anyToSeed pf ms = sha512PBKDF2 p s 2048 64+ where+ p = encodeUtf8 $ normalize NFKD ms+ s = encodeUtf8 $ normalize NFKD ("mnemonic" `T.append` pf)++-- | Get a seed from a mnemonic sentence. Will calculate checksum. Requires+-- same dictionary used to generate the mnemonic sentence as first argument.+-- Passphrase can be used to protect the mnemonic. Use an empty string as+-- passphrase if none is required.+mnemonicToSeed :: WordList -> Passphrase -> Mnemonic -> Either String Seed+mnemonicToSeed wl pf ms = do+ ent <- fromMnemonic wl ms+ mnm <- toMnemonic wl ent+ return $ anyToSeed pf mnm++-- | 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++-- Expects already normalized list of words+getIndices :: WordList -> [Text] -> Either String [Int]+getIndices wl ws+ | null n = return . fromJust $ sequence i+ | otherwise = Left $ "getIndices: words not found: " ++ T.unpack w+ where+ i = map (flip elemIndex wl) ws+ n = elemIndices Nothing i+ w = T.unwords $ map (ws !!) n++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 = not . isNothing $ 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++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)++-- | Standard English dictionary from BIP-0039 specification.+english :: [Text]+english =+ [ "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"+ ]
+ Network/Haskoin/Crypto/NormalizedKeys.hs view
@@ -0,0 +1,292 @@+module Network.Haskoin.Crypto.NormalizedKeys+( MasterKey(..)+, AccPrvKey(..)+, AccPubKey(..)+, AddrPrvKey(..)+, AddrPubKey(..)+, KeyIndex+, makeMasterKey+, loadMasterKey+, loadPrvAcc+, loadPubAcc+, addr+, accPrvKey+, accPubKey+, extPrvKey+, extPubKey+, intPrvKey+, intPubKey+, accPrvKeys+, accPubKeys+, extPrvKeys+, extPubKeys+, intPrvKeys+, intPubKeys+, extAddr+, intAddr+, extAddrs+, intAddrs+, extAddrs'+, intAddrs'+, extMulSigKey+, intMulSigKey+, extMulSigKeys+, intMulSigKeys+, extMulSigAddr+, intMulSigAddr+, extMulSigAddrs+, intMulSigAddrs+) where++import Control.Monad (liftM2, guard)+import Control.Applicative ((<$>))++import Data.Word (Word32)+import Data.Maybe (mapMaybe, fromJust, isJust)+import qualified Data.ByteString as BS (ByteString)++import Network.Haskoin.Crypto.ExtendedKeys+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Script.Parser++type KeyIndex = Word32++-- | Data type representing an extended private key at the root of the+-- derivation tree. Master keys have depth 0 and no parents. They are+-- represented as m\/ in BIP32 notation.+newtype MasterKey = MasterKey { masterKey :: XPrvKey }+ deriving (Eq, Show, Read)++-- | Data type representing a private account key. Account keys are generated+-- from a 'MasterKey' through prime derivation. This guarantees that the+-- 'MasterKey' will not be compromised if the account key is compromised. +-- 'AccPrvKey' is represented as m\/i'\/ in BIP32 notation.+newtype AccPrvKey = AccPrvKey { getAccPrvKey :: XPrvKey }+ deriving (Eq, Show, Read)++-- | Data type representing a public account key. It is computed through+-- derivation from an 'AccPrvKey'. It can not be derived from the 'MasterKey'+-- directly (property of prime derivation). It is represented as M\/i'\/ in+-- BIP32 notation. 'AccPubKey' is used for generating receiving payment+-- addresses without the knowledge of the 'AccPrvKey'.+newtype AccPubKey = AccPubKey { getAccPubKey :: XPubKey }+ deriving (Eq, Show, Read)++-- | Data type representing a private address key. Private address keys are+-- generated through a non-prime derivation from an 'AccPrvKey'. Non-prime+-- derivation is used so that the public account key can generate the receiving+-- payment addresses without knowledge of the private account key. 'AccPrvKey'+-- is represented as m\/i'\/0\/j\/ in BIP32 notation if it is a regular+-- receiving address. Internal (change) addresses are represented as+-- m\/i'\/1\/j\/. Non-prime subtree 0 is used for regular receiving addresses+-- and non-prime subtree 1 for internal (change) addresses.+newtype AddrPrvKey = AddrPrvKey { getAddrPrvKey :: XPrvKey }+ deriving (Eq, Show, Read)++-- | Data type representing a public address key. They are generated through+-- non-prime derivation from an 'AccPubKey'. This is a useful feature for+-- read-only wallets. They are represented as M\/i'\/0\/j in BIP32 notation+-- for regular receiving addresses and by M\/i'\/1\/j for internal (change)+-- addresses.+newtype AddrPubKey = AddrPubKey { getAddrPubKey :: XPubKey }+ deriving (Eq, Show, Read)++-- | Create a 'MasterKey' from a seed.+makeMasterKey :: BS.ByteString -> Maybe MasterKey+makeMasterKey bs = MasterKey <$> makeXPrvKey bs++-- | Load a 'MasterKey' from an 'XPrvKey'. This function will fail if the+-- extended private key does not have the properties of a 'MasterKey'.+loadMasterKey :: XPrvKey -> Maybe MasterKey+loadMasterKey k+ | xPrvDepth k == 0 && + xPrvParent k == 0 && + xPrvIndex k == 0 = Just $ MasterKey k+ | otherwise = Nothing++-- | Load a private account key from an 'XPrvKey'. This function will fail if+-- the extended private key does not have the properties of a 'AccPrvKey'.+loadPrvAcc :: XPrvKey -> Maybe AccPrvKey+loadPrvAcc k+ | xPrvDepth k == 1 &&+ xPrvIsPrime k = Just $ AccPrvKey k+ | otherwise = Nothing++-- | Load a public account key from an 'XPubKey'. This function will fail if+-- the extended public key does not have the properties of a 'AccPubKey'.+loadPubAcc :: XPubKey -> Maybe AccPubKey+loadPubAcc k+ | xPubDepth k == 1 &&+ xPubIsPrime k = Just $ AccPubKey k+ | otherwise = Nothing++-- | Computes an 'AccPrvKey' from a 'MasterKey' and a derivation index.+accPrvKey :: MasterKey -> KeyIndex -> Maybe AccPrvKey+accPrvKey (MasterKey par) i = AccPrvKey <$> (f =<< primeSubKey par i)+ where f k = guard (isJust $ prvSubKey k 0) >>+ guard (isJust $ prvSubKey k 1) >>+ return k++-- | Computes an 'AccPubKey' from a 'MasterKey' and a derivation index.+accPubKey :: MasterKey -> KeyIndex -> Maybe AccPubKey+accPubKey (MasterKey par) i = f <$> primeSubKey par i+ where f = AccPubKey . deriveXPubKey++-- | Computes an external 'AddrPrvKey' from an 'AccPrvKey' and a derivation+-- index.+extPrvKey :: AccPrvKey -> KeyIndex -> Maybe AddrPrvKey+extPrvKey (AccPrvKey par) i = AddrPrvKey <$> prvSubKey extKey i+ where extKey = fromJust $ prvSubKey par 0++-- | Computes an external 'AddrPubKey' from an 'AccPubKey' and a derivation+-- index.+extPubKey :: AccPubKey -> KeyIndex -> Maybe AddrPubKey+extPubKey (AccPubKey par) i = AddrPubKey <$> pubSubKey extKey i+ where extKey = fromJust $ pubSubKey par 0++-- | Computes an internal 'AddrPrvKey' from an 'AccPrvKey' and a derivation+-- index.+intPrvKey :: AccPrvKey -> KeyIndex -> Maybe AddrPrvKey+intPrvKey (AccPrvKey par) i = AddrPrvKey <$> prvSubKey intKey i+ where intKey = fromJust $ prvSubKey par 1++-- | Computes an internal 'AddrPubKey' from an 'AccPubKey' and a derivation+-- index.+intPubKey :: AccPubKey -> KeyIndex -> Maybe AddrPubKey+intPubKey (AccPubKey par) i = AddrPubKey <$> pubSubKey intKey i+ where intKey = fromJust $ pubSubKey par 1++-- | Cyclic list of all valid 'AccPrvKey' derived from a 'MasterKey' and+-- starting from an offset index.+accPrvKeys :: MasterKey -> KeyIndex -> [(AccPrvKey,KeyIndex)]+accPrvKeys m i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (accPrvKey m j) (return j)++-- | Cyclic list of all valid 'AccPubKey' derived from a 'MasterKey' and+-- starting from an offset index.+accPubKeys :: MasterKey -> KeyIndex -> [(AccPubKey,KeyIndex)]+accPubKeys m i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (accPubKey m j) (return j)++-- | Cyclic list of all valid external 'AddrPrvKey' derived from a 'AccPrvKey'+-- and starting from an offset index.+extPrvKeys :: AccPrvKey -> KeyIndex -> [(AddrPrvKey,KeyIndex)]+extPrvKeys a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (extPrvKey a j) (return j)++-- | Cyclic list of all valid external 'AddrPubKey' derived from a 'AccPubKey'+-- and starting from an offset index.+extPubKeys :: AccPubKey -> KeyIndex -> [(AddrPubKey,KeyIndex)]+extPubKeys a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (extPubKey a j) (return j)++-- | Cyclic list of all internal 'AddrPrvKey' derived from a 'AccPrvKey' and+-- starting from an offset index.+intPrvKeys :: AccPrvKey -> KeyIndex -> [(AddrPrvKey,KeyIndex)]+intPrvKeys a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (intPrvKey a j) (return j)++-- | Cyclic list of all internal 'AddrPubKey' derived from a 'AccPubKey' and+-- starting from an offset index.+intPubKeys :: AccPubKey -> KeyIndex -> [(AddrPubKey,KeyIndex)]+intPubKeys a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (intPubKey a j) (return j)++{- Generate addresses -}++-- | Computes an 'Address' from an 'AddrPubKey'.+addr :: AddrPubKey -> Address+addr = xPubAddr . getAddrPubKey++-- | Computes an external base58 address from an 'AccPubKey' and a +-- derivation index.+extAddr :: AccPubKey -> KeyIndex -> Maybe String+extAddr a i = addrToBase58 . addr <$> extPubKey a i++-- | Computes an internal base58 addres from an 'AccPubKey' and a +-- derivation index.+intAddr :: AccPubKey -> KeyIndex -> Maybe String+intAddr a i = addrToBase58 . addr <$> intPubKey a i++-- | Cyclic list of all external base58 addresses derived from a 'AccPubKey'+-- and starting from an offset index.+extAddrs :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+extAddrs a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (extAddr a j) (return j)++-- | Cyclic list of all internal base58 addresses derived from a 'AccPubKey'+-- and starting from an offset index.+intAddrs :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+intAddrs a i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (intAddr a j) (return j)++-- | Same as 'extAddrs' with the list reversed.+extAddrs' :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+extAddrs' a i = mapMaybe f $ cycleIndex' i+ where f j = liftM2 (,) (extAddr a j) (return j)++-- | Same as 'intAddrs' with the list reversed.+intAddrs' :: AccPubKey -> KeyIndex -> [(String,KeyIndex)]+intAddrs' a i = mapMaybe f $ cycleIndex' i+ where f j = liftM2 (,) (intAddr a j) (return j)++{- MultiSig -}++-- | Computes a list of external 'AddrPubKey' from an 'AccPubKey', a list+-- of thirdparty multisig keys and a derivation index. This is useful for +-- computing the public keys associated with a derivation index for+-- multisig accounts.+extMulSigKey :: AccPubKey -> [XPubKey] -> KeyIndex -> Maybe [AddrPubKey]+extMulSigKey a ps i = (map AddrPubKey) <$> mulSigSubKey keys i+ where keys = map (fromJust . (flip pubSubKey 0)) $ (getAccPubKey a) : ps++-- | Computes a list of internal 'AddrPubKey' from an 'AccPubKey', a list+-- of thirdparty multisig keys and a derivation index. This is useful for +-- computing the public keys associated with a derivation index for+-- multisig accounts.+intMulSigKey :: AccPubKey -> [XPubKey] -> KeyIndex -> Maybe [AddrPubKey]+intMulSigKey a ps i = (map AddrPubKey) <$> mulSigSubKey keys i+ where keys = map (fromJust . (flip pubSubKey 1)) $ (getAccPubKey a) : ps++-- | Cyclic list of all external multisignature 'AddrPubKey' derivations +-- starting from an offset index.+extMulSigKeys :: AccPubKey -> [XPubKey] -> KeyIndex -> [([AddrPubKey],KeyIndex)]+extMulSigKeys a ps i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (extMulSigKey a ps j) (return j)++-- | Cyclic list of all internal multisignature 'AddrPubKey' derivations+-- starting from an offset index.+intMulSigKeys :: AccPubKey -> [XPubKey] -> KeyIndex -> [([AddrPubKey],KeyIndex)]+intMulSigKeys a ps i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (intMulSigKey a ps j) (return j)++-- | Computes an external base58 multisig address from an 'AccPubKey', a+-- list of thirdparty multisig keys and a derivation index.+extMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String+extMulSigAddr a ps r i = do+ xs <- (map (xPubKey . getAddrPubKey)) <$> extMulSigKey a ps i+ return $ addrToBase58 $ scriptAddr $ sortMulSig $ PayMulSig xs r++-- | Computes an internal base58 multisig address from an 'AccPubKey', a+-- list of thirdparty multisig keys and a derivation index.+intMulSigAddr :: AccPubKey -> [XPubKey] -> Int -> KeyIndex -> Maybe String+intMulSigAddr a ps r i = do+ xs <- (map (xPubKey . getAddrPubKey)) <$> intMulSigKey a ps i+ return $ addrToBase58 $ scriptAddr $ sortMulSig $ PayMulSig xs r++-- | Cyclic list of all external base58 multisig addresses derived from+-- an 'AccPubKey' and a list of thirdparty multisig keys. The list starts+-- at an offset index.+extMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex + -> [(String,KeyIndex)]+extMulSigAddrs a ps r i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (extMulSigAddr a ps r j) (return j)++-- | Cyclic list of all internal base58 multisig addresses derived from+-- an 'AccPubKey' and a list of thirdparty multisig keys. The list starts+-- at an offset index.+intMulSigAddrs :: AccPubKey -> [XPubKey] -> Int -> KeyIndex + -> [(String,KeyIndex)]+intMulSigAddrs a ps r i = mapMaybe f $ cycleIndex i+ where f j = liftM2 (,) (intMulSigAddr a ps r j) (return j)+
+ Network/Haskoin/Crypto/NumberTheory.hs view
@@ -0,0 +1,23 @@+module Network.Haskoin.Crypto.NumberTheory+( extendedModGCD+, mulInverse+) where++-- Extended euclidean algorithm+-- Calculates the multiplicative inverse modulo p+extendedModGCD :: Integer -> Integer -> Integer -> (Integer, Integer)+extendedModGCD a b p+ | b == 0 = (1,0)+ | otherwise = (t, (s - q*t) `mod` p)+ where + (q,r) = quotRem a b+ (s,t) = extendedModGCD b r p++-- Find multiplicative inverse of a : a*s = 1 (mod p)+mulInverse :: Integer -> Integer -> Integer+mulInverse a p + | a*s `mod` p == 1 = s+ | otherwise = error "No multiplicative inverse (mod p) for a"+ where + (s,_) = extendedModGCD a p p+
+ Network/Haskoin/Crypto/Point.hs view
@@ -0,0 +1,147 @@+module Network.Haskoin.Crypto.Point+( Point( InfPoint )+, makePoint+, makeInfPoint+, getAffine, getX, getY+, validatePoint+, isInfPoint+, addPoint+, doublePoint+, mulPoint+, shamirsTrick+, curveB+, curveA+) where++import Data.Bits (shiftR)+import Control.Applicative ((<$>))++import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Crypto.BigWord ++curveA :: FieldP+curveA = fromInteger integerA++curveB :: FieldP+curveB = fromInteger integerB++{-| + Elliptic curves of the form y^2 = x^3 + 7 (mod p)+ Point on the elliptic curve in transformed Jacobian coordinates + (X,Y,Z) such that (x,y) = (X/Z^2, Y/Z^3)+ InfPoint is the point at infinity+-}+data Point = Point !FieldP !FieldP !FieldP | InfPoint+ deriving (Show, Read)++instance Eq Point where+ InfPoint == InfPoint = True+ (Point x1 y1 z1) == (Point x2 y2 z2) = a == b && c == d+ where + a = x1*z2 ^ (2 :: Int)+ b = x2*z1 ^ (2 :: Int)+ c = y1*z2 ^ (3 :: Int)+ d = y2*z1 ^ (3 :: Int)+ _ == _ = False++-- Create a new point from (x,y) coordinates.+-- Returns Nothing if the point doesn't lie on the curve+makePoint :: FieldP -> FieldP -> Maybe Point+makePoint x y+ | validatePoint point = Just point+ | otherwise = Nothing+ where + point = Point x y 1++makeInfPoint :: Point+makeInfPoint = InfPoint++-- Get the original (x,y) coordinates from the Jacobian triple (X,Y,Z)+getAffine :: Point -> Maybe (FieldP, FieldP)+getAffine point = case point of+ InfPoint -> Nothing+ (Point _ _ 0) -> Nothing+ (Point x y z) -> Just (x/z ^ (2 :: Int), y/z ^ (3 :: Int))++getX :: Point -> Maybe FieldP+getX point = fst <$> (getAffine point)++getY :: Point -> Maybe FieldP+getY point = snd <$> (getAffine point)++-- Section 3.2.2.1 http://www.secg.org/download/aid-780/sec1-v2.pdf+-- point 3.2.2.1.4 is not necessary as h=1+validatePoint :: Point -> Bool+validatePoint point = case getAffine point of+ -- 3.2.2.1.1 (check that point not equal to InfPoint)+ Nothing -> False + -- 3.2.2.1.2 (check that the point lies on the curve)+ Just (x,y) -> y ^ (2 :: Int) == x ^ (3 :: Int) + (curveA * x) + curveB++isInfPoint :: Point -> Bool+isInfPoint InfPoint = True+isInfPoint (Point _ _ 0) = True+isInfPoint _ = False++-- Elliptic curve point addition+-- http://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates+addPoint :: Point -> Point -> Point+addPoint InfPoint point = point+addPoint point InfPoint = point+addPoint p1@(Point x1 y1 z1) (Point x2 y2 z2)+ | u1 == u2 = if s1 == s2 then doublePoint p1 else InfPoint+ | otherwise = Point x3 y3 z3+ where + u1 = x1*z2 ^ (2 :: Int)+ u2 = x2*z1 ^ (2 :: Int)+ s1 = y1*z2 ^ (3 :: Int)+ s2 = y2*z1 ^ (3 :: Int)+ h = u2 - u1+ r = s2 - s1+ x3 = r ^ (2 :: Int) - h ^ (3 :: Int) - 2*u1*h ^ (2 :: Int) + y3 = r*(u1 * h ^ (2 :: Int) - x3) - s1 * h ^ (3 :: Int)+ z3 = h * z1 * z2++-- Elliptic curve point doubling +-- http://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates+doublePoint :: Point -> Point+doublePoint InfPoint = InfPoint+doublePoint (Point x y z)+ | y == 0 = InfPoint+ | otherwise = Point x' y' z'+ where + s = 4*x*y ^ (2 :: Int)+ m = 3*x ^ (2 :: Int) + curveA * z ^ (4 :: Int)+ x' = m ^ (2 :: Int) - 2*s+ y' = m*(s - x') - 8*y ^ (4 :: Int)+ z' = 2*y*z++-- Elliptic curve point multiplication+-- Double and add method (weak to side channel attacks)+-- http://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication+mulPoint :: FieldN -> Point -> Point+mulPoint 0 _ = InfPoint+mulPoint 1 p = p+mulPoint _ InfPoint = InfPoint+mulPoint n p + | n == 0 = InfPoint+ | odd n = addPoint p (mulPoint (n-1) p)+ | otherwise = mulPoint (n `shiftR` 1) (doublePoint p)++-- Efficiently compute r1*p1 + r2*p2+shamirsTrick :: FieldN -> Point -> FieldN -> Point -> Point+shamirsTrick r1 p1 r2 p2 = go r1 r2+ where + q = addPoint p1 p2+ go 0 0 = InfPoint+ go a b + | ea && eb = b2+ | ea = addPoint b2 p2+ | eb = addPoint b2 p1+ | otherwise = addPoint b2 q+ where + b2 = doublePoint $ go (a `shiftR` 1) (b `shiftR` 1)+ ea = even a+ eb = even b++
+ Network/Haskoin/Protocol.hs view
@@ -0,0 +1,99 @@+{-|+ This package provides all of the basic types used for the Bitcoin + networking protocol together with Data.Binary instances for efficiently+ serializing and de-serializing them. More information on the bitcoin protocol+ is available here: <http://en.bitcoin.it/wiki/Protocol_specification>+-}+module Network.Haskoin.Protocol+( + -- * Blocks+ Block(..)+, BlockLocator+, GetBlocks(..)++ -- * Block Headers+, BlockHeader(..)+, GetHeaders(..)+, Headers(..)+, BlockHeaderCount+, blockid++ -- * Requesting data+, GetData(..)+, Inv(..)+, InvVector(..)+, InvType(..)+, NotFound(..)++ -- *Scripts+ -- | More informations on scripts is available here:+ -- <http://en.bitcoin.it/wiki/Script>+, Script(..)+, ScriptOp(..)+, PushDataType(..)+, opPushData+, getScriptOps+, putScriptOps+, decodeScriptOps+, encodeScriptOps++ -- *Transactions+, Tx(..)+, txid+, cbid+, CoinbaseTx(..)+, TxIn(..)+, TxOut(..)+, OutPoint(..)+, encodeTxid+, decodeTxid++ -- * Merkle trees and bloom filters+, MerkleBlock(..)++ -- * Bloom Filter+, BloomFlags(..)+, BloomFilter(..)+, FilterLoad(..)+, FilterAdd(..)++ -- * Network types+, VarInt(..)+, VarString(..)+, NetworkAddress(..)+, Addr(..)+, NetworkAddressTime+, Version(..)+, Ping(..)+, Pong(..)+, Alert(..)++ -- *Messages+, Message(..)+, MessageHeader(..)+, MessageCommand(..)+) where++import Network.Haskoin.Protocol.Message+import Network.Haskoin.Protocol.Addr+import Network.Haskoin.Protocol.Alert+import Network.Haskoin.Protocol.BlockHeader+import Network.Haskoin.Protocol.Block+import Network.Haskoin.Protocol.MerkleBlock+import Network.Haskoin.Protocol.GetBlocks+import Network.Haskoin.Protocol.GetData+import Network.Haskoin.Protocol.GetHeaders+import Network.Haskoin.Protocol.Headers+import Network.Haskoin.Protocol.Inv+import Network.Haskoin.Protocol.InvVector+import Network.Haskoin.Protocol.MessageHeader+import Network.Haskoin.Protocol.NetworkAddress+import Network.Haskoin.Protocol.NotFound+import Network.Haskoin.Protocol.Ping+import Network.Haskoin.Protocol.Script+import Network.Haskoin.Protocol.Tx+import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.VarString+import Network.Haskoin.Protocol.Version+import Network.Haskoin.Protocol.BloomFilter+
+ Network/Haskoin/Protocol/Addr.hs view
@@ -0,0 +1,39 @@+module Network.Haskoin.Protocol.Addr +( Addr(..)+, NetworkAddressTime +) where++import Control.Monad (liftM2, replicateM, forM_)+import Control.Applicative ((<$>))++import Data.Word (Word32)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Binary.Put (putWord32le)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.NetworkAddress++-- | Network address with a timestamp+type NetworkAddressTime = (Word32, NetworkAddress)++-- | Provides information on known nodes in the bitcoin network. An 'Addr'+-- type is sent inside a 'Message' as a response to a 'GetAddr' message.+data Addr = + Addr { + -- List of addresses of other nodes on the network with timestamps.+ addrList :: ![NetworkAddressTime] + } + deriving (Eq, Show, Read)++instance Binary Addr where++ get = Addr <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) action+ action = liftM2 (,) getWord32le get ++ put (Addr xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs $ \(a,b) -> (putWord32le a) >> (put b)+
+ Network/Haskoin/Protocol/Alert.hs view
@@ -0,0 +1,21 @@+module Network.Haskoin.Protocol.Alert ( Alert(..) ) where++import Control.Applicative ((<$>),(<*>))+import Data.Binary (Binary, get, put)+import Network.Haskoin.Protocol.VarString++-- | 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)++instance Binary Alert where+ get = Alert <$> get <*> get+ put (Alert p s) = put p >> put s+
+ Network/Haskoin/Protocol/Block.hs view
@@ -0,0 +1,38 @@+module Network.Haskoin.Protocol.Block (Block(..)) where++import Control.Monad (replicateM, forM_)++import Data.Binary (Binary, get, put)++import Network.Haskoin.Protocol.Tx+import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.BlockHeader++-- | Data type describing a block in the bitcoin protocol. Blocks are sent in+-- response to 'GetData' messages that are requesting information from a+-- block hash.+data Block = + Block {+ -- | Header information for this block.+ blockHeader :: !BlockHeader+ -- | Coinbase transaction of this block.+ , blockCoinbaseTx :: !CoinbaseTx+ -- | List of transactions pertaining to this block.+ , blockTxns :: ![Tx]+ } deriving (Eq, Show)++instance Binary Block where++ get = do+ header <- get+ (VarInt c) <- get+ cb <- get+ txs <- replicateM (fromIntegral (c-1)) get+ return $ Block header cb txs++ put (Block h cb txs) = do+ put h+ put $ VarInt $ fromIntegral $ (length txs) + 1+ put cb+ forM_ txs put+
+ Network/Haskoin/Protocol/BlockHeader.hs view
@@ -0,0 +1,63 @@+module Network.Haskoin.Protocol.BlockHeader +( BlockHeader(..) +, blockid+) where++import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Binary.Put (putWord32le)++import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Util ++-- | Data type recording information on a 'Block'. The hash of a block is+-- defined as the hash of this data structure. The block mining process+-- involves finding a partial hash collision by varying the nonce in the+-- 'BlockHeader' and/or additional randomness in the 'CoinbaseTx' of this+-- 'Block'. Variations in the 'CoinbaseTx' will result in different merkle +-- roots in the 'BlockHeader'.+data BlockHeader = + BlockHeader {+ -- | Block version information, based on the version of the+ -- software creating this block.+ blockVersion :: !Word32+ -- | Hash of the previous block (parent) referenced by this+ -- block.+ , prevBlock :: !Hash256+ -- | Root of the merkle tree of all transactions pertaining+ -- to this block.+ , merkleRoot :: !Hash256+ -- | Unix timestamp recording when this block was created+ , blockTimestamp :: !Word32+ -- | The difficulty target being used for this block+ , blockBits :: !Word32+ -- | A random nonce used to generate this block. Additional+ -- randomness is included in the coinbase transaction of+ -- this block.+ , bhNonce :: !Word32+ } deriving (Eq, Show, Read)++instance Binary BlockHeader where++ get = BlockHeader <$> getWord32le+ <*> get+ <*> get+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le++ put (BlockHeader v p m bt bb n) = do+ putWord32le v+ put p+ put m+ putWord32le bt+ putWord32le bb+ putWord32le n ++-- | Compute the hash of a block header+blockid :: BlockHeader -> Hash256+blockid = doubleHash256 . encode'+
+ Network/Haskoin/Protocol/BloomFilter.hs view
@@ -0,0 +1,101 @@+module Network.Haskoin.Protocol.BloomFilter +( BloomFlags(..)+, BloomFilter(..)+, FilterLoad(..)+, FilterAdd(..)+) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word8, Word32)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord8, getWord32le, getByteString)+import Data.Binary.Put (putWord8, putWord32le, putByteString)+import qualified Data.Foldable as F (toList)+import qualified Data.Sequence as S (Seq, fromList, length)+import qualified Data.ByteString as BS (ByteString, length)++import Network.Haskoin.Protocol.VarInt++-- | 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 + -- ^ Only auto-update on outputs that are pay-to-pubkey or pay-to-multisig.+ -- This is the default setting.+ deriving (Eq, Show, Read)++instance Binary 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 are probabilistic and have a false positive rate. 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+ , bloomFull :: Bool + -- ^ Flag indicating if the filter is full ('bloomData' is all 0x00)+ , bloomEmpty :: Bool+ -- ^ Flag indicating if the filter is empty ('bloomData' is all 0xff)+ , 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)++instance Binary BloomFilter where++ get = BloomFilter <$> (S.fromList <$> (readDat =<< get))+ <*> (return False) <*> (return False)+ <*> getWord32le <*> getWord32le+ <*> get+ where+ readDat (VarInt len) = replicateM (fromIntegral len) getWord8 ++ put (BloomFilter dat _ _ hashFuncs tweak flags) = do+ put $ VarInt $ fromIntegral $ 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 { getBloomFilter :: BloomFilter }+ deriving (Eq, Show, Read)++instance Binary 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 :: BS.ByteString }+ deriving (Eq, Show, Read)++instance Binary FilterAdd where+ get = do+ (VarInt len) <- get+ dat <- getByteString $ fromIntegral len+ return $ FilterAdd dat++ put (FilterAdd bs) = do+ put $ VarInt $ fromIntegral $ BS.length bs+ putByteString bs++
+ Network/Haskoin/Protocol/GetBlocks.hs view
@@ -0,0 +1,53 @@+module Network.Haskoin.Protocol.GetBlocks +( GetBlocks(..) +, BlockLocator+) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Binary.Put (putWord32le)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Crypto.Hash ++type BlockLocator = [Hash256]++-- | 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 'BlockLocator' is a sparse list of block hashes+-- from the caller node with the purpose of informing the receiving node+-- about the state of the caller's blockchain. The receiver node will detect+-- a wrong branch in the caller's main chain and send the caller appropriate +-- 'Blocks'. The response to a 'GetBlocks' message is an 'Inv' message+-- containing the list of block hashes pertaining to the request. +data GetBlocks = + GetBlocks {+ -- | The protocol version+ getBlocksVersion :: !Word32+ -- | Block locator object. It is a list of block hashes from the+ -- most recent block back to the genesis block. The list is+ -- dense at first and sparse towards the end.+ , getBlocksLocator :: !BlockLocator+ -- | Hash of the last desired block. If set to zero, the+ -- maximum number of block hashes is returned (500).+ , getBlocksHashStop :: !Hash256+ } deriving (Eq, Show, Read)++instance Binary GetBlocks where++ get = GetBlocks <$> getWord32le+ <*> (repList =<< get)+ <*> get+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetBlocks v xs h) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+ put h+
+ Network/Haskoin/Protocol/GetData.hs view
@@ -0,0 +1,34 @@+module Network.Haskoin.Protocol.GetData ( GetData(..) ) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)++import Network.Haskoin.Protocol.InvVector+import Network.Haskoin.Protocol.VarInt++-- | 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 for which a+-- node wants to request information. The response to a 'GetBlock' message+-- wille 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 to obtain information on unknown object+-- hashes. +data GetData = + GetData {+ -- | List of object hashes + getDataList :: ![InvVector] + } deriving (Eq, Show, Read)++instance Binary GetData where++ get = GetData <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetData xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+
+ Network/Haskoin/Protocol/GetHeaders.hs view
@@ -0,0 +1,46 @@+module Network.Haskoin.Protocol.GetHeaders ( GetHeaders(..) ) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Binary.Put (putWord32le)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.GetBlocks +import Network.Haskoin.Crypto.Hash ++-- | 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 pertaining to the request. A maximum of+-- 2000 block headers can be returned. 'GetHeaders' is used by thin (SPV)+-- clients to exclude block contents when synchronizing the blockchain.+data GetHeaders = + GetHeaders {+ -- | The protocol version+ getHeadersVersion :: !Word32+ -- | Block locator object. It is a list of block hashes from+ -- the most recent block back to the Genesis block. The list+ -- is dense at first and sparse towards the end.+ , getHeadersBL :: !BlockLocator+ -- | Hash of the last desired block header. When set to zero,+ -- the maximum number of block headers is returned (2000)+ , getHeadersHashStop :: !Hash256+ } deriving (Eq, Show, Read)++instance Binary GetHeaders where++ get = GetHeaders <$> getWord32le+ <*> (repList =<< get)+ <*> get+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (GetHeaders v xs h) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+ put h+
+ Network/Haskoin/Protocol/Headers.hs view
@@ -0,0 +1,36 @@+module Network.Haskoin.Protocol.Headers +( Headers(..)+, BlockHeaderCount+) where++import Control.Monad (liftM2, replicateM, forM_)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.BlockHeader++-- | '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.+data Headers = + Headers { + -- | List of block headers with respective transaction counts+ headersList :: ![BlockHeaderCount] + } + deriving (Eq, Show, Read)++instance Binary Headers where++ get = Headers <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) action+ action = liftM2 (,) get get++ put (Headers xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs $ \(a,b) -> put a >> put b+
+ Network/Haskoin/Protocol/Inv.hs view
@@ -0,0 +1,29 @@+module Network.Haskoin.Protocol.Inv ( Inv(..) ) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)++import Network.Haskoin.Protocol.InvVector+import Network.Haskoin.Protocol.VarInt++-- | 'Inv' messages are used by nodes to advertise their knowledge of new+-- objects by publishing a list of hashes. 'Inv' messages can be sent+-- unsolicited or in response to a 'GetBlocks' message.+data Inv = + Inv { + -- | Inventory vectors+ invList :: ![InvVector] + } deriving (Eq, Show, Read)++instance Binary Inv where++ get = Inv <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (Inv xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+
+ Network/Haskoin/Protocol/InvVector.hs view
@@ -0,0 +1,54 @@+module Network.Haskoin.Protocol.InvVector +( InvVector(..) +, InvType(..)+) where++import Control.Applicative ((<$>),(<*>))++import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord32le)+import Data.Binary.Put (putWord32le)++import Network.Haskoin.Crypto.Hash ++-- | Data type identifying the type of an inventory vector. +data InvType + = InvError -- ^ Error. Data containing this type can be ignored.+ | InvTx -- ^ InvVector hash is related to a transaction + | InvBlock -- ^ InvVector hash is related to a block+ | InvMerkleBlock -- ^ InvVector has is related to a merkle block+ deriving (Eq, Show, Read)++instance Binary InvType where++ get = go =<< getWord32le+ where + go x = case x of+ 0 -> return InvError+ 1 -> return InvTx+ 2 -> return InvBlock+ 3 -> return InvMerkleBlock+ _ -> fail "bitcoinGet InvType: Invalid Type"++ put x = putWord32le $ case x of+ InvError -> 0+ InvTx -> 1+ InvBlock -> 2+ InvMerkleBlock -> 3++-- | Invectory vectors represent hashes identifying objects such as a 'Block'+-- or a 'Tx'. They are sent inside messages to notify other peers about +-- new data or data they have requested.+data InvVector = + InvVector {+ -- | Type of the object referenced by this inventory vector+ invType :: !InvType+ -- | Hash of the object referenced by this inventory vector+ , invHash :: !Hash256+ } deriving (Eq, Show, Read)++instance Binary InvVector where+ get = InvVector <$> get <*> get+ put (InvVector t h) = put t >> put h++
+ Network/Haskoin/Protocol/MerkleBlock.hs view
@@ -0,0 +1,67 @@+module Network.Haskoin.Protocol.MerkleBlock (MerkleBlock(..)) where++import Control.Monad (replicateM, forM_)++import Data.Word (Word8, Word32)+import Data.Bits (testBit, setBit)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord8, getWord32le)+import Data.Binary.Put (putWord8, putWord32le)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.BlockHeader+import Network.Haskoin.Crypto.Hash ++data MerkleBlock = + MerkleBlock {+ -- | Header information for this merkle block.+ merkleHeader :: !BlockHeader+ -- | Number of transactions in the block (including+ -- unmatched transactions).+ , merkleTotalTxns :: !Word32+ -- | Hashes in depth-first order. They are used to rebuild a+ -- partial merkle tree.+ , mHashes :: [Hash256]+ -- | Flag bits, packed per 8 in a byte. Least significant bit+ -- first. Flag bits are used to rebuild a partial merkle+ -- tree.+ , mFlags :: [Bool]+ } deriving (Eq, Show, Read)++instance Binary 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+ put $ VarInt $ fromIntegral $ length hashes+ forM_ hashes put+ let ws = encodeMerkleFlags flags+ put $ VarInt $ fromIntegral $ length ws+ forM_ ws putWord8++decodeMerkleFlags :: [Word8] -> [Bool]+decodeMerkleFlags ws = + [ b | p <- [0..(length ws)*8-1]+ , b <- [testBit (ws !! (p `div` 8)) (p `mod` 8)]+ ]++encodeMerkleFlags :: [Bool] -> [Word8]+encodeMerkleFlags bs = map boolsToWord8 $ splitIn 8 bs+ +splitIn :: Int -> [a] -> [[a]]+splitIn _ [] = []+splitIn c xs = take c xs : (splitIn c $ drop c xs)+ +boolsToWord8 :: [Bool] -> Word8+boolsToWord8 [] = 0+boolsToWord8 xs = foldl setBit 0 (map snd $ filter fst $ zip xs [0..7])+
+ Network/Haskoin/Protocol/Message.hs view
@@ -0,0 +1,120 @@+module Network.Haskoin.Protocol.Message ( Message(..) ) where++import Control.Monad (unless)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( lookAhead+ , getByteString+ )+import Data.Binary.Put (putByteString)+import qualified Data.ByteString as BS + ( length + , append+ , empty+ )++import Network.Haskoin.Protocol.MessageHeader+import Network.Haskoin.Protocol.Version+import Network.Haskoin.Protocol.Addr+import Network.Haskoin.Protocol.Inv+import Network.Haskoin.Protocol.GetData+import Network.Haskoin.Protocol.NotFound+import Network.Haskoin.Protocol.GetBlocks+import Network.Haskoin.Protocol.GetHeaders+import Network.Haskoin.Protocol.Tx+import Network.Haskoin.Protocol.Block+import Network.Haskoin.Protocol.Headers+import Network.Haskoin.Protocol.BloomFilter+import Network.Haskoin.Protocol.Ping+import Network.Haskoin.Protocol.Alert++import Network.Haskoin.Util +import Network.Haskoin.Crypto.Hash ++-- | 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 + | MHeaders Headers + | MGetAddr + | MFilterLoad FilterLoad+ | MFilterAdd FilterAdd+ | MFilterClear+ | MPing Ping + | MPong Pong + | MAlert Alert+ deriving (Eq, Show)++instance Binary Message where++ get = do+ (MessageHeader mgc cmd len chk) <- get+ bs <- lookAhead $ getByteString $ fromIntegral len+ unless (mgc == networkMagic)+ (fail $ "get: Invalid network magic bytes: " ++ (show mgc))+ unless (chksum32 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+ MCHeaders -> MHeaders <$> get+ MCFilterLoad -> MFilterLoad <$> get+ MCFilterAdd -> MFilterAdd <$> get+ MCPing -> MPing <$> get+ MCPong -> MPong <$> get+ MCAlert -> MAlert <$> get+ _ -> fail $ "get: Invalid command " ++ (show cmd)+ else case cmd of+ MCGetAddr -> return MGetAddr + MCVerAck -> return MVerAck+ MCFilterClear -> return MFilterClear+ _ -> fail $ "get: Invalid command " ++ (show cmd)++ put 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)+ (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)+ chk = chksum32 payload+ len = fromIntegral $ BS.length payload+ header = MessageHeader networkMagic cmd len chk+ putByteString $ (encode' header) `BS.append` payload+
+ Network/Haskoin/Protocol/MessageHeader.hs view
@@ -0,0 +1,134 @@+module Network.Haskoin.Protocol.MessageHeader + ( MessageHeader(..) + , MessageCommand(..)+ ) where++import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32)+import qualified Data.ByteString as BS + ( ByteString+ , takeWhile+ )+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( getWord32le+ , getWord32be+ , getByteString+ )+import Data.Binary.Put + ( putWord32le+ , putWord32be+ , putByteString+ )++import Network.Haskoin.Util (stringToBS, bsToString)+import Network.Haskoin.Crypto.Hash (CheckSum32)++-- | 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 + | MCHeaders + | MCGetAddr + | MCFilterLoad+ | MCFilterAdd+ | MCFilterClear+ | MCPing + | MCPong + | MCAlert+ deriving (Eq, Show, Read)++instance Binary MessageCommand where+ + get = go =<< getByteString 12+ where + go bs = case unpackCommand bs of+ "version" -> return MCVersion+ "verack" -> return MCVerAck+ "addr" -> return MCAddr+ "inv" -> return MCInv+ "getdata" -> return MCGetData+ "notfound" -> return MCNotFound+ "getblocks" -> return MCGetBlocks+ "getheaders" -> return MCGetHeaders+ "tx" -> return MCTx+ "block" -> return MCBlock+ "headers" -> return MCHeaders+ "getaddr" -> return MCGetAddr+ "filterload" -> return MCFilterLoad+ "filteradd" -> return MCFilterAdd+ "filterclear" -> return MCFilterClear+ "ping" -> return MCPing+ "pong" -> return MCPong+ "alert" -> return MCAlert+ _ -> fail "get MessageCommand : Invalid command"++ put mc = putByteString $ packCommand $ case mc of+ MCVersion -> "version"+ MCVerAck -> "verack"+ MCAddr -> "addr"+ MCInv -> "inv"+ MCGetData -> "getdata"+ MCNotFound -> "notfound"+ MCGetBlocks -> "getblocks"+ MCGetHeaders -> "getheaders"+ MCTx -> "tx"+ MCBlock -> "block"+ MCHeaders -> "headers"+ MCGetAddr -> "getaddr"+ MCFilterLoad -> "filterload"+ MCFilterAdd -> "filteradd"+ MCFilterClear -> "filterclear"+ MCPing -> "ping"+ MCPong -> "pong"+ MCAlert -> "alert"++packCommand :: String -> BS.ByteString+packCommand s = stringToBS $ take 12 $ s ++ repeat '\NUL'++unpackCommand :: BS.ByteString -> String+unpackCommand bs = bsToString $ BS.takeWhile (/= 0) bs++-- | Data type representing the header of a 'Message'. All messages sent between+-- nodes contain a message header.+data MessageHeader = + MessageHeader {+ -- | Network magic bytes. It is used to differentiate + -- messages meant for different bitcoin networks, such as+ -- prodnet and testnet.+ headMagic :: !Word32+ -- | Message command identifying the type of message.+ -- included in the payload.+ , headCmd :: !MessageCommand+ -- | Byte length of the payload.+ , headPayloadSize :: !Word32+ -- | Checksum of the payload. + , headChecksum :: !CheckSum32+ } deriving (Eq, Show, Read)++instance Binary MessageHeader where++ get = MessageHeader <$> getWord32be+ <*> get+ <*> getWord32le+ <*> get++ put (MessageHeader m c l chk) = do+ putWord32be m+ put c+ putWord32le l+ put chk+
+ Network/Haskoin/Protocol/NetworkAddress.hs view
@@ -0,0 +1,45 @@+module Network.Haskoin.Protocol.NetworkAddress ( NetworkAddress(..) ) where++import Control.Monad (liftM2)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word16, Word64)+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( getWord16be+ , getWord64le+ , getWord64be+ )+import Data.Binary.Put+ ( putWord16be+ , putWord64le+ , putWord64be+ )++-- | Data type describing a bitcoin network address. Addresses are stored in+-- IPv6. IPv4 addresses are mapped to IPv6 using IPv4 mapped IPv6 addresses:+-- <http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_IPv6_addresses>. Sometimes,+-- timestamps are sent together with the 'NetworkAddress' such as in the 'Addr'+-- data type.+data NetworkAddress = + NetworkAddress {+ -- | Bitmask of services available for this address+ naServices :: !Word64+ -- | IPv6 address serialized as big endian+ , naAddress :: !(Word64, Word64)+ -- | Port number serialized as big endian+ , naPort :: !Word16+ } deriving (Eq, Show, Read)++instance Binary NetworkAddress where++ get = NetworkAddress <$> getWord64le+ <*> (liftM2 (,) getWord64be getWord64be)+ <*> getWord16be++ put (NetworkAddress s (al,ar) p) = do+ putWord64le s+ putWord64be al+ putWord64be ar+ putWord16be p+
+ Network/Haskoin/Protocol/NotFound.hs view
@@ -0,0 +1,30 @@+module Network.Haskoin.Protocol.NotFound ( NotFound(..) ) where++import Control.Monad (replicateM, forM_)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.InvVector++-- | 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.+data NotFound = + NotFound {+ -- | Inventory vectors related to this request+ notFoundList :: ![InvVector] + } deriving (Eq, Show, Read)++instance Binary NotFound where++ get = NotFound <$> (repList =<< get)+ where + repList (VarInt c) = replicateM (fromIntegral c) get++ put (NotFound xs) = do+ put $ VarInt $ fromIntegral $ length xs+ forM_ xs put+
+ Network/Haskoin/Protocol/Ping.hs view
@@ -0,0 +1,37 @@+module Network.Haskoin.Protocol.Ping+( Ping(..)+, Pong(..)+) where++import Control.Applicative ((<$>))++import Data.Word (Word64)+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getWord64le)+import Data.Binary.Put (putWord64le)++-- | A Ping message is sent to bitcoin peers to check if a TCP\/IP connection+-- is still valid.+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)++-- | A Pong message is sent as a response to a ping message.+newtype Pong = + Pong { + -- | When responding to a Ping request, the nonce from the Ping+ -- is copied in the Pong response.+ pongNonce :: Word64 + } deriving (Eq, Show, Read)++instance Binary Ping where+ get = Ping <$> getWord64le+ put (Ping n) = putWord64le n++instance Binary Pong where+ get = Pong <$> getWord64le+ put (Pong n) = putWord64le n+
+ Network/Haskoin/Protocol/Script.hs view
@@ -0,0 +1,252 @@+module Network.Haskoin.Protocol.Script +( ScriptOp(..)+, Script(..)+, PushDataType(..)+, opPushData+, getScriptOps+, putScriptOps+, decodeScriptOps+, encodeScriptOps+) where++import Control.Monad (liftM2, unless, when)+import Control.Applicative ((<$>))++import Data.Word (Word8)+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( Get+ , isEmpty+ , getWord8+ , getWord16le+ , getWord32le+ , getByteString+ )+import Data.Binary.Put + ( Put + , putWord8+ , putWord16le+ , putWord32le+ , putByteString+ )+import qualified Data.ByteString as BS+ ( ByteString+ , length+ )++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Crypto.Keys +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 the spending signatures in the input of a transaction+data Script = + Script { + -- | List of script operators defining this script+ scriptOps :: [ScriptOp] + }+ deriving (Eq, Show, Read)++instance Binary Script where+ get = do+ (VarInt len) <- get+ isolate (fromIntegral len) $ Script <$> getScriptOps++ put (Script ops) = do+ let bs = runPut' $ putScriptOps ops+ put $ VarInt $ fromIntegral $ BS.length bs+ putByteString bs++-- | Deserialize a list of 'ScriptOp' inside the 'Data.Binary.Get' monad.+-- This deserialization does not take into account the length of the script.+getScriptOps :: Get [ScriptOp]+getScriptOps = do+ empty <- isEmpty+ if empty + then return [] + else liftM2 (:) get getScriptOps++-- | Serialize a list of 'ScriptOp' inside the 'Data.Binary.Put' monad.+-- This serialization does not take into account the length of the script.+putScriptOps :: [ScriptOp] -> Put+putScriptOps (x:xs) = put x >> putScriptOps xs+putScriptOps _ = return ()++-- | Decode a 'Script' from a ByteString by omiting the length of the script.+-- This is used to produce scripthash addresses.+decodeScriptOps :: BS.ByteString -> Either String Script+decodeScriptOps bs = fromRunGet getScriptOps bs msg (return . Script)+ where + msg = Left "decodeScriptOps: Could not decode scriptops"++-- | Encode a 'Script' into a ByteString by omiting the length of the script.+-- This is used to produce scripthash addresses.+encodeScriptOps :: Script -> BS.ByteString+encodeScriptOps = runPut' . putScriptOps . scriptOps++-- | Data type representing the type of an OP_PUSHDATA opcode.+data PushDataType+ = + -- | The next opcode bytes is data to be pushed onto the stack+ OPCODE + -- | The next byte contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA1 + -- | The next two bytes contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA2+ -- | The next four bytes contains the number of bytes to be pushed onto+ -- the stack+ | OPDATA4+ deriving (Show, Read, Eq)++-- | Data type representing all of the operators allowed inside a 'Script'.+data ScriptOp + -- Pushing Data+ = OP_PUSHDATA BS.ByteString PushDataType + | OP_0 + | OP_1NEGATE + | 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_VERIFY ++ -- Stack operations+ | OP_DUP ++ -- Bitwise logic+ | OP_EQUAL + | OP_EQUALVERIFY ++ -- Crypto+ | OP_HASH160 + | OP_CHECKSIG + | OP_CHECKMULTISIG ++ -- Other+ | OP_PUBKEY PubKey + | OP_INVALIDOPCODE Word8+ deriving (Show, Read, Eq)++instance Binary 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 == 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+ | op == 0x69 = return $ OP_VERIFY+ | op == 0x76 = return $ OP_DUP+ | op == 0x87 = return $ OP_EQUAL+ | op == 0x88 = return $ OP_EQUALVERIFY+ | op == 0xa9 = return $ OP_HASH160+ | op == 0xac = return $ OP_CHECKSIG+ | op == 0xae = return $ OP_CHECKMULTISIG+ | op == 0xfe = OP_PUBKEY <$> get+ | otherwise = return $ OP_INVALIDOPCODE op++ put op = case op of++ (OP_PUSHDATA payload optype)-> do+ let len = BS.length payload+ when (len == 0) $ fail "OP_PUSHDATA: Payload size must be > 0"+ case optype of+ OPCODE -> do+ unless (len <= 0x4b) $ fail + "OP_PUSHDATA OPCODE: Payload size too big"+ putWord8 $ fromIntegral len+ OPDATA1 -> do+ unless (len <= 0xff) $ fail + "OP_PUSHDATA OPDATA1: Payload size too big"+ putWord8 0x4c+ putWord8 $ fromIntegral len+ OPDATA2 -> do+ unless (len <= 0xffff) $ fail + "OP_PUSHDATA OPDATA2: Payload size too big"+ putWord8 0x4d+ putWord16le $ fromIntegral len+ OPDATA4 -> do+ unless (len <= 0xffffffff) $ fail + "OP_PUSHDATA OPDATA4: Payload size too big"+ putWord8 0x4e+ putWord32le $ fromIntegral len+ putByteString payload++ OP_0 -> putWord8 0x00+ OP_1NEGATE -> putWord8 0x4f+ 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+ OP_VERIFY -> putWord8 0x69+ OP_DUP -> putWord8 0x76+ OP_EQUAL -> putWord8 0x87+ OP_EQUALVERIFY -> putWord8 0x88+ OP_HASH160 -> putWord8 0xa9+ OP_CHECKSIG -> putWord8 0xac+ OP_CHECKMULTISIG -> putWord8 0xae+ (OP_PUBKEY pk) -> putWord8 0xfe >> put pk+ (OP_INVALIDOPCODE _) -> putWord8 0xff++-- | Optimally encode data using one of the 4 types of data pushing opcodes+opPushData :: BS.ByteString -> ScriptOp+opPushData bs+ | len <= 0 = error "opPushData: data length must be > 0"+ | 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 = BS.length bs+
+ Network/Haskoin/Protocol/Tx.hs view
@@ -0,0 +1,194 @@+module Network.Haskoin.Protocol.Tx +( Tx(..) +, TxIn(..)+, TxOut(..)+, OutPoint(..)+, CoinbaseTx(..)+, txid+, cbid+, encodeTxid+, decodeTxid+) where++import Control.Monad (replicateM, forM_, liftM2, unless)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32, Word64)+import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( getWord32le+ , getWord64le+ , getByteString+ )+import Data.Binary.Put + ( putWord32le+ , putWord64le+ , putByteString+ )+import qualified Data.ByteString as BS + ( ByteString+ , length+ , reverse+ )++import Network.Haskoin.Protocol.VarInt+import Network.Haskoin.Protocol.Script+import Network.Haskoin.Crypto.Hash +import Network.Haskoin.Util ++-- | Data type representing a bitcoin transaction+data Tx = + Tx { + -- | Transaction data format version+ txVersion :: !Word32+ -- | List of transaction inputs+ , txIn :: ![TxIn]+ -- | List of transaction outputs+ , txOut :: ![TxOut]+ -- | The block number of timestamp at which this transaction is locked+ , txLockTime :: !Word32+ } deriving (Eq, Show, Read)++instance Binary Tx where++ get = Tx <$> getWord32le+ <*> (replicateList =<< get)+ <*> (replicateList =<< get)+ <*> getWord32le+ where + replicateList (VarInt c) = replicateM (fromIntegral c) get++ put (Tx v is os l) = do+ putWord32le v+ put $ VarInt $ fromIntegral $ length is+ forM_ is put+ put $ VarInt $ fromIntegral $ length os+ forM_ os put+ putWord32le l++-- | Data type representing the coinbase transaction of a 'Block'. Coinbase+-- transactions are special types of transactions which are created by miners+-- when they find a new block. Coinbase transactions have no inputs. They have+-- outputs sending the newly generated bitcoins together with all the block's+-- fees to a bitcoin address (usually the miners address). Data can be embedded+-- in a Coinbase transaction which can be chosen by the miner of a block. This+-- data also typically contains some randomness which is used, together with+-- the nonce, to find a partial hash collision on the block's hash.+data CoinbaseTx = + CoinbaseTx { + -- | Transaction data format version.+ cbVersion :: !Word32+ -- | Previous outpoint. This is ignored for+ -- coinbase transactions but preserved for computing+ -- the correct txid.+ , cbPrevOutput :: !OutPoint+ -- | Data embedded inside the coinbase transaction.+ , cbData :: !BS.ByteString+ -- | Transaction sequence number. This is ignored for+ -- coinbase transactions but preserved for computing+ -- the correct txid.+ , cbInSequence :: !Word32+ -- | List of transaction outputs.+ , cbOut :: ![TxOut]+ -- | The block number of timestamp at which this + -- transaction is locked.+ , cbLockTime :: !Word32+ } deriving (Eq, Show, Read)++instance Binary CoinbaseTx where++ get = do+ v <- getWord32le+ (VarInt len) <- get+ unless (len == 1) $ fail "CoinbaseTx get: Input size is not 1"+ op <- get+ (VarInt cbLen) <- get+ cb <- getByteString (fromIntegral cbLen)+ sq <- getWord32le+ (VarInt oLen) <- get+ os <- replicateM (fromIntegral oLen) get+ lt <- getWord32le+ return $ CoinbaseTx v op cb sq os lt++ put (CoinbaseTx v op cb sq os lt) = do+ putWord32le v+ put $ VarInt 1+ put op+ put $ VarInt $ fromIntegral $ BS.length cb+ putByteString cb + putWord32le sq+ put $ VarInt $ fromIntegral $ length os+ forM_ os put+ putWord32le lt++-- | Data type representing a transaction input.+data TxIn = + TxIn { + -- | Reference the previous transaction output (hash + position)+ prevOutput :: !OutPoint+ -- | Script providing the requirements of the previous transaction+ -- output to spend those coins.+ , scriptInput :: !Script+ -- | Transaction version as defined by the sender of the+ -- transaction. The intended use is for replacing transactions with+ -- new information before the transaction is included in a block.+ , txInSequence :: !Word32+ } deriving (Eq, Show, Read)++instance Binary TxIn where+ get = TxIn <$> get <*> get <*> getWord32le+ put (TxIn o s q) = put o >> put s >> putWord32le q++-- | Data type representing a transaction output.+data TxOut = + TxOut { + -- | Transaction output value.+ outValue :: !Word64+ -- | Script specifying the conditions to spend this output.+ , scriptOutput :: !Script+ } deriving (Eq, Show, Read)++instance Binary TxOut where+ get = do+ val <- getWord64le+ unless (val <= 2100000000000000) $ fail $+ "Invalid TxOut value: " ++ (show val)+ TxOut val <$> get+ put (TxOut o s) = putWord64le o >> put s++-- | The OutPoint is used inside a transaction input to reference the previous+-- transaction output that it is spending.+data OutPoint = + OutPoint { + -- | The hash of the referenced transaction.+ outPointHash :: !Hash256+ -- | The position of the specific output in the transaction.+ -- The first output position is 0.+ , outPointIndex :: !Word32+ } deriving (Read, Show, Eq)++instance Binary OutPoint where+ get = do+ (h,i) <- liftM2 (,) get getWord32le+ return $ OutPoint h i+ put (OutPoint h i) = put h >> putWord32le i++-- | Computes the hash of a transaction.+txid :: Tx -> Hash256+txid = doubleHash256 . encode' ++-- | Computes the hash of a coinbase transaction.+cbid :: CoinbaseTx -> Hash256+cbid = doubleHash256 . encode' ++-- | Encodes a transaction hash as little endian in HEX format.+-- This is mostly used for displaying transaction ids. Internally, these ids+-- are handled as big endian but are transformed to little endian when+-- displaying them.+encodeTxid :: Hash256 -> String+encodeTxid = bsToHex . BS.reverse . encode' ++-- | Decodes a little endian transaction hash in HEX format. +decodeTxid :: String -> Maybe Hash256+decodeTxid = (decodeToMaybe . BS.reverse =<<) . hexToBS+
+ Network/Haskoin/Protocol/VarInt.hs view
@@ -0,0 +1,46 @@+module Network.Haskoin.Protocol.VarInt ( VarInt(..) ) where+ +import Data.Word (Word64)+import Control.Applicative ((<$>))++import Data.Binary (Binary, get, put)+import Data.Binary.Get + ( getWord8+ , getWord16le+ , getWord32le+ , getWord64le+ )+import Data.Binary.Put + ( putWord8+ , putWord16le+ , putWord32le+ , putWord64le+ )++-- | 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)+ +instance Binary 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+
+ Network/Haskoin/Protocol/VarString.hs view
@@ -0,0 +1,29 @@+module Network.Haskoin.Protocol.VarString ( VarString(..) ) where++import Control.Applicative ((<$>))++import qualified Data.ByteString as BS + ( ByteString+ , length+ )+import Data.Binary (Binary, get, put)+import Data.Binary.Get (getByteString)+import Data.Binary.Put (putByteString)++import Network.Haskoin.Protocol.VarInt++-- | Data type for variable length strings. Variable length strings are+-- serialized as a 'VarInt' followed by a bytestring.+newtype VarString = VarString { getVarString :: BS.ByteString }+ deriving (Eq, Show, Read)++instance Binary VarString where++ get = VarString <$> (readBS =<< get)+ where + readBS (VarInt len) = getByteString (fromIntegral len)++ put (VarString bs) = do+ put $ VarInt $ fromIntegral $ BS.length bs+ putByteString bs+
+ Network/Haskoin/Protocol/Version.hs view
@@ -0,0 +1,85 @@+module Network.Haskoin.Protocol.Version ( Version(..) ) where++import Control.Applicative ((<$>),(<*>))++import Data.Word (Word32, Word64)+import Data.Binary (Binary, get, put, Get, Put)+import Data.Binary.Get + ( getWord8+ , getWord32le+ , getWord64le+ , isEmpty+ )+import Data.Binary.Put + ( putWord8+ , putWord32le+ , putWord64le+ )++import Network.Haskoin.Protocol.VarString+import Network.Haskoin.Protocol.NetworkAddress++-- | 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 being used by the node.+ version :: !Word32+ -- | Bitmask of features to enable for this connection.+ , services :: !Word64+ -- | UNIX timestamp+ , timestamp :: !Word64+ -- | Network address of the node receiving this message.+ , addrRecv :: !NetworkAddress+ -- | Network address of the node sending this message.+ , addrSend :: !NetworkAddress+ -- | Randomly generated identifying sent with every version+ -- message. This nonce is used to detect connection to self.+ , verNonce :: !Word64+ -- | User agent+ , userAgent :: !VarString+ -- | The height of the last block received by the sending node.+ , startHeight :: !Word32+ -- | Wether the remote peer should announce relaying transactions+ -- or not. This feature is enabled since version >= 70001. See+ -- BIP37 for more details.+ , relay :: !Bool+ } deriving (Eq, Show, Read)++instance Binary Version where++ get = Version <$> getWord32le+ <*> getWord64le+ <*> getWord64le+ <*> get+ <*> get+ <*> getWord64le+ <*> 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++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+
+ Network/Haskoin/Script.hs view
@@ -0,0 +1,56 @@+{-|+ This package 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+(+ -- *Script Parsing+ -- **Script Outputs+ ScriptOutput(..)+, encodeOutput+, decodeOutput+, isPayPK+, isPayPKHash+, isPayMulSig+, isPayScriptHash+, scriptAddr+, sortMulSig+ -- **Script Inputs+, ScriptInput(..)+, encodeInput+, decodeInput+, isSpendPK+, isSpendPKHash+, isSpendMulSig+ -- **ScriptHash Inputs+, ScriptHashInput(..)+, RedeemScript+, encodeScriptHash+, decodeScriptHash+ -- * Helpers+, scriptRecipient+, scriptSender+, intToScriptOp+, scriptOpToInt++-- *SigHash+-- | For additional information on sighashes, see:+-- <http://en.bitcoin.it/wiki/OP_CHECKSIG>+, SigHash(..)+, txSigHash+, encodeSigHash32+, isSigAll+, isSigNone+, isSigSingle+, isSigUnknown+, TxSignature(..)+, encodeSig+, decodeSig+, decodeCanonicalSig++) where++import Network.Haskoin.Script.Parser+import Network.Haskoin.Script.SigHash+
+ Network/Haskoin/Script/Parser.hs view
@@ -0,0 +1,287 @@+module Network.Haskoin.Script.Parser+( ScriptOutput(..)+, ScriptInput(..)+, RedeemScript+, ScriptHashInput(..)+, scriptAddr+, scriptRecipient+, scriptSender+, encodeInput+, decodeInput+, encodeOutput+, decodeOutput+, encodeScriptHash+, decodeScriptHash+, sortMulSig+, intToScriptOp+, scriptOpToInt+, isPayPK+, isPayPKHash+, isPayMulSig+, isPayScriptHash+, isSpendPK+, isSpendPKHash+, isSpendMulSig+) where++import Control.Monad (liftM2)+import Control.Applicative ((<$>),(<*>))++import Data.List (sortBy)+import qualified Data.ByteString as BS (head, singleton)++import Network.Haskoin.Util+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Protocol.Script+import Network.Haskoin.Script.SigHash++-- | Data type describing standard transaction output scripts. Output scripts+-- provide the conditions that must be fulfilled for someone to spend the+-- output coins. +data ScriptOutput = + -- | Pay to a public key.+ PayPK { getOutputPubKey :: !PubKey }+ -- | Pay to a public key hash.+ | PayPKHash { getOutputAddress :: !Address }+ -- | Pay to multiple public keys.+ | PayMulSig { getOutputMulSigKeys :: ![PubKey]+ , getOutputMulSigRequired :: !Int+ }+ -- | Pay to a script hash.+ | PayScriptHash { getOutputAddress :: !Address }+ deriving (Eq, Show)++-- | Returns True if the script is a pay to public key output.+isPayPK :: ScriptOutput -> Bool+isPayPK (PayPK _) = True+isPayPK _ = False++-- | Returns True if the script is a pay to public key hash output.+isPayPKHash :: ScriptOutput -> Bool+isPayPKHash (PayPKHash _) = True+isPayPKHash _ = False++-- | Returns True if the script is a pay to multiple public keys output.+isPayMulSig :: ScriptOutput -> Bool+isPayMulSig (PayMulSig _ _) = True+isPayMulSig _ = False++-- | Returns true if the script is a pay to script hash output.+isPayScriptHash :: ScriptOutput -> Bool+isPayScriptHash (PayScriptHash _) = True+isPayScriptHash _ = False++-- | Computes a script address from a script output. This address can be used+-- in a pay to script hash output.+scriptAddr :: ScriptOutput -> Address+scriptAddr = ScriptAddress . hash160 . hash256BS . toBS+ where + toBS = encodeScriptOps . encodeOutput ++-- | Sorts the public keys of a multisignature output in ascending order by+-- comparing their serialized representations. This feature allows for easier+-- multisignature account management as participants in a multisignature wallet+-- will blindly agree on an ordering of the public keys without having to+-- communicate. +sortMulSig :: ScriptOutput -> ScriptOutput+sortMulSig out = case out of+ PayMulSig keys r -> PayMulSig (sortBy f keys) r+ _ -> error "Can only call orderMulSig on PayMulSig scripts"+ where + f a b = encode' a `compare` encode' b++-- | Computes a 'Script' from a 'ScriptOutput'. The 'Script' is a list of +-- 'ScriptOp' can can be used to build a 'Tx'.+encodeOutput :: ScriptOutput -> Script+encodeOutput s = Script $ case s of+ -- Pay to PubKey+ (PayPK k) -> [opPushData $ encode' k, OP_CHECKSIG]+ -- Pay to PubKey Hash Address+ (PayPKHash a) -> case a of+ (PubKeyAddress h) -> [ OP_DUP, OP_HASH160, opPushData $ encode' h+ , OP_EQUALVERIFY, OP_CHECKSIG + ] + (ScriptAddress _) -> + error "encodeOutput: ScriptAddress is invalid in PayPKHash"+ -- Pay to MultiSig Keys+ (PayMulSig ps r)+ | r <= length ps ->+ let opM = intToScriptOp r+ opN = intToScriptOp $ length ps+ keys = map (opPushData . encode') ps+ in opM : keys ++ [opN, OP_CHECKMULTISIG]+ | otherwise -> error "encodeOutput: PayMulSig r must be <= than pkeys"+ -- Pay to Script Hash Address+ (PayScriptHash a) -> case a of+ (ScriptAddress h) -> [ OP_HASH160+ , opPushData $ encode' h, OP_EQUAL+ ]+ (PubKeyAddress _) -> + error "encodeOutput: PubKeyAddress is invalid in PayScriptHash"++-- | 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 <$> decodeToEither bs+ -- Pay to PubKey Hash+ [OP_DUP, OP_HASH160, OP_PUSHDATA bs _, OP_EQUALVERIFY, OP_CHECKSIG] -> + (PayPKHash . PubKeyAddress) <$> decodeToEither bs+ -- Pay to Script Hash+ [OP_HASH160, OP_PUSHDATA bs _, OP_EQUAL] -> + (PayScriptHash . ScriptAddress) <$> decodeToEither bs+ -- Pay to MultiSig Keys+ _ -> matchPayMulSig s++-- 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 (:) (decodeToEither bs) (go xs)+ go [] = return []+ go _ = Left "matchPayMulSig: invalid multisig opcode"++-- | Transforms integers [1 .. 16] to 'ScriptOp' [OP_1 .. OP_16]+intToScriptOp :: Int -> ScriptOp+intToScriptOp i+ | i `elem` [1..16] = op+ | otherwise = error $ "intToScriptOp: Invalid integer " ++ (show i)+ where + op = decode' $ BS.singleton $ fromIntegral $ i + 0x50++-- | 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 $ BS.head $ encode' s) - 0x50++-- | Computes the recipient address of a script. This function fails if the+-- script could not be decoded as a pay to public key hash or pay to script+-- hash. +scriptRecipient :: Script -> Either String Address+scriptRecipient s = case decodeOutput s of+ Right (PayPKHash a) -> return a+ Right (PayScriptHash a) -> return a+ Right _ -> Left "scriptRecipient: bad output script type"+ _ -> Left "scriptRecipient: non-standard script type"++-- | Computes the sender address of a script. This function fails if the+-- script could not be decoded as a spend public key hash or script hash+-- input. +scriptSender :: Script -> Either String Address+scriptSender s = case decodeInput s of+ Right (SpendPKHash _ key) -> return $ pubKeyAddr key+ Right _ -> Left "scriptSender: bad input script type"+ _ -> case decodeScriptHash s of+ Right (ScriptHashInput _ rdm) -> return $ scriptAddr rdm+ _ -> Left "scriptSender: non-standard script type"++-- | 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. +data ScriptInput = + -- | Spend the coins of a PayPK output.+ SpendPK { getInputSig :: !TxSignature }+ -- | Spend the coins of a PayPKHash output.+ | SpendPKHash { getInputSig :: !TxSignature + , getInputKey :: !PubKey+ }+ -- | Spend the coins of a PayMulSig output.+ | SpendMulSig { getInputMulSigKeys :: ![TxSignature] + , getInputMulSigRequired :: !Int+ }+ deriving (Eq, Show)++-- | Returns True if the input script is spending a public key.+isSpendPK :: ScriptInput -> Bool+isSpendPK (SpendPK _) = True+isSpendPK _ = False++-- | Returns True if the input script is spending a public key hash.+isSpendPKHash :: ScriptInput -> Bool+isSpendPKHash (SpendPKHash _ _) = True+isSpendPKHash _ = False++-- | Returns True if the input script is spending a multisignature output.+isSpendMulSig :: ScriptInput -> Bool+isSpendMulSig (SpendMulSig _ _) = True+isSpendMulSig _ = False++-- | Computes a 'Script' from a 'ScriptInput'. The 'Script' is a list of +-- 'ScriptOp' can can be used to build a 'Tx'.+encodeInput :: ScriptInput -> Script+encodeInput s = Script $ case s of+ SpendPK ts -> [ opPushData $ encodeSig ts ]+ SpendPKHash ts p -> [ opPushData $ encodeSig ts+ , opPushData $ encode' p+ ]+ SpendMulSig ts r + | length ts <= 16 && r >= 1 && r <= 16 ->+ let sigs = map (opPushData . encodeSig) ts+ in OP_0 : sigs ++ replicate (r - length ts) OP_0+ | otherwise -> error "SpendMulSig: Bad multisig parameters"++-- | Decodes a 'ScriptInput' from a 'Script'. This function fails if the +-- script can not be parsed as a standard script input.+decodeInput :: Script -> Either String ScriptInput+decodeInput s = case scriptOps s of+ [OP_PUSHDATA bs _] -> SpendPK <$> decodeSig bs + [OP_PUSHDATA sig _, OP_PUSHDATA p _] -> + liftM2 SpendPKHash (decodeSig sig) (decodeToEither p)+ (OP_0 : xs) -> matchSpendMulSig $ Script xs+ _ -> Left "decodeInput: Script did not match input templates"++matchSpendMulSig :: Script -> Either String ScriptInput+matchSpendMulSig (Script ops) = + liftM2 SpendMulSig (go ops) (return $ length ops)+ where + go (OP_PUSHDATA bs _:xs) = liftM2 (:) (decodeSig bs) (go xs)+ go (OP_0:xs)+ | all (== OP_0) xs = return []+ | otherwise = Left "matchSpendMulSig: invalid opcode after OP_0"+ go [] = return []+ go _ = Left "matchSpendMulSig: invalid multisig opcode"++type RedeemScript = ScriptOutput++-- | Data type describing an input script spending a pay to script hash+-- output. To spend a script hash output, an input script must provide+-- both a redeem script and a regular input script spending the redeem +-- script.+data ScriptHashInput = ScriptHashInput + { -- | Input script spending the redeem script+ spendSHInput :: ScriptInput + -- | Redeem script+ , spendSHOutput :: RedeemScript+ } deriving (Eq, Show)++-- | Compute a 'Script' from a 'ScriptHashInput'. The 'Script' is a list of +-- 'ScriptOp' can can be used to build a 'Tx'.+encodeScriptHash :: ScriptHashInput -> Script+encodeScriptHash (ScriptHashInput i o) =+ Script $ (scriptOps si) ++ [opPushData $ encodeScriptOps so]+ where + si = encodeInput i+ so = encodeOutput o++-- | Tries to decode a 'ScriptHashInput' from a 'Script'. This function fails+-- if the script can not be parsed as a script hash input.+decodeScriptHash :: Script -> Either String ScriptHashInput+decodeScriptHash (Script ops) = case splitAt (length ops - 1) ops of+ (is,[OP_PUSHDATA bs _]) -> + ScriptHashInput <$> (decodeInput $ Script is) + <*> (decodeOutput =<< decodeScriptOps bs)+ _ -> Left "decodeScriptHash: Script did not match input template"+
+ Network/Haskoin/Script/SigHash.hs view
@@ -0,0 +1,199 @@+module Network.Haskoin.Script.SigHash+( SigHash(..)+, encodeSigHash32+, isSigAll+, isSigNone+, isSigSingle+, isSigUnknown+, txSigHash+, TxSignature(..)+, encodeSig+, decodeSig+, decodeCanonicalSig+) where++import Control.Monad (liftM2)++import Data.Word (Word8)+import Data.Bits (testBit, clearBit, setBit)+import Data.Maybe (fromMaybe)+import Data.Binary (Binary, get, put, getWord8, putWord8)+import qualified Data.ByteString as BS + ( ByteString+ , index+ , length+ , last+ , append+ , pack+ , splitAt+ )++import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Protocol.Script+import Network.Haskoin.Protocol.Tx+import Network.Haskoin.Util++-- | 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 anyoneCanPay flag is True, then only the current input is signed.+-- Otherwise, all of the inputs of a transaction are signed. The default value+-- for anyoneCanPay is False.+data SigHash + -- | Sign all of the outputs of a transaction (This is the default value).+ -- Changing any of the outputs of the transaction will invalidate the+ -- signature.+ = SigAll { anyoneCanPay :: Bool } + -- | Sign none of the outputs of a transaction. This allows anyone to+ -- change any of the outputs of the transaction.+ | SigNone { anyoneCanPay :: Bool } + -- | Sign only the output corresponding the the current transaction input.+ -- You care about your own output in the transaction but you don't+ -- care about any of the other outputs.+ | SigSingle { anyoneCanPay :: Bool } + -- | Unrecognized sighash types will decode to SigUnknown.+ | SigUnknown { anyoneCanPay :: Bool+ , getSigCode :: Word8 + }+ deriving (Eq, Show)++-- | Returns True if the 'SigHash' has the value SigAll.+isSigAll :: SigHash -> Bool+isSigAll sh = case sh of+ SigAll _ -> True+ _ -> False++-- | Returns True if the 'SigHash' has the value SigNone.+isSigNone :: SigHash -> Bool+isSigNone sh = case sh of+ SigNone _ -> True+ _ -> False++-- | Returns True if the 'SigHash' has the value SigSingle.+isSigSingle :: SigHash -> Bool+isSigSingle sh = case sh of+ SigSingle _ -> True+ _ -> False++-- | Returns True if the 'SigHash' has the value SigUnknown.+isSigUnknown :: SigHash -> Bool+isSigUnknown sh = case sh of+ SigUnknown _ _ -> True+ _ -> False++instance Binary SigHash where++ get = getWord8 >>= \w ->+ let acp = testBit w 7+ in return $ case clearBit w 7 of+ 1 -> SigAll acp+ 2 -> SigNone acp+ 3 -> SigSingle acp+ _ -> SigUnknown acp w++ put sh = putWord8 $ case sh of+ SigAll acp -> if acp then 0x81 else 0x01+ SigNone acp -> if acp then 0x82 else 0x02+ SigSingle acp -> if acp then 0x83 else 0x03+ SigUnknown _ w -> w++-- | Encodes a 'SigHash' to a 32 bit-long bytestring.+encodeSigHash32 :: SigHash -> BS.ByteString+encodeSigHash32 sh = encode' sh `BS.append` BS.pack [0,0,0]++-- | Computes the hash that will be used for signing a transaction.+txSigHash :: Tx -- ^ Transaction to sign.+ -> Script -- ^ Output script that is being spent.+ -> Int -- ^ Index of the input that is being signed.+ -> SigHash -- ^ What parts of the transaction should be signed.+ -> Hash256 -- ^ Result hash to be signed.+txSigHash tx out i sh = do+ let newIn = buildInputs (txIn tx) out i sh+ -- When SigSingle and input index > outputs, then sign integer 1+ fromMaybe (setBit 0 248) $ do+ newOut <- buildOutputs (txOut tx) i sh+ let newTx = tx{ txIn = newIn, txOut = newOut }+ return $ doubleHash256 $ encode' newTx `BS.append` encodeSigHash32 sh++-- Builds transaction inputs for computing SigHashes+buildInputs :: [TxIn] -> Script -> Int -> SigHash -> [TxIn]+buildInputs txins out i sh+ | anyoneCanPay sh = (txins !! i) { scriptInput = out } : []+ | isSigAll sh || isSigUnknown sh = single+ | otherwise = map noSeq $ zip single [0..]+ where + empty = map (\ti -> ti{ scriptInput = Script [] }) txins+ single = updateIndex i empty $ \ti -> ti{ scriptInput = 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+ | isSigAll sh || isSigUnknown sh = return txos+ | isSigNone sh = return []+ | i >= length txos = Nothing+ | otherwise = return $ buffer ++ [txos !! i]+ where + buffer = replicate i $ TxOut (-1) $ Script []++-- | Data type representing a 'Signature' together with a 'SigHash'. The+-- 'SigHash' is serialized as one byte at the end of a regular ECDSA+-- 'Signature'. All signatures in transaction inputs are of type 'TxSignature'.+data TxSignature = TxSignature + { txSignature :: Signature + , sigHashType :: SigHash+ } deriving (Eq, Show)++-- | Serialize a 'TxSignature' to a ByteString.+encodeSig :: TxSignature -> BS.ByteString+encodeSig (TxSignature sig sh) = runPut' $ put sig >> put sh++-- | Decode a 'TxSignature' from a ByteString.+decodeSig :: BS.ByteString -> Either String TxSignature+decodeSig bs = do+ let (h,l) = BS.splitAt (BS.length bs - 1) bs+ liftM2 TxSignature (decodeToEither h) (decodeToEither l)++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- | Decode a 'TxSignature' from a ByteString. This function will check if+-- the signature is canonical and fail if it is not.+decodeCanonicalSig :: BS.ByteString -> Either String TxSignature+decodeCanonicalSig bs+ | len < 9 = Left "Non-canonical signature: too short"+ | len > 73 = Left "Non-canonical signature: too long"+ | hashtype < 1 || hashtype > 3 = + Left" Non-canonical signature: unknown hashtype byte"+ | BS.index bs 0 /= 0x30 = Left "Non-canonical signature: wrong type"+ | BS.index bs 1 /= len - 3 = + Left "Non-canonical signature: wrong length marker"+ | 5 + rlen >= len = Left "Non-canonical signature: S length misplaced"+ | rlen + slen + 7 /= len = + Left "Non-canonical signature: R+S length mismatch"+ | BS.index bs 2 /= 0x02 = + Left "Non-canonical signature: R value type mismatch"+ | rlen == 0 = Left "Non-canonical signature: R length is zero"+ | testBit (BS.index bs 4) 7 = + Left "Non-canonical signature: R value negative"+ | rlen > 1 && BS.index bs 4 == 0 && not (testBit (BS.index bs 5) 7) =+ Left "Non-canonical signature: R value excessively padded"+ | BS.index bs (fromIntegral rlen+4) /= 0x02 =+ Left "Non-canonical signature: S value type mismatch"+ | slen == 0 = Left "Non-canonical signature: S length is zero"+ | testBit (BS.index bs (fromIntegral rlen+6)) 7 =+ Left "Non-canonical signature: S value negative"+ | slen > 1 && BS.index bs (fromIntegral rlen+6) == 0 + && not (testBit (BS.index bs (fromIntegral rlen+7)) 7) =+ Left "Non-canonical signature: S value excessively padded"+ | otherwise = decodeSig bs+ where + len = fromIntegral $ BS.length bs+ rlen = BS.index bs 3+ slen = BS.index bs (fromIntegral rlen + 5)+ hashtype = clearBit (BS.last bs) 7+
+ Network/Haskoin/Stratum.hs view
@@ -0,0 +1,274 @@+{-# LANGUAGE OverloadedStrings, FlexibleInstances #-}+module Network.Haskoin.Stratum+( -- * Types+ -- ** Bitcoin+ Balance(..)+, Coin(..)+, TxHeight(..)+ -- ** Stratum data+, StratumNotif(..)+, StratumQuery(..)+, StratumResponse(..)+ -- ** JSON-RPC data for Stratum+, MessageStratum+, NotifStratum+, RequestStratum+, ResponseStratum+, ResultStratum+ -- ** Stratum Session for JSON-RPC Conduit+, StratumSession+ -- * Functions+, toRequest+, parseResult+, parseNotif+, newStratumReq+ -- * Generic JSON-RPC Conduit+ -- ** Types+, Session+ -- ** Functions+, initSession+, newReq+, newNotif+, reqSource+, resConduit+ -- * Generic JSON-RPC Messages+ -- ** Types+, Method+, ErrorValue+, RequestValue+, ResponseValue+, MessageValue+, ResultValue+, Id(..)+, Result+, Error(..)+ -- ** Messages+, Request(..)+, Response(..)+, Message(..)+ -- ** Errors+, errParse+, errReq+, errMeth+, errParams+, errInternal+, errStr+ -- ** Helpers+, leftStr+, numericId+) where++import Control.Monad (mzero)+import Control.Monad.Trans (MonadIO)+import Data.Aeson+ ( FromJSON+ , ToJSON+ , Value (Array, Object, String)+ , (.:), (.=)+ , object+ , parseJSON+ , toJSON+ , withText+ )+import Data.Aeson.Types (Parser)+import Data.Maybe (fromJust)+import Data.Text (Text, pack, unpack)+import qualified Data.Vector as V+import Data.Word (Word, Word64)+import Network.Haskoin.Crypto+import Network.Haskoin.Protocol hiding (Message)+import Network.Haskoin.Stratum.Message+import Network.Haskoin.Stratum.Conduit+import Network.Haskoin.Util++-- | JSON-RPC request with Stratum payload.+type RequestStratum = Request StratumQuery+-- | JSON-RPC notification with Stratum payload.+type NotifStratum = Request StratumNotif+-- | JSON-RPC response with Stratum payload.+type ResponseStratum = Response StratumResponse Value String+-- | Stratum result in JSON-RPC response.+type ResultStratum = Result StratumResponse Value String+-- | Message from Stratum JSON-RPC server.+type MessageStratum = Message StratumNotif StratumResponse Value String+-- | Session type for JSON-RPC conduit.+type StratumSession+ = Session RequestStratum StratumResponse Value String StratumNotif++-- | Transaction height and ID pair. Used in history responses.+data TxHeight = TxHeight+ { txHeightBlock :: Word -- ^ Block height.+ , txHeightId :: Hash256 -- ^ Transaction id.+ } deriving (Show, Eq)++-- | Bitcoin outpoint information.+data Coin = Coin+ { coinOutPoint :: OutPoint -- ^ Coin data.+ , coinTxHeight :: TxHeight -- ^ Transaction information.+ , coinValue :: Word64 -- ^ Output vale.+ } deriving (Show, Eq)++-- | Balance information.+data Balance = Balance+ { balConfirmed :: Word64 -- ^ Confirmed balance.+ , balUnconfirmed :: Word64 -- ^ Unconfirmed balance.+ } deriving (Show, Eq)++-- | Stratum Request data. To be placed inside JSON request.+data StratumQuery+ = QueryVersion { queryClientVer :: Text, queryProtoVer :: Text }+ | QueryHistory { queryAddr :: Address }+ | QueryBalance { queryAddr :: Address }+ | QueryUnspent { queryAddr :: Address }+ | QueryTx { queryTxid :: Hash256 }+ | QueryBroadcast { queryTx :: Tx }+ | SubAddress { queryAddr :: Address }+ deriving (Eq, Show)++-- | Stratum Response Result data.+data StratumResponse+ = ServerVersion { stratumServerVer :: String }+ | AddressHistory { stratumAddrHist :: [TxHeight] }+ | AddressBalance { stratumBalance :: Balance }+ | AddressUnspent { stratumCoins :: [Coin] }+ | Transaction { stratumTx :: Tx }+ | BroadcastId { stratumTxid :: Hash256 }+ | AddrStatus { stratumAddrStatus :: Hash256 }+ deriving (Eq, Show)++data StratumNotif+ = NotifAddress { notifAddr :: Address, notifAddrStatus :: Hash256 }+ deriving (Eq, Show)++instance ToJSON StratumNotif where+ toJSON (NotifAddress a t) = toJSON (a, hashToJSON t)++instance ToJSON StratumQuery where+ toJSON (QueryVersion c p) = toJSON (c, p)+ toJSON (QueryHistory a) = toJSON [a]+ toJSON (QueryUnspent a) = toJSON [a]+ toJSON (QueryBalance a) = toJSON [a]+ toJSON (QueryTx i) = toJSON [txidToJSON i]+ toJSON (QueryBroadcast t) = txToJSON t+ toJSON (SubAddress a) = toJSON [a]++instance FromJSON Balance where+ parseJSON (Object o) = do+ c <- o .: "confirmed"+ u <- o .: "unconfirmed"+ return $ Balance c u+ parseJSON _ = mzero++instance FromJSON TxHeight where+ parseJSON (Object v) = do+ h <- v .: "height"+ t <- v .: "tx_hash"+ i <- txidParse t+ return $ TxHeight h i+ parseJSON _ = mzero++instance ToJSON TxHeight where+ toJSON x = object+ [ "height" .= txHeightBlock x+ , "tx_hash" .= txidToJSON (txHeightId x)+ ]++instance FromJSON Coin where+ parseJSON (Object o) = do+ h <- o .: "height"+ v <- o .: "value"+ t <- o .: "tx_hash"+ p <- o .: "tx_pos"+ i <- txidParse t+ let op = OutPoint i p+ th = TxHeight h i+ return $ Coin op th v+ parseJSON _ = mzero++instance ToJSON Coin where+ toJSON x = object+ [ "height" .= txHeightBlock (coinTxHeight x)+ , "value" .= coinValue x+ , "tx_hash" .= txidToJSON (txHeightId $ coinTxHeight x)+ , "tx_pos" .= outPointIndex (coinOutPoint x)+ ]++method :: StratumQuery -> Text+method (QueryVersion _ _) = "server.version"+method (QueryHistory _) = "blockchain.address.get_history"+method (QueryBalance _) = "blockchain.address.get_balance"+method (QueryUnspent _) = "blockchain.address.get_unspent"+method (QueryTx _) = "blockchain.transaction.get"+method (QueryBroadcast _) = "blockchain.transaction.broadcast"+method (SubAddress _) = "blockchain.address.subscribe"++-- | Create a JSON-RPC request from a Stratum request.+toRequest :: StratumQuery -- ^ Stratum request data.+ -> Int -- ^ JSON-RPC request id.+ -> RequestStratum -- ^ Returns JSON-RPC request object.+toRequest s i = Request (method s) (Just s) (Just (IntId i))++-- | Parse result from JSON-RPC response into a Stratum response.+parseResult :: StratumQuery -- ^ StratumQuery used in corresponding request.+ -> ResultValue -- ^ Result from JSON-RPC response+ -> Parser ResultStratum -- ^ Returns Aeson parser.+parseResult q (Right v) = parseHelper q v >>= return . Right+parseResult _ (Left e) = return $ Left e++parseHelper :: StratumQuery -> Value -> Parser StratumResponse+parseHelper (QueryVersion _ _) v = parseJSON v >>= return . ServerVersion+parseHelper (QueryHistory _) v = parseJSON v >>= return . AddressHistory+parseHelper (QueryBalance _) v = parseJSON v >>= return . AddressBalance+parseHelper (QueryUnspent _) v = parseJSON v >>= return . AddressUnspent+parseHelper (QueryTx _) v = txParse v >>= return . Transaction+parseHelper (QueryBroadcast _) v = txidParse v >>= return . BroadcastId+parseHelper (SubAddress _) v = hashParse v >>= return . AddrStatus++parseNotifHelper :: Method+ -> Value+ -> Parser StratumNotif+parseNotifHelper "blockchain.address.subscribe" (Array v) = do+ a <- parseJSON (V.head v)+ s <- hashParse (V.head $ V.tail v)+ return $ NotifAddress a s+parseNotifHelper _ _ = mzero++-- | Parse notification from JSON-RPC request into Stratum format.+parseNotif :: RequestValue+ -- ^ Request to parse.+ -> Parser NotifStratum+ -- ^ Parser to Stratum request format+parseNotif (Request m (Just p) i) =+ parseNotifHelper m p >>= \s -> return $ Request m (Just s) i+parseNotif _ = mzero++-- | Helper function for Network.Haskoin.JSONRPC.Conduit+newStratumReq :: MonadIO m+ => StratumSession+ -> StratumQuery+ -> m Int+newStratumReq s q = newReq s (toRequest q) p+ where+ p (Response r i) = do+ x <- parseResult q r+ return $ Response x i++txidToJSON :: Hash256 -> Value+txidToJSON = String . pack . encodeTxid++txToJSON :: Tx -> Value+txToJSON = String . pack . bsToHex . encode'++txParse :: Value -> Parser Tx+txParse = withText "bitcoin transaction" $+ return . decode' . fromJust . hexToBS . unpack++txidParse :: Value -> Parser Hash256+txidParse = withText "transaction id" $+ return . fromJust . decodeTxid . unpack++hashToJSON :: Hash256 -> Value+hashToJSON = String . pack . bsToHex . encode'++hashParse :: Value -> Parser Hash256+hashParse = withText "hash" $ return . decode' . fromJust . hexToBS . unpack
+ Network/Haskoin/Stratum/Conduit.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE OverloadedStrings, RankNTypes, FlexibleContexts #-}+module Network.Haskoin.Stratum.Conduit+( -- * Types+ Session+ -- * Functions+, initSession+, newReq+, newNotif+, reqSource+, resConduit+) where++import Prelude hiding (lines, lookup, map, map, null)+import Control.Applicative ((<$>), (<*>))+import Control.Concurrent.MVar (MVar, newMVar, putMVar, readMVar, takeMVar)+import Control.Concurrent.Chan (Chan, getChanContents, newChan, writeChan)+import Control.Monad (unless)+import Control.Monad.Trans (MonadIO, liftIO)+import Data.Aeson (ToJSON, eitherDecode, encode)+import Data.Aeson.Types (Parser, parseEither)+import Data.ByteString (ByteString)+import Data.ByteString.Lazy.Char8 (append, fromStrict, toStrict)+import Data.Conduit (Conduit, Source, ($=), (=$=), await, yield)+import Data.Conduit.List (sourceList, map)+import Data.IntMap.Strict (IntMap, delete, empty, insert, lookup, null)+import Network.Haskoin.Stratum.Message+import Network.Haskoin.Util (maybeToEither)++-- | Parse JSON-RPC ResponseValue to expected Response data type.+type ResponseParser r e v = ResponseValue -> Parser (Response r e v)++-- | Parse JSON-RPC RequestValue to expectedo Request data type.+type RequestParser j = RequestValue -> Parser (Request j)++-- | Map of ids to result parsers.+type ParserMap r e v = IntMap (ResponseParser r e v)++-- | Session state.+data Session q r e v j = Session+ (Chan q) -- Requests to be sent.+ (MVar Int) -- Last id of sent request.+ (MVar (ParserMap r e v)) -- Map of parsers for response result data.+ (Maybe (RequestParser j)) -- Parser for requests or notifications.++-- | Create initial session.+initSession :: MonadIO m+ => Maybe (RequestParser j)+ -- ^ Parse incoming requests and notifications.+ -- Keep connection open.+ -> m (Session q r e v j)+initSession d = liftIO $ Session+ <$> newChan+ <*> newMVar 0+ <*> newMVar empty+ <*> return d++-- | Send a new request. Goes to a channel that is read from reqSource.+newReq :: MonadIO m+ => Session q r e v j -- ^ Session state.+ -> (Int -> q) -- ^ Request builder.+ -> ResponseParser r e v -- ^ Parser for response.+ -> m Int -- ^ Output ID of sent request.+newReq (Session rc iv pv _) f g = liftIO $ do+ i <- (+1) <$> takeMVar iv+ p <- takeMVar pv+ putMVar pv (insert i g p)+ putMVar iv i+ writeChan rc (f i)+ return i++-- | New notification, or request with no id tracking.+newNotif :: MonadIO m+ => Session q r e v j -- ^ Session state.+ -> q -- ^ Request to send to the network.+ -> m () -- ^ No meaningful output.+newNotif (Session rc _ _ _) v = liftIO $ writeChan rc v++-- | Source of requests to send to the network.+reqSource :: (MonadIO m, ToJSON q)+ => Session q r e v j -- ^ Session state.+ -> Source m ByteString -- ^ Source with serialized requests.+reqSource (Session rc _ _ _) = do+ rs <- liftIO $ getChanContents rc+ sourceList rs $= map (toStrict . flip append "\n" . encode)++-- | Conduit that parses messages from network.+resConduit :: (MonadIO m)+ => Session q r e v j -- ^ Session state.+ -> Conduit ByteString m (Either String (Message j r e v))+ -- ^ Returns Conduit with parsed data or parsing errors.+resConduit (Session _ _ pv d) =+ stopOnNull pv (maybe True (const False) d) =$= decodeConduit pv d++decodeConduit :: (MonadIO m)+ => MVar (ParserMap r e v)+ -> Maybe (RequestParser j)+ -> Conduit ByteString m (Either String (Message j r e v))+decodeConduit pv d = do+ mx <- await+ case mx of+ Nothing -> return ()+ Just x -> do+ p <- liftIO $ takeMVar pv+ let m = decodeMsg p d x+ case m of+ Left e -> yield $ Left e+ Right (i, t) -> case i of+ Nothing -> do+ liftIO $ putMVar pv p+ yield $ Right t+ Just n -> do+ liftIO $ putMVar pv $ n `delete` p+ yield $ Right t+ decodeConduit pv d++stopOnNull :: MonadIO m+ => MVar (IntMap a)+ -> Bool+ -> Conduit i m i+stopOnNull pv d = do+ b <- null <$> liftIO (readMVar pv)+ unless (d && b) $ await >>= maybe e (\x -> yield x >> stopOnNull pv d)+ where+ e = error "Connection closed unexpectedly."++decodeMsg :: ParserMap r e v+ -> Maybe (RequestParser j)+ -> ByteString+ -> Either String (Maybe Int, Message j r e v)+decodeMsg p d x = do+ y <- eitherDecode (fromStrict x)+ case y of+ MsgRequest r -> do+ m <- case d of+ Nothing -> Left "No parser for requests"+ Just l -> parseEither l r+ return $ (Nothing, MsgRequest m)+ MsgResponse r -> do+ (i, z) <- decodeRes p r+ return $ (Just i, MsgResponse z)++decodeRes :: ParserMap r e v+ -> ResponseValue+ -> Either String (Int, (Response r e v))+decodeRes p r = do+ i <- case resId r of+ Just n -> numericId n+ Nothing -> Left "Id is not set."+ a <- maybeToEither (e i) (lookup i p)+ t <- parseEither a r+ return (i, t)+ where+ e i = "Parser not found for response id " ++ show i ++ "."
+ Network/Haskoin/Stratum/Message.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE OverloadedStrings #-}+-- | Implementation of basic JSON-RPC data types.+module Network.Haskoin.Stratum.Message+( -- * Types+ Method+, ErrorValue+, RequestValue+, ResponseValue+, MessageValue+, ResultValue+, Id(..)+, Result+, Error(..)+ -- * Messages+, Request(..)+, Response(..)+, Message(..)+ -- * Errors+, errParse+, errReq+, errMeth+, errParams+, errInternal+, errStr+ -- * Helpers+, leftStr+, numericId+) where++import Control.Applicative ((<|>))+import Control.Monad (mzero)+import Data.Aeson.Types hiding (Result)+import Data.Text (Text, unpack)+import Text.Read (readEither)+ +-- | JSON-RPC method name.+type Method = Text+-- | JSON-RPC result.+type Result r e v = Either (Error e v) r+-- | JSON-RPC error object with default JSON values.+type ErrorValue = Error Value String+-- | JSON-RPC request with default JSON values.+type RequestValue = Request Value+-- | JSON-RPC response with default JSON values.+type ResponseValue = Response Value Value String+-- | JSON-RPC request or response with default JSON values.+type MessageValue = Message Value Value Value String+-- | JSON-RPC result with default JSON values.+type ResultValue = Result Value Value String++-- | JSON-RPC id in text or integer form.+data Id = IntId { intId :: Int } -- ^ Id in integer form.+ | TxtId { txtId :: Text } -- ^ Id in string form (discouraged).+ deriving (Eq, Show)++-- | JSON-RPC error object in v1 or v2 format.+-- Sent inside a JSONRes in case of error.+data Error e v -- | Error object in JSON-RPC version 2 format.+ = ErrObj+ { errCode :: Int -- ^ Integer error code.+ , errMsg :: String -- ^ Error message.+ , errData :: Maybe e -- ^ Optional error object.+ }+ -- | Error object in JSON-RPC version 1 format.+ | ErrVal+ { errVal :: v -- ^ Usually String.+ }+ deriving (Eq, Show)++-- | JSON-RPC request on notification.+data Request j = Request+ { reqMethod :: Method -- ^ Request method.+ , reqParams :: Maybe j -- ^ Request parameters. Should be Object or Array.+ , reqId :: Maybe Id -- ^ Request id. Nothing for notifications.+ } deriving (Eq, Show)++-- | JSON-RPC response or error.+data Response r e v = Response+ { resResult :: Result r e v -- ^ Result or error.+ , resId :: Maybe Id -- ^ Result id.+ } deriving (Eq, Show)++-- | JSON-RPC message, can contain request or response.+data Message j r e v+ -- | Request message container.+ = MsgRequest (Request j)+ -- | response message container.+ | MsgResponse (Response r e v)+ deriving (Eq, Show)++instance FromJSON Id where+ parseJSON (String s) = return (TxtId s)+ parseJSON (Number n) = do+ let (i, d) = properFraction n+ if d == 0.0 then return (IntId i) else mzero+ parseJSON _ = mzero++instance ToJSON Id where+ toJSON (TxtId s) = toJSON s+ toJSON (IntId i) = toJSON i++instance (FromJSON e, FromJSON v) => FromJSON (Error e v) where+ parseJSON v@(Object o) = do+ mc <- o .:? "code"+ mm <- o .:? "message"+ md <- o .:? "data"+ case (mc, mm) of+ (Just c, Just m) -> return (ErrObj c m md)+ _ -> parseJSON v >>= return . ErrVal+ parseJSON v = parseJSON v >>= return . ErrVal++instance (ToJSON e, ToJSON v) => ToJSON (Error e v) where+ toJSON (ErrObj c m d) = object+ [ "code" .= c+ , "message" .= m+ , "data" .= d ]+ toJSON (ErrVal v) = toJSON v++instance FromJSON j => FromJSON (Request j) where+ parseJSON (Object o) = do+ m <- o .: "method"+ p <- o .:? "params"+ i <- o .:? "id"+ return (Request m p i)+ parseJSON _ = mzero++instance ToJSON j => ToJSON (Request j) where+ toJSON (Request m p i) = object $ filter f+ [ "jsonrpc" .= ("2.0" :: String)+ , "method" .= m+ , "params" .= p+ , "id" .= i ]+ where+ f (_, Null) = False+ f _ = True++instance (FromJSON r, FromJSON e, FromJSON v)+ => FromJSON (Response r e v)+ where+ parseJSON (Object o) = do+ mi <- o .: "id"+ me <- o .:? "error" .!= Nothing+ mr <- o .:? "result" .!= Nothing+ case (me, mr) of+ (Just e, _) -> return (Response (Left e) mi)+ (_, Just r) -> return (Response (Right r) mi)+ _ -> mzero+ parseJSON _ = mzero++instance (ToJSON r, ToJSON e, ToJSON v)+ => ToJSON (Response r e v)+ where+ toJSON (Response (Right r) i) = object+ [ "jsonrpc" .= ("2.0" :: String)+ , "id" .= i+ , "result" .= r+ ]+ toJSON (Response (Left e) i) = object+ [ "jsonrpc" .= ("2.0" :: String)+ , "id" .= i+ , "error" .= e+ ]++instance (FromJSON j, FromJSON r, FromJSON e, FromJSON v)+ => FromJSON (Message j r e v)+ where+ parseJSON o@(Object _) = q <|> s+ where+ q = parseJSON o >>= return . MsgRequest+ s = parseJSON o >>= return . MsgResponse+ parseJSON _ = mzero++instance (ToJSON j, ToJSON r, ToJSON e, ToJSON v)+ => ToJSON (Message j r e v)+ where+ toJSON (MsgRequest r) = toJSON r+ toJSON (MsgResponse r) = toJSON r++-- | Parse error in JSON-RPC v2 format.+-- Provide optional error object.+errParse :: ToJSON e => Maybe e -> Error e v+errParse = ErrObj (-32700) "Parse error"++-- | Request error in JSON-RPC v2 format.+-- Provide optional error object.+errReq :: ToJSON e => Maybe e -> Error e v+errReq = ErrObj (-32600) "Invalid request"++-- | Unknown method error in JSON-RPC v2 format.+-- Provide optional error object.+errMeth :: ToJSON e => Maybe e -> Error e v+errMeth = ErrObj (-32601) "Method not found"++-- | Invalid parameters error in JSON-RPC v2 format.+-- Provide optional error object.+errParams :: ToJSON e => Maybe e -> Error e v+errParams = ErrObj (-32602) "Invalid params"++-- | Internal error in JSON-RPC v2 format.+-- Provide optional error object.+errInternal :: ToJSON e => Maybe e -> Error e v+errInternal = ErrObj (-32606) "Internal error"++-- | Get string from error object.+errStr :: Error e Value -> String+errStr (ErrObj _ m _) = m+errStr (ErrVal v) = either id id . flip parseEither v $+ withText "error string" (return . unpack)++-- | Map Left error objects to strings.+leftStr :: Either (Error e Value) r -> Either String r+leftStr (Left e) = Left (errStr e)+leftStr (Right r) = Right r++-- | Force an id into a number or fail if not possible.+numericId :: Id -> Either String Int+numericId (IntId i) = Right i+numericId (TxtId t) = readEither $ unpack t
+ Network/Haskoin/Transaction.hs view
@@ -0,0 +1,26 @@+{-|+ This package provides functions for building and signing both simple+ transactions and multisignature transactions.+-}+module Network.Haskoin.Transaction+(+ -- *Build Transactions+ buildTx+, buildAddrTx++ -- *Transaction signing+, SigInput(..)+, signTx+, detSignTx+, isTxComplete++ -- *Coin selection+, Coin(..)+, chooseCoins+, chooseMSCoins+, guessTxSize++) where++import Network.Haskoin.Transaction.Builder+
+ Network/Haskoin/Transaction/Builder.hs view
@@ -0,0 +1,362 @@+module Network.Haskoin.Transaction.Builder +( Coin(..)+, buildTx+, buildAddrTx+, SigInput(..)+, signTx+, detSignTx+, verifyTx+, guessTxSize+, chooseCoins+, chooseMSCoins+, getFee+, getMSFee+, isTxComplete+) where++import Control.Monad (when, guard, liftM2)+import Control.Applicative ((<$>))+import Control.Monad.Trans (lift)++import Data.Maybe (catMaybes, maybeToList, fromMaybe)+import Data.List (sortBy, find)+import Data.Word (Word64)+import qualified Data.ByteString as BS (length, replicate)++import Network.Haskoin.Util+import Network.Haskoin.Crypto+import Network.Haskoin.Protocol+import Network.Haskoin.Script++-- | A Coin is something that can be spent by a transaction and is+-- represented by a transaction output, an outpoint and optionally a+-- redeem script.+data Coin = + Coin { coinTxOut :: TxOut -- ^ Transaction output+ , coinOutPoint :: OutPoint -- ^ Previous outpoint+ , coinRedeem :: Maybe Script -- ^ Redeem script+ } deriving (Eq, Show)++-- | 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 :: Word64 -- ^ Target price to pay.+ -> Word64 -- ^ Fee price per 1000 bytes.+ -> [Coin] -- ^ List of coins to choose from.+ -> Either String ([Coin],Word64) + -- ^ Coin selection result and change amount.+chooseCoins target kbfee xs + | target > 0 = maybeToEither err $ greedyAdd target (getFee kbfee) xs+ | otherwise = Left "chooseCoins: Target must be > 0"+ where err = "chooseCoins: No solution found"++-- | Coin selection algorithm for multisignature 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 :: Word64 -- ^ Target price to pay.+ -> Word64 -- ^ Fee price per 1000 bytes.+ -> (Int,Int) -- ^ Multisig parameters m of n (m,n).+ -> [Coin] -- ^ List of coins to choose from.+ -> Either String ([Coin],Word64) + -- ^ Coin selection result and change amount.+chooseMSCoins target kbfee ms xs + | target > 0 = maybeToEither err $ greedyAdd target (getMSFee kbfee ms) xs+ | otherwise = Left "chooseMSCoins: Target must be > 0"+ where err = "chooseMSCoins: No solution found"++-- Select coins greedily by starting from an empty solution+greedyAdd :: Word64 -> (Int -> Word64) -> [Coin] -> Maybe ([Coin],Word64)+greedyAdd target fee xs = go [] 0 [] 0 $ sortBy desc xs+ where desc a b = compare (outValue $ coinTxOut b) (outValue $ coinTxOut a)+ goal c = target + fee c+ go _ _ [] _ [] = Nothing+ go _ _ ps pTot [] = return (ps,pTot - (goal $ length ps))+ go acc aTot ps pTot (y:ys)+ | val + aTot >= (goal $ length acc + 1) =+ if aTot + val - target < pTot - target+ then go [] 0 (y:acc) (aTot + val) ys+ else return (ps,pTot - (goal $ length ps))+ | otherwise = go (y:acc) (aTot + val) ps pTot ys+ where val = outValue $ coinTxOut y++{-+-- Start from a solution containing all coins and greedily remove them+greedyRem :: Word64 -> (Int -> Word64) -> [Coin] -> Maybe ([Coin],Word64)+greedyRem target fee xs + | s < goal (length xs) = Nothing+ | otherwise = return $ go [] s $ sortBy desc xs+ where desc a b = compare (outValue $ coinTxOut b) (outValue $ coinTxOut a)+ s = sum $ map (outValue . coinTxOut) xs+ goal c = target + fee c+ go acc tot [] = (acc,tot - (goal $ length acc))+ go acc tot (y:ys) + | tot - val >= (goal $ length ys + length acc) = + go acc (tot - val) ys+ | otherwise = go (y:acc) tot ys+ where val = outValue $ coinTxOut y+-}++getFee :: Word64 -> Int -> Word64+getFee kbfee count = kbfee*((len + 999) `div` 1000)+ where len = fromIntegral $ guessTxSize count [] 2 0++getMSFee :: Word64 -> (Int,Int) -> Int -> Word64+getMSFee kbfee ms count = kbfee*((len + 999) `div` 1000)+ where len = fromIntegral $ guessTxSize 0 (replicate count ms) 2 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)] + -- ^ For every multisig input in the transaction, provide+ -- the multisig parameters m of n (m,n) for that input.+ -> Int -- ^ Number of pay to public key hash outputs.+ -> Int -- ^ Number of pay to script hash outputs.+ -> Int -- ^ Upper bound on the transaction size.+guessTxSize pki msi pkout msout = 8 + inpLen + inp + outLen + out+ where inpLen = BS.length $ encode' $ + VarInt $ fromIntegral $ (length msi) + pki+ outLen = BS.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 pay2sh input+guessMSSize :: (Int,Int) -> Int+ -- OutPoint (36) + Sequence (4) + Script+guessMSSize (m,n) = 40 + (BS.length $ encode' $ VarInt $ fromIntegral scp) + scp+ -- OP_M + n*PubKey + OP_N + OP_CHECKMULTISIG+ where rdm = BS.length $ encode' $ opPushData $ BS.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 recipients addresses and amounts as outputs. +buildAddrTx :: [OutPoint] -> [(String,Word64)] -> Either String Tx+buildAddrTx xs ys = buildTx xs =<< mapM f ys+ where f (s,v) = case base58ToAddr s of+ Just a@(PubKeyAddress _) -> return (PayPKHash a,v)+ Just a@(ScriptAddress _) -> return (PayScriptHash a,v)+ _ -> Left $ "buildAddrTx: Invalid address " ++ s++-- | 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 (Script []) maxBound+ fo (o,v) | v <= 2100000000000000 = return $ TxOut v $ encodeOutput o+ | otherwise = Left $ "buildTx: Invalid amount " ++ (show v)++-- | 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 + -- | Parameters for signing a pay to public key hash output.+ = SigInput { sigDataOut :: Script -- ^ Output script to spend.+ , sigDataOP :: OutPoint + -- ^ Reference to the transaction output to spend.+ , sigDataSH :: SigHash -- ^ Signature type.+ } + -- | Parameters for signing a pay to script hash output.+ | SigInputSH { sigDataOut :: Script + , sigDataOP :: OutPoint + , sigRedeem :: Script -- ^ Redeem script.+ , sigDataSH :: SigHash + } deriving (Eq, Show)++liftSecret :: Monad m => Build a -> SecretT (BuildT m) a+liftSecret = lift . liftBuild++-- | Returns True if all the inputs of a transactions are non-empty and if+-- all multisignature inputs are fully signed.+isTxComplete :: Tx -> Bool+isTxComplete = isComplete . (mapM toBuildTxIn) . txIn++-- | Sign a transaction by providing the 'SigInput' signing parameters and a+-- list of private keys. The signature is computed within the 'SecretT' monad+-- to generate the random signing nonce and within the 'BuildT' monad to add+-- information on wether the result was fully or partially signed.+signTx :: Monad m + => Tx -- ^ Transaction to sign+ -> [SigInput] -- ^ SigInput signing parameters+ -> [PrvKey] -- ^ List of private keys to use for signing+ -> SecretT (BuildT m) Tx -- ^ Signed transaction+signTx tx@(Tx _ ti _ _) sigis keys = do+ liftSecret $ when (null ti) $ Broken "signTx: Transaction has no inputs"+ newIn <- mapM sign $ orderSigInput ti sigis+ return tx{ txIn = newIn }+ where sign (maybeSI,txin,i) = case maybeSI of+ Just sigi -> signTxIn txin sigi tx i keys+ _ -> liftSecret $ toBuildTxIn txin++signTxIn :: Monad m => TxIn -> SigInput -> Tx -> Int -> [PrvKey] + -> SecretT (BuildT m) TxIn+signTxIn txin sigi tx i keys = do+ (out,vKeys,pubs,buildf) <- liftSecret $ decodeSigInput sigi keys+ let msg = txSigHash tx (encodeOutput out) i sh+ sigs <- mapM (signMsg msg) vKeys+ liftSecret $ buildf txin tx out i pubs $ map (flip TxSignature sh) sigs+ where sh = sigDataSH sigi++-- | Sign a transaction by providing the 'SigInput' signing paramters and +-- a list of private keys. The signature is computed deterministically as+-- defined in RFC-6979. The signature is computed within the 'Build' monad+-- to add information on wether the result was fully or partially signed.+detSignTx :: Tx -- ^ Transaction to sign+ -> [SigInput] -- ^ SigInput signing parameters+ -> [PrvKey] -- ^ List of private keys to use for signing+ -> Build Tx -- ^ Signed transaction+detSignTx tx@(Tx _ ti _ _) sigis keys = do+ when (null ti) $ Broken "detSignTx: Transaction has no inputs"+ newIn <- mapM sign $ orderSigInput ti sigis+ return tx{ txIn = newIn }+ where + sign (maybeSI,txin,i) + | isComplete txinB = txinB+ | otherwise = case maybeSI of+ Just sigi -> detSignTxIn txin sigi tx i keys+ _ -> txinB+ where+ txinB = toBuildTxIn txin+ +detSignTxIn :: TxIn -> SigInput -> Tx -> Int -> [PrvKey] -> Build TxIn+detSignTxIn txin sigi tx i keys = do+ (out,vKeys,pubs,buildf) <- decodeSigInput sigi keys+ let msg = txSigHash tx (encodeOutput out) i sh+ sigs = map (detSignMsg msg) vKeys+ buildf txin tx out i pubs $ map (flip TxSignature sh) sigs+ where sh = sigDataSH sigi++{- Helpers for signing transactions -}++-- |Decides if a TxIn is complete. If the TxIn could not be decoded and it+-- is not empty, we consider it complete.+toBuildTxIn :: TxIn -> Build TxIn+toBuildTxIn txin@(TxIn _ s _)+ | null $ scriptOps s = Partial txin+ | otherwise = case decodeScriptHash s of+ Right (ScriptHashInput (SpendMulSig xs r) _) -> + guardPartial (length xs == r) >> return txin+ Right _ -> return txin+ Left _ -> case decodeInput s of+ Right (SpendMulSig xs r) ->+ guardPartial (length xs == r) >> return txin+ _ -> return txin++orderSigInput :: [TxIn] -> [SigInput] -> [(Maybe SigInput, TxIn, Int)]+orderSigInput ti si = zip3 (matchTemplate si ti f) ti [0..]+ where f s txin = sigDataOP s == prevOutput txin++type BuildFunction = TxIn -> Tx -> ScriptOutput -> Int + -> [PubKey] -> [TxSignature] -> Build TxIn++decodeSigInput :: SigInput -> [PrvKey] -> + Build (ScriptOutput, [PrvKey], [PubKey], BuildFunction)+decodeSigInput sigi keys = case sigi of+ SigInput s _ _ -> do+ out <- eitherToBuild $ decodeOutput s+ (vKeys,pubs) <- sigKeys out keys+ return (out,vKeys,pubs,buildTxIn)+ SigInputSH s _ sr _ -> do+ out <- eitherToBuild $ decodeOutput s+ rdm <- eitherToBuild $ decodeOutput sr+ (vKeys,pubs) <- sigKeysSH out rdm keys+ return (rdm,vKeys,pubs,buildTxInSH)++buildTxInSH :: BuildFunction+buildTxInSH txin tx rdm i pubs sigs = do+ s <- scriptInput <$> buildTxIn txin tx rdm i pubs sigs+ res <- either emptyIn return $ + encodeScriptHash . (flip ScriptHashInput rdm) <$> decodeInput s+ return txin{ scriptInput = res }+ where emptyIn = const $ Partial $ Script []++buildTxIn :: BuildFunction+buildTxIn txin tx out i pubs sigs + | null sigs = Partial txin{ scriptInput = Script [] }+ | otherwise = buildRes <$> case out of+ PayPK _ -> return $ SpendPK $ head sigs+ PayPKHash _ -> return $ SpendPKHash (head sigs) (head pubs)+ PayMulSig msPubs r -> do+ let mSigs = take r $ catMaybes $ matchTemplate aSigs msPubs f+ guardPartial $ length mSigs == r+ return $ SpendMulSig mSigs r+ _ -> Broken "buildTxIn: Can't sign a P2SH script here"+ where buildRes res = txin{ scriptInput = encodeInput res }+ aSigs = concat+ [ sigs + , case decodeScriptHash $ scriptInput txin of+ Right (ScriptHashInput (SpendMulSig xs _) _) -> xs+ _ -> case decodeInput $ scriptInput txin of+ Right (SpendMulSig xs _) -> xs+ _ -> []+ ]+ f (TxSignature sig sh) pub = + verifySig (txSigHash tx (encodeOutput out) i sh) sig pub++sigKeysSH :: ScriptOutput -> RedeemScript -> [PrvKey]+ -> Build ([PrvKey],[PubKey])+sigKeysSH out rdm keys = case out of+ PayScriptHash a -> if scriptAddr rdm == a+ then sigKeys rdm keys+ else Broken "sigKeys: Redeem script does not match P2SH script"+ _ -> Broken "sigKeys: Can only decode P2SH script here"++sigKeys :: ScriptOutput -> [PrvKey] -> Build ([PrvKey],[PubKey])+sigKeys out keys = unzip <$> case out of+ PayPK p -> return $ maybeToList $ + find ((== p) . snd) zipKeys+ PayPKHash a -> return $ maybeToList $ + find ((== a) . pubKeyAddr . snd) zipKeys+ PayMulSig ps r -> return $ take r $ + filter ((`elem` ps) . snd) zipKeys+ _ -> Broken "sigKeys: Can't decode P2SH here" + where zipKeys = zip keys $ map derivePubKey keys++{- Tx verification -}++-- This is not the final transaction verification function. It is here mainly+-- as a helper for tests. It can only validates standard inputs.+verifyTx :: Tx -> [(Script,OutPoint)] -> Bool+verifyTx tx xs = flip all z3 $ \(maybeS,txin,i) -> fromMaybe False $ do+ (out,inp) <- maybeS >>= flip decodeVerifySigInput txin+ let so = encodeOutput out+ case (out,inp) of+ (PayPK pub,SpendPK (TxSignature sig sh)) -> + return $ verifySig (txSigHash tx so i sh) sig pub+ (PayPKHash a,SpendPKHash (TxSignature sig sh) pub) -> do+ guard $ pubKeyAddr pub == a+ return $ verifySig (txSigHash tx so i sh) sig pub+ (PayMulSig pubs r,SpendMulSig sigs _) ->+ (== r) <$> countMulSig tx so i pubs sigs + _ -> Nothing+ where m = map (fst <$>) $ matchTemplate xs (txIn tx) f+ f (_,o) txin = o == prevOutput txin+ z3 = zip3 m (txIn tx) [0..]+ +-- Count the number of valid signatures+countMulSig :: Tx -> Script -> Int -> [PubKey] -> [TxSignature] -> Maybe Int+countMulSig _ _ _ [] _ = return 0+countMulSig _ _ _ _ [] = return 0+countMulSig tx so i (pub:pubs) sigs@(TxSignature sig sh:rest)+ | verifySig (txSigHash tx so i sh) sig pub = + (+1) <$> countMulSig tx so i pubs rest+ | otherwise = countMulSig tx so i pubs sigs+ +decodeVerifySigInput :: Script -> TxIn -> Maybe (ScriptOutput, ScriptInput)+decodeVerifySigInput so (TxIn _ si _ ) = case decodeOutput so of+ Right (PayScriptHash a) -> do+ (ScriptHashInput inp rdm) <- eitherToMaybe $ decodeScriptHash si+ guard $ scriptAddr rdm == a+ return (rdm,inp)+ out -> eitherToMaybe $ liftM2 (,) out (decodeInput si)+
+ Network/Haskoin/Util.hs view
@@ -0,0 +1,332 @@+{-|+ This module defines various utility functions used across the + Network.Haskoin modules.+-}+module Network.Haskoin.Util+( + -- * ByteString helpers+ toStrictBS+, toLazyBS+, stringToBS+, bsToString+, bsToInteger+, integerToBS+, bsToHex+, hexToBS++ -- * Data.Binary helpers+, encode'+, decode'+, runPut'+, runGet'+, decodeOrFail'+, runGetOrFail'+, fromDecode+, fromRunGet+, decodeToEither+, decodeToMaybe+, isolate++ -- * Maybe and Either monad helpers+, isLeft+, isRight+, fromRight+, fromLeft+, eitherToMaybe+, maybeToEither+, liftEither+, liftMaybe++ -- * Various helpers+, updateIndex+, matchTemplate++ -- Triples+, fst3+, snd3+, lst3++ -- *Build monad+, Build(..)+, isComplete+, isPartial+, isBroken+, eitherToBuild+, buildToEither+, guardPartial++ -- *BuildT transformer monad+, BuildT(..)+, liftBuild++ -- *Constants+, addrPrefix+, scriptPrefix+, secretPrefix+, extPubKeyPrefix+, extSecretPrefix+, networkMagic+, genesisHeader+, maxBlockSize+, walletFile+, haskoinUserAgent++) where++import Numeric (readHex)+import Control.Applicative ((<$>))+import Control.Monad (guard)+import Control.Monad.Trans.Either (EitherT, hoistEither)+import Text.Printf (printf)++import Data.Monoid (mappend)+import Data.Word (Word8)+import Data.Bits ((.|.), shiftL, shiftR)+import Data.List (unfoldr)+import Data.List.Split (chunksOf)+import Data.Binary.Put (Put, runPut)+import Data.Binary + ( Binary+ , encode+ , decode+ , decodeOrFail+ )+import Data.Binary.Get+ ( Get+ , runGetOrFail+ , getByteString+ , ByteOffset+ , runGet+ )++import qualified Data.ByteString.Lazy as BL + ( ByteString+ , toChunks+ , fromChunks+ )+import qualified Data.ByteString as BS + ( ByteString+ , concat+ , pack, unpack+ , foldl'+ , null+ )+import qualified Data.ByteString.Char8 as C+ ( pack+ , unpack+ )++import Network.Haskoin.Util.BuildMonad+import Network.Haskoin.Util.Constants++-- ByteString helpers++-- | Transforms a lazy bytestring into a strict bytestring+toStrictBS :: BL.ByteString -> BS.ByteString+toStrictBS = BS.concat . BL.toChunks++-- | Transforms a strict bytestring into a lazy bytestring+toLazyBS :: BS.ByteString -> BL.ByteString+toLazyBS bs = BL.fromChunks [bs]++-- | Transforms a string into a strict bytestring+stringToBS :: String -> BS.ByteString+stringToBS = C.pack++-- | Transform a strict bytestring to a string+bsToString :: BS.ByteString -> String+bsToString = C.unpack++-- | Decode a big endian Integer from a bytestring+bsToInteger :: BS.ByteString -> Integer+bsToInteger = (foldr f 0) . reverse . BS.unpack+ where + f w n = (toInteger w) .|. shiftL n 8++-- | Encode an Integer to a bytestring as big endian+integerToBS :: Integer -> BS.ByteString+integerToBS 0 = BS.pack [0]+integerToBS i + | i > 0 = BS.pack $ reverse $ unfoldr f i+ | otherwise = error "integerToBS not defined for negative values"+ where + f 0 = Nothing+ f x = Just $ (fromInteger x :: Word8, x `shiftR` 8)++-- | Encode a bytestring to a base16 (HEX) representation+bsToHex :: BS.ByteString -> String+bsToHex = BS.foldl' (\a x -> mappend a (printf "%02x" x)) ""++-- | Decode a base16 (HEX) string from a bytestring. This function can fail+-- if the string contains invalid HEX characters+hexToBS :: String -> Maybe BS.ByteString+hexToBS xs = BS.pack <$> mapM hexWord (chunksOf 2 xs)+ where+ hexWord x = do+ guard $ length x == 2+ let (w, s) = head $ readHex x+ guard $ s == ""+ return w++-- Data.Binary helpers++-- | Strict version of @Data.Binary.encode@+encode' :: Binary a => a -> BS.ByteString+encode' = toStrictBS . encode++-- | Strict version of @Data.Binary.decode@+decode' :: Binary a => BS.ByteString -> a+decode' = decode . toLazyBS++-- | Strict version of @Data.Binary.runGet@+runGet' :: Binary a => Get a -> BS.ByteString -> a+runGet' m = (runGet m) . toLazyBS++-- | Strict version of @Data.Binary.runPut@+runPut' :: Put -> BS.ByteString+runPut' = toStrictBS . runPut++-- | Strict version of @Data.Binary.decodeOrFail@+decodeOrFail' :: + Binary a => + BS.ByteString -> + Either (BS.ByteString, ByteOffset, String) (BS.ByteString, ByteOffset, a)+decodeOrFail' bs = case decodeOrFail $ toLazyBS bs of+ Left (lbs,o,err) -> Left (toStrictBS lbs,o,err)+ Right (lbs,o,res) -> Right (toStrictBS lbs,o,res)++-- | Strict version of @Data.Binary.runGetOrFail@+runGetOrFail' ::+ Binary a => Get a -> BS.ByteString ->+ Either (BS.ByteString, ByteOffset, String) (BS.ByteString, ByteOffset, a)+runGetOrFail' m bs = case runGetOrFail m $ toLazyBS bs of+ Left (lbs,o,err) -> Left (toStrictBS lbs,o,err)+ Right (lbs,o,res) -> Right (toStrictBS lbs,o,res)++-- | Try to decode a Data.Binary value. If decoding succeeds, apply the function+-- to the result. Otherwise, return the default value.+fromDecode :: Binary a + => BS.ByteString -- ^ The bytestring to decode+ -> b -- ^ Default value to return when decoding fails + -> (a -> b) -- ^ Function to apply when decoding succeeds + -> b -- ^ Final result+fromDecode bs def f = either (const def) (f . lst) $ decodeOrFail' bs+ where + lst (_,_,c) = c++-- | Try to run a Data.Binary.Get monad. If decoding succeeds, apply a function+-- to the result. Otherwise, return the default value.+fromRunGet :: Binary a + => Get a -- ^ The Get monad to run+ -> BS.ByteString -- ^ The bytestring to decode+ -> b -- ^ Default value to return when decoding fails + -> (a -> b) -- ^ Function to apply when decoding succeeds + -> b -- ^ Final result+fromRunGet m bs def f = either (const def) (f . lst) $ runGetOrFail' m bs+ where + lst (_,_,c) = c++-- | Decode a Data.Binary value into the Either monad. A Right value is returned+-- with the result upon success. Otherwise a Left value with the error message+-- is returned.+decodeToEither :: Binary a => BS.ByteString -> Either String a+decodeToEither bs = case decodeOrFail' bs of+ Left (_,_,err) -> Left err+ Right (_,_,res) -> Right res++-- | Decode a Data.Binary value into the Maybe monad. A Just value is returned+-- with the result upon success. Otherwise, Nothing is returned.+decodeToMaybe :: Binary a => BS.ByteString -> Maybe a+decodeToMaybe bs = fromDecode bs Nothing Just++-- | Isolate a Data.Binary.Get monad for the next @Int@ bytes. Only the next+-- @Int@ bytes of the input bytestring will be available for the Get monad to+-- consume. This function will fail if the Get monad fails or some of the input+-- is not consumed.+isolate :: Binary a => Int -> Get a -> Get a+isolate i g = do+ bs <- getByteString i+ case runGetOrFail' g bs of+ Left (_, _, err) -> fail err+ Right (unconsumed, _, res)+ | BS.null unconsumed -> return res+ | otherwise -> fail "Isolate: unconsumed input"++-- Maybe and Eithre monad helpers++-- | Returns True if the Either value is Right+isRight :: Either a b -> Bool+isRight (Right _) = True+isRight _ = False++-- | Returns True if the Either value is Left+isLeft :: Either a b -> Bool+isLeft = not . isRight++-- | Extract the Right value from an Either value. Fails if the value is Left+fromRight :: Either a b -> b+fromRight (Right b) = b+fromRight _ = error "Either.fromRight: Left"++-- | Extract the Left value from an Either value. Fails if the value is Right+fromLeft :: Either a b -> a+fromLeft (Left a) = a+fromLeft _ = error "Either.fromLeft: Right"++-- | Transforms 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++-- | Transforms a Maybe value into an Either value. Just is mapped to Right and+-- Nothing is mapped to Left. You also pass in an error value in case Left is+-- returned.+maybeToEither :: b -> Maybe a -> Either b a+maybeToEither err m = maybe (Left err) Right m++-- | Lift a Either computation into the EitherT monad+liftEither :: Monad m => Either b a -> EitherT b m a+liftEither = hoistEither++-- | Lift a Maybe computation into the EitherT monad+liftMaybe :: Monad m => b -> Maybe a -> EitherT b m a+liftMaybe err = liftEither . (maybeToEither err)++-- Various helpers++-- | Applies a function to only one element of a list defined by it's index.+-- If the index is out of the bounds of the list, the original list is returned+updateIndex :: Int -- ^ The index of the element to change+ -> [a] -- ^ The list of elements+ -> (a -> a) -- ^ The function to apply+ -> [a] -- ^ The 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] -- ^ The input list+ -> [b] -- ^ The list to serve as a template + -> (a -> b -> Bool) -- ^ The comparison function+ -> [Maybe a] -- ^ Results of the template matching+matchTemplate [] bs _ = replicate (length bs) Nothing+matchTemplate _ [] _ = []+matchTemplate as (b:bs) f = case break (flip f b) as of+ (l,(r:rs)) -> (Just r) : matchTemplate (l ++ rs) bs f+ _ -> Nothing : matchTemplate as bs f++fst3 :: (a,b,c) -> a+fst3 (a,_,_) = a++snd3 :: (a,b,c) -> b+snd3 (_,b,_) = b++lst3 :: (a,b,c) -> c+lst3 (_,_,c) = c+
+ Network/Haskoin/Util/BuildMonad.hs view
@@ -0,0 +1,136 @@+{-|+ The Build type, and associated operations.+-}+module Network.Haskoin.Util.BuildMonad +( Build(..)+, BuildT(..)+, isComplete+, isPartial+, isBroken+, eitherToBuild+, buildToEither+, guardPartial+, liftBuild+) where++import Control.Monad (liftM)+import Control.Monad.Trans + ( MonadTrans+ , MonadIO+ , lift+ , liftIO+ )++{-|+ The Build monad represents computations that can be in one of three states:+ + * Complete++ * Partial++ * Broken++ It extends the Either monad with an additional Partial value to describe a+ valid computation flagged with a Partial context. The Build monad is useful+ when you describe computations where parts of the computation are either+ complete, partially complete or broken. Combining only Complete computations+ will produce a Complete result. However, if one of the computations is+ Partial, the whole computation will be Partial as well. And if some+ computation is Broken, the whole computation will be broken as well.++ The Build monad is used by Haskoin to describe the state of the transaction+ signing computation. To sign a transaction, all input scripts need to be + signed. The whole transaction will be completely signed only if all the+ input scripts are completely signed. If any of the inputs is partially signed,+ then the whole transaction will be partially signed as well. And the whole+ transaction is broken if one of the inputs failed to parse or is broken.+-}+data Build a + -- | Describes a successful complete computation+ = Complete { runBuild :: a } + -- | Describes a successful but partial computation+ | Partial { runBuild :: a }+ -- | Describes a broken computation+ | Broken { runBroken :: String }+ deriving Eq++instance Show a => Show (Build a) where+ show (Complete a) = "Complete " ++ (show a)+ show (Partial a) = "Partial " ++ (show a)+ show (Broken str) = "Broken " ++ str++instance Functor Build where+ fmap f (Complete x) = Complete (f x)+ fmap f (Partial x) = Partial (f x)+ fmap _ (Broken s) = Broken s++instance Monad Build where+ return = Complete+ Complete x >>= f = f x+ Partial x >>= f = case f x of+ e@(Broken _) -> e+ a -> Partial $ runBuild a+ Broken s >>= _ = Broken s++-- | Returns True if the Build value is Complete+isComplete :: Build a -> Bool+isComplete (Complete _) = True+isComplete _ = False++-- | Returns True if the Build value is Partial+isPartial :: Build a -> Bool+isPartial (Partial _) = True+isPartial _ = False++-- | Return True if the Build value is Broken+isBroken :: Build a -> Bool+isBroken (Broken _) = True+isBroken _ = False++-- | Transforms an Either String value into a Build value. Right is mapped to+-- Complete and Left is mapped to Broken+eitherToBuild :: Either String a -> Build a+eitherToBuild m = case m of+ Left err -> Broken err + Right res -> Complete res++-- | Transforms a Build value into an Either String value. Complete and Partial+-- are mapped to Right and Broken is mapped to Left.+buildToEither :: Build a -> Either String a+buildToEither m = case m of+ Complete a -> Right a+ Partial a -> Right a+ Broken err -> Left err++-- | Binds a Partial value to the computation when the predicate is False.+guardPartial :: Bool -> Build ()+guardPartial True = Complete ()+guardPartial False = Partial ()++-- | BuildT transformer monad+newtype BuildT m a = BuildT { runBuildT :: m (Build a) }++mapBuildT :: (m (Build a) -> n (Build b)) -> BuildT m a -> BuildT n b+mapBuildT f = BuildT . f . runBuildT++instance Functor m => Functor (BuildT m) where+ fmap f = mapBuildT (fmap (fmap f))++instance Monad m => Monad (BuildT m) where+ return = lift . return+ x >>= f = BuildT $ do+ v <- runBuildT x+ case v of Complete a -> runBuildT (f a)+ Partial a -> runBuildT (f a)+ Broken str -> return $ Broken str++instance MonadTrans BuildT where+ lift = BuildT . liftM Complete++instance MonadIO m => MonadIO (BuildT m) where+ liftIO = lift . liftIO++-- | Lift a Build computation into the BuildT monad+liftBuild :: Monad m => Build a -> BuildT m a+liftBuild = BuildT . return+
+ Network/Haskoin/Util/Constants.hs view
@@ -0,0 +1,55 @@+{-|+ Declaration of constants used across the bitcoin protocol.+-}+module Network.Haskoin.Util.Constants where++import Data.Word (Word8,Word32)++-- | Prefix for base58 PubKey hash address+addrPrefix :: Word8+addrPrefix = 0++-- | Prefix for base58 script hash address+scriptPrefix :: Word8+scriptPrefix = 5++-- | Prefix for private key WIF format+secretPrefix :: Word8+secretPrefix = 128++-- | Prefix for extended public keys (BIP32)+extPubKeyPrefix :: Word32+extPubKeyPrefix = 0x0488b21e++-- | Prefix for extended private keys (BIP32)+extSecretPrefix :: Word32+extSecretPrefix = 0x0488ade4++-- | Network magic bytes+networkMagic :: Word32+networkMagic = 0xf9beb4d9 ++-- | Genesis block header information+genesisHeader :: [Integer]+genesisHeader =+ -- Hash = 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f+ [ 0x01+ , 0x00+ , 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a+ , 1231006505+ , 486604799+ , 2083236893+ ]++-- | Maximum size of a block in bytes+maxBlockSize :: Int+maxBlockSize = 1000000++-- | Wallet database file name+walletFile :: String+walletFile = "walletdb"++-- | User agent of this haskoin package+haskoinUserAgent :: String+haskoinUserAgent = "/haskoin:0.0.2/"+
+ Network/Haskoin/Util/Constants/Testnet.hs view
@@ -0,0 +1,55 @@+{-|+ Declaration of constants used across the testnet bitcoin protocol+-}+module Network.Haskoin.Util.Constants.Testnet where++import Data.Word (Word8,Word32)++-- | Prefix for base58 PubKey hash address+addrPrefix :: Word8+addrPrefix = 111++-- | Prefix for base58 script hash address+scriptPrefix :: Word8+scriptPrefix = 196++-- | Prefix for private key WIF format+secretPrefix :: Word8+secretPrefix = 239++-- | Prefix for extended public keys (BIP32)+extPubKeyPrefix :: Word32+extPubKeyPrefix = 0x043587cf++-- | Prefix for extended private keys (BIP32)+extSecretPrefix :: Word32+extSecretPrefix = 0x04358394++-- | Network magic bytes+networkMagic :: Word32+networkMagic = 0x0b110907 ++-- | Genesis block header information+genesisHeader :: [Integer]+genesisHeader =+ -- Hash = 000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943+ [ 0x01+ , 0x00+ , 0x3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a+ , 1296688602+ , 486604799+ , 414098458+ ]++-- | Maximum size of a block in bytes+maxBlockSize :: Int+maxBlockSize = 1000000++-- | Wallet database file name+walletFile :: String+walletFile = "testwalletdb"++-- | User agent of this haskoin package+haskoinUserAgent :: String+haskoinUserAgent = "/haskoin:0.0.2/"+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ UNLICENSE view
@@ -0,0 +1,24 @@+This is free and unencumbered software released into the public domain.++Anyone is free to copy, modify, publish, use, compile, sell, or+distribute this software, either in source code form or as a compiled+binary, for any purpose, commercial or non-commercial, and by any+means.++In jurisdictions that recognize copyright laws, the author or authors+of this software dedicate any and all copyright interest in the+software to the public domain. We make this dedication for the benefit+of the public at large and to the detriment of our heirs and+successors. We intend this dedication to be an overt act of+relinquishment in perpetuity of all present and future rights to this+software under copyright law.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.++For more information, please refer to <http://unlicense.org/>
+ haskoin.cabal view
@@ -0,0 +1,149 @@+name: haskoin+version: 0.0.2+synopsis: Implementation of the Bitcoin protocol.+description:+ Haskoin is a package providing an implementation of the Bitcoin protocol+ specifications. It provides the cryptographic ECDSA and hashing primitives,+ functions for handling BIP32 extended keys, functions for building and+ signing various types of regular and multisig transactions and a definition+ of the network protocol messages.+homepage: http://github.com/plaprade/haskoin+bug-reports: http://github.com/plaprade/haskoin/issues+stability: experimental+license: PublicDomain+license-file: UNLICENSE+author: Philippe Laprade, Jean-Pierre Rupp+maintainer: plaprade+hackage@gmail.com+category: Bitcoin, Finance, Network+build-type: Simple+cabal-version: >= 1.9.2+extra-source-files: tests/data/*.json++source-repository head+ type: git+ location: git://github.com/plaprade/haskoin.git+++library+ exposed-modules: Network.Haskoin.Util,+ Network.Haskoin.Stratum,+ Network.Haskoin.Crypto,+ Network.Haskoin.Protocol,+ Network.Haskoin.Script,+ Network.Haskoin.Transaction+ other-modules: Network.Haskoin.Util.BuildMonad,+ Network.Haskoin.Util.Constants,+ Network.Haskoin.Util.Constants.Testnet,+ Network.Haskoin.Crypto.NumberTheory, + Network.Haskoin.Crypto.Curve, + Network.Haskoin.Crypto.Hash, + Network.Haskoin.Crypto.BigWord,+ Network.Haskoin.Crypto.Point,+ Network.Haskoin.Crypto.Base58,+ Network.Haskoin.Crypto.Keys,+ Network.Haskoin.Crypto.ExtendedKeys,+ Network.Haskoin.Crypto.NormalizedKeys,+ Network.Haskoin.Crypto.ECDSA,+ Network.Haskoin.Crypto.Mnemonic,+ Network.Haskoin.Crypto.Merkle,+ Network.Haskoin.Crypto.Bloom,+ Network.Haskoin.Protocol.Message,+ Network.Haskoin.Protocol.VarInt,+ Network.Haskoin.Protocol.BlockHeader,+ Network.Haskoin.Protocol.Addr, + Network.Haskoin.Protocol.Tx, + Network.Haskoin.Protocol.NotFound, + Network.Haskoin.Protocol.NetworkAddress, + Network.Haskoin.Protocol.Inv, + Network.Haskoin.Protocol.VarString, + Network.Haskoin.Protocol.GetBlocks, + Network.Haskoin.Protocol.Ping, + Network.Haskoin.Protocol.Script, + Network.Haskoin.Protocol.InvVector, + Network.Haskoin.Protocol.Alert, + Network.Haskoin.Protocol.MessageHeader, + Network.Haskoin.Protocol.Block, + Network.Haskoin.Protocol.MerkleBlock, + Network.Haskoin.Protocol.BloomFilter, + Network.Haskoin.Protocol.Version, + Network.Haskoin.Protocol.GetData, + Network.Haskoin.Protocol.Headers, + Network.Haskoin.Protocol.GetHeaders,+ Network.Haskoin.Script.Parser, + Network.Haskoin.Script.SigHash,+ Network.Haskoin.Stratum.Message,+ Network.Haskoin.Stratum.Conduit,+ Network.Haskoin.Transaction.Builder+ build-depends: aeson >= 0.7 && < 0.8,+ base >= 4.6 && < 4.7, + binary >= 0.7 && < 0.8, + byteable >= 0.1 && < 0.2,+ bytestring >= 0.10 && < 0.11, + conduit >= 1.1 && < 1.2,+ conduit-extra >= 1.1 && < 1.2,+ containers >= 0.5 && < 0.6,+ cryptohash >= 0.11 && < 0.12,+ either >= 4.0 && < 4.1,+ mtl >= 2.1 && < 2.2, + pbkdf >= 1.1 && < 1.2,+ split >= 0.2 && < 0.3,+ text >= 0.11 && < 0.12,+ text-icu >= 0.6 && < 0.7,+ vector >= 0.10 && < 0.11+ ghc-options: -Wall -fno-warn-orphans++test-suite test-haskoin+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules: Network.Haskoin.Util.Tests,+ Network.Haskoin.Util.Arbitrary,+ Network.Haskoin.Crypto.BigWord.Tests,+ Network.Haskoin.Crypto.Point.Tests,+ Network.Haskoin.Crypto.ECDSA.Tests,+ Network.Haskoin.Crypto.Base58.Tests,+ Network.Haskoin.Crypto.Keys.Tests,+ Network.Haskoin.Crypto.ExtendedKeys.Tests,+ Network.Haskoin.Crypto.ExtendedKeys.Units,+ Network.Haskoin.Crypto.Hash.Tests,+ Network.Haskoin.Crypto.Hash.Units,+ Network.Haskoin.Crypto.Mnemonic.Tests,+ Network.Haskoin.Crypto.Mnemonic.Units,+ Network.Haskoin.Crypto.Bloom.Tests,+ Network.Haskoin.Crypto.Bloom.Units,+ Network.Haskoin.Crypto.Merkle.Tests,+ Network.Haskoin.Crypto.Merkle.Units,+ Network.Haskoin.Crypto.Arbitrary,+ Network.Haskoin.Crypto.Units,+ Network.Haskoin.Protocol.Arbitrary,+ Network.Haskoin.Protocol.Tests,+ Network.Haskoin.Protocol.Units,+ Network.Haskoin.Script.Arbitrary,+ Network.Haskoin.Script.Tests,+ Network.Haskoin.Script.Units,+ Network.Haskoin.Transaction.Tests,+ Network.Haskoin.Transaction.Units,+ Network.Haskoin.Transaction.Arbitrary,+ Network.Haskoin.Stratum.Units+ build-depends: aeson >= 0.7 && < 0.8,+ base >= 4.6 && < 4.7, + binary >= 0.7 && < 0.8, + byteable >= 0.1 && < 0.2,+ bytestring >= 0.10 && < 0.11, + conduit >= 1.1 && < 1.2,+ conduit-extra >= 1.1 && < 1.2,+ containers >= 0.5 && < 0.6,+ cryptohash >= 0.11 && < 0.12,+ either >= 4.0 && < 4.1,+ mtl >= 2.1 && < 2.2, + pbkdf >= 1.1 && < 1.2,+ split >= 0.2 && < 0.3,+ text >= 0.11 && < 0.12,+ text-icu >= 0.6 && < 0.7,+ HUnit >= 1.2 && < 1.3,+ QuickCheck >= 2.6 && < 2.7,+ test-framework >= 0.8 && < 0.9, + test-framework-quickcheck2 >= 0.3 && < 0.4, + test-framework-hunit >= 0.3 && < 0.4 + hs-source-dirs: . tests+ ghc-options: -Wall -fno-warn-orphans+
+ tests/Main.hs view
@@ -0,0 +1,68 @@+module Main where++import Test.Framework (defaultMain)++-- Util tests+import qualified Network.Haskoin.Util.Tests (tests)++-- Crypto tests+import qualified Network.Haskoin.Crypto.BigWord.Tests (tests)+import qualified Network.Haskoin.Crypto.Point.Tests (tests)+import qualified Network.Haskoin.Crypto.ECDSA.Tests (tests)+import qualified Network.Haskoin.Crypto.Base58.Tests (tests)+import qualified Network.Haskoin.Crypto.Keys.Tests (tests)+import qualified Network.Haskoin.Crypto.ExtendedKeys.Tests (tests)+import qualified Network.Haskoin.Crypto.ExtendedKeys.Units (tests)+import qualified Network.Haskoin.Crypto.Hash.Tests (tests)+import qualified Network.Haskoin.Crypto.Hash.Units (tests)+import qualified Network.Haskoin.Crypto.Mnemonic.Tests (tests)+import qualified Network.Haskoin.Crypto.Mnemonic.Units (tests)+import qualified Network.Haskoin.Crypto.Bloom.Tests (tests)+import qualified Network.Haskoin.Crypto.Bloom.Units (tests)+import qualified Network.Haskoin.Crypto.Merkle.Tests (tests)+import qualified Network.Haskoin.Crypto.Merkle.Units (tests)+import qualified Network.Haskoin.Crypto.Units (tests)++-- Protocol tests+import qualified Network.Haskoin.Protocol.Tests (tests)+import qualified Network.Haskoin.Protocol.Units (tests)++-- Script tests+import qualified Network.Haskoin.Script.Tests (tests)+import qualified Network.Haskoin.Script.Units (tests)++-- Transaction tests+import qualified Network.Haskoin.Transaction.Tests (tests)+import qualified Network.Haskoin.Transaction.Units (tests)++-- Stratum tests+import qualified Network.Haskoin.Stratum.Units (tests)++main :: IO ()+main = defaultMain+ ( Network.Haskoin.Util.Tests.tests+ ++ Network.Haskoin.Crypto.BigWord.Tests.tests + ++ Network.Haskoin.Crypto.Point.Tests.tests + ++ Network.Haskoin.Crypto.ECDSA.Tests.tests + ++ Network.Haskoin.Crypto.Base58.Tests.tests + ++ Network.Haskoin.Crypto.Hash.Tests.tests + ++ Network.Haskoin.Crypto.Hash.Units.tests+ ++ Network.Haskoin.Crypto.Keys.Tests.tests + ++ Network.Haskoin.Crypto.ExtendedKeys.Tests.tests + ++ Network.Haskoin.Crypto.ExtendedKeys.Units.tests + ++ Network.Haskoin.Crypto.Mnemonic.Tests.tests + ++ Network.Haskoin.Crypto.Mnemonic.Units.tests + ++ Network.Haskoin.Crypto.Bloom.Tests.tests + ++ Network.Haskoin.Crypto.Bloom.Units.tests + ++ Network.Haskoin.Crypto.Merkle.Tests.tests + ++ Network.Haskoin.Crypto.Merkle.Units.tests + ++ Network.Haskoin.Crypto.Units.tests+ ++ Network.Haskoin.Protocol.Tests.tests+ ++ Network.Haskoin.Protocol.Units.tests+ ++ Network.Haskoin.Script.Tests.tests+ ++ Network.Haskoin.Script.Units.tests+ ++ Network.Haskoin.Transaction.Tests.tests+ ++ Network.Haskoin.Transaction.Units.tests+ ++ Network.Haskoin.Stratum.Units.tests+ )+
+ tests/Network/Haskoin/Crypto/Arbitrary.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE EmptyDataDecls #-}+{-| + QuickCheck Arbitrary instances for Haskoin.Crypto types.+-}+module Network.Haskoin.Crypto.Arbitrary +( Test32+, TestPrvKeyC(..)+, TestPrvKeyU(..)+, genPrvKeyC+, genPrvKeyU+) where++import Test.QuickCheck+import Network.Haskoin.Util.Arbitrary()++import Control.Applicative ((<$>), (<*>))++import Data.Maybe++import Network.Haskoin.Crypto.Point+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Crypto.ExtendedKeys+import Network.Haskoin.Crypto.NormalizedKeys++data Mod32+type Test32 = BigWord Mod32++instance BigWordMod Mod32 where+ rFromInteger i = BigWord $ i `mod` 2 ^ (32 :: Integer)+ rBitSize _ = 32++instance BigWordMod n => Arbitrary (BigWord n) where+ arbitrary = arbitrarySizedBoundedIntegral++instance Arbitrary CheckSum32 where+ arbitrary = chksum32 <$> arbitrary++instance Arbitrary Point where+ arbitrary = frequency+ [ (1, return makeInfPoint)+ , (9, (flip mulPoint $ curveG) <$> (arbitrary :: Gen FieldN))+ ]++-- | Generate an arbitrary compressed private key+genPrvKeyC :: Gen PrvKey+genPrvKeyC = do+ i <- fromInteger <$> choose (1, curveN-1)+ return $ fromJust $ makePrvKey i++-- | Generate an arbitrary uncompressed private key+genPrvKeyU :: Gen PrvKey+genPrvKeyU = do+ i <- fromInteger <$> choose (1, curveN-1)+ return $ fromJust $ makePrvKeyU i++newtype TestPrvKeyC = TestPrvKeyC { getTestPrvKeyC :: PrvKey }+ deriving (Eq, Show)++newtype TestPrvKeyU = TestPrvKeyU { getTestPrvKeyU :: PrvKey }+ deriving (Eq, Show)++instance Arbitrary TestPrvKeyC where+ arbitrary = TestPrvKeyC <$> genPrvKeyC++instance Arbitrary TestPrvKeyU where+ arbitrary = TestPrvKeyU <$> genPrvKeyU++instance Arbitrary PrvKey where+ arbitrary = oneof [genPrvKeyC, genPrvKeyU]++instance Arbitrary PubKey where+ arbitrary = derivePubKey <$> arbitrary++instance Arbitrary Address where+ arbitrary = do+ i <- fromInteger <$> choose (1,2^(160-1 :: Int))+ elements [ PubKeyAddress i+ , ScriptAddress i+ ]++instance Arbitrary Signature where+ arbitrary = do+ msg <- arbitrary+ prv <- prvKeyFieldN <$> arbitrary+ non <- prvKeyFieldN <$> arbitrary+ let pub = mulPoint non curveG+ case unsafeSignMsg msg prv (non,pub) of+ (Just sig) -> return sig+ Nothing -> arbitrary ++instance Arbitrary XPrvKey where+ arbitrary = XPrvKey <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> genPrvKeyC++instance Arbitrary XPubKey where+ arbitrary = deriveXPubKey <$> arbitrary++instance Arbitrary MasterKey where+ arbitrary = fromJust . makeMasterKey <$> arbitrary++instance Arbitrary AccPrvKey where+ arbitrary = do+ master <- arbitrary+ index <- choose (0,0x7fffffff)+ return $ fromJust $ accPrvKey master index++instance Arbitrary AccPubKey where+ arbitrary = do+ master <- arbitrary+ index <- choose (0,0x7fffffff)+ return $ fromJust $ accPubKey master index++instance Arbitrary AddrPrvKey where+ arbitrary = do+ accKey <- arbitrary+ index <- arbitrary+ elements [ fromJust $ extPrvKey accKey index+ , fromJust $ intPrvKey accKey index+ ]++instance Arbitrary AddrPubKey where+ arbitrary = do+ accKey <- arbitrary+ index <- arbitrary+ elements [ fromJust $ extPubKey accKey index+ , fromJust $ intPubKey accKey index+ ]+
+ tests/Network/Haskoin/Crypto/Base58/Tests.hs view
@@ -0,0 +1,32 @@+module Network.Haskoin.Crypto.Base58.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Maybe (fromJust)+import qualified Data.ByteString as BS (ByteString)++import Network.Haskoin.Crypto.Arbitrary()+import Network.Haskoin.Util.Arbitrary()++import Network.Haskoin.Crypto.Base58++tests :: [Test]+tests = + [ testGroup "Address and Base58"+ [ testProperty "decode58( encode58(i) ) = i" decodeEncode58+ , testProperty "decode58Chk( encode58Chk(i) ) = i" decodeEncode58Check+ , testProperty "decode58( encode58(address) ) = address" decEncAddr+ ]+ ]++decodeEncode58 :: BS.ByteString -> Bool+decodeEncode58 bs = (fromJust $ decodeBase58 $ encodeBase58 bs) == bs++decodeEncode58Check :: BS.ByteString -> Bool+decodeEncode58Check bs = + (fromJust $ decodeBase58Check $ encodeBase58Check bs) == bs++decEncAddr :: Address -> Bool+decEncAddr a = (fromJust $ base58ToAddr $ addrToBase58 a) == a+
+ tests/Network/Haskoin/Crypto/BigWord/Tests.hs view
@@ -0,0 +1,349 @@+module Network.Haskoin.Crypto.BigWord.Tests (tests) where++import Test.QuickCheck.Property (Property, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Bits + ( isSigned+ , bit+ , testBit+ , shift+ , bitSize+ , popCount+ , (.&.), (.|.)+ , xor, complement+ )+import Data.Word (Word8, Word32)+import qualified Data.ByteString as BS (length, index)++import Network.Haskoin.Crypto.Arbitrary +import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.NumberTheory+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "Number Theory" + [ testProperty "a * inv(a) = 1 (mod p)" inverseMod+ , testProperty "a * inv(a) = 1 (mod p) in FieldP" inverseModP+ , testProperty "a * inv(a) = 1 (mod n) in FieldN" inverseModN+ , testProperty "sqrt( a^2 ) = a (mod p)" sqrtP+ ],+ testGroup "BigWord Numeric"+ [ testProperty "BigWord fromInteger" ringFromInteger+ , testProperty "BigWord addition" ringAddition+ , testProperty "BigWord multiplication" ringMult+ , testProperty "BigWord negation" ringNegate+ , testProperty "BigWord abs" ringAbs+ , testProperty "BigWord signum" ringSignum+ ],+ testGroup "BigWord Bits"+ [ testProperty "BigWord AND" ringAnd+ , testProperty "BigWord OR" ringOr+ , testProperty "BigWord XOR" ringXor+ , testProperty "BigWord Complement" ringComplement+ , testProperty "BigWord Shift" ringShift+ , testProperty "BigWord Bitsize" ringBitsize+ , testProperty "BigWord Testbit" ringTestbit+ , testProperty "BigWord Bit" ringBit+ , testProperty "BigWord PopCount" ringPopCount+ , testProperty "BigWord IsSigned" ringIsSigned+ ],+ testGroup "BigWord Bounded"+ [ testProperty "BigWord minBound" ringMinBound+ , testProperty "BigWord maxBound" ringMaxBound+ ],+ testGroup "BigWord Enum"+ [ testProperty "BigWord succ" ringSucc+ , testProperty "BigWord pred" ringPred+ , testProperty "BigWord toEnum" ringToEnum+ , testProperty "BigWord fromEnum" ringFromEnum+ ],+ testGroup "BigWord Integral"+ [ testProperty "BigWord Quot" ringQuot+ , testProperty "BigWord Rem" ringRem+ , testProperty "BigWord Div" ringDiv+ , testProperty "BigWord Mod" ringMod+ , testProperty "BigWord QuotRem" ringQuotRem+ , testProperty "BigWord DivMod" ringDivMod+ , testProperty "BigWord toInteger" ringToInteger+ ],+ testGroup "BigWord Binary"+ [ testProperty "get( put(Word512) ) = Word512" getPutWord512+ , testProperty "get( put(Word256) ) = Word256" getPutWord256+ , testProperty "get( put(Word160) ) = Word160" getPutWord160+ , testProperty "get( put(Word128) ) = Word128" getPutWord128+ , testProperty "get( put(FieldP) ) = FieldP" getPutModP+ , testProperty "size( put(FieldP) ) = 32" putModPSize+ , testProperty "get( put(FieldN) ) = FieldN" getPutModN+ , testProperty "Verify DER of put(FieldN)" putModNSize+ ],+ testGroup "BigWord Read Show"+ [ testProperty "read( show(Word512) ) = Word512" readShowWord512+ , testProperty "read( show(Word256) ) = Word256" readShowWord256+ , testProperty "read( show(Word160) ) = Word160" readShowWord160+ , testProperty "read( show(Word128) ) = Word128" readShowWord128+ , testProperty "read( show(FieldP) ) = FieldP" readShowModP+ , testProperty "read( show(FieldN) ) = FieldN" readShowModN+ ]+ ]++{- Number Theory -}++inverseMod :: Integer -> Property+inverseMod i = p > 0 ==> (p * (mulInverse p curveP)) `mod` curveP == 1+ where + p = abs i++inverseModP :: FieldP -> Property+inverseModP r = r > 0 ==> r/r == 1++inverseModN :: FieldN -> Property+inverseModN r = r > 0 ==> r/r == 1++sqrtP :: FieldP -> Bool+sqrtP x = (a == x && b == (-x)) || (a == (-x) && b == x)+ where + (a:b:_) = quadraticResidue (x^(2 :: Int))++{- BigWord Numeric -}++ringFromInteger :: Integer -> Bool+ringFromInteger i = getBigWordInteger ring == fromIntegral model+ where + model = fromInteger i :: Word32+ ring = fromInteger i :: Test32++ringAddition :: Integer -> Integer -> Bool+ringAddition i1 i2 = getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) + (fromInteger i2) :: Word32+ ring = (fromInteger i1) + (fromInteger i2) :: Test32++ringMult :: Integer -> Integer -> Bool+ringMult i1 i2 = getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) * (fromInteger i2) :: Word32+ ring = (fromInteger i1) * (fromInteger i2) :: Test32++ringNegate :: Integer -> Bool+ringNegate i = getBigWordInteger ring == fromIntegral model+ where + model = negate (fromInteger i) :: Word32+ ring = negate (fromInteger i) :: Test32++ringAbs :: Integer -> Bool+ringAbs i = getBigWordInteger ring == fromIntegral model+ where + model = abs (fromInteger i) :: Word32+ ring = abs (fromInteger i) :: Test32++ringSignum :: Integer -> Bool+ringSignum i = getBigWordInteger ring == fromIntegral model+ where + model = signum (fromInteger i) :: Word32+ ring = signum (fromInteger i) :: Test32++{- BigWord Bits -}++ringAnd :: Integer -> Integer -> Bool+ringAnd i1 i2 = getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) .&. (fromInteger i2) :: Word32+ ring = (fromInteger i1) .&. (fromInteger i2) :: Test32++ringOr :: Integer -> Integer -> Bool+ringOr i1 i2 = getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) .|. (fromInteger i2) :: Word32+ ring = (fromInteger i1) .|. (fromInteger i2) :: Test32+ +ringXor :: Integer -> Integer -> Bool+ringXor i1 i2 = getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) `xor` (fromInteger i2) :: Word32+ ring = (fromInteger i1) `xor` (fromInteger i2) :: Test32++ringComplement :: Integer -> Bool+ringComplement i = getBigWordInteger ring == fromIntegral model+ where + model = complement (fromInteger i) :: Word32+ ring = complement (fromInteger i) :: Test32++ringShift :: Integer -> Word8 -> Bool+ringShift i j = getBigWordInteger ring == fromIntegral model+ where + model = shift (fromInteger i) (fromIntegral j) :: Word32+ ring = shift (fromInteger i) (fromIntegral j) :: Test32++ringBitsize :: Integer -> Bool+ringBitsize i = ring == model+ where + model = bitSize ((fromInteger i) :: Word32) + ring = bitSize ((fromInteger i) :: Test32) ++ringTestbit :: Integer -> Word8 -> Bool+ringTestbit i j = ring == model+ where + model = testBit ((fromInteger i) :: Word32) (fromIntegral j)+ ring = testBit ((fromInteger i) :: Test32) (fromIntegral j)++ringBit :: Word8 -> Bool+ringBit i = getBigWordInteger ring == fromIntegral model+ where + model = bit (fromIntegral i) :: Word32+ ring = bit (fromIntegral i) :: Test32++ringPopCount :: Integer -> Bool+ringPopCount i = ring == model+ where + model = popCount ((fromInteger i) :: Word32)+ ring = popCount ((fromInteger i) :: Test32)++ringIsSigned :: Integer -> Bool+ringIsSigned i = ring == model+ where + model = isSigned ((fromInteger i) :: Word32)+ ring = isSigned ((fromInteger i) :: Test32)++{- BigWord Bounded -}++ringMinBound :: Test32 -> Bool+ringMinBound _ = (minBound :: Test32) - 1 == (maxBound :: Test32)++ringMaxBound :: Test32 -> Bool+ringMaxBound _ = (maxBound :: Test32) + 1 == (minBound :: Test32)++{- BigWord Enum -}++ringSucc :: Integer -> Property+ringSucc i = (fromIntegral i) /= maxB ==> + getBigWordInteger ring == fromIntegral model+ where + model = succ (fromInteger i) :: Word32+ ring = succ (fromInteger i) :: Test32+ maxB = maxBound :: Word32++ringPred :: Integer -> Property+ringPred i = (fromIntegral i) /= minB ==> + getBigWordInteger ring == fromIntegral model+ where + model = pred (fromInteger i) :: Word32+ ring = pred (fromInteger i) :: Test32+ minB = minBound :: Word32++ringToEnum :: Word32 -> Bool+ringToEnum w = getBigWordInteger ring == fromIntegral model+ where + model = toEnum (fromIntegral w) :: Word32+ ring = toEnum (fromIntegral w) :: Test32++ringFromEnum :: Integer -> Bool+ringFromEnum i = model == ring+ where + model = fromEnum ((fromInteger i) :: Word32)+ ring = fromEnum ((fromInteger i) :: Test32)++{- BigWord Integral -}++ringQuot :: Integer -> Integer -> Property+ringQuot i1 i2 = i2 /= 0 ==> getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) `quot` (fromInteger i2) :: Word32+ ring = (fromInteger i1) `quot` (fromInteger i2) :: Test32++ringRem :: Integer -> Integer -> Property+ringRem i1 i2 = i2 /= 0 ==> getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) `rem` (fromInteger i2) :: Word32+ ring = (fromInteger i1) `rem` (fromInteger i2) :: Test32++ringDiv :: Integer -> Integer -> Property+ringDiv i1 i2 = i2 /= 0 ==> getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) `div` (fromInteger i2) :: Word32+ ring = (fromInteger i1) `div` (fromInteger i2) :: Test32++ringMod :: Integer -> Integer -> Property+ringMod i1 i2 = i2 /= 0 ==> getBigWordInteger ring == fromIntegral model+ where + model = (fromInteger i1) `mod` (fromInteger i2) :: Word32+ ring = (fromInteger i1) `mod` (fromInteger i2) :: Test32++ringQuotRem :: Integer -> Integer -> Property+ringQuotRem i1 i2 = i2 /= 0 ==> + (getBigWordInteger r1 == fromIntegral m1) && + (getBigWordInteger r2 == fromIntegral m2)+ where + (m1,m2) = (fromInteger i1) `quotRem` (fromInteger i2) :: (Word32, Word32)+ (r1,r2) = (fromInteger i1) `quotRem` (fromInteger i2) :: (Test32, Test32)++ringDivMod :: Integer -> Integer -> Property+ringDivMod i1 i2 = i2 /= 0 ==> + (getBigWordInteger r1 == fromIntegral m1) && + (getBigWordInteger r2 == fromIntegral m2)+ where + (m1,m2) = (fromInteger i1) `divMod` (fromInteger i2) :: (Word32, Word32)+ (r1,r2) = (fromInteger i1) `divMod` (fromInteger i2) :: (Test32, Test32)++ringToInteger :: Test32 -> Bool+ringToInteger r@(BigWord i) = toInteger r == i++{- BigWord Binary -}++getPutWord512 :: Word512 -> Bool+getPutWord512 r = r == (decode' $ encode' r)++getPutWord256 :: Word256 -> Bool+getPutWord256 r = r == (decode' $ encode' r)++getPutWord160 :: Word160 -> Bool+getPutWord160 r = r == (decode' $ encode' r)++getPutWord128 :: Word128 -> Bool+getPutWord128 r = r == (decode' $ encode' r)++getPutModP :: FieldP -> Bool+getPutModP r = r == (decode' $ encode' r)++putModPSize :: FieldP -> Bool+putModPSize r = BS.length (encode' r) == 32++getPutModN :: FieldN -> Property+getPutModN r = r > 0 ==> r == (decode' $ encode' r)++putModNSize :: FieldN -> Property+putModNSize r = r > 0 ==>+ ( a == 0x02 -- DER type is Integer+ && b <= 33 -- Can't be bigger than 32 + 0x00 padding+ && l == fromIntegral (b + 2) -- Advertised length matches+ && c < 0x80 -- High byte is never 1+ )+ where + bs = encode' r+ a = BS.index bs 0+ b = BS.index bs 1+ c = BS.index bs 2+ l = BS.length bs++{- BigWord Read Show -}++readShowWord512 :: Word512 -> Bool+readShowWord512 r = r == (read $ show r)++readShowWord256 :: Word256 -> Bool+readShowWord256 r = r == (read $ show r)++readShowWord160 :: Word160 -> Bool+readShowWord160 r = r == (read $ show r)++readShowWord128 :: Word128 -> Bool+readShowWord128 r = r == (read $ show r)++readShowModP :: FieldP -> Bool+readShowModP r = r == (read $ show r)++readShowModN :: FieldN -> Property+readShowModN r = r > 0 ==> r == (read $ show r)
+ tests/Network/Haskoin/Crypto/Bloom/Tests.hs view
@@ -0,0 +1,22 @@+module Network.Haskoin.Crypto.Bloom.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Network.Haskoin.Protocol.Arbitrary ()+import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "Bloom Filters"+ [ testProperty "decode . encode BloomFilter" decodeEncodeBloom+ ]+ ]++{- Bloom Filters -}++decodeEncodeBloom :: BloomFilter -> Bool+decodeEncodeBloom bfilter = (decode' $ encode' bfilter) == bfilter++
+ tests/Network/Haskoin/Crypto/Bloom/Units.hs view
@@ -0,0 +1,71 @@+module Network.Haskoin.Crypto.Bloom.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust)++import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ + -- Test cases come from bitcoind /src/test/bloom_tests.cpp+ testGroup "Bloom Filters"+ [ testCase "Bloom Filter Vector 1" bloomFilter1+ , testCase "Bloom Filter Vector 2" bloomFilter2+ , testCase "Bloom Filter Vector 3" bloomFilter3+ ]+ ]++bloomFilter1 :: Assertion+bloomFilter1 = 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" $ (encode' f4) == bs+ where+ f0 = bloomCreate 3 0.01 0 BloomUpdateAll+ f1 = bloomInsert f0 v1+ f3 = bloomInsert f1 v3+ f4 = bloomInsert f3 v4+ v1 = fromJust $ hexToBS "99108ad8ed9bb6274d3980bab5a85c048f0950c8"+ v2 = fromJust $ hexToBS "19108ad8ed9bb6274d3980bab5a85c048f0950c8"+ v3 = fromJust $ hexToBS "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"+ v4 = fromJust $ hexToBS "b9300670b4c5366e95b2699e8b18bc75e5f729c5"+ bs = fromJust $ hexToBS "03614e9b050000000000000001"++bloomFilter2 :: Assertion+bloomFilter2 = 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" $ (encode' f4) == bs+ where+ f0 = bloomCreate 3 0.01 2147483649 BloomUpdateAll+ f1 = bloomInsert f0 v1+ f3 = bloomInsert f1 v3+ f4 = bloomInsert f3 v4+ v1 = fromJust $ hexToBS "99108ad8ed9bb6274d3980bab5a85c048f0950c8"+ v2 = fromJust $ hexToBS "19108ad8ed9bb6274d3980bab5a85c048f0950c8"+ v3 = fromJust $ hexToBS "b5a2c786d9ef4658287ced5914b37a1b4aa32eee"+ v4 = fromJust $ hexToBS "b9300670b4c5366e95b2699e8b18bc75e5f729c5"+ bs = fromJust $ hexToBS "03ce4299050000000100008001"++bloomFilter3 :: Assertion+bloomFilter3 = do+ assertBool "Bloom filter serialization is incorrect" $ (encode' f2) == bs+ where+ f0 = bloomCreate 2 0.001 0 BloomUpdateAll+ f1 = bloomInsert f0 $ encode' p+ f2 = bloomInsert f1 $ encode' $ getAddrHash $ pubKeyAddr p+ k = fromJust $ fromWIF "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"+ p = derivePubKey k+ bs = fromJust $ hexToBS "038fc16b080000000000000001"+
+ tests/Network/Haskoin/Crypto/ECDSA/Tests.hs view
@@ -0,0 +1,97 @@+module Network.Haskoin.Crypto.ECDSA.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+import Test.QuickCheck.Property ((==>), Property)++import Data.Bits (testBit)+import qualified Data.ByteString as BS++import Network.Haskoin.Crypto.Arbitrary++import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Crypto.Point+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "ECDSA signatures"+ [ testProperty "verify( sign(msg) ) = True" signAndVerify+ , testProperty "verify( detSign(msg) ) = True" signAndVerifyD+ , testProperty "S component <= order/2" halfOrderSig+ ],+ testGroup "ECDSA Binary"+ [ testProperty "get( put(Sig) ) = Sig" getPutSig+ , testProperty "Encoded signature is canonical" testIsCanonical+ ]+ ]++{- ECDSA Signatures -}++signAndVerify :: Hash256 -> FieldN -> FieldN -> Property+signAndVerify msg k n = k > 0 && n > 0 ==> case sM of+ (Just s) -> verifySig msg s (PubKey kP)+ Nothing -> True -- very bad luck+ where kP = mulPoint k curveG+ nP = mulPoint n curveG+ sM = unsafeSignMsg msg k (n,nP)++signAndVerifyD :: Hash256 -> TestPrvKeyC -> Bool+signAndVerifyD msg (TestPrvKeyC k) = verifySig msg (detSignMsg msg k) p+ where p = derivePubKey k+ +halfOrderSig :: Signature -> Bool+halfOrderSig sig@(Signature _ (BigWord s)) = + s <= (curveN `div` 2) && isCanonicalHalfOrder sig++{- ECDSA Binary -}++getPutSig :: Signature -> Bool+getPutSig sig = (decode' $ encode' sig) == sig++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalSignature+testIsCanonical :: Signature -> 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 = encode' sig+ len = fromIntegral $ BS.length s+ rlen = BS.index s 3+ slen = BS.index s (fromIntegral rlen + 5)+
+ tests/Network/Haskoin/Crypto/ExtendedKeys/Tests.hs view
@@ -0,0 +1,66 @@+module Network.Haskoin.Crypto.ExtendedKeys.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Control.Monad (liftM2)+import Control.Applicative ((<$>))++import Data.Word (Word32)+import Data.Bits ((.&.))+import Data.Maybe (fromJust)++import Network.Haskoin.Crypto+import Network.Haskoin.Crypto.Arbitrary()+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "HDW Extended Keys"+ [ testProperty "prvSubKey(k,c)*G = pubSubKey(k*G,c)" subkeyTest+ , testProperty "decode . encode prvKey" binXPrvKey+ , testProperty "decode . encode pubKey" binXPubKey+ , testProperty "fromB58 . toB58 prvKey" b58PrvKey+ , testProperty "fromB58 . toB58 pubKey" b58PubKey+ ]+ , testGroup "HDW Normalized Keys"+ [ testProperty "decode . encode masterKey" decEncMaster+ , testProperty "decode . encode prvAccKey" decEncPrvAcc+ , testProperty "decode . encode pubAccKey" decEncPubAcc+ ]+ ]++{- HDW Extended Keys -}++subkeyTest :: XPrvKey -> Word32 -> Bool+subkeyTest k i = fromJust $ liftM2 (==) + (deriveXPubKey <$> prvSubKey k i') (pubSubKey (deriveXPubKey k) i')+ where i' = fromIntegral $ i .&. 0x7fffffff -- make it a public derivation++binXPrvKey :: XPrvKey -> Bool+binXPrvKey k = (decode' $ encode' k) == k++binXPubKey :: XPubKey -> Bool+binXPubKey k = (decode' $ encode' k) == k++b58PrvKey :: XPrvKey -> Bool+b58PrvKey k = (fromJust $ xPrvImport $ xPrvExport k) == k++b58PubKey :: XPubKey -> Bool+b58PubKey k = (fromJust $ xPubImport $ xPubExport k) == k++{- HDW Normalized Keys -}++decEncMaster :: MasterKey -> Bool+decEncMaster k = (fromJust $ loadMasterKey $ decode' bs) == k+ where bs = encode' $ masterKey k++decEncPrvAcc :: AccPrvKey -> Bool+decEncPrvAcc k = (fromJust $ loadPrvAcc $ decode' bs) == k+ where bs = encode' $ getAccPrvKey k++decEncPubAcc :: AccPubKey -> Bool+decEncPubAcc k = (fromJust $ loadPubAcc $ decode' bs) == k+ where bs = encode' $ getAccPubKey k++
+ tests/Network/Haskoin/Crypto/ExtendedKeys/Units.hs view
@@ -0,0 +1,250 @@+module Network.Haskoin.Crypto.ExtendedKeys.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust, catMaybes)++import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "BIP32 derivation vector 1" + [ testCase "Chain m" $ runXKeyVec (xKeyVec !! 0)+ , testCase "Chain m/0'" $ runXKeyVec (xKeyVec !! 1)+ , testCase "Chain m/0'/1" $ runXKeyVec (xKeyVec !! 2)+ , testCase "Chain m/0'/1/2'" $ runXKeyVec (xKeyVec !! 3)+ , testCase "Chain m/0'/1/2'/2" $ runXKeyVec (xKeyVec !! 4)+ , testCase "Chain m/0'/1/2'/2/1000000000" $ + runXKeyVec (xKeyVec !! 5)+ ] + , testGroup "BIP32 subkey derivation vector 2" + [ testCase "Chain m" $ runXKeyVec (xKeyVec2 !! 0)+ , testCase "Chain m/0" $ runXKeyVec (xKeyVec2 !! 1)+ , testCase "Chain m/0/2147483647'" $ + runXKeyVec (xKeyVec2 !! 2)+ , testCase "Chain m/0/2147483647'/1" $ + runXKeyVec (xKeyVec2 !! 3)+ , testCase "Chain m/0/2147483647'/1/2147483646'" $ + runXKeyVec (xKeyVec2 !! 4)+ , testCase "Chain m/0/2147483647'/1/2147483646'/2" $ + runXKeyVec (xKeyVec2 !! 5)+ ] + ]+++runXKeyVec :: ([String],XPrvKey) -> Assertion+runXKeyVec (v,m) = do+ assertBool "xPrvID" $ (bsToHex $ encode' $ xPrvID m) == v !! 0+ assertBool "xPrvFP" $ (bsToHex $ encode' $ xPrvFP m) == v !! 1+ assertBool "xPrvAddr" $ + (addrToBase58 $ xPubAddr $ deriveXPubKey m) == v !! 2+ assertBool "prvKey" $ (bsToHex $ runPut' $ putPrvKey $ xPrvKey m) == v !! 3+ assertBool "xPrvWIF" $ xPrvWIF m == v !! 4+ assertBool "pubKey" $ + (bsToHex $ encode' $ xPubKey $ deriveXPubKey m) == v !! 5+ assertBool "chain code" $ (bsToHex $ encode' $ xPrvChain m) == v !! 6+ assertBool "Hex PubKey" $ (bsToHex $ encode' $ deriveXPubKey m) == v !! 7+ assertBool "Hex PrvKey" $ (bsToHex $ encode' m) == v !! 8+ assertBool "Base58 PubKey" $ (xPubExport $ deriveXPubKey m) == v !! 9+ assertBool "Base58 PrvKey" $ xPrvExport m == v !! 10++-- BIP 0032 Test Vectors+-- https://en.bitcoin.it/wiki/BIP_0032_TestVectors++xKeyVec :: [([String],XPrvKey)]+xKeyVec = zip xKeyResVec $ catMaybes $ foldl f [m] der+ where f acc d = acc ++ [d =<< last acc]+ m = makeXPrvKey $ fromJust $ hexToBS m0+ der = [ flip primeSubKey 0+ , flip prvSubKey 1+ , flip primeSubKey 2+ , flip prvSubKey 2+ , flip prvSubKey 1000000000+ ]++xKeyVec2 :: [([String],XPrvKey)]+xKeyVec2 = zip xKeyResVec2 $ catMaybes $ foldl f [m] der+ where f acc d = acc ++ [d =<< last acc]+ m = makeXPrvKey $ fromJust $ hexToBS m1+ der = [ flip prvSubKey 0+ , flip primeSubKey 2147483647+ , flip prvSubKey 1+ , flip primeSubKey 2147483646+ , flip prvSubKey 2+ ]++m0 :: String+m0 = "000102030405060708090a0b0c0d0e0f"++xKeyResVec :: [[String]]+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 :: String+m1 = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"++xKeyResVec2 :: [[String]]+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"+ ]+ ]+
+ tests/Network/Haskoin/Crypto/Hash/Tests.hs view
@@ -0,0 +1,30 @@+module Network.Haskoin.Crypto.Hash.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.Arbitrary()++tests :: [Test]+tests = + [ testGroup "Hash tests" + [ testProperty "join512( split512(h) ) == h" joinSplit512+ , testProperty "decodeCompact . encodeCompact i == i" decEncCompact+ ]+ ]++joinSplit512 :: Hash512 -> Bool+joinSplit512 h = (join512 $ split512 h) == h++-- 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+ -- Otherwise precision will be lost and the decoded result will+ -- be smaller than the original number+ | i >= 0 = (decodeCompact $ encodeCompact i) < i+ | otherwise = (decodeCompact $ encodeCompact i) > i+
+ tests/Network/Haskoin/Crypto/Hash/Units.hs view
@@ -0,0 +1,1300 @@+module Network.Haskoin.Crypto.Hash.Units (tests) where++import Test.HUnit (assertBool, Assertion)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust)+import Data.Word (Word32)++import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Util++-- 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++tests :: [Test]+tests =+ [ testGroup "HMAC DRBG Suite 1" [mapDRBG t1]+ , testGroup "HMAC DRBG Suite 2" [mapDRBG t2]+ , testGroup "HMAC DRBG Suite 3" [mapDRBG t3]+ , testGroup "HMAC DRBG Suite 4" [mapDRBG t4]+ , testGroup "HMAC DRBG Suite 5 (Reseed)" [mapDRBGRsd r1] + , testGroup "HMAC DRBG Suite 6 (Reseed)" [mapDRBGRsd r2]+ , testGroup "HMAC DRBG Suite 7 (Reseed)" [mapDRBGRsd r3] + , testGroup "HMAC DRBG Suite 8 (Reseed)" [mapDRBGRsd r4] + , testGroup "Murmur Hash 3" + (map mapMurmurVector $ zip murmurVectors [0..])+ , testGroup "Compact number representation"+ [ testCase "Compact number representations" testCompact ]+ ]++type TestVector = [String]++mapDRBG :: [TestVector] -> Test.Framework.Test+mapDRBG vs = testCase "HMAC DRBG Vectors" $ mapM_ testDRBG $ zip vs [0..]++mapDRBGRsd :: [TestVector] -> Test.Framework.Test+mapDRBGRsd vs = testCase "HMAC DRBG Vectors" $ mapM_ testDRBGRsd $ zip vs [0..]++testDRBG :: (TestVector,Int) -> Assertion+testDRBG (s,i) = do+ let w1 = hmacDRBGNew (v !! 0) (v !! 1) (v !! 2)+ (w2,_) = hmacDRBGGen w1 128 (v !! 3)+ (_,r) = hmacDRBGGen w2 128 (v !! 4)+ assertBool name $ fromJust r == (v !! 5)+ where v = map (fromJust . hexToBS) s+ name = " > HMAC DRBG Vector " ++ (show i)++testDRBGRsd :: (TestVector,Int) -> Assertion+testDRBGRsd (s,i) = do+ let w1 = hmacDRBGNew (v !! 0) (v !! 1) (v !! 2)+ w2 = hmacDRBGRsd w1 (v !! 3) (v !! 4)+ (w3,_) = hmacDRBGGen w2 128 (v !! 5)+ (_,r) = hmacDRBGGen w3 128 (v !! 6)+ assertBool name $ fromJust r == (v !! 7)+ where v = map (fromJust . hexToBS) s+ name = " > HMAC DRBG Vector " ++ (show i)++mapMurmurVector :: ((Word32,Word32,String),Int) -> Test.Framework.Test+mapMurmurVector (_,i) =+ testCase ("MurmurHash3 Vector " ++ (show i)) func+ where+ func = testMurmurVector $ murmurVectors !! i++testMurmurVector :: (Word32,Word32,String) -> Assertion+testMurmurVector (expected,seed,str) =+ assertBool " > MurmurHash3 " $ hash == expected+ where+ hash = murmurHash3 seed (fromJust $ hexToBS str)++murmurVectors :: [(Word32,Word32,String)]+murmurVectors =+ [ (0x00000000, 0x00000000, "")+ , (0x6a396f08, 0xFBA4C795, "")+ , (0x81f16f39, 0xffffffff, "")+ , (0x514e28b7, 0x00000000, "00")+ , (0xea3f0b17, 0xFBA4C795, "00")+ , (0xfd6cf10d, 0x00000000, "ff")+ , (0x16c6b7ab, 0x00000000, "0011")+ , (0x8eb51c3d, 0x00000000, "001122")+ , (0xb4471bf8, 0x00000000, "00112233")+ , (0xe2301fa8, 0x00000000, "0011223344")+ , (0xfc2e4a15, 0x00000000, "001122334455")+ , (0xb074502c, 0x00000000, "00112233445566")+ , (0x8034d2a0, 0x00000000, "0011223344556677")+ , (0xb4698def, 0x00000000, "001122334455667788")+ ]++testCompact :: Assertion+testCompact = do+ assertBool "Vector 1" $ (encodeCompact 0x1234560000) == 0x05123456+ assertBool "Vector 2" $ (decodeCompact 0x05123456) == 0x1234560000+ assertBool "Vector 3" $ (encodeCompact 0xc0de000000) == 0x0600c0de+ assertBool "Vector 4" $ (decodeCompact 0x0600c0de) == 0xc0de000000+ assertBool "Vector 5" $ (encodeCompact (-0x40de000000)) == 0x05c0de00+ assertBool "Vector 6" $ (decodeCompact 0x05c0de00) == (-0x40de000000)++{- + [SHA-256]+ [PredictionResistance = False]+ [EntropyInputLen = 256]+ [NonceLen = 128]+ [PersonalizationStringLen = 0]+ [AdditionalInputLen = 0]+ [ReturnedBitsLen = 1024]+-}++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"+ ]+ ]+
+ tests/Network/Haskoin/Crypto/Keys/Tests.hs view
@@ -0,0 +1,133 @@+module Network.Haskoin.Crypto.Keys.Tests (tests) where++import Test.QuickCheck.Property (Property, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Maybe (fromJust, isNothing)+import Data.Binary.Get (runGet)+import Data.Binary.Put (runPut)+import qualified Data.ByteString as BS (ByteString, length, index)++import Network.Haskoin.Crypto.Arbitrary++import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.Point+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.Curve+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "PubKey Binary"+ [ testProperty "get( put(PubKey) ) = PubKey" getPutPubKey+ , testProperty "is public key canonical" isCanonicalPubKey+ , testProperty "makeKey( toKey(k) ) = k" makeToKey+ , testProperty "makeKeyU( toKey(k) ) = k" makeToKeyU+ , testProperty "decoded PubKey is always valid" decodePubKey+ ],+ testGroup "Key formats"+ [ testProperty "fromWIF( toWIF(i) ) = i" fromToWIF+ , testProperty "get( put(PrvKey) )" getPutPrv+ ],+ testGroup "Key compression"+ [ testProperty "Compressed public key" testCompressed+ , testProperty "Uncompressed public key" testUnCompressed+ , testProperty "Compressed private key" testPrivateCompressed+ , testProperty "Uncompressed private key" testPrivateUnCompressed+ ],+ testGroup "Public Key"+ [ testProperty "Derived public key valid" testDerivedPubKey+ , testProperty "Derived public key from Integer valid" deriveFromInt+ ],+ testGroup "Key properties"+ [ testProperty "PubKey addition" testAddPubKey+ , testProperty "PrvKey addition" testAddPrvKey+ ]+ ]++{- Public Key Binary -}++getPutPubKey :: PubKey -> Bool+getPutPubKey p = p == (decode' $ encode' p)++-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp+-- from function IsCanonicalPubKey+isCanonicalPubKey :: PubKey -> 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+ (not $ BS.index bs 0 `elem` [2,3,4])+ where bs = encode' p++makeToKey :: FieldN -> Property+makeToKey i = i /= 0 ==> + (fromPrvKey $ makeKey (fromIntegral i)) == (fromIntegral i)+ where makeKey = fromJust . makePrvKey++makeToKeyU :: FieldN -> Property+makeToKeyU i = i /= 0 ==> + (fromPrvKey $ makeKey (fromIntegral i)) == (fromIntegral i)+ where makeKey = fromJust . makePrvKeyU++decodePubKey :: BS.ByteString -> Bool+decodePubKey bs = fromDecode bs True isValidPubKey++{- Key formats -}++fromToWIF :: PrvKey -> Bool+fromToWIF pk = pk == (fromJust $ fromWIF $ toWIF pk)++getPutPrv :: PrvKey -> Bool+getPutPrv k@(PrvKey _) = k == runGet getPrvKey (runPut $ putPrvKey k)+getPutPrv k@(PrvKeyU _) = k == runGet getPrvKeyU (runPut $ putPrvKey k)++{- Key Compression -}++testCompressed :: FieldN -> Property+testCompressed n = n > 0 ==> + not $ isPubKeyU $ derivePubKey $ fromJust $ makePrvKey $ fromIntegral n++testUnCompressed :: FieldN -> Property+testUnCompressed n = n > 0 ==> + isPubKeyU $ derivePubKey $ fromJust $ makePrvKeyU $ fromIntegral n++testPrivateCompressed :: FieldN -> Property+testPrivateCompressed n = n > 0 ==> + not $ isPrvKeyU $ fromJust $ makePrvKey $ fromIntegral n++testPrivateUnCompressed :: FieldN -> Property+testPrivateUnCompressed n = n > 0 ==> + isPrvKeyU $ fromJust $ makePrvKeyU $ fromIntegral n++testDerivedPubKey :: PrvKey -> Bool+testDerivedPubKey k = isValidPubKey $ derivePubKey k++deriveFromInt :: Integer -> Bool+deriveFromInt i = maybe True (isValidPubKey . derivePubKey) $ makePrvKey i++{- Key properties -}++testAddPubKey :: TestPrvKeyC -> Hash256 -> Bool+testAddPubKey (TestPrvKeyC key) i + | toInteger i >= curveN = isNothing res+ | model == InfPoint = isNothing res+ | otherwise = PubKey model == fromJust res+ where pub = derivePubKey key+ pt = mulPoint (toFieldN i) curveG+ model = addPoint (pubKeyPoint pub) pt+ res = addPubKeys pub i++testAddPrvKey :: TestPrvKeyC -> Hash256 -> Bool+testAddPrvKey (TestPrvKeyC key) i+ | toInteger i >= curveN = isNothing res+ | model == 0 = isNothing res+ | otherwise = PrvKey model == fromJust res+ where model = (prvKeyFieldN key) + (toFieldN i)+ res = addPrvKeys key i+
+ tests/Network/Haskoin/Crypto/Merkle/Tests.hs view
@@ -0,0 +1,36 @@+module Network.Haskoin.Crypto.Merkle.Tests (tests) where++import Test.QuickCheck.Property (Property, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Network.Haskoin.Crypto.Arbitrary ()+import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "Merkle Trees"+ [ testProperty "Width of tree at maxmum height = 1" testTreeWidth+ , testProperty "Width of tree at height 0 is # txns" testBaseWidth+ , testProperty "extract . build partial merkle tree" buildExtractTree+ ]+ ]++{- Merkle Trees -}++testTreeWidth :: Int -> Property+testTreeWidth i = i > 0 ==> calcTreeWidth i (calcTreeHeight i) == 1++testBaseWidth :: Int -> Property+testBaseWidth i = i > 0 ==> calcTreeWidth i 0 == i++buildExtractTree :: [(Hash256,Bool)] -> Property+buildExtractTree txs = not (null txs) ==>+ r == (buildMerkleRoot hashes) && m == (map fst $ filter snd txs)+ where+ (f,h) = buildPartialMerkle txs+ (r,m) = fromRight $ extractMatches f h (length txs)+ hashes = map fst txs++
+ tests/Network/Haskoin/Crypto/Merkle/Units.hs view
@@ -0,0 +1,113 @@+module Network.Haskoin.Crypto.Merkle.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust)+import qualified Data.ByteString as BS (reverse)++import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "Merkle Roots" + (map mapMerkleVectors $ zip merkleVectors [0..])+ ]++mapMerkleVectors :: ((String,[String]),Int) -> Test.Framework.Test+mapMerkleVectors (v,i) = + testCase name $ runMerkleVector v+ where+ name = "MerkleRoot vector " ++ (show i)++runMerkleVector :: (String,[String]) -> Assertion+runMerkleVector (r,hs) = do+ assertBool " > Merkle Vector" $ buildMerkleRoot (map f hs) == (f r)+ where+ f = decode' . BS.reverse . fromJust . hexToBS++merkleVectors :: [(String,[String])]+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"+ ]+ )+ ]+
+ tests/Network/Haskoin/Crypto/Mnemonic/Tests.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE OverloadedStrings #-}+module Network.Haskoin.Crypto.Mnemonic.Tests (tests) where++import Data.Bits ((.&.), shiftR)+import Data.Binary (Binary)+import qualified Data.Text as T+import Data.Word (Word32)+import qualified Data.ByteString as BS+import Network.Haskoin.Crypto.Arbitrary()+import Network.Haskoin.Crypto.Mnemonic+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Util (encode', fromRight)+import Test.QuickCheck (Arbitrary, Property, arbitrary, choose, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)+++tests :: [Test]+tests = + [ testGroup "Encode mnemonic"+ [ testProperty "128-bit entropy -> 12 words" toMnemonic128+ , testProperty "160-bit entropy -> 18 words" toMnemonic160+ , testProperty "256-bit entropy -> 24 words" toMnemonic256+ , testProperty "512-bit entropy -> 48 words" toMnemonic512+ , testProperty "n-bit entropy -> m words" toMnemonicVar+ ]+ , testGroup "Encode/Decode Mnemonic"+ [ testProperty "128-bit entropy" fromToMnemonic128+ , testProperty "160-bit entropy" fromToMnemonic160+ , testProperty "256-bit entropy" fromToMnemonic256+ , testProperty "512-bit entropy" fromToMnemonic512+ , testProperty "n-bit entropy" fromToMnemonicVar+ ]+ , testGroup "Mnemonic to seed"+ [ testProperty "128-bit entropy" mnemonicToSeed128+ , testProperty "160-bit entropy" mnemonicToSeed160+ , testProperty "256-bit entropy" mnemonicToSeed256+ , testProperty "512-bit entropy" mnemonicToSeed512+ , testProperty "n-bit entropy" mnemonicToSeedVar+ ]+ , testGroup "Get bits from ByteString"+ [ testProperty "Byte count" getBitsByteCount+ , testProperty "End bits" getBitsEndBits+ ]+ ]++binWordsToBS :: Binary a => [a] -> BS.ByteString+binWordsToBS = foldr f BS.empty+ where+ f b a = a `BS.append` encode' b++{- Encode mnemonic -}++toMnemonic128 :: Word128 -> Bool+toMnemonic128 x = l == 12+ where+ bs = encode' x+ l = length . T.words . fromRight $ toMnemonic english bs++toMnemonic160 :: Word160 -> Bool+toMnemonic160 x = l == 15+ where+ bs = encode' x+ l = length . T.words . fromRight $ toMnemonic english bs++toMnemonic256 :: Word256 -> Bool+toMnemonic256 x = l == 24+ where+ bs = encode' x+ l = length . T.words . fromRight $ toMnemonic english bs++toMnemonic512 :: Word512 -> Bool+toMnemonic512 x = l == 48+ where+ bs = encode' x+ l = length . T.words . fromRight $ toMnemonic english bs++toMnemonicVar :: [Word32] -> Property+toMnemonicVar ls = not (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 $ toMnemonic english bs++{- Encode/Decode -}++fromToMnemonic128 :: Word128 -> Bool+fromToMnemonic128 x = bs == bs'+ where+ bs = encode' x+ bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)++fromToMnemonic160 :: Word160 -> Bool+fromToMnemonic160 x = bs == bs'+ where+ bs = encode' x+ bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)++fromToMnemonic256 :: Word256 -> Bool+fromToMnemonic256 x = bs == bs'+ where+ bs = encode' x+ bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)++fromToMnemonic512 :: Word512 -> Bool+fromToMnemonic512 x = bs == bs'+ where+ bs = encode' x+ bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)++fromToMnemonicVar :: [Word32] -> Property+fromToMnemonicVar ls = not (length ls > 8) ==> bs == bs'+ where+ bs = binWordsToBS ls+ bs' = fromRight (fromMnemonic english =<< toMnemonic english bs)++{- Mnemonic to seed -}++mnemonicToSeed128 :: Word128 -> Bool+mnemonicToSeed128 x = l == 64+ where+ bs = encode' x+ seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ l = BS.length seed++mnemonicToSeed160 :: Word160 -> Bool+mnemonicToSeed160 x = l == 64+ where+ bs = encode' x+ seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ l = BS.length seed++mnemonicToSeed256 :: Word256 -> Bool+mnemonicToSeed256 x = l == 64+ where+ bs = encode' x+ seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ l = BS.length seed++mnemonicToSeed512 :: Word512 -> Bool+mnemonicToSeed512 x = l == 64+ where+ bs = encode' x+ seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ l = BS.length seed++mnemonicToSeedVar :: [Word32] -> Property+mnemonicToSeedVar ls = not (length ls > 16) ==> l == 64+ where+ bs = binWordsToBS ls+ seed = fromRight (mnemonicToSeed english "" =<< toMnemonic english bs)+ l = BS.length seed++{- Get bits from ByteString -}++data ByteCountGen = ByteCountGen BS.ByteString Int deriving Show++instance Arbitrary ByteCountGen where+ arbitrary = do+ bs <- arbitrary+ 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) = mask+ where+ r = i `mod` 8+ bits = getBits i bs+ mask = if r == 0 then True else BS.last bits .&. (0xff `shiftR` r) == 0x00+
+ tests/Network/Haskoin/Crypto/Mnemonic/Units.hs view
@@ -0,0 +1,177 @@+{-# LANGUAGE OverloadedStrings #-}+module Network.Haskoin.Crypto.Mnemonic.Units (tests) where++import Test.HUnit (assertEqual)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust)+import qualified Data.Text as T+import Network.Haskoin.Crypto.Mnemonic+import Network.Haskoin.Util (hexToBS, bsToHex, fromRight)++tests :: [Test]+tests =+ [ testGroup "Entropy to mnemonic sentence" toMnemonicTest+ , testGroup "Mnemonic sentence to entropy" fromMnemonicTest+ , testGroup "Mnemonic sentence to seed" mnemonicToSeedTest+ ]++toMnemonicTest :: [Test]+toMnemonicTest = map f $ zip ents mss+ where+ f (e, m) = g e . assertEqual "" m . h $ e+ g = testCase+ h = fromRight . toMnemonic english . fromJust . hexToBS++fromMnemonicTest :: [Test]+fromMnemonicTest = map f $ zip ents mss+ where+ f (e, m) = g e . assertEqual "" e . h $ m+ g = testCase+ h = bsToHex . fromRight . fromMnemonic english++mnemonicToSeedTest :: [Test]+mnemonicToSeedTest = map f $ zip mss seeds+ where+ f (m, s) = g s . assertEqual "" s . h $ m+ g = testCase . (++ "...") . take 50+ h = bsToHex . fromRight . mnemonicToSeed english "TREZOR"+ ++ents :: [String]+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 :: [T.Text]+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 :: [String]+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"+ ]
+ tests/Network/Haskoin/Crypto/Point/Tests.hs view
@@ -0,0 +1,103 @@+module Network.Haskoin.Crypto.Point.Tests (tests) where++import Test.QuickCheck.Property (Property, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Maybe (fromJust)++import Network.Haskoin.Crypto.Arbitrary()+import Network.Haskoin.Crypto.Point+import Network.Haskoin.Crypto.BigWord++tests :: [Test]+tests = + [ testGroup "Elliptic curve point arithmetic"+ [ testProperty "P is on the curve" checkOnCurve+ , testProperty "P1 + P2 is on the curve" addOnCurve+ , testProperty "n*P is on the curve" mulOnCurve+ , testProperty "makePoint (getAffine P) = P" fromToAffine+ , testProperty "P + InfPoint = P" addInfPoint+ , testProperty "InfPoint + P = P" addInfPoint'+ , testProperty "P1 + P2 = P2 + P1" addCommutative+ , testProperty "(P1 + P2) + P3 = P1 + (P2 + P3)" addAssoc+ , testProperty "(x,y) + (x,-y) = InfPoint" addInverseY+ , testProperty "double P = P + P" doubleAddPoint+ , testProperty "double P = 2*P" doubleMulPoint+ , testProperty "n*P = P + (n-1)*P" mulPointInduction+ , testProperty "a*P + b*P = (a + b)*P" mulDistributivity+ , testProperty "shamirsTrick = n1*P1 + n2*P2" testShamirsTrick+ , testProperty "point equality" testPointEqual+ ]+ ]++{- Elliptic curve point arithmetic -}++checkOnCurve :: Point -> Bool+checkOnCurve InfPoint = True+checkOnCurve p = validatePoint p++addOnCurve :: Point -> Point -> Bool+addOnCurve p1 p2 = case addPoint p1 p2 of+ InfPoint -> True+ p -> validatePoint p++mulOnCurve :: Point -> FieldN -> Bool+mulOnCurve p1 n = case mulPoint n p1 of+ InfPoint -> True+ p -> validatePoint p++fromToAffine :: Point -> Property+fromToAffine p = not (isInfPoint p) ==> (fromJust $ makePoint x y) == p+ where + (x,y) = fromJust $ getAffine p++addInfPoint :: Point -> Bool+addInfPoint p = addPoint p makeInfPoint == p++addInfPoint' :: Point -> Bool+addInfPoint' p = addPoint makeInfPoint p == p++addCommutative :: Point -> Point -> Bool+addCommutative p1 p2 = addPoint p1 p2 == addPoint p2 p1++addAssoc :: Point -> Point -> Point -> Bool+addAssoc p1 p2 p3 = + addPoint (addPoint p1 p2) p3 == addPoint p1 (addPoint p2 p3)++addInverseY :: Point -> Bool+addInverseY p1 = case (getAffine p1) of+ (Just (x,y)) -> addPoint p1 (fromJust $ makePoint x (-y)) == makeInfPoint+ Nothing -> True++doubleAddPoint :: Point -> Bool+doubleAddPoint p = doublePoint p == addPoint p p++doubleMulPoint :: Point -> Bool+doubleMulPoint p = doublePoint p == mulPoint 2 p++mulPointInduction :: FieldN -> Point -> Property+mulPointInduction i p = i > 2 ==> + mulPoint i p == addPoint p (mulPoint (i-1) p)++mulDistributivity :: FieldN -> FieldN -> Point -> Bool+mulDistributivity a b p = + (addPoint (mulPoint a p) (mulPoint b p)) == mulPoint (a + b) p++testShamirsTrick :: FieldN -> Point -> FieldN -> Point -> Bool+testShamirsTrick n1 p1 n2 p2 = shamirRes == normalRes+ where + shamirRes = shamirsTrick n1 p1 n2 p2+ normalRes = addPoint (mulPoint n1 p1) (mulPoint n2 p2) ++testPointEqual :: Point -> Point -> Bool+testPointEqual p1@InfPoint p2@InfPoint = p1 == p2 +testPointEqual p1 p2@InfPoint = p1 /= p2+testPointEqual p1@InfPoint p2 = p1 /= p2+testPointEqual p1 p2+ | x1 == x2 && y1 == y2 = p1 == p2+ | otherwise = p1 /= p2+ where + (x1,y1) = fromJust $ getAffine p1+ (x2,y2) = fromJust $ getAffine p2+
+ tests/Network/Haskoin/Crypto/Units.hs view
@@ -0,0 +1,264 @@+module Network.Haskoin.Crypto.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Control.Monad (replicateM_)+import Control.Monad.Trans (liftIO)++import Data.Maybe+import Data.Binary+import qualified Data.ByteString as BS++import Network.Haskoin.Crypto.Keys+import Network.Haskoin.Crypto.BigWord+import Network.Haskoin.Crypto.ECDSA+import Network.Haskoin.Crypto.Hash+import Network.Haskoin.Crypto.Base58+import Network.Haskoin.Util++-- Unit tests copied from bitcoind implementation+-- https://github.com/bitcoin/bitcoin/blob/master/src/test/key_tests.cpp++strSecret1 :: String+strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"++strSecret2 :: String+strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"++strSecret1C :: String+strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"++strSecret2C :: String+strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"++addr1 :: String +addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"++addr2 :: String +addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"++addr1C :: String+addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"++addr2C :: String+addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"++strAddressBad :: String+strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"++sigMsg :: [String]+sigMsg = [ ("Very secret message " ++ (show (i :: Int)) ++ ": 11") + | i <- [0..15]+ ]++sec1 :: PrvKey+sec1 = fromJust $ fromWIF strSecret1++sec2 :: PrvKey+sec2 = fromJust $ fromWIF strSecret2++sec1C :: PrvKey+sec1C = fromJust $ fromWIF strSecret1C++sec2C :: PrvKey+sec2C = fromJust $ fromWIF strSecret2C++pub1 :: PubKey+pub1 = derivePubKey sec1++pub2 :: PubKey+pub2 = derivePubKey sec2++pub1C :: PubKey+pub1C = derivePubKey sec1C++pub2C :: PubKey+pub2C = derivePubKey sec2C++tests :: [Test]+tests =+ [ testGroup "ECDSA PRNG unit tests"+ [ testCase "signMsg produces unique sigantures" uniqueSigs+ , testCase "genPrvKey produces unique keys" uniqueKeys+ ] + , testGroup "bitcoind /src/test/key_tests.cpp" $+ [ testCase "Decode Valid WIF" checkPrivkey+ , testCase "Decode Invalid WIF" checkInvalidKey+ , testCase "Check private key compression" checkPrvKeyCompressed+ , testCase "Check public key compression" checkKeyCompressed+ , testCase "Check matching address" checkMatchingAddress+ ] ++ + ( map (\x -> (testCase ("Check sig: " ++ (show x)) + (checkSignatures $ doubleHash256 $ stringToBS x))) sigMsg )+ , testGroup "Trezor RFC 6979 Test Vectors"+ [ testCase "RFC 6979 Test Vector 1" (testDetSigning $ detVec !! 0)+ , testCase "RFC 6979 Test Vector 2" (testDetSigning $ detVec !! 1)+ , testCase "RFC 6979 Test Vector 3" (testDetSigning $ detVec !! 2)+ , testCase "RFC 6979 Test Vector 4" (testDetSigning $ detVec !! 3)+ , testCase "RFC 6979 Test Vector 5" (testDetSigning $ detVec !! 4)+ , testCase "RFC 6979 Test Vector 6" (testDetSigning $ detVec !! 5)+ , testCase "RFC 6979 Test Vector 7" (testDetSigning $ detVec !! 6)+ , testCase "RFC 6979 Test Vector 8" (testDetSigning $ detVec !! 7)+ , testCase "RFC 6979 Test Vector 9" (testDetSigning $ detVec !! 8)+ , testCase "RFC 6979 Test Vector 10" (testDetSigning $ detVec !! 9)+ , testCase "RFC 6979 Test Vector 11" (testDetSigning $ detVec !! 10)+ , testCase "RFC 6979 Test Vector 12" (testDetSigning $ detVec !! 11)+ ] + ]++{- ECDSA PRNG unit tests -}++uniqueSigs :: Assertion+uniqueSigs = do+ let msg = hash256 $ BS.pack [0..19]+ prv = fromJust $ makePrvKey 0x987654321+ ((r1,s1),(r2,s2),(r3,s3)) <- liftIO $ withSource devURandom $ do+ (Signature a b) <- signMsg msg prv+ (Signature c d) <- signMsg msg prv+ replicateM_ 20 $ signMsg msg prv+ (Signature e f) <- signMsg msg prv+ return $ ((a,b),(c,d),(e,f))+ assertBool "DiffSig" $ + r1 /= r2 && r1 /= r3 && r2 /= r3 &&+ s1 /= s2 && s1 /= s3 && s2 /= s3++uniqueKeys :: Assertion+uniqueKeys = do+ (k1,k2,k3) <- liftIO $ withSource devURandom $ do+ a <- genPrvKey+ b <- genPrvKey+ replicateM_ 20 genPrvKey+ c <- genPrvKey+ return (a,b,c)+ assertBool "DiffKey" $ k1 /= k2 && k1 /= k3 && k2 /= k3++{- bitcoind /src/test/key_tests.cpp -}++checkPrivkey :: Assertion+checkPrivkey = do+ assertBool "Key 1" $ isJust $ fromWIF strSecret1+ assertBool "Key 2" $ isJust $ fromWIF strSecret2+ assertBool "Key 1C" $ isJust $ fromWIF strSecret1C+ assertBool "Key 2C" $ isJust $ fromWIF strSecret2C++checkInvalidKey :: Assertion+checkInvalidKey = + assertBool "Bad key" $ isNothing $ fromWIF strAddressBad+++checkPrvKeyCompressed :: Assertion+checkPrvKeyCompressed = do+ assertBool "Key 1" $ isPrvKeyU sec1+ assertBool "Key 2" $ isPrvKeyU sec2+ assertBool "Key 1C" $ not $ isPrvKeyU sec1C+ assertBool "Key 2C" $ not $ isPrvKeyU sec2C++checkKeyCompressed :: Assertion+checkKeyCompressed = do+ assertBool "Key 1" $ isPubKeyU pub1+ assertBool "Key 2" $ isPubKeyU pub2+ assertBool "Key 1C" $ not $ isPubKeyU pub1C+ assertBool "Key 2C" $ not $ isPubKeyU pub2C++checkMatchingAddress :: Assertion+checkMatchingAddress = do+ assertBool "Key 1" $ addr1 == (addrToBase58 $ pubKeyAddr pub1)+ assertBool "Key 2" $ addr2 == (addrToBase58 $ pubKeyAddr pub2)+ assertBool "Key 1C" $ addr1C == (addrToBase58 $ pubKeyAddr pub1C)+ assertBool "Key 2C" $ addr2C == (addrToBase58 $ pubKeyAddr pub2C)+ +checkSignatures :: Hash256 -> Assertion+checkSignatures h = do+ (sign1, sign2, sign1C, sign2C) <- liftIO $ withSource devURandom $ do+ a <- signMsg h sec1+ b <- signMsg h sec2+ c <- signMsg h sec1C+ d <- signMsg h sec2C+ return (a,b,c,d)+ assertBool "Key 1, Sign1" $ verifySig h sign1 pub1+ assertBool "Key 1, Sign2" $ not $ verifySig h sign2 pub1+ assertBool "Key 1, Sign1C" $ verifySig h sign1C pub1+ assertBool "Key 1, Sign2C" $ not $ verifySig h sign2C pub1+ assertBool "Key 2, Sign1" $ not $ verifySig h sign1 pub2+ assertBool "Key 2, Sign2" $ verifySig h sign2 pub2+ assertBool "Key 2, Sign1C" $ not $ verifySig h sign1C pub2+ assertBool "Key 2, Sign2C" $ verifySig h sign2C pub2+ assertBool "Key 1C, Sign1" $ verifySig h sign1 pub1C+ assertBool "Key 1C, Sign2" $ not $ verifySig h sign2 pub1C+ assertBool "Key 1C, Sign1C" $ verifySig h sign1C pub1C+ assertBool "Key 1C, Sign2C" $ not $ verifySig h sign2C pub1C+ assertBool "Key 2C, Sign1" $ not $ verifySig h sign1 pub2C+ assertBool "Key 2C, Sign2" $ verifySig h sign2 pub2C+ assertBool "Key 2C, Sign1C" $ not $ verifySig h sign1C pub2C+ assertBool "Key 2C, Sign2C" $ verifySig h sign2C pub2C+++{- Trezor RFC 6979 Test Vectors -}+-- github.com/trezor/python-ecdsa/blob/master/ecdsa/test_pyecdsa.py++detVec :: [(Integer,String,String)]+detVec = + [ + ( 0x1+ , "Satoshi Nakamoto"+ , "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"+ )+ , ( 0x1+ , "All those moments will be lost in time, like tears in rain. Time to die..."+ , "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"+ )+ , ( 0Xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140+ , "Satoshi Nakamoto"+ , "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"+ )+ , ( 0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181+ , "Alan Turing"+ , "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"+ )+ , ( 0xe91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2+ , "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"+ )+ , ( 0x0000000000000000000000000000000000000000000000000000000000000001+ , "Everything should be made as simple as possible, but not simpler."+ , "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"+ )+ , ( 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140+ , "Equations are more important to me, because politics is for the present, but an equation is something for eternity."+ , "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"+ )+ , ( 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140+ , "Not only is the Universe stranger than we think, it is stranger than we can think."+ , "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"+ )+ , ( 0x0000000000000000000000000000000000000000000000000000000000000001+ , "How wonderful that we have met with a paradox. Now we have some hope of making progress."+ , "c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3"+ )+ , ( 0x69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64+ , "Computer science is no more about computers than astronomy is about telescopes."+ , "7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6"+ )+ , ( 0x00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637+ , "...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"+ )+ , ( 0x000000000000000000000000000000000000000000056916d0f9b31dc9b637f3+ , "The question of whether computers can think is like the question of whether submarines can swim."+ , "cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef"+ )+ ]++testDetSigning :: (Integer, String, String) -> Assertion+testDetSigning (prv,msg,str) = do+ assertBool "RFC 6979 Vector" $ res == (fromJust $ hexToBS str)+ assertBool "Valid sig" $ verifySig msg' sig (derivePubKey prv')+ where sig@(Signature r s) = detSignMsg msg' prv'+ msg' = hash256 $ stringToBS msg+ prv' = fromJust $ makePrvKey prv+ res = runPut' $ put (fromIntegral r :: Hash256) >> + put (fromIntegral s :: Hash256)++
+ tests/Network/Haskoin/Protocol/Arbitrary.hs view
@@ -0,0 +1,237 @@+{-|+ This package provides QuickCheck Arbitrary instances for all the protocol+ data types defined in 'Network.Haskoin.Protocol'.+-}+module Network.Haskoin.Protocol.Arbitrary () where++import Test.QuickCheck+import Network.Haskoin.Util.Arbitrary (nonEmptyBS)+import Network.Haskoin.Crypto.Arbitrary()++import Control.Monad+import Control.Applicative ++import qualified Data.Sequence as S (fromList)++import Network.Haskoin.Protocol+import Network.Haskoin.Crypto++instance Arbitrary VarInt where+ arbitrary = VarInt <$> arbitrary++instance Arbitrary VarString where+ arbitrary = VarString <$> arbitrary++instance Arbitrary NetworkAddress where+ arbitrary = do+ s <- arbitrary+ a <- liftM2 (,) arbitrary arbitrary+ p <- arbitrary+ return $ NetworkAddress s a p++instance Arbitrary InvType where+ arbitrary = elements [InvError, InvTx, InvBlock, InvMerkleBlock]++instance Arbitrary InvVector where+ arbitrary = InvVector <$> arbitrary <*> (hash256 <$> arbitrary)++instance Arbitrary Inv where+ arbitrary = Inv <$> listOf arbitrary++instance Arbitrary Version where+ arbitrary = Version <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary++instance Arbitrary Addr where+ arbitrary = Addr <$> listOf arbitrary++instance Arbitrary Alert where+ arbitrary = Alert <$> arbitrary <*> arbitrary++instance Arbitrary BlockHeader where+ arbitrary = BlockHeader <$> arbitrary+ <*> (hash256 <$> arbitrary)+ <*> (hash256 <$> arbitrary)+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ +instance Arbitrary Script where+ arbitrary = do+ i <- choose (1,10)+ Script <$> (vectorOf i arbitrary)++instance Arbitrary Tx where+ arbitrary = do+ v <- arbitrary+ tin <- do + l <- choose (0,10)+ vectorOf l arbitrary+ tout <- do+ l <- choose (0,10)+ vectorOf l arbitrary+ t <- arbitrary+ return $ Tx v tin tout t++instance Arbitrary CoinbaseTx where+ arbitrary = CoinbaseTx <$> arbitrary+ <*> (return $ OutPoint 0 0xffffffff)+ <*> arbitrary+ <*> (return $ 0xffffffff)+ <*> (listOf arbitrary)+ <*> arbitrary++instance Arbitrary TxIn where+ arbitrary = TxIn <$> arbitrary+ <*> arbitrary+ <*> arbitrary++instance Arbitrary TxOut where+ arbitrary = TxOut <$> (choose (0,2100000000000000))+ <*> arbitrary++instance Arbitrary OutPoint where+ arbitrary = OutPoint <$> arbitrary+ <*> arbitrary++instance Arbitrary Block where+ arbitrary = do+ h <- arbitrary+ c <- arbitrary+ t <- do + l <- choose (0,10)+ vectorOf l arbitrary+ return $ Block h c t++instance Arbitrary MerkleBlock where+ arbitrary = do+ h <- arbitrary+ ntx <- arbitrary+ hashes <- arbitrary+ c <- choose (1,10)+ flags <- vectorOf (c*8) arbitrary+ return $ MerkleBlock h ntx hashes flags++instance Arbitrary PushDataType where+ arbitrary = elements [ OPCODE, OPDATA1, OPDATA2, OPDATA4 ]++instance Arbitrary ScriptOp where+ arbitrary = oneof [ opPushData <$> nonEmptyBS+ , return OP_0+ , return OP_1NEGATE+ , 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+ , return OP_VERIFY+ , return OP_DUP+ , return OP_EQUAL+ , return OP_EQUALVERIFY+ , return OP_HASH160+ , return OP_CHECKSIG+ , return OP_CHECKMULTISIG+ , OP_PUBKEY <$> arbitrary+ , return $ OP_INVALIDOPCODE 0xff+ ]++instance Arbitrary GetBlocks where+ arbitrary = GetBlocks <$> arbitrary+ <*> (listOf arbitrary)+ <*> arbitrary++instance Arbitrary GetData where+ arbitrary = GetData <$> (listOf arbitrary)++instance Arbitrary GetHeaders where+ arbitrary = GetHeaders <$> arbitrary+ <*> (listOf arbitrary)+ <*> arbitrary++instance Arbitrary Headers where+ arbitrary = Headers <$> (listOf (liftM2 (,) arbitrary arbitrary))++instance Arbitrary NotFound where+ arbitrary = NotFound <$> (listOf arbitrary)++instance Arbitrary Ping where+ arbitrary = Ping <$> arbitrary++instance Arbitrary Pong where+ arbitrary = Pong <$> arbitrary++instance Arbitrary MessageCommand where+ arbitrary = elements [ MCVersion+ , MCVerAck+ , MCAddr+ , MCInv+ , MCGetData+ , MCNotFound+ , MCGetBlocks+ , MCGetHeaders+ , MCTx+ , MCBlock+ , MCHeaders+ , MCGetAddr+ , MCFilterLoad+ , MCFilterAdd+ , MCFilterClear+ , MCPing+ , MCPong+ , MCAlert+ ]++instance Arbitrary MessageHeader where+ arbitrary = MessageHeader <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary++instance Arbitrary Message where+ arbitrary = oneof [ MVersion <$> arbitrary+ , return MVerAck+ , MAddr <$> arbitrary+ , MInv <$> arbitrary+ , MGetData <$> arbitrary+ , MNotFound <$> arbitrary+ , MGetBlocks <$> arbitrary+ , MGetHeaders <$> arbitrary+ , MTx <$> arbitrary+ , MBlock <$> arbitrary+ , MHeaders <$> arbitrary+ , return MGetAddr+ , MFilterLoad <$> arbitrary+ , MFilterAdd <$> arbitrary+ , return MFilterClear+ , MPing <$> arbitrary+ , MPong <$> arbitrary+ , MAlert <$> arbitrary+ ]++instance Arbitrary BloomFlags where+ arbitrary = elements [ BloomUpdateNone+ , BloomUpdateAll+ , BloomUpdateP2PubKeyOnly+ ]++instance Arbitrary BloomFilter where+ arbitrary = BloomFilter <$> (S.fromList <$> arbitrary)+ <*> (return False)+ <*> (return False)+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ +instance Arbitrary FilterLoad where+ arbitrary = FilterLoad <$> arbitrary++instance Arbitrary FilterAdd where+ arbitrary = FilterAdd <$> arbitrary+
+ tests/Network/Haskoin/Protocol/Tests.hs view
@@ -0,0 +1,59 @@+module Network.Haskoin.Protocol.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Maybe+import Data.Binary+import Data.Binary.Get+import Data.Binary.Put++import Network.Haskoin.Protocol.Arbitrary()+import Network.Haskoin.Protocol+import Network.Haskoin.Crypto++tests :: [Test]+tests = + [ testGroup "Serialize & de-serialize protocol messages"+ [ testProperty "VarInt" (metaGetPut :: VarInt -> Bool)+ , testProperty "VarString" (metaGetPut :: VarString -> Bool)+ , testProperty "NetworkAddress" (metaGetPut :: NetworkAddress -> Bool)+ , testProperty "InvType" (metaGetPut :: InvType -> Bool)+ , testProperty "InvVector" (metaGetPut :: InvVector -> Bool)+ , testProperty "Inv" (metaGetPut :: Inv -> Bool)+ , testProperty "Version" (metaGetPut :: Version -> Bool)+ , testProperty "Addr" (metaGetPut :: Addr -> Bool)+ , testProperty "Alert" (metaGetPut :: Alert -> Bool)+ , testProperty "TxIn" (metaGetPut :: TxIn -> Bool)+ , testProperty "TxOut" (metaGetPut :: TxOut -> Bool)+ , testProperty "OutPoint" (metaGetPut :: OutPoint -> Bool)+ , testProperty "ScriptOp" (metaGetPut :: ScriptOp -> Bool)+ , testProperty "Script" (metaGetPut :: Script -> Bool)+ , testProperty "Tx" (metaGetPut :: Tx -> Bool)+ , testProperty "CoinbaseTx" (metaGetPut :: CoinbaseTx -> Bool)+ , testProperty "BlockHeader" (metaGetPut :: BlockHeader -> Bool)+ , testProperty "Block" (metaGetPut :: Block -> Bool)+ , testProperty "MerkleBlock" (metaGetPut :: MerkleBlock -> Bool)+ , testProperty "GetBlocks" (metaGetPut :: GetBlocks -> Bool)+ , testProperty "GetData" (metaGetPut :: GetData -> Bool)+ , testProperty "GetHeaders" (metaGetPut :: GetHeaders -> Bool)+ , testProperty "Headers" (metaGetPut :: Headers -> Bool)+ , testProperty "FilterLoad" (metaGetPut :: FilterLoad -> Bool)+ , testProperty "FilterAdd" (metaGetPut :: FilterAdd -> Bool)+ , testProperty "NotFound" (metaGetPut :: NotFound -> Bool)+ , testProperty "Ping" (metaGetPut :: Ping -> Bool)+ , testProperty "Pong" (metaGetPut :: Pong -> Bool)+ , testProperty "MessageCommand" (metaGetPut :: MessageCommand -> Bool)+ , testProperty "MessageHeader" (metaGetPut :: MessageHeader -> Bool)+ , testProperty "Message" (metaGetPut :: Message -> Bool)+ ]+ , testGroup "Transactions"+ [ testProperty "decode . encode Txid" decEncTxid ]+ ]++metaGetPut :: (Binary a, Eq a) => a -> Bool+metaGetPut x = (runGet get (runPut $ put x)) == x++decEncTxid :: Hash256 -> Bool+decEncTxid h = (fromJust $ decodeTxid $ encodeTxid h) == h+
+ tests/Network/Haskoin/Protocol/Units.hs view
@@ -0,0 +1,44 @@+module Network.Haskoin.Protocol.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe++import Network.Haskoin.Protocol+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "Computing TxID from Tx" + ( map mapTxIDVec $ zip txIDVec [0..] )+ ]++mapTxIDVec :: ((String,String),Int) -> Test.Framework.Test+mapTxIDVec (v,i) = testCase name $ runTxIDVec v+ where + name = "Compute TxID " ++ (show i)++runTxIDVec :: (String,String) -> Assertion+runTxIDVec (tid,tx) = assertBool "TxID" $ + (encodeTxid $ txid txBS) == tid+ where + txBS = decode' $ fromJust $ hexToBS tx++txIDVec :: [(String,String)]+txIDVec =+ [ ( "23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63"+ , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+ )+ , ( "c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73"+ , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"+ )+ , ( "f7fdd091fa6d8f5e7a8c2458f5c38faffff2d3f1406b6e4fe2c99dcc0d2d1cbb"+ , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"+ )+ , ( "afd9c17f8913577ec3509520bd6e5d63e9c0fd2a5f70c787993b097ba6ca9fae"+ , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"+ )+ ]+
+ tests/Network/Haskoin/Script/Arbitrary.hs view
@@ -0,0 +1,85 @@+{-|+ This module provides arbitrary instances for data types in+ 'Network.Haskoin.Script'.+-}+module Network.Haskoin.Script.Arbitrary where++import Test.QuickCheck + ( Gen+ , Arbitrary+ , arbitrary+ , oneof+ , choose+ , vectorOf+ , elements+ )+import Network.Haskoin.Crypto.Arbitrary()++import Control.Monad (liftM2)+import Control.Applicative ((<$>),(<*>))++import Data.Bits (testBit, clearBit)+import Data.Word (Word8)++import Network.Haskoin.Script+import Network.Haskoin.Protocol+import Network.Haskoin.Crypto++instance Arbitrary TxSignature where+ arbitrary = liftM2 TxSignature arbitrary arbitrary++instance Arbitrary SigHash where+ arbitrary = do+ w <- arbitrary :: Gen Word8+ let acp = testBit w 7+ return $ case clearBit w 7 of+ 1 -> SigAll acp+ 2 -> SigNone acp+ 3 -> SigSingle acp+ _ -> SigUnknown acp w++instance Arbitrary ScriptOutput where+ arbitrary = oneof + [ PayPK <$> arbitrary+ , (PayPKHash . pubKeyAddr) <$> arbitrary + , genPayMulSig+ , (PayScriptHash . scriptAddr) <$> arbitrary+ ]++-- | Generate an arbitrary 'ScriptOutput' of value PayMulSig.+genPayMulSig :: Gen ScriptOutput+genPayMulSig = do+ n <- choose (1,16)+ m <- choose (1,n)+ PayMulSig <$> (vectorOf n arbitrary) <*> (return m)++instance Arbitrary ScriptInput where+ arbitrary = oneof+ [ SpendPK <$> arbitrary+ , SpendPKHash <$> arbitrary <*> arbitrary+ , genSpendMulSig+ ]++-- | Generate an arbitrary 'ScriptInput' of value SpendMulSig.+genSpendMulSig :: Gen ScriptInput+genSpendMulSig = do+ m <- choose (1,16)+ s <- choose (1,m)+ SpendMulSig <$> (vectorOf s arbitrary) <*> (return m)++instance Arbitrary ScriptHashInput where+ arbitrary = ScriptHashInput <$> arbitrary <*> arbitrary++-- | Data type for generating an arbitrary 'ScriptOp' with a value in+-- [OP_1 .. OP_16]+data ScriptOpInt = ScriptOpInt ScriptOp+ deriving (Eq, Show)++instance Arbitrary ScriptOpInt where+ arbitrary = ScriptOpInt <$> 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+ ]+
+ tests/Network/Haskoin/Script/Tests.hs view
@@ -0,0 +1,116 @@+module Network.Haskoin.Script.Tests (tests) where++import Test.QuickCheck.Property (Property, (==>))+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Control.Applicative ((<$>))++import Data.Bits (setBit, testBit)+import Data.Binary (Word8)+import qualified Data.ByteString as BS + ( singleton+ , length+ , tail+ , head+ , pack+ )++import Network.Haskoin.Protocol.Arbitrary ()+import Network.Haskoin.Script.Arbitrary (ScriptOpInt(..))++import Network.Haskoin.Script+import Network.Haskoin.Crypto+import Network.Haskoin.Protocol+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "Script Parser"+ [ testProperty "decode . encode OP_1 .. OP_16" testScriptOpInt+ , testProperty "decode . encode ScriptOutput" testScriptOutput+ , testProperty "decode . encode ScriptInput" testScriptInput+ , testProperty "decode . encode ScriptHashInput" testScriptHashInput+ , testProperty "sorting MultiSig scripts" testSortMulSig+ ]+ , testGroup "Script SigHash"+ [ testProperty "canonical signatures" testCanonicalSig+ , testProperty "decode . encode SigHash" binSigHash+ , testProperty "decode SigHash from Word8" binSigHashByte+ , testProperty "encodeSigHash32 is 4 bytes long" testEncodeSH32+ , testProperty "decode . encode TxSignature" binTxSig+ , testProperty "decodeCanonical . encode TxSignature" binTxSigCanonical+ , testProperty "Testing txSigHash with SigSingle" testSigHashOne+ ]+ ]++{- Script Parser -}++testScriptOpInt :: ScriptOpInt -> Bool+testScriptOpInt (ScriptOpInt i) = (intToScriptOp <$> scriptOpToInt i) == Right i++testScriptOutput :: ScriptOutput -> Bool+testScriptOutput so = (decodeOutput $ encodeOutput so) == Right so++testScriptInput :: ScriptInput -> Bool+testScriptInput si = (decodeInput $ encodeInput si) == Right si++testScriptHashInput :: ScriptHashInput -> Bool+testScriptHashInput sh = (decodeScriptHash $ encodeScriptHash sh) == Right sh++testSortMulSig :: ScriptOutput -> Bool+testSortMulSig out = case out of+ (PayMulSig _ _) -> check $ sortMulSig out+ _ -> True+ where check (PayMulSig ps _) + | length ps <= 1 = True+ | otherwise = snd $ foldl f (head ps,True) $ tail ps+ check _ = False+ f (a,t) b | t && encode' a <= encode' b = (b,True)+ | otherwise = (b,False)+ +{- Script SigHash -}++testCanonicalSig :: TxSignature -> Bool+testCanonicalSig ts@(TxSignature _ sh) + | isSigUnknown sh = isLeft $ decodeCanonicalSig bs+ | otherwise = isRight (decodeCanonicalSig bs) && + isCanonicalHalfOrder (txSignature ts)+ where bs = encodeSig ts++binSigHash :: SigHash -> Bool+binSigHash sh = (decode' $ encode' sh) == sh++binSigHashByte :: Word8 -> Bool+binSigHashByte w+ | w == 0x01 = res == SigAll False+ | w == 0x02 = res == SigNone False+ | w == 0x03 = res == SigSingle False+ | w == 0x81 = res == SigAll True+ | w == 0x82 = res == SigNone True+ | w == 0x83 = res == SigSingle True+ | testBit w 7 = res == SigUnknown True w+ | otherwise = res == SigUnknown False w+ where res = decode' $ BS.singleton w++testEncodeSH32 :: SigHash -> Bool+testEncodeSH32 sh = BS.length bs == 4 && BS.head bs == w && BS.tail bs == zs+ where bs = encodeSigHash32 sh+ w = BS.head $ encode' sh+ zs = BS.pack [0,0,0]++binTxSig :: TxSignature -> Bool+binTxSig ts = (fromRight $ decodeSig $ encodeSig ts) == ts++binTxSigCanonical :: TxSignature -> Bool+binTxSigCanonical ts@(TxSignature _ sh) + | isSigUnknown sh = isLeft $ decodeCanonicalSig $ encodeSig ts+ | otherwise = (fromRight $ decodeCanonicalSig $ encodeSig ts) == ts++testSigHashOne :: Tx -> Script -> Bool -> Property+testSigHashOne tx s acp = not (null $ txIn tx) ==> + if length (txIn tx) > length (txOut tx) + then res == (setBit 0 248)+ else res /= (setBit 0 248)+ where res = txSigHash tx s (length (txIn tx) - 1) (SigSingle acp)+
+ tests/Network/Haskoin/Script/Units.hs view
@@ -0,0 +1,100 @@+module Network.Haskoin.Script.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Maybe (fromJust)++import Network.Haskoin.Script+import Network.Haskoin.Protocol+import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "Canonical signatures" + (map canonicalVectorsMap $ zip canonicalVectors [0..])+ , testGroup "Non canonical sigatures" + (map notCanonicalVectorsMap $ zip notCanonicalVectors [0..])+ , testGroup "Multi Signatures" + (map mapMulSigVector $ zip mulSigVectors [0..])+ ]++canonicalVectorsMap :: (String,Int) -> Test.Framework.Test+canonicalVectorsMap (_,i) = + testCase ("Canonical Sig " ++ (show i)) func+ where + func = testCanonicalSig $ canonicalVectors !! i++notCanonicalVectorsMap :: (String,Int) -> Test.Framework.Test+notCanonicalVectorsMap (_,i) = + testCase ("Not canonical Sig " ++ (show i)) func+ where + func = testNotCanonicalSig $ notCanonicalVectors !! i++testCanonicalSig :: String -> Assertion+testCanonicalSig str = + assertBool " > Canonical Sig" $ isRight $ decodeCanonicalSig bs+ where + bs = fromJust $ hexToBS str++testNotCanonicalSig :: String -> Assertion+testNotCanonicalSig str = + assertBool " > Not canonical sig" $ isLeft $ decodeCanonicalSig bs+ where + bs = fromJust $ hexToBS str++mapMulSigVector :: ((String,String),Int) -> Test.Framework.Test+mapMulSigVector (v,i) = + testCase name $ runMulSigVector v+ where + name = "MultiSignature vector " ++ (show i)++runMulSigVector :: (String,String) -> Assertion+runMulSigVector (a,ops) = + assertBool " > MultiSig Vector" $ a == b+ where + s = Script $ runGet' getScriptOps $ fromJust $ hexToBS ops+ b = addrToBase58 $ scriptAddr $ fromRight $ decodeOutput s++{- Canonical Signatures -}++-- Test vectors from bitcoind+-- http://github.com/bitcoin/bitcoin/blob/master/src/test/data/sig_canonical.json++canonicalVectors :: [String]+canonicalVectors =+ [ "300602010102010101" -- Changed 0x00 to 0x01 as 0x00 is invalid+ , "3008020200ff020200ff01"+ , "304402203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f9353189e5af33cdda8d77a5209aec03978fa001"+ , "30450220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01"+ , "3046022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01"+ ]++notCanonicalVectors :: [String]+notCanonicalVectors =+ [ "30050201ff020001"+ , "30470221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed11"+ , "314402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "301f01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb101"+ , "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed0001"+ , "304401205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "3024020002202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "304402208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "30450221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610501202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "302402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105020001"+ , "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ , "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"+ ]++mulSigVectors :: [(String,String)]+mulSigVectors =+ [ ( "3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC"+ , "52410491bba2510912a5bd37da1fb5b1673010e43d2c6d812c514e91bfa9f2eb129e1c183329db55bd868e209aac2fbc02cb33d98fe74bf23f0c235d6126b1d8334f864104865c40293a680cb9c020e7b1e106d8c1916d3cef99aa431a56d253e69256dac09ef122b1a986818a7cb624532f062c1d1f8722084861c5c3291ccffef4ec687441048d2455d2403e08708fc1f556002f1b6cd83f992d085097f9974ab08a28838f07896fbab08f39495e15fa6fad6edbfb1e754e35fa1c7844c41f322a1863d4621353ae"+ ) + ]+
+ tests/Network/Haskoin/Stratum/Units.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}+module Network.Haskoin.Stratum.Units (tests) where++import Control.Monad (liftM)+import Data.Aeson (decode)+import Data.ByteString.Lazy.Char8 (pack)+import Network.Haskoin.Stratum.Message+import Test.Framework (Test, testGroup, buildTest)+import Test.Framework.Providers.HUnit (testCase)+import qualified Test.HUnit as HUnit++tests :: [Test]+tests =+ [ testFile "Decode JSON-RPC request"+ isRequest "tests/data/requests.json"+ , testFile "Decode JSON-RPC notification"+ isNotif "tests/data/notifications.json"+ , testFile "Decode JSON-RPC response"+ isResponse "tests/data/responses.json"+ , testFile "Decode JSON-RPC error"+ isError "tests/data/errors.json"+ , testFile "Decode invalid JSON-RPC"+ isInvalid "tests/data/invalid.json"+ ]++testFile :: String -> (Maybe MessageValue -> Bool) -> String -> Test+testFile label f file = buildTest $ do+ vectors <- liftM lines $ readFile file+ let test = g vectors+ return test+ where+ g vectors = testGroup label $ do+ (vector, count) <- zip vectors [0..]+ let msg = decode $ pack vector+ lbl = label ++ " " ++ show (count :: Int)+ return . testCase lbl . HUnit.assertBool (failure vector) $ f msg+ failure vector = "Failed to decode: " ++ vector++isRequest :: Maybe MessageValue -> Bool+isRequest (Just (MsgRequest (Request _ _ (Just _)))) = True+isRequest _ = False++isNotif :: Maybe MessageValue -> Bool+isNotif (Just (MsgRequest (Request _ _ Nothing))) = True+isNotif _ = False++isResponse :: Maybe MessageValue -> Bool+isResponse (Just (MsgResponse (Response (Right _) _))) = True+isResponse _ = False++isError :: Maybe MessageValue -> Bool+isError (Just (MsgResponse (Response (Left _) _))) = True+isError _ = False++isInvalid :: Maybe MessageValue -> Bool+isInvalid Nothing = True+isInvalid _ = False
+ tests/Network/Haskoin/Transaction/Arbitrary.hs view
@@ -0,0 +1,162 @@+{-|+ Arbitrary instances for transaction package+-}+module Network.Haskoin.Transaction.Arbitrary +( genPubKeyC+, genMulSigInput+, genRegularInput +, genAddrOutput+, RegularTx(..)+, MSParam(..)+, PKHashSigTemplate(..)+, MulSigTemplate(..)+) where++import Test.QuickCheck + ( Gen+ , Arbitrary+ , arbitrary+ , vectorOf+ , oneof+ , choose+ )++import Control.Monad (liftM)+import Control.Applicative ((<$>),(<*>))++import Data.Word (Word64)+import Data.List (permutations, nubBy)++import Network.Haskoin.Crypto.Arbitrary +import Network.Haskoin.Protocol.Arbitrary ()+import Network.Haskoin.Script.Arbitrary ()++import Network.Haskoin.Transaction+import Network.Haskoin.Script+import Network.Haskoin.Protocol+import Network.Haskoin.Crypto+import Network.Haskoin.Util++-- | Data type for generating arbitrary valid multisignature parameters (m of n)+data MSParam = MSParam Int Int deriving (Eq, Show)++instance Arbitrary MSParam where+ arbitrary = do+ n <- choose (1,16)+ m <- choose (1,n)+ return $ MSParam m n++-- | Data type for generating arbitrary transaction with inputs and outputs+-- consisting only of script hash or pub key hash scripts.+data RegularTx = RegularTx Tx deriving (Eq, Show)++-- | Generate an arbitrary compressed public key.+genPubKeyC :: Gen PubKey+genPubKeyC = derivePubKey <$> genPrvKeyC++-- | Generate an arbitrary script hash input spending a multisignature+-- pay to script hash.+genMulSigInput :: Gen ScriptHashInput+genMulSigInput = do+ (MSParam m n) <- arbitrary+ rdm <- PayMulSig <$> (vectorOf n genPubKeyC) <*> (return m)+ inp <- SpendMulSig <$> (vectorOf m arbitrary) <*> (return m)+ return $ ScriptHashInput inp rdm++-- | Generate an arbitrary transaction input spending a public key hash or+-- script hash output.+genRegularInput :: Gen TxIn+genRegularInput = do+ op <- arbitrary+ sq <- arbitrary+ sc <- oneof [ encodeScriptHash <$> genMulSigInput+ , encodeInput <$> (SpendPKHash <$> arbitrary <*> genPubKeyC)+ ]+ return $ TxIn op sc sq++-- | Generate an arbitrary output paying to a public key hash or script hash+-- address.+genAddrOutput :: Gen TxOut+genAddrOutput = do+ v <- choose (1,2100000000000000)+ sc <- oneof [ (PayPKHash . pubKeyAddr) <$> arbitrary+ , (PayScriptHash . scriptAddr) <$> arbitrary+ ]+ return $ TxOut v $ encodeOutput sc++instance Arbitrary RegularTx where+ arbitrary = do+ x <- choose (1,10)+ y <- choose (1,10)+ liftM RegularTx $ Tx <$> arbitrary + <*> (vectorOf x genRegularInput) + <*> (vectorOf y genAddrOutput) + <*> arbitrary++instance Arbitrary Coin where+ arbitrary = Coin <$> arbitrary <*> arbitrary <*> arbitrary+ +data PKHashSigTemplate = PKHashSigTemplate Tx [SigInput] [PrvKey]+ deriving (Eq, Show)++data MulSigTemplate = MulSigTemplate Tx [SigInput] [PrvKey]+ deriving (Eq, Show)++-- Generates a private key that can sign a input using the OutPoint and SigInput+genPKHashData :: Gen (OutPoint, SigInput, PrvKey)+genPKHashData = do+ op <- arbitrary+ prv <- arbitrary+ sh <- arbitrary+ let pub = derivePubKey prv+ script = encodeOutput $ PayPKHash $ pubKeyAddr pub+ sigi = SigInput script op sh+ return (op, sigi, prv)++-- Generates private keys that can sign an input using the OutPoint and SigInput+genMSData :: Gen (OutPoint, SigInput, [PrvKey])+genMSData = do+ (MSParam m n) <- arbitrary+ prv <- vectorOf n arbitrary+ perm <- choose (0,n-1)+ op <- arbitrary+ sh <- arbitrary+ let pub = map derivePubKey prv+ rdm = PayMulSig pub m+ script = encodeOutput $ PayScriptHash $ scriptAddr rdm+ sigi = SigInputSH script op (encodeOutput rdm) sh+ perPrv = permutations prv !! perm+ return (op, sigi, take m perPrv)++genPayTo :: Gen (String,Word64)+genPayTo = do+ v <- choose (1,2100000000000000)+ sc <- oneof [ PubKeyAddress <$> arbitrary+ , ScriptAddress <$> arbitrary+ ]+ return (addrToBase58 sc, v)++-- Generates data for signing a PKHash transaction+instance Arbitrary PKHashSigTemplate where+ arbitrary = do+ inC <- choose (0,5)+ outC <- choose (0,10)+ dat <- nubBy (\a b -> fst3 a == fst3 b) <$> vectorOf inC genPKHashData+ perm <- choose (0,max 0 $ length dat - 1)+ payTo <- vectorOf outC genPayTo+ let tx = fromRight $ buildAddrTx (map fst3 dat) payTo+ perI = permutations (map snd3 dat) !! perm+ return $ PKHashSigTemplate tx perI (map lst3 dat)++-- Generates data for signing a P2SH transactions+instance Arbitrary MulSigTemplate where+ arbitrary = do+ inC <- choose (0,5)+ outC <- choose (0,10)+ dat <- nubBy (\a b -> fst3 a == fst3 b) <$> vectorOf inC genMSData+ perm <- choose (0,max 0 $ length dat - 1)+ payTo <- vectorOf outC genPayTo+ let tx = fromRight $ buildAddrTx (map fst3 dat) payTo+ perI = permutations (map snd3 dat) !! perm+ return $ MulSigTemplate tx perI (concat $ map lst3 dat)+
+ tests/Network/Haskoin/Transaction/Tests.hs view
@@ -0,0 +1,114 @@+module Network.Haskoin.Transaction.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.Word (Word64)+import qualified Data.ByteString as BS (length)++import Network.Haskoin.Transaction.Arbitrary+import Network.Haskoin.Transaction.Builder+import Network.Haskoin.Protocol+import Network.Haskoin.Script+import Network.Haskoin.Crypto+import Network.Haskoin.Util++tests :: [Test]+tests = + [ testGroup "Building Transactions"+ [ testProperty "building address tx" testBuildAddrTx+ , testProperty "testing guessTxSize function" testGuessSize+ , testProperty "testing chooseCoins function" testChooseCoins+ , testProperty "testing chooseMSCoins function" testChooseMSCoins+ ]+ , testGroup "Signing Transactions"+ [ testProperty "Sign and validate PKHash transactions" testSignTx+ , testProperty "Sign and validate Multisig transactions" testSignMS+ ]+ ]++{- Building Transactions -}++testBuildAddrTx :: [OutPoint] -> Address -> Word64 -> Bool+testBuildAddrTx os a v + | v <= 2100000000000000 = isRight tx && case a of+ x@(PubKeyAddress _) -> Right (PayPKHash x) == out+ x@(ScriptAddress _) -> Right (PayScriptHash x) == out+ | otherwise = isLeft tx+ where tx = buildAddrTx os [(addrToBase58 a,v)]+ out = decodeOutput $ scriptOutput $ txOut (fromRight tx) !! 0++testGuessSize :: RegularTx -> Bool+testGuessSize (RegularTx tx) =+ -- We compute an upper bound but it should be close enough to the real size+ -- We give 3 bytes of slack on every signature (1 on r and 2 on s)+ guess >= len && guess - 3*delta <= len+ where delta = pki + (sum $ map fst msi)+ guess = guessTxSize pki msi pkout msout+ len = BS.length $ encode' tx+ rIns = map (decodeInput . scriptInput) $ txIn tx+ mIns = map (decodeScriptHash . scriptInput) $ txIn tx+ pki = length $ filter (isSpendPKHash . fromRight) $ + filter isRight rIns+ msi = concat $ map (shData . fromRight) $ filter isRight mIns+ shData (ScriptHashInput _ (PayMulSig keys r)) = [(r,length keys)]+ shData _ = []+ out = map (fromRight . decodeOutput . scriptOutput) $ txOut tx+ pkout = length $ filter isPayPKHash out+ msout = length $ filter isPayScriptHash out++testChooseCoins :: Word64 -> Word64 -> [Coin] -> Bool+testChooseCoins target kbfee xs = case chooseCoins target kbfee xs of+ Right (chosen,change) ->+ let outSum = sum $ map (outValue . coinTxOut) chosen+ fee = getFee kbfee (length chosen) + in outSum == target + change + fee+ Left _ -> + let fee = getFee kbfee (length xs) + in target == 0 || s < target || s < target + fee+ where s = sum $ map (outValue . coinTxOut) xs++testChooseMSCoins :: Word64 -> Word64 -> MSParam -> [Coin] -> Bool+testChooseMSCoins target kbfee (MSParam m n) xs = + case chooseMSCoins target kbfee (m,n) xs of+ Right (chosen,change) ->+ let outSum = sum $ map (outValue . coinTxOut) chosen+ fee = getMSFee kbfee (m,n) (length chosen) + in outSum == target + change + fee+ Left _ -> + let fee = getMSFee kbfee (m,n) (length xs) + in target == 0 || s < target + fee+ where s = sum $ map (outValue . coinTxOut) xs++{- Signing Transactions -}++testSignTx :: PKHashSigTemplate -> Bool+testSignTx (PKHashSigTemplate tx sigi prv)+ | null $ txIn tx = isBroken txSig && isBroken txSigP+ | otherwise = (not $ verifyTx tx verData)+ && isComplete txSig + && verifyTx (runBuild txSig) verData+ && isPartial txSigP+ && (not $ verifyTx (runBuild txSigP) verData)+ && isComplete txSigC+ && verifyTx (runBuild txSigC) verData+ where txSig = detSignTx tx sigi prv+ txSigP = detSignTx tx sigi (tail prv)+ txSigC = detSignTx (runBuild txSigP) sigi [head prv]+ verData = map (\(SigInput s o _) -> (s,o)) sigi+ +testSignMS :: MulSigTemplate -> Bool+testSignMS (MulSigTemplate tx sigi prv)+ | null $ txIn tx = isBroken txSig && isBroken txSigP+ | otherwise = (not $ verifyTx tx verData)+ && isComplete txSig + && verifyTx (runBuild txSig) verData+ && isPartial txSigP+ && (not $ verifyTx (runBuild txSigP) verData)+ && isComplete txSigC+ && verifyTx (runBuild txSigC) verData+ where txSig = detSignTx tx sigi prv+ txSigP = detSignTx tx sigi (tail prv)+ txSigC = detSignTx (runBuild txSigP) sigi [head prv]+ verData = map (\(SigInputSH s o _ _) -> (s,o)) sigi+
+ tests/Network/Haskoin/Transaction/Units.hs view
@@ -0,0 +1,214 @@+module Network.Haskoin.Transaction.Units (tests) where++import Test.HUnit (Assertion, assertBool)+import Test.Framework (Test, testGroup)+import Test.Framework.Providers.HUnit (testCase)++import Data.Word (Word32, Word64)+import Data.Maybe (fromJust)+import Data.Binary.Get (getWord32le)+import qualified Data.ByteString as BS (reverse)++import Network.Haskoin.Transaction.Builder+import Network.Haskoin.Protocol+import Network.Haskoin.Util++tests :: [Test]+tests =+ [ testGroup "Build PKHash Transaction (generated from bitcoind)" + ( map mapPKHashVec $ zip pkHashVec [0..] )+ , testGroup "Verify transaction (bitcoind /test/data/tx_valid.json)" + ( map mapVerifyVec $ zip verifyVec [0..] )+ ]++mapPKHashVec :: (([(String,Word32)],[(String,Word64)],String),Int)+ -> Test.Framework.Test+mapPKHashVec (v,i) = testCase name $ runPKHashVec v+ where name = "Build PKHash Tx " ++ (show i)++runPKHashVec :: ([(String,Word32)],[(String,Word64)],String) -> Assertion+runPKHashVec (xs,ys,res) = + assertBool "Build PKHash Tx" $ (bsToHex $ encode' tx) == res+ where tx = fromRight $ buildAddrTx (map f xs) ys+ f (tid,ix) = OutPoint (fromJust $ decodeTxid tid) ix+++mapVerifyVec :: (([(String,String,String)],String),Int) + -> Test.Framework.Test+mapVerifyVec (v,i) = testCase name $ runVerifyVec v i+ where name = "Verify Tx " ++ (show i)++runVerifyVec :: ([(String,String,String)],String) -> Int -> Assertion+runVerifyVec (is,bsTx) i = + assertBool name $ verifyTx tx $ map f is+ where name = " > Verify transaction " ++ (show i)+ tx = decode' (fromJust $ hexToBS bsTx)+ f (o1,o2,bsScript) = + let ops = runGet' getScriptOps (fromJust $ hexToBS bsScript)+ op = OutPoint (decode' $ BS.reverse $ fromJust $ hexToBS o1) + (runGet' getWord32le $ fromJust $ hexToBS o2)+ in (Script ops,op)++-- These test vectors have been generated from bitcoind raw transaction api++pkHashVec :: [([(String,Word32)],[(String,Word64)],String)]+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"+ )+ ]++{- Test vectors from bitcoind -}+-- github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json++verifyVec :: [([(String,String,String)],String)]+verifyVec = + [+ -- It is of particular interest because it contains an invalidly-encoded signature which OpenSSL accepts+ ( [ + ( "60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1"+ , "00000000"+ , "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"+ )+ ]+ , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+ )+ -- It has an arbitrary extra byte stuffed into the signature at pos length - 2+ , ( [+ ( "60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1"+ , "00000000"+ , "514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae"+ )+ ]+ , "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba260000000004A0048304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2bab01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000"+ )+ -- it is of interest because it contains a 0-sequence as well as a signature of SIGHASH type 0 (which is not a real type)+ , ( [+ ( "406b2b06bcd34d3c8733e6b79f7a394c8a431fbf4ff5ac705c93f4076bb77602"+ , "00000000"+ , "76a914dc44b1164188067c3a32d4780f5996fa14a4f2d988ac"+ )+ ]+ , "01000000010276b76b07f4935c70acf54fbf1f438a4c397a9fb7e633873c4dd3bc062b6b40000000008c493046022100d23459d03ed7e9511a47d13292d3430a04627de6235b6e51a40f9cd386f2abe3022100e7d25b080f0bb8d8d5f878bba7d54ad2fda650ea8d158a33ee3cbd11768191fd004104b0e2c879e4daf7b9ab68350228c159766676a14f5815084ba166432aab46198d4cca98fa3e9981d0a90b2effc514b76279476550ba3663fdcaff94c38420e9d5000000000100093d00000000001976a9149a7b0f3b80c6baaeedce0a0842553800f832ba1f88ac00000000"+ )+ -- It caught a bug in the workaround for 23b397edccd3740a74adb603c9756370fafcde9bcc4483eb271ecad09a94dd63 in an overly simple implementation+ , ( [+ ( "b464e85df2a238416f8bdae11d120add610380ea07f4ef19c5f9dfd472f96c3d"+ , "00000000"+ , "76a914bef80ecf3a44500fda1bc92176e442891662aed288ac"+ )+ , ( "b7978cc96e59a8b13e0865d3f95657561a7f725be952438637475920bac9eb21"+ , "01000000"+ , "76a914bef80ecf3a44500fda1bc92176e442891662aed288ac"+ )+ ]+ , "01000000023d6cf972d4dff9c519eff407ea800361dd0a121de1da8b6f4138a2f25de864b4000000008a4730440220ffda47bfc776bcd269da4832626ac332adfca6dd835e8ecd83cd1ebe7d709b0e022049cffa1cdc102a0b56e0e04913606c70af702a1149dc3b305ab9439288fee090014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff21ebc9ba20594737864352e95b727f1a565756f9d365083eb1a8596ec98c97b7010000008a4730440220503ff10e9f1e0de731407a4a245531c9ff17676eda461f8ceeb8c06049fa2c810220c008ac34694510298fa60b3f000df01caa244f165b727d4896eb84f81e46bcc4014104266abb36d66eb4218a6dd31f09bb92cf3cfa803c7ea72c1fc80a50f919273e613f895b855fb7465ccbc8919ad1bd4a306c783f22cd3227327694c4fa4c1c439affffffff01f0da5200000000001976a914857ccd42dded6df32949d4646dfa10a92458cfaa88ac00000000"+ )+ -- It results in signing the constant 1, instead of something generated based on the transaction,+ -- when the input doing the signing has an index greater than the maximum output index+ , ( [ + ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "76a914e52b482f2faa8ecbf0db344f93c84ac908557f3388ac"+ )+ , ( "0000000000000000000000000000000000000000000000000000000000000200"+ , "00000000"+ , "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac"+ )+ ]+ , "01000000020002000000000000000000000000000000000000000000000000000000000000000000006a47304402200469f169b8091cd18a2770136be7411f079b3ac2b5c199885eb66a80aa3ed75002201fa89f3e6f80974e1b3474e70a0fbe907c766137ff231e4dd05a555d8544536701210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ffffffff0001000000000000000000000000000000000000000000000000000000000000000000006b483045022100c9cdd08798a28af9d1baf44a6c77bcc7e279f47dc487c8c899911bc48feaffcc0220503c5c50ae3998a733263c5c0f7061b483e2b56c4c41b456e7d2f5a78a74c077032102d5c25adb51b61339d2b05315791e21bbe80ea470a49db0135720983c905aace0ffffffff010000000000000000015100000000"+ )+ -- A valid P2SH Transaction using the standard transaction type put forth in BIP 16+ , ( [+ ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "a9148febbed40483661de6958d957412f82deed8e2f787"+ )+ ]+ , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100c66c9cdf4c43609586d15424c54707156e316d88b0a1534c9e6b0d4f311406310221009c0fe51dbc9c4ab7cc25d3fdbeccf6679fe6827f08edf2b4a9f16ee3eb0e438a0123210338e8034509af564c62644c07691942e0c056752008a173c89f60ab2a88ac2ebfacffffffff010000000000000000015100000000"+ )+ -- MAX_MONEY output+ , ( [+ ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "a91432afac281462b822adbec5094b8d4d337dd5bd6a87"+ )+ ]+ , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006e493046022100e1eadba00d9296c743cb6ecc703fd9ddc9b3cd12906176a226ae4c18d6b00796022100a71aef7d2874deff681ba6080f1b278bac7bb99c61b08a85f4311970ffe7f63f012321030c0588dc44d92bdcbf8e72093466766fdc265ead8db64517b0c542275b70fffbacffffffff010040075af0750700015100000000"+ )+ -- MAX_MONEY output + 0 output+ , ( [+ ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "a914b558cbf4930954aa6a344363a15668d7477ae71687"+ )+ ]+ , "01000000010001000000000000000000000000000000000000000000000000000000000000000000006d483045022027deccc14aa6668e78a8c9da3484fbcd4f9dcc9bb7d1b85146314b21b9ae4d86022100d0b43dece8cfb07348de0ca8bc5b86276fa88f7f2138381128b7c36ab2e42264012321029bb13463ddd5d2cc05da6e84e37536cb9525703cfd8f43afdb414988987a92f6acffffffff020040075af075070001510000000000000000015100000000"+ )+ -- Simple transaction with first input is signed with SIGHASH_ALL, second with SIGHASH_ANYONECANPAY+ , ( [+ ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac"+ )+ , ( "0000000000000000000000000000000000000000000000000000000000000200"+ , "00000000"+ , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac"+ )+ ]+ , "010000000200010000000000000000000000000000000000000000000000000000000000000000000049483045022100d180fd2eb9140aeb4210c9204d3f358766eb53842b2a9473db687fa24b12a3cc022079781799cd4f038b85135bbe49ec2b57f306b2bb17101b17f71f000fcab2b6fb01ffffffff0002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000"+ )+ -- Same as above, but we change the sequence number of the first input to check that SIGHASH_ANYONECANPAY is being followed+ , ( [+ ( "0000000000000000000000000000000000000000000000000000000000000100"+ , "00000000"+ , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac"+ )+ , ( "0000000000000000000000000000000000000000000000000000000000000200"+ , "00000000"+ , "21035e7f0d4d0841bcd56c39337ed086b1a633ee770c1ffdd94ac552a95ac2ce0efcac"+ )+ ]+ , "01000000020001000000000000000000000000000000000000000000000000000000000000000000004948304502203a0f5f0e1f2bdbcd04db3061d18f3af70e07f4f467cbc1b8116f267025f5360b022100c792b6e215afc5afc721a351ec413e714305cb749aae3d7fee76621313418df101010000000002000000000000000000000000000000000000000000000000000000000000000000004847304402205f7530653eea9b38699e476320ab135b74771e1c48b81a5d041e2ca84b9be7a802200ac8d1f40fb026674fe5a5edd3dea715c27baa9baca51ed45ea750ac9dc0a55e81ffffffff010100000000000000015100000000"+ )+ -- several SIGHASH_SINGLE signatures+ , ( [+ ( "63cfa5a09dc540bf63e53713b82d9ea3692ca97cd608c384f2aa88e51a0aac70"+ , "00000000"+ , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac"+ )+ , ( "04e8d0fcf3846c6734477b98f0f3d4badfb78f020ee097a0be5fe347645b817d"+ , "01000000"+ , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac"+ )+ , ( "ee1377aff5d0579909e11782e1d2f5f7b84d26537be7f5516dd4e43373091f3f"+ , "01000000"+ , "76a914dcf72c4fd02f5a987cf9b02f2fabfcac3341a87d88ac"+ )+ ]+ , "010000000370ac0a1ae588aaf284c308d67ca92c69a39e2db81337e563bf40c59da0a5cf63000000006a4730440220360d20baff382059040ba9be98947fd678fb08aab2bb0c172efa996fd8ece9b702201b4fb0de67f015c90e7ac8a193aeab486a1f587e0f54d0fb9552ef7f5ce6caec032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff7d815b6447e35fbea097e00e028fb7dfbad4f3f0987b4734676c84f3fcd0e804010000006b483045022100c714310be1e3a9ff1c5f7cacc65c2d8e781fc3a88ceb063c6153bf950650802102200b2d0979c76e12bb480da635f192cc8dc6f905380dd4ac1ff35a4f68f462fffd032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff3f1f097333e4d46d51f5e77b53264db8f7f5d2e18217e1099957d0f5af7713ee010000006c493046022100b663499ef73273a3788dea342717c2640ac43c5a1cf862c9e09b206fcb3f6bb8022100b09972e75972d9148f2bdd462e5cb69b57c1214b88fc55ca638676c07cfc10d8032103579ca2e6d107522f012cd00b52b9a65fb46f0c57b9b8b6e377c48f526a44741affffffff0380841e00000000001976a914bfb282c70c4191f45b5a6665cad1682f2c9cfdfb88ac80841e00000000001976a9149857cc07bed33a5cf12b9c5e0500b675d500c81188ace0fd1c00000000001976a91443c52850606c872403c0601e69fa34b26f62db4a88ac00000000"+ )+ ]+
+ tests/Network/Haskoin/Util/Arbitrary.hs view
@@ -0,0 +1,43 @@+{-|+ QuickCheck Arbitrary instances for various utility data types+-}+module Network.Haskoin.Util.Arbitrary (nonEmptyBS) where++import Test.QuickCheck + ( Arbitrary+ , Gen+ , arbitrary+ , choose+ , oneof+ )+import qualified Data.ByteString as BS + ( ByteString+ , pack+ , drop+ , null+ )++import Control.Applicative++import Network.Haskoin.Util.BuildMonad (Build(..))++-- Arbitrary instance for strict ByteStrings+instance Arbitrary BS.ByteString where+ arbitrary = do+ bs <- BS.pack `fmap` arbitrary+ n <- choose (0, 2)+ return $ BS.drop n bs -- to give us some with non-0 offset++-- | Generate non-empty strict ByteStrings+nonEmptyBS :: Gen BS.ByteString+nonEmptyBS = do+ bs <- arbitrary+ return $ if BS.null bs then BS.pack [0] else bs++-- Arbitrary instance for the Build monad+instance Arbitrary a => Arbitrary (Build a) where+ arbitrary = oneof [ Complete <$> arbitrary+ , Partial <$> arbitrary+ , return $ Broken "Arbitrary: Broken"+ ]+
+ tests/Network/Haskoin/Util/Tests.hs view
@@ -0,0 +1,115 @@+module Network.Haskoin.Util.Tests (tests) where++import Test.Framework (Test, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++import Data.List (permutations)+import Data.Maybe (fromJust, catMaybes)+import Data.Foldable (toList)+import qualified Data.Sequence as Seq (update, fromList)+import qualified Data.ByteString as BS (ByteString)++import Network.Haskoin.Util +import Network.Haskoin.Util.Arbitrary()++tests :: [Test]+tests = + [ testGroup "Utility functions"+ [ testProperty "toStrict( toLazy(bs) ) = bs" fromToLazy+ , testProperty "get( put(Integer) ) = Integer" getPutInteger+ , testProperty "stringToBS( bsToString(s) ) = s" fromToString+ , testProperty "decode'( encode'(s) ) = s" decEncBS+ , testProperty "decodeOrFail'( encode'(s) ) = s" decEncFailBS+ , testProperty "fromHex( toHex(bs) ) = bs" fromToHex+ , testProperty "testing decodeEither" testFromDecode+ , testProperty "compare updateIndex with Data.Sequence" testUpdateIndex+ , testProperty "testing matchTemplate" testMatchTemplate+ , testProperty + "testing matchTemplate with two lists" testMatchTemplateLen+ , testProperty "Testing either helper functions" testEither+ ]+ , testGroup "Build Monad"+ [ testProperty "Build monadic composition" testBuildCompose+ , testProperty "Testing guardPartial" testGuardPartial+ ]+ ]++{- Various utilities -}++fromToLazy :: BS.ByteString -> Bool+fromToLazy bs = (toStrictBS $ toLazyBS bs) == bs++fromToString :: BS.ByteString -> Bool+fromToString bs = (stringToBS $ bsToString bs) == bs++decEncBS :: BS.ByteString -> Bool+decEncBS bs = (decode' $ encode' bs) == bs++decEncFailBS :: BS.ByteString -> Bool+decEncFailBS bs = case (decodeOrFail' $ encode' bs) of+ (Left _) -> False+ (Right (_, _, res)) -> res == bs++getPutInteger :: Integer -> Bool+getPutInteger i = (bsToInteger $ integerToBS p) == p+ where + p = abs i++fromToHex :: BS.ByteString -> Bool+fromToHex bs = (fromJust $ hexToBS $ bsToHex bs) == bs++testFromDecode :: BS.ByteString -> Integer -> Integer -> Bool+testFromDecode bs def v = case decodeOrFail' bs of+ (Left _) -> fromDecode bs def (*v) == def + (Right (_,_,res)) -> fromDecode bs def (*v) == res*v ++testUpdateIndex :: [Int] -> Int -> Int -> Bool +testUpdateIndex xs v i = + (updateIndex i xs $ const v) == (toList $ Seq.update i v s)+ where + s = Seq.fromList xs++testMatchTemplate :: [Int] -> Int -> Bool+testMatchTemplate as i = catMaybes res == bs+ where + res = matchTemplate as bs (==)+ idx = if length as == 0 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 e == v)+ && (eitherToMaybe e == Just v)+ (Left v) -> (isLeft e)+ && (not $ isRight e)+ && (fromLeft e == v)+ && (eitherToMaybe e == Nothing)++{- Build Monad -}++testBuildCompose :: Build Int -> Build Int -> Bool+testBuildCompose ma mb+ | isBroken ma || isBroken mb = isBroken res+ | isPartial ma || isPartial mb = isPartial res+ | otherwise = isComplete res+ where + res = ma >>= (\a -> mb >>= (\b -> return $ a + b ))++testGuardPartial :: Build Int -> Build Int -> Bool+testGuardPartial ma mb+ | isBroken ma || isBroken mb = isBroken res+ | otherwise = isPartial res+ where + res = do+ a <- ma+ guardPartial False+ b <- mb+ return $ a + b+
+ tests/data/errors.json view
@@ -0,0 +1,7 @@+{"id": 3, "error": "unknown method:blockchain.foo"}+{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}+{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}+{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}+{"jsonrpc": "2.0", "error": {"code": -32700, "message": "Parse error"}, "id": null}+{"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null}+{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"}
+ tests/data/invalid.json view
@@ -0,0 +1,6 @@+{"jsonrpc": "2.0", "method": "foobar, "params": "bar", "baz]+{"jsonrpc": "2.0", "method": 1, "params": "bar"}+{"jsonrpc": "2.0", "method"+[]+[1,2,3]+{"foo": "boo"}
+ tests/data/notifications.json view
@@ -0,0 +1,7 @@+{"method": "handleMessage", "params": ["user1", "we were just talking"], "id": null}+{"method": "handleMessage", "params": ["user3", "sorry, gotta go now, ttyl"], "id": null}+{"method": "userLeft", "params": ["user3"], "id": null}+{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}+{"jsonrpc": "2.0", "method": "foobar"}+{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]}+{"jsonrpc": "2.0", "method": "notify_sum", "params": [1,2,4]}
+ tests/data/requests.json view
@@ -0,0 +1,12 @@+{ "method": "echo", "params": ["Hello JSON-RPC"], "id": 1}+{"method": "postMessage", "params": ["Hello all!"], "id": 99}+{"method": "postMessage", "params": ["I have a question:"], "id": 101}+{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}+{"jsonrpc": "2.0", "method": "subtract", "params": [23, 42], "id": 2}+{"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}+{"jsonrpc": "2.0", "method": "subtract", "params": {"minuend": 42, "subtrahend": 23}, "id": 4}+{"jsonrpc": "2.0", "method": "foobar", "id": "1"}+{"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"}+{"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"}+{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}+{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
+ tests/data/responses.json view
@@ -0,0 +1,10 @@+{ "result": "Hello JSON-RPC", "error": null, "id": 1}+{"result": 1, "error": null, "id": 99}+{"result": 1, "error": null, "id": 101}+{"jsonrpc": "2.0", "result": 19, "id": 1}+{"jsonrpc": "2.0", "result": -19, "id": 2}+{"jsonrpc": "2.0", "result": 19, "id": 3}+{"jsonrpc": "2.0", "result": 19, "id": 4}+{"jsonrpc": "2.0", "result": 7, "id": "1"}+{"jsonrpc": "2.0", "result": 19, "id": "2"}+{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}