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

Improve refdocs, Add a getting-started guide, Restore searchable docs #4

Open
wants to merge 12 commits into
base: dshuiski/minimal-example
Choose a base branch
from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
/example/minimal/output/
/example/minimal/.psa-stash
/example/minimal/.spago/
/generated-docs/
/node_modules
/output/
/result
/.psa-stash
/.psci_modules/
/.spago/
/.spago2nix/
.psc-ide-port
38 changes: 26 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
.PHONY: build, format, repl, docs, build-example, run-example, docker-cleanup
.PHONY: build, format, repl, docs, build-example, run-example, docker-cleanup, gen-keys

ps-sources := $(shell fd --no-ignore-parent -epurs)
nix-sources := $(shell fd --no-ignore-parent -enix --exclude='spago*')
purs-args := "--stash --censor-lib --censor-codes=ImplicitImport,ImplicitQualifiedImport,ImplicitQualifiedImportReExport,UserDefinedWarning"
example-docker := example/minimal/docker/cluster/docker-compose.yaml

system := $(shell uname -s)
ifeq (${system},Linux)
open-in-browser := xdg-open
else
open-in-browser := open
endif
example-keys := example/minimal/docker/cluster/keys/

requires-nix-shell:
@[ "$(IN_NIX_SHELL)" ] || \
Expand All @@ -29,11 +23,13 @@ format: requires-nix-shell
repl: requires-nix-shell
spago repl

docs:
nix build .#docs
${open-in-browser} result/generated-docs/html/index.html
docs: requires-nix-shell
mv package.json package.json.old
jq 'del(.type)' package.json.old > package.json
spago docs --open
mv -f package.json.old package.json

build-example:
build-example: requires-nix-shell
cd example/minimal && \
spago build --purs-args ${purs-args}

Expand All @@ -43,3 +39,21 @@ run-example: docker-cleanup
docker-cleanup:
docker compose -f ${example-docker} rm --force --stop
docker volume rm -f cluster_hydra-persist-a cluster_hydra-persist-b

gen-keys: requires-nix-shell
@hydra-node gen-hydra-key --output-file ${example-keys}/hydra-a
@hydra-node gen-hydra-key --output-file ${example-keys}/hydra-b
@cardano-cli address key-gen \
--signing-key-file ${example-keys}/cardano-a.sk \
--verification-key-file ${example-keys}/cardano-a.vk
@cardano-cli address build \
--payment-verification-key-file ${example-keys}/cardano-a.vk \
--testnet-magic 1
@echo
@cardano-cli address key-gen \
--signing-key-file ${example-keys}/cardano-b.sk \
--verification-key-file ${example-keys}/cardano-b.vk
@cardano-cli address build \
--payment-verification-key-file ${example-keys}/cardano-b.vk \
--testnet-magic 1
@echo
157 changes: 136 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,102 @@

