packages feed

bitcoin-address-0.1: lib/Bitcoin/Address/Hash.hs

-- | This module exports hash constructions used by Bitcoin addresses.
module Bitcoin.Address.Hash
 ( -- * PubHash160
   PubHash160
 , unPubHash160
 , parsePubHash160
 , pubHash160
 , pubUncompressedHash160

   -- * ScriptHash160
 , ScriptHash160
 , unScriptHash160
 , parseScriptHash160
 , scriptHash160

   -- * ScriptSHA256
 , ScriptSHA256
 , unScriptSHA256
 , parseScriptSHA256
 , scriptSHA256
 ) where

import Bitcoin.Keys (Pub, pubCompressed, pubUncompressed)
import Bitcoin.Hash (hash160)
import Bitcoin.Hash.Prim (sha256)
import Control.Monad
import qualified Data.Bitcoin.Script as S
import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as BB
import qualified Data.ByteString.Lazy.Char8 as BL8

import Bitcoin.Address.Internal (scriptBytes)

--------------------------------------------------------------------------------

-- | The 'hash160' of a 'Pub'lic key.
newtype PubHash160 = PubHash160 B.ByteString
  deriving newtype (Eq, Ord)

-- | Base-16 encoded.
instance Show PubHash160 where
  showsPrec n (PubHash160 b) = showParen (n > 10) $
    showString "PubHash160 " .
    mappend (BL8.unpack (BB.toLazyByteString (BB.byteStringHex b)))

-- | Get the 20 bytes in 'PubHash160'.
unPubHash160 :: PubHash160 -> B.ByteString
{-# INLINE unPubHash160 #-}
unPubHash160 (PubHash160 x) = x

-- | Create a 'PubHash160' from its 20 raw bytes.
parsePubHash160 :: B.ByteString -> Maybe PubHash160
{-# INLINE parsePubHash160 #-}
parsePubHash160 b = do
  guard (B.length b == 20)
  pure (PubHash160 b)

-- | The 'hash160' of the compressed SEC representation of a 'Pub'lic key.
--
-- Usually, __this is what you want__.
pubHash160 :: Pub -> PubHash160
{-# INLINE pubHash160 #-}
pubHash160 = PubHash160 . hash160 . pubCompressed

-- | The 'hash160' of the uncompressed SEC representation of a 'Pub'lic key.
--
-- It's very unlikely that you want this. Except if it's ten years ago or you
-- are dealing with very old addresses.
--
-- __WARNING__ do not use this with SegWit addresses 'Bitcoin.Address.P2WPKH',
-- or in 'Bitcoin.Address.P2WSH' 'S.Script's.
pubUncompressedHash160 :: Pub -> PubHash160
{-# INLINE pubUncompressedHash160 #-}
pubUncompressedHash160 = PubHash160 . hash160 . pubUncompressed

--------------------------------------------------------------------------------

-- | The 'hash160' of a 'S.Script' as required by 'Bitcoin.Address.P2SH'.
newtype ScriptHash160 = ScriptHash160 B.ByteString
  deriving newtype (Eq, Ord)

-- | Base-16 encoded.
instance Show ScriptHash160 where
  showsPrec n (ScriptHash160 b) = showParen (n > 10) $
    showString "ScriptHash160 " .
    mappend (BL8.unpack (BB.toLazyByteString (BB.byteStringHex b)))

-- | Get the 20 bytes in 'ScriptHash160'.
unScriptHash160 :: ScriptHash160 -> B.ByteString
{-# INLINE unScriptHash160 #-}
unScriptHash160 (ScriptHash160 x) = x

-- | Create a 'ScriptHash160' from its 20 raw bytes.
parseScriptHash160 :: B.ByteString -> Maybe ScriptHash160
{-# INLINE parseScriptHash160 #-}
parseScriptHash160 b = do
  guard (B.length b == 20)
  pure (ScriptHash160 b)

-- | Hash a 'S.Script' as required by 'Bitcoin.Address.P2SH'.
scriptHash160 :: S.Script -> ScriptHash160
{-# INLINE scriptHash160 #-}
scriptHash160 = ScriptHash160 . hash160 . scriptBytes

--------------------------------------------------------------------------------

-- | The 'sha256' of a 'S.Script' as required by 'Bitcoin.Address.P2WSH'.
newtype ScriptSHA256 = ScriptSHA256 B.ByteString
  deriving newtype (Eq, Ord)

-- | Base-16 encoded.
instance Show ScriptSHA256 where
  showsPrec n (ScriptSHA256 b) = showParen (n > 10) $
    showString "ScriptSHA256 " .
    mappend (BL8.unpack (BB.toLazyByteString (BB.byteStringHex b)))

-- | Get the 32 bytes in 'ScriptSHA256'.
unScriptSHA256 :: ScriptSHA256 -> B.ByteString
{-# INLINE unScriptSHA256 #-}
unScriptSHA256 (ScriptSHA256 x) = x

-- | Create a 'ScriptSHA256' from its 32 raw bytes.
parseScriptSHA256 :: B.ByteString -> Maybe ScriptSHA256
{-# INLINE parseScriptSHA256 #-}
parseScriptSHA256 b = do
  guard (B.length b == 32)
  pure (ScriptSHA256 b)

-- | Hash a 'S.Script' as required by 'Bitcoin.Address.P2WSH'.
scriptSHA256 :: S.Script -> ScriptSHA256
{-# INLINE scriptSHA256 #-}
scriptSHA256 = ScriptSHA256 . sha256 . scriptBytes