github.com/decred/dcrlnd@v0.7.6/lnwallet/sigpool.go (about)

     1  package lnwallet
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     8  	"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
     9  	"github.com/decred/dcrd/wire"
    10  	"github.com/decred/dcrlnd/input"
    11  	"github.com/decred/dcrlnd/lnwire"
    12  )
    13  
    14  const (
    15  	// jobBuffer is a constant the represents the buffer of jobs in the two
    16  	// main queues. This allows clients avoid necessarily blocking when
    17  	// submitting jobs into the queue.
    18  	jobBuffer = 100
    19  
    20  	// TODO(roasbeef): job buffer pool?
    21  )
    22  
    23  // VerifyJob is a job sent to the sigPool sig pool to verify a signature
    24  // on a transaction. The items contained in the struct are necessary and
    25  // sufficient to verify the full signature. The passed sigHash closure function
    26  // should be set to a function that generates the relevant sighash.
    27  //
    28  // TODO(roasbeef): when we move to ecschnorr, make into batch signature
    29  // verification using bos-coster (or pip?).
    30  type VerifyJob struct {
    31  	// PubKey is the public key that was used to generate the purported
    32  	// valid signature. Note that with the current channel construction,
    33  	// this public key will likely have been tweaked using the current per
    34  	// commitment point for a particular commitment transactions.
    35  	PubKey *secp256k1.PublicKey
    36  
    37  	// Sig is the raw signature generated using the above public key.  This
    38  	// is the signature to be verified.
    39  	Sig *ecdsa.Signature
    40  
    41  	// SigHash is a function closure generates the sighashes that the
    42  	// passed signature is known to have signed.
    43  	SigHash func() ([]byte, error)
    44  
    45  	// HtlcIndex is the index of the HTLC from the PoV of the remote
    46  	// party's update log.
    47  	HtlcIndex uint64
    48  
    49  	// Cancel is a channel that should be closed if the caller wishes to
    50  	// cancel all pending verification jobs part of a single batch. This
    51  	// channel is to be closed in the case that a single signature in a
    52  	// batch has been returned as invalid, as there is no need to verify
    53  	// the remainder of the signatures.
    54  	Cancel chan struct{}
    55  
    56  	// ErrResp is the channel that the result of the signature verification
    57  	// is to be sent over. In the see that the signature is valid, a nil
    58  	// error will be passed. Otherwise, a concrete error detailing the
    59  	// issue will be passed.
    60  	ErrResp chan *HtlcIndexErr
    61  }
    62  
    63  // HtlcIndexErr is a special type of error that also includes a pointer to the
    64  // original validation job. This error message allows us to craft more detailed
    65  // errors at upper layers.
    66  type HtlcIndexErr struct {
    67  	error
    68  
    69  	*VerifyJob
    70  }
    71  
    72  // SignJob is a job sent to the sigPool sig pool to generate a valid
    73  // signature according to the passed input.SignDescriptor for the passed transaction.
    74  // Jobs are intended to be sent in batches in order to parallelize the job of
    75  // generating signatures for a new commitment transaction.
    76  type SignJob struct {
    77  	// SignDesc is intended to be a full populated input.SignDescriptor which
    78  	// encodes the necessary material (keys, witness script, etc) required
    79  	// to generate a valid signature for the specified input.
    80  	SignDesc input.SignDescriptor
    81  
    82  	// Tx is the transaction to be signed. This is required to generate the
    83  	// proper sighash for the input to be signed.
    84  	Tx *wire.MsgTx
    85  
    86  	// OutputIndex is the output index of the HTLC on the commitment
    87  	// transaction being signed.
    88  	OutputIndex int32
    89  
    90  	// Cancel is a channel that should be closed if the caller wishes to
    91  	// abandon all pending sign jobs part of a single batch.
    92  	Cancel chan struct{}
    93  
    94  	// Resp is the channel that the response to this particular SignJob
    95  	// will be sent over.
    96  	//
    97  	// TODO(roasbeef): actually need to allow caller to set, need to retain
    98  	// order mark commit sig as special
    99  	Resp chan SignJobResp
   100  }
   101  
   102  // SignJobResp is the response to a sign job. Both channels are to be read in
   103  // order to ensure no unnecessary goroutine blocking occurs. Additionally, both
   104  // channels should be buffered.
   105  type SignJobResp struct {
   106  	// Sig is the generated signature for a particular SignJob In the case
   107  	// of an error during signature generation, then this value sent will
   108  	// be nil.
   109  	Sig lnwire.Sig
   110  
   111  	// Err is the error that occurred when executing the specified
   112  	// signature job. In the case that no error occurred, this value will
   113  	// be nil.
   114  	Err error
   115  }
   116  
   117  // TODO(roasbeef); fix description
   118  
   119  // SigPool is a struct that is meant to allow the current channel state
   120  // machine to parallelize all signature generation and verification. This
   121  // struct is needed as _each_ HTLC when creating a commitment transaction
   122  // requires a signature, and similarly a receiver of a new commitment must
   123  // verify all the HTLC signatures included within the CommitSig message. A pool
   124  // of workers will be maintained by the sigPool. Batches of jobs (either
   125  // to sign or verify) can be sent to the pool of workers which will
   126  // asynchronously perform the specified job.
   127  type SigPool struct {
   128  	started sync.Once
   129  	stopped sync.Once
   130  
   131  	signer input.Signer
   132  
   133  	verifyJobs chan VerifyJob
   134  	signJobs   chan SignJob
   135  
   136  	wg   sync.WaitGroup
   137  	quit chan struct{}
   138  
   139  	numWorkers int
   140  }
   141  
   142  // NewSigPool creates a new signature pool with the specified number of
   143  // workers. The recommended parameter for the number of works is the number of
   144  // physical CPU cores available on the target machine.
   145  func NewSigPool(numWorkers int, signer input.Signer) *SigPool {
   146  	return &SigPool{
   147  		signer:     signer,
   148  		numWorkers: numWorkers,
   149  		verifyJobs: make(chan VerifyJob, jobBuffer),
   150  		signJobs:   make(chan SignJob, jobBuffer),
   151  		quit:       make(chan struct{}),
   152  	}
   153  }
   154  
   155  // Start starts of all goroutines that the sigPool sig pool needs to
   156  // carry out its duties.
   157  func (s *SigPool) Start() error {
   158  	s.started.Do(func() {
   159  		for i := 0; i < s.numWorkers; i++ {
   160  			s.wg.Add(1)
   161  			go s.poolWorker()
   162  		}
   163  	})
   164  	return nil
   165  }
   166  
   167  // Stop signals any active workers carrying out jobs to exit so the sigPool can
   168  // gracefully shutdown.
   169  func (s *SigPool) Stop() error {
   170  	s.stopped.Do(func() {
   171  		close(s.quit)
   172  		s.wg.Wait()
   173  	})
   174  	return nil
   175  }
   176  
   177  // poolWorker is the main worker goroutine within the sigPool sig pool.
   178  // Individual batches are distributed amongst each of the active workers. The
   179  // workers then execute the task based on the type of job, and return the
   180  // result back to caller.
   181  func (s *SigPool) poolWorker() {
   182  	defer s.wg.Done()
   183  
   184  	for {
   185  		select {
   186  
   187  		// We've just received a new signature job. Given the items
   188  		// contained within the message, we'll craft a signature and
   189  		// send the result along with a possible error back to the
   190  		// caller.
   191  		case sigMsg := <-s.signJobs:
   192  			rawSig, err := s.signer.SignOutputRaw(
   193  				sigMsg.Tx, &sigMsg.SignDesc,
   194  			)
   195  			if err != nil {
   196  				select {
   197  				case sigMsg.Resp <- SignJobResp{
   198  					Sig: lnwire.Sig{},
   199  					Err: err,
   200  				}:
   201  					continue
   202  				case <-sigMsg.Cancel:
   203  					continue
   204  				case <-s.quit:
   205  					return
   206  				}
   207  			}
   208  
   209  			sig, err := lnwire.NewSigFromSignature(rawSig)
   210  			select {
   211  			case sigMsg.Resp <- SignJobResp{
   212  				Sig: sig,
   213  				Err: err,
   214  			}:
   215  			case <-sigMsg.Cancel:
   216  				continue
   217  			case <-s.quit:
   218  				return
   219  			}
   220  
   221  		// We've just received a new verification job from the outside
   222  		// world. We'll attempt to construct the sighash, parse the
   223  		// signature, and finally verify the signature.
   224  		case verifyMsg := <-s.verifyJobs:
   225  			sigHash, err := verifyMsg.SigHash()
   226  			if err != nil {
   227  				select {
   228  				case verifyMsg.ErrResp <- &HtlcIndexErr{
   229  					error:     err,
   230  					VerifyJob: &verifyMsg,
   231  				}:
   232  					continue
   233  				case <-verifyMsg.Cancel:
   234  					continue
   235  				}
   236  			}
   237  
   238  			rawSig := verifyMsg.Sig
   239  
   240  			if !rawSig.Verify(sigHash, verifyMsg.PubKey) {
   241  				err := fmt.Errorf("invalid signature "+
   242  					"sighash: %x, sig: %x", sigHash,
   243  					rawSig.Serialize())
   244  
   245  				select {
   246  				case verifyMsg.ErrResp <- &HtlcIndexErr{
   247  					error:     err,
   248  					VerifyJob: &verifyMsg,
   249  				}:
   250  				case <-verifyMsg.Cancel:
   251  				case <-s.quit:
   252  					return
   253  				}
   254  			} else {
   255  				select {
   256  				case verifyMsg.ErrResp <- nil:
   257  				case <-verifyMsg.Cancel:
   258  				case <-s.quit:
   259  					return
   260  				}
   261  			}
   262  
   263  		// The sigPool sig pool is exiting, so we will as well.
   264  		case <-s.quit:
   265  			return
   266  		}
   267  	}
   268  }
   269  
   270  // SubmitSignBatch submits a batch of signature jobs to the sigPool.  The
   271  // response and cancel channels for each of the SignJob's are expected to be
   272  // fully populated, as the response for each job will be sent over the
   273  // response channel within the job itself.
   274  func (s *SigPool) SubmitSignBatch(signJobs []SignJob) {
   275  	for _, job := range signJobs {
   276  		select {
   277  		case s.signJobs <- job:
   278  		case <-job.Cancel:
   279  			// TODO(roasbeef): return error?
   280  		case <-s.quit:
   281  			return
   282  		}
   283  	}
   284  }
   285  
   286  // SubmitVerifyBatch submits a batch of verification jobs to the sigPool. For
   287  // each job submitted, an error will be passed into the returned channel
   288  // denoting if signature verification was valid or not. The passed cancelChan
   289  // allows the caller to cancel all pending jobs in the case that they wish to
   290  // bail early.
   291  func (s *SigPool) SubmitVerifyBatch(verifyJobs []VerifyJob,
   292  	cancelChan chan struct{}) <-chan *HtlcIndexErr {
   293  
   294  	errChan := make(chan *HtlcIndexErr, len(verifyJobs))
   295  
   296  	for _, job := range verifyJobs {
   297  		job.Cancel = cancelChan
   298  		job.ErrResp = errChan
   299  
   300  		select {
   301  		case s.verifyJobs <- job:
   302  		case <-job.Cancel:
   303  			return errChan
   304  		}
   305  	}
   306  
   307  	return errChan
   308  }