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

Incremental commits off-chain #1541

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
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
92 changes: 75 additions & 17 deletions docs/docs/dev/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,100 @@

Additional implementation-specific documentation for the Hydra Head protocol and extensions like incremental decommits.

### Incremental Commits

#### Deposit flow

```mermaid
sequenceDiagram
Alice->>+Node A: POST /commit UTxO
Node A-->>-Alice: depositTx
Alice ->> Alice: sign depositTx
Alice ->> Chain: submit depositTx
Chain ->>+ Node A: OnDepositTx utxo
Chain ->>+ Node B: OnDepositTx utxo
Node A -->> Alice: DepositDetected
par Alice isLeader
Node A->>Node A: ReqSn utxoToCommit
and
Node A->>Node B: ReqSn utxoToCommit
end
Node A -->> Alice: CommitRequested
Node A->>Node A: sig = sign snapshot incl. utxoToCommit
par broadcast
Node A->>Node A: AckSn sig
and
Node A->>Node B: AckSn sig
end
Node B->>Node A: AckSn sig
Node A -->> Alice: SnapshotConfirmed
Node A -->> Alice: CommitApproved
Node A ->> Chain: IncrementTx snapshot sig
Chain ->> Node A: OnIncrementTx
Node A -->> Alice: CommitFinalized
```

#### Recover flow

```mermaid
sequenceDiagram
Alice->>+Node A: DELETE /commits/<tx-in>
Node A->>Chain: recoverTx
Chain ->>+ Node A: OnRecoverTx utxo
Chain ->>+ Node B: OnRecoverTx utxo
Node A -->>- Alice: CommitRecovered
Node B -->>- Bob: CommitRecovered
Node A-->>-Alice: OK
```

### Incremental Decommits

```mermaid
sequenceDiagram
Alice->>HeadLogic: Decommit (decTx)
HeadLogic->>HeadLogic: canApply decTx
Alice->>+Node A: POST /decommit (decTx)
Node A-->>-Alice: OK
Node A->>Node A: canApply decTx
par broadcast
HeadLogic->>HeadLogic: ReqDec decTx
Node A->>Node A: ReqDec decTx
and
HeadLogic->>Node B: ReqDec decTx
Node A->>Node B: ReqDec decTx
end
HeadLogic -->> Alice: DecommitRequested
Node A -->> Alice: DecommitRequested
par Alice isLeader
HeadLogic->>HeadLogic: ReqSn decTx
Node A->>Node A: ReqSn decTx
and
HeadLogic->>Node B: ReqSn decTx
Node A->>Node B: ReqSn decTx
end
HeadLogic->>HeadLogic: canApply decTx, decUTxO = outputs(decTx)
HeadLogic->>HeadLogic: sig = sign snapshot incl. decUTxO
Node A->>Node A: canApply decTx, decUTxO = outputs(decTx)
Node A->>Node A: sig = sign snapshot incl. decUTxO
par broadcast
HeadLogic->>HeadLogic: AckSn sig
Node A->>Node A: AckSn sig
and
HeadLogic->>Node B: AckSn sig
Node A->>Node B: AckSn sig
end
Node B->>HeadLogic: AckSn sig
Node B->>Node A: AckSn sig
HeadLogic -->> Alice: SnapshotConfirmed
HeadLogic -->> Alice: DecommitApproved
Node A -->> Alice: SnapshotConfirmed
Node A -->> Alice: DecommitApproved
HeadLogic ->> Chain: DecrementTx snapshot sig
Chain ->> HeadLogic: OnDecrementTx
HeadLogic -->> Alice: DecommitFinalized
Node A ->> Chain: DecrementTx snapshot sig
Chain ->> Node A: OnDecrementTx
Node A -->> Alice: DecommitFinalized
```
92 changes: 92 additions & 0 deletions docs/docs/how-to/incremental-commit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Commit funds to an open Head

Assuming we already have an open Head and some funds on the L1 we would like to commit.

### Demo setup

For example a demo setup could use our `hydra-cluster` binary:

:::caution TODO
Could also copy the faucet keys for more convenience.
:::

```shell
cabal run hydra-cluster -- \
--devnet \
--publish-hydra-scripts \
--state-directory incremental-demo
```

The following commands expect these environment variables:

```shell
export CARDANO_NODE_SOCKET_PATH=${PWD}/incremental-demo/node.socket
export CARDANO_NODE_NETWORK_ID=42
```

We can inspect the L1 utxo with:

```shell
cardano-cli query utxo --whole-utxo
```
and

```shell
cardano-cli query utxo \
--address $(cardano-cli address build --payment-verification-key-file hydra-cluster/config/credentials/faucet.vk)
```


In this setup, we would be using the `faucet` keys to commit everything into the head.

```shell
export WALLET_SK=${PWD}/hydra-cluster/config/credentials/faucet.sk
export WALLET_VK=${PWD}/hydra-cluster/config/credentials/faucet.vk
cd incremental-demo
```

### Deposit UTxO to commit

To observe funds owned on L2 we can use `hydra-tui`

```shell
cabal run hydra-tui -- --cardano-signing-key ${WALLET_SK}
```

The `/commit` endpoint supports two ways of specifying what to commit, one is just by showing the UTxO (which is assumed to be owned by public keys), while the more advanced way would be using [blueprint transactions](./commit-blueprint).

