Skip to content

Commit da4ee11

Browse files
ffakenzch1bov0d1ch
authored
GET /head api to query the full head state (#2049)
<!-- Describe your change here --> Closes #1957 This also refactors some of the other HTTP queries (and Greetings) in terms of a single 'aggregate'-reusing projection. This PR revives #2002. --- <!-- Consider each and tick it off one way or the other --> * [X] CHANGELOG updated or not needed * [X] Documentation updated or not needed * [X] Haddocks updated or not needed * [X] No new TODOs introduced or explained herafter --------- Co-authored-by: Sebastian Nagel <[email protected]> Co-authored-by: Sasha Bogicevic <[email protected]>
1 parent 6b38190 commit da4ee11

File tree

14 files changed

+5337
-211
lines changed

14 files changed

+5337
-211
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ changes.
4646
- Enhanced the error message for `etcd` cluster ID mismatches by including detailed information about
4747
the expected peers versus peers loaded from the `hydra-node` arguments.
4848

49+
- Add API query (GET /head) to fetch the latest head state by a node and help introspecting the whole internal state.
50+
4951
## [0.21.0] - 2025-04-28
5052

5153
- New metric for counting the number of active peers: `hydra_head_peers_connected`

hydra-node/golden/ReasonablySized (HeadState (Tx ConwayEra)).json

Lines changed: 4745 additions & 0 deletions
Large diffs are not rendered by default.

hydra-node/json-schemas/api.yaml

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,34 @@ channels:
118118
- $ref: "api.yaml#/components/messages/Contest"
119119
- $ref: "api.yaml#/components/messages/Fanout"
120120
- $ref: "api.yaml#/components/messages/SideLoadSnapshot"
121+
122+
/head:
123+
servers:
124+
- localhost-http
125+
publish:
126+
description: Get latest head state.
127+
operationId: getHeadState
128+
message:
129+
payload:
130+
type: "null"
131+
bindings:
132+
http:
133+
type: request
134+
method: GET
135+
bindingVersion: '0.1.0'
136+
subscribe:
137+
operationId: headStateResponse
138+
description: |
139+
Possible responses of this endpoint are:
140+
* 200 OK
141+
message:
142+
payload:
143+
$ref: "api.yaml#/components/schemas/HeadState"
144+
bindings:
145+
http:
146+
type: response
147+
method: GET
148+
bindingVersion: '0.1.0'
121149

122150
/commit:
123151
servers:
@@ -2964,3 +2992,241 @@ components:
29642992
description: Hydra network protocol version
29652993
type: integer
29662994
minimum: 0
2995+
2996+
HeadState:
2997+
oneOf:
2998+
- title: "Idle"
2999+
additionalProperties: false
3000+
required:
3001+
- tag
3002+
- contents
3003+
properties:
3004+
tag:
3005+
type: string
3006+
enum: ["Idle"]
3007+
contents:
3008+
$ref: "api.yaml#/components/schemas/IdleState"
3009+
- title: "Initial"
3010+
additionalProperties: false
3011+
required:
3012+
- tag
3013+
- contents
3014+
properties:
3015+
tag:
3016+
type: string
3017+
enum: ["Initial"]
3018+
contents:
3019+
$ref: "api.yaml#/components/schemas/InitialState"
3020+
- title: "Open"
3021+
additionalProperties: false
3022+
required:
3023+
- tag
3024+
- contents
3025+
properties:
3026+
tag:
3027+
type: string
3028+
enum: ["Open"]
3029+
contents:
3030+
$ref: "api.yaml#/components/schemas/OpenState"
3031+
- title: "Closed"
3032+
additionalProperties: false
3033+
required:
3034+
- tag
3035+
- contents
3036+
properties:
3037+
tag:
3038+
type: string
3039+
enum: ["Closed"]
3040+
contents:
3041+
$ref: "api.yaml#/components/schemas/ClosedState"
3042+
3043+
IdleState:
3044+
type: object
3045+
additionalProperties: false
3046+
required:
3047+
- chainState
3048+
properties:
3049+
chainState:
3050+
$ref: "api.yaml#/components/schemas/ChainState"
3051+
3052+
InitialState:
3053+
type: object
3054+
additionalProperties: false
3055+
required:
3056+
- parameters
3057+
- pendingCommits
3058+
- committed
3059+
- chainState
3060+
- headId
3061+
- headSeed
3062+
properties:
3063+
parameters:
3064+
$ref: "api.yaml#/components/schemas/HeadParameters"
3065+
pendingCommits:
3066+
type: array
3067+
items:
3068+
$ref: "api.yaml#/components/schemas/Party"
3069+
committed:
3070+
type: object
3071+
propertyNames:
3072+
$ref: "api.yaml#/components/schemas/Party"
3073+
items:
3074+
$ref: "api.yaml#/components/schemas/UTxO"
3075+
chainState:
3076+
$ref: "api.yaml#/components/schemas/ChainState"
3077+
headId:
3078+
$ref: "api.yaml#/components/schemas/HeadId"
3079+
headSeed:
3080+
$ref: "api.yaml#/components/schemas/HeadSeed"
3081+
3082+
OpenState:
3083+
type: object
3084+
additionalProperties: false
3085+
required:
3086+
- parameters
3087+
- coordinatedHeadState
3088+
- chainState
3089+
- headId
3090+
- currentSlot
3091+
- headSeed
3092+
properties:
3093+
parameters:
3094+
$ref: "api.yaml#/components/schemas/HeadParameters"
3095+
coordinatedHeadState:
3096+
$ref: "api.yaml#/components/schemas/CoordinatedHeadState"
3097+
chainState:
3098+
$ref: "api.yaml#/components/schemas/ChainState"
3099+
headId:
3100+
$ref: "api.yaml#/components/schemas/HeadId"
3101+
currentSlot:
3102+
$ref: "api.yaml#/components/schemas/ChainSlot"
3103+
headSeed:
3104+
$ref: "api.yaml#/components/schemas/HeadSeed"
3105+
3106+
ClosedState:
3107+
type: object
3108+
additionalProperties: false
3109+
required:
3110+
- parameters
3111+
- confirmedSnapshot
3112+
- contestationDeadline
3113+
- readyToFanoutSent
3114+
- chainState
3115+
- headId
3116+
- headSeed
3117+
- version
3118+
properties:
3119+
parameters:
3120+
$ref: "api.yaml#/components/schemas/HeadParameters"
3121+
confirmedSnapshot:
3122+
$ref: "api.yaml#/components/schemas/ConfirmedSnapshot"
3123+
contestationDeadline:
3124+
$ref: "api.yaml#/components/schemas/UTCTime"
3125+
readyToFanoutSent:
3126+
type: boolean
3127+
chainState:
3128+
$ref: "api.yaml#/components/schemas/ChainState"
3129+
headId:
3130+
$ref: "api.yaml#/components/schemas/HeadId"
3131+
headSeed:
3132+
$ref: "api.yaml#/components/schemas/HeadSeed"
3133+
version:
3134+
$ref: "api.yaml#/components/schemas/SnapshotVersion"
3135+
3136+
CoordinatedHeadState:
3137+
type: object
3138+
additionalProperties: false
3139+
required:
3140+
- localUTxO
3141+
- localTxs
3142+
- allTxs
3143+
- confirmedSnapshot
3144+
- seenSnapshot
3145+
- pendingDeposits
3146+
- currentDepositTxId
3147+
- decommitTx
3148+
- version
3149+
properties:
3150+
localUTxO:
3151+
$ref: "api.yaml#/components/schemas/UTxO"
3152+
localTxs:
3153+
type: array
3154+
items:
3155+
$ref: "api.yaml#/components/schemas/Transaction"
3156+
allTxs:
3157+
type: object
3158+
propertyNames:
3159+
$ref: "api.yaml#/components/schemas/TxId"
3160+
items:
3161+
$ref: "api.yaml#/components/schemas/Transaction"
3162+
confirmedSnapshot:
3163+
$ref: "api.yaml#/components/schemas/ConfirmedSnapshot"
3164+
seenSnapshot:
3165+
$ref: "api.yaml#/components/schemas/SeenSnapshot"
3166+
pendingDeposits:
3167+
type: object
3168+
propertyNames:
3169+
$ref: "api.yaml#/components/schemas/TxId"
3170+
items:
3171+
$ref: "api.yaml#/components/schemas/Deposit"
3172+
currentDepositTxId:
3173+
oneOf:
3174+
- type: "null"
3175+
- $ref: "api.yaml#/components/schemas/TxId"
3176+
decommitTx:
3177+
oneOf:
3178+
- type: "null"
3179+
- $ref: "api.yaml#/components/schemas/Transaction"
3180+
version:
3181+
$ref: "api.yaml#/components/schemas/SnapshotVersion"
3182+
3183+
Deposit:
3184+
type: object
3185+
additionalProperties: false
3186+
required:
3187+
- headId
3188+
- deposited
3189+
- created
3190+
- deadline
3191+
- status
3192+
properties:
3193+
headId:
3194+
$ref: "api.yaml#/components/schemas/HeadId"
3195+
deposited:
3196+
$ref: "api.yaml#/components/schemas/UTxO"
3197+
created:
3198+
$ref: "api.yaml#/components/schemas/UTCTime"
3199+
deadline:
3200+
$ref: "api.yaml#/components/schemas/UTCTime"
3201+
status:
3202+
$ref: "api.yaml#/components/schemas/DepositStatus"
3203+
3204+
DepositStatus:
3205+
oneOf:
3206+
- title: Unknown
3207+
type: object
3208+
additionalProperties: false
3209+
required:
3210+
- tag
3211+
properties:
3212+
tag:
3213+
type: string
3214+
enum: ["Unknown"]
3215+
- title: Active
3216+
type: object
3217+
additionalProperties: false
3218+
required:
3219+
- tag
3220+
properties:
3221+
tag:
3222+
type: string
3223+
enum: ["Active"]
3224+
- title: Expired
3225+
type: object
3226+
additionalProperties: false
3227+
required:
3228+
- tag
3229+
properties:
3230+
tag:
3231+
type: string
3232+
enum: ["Expired"]

hydra-node/src/Hydra/API/HTTPServer.hs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import Data.ByteString.Short ()
1212
import Data.Text (pack)
1313
import Hydra.API.APIServerLog (APIServerLog (..), Method (..), PathInfo (..))
1414
import Hydra.API.ClientInput (ClientInput (..))
15-
import Hydra.API.ServerOutput (CommitInfo (..))
15+
import Hydra.API.ServerOutput (CommitInfo (..), getConfirmedSnapshot, getSeenSnapshot, getSnapshotUtxo)
1616
import Hydra.Cardano.Api (
1717
LedgerEra,
1818
Tx,
@@ -22,7 +22,9 @@ import Hydra.Chain.ChainState (
2222
IsChainState,
2323
)
2424
import Hydra.Chain.Direct.State ()
25-
import Hydra.HeadLogic.State (SeenSnapshot (..))
25+
import Hydra.HeadLogic.State (
26+
HeadState (..),
27+
)
2628
import Hydra.Logging (Tracer, traceWith)
2729
import Hydra.Node.DepositPeriod (toNominalDiffTime)
2830
import Hydra.Node.Environment (Environment (..))
@@ -147,43 +149,41 @@ httpApp ::
147149
Chain tx IO ->
148150
Environment ->
149151
PParams LedgerEra ->
152+
-- | Get latest 'HeadState'.
153+
IO (HeadState tx) ->
150154
-- | A means to get commit info.
151155
IO CommitInfo ->
152-
-- | Get latest confirmed UTxO snapshot.
153-
IO (Maybe (UTxOType tx)) ->
154-
-- | Get latest seen snapshot.
155-
IO (SeenSnapshot tx) ->
156-
-- | Get latest confirmed snapshot.
157-
IO (Maybe (ConfirmedSnapshot tx)) ->
158156
-- | Get the pending commits (deposits)
159157
IO [TxIdType tx] ->
160158
-- | Callback to yield a 'ClientInput' to the main event loop.
161159
(ClientInput tx -> IO ()) ->
162160
Application
163-
httpApp tracer directChain env pparams getCommitInfo getConfirmedUTxO getSeenSnapshot getConfirmedSnapshot getPendingDeposits putClientInput request respond = do
161+
httpApp tracer directChain env pparams getHeadState getCommitInfo getPendingDeposits putClientInput request respond = do
164162
traceWith tracer $
165163
APIHTTPRequestReceived
166164
{ method = Method $ requestMethod request
167165
, path = PathInfo $ rawPathInfo request
168166
}
169167
case (requestMethod request, pathInfo request) of
170-
("GET", ["snapshot", "last-seen"]) ->
171-
getSeenSnapshot >>= respond . okJSON
172-
("GET", ["snapshot"]) ->
173-
getConfirmedSnapshot >>= \case
168+
("GET", ["head"]) ->
169+
getHeadState >>= respond . okJSON
170+
("GET", ["snapshot"]) -> do
171+
hs <- getHeadState
172+
case getConfirmedSnapshot hs of
173+
Just confirmedSnapshot -> respond $ okJSON confirmedSnapshot
174174
Nothing -> respond notFound
175-
Just snapshot -> respond $ okJSON snapshot
175+
("GET", ["snapshot", "utxo"]) -> do
176+
hs <- getHeadState
177+
case getSnapshotUtxo hs of
178+
Just utxo -> respond $ okJSON utxo
179+
_ -> respond notFound
180+
("GET", ["snapshot", "last-seen"]) -> do
181+
hs <- getHeadState
182+
respond . okJSON $ getSeenSnapshot hs
176183
("POST", ["snapshot"]) ->
177184
consumeRequestBodyStrict request
178185
>>= handleSideLoadSnapshot putClientInput
179186
>>= respond
180-
("GET", ["snapshot", "utxo"]) ->
181-
-- XXX: Should ensure the UTxO is of the right head and the head is still
182-
-- open. This is something we should fix on the "read model" side of the
183-
-- server.
184-
getConfirmedUTxO >>= \case
185-
Nothing -> respond notFound
186-
Just utxo -> respond $ okJSON utxo
187187
("POST", ["commit"]) ->
188188
consumeRequestBodyStrict request
189189
>>= handleDraftCommitUtxo env directChain getCommitInfo

0 commit comments

Comments
 (0)