Repository: lightningnetwork/lnd
Pull request #9074 (2024-09-09 11:12:29)

lnwallet: aux signer batching fixes
## Change Description
Description of change / link to associated issue.

Addresses https://github.com/lightninglabs/taproot-assets/issues/1114 . Improves the safety of cancel channel usage, as the cancel signal can now be sent by `tapd` as well as `lnd`.

## Steps to Test
Steps for reviewers to follow to test the change.

Unclear; not sure exactly which events led to the original user issue that caused the stack trace from https://github.com/lightninglabs/taproot-assets/issues/1114 .

## Pull Request Checklist
### Testing
- [ ] Your PR passes all CI checks.
- [ ] Tests covering the positive and negative (error paths) are included.
- [ ] Bug fixes contain tests triggering the bug to prevent regressions.

### Code Style and Documentation
- [ ] The change obeys the [Code Documentation and Commenting](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#CodeDocumentation) guidelines, and lines wrap at 80.
- [ ] Commits follow the [Ideal Git Commit Structure](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md#IdealGitCommitStructure).
- [ ] Any new logging statements use an appropriate subsystem and logging level.
- [ ] Any new lncli commands have appropriate tags in the comments for the rpc in the proto file.
- [ ] [There is a change description in the release notes](https://github.com/lightningnetwork/lnd/tree/master/docs/release-notes), or `[skip ci]` in the commit message for small changes.

📝 Please see our [Contribution Guidelines](https://github.com/lightningnetwork/lnd/blob/master/docs/code_contribution_guidelines.md) for further guidance.


coderabbitai[bot]: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are limited to specific labels.
> 
> <details>
> <summary>Labels to auto review (1)</summary>
> 
> * llm-review
> 
> </details>
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai --><!-- tips_start -->

---

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

<details>
<summary>Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
	- `I pushed a fix in commit <commit_id>.`
	- `Generate unit testing code for this file.`
	- `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
	- `@coderabbitai generate unit testing code for this file.`
	-	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
	- `@coderabbitai generate interesting stats about this repository and render them as a table.`
	- `@coderabbitai show all the console.log statements in this repository.`
	- `@coderabbitai read src/utils.ts and generate unit testing code.`
	- `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
	- `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://coderabbit.ai/docs) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>



<!-- tips_end -->
jharveyb: Updated to sort in the correct spot; we lose the index info when processing the job, so this sort should be earlier.

Migrating to `slices.SortFunc` is the recommended change:

https://pkg.go.dev/sort#Slice

And in this case, `-` is the direct replacement for `<`:

https://pkg.go.dev/slices@go1.21.4#example-SortFunc-CaseInsensitive

Will add more changes to this PR following this sketch:

https://github.com/lightninglabs/taproot-assets/issues/1114#issuecomment-2343280960
jharveyb: Not sure what's up with that linter error, but I observe the same error locally. Seems like imports are already properly ordered.
guggero: > Not sure what's up with that linter error, but I observe the same error locally. Seems like imports are already properly ordered.

That's a false positive with the `slices` package. Should be fixed in `master`, the linter is just out of date in this branch.
dstadulis: Investigating if a rogue quit signal is occurring.

Patch:
diff --git a/.gitignore b/.gitignore
index a44a7b8f5..6be439ed7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,6 +66,7 @@ profile.tmp
 .DS_Store
 
 .vscode
+*.code-workspace
 
 # Coverage test
 coverage.txt
diff --git a/htlcswitch/interceptable_switch.go b/htlcswitch/interceptable_switch.go
index 67832a985..55f5980b6 100644
--- a/htlcswitch/interceptable_switch.go
+++ b/htlcswitch/interceptable_switch.go
@@ -92,7 +92,7 @@ type InterceptableSwitch struct {
 
 type interceptedPackets struct {
 	packets  []*htlcPacket
-	linkQuit chan struct{}
+	linkQuit <-chan struct{}
 	isReplay bool
 }
 
@@ -442,8 +442,8 @@ func (s *InterceptableSwitch) Resolve(res *FwdResolution) error {
 // interceptor. If the interceptor signals the resume action, the htlcs are
 // forwarded to the switch. The link's quit signal should be provided to allow
 // cancellation of forwarding during link shutdown.
-func (s *InterceptableSwitch) ForwardPackets(linkQuit chan struct{}, isReplay bool,
-	packets ...*htlcPacket) error {
+func (s *InterceptableSwitch) ForwardPackets(linkQuit <-chan struct{},
+	isReplay bool, packets ...*htlcPacket) error {
 
 	// Synchronize with the main event loop. This should be light in the
 	// case where there is no interceptor.
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index a59cedf06..01abc6a3e 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -2,6 +2,7 @@ package htlcswitch
 
 import (
 	"bytes"
+	"context"
 	crand "crypto/rand"
 	"crypto/sha256"
 	"fmt"
@@ -101,7 +102,7 @@ type ChannelLinkConfig struct {
 	// switch. The function returns and error in case it fails to send one or
 	// more packets. The link's quit signal should be provided to allow
 	// cancellation of forwarding during link shutdown.
-	ForwardPackets func(chan struct{}, bool, ...*htlcPacket) error
+	ForwardPackets func(<-chan struct{}, bool, ...*htlcPacket) error
 
 	// DecodeHopIterators facilitates batched decoding of HTLC Sphinx onion
 	// blobs, which are then used to inform how to forward an HTLC.
@@ -382,8 +383,9 @@ type channelLink struct {
 	// our next CommitSig.
 	incomingCommitHooks hookMap
 
-	wg   sync.WaitGroup
-	quit chan struct{}
+	wg       sync.WaitGroup
+	quit     context.Context //nolint:containedctx
+	quitFunc context.CancelFunc
 }
 
 // hookMap is a data structure that is used to track the hooks that need to be
@@ -448,6 +450,10 @@ func NewChannelLink(cfg ChannelLinkConfig,
 	channel *lnwallet.LightningChannel) ChannelLink {
 
 	logPrefix := fmt.Sprintf("ChannelLink(%v):", channel.ChannelPoint())
+	quitCtx, quitFunc := context.WithCancel(context.Background())
+
+	// Initialize the Done channel for our quit context.
+	_ = quitCtx.Done()
 
 	return &channelLink{
 		cfg:                 cfg,
@@ -458,7 +464,8 @@ func NewChannelLink(cfg ChannelLinkConfig,
 		flushHooks:          newHookMap(),
 		outgoingCommitHooks: newHookMap(),
 		incomingCommitHooks: newHookMap(),
-		quit:                make(chan struct{}),
+		quit:                quitCtx,
+		quitFunc:            quitFunc,
 	}
 }
 
@@ -573,7 +580,7 @@ func (l *channelLink) Stop() {
 
 	l.hodlQueue.Stop()
 
-	close(l.quit)
+	l.quitFunc()
 	l.wg.Wait()
 
 	// Now that the htlcManager has completely exited, reset the packet
@@ -660,7 +667,7 @@ func (l *channelLink) IsFlushing(linkDirection LinkDirection) bool {
 func (l *channelLink) OnFlushedOnce(hook func()) {
 	select {
 	case l.flushHooks.newTransients <- hook:
-	case <-l.quit:
+	case <-l.quit.Done():
 	}
 }
 
@@ -679,7 +686,7 @@ func (l *channelLink) OnCommitOnce(direction LinkDirection, hook func()) {
 
 	select {
 	case queue <- hook:
-	case <-l.quit:
+	case <-l.quit.Done():
 	}
 }
 
@@ -889,7 +896,7 @@ func (l *channelLink) syncChanStates() error {
 		// party, so we'll process the message  in order to determine
 		// if we need to re-transmit any messages to the remote party.
 		msgsToReSend, openedCircuits, closedCircuits, err =
-			l.channel.ProcessChanSyncMsg(remoteChanSyncMsg)
+			l.channel.ProcessChanSyncMsg(l.quit, remoteChanSyncMsg)
 		if err != nil {
 			return err
 		}
@@ -918,7 +925,7 @@ func (l *channelLink) syncChanStates() error {
 			l.cfg.Peer.SendMessage(false, msg)
 		}
 
-	case <-l.quit:
+	case <-l.quit.Done():
 		return ErrLinkShuttingDown
 	}
 
@@ -1041,7 +1048,7 @@ func (l *channelLink) fwdPkgGarbager() {
 					err)
 				continue
 			}
-		case <-l.quit:
+		case <-l.quit.Done():
 			return
 		}
 	}
@@ -1442,7 +1449,7 @@ func (l *channelLink) htlcManager() {
 				)
 			}
 
-		case <-l.quit:
+		case <-l.quit.Done():
 			return
 		}
 	}
@@ -2272,7 +2279,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
 		}
 
 		select {
-		case <-l.quit:
+		case <-l.quit.Done():
 			return
 		default:
 		}
@@ -2334,7 +2341,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
 		}
 
 		select {
-		case <-l.quit:
+		case <-l.quit.Done():
 			return
 		default:
 		}
@@ -2541,7 +2548,7 @@ func (l *channelLink) updateCommitTx() error {
 		return nil
 	}
 
-	newCommit, err := l.channel.SignNextCommitment()
+	newCommit, err := l.channel.SignNextCommitment(l.quit)
 	if err == lnwallet.ErrNoWindow {
 		l.cfg.PendingCommitTicker.Resume()
 		l.log.Trace("PendingCommitTicker resumed")
@@ -2582,7 +2589,7 @@ func (l *channelLink) updateCommitTx() error {
 	}
 
 	select {
-	case <-l.quit:
+	case <-l.quit.Done():
 		return ErrLinkShuttingDown
 	default:
 	}
@@ -3057,7 +3064,7 @@ func (l *channelLink) handleSwitchPacket(pkt *htlcPacket) error {
 // NOTE: Part of the ChannelLink interface.
 func (l *channelLink) HandleChannelUpdate(message lnwire.Message) {
 	select {
-	case <-l.quit:
+	case <-l.quit.Done():
 		// Return early if the link is already in the process of
 		// quitting. It doesn't make sense to hand the message to the
 		// mailbox here.
@@ -3744,7 +3751,7 @@ func (l *channelLink) forwardBatch(replay bool, packets ...*htlcPacket) {
 		filteredPkts = append(filteredPkts, pkt)
 	}
 
-	err := l.cfg.ForwardPackets(l.quit, replay, filteredPkts...)
+	err := l.cfg.ForwardPackets(l.quit.Done(), replay, filteredPkts...)
 	if err != nil {
 		log.Errorf("Unhandled error while reforwarding htlc "+
 			"settle/fail over htlcswitch: %v", err)
diff --git a/htlcswitch/mailbox.go b/htlcswitch/mailbox.go
index a729e3ba5..7f5bf5b37 100644
--- a/htlcswitch/mailbox.go
+++ b/htlcswitch/mailbox.go
@@ -94,7 +94,7 @@ type mailBoxConfig struct {
 	// forwardPackets send a varidic number of htlcPackets to the switch to
 	// be routed. A quit channel should be provided so that the call can
 	// properly exit during shutdown.
-	forwardPackets func(chan struct{}, ...*htlcPacket) error
+	forwardPackets func(<-chan struct{}, ...*htlcPacket) error
 
 	// clock is a time source for the mailbox.
 	clock clock.Clock
@@ -801,7 +801,7 @@ type mailOrchConfig struct {
 	// forwardPackets send a varidic number of htlcPackets to the switch to
 	// be routed. A quit channel should be provided so that the call can
 	// properly exit during shutdown.
-	forwardPackets func(chan struct{}, ...*htlcPacket) error
+	forwardPackets func(<-chan struct{}, ...*htlcPacket) error
 
 	// clock is a time source for the generated mailboxes.
 	clock clock.Clock
diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go
index 2c0262581..cad7d6e6b 100644
--- a/htlcswitch/switch.go
+++ b/htlcswitch/switch.go
@@ -654,7 +654,7 @@ func (s *Switch) IsForwardedHTLC(chanID lnwire.ShortChannelID,
 // given to forward them through the router. The sending link's quit channel is
 // used to prevent deadlocks when the switch stops a link in the midst of
 // forwarding.
-func (s *Switch) ForwardPackets(linkQuit chan struct{},
+func (s *Switch) ForwardPackets(linkQuit <-chan struct{},
 	packets ...*htlcPacket) error {
 
 	var (
@@ -832,7 +832,7 @@ func (s *Switch) logFwdErrs(num *int, wg *sync.WaitGroup, fwdChan chan error) {
 // receive a shutdown requuest. This method does not wait for a response from
 // the htlcForwarder before returning.
 func (s *Switch) routeAsync(packet *htlcPacket, errChan chan error,
-	linkQuit chan struct{}) error {
+	linkQuit <-chan struct{}) error {
 
 	command := &plexPacket{
 		pkt: packet,
diff --git a/lnwallet/aux_signer.go b/lnwallet/aux_signer.go
index a2a4aa2e7..74b2d309f 100644
--- a/lnwallet/aux_signer.go
+++ b/lnwallet/aux_signer.go
@@ -47,18 +47,19 @@ type AuxSigJob struct {
 	BaseAuxJob
 
 	// Resp is a channel that will be used to send the result of the sign
-	// job.
+	// job. This channel MUST be buffered.
 	Resp chan AuxSigJobResp
 
-	// Cancel is a channel that should be closed if the caller wishes to
-	// abandon all pending sign jobs part of a single batch.
-	Cancel chan struct{}
+	// Cancel is a channel that is closed by the caller if they wish to
+	// abandon all pending sign jobs part of a single batch. This should
+	// never be closed by the validator.
+	Cancel <-chan struct{}
 }
 
 // NewAuxSigJob creates a new AuxSigJob.
 func NewAuxSigJob(sigJob SignJob, keyRing CommitmentKeyRing, incoming bool,
 	htlc PaymentDescriptor, commitBlob fn.Option[tlv.Blob],
-	htlcLeaf input.AuxTapLeaf, cancelChan chan struct{}) AuxSigJob {
+	htlcLeaf input.AuxTapLeaf, cancelChan <-chan struct{}) AuxSigJob {
 
 	return AuxSigJob{
 		SignDesc: sigJob.SignDesc,
diff --git a/lnwallet/channel.go b/lnwallet/channel.go
index 4a0716055..faa9a284e 100644
--- a/lnwallet/channel.go
+++ b/lnwallet/channel.go
@@ -2,12 +2,14 @@ package lnwallet
 
 import (
 	"bytes"
+	"cmp"
 	"container/list"
+	"context"
 	"crypto/sha256"
 	"errors"
 	"fmt"
 	"math"
-	"sort"
+	"slices"
 	"sync"
 
 	"github.com/btcsuite/btcd/blockchain"
@@ -137,6 +139,10 @@ var (
 	// errNoPartialSig is returned when a partial signature is required,
 	// but none is found.
 	errNoPartialSig = errors.New("no partial signature found")
+
+	// errQuit is returned when a quit signal was received, interrupting the
+	// current operation.
+	errQuit = errors.New("received quit signal")
 )
 
 // ErrCommitSyncLocalDataLoss is returned in the case that we receive a valid
@@ -4525,7 +4531,9 @@ type NewCommitState struct {
 // for the remote party's commitment are also returned.
 //
 //nolint:funlen
-func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
+func (lc *LightningChannel) SignNextCommitment(
+	ctx context.Context) (*NewCommitState, error) {
+
 	lc.Lock()
 	defer lc.Unlock()
 
@@ -4626,6 +4634,17 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
 	if err != nil {
 		return 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.
+	slices.SortFunc(sigBatch, func(i, j SignJob) int {
+		return cmp.Compare(i.OutputIndex, j.OutputIndex)
+	})
+	slices.SortFunc(auxSigBatch, func(i, j AuxSigJob) int {
+		return cmp.Compare(i.OutputIndex, j.OutputIndex)
+	})
+
 	lc.sigPool.SubmitSignBatch(sigBatch)
 
 	err = fn.MapOptionZ(lc.auxSigner, func(a AuxSigner) error {
@@ -4675,23 +4694,19 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
 		}
 	}
 
-	// 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.
-	sort.Slice(sigBatch, func(i, j int) bool {
-		return sigBatch[i].OutputIndex < sigBatch[j].OutputIndex
-	})
-	sort.Slice(auxSigBatch, func(i, j int) bool {
-		return auxSigBatch[i].OutputIndex < auxSigBatch[j].OutputIndex
-	})
-
-	// With the jobs sorted, we'll now iterate through all the responses to
-	// gather each of the signatures in order.
+	// Iterate through all the responses to gather each of the signatures
+	// in the order they were submitted.
 	htlcSigs = make([]lnwire.Sig, 0, len(sigBatch))
 	auxSigs := make([]fn.Option[tlv.Blob], 0, len(auxSigBatch))
 	for i := range sigBatch {
 		htlcSigJob := sigBatch[i]
-		jobResp := <-htlcSigJob.Resp
+		var jobResp SignJobResp
+
+		select {
+		case jobResp = <-htlcSigJob.Resp:
+		case <-ctx.Done():
+			return nil, errQuit
+		}
 
 		// If an error occurred, then we'll cancel any other active
 		// jobs.
@@ -4707,7 +4722,13 @@ func (lc *LightningChannel) SignNextCommitment() (*NewCommitState, error) {
 		}
 
 		auxHtlcSigJob := auxSigBatch[i]
-		auxJobResp := <-auxHtlcSigJob.Resp
+		var auxJobResp AuxSigJobResp
+
+		select {
+		case auxJobResp = <-auxHtlcSigJob.Resp:
+		case <-ctx.Done():
+			return nil, errQuit
+		}
 
 		// If an error occurred, then we'll cancel any other active
 		// jobs.
@@ -4800,7 +4821,7 @@ func (lc *LightningChannel) resignMusigCommit(commitTx *wire.MsgTx,
 // previous commitment txn. This allows the link to clear its mailbox of those
 // circuits in case they are still in memory, and ensure the switch's circuit
 // map has been updated by deleting the closed circuits.
-func (lc *LightningChannel) ProcessChanSyncMsg(
+func (lc *LightningChannel) ProcessChanSyncMsg(ctx context.Context,
 	msg *lnwire.ChannelReestablish) ([]lnwire.Message, []models.CircuitKey,
 	[]models.CircuitKey, error) {
 
@@ -4964,7 +4985,7 @@ func (lc *LightningChannel) ProcessChanSyncMsg(
 		// revocation, but also initiate a state transition to re-sync
 		// them.
 		if lc.OweCommitment() {
-			newCommit, err := lc.SignNextCommitment()
+			newCommit, err := lc.SignNextCommitment(ctx)
 			switch {
 
 			// If we signed this state, then we'll accumulate
diff --git a/lnwallet/mock.go b/lnwallet/mock.go
index 89c31ad98..1e531b922 100644
--- a/lnwallet/mock.go
+++ b/lnwallet/mock.go
@@ -391,6 +391,14 @@ func (*mockChainIO) GetBlockHeader(
 
 type auxSignerMock struct {
 	mock.Mock
+
+	jobHandlerFunc func([]AuxSigJob)
+}
+
+func NewAuxSignerMock(jobHandler func([]AuxSigJob)) *auxSignerMock {
+	return &auxSignerMock{
+		jobHandlerFunc: jobHandler,
+	}
 }
 
 func (a *auxSignerMock) SubmitSecondLevelSigBatch(
@@ -398,12 +406,7 @@ func (a *auxSignerMock) SubmitSecondLevelSigBatch(
 	commitTx *wire.MsgTx, sigJobs []AuxSigJob) error {
 
 	args := a.Called(chanState, commitTx, sigJobs)
-
-	// While we return, we'll also send back an instant response for the
-	// set of jobs.
-	for _, sigJob := range sigJobs {
-		sigJob.Resp <- AuxSigJobResp{}
-	}
+	a.jobHandlerFunc(sigJobs)
 
 	return args.Error(0)
 }
diff --git a/lnwallet/sigpool.go b/lnwallet/sigpool.go
index 78e66d948..3ee6c399a 100644
--- a/lnwallet/sigpool.go
+++ b/lnwallet/sigpool.go
@@ -47,17 +47,17 @@ type VerifyJob struct {
 	// TODO(roasbeef): remove -- never actually used?
 	HtlcIndex uint64
 
-	// Cancel is a channel that should be closed if the caller wishes to
+	// Cancel is a channel that is closed by the caller if they wish to
 	// 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
-	// batch has been returned as invalid, as there is no need to verify
-	// the remainder of the signatures.
-	Cancel chan struct{}
+	// channel is closed in the case that a single signature in a batch has
+	// been returned as invalid, as there is no need to verify the remainder
+	// of the signatures.
+	Cancel <-chan struct{}
 
 	// 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
 	// error will be passed. Otherwise, a concrete error detailing the
-	// issue will be passed.
+	// issue will be passed. This channel MUST be buffered.
 	ErrResp chan *HtlcIndexErr
 }
 
@@ -88,12 +88,13 @@ type SignJob struct {
 	// transaction being signed.
 	OutputIndex int32
 
-	// Cancel is a channel that should be closed if the caller wishes to
-	// abandon all pending sign jobs part of a single batch.
-	Cancel chan struct{}
+	// Cancel is a channel that is closed by the caller if they wish to
+	// abandon all pending sign jobs part of a single batch. This should
+	// never be closed by the validator.
+	Cancel <-chan struct{}
 
 	// Resp is the channel that the response to this particular SignJob
-	// will be sent over.
+	// will be sent over. This channel MUST be buffered.
 	//
 	// TODO(roasbeef): actually need to allow caller to set, need to retain
 	// order mark commit sig as special


Test patch:
diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go
index 489a60518..26ffe2068 100644
--- a/contractcourt/chain_watcher_test.go
+++ b/contractcourt/chain_watcher_test.go
@@ -2,6 +2,7 @@ package contractcourt
 
 import (
 	"bytes"
+	"context"
 	"crypto/sha256"
 	"fmt"
 	"testing"
@@ -145,7 +146,9 @@ func TestChainWatcherRemoteUnilateralClosePendingCommit(t *testing.T) {
 
 	// With the HTLC added, we'll now manually initiate a state transition
 	// from Alice to Bob.
-	_, err = aliceChannel.SignNextCommitment()
+	testQuit, testQuitFunc := context.WithCancel(context.Background())
+	_ = testQuitFunc
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go
index 89d0ef319..0640e211d 100644
--- a/htlcswitch/link_isolated_test.go
+++ b/htlcswitch/link_isolated_test.go
@@ -1,6 +1,7 @@
 package htlcswitch
 
 import (
+	"context"
 	"crypto/sha256"
 	"testing"
 	"time"
@@ -94,7 +95,9 @@ func (l *linkTestContext) receiveHtlcAliceToBob() {
 func (l *linkTestContext) sendCommitSigBobToAlice(expHtlcs int) {
 	l.t.Helper()
 
-	sigs, err := l.bobChannel.SignNextCommitment()
+	testQuit, testQuitFunc := context.WithCancel(context.Background())
+	_ = testQuitFunc
+	sigs, err := l.bobChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		l.t.Fatalf("error signing commitment: %v", err)
 	}
diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go
index 93c76bd40..7833efb48 100644
--- a/htlcswitch/link_test.go
+++ b/htlcswitch/link_test.go
@@ -2189,17 +2189,21 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 		return nil
 	}
 
+	forwardPackets := func(linkQuit <-chan struct{}, _ bool,
+		packets ...*htlcPacket) error {
+
+		return aliceSwitch.ForwardPackets(linkQuit, packets...)
+	}
+
 	// Instantiate with a long interval, so that we can precisely control
 	// the firing via force feeding.
 	bticker := ticker.NewForce(time.Hour)
 	aliceCfg := ChannelLinkConfig{
-		FwrdingPolicy: globalPolicy,
-		Peer:          alicePeer,
-		BestHeight:    aliceSwitch.BestHeight,
-		Circuits:      aliceSwitch.CircuitModifier(),
-		ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error {
-			return aliceSwitch.ForwardPackets(linkQuit, packets...)
-		},
+		FwrdingPolicy:      globalPolicy,
+		Peer:               alicePeer,
+		BestHeight:         aliceSwitch.BestHeight,
+		Circuits:           aliceSwitch.CircuitModifier(),
+		ForwardPackets:     forwardPackets,
 		DecodeHopIterators: decoder.DecodeHopIterators,
 		ExtractErrorEncrypter: func(*btcec.PublicKey) (
 			hop.ErrorEncrypter, lnwire.FailCode) {
@@ -2240,12 +2244,14 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 		return aliceSwitch.AddLink(aliceLink)
 	}
 	go func() {
-		for {
-			select {
-			case <-notifyUpdateChan:
-			case <-aliceLink.(*channelLink).quit:
-				close(doneChan)
-				return
+		if chanLink, ok := aliceLink.(*channelLink); ok {
+			for {
+				select {
+				case <-notifyUpdateChan:
+				case <-chanLink.quit.Done():
+					close(doneChan)
+					return
+				}
 			}
 		}
 	}()
@@ -2312,7 +2318,7 @@ func handleStateUpdate(link *channelLink,
 	}
 	link.HandleChannelUpdate(remoteRev)
 
-	remoteSigs, err := remoteChannel.SignNextCommitment()
+	remoteSigs, err := remoteChannel.SignNextCommitment(link.quit)
 	if err != nil {
 		return err
 	}
@@ -2355,7 +2361,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
 		// Trigger update by ticking the batchTicker.
 		select {
 		case batchTick <- time.Now():
-		case <-link.quit:
+		case <-link.quit.Done():
 			return fmt.Errorf("link shutting down")
 		}
 		return handleStateUpdate(link, remoteChannel)
@@ -2363,7 +2369,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
 
 	// The remote is triggering the state update, emulate this by
 	// signing and sending CommitSig to the link.
-	remoteSigs, err := remoteChannel.SignNextCommitment()
+	remoteSigs, err := remoteChannel.SignNextCommitment(link.quit)
 	if err != nil {
 		return err
 	}
@@ -4849,17 +4855,21 @@ func (h *persistentLinkHarness) restartLink(
 		return nil
 	}
 
+	forwardPackets := func(linkQuit <-chan struct{}, _ bool,
+		packets ...*htlcPacket) error {
+
+		return h.hSwitch.ForwardPackets(linkQuit, packets...)
+	}
+
 	// Instantiate with a long interval, so that we can precisely control
 	// the firing via force feeding.
 	bticker := ticker.NewForce(time.Hour)
 	aliceCfg := ChannelLinkConfig{
-		FwrdingPolicy: globalPolicy,
-		Peer:          alicePeer,
-		BestHeight:    h.hSwitch.BestHeight,
-		Circuits:      h.hSwitch.CircuitModifier(),
-		ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error {
-			return h.hSwitch.ForwardPackets(linkQuit, packets...)
-		},
+		FwrdingPolicy:      globalPolicy,
+		Peer:               alicePeer,
+		BestHeight:         h.hSwitch.BestHeight,
+		Circuits:           h.hSwitch.CircuitModifier(),
+		ForwardPackets:     forwardPackets,
 		DecodeHopIterators: decoder.DecodeHopIterators,
 		ExtractErrorEncrypter: func(*btcec.PublicKey) (
 			hop.ErrorEncrypter, lnwire.FailCode) {
@@ -4904,12 +4914,14 @@ func (h *persistentLinkHarness) restartLink(
 		return nil, nil, err
 	}
 	go func() {
-		for {
-			select {
-			case <-notifyUpdateChan:
-			case <-aliceLink.(*channelLink).quit:
-				close(doneChan)
-				return
+		if chanLink, ok := aliceLink.(*channelLink); ok {
+			for {
+				select {
+				case <-notifyUpdateChan:
+				case <-chanLink.quit.Done():
+					close(doneChan)
+					return
+				}
 			}
 		}
 	}()
@@ -5892,7 +5904,9 @@ func TestChannelLinkFail(t *testing.T) {
 
 				// Sign a commitment that will include
 				// signature for the HTLC just sent.
-				sigs, err := remoteChannel.SignNextCommitment()
+				sigs, err := remoteChannel.SignNextCommitment(
+					c.quit,
+				)
 				if err != nil {
 					t.Fatalf("error signing commitment: %v",
 						err)
@@ -5934,7 +5948,9 @@ func TestChannelLinkFail(t *testing.T) {
 
 				// Sign a commitment that will include
 				// signature for the HTLC just sent.
-				sigs, err := remoteChannel.SignNextCommitment()
+				sigs, err := remoteChannel.SignNextCommitment(
+					c.quit,
+				)
 				if err != nil {
 					t.Fatalf("error signing commitment: %v",
 						err)
@@ -7018,7 +7034,7 @@ func TestPipelineSettle(t *testing.T) {
 	// erroneously forwarded. If the forwardChan is closed before the last
 	// step, then the test will fail.
 	forwardChan := make(chan struct{})
-	fwdPkts := func(c chan struct{}, _ bool, hp ...*htlcPacket) error {
+	fwdPkts := func(c <-chan struct{}, _ bool, hp ...*htlcPacket) error {
 		close(forwardChan)
 		return nil
 	}
@@ -7204,7 +7220,7 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 	aliceMsgs := mockPeer.sentMsgs
 	switchChan := make(chan *htlcPacket)
 
-	coreLink.cfg.ForwardPackets = func(linkQuit chan struct{}, _ bool,
+	coreLink.cfg.ForwardPackets = func(linkQuit <-chan struct{}, _ bool,
 		packets ...*htlcPacket) error {
 
 		for _, p := range packets {
diff --git a/htlcswitch/mailbox_test.go b/htlcswitch/mailbox_test.go
index e48aacfcd..aa2f1b3ee 100644
--- a/htlcswitch/mailbox_test.go
+++ b/htlcswitch/mailbox_test.go
@@ -250,7 +250,7 @@ func newMailboxContext(t *testing.T, startTime time.Time,
 	return ctx
 }
 
-func (c *mailboxContext) forward(_ chan struct{},
+func (c *mailboxContext) forward(_ <-chan struct{},
 	pkts ...*htlcPacket) error {
 
 	for _, pkt := range pkts {
@@ -706,7 +706,7 @@ func TestMailOrchestrator(t *testing.T) {
 	// First, we'll create a new instance of our orchestrator.
 	mo := newMailOrchestrator(&mailOrchConfig{
 		failMailboxUpdate: failMailboxUpdate,
-		forwardPackets: func(_ chan struct{},
+		forwardPackets: func(_ <-chan struct{},
 			pkts ...*htlcPacket) error {
 
 			return nil
diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go
index 9a72197ec..76e7db753 100644
--- a/htlcswitch/test_utils.go
+++ b/htlcswitch/test_utils.go
@@ -1127,15 +1127,19 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
 		return nil
 	}
 
+	forwardPackets := func(linkQuit <-chan struct{}, _ bool,
+		packets ...*htlcPacket) error {
+
+		return server.htlcSwitch.ForwardPackets(linkQuit, packets...)
+	}
+
 	link := NewChannelLink(
 		ChannelLinkConfig{
-			BestHeight:    server.htlcSwitch.BestHeight,
-			FwrdingPolicy: h.globalPolicy,
-			Peer:          peer,
-			Circuits:      server.htlcSwitch.CircuitModifier(),
-			ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error {
-				return server.htlcSwitch.ForwardPackets(linkQuit, packets...)
-			},
+			BestHeight:         server.htlcSwitch.BestHeight,
+			FwrdingPolicy:      h.globalPolicy,
+			Peer:               peer,
+			Circuits:           server.htlcSwitch.CircuitModifier(),
+			ForwardPackets:     forwardPackets,
 			DecodeHopIterators: decoder.DecodeHopIterators,
 			ExtractErrorEncrypter: func(*btcec.PublicKey) (
 				hop.ErrorEncrypter, lnwire.FailCode) {
@@ -1176,12 +1180,14 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
 	}
 
 	go func() {
-		for {
-			select {
-			case <-notifyUpdateChan:
-			case <-link.(*channelLink).quit:
-				close(doneChan)
-				return
+		if chanLink, ok := link.(*channelLink); ok {
+			for {
+				select {
+				case <-notifyUpdateChan:
+				case <-chanLink.quit.Done():
+					close(doneChan)
+					return
+				}
 			}
 		}
 	}()
diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go
index 4d972ece4..6d30916c1 100644
--- a/lnwallet/channel_test.go
+++ b/lnwallet/channel_test.go
@@ -3,13 +3,16 @@ package lnwallet
 import (
 	"bytes"
 	"container/list"
+	"context"
 	"crypto/sha256"
+	"errors"
 	"fmt"
 	"math/rand"
 	"reflect"
 	"runtime"
 	"testing"
 	"testing/quick"
+	"time"
 
 	"github.com/btcsuite/btcd/blockchain"
 	"github.com/btcsuite/btcd/btcec/v2"
@@ -22,6 +25,7 @@ import (
 	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
+	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/lntypes"
@@ -49,6 +53,18 @@ func createHTLC(id int, amount lnwire.MilliSatoshi) (*lnwire.UpdateAddHTLC, [32]
 	}, returnPreimage
 }
 
+// addAndReceiveHTLC adds an HTLC as local to the first channel, and as remote
+// to a second channel. The HTLC ID is not modified.
+func addAndReceiveHTLC(t *testing.T, channel1, channel2 *LightningChannel,
+	htlc *lnwire.UpdateAddHTLC, openKey *models.CircuitKey) {
+
+	_, err := channel1.AddHTLC(htlc, openKey)
+	require.NoErrorf(t, err, "channel 1 unable to add htlc: %v", err)
+
+	_, err = channel2.ReceiveHTLC(htlc)
+	require.NoErrorf(t, err, "channel 2 unable to recv htlc: %v", err)
+}
+
 func assertOutputExistsByValue(t *testing.T, commitTx *wire.MsgTx,
 	value btcutil.Amount) {
 
@@ -110,7 +126,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool,
 	// we expect the messages to be ordered, Bob will receive the HTLC we
 	// just sent before he receives this signature, so the signature will
 	// cover the HTLC.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Bob receives this signature message, and checks that this covers the
@@ -128,7 +144,7 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool,
 	// This signature will cover the HTLC, since Bob will first send the
 	// revocation just created. The revocation also acks every received
 	// HTLC up to the point where Alice sent here signature.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign alice's commitment")
 
 	// Alice then processes this revocation, sending her own revocation for
@@ -241,14 +257,14 @@ func testAddSettleWorkflow(t *testing.T, tweakless bool,
 		t.Fatalf("alice unable to accept settle of outbound htlc: %v", err)
 	}
 
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign settle commitment")
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err, "alice unable to process bob's new commitment")
 
 	aliceRevocation2, _, _, err := aliceChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "alice unable to generate revocation")
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign new commitment")
 
 	fwdPkg, _, _, _, err = bobChannel.ReceiveRevocation(aliceRevocation2)
@@ -435,10 +451,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) {
 	htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000))
 
 	// -----add----->
-	_, err = aliceChannel.AddHTLC(htlc, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Force a state transition to lock in this add on both commitments.
 	// -----sig----->
@@ -457,7 +470,7 @@ func TestChannelZeroAddLocalHeight(t *testing.T) {
 
 	// Bob should send a commitment signature to Alice.
 	// <----sig------
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
@@ -484,14 +497,11 @@ func TestChannelZeroAddLocalHeight(t *testing.T) {
 	htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000))
 
 	// <----add-----
-	_, err = bobChannel.AddHTLC(htlc2, nil)
-	require.NoError(t, err)
-	_, err = newAliceChannel.ReceiveHTLC(htlc2)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil)
 
 	// Bob should now send a commitment signature to Alice.
 	// <----sig-----
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	// Alice should accept the commitment. Previously she would
@@ -544,12 +554,7 @@ func TestCheckCommitTxSize(t *testing.T) {
 	for i := 0; i <= 10; i++ {
 		htlc, _ := createHTLC(i, lnwire.MilliSatoshi(1e7))
 
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("alice unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("bob unable to receive htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 		if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 			t.Fatalf("unable to complete state update: %v", err)
@@ -624,19 +629,14 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) {
 			Expiry:      uint32(numHtlcs - i),
 		}
 
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("alice unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("bob unable to receive htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 	}
 
 	// Have Alice initiate the first half of the commitment dance. The
 	// tie-breaking for commitment sorting won't affect the commitment
 	// signed by Alice because received HTLC scripts commit to the CLTV
 	// directly, so the outputs will have different scriptPubkeys.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign alice's commitment")
 
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
@@ -651,7 +651,7 @@ func testCommitHTLCSigTieBreak(t *testing.T, restart bool) {
 	// the offered HTLC scripts he adds for Alice will need to have the
 	// tie-breaking applied because the CLTV is not committed, but instead
 	// implicit via the construction of the second-level transactions.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign bob's commitment")
 
 	if len(bobNewCommit.PendingHTLCs) != numHtlcs {
@@ -873,23 +873,13 @@ func testForceClose(t *testing.T, testCase *forceCloseTestCase) {
 	// We'll ensure that the HTLC amount is above Alice's dust limit.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	// We'll also a distinct HTLC from Bob -> Alice. This way, Alice will
 	// have both an incoming and outgoing HTLC on her commitment
 	// transaction.
 	htlcBob, preimageBob := createHTLC(0, htlcAmount)
-	if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil)
 
 	// Next, we'll perform two state transitions to ensure that both HTLC's
 	// get fully locked-in.
@@ -1343,12 +1333,7 @@ func TestDustHTLCFees(t *testing.T) {
 	// This HTLC amount should be lower than the dust limits of both nodes.
 	htlcAmount := lnwire.NewMSatFromSatoshis(100)
 	htlc, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("bob unable to receive htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("Can't update the channel state: %v", err)
 	}
@@ -1501,14 +1486,9 @@ func TestHTLCSigNumber(t *testing.T) {
 		for i, htlcSat := range htlcValues {
 			htlcMsat := lnwire.NewMSatFromSatoshis(htlcSat)
 			htlc, _ := createHTLC(i, htlcMsat)
-			_, err := aliceChannel.AddHTLC(htlc, nil)
-			if err != nil {
-				t.Fatalf("alice unable to add htlc: %v", err)
-			}
-			_, err = bobChannel.ReceiveHTLC(htlc)
-			if err != nil {
-				t.Fatalf("bob unable to receive htlc: %v", err)
-			}
+			addAndReceiveHTLC(
+				t, aliceChannel, bobChannel, htlc, nil,
+			)
 		}
 
 		return aliceChannel, bobChannel
@@ -1532,7 +1512,7 @@ func TestHTLCSigNumber(t *testing.T) {
 	// ===================================================================
 	aliceChannel, bobChannel := createChanWithHTLC(aboveDust, aboveDust)
 
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "Error signing next commitment")
 
 	if len(aliceNewCommit.HtlcSigs) != 2 {
@@ -1555,7 +1535,7 @@ func TestHTLCSigNumber(t *testing.T) {
 	// ===================================================================
 	aliceChannel, bobChannel = createChanWithHTLC(aboveDust)
 
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "Error signing next commitment")
 
 	if len(aliceNewCommit.HtlcSigs) != 1 {
@@ -1577,7 +1557,7 @@ func TestHTLCSigNumber(t *testing.T) {
 	// ==============================================================
 	aliceChannel, bobChannel = createChanWithHTLC(belowDust)
 
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "Error signing next commitment")
 
 	// Since the HTLC is below Bob's dust limit, Alice won't need to send
@@ -1595,7 +1575,7 @@ func TestHTLCSigNumber(t *testing.T) {
 	// ================================================================
 	aliceChannel, bobChannel = createChanWithHTLC(aboveDust)
 
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "Error signing next commitment")
 
 	// Since the HTLC is above Bob's dust limit, Alice should send a
@@ -1616,7 +1596,7 @@ func TestHTLCSigNumber(t *testing.T) {
 
 	// Alice should produce only one signature, since one HTLC is below
 	// dust.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "Error signing next commitment")
 
 	if len(aliceNewCommit.HtlcSigs) != 1 {
@@ -1742,12 +1722,7 @@ func TestStateUpdatePersistence(t *testing.T) {
 			OnionBlob:   fakeOnionBlob,
 		}
 
-		if _, err := aliceChannel.AddHTLC(h, nil); err != nil {
-			t.Fatalf("unable to add alice's htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(h); err != nil {
-			t.Fatalf("unable to recv alice's htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, h, nil)
 	}
 	rHash := sha256.Sum256(bobPreimage[:])
 	bobh := &lnwire.UpdateAddHTLC{
@@ -1756,12 +1731,7 @@ func TestStateUpdatePersistence(t *testing.T) {
 		Expiry:      uint32(10),
 		OnionBlob:   fakeOnionBlob,
 	}
-	if _, err := bobChannel.AddHTLC(bobh, nil); err != nil {
-		t.Fatalf("unable to add bob's htlc: %v", err)
-	}
-	if _, err := aliceChannel.ReceiveHTLC(bobh); err != nil {
-		t.Fatalf("unable to recv bob's htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, bobh, nil)
 
 	// Also add a fee update to the update logs.
 	fee := chainfee.SatPerKWeight(333)
@@ -2376,7 +2346,7 @@ func TestUpdateFeeFail(t *testing.T) {
 
 	// Alice sends signature for commitment that does not cover any fee
 	// update.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Bob verifies this commit, meaning that he checks that it is
@@ -2410,12 +2380,7 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
 	// First Alice adds the outgoing HTLC to her local channel's state
 	// update log. Then Alice sends this wire message over to Bob who
 	// adds this htlc to his remote state update log.
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Simulate Alice sending update fee message to bob.
 	fee := chainfee.SatPerKWeight(333)
@@ -2424,11 +2389,11 @@ func TestUpdateFeeConcurrentSig(t *testing.T) {
 	}
 
 	// Alice signs a commitment, and sends this to bob.
-	aliceNewCommits, err := aliceChannel.SignNextCommitment()
+	aliceNewCommits, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// At the same time, Bob signs a commitment.
-	bobNewCommits, err := bobChannel.SignNextCommitment()
+	bobNewCommits, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign alice's commitment")
 
 	// ...that Alice receives.
@@ -2485,12 +2450,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
 	// First Alice adds the outgoing HTLC to her local channel's state
 	// update log. Then Alice sends this wire message over to Bob who
 	// adds this htlc to his remote state update log.
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Simulate Alice sending update fee message to bob.
 	fee := chainfee.SatPerKWeight(333)
@@ -2500,7 +2460,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
 	// Alice signs a commitment, which will cover everything sent to Bob
 	// (the HTLC and the fee update), and everything acked by Bob (nothing
 	// so far).
-	aliceNewCommits, err := aliceChannel.SignNextCommitment()
+	aliceNewCommits, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Bob receives this signature message, and verifies that it is
@@ -2530,7 +2490,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
 
 	// Bob commits to all updates he has received from Alice. This includes
 	// the HTLC he received, and the fee update.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign alice's commitment")
 
 	// Alice receives the revocation of the old one, and can now assume
@@ -2594,12 +2554,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
 	// First Alice adds the outgoing HTLC to her local channel's state
 	// update log. Then Alice sends this wire message over to Bob who
 	// adds this htlc to his remote state update log.
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Simulate Alice sending update fee message to bob
 	fee := chainfee.SatPerKWeight(333)
@@ -2609,7 +2564,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
 	// Bob commits to every change he has sent since last time (none). He
 	// does not commit to the received HTLC and fee update, since Alice
 	// cannot know if he has received them.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Alice receives this signature message, and verifies that it is
@@ -2630,7 +2585,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
 	// Alice will sign next commitment. Since she sent the revocation, she
 	// also ack'ed everything received, but in this case this is nothing.
 	// Since she sent the two updates, this signature will cover those two.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign alice's commitment")
 
 	// Bob gets the signature for the new commitment from Alice. He assumes
@@ -2660,7 +2615,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
 
 	// Bob will send a new signature, which will cover what he just acked:
 	// the HTLC and fee update.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Alice receives revocation from Bob, and can now be sure that Bob
@@ -2750,7 +2705,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
 	// Alice signs a commitment, which will cover everything sent to Bob
 	// (the HTLC and the fee update), and everything acked by Bob (nothing
 	// so far).
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	bobChannel.ReceiveUpdateFee(fee1)
@@ -2796,7 +2751,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
 
 	// Bob commits to all updates he has received from Alice. This includes
 	// the HTLC he received, and the fee update.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign alice's commitment")
 
 	// Alice receives the revocation of the old one, and can now assume that
@@ -2902,7 +2857,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel,
 		}
 	}
 
-	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceChanSyncMsg)
+	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
+		testQuit, aliceChanSyncMsg,
+	)
 	if err != nil {
 		t.Fatalf("line #%v: unable to process ChannelReestablish "+
 			"msg: %v", line, err)
@@ -2912,7 +2869,9 @@ func assertNoChanSyncNeeded(t *testing.T, aliceChannel *LightningChannel,
 			"instead wants to send: %v", line, spew.Sdump(bobMsgsToSend))
 	}
 
-	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobChanSyncMsg)
+	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
+		testQuit, bobChanSyncMsg,
+	)
 	if err != nil {
 		t.Fatalf("line #%v: unable to process ChannelReestablish "+
 			"msg: %v", line, err)
@@ -3102,7 +3061,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) {
 	// Now we'll begin the core of the test itself. Alice will extend a new
 	// commitment to Bob, but the connection drops before Bob can process
 	// it.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// If this is a taproot channel, then we'll generate fresh verification
@@ -3127,7 +3086,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) {
 	// above.
 	assertAliceCommitRetransmit := func() *lnwire.CommitSig {
 		aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
-			bobSyncMsg,
+			testQuit, bobSyncMsg,
 		)
 		if err != nil {
 			t.Fatalf("unable to process chan sync msg: %v", err)
@@ -3217,7 +3176,9 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) {
 
 	// From Bob's Pov he has nothing else to send, so he should conclude he
 	// has no further action remaining.
-	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
+	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
+		testQuit, aliceSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(bobMsgsToSend) != 0 {
 		t.Fatalf("expected bob to send %v messages instead will "+
@@ -3245,7 +3206,7 @@ func testChanSyncOweCommitment(t *testing.T, chanType channeldb.ChannelType) {
 	require.NoError(t, err, "bob unable to process alice's commitment")
 	bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 	_, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
 	require.NoError(t, err, "alice unable to recv revocation")
@@ -3389,8 +3350,14 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) {
 	aliceChannel, bobChannel, err := CreateTestChannels(t, chanType)
 	require.NoError(t, err, "unable to create test channels")
 
-	// We'll now manually attach an aux signer to Alice's channel.
-	auxSigner := &auxSignerMock{}
+	// We'll now manually attach an aux signer to Alice's channel. We'll
+	// set each aux sig job to receive an instant response.
+	emptyAuxSigJobResponder := func(jobs []AuxSigJob) {
+		for _, sigJob := range jobs {
+			sigJob.Resp <- AuxSigJobResp{}
+		}
+	}
+	auxSigner := NewAuxSignerMock(emptyAuxSigJobResponder)
 	aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner)
 
 	var fakeOnionBlob [lnwire.OnionPacketSize]byte
@@ -3414,8 +3381,8 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) {
 	_, err = aliceChannel.AddHTLC(h, nil)
 	require.NoError(t, err, "unable to recv bob's htlc: %v", err)
 
-	// We'll set up the mock to expect calls to PackSigs and also
-	// SubmitSubmitSecondLevelSigBatch.
+	// We'll set up the mock aux signer to expect calls to PackSigs and also
+	// SubmitSecondLevelSigBatch.
 	var sigBlobBuf bytes.Buffer
 	sigBlob := testSigBlob{
 		BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5),
@@ -3434,7 +3401,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) {
 		fn.Some(sigBlobBuf.Bytes()), nil,
 	)
 
-	_, err = aliceChannel.SignNextCommitment()
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	_, err = aliceChannel.GenMusigNonces()
@@ -3446,7 +3413,7 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) {
 	require.NoError(t, err, "unable to produce chan sync msg")
 
 	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
-		bobSyncMsg,
+		testQuit, bobSyncMsg,
 	)
 	require.NoError(t, err)
 	require.Len(t, aliceMsgsToSend, 2)
@@ -3463,6 +3430,154 @@ func TestChanSyncOweCommitmentAuxSigner(t *testing.T) {
 	require.NotEmpty(t, sigMsg.CustomRecords)
 }
 
+// TestAuxSignerShutdown tests that the channel state machine gracefully handles
+// a failure of the aux signer when signing a new commitment.
+func TestAuxSignerShutdown(t *testing.T) {
+	t.Parallel()
+
+	// We'll kick off the test by creating our channels which both are
+	// loaded with 5 BTC each.
+	aliceChannel, bobChannel, err := CreateTestChannels(
+		t, channeldb.SingleFunderTweaklessBit,
+	)
+	require.NoError(t, err, "unable to create test channels")
+
+	auxSignerShutdownErr := errors.New("aux signer shutdown")
+
+	// We know that aux sig jobs will be checked in SignNextCommitment() in
+	// ascending output index order. So we'll fail on the first job that is
+	// out of order, i.e. with an output index greater than its position in
+	// the submitted jobs slice. If the jobs are ordered, we'll fail on the
+	// job that is at the middle of the submitted job slice.
+	failAuxSigJob := func(jobs []AuxSigJob) {
+		for idx, sigJob := range jobs {
+			// Simulate a clean shutdown of the aux signer and send
+			// an error. Skip all remaining jobs.
+			isMiddleJob := idx == len(jobs)/2
+			if int(sigJob.OutputIndex) > idx || isMiddleJob {
+				sigJob.Resp <- AuxSigJobResp{
+					Err: auxSignerShutdownErr,
+				}
+
+				return
+			}
+
+			// If the job is 'in order', send a response with no
+			// error.
+			sigJob.Resp <- AuxSigJobResp{}
+		}
+	}
+
+	auxSigner := NewAuxSignerMock(failAuxSigJob)
+	aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner)
+
+	// Each HTLC amount is 0.01 BTC.
+	htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin)
+
+	// Create enough HTLCs to create multiple sig jobs (one job per HTLC).
+	const numHTLCs = 24
+
+	// Send the specified number of HTLCs.
+	for i := 0; i < numHTLCs; i++ {
+		htlc, _ := createHTLC(i, htlcAmt)
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
+	}
+
+	// We'll set up the mock aux signer to expect calls to PackSigs and also
+	// SubmitSecondLevelSigBatch. The direct return values for this mock aux
+	// signer are nil. The expected error comes from the sig jobs being
+	// passed to failAuxSigJob above, which mimics a faulty aux signer.
+	var sigBlobBuf bytes.Buffer
+	sigBlob := testSigBlob{
+		BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5),
+	}
+	tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record())
+	require.NoError(t, err, "unable to create tlv stream")
+	require.NoError(t, tlvStream.Encode(&sigBlobBuf))
+
+	auxSigner.On(
+		"SubmitSecondLevelSigBatch", mock.Anything, mock.Anything,
+		mock.Anything,
+	).Return(nil).Twice()
+	auxSigner.On(
+		"PackSigs", mock.Anything,
+	).Return(
+		fn.Some(sigBlobBuf.Bytes()), nil,
+	)
+
+	_, err = aliceChannel.SignNextCommitment(testQuit)
+	require.ErrorIs(t, err, auxSignerShutdownErr)
+}
+
+// TestQuitDuringSignNextCommitment tests that the channel state machine can
+// successfully exit on receiving a quit signal when signing a new commitment.
+func TestQuitDuringSignNextCommitment(t *testing.T) {
+	t.Parallel()
+
+	// We'll kick off the test by creating our channels which both are
+	// loaded with 5 BTC each.
+	aliceChannel, bobChannel, err := CreateTestChannels(
+		t, channeldb.SingleFunderTweaklessBit,
+	)
+	require.NoError(t, err, "unable to create test channels")
+
+	// We'll simulate an aux signer that was started successfully, but is
+	// now frozen / inactive. This could happen if the aux signer shut down
+	// without sending an error on any aux sig job error channel.
+	noopAuxSigJob := func(jobs []AuxSigJob) {}
+
+	auxSigner := NewAuxSignerMock(noopAuxSigJob)
+	aliceChannel.auxSigner = fn.Some[AuxSigner](auxSigner)
+
+	// Each HTLC amount is 0.01 BTC.
+	htlcAmt := lnwire.NewMSatFromSatoshis(0.01 * btcutil.SatoshiPerBitcoin)
+
+	// Create enough HTLCs to create multiple sig jobs (one job per HTLC).
+	const numHTLCs = 24
+
+	// Send the specified number of HTLCs.
+	for i := 0; i < numHTLCs; i++ {
+		htlc, _ := createHTLC(i, htlcAmt)
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
+	}
+
+	// We'll set up the mock aux signer to expect calls to PackSigs and also
+	// SubmitSecondLevelSigBatch. The direct return values for this mock aux
+	// signer are nil. The expected error comes from the behavior of
+	// noopAuxSigJob above, which mimics a faulty aux signer.
+	var sigBlobBuf bytes.Buffer
+	sigBlob := testSigBlob{
+		BlobInt: tlv.NewPrimitiveRecord[tlv.TlvType65634, uint16](5),
+	}
+	tlvStream, err := tlv.NewStream(sigBlob.BlobInt.Record())
+	require.NoError(t, err, "unable to create tlv stream")
+	require.NoError(t, tlvStream.Encode(&sigBlobBuf))
+
+	auxSigner.On(
+		"SubmitSecondLevelSigBatch", mock.Anything, mock.Anything,
+		mock.Anything,
+	).Return(nil).Twice()
+	auxSigner.On(
+		"PackSigs", mock.Anything,
+	).Return(
+		fn.Some(sigBlobBuf.Bytes()), nil,
+	)
+
+	quitDelay := time.Millisecond * 20
+	quit, quitFunc := context.WithCancel(context.Background())
+
+	// Alice's channel will be stuck waiting for aux sig job responses until
+	// we send the quit signal. We add an explicit sleep here so that we can
+	// cause a failure if we run the test with a very short timeout.
+	go func() {
+		time.Sleep(quitDelay)
+		quitFunc()
+	}()
+
+	_, err = aliceChannel.SignNextCommitment(quit)
+	require.ErrorIs(t, err, errQuit)
+}
+
 func testChanSyncOweCommitmentPendingRemote(t *testing.T,
 	chanType channeldb.ChannelType) {
 
@@ -3472,7 +3587,9 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T,
 	require.NoError(t, err, "unable to create test channels")
 
 	var fakeOnionBlob [lnwire.OnionPacketSize]byte
-	copy(fakeOnionBlob[:], bytes.Repeat([]byte{0x05}, lnwire.OnionPacketSize))
+	copy(fakeOnionBlob[:], bytes.Repeat(
+		[]byte{0x05}, lnwire.OnionPacketSize,
+	))
 
 	// We'll start off the scenario where Bob send two htlcs to Alice in a
 	// single state update.
@@ -3511,7 +3628,10 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T,
 
 	// Next, Alice settles the HTLCs from Bob in distinct state updates.
 	for i := 0; i < numHtlcs; i++ {
-		err = aliceChannel.SettleHTLC(preimages[i], uint64(i), nil, nil, nil)
+		err = aliceChannel.SettleHTLC(
+			preimages[i], uint64(i), nil, nil, nil,
+		)
+
 		if err != nil {
 			t.Fatalf("unable to settle htlc: %v", err)
 		}
@@ -3520,7 +3640,9 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T,
 			t.Fatalf("unable to settle htlc: %v", err)
 		}
 
-		aliceNewCommit, err := aliceChannel.SignNextCommitment()
+		aliceNewCommit, err := aliceChannel.SignNextCommitment(
+			testQuit,
+		)
 		if err != nil {
 			t.Fatalf("unable to sign commitment: %v", err)
 		}
@@ -3558,7 +3680,7 @@ func testChanSyncOweCommitmentPendingRemote(t *testing.T,
 	}
 
 	// Bob signs the commitment he owes.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// This commitment is expected to contain no htlcs anymore.
@@ -3667,14 +3789,14 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) {
 	//
 	// Alice signs the next state, then Bob receives and sends his
 	// revocation message.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err, "bob unable to process alice's commitment")
 
 	bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 
 	_, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
@@ -3710,7 +3832,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) {
 		t.Helper()
 
 		aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
-			bobSyncMsg,
+			testQuit, bobSyncMsg,
 		)
 		if err != nil {
 			t.Fatalf("unable to process chan sync msg: %v", err)
@@ -3741,7 +3863,9 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) {
 	}
 
 	// From Bob's PoV he shouldn't think that he owes Alice any messages.
-	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
+	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
+		testQuit, aliceSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(bobMsgsToSend) != 0 {
 		t.Fatalf("expected bob to not retransmit, instead has: %v",
@@ -3786,12 +3910,7 @@ func testChanSyncOweRevocation(t *testing.T, chanType channeldb.ChannelType) {
 		Amount:      htlcAmt,
 		Expiry:      uint32(10),
 	}
-	if _, err := aliceChannel.AddHTLC(aliceHtlc, nil); err != nil {
-		t.Fatalf("unable to add alice's htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(aliceHtlc); err != nil {
-		t.Fatalf("unable to recv alice's htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, aliceHtlc, nil)
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("unable to complete alice's state transition: %v", err)
 	}
@@ -3875,7 +3994,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T,
 
 	// Progressing the exchange: Alice will send her signature, Bob will
 	// receive, send a revocation and also a signature for Alice's state.
-	aliceNewCommits, err := aliceChannel.SignNextCommitment()
+	aliceNewCommits, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs)
 	require.NoError(t, err, "bob unable to process alice's commitment")
@@ -3884,7 +4003,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T,
 	// reach Alice before the connection dies.
 	bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 
 	// If we now attempt to resync, then Alice should conclude that she
@@ -3906,7 +4025,9 @@ func testChanSyncOweRevocationAndCommit(t *testing.T,
 		}
 	}
 
-	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
+		testQuit, bobSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(aliceMsgsToSend) != 0 {
 		t.Fatalf("expected alice to not retransmit, instead she's "+
@@ -3917,7 +4038,7 @@ func testChanSyncOweRevocationAndCommit(t *testing.T,
 		t.Helper()
 
 		bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
-			aliceSyncMsg,
+			testQuit, aliceSyncMsg,
 		)
 		if err != nil {
 			t.Fatalf("unable to process chan sync msg: %v", err)
@@ -4080,13 +4201,10 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T,
 		Expiry:      uint32(10),
 		ID:          1,
 	}
-	_, err = bobChannel.AddHTLC(bobHtlc[1], nil)
-	require.NoError(t, err, "unable to add bob's htlc")
-	_, err = aliceChannel.ReceiveHTLC(bobHtlc[1])
-	require.NoError(t, err, "unable to recv bob's htlc")
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc[1], nil)
 
 	// Bob signs the new state update, and sends the signature to Alice.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
@@ -4110,7 +4228,7 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T,
 
 	// Progressing the exchange: Alice will send her signature, with Bob
 	// processing the new state locally.
-	aliceNewCommits, err := aliceChannel.SignNextCommitment()
+	aliceNewCommits, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommits.CommitSigs)
 	require.NoError(t, err, "bob unable to process alice's commitment")
@@ -4140,7 +4258,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T,
 		}
 	}
 
-	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
+		testQuit, bobSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(aliceMsgsToSend) != 0 {
 		t.Fatalf("expected alice to not retransmit, instead she's "+
@@ -4151,7 +4271,9 @@ func testChanSyncOweRevocationAndCommitForceTransition(t *testing.T,
 	// send his RevokeAndAck message again. Additionally, the CommitSig
 	// message that he sends should be sufficient to finalize the state
 	// transition.
-	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
+	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
+		testQuit, aliceSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(bobMsgsToSend) != 2 {
 		t.Fatalf("expected bob to send %v messages, instead "+
@@ -4316,14 +4438,7 @@ func TestChanSyncFailure(t *testing.T) {
 		}
 		index++
 
-		_, err := bobChannel.AddHTLC(bobHtlc, nil)
-		if err != nil {
-			t.Fatalf("unable to add bob's htlc: %v", err)
-		}
-		_, err = aliceChannel.ReceiveHTLC(bobHtlc)
-		if err != nil {
-			t.Fatalf("unable to recv bob's htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil)
 		err = ForceStateTransition(bobChannel, aliceChannel)
 		if err != nil {
 			t.Fatalf("unable to complete bob's state "+
@@ -4349,16 +4464,11 @@ func TestChanSyncFailure(t *testing.T) {
 		}
 		index++
 
-		_, err := bobChannel.AddHTLC(bobHtlc, nil)
-		if err != nil {
-			t.Fatalf("unable to add bob's htlc: %v", err)
-		}
-		_, err = aliceChannel.ReceiveHTLC(bobHtlc)
-		if err != nil {
-			t.Fatalf("unable to recv bob's htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil)
 
-		aliceNewCommit, err := aliceChannel.SignNextCommitment()
+		aliceNewCommit, err := aliceChannel.SignNextCommitment(
+			testQuit,
+		)
 		if err != nil {
 			t.Fatalf("unable to sign next commit: %v", err)
 		}
@@ -4383,7 +4493,7 @@ func TestChanSyncFailure(t *testing.T) {
 		}
 
 		// Alice should detect from Bob's message that she lost state.
-		_, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg)
+		_, _, _, err = aliceOld.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 		if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok {
 			t.Fatalf("wrong error, expected "+
 				"ErrCommitSyncLocalDataLoss instead got: %v",
@@ -4391,7 +4501,9 @@ func TestChanSyncFailure(t *testing.T) {
 		}
 
 		// Bob should detect that Alice probably lost state.
-		_, _, _, err = bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
+		_, _, _, err = bobChannel.ProcessChanSyncMsg(
+			testQuit, aliceSyncMsg,
+		)
 		if err != ErrCommitSyncRemoteDataLoss {
 			t.Fatalf("wrong error, expected "+
 				"ErrCommitSyncRemoteDataLoss instead got: %v",
@@ -4452,7 +4564,7 @@ func TestChanSyncFailure(t *testing.T) {
 	bobSyncMsg, err := bobChannel.channelState.ChanSyncMsg()
 	require.NoError(t, err, "unable to produce chan sync msg")
 	bobSyncMsg.LocalUnrevokedCommitPoint = nil
-	_, _, _, err = aliceOld.ProcessChanSyncMsg(bobSyncMsg)
+	_, _, _, err = aliceOld.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 	if err != ErrCannotSyncCommitChains {
 		t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+
 			"instead got: %v", err)
@@ -4464,7 +4576,7 @@ func TestChanSyncFailure(t *testing.T) {
 	bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg()
 	require.NoError(t, err, "unable to produce chan sync msg")
 	bobSyncMsg.NextLocalCommitHeight++
-	_, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	_, _, _, err = aliceChannel.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 	if err != ErrCannotSyncCommitChains {
 		t.Fatalf("wrong error, expected ErrCannotSyncCommitChains "+
 			"instead got: %v", err)
@@ -4475,7 +4587,7 @@ func TestChanSyncFailure(t *testing.T) {
 	bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg()
 	require.NoError(t, err, "unable to produce chan sync msg")
 	bobSyncMsg.NextLocalCommitHeight--
-	_, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	_, _, _, err = aliceChannel.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 	if err != ErrCommitSyncRemoteDataLoss {
 		t.Fatalf("wrong error, expected ErrCommitSyncRemoteDataLoss "+
 			"instead got: %v", err)
@@ -4491,7 +4603,7 @@ func TestChanSyncFailure(t *testing.T) {
 	require.NoError(t, err, "unable to parse pubkey")
 
 	bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint
-	_, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	_, _, _, err = aliceChannel.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 	if err != ErrInvalidLocalUnrevokedCommitPoint {
 		t.Fatalf("wrong error, expected "+
 			"ErrInvalidLocalUnrevokedCommitPoint instead got: %v",
@@ -4511,7 +4623,7 @@ func TestChanSyncFailure(t *testing.T) {
 	bobSyncMsg, err = bobChannel.channelState.ChanSyncMsg()
 	require.NoError(t, err, "unable to produce chan sync msg")
 	bobSyncMsg.LocalUnrevokedCommitPoint = modCommitPoint
-	_, _, _, err = aliceChannel.ProcessChanSyncMsg(bobSyncMsg)
+	_, _, _, err = aliceChannel.ProcessChanSyncMsg(testQuit, bobSyncMsg)
 	if err != ErrInvalidLocalUnrevokedCommitPoint {
 		t.Fatalf("wrong error, expected "+
 			"ErrInvalidLocalUnrevokedCommitPoint instead got: %v",
@@ -4578,7 +4690,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
 
 	// Now, Alice will send a new commitment to Bob, but we'll simulate a
 	// connection failure, so Bob doesn't get her signature.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// Restart both channels to simulate a connection restart.
@@ -4596,7 +4708,9 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
 	require.NoError(t, err, "unable to produce chan sync msg")
 
 	// Bob should detect that he doesn't need to send anything to Alice.
-	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(aliceSyncMsg)
+	bobMsgsToSend, _, _, err := bobChannel.ProcessChanSyncMsg(
+		testQuit, aliceSyncMsg,
+	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(bobMsgsToSend) != 0 {
 		t.Fatalf("expected bob to send %v messages instead "+
@@ -4608,7 +4722,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
 	// that she needs to first send a new UpdateFee message, and also a
 	// CommitSig.
 	aliceMsgsToSend, _, _, err := aliceChannel.ProcessChanSyncMsg(
-		bobSyncMsg,
+		testQuit, bobSyncMsg,
 	)
 	require.NoError(t, err, "unable to process chan sync msg")
 	if len(aliceMsgsToSend) != 2 {
@@ -4664,7 +4778,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
 	require.NoError(t, err, "bob unable to process alice's commitment")
 	bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 	_, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
 	require.NoError(t, err, "alice unable to recv revocation")
@@ -4699,12 +4813,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
 		Amount:      lnwire.NewMSatFromSatoshis(20000),
 		Expiry:      uint32(10),
 	}
-	if _, err := bobChannel.AddHTLC(bobHtlc, nil); err != nil {
-		t.Fatalf("unable to add bob's htlc: %v", err)
-	}
-	if _, err := aliceChannel.ReceiveHTLC(bobHtlc); err != nil {
-		t.Fatalf("unable to recv bob's htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, bobHtlc, nil)
 	if err := ForceStateTransition(bobChannel, aliceChannel); err != nil {
 		t.Fatalf("unable to complete bob's state transition: %v", err)
 	}
@@ -4776,12 +4885,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
 	var htlcs []*lnwire.UpdateAddHTLC
 	for i := 0; i < numHTLCs; i++ {
 		htlc, _ := createHTLC(i, htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 		htlcs = append(htlcs, htlc)
 
 		if i%5 != 0 {
@@ -4805,7 +4909,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
 
 	// Now, Alice will send a new commitment to Bob, but we'll simulate a
 	// connection failure, so Bob doesn't get the signature.
-	aliceNewCommitSig, err := aliceChannel.SignNextCommitment()
+	aliceNewCommitSig, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// Before restarting Alice, to mimic the old format, we fetch the
@@ -4862,7 +4966,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
 	require.NoError(t, err, "bob unable to process alice's commitment")
 	bobRevocation, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommitSigs, err := bobChannel.SignNextCommitment()
+	bobNewCommitSigs, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "bob unable to sign commitment")
 	_, _, _, _, err = aliceChannel.ReceiveRevocation(bobRevocation)
 	require.NoError(t, err, "alice unable to recv revocation")
@@ -4890,12 +4994,7 @@ func TestFeeUpdateOldDiskFormat(t *testing.T) {
 	// Finally, to trigger a compactLogs execution, we'll add a new HTLC,
 	// then force a state transition.
 	htlc, _ := createHTLC(numHTLCs, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("unable to complete bob's state transition: %v", err)
 	}
@@ -4943,11 +5042,11 @@ func TestChanSyncUnableToSync(t *testing.T) {
 		NextLocalCommitHeight:  1000,
 		RemoteCommitTailHeight: 9000,
 	}
-	_, _, _, err = bobChannel.ProcessChanSyncMsg(badChanSync)
+	_, _, _, err = bobChannel.ProcessChanSyncMsg(testQuit, badChanSync)
 	if err != ErrCannotSyncCommitChains {
 		t.Fatalf("expected error instead have: %v", err)
 	}
-	_, _, _, err = aliceChannel.ProcessChanSyncMsg(badChanSync)
+	_, _, _, err = aliceChannel.ProcessChanSyncMsg(testQuit, badChanSync)
 	if err != ErrCannotSyncCommitChains {
 		t.Fatalf("expected error instead have: %v", err)
 	}
@@ -4989,12 +5088,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) {
 		Amount:      htlcAmt,
 		Expiry:      uint32(5),
 	}
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Then we'll initiate a state transition to lock in this new HTLC.
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
@@ -5020,7 +5114,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) {
 
 	// Alice's former self should conclude that she possibly lost data as
 	// Bob is sending a valid commit secret for the latest state.
-	_, _, _, err = aliceOld.ProcessChanSyncMsg(bobChanSync)
+	_, _, _, err = aliceOld.ProcessChanSyncMsg(testQuit, bobChanSync)
 	if _, ok := err.(*ErrCommitSyncLocalDataLoss); !ok {
 		t.Fatalf("wrong error, expected ErrCommitSyncLocalDataLoss "+
 			"instead got: %v", err)
@@ -5028,7 +5122,7 @@ func TestChanSyncInvalidLastSecret(t *testing.T) {
 
 	// Bob should conclude that he should force close the channel, as Alice
 	// cannot continue operation.
-	_, _, _, err = bobChannel.ProcessChanSyncMsg(aliceChanSync)
+	_, _, _, err = bobChannel.ProcessChanSyncMsg(testQuit, aliceChanSync)
 	if err != ErrInvalidLastCommitSecret {
 		t.Fatalf("wrong error, expected ErrInvalidLastCommitSecret, "+
 			"instead got: %v", err)
@@ -5116,12 +5210,7 @@ func TestChanAvailableBandwidth(t *testing.T) {
 	alicePreimages := make([][32]byte, numHtlcs)
 	for i := 0; i < numHtlcs; i++ {
 		htlc, preImage := createHTLC(i, dustAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 		alicePreimages[i] = preImage
 	}
@@ -5134,12 +5223,7 @@ func TestChanAvailableBandwidth(t *testing.T) {
 	htlcAmt := lnwire.NewMSatFromSatoshis(30000)
 	for i := 0; i < numHtlcs; i++ {
 		htlc, preImage := createHTLC(numHtlcs+i, htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 		alicePreimages = append(alicePreimages, preImage)
 	}
@@ -5408,13 +5492,7 @@ func TestChanCommitWeightDustHtlcs(t *testing.T) {
 		t.Helper()
 
 		htlc, preImage := createHTLC(int(htlcIndex), htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
-
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 		if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 			t.Fatalf("unable to complete alice's state "+
 				"transition: %v", err)
@@ -5525,7 +5603,7 @@ func TestSignCommitmentFailNotLockedIn(t *testing.T) {
 
 	// If we now try to initiate a state update, then it should fail as
 	// Alice is unable to actually create a new state.
-	_, err = aliceChannel.SignNextCommitment()
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != ErrNoWindow {
 		t.Fatalf("expected ErrNoWindow, instead have: %v", err)
 	}
@@ -5547,23 +5625,14 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 	// a state transition.
 	var htlcAmt lnwire.MilliSatoshi = 100000
 	htlc, _ := createHTLC(0, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
+
 	htlc2, _ := createHTLC(1, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc2, nil); err != nil {
-		t.Fatalf("unable to add htlc2: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc2); err != nil {
-		t.Fatalf("unable to recv htlc2: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil)
 
 	// We'll now manually initiate a state transition between Alice and
 	// bob.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5592,7 +5661,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 
 	// Now, have Bob initiate a transition to lock in the Adds sent by
 	// Alice.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5637,7 +5706,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 
 	// We'll now initiate another state transition, but this time Bob will
 	// lead.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5672,7 +5741,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 
 	// Now, begin another state transition led by Alice, and fail the second
 	// HTLC part-way through the dance.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5736,7 +5805,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 
 	// Have Alice initiate a state transition, which does not include the
 	// HTLCs just re-added to the channel state.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5765,7 +5834,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 	}
 
 	// Now initiate a final update from Bob to lock in the final Fail.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5797,7 +5866,7 @@ func TestLockedInHtlcForwardingSkipAfterRestart(t *testing.T) {
 
 	// Finally, have Bob initiate a state transition that locks in the Fail
 	// added after the restart.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -5847,15 +5916,10 @@ func TestInvalidCommitSigError(t *testing.T) {
 	// Alice to Bob.
 	var htlcAmt lnwire.MilliSatoshi = 100000
 	htlc, _ := createHTLC(0, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Alice will now attempt to initiate a state transition.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign new commit")
 
 	// Before the signature gets to Bob, we'll mutate it, such that the
@@ -5897,19 +5961,9 @@ func TestChannelUnilateralCloseHtlcResolution(t *testing.T) {
 	// initiating enough state transitions to lock both of them in.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 	htlcBob, preimageBob := createHTLC(0, htlcAmount)
-	if _, err := bobChannel.AddHTLC(htlcBob, nil); err != nil {
-		t.Fatalf("bob unable to add htlc: %v", err)
-	}
-	if _, err := aliceChannel.ReceiveHTLC(htlcBob); err != nil {
-		t.Fatalf("alice unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlcBob, nil)
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("Can't update the channel state: %v", err)
 	}
@@ -6053,16 +6107,11 @@ func TestChannelUnilateralClosePendingCommit(t *testing.T) {
 	// create a new state transition.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	// With the HTLC added, we'll now manually initiate a state transition
 	// from Alice to Bob.
-	_, err = aliceChannel.SignNextCommitment()
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -6252,12 +6301,7 @@ func TestMaxAcceptedHTLCs(t *testing.T) {
 	// Send the maximum allowed number of HTLCs.
 	for i := 0; i < numHTLCs; i++ {
 		htlc, _ := createHTLC(i, htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 		// Just assign htlcID to the last received HTLC.
 		htlcID = htlc.ID
@@ -6296,15 +6340,10 @@ func TestMaxAcceptedHTLCs(t *testing.T) {
 	// failed. We use numHTLCs here since the previous AddHTLC with this index
 	// failed.
 	htlc, _ = createHTLC(numHTLCs, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Add a commitment to Bob's commitment chain.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign next commitment")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err, "unable to recv new commitment")
@@ -6377,12 +6416,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) {
 	// Send the maximum allowed number of HTLCs minus one.
 	for i := 0; i < numHTLCs-1; i++ {
 		htlc, _ := createHTLC(i, htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 		// Just assign htlcID to the last received HTLC.
 		htlcID = htlc.ID
@@ -6394,12 +6428,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) {
 
 	// Send an HTLC to Bob so that Bob's commitment transaction is full.
 	htlc, _ := createHTLC(numHTLCs-1, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Fail back an HTLC and sign a commitment as in steps 1 & 2.
 	err = bobChannel.FailHTLC(htlcID, []byte{}, nil, nil, nil)
@@ -6409,7 +6438,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) {
 		t.Fatalf("unable to receive fail htlc: %v", err)
 	}
 
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign next commitment")
 
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
@@ -6417,7 +6446,7 @@ func TestMaxAsynchronousHtlcs(t *testing.T) {
 
 	// Cover the HTLC referenced with id equal to numHTLCs-1 with a new
 	// signature (step 3).
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign next commitment")
 
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
@@ -6438,16 +6467,11 @@ func TestMaxAsynchronousHtlcs(t *testing.T) {
 
 	// Send the final Add which should succeed as in step 6.
 	htlc, _ = createHTLC(numHTLCs, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Receiving the commitment should succeed as in step 7 since space was
 	// made.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign next commitment")
 
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
@@ -6484,12 +6508,7 @@ func TestMaxPendingAmount(t *testing.T) {
 	htlcAmt := lnwire.NewMSatFromSatoshis(1.5 * btcutil.SatoshiPerBitcoin)
 	for i := 0; i < numHTLCs; i++ {
 		htlc, _ := createHTLC(i, htlcAmt)
-		if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-			t.Fatalf("unable to add htlc: %v", err)
-		}
-		if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-			t.Fatalf("unable to recv htlc: %v", err)
-		}
+		addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 	}
 
 	// We finally add one more HTLC of 0.1 BTC to Alice's commitment. This
@@ -6591,12 +6610,7 @@ func TestChanReserve(t *testing.T) {
 	htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin)
 	htlc, _ := createHTLC(aliceIndex, htlcAmt)
 	aliceIndex++
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Force a state transition, making sure this HTLC is considered valid
 	// even though the channel reserves are not met.
@@ -6643,12 +6657,7 @@ func TestChanReserve(t *testing.T) {
 	// The first HTLC should successfully be sent.
 	htlc, _ = createHTLC(aliceIndex, htlcAmt)
 	aliceIndex++
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Add a second HTLC of 1 BTC. This should fail because it will take
 	// Alice's balance all the way down to her channel reserve, but since
@@ -6714,12 +6723,7 @@ func TestChanReserve(t *testing.T) {
 	htlcAmt = lnwire.NewMSatFromSatoshis(1 * btcutil.SatoshiPerBitcoin)
 	htlc, _ = createHTLC(bobIndex, htlcAmt)
 	bobIndex++
-	if _, err := bobChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := aliceChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil)
 
 	// Do a last state transition, which should succeed.
 	if err := ForceStateTransition(bobChannel, aliceChannel); err != nil {
@@ -6847,12 +6851,7 @@ func TestMinHTLC(t *testing.T) {
 	// ErrBelowMinHTLC.
 	htlcAmt := lnwire.NewMSatFromSatoshis(0.5 * btcutil.SatoshiPerBitcoin)
 	htlc, _ := createHTLC(0, htlcAmt)
-	if _, err := aliceChannel.AddHTLC(htlc, nil); err != nil {
-		t.Fatalf("unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlc); err != nil {
-		t.Fatalf("unable to recv htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// We add an HTLC below the min value, this should result in
 	// an ErrBelowMinHTLC error.
@@ -7093,15 +7092,10 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
 	// Bob's commit, but not on Alice's.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	// Let Alice sign a new state, which will include the HTLC just sent.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// Bob receives this commitment signature, and revokes his old state.
@@ -7131,7 +7125,7 @@ func TestChannelRestoreUpdateLogs(t *testing.T) {
 	// and remote commit chains are updated in an async fashion. Since the
 	// remote chain was updated with the latest state (since Bob sent the
 	// revocation earlier) we can keep advancing the remote commit chain.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// After Alice has signed this commitment, her local commitment will
@@ -7275,7 +7269,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
 	restoreAndAssert(t, aliceChannel, 1, 0, 0, 0)
 
 	// Bob sends a signature.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err, "unable to receive commitment")
@@ -7302,7 +7296,7 @@ func TestChannelRestoreUpdateLogsFailedHTLC(t *testing.T) {
 
 	// Now send a signature from Alice. This will give Bob a new commitment
 	// where the HTLC is removed.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err, "unable to receive commitment")
@@ -7339,11 +7333,7 @@ func TestDuplicateFailRejection(t *testing.T) {
 	// parties.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	_, err = bobChannel.ReceiveHTLC(htlcAlice)
-	require.NoError(t, err, "unable to recv htlc")
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("unable to complete state update: %v", err)
@@ -7369,7 +7359,7 @@ func TestDuplicateFailRejection(t *testing.T) {
 
 	// We'll now have Bob sign a new commitment to lock in the HTLC fail
 	// for Alice.
-	_, err = bobChannel.SignNextCommitment()
+	_, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commit")
 
 	// We'll now force a restart for Bob and Alice, so we can test the
@@ -7406,11 +7396,7 @@ func TestDuplicateSettleRejection(t *testing.T) {
 	// parties.
 	htlcAmount := lnwire.NewMSatFromSatoshis(20000)
 	htlcAlice, alicePreimage := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	_, err = bobChannel.ReceiveHTLC(htlcAlice)
-	require.NoError(t, err, "unable to recv htlc")
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	if err := ForceStateTransition(aliceChannel, bobChannel); err != nil {
 		t.Fatalf("unable to complete state update: %v", err)
@@ -7436,7 +7422,7 @@ func TestDuplicateSettleRejection(t *testing.T) {
 
 	// We'll now have Bob sign a new commitment to lock in the HTLC fail
 	// for Alice.
-	_, err = bobChannel.SignNextCommitment()
+	_, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commit")
 
 	// We'll now force a restart for Bob and Alice, so we can test the
@@ -7512,15 +7498,10 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
 	// We'll send an HtLC from Alice to Bob.
 	htlcAmount := lnwire.NewMSatFromSatoshis(100000000)
 	htlcAlice, _ := createHTLC(0, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	// Let Alice sign a new state, which will include the HTLC just sent.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// The HTLC should only be on the pending remote commitment, so the
@@ -7552,7 +7533,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
 
 	// Now let Bob send the commitment signature making the HTLC lock in on
 	// Alice's commitment.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// At this stage Bob has a pending remote commitment. Make sure
@@ -7581,16 +7562,11 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
 	// existing HTLCs (the HTLC with index 0) keep getting the add heights
 	// restored properly.
 	htlcAlice, _ = createHTLC(1, htlcAmount)
-	if _, err := aliceChannel.AddHTLC(htlcAlice, nil); err != nil {
-		t.Fatalf("alice unable to add htlc: %v", err)
-	}
-	if _, err := bobChannel.ReceiveHTLC(htlcAlice); err != nil {
-		t.Fatalf("bob unable to recv add htlc: %v", err)
-	}
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlcAlice, nil)
 
 	// Send a new signature from Alice to Bob, making Alice have a pending
 	// remote commitment.
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// A restoration should keep the add heights iof the first HTLC, and
@@ -7629,7 +7605,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
 
 	// Sign a new state for Alice, making Bob have a pending remote
 	// commitment.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// The signing of a new commitment for Alice should have given the new
@@ -7666,7 +7642,7 @@ func TestChannelRestoreCommitHeight(t *testing.T) {
 	require.NoError(t, err, "unable to recv htlc cancel")
 
 	// Now Bob signs for the fail update.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// Bob has a pending commitment for Alice, it shouldn't affect the add
@@ -7724,13 +7700,13 @@ func TestForceCloseBorkedState(t *testing.T) {
 	// Do the commitment dance until Bob sends a revocation so Alice is
 	// able to receive the revocation, and then also make a new state
 	// herself.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commit")
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err, "unable to receive commitment")
 	revokeMsg, _, _, err := bobChannel.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke bob commitment")
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commit")
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err, "unable to receive commitment")
@@ -7764,7 +7740,7 @@ func TestForceCloseBorkedState(t *testing.T) {
 	// We manually advance the commitment tail here since the above
 	// ReceiveRevocation call will fail before it's actually advanced.
 	aliceChannel.remoteCommitChain.advanceTail()
-	_, err = aliceChannel.SignNextCommitment()
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	if err != channeldb.ErrChanBorked {
 		t.Fatalf("sign commitment should have failed: %v", err)
 	}
@@ -8231,7 +8207,7 @@ func TestChannelFeeRateFloor(t *testing.T) {
 	}
 
 	// Check that alice can still sign commitments.
-	aliceNewCommit, err := alice.SignNextCommitment()
+	aliceNewCommit, err := alice.SignNextCommitment(testQuit)
 	require.NoError(t, err, "alice unable to sign commitment")
 
 	// Check that bob can still receive commitments.
@@ -9516,10 +9492,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) {
 	htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000))
 
 	// -----add----->
-	_, err = aliceChannel.AddHTLC(htlc, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Force a state transition to lock in this add on both commitments.
 	// -----sig----->
@@ -9538,7 +9511,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) {
 
 	// Bob should send a commitment signature to Alice.
 	// <----sig------
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9553,7 +9526,7 @@ func TestChannelUnsignedAckedFailure(t *testing.T) {
 	// Alice should sign the next commitment and go down before
 	// sending it.
 	// -----sig-----X
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	newAliceChannel, err := NewLightningChannel(
@@ -9579,15 +9552,12 @@ func TestChannelUnsignedAckedFailure(t *testing.T) {
 	htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(500000))
 
 	// <----add------
-	_, err = bobChannel.AddHTLC(htlc2, nil)
-	require.NoError(t, err)
-	_, err = newAliceChannel.ReceiveHTLC(htlc2)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, newAliceChannel, htlc2, nil)
 
 	// Bob sends the final signature to Alice and Alice should not
 	// reject it, given that we properly restore the unsigned acked
 	// updates and therefore our update log is structured correctly.
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9626,10 +9596,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) {
 	htlc, _ := createHTLC(0, lnwire.MilliSatoshi(500000))
 
 	// <----add-----
-	_, err = bobChannel.AddHTLC(htlc, nil)
-	require.NoError(t, err)
-	_, err = aliceChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil)
 
 	// Force a state transition to lock in this add on both commitments.
 	// <----sig-----
@@ -9648,7 +9615,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) {
 
 	// Alice should send a commitment signature to Bob.
 	// -----sig---->
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9672,7 +9639,7 @@ func TestChannelLocalUnsignedUpdatesFailure(t *testing.T) {
 
 	// Bob sends the final signature and Alice should not reject it.
 	// <----sig-----
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = newAliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9713,10 +9680,7 @@ func TestChannelSignedAckRegression(t *testing.T) {
 	htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000))
 
 	// <----add------
-	_, err = bobChannel.AddHTLC(htlc, nil)
-	require.NoError(t, err)
-	_, err = aliceChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc, nil)
 
 	// Force a state transition to lock in the HTLC.
 	// <----sig------
@@ -9734,7 +9698,7 @@ func TestChannelSignedAckRegression(t *testing.T) {
 	require.NoError(t, err)
 
 	// -----sig---->
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9746,7 +9710,7 @@ func TestChannelSignedAckRegression(t *testing.T) {
 	require.NoError(t, err)
 
 	// <----sig-----
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9755,13 +9719,10 @@ func TestChannelSignedAckRegression(t *testing.T) {
 	htlc2, _ := createHTLC(0, lnwire.MilliSatoshi(5000000))
 
 	// -----add---->
-	_, err = aliceChannel.AddHTLC(htlc2, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc2)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil)
 
 	// -----sig---->
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9791,7 +9752,7 @@ func TestChannelSignedAckRegression(t *testing.T) {
 	// Bob should no longer fail to sign this commitment due to faulty
 	// update logs.
 	// <----sig-----
-	bobNewCommit, err = newBobChannel.SignNextCommitment()
+	bobNewCommit, err = newBobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	// Alice should receive the new commitment without hiccups.
@@ -9855,17 +9816,14 @@ func TestIsChannelClean(t *testing.T) {
 	// sends an htlc.
 	// ---add--->
 	htlc, preimage := createHTLC(0, lnwire.MilliSatoshi(5000000))
-	_, err = aliceChannel.AddHTLC(htlc, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// Assert that the channel remains dirty until the HTLC is completely
 	// removed from both commitments.
 
 	// ---sig--->
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9879,7 +9837,7 @@ func TestIsChannelClean(t *testing.T) {
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// <---sig---
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9900,7 +9858,7 @@ func TestIsChannelClean(t *testing.T) {
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// <---sig---
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9914,7 +9872,7 @@ func TestIsChannelClean(t *testing.T) {
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// ---sig--->
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9938,7 +9896,7 @@ func TestIsChannelClean(t *testing.T) {
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// ---sig--->
-	aliceNewCommit, err = aliceChannel.SignNextCommitment()
+	aliceNewCommit, err = aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -9952,7 +9910,7 @@ func TestIsChannelClean(t *testing.T) {
 	assertCleanOrDirty(false, aliceChannel, bobChannel, t)
 
 	// <---sig---
-	bobNewCommit, err = bobChannel.SignNextCommitment()
+	bobNewCommit, err = bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -10032,10 +9990,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) {
 	htlc1Amt := lnwire.MilliSatoshi(700_000)
 	htlc1, preimage1 := createHTLC(0, htlc1Amt)
 
-	_, err = bobChannel.AddHTLC(htlc1, nil)
-	require.NoError(t, err)
-	_, err = aliceChannel.ReceiveHTLC(htlc1)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc1, nil)
 
 	// Assert that GetDustSum from Alice's perspective does not consider
 	// the HTLC dust on her commitment, but does on Bob's commitment.
@@ -10075,10 +10030,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) {
 	htlc2Amt := lnwire.MilliSatoshi(100_000)
 	htlc2, _ := createHTLC(0, htlc2Amt)
 
-	_, err = aliceChannel.AddHTLC(htlc2, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc2)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc2, nil)
 
 	// Assert that GetDustSum from Alice's perspective includes the new
 	// HTLC as dust on both commitments.
@@ -10089,7 +10041,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) {
 	checkDust(bobChannel, htlc2Amt, htlc2Amt)
 
 	// Alice signs for this HTLC and neither perspective should change.
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -10108,7 +10060,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) {
 
 	// The rest of the dance is completed and neither perspective should
 	// change.
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -10126,10 +10078,7 @@ func testGetDustSum(t *testing.T, chantype channeldb.ChannelType) {
 	htlc3Amt := lnwire.MilliSatoshi(400_000)
 	htlc3, _ := createHTLC(1, htlc3Amt)
 
-	_, err = aliceChannel.AddHTLC(htlc3, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc3)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc3, nil)
 
 	// Assert that this new HTLC is not counted on Alice's local commitment
 	// in the dust sum. Bob's commitment should count it.
@@ -10913,7 +10862,7 @@ func TestAsynchronousSendingContraint(t *testing.T) {
 	// Bob signs the new state for alice, which ONLY has his htlc on it
 	// because he only includes acked updates of alice.
 	// <----sig-------|---------------
-	bobNewCommit, err := bobChannel.SignNextCommitment()
+	bobNewCommit, err := bobChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	err = aliceChannel.ReceiveNewCommitment(bobNewCommit.CommitSigs)
@@ -10929,7 +10878,7 @@ func TestAsynchronousSendingContraint(t *testing.T) {
 	// incoming htlc in her commitment sig to bob, but this will dip her
 	// local balance below her reserve because she already used everything
 	// up when adding her htlc.
-	_, err = aliceChannel.SignNextCommitment()
+	_, err = aliceChannel.SignNextCommitment(testQuit)
 	require.ErrorIs(t, err, ErrBelowChanReserve)
 }
 
@@ -11054,7 +11003,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) {
 	// Force a state transition, this will lock-in the htlc of bob.
 	// ------sig-----> (includes bob's htlc)
 	// <----rev------ (locks in bob's htlc for alice)
-	aliceNewCommit, err := aliceChannel.SignNextCommitment()
+	aliceNewCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 	err = bobChannel.ReceiveNewCommitment(aliceNewCommit.CommitSigs)
 	require.NoError(t, err)
@@ -11112,11 +11061,7 @@ func TestAsynchronousSendingWithFeeBuffer(t *testing.T) {
 		aliceChannel.channelState.LocalChanCfg.DustLimit + htlcFee,
 	)
 	htlc3, _ := createHTLC(1, htlcAmt3)
-	_, err = bobChannel.AddHTLC(htlc3, nil)
-	require.NoError(t, err)
-
-	_, err = aliceChannel.ReceiveHTLC(htlc3)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc3, nil)
 
 	err = ForceStateTransition(bobChannel, aliceChannel)
 	require.NoError(t, err)
@@ -11222,10 +11167,7 @@ func TestEnforceFeeBuffer(t *testing.T) {
 	//	---------------	|-----rev------>
 	htlc1, _ := createHTLC(0, lnwire.NewMSatFromSatoshis(htlcAmt1))
 
-	_, err = aliceChannel.AddHTLC(htlc1, nil)
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc1)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc1, nil)
 
 	err = ForceStateTransition(aliceChannel, bobChannel)
 	require.NoError(t, err)
@@ -11239,10 +11181,7 @@ func TestEnforceFeeBuffer(t *testing.T) {
 	htlcAmt2 := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcent)
 	htlc2, _ := createHTLC(0, htlcAmt2)
 
-	_, err = bobChannel.AddHTLC(htlc2, nil)
-	require.NoError(t, err)
-	_, err = aliceChannel.ReceiveHTLC(htlc2)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc2, nil)
 
 	err = ForceStateTransition(bobChannel, aliceChannel)
 	require.NoError(t, err)
@@ -11262,10 +11201,7 @@ func TestEnforceFeeBuffer(t *testing.T) {
 	//	<----rev-------	|---------------
 	htlc4, _ := createHTLC(1, htlcAmt2)
 
-	_, err = bobChannel.AddHTLC(htlc4, nil)
-	require.NoError(t, err)
-	_, err = aliceChannel.ReceiveHTLC(htlc4)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, bobChannel, aliceChannel, htlc4, nil)
 
 	err = ForceStateTransition(bobChannel, aliceChannel)
 	require.NoError(t, err)
@@ -11305,15 +11241,11 @@ func TestBlindingPointPersistence(t *testing.T) {
 		tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding),
 	)
 
-	_, err = aliceChannel.AddHTLC(htlc, nil)
-
-	require.NoError(t, err)
-	_, err = bobChannel.ReceiveHTLC(htlc)
-	require.NoError(t, err)
+	addAndReceiveHTLC(t, aliceChannel, bobChannel, htlc, nil)
 
 	// Now, Alice will send a new commitment to Bob, which will persist our
 	// pending HTLC to disk.
-	aliceCommit, err := aliceChannel.SignNextCommitment()
+	aliceCommit, err := aliceChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "unable to sign commitment")
 
 	// Restart alice to force fetching state from disk.
diff --git a/lnwallet/test_utils.go b/lnwallet/test_utils.go
index 8ff8860e9..63f96e8c3 100644
--- a/lnwallet/test_utils.go
+++ b/lnwallet/test_utils.go
@@ -1,6 +1,7 @@
 package lnwallet
 
 import (
+	"context"
 	"crypto/rand"
 	"encoding/binary"
 	"encoding/hex"
@@ -100,6 +101,11 @@ var (
 	bobDustLimit   = btcutil.Amount(1300)
 
 	testChannelCapacity float64 = 10
+
+	// testQuit is a context that will never be cancelled, that is used in
+	// place of a real quit context.
+	testQuit, testQuitFunc = context.WithCancel(context.Background())
+	_                      = testQuitFunc
 )
 
 // CreateTestChannels creates to fully populated channels to be used within
@@ -541,7 +547,7 @@ func calcStaticFee(chanType channeldb.ChannelType, numHTLCs int) btcutil.Amount
 // pending updates. This method is useful when testing interactions between two
 // live state machines.
 func ForceStateTransition(chanA, chanB *LightningChannel) error {
-	aliceNewCommit, err := chanA.SignNextCommitment()
+	aliceNewCommit, err := chanA.SignNextCommitment(testQuit)
 	if err != nil {
 		return err
 	}
@@ -554,7 +560,7 @@ func ForceStateTransition(chanA, chanB *LightningChannel) error {
 	if err != nil {
 		return err
 	}
-	bobNewCommit, err := chanB.SignNextCommitment()
+	bobNewCommit, err := chanB.SignNextCommitment(testQuit)
 	if err != nil {
 		return err
 	}
diff --git a/lnwallet/transactions_test.go b/lnwallet/transactions_test.go
index 464597e12..2bdca78d7 100644
--- a/lnwallet/transactions_test.go
+++ b/lnwallet/transactions_test.go
@@ -357,7 +357,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) {
 
 	// Execute commit dance to arrive at the point where the local node has
 	// received the test commitment and the remote signature.
-	localNewCommit, err := localChannel.SignNextCommitment()
+	localNewCommit, err := localChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err, "local unable to sign commitment")
 
 	err = remoteChannel.ReceiveNewCommitment(localNewCommit.CommitSigs)
@@ -369,7 +369,7 @@ func testVectors(t *testing.T, chanType channeldb.ChannelType, test testCase) {
 	_, _, _, _, err = localChannel.ReceiveRevocation(revMsg)
 	require.NoError(t, err)
 
-	remoteNewCommit, err := remoteChannel.SignNextCommitment()
+	remoteNewCommit, err := remoteChannel.SignNextCommitment(testQuit)
 	require.NoError(t, err)
 
 	require.Equal(