Repository: lightningnetwork/lnd
Pull request #8631 (2024-04-08 11:51:13)

[code-health]: reduce duplication in brontide test.
## Change Description
This pull request is an off shoot of the peer backup PR (currently halted): https://github.com/lightningnetwork/lnd/pull/8490

It removes the duplicity in creating a peer instance and starting a peer in brontide tests.

It does not require a change log.



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

> [!NOTE]
> Currently processing new changes in this PR. This may take a few minutes, please wait...
> 
> <details>
> <summary>Commits</summary>
> Files that changed from the base of the PR and between 6377f98536a6e5f22924ea498f8565b2dcbe4aee and 793040320ac8a37b60d58dd28b05275cf79c894f.
> </details>
> 
> 
> <details>
> <summary>Files selected for processing (2)</summary>
> 
> * peer/brontide_test.go (14 hunks)
> * peer/test_utils.go (12 hunks)
> 
> </details>
> 
> 
> 
> ```ascii
>  __________________________________________
> < Lint long and prosper with cleaner code. >
>  ------------------------------------------
>   \
>    \   (\__/)
>        (•ㅅ•)
>        / 　 づ
> ```

<!-- end of auto-generated comment: summarize review in progress by coderabbit.ai --><!-- tweet_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>

<!-- tweet_end --><!-- tips_start -->

---

<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.`

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 as PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger a review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai help` to get help.


Additionally, you can add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.

### CodeRabbit Configration 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 -->
Chinwendu20: @ProofOfKeags 
lightninglabs-deploy: @chinwendu20, remember to re-request review from reviewers when ready
lightninglabs-deploy: @chinwendu20, remember to re-request review from reviewers when ready

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 6d23cf8d9..429b09171 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -225,7 +225,6 @@ jobs:
         if: matrix.unit_type == 'btcd unit-cover'
         with:
           path-to-profile: coverage.txt
-          flag-name: 'unit'
           parallel: true
 
 
@@ -242,13 +241,13 @@ jobs:
       matrix:
         include:
           - name: btcd
-            args: backend=btcd cover=1
+            args: backend=btcd
           - name: bitcoind
-            args: backend=bitcoind cover=1
+            args: backend=bitcoind
           - name: bitcoind-notxindex
             args: backend="bitcoind notxindex"
           - name: bitcoind-rpcpolling
-            args: backend="bitcoind rpcpolling" cover=1
+            args: backend="bitcoind rpcpolling"
           - name: bitcoind-etcd
             args: backend=bitcoind dbbackend=etcd
           - name: bitcoind-postgres
@@ -260,7 +259,7 @@ jobs:
           - name: bitcoind-sqlite-nativesql
             args: backend=bitcoind dbbackend=sqlite nativesql=true
           - name: neutrino
-            args: backend=neutrino cover=1
+            args: backend=neutrino
     steps:
       - name: git checkout
         uses: actions/checkout@v3
@@ -277,14 +276,6 @@ jobs:
       - name: run ${{ matrix.name }}
         run: make itest-parallel ${{ matrix.args }}
 
-      - name: Send coverage
-        if: ${{ contains(matrix.args, 'cover=1') }}
-        uses: shogo82148/actions-goveralls@v1
-        with:
-          path-to-profile: coverage.txt
-          flag-name: 'itest-${{ matrix.name }}'
-          parallel: true
-
       - name: Zip log files on failure
         if: ${{ failure() }}
         timeout-minutes: 5 # timeout after 5 minute
@@ -410,13 +401,3 @@ jobs:
 
       - name: release notes check
         run: scripts/check-release-notes.sh
-  
-  # Notify about the completion of all coverage collecting jobs.
-  finish:
-    if: ${{ always() }}
-    needs: [unit-test, ubuntu-integration-test]
-    runs-on: ubuntu-latest
-    steps:
-      - uses: shogo82148/actions-goveralls@v1
-        with:
-          parallel-finished: true
diff --git a/.gitignore b/.gitignore
index a44a7b8f5..16d33c142 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,7 +38,6 @@ itest/.minerlogs
 itest/lnd-itest
 itest/btcd-itest
 itest/.logs-*
-itest/cover
 
 cmd/cmd
 *.key
diff --git a/Makefile b/Makefile
index 5ea087892..088bb4880 100644
--- a/Makefile
+++ b/Makefile
@@ -111,7 +111,7 @@ build:
 build-itest:
 	@$(call print, "Building itest btcd and lnd.")
 	CGO_ENABLED=0 $(GOBUILD) -tags="integration" -o itest/btcd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(BTCD_PKG)
-	CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" $(ITEST_COVERAGE) -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd
+	CGO_ENABLED=0 $(GOBUILD) -tags="$(ITEST_TAGS)" -o itest/lnd-itest$(EXEC_SUFFIX) $(DEV_LDFLAGS) $(PKG)/cmd/lnd
 
 	@$(call print, "Building itest binary for ${backend} backend.")
 	CGO_ENABLED=0 $(GOTEST) -v ./itest -tags="$(DEV_TAGS) $(RPC_TAGS) integration $(backend)" -c -o itest/itest.test$(EXEC_SUFFIX)
@@ -136,13 +136,8 @@ manpages:
 	@$(call print, "Generating man pages lncli.1 and lnd.1.")
 	./scripts/gen_man_pages.sh $(DESTDIR) $(PREFIX)
 
-#? install: Build and install lnd and lncli binaries and place them in $GOPATH/bin.
-install: install-binaries
-
-#? install-all: Performs all the same tasks as the install command along with generating and
-# installing the man pages for the lnd and lncli binaries. This command is useful in an
-# environment where a user has root access and so has write access to the man page directory.
-install-all: install manpages
+#? install: Build and install lnd and lncli binaries, place them in $GOPATH/bin, generate and install man pages
+install: install-binaries manpages
 
 #? release-install: Build and install lnd and lncli release binaries, place them in $GOPATH/bin
 release-install:
