This repository is archived, to be superseded by the cardano-hydrozoa/hydrozoa repository when it becomes public ("soon").
By George Flerovsky
This paper introduces Hydrozoa — an adaptation of the Coordinated Hydra Head protocol with more lightweight and scalable Hydra Heads. It introduces cheap and deterministic state update transactions as the default way to commit utxos to and decommit utxos from the Hydra Head. A similarly cheap finalization procedure can be used to stop operating the head if consensus can be maintained throughout its life. The halt and dissolve procedures (analogous to close and fanout in the Coordinated Hydra Head) are more expensive and should only be used if L2 consensus breaks down.
The notation used throughout this document is similar to the notation of the Coordinated Hydra Head protocol spec and other Hydra papers.
The Hydra Head protocol allows a group of participants to temporarily transfer control over a subset of utxos from the Cardano mainnet (L1) ledger to a private unanimous consensus protocol (L2) run by the participants, instead of the Ouroboros consensus protocol running on the whole Cardano mainnet. While the Hydra Head controls these utxos, transactions involving them are quickly broadcast among the participants and then confirmed with immediate finality as soon as all participants sign a corresponding snapshot. The utxos resulting from the L2 transactions can be brought back under L1 control by finalizing an L2 snapshot (via Close/Contest transactions) and then fanning them out on L1 from the state utxo.
The protocol was developed before Plutus was added to mainnet, effective design patterns emerged for building dApps in the eUTXO model, and the Vasil enhancements enabled a completely different approach to dApp design on Cardano. It can be improved by applying the lessons learned over the past year.
The main design ideas of Hydrozoa are:
- Eliminate the initialization phase — open the Hydra Head immediately with an empty L2 active utxo set.
- Commit utxos on L1 to the head simply by sending them to a native script address controlled by the participants' keys.
- Transact on L2 in the same way as before, using Cardano ledger rules to apply transactions to the L2 active utxo set.
- Decommit utxos via a new L2 request to remove certain utxos from the L2 active utxo set, validated like an input-only transaction via modified Cardano ledger rules.
- While the head is open, periodically collect commits and release decommits on L1, updating the major version in the L1 head state. Semantic versioning is used because these updates are backwards incompatible with previous snapshots — updates change the balance of funds at the head state utxo.
- If participants can maintain L2 consensus, finalize the head and release its L2 active utxo set on L1 just as cheaply and efficiently as if all utxos decommitted.
- If L2 consensus breaks down, halt the head and dissolve its remaining funds to produce outputs corresponding to the L2 active utxo set of the snapshot with which it was halted. Dissolution is specified and authorized via multi-signed certificates instead of multi-signed transactions.
- Spin off the contestation mechanism so that it can only affect the head state when the contestation deadline elapses or all participants have exercised their right to contest.
- Updates should be used during normal operation to commit and decommit utxos, finalization should be used if the participants want to stop operating the head, and the contestation mechanism should only be used to halt the head if consensus breaks down.
The L1 component of the Hydra Head protocol becomes simpler, cheaper, and more scalable. In exchange, the L2 component is somewhat more complicated.
A group of participants
- Establish pairwise communication channels between each other.
- Generate the participant keys
$K = \{(k_{i}^\textrm{ver}, k_i^\textrm{sig})\}$ , with each signing key$k_i^\textrm{sig}$ kept privately by its respective participant and each verification key shared with all participants. - Agree on the timeout period for pending commits
$T_P$ . - Agree on the contestation period
$T_C$ .
Unlike the existing Hydra Head protocol, there is no need to generate a separate set of L2 participant keys, because the initialization phase has been eliminated. The participant keys
The participants submit a multi-signed L1 open transaction
- Spend a seed input utxo
$\phi_\textrm{seed}$ . - Mint a state token
$\textrm{ST}$ . - Mint a contestation thread token
$\textrm{CT}$ . - Produce the head state utxo at the unparametrized validator
$\nu_\textrm{head}$ . - Produce the contestation thread utxo at the unparametrized validator
$\nu_\textrm{contest}$ .
The head state utxo is uniquely identified by the state token
The datum of the state utxo contains the immutable
The state of the Hydra Head is either open, halted, or final:
The state is initialized as
A head can transition out of the open state in two ways:
- The participants can unanimously agree to finalize the head.
- One of the participants can unilaterally trigger the contestation mechanism, in order to halt the head.
stateDiagram
direction LR
[*] --> Open: open
Open --> Open: update
Open --> Final: finalize
Open --> Halted: halt
Final --> [*]: cleanup
Halted --> [*]: dissolve
Figure 1: State diagram for the Hydra Head on L1.
The Hydra Head opens with an empty L2 active utxo set. To populate it, utxos must be committed to the head on L1.
Suppose that one of that participants wants a utxo
The participant should prepare (but neither sign nor submit) an L1 commit transaction
The native script
Before signing or submitting
The participants provide this assurance by multi-signing an L1 assurance transaction
When the participant obtains this multi-signed assurance transaction
Any participant may submit a new transaction on L2 while the head is open. The transaction is validated according to the Cardano ledger rules, and the effect of a valid L2 transaction is to remove its inputs and add its outputs to the L2 active utxo set.
Each participant caches the received L2 transaction requests, without validating them, until they are needed to create or verify a snapshot.
Any participant may submit a decommit request on L2 while the head is open. The decommit request must include a notional transaction for the purposes of validating the request. The transaction must:
- Spend any number of inputs.
- Reference any number of reference inputs.
- Produce no outputs.
- Mint no tokens and burn no tokens.
- Pay no fees.
- Make no reward account withdrawals.
- Contain no metadata, certificates, or update proposals.
The decommit request is valid if both of the following are true:
- When applying the UTXOW rules, no UTXOW predicate failures are thrown. (UTXO predicate failures are to be expected because the transaction is unbalanced)
- When applying the UTXOS rules, no predicate failures are thrown.
In other words, utxos at pubkey and phase-1 script addresses can be decommitted with consent from their owners, while utxos at phase-2 script addresses can be decommitted if their validators allow them to be spent in the decommit request's unbalanced notional transaction (perhaps with a redeemer specialized for this purpose).
The effect of a valid decommit request is to remove its notional transaction's inputs (but not the reference inputs) from the L2 active utxo set and add them to the L2 decommit set.
Each participant caches the received L2 decommit requests, without validating them, until they are needed to create or verify a snapshot.
Participants take turns in a round-robin fashion to create the sequence of L2 snapshots. When a snapshot leader receives a transaction request and there is no snapshot currently accumulating signatures to be confirmed, then it is time for the snapshot leader to create and broadcast the next snapshot.
Snapshots are versioned semantically. Let the previous snapshot be
A snapshot
- The snapshot version
$(s',r')$ . - Hashes of the L2 transaction and decommit requests
$\mathcal{R}^\textrm{L2}(s',r')$ that the snapshot leader has observed since the last snapshot$\Upsilon(s,r)$ , sequenced in order of arrival. If the snapshot is minor, then$\mathcal{R}^\textrm{L2}(s',r')$ does not contain decommit requests. - The assurance transactions
$\mathcal{T}_\textrm{assure}(s', r')$ for pending commits requested since the previous snapshot. - If the snapshot is major, the new L1 commits
$\Phi^\textrm{L1}_\textrm{commit}(s')$ and corresponding new L2 commits$\Phi^\textrm{L2}_\textrm{commit}(s')$ introduced into the head by the update transaction.
Update transactions cause the major version to be incremented because they cause backwards-incompatible changes to the head state:
- When a new commit is collected, previous snapshots' conditional dissolution plans are invalidated because the head state utxo now contains more funds than they expect to release from the head.
- When a decommit is released from the head, the previous plans are invalidated because the head state utxo now contains fewer funds than they expect to release from the head.
A snapshot leader must create a major snapshot
The snapshot leader applies the L2 transaction and decommit requests
- Select the largest prefix
$\Phi^\textrm{L1}_\textrm{commit}(s') \subseteq \Phi^\textrm{L1}_\textrm{eligible}(s', 0)$ of commits that can be collected in the snapshot$\Upsilon(s',0)$ , leaving the rest to be collected in future snapshots. - Derive a non-empty tree of L1 settlement transactions
$\mathcal{T}_\textrm{settle}(s')$ that together update the open head state, collect the selected L1 commits, and release outputs corresponding to the L2 decommit set in$\mathcal{L}^\textrm{L2}(s',0)$ .
The update transaction
- Spend the previous head state utxo
$\phi_\textrm{open}(s)$ with state$(\textrm{Open} \;s)$ . - Produce the updated head state utxo
$\phi_\textrm{open}(s')$ with state$(\textrm{Open} \; s')$ . - Do at least one of the following:
- Spend one or more eligible commit utxos.
- Produce one or more decommit utxos and/or aggregate decommit utxos.
If
All aggregate decommits must be sent to the participants' native script address
If a major snapshot is confirmed, then its update transaction remains valid regardless of what may happen in any future snapshots. Furthermore, each update transaction
The update transaction of a confirmed snapshot can only be invalidated if it fails to spend a commit utxo, which can occur in two ways:
- The commit utxo is never created because an alternative blockchain fork prevails and replaces the commit transaction with a conflicting transaction.
- The commit utxo is spent by its assurance transaction.
To minimize the chance of invalidation, the update transaction must only collect commits with sufficient block depth and these commits must be spendable with enough time remaining before their assurance transactions become valid. These are the "eligible" commits for
flowchart LR
head\_state\_1([Open s])
commit\_1([Commit 1])
commit\_2([Commit 2])
commit\_3([Commit 3])
commit\_4([Commit 4])
head\_state\_2([Open s'])
agg\_decommit\_1([Aggregate decommit 1])
agg\_decommit\_2([Aggregate decommit 2])
decommit\_1([Decommit 1])
decommit\_2([Decommit 2])
decommit\_3([Decommit 3])
decommit\_4([Decommit 4])
decommit\_5([Decommit 5])
update[Update]
update\_2[Next update]
split\_1[Split]
split\_2[Split]
head\_state\_1 --> update
commit\_1 --> update
commit\_2 --> update
commit\_3 --> update
commit\_4 --> update
update --> agg\_decommit\_1
update --> agg\_decommit\_2
update ----> decommit\_5
update ----> head\_state\_2
head\_state\_2 --> update\_2
agg\_decommit\_1 --> split\_1
split\_1 --> decommit\_1
split\_1 --> decommit\_2
agg\_decommit\_2 --> split\_2
split\_2 --> decommit\_3
split\_2 --> decommit\_4
style update\_2 stroke-dasharray: 5 5
Figure 2: Example transaction flow for a major snapshot's update that collects four commits and releases five decommits via two aggregate decommits. Utxos are represented by rounded nodes, while transactions are represented by square nodes.
The snapshot certificate
The snapshot's L2 ledger state
Suppose that the halted head state utxo
The root dissolution transaction
- Spend the halted head state utxo
$\phi_\textrm{halted}(s,r)$ with state$(\textrm{Halted} \; s \; r)$ , using the multi-signed snapshot certificate$\xi(s,r)$ in the redeemer. - Do at least one of the following:
- Produce one or more granular outputs.
- Produce one or more aggregate outputs. Each aggregate output
$\phi_\textrm{agg}(s,r,j)$ is at the unparametrized validator$\nu_\textrm{dissolve}$ with datum$(s, r, j)$ .
- Verify that the granular and aggregate outputs hash to
$\eta(s,r)$ .
If
The snapshot certificate references
flowchart LR
head\_state\_1([Halted s r])
agg\_output\_1([Aggregate output 1])
agg\_output\_2([Aggregate output 2])
output\_1([Output 1])
output\_2([Output 2])
output\_3([Output 3])
output\_4([Output 4])
output\_5([Output 5])
output\_6([Output 6])
output\_7([Output 7])
dissolve\_1[Dissolve]
dissolve\_2[Dissolve]
dissolve\_3[Dissolve]
head\_state\_1 -- Cert s r --> dissolve\_1
dissolve\_1 ----> output\_1
dissolve\_1 ----> output\_2
dissolve\_1 --> agg\_output\_1
dissolve\_1 --> agg\_output\_2
agg\_output\_1 -- Cert s r 1 --> dissolve\_2
dissolve\_2 --> output\_3
dissolve\_2 --> output\_4
dissolve\_3 --> output\_5
agg\_output\_2 -- Cert s r 2 --> dissolve\_3
dissolve\_3 --> output\_6
dissolve\_3 --> output\_7
Figure 3: Example transaction flow for the dissolution of a hydra head into six granular outputs via two aggregate outputs.
Suppose that the latest confirmed snapshot is
First, the participant checks the snapshot validity preconditions:
- The current L1 head state is not halted or final.
- The snapshot was received from the expected snapshot leader.
- The snapshot version numbers are correct.
The participant waits until he has observed all of the L2 transaction and decommit requests
The participant applies the L2 requests
The participant simulates an L1 ledger state
If the snapshot is major, the participant applies the settlement transactions
The participant obtains the halted ledger state
The participant derives a set of dissolution transactions
The participant applies the dissolution transactions
If the snapshot is minor, then the participant checks that:
- The snapshot certificate
$\xi(s',r')$ references the open head state utxo$\phi_\textrm{open}(s)$ produced by the update transaction in the last major snapshot$\Upsilon(s,0)$ . - The assurance transactions
$\mathcal{T}_\textrm{assure}(s',r')$ each consume a single pending commit utxo (which may not yet exist) and produce an L2-equivalent utxo. - The L2 requests
$\mathcal{R}^\textrm{L2}(s',r')$ contain no decommit requests. - The L1 active utxo set in
$\mathcal{L}^\textrm{Sim}_\textrm{dissolved}(s',r')$ , excluding the head state utxo, is equivalent to the L2 active utxo set in$\mathcal{L}^\textrm{L2}(s',r')$ .
If the snapshot is major, then the participant checks that:
- The snapshot certificate
$\xi(s',r')$ references the open head state utxo$\phi_\textrm{open}(s')$ produced by the update transaction in snapshot$\Upsilon(s',r')$ . - The assurance transactions
$\mathcal{T}_\textrm{assure}(s',r')$ each consume a single pending commit utxo (which may not yet exist) and produce an L2-equivalent utxo. - The L2 new commits
$\Phi^\textrm{L2}_\textrm{commit}(s')$ are equivalent to the L1 new commits$\Phi^\textrm{L1}_\textrm{commit}(s')$ . - The L1 active utxo set in
$\mathcal{L}^\textrm{Sim}_\textrm{updated}(s')$ , excluding the head state utxo, is equivalent to the L2 decommit set in$\mathcal{L}^\textrm{L2}(s',r')$ . - The L1 active utxo set in
$\mathcal{L}^\textrm{Sim}_\textrm{dissolved}(s',r')$ , excluding the head state utxo, is equivalent to the union of the L2 decommit and active utxo sets in$\mathcal{L}^\textrm{L2}(s',r')$ .
A snapshot
However, participants broadcast their signatures for the snapshot
- If a participant has verified the snapshot, then he broadcasts all of his snapshot signatures except for his signatures of the snapshot certificate
$\xi(s',r')$ and the update transaction$\textrm{tx}_\textrm{update}(s')$ . - If a participant has received all signatures from all participants in round 1, then he broadcasts his signature of the snapshot certificate.
- If a participant has received all signatures from all participants in round 2, and the snapshot is major, then he broadcasts his signature of the update transaction.
This ensures that no one can execute any of the snapshot's settlement or dissolution transactions until all of the participants have provided all of the signatures necessary for the snapshot's complete confirmation.
Each participant should store the transactions and certificates of every confirmed snapshot, as long as they are relevant. Confirmed settlement transactions should be submitted without delay on L1.
Suppose that one of the participants wants to stop operating the head but would prefer to obtain group consensus to do so. Instead, he withholds his usual final-round signature to confirm the current snapshot
- His final-round signature of the snapshot
$\Upsilon(s,r)$ . - Hashes of the L2 requests
$\mathcal{R}^\textrm{L2}_i(s',r')$ that he has observed since$\Upsilon(s,r)$ .
After broadcasting a finalization statement, the participant ignores all new L2 transaction and decommit requests.
When the next snapshot leader observes that one or more participants have broadcast a finalization statement instead of the usual snapshot confirmation message, the snapshot leader creates a final snapshot
The snapshot leader prepares the list of L2 requests
This final snapshot
- No version number.
- No new L1 commits and no new L2 commits.
- No new assurance transactions.
- Hashes of the final L2 requests
$\mathcal{R}^\textrm{L2}_\textrm{final}$ .
For a final snapshot, the deterministic algorithms generate:
- No dissolution certificates.
- No dissolution transactions.
- Settlement transactions
$\mathcal{T}_\textrm{final-settle}$ that produce utxos equivalent to the union of the L2 decommit and active utxo sets in$\mathcal{L}^\textrm{L2}_\textrm{final}$ .
The final update transaction
The snapshot leader broadcasts the final snapshot using a special message reserved for final snapshots and participants confirm the final snapshot accordingly.
If L2 consensus breaks down, then it is impossible to finalize the head and produce all of its L2 utxos on L1 with efficient settlement transactions. Instead, the participants must engage the contestation mechanism to determine the latest snapshot and trigger its dissolution plan on L1.
The contestation mechanism is a state machine with three states, which begins in the
From the
- Spend the uncontested utxo
$\phi_\textrm{uncontested}$ , using some multi-signed snapshot certificate$\xi(s,r)$ as redeemer. - Produce the contested utxo
$\phi_\textrm{contested}(s,r, t_\textrm{deadline}, \mathcal{P}_\textrm{remaining})$ . - Verify the multi-signature of the certificate
$\xi(s,r)$ . - Verify that the transaction validity interval duration is within reasonable bounds and that the contestation deadline
$t_\textrm{deadline}$ is$T_C$ seconds after the upper bound of this validity interval. - Verify that the set of remaining contestants
$\mathcal{P}_\textrm{remaining}$ is equal to the full set of head participants$\mathcal{P}$ , including the transaction submitter.
The remaining contestants should monitor the state of the contestation mechanism and be ready to react quickly if it is engaged. If the discord transaction did not use the latest confirmed snapshot certificate, then any remaining contestant can submit a contest transaction
- Spend the contested utxo
$\phi_\textrm{contested}(s, r, t_\textrm{deadline}, \mathcal{P}_\textrm{remaining})$ , using a snapshot certificate$\xi(s',r')$ as redeemer. - Produce the new contested utxo
$\phi_\textrm{contested}(s',r', t_\textrm{deadline}, \mathcal{P}'_\textrm{remaining})$ . - Verify the multi-signature of the certificate
$\xi(s,r)$ . - Verify that the new snapshot redeemer is newer:
$(s',r') > (s,r)$ . - Verify that the transaction validity interval ends before the contestation deadline
$t_\textrm{deadline}$ . - Verify that the submitter is in the old set of remaining contestants
$\mathcal{P}_\textrm{remaining}$ and is excluded in the new set$\mathcal{P}'_\textrm{remaining}$ .
The contestation mechanism is concluded when either the contestation deadline elapses or all participants have exercised their right to contest.
If the contestation mechanism is concluded and the L1 head state's major version has caught up to the major version discovered by the contestation mechanism, without surpassing it, then any participant can submit a halting transaction
- Spend the contested utxo
$\phi_\textrm{contested}(s, r, t_\textrm{deadline}, \mathcal{P}_\textrm{remaining})$ , using the$\textrm{Halt}$ redeemer. - Spend the open head state utxo
$\phi_\textrm{open}(s)$ , using the snapshot certificate$\xi(s,r)$ in the$\textrm{Halt}$ redeemer. - Produce the halted head state utxo
$\phi_\textrm{halted}(s,r)$ . - Verify the multi-signature of the certificate
$\xi(s,r)$ . - Verify that the major version
$s$ matches in$\phi_\textrm{contested}$ and$\phi_\textrm{open}$ . - Verify that the snapshot certificate
$\xi(s,r)$ references the major version$s$ , the minor version$r$ , and the open head state utxo$\phi_\textrm{open}(s)$ . - Verify that either of the following holds:
- The transaction validity interval starts after the contestation deadline
$t_\textrm{deadline}$ . - The set of remaining contestants
$\mathcal{P}_\textrm{remaining}$ is empty.
- The transaction validity interval starts after the contestation deadline
Once the halted head state
If the contestation mechanism is concluded but the L1 head state's major version has surpassed the contest-discovered major version, then any participant can reset the contestation mechanism in the
- Spend the contested utxo
$\phi_\textrm{contested}(s, r, t_\textrm{deadline}, \mathcal{P}_\textrm{remaining})$ , using the$\textrm{Reset}$ redeemer. - Reference the open head state utxo
$\phi_\textrm{open}(s')$ . - Verify that the open head state's major version is newer:
$s' > s$ . - Verify that either of the following holds:
- The transaction validity interval starts after the contestation deadline
$t_\textrm{deadline}$ . - The set of remaining contestants
$\mathcal{P}_\textrm{remaining}$ is empty.
- The transaction validity interval starts after the contestation deadline
If the head state is final, then the contestation mechanism can be cleaned up regardless of the contest state. The cleanup transaction
- Spend either the uncontested utxo
$\phi_\textrm{uncontested}$ or the contested utxo$\phi_\textrm{contested}(s, r, t_\textrm{deadline}, \mathcal{P}_\textrm{remaining})$ , using the$\textrm{Cleanup}$ redeemer. - Spend the final head state utxo
$\phi_\textrm{final}$ , with the$\textrm{Cleanup}$ redeemer.
[TODO]
[TODO]
[TODO]
[TODO]
Committing to a head by sending a utxo to a native script address has a major advantage that spending that utxo does not incur any script execution cost. The tradeoff for this is that the committer must participate in the L2 protocol to obtain an assurance transaction before submitting the commit transaction on L1.
A more heavyweight alternative to this would be committing to an unparametrized Plutus script address and adding the commit timeout to the L1 datum, so that the Plutus script can enforce the recoverability of the commit without a separate assurance transaction needing to be signed in advance. This may make it less complicated for external people and dApps to commit utxos to the Hydra Head, but spending such Plutus-guarded commits would be more expensive due to script execution cost. The extra cost would be covered upfront by the funds contained in the committed utxo.
Perhaps, the Hydra Head protocol could support both kinds of commits, allowing users to choose between cost-efficiency and convenience.
This prevents logical branching in the head state that would interfere with the unconditional/deterministic nature of settlement transactions.
The only remaining point of contention occurs when the contestation mechanism concludes with an old snapshot certificate and then the head is halted with that old snapshot, before a confirmed update transaction can increment the head state's major version above that old snapshot. However, this shouldn't happen if participants are honest and diligent in the contestation mechanism, and if it does, then the participants have in some sense consented to it.
Interoperability with L1 applications is straightforward. An application that wants to send a commit to a head should make a request to one of the participants (via some off-chain API) to obtain a multi-signed assurance transaction, and then it can send the commit to the head's native script address
The application can monitor the state of the funds in the head by querying participants about the L2 ledger state (via some off-chain API). When desired, it can make a request to one of the participants to decommit the funds from the head. The head doesn't need to halt for the decommitted funds to be released to the application on L1.
Snapshot confirmation should be binary: either the entirety of a snapshot's certificates and transactions are signed by all participants and can be executed, or none of them are. It would be problematic if any participant could wait for all the other participants' snapshot signatures and then cherry-pick which transactions and certificates he wants to sign.
Atomicity of snapshot confirmation is provided by transaction and hash causality:
- Each snapshot certificate includes the utxo reference of the head state utxo produced by the last major snapshot, and the contestation mechanism only allows the head to be halted if the head state is open with that major snapshot version.
- Dissolution transactions are only valid if the head halts with their snapshot version and the snapshot certificate is signed.
- The update transaction updates the major snapshot version and triggers the set of helper transactions that split its aggregate decommits into granular decommits.
Therefore, a major snapshot can take effect only if its update transaction is signed, while a minor snapshot (which doesn't have an update transaction) can take effect only if its snapshot certificate is signed.
In the Hydrozoa design, each major snapshot is backwards incompatible with previous snapshots because it can change the balance of funds contained in the head utxo. Since it is already backwards incompatible, then in principle there's no reason why it couldn't also include a change to the membership of Hydra Head participants for subsequent snapshots.
Such a change of membership could be proposed as a governance motion on L2 and would have to be manually ratified by each participant, in order to be included in the next snapshot.
© "Hydrozoa: Lightweight and scalable Hydra Heads" by George Flerovsky is licensed under the Creative Commons Attribution 4.0 International Public License (CC-BY-4.0).