github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/signtx.go (about)

     1  package qln
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/mit-dci/lit/btcutil/txscript"
     8  	"github.com/mit-dci/lit/crypto/koblitz"
     9  	"github.com/mit-dci/lit/lnutil"
    10  	"github.com/mit-dci/lit/logging"
    11  	"github.com/mit-dci/lit/sig64"
    12  	"github.com/mit-dci/lit/wire"
    13  )
    14  
    15  // SignBreak signs YOUR tx, which you already have a sig for
    16  func (nd *LitNode) SignBreakTx(q *Qchan) (*wire.MsgTx, error) {
    17  	// TODO: we probably have to do something with the HTLCs here
    18  
    19  	tx, _, _, err := q.BuildStateTxs(true)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  
    24  	// make hash cache for this tx
    25  	hCache := txscript.NewTxSigHashes(tx)
    26  
    27  	// generate script preimage (keep track of key order)
    28  	pre, swap, err := lnutil.FundTxScript(q.MyPub, q.TheirPub)
    29  	if err != nil {
    30  		return nil, err
    31  	}
    32  
    33  	// get private signing key
    34  	priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  	// generate sig.
    39  	mySig, err := txscript.RawTxInWitnessSignature(
    40  		tx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	theirSig := sig64.SigDecompress(q.State.Sig)
    46  	// put the sighash all byte on the end of their signature
    47  	theirSig = append(theirSig, byte(txscript.SigHashAll))
    48  
    49  	logging.Infof("made mysig: %x theirsig: %x\n", mySig, theirSig)
    50  	// add sigs to the witness stack
    51  	if swap {
    52  		tx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirSig, mySig)
    53  	} else {
    54  		tx.TxIn[0].Witness = SpendMultiSigWitStack(pre, mySig, theirSig)
    55  	}
    56  
    57  	// save channel state as closed
    58  	// Removed - this is already done in the calling function - and is killing
    59  	// the ability to just print the TX.
    60  	// q.CloseData.Closed = true
    61  	// q.CloseData.CloseTxid = tx.TxHash()
    62  	// err = nd.SaveQchanUtxoData(q)
    63  	// if err != nil {
    64  	//	return nil, err
    65  	// }
    66  
    67  	return tx, nil
    68  }
    69  
    70  // SignSimpleClose signs the given simpleClose tx, given the other signature
    71  // Tx is modified in place.
    72  func (nd *LitNode) SignSimpleClose(q *Qchan, tx *wire.MsgTx) ([64]byte, error) {
    73  
    74  	var sig [64]byte
    75  	// make hash cache
    76  	hCache := txscript.NewTxSigHashes(tx)
    77  
    78  	// generate script preimage for signing (ignore key order)
    79  	pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub)
    80  	if err != nil {
    81  		return sig, err
    82  	}
    83  	// get private signing key
    84  	priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen)
    85  	if err != nil {
    86  		return sig, err
    87  	}
    88  	// generate sig
    89  	mySig, err := txscript.RawTxInWitnessSignature(
    90  		tx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv)
    91  	if err != nil {
    92  		return sig, err
    93  	}
    94  	// truncate sig (last byte is sighash type, always sighashAll)
    95  	mySig = mySig[:len(mySig)-1]
    96  	return sig64.SigCompress(mySig)
    97  }
    98  
    99  // SignSettlementTx signs the given settlement tx based on the passed contract
   100  // using the passed private key. Tx is modified in place.
   101  func (nd *LitNode) SignSettlementTx(c *lnutil.DlcContract, tx *wire.MsgTx,
   102  	priv *koblitz.PrivateKey) ([64]byte, error) {
   103  
   104  	var sig [64]byte
   105  	// make hash cache
   106  	hCache := txscript.NewTxSigHashes(tx)
   107  
   108  	// generate script preimage for signing (ignore key order)
   109  	pre, _, err := lnutil.FundTxScript(c.OurFundMultisigPub,
   110  		c.TheirFundMultisigPub)
   111  
   112  	if err != nil {
   113  		return sig, err
   114  	}
   115  	// generate sig
   116  	mySig, err := txscript.RawTxInWitnessSignature(
   117  		tx, hCache, 0, c.TheirFundingAmount+c.OurFundingAmount,
   118  		pre, txscript.SigHashAll, priv)
   119  
   120  	if err != nil {
   121  		return sig, err
   122  	}
   123  	// truncate sig (last byte is sighash type, always sighashAll)
   124  	mySig = mySig[:len(mySig)-1]
   125  	return sig64.SigCompress(mySig)
   126  }
   127  
   128  // SignClaimTx signs the given claim tx based on the passed preimage and value
   129  // using the passed private key. Tx is modified in place. timeout=false means
   130  // it's a regular claim, timeout=true means we're claiming an output that has
   131  // expired (for instance if someone) published the wrong settlement TX, we can
   132  // claim this output back to our wallet after the timelock expired.
   133  func (nd *LitNode) SignClaimTx(claimTx *wire.MsgTx, value int64, pre []byte,
   134  	priv *koblitz.PrivateKey, timeout bool) error {
   135  
   136  	// make hash cache
   137  	hCache := txscript.NewTxSigHashes(claimTx)
   138  
   139  	// generate sig
   140  	mySig, err := txscript.RawTxInWitnessSignature(
   141  		claimTx, hCache, 0, value, pre, txscript.SigHashAll, priv)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	witStash := make([][]byte, 3)
   147  	witStash[0] = mySig
   148  	if timeout {
   149  		witStash[1] = nil
   150  	} else {
   151  		witStash[1] = []byte{0x01}
   152  	}
   153  	witStash[2] = pre
   154  	claimTx.TxIn[0].Witness = witStash
   155  	return nil
   156  }
   157  
   158  // SignNextState generates your signature for their state.
   159  func (nd *LitNode) SignState(q *Qchan) ([64]byte, [][64]byte, error) {
   160  	var sig [64]byte
   161  
   162  	// make sure channel exists, and wallet is present on node
   163  	if q == nil {
   164  		return sig, nil, fmt.Errorf("SignState nil channel")
   165  	}
   166  	_, ok := nd.SubWallet[q.Coin()]
   167  	if !ok {
   168  		return sig, nil, fmt.Errorf("SignState no wallet for cointype %d", q.Coin())
   169  	}
   170  	// build transaction for next state
   171  	commitmentTx, spendHTLCTxs, HTLCTxOuts, err := q.BuildStateTxs(false) // their tx, as I'm signing
   172  	if err != nil {
   173  		return sig, nil, err
   174  	}
   175  
   176  	logging.Infof("Signing state with Elk [%x] NextElk [%x] N2Elk [%x]\n", q.State.ElkPoint, q.State.NextElkPoint, q.State.N2ElkPoint)
   177  
   178  	// make hash cache for this tx
   179  	hCache := txscript.NewTxSigHashes(commitmentTx)
   180  
   181  	// generate script preimage (ignore key order)
   182  	pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub)
   183  	if err != nil {
   184  		return sig, nil, err
   185  	}
   186  
   187  	// get private signing key
   188  	priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen)
   189  	if err != nil {
   190  		return sig, nil, err
   191  	}
   192  
   193  	// generate sig.
   194  	bigSig, err := txscript.RawTxInWitnessSignature(
   195  		commitmentTx, hCache, 0, q.Value, pre, txscript.SigHashAll, priv)
   196  	if err != nil {
   197  		return sig, nil, err
   198  	}
   199  
   200  	// truncate sig (last byte is sighash type, always sighashAll)
   201  	bigSig = bigSig[:len(bigSig)-1]
   202  
   203  	sig, err = sig64.SigCompress(bigSig)
   204  	if err != nil {
   205  		return sig, nil, err
   206  	}
   207  
   208  	logging.Infof("____ sig creation for channel (%d,%d):\n", q.Peer(), q.Idx())
   209  	logging.Infof("\tinput %s\n", commitmentTx.TxIn[0].PreviousOutPoint.String())
   210  	for i, txout := range commitmentTx.TxOut {
   211  		logging.Infof("\toutput %d: %x %d\n", i, txout.PkScript, txout.Value)
   212  	}
   213  
   214  	logging.Infof("\tstate %d myamt: %d theiramt: %d\n", q.State.StateIdx, q.State.MyAmt, q.Value-q.State.MyAmt)
   215  
   216  	// Generate signatures for HTLC-success/failure transactions
   217  	spendHTLCSigs := map[int][64]byte{}
   218  
   219  	curElk, err := q.ElkSnd.AtIndex(q.State.StateIdx)
   220  	if err != nil {
   221  		return sig, nil, err
   222  	}
   223  	elkScalar := lnutil.ElkScalar(curElk)
   224  
   225  	ep := lnutil.ElkPointFromHash(curElk)
   226  
   227  	logging.Infof("Using elkpoint %x to sign HTLC txs", ep)
   228  
   229  	for idx, h := range HTLCTxOuts {
   230  		// Find out which vout this HTLC is in the commitment tx since BIP69
   231  		// potentially reordered them
   232  		var where uint32
   233  		for i, o := range commitmentTx.TxOut {
   234  			if bytes.Compare(o.PkScript, h.PkScript) == 0 {
   235  				where = uint32(i)
   236  				break
   237  			}
   238  		}
   239  
   240  		var HTLCPrivBase *koblitz.PrivateKey
   241  		if idx == len(q.State.HTLCs) {
   242  			HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.InProgHTLC.KeyGen)
   243  		} else if idx == len(q.State.HTLCs)+1 {
   244  			HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.CollidingHTLC.KeyGen)
   245  		} else {
   246  			HTLCPrivBase, err = nd.SubWallet[q.Coin()].GetPriv(q.State.HTLCs[idx].KeyGen)
   247  		}
   248  
   249  		if err != nil {
   250  			return sig, nil, err
   251  		}
   252  
   253  		HTLCPriv := lnutil.CombinePrivKeyWithBytes(HTLCPrivBase, elkScalar[:])
   254  
   255  		// Find the tx we need to sign. (this would all be much easier if we
   256  		// didn't use BIP69)
   257  		var spendTx *wire.MsgTx
   258  		var which int
   259  		for i, t := range spendHTLCTxs {
   260  			if t.TxIn[0].PreviousOutPoint.Index == where {
   261  				spendTx = t
   262  				which = i
   263  				break
   264  			}
   265  		}
   266  
   267  		hc := txscript.NewTxSigHashes(spendTx)
   268  		var HTLCScript []byte
   269  
   270  		if idx == len(q.State.HTLCs) {
   271  			HTLCScript, err = q.GenHTLCScript(*q.State.InProgHTLC, false)
   272  		} else if idx == len(q.State.HTLCs)+1 {
   273  			HTLCScript, err = q.GenHTLCScript(*q.State.CollidingHTLC, false)
   274  		} else {
   275  			HTLCScript, err = q.GenHTLCScript(q.State.HTLCs[idx], false)
   276  		}
   277  		if err != nil {
   278  			return sig, nil, err
   279  		}
   280  
   281  		HTLCparsed, err := txscript.ParseScript(HTLCScript)
   282  		if err != nil {
   283  			return sig, nil, err
   284  		}
   285  
   286  		spendHTLCHash := txscript.CalcWitnessSignatureHash(
   287  			HTLCparsed, hc, txscript.SigHashAll, spendTx, 0, h.Value)
   288  
   289  		logging.Infof("Signing HTLC hash: %x, with pubkey: %x", spendHTLCHash, HTLCPriv.PubKey().SerializeCompressed())
   290  
   291  		mySig, err := HTLCPriv.Sign(spendHTLCHash)
   292  		if err != nil {
   293  			return sig, nil, err
   294  		}
   295  
   296  		HTLCSig := mySig.Serialize()
   297  		s, err := sig64.SigCompress(HTLCSig)
   298  		if err != nil {
   299  			return sig, nil, err
   300  		}
   301  
   302  		spendHTLCSigs[which] = s
   303  	}
   304  
   305  	// Get the sigs in the same order as the HTLCs in the tx
   306  	var spendHTLCSigsArr [][64]byte
   307  	for i := 0; i < len(spendHTLCSigs)+2; i++ {
   308  		if s, ok := spendHTLCSigs[i]; ok {
   309  			spendHTLCSigsArr = append(spendHTLCSigsArr, s)
   310  		}
   311  	}
   312  
   313  	return sig, spendHTLCSigsArr, err
   314  }
   315  
   316  // VerifySig verifies their signature for your next state.
   317  // it also saves the sig if it's good.
   318  // do bool, error or just error?  Bad sig is an error I guess.
   319  // for verifying signature, always use theirHAKDpub, so generate & populate within
   320  // this function.
   321  func (q *Qchan) VerifySigs(sig [64]byte, HTLCSigs [][64]byte) error {
   322  
   323  	bigSig := sig64.SigDecompress(sig)
   324  	// my tx when I'm verifying.
   325  	commitmentTx, spendHTLCTxs, HTLCTxOuts, err := q.BuildStateTxs(true)
   326  	if err != nil {
   327  		return err
   328  	}
   329  
   330  	logging.Infof("Verifying signatures with Elk [%x] NextElk [%x] N2Elk [%x]\n", q.State.ElkPoint, q.State.NextElkPoint, q.State.N2ElkPoint)
   331  
   332  	// generate fund output script preimage (ignore key order)
   333  	pre, _, err := lnutil.FundTxScript(q.MyPub, q.TheirPub)
   334  	if err != nil {
   335  		return err
   336  	}
   337  
   338  	hCache := txscript.NewTxSigHashes(commitmentTx)
   339  
   340  	parsed, err := txscript.ParseScript(pre)
   341  	if err != nil {
   342  		return err
   343  	}
   344  	// always sighash all
   345  	hash := txscript.CalcWitnessSignatureHash(
   346  		parsed, hCache, txscript.SigHashAll, commitmentTx, 0, q.Value)
   347  
   348  	// sig is pre-truncated; last byte for sighashtype is always sighashAll
   349  	pSig, err := koblitz.ParseDERSignature(bigSig, koblitz.S256())
   350  	if err != nil {
   351  		return err
   352  	}
   353  	theirPubKey, err := koblitz.ParsePubKey(q.TheirPub[:], koblitz.S256())
   354  	if err != nil {
   355  		return err
   356  	}
   357  	logging.Infof("____ sig verification for channel (%d,%d):\n", q.Peer(), q.Idx())
   358  	logging.Infof("\tinput %s\n", commitmentTx.TxIn[0].PreviousOutPoint.String())
   359  	for i, txout := range commitmentTx.TxOut {
   360  		logging.Infof("\toutput %d: %x %d\n", i, txout.PkScript, txout.Value)
   361  	}
   362  	logging.Infof("\tstate %d myamt: %d theiramt: %d\n", q.State.StateIdx, q.State.MyAmt, q.Value-q.State.MyAmt)
   363  	logging.Infof("\tsig: %x\n", sig)
   364  
   365  	worked := pSig.Verify(hash, theirPubKey)
   366  	if !worked {
   367  		return fmt.Errorf("Invalid signature on chan %d state %d",
   368  			q.Idx(), q.State.StateIdx)
   369  	}
   370  
   371  	// Verify HTLC-success/failure signatures
   372  
   373  	if len(HTLCSigs) != len(spendHTLCTxs) {
   374  		return fmt.Errorf("Wrong number of signatures provided for HTLCs in channel. Got %d expected %d.",
   375  			len(HTLCSigs), len(spendHTLCTxs))
   376  	}
   377  
   378  	// Map HTLC index to signature index
   379  	sigIndex := map[uint32]uint32{}
   380  
   381  	logging.Infof("Using elkpoint %x to verify HTLC txs", q.State.NextElkPoint)
   382  
   383  	for idx, h := range HTLCTxOuts {
   384  		// Find out which vout this HTLC is in the commitment tx since BIP69
   385  		// potentially reordered them
   386  		var where uint32
   387  		for i, o := range commitmentTx.TxOut {
   388  			if bytes.Compare(o.PkScript, h.PkScript) == 0 {
   389  				where = uint32(i)
   390  				break
   391  			}
   392  		}
   393  
   394  		// Find the tx we need to verify. (this would all be much easier if we
   395  		// didn't use BIP69)
   396  		var spendTx *wire.MsgTx
   397  		var which int
   398  		for i, t := range spendHTLCTxs {
   399  			if t.TxIn[0].PreviousOutPoint.Index == where {
   400  				spendTx = t
   401  				which = i
   402  				sigIndex[uint32(idx)] = uint32(which)
   403  				break
   404  			}
   405  		}
   406  
   407  		hc := txscript.NewTxSigHashes(spendTx)
   408  		var HTLCScript []byte
   409  		if idx == len(q.State.HTLCs) {
   410  			HTLCScript, err = q.GenHTLCScript(*q.State.InProgHTLC, true)
   411  		} else if idx == len(q.State.HTLCs)+1 {
   412  			HTLCScript, err = q.GenHTLCScript(*q.State.CollidingHTLC, true)
   413  		} else {
   414  			HTLCScript, err = q.GenHTLCScript(q.State.HTLCs[idx], true)
   415  		}
   416  		if err != nil {
   417  			return err
   418  		}
   419  
   420  		HTLCparsed, err := txscript.ParseScript(HTLCScript)
   421  		if err != nil {
   422  			return err
   423  		}
   424  		// always sighash all
   425  		spendHTLCHash := txscript.CalcWitnessSignatureHash(
   426  			HTLCparsed, hc, txscript.SigHashAll, spendTx, 0, h.Value)
   427  
   428  		// sig is pre-truncated; last byte for sighashtype is always sighashAll
   429  		HTLCSig, err := koblitz.ParseDERSignature(sig64.SigDecompress(HTLCSigs[which]), koblitz.S256())
   430  		if err != nil {
   431  			return err
   432  		}
   433  
   434  		var theirHTLCPub [33]byte
   435  		if idx == len(q.State.HTLCs) {
   436  			theirHTLCPub = lnutil.CombinePubs(q.State.InProgHTLC.TheirHTLCBase, q.State.NextElkPoint)
   437  		} else if idx == len(q.State.HTLCs)+1 {
   438  			theirHTLCPub = lnutil.CombinePubs(q.State.CollidingHTLC.TheirHTLCBase, q.State.NextElkPoint)
   439  		} else {
   440  			theirHTLCPub = lnutil.CombinePubs(q.State.HTLCs[idx].TheirHTLCBase, q.State.NextElkPoint)
   441  		}
   442  
   443  		theirHTLCPubKey, err := koblitz.ParsePubKey(theirHTLCPub[:], koblitz.S256())
   444  		if err != nil {
   445  			return err
   446  		}
   447  
   448  		logging.Infof("Verifying HTLC hash: %x, with pubkey: %x", spendHTLCHash, theirHTLCPub)
   449  
   450  		sigValid := HTLCSig.Verify(spendHTLCHash, theirHTLCPubKey)
   451  		if !sigValid {
   452  			return fmt.Errorf("Invalid signature HTLC on chan %d state %d HTLC %d",
   453  				q.Idx(), q.State.StateIdx, idx)
   454  		}
   455  	}
   456  
   457  	// copy signature, overwriting old signature.
   458  	q.State.Sig = sig
   459  
   460  	// copy HTLC-success/failure signatures
   461  	for i, s := range sigIndex {
   462  		if int(i) == len(q.State.HTLCs) {
   463  			q.State.InProgHTLC.Sig = HTLCSigs[s]
   464  		} else if int(i) == len(q.State.HTLCs)+1 {
   465  			q.State.CollidingHTLC.Sig = HTLCSigs[s]
   466  		} else {
   467  			q.State.HTLCs[i].Sig = HTLCSigs[s]
   468  		}
   469  	}
   470  
   471  	return nil
   472  }