{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} module Network.Haskoin.Store.Proto where import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy.Char8 as L8 import qualified Data.ByteString.Short as S import qualified Data.Sequence as Seq import Data.Serialize import qualified Data.Text as T import qualified Data.Text.Encoding as E import Data.Version import Haskoin import Network.Haskoin.Store.Data import qualified Network.Haskoin.Store.ProtocolBuffers.Balance as P.Balance import qualified Network.Haskoin.Store.ProtocolBuffers.BalanceList as P.BalanceList import qualified Network.Haskoin.Store.ProtocolBuffers.BlockData as P.BlockData import qualified Network.Haskoin.Store.ProtocolBuffers.BlockDataList as P.BlockDataList import qualified Network.Haskoin.Store.ProtocolBuffers.BlockRef as P.BlockRef import qualified Network.Haskoin.Store.ProtocolBuffers.BlockRef.Block as P.BlockRef.Block import qualified Network.Haskoin.Store.ProtocolBuffers.BlockRef.Block_ref as P.BlockRef.Block_ref import qualified Network.Haskoin.Store.ProtocolBuffers.BlockRef.Mempool as P.BlockRef.Mempool import qualified Network.Haskoin.Store.ProtocolBuffers.BlockTx as P.BlockTx import qualified Network.Haskoin.Store.ProtocolBuffers.BlockTxList as P.BlockTxList import qualified Network.Haskoin.Store.ProtocolBuffers.Error as P.Error import qualified Network.Haskoin.Store.ProtocolBuffers.Event as P.Event import qualified Network.Haskoin.Store.ProtocolBuffers.Event.Type as P.Event.Type import qualified Network.Haskoin.Store.ProtocolBuffers.EventList as P.EventList import qualified Network.Haskoin.Store.ProtocolBuffers.HealthCheck as P.HealthCheck import qualified Network.Haskoin.Store.ProtocolBuffers.Input as P.Input import qualified Network.Haskoin.Store.ProtocolBuffers.Output as P.Output import qualified Network.Haskoin.Store.ProtocolBuffers.Peer as P.Peer import qualified Network.Haskoin.Store.ProtocolBuffers.PeerList as P.PeerList import qualified Network.Haskoin.Store.ProtocolBuffers.Spender as P.Spender import qualified Network.Haskoin.Store.ProtocolBuffers.Transaction as P.Transaction import qualified Network.Haskoin.Store.ProtocolBuffers.TransactionList as P.TransactionList import qualified Network.Haskoin.Store.ProtocolBuffers.TxAfterHeight as P.TxAfterHeight import qualified Network.Haskoin.Store.ProtocolBuffers.TxId as P.TxId import qualified Network.Haskoin.Store.ProtocolBuffers.TxIdList as P.TxIdList import qualified Network.Haskoin.Store.ProtocolBuffers.Unspent as P.Unspent import qualified Network.Haskoin.Store.ProtocolBuffers.UnspentList as P.UnspentList import qualified Network.Haskoin.Store.ProtocolBuffers.XPubBalance as P.XPubBalance import qualified Network.Haskoin.Store.ProtocolBuffers.XPubBalanceList as P.XPubBalanceList import qualified Network.Haskoin.Store.ProtocolBuffers.XPubUnspent as P.XPubUnspent import qualified Network.Haskoin.Store.ProtocolBuffers.XPubUnspentList as P.XPubUnspentList import Paths_haskoin_store as Paths import Text.ProtocolBuffers class ProtoSerial a where protoSerial :: Network -> a -> L.ByteString protoAddress :: Network -> Address -> Utf8 protoAddress net = Utf8 . L.fromStrict . E.encodeUtf8 . addrToString net protoPkScriptAddr :: Network -> ByteString -> Maybe Utf8 protoPkScriptAddr net pks = Utf8 . L.fromStrict . E.encodeUtf8 . addrToString net <$> eitherToMaybe (scriptToAddressBS pks) protoBalance :: Network -> Balance -> P.Balance.Balance protoBalance net Balance { balanceAddress = a , balanceAmount = v , balanceZero = z , balanceUnspentCount = u , balanceTxCount = c , balanceTotalReceived = t } = P.Balance.Balance { P.Balance.address = protoAddress net a , P.Balance.confirmed = v , P.Balance.unconfirmed = z , P.Balance.utxo = u , P.Balance.received = t , P.Balance.txs = c } instance ProtoSerial Balance where protoSerial net = messagePut . protoBalance net protoBalanceList :: Network -> [Balance] -> P.BalanceList.BalanceList protoBalanceList net bs = P.BalanceList.BalanceList {P.BalanceList.balance = Seq.fromList (map (protoBalance net) bs)} instance ProtoSerial [Balance] where protoSerial net = messagePut . protoBalanceList net protoBlockRef :: BlockRef -> P.BlockRef.BlockRef protoBlockRef BlockRef {blockRefHeight = h, blockRefPos = p} = P.BlockRef.BlockRef { P.BlockRef.block_ref = Just (P.BlockRef.Block_ref.Block { P.BlockRef.Block_ref.block = P.BlockRef.Block.Block { P.BlockRef.Block.height = h , P.BlockRef.Block.position = p } }) } protoBlockRef MemRef {memRefTime = PreciseUnixTime t} = P.BlockRef.BlockRef { P.BlockRef.block_ref = Just (P.BlockRef.Block_ref.Mempool { P.BlockRef.Block_ref.mempool = P.BlockRef.Mempool.Mempool {P.BlockRef.Mempool.mempool = t} }) } instance ProtoSerial BlockRef where protoSerial _ = messagePut . protoBlockRef protoBlockTx :: BlockTx -> P.BlockTx.BlockTx protoBlockTx BlockTx {blockTxBlock = b, blockTxHash = t} = P.BlockTx.BlockTx { P.BlockTx.block = protoBlockRef b , P.BlockTx.txid = protoTxId t } instance ProtoSerial BlockTx where protoSerial _ = messagePut . protoBlockTx protoBlockTxList :: [BlockTx] -> P.BlockTxList.BlockTxList protoBlockTxList bs = P.BlockTxList.BlockTxList {P.BlockTxList.blocktx = Seq.fromList (map protoBlockTx bs)} instance ProtoSerial [BlockTx] where protoSerial _ = messagePut . protoBlockTxList protoUnspent :: Network -> Unspent -> P.Unspent.Unspent protoUnspent net Unspent { unspentBlock = b , unspentPoint = OutPoint { outPointHash = t , outPointIndex = i } , unspentAmount = v , unspentScript = s } = P.Unspent.Unspent { P.Unspent.txid = protoTxId t , P.Unspent.index = i , P.Unspent.pkscript = L.fromStrict (S.fromShort s) , P.Unspent.value = v , P.Unspent.block = protoBlockRef b , P.Unspent.address = protoPkScriptAddr net (S.fromShort s) } instance ProtoSerial Unspent where protoSerial net = messagePut . protoUnspent net protoUnspentList :: Network -> [Unspent] -> P.UnspentList.UnspentList protoUnspentList net us = P.UnspentList.UnspentList {P.UnspentList.unspent = Seq.fromList (map (protoUnspent net) us)} instance ProtoSerial [Unspent] where protoSerial net = messagePut . protoUnspentList net protoBlockData :: BlockData -> P.BlockData.BlockData protoBlockData BlockData { blockDataHeight = g , blockDataMainChain = m , blockDataHeader = h , blockDataSize = s , blockDataWeight = e , blockDataTxs = t , blockDataOutputs = o , blockDataFees = f , blockDataSubsidy = y } = P.BlockData.BlockData { P.BlockData.hash = encodeLazy (headerHash h) , P.BlockData.size = s , P.BlockData.height = g , P.BlockData.mainchain = m , P.BlockData.previous = encodeLazy (prevBlock h) , P.BlockData.time = blockTimestamp h , P.BlockData.version = blockVersion h , P.BlockData.bits = blockBits h , P.BlockData.nonce = bhNonce h , P.BlockData.tx = encodeLazy <$> Seq.fromList t , P.BlockData.merkle = encodeLazy (merkleRoot h) , P.BlockData.fees = f , P.BlockData.outputs = o , P.BlockData.subsidy = y , P.BlockData.weight = e } instance ProtoSerial BlockData where protoSerial _ = messagePut . protoBlockData protoBlockDataList :: [BlockData] -> P.BlockDataList.BlockDataList protoBlockDataList bs = P.BlockDataList.BlockDataList {P.BlockDataList.blockdata = Seq.fromList (map protoBlockData bs)} instance ProtoSerial [BlockData] where protoSerial _ = messagePut . protoBlockDataList protoInput :: Network -> Input -> P.Input.Input protoInput _ Coinbase { inputPoint = OutPoint { outPointHash = h , outPointIndex = i } , inputSequence = q , inputSigScript = s , inputWitness = w } = P.Input.Input { P.Input.coinbase = True , P.Input.txid = protoTxId h , P.Input.output = i , P.Input.sigscript = L.fromStrict s , P.Input.sequence = q , P.Input.witness = Seq.fromList (maybe [] (map L.fromStrict) w) , P.Input.value = Nothing , P.Input.pkscript = Nothing , P.Input.address = Nothing } protoInput net Input { inputPoint = OutPoint { outPointHash = h , outPointIndex = i } , inputSequence = q , inputSigScript = s , inputPkScript = k , inputAmount = v , inputWitness = w } = P.Input.Input { P.Input.coinbase = False , P.Input.txid = protoTxId h , P.Input.output = i , P.Input.sigscript = L.fromStrict s , P.Input.sequence = q , P.Input.witness = Seq.fromList (maybe [] (map L.fromStrict) w) , P.Input.value = Just v , P.Input.pkscript = Just (L.fromStrict k) , P.Input.address = protoPkScriptAddr net k } protoSpender :: Spender -> P.Spender.Spender protoSpender Spender {spenderHash = h, spenderIndex = i} = P.Spender.Spender {P.Spender.txid = protoTxId h, P.Spender.input = i} protoOutput :: Network -> Output -> P.Output.Output protoOutput net Output {outputAmount = v, outputScript = k, outputSpender = s} = P.Output.Output { P.Output.pkscript = L.fromStrict k , P.Output.value = v , P.Output.address = protoPkScriptAddr net k , P.Output.spender = protoSpender <$> s } protoTransaction :: Network -> Transaction -> P.Transaction.Transaction protoTransaction net tx@Transaction { transactionBlock = b , transactionVersion = v , transactionLockTime = l , transactionInputs = i , transactionOutputs = o , transactionDeleted = d , transactionRBF = r , transactionTime = t } = P.Transaction.Transaction { P.Transaction.txid = protoTxId (txHash (transactionData tx)) , P.Transaction.size = fromIntegral (B.length (encode (transactionData tx))) , P.Transaction.version = v , P.Transaction.locktime = l , P.Transaction.block = protoBlockRef b , P.Transaction.deleted = d , P.Transaction.fee = if all isCoinbase i then 0 else sum (map inputAmount i) - sum (map outputAmount o) , P.Transaction.rbf = r , P.Transaction.time = t , P.Transaction.inputs = Seq.fromList (map (protoInput net) i) , P.Transaction.outputs = Seq.fromList (map (protoOutput net) o) , P.Transaction.weight = if getSegWit net then Just w else Nothing } where w = let base = B.length $ encode (transactionData tx) {txWitness = []} wit = B.length $ encode (transactionData tx) in fromIntegral $ base * 3 + wit instance ProtoSerial Transaction where protoSerial net = messagePut . protoTransaction net protoTransactionList :: Network -> [Transaction] -> P.TransactionList.TransactionList protoTransactionList net ts = P.TransactionList.TransactionList { P.TransactionList.transaction = Seq.fromList (map (protoTransaction net) ts) } instance ProtoSerial [Transaction] where protoSerial net = messagePut . protoTransactionList net protoTxId :: TxHash -> P.TxId.TxId protoTxId t = P.TxId.TxId {P.TxId.txid = encodeLazy t} protoTxIdList :: [TxHash] -> P.TxIdList.TxIdList protoTxIdList ts = P.TxIdList.TxIdList {P.TxIdList.txid = Seq.fromList (map protoTxId ts)} instance ProtoSerial [TxHash] where protoSerial _ = messagePut . protoTxIdList protoPeer :: PeerInformation -> P.Peer.Peer protoPeer PeerInformation { peerUserAgent = u , peerAddress = a , peerVersion = v , peerServices = s , peerRelay = r } = P.Peer.Peer { P.Peer.useragent = either (const (Utf8 L.empty)) id (toUtf8 (L.fromStrict u)) , P.Peer.address = Utf8 (L8.pack (show a)) , P.Peer.version = v , P.Peer.services = s , P.Peer.relay = r } instance ProtoSerial PeerInformation where protoSerial _ = messagePut . protoPeer protoPeerList :: [PeerInformation] -> P.PeerList.PeerList protoPeerList ps = P.PeerList.PeerList {P.PeerList.peer = Seq.fromList (map protoPeer ps)} instance ProtoSerial [PeerInformation] where protoSerial _ = messagePut . protoPeerList protoXPubBalance :: Network -> XPubBal -> P.XPubBalance.XPubBalance protoXPubBalance net XPubBal {xPubBalPath = p, xPubBal = b} = P.XPubBalance.XPubBalance { P.XPubBalance.path = Seq.fromList p , P.XPubBalance.balance = protoBalance net b } instance ProtoSerial XPubBal where protoSerial net = messagePut . protoXPubBalance net protoXPubBalanceList :: Network -> [XPubBal] -> P.XPubBalanceList.XPubBalanceList protoXPubBalanceList net bs = P.XPubBalanceList.XPubBalanceList { P.XPubBalanceList.xpubbalance = Seq.fromList (map (protoXPubBalance net) bs) } instance ProtoSerial [XPubBal] where protoSerial net = messagePut . protoXPubBalanceList net protoXPubUnspent :: Network -> XPubUnspent -> P.XPubUnspent.XPubUnspent protoXPubUnspent net XPubUnspent {xPubUnspentPath = p, xPubUnspent = u} = P.XPubUnspent.XPubUnspent { P.XPubUnspent.path = Seq.fromList p , P.XPubUnspent.unspent = protoUnspent net u } instance ProtoSerial XPubUnspent where protoSerial net = messagePut . protoXPubUnspent net protoXPubUnspentList :: Network -> [XPubUnspent] -> P.XPubUnspentList.XPubUnspentList protoXPubUnspentList net us = P.XPubUnspentList.XPubUnspentList { P.XPubUnspentList.xpubunspent = Seq.fromList (map (protoXPubUnspent net) us) } instance ProtoSerial [XPubUnspent] where protoSerial net = messagePut . protoXPubUnspentList net protoEvent :: Event -> P.Event.Event protoEvent (EventTx h) = P.Event.Event {P.Event.type' = P.Event.Type.TX, P.Event.id = encodeLazy h} protoEvent (EventBlock h) = P.Event.Event {P.Event.type' = P.Event.Type.BLOCK, P.Event.id = encodeLazy h} instance ProtoSerial Event where protoSerial _ = messagePut . protoEvent protoEventList :: [Event] -> P.EventList.EventList protoEventList es = P.EventList.EventList {P.EventList.event = Seq.fromList (map protoEvent es)} instance ProtoSerial [Event] where protoSerial _ = messagePut . protoEventList protoHealthCheck :: HealthCheck -> P.HealthCheck.HealthCheck protoHealthCheck HealthCheck { healthHeaderBest = maybe_header_hash , healthHeaderHeight = maybe_header_height , healthBlockBest = maybe_block_hash , healthBlockHeight = maybe_block_height , healthPeers = maybe_peer_count , healthNetwork = network_name , healthOK = ok , healthSynced = synced } = P.HealthCheck.HealthCheck { P.HealthCheck.ok = ok , P.HealthCheck.synced = synced , P.HealthCheck.version = Utf8 (L8.pack (showVersion Paths.version)) , P.HealthCheck.net = Utf8 (L8.pack network_name) , P.HealthCheck.peers = fromIntegral <$> maybe_peer_count , P.HealthCheck.headers_hash = encodeLazy <$> maybe_header_hash , P.HealthCheck.headers_height = maybe_header_height , P.HealthCheck.blocks_hash = encodeLazy <$> maybe_block_hash , P.HealthCheck.blocks_height = maybe_block_height } instance ProtoSerial HealthCheck where protoSerial _ = messagePut . protoHealthCheck protoTxAfterHeight :: TxAfterHeight -> P.TxAfterHeight.TxAfterHeight protoTxAfterHeight (TxAfterHeight b) = P.TxAfterHeight.TxAfterHeight b instance ProtoSerial TxAfterHeight where protoSerial _ = messagePut . protoTxAfterHeight protoExcept :: Except -> P.Error.Error protoExcept = P.Error.Error . Utf8 . L.fromStrict . E.encodeUtf8 . T.pack . show instance ProtoSerial Except where protoSerial _ = messagePut . protoExcept instance ProtoSerial TxId where protoSerial _ (TxId h) = messagePut (protoTxId h)