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

     1  package lnutil
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/binary"
     7  	"fmt"
     8  	"math/big"
     9  
    10  	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
    11  	"github.com/mit-dci/lit/consts"
    12  	"github.com/mit-dci/lit/crypto/koblitz"
    13  	"github.com/mit-dci/lit/logging"
    14  	"github.com/mit-dci/lit/wire"
    15  )
    16  
    17  // DlcContractStatus is an enumeration containing the various statuses a
    18  // contract can have
    19  type DlcContractStatus int
    20  
    21  const (
    22  	ContractStatusDraft        DlcContractStatus = 0
    23  	ContractStatusOfferedByMe  DlcContractStatus = 1
    24  	ContractStatusOfferedToMe  DlcContractStatus = 2
    25  	ContractStatusDeclined     DlcContractStatus = 3
    26  	ContractStatusAccepted     DlcContractStatus = 4
    27  	ContractStatusAcknowledged DlcContractStatus = 5
    28  	ContractStatusActive       DlcContractStatus = 6
    29  	ContractStatusSettling     DlcContractStatus = 7
    30  	ContractStatusClosed       DlcContractStatus = 8
    31  	ContractStatusError        DlcContractStatus = 9
    32  	ContractStatusAccepting    DlcContractStatus = 10
    33  )
    34  
    35  // scalarSize is the size of an encoded big endian scalar.
    36  const scalarSize = 32
    37  
    38  // DlcContract is a struct containing all elements to work with a Discreet
    39  // Log Contract. This struct is stored in the database
    40  type DlcContract struct {
    41  	// Index of the contract for referencing in commands
    42  	Idx uint64
    43  	// Index of the contract on the other peer (so we can reference it in
    44  	// messages)
    45  	TheirIdx uint64
    46  	// Index of the peer we've offered the contract to or received the contract
    47  	// from
    48  	PeerIdx uint32
    49  	// Coin type
    50  	CoinType uint32
    51  	// Pub keys of the oracle and the R point used in the contract
    52  	OracleA, OracleR [33]byte
    53  	// The time we expect the oracle to publish
    54  	OracleTimestamp uint64
    55  	// The payout specification
    56  	Division []DlcContractDivision
    57  	// The amounts either side are funding
    58  	OurFundingAmount, TheirFundingAmount int64
    59  	// PKH to which the contracts funding change should go
    60  	OurChangePKH, TheirChangePKH [20]byte
    61  	// Pubkey used in the funding multisig output
    62  	OurFundMultisigPub, TheirFundMultisigPub [33]byte
    63  	// Pubkey to be used in the commit script (combined with oracle pubkey
    64  	// or CSV timeout)
    65  	OurPayoutBase, TheirPayoutBase [33]byte
    66  	// Pubkeyhash to which the contract pays out (directly)
    67  	OurPayoutPKH, TheirPayoutPKH [20]byte
    68  	// Status of the contract
    69  	Status DlcContractStatus
    70  	// Outpoints used to fund the contract
    71  	OurFundingInputs, TheirFundingInputs []DlcContractFundingInput
    72  	// Signatures for the settlement transactions
    73  	TheirSettlementSignatures []DlcContractSettlementSignature
    74  	// The outpoint of the funding TX we want to spend in the settlement
    75  	// for easier monitoring
    76  	FundingOutpoint wire.OutPoint
    77  }
    78  
    79  // DlcContractDivision describes a single division of the contract. If the
    80  // oracle predicts OracleValue, we receive ValueOurs
    81  type DlcContractDivision struct {
    82  	OracleValue int64
    83  	ValueOurs   int64
    84  }
    85  
    86  // DlcContractFundingInput describes a UTXO that is offered to fund the
    87  // contract with
    88  type DlcContractFundingInput struct {
    89  	Outpoint wire.OutPoint
    90  	Value    int64
    91  }
    92  
    93  // DlcContractFromBytes deserializes a byte array back into a DlcContract struct
    94  func DlcContractFromBytes(b []byte) (*DlcContract, error) {
    95  	buf := bytes.NewBuffer(b)
    96  	c := new(DlcContract)
    97  
    98  	ourIdx, err := wire.ReadVarInt(buf, 0)
    99  	if err != nil {
   100  		logging.Errorf("Error while deserializing varint for theirIdx: %s", err.Error())
   101  		return nil, err
   102  	}
   103  	c.Idx = ourIdx
   104  
   105  	theirIdx, err := wire.ReadVarInt(buf, 0)
   106  	if err != nil {
   107  		logging.Errorf("Error while deserializing varint for theirIdx: %s", err.Error())
   108  		return nil, err
   109  	}
   110  	c.TheirIdx = theirIdx
   111  
   112  	copy(c.OracleA[:], buf.Next(33))
   113  	copy(c.OracleR[:], buf.Next(33))
   114  
   115  	peerIdx, err := wire.ReadVarInt(buf, 0)
   116  	if err != nil {
   117  		logging.Errorf("Error while deserializing varint for peerIdx: %s", err.Error())
   118  		return nil, err
   119  	}
   120  	c.PeerIdx = uint32(peerIdx)
   121  
   122  	coinType, err := wire.ReadVarInt(buf, 0)
   123  	if err != nil {
   124  		logging.Errorf("Error while deserializing varint for coinType: %s", err.Error())
   125  		return nil, err
   126  	}
   127  	c.CoinType = uint32(coinType)
   128  	c.OracleTimestamp, err = wire.ReadVarInt(buf, 0)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	ourFundingAmount, err := wire.ReadVarInt(buf, 0)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	c.OurFundingAmount = int64(ourFundingAmount)
   137  	theirFundingAmount, err := wire.ReadVarInt(buf, 0)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  	c.TheirFundingAmount = int64(theirFundingAmount)
   142  
   143  	copy(c.OurChangePKH[:], buf.Next(20))
   144  	copy(c.TheirChangePKH[:], buf.Next(20))
   145  
   146  	copy(c.OurFundMultisigPub[:], buf.Next(33))
   147  	copy(c.TheirFundMultisigPub[:], buf.Next(33))
   148  
   149  	copy(c.OurPayoutBase[:], buf.Next(33))
   150  	copy(c.TheirPayoutBase[:], buf.Next(33))
   151  
   152  	copy(c.OurPayoutPKH[:], buf.Next(20))
   153  	copy(c.TheirPayoutPKH[:], buf.Next(20))
   154  
   155  	status, err := wire.ReadVarInt(buf, 0)
   156  	if err != nil {
   157  		logging.Errorf("Error while deserializing varint for status: %s", err.Error())
   158  		return nil, err
   159  	}
   160  
   161  	c.Status = DlcContractStatus(status)
   162  
   163  	ourInputsLen, err := wire.ReadVarInt(buf, 0)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	c.OurFundingInputs = make([]DlcContractFundingInput, ourInputsLen)
   169  	var op [36]byte
   170  	for i := uint64(0); i < ourInputsLen; i++ {
   171  		copy(op[:], buf.Next(36))
   172  		c.OurFundingInputs[i].Outpoint = *OutPointFromBytes(op)
   173  		inputValue, err := wire.ReadVarInt(buf, 0)
   174  		if err != nil {
   175  			return nil, err
   176  		}
   177  		c.OurFundingInputs[i].Value = int64(inputValue)
   178  	}
   179  
   180  	theirInputsLen, err := wire.ReadVarInt(buf, 0)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	c.TheirFundingInputs = make([]DlcContractFundingInput, theirInputsLen)
   186  	for i := uint64(0); i < theirInputsLen; i++ {
   187  		copy(op[:], buf.Next(36))
   188  		c.TheirFundingInputs[i].Outpoint = *OutPointFromBytes(op)
   189  		inputValue, err := wire.ReadVarInt(buf, 0)
   190  		if err != nil {
   191  
   192  			return nil, err
   193  		}
   194  		c.TheirFundingInputs[i].Value = int64(inputValue)
   195  	}
   196  
   197  	divisionLen, err := wire.ReadVarInt(buf, 0)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	c.Division = make([]DlcContractDivision, divisionLen)
   203  	for i := uint64(0); i < divisionLen; i++ {
   204  		oracleValue, err := wire.ReadVarInt(buf, 0)
   205  		if err != nil {
   206  			return nil, err
   207  		}
   208  		valueOurs, err := wire.ReadVarInt(buf, 0)
   209  		if err != nil {
   210  			return nil, err
   211  		}
   212  		c.Division[i].OracleValue = int64(oracleValue)
   213  		c.Division[i].ValueOurs = int64(valueOurs)
   214  	}
   215  
   216  	theirSigCount, err := wire.ReadVarInt(buf, 0)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	c.TheirSettlementSignatures = make([]DlcContractSettlementSignature,
   221  		theirSigCount)
   222  
   223  	for i := uint64(0); i < theirSigCount; i++ {
   224  		outcome, err := wire.ReadVarInt(buf, 0)
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  		c.TheirSettlementSignatures[i].Outcome = int64(outcome)
   229  		copy(c.TheirSettlementSignatures[i].Signature[:], buf.Next(64))
   230  	}
   231  
   232  	copy(op[:], buf.Next(36))
   233  	c.FundingOutpoint = *OutPointFromBytes(op)
   234  
   235  	return c, nil
   236  }
   237  
   238  // Bytes serializes a DlcContract struct into a byte array
   239  func (self *DlcContract) Bytes() []byte {
   240  	var buf bytes.Buffer
   241  
   242  	wire.WriteVarInt(&buf, 0, uint64(self.Idx))
   243  	wire.WriteVarInt(&buf, 0, uint64(self.TheirIdx))
   244  	buf.Write(self.OracleA[:])
   245  	buf.Write(self.OracleR[:])
   246  	wire.WriteVarInt(&buf, 0, uint64(self.PeerIdx))
   247  	wire.WriteVarInt(&buf, 0, uint64(self.CoinType))
   248  	wire.WriteVarInt(&buf, 0, uint64(self.OracleTimestamp))
   249  	wire.WriteVarInt(&buf, 0, uint64(self.OurFundingAmount))
   250  	wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingAmount))
   251  
   252  	buf.Write(self.OurChangePKH[:])
   253  	buf.Write(self.TheirChangePKH[:])
   254  	buf.Write(self.OurFundMultisigPub[:])
   255  	buf.Write(self.TheirFundMultisigPub[:])
   256  	buf.Write(self.OurPayoutBase[:])
   257  	buf.Write(self.TheirPayoutBase[:])
   258  	buf.Write(self.OurPayoutPKH[:])
   259  	buf.Write(self.TheirPayoutPKH[:])
   260  
   261  	var status = uint64(self.Status)
   262  	wire.WriteVarInt(&buf, 0, status)
   263  
   264  	ourInputsLen := uint64(len(self.OurFundingInputs))
   265  	wire.WriteVarInt(&buf, 0, ourInputsLen)
   266  
   267  	for i := 0; i < len(self.OurFundingInputs); i++ {
   268  		opArr := OutPointToBytes(self.OurFundingInputs[i].Outpoint)
   269  		buf.Write(opArr[:])
   270  		wire.WriteVarInt(&buf, 0, uint64(self.OurFundingInputs[i].Value))
   271  	}
   272  
   273  	theirInputsLen := uint64(len(self.TheirFundingInputs))
   274  	wire.WriteVarInt(&buf, 0, theirInputsLen)
   275  
   276  	for i := 0; i < len(self.TheirFundingInputs); i++ {
   277  		opArr := OutPointToBytes(self.TheirFundingInputs[i].Outpoint)
   278  		buf.Write(opArr[:])
   279  		wire.WriteVarInt(&buf, 0, uint64(self.TheirFundingInputs[i].Value))
   280  	}
   281  
   282  	divisionLen := uint64(len(self.Division))
   283  	wire.WriteVarInt(&buf, 0, divisionLen)
   284  
   285  	for i := 0; i < len(self.Division); i++ {
   286  		wire.WriteVarInt(&buf, 0, uint64(self.Division[i].OracleValue))
   287  		wire.WriteVarInt(&buf, 0, uint64(self.Division[i].ValueOurs))
   288  	}
   289  
   290  	theirSigLen := uint64(len(self.TheirSettlementSignatures))
   291  	wire.WriteVarInt(&buf, 0, theirSigLen)
   292  
   293  	for i := 0; i < len(self.TheirSettlementSignatures); i++ {
   294  		outcome := uint64(self.TheirSettlementSignatures[i].Outcome)
   295  		wire.WriteVarInt(&buf, 0, outcome)
   296  		buf.Write(self.TheirSettlementSignatures[i].Signature[:])
   297  	}
   298  
   299  	opArr := OutPointToBytes(self.FundingOutpoint)
   300  	buf.Write(opArr[:])
   301  
   302  	return buf.Bytes()
   303  }
   304  
   305  // GetDivision loops over all division specifications inside the contract and
   306  // returns the one matching the requested oracle value
   307  func (c DlcContract) GetDivision(value int64) (*DlcContractDivision, error) {
   308  	for _, d := range c.Division {
   309  		if d.OracleValue == value {
   310  			return &d, nil
   311  		}
   312  	}
   313  
   314  	return nil, fmt.Errorf("Division not found in contract")
   315  }
   316  
   317  // GetTheirSettlementSignature loops over all stored settlement signatures from
   318  // the counter party and returns the one matching the requested oracle value
   319  func (c DlcContract) GetTheirSettlementSignature(val int64) ([64]byte, error) {
   320  
   321  	for _, s := range c.TheirSettlementSignatures {
   322  		if s.Outcome == val {
   323  			return s.Signature, nil
   324  		}
   325  	}
   326  
   327  	return [64]byte{}, fmt.Errorf("Signature not found in contract")
   328  }
   329  
   330  // PrintTx prints out a transaction as serialized byte array to StdOut
   331  func PrintTx(tx *wire.MsgTx) {
   332  	var buf bytes.Buffer
   333  	w := bufio.NewWriter(&buf)
   334  	tx.Serialize(w)
   335  	w.Flush()
   336  	logging.Infof("%x\n", buf.Bytes())
   337  }
   338  
   339  // DlcOutput returns a Txo for a particular value that pays to
   340  // (PubKeyPeer+PubKeyOracleSig or (OurPubKey and TimeDelay))
   341  func DlcOutput(pkPeer, pkOracleSig, pkOurs [33]byte, value int64) *wire.TxOut {
   342  	scriptBytes := DlcCommitScript(pkPeer, pkOracleSig, pkOurs, 5)
   343  	scriptBytes = P2WSHify(scriptBytes)
   344  
   345  	return wire.NewTxOut(value, scriptBytes)
   346  }
   347  
   348  // DlcCommitScript makes a script that pays to (PubKeyPeer+PubKeyOracleSig or
   349  // (OurPubKey and TimeDelay)). We send this over (signed) to the other side. If
   350  // they publish the TX with the correct script they can use the oracle's
   351  // signature and their own private key to claim the funds from the output.
   352  // However, if they send the wrong one, they won't be able to claim the funds
   353  // and we can claim them once the time delay has passed.
   354  func DlcCommitScript(pubKeyPeer, pubKeyOracleSig, ourPubKey [33]byte,
   355  	delay uint16) []byte {
   356  	// Combine pubKey and Oracle Sig
   357  	combinedPubKey := CombinePubs(pubKeyPeer, pubKeyOracleSig)
   358  	return CommitScript(combinedPubKey, ourPubKey, delay)
   359  }
   360  
   361  // BigIntToEncodedBytes converts a big integer into its corresponding
   362  // 32 byte big endian representation.
   363  func BigIntToEncodedBytes(a *big.Int) *[32]byte {
   364  	s := new([32]byte)
   365  	if a == nil {
   366  		return s
   367  	}
   368  	// Caveat: a can be longer than 32 bytes.
   369  	aB := a.Bytes()
   370  
   371  	// If we have a short byte string, expand
   372  	// it so that it's long enough.
   373  	aBLen := len(aB)
   374  	if aBLen < scalarSize {
   375  		diff := scalarSize - aBLen
   376  		for i := 0; i < diff; i++ {
   377  			aB = append([]byte{0x00}, aB...)
   378  		}
   379  	}
   380  
   381  	for i := 0; i < scalarSize; i++ {
   382  		s[i] = aB[i]
   383  	}
   384  
   385  	return s
   386  }
   387  
   388  // DlcCalcOracleSignaturePubKey computes the predicted signature s*G
   389  // it's just R - h(R||m)A
   390  func DlcCalcOracleSignaturePubKey(msg []byte, oracleA,
   391  	oracleR [33]byte) ([33]byte, error) {
   392  	return computePubKey(oracleA, oracleR, msg)
   393  }
   394  
   395  // calculates P = pubR - h(msg, pubR)pubA
   396  func computePubKey(pubA, pubR [33]byte, msg []byte) ([33]byte, error) {
   397  	var returnValue [33]byte
   398  
   399  	// Hardcode curve
   400  	curve := koblitz.S256()
   401  
   402  	A, err := koblitz.ParsePubKey(pubA[:], curve)
   403  	if err != nil {
   404  		return returnValue, err
   405  	}
   406  
   407  	R, err := koblitz.ParsePubKey(pubR[:], curve)
   408  	if err != nil {
   409  		return returnValue, err
   410  	}
   411  
   412  	// e = Hash(messageType, oraclePubQ)
   413  	var hashInput []byte
   414  	hashInput = append(msg, R.X.Bytes()...)
   415  	e := chainhash.HashB(hashInput)
   416  
   417  	bigE := new(big.Int).SetBytes(e)
   418  
   419  	if bigE.Cmp(curve.N) >= 0 {
   420  		return returnValue, fmt.Errorf("hash of (msg, pubR) too big")
   421  	}
   422  
   423  	// e * B
   424  	A.X, A.Y = curve.ScalarMult(A.X, A.Y, e)
   425  
   426  	A.Y.Neg(A.Y)
   427  
   428  	A.Y.Mod(A.Y, curve.P)
   429  
   430  	P := new(koblitz.PublicKey)
   431  
   432  	// add to R
   433  	P.X, P.Y = curve.Add(A.X, A.Y, R.X, R.Y)
   434  	copy(returnValue[:], P.SerializeCompressed())
   435  	return returnValue, nil
   436  }
   437  
   438  // SettlementTx returns the transaction to settle the contract. ours = the one
   439  // we generate & sign. Theirs (ours = false) = the one they generated, so we can
   440  // use their sigs
   441  func SettlementTx(c *DlcContract, d DlcContractDivision,
   442  	ours bool) (*wire.MsgTx, error) {
   443  
   444  	tx := wire.NewMsgTx()
   445  	// set version 2, for op_csv
   446  	tx.Version = 2
   447  
   448  	tx.AddTxIn(wire.NewTxIn(&c.FundingOutpoint, nil, nil))
   449  
   450  	totalFee := int64(consts.DlcSettlementTxFee) // TODO: Calculate
   451  	feeEach := int64(float64(totalFee) / float64(2))
   452  	feeOurs := feeEach
   453  	feeTheirs := feeEach
   454  	valueOurs := d.ValueOurs
   455  	// We don't have enough to pay for a fee. We get 0, our contract partner
   456  	// pays the rest of the fee
   457  	if valueOurs < feeEach {
   458  		feeOurs = valueOurs
   459  		valueOurs = 0
   460  	} else {
   461  		valueOurs = d.ValueOurs - feeOurs
   462  	}
   463  	totalContractValue := c.TheirFundingAmount + c.OurFundingAmount
   464  	valueTheirs := totalContractValue - d.ValueOurs
   465  
   466  	if valueTheirs < feeEach {
   467  		feeTheirs = valueTheirs
   468  		valueTheirs = 0
   469  		feeOurs = totalFee - feeTheirs
   470  		valueOurs = d.ValueOurs - feeOurs
   471  	} else {
   472  		valueTheirs -= feeTheirs
   473  	}
   474  
   475  	var buf bytes.Buffer
   476  	binary.Write(&buf, binary.BigEndian, uint64(0))
   477  	binary.Write(&buf, binary.BigEndian, uint64(0))
   478  	binary.Write(&buf, binary.BigEndian, uint64(0))
   479  	binary.Write(&buf, binary.BigEndian, d.OracleValue)
   480  	oracleSigPub, err := DlcCalcOracleSignaturePubKey(buf.Bytes(),
   481  		c.OracleA, c.OracleR)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	// Ours = the one we generate & sign. Theirs (ours = false) = the one they
   487  	// generated, so we can use their sigs
   488  	if ours {
   489  		if valueTheirs > 0 {
   490  			tx.AddTxOut(DlcOutput(c.TheirPayoutBase, oracleSigPub,
   491  				c.OurPayoutBase, valueTheirs))
   492  		}
   493  
   494  		if valueOurs > 0 {
   495  			tx.AddTxOut(wire.NewTxOut(valueOurs,
   496  				DirectWPKHScriptFromPKH(c.OurPayoutPKH)))
   497  		}
   498  	} else {
   499  		if valueOurs > 0 {
   500  			tx.AddTxOut(DlcOutput(c.OurPayoutBase, oracleSigPub,
   501  				c.TheirPayoutBase, valueOurs))
   502  		}
   503  
   504  		if valueTheirs > 0 {
   505  			tx.AddTxOut(wire.NewTxOut(valueTheirs,
   506  				DirectWPKHScriptFromPKH(c.TheirPayoutPKH)))
   507  		}
   508  	}
   509  
   510  	return tx, nil
   511  }