module Network.Haskoin.Transaction.Types
( Tx(..)
, TxIn(..)
, TxOut(..)
, OutPoint(..)
, CoinbaseTx(..)
, TxHash(..)
, txHash
, hexToTxHash
, txHashToHex
, nosigTxHash
, cbHash
) where
import Control.DeepSeq (NFData, rnf)
import Control.Monad (liftM2, replicateM, forM_, unless, mzero, (<=<))
import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText)
import Data.Word (Word32, Word64)
import Data.Binary (Binary, get, put)
import Data.Binary.Get
( getWord32le
, getWord64le
, getByteString
)
import Data.Binary.Put
( putWord32le
, putWord64le
, putByteString
)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
( length
, empty
, reverse
)
import Data.Maybe (fromMaybe)
import Data.String (IsString, fromString)
import Data.String.Conversions (cs)
import Text.Read (readPrec, parens, lexP, pfail)
import qualified Text.Read as Read (Lexeme(Ident, String))
import Network.Haskoin.Util
import Network.Haskoin.Crypto.Hash
import Network.Haskoin.Node.Types
newtype TxHash = TxHash { getTxHash :: Hash256 }
deriving (Eq, Ord)
instance NFData TxHash where
rnf = rnf . getHash256 . getTxHash
instance Read TxHash where
readPrec = parens $ do
Read.Ident "TxHash" <- lexP
Read.String str <- lexP
maybe pfail return $ hexToTxHash $ cs str
instance Show TxHash where
showsPrec d h = showParen (d > 10) $
showString "TxHash " . shows (txHashToHex h)
instance IsString TxHash where
fromString =
TxHash . fromMaybe e . bsToHash256
. BS.reverse . fromMaybe e' . decodeHex . cs
where
e = error "Could not read transaction hash from decoded hex string"
e' = error "Colud not decode hex string with transaction hash"
instance Binary TxHash where
get = TxHash <$> get
put = put . getTxHash
txHash :: Tx -> TxHash
txHash = TxHash . doubleHash256 . encode'
nosigTxHash :: Tx -> TxHash
nosigTxHash tx =
txHash tx{ txIn = map clearInput $ txIn tx }
where
clearInput ti = ti{ scriptInput = BS.empty }
cbHash :: CoinbaseTx -> TxHash
cbHash = TxHash . doubleHash256 . encode'
txHashToHex :: TxHash -> ByteString
txHashToHex (TxHash h) = encodeHex $ BS.reverse $ getHash256 h
hexToTxHash :: ByteString -> Maybe TxHash
hexToTxHash hex = do
bs <- BS.reverse <$> decodeHex hex
h <- bsToHash256 bs
return $ TxHash h
instance FromJSON TxHash where
parseJSON = withText "Transaction id" $ \t ->
maybe mzero return $ hexToTxHash $ cs t
instance ToJSON TxHash where
toJSON = String . cs . txHashToHex
data Tx =
Tx {
txVersion :: !Word32
, txIn :: ![TxIn]
, txOut :: ![TxOut]
, txLockTime :: !Word32
} deriving (Eq)
instance Show Tx where
showsPrec d tx = showParen (d > 10) $
showString "Tx " . shows (encodeHex $ encode' tx)
instance Read Tx where
readPrec = parens $ do
Read.Ident "Tx" <- lexP
Read.String str <- lexP
maybe pfail return $ decodeToMaybe =<< decodeHex (cs str)
instance IsString Tx where
fromString =
fromMaybe e . (decodeToMaybe <=< decodeHex) . cs
where
e = error "Could not read transaction from hex string"
instance NFData Tx where
rnf (Tx v i o l) = rnf v `seq` rnf i `seq` rnf o `seq` rnf l
instance Binary Tx where
get =
Tx <$> getWord32le
<*> (replicateList =<< get)
<*> (replicateList =<< get)
<*> getWord32le
where
replicateList (VarInt c) = replicateM (fromIntegral c) get
put (Tx v is os l) = do
putWord32le v
put $ VarInt $ fromIntegral $ length is
forM_ is put
put $ VarInt $ fromIntegral $ length os
forM_ os put
putWord32le l
instance FromJSON Tx where
parseJSON = withText "Tx" $
maybe mzero return . (decodeToMaybe <=< decodeHex) . cs
instance ToJSON Tx where
toJSON = String . cs . encodeHex . encode'
data CoinbaseTx = CoinbaseTx
{
cbVersion :: !Word32
, cbPrevOutput :: !OutPoint
, cbData :: !ByteString
, cbInSequence :: !Word32
, cbOut :: ![TxOut]
, cbLockTime :: !Word32
} deriving (Eq, Show, Read)
instance NFData CoinbaseTx where
rnf (CoinbaseTx v p d i o l) =
rnf v `seq` rnf p `seq` rnf d `seq` rnf i `seq` rnf o `seq` rnf l
instance Binary CoinbaseTx where
get = do
v <- getWord32le
(VarInt len) <- get
unless (len == 1) $ fail "CoinbaseTx get: Input size is not 1"
op <- get
(VarInt cbLen) <- get
cb <- getByteString (fromIntegral cbLen)
sq <- getWord32le
(VarInt oLen) <- get
os <- replicateM (fromIntegral oLen) get
lt <- getWord32le
return $ CoinbaseTx v op cb sq os lt
put (CoinbaseTx v op cb sq os lt) = do
putWord32le v
put $ VarInt 1
put op
put $ VarInt $ fromIntegral $ BS.length cb
putByteString cb
putWord32le sq
put $ VarInt $ fromIntegral $ length os
forM_ os put
putWord32le lt
data TxIn =
TxIn {
prevOutput :: !OutPoint
, scriptInput :: !ByteString
, txInSequence :: !Word32
} deriving (Eq, Show, Read)
instance NFData TxIn where
rnf (TxIn p i s) = rnf p `seq` rnf i `seq` rnf s
instance Binary TxIn where
get =
TxIn <$> get <*> (readBS =<< get) <*> getWord32le
where
readBS (VarInt len) = getByteString $ fromIntegral len
put (TxIn o s q) = do
put o
put $ VarInt $ fromIntegral $ BS.length s
putByteString s
putWord32le q
data TxOut =
TxOut {
outValue :: !Word64
, scriptOutput :: !ByteString
} deriving (Eq, Show, Read)
instance NFData TxOut where
rnf (TxOut v o) = rnf v `seq` rnf o
instance Binary TxOut where
get = do
val <- getWord64le
(VarInt len) <- get
TxOut val <$> (getByteString $ fromIntegral len)
put (TxOut o s) = do
putWord64le o
put $ VarInt $ fromIntegral $ BS.length s
putByteString s
data OutPoint = OutPoint
{
outPointHash :: !TxHash
, outPointIndex :: !Word32
} deriving (Read, Show, Eq)
instance NFData OutPoint where
rnf (OutPoint h i) = rnf h `seq` rnf i
instance FromJSON OutPoint where
parseJSON = withText "OutPoint" $
maybe mzero return . (decodeToMaybe <=< decodeHex) . cs
instance ToJSON OutPoint where
toJSON = String . cs . encodeHex . encode'
instance Binary OutPoint where
get = do
(h,i) <- liftM2 (,) get getWord32le
return $ OutPoint h i
put (OutPoint h i) = put h >> putWord32le i