Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example of the simple dropout example on-top of the raft branch #1786

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions hydra-cluster/src/Hydra/Cluster/Scenarios.hs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ import Hydra.Cardano.Api (
)
import Hydra.Cluster.Faucet (FaucetLog, seedFromFaucet, seedFromFaucet_)
import Hydra.Cluster.Faucet qualified as Faucet
import Hydra.Cluster.Fixture (Actor (..), actorName, alice, aliceSk, aliceVk, bob, bobSk, bobVk, carol, carolSk)
import Hydra.Cluster.Fixture (Actor (..), actorName, alice, aliceSk, aliceVk, bob, bobSk, bobVk, carol, carolSk, carolVk)
import Hydra.Cluster.Mithril (MithrilLog)
import Hydra.Cluster.Options (Options)
import Hydra.Cluster.Util (chainConfigFor, keysFor, modifyConfig, setNetworkId)
import Hydra.Ledger.Cardano (mkSimpleTx)
import Hydra.Ledger.Cardano (mkTransferTx, mkSimpleTx)
import Hydra.Logging (Tracer, traceWith)
import Hydra.Options (DirectChainConfig (..), networkId, startChainFrom)
import Hydra.Tx (HeadId, IsTx (balance), Party, txId)
Expand Down Expand Up @@ -117,6 +117,80 @@ data EndToEndLog
deriving stock (Eq, Show, Generic)
deriving anyclass (ToJSON, FromJSON)

oneOfNNodesCanDropForAWhile :: Tracer IO EndToEndLog -> FilePath -> RunningNode -> TxId -> IO ()
oneOfNNodesCanDropForAWhile tracer workDir cardanoNode hydraScriptsTxId = do
let clients = [Alice, Bob, Carol]
[(aliceCardanoVk, aliceCardanoSk), (bobCardanoVk, _), (carolCardanoVk, _)] <- forM clients keysFor
seedFromFaucet_ cardanoNode aliceCardanoVk 100_000_000 (contramap FromFaucet tracer)
seedFromFaucet_ cardanoNode bobCardanoVk 100_000_000 (contramap FromFaucet tracer)
seedFromFaucet_ cardanoNode carolCardanoVk 100_000_000 (contramap FromFaucet tracer)

let contestationPeriod = UnsafeContestationPeriod 1
aliceChainConfig <-
chainConfigFor Alice workDir nodeSocket hydraScriptsTxId [Bob, Carol] contestationPeriod
<&> setNetworkId networkId

bobChainConfig <-
chainConfigFor Bob workDir nodeSocket hydraScriptsTxId [Alice, Carol] contestationPeriod
<&> setNetworkId networkId

carolChainConfig <-
chainConfigFor Carol workDir nodeSocket hydraScriptsTxId [Alice, Bob] contestationPeriod
<&> setNetworkId networkId

withHydraNode hydraTracer aliceChainConfig workDir 1 aliceSk [bobVk, carolVk] [1, 2, 3] $ \n1 -> do
aliceUTxO <- seedFromFaucet cardanoNode aliceCardanoVk 1_000_000 (contramap FromFaucet tracer)
withHydraNode hydraTracer bobChainConfig workDir 2 bobSk [aliceVk, carolVk] [1, 2, 3] $ \n2 -> do
withHydraNode hydraTracer carolChainConfig workDir 3 carolSk [aliceVk, bobVk] [1, 2, 3] $ \n3 -> do
-- Init
send n1 $ input "Init" []
headId <- waitForAllMatch (10 * blockTime) [n1, n2, n3] $ headIsInitializingWith (Set.fromList [alice, bob, carol])

-- Alice commits something
requestCommitTx n1 aliceUTxO >>= submitTx cardanoNode

-- Everyone else commits nothing
mapConcurrently_ (\n -> requestCommitTx n mempty >>= submitTx cardanoNode) [n2, n3]

-- Observe open with the relevant UTxOs
waitFor hydraTracer (20 * blockTime) [n1, n2, n3] $
output "HeadIsOpen" ["utxo" .= toJSON aliceUTxO, "headId" .= headId]

-- Perform a simple transaction from alice to herself
utxo <- getSnapshotUTxO n1
tx <- mkTransferTx networkId utxo aliceCardanoSk aliceCardanoVk
send n1 $ input "NewTx" ["transaction" .= tx]

