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
|
*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
|
// SignNextCommitment signs a new commitment which includes any previous
|
||||||
// unsettled HTLCs, any new HTLCs, and any modifications to prior HTLCs
|
// unsettled HTLCs, any new HTLCs, and any modifications to prior HTLCs
|
||||||
// committed in previous commitment updates. Signing a new commitment
|
// committed in previous commitment updates. Signing a new commitment
|
||||||
// decrements the available revocation window by 1. After a successful method
|
// decrements the available revocation window by 1. After a successful method
|
||||||
// call, the remote party's commitment chain is extended by a new commitment
|
// 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.
|
// 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()
|
lc.Lock()
|
||||||
defer lc.Unlock()
|
defer lc.Unlock()
|
||||||
|
|
||||||
@ -1898,7 +2048,7 @@ func (lc *LightningChannel) SignNextCommitment() ([]byte, error) {
|
|||||||
err := lc.validateCommitmentSanity(lc.remoteUpdateLog.ackedIndex,
|
err := lc.validateCommitmentSanity(lc.remoteUpdateLog.ackedIndex,
|
||||||
lc.localUpdateLog.logIndex, false, true, true)
|
lc.localUpdateLog.logIndex, false, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab the next commitment point for the remote party. This well be
|
// 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,
|
newCommitView, err := lc.fetchCommitmentView(true, lc.localUpdateLog.logIndex,
|
||||||
lc.remoteUpdateLog.ackedIndex, remoteRevocationKey, remoteRevocationHash)
|
lc.remoteUpdateLog.ackedIndex, remoteRevocationKey, remoteRevocationHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
walletLog.Tracef("ChannelPoint(%v): extending remote chain to height %v",
|
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.
|
// With the commitment view constructed, if there are any HTLC's, we'll
|
||||||
lc.signDesc.SigHashes = txscript.NewTxSigHashes(newCommitView.txn)
|
// need to generate signatures of each of them for the remote party's
|
||||||
sig, err := lc.signer.SignOutputRaw(newCommitView.txn, lc.signDesc)
|
// 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 {
|
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
|
// 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.
|
// properly track which changes have been ACK'd.
|
||||||
lc.localUpdateLog.initiateTransition()
|
lc.localUpdateLog.initiateTransition()
|
||||||
|
|
||||||
return sig, nil
|
return sig, htlcSigs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateCommitmentSanity is used to validate that on current state the commitment
|
// validateCommitmentSanity is used to validate that on current state the commitment
|
||||||
|
Loading…
Reference in New Issue
Block a user