[Lightning-dev] Lightning in a Taproot future

Anthony Towns aj at erisian.com.au
Tue Dec 17 14:02:29 UTC 2019

On Sun, Dec 15, 2019 at 03:43:07PM +0000, ZmnSCPxj via Lightning-dev wrote:
> For now, I am assuming the continued use of the existing Poon-Dryja update mechanism.
> Decker-Russell-Osuntokun requires `SIGHASH_NOINPUT`/`SIGHASH_ANYPREVOUT`, and its details seem less settled for now than taproot details.

Supporting PTLCs instead of HTLCs is a global upgrade in that you need
all nodes along your payment path to support it; moving from Poon-Dryja
to Decker-Russell-Osuntokun is only relevant to individual peers. So I
think it makes sense to do PTLCs first if the required features aren't
both enabled at the same time.

> Poon-Dryja with Schnorr
> -----------------------

I think MuSig between the two pairs is always superior to a NUMS point
for the taproot internal key; you definitely want to calculate a point
rather than use a constant, or you're giving away that it's lightning,
and if you're calculating you might as well calculate something that can
be used for a cooperative key path spend if you ever want to.

> A potential issue with MuSig is the increased number of communication rounds needed to generate signatures.

I think you can reduce this via an alternative script path. In
particular, if you want a script that the other guy can spend if they
reveal the discrete log of point X, with musig you do:

   P = H(H(A,B),1)*A + H(H(A,B),2)*B
   [exchange H(RA),H(RB),RA,RB]

   [send X]

   sb = rb + H(RA+RB+X,P,m)*H(H(A,B),2)*b

   [wait for sb]

   sa = ra + H(RA+RB+X,P,m)*H(H(A,B),1)*a

   [store RA+RB+X, sa+sb, supply sa, watch for sig]

   sig = (RA+RB+X, sa+sb+x)

So the 1.5 round trips are "I want to do a PTLC for X", "okay here's
sb", "great, here's sa".

But with taproot you can have a script path as well, so you could have a


and supply a partial signature:

   R+X,s,X where s = r + H(R+X,A,m)*a

to allow them to satisfy "A CHECKSIGVERIFY" if they know the discrete
log of X, and of course they can sign with B at any time. This is only
half a round trip, and can be done at the same time as sending the "I
want to do a PTLC for X" message to setup the (ultimately cheaper) MuSig
spend. It's an extra signature on the sender's side and an extra verification 
on the receiver's side, but I think it works out fine.

> Pointlocked Timelocked Contracts
> --------------------------------
> First, I will discuss how to create a certain kind of PTLCs, which I call "purely scriptless" PTLCs.
> In particular, I would like to point out that we *actually* use in current Poon-Dryja Lightning Network channels is *revocable* HTLCs, thus we need to have *revocable* PTLCs to replace them.
> * First, we must have a sender A, who is buying a secret scalar, and knows the point equivalent to that scalar.
> * Second, we have a receiver B, who knows this secret scalar (or can somehow learn this secret scalar).
> * A and B agree on the specifications of the PTLC: the point, the future absolute timelock, the value.
> * A creates (but *does not* sign or broadcast) a transaction that pays to a MuSig of A and B and shares the txid and output number with the relevant MuSig output.
> * A and B create a backout transaction.
>   * This backout has an `nLockTime` equal to the agreed absolute timelock.
>   * It spends the above MuSig output (this input must enable `nLockTime`, e.g. by setting `nSequence` to `0xFFFFFFFE`).
>   * It creates an output that is solely controlled by A.
> * A and B perform a MuSig ritual to sign the backout transaction.
> * A now signs and broadcast the first transaction, the one that has an output that represents the PTLC.
> * A and B wait for the above transaction to confirm deeply.
>   This completes the setup phase for the PTLC.
> * After this point, if the agreed-upon locktime is reached, A broadcasts the backout transaction and aborts the ritual.
> * A and B create a claim transaction.
>   * This has an `nLockTime` of 0, or a present or past blockheight, or disabled `nLockTime`.
>   * This spends the above MuSig output.
>   * This creates an output that is solely controlled by B.
> * A and B generate an adaptor signature for the claim transaction, which reveals the agreed scalar.
>   * This is almost entirely a MuSig ritual, except at `s` exchange, B provides `t + r + h(R | MuSig(A,B) | m) * MuSigTweak(A, B, B) * b` first, then demands `r + h(R | MuSig(A,B) | m) * MuSigTweak(A, B, A) * a` from A, then reveals `r + h(R | MuSig(A,B) | m) * MuSigTweak(A, B, B) * b` (or the completed signature, by publishing onchain), revealing the secret scalar `t` to A.
> * A is able to learn the secret scalar from the above adaptor signature followed by the full signature, completing the ritual.

