Repository: lightningnetwork/lnd
Pull request #8526 (2024-03-06 16:38:38)

cli: add confirmation prompt to closeallchannels
Fixes https://github.com/lightningnetwork/lnd/issues/6743.


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

> [!IMPORTANT]
> ## Auto Review Skipped
> 
> Auto reviews are limited to the following labels: llm-review. Please add one of these labels to enable auto reviews.
> 
> 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.
> 

<!-- end of auto-generated comment: skip review 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:

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
	- `I pushed a fix in commit <commit_id>.`
	- `Generate unit-tests 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 tests 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 tests.`
	- `@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.
- The JSON schema for the configuration file is available [here](https://coderabbit.ai/integrations/coderabbit-overrides.v2.json).
- 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/coderabbit-overrides.v2.json`

### CodeRabbit Discord Community

Join our [Discord Community](https://discord.com/invite/GsXnASn26c) to get help, request features, and share feedback.

</details>

<!-- tips_end -->

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index deceba636..d51ca9c32 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -252,10 +252,6 @@ jobs:
             args: backend=bitcoind dbbackend=postgres
           - name: bitcoind-sqlite
             args: backend=bitcoind dbbackend=sqlite
-          - name: bitcoind-postgres-nativesql
-            args: backend=bitcoind dbbackend=postgres nativesql=true
-          - name: bitcoind-sqlite-nativesql
-            args: backend=bitcoind dbbackend=sqlite nativesql=true
           - name: neutrino
             args: backend=neutrino
     steps:
diff --git a/cmd/lncli/commands.go b/cmd/lncli/commands.go
index cc8efc5f0..5190cd69f 100644
--- a/cmd/lncli/commands.go
+++ b/cmd/lncli/commands.go
@@ -994,6 +994,11 @@ var closeAllChannelsCommand = cli.Command{
 				"sat/vbyte that should be used when crafting " +
 				"the closing transactions",
 		},
+		cli.BoolFlag{
+			Name: "s, skip_confirmation",
+			Usage: "Skip the confirmation prompt and close all " +
+				"channels immediately",
+		},
 	},
 	Action: actionDecorator(closeAllChannels),
 }
@@ -1012,6 +1017,11 @@ func closeAllChannels(ctx *cli.Context) error {
 		return err
 	}
 
