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