Skip to content

Instantly share code, notes, and snippets.

@jooray
Last active June 22, 2020 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jooray/462e71df27d4b3919c50256d1b58c738 to your computer and use it in GitHub Desktop.
Save jooray/462e71df27d4b3919c50256d1b58c738 to your computer and use it in GitHub Desktop.

Proof of concept DLC channels

DLC channels combine the ideas of Discreet Log Contracts and bidirectional payment channels (without the Lightning network) on top of Bitcoin Lightning Network. It is one of the first settlement implementation of Debnk protocol.

We believe that DLC channels can be also implemented on top of Elements/Liquid, but probably without confidential transactions. (TODO: Verify)

Discreet log contracts

Discreet log contracts are simple contracts that use on-chain multisig capability. Let's say that Alice and Bob want to enter an (inverse) perpetual swap contract based on the price of Bitcoin in USD. Alice goes long 1000 USD, Bob goes short 1000 USD. They both post some collateral (let's say 0.05 BTC each). The price of Bitcoin is 8000 USD at the time of the opening of the contract. The contract expires and the funds are distributed based on the price of Bitcoin on Friday noon.

If, on Friday noon, price is 8500, Alice should get her deposit (0.05 BTC) plus approx. 0.00735294 BTC as settlement. Bob will get only 0.05-0.00735294=0.04264706 BTC back.

The way that DLC achieves this is that they sign transactions for all possible divisions according to prices in some predefined price increments. Let's say that we only consider Bitcoin price increments of 100 USD.

Price Alice Bob
7500 0.04166666667 0.05833333333
7600 0.04342105263 0.05657894737
7700 0.04512987013 0.05487012987
7800 0.04679487179 0.05320512821
7900 0.04841772152 0.05158227848
8000 0.05 0.05
8100 0.05154320988 0.04845679012
8200 0.05304878049 0.04695121951
8300 0.05451807229 0.04548192771
8400 0.05595238095 0.04404761905
8500 0.05735294118 0.04264705882

Alice and Bob both sign a set of similar transactions. Alice signs transaction that sends 0.04166666667 to herself and 0.05833333333 to Bob if the price is 7500, or back to Alice if there's a timeout. She also signs the transaction that sends herself 0.04342105263 BTC and 0.05657894737 to Bob if the price is 7600, or back to Alice if there's a timeout. And so on. Bob signs the "mirror" transactions.

When the price is revealed, either Alice or Bob can pick the correct transaction based on Oracle revealing the corresponding key and broadcasts it to blockchain, splitting the funds as agreed.

Payment channels

The problem with this approach is that the splitting has to happen on-chain. If Alice and Bob would rather like to renew the contract, they could delay the payment (if both sides deem the deposit after potential settlement sufficient) and create a new version of discreet log contract based on new price. This way, the contract could become perpetual (meaning "renewing automatically"), without intermediate settlements reaching the chain.

Payment channels already solve channel state revocation. Using this technology, we could construct new DLC with the new price and invalidate the old state.

Lightning settlement

Another option would be to add a way to perform settlement on lightning, "resetting" the contract back to it's "centered" state each time settlement occurs. This way, the DLC only needs collateral and all settlements are handled on Lightning in "cooperative" use-case. In non-cooperative case (a party disappears), the DLC would settle on-chain.

TODO: How to perform secure settlement? Meaning - new state is signed and valid after lightning payment is performed, but the old state is not?

DLC Construction

The original DLC construction consists of several steps:

  1. Find an oracle

  2. Create funding transaction without signing it yet, just exchange the transaction template in order to verify it's construction and get txid

  3. Create all possible execution transactions based on the table above, depending on oracle price.

  4. Exchange signatures to the Contract Execution Transactions

  5. Exchange signatures to the funding transaction

  6. Publish the funding transaction to the blockchain

  7. Settle the contract

State revocation protocol for payment channels as defined in Bolt #3 specification:

Payment Channel Commitment Transaction Outputs

To allow an opportunity for penalty transactions, in case of a revoked commitment transaction, all outputs that return funds to the owner of the commitment transaction (a.k.a. the "local node") must be delayed for to_self_delay blocks. This delay is done in a second-stage HTLC transaction (HTLC-success for HTLCs accepted by the local node, HTLC-timeout for HTLCs offered by the local node). The reason for the separate transaction stage for HTLC outputs is so that HTLCs can timeout or be fulfilled even though they are within the to_self_delay delay. Otherwise, the required minimum timeout on HTLCs is lengthened by this delay, causing longer timeouts for HTLCs traversing the network. The amounts for each output MUST be rounded down to whole satoshis. If this amount, minus the fees for the HTLC transaction, is less than the dust_limit_satoshis set by the owner of the commitment transaction, the output MUST NOT be produced (thus the funds add to fees).

to_local Output

This output sends funds back to the owner of this commitment transaction and thus must be timelocked using OP_CHECKSEQUENCEVERIFY. It can be claimed, without delay, by the other party if they know the revocation private key. The output is a version-0 P2WSH, with a witness script:

OP_IF
    # Penalty transaction
    <revocationpubkey>
OP_ELSE
    `to_self_delay`
    OP_CHECKSEQUENCEVERIFY
    OP_DROP
    <local_delayedpubkey>
OP_ENDIF
OP_CHECKSIG

The output is spent by a transaction with nSequence field set to to_self_delay (which can only be valid after that duration has passed) and witness:

<local_delayedsig> <>

If a revoked commitment transaction is published, the other party can spend this output immediately with the following witness:

<revocation_sig> 1

to_remote Output

This output sends funds to the other peer and thus is a simple P2WPKH to remotepubkey.

revocationpubkey Derivation

The revocationpubkey is a blinded key: when the local node wishes to create a new commitment for the remote node, it uses its own revocation_basepoint and the remote node's per_commitment_point to derive a new revocationpubkey for the commitment. After the remote node reveals the per_commitment_secret used (thereby revoking that commitment), the local node can then derive the revocationprivkey, as it now knows the two secrets necessary to derive the key (revocation_basepoint_secret and per_commitment_secret). The per_commitment_point is generated using elliptic-curve multiplication:

per_commitment_point = per_commitment_secret * G And this is used to derive the revocation pubkey from the remote node's revocation_basepoint: revocationpubkey = revocation_basepoint * SHA256(revocation_basepoint || per_commitment_point) + per_commitment_point * SHA256(per_commitment_point || revocation_basepoint)

This construction ensures that neither the node providing the basepoint nor the node providing the per_commitment_point can know the private key without the other node's secret. The corresponding private key can be derived once the per_commitment_secret is known: revocationprivkey = revocation_basepoint_secret * SHA256(revocation_basepoint || per_commitment_point) + per_commitment_secret * SHA256(per_commitment_point || revocation_basepoint)

Putting it together

In the contract execution transaction of DLC, the to_remote output will be simple P2WPKH of the remote party. For to_local, we will have:

OP_IF
    # Penalty transaction
    <revocationpubkey>
OP_ELSE
    `to_self_delay`
    OP_CHECKSEQUENCEVERIFY
    OP_DROP
    <combined_oracle_pubkey_for_price>
OP_ENDIF
OP_CHECKSIG

combined_oracle_pubkey_for_price is a combined pubkey - see this implementation of CombinePubs - created for a particular price point and public key of the local party.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment