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

     1  package qln
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/mit-dci/lit/btcutil/txsort"
     6  	"github.com/mit-dci/lit/consts"
     7  	"github.com/mit-dci/lit/crypto/koblitz"
     8  	"github.com/mit-dci/lit/elkrem"
     9  	"github.com/mit-dci/lit/lnutil"
    10  	"github.com/mit-dci/lit/logging"
    11  	"github.com/mit-dci/lit/portxo"
    12  	"github.com/mit-dci/lit/wire"
    13  )
    14  
    15  const (
    16  	DUALFUND_DECLINE_REASON_USER                    = 0x01 // User manually declined
    17  	DUALFUND_DECLINE_REASON_INSUFFICIENT_BALANCE    = 0x02 // Not enough balance to accept the request
    18  	DUALFUND_DECLINE_REASON_UNSUPPORTED_COIN        = 0x03 // We don't have that coin type, so we declined automatically
    19  	DUALFUND_DECLINE_REASON_ALREADY_PENDING_REQUEST = 0x04 // Current design only supports single dualfund request at a time. So decline when we have one in progress.
    20  
    21  )
    22  
    23  /* Dual funding process.
    24  
    25  Allows a peer to initiate funding with its counterparty, requesting that party
    26  to also commit funds into the channel.
    27  
    28  Parameters:
    29  
    30  Peer		:	The peer index to dual fund with
    31  MyAmount	:	The amount i am funding myself
    32  TheirAmount	:	The amount we request the peer to fund
    33  
    34  Initial send and data seems unnecessary here, since both parties are funding,
    35  neither is sending the other side money (the initial state will be equal
    36  to the amounts funded)
    37  
    38  The process that's followed:
    39  
    40  A -> B      Request funding UTXOs and change address for TheirAmount
    41  
    42  B -> A      Respond with UTXOs and change address for TheirAmount
    43  (or) B -> A Respond with a decline
    44  
    45  A -> B      Respond with UTXOs and change address for MyAmount
    46  
    47  A and B     Compose the funding TX
    48  
    49  Then (regular funding messages):
    50  A -> B      Requests channel point and refund pubkey, sending along its own
    51  
    52              A channel point (33) (channel pubkey for now)
    53              A refund (33)
    54  
    55  B -> A      Replies with channel point and refund pubkey
    56  
    57              B channel point (32) (channel pubkey for now)
    58              B refund (33)
    59  
    60  A -> B      Sends channel Description:
    61              ---
    62              outpoint (36)
    63              capacity (8)
    64              initial push (8)
    65              B's HAKD pub #1 (33)
    66              signature (~70)
    67              ---
    68  
    69  B -> A      Acknowledges Channel:
    70              ---
    71              A's HAKD pub #1 (33)
    72              signature (~70)
    73              ---
    74  
    75  Then (dual funding specific, exchange of signatures for the funding TX):
    76  
    77  A -> B      Requests signatures for the funding TX, sending along its own
    78  
    79  B -> A      Sends signatures for the funding TX
    80  
    81  A           Publishes funding TX
    82  
    83  === time passes, fund tx gets in a block ===
    84  
    85  A -> B      SigProof
    86              SPV proof of the outpoint (block height, tree depth, tx index, hashes)
    87              signature (~70)
    88  
    89  */
    90  
    91  // DualFundChannel requests a peer to do dual funding. The remote peer can decline.
    92  
    93  func (nd *LitNode) DualFundChannel(
    94  	peerIdx, cointype uint32, ourAmount int64, theirAmount int64) (*DualFundingResult, error) {
    95  
    96  	nullFundingResult := new(DualFundingResult)
    97  	nullFundingResult.Error = true
    98  
    99  	wal, ok := nd.SubWallet[cointype]
   100  	if !ok {
   101  		return nullFundingResult, fmt.Errorf("No wallet of type %d connected", cointype)
   102  	}
   103  
   104  	changeAddr, err := wal.NewAdr()
   105  	if err != nil {
   106  		return nullFundingResult, err
   107  	}
   108  
   109  	nd.InProgDual.mtx.Lock()
   110  	//	defer nd.InProg.mtx.Lock()
   111  	if nd.InProgDual.PeerIdx != 0 {
   112  		nd.InProgDual.mtx.Unlock()
   113  		return nullFundingResult, fmt.Errorf("dual fund with peer %d not done yet", nd.InProgDual.PeerIdx)
   114  	}
   115  
   116  	if theirAmount <= 0 || ourAmount <= 0 {
   117  		nd.InProgDual.mtx.Unlock()
   118  		return nullFundingResult, fmt.Errorf("dual funding requires both parties to commit funds")
   119  	}
   120  
   121  	if theirAmount+ourAmount < consts.MinChanCapacity { // limit for now
   122  		nd.InProgDual.mtx.Unlock()
   123  		return nullFundingResult, fmt.Errorf("Min channel capacity 1M sat")
   124  	}
   125  
   126  	// TODO - would be convenient if it auto connected to the peer huh
   127  	if !nd.ConnectedToPeer(peerIdx) {
   128  		nd.InProgDual.mtx.Unlock()
   129  		return nullFundingResult, fmt.Errorf("Not connected to peer %d. Do that yourself.", peerIdx)
   130  	}
   131  
   132  	cIdx, err := nd.NextChannelIdx()
   133  	if err != nil {
   134  		nd.InProgDual.mtx.Unlock()
   135  		return nullFundingResult, err
   136  	}
   137  
   138  	// Find UTXOs to use
   139  	utxos, _, err := wal.PickUtxos(ourAmount, consts.DualFundFee, wal.Fee(), true) //TODO Fee calculation
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	ourInputs := make([]lnutil.DualFundingInput, len(utxos))
   145  	for i := 0; i < len(utxos); i++ {
   146  		ourInputs[i] = lnutil.DualFundingInput{Outpoint: utxos[i].Op, Value: utxos[i].Value}
   147  	}
   148  
   149  	var kg portxo.KeyGen
   150  	kg.Depth = 5
   151  	kg.Step[0] = 44 | 1<<31
   152  	kg.Step[1] = cointype | 1<<31
   153  	kg.Step[2] = UseChannelFund
   154  	kg.Step[3] = peerIdx | 1<<31
   155  	kg.Step[4] = cIdx | 1<<31
   156  
   157  	myChanPub, _ := nd.GetUsePub(kg, UseChannelFund)
   158  	myRefundPub, _ := nd.GetUsePub(kg, UseChannelRefund)
   159  	myHAKDbase, err := nd.GetUsePub(kg, UseChannelHAKDBase)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	nd.InProgDual.ChanIdx = cIdx
   165  	nd.InProgDual.PeerIdx = peerIdx
   166  	nd.InProgDual.CoinType = cointype
   167  	nd.InProgDual.OurAmount = ourAmount
   168  	nd.InProgDual.TheirAmount = theirAmount
   169  	nd.InProgDual.OurChangeAddress = changeAddr
   170  	nd.InProgDual.InitiatedByUs = true
   171  	nd.InProgDual.OurPub = myChanPub
   172  	nd.InProgDual.OurRefundPub = myRefundPub
   173  	nd.InProgDual.OurHAKDBase = myHAKDbase
   174  	nd.InProgDual.OurInputs = ourInputs
   175  	nd.InProgDual.mtx.Unlock() // switch to defer
   176  
   177  	outMsg := lnutil.NewDualFundingReqMsg(peerIdx, cointype, ourAmount, theirAmount, myChanPub, myRefundPub, myHAKDbase, changeAddr, ourInputs)
   178  
   179  	nd.tmpSendLitMsg(outMsg)
   180  
   181  	// wait until it's done!
   182  	result := <-nd.InProgDual.done
   183  
   184  	return result, nil
   185  }
   186  
   187  // Declines the in progress (received) dual funding request
   188  func (nd *LitNode) DualFundDecline(reason uint8) {
   189  	outMsg := lnutil.NewDualFundingDeclMsg(nd.InProgDual.PeerIdx, reason)
   190  	nd.tmpSendLitMsg(outMsg)
   191  	nd.InProgDual.mtx.Lock()
   192  	nd.InProgDual.Clear()
   193  	nd.InProgDual.mtx.Unlock()
   194  	return
   195  }
   196  
   197  func (nd *LitNode) DualFundAccept() *DualFundingResult {
   198  	wal, ok := nd.SubWallet[nd.InProgDual.CoinType]
   199  	if !ok {
   200  		logging.Errorf("DualFundingReqHandler err no wallet for type %d", nd.InProgDual.CoinType)
   201  		return nil
   202  	}
   203  
   204  	changeAddr, err := wal.NewAdr()
   205  	if err != nil {
   206  		logging.Errorf("Error creating change address: %s", err.Error())
   207  		return nil
   208  	}
   209  
   210  	// Find UTXOs to use
   211  	utxos, _, err := wal.PickUtxos(nd.InProgDual.OurAmount, 500, wal.Fee(), true) //TODO Fee calculation
   212  	if err != nil {
   213  		logging.Errorf("Error fetching UTXOs to use for funding: %s", err.Error())
   214  		return nil
   215  	}
   216  
   217  	ourInputs := make([]lnutil.DualFundingInput, len(utxos))
   218  	for i := 0; i < len(utxos); i++ {
   219  		ourInputs[i] = lnutil.DualFundingInput{Outpoint: utxos[i].Op, Value: utxos[i].Value}
   220  	}
   221  
   222  	var kg portxo.KeyGen
   223  	kg.Depth = 5
   224  	kg.Step[0] = 44 | 1<<31
   225  	kg.Step[1] = nd.InProgDual.CoinType | 1<<31
   226  	kg.Step[2] = UseChannelFund
   227  	kg.Step[3] = nd.InProgDual.PeerIdx | 1<<31
   228  	kg.Step[4] = nd.InProgDual.ChanIdx | 1<<31
   229  
   230  	myChanPub, _ := nd.GetUsePub(kg, UseChannelFund)
   231  	myRefundPub, _ := nd.GetUsePub(kg, UseChannelRefund)
   232  	myHAKDbase, err := nd.GetUsePub(kg, UseChannelHAKDBase)
   233  	if err != nil {
   234  		logging.Errorf("Error fetching UTXOs to use for funding: %s", err.Error())
   235  		return nil
   236  	}
   237  
   238  	var keyGen portxo.KeyGen
   239  	keyGen.Depth = 5
   240  	keyGen.Step[0] = 44 | 1<<31
   241  	keyGen.Step[1] = nd.InProgDual.CoinType | 1<<31
   242  	keyGen.Step[2] = UseHTLCBase
   243  	keyGen.Step[3] = 0 | 1<<31
   244  	keyGen.Step[4] = nd.InProgDual.ChanIdx | 1<<31
   245  
   246  	MyNextHTLCBase, err := nd.GetUsePub(keyGen, UseHTLCBase)
   247  	if err != nil {
   248  		logging.Errorf("error generating NextHTLCBase %v", err)
   249  		return nil
   250  	}
   251  
   252  	keyGen.Step[3] = 1 | 1<<31
   253  	MyN2HTLCBase, err := nd.GetUsePub(keyGen, UseHTLCBase)
   254  	if err != nil {
   255  		logging.Errorf("error generating N2HTLCBase %v", err)
   256  		return nil
   257  	}
   258  
   259  	nd.InProgDual.mtx.Lock()
   260  	nd.InProgDual.OurChangeAddress = changeAddr
   261  	nd.InProgDual.OurInputs = ourInputs
   262  	nd.InProgDual.OurPub = myChanPub
   263  	nd.InProgDual.OurRefundPub = myRefundPub
   264  	nd.InProgDual.OurHAKDBase = myHAKDbase
   265  	nd.InProgDual.OurNextHTLCBase = MyNextHTLCBase
   266  	nd.InProgDual.OurN2HTLCBase = MyN2HTLCBase
   267  	nd.InProgDual.mtx.Unlock()
   268  
   269  	outMsg := lnutil.NewDualFundingAcceptMsg(nd.InProgDual.PeerIdx, nd.InProgDual.CoinType, myChanPub, myRefundPub, myHAKDbase, changeAddr, ourInputs, MyNextHTLCBase, MyN2HTLCBase)
   270  
   271  	nd.tmpSendLitMsg(outMsg)
   272  
   273  	// wait until it's done!
   274  	result := <-nd.InProgDual.done
   275  
   276  	return result
   277  }
   278  
   279  // RECIPIENT
   280  // DualFundingReqHandler gets a request with the funding data of the remote peer, along with the
   281  // amount of funding requested to return data for.
   282  func (nd *LitNode) DualFundingReqHandler(msg lnutil.DualFundingReqMsg) {
   283  
   284  	cIdx, err := nd.NextChannelIdx()
   285  	if err != nil {
   286  		logging.Errorf("DualFundingReqHandler err %s", err.Error())
   287  		return
   288  	}
   289  
   290  	wal, ok := nd.SubWallet[msg.CoinType]
   291  	if !ok {
   292  		logging.Errorf("DualFundingReqHandler err no wallet for type %d", msg.CoinType)
   293  		logging.Warnf("Auto declining dual fund request. We don't handle coin type %d\n", msg.CoinType)
   294  		nd.DualFundDecline(DUALFUND_DECLINE_REASON_UNSUPPORTED_COIN)
   295  		return
   296  	}
   297  
   298  	nd.InProgDual.mtx.Lock()
   299  	if nd.InProgDual.PeerIdx != 0 {
   300  		logging.Warnf("DualFundingReqHandler already have a pending request. Declining.")
   301  		nd.DualFundDecline(DUALFUND_DECLINE_REASON_ALREADY_PENDING_REQUEST)
   302  		nd.InProgDual.mtx.Unlock()
   303  		return
   304  	}
   305  
   306  	nd.InProgDual.ChanIdx = cIdx
   307  	nd.InProgDual.PeerIdx = msg.Peer()
   308  	nd.InProgDual.CoinType = msg.CoinType
   309  	nd.InProgDual.OurAmount = msg.TheirAmount
   310  	nd.InProgDual.TheirInputs = msg.OurInputs
   311  	nd.InProgDual.TheirAmount = msg.OurAmount
   312  	nd.InProgDual.TheirChangeAddress = msg.OurChangeAddressPKH
   313  	nd.InProgDual.TheirPub = msg.OurPub
   314  	nd.InProgDual.TheirRefundPub = msg.OurRefundPub
   315  	nd.InProgDual.TheirHAKDBase = msg.OurHAKDBase
   316  	nd.InProgDual.InitiatedByUs = false
   317  	nd.InProgDual.mtx.Unlock()
   318  
   319  	// Check if we have the requested amount, otherwise auto-decline
   320  	var allPorTxos portxo.TxoSliceByAmt
   321  	allPorTxos, err = wal.UtxoDump()
   322  	if err != nil {
   323  		logging.Errorf("DualFundingReqHandler err %s", err.Error())
   324  		return
   325  	}
   326  
   327  	nowHeight := wal.CurrentHeight()
   328  	spendable := allPorTxos.SumWitness(nowHeight)
   329  	if msg.TheirAmount > spendable-50000 {
   330  		logging.Warnf("Auto declining dual fund request. Requested %d, but we only have %d for funding\n", msg.TheirAmount, spendable-50000)
   331  		nd.DualFundDecline(DUALFUND_DECLINE_REASON_INSUFFICIENT_BALANCE)
   332  		return
   333  	}
   334  
   335  	return
   336  }
   337  
   338  // RECIPIENT
   339  // DualFundingDeclHandler gets a message where the remote party is declining the
   340  // request to dualfund.
   341  func (nd *LitNode) DualFundingDeclHandler(msg lnutil.DualFundingDeclMsg) {
   342  
   343  	result := new(DualFundingResult)
   344  
   345  	result.DeclineReason = msg.Reason
   346  
   347  	nd.InProgDual.mtx.Lock()
   348  	nd.InProgDual.done <- result
   349  	nd.InProgDual.Clear()
   350  	nd.InProgDual.mtx.Unlock()
   351  
   352  	return
   353  }
   354  
   355  // DualFundingAcceptHandler gets a message where the remote party is accepting the
   356  // request to dualfund.
   357  func (nd *LitNode) DualFundingAcceptHandler(msg lnutil.DualFundingAcceptMsg) {
   358  
   359  	nd.InProgDual.mtx.Lock()
   360  	nd.InProgDual.TheirInputs = msg.OurInputs
   361  	nd.InProgDual.TheirChangeAddress = msg.OurChangeAddressPKH
   362  	nd.InProgDual.TheirPub = msg.OurPub
   363  	nd.InProgDual.TheirRefundPub = msg.OurRefundPub
   364  	nd.InProgDual.TheirHAKDBase = msg.OurHAKDBase
   365  
   366  	// make channel (not in db) just for keys / elk
   367  	q := new(Qchan)
   368  
   369  	q.Height = -1
   370  
   371  	q.Value = nd.InProg.Amt
   372  
   373  	q.KeyGen.Depth = 5
   374  	q.KeyGen.Step[0] = 44 | 1<<31
   375  	q.KeyGen.Step[1] = nd.InProgDual.CoinType | 1<<31
   376  	q.KeyGen.Step[2] = UseChannelFund
   377  	q.KeyGen.Step[3] = nd.InProgDual.PeerIdx | 1<<31
   378  	q.KeyGen.Step[4] = nd.InProgDual.ChanIdx | 1<<31
   379  
   380  	q.MyPub, _ = nd.GetUsePub(q.KeyGen, UseChannelFund)
   381  	q.MyRefundPub, _ = nd.GetUsePub(q.KeyGen, UseChannelRefund)
   382  	q.MyHAKDBase, _ = nd.GetUsePub(q.KeyGen, UseChannelHAKDBase)
   383  
   384  	// chop up incoming message, save points to channel struct
   385  	copy(q.TheirPub[:], nd.InProgDual.TheirPub[:])
   386  	copy(q.TheirRefundPub[:], nd.InProgDual.TheirRefundPub[:])
   387  	copy(q.TheirHAKDBase[:], nd.InProgDual.TheirHAKDBase[:])
   388  
   389  	// make sure their pubkeys are real pubkeys
   390  	_, err := koblitz.ParsePubKey(q.TheirPub[:], koblitz.S256())
   391  	if err != nil {
   392  		nd.InProgDual.mtx.Unlock()
   393  		logging.Errorf("PubRespHandler TheirPub err %s", err.Error())
   394  		return
   395  	}
   396  	_, err = koblitz.ParsePubKey(q.TheirRefundPub[:], koblitz.S256())
   397  	if err != nil {
   398  		nd.InProgDual.mtx.Unlock()
   399  		logging.Errorf("PubRespHandler TheirRefundPub err %s", err.Error())
   400  		return
   401  	}
   402  	_, err = koblitz.ParsePubKey(q.TheirHAKDBase[:], koblitz.S256())
   403  	if err != nil {
   404  		nd.InProgDual.mtx.Unlock()
   405  		logging.Errorf("PubRespHandler TheirHAKDBase err %s", err.Error())
   406  		return
   407  	}
   408  
   409  	_, err = koblitz.ParsePubKey(msg.OurNextHTLCBase[:], koblitz.S256())
   410  	if err != nil {
   411  		nd.InProgDual.mtx.Unlock()
   412  		logging.Errorf("PubRespHandler NextHTLCBase err %s", err.Error())
   413  		return
   414  	}
   415  	_, err = koblitz.ParsePubKey(msg.OurN2HTLCBase[:], koblitz.S256())
   416  	if err != nil {
   417  		nd.InProgDual.mtx.Unlock()
   418  		logging.Errorf("PubRespHandler N2HTLCBase err %s", err.Error())
   419  		return
   420  	}
   421  
   422  	nd.InProgDual.TheirNextHTLCBase = msg.OurNextHTLCBase
   423  	nd.InProgDual.TheirN2HTLCBase = msg.OurN2HTLCBase
   424  
   425  	// derive elkrem sender root from HD keychain
   426  	elkRoot, _ := nd.GetElkremRoot(q.KeyGen)
   427  	q.ElkSnd = elkrem.NewElkremSender(elkRoot)
   428  
   429  	// Build the funding transaction
   430  	tx, _ := nd.BuildDualFundingTransaction()
   431  
   432  	outPoint := wire.OutPoint{Hash: tx.TxHash(), Index: 0}
   433  	nd.InProgDual.OutPoint = &outPoint
   434  	q.Op = *nd.InProgDual.OutPoint
   435  
   436  	// create initial state for elkrem points
   437  	q.State = new(StatCom)
   438  	q.State.StateIdx = 0
   439  	q.State.MyAmt = nd.InProgDual.OurAmount
   440  	// get fee from sub wallet.  Later should make fee per channel and update state
   441  	// based on size
   442  	q.State.Fee = nd.SubWallet[q.Coin()].Fee() * consts.QcStateFee
   443  	q.Value = nd.InProgDual.OurAmount + nd.InProgDual.TheirAmount
   444  
   445  	q.State.NextHTLCBase = msg.OurNextHTLCBase
   446  	q.State.N2HTLCBase = msg.OurN2HTLCBase
   447  	q.State.MyNextHTLCBase = nd.InProgDual.OurNextHTLCBase
   448  	q.State.MyN2HTLCBase = nd.InProgDual.OurN2HTLCBase
   449  
   450  	// save channel to db
   451  	err = nd.SaveQChan(q)
   452  	if err != nil {
   453  		logging.Errorf("PointRespHandler SaveQchanState err %s", err.Error())
   454  		return
   455  	}
   456  
   457  	// when funding a channel, give them the first *3* elkpoints.
   458  	elkPointZero, err := q.ElkPoint(false, 0)
   459  	if err != nil {
   460  		logging.Errorf("PointRespHandler ElkpointZero err %s", err.Error())
   461  		return
   462  	}
   463  	elkPointOne, err := q.ElkPoint(false, 1)
   464  	if err != nil {
   465  		logging.Errorf("PointRespHandler ElkpointOne err %s", err.Error())
   466  		return
   467  	}
   468  
   469  	elkPointTwo, err := q.N2ElkPointForThem()
   470  	if err != nil {
   471  		logging.Errorf("PointRespHandler ElkpointTwo err %s", err.Error())
   472  		return
   473  	}
   474  
   475  	outMsg := lnutil.NewChanDescMsg(
   476  		nd.InProgDual.PeerIdx, *nd.InProgDual.OutPoint, q.MyPub, q.MyRefundPub, q.MyHAKDBase, nd.InProgDual.OurNextHTLCBase,
   477  		nd.InProgDual.OurN2HTLCBase,
   478  		nd.InProgDual.CoinType, nd.InProgDual.OurAmount+nd.InProgDual.TheirAmount, nd.InProgDual.TheirAmount,
   479  		elkPointZero, elkPointOne, elkPointTwo, q.State.Data)
   480  
   481  	nd.InProgDual.mtx.Unlock()
   482  	nd.tmpSendLitMsg(outMsg)
   483  
   484  	return
   485  }
   486  
   487  func (nd *LitNode) BuildDualFundingTransaction() (*wire.MsgTx, error) {
   488  	// make the tx
   489  	tx := wire.NewMsgTx()
   490  
   491  	w, ok := nd.SubWallet[nd.InProgDual.CoinType]
   492  	if !ok {
   493  		err := fmt.Errorf("BuildDualFundingTransaction err no wallet for type %d", nd.InProgDual.CoinType)
   494  		return tx, err
   495  	}
   496  
   497  	// set version 2, for op_csv
   498  	tx.Version = 2
   499  	// set the time, the way core does.
   500  	tx.LockTime = uint32(w.CurrentHeight())
   501  
   502  	// add all the txins
   503  	var ourInputTotal int64
   504  	var theirInputTotal int64
   505  
   506  	for _, u := range nd.InProgDual.OurInputs {
   507  		tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil))
   508  		ourInputTotal += u.Value
   509  	}
   510  	for _, u := range nd.InProgDual.TheirInputs {
   511  		tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil))
   512  		theirInputTotal += u.Value
   513  	}
   514  
   515  	var initiatorPub [33]byte
   516  	var counterPartyPub [33]byte
   517  	if nd.InProgDual.InitiatedByUs {
   518  		initiatorPub = nd.InProgDual.OurPub
   519  		counterPartyPub = nd.InProgDual.TheirPub
   520  	} else {
   521  		initiatorPub = nd.InProgDual.TheirPub
   522  		counterPartyPub = nd.InProgDual.OurPub
   523  	}
   524  
   525  	// get txo for channel
   526  	txo, err := lnutil.FundTxOut(initiatorPub, counterPartyPub, nd.InProgDual.OurAmount+nd.InProgDual.TheirAmount)
   527  	if err != nil {
   528  		return tx, err
   529  	}
   530  	tx.AddTxOut(txo)
   531  
   532  	// add the change outputs
   533  	var initiatorChange int64
   534  	var counterPartyChange int64
   535  	var initiatorChangeAddress [20]byte
   536  	var counterPartyChangeAddress [20]byte
   537  
   538  	if nd.InProgDual.InitiatedByUs {
   539  		initiatorChangeAddress = nd.InProgDual.OurChangeAddress
   540  		initiatorChange = ourInputTotal - nd.InProgDual.OurAmount - consts.DualFundFee
   541  		counterPartyChangeAddress = nd.InProgDual.TheirChangeAddress
   542  		counterPartyChange = theirInputTotal - nd.InProgDual.TheirAmount - consts.DualFundFee
   543  
   544  	} else {
   545  		initiatorChangeAddress = nd.InProgDual.TheirChangeAddress
   546  		initiatorChange = theirInputTotal - nd.InProgDual.TheirAmount - consts.DualFundFee
   547  		counterPartyChangeAddress = nd.InProgDual.OurChangeAddress
   548  		counterPartyChange = ourInputTotal - nd.InProgDual.OurAmount - consts.DualFundFee
   549  	}
   550  
   551  	changeScriptInitiator := lnutil.DirectWPKHScriptFromPKH(initiatorChangeAddress)
   552  	tx.AddTxOut(wire.NewTxOut(initiatorChange, changeScriptInitiator))
   553  
   554  	changeScriptCounterParty := lnutil.DirectWPKHScriptFromPKH(counterPartyChangeAddress)
   555  	tx.AddTxOut(wire.NewTxOut(counterPartyChange, changeScriptCounterParty))
   556  
   557  	txsort.InPlaceSort(tx)
   558  
   559  	return tx, nil
   560  }
   561  
   562  // RECIPIENT
   563  // QChanDescHandler takes in a description of a channel output.  It then
   564  // saves it to the local db, and returns a channel acknowledgement
   565  func (nd *LitNode) DualFundChanDescHandler(msg lnutil.ChanDescMsg) error {
   566  
   567  	logging.Infof("DualFundChanDescHandler\n")
   568  
   569  	wal, ok := nd.SubWallet[msg.CoinType]
   570  	if !ok {
   571  		return fmt.Errorf("DualFundChanDescHandler err no wallet for type %d", msg.CoinType)
   572  	}
   573  
   574  	// deserialize desc
   575  	op := msg.Outpoint
   576  	opArr := lnutil.OutPointToBytes(op)
   577  	amt := msg.Capacity
   578  
   579  	cIdx, err := nd.NextChannelIdx()
   580  	if err != nil {
   581  		return fmt.Errorf("DualFundChanDescHandler err %s", err.Error())
   582  	}
   583  
   584  	qc := new(Qchan)
   585  
   586  	qc.Height = -1
   587  	qc.KeyGen.Depth = 5
   588  	qc.KeyGen.Step[0] = 44 | 1<<31
   589  	qc.KeyGen.Step[1] = msg.CoinType | 1<<31
   590  	qc.KeyGen.Step[2] = UseChannelFund
   591  	qc.KeyGen.Step[3] = msg.Peer() | 1<<31
   592  	qc.KeyGen.Step[4] = cIdx | 1<<31
   593  	qc.Value = amt
   594  	qc.Mode = portxo.TxoP2WSHComp
   595  	qc.Op = op
   596  
   597  	qc.TheirPub = msg.PubKey
   598  	qc.TheirRefundPub = msg.RefundPub
   599  	qc.TheirHAKDBase = msg.HAKDbase
   600  	qc.MyPub, _ = nd.GetUsePub(qc.KeyGen, UseChannelFund)
   601  	qc.MyRefundPub, _ = nd.GetUsePub(qc.KeyGen, UseChannelRefund)
   602  	qc.MyHAKDBase, _ = nd.GetUsePub(qc.KeyGen, UseChannelHAKDBase)
   603  
   604  	_, err = koblitz.ParsePubKey(msg.NextHTLCBase[:], koblitz.S256())
   605  	if err != nil {
   606  		return fmt.Errorf("QChanDescHandler NextHTLCBase err %s", err.Error())
   607  	}
   608  	_, err = koblitz.ParsePubKey(msg.N2HTLCBase[:], koblitz.S256())
   609  	if err != nil {
   610  		return fmt.Errorf("QChanDescHandler N2HTLCBase err %s", err.Error())
   611  	}
   612  
   613  	// it should go into the next bucket and get the right key index.
   614  	// but we can't actually check that.
   615  	//	qc, err := nd.SaveFundTx(
   616  	//		op, amt, peerArr, theirPub, theirRefundPub, theirHAKDbase)
   617  	//	if err != nil {
   618  	//		logging.Errorf("QChanDescHandler SaveFundTx err %s", err.Error())
   619  	//		return
   620  	//	}
   621  	logging.Infof("got multisig output %s amt %d\n", op.String(), amt)
   622  
   623  	// create initial state
   624  	qc.State = new(StatCom)
   625  	// similar to SIGREV in pushpull
   626  
   627  	// TODO assumes both parties use same fee
   628  	qc.State.Fee = wal.Fee() * consts.QcStateFee
   629  	qc.State.MyAmt = msg.InitPayment
   630  
   631  	qc.State.Data = msg.Data
   632  
   633  	qc.State.StateIdx = 0
   634  	// use new ElkPoint for signing
   635  	qc.State.ElkPoint = msg.ElkZero
   636  	qc.State.NextElkPoint = msg.ElkOne
   637  	qc.State.N2ElkPoint = msg.ElkTwo
   638  
   639  	qc.State.MyNextHTLCBase = nd.InProgDual.OurNextHTLCBase
   640  	qc.State.MyN2HTLCBase = nd.InProgDual.OurN2HTLCBase
   641  
   642  	qc.State.NextHTLCBase = msg.NextHTLCBase
   643  	qc.State.N2HTLCBase = msg.N2HTLCBase
   644  
   645  	// save new channel to db
   646  	err = nd.SaveQChan(qc)
   647  	if err != nil {
   648  		return fmt.Errorf("DualFundChanDescHandler err %s", err.Error())
   649  	}
   650  
   651  	// load ... the thing I just saved.  why?
   652  	qc, err = nd.GetQchan(opArr)
   653  	if err != nil {
   654  		return fmt.Errorf("DualFundChanDescHandler GetQchan err %s", err.Error())
   655  	}
   656  
   657  	// when funding a channel, give them the first *2* elkpoints.
   658  	theirElkPointZero, err := qc.ElkPoint(false, 0)
   659  	if err != nil {
   660  		return fmt.Errorf("DualFundChanDescHandler err %s", err.Error())
   661  	}
   662  	theirElkPointOne, err := qc.ElkPoint(false, 1)
   663  	if err != nil {
   664  		return fmt.Errorf("DualFundChanDescHandler err %s", err.Error())
   665  	}
   666  
   667  	theirElkPointTwo, err := qc.N2ElkPointForThem()
   668  	if err != nil {
   669  		return fmt.Errorf("DualFundChanDescHandler err %s", err.Error())
   670  	}
   671  
   672  	sig, _, err := nd.SignState(qc)
   673  	if err != nil {
   674  		return fmt.Errorf("DualFundChanDescHandler SignState err %s", err.Error())
   675  	}
   676  
   677  	logging.Infof("Acking channel...\n")
   678  
   679  	fundingTx, err := nd.BuildDualFundingTransaction()
   680  	if err != nil {
   681  		return fmt.Errorf("DualFundChanDescHandler BuildDualFundingTransaction err %s", err.Error())
   682  	}
   683  
   684  	wal.SignMyInputs(fundingTx)
   685  
   686  	outMsg := lnutil.NewDualFundingChanAckMsg(
   687  		msg.Peer(), op,
   688  		theirElkPointZero, theirElkPointOne, theirElkPointTwo,
   689  		sig, fundingTx)
   690  
   691  	nd.tmpSendLitMsg(outMsg)
   692  
   693  	return nil
   694  }
   695  
   696  // FUNDER
   697  // QChanAckHandler takes in an acknowledgement multisig description.
   698  // when a multisig outpoint is ackd, that causes the funder to sign and broadcast.
   699  func (nd *LitNode) DualFundChanAckHandler(msg lnutil.DualFundingChanAckMsg, peer *RemotePeer) {
   700  	opArr := lnutil.OutPointToBytes(msg.Outpoint)
   701  	sig := msg.Signature
   702  
   703  	// load channel to save their refund address
   704  	qc, err := nd.GetQchan(opArr)
   705  	if err != nil {
   706  		logging.Errorf("DualFundChanAckHandler GetQchan err %s", err.Error())
   707  		return
   708  	}
   709  
   710  	//	err = qc.IngestElkrem(revElk)
   711  	//	if err != nil { // this can't happen because it's the first elk... remove?
   712  	//		logging.Errorf("QChanAckHandler IngestElkrem err %s", err.Error())
   713  	//		return
   714  	//	}
   715  	qc.State.ElkPoint = msg.ElkZero
   716  	qc.State.NextElkPoint = msg.ElkOne
   717  	qc.State.N2ElkPoint = msg.ElkTwo
   718  
   719  	err = qc.VerifySigs(sig, nil)
   720  	if err != nil {
   721  		logging.Errorf("DualFundChanAckHandler VerifySig err %s", err.Error())
   722  		return
   723  	}
   724  
   725  	// verify worked; Save state 1 to DB
   726  	err = nd.SaveQchanState(qc)
   727  	if err != nil {
   728  		logging.Errorf("DualFundChanAckHandler SaveQchanState err %s", err.Error())
   729  		return
   730  	}
   731  
   732  	// Make sure everything works & is saved, then clear InProg.
   733  
   734  	// sign their com tx to send
   735  	sig, _, err = nd.SignState(qc)
   736  	if err != nil {
   737  		logging.Errorf("DualFundChanAckHandler SignState err %s", err.Error())
   738  		return
   739  	}
   740  
   741  	// OK to fund.
   742  	tx, err := nd.BuildDualFundingTransaction()
   743  	if err != nil {
   744  		logging.Errorf("DualFundChanAckHandler BuildDualFundingTransaction err %s", err.Error())
   745  		return
   746  	}
   747  	err = nd.SubWallet[qc.Coin()].SignMyInputs(tx)
   748  	if err != nil {
   749  		logging.Errorf("DualFundChanAckHandler SignMyInputs err %s", err.Error())
   750  		return
   751  	}
   752  
   753  	// Add signatures from peer
   754  	for i := range msg.SignedFundingTx.TxIn {
   755  		if (msg.SignedFundingTx.TxIn[i].Witness != nil || msg.SignedFundingTx.TxIn[i].SignatureScript != nil) && tx.TxIn[i].Witness == nil && tx.TxIn[i].SignatureScript == nil {
   756  			tx.TxIn[i].Witness = msg.SignedFundingTx.TxIn[i].Witness
   757  			tx.TxIn[i].SignatureScript = msg.SignedFundingTx.TxIn[i].SignatureScript
   758  		}
   759  	}
   760  
   761  	nd.SubWallet[qc.Coin()].DirectSendTx(tx)
   762  
   763  	err = nd.SubWallet[qc.Coin()].WatchThis(qc.Op)
   764  	if err != nil {
   765  		logging.Errorf("DualFundChanAckHandler WatchThis err %s", err.Error())
   766  		return
   767  	}
   768  
   769  	logging.Infof("Registering refund address in the wallet\n")
   770  	// tell base wallet about watcher refund address in case that happens
   771  	// TODO this is weird & ugly... maybe have an export keypath func?
   772  	nullTxo := new(portxo.PorTxo)
   773  	nullTxo.Value = 0 // redundant, but explicitly show that this is just for adr
   774  	nullTxo.KeyGen = qc.KeyGen
   775  	nullTxo.KeyGen.Step[2] = UseChannelWatchRefund
   776  	nd.SubWallet[qc.Coin()].ExportUtxo(nullTxo)
   777  
   778  	// channel creation is ~complete, clear InProg.
   779  	// We may be asked to re-send the sig-proof
   780  	result := new(DualFundingResult)
   781  
   782  	result.Accepted = true
   783  	result.ChannelId = qc.KeyGen.Step[4] & 0x7fffffff
   784  
   785  	logging.Infof("Built result with channel ID %d, committing...\n", result.ChannelId)
   786  
   787  	nd.InProgDual.mtx.Lock()
   788  	nd.InProgDual.done <- result
   789  	nd.InProgDual.Clear()
   790  	nd.InProgDual.mtx.Unlock()
   791  
   792  	peer.QCs[qc.Idx()] = qc
   793  	peer.OpMap[opArr] = qc.Idx()
   794  
   795  	// sig proof should be sent later once there are confirmations.
   796  	// it'll have an spv proof of the fund tx.
   797  	// but for now just send the sig.
   798  	logging.Infof("Sending sigproof\n")
   799  
   800  	outMsg := lnutil.NewSigProofMsg(msg.Peer(), msg.Outpoint, sig)
   801  
   802  	nd.tmpSendLitMsg(outMsg)
   803  
   804  	return
   805  }
   806  
   807  // RECIPIENT
   808  // SigProofHandler saves the signature the recipient stores.
   809  // In some cases you don't need this message.
   810  func (nd *LitNode) DualFundSigProofHandler(msg lnutil.SigProofMsg, peer *RemotePeer) {
   811  
   812  	op := msg.Outpoint
   813  	opArr := lnutil.OutPointToBytes(op)
   814  
   815  	qc, err := nd.GetQchan(opArr)
   816  	if err != nil {
   817  		logging.Errorf("DualFundSigProofHandler err %s", err.Error())
   818  		return
   819  	}
   820  
   821  	wal, ok := nd.SubWallet[qc.Coin()]
   822  	if !ok {
   823  		logging.Errorf("Not connected to coin type %d\n", qc.Coin())
   824  		return
   825  	}
   826  
   827  	err = qc.VerifySigs(msg.Signature, nil)
   828  	if err != nil {
   829  		logging.Errorf("DualFundSigProofHandler err %s", err.Error())
   830  		return
   831  	}
   832  
   833  	// sig OK, save
   834  	err = nd.SaveQchanState(qc)
   835  	if err != nil {
   836  		logging.Errorf("DualFundSigProofHandler err %s", err.Error())
   837  		return
   838  	}
   839  
   840  	err = wal.WatchThis(op)
   841  
   842  	if err != nil {
   843  		logging.Errorf("DualFundSigProofHandler err %s", err.Error())
   844  		return
   845  	}
   846  
   847  	// tell base wallet about watcher refund address in case that happens
   848  	nullTxo := new(portxo.PorTxo)
   849  	nullTxo.Value = 0 // redundant, but explicitly show that this is just for adr
   850  	nullTxo.KeyGen = qc.KeyGen
   851  	nullTxo.KeyGen.Step[2] = UseChannelWatchRefund
   852  	wal.ExportUtxo(nullTxo)
   853  
   854  	peer.QCs[qc.Idx()] = qc
   855  	peer.OpMap[opArr] = qc.Idx()
   856  
   857  	// sig OK; in terms of UI here's where you can say "payment received"
   858  	// "channel online" etc
   859  
   860  	result := new(DualFundingResult)
   861  
   862  	result.Accepted = true
   863  	result.ChannelId = qc.KeyGen.Step[4] & 0x7fffffff
   864  	nd.InProgDual.mtx.Lock()
   865  	nd.InProgDual.done <- result
   866  	nd.InProgDual.Clear()
   867  	nd.InProgDual.mtx.Unlock()
   868  
   869  	return
   870  }