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 }