module Network.Haskoin.Protocol.MessageHeader ( MessageHeader(..) , MessageCommand(..) ) where import Control.Applicative ((<$>),(<*>)) import Data.Word (Word32) import qualified Data.ByteString as BS ( ByteString , takeWhile ) import Data.Binary (Binary, get, put) import Data.Binary.Get ( getWord32le , getWord32be , getByteString ) import Data.Binary.Put ( putWord32le , putWord32be , putByteString ) import Network.Haskoin.Util (stringToBS, bsToString) import Network.Haskoin.Crypto (CheckSum32) -- | A 'MessageCommand' is included in a 'MessageHeader' in order to identify -- the type of message present in the payload. This allows the message -- de-serialization code to know how to decode a particular message payload. -- Every valid 'Message' constructor has a corresponding 'MessageCommand' -- constructor. data MessageCommand = MCVersion | MCVerAck | MCAddr | MCInv | MCGetData | MCNotFound | MCGetBlocks | MCGetHeaders | MCTx | MCBlock | MCHeaders | MCGetAddr | MCPing | MCPong | MCAlert deriving (Eq, Show) instance Binary MessageCommand where get = go =<< getByteString 12 where go bs = case unpackCommand bs of "version" -> return MCVersion "verack" -> return MCVerAck "addr" -> return MCAddr "inv" -> return MCInv "getdata" -> return MCGetData "notfound" -> return MCNotFound "getblocks" -> return MCGetBlocks "getheaders" -> return MCGetHeaders "tx" -> return MCTx "block" -> return MCBlock "headers" -> return MCHeaders "getaddr" -> return MCGetAddr "ping" -> return MCPing "pong" -> return MCPong "alert" -> return MCAlert _ -> fail "get MessageCommand : Invalid command" put mc = putByteString $ packCommand $ case mc of MCVersion -> "version" MCVerAck -> "verack" MCAddr -> "addr" MCInv -> "inv" MCGetData -> "getdata" MCNotFound -> "notfound" MCGetBlocks -> "getblocks" MCGetHeaders -> "getheaders" MCTx -> "tx" MCBlock -> "block" MCHeaders -> "headers" MCGetAddr -> "getaddr" MCPing -> "ping" MCPong -> "pong" MCAlert -> "alert" packCommand :: String -> BS.ByteString packCommand s = stringToBS $ take 12 $ s ++ repeat '\NUL' unpackCommand :: BS.ByteString -> String unpackCommand bs = bsToString $ BS.takeWhile (/= 0) bs -- | Data type representing the header of a 'Message'. All messages sent between -- nodes contain a message header. data MessageHeader = MessageHeader { -- | Network magic bytes. It is used to differentiate -- messages meant for different bitcoin networks, such as -- prodnet and testnet. headMagic :: !Word32 -- | Message command identifying the type of message. -- included in the payload. , headCmd :: !MessageCommand -- | Byte length of the payload. , headPayloadSize :: !Word32 -- | Checksum of the payload. , headChecksum :: !CheckSum32 } deriving (Eq, Show) instance Binary MessageHeader where get = MessageHeader <$> getWord32be <*> get <*> getWord32le <*> get put (MessageHeader m c l chk) = do putWord32be m put c putWord32le l put chk