-- | 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 (PubHash160 -> PubHash160 -> Bool
(PubHash160 -> PubHash160 -> Bool)
-> (PubHash160 -> PubHash160 -> Bool) -> Eq PubHash160
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PubHash160 -> PubHash160 -> Bool
$c/= :: PubHash160 -> PubHash160 -> Bool
== :: PubHash160 -> PubHash160 -> Bool
$c== :: PubHash160 -> PubHash160 -> Bool
Eq, Eq PubHash160
Eq PubHash160 =>
(PubHash160 -> PubHash160 -> Ordering)
-> (PubHash160 -> PubHash160 -> Bool)
-> (PubHash160 -> PubHash160 -> Bool)
-> (PubHash160 -> PubHash160 -> Bool)
-> (PubHash160 -> PubHash160 -> Bool)
-> (PubHash160 -> PubHash160 -> PubHash160)
-> (PubHash160 -> PubHash160 -> PubHash160)
-> Ord PubHash160
PubHash160 -> PubHash160 -> Bool
PubHash160 -> PubHash160 -> Ordering
PubHash160 -> PubHash160 -> PubHash160
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: PubHash160 -> PubHash160 -> PubHash160
$cmin :: PubHash160 -> PubHash160 -> PubHash160
max :: PubHash160 -> PubHash160 -> PubHash160
$cmax :: PubHash160 -> PubHash160 -> PubHash160
>= :: PubHash160 -> PubHash160 -> Bool
$c>= :: PubHash160 -> PubHash160 -> Bool
> :: PubHash160 -> PubHash160 -> Bool
$c> :: PubHash160 -> PubHash160 -> Bool
<= :: PubHash160 -> PubHash160 -> Bool
$c<= :: PubHash160 -> PubHash160 -> Bool
< :: PubHash160 -> PubHash160 -> Bool
$c< :: PubHash160 -> PubHash160 -> Bool
compare :: PubHash160 -> PubHash160 -> Ordering
$ccompare :: PubHash160 -> PubHash160 -> Ordering
$cp1Ord :: Eq PubHash160
Ord)

-- | Base-16 encoded.
instance Show PubHash160 where
  showsPrec :: Int -> PubHash160 -> ShowS
