lnwallet: when creating new states, dispatch HTLC signing to sigPool
This commit is contained in:
parent
f569b80a80
commit
3a052d2874
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user