module Network.Bitcoin.RawTransaction ( Client
, getClient
, RawTransaction
, getRawTransaction
, TxIn(..)
, TxnOutputType(..)
, ScriptPubKey(..)
, ScriptSig(..)
, TxOut(..)
, BlockInfo(..)
, RawTransactionInfo(..)
, getRawTransactionInfo
, UnspentTransaction(..)
, listUnspent
, createRawTransaction
, DecodedRawTransaction(..)
, decodeRawTransaction
, WhoCanPay(..)
, RawSignedTransaction(..)
, signRawTransaction
, sendRawTransaction
) where
import Control.Applicative
import Control.Monad
import Data.Aeson as A
import Data.Aeson.Types as AT
import Data.Maybe
import qualified Data.Vector as V
import Network.Bitcoin.Internal
type RawTransaction = HexString
getRawTransaction :: Client -> TransactionID -> IO RawTransaction
getRawTransaction client txid =
callApi client "getrawtransaction" [ tj txid, tj verbose ]
where verbose = 0 :: Int
data TxIn = TxCoinbase { txCoinbase :: HexString
}
| TxIn {
txInId :: TransactionID
, numOut :: Integer
, scriptSig :: ScriptSig
, txSequence :: Integer
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON TxIn where
parseJSON (Object o) = parseCB <|> parseTxIn
where
parseCB = TxCoinbase <$> o .: "coinbase"
parseTxIn = TxIn <$> o .: "txid"
<*> o .: "vout"
<*> o .: "scriptSig"
<*> o .: "sequence"
parseJSON _ = mzero
data TxnOutputType = TxnPubKey
| TxnPubKeyHash
| TxnScriptHash
| TxnMultisig
deriving ( Show, Read, Ord, Eq )
instance FromJSON TxnOutputType where
parseJSON (A.String s) | s == "pubkey" = return TxnPubKey
| s == "pubkeyhash" = return TxnPubKeyHash
| s == "scripthash" = return TxnScriptHash
| s == "multisig" = return TxnMultisig
| otherwise = mzero
parseJSON _ = mzero
data TxOut =
TxOut {
txoutVal :: BTC
, scriptPubKey :: ScriptPubKey
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON TxOut where
parseJSON (Object o) = TxOut <$> o .: "value"
<*> o .: "scriptPubKey"
parseJSON _ = mzero
data ScriptPubKey = NonStandardScriptPubKey {
nspkAsm :: HexString
, nspkHex :: HexString
}
| StandardScriptPubKey {
sspkAsm :: HexString
, sspkHex :: HexString
, requiredSigs :: Integer
, sspkType :: TxnOutputType
, sspkAddresses :: Vector Address
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON ScriptPubKey where
parseJSON (Object o) = parseStandard <|> parseNonstandard
where
parseStandard = StandardScriptPubKey <$> o .: "asm"
<*> o .: "hex"
<*> o .: "reqSigs"
<*> o .: "type"
<*> o .: "addresses"
parseNonstandard = NonStandardScriptPubKey <$> o .: "asm"
<*> o .: "hex"
parseJSON _ = mzero
data ScriptSig = ScriptSig { sigAsm :: HexString
, sigHex :: HexString
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON ScriptSig where
parseJSON (Object o) = ScriptSig <$> o .: "asm"
<*> o .: "hex"
parseJSON _ = mzero
data BlockInfo = ConfirmedBlock {
confirmations :: Integer
, cbTime :: Integer
, blockTime :: Integer
}
| UnconfirmedBlock
deriving ( Show, Read, Ord, Eq )
instance FromJSON BlockInfo where
parseJSON (Object o) = parseConfirmed <|> parseUnconfirmed
where
parseConfirmed = ConfirmedBlock <$> o .: "confirmations"
<*> o .: "time"
<*> o .: "blocktime"
parseUnconfirmed = do c <- o .: "confirmations" :: AT.Parser Integer
guard $ c == 0
return UnconfirmedBlock
parseJSON _ = mzero
data RawTransactionInfo =
RawTransactionInfo {
raw :: RawTransaction
, txnVersion :: Integer
, txnLockTime :: Integer
, vin :: Vector TxIn
, vout :: Vector TxOut
, rawTxBlockHash :: HexString
, rawBlockInfo :: BlockInfo
}
deriving ( Show, Read, Ord, Eq )
instance FromJSON RawTransactionInfo where
parseJSON v@(Object o) = RawTransactionInfo <$> o .: "hex"
<*> o .: "version"
<*> o .: "locktime"
<*> o .: "vin"
<*> o .: "vout"
<*> o .: "blockhash"
<*> parseJSON v
parseJSON _ = mzero
getRawTransactionInfo :: Client -> TransactionID -> IO RawTransactionInfo
getRawTransactionInfo client txid =
callApi client "getrawtransaction" [ tj txid, tj verbose ]
where verbose = 1 :: Int
data UnspentTransaction =
UnspentTransaction { unspentTransactionId :: TransactionID
, outIdx :: Integer
, unspentAddress :: Address
, unspentScriptPubKey :: HexString
, redeemScript :: Maybe HexString
, unspentAmount :: BTC
, usConfirmations :: Integer
} deriving ( Show, Eq )
instance FromJSON UnspentTransaction where
parseJSON (Object o) = UnspentTransaction <$> o .: "txid"
<*> o .: "vout"
<*> o .: "address"
<*> o .: "scriptPubKey"
<*> o .:? "redeemScript"
<*> o .: "amount"
<*> o .: "confirmations"
parseJSON _ = mzero
instance ToJSON UnspentTransaction where
toJSON (UnspentTransaction{..}) = object [ "txid" .= unspentTransactionId
, "vout" .= outIdx
]
listUnspent :: Client
-> Maybe Int
-> Maybe Int
-> Vector Address
-> IO (Vector UnspentTransaction)
listUnspent client mmin mmax vaddrs =
let min' = fromMaybe 1 mmin
max' = fromMaybe 9999999 mmax
in callApi client "listunspent" [ tj min', tj max', tj vaddrs ]
createRawTransaction :: Client
-> Vector UnspentTransaction
-> Vector (Address, BTC)
-> IO HexString
createRawTransaction client us tgts =
callApi client "createrawtransaction" [ tj us, tj $ AA tgts ]
data DecodedRawTransaction =
DecodedRawTransaction {
decRaw :: RawTransaction
, decTxnVersion :: Integer
, decTxnLockTime :: Integer
, decVin :: Vector TxIn
, decVout :: Vector TxOut
}
instance FromJSON DecodedRawTransaction where
parseJSON (Object o) = DecodedRawTransaction <$> o .: "hex"
<*> o .: "version"
<*> o .: "locktime"
<*> o .: "vin"
<*> o .: "vout"
parseJSON _ = mzero
decodeRawTransaction :: Client -> RawTransaction -> IO DecodedRawTransaction
decodeRawTransaction client tx = callApi client "decoderawtransaction" [ tj tx ]
newtype UnspentForSigning = UFS UnspentTransaction
instance ToJSON UnspentForSigning where
toJSON (UFS (UnspentTransaction{..}))
| isNothing redeemScript =
object [ "txid" .= unspentTransactionId
, "vout" .= outIdx
, "scriptPubKey" .= unspentScriptPubKey
]
| otherwise =
object [ "txid" .= unspentTransactionId
, "vout" .= outIdx
, "scriptPubKey" .= unspentScriptPubKey
, "redeemScript" .= fromJust redeemScript
]
data WhoCanPay = All
| AllOrAnyoneCanPay
| None
| NoneOrAnyoneCanPay
| Single
| SingleOrAnyoneCanPay
toString :: WhoCanPay -> Text
toString All = "ALL"
toString AllOrAnyoneCanPay = "ALL|ANYONECANPAY"
toString None = "NONE"
toString NoneOrAnyoneCanPay = "NONE|ANYONECANPAY"
toString Single = "SINGLE"
toString SingleOrAnyoneCanPay = "SINGLE|ANYONECANPAY"
data RawSignedTransaction =
RawSignedTransaction { rawSigned :: HexString
, hasCompleteSigSet :: Bool
}
instance FromJSON RawSignedTransaction where
parseJSON (Object o) = RawSignedTransaction <$> o .: "hex"
<*> o .: "complete"
parseJSON _ = mzero
signRawTransaction :: Client
-> RawTransaction
-> Maybe (Vector UnspentTransaction)
-> Maybe (Vector HexString)
-> Maybe WhoCanPay
-> IO RawSignedTransaction
signRawTransaction client rt us' privkeys wcp =
let us = V.map UFS <$> us' :: Maybe (Vector UnspentForSigning)
in callApi client "signrawtransaction" [ tj rt
, tj us
, tj privkeys
, tj . toString $ fromMaybe All wcp
]
sendRawTransaction :: Client -> RawTransaction -> IO TransactionID
sendRawTransaction client rt = callApi client "sendrawtransaction" [ tj rt ]