[dlc-dev] Off-Chain DLC Market Maker

conduition conduition at proton.me
Sun Nov 19 20:47:39 CET 2023

Hi dlc-devs. I had an idea which I think might enable better scaling
and privacy for DLCs which have lots of participants. The concept was
originally geared towards provably fair lotteries. I made a more
detailed blog post about it here:

  - https://github.com/conduition/conduition.io/pull/4
  - https://conduition.io/scriptless/lottery/
  - http://conduit4u4zsimbgvcatj6lyy36ot6k7w7bvbvivgwhbgzs7gfds7qad.onion/scriptless/lottery/
  - https://stacker.news/items/321995

At the end of that article, I briefly note how the strategy could be
generalized beyond simple lotteries to DLCs. Let me summarize the concept
here so you don't have to read the whole article above.

## Concept

A bunch of players with pubkeys `[P_1, P_2, ... P_n]` want to participate in
a DLC together.

Let `delta` be some block delay within with a transaction could reasonably be
mined, e.g. 144 blocks.

The outcome of the DLC will be signed by an oracle who promises to sign one of
a set of outcome messages `[m_1, m_2, ...]` using nonce `R`. A message `m_i`
if signed would unlock the point `S_i = R + H(R, D, m_i) * D`, where `D` is
the oracle's pubkey. WLOG we can also support multiple oracles.

If there are many players, and buy-in costs are low, then setting up an n-of-n
contract with deposits paid from all `n` players would be expensive and
inefficient. Instead, as long as there is an available 'market maker' with
enough capital to cover the cost of the whole DLC up-front, the players can
use the market maker's capital on-chain, and each buy into the DLC using
off-chain HTLCs or PTLCs paid to the market maker. 

The market maker could be a contestant in the DLC himself, or he could be an
independent bystander seeking to profit by leasing his on-chain capital to 

serve others.

## Example

Here's a simple example of a DLC with 3 players, and a fourth market maker
who isn't participating in the DLC. We assume the oracle signs one of two
possible messages, which dictates the outcome.

1. The market maker sets up `tx_init`, which deposits the whole DLC value into a
   4-of-4 multisig address.

2. The market maker sets up `[tx_outcome_1, tx_outcome_2]` - one
   for every possible outcome.
  - Each `tx_outcome_i` distributes the value of the DLC, paying to a specific set
    of winners depending on the outcome.
  - For each player (a 'winner') who receives money from the DLC outcome, we add
    an output to `tx_outcome_i`.
  - Each output in `tx_outcome_i` pays to a 2-of-2 escrow contract jointly owned
    by a winning player and the market maker.
  - Spending from each output in `tx_outcome_i`, the market maker sets up two
    mutually exclusive transactions:
    - `tx_winner_i_j`, which pays to the sole control of player `j` after a
      relative timelock of `delta` blocks.
    - `tx_reclaim_i_j`, which pays to the sole control of the market maker after
      a relative timelock of `2 * delta` blocks.

3. The market maker sends these transactions to the players, who reply with partial
   signatures. The players take no issue with signing, because so far the players
   haven't invested any money into the DLC; The market maker is frollicking about
   with their own capital at this stage.

