Repository: lightningnetwork/lnd
Pull request #9383 (2024-12-20 11:13:52)

bugfix createmissingedge
Cherry-Picked from https://github.com/lightningnetwork/lnd/pull/8777.


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>:label: 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>, please review it.`
  - `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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@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 generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR. ([Beta](https://docs.coderabbit.ai/early-access#beta))
- `@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://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
lightninglabs-deploy: @yyforyongyu: review reminder
@crypt-iq: review reminder
ellemouton: ah, @ziggie1984 , i think we'd want the itests to run here though right? ie, shall we remove the `no-itest` label and repush to get the full CI going?
ziggie1984: I don't think we test this feature in the itests in particular we never set the flag `createmissingedge` but better safe than sorry I let them run through.

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 9e82038aa..b180aa04d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -327,7 +327,7 @@ jobs:
         run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log itest/postgres.log
 
       - name: Upload log files on failure
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v3
         if: ${{ failure() }}
         with:
           name: logs-itest-${{ matrix.name }}
@@ -394,7 +394,7 @@ jobs:
         run: 7z a logs-itest-${{ matrix.name }}.zip itest/**/*.log
 
       - name: Upload log files on failure
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v3
         if: ${{ failure() }}
         with:
           name: logs-itest-${{ matrix.name }}
@@ -439,7 +439,7 @@ jobs:
         run: 7z a logs-itest-windows.zip itest/**/*.log
 
       - name: Upload log files on failure
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v3
         if: ${{ failure() }}
         with:
           name: logs-itest-windows
@@ -478,7 +478,7 @@ jobs:
         run: 7z a logs-itest-macos.zip itest/**/*.log
 
       - name: Upload log files on failure
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v3
         if: ${{ failure() }}
         with:
           name: logs-itest-macos
diff --git a/build/sub_logger.go b/build/sub_logger.go
index 92f52ac23..2a68e6cd5 100644
--- a/build/sub_logger.go
+++ b/build/sub_logger.go
@@ -11,7 +11,7 @@ import (
 
 // SubLogCreator can be used to create a new logger for a particular subsystem.
 type SubLogCreator interface {
-	// Logger returns a new logger for a particular subsystem.
+	// Logger returns a new logger for a particular subsytem.
 	Logger(subsystemTag string) btclog.Logger
 }
 
diff --git a/build/version.go b/build/version.go
index c3db5dc70..632ebafce 100644
--- a/build/version.go
+++ b/build/version.go
@@ -122,5 +122,11 @@ func WithBuildInfo(ctx context.Context, cfg *LogConfig) (context.Context,
 		return nil, fmt.Errorf("unable to decode commit hash: %w", err)
 	}
 
-	return btclog.WithCtx(ctx, btclog.Hex3("rev", commitHash)), nil
+	// Include the first 3 bytes of the commit hash in the context as an
+	// slog attribute.
+	if len(commitHash) > 3 {
+		commitHash = commitHash[:3]
+	}
+
+	return btclog.WithCtx(ctx, btclog.Hex("rev", commitHash)), nil
 }
diff --git a/chainntnfs/bitcoindnotify/bitcoind.go b/chainntnfs/bitcoindnotify/bitcoind.go
index 20e09d7f2..59c03d517 100644
--- a/chainntnfs/bitcoindnotify/bitcoind.go
+++ b/chainntnfs/bitcoindnotify/bitcoind.go
@@ -491,7 +491,7 @@ out:
 func (b *BitcoindNotifier) handleRelevantTx(tx *btcutil.Tx,
 	mempool bool, height uint32) {
 
-	// If this is a mempool spend, we'll ask the mempool notifier to handle
+	// If this is a mempool spend, we'll ask the mempool notifier to hanlde
 	// it.
 	if mempool {
 		err := b.memNotifier.ProcessRelevantSpendTx(tx)
diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go
index d5750ec2a..e3bff289c 100644
--- a/chainntnfs/btcdnotify/btcd.go
+++ b/chainntnfs/btcdnotify/btcd.go
@@ -539,7 +539,7 @@ out:
 func (b *BtcdNotifier) handleRelevantTx(tx *btcutil.Tx,
 	mempool bool, height uint32) {
 
-	// If this is a mempool spend, we'll ask the mempool notifier to handle
+	// If this is a mempool spend, we'll ask the mempool notifier to hanlde
 	// it.
 	if mempool {
 		err := b.memNotifier.ProcessRelevantSpendTx(tx)
diff --git a/contractcourt/breach_arbitrator.go b/contractcourt/breach_arbitrator.go
index d11b725ed..33bc7f7e3 100644
--- a/contractcourt/breach_arbitrator.go
+++ b/contractcourt/breach_arbitrator.go
@@ -1719,7 +1719,7 @@ func NewRetributionStore(db kvdb.Backend) *RetributionStore {
 }
 
 // taprootBriefcaseFromRetInfo creates a taprootBriefcase from a retribution
-// info struct. This stores all the tap tweak information we need to inrder to
+// info struct. This stores all the tap tweak informatoin we need to inrder to
 // be able to hadnel breaches after a restart.
 func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
 	tapCase := newTaprootBriefcase()
@@ -1776,7 +1776,7 @@ func taprootBriefcaseFromRetInfo(retInfo *retributionInfo) *taprootBriefcase {
 	return tapCase
 }
 
-// applyTaprootRetInfo attaches the taproot specific information in the tapCase
+// applyTaprootRetInfo attaches the taproot specific inforamtion in the tapCase
 // to the passed retInfo struct.
 func applyTaprootRetInfo(tapCase *taprootBriefcase,
 	retInfo *retributionInfo) error {
diff --git a/contractcourt/htlc_timeout_resolver.go b/contractcourt/htlc_timeout_resolver.go
index f2b9ba2b7..872a3da92 100644
--- a/contractcourt/htlc_timeout_resolver.go
+++ b/contractcourt/htlc_timeout_resolver.go
@@ -444,7 +444,7 @@ func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) {
 }
 
 // sweepTimeoutTx sends a second level timeout transaction to the sweeper.
-// This transaction uses the SINGLE|ANYONECANPAY flag.
+// This transaction uses the SINLGE|ANYONECANPAY flag.
 func (h *htlcTimeoutResolver) sweepTimeoutTx() error {
 	var inp input.Input
 	if h.isTaproot() {
@@ -465,9 +465,15 @@ func (h *htlcTimeoutResolver) sweepTimeoutTx() error {
 	}
 
 	// Calculate the budget.
+	//
+	// TODO(yy): the budget is twice the output's value, which is needed as
+	// we don't force sweep the output now. To prevent cascading force
+	// closes, we use all its output value plus a wallet input as the
+	// budget. This is a temporary solution until we can optionally cancel
+	// the incoming HTLC, more details in,
+	// - https://github.com/lightningnetwork/lnd/issues/7969
 	budget := calculateBudget(
-		btcutil.Amount(inp.SignDesc().Output.Value),
-		h.Budget.DeadlineHTLCRatio, h.Budget.DeadlineHTLC,
+		btcutil.Amount(inp.SignDesc().Output.Value), 2, 0,
 	)
 
 	h.log.Infof("offering 2nd-level HTLC timeout tx to sweeper "+
@@ -529,9 +535,15 @@ func (h *htlcTimeoutResolver) sweepDirectHtlcOutput() error {
 	)
 
 	// Calculate the budget.
+	//
+	// TODO(yy): the budget is twice the output's value, which is needed as
+	// we don't force sweep the output now. To prevent cascading force
+	// closes, we use all its output value plus a wallet input as the
+	// budget. This is a temporary solution until we can optionally cancel
+	// the incoming HTLC, more details in,
+	// - https://github.com/lightningnetwork/lnd/issues/7969
 	budget := calculateBudget(
-		btcutil.Amount(sweepInput.SignDesc().Output.Value),
-		h.Budget.DeadlineHTLCRatio, h.Budget.DeadlineHTLC,
+		btcutil.Amount(sweepInput.SignDesc().Output.Value), 2, 0,
 	)
 
 	log.Infof("%T(%x): offering offered remote timeout HTLC output to "+
@@ -1058,7 +1070,7 @@ func (h *htlcTimeoutResolver) sweepTimeoutTxOutput() error {
 
 	// TODO(yy): use the result chan returned from SweepInput to get the
 	// confirmation status of this sweeping tx so we don't need to make
-	// another subscription via `RegisterSpendNtfn` for this outpoint here
+	// anothe subscription via `RegisterSpendNtfn` for this outpoint here
 	// in the resolver.
 	_, err = h.Sweeper.SweepInput(
 		inp,
diff --git a/docs/code_contribution_guidelines.md b/docs/code_contribution_guidelines.md
index 68995be03..62c7d889b 100644
--- a/docs/code_contribution_guidelines.md
+++ b/docs/code_contribution_guidelines.md
@@ -337,7 +337,7 @@ Examples of common patterns w.r.t commit structures within the project:
     small scale, fix typos, or any changes that do not modify the code, the
     commit message of the HEAD commit of the PR should end with `[skip ci]` to
     skip the CI checks. When pushing to such an existing PR, the latest commit
-    being pushed should end with `[skip ci]` as to not inadvertently trigger the
+    being pushed should end with `[skip ci]` as to not inadvertantly trigger the
     CI checks.
     
 ## Sign your git commits
diff --git a/docs/debugging_lnd.md b/docs/debugging_lnd.md
index 63dd78cdb..cdb9f7a4f 100644
--- a/docs/debugging_lnd.md
+++ b/docs/debugging_lnd.md
@@ -78,7 +78,7 @@ prefix is overwritten by the `LNWL` subsystem.
 
 Moreover when using the `lncli` command the return value will provide the 
 updated list of all subsystems and their associated logging levels. This makes
-it easy to get an overview of the current logging level for the whole system.
+it easy to get an overview of the corrent logging level for the whole system.
 
 Example:
 
diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md
index 501f59ea5..4fc64028d 100644
--- a/docs/release-notes/release-notes-0.19.0.md
+++ b/docs/release-notes/release-notes-0.19.0.md
@@ -135,10 +135,6 @@
   block. Check
   [here](https://github.com/lightningnetwork/lnd/blob/master/chainio/README.md)
   to learn more.
-  
-* [The sweeper](https://github.com/lightningnetwork/lnd/pull/9274) does now also
- use the configured budget values for HTLCs (first level sweep) in parcticular
- `--sweeper.budget.deadlinehtlcratio` and `--sweeper.budget.deadlinehtlc`.
 
 ## RPC Updates
 
@@ -255,10 +251,6 @@ The underlying functionality between those two options remain the same.
   [GoroutineManager](https://github.com/lightningnetwork/lnd/pull/9141) so that 
   its constructor does not take a context.
 
-* [Update protofsm 
- StateMachine](https://github.com/lightningnetwork/lnd/pull/9342) to use the 
-  new GoroutineManager API along with structured logging.
-
 ## Tooling and Documentation
 
 * [Improved `lncli create` command help text](https://github.com/lightningnetwork/lnd/pull/9077)
diff --git a/docs/signing_release_candidate.md b/docs/signing_release_candidate.md
index 2d31ac06d..263e170d0 100644
--- a/docs/signing_release_candidate.md
+++ b/docs/signing_release_candidate.md
@@ -50,7 +50,7 @@ Tip: Download the official release Manifest file to your local maschine and do:
 `diff lnd-v0.18.3-beta/manifest-v0.18.3-beta.txt ~/Downloads/manifest-v0.18.3-beta.txt`
 (example command for a release candidate called `v0.18.3-beta`)  
 Only if all hashes are identical, should you sign the release.  If the digests
-match, see the example signing command, assuming your PGP signing key is
+match, see the example signing comand, assuming your PGP signing key is
 available on your local device:  
 `gpg --local-user $KEYID  --detach-sig --output manifest-$USERNAME-v0.18.3-beta.sig manifest-v0.18.3-beta.txt`.  
 `USERNAME` being the name in the `[verify-install.sh](../scripts/verify-install.sh)`
diff --git a/fn/context_guard.go b/fn/context_guard.go
index 94b2d8ee8..3868a3ff0 100644
--- a/fn/context_guard.go
+++ b/fn/context_guard.go
@@ -51,10 +51,6 @@ func (g *ContextGuard) Quit() {
 			cancel()
 		}
 
-		// Clear cancelFns. It is safe to use nil, because no write
-		// operations to it can happen after g.quit is closed.
-		g.cancelFns = nil
-
 		close(g.quit)
 	})
 }
@@ -153,7 +149,7 @@ func (g *ContextGuard) Create(ctx context.Context,
 	}
 
 	if opts.blocking {
-		g.ctxBlocking(ctx)
+		g.ctxBlocking(ctx, cancel)
 
 		return ctx, cancel
 	}
@@ -173,10 +169,9 @@ func (g *ContextGuard) Create(ctx context.Context,
 	return ctx, cancel
 }
 
-// ctxQuitUnsafe increases the wait group counter, waits until the context is
-// cancelled and decreases the wait group counter. It stores the passed cancel
-// function and returns a wrapped version, which removed the stored one and
-// calls it. The Quit method calls all the stored cancel functions.
+// ctxQuitUnsafe spins off a goroutine that will block until the passed context
+// is cancelled or until the quit channel has been signaled after which it will
+// call the passed cancel function and decrement the wait group.
 //
 // NOTE: the caller must hold the ContextGuard's mutex before calling this
 // function.
@@ -186,27 +181,35 @@ func (g *ContextGuard) ctxQuitUnsafe(ctx context.Context,
 	cancel = g.addCancelFnUnsafe(cancel)
 
 	g.wg.Add(1)
+	go func() {
+		defer cancel()
+		defer g.wg.Done()
 
-	// We don't have to wait on g.quit here: g.quit can be closed only in
-	// the Quit method, which also closes the context we are waiting for.
-	context.AfterFunc(ctx, func() {
-		g.wg.Done()
-	})
+		select {
+		case <-g.quit:
+
+		case <-ctx.Done():
+		}
+	}()
 
 	return cancel
 }
 
-// ctxBlocking increases the wait group counter, waits until the context is
-// cancelled and decreases the wait group counter.
-//
-// NOTE: the caller must hold the ContextGuard's mutex before calling this
-// function.
-func (g *ContextGuard) ctxBlocking(ctx context.Context) {
+// ctxBlocking spins off a goroutine that will block until the passed context
+// is cancelled after which it will call the passed cancel function and
+// decrement the wait group.
+func (g *ContextGuard) ctxBlocking(ctx context.Context,
+	cancel context.CancelFunc) {
+
 	g.wg.Add(1)
+	go func() {
+		defer cancel()
+		defer g.wg.Done()
 
-	context.AfterFunc(ctx, func() {
-		g.wg.Done()
-	})
+		select {
+		case <-ctx.Done():
+		}
+	}()
 }
 
 // addCancelFnUnsafe adds a context cancel function to the manager and returns a
diff --git a/go.mod b/go.mod
index 5f4219afa..7680509fd 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/btcsuite/btcd/btcutil/psbt v1.1.8
 	github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0
 	github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c
-	github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318
+	github.com/btcsuite/btclog/v2 v2.0.0
 	github.com/btcsuite/btcwallet v0.16.10-0.20241127094224-93c858b2ad63
 	github.com/btcsuite/btcwallet/wallet/txauthor v1.3.5
 	github.com/btcsuite/btcwallet/wallet/txrules v1.2.2
@@ -36,7 +36,7 @@ require (
 	github.com/lightningnetwork/lightning-onion v1.2.1-0.20240712235311-98bd56499dfb
 	github.com/lightningnetwork/lnd/cert v1.2.2
 	github.com/lightningnetwork/lnd/clock v1.1.1
-	github.com/lightningnetwork/lnd/fn/v2 v2.0.8
+	github.com/lightningnetwork/lnd/fn/v2 v2.0.2
 	github.com/lightningnetwork/lnd/healthcheck v1.2.6
 	github.com/lightningnetwork/lnd/kvdb v1.4.12
 	github.com/lightningnetwork/lnd/queue v1.1.1
diff --git a/go.sum b/go.sum
index 6e744d679..5ed9cd204 100644
--- a/go.sum
+++ b/go.sum
@@ -92,8 +92,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd
 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
 github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c h1:4HxD1lBUGUddhzgaNgrCPsFWd7cGYNpeFUgd9ZIgyM0=
 github.com/btcsuite/btclog v0.0.0-20241003133417-09c4e92e319c/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xeHN2y3TQ=
-github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318 h1:oCjIcinPt7XQ644MP/22JcjYEC84qRc3bRBH0d7Hhd4=
-github.com/btcsuite/btclog/v2 v2.0.1-0.20250110154127-3ae4bf1cb318/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE=
+github.com/btcsuite/btclog/v2 v2.0.0 h1:ZfOBItEeLWfU0voi88K72j8vtxP4/dHhxRFf2bxZkVo=
+github.com/btcsuite/btclog/v2 v2.0.0/go.mod h1:XItGUfVOxotJL8kkuk2Hj3EVow5KCugXl3wWfQ6K0AE=
 github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
 github.com/btcsuite/btcwallet v0.16.10-0.20241127094224-93c858b2ad63 h1:YN+PekOLlLoGxE3P5RJaGgodZD5DDJSU8eXQZVwwCxM=
 github.com/btcsuite/btcwallet v0.16.10-0.20241127094224-93c858b2ad63/go.mod h1:1HJXYbjJzgumlnxOC2+ViR1U+gnHWoOn7WeK5OfY1eU=
@@ -456,8 +456,8 @@ github.com/lightningnetwork/lnd/cert v1.2.2 h1:71YK6hogeJtxSxw2teq3eGeuy4rHGKcFf
 github.com/lightningnetwork/lnd/cert v1.2.2/go.mod h1:jQmFn/Ez4zhDgq2hnYSw8r35bqGVxViXhX6Cd7HXM6U=
 github.com/lightningnetwork/lnd/clock v1.1.1 h1:OfR3/zcJd2RhH0RU+zX/77c0ZiOnIMsDIBjgjWdZgA0=
 github.com/lightningnetwork/lnd/clock v1.1.1/go.mod h1:mGnAhPyjYZQJmebS7aevElXKTFDuO+uNFFfMXK1W8xQ=
-github.com/lightningnetwork/lnd/fn/v2 v2.0.8 h1:r2SLz7gZYQPVc3IZhU82M66guz3Zk2oY+Rlj9QN5S3g=
-github.com/lightningnetwork/lnd/fn/v2 v2.0.8/go.mod h1:TOzwrhjB/Azw1V7aa8t21ufcQmdsQOQMDtxVOQWNl8s=
+github.com/lightningnetwork/lnd/fn/v2 v2.0.2 h1:M7o2lYrh/zCp+lntPB3WP/rWTu5U+4ssyHW+kqNJ0fs=
+github.com/lightningnetwork/lnd/fn/v2 v2.0.2/go.mod h1:TOzwrhjB/Azw1V7aa8t21ufcQmdsQOQMDtxVOQWNl8s=
 github.com/lightningnetwork/lnd/healthcheck v1.2.6 h1:1sWhqr93GdkWy4+6U7JxBfcyZIE78MhIHTJZfPx7qqI=
 github.com/lightningnetwork/lnd/healthcheck v1.2.6/go.mod h1:Mu02um4CWY/zdTOvFje7WJgJcHyX2zq/FG3MhOAiGaQ=
 github.com/lightningnetwork/lnd/kvdb v1.4.12 h1:Y0WY5Tbjyjn6eCYh068qkWur5oFtioJlfxc8w5SlJeQ=
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index f0e958992..6e67e87de 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -2,7 +2,6 @@ package htlcswitch
 
 import (
 	"bytes"
-	"context"
 	crand "crypto/rand"
 	"crypto/sha256"
 	"errors"
@@ -409,10 +408,10 @@ type channelLink struct {
 	// the result.
 	quiescenceReqs chan StfuReq
 
-	// cg is a helper that encapsulates a wait group and quit channel and
-	// allows contexts that either block or cancel on those depending on
-	// the use case.
-	cg *fn.ContextGuard
+	// ContextGuard is a helper that encapsulates a wait group and quit
+	// channel and allows contexts that either block or cancel on those
+	// depending on the use case.
+	*fn.ContextGuard
 }
 
 // hookMap is a data structure that is used to track the hooks that need to be
@@ -518,7 +517,7 @@ func NewChannelLink(cfg ChannelLinkConfig,
 		incomingCommitHooks: newHookMap(),
 		quiescer:            qsm,
 		quiescenceReqs:      quiescenceReqs,
-		cg:                  fn.NewContextGuard(),
+		ContextGuard:        fn.NewContextGuard(),
 	}
 }
 
@@ -597,8 +596,8 @@ func (l *channelLink) Start() error {
 
 	l.updateFeeTimer = time.NewTimer(l.randomFeeUpdateTimeout())
 
-	l.cg.WgAdd(1)
-	go l.htlcManager(context.TODO())
+	l.Wg.Add(1)
+	go l.htlcManager()
 
 	return nil
 }
@@ -637,8 +636,8 @@ func (l *channelLink) Stop() {
 		l.hodlQueue.Stop()
 	}
 
-	l.cg.Quit()
-	l.cg.WgWait()
+	close(l.Quit)
+	l.Wg.Wait()
 
 	// Now that the htlcManager has completely exited, reset the packet
 	// courier. This allows the mailbox to revaluate any lingering Adds that
@@ -663,7 +662,7 @@ func (l *channelLink) Stop() {
 // WaitForShutdown blocks until the link finishes shutting down, which includes
 // termination of all dependent goroutines.
 func (l *channelLink) WaitForShutdown() {
-	l.cg.WgWait()
+	l.Wg.Wait()
 }
 
 // EligibleToForward returns a bool indicating if the channel is able to
@@ -741,7 +740,7 @@ func (l *channelLink) IsFlushing(linkDirection LinkDirection) bool {
 func (l *channelLink) OnFlushedOnce(hook func()) {
 	select {
 	case l.flushHooks.newTransients <- hook:
-	case <-l.cg.Done():
+	case <-l.Quit:
 	}
 }
 
@@ -760,7 +759,7 @@ func (l *channelLink) OnCommitOnce(direction LinkDirection, hook func()) {
 
 	select {
 	case queue <- hook:
-	case <-l.cg.Done():
+	case <-l.Quit:
 	}
 }
 
@@ -778,7 +777,7 @@ func (l *channelLink) InitStfu() <-chan fn.Result[lntypes.ChannelParty] {
 
 	select {
 	case l.quiescenceReqs <- req:
-	case <-l.cg.Done():
+	case <-l.Quit:
 		req.Resolve(fn.Err[lntypes.ChannelParty](ErrLinkShuttingDown))
 	}
 
@@ -888,7 +887,7 @@ func (l *channelLink) createFailureWithUpdate(incoming bool,
 // This method is to be called upon reconnection after the initial funding
 // flow. We'll compare out commitment chains with the remote party, and re-send
 // either a danging commit signature, a revocation, or both.
-func (l *channelLink) syncChanStates(ctx context.Context) error {
+func (l *channelLink) syncChanStates() error {
 	chanState := l.channel.State()
 
 	l.log.Infof("Attempting to re-synchronize channel: %v", chanState)
@@ -990,7 +989,7 @@ func (l *channelLink) syncChanStates(ctx context.Context) error {
 		// We've just received a ChanSync message from the remote
 		// party, so we'll process the message  in order to determine
 		// if we need to re-transmit any messages to the remote party.
-		ctx, cancel := l.cg.Create(ctx)
+		ctx, cancel := l.WithCtxQuitNoTimeout()
 		defer cancel()
 		msgsToReSend, openedCircuits, closedCircuits, err =
 			l.channel.ProcessChanSyncMsg(ctx, remoteChanSyncMsg)
@@ -1022,7 +1021,7 @@ func (l *channelLink) syncChanStates(ctx context.Context) error {
 			l.cfg.Peer.SendMessage(false, msg)
 		}
 
-	case <-l.cg.Done():
+	case <-l.Quit:
 		return ErrLinkShuttingDown
 	}
 
@@ -1034,7 +1033,7 @@ func (l *channelLink) syncChanStates(ctx context.Context) error {
 // we previously received are reinstated in memory, and forwarded to the switch
 // if necessary. After a restart, this will also delete any previously
 // completed packages.
-func (l *channelLink) resolveFwdPkgs(ctx context.Context) error {
+func (l *channelLink) resolveFwdPkgs() error {
 	fwdPkgs, err := l.channel.LoadFwdPkgs()
 	if err != nil {
 		return err
@@ -1051,7 +1050,7 @@ func (l *channelLink) resolveFwdPkgs(ctx context.Context) error {
 	// If any of our reprocessing steps require an update to the commitment
 	// txn, we initiate a state transition to capture all relevant changes.
 	if l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote) > 0 {
-		return l.updateCommitTx(ctx)
+		return l.updateCommitTx()
 	}
 
 	return nil
@@ -1112,7 +1111,7 @@ func (l *channelLink) resolveFwdPkg(fwdPkg *channeldb.FwdPkg) error {
 //
 // NOTE: This MUST be run as a goroutine.
 func (l *channelLink) fwdPkgGarbager() {
-	defer l.cg.WgDone()
+	defer l.Wg.Done()
 
 	l.cfg.FwdPkgGCTicker.Resume()
 	defer l.cfg.FwdPkgGCTicker.Stop()
@@ -1129,7 +1128,7 @@ func (l *channelLink) fwdPkgGarbager() {
 					err)
 				continue
 			}
-		case <-l.cg.Done():
+		case <-l.Quit:
 			return
 		}
 	}
@@ -1249,12 +1248,10 @@ func (l *channelLink) handleChanSyncErr(err error) {
 // and also the htlc trickle queue+timer for this active channels.
 //
 // NOTE: This MUST be run as a goroutine.
-//
-//nolint:funlen
-func (l *channelLink) htlcManager(ctx context.Context) {
+func (l *channelLink) htlcManager() {
 	defer func() {
 		l.cfg.BatchTicker.Stop()
-		l.cg.WgDone()
+		l.Wg.Done()
 		l.log.Infof("exited")
 	}()
 
@@ -1274,7 +1271,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 	// re-synchronize state with the remote peer. settledHtlcs is a map of
 	// HTLC's that we re-settled as part of the channel state sync.
 	if l.cfg.SyncStates {
-		err := l.syncChanStates(ctx)
+		err := l.syncChanStates()
 		if err != nil {
 			l.handleChanSyncErr(err)
 			return
@@ -1325,7 +1322,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 	// the channel is not pending, otherwise we should have no htlcs to
 	// reforward.
 	if l.ShortChanID() != hop.Source {
-		err := l.resolveFwdPkgs(ctx)
+		err := l.resolveFwdPkgs()
 		switch err {
 		// No error was encountered, success.
 		case nil:
@@ -1348,7 +1345,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 		// With our link's in-memory state fully reconstructed, spawn a
 		// goroutine to manage the reclamation of disk space occupied by
 		// completed forwarding packages.
-		l.cg.WgAdd(1)
+		l.Wg.Add(1)
 		go l.fwdPkgGarbager()
 	}
 
@@ -1450,8 +1447,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 
 			// If we do, then we'll send a new UpdateFee message to
 			// the remote party, to be locked in with a new update.
-			err = l.updateChannelFee(ctx, newCommitFee)
-			if err != nil {
+			if err := l.updateChannelFee(newCommitFee); err != nil {
 				l.log.Errorf("unable to update fee rate: %v",
 					err)
 				continue
@@ -1479,7 +1475,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 			// including all the currently pending entries. If the
 			// send was unsuccessful, then abandon the update,
 			// waiting for the revocation window to open up.
-			if !l.updateCommitTxOrFail(ctx) {
+			if !l.updateCommitTxOrFail() {
 				return
 			}
 
@@ -1497,19 +1493,19 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 		// that the link is an intermediate hop in a multi-hop HTLC
 		// circuit.
 		case pkt := <-l.downstream:
-			l.handleDownstreamPkt(ctx, pkt)
+			l.handleDownstreamPkt(pkt)
 
 		// A message from the connected peer was just received. This
 		// indicates that we have a new incoming HTLC, either directly
 		// for us, or part of a multi-hop HTLC circuit.
 		case msg := <-l.upstream:
-			l.handleUpstreamMsg(ctx, msg)
+			l.handleUpstreamMsg(msg)
 
 		// A htlc resolution is received. This means that we now have a
 		// resolution for a previously accepted htlc.
 		case hodlItem := <-l.hodlQueue.ChanOut():
 			htlcResolution := hodlItem.(invoices.HtlcResolution)
-			err := l.processHodlQueue(ctx, htlcResolution)
+			err := l.processHodlQueue(htlcResolution)
 			switch err {
 			// No error, success.
 			case nil:
@@ -1547,7 +1543,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 				}
 			}
 
-		case <-l.cg.Done():
+		case <-l.Quit:
 			return
 		}
 	}
@@ -1556,7 +1552,7 @@ func (l *channelLink) htlcManager(ctx context.Context) {
 // processHodlQueue processes a received htlc resolution and continues reading
 // from the hodl queue until no more resolutions remain. When this function
 // returns without an error, the commit tx should be updated.
-func (l *channelLink) processHodlQueue(ctx context.Context,
+func (l *channelLink) processHodlQueue(
 	firstResolution invoices.HtlcResolution) error {
 
 	// Try to read all waiting resolution messages, so that they can all be
@@ -1588,7 +1584,7 @@ loop:
 	}
 
 	// Update the commitment tx.
-	if err := l.updateCommitTx(ctx); err != nil {
+	if err := l.updateCommitTx(); err != nil {
 		return err
 	}
 
@@ -1675,9 +1671,7 @@ func (l *channelLink) randomFeeUpdateTimeout() time.Duration {
 
 // handleDownstreamUpdateAdd processes an UpdateAddHTLC packet sent from the
 // downstream HTLC Switch.
-func (l *channelLink) handleDownstreamUpdateAdd(ctx context.Context,
-	pkt *htlcPacket) error {
-
+func (l *channelLink) handleDownstreamUpdateAdd(pkt *htlcPacket) error {
 	htlc, ok := pkt.htlc.(*lnwire.UpdateAddHTLC)
 	if !ok {
 		return errors.New("not an UpdateAddHTLC packet")
@@ -1781,7 +1775,7 @@ func (l *channelLink) handleDownstreamUpdateAdd(ctx context.Context,
 		getEventType(pkt),
 	)
 
-	l.tryBatchUpdateCommitTx(ctx)
+	l.tryBatchUpdateCommitTx()
 
 	return nil
 }
@@ -1792,9 +1786,7 @@ func (l *channelLink) handleDownstreamUpdateAdd(ctx context.Context,
 // cleared HTLCs with the upstream peer.
 //
 // TODO(roasbeef): add sync ntfn to ensure switch always has consistent view?
-func (l *channelLink) handleDownstreamPkt(ctx context.Context,
-	pkt *htlcPacket) {
-
+func (l *channelLink) handleDownstreamPkt(pkt *htlcPacket) {
 	if pkt.htlc.MsgType().IsChannelUpdate() &&
 		!l.quiescer.CanSendUpdates() {
 
@@ -1808,7 +1800,7 @@ func (l *channelLink) handleDownstreamPkt(ctx context.Context,
 	case *lnwire.UpdateAddHTLC:
 		// Handle add message. The returned error can be ignored,
 		// because it is also sent through the mailbox.
-		_ = l.handleDownstreamUpdateAdd(ctx, pkt)
+		_ = l.handleDownstreamUpdateAdd(pkt)
 
 	case *lnwire.UpdateFulfillHTLC:
 		// If hodl.SettleOutgoing mode is active, we exit early to
@@ -1875,7 +1867,7 @@ func (l *channelLink) handleDownstreamPkt(ctx context.Context,
 		)
 
 		// Immediately update the commitment tx to minimize latency.
-		l.updateCommitTxOrFail(ctx)
+		l.updateCommitTxOrFail()
 
 	case *lnwire.UpdateFailHTLC:
 		// If hodl.FailOutgoing mode is active, we exit early to
@@ -1965,19 +1957,19 @@ func (l *channelLink) handleDownstreamPkt(ctx context.Context,
 		}
 
 		// Immediately update the commitment tx to minimize latency.
-		l.updateCommitTxOrFail(ctx)
+		l.updateCommitTxOrFail()
 	}
 }
 
 // tryBatchUpdateCommitTx updates the commitment transaction if the batch is
 // full.
-func (l *channelLink) tryBatchUpdateCommitTx(ctx context.Context) {
+func (l *channelLink) tryBatchUpdateCommitTx() {
 	pending := l.channel.NumPendingUpdates(lntypes.Local, lntypes.Remote)
 	if pending < uint64(l.cfg.BatchSize) {
 		return
 	}
 
-	l.updateCommitTxOrFail(ctx)
+	l.updateCommitTxOrFail()
 }
 
 // cleanupSpuriousResponse attempts to ack any AddRef or SettleFailRef
@@ -2047,11 +2039,7 @@ func (l *channelLink) cleanupSpuriousResponse(pkt *htlcPacket) {
 // handleUpstreamMsg processes wire messages related to commitment state
 // updates from the upstream peer. The upstream peer is the peer whom we have a
 // direct channel with, updating our respective commitment chains.
-//
-//nolint:funlen
-func (l *channelLink) handleUpstreamMsg(ctx context.Context,
-	msg lnwire.Message) {
-
+func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
 	// First check if the message is an update and we are capable of
 	// receiving updates right now.
 	if msg.MsgType().IsChannelUpdate() && !l.quiescer.CanRecvUpdates() {
@@ -2430,7 +2418,7 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context,
 		}
 
 		select {
-		case <-l.cg.Done():
+		case <-l.Quit:
 			return
 		default:
 		}
@@ -2442,7 +2430,7 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context,
 		// reply with a signature as both sides already have a
 		// commitment with the latest accepted.
 		if l.channel.OweCommitment() {
-			if !l.updateCommitTxOrFail(ctx) {
+			if !l.updateCommitTxOrFail() {
 				return
 			}
 		}
@@ -2500,7 +2488,7 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context,
 		}
 
 		select {
-		case <-l.cg.Done():
+		case <-l.Quit:
 			return
 		default:
 		}
@@ -2554,7 +2542,7 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context,
 		// but there are still remote updates that are not in the remote
 		// commit tx yet, send out an update.
 		if l.channel.OweCommitment() {
-			if !l.updateCommitTxOrFail(ctx) {
+			if !l.updateCommitTxOrFail() {
 				return
 			}
 		}
@@ -2744,8 +2732,8 @@ func (l *channelLink) ackDownStreamPackets() error {
 
 // updateCommitTxOrFail updates the commitment tx and if that fails, it fails
 // the link.
-func (l *channelLink) updateCommitTxOrFail(ctx context.Context) bool {
-	err := l.updateCommitTx(ctx)
+func (l *channelLink) updateCommitTxOrFail() bool {
+	err := l.updateCommitTx()
 	switch err {
 	// No error encountered, success.
 	case nil:
@@ -2771,7 +2759,7 @@ func (l *channelLink) updateCommitTxOrFail(ctx context.Context) bool {
 // updateCommitTx signs, then sends an update to the remote peer adding a new
 // commitment to their commitment chain which includes all the latest updates
 // we've received+processed up to this point.
-func (l *channelLink) updateCommitTx(ctx context.Context) error {
+func (l *channelLink) updateCommitTx() error {
 	// Preemptively write all pending keystones to disk, just in case the
 	// HTLCs we have in memory are included in the subsequent attempt to
 	// sign a commitment state.
@@ -2794,7 +2782,7 @@ func (l *channelLink) updateCommitTx(ctx context.Context) error {
 		return nil
 	}
 
-	ctx, done := l.cg.Create(ctx)
+	ctx, done := l.WithCtxQuitNoTimeout()
 	defer done()
 
 	newCommit, err := l.channel.SignNextCommitment(ctx)
@@ -2834,7 +2822,7 @@ func (l *channelLink) updateCommitTx(ctx context.Context) error {
 	}
 
 	select {
-	case <-l.cg.Done():
+	case <-l.Quit:
 		return ErrLinkShuttingDown
 	default:
 	}
@@ -3541,7 +3529,7 @@ func (l *channelLink) handleSwitchPacket(pkt *htlcPacket) error {
 // NOTE: Part of the ChannelLink interface.
 func (l *channelLink) HandleChannelUpdate(message lnwire.Message) {
 	select {
-	case <-l.cg.Done():
+	case <-l.Quit:
 		// 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.
@@ -3557,9 +3545,7 @@ func (l *channelLink) HandleChannelUpdate(message lnwire.Message) {
 
 // updateChannelFee updates the commitment fee-per-kw on this channel by
 // committing to an update_fee message.
-func (l *channelLink) updateChannelFee(ctx context.Context,
-	feePerKw chainfee.SatPerKWeight) error {
-
+func (l *channelLink) updateChannelFee(feePerKw chainfee.SatPerKWeight) error {
 	l.log.Infof("updating commit fee to %v", feePerKw)
 
 	// We skip sending the UpdateFee message if the channel is not
@@ -3597,8 +3583,7 @@ func (l *channelLink) updateChannelFee(ctx context.Context,
 	if err := l.cfg.Peer.SendMessage(false, msg); err != nil {
 		return err
 	}
-
-	return l.updateCommitTx(ctx)
+	return l.updateCommitTx()
 }
 
 // processRemoteSettleFails accepts a batch of settle/fail payment descriptors
@@ -4305,7 +4290,7 @@ func (l *channelLink) forwardBatch(replay bool, packets ...*htlcPacket) {
 		filteredPkts = append(filteredPkts, pkt)
 	}
 
-	err := l.cfg.ForwardPackets(l.cg.Done(), replay, filteredPkts...)
+	err := l.cfg.ForwardPackets(l.Quit, replay, filteredPkts...)
 	if err != nil {
 		log.Errorf("Unhandled error while reforwarding htlc "+
 			"settle/fail over htlcswitch: %v", err)
diff --git a/lnrpc/autopilotrpc/autopilot_server.go b/lnrpc/autopilotrpc/autopilot_server.go
index 23d0ff5f3..761d5f092 100644
--- a/lnrpc/autopilotrpc/autopilot_server.go
+++ b/lnrpc/autopilotrpc/autopilot_server.go
@@ -137,7 +137,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterAutopilotServer(grpcServer, r)
 
-	log.Debugf("Autopilot RPC server successfully registered with root " +
+	log.Debugf("Autopilot RPC server successfully register with root " +
 		"gRPC server")
 
 	return nil
diff --git a/lnrpc/chainrpc/chain_server.go b/lnrpc/chainrpc/chain_server.go
index 3644d57f5..1defadafc 100644
--- a/lnrpc/chainrpc/chain_server.go
+++ b/lnrpc/chainrpc/chain_server.go
@@ -207,12 +207,12 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// We make sure that we register it with the main gRPC server to ensure
 	// all our methods are routed properly.
 	RegisterChainNotifierServer(grpcServer, r)
-	log.Debug("ChainNotifier RPC server successfully registered with " +
-		"root gRPC server")
+	log.Debug("ChainNotifier RPC server successfully register with root " +
+		"gRPC server")
 
 	RegisterChainKitServer(grpcServer, r)
-	log.Debug("ChainKit RPC server successfully registered with root " +
-		"gRPC server")
+	log.Debug("ChainKit RPC server successfully register with root gRPC " +
+		"server")
 
 	return nil
 }
diff --git a/lnrpc/devrpc/dev_server.go b/lnrpc/devrpc/dev_server.go
index 71f51b085..b26b144c8 100644
--- a/lnrpc/devrpc/dev_server.go
+++ b/lnrpc/devrpc/dev_server.go
@@ -134,7 +134,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterDevServer(grpcServer, r)
 
-	log.Debugf("DEV RPC server successfully registered with root the " +
+	log.Debugf("DEV RPC server successfully register with root the " +
 		"gRPC server")
 
 	return nil
diff --git a/lnrpc/neutrinorpc/neutrino_server.go b/lnrpc/neutrinorpc/neutrino_server.go
index 7154be7db..65b0c8a6e 100644
--- a/lnrpc/neutrinorpc/neutrino_server.go
+++ b/lnrpc/neutrinorpc/neutrino_server.go
@@ -138,7 +138,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterNeutrinoKitServer(grpcServer, r)
 
-	log.Debugf("Neutrino RPC server successfully registered with root " +
+	log.Debugf("Neutrino RPC server successfully register with root " +
 		"gRPC server")
 
 	return nil
diff --git a/lnrpc/peersrpc/peers_server.go b/lnrpc/peersrpc/peers_server.go
index 27dfa3470..14e039f61 100644
--- a/lnrpc/peersrpc/peers_server.go
+++ b/lnrpc/peersrpc/peers_server.go
@@ -116,7 +116,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterPeersServer(grpcServer, r)
 
-	log.Debugf("Peers RPC server successfully registered with root " +
+	log.Debugf("Peers RPC server successfully register with root " +
 		"gRPC server")
 
 	return nil
diff --git a/lnrpc/routerrpc/router_server.go b/lnrpc/routerrpc/router_server.go
index c7077a94b..9499fa25a 100644
--- a/lnrpc/routerrpc/router_server.go
+++ b/lnrpc/routerrpc/router_server.go
@@ -289,7 +289,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterRouterServer(grpcServer, r)
 
-	log.Debugf("Router RPC server successfully registered with root gRPC " +
+	log.Debugf("Router RPC server successfully register with root gRPC " +
 		"server")
 
 	return nil
diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go
index a98e24d1b..4b2ac2c6a 100644
--- a/lnrpc/signrpc/signer_server.go
+++ b/lnrpc/signrpc/signer_server.go
@@ -213,7 +213,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterSignerServer(grpcServer, r)
 
-	log.Debugf("Signer RPC server successfully registered with root gRPC " +
+	log.Debugf("Signer RPC server successfully register with root gRPC " +
 		"server")
 
 	return nil
diff --git a/lnrpc/watchtowerrpc/handler.go b/lnrpc/watchtowerrpc/handler.go
index 5c99bc121..f61dabfd0 100644
--- a/lnrpc/watchtowerrpc/handler.go
+++ b/lnrpc/watchtowerrpc/handler.go
@@ -97,7 +97,7 @@ func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
 	// all our methods are routed properly.
 	RegisterWatchtowerServer(grpcServer, r)
 
-	log.Debugf("Watchtower RPC server successfully registered with root " +
+	log.Debugf("Watchtower RPC server successfully register with root " +
 		"gRPC server")
 
 	return nil
diff --git a/protofsm/log.go b/protofsm/log.go
index 6978f1e89..8ff9c1b62 100644
--- a/protofsm/log.go
+++ b/protofsm/log.go
@@ -1,7 +1,7 @@
 package protofsm
 
 import (
-	"github.com/btcsuite/btclog/v2"
+	"github.com/btcsuite/btclog"
 	"github.com/lightningnetwork/lnd/build"
 )
 
diff --git a/protofsm/state_machine.go b/protofsm/state_machine.go
index d78563e95..2cc121902 100644
--- a/protofsm/state_machine.go
+++ b/protofsm/state_machine.go
@@ -9,7 +9,6 @@ import (
 	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/wire"
-	"github.com/btcsuite/btclog/v2"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/fn/v2"
 	"github.com/lightningnetwork/lnd/lnutils"
@@ -131,8 +130,6 @@ type stateQuery[Event any, Env Environment] struct {
 type StateMachine[Event any, Env Environment] struct {
 	cfg StateMachineCfg[Event, Env]
 
-	log btclog.Logger
-
 	// events is the channel that will be used to send new events to the
 	// FSM.
 	events chan Event
@@ -145,7 +142,7 @@ type StateMachine[Event any, Env Environment] struct {
 	// query the internal state machine state.
 	stateQuery chan stateQuery[Event, Env]
 
-	gm   fn.GoroutineManager
+	wg   fn.GoroutineManager
 	quit chan struct{}
 
 	startOnce sync.Once
@@ -196,17 +193,14 @@ type StateMachineCfg[Event any, Env Environment] struct {
 // an initial state, an environment, and an event to process as if emitted at
 // the onset of the state machine. Such an event can be used to set up tracking
 // state such as a txid confirmation event.
-func NewStateMachine[Event any, Env Environment](
-	cfg StateMachineCfg[Event, Env]) StateMachine[Event, Env] {
+func NewStateMachine[Event any, Env Environment](cfg StateMachineCfg[Event, Env], //nolint:ll
+) StateMachine[Event, Env] {
 
 	return StateMachine[Event, Env]{
-		cfg: cfg,
-		log: log.WithPrefix(
-			fmt.Sprintf("FSM(%v):", cfg.Env.Name()),
-		),
+		cfg:            cfg,
 		events:         make(chan Event, 1),
 		stateQuery:     make(chan stateQuery[Event, Env]),
-		gm:             *fn.NewGoroutineManager(),
+		wg:             *fn.NewGoroutineManager(context.Background()),
 		newStateEvents: fn.NewEventDistributor[State[Event, Env]](),
 		quit:           make(chan struct{}),
 	}
@@ -214,10 +208,10 @@ func NewStateMachine[Event any, Env Environment](
 
 // Start starts the state machine. This will spawn a goroutine that will drive
 // the state machine to completion.
-func (s *StateMachine[Event, Env]) Start(ctx context.Context) {
+func (s *StateMachine[Event, Env]) Start() {
 	s.startOnce.Do(func() {
-		_ = s.gm.Go(ctx, func(ctx context.Context) {
-			s.driveMachine(ctx)
+		_ = s.wg.Go(func(ctx context.Context) {
+			s.driveMachine()
 		})
 	})
 }
@@ -227,21 +221,20 @@ func (s *StateMachine[Event, Env]) Start(ctx context.Context) {
 func (s *StateMachine[Event, Env]) Stop() {
 	s.stopOnce.Do(func() {
 		close(s.quit)
-		s.gm.Stop()
+		s.wg.Stop()
 	})
 }
 
 // SendEvent sends a new event to the state machine.
 //
 // TODO(roasbeef): bool if processed?
-func (s *StateMachine[Event, Env]) SendEvent(ctx context.Context, event Event) {
-	s.log.DebugS(ctx, "Sending event",
-		"event", lnutils.SpewLogClosure(event))
+func (s *StateMachine[Event, Env]) SendEvent(event Event) {
+	log.Debugf("FSM(%v): sending event: %v", s.cfg.Env.Name(),
+		lnutils.SpewLogClosure(event),
+	)
 
 	select {
 	case s.events <- event:
-	case <-ctx.Done():
-		return
 	case <-s.quit:
 		return
 	}
@@ -265,16 +258,16 @@ func (s *StateMachine[Event, Env]) Name() string {
 // message can be mapped using the default message mapper, then true is
 // returned indicating that the message was processed. Otherwise, false is
 // returned.
-func (s *StateMachine[Event, Env]) SendMessage(ctx context.Context,
-	msg lnwire.Message) bool {
-
+func (s *StateMachine[Event, Env]) SendMessage(msg lnwire.Message) bool {
 	// If we have no message mapper, then return false as we can't process
 	// this message.
 	if !s.cfg.MsgMapper.IsSome() {
 		return false
 	}
 
-	s.log.DebugS(ctx, "Sending msg", "msg", lnutils.SpewLogClosure(msg))
+	log.Debugf("FSM(%v): sending msg: %v", s.cfg.Env.Name(),
+		lnutils.SpewLogClosure(msg),
+	)
 
 	// Otherwise, try to map the message using the default message mapper.
 	// If we can't extract an event, then we'll return false to indicate
@@ -284,7 +277,7 @@ func (s *StateMachine[Event, Env]) SendMessage(ctx context.Context,
 		event := mapper.MapMsg(msg)
 
 		event.WhenSome(func(event Event) {
-			s.SendEvent(ctx, event)
+			s.SendEvent(event)
 
 			processed = true
 		})
@@ -335,7 +328,9 @@ func (s *StateMachine[Event, Env]) RemoveStateSub(sub StateSubscriber[
 // executeDaemonEvent executes a daemon event, which is a special type of event
 // that can be emitted as part of the state transition function of the state
 // machine. An error is returned if the type of event is unknown.
-func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
+//
+//nolint:funlen
+func (s *StateMachine[Event, Env]) executeDaemonEvent(
 	event DaemonEvent) error {
 
 	switch daemonEvent := event.(type) {
@@ -343,9 +338,11 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 	// any preconditions as well as post-send events.
 	case *SendMsgEvent[Event]:
 		sendAndCleanUp := func() error {
-			s.log.DebugS(ctx, "Sending message to target",
-				btclog.Hex6("target", daemonEvent.TargetPeer.SerializeCompressed()),
-				"messages", lnutils.SpewLogClosure(daemonEvent.Msgs))
+			log.Debugf("FSM(%v): sending message to target(%x): "+
+				"%v", s.cfg.Env.Name(),
+				daemonEvent.TargetPeer.SerializeCompressed(),
+				lnutils.SpewLogClosure(daemonEvent.Msgs),
+			)
 
 			err := s.cfg.Daemon.SendMessages(
 				daemonEvent.TargetPeer, daemonEvent.Msgs,
@@ -358,14 +355,15 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 			// If a post-send event was specified, then we'll funnel
 			// that back into the main state machine now as well.
 			return fn.MapOptionZ(daemonEvent.PostSendEvent, func(event Event) error { //nolint:ll
-				launched := s.gm.Go(
-					ctx, func(ctx context.Context) {
-						s.log.DebugS(ctx, "Sending post-send event",
-							"event", lnutils.SpewLogClosure(event))
+				launched := s.wg.Go(func(ctx context.Context) {
+					log.Debugf("FSM(%v): sending "+
+						"post-send event: %v",
+						s.cfg.Env.Name(),
+						lnutils.SpewLogClosure(event),
+					)
 
-						s.SendEvent(ctx, event)
-					},
-				)
+					s.SendEvent(event)
+				})
 
 				if !launched {
 					return ErrStateMachineShutdown
@@ -384,13 +382,14 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 		// Otherwise, this has a SendWhen predicate, so we'll need
 		// launch a goroutine to poll the SendWhen, then send only once
 		// the predicate is true.
-		launched := s.gm.Go(ctx, func(ctx context.Context) {
+		launched := s.wg.Go(func(ctx context.Context) {
 			predicateTicker := time.NewTicker(
 				s.cfg.CustomPollInterval.UnwrapOr(pollInterval),
 			)
 			defer predicateTicker.Stop()
 
-			s.log.InfoS(ctx, "Waiting for send predicate to be true")
+			log.Infof("FSM(%v): waiting for send predicate to "+
+				"be true", s.cfg.Env.Name())
 
 			for {
 				select {
@@ -403,11 +402,14 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 					)
 
 					if canSend {
-						s.log.InfoS(ctx, "Send active predicate")
+						log.Infof("FSM(%v): send "+
+							"active predicate",
+							s.cfg.Env.Name())
 
 						err := sendAndCleanUp()
 						if err != nil {
-							s.log.ErrorS(ctx, "Unable to send message", err)
+							//nolint:ll
+							log.Errorf("FSM(%v): unable to send message: %v", err)
 						}
 
 						return
@@ -428,8 +430,8 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 	// If this is a broadcast transaction event, then we'll broadcast with
 	// the label attached.
 	case *BroadcastTxn:
-		s.log.DebugS(ctx, "Broadcasting txn",
-			"txid", daemonEvent.Tx.TxHash())
+		log.Debugf("FSM(%v): broadcasting txn, txid=%v",
+			s.cfg.Env.Name(), daemonEvent.Tx.TxHash())
 
 		err := s.cfg.Daemon.BroadcastTransaction(
 			daemonEvent.Tx, daemonEvent.Label,
@@ -443,8 +445,8 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 	// The state machine has requested a new event to be sent once a
 	// transaction spending a specified outpoint has confirmed.
 	case *RegisterSpend[Event]:
-		s.log.DebugS(ctx, "Registering spend",
-			"outpoint", daemonEvent.OutPoint)
+		log.Debugf("FSM(%v): registering spend: %v", s.cfg.Env.Name(),
+			daemonEvent.OutPoint)
 
 		spendEvent, err := s.cfg.Daemon.RegisterSpendNtfn(
 			&daemonEvent.OutPoint, daemonEvent.PkScript,
@@ -454,7 +456,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 			return fmt.Errorf("unable to register spend: %w", err)
 		}
 
-		launched := s.gm.Go(ctx, func(ctx context.Context) {
+		launched := s.wg.Go(func(ctx context.Context) {
 			for {
 				select {
 				case spend, ok := <-spendEvent.Spend:
@@ -468,7 +470,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 					postSpend := daemonEvent.PostSpendEvent
 					postSpend.WhenSome(func(f SpendMapper[Event]) { //nolint:ll
 						customEvent := f(spend)
-						s.SendEvent(ctx, customEvent)
+						s.SendEvent(customEvent)
 					})
 
 					return
@@ -488,8 +490,8 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 	// The state machine has requested a new event to be sent once a
 	// specified txid+pkScript pair has confirmed.
 	case *RegisterConf[Event]:
-		s.log.DebugS(ctx, "Registering conf",
-			"txid", daemonEvent.Txid)
+		log.Debugf("FSM(%v): registering conf: %v", s.cfg.Env.Name(),
+			daemonEvent.Txid)
 
 		numConfs := daemonEvent.NumConfs.UnwrapOr(1)
 		confEvent, err := s.cfg.Daemon.RegisterConfirmationsNtfn(
@@ -500,7 +502,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 			return fmt.Errorf("unable to register conf: %w", err)
 		}
 
-		launched := s.gm.Go(ctx, func(ctx context.Context) {
+		launched := s.wg.Go(func(ctx context.Context) {
 			for {
 				select {
 				case <-confEvent.Confirmed:
@@ -512,7 +514,7 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 					// dispatchAfterRecv w/ above
 					postConf := daemonEvent.PostConfEvent
 					postConf.WhenSome(func(e Event) {
-						s.SendEvent(ctx, e)
+						s.SendEvent(e)
 					})
 
 					return
@@ -536,13 +538,12 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent(ctx context.Context,
 // applyEvents applies a new event to the state machine. This will continue
 // until no further events are emitted by the state machine. Along the way,
 // we'll also ensure to execute any daemon events that are emitted.
-func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
-	currentState State[Event, Env], newEvent Event) (State[Event, Env],
-	error) {
-
-	s.log.DebugS(ctx, "Applying new event",
-		"event", lnutils.SpewLogClosure(newEvent))
+func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
+	newEvent Event) (State[Event, Env], error) {
 
+	log.Debugf("FSM(%v): applying new event", s.cfg.Env.Name(),
+		lnutils.SpewLogClosure(newEvent),
+	)
 	eventQueue := fn.NewQueue(newEvent)
 
 	// Given the next event to handle, we'll process the event, then add
@@ -553,8 +554,10 @@ func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
 	//nolint:ll
 	for nextEvent := eventQueue.Dequeue(); nextEvent.IsSome(); nextEvent = eventQueue.Dequeue() {
 		err := fn.MapOptionZ(nextEvent, func(event Event) error {
-			s.log.DebugS(ctx, "Processing event",
-				"event", lnutils.SpewLogClosure(event))
+			log.Debugf("FSM(%v): processing event: %v",
+				s.cfg.Env.Name(),
+				lnutils.SpewLogClosure(event),
+			)
 
 			// Apply the state transition function of the current
 			// state given this new event and our existing env.
@@ -572,7 +575,7 @@ func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
 				// of this new state transition.
 				for _, dEvent := range events.ExternalEvents {
 					err := s.executeDaemonEvent(
-						ctx, dEvent,
+						dEvent,
 					)
 					if err != nil {
 						return err
@@ -584,8 +587,14 @@ func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
 				//
 				//nolint:ll
 				for _, inEvent := range events.InternalEvent {
-					s.log.DebugS(ctx, "Adding new internal event to queue",
-						"event", lnutils.SpewLogClosure(inEvent))
+					log.Debugf("FSM(%v): adding "+
+						"new internal event "+
+						"to queue: %v",
+						s.cfg.Env.Name(),
+						lnutils.SpewLogClosure(
+							inEvent,
+						),
+					)
 
 					eventQueue.Enqueue(inEvent)
 				}
@@ -596,9 +605,10 @@ func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
 				return err
 			}
 
-			s.log.InfoS(ctx, "State transition",
-				btclog.Fmt("from_state", "%T", currentState),
-				btclog.Fmt("to_state", "%T", transition.NextState))
+			log.Infof("FSM(%v): state transition: from_state=%T, "+
+				"to_state=%T",
+				s.cfg.Env.Name(), currentState,
+				transition.NextState)
 
 			// With our events processed, we'll now update our
 			// internal state.
@@ -623,18 +633,18 @@ func (s *StateMachine[Event, Env]) applyEvents(ctx context.Context,
 // driveMachine is the main event loop of the state machine. It accepts any new
 // incoming events, and then drives the state machine forward until it reaches
 // a terminal state.
-func (s *StateMachine[Event, Env]) driveMachine(ctx context.Context) {
-	s.log.DebugS(ctx, "Starting state machine")
+func (s *StateMachine[Event, Env]) driveMachine() {
+	log.Debugf("FSM(%v): starting state machine", s.cfg.Env.Name())
 
 	currentState := s.cfg.InitialState
 
 	// Before we start, if we have an init daemon event specified, then
 	// we'll handle that now.
 	err := fn.MapOptionZ(s.cfg.InitEvent, func(event DaemonEvent) error {
-		return s.executeDaemonEvent(ctx, event)
+		return s.executeDaemonEvent(event)
 	})
 	if err != nil {
-		s.log.ErrorS(ctx, "Unable to execute init event", err)
+		log.Errorf("unable to execute init event: %w", err)
 		return
 	}
 
@@ -648,13 +658,11 @@ func (s *StateMachine[Event, Env]) driveMachine(ctx context.Context) {
 		// machine forward until we either run out of internal events,
 		// or we reach a terminal state.
 		case newEvent := <-s.events:
-			newState, err := s.applyEvents(
-				ctx, currentState, newEvent,
-			)
+			newState, err := s.applyEvents(currentState, newEvent)
 			if err != nil {
 				s.cfg.ErrorReporter.ReportError(err)
 
-				s.log.ErrorS(ctx, "Unable to apply event", err)
+				log.Errorf("unable to apply event: %v", err)
 
 				// An error occurred, so we'll tear down the
 				// entire state machine as we can't proceed.
@@ -672,7 +680,7 @@ func (s *StateMachine[Event, Env]) driveMachine(ctx context.Context) {
 				return
 			}
 
-		case <-s.gm.Done():
+		case <-s.wg.Done():
 			return
 		}
 	}
diff --git a/routing/localchans/manager.go b/routing/localchans/manager.go
index cd9e58fca..3beb28525 100644
--- a/routing/localchans/manager.go
+++ b/routing/localchans/manager.go
@@ -311,8 +311,16 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel,
 			err)
 	}
 
+	// We need to make sure we use the real scid for public confirmed
+	// zero-conf channels.
+	shortChanID := channel.ShortChanID()
+	isPublic := channel.ChannelFlags&lnwire.FFAnnounceChannel != 0
+	if isPublic && channel.IsZeroConf() && channel.ZeroConfConfirmed() {
+		shortChanID = channel.ZeroConfRealScid()
+	}
+
 	info := &models.ChannelEdgeInfo{
-		ChannelID:    channel.ShortChanID().ToUint64(),
+		ChannelID:    shortChanID.ToUint64(),
 		ChainHash:    channel.ChainHash,
 		Features:     featureBuf.Bytes(),
 		Capacity:     channel.Capacity,
@@ -328,7 +336,7 @@ func (r *Manager) createEdge(channel *channeldb.OpenChannel,
 	// be updated with the new values in the call to processChan below.
 	timeLockDelta := uint16(r.DefaultRoutingPolicy.TimeLockDelta)
 	edge := &models.ChannelEdgePolicy{
-		ChannelID:                 channel.ShortChanID().ToUint64(),
+		ChannelID:                 shortChanID.ToUint64(),
 		LastUpdate:                timestamp,
 		TimeLockDelta:             timeLockDelta,
 		ChannelFlags:              channelFlags,
diff --git a/scripts/keys/ellemouton.asc b/scripts/keys/ellemouton.asc
index ceb0955d8..712bf45cb 100644
--- a/scripts/keys/ellemouton.asc
+++ b/scripts/keys/ellemouton.asc
@@ -11,54 +11,42 @@ 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
-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
-Rg==
-=5WUA
+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
+hTV2kdJi+8+qqUY=
+=0VfH
 -----END PGP PUBLIC KEY BLOCK-----


Test patch:
diff --git a/chainio/consumer_test.go b/chainio/consumer_test.go
index 50123fa83..d1cabf316 100644
--- a/chainio/consumer_test.go
+++ b/chainio/consumer_test.go
@@ -121,7 +121,7 @@ func TestProcessBlockConsumerQuitAfterSend(t *testing.T) {
 	require.Equal(t, mockBeat, beat)
 
 	// Instead of sending nil to the consumer's error channel, close the
-	// quit channel.
+	// quit chanel.
 	close(quitChan)
 
 	// Assert ProcessBlock returned nil.
diff --git a/chainio/dispatcher_test.go b/chainio/dispatcher_test.go
index 1cab5b998..11abbeb65 100644
--- a/chainio/dispatcher_test.go
+++ b/chainio/dispatcher_test.go
@@ -120,7 +120,7 @@ func TestDispatchSequential(t *testing.T) {
 	// prevConsumer specifies the previous consumer that was called.
 	var prevConsumer string
 
-	// Mock the ProcessBlock on consumers to return immediately.
+	// Mock the ProcessBlock on consumers to reutrn immediately.
 	consumer1.On("ProcessBlock", mockBeat).Return(nil).Run(
 		func(args mock.Arguments) {
 			// Check the order of the consumers.
@@ -260,7 +260,7 @@ func TestDispatchBlocks(t *testing.T) {
 	b.RegisterQueue([]Consumer{consumer})
 
 	// Mock the consumer to return nil error on ProcessBlock. This
-	// implicitly asserts that the step `notifyQueues` is successfully
+	// implictly asserts that the step `notifyQueues` is successfully
 	// reached in the `dispatchBlocks` method.
 	consumer.On("ProcessBlock", mock.Anything).Return(nil).Once()
 
diff --git a/chainntnfs/test/test_interface.go b/chainntnfs/test/test_interface.go
index fd42b10de..99daf54f1 100644
--- a/chainntnfs/test/test_interface.go
+++ b/chainntnfs/test/test_interface.go
@@ -41,8 +41,9 @@ func testSingleConfirmationNotification(miner *rpctest.Harness,
 	// function.
 	txid, pkScript, err := chainntnfs.GetTestTxidAndScript(miner)
 	require.NoError(t, err, "unable to create test tx")
-	err = chainntnfs.WaitForMempoolTx(miner, txid)
-	require.NoError(t, err, "tx not relayed to miner")
+	if err := chainntnfs.WaitForMempoolTx(miner, txid); err != nil {
+		t.Fatalf("tx not relayed to miner: %v", err)
+	}
 
 	_, currentHeight, err := miner.Client.GetBestBlock()
 	require.NoError(t, err, "unable to get current height")
@@ -67,11 +68,6 @@ func testSingleConfirmationNotification(miner *rpctest.Harness,
 	blockHash, err := miner.Client.Generate(1)
 	require.NoError(t, err, "unable to generate single block")
 
-	// Assert the above tx is mined in the block.
-	block, err := miner.Client.GetBlock(blockHash[0])
-	require.NoError(t, err)
-	require.Len(t, block.Transactions, 2, "block does not contain tx")
-
 	select {
 	case confInfo := <-confIntent.Confirmed:
 		if !confInfo.BlockHash.IsEqual(blockHash[0]) {
diff --git a/channeldb/revocation_log_test.go b/channeldb/revocation_log_test.go
index dcce16a03..2df6627e2 100644
--- a/channeldb/revocation_log_test.go
+++ b/channeldb/revocation_log_test.go
@@ -565,7 +565,7 @@ func TestPutRevocationLog(t *testing.T) {
 		},
 		{
 			// Test dust htlc is not saved.
-			name:        "dust htlc not saved with amount data",
+			name:        "dust htlc not saved with amout data",
 			commit:      testCommitDust,
 			ourIndex:    0,
 			theirIndex:  1,
diff --git a/contractcourt/taproot_briefcase_test.go b/contractcourt/taproot_briefcase_test.go
index 80b504bd5..441aebf1d 100644
--- a/contractcourt/taproot_briefcase_test.go
+++ b/contractcourt/taproot_briefcase_test.go
@@ -127,30 +127,20 @@ func TestTaprootBriefcase(t *testing.T) {
 	require.Equal(t, testCase, &decodedCase)
 }
 
-// testHtlcAuxBlobProperties is a rapid property that verifies the encoding and
-// decoding of the HTLC aux blobs.
-func testHtlcAuxBlobProperties(t *rapid.T) {
-	htlcBlobs := rapid.Make[htlcAuxBlobs]().Draw(t, "htlcAuxBlobs")
-
-	var b bytes.Buffer
-	require.NoError(t, htlcBlobs.Encode(&b))
-
-	decodedBlobs := newAuxHtlcBlobs()
-	require.NoError(t, decodedBlobs.Decode(&b))
-
-	require.Equal(t, htlcBlobs, decodedBlobs)
-}
-
 // TestHtlcAuxBlobEncodeDecode tests the encode/decode methods of the HTLC aux
 // blobs.
 func TestHtlcAuxBlobEncodeDecode(t *testing.T) {
 	t.Parallel()
 
-	rapid.Check(t, testHtlcAuxBlobProperties)
-}
+	rapid.Check(t, func(t *rapid.T) {
+		htlcBlobs := rapid.Make[htlcAuxBlobs]().Draw(t, "htlcAuxBlobs")
+
+		var b bytes.Buffer
+		require.NoError(t, htlcBlobs.Encode(&b))
+
+		decodedBlobs := newAuxHtlcBlobs()
+		require.NoError(t, decodedBlobs.Decode(&b))
 
-// FuzzHtlcAuxBlobEncodeDecodeFuzz tests the encode/decode methods of the HTLC
-// aux blobs using the rapid derived fuzzer.
-func FuzzHtlcAuxBlobEncodeDecode(f *testing.F) {
-	f.Fuzz(rapid.MakeFuzz(testHtlcAuxBlobProperties))
+		require.Equal(t, htlcBlobs, decodedBlobs)
+	})
 }
diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go
index 5e2ebeac4..b74f69bf0 100644
--- a/discovery/gossiper_test.go
+++ b/discovery/gossiper_test.go
@@ -2709,7 +2709,7 @@ func assertBroadcast(t *testing.T, ctx *testCtx, num int) []lnwire.Message {
 	return msgs
 }
 
-// assertProcessAnnouncement is a helper method that checks that the result of
+// assertProcessAnnouncemnt is a helper method that checks that the result of
 // processing an announcement is successful.
 func assertProcessAnnouncement(t *testing.T, result chan error) {
 	t.Helper()
diff --git a/fn/context_guard_test.go b/fn/context_guard_test.go
index 576ca5364..e13cba5dc 100644
--- a/fn/context_guard_test.go
+++ b/fn/context_guard_test.go
@@ -2,11 +2,8 @@ package fn
 
 import (
 	"context"
-	"runtime"
 	"testing"
 	"time"
-
-	"github.com/stretchr/testify/require"
 )
 
 // TestContextGuard tests the behaviour of the ContextGuard.
@@ -301,12 +298,6 @@ func TestContextGuard(t *testing.T) {
 		case <-time.After(time.Second):
 			t.Fatalf("timeout")
 		}
-
-		// Cancel the context.
-		cancel()
-
-		// Make sure wg's counter gets to 0 eventually.
-		g.WgWait()
 	})
 
 	// Test that if we add the CustomTimeoutCGOpt option, then the context
@@ -442,36 +433,3 @@ func TestContextGuard(t *testing.T) {
 		}
 	})
 }
-
-// TestContextGuardCountGoroutines makes sure that ContextGuard doesn't create
-// any goroutines while waiting for contexts.
-func TestContextGuardCountGoroutines(t *testing.T) {
-	// NOTE: t.Parallel() is not called in this test because it relies on an
-	// accurate count of active goroutines. Running other tests in parallel
-	// would introduce additional goroutines, leading to unreliable results.
-
-	g := NewContextGuard()
-
-	ctx, cancel := context.WithCancel(context.Background())
-
-	// Count goroutines before contexts are created.
-	count1 := runtime.NumGoroutine()
-
-	// Create 1000 contexts of each type.
-	for i := 0; i < 1000; i++ {
-		_, _ = g.Create(ctx)
-		_, _ = g.Create(ctx, WithBlockingCG())
-		_, _ = g.Create(ctx, WithTimeoutCG())
-		_, _ = g.Create(ctx, WithBlockingCG(), WithTimeoutCG())
-	}
-
-	// Make sure no new goroutine was launched.
-	count2 := runtime.NumGoroutine()
-	require.LessOrEqual(t, count2, count1)
-
-	// Cancel root context.
-	cancel()
-
-	// Make sure wg's counter gets to 0 eventually.
-	g.WgWait()
-}
diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go
index cdeae6d81..174710559 100644
--- a/htlcswitch/link_test.go
+++ b/htlcswitch/link_test.go
@@ -2257,7 +2257,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 			for {
 				select {
 				case <-notifyUpdateChan:
-				case <-chanLink.cg.Done():
+				case <-chanLink.Quit:
 					close(doneChan)
 					return
 				}
@@ -2326,7 +2326,7 @@ func handleStateUpdate(link *channelLink,
 	}
 	link.HandleChannelUpdate(remoteRev)
 
-	ctx, done := link.cg.Create(context.Background())
+	ctx, done := link.WithCtxQuitNoTimeout()
 	defer done()
 
 	remoteSigs, err := remoteChannel.SignNextCommitment(ctx)
@@ -2372,7 +2372,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
 		// Trigger update by ticking the batchTicker.
 		select {
 		case batchTick <- time.Now():
-		case <-link.cg.Done():
+		case <-link.Quit:
 			return fmt.Errorf("link shutting down")
 		}
 		return handleStateUpdate(link, remoteChannel)
@@ -2380,7 +2380,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.
-	ctx, done := link.cg.Create(context.Background())
+	ctx, done := link.WithCtxQuitNoTimeout()
 	defer done()
 
 	remoteSigs, err := remoteChannel.SignNextCommitment(ctx)
@@ -4946,7 +4946,7 @@ func (h *persistentLinkHarness) restartLink(
 			for {
 				select {
 				case <-notifyUpdateChan:
-				case <-chanLink.cg.Done():
+				case <-chanLink.Quit:
 					close(doneChan)
 					return
 				}
@@ -5932,9 +5932,7 @@ func TestChannelLinkFail(t *testing.T) {
 
 				// Sign a commitment that will include
 				// signature for the HTLC just sent.
-				quitCtx, done := c.cg.Create(
-					context.Background(),
-				)
+				quitCtx, done := c.WithCtxQuitNoTimeout()
 				defer done()
 
 				sigs, err := remoteChannel.SignNextCommitment(
@@ -5981,9 +5979,7 @@ func TestChannelLinkFail(t *testing.T) {
 
 				// Sign a commitment that will include
 				// signature for the HTLC just sent.
-				quitCtx, done := c.cg.Create(
-					context.Background(),
-				)
+				quitCtx, done := c.WithCtxQuitNoTimeout()
 				defer done()
 
 				sigs, err := remoteChannel.SignNextCommitment(
diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go
index 43902dc33..212346588 100644
--- a/htlcswitch/test_utils.go
+++ b/htlcswitch/test_utils.go
@@ -1190,7 +1190,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
 			for {
 				select {
 				case <-notifyUpdateChan:
-				case <-chanLink.cg.Done():
+				case <-chanLink.Quit:
 					close(doneChan)
 					return
 				}
diff --git a/itest/lnd_sweep_test.go b/itest/lnd_sweep_test.go
index 60f63ac3c..8ad00a430 100644
--- a/itest/lnd_sweep_test.go
+++ b/itest/lnd_sweep_test.go
@@ -928,7 +928,12 @@ func testSweepHTLCs(ht *lntest.HarnessTest) {
 	require.Len(ht, outgoingSweep.TxOut, 2)
 
 	// Calculate the ending fee rate.
-	outgoingBudget := invoiceAmt.MulF64(contractcourt.DefaultBudgetRatio)
+	//
+	// TODO(yy): the budget we use to sweep the first-level outgoing HTLC
+	// is twice its value. This is a temporary mitigation to prevent
+	// cascading FCs and the test should be updated once it's properly
+	// fixed.
+	outgoingBudget := 2 * invoiceAmt
 	outgoingTxSize := ht.CalculateTxWeight(outgoingSweep)
 	outgoingEndFeeRate := chainfee.NewSatPerKWeight(
 		outgoingBudget, outgoingTxSize,
diff --git a/itest/lnd_switch_test.go b/itest/lnd_switch_test.go
index 82900c9d3..10be21613 100644
--- a/itest/lnd_switch_test.go
+++ b/itest/lnd_switch_test.go
@@ -64,7 +64,7 @@ func testSwitchCircuitPersistence(ht *lntest.HarnessTest) {
 	// transaction, in channel Bob->Alice->David->Carol, order is Carol,
 	// David, Alice, Bob.
 	var amountPaid = int64(5000)
-	s.assertAmountPaid(ht, amountPaid, numPayments)
+	s.assertAmoutPaid(ht, amountPaid, numPayments)
 
 	// Lastly, we will send one more payment to ensure all channels are
 	// still functioning properly.
@@ -80,7 +80,7 @@ func testSwitchCircuitPersistence(ht *lntest.HarnessTest) {
 	ht.CompletePaymentRequests(s.bob, payReqs)
 
 	amountPaid = int64(6000)
-	s.assertAmountPaid(ht, amountPaid, numPayments+1)
+	s.assertAmoutPaid(ht, amountPaid, numPayments+1)
 }
 
 // testSwitchOfflineDelivery constructs a set of multihop payments, and tests
@@ -136,7 +136,7 @@ func testSwitchOfflineDelivery(ht *lntest.HarnessTest) {
 	// transaction, in channel Bob->Alice->David->Carol, order is Carol,
 	// David, Alice, Bob.
 	var amountPaid = int64(5000)
-	s.assertAmountPaid(ht, amountPaid, numPayments)
+	s.assertAmoutPaid(ht, amountPaid, numPayments)
 
 	// Lastly, we will send one more payment to ensure all channels are
 	// still functioning properly.
@@ -152,7 +152,7 @@ func testSwitchOfflineDelivery(ht *lntest.HarnessTest) {
 	ht.CompletePaymentRequests(s.bob, payReqs)
 
 	amountPaid = int64(6000)
-	s.assertAmountPaid(ht, amountPaid, numPayments+1)
+	s.assertAmoutPaid(ht, amountPaid, numPayments+1)
 }
 
 // testSwitchOfflineDeliveryPersistence constructs a set of multihop payments,
@@ -218,7 +218,7 @@ func testSwitchOfflineDeliveryPersistence(ht *lntest.HarnessTest) {
 	// transaction, in channel Bob->Alice->David->Carol, order is Carol,
 	// David, Alice, Bob.
 	var amountPaid = int64(5000)
-	s.assertAmountPaid(ht, amountPaid, numPayments)
+	s.assertAmoutPaid(ht, amountPaid, numPayments)
 
 	// Lastly, we will send one more payment to ensure all channels are
 	// still functioning properly.
@@ -238,7 +238,7 @@ func testSwitchOfflineDeliveryPersistence(ht *lntest.HarnessTest) {
 	ht.CompletePaymentRequests(s.bob, payReqs)
 
 	amountPaid = int64(6000)
-	s.assertAmountPaid(ht, amountPaid, numPayments+1)
+	s.assertAmoutPaid(ht, amountPaid, numPayments+1)
 }
 
 // testSwitchOfflineDeliveryOutgoingOffline constructs a set of multihop
@@ -453,10 +453,10 @@ func (s *scenarioFourNodes) assertHTLCs(ht *lntest.HarnessTest, num int) {
 	ht.AssertNumActiveHtlcs(s.carol, num)
 }
 
-// assertAmountPaid is a helper method which takes a given paid amount
+// assertAmoutPaid is a helper method which takes a given paid amount
 // and number of payments and asserts the desired payments are made in
 // the four nodes.
-func (s *scenarioFourNodes) assertAmountPaid(ht *lntest.HarnessTest,
+func (s *scenarioFourNodes) assertAmoutPaid(ht *lntest.HarnessTest,
 	amt int64, num int64) {
 
 	ht.AssertAmountPaid(
diff --git a/lnwallet/chancloser/rbf_coop_test.go b/lnwallet/chancloser/rbf_coop_test.go
index 17d783eae..07bc32e3d 100644
--- a/lnwallet/chancloser/rbf_coop_test.go
+++ b/lnwallet/chancloser/rbf_coop_test.go
@@ -2,7 +2,6 @@ package chancloser
 
 import (
 	"bytes"
-	"context"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -145,9 +144,7 @@ func assertUnknownEventFail(t *testing.T, startingState ProtocolState) {
 
 		closeHarness.expectFailure(ErrInvalidStateTransition)
 
-		closeHarness.chanCloser.SendEvent(
-			context.Background(), &unknownEvent{},
-		)
+		closeHarness.chanCloser.SendEvent(&unknownEvent{})
 
 		// There should be no further state transitions.
 		closeHarness.assertNoStateTransitions()
@@ -484,7 +481,6 @@ func (r *rbfCloserTestHarness) expectHalfSignerIteration(
 	initEvent ProtocolEvent, balanceAfterClose, absoluteFee btcutil.Amount,
 	dustExpect dustExpectation) {
 
-	ctx := context.Background()
 	numFeeCalls := 2
 
 	// If we're using the SendOfferEvent as a trigger, we only need to call
@@ -531,7 +527,7 @@ func (r *rbfCloserTestHarness) expectHalfSignerIteration(
 	})
 	r.expectMsgSent(msgExpect)
 
-	r.chanCloser.SendEvent(ctx, initEvent)
+	r.chanCloser.SendEvent(initEvent)
 
 	// Based on the init event, we'll either just go to the closing
 	// negotiation state, or go through the channel flushing state first.
@@ -586,8 +582,6 @@ func (r *rbfCloserTestHarness) assertSingleRbfIteration(
 	initEvent ProtocolEvent, balanceAfterClose, absoluteFee btcutil.Amount,
 	dustExpect dustExpectation) {
 
-	ctx := context.Background()
-
 	// We'll now send in the send offer event, which should trigger 1/2 of
 	// the RBF loop, ending us in the LocalOfferSent state.
 	r.expectHalfSignerIteration(
@@ -613,7 +607,7 @@ func (r *rbfCloserTestHarness) assertSingleRbfIteration(
 		balanceAfterClose, true,
 	)
 
-	r.chanCloser.SendEvent(ctx, localSigEvent)
+	r.chanCloser.SendEvent(localSigEvent)
 
 	// We should transition to the pending closing state now.
 	r.assertLocalClosePending()
@@ -623,8 +617,6 @@ func (r *rbfCloserTestHarness) assertSingleRemoteRbfIteration(
 	initEvent ProtocolEvent, balanceAfterClose, absoluteFee btcutil.Amount,
 	sequence uint32, iteration bool) {
 
-	ctx := context.Background()
-
 	// If this is an iteration, then we expect some intermediate states,
 	// before we enter the main RBF/sign loop.
 	if iteration {
@@ -643,7 +635,7 @@ func (r *rbfCloserTestHarness) assertSingleRemoteRbfIteration(
 		absoluteFee, balanceAfterClose, false,
 	)
 
-	r.chanCloser.SendEvent(ctx, initEvent)
+	r.chanCloser.SendEvent(initEvent)
 
 	// Our outer state should transition to ClosingNegotiation state.
 	r.assertStateTransitions(&ClosingNegotiation{})
@@ -676,8 +668,6 @@ func assertStateT[T ProtocolState](h *rbfCloserTestHarness) T {
 func newRbfCloserTestHarness(t *testing.T,
 	cfg *harnessCfg) *rbfCloserTestHarness {
 
-	ctx := context.Background()
-
 	startingHeight := 200
 
 	chanPoint := randOutPoint(t)
@@ -757,7 +747,7 @@ func newRbfCloserTestHarness(t *testing.T,
 	).Return(nil)
 
 	chanCloser := protofsm.NewStateMachine(protoCfg)
-	chanCloser.Start(ctx)
+	chanCloser.Start()
 
 	harness.stateSub = chanCloser.RegisterStateEvents()
 
@@ -779,7 +769,6 @@ func newCloser(t *testing.T, cfg *harnessCfg) *rbfCloserTestHarness {
 // TestRbfChannelActiveTransitions tests the transitions of from the
 // ChannelActive state.
 func TestRbfChannelActiveTransitions(t *testing.T) {
-	ctx := context.Background()
 	localAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x01}, 20))
 	remoteAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x02}, 20))
 
@@ -793,7 +782,7 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
 		})
 		defer closeHarness.stopAndAssert()
 
-		closeHarness.chanCloser.SendEvent(ctx, &SpendEvent{})
+		closeHarness.chanCloser.SendEvent(&SpendEvent{})
 
 		closeHarness.assertStateTransitions(&CloseFin{})
 	})
@@ -810,7 +799,7 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
 		// We don't specify an upfront shutdown addr, and don't specify
 		// on here in the vent, so we should call new addr, but then
 		// fail.
-		closeHarness.chanCloser.SendEvent(ctx, &SendShutdown{})
+		closeHarness.chanCloser.SendEvent(&SendShutdown{})
 
 		// We shouldn't have transitioned to a new state.
 		closeHarness.assertNoStateTransitions()
@@ -835,9 +824,9 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
 
 		// If we send the shutdown event, we should transition to the
 		// shutdown pending state.
-		closeHarness.chanCloser.SendEvent(
-			ctx, &SendShutdown{IdealFeeRate: feeRate},
-		)
+		closeHarness.chanCloser.SendEvent(&SendShutdown{
+			IdealFeeRate: feeRate,
+		})
 		closeHarness.assertStateTransitions(&ShutdownPending{})
 
 		// If we examine the internal state, it should be consistent
@@ -880,9 +869,9 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
 
 		// Next, we'll emit the recv event, with the addr of the remote
 		// party.
-		closeHarness.chanCloser.SendEvent(
-			ctx, &ShutdownReceived{ShutdownScript: remoteAddr},
-		)
+		closeHarness.chanCloser.SendEvent(&ShutdownReceived{
+			ShutdownScript: remoteAddr,
+		})
 
 		// We should transition to the shutdown pending state.
 		closeHarness.assertStateTransitions(&ShutdownPending{})
@@ -910,7 +899,6 @@ func TestRbfChannelActiveTransitions(t *testing.T) {
 // shutdown ourselves.
 func TestRbfShutdownPendingTransitions(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	startingState := &ShutdownPending{}
 
@@ -925,7 +913,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
 		})
 		defer closeHarness.stopAndAssert()
 
-		closeHarness.chanCloser.SendEvent(ctx, &SpendEvent{})
+		closeHarness.chanCloser.SendEvent(&SpendEvent{})
 
 		closeHarness.assertStateTransitions(&CloseFin{})
 	})
@@ -948,7 +936,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
 		// We'll now send in a ShutdownReceived event, but with a
 		// different address provided in the shutdown message. This
 		// should result in an error.
-		closeHarness.chanCloser.SendEvent(ctx, &ShutdownReceived{
+		closeHarness.chanCloser.SendEvent(&ShutdownReceived{
 			ShutdownScript: localAddr,
 		})
 
@@ -984,9 +972,9 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
 
 		// We'll send in a shutdown received event, with the expected
 		// co-op close addr.
-		closeHarness.chanCloser.SendEvent(
-			ctx, &ShutdownReceived{ShutdownScript: remoteAddr},
-		)
+		closeHarness.chanCloser.SendEvent(&ShutdownReceived{
+			ShutdownScript: remoteAddr,
+		})
 
 		// We should transition to the channel flushing state.
 		closeHarness.assertStateTransitions(&ChannelFlushing{})
@@ -1027,7 +1015,7 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
 		closeHarness.expectFinalBalances(fn.None[ShutdownBalances]())
 
 		// We'll send in a shutdown received event.
-		closeHarness.chanCloser.SendEvent(ctx, &ShutdownComplete{})
+		closeHarness.chanCloser.SendEvent(&ShutdownComplete{})
 
 		// We should transition to the channel flushing state.
 		closeHarness.assertStateTransitions(&ChannelFlushing{})
@@ -1042,7 +1030,6 @@ func TestRbfShutdownPendingTransitions(t *testing.T) {
 // transition to the negotiation state.
 func TestRbfChannelFlushingTransitions(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	localBalance := lnwire.NewMSatFromSatoshis(10_000)
 	remoteBalance := lnwire.NewMSatFromSatoshis(50_000)
@@ -1095,9 +1082,7 @@ func TestRbfChannelFlushingTransitions(t *testing.T) {
 
 			// We'll now send in the event which should trigger
 			// this code path.
-			closeHarness.chanCloser.SendEvent(
-				ctx, &chanFlushedEvent,
-			)
+			closeHarness.chanCloser.SendEvent(&chanFlushedEvent)
 
 			// With the event sent, we should now transition
 			// straight to the ClosingNegotiation state, with no
@@ -1164,7 +1149,6 @@ func TestRbfChannelFlushingTransitions(t *testing.T) {
 // rate.
 func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	localBalance := lnwire.NewMSatFromSatoshis(40_000)
 	remoteBalance := lnwire.NewMSatFromSatoshis(50_000)
@@ -1248,7 +1232,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
 
 		// We should fail as the remote party sent us more than one
 		// signature.
-		closeHarness.chanCloser.SendEvent(ctx, localSigEvent)
+		closeHarness.chanCloser.SendEvent(localSigEvent)
 	})
 
 	// Next, we'll verify that if the balance of the remote party is dust,
@@ -1349,7 +1333,7 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
 			singleMsgMatcher[*lnwire.Shutdown](nil),
 		)
 
-		closeHarness.chanCloser.SendEvent(ctx, sendShutdown)
+		closeHarness.chanCloser.SendEvent(sendShutdown)
 
 		// We should first transition to the Channel Active state
 		// momentarily, before transitioning to the shutdown pending
@@ -1383,7 +1367,6 @@ func TestRbfCloseClosingNegotiationLocal(t *testing.T) {
 // party.
 func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	localBalance := lnwire.NewMSatFromSatoshis(40_000)
 	remoteBalance := lnwire.NewMSatFromSatoshis(50_000)
@@ -1433,7 +1416,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
 				FeeSatoshis: absoluteFee * 10,
 			},
 		}
-		closeHarness.chanCloser.SendEvent(ctx, feeOffer)
+		closeHarness.chanCloser.SendEvent(feeOffer)
 
 		// We shouldn't have transitioned to a new state.
 		closeHarness.assertNoStateTransitions()
@@ -1477,7 +1460,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
 				},
 			},
 		}
-		closeHarness.chanCloser.SendEvent(ctx, feeOffer)
+		closeHarness.chanCloser.SendEvent(feeOffer)
 
 		// We shouldn't have transitioned to a new state.
 		closeHarness.assertNoStateTransitions()
@@ -1506,7 +1489,7 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
 				},
 			},
 		}
-		closeHarness.chanCloser.SendEvent(ctx, feeOffer)
+		closeHarness.chanCloser.SendEvent(feeOffer)
 
 		// We shouldn't have transitioned to a new state.
 		closeHarness.assertNoStateTransitions()
@@ -1578,9 +1561,9 @@ func TestRbfCloseClosingNegotiationRemote(t *testing.T) {
 		// We'll now simulate the start of the RBF loop, by receiving a
 		// new Shutdown message from the remote party. This signals
 		// that they want to obtain a new commit sig.
-		closeHarness.chanCloser.SendEvent(
-			ctx, &ShutdownReceived{ShutdownScript: remoteAddr},
-		)
+		closeHarness.chanCloser.SendEvent(&ShutdownReceived{
+			ShutdownScript: remoteAddr,
+		})
 
 		// Next, we'll receive an offer from the remote party, and
 		// drive another RBF iteration. This time, we'll increase the
diff --git a/protofsm/state_machine_test.go b/protofsm/state_machine_test.go
index bda498683..ea596dc25 100644
--- a/protofsm/state_machine_test.go
+++ b/protofsm/state_machine_test.go
@@ -1,7 +1,6 @@
 package protofsm
 
 import (
-	"context"
 	"encoding/hex"
 	"fmt"
 	"sync/atomic"
@@ -224,8 +223,6 @@ func (d *dummyAdapters) RegisterSpendNtfn(outpoint *wire.OutPoint,
 // TestStateMachineOnInitDaemonEvent tests that the state machine will properly
 // execute any init-level daemon events passed into it.
 func TestStateMachineOnInitDaemonEvent(t *testing.T) {
-	ctx := context.Background()
-
 	// First, we'll create our state machine given the env, and our
 	// starting state.
 	env := &dummyEnv{}
@@ -257,7 +254,7 @@ func TestStateMachineOnInitDaemonEvent(t *testing.T) {
 	stateSub := stateMachine.RegisterStateEvents()
 	defer stateMachine.RemoveStateSub(stateSub)
 
-	stateMachine.Start(ctx)
+	stateMachine.Start()
 	defer stateMachine.Stop()
 
 	// Assert that we go from the starting state to the final state.  The
@@ -278,7 +275,6 @@ func TestStateMachineOnInitDaemonEvent(t *testing.T) {
 // transition.
 func TestStateMachineInternalEvents(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	// First, we'll create our state machine given the env, and our
 	// starting state.
@@ -300,12 +296,12 @@ func TestStateMachineInternalEvents(t *testing.T) {
 	stateSub := stateMachine.RegisterStateEvents()
 	defer stateMachine.RemoveStateSub(stateSub)
 
-	stateMachine.Start(ctx)
+	stateMachine.Start()
 	defer stateMachine.Stop()
 
 	// For this transition, we'll send in the emitInternal event, which'll
 	// send us back to the starting event, but emit an internal event.
-	stateMachine.SendEvent(ctx, &emitInternal{})
+	stateMachine.SendEvent(&emitInternal{})
 
 	// We'll now also assert the path we took to get here to ensure the
 	// internal events were processed.
@@ -327,7 +323,6 @@ func TestStateMachineInternalEvents(t *testing.T) {
 // daemon emitted as part of the state transition process.
 func TestStateMachineDaemonEvents(t *testing.T) {
 	t.Parallel()
-	ctx := context.Background()
 
 	// First, we'll create our state machine given the env, and our
 	// starting state.
@@ -353,7 +348,7 @@ func TestStateMachineDaemonEvents(t *testing.T) {
 	stateSub := stateMachine.RegisterStateEvents()
 	defer stateMachine.RemoveStateSub(stateSub)
 
-	stateMachine.Start(ctx)
+	stateMachine.Start()
 	defer stateMachine.Stop()
 
 	// As soon as we send in the daemon event, we expect the
@@ -365,7 +360,7 @@ func TestStateMachineDaemonEvents(t *testing.T) {
 
 	// We'll start off by sending in the daemon event, which'll trigger the
 	// state machine to execute the series of daemon events.
-	stateMachine.SendEvent(ctx, &daemonEvents{})
+	stateMachine.SendEvent(&daemonEvents{})
 
 	// We should transition back to the starting state now, after we
 	// started from the very same state.
@@ -407,8 +402,6 @@ func (d *dummyMsgMapper) MapMsg(wireMsg lnwire.Message) fn.Option[dummyEvents] {
 // TestStateMachineMsgMapper tests that given a message mapper, we can properly
 // send in wire messages get mapped to FSM events.
 func TestStateMachineMsgMapper(t *testing.T) {
-	ctx := context.Background()
-
 	// First, we'll create our state machine given the env, and our
 	// starting state.
 	env := &dummyEnv{}
@@ -443,7 +436,7 @@ func TestStateMachineMsgMapper(t *testing.T) {
 	stateSub := stateMachine.RegisterStateEvents()
 	defer stateMachine.RemoveStateSub(stateSub)
 
-	stateMachine.Start(ctx)
+	stateMachine.Start()
 	defer stateMachine.Stop()
 
 	// First, we'll verify that the CanHandle method works as expected.
@@ -452,7 +445,7 @@ func TestStateMachineMsgMapper(t *testing.T) {
 
 	// Next, we'll attempt to send the wire message into the state machine.
 	// We should transition to the final state.
-	require.True(t, stateMachine.SendMessage(ctx, wireError))
+	require.True(t, stateMachine.SendMessage(wireError))
 
 	// We should transition to the final state.
 	expectedStates := []State[dummyEvents, *dummyEnv]{