[Lightning-dev] More thoughts on NOINPUT safety

Anthony Towns aj at erisian.com.au
Wed Mar 13 11:10:50 UTC 2019


On Wed, Mar 13, 2019 at 06:41:47AM +0000, ZmnSCPxj via Lightning-dev wrote:
> > -   alternatively, we could require every script to have a valid signature
> >     that commits to the input. In that case, you could do eltoo with a
> >     script like either:
> >     <A> CHECKSIGVERIFY <B> CHECKSIG
> >     or <P> CHECKSIGVERIFY <Q> CHECKSIG
> > where A is Alice's key and B is Bob's key, P is muSig(A,B) and Q is
> > a key they both know the private key for. In the first case, Alice
> > would give Bob a NOINPUT sig for the tx, and when Bob wanted to publish
> > Bob would just do a SIGHASH_ALL sig with his own key. In the second,
> > Alice and Bob would share partial NOINPUT sigs of the tx with P, and
> > finish that when they wanted to publish.
> At my point of view, if a NONINPUT sig is restricted and cannot be
> used to spend an "ordinary" 2-of-2, this is output tagging regardless
> of exact mechanism.

With taproot, you could always do the 2-of-2 spend without revealing a
script at all, let alone that it was meant to be NOINPUT capable. The
setup I'm thinking of in this scenario is something like:

  0) my key is A, your key is B, we want to setup an eltoo channel

  1) post a funding tx to the blockchain, spending money to an address
     P = muSig(A,B)

  2) we cycle through a bunch of states from 0..N, with "0" being the
     refund state we establish before publishing the funding tx to
     the blockchain. each state essentially has two corresponding tx's,
     and update tx and a settlement tx.

  3) the update tx for state k spends to an output Qk which is a
     taproot address Qk = P + H(P,Sk)*G where Sk is the eltoo ratchet
     condition:
        Sk = (5e8+k+1) CLTV A CHECKDLS_NOINPUT B CHECKDLS_NOINPUT_VERIFY

     we establish two partial signatures for update state k, one which
     is a partial signature spending the funding tx with key P and
     SIGHASH_ALL, the other is a NOINPUT signature via A (for you) and
     via B (for me) with locktime set to (k+5e8), so that we can spend
     any earlier state's update tx's, but not itself or any later
     state's update tx's.

  4) for each state we have also have a settlement transaction,
     Sk, which spends update tx k, to outputs corresponding to the state
     of the channel, after a relative timelock delay.

     we have two partial signatures for this transaction too, one with
     SIGHASH_ALL assuming that we directly spent the funding tx with
     update state k (so the input txid is known), via the key path with
     key Qk; the other SIGHASH_NOINPUT via the Sk path. both partially
     signed tx's have nSequence set to the required relative timelock
     delay.

  5) if you're using scriptless scripts to do HTLCs, you'll need to
     allow for NOINPUT sigs when claiming funds as well (and update
     the partial signatures for the non-NOINPUT cases if you want to
     maximise privacy), which is a bit fiddly

  6) when closing the channel the process is then:

       - if you're in contact with the other party, negotiate a new
         key path spend of the funding tx, publish it, and you're done.

       - otherwise, if the funding tx hasn't been spent, post the latest
         update tx you know about, using the "spend the funding tx via
	 key path" partial signature

       - otherwise, trace the children of the funding tx, so you can see
         the most recent published state:
	   - if that's newer than the latest state you know about, your
	     info is out of date (restored from an old backup?), and you
	     have to wait for your counterparty to post the settlement tx
	   - if it's equal to the latest state you know about, wait
	   - if it's older than the latest state, post the latest update
	     tx (via the NOINPUT script path sig), and wait

       - once the CSV delay for the latest update tx has expired, post
	 the corresponding settlement tx (key path if the update tx
	 spent the funding tx, NOINPUT if the update tx spent an earlier
	 update tx)

       - once the settlement tx is posted, claim your funds

So the cases look like:

   mutual close:
     funding tx -> claimed funds

     -- only see one key via muSig, single signature, SIGHASH_ALL
     -- if there are active HTLCs when closing the channel, and they
        timeout, then the claiming tx will likely be one-in, one-out,
	SIGHASH_ALL, with a locktime, which may be unusual enough to
	indicate a lightning channel.

   unilateral close, no cheating: 
     funding tx -> update N -> settlement N -> claimed funds

     -- update N is probably SINGLE|ANYONECANPAY, so chain analysis
        of accompanying inputs might reveal who closed the channel
     -- settlement N has relative timelock
     -- claimed funds may have timelocks if they claim active HTLCs via
        the refund path
     -- no NOINPUT signatures needed, and all signatures use the key path
        so don't reveal any scripts

   unilateral close, attempted cheating:
     funding tx -> update K -> update N -> settlement N -> claimed funds

     -- update K, update N are probably SINGLE|ANYONECANPAY, so chain
        analysis might reveal the identity of both sides of the channel 
     -- update N and settlement N both use NOINPUT signatures and
        reveal CLTV script that looks like eltoo
     -- update N has timelock set
     -- settlement N has a relative timelock
     -- claimed funds may have timelocks if they claim active HTLCs via
        the refund path

     Notes:
      * cheating isn't 100% accurate: could be due to someone having to
        restore from an old backup

      * you could end up with:

          funding tx -> update K -> update W -> update N
	             -> settlement N -> claimed funds

	if someone restored from an old backup and posted K, a watchtower
	had a newer but not current state W, and finally you posted
	state N directly. with multiple watchtowers you might have more
	intermediate states' update tx's posted. afaics it has similar
	privacy results to the 2-update-tx case.

> So the restriction to add a non-NOINPUT sig in addition to a NOINPUT sig is still output tagging, as a cooperative close would still reveal that the output is not a 2-of-2.

With the above setup, you don't discover that NOINPUT was possible unless it
is actually needed because someone cheated.

As long as you're using muSig key path spending for a cooperative close,
you're not even revealing the output is 2-of-2, let alone a weird
2-of-2 variant.

> Ideally, historical data of whether onchain coin was used in Lightning or not should be revealed as little as possible.
> So in a cooperative close (which we hope, to be a common case), ideally the spend should look no different from an ordinary 2-of-2 spend.

With taproot, the goal is it shouldn't look different from an ordinary
"pay to public key" spend, and I think that's pretty achievable.

> Of course if the channel is published on Lightning, those who participated in Lightning at the time will learn of it, but at least the effort to remember this information is on those who want to remember this fact.

Well, presumaby lightning will continue to support private channels that
don't get published, and the concern's definitely valid for them!

> Now, this can be worked around by adding a "kickoff" transaction that spends the eltoo setup transaction.
> The eltoo setup transaction outputs to an ordinary 2-of-2.
> The kickoff outputs to an output that allows NOINPUT.
> Then the rest of the protocol anchors on top of the kickoff.
> [...]

I think this is possible too, but I think the scheme I describe above
is superior: iit means calculating a few more signatures each update,
but keeps more information off chain, which is better for privacy, and
probably cheaper (unless you have very high-frequency channel updates?).

Cheers,
aj



More information about the Lightning-dev mailing list