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

     1  package qln
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sync"
     7  
     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  
    13  	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
    14  	"github.com/mit-dci/lit/crypto/koblitz"
    15  )
    16  
    17  // Uhh, quick channel.  For now.  Once you get greater spire it upgrades to
    18  // a full channel that can do everything.
    19  type Qchan struct {
    20  	// S for stored (on disk), D for derived
    21  
    22  	portxo.PorTxo            // S underlying utxo data
    23  	CloseData     QCloseData // S closing outpoint
    24  
    25  	MyPub    [33]byte // D my channel specific pubkey
    26  	TheirPub [33]byte // S their channel specific pubkey
    27  
    28  	// Refunds are also elkremified
    29  	MyRefundPub    [33]byte // D my refund pubkey for channel break
    30  	TheirRefundPub [33]byte // S their pubkey for channel break
    31  
    32  	MyHAKDBase    [33]byte // D my base point for HAKD and timeout keys
    33  	TheirHAKDBase [33]byte // S their base point for HAKD and timeout keys
    34  	// PKH for penalty tx.  Derived
    35  	WatchRefundAdr [20]byte
    36  
    37  	// Elkrem is used for revoking state commitments
    38  	ElkSnd *elkrem.ElkremSender   // D derived from channel specific key
    39  	ElkRcv *elkrem.ElkremReceiver // S stored in db
    40  
    41  	Delay uint16 // blocks for timeout (default 5 for testing)
    42  
    43  	State *StatCom // S current state of channel
    44  
    45  	ClearToSend chan bool // send a true here when you get a rev
    46  	ChanMtx     sync.Mutex
    47  	// exists only in ram, doesn't touch disk
    48  
    49  	LastUpdate uint64 // unix timestamp of last update (milliseconds)
    50  
    51  }
    52  
    53  // 4 + 1 + 8 + 32 + 4 + 33 + 33 + 1 + 5 + 32 + 64 = 217 bytes
    54  type HTLC struct {
    55  	Idx uint32 `json:"idx"`
    56  
    57  	Incoming bool     `json:"incoming"`
    58  	Amt      int64    `json:"amt"`
    59  	RHash    [32]byte `json:"hash"`
    60  	Locktime uint32   `json:"locktime"`
    61  
    62  	MyHTLCBase    [33]byte `json:"mhtlcbase"`
    63  	TheirHTLCBase [33]byte `json:"rhtlcbase"`
    64  
    65  	KeyGen portxo.KeyGen `json:"keygen"`
    66  
    67  	Sig [64]byte `json:"sig"`
    68  
    69  	R              [16]byte `json:"preimage"`
    70  	Clearing       bool     `json:"clearing"`
    71  	Cleared        bool     `json:"cleared"`
    72  	ClearedOnChain bool     `json:"clearedoc"` // To keep track of what HTLCs we claimed on-chain
    73  }
    74  
    75  // StatComs are State Commitments.
    76  // all elements are saved to the db.
    77  type StatCom struct {
    78  	StateIdx uint64 `json:"idx"` // this is the n'th state commitment
    79  
    80  	WatchUpTo uint64 `json:"watchupto"` // have sent out to watchtowers up to this state  ( < stateidx)
    81  
    82  	MyAmt int64 `json:"amt"` // my channel allocation
    83  
    84  	Fee int64 `json:"fee"` // symmetric fee in absolute satoshis
    85  
    86  	Data [32]byte `json:"miscdata"`
    87  
    88  	// their Amt is the utxo.Value minus this
    89  	Delta int32 `json:"delta"` // fund amount in-transit; is negative for the pusher
    90  	// Delta for when the channel is in a collision state which needs to be resolved
    91  	Collision int32 `json:"collision"`
    92  
    93  	// Elkrem point from counterparty, used to make
    94  	// Homomorphic Adversarial Key Derivation public keys (HAKD)
    95  	ElkPoint     [33]byte `json:"elkp0"` // saved to disk, current revealable point
    96  	NextElkPoint [33]byte `json:"elkp1"` // Point stored for next state
    97  	N2ElkPoint   [33]byte `json:"elkp2"` // Point for state after next (in case of collision)
    98  
    99  	Sig [64]byte `json:"sig"` // Counterparty's signature for current state
   100  	// don't write to sig directly; only overwrite via fn() call
   101  
   102  	// note sig can be nil during channel creation. if stateIdx isn't 0,
   103  	// sig should have a sig.
   104  	// only one sig is ever stored, to prevent broadcasting the wrong tx.
   105  	// could add a mutex here... maybe will later.
   106  
   107  	HTLCIdx       uint32 `json:"htlcidx"`
   108  	InProgHTLC    *HTLC  `json:"iphtlc"`   // Current in progress HTLC
   109  	CollidingHTLC *HTLC  `json:"collhtlc"` // HTLC for when the channel is colliding
   110  
   111  	CollidingHashDelta     bool `json:"colhd"` // True when colliding between a DeltaSig and HashSig/PreImageSig
   112  	CollidingHashPreimage  bool `json:"colhp"` // True when colliding between HashSig and PreimageSig
   113  	CollidingPreimages     bool `json:"colpp"` // True when colliding between PreimageSig and PreimageSig
   114  	CollidingPreimageDelta bool `json:"colpd"` // True when colliding between a DeltaSig and HashSig/PreImageSig
   115  
   116  	// Analogous to the ElkPoints above but used for generating their pubkey for the HTLC
   117  	NextHTLCBase [33]byte `json:"rnexthtlcbase"`
   118  	N2HTLCBase   [33]byte `json:"rnexthtlcbase2"`
   119  
   120  	MyNextHTLCBase [33]byte `json:"mnexthtlcbase"`
   121  	MyN2HTLCBase   [33]byte `json:"mnexthtlcbase2"`
   122  
   123  	// Any HTLCs associated with this channel state (can be nil)
   124  	HTLCs []HTLC `json:"htlcs"`
   125  
   126  	Failed bool `json:"failed"` // S there was a fatal error with the channel
   127  	// meaning it cannot be used safely
   128  }
   129  
   130  // QCloseData is the output resulting from an un-cooperative close
   131  // of the channel.  This happens when either party breaks non-cooperatively.
   132  // It describes "your" output, either pkh or time-delay script.
   133  // If you have pkh but can grab the other output, "grabbable" is set to true.
   134  // This can be serialized in a separate bucket
   135  
   136  type QCloseData struct {
   137  	// 3 txid / height pairs are stored.  All 3 only are used in the
   138  	// case where you grab their invalid close.
   139  	CloseTxid   chainhash.Hash `json:"txid"`
   140  	CloseHeight int32          `json:"height"`
   141  	Closed      bool           `json:"closed"` // if channel is closed; if CloseTxid != -1
   142  }
   143  
   144  // ChannelInfo prints info about a channel.
   145  func (nd *LitNode) QchanInfo(q *Qchan) error {
   146  	// display txid instead of outpoint because easier to copy/paste
   147  	logging.Infof("CHANNEL %s h:%d %s cap: %d\n",
   148  		q.Op.String(), q.Height, q.KeyGen.String(), q.Value)
   149  	logging.Infof("\tPUB mine:%x them:%x REFBASE mine:%x them:%x BASE mine:%x them:%x\n",
   150  		q.MyPub[:4], q.TheirPub[:4], q.MyRefundPub[:4], q.TheirRefundPub[:4],
   151  		q.MyHAKDBase[:4], q.TheirHAKDBase[:4])
   152  	if q.State == nil || q.ElkRcv == nil {
   153  		logging.Errorf("\t no valid state or elkrem\n")
   154  	} else {
   155  		logging.Infof("\ta %d (them %d) state index %d\n",
   156  			q.State.MyAmt, q.Value-q.State.MyAmt, q.State.StateIdx)
   157  
   158  		logging.Infof("\tdelta:%d HAKD:%x elk@ %d\n",
   159  			q.State.Delta, q.State.ElkPoint[:4], q.ElkRcv.UpTo())
   160  		elkp, _ := q.ElkPoint(false, q.State.StateIdx)
   161  		myRefPub := lnutil.AddPubsEZ(q.MyRefundPub, elkp)
   162  		theirRefPub := lnutil.AddPubsEZ(q.TheirRefundPub, q.State.ElkPoint)
   163  		logging.Infof("\tMy Refund: %x Their Refund %x\n", myRefPub[:4], theirRefPub[:4])
   164  	}
   165  
   166  	if !q.CloseData.Closed { // still open, finish here
   167  		return nil
   168  	}
   169  
   170  	logging.Infof("\tCLOSED at height %d by tx: %s\n",
   171  		q.CloseData.CloseHeight, q.CloseData.CloseTxid.String())
   172  	//	clTx, err := t.GetTx(&q.CloseData.CloseTxid)
   173  	//	if err != nil {
   174  	//		return err
   175  	//	}
   176  	//	ctxos, err := q.GetCloseTxos(clTx)
   177  	//	if err != nil {
   178  	//		return err
   179  	//	}
   180  
   181  	//	if len(ctxos) == 0 {
   182  	//		logging.Infof("\tcooperative close.\n")
   183  	//		return nil
   184  	//	}
   185  
   186  	//	logging.Infof("\tClose resulted in %d spendable txos\n", len(ctxos))
   187  	//	if len(ctxos) == 2 {
   188  	//		logging.Infof("\t\tINVALID CLOSE!!!11\n")
   189  	//	}
   190  	//	for i, u := range ctxos {
   191  	//		logging.Infof("\t\t%d) amt: %d spendable: %d\n", i, u.Value, u.Seq)
   192  	//	}
   193  	return nil
   194  }
   195  
   196  // Peer returns the local peer index of the channel
   197  func (q *Qchan) Peer() uint32 {
   198  	if q == nil {
   199  		return 0
   200  	}
   201  	return q.KeyGen.Step[3] & 0x7fffffff
   202  }
   203  
   204  // Idx returns the local index of the channel
   205  func (q *Qchan) Idx() uint32 {
   206  	if q == nil {
   207  		return 0
   208  	}
   209  	return q.KeyGen.Step[4] & 0x7fffffff
   210  }
   211  
   212  // Coin returns the coin type of the channel
   213  func (q *Qchan) Coin() uint32 {
   214  	if q == nil {
   215  		return 0
   216  	}
   217  	return q.KeyGen.Step[1] & 0x7fffffff
   218  }
   219  
   220  // ImFirst decides who goes first when it's unclear.  Smaller pubkey goes first.
   221  func (q *Qchan) ImFirst() bool {
   222  	return bytes.Compare(q.MyRefundPub[:], q.TheirRefundPub[:]) == -1
   223  }
   224  
   225  // GetChanHint gives the 6 byte hint mask of the channel.  It's derived from the
   226  // hash of the PKH output pubkeys.  "Mine" means the hint is in the tx I store.
   227  // So it's actually a hint for them to decode... which is confusing, but consistent
   228  // with the "mine" bool for transactions, so "my" tx has "my" hint.
   229  // (1<<48 - 1 is the max valid value)
   230  func (q *Qchan) GetChanHint(mine bool) uint64 {
   231  	// could cache these in memory for a slight speedup
   232  	var h []byte
   233  	if mine {
   234  		h = chainhash.DoubleHashB(append(q.MyRefundPub[:], q.TheirRefundPub[:]...))
   235  	} else {
   236  		h = chainhash.DoubleHashB(append(q.TheirRefundPub[:], q.MyRefundPub[:]...))
   237  	}
   238  
   239  	if len(h) != 32 {
   240  		return 1 << 63
   241  	}
   242  	// get 6 bytes from that hash (leave top 2 bytes of return value empty)
   243  	x := make([]byte, 8)
   244  
   245  	copy(x[2:8], h[2:8])
   246  
   247  	return lnutil.BtU64(x)
   248  }
   249  
   250  // GetDHSecret gets a per-channel shared secret from the Diffie-Helman of the
   251  // two pubkeys in the fund tx.
   252  func (nd *LitNode) GetDHSecret(q *Qchan) ([]byte, error) {
   253  	if nd.SubWallet[q.Coin()] == nil {
   254  		return nil, fmt.Errorf("Not connected to coin type %d\n", q.Coin())
   255  	}
   256  	if nd == nil || q == nil {
   257  		return nil, fmt.Errorf("GetDHPoint: nil node or channel")
   258  	}
   259  
   260  	theirPub, err := koblitz.ParsePubKey(q.TheirPub[:], koblitz.S256())
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	priv, err := nd.SubWallet[q.Coin()].GetPriv(q.KeyGen)
   265  	// if this breaks, return
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  
   270  	return koblitz.GenerateSharedSecret(priv, theirPub), nil
   271  }
   272  
   273  // GetChannelBalances returns myAmt and theirAmt in the channel
   274  // that aren't locked up in HTLCs in satoshis
   275  func (q *Qchan) GetChannelBalances() (int64, int64) {
   276  	value := q.Value
   277  
   278  	for _, h := range q.State.HTLCs {
   279  		if !h.Cleared {
   280  			value -= h.Amt
   281  		}
   282  	}
   283  
   284  	myAmt := q.State.MyAmt
   285  	theirAmt := value - myAmt
   286  
   287  	return myAmt, theirAmt
   288  }