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

     1  package qln
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/mit-dci/lit/btcutil"
     7  	"github.com/mit-dci/lit/btcutil/txscript"
     8  	"github.com/mit-dci/lit/btcutil/txsort"
     9  	"github.com/mit-dci/lit/crypto/koblitz"
    10  	"github.com/mit-dci/lit/dlc"
    11  	"github.com/mit-dci/lit/lnutil"
    12  	"github.com/mit-dci/lit/logging"
    13  	"github.com/mit-dci/lit/portxo"
    14  	"github.com/mit-dci/lit/sig64"
    15  	"github.com/mit-dci/lit/wire"
    16  )
    17  
    18  func (nd *LitNode) AddContract() (*lnutil.DlcContract, error) {
    19  
    20  	c, err := nd.DlcManager.AddContract()
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  
    25  	return c, nil
    26  }
    27  
    28  func (nd *LitNode) OfferDlc(peerIdx uint32, cIdx uint64) error {
    29  	c, err := nd.DlcManager.LoadContract(cIdx)
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	if c.Status != lnutil.ContractStatusDraft {
    35  		return fmt.Errorf("You cannot offer a contract to someone that is not in draft stage")
    36  	}
    37  
    38  	if !nd.ConnectedToPeer(peerIdx) {
    39  		return fmt.Errorf("You are not connected to peer %d, do that first", peerIdx)
    40  	}
    41  
    42  	var nullBytes [33]byte
    43  	// Check if everything's set
    44  	if c.OracleA == nullBytes {
    45  		return fmt.Errorf("You need to set an oracle for the contract before offering it")
    46  	}
    47  
    48  	if c.OracleR == nullBytes {
    49  		return fmt.Errorf("You need to set an R-point for the contract before offering it")
    50  	}
    51  
    52  	if c.OracleTimestamp == 0 {
    53  		return fmt.Errorf("You need to set a settlement time for the contract before offering it")
    54  	}
    55  
    56  	if c.CoinType == dlc.COINTYPE_NOT_SET {
    57  		return fmt.Errorf("You need to set a coin type for the contract before offering it")
    58  	}
    59  
    60  	if c.Division == nil {
    61  		return fmt.Errorf("You need to set a payout division for the contract before offering it")
    62  	}
    63  
    64  	if c.OurFundingAmount+c.TheirFundingAmount == 0 {
    65  		return fmt.Errorf("You need to set a funding amount for the peers in contract before offering it")
    66  	}
    67  
    68  	c.PeerIdx = peerIdx
    69  
    70  	var kg portxo.KeyGen
    71  	kg.Depth = 5
    72  	kg.Step[0] = 44 | 1<<31
    73  	kg.Step[1] = c.CoinType | 1<<31
    74  	kg.Step[2] = UseContractFundMultisig
    75  	kg.Step[3] = c.PeerIdx | 1<<31
    76  	kg.Step[4] = uint32(c.Idx) | 1<<31
    77  
    78  	c.OurFundMultisigPub, err = nd.GetUsePub(kg, UseContractFundMultisig)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	c.OurPayoutBase, err = nd.GetUsePub(kg, UseContractPayoutBase)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	// Fund the contract
    89  	err = nd.FundContract(c)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	msg := lnutil.NewDlcOfferMsg(peerIdx, c)
    95  
    96  	c.Status = lnutil.ContractStatusOfferedByMe
    97  	err = nd.DlcManager.SaveContract(c)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	nd.tmpSendLitMsg(msg)
   103  
   104  	return nil
   105  }
   106  
   107  func (nd *LitNode) DeclineDlc(cIdx uint64, reason uint8) error {
   108  	c, err := nd.DlcManager.LoadContract(cIdx)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	if c.Status != lnutil.ContractStatusOfferedToMe {
   114  		return fmt.Errorf("You cannot decline a contract unless it is in the 'Offered/Awaiting reply' state")
   115  	}
   116  
   117  	if !nd.ConnectedToPeer(c.PeerIdx) {
   118  		return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx)
   119  	}
   120  
   121  	msg := lnutil.NewDlcOfferDeclineMsg(c.PeerIdx, reason, c.TheirIdx)
   122  	c.Status = lnutil.ContractStatusDeclined
   123  
   124  	err = nd.DlcManager.SaveContract(c)
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	nd.tmpSendLitMsg(msg)
   130  
   131  	return nil
   132  }
   133  
   134  func (nd *LitNode) AcceptDlc(cIdx uint64) error {
   135  	c, err := nd.DlcManager.LoadContract(cIdx)
   136  	if err != nil {
   137  		return err
   138  	}
   139  
   140  	if c.Status != lnutil.ContractStatusOfferedToMe {
   141  		return fmt.Errorf("You cannot decline a contract unless it is in the 'Offered/Awaiting reply' state")
   142  	}
   143  
   144  	if !nd.ConnectedToPeer(c.PeerIdx) {
   145  		return fmt.Errorf("You are not connected to peer %d, do that first", c.PeerIdx)
   146  	}
   147  
   148  	// Preconditions checked - Go execute the acceptance in a separate go routine
   149  	// while returning the status back to the client
   150  	go func(nd *LitNode, c *lnutil.DlcContract) {
   151  		c.Status = lnutil.ContractStatusAccepting
   152  		nd.DlcManager.SaveContract(c)
   153  
   154  		// Fund the contract
   155  		err = nd.FundContract(c)
   156  		if err != nil {
   157  			c.Status = lnutil.ContractStatusError
   158  			nd.DlcManager.SaveContract(c)
   159  			return
   160  		}
   161  
   162  		var kg portxo.KeyGen
   163  		kg.Depth = 5
   164  		kg.Step[0] = 44 | 1<<31
   165  		kg.Step[1] = c.CoinType | 1<<31
   166  		kg.Step[2] = UseContractFundMultisig
   167  		kg.Step[3] = c.PeerIdx | 1<<31
   168  		kg.Step[4] = uint32(c.Idx) | 1<<31
   169  
   170  		c.OurFundMultisigPub, err = nd.GetUsePub(kg, UseContractFundMultisig)
   171  		if err != nil {
   172  			logging.Errorf("Error while getting multisig pubkey: %s", err.Error())
   173  			c.Status = lnutil.ContractStatusError
   174  			nd.DlcManager.SaveContract(c)
   175  			return
   176  		}
   177  
   178  		c.OurPayoutBase, err = nd.GetUsePub(kg, UseContractPayoutBase)
   179  		if err != nil {
   180  			logging.Errorf("Error while getting payoutbase: %s", err.Error())
   181  			c.Status = lnutil.ContractStatusError
   182  			nd.DlcManager.SaveContract(c)
   183  			return
   184  		}
   185  
   186  		ourPayoutPKHKey, err := nd.GetUsePub(kg, UseContractPayoutPKH)
   187  		if err != nil {
   188  			logging.Errorf("Error while getting our payout pubkey: %s", err.Error())
   189  			c.Status = lnutil.ContractStatusError
   190  			nd.DlcManager.SaveContract(c)
   191  			return
   192  		}
   193  		copy(c.OurPayoutPKH[:], btcutil.Hash160(ourPayoutPKHKey[:]))
   194  
   195  		// Now we can sign the division
   196  		sigs, err := nd.SignSettlementDivisions(c)
   197  		if err != nil {
   198  			logging.Errorf("Error signing settlement divisions: %s", err.Error())
   199  			c.Status = lnutil.ContractStatusError
   200  			nd.DlcManager.SaveContract(c)
   201  			return
   202  		}
   203  
   204  		msg := lnutil.NewDlcOfferAcceptMsg(c, sigs)
   205  		c.Status = lnutil.ContractStatusAccepted
   206  
   207  		nd.DlcManager.SaveContract(c)
   208  		nd.tmpSendLitMsg(msg)
   209  	}(nd, c)
   210  	return nil
   211  }
   212  
   213  func (nd *LitNode) DlcOfferHandler(msg lnutil.DlcOfferMsg, peer *RemotePeer) {
   214  	c := new(lnutil.DlcContract)
   215  
   216  	c.PeerIdx = peer.Idx
   217  	c.Status = lnutil.ContractStatusOfferedToMe
   218  	// Reverse copy from the contract we received
   219  	c.OurFundingAmount = msg.Contract.TheirFundingAmount
   220  	c.TheirFundingAmount = msg.Contract.OurFundingAmount
   221  	c.OurFundingInputs = msg.Contract.TheirFundingInputs
   222  	c.TheirFundingInputs = msg.Contract.OurFundingInputs
   223  	c.OurFundMultisigPub = msg.Contract.TheirFundMultisigPub
   224  	c.TheirFundMultisigPub = msg.Contract.OurFundMultisigPub
   225  	c.OurPayoutBase = msg.Contract.TheirPayoutBase
   226  	c.TheirPayoutBase = msg.Contract.OurPayoutBase
   227  	c.OurChangePKH = msg.Contract.TheirChangePKH
   228  	c.TheirChangePKH = msg.Contract.OurChangePKH
   229  	c.TheirIdx = msg.Contract.Idx
   230  
   231  	c.Division = make([]lnutil.DlcContractDivision, len(msg.Contract.Division))
   232  	for i := 0; i < len(msg.Contract.Division); i++ {
   233  		c.Division[i].OracleValue = msg.Contract.Division[i].OracleValue
   234  		c.Division[i].ValueOurs = (c.TheirFundingAmount + c.OurFundingAmount) - msg.Contract.Division[i].ValueOurs
   235  	}
   236  
   237  	// Copy
   238  	c.CoinType = msg.Contract.CoinType
   239  	c.OracleA = msg.Contract.OracleA
   240  	c.OracleR = msg.Contract.OracleR
   241  	c.OracleTimestamp = msg.Contract.OracleTimestamp
   242  
   243  	err := nd.DlcManager.SaveContract(c)
   244  	if err != nil {
   245  		logging.Errorf("DlcOfferHandler SaveContract err %s\n", err.Error())
   246  		return
   247  	}
   248  
   249  	_, ok := nd.SubWallet[msg.Contract.CoinType]
   250  	if !ok {
   251  		// We don't have this coin type, automatically decline
   252  		nd.DeclineDlc(c.Idx, 0x02)
   253  	}
   254  
   255  }
   256  
   257  func (nd *LitNode) DlcDeclineHandler(msg lnutil.DlcOfferDeclineMsg, peer *RemotePeer) {
   258  	c, err := nd.DlcManager.LoadContract(msg.Idx)
   259  	if err != nil {
   260  		logging.Errorf("DlcDeclineHandler FindContract err %s\n", err.Error())
   261  		return
   262  	}
   263  
   264  	c.Status = lnutil.ContractStatusDeclined
   265  	err = nd.DlcManager.SaveContract(c)
   266  	if err != nil {
   267  		logging.Errorf("DlcDeclineHandler SaveContract err %s\n", err.Error())
   268  		return
   269  	}
   270  }
   271  
   272  func (nd *LitNode) DlcAcceptHandler(msg lnutil.DlcOfferAcceptMsg, peer *RemotePeer) error {
   273  	c, err := nd.DlcManager.LoadContract(msg.Idx)
   274  	if err != nil {
   275  		logging.Errorf("DlcAcceptHandler FindContract err %s\n", err.Error())
   276  		return err
   277  	}
   278  
   279  	// TODO: Check signatures
   280  
   281  	c.TheirChangePKH = msg.OurChangePKH
   282  	c.TheirFundingInputs = msg.FundingInputs
   283  	c.TheirSettlementSignatures = msg.SettlementSignatures
   284  	c.TheirFundMultisigPub = msg.OurFundMultisigPub
   285  	c.TheirPayoutBase = msg.OurPayoutBase
   286  	c.TheirPayoutPKH = msg.OurPayoutPKH
   287  	c.TheirIdx = msg.OurIdx
   288  
   289  	c.Status = lnutil.ContractStatusAccepted
   290  	err = nd.DlcManager.SaveContract(c)
   291  	if err != nil {
   292  		logging.Errorf("DlcAcceptHandler SaveContract err %s\n", err.Error())
   293  		return err
   294  	}
   295  
   296  	// create our settlement signatures and ack
   297  	sigs, err := nd.SignSettlementDivisions(c)
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	outMsg := lnutil.NewDlcContractAckMsg(c, sigs)
   303  	c.Status = lnutil.ContractStatusAcknowledged
   304  
   305  	err = nd.DlcManager.SaveContract(c)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	nd.tmpSendLitMsg(outMsg)
   311  
   312  	return nil
   313  
   314  }
   315  
   316  func (nd *LitNode) DlcContractAckHandler(msg lnutil.DlcContractAckMsg, peer *RemotePeer) {
   317  	c, err := nd.DlcManager.LoadContract(msg.Idx)
   318  	if err != nil {
   319  		logging.Errorf("DlcContractAckHandler FindContract err %s\n", err.Error())
   320  		return
   321  	}
   322  
   323  	// TODO: Check signatures
   324  
   325  	c.Status = lnutil.ContractStatusAcknowledged
   326  
   327  	err = nd.DlcManager.SaveContract(c)
   328  	if err != nil {
   329  		logging.Errorf("DlcContractAckHandler SaveContract err %s\n", err.Error())
   330  		return
   331  	}
   332  
   333  	// We have everything now, send our signatures to the funding TX
   334  	wal, ok := nd.SubWallet[c.CoinType]
   335  	if !ok {
   336  		logging.Errorf("DlcContractAckHandler No wallet for cointype %d\n", c.CoinType)
   337  		return
   338  	}
   339  
   340  	tx, err := nd.BuildDlcFundingTransaction(c)
   341  	if err != nil {
   342  		logging.Errorf("DlcContractAckHandler BuildDlcFundingTransaction err %s\n", err.Error())
   343  		return
   344  	}
   345  
   346  	err = wal.SignMyInputs(&tx)
   347  	if err != nil {
   348  		logging.Errorf("DlcContractAckHandler SignMyInputs err %s\n", err.Error())
   349  		return
   350  	}
   351  
   352  	outMsg := lnutil.NewDlcContractFundingSigsMsg(c, &tx)
   353  
   354  	nd.tmpSendLitMsg(outMsg)
   355  }
   356  
   357  func (nd *LitNode) DlcFundingSigsHandler(msg lnutil.DlcContractFundingSigsMsg, peer *RemotePeer) {
   358  	c, err := nd.DlcManager.LoadContract(msg.Idx)
   359  	if err != nil {
   360  		logging.Errorf("DlcFundingSigsHandler FindContract err %s\n", err.Error())
   361  		return
   362  	}
   363  
   364  	// TODO: Check signatures
   365  
   366  	// We have everything now. Sign our inputs to the funding TX and send it to the blockchain.
   367  	wal, ok := nd.SubWallet[c.CoinType]
   368  	if !ok {
   369  		logging.Errorf("DlcFundingSigsHandler No wallet for cointype %d\n", c.CoinType)
   370  		return
   371  	}
   372  
   373  	wal.SignMyInputs(msg.SignedFundingTx)
   374  
   375  	wal.DirectSendTx(msg.SignedFundingTx)
   376  
   377  	err = wal.WatchThis(c.FundingOutpoint)
   378  	if err != nil {
   379  		logging.Errorf("DlcFundingSigsHandler WatchThis err %s\n", err.Error())
   380  		return
   381  	}
   382  
   383  	c.Status = lnutil.ContractStatusActive
   384  	err = nd.DlcManager.SaveContract(c)
   385  	if err != nil {
   386  		logging.Errorf("DlcFundingSigsHandler SaveContract err %s\n", err.Error())
   387  		return
   388  	}
   389  
   390  	outMsg := lnutil.NewDlcContractSigProofMsg(c, msg.SignedFundingTx)
   391  
   392  	nd.tmpSendLitMsg(outMsg)
   393  }
   394  
   395  func (nd *LitNode) DlcSigProofHandler(msg lnutil.DlcContractSigProofMsg, peer *RemotePeer) {
   396  	c, err := nd.DlcManager.LoadContract(msg.Idx)
   397  	if err != nil {
   398  		logging.Errorf("DlcSigProofHandler FindContract err %s\n", err.Error())
   399  		return
   400  	}
   401  
   402  	// TODO: Check signatures
   403  	wal, ok := nd.SubWallet[c.CoinType]
   404  	if !ok {
   405  		logging.Errorf("DlcSigProofHandler No wallet for cointype %d\n", c.CoinType)
   406  		return
   407  	}
   408  
   409  	err = wal.WatchThis(c.FundingOutpoint)
   410  	if err != nil {
   411  		logging.Errorf("DlcSigProofHandler WatchThis err %s\n", err.Error())
   412  		return
   413  	}
   414  
   415  	c.Status = lnutil.ContractStatusActive
   416  	err = nd.DlcManager.SaveContract(c)
   417  	if err != nil {
   418  		logging.Errorf("DlcSigProofHandler SaveContract err %s\n", err.Error())
   419  		return
   420  	}
   421  }
   422  
   423  func (nd *LitNode) SignSettlementDivisions(c *lnutil.DlcContract) ([]lnutil.DlcContractSettlementSignature, error) {
   424  	wal, ok := nd.SubWallet[c.CoinType]
   425  	if !ok {
   426  		return nil, fmt.Errorf("Wallet of type %d not found", c.CoinType)
   427  	}
   428  
   429  	var kg portxo.KeyGen
   430  	kg.Depth = 5
   431  	kg.Step[0] = 44 | 1<<31
   432  	kg.Step[1] = c.CoinType | 1<<31
   433  	kg.Step[2] = UseContractFundMultisig
   434  	kg.Step[3] = c.PeerIdx | 1<<31
   435  	kg.Step[4] = uint32(c.Idx) | 1<<31
   436  
   437  	priv, err := wal.GetPriv(kg)
   438  	if err != nil {
   439  		return nil, fmt.Errorf("Could not get private key for contract %d", c.Idx)
   440  	}
   441  
   442  	fundingTx, err := nd.BuildDlcFundingTransaction(c)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  
   447  	c.FundingOutpoint = wire.OutPoint{Hash: fundingTx.TxHash(), Index: 0}
   448  
   449  	returnValue := make([]lnutil.DlcContractSettlementSignature, len(c.Division))
   450  	for i, d := range c.Division {
   451  		tx, err := lnutil.SettlementTx(c, d, true)
   452  		if err != nil {
   453  			return nil, err
   454  		}
   455  		sig, err := nd.SignSettlementTx(c, tx, priv)
   456  		if err != nil {
   457  			return nil, err
   458  		}
   459  		returnValue[i].Outcome = d.OracleValue
   460  		returnValue[i].Signature = sig
   461  	}
   462  
   463  	return returnValue, nil
   464  }
   465  
   466  func (nd *LitNode) BuildDlcFundingTransaction(c *lnutil.DlcContract) (wire.MsgTx, error) {
   467  	// make the tx
   468  	tx := wire.NewMsgTx()
   469  
   470  	// set version 2, for op_csv
   471  	tx.Version = 2
   472  
   473  	// add all the txins
   474  	var ourInputTotal int64
   475  	var theirInputTotal int64
   476  
   477  	for _, u := range c.OurFundingInputs {
   478  		tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil))
   479  		ourInputTotal += u.Value
   480  	}
   481  	for _, u := range c.TheirFundingInputs {
   482  		tx.AddTxIn(wire.NewTxIn(&u.Outpoint, nil, nil))
   483  		theirInputTotal += u.Value
   484  	}
   485  
   486  	// add change and sort
   487  	tx.AddTxOut(wire.NewTxOut(theirInputTotal-c.TheirFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.TheirChangePKH)))
   488  	tx.AddTxOut(wire.NewTxOut(ourInputTotal-c.OurFundingAmount-500, lnutil.DirectWPKHScriptFromPKH(c.OurChangePKH)))
   489  
   490  	txsort.InPlaceSort(tx)
   491  
   492  	// get txo for channel
   493  	txo, err := lnutil.FundTxOut(c.TheirFundMultisigPub, c.OurFundMultisigPub, c.OurFundingAmount+c.TheirFundingAmount)
   494  	if err != nil {
   495  		return *tx, err
   496  	}
   497  
   498  	// Ensure contract funding output is always at position 0
   499  	txos := make([]*wire.TxOut, len(tx.TxOut)+1)
   500  	txos[0] = txo
   501  	copy(txos[1:], tx.TxOut)
   502  	tx.TxOut = txos
   503  
   504  	return *tx, nil
   505  
   506  }
   507  
   508  func (nd *LitNode) FundContract(c *lnutil.DlcContract) error {
   509  	wal, ok := nd.SubWallet[c.CoinType]
   510  	if !ok {
   511  		return fmt.Errorf("No wallet of type %d connected", c.CoinType)
   512  	}
   513  
   514  	utxos, _, err := wal.PickUtxos(int64(c.OurFundingAmount), 500, wal.Fee(), true)
   515  	if err != nil {
   516  		return err
   517  	}
   518  
   519  	c.OurFundingInputs = make([]lnutil.DlcContractFundingInput, len(utxos))
   520  	for i := 0; i < len(utxos); i++ {
   521  		c.OurFundingInputs[i] = lnutil.DlcContractFundingInput{Outpoint: utxos[i].Op, Value: utxos[i].Value}
   522  	}
   523  
   524  	c.OurChangePKH, err = wal.NewAdr()
   525  	if err != nil {
   526  		return err
   527  	}
   528  
   529  	return nil
   530  }
   531  
   532  func (nd *LitNode) SettleContract(cIdx uint64, oracleValue int64, oracleSig [32]byte) ([32]byte, [32]byte, error) {
   533  
   534  	c, err := nd.DlcManager.LoadContract(cIdx)
   535  	if err != nil {
   536  		logging.Errorf("SettleContract FindContract err %s\n", err.Error())
   537  		return [32]byte{}, [32]byte{}, err
   538  	}
   539  
   540  	c.Status = lnutil.ContractStatusSettling
   541  	err = nd.DlcManager.SaveContract(c)
   542  	if err != nil {
   543  		logging.Errorf("SettleContract SaveContract err %s\n", err.Error())
   544  		return [32]byte{}, [32]byte{}, err
   545  	}
   546  
   547  	d, err := c.GetDivision(oracleValue)
   548  	if err != nil {
   549  		logging.Errorf("SettleContract GetDivision err %s\n", err.Error())
   550  		return [32]byte{}, [32]byte{}, err
   551  	}
   552  
   553  	wal, ok := nd.SubWallet[c.CoinType]
   554  	if !ok {
   555  		return [32]byte{}, [32]byte{}, fmt.Errorf("SettleContract Wallet of type %d not found", c.CoinType)
   556  	}
   557  
   558  	var kg portxo.KeyGen
   559  	kg.Depth = 5
   560  	kg.Step[0] = 44 | 1<<31
   561  	kg.Step[1] = c.CoinType | 1<<31
   562  	kg.Step[2] = UseContractFundMultisig
   563  	kg.Step[3] = c.PeerIdx | 1<<31
   564  	kg.Step[4] = uint32(c.Idx) | 1<<31
   565  
   566  	priv, err := wal.GetPriv(kg)
   567  	if err != nil {
   568  		return [32]byte{}, [32]byte{}, fmt.Errorf("SettleContract Could not get private key for contract %d", c.Idx)
   569  	}
   570  
   571  	settleTx, err := lnutil.SettlementTx(c, *d, false)
   572  	if err != nil {
   573  		logging.Errorf("SettleContract SettlementTx err %s\n", err.Error())
   574  		return [32]byte{}, [32]byte{}, err
   575  	}
   576  
   577  	mySig, err := nd.SignSettlementTx(c, settleTx, priv)
   578  	if err != nil {
   579  		logging.Errorf("SettleContract SignSettlementTx err %s", err.Error())
   580  		return [32]byte{}, [32]byte{}, err
   581  	}
   582  
   583  	myBigSig := sig64.SigDecompress(mySig)
   584  
   585  	theirSig, err := c.GetTheirSettlementSignature(oracleValue)
   586  	theirBigSig := sig64.SigDecompress(theirSig)
   587  
   588  	// put the sighash all byte on the end of both signatures
   589  	myBigSig = append(myBigSig, byte(txscript.SigHashAll))
   590  	theirBigSig = append(theirBigSig, byte(txscript.SigHashAll))
   591  
   592  	pre, swap, err := lnutil.FundTxScript(c.OurFundMultisigPub, c.TheirFundMultisigPub)
   593  	if err != nil {
   594  		logging.Errorf("SettleContract FundTxScript err %s", err.Error())
   595  		return [32]byte{}, [32]byte{}, err
   596  	}
   597  
   598  	// swap if needed
   599  	if swap {
   600  		settleTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, theirBigSig, myBigSig)
   601  	} else {
   602  		settleTx.TxIn[0].Witness = SpendMultiSigWitStack(pre, myBigSig, theirBigSig)
   603  	}
   604  
   605  	// Settlement TX should be valid here, so publish it.
   606  	err = wal.DirectSendTx(settleTx)
   607  	if err != nil {
   608  		logging.Errorf("SettleContract DirectSendTx (settle) err %s", err.Error())
   609  		return [32]byte{}, [32]byte{}, err
   610  	}
   611  
   612  	// TODO: Claim the contract settlement output back to our wallet - otherwise the peer can claim it after locktime.
   613  	txClaim := wire.NewMsgTx()
   614  	txClaim.Version = 2
   615  
   616  	settleOutpoint := wire.OutPoint{Hash: settleTx.TxHash(), Index: 0}
   617  	txClaim.AddTxIn(wire.NewTxIn(&settleOutpoint, nil, nil))
   618  
   619  	addr, err := wal.NewAdr()
   620  	txClaim.AddTxOut(wire.NewTxOut(d.ValueOurs-1000, lnutil.DirectWPKHScriptFromPKH(addr))) // todo calc fee - fee is double here because the contract output already had the fee deducted in the settlement TX
   621  
   622  	kg.Step[2] = UseContractPayoutBase
   623  	privSpend, _ := wal.GetPriv(kg)
   624  
   625  	pubSpend := wal.GetPub(kg)
   626  	privOracle, pubOracle := koblitz.PrivKeyFromBytes(koblitz.S256(), oracleSig[:])
   627  	privContractOutput := lnutil.CombinePrivateKeys(privSpend, privOracle)
   628  
   629  	var pubOracleBytes [33]byte
   630  	copy(pubOracleBytes[:], pubOracle.SerializeCompressed())
   631  	var pubSpendBytes [33]byte
   632  	copy(pubSpendBytes[:], pubSpend.SerializeCompressed())
   633  
   634  	settleScript := lnutil.DlcCommitScript(c.OurPayoutBase, pubOracleBytes, c.TheirPayoutBase, 5)
   635  	err = nd.SignClaimTx(txClaim, settleTx.TxOut[0].Value, settleScript, privContractOutput, false)
   636  	if err != nil {
   637  		logging.Errorf("SettleContract SignClaimTx err %s", err.Error())
   638  		return [32]byte{}, [32]byte{}, err
   639  	}
   640  
   641  	// Claim TX should be valid here, so publish it.
   642  	err = wal.DirectSendTx(txClaim)
   643  	if err != nil {
   644  		logging.Errorf("SettleContract DirectSendTx (claim) err %s", err.Error())
   645  		return [32]byte{}, [32]byte{}, err
   646  	}
   647  
   648  	c.Status = lnutil.ContractStatusClosed
   649  	err = nd.DlcManager.SaveContract(c)
   650  	if err != nil {
   651  		return [32]byte{}, [32]byte{}, err
   652  	}
   653  	return settleTx.TxHash(), txClaim.TxHash(), nil
   654  }