Skip to content

Commit

Permalink
Decode headers, test this & some scaffold
Browse files Browse the repository at this point in the history
  • Loading branch information
Anviking committed May 14, 2019
1 parent c1ca651 commit a31b823
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 24 deletions.
8 changes: 3 additions & 5 deletions lib/jormungandr/cardano-wallet-jormungandr.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@ library
build-depends:
base
, cardano-wallet-core
-- , binary
-- , bytestring
-- , cardano-crypto
-- , cryptonite
-- , digest
, binary
, bytestring
, memory
, text
, text-class
hs-source-dirs:
Expand Down
173 changes: 172 additions & 1 deletion lib/jormungandr/src/Cardano/Wallet/Binary/Jormungandr.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}

-- |
-- Copyright: © 2018-2019 IOHK
Expand All @@ -8,5 +11,173 @@
-- The format is for the Shelley era as implemented by the Jörmungandr node.

module Cardano.Wallet.Binary.Jormungandr
(
( getBlockHeader
, Message (..)
, BlockHeader (..)

-- * Re-export
, runGet

-- * For dev
, genesisBlock
, dev
) where

import Prelude

import Cardano.Wallet.Primitive.Types
( SlotId (..) )
import Data.Binary.Get
( Get
, getByteString
, getWord16be
, getWord32be
, getWord8
, isEmpty
, isolate
, runGet
, skip
)
import Data.ByteArray.Encoding
( Base (Base16), convertFromBase )
import Data.ByteString
( ByteString )
import Data.Word
( Word16, Word32 )

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL


-- | Messages is what the block body consists of.
--
-- Every message is prefixed with a message header.
--
-- Following, as closely as possible:
-- https://github.com/input-output-hk/rust-cardano/blob/e0616f13bebd6b908320bddb1c1502dea0d3305a/chain-impl-mockchain/src/message/mod.rs#L22-L29
data Message
= Initial [ConfigParam]
| OldUtxoDeclaration TODO
| Transaction TODO
| Certificate TODO
| UpdateProposal SignedUpdateProposal
| UpdateVote SignedVote
| UnimplementedMessage Int -- For development. Remove later.
deriving Show


data BlockHeader = BlockHeader
{ version :: Word16
, contentSize :: Word32
, slot :: SlotId
, chainLength :: Word32
, contentHash :: ByteString
, parentHeaderHash :: Maybe ByteString
} deriving (Show, Eq)

data Block = Block BlockHeader [Message]
deriving Show


data SignedUpdateProposal = SignedUpdateProposal
deriving Show
data TODO = TODO
deriving Show
data SignedVote = SignedVote
deriving Show
data ConfigParam = ConfigParam
deriving Show

{-# ANN getBlockHeader ("HLint: ignore Use <$>" :: String) #-}
getBlockHeader :: Get BlockHeader
getBlockHeader = (fromIntegral <$> getWord16be) >>= \s -> isolate s $ do
version <- getWord16be
sizeOfContent <- getWord32be

slotEpoch <- fromIntegral <$> getWord32be
slotId <- fromIntegral <$> getWord32be

chainLength <- getWord32be

contentHash <- getByteString 32 -- or 256 bits

parentHeaderHash <- getParentHeaderHash

-- TODO: Handle special case for BFT
-- TODO: Handle special case for Praos/Genesis

return $
BlockHeader
version
sizeOfContent
(SlotId slotId slotEpoch)
chainLength
contentHash
parentHeaderHash

getBlock :: Get Block
getBlock = do
header <- getBlockHeader
msgs <- isolate (fromIntegral $ contentSize header)
$ whileM (not <$> isEmpty) getBlockContent
return $ Block header msgs

getBlockContent :: Get Message
getBlockContent = do
size <- getWord16be
contentType <- fromIntegral <$> getWord8

let msgSize = fromIntegral size - 1

let unimpl = skip msgSize >> return (UnimplementedMessage contentType)

isolate msgSize $ case contentType of
0 -> unimpl
1 -> unimpl
2 -> unimpl
3 -> unimpl
4 -> unimpl
5 -> unimpl
other -> fail $ "Unexpected content type tag " ++ show other

getParentHeaderHash :: Get (Maybe ByteString)
getParentHeaderHash = getByteString 32 >>= \case
a | a == BS.pack (replicate 32 0)
-> return Nothing
| otherwise
-> return $ Just a


{-------------------------------------------------------------------------------
For development
-------------------------------------------------------------------------------}

dev :: IO ()
dev = print $ runGet getBlock genesisBlock

genesisBlock :: BL.ByteString
genesisBlock = either error BL.fromStrict $ convertFromBase @ByteString Base16
"005200000000009f000000000000000000000000ffadebfecd59d9eaa12e903a\
\d58100f7c1e35899739c3d05d022835c069d2b4f000000000000000000000000\
\00000000000000000000000000000000000000000047000048000000005cc1c2\
\4900810200c200010108000000000000087001410f01840000000a01e030a694\
\b80dbba2d1b8a4b55652b03d96315c8414b054fa737445ac2d2a865c76002604\
\0001000000ff0005000006000000000000000000000000000000000000000000\
\0000000000002c020001833324c37869c122689a35917df53a4f2294a3a52f68\
\5e05f5f8e53b87e7ea452f000000000000000e"


{-------------------------------------------------------------------------------
Helpers
-------------------------------------------------------------------------------}

whileM :: Monad m => m Bool -> m a -> m [a]
whileM p f = go
where
go = do
x <- p
if x then do
x' <- f
xs <- go
return (x' : xs)
else return []
38 changes: 20 additions & 18 deletions lib/jormungandr/test/unit/Cardano/Wallet/Binary/JormungandrSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,41 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Wallet.Binary.JormungandrSpec
( spec
) where
module Cardano.Wallet.Binary.JormungandrSpec (spec) where

import Prelude

import Cardano.Wallet.Binary.Jormungandr
()
import Data.ByteString
( ByteString )

( BlockHeader (..), getBlockHeader, runGet )
import Cardano.Wallet.Primitive.Types
( BlockHeader (..), Hash (..), SlotId (..) )

( SlotId (..) )
import Data.ByteArray.Encoding
( Base (Base16), convertFromBase )
import Data.ByteString
( ByteString )
import Test.Hspec
( Spec, describe, shouldBe, xit )
( Spec, describe, it, shouldBe )

import qualified Data.ByteString.Lazy as BL

{-# ANN spec ("HLint: ignore Use head" :: String) #-}
spec :: Spec
spec = do
describe "Decoding blocks" $ do
xit "should decode a genesis block" $ do
unsafeDeserialiseFromBytes decodeGenesisBlock genesisBlock
it "should decode a genesis block header" $ do
runGet getBlockHeader genesisBlock
`shouldBe`
BlockHeader (SlotId 0 0) (Hash "?")
where
unsafeDeserialiseFromBytes = undefined
decodeGenesisBlock = error "TODO: import from Binary.Jormungandr"
(BlockHeader
{ version = 0
, contentSize = 159
, slot = SlotId {epochNumber = 0 , slotNumber = 0}
, chainLength = 0
, contentHash = "\255\173\235\254\205Y\217\234\161.\144:\213\129\NUL\247\193\227X\153s\156=\ENQ\208\"\131\\\ACK\157+O"
, parentHeaderHash = Nothing
})

genesisBlock :: ByteString
genesisBlock = either error id $ convertFromBase @ByteString Base16
genesisBlock :: BL.ByteString
genesisBlock = either error BL.fromStrict $ convertFromBase @ByteString Base16
"005200000000009f000000000000000000000000ffadebfecd59d9eaa12e903a\
\d58100f7c1e35899739c3d05d022835c069d2b4f000000000000000000000000\
\00000000000000000000000000000000000000000047000048000000005cc1c2\
Expand Down

0 comments on commit a31b823

Please sign in to comment.