Skip to content

Commit

Permalink
Pr review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
v0d1ch committed Apr 1, 2023
1 parent 4d3f6fc commit 4abaead
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 93 deletions.
5 changes: 1 addition & 4 deletions docs/core-concepts/behavior.md
Expand Up @@ -22,7 +22,4 @@ When a `hydra-node` restarts, by default it will load it's history from persiste

Clients can optionally decide to skip history outputs and receive only the `Greetings` and following ones. In order to do that they can use query param `history=no`.

They can also decide to be served with transactions encoded as CBOR by using query param `tx-output=cbor`.

For example if the client wants to connect to a local `hydra-node` and doesn't want to view the server history but also want to have the
transactions encoded as CBOR (base16) they would connect using default port `4001` and the full path `ws://localhost:4001/?history=0&tx-output=cbor`.
For example if the client wants to connect to a local `hydra-node` and doesn't want to view the server history but also want to have the transactions encoded as CBOR (base16) they would connect using default port `4001` and the full path `ws://localhost:4001/?history=no&tx-output=cbor`.
2 changes: 0 additions & 2 deletions hydra-node/hydra-node.cabal
Expand Up @@ -151,8 +151,6 @@ library
, io-classes
, iohk-monitoring
, iproute
, lens
, lens-aeson
, memory
, modern-uri
, network
Expand Down
31 changes: 27 additions & 4 deletions hydra-node/json-schemas/api.yaml
Expand Up @@ -7,7 +7,8 @@ info:
Once started, a Hydra node provides an API in the forms of JSON messages over WebSocket. An Hydra node is an event-driven application where users (a.k.a you) are one possible source of inputs. Other sources can be mainly other Hydra nodes in the network, or transactions observed on the layer 1 (e.g. a closing transaction).
Therefore, once connected, clients receive a stream of outputs as they arrive. Clients get to decide (using query parameters) if they want to observe the history of outputs and the transaction format.
For example if client provides a path that looks like this `/?history=0&tx-output=cbor` the server will not serve prior history of server outputs and the transaction fields in the json will be encoded as CBOR.
For example if client provides a path that looks like this `/?history=no&tx-output=cbor` the server will not serve prior history of server outputs and the transaction fields in the json will be encoded as CBOR (base16 encoded).
They can interact with their node by pushing events to it, some are local, and some will have consequences on the rest of the head.
See [the documentation](https://hydra.family/head-protocol/core-concepts/behavior/) for more details on the overall API behavior.
Expand Down Expand Up @@ -37,6 +38,22 @@ channels:
description: Main (and sole) entry point for the Hydra service.
servers:
- localhost
bindings:
ws:
query:
history:
required: false
description: Specify weather the client wants to receive the full node history. Default is yes.
schema:
type: string
enum: ["yes", "no"]
tx-output:
description: Specify weather the client wants see transactions encoded as cbor. Default is json.
schema:
type: string
enum: ["json", "cbor"]


subscribe:
summary: Events emitted by the Hydra node.
operationId: serverOutput
Expand Down Expand Up @@ -495,7 +512,10 @@ components:
headId:
$ref: "#/components/schemas/HeadId"
transaction:
$ref: "#/components/schemas/Transaction"
description: Choose between output formats using the `tx-output` query parameter.
oneOf:
- $ref: "#/components/schemas/Transaction"
- $ref: "#/components/schemas/RawTransaction"
seq:
$ref: "#/components/schemas/SequenceNumber"
timestamp:
Expand Down Expand Up @@ -528,7 +548,10 @@ components:
utxo:
$ref: "#/components/schemas/UTxO"
transaction:
$ref: "#/components/schemas/Transaction"
description: Choose between output formats using the `tx-output` query parameter.
oneOf:
- $ref: "#/components/schemas/Transaction"
- $ref: "#/components/schemas/RawTransaction"
validationError:
type: object
properties:
Expand Down Expand Up @@ -1188,7 +1211,7 @@ components:
RawTransaction:
title: RawTransaction
description: |
A CBOR-serialised signed Alonzo transaction, encoded in base16.
A CBOR-serialised signed Cardano transaction, encoded in base16.
type: string
contentEncoding: base16

Expand Down
35 changes: 18 additions & 17 deletions hydra-node/src/Hydra/API/Server.hs
@@ -1,3 +1,4 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}