(I think it makes more sense to provide "r + H(R+T, P, m)*b" instead of
"r+t + H(R,P,m)*b" -- you might not know "t" at the point you need to
start the signature exchange)

I think the setup can be similar to BOLT-3:

  Funding TX output:  MuSig(A,B)

  Commitment Tx Held by A:
    - locktime: obscured commitment number
    - input: funding tx
    - outputs:
        balance A
	balance B
	PTLC paying to A
	PTLC paying to B

balance B can pay directly to B

balance A pays to MuSig(Rn,B) -- where Rn is the revocation key for the
nth commitment, and A holds a presigned tx by B with relative locktime
set, paying the funds to A.

PTLCs also pay to MuSig(Rn,B) -- 

for the one paying to A, A holds a tx
partially presigned by B that needs the point's discrete log added to x
with relative timelock; for the one paying to B, A holds a presigned tx
by B that has absolute locktime set and pays to MuSig(Rn,B) for which
A has another presigned tx by B that has relative locktime set.

HTLC's also pay to MuSig(Rn,B) but they're tweaked by a script -- if
they're paying to B, A just holds the same timelocked presigned tx's as
for PTLCs; if they're paying to A, A will use a "HASH160 X EQUALVERIFY
A CHECKSIGVERIFY delay CSV" script path instead.

B has a similar commitment Tx; and A holds pre-signed tx's for the
PTLC's there, except without the relative timelock txs.

This adds up to B needing to provide A with signatures for:

  the funding tx
  A's balance
  each PTLC/HTLC paying/refunding to A from A's commitment and the sub tx for each of those
  each PTLC/HTLC paying/refunding to B from B's commitment

and each of those signatures need to be updated every commitment update.

Note that you need to receive all the partial signatures to spend the
commitment tx before you provide your partial signature over the funding
tx to authorise the commitment tx. So yet more rounds of communication.

I think this approach would let you upgrade existing channels to support
PTLCs without closing/reopening, though.

I think you could do something eltoo-ish to simplify watchtowers:

    Funding TX output: MuSig(A,B)

    Commitment Tx Held by A:
      - locktime: obscured commitment number
      - inputs: funding tx
      - outputs: MuSig(Rn,B)

    Settlement Tx Held by A:
      - nsequence: to_self_delay
      - input: commitment tx held by A
      - outputs:
         balance A
	 balance B
	 PTLC paying to A
	 PTLC paying to B

    Secondary Settlement Tx Held by B:
      - no abs/relative locktime
      - input: commitment tx held by A
      - outputs:
         balance A
	 balance B
	 PTLC paying to A
	 PTLC paying to B

in which case once the commitment is revoked, B only needs to do a single
signature of MuSig(Rn,B) to claim all the funds if A publishes the
old commitment, without having to worry about any HTLC/PTLC info? The
settlement tx can be a bit simplified I think too. Maybe the edge case
when the absolute locktime is close to the relative locktime makes this
painful, though I think it would make eltoo painful then too? Not sure
if I'm missing something here. 

(Note that this gives symmetric delays: if A unilaterally closes the
channel by publishing the commitment tx, and B wants their funds, B can
either immediately claim all the funds via the revocation secret if A
was cheating, or has to publish the settlement tx first, at which point
A can get all A's funds immediately as well)

> Revocable Outputs and PTLCs
> ---------------------------
> * No wallet uses relative-timelock for ordinary spends, unlike abolute-locktime where Bitcoin Core always uses absolute-locktime `nLockTime` for ordinary spends.

I think the fact lightning relative timelocks will be "round" numbers,
and the timelocked tx will be published as soon as possible will make
it relatively easy to distinguish them from other spends anyway. Doing
it scriptlessly is cheaper on chain though.

Anyway, my picks are:

 - [x] Poon-Dryja PTLCs prior to eltoo
 - [x] scriptless revocation via MuSig(Rn,B) internal keys
 - [x] support HTLCs via taproot
 - [x] (optionally) allow use of script paths to minimise round trip


More information about the Lightning-dev mailing list