+	prompt := "Do you really want to close ALL channels? (yes/no): "
+	if !ctx.Bool("skip_confirmation") && !promptForConfirmation(prompt) {
+		return errors.New("action aborted by user")
+	}
+
 	listReq := &lnrpc.ListChannelsRequest{}
 	openChannels, err := client.ListChannels(ctxc, listReq)
 	if err != nil {
diff --git a/config_builder.go b/config_builder.go
index 5195777e1..27e9f47f6 100644
--- a/config_builder.go
+++ b/config_builder.go
@@ -3,7 +3,6 @@ package lnd
 import (
 	"bytes"
 	"context"
-	"database/sql"
 	"errors"
 	"fmt"
 	"io/ioutil"
@@ -32,7 +31,6 @@ import (
 	"github.com/lightningnetwork/lnd/chainntnfs"
 	"github.com/lightningnetwork/lnd/chainreg"
 	"github.com/lightningnetwork/lnd/channeldb"
-	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/invoices"
 	"github.com/lightningnetwork/lnd/keychain"
 	"github.com/lightningnetwork/lnd/kvdb"
@@ -44,7 +42,6 @@ import (
 	"github.com/lightningnetwork/lnd/macaroons"
 	"github.com/lightningnetwork/lnd/rpcperms"
 	"github.com/lightningnetwork/lnd/signal"
-	"github.com/lightningnetwork/lnd/sqldb"
 	"github.com/lightningnetwork/lnd/walletunlocker"
 	"github.com/lightningnetwork/lnd/watchtower"
 	"github.com/lightningnetwork/lnd/watchtower/wtclient"
@@ -872,11 +869,6 @@ type DatabaseInstances struct {
 	// WalletDB is the configuration for loading the wallet database using
 	// the btcwallet's loader.
 	WalletDB btcwallet.LoaderOption
-
-	// NativeSQLStore is a pointer to a native SQL store that can be used
-	// for native SQL queries for tables that already support it. This may
-	// be nil if the use-native-sql flag was not set.
-	NativeSQLStore *sqldb.BaseDB
 }
 
 // DefaultDatabaseBuilder is a type that builds the default database backends
@@ -931,11 +923,10 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
 	// state DB point to the same local or remote DB and the same namespace
 	// within that DB.
 	dbs := &DatabaseInstances{
-		HeightHintDB:   databaseBackends.HeightHintDB,
-		MacaroonDB:     databaseBackends.MacaroonDB,
-		DecayedLogDB:   databaseBackends.DecayedLogDB,
-		WalletDB:       databaseBackends.WalletDB,
-		NativeSQLStore: databaseBackends.NativeSQLStore,
+		HeightHintDB: databaseBackends.HeightHintDB,
+		MacaroonDB:   databaseBackends.MacaroonDB,
+		DecayedLogDB: databaseBackends.DecayedLogDB,
+		WalletDB:     databaseBackends.WalletDB,
 	}
 	cleanUp := func() {
 		// We can just close the returned close functions directly. Even
@@ -1020,21 +1011,11 @@ func (d *DefaultDatabaseBuilder) BuildDatabase(
 	// using the same struct (and DB backend) instance.
 	dbs.ChanStateDB = dbs.GraphDB
 
-	// Instantiate a native SQL invoice store if the flag is set.
-	if d.cfg.DB.UseNativeSQL {
-		executor := sqldb.NewTransactionExecutor(
-			dbs.NativeSQLStore,
-			func(tx *sql.Tx) sqldb.InvoiceQueries {
-				return dbs.NativeSQLStore.WithTx(tx)
-			},
-		)
-
-		dbs.InvoiceDB = sqldb.NewInvoiceStore(
-			executor, clock.NewDefaultClock(),
-		)
-	} else {
-		dbs.InvoiceDB = dbs.GraphDB
-	}
+	// For now the only InvoiceDB implementation is the *channeldb.DB.
+	//
+	// TODO(positiveblue): use a sql first implementation for this
+	// interface.
+	dbs.InvoiceDB = dbs.GraphDB
 
 	// Wrap the watchtower client DB and make sure we clean up.
 	if cfg.WtClient.Active {
@@ -1317,9 +1298,8 @@ func initNeutrinoBackend(ctx context.Context, cfg *Config, chainDir string,
 	)
 	switch {
 	case cfg.DB.Backend == kvdb.SqliteBackendName:
-		sqliteConfig := lncfg.GetSqliteConfigKVDB(cfg.DB.Sqlite)
 		db, err = kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, dbPath,
+			kvdb.SqliteBackendName, ctx, cfg.DB.Sqlite, dbPath,
 			lncfg.SqliteNeutrinoDBName, lncfg.NSNeutrinoDB,
 		)
 
diff --git a/docs/release-notes/release-notes-0.18.0.md b/docs/release-notes/release-notes-0.18.0.md
index 6ea9bcd03..95c6fc80a 100644
--- a/docs/release-notes/release-notes-0.18.0.md
+++ b/docs/release-notes/release-notes-0.18.0.md
@@ -136,10 +136,7 @@
   through [mempool acceptance
   check](https://github.com/lightningnetwork/lnd/pull/8345) before being
   broadcast. This means when a transaction has failed the `testmempoolaccept`
-  check by bitcoind or btcd, the broadcast won't be attempted. This check will
-  be performed if the version of the chain backend supports it - for bitcoind
-  it's v22.0.0 and above, for btcd it's v0.24.1 and above. Otherwise, the check
-  will be [skipped](https://github.com/lightningnetwork/lnd/pull/8505)
+  check by bitcoind or btcd, the broadcast won't be attempted.
 
 * The `coin-selection-strategy` config option [now also applies to channel
   funding operations and the new `PsbtCoinSelect` option of the `FundPsbt`
@@ -191,6 +188,9 @@
   [Deprecate bumpclosefee for bumpforceclosefee and add `max_fee_rate` option
    to `closechannel` cmd](https://github.com/lightningnetwork/lnd/pull/8350).
 
+* The [`closeallchannels` command now asks for confirmation before closing 
+  all channels](https://github.com/lightningnetwork/lnd/pull/8526).
+
 # Improvements
 ## Functional Updates
 ### Tlv
@@ -339,10 +339,6 @@
 * [Refactor UpdateInvoice](https://github.com/lightningnetwork/lnd/pull/8100) to
   make it simpler to adjust code to also support SQL InvoiceDB implementation.
 
-* [InvoiceDB implementation](https://github.com/lightningnetwork/lnd/pull/8052)
-  for SQL backends enabling new users to optionally use  an experimental native
-  SQL invoices database.
-
 ## Code Health
 
 * [Remove database pointers](https://github.com/lightningnetwork/lnd/pull/8117) 
diff --git a/go.mod b/go.mod
index 6a33f3221..ebbd24ba8 100644
--- a/go.mod
+++ b/go.mod
@@ -4,13 +4,13 @@ require (
 	github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82
 	github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344
 	github.com/andybalholm/brotli v1.0.3
-	github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af
+	github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5
 	github.com/btcsuite/btcd/btcec/v2 v2.3.2
 	github.com/btcsuite/btcd/btcutil v1.1.5
 	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-20170628155309-84c8d2346e9f
-	github.com/btcsuite/btcwallet v0.16.10-0.20240305014015-f7c216e78ee8
+	github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14
 	github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4
 	github.com/btcsuite/btcwallet/wallet/txrules v1.2.1
 	github.com/btcsuite/btcwallet/walletdb v1.4.1
diff --git a/go.sum b/go.sum
index ccfd72772..99d183cba 100644
--- a/go.sum
+++ b/go.sum
@@ -71,8 +71,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
 github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
 github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
 github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
-github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af h1:F60A3wst4/fy9Yr1Vn8MYmFlfn7DNLxp8o8UTvhqgBE=
-github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
+github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5 h1:8BHBWvtP6kkzvmCpyWEznq4eS0gfLOSVuXLesv413Xs=
+github.com/btcsuite/btcd v0.24.1-0.20240123000108-62e6af035ec5/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
 github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
 github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
 github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
@@ -90,8 +90,8 @@ github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtyd
 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
 github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
-github.com/btcsuite/btcwallet v0.16.10-0.20240305014015-f7c216e78ee8 h1:fkPZ7LfOCm3Nn0Y/56LOqwooYaw9cV/4jZkdHw7XGik=
-github.com/btcsuite/btcwallet v0.16.10-0.20240305014015-f7c216e78ee8/go.mod h1:q+J5AzV+ymzUaBXkP099uuVh18yzCyHHXPc4rxi55mk=
+github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14 h1:GnTInK5UIkDJw9alXR4JeOmKmodJu/5qYknZWqp1Sk0=
+github.com/btcsuite/btcwallet v0.16.10-0.20240206195028-1f3534b00d14/go.mod h1:3EItwIQcrXGkcQlO1OginQ3Ab8YgE8kxka9hjgFuWxM=
 github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4 h1:poyHFf7+5+RdxNp5r2T6IBRD7RyraUsYARYbp/7t4D8=
 github.com/btcsuite/btcwallet/wallet/txauthor v1.3.4/go.mod h1:GETGDQuyq+VFfH1S/+/7slLM/9aNa4l7P4ejX6dJfb0=
 github.com/btcsuite/btcwallet/wallet/txrules v1.2.1 h1:UZo7YRzdHbwhK7Rhv3PO9bXgTxiOH45edK5qdsdiatk=
diff --git a/htlcswitch/interfaces.go b/htlcswitch/interfaces.go
index ac3e10bab..75866beba 100644
--- a/htlcswitch/interfaces.go
+++ b/htlcswitch/interfaces.go
@@ -7,6 +7,7 @@ import (
 	"github.com/lightningnetwork/lnd/channeldb"
 	"github.com/lightningnetwork/lnd/channeldb/models"
 	"github.com/lightningnetwork/lnd/invoices"
+	"github.com/lightningnetwork/lnd/lnpeer"
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwallet/chainfee"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -261,9 +262,9 @@ type ChannelLink interface {
 	// total sent/received milli-satoshis.
 	Stats() (uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi)
 
-	// Peer returns the serialized public key of remote peer with which we
-	// have the channel link opened.
-	PeerPubKey() [33]byte
+	// Peer returns the representation of remote peer with which we have
+	// the channel link opened.
+	Peer() lnpeer.Peer
 
 	// AttachMailBox delivers an active MailBox to the link. The MailBox may
 	// have buffered messages.
diff --git a/htlcswitch/link.go b/htlcswitch/link.go
index 0b1a3b160..f8f87ca8f 100644
--- a/htlcswitch/link.go
+++ b/htlcswitch/link.go
@@ -92,6 +92,13 @@ type ChannelLinkConfig struct {
 	// allowing the link to open and close circuits.
 	Circuits CircuitModifier
 
+	// Switch provides a reference to the HTLC switch, we only use this in
+	// testing to access circuit operations not typically exposed by the
+	// CircuitModifier.
+	//
+	// TODO(conner): remove after refactoring htlcswitch testing framework.
+	Switch *Switch
+
 	// BestHeight returns the best known height.
 	BestHeight func() uint32
 
@@ -313,6 +320,9 @@ type channelLink struct {
 	// updates.
 	channel *lnwallet.LightningChannel
 
+	// shortChanID is the most up to date short channel ID for the link.
+	shortChanID lnwire.ShortChannelID
+
 	// cfg is a structure which carries all dependable fields/handlers
 	// which may affect behaviour of the service.
 	cfg ChannelLinkConfig
@@ -445,6 +455,7 @@ func NewChannelLink(cfg ChannelLinkConfig,
 	return &channelLink{
 		cfg:                 cfg,
 		channel:             channel,
+		shortChanID:         channel.ShortChanID(),
 		hodlMap:             make(map[models.CircuitKey]hodlHtlc),
 		hodlQueue:           queue.NewConcurrentQueue(10),
 		log:                 build.NewPrefixLog(logPrefix, log),
@@ -2191,7 +2202,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
 		for id, settled := range finalHTLCs {
 			l.cfg.HtlcNotifier.NotifyFinalHtlcEvent(
 				models.CircuitKey{
-					ChanID: l.ShortChanID(),
+					ChanID: l.shortChanID,
 					HtlcID: id,
 				},
 				channeldb.FinalHtlcInfo{
@@ -2552,8 +2563,8 @@ func (l *channelLink) updateCommitTx() error {
 // channel link opened.
 //
 // NOTE: Part of the ChannelLink interface.
-func (l *channelLink) PeerPubKey() [33]byte {
-	return l.cfg.Peer.PubKey()
+func (l *channelLink) Peer() lnpeer.Peer {
+	return l.cfg.Peer
 }
 
 // ChannelPoint returns the channel outpoint for the channel link.
@@ -2571,7 +2582,7 @@ func (l *channelLink) ShortChanID() lnwire.ShortChannelID {
 	l.RLock()
 	defer l.RUnlock()
 
-	return l.channel.ShortChanID()
+	return l.shortChanID
 }
 
 // UpdateShortChanID updates the short channel ID for a link. This may be
diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go
index e995b3ed7..ab6fbe76a 100644
--- a/htlcswitch/mock.go
+++ b/htlcswitch/mock.go
@@ -891,22 +891,10 @@ func (f *mockChannelLink) Start() error {
 	return nil
 }
 
-func (f *mockChannelLink) ChanID() lnwire.ChannelID {
-	return f.chanID
-}
-
-func (f *mockChannelLink) ShortChanID() lnwire.ShortChannelID {
-	return f.shortChanID
-}
-
-func (f *mockChannelLink) Bandwidth() lnwire.MilliSatoshi {
-	return 99999999
-}
-
-func (f *mockChannelLink) PeerPubKey() [33]byte {
-	return f.peer.PubKey()
-}
-
+func (f *mockChannelLink) ChanID() lnwire.ChannelID                     { return f.chanID }
+func (f *mockChannelLink) ShortChanID() lnwire.ShortChannelID           { return f.shortChanID }
+func (f *mockChannelLink) Bandwidth() lnwire.MilliSatoshi               { return 99999999 }
+func (f *mockChannelLink) Peer() lnpeer.Peer                            { return f.peer }
 func (f *mockChannelLink) ChannelPoint() *wire.OutPoint                 { return &wire.OutPoint{} }
 func (f *mockChannelLink) Stop()                                        {}
 func (f *mockChannelLink) EligibleToForward() bool                      { return f.eligible }
diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go
index 0e2101027..fa98cca11 100644
--- a/htlcswitch/switch.go
+++ b/htlcswitch/switch.go
@@ -1147,7 +1147,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
 
 			return s.failAddPacket(packet, linkError)
 		}
-		targetPeerKey := targetLink.PeerPubKey()
+		targetPeerKey := targetLink.Peer().PubKey()
 		interfaceLinks, _ := s.getLinks(targetPeerKey)
 		s.indexMtx.RUnlock()
 
@@ -1810,9 +1810,9 @@ out:
 			}
 			s.indexMtx.RUnlock()
 
-			peerPub := link.PeerPubKey()
+			peerPub := link.Peer().PubKey()
 			log.Debugf("Requesting local channel close: peer=%v, "+
-				"chan_id=%x", link.PeerPubKey(), chanID[:])
+				"chan_id=%x", link.Peer(), chanID[:])
 
 			go s.cfg.LocalChannelClose(peerPub[:], req)
 
@@ -2335,7 +2335,7 @@ func (s *Switch) addLiveLink(link ChannelLink) {
 
 	// Next we'll add the link to the interface index so we can
 	// quickly look up all the channels for a particular node.
-	peerPub := link.PeerPubKey()
+	peerPub := link.Peer().PubKey()
 	if _, ok := s.interfaceIndex[peerPub]; !ok {
 		s.interfaceIndex[peerPub] = make(map[lnwire.ChannelID]ChannelLink)
 	}
@@ -2610,7 +2610,7 @@ func (s *Switch) removeLink(chanID lnwire.ChannelID) ChannelLink {
 
 	// If the link has been added to the peer index, then we'll move to
 	// delete the entry within the index.
-	peerPub := link.PeerPubKey()
+	peerPub := link.Peer().PubKey()
 	if peerIndex, ok := s.interfaceIndex[peerPub]; ok {
 		delete(peerIndex, link.ChanID())
 
diff --git a/lncfg/db.go b/lncfg/db.go
index 49f506bb1..d1ecf6386 100644
--- a/lncfg/db.go
+++ b/lncfg/db.go
@@ -3,7 +3,6 @@ package lncfg
 import (
 	"context"
 	"fmt"
-	"path"
 	"path/filepath"
 	"time"
 
@@ -15,7 +14,6 @@ import (
 	"github.com/lightningnetwork/lnd/kvdb/sqlite"
 	"github.com/lightningnetwork/lnd/lnrpc"
 	"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
-	"github.com/lightningnetwork/lnd/sqldb"
 )
 
 const (
@@ -30,7 +28,6 @@ const (
 	SqliteChainDBName    = "chain.sqlite"
 	SqliteNeutrinoDBName = "neutrino.sqlite"
 	SqliteTowerDBName    = "watchtower.sqlite"
-	SqliteNativeDBName   = "lnd.sqlite"
 
 	BoltBackend                = "bolt"
 	EtcdBackend                = "etcd"
@@ -81,11 +78,9 @@ type DB struct {
 
 	Bolt *kvdb.BoltConfig `group:"bolt" namespace:"bolt" description:"Bolt settings."`
 
-	Postgres *sqldb.PostgresConfig `group:"postgres" namespace:"postgres" description:"Postgres settings."`
+	Postgres *postgres.Config `group:"postgres" namespace:"postgres" description:"Postgres settings."`
 
-	Sqlite *sqldb.SqliteConfig `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."`
-
-	UseNativeSQL bool `long:"use-native-sql" description:"Use native SQL for tables that already support it."`
+	Sqlite *sqlite.Config `group:"sqlite" namespace:"sqlite" description:"Sqlite settings."`
 
 	NoGraphCache bool `long:"no-graph-cache" description:"Don't use the in-memory graph cache for path finding. Much slower but uses less RAM. Can only be used with a bolt database backend."`
 
@@ -108,14 +103,13 @@ func DefaultDB() *DB {
 			// Allow at most 32 MiB messages by default.
 			MaxMsgSize: 32768 * 1024,
 		},
-		Postgres: &sqldb.PostgresConfig{
+		Postgres: &postgres.Config{
 			MaxConnections: defaultPostgresMaxConnections,
 		},
-		Sqlite: &sqldb.SqliteConfig{
+		Sqlite: &sqlite.Config{
 			MaxConnections: defaultSqliteMaxConnections,
 			BusyTimeout:    defaultSqliteBusyTimeout,
 		},
-		UseNativeSQL: false,
 	}
 }
 
@@ -124,8 +118,8 @@ func (db *DB) Validate() error {
 	switch db.Backend {
 	case BoltBackend, SqliteBackend:
 	case PostgresBackend:
-		if err := db.Postgres.Validate(); err != nil {
-			return err
+		if db.Postgres.Dsn == "" {
+			return fmt.Errorf("postgres dsn must be set")
 		}
 
 	case EtcdBackend:
@@ -220,11 +214,6 @@ type DatabaseBackends struct {
 	// the underlying wallet database from.
 	WalletDB btcwallet.LoaderOption
 
-	// NativeSQLStore is a pointer to a native SQL store that can be used
-	// for native SQL queries for tables that already support it. This may
-	// be nil if the use-native-sql flag was not set.
-	NativeSQLStore *sqldb.BaseDB
-
 	// Remote indicates whether the database backends are remote, possibly
 	// replicated instances or local bbolt or sqlite backed databases.
 	Remote bool
@@ -234,26 +223,6 @@ type DatabaseBackends struct {
 	CloseFuncs map[string]func() error
 }
 
-// GetPostgresConfigKVDB converts a sqldb.PostgresConfig to a kvdb
-// postgres.Config.
-func GetPostgresConfigKVDB(cfg *sqldb.PostgresConfig) *postgres.Config {
-	return &postgres.Config{
-		Dsn:            cfg.Dsn,
-		Timeout:        cfg.Timeout,
-		MaxConnections: cfg.MaxConnections,
-	}
-}
-
-// GetSqliteConfigKVDB converts a sqldb.SqliteConfig to a kvdb sqlite.Config.
-func GetSqliteConfigKVDB(cfg *sqldb.SqliteConfig) *sqlite.Config {
-	return &sqlite.Config{
-		Timeout:        cfg.Timeout,
-		BusyTimeout:    cfg.BusyTimeout,
-		MaxConnections: cfg.MaxConnections,
-		PragmaOptions:  cfg.PragmaOptions,
-	}
-}
-
 // GetBackends returns a set of kvdb.Backends as set in the DB config.
 func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 	walletDBPath, towerServerDBPath string, towerClientEnabled,
@@ -373,14 +342,9 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		}, nil
 
 	case PostgresBackend:
-		// Convert the sqldb PostgresConfig to a kvdb postgres.Config.
-		// This is a temporary measure until we migrate all kvdb SQL
-		// users to native SQL.
-		postgresConfig := GetPostgresConfigKVDB(db.Postgres)
-
 		postgresBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSChannelDB,
+			db.Postgres, NSChannelDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres graph "+
@@ -390,7 +354,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 
 		postgresMacaroonBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSMacaroonDB,
+			db.Postgres, NSMacaroonDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres "+
@@ -400,7 +364,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 
 		postgresDecayedLogBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSDecayedLogDB,
+			db.Postgres, NSDecayedLogDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres "+
@@ -410,7 +374,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 
 		postgresTowerClientBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSTowerClientDB,
+			db.Postgres, NSTowerClientDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres tower "+
@@ -420,7 +384,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 
 		postgresTowerServerBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSTowerServerDB,
+			db.Postgres, NSTowerServerDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres tower "+
@@ -430,7 +394,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 
 		postgresWalletBackend, err := kvdb.Open(
 			kvdb.PostgresBackendName, ctx,
-			postgresConfig, NSWalletDB,
+			db.Postgres, NSWalletDB,
 		)
 		if err != nil {
 			return nil, fmt.Errorf("error opening postgres macaroon "+
@@ -438,20 +402,6 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		}
 		closeFuncs[NSWalletDB] = postgresWalletBackend.Close
 
-		var nativeSQLStore *sqldb.BaseDB
-		if db.UseNativeSQL {
-			nativePostgresStore, err := sqldb.NewPostgresStore(
-				db.Postgres,
-			)
-			if err != nil {
-				return nil, fmt.Errorf("error opening "+
-					"native postgres store: %v", err)
-			}
-
-			nativeSQLStore = nativePostgresStore.BaseDB
-			closeFuncs[PostgresBackend] = nativePostgresStore.Close
-		}
-
 		// Warn if the user is trying to switch over to a Postgres DB
 		// while there is a wallet or channel bbolt DB still present.
 		warnExistingBoltDBs(
@@ -479,17 +429,11 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 			WalletDB: btcwallet.LoaderWithExternalWalletDB(
 				postgresWalletBackend,
 			),
-			NativeSQLStore: nativeSQLStore,
-			Remote:         true,
-			CloseFuncs:     closeFuncs,
+			Remote:     true,
+			CloseFuncs: closeFuncs,
 		}, nil
 
 	case SqliteBackend:
-		// Convert the sqldb SqliteConfig to a kvdb sqlite.Config.
-		// This is a temporary measure until we migrate all kvdb SQL
-		// users to native SQL.
-		sqliteConfig := GetSqliteConfigKVDB(db.Sqlite)
-
 		// Note that for sqlite, we put kv tables for the channel.db,
 		// wtclient.db and sphinxreplay.db all in the channel.sqlite db.
 		// The tables for wallet.db and macaroon.db are in the
@@ -501,7 +445,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		// would cause deadlocks in the code due to the wallet db often
 		// being accessed during a write to another db.
 		sqliteBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
+			kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath,
 			SqliteChannelDBName, NSChannelDB,
 		)
 		if err != nil {
@@ -511,7 +455,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		closeFuncs[NSChannelDB] = sqliteBackend.Close
 
 		sqliteMacaroonBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
+			kvdb.SqliteBackendName, ctx, db.Sqlite, walletDBPath,
 			SqliteChainDBName, NSMacaroonDB,
 		)
 		if err != nil {
@@ -521,7 +465,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		closeFuncs[NSMacaroonDB] = sqliteMacaroonBackend.Close
 
 		sqliteDecayedLogBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
+			kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath,
 			SqliteChannelDBName, NSDecayedLogDB,
 		)
 		if err != nil {
@@ -531,7 +475,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		closeFuncs[NSDecayedLogDB] = sqliteDecayedLogBackend.Close
 
 		sqliteTowerClientBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, chanDBPath,
+			kvdb.SqliteBackendName, ctx, db.Sqlite, chanDBPath,
 			SqliteChannelDBName, NSTowerClientDB,
 		)
 		if err != nil {
@@ -541,7 +485,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		closeFuncs[NSTowerClientDB] = sqliteTowerClientBackend.Close
 
 		sqliteTowerServerBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig,
+			kvdb.SqliteBackendName, ctx, db.Sqlite,
 			towerServerDBPath, SqliteTowerDBName, NSTowerServerDB,
 		)
 		if err != nil {
@@ -551,7 +495,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		closeFuncs[NSTowerServerDB] = sqliteTowerServerBackend.Close
 
 		sqliteWalletBackend, err := kvdb.Open(
-			kvdb.SqliteBackendName, ctx, sqliteConfig, walletDBPath,
+			kvdb.SqliteBackendName, ctx, db.Sqlite, walletDBPath,
 			SqliteChainDBName, NSWalletDB,
 		)
 		if err != nil {
@@ -560,21 +504,6 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 		}
 		closeFuncs[NSWalletDB] = sqliteWalletBackend.Close
 
-		var nativeSQLStore *sqldb.BaseDB
-		if db.UseNativeSQL {
-			nativeSQLiteStore, err := sqldb.NewSqliteStore(
-				db.Sqlite,
-				path.Join(chanDBPath, SqliteNativeDBName),
-			)
-			if err != nil {
-				return nil, fmt.Errorf("error opening "+
-					"native SQLite store: %v", err)
-			}
-
-			nativeSQLStore = nativeSQLiteStore.BaseDB
-			closeFuncs[SqliteBackend] = nativeSQLiteStore.Close
-		}
-
 		// Warn if the user is trying to switch over to a sqlite DB
 		// while there is a wallet or channel bbolt DB still present.
 		warnExistingBoltDBs(
@@ -602,8 +531,7 @@ func (db *DB) GetBackends(ctx context.Context, chanDBPath,
 			WalletDB: btcwallet.LoaderWithExternalWalletDB(
 				sqliteWalletBackend,
 			),
-			NativeSQLStore: nativeSQLStore,
-			CloseFuncs:     closeFuncs,
+			CloseFuncs: closeFuncs,
 		}, nil
 	}
 
diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go
index fd4683509..364953be4 100644
--- a/lnwallet/btcwallet/btcwallet.go
+++ b/lnwallet/btcwallet/btcwallet.go
@@ -14,7 +14,6 @@ import (
 	"github.com/btcsuite/btcd/btcutil/hdkeychain"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
-	"github.com/btcsuite/btcd/rpcclient"
 	"github.com/btcsuite/btcd/txscript"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/btcsuite/btcwallet/chain"
@@ -1263,18 +1262,6 @@ func (b *BtcWallet) PublishTransaction(tx *wire.MsgTx, label string) error {
 	// or 10,000 sat/vb.
 	results, err := b.chain.TestMempoolAccept([]*wire.MsgTx{tx}, 0)
 	if err != nil {
-		// If the chain backend doesn't support the mempool acceptance
-		// test RPC, we'll just attempt to publish the transaction.
-		if errors.Is(err, rpcclient.ErrBackendVersion) {
-			log.Warnf("TestMempoolAccept not supported by "+
-				"backend, consider upgrading %s to a newer "+
-				"version", b.chain.BackEnd())
-
-			err := b.wallet.PublishTransaction(tx, label)
-
-			return handleErr(err)
-		}
-
 		return err
 	}
 
diff --git a/lnwire/message.go b/lnwire/message.go
index bcee9f86d..634f78ab3 100644
--- a/lnwire/message.go
+++ b/lnwire/message.go
@@ -190,19 +190,6 @@ type Message interface {
 	MsgType() MessageType
 }
 
-// LinkUpdater is an interface implemented by most messages in BOLT 2 that are
-// allowed to update the channel state.
-type LinkUpdater interface {
-	// All LinkUpdater messages are messages and so we embed the interface
-	// so that we can treat it as a message if all we know about it is that
-	// it is a LinkUpdater message.
-	Message
-
-	// TargetChanID returns the channel id of the link for which this
-	// message is intended.
-	TargetChanID() ChannelID
-}
-
 // makeEmptyMessage creates a new empty message of the proper concrete type
 // based on the passed message type.
 func makeEmptyMessage(msgType MessageType) (Message, error) {
diff --git a/peer/brontide.go b/peer/brontide.go
index 189fb1660..696ad8610 100644
--- a/peer/brontide.go
+++ b/peer/brontide.go
@@ -1750,7 +1750,7 @@ out:
 		// will consider them as link updates and send them to
 		// chanStream. These messages will be queued inside chanStream
 		// if the channel is not active yet.
-		case lnwire.LinkUpdater:
+		case LinkUpdater:
 			targetChan = msg.TargetChanID()
 			isLinkUpdate = p.hasChannel(targetChan)
 
diff --git a/peer/interfaces.go b/peer/interfaces.go
index 4f4619fef..562d89723 100644
--- a/peer/interfaces.go
+++ b/peer/interfaces.go
@@ -34,6 +34,14 @@ type messageSwitch interface {
 		error)
 }
 
+// LinkUpdater is an interface implemented by most messages in BOLT 2 that are
+// allowed to update the channel state.
+type LinkUpdater interface {
+	// TargetChanID returns the channel id of the link for which this message
+	// is intended.
+	TargetChanID() lnwire.ChannelID
+}
+
 // MessageConn is an interface implemented by anything that delivers
 // an lnwire.Message using a net.Conn interface.
 type MessageConn interface {
diff --git a/rpcserver.go b/rpcserver.go
index d55c97257..fdfb8ebd6 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -5806,7 +5806,7 @@ func (r *rpcServer) ListInvoices(ctx context.Context,
 		CreationDateEnd:   int64(req.CreationDateEnd),
 	}
 
-	invoiceSlice, err := r.server.invoicesDB.QueryInvoices(ctx, q)
+	invoiceSlice, err := r.server.miscDB.QueryInvoices(ctx, q)
 	if err != nil {
 		return nil, fmt.Errorf("unable to query invoices: %w", err)
 	}
diff --git a/sample-lnd.conf b/sample-lnd.conf
index abc0ea80f..cac080c7b 100644
--- a/sample-lnd.conf
+++ b/sample-lnd.conf
@@ -1309,11 +1309,6 @@
 ; the future.
 ; db.no-rev-log-amt-data=false
 
-; If set to true, native SQL will be used instead of KV emulation for tables
-; that support it already. Note: this is an experimental feature, use at your
-; own risk.
-; db.use-native-sql=false
-
 
 [etcd]
 
@@ -1409,9 +1404,6 @@
 ; Example:
 ;   db.postgres.maxconnections=
 
-; Whether to skip executing schema migrations.
-; db.postgres.skipmigrations=false
-
 
 [sqlite]
 
@@ -1436,8 +1428,6 @@
 ;   db.sqlite.pragmaoptions=auto_vacuum=incremental
 ;   db.sqlite.pragmaoptions=temp_store=MEMORY
 
-; Whether to skip executing schema migrations.
-; db.sqlite.skipmigrations=false
 
 [bolt]
 
diff --git a/scripts/gen_sqlc_docker.sh b/scripts/gen_sqlc_docker.sh
index e011d689e..9c8e88487 100755
--- a/scripts/gen_sqlc_docker.sh
+++ b/scripts/gen_sqlc_docker.sh
@@ -16,4 +16,4 @@ docker run \
   -e UID=$UID \
   -v "$DIR/../:/build" \
   -w /build \
-  sqlc/sqlc:1.25.0 generate
+  kjconroy/sqlc:1.18.0 generate
diff --git a/server.go b/server.go
index 32dbc9cf0..cb1540a2c 100644
--- a/server.go
+++ b/server.go
@@ -246,8 +246,6 @@ type server struct {
 	// channel DB that haven't been separated out yet.
 	miscDB *channeldb.DB
 
-	invoicesDB invoices.InvoiceDB
-
 	aliasMgr *aliasmgr.Manager
 
 	htlcSwitch *htlcswitch.Switch
@@ -566,7 +564,6 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 		chanStateDB:    dbs.ChanStateDB.ChannelStateDB(),
 		addrSource:     dbs.ChanStateDB,
 		miscDB:         dbs.ChanStateDB,
-		invoicesDB:     dbs.InvoiceDB,
 		cc:             cc,
 		sigPool:        lnwallet.NewSigPool(cfg.Workers.Sig, cc.Signer),
 		writePool:      writePool,
diff --git a/sqldb/config.go b/sqldb/config.go
deleted file mode 100644
index 5bc807806..000000000
--- a/sqldb/config.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package sqldb
-
-import (
-	"fmt"
-	"net/url"
-	"time"
-)
-
-const (
-	// defaultMaxConns is the number of permitted active and idle
-	// connections. We want to limit this so it isn't unlimited. We use the
-	// same value for the number of idle connections as, this can speed up
-	// queries given a new connection doesn't need to be established each
-	// time.
-	defaultMaxConns = 25
-
-	// connIdleLifetime is the amount of time a connection can be idle.
-	connIdleLifetime = 5 * time.Minute
-)
-
-// SqliteConfig holds all the config arguments needed to interact with our
-// sqlite DB.
-//
-//nolint:lll
-type SqliteConfig struct {
-	Timeout        time.Duration `long:"timeout" description:"The time after which a database query should be timed out."`
-	BusyTimeout    time.Duration `long:"busytimeout" description:"The maximum amount of time to wait for a database connection to become available for a query."`
-	MaxConnections int           `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."`
-	PragmaOptions  []string      `long:"pragmaoptions" description:"A list of pragma options to set on a database connection. For example, 'auto_vacuum=incremental'. Note that the flag must be specified multiple times if multiple options are to be set."`
-	SkipMigrations bool          `long:"skipmigrations" description:"Skip applying migrations on startup."`
-}
-
-// PostgresConfig holds the postgres database configuration.
-//
-//nolint:lll
-type PostgresConfig struct {
-	Dsn            string        `long:"dsn" description:"Database connection string."`
-	Timeout        time.Duration `long:"timeout" description:"Database connection timeout. Set to zero to disable."`
-	MaxConnections int           `long:"maxconnections" description:"The maximum number of open connections to the database. Set to zero for unlimited."`
-	SkipMigrations bool          `long:"skipmigrations" description:"Skip applying migrations on startup."`
-}
-
-func (p *PostgresConfig) Validate() error {
-	if p.Dsn == "" {
-		return fmt.Errorf("DSN is required")
-	}
-
-	// Parse the DSN as a URL.
-	_, err := url.Parse(p.Dsn)
-	if err != nil {
-		return fmt.Errorf("invalid DSN: %w", err)
-	}
-
-	return nil
-}
diff --git a/sqldb/invoices.go b/sqldb/invoices.go
deleted file mode 100644
index 6e2cb47a1..000000000
--- a/sqldb/invoices.go
+++ /dev/null
@@ -1,1658 +0,0 @@
-package sqldb
-
-import (
-	"context"
-	"crypto/sha256"
-	"database/sql"
-	"errors"
-	"fmt"
-	"math"
-	"strconv"
-	"time"
-
-	"github.com/lightningnetwork/lnd/channeldb/models"
-	"github.com/lightningnetwork/lnd/clock"
-	invpkg "github.com/lightningnetwork/lnd/invoices"
-	"github.com/lightningnetwork/lnd/lntypes"
-	"github.com/lightningnetwork/lnd/lnwire"
-	"github.com/lightningnetwork/lnd/record"
-	"github.com/lightningnetwork/lnd/sqldb/sqlc"
-)
-
-const (
-	// queryPaginationLimit is used in the LIMIT clause of the SQL queries
-	// to limit the number of rows returned.
-	queryPaginationLimit = 100
-)
-
-// InvoiceQueries is an interface that defines the set of operations that can be
-// executed against the invoice database.
-type InvoiceQueries interface { //nolint:interfacebloat
-	InsertInvoice(ctx context.Context, arg sqlc.InsertInvoiceParams) (int64,
-		error)
-
-	InsertInvoiceFeature(ctx context.Context,
-		arg sqlc.InsertInvoiceFeatureParams) error
-
-	InsertInvoiceHTLC(ctx context.Context,
-		arg sqlc.InsertInvoiceHTLCParams) (int64, error)
-
-	InsertInvoiceHTLCCustomRecord(ctx context.Context,
-		arg sqlc.InsertInvoiceHTLCCustomRecordParams) error
-
-	FilterInvoices(ctx context.Context,
-		arg sqlc.FilterInvoicesParams) ([]sqlc.Invoice, error)
-
-	GetInvoice(ctx context.Context,
-		arg sqlc.GetInvoiceParams) ([]sqlc.Invoice, error)
-
-	GetInvoiceFeatures(ctx context.Context,
-		invoiceID int64) ([]sqlc.InvoiceFeature, error)
-
-	GetInvoiceHTLCCustomRecords(ctx context.Context,
-		invoiceID int64) ([]sqlc.GetInvoiceHTLCCustomRecordsRow, error)
-
-	GetInvoiceHTLCs(ctx context.Context,
-		invoiceID int64) ([]sqlc.InvoiceHtlc, error)
-
-	UpdateInvoiceState(ctx context.Context,
-		arg sqlc.UpdateInvoiceStateParams) (sql.Result, error)
-
-	UpdateInvoiceAmountPaid(ctx context.Context,
-		arg sqlc.UpdateInvoiceAmountPaidParams) (sql.Result, error)
-
-	NextInvoiceSettleIndex(ctx context.Context) (int64, error)
-
-	UpdateInvoiceHTLC(ctx context.Context,
-		arg sqlc.UpdateInvoiceHTLCParams) error
-
-	DeleteInvoice(ctx context.Context, arg sqlc.DeleteInvoiceParams) (
-		sql.Result, error)
-
-	DeleteCanceledInvoices(ctx context.Context) (sql.Result, error)
-
-	// AMP sub invoice specific methods.
-	UpsertAMPSubInvoice(ctx context.Context,
-		arg sqlc.UpsertAMPSubInvoiceParams) (sql.Result, error)
-
-	UpdateAMPSubInvoiceState(ctx context.Context,
-		arg sqlc.UpdateAMPSubInvoiceStateParams) error
-
-	InsertAMPSubInvoiceHTLC(ctx context.Context,
-		arg sqlc.InsertAMPSubInvoiceHTLCParams) error
-
-	FetchAMPSubInvoices(ctx context.Context,
-		arg sqlc.FetchAMPSubInvoicesParams) ([]sqlc.AmpSubInvoice,
-		error)
-
-	FetchAMPSubInvoiceHTLCs(ctx context.Context,
-		arg sqlc.FetchAMPSubInvoiceHTLCsParams) (
-		[]sqlc.FetchAMPSubInvoiceHTLCsRow, error)
-
-	FetchSettledAMPSubInvoices(ctx context.Context,
-		arg sqlc.FetchSettledAMPSubInvoicesParams) (
-		[]sqlc.FetchSettledAMPSubInvoicesRow, error)
-
-	UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context,
-		arg sqlc.UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result,
-		error)
-
-	// Invoice events specific methods.
-	OnInvoiceCreated(ctx context.Context,
-		arg sqlc.OnInvoiceCreatedParams) error
-
-	OnInvoiceCanceled(ctx context.Context,
-		arg sqlc.OnInvoiceCanceledParams) error
-
-	OnInvoiceSettled(ctx context.Context,
-		arg sqlc.OnInvoiceSettledParams) error
-
-	OnAMPSubInvoiceCreated(ctx context.Context,
-		arg sqlc.OnAMPSubInvoiceCreatedParams) error
-
-	OnAMPSubInvoiceCanceled(ctx context.Context,
-		arg sqlc.OnAMPSubInvoiceCanceledParams) error
-
-	OnAMPSubInvoiceSettled(ctx context.Context,
-		arg sqlc.OnAMPSubInvoiceSettledParams) error
-}
-
-var _ invpkg.InvoiceDB = (*InvoiceStore)(nil)
-
-// InvoiceQueriesTxOptions defines the set of db txn options the InvoiceQueries
-// understands.
-type InvoiceQueriesTxOptions struct {
-	// readOnly governs if a read only transaction is needed or not.
-	readOnly bool
-}
-
-// ReadOnly returns true if the transaction should be read only.
-//
-// NOTE: This implements the TxOptions.
-func (a *InvoiceQueriesTxOptions) ReadOnly() bool {
-	return a.readOnly
-}
-
-// NewInvoiceQueryReadTx creates a new read transaction option set.
-func NewInvoiceQueryReadTx() InvoiceQueriesTxOptions {
-	return InvoiceQueriesTxOptions{
-		readOnly: true,
-	}
-}
-
-// BatchedInvoiceQueries is a version of the InvoiceQueries that's capable of
-// batched database operations.
-type BatchedInvoiceQueries interface {
-	InvoiceQueries
-
-	BatchedTx[InvoiceQueries]
-}
-
-// InvoiceStore represents a storage backend.
-type InvoiceStore struct {
-	db    BatchedInvoiceQueries
-	clock clock.Clock
-}
-
-// NewInvoiceStore creates a new InvoiceStore instance given a open
-// BatchedInvoiceQueries storage backend.
-func NewInvoiceStore(db BatchedInvoiceQueries,
-	clock clock.Clock) *InvoiceStore {
-
-	return &InvoiceStore{
-		db:    db,
-		clock: clock,
-	}
-}
-
-// AddInvoice inserts the targeted invoice into the database. If the invoice has
-// *any* payment hashes which already exists within the database, then the
-// insertion will be aborted and rejected due to the strict policy banning any
-// duplicate payment hashes.
-//
-// NOTE: A side effect of this function is that it sets AddIndex on newInvoice.
-func (i *InvoiceStore) AddInvoice(ctx context.Context,
-	newInvoice *invpkg.Invoice, paymentHash lntypes.Hash) (uint64, error) {
-
-	// Make sure this is a valid invoice before trying to store it in our
-	// DB.
-	if err := invpkg.ValidateInvoice(newInvoice, paymentHash); err != nil {
-		return 0, err
-	}
-
-	var (
-		writeTxOpts InvoiceQueriesTxOptions
-		invoiceID   int64
-	)
-
-	// Precompute the payment request hash so we can use it in the query.
-	var paymentRequestHash []byte
-	if len(newInvoice.PaymentRequest) > 0 {
-		h := sha256.New()
-		h.Write(newInvoice.PaymentRequest)
-		paymentRequestHash = h.Sum(nil)
-	}
-
-	err := i.db.ExecTx(ctx, &writeTxOpts, func(db InvoiceQueries) error {
-		params := sqlc.InsertInvoiceParams{
-			Hash:       paymentHash[:],
-			Memo:       sqlStr(string(newInvoice.Memo)),
-			AmountMsat: int64(newInvoice.Terms.Value),
-			// Note: BOLT12 invoices don't have a final cltv delta.
-			CltvDelta: sqlInt32(newInvoice.Terms.FinalCltvDelta),
-			Expiry:    int32(newInvoice.Terms.Expiry),
-			// Note: keysend invoices don't have a payment request.
-			PaymentRequest: sqlStr(string(
-				newInvoice.PaymentRequest),
-			),
-			PaymentRequestHash: paymentRequestHash,
-			State:              int16(newInvoice.State),
-			AmountPaidMsat:     int64(newInvoice.AmtPaid),
-			IsAmp:              newInvoice.IsAMP(),
-			IsHodl:             newInvoice.HodlInvoice,
-			IsKeysend:          newInvoice.IsKeysend(),
-			CreatedAt:          newInvoice.CreationDate.UTC(),
-		}
-
-		// Some invoices may not have a preimage, like in the case of
-		// HODL invoices.
-		if newInvoice.Terms.PaymentPreimage != nil {
-			preimage := *newInvoice.Terms.PaymentPreimage
-			if preimage == invpkg.UnknownPreimage {
-				return errors.New("cannot use all-zeroes " +
-					"preimage")
-			}
-			params.Preimage = preimage[:]
-		}
-
-		// Some non MPP payments may have the default (invalid) value.
-		if newInvoice.Terms.PaymentAddr != invpkg.BlankPayAddr {
-			params.PaymentAddr = newInvoice.Terms.PaymentAddr[:]
-		}
-
-		var err error
-		invoiceID, err = db.InsertInvoice(ctx, params)
-		if err != nil {
-			return fmt.Errorf("unable to insert invoice: %w", err)
-		}
-
-		// TODO(positiveblue): if invocies do not have custom features
-		// maybe just store the "invoice type" and populate the features
-		// based on that.
-		for feature := range newInvoice.Terms.Features.Features() {
-			params := sqlc.InsertInvoiceFeatureParams{
-				InvoiceID: invoiceID,
-				Feature:   int32(feature),
-			}
-
-			err := db.InsertInvoiceFeature(ctx, params)
-			if err != nil {
-				return fmt.Errorf("unable to insert invoice "+
-					"feature(%v): %w", feature, err)
-			}
-		}
-
-		// Finally add a new event for this invoice.
-		return db.OnInvoiceCreated(ctx, sqlc.OnInvoiceCreatedParams{
-			AddedAt:   newInvoice.CreationDate.UTC(),
-			InvoiceID: invoiceID,
-		})
-	})
-	if err != nil {
-		mappedSQLErr := MapSQLError(err)
-		var uniqueConstraintErr *ErrSQLUniqueConstraintViolation
-		if errors.As(mappedSQLErr, &uniqueConstraintErr) {
-			// Add context to unique constraint errors.
-			return 0, invpkg.ErrDuplicateInvoice
-		}
-
-		return 0, fmt.Errorf("unable to add invoice(%v): %w",
-			paymentHash, err)
-	}
-
-	newInvoice.AddIndex = uint64(invoiceID)
-
-	return newInvoice.AddIndex, nil
-}
-
-// fetchInvoice fetches the common invoice data and the AMP state for the
-// invoice with the given reference.
-func (i *InvoiceStore) fetchInvoice(ctx context.Context,
-	db InvoiceQueries, ref invpkg.InvoiceRef) (*invpkg.Invoice, error) {
-
-	if ref.PayHash() == nil && ref.PayAddr() == nil && ref.SetID() == nil {
-		return nil, invpkg.ErrInvoiceNotFound
-	}
-
-	var (
-		invoice *invpkg.Invoice
-		params  sqlc.GetInvoiceParams
-	)
-
-	// Given all invoices are uniquely identified by their payment hash,
-	// we can use it to query a specific invoice.
-	if ref.PayHash() != nil {
-		params.Hash = ref.PayHash()[:]
-	}
-
-	// Newer invoices (0.11 and up) are indexed by payment address in
-	// addition to payment hash, but pre 0.8 invoices do not have one at
-	// all. Only allow lookups for payment address if it is not a blank
-	// payment address, which is a special-cased value for legacy keysend
-	// invoices.
-	if ref.PayAddr() != nil && *ref.PayAddr() != invpkg.BlankPayAddr {
-		params.PaymentAddr = ref.PayAddr()[:]
-	}
-
-	// If the reference has a set ID we'll fetch the invoice which has the
-	// corresponding AMP sub invoice.
-	if ref.SetID() != nil {
-		params.SetID = ref.SetID()[:]
-	}
-
-	rows, err := db.GetInvoice(ctx, params)
-	switch {
-	case len(rows) == 0:
-		return nil, invpkg.ErrInvoiceNotFound
-
-	case len(rows) > 1:
-		// In case the reference is ambiguous, meaning it matches more
-		// than	one invoice, we'll return an error.
-		return nil, fmt.Errorf("ambiguous invoice ref: %s",
-			ref.String())
-
-	case err != nil:
-		return nil, fmt.Errorf("unable to fetch invoice: %w", err)
-	}
-
-	var (
-		setID         *[32]byte
-		fetchAmpHtlcs bool
-	)
-
-	// Now that we got the invoice itself, fetch the HTLCs as requested by
-	// the modifier.
-	switch ref.Modifier() {
-	case invpkg.DefaultModifier:
-		// By default we'll fetch all AMP HTLCs.
-		setID = nil
-		fetchAmpHtlcs = true
-
-	case invpkg.HtlcSetOnlyModifier:
-		// In this case we'll fetch all AMP HTLCs for the
-		// specified set id.
-		if ref.SetID() == nil {
-			return nil, fmt.Errorf("set ID is required to use " +
-				"the HTLC set only modifier")
-		}
-
-		setID = ref.SetID()
-		fetchAmpHtlcs = true
-
-	case invpkg.HtlcSetBlankModifier:
-		// No need to fetch any HTLCs.
-		setID = nil
-		fetchAmpHtlcs = false
-
-	default:
-		return nil, fmt.Errorf("unknown invoice ref modifier: %v",
-			ref.Modifier())
-	}
-
-	// Fetch the rest of the invoice data and fill the invoice struct.
-	_, invoice, err = fetchInvoiceData(
-		ctx, db, rows[0], setID, fetchAmpHtlcs,
-	)
-	if err != nil {
-		return nil, err
-	}
-
-	return invoice, nil
-}
-
-// fetchAmpState fetches the AMP state for the invoice with the given ID.
-// Optional setID can be provided to fetch the state for a specific AMP HTLC
-// set. If setID is nil then we'll fetch the state for all AMP sub invoices. If
-// fetchHtlcs is set to true, the HTLCs for the given set will be fetched as
-// well.
-//
-//nolint:funlen
-func fetchAmpState(ctx context.Context, db InvoiceQueries, invoiceID int64,
-	setID *[32]byte, fetchHtlcs bool) (invpkg.AMPInvoiceState,
-	invpkg.HTLCSet, error) {
-
-	var paramSetID []byte
-	if setID != nil {
-		paramSetID = setID[:]
-	}
-
-	// First fetch all the AMP sub invoices for this invoice or the one
-	// matching the provided set ID.
-	ampInvoiceRows, err := db.FetchAMPSubInvoices(
-		ctx, sqlc.FetchAMPSubInvoicesParams{
-			InvoiceID: invoiceID,
-			SetID:     paramSetID,
-		},
-	)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	ampState := make(map[invpkg.SetID]invpkg.InvoiceStateAMP)
-	for _, row := range ampInvoiceRows {
-		var rowSetID [32]byte
-
-		if len(row.SetID) != 32 {
-			return nil, nil, fmt.Errorf("invalid set id length: %d",
-				len(row.SetID))
-		}
-
-		var settleDate time.Time
-		if row.SettledAt.Valid {
-			settleDate = row.SettledAt.Time.Local()
-		}
-
-		copy(rowSetID[:], row.SetID)
-		ampState[rowSetID] = invpkg.InvoiceStateAMP{
-			State:       invpkg.HtlcState(row.State),
-			SettleIndex: uint64(row.SettleIndex.Int64),
-			SettleDate:  settleDate,
-			InvoiceKeys: make(map[models.CircuitKey]struct{}),
-		}
-	}
-
-	if !fetchHtlcs {
-		return ampState, nil, nil
-	}
-
-	customRecordRows, err := db.GetInvoiceHTLCCustomRecords(ctx, invoiceID)
-	if err != nil {
-		return nil, nil, fmt.Errorf("unable to get custom records for "+
-			"invoice HTLCs: %w", err)
-	}
-
-	customRecords := make(map[int64]record.CustomSet, len(customRecordRows))
-	for _, row := range customRecordRows {
-		if _, ok := customRecords[row.HtlcID]; !ok {
-			customRecords[row.HtlcID] = make(record.CustomSet)
-		}
-
-		value := row.Value
-		if value == nil {
-			value = []byte{}
-		}
-
-		customRecords[row.HtlcID][uint64(row.Key)] = value
-	}
-
-	// Now fetch all the AMP HTLCs for this invoice or the one matching the
-	// provided set ID.
-	ampHtlcRows, err := db.FetchAMPSubInvoiceHTLCs(
-		ctx, sqlc.FetchAMPSubInvoiceHTLCsParams{
-			InvoiceID: invoiceID,
-			SetID:     paramSetID,
-		},
-	)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	ampHtlcs := make(map[models.CircuitKey]*invpkg.InvoiceHTLC)
-	for _, row := range ampHtlcRows {
-		uint64ChanID, err := strconv.ParseUint(row.ChanID, 10, 64)
-		if err != nil {
-			return nil, nil, err
-		}
-
-		chanID := lnwire.NewShortChanIDFromInt(uint64ChanID)
-
-		if row.HtlcID < 0 {
-			return nil, nil, fmt.Errorf("invalid HTLC ID "+
-				"value: %v", row.HtlcID)
-		}
-
-		htlcID := uint64(row.HtlcID)
-
-		circuitKey := invpkg.CircuitKey{
-			ChanID: chanID,
-			HtlcID: htlcID,
-		}
-
-		htlc := &invpkg.InvoiceHTLC{
-			Amt:          lnwire.MilliSatoshi(row.AmountMsat),
-			AcceptHeight: uint32(row.AcceptHeight),
-			AcceptTime:   row.AcceptTime.Local(),
-			Expiry:       uint32(row.ExpiryHeight),
-			State:        invpkg.HtlcState(row.State),
-		}
-
-		if row.TotalMppMsat.Valid {
-			htlc.MppTotalAmt = lnwire.MilliSatoshi(
-				row.TotalMppMsat.Int64,
-			)
-		}
-
-		if row.ResolveTime.Valid {
-			htlc.ResolveTime = row.ResolveTime.Time.Local()
-		}
-
-		var (
-			rootShare [32]byte
-			setID     [32]byte
-		)
-
-		if len(row.RootShare) != 32 {
-			return nil, nil, fmt.Errorf("invalid root share "+
-				"length: %d", len(row.RootShare))
-		}
-		copy(rootShare[:], row.RootShare)
-
-		if len(row.SetID) != 32 {
-			return nil, nil, fmt.Errorf("invalid set ID length: %d",
-				len(row.SetID))
-		}
-		copy(setID[:], row.SetID)
-
-		if row.ChildIndex < 0 || row.ChildIndex > math.MaxUint32 {
-			return nil, nil, fmt.Errorf("invalid child index "+
-				"value: %v", row.ChildIndex)
-		}
-
-		ampRecord := record.NewAMP(
-			rootShare, setID, uint32(row.ChildIndex),
-		)
-
-		htlc.AMP = &invpkg.InvoiceHtlcAMPData{
-			Record: *ampRecord,
-		}
-
-		if len(row.Hash) != 32 {
-			return nil, nil, fmt.Errorf("invalid hash length: %d",
-				len(row.Hash))
-		}
-		copy(htlc.AMP.Hash[:], row.Hash)
-
-		if row.Preimage != nil {
-			preimage, err := lntypes.MakePreimage(row.Preimage)
-			if err != nil {
-				return nil, nil, err
-			}
-
-			htlc.AMP.Preimage = &preimage
-		}
-
-		if _, ok := customRecords[row.ID]; ok {
-			htlc.CustomRecords = customRecords[row.ID]
-		} else {
-			htlc.CustomRecords = make(record.CustomSet)
-		}
-
-		ampHtlcs[circuitKey] = htlc
-	}
-
-	if len(ampHtlcs) > 0 {
-		for setID := range ampState {
-			var amtPaid lnwire.MilliSatoshi
-			invoiceKeys := make(
-				map[models.CircuitKey]struct{},
-			)
-
-			for key, htlc := range ampHtlcs {
-				if htlc.AMP.Record.SetID() != setID {
-					continue
-				}
-
-				invoiceKeys[key] = struct{}{}
-
-				if htlc.State != invpkg.HtlcStateCanceled { //nolint: lll
-					amtPaid += htlc.Amt
-				}
-			}
-
-			setState := ampState[setID]
-			setState.InvoiceKeys = invoiceKeys
-			setState.AmtPaid = amtPaid
-			ampState[setID] = setState
-		}
-	}
-
-	return ampState, ampHtlcs, nil
-}
-
-// LookupInvoice attempts to look up an invoice corresponding the passed in
-// reference. The reference may be a payment hash, a payment address, or a set
-// ID for an AMP sub invoice. If the invoice is found, we'll return the complete
-// invoice. If the invoice is not found, then we'll return an ErrInvoiceNotFound
-// error.
-func (i *InvoiceStore) LookupInvoice(ctx context.Context,
-	ref invpkg.InvoiceRef) (invpkg.Invoice, error) {
-
-	var (
-		invoice *invpkg.Invoice
-		err     error
-	)
-
-	readTxOpt := NewInvoiceQueryReadTx()
-	txErr := i.db.ExecTx(ctx, &readTxOpt, func(db InvoiceQueries) error {
-		invoice, err = i.fetchInvoice(ctx, db, ref)
-
-		return err
-	})
-	if txErr != nil {
-		return invpkg.Invoice{}, txErr
-	}
-
-	return *invoice, nil
-}
-
-// FetchPendingInvoices returns all the invoices that are currently in a
-// "pending" state. An invoice is pending if it has been created but not yet
-// settled or canceled.
-func (i *InvoiceStore) FetchPendingInvoices(ctx context.Context) (
-	map[lntypes.Hash]invpkg.Invoice, error) {
-
-	invoices := make(map[lntypes.Hash]invpkg.Invoice)
-
-	readTxOpt := NewInvoiceQueryReadTx()
-	err := i.db.ExecTx(ctx, &readTxOpt, func(db InvoiceQueries) error {
-		limit := queryPaginationLimit
-
-		return queryWithLimit(func(offset int) (int, error) {
-			params := sqlc.FilterInvoicesParams{
-				PendingOnly: true,
-				NumOffset:   int32(offset),
-				NumLimit:    int32(limit),
-			}
-
-			rows, err := db.FilterInvoices(ctx, params)
-			if err != nil && !errors.Is(err, sql.ErrNoRows) {
-				return 0, fmt.Errorf("unable to get invoices "+
-					"from db: %w", err)
-			}
-
-			// Load all the information for the invoices.
-			for _, row := range rows {
-				hash, invoice, err := fetchInvoiceData(
-					ctx, db, row, nil, true,
-				)
-				if err != nil {
-					return 0, err
-				}
-
-				invoices[*hash] = *invoice
-			}
-
-			return len(rows), nil
-		}, limit)
-	})
-	if err != nil {
-		return nil, fmt.Errorf("unable to fetch pending invoices: %w",
-			err)
-	}
-
-	return invoices, nil
-}
-
-// InvoicesSettledSince can be used by callers to catch up any settled invoices
-// they missed within the settled invoice time series. We'll return all known
-// settled invoice that have a settle index higher than the passed
-// sinceSettleIndex.
-//
-// NOTE: The index starts from 1. As a result we enforce that specifying a value
-// below the starting index value is a noop.
-func (i *InvoiceStore) InvoicesSettledSince(ctx context.Context, idx uint64) (
-	[]invpkg.Invoice, error) {
-
-	var invoices []invpkg.Invoice
-
-	if idx == 0 {
-		return invoices, nil
-	}
-
-	readTxOpt := NewInvoiceQueryReadTx()
-	err := i.db.ExecTx(ctx, &readTxOpt, func(db InvoiceQueries) error {
-		settleIdx := idx
-		limit := queryPaginationLimit
-
-		err := queryWithLimit(func(offset int) (int, error) {
-			params := sqlc.FilterInvoicesParams{
-				SettleIndexGet: sqlInt64(settleIdx + 1),
-				NumLimit:       int32(limit),
-				NumOffset:      int32(offset),
-			}
-
-			rows, err := db.FilterInvoices(ctx, params)
-			if err != nil && !errors.Is(err, sql.ErrNoRows) {
-				return 0, fmt.Errorf("unable to get invoices "+
-					"from db: %w", err)
-			}
-
-			// Load all the information for the invoices.
-			for _, row := range rows {
-				_, invoice, err := fetchInvoiceData(
-					ctx, db, row, nil, true,
-				)
-				if err != nil {
-					return 0, fmt.Errorf("unable to fetch "+
-						"invoice(id=%d) from db: %w",
-						row.ID, err)
-				}
-
-				invoices = append(invoices, *invoice)
-			}
-
-			settleIdx += uint64(limit)
-
-			return len(rows), nil
-		}, limit)
-		if err != nil {
-			return err
-		}
-
-		// Now fetch all the AMP sub invoices that were settled since
-		// the provided index.
-		ampInvoices, err := i.db.FetchSettledAMPSubInvoices(
-			ctx, sqlc.FetchSettledAMPSubInvoicesParams{
-				SettleIndexGet: sqlInt64(idx + 1),
-			},
-		)
-		if err != nil {
-			return err
-		}
-
-		for _, ampInvoice := range ampInvoices {
-			// Convert the row to a sqlc.Invoice so we can use the
-			// existing fetchInvoiceData function.
-			sqlInvoice := sqlc.Invoice{
-				ID:             ampInvoice.ID,
-				Hash:           ampInvoice.Hash,
-				Preimage:       ampInvoice.Preimage,
-				SettleIndex:    ampInvoice.AmpSettleIndex,
-				SettledAt:      ampInvoice.AmpSettledAt,
-				Memo:           ampInvoice.Memo,
-				AmountMsat:     ampInvoice.AmountMsat,
-				CltvDelta:      ampInvoice.CltvDelta,
-				Expiry:         ampInvoice.Expiry,
-				PaymentAddr:    ampInvoice.PaymentAddr,
-				PaymentRequest: ampInvoice.PaymentRequest,
-				State:          ampInvoice.State,
-				AmountPaidMsat: ampInvoice.AmountPaidMsat,
-				IsAmp:          ampInvoice.IsAmp,
-				IsHodl:         ampInvoice.IsHodl,
-				IsKeysend:      ampInvoice.IsKeysend,
-				CreatedAt:      ampInvoice.CreatedAt.UTC(),
-			}
-
-			// Fetch the state and HTLCs for this AMP sub invoice.
-			_, invoice, err := fetchInvoiceData(
-				ctx, db, sqlInvoice,
-				(*[32]byte)(ampInvoice.SetID), true,
-			)
-			if err != nil {
-				return fmt.Errorf("unable to fetch "+
-					"AMP invoice(id=%d) from db: %w",
-					ampInvoice.ID, err)
-			}
-
-			invoices = append(invoices, *invoice)
-		}
-
-		return nil
-	})
-	if err != nil {
-		return nil, fmt.Errorf("unable to get invoices settled since "+
-			"index (excluding) %d: %w", idx, err)
-	}
-
-	return invoices, nil
-}
-
-// InvoicesAddedSince can be used by callers to seek into the event time series
-// of all the invoices added in the database. This method will return all
-// invoices with an add index greater than the specified sinceAddIndex.
-//
-// NOTE: The index starts from 1. As a result we enforce that specifying a value
-// below the starting index value is a noop.
-func (i *InvoiceStore) InvoicesAddedSince(ctx context.Context, idx uint64) (
-	[]invpkg.Invoice, error) {
-
-	var result []invpkg.Invoice
-
-	if idx == 0 {
-		return result, nil
-	}
-
-	readTxOpt := NewInvoiceQueryReadTx()
-	err := i.db.ExecTx(ctx, &readTxOpt, func(db InvoiceQueries) error {
-		addIdx := idx
-		limit := queryPaginationLimit
-
-		return queryWithLimit(func(offset int) (int, error) {
-			params := sqlc.FilterInvoicesParams{
-				AddIndexGet: sqlInt64(addIdx + 1),
-				NumLimit:    int32(limit),
-				NumOffset:   int32(offset),
-			}
-
-			rows, err := db.FilterInvoices(ctx, params)
-			if err != nil && !errors.Is(err, sql.ErrNoRows) {
-				return 0, fmt.Errorf("unable to get invoices "+
-					"from db: %w", err)
-			}
-
-			// Load all the information for the invoices.
-			for _, row := range rows {
-				_, invoice, err := fetchInvoiceData(
-					ctx, db, row, nil, true,
-				)
-				if err != nil {
-					return 0, err
-				}
-
-				result = append(result, *invoice)
-			}
-
-			addIdx += uint64(limit)
-
-			return len(rows), nil
-		}, limit)
-	})
-
-	if err != nil {
-		return nil, fmt.Errorf("unable to get invoices added since "+
-			"index %d: %w", idx, err)
-	}
-
-	return result, nil
-}
-
-// QueryInvoices allows a caller to query the invoice database for invoices
-// within the specified add index range.
-func (i *InvoiceStore) QueryInvoices(ctx context.Context,
-	q invpkg.InvoiceQuery) (invpkg.InvoiceSlice, error) {
-
-	var invoices []invpkg.Invoice
-
-	if q.NumMaxInvoices == 0 {
-		return invpkg.InvoiceSlice{}, fmt.Errorf("max invoices must " +
-			"be non-zero")
-	}
-
-	readTxOpt := NewInvoiceQueryReadTx()
-	err := i.db.ExecTx(ctx, &readTxOpt, func(db InvoiceQueries) error {
-		limit := queryPaginationLimit
-
-		return queryWithLimit(func(offset int) (int, error) {
-			params := sqlc.FilterInvoicesParams{
-				NumOffset:   int32(offset),
-				NumLimit:    int32(limit),
-				PendingOnly: q.PendingOnly,
-			}
-
-			if !q.Reversed {
-				// The invoice with index offset id must not be
-				// included in the results.
-				params.AddIndexGet = sqlInt64(
-					q.IndexOffset + uint64(offset) + 1,
-				)
-			}
-
-			if q.Reversed {
-				idx := int32(q.IndexOffset)
-
-				// If the index offset was not set, we want to
-				// fetch from the lastest invoice.
-				if idx == 0 {
-					params.AddIndexLet = sqlInt64(
-						int64(math.MaxInt64),
-					)
-				} else {
-					// The invoice with index offset id must
-					// not be included in the results.
-					params.AddIndexLet = sqlInt64(
-						idx - int32(offset) - 1,
-					)
-				}
-
-				params.Reverse = true
-			}
-
-			if q.CreationDateStart != 0 {
-				params.CreatedAfter = sqlTime(
-					time.Unix(q.CreationDateStart, 0).UTC(),
-				)
-			}
-
-			if q.CreationDateEnd != 0 {
-				params.CreatedBefore = sqlTime(
-					time.Unix(q.CreationDateEnd, 0).UTC(),
-				)
-			}
-
-			rows, err := db.FilterInvoices(ctx, params)
-			if err != nil && !errors.Is(err, sql.ErrNoRows) {
-				return 0, fmt.Errorf("unable to get invoices "+
-					"from db: %w", err)
-			}
-
-			// Load all the information for the invoices.
-			for _, row := range rows {
-				_, invoice, err := fetchInvoiceData(
-					ctx, db, row, nil, true,
-				)
-				if err != nil {
-					return 0, err
-				}
-
-				invoices = append(invoices, *invoice)
-
-				if len(invoices) == int(q.NumMaxInvoices) {
-					return 0, nil
-				}
-			}
-
-			return len(rows), nil
-		}, limit)
-	})
-	if err != nil {
-		return invpkg.InvoiceSlice{}, fmt.Errorf("unable to query "+
-			"invoices: %w", err)
-	}
-
-	if len(invoices) == 0 {
-		return invpkg.InvoiceSlice{
-			InvoiceQuery: q,
-		}, nil
-	}
-
-	// If we iterated through the add index in reverse order, then
-	// we'll need to reverse the slice of invoices to return them in
-	// forward order.
-	if q.Reversed {
-		numInvoices := len(invoices)
-		for i := 0; i < numInvoices/2; i++ {
-			reverse := numInvoices - i - 1
-			invoices[i], invoices[reverse] =
-				invoices[reverse], invoices[i]
-		}
-	}
-
-	res := invpkg.InvoiceSlice{
-		InvoiceQuery:     q,
-		Invoices:         invoices,
-		FirstIndexOffset: invoices[0].AddIndex,
-		LastIndexOffset:  invoices[len(invoices)-1].AddIndex,
-	}
-
-	return res, nil
-}
-
-// sqlInvoiceUpdater is the implementation of the InvoiceUpdater interface using
-// a SQL database as the backend.
-type sqlInvoiceUpdater struct {
-	db         InvoiceQueries
-	ctx        context.Context //nolint:containedctx
-	invoice    *invpkg.Invoice
-	updateTime time.Time
-}
-
-// AddHtlc adds a new htlc to the invoice.
-func (s *sqlInvoiceUpdater) AddHtlc(circuitKey models.CircuitKey,
-	newHtlc *invpkg.InvoiceHTLC) error {
-
-	htlcPrimaryKeyID, err := s.db.InsertInvoiceHTLC(
-		s.ctx, sqlc.InsertInvoiceHTLCParams{
-			HtlcID: int64(circuitKey.HtlcID),
-			ChanID: strconv.FormatUint(
-				circuitKey.ChanID.ToUint64(), 10,
-			),
-			AmountMsat: int64(newHtlc.Amt),
-			TotalMppMsat: sql.NullInt64{
-				Int64: int64(newHtlc.MppTotalAmt),
-				Valid: newHtlc.MppTotalAmt != 0,
-			},
-			AcceptHeight: int32(newHtlc.AcceptHeight),
-			AcceptTime:   newHtlc.AcceptTime.UTC(),
-			ExpiryHeight: int32(newHtlc.Expiry),
-			State:        int16(newHtlc.State),
-			InvoiceID:    int64(s.invoice.AddIndex),
-		},
-	)
-	if err != nil {
-		return err
-	}
-
-	for key, value := range newHtlc.CustomRecords {
-		err = s.db.InsertInvoiceHTLCCustomRecord(
-			s.ctx, sqlc.InsertInvoiceHTLCCustomRecordParams{
-				// TODO(bhandras): schema might be wrong here
-				// as the custom record key is an uint64.
-				Key:    int64(key),
-				Value:  value,
-				HtlcID: htlcPrimaryKeyID,
-			},
-		)
-		if err != nil {
-			return err
-		}
-	}
-
-	if newHtlc.AMP != nil {
-		setID := newHtlc.AMP.Record.SetID()
-
-		upsertResult, err := s.db.UpsertAMPSubInvoice(
-			s.ctx, sqlc.UpsertAMPSubInvoiceParams{
-				SetID:     setID[:],
-				CreatedAt: s.updateTime.UTC(),
-				InvoiceID: int64(s.invoice.AddIndex),
-			},
-		)
-		if err != nil {
-			mappedSQLErr := MapSQLError(err)
-			var uniqueConstraintErr *ErrSQLUniqueConstraintViolation
-			if errors.As(mappedSQLErr, &uniqueConstraintErr) {
-				return invpkg.ErrDuplicateSetID{
-					SetID: setID,
-				}
-			}
-
-			return err
-		}
-
-		// If we're just inserting the AMP invoice, we'll get a non
-		// zero rows affected count.
-		rowsAffected, err := upsertResult.RowsAffected()
-		if err != nil {
-			return err
-		}
-		if rowsAffected != 0 {
-			// If we're inserting a new AMP invoice, we'll also
-			// insert a new invoice event.
-			err = s.db.OnAMPSubInvoiceCreated(
-				s.ctx, sqlc.OnAMPSubInvoiceCreatedParams{
-					AddedAt:   s.updateTime.UTC(),
-					InvoiceID: int64(s.invoice.AddIndex),
-					SetID:     setID[:],
-				},
-			)
-			if err != nil {
-				return err
-			}
-		}
-
-		rootShare := newHtlc.AMP.Record.RootShare()
-
-		ampHtlcParams := sqlc.InsertAMPSubInvoiceHTLCParams{
-			InvoiceID: int64(s.invoice.AddIndex),
-			SetID:     setID[:],
-			HtlcID:    htlcPrimaryKeyID,
-			RootShare: rootShare[:],
-			ChildIndex: int64(
-				newHtlc.AMP.Record.ChildIndex(),
-			),
-			Hash: newHtlc.AMP.Hash[:],
-		}
-
-		if newHtlc.AMP.Preimage != nil {
-			ampHtlcParams.Preimage = newHtlc.AMP.Preimage[:]
-		}
-
-		err = s.db.InsertAMPSubInvoiceHTLC(s.ctx, ampHtlcParams)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// ResolveHtlc marks an htlc as resolved with the given state.
-func (s *sqlInvoiceUpdater) ResolveHtlc(circuitKey models.CircuitKey,
-	state invpkg.HtlcState, resolveTime time.Time) error {
-
-	return s.db.UpdateInvoiceHTLC(s.ctx, sqlc.UpdateInvoiceHTLCParams{
-		HtlcID: int64(circuitKey.HtlcID),
-		ChanID: strconv.FormatUint(
-			circuitKey.ChanID.ToUint64(), 10,
-		),
-		InvoiceID:   int64(s.invoice.AddIndex),
-		State:       int16(state),
-		ResolveTime: sqlTime(resolveTime.UTC()),
-	})
-}
-
-// AddAmpHtlcPreimage adds a preimage of an AMP htlc to the AMP sub invoice
-// identified by the setID.
-func (s *sqlInvoiceUpdater) AddAmpHtlcPreimage(setID [32]byte,
-	circuitKey models.CircuitKey, preimage lntypes.Preimage) error {
-
-	result, err := s.db.UpdateAMPSubInvoiceHTLCPreimage(
-		s.ctx, sqlc.UpdateAMPSubInvoiceHTLCPreimageParams{
-			InvoiceID: int64(s.invoice.AddIndex),
-			SetID:     setID[:],
-			HtlcID:    int64(circuitKey.HtlcID),
-			Preimage:  preimage[:],
-		},
-	)
-	if err != nil {
-		return err
-	}
-
-	rowsAffected, err := result.RowsAffected()
-	if err != nil {
-		return err
-	}
-	if rowsAffected == 0 {
-		return invpkg.ErrInvoiceNotFound
-	}
-
-	return nil
-}
-
-// UpdateInvoiceState updates the invoice state to the new state.
-func (s *sqlInvoiceUpdater) UpdateInvoiceState(
-	newState invpkg.ContractState, preimage *lntypes.Preimage) error {
-
-	var (
-		settleIndex sql.NullInt64
-		settledAt   sql.NullTime
-	)
-
-	switch newState {
-	case invpkg.ContractSettled:
-		nextSettleIndex, err := s.db.NextInvoiceSettleIndex(s.ctx)
-		if err != nil {
-			return err
-		}
-
-		settleIndex = sqlInt64(nextSettleIndex)
-
-		// If the invoice is settled, we'll also update the settle time.
-		settledAt = sqlTime(s.updateTime.UTC())
-
-		err = s.db.OnInvoiceSettled(
-			s.ctx, sqlc.OnInvoiceSettledParams{
-				AddedAt:   s.updateTime.UTC(),
-				InvoiceID: int64(s.invoice.AddIndex),
-			},
-		)
-		if err != nil {
-			return err
-		}
-
-	case invpkg.ContractCanceled:
-		err := s.db.OnInvoiceCanceled(
-			s.ctx, sqlc.OnInvoiceCanceledParams{
-				AddedAt:   s.updateTime.UTC(),
-				InvoiceID: int64(s.invoice.AddIndex),
-			},
-		)
-		if err != nil {
-			return err
-		}
-	}
-
-	params := sqlc.UpdateInvoiceStateParams{
-		ID:          int64(s.invoice.AddIndex),
-		State:       int16(newState),
-		SettleIndex: settleIndex,
-		SettledAt:   settledAt,
-	}
-
-	if preimage != nil {
-		params.Preimage = preimage[:]
-	}
-
-	result, err := s.db.UpdateInvoiceState(s.ctx, params)
-	if err != nil {
-		return err
-	}
-	rowsAffected, err := result.RowsAffected()
-	if err != nil {
-		return err
-	}
-
-	if rowsAffected == 0 {
-		return invpkg.ErrInvoiceNotFound
-	}
-
-	if settleIndex.Valid {
-		s.invoice.SettleIndex = uint64(settleIndex.Int64)
-		s.invoice.SettleDate = s.updateTime
-	}
-
-	return nil
-}
-
-// UpdateInvoiceAmtPaid updates the invoice amount paid to the new amount.
-func (s *sqlInvoiceUpdater) UpdateInvoiceAmtPaid(
-	amtPaid lnwire.MilliSatoshi) error {
-
-	_, err := s.db.UpdateInvoiceAmountPaid(
-		s.ctx, sqlc.UpdateInvoiceAmountPaidParams{
-			ID:             int64(s.invoice.AddIndex),
-			AmountPaidMsat: int64(amtPaid),
-		},
-	)
-
-	return err
-}
-
-// UpdateAmpState updates the state of the AMP sub invoice identified by the
-// setID.
-func (s *sqlInvoiceUpdater) UpdateAmpState(setID [32]byte,
-	newState invpkg.InvoiceStateAMP, _ models.CircuitKey) error {
-
-	var (
-		settleIndex sql.NullInt64
-		settledAt   sql.NullTime
-	)
-
-	switch newState.State {
-	case invpkg.HtlcStateSettled:
-		nextSettleIndex, err := s.db.NextInvoiceSettleIndex(s.ctx)
-		if err != nil {
-			return err
-		}
-
-		settleIndex = sqlInt64(nextSettleIndex)
-
-		// If the invoice is settled, we'll also update the settle time.
-		settledAt = sqlTime(s.updateTime.UTC())
-
-		err = s.db.OnAMPSubInvoiceSettled(
-			s.ctx, sqlc.OnAMPSubInvoiceSettledParams{
-				AddedAt:   s.updateTime.UTC(),
-				InvoiceID: int64(s.invoice.AddIndex),
-				SetID:     setID[:],
-			},
-		)
-		if err != nil {
-			return err
-		}
-
-	case invpkg.HtlcStateCanceled:
-		err := s.db.OnAMPSubInvoiceCanceled(
-			s.ctx, sqlc.OnAMPSubInvoiceCanceledParams{
-				AddedAt:   s.updateTime.UTC(),
-				InvoiceID: int64(s.invoice.AddIndex),
-				SetID:     setID[:],
-			},
-		)
-		if err != nil {
-			return err
-		}
-	}
-
-	err := s.db.UpdateAMPSubInvoiceState(
-		s.ctx, sqlc.UpdateAMPSubInvoiceStateParams{
-			SetID:       setID[:],
-			State:       int16(newState.State),
-			SettleIndex: settleIndex,
-			SettledAt:   settledAt,
-		},
-	)
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// Finalize finalizes the update before it is written to the database. Note that
-// we don't use this directly in the SQL implementation, so the function is just
-// a stub.
-func (s *sqlInvoiceUpdater) Finalize(_ invpkg.UpdateType) error {
-	return nil
-}
-
-// UpdateInvoice attempts to update an invoice corresponding to the passed
-// reference. If an invoice matching the passed reference doesn't exist within
-// the database, then the action will fail with  ErrInvoiceNotFound error.
-//
-// The update is performed inside the same database transaction that fetches the
-// invoice and is therefore atomic. The fields to update are controlled by the
-// supplied callback.
-func (i *InvoiceStore) UpdateInvoice(ctx context.Context, ref invpkg.InvoiceRef,
-	_ *invpkg.SetID, callback invpkg.InvoiceUpdateCallback) (
-	*invpkg.Invoice, error) {
-
-	var updatedInvoice *invpkg.Invoice
-
-	txOpt := InvoiceQueriesTxOptions{readOnly: false}
-	txErr := i.db.ExecTx(ctx, &txOpt, func(db InvoiceQueries) error {
-		invoice, err := i.fetchInvoice(ctx, db, ref)
-		if err != nil {
-			return err
-		}
-
-		updateTime := i.clock.Now()
-		updater := &sqlInvoiceUpdater{
-			db:         db,
-			ctx:        ctx,
-			invoice:    invoice,
-			updateTime: updateTime,
-		}
-
-		payHash := ref.PayHash()
-		updatedInvoice, err = invpkg.UpdateInvoice(
-			payHash, invoice, updateTime, callback, updater,
-		)
-
-		return err
-	})
-	if txErr != nil {
-		// If the invoice is already settled, we'll return the
-		// (unchanged) invoice and the ErrInvoiceAlreadySettled error.
-		if errors.Is(txErr, invpkg.ErrInvoiceAlreadySettled) {
-			return updatedInvoice, txErr
-		}
-
-		return nil, txErr
-	}
-
-	return updatedInvoice, nil
-}
-
-// DeleteInvoice attempts to delete the passed invoices and all their related
-// data from the database in one transaction.
-func (i *InvoiceStore) DeleteInvoice(ctx context.Context,
-	invoicesToDelete []invpkg.InvoiceDeleteRef) error {
-
-	// All the InvoiceDeleteRef instances include the add index of the
-	// invoice. The rest was added to ensure that the invoices were deleted
-	// properly in the kv database. When we have fully migrated we can
-	// remove the rest of the fields.
-	for _, ref := range invoicesToDelete {
-		if ref.AddIndex == 0 {
-			return fmt.Errorf("unable to delete invoice using a "+
-				"ref without AddIndex set: %v", ref)
-		}
-	}
-
-	var writeTxOpt InvoiceQueriesTxOptions
-	err := i.db.ExecTx(ctx, &writeTxOpt, func(db InvoiceQueries) error {
-		for _, ref := range invoicesToDelete {
-			params := sqlc.DeleteInvoiceParams{
-				AddIndex: sqlInt64(ref.AddIndex),
-			}
-
-			if ref.SettleIndex != 0 {
-				params.SettleIndex = sqlInt64(ref.SettleIndex)
-			}
-
-			if ref.PayHash != lntypes.ZeroHash {
-				params.Hash = ref.PayHash[:]
-			}
-
-			result, err := db.DeleteInvoice(ctx, params)
-			if err != nil {
-				return fmt.Errorf("unable to delete "+
-					"invoice(%v): %w", ref.AddIndex, err)
-			}
-			rowsAffected, err := result.RowsAffected()
-			if err != nil {
-				return fmt.Errorf("unable to get rows "+
-					"affected: %w", err)
-			}
-			if rowsAffected == 0 {
-				return fmt.Errorf("%w: %v",
-					invpkg.ErrInvoiceNotFound, ref.AddIndex)
-			}
-		}
-
-		return nil
-	})
-
-	if err != nil {
-		return fmt.Errorf("unable to delete invoices: %w", err)
-	}
-
-	return nil
-}
-
-// DeleteCanceledInvoices removes all canceled invoices from the database.
-func (i *InvoiceStore) DeleteCanceledInvoices(ctx context.Context) error {
-	var writeTxOpt InvoiceQueriesTxOptions
-	err := i.db.ExecTx(ctx, &writeTxOpt, func(db InvoiceQueries) error {
-		_, err := db.DeleteCanceledInvoices(ctx)
-		if err != nil {
-			return fmt.Errorf("unable to delete canceled "+
-				"invoices: %w", err)
-		}
-
-		return nil
-	})
-	if err != nil {
-		return fmt.Errorf("unable to delete invoices: %w", err)
-	}
-
-	return nil
-}
-
-// fetchInvoiceData fetches additional data for the given invoice. If the
-// invoice is AMP and the setID is not nil, then it will also fetch the AMP
-// state and HTLCs for the given setID, otherwise for all AMP sub invoices of
-// the invoice. If fetchAmpHtlcs is true, it will also fetch the AMP HTLCs.
-func fetchInvoiceData(ctx context.Context, db InvoiceQueries,
-	row sqlc.Invoice, setID *[32]byte, fetchAmpHtlcs bool) (*lntypes.Hash,
-	*invpkg.Invoice, error) {
-
-	// Unmarshal the common data.
-	hash, invoice, err := unmarshalInvoice(row)
-	if err != nil {
-		return nil, nil, fmt.Errorf("unable to unmarshal "+
-			"invoice(id=%d) from db: %w", row.ID, err)
-	}
-
-	// Fetch the invoice features.
-	features, err := getInvoiceFeatures(ctx, db, row.ID)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	invoice.Terms.Features = features
-
-	// If this is an AMP invoice, we'll need fetch the AMP state along
-	// with the HTLCs (if requested).
-	if invoice.IsAMP() {
-		invoiceID := int64(invoice.AddIndex)
-		ampState, ampHtlcs, err := fetchAmpState(
-			ctx, db, invoiceID, setID, fetchAmpHtlcs,
-		)
-		if err != nil {
-			return nil, nil, err
-		}
-
-		invoice.AMPState = ampState
-		invoice.Htlcs = ampHtlcs
-
-		return hash, invoice, nil
-	}
-
-	// Otherwise simply fetch the invoice HTLCs.
-	htlcs, err := getInvoiceHtlcs(ctx, db, row.ID)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if len(htlcs) > 0 {
-		invoice.Htlcs = htlcs
-		var amountPaid lnwire.MilliSatoshi
-		for _, htlc := range htlcs {
-			if htlc.State == invpkg.HtlcStateSettled {
-				amountPaid += htlc.Amt
-			}
-		}
-		invoice.AmtPaid = amountPaid
-	}
-
-	return hash, invoice, nil
-}
-
-// getInvoiceFeatures fetches the invoice features for the given invoice id.
-func getInvoiceFeatures(ctx context.Context, db InvoiceQueries,
-	invoiceID int64) (*lnwire.FeatureVector, error) {
-
-	rows, err := db.GetInvoiceFeatures(ctx, invoiceID)
-	if err != nil {
-		return nil, fmt.Errorf("unable to get invoice features: %w",
-			err)
-	}
-
-	features := lnwire.EmptyFeatureVector()
-	for _, feature := range rows {
-		features.Set(lnwire.FeatureBit(feature.Feature))
-	}
-
-	return features, nil
-}
-
-// getInvoiceHtlcs fetches the invoice htlcs for the given invoice id.
-func getInvoiceHtlcs(ctx context.Context, db InvoiceQueries,
-	invoiceID int64) (map[invpkg.CircuitKey]*invpkg.InvoiceHTLC, error) {
-
-	htlcRows, err := db.GetInvoiceHTLCs(ctx, invoiceID)
-	if err != nil {
-		return nil, fmt.Errorf("unable to get invoice htlcs: %w", err)
-	}
-
-	// We have no htlcs to unmarshal.
-	if len(htlcRows) == 0 {
-		return nil, nil
-	}
-
-	crRows, err := db.GetInvoiceHTLCCustomRecords(ctx, invoiceID)
-	if err != nil {
-		return nil, fmt.Errorf("unable to get custom records for "+
-			"invoice htlcs: %w", err)
-	}
-
-	cr := make(map[int64]record.CustomSet, len(crRows))
-	for _, row := range crRows {
-		if _, ok := cr[row.HtlcID]; !ok {
-			cr[row.HtlcID] = make(record.CustomSet)
-		}
-
-		value := row.Value
-		if value == nil {
-			value = []byte{}
-		}
-		cr[row.HtlcID][uint64(row.Key)] = value
-	}
-
-	htlcs := make(map[invpkg.CircuitKey]*invpkg.InvoiceHTLC, len(htlcRows))
-
-	for _, row := range htlcRows {
-		circuiteKey, htlc, err := unmarshalInvoiceHTLC(row)
-		if err != nil {
-			return nil, fmt.Errorf("unable to unmarshal "+
-				"htlc(%d): %w", row.ID, err)
-		}
-
-		if customRecords, ok := cr[row.ID]; ok {
-			htlc.CustomRecords = customRecords
-		} else {
-			htlc.CustomRecords = make(record.CustomSet)
-		}
-
-		htlcs[circuiteKey] = htlc
-	}
-
-	return htlcs, nil
-}
-
-// unmarshalInvoice converts an InvoiceRow to an Invoice.
-func unmarshalInvoice(row sqlc.Invoice) (*lntypes.Hash, *invpkg.Invoice,
-	error) {
-
-	var (
-		settleIndex    int64
-		settledAt      time.Time
-		memo           []byte
-		paymentRequest []byte
-		preimage       *lntypes.Preimage
-		paymentAddr    [32]byte
-	)
-
-	hash, err := lntypes.MakeHash(row.Hash)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if row.SettleIndex.Valid {
-		settleIndex = row.SettleIndex.Int64
-	}
-
-	if row.SettledAt.Valid {
-		settledAt = row.SettledAt.Time.Local()
-	}
-
-	if row.Memo.Valid {
-		memo = []byte(row.Memo.String)
-	}
-
-	// Keysend payments will have this field empty.
-	if row.PaymentRequest.Valid {
-		paymentRequest = []byte(row.PaymentRequest.String)
-	} else {
-		paymentRequest = []byte{}
-	}
-
-	// We may not have the preimage if this a hodl invoice.
-	if row.Preimage != nil {
-		preimage = &lntypes.Preimage{}
-		copy(preimage[:], row.Preimage)
-	}
-
-	copy(paymentAddr[:], row.PaymentAddr)
-
-	var cltvDelta int32
-	if row.CltvDelta.Valid {
-		cltvDelta = row.CltvDelta.Int32
-	}
-
-	invoice := &invpkg.Invoice{
-		SettleIndex:    uint64(settleIndex),
-		SettleDate:     settledAt,
-		Memo:           memo,
-		PaymentRequest: paymentRequest,
-		CreationDate:   row.CreatedAt.Local(),
-		Terms: invpkg.ContractTerm{
-			FinalCltvDelta:  cltvDelta,
-			Expiry:          time.Duration(row.Expiry),
-			PaymentPreimage: preimage,
-			Value:           lnwire.MilliSatoshi(row.AmountMsat),
-			PaymentAddr:     paymentAddr,
-		},
-		AddIndex:    uint64(row.ID),
-		State:       invpkg.ContractState(row.State),
-		AmtPaid:     lnwire.MilliSatoshi(row.AmountPaidMsat),
-		Htlcs:       make(map[models.CircuitKey]*invpkg.InvoiceHTLC),
-		AMPState:    invpkg.AMPInvoiceState{},
-		HodlInvoice: row.IsHodl,
-	}
-
-	return &hash, invoice, nil
-}
-
-// unmarshalInvoiceHTLC converts an sqlc.InvoiceHtlc to an InvoiceHTLC.
-func unmarshalInvoiceHTLC(row sqlc.InvoiceHtlc) (invpkg.CircuitKey,
-	*invpkg.InvoiceHTLC, error) {
-
-	uint64ChanID, err := strconv.ParseUint(row.ChanID, 10, 64)
-	if err != nil {
-		return invpkg.CircuitKey{}, nil, err
-	}
-
-	chanID := lnwire.NewShortChanIDFromInt(uint64ChanID)
-
-	if row.HtlcID < 0 {
-		return invpkg.CircuitKey{}, nil, fmt.Errorf("invalid uint64 "+
-			"value: %v", row.HtlcID)
-	}
-
-	htlcID := uint64(row.HtlcID)
-
-	circuitKey := invpkg.CircuitKey{
-		ChanID: chanID,
-		HtlcID: htlcID,
-	}
-
-	htlc := &invpkg.InvoiceHTLC{
-		Amt:          lnwire.MilliSatoshi(row.AmountMsat),
-		AcceptHeight: uint32(row.AcceptHeight),
-		AcceptTime:   row.AcceptTime.Local(),
-		Expiry:       uint32(row.ExpiryHeight),
-		State:        invpkg.HtlcState(row.State),
-	}
-
-	if row.TotalMppMsat.Valid {
-		htlc.MppTotalAmt = lnwire.MilliSatoshi(row.TotalMppMsat.Int64)
-	}
-
-	if row.ResolveTime.Valid {
-		htlc.ResolveTime = row.ResolveTime.Time.Local()
-	}
-
-	return circuitKey, htlc, nil
-}
-
-// queryWithLimit is a helper method that can be used to query the database
-// using a limit and offset. The passed query function should return the number
-// of rows returned and an error if any.
-func queryWithLimit(query func(int) (int, error), limit int) error {
-	offset := 0
-	for {
-		rows, err := query(offset)
-		if err != nil {
-			return err
-		}
-
-		if rows < limit {
-			return nil
-		}
-
-		offset += limit
-	}
-}
diff --git a/sqldb/no_sqlite.go b/sqldb/no_sqlite.go
deleted file mode 100644
index 9ea35c43c..000000000
--- a/sqldb/no_sqlite.go
+++ /dev/null
@@ -1,18 +0,0 @@
-//go:build js || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64))
-
-package sqldb
-
-import "fmt"
-
-// SqliteStore is a database store implementation that uses a sqlite backend.
-type SqliteStore struct {
-	cfg *SqliteConfig
-
-	*BaseDB
-}
-
-// NewSqliteStore attempts to open a new sqlite database based on the passed
-// config.
-func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
-	return nil, fmt.Errorf("SQLite backend not supported in WebAssembly")
-}
diff --git a/sqldb/postgres.go b/sqldb/postgres.go
index e6e88c93b..3cfa59efc 100644
--- a/sqldb/postgres.go
+++ b/sqldb/postgres.go
@@ -2,14 +2,18 @@ package sqldb
 
 import (
 	"database/sql"
-	"net/url"
-	"path"
-	"strings"
+	"fmt"
+	"testing"
 	"time"
 
 	postgres_migrate "github.com/golang-migrate/migrate/v4/database/postgres"
 	_ "github.com/golang-migrate/migrate/v4/source/file" // Read migrations from files. // nolint:lll
 	"github.com/lightningnetwork/lnd/sqldb/sqlc"
+	"github.com/stretchr/testify/require"
+)
+
+const (
+	dsnTemplate = "postgres://%v:%v@%v:%d/%v?sslmode=%v"
 )
 
 var (
@@ -21,45 +25,35 @@ var (
 	DefaultPostgresFixtureLifetime = 10 * time.Minute
 )
 
-// replacePasswordInDSN takes a DSN string and returns it with the password
-// replaced by "***".
-func replacePasswordInDSN(dsn string) (string, error) {
-	// Parse the DSN as a URL
-	u, err := url.Parse(dsn)
-	if err != nil {
-		return "", err
-	}
-
-	// Check if the URL has a user info part
-	if u.User != nil {
-		username := u.User.Username()
-
-		// Reconstruct user info with "***" as password
-		userInfo := username + ":***@"
-
-		// Rebuild the DSN with the modified user info
-		sanitizeDSN := strings.Replace(
-			dsn, u.User.String()+"@", userInfo, 1,
-		)
+// PostgresConfig holds the postgres database configuration.
+//
+//nolint:lll
+type PostgresConfig struct {
+	SkipMigrations     bool   `long:"skipmigrations" description:"Skip applying migrations on startup."`
+	Host               string `long:"host" description:"Database server hostname."`
+	Port               int    `long:"port" description:"Database server port."`
+	User               string `long:"user" description:"Database user."`
+	Password           string `long:"password" description:"Database user's password."`
+	DBName             string `long:"dbname" description:"Database name to use."`
+	MaxOpenConnections int    `long:"maxconnections" description:"Max open connections to keep alive to the database server."`
+	RequireSSL         bool   `long:"requiressl" description:"Whether to require using SSL (mode: require) when connecting to the server."`
+}
 
-		return sanitizeDSN, nil
+// DSN returns the dns to connect to the database.
+func (s *PostgresConfig) DSN(hidePassword bool) string {
+	var sslMode = "disable"
+	if s.RequireSSL {
+		sslMode = "require"
 	}
 
-	// Return the original DSN if no user info is present
-	return dsn, nil
-}
-
-// getDatabaseNameFromDSN extracts the database name from a DSN string.
-func getDatabaseNameFromDSN(dsn string) (string, error) {
-	// Parse the DSN as a URL
-	u, err := url.Parse(dsn)
-	if err != nil {
-		return "", err
+	password := s.Password
+	if hidePassword {
+		// Placeholder used for logging the DSN safely.
+		password = "****"
 	}
 
-	// The database name is the last segment of the path. Trim leading slash
-	// and return the last segment.
-	return path.Base(u.Path), nil
+	return fmt.Sprintf(dsnTemplate, s.User, password, s.Host, s.Port,
+		s.DBName, sslMode)
 }
 
 // PostgresStore is a database store implementation that uses a Postgres
@@ -73,25 +67,16 @@ type PostgresStore struct {
 // NewPostgresStore creates a new store that is backed by a Postgres database
 // backend.
 func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
-	sanitizedDSN, err := replacePasswordInDSN(cfg.Dsn)
-	if err != nil {
-		return nil, err
-	}
-	log.Infof("Using SQL database '%s'", sanitizedDSN)
-
-	dbName, err := getDatabaseNameFromDSN(cfg.Dsn)
-	if err != nil {
-		return nil, err
-	}
+	log.Infof("Using SQL database '%s'", cfg.DSN(true))
 
-	rawDB, err := sql.Open("pgx", cfg.Dsn)
+	rawDB, err := sql.Open("pgx", cfg.DSN(false))
 	if err != nil {
 		return nil, err
 	}
 
 	maxConns := defaultMaxConns
-	if cfg.MaxConnections > 0 {
-		maxConns = cfg.MaxConnections
+	if cfg.MaxOpenConnections > 0 {
+		maxConns = cfg.MaxOpenConnections
 	}
 
 	rawDB.SetMaxOpenConns(maxConns)
@@ -104,7 +89,7 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
 		// system.
 		//
 		// First, we'll need to open up a new migration instance for
-		// our current target database: Postgres.
+		// our current target database: sqlite.
 		driver, err := postgres_migrate.WithInstance(
 			rawDB, &postgres_migrate.Config{},
 		)
@@ -115,12 +100,11 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
 		postgresFS := newReplacerFS(sqlSchemas, map[string]string{
 			"BLOB":                "BYTEA",
 			"INTEGER PRIMARY KEY": "SERIAL PRIMARY KEY",
-			"BIGINT PRIMARY KEY":  "BIGSERIAL PRIMARY KEY",
 			"TIMESTAMP":           "TIMESTAMP WITHOUT TIME ZONE",
 		})
 
 		err = applyMigrations(
-			postgresFS, driver, "sqlc/migrations", dbName,
+			postgresFS, driver, "sqlc/migrations", cfg.DBName,
 		)
 		if err != nil {
 			return nil, err
@@ -137,3 +121,21 @@ func NewPostgresStore(cfg *PostgresConfig) (*PostgresStore, error) {
 		},
 	}, nil
 }
+
+// NewTestPostgresDB is a helper function that creates a Postgres database for
+// testing.
+func NewTestPostgresDB(t *testing.T) *PostgresStore {
+	t.Helper()
+
+	t.Logf("Creating new Postgres DB for testing")
+
+	sqlFixture := NewTestPgFixture(t, DefaultPostgresFixtureLifetime)
+	store, err := NewPostgresStore(sqlFixture.GetConfig())
+	require.NoError(t, err)
+
+	t.Cleanup(func() {
+		sqlFixture.TearDown(t)
+	})
+
+	return store
+}
diff --git a/sqldb/postgres_fixture.go b/sqldb/postgres_fixture.go
index a0f0893ce..991aad980 100644
--- a/sqldb/postgres_fixture.go
+++ b/sqldb/postgres_fixture.go
@@ -1,12 +1,8 @@
-//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64)) && !(netbsd || openbsd)
-
 package sqldb
 
 import (
 	"context"
-	"crypto/rand"
 	"database/sql"
-	"encoding/hex"
 	"fmt"
 	"strconv"
 	"strings"
@@ -59,7 +55,6 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
 			"postgres",
 			"-c", "log_statement=all",
 			"-c", "log_destination=stderr",
-			"-c", "max_connections=1000",
 		},
 	}, func(config *docker.HostConfig) {
 		// Set AutoRemove to true so that stopped container goes away
@@ -79,7 +74,7 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
 		host: host,
 		port: int(port),
 	}
-	databaseURL := fixture.GetConfig(testPgDBName).Dsn
+	databaseURL := fixture.GetDSN()
 	log.Infof("Connecting to Postgres fixture: %v\n", databaseURL)
 
 	// Tell docker to hard kill the container in "expiry" seconds.
@@ -108,13 +103,20 @@ func NewTestPgFixture(t *testing.T, expiry time.Duration) *TestPgFixture {
 	return fixture
 }
 
+// GetDSN returns the DSN (Data Source Name) for the started Postgres node.
+func (f *TestPgFixture) GetDSN() string {
+	return f.GetConfig().DSN(false)
+}
+
 // GetConfig returns the full config of the Postgres node.
-func (f *TestPgFixture) GetConfig(dbName string) *PostgresConfig {
+func (f *TestPgFixture) GetConfig() *PostgresConfig {
 	return &PostgresConfig{
-		Dsn: fmt.Sprintf(
-			"postgres://%v:%v@%v:%v/%v?sslmode=disable",
-			testPgUser, testPgPass, f.host, f.port, dbName,
-		),
+		Host:       f.host,
+		Port:       f.port,
+		User:       testPgUser,
+		Password:   testPgPass,
+		DBName:     testPgDBName,
+		RequireSSL: false,
 	}
 }
 
@@ -124,32 +126,15 @@ func (f *TestPgFixture) TearDown(t *testing.T) {
 	require.NoError(t, err, "Could not purge resource")
 }
 
-// NewTestPostgresDB is a helper function that creates a Postgres database for
-// testing using the given fixture.
-func NewTestPostgresDB(t *testing.T, fixture *TestPgFixture) *PostgresStore {
-	t.Helper()
-
-	// Create random database name.
-	randBytes := make([]byte, 8)
-	_, err := rand.Read(randBytes)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	dbName := "test_" + hex.EncodeToString(randBytes)
-
-	t.Logf("Creating new Postgres DB '%s' for testing", dbName)
+// ClearDB clears the database.
+func (f *TestPgFixture) ClearDB(t *testing.T) {
+	dbConn, err := sql.Open("postgres", f.GetDSN())
+	require.NoError(t, err)
 
-	_, err = fixture.db.ExecContext(
-		context.Background(), "CREATE DATABASE "+dbName,
+	_, err = dbConn.ExecContext(
+		context.Background(),
+		`DROP SCHEMA IF EXISTS public CASCADE;
+		 CREATE SCHEMA public;`,
 	)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	cfg := fixture.GetConfig(dbName)
-	store, err := NewPostgresStore(cfg)
 	require.NoError(t, err)
-
-	return store
 }
diff --git a/sqldb/sqlc/amp_invoices.sql.go b/sqldb/sqlc/amp_invoices.sql.go
index 3fcfe4b27..c803a8307 100644
--- a/sqldb/sqlc/amp_invoices.sql.go
+++ b/sqldb/sqlc/amp_invoices.sql.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 // source: amp_invoices.sql
 
 package sqlc
@@ -11,69 +11,67 @@ import (
 	"time"
 )
 
-const fetchAMPSubInvoiceHTLCs = `-- name: FetchAMPSubInvoiceHTLCs :many
-SELECT 
-    amp.set_id, amp.root_share, amp.child_index, amp.hash, amp.preimage, 
-    invoice_htlcs.id, invoice_htlcs.chan_id, invoice_htlcs.htlc_id, invoice_htlcs.amount_msat, invoice_htlcs.total_mpp_msat, invoice_htlcs.accept_height, invoice_htlcs.accept_time, invoice_htlcs.expiry_height, invoice_htlcs.state, invoice_htlcs.resolve_time, invoice_htlcs.invoice_id
-FROM amp_sub_invoice_htlcs amp
-INNER JOIN invoice_htlcs ON amp.htlc_id = invoice_htlcs.id
-WHERE amp.invoice_id = $1
-AND (
-    set_id = $2 OR 
-    $2 IS NULL
+const deleteAMPHTLCCustomRecords = `-- name: DeleteAMPHTLCCustomRecords :exec
+WITH htlc_ids AS (
+    SELECT htlc_id
+    FROM amp_invoice_htlcs 
+    WHERE invoice_id = $1
 )
+DELETE
+FROM invoice_htlc_custom_records
+WHERE htlc_id IN (SELECT id FROM htlc_ids)
 `
 
-type FetchAMPSubInvoiceHTLCsParams struct {
-	InvoiceID int64
-	SetID     []byte
+func (q *Queries) DeleteAMPHTLCCustomRecords(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteAMPHTLCCustomRecords, invoiceID)
+	return err
 }
 
-type FetchAMPSubInvoiceHTLCsRow struct {
-	SetID        []byte
-	RootShare    []byte
-	ChildIndex   int64
-	Hash         []byte
-	Preimage     []byte
-	ID           int64
-	ChanID       string
-	HtlcID       int64
-	AmountMsat   int64
-	TotalMppMsat sql.NullInt64
-	AcceptHeight int32
-	AcceptTime   time.Time
-	ExpiryHeight int32
-	State        int16
-	ResolveTime  sql.NullTime
-	InvoiceID    int64
+const deleteAMPHTLCs = `-- name: DeleteAMPHTLCs :exec
+DELETE 
+FROM amp_invoice_htlcs  
+WHERE invoice_id = $1
+`
+
+func (q *Queries) DeleteAMPHTLCs(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteAMPHTLCs, invoiceID)
+	return err
 }
 
-func (q *Queries) FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error) {
-	rows, err := q.db.QueryContext(ctx, fetchAMPSubInvoiceHTLCs, arg.InvoiceID, arg.SetID)
+const deleteAMPInvoiceHTLC = `-- name: DeleteAMPInvoiceHTLC :exec
+DELETE 
+FROM amp_invoice_htlcs
+WHERE set_id = $1
+`
+
+func (q *Queries) DeleteAMPInvoiceHTLC(ctx context.Context, setID []byte) error {
+	_, err := q.db.ExecContext(ctx, deleteAMPInvoiceHTLC, setID)
+	return err
+}
+
+const getAMPInvoiceHTLCsByInvoiceID = `-- name: GetAMPInvoiceHTLCsByInvoiceID :many
+SELECT set_id, htlc_id, invoice_id, root_share, child_index, hash, preimage
+FROM amp_invoice_htlcs
+WHERE invoice_id = $1
+`
+
+func (q *Queries) GetAMPInvoiceHTLCsByInvoiceID(ctx context.Context, invoiceID int32) ([]AmpInvoiceHtlc, error) {
+	rows, err := q.db.QueryContext(ctx, getAMPInvoiceHTLCsByInvoiceID, invoiceID)
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
-	var items []FetchAMPSubInvoiceHTLCsRow
+	var items []AmpInvoiceHtlc
 	for rows.Next() {
-		var i FetchAMPSubInvoiceHTLCsRow
+		var i AmpInvoiceHtlc
 		if err := rows.Scan(
 			&i.SetID,
+			&i.HtlcID,
+			&i.InvoiceID,
 			&i.RootShare,
 			&i.ChildIndex,
 			&i.Hash,
 			&i.Preimage,
-			&i.ID,
-			&i.ChanID,
-			&i.HtlcID,
-			&i.AmountMsat,
-			&i.TotalMppMsat,
-			&i.AcceptHeight,
-			&i.AcceptTime,
-			&i.ExpiryHeight,
-			&i.State,
-			&i.ResolveTime,
-			&i.InvoiceID,
 		); err != nil {
 			return nil, err
 		}
@@ -88,37 +86,29 @@ func (q *Queries) FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubIn
 	return items, nil
 }
 
-const fetchAMPSubInvoices = `-- name: FetchAMPSubInvoices :many
-SELECT set_id, state, created_at, settled_at, settle_index, invoice_id
-FROM amp_sub_invoices
-WHERE invoice_id = $1 
-AND (
-    set_id = $2 OR 
-    $2 IS NULL
-)
+const getAMPInvoiceHTLCsBySetID = `-- name: GetAMPInvoiceHTLCsBySetID :many
+SELECT set_id, htlc_id, invoice_id, root_share, child_index, hash, preimage
+FROM amp_invoice_htlcs
+WHERE set_id = $1
 `
 
-type FetchAMPSubInvoicesParams struct {
-	InvoiceID int64
-	SetID     []byte
-}
-
-func (q *Queries) FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error) {
-	rows, err := q.db.QueryContext(ctx, fetchAMPSubInvoices, arg.InvoiceID, arg.SetID)
+func (q *Queries) GetAMPInvoiceHTLCsBySetID(ctx context.Context, setID []byte) ([]AmpInvoiceHtlc, error) {
+	rows, err := q.db.QueryContext(ctx, getAMPInvoiceHTLCsBySetID, setID)
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
-	var items []AmpSubInvoice
+	var items []AmpInvoiceHtlc
 	for rows.Next() {
-		var i AmpSubInvoice
+		var i AmpInvoiceHtlc
 		if err := rows.Scan(
 			&i.SetID,
-			&i.State,
-			&i.CreatedAt,
-			&i.SettledAt,
-			&i.SettleIndex,
+			&i.HtlcID,
 			&i.InvoiceID,
+			&i.RootShare,
+			&i.ChildIndex,
+			&i.Hash,
+			&i.Preimage,
 		); err != nil {
 			return nil, err
 		}
@@ -133,84 +123,28 @@ func (q *Queries) FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoic
 	return items, nil
 }
 
-const fetchSettledAMPSubInvoices = `-- name: FetchSettledAMPSubInvoices :many
-SELECT 
-    a.set_id, 
-    a.settle_index as amp_settle_index, 
-    a.settled_at as amp_settled_at,
-    i.id, i.hash, i.preimage, i.settle_index, i.settled_at, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.payment_request_hash, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at
-FROM amp_sub_invoices a
-INNER JOIN invoices i ON a.invoice_id = i.id
-WHERE (
-    a.settle_index >= $1 OR
-    $1 IS NULL
-) AND (
-    a.settle_index <= $2 OR
-    $2 IS NULL
-)
+const getSetIDHTLCsCustomRecords = `-- name: GetSetIDHTLCsCustomRecords :many
+SELECT ihcr.htlc_id, key, value
+FROM amp_invoice_htlcs aih JOIN invoice_htlc_custom_records ihcr ON aih.id=ihcr.htlc_id 
+WHERE aih.set_id = $1
 `
 
-type FetchSettledAMPSubInvoicesParams struct {
-	SettleIndexGet sql.NullInt64
-	SettleIndexLet sql.NullInt64
-}
-
-type FetchSettledAMPSubInvoicesRow struct {
-	SetID              []byte
-	AmpSettleIndex     sql.NullInt64
-	AmpSettledAt       sql.NullTime
-	ID                 int64
-	Hash               []byte
-	Preimage           []byte
-	SettleIndex        sql.NullInt64
-	SettledAt          sql.NullTime
-	Memo               sql.NullString
-	AmountMsat         int64
-	CltvDelta          sql.NullInt32
-	Expiry             int32
-	PaymentAddr        []byte
-	PaymentRequest     sql.NullString
-	PaymentRequestHash []byte
-	State              int16
-	AmountPaidMsat     int64
-	IsAmp              bool
-	IsHodl             bool
-	IsKeysend          bool
-	CreatedAt          time.Time
+type GetSetIDHTLCsCustomRecordsRow struct {
+	HtlcID int64
+	Key    int64
+	Value  []byte
 }
 
-func (q *Queries) FetchSettledAMPSubInvoices(ctx context.Context, arg FetchSettledAMPSubInvoicesParams) ([]FetchSettledAMPSubInvoicesRow, error) {
-	rows, err := q.db.QueryContext(ctx, fetchSettledAMPSubInvoices, arg.SettleIndexGet, arg.SettleIndexLet)
+func (q *Queries) GetSetIDHTLCsCustomRecords(ctx context.Context, setID []byte) ([]GetSetIDHTLCsCustomRecordsRow, error) {
+	rows, err := q.db.QueryContext(ctx, getSetIDHTLCsCustomRecords, setID)
 	if err != nil {
 		return nil, err
 	}
 	defer rows.Close()
-	var items []FetchSettledAMPSubInvoicesRow
+	var items []GetSetIDHTLCsCustomRecordsRow
 	for rows.Next() {
-		var i FetchSettledAMPSubInvoicesRow
-		if err := rows.Scan(
-			&i.SetID,
-			&i.AmpSettleIndex,
-			&i.AmpSettledAt,
-			&i.ID,
-			&i.Hash,
-			&i.Preimage,
-			&i.SettleIndex,
-			&i.SettledAt,
-			&i.Memo,
-			&i.AmountMsat,
-			&i.CltvDelta,
-			&i.Expiry,
-			&i.PaymentAddr,
-			&i.PaymentRequest,
-			&i.PaymentRequestHash,
-			&i.State,
-			&i.AmountPaidMsat,
-			&i.IsAmp,
-			&i.IsHodl,
-			&i.IsKeysend,
-			&i.CreatedAt,
-		); err != nil {
+		var i GetSetIDHTLCsCustomRecordsRow
+		if err := rows.Scan(&i.HtlcID, &i.Key, &i.Value); err != nil {
 			return nil, err
 		}
 		items = append(items, i)
@@ -224,27 +158,15 @@ func (q *Queries) FetchSettledAMPSubInvoices(ctx context.Context, arg FetchSettl
 	return items, nil
 }
 
-const getAMPInvoiceID = `-- name: GetAMPInvoiceID :one
-SELECT invoice_id FROM amp_sub_invoices WHERE set_id = $1
-`
-
-func (q *Queries) GetAMPInvoiceID(ctx context.Context, setID []byte) (int64, error) {
-	row := q.db.QueryRowContext(ctx, getAMPInvoiceID, setID)
-	var invoice_id int64
-	err := row.Scan(&invoice_id)
-	return invoice_id, err
-}
-
-const insertAMPSubInvoiceHTLC = `-- name: InsertAMPSubInvoiceHTLC :exec
-INSERT INTO amp_sub_invoice_htlcs (
-    invoice_id, set_id, htlc_id, root_share, child_index, hash, preimage
+const insertAMPInvoiceHTLC = `-- name: InsertAMPInvoiceHTLC :exec
+INSERT INTO amp_invoice_htlcs (
+    set_id, htlc_id, root_share, child_index, hash, preimage
 ) VALUES (
-    $1, $2, $3, $4, $5, $6, $7
+    $1, $2, $3, $4, $5, $6
 )
 `
 
-type InsertAMPSubInvoiceHTLCParams struct {
-	InvoiceID  int64
+type InsertAMPInvoiceHTLCParams struct {
 	SetID      []byte
 	HtlcID     int64
 	RootShare  []byte
@@ -253,9 +175,8 @@ type InsertAMPSubInvoiceHTLCParams struct {
 	Preimage   []byte
 }
 
-func (q *Queries) InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubInvoiceHTLCParams) error {
-	_, err := q.db.ExecContext(ctx, insertAMPSubInvoiceHTLC,
-		arg.InvoiceID,
+func (q *Queries) InsertAMPInvoiceHTLC(ctx context.Context, arg InsertAMPInvoiceHTLCParams) error {
+	_, err := q.db.ExecContext(ctx, insertAMPInvoiceHTLC,
 		arg.SetID,
 		arg.HtlcID,
 		arg.RootShare,
@@ -266,75 +187,140 @@ func (q *Queries) InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubI
 	return err
 }
 
-const updateAMPSubInvoiceHTLCPreimage = `-- name: UpdateAMPSubInvoiceHTLCPreimage :execresult
-UPDATE amp_sub_invoice_htlcs AS a
-SET preimage = $4
-WHERE a.invoice_id = $1 AND a.set_id = $2 AND a.htlc_id = (
-    SELECT id FROM invoice_htlcs AS i WHERE i.htlc_id = $3
+const insertAMPInvoicePayment = `-- name: InsertAMPInvoicePayment :exec
+INSERT INTO amp_invoice_payments (
+    set_id, state, created_at, settled_index, invoice_id
+) VALUES (
+    $1, $2, $3, $4, $5
 )
 `
 
-type UpdateAMPSubInvoiceHTLCPreimageParams struct {
-	InvoiceID int64
-	SetID     []byte
-	HtlcID    int64
-	Preimage  []byte
+type InsertAMPInvoicePaymentParams struct {
+	SetID        []byte
+	State        int16
+	CreatedAt    time.Time
+	SettledIndex sql.NullInt32
+	InvoiceID    int32
 }
 
-func (q *Queries) UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context, arg UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, updateAMPSubInvoiceHTLCPreimage,
-		arg.InvoiceID,
+func (q *Queries) InsertAMPInvoicePayment(ctx context.Context, arg InsertAMPInvoicePaymentParams) error {
+	_, err := q.db.ExecContext(ctx, insertAMPInvoicePayment,
 		arg.SetID,
-		arg.HtlcID,
-		arg.Preimage,
+		arg.State,
+		arg.CreatedAt,
+		arg.SettledIndex,
+		arg.InvoiceID,
 	)
+	return err
 }
 
-const updateAMPSubInvoiceState = `-- name: UpdateAMPSubInvoiceState :exec
-UPDATE amp_sub_invoices
-SET state = $2, 
-    settle_index = COALESCE(settle_index, $3),
-    settled_at = COALESCE(settled_at, $4)
-WHERE set_id = $1
+const selectAMPInvoicePayments = `-- name: SelectAMPInvoicePayments :many
+SELECT aip.set_id, aip.state, aip.created_at, aip.settled_index, aip.invoice_id, ip.id, ip.settled_at, ip.amount_paid_msat, ip.invoice_id
+FROM amp_invoice_payments aip LEFT JOIN invoice_payments ip ON aip.settled_index = ip.id
+WHERE (
+    set_id = $1 OR 
+    $1 IS NULL
+) AND (
+    aip.settled_index = $2 OR 
+    $2 IS NULL
+) AND (
+    aip.invoice_id = $3 OR 
+    $3 IS NULL
+)
 `
 
-type UpdateAMPSubInvoiceStateParams struct {
-	SetID       []byte
-	State       int16
-	SettleIndex sql.NullInt64
-	SettledAt   sql.NullTime
+type SelectAMPInvoicePaymentsParams struct {
+	SetID        []byte
+	SettledIndex sql.NullInt32
+	InvoiceID    sql.NullInt32
 }
 
-func (q *Queries) UpdateAMPSubInvoiceState(ctx context.Context, arg UpdateAMPSubInvoiceStateParams) error {
-	_, err := q.db.ExecContext(ctx, updateAMPSubInvoiceState,
-		arg.SetID,
-		arg.State,
-		arg.SettleIndex,
-		arg.SettledAt,
-	)
+type SelectAMPInvoicePaymentsRow struct {
+	SetID          []byte
+	State          int16
+	CreatedAt      time.Time
+	SettledIndex   sql.NullInt32
+	InvoiceID      int32
+	ID             sql.NullInt32
+	SettledAt      sql.NullTime
+	AmountPaidMsat sql.NullInt64
+	InvoiceID_2    sql.NullInt32
+}
+
+func (q *Queries) SelectAMPInvoicePayments(ctx context.Context, arg SelectAMPInvoicePaymentsParams) ([]SelectAMPInvoicePaymentsRow, error) {
+	rows, err := q.db.QueryContext(ctx, selectAMPInvoicePayments, arg.SetID, arg.SettledIndex, arg.InvoiceID)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []SelectAMPInvoicePaymentsRow
+	for rows.Next() {
+		var i SelectAMPInvoicePaymentsRow
+		if err := rows.Scan(
+			&i.SetID,
+			&i.State,
+			&i.CreatedAt,
+			&i.SettledIndex,
+			&i.InvoiceID,
+			&i.ID,
+			&i.SettledAt,
+			&i.AmountPaidMsat,
+			&i.InvoiceID_2,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
+}
+
+const updateAMPInvoiceHTLC = `-- name: UpdateAMPInvoiceHTLC :exec
+UPDATE amp_invoice_htlcs
+SET preimage = $1
+WHERE htlc_id = $2
+`
+
+type UpdateAMPInvoiceHTLCParams struct {
+	Preimage []byte
+	HtlcID   int64
+}
+
+func (q *Queries) UpdateAMPInvoiceHTLC(ctx context.Context, arg UpdateAMPInvoiceHTLCParams) error {
+	_, err := q.db.ExecContext(ctx, updateAMPInvoiceHTLC, arg.Preimage, arg.HtlcID)
 	return err
 }
 
-const upsertAMPSubInvoice = `-- name: UpsertAMPSubInvoice :execresult
-INSERT INTO amp_sub_invoices (
-    set_id, state, created_at, invoice_id
-) VALUES (
-    $1, $2, $3, $4
-) ON CONFLICT (set_id, invoice_id) DO NOTHING
+const updateAMPPayment = `-- name: UpdateAMPPayment :exec
+UPDATE amp_invoice_payments
+SET state = $1, settled_index = $2
+WHERE state = 0 AND (
+    set_id = $3 OR 
+    $3 IS NULL
+) AND (
+    invoice_id = $4 OR 
+    $4 IS NULL
+)
 `
 
-type UpsertAMPSubInvoiceParams struct {
-	SetID     []byte
-	State     int16
-	CreatedAt time.Time
-	InvoiceID int64
+type UpdateAMPPaymentParams struct {
+	State        int16
+	SettledIndex sql.NullInt32
+	SetID        []byte
+	InvoiceID    sql.NullInt32
 }
 
-func (q *Queries) UpsertAMPSubInvoice(ctx context.Context, arg UpsertAMPSubInvoiceParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, upsertAMPSubInvoice,
-		arg.SetID,
+func (q *Queries) UpdateAMPPayment(ctx context.Context, arg UpdateAMPPaymentParams) error {
+	_, err := q.db.ExecContext(ctx, updateAMPPayment,
 		arg.State,
-		arg.CreatedAt,
+		arg.SettledIndex,
+		arg.SetID,
 		arg.InvoiceID,
 	)
+	return err
 }
diff --git a/sqldb/sqlc/db.go b/sqldb/sqlc/db.go
index 8ed64d139..9848f3e6a 100644
--- a/sqldb/sqlc/db.go
+++ b/sqldb/sqlc/db.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 
 package sqlc
 
diff --git a/sqldb/sqlc/invoice_events.sql.go b/sqldb/sqlc/invoice_events.sql.go
index 983dfbb15..054d9091c 100644
--- a/sqldb/sqlc/invoice_events.sql.go
+++ b/sqldb/sqlc/invoice_events.sql.go
@@ -1,122 +1,128 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 // source: invoice_events.sql
 
 package sqlc
 
 import (
 	"context"
+	"database/sql"
 	"time"
 )
 
-const onAMPSubInvoiceCanceled = `-- name: OnAMPSubInvoiceCanceled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
-) VALUES (
-    $1, 4, $2, $3
-)
-`
-
-type OnAMPSubInvoiceCanceledParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
-	SetID     []byte
-}
-
-func (q *Queries) OnAMPSubInvoiceCanceled(ctx context.Context, arg OnAMPSubInvoiceCanceledParams) error {
-	_, err := q.db.ExecContext(ctx, onAMPSubInvoiceCanceled, arg.AddedAt, arg.InvoiceID, arg.SetID)
-	return err
-}
-
-const onAMPSubInvoiceCreated = `-- name: OnAMPSubInvoiceCreated :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
-) VALUES (
-    $1, 3, $2, $3
-)
+const deleteInvoiceEvents = `-- name: DeleteInvoiceEvents :exec
+DELETE
+FROM invoice_events
+WHERE invoice_id = $1
 `
 
-type OnAMPSubInvoiceCreatedParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
-	SetID     []byte
-}
-
-func (q *Queries) OnAMPSubInvoiceCreated(ctx context.Context, arg OnAMPSubInvoiceCreatedParams) error {
-	_, err := q.db.ExecContext(ctx, onAMPSubInvoiceCreated, arg.AddedAt, arg.InvoiceID, arg.SetID)
+func (q *Queries) DeleteInvoiceEvents(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoiceEvents, invoiceID)
 	return err
 }
 
-const onAMPSubInvoiceSettled = `-- name: OnAMPSubInvoiceSettled :exec
+const insertInvoiceEvent = `-- name: InsertInvoiceEvent :exec
 INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
+    created_at, invoice_id, htlc_id, set_id, event_type, event_metadata
 ) VALUES (
-    $1, 5, $2, $3
+    $1, $2, $3, $4, $5, $6
 )
 `
 
-type OnAMPSubInvoiceSettledParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
-	SetID     []byte
+type InsertInvoiceEventParams struct {
+	CreatedAt     time.Time
+	InvoiceID     int32
+	HtlcID        sql.NullInt64
+	SetID         []byte
+	EventType     int32
+	EventMetadata []byte
 }
 
-func (q *Queries) OnAMPSubInvoiceSettled(ctx context.Context, arg OnAMPSubInvoiceSettledParams) error {
-	_, err := q.db.ExecContext(ctx, onAMPSubInvoiceSettled, arg.AddedAt, arg.InvoiceID, arg.SetID)
+func (q *Queries) InsertInvoiceEvent(ctx context.Context, arg InsertInvoiceEventParams) error {
+	_, err := q.db.ExecContext(ctx, insertInvoiceEvent,
+		arg.CreatedAt,
+		arg.InvoiceID,
+		arg.HtlcID,
+		arg.SetID,
+		arg.EventType,
+		arg.EventMetadata,
+	)
 	return err
 }
 
-const onInvoiceCanceled = `-- name: OnInvoiceCanceled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
-) VALUES (
-    $1, 1, $2
-)
+const selectInvoiceEvents = `-- name: SelectInvoiceEvents :many
+SELECT id, created_at, invoice_id, htlc_id, set_id, event_type, event_metadata
+FROM invoice_events
+WHERE (
+    invoice_id = $1 OR 
+    $1 IS NULL
+) AND (
+    htlc_id = $2 OR 
+    $2 IS NULL
+) AND (
+    set_id = $3 OR 
+    $3 IS NULL
+) AND (
+    event_type = $4 OR 
+    $4 IS NULL
+) AND (
+    created_at >= $5 OR 
+    $5 IS NULL
+) AND (
+    created_at <= $6 OR 
+    $6 IS NULL
+) 
+LIMIT $8 OFFSET $7
 `
 
-type OnInvoiceCanceledParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
+type SelectInvoiceEventsParams struct {
+	InvoiceID     sql.NullInt32
+	HtlcID        sql.NullInt64
+	SetID         []byte
+	EventType     sql.NullInt32
+	CreatedAfter  sql.NullTime
+	CreatedBefore sql.NullTime
+	NumOffset     int32
+	NumLimit      int32
 }
 
-func (q *Queries) OnInvoiceCanceled(ctx context.Context, arg OnInvoiceCanceledParams) error {
-	_, err := q.db.ExecContext(ctx, onInvoiceCanceled, arg.AddedAt, arg.InvoiceID)
-	return err
-}
-
-const onInvoiceCreated = `-- name: OnInvoiceCreated :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
-) VALUES (
-    $1, 0, $2
-)
-`
-
-type OnInvoiceCreatedParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
-}
-
-func (q *Queries) OnInvoiceCreated(ctx context.Context, arg OnInvoiceCreatedParams) error {
-	_, err := q.db.ExecContext(ctx, onInvoiceCreated, arg.AddedAt, arg.InvoiceID)
-	return err
-}
-
-const onInvoiceSettled = `-- name: OnInvoiceSettled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
-) VALUES (
-    $1, 2, $2
-)
-`
-
-type OnInvoiceSettledParams struct {
-	AddedAt   time.Time
-	InvoiceID int64
-}
-
-func (q *Queries) OnInvoiceSettled(ctx context.Context, arg OnInvoiceSettledParams) error {
-	_, err := q.db.ExecContext(ctx, onInvoiceSettled, arg.AddedAt, arg.InvoiceID)
-	return err
+func (q *Queries) SelectInvoiceEvents(ctx context.Context, arg SelectInvoiceEventsParams) ([]InvoiceEvent, error) {
+	rows, err := q.db.QueryContext(ctx, selectInvoiceEvents,
+		arg.InvoiceID,
+		arg.HtlcID,
+		arg.SetID,
+		arg.EventType,
+		arg.CreatedAfter,
+		arg.CreatedBefore,
+		arg.NumOffset,
+		arg.NumLimit,
+	)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []InvoiceEvent
+	for rows.Next() {
+		var i InvoiceEvent
+		if err := rows.Scan(
+			&i.ID,
+			&i.CreatedAt,
+			&i.InvoiceID,
+			&i.HtlcID,
+			&i.SetID,
+			&i.EventType,
+			&i.EventMetadata,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
 }
diff --git a/sqldb/sqlc/invoices.sql.go b/sqldb/sqlc/invoices.sql.go
index 09ca1a1e2..fa8d9c294 100644
--- a/sqldb/sqlc/invoices.sql.go
+++ b/sqldb/sqlc/invoices.sql.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 // source: invoices.sql
 
 package sqlc
@@ -11,17 +11,7 @@ import (
 	"time"
 )
 
-const deleteCanceledInvoices = `-- name: DeleteCanceledInvoices :execresult
-DELETE
-FROM invoices
-WHERE state = 2
-`
-
-func (q *Queries) DeleteCanceledInvoices(ctx context.Context) (sql.Result, error) {
-	return q.db.ExecContext(ctx, deleteCanceledInvoices)
-}
-
-const deleteInvoice = `-- name: DeleteInvoice :execresult
+const deleteInvoice = `-- name: DeleteInvoice :exec
 DELETE 
 FROM invoices 
 WHERE (
@@ -31,7 +21,7 @@ WHERE (
     hash = $2 OR 
     $2 IS NULL
 ) AND (
-    settle_index = $3 OR 
+    preimage = $3 OR 
     $3 IS NULL
 ) AND (
     payment_addr = $4 OR
@@ -40,24 +30,174 @@ WHERE (
 `
 
 type DeleteInvoiceParams struct {
-	AddIndex    sql.NullInt64
+	AddIndex    sql.NullInt32
 	Hash        []byte
-	SettleIndex sql.NullInt64
+	Preimage    []byte
 	PaymentAddr []byte
 }
 
-func (q *Queries) DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, deleteInvoice,
+func (q *Queries) DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoice,
 		arg.AddIndex,
 		arg.Hash,
-		arg.SettleIndex,
+		arg.Preimage,
 		arg.PaymentAddr,
 	)
+	return err
+}
+
+const deleteInvoiceFeatures = `-- name: DeleteInvoiceFeatures :exec
+DELETE 
+FROM invoice_features
+WHERE invoice_id = $1
+`
+
+func (q *Queries) DeleteInvoiceFeatures(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoiceFeatures, invoiceID)
+	return err
+}
+
+const deleteInvoiceHTLC = `-- name: DeleteInvoiceHTLC :exec
+DELETE
+FROM invoice_htlcs
+WHERE htlc_id = $1
+`
+
+func (q *Queries) DeleteInvoiceHTLC(ctx context.Context, htlcID int64) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoiceHTLC, htlcID)
+	return err
+}
+
+const deleteInvoiceHTLCCustomRecords = `-- name: DeleteInvoiceHTLCCustomRecords :exec
+WITH htlc_ids AS (
+    SELECT ih.id
+    FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id 
+    WHERE ih.invoice_id = $1
+)
+DELETE
+FROM invoice_htlc_custom_records
+WHERE htlc_id IN (SELECT id FROM htlc_ids)
+`
+
+func (q *Queries) DeleteInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoiceHTLCCustomRecords, invoiceID)
+	return err
+}
+
+const deleteInvoiceHTLCs = `-- name: DeleteInvoiceHTLCs :exec
+DELETE
+FROM invoice_htlcs
+WHERE invoice_id = $1
+`
+
+func (q *Queries) DeleteInvoiceHTLCs(ctx context.Context, invoiceID int32) error {
+	_, err := q.db.ExecContext(ctx, deleteInvoiceHTLCs, invoiceID)
+	return err
+}
+
+const filterInvoicePayments = `-- name: FilterInvoicePayments :many
+SELECT  
+    ip.id AS settle_index, ip.amount_paid_msat, ip.settled_at AS settle_date, 
+    i.id, i.hash, i.preimage, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at 
+FROM invoice_payments ip JOIN invoices i ON ip.invoice_id = i.id
+WHERE (
+    ip.id >= $1 OR 
+    $1 IS NULL
+) AND (
+    ip.settled_at >= $2 OR 
+    $2 IS NULL
+)
+ORDER BY
+    CASE
+        WHEN $3 = FALSE THEN ip.id  
+        ELSE NULL
+    END ASC,
+    CASE
+        WHEN $3 = TRUE THEN ip.id  
+        ELSE NULL
+    END DESC
+LIMIT $5 OFFSET $4
+`
+
+type FilterInvoicePaymentsParams struct {
+	SettleIndexGet sql.NullInt32
+	SettledAfter   sql.NullTime
+	Reverse        interface{}
+	NumOffset      int32
+	NumLimit       int32
+}
+
+type FilterInvoicePaymentsRow struct {
+	SettleIndex      int32
+	AmountPaidMsat   int64
+	SettleDate       time.Time
+	ID               int32
+	Hash             []byte
+	Preimage         []byte
+	Memo             sql.NullString
+	AmountMsat       int64
+	CltvDelta        sql.NullInt32
+	Expiry           int32
+	PaymentAddr      []byte
+	PaymentRequest   sql.NullString
+	State            int16
+	AmountPaidMsat_2 int64
+	IsAmp            bool
+	IsHodl           bool
+	IsKeysend        bool
+	CreatedAt        time.Time
+}
+
+func (q *Queries) FilterInvoicePayments(ctx context.Context, arg FilterInvoicePaymentsParams) ([]FilterInvoicePaymentsRow, error) {
+	rows, err := q.db.QueryContext(ctx, filterInvoicePayments,
+		arg.SettleIndexGet,
+		arg.SettledAfter,
+		arg.Reverse,
+		arg.NumOffset,
+		arg.NumLimit,
+	)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []FilterInvoicePaymentsRow
+	for rows.Next() {
+		var i FilterInvoicePaymentsRow
+		if err := rows.Scan(
+			&i.SettleIndex,
+			&i.AmountPaidMsat,
+			&i.SettleDate,
+			&i.ID,
+			&i.Hash,
+			&i.Preimage,
+			&i.Memo,
+			&i.AmountMsat,
+			&i.CltvDelta,
+			&i.Expiry,
+			&i.PaymentAddr,
+			&i.PaymentRequest,
+			&i.State,
+			&i.AmountPaidMsat_2,
+			&i.IsAmp,
+			&i.IsHodl,
+			&i.IsKeysend,
+			&i.CreatedAt,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
 }
 
 const filterInvoices = `-- name: FilterInvoices :many
-SELECT
-    invoices.id, invoices.hash, invoices.preimage, invoices.settle_index, invoices.settled_at, invoices.memo, invoices.amount_msat, invoices.cltv_delta, invoices.expiry, invoices.payment_addr, invoices.payment_request, invoices.payment_request_hash, invoices.state, invoices.amount_paid_msat, invoices.is_amp, invoices.is_hodl, invoices.is_keysend, invoices.created_at
+SELECT id, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, created_at 
 FROM invoices
 WHERE (
     id >= $1 OR 
@@ -66,58 +206,48 @@ WHERE (
     id <= $2 OR 
     $2 IS NULL
 ) AND (
-    settle_index >= $3 OR
+    state = $3 OR 
     $3 IS NULL
 ) AND (
-    settle_index <= $4 OR
+    created_at >= $4 OR
     $4 IS NULL
 ) AND (
-    state = $5 OR 
+    created_at <= $5 OR 
     $5 IS NULL
-) AND (
-    created_at >= $6 OR
-    $6 IS NULL
-) AND (
-    created_at <= $7 OR 
-    $7 IS NULL
 ) AND (
     CASE
-        WHEN $8=TRUE THEN (state = 0 OR state = 3)
+        WHEN $6=TRUE THEN (state = 0 OR state = 3)
         ELSE TRUE 
     END
 )
 ORDER BY
     CASE
-        WHEN $9 = FALSE THEN id  
+        WHEN $7 = FALSE THEN id  
         ELSE NULL
     END ASC,
     CASE
-        WHEN $9 = TRUE  THEN id  
+        WHEN $7 = TRUE  THEN id  
         ELSE NULL
     END DESC
-LIMIT $11 OFFSET $10
+LIMIT $9 OFFSET $8
 `
 
 type FilterInvoicesParams struct {
-	AddIndexGet    sql.NullInt64
-	AddIndexLet    sql.NullInt64
-	SettleIndexGet sql.NullInt64
-	SettleIndexLet sql.NullInt64
-	State          sql.NullInt16
-	CreatedAfter   sql.NullTime
-	CreatedBefore  sql.NullTime
-	PendingOnly    interface{}
-	Reverse        interface{}
-	NumOffset      int32
-	NumLimit       int32
+	AddIndexGet   sql.NullInt32
+	AddIndexLet   sql.NullInt32
+	State         sql.NullInt16
+	CreatedAfter  sql.NullTime
+	CreatedBefore sql.NullTime
+	PendingOnly   interface{}
+	Reverse       interface{}
+	NumOffset     int32
+	NumLimit      int32
 }
 
 func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error) {
 	rows, err := q.db.QueryContext(ctx, filterInvoices,
 		arg.AddIndexGet,
 		arg.AddIndexLet,
-		arg.SettleIndexGet,
-		arg.SettleIndexLet,
 		arg.State,
 		arg.CreatedAfter,
 		arg.CreatedBefore,
@@ -137,15 +267,12 @@ func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams)
 			&i.ID,
 			&i.Hash,
 			&i.Preimage,
-			&i.SettleIndex,
-			&i.SettledAt,
 			&i.Memo,
 			&i.AmountMsat,
 			&i.CltvDelta,
 			&i.Expiry,
 			&i.PaymentAddr,
 			&i.PaymentRequest,
-			&i.PaymentRequestHash,
 			&i.State,
 			&i.AmountPaidMsat,
 			&i.IsAmp,
@@ -168,35 +295,29 @@ func (q *Queries) FilterInvoices(ctx context.Context, arg FilterInvoicesParams)
 
 const getInvoice = `-- name: GetInvoice :many
 
-SELECT i.id, i.hash, i.preimage, i.settle_index, i.settled_at, i.memo, i.amount_msat, i.cltv_delta, i.expiry, i.payment_addr, i.payment_request, i.payment_request_hash, i.state, i.amount_paid_msat, i.is_amp, i.is_hodl, i.is_keysend, i.created_at
-FROM invoices i
-LEFT JOIN amp_sub_invoices a on i.id = a.invoice_id
+SELECT id, hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, created_at
+FROM invoices
 WHERE (
-    i.id = $1 OR 
+    id = $1 OR 
     $1 IS NULL
 ) AND (
-    i.hash = $2 OR 
+    hash = $2 OR 
     $2 IS NULL
 ) AND (
-    i.preimage = $3 OR 
+    preimage = $3 OR 
     $3 IS NULL
 ) AND (
-    i.payment_addr = $4 OR 
+    payment_addr = $4 OR 
     $4 IS NULL
-) AND (
-    a.set_id = $5 OR
-    $5 IS NULL
 )
-GROUP BY i.id
 LIMIT 2
 `
 
 type GetInvoiceParams struct {
-	AddIndex    sql.NullInt64
+	AddIndex    sql.NullInt32
 	Hash        []byte
 	Preimage    []byte
 	PaymentAddr []byte
-	SetID       []byte
 }
 
 // This method may return more than one invoice if filter using multiple fields
@@ -208,7 +329,6 @@ func (q *Queries) GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoi
 		arg.Hash,
 		arg.Preimage,
 		arg.PaymentAddr,
-		arg.SetID,
 	)
 	if err != nil {
 		return nil, err
@@ -221,15 +341,12 @@ func (q *Queries) GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoi
 			&i.ID,
 			&i.Hash,
 			&i.Preimage,
-			&i.SettleIndex,
-			&i.SettledAt,
 			&i.Memo,
 			&i.AmountMsat,
 			&i.CltvDelta,
 			&i.Expiry,
 			&i.PaymentAddr,
 			&i.PaymentRequest,
-			&i.PaymentRequestHash,
 			&i.State,
 			&i.AmountPaidMsat,
 			&i.IsAmp,
@@ -256,7 +373,7 @@ FROM invoice_features
 WHERE invoice_id = $1
 `
 
-func (q *Queries) GetInvoiceFeatures(ctx context.Context, invoiceID int64) ([]InvoiceFeature, error) {
+func (q *Queries) GetInvoiceFeatures(ctx context.Context, invoiceID int32) ([]InvoiceFeature, error) {
 	rows, err := q.db.QueryContext(ctx, getInvoiceFeatures, invoiceID)
 	if err != nil {
 		return nil, err
@@ -291,7 +408,7 @@ type GetInvoiceHTLCCustomRecordsRow struct {
 	Value  []byte
 }
 
-func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int64) ([]GetInvoiceHTLCCustomRecordsRow, error) {
+func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) ([]GetInvoiceHTLCCustomRecordsRow, error) {
 	rows, err := q.db.QueryContext(ctx, getInvoiceHTLCCustomRecords, invoiceID)
 	if err != nil {
 		return nil, err
@@ -315,12 +432,12 @@ func (q *Queries) GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int
 }
 
 const getInvoiceHTLCs = `-- name: GetInvoiceHTLCs :many
-SELECT id, chan_id, htlc_id, amount_msat, total_mpp_msat, accept_height, accept_time, expiry_height, state, resolve_time, invoice_id
+SELECT id, htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time, expiry_height, state, resolve_time, invoice_id
 FROM invoice_htlcs
 WHERE invoice_id = $1
 `
 
-func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]InvoiceHtlc, error) {
+func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]InvoiceHtlc, error) {
 	rows, err := q.db.QueryContext(ctx, getInvoiceHTLCs, invoiceID)
 	if err != nil {
 		return nil, err
@@ -331,8 +448,8 @@ func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]Invoi
 		var i InvoiceHtlc
 		if err := rows.Scan(
 			&i.ID,
-			&i.ChanID,
 			&i.HtlcID,
+			&i.ChanID,
 			&i.AmountMsat,
 			&i.TotalMppMsat,
 			&i.AcceptHeight,
@@ -355,35 +472,68 @@ func (q *Queries) GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]Invoi
 	return items, nil
 }
 
+const getInvoicePayments = `-- name: GetInvoicePayments :many
+SELECT id, settled_at, amount_paid_msat, invoice_id
+FROM invoice_payments
+WHERE invoice_id = $1
+`
+
+func (q *Queries) GetInvoicePayments(ctx context.Context, invoiceID int32) ([]InvoicePayment, error) {
+	rows, err := q.db.QueryContext(ctx, getInvoicePayments, invoiceID)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []InvoicePayment
+	for rows.Next() {
+		var i InvoicePayment
+		if err := rows.Scan(
+			&i.ID,
+			&i.SettledAt,
+			&i.AmountPaidMsat,
+			&i.InvoiceID,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
+}
+
 const insertInvoice = `-- name: InsertInvoice :one
 INSERT INTO invoices (
     hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, 
-    payment_request, payment_request_hash, state, amount_paid_msat, is_amp,
-    is_hodl, is_keysend, created_at
+    payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, 
+    created_at
 ) VALUES (
-    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
+    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14
 ) RETURNING id
 `
 
 type InsertInvoiceParams struct {
-	Hash               []byte
-	Preimage           []byte
-	Memo               sql.NullString
-	AmountMsat         int64
-	CltvDelta          sql.NullInt32
-	Expiry             int32
-	PaymentAddr        []byte
-	PaymentRequest     sql.NullString
-	PaymentRequestHash []byte
-	State              int16
-	AmountPaidMsat     int64
-	IsAmp              bool
-	IsHodl             bool
-	IsKeysend          bool
-	CreatedAt          time.Time
-}
-
-func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error) {
+	Hash           []byte
+	Preimage       []byte
+	Memo           sql.NullString
+	AmountMsat     int64
+	CltvDelta      sql.NullInt32
+	Expiry         int32
+	PaymentAddr    []byte
+	PaymentRequest sql.NullString
+	State          int16
+	AmountPaidMsat int64
+	IsAmp          bool
+	IsHodl         bool
+	IsKeysend      bool
+	CreatedAt      time.Time
+}
+
+func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int32, error) {
 	row := q.db.QueryRowContext(ctx, insertInvoice,
 		arg.Hash,
 		arg.Preimage,
@@ -393,7 +543,6 @@ func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (i
 		arg.Expiry,
 		arg.PaymentAddr,
 		arg.PaymentRequest,
-		arg.PaymentRequestHash,
 		arg.State,
 		arg.AmountPaidMsat,
 		arg.IsAmp,
@@ -401,7 +550,7 @@ func (q *Queries) InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (i
 		arg.IsKeysend,
 		arg.CreatedAt,
 	)
-	var id int64
+	var id int32
 	err := row.Scan(&id)
 	return id, err
 }
@@ -415,7 +564,7 @@ INSERT INTO invoice_features (
 `
 
 type InsertInvoiceFeatureParams struct {
-	InvoiceID int64
+	InvoiceID int32
 	Feature   int32
 }
 
@@ -424,13 +573,13 @@ func (q *Queries) InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFea
 	return err
 }
 
-const insertInvoiceHTLC = `-- name: InsertInvoiceHTLC :one
+const insertInvoiceHTLC = `-- name: InsertInvoiceHTLC :exec
 INSERT INTO invoice_htlcs (
     htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time,
     expiry_height, state, resolve_time, invoice_id
 ) VALUES (
     $1, $2, $3, $4, $5, $6, $7, $8, $9, $10
-) RETURNING id
+)
 `
 
 type InsertInvoiceHTLCParams struct {
@@ -443,11 +592,11 @@ type InsertInvoiceHTLCParams struct {
 	ExpiryHeight int32
 	State        int16
 	ResolveTime  sql.NullTime
-	InvoiceID    int64
+	InvoiceID    int32
 }
 
-func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error) {
-	row := q.db.QueryRowContext(ctx, insertInvoiceHTLC,
+func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) error {
+	_, err := q.db.ExecContext(ctx, insertInvoiceHTLC,
 		arg.HtlcID,
 		arg.ChanID,
 		arg.AmountMsat,
@@ -459,9 +608,7 @@ func (q *Queries) InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCPa
 		arg.ResolveTime,
 		arg.InvoiceID,
 	)
-	var id int64
-	err := row.Scan(&id)
-	return id, err
+	return err
 }
 
 const insertInvoiceHTLCCustomRecord = `-- name: InsertInvoiceHTLCCustomRecord :exec
@@ -483,56 +630,64 @@ func (q *Queries) InsertInvoiceHTLCCustomRecord(ctx context.Context, arg InsertI
 	return err
 }
 
-const nextInvoiceSettleIndex = `-- name: NextInvoiceSettleIndex :one
-UPDATE invoice_sequences SET current_value = current_value + 1
-WHERE name = 'settle_index'
-RETURNING current_value
+const insertInvoicePayment = `-- name: InsertInvoicePayment :one
+INSERT INTO invoice_payments (
+    invoice_id, amount_paid_msat, settled_at 
+) VALUES (
+    $1, $2, $3
+) RETURNING id
 `
 
-func (q *Queries) NextInvoiceSettleIndex(ctx context.Context) (int64, error) {
-	row := q.db.QueryRowContext(ctx, nextInvoiceSettleIndex)
-	var current_value int64
-	err := row.Scan(&current_value)
-	return current_value, err
+type InsertInvoicePaymentParams struct {
+	InvoiceID      int32
+	AmountPaidMsat int64
+	SettledAt      time.Time
 }
 
-const updateInvoiceAmountPaid = `-- name: UpdateInvoiceAmountPaid :execresult
-UPDATE invoices
-SET amount_paid_msat = $2
-WHERE id = $1
+func (q *Queries) InsertInvoicePayment(ctx context.Context, arg InsertInvoicePaymentParams) (int32, error) {
+	row := q.db.QueryRowContext(ctx, insertInvoicePayment, arg.InvoiceID, arg.AmountPaidMsat, arg.SettledAt)
+	var id int32
+	err := row.Scan(&id)
+	return id, err
+}
+
+const updateInvoice = `-- name: UpdateInvoice :exec
+UPDATE invoices 
+SET preimage=$2, state=$3, amount_paid_msat=$4
+WHERE id=$1
 `
 
-type UpdateInvoiceAmountPaidParams struct {
-	ID             int64
+type UpdateInvoiceParams struct {
+	ID             int32
+	Preimage       []byte
+	State          int16
 	AmountPaidMsat int64
 }
 
-func (q *Queries) UpdateInvoiceAmountPaid(ctx context.Context, arg UpdateInvoiceAmountPaidParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, updateInvoiceAmountPaid, arg.ID, arg.AmountPaidMsat)
+func (q *Queries) UpdateInvoice(ctx context.Context, arg UpdateInvoiceParams) error {
+	_, err := q.db.ExecContext(ctx, updateInvoice,
+		arg.ID,
+		arg.Preimage,
+		arg.State,
+		arg.AmountPaidMsat,
+	)
+	return err
 }
 
 const updateInvoiceHTLC = `-- name: UpdateInvoiceHTLC :exec
 UPDATE invoice_htlcs 
-SET state=$4, resolve_time=$5
-WHERE htlc_id = $1 AND chan_id = $2 AND invoice_id = $3
+SET state=$2, resolve_time=$3
+WHERE id = $1
 `
 
 type UpdateInvoiceHTLCParams struct {
-	HtlcID      int64
-	ChanID      string
-	InvoiceID   int64
+	ID          int32
 	State       int16
 	ResolveTime sql.NullTime
 }
 
 func (q *Queries) UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error {
-	_, err := q.db.ExecContext(ctx, updateInvoiceHTLC,
-		arg.HtlcID,
-		arg.ChanID,
-		arg.InvoiceID,
-		arg.State,
-		arg.ResolveTime,
-	)
+	_, err := q.db.ExecContext(ctx, updateInvoiceHTLC, arg.ID, arg.State, arg.ResolveTime)
 	return err
 }
 
@@ -543,7 +698,7 @@ WHERE invoice_id = $1 AND resolve_time IS NULL
 `
 
 type UpdateInvoiceHTLCsParams struct {
-	InvoiceID   int64
+	InvoiceID   int32
 	State       int16
 	ResolveTime sql.NullTime
 }
@@ -552,30 +707,3 @@ func (q *Queries) UpdateInvoiceHTLCs(ctx context.Context, arg UpdateInvoiceHTLCs
 	_, err := q.db.ExecContext(ctx, updateInvoiceHTLCs, arg.InvoiceID, arg.State, arg.ResolveTime)
 	return err
 }
-
-const updateInvoiceState = `-- name: UpdateInvoiceState :execresult
-UPDATE invoices
-SET state = $2,
-    preimage = COALESCE(preimage, $3),
-    settle_index = COALESCE(settle_index, $4),
-    settled_at = COALESCE(settled_at, $5)
-WHERE id = $1
-`
-
-type UpdateInvoiceStateParams struct {
-	ID          int64
-	State       int16
-	Preimage    []byte
-	SettleIndex sql.NullInt64
-	SettledAt   sql.NullTime
-}
-
-func (q *Queries) UpdateInvoiceState(ctx context.Context, arg UpdateInvoiceStateParams) (sql.Result, error) {
-	return q.db.ExecContext(ctx, updateInvoiceState,
-		arg.ID,
-		arg.State,
-		arg.Preimage,
-		arg.SettleIndex,
-		arg.SettledAt,
-	)
-}
diff --git a/sqldb/sqlc/migrations/000001_invoices.up.sql b/sqldb/sqlc/migrations/000001_invoices.up.sql
index e4c80a26f..32b70b9fa 100644
--- a/sqldb/sqlc/migrations/000001_invoices.up.sql
+++ b/sqldb/sqlc/migrations/000001_invoices.up.sql
@@ -1,17 +1,7 @@
--- sequences contains all sequences used for invoices.
-CREATE TABLE invoice_sequences (
-    name TEXT PRIMARY KEY,
-    current_value BIGINT NOT NULL
-);
-
--- Initialize a sequence for the settled index used to track invoice settlement
--- to remain compatible with the legacy channeldb implementation.
-INSERT INTO invoice_sequences(name, current_value) VALUES ('settle_index', 0);
-
 -- invoices table contains all the information shared by all the invoice types. 
 CREATE TABLE IF NOT EXISTS invoices (
     -- The id of the invoice. Translates to the AddIndex.
-    id BIGINT PRIMARY KEY,
+    id INTEGER PRIMARY KEY,
 
     -- The hash for this invoice. The invoice hash will always identify that 
     -- invoice.
@@ -20,14 +10,6 @@ CREATE TABLE IF NOT EXISTS invoices (
     -- The preimage for the hash in this invoice. Some invoices may have this 
     -- field empty, like unsettled hodl invoices or AMP invoices.
     preimage BLOB,
-    
-    -- If settled, the index is set to the current_value+1 of the settle_index
-    -- seuqence in the invoice_sequences table. If not settled, then it is set
-    -- to NULL.
-    settle_index BIGINT,
-
-    -- When the invoice was settled.
-    settled_at TIMESTAMP,
 
     -- An optional memo to attach along with the invoice. 
     memo TEXT,
@@ -49,12 +31,7 @@ CREATE TABLE IF NOT EXISTS invoices (
 
     -- The encoded payment request for this invoice. Some invoice types may 
     -- not have leave this empty, like Keysends.
-    payment_request TEXT,
-
-    -- Holds the hash of the payment request. This field is used to ensure that
-    -- there are no duplicates in the database. This trick is needed because
-    -- PostgreSQL has a limitiation of 2712 bytes for unique index rows.
-    payment_request_hash BLOB UNIQUE,
+    payment_request TEXT UNIQUE, 
 
     -- The invoice state.
     state SMALLINT NOT NULL,
@@ -81,7 +58,6 @@ CREATE INDEX IF NOT EXISTS invoices_preimage_idx ON invoices(preimage);
 CREATE INDEX IF NOT EXISTS invoices_payment_addr_idx ON invoices(payment_addr);
 CREATE INDEX IF NOT EXISTS invoices_state_idx ON invoices(state);
 CREATE INDEX IF NOT EXISTS invoices_created_at_idx ON invoices(created_at);
-CREATE INDEX IF NOT EXISTS invoices_settled_at_idx ON invoices(settled_at);
 
 -- invoice_features contains the feature bits of an invoice.
 CREATE TABLE IF NOT EXISTS invoice_features (
@@ -89,7 +65,7 @@ CREATE TABLE IF NOT EXISTS invoice_features (
     feature INTEGER NOT NULL,
 
     -- The invoice id this feature belongs to.
-    invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id),
 
     -- The feature bit is unique per invoice.
     UNIQUE (feature, invoice_id)
@@ -102,15 +78,15 @@ CREATE INDEX IF NOT EXISTS invoice_feature_invoice_id_idx ON invoice_features(in
 CREATE TABLE IF NOT EXISTS invoice_htlcs (
     -- The id for this htlc. Used in foreign keys instead of the 
     -- htlc_id/chan_id combination.
-    id BIGINT PRIMARY KEY,
-    
-    -- Short chan id indicating the htlc's origin. uint64 stored as text.
-    chan_id TEXT NOT NULL, 
+    id INTEGER PRIMARY KEY,
 
     -- The uint64 htlc id. This field is a counter so it is safe to store it as 
     -- int64 in the database. The application layer must check that there is no 
     -- overflow when storing/loading this column.
     htlc_id BIGINT NOT NULL,
+
+    -- Short chan id indicating the htlc's origin. uint64 stored as text.
+    chan_id TEXT NOT NULL, 
     
     -- The htlc's amount in millisatoshis.
     amount_msat BIGINT NOT NULL,
@@ -134,10 +110,10 @@ CREATE TABLE IF NOT EXISTS invoice_htlcs (
     resolve_time TIMESTAMP,
 
     -- The id of the invoice this htlc belongs to.
-    invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id),
 
     -- The htlc_id and chan_id identify the htlc.
-    UNIQUE (chan_id, htlc_id)
+    UNIQUE (htlc_id, chan_id)
 );
 
 CREATE INDEX IF NOT EXISTS invoice_htlc_invoice_id_idx ON invoice_htlcs(invoice_id);
@@ -153,8 +129,26 @@ CREATE TABLE IF NOT EXISTS invoice_htlc_custom_records (
     value BLOB NOT NULL,
 
     -- The htlc id this record belongs to. 
-    htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id) ON DELETE CASCADE
+    htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id)
 );
 
 CREATE INDEX IF NOT EXISTS invoice_htlc_custom_records_htlc_id_idx ON invoice_htlc_custom_records(htlc_id);
 
+-- invoice_payments contains the information of a settled invoice payment. 
+CREATE TABLE IF NOT EXISTS invoice_payments (
+    -- The id for this invoice payment. Translates to SettleIndex.
+    id INTEGER PRIMARY KEY,
+    
+    -- When the payment was settled.
+    settled_at TIMESTAMP NOT NULL,
+
+    -- The amount of the payment in millisatoshis. This is the sum of all the 
+    -- the htlcs settled for this payment.
+    amount_paid_msat BIGINT NOT NULL,
+
+    -- The invoice id this payment is for.
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id)
+);
+
+CREATE INDEX IF NOT EXISTS invoice_payments_settled_at_idx ON invoice_payments(settled_at);
+CREATE INDEX IF NOT EXISTS invoice_payments_invoice_id_idx ON invoice_payments(invoice_id);
diff --git a/sqldb/sqlc/migrations/000002_amp_invoices.up.sql b/sqldb/sqlc/migrations/000002_amp_invoices.up.sql
index 7204e775b..a4daa8c30 100644
--- a/sqldb/sqlc/migrations/000002_amp_invoices.up.sql
+++ b/sqldb/sqlc/migrations/000002_amp_invoices.up.sql
@@ -1,6 +1,5 @@
--- amp_sub_invoices holds all AMP sub-invoices that belong to a given parent
--- invoice.
-CREATE TABLE IF NOT EXISTS amp_sub_invoices (
+-- amp_invoices_payments  
+CREATE TABLE IF NOT EXISTS amp_invoice_payments (
     -- The set id identifying the payment. 
     set_id BLOB PRIMARY KEY,
 
@@ -11,35 +10,26 @@ CREATE TABLE IF NOT EXISTS amp_sub_invoices (
     -- Timestamp of when the first htlc for this payment was accepted.
     created_at TIMESTAMP NOT NULL,
 
-    -- When the invoice was settled.
-    settled_at TIMESTAMP,
+    -- If settled, the invoice payment related to this set id.
+    settled_index INTEGER REFERENCES invoice_payments(id),
 
-    -- If settled, the index is set to the current_value+1 of the settle_index
-    -- seuqence in the invoice_sequences table. If not settled, then it is set
-    -- to NULL.
-    settle_index BIGINT,
-
-    -- The id of the parent invoice this AMP invoice belongs to.
-    invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-
-    -- A unique constraint on the set_id and invoice_id is needed to ensure that
-    -- we don't have two AMP invoices for the same invoice.
-    UNIQUE (set_id, invoice_id)
+    -- The invoice id this set id is related to.
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id)
 );
 
-CREATE INDEX IF NOT EXISTS amp_sub_invoices_invoice_id_idx ON amp_sub_invoices(invoice_id);
+CREATE INDEX IF NOT EXISTS amp_invoice_payments_invoice_id_idx ON amp_invoice_payments(invoice_id);
 
 -- amp_invoice_htlcs contains the complementary information for an htlc related 
 -- to an AMP invoice.
-CREATE TABLE IF NOT EXISTS amp_sub_invoice_htlcs (
-    -- The invoice id this entry is related to.
-    invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
-
+CREATE TABLE IF NOT EXISTS amp_invoice_htlcs (
     -- The set id identifying the payment this htlc belongs to.
-    set_id BLOB NOT NULL REFERENCES amp_sub_invoices(set_id) ON DELETE CASCADE,
+    set_id BLOB NOT NULL REFERENCES amp_invoice_payments(set_id),
 
     -- The id of the htlc this entry blongs to.
-    htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id) ON DELETE CASCADE,
+    htlc_id BIGINT NOT NULL REFERENCES invoice_htlcs(id),
+
+    -- The invoice id this entry is related to.
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id),
 
     -- The root share for this amp htlc.
     root_share BLOB NOT NULL,
@@ -58,7 +48,7 @@ CREATE TABLE IF NOT EXISTS amp_sub_invoice_htlcs (
     preimage BLOB 
 );
 
-CREATE INDEX IF NOT EXISTS amp_htlcs_invoice_id_idx ON amp_sub_invoice_htlcs(invoice_id);
-CREATE INDEX IF NOT EXISTS amp_htlcs_set_id_idx ON amp_sub_invoice_htlcs(set_id);
-CREATE INDEX IF NOT EXISTS amp_htlcs_htlc_id_idx ON amp_sub_invoice_htlcs(htlc_id);
+CREATE INDEX IF NOT EXISTS amp_htlcs_set_id_idx ON amp_invoice_htlcs(set_id);
+CREATE INDEX IF NOT EXISTS amp_htlcs_invoice_id_idx ON amp_invoice_htlcs(invoice_id);
+CREATE INDEX IF NOT EXISTS amp_htlcs_htlc_id_idx ON amp_invoice_htlcs(htlc_id);
 
diff --git a/sqldb/sqlc/migrations/000003_invoice_events.down.sql b/sqldb/sqlc/migrations/000003_invoice_events.down.sql
index 3f447dfa8..66e554e24 100644
--- a/sqldb/sqlc/migrations/000003_invoice_events.down.sql
+++ b/sqldb/sqlc/migrations/000003_invoice_events.down.sql
@@ -1,6 +1,9 @@
 DROP TABLE IF EXISTS invoice_events;
-DROP INDEX IF EXISTS invoice_events_added_at_idx;
-DROP INDEX IF EXISTS invoice_events_event_type_idx;
+
+DROP INDEX IF EXISTS invoice_events_created_at_idx;
 DROP INDEX IF EXISTS invoice_events_invoice_id_idx;
+DROP INDEX IF EXISTS invoice_events_htlc_id_idx;
 DROP INDEX IF EXISTS invoice_events_set_id_idx;
+DROP INDEX IF EXISTS invoice_events_event_type_idx;
+
 DROP TABLE IF EXISTS invoice_event_types;
diff --git a/sqldb/sqlc/migrations/000003_invoice_events.up.sql b/sqldb/sqlc/migrations/000003_invoice_events.up.sql
index a3718c0eb..47d2a91d0 100644
--- a/sqldb/sqlc/migrations/000003_invoice_events.up.sql
+++ b/sqldb/sqlc/migrations/000003_invoice_events.up.sql
@@ -1,51 +1,59 @@
--- invoice_event_types stores the different types of invoice events. 
+-- invoice_event_types stores the different types of events that can be emitted 
+-- for invoices.
 CREATE TABLE IF NOT EXISTS invoice_event_types(
     id INTEGER PRIMARY KEY,
 
     description TEXT NOT NULL
 );
 
--- invoice_event_types defines the different types of invoice events.
-INSERT INTO invoice_event_types (id, description) 
-VALUES 
-    -- invoice_created is the event type used when an invoice is created.
-    (0, 'invoice_created'), 
-    -- invoice_canceled is the event type used when an invoice is canceled.
-    (1, 'invoice_canceled'), 
-    -- invoice_settled is the event type used when an invoice is settled.
-    (2, 'invoice_settled'),
-    -- setid_created is the event type used when an AMP sub invoice
-    -- corresponding to the set_id is created.
-    (3, 'setid_created'),
-    -- setid_canceled is the event type used when an AMP sub invoice
-    -- corresponding to the set_id is canceled.
-    (4, 'setid_canceled'),
-    -- setid_settled is the event type used when an AMP sub invoice 
-    -- corresponding to the set_id is settled.
-    (5, 'setid_settled');
-
-
--- invoice_events stores all major events related to the node's invoices and
--- AMP sub invoices. This table can be used to create a historical view of what
--- happened to the node's invoices.
+-- invoice_events stores all events related to the node invoices.
 CREATE TABLE IF NOT EXISTS invoice_events (
-    id BIGINT PRIMARY KEY,
+    id INTEGER PRIMARY KEY,
 
-    -- added_at is the timestamp when this event was added.
-    added_at TIMESTAMP NOT NULL,
+    -- created_at is the creation time of this event.
+    created_at TIMESTAMP NOT NULL,
 
-    -- event_type is the type of this event.
-    event_type INTEGER NOT NULL REFERENCES invoice_event_types(id) ON DELETE CASCADE,
+    -- invoice_id is the reference to the invoice this event was emitted for.
+    invoice_id INTEGER NOT NULL REFERENCES invoices(id),
+
+    -- htlc_id is the reference to the htlc this event was emitted for, may be 
+    -- null.
+    htlc_id BIGINT REFERENCES invoice_htlcs(htlc_id),
 
-    -- invoice_id is the reference to the invoice this event was added for.
-    invoice_id BIGINT NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
+    -- set_id is the reference to the set_id this event was emitted for, may be
+    -- null.
+    set_id BLOB NOT NULL REFERENCES amp_invoice_payments(set_id),
+
+    -- event_type is the type of this event.
+    event_type INTEGER NOT NULL REFERENCES invoice_event_types(id),
 
-    -- set_id is the reference to the AMP sub invoice this event was added for.
-    -- May be NULL if the event is not related to an AMP sub invoice.
-    set_id BLOB REFERENCES amp_sub_invoices(set_id) ON DELETE CASCADE
+    -- event_metadata is a TLV encoding any relevant information for this kind 
+    -- of events.
+    event_metadata BLOB
 );
 
-CREATE INDEX IF NOT EXISTS invoice_events_added_at_idx ON invoice_events(added_at);
-CREATE INDEX IF NOT EXISTS invoice_events_event_type_idx ON invoice_events(event_type);
+CREATE INDEX IF NOT EXISTS invoice_events_created_at_idx ON invoice_events(created_at);
 CREATE INDEX IF NOT EXISTS invoice_events_invoice_id_idx ON invoice_events(invoice_id);
+CREATE INDEX IF NOT EXISTS invoice_events_htlc_id_idx ON invoice_events(htlc_id);
 CREATE INDEX IF NOT EXISTS invoice_events_set_id_idx ON invoice_events(set_id);
+CREATE INDEX IF NOT EXISTS invoice_events_event_type_idx ON invoice_events(event_type);
+
+
+-- invoice_event_types defines the different types of events that can be emitted
+-- for an invoice.
+INSERT INTO invoice_event_types (id, description) 
+VALUES 
+    -- invoice_created is the event emitted when an invoice is created.
+    (0, 'invoice_created'), 
+    -- invoice_canceled is the event emitted when an invoice is canceled.
+    (1, "invoice_canceled"), 
+    -- invoice_settled is the event emitted when an invoice is settled.
+    (2, "invoice_settled"),
+    -- setid_created is the event emitted when the first htlc for the set_id is 
+    -- received. 
+    (3, "setid_created"),
+    -- setid_canceled is the event emitted when the set_id is canceled.
+    (4, "setid_canceled"),
+    -- setid_settled is the event emitted when the set_id is settled.
+    (5, "setid_settled");
+
diff --git a/sqldb/sqlc/models.go b/sqldb/sqlc/models.go
index 83be5a708..b0ef3c857 100644
--- a/sqldb/sqlc/models.go
+++ b/sqldb/sqlc/models.go
@@ -1,6 +1,6 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 
 package sqlc
 
@@ -9,52 +9,50 @@ import (
 	"time"
 )
 
-type AmpSubInvoice struct {
-	SetID       []byte
-	State       int16
-	CreatedAt   time.Time
-	SettledAt   sql.NullTime
-	SettleIndex sql.NullInt64
-	InvoiceID   int64
-}
-
-type AmpSubInvoiceHtlc struct {
-	InvoiceID  int64
+type AmpInvoiceHtlc struct {
 	SetID      []byte
 	HtlcID     int64
+	InvoiceID  int32
 	RootShare  []byte
 	ChildIndex int64
 	Hash       []byte
 	Preimage   []byte
 }
 
+type AmpInvoicePayment struct {
+	SetID        []byte
+	State        int16
+	CreatedAt    time.Time
+	SettledIndex sql.NullInt32
+	InvoiceID    int32
+}
+
 type Invoice struct {
-	ID                 int64
-	Hash               []byte
-	Preimage           []byte
-	SettleIndex        sql.NullInt64
-	SettledAt          sql.NullTime
-	Memo               sql.NullString
-	AmountMsat         int64
-	CltvDelta          sql.NullInt32
-	Expiry             int32
-	PaymentAddr        []byte
-	PaymentRequest     sql.NullString
-	PaymentRequestHash []byte
-	State              int16
-	AmountPaidMsat     int64
-	IsAmp              bool
-	IsHodl             bool
-	IsKeysend          bool
-	CreatedAt          time.Time
+	ID             int32
+	Hash           []byte
+	Preimage       []byte
+	Memo           sql.NullString
+	AmountMsat     int64
+	CltvDelta      sql.NullInt32
+	Expiry         int32
+	PaymentAddr    []byte
+	PaymentRequest sql.NullString
+	State          int16
+	AmountPaidMsat int64
+	IsAmp          bool
+	IsHodl         bool
+	IsKeysend      bool
+	CreatedAt      time.Time
 }
 
 type InvoiceEvent struct {
-	ID        int64
-	AddedAt   time.Time
-	EventType int32
-	InvoiceID int64
-	SetID     []byte
+	ID            int32
+	CreatedAt     time.Time
+	InvoiceID     int32
+	HtlcID        sql.NullInt64
+	SetID         []byte
+	EventType     int32
+	EventMetadata []byte
 }
 
 type InvoiceEventType struct {
@@ -64,13 +62,13 @@ type InvoiceEventType struct {
 
 type InvoiceFeature struct {
 	Feature   int32
-	InvoiceID int64
+	InvoiceID int32
 }
 
 type InvoiceHtlc struct {
-	ID           int64
-	ChanID       string
+	ID           int32
 	HtlcID       int64
+	ChanID       string
 	AmountMsat   int64
 	TotalMppMsat sql.NullInt64
 	AcceptHeight int32
@@ -78,7 +76,7 @@ type InvoiceHtlc struct {
 	ExpiryHeight int32
 	State        int16
 	ResolveTime  sql.NullTime
-	InvoiceID    int64
+	InvoiceID    int32
 }
 
 type InvoiceHtlcCustomRecord struct {
@@ -87,7 +85,9 @@ type InvoiceHtlcCustomRecord struct {
 	HtlcID int64
 }
 
-type InvoiceSequence struct {
-	Name         string
-	CurrentValue int64
+type InvoicePayment struct {
+	ID             int32
+	SettledAt      time.Time
+	AmountPaidMsat int64
+	InvoiceID      int32
 }
diff --git a/sqldb/sqlc/querier.go b/sqldb/sqlc/querier.go
index d55d8090a..1e766ac63 100644
--- a/sqldb/sqlc/querier.go
+++ b/sqldb/sqlc/querier.go
@@ -1,48 +1,51 @@
 // Code generated by sqlc. DO NOT EDIT.
 // versions:
-//   sqlc v1.25.0
+//   sqlc v1.18.0
 
 package sqlc
 
 import (
 	"context"
-	"database/sql"
 )
 
 type Querier interface {
-	DeleteCanceledInvoices(ctx context.Context) (sql.Result, error)
-	DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) (sql.Result, error)
-	FetchAMPSubInvoiceHTLCs(ctx context.Context, arg FetchAMPSubInvoiceHTLCsParams) ([]FetchAMPSubInvoiceHTLCsRow, error)
-	FetchAMPSubInvoices(ctx context.Context, arg FetchAMPSubInvoicesParams) ([]AmpSubInvoice, error)
-	FetchSettledAMPSubInvoices(ctx context.Context, arg FetchSettledAMPSubInvoicesParams) ([]FetchSettledAMPSubInvoicesRow, error)
+	DeleteAMPHTLCCustomRecords(ctx context.Context, invoiceID int32) error
+	DeleteAMPHTLCs(ctx context.Context, invoiceID int32) error
+	DeleteAMPInvoiceHTLC(ctx context.Context, setID []byte) error
+	DeleteInvoice(ctx context.Context, arg DeleteInvoiceParams) error
+	DeleteInvoiceEvents(ctx context.Context, invoiceID int32) error
+	DeleteInvoiceFeatures(ctx context.Context, invoiceID int32) error
+	DeleteInvoiceHTLC(ctx context.Context, htlcID int64) error
+	DeleteInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) error
+	DeleteInvoiceHTLCs(ctx context.Context, invoiceID int32) error
+	FilterInvoicePayments(ctx context.Context, arg FilterInvoicePaymentsParams) ([]FilterInvoicePaymentsRow, error)
 	FilterInvoices(ctx context.Context, arg FilterInvoicesParams) ([]Invoice, error)
-	GetAMPInvoiceID(ctx context.Context, setID []byte) (int64, error)
+	GetAMPInvoiceHTLCsByInvoiceID(ctx context.Context, invoiceID int32) ([]AmpInvoiceHtlc, error)
+	GetAMPInvoiceHTLCsBySetID(ctx context.Context, setID []byte) ([]AmpInvoiceHtlc, error)
 	// This method may return more than one invoice if filter using multiple fields
 	// from different invoices. It is the caller's responsibility to ensure that
 	// we bubble up an error in those cases.
 	GetInvoice(ctx context.Context, arg GetInvoiceParams) ([]Invoice, error)
-	GetInvoiceFeatures(ctx context.Context, invoiceID int64) ([]InvoiceFeature, error)
-	GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int64) ([]GetInvoiceHTLCCustomRecordsRow, error)
-	GetInvoiceHTLCs(ctx context.Context, invoiceID int64) ([]InvoiceHtlc, error)
-	InsertAMPSubInvoiceHTLC(ctx context.Context, arg InsertAMPSubInvoiceHTLCParams) error
-	InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int64, error)
+	GetInvoiceFeatures(ctx context.Context, invoiceID int32) ([]InvoiceFeature, error)
+	GetInvoiceHTLCCustomRecords(ctx context.Context, invoiceID int32) ([]GetInvoiceHTLCCustomRecordsRow, error)
+	GetInvoiceHTLCs(ctx context.Context, invoiceID int32) ([]InvoiceHtlc, error)
+	GetInvoicePayments(ctx context.Context, invoiceID int32) ([]InvoicePayment, error)
+	GetSetIDHTLCsCustomRecords(ctx context.Context, setID []byte) ([]GetSetIDHTLCsCustomRecordsRow, error)
+	InsertAMPInvoiceHTLC(ctx context.Context, arg InsertAMPInvoiceHTLCParams) error
+	InsertAMPInvoicePayment(ctx context.Context, arg InsertAMPInvoicePaymentParams) error
+	InsertInvoice(ctx context.Context, arg InsertInvoiceParams) (int32, error)
+	InsertInvoiceEvent(ctx context.Context, arg InsertInvoiceEventParams) error
 	InsertInvoiceFeature(ctx context.Context, arg InsertInvoiceFeatureParams) error
-	InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) (int64, error)
+	InsertInvoiceHTLC(ctx context.Context, arg InsertInvoiceHTLCParams) error
 	InsertInvoiceHTLCCustomRecord(ctx context.Context, arg InsertInvoiceHTLCCustomRecordParams) error
-	NextInvoiceSettleIndex(ctx context.Context) (int64, error)
-	OnAMPSubInvoiceCanceled(ctx context.Context, arg OnAMPSubInvoiceCanceledParams) error
-	OnAMPSubInvoiceCreated(ctx context.Context, arg OnAMPSubInvoiceCreatedParams) error
-	OnAMPSubInvoiceSettled(ctx context.Context, arg OnAMPSubInvoiceSettledParams) error
-	OnInvoiceCanceled(ctx context.Context, arg OnInvoiceCanceledParams) error
-	OnInvoiceCreated(ctx context.Context, arg OnInvoiceCreatedParams) error
-	OnInvoiceSettled(ctx context.Context, arg OnInvoiceSettledParams) error
-	UpdateAMPSubInvoiceHTLCPreimage(ctx context.Context, arg UpdateAMPSubInvoiceHTLCPreimageParams) (sql.Result, error)
-	UpdateAMPSubInvoiceState(ctx context.Context, arg UpdateAMPSubInvoiceStateParams) error
-	UpdateInvoiceAmountPaid(ctx context.Context, arg UpdateInvoiceAmountPaidParams) (sql.Result, error)
+	InsertInvoicePayment(ctx context.Context, arg InsertInvoicePaymentParams) (int32, error)
+	SelectAMPInvoicePayments(ctx context.Context, arg SelectAMPInvoicePaymentsParams) ([]SelectAMPInvoicePaymentsRow, error)
+	SelectInvoiceEvents(ctx context.Context, arg SelectInvoiceEventsParams) ([]InvoiceEvent, error)
+	UpdateAMPInvoiceHTLC(ctx context.Context, arg UpdateAMPInvoiceHTLCParams) error
+	UpdateAMPPayment(ctx context.Context, arg UpdateAMPPaymentParams) error
+	UpdateInvoice(ctx context.Context, arg UpdateInvoiceParams) error
 	UpdateInvoiceHTLC(ctx context.Context, arg UpdateInvoiceHTLCParams) error
 	UpdateInvoiceHTLCs(ctx context.Context, arg UpdateInvoiceHTLCsParams) error
-	UpdateInvoiceState(ctx context.Context, arg UpdateInvoiceStateParams) (sql.Result, error)
-	UpsertAMPSubInvoice(ctx context.Context, arg UpsertAMPSubInvoiceParams) (sql.Result, error)
 }
 
 var _ Querier = (*Queries)(nil)
diff --git a/sqldb/sqlc/queries/amp_invoices.sql b/sqldb/sqlc/queries/amp_invoices.sql
index 3b6ee76ac..d965de768 100644
--- a/sqldb/sqlc/queries/amp_invoices.sql
+++ b/sqldb/sqlc/queries/amp_invoices.sql
@@ -1,67 +1,79 @@
--- name: UpsertAMPSubInvoice :execresult
-INSERT INTO amp_sub_invoices (
-    set_id, state, created_at, invoice_id
+-- name: InsertAMPInvoicePayment :exec
+INSERT INTO amp_invoice_payments (
+    set_id, state, created_at, settled_index, invoice_id
 ) VALUES (
-    $1, $2, $3, $4
-) ON CONFLICT (set_id, invoice_id) DO NOTHING;
+    $1, $2, $3, $4, $5
+); 
 
--- name: GetAMPInvoiceID :one
-SELECT invoice_id FROM amp_sub_invoices WHERE set_id = $1;
+-- name: SelectAMPInvoicePayments :many
+SELECT aip.*, ip.*
+FROM amp_invoice_payments aip LEFT JOIN invoice_payments ip ON aip.settled_index = ip.id
+WHERE (
+    set_id = sqlc.narg('set_id') OR 
+    sqlc.narg('set_id') IS NULL
+) AND (
+    aip.settled_index = sqlc.narg('settled_index') OR 
+    sqlc.narg('settled_index') IS NULL
+) AND (
+    aip.invoice_id = sqlc.narg('invoice_id') OR 
+    sqlc.narg('invoice_id') IS NULL
+); 
 
--- name: UpdateAMPSubInvoiceState :exec
-UPDATE amp_sub_invoices
-SET state = $2, 
-    settle_index = COALESCE(settle_index, $3),
-    settled_at = COALESCE(settled_at, $4)
-WHERE set_id = $1; 
+-- name: UpdateAMPPayment :exec
+UPDATE amp_invoice_payments
+SET state = $1, settled_index = $2
+WHERE state = 0 AND (
+    set_id = sqlc.narg('set_id') OR 
+    sqlc.narg('set_id') IS NULL
+) AND (
+    invoice_id = sqlc.narg('invoice_id') OR 
+    sqlc.narg('invoice_id') IS NULL
+); 
 
--- name: InsertAMPSubInvoiceHTLC :exec
-INSERT INTO amp_sub_invoice_htlcs (
-    invoice_id, set_id, htlc_id, root_share, child_index, hash, preimage
+-- name: InsertAMPInvoiceHTLC :exec
+INSERT INTO amp_invoice_htlcs (
+    set_id, htlc_id, root_share, child_index, hash, preimage
 ) VALUES (
-    $1, $2, $3, $4, $5, $6, $7
+    $1, $2, $3, $4, $5, $6
 );
 
--- name: FetchAMPSubInvoices :many
+-- name: GetAMPInvoiceHTLCsBySetID :many
 SELECT *
-FROM amp_sub_invoices
-WHERE invoice_id = $1 
-AND (
-    set_id = sqlc.narg('set_id') OR 
-    sqlc.narg('set_id') IS NULL
-);
+FROM amp_invoice_htlcs
+WHERE set_id = $1;
 
--- name: FetchAMPSubInvoiceHTLCs :many
-SELECT 
-    amp.set_id, amp.root_share, amp.child_index, amp.hash, amp.preimage, 
-    invoice_htlcs.*
-FROM amp_sub_invoice_htlcs amp
-INNER JOIN invoice_htlcs ON amp.htlc_id = invoice_htlcs.id
-WHERE amp.invoice_id = $1
-AND (
-    set_id = sqlc.narg('set_id') OR 
-    sqlc.narg('set_id') IS NULL
-);
+-- name: GetAMPInvoiceHTLCsByInvoiceID :many
+SELECT *
+FROM amp_invoice_htlcs
+WHERE invoice_id = $1;
 
--- name: FetchSettledAMPSubInvoices :many
-SELECT 
-    a.set_id, 
-    a.settle_index as amp_settle_index, 
-    a.settled_at as amp_settled_at,
-    i.*
-FROM amp_sub_invoices a
-INNER JOIN invoices i ON a.invoice_id = i.id
-WHERE (
-    a.settle_index >= sqlc.narg('settle_index_get') OR
-    sqlc.narg('settle_index_get') IS NULL
-) AND (
-    a.settle_index <= sqlc.narg('settle_index_let') OR
-    sqlc.narg('settle_index_let') IS NULL
-);
+-- name: GetSetIDHTLCsCustomRecords :many
+SELECT ihcr.htlc_id, key, value
+FROM amp_invoice_htlcs aih JOIN invoice_htlc_custom_records ihcr ON aih.id=ihcr.htlc_id 
+WHERE aih.set_id = $1;
+
+-- name: UpdateAMPInvoiceHTLC :exec
+UPDATE amp_invoice_htlcs
+SET preimage = $1
+WHERE htlc_id = $2;
+
+-- name: DeleteAMPHTLCCustomRecords :exec
+WITH htlc_ids AS (
+    SELECT htlc_id
+    FROM amp_invoice_htlcs 
+    WHERE invoice_id = $1
+)
+DELETE
+FROM invoice_htlc_custom_records
+WHERE htlc_id IN (SELECT id FROM htlc_ids);
+
+-- name: DeleteAMPHTLCs :exec
+DELETE 
+FROM amp_invoice_htlcs  
+WHERE invoice_id = $1;
+
+-- name: DeleteAMPInvoiceHTLC :exec
+DELETE 
+FROM amp_invoice_htlcs
+WHERE set_id = $1;
 
--- name: UpdateAMPSubInvoiceHTLCPreimage :execresult
-UPDATE amp_sub_invoice_htlcs AS a
-SET preimage = $4
-WHERE a.invoice_id = $1 AND a.set_id = $2 AND a.htlc_id = (
-    SELECT id FROM invoice_htlcs AS i WHERE i.htlc_id = $3
-);
diff --git a/sqldb/sqlc/queries/invoice_events.sql b/sqldb/sqlc/queries/invoice_events.sql
index 4192c466b..4216a2513 100644
--- a/sqldb/sqlc/queries/invoice_events.sql
+++ b/sqldb/sqlc/queries/invoice_events.sql
@@ -1,41 +1,35 @@
--- name: OnInvoiceCreated :exec
+-- name: InsertInvoiceEvent :exec
 INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
+    created_at, invoice_id, htlc_id, set_id, event_type, event_metadata
 ) VALUES (
-    $1, 0, $2
+    $1, $2, $3, $4, $5, $6
 );
 
--- name: OnInvoiceCanceled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
-) VALUES (
-    $1, 1, $2
-);
-
--- name: OnInvoiceSettled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id
-) VALUES (
-    $1, 2, $2
-);
-
--- name: OnAMPSubInvoiceCreated :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
-) VALUES (
-    $1, 3, $2, $3
-);
+-- name: SelectInvoiceEvents :many
+SELECT *
+FROM invoice_events
+WHERE (
+    invoice_id = sqlc.narg('invoice_id') OR 
+    sqlc.narg('invoice_id') IS NULL
+) AND (
+    htlc_id = sqlc.narg('htlc_id') OR 
+    sqlc.narg('htlc_id') IS NULL
+) AND (
+    set_id = sqlc.narg('set_id') OR 
+    sqlc.narg('set_id') IS NULL
+) AND (
+    event_type = sqlc.narg('event_type') OR 
+    sqlc.narg('event_type') IS NULL
+) AND (
+    created_at >= sqlc.narg('created_after') OR 
+    sqlc.narg('created_after') IS NULL
+) AND (
+    created_at <= sqlc.narg('created_before') OR 
+    sqlc.narg('created_before') IS NULL
+) 
+LIMIT @num_limit OFFSET @num_offset;
 
--- name: OnAMPSubInvoiceCanceled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
-) VALUES (
-    $1, 4, $2, $3
-);
-
--- name: OnAMPSubInvoiceSettled :exec
-INSERT INTO invoice_events (
-    added_at, event_type, invoice_id, set_id
-) VALUES (
-    $1, 5, $2, $3
-);
+-- name: DeleteInvoiceEvents :exec
+DELETE
+FROM invoice_events
+WHERE invoice_id = $1;
diff --git a/sqldb/sqlc/queries/invoices.sql b/sqldb/sqlc/queries/invoices.sql
index c36976a25..335929e05 100644
--- a/sqldb/sqlc/queries/invoices.sql
+++ b/sqldb/sqlc/queries/invoices.sql
@@ -1,10 +1,10 @@
 -- name: InsertInvoice :one
 INSERT INTO invoices (
     hash, preimage, memo, amount_msat, cltv_delta, expiry, payment_addr, 
-    payment_request, payment_request_hash, state, amount_paid_msat, is_amp,
-    is_hodl, is_keysend, created_at
+    payment_request, state, amount_paid_msat, is_amp, is_hodl, is_keysend, 
+    created_at
 ) VALUES (
-    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15
+    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14
 ) RETURNING id;
 
 -- name: InsertInvoiceFeature :exec
@@ -19,36 +19,35 @@ SELECT *
 FROM invoice_features
 WHERE invoice_id = $1;
 
+-- name: DeleteInvoiceFeatures :exec
+DELETE 
+FROM invoice_features
+WHERE invoice_id = $1;
+
 -- This method may return more than one invoice if filter using multiple fields
 -- from different invoices. It is the caller's responsibility to ensure that 
 -- we bubble up an error in those cases.
 
 -- name: GetInvoice :many
-SELECT i.*
-FROM invoices i
-LEFT JOIN amp_sub_invoices a on i.id = a.invoice_id
+SELECT *
+FROM invoices
 WHERE (
-    i.id = sqlc.narg('add_index') OR 
+    id = sqlc.narg('add_index') OR 
     sqlc.narg('add_index') IS NULL
 ) AND (
-    i.hash = sqlc.narg('hash') OR 
+    hash = sqlc.narg('hash') OR 
     sqlc.narg('hash') IS NULL
 ) AND (
-    i.preimage = sqlc.narg('preimage') OR 
+    preimage = sqlc.narg('preimage') OR 
     sqlc.narg('preimage') IS NULL
 ) AND (
-    i.payment_addr = sqlc.narg('payment_addr') OR 
+    payment_addr = sqlc.narg('payment_addr') OR 
     sqlc.narg('payment_addr') IS NULL
-) AND (
-    a.set_id = sqlc.narg('set_id') OR
-    sqlc.narg('set_id') IS NULL
 )
-GROUP BY i.id
 LIMIT 2;
 
 -- name: FilterInvoices :many
-SELECT
-    invoices.*
+SELECT * 
 FROM invoices
 WHERE (
     id >= sqlc.narg('add_index_get') OR 
@@ -56,12 +55,6 @@ WHERE (
 ) AND (
     id <= sqlc.narg('add_index_let') OR 
     sqlc.narg('add_index_let') IS NULL
-) AND (
-    settle_index >= sqlc.narg('settle_index_get') OR
-    sqlc.narg('settle_index_get') IS NULL
-) AND (
-    settle_index <= sqlc.narg('settle_index_let') OR
-    sqlc.narg('settle_index_let') IS NULL
 ) AND (
     state = sqlc.narg('state') OR 
     sqlc.narg('state') IS NULL
@@ -88,25 +81,12 @@ ORDER BY
     END DESC
 LIMIT @num_limit OFFSET @num_offset;
 
--- name: UpdateInvoiceState :execresult
-UPDATE invoices
-SET state = $2,
-    preimage = COALESCE(preimage, $3),
-    settle_index = COALESCE(settle_index, $4),
-    settled_at = COALESCE(settled_at, $5)
-WHERE id = $1;
-
--- name: UpdateInvoiceAmountPaid :execresult
-UPDATE invoices
-SET amount_paid_msat = $2
-WHERE id = $1;
-
--- name: NextInvoiceSettleIndex :one
-UPDATE invoice_sequences SET current_value = current_value + 1
-WHERE name = 'settle_index'
-RETURNING current_value;
+-- name: UpdateInvoice :exec 
+UPDATE invoices 
+SET preimage=$2, state=$3, amount_paid_msat=$4
+WHERE id=$1;
 
--- name: DeleteInvoice :execresult
+-- name: DeleteInvoice :exec
 DELETE 
 FROM invoices 
 WHERE (
@@ -116,25 +96,20 @@ WHERE (
     hash = sqlc.narg('hash') OR 
     sqlc.narg('hash') IS NULL
 ) AND (
-    settle_index = sqlc.narg('settle_index') OR 
-    sqlc.narg('settle_index') IS NULL
+    preimage = sqlc.narg('preimage') OR 
+    sqlc.narg('preimage') IS NULL
 ) AND (
     payment_addr = sqlc.narg('payment_addr') OR
     sqlc.narg('payment_addr') IS NULL
 );
 
--- name: DeleteCanceledInvoices :execresult
-DELETE
-FROM invoices
-WHERE state = 2;
-
--- name: InsertInvoiceHTLC :one
+-- name: InsertInvoiceHTLC :exec
 INSERT INTO invoice_htlcs (
     htlc_id, chan_id, amount_msat, total_mpp_msat, accept_height, accept_time,
     expiry_height, state, resolve_time, invoice_id
 ) VALUES (
     $1, $2, $3, $4, $5, $6, $7, $8, $9, $10
-) RETURNING id;
+);
 
 -- name: GetInvoiceHTLCs :many
 SELECT *
@@ -143,14 +118,25 @@ WHERE invoice_id = $1;
 
 -- name: UpdateInvoiceHTLC :exec
 UPDATE invoice_htlcs 
-SET state=$4, resolve_time=$5
-WHERE htlc_id = $1 AND chan_id = $2 AND invoice_id = $3;
+SET state=$2, resolve_time=$3
+WHERE id = $1;
 
 -- name: UpdateInvoiceHTLCs :exec
 UPDATE invoice_htlcs 
 SET state=$2, resolve_time=$3
 WHERE invoice_id = $1 AND resolve_time IS NULL;
 
+
+-- name: DeleteInvoiceHTLC :exec
+DELETE
+FROM invoice_htlcs
+WHERE htlc_id = $1;
+
+-- name: DeleteInvoiceHTLCs :exec
+DELETE
+FROM invoice_htlcs
+WHERE invoice_id = $1;
+
 -- name: InsertInvoiceHTLCCustomRecord :exec
 INSERT INTO invoice_htlc_custom_records (
     key, value, htlc_id 
@@ -162,3 +148,49 @@ INSERT INTO invoice_htlc_custom_records (
 SELECT ihcr.htlc_id, key, value
 FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id 
 WHERE ih.invoice_id = $1;
+
+-- name: DeleteInvoiceHTLCCustomRecords :exec
+WITH htlc_ids AS (
+    SELECT ih.id
+    FROM invoice_htlcs ih JOIN invoice_htlc_custom_records ihcr ON ih.id=ihcr.htlc_id 
+    WHERE ih.invoice_id = $1
+)
+DELETE
+FROM invoice_htlc_custom_records
+WHERE htlc_id IN (SELECT id FROM htlc_ids);
+
+-- name: InsertInvoicePayment :one
+INSERT INTO invoice_payments (
+    invoice_id, amount_paid_msat, settled_at 
+) VALUES (
+    $1, $2, $3
+) RETURNING id;
+
+-- name: GetInvoicePayments :many
+SELECT *
+FROM invoice_payments
+WHERE invoice_id = $1;
+
+-- name: FilterInvoicePayments :many
+SELECT  
+    ip.id AS settle_index, ip.amount_paid_msat, ip.settled_at AS settle_date, 
+    i.* 
+FROM invoice_payments ip JOIN invoices i ON ip.invoice_id = i.id
+WHERE (
+    ip.id >= sqlc.narg('settle_index_get') OR 
+    sqlc.narg('settle_index_get') IS NULL
+) AND (
+    ip.settled_at >= sqlc.narg('settled_after') OR 
+    sqlc.narg('settled_after') IS NULL
+)
+ORDER BY
+    CASE
+        WHEN sqlc.narg('reverse') = FALSE THEN ip.id  
+        ELSE NULL
+    END ASC,
+    CASE
+        WHEN sqlc.narg('reverse') = TRUE THEN ip.id  
+        ELSE NULL
+    END DESC
+LIMIT @num_limit OFFSET @num_offset;
+
diff --git a/sqldb/sqlerrors.go b/sqldb/sqlerrors.go
index a939ec499..c595bd528 100644
--- a/sqldb/sqlerrors.go
+++ b/sqldb/sqlerrors.go
@@ -1,5 +1,3 @@
-//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))
-
 package sqldb
 
 import (
@@ -48,11 +46,6 @@ func parseSqliteError(sqliteErr *sqlite.Error) error {
 			DBError: sqliteErr,
 		}
 
-	case sqlite3.SQLITE_CONSTRAINT_PRIMARYKEY:
-		return &ErrSQLUniqueConstraintViolation{
-			DBError: sqliteErr,
-		}
-
 	// Database is currently busy, so we'll need to try again.
 	case sqlite3.SQLITE_BUSY:
 		return &ErrSerializationError{
diff --git a/sqldb/sqlerrors_no_sqlite.go b/sqldb/sqlerrors_no_sqlite.go
deleted file mode 100644
index 717d94152..000000000
--- a/sqldb/sqlerrors_no_sqlite.go
+++ /dev/null
@@ -1,86 +0,0 @@
-//go:build js || (windows && (arm || 386)) || (linux && (ppc64 || mips || mipsle || mips64))
-
-package sqldb
-
-import (
-	"errors"
-	"fmt"
-
-	"github.com/jackc/pgconn"
-	"github.com/jackc/pgerrcode"
-)
-
-var (
-	// ErrRetriesExceeded is returned when a transaction is retried more
-	// than the max allowed valued without a success.
-	ErrRetriesExceeded = errors.New("db tx retries exceeded")
-)
-
-// MapSQLError attempts to interpret a given error as a database agnostic SQL
-// error.
-func MapSQLError(err error) error {
-	// Attempt to interpret the error as a postgres error.
-	var pqErr *pgconn.PgError
-	if errors.As(err, &pqErr) {
-		return parsePostgresError(pqErr)
-	}
-
-	// Return original error if it could not be classified as a database
-	// specific error.
-	return err
-}
-
-// parsePostgresError attempts to parse a postgres error as a database agnostic
-// SQL error.
-func parsePostgresError(pqErr *pgconn.PgError) error {
-	switch pqErr.Code {
-	// Handle unique constraint violation error.
-	case pgerrcode.UniqueViolation:
-		return &ErrSQLUniqueConstraintViolation{
-			DBError: pqErr,
-		}
-
-	// Unable to serialize the transaction, so we'll need to try again.
-	case pgerrcode.SerializationFailure:
-		return &ErrSerializationError{
-			DBError: pqErr,
-		}
-
-	default:
-		return fmt.Errorf("unknown postgres error: %w", pqErr)
-	}
-}
-
-// ErrSQLUniqueConstraintViolation is an error type which represents a database
-// agnostic SQL unique constraint violation.
-type ErrSQLUniqueConstraintViolation struct {
-	DBError error
-}
-
-func (e ErrSQLUniqueConstraintViolation) Error() string {
-	return fmt.Sprintf("sql unique constraint violation: %v", e.DBError)
-}
-
-// ErrSerializationError is an error type which represents a database agnostic
-// error that a transaction couldn't be serialized with other concurrent db
-// transactions.
-type ErrSerializationError struct {
-	DBError error
-}
-
-// Unwrap returns the wrapped error.
-func (e ErrSerializationError) Unwrap() error {
-	return e.DBError
-}
-
-// Error returns the error message.
-func (e ErrSerializationError) Error() string {
-	return e.DBError.Error()
-}
-
-// IsSerializationError returns true if the given error is a serialization
-// error.
-func IsSerializationError(err error) bool {
-	var serializationError *ErrSerializationError
-	return errors.As(err, &serializationError)
-}
diff --git a/sqldb/sqlite.go b/sqldb/sqlite.go
index 705d5cc47..8ecadd68d 100644
--- a/sqldb/sqlite.go
+++ b/sqldb/sqlite.go
@@ -1,5 +1,3 @@
-//go:build !js && !(windows && (arm || 386)) && !(linux && (ppc64 || mips || mipsle || mips64))
-
 package sqldb
 
 import (
@@ -8,6 +6,7 @@ import (
 	"net/url"
 	"path/filepath"
 	"testing"
+	"time"
 
 	sqlite_migrate "github.com/golang-migrate/migrate/v4/database/sqlite"
 	"github.com/lightningnetwork/lnd/sqldb/sqlc"
@@ -24,8 +23,32 @@ const (
 	// sqliteTxLockImmediate is a dsn option used to ensure that write
 	// transactions are started immediately.
 	sqliteTxLockImmediate = "_txlock=immediate"
+
+	// defaultMaxConns is the number of permitted active and idle
+	// connections. We want to limit this so it isn't unlimited. We use the
+	// same value for the number of idle connections as, this can speed up
+	// queries given a new connection doesn't need to be established each
+	// time.
+	defaultMaxConns = 25
+
+	// connIdleLifetime is the amount of time a connection can be idle.
+	connIdleLifetime = 5 * time.Minute
 )
 
+// SqliteConfig holds all the config arguments needed to interact with our
+// sqlite DB.
+//
+//nolint:lll
+type SqliteConfig struct {
+	// SkipMigrations if true, then all the tables will be created on start
+	// up if they don't already exist.
+	SkipMigrations bool `long:"skipmigrations" description:"Skip applying migrations on startup."`
+
+	// DatabaseFileName is the full file path where the database file can be
+	// found.
+	DatabaseFileName string `long:"dbfile" description:"The full path to the database."`
+}
+
 // SqliteStore is a database store implementation that uses a sqlite backend.
 type SqliteStore struct {
 	cfg *SqliteConfig
@@ -35,7 +58,7 @@ type SqliteStore struct {
 
 // NewSqliteStore attempts to open a new sqlite database based on the passed
 // config.
-func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
+func NewSqliteStore(cfg *SqliteConfig) (*SqliteStore, error) {
 	// The set of pragma options are accepted using query options. For now
 	// we only want to ensure that foreign key constraints are properly
 	// enforced.
@@ -84,7 +107,7 @@ func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
 	// details on the formatting here, see the modernc.org/sqlite docs:
 	// https://pkg.go.dev/modernc.org/sqlite#Driver.Open.
 	dsn := fmt.Sprintf(
-		"%v?%v&%v", dbPath, sqliteOptions.Encode(),
+		"%v?%v&%v", cfg.DatabaseFileName, sqliteOptions.Encode(),
 		sqliteTxLockImmediate,
 	)
 	db, err := sql.Open("sqlite", dsn)
@@ -110,16 +133,8 @@ func NewSqliteStore(cfg *SqliteConfig, dbPath string) (*SqliteStore, error) {
 			return nil, err
 		}
 
-		// We use INTEGER PRIMARY KEY for sqlite, because it acts as a
-		// ROWID alias which is 8 bytes big and also autoincrements.
-		// It's important to use the ROWID as a primary key because the
-		// key look ups are almost twice as fast
-		sqliteFS := newReplacerFS(sqlSchemas, map[string]string{
-			"BIGINT PRIMARY KEY": "INTEGER PRIMARY KEY",
-		})
-
 		err = applyMigrations(
-			sqliteFS, driver, "sqlc/migrations", "sqlc",
+			sqlSchemas, driver, "sqlc/migrations", "sqlc",
 		)
 		if err != nil {
 			return nil, err
@@ -148,8 +163,9 @@ func NewTestSqliteDB(t *testing.T) *SqliteStore {
 	// an in mem version to speed up tests
 	dbFileName := filepath.Join(t.TempDir(), "tmp.db")
 	sqlDB, err := NewSqliteStore(&SqliteConfig{
-		SkipMigrations: false,
-	}, dbFileName)
+		DatabaseFileName: dbFileName,
+		SkipMigrations:   false,
+	})
 	require.NoError(t, err)
 
 	t.Cleanup(func() {
diff --git a/sqldb/sqlutils.go b/sqldb/sqlutils.go
deleted file mode 100644
index b71c5f00c..000000000
--- a/sqldb/sqlutils.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package sqldb
-
-import (
-	"database/sql"
-	"time"
-
-	"golang.org/x/exp/constraints"
-)
-
-// sqlInt32 turns a numerical integer type into the NullInt32 that sql/sqlc
-// uses when an integer field can be permitted to be NULL.
-//
-// We use this constraints.Integer constraint here which maps to all signed and
-// unsigned integer types.
-func sqlInt32[T constraints.Integer](num T) sql.NullInt32 {
-	return sql.NullInt32{
-		Int32: int32(num),
-		Valid: true,
-	}
-}
-
-// sqlInt64 turns a numerical integer type into the NullInt64 that sql/sqlc
-// uses when an integer field can be permitted to be NULL.
-//
-// We use this constraints.Integer constraint here which maps to all signed and
-// unsigned integer types.
-func sqlInt64[T constraints.Integer](num T) sql.NullInt64 {
-	return sql.NullInt64{
-		Int64: int64(num),
-		Valid: true,
-	}
-}
-
-// sqlStr turns a string into the NullString that sql/sqlc uses when a string
-// can be permitted to be NULL.
-func sqlStr(s string) sql.NullString {
-	if s == "" {
-		return sql.NullString{}
-	}
-
-	return sql.NullString{
-		String: s,
-		Valid:  true,
-	}
-}
-
-// sqlTime turns a time.Time into the NullTime that sql/sqlc uses when a time
-// can be permitted to be NULL.
-func sqlTime(t time.Time) sql.NullTime {
-	return sql.NullTime{
-		Time:  t,
-		Valid: true,
-	}
-}


Test patch:
diff --git a/htlcswitch/link_isolated_test.go b/htlcswitch/link_isolated_test.go
index 89d0ef319..6b616d2d8 100644
--- a/htlcswitch/link_isolated_test.go
+++ b/htlcswitch/link_isolated_test.go
@@ -13,10 +13,9 @@ import (
 type linkTestContext struct {
 	t *testing.T
 
-	aliceSwitch *Switch
-	aliceLink   ChannelLink
-	bobChannel  *lnwallet.LightningChannel
-	aliceMsgs   <-chan lnwire.Message
+	aliceLink  ChannelLink
+	bobChannel *lnwallet.LightningChannel
+	aliceMsgs  <-chan lnwire.Message
 }
 
 // sendHtlcBobToAlice sends an HTLC from Bob to Alice, that pays to a preimage
@@ -40,7 +39,7 @@ func (l *linkTestContext) sendHtlcAliceToBob(htlcID int,
 
 	l.t.Helper()
 
-	circuitMap := l.aliceSwitch.circuits
+	circuitMap := l.aliceLink.(*channelLink).cfg.Switch.circuits
 	fwdActions, err := circuitMap.CommitCircuits(
 		&PaymentCircuit{
 			Incoming: CircuitKey{
diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go
index fdd2a0d6a..d9e583876 100644
--- a/htlcswitch/link_test.go
+++ b/htlcswitch/link_test.go
@@ -187,30 +187,28 @@ func TestChannelLinkRevThenSig(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err := newSingleLinkTestHarness(t, chanAmt, chanReserve)
+	aliceLink, bobChannel, batchTicker, start, restore, err :=
+		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err)
 
-	err = harness.start()
+	err = start()
 	require.NoError(t, err)
-	defer harness.aliceLink.Stop()
+	defer aliceLink.Stop()
 
-	alice := newPersistentLinkHarness(t, harness.aliceSwitch,
-		harness.aliceLink, harness.aliceBatchTicker,
-		harness.aliceRestore,
+	alice := newPersistentLinkHarness(
+		t, aliceLink, batchTicker, restore,
 	)
 
 	var (
-		//nolint: forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	bobHtlc1 := generateHtlc(t, coreLink, 0)
@@ -242,7 +240,7 @@ func TestChannelLinkRevThenSig(t *testing.T) {
 	// ------sig----x
 	// Trigger a commitsig from Alice->Bob.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(5 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -267,10 +265,10 @@ func TestChannelLinkRevThenSig(t *testing.T) {
 	ctx.aliceMsgs = alice.msgs
 
 	// Restart Bob as well by calling NewLightningChannel.
-	bobSigner := harness.bobChannel.Signer
+	bobSigner := bobChannel.Signer
 	bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner)
-	bobChannel, err := lnwallet.NewLightningChannel(
-		bobSigner, harness.bobChannel.State(), bobPool,
+	bobChannel, err = lnwallet.NewLightningChannel(
+		bobSigner, bobChannel.State(), bobPool,
 	)
 	require.NoError(t, err)
 	err = bobPool.Start()
@@ -321,31 +319,28 @@ func TestChannelLinkSigThenRev(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err)
 
-	err = harness.start()
+	err = start()
 	require.NoError(t, err)
-	defer harness.aliceLink.Stop()
+	defer aliceLink.Stop()
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink,
-		harness.aliceBatchTicker, harness.aliceRestore,
+		t, aliceLink, batchTicker, restore,
 	)
 
 	var (
-		//nolint: forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	bobHtlc1 := generateHtlc(t, coreLink, 0)
@@ -363,7 +358,7 @@ func TestChannelLinkSigThenRev(t *testing.T) {
 	// ------sig----x
 	// Trigger a commitsig from Alice->Bob.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(5 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -402,10 +397,10 @@ func TestChannelLinkSigThenRev(t *testing.T) {
 	ctx.aliceMsgs = alice.msgs
 
 	// Restart Bob as well by calling NewLightningChannel.
-	bobSigner := harness.bobChannel.Signer
+	bobSigner := bobChannel.Signer
 	bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner)
-	bobChannel, err := lnwallet.NewLightningChannel(
-		bobSigner, harness.bobChannel.State(), bobPool,
+	bobChannel, err = lnwallet.NewLightningChannel(
+		bobSigner, bobChannel.State(), bobPool,
 	)
 	require.NoError(t, err)
 	err = bobPool.Start()
@@ -1920,21 +1915,13 @@ func (m *mockPeer) RemovePendingChannel(_ lnwire.ChannelID) error {
 	return nil
 }
 
-type singleLinkTestHarness struct {
-	aliceSwitch      *Switch
-	aliceLink        ChannelLink
-	bobChannel       *lnwallet.LightningChannel
-	aliceBatchTicker chan time.Time
-	start            func() error
-	aliceRestore     func() (*lnwallet.LightningChannel, error)
-}
-
-func newSingleLinkTestHarness(t *testing.T, chanAmt,
-	chanReserve btcutil.Amount) (singleLinkTestHarness, error) {
+func newSingleLinkTestHarness(t *testing.T, chanAmt, chanReserve btcutil.Amount) (
+	ChannelLink, *lnwallet.LightningChannel, chan time.Time, func() error,
+	func() (*lnwallet.LightningChannel, error), error) {
 
 	var chanIDBytes [8]byte
 	if _, err := io.ReadFull(rand.Reader, chanIDBytes[:]); err != nil {
-		return singleLinkTestHarness{}, err
+		return nil, nil, nil, nil, nil, err
 	}
 
 	chanID := lnwire.NewShortChanIDFromInt(
@@ -1945,7 +1932,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 		chanReserve, chanReserve, chanID,
 	)
 	if err != nil {
-		return singleLinkTestHarness{}, err
+		return nil, nil, nil, nil, nil, err
 	}
 
 	var (
@@ -1969,7 +1956,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 	aliceDb := aliceLc.channel.State().Db.GetParentDB()
 	aliceSwitch, err := initSwitchWithDB(testStartingHeight, aliceDb)
 	if err != nil {
-		return singleLinkTestHarness{}, err
+		return nil, nil, nil, nil, nil, err
 	}
 
 	notifyUpdateChan := make(chan *contractcourt.ContractUpdate)
@@ -1995,6 +1982,7 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 	aliceCfg := ChannelLinkConfig{
 		FwrdingPolicy: globalPolicy,
 		Peer:          alicePeer,
+		Switch:        aliceSwitch,
 		BestHeight:    aliceSwitch.BestHeight,
 		Circuits:      aliceSwitch.CircuitModifier(),
 		ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error {
@@ -2055,16 +2043,8 @@ func newSingleLinkTestHarness(t *testing.T, chanAmt,
 		invoiceRegistry.cleanup()
 	})
 
-	harness := singleLinkTestHarness{
-		aliceSwitch:      aliceSwitch,
-		aliceLink:        aliceLink,
-		bobChannel:       bobLc.channel,
-		aliceBatchTicker: bticker.Force,
-		start:            start,
-		aliceRestore:     aliceLc.restore,
-	}
-
-	return harness, nil
+	return aliceLink, bobLc.channel, bticker.Force, start,
+		aliceLc.restore, nil
 }
 
 func assertLinkBandwidth(t *testing.T, link ChannelLink,
@@ -2243,11 +2223,11 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	// We'll start the test by creating a single instance of
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 
-	harness, err :=
+	aliceLink, bobChannel, tmr, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
@@ -2261,7 +2241,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 		commitWeight = lnwallet.CommitWeight(cType)
 	)
 
-	link, ok := harness.aliceLink.(*channelLink)
+	link, ok := aliceLink.(*channelLink)
 	require.True(t, ok)
 	coreLink = link
 	mockedPeer := coreLink.cfg.Peer
@@ -2288,7 +2268,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	// The starting bandwidth of the channel should be exactly the amount
 	// that we created the channel between her and Bob, minus the feebuffer.
 	expectedBandwidth := chanAmtMSat - feeBuffer
-	assertLinkBandwidth(t, harness.aliceLink, expectedBandwidth)
+	assertLinkBandwidth(t, aliceLink, expectedBandwidth)
 
 	// Next, we'll create an HTLC worth 1 BTC, and send it into the link as
 	// a switch initiated payment.  The resulting bandwidth should
@@ -2306,11 +2286,11 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	}
 
 	circuit := makePaymentCircuit(&htlc.PaymentHash, &addPkt)
-	_, err = harness.aliceSwitch.commitCircuits(&circuit)
+	_, err = coreLink.cfg.Switch.commitCircuits(&circuit)
 	require.NoError(t, err, "unable to commit circuit")
 
 	addPkt.circuit = &circuit
-	if err := harness.aliceLink.handleSwitchPacket(&addPkt); err != nil {
+	if err := aliceLink.handleSwitchPacket(&addPkt); err != nil {
 		t.Fatalf("unable to handle switch packet: %v", err)
 	}
 	time.Sleep(time.Millisecond * 500)
@@ -2324,7 +2304,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 
 	// The resulting bandwidth should reflect that Alice is paying the
 	// htlc amount in addition to the fee buffer.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// Alice should send the HTLC to Bob.
 	var msg lnwire.Message
@@ -2339,40 +2319,34 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
 	}
 
-	bobIndex, err := harness.bobChannel.ReceiveHTLC(addHtlc)
+	bobIndex, err := bobChannel.ReceiveHTLC(addHtlc)
 	require.NoError(t, err, "bob failed receiving htlc")
 
 	// Lock in the HTLC.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, true,
-	); err != nil {
+	if err := updateState(tmr, coreLink, bobChannel, true); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 	// Locking in the HTLC should not change Alice's bandwidth.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// If we now send in a valid HTLC settle for the prior HTLC we added,
 	// then the bandwidth should remain unchanged as the remote party will
 	// gain additional channel balance.
-	err = harness.bobChannel.SettleHTLC(
-		*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil,
-	)
+	err = bobChannel.SettleHTLC(*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
 	require.NoError(t, err, "unable to settle htlc")
 	htlcSettle := &lnwire.UpdateFulfillHTLC{
 		ID:              0,
 		PaymentPreimage: *invoice.Terms.PaymentPreimage,
 	}
-	harness.aliceLink.HandleChannelUpdate(htlcSettle)
+	aliceLink.HandleChannelUpdate(htlcSettle)
 	time.Sleep(time.Millisecond * 500)
 
 	// Since the settle is not locked in yet, Alice's bandwidth should still
 	// reflect that she has to account for the fee buffer.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// Lock in the settle.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, false,
-	); err != nil {
+	if err := updateState(tmr, coreLink, bobChannel, false); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -2384,7 +2358,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// Now that it is settled, Alice fee buffer should have been decreased.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// Next, we'll add another HTLC initiated by the switch (of the same
 	// amount as the prior one).
@@ -2398,11 +2372,11 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	}
 
 	circuit = makePaymentCircuit(&htlc.PaymentHash, &addPkt)
-	_, err = harness.aliceSwitch.commitCircuits(&circuit)
+	_, err = coreLink.cfg.Switch.commitCircuits(&circuit)
 	require.NoError(t, err, "unable to commit circuit")
 
 	addPkt.circuit = &circuit
-	if err := harness.aliceLink.handleSwitchPacket(&addPkt); err != nil {
+	if err := aliceLink.handleSwitchPacket(&addPkt); err != nil {
 		t.Fatalf("unable to handle switch packet: %v", err)
 	}
 	time.Sleep(time.Millisecond * 500)
@@ -2415,9 +2389,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// Again, Alice's bandwidth decreases by htlcAmt and fee buffer.
-	assertLinkBandwidth(
-		t, harness.aliceLink, chanAmtMSat-feeBuffer-2*htlcAmt,
-	)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-2*htlcAmt)
 
 	// Alice will send the HTLC to Bob.
 	select {
@@ -2431,19 +2403,15 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
 	}
 
-	bobIndex, err = harness.bobChannel.ReceiveHTLC(addHtlc)
+	bobIndex, err = bobChannel.ReceiveHTLC(addHtlc)
 	require.NoError(t, err, "bob failed receiving htlc")
 
 	// Lock in the HTLC, which should not affect the bandwidth.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, true,
-	); err != nil {
+	if err := updateState(tmr, coreLink, bobChannel, true); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
-	assertLinkBandwidth(
-		t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt*2,
-	)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt*2)
 
 	// With that processed, we'll now generate an HTLC fail (sent by the
 	// remote peer) to cancel the HTLC we just added. This should return us
@@ -2451,25 +2419,21 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	reason := make([]byte, 292)
 	copy(reason, []byte("nop"))
 
-	err = harness.bobChannel.FailHTLC(bobIndex, reason, nil, nil, nil)
+	err = bobChannel.FailHTLC(bobIndex, reason, nil, nil, nil)
 	require.NoError(t, err, "unable to fail htlc")
 	failMsg := &lnwire.UpdateFailHTLC{
 		ID:     1,
 		Reason: lnwire.OpaqueReason(reason),
 	}
 
-	harness.aliceLink.HandleChannelUpdate(failMsg)
+	aliceLink.HandleChannelUpdate(failMsg)
 	time.Sleep(time.Millisecond * 500)
 
 	// Before the Fail gets locked in, the bandwidth should remain unchanged.
-	assertLinkBandwidth(
-		t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt*2,
-	)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt*2)
 
 	// Lock in the Fail.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, false,
-	); err != nil {
+	if err := updateState(tmr, coreLink, bobChannel, false); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -2481,7 +2445,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// Now the bandwidth should reflect the failed HTLC.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// Moving along, we'll now receive a new HTLC from the remote peer,
 	// with an ID of 0 as this is their first HTLC. The bandwidth should
@@ -2505,17 +2469,15 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	require.NoError(t, err, "unable to add invoice to registry")
 
 	htlc.ID = 0
-	_, err = harness.bobChannel.AddHTLC(htlc, nil)
+	_, err = bobChannel.AddHTLC(htlc, nil)
 	require.NoError(t, err, "unable to add htlc")
-	harness.aliceLink.HandleChannelUpdate(htlc)
+	aliceLink.HandleChannelUpdate(htlc)
 
 	// Alice's balance remains unchanged until this HTLC is locked in.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 
 	// Lock in the HTLC.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, false,
-	); err != nil {
+	if err := updateState(tmr, coreLink, bobChannel, false); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -2528,18 +2490,18 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 
 	// Since Bob is adding this HTLC, Alice will only have an increased fee
 	// buffer.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer-htlcAmt)
 	time.Sleep(time.Millisecond * 500)
 
 	addPkt = htlcPacket{
 		htlc:           htlc,
-		incomingChanID: harness.aliceLink.ShortChanID(),
+		incomingChanID: aliceLink.ShortChanID(),
 		incomingHTLCID: 0,
 		obfuscator:     NewMockObfuscator(),
 	}
 
 	circuit = makePaymentCircuit(&htlc.PaymentHash, &addPkt)
-	_, err = harness.aliceSwitch.commitCircuits(&circuit)
+	_, err = coreLink.cfg.Switch.commitCircuits(&circuit)
 	require.NoError(t, err, "unable to commit circuit")
 
 	addPkt.outgoingChanID = carolChanID
@@ -2552,7 +2514,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	// we eventually learn (simulating a multi-hop payment). The bandwidth
 	// of the channel should now be re-balanced to the starting point.
 	settlePkt := htlcPacket{
-		incomingChanID: harness.aliceLink.ShortChanID(),
+		incomingChanID: aliceLink.ShortChanID(),
 		incomingHTLCID: 0,
 		circuit:        &circuit,
 		outgoingChanID: addPkt.outgoingChanID,
@@ -2564,7 +2526,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 		obfuscator: NewMockObfuscator(),
 	}
 
-	if err := harness.aliceLink.handleSwitchPacket(&settlePkt); err != nil {
+	if err := aliceLink.handleSwitchPacket(&settlePkt); err != nil {
 		t.Fatalf("unable to handle switch packet: %v", err)
 	}
 	time.Sleep(time.Millisecond * 500)
@@ -2577,7 +2539,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// Settling this HTLC gives Alice all her original bandwidth back.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer)
 
 	select {
 	case msg = <-aliceMsgs:
@@ -2589,14 +2551,12 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	if !ok {
 		t.Fatalf("expected UpdateFulfillHTLC, got %T", msg)
 	}
-	err = harness.bobChannel.ReceiveHTLCSettle(
-		settleMsg.PaymentPreimage, settleMsg.ID,
-	)
+	err = bobChannel.ReceiveHTLCSettle(settleMsg.PaymentPreimage, settleMsg.ID)
 	require.NoError(t, err, "failed receiving fail htlc")
 
 	// After failing an HTLC, the link will automatically trigger
 	// a state update.
-	if err := handleStateUpdate(coreLink, harness.bobChannel); err != nil {
+	if err := handleStateUpdate(coreLink, bobChannel); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -2620,16 +2580,14 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	// HTLC we add, hence it should have an ID of 1 (Alice's channel
 	// link will set this automatically for her side).
 	htlc.ID = 1
-	_, err = harness.bobChannel.AddHTLC(htlc, nil)
+	_, err = bobChannel.AddHTLC(htlc, nil)
 	require.NoError(t, err, "unable to add htlc")
-	harness.aliceLink.HandleChannelUpdate(htlc)
+	aliceLink.HandleChannelUpdate(htlc)
 	time.Sleep(time.Millisecond * 500)
 
 	// No changes before the HTLC is locked in.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer)
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, false,
-	); err != nil {
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer)
+	if err := updateState(tmr, coreLink, bobChannel, false); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -2641,17 +2599,17 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// After lock-in, Alice will have to account for a fee buffer.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer)
 
 	addPkt = htlcPacket{
 		htlc:           htlc,
-		incomingChanID: harness.aliceLink.ShortChanID(),
+		incomingChanID: aliceLink.ShortChanID(),
 		incomingHTLCID: 1,
 		obfuscator:     NewMockObfuscator(),
 	}
 
 	circuit = makePaymentCircuit(&htlc.PaymentHash, &addPkt)
-	_, err = harness.aliceSwitch.commitCircuits(&circuit)
+	_, err = coreLink.cfg.Switch.commitCircuits(&circuit)
 	require.NoError(t, err, "unable to commit circuit")
 
 	addPkt.outgoingChanID = carolChanID
@@ -2661,7 +2619,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	require.NoError(t, err, "unable to set keystone")
 
 	failPkt := htlcPacket{
-		incomingChanID: harness.aliceLink.ShortChanID(),
+		incomingChanID: aliceLink.ShortChanID(),
 		incomingHTLCID: 1,
 		circuit:        &circuit,
 		outgoingChanID: addPkt.outgoingChanID,
@@ -2673,7 +2631,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 		obfuscator: NewMockObfuscator(),
 	}
 
-	if err := harness.aliceLink.handleSwitchPacket(&failPkt); err != nil {
+	if err := aliceLink.handleSwitchPacket(&failPkt); err != nil {
 		t.Fatalf("unable to handle switch packet: %v", err)
 	}
 	time.Sleep(time.Millisecond * 500)
@@ -2686,7 +2644,7 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	)
 
 	// Alice should get all her bandwidth back.
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer)
 
 	// Message should be sent to Bob.
 	select {
@@ -2698,15 +2656,15 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
 	if !ok {
 		t.Fatalf("expected UpdateFailHTLC, got %T", msg)
 	}
-	err = harness.bobChannel.ReceiveFailHTLC(failMsg.ID, []byte("fail"))
+	err = bobChannel.ReceiveFailHTLC(failMsg.ID, []byte("fail"))
 	require.NoError(t, err, "failed receiving fail htlc")
 
 	// After failing an HTLC, the link will automatically trigger
 	// a state update.
-	if err := handleStateUpdate(coreLink, harness.bobChannel); err != nil {
+	if err := handleStateUpdate(coreLink, bobChannel); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
-	assertLinkBandwidth(t, harness.aliceLink, chanAmtMSat-feeBuffer)
+	assertLinkBandwidth(t, aliceLink, chanAmtMSat-feeBuffer)
 }
 
 // genAddsAndCircuits creates `numHtlcs` sequential ADD packets and there
@@ -2755,17 +2713,16 @@ func TestChannelLinkTrimCircuitsPending(t *testing.T) {
 	// We'll start by creating a new link with our chanAmt (5 BTC). We will
 	// only be testing Alice's behavior, so the reference to Bob's channel
 	// state is unnecessary.
-	harness, err :=
+	aliceLink, _, batchTicker, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink,
-		harness.aliceBatchTicker, harness.aliceRestore,
+		t, aliceLink, batchTicker, restore,
 	)
 
 	// Compute the static fees that will be used to determine the
@@ -3038,17 +2995,16 @@ func TestChannelLinkTrimCircuitsNoCommit(t *testing.T) {
 	// We'll start by creating a new link with our chanAmt (5 BTC). We will
 	// only be testing Alice's behavior, so the reference to Bob's channel
 	// state is unnecessary.
-	harness, err :=
+	aliceLink, _, batchTicker, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink,
-		harness.aliceBatchTicker, harness.aliceRestore,
+		t, aliceLink, batchTicker, restore,
 	)
 
 	// We'll put Alice into hodl.Commit mode, such that the circuits for any
@@ -3319,17 +3275,16 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) {
 	)
 
 	// We'll start by creating a new link with our chanAmt (5 BTC).
-	harness, err :=
+	aliceLink, bobChan, batchTicker, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink,
-		harness.aliceBatchTicker, harness.aliceRestore,
+		t, aliceLink, batchTicker, restore,
 	)
 
 	// Compute the static fees that will be used to determine the
@@ -3394,7 +3349,7 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) {
 
 		pkt.ID = uint64(i)
 
-		_, err := harness.bobChannel.ReceiveHTLC(pkt)
+		_, err := bobChan.ReceiveHTLC(pkt)
 		if err != nil {
 			t.Fatalf("unable to receive htlc: %v", err)
 		}
@@ -3424,12 +3379,10 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) {
 			t.Fatalf("alice did not send commitment signature")
 		}
 
-		err := harness.bobChannel.ReceiveNewCommitment(
-			&lnwallet.CommitSigs{
-				CommitSig: sig.CommitSig,
-				HtlcSigs:  sig.HtlcSigs,
-			},
-		)
+		err := bobChan.ReceiveNewCommitment(&lnwallet.CommitSigs{
+			CommitSig: sig.CommitSig,
+			HtlcSigs:  sig.HtlcSigs,
+		})
 		if err != nil {
 			t.Fatalf("unable to receive new commitment: %v", err)
 		}
@@ -3438,7 +3391,7 @@ func TestChannelLinkTrimCircuitsRemoteCommit(t *testing.T) {
 
 	// Next, revoke Bob's current commitment and send it to Alice so that we
 	// can test that Alice's circuits aren't trimmed.
-	rev, _, _, err := harness.bobChannel.RevokeCurrentCommitment()
+	rev, _, _, err := bobChan.RevokeCurrentCommitment()
 	require.NoError(t, err, "unable to revoke current commitment")
 
 	_, _, _, _, err = alice.channel.ReceiveRevocation(rev)
@@ -3467,11 +3420,11 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 	// channel reserve.
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTimer, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
@@ -3485,7 +3438,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 		commitWeight    = lnwallet.CommitWeight(cType)
 	)
 
-	link, ok := harness.aliceLink.(*channelLink)
+	link, ok := aliceLink.(*channelLink)
 	require.True(t, ok)
 	coreLink = link
 	mockedPeer := coreLink.cfg.Peer
@@ -3507,7 +3460,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 	// that we created the channel between her and Bob, minus the channel
 	// reserve and the fee buffer.
 	expectedBandwidth := chanAmtMSat - feeBuffer - chanReserveMSat
-	assertLinkBandwidth(t, harness.aliceLink, expectedBandwidth)
+	assertLinkBandwidth(t, aliceLink, expectedBandwidth)
 
 	// Next, we'll create an HTLC worth 3 BTC, and send it into the link as
 	// a switch initiated payment.  The resulting bandwidth should
@@ -3521,10 +3474,10 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 		obfuscator: NewMockObfuscator(),
 	}
 	circuit := makePaymentCircuit(&htlc.PaymentHash, addPkt)
-	_, err = harness.aliceSwitch.commitCircuits(&circuit)
+	_, err = coreLink.cfg.Switch.commitCircuits(&circuit)
 	require.NoError(t, err, "unable to commit circuit")
 
-	_ = harness.aliceLink.handleSwitchPacket(addPkt)
+	_ = aliceLink.handleSwitchPacket(addPkt)
 	time.Sleep(time.Millisecond * 100)
 
 	htlcWeight = int64(2) * input.HTLCWeight
@@ -3532,8 +3485,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 		feePerKw, commitWeight+htlcWeight,
 	)
 	assertLinkBandwidth(
-		t, harness.aliceLink,
-		chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
+		t, aliceLink, chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
 	)
 
 	// Alice should send the HTLC to Bob.
@@ -3549,46 +3501,38 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
 	}
 
-	bobIndex, err := harness.bobChannel.ReceiveHTLC(addHtlc)
+	bobIndex, err := bobChannel.ReceiveHTLC(addHtlc)
 	require.NoError(t, err, "bob failed receiving htlc")
 
 	// Lock in the HTLC.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, true,
-	); err != nil {
+	if err := updateState(batchTimer, coreLink, bobChannel, true); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
 	assertLinkBandwidth(
-		t, harness.aliceLink,
-		chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
+		t, aliceLink, chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
 	)
 
 	// If we now send in a valid HTLC settle for the prior HTLC we added,
 	// then the bandwidth should remain unchanged as the remote party will
 	// gain additional channel balance.
-	err = harness.bobChannel.SettleHTLC(
-		*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil,
-	)
+	err = bobChannel.SettleHTLC(*invoice.Terms.PaymentPreimage, bobIndex, nil, nil, nil)
 	require.NoError(t, err, "unable to settle htlc")
 	htlcSettle := &lnwire.UpdateFulfillHTLC{
 		ID:              bobIndex,
 		PaymentPreimage: *invoice.Terms.PaymentPreimage,
 	}
-	harness.aliceLink.HandleChannelUpdate(htlcSettle)
+	aliceLink.HandleChannelUpdate(htlcSettle)
 	time.Sleep(time.Millisecond * 500)
 
 	// Since the settle is not locked in yet, Alice's bandwidth should still
 	// reflect that she has to pay the fee.
 	assertLinkBandwidth(
-		t, harness.aliceLink,
-		chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
+		t, aliceLink, chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
 	)
 
 	// Lock in the settle.
-	if err := updateState(
-		harness.aliceBatchTicker, coreLink, harness.bobChannel, false,
-	); err != nil {
+	if err := updateState(batchTimer, coreLink, bobChannel, false); err != nil {
 		t.Fatalf("unable to update state: %v", err)
 	}
 
@@ -3598,8 +3542,7 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 
 	time.Sleep(time.Millisecond * 100)
 	assertLinkBandwidth(
-		t, harness.aliceLink,
-		chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
+		t, aliceLink, chanAmtMSat-htlcAmt-feeBuffer-chanReserveMSat,
 	)
 
 	// Now we create a channel that has a channel reserve that is
@@ -3608,15 +3551,16 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
 	// should therefore be 0.
 	const bobChanAmt = btcutil.SatoshiPerBitcoin * 1
 	const bobChanReserve = btcutil.SatoshiPerBitcoin * 1.5
-	harness, err = newSingleLinkTestHarness(t, bobChanAmt, bobChanReserve)
+	bobLink, _, _, start, _, err :=
+		newSingleLinkTestHarness(t, bobChanAmt, bobChanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	// Make sure bandwidth is reported as 0.
-	assertLinkBandwidth(t, harness.aliceLink, 0)
+	assertLinkBandwidth(t, bobLink, 0)
 }
 
 // TestChannelRetransmission tests the ability of the channel links to
@@ -4044,7 +3988,7 @@ func TestChannelLinkShutdownDuringForward(t *testing.T) {
 	// Define a helper method that strobes the switch's log ticker, and
 	// unblocks after nothing has been pulled for two seconds.
 	waitForBobsSwitchToBlock := func() {
-		bobSwitch := n.bobServer.htlcSwitch
+		bobSwitch := n.firstBobChannelLink.cfg.Switch
 		ticker := bobSwitch.cfg.LogEventTicker.(*ticker.Force)
 		timeout := time.After(15 * time.Second)
 		for {
@@ -4094,7 +4038,7 @@ func TestChannelLinkShutdownDuringForward(t *testing.T) {
 	// first acquiring the index mutex and forcing a log event so that the
 	// htlcForwarder is blocked inside the logTicker case, which also needs
 	// the indexMtx.
-	n.bobServer.htlcSwitch.indexMtx.Lock()
+	n.firstBobChannelLink.cfg.Switch.indexMtx.Lock()
 
 	// Strobe the log ticker, and wait for switch to stop accepting any more
 	// log ticks.
@@ -4104,8 +4048,8 @@ func TestChannelLinkShutdownDuringForward(t *testing.T) {
 	// channel, and unlock the indexMtx to allow return to the
 	// htlcForwarder's main select. After this, any attempt to forward
 	// through the switch will block.
-	n.bobServer.htlcSwitch.htlcPlex = nil
-	n.bobServer.htlcSwitch.indexMtx.Unlock()
+	n.firstBobChannelLink.cfg.Switch.htlcPlex = nil
+	n.firstBobChannelLink.cfg.Switch.indexMtx.Unlock()
 
 	// Now, make a payment from Alice to Carol, which should cause Bob's
 	// incoming link to block when it tries to submit the packet to the nil
@@ -4456,7 +4400,6 @@ func TestChannelLinkAcceptOverpay(t *testing.T) {
 type persistentLinkHarness struct {
 	t *testing.T
 
-	hSwitch  *Switch
 	link     ChannelLink
 	coreLink *channelLink
 	channel  *lnwallet.LightningChannel
@@ -4469,7 +4412,7 @@ type persistentLinkHarness struct {
 
 // newPersistentLinkHarness initializes a new persistentLinkHarness and derives
 // the supporting references from the active link.
-func newPersistentLinkHarness(t *testing.T, hSwitch *Switch, link ChannelLink,
+func newPersistentLinkHarness(t *testing.T, link ChannelLink,
 	batchTicker chan time.Time,
 	restore func() (*lnwallet.LightningChannel,
 		error)) *persistentLinkHarness {
@@ -4478,7 +4421,6 @@ func newPersistentLinkHarness(t *testing.T, hSwitch *Switch, link ChannelLink,
 
 	return &persistentLinkHarness{
 		t:           t,
-		hSwitch:     hSwitch,
 		link:        link,
 		coreLink:    coreLink,
 		channel:     coreLink.channel,
@@ -4502,15 +4444,12 @@ func (h *persistentLinkHarness) restart(restartSwitch, syncStates bool,
 	hodlFlags ...hodl.Flag) {
 
 	// First, remove the link from the switch.
-	h.hSwitch.RemoveLink(h.link.ChanID())
+	h.coreLink.cfg.Switch.RemoveLink(h.link.ChanID())
 
 	if restartSwitch {
 		// If a switch restart is requested, we will stop it. It will be
 		// reinstantiated in restartLink.
-		err := h.hSwitch.Stop()
-		if err != nil {
-			h.t.Fatalf("unable to stop switch: %v", err)
-		}
+		h.coreLink.cfg.Switch.Stop()
 	}
 
 	// Since our in-memory state may have diverged from our persistent
@@ -4559,7 +4498,7 @@ func (h *persistentLinkHarness) checkSent(pkts []*htlcPacket) {
 // switch's circuit map. The forwarding actions are returned if there was no
 // failure.
 func (h *persistentLinkHarness) commitCircuits(circuits []*PaymentCircuit) *CircuitFwdActions {
-	fwdActions, err := h.hSwitch.commitCircuits(circuits...)
+	fwdActions, err := h.coreLink.cfg.Switch.commitCircuits(circuits...)
 	if err != nil {
 		h.t.Fatalf("unable to commit circuit: %v", err)
 	}
@@ -4572,12 +4511,12 @@ func (h *persistentLinkHarness) assertNumPendingNumOpenCircuits(
 
 	_, _, line, _ := runtime.Caller(1)
 
-	numPending := h.hSwitch.circuits.NumPending()
+	numPending := h.coreLink.cfg.Switch.circuits.NumPending()
 	if numPending != wantPending {
 		h.t.Fatalf("line: %d: wrong number of pending circuits: "+
 			"want %d, got %d", line, wantPending, numPending)
 	}
-	numOpen := h.hSwitch.circuits.NumOpen()
+	numOpen := h.coreLink.cfg.Switch.circuits.NumOpen()
 	if numOpen != wantOpen {
 		h.t.Fatalf("line: %d: wrong number of open circuits: "+
 			"want %d, got %d", line, wantOpen, numOpen)
@@ -4623,9 +4562,10 @@ func (h *persistentLinkHarness) restartLink(
 	)
 
 	aliceDb := aliceChannel.State().Db.GetParentDB()
+	aliceSwitch := h.coreLink.cfg.Switch
 	if restartSwitch {
 		var err error
-		h.hSwitch, err = initSwitchWithDB(testStartingHeight, aliceDb)
+		aliceSwitch, err = initSwitchWithDB(testStartingHeight, aliceDb)
 		if err != nil {
 			return nil, nil, err
 		}
@@ -4654,10 +4594,11 @@ func (h *persistentLinkHarness) restartLink(
 	aliceCfg := ChannelLinkConfig{
 		FwrdingPolicy: globalPolicy,
 		Peer:          alicePeer,
-		BestHeight:    h.hSwitch.BestHeight,
-		Circuits:      h.hSwitch.CircuitModifier(),
+		Switch:        aliceSwitch,
+		BestHeight:    aliceSwitch.BestHeight,
+		Circuits:      aliceSwitch.CircuitModifier(),
 		ForwardPackets: func(linkQuit chan struct{}, _ bool, packets ...*htlcPacket) error {
-			return h.hSwitch.ForwardPackets(linkQuit, packets...)
+			return aliceSwitch.ForwardPackets(linkQuit, packets...)
 		},
 		DecodeHopIterators: decoder.DecodeHopIterators,
 		ExtractErrorEncrypter: func(*btcec.PublicKey) (
@@ -4693,13 +4634,13 @@ func (h *persistentLinkHarness) restartLink(
 		NotifyActiveChannel:     func(wire.OutPoint) {},
 		NotifyInactiveChannel:   func(wire.OutPoint) {},
 		NotifyInactiveLinkEvent: func(wire.OutPoint) {},
-		HtlcNotifier:            h.hSwitch.cfg.HtlcNotifier,
+		HtlcNotifier:            aliceSwitch.cfg.HtlcNotifier,
 		SyncStates:              syncStates,
 		GetAliases:              getAliases,
 	}
 
 	aliceLink := NewChannelLink(aliceCfg, aliceChannel)
-	if err := h.hSwitch.AddLink(aliceLink); err != nil {
+	if err := aliceSwitch.AddLink(aliceLink); err != nil {
 		return nil, nil, err
 	}
 	go func() {
@@ -4776,17 +4717,16 @@ func TestChannelLinkNoMoreUpdates(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, _, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
@@ -4795,11 +4735,10 @@ func TestChannelLinkNoMoreUpdates(t *testing.T) {
 	htlc2 := generateHtlc(t, coreLink, 1)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// We now play out the following scanario:
@@ -4901,17 +4840,16 @@ func TestChannelLinkWaitForRevocation(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, _, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
@@ -4924,11 +4862,10 @@ func TestChannelLinkWaitForRevocation(t *testing.T) {
 	}
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	assertNoMsgFromAlice := func() {
@@ -5021,27 +4958,25 @@ func TestChannelLinkNoEmptySig(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
-	t.Cleanup(harness.aliceLink.Stop)
+	t.Cleanup(aliceLink.Stop)
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// Send htlc 1 from Alice to Bob.
@@ -5051,7 +4986,7 @@ func TestChannelLinkNoEmptySig(t *testing.T) {
 
 	// Tick the batch ticker to trigger a commitsig from Alice->Bob.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(5 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -5069,7 +5004,7 @@ func TestChannelLinkNoEmptySig(t *testing.T) {
 
 	// Tick the batch ticker to trigger a commitsig from Alice->Bob.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(5 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -5081,7 +5016,7 @@ func TestChannelLinkNoEmptySig(t *testing.T) {
 	ctx.sendCommitSigBobToAlice(1)
 
 	// Now send Bob the signature from Alice covering both htlcs.
-	err = harness.bobChannel.ReceiveNewCommitment(&lnwallet.CommitSigs{
+	err = bobChannel.ReceiveNewCommitment(&lnwallet.CommitSigs{
 		CommitSig: commitSigAlice.CommitSig,
 		HtlcSigs:  commitSigAlice.HtlcSigs,
 	})
@@ -5128,17 +5063,16 @@ func TestChannelLinkBatchPreimageWrite(t *testing.T) {
 func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, startUp, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := startUp(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
@@ -5153,11 +5087,10 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
 	}
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// First, send a batch of Adds from Alice to Bob.
@@ -5172,7 +5105,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
 	// Force alice's link to sign a commitment covering the htlcs sent thus
 	// far.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(15 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -5204,7 +5137,7 @@ func testChannelLinkBatchPreimageWrite(t *testing.T, disconnect bool) {
 	// CommitSig, and simply stop Alice's link. As she exits, we should
 	// detect that she has uncommitted preimages and write them to disk.
 	if disconnect {
-		harness.aliceLink.Stop()
+		aliceLink.Stop()
 		checkHasPreimages(t, coreLink, htlcs, true)
 		return
 	}
@@ -5238,17 +5171,16 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, _, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
@@ -5262,11 +5194,10 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 	htlc2 := generateHtlc(t, coreLink, 1)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// We start with he following scenario: Bob sends Alice two HTLCs, and a
@@ -5324,12 +5255,12 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 	// packet will NOT include a sourceRef, meaning the AddRef on disk will
 	// not be acked after committing this response.
 	fail0 := &htlcPacket{
-		incomingChanID: harness.bobChannel.ShortChanID(),
+		incomingChanID: bobChannel.ShortChanID(),
 		incomingHTLCID: 0,
 		obfuscator:     NewMockObfuscator(),
 		htlc:           &lnwire.UpdateFailHTLC{},
 	}
-	_ = harness.aliceLink.handleSwitchPacket(fail0)
+	_ = aliceLink.handleSwitchPacket(fail0)
 
 	//  Bob               Alice
 	//   |<----- fal-1 ------|
@@ -5382,12 +5313,12 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 			Height: addHeight,
 			Index:  1,
 		},
-		incomingChanID: harness.bobChannel.ShortChanID(),
+		incomingChanID: bobChannel.ShortChanID(),
 		incomingHTLCID: 1,
 		obfuscator:     NewMockObfuscator(),
 		htlc:           &lnwire.UpdateFailHTLC{},
 	}
-	_ = harness.aliceLink.handleSwitchPacket(fail1)
+	_ = aliceLink.handleSwitchPacket(fail1)
 
 	//  Bob               Alice
 	//   |<----- fal-1 ------|
@@ -5442,7 +5373,7 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 	// this should trigger an attempt to cleanup the spurious response.
 	// However, we expect it to result in a NOP since it is still missing
 	// its sourceRef.
-	_ = harness.aliceLink.handleSwitchPacket(fail0)
+	_ = aliceLink.handleSwitchPacket(fail0)
 
 	// Allow the link enough time to process and reject the duplicate
 	// packet, we'll also check that this doesn't trigger Alice to send the
@@ -5490,12 +5421,12 @@ func TestChannelLinkCleanupSpuriousResponses(t *testing.T) {
 			Height: addHeight,
 			Index:  0,
 		},
-		incomingChanID: harness.bobChannel.ShortChanID(),
+		incomingChanID: bobChannel.ShortChanID(),
 		incomingHTLCID: 0,
 		obfuscator:     NewMockObfuscator(),
 		htlc:           &lnwire.UpdateFailHTLC{},
 	}
-	_ = harness.aliceLink.handleSwitchPacket(fail0)
+	_ = aliceLink.handleSwitchPacket(fail0)
 
 	// Allow the link enough time to process and reject the duplicate
 	// packet, we'll also check that this doesn't trigger Alice to send the
@@ -5579,7 +5510,7 @@ func TestChannelLinkFail(t *testing.T) {
 
 		// link test is used to execute the given test on the channel
 		// link after it is started.
-		linkTest func(*testing.T, *Switch, *channelLink,
+		linkTest func(*testing.T, *channelLink,
 			*lnwallet.LightningChannel)
 
 		// shouldFail indicates whether or not the link should fail
@@ -5601,7 +5532,7 @@ func TestChannelLinkFail(t *testing.T) {
 				"message",
 			func(c *channelLink) {
 			},
-			func(_ *testing.T, _ *Switch, c *channelLink,
+			func(_ *testing.T, c *channelLink,
 				_ *lnwallet.LightningChannel) {
 
 				warningMsg := &lnwire.Warning{
@@ -5622,7 +5553,7 @@ func TestChannelLinkFail(t *testing.T) {
 				// the SendMessage call fail.
 				c.cfg.Peer.(*mockPeer).disconnected = true
 			},
-			func(*testing.T, *Switch, *channelLink,
+			func(*testing.T, *channelLink,
 				*lnwallet.LightningChannel) { //nolint:whitespace,lll
 
 				// Should fail at startup.
@@ -5642,7 +5573,7 @@ func TestChannelLinkFail(t *testing.T) {
 				}
 				c.channel.State().Packager = pkg
 			},
-			func(*testing.T, *Switch, *channelLink,
+			func(*testing.T, *channelLink,
 				*lnwallet.LightningChannel) { //nolint:whitespace,lll
 
 				// Should fail at startup.
@@ -5656,7 +5587,7 @@ func TestChannelLinkFail(t *testing.T) {
 				"invalid Settle message",
 			func(c *channelLink) {
 			},
-			func(_ *testing.T, s *Switch, c *channelLink,
+			func(_ *testing.T, c *channelLink,
 				_ *lnwallet.LightningChannel) {
 
 				// Recevive an htlc settle for an htlc that was
@@ -5676,16 +5607,15 @@ func TestChannelLinkFail(t *testing.T) {
 				"CommitSig, not containing enough HTLC sigs",
 			func(c *channelLink) {
 			},
-			func(_ *testing.T, s *Switch, c *channelLink,
+			func(_ *testing.T, c *channelLink,
 				remoteChannel *lnwallet.LightningChannel) {
 
 				// Generate an HTLC and send to the link.
 				htlc1 := generateHtlc(t, c, 0)
 				ctx := linkTestContext{
-					t:           t,
-					aliceSwitch: s,
-					aliceLink:   c,
-					bobChannel:  remoteChannel,
+					t:          t,
+					aliceLink:  c,
+					bobChannel: remoteChannel,
 				}
 				ctx.sendHtlcBobToAlice(htlc1)
 
@@ -5715,7 +5645,7 @@ func TestChannelLinkFail(t *testing.T) {
 				"CommitSig, where the sig itself is corrupted",
 			func(c *channelLink) {
 			},
-			func(t *testing.T, s *Switch, c *channelLink,
+			func(t *testing.T, c *channelLink,
 				remoteChannel *lnwallet.LightningChannel) {
 
 				t.Helper()
@@ -5723,10 +5653,9 @@ func TestChannelLinkFail(t *testing.T) {
 				// Generate an HTLC and send to the link.
 				htlc1 := generateHtlc(t, c, 0)
 				ctx := linkTestContext{
-					t:           t,
-					aliceSwitch: s,
-					aliceLink:   c,
-					bobChannel:  remoteChannel,
+					t:          t,
+					aliceLink:  c,
+					bobChannel: remoteChannel,
 				}
 
 				ctx.sendHtlcBobToAlice(htlc1)
@@ -5764,7 +5693,7 @@ func TestChannelLinkFail(t *testing.T) {
 				"error from the remote",
 			func(c *channelLink) {
 			},
-			func(_ *testing.T, _ *Switch, c *channelLink,
+			func(_ *testing.T, c *channelLink,
 				remoteChannel *lnwallet.LightningChannel) {
 
 				err := &lnwire.Error{}
@@ -5782,12 +5711,11 @@ func TestChannelLinkFail(t *testing.T) {
 
 	// Execute each test case.
 	for _, test := range testCases {
-		harness, err :=
+		link, remoteChannel, _, start, _, err :=
 			newSingleLinkTestHarness(t, chanAmt, 0)
 		require.NoError(t, err, test.name)
 
-		//nolint:forcetypeassert
-		coreLink := harness.aliceLink.(*channelLink)
+		coreLink := link.(*channelLink)
 
 		// Set up a channel used to check whether the link error
 		// force closed the channel.
@@ -5800,13 +5728,11 @@ func TestChannelLinkFail(t *testing.T) {
 
 		// Set up the link before starting it.
 		test.options(coreLink)
-		err = harness.start()
+		err = start()
 		require.NoError(t, err, test.name)
 
 		// Execute the test case.
-		test.linkTest(
-			t, harness.aliceSwitch, coreLink, harness.bobChannel,
-		)
+		test.linkTest(t, coreLink, remoteChannel)
 
 		// Currently we expect all test cases to lead to link error.
 		var linkErr LinkFailureError
@@ -6281,16 +6207,15 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
 	// We'll start by creating a new link with our chanAmt (5 BTC). We will
 	// only be testing Alice's behavior, so the reference to Bob's channel
 	// state is unnecessary.
-	harness, err :=
+	aliceLink, bobChannel, _, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink, nil,
-		harness.aliceRestore,
+		t, aliceLink, nil, restore,
 	)
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
@@ -6316,11 +6241,10 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
 	require.NoError(t, err, "unable to add invoice to registry")
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   alice.link,
-		aliceMsgs:   alice.msgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  alice.link,
+		aliceMsgs:  alice.msgs,
+		bobChannel: bobChannel,
 	}
 
 	// Lock in htlc paying the hodl invoice.
@@ -6336,7 +6260,7 @@ func TestChannelLinkHoldInvoiceRestart(t *testing.T) {
 
 	// Increase block height. This height will be retrieved by the link
 	// after restart.
-	harness.aliceSwitch.bestHeight++
+	coreLink.cfg.Switch.bestHeight++
 
 	// Restart link.
 	alice.restart(false, false)
@@ -6377,28 +6301,26 @@ func TestChannelLinkRevocationWindowRegular(t *testing.T) {
 	// We'll start by creating a new link with our chanAmt (5 BTC). We will
 	// only be testing Alice's behavior, so the reference to Bob's channel
 	// state is unnecessary.
-	harness, err :=
+	aliceLink, bobChannel, _, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
-	t.Cleanup(harness.aliceLink.Stop)
+	t.Cleanup(aliceLink.Stop)
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		registry  = coreLink.cfg.Registry.(*mockInvoiceRegistry)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	registry.settleChan = make(chan lntypes.Hash)
@@ -6462,17 +6384,16 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
 	// We'll start by creating a new link with our chanAmt (5 BTC). We will
 	// only be testing Alice's behavior, so the reference to Bob's channel
 	// state is unnecessary.
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		registry  = coreLink.cfg.Registry.(*mockInvoiceRegistry)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
@@ -6501,11 +6422,10 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
 	require.NoError(t, err, "unable to add invoice to registry")
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// Lock in htlc 1 on both sides.
@@ -6573,7 +6493,7 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
 
 	// Trigger the batch timer as this may trigger Alice to send a commit
 	// sig.
-	harness.aliceBatchTicker <- time.Time{}
+	batchTicker <- time.Time{}
 
 	// After the revocation, it is again possible for Alice to send a commit
 	// sig no more htlcs. Bob acks the update.
@@ -6585,7 +6505,7 @@ func TestChannelLinkRevocationWindowHodl(t *testing.T) {
 	ctx.receiveRevAndAckAliceToBob()
 
 	// Stop the link
-	harness.aliceLink.Stop()
+	aliceLink.Stop()
 
 	// Check that no unexpected messages were sent.
 	select {
@@ -6603,26 +6523,24 @@ func TestChannelLinkReceiveEmptySig(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	htlc, _ := generateHtlcAndInvoice(t, 0)
@@ -6633,7 +6551,7 @@ func TestChannelLinkReceiveEmptySig(t *testing.T) {
 
 	// Tick the batch ticker to trigger a commitsig from Alice->Bob.
 	select {
-	case harness.aliceBatchTicker <- time.Now():
+	case batchTicker <- time.Now():
 	case <-time.After(5 * time.Second):
 		t.Fatalf("could not force commit sig")
 	}
@@ -6662,7 +6580,7 @@ func TestChannelLinkReceiveEmptySig(t *testing.T) {
 	ctx.assertNoMsgFromAlice(time.Second)
 
 	// Stop the link
-	harness.aliceLink.Stop()
+	aliceLink.Stop()
 }
 
 // TestPendingCommitTicker tests that a link will fail itself after a timeout if
@@ -6672,13 +6590,12 @@ func TestPendingCommitTicker(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err, "unable to create link")
 
 	var (
-		//nolint:forcetypeassert
-		coreLink  = harness.aliceLink.(*channelLink)
+		coreLink  = aliceLink.(*channelLink)
 		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
 	)
 
@@ -6691,16 +6608,15 @@ func TestPendingCommitTicker(t *testing.T) {
 		linkErrs <- linkErr
 	}
 
-	if err := harness.start(); err != nil {
+	if err := start(); err != nil {
 		t.Fatalf("unable to start test harness: %v", err)
 	}
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		bobChannel:  harness.bobChannel,
-		aliceMsgs:   aliceMsgs,
+		t:          t,
+		aliceLink:  aliceLink,
+		bobChannel: bobChannel,
+		aliceMsgs:  aliceMsgs,
 	}
 
 	// Send an HTLC from Alice to Bob, and signal the batch ticker to signa
@@ -6708,7 +6624,7 @@ func TestPendingCommitTicker(t *testing.T) {
 	htlc, _ := generateHtlcAndInvoice(t, 0)
 	ctx.sendHtlcAliceToBob(0, htlc)
 	ctx.receiveHtlcAliceToBob()
-	harness.aliceBatchTicker <- time.Now()
+	batchTicker <- time.Now()
 
 	select {
 	case msg := <-aliceMsgs:
@@ -6733,7 +6649,7 @@ func TestPendingCommitTicker(t *testing.T) {
 	htlc, _ = generateHtlcAndInvoice(t, 1)
 	ctx.sendHtlcAliceToBob(1, htlc)
 	ctx.receiveHtlcAliceToBob()
-	harness.aliceBatchTicker <- time.Now()
+	batchTicker <- time.Now()
 
 	// Assert that we get the expected link failure from Alice.
 	select {
@@ -6757,13 +6673,12 @@ func TestPipelineSettle(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 	const chanReserve = btcutil.SatoshiPerBitcoin * 1
-	harness, err :=
+	aliceLink, bobChannel, _, start, restore, err :=
 		newSingleLinkTestHarness(t, chanAmt, chanReserve)
 	require.NoError(t, err)
 
 	alice := newPersistentLinkHarness(
-		t, harness.aliceSwitch, harness.aliceLink, nil,
-		harness.aliceRestore,
+		t, aliceLink, nil, restore,
 	)
 
 	linkErrors := make(chan LinkFailureError, 1)
@@ -6791,15 +6706,14 @@ func TestPipelineSettle(t *testing.T) {
 	// settle.
 	alice.coreLink.cfg.HodlMask = hodl.Mask(hodl.ExitSettle)
 
-	err = harness.start()
+	err = start()
 	require.NoError(t, err)
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   alice.link,
-		bobChannel:  harness.bobChannel,
-		aliceMsgs:   alice.msgs,
+		t:          t,
+		aliceLink:  alice.link,
+		bobChannel: bobChannel,
+		aliceMsgs:  alice.msgs,
 	}
 
 	// First lock in an HTLC from Bob to Alice.
@@ -6950,13 +6864,13 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 
 	const chanAmt = btcutil.SatoshiPerBitcoin * 5
 
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(t, chanAmt, 0)
 	require.NoError(t, err, "unable to create link")
 
-	require.NoError(t, harness.start())
+	require.NoError(t, start())
 
-	coreLink, ok := harness.aliceLink.(*channelLink)
+	coreLink, ok := aliceLink.(*channelLink)
 	require.True(t, ok)
 
 	mockPeer, ok := coreLink.cfg.Peer.(*mockPeer)
@@ -6976,11 +6890,10 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 	}
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		aliceMsgs:   aliceMsgs,
-		bobChannel:  harness.bobChannel,
+		t:          t,
+		aliceLink:  aliceLink,
+		aliceMsgs:  aliceMsgs,
+		bobChannel: bobChannel,
 	}
 
 	// Send and lock in htlc from Alice to Bob.
@@ -6990,7 +6903,7 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 	ctx.sendHtlcAliceToBob(htlcID, htlc)
 	ctx.receiveHtlcAliceToBob()
 
-	harness.aliceBatchTicker <- time.Now()
+	batchTicker <- time.Now()
 
 	ctx.receiveCommitSigAliceToBob(1)
 	ctx.sendRevAndAckBobToAlice()
@@ -7001,10 +6914,10 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 	// Return a short htlc failure from Bob to Alice and lock in.
 	shortReason := make([]byte, 260)
 
-	err = harness.bobChannel.FailHTLC(0, shortReason, nil, nil, nil)
+	err = bobChannel.FailHTLC(0, shortReason, nil, nil, nil)
 	require.NoError(t, err)
 
-	harness.aliceLink.HandleChannelUpdate(&lnwire.UpdateFailHTLC{
+	aliceLink.HandleChannelUpdate(&lnwire.UpdateFailHTLC{
 		ID:     htlcID,
 		Reason: shortReason,
 	})
@@ -7028,7 +6941,7 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 	require.Len(t, htlcFailMsg.Reason, 292)
 
 	// Stop the link
-	harness.aliceLink.Stop()
+	aliceLink.Stop()
 
 	// Check that no unexpected messages were sent.
 	select {
@@ -7048,12 +6961,11 @@ func TestChannelLinkShortFailureRelay(t *testing.T) {
 // unaffected by calls to EnableAdds/DisableAdds on the Incoming direction and
 // vice-versa.
 func TestLinkFlushApiDirectionIsolation(t *testing.T) {
-	harness, _ :=
+	aliceLink, _, _, _, _, _ :=
 		newSingleLinkTestHarness(
 			t, 5*btcutil.SatoshiPerBitcoin,
 			1*btcutil.SatoshiPerBitcoin,
 		)
-	aliceLink := harness.aliceLink
 
 	for i := 0; i < 10; i++ {
 		if prand.Uint64()%2 == 0 {
@@ -7086,12 +6998,11 @@ func TestLinkFlushApiDirectionIsolation(t *testing.T) {
 // error (to inform the caller that the call was unnecessary in case it implies
 // a bug in their logic).
 func TestLinkFlushApiGateStateIdempotence(t *testing.T) {
-	harness, _ :=
+	aliceLink, _, _, _, _, _ :=
 		newSingleLinkTestHarness(
 			t, 5*btcutil.SatoshiPerBitcoin,
 			1*btcutil.SatoshiPerBitcoin,
 		)
-	aliceLink := harness.aliceLink
 
 	for _, dir := range []LinkDirection{Incoming, Outgoing} {
 		require.True(t, aliceLink.DisableAdds(dir))
@@ -7109,39 +7020,34 @@ func TestLinkFlushApiGateStateIdempotence(t *testing.T) {
 }
 
 func TestLinkOutgoingCommitHooksCalled(t *testing.T) {
-	harness, err :=
+	aliceLink, bobChannel, batchTicker, start, _, err :=
 		newSingleLinkTestHarness(
 			t, 5*btcutil.SatoshiPerBitcoin,
 			btcutil.SatoshiPerBitcoin,
 		)
 	require.NoError(t, err)
 
-	require.NoError(t, harness.start(), "could not start link")
+	require.NoError(t, start(), "could not start link")
 
 	hookCalled := make(chan struct{})
 
-	var (
-		//nolint:forcetypeassert
-		coreLink = harness.aliceLink.(*channelLink)
-		//nolint:forcetypeassert
-		aliceMsgs = coreLink.cfg.Peer.(*mockPeer).sentMsgs
-	)
+	//nolint:forcetypeassert
+	aliceMsgs := aliceLink.(*channelLink).cfg.Peer.(*mockPeer).sentMsgs
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		bobChannel:  harness.bobChannel,
-		aliceMsgs:   aliceMsgs,
+		t:          t,
+		aliceLink:  aliceLink,
+		bobChannel: bobChannel,
+		aliceMsgs:  aliceMsgs,
 	}
 
 	// Set up a pending HTLC on the link.
 	//nolint:forcetypeassert
-	htlc := generateHtlc(t, harness.aliceLink.(*channelLink), 0)
+	htlc := generateHtlc(t, aliceLink.(*channelLink), 0)
 	ctx.sendHtlcAliceToBob(0, htlc)
 	ctx.receiveHtlcAliceToBob()
 
-	harness.aliceLink.OnCommitOnce(Outgoing, func() {
+	aliceLink.OnCommitOnce(Outgoing, func() {
 		close(hookCalled)
 	})
 
@@ -7151,11 +7057,11 @@ func TestLinkOutgoingCommitHooksCalled(t *testing.T) {
 	case <-time.NewTimer(time.Second).C:
 	}
 
-	harness.aliceBatchTicker <- time.Now()
+	batchTicker <- time.Now()
 
 	// Send a second tick just to ensure the hook isn't called more than
 	// once.
-	harness.aliceBatchTicker <- time.Now()
+	batchTicker <- time.Now()
 
 	select {
 	case <-hookCalled:
@@ -7165,26 +7071,23 @@ func TestLinkOutgoingCommitHooksCalled(t *testing.T) {
 }
 
 func TestLinkFlushHooksCalled(t *testing.T) {
-	harness, err :=
+	aliceLink, bobChannel, _, start, _, err :=
 		newSingleLinkTestHarness(
 			t, 5*btcutil.SatoshiPerBitcoin,
 			btcutil.SatoshiPerBitcoin,
 		)
 	require.NoError(t, err)
 
-	require.NoError(t, harness.start(), "could not start link")
+	require.NoError(t, start(), "could not start link")
 
 	//nolint:forcetypeassert
-	coreLink := harness.aliceLink.(*channelLink)
-	//nolint:forcetypeassert
-	aliceMsgs := coreLink.cfg.Peer.(*mockPeer).sentMsgs
+	aliceMsgs := aliceLink.(*channelLink).cfg.Peer.(*mockPeer).sentMsgs
 
 	ctx := linkTestContext{
-		t:           t,
-		aliceSwitch: harness.aliceSwitch,
-		aliceLink:   harness.aliceLink,
-		bobChannel:  harness.bobChannel,
-		aliceMsgs:   aliceMsgs,
+		t:          t,
+		aliceLink:  aliceLink,
+		bobChannel: bobChannel,
+		aliceMsgs:  aliceMsgs,
 	}
 
 	hookCalled := make(chan struct{})
@@ -7201,7 +7104,7 @@ func TestLinkFlushHooksCalled(t *testing.T) {
 	}
 
 	//nolint:forcetypeassert
-	htlc := generateHtlc(t, harness.aliceLink.(*channelLink), 0)
+	htlc := generateHtlc(t, aliceLink.(*channelLink), 0)
 
 	// A <- add -- B
 	ctx.sendHtlcBobToAlice(htlc)
@@ -7213,7 +7116,7 @@ func TestLinkFlushHooksCalled(t *testing.T) {
 	ctx.receiveRevAndAckAliceToBob()
 
 	// Register flush hook
-	harness.aliceLink.OnFlushedOnce(func() {
+	aliceLink.OnFlushedOnce(func() {
 		close(hookCalled)
 	})
 
diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go
index c4982b296..2d507f01b 100644
--- a/htlcswitch/switch_test.go
+++ b/htlcswitch/switch_test.go
@@ -5263,12 +5263,10 @@ func testSwitchHandlePacketForward(t *testing.T, zeroConf, private,
 	t.Parallel()
 
 	// Create a link for Alice that we'll add to the switch.
-	harness, err :=
+	aliceLink, _, _, _, _, err :=
 		newSingleLinkTestHarness(t, btcutil.SatoshiPerBitcoin, 0)
 	require.NoError(t, err)
 
-	aliceLink := harness.aliceLink
-
 	s, err := initSwitchWithTempDB(t, testStartingHeight)
 	if err != nil {
 		t.Fatalf("unable to init switch: %v", err)
@@ -5315,6 +5313,7 @@ func testSwitchHandlePacketForward(t *testing.T, zeroConf, private,
 	if zeroConf {
 		// Store the alias in the shortChanID field and mark the real
 		// scid in the database.
+		aliceChannelLink.shortChanID = aliceAlias
 		err = aliceChannelState.MarkRealScid(aliceScid)
 		require.NoError(t, err)
 
diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go
index c566118d6..e33c5fe20 100644
--- a/htlcswitch/test_utils.go
+++ b/htlcswitch/test_utils.go
@@ -1129,6 +1129,7 @@ func (h *hopNetwork) createChannelLink(server, peer *mockServer,
 
 	link := NewChannelLink(
 		ChannelLinkConfig{
+			Switch:        server.htlcSwitch,
 			BestHeight:    server.htlcSwitch.BestHeight,
 			FwrdingPolicy: h.globalPolicy,
 			Peer:          peer,
diff --git a/invoices/invoiceregistry_test.go b/invoices/invoiceregistry_test.go
index 99ad6cddd..04fe310b5 100644
--- a/invoices/invoiceregistry_test.go
+++ b/invoices/invoiceregistry_test.go
@@ -3,7 +3,6 @@ package invoices_test
 import (
 	"context"
 	"crypto/rand"
-	"database/sql"
 	"fmt"
 	"math"
 	"testing"
@@ -19,7 +18,6 @@ import (
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/record"
-	"github.com/lightningnetwork/lnd/sqldb"
 	"github.com/stretchr/testify/require"
 )
 
@@ -116,60 +114,12 @@ func TestInvoiceRegistry(t *testing.T) {
 		return db, testClock
 	}
 
-	// First create a shared Postgres instance so we don't spawn a new
-	// docker container for each test.
-	pgFixture := sqldb.NewTestPgFixture(
-		t, sqldb.DefaultPostgresFixtureLifetime,
-	)
-	t.Cleanup(func() {
-		pgFixture.TearDown(t)
-	})
-
-	makeSQLDB := func(t *testing.T, sqlite bool) (invpkg.InvoiceDB,
-		*clock.TestClock) {
-
-		var db *sqldb.BaseDB
-		if sqlite {
-			db = sqldb.NewTestSqliteDB(t).BaseDB
-		} else {
-			db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
-		}
-
-		executor := sqldb.NewTransactionExecutor(
-			db, func(tx *sql.Tx) sqldb.InvoiceQueries {
-				return db.WithTx(tx)
-			},
-		)
-
-		testClock := clock.NewTestClock(testNow)
-
-		return sqldb.NewInvoiceStore(executor, testClock), testClock
-	}
-
 	for _, test := range testList {
 		test := test
 
-		t.Run(test.name+"_KV", func(t *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			test.test(t, makeKeyValueDB)
 		})
-
-		t.Run(test.name+"_SQLite", func(t *testing.T) {
-			test.test(t,
-				func(t *testing.T) (
-					invpkg.InvoiceDB, *clock.TestClock) {
-
-					return makeSQLDB(t, true)
-				})
-		})
-
-		t.Run(test.name+"_Postgres", func(t *testing.T) {
-			test.test(t,
-				func(t *testing.T) (
-					invpkg.InvoiceDB, *clock.TestClock) {
-
-					return makeSQLDB(t, false)
-				})
-		})
 	}
 }
 
diff --git a/invoices/invoices_test.go b/invoices/invoices_test.go
index 7e1baadd3..caf58f13a 100644
--- a/invoices/invoices_test.go
+++ b/invoices/invoices_test.go
@@ -3,10 +3,8 @@ package invoices_test
 import (
 	"context"
 	"crypto/rand"
-	"database/sql"
 	"fmt"
 	"math"
-	"strings"
 	"testing"
 	"time"
 
@@ -18,7 +16,6 @@ import (
 	"github.com/lightningnetwork/lnd/lntypes"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/record"
-	"github.com/lightningnetwork/lnd/sqldb"
 	"github.com/stretchr/testify/require"
 )
 
@@ -37,23 +34,6 @@ var (
 	testNow = time.Unix(1, 0)
 )
 
-// randBytesToString will return a "safe" string from a byte slice. This is
-// used to generate random strings for the invoice payment request.
-func randBytesToString(buf []byte) (string, error) {
-	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
-	var stringBuilder strings.Builder
-
-	stringBuilder.Grow(len(buf))
-	for i := 0; i < len(buf); i++ {
-		ch := charset[int(buf[i])%len(charset)]
-		if err := stringBuilder.WriteByte(ch); err != nil {
-			return "", err
-		}
-	}
-
-	return stringBuilder.String(), nil
-}
-
 func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
 	var (
 		pre     lntypes.Preimage
@@ -88,11 +68,7 @@ func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
 		return nil, err
 	}
 	if r[0]&1 == 0 {
-		paymentReq, err := randBytesToString(r[:])
-		if err != nil {
-			return nil, err
-		}
-		i.PaymentRequest = []byte(paymentReq)
+		i.PaymentRequest = r[:]
 	} else {
 		i.PaymentRequest = []byte("")
 	}
@@ -101,13 +77,11 @@ func randInvoice(value lnwire.MilliSatoshi) (*invpkg.Invoice, error) {
 }
 
 // settleTestInvoice settles a test invoice.
-func settleTestInvoice(invoice *invpkg.Invoice, htlcID uint64,
-	settleIndex uint64) {
-
+func settleTestInvoice(invoice *invpkg.Invoice, settleIndex uint64) {
 	invoice.SettleDate = testNow
 	invoice.AmtPaid = invoice.Terms.Value
 	invoice.State = invpkg.ContractSettled
-	invoice.Htlcs[models.CircuitKey{HtlcID: htlcID}] = &invpkg.InvoiceHTLC{
+	invoice.Htlcs[models.CircuitKey{}] = &invpkg.InvoiceHTLC{
 		Amt:           invoice.Terms.Value,
 		AcceptTime:    testNow,
 		ResolveTime:   testNow,
@@ -222,52 +196,11 @@ func TestInvoices(t *testing.T) {
 		return db
 	}
 
-	// First create a shared Postgres instance so we don't spawn a new
-	// docker container for each test.
-	pgFixture := sqldb.NewTestPgFixture(
-		t, sqldb.DefaultPostgresFixtureLifetime,
-	)
-	t.Cleanup(func() {
-		pgFixture.TearDown(t)
-	})
-
-	makeSQLDB := func(t *testing.T, sqlite bool) invpkg.InvoiceDB {
-		var db *sqldb.BaseDB
-		if sqlite {
-			db = sqldb.NewTestSqliteDB(t).BaseDB
-		} else {
-			db = sqldb.NewTestPostgresDB(t, pgFixture).BaseDB
-		}
-
-		executor := sqldb.NewTransactionExecutor(
-			db, func(tx *sql.Tx) sqldb.InvoiceQueries {
-				return db.WithTx(tx)
-			},
-		)
-
-		testClock := clock.NewTestClock(testNow)
-
-		return sqldb.NewInvoiceStore(executor, testClock)
-	}
-
 	for _, test := range testList {
 		test := test
-		t.Run(test.name+"_KV", func(t *testing.T) {
-			test.test(t, makeKeyValueDB)
-		})
 
-		t.Run(test.name+"_SQLite", func(t *testing.T) {
-			test.test(t,
-				func(t *testing.T) invpkg.InvoiceDB {
-					return makeSQLDB(t, true)
-				})
-		})
-
-		t.Run(test.name+"_Postgres", func(t *testing.T) {
-			test.test(t,
-				func(t *testing.T) invpkg.InvoiceDB {
-					return makeSQLDB(t, false)
-				})
+		t.Run(test.name, func(t *testing.T) {
+			test.test(t, makeKeyValueDB)
 		})
 	}
 }
@@ -400,7 +333,7 @@ func testInvoiceWorkflowImpl(t *testing.T, test invWorkflowTest,
 	// now have the settled bit toggle to true and a non-default
 	// SettledDate
 	payAmt := fakeInvoice.Terms.Value * 2
-	_, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(0, payAmt))
+	_, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(payAmt))
 	require.NoError(t, err, "unable to settle invoice")
 	dbInvoice2, err := db.LookupInvoice(ctxb, ref)
 	require.NoError(t, err, "unable to fetch invoice")
@@ -579,7 +512,7 @@ func testFailInvoiceLookupMPPPayAddrOnly(t *testing.T,
 	// for the given HTLC.
 	ref := invpkg.InvoiceRefByHashAndAddr(payHash, payAddr)
 	_, err = db.LookupInvoice(ctxb, ref)
-	require.ErrorIs(t, err, invpkg.ErrInvoiceNotFound)
+	require.Equal(t, invpkg.ErrInvoiceNotFound, err)
 }
 
 // testInvRefEquivocation asserts that retrieving or updating an invoice using
@@ -757,7 +690,7 @@ func testInvoiceCancelSingleHtlcAMP(t *testing.T,
 
 	// At this point, we should detect that 3k satoshis total has been
 	// paid.
-	require.EqualValues(t, amt*3, dbInvoice.AmtPaid)
+	require.Equal(t, dbInvoice.AmtPaid, amt*3)
 
 	callback := func(
 		invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc, error) {
@@ -969,11 +902,8 @@ func testInvoiceAddTimeSeries(t *testing.T,
 	_, err = db.InvoicesSettledSince(ctxb, 0)
 	require.NoError(t, err)
 
-	var (
-		settledInvoices []invpkg.Invoice
-		settleIndex     uint64 = 1
-		htlcID          uint64 = 0
-	)
+	var settledInvoices []invpkg.Invoice
+	var settleIndex uint64 = 1
 	// We'll now only settle the latter half of each of those invoices.
 	for i := 10; i < len(invoices); i++ {
 		invoice := &invoices[i]
@@ -982,18 +912,15 @@ func testInvoiceAddTimeSeries(t *testing.T,
 
 		ref := invpkg.InvoiceRefByHash(paymentHash)
 		_, err := db.UpdateInvoice(
-			ctxb, ref, nil, getUpdateInvoice(
-				htlcID, invoice.Terms.Value,
-			),
+			ctxb, ref, nil, getUpdateInvoice(invoice.Terms.Value),
 		)
 		if err != nil {
 			t.Fatalf("unable to settle invoice: %v", err)
 		}
 
 		// Create the settled invoice for the expectation set.
-		settleTestInvoice(invoice, htlcID, settleIndex)
+		settleTestInvoice(invoice, settleIndex)
 		settleIndex++
-		htlcID++
 
 		settledInvoices = append(settledInvoices, *invoice)
 	}
@@ -1033,10 +960,7 @@ func testInvoiceAddTimeSeries(t *testing.T,
 			t.Fatalf("unable to query: %v", err)
 		}
 
-		require.Equal(
-			t, len(query.resp), len(resp),
-			fmt.Sprintf("test: #%v", i),
-		)
+		require.Equal(t, len(query.resp), len(resp))
 
 		for j := 0; j < len(query.resp); j++ {
 			require.Equal(t,
@@ -1214,11 +1138,7 @@ func testSettleIndexAmpPayments(t *testing.T,
 			AddIndex: testInvoice.AddIndex,
 		},
 	})
-	require.NoError(t, err)
-
-	settledInvoices, err = db.InvoicesSettledSince(ctxb, 0)
-	require.NoError(t, err)
-	require.Len(t, settledInvoices, 0)
+	require.Nil(t, err)
 }
 
 // testFetchPendingInvoices tests that we can fetch all pending invoices from
@@ -1238,11 +1158,7 @@ func testFetchPendingInvoices(t *testing.T,
 	require.Empty(t, pending)
 
 	const numInvoices = 20
-	var (
-		settleIndex uint64 = 1
-		htlcID      uint64 = 0
-	)
-
+	var settleIndex uint64 = 1
 	pendingInvoices := make(map[lntypes.Hash]invpkg.Invoice)
 
 	for i := 1; i <= numInvoices; i++ {
@@ -1266,14 +1182,11 @@ func testFetchPendingInvoices(t *testing.T,
 		}
 
 		ref := invpkg.InvoiceRefByHash(paymentHash)
-		_, err = db.UpdateInvoice(
-			ctxb, ref, nil, getUpdateInvoice(htlcID, amt),
-		)
+		_, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(amt))
 		require.NoError(t, err)
 
-		settleTestInvoice(invoice, htlcID, settleIndex)
+		settleTestInvoice(invoice, settleIndex)
 		settleIndex++
-		htlcID++
 	}
 
 	// Fetch all pending invoices.
@@ -1306,7 +1219,7 @@ func testDuplicateSettleInvoice(t *testing.T,
 	// With the invoice in the DB, we'll now attempt to settle the invoice.
 	ref := invpkg.InvoiceRefByHash(payHash)
 	dbInvoice, err := db.UpdateInvoice(
-		ctxb, ref, nil, getUpdateInvoice(0, amt),
+		ctxb, ref, nil, getUpdateInvoice(amt),
 	)
 	require.NoError(t, err, "unable to settle invoice")
 
@@ -1331,9 +1244,7 @@ func testDuplicateSettleInvoice(t *testing.T,
 
 	// If we try to settle the invoice again, then we should get the very
 	// same invoice back, but with an error this time.
-	dbInvoice, err = db.UpdateInvoice(
-		ctxb, ref, nil, getUpdateInvoice(1, amt),
-	)
+	dbInvoice, err = db.UpdateInvoice(ctxb, ref, nil, getUpdateInvoice(amt))
 	require.ErrorIs(t, err, invpkg.ErrInvoiceAlreadySettled)
 
 	if dbInvoice == nil {
@@ -1358,12 +1269,9 @@ func testQueryInvoices(t *testing.T,
 	// assume that the index of the invoice within the database is the same
 	// as the amount of the invoice itself.
 	const numInvoices = 50
-	var (
-		settleIndex     uint64 = 1
-		htlcID          uint64 = 0
-		invoices        []invpkg.Invoice
-		pendingInvoices []invpkg.Invoice
-	)
+	var settleIndex uint64 = 1
+	var invoices []invpkg.Invoice
+	var pendingInvoices []invpkg.Invoice
 
 	ctxb := context.Background()
 	for i := 1; i <= numInvoices; i++ {
@@ -1388,16 +1296,15 @@ func testQueryInvoices(t *testing.T,
 		if i%2 == 0 {
 			ref := invpkg.InvoiceRefByHash(paymentHash)
 			_, err := db.UpdateInvoice(
-				ctxb, ref, nil, getUpdateInvoice(htlcID, amt),
+				ctxb, ref, nil, getUpdateInvoice(amt),
 			)
 			if err != nil {
 				t.Fatalf("unable to settle invoice: %v", err)
 			}
 
 			// Create the settled invoice for the expectation set.
-			settleTestInvoice(invoice, htlcID, settleIndex)
+			settleTestInvoice(invoice, settleIndex)
 			settleIndex++
-			htlcID++
 		} else {
 			pendingInvoices = append(pendingInvoices, *invoice)
 		}
@@ -1742,8 +1649,7 @@ func testQueryInvoices(t *testing.T,
 			t.Fatalf("unable to query invoice database: %v", err)
 		}
 
-		require.Equal(t, len(testCase.expected), len(response.Invoices),
-			fmt.Sprintf("test: #%v", i))
+		require.Equal(t, len(testCase.expected), len(response.Invoices))
 
 		for j, expected := range testCase.expected {
 			require.Equal(t,
@@ -1756,9 +1662,7 @@ func testQueryInvoices(t *testing.T,
 
 // getUpdateInvoice returns an invoice update callback that, when called,
 // settles the invoice with the given amount.
-func getUpdateInvoice(htlcID uint64,
-	amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback {
-
+func getUpdateInvoice(amt lnwire.MilliSatoshi) invpkg.InvoiceUpdateCallback {
 	return func(invoice *invpkg.Invoice) (*invpkg.InvoiceUpdateDesc,
 		error) {
 
@@ -1768,7 +1672,7 @@ func getUpdateInvoice(htlcID uint64,
 
 		noRecords := make(record.CustomSet)
 		htlcs := map[models.CircuitKey]*invpkg.HtlcAcceptDesc{
-			{HtlcID: htlcID}: {
+			{}: {
 				Amt:           amt,
 				CustomRecords: noRecords,
 			},
@@ -2178,10 +2082,7 @@ func testSetIDIndex(t *testing.T, makeDB func(t *testing.T) invpkg.InvoiceDB) {
 	)
 	_, err = db.UpdateInvoice(
 		ctxb, ref2, (*invpkg.SetID)(setID),
-		// Note: we need a unique HTLC ID here otherwise the update will
-		// be rejected as a duplicate (due to SQL unique constraint
-		// violation).
-		updateAcceptAMPHtlc(55, amt, setID, true),
+		updateAcceptAMPHtlc(0, amt, setID, true),
 	)
 	require.Equal(t, invpkg.ErrDuplicateSetID{SetID: *setID}, err)
 
@@ -2465,7 +2366,7 @@ func testUnexpectedInvoicePreimage(t *testing.T,
 	// payment hash against which to validate the preimage.
 	_, err = db.UpdateInvoice(
 		ctxb, invpkg.InvoiceRefByAddr(invoice.Terms.PaymentAddr), nil,
-		getUpdateInvoice(0, invoice.Terms.Value),
+		getUpdateInvoice(invoice.Terms.Value),
 	)
 
 	// Assert that we get ErrUnexpectedInvoicePreimage.
@@ -2600,9 +2501,7 @@ func testDeleteInvoices(t *testing.T,
 		if i == 1 {
 			invoice, err = db.UpdateInvoice(
 				ctxb, invpkg.InvoiceRefByHash(paymentHash), nil,
-				getUpdateInvoice(
-					uint64(i), invoice.Terms.Value,
-				),
+				getUpdateInvoice(invoice.Terms.Value),
 			)
 			require.NoError(t, err, "unable to settle invoice")
 		}
diff --git a/invoices/test_utils_test.go b/invoices/test_utils_test.go
index a0adf7dc8..dd2638408 100644
--- a/invoices/test_utils_test.go
+++ b/invoices/test_utils_test.go
@@ -231,7 +231,7 @@ func timeout() func() {
 
 	go func() {
 		select {
-		case <-time.After(10 * time.Second):
+		case <-time.After(5 * time.Second):
 			err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
 			if err != nil {
 				panic(fmt.Sprintf("error writing to std out "+
diff --git a/itest/lnd_test.go b/itest/lnd_test.go
index 9c823b5a8..cb9f60dbf 100644
--- a/itest/lnd_test.go
+++ b/itest/lnd_test.go
@@ -71,9 +71,6 @@ var (
 	dbBackendFlag = flag.String("dbbackend", "bbolt", "Database backend "+
 		"(bbolt, etcd, postgres)")
 
-	nativeSQLFlag = flag.Bool("nativesql", false, "Database backend to "+
-		"use native SQL when applicable (only for sqlite and postgres")
-
 	// lndExecutable is the full path to the lnd binary.
 	lndExecutable = flag.String(
 		"lndexec", itestLndBinary, "full path to lnd binary",
@@ -98,7 +95,7 @@ func TestLightningNetworkDaemon(t *testing.T) {
 	// Get the binary path and setup the harness test.
 	binary := getLndBinary(t)
 	harnessTest := lntest.SetupHarness(
-		t, binary, *dbBackendFlag, *nativeSQLFlag, feeService,
+		t, binary, *dbBackendFlag, feeService,
 	)
 	defer harnessTest.Stop()
 
diff --git a/lntest/harness.go b/lntest/harness.go
index 1b9d18fae..c0a5da7f0 100644
--- a/lntest/harness.go
+++ b/lntest/harness.go
@@ -103,14 +103,14 @@ type HarnessTest struct {
 // NewHarnessTest creates a new instance of a harnessTest from a regular
 // testing.T instance.
 func NewHarnessTest(t *testing.T, lndBinary string, feeService WebFeeService,
-	dbBackend node.DatabaseBackend, nativeSQL bool) *HarnessTest {
+	dbBackend node.DatabaseBackend) *HarnessTest {
 
 	t.Helper()
 
 	// Create the run context.
 	ctxt, cancel := context.WithCancel(context.Background())
 
-	manager := newNodeManager(lndBinary, dbBackend, nativeSQL)
+	manager := newNodeManager(lndBinary, dbBackend)
 
 	return &HarnessTest{
 		T:          t,
diff --git a/lntest/harness_node_manager.go b/lntest/harness_node_manager.go
index 3b0c412d9..dd1b59ea3 100644
--- a/lntest/harness_node_manager.go
+++ b/lntest/harness_node_manager.go
@@ -31,10 +31,6 @@ type nodeManager struct {
 	// dbBackend sets the database backend to use.
 	dbBackend node.DatabaseBackend
 
-	// nativeSQL sets the database backend to use native SQL when
-	// applicable.
-	nativeSQL bool
-
 	// activeNodes is a map of all running nodes, format:
 	// {pubkey: *HarnessNode}.
 	activeNodes map[uint32]*node.HarnessNode
@@ -52,13 +48,12 @@ type nodeManager struct {
 }
 
 // newNodeManager creates a new node manager instance.
-func newNodeManager(lndBinary string, dbBackend node.DatabaseBackend,
-	nativeSQL bool) *nodeManager {
+func newNodeManager(lndBinary string,
+	dbBackend node.DatabaseBackend) *nodeManager {
 
 	return &nodeManager{
 		lndBinary:    lndBinary,
 		dbBackend:    dbBackend,
-		nativeSQL:    nativeSQL,
 		activeNodes:  make(map[uint32]*node.HarnessNode),
 		standbyNodes: make(map[uint32]*node.HarnessNode),
 	}
@@ -85,7 +80,6 @@ func (nm *nodeManager) newNode(t *testing.T, name string, extraArgs []string,
 		ExtraArgs:         extraArgs,
 		FeeURL:            nm.feeServiceURL,
 		DBBackend:         nm.dbBackend,
-		NativeSQL:         nm.nativeSQL,
 		NodeID:            nm.nextNodeID(),
 		LndBinary:         nm.lndBinary,
 		NetParams:         harnessNetParams,
diff --git a/lntest/harness_setup.go b/lntest/harness_setup.go
index 61eac5d2f..20442f9c2 100644
--- a/lntest/harness_setup.go
+++ b/lntest/harness_setup.go
@@ -18,7 +18,7 @@ import (
 // 4. connect the miner and the chain backend.
 // 5. start the HarnessTest.
 func SetupHarness(t *testing.T, binaryPath, dbBackendName string,
-	nativeSQL bool, feeService WebFeeService) *HarnessTest {
+	feeService WebFeeService) *HarnessTest {
 
 	t.Log("Setting up HarnessTest...")
 
@@ -30,7 +30,7 @@ func SetupHarness(t *testing.T, binaryPath, dbBackendName string,
 	dbBackend := prepareDBBackend(t, dbBackendName)
 
 	// Create a new HarnessTest.
-	ht := NewHarnessTest(t, binaryPath, feeService, dbBackend, nativeSQL)
+	ht := NewHarnessTest(t, binaryPath, feeService, dbBackend)
 
 	// Init the miner.
 	t.Log("Prepare the miner and mine blocks to activate segwit...")
diff --git a/lntest/node/config.go b/lntest/node/config.go
index 1573af42e..449ad2fad 100644
--- a/lntest/node/config.go
+++ b/lntest/node/config.go
@@ -119,7 +119,6 @@ type BaseNodeConfig struct {
 
 	DBBackend   DatabaseBackend
 	PostgresDsn string
-	NativeSQL   bool
 
 	// NodeID is a unique ID used to identify the node.
 	NodeID uint32
@@ -278,17 +277,11 @@ func (cfg *BaseNodeConfig) GenArgs() []string {
 	case BackendPostgres:
 		args = append(args, "--db.backend=postgres")
 		args = append(args, "--db.postgres.dsn="+cfg.PostgresDsn)
-		if cfg.NativeSQL {
-			args = append(args, "--db.use-native-sql")
-		}
 
 	case BackendSqlite:
 		args = append(args, "--db.backend=sqlite")
 		args = append(args, fmt.Sprintf("--db.sqlite.busytimeout=%v",
 			wait.SqliteBusyTimeout))
-		if cfg.NativeSQL {
-			args = append(args, "--db.use-native-sql")
-		}
 	}
 
 	if cfg.FeeURL != "" {
diff --git a/make/testing_flags.mk b/make/testing_flags.mk
index df20cdb7e..68afc9aa0 100644
--- a/make/testing_flags.mk
+++ b/make/testing_flags.mk
@@ -60,16 +60,10 @@ DEV_TAGS += kvdb_etcd
 endif
 
 ifeq ($(dbbackend),postgres)
-ifneq ($(nativesql),)
-ITEST_FLAGS += -nativesql
-endif
 DEV_TAGS += kvdb_postgres
 endif
 
 ifeq ($(dbbackend),sqlite)
-ifneq ($(nativesql),)
-ITEST_FLAGS += -nativesql
-endif
 DEV_TAGS += kvdb_sqlite
 endif