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  }