showsPrec n :: Int
n (PubHash160 b :: ByteString
b) = Bool -> ShowS -> ShowS
showParen (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 10) (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
    String -> ShowS
showString "PubHash160 " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    String -> ShowS
forall a. Monoid a => a -> a -> a
mappend (ByteString -> String
BL8.unpack (Builder -> ByteString
BB.toLazyByteString (ByteString -> Builder
BB.byteStringHex ByteString
b)))

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

-- | Create a 'PubHash160' from its 20 raw bytes.
parsePubHash160 :: B.ByteString -> Maybe PubHash160
{-# INLINE parsePubHash160 #-}
parsePubHash160 :: ByteString -> Maybe PubHash160
parsePubHash160 b :: ByteString
b = do
  Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Int
B.length ByteString
b Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 20)
  PubHash160 -> Maybe PubHash160
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> PubHash160
PubHash160 ByteString
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 :: Pub -> PubHash160
pubHash160 = ByteString -> PubHash160
PubHash160 (ByteString -> PubHash160)
-> (Pub -> ByteString) -> Pub -> PubHash160
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
hash160 (ByteString -> ByteString)
-> (Pub -> ByteString) -> Pub -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pub -> ByteString
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 :: Pub -> PubHash160
pubUncompressedHash160 = ByteString -> PubHash160
PubHash160 (ByteString -> PubHash160)
-> (Pub -> ByteString) -> Pub -> PubHash160
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
hash160 (ByteString -> ByteString)
-> (Pub -> ByteString) -> Pub -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pub -> ByteString
pubUncompressed

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

-- | The 'hash160' of a 'S.Script' as required by 'Bitcoin.Address.P2SH'.
newtype ScriptHash160 = ScriptHash160 B.ByteString
  deriving newtype (ScriptHash160 -> ScriptHash160 -> Bool
(ScriptHash160 -> ScriptHash160 -> Bool)
-> (ScriptHash160 -> ScriptHash160 -> Bool) -> Eq ScriptHash160
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ScriptHash160 -> ScriptHash160 -> Bool
$c/= :: ScriptHash160 -> ScriptHash160 -> Bool
== :: ScriptHash160 -> ScriptHash160 -> Bool
$c== :: ScriptHash160 -> ScriptHash160 -> Bool
Eq, Eq ScriptHash160
Eq ScriptHash160 =>
(ScriptHash160 -> ScriptHash160 -> Ordering)
-> (ScriptHash160 -> ScriptHash160 -> Bool)
-> (ScriptHash160 -> ScriptHash160 -> Bool)
-> (ScriptHash160 -> ScriptHash160 -> Bool)
-> (ScriptHash160 -> ScriptHash160 -> Bool)
-> (ScriptHash160 -> ScriptHash160 -> ScriptHash160)
-> (ScriptHash160 -> ScriptHash160 -> ScriptHash160)
-> Ord ScriptHash160
ScriptHash160 -> ScriptHash160 -> Bool
ScriptHash160 -> ScriptHash160 -> Ordering
ScriptHash160 -> ScriptHash160 -> ScriptHash160
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: ScriptHash160 -> ScriptHash160 -> ScriptHash160
$cmin :: ScriptHash160 -> ScriptHash160 -> ScriptHash160
max :: ScriptHash160 -> ScriptHash160 -> ScriptHash160
$cmax :: ScriptHash160 -> ScriptHash160 -> ScriptHash160
>= :: ScriptHash160 -> ScriptHash160 -> Bool
$c>= :: ScriptHash160 -> ScriptHash160 -> Bool
> :: ScriptHash160 -> ScriptHash160 -> Bool
$c> :: ScriptHash160 -> ScriptHash160 -> Bool
<= :: ScriptHash160 -> ScriptHash160 -> Bool
$c<= :: ScriptHash160 -> ScriptHash160 -> Bool
< :: ScriptHash160 -> ScriptHash160 -> Bool
$c< :: ScriptHash160 -> ScriptHash160 -> Bool
compare :: ScriptHash160 -> ScriptHash160 -> Ordering
$ccompare :: ScriptHash160 -> ScriptHash160 -> Ordering
$cp1Ord :: Eq ScriptHash160
Ord)

-- | Base-16 encoded.
instance Show ScriptHash160 where
  showsPrec :: Int -> ScriptHash160 -> ShowS
showsPrec n :: Int
n (ScriptHash160 b :: ByteString
b) = Bool -> ShowS -> ShowS
showParen (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 10) (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
    String -> ShowS
showString "ScriptHash160 " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    String -> ShowS
forall a. Monoid a => a -> a -> a
mappend (ByteString -> String
BL8.unpack (Builder -> ByteString
BB.toLazyByteString (ByteString -> Builder
BB.byteStringHex ByteString
b)))

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

-- | Create a 'ScriptHash160' from its 20 raw bytes.
parseScriptHash160 :: B.ByteString -> Maybe ScriptHash160
{-# INLINE parseScriptHash160 #-}
parseScriptHash160 :: ByteString -> Maybe ScriptHash160
parseScriptHash160 b :: ByteString
b = do
  Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Int
B.length ByteString
b Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 20)
  ScriptHash160 -> Maybe ScriptHash160
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> ScriptHash160
ScriptHash160 ByteString
b)

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

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

-- | The 'sha256' of a 'S.Script' as required by 'Bitcoin.Address.P2WSH'.
newtype ScriptSHA256 = ScriptSHA256 B.ByteString
  deriving newtype (ScriptSHA256 -> ScriptSHA256 -> Bool
(ScriptSHA256 -> ScriptSHA256 -> Bool)
-> (ScriptSHA256 -> ScriptSHA256 -> Bool) -> Eq ScriptSHA256
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c/= :: ScriptSHA256 -> ScriptSHA256 -> Bool
== :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c== :: ScriptSHA256 -> ScriptSHA256 -> Bool
Eq, Eq ScriptSHA256
Eq ScriptSHA256 =>
(ScriptSHA256 -> ScriptSHA256 -> Ordering)
-> (ScriptSHA256 -> ScriptSHA256 -> Bool)
-> (ScriptSHA256 -> ScriptSHA256 -> Bool)
-> (ScriptSHA256 -> ScriptSHA256 -> Bool)
-> (ScriptSHA256 -> ScriptSHA256 -> Bool)
-> (ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256)
-> (ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256)
-> Ord ScriptSHA256
ScriptSHA256 -> ScriptSHA256 -> Bool
ScriptSHA256 -> ScriptSHA256 -> Ordering
ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256
$cmin :: ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256
max :: ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256
$cmax :: ScriptSHA256 -> ScriptSHA256 -> ScriptSHA256
>= :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c>= :: ScriptSHA256 -> ScriptSHA256 -> Bool
> :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c> :: ScriptSHA256 -> ScriptSHA256 -> Bool
<= :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c<= :: ScriptSHA256 -> ScriptSHA256 -> Bool
< :: ScriptSHA256 -> ScriptSHA256 -> Bool
$c< :: ScriptSHA256 -> ScriptSHA256 -> Bool
compare :: ScriptSHA256 -> ScriptSHA256 -> Ordering
$ccompare :: ScriptSHA256 -> ScriptSHA256 -> Ordering
$cp1Ord :: Eq ScriptSHA256
Ord)

-- | Base-16 encoded.
instance Show ScriptSHA256 where
  showsPrec :: Int -> ScriptSHA256 -> ShowS
showsPrec n :: Int
n (ScriptSHA256 b :: ByteString
b) = Bool -> ShowS -> ShowS
showParen (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 10) (ShowS -> ShowS) -> ShowS -> ShowS
forall a b. (a -> b) -> a -> b
$
    String -> ShowS
showString "ScriptSHA256 " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
    String -> ShowS
forall a. Monoid a => a -> a -> a
mappend (ByteString -> String
BL8.unpack (Builder -> ByteString
BB.toLazyByteString (ByteString -> Builder
BB.byteStringHex ByteString
b)))

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

-- | Create a 'ScriptSHA256' from its 32 raw bytes.
parseScriptSHA256 :: B.ByteString -> Maybe ScriptSHA256
{-# INLINE parseScriptSHA256 #-}
parseScriptSHA256 :: ByteString -> Maybe ScriptSHA256
parseScriptSHA256 b :: ByteString
b = do
  Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (ByteString -> Int
B.length ByteString
b Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 32)
  ScriptSHA256 -> Maybe ScriptSHA256
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> ScriptSHA256
ScriptSHA256 ByteString
b)

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