-- Everyone confirms it
waitForAllMatch (200 * blockTime) [n1, n2, n3] $ \v -> do
guard $ v ^? key "tag" == Just "SnapshotConfirmed"
guard $ v ^? key "snapshot" . key "snapshotNumber" == Just (toJSON (1 :: Integer))

-- Carol disconnects and the others observe it
-- waitForAllMatch (100 * blockTime) [n1, n2] $ \v -> do
-- guard $ v ^? key "tag" == Just "PeerDisconnected"

-- Alice never-the-less submits a transaction
utxo <- getSnapshotUTxO n1
tx <- mkTransferTx networkId utxo aliceCardanoSk aliceCardanoVk
send n1 $ input "NewTx" ["transaction" .= tx]

-- Carol reconnects, and then the snapshot can be confirmed
withHydraNode hydraTracer carolChainConfig workDir 3 carolSk [aliceVk, bobVk] [1, 2, 3] $ \n3 -> do
-- Note: We can't use `waitForAlMatch` here as it expects them to
-- emit the exact same datatype; but Carol will be behind in sequence
-- numbers as she was offline.
flip mapConcurrently_ [n1, n2, n3] $ \n ->
waitMatch (200 * blockTime) n $ \v -> do
guard $ v ^? key "tag" == Just "SnapshotConfirmed"
guard $ v ^? key "snapshot" . key "snapshotNumber" == Just (toJSON (2 :: Integer))
-- Just check that everyone signed it.
let sigs = v ^.. key "signatures" . key "multiSignature" . values
guard $ length sigs == 3
where
RunningNode{nodeSocket, networkId, blockTime} = cardanoNode
hydraTracer = contramap FromHydraNode tracer

restartedNodeCanObserveCommitTx :: Tracer IO EndToEndLog -> FilePath -> RunningNode -> TxId -> IO ()
restartedNodeCanObserveCommitTx tracer workDir cardanoNode hydraScriptsTxId = do
let clients = [Alice, Bob]
Expand Down
7 changes: 7 additions & 0 deletions hydra-cluster/test/Test/EndToEndSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import Hydra.Cluster.Scenarios (
singlePartyHeadFullLifeCycle,
testPreventResumeReconfiguredPeer,
threeNodesNoErrorsOnOpen,
oneOfNNodesCanDropForAWhile,
)
import Hydra.Cluster.Util (chainConfigFor, keysFor, modifyConfig)
import Hydra.Ledger.Cardano (mkRangedTx, mkSimpleTx)
Expand Down Expand Up @@ -195,6 +196,12 @@ spec = around (showLogsOnFailure "EndToEndSpec") $ do
>>= canDecommit tracer tmpDir node

describe "three hydra nodes scenario" $ do
it "can survive a bit of downtime of 1 in 3 nodes" $ \tracer -> do
withClusterTempDir $ \tmpDir -> do
withCardanoNodeDevnet (contramap FromCardanoNode tracer) tmpDir $ \node ->
publishHydraScriptsAs node Faucet
>>= oneOfNNodesCanDropForAWhile tracer tmpDir node

it "does not error when all nodes open the head concurrently" $ \tracer ->
failAfter 60 $
withClusterTempDir $ \tmpDir -> do
Expand Down
19 changes: 19 additions & 0 deletions hydra-node/src/Hydra/Ledger/Cardano.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,25 @@ import Test.QuickCheck (
vectorOf,
)

-- | Build a zero-fee transaction which spends the first output owned by given
-- signing key and transfers it in full to given verification key.
mkTransferTx ::
MonadFail m =>
NetworkId ->
UTxO ->
SigningKey PaymentKey ->
VerificationKey PaymentKey ->
m Tx
mkTransferTx networkId utxo sender recipient =
case UTxO.find (isVkTxOut $ getVerificationKey sender) utxo of
Nothing -> fail "no utxo left to spend"
Just (txIn, txOut) ->
case mkSimpleTx (txIn, txOut) (mkVkAddress networkId recipient, txOutValue txOut) sender of
Left err ->
fail $ "mkSimpleTx failed: " <> show err
Right tx ->
pure tx

-- * Ledger

-- | Use the cardano-ledger as an in-hydra 'Ledger'.
Expand Down
Loading