packages feed

haskoin-core-0.14.1: 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.Util.Arbitrary
import           Haskoin.Util
import           Test.Hspec
import           Test.QuickCheck

serialVals :: [SerialBox]
serialVals =
    [ SerialBox (arbitrary :: Gen SecKey)
    , SerialBox (snd <$> arbitraryKeyPair) -- PubKeyI
    ]

readVals :: [ReadBox]
readVals =
    [ ReadBox (arbitrary :: Gen SecKey)
    , ReadBox arbitrarySecKeyI
    , ReadBox (snd <$> arbitraryKeyPair) -- PubKeyI
    ]

jsonVals :: [JsonBox]
jsonVals =
    [ JsonBox (snd <$> arbitraryKeyPair) -- PubKeyI
    ]

spec :: Spec
spec =
    describe "keys" $ do
        testIdentity serialVals readVals jsonVals []
        it "is public key canonical" $
            property $ forAll arbitraryKeyPair (isCanonicalPubKey . snd)
        it "from string public key" $
            property $
            forAll arbitraryKeyPair $ \(_, k) ->
                fromString (cs . encodeHex $ S.encode k) == k

-- 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