@@ -203,7 +198,6 @@ itest-only: db-instance
 	@$(call print, "Running integration tests with ${backend} backend.")
 	rm -rf itest/*.log itest/.logs-*; date
 	EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_part.sh 0 1 $(TEST_FLAGS) $(ITEST_FLAGS)
-	$(COLLECT_ITEST_COVERAGE)
 
 #? itest: Build and run integration tests
 itest: build-itest itest-only
@@ -216,7 +210,6 @@ itest-parallel: build-itest db-instance
 	@$(call print, "Running tests")
 	rm -rf itest/*.log itest/.logs-*; date
 	EXEC_SUFFIX=$(EXEC_SUFFIX) scripts/itest_parallel.sh $(ITEST_PARALLELISM) $(NUM_ITEST_TRANCHES) $(TEST_FLAGS) $(ITEST_FLAGS)
-	$(COLLECT_ITEST_COVERAGE)
 
 #? itest-clean: Kill all running itest processes
 itest-clean:
diff --git a/build/version.go b/build/version.go
index 9ff734a2b..62aece381 100644
--- a/build/version.go
+++ b/build/version.go
@@ -47,7 +47,7 @@ const (
 
 	// AppPreRelease MUST only contain characters from semanticAlphabet per
 	// the semantic versioning spec.
-	AppPreRelease = "beta.rc2"
+	AppPreRelease = "beta.rc1"
 )
 
 func init() {
diff --git a/chainreg/chainregistry.go b/chainreg/chainregistry.go
index 37e72fdee..8b562e87a 100644
--- a/chainreg/chainregistry.go
+++ b/chainreg/chainregistry.go
@@ -77,13 +77,9 @@ type Config struct {
 	// ActiveNetParams details the current chain we are on.
 	ActiveNetParams BitcoinNetParams
 
-	// Deprecated: Use Fee.URL. FeeURL defines the URL for fee estimation
-	// we will use. This field is optional.
-	FeeURL string
-
-	// Fee defines settings for the web fee estimator. This field is
+	// FeeURL defines the URL for fee estimation we will use. This field is
 	// optional.
-	Fee *lncfg.Fee
+	FeeURL string
 
 	// Dialer is a function closure that will be used to establish outbound
 	// TCP connections to Bitcoin peers in the event of a pruned block being
@@ -247,16 +243,6 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
 			"cache: %v", err)
 	}
 
-	// Map the deprecated feeurl flag to fee.url.
-	if cfg.FeeURL != "" {
-		if cfg.Fee.URL != "" {
-			return nil, nil, errors.New("fee.url and " +
-				"feeurl are mutually exclusive")
-		}
-
-		cfg.Fee.URL = cfg.FeeURL
-	}
-
 	// If spv mode is active, then we'll be using a distinct set of
 	// chainControl interfaces that interface directly with the p2p network
 	// of the selected chain.
@@ -275,6 +261,18 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
 			return nil, nil, err
 		}
 
+		// Map the deprecated neutrino feeurl flag to the general fee
+		// url.
+		if cfg.NeutrinoMode.FeeURL != "" {
+			if cfg.FeeURL != "" {
+				return nil, nil, errors.New("feeurl and " +
+					"neutrino.feeurl are mutually " +
+					"exclusive")
+			}
+
+			cfg.FeeURL = cfg.NeutrinoMode.FeeURL
+		}
+
 		cc.ChainSource = chain.NewNeutrinoClient(
 			cfg.ActiveNetParams.Params, cfg.NeutrinoCS,
 		)
@@ -696,34 +694,27 @@ func NewPartialChainControl(cfg *Config) (*PartialChainControl, func(), error) {
 	// If the fee URL isn't set, and the user is running mainnet, then
 	// we'll return an error to instruct them to set a proper fee
 	// estimator.
-	case cfg.Fee.URL == "" && cfg.Bitcoin.MainNet &&
+	case cfg.FeeURL == "" && cfg.Bitcoin.MainNet &&
 		cfg.Bitcoin.Node == "neutrino":
 
-		return nil, nil, fmt.Errorf("--fee.url parameter required " +
+		return nil, nil, fmt.Errorf("--feeurl parameter required " +
 			"when running neutrino on mainnet")
 
 	// Override default fee estimator if an external service is specified.
-	case cfg.Fee.URL != "":
+	case cfg.FeeURL != "":
 		// Do not cache fees on regtest to make it easier to execute
 		// manual or automated test cases.
 		cacheFees := !cfg.Bitcoin.RegTest
 
-		log.Infof("Using external fee estimator %v: cached=%v: "+
-			"min update timeout=%v, max update timeout=%v",
-			cfg.Fee.URL, cacheFees, cfg.Fee.MinUpdateTimeout,
-			cfg.Fee.MaxUpdateTimeout)
+		log.Infof("Using external fee estimator %v: cached=%v",
+			cfg.FeeURL, cacheFees)
 
-		cc.FeeEstimator, err = chainfee.NewWebAPIEstimator(
+		cc.FeeEstimator = chainfee.NewWebAPIEstimator(
 			chainfee.SparseConfFeeSource{
-				URL: cfg.Fee.URL,
+				URL: cfg.FeeURL,
 			},
 			!cacheFees,
-			cfg.Fee.MinUpdateTimeout,
-			cfg.Fee.MaxUpdateTimeout,
 		)
-		if err != nil {
-			return nil, nil, err
-		}
 	}
 
 	ccCleanup := func() {
diff --git a/channeldb/invoices.go b/channeldb/invoices.go
index df124b632..a7663cabf 100644
--- a/channeldb/invoices.go
+++ b/channeldb/invoices.go
@@ -2139,14 +2139,14 @@ func (d *DB) DeleteCanceledInvoices(_ context.Context) error {
 			invoiceIndexBucket,
 		)
 		if invoiceIndex == nil {
-			return nil
+			return invpkg.ErrNoInvoicesCreated
 		}
 
 		invoiceAddIndex := invoices.NestedReadWriteBucket(
 			addIndexBucket,
 		)
 		if invoiceAddIndex == nil {
-			return nil
+			return invpkg.ErrNoInvoicesCreated
 		}
 
 		payAddrIndex := tx.ReadWriteBucket(payAddrIndexBucket)
diff --git a/cmd/lncli/cmd_payments.go b/cmd/lncli/cmd_payments.go
index a554b9dec..4288b0f01 100644
--- a/cmd/lncli/cmd_payments.go
+++ b/cmd/lncli/cmd_payments.go
@@ -886,7 +886,6 @@ func payInvoice(ctx *cli.Context) error {
 		PaymentRequest:    stripPrefix(payReq),
 		Amt:               ctx.Int64("amt"),
 		DestCustomRecords: make(map[uint64][]byte),
-		Amp:               ctx.Bool(ampFlag.Name),
 	}
 
 	return sendPaymentRequest(ctx, req)
diff --git a/config.go b/config.go
index a8b609deb..6c8df62dd 100644
--- a/config.go
+++ b/config.go
@@ -350,7 +350,7 @@ type Config struct {
 	MaxPendingChannels int    `long:"maxpendingchannels" description:"The maximum number of incoming pending channels permitted per peer."`
 	BackupFilePath     string `long:"backupfilepath" description:"The target location of the channel backup file"`
 
-	FeeURL string `long:"feeurl" description:"DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet." hidden:"true"`
+	FeeURL string `long:"feeurl" description:"Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet."`
 
 	Bitcoin      *lncfg.Chain    `group:"Bitcoin" namespace:"bitcoin"`
 	BtcdMode     *lncfg.Btcd     `group:"btcd" namespace:"btcd"`
@@ -442,8 +442,6 @@ type Config struct {
 
 	DustThreshold uint64 `long:"dust-threshold" description:"Sets the dust sum threshold in satoshis for a channel after which dust HTLC's will be failed."`
 
-	Fee *lncfg.Fee `group:"fee" namespace:"fee"`
-
 	Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`
 
 	Routing *lncfg.Routing `group:"routing" namespace:"routing"`
@@ -584,12 +582,6 @@ func DefaultConfig() Config {
 		MinBackoff:         defaultMinBackoff,
 		MaxBackoff:         defaultMaxBackoff,
 		ConnectionTimeout:  tor.DefaultConnTimeout,
-
-		Fee: &lncfg.Fee{
-			MinUpdateTimeout: lncfg.DefaultMinUpdateTimeout,
-			MaxUpdateTimeout: lncfg.DefaultMaxUpdateTimeout,
-		},
-
 		SubRPCServers: &subRPCServerConfigs{
 			SignRPC:   &signrpc.Config{},
 			RouterRPC: routerrpc.DefaultConfig(),
diff --git a/config_builder.go b/config_builder.go
index 49a0b6c50..895f7e4ad 100644
--- a/config_builder.go
+++ b/config_builder.go
@@ -550,11 +550,6 @@ func (d *DefaultWalletImpl) BuildWalletConfig(ctx context.Context,
 		NeutrinoCS:                  neutrinoCS,
 		ActiveNetParams:             d.cfg.ActiveNetParams,
 		FeeURL:                      d.cfg.FeeURL,
-		Fee: &lncfg.Fee{
-			URL:              d.cfg.Fee.URL,
-			MinUpdateTimeout: d.cfg.Fee.MinUpdateTimeout,
-			MaxUpdateTimeout: d.cfg.Fee.MaxUpdateTimeout,
-		},
 		Dialer: func(addr string) (net.Conn, error) {
 			return d.cfg.net.Dial(
 				"tcp", addr, d.cfg.ConnectionTimeout,
diff --git a/dev.Dockerfile b/dev.Dockerfile
index bb7c67312..d3ef269de 100644
--- a/dev.Dockerfile
+++ b/dev.Dockerfile
@@ -24,7 +24,7 @@ COPY . /go/src/github.com/lightningnetwork/lnd
 #  Install/build lnd.
 RUN cd /go/src/github.com/lightningnetwork/lnd \
 &&  make \
-&&  make install-all tags="signrpc walletrpc chainrpc invoicesrpc peersrpc"
+&&  make install tags="signrpc walletrpc chainrpc invoicesrpc peersrpc"
 
 # Start a new, final image to reduce size.
 FROM alpine as final
diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md
index 7628726ef..7e9f1e616 100644
--- a/docs/release-notes/release-notes-0.18.0.md
+++ b/docs/release-notes/release-notes-0.18.0.md
@@ -209,17 +209,6 @@
   its bitcoin peers' `feefilter` values into
   account](https://github.com/lightningnetwork/lnd/pull/8418).
 
-* Web fee estimator settings have been moved into a new `fee` config group.
-  A new `fee.url` option has been added within this group that replaces the old
-  `feeurl` option, which is now deprecated. Additionally, [two new config values,
-  fee.min-update-timeout and fee.max-update-timeout](https://github.com/lightningnetwork/lnd/pull/8484)
-  are added to allow users to specify the minimum and maximum time between fee
-  updates from the web fee estimator. The default values are 5 minutes and 20
-  minutes respectively. These values are used to prevent the fee estimator from
-  being queried too frequently. This replaces previously hardcoded values that
-  were set to the same values as the new defaults. The previously deprecated
-  `neutrino.feeurl` option has been removed.
-
 * [Preparatory work](https://github.com/lightningnetwork/lnd/pull/8159) for 
   forwarding of blinded routes was added, along with [support](https://github.com/lightningnetwork/lnd/pull/8160)
   for forwarding blinded payments and [error handling](https://github.com/lightningnetwork/lnd/pull/8485).
@@ -284,9 +273,7 @@
 
 * [Man pages](https://github.com/lightningnetwork/lnd/pull/8525) Generate man
   pages automatically using `lncli generatemanpage` command for both `lncli`
-  and `lnd` commands when running 
-  [`make install-all`](https://github.com/lightningnetwork/lnd/pull/8739) in 
-  the Makefile.
+  and `lnd` commands when running `make install` in the Makefile.
 
 # Improvements
 ## Functional Updates
@@ -446,9 +433,6 @@
   logged](https://github.com/lightningnetwork/lnd/pull/8693) when no values are
   specified.
 
-* Removed deprecated `neutrino.feeurl` option. Please use the newer `fee.url`
-  option instead.
-
 ## Performance Improvements
 
 * Watchtower client DB migration to massively [improve the start-up 
@@ -585,7 +569,6 @@
 * testwill
 * Thabokani
 * threewebcode
-* Tom Kirkpatrick
 * Turtle
 * twofaktor
 * vuittont60
diff --git a/fn/map.go b/fn/map.go
deleted file mode 100644
index 071da9872..000000000
--- a/fn/map.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package fn
-
-import (
-	"fmt"
-
-	"golang.org/x/exp/maps"
-)
-
-// KeySet converts a map into a Set containing the keys of the map.
-func KeySet[K comparable, V any](m map[K]V) Set[K] {
-	return NewSet(maps.Keys(m)...)
-}
-
-// NewSubMapIntersect returns a sub-map of `m` containing only the keys found in
-// both `m` and the `keys` slice.
-func NewSubMapIntersect[K comparable, V any](m map[K]V, keys []K) map[K]V {
-	result := make(map[K]V)
-	for _, k := range keys {
-		v, ok := m[k]
-		if !ok {
-			continue
-		}
-
-		result[k] = v
-	}
-
-	return result
-}
-
-// NewSubMap creates a sub-map from a given map using specified keys. It errors
-// if any of the keys is not found in the map.
-func NewSubMap[K comparable, V any](m map[K]V, keys []K) (map[K]V, error) {
-	result := make(map[K]V, len(keys))
-	for _, k := range keys {
-		v, ok := m[k]
-		if !ok {
-			return nil, fmt.Errorf("NewSubMap: missing key %v", k)
-		}
-
-		result[k] = v
-	}
-
-	return result, nil
-}
diff --git a/fn/slice.go b/fn/slice.go
index 2e27acc45..fe821bff7 100644
--- a/fn/slice.go
+++ b/fn/slice.go
@@ -1,13 +1,5 @@
 package fn
 
-import "golang.org/x/exp/constraints"
-
-// Number is a type constraint for all numeric types in Go (integers,
-// float and complex numbers)
-type Number interface {
-	constraints.Integer | constraints.Float | constraints.Complex
-}
-
 // All returns true when the supplied predicate evaluates to true for all of
 // the values in the slice.
 func All[A any](pred func(A) bool, s []A) bool {
@@ -176,25 +168,3 @@ func ZipWith[A, B, C any](f func(A, B) C, a []A, b []B) []C {
 
 	return res
 }
-
-// SliceToMap converts a slice to a map using the provided key and value
-// functions.
-func SliceToMap[A any, K comparable, V any](s []A, keyFunc func(A) K,
-	valueFunc func(A) V) map[K]V {
-
-	res := make(map[K]V, len(s))
-	for _, val := range s {
-		key := keyFunc(val)
-		value := valueFunc(val)
-		res[key] = value
-	}
-
-	return res
-}
-
-// Sum calculates the sum of a slice of numbers, `items`.
-func Sum[B Number](items []B) B {
-	return Foldl(func(a, b B) B {
-		return a + b
-	}, 0, items)
-}
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index c0c828f83..ed3151728 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -202,15 +202,15 @@ type ChannelLinkConfig struct {
 	// receiving node is persistent.
 	UnsafeReplay bool
 
-	// MinUpdateTimeout represents the minimum interval in which a link
+	// MinFeeUpdateTimeout represents the minimum interval in which a link
 	// will propose to update its commitment fee rate. A random timeout will
-	// be selected between this and MaxUpdateTimeout.
-	MinUpdateTimeout time.Duration
+	// be selected between this and MaxFeeUpdateTimeout.
+	MinFeeUpdateTimeout time.Duration
 
-	// MaxUpdateTimeout represents the maximum interval in which a link
+	// MaxFeeUpdateTimeout represents the maximum interval in which a link
 	// will propose to update its commitment fee rate. A random timeout will
-	// be selected between this and MinUpdateTimeout.
-	MaxUpdateTimeout time.Duration
+	// be selected between this and MinFeeUpdateTimeout.
+	MaxFeeUpdateTimeout time.Duration
 
 	// OutgoingCltvRejectDelta defines the number of blocks before expiry of
 	// an htlc where we don't offer an htlc anymore. This should be at least
@@ -1558,8 +1558,8 @@ func getResolutionFailure(resolution *invoices.HtlcFailResolution,
 // within the link's configuration that will be used to determine when the link
 // should propose an update to its commitment fee rate.
 func (l *channelLink) randomFeeUpdateTimeout() time.Duration {
-	lower := int64(l.cfg.MinUpdateTimeout)
-	upper := int64(l.cfg.MaxUpdateTimeout)
+	lower := int64(l.cfg.MinFeeUpdateTimeout)
+	upper := int64(l.cfg.MaxFeeUpdateTimeout)
 	return time.Duration(prand.Int63n(upper-lower) + lower)
 }
 
diff --git a/lncfg/fee.go b/lncfg/fee.go
deleted file mode 100644
index 200ae9e81..000000000
--- a/lncfg/fee.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package lncfg
-
-import "time"
-
-// DefaultMinUpdateTimeout represents the minimum interval in which a
-// WebAPIEstimator will request fresh fees from its API.
-const DefaultMinUpdateTimeout = 5 * time.Minute
-
-// DefaultMaxUpdateTimeout represents the maximum interval in which a
-// WebAPIEstimator will request fresh fees from its API.
-const DefaultMaxUpdateTimeout = 20 * time.Minute
-
-// Fee holds the configuration options for fee estimation.
-//
-//nolint:lll
-type Fee struct {
-	URL              string        `long:"url" description:"Optional URL for external fee estimation. If no URL is specified, the method for fee estimation will depend on the chosen backend and network. Must be set for neutrino on mainnet."`
-	MinUpdateTimeout time.Duration `long:"min-update-timeout" description:"The minimum interval in which fees will be updated from the specified fee URL."`
-	MaxUpdateTimeout time.Duration `long:"max-update-timeout" description:"The maximum interval in which fees will be updated from the specified fee URL."`
-}
diff --git a/lncfg/neutrino.go b/lncfg/neutrino.go
index e46c292dc..09a66312a 100644
--- a/lncfg/neutrino.go
+++ b/lncfg/neutrino.go
@@ -12,6 +12,7 @@ type Neutrino struct {
 	MaxPeers           int           `long:"maxpeers" description:"Max number of inbound and outbound peers"`
 	BanDuration        time.Duration `long:"banduration" description:"How long to ban misbehaving peers.  Valid time units are {s, m, h}.  Minimum 1 second"`
 	BanThreshold       uint32        `long:"banthreshold" description:"Maximum allowed ban score before disconnecting and banning misbehaving peers."`
+	FeeURL             string        `long:"feeurl" description:"DEPRECATED: Use top level 'feeurl' option. Optional URL for fee estimation. If a URL is not specified, static fees will be used for estimation." hidden:"true"`
 	AssertFilterHeader string        `long:"assertfilterheader" description:"Optional filter header in height:hash format to assert the state of neutrino's filter header chain on startup. If the assertion does not hold, then the filter header chain will be re-synced from the genesis block."`
 	UserAgentName      string        `long:"useragentname" description:"Used to help identify ourselves to other bitcoin peers"`
 	UserAgentVersion   string        `long:"useragentversion" description:"Used to help identify ourselves to other bitcoin peers"`
diff --git a/lnwallet/chainfee/estimator.go b/lnwallet/chainfee/estimator.go
index 0ce9ae9b8..76c235d1b 100644
--- a/lnwallet/chainfee/estimator.go
+++ b/lnwallet/chainfee/estimator.go
@@ -28,6 +28,14 @@ const (
 	// less than this will result in an error.
 	minBlockTarget uint32 = 1
 
+	// minFeeUpdateTimeout represents the minimum interval in which a
+	// WebAPIEstimator will request fresh fees from its API.
+	minFeeUpdateTimeout = 5 * time.Minute
+
+	// maxFeeUpdateTimeout represents the maximum interval in which a
+	// WebAPIEstimator will request fresh fees from its API.
+	maxFeeUpdateTimeout = 20 * time.Minute
+
 	// WebAPIConnectionTimeout specifies the timeout value for connecting
 	// to the api source.
 	WebAPIConnectionTimeout = 5 * time.Second
@@ -731,43 +739,19 @@ type WebAPIEstimator struct {
 	// estimates.
 	noCache bool
 
-	// minFeeUpdateTimeout represents the minimum interval in which the
-	// web estimator will request fresh fees from its API.
-	minFeeUpdateTimeout time.Duration
-
-	// minFeeUpdateTimeout represents the maximum interval in which the
-	// web estimator will request fresh fees from its API.
-	maxFeeUpdateTimeout time.Duration
-
 	quit chan struct{}
 	wg   sync.WaitGroup
 }
 
 // NewWebAPIEstimator creates a new WebAPIEstimator from a given URL and a
 // fallback default fee. The fees are updated whenever a new block is mined.
-func NewWebAPIEstimator(api WebAPIFeeSource, noCache bool,
-	minFeeUpdateTimeout time.Duration,
-	maxFeeUpdateTimeout time.Duration) (*WebAPIEstimator, error) {
-
-	if minFeeUpdateTimeout == 0 || maxFeeUpdateTimeout == 0 {
-		return nil, fmt.Errorf("minFeeUpdateTimeout and " +
-			"maxFeeUpdateTimeout must be greater than 0")
-	}
-
-	if minFeeUpdateTimeout >= maxFeeUpdateTimeout {
-		return nil, fmt.Errorf("minFeeUpdateTimeout target of %v "+
-			"cannot be greater than maxFeeUpdateTimeout of %v",
-			minFeeUpdateTimeout, maxFeeUpdateTimeout)
-	}
-
+func NewWebAPIEstimator(api WebAPIFeeSource, noCache bool) *WebAPIEstimator {
 	return &WebAPIEstimator{
-		apiSource:           api,
-		feeByBlockTarget:    make(map[uint32]uint32),
-		noCache:             noCache,
-		quit:                make(chan struct{}),
-		minFeeUpdateTimeout: minFeeUpdateTimeout,
-		maxFeeUpdateTimeout: maxFeeUpdateTimeout,
-	}, nil
+		apiSource:        api,
+		feeByBlockTarget: make(map[uint32]uint32),
+		noCache:          noCache,
+		quit:             make(chan struct{}),
+	}
 }
 
 // EstimateFeePerKW takes in a target for the number of blocks until an initial
@@ -825,12 +809,7 @@ func (w *WebAPIEstimator) Start() error {
 	w.started.Do(func() {
 		log.Infof("Starting web API fee estimator")
 
-		feeUpdateTimeout := w.randomFeeUpdateTimeout()
-
-		log.Infof("Web API fee estimator using update timeout of %v",
-			feeUpdateTimeout)
-
-		w.updateFeeTicker = time.NewTicker(feeUpdateTimeout)
+		w.updateFeeTicker = time.NewTicker(w.randomFeeUpdateTimeout())
 		w.updateFeeEstimates()
 
 		w.wg.Add(1)
@@ -873,11 +852,9 @@ func (w *WebAPIEstimator) RelayFeePerKW() SatPerKWeight {
 // and maxFeeUpdateTimeout that will be used to determine how often the Estimator
 // should retrieve fresh fees from its API.
 func (w *WebAPIEstimator) randomFeeUpdateTimeout() time.Duration {
-	lower := int64(w.minFeeUpdateTimeout)
-	upper := int64(w.maxFeeUpdateTimeout)
-	return time.Duration(
-		prand.Int63n(upper-lower) + lower, //nolint:gosec
-	).Round(time.Second)
+	lower := int64(minFeeUpdateTimeout)
+	upper := int64(maxFeeUpdateTimeout)
+	return time.Duration(prand.Int63n(upper-lower) + lower)
 }
 
 // getCachedFee takes a conf target and returns the cached fee rate. When the
diff --git a/peer/brontide.go b/peer/brontide.go
index 1fa6c1311..984a52809 100644
--- a/peer/brontide.go
+++ b/peer/brontide.go
@@ -1146,8 +1146,8 @@ func (p *Brontide) addLink(chanPoint *wire.OutPoint,
 		),
 		BatchSize:               p.cfg.ChannelCommitBatchSize,
 		UnsafeReplay:            p.cfg.UnsafeReplay,
-		MinUpdateTimeout:        htlcswitch.DefaultMinLinkFeeUpdateTimeout,
-		MaxUpdateTimeout:        htlcswitch.DefaultMaxLinkFeeUpdateTimeout,
+		MinFeeUpdateTimeout:     htlcswitch.DefaultMinLinkFeeUpdateTimeout,
+		MaxFeeUpdateTimeout:     htlcswitch.DefaultMaxLinkFeeUpdateTimeout,
 		OutgoingCltvRejectDelta: p.cfg.OutgoingCltvRejectDelta,
 		TowerClient:             p.cfg.TowerClient,
 		MaxOutgoingCltvExpiry:   p.cfg.MaxOutgoingCltvExpiry,
diff --git a/sample-lnd.conf b/sample-lnd.conf
index 2783c58fd..d6bc83d4b 100644
--- a/sample-lnd.conf
+++ b/sample-lnd.conf
@@ -305,9 +305,9 @@
 ; The default value below is 20 MB (1024 * 1024 * 20)
 ; blockcachesize=20971520
 
-; DEPRECATED: Use 'fee.url' option. Optional URL for external fee estimation.
-; If no URL is specified, the method for fee estimation will depend on the
-; chosen backend and network. Must be set for neutrino on mainnet.
+; Optional URL for external fee estimation. If no URL is specified, the method
+; for fee estimation will depend on the chosen backend and network. Must be set
+; for neutrino on mainnet.
 ; Default:
 ;   feeurl=
 ; Example:
@@ -530,24 +530,6 @@
 ; the headers of an HTTP request.
 ; http-header-timeout=5s
 
-
-[fee]
-
-; Optional URL for external fee estimation. If no URL is specified, the method
-; for fee estimation will depend on the chosen backend and network. Must be set
-; for neutrino on mainnet.
-; Default:
-;   fee.url=
-; Example:
-;   fee.url=https://nodes.lightning.computer/fees/v1/btc-fee-estimates.json
-
-; The minimum interval in which fees will be updated from the specified fee URL.
-; fee.min-update-timeout=5m
-
-; The maximum interval in which fees will be updated from the specified fee URL.
-; fee.max-update-timeout=20m
-
-
 [prometheus]
 
 ; If true, lnd will start the Prometheus exporter. Prometheus flags are
@@ -820,6 +802,10 @@
 ; NOTE: This value is currently unused.
 ; neutrino.banthreshold=
 
+; DEPRECATED: Use top level 'feeurl' option. Optional URL for fee estimation. If
+; a URL is not specified, static fees will be used for estimation.
+; neutrino.feeurl=
+
 ; Optional filter header in height:hash format to assert the state of neutrino's
 ; filter header chain on startup. If the assertion does not hold, then the
 ; filter header chain will be re-synced from the genesis block.
diff --git a/sweep/fee_bumper.go b/sweep/fee_bumper.go
index a0e558cbb..61e63cc71 100644
--- a/sweep/fee_bumper.go
+++ b/sweep/fee_bumper.go
@@ -267,7 +267,7 @@ type TxPublisher struct {
 	cfg *TxPublisherConfig
 
 	// currentHeight is the current block height.
-	currentHeight atomic.Int32
+	currentHeight int32
 
 	// records is a map keyed by the requestCounter and the value is the tx
 	// being monitored.
@@ -373,9 +373,7 @@ func (t *TxPublisher) initializeFeeFunction(
 	}
 
 	// Get the initial conf target.
-	confTarget := calcCurrentConfTarget(
-		t.currentHeight.Load(), req.DeadlineHeight,
-	)
+	confTarget := calcCurrentConfTarget(t.currentHeight, req.DeadlineHeight)
 
 	log.Debugf("Initializing fee function with conf target=%v, budget=%v, "+
 		"maxFeeRateAllowed=%v", confTarget, req.Budget,
@@ -544,7 +542,7 @@ func (t *TxPublisher) broadcast(requestID uint64) (*BumpResult, error) {
 
 	tx := record.tx
 	log.Debugf("Publishing sweep tx %v, num_inputs=%v, height=%v",
-		txid, len(tx.TxIn), t.currentHeight.Load())
+		txid, len(tx.TxIn), t.currentHeight)
 
 	// Set the event, and change it to TxFailed if the wallet fails to
 	// publish it.
@@ -717,7 +715,7 @@ func (t *TxPublisher) monitor(blockEvent *chainntnfs.BlockEpochEvent) {
 				epoch.Height)
 
 			// Update the best known height for the publisher.
-			t.currentHeight.Store(epoch.Height)
+			t.currentHeight = epoch.Height
 
 			// Check all monitored txns to see if any of them needs
 			// to be bumped.
@@ -790,7 +788,7 @@ func (t *TxPublisher) processRecords() {
 	}
 
 	// Get the current height to be used in the following goroutines.
-	currentHeight := t.currentHeight.Load()
+	currentHeight := t.currentHeight
 
 	// For records that are not confirmed, we perform a fee bump if needed.
 	for requestID, r := range feeBumpRecords {
@@ -856,7 +854,7 @@ func (t *TxPublisher) handleFeeBumpTx(requestID uint64, r *monitorRecord,
 		// TODO(yy): send this error back to the sweeper so it can
 		// re-group the inputs?
 		log.Errorf("Failed to increase fee rate for tx %v at "+
-			"height=%v: %v", oldTxid, t.currentHeight.Load(), err)
+			"height=%v: %v", oldTxid, t.currentHeight, err)
 
 		return
 	}
@@ -864,7 +862,7 @@ func (t *TxPublisher) handleFeeBumpTx(requestID uint64, r *monitorRecord,
 	// If the fee rate was not increased, there's no need to bump the fee.
 	if !increased {
 		log.Tracef("Skip bumping tx %v at height=%v", oldTxid,
-			t.currentHeight.Load())
+			t.currentHeight)
 
 		return
 	}
@@ -1124,7 +1122,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte,
 
 	// Validate and calculate the fee and change amount.
 	txFee, changeAmtOpt, locktimeOpt, err := prepareSweepTx(
-		inputs, changePkScript, feeRate, t.currentHeight.Load(),
+		inputs, changePkScript, feeRate, t.currentHeight,
 	)
 	if err != nil {
 		return nil, 0, err
@@ -1180,7 +1178,7 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input, changePkScript []byte,
 
 	// We'll default to using the current block height as locktime, if none
 	// of the inputs commits to a different locktime.
-	sweepTx.LockTime = uint32(locktimeOpt.UnwrapOr(t.currentHeight.Load()))
+	sweepTx.LockTime = uint32(locktimeOpt.UnwrapOr(t.currentHeight))
 
 	prevInputFetcher, err := input.MultiPrevOutFetcher(inputs)
 	if err != nil {


Test patch:
diff --git a/fn/map_test.go b/fn/map_test.go
deleted file mode 100644
index b0694bde6..000000000
--- a/fn/map_test.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package fn
-
-import (
-	"testing"
-
-	"github.com/stretchr/testify/require"
-)
-
-func TestKeySet(t *testing.T) {
-	testMap := map[string]int{"a": 1, "b": 2, "c": 3}
-	expected := NewSet([]string{"a", "b", "c"}...)
-
-	require.Equal(t, expected, KeySet(testMap))
-}
-
-// TestNewSubMap tests the NewSubMap function with various input cases.
-func TestNewSubMap(t *testing.T) {
-	tests := []struct {
-		name     string
-		original map[int]string
-		keys     []int
-		expected map[int]string
-		wantErr  bool
-	}{
-		{
-			name: "Successful submap creation",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-				3: "cherry",
-			},
-			keys: []int{1, 3},
-			expected: map[int]string{
-				1: "apple",
-				3: "cherry",
-			},
-			wantErr: false,
-		},
-		{
-			name: "Key not found",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-			},
-			keys:     []int{1, 4},
-			expected: nil,
-			wantErr:  true,
-		},
-		{
-			name: "Empty keys list",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-			},
-			keys:     []int{},
-			expected: map[int]string{},
-			wantErr:  false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result, err := NewSubMap(tt.original, tt.keys)
-			if tt.wantErr {
-				require.ErrorContains(
-					t, err, "NewSubMap: missing key",
-				)
-
-				require.Nil(t, result)
-
-				return
-			}
-
-			require.NoError(t, err)
-			require.Equal(t, tt.expected, result)
-		})
-	}
-}
-
-// TestNewSubMapIntersect tests the NewSubMapIntersect function for correctness.
-func TestNewSubMapIntersect(t *testing.T) {
-	tests := []struct {
-		name     string
-		original map[int]string
-		keys     []int
-		expected map[int]string
-	}{
-		{
-			name: "Successful intersection",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-				3: "cherry",
-				4: "date",
-			},
-			keys: []int{2, 3, 5},
-			expected: map[int]string{
-				2: "banana",
-				3: "cherry",
-			},
-		},
-		{
-			name: "No intersection",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-			},
-			keys:     []int{3, 4},
-			expected: map[int]string{},
-		},
-		{
-			name:     "Empty original map",
-			original: map[int]string{},
-			keys:     []int{1, 2},
-			expected: map[int]string{},
-		},
-		{
-			name: "Empty keys list",
-			original: map[int]string{
-				1: "apple",
-				2: "banana",
-			},
-			keys:     []int{},
-			expected: map[int]string{},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			require.Equal(
-				t, tt.expected,
-				NewSubMapIntersect(tt.original, tt.keys))
-		})
-	}
-}
diff --git a/fn/slice_test.go b/fn/slice_test.go
index de02be836..0178cc946 100644
--- a/fn/slice_test.go
+++ b/fn/slice_test.go
@@ -1,7 +1,6 @@
 package fn
 
 import (
-	"fmt"
 	"slices"
 	"testing"
 
@@ -137,107 +136,3 @@ func TestZipWith(t *testing.T) {
 		z, []bool{false, true, false, false, false},
 	))
 }
-
-// TestSum checks if the Sum function correctly calculates the sum of the
-// numbers in the slice.
-func TestSum(t *testing.T) {
-	tests := []struct {
-		name   string
-		items  interface{}
-		result interface{}
-	}{
-		{
-			name:   "Sum of positive integers",
-			items:  []int{1, 2, 3},
-			result: 6,
-		},
-		{
-			name:   "Sum of negative integers",
-			items:  []int{-1, -2, -3},
-			result: -6,
-		},
-		{
-			name:   "Sum of float numbers",
-			items:  []float64{1.1, 2.2, 3.3},
-			result: 6.6,
-		},
-		{
-			name: "Sum of complex numbers",
-			items: []complex128{
-				complex(1, 1),
-				complex(2, 2),
-				complex(3, 3),
-			},
-			result: complex(6, 6),
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			switch v := tt.items.(type) {
-			case []int:
-				require.Equal(t, tt.result, Sum(v))
-			case []float64:
-				require.Equal(t, tt.result, Sum(v))
-			case []complex128:
-				require.Equal(t, tt.result, Sum(v))
-			}
-		})
-	}
-}
-
-// TestSliceToMap tests the SliceToMap function.
-func TestSliceToMap(t *testing.T) {
-	tests := []struct {
-		name      string
-		slice     []int
-		keyFunc   func(int) int
-		valueFunc func(int) string
-		expected  map[int]string
-	}{
-		{
-			name:    "Integers to string map",
-			slice:   []int{1, 2, 3},
-			keyFunc: func(a int) int { return a },
-			valueFunc: func(a int) string {
-				return fmt.Sprintf("Value%d", a)
-			},
-			expected: map[int]string{
-				1: "Value1",
-				2: "Value2",
-				3: "Value3",
-			},
-		},
-		{
-			name:    "Duplicates in slice",
-			slice:   []int{1, 2, 2, 3},
-			keyFunc: func(a int) int { return a },
-			valueFunc: func(a int) string {
-				return fmt.Sprintf("Value%d", a)
-			},
-			expected: map[int]string{
-				1: "Value1",
-				2: "Value2",
-				3: "Value3",
-			},
-		},
-		{
-			name:    "Empty slice",
-			slice:   []int{},
-			keyFunc: func(a int) int { return a },
-			valueFunc: func(a int) string {
-				return fmt.Sprintf("Value%d", a)
-			},
-			expected: map[int]string{},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			require.Equal(
-				t, tt.expected,
-				SliceToMap(tt.slice, tt.keyFunc, tt.valueFunc),
-			)
-		})
-	}
-}
diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go
index 6963bf708..4db22d173 100644
--- a/htlcswitch/link_test.go
+++ b/htlcswitch/link_test.go
@@ -2220,11 +2220,11 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 		BatchTicker:          bticker,
 		FwdPkgGCTicker:       ticker.NewForce(15 * time.Second),
 		PendingCommitTicker:  ticker.New(time.Minute),
-		// Make the BatchSize and Min/MaxUpdateTimeout large enough
+		// Make the BatchSize and Min/MaxFeeUpdateTimeout large enough
 		// to not trigger commit updates automatically during tests.
 		BatchSize:               10000,
-		MinUpdateTimeout:        30 * time.Minute,
-		MaxUpdateTimeout:        40 * time.Minute,
+		MinFeeUpdateTimeout:     30 * time.Minute,
+		MaxFeeUpdateTimeout:     40 * time.Minute,
 		MaxOutgoingCltvExpiry:   DefaultMaxOutgoingCltvExpiry,
 		MaxFeeAllocation:        DefaultMaxLinkFeeAllocation,
 		NotifyActiveLink:        func(wire.OutPoint) {},
@@ -4881,11 +4881,11 @@ func (h *persistentLinkHarness) restartLink(
 		BatchTicker:          bticker,
 		FwdPkgGCTicker:       ticker.New(5 * time.Second),
 		PendingCommitTicker:  ticker.New(time.Minute),
-		// Make the BatchSize and Min/MaxUpdateTimeout large enough
+		// Make the BatchSize and Min/MaxFeeUpdateTimeout large enough
 		// to not trigger commit updates automatically during tests.
-		BatchSize:        10000,
-		MinUpdateTimeout: 30 * time.Minute,
-		MaxUpdateTimeout: 40 * time.Minute,
+		BatchSize:           10000,
+		MinFeeUpdateTimeout: 30 * time.Minute,
+		MaxFeeUpdateTimeout: 40 * time.Minute,
 		// Set any hodl flags requested for the new link.
 		HodlMask:                hodl.MaskFromFlags(hodlFlags...),
 		MaxOutgoingCltvExpiry:   DefaultMaxOutgoingCltvExpiry,
diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go
index 9a72197ec..0c98fa4fe 100644
--- a/htlcswitch/test_utils.go
+++ b/htlcswitch/test_utils.go
@@ -1155,8 +1155,8 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
 			BatchTicker:             ticker.NewForce(testBatchTimeout),
 			FwdPkgGCTicker:          ticker.NewForce(fwdPkgTimeout),
 			PendingCommitTicker:     ticker.New(2 * time.Minute),
-			MinUpdateTimeout:        minFeeUpdateTimeout,
-			MaxUpdateTimeout:        maxFeeUpdateTimeout,
+			MinFeeUpdateTimeout:     minFeeUpdateTimeout,
+			MaxFeeUpdateTimeout:     maxFeeUpdateTimeout,
 			OnChannelFailure:        func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {},
 			OutgoingCltvRejectDelta: 3,
 			MaxOutgoingCltvExpiry:   DefaultMaxOutgoingCltvExpiry,
diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go
index 4a76d58aa..b0c019522 100644
--- a/invoices/invoiceregistry_test.go
+++ b/invoices/invoiceregistry_test.go
@@ -6,7 +6,6 @@ import (
 	"database/sql"
 	"fmt"
 	"math"
-	"sync"
 	"testing"
 	"testing/quick"
 	"time"
@@ -24,12 +23,6 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-// sqliteConstructorMu is used to ensure that only one thread can call the
-// sqldb.NewTestSqliteDB constructor at a time. This is a temporary workaround
-// that can be removed once this race condition in the sqlite repo is resolved:
-// https://gitlab.com/cznic/sqlite/-/issues/180
-var sqliteConstructorMu sync.Mutex
-
 // TestInvoiceRegistry is a master test which encompasses all tests using an
 // InvoiceDB instance. The purpose of this test is to be able to run all tests
 // with a custom DB instance, so that we can test the same logic with different
@@ -137,9 +130,7 @@ func TestInvoiceRegistry(t *testing.T) {
 
 		var db *sqldb.BaseDB
 		if sqlite {
-			sqliteConstructorMu.Lock()
 			db = sqldb.NewTestSqliteDB(t).BaseDB
-			sqliteConstructorMu.Unlock()
 		} else {
 			db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
 		}
diff --git a/invoices/invoices_test.go b/invoices/invoices_test.go
index 71da38571..6f3cbd8f2 100644
--- a/invoices/invoices_test.go
+++ b/invoices/invoices_test.go
@@ -234,9 +234,7 @@ func TestInvoices(t *testing.T) {
 	makeSQLDB := func(t *testing.T, sqlite bool) invpkg.InvoiceDB {
 		var db *sqldb.BaseDB
 		if sqlite {
-			sqliteConstructorMu.Lock()
 			db = sqldb.NewTestSqliteDB(t).BaseDB
-			sqliteConstructorMu.Unlock()
 		} else {
 			db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
 		}
@@ -2683,11 +2681,8 @@ func testDeleteCanceledInvoices(t *testing.T,
 		}, nil
 	}
 
-	// Test deletion of canceled invoices when there are none.
-	ctxb := context.Background()
-	require.NoError(t, db.DeleteCanceledInvoices(ctxb))
-
 	// Add some invoices to the test db.
+	ctxb := context.Background()
 	var invoices []invpkg.Invoice
 	for i := 0; i < 10; i++ {
 		invoice, err := randInvoice(lnwire.MilliSatoshi(i + 1))
diff --git a/itest/lnd_route_blinding_test.go b/itest/lnd_route_blinding_test.go
index 4b0eef859..1cae46a0c 100644
--- a/itest/lnd_route_blinding_test.go
+++ b/itest/lnd_route_blinding_test.go
@@ -1018,14 +1018,10 @@ func testErrorHandlingOnChainFailure(ht *lntest.HarnessTest) {
 	// suspended Carol we don't need to account for her commitment output
 	// claim.
 	ht.Miner.MineBlocksAndAssertNumTxes(1, 1)
-	ht.AssertNumPendingSweeps(ht.Bob, 0)
 
 	// Assert that the HTLC has cleared.
-	ht.WaitForBlockchainSync(ht.Bob)
-	ht.WaitForBlockchainSync(ht.Alice)
-
-	ht.AssertHTLCNotActive(ht.Bob, testCase.channels[0], hash[:])
 	ht.AssertHTLCNotActive(ht.Alice, testCase.channels[0], hash[:])
+	ht.AssertHTLCNotActive(ht.Bob, testCase.channels[0], hash[:])
 
 	// Wait for the HTLC to reflect as failed for Alice.
 	paymentStream := ht.Alice.RPC.TrackPaymentV2(hash[:])
diff --git a/lntest/fee_service.go b/lntest/fee_service.go
index cee9ae0ab..d96bd7588 100644
--- a/lntest/fee_service.go
+++ b/lntest/fee_service.go
@@ -17,7 +17,7 @@ import (
 
 // WebFeeService defines an interface that's used to provide fee estimation
 // service used in the integration tests. It must provide an URL so that a lnd
-// node can be started with the flag `--fee.url` and uses the customized fee
+// node can be started with the flag `--feeurl` and uses the customized fee
 // estimator.
 type WebFeeService interface {
 	// Start starts the service.
diff --git a/lntest/node/config.go b/lntest/node/config.go
index de1ea92ec..d0de2fdd4 100644
--- a/lntest/node/config.go
+++ b/lntest/node/config.go
@@ -279,7 +279,7 @@ func (cfg *BaseNodeConfig) GenArgs() []string {
 	}
 
 	if cfg.FeeURL != "" {
-		args = append(args, "--fee.url="+cfg.FeeURL)
+		args = append(args, "--feeurl="+cfg.FeeURL)
 	}
 
 	// Put extra args in the end so the args can be overwritten.
diff --git a/lnwallet/chainfee/estimator_test.go b/lnwallet/chainfee/estimator_test.go
index 51ab5bf29..fc16c9b12 100644
--- a/lnwallet/chainfee/estimator_test.go
+++ b/lnwallet/chainfee/estimator_test.go
@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"encoding/json"
 	"testing"
-	"time"
 
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/stretchr/testify/require"
@@ -143,9 +142,6 @@ func TestWebAPIFeeEstimator(t *testing.T) {
 		// Fee rates are in sat/kb.
 		minFeeRate uint32 = 2000 // 500 sat/kw
 		maxFeeRate uint32 = 4000 // 1000 sat/kw
-
-		minFeeUpdateTimeout = 5 * time.Minute
-		maxFeeUpdateTimeout = 20 * time.Minute
 	)
 
 	testCases := []struct {
@@ -203,9 +199,7 @@ func TestWebAPIFeeEstimator(t *testing.T) {
 	feeSource := &mockFeeSource{}
 	feeSource.On("GetFeeMap").Return(feeRateResp, nil)
 
-	estimator, _ := NewWebAPIEstimator(
-		feeSource, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
-	)
+	estimator := NewWebAPIEstimator(feeSource, false)
 
 	// Test that requesting a fee when no fees have been cached won't fail.
 	feeRate, err := estimator.EstimateFeePerKW(5)
@@ -253,15 +247,10 @@ func TestGetCachedFee(t *testing.T) {
 
 		minFeeRate uint32 = 100
 		maxFeeRate uint32 = 1000
-
-		minFeeUpdateTimeout = 5 * time.Minute
-		maxFeeUpdateTimeout = 20 * time.Minute
 	)
 
 	// Create a dummy estimator without WebAPIFeeSource.
-	estimator, _ := NewWebAPIEstimator(
-		nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
-	)
+	estimator := NewWebAPIEstimator(nil, false)
 
 	// When the cache is empty, an error should be returned.
 	cachedFee, err := estimator.getCachedFee(minTarget)
@@ -326,38 +315,3 @@ func TestGetCachedFee(t *testing.T) {
 		})
 	}
 }
-
-func TestRandomFeeUpdateTimeout(t *testing.T) {
-	t.Parallel()
-
-	var (
-		minFeeUpdateTimeout = 1 * time.Minute
-		maxFeeUpdateTimeout = 2 * time.Minute
-	)
-
-	estimator, _ := NewWebAPIEstimator(
-		nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
-	)
-
-	for i := 0; i < 1000; i++ {
-		timeout := estimator.randomFeeUpdateTimeout()
-
-		require.GreaterOrEqual(t, timeout, minFeeUpdateTimeout)
-		require.LessOrEqual(t, timeout, maxFeeUpdateTimeout)
-	}
-}
-
-func TestInvalidFeeUpdateTimeout(t *testing.T) {
-	t.Parallel()
-
-	var (
-		minFeeUpdateTimeout = 2 * time.Minute
-		maxFeeUpdateTimeout = 1 * time.Minute
-	)
-
-	_, err := NewWebAPIEstimator(
-		nil, false, minFeeUpdateTimeout, maxFeeUpdateTimeout,
-	)
-	require.Error(t, err, "NewWebAPIEstimator should return an error "+
-		"when minFeeUpdateTimeout > maxFeeUpdateTimeout")
-}
diff --git a/make/testing_flags.mk b/make/testing_flags.mk
index b2db6861c..3fecb073a 100644
--- a/make/testing_flags.mk
+++ b/make/testing_flags.mk
@@ -2,9 +2,7 @@ DEV_TAGS = dev
 RPC_TAGS = autopilotrpc chainrpc invoicesrpc neutrinorpc peersrpc routerrpc signrpc verrpc walletrpc watchtowerrpc wtclientrpc
 LOG_TAGS =
 TEST_FLAGS =
-ITEST_FLAGS =
-ITEST_COVERAGE =
-COLLECT_ITEST_COVERAGE =
+ITEST_FLAGS = 
 EXEC_SUFFIX =
 COVER_PKG = $$(go list -deps -tags="$(DEV_TAGS)" ./... | grep '$(PKG)' | grep -v lnrpc)
 NUM_ITEST_TRANCHES = 4
@@ -79,12 +77,6 @@ ifneq ($(tags),)
 DEV_TAGS += ${tags}
 endif
 
-# Enable integration test coverage (requires Go >= 1.20.0).
-ifneq ($(cover),)
-ITEST_COVERAGE = -cover
-COLLECT_ITEST_COVERAGE = go tool covdata textfmt -i=itest/cover -o coverage.txt
-endif
-
 # Define the log tags that will be applied only when running unit tests. If none
 # are provided, we default to "nolog" which will be silent.
 ifneq ($(log),)
diff --git a/peer/brontide_test.go b/peer/brontide_test.go
index 8ea8846b2..d2f3fc7bc 100644
--- a/peer/brontide_test.go
+++ b/peer/brontide_test.go
@@ -6,22 +6,19 @@ import (
 	"testing"
 	"time"
 
-	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/btcsuite/btcd/btcutil"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
-	"github.com/lightningnetwork/lnd/channelnotifier"
 	"github.com/lightningnetwork/lnd/contractcourt"
+	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/htlcswitch"
-	"github.com/lightningnetwork/lnd/lntest/mock"
 	"github.com/lightningnetwork/lnd/lntest/wait"
 	"github.com/lightningnetwork/lnd/lnwallet"
 	"github.com/lightningnetwork/lnd/lnwallet/chancloser"
 	"github.com/lightningnetwork/lnd/lnwire"
-	"github.com/lightningnetwork/lnd/pool"
 	"github.com/stretchr/testify/require"
 )
 
@@ -39,19 +36,13 @@ var (
 func TestPeerChannelClosureShutdownResponseLinkRemoved(t *testing.T) {
 	t.Parallel()
 
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-
-	mockSwitch := &mockMessageSwitch{}
+	harness, err := createTestPeerWithChannel(t, noUpdate)
+	require.NoError(t, err, "unable to create test channels")
 
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
+	var (
+		alicePeer = harness.peer
+		bobChan   = harness.channel
 	)
-	require.NoError(t, err, "unable to create test channels")
 
 	chanPoint := bobChan.ChannelPoint()
 	chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
@@ -87,19 +78,16 @@ func TestPeerChannelClosureShutdownResponseLinkRemoved(t *testing.T) {
 func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
 	t.Parallel()
 
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-
-	mockSwitch := &mockMessageSwitch{}
+	harness, err := createTestPeerWithChannel(t, noUpdate)
+	require.NoError(t, err, "unable to create test channels")
 
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
+	var (
+		alicePeer       = harness.peer
+		bobChan         = harness.channel
+		mockSwitch      = harness.mockSwitch
+		broadcastTxChan = harness.publishTx
+		notifier        = harness.notifier
 	)
-	require.NoError(t, err, "unable to create test channels")
 
 	chanPoint := bobChan.ChannelPoint()
 	chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
@@ -192,19 +180,16 @@ func TestPeerChannelClosureAcceptFeeResponder(t *testing.T) {
 func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
 	t.Parallel()
 
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-
-	mockSwitch := &mockMessageSwitch{}
+	harness, err := createTestPeerWithChannel(t, noUpdate)
+	require.NoError(t, err, "unable to create test channels")
 
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
+	var (
+		bobChan         = harness.channel
+		alicePeer       = harness.peer
+		mockSwitch      = harness.mockSwitch
+		broadcastTxChan = harness.publishTx
+		notifier        = harness.notifier
 	)
-	require.NoError(t, err, "unable to create test channels")
 
 	chanPoint := bobChan.ChannelPoint()
 	chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
@@ -316,19 +301,16 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
 func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
 	t.Parallel()
 
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-
-	mockSwitch := &mockMessageSwitch{}
+	harness, err := createTestPeerWithChannel(t, noUpdate)
+	require.NoError(t, err, "unable to create test channels")
 
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
+	var (
+		bobChan         = harness.channel
+		alicePeer       = harness.peer
+		mockSwitch      = harness.mockSwitch
+		broadcastTxChan = harness.publishTx
+		notifier        = harness.notifier
 	)
-	require.NoError(t, err, "unable to create test channels")
 
 	chanPoint := bobChan.ChannelPoint()
 	chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
@@ -503,19 +485,16 @@ func TestPeerChannelClosureFeeNegotiationsResponder(t *testing.T) {
 func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
 	t.Parallel()
 
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-
-	mockSwitch := &mockMessageSwitch{}
+	harness, err := createTestPeerWithChannel(t, noUpdate)
+	require.NoError(t, err, "unable to create test channels")
 
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
+	var (
+		alicePeer       = harness.peer
+		bobChan         = harness.channel
+		mockSwitch      = harness.mockSwitch
+		broadcastTxChan = harness.publishTx
+		notifier        = harness.notifier
 	)
-	require.NoError(t, err, "unable to create test channels")
 
 	chanPoint := bobChan.ChannelPoint()
 	chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
@@ -830,31 +809,27 @@ func TestCustomShutdownScript(t *testing.T) {
 		test := test
 
 		t.Run(test.name, func(t *testing.T) {
-			notifier := &mock.ChainNotifier{
-				SpendChan: make(chan *chainntnfs.SpendDetail),
-				EpochChan: make(chan *chainntnfs.BlockEpoch),
-				ConfChan:  make(chan *chainntnfs.TxConfirmation),
-			}
-			broadcastTxChan := make(chan *wire.MsgTx)
-
-			mockSwitch := &mockMessageSwitch{}
-
 			// Open a channel.
-			alicePeer, bobChan, err := createTestPeer(
-				t, notifier, broadcastTxChan, test.update,
-				mockSwitch,
+			harness, err := createTestPeerWithChannel(
+				t, test.update,
 			)
 			if err != nil {
 				t.Fatalf("unable to create test channels: %v", err)
 			}
 
+			var (
+				alicePeer  = harness.peer
+				bobChan    = harness.channel
+				mockSwitch = harness.mockSwitch
+			)
+
 			chanPoint := bobChan.ChannelPoint()
 			chanID := lnwire.NewChanIDFromOutPoint(chanPoint)
 			mockLink := newMockUpdateHandler(chanID)
 			mockSwitch.links = append(mockSwitch.links, mockLink)
 
-			// Request initiator to cooperatively close the channel, with
-			// a specified delivery address.
+			// Request initiator to cooperatively close the channel,
+			// with a specified delivery address.
 			updateChan := make(chan interface{}, 1)
 			errChan := make(chan error, 1)
 			closeCommand := htlcswitch.ChanClose{
@@ -1000,35 +975,23 @@ func TestStaticRemoteDowngrade(t *testing.T) {
 		test := test
 
 		t.Run(test.name, func(t *testing.T) {
-			writeBufferPool := pool.NewWriteBuffer(
-				pool.DefaultWriteBufferGCInterval,
-				pool.DefaultWriteBufferExpiryInterval,
-			)
+			params := createTestPeer(t)
 
-			writePool := pool.NewWrite(
-				writeBufferPool, 1, timeout,
+			var (
+				p         = params.peer
+				mockConn  = params.mockConn
+				writePool = p.cfg.WritePool
 			)
-			require.NoError(t, writePool.Start())
-
-			mockConn := newMockConn(t, 1)
-
-			p := Brontide{
-				cfg: Config{
-					LegacyFeatures: legacy,
-					Features:       test.features,
-					Conn:           mockConn,
-					WritePool:      writePool,
-					PongBuf:        make([]byte, lnwire.MaxPongBytes),
-				},
-				log: peerLog,
-			}
+			// Set feature bits.
+			p.cfg.LegacyFeatures = legacy
+			p.cfg.Features = test.features
 
 			var b bytes.Buffer
 			_, err := lnwire.WriteMessage(&b, test.expectedInit, 0)
 			require.NoError(t, err)
 
-			// Send our init message, assert that we write our expected message
-			// and shutdown our write pool.
+			// Send our init message, assert that we write our
+			// expected message and shutdown our write pool.
 			require.NoError(t, p.sendInitMsg(test.legacy))
 			mockConn.assertWrite(b.Bytes())
 			require.NoError(t, writePool.Stop())
@@ -1056,100 +1019,19 @@ func genScript(t *testing.T, address string) lnwire.DeliveryAddress {
 func TestPeerCustomMessage(t *testing.T) {
 	t.Parallel()
 
-	// Set up node Alice.
-	dbAlice, err := channeldb.Open(t.TempDir())
-	require.NoError(t, err)
-
-	aliceKey, err := btcec.NewPrivateKey()
-	require.NoError(t, err)
-
-	writeBufferPool := pool.NewWriteBuffer(
-		pool.DefaultWriteBufferGCInterval,
-		pool.DefaultWriteBufferExpiryInterval,
-	)
+	params := createTestPeer(t)
 
-	writePool := pool.NewWrite(
-		writeBufferPool, 1, timeout,
-	)
-	require.NoError(t, writePool.Start())
-
-	readBufferPool := pool.NewReadBuffer(
-		pool.DefaultReadBufferGCInterval,
-		pool.DefaultReadBufferExpiryInterval,
-	)
-
-	readPool := pool.NewRead(
-		readBufferPool, 1, timeout,
+	var (
+		mockConn           = params.mockConn
+		alicePeer          = params.peer
+		receivedCustomChan = params.customChan
+		remoteKey          = alicePeer.PubKey()
 	)
-	require.NoError(t, readPool.Start())
 
-	mockConn := newMockConn(t, 1)
-
-	receivedCustomChan := make(chan *customMsg)
-
-	remoteKey := [33]byte{8}
-
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-
-	// TODO(yy): change ChannelNotifier to be an interface.
-	channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB())
-	require.NoError(t, channelNotifier.Start())
-	t.Cleanup(func() {
-		require.NoError(t, channelNotifier.Stop(),
-			"stop channel notifier failed")
-	})
-
-	alicePeer := NewBrontide(Config{
-		PubKeyBytes: remoteKey,
-		ChannelDB:   dbAlice.ChannelStateDB(),
-		Addr: &lnwire.NetAddress{
-			IdentityKey: aliceKey.PubKey(),
-		},
-		PrunePersistentPeerConnection: func([33]byte) {},
-		Features:                      lnwire.EmptyFeatureVector(),
-		LegacyFeatures:                lnwire.EmptyFeatureVector(),
-		WritePool:                     writePool,
-		ReadPool:                      readPool,
-		Conn:                          mockConn,
-		ChainNotifier:                 notifier,
-		HandleCustomMessage: func(
-			peer [33]byte, msg *lnwire.Custom) error {
-
-			receivedCustomChan <- &customMsg{
-				peer: peer,
-				msg:  *msg,
-			}
-			return nil
-		},
-		PongBuf:         make([]byte, lnwire.MaxPongBytes),
-		ChannelNotifier: channelNotifier,
-	})
-
-	// Set up the init sequence.
-	go func() {
-		// Read init message.
-		<-mockConn.writtenMessages
-
-		// Write the init reply message.
-		initReplyMsg := lnwire.NewInitMessage(
-			lnwire.NewRawFeatureVector(
-				lnwire.DataLossProtectRequired,
-			),
-			lnwire.NewRawFeatureVector(),
-		)
-		var b bytes.Buffer
-		_, err = lnwire.WriteMessage(&b, initReplyMsg, 0)
-		require.NoError(t, err)
-
-		mockConn.readMessages <- b.Bytes()
-	}()
-
-	// Start the peer.
-	require.NoError(t, alicePeer.Start())
+	// Start peer.
+	startPeerDone := startPeer(t, mockConn, alicePeer)
+	_, err := fn.RecvOrTimeout(startPeerDone, 2*timeout)
+	require.NoError(t, err)
 
 	// Send a custom message.
 	customMsg, err := lnwire.NewCustom(
@@ -1185,21 +1067,12 @@ func TestUpdateNextRevocation(t *testing.T) {
 
 	require := require.New(t)
 
-	// TODO(yy): create interface for lnwallet.LightningChannel so we can
-	// easily mock it without the following setups.
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-	mockSwitch := &mockMessageSwitch{}
-
-	alicePeer, bobChan, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
-	)
+	harness, err := createTestPeerWithChannel(t, noUpdate)
 	require.NoError(err, "unable to create test channels")
 
+	bobChan := harness.channel
+	alicePeer := harness.peer
+
 	// testChannel is used to test the updateNextRevocation function.
 	testChannel := bobChan.State()
 
@@ -1412,30 +1285,22 @@ func TestHandleRemovePendingChannel(t *testing.T) {
 func TestStartupWriteMessageRace(t *testing.T) {
 	t.Parallel()
 
-	// Set up parameters for createTestPeer.
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-	mockSwitch := &mockMessageSwitch{}
-
-	// Use a callback to extract the channel created by createTestPeer, so
-	// we can mark it borked below. We can't mark it borked within the
-	// callback, since the channel hasn't been saved to the DB yet when the
-	// callback executes.
+	// Use a callback to extract the channel created by
+	// createTestPeerWithChannel, so we can mark it borked below.
+	// We can't mark it borked within the callback, since the channel hasn't
+	// been saved to the DB yet when the callback executes.
 	var channel *channeldb.OpenChannel
 	getChannels := func(a, b *channeldb.OpenChannel) {
 		channel = a
 	}
 
-	// createTestPeer creates a peer and a channel with that peer.
-	peer, _, err := createTestPeer(
-		t, notifier, broadcastTxChan, getChannels, mockSwitch,
-	)
+	// createTestPeerWithChannel creates a peer and a channel with that
+	// peer.
+	harness, err := createTestPeerWithChannel(t, getChannels)
 	require.NoError(t, err, "unable to create test channel")
 
+	peer := harness.peer
+
 	// Avoid the need to mock the channel graph by marking the channel
 	// borked. Borked channels still get a reestablish message sent on
 	// reconnect, while skipping channel graph checks and link creation.
@@ -1445,58 +1310,21 @@ func TestStartupWriteMessageRace(t *testing.T) {
 	mockConn := newMockConn(t, 2)
 	peer.cfg.Conn = mockConn
 
-	// Set up other configuration necessary to successfully execute
-	// peer.Start().
-	peer.cfg.LegacyFeatures = lnwire.EmptyFeatureVector()
-	writeBufferPool := pool.NewWriteBuffer(
-		pool.DefaultWriteBufferGCInterval,
-		pool.DefaultWriteBufferExpiryInterval,
-	)
-	writePool := pool.NewWrite(
-		writeBufferPool, 1, timeout,
-	)
-	require.NoError(t, writePool.Start())
-	peer.cfg.WritePool = writePool
-	readBufferPool := pool.NewReadBuffer(
-		pool.DefaultReadBufferGCInterval,
-		pool.DefaultReadBufferExpiryInterval,
-	)
-	readPool := pool.NewRead(
-		readBufferPool, 1, timeout,
-	)
-	require.NoError(t, readPool.Start())
-	peer.cfg.ReadPool = readPool
-
 	// Send a message while starting the peer. As the peer starts up, it
 	// should not trigger a data race between the sending of this message
 	// and the sending of the channel reestablish message.
-	sendPingDone := make(chan struct{})
+	var sendPingDone = make(chan struct{})
 	go func() {
 		require.NoError(t, peer.SendMessage(true, lnwire.NewPing(0)))
 		close(sendPingDone)
 	}()
 
-	// Handle init messages.
-	go func() {
-		// Read init message.
-		<-mockConn.writtenMessages
-
-		// Write the init reply message.
-		initReplyMsg := lnwire.NewInitMessage(
-			lnwire.NewRawFeatureVector(
-				lnwire.DataLossProtectRequired,
-			),
-			lnwire.NewRawFeatureVector(),
-		)
-		var b bytes.Buffer
-		_, err = lnwire.WriteMessage(&b, initReplyMsg, 0)
-		require.NoError(t, err)
-
-		mockConn.readMessages <- b.Bytes()
-	}()
-
 	// Start the peer. No data race should occur.
-	require.NoError(t, peer.Start())
+	startPeerDone := startPeer(t, mockConn, peer)
+
+	// Ensure startup is complete.
+	_, err = fn.RecvOrTimeout(startPeerDone, 2*timeout)
+	require.NoError(t, err)
 
 	// Ensure messages were sent during startup.
 	<-sendPingDone
@@ -1516,21 +1344,12 @@ func TestStartupWriteMessageRace(t *testing.T) {
 func TestRemovePendingChannel(t *testing.T) {
 	t.Parallel()
 
-	// Set up parameters for createTestPeer.
-	notifier := &mock.ChainNotifier{
-		SpendChan: make(chan *chainntnfs.SpendDetail),
-		EpochChan: make(chan *chainntnfs.BlockEpoch),
-		ConfChan:  make(chan *chainntnfs.TxConfirmation),
-	}
-	broadcastTxChan := make(chan *wire.MsgTx)
-	mockSwitch := &mockMessageSwitch{}
-
-	// createTestPeer creates a peer and a channel with that peer.
-	peer, _, err := createTestPeer(
-		t, notifier, broadcastTxChan, noUpdate, mockSwitch,
-	)
+	// createTestPeerWithChannel creates a peer and a channel.
+	harness, err := createTestPeerWithChannel(t, noUpdate)
 	require.NoError(t, err, "unable to create test channel")
 
+	peer := harness.peer
+
 	// Add a pending channel to the peer Alice.
 	errChan := make(chan error, 1)
 	pendingChanID := lnwire.ChannelID{1}
diff --git a/peer/test_utils.go b/peer/test_utils.go
index 05bfe6ad4..8667b04cd 100644
--- a/peer/test_utils.go
+++ b/peer/test_utils.go
@@ -18,6 +18,7 @@ import (
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/channeldb"
 	"github.com/lightningnetwork/lnd/channelnotifier"
+	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/htlcswitch"
 	"github.com/lightningnetwork/lnd/input"
 	"github.com/lightningnetwork/lnd/keychain"
@@ -27,6 +28,7 @@ import (
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/netann"
+	"github.com/lightningnetwork/lnd/pool"
 	"github.com/lightningnetwork/lnd/queue"
 	"github.com/lightningnetwork/lnd/shachain"
 	"github.com/stretchr/testify/require"
@@ -48,32 +50,52 @@ var (
 	testKeyLoc = keychain.KeyLocator{Family: keychain.KeyFamilyNodeKey}
 )
 
-// noUpdate is a function which can be used as a parameter in createTestPeer to
-// call the setup code with no custom values on the channels set up.
+// noUpdate is a function which can be used as a parameter in
+// createTestPeerWithChannel to call the setup code with no custom values on
+// the channels set up.
 var noUpdate = func(a, b *channeldb.OpenChannel) {}
 
-// createTestPeer creates a channel between two nodes, and returns a peer for
-// one of the nodes, together with the channel seen from both nodes. It takes
-// an updateChan function which can be used to modify the default values on
-// the channel states for each peer.
-func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
-	publTx chan *wire.MsgTx, updateChan func(a, b *channeldb.OpenChannel),
-	mockSwitch *mockMessageSwitch) (
-	*Brontide, *lnwallet.LightningChannel, error) {
+type peerTestCtx struct {
+	peer          *Brontide
+	channel       *lnwallet.LightningChannel
+	notifier      *mock.ChainNotifier
+	publishTx     <-chan *wire.MsgTx
+	mockSwitch    *mockMessageSwitch
+	db            *channeldb.DB
+	privKey       *btcec.PrivateKey
+	mockConn      *mockMessageConn
+	customChan    chan *customMsg
+	chanStatusMgr *netann.ChanStatusManager
+}
 
-	nodeKeyLocator := keychain.KeyLocator{
-		Family: keychain.KeyFamilyNodeKey,
-	}
-	aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(
-		channels.AlicesPrivKey,
-	)
-	aliceKeySigner := keychain.NewPrivKeyMessageSigner(
-		aliceKeyPriv, nodeKeyLocator,
-	)
-	bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(
-		channels.BobsPrivKey,
+// createTestPeerWithChannel creates a channel between two nodes, and returns a
+// peer for one of the nodes, together with the channel seen from both nodes.
+// It takes an updateChan function which can be used to modify the default
+// values on the channel states for each peer.
+func createTestPeerWithChannel(t *testing.T, updateChan func(a,
+	b *channeldb.OpenChannel)) (*peerTestCtx, error) {
+
+	params := createTestPeer(t)
+
+	var (
+		publishTx     = params.publishTx
+		mockSwitch    = params.mockSwitch
+		alicePeer     = params.peer
+		notifier      = params.notifier
+		aliceKeyPriv  = params.privKey
+		dbAlice       = params.db
+		chanStatusMgr = params.chanStatusMgr
 	)
 
+	err := chanStatusMgr.Start()
+	require.NoError(t, err)
+	t.Cleanup(func() {
+		require.NoError(t, chanStatusMgr.Stop())
+	})
+
+	aliceKeyPub := alicePeer.IdentityKey()
+	estimator := alicePeer.cfg.FeeEstimator
+
 	channelCapacity := btcutil.Amount(10 * 1e8)
 	channelBal := channelCapacity / 2
 	aliceDustLimit := btcutil.Amount(200)
@@ -88,6 +110,10 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 	}
 	fundingTxIn := wire.NewTxIn(prevOut, nil, nil)
 
+	bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(
+		channels.BobsPrivKey,
+	)
+
 	aliceCfg := channeldb.ChannelConfig{
 		ChannelConstraints: channeldb.ChannelConstraints{
 			DustLimit:        aliceDustLimit,
@@ -141,23 +167,23 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 
 	bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
 	bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:])
 
 	aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
 	aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
 
@@ -167,29 +193,20 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 		isAliceInitiator, 0,
 	)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	dbAlice, err := channeldb.Open(t.TempDir())
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
-	t.Cleanup(func() {
-		require.NoError(t, dbAlice.Close())
-	})
 
 	dbBob, err := channeldb.Open(t.TempDir())
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	t.Cleanup(func() {
 		require.NoError(t, dbBob.Close())
 	})
 
-	estimator := chainfee.NewStaticEstimator(12500, 0)
 	feePerKw, err := estimator.EstimateFeePerKW(1)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	// TODO(roasbeef): need to factor in commit fee?
@@ -214,7 +231,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 
 	var chanIDBytes [8]byte
 	if _, err := io.ReadFull(crand.Reader, chanIDBytes[:]); err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	shortChanID := lnwire.NewShortChanIDFromInt(
@@ -259,13 +276,9 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 	// Set custom values on the channel states.
 	updateChan(aliceChannelState, bobChannelState)
 
-	aliceAddr := &net.TCPAddr{
-		IP:   net.ParseIP("127.0.0.1"),
-		Port: 18555,
-	}
-
+	aliceAddr := alicePeer.cfg.Addr.Address
 	if err := aliceChannelState.SyncPending(aliceAddr, 0); err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	bobAddr := &net.TCPAddr{
@@ -274,7 +287,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 	}
 
 	if err := bobChannelState.SyncPending(bobAddr, 0); err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
 	aliceSigner := input.NewMockSigner(
@@ -289,7 +302,7 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 		aliceSigner, aliceChannelState, alicePool,
 	)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	_ = alicePool.Start()
 	t.Cleanup(func() {
@@ -301,116 +314,16 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 		bobSigner, bobChannelState, bobPool,
 	)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	_ = bobPool.Start()
 	t.Cleanup(func() {
 		require.NoError(t, bobPool.Stop())
 	})
 
-	chainIO := &mock.ChainIO{
-		BestHeight: broadcastHeight,
-	}
-	wallet := &lnwallet.LightningWallet{
-		WalletController: &mock.WalletController{
-			RootKey:               aliceKeyPriv,
-			PublishedTransactions: publTx,
-		},
-	}
-
-	// If mockSwitch is not set by the caller, set it to the default as the
-	// caller does not need to control it.
-	if mockSwitch == nil {
-		mockSwitch = &mockMessageSwitch{}
-	}
-
-	nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner)
-
-	const chanActiveTimeout = time.Minute
-
-	chanStatusMgr, err := netann.NewChanStatusManager(&netann.ChanStatusConfig{
-		ChanStatusSampleInterval: 30 * time.Second,
-		ChanEnableTimeout:        chanActiveTimeout,
-		ChanDisableTimeout:       2 * time.Minute,
-		DB:                       dbAlice.ChannelStateDB(),
-		Graph:                    dbAlice.ChannelGraph(),
-		MessageSigner:            nodeSignerAlice,
-		OurPubKey:                aliceKeyPub,
-		OurKeyLoc:                testKeyLoc,
-		IsChannelActive:          func(lnwire.ChannelID) bool { return true },
-		ApplyChannelUpdate: func(*lnwire.ChannelUpdate,
-			*wire.OutPoint, bool) error {
-
-			return nil
-		},
-	})
-	if err != nil {
-		return nil, nil, err
-	}
-	if err = chanStatusMgr.Start(); err != nil {
-		return nil, nil, err
-	}
-
-	errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var pubKey [33]byte
-	copy(pubKey[:], aliceKeyPub.SerializeCompressed())
-
-	cfgAddr := &lnwire.NetAddress{
-		IdentityKey: aliceKeyPub,
-		Address:     aliceAddr,
-		ChainNet:    wire.SimNet,
-	}
-
-	interceptableSwitchNotifier := &mock.ChainNotifier{
-		EpochChan: make(chan *chainntnfs.BlockEpoch, 1),
-	}
-	interceptableSwitchNotifier.EpochChan <- &chainntnfs.BlockEpoch{
-		Height: 1,
-	}
-
-	interceptableSwitch, err := htlcswitch.NewInterceptableSwitch(
-		&htlcswitch.InterceptableSwitchConfig{
-			CltvRejectDelta:    testCltvRejectDelta,
-			CltvInterceptDelta: testCltvRejectDelta + 3,
-			Notifier:           interceptableSwitchNotifier,
-		},
+	alicePeer.remoteFeatures = lnwire.NewFeatureVector(
+		nil, lnwire.Features,
 	)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	// TODO(yy): change ChannelNotifier to be an interface.
-	channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB())
-	require.NoError(t, channelNotifier.Start())
-	t.Cleanup(func() {
-		require.NoError(t, channelNotifier.Stop(),
-			"stop channel notifier failed")
-	})
-
-	cfg := &Config{
-		Addr:              cfgAddr,
-		PubKeyBytes:       pubKey,
-		ErrorBuffer:       errBuffer,
-		ChainIO:           chainIO,
-		Switch:            mockSwitch,
-		ChanActiveTimeout: chanActiveTimeout,
-		InterceptSwitch:   interceptableSwitch,
-		ChannelDB:         dbAlice.ChannelStateDB(),
-		FeeEstimator:      estimator,
-		Wallet:            wallet,
-		ChainNotifier:     notifier,
-		ChanStatusMgr:     chanStatusMgr,
-		Features:          lnwire.NewFeatureVector(nil, lnwire.Features),
-		DisconnectPeer:    func(b *btcec.PublicKey) error { return nil },
-		ChannelNotifier:   channelNotifier,
-	}
-
-	alicePeer := NewBrontide(*cfg)
-	alicePeer.remoteFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
 
 	chanID := lnwire.NewChanIDFromOutPoint(channelAlice.ChannelPoint())
 	alicePeer.activeChannels.Store(chanID, channelAlice)
@@ -418,7 +331,13 @@ func createTestPeer(t *testing.T, notifier chainntnfs.ChainNotifier,
 	alicePeer.wg.Add(1)
 	go alicePeer.channelManager()
 
-	return alicePeer, channelBob, nil
+	return &peerTestCtx{
+		peer:       alicePeer,
+		channel:    channelBob,
+		notifier:   notifier,
+		publishTx:  publishTx,
+		mockSwitch: mockSwitch,
+	}, nil
 }
 
 // mockMessageSwitch is a mock implementation of the messageSwitch interface
@@ -618,3 +537,240 @@ func (m *mockMessageConn) LocalAddr() net.Addr {
 func (m *mockMessageConn) Close() error {
 	return nil
 }
+
+// createTestPeer creates a new peer for testing and returns a context struct
+// containing necessary handles and mock objects for conducting tests on peer
+// functionalities.
+func createTestPeer(t *testing.T) *peerTestCtx {
+	nodeKeyLocator := keychain.KeyLocator{
+		Family: keychain.KeyFamilyNodeKey,
+	}
+
+	aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(
+		channels.AlicesPrivKey,
+	)
+
+	aliceKeySigner := keychain.NewPrivKeyMessageSigner(
+		aliceKeyPriv, nodeKeyLocator,
+	)
+
+	aliceAddr := &net.TCPAddr{
+		IP:   net.ParseIP("127.0.0.1"),
+		Port: 18555,
+	}
+	cfgAddr := &lnwire.NetAddress{
+		IdentityKey: aliceKeyPub,
+		Address:     aliceAddr,
+		ChainNet:    wire.SimNet,
+	}
+
+	errBuffer, err := queue.NewCircularBuffer(ErrorBufferSize)
+	require.NoError(t, err)
+
+	chainIO := &mock.ChainIO{
+		BestHeight: broadcastHeight,
+	}
+
+	publishTx := make(chan *wire.MsgTx)
+	wallet := &lnwallet.LightningWallet{
+		WalletController: &mock.WalletController{
+			RootKey:               aliceKeyPriv,
+			PublishedTransactions: publishTx,
+		},
+	}
+
+	const chanActiveTimeout = time.Minute
+
+	dbAlice, err := channeldb.Open(t.TempDir())
+	require.NoError(t, err)
+	t.Cleanup(func() {
+		require.NoError(t, dbAlice.Close())
+	})
+
+	nodeSignerAlice := netann.NewNodeSigner(aliceKeySigner)
+
+	chanStatusMgr, err := netann.NewChanStatusManager(&netann.
+		ChanStatusConfig{
+		ChanStatusSampleInterval: 30 * time.Second,
+		ChanEnableTimeout:        chanActiveTimeout,
+		ChanDisableTimeout:       2 * time.Minute,
+		DB:                       dbAlice.ChannelStateDB(),
+		Graph:                    dbAlice.ChannelGraph(),
+		MessageSigner:            nodeSignerAlice,
+		OurPubKey:                aliceKeyPub,
+		OurKeyLoc:                testKeyLoc,
+		IsChannelActive: func(lnwire.ChannelID) bool {
+			return true
+		},
+		ApplyChannelUpdate: func(*lnwire.ChannelUpdate,
+			*wire.OutPoint, bool) error {
+
+			return nil
+		},
+	})
+	require.NoError(t, err)
+
+	interceptableSwitchNotifier := &mock.ChainNotifier{
+		EpochChan: make(chan *chainntnfs.BlockEpoch, 1),
+	}
+	interceptableSwitchNotifier.EpochChan <- &chainntnfs.BlockEpoch{
+		Height: 1,
+	}
+
+	interceptableSwitch, err := htlcswitch.NewInterceptableSwitch(
+		&htlcswitch.InterceptableSwitchConfig{
+			CltvRejectDelta:    testCltvRejectDelta,
+			CltvInterceptDelta: testCltvRejectDelta + 3,
+			Notifier:           interceptableSwitchNotifier,
+		},
+	)
+	require.NoError(t, err)
+
+	// TODO(yy): create interface for lnwallet.LightningChannel so we can
+	// easily mock it without the following setups.
+	notifier := &mock.ChainNotifier{
+		SpendChan: make(chan *chainntnfs.SpendDetail),
+		EpochChan: make(chan *chainntnfs.BlockEpoch),
+		ConfChan:  make(chan *chainntnfs.TxConfirmation),
+	}
+
+	mockSwitch := &mockMessageSwitch{}
+
+	// TODO(yy): change ChannelNotifier to be an interface.
+	channelNotifier := channelnotifier.New(dbAlice.ChannelStateDB())
+	require.NoError(t, channelNotifier.Start())
+	t.Cleanup(func() {
+		require.NoError(t, channelNotifier.Stop(),
+			"stop channel notifier failed")
+	})
+
+	writeBufferPool := pool.NewWriteBuffer(
+		pool.DefaultWriteBufferGCInterval,
+		pool.DefaultWriteBufferExpiryInterval,
+	)
+
+	writePool := pool.NewWrite(
+		writeBufferPool, 1, timeout,
+	)
+	require.NoError(t, writePool.Start())
+
+	readBufferPool := pool.NewReadBuffer(
+		pool.DefaultReadBufferGCInterval,
+		pool.DefaultReadBufferExpiryInterval,
+	)
+
+	readPool := pool.NewRead(
+		readBufferPool, 1, timeout,
+	)
+	require.NoError(t, readPool.Start())
+
+	mockConn := newMockConn(t, 1)
+
+	receivedCustomChan := make(chan *customMsg)
+
+	var pubKey [33]byte
+	copy(pubKey[:], aliceKeyPub.SerializeCompressed())
+
+	estimator := chainfee.NewStaticEstimator(12500, 0)
+
+	cfg := &Config{
+		Addr:              cfgAddr,
+		PubKeyBytes:       pubKey,
+		ErrorBuffer:       errBuffer,
+		ChainIO:           chainIO,
+		Switch:            mockSwitch,
+		ChanActiveTimeout: chanActiveTimeout,
+		InterceptSwitch:   interceptableSwitch,
+		ChannelDB:         dbAlice.ChannelStateDB(),
+		FeeEstimator:      estimator,
+		Wallet:            wallet,
+		ChainNotifier:     notifier,
+		ChanStatusMgr:     chanStatusMgr,
+		Features: lnwire.NewFeatureVector(
+			nil, lnwire.Features,
+		),
+		DisconnectPeer: func(b *btcec.PublicKey) error {
+			return nil
+		},
+		ChannelNotifier:               channelNotifier,
+		PrunePersistentPeerConnection: func([33]byte) {},
+		LegacyFeatures:                lnwire.EmptyFeatureVector(),
+		WritePool:                     writePool,
+		ReadPool:                      readPool,
+		Conn:                          mockConn,
+		HandleCustomMessage: func(
+			peer [33]byte, msg *lnwire.Custom) error {
+
+			receivedCustomChan <- &customMsg{
+				peer: peer,
+				msg:  *msg,
+			}
+
+			return nil
+		},
+		PongBuf: make([]byte, lnwire.MaxPongBytes),
+	}
+
+	alicePeer := NewBrontide(*cfg)
+
+	return &peerTestCtx{
+		publishTx:     publishTx,
+		mockSwitch:    mockSwitch,
+		peer:          alicePeer,
+		notifier:      notifier,
+		db:            dbAlice,
+		privKey:       aliceKeyPriv,
+		mockConn:      mockConn,
+		customChan:    receivedCustomChan,
+		chanStatusMgr: chanStatusMgr,
+	}
+}
+
+// startPeer invokes the `Start` method on the specified peer and handles any
+// initial startup messages for testing.
+func startPeer(t *testing.T, mockConn *mockMessageConn,
+	peer *Brontide) <-chan struct{} {
+
+	// Start the peer in a goroutine so that we can handle and test for
+	// startup messages. Successfully sending and receiving init message,
+	// indicates a successful startup.
+	done := make(chan struct{})
+	go func() {
+		require.NoError(t, peer.Start())
+		close(done)
+	}()
+
+	// Receive the init message that should be the first message received on
+	// startup.
+	rawMsg, err := fn.RecvOrTimeout[[]byte](
+		mockConn.writtenMessages, timeout,
+	)
+	require.NoError(t, err)
+
+	msgReader := bytes.NewReader(rawMsg)
+	nextMsg, err := lnwire.ReadMessage(msgReader, 0)
+	require.NoError(t, err)
+
+	_, ok := nextMsg.(*lnwire.Init)
+	require.True(t, ok)
+
+	// Write the reply for the init message to complete the startup.
+	initReplyMsg := lnwire.NewInitMessage(
+		lnwire.NewRawFeatureVector(
+			lnwire.DataLossProtectRequired,
+			lnwire.GossipQueriesOptional,
+		),
+		lnwire.NewRawFeatureVector(),
+	)
+
+	var b bytes.Buffer
+	_, err = lnwire.WriteMessage(&b, initReplyMsg, 0)
+	require.NoError(t, err)
+
+	ok = fn.SendOrQuit[[]byte, struct{}](
+		mockConn.readMessages, b.Bytes(), make(chan struct{}),
+	)
+	require.True(t, ok)
+
+	return done
+}
diff --git a/scripts/itest_part.sh b/scripts/itest_part.sh
index bdf4ecdf5..312e13a01 100755
--- a/scripts/itest_part.sh
+++ b/scripts/itest_part.sh
@@ -16,8 +16,6 @@ shift
 EXEC="$WORKDIR"/itest.test"$EXEC_SUFFIX"
 LND_EXEC="$WORKDIR"/lnd-itest"$EXEC_SUFFIX"
 BTCD_EXEC="$WORKDIR"/btcd-itest"$EXEC_SUFFIX"
-export GOCOVERDIR="$WORKDIR/cover"
-mkdir -p "$GOCOVERDIR"
 echo $EXEC -test.v "$@" -logoutput -logdir=.logs-tranche$TRANCHE -lndexec=$LND_EXEC -btcdexec=$BTCD_EXEC -splittranches=$NUM_TRANCHES -runtranche=$TRANCHE
 
 # Exit code 255 causes the parallel jobs to abort, so if one part fails the