github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/fund.go (about) 1 package qln 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/mit-dci/lit/consts" 8 "github.com/mit-dci/lit/crypto/koblitz" 9 "github.com/mit-dci/lit/elkrem" 10 "github.com/mit-dci/lit/lnutil" 11 "github.com/mit-dci/lit/logging" 12 "github.com/mit-dci/lit/portxo" 13 "github.com/mit-dci/lit/wire" 14 ) 15 16 /* 17 right now fund makes a channel without actually building commit 18 transactions before signing and broadcasting the fund transaction. 19 Once state update push/pull messages work that will be added on to 20 this process 21 22 Note that the first elkrem exchange revokes state 0, which was never actually 23 committed to (there are no HAKDpubs for state 0; those start at state 1.) 24 So it's kindof pointless, but you still have to send the right one, because 25 elkrem 2 is the parent of elkrems 0 and 1, so that checks 0. 26 27 */ 28 29 /* 30 New funding process. 31 No fancy curve stuff. Just ask the other node for a point on a curve, which 32 will be their channel pubkey. 33 There are 2 ways to then change to a channel-proof-y method later. 34 One way is to construct the channel pubkey FROM that point, by having 35 ID:A 36 Random point:B 37 Channel point:C 38 and set C = hash(A, B)*(A + B) 39 or you could do it 4-way so that 40 C = hash(A1, B1, A2, B2)*(A + B) 41 to commit to both sides' creation process. This is a little more complex, 42 but the proof of ownership for the channel just consists of the point B so 43 it's compact. 44 45 Another way to do it, after the fact with arbitrary points. 46 ID:A, Channel pub:C 47 B = A + C 48 sign B with b. That signature is proof that someone / something knew 49 a and c at the same time. 50 51 Either of these can be added later without changing much. The messages 52 don't have to change at all, and in the first case you'd change the channel 53 pubkey calculation. In the second it's independent of the fund process. 54 55 For now though: 56 funding -- 57 A -> B point request 58 59 A channel point (33) (channel pubkey for now) 60 A refund (33) 61 62 B -> A point response 63 B replies with channel point and refund pubkey 64 65 B channel point (32) (channel pubkey for now) 66 B refund (33) 67 68 A -> B Channel Description: 69 --- 70 outpoint (36) 71 capacity (8) 72 initial push (8) 73 B's HAKD pub #1 (33) 74 signature (~70) 75 --- 76 77 add next: 78 timeout (2) 79 fee? fee can 80 (fee / timeout...? hardcoded for now) 81 82 B -> A Channel Acknowledge: 83 A's HAKD pub #1 (33) 84 signature (~70) 85 86 === time passes, fund tx gets in a block === 87 88 A -> B SigProof 89 SPV proof of the outpoint (block height, tree depth, tx index, hashes) 90 signature (~70) 91 92 93 B knows the channel is open and he got paid when he receives the sigproof. 94 A's got B's signature already. So "payment happened" is sortof the same as 95 bitcoin now; wait for confirmations. 96 97 Alternatively A can open a channel with no initial funding going to B, then 98 update the state once the channel is open. If for whatever reason you want 99 an exact timing for the payment. 100 101 */ 102 103 // FundChannel opens a channel with a peer. Doesn't return until the channel 104 // has been created. Maybe timeout if it takes too long? 105 func (nd *LitNode) FundChannel( 106 peerIdx, cointype uint32, ccap, initSend int64, data [32]byte) (uint32, error) { 107 108 _, ok := nd.SubWallet[cointype] 109 if !ok { 110 return 0, fmt.Errorf("No wallet of type %d connected", cointype) 111 } 112 113 nd.InProg.mtx.Lock() 114 // defer nd.InProg.mtx.Lock() 115 116 _, ok = nd.ConnectedCoinTypes[cointype] 117 if !ok { 118 nd.InProg.mtx.Unlock() 119 return 0, fmt.Errorf("No daemon of type %d connected. Can't fund, only receive", cointype) 120 } 121 122 fee := nd.SubWallet[cointype].Fee() * 1000 123 124 if nd.InProg.PeerIdx != 0 { 125 nd.InProg.mtx.Unlock() 126 return 0, fmt.Errorf("fund with peer %d not done yet", nd.InProg.PeerIdx) 127 } 128 129 if initSend < 0 || ccap < 0 { 130 nd.InProg.mtx.Unlock() 131 return 0, fmt.Errorf("Can't have negative send or capacity") 132 } 133 if ccap < consts.MinChanCapacity { // limit for now 134 nd.InProg.mtx.Unlock() 135 return 0, fmt.Errorf("Min channel capacity 1M sat") 136 } 137 if initSend > ccap { 138 nd.InProg.mtx.Unlock() 139 return 0, fmt.Errorf("Can't send %d in %d capacity channel", initSend, ccap) 140 } 141 142 if initSend != 0 && initSend < consts.MinOutput+fee { 143 nd.InProg.mtx.Unlock() 144 return 0, fmt.Errorf("Can't send %d as initial send because MinOutput is %d", initSend, consts.MinOutput+fee) 145 } 146 147 if ccap-initSend < consts.MinOutput+fee { 148 nd.InProg.mtx.Unlock() 149 return 0, fmt.Errorf("Can't send %d as initial send because MinOutput is %d and you would only have %d", initSend, consts.MinOutput+fee, ccap-initSend) 150 } 151 152 // TODO - would be convenient if it auto connected to the peer huh 153 if !nd.ConnectedToPeer(peerIdx) { 154 nd.InProg.mtx.Unlock() 155 return 0, fmt.Errorf("Not connected to peer %d. Do that yourself.", peerIdx) 156 } 157 158 cIdx, err := nd.NextChannelIdx() 159 if err != nil { 160 nd.InProg.mtx.Unlock() 161 return 0, err 162 } 163 164 logging.Infof("next channel idx: %d", cIdx) 165 166 nd.InProg.ChanIdx = cIdx 167 nd.InProg.PeerIdx = peerIdx 168 nd.InProg.Amt = ccap 169 nd.InProg.InitSend = initSend 170 nd.InProg.Data = data 171 172 nd.InProg.Coin = cointype 173 nd.InProg.mtx.Unlock() // switch to defer 174 175 outMsg := lnutil.NewPointReqMsg(peerIdx, cointype) 176 177 nd.tmpSendLitMsg(outMsg) 178 179 // wait until it's done! 180 idx := <-nd.InProg.done 181 182 return idx, nil 183 } 184 185 // RECIPIENT 186 // PubReqHandler gets a (content-less) pubkey request. Respond with a pubkey 187 // and a refund pubkey hash. (currently makes pubkey hash, need to only make 1) 188 // so if someone sends 10 pubkeyreqs, they'll get the same pubkey back 10 times. 189 // they have to provide an actual tx before the next pubkey will come out. 190 func (nd *LitNode) PointReqHandler(msg lnutil.PointReqMsg) { 191 192 /* shouldn't be possible to get this error... 193 if nd.RemoteCon == nil || nd.RemoteCon.RemotePub == nil { 194 logging.Errorf("Not connected to anyone\n") 195 return 196 }*/ 197 198 // pub req; check that idx matches next idx of ours and create pubkey 199 // peerArr, _ := nd.GetPubHostFromPeerIdx(msg.Peer()) 200 201 cIdx, err := nd.NextChannelIdx() 202 if err != nil { 203 logging.Errorf("PointReqHandler err %s", err.Error()) 204 return 205 } 206 207 _, ok := nd.SubWallet[msg.Cointype] 208 if !ok { 209 logging.Errorf("PointReqHandler err no wallet for type %d", msg.Cointype) 210 return 211 } 212 213 var kg portxo.KeyGen 214 kg.Depth = 5 215 kg.Step[0] = 44 | 1<<31 216 kg.Step[1] = msg.Cointype | 1<<31 217 kg.Step[2] = UseChannelFund 218 kg.Step[3] = msg.Peer() | 1<<31 219 kg.Step[4] = cIdx | 1<<31 220 221 myChanPub, _ := nd.GetUsePub(kg, UseChannelFund) 222 myRefundPub, _ := nd.GetUsePub(kg, UseChannelRefund) 223 myHAKDbase, err := nd.GetUsePub(kg, UseChannelHAKDBase) 224 if err != nil { 225 logging.Errorf("PointReqHandler err %s", err.Error()) 226 return 227 } 228 229 logging.Infof("Generated channel pubkey %x\n", myChanPub) 230 231 var keyGen portxo.KeyGen 232 keyGen.Depth = 5 233 keyGen.Step[0] = 44 | 1<<31 234 keyGen.Step[1] = msg.Cointype | 1<<31 235 keyGen.Step[2] = UseHTLCBase 236 keyGen.Step[3] = 0 | 1<<31 237 keyGen.Step[4] = cIdx | 1<<31 238 239 myNextHTLCBase, err := nd.GetUsePub(keyGen, UseHTLCBase) 240 if err != nil { 241 logging.Errorf("error generating NextHTLCBase %v", err) 242 return 243 } 244 245 keyGen.Step[3] = 1 | 1<<31 246 myN2HTLCBase, err := nd.GetUsePub(keyGen, UseHTLCBase) 247 if err != nil { 248 logging.Errorf("error generating N2HTLCBase %v", err) 249 return 250 } 251 252 outMsg := lnutil.NewPointRespMsg(msg.Peer(), myChanPub, myRefundPub, myHAKDbase, 253 myNextHTLCBase, myN2HTLCBase) 254 nd.tmpSendLitMsg(outMsg) 255 256 return 257 } 258 259 // FUNDER 260 // PointRespHandler takes in a point response, and returns a channel description 261 func (nd *LitNode) PointRespHandler(msg lnutil.PointRespMsg) error { 262 var err error 263 logging.Infof("Got PointResponse") 264 265 nd.InProg.mtx.Lock() 266 defer nd.InProg.mtx.Unlock() 267 268 if nd.InProg.PeerIdx == 0 { 269 return fmt.Errorf("Got point response but no channel creation in progress") 270 } 271 272 if nd.InProg.PeerIdx != msg.Peer() { 273 return fmt.Errorf( 274 "making channel with peer %d but got PointResp from %d", 275 nd.InProg.PeerIdx, msg.Peer()) 276 } 277 278 if nd.SubWallet[nd.InProg.Coin] == nil { 279 return fmt.Errorf("Not connected to coin type %d\n", nd.InProg.Coin) 280 } 281 282 // make channel (not in db) just for keys / elk 283 q := new(Qchan) 284 285 q.Height = -1 286 287 q.Value = nd.InProg.Amt 288 289 q.KeyGen.Depth = 5 290 q.KeyGen.Step[0] = 44 | 1<<31 291 q.KeyGen.Step[1] = nd.InProg.Coin | 1<<31 292 q.KeyGen.Step[2] = UseChannelFund 293 q.KeyGen.Step[3] = nd.InProg.PeerIdx | 1<<31 294 q.KeyGen.Step[4] = nd.InProg.ChanIdx | 1<<31 295 296 q.MyPub, _ = nd.GetUsePub(q.KeyGen, UseChannelFund) 297 q.MyRefundPub, _ = nd.GetUsePub(q.KeyGen, UseChannelRefund) 298 q.MyHAKDBase, _ = nd.GetUsePub(q.KeyGen, UseChannelHAKDBase) 299 q.ElkRcv = elkrem.NewElkremReceiver() 300 301 // chop up incoming message, save points to channel struct 302 copy(q.TheirPub[:], msg.ChannelPub[:]) 303 copy(q.TheirRefundPub[:], msg.RefundPub[:]) 304 copy(q.TheirHAKDBase[:], msg.HAKDbase[:]) 305 306 // make sure their pubkeys are real pubkeys 307 _, err = koblitz.ParsePubKey(q.TheirPub[:], koblitz.S256()) 308 if err != nil { 309 return fmt.Errorf("PubRespHandler TheirPub err %s", err.Error()) 310 } 311 _, err = koblitz.ParsePubKey(q.TheirRefundPub[:], koblitz.S256()) 312 if err != nil { 313 return fmt.Errorf("PubRespHandler TheirRefundPub err %s", err.Error()) 314 } 315 _, err = koblitz.ParsePubKey(q.TheirHAKDBase[:], koblitz.S256()) 316 if err != nil { 317 return fmt.Errorf("PubRespHandler TheirHAKDBase err %s", err.Error()) 318 } 319 320 // derive elkrem sender root from HD keychain 321 elkRoot, _ := nd.GetElkremRoot(q.KeyGen) 322 q.ElkSnd = elkrem.NewElkremSender(elkRoot) 323 324 // set the time 325 q.LastUpdate = uint64(time.Now().UnixNano() / 1000) 326 327 // get txo for channel 328 txo, err := lnutil.FundTxOut(q.MyPub, q.TheirPub, nd.InProg.Amt) 329 if err != nil { 330 return err 331 } 332 333 // call MaybeSend, freezing inputs and learning the txid of the channel 334 // here, we require only witness inputs 335 outPoints, err := nd.SubWallet[q.Coin()].MaybeSend([]*wire.TxOut{txo}, true) 336 if err != nil { 337 return err 338 } 339 340 // should only have 1 txout index from MaybeSend, which we use 341 if len(outPoints) != 1 { 342 return fmt.Errorf("got %d OPs from MaybeSend (expect 1)", len(outPoints)) 343 } 344 345 // save fund outpoint to inProg 346 nd.InProg.op = outPoints[0] 347 // also set outpoint in channel 348 q.Op = *nd.InProg.op 349 350 // create initial state for elkrem points 351 q.State = new(StatCom) 352 q.State.StateIdx = 0 353 q.State.MyAmt = nd.InProg.Amt - nd.InProg.InitSend 354 // get fee from sub wallet. Later should make fee per channel and update state 355 // based on size 356 q.State.Fee = nd.SubWallet[q.Coin()].Fee() * consts.QcStateFee 357 358 q.State.Data = nd.InProg.Data 359 360 _, err = koblitz.ParsePubKey(msg.NextHTLCBase[:], koblitz.S256()) 361 if err != nil { 362 return fmt.Errorf("PubRespHandler NextHTLCBase err %s", err.Error()) 363 } 364 _, err = koblitz.ParsePubKey(msg.N2HTLCBase[:], koblitz.S256()) 365 if err != nil { 366 return fmt.Errorf("PubRespHandler N2HTLCBase err %s", err.Error()) 367 } 368 369 var keyGen portxo.KeyGen 370 keyGen.Depth = 5 371 keyGen.Step[0] = 44 | 1<<31 372 keyGen.Step[1] = nd.InProg.Coin | 1<<31 373 keyGen.Step[2] = UseHTLCBase 374 keyGen.Step[3] = 0 | 1<<31 375 keyGen.Step[4] = nd.InProg.ChanIdx | 1<<31 376 377 q.State.MyNextHTLCBase, err = nd.GetUsePub(keyGen, UseHTLCBase) 378 if err != nil { 379 return fmt.Errorf("error generating NextHTLCBase %v", err) 380 } 381 382 keyGen.Step[3] = 1 | 1<<31 383 q.State.MyN2HTLCBase, err = nd.GetUsePub(keyGen, UseHTLCBase) 384 if err != nil { 385 return fmt.Errorf("error generating N2HTLCBase %v", err) 386 } 387 388 q.State.NextHTLCBase = msg.NextHTLCBase 389 q.State.N2HTLCBase = msg.N2HTLCBase 390 391 // save channel to db 392 err = nd.SaveQChan(q) 393 if err != nil { 394 nd.FailChannel(q) 395 return fmt.Errorf("PointRespHandler SaveQchanState err %s", err.Error()) 396 } 397 398 // when funding a channel, give them the first *3* elkpoints. 399 elkPointZero, err := q.ElkPoint(false, 0) 400 if err != nil { 401 nd.FailChannel(q) 402 return err 403 } 404 elkPointOne, err := q.ElkPoint(false, 1) 405 if err != nil { 406 nd.FailChannel(q) 407 return err 408 } 409 410 elkPointTwo, err := q.N2ElkPointForThem() 411 if err != nil { 412 nd.FailChannel(q) 413 return err 414 } 415 416 // description is outpoint (36), mypub(33), myrefund(33), 417 // myHAKDbase(33), capacity (8), 418 // initial payment (8), ElkPoint0,1,2 (99) 419 420 outMsg := lnutil.NewChanDescMsg( 421 msg.Peer(), *nd.InProg.op, q.MyPub, q.MyRefundPub, q.MyHAKDBase, 422 q.State.MyNextHTLCBase, q.State.MyN2HTLCBase, 423 nd.InProg.Coin, nd.InProg.Amt, nd.InProg.InitSend, 424 elkPointZero, elkPointOne, elkPointTwo, nd.InProg.Data) 425 426 nd.tmpSendLitMsg(outMsg) 427 428 return nil 429 } 430 431 // RECIPIENT 432 // QChanDescHandler takes in a description of a channel output. It then 433 // saves it to the local db, and returns a channel acknowledgement 434 func (nd *LitNode) QChanDescHandler(msg lnutil.ChanDescMsg) error { 435 436 wal, ok := nd.SubWallet[msg.CoinType] 437 if !ok { 438 return fmt.Errorf("QChanDescHandler err no wallet for type %d", msg.CoinType) 439 } 440 441 // deserialize desc 442 op := msg.Outpoint 443 opArr := lnutil.OutPointToBytes(op) 444 amt := msg.Capacity 445 446 cIdx, err := nd.NextChannelIdx() 447 if err != nil { 448 return fmt.Errorf("QChanDescHandler err %s", err.Error()) 449 } 450 451 qc := new(Qchan) 452 453 qc.Height = -1 454 qc.KeyGen.Depth = 5 455 qc.KeyGen.Step[0] = 44 | 1<<31 456 qc.KeyGen.Step[1] = msg.CoinType | 1<<31 457 qc.KeyGen.Step[2] = UseChannelFund 458 qc.KeyGen.Step[3] = msg.Peer() | 1<<31 459 qc.KeyGen.Step[4] = cIdx | 1<<31 460 qc.Value = amt 461 qc.Mode = portxo.TxoP2WSHComp 462 qc.Op = op 463 464 qc.TheirPub = msg.PubKey 465 qc.TheirRefundPub = msg.RefundPub 466 qc.TheirHAKDBase = msg.HAKDbase 467 qc.MyPub, _ = nd.GetUsePub(qc.KeyGen, UseChannelFund) 468 qc.MyRefundPub, _ = nd.GetUsePub(qc.KeyGen, UseChannelRefund) 469 qc.MyHAKDBase, _ = nd.GetUsePub(qc.KeyGen, UseChannelHAKDBase) 470 471 // it should go into the next bucket and get the right key index. 472 // but we can't actually check that. 473 // qc, err := nd.SaveFundTx( 474 // op, amt, peerArr, theirPub, theirRefundPub, theirHAKDbase) 475 // if err != nil { 476 // logging.Errorf("QChanDescHandler SaveFundTx err %s", err.Error()) 477 // return 478 // } 479 logging.Infof("got multisig output %s amt %d\n", op.String(), amt) 480 481 // create initial state 482 qc.State = new(StatCom) 483 // similar to SIGREV in pushpull 484 485 // TODO assumes both parties use same fee 486 qc.State.Fee = wal.Fee() * consts.QcStateFee 487 qc.State.MyAmt = msg.InitPayment 488 489 qc.State.Data = msg.Data 490 491 qc.State.StateIdx = 0 492 // use new ElkPoint for signing 493 qc.State.ElkPoint = msg.ElkZero 494 qc.State.NextElkPoint = msg.ElkOne 495 qc.State.N2ElkPoint = msg.ElkTwo 496 497 _, err = koblitz.ParsePubKey(msg.NextHTLCBase[:], koblitz.S256()) 498 if err != nil { 499 return fmt.Errorf("QChanDescHandler NextHTLCBase err %s", err.Error()) 500 } 501 _, err = koblitz.ParsePubKey(msg.N2HTLCBase[:], koblitz.S256()) 502 if err != nil { 503 return fmt.Errorf("QChanDescHandler N2HTLCBase err %s", err.Error()) 504 } 505 506 var keyGen portxo.KeyGen 507 keyGen.Depth = 5 508 keyGen.Step[0] = 44 | 1<<31 509 keyGen.Step[1] = msg.CoinType | 1<<31 510 keyGen.Step[2] = UseHTLCBase 511 keyGen.Step[3] = 0 | 1<<31 512 keyGen.Step[4] = cIdx | 1<<31 513 514 qc.State.MyNextHTLCBase, err = nd.GetUsePub(keyGen, UseHTLCBase) 515 if err != nil { 516 return fmt.Errorf("error generating NextHTLCBase %v", err) 517 } 518 519 keyGen.Step[3] = 1 | 1<<31 520 qc.State.MyN2HTLCBase, err = nd.GetUsePub(keyGen, UseHTLCBase) 521 if err != nil { 522 return fmt.Errorf("error generating N2HTLCBase %v", err) 523 } 524 525 qc.State.NextHTLCBase = msg.NextHTLCBase 526 qc.State.N2HTLCBase = msg.N2HTLCBase 527 528 // save new channel to db 529 err = nd.SaveQChan(qc) 530 if err != nil { 531 nd.FailChannel(qc) 532 logging.Errorf("QChanDescHandler err %s", err.Error()) 533 return err 534 } 535 536 // load ... the thing I just saved. why? 537 qc, err = nd.GetQchan(opArr) 538 if err != nil { 539 nd.FailChannel(qc) 540 logging.Errorf("QChanDescHandler GetQchan err %s", err.Error()) 541 return err 542 } 543 544 // when funding a channel, give them the first *2* elkpoints. 545 theirElkPointZero, err := qc.ElkPoint(false, 0) 546 if err != nil { 547 nd.FailChannel(qc) 548 logging.Errorf("QChanDescHandler err %s", err.Error()) 549 return err 550 } 551 theirElkPointOne, err := qc.ElkPoint(false, 1) 552 if err != nil { 553 nd.FailChannel(qc) 554 logging.Errorf("QChanDescHandler err %s", err.Error()) 555 return err 556 } 557 558 theirElkPointTwo, err := qc.N2ElkPointForThem() 559 if err != nil { 560 nd.FailChannel(qc) 561 logging.Errorf("QChanDescHandler err %s", err.Error()) 562 return err 563 } 564 565 sig, _, err := nd.SignState(qc) 566 if err != nil { 567 nd.FailChannel(qc) 568 logging.Errorf("QChanDescHandler SignState err %s", err.Error()) 569 return err 570 } 571 572 outMsg := lnutil.NewChanAckMsg( 573 msg.Peer(), op, 574 theirElkPointZero, theirElkPointOne, theirElkPointTwo, 575 sig) 576 outMsg.Bytes() 577 578 nd.tmpSendLitMsg(outMsg) 579 580 return nil 581 } 582 583 // FUNDER 584 // QChanAckHandler takes in an acknowledgement multisig description. 585 // when a multisig outpoint is ackd, that causes the funder to sign and broadcast. 586 func (nd *LitNode) QChanAckHandler(msg lnutil.ChanAckMsg, peer *RemotePeer) { 587 opArr := lnutil.OutPointToBytes(msg.Outpoint) 588 sig := msg.Signature 589 590 // load channel to save their refund address 591 qc, err := nd.GetQchan(opArr) 592 if err != nil { 593 nd.FailChannel(qc) 594 logging.Errorf("QChanAckHandler GetQchan err %s", err.Error()) 595 return 596 } 597 598 // err = qc.IngestElkrem(revElk) 599 // if err != nil { // this can't happen because it's the first elk... remove? 600 // logging.Errorf("QChanAckHandler IngestElkrem err %s", err.Error()) 601 // return 602 // } 603 qc.State.ElkPoint = msg.ElkZero 604 qc.State.NextElkPoint = msg.ElkOne 605 qc.State.N2ElkPoint = msg.ElkTwo 606 607 err = qc.VerifySigs(sig, nil) 608 if err != nil { 609 nd.FailChannel(qc) 610 logging.Errorf("QChanAckHandler VerifySig err %s", err.Error()) 611 return 612 } 613 614 // verify worked; Save state 1 to DB 615 err = nd.SaveQchanState(qc) 616 if err != nil { 617 nd.FailChannel(qc) 618 logging.Errorf("QChanAckHandler SaveQchanState err %s", err.Error()) 619 return 620 } 621 622 // Make sure everything works & is saved, then clear InProg. 623 624 // sign their com tx to send 625 sig, _, err = nd.SignState(qc) 626 if err != nil { 627 nd.FailChannel(qc) 628 logging.Errorf("QChanAckHandler SignState err %s", err.Error()) 629 return 630 } 631 632 // OK to fund. 633 err = nd.SubWallet[qc.Coin()].ReallySend(&qc.Op.Hash) 634 if err != nil { 635 nd.FailChannel(qc) 636 logging.Errorf("QChanAckHandler ReallySend err %s", err.Error()) 637 return 638 } 639 640 err = nd.SubWallet[qc.Coin()].WatchThis(qc.Op) 641 if err != nil { 642 nd.FailChannel(qc) 643 logging.Errorf("QChanAckHandler WatchThis err %s", err.Error()) 644 return 645 } 646 647 // tell base wallet about watcher refund address in case that happens 648 // TODO this is weird & ugly... maybe have an export keypath func? 649 nullTxo := new(portxo.PorTxo) 650 nullTxo.Value = 0 // redundant, but explicitly show that this is just for adr 651 nullTxo.KeyGen = qc.KeyGen 652 nullTxo.KeyGen.Step[2] = UseChannelWatchRefund 653 nd.SubWallet[qc.Coin()].ExportUtxo(nullTxo) 654 655 // channel creation is ~complete, clear InProg. 656 // We may be asked to re-send the sig-proof 657 658 nd.InProg.mtx.Lock() 659 nd.InProg.done <- qc.KeyGen.Step[4] & 0x7fffffff 660 nd.InProg.Clear() 661 nd.InProg.mtx.Unlock() 662 663 peer.QCs[qc.Idx()] = qc 664 peer.OpMap[opArr] = qc.Idx() 665 666 // sig proof should be sent later once there are confirmations. 667 // it'll have an spv proof of the fund tx. 668 // but for now just send the sig. 669 670 outMsg := lnutil.NewSigProofMsg(msg.Peer(), msg.Outpoint, sig) 671 672 nd.tmpSendLitMsg(outMsg) 673 674 return 675 } 676 677 // RECIPIENT 678 // SigProofHandler saves the signature the recipient stores. 679 // In some cases you don't need this message. 680 func (nd *LitNode) SigProofHandler(msg lnutil.SigProofMsg, peer *RemotePeer) { 681 682 op := msg.Outpoint 683 opArr := lnutil.OutPointToBytes(op) 684 685 qc, err := nd.GetQchan(opArr) 686 if err != nil { 687 nd.FailChannel(qc) 688 logging.Errorf("SigProofHandler err %s", err.Error()) 689 return 690 } 691 692 wal, ok := nd.SubWallet[qc.Coin()] 693 if !ok { 694 nd.FailChannel(qc) 695 logging.Errorf("Not connected to coin type %d\n", qc.Coin()) 696 return 697 } 698 699 err = qc.VerifySigs(msg.Signature, nil) 700 if err != nil { 701 nd.FailChannel(qc) 702 logging.Errorf("SigProofHandler err %s", err.Error()) 703 return 704 } 705 706 // sig OK, save 707 err = nd.SaveQchanState(qc) 708 if err != nil { 709 nd.FailChannel(qc) 710 logging.Errorf("SigProofHandler err %s", err.Error()) 711 return 712 } 713 714 err = wal.WatchThis(op) 715 716 if err != nil { 717 nd.FailChannel(qc) 718 logging.Errorf("SigProofHandler err %s", err.Error()) 719 return 720 } 721 722 // tell base wallet about watcher refund address in case that happens 723 nullTxo := new(portxo.PorTxo) 724 nullTxo.Value = 0 // redundant, but explicitly show that this is just for adr 725 nullTxo.KeyGen = qc.KeyGen 726 nullTxo.KeyGen.Step[2] = UseChannelWatchRefund 727 wal.ExportUtxo(nullTxo) 728 729 peer.QCs[qc.Idx()] = qc 730 peer.OpMap[opArr] = qc.Idx() 731 732 // sig OK; in terms of UI here's where you can say "payment received" 733 // "channel online" etc 734 735 peerIdx := qc.Peer() 736 existingPeer := nd.PeerMan.GetPeerByIdx(int32(peerIdx)) 737 738 sigProofEvent := ChannelStateUpdateEvent{ 739 Action: "sigproof", 740 ChanIdx: qc.Idx(), 741 State: qc.State, 742 TheirPub: existingPeer.GetPubkey(), 743 CoinType: qc.Coin(), 744 } 745 746 if succeed, err := nd.Events.Publish(sigProofEvent); err != nil { 747 logging.Errorf("SigProofHandler publish err %s", err) 748 return 749 } else if !succeed { 750 logging.Errorf("SigProofHandler publish did not succeed") 751 return 752 } 753 754 return 755 }