Skip to content

Commit f241679

Browse files
committed
Extend the test so it uses two nodes and add recovering
It is hard to expose error when local deposit was not found on recover since as soon as node observes OnDepositTx it populates the local state. Signed-off-by: Sasha Bogicevic <[email protected]>
1 parent a6dfa64 commit f241679

File tree

1 file changed

+98
-49
lines changed

1 file changed

+98
-49
lines changed

hydra-cluster/src/Hydra/Cluster/Scenarios.hs

+98-49
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import Hydra.Cardano.Api (
4848
KeyWitnessInCtx (KeyWitnessForSpending),
4949
PaymentKey,
5050
Tx,
51-
TxId,
51+
TxId (..),
5252
UTxO,
5353
addTxIns,
5454
addTxInsCollateral,
@@ -138,6 +138,7 @@ import Network.HTTP.Req (
138138
import Network.HTTP.Simple (getResponseBody, httpJSON, setRequestBodyJSON)
139139
import Network.HTTP.Types (urlEncode)
140140
import System.FilePath ((</>))
141+
import System.Process (proc, readCreateProcessWithExitCode)
141142
import Test.Hydra.Tx.Fixture (testNetworkId)
142143
import Test.Hydra.Tx.Gen (genKeyPair)
143144
import Test.QuickCheck (choose, elements, generate)
@@ -298,78 +299,126 @@ restartedNodeCanAbort tracer workDir cardanoNode hydraScriptsTxId = do
298299
nodeReObservesOnChainTxs :: Tracer IO EndToEndLog -> FilePath -> RunningNode -> [TxId] -> IO ()
299300
nodeReObservesOnChainTxs tracer workDir cardanoNode hydraScriptsTxId = do
300301
refuelIfNeeded tracer cardanoNode Alice 100_000_000
302+
refuelIfNeeded tracer cardanoNode Bob 100_000_000
301303
-- Start hydra-node on chain tip
302304
tip <- queryTip networkId nodeSocket
303305
let contestationPeriod = UnsafeContestationPeriod 2
304-
let depositDeadline = UnsafeDepositDeadline 200
306+
let deadline = 10
307+
let depositDeadline = UnsafeDepositDeadline deadline
305308
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
307314
<&> modifyConfig (\config -> config{networkId, startChainFrom = Nothing})
308315

309316
(aliceCardanoVk, aliceCardanoSk) <- keysFor Alice
317+
(bobCardanoVk, bobCardanoSk) <- keysFor Bob
310318
commitUTxO <- seedFromFaucet cardanoNode aliceCardanoVk 5_000_000 (contramap FromFaucet tracer)
319+
commitUTxO2 <- seedFromFaucet cardanoNode bobCardanoVk 7_000_000 (contramap FromFaucet tracer)
311320

312321
let hydraTracer = contramap FromHydraNode tracer
313322

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]
317335

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 <-
319382
parseUrlThrow ("POST " <> hydraNodeBaseUrl n1 <> "/commit")
320-
<&> setRequestBodyJSON commitUTxO
383+
<&> setRequestBodyJSON commitUTxO2
321384
>>= httpJSON
322385

323-
let depositTransaction = getResponseBody resp :: Tx
324-
let tx = signTx aliceCardanoSk depositTransaction
386+
let depositTransaction2 = getResponseBody resp2 :: Tx
387+
let tx2 = signTx bobCardanoSk depositTransaction2
325388

326-
submitTx cardanoNode tx
389+
submitTx cardanoNode tx2
327390

328-
waitFor hydraTracer (20 * blockTime) [n1] $
329-
output "HeadIsOpen" ["utxo" .= toJSON commitUTxO, "headId" .= headId]
391+
threadDelay $ fromIntegral (deadline * 2 + 1)
330392

331-
getSnapshotUTxO n1 `shouldReturn` commitUTxO
393+
bobChainConfigFromTip <-
394+
chainConfigFor Bob workDir nodeSocket hydraScriptsTxId [Alice] contestationPeriod depositDeadline
395+
<&> modifyConfig (\config -> config{networkId, startChainFrom = Just tip})
332396

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]
338406

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]
342409

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
349412

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
362416

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"
370418

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)
373422
where
374423
RunningNode{nodeSocket, networkId, blockTime} = cardanoNode
375424

0 commit comments

Comments
 (0)