@@ -4,32 +4,36 @@ module HydraSdk.Example.Minimal.Main
44
55import Prelude
66
7- import Cardano.AsCbor (encodeCbor )
7+ import Aeson (stringifyAeson )
8+ import Cardano.AsCbor (decodeCbor , encodeCbor )
89import Cardano.Types (Language (PlutusV2), Transaction )
10+ import Cardano.Types.AuxiliaryData (hashAuxiliaryData )
11+ import Cardano.Types.Transaction (_body , _witnessSet )
12+ import Cardano.Types.TransactionBody (_auxiliaryDataHash )
13+ import Cardano.Types.TransactionWitnessSet (_vkeys )
914import Contract.CborBytes (cborBytesToHex )
1015import Contract.Log (logError' , logInfo' , logTrace' , logWarn' )
1116import Contract.Monad (Contract , ContractEnv , stopContractEnv )
1217import Contract.ProtocolParameters (getProtocolParameters )
13- import Contract.Transaction (submit )
18+ import Contract.Transaction (signTransaction , submit )
1419import Control.Monad.Error.Class (throwError )
1520import Control.Monad.Reader (ask )
1621import Ctl.Internal.Transaction (setScriptDataHash )
17- import Data.Argonaut (stringifyWithIndent )
1822import Data.Array (length ) as Array
1923import Data.Codec.Argonaut (encode ) as CA
2024import Data.Either (Either (Left, Right))
25+ import Data.Lens ((.~))
2126import Data.Log.Level (LogLevel (Info, Error))
2227import Data.Map (empty , filterKeys , fromFoldable ) as Map
23- import Data.Maybe (Maybe (Just, Nothing))
28+ import Data.Maybe (Maybe (Just, Nothing), maybe )
2429import Data.Newtype (unwrap )
2530import Data.Posix.Signal (Signal (SIGINT, SIGTERM))
2631import Data.Traversable (traverse_ )
27- import Data.UInt (fromInt ) as UInt
2832import Effect (Effect )
2933import Effect.Aff (Aff , launchAff_ , runAff_ )
3034import Effect.Aff.Class (liftAff )
3135import Effect.Class (liftEffect )
32- import Effect.Exception (error , message )
36+ import Effect.Exception (error , message , name , stack ) as Error
3337import Effect.Ref (Ref )
3438import Effect.Ref (new , read , write ) as Ref
3539import HydraSdk.Example.Minimal.App
@@ -46,7 +50,7 @@ import HydraSdk.Example.Minimal.App
4650import HydraSdk.Example.Minimal.App (setHeadStatus , setUtxoSnapshot ) as App
4751import HydraSdk.Example.Minimal.Config (configFromArgv )
4852import HydraSdk.Example.Minimal.Contract.L2 (placeArbitraryDatumL2 )
49- import HydraSdk.Lib (log' , reSignTransaction , setAuxDataHash )
53+ import HydraSdk.Lib (log' )
5054import HydraSdk.NodeApi
5155 ( HydraNodeApiWebSocket
5256 , HydraTxRetryStrategy (RetryTxWithParams, DontRetryTx)
@@ -55,14 +59,7 @@ import HydraSdk.NodeApi
5559 )
5660import HydraSdk.Process (spawnHydraNode )
5761import HydraSdk.Types
58- ( HydraHeadStatus
59- ( HeadStatus_Idle
60- , HeadStatus_Initializing
61- , HeadStatus_Open
62- , HeadStatus_Closed
63- , HeadStatus_FanoutPossible
64- , HeadStatus_Final
65- )
62+ ( HydraHeadStatus (HeadStatus_Idle, HeadStatus_Closed)
6663 , HydraNodeApi_InMessage
6764 ( Greetings
6865 , HeadIsInitializing
@@ -76,14 +73,11 @@ import HydraSdk.Types
7673 , HydraSnapshot (HydraSnapshot )
7774 , hydraSnapshotCodec
7875 , mkSimpleCommitRequest
79- , printHeadStatus
80- , printHost
8176 , printHostPort
8277 , toUtxoMap
8378 )
8479import Node.ChildProcess (ChildProcess , kill )
8580import Node.Process (onSignal , onUncaughtException )
86- import URI.Port (toInt ) as Port
8781
8882type AppHandle =
8983 { cleanupHandler :: Effect Unit
@@ -99,7 +93,12 @@ main =
9993 appHandle <- startDelegateServer appState logger
10094 liftEffect do
10195 onUncaughtException \err -> do
102- runAppEff appState logger $ logError' $ " UNCAUGHT EXCEPTION: " <> message err
96+ runAppEff appState logger $ logError' $
97+ " UNCAUGHT "
98+ <> Error .name err
99+ <> " : "
100+ <> Error .message err
101+ <> maybe mempty (append " , STACK: " ) (Error .stack err)
103102 appHandle.cleanupHandler
104103 onSignal SIGINT appHandle.cleanupHandler
105104 onSignal SIGTERM appHandle.cleanupHandler
@@ -119,6 +118,7 @@ startDelegateServer state logger = do
119118 , handlers:
120119 { connectHandler: const (pure unit)
121120 , messageHandler: \ws -> messageHandler ws
121+ , headStatusHandler: Just App .setHeadStatus
122122 , errorHandler: \_ws err ->
123123 logError' $ " hydra-node API WebSocket error: " <> show err
124124 }
@@ -160,34 +160,32 @@ messageHandler ws =
160160 Left _rawMessage -> pure unit
161161 Right message ->
162162 case message of
163- Greetings { headStatus } -> do
164- setHeadStatus headStatus
165- when (headStatus == HeadStatus_Idle ) $ liftEffect ws.initHead
163+ Greetings { headStatus } ->
164+ when ( headStatus == HeadStatus_Idle ) $
165+ liftEffect ws.initHead
166166 HeadIsInitializing _ -> do
167- setHeadStatus HeadStatus_Initializing
168167 { commitUtxo, config: { hydraNodeStartupParams: { hydraNodeApiAddress } } } <- ask
169168 let
170169 payload = mkSimpleCommitRequest $ Map .fromFoldable [ commitUtxo ]
171- serverConfig =
172- { port: UInt .fromInt $ Port .toInt hydraNodeApiAddress.port
173- , host: printHost hydraNodeApiAddress
174- , secure: false
175- , path: Nothing
176- }
177- liftAff (commitRequest serverConfig payload) >>= case _ of
170+ hydraNodeHttpUrl = " http://" <> printHostPort hydraNodeApiAddress
171+ liftAff (commitRequest hydraNodeHttpUrl payload) >>= case _ of
178172 Left httpErr ->
179- throwError $ error $ " Commit request failed with error: "
173+ throwError $ Error . error $ " Commit request failed with error: "
180174 <> show httpErr
181- Right { cborHex: commitTx } -> do
182- txHash <- runContractInApp $ submit =<< fixCommitTx commitTx
183- logInfo' $ " Submitted Commit transaction: " <> cborBytesToHex
184- (encodeCbor txHash)
175+ Right { cborHex } -> do
176+ case decodeCbor cborHex of
177+ Just commitTx -> do
178+ txHash <- runContractInApp $ submit =<< fixCommitTx commitTx
179+ logInfo' $ " Submitted Commit transaction: " <> cborBytesToHex
180+ (encodeCbor txHash)
181+ Nothing ->
182+ throwError $ Error .error " Could not decode CommitTx CBOR"
185183 HeadIsOpen { headId, utxo } -> do
186- setHeadStatus HeadStatus_Open
187- logInfo' $ " Head ID: " <> cborBytesToHex (encodeCbor headId)
184+ logInfo' $ " Head ID: " <> headId
188185 setUtxoSnapshot $ HydraSnapshot
189186 { snapshotNumber: zero
190187 , utxo
188+ , confirmedTransactions: mempty
191189 }
192190 tx <- runContractInApp $ placeArbitraryDatumL2 $ toUtxoMap utxo
193191 liftEffect $ ws.submitTxL2 tx
@@ -197,24 +195,31 @@ messageHandler ws =
197195 when ((unwrap snapshot).snapshotNumber > Array .length peers) do
198196 logInfo' " All Head participants must have advanced the L2 state. Closing Head..."
199197 liftEffect ws.closeHead
200- HeadIsClosed { snapshotNumber } -> do
201- -- TODO: set head status implicitly
202- setHeadStatus HeadStatus_Closed
198+ HeadIsClosed { snapshotNumber } ->
203199 contestClosureIfNeeded ws snapshotNumber
204200 HeadIsContested { snapshotNumber } ->
205201 contestClosureIfNeeded ws snapshotNumber
206- ReadyToFanout _ -> do
207- setHeadStatus HeadStatus_FanoutPossible
202+ ReadyToFanout _ ->
208203 liftEffect ws.fanout
209- HeadIsFinalized _ -> do
210- setHeadStatus HeadStatus_Final
204+ HeadIsFinalized _ ->
211205 -- TODO: output fanout tx hash
212- throwError $ error " SUCCESS: Head finalized, Funds transfered to L1 - Exiting..."
206+ throwError $ Error .error
207+ " SUCCESS: Head finalized, Funds transfered to L1 - Exiting..."
213208 _ -> pure unit
214209
215210fixCommitTx :: Transaction -> Contract Transaction
216211fixCommitTx = reSignTransaction <=< fixScriptIntegrityHash <<< setAuxDataHash
217212 where
213+ -- | Computes and sets the transaction auxiliary data hash.
214+ setAuxDataHash :: Transaction -> Transaction
215+ setAuxDataHash tx =
216+ tx # _body <<< _auxiliaryDataHash .~
217+ (hashAuxiliaryData <$> (unwrap tx).auxiliaryData)
218+
219+ -- | Removes existing vkey witnesses and signs the transaction.
220+ reSignTransaction :: Transaction -> Contract Transaction
221+ reSignTransaction tx = signTransaction (tx # _witnessSet <<< _vkeys .~ mempty)
222+
218223 fixScriptIntegrityHash :: Transaction -> Contract Transaction
219224 fixScriptIntegrityHash tx = do
220225 pparams <- unwrap <$> getProtocolParameters
@@ -235,15 +240,10 @@ contestClosureIfNeeded ws closeSnapshot = do
235240 <> " . Contesting Head closure..."
236241 liftEffect ws.challengeSnapshot
237242
238- setHeadStatus :: HydraHeadStatus -> AppM Unit
239- setHeadStatus status = do
240- App .setHeadStatus status
241- logInfo' $ " New Head status: " <> printHeadStatus status
242-
243243setUtxoSnapshot :: HydraSnapshot -> AppM Unit
244244setUtxoSnapshot snapshot = do
245245 App .setUtxoSnapshot snapshot
246- let snapshotFormatted = stringifyWithIndent 2 $ CA .encode hydraSnapshotCodec snapshot
246+ let snapshotFormatted = stringifyAeson $ CA .encode hydraSnapshotCodec snapshot
247247 logInfo' $ " New confirmed snapshot: " <> snapshotFormatted
248248
249249cleanupHandler
@@ -264,7 +264,7 @@ cleanupHandler logger { hydraNodeProcess, hydraNodeApiWsRef, contractEnv } = do
264264 ( case _ of
265265 Left err ->
266266 logger Error $ " stopContractEnv failed with error: "
267- <> message err
267+ <> Error . message err
268268 Right _ ->
269269 logger Info " Successfully completed all cleanup actions -> exiting."
270270 )
0 commit comments