@@ -14,7 +14,7 @@ import Data.ByteString.Short ()
14
14
import Data.Text (pack )
15
15
import Hydra.API.APIServerLog (APIServerLog (.. ), Method (.. ), PathInfo (.. ))
16
16
import Hydra.API.ClientInput (ClientInput (.. ))
17
- import Hydra.API.ServerOutput (ClientMessage , CommitInfo (.. ), ServerOutput (.. ), TimedServerOutput (.. ), getConfirmedSnapshot , getSeenSnapshot , getSnapshotUtxo )
17
+ import Hydra.API.ServerOutput (ClientMessage ( .. ) , CommitInfo (.. ), ServerOutput (.. ), TimedServerOutput (.. ), getConfirmedSnapshot , getSeenSnapshot , getSnapshotUtxo )
18
18
import Hydra.Cardano.Api (Coin , LedgerEra , Tx )
19
19
import Hydra.Chain (Chain (.. ), PostTxError (.. ), draftCommitTx )
20
20
import Hydra.Chain.ChainState (IsChainState )
@@ -227,21 +227,21 @@ httpApp tracer directChain env pparams getHeadState getCommitInfo getPendingDepo
227
227
respond . okJSON $ getSeenSnapshot hs
228
228
(" POST" , [" snapshot" ]) ->
229
229
consumeRequestBodyStrict request
230
- >>= handleSideLoadSnapshot putClientInput
230
+ >>= handleSideLoadSnapshot putClientInput apiTransactionTimeout responseChannel
231
231
>>= respond
232
232
(" POST" , [" commit" ]) ->
233
233
consumeRequestBodyStrict request
234
234
>>= handleDraftCommitUtxo env pparams directChain getCommitInfo
235
235
>>= respond
236
236
(" DELETE" , [" commits" , _]) ->
237
237
consumeRequestBodyStrict request
238
- >>= handleRecoverCommitUtxo putClientInput (last . fromList $ pathInfo request)
238
+ >>= handleRecoverCommitUtxo putClientInput apiTransactionTimeout responseChannel (last . fromList $ pathInfo request)
239
239
>>= respond
240
240
(" GET" , [" commits" ]) ->
241
241
getPendingDeposits >>= respond . responseLBS status200 jsonContent . Aeson. encode
242
242
(" POST" , [" decommit" ]) ->
243
243
consumeRequestBodyStrict request
244
- >>= handleDecommit putClientInput
244
+ >>= handleDecommit putClientInput apiTransactionTimeout responseChannel
245
245
>>= respond
246
246
(" GET" , [" protocol-parameters" ]) ->
247
247
respond . responseLBS status200 jsonContent . Aeson. encode $ pparams
@@ -326,15 +326,38 @@ handleRecoverCommitUtxo ::
326
326
forall tx .
327
327
IsChainState tx =>
328
328
(ClientInput tx -> IO () ) ->
329
+ ApiTransactionTimeout ->
330
+ TChan (Either (TimedServerOutput tx ) (ClientMessage tx )) ->
329
331
Text ->
330
332
LBS. ByteString ->
331
333
IO Response
332
- handleRecoverCommitUtxo putClientInput recoverPath _body = do
334
+ handleRecoverCommitUtxo putClientInput apiTransactionTimeout responseChannel recoverPath _body = do
333
335
case parseTxIdFromPath recoverPath of
334
336
Left err -> pure err
335
337
Right recoverTxId -> do
338
+ dupChannel <- atomically $ dupTChan responseChannel
336
339
putClientInput Recover {recoverTxId}
337
- pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
340
+ let wait = do
341
+ event <- atomically $ readTChan dupChannel
342
+ case event of
343
+ Left TimedServerOutput {output = CommitRecovered {}} ->
344
+ pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
345
+ Right (CommandFailed {clientInput = Recover {}}) ->
346
+ pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String " Recover failed" )
347
+ _ -> wait
348
+ timeout (realToFrac (apiTransactionTimeoutNominalDiffTime apiTransactionTimeout)) wait >>= \ case
349
+ Just r -> pure r
350
+ Nothing ->
351
+ pure $
352
+ responseLBS
353
+ status202
354
+ jsonContent
355
+ ( Aeson. encode $
356
+ object
357
+ [ " tag" .= Aeson. String " RecoverSubmitted"
358
+ , " timeout" .= Aeson. String (" Operation timed out after " <> pack (show apiTransactionTimeout) <> " seconds" )
359
+ ]
360
+ )
338
361
where
339
362
parseTxIdFromPath txIdStr =
340
363
case Aeson. eitherDecode (encodeUtf8 txIdStr) :: Either String (TxIdType tx ) of
@@ -361,29 +384,82 @@ handleSubmitUserTx directChain body = do
361
384
where
362
385
Chain {submitTx} = directChain
363
386
364
- handleDecommit :: forall tx . FromJSON tx => (ClientInput tx -> IO () ) -> LBS. ByteString -> IO Response
365
- handleDecommit putClientInput body =
387
+ handleDecommit ::
388
+ forall tx .
389
+ FromJSON tx =>
390
+ (ClientInput tx -> IO () ) ->
391
+ ApiTransactionTimeout ->
392
+ TChan (Either (TimedServerOutput tx ) (ClientMessage tx )) ->
393
+ LBS. ByteString ->
394
+ IO Response
395
+ handleDecommit putClientInput apiTransactionTimeout responseChannel body =
366
396
case Aeson. eitherDecode' body :: Either String tx of
367
397
Left err ->
368
398
pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String $ pack err)
369
399
Right decommitTx -> do
400
+ dupChannel <- atomically $ dupTChan responseChannel
370
401
putClientInput Decommit {decommitTx}
371
- pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
402
+ let wait = do
403
+ event <- atomically $ readTChan dupChannel
404
+ case event of
405
+ Left TimedServerOutput {output = DecommitFinalized {}} ->
406
+ pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
407
+ Left TimedServerOutput {output = DecommitInvalid {}} ->
408
+ pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String " Decommit invalid" )
409
+ Right (CommandFailed {clientInput = Decommit {}}) ->
410
+ pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String " Decommit failed" )
411
+ _ -> wait
412
+ timeout (realToFrac (apiTransactionTimeoutNominalDiffTime apiTransactionTimeout)) wait >>= \ case
413
+ Just r -> pure r
414
+ Nothing ->
415
+ pure $
416
+ responseLBS
417
+ status202
418
+ jsonContent
419
+ ( Aeson. encode $
420
+ object
421
+ [ " tag" .= Aeson. String " DecommitSubmitted"
422
+ , " timeout" .= Aeson. String (" Operation timed out after " <> pack (show apiTransactionTimeout) <> " seconds" )
423
+ ]
424
+ )
372
425
373
426
-- | Handle request to side load confirmed snapshot.
374
427
handleSideLoadSnapshot ::
375
428
forall tx .
376
429
IsChainState tx =>
377
430
(ClientInput tx -> IO () ) ->
431
+ ApiTransactionTimeout ->
432
+ TChan (Either (TimedServerOutput tx ) (ClientMessage tx )) ->
378
433
LBS. ByteString ->
379
434
IO Response
380
- handleSideLoadSnapshot putClientInput body = do
435
+ handleSideLoadSnapshot putClientInput apiTransactionTimeout responseChannel body = do
381
436
case Aeson. eitherDecode' body :: Either String (SideLoadSnapshotRequest tx ) of
382
437
Left err ->
383
438
pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String $ pack err)
384
439
Right SideLoadSnapshotRequest {snapshot} -> do
440
+ dupChannel <- atomically $ dupTChan responseChannel
385
441
putClientInput $ SideLoadSnapshot snapshot
386
- pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
442
+ let wait = do
443
+ event <- atomically $ readTChan dupChannel
444
+ case event of
445
+ Left TimedServerOutput {output = SnapshotSideLoaded {}} ->
446
+ pure $ responseLBS status200 jsonContent (Aeson. encode $ Aeson. String " OK" )
447
+ Right (CommandFailed {clientInput = SideLoadSnapshot {}}) ->
448
+ pure $ responseLBS status400 jsonContent (Aeson. encode $ Aeson. String " Side-load snapshot failed" )
449
+ _ -> wait
450
+ timeout (realToFrac (apiTransactionTimeoutNominalDiffTime apiTransactionTimeout)) wait >>= \ case
451
+ Just r -> pure r
452
+ Nothing ->
453
+ pure $
454
+ responseLBS
455
+ status202
456
+ jsonContent
457
+ ( Aeson. encode $
458
+ object
459
+ [ " tag" .= Aeson. String " SideLoadSnapshotSubmitted"
460
+ , " timeout" .= Aeson. String (" Operation timed out after " <> pack (show apiTransactionTimeout) <> " seconds" )
461
+ ]
462
+ )
387
463
388
464
-- | Handle request to submit a transaction to the head.
389
465
handleSubmitL2Tx ::
0 commit comments