bitcoin-tx-0.12.1: src/Data/Bitcoin/Transaction/Types.hs
{-# LANGUAGE FlexibleInstances #-}
module Data.Bitcoin.Transaction.Types where
import Control.Applicative ( (<$>)
, (<*>) )
import Control.Monad ( liftM2
, replicateM
, forM_
, unless )
import Data.Word ( Word32
, Word64 )
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Bits (shiftL, shiftR)
import Data.Binary ( Binary, get, put, encode, decode )
import Data.Binary.Get ( getByteString
, getWord32le
, getWord64le
, getWord64be )
import Data.Binary.Put ( putByteString
, putWord32le
, putWord64le
, putWord64be )
import Data.Bitcoin.Types ( VarInt (..) )
import qualified Data.Bitcoin.Script as Btc ( Script (..) )
data TxnOutputType = TxnPubKey -- ^ JSON of "pubkey" received.
| TxnPubKeyHash -- ^ JSON of "pubkeyhash" received.
| TxnScriptHash -- ^ JSON of "scripthash" received.
| TxnMultisig -- ^ JSON of "multisig" received.
deriving ( Show, Read, Ord, Eq )
data TransactionHash = TransactionHash Integer
deriving ( Show, Read, Eq )
instance Binary TransactionHash where
get = do
a <- fromIntegral <$> getWord64be
b <- fromIntegral <$> getWord64be
c <- fromIntegral <$> getWord64be
d <- fromIntegral <$> getWord64be
return $ TransactionHash ((a `shiftL` 192) + (b `shiftL` 128) + (c `shiftL` 64) + d)
put (TransactionHash i) = do
putWord64be $ fromIntegral (i `shiftR` 192)
putWord64be $ fromIntegral (i `shiftR` 128)
putWord64be $ fromIntegral (i `shiftR` 64)
putWord64be $ fromIntegral i
-- | 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 :: TransactionHash,
-- | The position of the specific output in the transaction.
-- The first output position is 0.
outPointIndex :: !Word32
} deriving (Read, Show, Eq)
instance Binary OutPoint where
get = do
(h,i) <- liftM2 (,) get getWord32le
return $ OutPoint h i
put (OutPoint h i) = put h >> putWord32le i
-- | Data type representing a transaction input.
data TransactionIn = TransactionIn {
-- | Reference the previous transaction output (hash + position)
prevOutput :: OutPoint,
-- | Script providing the requirements of the previous transaction
-- output to spend those coins.
scriptInput :: Btc.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, Read)
instance Binary TransactionIn where
get = do
o <- get
(VarInt len) <- get
scriptBs <- getByteString (fromIntegral len)
s <- getWord32le
let i = decode $ BSL.fromStrict scriptBs
return $ TransactionIn o i s
put (TransactionIn o i s) = do
let scriptBs = BSL.toStrict $ encode i
put o
put $ VarInt $ fromIntegral $ BS.length scriptBs
putByteString scriptBs
putWord32le s
-- | Data type representing a transaction output.
data TransactionOut = TransactionOut {
-- | Transaction output value.
outValue :: Word64,
-- | Script specifying the conditions to spend this output.
scriptOutput :: Btc.Script
} deriving (Eq, Show, Read)
instance Binary TransactionOut where
get = do
val <- getWord64le
(VarInt len) <- get
scriptBs <- getByteString (fromIntegral len)
let s = decode $ BSL.fromStrict scriptBs
return $ TransactionOut val s
put (TransactionOut o s) = do
let scriptBs = BSL.toStrict $ encode s
putWord64le o
put $ VarInt $ fromIntegral $ BS.length scriptBs
putByteString scriptBs
-- | Data type representing a bitcoin transaction
data Transaction = Transaction {
-- | Transaction data format version
txVersion :: Word32,
-- | List of transaction inputs
txIn :: [TransactionIn],
-- | List of transaction outputs
txOut :: [TransactionOut],
-- | The block number of timestamp at which this transaction is locked
txLockTime :: Word32
} deriving (Eq, Show, Read)
instance Binary Transaction where
get = Transaction <$> getWord32le
<*> (replicateList =<< get)
<*> (replicateList =<< get)
<*> getWord32le
where
replicateList (VarInt c) = replicateM (fromIntegral c) get
put (Transaction 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 Coinbase = Coinbase {
-- | Transaction data format version.
cbVersion :: Word32,
-- | Previous outpoint. This is ignored for
-- coinbase transactions but preserved for computing
-- the correct txid.
cbPrevOutput :: OutPoint,
-- | Data embedded inside the coinbase transaction.
cbData :: BS.ByteString,
-- | Transaction sequence number. This is ignored for
-- coinbase transactions but preserved for computing
-- the correct txid.
cbInSequence :: Word32,
-- | List of transaction outputs.
cbOut :: [TransactionOut],
-- | The block number of timestamp at which this
-- transaction is locked.
cbLockTime :: Word32
} deriving (Eq, Show, Read)
instance Binary Coinbase where
get = do
v <- getWord32le
(VarInt len) <- get
unless (len == 1) $ fail "Coinbase 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 $ Coinbase v op cb sq os lt
put (Coinbase 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