Merge pull request #2329 from Roasbeef/global-sig-pool

multi: replace per channel sigPool with global daemon level sigPool
This commit is contained in:
Olaoluwa Osuntokun 2018-12-17 16:33:56 -08:00 committed by GitHub
commit 152fc8b1f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 239 additions and 286 deletions

@ -1123,18 +1123,6 @@ func TestBreachHandoffFail(t *testing.T) {
} }
defer cleanUpArb() defer cleanUpArb()
// Instantiate a second lightning channel for alice, using the state of
// her last channel.
aliceKeyPriv, _ := btcec.PrivKeyFromBytes(btcec.S256(),
alicesPrivKey)
aliceSigner := &mockSigner{aliceKeyPriv}
alice2, err := lnwallet.NewLightningChannel(aliceSigner, nil, alice.State())
if err != nil {
t.Fatalf("unable to create test channels: %v", err)
}
defer alice2.Stop()
// Signal a spend of the funding transaction and wait for the close // Signal a spend of the funding transaction and wait for the close
// observer to exit. This time we are allowing the handoff to succeed. // observer to exit. This time we are allowing the handoff to succeed.
breach = &ContractBreachEvent{ breach = &ContractBreachEvent{
@ -1567,18 +1555,23 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
aliceSigner := &mockSigner{aliceKeyPriv} aliceSigner := &mockSigner{aliceKeyPriv}
bobSigner := &mockSigner{bobKeyPriv} bobSigner := &mockSigner{bobKeyPriv}
alicePool := lnwallet.NewSigPool(1, aliceSigner)
channelAlice, err := lnwallet.NewLightningChannel( channelAlice, err := lnwallet.NewLightningChannel(
aliceSigner, pCache, aliceChannelState, aliceSigner, pCache, aliceChannelState, alicePool,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
alicePool.Start()
bobPool := lnwallet.NewSigPool(1, bobSigner)
channelBob, err := lnwallet.NewLightningChannel( channelBob, err := lnwallet.NewLightningChannel(
bobSigner, pCache, bobChannelState, bobSigner, pCache, bobChannelState, bobPool,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
bobPool.Start()
addr := &net.TCPAddr{ addr := &net.TCPAddr{
IP: net.ParseIP("127.0.0.1"), IP: net.ParseIP("127.0.0.1"),

@ -3,10 +3,11 @@ package contractcourt
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/lightningnetwork/lnd/sweep"
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/lightningnetwork/lnd/sweep"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
@ -238,11 +239,11 @@ func newActiveChannelArbitrator(channel *channeldb.OpenChannel,
} }
chanMachine, err := lnwallet.NewLightningChannel( chanMachine, err := lnwallet.NewLightningChannel(
c.cfg.Signer, c.cfg.PreimageDB, channel) c.cfg.Signer, c.cfg.PreimageDB, channel, nil,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
chanMachine.Stop()
if err := c.cfg.MarkLinkInactive(chanPoint); err != nil { if err := c.cfg.MarkLinkInactive(chanPoint); err != nil {
log.Errorf("unable to mark link inactive: %v", err) log.Errorf("unable to mark link inactive: %v", err)

@ -1681,13 +1681,12 @@ func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
// Go on adding the channel to the channel graph, and crafting // Go on adding the channel to the channel graph, and crafting
// channel announcements. // channel announcements.
lnChannel, err := lnwallet.NewLightningChannel( lnChannel, err := lnwallet.NewLightningChannel(
nil, nil, completeChan, nil, nil, completeChan, nil,
) )
if err != nil { if err != nil {
fndgLog.Errorf("failed creating lnChannel: %v", err) fndgLog.Errorf("failed creating lnChannel: %v", err)
return return
} }
defer lnChannel.Stop()
err = f.sendFundingLocked( err = f.sendFundingLocked(
fmsg.peer, completeChan, lnChannel, shortChanID, fmsg.peer, completeChan, lnChannel, shortChanID,
@ -1971,12 +1970,11 @@ func (f *fundingManager) handleFundingConfirmation(peer lnpeer.Peer,
// We create the state-machine object which wraps the database state. // We create the state-machine object which wraps the database state.
lnChannel, err := lnwallet.NewLightningChannel( lnChannel, err := lnwallet.NewLightningChannel(
nil, nil, completeChan, nil, nil, completeChan, nil,
) )
if err != nil { if err != nil {
return err return err
} }
defer lnChannel.Stop()
chanID := lnwire.NewChanIDFromOutPoint(&completeChan.FundingOutpoint) chanID := lnwire.NewChanIDFromOutPoint(&completeChan.FundingOutpoint)

@ -443,7 +443,6 @@ func (l *channelLink) Stop() {
} }
l.updateFeeTimer.Stop() l.updateFeeTimer.Stop()
l.channel.Stop()
l.overflowQueue.Stop() l.overflowQueue.Stop()
close(l.quit) close(l.quit)

@ -1592,8 +1592,6 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) (
cleanUp := func() { cleanUp := func() {
close(alicePeer.quit) close(alicePeer.quit)
defer fCleanUp() defer fCleanUp()
defer aliceLink.Stop()
defer bobChannel.Stop()
} }
return aliceLink, bobChannel, bticker.Force, start, cleanUp, restore, nil return aliceLink, bobChannel, bticker.Force, start, cleanUp, restore, nil

@ -10,6 +10,7 @@ import (
"math/big" "math/big"
"net" "net"
"os" "os"
"runtime"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -369,18 +370,23 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
preimageMap: make(map[[32]byte][]byte), preimageMap: make(map[[32]byte][]byte),
} }
alicePool := lnwallet.NewSigPool(runtime.NumCPU(), aliceSigner)
channelAlice, err := lnwallet.NewLightningChannel( channelAlice, err := lnwallet.NewLightningChannel(
aliceSigner, pCache, aliceChannelState, aliceSigner, pCache, aliceChannelState, alicePool,
) )
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
alicePool.Start()
bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner)
channelBob, err := lnwallet.NewLightningChannel( channelBob, err := lnwallet.NewLightningChannel(
bobSigner, pCache, bobChannelState, bobSigner, pCache, bobChannelState, bobPool,
) )
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
bobPool.Start()
// Now that the channel are open, simulate the start of a session by // Now that the channel are open, simulate the start of a session by
// having Alice and Bob extend their revocation windows to each other. // having Alice and Bob extend their revocation windows to each other.
@ -402,6 +408,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
restore := func() (*lnwallet.LightningChannel, *lnwallet.LightningChannel, restore := func() (*lnwallet.LightningChannel, *lnwallet.LightningChannel,
error) { error) {
aliceStoredChannels, err := dbAlice.FetchOpenChannels(aliceKeyPub) aliceStoredChannels, err := dbAlice.FetchOpenChannels(aliceKeyPub)
switch err { switch err {
case nil: case nil:
@ -434,8 +441,9 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
return nil, nil, errors.New("unable to find stored alice channel") return nil, nil, errors.New("unable to find stored alice channel")
} }
newAliceChannel, err := lnwallet.NewLightningChannel(aliceSigner, newAliceChannel, err := lnwallet.NewLightningChannel(
nil, aliceStoredChannel) aliceSigner, nil, aliceStoredChannel, alicePool,
)
if err != nil { if err != nil {
return nil, nil, errors.Errorf("unable to create new channel: %v", return nil, nil, errors.Errorf("unable to create new channel: %v",
err) err)
@ -473,8 +481,9 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
return nil, nil, errors.New("unable to find stored bob channel") return nil, nil, errors.New("unable to find stored bob channel")
} }
newBobChannel, err := lnwallet.NewLightningChannel(bobSigner, newBobChannel, err := lnwallet.NewLightningChannel(
nil, bobStoredChannel) bobSigner, nil, bobStoredChannel, bobPool,
)
if err != nil { if err != nil {
return nil, nil, errors.Errorf("unable to create new channel: %v", return nil, nil, errors.Errorf("unable to create new channel: %v",
err) err)
@ -485,7 +494,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
return channelAlice, channelBob, cleanUpFunc, restore, nil return channelAlice, channelBob, cleanUpFunc, restore, nil
} }
// getChanID retrieves the channel point from nwire message. // getChanID retrieves the channel point from an lnnwire message.
func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) { func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
var chanID lnwire.ChannelID var chanID lnwire.ChannelID
switch msg := msg.(type) { switch msg := msg.(type) {

@ -5,10 +5,8 @@ import (
"container/list" "container/list"
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"runtime"
"sort" "sort"
"sync" "sync"
"sync/atomic"
"github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
@ -1260,8 +1258,6 @@ func compactLogs(ourLog, theirLog *updateLog,
// //
// See the individual comments within the above methods for further details. // See the individual comments within the above methods for further details.
type LightningChannel struct { type LightningChannel struct {
shutdown int32 // To be used atomically.
// Signer is the main signer instances that will be responsible for // Signer is the main signer instances that will be responsible for
// signing any HTLC and commitment transaction generated by the state // signing any HTLC and commitment transaction generated by the state
// machine. // machine.
@ -1282,7 +1278,7 @@ type LightningChannel struct {
// validating signatures in parallel. This is utilized as an // validating signatures in parallel. This is utilized as an
// optimization to void serially signing or validating the HTLC // optimization to void serially signing or validating the HTLC
// signatures, of which there may be hundreds. // signatures, of which there may be hundreds.
sigPool *sigPool sigPool *SigPool
// pCache is the global preimage cache shared across all other // pCache is the global preimage cache shared across all other
// LightningChannel instance. We'll use this cache either when we force // LightningChannel instance. We'll use this cache either when we force
@ -1347,11 +1343,6 @@ type LightningChannel struct {
RemoteFundingKey *btcec.PublicKey RemoteFundingKey *btcec.PublicKey
sync.RWMutex sync.RWMutex
cowg sync.WaitGroup
wg sync.WaitGroup
quit chan struct{}
} }
// NewLightningChannel creates a new, active payment channel given an // NewLightningChannel creates a new, active payment channel given an
@ -1360,7 +1351,8 @@ type LightningChannel struct {
// automatically persist pertinent state to the database in an efficient // automatically persist pertinent state to the database in an efficient
// manner. // manner.
func NewLightningChannel(signer Signer, pCache PreimageCache, func NewLightningChannel(signer Signer, pCache PreimageCache,
state *channeldb.OpenChannel) (*LightningChannel, error) { state *channeldb.OpenChannel,
sigPool *SigPool) (*LightningChannel, error) {
localCommit := state.LocalCommitment localCommit := state.LocalCommitment
remoteCommit := state.RemoteCommitment remoteCommit := state.RemoteCommitment
@ -1375,9 +1367,8 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
) )
lc := &LightningChannel{ lc := &LightningChannel{
// TODO(roasbeef): tune num sig workers?
sigPool: newSigPool(runtime.NumCPU(), signer),
Signer: signer, Signer: signer,
sigPool: sigPool,
pCache: pCache, pCache: pCache,
currentHeight: localCommit.CommitHeight, currentHeight: localCommit.CommitHeight,
remoteCommitChain: newCommitmentChain(), remoteCommitChain: newCommitmentChain(),
@ -1391,7 +1382,6 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
Capacity: state.Capacity, Capacity: state.Capacity,
LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey, LocalFundingKey: state.LocalChanCfg.MultiSigKey.PubKey,
RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey, RemoteFundingKey: state.RemoteChanCfg.MultiSigKey.PubKey,
quit: make(chan struct{}),
} }
// With the main channel struct reconstructed, we'll now restore the // With the main channel struct reconstructed, we'll now restore the
@ -1411,12 +1401,6 @@ func NewLightningChannel(signer Signer, pCache PreimageCache,
lc.createStateHintObfuscator() lc.createStateHintObfuscator()
// Finally, we'll kick of the signature job pool to handle any upcoming
// commitment state generation and validation.
if err := lc.sigPool.Start(); err != nil {
return nil, err
}
return lc, nil return lc, nil
} }
@ -1467,23 +1451,6 @@ func (lc *LightningChannel) createStateHintObfuscator() {
} }
} }
// Stop gracefully shuts down any active goroutines spawned by the
// LightningChannel during regular duties.
func (lc *LightningChannel) Stop() {
if !atomic.CompareAndSwapInt32(&lc.shutdown, 0, 1) {
return
}
lc.sigPool.Stop()
close(lc.quit)
}
// WaitForClose blocks until the channel's close observer has terminated.
func (lc *LightningChannel) WaitForClose() {
lc.cowg.Wait()
}
// ResetState resets the state of the channel back to the default state. This // ResetState resets the state of the channel back to the default state. This
// ensures that any active goroutines which need to act based on on-chain // ensures that any active goroutines which need to act based on on-chain
// events do so properly. // events do so properly.
@ -2673,7 +2640,7 @@ func processRemoveEntry(htlc *PaymentDescriptor, ourBalance,
// asynchronously and in parallel. // asynchronously and in parallel.
func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing, func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig, localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
remoteCommitView *commitment) ([]signJob, chan struct{}, error) { remoteCommitView *commitment) ([]SignJob, chan struct{}, error) {
txHash := remoteCommitView.txn.TxHash() txHash := remoteCommitView.txn.TxHash()
dustLimit := remoteChanCfg.DustLimit dustLimit := remoteChanCfg.DustLimit
@ -2684,7 +2651,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// smaller (than its total capacity) and some HTLCs may be dust. // smaller (than its total capacity) and some HTLCs may be dust.
numSigs := (len(remoteCommitView.incomingHTLCs) + numSigs := (len(remoteCommitView.incomingHTLCs) +
len(remoteCommitView.outgoingHTLCs)) len(remoteCommitView.outgoingHTLCs))
sigBatch := make([]signJob, 0, numSigs) sigBatch := make([]SignJob, 0, numSigs)
var err error var err error
cancelChan := make(chan struct{}) cancelChan := make(chan struct{})
@ -2700,9 +2667,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// If the HTLC isn't dust, then we'll create an empty sign job // If the HTLC isn't dust, then we'll create an empty sign job
// to add to the batch momentarily. // to add to the batch momentarily.
sigJob := signJob{} sigJob := SignJob{}
sigJob.cancel = cancelChan sigJob.Cancel = cancelChan
sigJob.resp = make(chan signJobResp, 1) sigJob.Resp = make(chan SignJobResp, 1)
// As this is an incoming HTLC and we're sinning the commitment // As this is an incoming HTLC and we're sinning the commitment
// transaction of the remote node, we'll need to generate an // transaction of the remote node, we'll need to generate an
@ -2718,7 +2685,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
Hash: txHash, Hash: txHash,
Index: uint32(htlc.remoteOutputIndex), Index: uint32(htlc.remoteOutputIndex),
} }
sigJob.tx, err = createHtlcTimeoutTx( sigJob.Tx, err = createHtlcTimeoutTx(
op, outputAmt, htlc.Timeout, op, outputAmt, htlc.Timeout,
uint32(remoteChanCfg.CsvDelay), uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.DelayKey,
@ -2730,7 +2697,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// Finally, we'll generate a sign descriptor to generate a // Finally, we'll generate a sign descriptor to generate a
// signature to give to the remote party for this commitment // signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount. // transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{ sigJob.SignDesc = SignDescriptor{
KeyDesc: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlc.theirWitnessScript, WitnessScript: htlc.theirWitnessScript,
@ -2738,10 +2705,10 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
Value: int64(htlc.Amount.ToSatoshis()), Value: int64(htlc.Amount.ToSatoshis()),
}, },
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(sigJob.tx), SigHashes: txscript.NewTxSigHashes(sigJob.Tx),
InputIndex: 0, InputIndex: 0,
} }
sigJob.outputIndex = htlc.remoteOutputIndex sigJob.OutputIndex = htlc.remoteOutputIndex
sigBatch = append(sigBatch, sigJob) sigBatch = append(sigBatch, sigJob)
} }
@ -2751,9 +2718,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
continue continue
} }
sigJob := signJob{} sigJob := SignJob{}
sigJob.cancel = cancelChan sigJob.Cancel = cancelChan
sigJob.resp = make(chan signJobResp, 1) sigJob.Resp = make(chan SignJobResp, 1)
// As this is an outgoing HTLC and we're signing the commitment // As this is an outgoing HTLC and we're signing the commitment
// transaction of the remote node, we'll need to generate an // transaction of the remote node, we'll need to generate an
@ -2770,7 +2737,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
Hash: txHash, Hash: txHash,
Index: uint32(htlc.remoteOutputIndex), Index: uint32(htlc.remoteOutputIndex),
} }
sigJob.tx, err = createHtlcSuccessTx( sigJob.Tx, err = createHtlcSuccessTx(
op, outputAmt, uint32(remoteChanCfg.CsvDelay), op, outputAmt, uint32(remoteChanCfg.CsvDelay),
keyRing.RevocationKey, keyRing.DelayKey, keyRing.RevocationKey, keyRing.DelayKey,
) )
@ -2781,7 +2748,7 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
// Finally, we'll generate a sign descriptor to generate a // Finally, we'll generate a sign descriptor to generate a
// signature to give to the remote party for this commitment // signature to give to the remote party for this commitment
// transaction. Note we use the raw HTLC amount. // transaction. Note we use the raw HTLC amount.
sigJob.signDesc = SignDescriptor{ sigJob.SignDesc = SignDescriptor{
KeyDesc: localChanCfg.HtlcBasePoint, KeyDesc: localChanCfg.HtlcBasePoint,
SingleTweak: keyRing.LocalHtlcKeyTweak, SingleTweak: keyRing.LocalHtlcKeyTweak,
WitnessScript: htlc.theirWitnessScript, WitnessScript: htlc.theirWitnessScript,
@ -2789,10 +2756,10 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
Value: int64(htlc.Amount.ToSatoshis()), Value: int64(htlc.Amount.ToSatoshis()),
}, },
HashType: txscript.SigHashAll, HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(sigJob.tx), SigHashes: txscript.NewTxSigHashes(sigJob.Tx),
InputIndex: 0, InputIndex: 0,
} }
sigJob.outputIndex = htlc.remoteOutputIndex sigJob.OutputIndex = htlc.remoteOutputIndex
sigBatch = append(sigBatch, sigJob) sigBatch = append(sigBatch, sigJob)
} }
@ -3059,26 +3026,23 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, erro
// order as they appear on the commitment transaction after BIP 69 // order as they appear on the commitment transaction after BIP 69
// sorting. // sorting.
sort.Slice(sigBatch, func(i, j int) bool { sort.Slice(sigBatch, func(i, j int) bool {
return sigBatch[i].outputIndex < sigBatch[j].outputIndex return sigBatch[i].OutputIndex < sigBatch[j].OutputIndex
}) })
// With the jobs sorted, we'll now iterate through all the responses to // With the jobs sorted, we'll now iterate through all the responses to
// gather each of the signatures in order. // gather each of the signatures in order.
htlcSigs = make([]lnwire.Sig, 0, len(sigBatch)) htlcSigs = make([]lnwire.Sig, 0, len(sigBatch))
for _, htlcSigJob := range sigBatch { for _, htlcSigJob := range sigBatch {
select { jobResp := <-htlcSigJob.Resp
case jobResp := <-htlcSigJob.resp:
// If an error occurred, then we'll cancel any other
// active jobs.
if jobResp.err != nil {
close(cancelChan)
return sig, htlcSigs, err
}
htlcSigs = append(htlcSigs, jobResp.sig) // If an error occurred, then we'll cancel any other active
case <-lc.quit: // jobs.
return sig, htlcSigs, fmt.Errorf("channel shutting down") if jobResp.Err != nil {
close(cancelChan)
return sig, htlcSigs, err
} }
htlcSigs = append(htlcSigs, jobResp.Sig)
} }
// As we're about to proposer a new commitment state for the remote // As we're about to proposer a new commitment state for the remote
@ -3740,7 +3704,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// directly into the pool of workers. // directly into the pool of workers.
func genHtlcSigValidationJobs(localCommitmentView *commitment, func genHtlcSigValidationJobs(localCommitmentView *commitment,
keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig, keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]verifyJob, error) { localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]VerifyJob, error) {
txHash := localCommitmentView.txn.TxHash() txHash := localCommitmentView.txn.TxHash()
feePerKw := localCommitmentView.feePerKw feePerKw := localCommitmentView.feePerKw
@ -3751,7 +3715,7 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
// length will be smaller than the total capacity. // length will be smaller than the total capacity.
numHtlcs := (len(localCommitmentView.incomingHTLCs) + numHtlcs := (len(localCommitmentView.incomingHTLCs) +
len(localCommitmentView.outgoingHTLCs)) len(localCommitmentView.outgoingHTLCs))
verifyJobs := make([]verifyJob, 0, numHtlcs) verifyJobs := make([]VerifyJob, 0, numHtlcs)
// We'll iterate through each output in the commitment transaction, // We'll iterate through each output in the commitment transaction,
// populating the sigHash closure function if it's detected to be an // populating the sigHash closure function if it's detected to be an
@ -3879,11 +3843,11 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
continue continue
} }
verifyJobs = append(verifyJobs, verifyJob{ verifyJobs = append(verifyJobs, VerifyJob{
htlcIndex: htlcIndex, HtlcIndex: htlcIndex,
pubKey: keyRing.RemoteHtlcKey, PubKey: keyRing.RemoteHtlcKey,
sig: sig, Sig: sig,
sigHash: sigHash, SigHash: sigHash,
}) })
i++ i++
@ -4083,34 +4047,30 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
for i := 0; i < len(verifyJobs); i++ { for i := 0; i < len(verifyJobs); i++ {
// In the case that a single signature is invalid, we'll exit // In the case that a single signature is invalid, we'll exit
// early and cancel all the outstanding verification jobs. // early and cancel all the outstanding verification jobs.
select { htlcErr := <-verifyResps
case htlcErr := <-verifyResps: if htlcErr != nil {
if htlcErr != nil { close(cancelChan)
close(cancelChan)
sig, err := lnwire.NewSigFromSignature( sig, err := lnwire.NewSigFromSignature(
htlcErr.sig, htlcErr.Sig,
) )
if err != nil { if err != nil {
return err return err
} }
sigHash, err := htlcErr.sigHash() sigHash, err := htlcErr.SigHash()
if err != nil { if err != nil {
return err return err
} }
var txBytes bytes.Buffer var txBytes bytes.Buffer
localCommitTx.Serialize(&txBytes) localCommitTx.Serialize(&txBytes)
return &InvalidHtlcSigError{ return &InvalidHtlcSigError{
commitHeight: nextHeight, commitHeight: nextHeight,
htlcSig: sig.ToSignatureBytes(), htlcSig: sig.ToSignatureBytes(),
htlcIndex: htlcErr.htlcIndex, htlcIndex: htlcErr.HtlcIndex,
sigHash: sigHash, sigHash: sigHash,
commitTx: txBytes.Bytes(), commitTx: txBytes.Bytes(),
}
} }
case <-lc.quit:
return fmt.Errorf("channel shutting down")
} }
} }

