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