lnwallet: when creating new states, dispatch HTLC signing to sigPool

This commit is contained in:
Olaoluwa Osuntokun 2017-07-30 13:00:24 -07:00
parent f569b80a80
commit 3a052d2874
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -1874,13 +1874,163 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance,
*removeHeight = nextHeight
}
// generateRemoteHtlcSigJobs generates a series of HTLC signature jobs for the
// sig pool, along with a channel that if closed, will cancel any jobs after
// they have been submitted to the sigPool. This method is to be used when
// generating a new commitment for the remote party. The jobs generated by the
// signature can be submitted to the sigPool to generate all the signatures
// asynchronously and in parallel.
//
// TODO(roasbeef): all keys will eventually be generated within the commitment
// itself
func genRemoteHtlcSigJobs(commitPoint *btcec.PublicKey,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
remoteCommitView *commitment) ([]signJob, chan struct{}, error) {
// TODO(roasbeef): make the below into a sig pool job as well
// First, we'll generate all the keys required to generate the scripts
// for each HTLC output and transaction.
//
// TODO(roabseef): avoid re-calculating, put in commitment struct?
commitTweak := SingleTweakBytes(commitPoint,
localChanCfg.PaymentBasePoint)
revocationKey := DeriveRevocationPubkey(
localChanCfg.RevocationBasePoint,
commitPoint,
)
remoteDelayKey := TweakPubKey(remoteChanCfg.DelayBasePoint,
commitPoint)
txHash := remoteCommitView.txn.TxHash()
dustLimit := localChanCfg.DustLimit
feePerKw := remoteCommitView.feePerKw
// With the keys generated, we'll make a slice with enough capacity to
// hold potentially all the HTLC's. The actual slice may be a bit
// smaller (than its total capacity) an some HTLC's may be dust.
numSigs := (len(remoteCommitView.incomingHTLCs) +
len(remoteCommitView.outgoingHTLCs))
sigBatch := make([]signJob, 0, numSigs)
var err error
cancelChan := make(chan struct{})
// For ech outgoing and incoming HTLC, if the HTLC isn't considered a
// dust output after taking into account second-level HTLC fees, then a
// sigJob will be generated and appended to the current batch.
for _, htlc := range remoteCommitView.incomingHTLCs {
if htlcIsDust(true, false, feePerKw, htlc.Amount, dustLimit) {
continue
}
// If the HTLC isn't dust, then we'll create an empty sign job
// to add to the batch momentarily.
sigJob := signJob{}
sigJob.cancel = cancelChan
sigJob.resp = make(chan signJobResp, 1)
// As this is an incoming HTLC and we're sinning the commitment
// transaction of the remote node, we'll need to generate an
// HTLC timeout transaction for them. The output of the timeout
// transaction needs to account for fees, so we'll compute the
// required fee and output now.
htlcFee := htlcTimeoutFee(feePerKw)
outputAmt := htlc.Amount - htlcFee
// With the fee calculate, we can properly create the HTLC
// timeout transaction using the HTLC amount minus the fee.
op := wire.OutPoint{
Hash: txHash,
Index: uint32(htlc.remoteOutputIndex),
}
sigJob.tx, err = createHtlcTimeoutTx(op, outputAmt,
htlc.Timeout, uint32(remoteChanCfg.CsvDelay),
revocationKey, remoteDelayKey)
if err != nil {
return nil, nil, err
}
// Finally, we'll generate a sign descriptor to generate a
// signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{
PubKey: localChanCfg.PaymentBasePoint,
SingleTweak: commitTweak,
WitnessScript: htlc.theirWitnessScript,
Output: &wire.TxOut{
Value: int64(htlc.Amount),
},
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(sigJob.tx),
InputIndex: 0,
}
sigJob.outputIndex = htlc.remoteOutputIndex
sigBatch = append(sigBatch, sigJob)
}
for _, htlc := range remoteCommitView.outgoingHTLCs {
if htlcIsDust(false, false, feePerKw, htlc.Amount, dustLimit) {
continue
}
sigJob := signJob{}
sigJob.cancel = cancelChan
sigJob.resp = make(chan signJobResp, 1)
// As this is an outgoing HTLC and we're signing the commitment
// transaction of the remote node, we'll need to generate an
// HTLC success transaction for them. The output of the timeout
// transaction needs to account for fees, so we'll compute the
// required fee and output now.
htlcFee := htlcSuccessFee(feePerKw)
outputAmt := htlc.Amount - htlcFee
// With the proper output amount calculated, we can now
// generate the success transaction using the remote party's
// CSV delay.
op := wire.OutPoint{
Hash: txHash,
Index: uint32(htlc.remoteOutputIndex),
}
sigJob.tx, err = createHtlcSuccessTx(op, outputAmt,
uint32(remoteChanCfg.CsvDelay), revocationKey,
remoteDelayKey)
if err != nil {
return nil, nil, err
}
// Finally, we'll generate a sign descriptor to generate a
// signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{
PubKey: localChanCfg.PaymentBasePoint,
SingleTweak: commitTweak,
WitnessScript: htlc.theirWitnessScript,
Output: &wire.TxOut{
Value: int64(htlc.Amount),
},
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(sigJob.tx),
InputIndex: 0,
}
sigJob.outputIndex = htlc.remoteOutputIndex
sigBatch = append(sigBatch, sigJob)
}
return sigBatch, cancelChan, nil
}
// SignNextCommitment signs a new commitment which includes any previous
// unsettled HTLCs, any new HTLCs, and any modifications to prior HTLCs
// committed in previous commitment updates. Signing a new commitment
// decrements the available revocation window by 1. After a successful method
// call, the remote party's commitment chain is extended by a new commitment
// which includes all updates to the HTLC log prior to this method invocation.
func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
//
// TODO(roasbeef): update to detail second param
func (lc *LightningChannel) SignNextCommitment() (*btcec.Signature, []*btcec.Signature, error) {
lc.Lock()
defer lc.Unlock()
@ -1898,7 +2048,7 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
err := lc.validateCommitmentSanity(lc.remoteUpdateLog.ackedIndex,
lc.localUpdateLog.logIndex, false, true, true)
if err != nil {
return nil, err
return nil, nil, err
}
// Grab the next commitment point for the remote party. This well be
@ -1916,7 +2066,7 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
newCommitView, err := lc.fetchCommitmentView(true, lc.localUpdateLog.logIndex,
lc.remoteUpdateLog.ackedIndex, remoteRevocationKey, remoteRevocationHash)
if err != nil {
return nil, err
return nil, nil, err
}
walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v",
@ -1931,11 +2081,54 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
}),
)
// Sign their version of the new commitment transaction.
lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn)
sig, err := lc.signer.SignOutputRaw(newCommitView.txn, lc.signDesc)
// With the commitment view constructed, if there are any HTLC's, we'll
// need to generate signatures of each of them for the remote party's
// commitment state. We do so in two phases: first we generate and
// submit the set of signature jobs to the worker pool.
sigBatch, cancelChan, err := genRemoteHtlcSigJobs(commitPoint,
lc.localChanCfg, lc.remoteChanCfg, newCommitView,
)
if err != nil {
return nil, err
return nil, nil, err
}
// TODO(roasbeef): make fully async?
lc.sigPool.SubmitSignBatch(sigBatch)
// While the jobs are being carried out, we'll Sign their version of
// the new commitment transaction while we're waiting for the rest of
// the HTLC signatures to be processed.
lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn)
rawSig, err := lc.signer.SignOutputRaw(newCommitView.txn, lc.signDesc)
if err != nil {
close(cancelChan)
return nil, nil, err
}
sig, err := btcec.ParseSignature(rawSig, btcec.S256())
if err != nil {
close(cancelChan)
return nil, nil, err
}
// We'll need to send over the signatures to the remote party in the
// order as they appear on the commitment transaction after BIP 69
// sorting.
sortedSigs := sortableSignBatch(sigBatch)
sort.Sort(sortedSigs)
// With the jobs sorted, we'll now iterate through all the responses to
// gather each of the signatures in order.
htlcSigs := make([]*btcec.Signature, 0, len(sigBatch))
for _, htlcSigJob := range sortedSigs {
jobResp := <-htlcSigJob.resp
// If an error occurred, then we'll cancel any other active
// jobs.
if jobResp.err != nil {
close(cancelChan)
return nil, nil, err
}
htlcSigs = append(htlcSigs, jobResp.sig)
}
// Extend the remote commitment chain by one with the addition of our
@ -1961,7 +2154,7 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
// properly track which changes have been ACK'd.
lc.localUpdateLog.initiateTransition()
return sig, nil
return sig, htlcSigs, nil
}
// validateCommitmentSanity is used to validate that on current state the commitment