@@ -48,7 +48,7 @@ import Hydra.Cardano.Api (
48
48
KeyWitnessInCtx (KeyWitnessForSpending ),
49
49
PaymentKey ,
50
50
Tx ,
51
- TxId ,
51
+ TxId ( .. ) ,
52
52
UTxO ,
53
53
addTxIns ,
54
54
addTxInsCollateral ,
@@ -138,6 +138,7 @@ import Network.HTTP.Req (
138
138
import Network.HTTP.Simple (getResponseBody , httpJSON , setRequestBodyJSON )
139
139
import Network.HTTP.Types (urlEncode )
140
140
import System.FilePath ((</>) )
141
+ import System.Process (proc , readCreateProcessWithExitCode )
141
142
import Test.Hydra.Tx.Fixture (testNetworkId )
142
143
import Test.Hydra.Tx.Gen (genKeyPair )
143
144
import Test.QuickCheck (choose , elements , generate )
@@ -298,78 +299,126 @@ restartedNodeCanAbort tracer workDir cardanoNode hydraScriptsTxId = do
298
299
nodeReObservesOnChainTxs :: Tracer IO EndToEndLog -> FilePath -> RunningNode -> [TxId ] -> IO ()
299
300
nodeReObservesOnChainTxs tracer workDir cardanoNode hydraScriptsTxId = do
300
301
refuelIfNeeded tracer cardanoNode Alice 100_000_000
302
+ refuelIfNeeded tracer cardanoNode Bob 100_000_000
301
303
-- Start hydra-node on chain tip
302
304
tip <- queryTip networkId nodeSocket
303
305
let contestationPeriod = UnsafeContestationPeriod 2
304
- let depositDeadline = UnsafeDepositDeadline 200
306
+ let deadline = 10
307
+ let depositDeadline = UnsafeDepositDeadline deadline
305
308
aliceChainConfig <-
306
- chainConfigFor Alice workDir nodeSocket hydraScriptsTxId [] contestationPeriod depositDeadline
309
+ chainConfigFor Alice workDir nodeSocket hydraScriptsTxId [Bob ] contestationPeriod depositDeadline
310
+ <&> modifyConfig (\ config -> config{networkId, startChainFrom = Nothing })
311
+
312
+ bobChainConfig <-
313
+ chainConfigFor Bob workDir nodeSocket hydraScriptsTxId [Alice ] contestationPeriod depositDeadline
307
314
<&> modifyConfig (\ config -> config{networkId, startChainFrom = Nothing })
308
315
309
316
(aliceCardanoVk, aliceCardanoSk) <- keysFor Alice
317
+ (bobCardanoVk, bobCardanoSk) <- keysFor Bob
310
318
commitUTxO <- seedFromFaucet cardanoNode aliceCardanoVk 5_000_000 (contramap FromFaucet tracer)
319
+ commitUTxO2 <- seedFromFaucet cardanoNode bobCardanoVk 7_000_000 (contramap FromFaucet tracer)
311
320
312
321
let hydraTracer = contramap FromHydraNode tracer
313
322
314
- (headId1, decrementOuts) <- withHydraNode hydraTracer aliceChainConfig workDir 1 aliceSk [] [1 ] $ \ n1 -> do
315
- send n1 $ input " Init" []
316
- headId <- waitMatch 10 n1 $ headIsInitializingWith (Set. fromList [alice])
323
+ withHydraNode hydraTracer aliceChainConfig workDir 1 aliceSk [bobVk] [2 ] $ \ n1 -> do
324
+ (headId1, decrementOuts) <- withHydraNode hydraTracer bobChainConfig workDir 2 bobSk [aliceVk] [1 ] $ \ n2 -> do
325
+ send n1 $ input " Init" []
326
+
327
+ headId <- waitMatch (20 * blockTime) n1 $ headIsInitializingWith (Set. fromList [alice, bob])
328
+ _ <- waitMatch (20 * blockTime) n2 $ headIsInitializingWith (Set. fromList [alice, bob])
329
+
330
+ requestCommitTx n1 mempty >>= submitTx cardanoNode
331
+ requestCommitTx n2 mempty >>= submitTx cardanoNode
332
+
333
+ waitFor hydraTracer (20 * blockTime) [n1, n2] $
334
+ output " HeadIsOpen" [" utxo" .= object mempty , " headId" .= headId]
317
335
318
- resp <-
336
+ resp <-
337
+ parseUrlThrow (" POST " <> hydraNodeBaseUrl n2 <> " /commit" )
338
+ <&> setRequestBodyJSON commitUTxO
339
+ >>= httpJSON
340
+
341
+ let depositTransaction = getResponseBody resp :: Tx
342
+ let tx = signTx aliceCardanoSk depositTransaction
343
+
344
+ submitTx cardanoNode tx
345
+
346
+ waitFor hydraTracer 10 [n2] $
347
+ output " CommitApproved" [" headId" .= headId, " utxoToCommit" .= commitUTxO]
348
+ waitFor hydraTracer 10 [n2] $
349
+ output " CommitFinalized" [" headId" .= headId, " depositTxId" .= getTxId (getTxBody tx)]
350
+
351
+ getSnapshotUTxO n1 `shouldReturn` commitUTxO
352
+
353
+ let aliceAddress = mkVkAddress networkId aliceCardanoVk
354
+
355
+ decommitTx <- do
356
+ let (i, o) = List. head $ UTxO. pairs commitUTxO
357
+ either (failure . show ) pure $
358
+ mkSimpleTx (i, o) (aliceAddress, txOutValue o) aliceCardanoSk
359
+
360
+ let decommitUTxO = utxoFromTx decommitTx
361
+ decommitTxId = txId decommitTx
362
+ expectedDecrementOutputs = snd <$> UTxO. pairs decommitUTxO
363
+
364
+ -- Sometimes use websocket, sometimes use HTTP
365
+ join . generate $
366
+ elements
367
+ [ send n2 $ input " Decommit" [" decommitTx" .= decommitTx]
368
+ , postDecommit n1 decommitTx
369
+ ]
370
+
371
+ waitFor hydraTracer 10 [n1, n2] $
372
+ output " DecommitRequested" [" headId" .= headId, " decommitTx" .= decommitTx, " utxoToDecommit" .= decommitUTxO]
373
+ waitFor hydraTracer 10 [n1, n2] $
374
+ output " DecommitApproved" [" headId" .= headId, " decommitTxId" .= decommitTxId, " utxoToDecommit" .= decommitUTxO]
375
+ failAfter 10 $ waitForUTxO cardanoNode decommitUTxO
376
+ waitFor hydraTracer 10 [n1, n2] $
377
+ output " DecommitFinalized" [" headId" .= headId, " distributedOutputs" .= expectedDecrementOutputs]
378
+ pure (headId, expectedDecrementOutputs)
379
+
380
+ -- Here we post a deposit while one node is down so we can test if recover works later on
381
+ resp2 <-
319
382
parseUrlThrow (" POST " <> hydraNodeBaseUrl n1 <> " /commit" )
320
- <&> setRequestBodyJSON commitUTxO
383
+ <&> setRequestBodyJSON commitUTxO2
321
384
>>= httpJSON
322
385
323
- let depositTransaction = getResponseBody resp :: Tx
324
- let tx = signTx aliceCardanoSk depositTransaction
386
+ let depositTransaction2 = getResponseBody resp2 :: Tx
387
+ let tx2 = signTx bobCardanoSk depositTransaction2
325
388
326
- submitTx cardanoNode tx
389
+ submitTx cardanoNode tx2
327
390
328
- waitFor hydraTracer (20 * blockTime) [n1] $
329
- output " HeadIsOpen" [" utxo" .= toJSON commitUTxO, " headId" .= headId]
391
+ threadDelay $ fromIntegral (deadline * 2 + 1 )
330
392
331
- getSnapshotUTxO n1 `shouldReturn` commitUTxO
393
+ bobChainConfigFromTip <-
394
+ chainConfigFor Bob workDir nodeSocket hydraScriptsTxId [Alice ] contestationPeriod depositDeadline
395
+ <&> modifyConfig (\ config -> config{networkId, startChainFrom = Just tip})
332
396
333
- let aliceAddress = mkVkAddress networkId aliceCardanoVk
334
- decommitTx <- do
335
- let (i, o) = List. head $ UTxO. pairs commitUTxO
336
- either (failure . show ) pure $
337
- mkSimpleTx (i, o) (aliceAddress, txOutValue o) aliceCardanoSk
397
+ withTempDir " blank-state" $ \ tmpDir -> do
398
+ void $ readCreateProcessWithExitCode (proc " cp" [" -r" , workDir </> " state-2" , tmpDir]) " "
399
+ void $ readCreateProcessWithExitCode (proc " rm" [" -rf" , tmpDir </> " state-2" </> " state" ]) " "
400
+ withHydraNode hydraTracer bobChainConfigFromTip tmpDir 2 bobSk [aliceVk] [1 ] $ \ n2 -> do
401
+ -- Also expect to see past server outputs replayed
402
+ headId2 <- waitMatch 5 n2 $ headIsInitializingWith (Set. fromList [alice, bob])
403
+ headId2 `shouldBe` headId1
404
+ waitFor hydraTracer 5 [n2] $
405
+ output " HeadIsOpen" [" utxo" .= object mempty , " headId" .= headId2]
338
406
339
- let decommitUTxO = utxoFromTx decommitTx
340
- decommitTxId = txId decommitTx
341
- expectedDecrementOutputs = snd <$> UTxO. pairs decommitUTxO
407
+ waitFor hydraTracer 5 [n2] $
408
+ output " DecommitFinalized" [" headId" .= headId2, " distributedOutputs" .= decrementOuts]
342
409
343
- -- Sometimes use websocket, sometimes use HTTP
344
- join . generate $
345
- elements
346
- [ send n1 $ input " Decommit" [" decommitTx" .= decommitTx]
347
- , postDecommit n1 decommitTx
348
- ]
410
+ let depositTxId = getTxId $ getTxBody depositTransaction2
411
+ let path = BSC. unpack $ urlEncode False $ encodeUtf8 $ T. pack $ show depositTxId
349
412
350
- waitFor hydraTracer 10 [n1] $
351
- output " DecommitRequested" [" headId" .= headId, " decommitTx" .= decommitTx, " utxoToDecommit" .= decommitUTxO]
352
- waitFor hydraTracer 10 [n1] $
353
- output " DecommitApproved" [" headId" .= headId, " decommitTxId" .= decommitTxId, " utxoToDecommit" .= decommitUTxO]
354
- failAfter 10 $ waitForUTxO cardanoNode decommitUTxO
355
- waitFor hydraTracer 10 [n1] $
356
- output " DecommitFinalized" [" headId" .= headId, " distributedOutputs" .= expectedDecrementOutputs]
357
- pure (headId, expectedDecrementOutputs)
358
-
359
- aliceChainConfig2 <-
360
- chainConfigFor Alice workDir nodeSocket hydraScriptsTxId [] contestationPeriod depositDeadline
361
- <&> modifyConfig (\ config -> config{networkId, startChainFrom = Just tip})
413
+ recoverResp <-
414
+ parseUrlThrow (" DELETE " <> hydraNodeBaseUrl n2 <> " /commits/" <> path)
415
+ >>= httpJSON
362
416
363
- withTempDir " blank-state" $ \ tmpDir ->
364
- withHydraNode hydraTracer aliceChainConfig2 tmpDir 1 aliceSk [] [1 ] $ \ n1 -> do
365
- -- Also expect to see past server outputs replayed
366
- headId2 <- waitMatch (20 * blockTime) n1 $ headIsInitializingWith (Set. fromList [alice])
367
- headId2 `shouldBe` headId1
368
- waitFor hydraTracer (20 * blockTime) [n1] $
369
- output " HeadIsOpen" [" utxo" .= toJSON commitUTxO, " headId" .= headId2]
417
+ (getResponseBody recoverResp :: String ) `shouldBe` " OK"
370
418
371
- waitFor hydraTracer (20 * blockTime) [n1] $
372
- output " DecommitFinalized" [" headId" .= headId2, " distributedOutputs" .= decrementOuts]
419
+ waitForAllMatch (20 * blockTime) [n2] $ \ v -> do
420
+ guard $ v ^? key " tag" == Just " CommitRecovered"
421
+ guard $ v ^? key " recoveredTxId" == Just (toJSON depositTxId)
373
422
where
374
423
RunningNode {nodeSocket, networkId, blockTime} = cardanoNode
375
424
0 commit comments