Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

zkvm: payment channel API #481

Open
oleganza opened this issue Jul 28, 2020 · 0 comments
Open

zkvm: payment channel API #481

oleganza opened this issue Jul 28, 2020 · 0 comments

Comments

@oleganza
Copy link
Contributor

oleganza commented Jul 28, 2020

Here's a sketch of a simple payment channel protocol.

Use case: a payment hub

Senders send actual cash into hub's account, while hub moves IOUs to the recipients.
At the end of the day (arbitrary settlement period), hub settles channels:

  1. Net senders can move in extra cash through additional inputs to the tx.
  2. Cash is moved to the receivers' outputs.
  3. IOUs from receivers are retired or moved back under issuer's control.

As a result, arbitrary amount of channels are re-filled and settled in a single tx and net recipients get liquid cash (not IOUs) in the end of the settlement.

In case of failure, recipients have IOUs accumulated in the period between settlements that act as a hard record of the promised payment. It can be used to redeem cash through the separate means (e.g. if the payment hub was compromised and stopped operating, the settlement can be done through other software), or be re-sold at a discount.

Most importantly, such setup protects the hub from internal attacks or data corruption. The correct state of all balances is properly recorded on the blockchain, while transient states of the channels are distributed among all participants.

Requirements

  1. Two parties agree to lock two values, one from each side, under a 2-of-2 multisig predicate. Quantities could be different, the flavors are the same. E.g. $10:$10, or $100:nothing.
  2. Before forming a transaction that locks the funds, they prepare a signed program (to be used with signtag instruction) that re-locks the values under their individual predicates, but allows withdrawal only after a fixed timeout (e.g. 24h) since the publication of the transaction that uses that program.
  3. To update the balances, they mutually sign a new signed program that re-locks values in different proportions.
  4. During the timeout, a later-signed program can be used to update the state of the contract. This is important to ensure that counterparty cannot publish and withdraw money that was already spent.
  5. When parties are done exchanging balances, they cooperatively sign a transaction that unlocks the value in required proportion, without ever using signed programs, that were needed as a security measure. This makes transactions that open and close the channel indistinguishable from a plain payment.

Design

States of the channel

  1. Nil: funds belong to their owners, the channel is not created yet.
  2. Ready(n): funds are locked at state N (=0,1,2,3,...).
  3. Closing: funds are time-locked before they can be released to the owners.
  4. Final: funds are returned to the owners in a new proportion.

Transitions

Nil⭢Ready: transaction with two inputs, and 3 outputs: one output containing values locked in the channel under predicate P, the other two containing change quantities that return back to the users.

Ready⭢Final: transaction that's mutually signed by both parties that locks the values under predicates chosen by the parties. This may also be used to "top up" the existing channel using a single transaction (that moves funds from the old channel to a new one, possibly with additional funds injected).

Ready⭢Closing(k): a signed program S(k) is used in a transaction that moves the funds from the idle state to a state of a time-lock before the values can be withdrawn. Assets are locked under predicate C(k) in a state Closing(k).

Closing(k)⭢Closing(n): a signed predicate is used to "bump" the state of the contract to a more up-to-date balance distribution (contract verifies that n > k). Assets are locked under new predicate C(n).

Closing(n)⭢Final: after a timeout, the user can form a transaction that unlocks the funds into two predicates, per specified distribution, one value per party.

Predicates and programs

P: a Taproot predicate with a signing key defined as 2-of-2 multikey K1, aggregated key belonging to the parties and a program.

Stack: [ value ]

Predicate: 2-of-2 multikey K1, plus a program branch to initiate exit:

  • push commitments with dummy commitments and predicates, maximum timestamp (0xffffffffffffffff) and a zero sequence number.
  • lock with C(0) predicate in a transient contract (not output), so it can be used to create a valid lock immediately within the same transaction.

Note: this can be simplified if the signid/signtag instructions support merkleized multi-branch programs, so we can sign two variants of the S(k): for the initial state and the closing state.

C(k): a Taproot predicate with the same 2-of-2 multikey K2, with a clause that allows withdrawal after timeout.

Stack: [ value, comA, predA, comB, predB, timestamp, sequence_number ]

Predicate: 2-of-2 multikey, plus a program branch to exit the channel:

  • check tx.mintime > timestamp+24h,
  • borrow value comA and locks it with predA,
  • borrow value comB and locks it with predB,
  • leave the original combined value on stack, along with negative values for A and B

S(k): a 2-of-2 signed program under key С(k).

  • check that sequence number is less than one embedded in the program,
  • check that tx.maxtime = tx.mintime + 6h - this is to avoid DoS with blocking exit from the channel until too long in the future (tx.maxtime represents approximation of "current time"),
  • drop existing arguments (commitments, predicates, timestamp, seq number) and replace them with the new commitments and predicates specified in the program, push tx.maxtime as "current time", push new sequence number,
  • lock the new 7 items (value, two commitments, two predicates, timestamp and sequence number)
    TBD: specify the keys set up by both parties.

Workflow

  1. When parties agree to lock $A and $B from each side, they form a tx, co-sign it and maintain the utxo with the contract P.
  2. Setup a shared key for deriving blinding factors deterministically, based on sequence number in order to verify commitments.
  3. To make a payment, form a signed program, co-sign it as a first party. The recipient finishes the signature.
  4. If they detect an attempt to withdraw with a wrong sequence number, form a transaction that uses the latest signed predicate available.
  5. Send request to close the channel.
  6. Reply to request to close and co-sign the exit.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant