@@ -5,18 +5,23 @@ module HydraSdk.Example.Minimal.Main
55import Prelude
66
77import Cardano.AsCbor (encodeCbor )
8+ import Cardano.Types (Language (PlutusV2), Transaction )
89import Contract.CborBytes (cborBytesToHex )
910import Contract.Log (logError' , logInfo' , logTrace' , logWarn' )
10- import Contract.Monad (ContractEnv , stopContractEnv )
11+ import Contract.Monad (Contract , ContractEnv , stopContractEnv )
12+ import Contract.ProtocolParameters (getProtocolParameters )
1113import Contract.Transaction (submit )
1214import Control.Monad.Error.Class (throwError )
1315import Control.Monad.Reader (ask )
16+ import Ctl.Internal.Transaction (setScriptDataHash )
1417import Data.Argonaut (stringifyWithIndent )
18+ import Data.Array (length ) as Array
1519import Data.Codec.Argonaut (encode ) as CA
1620import Data.Either (Either (Left, Right))
1721import Data.Log.Level (LogLevel (Info, Error))
18- import Data.Map (empty , fromFoldable ) as Map
22+ import Data.Map (empty , filterKeys , fromFoldable ) as Map
1923import Data.Maybe (Maybe (Just, Nothing))
24+ import Data.Newtype (unwrap )
2025import Data.Posix.Signal (Signal (SIGINT, SIGTERM))
2126import Data.Traversable (traverse_ )
2227import Data.UInt (fromInt ) as UInt
@@ -34,12 +39,14 @@ import HydraSdk.Example.Minimal.App
3439 , appLogger
3540 , initApp
3641 , readHeadStatus
42+ , readUtxoSnapshot
3743 , runAppEff
3844 , runContractInApp
3945 )
4046import HydraSdk.Example.Minimal.App (setHeadStatus , setUtxoSnapshot ) as App
4147import HydraSdk.Example.Minimal.Config (configFromArgv )
42- import HydraSdk.Lib (log' )
48+ import HydraSdk.Example.Minimal.Contract.L2 (placeArbitraryDatumL2 )
49+ import HydraSdk.Lib (log' , reSignTransaction , setAuxDataHash )
4350import HydraSdk.NodeApi
4451 ( HydraNodeApiWebSocket
4552 , HydraTxRetryStrategy (RetryTxWithParams, DontRetryTx)
@@ -53,8 +60,19 @@ import HydraSdk.Types
5360 , HeadStatus_Initializing
5461 , HeadStatus_Open
5562 , HeadStatus_Closed
63+ , HeadStatus_FanoutPossible
64+ , HeadStatus_Final
65+ )
66+ , HydraNodeApi_InMessage
67+ ( Greetings
68+ , HeadIsInitializing
69+ , HeadIsOpen
70+ , SnapshotConfirmed
71+ , HeadIsClosed
72+ , HeadIsContested
73+ , ReadyToFanout
74+ , HeadIsFinalized
5675 )
57- , HydraNodeApi_InMessage (Greetings , HeadIsInitializing , HeadIsOpen )
5876 , HydraSnapshot (HydraSnapshot )
5977 , hydraSnapshotCodec
6078 , mkSimpleCommitRequest
@@ -161,7 +179,7 @@ messageHandler ws =
161179 throwError $ error $ " Commit request failed with error: "
162180 <> show httpErr
163181 Right { cborHex: commitTx } -> do
164- txHash <- runContractInApp $ submit commitTx
182+ txHash <- runContractInApp $ submit =<< fixCommitTx commitTx
165183 logInfo' $ " Submitted Commit transaction: " <> cborBytesToHex
166184 (encodeCbor txHash)
167185 HeadIsOpen { headId, utxo } -> do
@@ -171,8 +189,52 @@ messageHandler ws =
171189 { snapshotNumber: zero
172190 , utxo
173191 }
192+ tx <- runContractInApp $ placeArbitraryDatumL2 $ toUtxoMap utxo
193+ liftEffect $ ws.submitTxL2 tx
194+ SnapshotConfirmed { snapshot } -> do
195+ setUtxoSnapshot snapshot
196+ { config: { hydraNodeStartupParams: { peers } } } <- ask
197+ when ((unwrap snapshot).snapshotNumber > Array .length peers) do
198+ logInfo' " All Head participants must have advanced the L2 state. Closing Head..."
199+ liftEffect ws.closeHead
200+ HeadIsClosed { snapshotNumber } -> do
201+ -- TODO: set head status implicitly
202+ setHeadStatus HeadStatus_Closed
203+ contestClosureIfNeeded ws snapshotNumber
204+ HeadIsContested { snapshotNumber } ->
205+ contestClosureIfNeeded ws snapshotNumber
206+ ReadyToFanout _ -> do
207+ setHeadStatus HeadStatus_FanoutPossible
208+ liftEffect ws.fanout
209+ HeadIsFinalized _ -> do
210+ setHeadStatus HeadStatus_Final
211+ -- TODO: output fanout tx hash
212+ throwError $ error " SUCCESS: Head finalized, Funds transfered to L1 - Exiting..."
174213 _ -> pure unit
175214
215+ fixCommitTx :: Transaction -> Contract Transaction
216+ fixCommitTx = reSignTransaction <=< fixScriptIntegrityHash <<< setAuxDataHash
217+ where
218+ fixScriptIntegrityHash :: Transaction -> Contract Transaction
219+ fixScriptIntegrityHash tx = do
220+ pparams <- unwrap <$> getProtocolParameters
221+ let
222+ costModels = Map .filterKeys (eq PlutusV2 ) pparams.costModels
223+ ws = unwrap (unwrap tx).witnessSet
224+ liftEffect $ setScriptDataHash costModels ws.redeemers ws.plutusData tx
225+
226+ contestClosureIfNeeded :: HydraNodeApiWebSocket AppM -> Int -> AppM Unit
227+ contestClosureIfNeeded ws closeSnapshot = do
228+ HydraSnapshot { snapshotNumber: localSnapshot } <- readUtxoSnapshot
229+ when (closeSnapshot < localSnapshot) do
230+ logInfo' $
231+ " Detected attempt to close the Head with older snapshot. Close snapshot: "
232+ <> show closeSnapshot
233+ <> " , local snapshot: "
234+ <> show localSnapshot
235+ <> " . Contesting Head closure..."
236+ liftEffect ws.challengeSnapshot
237+
176238setHeadStatus :: HydraHeadStatus -> AppM Unit
177239setHeadStatus status = do
178240 App .setHeadStatus status
0 commit comments