4. The market maker verifiably encrypts the signatures on each `tx_outcome_i` with
   the point `S_i` (the oracle's locking point for outcome `i`)

5. The market maker generates a set of secret tickets `[t_1, t_2, t_3]`, one for
   each player. Each ticket has a public point `T_i = t_i * G`.

6. The market maker verifiably encrypts the signatures on each `tx_winner_i_j` with
   the point `T_j`, so that the ticket for player `j` is needed to decrypt
   `tx_winner_i_j` (the TX which pays out to player `j`).

7. The market maker sends the encrypted signatures on all `tx_outcome_i` and
   `tx_winner_i_j` transactions to the players.

8. The players verify and ACK the adaptor signatures.

9. (optional) All players pay a small anti-spam deposit to the market maker.

10. The market maker signs and publishes `tx_init`.

11. Once `tx_init` is confirmed, each player `j` has an incentive to learn the
    ticket secret `t_j` that encrypts `tx_winner_i_j`. The market maker can now
    sell each ticket secret `t_j` to player `j` using an off-chain point time
    lock contract. The price of the earlier deposit can be factored into this
    ticket price.

12. Once all tickets have been purchased, and the outcome signature `s_i` is
    published by the oracle, then the market maker has several options to settle
    the on-chain contract with each winning player:

  a. (cooperative, off-chain) The winner can claim the money in her outcome
     transaction output, but only after `delta` blocks. She can adaptor-sign a
     settlement transaction with no locktime returning the output of `tx_outcome_i`
     to the market maker, and then sell the adaptor secret key to the market
     maker. This TX would supercede `tx_winner_i_j`.

  b. (cooperative, on-chain) If the winner wishes to receive their DLC winnings
     on-chain, she can cooperate with the market maker to sign a version of
     `tx_winner_i_j` which has no relative locktime, and broadcast it. The two 

     could even arrange a CoinSwap [1], where the winner is paid out from a
     completely unrelated UTXO, owned by the market maker.

  c. (forceful, on-chain) If the market maker is not cooperative, the winning player
     simply waits for the relative locktime on `tx_winner_i_j` to mature and
     broadcasts it.

## Discussion

Assuming a player can learn `s_i` (the discrete log of `S_i`) from the oracle, then
they can decrypt and publish `tx_outcome_i` independently without the market maker's
cooperation. However, by itself this is not useful to the player, because the market 

maker will use `tx_reclaim_i_j` to return the money to himself after its relative 

locktime of `2 * delta` blocks matures.

However, if player `j` knows their ticket `t_j` as well, they can decrypt and publish
`tx_winner_i_j` to claim their winnings independently (under outcome `i`). This
is what creates the incentive to buy tickets.

As described, nothing forces players to buy a ticket. A player could feign interest
but abstain from actually purchasing a ticket. Players who abstain in this way
will force the market maker into assuming their position in the DLC, creating
counterparty risk.

If the market maker is unwilling to expose themselves to any counterparty risk,
they can use HODL invoices to accept deposits and ticket payments, only revealing
ticket secrets once all players have active PTLC/HTLC offers for their tickets. If
a player paid a deposit but did not buy a ticket, then the market maker can take 

their deposit as payment for wasting their time and locking up their capital for 

no reason. (Is there a better system there? more investigation needed)

It would also be nice if there were a way to make an even smoother off-chain
settlement flow. Perhaps in a situation where all DLC winners cooperate with
the market maker, there could be some kind of cooperative atomic off-chain
payment which pays out to all the winners at once, and returns the whole DLC
output to the market maker somehow. 

## Benefits

- No on-chain association between DLC contestants
- On-chain footprint does not indicate how many participants; only reveals
  number of winners paid out.
- Minimal on-chain footprint (one TXO in, one TXO back out) might be achievable
- Market maker is not trusted with custody of funds
- Market maker can be incentivized by charging fees (e.g. keeping deposits)
- If using PTLCs, the contract's happy path consists entirely of taproot key
  spending (great for fungibility).
- If using HTLCs, this protocol could be executed via LN today, no new BOLTs, 

  opcodes, or sighash types required.

## Diagram

Although not exactly the same as the DLC generalization i just described, 

the diagram in this section of the lottery protocol blog post might help 

to visualize the flow of on-chain transactions.


## HTLCs instead of PTLCs

To suppor classic lightning HTLCs, we simply rephrase the protocol such that the
ticket secret `t_j` is a preimage, and `T_j = H(t_j)`. Instead of adaptor signatures
on each `tx_winner_i_j`, we instead encumber the relevant output of `tx_outcome_i` 

with an HTLC script or tapscript tree like this:

<market_maker && P_j> OR
<P_j && preimage(T_j) && delay(delta)> OR
<market_maker && delay(2*delta)>

Off-chain payouts to winners can also be trivially converted to HTLCs.

Keen to hear your thoughts,

[1] https://bitcoinops.org/en/topics/coinswap/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: publickey - conduition at proton.me - 0x474891AD.asc
Type: application/pgp-keys
Size: 649 bytes
Desc: not available
URL: <https://mailmanlists.org/pipermail/dlc-dev/attachments/20231119/ee3f6969/attachment.asc>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 249 bytes
Desc: OpenPGP digital signature
URL: <https://mailmanlists.org/pipermail/dlc-dev/attachments/20231119/ee3f6969/attachment.sig>

More information about the dlc-dev mailing list