Expand All @@ -19,9 +20,9 @@ import Control.Exception (IOException)
import qualified Data.Aeson as Aeson
import Hydra.API.ClientInput (ClientInput)
import Hydra.API.ServerOutput (
OutputFormat (..),
ServerOutput (Greetings, InvalidInput),
TimedServerOutput (..),
TxDisplay (..),
prepareServerOutput,
)
import Hydra.Chain (IsChainState)
Expand All @@ -41,6 +42,7 @@ import Network.WebSockets (
)
import Test.QuickCheck (oneof)
import Text.URI
import Text.URI.QQ (queryKey, queryValue)

data APIServerLog
= APIServerStarted {listeningPort :: PortNumber}
Expand Down Expand Up @@ -142,13 +144,12 @@ runAPIServer host port party tracer history callback responseChannel = do
traceWith tracer NewAPIConnection

-- api client can decide if they want to see the past history of server outputs
dontServeHistory <- shouldNotServeHistory queryParams
if dontServeHistory
if shouldNotServeHistory queryParams
then forwardGreetingOnly con
else forwardHistory con

-- api client can decide if they want tx's to be displayed as CBOR instead of plain json
txDisplay <- decideOnTxDisplay queryParams
let txDisplay = decideOnTxDisplay queryParams

withPingThread con 30 (pure ()) $
race_ (receiveInputs con) (sendOutputs chan con txDisplay)
Expand All @@ -162,19 +163,19 @@ runAPIServer host port party tracer history callback responseChannel = do
, seq = 0
, output = Greetings party :: ServerOutput tx
}

decideOnTxDisplay qp = do
k <- mkQueryKey "tx-output"
v <- mkQueryValue "cbor"
pure $
case (QueryParam k v) `elem` qp of
True -> TxCBOR
False -> TxJSON

shouldNotServeHistory qp = do
k <- mkQueryKey "history"
v <- mkQueryValue "0"
pure $ (QueryParam k v) `elem` qp
decideOnTxDisplay qp =
let k = [queryKey|tx-output|]
v = [queryValue|cbor|]
queryP = QueryParam k v
in case queryP `elem` qp of
True -> OutputCBOR
False -> OutputJSON

shouldNotServeHistory qp =
flip any qp $ \case
(QueryParam key val)
| key == [queryKey|history|] -> val == [queryValue|no|]
_other -> False

onIOException ioException =
throwIO $
Expand Down
23 changes: 11 additions & 12 deletions hydra-node/src/Hydra/API/ServerOutput.hs
Expand Up @@ -2,14 +2,11 @@

module Hydra.API.ServerOutput where

import Cardano.Binary (toStrictByteString)
import Control.Lens ((?~))
import Cardano.Binary (serialize')
import Data.Aeson (Value (..), encode, withObject, (.:))
import qualified Data.Aeson.KeyMap as KeyMap
import Data.Aeson.Lens (atKey)
import Data.ByteString.Char8 (unpack)
import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Lazy as LBS
import Data.Text (pack)
import Hydra.API.ClientInput (ClientInput (..))
import Hydra.Chain (ChainStateType, HeadId, IsChainState, PostChainTx, PostTxError)
import Hydra.Crypto (MultiSignature)
Expand Down Expand Up @@ -123,9 +120,9 @@ instance
RolledBack -> []

-- | Possible transaction formats in the api server output
data TxDisplay
= TxCBOR
| TxJSON
data OutputFormat
= OutputCBOR
| OutputJSON
deriving (Eq, Show)

-- | Replaces the json encoded tx field with it's cbor representation.
Expand All @@ -134,13 +131,13 @@ data TxDisplay
prepareServerOutput ::
IsChainState tx =>
-- | Decide on tx representation
TxDisplay ->
OutputFormat ->
-- | Server output
TimedServerOutput tx ->
-- | Final output
LBS.ByteString
prepareServerOutput TxJSON response = encode response
prepareServerOutput TxCBOR response =
prepareServerOutput OutputJSON response = encode response
prepareServerOutput OutputCBOR response =
case output response of
PeerConnected{} -> encodedResponse
PeerDisconnected{} -> encodedResponse
Expand Down Expand Up @@ -173,4 +170,6 @@ prepareServerOutput TxCBOR response =
where
encodedResponse = encode response
replacedResponse tx =
encodedResponse & atKey "transaction" ?~ String (pack . unpack $ toStrictByteString $ toCBOR tx)
case toJSON response of
Object km -> encode $ Object $ KeyMap.insert "transaction" (String . decodeUtf8 . Base16.encode $ serialize' tx) km
_other -> encodedResponse

0 comments on commit 4abaead

Please sign in to comment.