github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/elkpoints.go (about) 1 package qln 2 3 import ( 4 "fmt" 5 6 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 7 "github.com/mit-dci/lit/lnutil" 8 "github.com/mit-dci/lit/logging" 9 ) 10 11 /* 12 functions dealing with elkrem points and the elkrem structure 13 */ 14 15 // CurElkPointForThem makes the current state elkrem point to send out 16 func (q *Qchan) N2ElkPointForThem() (p [33]byte, err error) { 17 // generate revocable elkrem point 18 return q.ElkPoint(false, q.State.StateIdx+2) 19 } 20 21 // ElkPoint generates an elkrem Point. "My" elkrem point is the point 22 // I receive from the counter party, and can create after the state has 23 // been revoked. "Their" elkrem point (mine=false) is generated from my elkrem 24 // sender at any index. 25 // Elkrem points pubkeys where the scalar is a hash coming from the elkrem tree. 26 // With delinearized aggregation, only one point is needed. I'm pretty sure. 27 func (q *Qchan) ElkPoint(mine bool, idx uint64) (p [33]byte, err error) { 28 // sanity check 29 if q == nil || q.ElkSnd == nil { // no sender 30 err = fmt.Errorf("can't access elkrem sender") 31 return 32 } 33 if mine && q.ElkRcv == nil { // no receiver 34 err = fmt.Errorf("can't access elkrem receiver") 35 return 36 } 37 38 elk := new(chainhash.Hash) 39 40 if mine { // make mine based on receiver 41 elk, err = q.ElkRcv.AtIndex(idx) 42 } else { // make theirs based on sender 43 elk, err = q.ElkSnd.AtIndex(idx) 44 } 45 // elkrem problem, error out here 46 if err != nil { 47 return 48 } 49 p = lnutil.ElkPointFromHash(elk) 50 51 return 52 } 53 54 func (q *Qchan) AdvanceElkrem(elk *chainhash.Hash, n2Elk [33]byte) error { 55 // check that the revocation works 56 err := q.IngestElkrem(elk) 57 if err != nil { 58 return err 59 } 60 // if ingest was successful, update the stored elk points 61 // the already stored "next" point becomes the current point 62 q.State.ElkPoint = q.State.NextElkPoint 63 // n+2 drops to n+1 64 q.State.NextElkPoint = q.State.N2ElkPoint 65 // and the newly received point is n+2 66 q.State.N2ElkPoint = n2Elk 67 return nil 68 } 69 70 // IngestElkrem takes in an elkrem hash, performing 2 checks: 71 // that it produces the proper elk point, and that it fits into the elkrem tree. 72 // If either of these checks fail, and definitely the second one 73 // fails, I'm pretty sure the channel is not recoverable and needs to be closed. 74 func (q *Qchan) IngestElkrem(elk *chainhash.Hash) error { 75 if elk == nil { 76 return fmt.Errorf("IngestElkrem: nil hash") 77 } 78 79 // first verify the elkrem insertion (this only performs checks 1/2 the time, so 80 // 1/2 the time it'll work even if the elkrem is invalid, oh well) 81 err := q.ElkRcv.AddNext(elk) 82 if err != nil { 83 return err 84 } 85 logging.Infof("ingested hash, receiver now has up to %d\n", q.ElkRcv.UpTo()) 86 87 // if this is state 0, then we have elkrem 0 and we can stop here. 88 // there's nothing to revoke. 89 if q.State.StateIdx == 0 { 90 return nil 91 } 92 93 // next verify if the elkrem produces the previous elk point. 94 // We don't actually use the private key operation here, because we can 95 // do the same operation on our pubkey that they did, and we have faith 96 // in the mysterious power of abelian group homomorphisms that the private 97 // key modification will also work. 98 99 // Make point from received elk hash 100 point := lnutil.ElkPointFromHash(elk) 101 102 // see if it matches previous elk point 103 if point != q.State.ElkPoint { 104 logging.Errorf("elk1: %x\nelk2: %x\nelk3: %x\nngst: %x\n", 105 q.State.ElkPoint, q.State.NextElkPoint, q.State.N2ElkPoint, point) 106 // didn't match, the whole channel is borked. 107 return fmt.Errorf("hash %x (index %d) fits tree but creates wrong elkpoint!", 108 elk[:8], q.State.StateIdx) 109 } 110 111 return nil 112 }