Status
Proposed
Context
ADR-3 concluded that a full-duplex communication channels are desirable to interact with a reactive system.
The Client API communicates several types of messages to clients. Currently this ranges from node-level
PeerConnected
, over head-specificHeadIsOpen
to messages about transactions likeTxValid
. These messages are all of typeStateChanged
.Current capabilities of the API:
Clients can retrieve the whole history of
StateChanged
messages or opt-out using a query parameter - all or nothing.There is a welcome message called
Greetings
which is always sent, that contains the lastheadStatus
.There exists a
GetUTxO
query-likeClientInput
, which will respond with aGetUTxOResponse
containing the confirmed UTxO set in an open head, or (!) the currently committed UTxO set when the head is initializing.While overall
json
encoded, clients can choose choose betweenjson
or binary (cbor
) output oftransaction
fields in several of these using a query parameter.
Many of these features have been added in a "quick and dirty" way, by monkey patching the encoded JSON.
The current capabalities even do not satisfy all user needs:
Need to wade through lots of events to know the latest state (except the very basic
headStatus
from theGreetings
).Need to poll
GetUTxO
or aggregate confirmed transactions on client side to know the latest UTxO set for constructing transactions.Inclusion of the whole UTxO set in the head is not always desirable and filtering by address would be beneficial. (not addressed in this ADR though, relevant discussion #797)
As ADR-15 also proposes, some clients may not need (or should not have) access to administrative information.
It is often a good idea to separate the responsibilities of Commands and Queries (CQRS), as well as the model they use.
Decision
Drop
GetUTxO
andGetUTxOResponse
messages as they advocate a request/response way of querying.Realize that
ClientInput
data is actually aClientCommand
(renaming them) and thatServerOutput
are justprojections
of the internal event stream (see ADR-24) into readmodels
on the API layer.Compose a versioned (
/v1
) API out of resourcemodels
, which compartmentalize the domain into topics on the API layer.A resource has a
model
type and the latest value is the result of a pureprojection
folded over theStateChanged
event stream, i.e.project :: model -> StateChanged -> model
.Each resource is available at some HTTP path, also called "endpoint":
GET
requests must respond with the latest state in a single response.GET
requests withUpgrade: websocket
headers must start a websocket connection, push the latest state as first message and any resource state updates after.Other HTTP verbs may be accepted by a resource handler, i.e. to issue resource-specific commands. Any commands accepted must also be available via the corresponding websocket connection.
Accept
request headers can be used to configure theContent-Type
of the responseAll resources must provide
application/json
responsesSome resources might support more content types (e.g. CBOR-encoded binary)
Query parameters may be used to further configure responses of some resources. For example,
?address=<bech32>
could be used to filter UTxO by some address.
Keep the semantics of
/
, which accepts websocket upgrade connections and sends direct/raw output ofServerOutput
events on/
, while accepting allClientCommand
messages.- Define
ServerOutput
also in terms of theStateChanged
event stream
- Define
Example resources
Example resource paths + HTTP verbs mapped to existing things to demonstrate the effects of the decision points above. The mappings may change and are to be documented by an API specification instead.
Path | GET | POST | PATCH | DELETE |
---|---|---|---|---|
/v1/head/status | HeadStatus(..) | - | - | - |
/v1/head/snapshot/utxo | last confirmed snapshot utxo | - | - | - |
/v1/head/snapshot/transactions | confirmed snapshot txs | NewTx + responses | - | - |
/v1/head/ledger/utxo | localUTxO | - | - | - |
/v1/head/ledger/transactions | localTxs | NewTx + responses | - | - |
/v1/head/commit | - | Chain{draftCommitTx} | - | - |
/v1/head | all /v1/head/* data | Init | Close | Fanout / Abort |
/v1/protocol-parameters | current protocol parameters | |||
/v1/cardano-transaction | - | Chain{submitTx} | - | - |
/v1/peers | a list of peers | - | - | - |
/v1/node-version | node version as in Greetings | - | - | - |
/v1/ | all /v1/* data | - | - | - |
Multiple heads are out of scope now and hence paths are not including a
<headId>
variable section.
Consequences
Clear separation of what types are used for querying and gets subscribed to by clients and we have dedicated types for sending data to clients
Changes on the querying side of the API are separated from the business logic.
Clients do not need to aggregate data that is already available on the server side without coupling the API to internal state representation.
Separation of Head operation and Head usage, e.g. some HTTP endpoints can be operated with authentication.
Clients have a fine-grained control over what to subscribe to and what to query.
Versioned API allows clients to detect incompatibility easily.
Need to rewrite how the
hydra-tui
is implemented.