{"id":24552010,"url":"https://github.com/mlabs-haskell/hydra-demo","last_synced_at":"2025-04-15T22:20:36.048Z","repository":{"id":37097343,"uuid":"486527556","full_name":"mlabs-haskell/hydra-demo","owner":"mlabs-haskell","description":"Hydra demo","archived":false,"fork":false,"pushed_at":"2022-08-02T16:41:59.000Z","size":2998,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-29T02:04:20.659Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mlabs-haskell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-04-28T09:25:51.000Z","updated_at":"2022-07-23T07:17:12.000Z","dependencies_parsed_at":"2022-07-12T02:30:22.492Z","dependency_job_id":null,"html_url":"https://github.com/mlabs-haskell/hydra-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fhydra-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fhydra-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fhydra-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mlabs-haskell%2Fhydra-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mlabs-haskell","download_url":"https://codeload.github.com/mlabs-haskell/hydra-demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249161943,"owners_count":21222571,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-23T01:19:43.140Z","updated_at":"2025-04-15T22:20:36.029Z","avatar_url":"https://github.com/mlabs-haskell.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Overview\n\nA simple betting game for two players (“Rock-Paper-Scissors”) played inside a Hydra head.\n\n# Goals\n\n- Explore the implementation possibilities of a simple but still non-trivial distributed application with Hydra Node WebSocket API\n- Relate any feedback or pain points encountered during development to the Hydra team\n- Produce an experience report for the whole process that can be used as a reference both for the Hydra team to understand the hurdles encountered and for other parties wanting to develop on Hydra.\n\n## Advantages of Hydra in this use case\n\n* Rapid games in close to realtime\n* Zero fees once funds are in the hydra head\n\n## Overall architecture\n\nWe'll be running a local devnet consisting of the following components:\n\n* a single block producing Cardano Node\n* For each player\n    * a Hydra Node communicating with the Cardano Node through its Unix socket\n    * an application which communicates with this Hydra Node through WS API and also reads commands from the user's terminal and produces some diagnostic output reflecting the Hydra Head state transitions\n\n# Running the app\n\n## Building and setting up the environment\n\nWe'll need Docker, Docker Compose, and Nix with `nix-command` and `flakes` features enabled.\n\nFirst, clone the git repository at `https://github.com/mlabs-haskell/hydra-demo.git` and build the application.\nBuilding may take a while the first time.\n\n```\n$ git clone https://github.com/mlabs-haskell/hydra-demo.git\n$ cd hydra-demo\n$ nix build .\n```\n\nThen pull all the necessary Docker images, and spin up the devnet. From the repository root:\n\n```\n$ docker-compose pull\n\n$ ./spin-up-devnet-from-scratch\n```\n\nIt's worth noting that `spin-up-devnet-from-scratch` invokes `sudo` and may prompt for your password, but fear not! It is there just to remove any stale `./devnet` data directory from previous runs.\n\nNow that the stage is set, we can finally start the application instances.\n\nFrom the repository root in two separate terminals execute:\n\n```\nnix run . 127.0.0.1 4001 devnet/credentials/alice.sk devnet/protocol-parameters.json\n```\n\nand\n\n```\nnix run . 127.0.0.1 4002 devnet/credentials/bob.sk devnet/protocol-parameters.json\n```\n\nEach of these will spin up an instance of the application, connected to a different hydra-node (cfr. with the `docker-compose.yaml` file). We must also pass in the signing keys for each user, as the CLI will sign txs and submit them to the hydra node.\n## Opening the head\n\nWe can now open the head and start playing the RPS game.\n[This](https://hydra.family/head-protocol/core-concepts) page has a good explanation (alongside nice diagrams) to show all the phases of the Hydra head, an in-depth discussion of those is out of the scope of this tutorial, so we invite the reader to familiarise themselves with that before going forward.\n\nTo open the head, we must submit an `Init`, followed by each user committing (`Commit`) some UTXOs from the Cardano main chain to the head.\n\nEither player can send an `init n` command through the CLI, where `n` is the number of seconds allowed for contestation.\n\nAfter that, each participant must commit their funds. If you paid attention when running the `spin-up-devnet-from-scratch` script, you might have noticed the last lines logging something about seeding transactions.\nWhat happened there is that we distributed some funds to both Alice and Bob so that they have an UTxO to commit to the head (we also create transactions sending both of them fuel, which is needed by the node to drive the head protocol forward, fuel will be ignored for the rest of this discussion. Refer to [the hydra docs](https://hydra.family/head-protocol/docs/getting-started/quickstart/#fuel) for more information).\nSince our devnet setup is completely deterministic, the TxIds and indexes for those transactions are fixed, so we can use a fixed pair of UTxOs for the commit command. It is worth running:\n\n```\ndocker-compose exec cardano-node cardano-cli query utxo --testnet-magic 42 --whole-utxo\n```\n\nAt this point to see how the initial funds have been distributed between the two users.\n\nNow, Alice can commit some funds to the head by running:\n\n```\ncommit 3eeea5c2376b033d5bdeab6fe551950883b04c08a37848c6d648ea03476dce83#1 addr_test1vru2drx33ev6dt8gfq245r5k0tmy7ngqe79va69de9dxkrg09c7d3 1000000000\n```\n\nThe `commit` command requires the `TxIn` of the UTxO being committed, as mentioned previously these are deterministic so the one above will always work.\nWe also pass in the `address` of the committer and the amount of lovelace in the UTxO (this is only done for convenience, we could fetch this information from the `TxId`).\n\nSimilarly, Bob can commit some funds with:\n\n```\ncommit bd279aad1fa00d7f5cd00b33ad0ae20ac493f29558809a110761b4d3136324a3#1 addr_test1vqg9ywrpx6e50uam03nlu0ewunh3yrscxmjayurmkp52lfskgkq5k 1000000000\n```\n\nAt this point, the head will be open and we can start placing and collecting bets.\n\n## Playing the game\n\nWe are finally ready to place some bets inside the head.\nPlayers have two commands they can use: `bet` and `claim`.\n`bet` takes as argument the gesture (Rock, Paper or Scissors) and a salt that is used to encrypt the gesture on-chain.\n\nAn example `bet` for Alice could be something like:\n\n```\nbet Rock 1234\n```\n\nwhile Bob could bet:\n\n```\nbet Paper 5678\n```\n\nOnce the two bets are placed, either player can issue a claim command (the CLI will create the appropriate tx based on who won the round). The claim command takes as arguments your own salt and the pair of pkh and salt from the other player (this information is required to build the redeemer for the locked UTxOs).\n\n\nIn this example, Bob would issue:\n\n```\nclaim 5678 f8a68cd18e59a6ace848155a0e967af64f4d00cf8acee8adc95a6b0d 1234\n```\n\nto claim the UTxO pair, as he is the winner of this round.\n\n(Note the CLI will print the address and pkh of each participant on startup)\n\nFrom this moment onwards we can play around with the CLI placing more bets and testing the claim. Keep in mind that currently, the claim command will try to claim the first pair of UTxOs at the script address that matches the given salts.\n\n## Closing the head\n\nOnce we are done playing the game, we can close the head and observe the funds being re-distributed to the Cardano main chain.\nAt any time, a player can issue a `close` command through the CLI. This will cause the head to transition to the `Closing` state immediately, and stop processing further transactions. After the contestation period is over, either user can issue a `fanout` command through the CLI that will finalise the head and distribute funds according to their distribution inside the head.\n\nWe can, once again, check the UTxO distribution on the main chain by running:\n\n```\ndocker-compose exec cardano-node cardano-cli query utxo --testnet-magic 42 --whole-utxo\n```\n\nTo confirm that the funds have been distributed correctly.\n\n# Going forward\n\nThe goal of this project was to explore building a simple - but not trivial - application that runs inside a hydra head.\nWe have implemented this as a CLI tool for simplicity, but we could imagine this game being played online through a web UI. This would be a nice exercise in extending this project which would come with its unique sets of challenges.\nAnother interesting avenue to explore would be to adapt the setup so that hydra-nodes can be run from separate networks.\n\nWith these two extensions, this project could be the basis for the first website that allows players to play rounds of RPS (or any other betting game) inside a hydra head.\n\n# Code\n\nThe code is split up as follows:\n\nLibrary code can be found in `src/HydraRPS`.\n\n- `OnChain` module contains the validator and all on-chain code and data structures.\n\n- `OffChain` module contains some off-chain code, this was mainly developed to test the contract and is used by tests.\n\n- `App` is the heart of the app, it contains most of the code to interact with the hydra node and all the main data structures.\n\n- `UserInput` contains some data definitions shared by off-chain code and code that interacts with the head\n\n- `Node.Command` contains data structures and instances for hydra-node commands\n\n- `Tx` contains some common functions to build transactions through cardano-lib.\n\nThe executable main code can be found in `app/Main`. This simply parses the command line arguments that are used by the CLI and takes care of converting user input (acquired through stdin) to the proper `UserCommand`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlabs-haskell%2Fhydra-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmlabs-haskell%2Fhydra-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmlabs-haskell%2Fhydra-demo/lists"}