We are using the simple request here and want to commit everything owned by `${WALLET_SK}`. So we can just query the L1 for all UTxO owned by it:
```shell
cardano-cli query utxo \
--address $(cardano-cli address build --payment-verification-key-file ${WALLET_VK}) \
--out-file commit-utxo.json
```

Then a request to the `/commit` endpoint provides us with a transaction:

:::danger FIXME
Should be able to do just this:
```shell
curl -X POST localhost:4001/commit \
--data @commit-utxo.json
```
:::

```shell
jq '{utxo: .}' commit-utxo.json | \
curl -X POST localhost:4001/commit --data @- \
> deposit-tx.json
```

Which we can submit to the cardano network:
```shell
cardano-cli transaction sign \
--tx-file deposit-tx.json \
--signing-key-file ${WALLET_SK} \
--out-file deposit-tx.signed.json

cardano-cli transaction submit \
--tx-file deposit-tx.signed.json
```

This will result in a deposit being detected by the `hydra-node` and consequently the funds to be committed to the head.
1 change: 1 addition & 0 deletions hydra-cardano-api/src/Hydra/Cardano/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ import Cardano.Api.UTxO (
UTxO' (..),
)
import Cardano.Ledger.Coin as X (Coin (..))
import Hydra.Cardano.Api.Network as X (networkIdToNetwork)
import Hydra.Cardano.Api.Prelude (
Era,
LedgerEra,
Expand Down
1 change: 1 addition & 0 deletions hydra-chain-observer/hydra-chain-observer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ test-suite tests
type: exitcode-stdio-1.0
build-depends:
, hspec
, hydra-cardano-api
, hydra-chain-observer
, hydra-node
, hydra-prelude
Expand Down
6 changes: 6 additions & 0 deletions hydra-chain-observer/src/Hydra/ChainObserver.hs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ data ChainObserverLog
| HeadInitTx {headId :: HeadId}
| HeadCommitTx {headId :: HeadId}
| HeadCollectComTx {headId :: HeadId}
| HeadDepositTx {headId :: HeadId}
| HeadRecoverTx {headId :: HeadId}
| HeadIncrementTx {headId :: HeadId}
| HeadDecrementTx {headId :: HeadId}
| HeadCloseTx {headId :: HeadId}
| HeadFanoutTx {headId :: HeadId}
Expand Down Expand Up @@ -203,6 +206,9 @@ chainSyncClient tracer networkId startingPoint observerHandler =
OnInitTx{headId} -> HeadInitTx{headId}
OnCommitTx{headId} -> HeadCommitTx{headId}
OnCollectComTx{headId} -> HeadCollectComTx{headId}
OnIncrementTx{headId} -> HeadIncrementTx{headId}
OnDepositTx{headId} -> HeadDepositTx{headId}
OnRecoverTx{headId} -> HeadRecoverTx{headId}
OnDecrementTx{headId} -> HeadDecrementTx{headId}
OnCloseTx{headId} -> HeadCloseTx{headId}
OnFanoutTx{headId} -> HeadFanoutTx{headId}
Expand Down
12 changes: 9 additions & 3 deletions hydra-chain-observer/test/Hydra/ChainObserverSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Hydra.ChainObserverSpec where
import Hydra.Prelude
import Test.Hydra.Prelude

import Hydra.Cardano.Api (utxoFromTx)
import Hydra.Chain.Direct.State (HasKnownUTxO (getKnownUTxO), genChainStateWithTx)
import Hydra.Chain.Direct.State qualified as Transition
import Hydra.Chain.Direct.Tx (HeadObservation (..))
Expand All @@ -20,11 +21,13 @@ spec =
forAllBlind genChainStateWithTx $ \(_ctx, st, tx, transition) ->
genericCoverTable [transition] $
counterexample (show transition) $
let utxo = getKnownUTxO st
let utxo = getKnownUTxO st <> utxoFromTx tx
in case snd $ observeTx testNetworkId utxo tx of
Just (Init{}) -> transition === Transition.Init
Just (Commit{}) -> transition === Transition.Commit
Just (CollectCom{}) -> transition === Transition.Collect
Just (Deposit{}) -> transition === Transition.Deposit
Just (Increment{}) -> transition === Transition.Increment
Just (Decrement{}) -> transition === Transition.Decrement
Just (Abort{}) -> transition === Transition.Abort
Just (Close{}) -> transition === Transition.Close
Expand All @@ -33,9 +36,12 @@ spec =
_ -> property False

prop "Updates UTxO state given transaction part of Head lifecycle" $
forAllBlind genChainStateWithTx $ \(_ctx, st, tx, _transition) ->
forAllBlind genChainStateWithTx $ \(_ctx, st, tx, transition) ->
let utxo = getKnownUTxO st
in fst (observeTx testNetworkId utxo tx) =/= utxo
in -- NOTE: deposit doesn't affect the Head UTxO state
if transition == Transition.Deposit
then property True
else fst (observeTx testNetworkId utxo tx) =/= utxo

prop "Does not updates UTxO state given transactions outside of Head lifecycle" $
forAll genSequenceOfSimplePaymentTransactions $ \(utxo, txs) ->
Expand Down
1 change: 1 addition & 0 deletions hydra-cluster/hydra-cluster.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ library
, directory
, filepath
, http-conduit
, http-types
, hydra-cardano-api
, hydra-node
, hydra-prelude
Expand Down
Loading
Loading