haskoin-protocol-0.0.1: Network/Haskoin/Protocol/Tx.hs
module Network.Haskoin.Protocol.Tx
( Tx(..)
, TxIn(..)
, TxOut(..)
, OutPoint(..)
, CoinbaseTx(..)
, txid
, encodeTxid
, decodeTxid
) where
import Control.Monad (replicateM, forM_, liftM2, unless)
import Control.Applicative ((<$>),(<*>))
import Data.Word (Word32, Word64)
import Data.Binary (Binary, get, put)
import Data.Binary.Get
( getWord32le
, getWord64le
, getByteString
, skip
)
import Data.Binary.Put
( putWord32le
, putWord64le
, putByteString
)
import qualified Data.ByteString as BS
( ByteString
, length
, reverse
)
import Network.Haskoin.Protocol.VarInt
import Network.Haskoin.Protocol.Script
import Network.Haskoin.Crypto (Hash256, doubleHash256)
import Network.Haskoin.Util (bsToHex, hexToBS, encode', decodeToMaybe)
-- | Data type representing a bitcoin transaction
data Tx =
Tx {
-- | Transaction data format version
txVersion :: !Word32
-- | List of transaction inputs
, txIn :: ![TxIn]
-- | List of transaction outputs
, txOut :: ![TxOut]
-- | The block number of timestamp at which this transaction is locked
, txLockTime :: !Word32
} deriving (Eq, Show)
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
-- | Data type representing the coinbase transaction of a 'Block'. Coinbase
-- transactions are special types of transactions which are created by miners
-- when they find a new block. Coinbase transactions have no inputs. They have
-- outputs sending the newly generated bitcoins together with all the block's
-- fees to a bitcoin address (usually the miners address). Data can be embedded
-- in a Coinbase transaction which can be chosen by the miner of a block. This
-- data also typically contains some randomness which is used, together with
-- the nonce, to find a partial hash collision on the block's hash.
data CoinbaseTx =
CoinbaseTx {
-- | Transaction data format version.
cbVersion :: !Word32
-- | Data embedded inside the coinbase transaction.
, cbData :: !BS.ByteString
-- | List of transaction outputs.
, cbOut :: ![TxOut]
-- | The block number of timestamp at which this
-- transaction is locked.
, cbLockTime :: !Word32
} deriving (Eq, Show)
instance Binary CoinbaseTx where
get = CoinbaseTx <$> getWord32le
<*> (readCoinbase =<< get)
<*> (replicateList =<< get)
<*> getWord32le
where
readCoinbase (VarInt _) = do
skip 36 -- skip OutPoint
(VarInt len) <- get
coinbase <- getByteString (fromIntegral len)
skip 4 -- skip sequence
return coinbase
replicateList (VarInt c) = replicateM (fromIntegral c) get
put (CoinbaseTx v cb os l) = do
putWord32le v
put $ VarInt 1
put $ OutPoint 0 maxBound
put $ VarInt $ fromIntegral $ BS.length cb
putByteString cb
putWord32le 0 --sequence number
put $ VarInt $ fromIntegral $ length os
forM_ os put
putWord32le l
-- | Data type representing a transaction input.
data TxIn =
TxIn {
-- | Reference the previous transaction output (hash + position)
prevOutput :: !OutPoint
-- | Script providing the requirements of the previous transaction
-- output to spend those coins.
, scriptInput :: !Script
-- | Transaction version as defined by the sender of the
-- transaction. The intended use is for replacing transactions with
-- new information before the transaction is included in a block.
, txInSequence :: !Word32
} deriving (Eq, Show)
instance Binary TxIn where
get = TxIn <$> get <*> get <*> getWord32le
put (TxIn o s q) = put o >> put s >> putWord32le q
-- | Data type representing a transaction output.
data TxOut =
TxOut {
-- | Transaction output value.
outValue :: !Word64
-- | Script specifying the conditions to spend this output.
, scriptOutput :: !Script
} deriving (Eq, Show)
instance Binary TxOut where
get = do
val <- getWord64le
unless (val <= 2100000000000000) $ fail $
"Invalid TxOut value: " ++ (show val)
TxOut val <$> get
put (TxOut o s) = putWord64le o >> put s
-- | The OutPoint is used inside a transaction input to reference the previous
-- transaction output that it is spending.
data OutPoint =
OutPoint {
-- | The hash of the referenced transaction.
outPointHash :: !Hash256
-- | The position of the specific output in the transaction.
-- The first output position is 0.
, outPointIndex :: !Word32
} deriving Eq
instance Show OutPoint where
show (OutPoint h i) = show ("txid = " ++ h',"index = " ++ (show i))
where
h' = encodeTxid h
instance Binary OutPoint where
get = do
(h,i) <- liftM2 (,) get getWord32le
unless (i <= 2147483647) $ fail $
"Invalid OutPoint index: " ++ (show i)
return $ OutPoint h i
put (OutPoint h i) = put h >> putWord32le i
-- | Computed the hash of a transaction.
txid :: Tx -> Hash256
txid = doubleHash256 . encode'
-- | Encodes a transaction hash as little endian in HEX format.
-- This is mostly used for displaying transaction ids. Internally, these ids
-- are handled as big endian but are transformed to little endian when
-- displaying them.
encodeTxid :: Hash256 -> String
encodeTxid = bsToHex . BS.reverse . encode'
-- | Decodes a little endian transaction hash in HEX format.
decodeTxid :: String -> Maybe Hash256
decodeTxid = (decodeToMaybe . BS.reverse =<<) . hexToBS