@ -1444,14 +1444,18 @@ func TestStateUpdatePersistence(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch channel: %v", err) t.Fatalf("unable to fetch channel: %v", err)
} }
aliceChannelNew, err := NewLightningChannel( aliceChannelNew, err := NewLightningChannel(
aliceChannel.Signer, nil, aliceChannels[0], aliceChannel.Signer, nil, aliceChannels[0],
aliceChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
bobChannelNew, err := NewLightningChannel( bobChannelNew, err := NewLightningChannel(
bobChannel.Signer, nil, bobChannels[0], bobChannel.Signer, nil, bobChannels[0],
bobChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
@ -2544,20 +2548,19 @@ func TestChanSyncFullySynced(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to fetch channel: %v", err) t.Fatalf("unable to fetch channel: %v", err)
} }
aliceChannelNew, err := NewLightningChannel( aliceChannelNew, err := NewLightningChannel(
aliceChannel.Signer, nil, aliceChannels[0], aliceChannel.Signer, nil, aliceChannels[0], aliceChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
defer aliceChannelNew.Stop()
bobChannelNew, err := NewLightningChannel( bobChannelNew, err := NewLightningChannel(
bobChannel.Signer, nil, bobChannels[0], bobChannel.Signer, nil, bobChannels[0], bobChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
defer bobChannelNew.Stop()
assertNoChanSyncNeeded(t, aliceChannelNew, bobChannelNew) assertNoChanSyncNeeded(t, aliceChannelNew, bobChannelNew)
} }
@ -2576,6 +2579,7 @@ func restartChannel(channelOld *LightningChannel) (*LightningChannel, error) {
channelNew, err := NewLightningChannel( channelNew, err := NewLightningChannel(
channelOld.Signer, channelOld.pCache, nodeChannels[0], channelOld.Signer, channelOld.pCache, nodeChannels[0],
channelOld.sigPool,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -2786,7 +2790,6 @@ func TestChanSyncOweCommitment(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart alice: %v", err) t.Fatalf("unable to restart alice: %v", err)
} }
defer aliceChannel.Stop()
assertAliceCommitRetransmit() assertAliceCommitRetransmit()
// TODO(roasbeef): restart bob as well??? // TODO(roasbeef): restart bob as well???
@ -3053,7 +3056,6 @@ func TestChanSyncOweRevocation(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart alice: %v", err) t.Fatalf("unable to restart alice: %v", err)
} }
defer aliceChannel.Stop()
assertAliceOwesRevoke() assertAliceOwesRevoke()
// TODO(roasbeef): restart bob too??? // TODO(roasbeef): restart bob too???
@ -3233,7 +3235,6 @@ func TestChanSyncOweRevocationAndCommit(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer bobChannel.Stop()
assertBobSendsRevokeAndCommit() assertBobSendsRevokeAndCommit()
// We'll now finish the state transition by having Alice process both // We'll now finish the state transition by having Alice process both
@ -3428,7 +3429,6 @@ func TestChanSyncOweRevocationAndCommitForceTransition(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer bobChannel.Stop()
if len(bobMsgsToSend) != 2 { if len(bobMsgsToSend) != 2 {
t.Fatalf("expected bob to send %v messages, instead "+ t.Fatalf("expected bob to send %v messages, instead "+
"sends: %v", 2, spew.Sdump(bobMsgsToSend)) "sends: %v", 2, spew.Sdump(bobMsgsToSend))
@ -3775,12 +3775,10 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart alice: %v", err) t.Fatalf("unable to restart alice: %v", err)
} }
defer aliceChannel.Stop()
bobChannel, err = restartChannel(bobChannel) bobChannel, err = restartChannel(bobChannel)
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer bobChannel.Stop()
// Bob doesn't get this message so upon reconnection, they need to // Bob doesn't get this message so upon reconnection, they need to
// synchronize. Alice should conclude that she owes Bob a commitment, // synchronize. Alice should conclude that she owes Bob a commitment,
@ -4293,12 +4291,10 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart alice: %v", err) t.Fatalf("unable to restart alice: %v", err)
} }
defer aliceChannel.Stop()
bobChannel, err = restartChannel(bobChannel) bobChannel, err = restartChannel(bobChannel)
if err != nil { if err != nil {
t.Fatalf("unable to restart bob: %v", err) t.Fatalf("unable to restart bob: %v", err)
} }
defer bobChannel.Stop()
// With both nodes restarted, Bob will now attempt to cancel one of // With both nodes restarted, Bob will now attempt to cancel one of
// Alice's HTLC's. // Alice's HTLC's.
@ -4399,12 +4395,10 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart alice: %v", err) t.Fatalf("unable to restart alice: %v", err)
} }
defer aliceChannel.Stop()
bobChannel, err = restartChannel(bobChannel) bobChannel, err = restartChannel(bobChannel)
if err != nil { if err != nil {
t.Fatalf("unable to restart bob: %v", err) t.Fatalf("unable to restart bob: %v", err)
} }
defer bobChannel.Stop()
// Readd the Fail to both Alice and Bob's channels, as the non-committed // Readd the Fail to both Alice and Bob's channels, as the non-committed
// update will not have survived the restart. // update will not have survived the restart.
@ -5590,19 +5584,19 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
// the update logs up to the correct state set up above. // the update logs up to the correct state set up above.
newAliceChannel, err := NewLightningChannel( newAliceChannel, err := NewLightningChannel(
aliceChannel.Signer, nil, aliceChannel.channelState, aliceChannel.Signer, nil, aliceChannel.channelState,
aliceChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
defer newAliceChannel.Stop()
newBobChannel, err := NewLightningChannel( newBobChannel, err := NewLightningChannel(
bobChannel.Signer, nil, bobChannel.channelState, bobChannel.Signer, nil, bobChannel.channelState,
bobChannel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
defer newBobChannel.Stop()
// compare all the logs between the old and new channels, to make sure // compare all the logs between the old and new channels, to make sure
// they all got restored properly. // they all got restored properly.
@ -5669,13 +5663,14 @@ func assertInLogs(t *testing.T, channel *LightningChannel, numAddsLocal,
// expected state. // expected state.
func restoreAndAssert(t *testing.T, channel *LightningChannel, numAddsLocal, func restoreAndAssert(t *testing.T, channel *LightningChannel, numAddsLocal,
numFailsLocal, numAddsRemote, numFailsRemote int) { numFailsLocal, numAddsRemote, numFailsRemote int) {
newChannel, err := NewLightningChannel( newChannel, err := NewLightningChannel(
channel.Signer, nil, channel.channelState, channel.Signer, nil, channel.channelState,
channel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
} }
defer newChannel.Stop()
assertInLog(t, newChannel.localUpdateLog, numAddsLocal, numFailsLocal) assertInLog(t, newChannel.localUpdateLog, numAddsLocal, numFailsLocal)
assertInLog(t, newChannel.remoteUpdateLog, numAddsRemote, numFailsRemote) assertInLog(t, newChannel.remoteUpdateLog, numAddsRemote, numFailsRemote)
@ -5863,12 +5858,10 @@ func TestDuplicateFailRejection(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer bobChannel.Stop()
aliceChannel, err = restartChannel(aliceChannel) aliceChannel, err = restartChannel(aliceChannel)
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer aliceChannel.Stop()
// If we try to fail the same HTLC again, then we should get an error. // If we try to fail the same HTLC again, then we should get an error.
err = bobChannel.FailHTLC(0, []byte("failreason"), nil, nil, nil) err = bobChannel.FailHTLC(0, []byte("failreason"), nil, nil, nil)
@ -5945,12 +5938,10 @@ func TestDuplicateSettleRejection(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer bobChannel.Stop()
aliceChannel, err = restartChannel(aliceChannel) aliceChannel, err = restartChannel(aliceChannel)
if err != nil { if err != nil {
t.Fatalf("unable to restart channel: %v", err) t.Fatalf("unable to restart channel: %v", err)
} }
defer aliceChannel.Stop()
// If we try to fail the same HTLC again, then we should get an error. // If we try to fail the same HTLC again, then we should get an error.
err = bobChannel.SettleHTLC(alicePreimage, uint64(0), nil, nil, nil) err = bobChannel.SettleHTLC(alicePreimage, uint64(0), nil, nil, nil)
@ -5983,9 +5974,9 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
channel *LightningChannel, remoteLog bool, htlcIndex uint64, channel *LightningChannel, remoteLog bool, htlcIndex uint64,
expLocal, expRemote uint64) *LightningChannel { expLocal, expRemote uint64) *LightningChannel {
channel.Stop()
newChannel, err := NewLightningChannel( newChannel, err := NewLightningChannel(
channel.Signer, nil, channel.channelState, channel.Signer, nil, channel.channelState,
channel.sigPool,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create new channel: %v", err) t.Fatalf("unable to create new channel: %v", err)
@ -6152,7 +6143,4 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
// HTLC an add height. // HTLC an add height.
bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 2, 1) bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 0, 2, 1)
bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 1, 2, 2) bobChannel = restoreAndAssertCommitHeights(t, bobChannel, true, 1, 2, 2)
aliceChannel.Stop()
bobChannel.Stop()
} }

@ -19,119 +19,118 @@ const (
// TODO(roasbeef): job buffer pool? // TODO(roasbeef): job buffer pool?
) )
// verifyJob is a job sent to the sigPool to verify a signature on a // VerifyJob is a job sent to the sigPool sig pool to verify a signature
// transaction. The items contained in the struct are necessary and sufficient // on a transaction. The items contained in the struct are necessary and
// to verify the full signature. The passed sigHash closure function should be // sufficient to verify the full signature. The passed sigHash closure function
// set to a function that generates the relevant sighash. // should be set to a function that generates the relevant sighash.
// //
// TODO(roasbeef): when we move to ecschnorr, make into batch signature // TODO(roasbeef): when we move to ecschnorr, make into batch signature
// verification using bos-coster (or pip?). // verification using bos-coster (or pip?).
type verifyJob struct { type VerifyJob struct {
// pubKey is the public key that was used to generate the purported // PubKey is the public key that was used to generate the purported
// valid signature. Note that with the current channel construction, // valid signature. Note that with the current channel construction,
// this public key will likely have been tweaked using the current per // this public key will likely have been tweaked using the current per
// commitment point for a particular commitment transactions. // commitment point for a particular commitment transactions.
pubKey *btcec.PublicKey PubKey *btcec.PublicKey
// sig is the raw signature generated using the above public key. This // Sig is the raw signature generated using the above public key. This
// is the signature to be verified. // is the signature to be verified.
sig *btcec.Signature Sig *btcec.Signature
// sigHash is a function closure generates the sighashes that the // SigHash is a function closure generates the sighashes that the
// passed signature is known to have signed. // passed signature is known to have signed.
sigHash func() ([]byte, error) SigHash func() ([]byte, error)
// htlcIndex is the index of the HTLC from the PoV of the remote // HtlcIndex is the index of the HTLC from the PoV of the remote
// party's update log. // party's update log.
htlcIndex uint64 HtlcIndex uint64
// cancel is a channel that should be closed if the caller wishes to // Cancel is a channel that should be closed if the caller wishes to
// cancel all pending verification jobs part of a single batch. This // cancel all pending verification jobs part of a single batch. This
// channel is to be closed in the case that a single signature in a // channel is to be closed in the case that a single signature in a
// batch has been returned as invalid, as there is no need to verify // batch has been returned as invalid, as there is no need to verify
// the remainder of the signatures. // the remainder of the signatures.
cancel chan struct{} Cancel chan struct{}
// errResp is the channel that the result of the signature verification // ErrResp is the channel that the result of the signature verification
// is to be sent over. In the see that the signature is valid, a nil // is to be sent over. In the see that the signature is valid, a nil
// error will be passed. Otherwise, a concrete error detailing the // error will be passed. Otherwise, a concrete error detailing the
// issue will be passed. // issue will be passed.
errResp chan *htlcIndexErr ErrResp chan *HtlcIndexErr
} }
// verifyJobErr is a special type of error that also includes a pointer to the // HtlcIndexErr is a special type of error that also includes a pointer to the
// original validation job. Ths error message allows us to craft more detailed // original validation job. This error message allows us to craft more detailed
// errors at upper layers. // errors at upper layers.
type htlcIndexErr struct { type HtlcIndexErr struct {
error error
*verifyJob *VerifyJob
} }
// signJob is a job sent to the sigPool to generate a valid signature according // SignJob is a job sent to the sigPool sig pool to generate a valid
// to the passed SignDescriptor for the passed transaction. Jobs are intended // signature according to the passed SignDescriptor for the passed transaction.
// to be sent in batches in order to parallelize the job of generating // Jobs are intended to be sent in batches in order to parallelize the job of
// signatures for a new commitment transaction. // generating signatures for a new commitment transaction.
type signJob struct { type SignJob struct {
// signDesc is intended to be a full populated SignDescriptor which // SignDesc is intended to be a full populated SignDescriptor which
// encodes the necessary material (keys, witness script, etc) required // encodes the necessary material (keys, witness script, etc) required
// to generate a valid signature for the specified input. // to generate a valid signature for the specified input.
signDesc SignDescriptor SignDesc SignDescriptor
// tx is the transaction to be signed. This is required to generate the // Tx is the transaction to be signed. This is required to generate the
// proper sighash for the input to be signed. // proper sighash for the input to be signed.
tx *wire.MsgTx Tx *wire.MsgTx
// outputIndex is the output index of the HTLC on the commitment // OutputIndex is the output index of the HTLC on the commitment
// transaction being signed. // transaction being signed.
outputIndex int32 OutputIndex int32
// cancel is a channel that should be closed if the caller wishes to // Cancel is a channel that should be closed if the caller wishes to
// abandon all pending sign jobs part of a single batch. // abandon all pending sign jobs part of a single batch.
cancel chan struct{} Cancel chan struct{}
// resp is the channel that the response to this particular signJob // Resp is the channel that the response to this particular SignJob
// will be sent over. // will be sent over.
// //
// TODO(roasbeef): actually need to allow caller to set, need to retain // TODO(roasbeef): actually need to allow caller to set, need to retain
// order mark commit sig as special // order mark commit sig as special
resp chan signJobResp Resp chan SignJobResp
} }
// signJobResp is the response to a sign job. Both channels are to be read in // SignJobResp is the response to a sign job. Both channels are to be read in
// order to ensure no unnecessary goroutine blocking occurs. Additionally, both // order to ensure no unnecessary goroutine blocking occurs. Additionally, both
// channels should be buffered. // channels should be buffered.
type signJobResp struct { type SignJobResp struct {
// sig is the generated signature for a particular signJob In the case // Sig is the generated signature for a particular SignJob In the case
// of an error during signature generation, then this value sent will // of an error during signature generation, then this value sent will
// be nil. // be nil.
sig lnwire.Sig Sig lnwire.Sig
// err is the error that occurred when executing the specified // Err is the error that occurred when executing the specified
// signature job. In the case that no error occurred, this value will // signature job. In the case that no error occurred, this value will
// be nil. // be nil.
err error Err error
} }
// sigPool is a struct that is meant to allow the current channel state machine // TODO(roasbeef); fix description
// to parallelize all signature generation and verification. This struct is
// needed as _each_ HTLC when creating a commitment transaction requires a // SigPool is a struct that is meant to allow the current channel state
// signature, and similarly a receiver of a new commitment must verify all the // machine to parallelize all signature generation and verification. This
// HTLC signatures included within the CommitSig message. A pool of workers // struct is needed as _each_ HTLC when creating a commitment transaction
// will be maintained by the sigPool. Batches of jobs (either to sign or // requires a signature, and similarly a receiver of a new commitment must
// verify) can be sent to the pool of workers which will asynchronously perform // verify all the HTLC signatures included within the CommitSig message. A pool
// the specified job. // of workers will be maintained by the sigPool. Batches of jobs (either
// // to sign or verify) can be sent to the pool of workers which will
// TODO(roasbeef): rename? // asynchronously perform the specified job.
// * ecdsaPool? type SigPool struct {
type sigPool struct {
started uint32 // To be used atomically. started uint32 // To be used atomically.
stopped uint32 // To be used atomically. stopped uint32 // To be used atomically.
signer Signer signer Signer
verifyJobs chan verifyJob verifyJobs chan VerifyJob
signJobs chan signJob signJobs chan SignJob
wg sync.WaitGroup wg sync.WaitGroup
quit chan struct{} quit chan struct{}
@ -139,22 +138,22 @@ type sigPool struct {
numWorkers int numWorkers int
} }
// newSigPool creates a new signature pool with the specified number of // NewSigPool creates a new signature pool with the specified number of
// workers. The recommended parameter for the number of works is the number of // workers. The recommended parameter for the number of works is the number of
// physical CPU cores available on the target machine. // physical CPU cores available on the target machine.
func newSigPool(numWorkers int, signer Signer) *sigPool { func NewSigPool(numWorkers int, signer Signer) *SigPool {
return &sigPool{ return &SigPool{
signer: signer, signer: signer,
numWorkers: numWorkers, numWorkers: numWorkers,
verifyJobs: make(chan verifyJob, jobBuffer), verifyJobs: make(chan VerifyJob, jobBuffer),
signJobs: make(chan signJob, jobBuffer), signJobs: make(chan SignJob, jobBuffer),
quit: make(chan struct{}), quit: make(chan struct{}),
} }
} }
// Start starts of all goroutines that the sigPool needs to carry out its // Start starts of all goroutines that the sigPool sig pool needs to
// duties. // carry out its duties.
func (s *sigPool) Start() error { func (s *SigPool) Start() error {
if !atomic.CompareAndSwapUint32(&s.started, 0, 1) { if !atomic.CompareAndSwapUint32(&s.started, 0, 1) {
return nil return nil
} }
@ -169,7 +168,7 @@ func (s *sigPool) Start() error {
// Stop signals any active workers carrying out jobs to exit so the sigPool can // Stop signals any active workers carrying out jobs to exit so the sigPool can
// gracefully shutdown. // gracefully shutdown.
func (s *sigPool) Stop() error { func (s *SigPool) Stop() error {
if !atomic.CompareAndSwapUint32(&s.stopped, 0, 1) { if !atomic.CompareAndSwapUint32(&s.stopped, 0, 1) {
return nil return nil
} }
@ -180,11 +179,11 @@ func (s *sigPool) Stop() error {
return nil return nil
} }
// poolWorker is the main worker goroutine within the sigPool. Individual // poolWorker is the main worker goroutine within the sigPool sig pool.
// batches are distributed amongst each of the active workers. The workers then // Individual batches are distributed amongst each of the active workers. The
// execute the task based on the type of job, and return the result back to // workers then execute the task based on the type of job, and return the
// caller. // result back to caller.
func (s *sigPool) poolWorker() { func (s *SigPool) poolWorker() {
defer s.wg.Done() defer s.wg.Done()
for { for {
@ -195,16 +194,17 @@ func (s *sigPool) poolWorker() {
// send the result along with a possible error back to the // send the result along with a possible error back to the
// caller. // caller.
case sigMsg := <-s.signJobs: case sigMsg := <-s.signJobs:
rawSig, err := s.signer.SignOutputRaw(sigMsg.tx, rawSig, err := s.signer.SignOutputRaw(
&sigMsg.signDesc) sigMsg.Tx, &sigMsg.SignDesc,
)
if err != nil { if err != nil {
select { select {
case sigMsg.resp <- signJobResp{ case sigMsg.Resp <- SignJobResp{
sig: lnwire.Sig{}, Sig: lnwire.Sig{},
err: err, Err: err,
}: }:
continue continue
case <-sigMsg.cancel: case <-sigMsg.Cancel:
continue continue
case <-s.quit: case <-s.quit:
return return
@ -213,11 +213,11 @@ func (s *sigPool) poolWorker() {
sig, err := lnwire.NewSigFromRawSignature(rawSig) sig, err := lnwire.NewSigFromRawSignature(rawSig)
select { select {
case sigMsg.resp <- signJobResp{ case sigMsg.Resp <- SignJobResp{
sig: sig, Sig: sig,
err: err, Err: err,
}: }:
case <-sigMsg.cancel: case <-sigMsg.Cancel:
continue continue
case <-s.quit: case <-s.quit:
return return
@ -227,58 +227,60 @@ func (s *sigPool) poolWorker() {
// world. We'll attempt to construct the sighash, parse the // world. We'll attempt to construct the sighash, parse the
// signature, and finally verify the signature. // signature, and finally verify the signature.
case verifyMsg := <-s.verifyJobs: case verifyMsg := <-s.verifyJobs:
sigHash, err := verifyMsg.sigHash() sigHash, err := verifyMsg.SigHash()
if err != nil { if err != nil {
select { select {
case verifyMsg.errResp <- &htlcIndexErr{ case verifyMsg.ErrResp <- &HtlcIndexErr{
error: err, error: err,
verifyJob: &verifyMsg, VerifyJob: &verifyMsg,
}: }:
continue continue
case <-verifyMsg.cancel: case <-verifyMsg.Cancel:
continue continue
} }
} }
rawSig := verifyMsg.sig rawSig := verifyMsg.Sig
if !rawSig.Verify(sigHash, verifyMsg.pubKey) { if !rawSig.Verify(sigHash, verifyMsg.PubKey) {
err := fmt.Errorf("invalid signature "+ err := fmt.Errorf("invalid signature "+
"sighash: %x, sig: %x", sigHash, rawSig.Serialize()) "sighash: %x, sig: %x", sigHash,
rawSig.Serialize())
select { select {
case verifyMsg.errResp <- &htlcIndexErr{ case verifyMsg.ErrResp <- &HtlcIndexErr{
error: err, error: err,
verifyJob: &verifyMsg, VerifyJob: &verifyMsg,
}: }:
case <-verifyMsg.cancel: case <-verifyMsg.Cancel:
case <-s.quit: case <-s.quit:
return return
} }
} else { } else {
select { select {
case verifyMsg.errResp <- nil: case verifyMsg.ErrResp <- nil:
case <-verifyMsg.cancel: case <-verifyMsg.Cancel:
case <-s.quit: case <-s.quit:
return return
} }
} }
// The sigPool is exiting, so we will as well. // The sigPool sig pool is exiting, so we will as well.
case <-s.quit: case <-s.quit:
return return
} }
} }
} }
// SubmitSignBatch submits a batch of signature jobs to the sigPool. The // SubmitSignBatch submits a batch of signature jobs to the sigPool. The
// response and cancel channels for each of the signJob's are expected to be // response and cancel channels for each of the SignJob's are expected to be
// fully populated, as the response for each job will be sent over the response // fully populated, as the response for each job will be sent over the
// channel within the job itself. // response channel within the job itself.
func (s *sigPool) SubmitSignBatch(signJobs []signJob) { func (s *SigPool) SubmitSignBatch(signJobs []SignJob) {
for _, job := range signJobs { for _, job := range signJobs {
select { select {
case s.signJobs <- job: case s.signJobs <- job:
case <-job.cancel: case <-job.Cancel:
// TODO(roasbeef): return error? // TODO(roasbeef): return error?
case <-s.quit: case <-s.quit:
return return
@ -291,18 +293,18 @@ func (s *sigPool) SubmitSignBatch(signJobs []signJob) {
// denoting if signature verification was valid or not. The passed cancelChan // denoting if signature verification was valid or not. The passed cancelChan
// allows the caller to cancel all pending jobs in the case that they wish to // allows the caller to cancel all pending jobs in the case that they wish to
// bail early. // bail early.
func (s *sigPool) SubmitVerifyBatch(verifyJobs []verifyJob, func (s *SigPool) SubmitVerifyBatch(verifyJobs []VerifyJob,
cancelChan chan struct{}) <-chan *htlcIndexErr { cancelChan chan struct{}) <-chan *HtlcIndexErr {
errChan := make(chan *htlcIndexErr, len(verifyJobs)) errChan := make(chan *HtlcIndexErr, len(verifyJobs))
for _, job := range verifyJobs { for _, job := range verifyJobs {
job.cancel = cancelChan job.Cancel = cancelChan
job.errResp = errChan job.ErrResp = errChan
select { select {
case s.verifyJobs <- job: case s.verifyJobs <- job:
case <-job.cancel: case <-job.Cancel:
return errChan return errChan
} }
} }

@ -309,18 +309,24 @@ func CreateTestChannels() (*LightningChannel, *LightningChannel, func(), error)
} }
// TODO(roasbeef): make mock version of pre-image store // TODO(roasbeef): make mock version of pre-image store
alicePool := NewSigPool(1, aliceSigner)
channelAlice, err := NewLightningChannel( channelAlice, err := NewLightningChannel(
aliceSigner, pCache, aliceChannelState, aliceSigner, pCache, aliceChannelState, alicePool,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
alicePool.Start()
bobPool := NewSigPool(1, bobSigner)
channelBob, err := NewLightningChannel( channelBob, err := NewLightningChannel(
bobSigner, pCache, bobChannelState, bobSigner, pCache, bobChannelState, bobPool,
) )
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
bobPool.Start()
err = SetStateNumHint( err = SetStateNumHint(
aliceCommitTx, 0, channelAlice.stateHintObfuscator, aliceCommitTx, 0, channelAlice.stateHintObfuscator,
@ -346,8 +352,8 @@ func CreateTestChannels() (*LightningChannel, *LightningChannel, func(), error)
os.RemoveAll(bobPath) os.RemoveAll(bobPath)
os.RemoveAll(alicePath) os.RemoveAll(alicePath)
channelAlice.Stop() alicePool.Stop()
channelBob.Stop() bobPool.Stop()
} }
// Now that the channel are open, simulate the start of a session by // Now that the channel are open, simulate the start of a session by

19
peer.go

@ -383,6 +383,7 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
for _, dbChan := range chans { for _, dbChan := range chans {
lnChan, err := lnwallet.NewLightningChannel( lnChan, err := lnwallet.NewLightningChannel(
p.server.cc.signer, p.server.witnessBeacon, dbChan, p.server.cc.signer, p.server.witnessBeacon, dbChan,
p.server.sigPool,
) )
if err != nil { if err != nil {
return err return err
@ -400,7 +401,6 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
if dbChan.ChanStatus() != channeldb.Default { if dbChan.ChanStatus() != channeldb.Default {
peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+ peerLog.Warnf("ChannelPoint(%v) has status %v, won't "+
"start.", chanPoint, dbChan.ChanStatus()) "start.", chanPoint, dbChan.ChanStatus())
lnChan.Stop()
continue continue
} }
@ -409,13 +409,11 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
if _, ok := p.failedChannels[chanID]; ok { if _, ok := p.failedChannels[chanID]; ok {
peerLog.Warnf("ChannelPoint(%v) is failed, won't "+ peerLog.Warnf("ChannelPoint(%v) is failed, won't "+
"start.", chanPoint) "start.", chanPoint)
lnChan.Stop()
continue continue
} }
_, currentHeight, err := p.server.cc.chainIO.GetBestBlock() _, currentHeight, err := p.server.cc.chainIO.GetBestBlock()
if err != nil { if err != nil {
lnChan.Stop()
return err return err
} }
@ -425,7 +423,6 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
graph := p.server.chanDB.ChannelGraph() graph := p.server.chanDB.ChannelGraph()
info, p1, p2, err := graph.FetchChannelEdgesByOutpoint(chanPoint) info, p1, p2, err := graph.FetchChannelEdgesByOutpoint(chanPoint)
if err != nil && err != channeldb.ErrEdgeNotFound { if err != nil && err != channeldb.ErrEdgeNotFound {
lnChan.Stop()
return err return err
} }
@ -473,7 +470,6 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
*chanPoint, *chanPoint,
) )
if err != nil { if err != nil {
lnChan.Stop()
return err return err
} }
@ -483,7 +479,6 @@ func (p *peer) loadActiveChannels(chans []*channeldb.OpenChannel) error {
currentHeight, true, currentHeight, true,
) )
if err != nil { if err != nil {
lnChan.Stop()
return fmt.Errorf("unable to add link %v to switch: %v", return fmt.Errorf("unable to add link %v to switch: %v",
chanPoint, err) chanPoint, err)
} }
@ -1596,7 +1591,7 @@ out:
// easily according to its channel ID. // easily according to its channel ID.
lnChan, err := lnwallet.NewLightningChannel( lnChan, err := lnwallet.NewLightningChannel(
p.server.cc.signer, p.server.witnessBeacon, p.server.cc.signer, p.server.witnessBeacon,
newChan, newChan, p.server.sigPool,
) )
if err != nil { if err != nil {
p.activeChanMtx.Unlock() p.activeChanMtx.Unlock()
@ -1624,7 +1619,6 @@ out:
"block: %v", err) "block: %v", err)
peerLog.Errorf(err.Error()) peerLog.Errorf(err.Error())
lnChan.Stop()
newChanReq.err <- err newChanReq.err <- err
continue continue
} }
@ -1636,7 +1630,6 @@ out:
"chain events: %v", err) "chain events: %v", err)
peerLog.Errorf(err.Error()) peerLog.Errorf(err.Error())
lnChan.Stop()
newChanReq.err <- err newChanReq.err <- err
continue continue
} }
@ -1666,7 +1659,6 @@ out:
p.PubKey()) p.PubKey())
peerLog.Errorf(err.Error()) peerLog.Errorf(err.Error())
lnChan.Stop()
newChanReq.err <- err newChanReq.err <- err
continue continue
} }
@ -2019,8 +2011,6 @@ func (p *peer) finalizeChanClosure(chanCloser *channelCloser) {
} }
} }
chanCloser.cfg.channel.Stop()
// Next, we'll launch a goroutine which will request to be notified by // Next, we'll launch a goroutine which will request to be notified by
// the ChainNotifier once the closure transaction obtains a single // the ChainNotifier once the closure transaction obtains a single
// confirmation. // confirmation.
@ -2121,10 +2111,7 @@ func (p *peer) WipeChannel(chanPoint *wire.OutPoint) error {
chanID := lnwire.NewChanIDFromOutPoint(chanPoint) chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
p.activeChanMtx.Lock() p.activeChanMtx.Lock()
if channel, ok := p.activeChannels[chanID]; ok { delete(p.activeChannels, chanID)
channel.Stop()
delete(p.activeChannels, chanID)
}
p.activeChanMtx.Unlock() p.activeChanMtx.Unlock()
// Instruct the HtlcSwitch to close this link as the channel is no // Instruct the HtlcSwitch to close this link as the channel is no

@ -1431,7 +1431,6 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
if err != nil { if err != nil {
return err return err
} }
channel.Stop()
// If a force closure was requested, then we'll handle all the details // If a force closure was requested, then we'll handle all the details
// around the creation and broadcast of the unilateral closure // around the creation and broadcast of the unilateral closure
@ -1667,7 +1666,7 @@ func (r *rpcServer) fetchOpenDbChannel(chanPoint wire.OutPoint) (
// fetchActiveChannel attempts to locate a channel identified by its channel // fetchActiveChannel attempts to locate a channel identified by its channel
// point from the database's set of all currently opened channels and // point from the database's set of all currently opened channels and
// return it as a fully popuplated state machine // return it as a fully populated state machine
func (r *rpcServer) fetchActiveChannel(chanPoint wire.OutPoint) ( func (r *rpcServer) fetchActiveChannel(chanPoint wire.OutPoint) (
*lnwallet.LightningChannel, error) { *lnwallet.LightningChannel, error) {
@ -1680,7 +1679,7 @@ func (r *rpcServer) fetchActiveChannel(chanPoint wire.OutPoint) (
// we create a fully populated channel state machine which // we create a fully populated channel state machine which
// uses the db channel as backing storage. // uses the db channel as backing storage.
return lnwallet.NewLightningChannel( return lnwallet.NewLightningChannel(
r.server.cc.wallet.Cfg.Signer, nil, dbChan, r.server.cc.wallet.Cfg.Signer, nil, dbChan, nil,
) )
} }

@ -11,6 +11,7 @@ import (
"net" "net"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -161,6 +162,8 @@ type server struct {
connMgr *connmgr.ConnManager connMgr *connmgr.ConnManager
sigPool *lnwallet.SigPool
// globalFeatures feature vector which affects HTLCs and thus are also // globalFeatures feature vector which affects HTLCs and thus are also
// advertised to other nodes. // advertised to other nodes.
globalFeatures *lnwire.FeatureVector globalFeatures *lnwire.FeatureVector
@ -258,8 +261,9 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
sphinxRouter := sphinx.NewRouter(privKey, activeNetParams.Params, replayLog) sphinxRouter := sphinx.NewRouter(privKey, activeNetParams.Params, replayLog)
s := &server{ s := &server{
chanDB: chanDB, chanDB: chanDB,
cc: cc, cc: cc,
sigPool: lnwallet.NewSigPool(runtime.NumCPU()*2, cc.signer),
invoices: newInvoiceRegistry(chanDB), invoices: newInvoiceRegistry(chanDB),
@ -947,6 +951,9 @@ func (s *server) Start() error {
// sufficient number of confirmations, or when the input for the // sufficient number of confirmations, or when the input for the
// funding transaction is spent in an attempt at an uncooperative close // funding transaction is spent in an attempt at an uncooperative close
// by the counterparty. // by the counterparty.
if err := s.sigPool.Start(); err != nil {
return err
}
if err := s.cc.chainNotifier.Start(); err != nil { if err := s.cc.chainNotifier.Start(); err != nil {
return err return err
} }
@ -1034,6 +1041,7 @@ func (s *server) Stop() error {
} }
// Shutdown the wallet, funding manager, and the rpc server. // Shutdown the wallet, funding manager, and the rpc server.
s.sigPool.Stop()
s.cc.chainNotifier.Stop() s.cc.chainNotifier.Stop()
s.chanRouter.Stop() s.chanRouter.Stop()
s.htlcSwitch.Stop() s.htlcSwitch.Stop()

@ -298,18 +298,23 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
aliceSigner := &mockSigner{aliceKeyPriv} aliceSigner := &mockSigner{aliceKeyPriv}
bobSigner := &mockSigner{bobKeyPriv} bobSigner := &mockSigner{bobKeyPriv}
alicePool := lnwallet.NewSigPool(1, aliceSigner)
channelAlice, err := lnwallet.NewLightningChannel( channelAlice, err := lnwallet.NewLightningChannel(
aliceSigner, nil, aliceChannelState, aliceSigner, nil, aliceChannelState, alicePool,
) )
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
alicePool.Start()
bobPool := lnwallet.NewSigPool(1, bobSigner)
channelBob, err := lnwallet.NewLightningChannel( channelBob, err := lnwallet.NewLightningChannel(
bobSigner, nil, bobChannelState, bobSigner, nil, bobChannelState, bobPool,
) )
if err != nil { if err != nil {
return nil, nil, nil, nil, err return nil, nil, nil, nil, err
} }
bobPool.Start()
chainIO := &mockChainIO{} chainIO := &mockChainIO{}
wallet := &lnwallet.LightningWallet{ wallet := &lnwallet.LightningWallet{