module Network.Haskoin.Block.Types
( Block(..)
, BlockHeader(..)
, BlockLocator
, GetBlocks(..)
, GetHeaders(..)
, BlockHeaderCount
, BlockHash(..)
, blockHashToHex
, hexToBlockHash
, Headers(..)
, headerHash
, decodeCompact
, encodeCompact
) where
import Control.DeepSeq (NFData, rnf)
import Control.Monad (liftM2, replicateM, forM_, mzero)
import Data.Maybe (fromMaybe)
import Data.Aeson (Value(String), FromJSON, ToJSON, parseJSON, toJSON, withText)
import Data.Bits ((.&.), (.|.), shiftR, shiftL)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS (length, reverse)
import Data.Word (Word32)
import Data.Binary (Binary, get, put)
import Data.Binary.Get (getWord32le)
import Data.Binary.Put (putWord32le)
import Data.String (IsString, fromString)
import Data.String.Conversions (cs)
import Text.Read (readPrec, parens, lexP, pfail)
import qualified Text.Read as Read (Lexeme(Ident, String))
import Network.Haskoin.Util
import Network.Haskoin.Crypto.Hash
import Network.Haskoin.Node.Types
import Network.Haskoin.Transaction.Types
data Block =
Block {
blockHeader :: !BlockHeader
, blockCoinbaseTx :: !CoinbaseTx
, blockTxns :: ![Tx]
} deriving (Eq, Show, Read)
instance NFData Block where
rnf (Block h c ts) = rnf h `seq` rnf c `seq` rnf ts
instance Binary Block where
get = do
header <- get
(VarInt c) <- get
cb <- get
txs <- replicateM (fromIntegral (c1)) get
return $ Block header cb txs
put (Block h cb txs) = do
put h
put $ VarInt $ fromIntegral $ (length txs) + 1
put cb
forM_ txs put
newtype BlockHash = BlockHash { getBlockHash :: Hash256 }
deriving (Eq, Ord)
instance NFData BlockHash where
rnf (BlockHash h) = rnf $ getHash256 h
instance Show BlockHash where
showsPrec d h = showParen (d > 10) $
showString "BlockHash " . shows (blockHashToHex h)
instance Read BlockHash where
readPrec = parens $ do
Read.Ident "BlockHash" <- lexP
Read.String str <- lexP
maybe pfail return $ hexToBlockHash $ cs str
instance IsString BlockHash where
fromString = fromMaybe e . hexToBlockHash . cs where
e = error "Could not read block hash from hex string"
instance Binary BlockHash where
get = BlockHash <$> get
put = put . getBlockHash
instance FromJSON BlockHash where
parseJSON = withText "Block hash" $ \t ->
maybe mzero return $ hexToBlockHash $ cs t
instance ToJSON BlockHash where
toJSON = String . cs . blockHashToHex
blockHashToHex :: BlockHash -> ByteString
blockHashToHex (BlockHash h) = encodeHex $ BS.reverse $ getHash256 h
hexToBlockHash :: ByteString -> Maybe BlockHash
hexToBlockHash hex = do
bs <- BS.reverse <$> decodeHex hex
h <- bsToHash256 bs
return $ BlockHash h
headerHash :: BlockHeader -> BlockHash
headerHash = BlockHash . doubleHash256 . encode'
data BlockHeader =
BlockHeader {
blockVersion :: !Word32
, prevBlock :: !BlockHash
, merkleRoot :: !Hash256
, blockTimestamp :: !Word32
, blockBits :: !Word32
, bhNonce :: !Word32
} deriving (Eq, Show, Read)
instance NFData BlockHeader where
rnf (BlockHeader v p m t b n) =
rnf v `seq` rnf p `seq` rnf m `seq` rnf t `seq` rnf b `seq` rnf n
instance Binary BlockHeader where
get = BlockHeader <$> getWord32le
<*> get
<*> get
<*> getWord32le
<*> getWord32le
<*> getWord32le
put (BlockHeader v p m bt bb n) = do
putWord32le v
put p
put m
putWord32le bt
putWord32le bb
putWord32le n
type BlockLocator = [BlockHash]
data GetBlocks =
GetBlocks {
getBlocksVersion :: !Word32
, getBlocksLocator :: !BlockLocator
, getBlocksHashStop :: !BlockHash
} deriving (Eq, Show, Read)
instance NFData GetBlocks where
rnf (GetBlocks v l h) = rnf v `seq` rnf l `seq` rnf h
instance Binary GetBlocks where
get = GetBlocks <$> getWord32le
<*> (repList =<< get)
<*> get
where
repList (VarInt c) = replicateM (fromIntegral c) get
put (GetBlocks v xs h) = do
putWord32le v
put $ VarInt $ fromIntegral $ length xs
forM_ xs put
put h
data GetHeaders =
GetHeaders {
getHeadersVersion :: !Word32
, getHeadersBL :: !BlockLocator
, getHeadersHashStop :: !BlockHash
} deriving (Eq, Show, Read)
instance NFData GetHeaders where
rnf (GetHeaders v l h) = rnf v `seq` rnf l `seq` rnf h
instance Binary GetHeaders where
get = GetHeaders <$> getWord32le
<*> (repList =<< get)
<*> get
where
repList (VarInt c) = replicateM (fromIntegral c) get
put (GetHeaders v xs h) = do
putWord32le v
put $ VarInt $ fromIntegral $ length xs
forM_ xs put
put h
type BlockHeaderCount = (BlockHeader, VarInt)
data Headers =
Headers {
headersList :: ![BlockHeaderCount]
}
deriving (Eq, Show, Read)
instance NFData Headers where
rnf (Headers l) = rnf l
instance Binary Headers where
get = Headers <$> (repList =<< get)
where
repList (VarInt c) = replicateM (fromIntegral c) action
action = liftM2 (,) get get
put (Headers xs) = do
put $ VarInt $ fromIntegral $ length xs
forM_ xs $ \(a,b) -> put a >> put b
decodeCompact :: Word32 -> Integer
decodeCompact c =
if neg then (res) else res
where
size = fromIntegral $ c `shiftR` 24
neg = (c .&. 0x00800000) /= 0
wrd = c .&. 0x007fffff
res | size <= 3 = (toInteger wrd) `shiftR` (8*(3 size))
| otherwise = (toInteger wrd) `shiftL` (8*(size 3))
encodeCompact :: Integer -> Word32
encodeCompact i
| i < 0 = c3 .|. 0x00800000
| otherwise = c3
where
posi = abs i
s1 = BS.length $ integerToBS posi
c1 | s1 < 3 = posi `shiftL` (8*(3 s1))
| otherwise = posi `shiftR` (8*(s1 3))
(s2,c2) | c1 .&. 0x00800000 /= 0 = (s1 + 1, c1 `shiftR` 8)
| otherwise = (s1, c1)
c3 = fromIntegral $ c2 .|. ((toInteger s2) `shiftL` 24)