Repository: lightningnetwork/lnd
Pull request #9033 (2024-08-25 08:00:08)

error out when non-anchor channels are tried to bump the force close fee.
Small bug-fix for the new `bumpforceclose` api.


Roasbeef: Perhaps we should target this for 0.18.3? Small bug fix that makes the API harder to use incorrectly. 
ziggie1984: This api call was introduced in the current lnd 19 master branch (https://github.com/lightningnetwork/lnd/pull/8843)
Roasbeef: Needs a rebase, then this should be g2g. 

Patch:
diff --git a/channeldb/db.go b/channeldb/db.go
index 92e0498ec..d8ba05f3d 100644
--- a/channeldb/db.go
+++ b/channeldb/db.go
@@ -27,7 +27,6 @@ import (
 	"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"
@@ -292,10 +291,6 @@ var (
 			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..e50e5054e 100644
--- a/channeldb/log.go
+++ b/channeldb/log.go
@@ -11,7 +11,6 @@ import (
 	"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"
 )
@@ -45,6 +44,5 @@ func UseLogger(logger btclog.Logger) {
 	migration30.UseLogger(logger)
 	migration31.UseLogger(logger)
 	migration32.UseLogger(logger)
-	migration33.UseLogger(logger)
 	kvdb.UseLogger(logger)
 }
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..ee63ca58f 100644
--- a/docs/release-notes/release-notes-0.19.0.md
+++ b/docs/release-notes/release-notes-0.19.0.md
@@ -26,6 +26,11 @@
 * [Fix a bug](https://github.com/lightningnetwork/lnd/pull/9134) that would 
   cause a nil pointer dereference during the probing of a payment request that 
   does not contain a payment address.
+  
+* [Fixed a bug](https://github.com/lightningnetwork/lnd/pull/9033) where we
+  would not signal an error when trying to bump an non-anchor channel but
+  instead report a successful cpfp registration although no fee bumping is
+  possible for non-anchor channels anyways.
 
 # New Features
 ## Functional Enhancements
@@ -66,10 +71,6 @@
   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/lnrpc/walletrpc/walletkit_server.go b/lnrpc/walletrpc/walletkit_server.go
index 5ed33cdc8..f955168f4 100644
--- a/lnrpc/walletrpc/walletkit_server.go
+++ b/lnrpc/walletrpc/walletkit_server.go
@@ -1179,6 +1179,11 @@ func (w *WalletKit) BumpForceCloseFee(_ context.Context,
 		return nil, err
 	}
 
+	if !channel.ChanType.HasAnchors() {
+		return nil, fmt.Errorf("not able to bump the fee of a " +
+			"non-anchor channel")
+	}
+
 	// Match pending sweeps with commitments of the channel for which a bump
 	// is requested. Depending on the commitment state when force closing
 	// the channel we might have up to 3 commitments to consider when
@@ -1237,6 +1242,10 @@ func (w *WalletKit) BumpForceCloseFee(_ context.Context,
 		return commitSet.Contains(sweep.OutPoint.Hash)
 	}, pendingSweeps)
 
+	if len(anchors) == 0 {
+		return nil, fmt.Errorf("unable to find pending anchor outputs")
+	}
+
 	// Filter all relevant anchor sweeps and update the sweep request.
 	for _, anchor := range anchors {
 		// Anchor cpfp bump request are predictable because they are
diff --git a/routing/missioncontrol.go b/routing/missioncontrol.go
index d848235d8..ea2a56cf0 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 {
@@ -269,9 +214,9 @@ type paymentResult struct {
 	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,
@@ -608,7 +449,7 @@ 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,
@@ -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..9f1dc8d31 100644
--- a/routing/missioncontrol_store.go
+++ b/routing/missioncontrol_store.go
@@ -32,21 +32,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 +41,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 +67,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 +76,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 +119,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 +140,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 {
@@ -511,7 +511,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 {
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/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/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..804824f64 100644
--- a/routing/missioncontrol_store_test.go
+++ b/routing/missioncontrol_store_test.go
@@ -62,9 +62,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 +115,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 +213,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/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