Repository: lightningnetwork/lnd
Pull request #9144 (2024-09-30 09:54:30)

lnd: add logger for rpcclient
Adding the logger of the rpc bitcoin connection via the subsystem name `RPCC`

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

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

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

---

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

<details>
<summary>❤️ Share</summary>

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

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

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

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

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

### CodeRabbit Commands (Invoked using PR comments)

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

### Other keywords and placeholders

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

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

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

### Documentation and Community

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

</details>

<!-- tips_end -->
ellemouton: @ziggie1984  - can you re-push here so that the required checks can pass with the new labels? 

Patch:
diff --git a/channeldb/db.go b/channeldb/db.go
index 92e0498ec..1e210d032 100644
--- a/channeldb/db.go
+++ b/channeldb/db.go
@@ -26,8 +26,6 @@ import (
 	"github.com/lightningnetwork/lnd/channeldb/migration29"
 	"github.com/lightningnetwork/lnd/channeldb/migration30"
 	"github.com/lightningnetwork/lnd/channeldb/migration31"
-	"github.com/lightningnetwork/lnd/channeldb/migration32"
-	"github.com/lightningnetwork/lnd/channeldb/migration33"
 	"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
 	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/invoices"
@@ -288,14 +286,6 @@ var (
 			number:    31,
 			migration: migration31.DeleteLastPublishedTxTLB,
 		},
-		{
-			number:    32,
-			migration: migration32.MigrateMCRouteSerialisation,
-		},
-		{
-			number:    33,
-			migration: migration33.MigrateMCStoreNameSpacedResults,
-		},
 	}
 
 	// optionalVersions stores all optional migrations that are applied
diff --git a/channeldb/log.go b/channeldb/log.go
index 10b1b54d3..a53d662cd 100644
--- a/channeldb/log.go
+++ b/channeldb/log.go
@@ -10,8 +10,6 @@ import (
 	"github.com/lightningnetwork/lnd/channeldb/migration24"
 	"github.com/lightningnetwork/lnd/channeldb/migration30"
 	"github.com/lightningnetwork/lnd/channeldb/migration31"
-	"github.com/lightningnetwork/lnd/channeldb/migration32"
-	"github.com/lightningnetwork/lnd/channeldb/migration33"
 	"github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
 	"github.com/lightningnetwork/lnd/kvdb"
 )
@@ -44,7 +42,5 @@ func UseLogger(logger btclog.Logger) {
 	migration24.UseLogger(logger)
 	migration30.UseLogger(logger)
 	migration31.UseLogger(logger)
-	migration32.UseLogger(logger)
-	migration33.UseLogger(logger)
 	kvdb.UseLogger(logger)
 }
diff --git a/channeldb/migration/lnwire21/custom_records.go b/channeldb/migration/lnwire21/custom_records.go
deleted file mode 100644
index f0f59185e..000000000
--- a/channeldb/migration/lnwire21/custom_records.go
+++ /dev/null
@@ -1,263 +0,0 @@
-package lnwire
-
-import (
-	"bytes"
-	"fmt"
-	"io"
-	"sort"
-
-	"github.com/lightningnetwork/lnd/fn"
-	"github.com/lightningnetwork/lnd/tlv"
-)
-
-const (
-	// MinCustomRecordsTlvType is the minimum custom records TLV type as
-	// defined in BOLT 01.
-	MinCustomRecordsTlvType = 65536
-)
-
-// CustomRecords stores a set of custom key/value pairs. Map keys are TLV types
-// which must be greater than or equal to MinCustomRecordsTlvType.
-type CustomRecords map[uint64][]byte
-
-// NewCustomRecords creates a new CustomRecords instance from a
-// tlv.TypeMap.
-func NewCustomRecords(tlvMap tlv.TypeMap) (CustomRecords, error) {
-	// Make comparisons in unit tests easy by returning nil if the map is
-	// empty.
-	if len(tlvMap) == 0 {
-		return nil, nil
-	}
-
-	customRecords := make(CustomRecords, len(tlvMap))
-	for k, v := range tlvMap {
-		customRecords[uint64(k)] = v
-	}
-
-	// Validate the custom records.
-	err := customRecords.Validate()
-	if err != nil {
-		return nil, fmt.Errorf("custom records from tlv map "+
-			"validation error: %w", err)
-	}
-
-	return customRecords, nil
-}
-
-// ParseCustomRecords creates a new CustomRecords instance from a tlv.Blob.
-func ParseCustomRecords(b tlv.Blob) (CustomRecords, error) {
-	return ParseCustomRecordsFrom(bytes.NewReader(b))
-}
-
-// ParseCustomRecordsFrom creates a new CustomRecords instance from a reader.
-func ParseCustomRecordsFrom(r io.Reader) (CustomRecords, error) {
-	typeMap, err := DecodeRecords(r)
-	if err != nil {
-		return nil, fmt.Errorf("error decoding HTLC record: %w", err)
-	}
-
-	return NewCustomRecords(typeMap)
-}
-
-// Validate checks that all custom records are in the custom type range.
-func (c CustomRecords) Validate() error {
-	if c == nil {
-		return nil
-	}
-
-	for key := range c {
-		if key < MinCustomRecordsTlvType {
-			return fmt.Errorf("custom records entry with TLV "+
-				"type below min: %d", MinCustomRecordsTlvType)
-		}
-	}
-
-	return nil
-}
-
-// Copy returns a copy of the custom records.
-func (c CustomRecords) Copy() CustomRecords {
-	if c == nil {
-		return nil
-	}
-
-	customRecords := make(CustomRecords, len(c))
-	for k, v := range c {
-		customRecords[k] = v
-	}
-
-	return customRecords
-}
-
-// ExtendRecordProducers extends the given records slice with the custom
-// records. The resultant records slice will be sorted if the given records
-// slice contains TLV types greater than or equal to MinCustomRecordsTlvType.
-func (c CustomRecords) ExtendRecordProducers(
-	producers []tlv.RecordProducer) ([]tlv.RecordProducer, error) {
-
-	// If the custom records are nil or empty, there is nothing to do.
-	if len(c) == 0 {
-		return producers, nil
-	}
-
-	// Validate the custom records.
-	err := c.Validate()
-	if err != nil {
-		return nil, err
-	}
-
-	// Ensure that the existing records slice TLV types are not also present
-	// in the custom records. If they are, the resultant extended records
-	// slice would erroneously contain duplicate TLV types.
-	for _, rp := range producers {
-		record := rp.Record()
-		recordTlvType := uint64(record.Type())
-
-		_, foundDuplicateTlvType := c[recordTlvType]
-		if foundDuplicateTlvType {
-			return nil, fmt.Errorf("custom records contains a TLV "+
-				"type that is already present in the "+
-				"existing records: %d", recordTlvType)
-		}
-	}
-
-	// Convert the custom records map to a TLV record producer slice and
-	// append them to the exiting records slice.
-	customRecordProducers := RecordsAsProducers(tlv.MapToRecords(c))
-	producers = append(producers, customRecordProducers...)
-
-	// If the records slice which was given as an argument included TLV
-	// values greater than or equal to the minimum custom records TLV type
-	// we will sort the extended records slice to ensure that it is ordered
-	// correctly.
-	SortProducers(producers)
-
-	return producers, nil
-}
-
-// RecordProducers returns a slice of record producers for the custom records.
-func (c CustomRecords) RecordProducers() []tlv.RecordProducer {
-	// If the custom records are nil or empty, return an empty slice.
-	if len(c) == 0 {
-		return nil
-	}
-
-	// Convert the custom records map to a TLV record producer slice.
-	records := tlv.MapToRecords(c)
-
-	return RecordsAsProducers(records)
-}
-
-// Serialize serializes the custom records into a byte slice.
-func (c CustomRecords) Serialize() ([]byte, error) {
-	records := tlv.MapToRecords(c)
-	return EncodeRecords(records)
-}
-
-// SerializeTo serializes the custom records into the given writer.
-func (c CustomRecords) SerializeTo(w io.Writer) error {
-	records := tlv.MapToRecords(c)
-	return EncodeRecordsTo(w, records)
-}
-
-// ProduceRecordsSorted converts a slice of record producers into a slice of
-// records and then sorts it by type.
-func ProduceRecordsSorted(recordProducers ...tlv.RecordProducer) []tlv.Record {
-	records := fn.Map(func(producer tlv.RecordProducer) tlv.Record {
-		return producer.Record()
-	}, recordProducers)
-
-	// Ensure that the set of records are sorted before we attempt to
-	// decode from the stream, to ensure they're canonical.
-	tlv.SortRecords(records)
-
-	return records
-}
-
-// SortProducers sorts the given record producers by their type.
-func SortProducers(producers []tlv.RecordProducer) {
-	sort.Slice(producers, func(i, j int) bool {
-		recordI := producers[i].Record()
-		recordJ := producers[j].Record()
-		return recordI.Type() < recordJ.Type()
-	})
-}
-
-// TlvMapToRecords converts a TLV map into a slice of records.
-func TlvMapToRecords(tlvMap tlv.TypeMap) []tlv.Record {
-	tlvMapGeneric := make(map[uint64][]byte)
-	for k, v := range tlvMap {
-		tlvMapGeneric[uint64(k)] = v
-	}
-
-	return tlv.MapToRecords(tlvMapGeneric)
-}
-
-// RecordsAsProducers converts a slice of records into a slice of record
-// producers.
-func RecordsAsProducers(records []tlv.Record) []tlv.RecordProducer {
-	return fn.Map(func(record tlv.Record) tlv.RecordProducer {
-		return &record
-	}, records)
-}
-
-// EncodeRecords encodes the given records into a byte slice.
-func EncodeRecords(records []tlv.Record) ([]byte, error) {
-	var buf bytes.Buffer
-	if err := EncodeRecordsTo(&buf, records); err != nil {
-		return nil, err
-	}
-
-	return buf.Bytes(), nil
-}
-
-// EncodeRecordsTo encodes the given records into the given writer.
-func EncodeRecordsTo(w io.Writer, records []tlv.Record) error {
-	tlvStream, err := tlv.NewStream(records...)
-	if err != nil {
-		return err
-	}
-
-	return tlvStream.Encode(w)
-}
-
-// DecodeRecords decodes the given byte slice into the given records and returns
-// the rest as a TLV type map.
-func DecodeRecords(r io.Reader,
-	records ...tlv.Record) (tlv.TypeMap, error) {
-
-	tlvStream, err := tlv.NewStream(records...)
-	if err != nil {
-		return nil, err
-	}
-
-	return tlvStream.DecodeWithParsedTypes(r)
-}
-
-// DecodeRecordsP2P decodes the given byte slice into the given records and
-// returns the rest as a TLV type map. This function is identical to
-// DecodeRecords except that the record size is capped at 65535.
-func DecodeRecordsP2P(r *bytes.Reader,
-	records ...tlv.Record) (tlv.TypeMap, error) {
-
-	tlvStream, err := tlv.NewStream(records...)
-	if err != nil {
-		return nil, err
-	}
-
-	return tlvStream.DecodeWithParsedTypesP2P(r)
-}
-
-// AssertUniqueTypes asserts that the given records have unique types.
-func AssertUniqueTypes(r []tlv.Record) error {
-	seen := make(fn.Set[tlv.Type], len(r))
-	for _, record := range r {
-		t := record.Type()
-		if seen.Contains(t) {
-			return fmt.Errorf("duplicate record type: %d", t)
-		}
-		seen.Add(t)
-	}
-
-	return nil
-}
diff --git a/channeldb/migration/lnwire21/onion_error.go b/channeldb/migration/lnwire21/onion_error.go
index 145e166c9..a2923b076 100644
--- a/channeldb/migration/lnwire21/onion_error.go
+++ b/channeldb/migration/lnwire21/onion_error.go
@@ -54,8 +54,6 @@ type FailCode uint16
 
 // The currently defined onion failure types within this current version of the
 // Lightning protocol.