[Cardano Hydra](https://hydra.family/head-protocol/)
SDK (Software Development Kit) for PureScript. This library offers
various interfaces to facilitate rapid development of Hydra-based
applications.
various interfaces to facilitate the rapid development
of Hydra-based applications.

**Table of Contents**

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Applications](#applications)
- [Functionality](#functionality)
- [Compatibility](#compatibility)
- [Preliminaries](#preliminaries)
- [Development Workflows](#development-workflows)
- [Getting Started with Example](#getting-started-with-example)
- [SDK Core Functionality](#sdk-core-functionality)
- [SDK Additionals: AppManager](#sdk-additionals-appmanager)
- [Applications](#applications)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

### Applications
## Compatibility

Please refer to [hydra-auction-offchain](https://github.com/mlabs-haskell/hydra-auction-offchain)
for a full-fledged example that utilizes this SDK.
| hydra-sdk | hydra-node | cardano-node |
| ----------- | ------------ | ------------ |
| **`0.1.0`** | **`0.19.0`** | **`10.1.2`** |


## Preliminaries

Since using this SDK implies a certain degree of understanding of the Hydra
protocol, it is advisable to get familiar with the ["Hydra: Fast Isomorphic State Channels" paper](https://iohk.io/en/research/library/papers/hydra-fast-isomorphic-state-channels/)
and the [Hydra Head protocol documentation](https://hydra.family/head-protocol/docs/)
before proceeding with the **Getting Started** guide below.

## Development Workflows

Before executing most of the commands listed below, first enter the Nix
development shell by running `nix develop`.

* **Build docs and open them in the browser**: `make docs`
* **Build the project**: `make build`
* **Format code**: `make format`

## Getting Started with Example

The simplest way to get a sense of how this SDK can help build Hydra-based
applications is to run our minimal example and then adapt or extend it to suit
your specific requirements.

Go to `example/minimal` and follow the step-by-step guide below to spin up
a cluster of two nodes, with each node running the minimal example logic.

1. Enter the Nix development environment by running `nix develop` from the root
directory of this repository. This will put you in the shell with all the
necessary executables required to continue with the setup procedure.

### Functionality
2. In [example/minimal/docker/cluster/](example/minimal/docker/cluster/) you
can find configuration files for both nodes. The only field that needs to be
updated here is the `blockfrostApiKey`, which should be set to a valid
Blockfrost API key **for preprod**.
Visit the [Blockfrost website](https://blockfrost.io/) to generate a fresh API key.

The `HydraSdk.Process` module provides an interface for spinning up a hydra-node
3. Execute `make gen-keys` to generate the necessary Cardano and Hydra keys
required by the underlying Hydra nodes. Cardano keys are used to authenticate
on-chain transactions, while the Hydra keys are used for multi-signing snapshots
within a Hydra Head. This command will output two Cardano preprod addresses
that must be pre-funded with sufficient tADA to run properly functioning Hydra
nodes.
Use the [Testnets faucet](https://docs.cardano.org/cardano-testnets/tools/faucet/)
to request tADA.

4. Finally, execute `make run-example` to launch a Hydra Head
with two participants both running the minimal example logic.

NOTE: Hydra nodes require a fully synchronized Cardano node to operate
correctly. No additional setup actions need to be performed to
configure the Cardano node as it is already included in the Docker
Compose configuration.
However, node synchronization may take several hours on the first run.
Keep in mind that Hydra nodes are configured to run only once
the Cardano node is fully synchronized,
and the `cardano-node` output is suppressed to not
interfere with useful Hydra Head logs.
As a result, there will be no output during synchronization.
Instead, use `docker logs` or run `cardano-cli query tip`
from within the `cardano-node` Docker container
to track the synchronization progress.

## SDK Core Functionality

The SDK exposes the following top-level modules which constitus it API which
is considered to be relatvely stable. You also can use `Internal` modules
at yor own risk.

* `HydraSdk.Process` module provides an interface for spinning up a hydra-node
as a Node.js subprocess.

The `HydraSdk.NodeApi` module exports functions for connecting to the Hydra Node
* `HydraSdk.NodeApi` module exports functions for connecting to the Hydra Node
WebSocket API and sending HTTP requests. The primary function provided by this
module is `mkHydraNodeApiWebSocket`, which establishes a WebSocket connection to
the hydra-node, attaches specified handlers for incoming messages, and returns a
Expand All @@ -35,21 +106,65 @@ Hydra Node API. It also allows to specify retry strategies for Hydra
transactions that may be silently dropped by cardano-node, particularly for
Close and Contest transactions.

The `HydraSdk.Types` module re-exports various Hydra domain-specific types
* `HydraSdk.Types` module re-exports various Hydra domain-specific types
(such as `HydraHeadStatus` and `HydraNodeApi_InMessage`), along with other
utility types (e.g., `HostPort` and `Network`) used by the components of this
library.

`HydraSdk.Extra.AppManager` provides an opinionated interface for managing
multiple Hydra application instances, with each instance running a separate
hydra-node process, as implemented in [hydra-auction-offchain](https://github.com/mlabs-haskell/hydra-auction-offchain).
For more information, refer to the [AppManager README](src/Extra/README.md).
* Lastly, `HydraSdk.Lib` module contains useful helpers like codecs for many types,
logging action and some others.

### Development Workflows
## SDK Additionals: AppManager

Before executing most of the commands listed below, first enter the Nix
development shell by running `nix develop`.
Under `Extra` folder, modules with additional functionality are located,
currently being the only one for managing multiple app instances.

**Build the project** (requires Nix shell): `make build`
**Format code** (requires Nix shell): `make format`
**Build docs and open them in the browser**: `make docs`
Originally the SDK was designed for Hydra applications, where Hydra Heads were
operated by designated delegates. In that model a delegate can be anyone who wants
to participate as a provider of computational resources to host Hydra
nodes. Delegates must form a group upfront to maintain Hydra
consensus. Upon forming a group, all members need to specify
information about their peers - such as Hydra node addresses, public
keys, etc. That way there exists a strict correspondence between
delegate configurations to start a functioning Hydra Head.

Each application instance should monitor its underlying Hydra node and
have access to information about other Hydra Head participants.
AppManager is an opinionated interface for managing multiple app
instances within a delegate group. This interface is utilized by
applications like `hydra-auction-offchain`, enabling delegates to host
multiple Layer-2 auctions simultaneously.

At the core of AppManager is the concept of slots. When delegates
decide to form a group, they agree on the configurations for their
future Hydra nodes. Since each delegate have to specify information
about their peers (hydra-node address, public keys, etc.), there must
be strict correspondence between delegate configurations to start
a working Hydra Head. This is where slots come into play. Each slot
represents a set of delegate configurations sufficient to spin up a
properly configured Hydra Head. In hydra-auction-offchain, slot
numbers are implicitly derived from the configurations provided to
the delegate-server, with the first configuration corresponding to
slot 0, the second to slot 1, and so forth. Users are expected to
reserve slots before making an initial Layer-1 commitment, such as
announcing an auction. Upon reservation, they receive secrets from
each delegate, which can later be provided to host a Layer-2
application in the reserved slot.

One clear drawback of this approach is the potential for malicious
actors to reserve all available slots within a delegate group,
effectively paralyzing its operations. In real-world applications, a
flooding detection mechanism should be implemented to prevent this
scenario, although there seems to be no obvious incentive for anyone
to carry out such an attack.

Coming with the limitations mentioned, this approach simplifies things
since it neither requires communication and synchronization between
delegates during runtime nor does it rely on a central server to
orchestrate the initialization of Hydra Heads, making it a great fit
for hydra-auction-offchain and hopefully other Hydra applications.

## Applications

Please refer to [hydra-auction-offchain](https://github.com/mlabs-haskell/hydra-auction-offchain)
for a full-fledged example that utilizes this SDK.
13 changes: 1 addition & 12 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
packageLockOnly = true;
packages = with pkgs; [
fd
jq
hydra.packages.${system}.hydra-node
nixpkgs-fmt
nodePackages.prettier
Expand All @@ -76,18 +77,6 @@
}
);

packages = perSystem (system:
let
pkgs = nixpkgsFor system;
project = psProjectFor system pkgs;
in
{
docs = project.buildPursDocs {
packageName = projectName;
};
}
);

apps = perSystem (system:
let
pkgs = nixpkgsFor system;
Expand Down
4 changes: 4 additions & 0 deletions src/Extra/AppManager.purs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
--| An opinionated interface for managing multiple Hydra application instances,
--| with each instance running a separate hydra-node process.
--| Please refer to README.md for more details.

module HydraSdk.Extra.AppManager
( module ExportAppManager
) where
Expand Down
46 changes: 0 additions & 46 deletions src/Extra/README.md

This file was deleted.

Loading