packages feed

haskoin-core-0.13.2: test/Haskoin/KeysSpec.hs

module Haskoin.KeysSpec (spec) where

import           Data.Aeson              as A
import qualified Data.ByteString         as BS
import           Data.Map.Strict         (singleton)
import           Data.Serialize          as S
import           Data.String             (fromString)
import           Data.String.Conversions (cs)
import           Haskoin.Address
import           Haskoin.Constants
import           Haskoin.Crypto
import           Haskoin.Keys
import           Haskoin.Test
import           Haskoin.Util
import           Test.Hspec
import           Test.QuickCheck

spec :: Spec
spec =
    describe "keys" $ do
        let net = btc
        it "is public key canonical" $
            property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)
        it "encode and decode wif private keys" $
            property $
            forAll arbitraryKeyPair $ \(pk, _) ->
                fromWif net (toWif net pk) == Just pk
        it "encode and decode serialized private key" $
            property $ forAll arbitrary binaryPrvKey
        it "read and show public key" $
            property $ forAll arbitraryKeyPair $ \(_, k) -> read (show k) == k
        it "read and show private key" $
            property $ forAll arbitrarySecKeyI $ \k -> read (show k) == k
        it "from string public key" $
            property $
            forAll arbitraryKeyPair $ \(_, k) ->
                fromString (cs . encodeHex $ S.encode k) == k
        it "encode and decode json public key" $
            property $ forAll arbitraryKeyPair (testID . snd)
        it "encodes and decodes serialized public key" $
            property $ forAll arbitraryKeyPair $ cerealID . snd


-- github.com/bitcoin/bitcoin/blob/master/src/script.cpp
-- from function IsCanonicalPubKey
isCanonicalPubKey :: PubKeyI -> Bool
isCanonicalPubKey p = not $
    -- Non-canonical public key: too short
    (BS.length bs < 33) ||
    -- Non-canonical public key: invalid length for uncompressed key
    (BS.index bs 0 == 4 && BS.length bs /= 65) ||
    -- Non-canonical public key: invalid length for compressed key
    (BS.index bs 0 `elem` [2,3] && BS.length bs /= 33) ||
    -- Non-canonical public key: compressed nor uncompressed
    (BS.index bs 0 `notElem` [2,3,4])
  where
    bs = S.encode p

{- Key formats -}

binaryPrvKey :: SecKey -> Bool
binaryPrvKey k =
    Right k == runGet secKeyGet (runPut (secKeyPut k)) &&
    Just k == secKey (getSecKey k)

testID :: (FromJSON a, ToJSON a, Eq a) => a -> Bool
testID x =
    (A.decode . A.encode) (singleton ("object" :: String) x) ==
    Just (singleton ("object" :: String) x)

cerealID :: (Serialize a, Eq a) => a -> Bool
cerealID x = S.decode (S.encode x) == Right x