-//
-//nolint:lll
 const (
 	CodeNone                             FailCode = 0
 	CodeInvalidRealm                              = FlagBadOnion | 1
@@ -82,7 +80,6 @@ const (
 	CodeExpiryTooFar                     FailCode = 21
 	CodeInvalidOnionPayload                       = FlagPerm | 22
 	CodeMPPTimeout                       FailCode = 23
-	CodeInvalidBlinding                           = FlagBadOnion | FlagPerm | 24
 )
 
 // String returns the string representation of the failure code.
@@ -160,9 +157,6 @@ func (c FailCode) String() string {
 	case CodeMPPTimeout:
 		return "MPPTimeout"
 
-	case CodeInvalidBlinding:
-		return "InvalidBlinding"
-
 	default:
 		return "<unknown>"
 	}
@@ -577,51 +571,6 @@ func (f *FailInvalidOnionKey) Error() string {
 	return fmt.Sprintf("InvalidOnionKey(onion_sha=%x)", f.OnionSHA256[:])
 }
 
-// FailInvalidBlinding is returned if there has been a route blinding related
-// error.
-type FailInvalidBlinding struct {
-	OnionSHA256 [sha256.Size]byte
-}
-
-// Code returns the failure unique code.
-//
-// NOTE: Part of the FailureMessage interface.
-func (f *FailInvalidBlinding) Code() FailCode {
-	return CodeInvalidBlinding
-}
-
-// Returns a human readable string describing the target FailureMessage.
-//
-// NOTE: Implements the error interface.
-func (f *FailInvalidBlinding) Error() string {
-	return f.Code().String()
-}
-
-// Decode decodes the failure from bytes stream.
-//
-// NOTE: Part of the Serializable interface.
-func (f *FailInvalidBlinding) Decode(r io.Reader, _ uint32) error {
-	return ReadElement(r, f.OnionSHA256[:])
-}
-
-// Encode writes the failure in bytes stream.
-//
-// NOTE: Part of the Serializable interface.
-func (f *FailInvalidBlinding) Encode(w *bytes.Buffer, _ uint32) error {
-	return WriteElement(w, f.OnionSHA256[:])
-}
-
-// NewInvalidBlinding creates new instance of FailInvalidBlinding.
-func NewInvalidBlinding(onion []byte) *FailInvalidBlinding {
-	// The spec allows empty onion hashes for invalid blinding, so we only
-	// include our onion hash if it's provided.
-	if onion == nil {
-		return &FailInvalidBlinding{}
-	}
-
-	return &FailInvalidBlinding{OnionSHA256: sha256.Sum256(onion)}
-}
-
 // parseChannelUpdateCompatabilityMode will attempt to parse a channel updated
 // encoded into an onion error payload in two ways. First, we'll try the
 // compatibility oriented version wherein we'll _skip_ the length prefixing on
@@ -1443,9 +1392,6 @@ func makeEmptyOnionError(code FailCode) (FailureMessage, error) {
 	case CodeMPPTimeout:
 		return &FailMPPTimeout{}, nil
 
-	case CodeInvalidBlinding:
-		return &FailInvalidBlinding{}, nil
-
 	default:
 		return nil, errors.Errorf("unknown error code: %v", code)
 	}
diff --git a/channeldb/migration32/codec.go b/channeldb/migration32/codec.go
deleted file mode 100644
index 9f626c574..000000000
--- a/channeldb/migration32/codec.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package migration32
-
-import (
-	"encoding/binary"
-	"fmt"
-	"io"
-
-	"github.com/btcsuite/btcd/wire"
-	lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
-)
-
-var (
-	// Big endian is the preferred byte order, due to cursor scans over
-	// integer keys iterating in order.
-	byteOrder = binary.BigEndian
-)
-
-// ReadElement is a one-stop utility function to deserialize any datastructure
-// encoded using the serialization format of the database.
-func ReadElement(r io.Reader, element interface{}) error {
-	switch e := element.(type) {
-	case *uint32:
-		if err := binary.Read(r, byteOrder, e); err != nil {
-			return err
-		}
-
-	case *lnwire.MilliSatoshi:
-		var a uint64
-		if err := binary.Read(r, byteOrder, &a); err != nil {
-			return err
-		}
-
-		*e = lnwire.MilliSatoshi(a)
-
-	case *[]byte:
-		bytes, err := wire.ReadVarBytes(r, 0, 66000, "[]byte")
-		if err != nil {
-			return err
-		}
-
-		*e = bytes
-
-	case *int64, *uint64:
-		if err := binary.Read(r, byteOrder, e); err != nil {
-			return err
-		}
-
-	case *bool:
-		if err := binary.Read(r, byteOrder, e); err != nil {
-			return err
-		}
-
-	case *int32:
-		if err := binary.Read(r, byteOrder, e); err != nil {
-			return err
-		}
-
-	default:
-		return UnknownElementType{"ReadElement", e}
-	}
-
-	return nil
-}
-
-// ReadElements deserializes a variable number of elements into the passed
-// io.Reader, with each element being deserialized according to the ReadElement
-// function.
-func ReadElements(r io.Reader, elements ...interface{}) error {
-	for _, element := range elements {
-		err := ReadElement(r, element)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// UnknownElementType is an error returned when the codec is unable to encode or
-// decode a particular type.
-type UnknownElementType struct {
-	method  string
-	element interface{}
-}
-
-// Error returns the name of the method that encountered the error, as well as
-// the type that was unsupported.
-func (e UnknownElementType) Error() string {
-	return fmt.Sprintf("Unknown type in %s: %T", e.method, e.element)
-}
-
-// WriteElement is a one-stop shop to write the big endian representation of
-// any element which is to be serialized for storage on disk. The passed
-// io.Writer should be backed by an appropriately sized byte slice, or be able
-// to dynamically expand to accommodate additional data.
-func WriteElement(w io.Writer, element interface{}) error {
-	switch e := element.(type) {
-	case int64, uint64:
-		if err := binary.Write(w, byteOrder, e); err != nil {
-			return err
-		}
-
-	case uint32:
-		if err := binary.Write(w, byteOrder, e); err != nil {
-			return err
-		}
-
-	case int32:
-		if err := binary.Write(w, byteOrder, e); err != nil {
-			return err
-		}
-
-	case bool:
-		if err := binary.Write(w, byteOrder, e); err != nil {
-			return err
-		}
-
-	case lnwire.MilliSatoshi:
-		if err := binary.Write(w, byteOrder, uint64(e)); err != nil {
-			return err
-		}
-
-	case []byte:
-		if err := wire.WriteVarBytes(w, 0, e); err != nil {
-			return err
-		}
-
-	default:
-		return UnknownElementType{"WriteElement", e}
-	}
-
-	return nil
-}
-
-// WriteElements is writes each element in the elements slice to the passed
-// io.Writer using WriteElement.
-func WriteElements(w io.Writer, elements ...interface{}) error {
-	for _, element := range elements {
-		err := WriteElement(w, element)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
diff --git a/channeldb/migration32/hop.go b/channeldb/migration32/hop.go
deleted file mode 100644
index 5a741fae5..000000000
--- a/channeldb/migration32/hop.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package migration32
-
-import (
-	"github.com/btcsuite/btcd/btcec/v2"
-	"github.com/lightningnetwork/lnd/tlv"
-)
-
-const (
-	// EncryptedDataOnionType is the type used to include encrypted data
-	// provided by the receiver in the onion for use in blinded paths.
-	EncryptedDataOnionType tlv.Type = 10
-
-	// BlindingPointOnionType is the type used to include receiver provided
-	// ephemeral keys in the onion that are used in blinded paths.
-	BlindingPointOnionType tlv.Type = 12
-
-	// MetadataOnionType is the type used in the onion for the payment
-	// metadata.
-	MetadataOnionType tlv.Type = 16
-
-	// TotalAmtMsatBlindedType is the type used in the onion for the total
-	// amount field that is included in the final hop for blinded payments.
-	TotalAmtMsatBlindedType tlv.Type = 18
-)
-
-// NewEncryptedDataRecord creates a tlv.Record that encodes the encrypted_data
-// (type 10) record for an onion payload.
-func NewEncryptedDataRecord(data *[]byte) tlv.Record {
-	return tlv.MakePrimitiveRecord(EncryptedDataOnionType, data)
-}
-
-// NewBlindingPointRecord creates a tlv.Record that encodes the blinding_point
-// (type 12) record for an onion payload.
-func NewBlindingPointRecord(point **btcec.PublicKey) tlv.Record {
-	return tlv.MakePrimitiveRecord(BlindingPointOnionType, point)
-}
-
-// NewMetadataRecord creates a tlv.Record that encodes the metadata (type 10)
-// for an onion payload.
-func NewMetadataRecord(metadata *[]byte) tlv.Record {
-	return tlv.MakeDynamicRecord(
-		MetadataOnionType, metadata,
-		func() uint64 {
-			return uint64(len(*metadata))
-		},
-		tlv.EVarBytes, tlv.DVarBytes,
-	)
-}
-
-// NewTotalAmtMsatBlinded creates a tlv.Record that encodes the
-// total_amount_msat for the final an onion payload within a blinded route.
-func NewTotalAmtMsatBlinded(amt *uint64) tlv.Record {
-	return tlv.MakeDynamicRecord(
-		TotalAmtMsatBlindedType, amt, func() uint64 {
-			return tlv.SizeTUint64(*amt)
-		},
-		tlv.ETUint64, tlv.DTUint64,
-	)
-}
diff --git a/channeldb/migration32/log.go b/channeldb/migration32/log.go
deleted file mode 100644
index 98709c28e..000000000
--- a/channeldb/migration32/log.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package migration32
-
-import (
-	"github.com/btcsuite/btclog"
-)
-
-// log is a logger that is initialized as disabled.  This means the package will
-// not perform any logging by default until a logger is set.
-var log = btclog.Disabled
-
-// UseLogger uses a specified Logger to output package logging info.
-func UseLogger(logger btclog.Logger) {
-	log = logger
-}
diff --git a/channeldb/migration32/migration.go b/channeldb/migration32/migration.go
deleted file mode 100644
index 31d8ce39a..000000000
--- a/channeldb/migration32/migration.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package migration32
-
-import (
-	"bytes"
-	"fmt"
-
-	"github.com/lightningnetwork/lnd/kvdb"
-)
-
-// MigrateMCRouteSerialisation reads all the mission control store entries and
-// re-serializes them using a minimal route serialisation so that only the parts
-// of the route that are actually required for mission control are persisted.
-func MigrateMCRouteSerialisation(tx kvdb.RwTx) error {
-	log.Infof("Migrating Mission Control store to use a more minimal " +
-		"encoding for routes")
-
-	resultBucket := tx.ReadWriteBucket(resultsKey)
-
-	// If the results bucket does not exist then there are no entries in
-	// the mission control store yet and so there is nothing to migrate.
-	if resultBucket == nil {
-		return nil
-	}
-
-	// For each entry, read it into memory using the old encoding. Then,
-	// extract the more minimal route, re-encode and persist the entry.
-	return resultBucket.ForEach(func(k, v []byte) error {
-		// Read the entry using the old encoding.
-		resultOld, err := deserializeOldResult(k, v)
-		if err != nil {
-			return err
-		}
-
-		// Convert to the new payment result format with the minimal
-		// route.
-		resultNew := convertPaymentResult(resultOld)
-
-		// Serialise the new payment result using the new encoding.
-		key, resultNewBytes, err := serializeNewResult(resultNew)
-		if err != nil {
-			return err
-		}
-
-		// Make sure that the derived key is the same.
-		if !bytes.Equal(key, k) {
-			return fmt.Errorf("new payment result key (%v) is "+
-				"not the same as the old key (%v)", key, k)
-		}
-
-		// Finally, overwrite the previous value with the new encoding.
-		return resultBucket.Put(k, resultNewBytes)
-	})
-}
diff --git a/channeldb/migration32/mission_control_store.go b/channeldb/migration32/mission_control_store.go
deleted file mode 100644
index 3953cd1f2..000000000
--- a/channeldb/migration32/mission_control_store.go
+++ /dev/null
@@ -1,320 +0,0 @@
-package migration32
-
-import (
-	"bytes"
-	"io"
-	"math"
-	"time"
-
-	"github.com/btcsuite/btcd/wire"
-	lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
-)
-
-const (
-	// unknownFailureSourceIdx is the database encoding of an unknown error
-	// source.
-	unknownFailureSourceIdx = -1
-)
-
-var (
-	// resultsKey is the fixed key under which the attempt results are
-	// stored.
-	resultsKey = []byte("missioncontrol-results")
-)
-
-// paymentResultCommon holds the fields that are shared by the old and new
-// payment result encoding.
-type paymentResultCommon struct {
-	id                 uint64
-	timeFwd, timeReply time.Time
-	success            bool
-	failureSourceIdx   *int
-	failure            lnwire.FailureMessage
-}
-
-// paymentResultOld is the information that becomes available when a payment
-// attempt completes.
-type paymentResultOld struct {
-	paymentResultCommon
-	route *Route
-}
-
-// deserializeOldResult deserializes a payment result using the old encoding.
-func deserializeOldResult(k, v []byte) (*paymentResultOld, error) {
-	// Parse payment id.
-	result := paymentResultOld{
-		paymentResultCommon: paymentResultCommon{
-			id: byteOrder.Uint64(k[8:]),
-		},
-	}
-
-	r := bytes.NewReader(v)
-
-	// Read timestamps, success status and failure source index.
-	var (
-		timeFwd, timeReply uint64
-		dbFailureSourceIdx int32
-	)
-
-	err := ReadElements(
-		r, &timeFwd, &timeReply, &result.success, &dbFailureSourceIdx,
-	)
-	if err != nil {
-		return nil, err
-	}
-
-	// Convert time stamps to local time zone for consistent logging.
-	result.timeFwd = time.Unix(0, int64(timeFwd)).Local()
-	result.timeReply = time.Unix(0, int64(timeReply)).Local()
-
-	// Convert from unknown index magic number to nil value.
-	if dbFailureSourceIdx != unknownFailureSourceIdx {
-		failureSourceIdx := int(dbFailureSourceIdx)
-		result.failureSourceIdx = &failureSourceIdx
-	}
-
-	// Read route.
-	route, err := DeserializeRoute(r)
-	if err != nil {
-		return nil, err
-	}
-	result.route = &route
-
-	// Read failure.
-	failureBytes, err := wire.ReadVarBytes(r, 0, math.MaxUint16, "failure")
-	if err != nil {
-		return nil, err
-	}
-	if len(failureBytes) > 0 {
-		result.failure, err = lnwire.DecodeFailureMessage(
-			bytes.NewReader(failureBytes), 0,
-		)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return &result, nil
-}
-
-// convertPaymentResult converts a paymentResultOld to a paymentResultNew.
-func convertPaymentResult(old *paymentResultOld) *paymentResultNew {
-	return &paymentResultNew{
-		paymentResultCommon: old.paymentResultCommon,
-		route:               extractMCRoute(old.route),
-	}
-}
-
-// paymentResultNew is the information that becomes available when a payment
-// attempt completes.
-type paymentResultNew struct {
-	paymentResultCommon
-	route *mcRoute
-}
-
-// extractMCRoute extracts the fields required by MC from the Route struct to
-// create the more minimal mcRoute struct.
-func extractMCRoute(route *Route) *mcRoute {
-	return &mcRoute{
-		sourcePubKey: route.SourcePubKey,
-		totalAmount:  route.TotalAmount,
-		hops:         extractMCHops(route.Hops),
-	}
-}
-
-// extractMCHops extracts the Hop fields that MC actually uses from a slice of
-// Hops.
-func extractMCHops(hops []*Hop) []*mcHop {
-	mcHops := make([]*mcHop, len(hops))
-	for i, hop := range hops {
-		mcHops[i] = extractMCHop(hop)
-	}
-
-	return mcHops
-}
-
-// extractMCHop extracts the Hop fields that MC actually uses from a Hop.
-func extractMCHop(hop *Hop) *mcHop {
-	return &mcHop{
-		channelID:        hop.ChannelID,
-		pubKeyBytes:      hop.PubKeyBytes,
-		amtToFwd:         hop.AmtToForward,
-		hasBlindingPoint: hop.BlindingPoint != nil,
-		hasCustomRecords: len(hop.CustomRecords) > 0,
-	}
-}
-
-// mcRoute holds the bare minimum info about a payment attempt route that MC
-// requires.
-type mcRoute struct {
-	sourcePubKey Vertex
-	totalAmount  lnwire.MilliSatoshi
-	hops         []*mcHop
-}
-
-// mcHop holds the bare minimum info about a payment attempt route hop that MC
-// requires.
-type mcHop struct {
-	channelID        uint64
-	pubKeyBytes      Vertex
-	amtToFwd         lnwire.MilliSatoshi
-	hasBlindingPoint bool
-	hasCustomRecords bool
-}
-
-// serializeOldResult serializes a payment result and returns a key and value
-// byte slice to insert into the bucket.
-func serializeOldResult(rp *paymentResultOld) ([]byte, []byte, error) {
-	// Write timestamps, success status, failure source index and route.
-	var b bytes.Buffer
-	var dbFailureSourceIdx int32
-	if rp.failureSourceIdx == nil {
-		dbFailureSourceIdx = unknownFailureSourceIdx
-	} else {
-		dbFailureSourceIdx = int32(*rp.failureSourceIdx)
-	}
-	err := WriteElements(
-		&b,
-		uint64(rp.timeFwd.UnixNano()),
-		uint64(rp.timeReply.UnixNano()),
-		rp.success, dbFailureSourceIdx,
-	)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if err := SerializeRoute(&b, *rp.route); err != nil {
-		return nil, nil, err
-	}
-
-	// Write failure. If there is no failure message, write an empty
-	// byte slice.
-	var failureBytes bytes.Buffer
-	if rp.failure != nil {
-		err := lnwire.EncodeFailureMessage(&failureBytes, rp.failure, 0)
-		if err != nil {
-			return nil, nil, err
-		}
-	}
-	err = wire.WriteVarBytes(&b, 0, failureBytes.Bytes())
-	if err != nil {
-		return nil, nil, err
-	}
-	// Compose key that identifies this result.
-	key := getResultKeyOld(rp)
-
-	return key, b.Bytes(), nil
-}
-
-// getResultKeyOld returns a byte slice representing a unique key for this
-// payment result.
-func getResultKeyOld(rp *paymentResultOld) []byte {
-	var keyBytes [8 + 8 + 33]byte
-
-	// Identify records by a combination of time, payment id and sender pub
-	// key. This allows importing mission control data from an external
-	// source without key collisions and keeps the records sorted
-	// chronologically.
-	byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
-	byteOrder.PutUint64(keyBytes[8:], rp.id)
-	copy(keyBytes[16:], rp.route.SourcePubKey[:])
-
-	return keyBytes[:]
-}
-
-// serializeNewResult serializes a payment result and returns a key and value
-// byte slice to insert into the bucket.
-func serializeNewResult(rp *paymentResultNew) ([]byte, []byte, error) {
-	// Write timestamps, success status, failure source index and route.
-	var b bytes.Buffer
-
-	var dbFailureSourceIdx int32
-	if rp.failureSourceIdx == nil {
-		dbFailureSourceIdx = unknownFailureSourceIdx
-	} else {
-		dbFailureSourceIdx = int32(*rp.failureSourceIdx)
-	}
-
-	err := WriteElements(
-		&b,
-		uint64(rp.timeFwd.UnixNano()),
-		uint64(rp.timeReply.UnixNano()),
-		rp.success, dbFailureSourceIdx,
-	)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	if err := serializeMCRoute(&b, rp.route); err != nil {
-		return nil, nil, err
-	}
-
-	// Write failure. If there is no failure message, write an empty
-	// byte slice.
-	var failureBytes bytes.Buffer
-	if rp.failure != nil {
-		err := lnwire.EncodeFailureMessage(&failureBytes, rp.failure, 0)
-		if err != nil {
-			return nil, nil, err
-		}
-	}
-	err = wire.WriteVarBytes(&b, 0, failureBytes.Bytes())
-	if err != nil {
-		return nil, nil, err
-	}
-
-	// Compose key that identifies this result.
-	key := getResultKeyNew(rp)
-
-	return key, b.Bytes(), nil
-}
-
-// getResultKeyNew returns a byte slice representing a unique key for this
-// payment result.
-func getResultKeyNew(rp *paymentResultNew) []byte {
-	var keyBytes [8 + 8 + 33]byte
-
-	// Identify records by a combination of time, payment id and sender pub
-	// key. This allows importing mission control data from an external
-	// source without key collisions and keeps the records sorted
-	// chronologically.
-	byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
-	byteOrder.PutUint64(keyBytes[8:], rp.id)
-	copy(keyBytes[16:], rp.route.sourcePubKey[:])
-
-	return keyBytes[:]
-}
-
-// serializeMCRoute serializes an mcRoute and writes the bytes to the given
-// io.Writer.
-func serializeMCRoute(w io.Writer, r *mcRoute) error {
-	if err := WriteElements(
-		w, r.totalAmount, r.sourcePubKey[:],
-	); err != nil {
-		return err
-	}
-
-	if err := WriteElements(w, uint32(len(r.hops))); err != nil {
-		return err
-	}
-
-	for _, h := range r.hops {
-		if err := serializeNewHop(w, h); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// serializeMCRoute serializes an mcHop and writes the bytes to the given
-// io.Writer.
-func serializeNewHop(w io.Writer, h *mcHop) error {
-	return WriteElements(w,
-		h.pubKeyBytes[:],
-		h.channelID,
-		h.amtToFwd,
-		h.hasBlindingPoint,
-		h.hasCustomRecords,
-	)
-}
diff --git a/channeldb/migration32/route.go b/channeldb/migration32/route.go
deleted file mode 100644
index a4d40a45c..000000000
--- a/channeldb/migration32/route.go
+++ /dev/null
@@ -1,583 +0,0 @@
-package migration32
-
-import (
-	"bytes"
-	"encoding/binary"
-	"fmt"
-	"io"
-
-	"github.com/btcsuite/btcd/btcec/v2"
-	"github.com/btcsuite/btcd/wire"
-	lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
-	"github.com/lightningnetwork/lnd/tlv"
-)
-
-const (
-	// MPPOnionType is the type used in the onion to reference the MPP
-	// fields: total_amt and payment_addr.
-	MPPOnionType tlv.Type = 8
-
-	// AMPOnionType is the type used in the onion to reference the AMP
-	// fields: root_share, set_id, and child_index.
-	AMPOnionType tlv.Type = 14
-)
-
-// VertexSize is the size of the array to store a vertex.
-const VertexSize = 33
-
-// Vertex is a simple alias for the serialization of a compressed Bitcoin
-// public key.
-type Vertex [VertexSize]byte
-
-// Route represents a path through the channel graph which runs over one or
-// more channels in succession. This struct carries all the information
-// required to craft the Sphinx onion packet, and send the payment along the
-// first hop in the path. A route is only selected as valid if all the channels
-// have sufficient capacity to carry the initial payment amount after fees are
-// accounted for.
-type Route struct {
-	// TotalTimeLock is the cumulative (final) time lock across the entire
-	// route. This is the CLTV value that should be extended to the first
-	// hop in the route. All other hops will decrement the time-lock as
-	// advertised, leaving enough time for all hops to wait for or present
-	// the payment preimage to complete the payment.
-	TotalTimeLock uint32
-
-	// TotalAmount is the total amount of funds required to complete a
-	// payment over this route. This value includes the cumulative fees at
-	// each hop. As a result, the HTLC extended to the first-hop in the
-	// route will need to have at least this many satoshis, otherwise the
-	// route will fail at an intermediate node due to an insufficient
-	// amount of fees.
-	TotalAmount lnwire.MilliSatoshi
-
-	// SourcePubKey is the pubkey of the node where this route originates
-	// from.
-	SourcePubKey Vertex
-
-	// Hops contains details concerning the specific forwarding details at
-	// each hop.
-	Hops []*Hop
-
-	// FirstHopAmount is the amount that should actually be sent to the
-	// first hop in the route. This is only different from TotalAmount above
-	// for custom channels where the on-chain amount doesn't necessarily
-	// reflect all the value of an outgoing payment.
-	FirstHopAmount tlv.RecordT[
-		tlv.TlvType0, tlv.BigSizeT[lnwire.MilliSatoshi],
-	]
-
-	// FirstHopWireCustomRecords is a set of custom records that should be
-	// included in the wire message sent to the first hop. This is only set
-	// on custom channels and is used to include additional information
-	// about the actual value of the payment.
-	//
-	// NOTE: Since these records already represent TLV records, and we
-	// enforce them to be in the custom range (e.g. >= 65536), we don't use
-	// another parent record type here. Instead, when serializing the Route
-	// we merge the TLV records together with the custom records and encode
-	// everything as a single TLV stream.
-	FirstHopWireCustomRecords lnwire.CustomRecords
-}
-
-// Hop represents an intermediate or final node of the route. This naming
-// is in line with the definition given in BOLT #4: Onion Routing Protocol.
-// The struct houses the channel along which this hop can be reached and
-// the values necessary to create the HTLC that needs to be sent to the
-// next hop. It is also used to encode the per-hop payload included within
-// the Sphinx packet.
-type Hop struct {
-	// PubKeyBytes is the raw bytes of the public key of the target node.
-	PubKeyBytes Vertex
-
-	// ChannelID is the unique channel ID for the channel. The first 3
-	// bytes are the block height, the next 3 the index within the block,
-	// and the last 2 bytes are the output index for the channel.
-	ChannelID uint64
-
-	// OutgoingTimeLock is the timelock value that should be used when
-	// crafting the _outgoing_ HTLC from this hop.
-	OutgoingTimeLock uint32
-
-	// AmtToForward is the amount that this hop will forward to the next
-	// hop. This value is less than the value that the incoming HTLC
-	// carries as a fee will be subtracted by the hop.
-	AmtToForward lnwire.MilliSatoshi
-
-	// MPP encapsulates the data required for option_mpp. This field should
-	// only be set for the final hop.
-	MPP *MPP
-
-	// AMP encapsulates the data required for option_amp. This field should
-	// only be set for the final hop.
-	AMP *AMP
-
-	// CustomRecords if non-nil are a set of additional TLV records that
-	// should be included in the forwarding instructions for this node.
-	CustomRecords lnwire.CustomRecords
-
-	// LegacyPayload if true, then this signals that this node doesn't
-	// understand the new TLV payload, so we must instead use the legacy
-	// payload.
-	//
-	// NOTE: we should no longer ever create a Hop with Legacy set to true.
-	// The only reason we are keeping this member is that it could be the
-	// case that we have serialised hops persisted to disk where
-	// LegacyPayload is true.
-	LegacyPayload bool
-
-	// Metadata is additional data that is sent along with the payment to
-	// the payee.
-	Metadata []byte
-
-	// EncryptedData is an encrypted data blob includes for hops that are
-	// part of a blinded route.
-	EncryptedData []byte
-
-	// BlindingPoint is an ephemeral public key used by introduction nodes
-	// in blinded routes to unblind their portion of the route and pass on
-	// the next ephemeral key to the next blinded node to do the same.
-	BlindingPoint *btcec.PublicKey
-
-	// TotalAmtMsat is the total amount for a blinded payment, potentially
-	// spread over more than one HTLC. This field should only be set for
-	// the final hop in a blinded path.
-	TotalAmtMsat lnwire.MilliSatoshi
-}
-
-// MPP is a record that encodes the fields necessary for multi-path payments.
-type MPP struct {
-	// paymentAddr is a random, receiver-generated value used to avoid
-	// collisions with concurrent payers.
-	paymentAddr [32]byte
-
-	// totalMsat is the total value of the payment, potentially spread
-	// across more than one HTLC.
-	totalMsat lnwire.MilliSatoshi
-}
-
-// Record returns a tlv.Record that can be used to encode or decode this record.
-func (r *MPP) Record() tlv.Record {
-	// Fixed-size, 32 byte payment address followed by truncated 64-bit
-	// total msat.
-	size := func() uint64 {
-		return 32 + tlv.SizeTUint64(uint64(r.totalMsat))
-	}
-
-	return tlv.MakeDynamicRecord(
-		MPPOnionType, r, size, MPPEncoder, MPPDecoder,
-	)
-}
-
-const (
-	// minMPPLength is the minimum length of a serialized MPP TLV record,
-	// which occurs when the truncated encoding of total_amt_msat takes 0
-	// bytes, leaving only the payment_addr.
-	minMPPLength = 32
-
-	// maxMPPLength is the maximum length of a serialized MPP TLV record,
-	// which occurs when the truncated encoding of total_amt_msat takes 8
-	// bytes.
-	maxMPPLength = 40
-)
-
-// MPPEncoder writes the MPP record to the provided io.Writer.
-func MPPEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
-	if v, ok := val.(*MPP); ok {
-		err := tlv.EBytes32(w, &v.paymentAddr, buf)
-		if err != nil {
-			return err
-		}
-
-		return tlv.ETUint64T(w, uint64(v.totalMsat), buf)
-	}
-
-	return tlv.NewTypeForEncodingErr(val, "MPP")
-}
-
-// MPPDecoder reads the MPP record to the provided io.Reader.
-func MPPDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
-	if v, ok := val.(*MPP); ok && minMPPLength <= l && l <= maxMPPLength {
-		if err := tlv.DBytes32(r, &v.paymentAddr, buf, 32); err != nil {
-			return err
-		}
-
-		var total uint64
-		if err := tlv.DTUint64(r, &total, buf, l-32); err != nil {
-			return err
-		}
-		v.totalMsat = lnwire.MilliSatoshi(total)
-
-		return nil
-	}
-
-	return tlv.NewTypeForDecodingErr(val, "MPP", l, maxMPPLength)
-}
-
-// AMP is a record that encodes the fields necessary for atomic multi-path
-// payments.
-type AMP struct {
-	rootShare  [32]byte
-	setID      [32]byte
-	childIndex uint32
-}
-
-// AMPEncoder writes the AMP record to the provided io.Writer.
-func AMPEncoder(w io.Writer, val interface{}, buf *[8]byte) error {
-	if v, ok := val.(*AMP); ok {
-		if err := tlv.EBytes32(w, &v.rootShare, buf); err != nil {
-			return err
-		}
-
-		if err := tlv.EBytes32(w, &v.setID, buf); err != nil {
-			return err
-		}
-
-		return tlv.ETUint32T(w, v.childIndex, buf)
-	}
-
-	return tlv.NewTypeForEncodingErr(val, "AMP")
-}
-
-const (
-	// minAMPLength is the minimum length of a serialized AMP TLV record,
-	// which occurs when the truncated encoding of child_index takes 0
-	// bytes, leaving only the root_share and set_id.
-	minAMPLength = 64
-
-	// maxAMPLength is the maximum length of a serialized AMP TLV record,
-	// which occurs when the truncated encoding of a child_index takes 2
-	// bytes.
-	maxAMPLength = 68
-)
-
-// AMPDecoder reads the AMP record from the provided io.Reader.
-func AMPDecoder(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
-	if v, ok := val.(*AMP); ok && minAMPLength <= l && l <= maxAMPLength {
-		if err := tlv.DBytes32(r, &v.rootShare, buf, 32); err != nil {
-			return err
-		}
-
-		if err := tlv.DBytes32(r, &v.setID, buf, 32); err != nil {
-			return err
-		}
-
-		return tlv.DTUint32(r, &v.childIndex, buf, l-minAMPLength)
-	}
-
-	return tlv.NewTypeForDecodingErr(val, "AMP", l, maxAMPLength)
-}
-
-// Record returns a tlv.Record that can be used to encode or decode this record.
-func (a *AMP) Record() tlv.Record {
-	return tlv.MakeDynamicRecord(
-		AMPOnionType, a, a.PayloadSize, AMPEncoder, AMPDecoder,
-	)
-}
-
-// PayloadSize returns the size this record takes up in encoded form.
-func (a *AMP) PayloadSize() uint64 {
-	return 32 + 32 + tlv.SizeTUint32(a.childIndex)
-}
-
-// SerializeRoute serializes a route.
-func SerializeRoute(w io.Writer, r Route) error {
-	if err := WriteElements(w,
-		r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
-	); err != nil {
-		return err
-	}
-
-	if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
-		return err
-	}
-
-	for _, h := range r.Hops {
-		if err := serializeHop(w, h); err != nil {
-			return err
-		}
-	}
-
-	// Any new/extra TLV data is encoded in serializeHTLCAttemptInfo!
-
-	return nil
-}
-
-func serializeHop(w io.Writer, h *Hop) error {
-	if err := WriteElements(w,
-		h.PubKeyBytes[:],
-		h.ChannelID,
-		h.OutgoingTimeLock,
-		h.AmtToForward,
-	); err != nil {
-		return err
-	}
-
-	if err := binary.Write(w, byteOrder, h.LegacyPayload); err != nil {
-		return err
-	}
-
-	// For legacy payloads, we don't need to write any TLV records, so
-	// we'll write a zero indicating the our serialized TLV map has no
-	// records.
-	if h.LegacyPayload {
-		return WriteElements(w, uint32(0))
-	}
-
-	// Gather all non-primitive TLV records so that they can be serialized
-	// as a single blob.
-	//
-	// TODO(conner): add migration to unify all fields in a single TLV
-	// blobs. The split approach will cause headaches down the road as more
-	// fields are added, which we can avoid by having a single TLV stream
-	// for all payload fields.
-	var records []tlv.Record
-	if h.MPP != nil {
-		records = append(records, h.MPP.Record())
-	}
-
-	// Add blinding point and encrypted data if present.
-	if h.EncryptedData != nil {
-		records = append(records, NewEncryptedDataRecord(
-			&h.EncryptedData,
-		))
-	}
-
-	if h.BlindingPoint != nil {
-		records = append(records, NewBlindingPointRecord(
-			&h.BlindingPoint,
-		))
-	}
-
-	if h.AMP != nil {
-		records = append(records, h.AMP.Record())
-	}
-
-	if h.Metadata != nil {
-		records = append(records, NewMetadataRecord(&h.Metadata))
-	}
-
-	if h.TotalAmtMsat != 0 {
-		totalMsatInt := uint64(h.TotalAmtMsat)
-		records = append(
-			records, NewTotalAmtMsatBlinded(&totalMsatInt),
-		)
-	}
-
-	// Final sanity check to absolutely rule out custom records that are not
-	// custom and write into the standard range.
-	if err := h.CustomRecords.Validate(); err != nil {
-		return err
-	}
-
-	// Convert custom records to tlv and add to the record list.
-	// MapToRecords sorts the list, so adding it here will keep the list
-	// canonical.
-	tlvRecords := tlv.MapToRecords(h.CustomRecords)
-	records = append(records, tlvRecords...)
-
-	// Otherwise, we'll transform our slice of records into a map of the
-	// raw bytes, then serialize them in-line with a length (number of
-	// elements) prefix.
-	mapRecords, err := tlv.RecordsToMap(records)
-	if err != nil {
-		return err
-	}
-
-	numRecords := uint32(len(mapRecords))
-	if err := WriteElements(w, numRecords); err != nil {
-		return err
-	}
-
-	for recordType, rawBytes := range mapRecords {
-		if err := WriteElements(w, recordType); err != nil {
-			return err
-		}
-
-		if err := wire.WriteVarBytes(w, 0, rawBytes); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// DeserializeRoute deserializes a route.
-func DeserializeRoute(r io.Reader) (Route, error) {
-	rt := Route{}
-	if err := ReadElements(r,
-		&rt.TotalTimeLock, &rt.TotalAmount,
-	); err != nil {
-		return rt, err
-	}
-
-	var pub []byte
-	if err := ReadElements(r, &pub); err != nil {
-		return rt, err
-	}
-	copy(rt.SourcePubKey[:], pub)
-
-	var numHops uint32
-	if err := ReadElements(r, &numHops); err != nil {
-		return rt, err
-	}
-
-	var hops []*Hop
-	for i := uint32(0); i < numHops; i++ {
-		hop, err := deserializeHop(r)
-		if err != nil {
-			return rt, err
-		}
-		hops = append(hops, hop)
-	}
-	rt.Hops = hops
-
-	// Any new/extra TLV data is decoded in deserializeHTLCAttemptInfo!
-
-	return rt, nil
-}
-
-// maxOnionPayloadSize is the largest Sphinx payload possible, so we don't need
-// to read/write a TLV stream larger than this.
-const maxOnionPayloadSize = 1300
-
-func deserializeHop(r io.Reader) (*Hop, error) {
-	h := &Hop{}
-
-	var pub []byte
-	if err := ReadElements(r, &pub); err != nil {
-		return nil, err
-	}
-	copy(h.PubKeyBytes[:], pub)
-
-	if err := ReadElements(r,
-		&h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
-	); err != nil {
-		return nil, err
-	}
-
-	// TODO(roasbeef): change field to allow LegacyPayload false to be the
-	// legacy default?
-	err := binary.Read(r, byteOrder, &h.LegacyPayload)
-	if err != nil {
-		return nil, err
-	}
-
-	var numElements uint32
-	if err := ReadElements(r, &numElements); err != nil {
-		return nil, err
-	}
-
-	// If there're no elements, then we can return early.
-	if numElements == 0 {
-		return h, nil
-	}
-
-	tlvMap := make(map[uint64][]byte)
-	for i := uint32(0); i < numElements; i++ {
-		var tlvType uint64
-		if err := ReadElements(r, &tlvType); err != nil {
-			return nil, err
-		}
-
-		rawRecordBytes, err := wire.ReadVarBytes(
-			r, 0, maxOnionPayloadSize, "tlv",
-		)
-		if err != nil {
-			return nil, err
-		}
-
-		tlvMap[tlvType] = rawRecordBytes
-	}
-
-	// If the MPP type is present, remove it from the generic TLV map and
-	// parse it back into a proper MPP struct.
-	//
-	// TODO(conner): add migration to unify all fields in a single TLV
-	// blobs. The split approach will cause headaches down the road as more
-	// fields are added, which we can avoid by having a single TLV stream
-	// for all payload fields.
-	mppType := uint64(MPPOnionType)
-	if mppBytes, ok := tlvMap[mppType]; ok {
-		delete(tlvMap, mppType)
-
-		var (
-			mpp    = &MPP{}
-			mppRec = mpp.Record()
-			r      = bytes.NewReader(mppBytes)
-		)
-		err := mppRec.Decode(r, uint64(len(mppBytes)))
-		if err != nil {
-			return nil, err
-		}
-		h.MPP = mpp
-	}
-
-	// If encrypted data or blinding key are present, remove them from
-	// the TLV map and parse into proper types.
-	encryptedDataType := uint64(EncryptedDataOnionType)
-	if data, ok := tlvMap[encryptedDataType]; ok {
-		delete(tlvMap, encryptedDataType)
-		h.EncryptedData = data
-	}
-
-	blindingType := uint64(BlindingPointOnionType)
-	if blindingPoint, ok := tlvMap[blindingType]; ok {
-		delete(tlvMap, blindingType)
-
-		h.BlindingPoint, err = btcec.ParsePubKey(blindingPoint)
-		if err != nil {
-			return nil, fmt.Errorf("invalid blinding point: %w",
-				err)
-		}
-	}
-
-	ampType := uint64(AMPOnionType)
-	if ampBytes, ok := tlvMap[ampType]; ok {
-		delete(tlvMap, ampType)
-
-		var (
-			amp    = &AMP{}
-			ampRec = amp.Record()
-			r      = bytes.NewReader(ampBytes)
-		)
-		err := ampRec.Decode(r, uint64(len(ampBytes)))
-		if err != nil {
-			return nil, err
-		}
-		h.AMP = amp
-	}
-
-	// If the metadata type is present, remove it from the tlv map and
-	// populate directly on the hop.
-	metadataType := uint64(MetadataOnionType)
-	if metadata, ok := tlvMap[metadataType]; ok {
-		delete(tlvMap, metadataType)
-
-		h.Metadata = metadata
-	}
-
-	totalAmtMsatType := uint64(TotalAmtMsatBlindedType)
-	if totalAmtMsat, ok := tlvMap[totalAmtMsatType]; ok {
-		delete(tlvMap, totalAmtMsatType)
-
-		var (
-			totalAmtMsatInt uint64
-			buf             [8]byte
-		)
-		if err := tlv.DTUint64(
-			bytes.NewReader(totalAmtMsat),
-			&totalAmtMsatInt,
-			&buf,
-			uint64(len(totalAmtMsat)),
-		); err != nil {
-			return nil, err
-		}
-
-		h.TotalAmtMsat = lnwire.MilliSatoshi(totalAmtMsatInt)
-	}
-
-	h.CustomRecords = tlvMap
-
-	return h, nil
-}
diff --git a/channeldb/migration33/log.go b/channeldb/migration33/log.go
deleted file mode 100644
index e9b271f5d..000000000
--- a/channeldb/migration33/log.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package migration33
-
-import (
-	"github.com/btcsuite/btclog"
-)
-
-// log is a logger that is initialized as disabled.  This means the package will
-// not perform any logging by default until a logger is set.
-var log = btclog.Disabled
-
-// UseLogger uses a specified Logger to output package logging info.
-func UseLogger(logger btclog.Logger) {
-	log = logger
-}
diff --git a/channeldb/migration33/migration.go b/channeldb/migration33/migration.go
deleted file mode 100644
index 6e2069a37..000000000
--- a/channeldb/migration33/migration.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package migration33
-
-import (
-	"bytes"
-
-	"github.com/lightningnetwork/lnd/kvdb"
-)
-
-var (
-	// resultsKey is the fixed key under which the attempt results are
-	// stored.
-	resultsKey = []byte("missioncontrol-results")
-
-	// defaultMCNamespaceKey is the key of the default mission control store
-	// namespace.
-	defaultMCNamespaceKey = []byte("default")
-)
-
-// MigrateMCStoreNameSpacedResults reads in all the current mission control
-// entries and re-writes them under a new default namespace.
-func MigrateMCStoreNameSpacedResults(tx kvdb.RwTx) error {
-	log.Infof("Migrating Mission Control store to use namespaced results")
-
-	// Get the top level bucket. All the MC results are currently stored
-	// as KV pairs in this bucket
-	topLevelBucket := tx.ReadWriteBucket(resultsKey)
-
-	// If the results bucket does not exist then there are no entries in
-	// the mission control store yet and so there is nothing to migrate.
-	if topLevelBucket == nil {
-		return nil
-	}
-
-	// Create a new default namespace bucket under the top-level bucket.
-	defaultNSBkt, err := topLevelBucket.CreateBucket(defaultMCNamespaceKey)
-	if err != nil {
-		return err
-	}
-
-	// Iterate through each of the existing result pairs, write them to the
-	// new namespaced bucket. Also collect the set of keys so that we can
-	// later delete them from the top level bucket.
-	var keys [][]byte
-	err = topLevelBucket.ForEach(func(k, v []byte) error {
-		// Skip the new default namespace key.
-		if bytes.Equal(k, defaultMCNamespaceKey) {
-			return nil
-		}
-
-		// Collect the key.
-		keys = append(keys, k)
-
-		// Write the pair under the default namespace.
-		return defaultNSBkt.Put(k, v)
-	})
-	if err != nil {
-		return err
-	}
-
-	// Finally, iterate through the set of keys and delete them from the
-	// top level bucket.
-	for _, k := range keys {
-		if err := topLevelBucket.Delete(k); err != nil {
-			return err
-		}
-	}
-
-	return err
-}
diff --git a/docs/release-notes/release-notes-0.19.0.md b/docs/release-notes/release-notes-0.19.0.md
index a0262b0f5..47562b074 100644
--- a/docs/release-notes/release-notes-0.19.0.md
+++ b/docs/release-notes/release-notes-0.19.0.md
@@ -62,14 +62,6 @@
 ## Testing
 ## Database
 
-* [Migrate the mission control 
-  store](https://github.com/lightningnetwork/lnd/pull/8911) to use a more 
-  minimal encoding for payment attempt routes.
-
-* [Migrate the mission control 
-  store](https://github.com/lightningnetwork/lnd/pull/9001) so that results are 
-  namespaced. All existing results are written to the "default" namespace.
-
 ## Code Health
 
 ## Tooling and Documentation
diff --git a/kvdb/postgres/db.go b/kvdb/postgres/db.go
index 425ba1622..90ca8324a 100644
--- a/kvdb/postgres/db.go
+++ b/kvdb/postgres/db.go
@@ -28,6 +28,7 @@ func newPostgresBackend(ctx context.Context, config *Config, prefix string) (
 		Schema:                "public",
 		TableNamePrefix:       prefix,
 		SQLiteCmdReplacements: sqliteCmdReplacements,
+		WithTxLevelLock:       true,
 	}
 
 	return sqlbase.NewSqlBackend(ctx, cfg)
diff --git a/kvdb/sqlbase/db.go b/kvdb/sqlbase/db.go
index 6ef085712..221a77bfd 100644
--- a/kvdb/sqlbase/db.go
+++ b/kvdb/sqlbase/db.go
@@ -55,6 +55,10 @@ type Config struct {
 	// commands. Note that the sqlite keywords to be replaced are
 	// case-sensitive.
 	SQLiteCmdReplacements SQLiteCmdReplacements
+
+	// WithTxLevelLock when set will ensure that there is a transaction
+	// level lock.
+	WithTxLevelLock bool
 }
 
 // db holds a reference to the sql db connection.
@@ -75,6 +79,10 @@ type db struct {
 	// db is the underlying database connection instance.
 	db *sql.DB
 
+	// lock is the global write lock that ensures single writer. This is
+	// only used if cfg.WithTxLevelLock is set.
+	lock sync.RWMutex
+
 	// table is the name of the table that contains the data for all
 	// top-level buckets that have keys that cannot be mapped to a distinct
 	// sql table.
diff --git a/kvdb/sqlbase/readwrite_tx.go b/kvdb/sqlbase/readwrite_tx.go
index 18a6a682c..ec761931a 100644
--- a/kvdb/sqlbase/readwrite_tx.go
+++ b/kvdb/sqlbase/readwrite_tx.go
@@ -5,6 +5,7 @@ package sqlbase
 import (
 	"context"
 	"database/sql"
+	"sync"
 
 	"github.com/btcsuite/btcwallet/walletdb"
 )
@@ -19,11 +20,28 @@ type readWriteTx struct {
 
 	// active is true if the transaction hasn't been committed yet.
 	active bool
+
+	// locker is a pointer to the global db lock.
+	locker sync.Locker
 }
 
 // newReadWriteTx creates an rw transaction using a connection from the
 // specified pool.
 func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) {
+	locker := newNoopLocker()
+	if db.cfg.WithTxLevelLock {
+		// Obtain the global lock instance. An alternative here is to
+		// obtain a database lock from Postgres. Unfortunately there is
+		// no database-level lock in Postgres, meaning that each table
+		// would need to be locked individually. Perhaps an advisory
+		// lock could perform this function too.
+		locker = &db.lock
+		if readOnly {
+			locker = db.lock.RLocker()
+		}
+	}
+	locker.Lock()
+
 	// Start the transaction. Don't use the timeout context because it would
 	// be applied to the transaction as a whole. If possible, mark the
 	// transaction as read-only to make sure that potential programming
@@ -36,6 +54,7 @@ func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) {
 		},
 	)
 	if err != nil {
+		locker.Unlock()
 		return nil, err
 	}
 
@@ -43,6 +62,7 @@ func newReadWriteTx(db *db, readOnly bool) (*readWriteTx, error) {
 		db:     db,
 		tx:     tx,
 		active: true,
+		locker: locker,
 	}, nil
 }
 
@@ -74,6 +94,7 @@ func (tx *readWriteTx) Rollback() error {
 
 	// Unlock the transaction regardless of the error result.
 	tx.active = false
+	tx.locker.Unlock()
 	return err
 }
 
@@ -141,6 +162,7 @@ func (tx *readWriteTx) Commit() error {
 
 	// Unlock the transaction regardless of the error result.
 	tx.active = false
+	tx.locker.Unlock()
 
 	return err
 }
@@ -182,3 +204,25 @@ func (tx *readWriteTx) Exec(query string, args ...interface{}) (sql.Result,
 
 	return tx.tx.ExecContext(ctx, query, args...)
 }
+
+// noopLocker is an implementation of a no-op sync.Locker.
+type noopLocker struct{}
+
+// newNoopLocker creates a new noopLocker.
+func newNoopLocker() sync.Locker {
+	return &noopLocker{}
+}
+
+// Lock is a noop.
+//
+// NOTE: this is part of the sync.Locker interface.
+func (n *noopLocker) Lock() {
+}
+
+// Unlock is a noop.
+//
+// NOTE: this is part of the sync.Locker interface.
+func (n *noopLocker) Unlock() {
+}
+
+var _ sync.Locker = (*noopLocker)(nil)
diff --git a/log.go b/log.go
index 0601ff9f8..a24c39fd4 100644
--- a/log.go
+++ b/log.go
@@ -2,6 +2,7 @@ package lnd
 
 import (
 	"github.com/btcsuite/btcd/connmgr"
+	"github.com/btcsuite/btcd/rpcclient"
 	"github.com/btcsuite/btclog"
 	"github.com/lightninglabs/neutrino"
 	sphinx "github.com/lightningnetwork/lightning-onion"
@@ -133,6 +134,7 @@ func SetupLoggers(root *build.RotatingLogWriter, interceptor signal.Interceptor)
 	// can be overwritten later.
 	AddSubLogger(root, "BTCN", interceptor, neutrino.UseLogger)
 	AddSubLogger(root, "CMGR", interceptor, connmgr.UseLogger)
+	AddSubLogger(root, "RPCC", interceptor, rpcclient.UseLogger)
 
 	// Some of the loggers declared in the main lnd package are also used
 	// in sub packages.
diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go
index d848235d8..100d900a7 100644
--- a/routing/missioncontrol.go
+++ b/routing/missioncontrol.go
@@ -7,11 +7,7 @@ import (
 	"time"
 
 	"github.com/btcsuite/btcd/btcutil"
-	"github.com/btcsuite/btclog"
-	"github.com/btcsuite/btcwallet/walletdb"
-	"github.com/lightningnetwork/lnd/build"
 	"github.com/lightningnetwork/lnd/channeldb"
-	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/fn"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
@@ -72,11 +68,6 @@ const (
 	// FeeEstimationTimeout. It defines the maximum duration that the
 	// probing fee estimation is allowed to take.
 	DefaultFeeEstimationTimeout = time.Minute
-
-	// DefaultMissionControlNamespace is the name of the default mission
-	// control name space. This is used as the sub-bucket key within the
-	// top level DB bucket to store mission control results.
-	DefaultMissionControlNamespace = "default"
 )
 
 var (
@@ -93,16 +84,6 @@ var (
 // NodeResults contains previous results from a node to its peers.
 type NodeResults map[route.Vertex]TimedPairResult
 
-// mcConfig holds various config members that will be required by all
-// MissionControl instances and will be the same regardless of namespace.
-type mcConfig struct {
-	// clock is a time source used by mission control.
-	clock clock.Clock
-
-	// selfNode is our pubkey.
-	selfNode route.Vertex
-}
-
 // MissionControl contains state which summarizes the past attempts of HTLC
 // routing by external callers when sending payments throughout the network. It
 // acts as a shared memory during routing attempts with the goal to optimize the
@@ -113,12 +94,17 @@ type mcConfig struct {
 // since the last failure is used to estimate a success probability that is fed
 // into the path finding process for subsequent payment attempts.
 type MissionControl struct {
-	cfg *mcConfig
-
 	// state is the internal mission control state that is input for
 	// probability estimation.
 	state *missionControlState
 
+	// now is expected to return the current time. It is supplied as an
+	// external function to enable deterministic unit tests.
+	now func() time.Time
+
+	// selfNode is our pubkey.
+	selfNode route.Vertex
+
 	store *missionControlStore
 
 	// estimator is the probability estimator that is used with the payment
@@ -129,19 +115,7 @@ type MissionControl struct {
 	// mission control state is updated.
 	onConfigUpdate fn.Option[func(cfg *MissionControlConfig)]
 
-	log btclog.Logger
-
-	mu sync.Mutex
-}
-
-// MissionController manages MissionControl instances in various namespaces.
-type MissionController struct {
-	db           kvdb.Backend
-	cfg          *mcConfig
-	defaultMCCfg *MissionControlConfig
-
-	mc map[string]*MissionControl
-	mu sync.Mutex
+	sync.Mutex
 
 	// TODO(roasbeef): further counters, if vertex continually unavailable,
 	// add to another generation
@@ -149,35 +123,6 @@ type MissionController struct {
 	// TODO(roasbeef): also add favorable metrics for nodes
 }
 
-// GetNamespacedStore returns the MissionControl in the given namespace. If one
-// does not yet exist, then it is initialised.
-func (m *MissionController) GetNamespacedStore(ns string) (*MissionControl,
-	error) {
-
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	if mc, ok := m.mc[ns]; ok {
-		return mc, nil
-	}
-
-	return m.initMissionControl(ns)
-}
-
-// ListNamespaces returns a list of the namespaces that the MissionController
-// is aware of.
-func (m *MissionController) ListNamespaces() []string {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	namespaces := make([]string, 0, len(m.mc))
-	for ns := range m.mc {
-		namespaces = append(namespaces, ns)
-	}
-
-	return namespaces
-}
-
 // MissionControlConfig defines parameters that control mission control
 // behaviour.
 type MissionControlConfig struct {
@@ -263,15 +208,15 @@ type MissionControlPairSnapshot struct {
 type paymentResult struct {
 	id                 uint64
 	timeFwd, timeReply time.Time
-	route              *mcRoute
+	route              *route.Route
 	success            bool
 	failureSourceIdx   *int
 	failure            lnwire.FailureMessage
 }
 
-// NewMissionController returns a new instance of MissionController.
-func NewMissionController(db kvdb.Backend, self route.Vertex,
-	cfg *MissionControlConfig) (*MissionController, error) {
+// NewMissionControl returns a new instance of missionControl.
+func NewMissionControl(db kvdb.Backend, self route.Vertex,
+	cfg *MissionControlConfig) (*MissionControl, error) {
 
 	log.Debugf("Instantiating mission control with config: %v, %v", cfg,
 		cfg.Estimator)
@@ -280,151 +225,47 @@ func NewMissionController(db kvdb.Backend, self route.Vertex,
 		return nil, err
 	}
 
-	mcCfg := &mcConfig{
-		clock:    clock.NewDefaultClock(),
-		selfNode: self,
-	}
-
-	mgr := &MissionController{
-		db:           db,
-		defaultMCCfg: cfg,
-		cfg:          mcCfg,
-		mc:           make(map[string]*MissionControl),
-	}
-
-	if err := mgr.loadMissionControls(); err != nil {
-		return nil, err
-	}
-
-	for _, mc := range mgr.mc {
-		if err := mc.init(); err != nil {
-			return nil, err
-		}
-	}
-
-	return mgr, nil
-}
-
-// loadMissionControls initialises a MissionControl in the default namespace if
-// one does not yet exist. It then initialises a MissionControl for all other
-// namespaces found in the DB.
-//
-// NOTE: this should only be called once during MissionController construction.
-func (m *MissionController) loadMissionControls() error {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	// Always initialise the default namespace.
-	_, err := m.initMissionControl(DefaultMissionControlNamespace)
-	if err != nil {
-		return err
-	}
-
-	namespaces := make(map[string]struct{})
-	err = m.db.View(func(tx walletdb.ReadTx) error {
-		mcStoreBkt := tx.ReadBucket(resultsKey)
-		if mcStoreBkt == nil {
-			return fmt.Errorf("top level mission control bucket " +
-				"not found")
-		}
-
-		// Iterate through all the keys in the bucket and collect the
-		// namespaces.
-		return mcStoreBkt.ForEach(func(k, _ []byte) error {
-			// We've already initialised the default namespace so
-			// we can skip it.
-			if string(k) == DefaultMissionControlNamespace {
-				return nil
-			}
-
-			namespaces[string(k)] = struct{}{}
-
-			return nil
-		})
-	}, func() {})
-	if err != nil {
-		return err
-	}
-
-	// Now, iterate through all the namespaces and initialise them.
-	for ns := range namespaces {
-		_, err = m.initMissionControl(ns)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// initMissionControl creates a new MissionControl instance with the given
-// namespace if one does not yet exist.
-//
-// NOTE: the MissionController's mutex must be held before calling this method.
-func (m *MissionController) initMissionControl(namespace string) (
-	*MissionControl, error) {
-
-	// If a mission control with this namespace has already been initialised
-	// then there is nothing left to do.
-	if mc, ok := m.mc[namespace]; ok {
-		return mc, nil
-	}
-
-	cfg := m.defaultMCCfg
-
 	store, err := newMissionControlStore(
-		newNamespacedDB(m.db, namespace), cfg.MaxMcHistory,
-		cfg.McFlushInterval,
+		db, cfg.MaxMcHistory, cfg.McFlushInterval,
 	)
 	if err != nil {
 		return nil, err
 	}
 
 	mc := &MissionControl{
-		cfg:       m.cfg,
-		state:     newMissionControlState(cfg.MinFailureRelaxInterval),
-		store:     store,
-		estimator: cfg.Estimator,
-		log: build.NewPrefixLog(
-			fmt.Sprintf("[%s]:", namespace), log,
+		state: newMissionControlState(
+			cfg.MinFailureRelaxInterval,
 		),
+		now:            time.Now,
+		selfNode:       self,
+		store:          store,
+		estimator:      cfg.Estimator,
 		onConfigUpdate: cfg.OnConfigUpdate,
 	}
 
-	m.mc[namespace] = mc
+	if err := mc.init(); err != nil {
+		return nil, err
+	}
 
 	return mc, nil
 }
 
-// RunStoreTickers runs the mission controller store's tickers.
-func (m *MissionController) RunStoreTickers() {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	for _, mc := range m.mc {
-		mc.store.run()
-	}
+// RunStoreTicker runs the mission control store's ticker.
+func (m *MissionControl) RunStoreTicker() {
+	m.store.run()
 }
 
-// StopStoreTickers stops the mission control store's tickers.
-func (m *MissionController) StopStoreTickers() {
+// StopStoreTicker stops the mission control store's ticker.
+func (m *MissionControl) StopStoreTicker() {
 	log.Debug("Stopping mission control store ticker")
 	defer log.Debug("Mission control store ticker stopped")
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
-
-	for _, mc := range m.mc {
-		mc.store.stop()
-	}
+	m.store.stop()
 }
 
 // init initializes mission control with historical data.
 func (m *MissionControl) init() error {
-	m.log.Debugf("Mission control state reconstruction started")
-
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	log.Debugf("Mission control state reconstruction started")
 
 	start := time.Now()
 
@@ -434,10 +275,10 @@ func (m *MissionControl) init() error {
 	}
 
 	for _, result := range results {
-		_ = m.applyPaymentResult(result)
+		m.applyPaymentResult(result)
 	}
 
-	m.log.Debugf("Mission control state reconstruction finished: "+
+	log.Debugf("Mission control state reconstruction finished: "+
 		"n=%v, time=%v", len(results), time.Since(start))
 
 	return nil
@@ -447,8 +288,8 @@ func (m *MissionControl) init() error {
 // with. All fields are copied by value, so we do not need to worry about
 // mutation.
 func (m *MissionControl) GetConfig() *MissionControlConfig {
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
 	return &MissionControlConfig{
 		Estimator:               m.estimator,
@@ -469,10 +310,10 @@ func (m *MissionControl) SetConfig(cfg *MissionControlConfig) error {
 		return err
 	}
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
-	m.log.Infof("Active mission control cfg: %v, estimator: %v", cfg,
+	log.Infof("Active mission control cfg: %v, estimator: %v", cfg,
 		cfg.Estimator)
 
 	m.store.maxRecords = cfg.MaxMcHistory
@@ -490,8 +331,8 @@ func (m *MissionControl) SetConfig(cfg *MissionControlConfig) error {
 // ResetHistory resets the history of MissionControl returning it to a state as
 // if no payment attempts have been made.
 func (m *MissionControl) ResetHistory() error {
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
 	if err := m.store.clear(); err != nil {
 		return err
@@ -499,7 +340,7 @@ func (m *MissionControl) ResetHistory() error {
 
 	m.state.resetHistory()
 
-	m.log.Debugf("Mission control history cleared")
+	log.Debugf("Mission control history cleared")
 
 	return nil
 }
@@ -509,14 +350,14 @@ func (m *MissionControl) ResetHistory() error {
 func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
 	amt lnwire.MilliSatoshi, capacity btcutil.Amount) float64 {
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
-	now := m.cfg.clock.Now()
+	now := m.now()
 	results, _ := m.state.getLastPairResult(fromNode)
 
 	// Use a distinct probability estimation function for local channels.
-	if fromNode == m.cfg.selfNode {
+	if fromNode == m.selfNode {
 		return m.estimator.LocalPairProbability(now, results, toNode)
 	}
 
@@ -528,10 +369,10 @@ func (m *MissionControl) GetProbability(fromNode, toNode route.Vertex,
 // GetHistorySnapshot takes a snapshot from the current mission control state
 // and actual probability estimates.
 func (m *MissionControl) GetHistorySnapshot() *MissionControlSnapshot {
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
-	m.log.Debugf("Requesting history snapshot from mission control")
+	log.Debugf("Requesting history snapshot from mission control")
 
 	return m.state.getSnapshot()
 }
@@ -546,15 +387,15 @@ func (m *MissionControl) ImportHistory(history *MissionControlSnapshot,
 		return errors.New("cannot import nil history")
 	}
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
-	m.log.Infof("Importing history snapshot with %v pairs to mission "+
-		"control", len(history.Pairs))
+	log.Infof("Importing history snapshot with %v pairs to mission control",
+		len(history.Pairs))
 
 	imported := m.state.importSnapshot(history, force)
 
-	m.log.Infof("Imported %v results to mission control", imported)
+	log.Infof("Imported %v results to mission control", imported)
 
 	return nil
 }
@@ -563,8 +404,8 @@ func (m *MissionControl) ImportHistory(history *MissionControlSnapshot,
 func (m *MissionControl) GetPairHistorySnapshot(
 	fromNode, toNode route.Vertex) TimedPairResult {
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
 	results, ok := m.state.getLastPairResult(fromNode)
 	if !ok {
@@ -588,7 +429,7 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
 	failureSourceIdx *int, failure lnwire.FailureMessage) (
 	*channeldb.FailureReason, error) {
 
-	timestamp := m.cfg.clock.Now()
+	timestamp := m.now()
 
 	result := &paymentResult{
 		success:          false,
@@ -597,7 +438,7 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
 		id:               paymentID,
 		failureSourceIdx: failureSourceIdx,
 		failure:          failure,
-		route:            extractMCRoute(rt),
+		route:            rt,
 	}
 
 	return m.processPaymentResult(result)
@@ -608,14 +449,14 @@ func (m *MissionControl) ReportPaymentFail(paymentID uint64, rt *route.Route,
 func (m *MissionControl) ReportPaymentSuccess(paymentID uint64,
 	rt *route.Route) error {
 
-	timestamp := m.cfg.clock.Now()
+	timestamp := m.now()
 
 	result := &paymentResult{
 		timeFwd:   timestamp,
 		timeReply: timestamp,
 		id:        paymentID,
 		success:   true,
-		route:     extractMCRoute(rt),
+		route:     rt,
 	}
 
 	_, err := m.processPaymentResult(result)
@@ -630,8 +471,8 @@ func (m *MissionControl) processPaymentResult(result *paymentResult) (
 	// Store complete result in database.
 	m.store.AddResult(result)
 
-	m.mu.Lock()
-	defer m.mu.Unlock()
+	m.Lock()
+	defer m.Unlock()
 
 	// Apply result to update mission control state.
 	reason := m.applyPaymentResult(result)
@@ -678,7 +519,7 @@ func (m *MissionControl) applyPaymentResult(
 	// that case, a node-level failure would not be applied to untried
 	// channels.
 	if i.nodeFailure != nil {
-		m.log.Debugf("Reporting node failure to Mission Control: "+
+		log.Debugf("Reporting node failure to Mission Control: "+
 			"node=%v", *i.nodeFailure)
 
 		m.state.setAllFail(*i.nodeFailure, result.timeReply)
@@ -688,11 +529,11 @@ func (m *MissionControl) applyPaymentResult(
 		pairResult := pairResult
 
 		if pairResult.success {
-			m.log.Debugf("Reporting pair success to Mission "+
+			log.Debugf("Reporting pair success to Mission "+
 				"Control: pair=%v, amt=%v",
 				pair, pairResult.amt)
 		} else {
-			m.log.Debugf("Reporting pair failure to Mission "+
+			log.Debugf("Reporting pair failure to Mission "+
 				"Control: pair=%v, amt=%v",
 				pair, pairResult.amt)
 		}
@@ -704,102 +545,3 @@ func (m *MissionControl) applyPaymentResult(
 
 	return i.finalFailureReason
 }
-
-// namespacedDB is an implementation of the missionControlDB that gives a user
-// of the interface access to a namespaced bucket within the top level mission
-// control bucket.
-type namespacedDB struct {
-	topLevelBucketKey []byte
-	namespace         []byte
-	db                kvdb.Backend
-}
-
-// A compile-time check to ensure that namespacedDB implements missionControlDB.
-var _ missionControlDB = (*namespacedDB)(nil)
-
-// newDefaultNamespacedStore creates an instance of namespaceDB that uses the
-// default namespace.
-func newDefaultNamespacedStore(db kvdb.Backend) missionControlDB {
-	return newNamespacedDB(db, DefaultMissionControlNamespace)
-}
-
-// newNamespacedDB creates a new instance of missionControlDB where the DB will
-// have access to a namespaced bucket within the top level mission control
-// bucket.
-func newNamespacedDB(db kvdb.Backend, namespace string) missionControlDB {
-	return &namespacedDB{
-		db:                db,
-		namespace:         []byte(namespace),
-		topLevelBucketKey: resultsKey,
-	}
-}
-
-// update can be used to perform reads and writes on the given bucket.
-//
-// NOTE: this is part of the missionControlDB interface.
-func (n *namespacedDB) update(f func(bkt walletdb.ReadWriteBucket) error,
-	reset func()) error {
-
-	return n.db.Update(func(tx kvdb.RwTx) error {
-		mcStoreBkt, err := tx.CreateTopLevelBucket(n.topLevelBucketKey)
-		if err != nil {
-			return fmt.Errorf("cannot create top level mission "+
-				"control bucket: %w", err)
-		}
-
-		namespacedBkt, err := mcStoreBkt.CreateBucketIfNotExists(
-			n.namespace,
-		)
-		if err != nil {
-			return fmt.Errorf("cannot create namespaced bucket "+
-				"(%s) in mission control store: %w",
-				n.namespace, err)
-		}
-
-		return f(namespacedBkt)
-	}, reset)
-}
-
-// view can be used to perform reads on the given bucket.
-//
-// NOTE: this is part of the missionControlDB interface.
-func (n *namespacedDB) view(f func(bkt walletdb.ReadBucket) error,
-	reset func()) error {
-
-	return n.db.View(func(tx kvdb.RTx) error {
-		mcStoreBkt := tx.ReadBucket(n.topLevelBucketKey)
-		if mcStoreBkt == nil {
-			return fmt.Errorf("top level mission control bucket " +
-				"not found")
-		}
-
-		namespacedBkt := mcStoreBkt.NestedReadBucket(n.namespace)
-		if namespacedBkt == nil {
-			return fmt.Errorf("namespaced bucket (%s) not found "+
-				"in mission control store", n.namespace)
-		}
-
-		return f(namespacedBkt)
-	}, reset)
-}
-
-// purge will delete all the contents in the namespace.
-//
-// NOTE: this is part of the missionControlDB interface.
-func (n *namespacedDB) purge() error {
-	return n.db.Update(func(tx kvdb.RwTx) error {
-		mcStoreBkt := tx.ReadWriteBucket(n.topLevelBucketKey)
-		if mcStoreBkt == nil {
-			return nil
-		}
-
-		err := mcStoreBkt.DeleteNestedBucket(n.namespace)
-		if err != nil {
-			return err
-		}
-
-		_, err = mcStoreBkt.CreateBucket(n.namespace)
-
-		return err
-	}, func() {})
-}
diff --git a/routing/missioncontrol_state.go b/routing/missioncontrol_state.go
index c61ec8335..7d6633f2b 100644
--- a/routing/missioncontrol_state.go
+++ b/routing/missioncontrol_state.go
@@ -37,8 +37,7 @@ func newMissionControlState(
 	}
 }
 
-// getLastPairResult returns the current state for connections to the given
-// node.
+// getLastPairResult returns the current state for connections to the given node.
 func (m *missionControlState) getLastPairResult(node route.Vertex) (NodeResults,
 	bool) {
 
@@ -46,8 +45,8 @@ func (m *missionControlState) getLastPairResult(node route.Vertex) (NodeResults,
 	return result, ok
 }
 
-// ResetHistory resets the history of missionControlState returning it to a
-// state as if no payment attempts have been made.
+// ResetHistory resets the history of MissionControl returning it to a state as
+// if no payment attempts have been made.
 func (m *missionControlState) resetHistory() {
 	m.lastPairResult = make(map[route.Vertex]NodeResults)
 	m.lastSecondChance = make(map[DirectedNodePair]time.Time)
diff --git a/routing/missioncontrol_store.go b/routing/missioncontrol_store.go
index c40f24697..e07a46136 100644
--- a/routing/missioncontrol_store.go
+++ b/routing/missioncontrol_store.go
@@ -5,7 +5,6 @@ import (
 	"container/list"
 	"encoding/binary"
 	"fmt"
-	"io"
 	"math"
 	"sync"
 	"time"
@@ -32,21 +31,6 @@ const (
 	unknownFailureSourceIdx = -1
 )
 
-// missionControlDB is an interface that defines the database methods that a
-// single missionControlStore has access to. It allows the missionControlStore
-// to be unaware of the overall DB structure and restricts its access to the DB
-// by only providing it the bucket that it needs to care about.
-type missionControlDB interface {
-	// update can be used to perform reads and writes on the given bucket.
-	update(f func(bkt kvdb.RwBucket) error, reset func()) error
-
-	// view can be used to perform reads on the given bucket.
-	view(f func(bkt kvdb.RBucket) error, reset func()) error
-
-	// purge will delete all the contents in this store.
-	purge() error
-}
-
 // missionControlStore is a bolt db based implementation of a mission control
 // store. It stores the raw payment attempt data from which the internal mission
 // controls state can be rederived on startup. This allows the mission control
@@ -56,7 +40,7 @@ type missionControlDB interface {
 type missionControlStore struct {
 	done chan struct{}
 	wg   sync.WaitGroup
-	db   missionControlDB
+	db   kvdb.Backend
 
 	// queueCond is signalled when items are put into the queue.
 	queueCond *sync.Cond
@@ -82,7 +66,7 @@ type missionControlStore struct {
 	flushInterval time.Duration
 }
 
-func newMissionControlStore(db missionControlDB, maxRecords int,
+func newMissionControlStore(db kvdb.Backend, maxRecords int,
 	flushInterval time.Duration) (*missionControlStore, error) {
 
 	var (
@@ -91,7 +75,13 @@ func newMissionControlStore(db missionControlDB, maxRecords int,
 	)
 
 	// Create buckets if not yet existing.
-	err := db.update(func(resultsBucket kvdb.RwBucket) error {
+	err := kvdb.Update(db, func(tx kvdb.RwTx) error {
+		resultsBucket, err := tx.CreateTopLevelBucket(resultsKey)
+		if err != nil {
+			return fmt.Errorf("cannot create results bucket: %w",
+				err)
+		}
+
 		// Collect all keys to be able to quickly calculate the
 		// difference when updating the DB state.
 		c := resultsBucket.ReadCursor()
@@ -128,12 +118,20 @@ func (b *missionControlStore) clear() error {
 	b.queueCond.L.Lock()
 	defer b.queueCond.L.Unlock()
 
-	if err := b.db.purge(); err != nil {
+	err := kvdb.Update(b.db, func(tx kvdb.RwTx) error {
+		if err := tx.DeleteTopLevelBucket(resultsKey); err != nil {
+			return err
+		}
+
+		_, err := tx.CreateTopLevelBucket(resultsKey)
+		return err
+	}, func() {})
+
+	if err != nil {
 		return err
 	}
 
 	b.queue = list.New()
-
 	return nil
 }
 
@@ -141,7 +139,8 @@ func (b *missionControlStore) clear() error {
 func (b *missionControlStore) fetchAll() ([]*paymentResult, error) {
 	var results []*paymentResult
 
-	err := b.db.view(func(resultBucket kvdb.RBucket) error {
+	err := kvdb.View(b.db, func(tx kvdb.RTx) error {
+		resultBucket := tx.ReadBucket(resultsKey)
 		results = make([]*paymentResult, 0)
 
 		return resultBucket.ForEach(func(k, v []byte) error {
@@ -188,7 +187,7 @@ func serializeResult(rp *paymentResult) ([]byte, []byte, error) {
 		return nil, nil, err
 	}
 
-	if err := serializeRoute(&b, rp.route); err != nil {
+	if err := channeldb.SerializeRoute(&b, *rp.route); err != nil {
 		return nil, nil, err
 	}
 
@@ -212,90 +211,6 @@ func serializeResult(rp *paymentResult) ([]byte, []byte, error) {
 	return key, b.Bytes(), nil
 }
 
-// deserializeRoute deserializes the mcRoute from the given io.Reader.
-func deserializeRoute(r io.Reader) (*mcRoute, error) {
-	var rt mcRoute
-	if err := channeldb.ReadElements(r, &rt.totalAmount); err != nil {
-		return nil, err
-	}
-
-	var pub []byte
-	if err := channeldb.ReadElements(r, &pub); err != nil {
-		return nil, err
-	}
-	copy(rt.sourcePubKey[:], pub)
-
-	var numHops uint32
-	if err := channeldb.ReadElements(r, &numHops); err != nil {
-		return nil, err
-	}
-
-	var hops []*mcHop
-	for i := uint32(0); i < numHops; i++ {
-		hop, err := deserializeHop(r)
-		if err != nil {
-			return nil, err
-		}
-		hops = append(hops, hop)
-	}
-	rt.hops = hops
-
-	return &rt, nil
-}
-
-// deserializeHop deserializes the mcHop from the given io.Reader.
-func deserializeHop(r io.Reader) (*mcHop, error) {
-	var h mcHop
-
-	var pub []byte
-	if err := channeldb.ReadElements(r, &pub); err != nil {
-		return nil, err
-	}
-	copy(h.pubKeyBytes[:], pub)
-
-	if err := channeldb.ReadElements(r,
-		&h.channelID, &h.amtToFwd, &h.hasBlindingPoint,
-		&h.hasCustomRecords,
-	); err != nil {
-		return nil, err
-	}
-
-	return &h, nil
-}
-
-// serializeRoute serializes a mcRoute and writes the resulting bytes to the
-// given io.Writer.
-func serializeRoute(w io.Writer, r *mcRoute) error {
-	err := channeldb.WriteElements(w, r.totalAmount, r.sourcePubKey[:])
-	if err != nil {
-		return err
-	}
-
-	if err := channeldb.WriteElements(w, uint32(len(r.hops))); err != nil {
-		return err
-	}
-
-	for _, h := range r.hops {
-		if err := serializeHop(w, h); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// serializeHop serializes a mcHop and writes the resulting bytes to the given
-// io.Writer.
-func serializeHop(w io.Writer, h *mcHop) error {
-	return channeldb.WriteElements(w,
-		h.pubKeyBytes[:],
-		h.channelID,
-		h.amtToFwd,
-		h.hasBlindingPoint,
-		h.hasCustomRecords,
-	)
-}
-
 // deserializeResult deserializes a payment result.
 func deserializeResult(k, v []byte) (*paymentResult, error) {
 	// Parse payment id.
@@ -329,11 +244,11 @@ func deserializeResult(k, v []byte) (*paymentResult, error) {
 	}
 
 	// Read route.
-	route, err := deserializeRoute(r)
+	route, err := channeldb.DeserializeRoute(r)
 	if err != nil {
 		return nil, err
 	}
-	result.route = route
+	result.route = &route
 
 	// Read failure.
 	failureBytes, err := wire.ReadVarBytes(
@@ -511,7 +426,9 @@ func (b *missionControlStore) storeResults() error {
 		}
 	}
 
-	err := b.db.update(func(bucket kvdb.RwBucket) error {
+	err := kvdb.Update(b.db, func(tx kvdb.RwTx) error {
+		bucket := tx.ReadWriteBucket(resultsKey)
+
 		for e := l.Front(); e != nil; e = e.Next() {
 			pr, ok := e.Value.(*paymentResult)
 			if !ok {
@@ -582,7 +499,7 @@ func getResultKey(rp *paymentResult) []byte {
 	// chronologically.
 	byteOrder.PutUint64(keyBytes[:], uint64(rp.timeReply.UnixNano()))
 	byteOrder.PutUint64(keyBytes[8:], rp.id)
-	copy(keyBytes[16:], rp.route.sourcePubKey[:])
+	copy(keyBytes[16:], rp.route.SourcePubKey[:])
 
 	return keyBytes[:]
 }
diff --git a/routing/payment_session.go b/routing/payment_session.go
index 89fb2fd13..9ddb62600 100644
--- a/routing/payment_session.go
+++ b/routing/payment_session.go
@@ -159,7 +159,7 @@ type PaymentSession interface {
 
 // paymentSession is used during an HTLC routings session to prune the local
 // chain view in response to failures, and also report those failures back to
-// MissionController. The snapshot copied for this session will only ever grow,
+// MissionControl. The snapshot copied for this session will only ever grow,
 // and will now be pruned after a decay like the main view within mission
 // control. We do this as we want to avoid the case where we continually try a
 // bad edge or route multiple times in a session. This can lead to an infinite
@@ -184,7 +184,7 @@ type paymentSession struct {
 	// trade-off in path finding between fees and probability.
 	pathFindingConfig PathFindingConfig
 
-	missionControl MissionControlQuerier
+	missionControl MissionController
 
 	// minShardAmt is the amount beyond which we won't try to further split
 	// the payment if no route is found. If the maximum number of htlcs
@@ -199,8 +199,7 @@ type paymentSession struct {
 // newPaymentSession instantiates a new payment session.
 func newPaymentSession(p *LightningPayment, selfNode route.Vertex,
 	getBandwidthHints func(Graph) (bandwidthHints, error),
-	graphSessFactory GraphSessionFactory,
-	missionControl MissionControlQuerier,
+	graphSessFactory GraphSessionFactory, missionControl MissionController,
 	pathFindingConfig PathFindingConfig) (*paymentSession, error) {
 
 	edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
@@ -267,7 +266,7 @@ func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
 
 	// Taking into account this prune view, we'll attempt to locate a path
 	// to our destination, respecting the recommendations from
-	// MissionController.
+	// MissionControl.
 	restrictions := &RestrictParams{
 		ProbabilitySource:     p.missionControl.GetProbability,
 		FeeLimit:              feeLimit,
diff --git a/routing/payment_session_source.go b/routing/payment_session_source.go
index ccee9bc44..c89d6a8e5 100644
--- a/routing/payment_session_source.go
+++ b/routing/payment_session_source.go
@@ -11,7 +11,7 @@ import (
 	"github.com/lightningnetwork/lnd/zpay32"
 )
 
-// A compile time assertion to ensure SessionSource meets the
+// A compile time assertion to ensure MissionControl meets the
 // PaymentSessionSource interface.
 var _ PaymentSessionSource = (*SessionSource)(nil)
 
@@ -40,7 +40,7 @@ type SessionSource struct {
 	// then take into account this set of pruned vertexes/edges to reduce
 	// route failure and pass on graph information gained to the next
 	// execution.
-	MissionControl MissionControlQuerier
+	MissionControl MissionController
 
 	// PathFindingConfig defines global parameters that control the
 	// trade-off in path finding between fees and probability.
diff --git a/routing/result_interpretation.go b/routing/result_interpretation.go
index 4da413421..4118286e6 100644
--- a/routing/result_interpretation.go
+++ b/routing/result_interpretation.go
@@ -76,7 +76,7 @@ type interpretedResult struct {
 
 // interpretResult interprets a payment outcome and returns an object that
 // contains information required to update mission control.
-func interpretResult(rt *mcRoute, success bool, failureSrcIdx *int,
+func interpretResult(rt *route.Route, success bool, failureSrcIdx *int,
 	failure lnwire.FailureMessage) *interpretedResult {
 
 	i := &interpretedResult{
@@ -92,14 +92,15 @@ func interpretResult(rt *mcRoute, success bool, failureSrcIdx *int,
 }
 
 // processSuccess processes a successful payment attempt.
-func (i *interpretedResult) processSuccess(route *mcRoute) {
+func (i *interpretedResult) processSuccess(route *route.Route) {
 	// For successes, all nodes must have acted in the right way. Therefore
 	// we mark all of them with a success result.
-	i.successPairRange(route, 0, len(route.hops)-1)
+	i.successPairRange(route, 0, len(route.Hops)-1)
 }
 
 // processFail processes a failed payment attempt.
-func (i *interpretedResult) processFail(rt *mcRoute, errSourceIdx *int,
+func (i *interpretedResult) processFail(
+	rt *route.Route, errSourceIdx *int,
 	failure lnwire.FailureMessage) {
 
 	if errSourceIdx == nil {
@@ -124,8 +125,10 @@ func (i *interpretedResult) processFail(rt *mcRoute, errSourceIdx *int,
 		i.processPaymentOutcomeSelf(rt, failure)
 
 	// A failure from the final hop was received.
-	case len(rt.hops):
-		i.processPaymentOutcomeFinal(rt, failure)
+	case len(rt.Hops):
+		i.processPaymentOutcomeFinal(
+			rt, failure,
+		)
 
 	// An intermediate hop failed. Interpret the outcome, update reputation
 	// and try again.
@@ -141,7 +144,7 @@ func (i *interpretedResult) processFail(rt *mcRoute, errSourceIdx *int,
 // node. This indicates that the introduction node is not obeying the route
 // blinding specification, as we expect all errors from the introduction node
 // to be source from it.
-func (i *interpretedResult) processPaymentOutcomeBadIntro(route *mcRoute,
+func (i *interpretedResult) processPaymentOutcomeBadIntro(route *route.Route,
 	introIdx, errSourceIdx int) {
 
 	// We fail the introduction node for not obeying the specification.
@@ -158,14 +161,14 @@ func (i *interpretedResult) processPaymentOutcomeBadIntro(route *mcRoute,
 	// a final failure reason because the recipient can't process the
 	// payment (independent of the introduction failing to convert the
 	// error, we can't complete the payment if the last hop fails).
-	if errSourceIdx == len(route.hops) {
+	if errSourceIdx == len(route.Hops) {
 		i.finalFailureReason = &reasonError
 	}
 }
 
 // processPaymentOutcomeSelf handles failures sent by ourselves.
-func (i *interpretedResult) processPaymentOutcomeSelf(rt *mcRoute,
-	failure lnwire.FailureMessage) {
+func (i *interpretedResult) processPaymentOutcomeSelf(
+	rt *route.Route, failure lnwire.FailureMessage) {
 
 	switch failure.(type) {
 
@@ -178,7 +181,7 @@ func (i *interpretedResult) processPaymentOutcomeSelf(rt *mcRoute,
 		i.failNode(rt, 1)
 
 		// If this was a payment to a direct peer, we can stop trying.
-		if len(rt.hops) == 1 {
+		if len(rt.Hops) == 1 {
 			i.finalFailureReason = &reasonError
 		}
 
@@ -188,15 +191,15 @@ func (i *interpretedResult) processPaymentOutcomeSelf(rt *mcRoute,
 	// available in the link has been updated.
 	default:
 		log.Warnf("Routing failure for local channel %v occurred",
-			rt.hops[0].channelID)
+			rt.Hops[0].ChannelID)
 	}
 }
 
 // processPaymentOutcomeFinal handles failures sent by the final hop.
-func (i *interpretedResult) processPaymentOutcomeFinal(route *mcRoute,
-	failure lnwire.FailureMessage) {
+func (i *interpretedResult) processPaymentOutcomeFinal(
+	route *route.Route, failure lnwire.FailureMessage) {
 
-	n := len(route.hops)
+	n := len(route.Hops)
 
 	failNode := func() {
 		i.failNode(route, n)
@@ -289,10 +292,9 @@ func (i *interpretedResult) processPaymentOutcomeFinal(route *mcRoute,
 
 // processPaymentOutcomeIntermediate handles failures sent by an intermediate
 // hop.
-//
-//nolint:funlen
-func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
-	errorSourceIdx int, failure lnwire.FailureMessage) {
+func (i *interpretedResult) processPaymentOutcomeIntermediate(
+	route *route.Route, errorSourceIdx int,
+	failure lnwire.FailureMessage) {
 
 	reportOutgoing := func() {
 		i.failPair(
@@ -396,8 +398,8 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
 		// Set the node pair for which a channel update may be out of
 		// date. The second chance logic uses the policyFailure field.
 		i.policyFailure = &DirectedNodePair{
-			From: route.hops[errorSourceIdx-1].pubKeyBytes,
-			To:   route.hops[errorSourceIdx].pubKeyBytes,
+			From: route.Hops[errorSourceIdx-1].PubKeyBytes,
+			To:   route.Hops[errorSourceIdx].PubKeyBytes,
 		}
 
 		reportOutgoing()
@@ -425,8 +427,8 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
 		// Set the node pair for which a channel update may be out of
 		// date. The second chance logic uses the policyFailure field.
 		i.policyFailure = &DirectedNodePair{
-			From: route.hops[errorSourceIdx-1].pubKeyBytes,
-			To:   route.hops[errorSourceIdx].pubKeyBytes,
+			From: route.Hops[errorSourceIdx-1].PubKeyBytes,
+			To:   route.Hops[errorSourceIdx].PubKeyBytes,
 		}
 
 		// We report incoming channel. If a second pair is granted in
@@ -500,14 +502,16 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
 		// Note that if LND is extended to support multiple blinded
 		// routes, this will terminate the payment without re-trying
 		// the other routes.
-		if introIdx == len(route.hops)-1 {
+		if introIdx == len(route.Hops)-1 {
 			i.finalFailureReason = &reasonError
 		} else {
 			// If there are other hops between the recipient and
 			// introduction node, then we just penalize the last
 			// hop in the blinded route to minimize the storage of
 			// results for ephemeral keys.
-			i.failPairBalance(route, len(route.hops)-1)
+			i.failPairBalance(
+				route, len(route.Hops)-1,
+			)
 		}
 
 	// In all other cases, we penalize the reporting node. These are all
@@ -521,9 +525,9 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
 // route, using the same indexing in the route that we use for errorSourceIdx
 // (i.e., that we consider our own node to be at index zero). A boolean is
 // returned to indicate whether the route contains a blinded portion at all.
-func introductionPointIndex(route *mcRoute) (int, bool) {
-	for i, hop := range route.hops {
-		if hop.hasBlindingPoint {
+func introductionPointIndex(route *route.Route) (int, bool) {
+	for i, hop := range route.Hops {
+		if hop.BlindingPoint != nil {
 			return i + 1, true
 		}
 	}
@@ -533,8 +537,8 @@ func introductionPointIndex(route *mcRoute) (int, bool) {
 
 // processPaymentOutcomeUnknown processes a payment outcome for which no failure
 // message or source is available.
-func (i *interpretedResult) processPaymentOutcomeUnknown(route *mcRoute) {
-	n := len(route.hops)
+func (i *interpretedResult) processPaymentOutcomeUnknown(route *route.Route) {
+	n := len(route.Hops)
 
 	// If this is a direct payment, the destination must be at fault.
 	if n == 1 {
@@ -549,62 +553,12 @@ func (i *interpretedResult) processPaymentOutcomeUnknown(route *mcRoute) {
 	i.failPairRange(route, 0, n-1)
 }
 
-// extractMCRoute extracts the fields required by MC from the Route struct to
-// create the more minimal mcRoute struct.
-func extractMCRoute(route *route.Route) *mcRoute {
-	return &mcRoute{
-		sourcePubKey: route.SourcePubKey,
-		totalAmount:  route.TotalAmount,
-		hops:         extractMCHops(route.Hops),
-	}
-}
-
-// extractMCHops extracts the Hop fields that MC actually uses from a slice of
-// Hops.
-func extractMCHops(hops []*route.Hop) []*mcHop {
-	mcHops := make([]*mcHop, len(hops))
-	for i, hop := range hops {
-		mcHops[i] = extractMCHop(hop)
-	}
-
-	return mcHops
-}
-
-// extractMCHop extracts the Hop fields that MC actually uses from a Hop.
-func extractMCHop(hop *route.Hop) *mcHop {
-	return &mcHop{
-		channelID:        hop.ChannelID,
-		pubKeyBytes:      hop.PubKeyBytes,
-		amtToFwd:         hop.AmtToForward,
-		hasBlindingPoint: hop.BlindingPoint != nil,
-		hasCustomRecords: len(hop.CustomRecords) > 0,
-	}
-}
-
-// mcRoute holds the bare minimum info about a payment attempt route that MC
-// requires.
-type mcRoute struct {
-	sourcePubKey route.Vertex
-	totalAmount  lnwire.MilliSatoshi
-	hops         []*mcHop
-}
-
-// mcHop holds the bare minimum info about a payment attempt route hop that MC
-// requires.
-type mcHop struct {
-	channelID        uint64
-	pubKeyBytes      route.Vertex
-	amtToFwd         lnwire.MilliSatoshi
-	hasBlindingPoint bool
-	hasCustomRecords bool
-}
-
 // failNode marks the node indicated by idx in the route as failed. It also
 // marks the incoming and outgoing channels of the node as failed. This function
 // intentionally panics when the self node is failed.
-func (i *interpretedResult) failNode(rt *mcRoute, idx int) {
+func (i *interpretedResult) failNode(rt *route.Route, idx int) {
 	// Mark the node as failing.
-	i.nodeFailure = &rt.hops[idx-1].pubKeyBytes
+	i.nodeFailure = &rt.Hops[idx-1].PubKeyBytes
 
 	// Mark the incoming connection as failed for the node. We intent to
 	// penalize as much as we can for a node level failure, including future
@@ -620,7 +574,7 @@ func (i *interpretedResult) failNode(rt *mcRoute, idx int) {
 
 	// If not the ultimate node, mark the outgoing connection as failed for
 	// the node.
-	if idx < len(rt.hops) {
+	if idx < len(rt.Hops) {
 		outgoingChannelIdx := idx
 		outPair, _ := getPair(rt, outgoingChannelIdx)
 		i.pairResults[outPair] = failPairResult(0)
@@ -630,14 +584,18 @@ func (i *interpretedResult) failNode(rt *mcRoute, idx int) {
 
 // failPairRange marks the node pairs from node fromIdx to node toIdx as failed
 // in both direction.
-func (i *interpretedResult) failPairRange(rt *mcRoute, fromIdx, toIdx int) {
+func (i *interpretedResult) failPairRange(
+	rt *route.Route, fromIdx, toIdx int) {
+
 	for idx := fromIdx; idx <= toIdx; idx++ {
 		i.failPair(rt, idx)
 	}
 }
 
 // failPair marks a pair as failed in both directions.
-func (i *interpretedResult) failPair(rt *mcRoute, idx int) {
+func (i *interpretedResult) failPair(
+	rt *route.Route, idx int) {
+
 	pair, _ := getPair(rt, idx)
 
 	// Report pair in both directions without a minimum penalization amount.
@@ -646,7 +604,9 @@ func (i *interpretedResult) failPair(rt *mcRoute, idx int) {
 }
 
 // failPairBalance marks a pair as failed with a minimum penalization amount.
-func (i *interpretedResult) failPairBalance(rt *mcRoute, channelIdx int) {
+func (i *interpretedResult) failPairBalance(
+	rt *route.Route, channelIdx int) {
+
 	pair, amt := getPair(rt, channelIdx)
 
 	i.pairResults[pair] = failPairResult(amt)
@@ -654,7 +614,9 @@ func (i *interpretedResult) failPairBalance(rt *mcRoute, channelIdx int) {
 
 // successPairRange marks the node pairs from node fromIdx to node toIdx as
 // succeeded.
-func (i *interpretedResult) successPairRange(rt *mcRoute, fromIdx, toIdx int) {
+func (i *interpretedResult) successPairRange(
+	rt *route.Route, fromIdx, toIdx int) {
+
 	for idx := fromIdx; idx <= toIdx; idx++ {
 		pair, amt := getPair(rt, idx)
 
@@ -664,21 +626,21 @@ func (i *interpretedResult) successPairRange(rt *mcRoute, fromIdx, toIdx int) {
 
 // getPair returns a node pair from the route and the amount passed between that
 // pair.
-func getPair(rt *mcRoute, channelIdx int) (DirectedNodePair,
+func getPair(rt *route.Route, channelIdx int) (DirectedNodePair,
 	lnwire.MilliSatoshi) {
 
-	nodeTo := rt.hops[channelIdx].pubKeyBytes
+	nodeTo := rt.Hops[channelIdx].PubKeyBytes
 	var (
 		nodeFrom route.Vertex
 		amt      lnwire.MilliSatoshi
 	)
 
 	if channelIdx == 0 {
-		nodeFrom = rt.sourcePubKey
-		amt = rt.totalAmount
+		nodeFrom = rt.SourcePubKey
+		amt = rt.TotalAmount
 	} else {
-		nodeFrom = rt.hops[channelIdx-1].pubKeyBytes
-		amt = rt.hops[channelIdx-1].amtToFwd
+		nodeFrom = rt.Hops[channelIdx-1].PubKeyBytes
+		amt = rt.Hops[channelIdx-1].AmtToForward
 	}
 
 	pair := NewDirectedNodePair(nodeFrom, nodeTo)
diff --git a/routing/router.go b/routing/router.go
index 1fea60ddb..3b340f35a 100644
--- a/routing/router.go
+++ b/routing/router.go
@@ -167,9 +167,9 @@ type PaymentSessionSource interface {
 	NewPaymentSessionEmpty() PaymentSession
 }
 
-// MissionControlQuerier is an interface that exposes failure reporting and
+// MissionController is an interface that exposes failure reporting and
 // probability estimation.
-type MissionControlQuerier interface {
+type MissionController interface {
 	// ReportPaymentFail reports a failed payment to mission control as
 	// input for future probability estimates. It returns a bool indicating
 	// whether this error is a final error and no further payment attempts
@@ -260,7 +260,7 @@ type Config struct {
 	// Each run will then take into account this set of pruned
 	// vertexes/edges to reduce route failure and pass on graph information
 	// gained to the next execution.
-	MissionControl MissionControlQuerier
+	MissionControl MissionController
 
 	// SessionSource defines a source for the router to retrieve new payment
 	// sessions.
diff --git a/rpcserver.go b/rpcserver.go
index c9edf852a..44a3ae391 100644
--- a/rpcserver.go
+++ b/rpcserver.go
@@ -729,7 +729,7 @@ func (r *rpcServer) addDeps(s *server, macService *macaroons.Service,
 			return info.NodeKey1Bytes, info.NodeKey2Bytes, nil
 		},
 		FindRoute:              s.chanRouter.FindRoute,
-		MissionControl:         s.defaultMC,
+		MissionControl:         s.missionControl,
 		ActiveNetParams:        r.cfg.ActiveNetParams.Params,
 		Tower:                  s.controlTower,
 		MaxTotalTimelock:       r.cfg.MaxOutgoingCltvExpiry,
@@ -6071,7 +6071,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
 
 			return r.server.chanRouter.FindBlindedPaths(
 				r.selfNode, amt,
-				r.server.defaultMC.GetProbability,
+				r.server.missionControl.GetProbability,
 				blindingRestrictions,
 			)
 		},
diff --git a/server.go b/server.go
index 8b142f87a..9a7276948 100644
--- a/server.go
+++ b/server.go
@@ -275,8 +275,7 @@ type server struct {
 
 	breachArbitrator *contractcourt.BreachArbitrator
 
-	missionController *routing.MissionController
-	defaultMC         *routing.MissionControl
+	missionControl *routing.MissionControl
 
 	graphBuilder *graph.Builder
 
@@ -956,20 +955,11 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 		McFlushInterval:         routingConfig.McFlushInterval,
 		MinFailureRelaxInterval: routing.DefaultMinFailureRelaxInterval,
 	}
-
-	s.missionController, err = routing.NewMissionController(
+	s.missionControl, err = routing.NewMissionControl(
 		dbs.ChanStateDB, selfNode.PubKeyBytes, mcCfg,
 	)
 	if err != nil {
-		return nil, fmt.Errorf("can't create mission control "+
-			"manager: %w", err)
-	}
-	s.defaultMC, err = s.missionController.GetNamespacedStore(
-		routing.DefaultMissionControlNamespace,
-	)
-	if err != nil {
-		return nil, fmt.Errorf("can't create mission control in the "+
-			"default namespace: %w", err)
+		return nil, fmt.Errorf("can't create mission control: %w", err)
 	}
 
 	srvrLog.Debugf("Instantiating payment session source with config: "+
@@ -995,7 +985,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 			chanGraph,
 		),
 		SourceNode:        sourceNode,
-		MissionControl:    s.defaultMC,
+		MissionControl:    s.missionControl,
 		GetLink:           s.htlcSwitch.GetLinkByShortID,
 		PathFindingConfig: pathFindingConfig,
 	}
@@ -1030,7 +1020,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
 		Chain:              cc.ChainIO,
 		Payer:              s.htlcSwitch,
 		Control:            s.controlTower,
-		MissionControl:     s.defaultMC,
+		MissionControl:     s.missionControl,
 		SessionSource:      paymentSessionSource,
 		GetLink:            s.htlcSwitch.GetLinkByShortID,
 		NextPaymentID:      sequencer.NextID,
@@ -2192,10 +2182,10 @@ func (s *server) Start() error {
 		}
 
 		cleanup.add(func() error {
-			s.missionController.StopStoreTickers()
+			s.missionControl.StopStoreTicker()
 			return nil
 		})
-		s.missionController.RunStoreTickers()
+		s.missionControl.RunStoreTicker()
 
 		// Before we start the connMgr, we'll check to see if we have
 		// any backups to recover. We do this now as we want to ensure
@@ -2477,7 +2467,7 @@ func (s *server) Stop() error {
 			srvrLog.Warnf("Unable to stop ChannelEventStore: %v",
 				err)
 		}
-		s.missionController.StopStoreTickers()
+		s.missionControl.StopStoreTicker()
 
 		// Disconnect from each active peers to ensure that
 		// peerTerminationWatchers signal completion to each peer.


Test patch:
diff --git a/channeldb/migration32/migration_test.go b/channeldb/migration32/migration_test.go
deleted file mode 100644
index 1ce6016ed..000000000
--- a/channeldb/migration32/migration_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-package migration32
-
-import (
-	"encoding/hex"
-	"testing"
-	"time"
-
-	"github.com/btcsuite/btcd/btcec/v2"
-	lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
-	"github.com/lightningnetwork/lnd/channeldb/migtest"
-	"github.com/lightningnetwork/lnd/kvdb"
-)
-
-var (
-	failureIndex = 8
-	testPub      = Vertex{2, 202, 4}
-	testPub2     = Vertex{22, 202, 4}
-
-	pubkeyBytes, _ = hex.DecodeString(
-		"598ec453728e0ffe0ae2f5e174243cf58f2" +
-			"a3f2c83d2457b43036db568b11093",
-	)
-	pubKeyY = new(btcec.FieldVal)
-	_       = pubKeyY.SetByteSlice(pubkeyBytes)
-	pubkey  = btcec.NewPublicKey(new(btcec.FieldVal).SetInt(4), pubKeyY)
-
-	paymentResultCommon1 = paymentResultCommon{
-		id:               0,
-		timeFwd:          time.Unix(0, 1),
-		timeReply:        time.Unix(0, 2),
-		success:          false,
-		failureSourceIdx: &failureIndex,
-		failure:          &lnwire.FailFeeInsufficient{},
-	}
-
-	paymentResultCommon2 = paymentResultCommon{
-		id:        2,
-		timeFwd:   time.Unix(0, 4),
-		timeReply: time.Unix(0, 7),
-		success:   true,
-	}
-)
-
-// TestMigrateMCRouteSerialisation tests that the MigrateMCRouteSerialisation
-// migration function correctly migrates the MC store from using the old route
-// encoding to using the newer, more minimal route encoding.
-func TestMigrateMCRouteSerialisation(t *testing.T) {
-	customRecord := map[uint64][]byte{
-		65536: {4, 2, 2},
-	}
-
-	resultsOld := []*paymentResultOld{
-		{
-			paymentResultCommon: paymentResultCommon1,
-			route: &Route{
-				TotalTimeLock: 100,
-				TotalAmount:   400,
-				SourcePubKey:  testPub,
-				Hops: []*Hop{
-					// A hop with MPP, AMP and custom
-					// records.
-					{
-						PubKeyBytes:      testPub,
-						ChannelID:        100,
-						OutgoingTimeLock: 300,
-						AmtToForward:     500,
-						MPP: &MPP{
-							paymentAddr: [32]byte{
-								4, 5,
-							},
-							totalMsat: 900,
-						},
-						AMP: &AMP{
-							rootShare: [32]byte{
-								0, 0,
-							},
-							setID: [32]byte{
-								5, 5, 5,
-							},
-							childIndex: 90,
-						},
-						CustomRecords: customRecord,
-						Metadata:      []byte{6, 7, 7},
-					},
-					// A legacy hop.
-					{
-						PubKeyBytes:      testPub,
-						ChannelID:        800,
-						OutgoingTimeLock: 4,
-						AmtToForward:     4,
-						LegacyPayload:    true,
-					},
-					// A hop with a blinding key.
-					{
-						PubKeyBytes:      testPub,
-						ChannelID:        800,
-						OutgoingTimeLock: 4,
-						AmtToForward:     4,
-						BlindingPoint:    pubkey,
-						EncryptedData: []byte{
-							1, 2, 3,
-						},
-						TotalAmtMsat: 600,
-					},
-					// A hop with a blinding key and custom
-					// records.
-					{
-						PubKeyBytes:      testPub,
-						ChannelID:        800,
-						OutgoingTimeLock: 4,
-						AmtToForward:     4,
-						CustomRecords:    customRecord,
-						BlindingPoint:    pubkey,
-						EncryptedData: []byte{
-							1, 2, 3,
-						},
-						TotalAmtMsat: 600,
-					},
-				},
-			},
-		},
-		{
-			paymentResultCommon: paymentResultCommon2,
-			route: &Route{
-				TotalTimeLock: 101,
-				TotalAmount:   401,
-				SourcePubKey:  testPub2,
-				Hops: []*Hop{
-					{
-						PubKeyBytes:      testPub,
-						ChannelID:        800,
-						OutgoingTimeLock: 4,
-						AmtToForward:     4,
-						BlindingPoint:    pubkey,
-						EncryptedData: []byte{
-							1, 2, 3,
-						},
-						TotalAmtMsat: 600,
-					},
-				},
-			},
-		},
-	}
-
-	expectedResultsNew := []*paymentResultNew{
-		{
-			paymentResultCommon: paymentResultCommon1,
-			route: &mcRoute{
-				sourcePubKey: testPub,
-				totalAmount:  400,
-				hops: []*mcHop{
-					{
-						channelID:        100,
-						pubKeyBytes:      testPub,
-						amtToFwd:         500,
-						hasCustomRecords: true,
-					},
-					{
-						channelID:   800,
-						pubKeyBytes: testPub,
-						amtToFwd:    4,
-					},
-					{
-						channelID:        800,
-						pubKeyBytes:      testPub,
-						amtToFwd:         4,
-						hasBlindingPoint: true,
-					},
-					{
-						channelID:        800,
-						pubKeyBytes:      testPub,
-						amtToFwd:         4,
-						hasBlindingPoint: true,
-						hasCustomRecords: true,
-					},
-				},
-			},
-		},
-		{
-			paymentResultCommon: paymentResultCommon2,
-			route: &mcRoute{
-				sourcePubKey: testPub2,
-				totalAmount:  401,
-				hops: []*mcHop{
-					{
-						channelID:        800,
-						pubKeyBytes:      testPub,
-						amtToFwd:         4,
-						hasBlindingPoint: true,
-					},
-				},
-			},
-		},
-	}
-
-	// Prime the database with some mission control data that uses the
-	// old route encoding.
-	before := func(tx kvdb.RwTx) error {
-		resultBucket, err := tx.CreateTopLevelBucket(resultsKey)
-		if err != nil {
-			return err
-		}
-
-		for _, result := range resultsOld {
-			k, v, err := serializeOldResult(result)
-			if err != nil {
-				return err
-			}
-
-			if err := resultBucket.Put(k, v); err != nil {
-				return err
-			}
-		}
-
-		return nil
-	}
-
-	// After the migration, ensure that all the relevant info was
-	// maintained.
-	after := func(tx kvdb.RwTx) error {
-		m := make(map[string]interface{})
-		for _, result := range expectedResultsNew {
-			k, v, err := serializeNewResult(result)
-			if err != nil {
-				return err
-			}
-
-			m[string(k)] = string(v)
-		}
-
-		return migtest.VerifyDB(tx, resultsKey, m)
-	}
-
-	migtest.ApplyMigration(
-		t, before, after, MigrateMCRouteSerialisation, false,
-	)
-}
diff --git a/channeldb/migration33/migration_test.go b/channeldb/migration33/migration_test.go
deleted file mode 100644
index 851e2467e..000000000
--- a/channeldb/migration33/migration_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package migration33
-
-import (
-	"testing"
-
-	"github.com/lightningnetwork/lnd/channeldb/migtest"
-	"github.com/lightningnetwork/lnd/kvdb"
-)
-
-var (
-	// before represents the structure of the MC store before the migration.
-	before = map[string]interface{}{
-		"key1": "result1",
-		"key2": "result2",
-		"key3": "result3",
-		"key4": "result4",
-	}
-
-	// after represents the expected structure of the store after the
-	// migration. It should be identical to before except all the kv pairs
-	// are now under a new default namespace key.
-	after = map[string]interface{}{
-		string(defaultMCNamespaceKey): before,
-	}
-)
-
-// TestMigrateMCStoreNameSpacedResults tests that the MC store results are
-// correctly moved to be under a new default namespace bucket.
-func TestMigrateMCStoreNameSpacedResults(t *testing.T) {
-	before := func(tx kvdb.RwTx) error {
-		return migtest.RestoreDB(tx, resultsKey, before)
-	}
-
-	after := func(tx kvdb.RwTx) error {
-		return migtest.VerifyDB(tx, resultsKey, after)
-	}
-
-	migtest.ApplyMigration(
-		t, before, after, MigrateMCStoreNameSpacedResults, false,
-	)
-}
diff --git a/routing/integrated_routing_context_test.go b/routing/integrated_routing_context_test.go
index 085ac1a9f..061dcfc3a 100644
--- a/routing/integrated_routing_context_test.go
+++ b/routing/integrated_routing_context_test.go
@@ -163,15 +163,12 @@ func (c *integratedRoutingContext) testPayment(maxParts uint32,
 		}
 	})
 
-	// Instantiate a new mission controller with the current configuration
+	// Instantiate a new mission control with the current configuration
 	// values.
-	mcController, err := NewMissionController(db, c.source.pubkey, &c.mcCfg)
-	require.NoError(c.t, err)
-
-	mc, err := mcController.GetNamespacedStore(
-		DefaultMissionControlNamespace,
-	)
-	require.NoError(c.t, err)
+	mc, err := NewMissionControl(db, c.source.pubkey, &c.mcCfg)
+	if err != nil {
+		c.t.Fatal(err)
+	}
 
 	getBandwidthHints := func(_ Graph) (bandwidthHints, error) {
 		// Create bandwidth hints based on local channel balances.
diff --git a/routing/missioncontrol_store_test.go b/routing/missioncontrol_store_test.go
index 2dbfc1121..34b925a3e 100644
--- a/routing/missioncontrol_store_test.go
+++ b/routing/missioncontrol_store_test.go
@@ -18,16 +18,12 @@ const testMaxRecords = 2
 
 var (
 	// mcStoreTestRoute is a test route for the mission control store tests.
-	mcStoreTestRoute = mcRoute{
-		totalAmount:  lnwire.MilliSatoshi(5),
-		sourcePubKey: route.Vertex{1},
-		hops: []*mcHop{
+	mcStoreTestRoute = route.Route{
+		SourcePubKey: route.Vertex{1},
+		Hops: []*route.Hop{
 			{
-				pubKeyBytes:      route.Vertex{2},
-				channelID:        4,
-				amtToFwd:         lnwire.MilliSatoshi(7),
-				hasCustomRecords: true,
-				hasBlindingPoint: false,
+				PubKeyBytes:   route.Vertex{2},
+				LegacyPayload: true,
 			},
 		},
 	}
@@ -62,9 +58,7 @@ func newMCStoreTestHarness(t testing.TB, maxRecords int,
 		require.NoError(t, db.Close())
 	})
 
-	store, err := newMissionControlStore(
-		newDefaultNamespacedStore(db), maxRecords, flushInterval,
-	)
+	store, err := newMissionControlStore(db, maxRecords, flushInterval)
 	require.NoError(t, err)
 
 	return mcStoreTestHarness{db: db, store: store}
@@ -117,9 +111,7 @@ func TestMissionControlStore(t *testing.T) {
 	require.Equal(t, &result2, results[1])
 
 	// Recreate store to test pruning.
-	store, err = newMissionControlStore(
-		newDefaultNamespacedStore(db), testMaxRecords, time.Second,
-	)
+	store, err = newMissionControlStore(db, testMaxRecords, time.Second)
 	require.NoError(t, err)
 
 	// Add a newer result which failed due to mpp timeout.
@@ -217,9 +209,7 @@ func TestMissionControlStoreFlushing(t *testing.T) {
 	store.stop()
 
 	// Recreate store.
-	store, err := newMissionControlStore(
-		newDefaultNamespacedStore(db), testMaxRecords, flushInterval,
-	)
+	store, err := newMissionControlStore(db, testMaxRecords, flushInterval)
 	require.NoError(t, err)
 	store.run()
 	defer store.stop()
diff --git a/routing/missioncontrol_test.go b/routing/missioncontrol_test.go
index dad033ac4..4a0f73871 100644
--- a/routing/missioncontrol_test.go
+++ b/routing/missioncontrol_test.go
@@ -5,7 +5,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/lightningnetwork/lnd/clock"
 	"github.com/lightningnetwork/lnd/kvdb"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -41,11 +40,9 @@ var (
 )
 
 type mcTestContext struct {
-	t *testing.T
-
-	mcController *MissionController
-	mc           *MissionControl
-	clock        *testClock
+	t   *testing.T
+	mc  *MissionControl
+	now time.Time
 
 	db     kvdb.Backend
 	dbPath string
@@ -55,8 +52,8 @@ type mcTestContext struct {
 
 func createMcTestContext(t *testing.T) *mcTestContext {
 	ctx := &mcTestContext{
-		t:     t,
-		clock: newTestClock(mcTestTime),
+		t:   t,
+		now: mcTestTime,
 	}
 
 	file, err := os.CreateTemp("", "*.db")
@@ -88,11 +85,9 @@ func createMcTestContext(t *testing.T) *mcTestContext {
 // restartMc creates a new instances of mission control on the same database.
 func (ctx *mcTestContext) restartMc() {
 	// Since we don't run a timer to store results in unit tests, we store
-	// them here before fetching back everything in NewMissionController.
-	if ctx.mcController != nil {
-		for _, mc := range ctx.mcController.mc {
-			require.NoError(ctx.t, mc.store.storeResults())
-		}
+	// them here before fetching back everything in NewMissionControl.
+	if ctx.mc != nil {
+		require.NoError(ctx.t, ctx.mc.store.storeResults())
 	}
 
 	aCfg := AprioriConfig{
@@ -104,7 +99,7 @@ func (ctx *mcTestContext) restartMc() {
 	estimator, err := NewAprioriEstimator(aCfg)
 	require.NoError(ctx.t, err)
 
-	ctx.mcController, err = NewMissionController(
+	mc, err := NewMissionControl(
 		ctx.db, mcTestSelf,
 		&MissionControlConfig{Estimator: estimator},
 	)
@@ -112,18 +107,8 @@ func (ctx *mcTestContext) restartMc() {
 		ctx.t.Fatal(err)
 	}
 
-	ctx.mcController.cfg.clock = ctx.clock
-
-	// By default, select the default namespace.
-	ctx.setNamespacedMC(DefaultMissionControlNamespace)
-}
-
-// setNamespacedMC sets the currently selected MissionControl instance of the
-// mcTextContext to the one with the given namespace.
-func (ctx *mcTestContext) setNamespacedMC(namespace string) {
-	var err error
-	ctx.mc, err = ctx.mcController.GetNamespacedStore(namespace)
-	require.NoError(ctx.t, err)
+	mc.now = func() time.Time { return ctx.now }
+	ctx.mc = mc
 }
 
 // Assert that mission control returns a probability for an edge.
@@ -165,7 +150,7 @@ func (ctx *mcTestContext) reportSuccess() {
 func TestMissionControl(t *testing.T) {
 	ctx := createMcTestContext(t)
 
-	ctx.clock.setTime(testTime)
+	ctx.now = testTime
 
 	testTime := time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
 
@@ -193,7 +178,7 @@ func TestMissionControl(t *testing.T) {
 	// Edge decay started. The node probability weighted average should now
 	// have shifted from 1:1 to 1:0.5 -> 60%. The connection probability is
 	// half way through the recovery, so we expect 30% here.
-	ctx.clock.setTime(testTime.Add(30 * time.Minute))
+	ctx.now = testTime.Add(30 * time.Minute)
 	ctx.expectP(1000, 0.3)
 
 	// Edge fails again, this time without a min penalization amt. The edge
@@ -203,7 +188,7 @@ func TestMissionControl(t *testing.T) {
 	ctx.expectP(500, 0)
 
 	// Edge decay started.
-	ctx.clock.setTime(testTime.Add(60 * time.Minute))
+	ctx.now = testTime.Add(60 * time.Minute)
 	ctx.expectP(1000, 0.3)
 
 	// Restart mission control to test persistence.
@@ -245,94 +230,3 @@ func TestMissionControlChannelUpdate(t *testing.T) {
 	)
 	ctx.expectP(100, 0)
 }
-
-// TestMissionControlNamespaces tests that the results reported to a
-// MissionControl instance in one namespace does not affect the query results in
-// another namespace.
-func TestMissionControlNamespaces(t *testing.T) {
-	// Create a new MC context. This will select the default namespace
-	// MissionControl instance.
-	ctx := createMcTestContext(t)
-
-	// Initially, the controller should only be aware of the default
-	// namespace.
-	require.ElementsMatch(t, ctx.mcController.ListNamespaces(), []string{
-		DefaultMissionControlNamespace,
-	})
-
-	// Initial probability is expected to be the apriori.
-	ctx.expectP(1000, testAprioriHopProbability)
-
-	// Expect probability to be zero after reporting the edge as failed.
-	ctx.reportFailure(1000, lnwire.NewTemporaryChannelFailure(nil))
-	ctx.expectP(1000, 0)
-
-	// Now, switch namespaces.
-	const newNs = "new-namespace"
-	ctx.setNamespacedMC(newNs)
-
-	// Now, the controller should only be aware of the default namespace and
-	// the new one.
-	require.ElementsMatch(t, ctx.mcController.ListNamespaces(), []string{
-		DefaultMissionControlNamespace,
-		newNs,
-	})
-
-	// Since this new namespace has no idea about the reported failure, the
-	// expected probability should once again be the apriori probability.
-	ctx.expectP(1000, testAprioriHopProbability)
-
-	// Report a success in the new namespace.
-	ctx.reportSuccess()
-
-	// The probability of the pair should now have increased.
-	ctx.expectP(1000, testAprioriHopProbability+0.05)
-
-	// Switch back to the default namespace.
-	ctx.setNamespacedMC(DefaultMissionControlNamespace)
-
-	// The probability in the default namespace should still be zero.
-	ctx.expectP(1000, 0)
-
-	// We also want to test that the initial loading of the namespaces is
-	// done correctly. So let's reload the controller and assert that the
-	// probabilities in both namespaces remain the same after restart.
-	ctx.restartMc()
-
-	// Assert that both namespaces were loaded.
-	require.ElementsMatch(t, ctx.mcController.ListNamespaces(), []string{
-		DefaultMissionControlNamespace,
-		newNs,
-	})
-
-	// Assert that the probabilities in both namespaces remain unchanged.
-	ctx.expectP(1000, 0)
-	ctx.setNamespacedMC(newNs)
-	ctx.expectP(1000, testAprioriHopProbability+0.05)
-}
-
-// testClock is an implementation of clock.Clock that lets the caller overwrite
-// the current time at any point.
-type testClock struct {
-	now time.Time
-	clock.Clock
-}
-
-// newTestClock constructs a new testClock.
-func newTestClock(startTime time.Time) *testClock {
-	return &testClock{
-		now: startTime,
-	}
-}
-
-// Now returns the underlying current time.
-//
-// NOTE: this is part of the clock.Clock interface.
-func (c *testClock) Now() time.Time {
-	return c.now
-}
-
-// setTime overwrites the current time.
-func (c *testClock) setTime(n time.Time) {
-	c.now = n
-}
diff --git a/routing/mock_test.go b/routing/mock_test.go
index 35ac3ecd9..32f83420f 100644
--- a/routing/mock_test.go
+++ b/routing/mock_test.go
@@ -125,10 +125,10 @@ func (m *mockPaymentSessionSourceOld) NewPaymentSessionEmpty() PaymentSession {
 }
 
 type mockMissionControlOld struct {
-	MissionController
+	MissionControl
 }
 
-var _ MissionControlQuerier = (*mockMissionControlOld)(nil)
+var _ MissionController = (*mockMissionControlOld)(nil)
 
 func (m *mockMissionControlOld) ReportPaymentFail(
 	paymentID uint64, rt *route.Route,
@@ -657,7 +657,7 @@ type mockMissionControl struct {
 	mock.Mock
 }
 
-var _ MissionControlQuerier = (*mockMissionControl)(nil)
+var _ MissionController = (*mockMissionControl)(nil)
 
 func (m *mockMissionControl) ReportPaymentFail(
 	paymentID uint64, rt *route.Route,
diff --git a/routing/result_interpretation_test.go b/routing/result_interpretation_test.go
index 14506e3a1..68b527e5a 100644
--- a/routing/result_interpretation_test.go
+++ b/routing/result_interpretation_test.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/btcsuite/btcd/btcec/v2"
 	"github.com/davecgh/go-spew/spew"
 	"github.com/lightningnetwork/lnd/lnwire"
 	"github.com/lightningnetwork/lnd/routing/route"
@@ -14,105 +15,109 @@ var (
 		{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4},
 	}
 
-	routeOneHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
+	// blindingPoint provides a non-nil blinding point (value is never
+	// used).
+	blindingPoint = &btcec.PublicKey{}
+
+	routeOneHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
 		},
 	}
 
-	routeTwoHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
-			{pubKeyBytes: hops[2], amtToFwd: 97},
+	routeTwoHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
+			{PubKeyBytes: hops[2], AmtToForward: 97},
 		},
 	}
 
-	routeThreeHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
-			{pubKeyBytes: hops[2], amtToFwd: 97},
-			{pubKeyBytes: hops[3], amtToFwd: 94},
+	routeThreeHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
+			{PubKeyBytes: hops[2], AmtToForward: 97},
+			{PubKeyBytes: hops[3], AmtToForward: 94},
 		},
 	}
 
-	routeFourHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
-			{pubKeyBytes: hops[2], amtToFwd: 97},
-			{pubKeyBytes: hops[3], amtToFwd: 94},
-			{pubKeyBytes: hops[4], amtToFwd: 90},
+	routeFourHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
+			{PubKeyBytes: hops[2], AmtToForward: 97},
+			{PubKeyBytes: hops[3], AmtToForward: 94},
+			{PubKeyBytes: hops[4], AmtToForward: 90},
 		},
 	}
 
 	// blindedMultiHop is a blinded path where there are cleartext hops
 	// before the introduction node, and an intermediate blinded hop before
 	// the recipient after it.
-	blindedMultiHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
+	blindedMultiHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
 			{
-				pubKeyBytes:      hops[2],
-				amtToFwd:         95,
-				hasBlindingPoint: true,
+				PubKeyBytes:   hops[2],
+				AmtToForward:  95,
+				BlindingPoint: blindingPoint,
 			},
-			{pubKeyBytes: hops[3], amtToFwd: 88},
-			{pubKeyBytes: hops[4], amtToFwd: 77},
+			{PubKeyBytes: hops[3], AmtToForward: 88},
+			{PubKeyBytes: hops[4], AmtToForward: 77},
 		},
 	}
 
 	// blindedSingleHop is a blinded path with a single blinded hop after
 	// the introduction node.
-	blindedSingleHop = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 99},
+	blindedSingleHop = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 99},
 			{
-				pubKeyBytes:      hops[2],
-				amtToFwd:         95,
-				hasBlindingPoint: true,
+				PubKeyBytes:   hops[2],
+				AmtToForward:  95,
+				BlindingPoint: blindingPoint,
 			},
-			{pubKeyBytes: hops[3], amtToFwd: 88},
+			{PubKeyBytes: hops[3], AmtToForward: 88},
 		},
 	}
 
 	// blindedMultiToIntroduction is a blinded path which goes directly
 	// to the introduction node, with multiple blinded hops after it.
-	blindedMultiToIntroduction = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
+	blindedMultiToIntroduction = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
 			{
-				pubKeyBytes:      hops[1],
-				amtToFwd:         90,
-				hasBlindingPoint: true,
+				PubKeyBytes:   hops[1],
+				AmtToForward:  90,
+				BlindingPoint: blindingPoint,
 			},
-			{pubKeyBytes: hops[2], amtToFwd: 75},
-			{pubKeyBytes: hops[3], amtToFwd: 58},
+			{PubKeyBytes: hops[2], AmtToForward: 75},
+			{PubKeyBytes: hops[3], AmtToForward: 58},
 		},
 	}
 
 	// blindedIntroReceiver is a blinded path where the introduction node
 	// is the recipient.
-	blindedIntroReceiver = mcRoute{
-		sourcePubKey: hops[0],
-		totalAmount:  100,
-		hops: []*mcHop{
-			{pubKeyBytes: hops[1], amtToFwd: 95},
+	blindedIntroReceiver = route.Route{
+		SourcePubKey: hops[0],
+		TotalAmount:  100,
+		Hops: []*route.Hop{
+			{PubKeyBytes: hops[1], AmtToForward: 95},
 			{
-				pubKeyBytes:      hops[2],
-				amtToFwd:         90,
-				hasBlindingPoint: true,
+				PubKeyBytes:   hops[2],
+				AmtToForward:  90,
+				BlindingPoint: blindingPoint,
 			},
 		},
 	}
@@ -129,7 +134,7 @@ func getPolicyFailure(from, to int) *DirectedNodePair {
 
 type resultTestCase struct {
 	name          string
-	route         *mcRoute
+	route         *route.Route
 	success       bool
 	failureSrcIdx int
 	failure       lnwire.FailureMessage
@@ -154,7 +159,7 @@ var resultTestCases = []resultTestCase{
 		},
 	},
 
-	// Tests that an expiry too soon failure result is properly interpreted.
+	// Tests that a expiry too soon failure result is properly interpreted.
 	{
 		name:          "fail expiry too soon",
 		route:         &routeFourHop,
diff --git a/routing/router_test.go b/routing/router_test.go
index 6f6f2e434..0380bab00 100644
--- a/routing/router_test.go
+++ b/routing/router_test.go
@@ -128,16 +128,11 @@ func createTestCtxFromGraphInstanceAssumeValid(t *testing.T,
 
 	mcConfig := &MissionControlConfig{Estimator: estimator}
 
-	mcController, err := NewMissionController(
+	mc, err := NewMissionControl(
 		graphInstance.graphBackend, route.Vertex{}, mcConfig,
 	)
 	require.NoError(t, err, "failed to create missioncontrol")
 
-	mc, err := mcController.GetNamespacedStore(
-		DefaultMissionControlNamespace,
-	)
-	require.NoError(t, err)
-
 	sourceNode, err := graphInstance.graph.SourceNode()
 	require.NoError(t, err)
 	sessionSource := &SessionSource{
@@ -1086,15 +1081,11 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
 			return preImage, nil
 		})
 
-	require.IsType(t, ctx.router.cfg.MissionControl, &MissionControl{})
-	mc, _ := ctx.router.cfg.MissionControl.(*MissionControl)
-
-	err := mc.ResetHistory()
-	require.NoError(t, err)
+	ctx.router.cfg.MissionControl.(*MissionControl).ResetHistory()
 
 	// When we try to dispatch that payment, we should receive an error as
 	// both attempts should fail and cause both routes to be pruned.
-	_, _, err = ctx.router.SendPayment(payment)
+	_, _, err := ctx.router.SendPayment(payment)
 	require.Error(t, err, "payment didn't return error")
 
 	// The final error returned should also indicate that the peer wasn't
@@ -1111,10 +1102,12 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
 	// We expect the first attempt to have failed with a
 	// TemporaryChannelFailure, the second with UnknownNextPeer.
 	msg := htlcs[0].Failure.Message
-	require.IsType(t, msg, &lnwire.FailTemporaryChannelFailure{})
+	_, ok := msg.(*lnwire.FailTemporaryChannelFailure)
+	require.True(t, ok, "unexpected fail message")
 
 	msg = htlcs[1].Failure.Message
-	require.IsType(t, msg, &lnwire.FailUnknownNextPeer{})
+	_, ok = msg.(*lnwire.FailUnknownNextPeer)
+	require.True(t, ok, "unexpected fail message")
 
 	err = ctx.router.cfg.MissionControl.(*MissionControl).ResetHistory()
 	require.NoError(t, err, "reset history failed")
@@ -1151,11 +1144,7 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
 		getAliasFromPubKey(rt.Hops[0].PubKeyBytes, ctx.aliases),
 	)
 
-	require.IsType(t, ctx.router.cfg.MissionControl, &MissionControl{})
-	mc, _ = ctx.router.cfg.MissionControl.(*MissionControl)
-
-	err = mc.ResetHistory()
-	require.NoError(t, err)
+	ctx.router.cfg.MissionControl.(*MissionControl).ResetHistory()
 
 	// Finally, we'll modify the SendToSwitch function to indicate that the
 	// roasbeef -> luoji channel has insufficient capacity. This should