Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Jörmungandr Servant Api Type #310

Merged
merged 1 commit into from
May 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/jormungandr/cardano-wallet-jormungandr.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ library
, binary
, bytestring
, cardano-wallet-core
, servant
, memory
, text
, text-class
hs-source-dirs:
src
exposed-modules:
Cardano.Environment.Jormungandr
Cardano.Wallet.Binary.Jormungandr
Cardano.Wallet.Network.Jormungandr.Api
Cardano.Wallet.Compatibility.Jormungandr
Cardano.Wallet.Transaction.Jormungandr

Expand Down
39 changes: 38 additions & 1 deletion lib/jormungandr/src/Cardano/Wallet/Binary/Jormungandr.hs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ module Cardano.Wallet.Binary.Jormungandr
, LinearFee (..)
, Milli (..)

-- * Classes
, FromBinary (..)


-- * Re-export
, runGet
, Get

) where

Expand Down Expand Up @@ -67,13 +72,15 @@ import Data.Quantity
import Data.Word
( Word16, Word32, Word64, Word8 )

import qualified Cardano.Wallet.Primitive.Types as W

data BlockHeader = BlockHeader
{ version :: Word16
, contentSize :: Word32
, slot :: SlotId
, chainLength :: Word32
, contentHash :: Hash "content"
, parentHeaderHash :: Hash "parentHeader"
, parentHeaderHash :: Hash "BlockHeader"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To match with the one in Cardano.Primitive.Types (BlockHeader)

} deriving (Show, Eq)

data Block = Block BlockHeader [Message]
Expand Down Expand Up @@ -364,3 +371,33 @@ whileM cond next = go
as <- go
return (a : as)
else return []


{-------------------------------------------------------------------------------
Classes
-------------------------------------------------------------------------------}

class FromBinary a where
get :: Get a

instance FromBinary Block where
get = getBlock

instance FromBinary W.Block where
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Block type in this module contain more information than the one in Cardano.Wallet.Primitive.Types (W.Block).

get = convertBlock <$> getBlock
where
convertBlock :: Block -> W.Block
convertBlock (Block h msgs) =
W.Block (convertHeader h) (convertMessages msgs)

convertHeader :: BlockHeader -> W.BlockHeader
convertHeader h = W.BlockHeader (slot h) (parentHeaderHash h)

convertMessages :: [Message] -> [Tx]
convertMessages msgs = msgs >>= \case
Initial _ -> []
Transaction tx -> return tx
UnimplementedMessage _ -> []

instance FromBinary a => FromBinary [a] where
get = whileM (not <$> isEmpty) get
116 changes: 116 additions & 0 deletions lib/jormungandr/src/Cardano/Wallet/Network/Jormungandr/Api.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeOperators #-}

{-# OPTIONS_GHC -fno-warn-orphans #-} -- for content types

-- |
-- Copyright: © 2018-2019 IOHK
-- License: MIT
--
-- An specification for the Jörmungandr REST API.
module Cardano.Wallet.Network.Jormungandr.Api
( Api
, GetBlock
, GetTipId
, GetBlockDecendantIds
, PostSignedTx
, BlockId
, api
, SignedTx
) where

import Prelude

import Cardano.Wallet.Binary.Jormungandr
( FromBinary (..), runGet )
import Cardano.Wallet.Primitive.Types
( Block, Hash (..) )
import Data.Binary.Get
( getByteString )
import Data.ByteArray.Encoding
( Base (Base16), convertFromBase, convertToBase )
import Data.Proxy
( Proxy (..) )
import Data.Text.Encoding
( decodeUtf8 )
import Servant.API
( (:<|>)
, (:>)
, Capture
, Get
, MimeUnrender (..)
, NoContent
, Post
, QueryParam
, ReqBody
, ToHttpApiData (..)
)
import Servant.API.ContentTypes
( OctetStream, PlainText )

import qualified Data.ByteString.Lazy as BL


api :: Proxy Api
api = Proxy

type Api =
GetBlock :<|> GetTipId :<|> GetBlockDecendantIds :<|> PostSignedTx


-- | Retrieve a block by its id.
type GetBlock
= "api"
:> "v0"
:> "block"
:> Capture "blockHeaderHash" BlockId
:> Get '[OctetStream] Block

-- | Retrieve a list of 'n' block decendant ids, sorted from closest to
-- farthest.
--
-- There might also exist fewer than 'n' decendants.
type GetBlockDecendantIds
= "api"
:> "v0"
:> "block"
:> Capture "blockId" BlockId
:> QueryParam "count" Int
:> Get '[OctetStream] [BlockId]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure OctetStream is the best fit here. We actually know the format of the API we're talking to. So it's not just plain bytes. I'd suggest to define some custom format like Jormungandr or BetterCBOR or any name that fits to describe the format, and makes it clear that we're still expecting things in a particular format (whereas OctetStream conveys the idea of a stream of bytes without any particular structure).

This will also prevent the orphan 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


-- | Retrieve the header of the latest known block.
type GetTipId
= "api"
:> "v0"
:> "tip"
:> Get '[PlainText] BlockId

type PostSignedTx
= "api"
:> "v0"
:> "transaction"
:> ReqBody '[OctetStream] SignedTx
:> Post '[NoContent] NoContent

-- TODO: Replace SignedTx with something real
data SignedTx

newtype BlockId = BlockId (Hash "block")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also have ApiT, but the jörmungandr api use the term "BlockId" which this newtype can capture.

deriving Show

instance ToHttpApiData BlockId where
toUrlPiece (BlockId (Hash bytes)) = decodeUtf8 $ convertToBase Base16 bytes

instance FromBinary BlockId where
get = BlockId . Hash <$> getByteString 32

instance MimeUnrender PlainText BlockId where
mimeUnrender _ bs =
BlockId . Hash <$> convertFromBase Base16 (BL.toStrict bs)

-- Orphan instance
instance FromBinary a => MimeUnrender OctetStream a where
Anviking marked this conversation as resolved.
Show resolved Hide resolved
mimeUnrender _ bs = Right $ runGet get bs