-
Notifications
You must be signed in to change notification settings - Fork 87
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 commit #199
Comments
In a discussion I had with @mchakravarty we touched on the fact that it would also be possible to directly |
Incremental decommits may be very interesting to oracle use cases, where the resulting data is getting decomitted upon consumption. |
The use case of Incremental De/Commit really adds so much added value to many of the Use Cases that Hydra seeks solve for end-users. From an end-users perspective, with all of the potential Heads that will be running, they will be locking funds in various places. A stake pool is well understood way of locking in funds, but with Heads, and the varied use cases, they may be more hesitant or unable (because they have commit to previous heads), to participate. By having Incremental De/Commit users are free to allocate their funds as necessary with their goals, and feel more confident with the developers integrating features utilizing Hydra Heads. So I see this as a very important feature for optics to the greater cardano community, and Hydra Pay would definitely be able to make good use of it. |
@Sbcdn mentioned they would love to have this feature as well for their use case so +1 |
We discussed incremental commits/decommits (aka increments/decrements) today with the original authors of the Hydra Head paper:
|
Some use cases that we believe could benefit from this:
|
Notes from @mchakravarty on incremental commits and more general configuration changes (from back in the days): Hydra Head with Incremental Commits Requirements
Configuration change transactions
Adding and removing participants
Committing and decommitting UTxOs
Synchronisation
Multisigs
|
Besides the basic mermaid diagram above, here is another drawing of the potential life-cycle with incremental commits/decommits from our Miro board: |
Grooming discussion:
Next steps:
|
We continued work on this after also starting #1057. We had implemented the off-chain workflow to the point of this diagram shows: sequenceDiagram
Alice->>+API: POST /commit (UTxO)
API->>HeadLogic: Commit UTxO
par broadcast
HeadLogic->>HeadLogic: ReqInc incUTxO
and
HeadLogic->>Node B: ReqInc incUTxO
end
HeadLogic -->> Alice: WS CommitRequested
par Alice isLeader
HeadLogic->>HeadLogic: ReqSn incUTxO
and
HeadLogic->>Node B: ReqSn incUTxO
end
Note over HeadLogic,Chain: PROBLEM: Need to verify incUTxO on L1 as we authorize the TxIns to use (because of on-chain scripts).
HeadLogic->>HeadLogic: sig = sign snapshot incl. inputs(incUTxO)
par broadcast
HeadLogic->>HeadLogic: AckSn sig
and
HeadLogic->>Node B: AckSn sig
end
Node B->>HeadLogic: AckSn sig
HeadLogic -->> Alice: WS SnapshotConfirmed
HeadLogic -->> Alice: WS CommitApproved
HeadLogic -->> API: SnapshotConfirmed
API->>API: draftIncrementTx vk snapshot sig >>= finalizeTx >>= signTx sk
API-->>-Alice: IncrementTx
Alice->>Alice: sign IncrementTx
Alice->>Chain: submit IncrementTx
Chain->>HeadLogic: OnIncrementTx
HeadLogic-->>Alice: CommitFinalized
However, when working on the specification and trying to realize the recommendation of researchers, we hit the problem as indicated in the picture. Namely, that in one of the designs the assumption was made that Hydra participants would sign off on the transaction output references ( We need to discuss this and the alternative of using a Merkle-Tree based |
This PR adds "incremental decommits" to the Hydra Head protocol, which allows users to take funds out of an open Head. - New API endpoint `/decommit` which accepts a "Decommit transaction", that spends some UTxO and whatever outputs it produces will be made available on the L1. This can be also done through a new `Decommit` client input and new server outputs `DecommitRequested`, `DecommitApproved` and `DecommitFinalized`, as well as `DecommitInvalid` to inform about status of the decommit. - Decommits are first approved in a snapshot on L2 via a new network message `ReqDec`, before a new `decrementTx` can be posted and observed on-chain. - Only one decommit can be processed at a given time. - Update documentation and added how-to about how to use this. - Acknowledged specification changes by "clearing" of $\textcolor{red}{\\red}$ areas covered by this implementation in the specification. - End-to-end test covering the main scenario of decommitting funds. - Added mutation tests for Decrement, Close and Contest to cover all on-chain-verification changes. - Enhanced `TxTrace` tests to test decrements with various snapshots and their interaction with close/contest and fanout of a head. --- * [x] CHANGELOG updated * [x] Documentation updated * [x] Haddocks updated * [x] New TODOs explained hereafter ![image](https://github.com/user-attachments/assets/eed47f06-d519-42cb-a897-98397066fdd9) - Two FIXMEs covered by #1524 - TODO in HeadLogic coverd by #1502 - TODO in tx-cost how we could improve the benchmark output (not crucial) - TODO in head logic about rollbacks .. actually something we need to consider with #199 too
Got triggered by the thought that if we want to be safe properly secure Hydra Heads against rollbacks of incrementally committed funds, having full sequencing requirements + potentially long lockup times is going to be really bad. Also the UX workflow is an odd (see #199 (comment) and https://github.com/cardano-scaling/hydra/blob/f24022532cb169eb3d404e8a70362bde37a21712/docs/docs/dev/protocol.md) mix between synchronous API and asynchronous interactive rounds on the Head. In presence of rollbacks/pessimistic settings, this is creating a Head that is not "very live" as it would need to be forced to close in case a requested to commit UTxO is spent otherwise before the A deposit based scheme (as @GeorgeFlerovsky and others have been exploring too) using a synchrony assumption where funds are locked "to get picked up" for longer than typical rollbacks occur, is much preferable in these points. While it will require two on-chain transactions to add funds to a head, we can be sure after the first deposit that any spend into the head (before a reclaim deadline) is going to still apply if rolled back. So we can employ varying strategy on timeouts between deposit (wait long) and increment (no need to wait). Here is a drawing of this scenario: |
We discussed the above in grooming and realised that another validator will be needed for the deposit workflow; a minting policy, because we need to ensure that things are correctly recorded into the deposit; we will need a token on the deposit, and these can be used to discover deposits. |
(1) Why do you need to enforce correct deposit datum via an on-chain minting policy? If the datum is incorrect, then the deposit can't be collected, but the user can reclaim it after deposit timeout. (2) Doesn't that duplicate computation that occurs when collecting the commit later? The datum needs to be inspected when checking that the head state merkel root is evolved properly, during collection. |
@GeorgeFlerovsky How would you know whether the datum is correct? The transaction collecting from the deposit can't inspect the output that was deposited and needs to rely that it was recorded correctly into the datum (e.g. that the right address is recorded) Ultimately, deposit is a two step protocol and the second step needs to rely that the first step was executed correctly. Using a minting policy and a minted token for contract continuity between the two steps is the standard technique for this. Are there other ways to ensure this?
In a way, yes. It requires processing the output twice, but no duplicate computation. We are not necessarily using merkle tree structures for this feature (we might switch to that for #1468), but the processing would always be two steps: serialize and digest. The plan is that the |
I guess my question is whether your onchain code should: (A) merely check that the serialization parses into the correct type (i.e. something like (B) also check that the serialization corresponds to the depositor's input utxo that provided the funds to create the deposit utxo. Computation (A) is mandatory if your onchain code is responsible for the correct evolution of the head state hash during collection. Computation (B) is a nice guardrail to prevent the depositor from shooting himself in the foot with a correct type but incorrect value in the deposit datum, but it incurs more fees and is not strictly necessary from the perspective of the hydra head. |
@GeorgeFlerovsky I disagree that (B) is optional, it is crucial for the correctness of any layer 2. Otherwise anyone could claim anything on the L2. While the damage is somewhat limited on |
Fair enough 👍 |
Although, I'm not quite sure what you mean by "anyone could claim anything on L2" and "any datum is retained correctly to not break scripts when moving to L2". If the deposit utxo is being created from pubkey-held funds, then the deposit's L2 address and datum can be anything that the pubkey owner wants, as authorized by his signature in the tx. If the deposit utxo is being created from script-held funds, then the deposit's L2 address and datum can be anything that the script allows in its "SendToL2" redeemer logic. I think the difference between our views is:
|
@GeorgeFlerovsky When typing out the specification for the on-chain checks to be done on There is no actual need for on-chain checks of what a valid deposit is (what I would have encoded in the minting policy). Anyone paying to the deposit validator (off-chain code) should ensure that what they put is a valid datum having a deadline and if a script wants to ensure continuity of its datum, it would need to green light any While the interface between downstream scripts and the deposit protocol would become slightly more involved, it comes at the benefit of greatly simplifying the protocol transactions. But.. How would you describe this interface? I was thinking something similar to the |
@ch1bo TBH, I haven't thought too much about that interface yet. In a hand-wavy sense, we can adapt components from what you've previously been doing for users — serializing utxos, deserializing their datum representation, comparing redeemers to hashes, etc. We're just pushing some of those onchain/offchain mechanisms outside the hydra protocol. The onchain parts become opt-in for scripts, while pubkey users rely on the offchain checks. |
### Why This is a leftover from off-chain changes needed to implement #199 We kept in the local state a map of UTxO we already observed so this simplifies the code around that since the observed UTxO is already to be found in the chain state UTxO. Now we keep a list of `[(TxId, UTxO)]` where UTxO is just there to check off-chain if the next snapshot is snapshotting exactly what we saw already as (`utxoToCommit`). ### What Remove the local map and fields in the observations for increment/recover and rely completely on observed UTxO (`spendableUTxO`) --- <!-- Consider each and tick it off one way or the other --> * [x] CHANGELOG updated or not needed * [x] Documentation updated or not needed * [x] Haddocks updated or not needed * [x] No new TODOs introduced or explained herafter
…mutations (#1710) Subtask of #199 - Implement the `Claim` redeemer branch of the deposit validator. - Add head currency symbol to the `Claim` redeemer in order to check it against the datum value. - Add upper validity bound for increment in order to be able to check if the deposit deadline has not been reached. - Double the amount of contestation period value when setting the deposit deadline in order to give enough room for increment to be valid (before the deadline). - Add appropriate deposit mutations. - Left a FIXME to not forget to fix the specification in terms of changes in the `Claim` redeemer --- <!-- Consider each and tick it off one way or the other --> * [x] CHANGELOG updated or not needed * [x] Documentation updated or not needed * [x] Haddocks updated or not needed * [ ] No new TODOs introduced or explained herafter
Why
Hydra Heads should not need to be closed and re-opened just to add more funds. This will make Hydra Heads more flexible in enables use cases where long-living Heads are beneficial.
Furthermore, it could pave the way for getting rid of the initialization phase altogether, which would result in a much simpler protocol.
What
Implement the protocol extension for more committing additional UTXOs into a Head as already briefly described in the original Hydra Head paper.
"As user, I want to add more funds to the head, such that I can spend them later in the head"
When the head is open, a hydra client can request an incremental commit:
POST /commit
. Just like with the "normal" commit, the user needs to send either a UTxO or a "blueprint transaction".depositTx
corresponding to the requested commit. (This works just the same way as the commit endpoint works so far during initialization phase)depositTx
Submitting the
depositTx
transaction should have the requestedUTxO
eventually added to the headCommitRequested
(TBD: orDepositDetected
?) server output is sent to signal observation of the depositUTxO
and wait for aSnapshotConfirmed
with inclusion approval.incrementTx
, signs and submits that.CommitFinalized
server output is sent to the clients when theincrementTx
is observed.The node provides a list of pending commits via the API using
GET /commits
Each pending commit (deposit) has an id (i.e. the deposit outputs'
TxIn
) and a deadline attached, after which a user can request refund of the commitDELETE /commits/<id>
, which has the node construct and submit arecoverTx
for the user. TBD: okay that node pay fees here?recoverTx
Any UTxO which can be committed, can also be incrementally committed
Scenarios
All of the positive scenarios also ensure correct balance after fanout:
incrementTx
Security
Out of scope
How
Protocol design
Important
Idea: Deposit anything to commit into a
deposit
output. Head participants then re-use theReqSn
off-chain consensus to request inclusion of UTxO (like incremental decommit). Deposits have an asymmetric deadline, such that users need to wait longer before they can reclaim than the head participants have time to ensure the deposit is not rolled back (double spent). Deposits are recording outputs like in thecommitTx
and claiming adeposit
into the head via anincrementTx
ensures the recorded UTxO matches (completely) with what was agreed off-chain. Participants only agree off-chain if they saw a matchingdeposit
.Outline of one deposit being claimed in an increment and one deposit being recovered:
Protocol transitions:
Situation: Head is open,$U_0$ locked, off-chain busy transacting
Deposit:
depositTx
hydra-node
or through a library/tool.depositTx
ensures through minting of a deposit token (DT) that anything to be committeddepositTx
to ensure the transition to L2 is "correct"? Could mean more coupling, but simpler protocol here.Recover:
recoverTx
after the deadline has passedIncrement:
hydra-node
) observe pending deposits using the common deposit addressdepositTx
is not rolled backA node requests inclusion of a pending decommit by sending a$U_\alpha$ ReqSn
message withincrementTx
, which:incrementTx
on L1 with addedTo be discussed
What happens if
incrementTx
is not posted after being signed on L2?fanout
for exampleDo we really need to change
η
to be a merkle-tree-like structure with inclusion proofs?Shall we drop the initialization phase?
How to deal with rollbacks/forward which result in a different$\eta$ ? When is it safe to integrate $U_\alpha$ into confirmed $U$ ?
recoverTx
should be>>
than a safe margin on observingdepositTx
(e.g. deadline = 7 days, delay on deposit observe ~ 1 day); As deposits can only be spent into the head before the deadline passed, we don't need to wait when observingincrementTx
incrementTx
(re-use contestation period?)The text was updated successfully, but these errors were encountered: